Loading media/libheif/HeifDecoderImpl.cpp +152 −33 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ #include <drm/drm_framework_common.h> #include <media/IDataSource.h> #include <media/mediametadataretriever.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/MediaSource.h> #include <private/media/VideoFrame.h> #include <utils/Log.h> Loading @@ -48,7 +49,8 @@ public: * Constructs HeifDataSource; will take ownership of |stream|. */ HeifDataSource(HeifStream* stream) : mStream(stream), mReadPos(0), mEOS(false) {} : mStream(stream), mEOS(false), mCachedOffset(0), mCachedSize(0), mCacheBufferSize(0) {} ~HeifDataSource() override {} Loading @@ -68,17 +70,25 @@ public: } private: enum { /* * Buffer size for passing the read data to mediaserver. Set to 64K * (which is what MediaDataSource Java API's jni implementation uses). */ enum { kBufferSize = 64 * 1024, /* * Initial and max cache buffer size. */ kInitialCacheBufferSize = 4 * 1024 * 1024, kMaxCacheBufferSize = 64 * 1024 * 1024, }; sp<IMemory> mMemory; std::unique_ptr<HeifStream> mStream; off64_t mReadPos; bool mEOS; std::unique_ptr<uint8_t> mCache; off64_t mCachedOffset; size_t mCachedSize; size_t mCacheBufferSize; }; bool HeifDataSource::init() { Loading @@ -89,25 +99,29 @@ bool HeifDataSource::init() { ALOGE("Failed to allocate shared memory!"); return false; } mCache.reset(new uint8_t[kInitialCacheBufferSize]); if (mCache.get() == nullptr) { ALOGE("mFailed to allocate cache!"); return false; } mCacheBufferSize = kInitialCacheBufferSize; return true; } ssize_t HeifDataSource::readAt(off64_t offset, size_t size) { ALOGV("readAt: offset=%lld, size=%zu", (long long)offset, size); if (size == 0) { return mEOS ? ERROR_END_OF_STREAM : 0; } if (offset < mReadPos) { if (offset < mCachedOffset) { // try seek, then rewind/skip, fail if none worked if (mStream->seek(offset)) { ALOGV("readAt: seek to offset=%lld", (long long)offset); mReadPos = offset; mCachedOffset = offset; mCachedSize = 0; mEOS = false; } else if (mStream->rewind()) { ALOGV("readAt: rewind to offset=0"); mReadPos = 0; mCachedOffset = 0; mCachedSize = 0; mEOS = false; } else { ALOGE("readAt: couldn't seek or rewind!"); Loading @@ -115,38 +129,127 @@ ssize_t HeifDataSource::readAt(off64_t offset, size_t size) { } } if (mEOS) { if (mEOS && (offset < mCachedOffset || offset >= (off64_t)(mCachedOffset + mCachedSize))) { ALOGV("readAt: EOS"); return ERROR_END_OF_STREAM; } if (offset > mReadPos) { // skipping size_t skipSize = offset - mReadPos; size_t bytesSkipped = mStream->read(nullptr, skipSize); if (bytesSkipped <= skipSize) { mReadPos += bytesSkipped; // at this point, offset must be >= mCachedOffset, other cases should // have been caught above. CHECK(offset >= mCachedOffset); if (size == 0) { return 0; } if (bytesSkipped != skipSize) { // Can only read max of kBufferSize if (size > kBufferSize) { size = kBufferSize; } // copy from cache if the request falls entirely in cache if (offset + size <= mCachedOffset + mCachedSize) { memcpy(mMemory->pointer(), mCache.get() + offset - mCachedOffset, size); return size; } // need to fetch more, check if we need to expand the cache buffer. if ((off64_t)(offset + size) > mCachedOffset + kMaxCacheBufferSize) { // it's reaching max cache buffer size, need to roll window, and possibly // expand the cache buffer. size_t newCacheBufferSize = mCacheBufferSize; std::unique_ptr<uint8_t> newCache; uint8_t* dst = mCache.get(); if (newCacheBufferSize < kMaxCacheBufferSize) { newCacheBufferSize = kMaxCacheBufferSize; newCache.reset(new uint8_t[newCacheBufferSize]); dst = newCache.get(); } // when rolling the cache window, try to keep about half the old bytes // in case that the client goes back. off64_t newCachedOffset = offset - (off64_t)(newCacheBufferSize / 2); if (newCachedOffset < mCachedOffset) { newCachedOffset = mCachedOffset; } int64_t newCachedSize = (int64_t)(mCachedOffset + mCachedSize) - newCachedOffset; if (newCachedSize > 0) { // in this case, the new cache region partially overlop the old cache, // move the portion of the cache we want to save to the beginning of // the cache buffer. memcpy(dst, mCache.get() + newCachedOffset - mCachedOffset, newCachedSize); } else if (newCachedSize < 0){ // in this case, the new cache region is entirely out of the old cache, // in order to guarantee sequential read, we need to skip a number of // bytes before reading. size_t bytesToSkip = -newCachedSize; size_t bytesSkipped = mStream->read(nullptr, bytesToSkip); if (bytesSkipped != bytesToSkip) { // bytesSkipped is invalid, there is not enough bytes to reach // the requested offset. ALOGE("readAt: skip failed, EOS"); mEOS = true; mCachedOffset = newCachedOffset; mCachedSize = 0; return ERROR_END_OF_STREAM; } // set cache size to 0, since we're not keeping any old cache newCachedSize = 0; } if (size > kBufferSize) { size = kBufferSize; if (newCache.get() != nullptr) { mCache.reset(newCache.release()); mCacheBufferSize = newCacheBufferSize; } mCachedOffset = newCachedOffset; mCachedSize = newCachedSize; ALOGV("readAt: rolling cache window to (%lld, %zu), cache buffer size %zu", (long long)mCachedOffset, mCachedSize, mCacheBufferSize); } else { // expand cache buffer, but no need to roll the window size_t newCacheBufferSize = mCacheBufferSize; while (offset + size > mCachedOffset + newCacheBufferSize) { newCacheBufferSize *= 2; } CHECK(newCacheBufferSize <= kMaxCacheBufferSize); if (mCacheBufferSize < newCacheBufferSize) { uint8_t* newCache = new uint8_t[newCacheBufferSize]; memcpy(newCache, mCache.get(), mCachedSize); mCache.reset(newCache); mCacheBufferSize = newCacheBufferSize; ALOGV("readAt: current cache window (%lld, %zu), new cache buffer size %zu", (long long) mCachedOffset, mCachedSize, mCacheBufferSize); } } size_t bytesRead = mStream->read(mMemory->pointer(), size); if (bytesRead > size || bytesRead == 0) { size_t bytesToRead = offset + size - mCachedOffset - mCachedSize; size_t bytesRead = mStream->read(mCache.get() + mCachedSize, bytesToRead); if (bytesRead > bytesToRead || bytesRead == 0) { // bytesRead is invalid mEOS = true; return ERROR_END_OF_STREAM; } if (bytesRead < size) { // read some bytes but not all, set EOS and return ERROR_END_OF_STREAM next time bytesRead = 0; } else if (bytesRead < bytesToRead) { // read some bytes but not all, set EOS mEOS = true; } mReadPos += bytesRead; return bytesRead; mCachedSize += bytesRead; ALOGV("readAt: current cache window (%lld, %zu)", (long long) mCachedOffset, mCachedSize); // here bytesAvailable could be negative if offset jumped past EOS. int64_t bytesAvailable = mCachedOffset + mCachedSize - offset; if (bytesAvailable <= 0) { return ERROR_END_OF_STREAM; } if (bytesAvailable < (int64_t)size) { size = bytesAvailable; } memcpy(mMemory->pointer(), mCache.get() + offset - mCachedOffset, size); return size; } status_t HeifDataSource::getSize(off64_t* size) { Loading @@ -166,13 +269,15 @@ HeifDecoderImpl::HeifDecoderImpl() : // output color format should always be set via setOutputColor(), in case // it's not, default to HAL_PIXEL_FORMAT_RGB_565. mOutputColor(HAL_PIXEL_FORMAT_RGB_565), mCurScanline(0) { mCurScanline(0), mFrameDecoded(false) { } HeifDecoderImpl::~HeifDecoderImpl() { } bool HeifDecoderImpl::init(HeifStream* stream, HeifFrameInfo* frameInfo) { mFrameDecoded = false; sp<HeifDataSource> dataSource = new HeifDataSource(stream); if (!dataSource->init()) { return false; Loading Loading @@ -256,6 +361,13 @@ bool HeifDecoderImpl::setOutputColor(HeifColorFormat heifColor) { } bool HeifDecoderImpl::decode(HeifFrameInfo* frameInfo) { // reset scanline pointer mCurScanline = 0; if (mFrameDecoded) { return true; } mFrameMemory = mRetriever->getFrameAtTime(0, IMediaSource::ReadOptions::SEEK_PREVIOUS_SYNC, mOutputColor); if (mFrameMemory == nullptr || mFrameMemory->pointer() == nullptr) { Loading @@ -264,6 +376,12 @@ bool HeifDecoderImpl::decode(HeifFrameInfo* frameInfo) { } VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->pointer()); if (videoFrame->mSize == 0 || mFrameMemory->size() < videoFrame->getFlattenedSize()) { ALOGE("getFrameAtTime: videoFrame size is invalid"); return false; } ALOGV("Decoded dimension %dx%d, display %dx%d, angle %d, rowbytes %d, size %d", videoFrame->mWidth, videoFrame->mHeight, Loading @@ -282,6 +400,7 @@ bool HeifDecoderImpl::decode(HeifFrameInfo* frameInfo) { videoFrame->mIccSize, videoFrame->getFlattenedIccData()); } mFrameDecoded = true; return true; } Loading media/libheif/HeifDecoderImpl.h +1 −0 Original line number Diff line number Diff line Loading @@ -54,6 +54,7 @@ private: sp<IMemory> mFrameMemory; android_pixel_format_t mOutputColor; size_t mCurScanline; bool mFrameDecoded; }; } // namespace android Loading Loading
media/libheif/HeifDecoderImpl.cpp +152 −33 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ #include <drm/drm_framework_common.h> #include <media/IDataSource.h> #include <media/mediametadataretriever.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/MediaSource.h> #include <private/media/VideoFrame.h> #include <utils/Log.h> Loading @@ -48,7 +49,8 @@ public: * Constructs HeifDataSource; will take ownership of |stream|. */ HeifDataSource(HeifStream* stream) : mStream(stream), mReadPos(0), mEOS(false) {} : mStream(stream), mEOS(false), mCachedOffset(0), mCachedSize(0), mCacheBufferSize(0) {} ~HeifDataSource() override {} Loading @@ -68,17 +70,25 @@ public: } private: enum { /* * Buffer size for passing the read data to mediaserver. Set to 64K * (which is what MediaDataSource Java API's jni implementation uses). */ enum { kBufferSize = 64 * 1024, /* * Initial and max cache buffer size. */ kInitialCacheBufferSize = 4 * 1024 * 1024, kMaxCacheBufferSize = 64 * 1024 * 1024, }; sp<IMemory> mMemory; std::unique_ptr<HeifStream> mStream; off64_t mReadPos; bool mEOS; std::unique_ptr<uint8_t> mCache; off64_t mCachedOffset; size_t mCachedSize; size_t mCacheBufferSize; }; bool HeifDataSource::init() { Loading @@ -89,25 +99,29 @@ bool HeifDataSource::init() { ALOGE("Failed to allocate shared memory!"); return false; } mCache.reset(new uint8_t[kInitialCacheBufferSize]); if (mCache.get() == nullptr) { ALOGE("mFailed to allocate cache!"); return false; } mCacheBufferSize = kInitialCacheBufferSize; return true; } ssize_t HeifDataSource::readAt(off64_t offset, size_t size) { ALOGV("readAt: offset=%lld, size=%zu", (long long)offset, size); if (size == 0) { return mEOS ? ERROR_END_OF_STREAM : 0; } if (offset < mReadPos) { if (offset < mCachedOffset) { // try seek, then rewind/skip, fail if none worked if (mStream->seek(offset)) { ALOGV("readAt: seek to offset=%lld", (long long)offset); mReadPos = offset; mCachedOffset = offset; mCachedSize = 0; mEOS = false; } else if (mStream->rewind()) { ALOGV("readAt: rewind to offset=0"); mReadPos = 0; mCachedOffset = 0; mCachedSize = 0; mEOS = false; } else { ALOGE("readAt: couldn't seek or rewind!"); Loading @@ -115,38 +129,127 @@ ssize_t HeifDataSource::readAt(off64_t offset, size_t size) { } } if (mEOS) { if (mEOS && (offset < mCachedOffset || offset >= (off64_t)(mCachedOffset + mCachedSize))) { ALOGV("readAt: EOS"); return ERROR_END_OF_STREAM; } if (offset > mReadPos) { // skipping size_t skipSize = offset - mReadPos; size_t bytesSkipped = mStream->read(nullptr, skipSize); if (bytesSkipped <= skipSize) { mReadPos += bytesSkipped; // at this point, offset must be >= mCachedOffset, other cases should // have been caught above. CHECK(offset >= mCachedOffset); if (size == 0) { return 0; } if (bytesSkipped != skipSize) { // Can only read max of kBufferSize if (size > kBufferSize) { size = kBufferSize; } // copy from cache if the request falls entirely in cache if (offset + size <= mCachedOffset + mCachedSize) { memcpy(mMemory->pointer(), mCache.get() + offset - mCachedOffset, size); return size; } // need to fetch more, check if we need to expand the cache buffer. if ((off64_t)(offset + size) > mCachedOffset + kMaxCacheBufferSize) { // it's reaching max cache buffer size, need to roll window, and possibly // expand the cache buffer. size_t newCacheBufferSize = mCacheBufferSize; std::unique_ptr<uint8_t> newCache; uint8_t* dst = mCache.get(); if (newCacheBufferSize < kMaxCacheBufferSize) { newCacheBufferSize = kMaxCacheBufferSize; newCache.reset(new uint8_t[newCacheBufferSize]); dst = newCache.get(); } // when rolling the cache window, try to keep about half the old bytes // in case that the client goes back. off64_t newCachedOffset = offset - (off64_t)(newCacheBufferSize / 2); if (newCachedOffset < mCachedOffset) { newCachedOffset = mCachedOffset; } int64_t newCachedSize = (int64_t)(mCachedOffset + mCachedSize) - newCachedOffset; if (newCachedSize > 0) { // in this case, the new cache region partially overlop the old cache, // move the portion of the cache we want to save to the beginning of // the cache buffer. memcpy(dst, mCache.get() + newCachedOffset - mCachedOffset, newCachedSize); } else if (newCachedSize < 0){ // in this case, the new cache region is entirely out of the old cache, // in order to guarantee sequential read, we need to skip a number of // bytes before reading. size_t bytesToSkip = -newCachedSize; size_t bytesSkipped = mStream->read(nullptr, bytesToSkip); if (bytesSkipped != bytesToSkip) { // bytesSkipped is invalid, there is not enough bytes to reach // the requested offset. ALOGE("readAt: skip failed, EOS"); mEOS = true; mCachedOffset = newCachedOffset; mCachedSize = 0; return ERROR_END_OF_STREAM; } // set cache size to 0, since we're not keeping any old cache newCachedSize = 0; } if (size > kBufferSize) { size = kBufferSize; if (newCache.get() != nullptr) { mCache.reset(newCache.release()); mCacheBufferSize = newCacheBufferSize; } mCachedOffset = newCachedOffset; mCachedSize = newCachedSize; ALOGV("readAt: rolling cache window to (%lld, %zu), cache buffer size %zu", (long long)mCachedOffset, mCachedSize, mCacheBufferSize); } else { // expand cache buffer, but no need to roll the window size_t newCacheBufferSize = mCacheBufferSize; while (offset + size > mCachedOffset + newCacheBufferSize) { newCacheBufferSize *= 2; } CHECK(newCacheBufferSize <= kMaxCacheBufferSize); if (mCacheBufferSize < newCacheBufferSize) { uint8_t* newCache = new uint8_t[newCacheBufferSize]; memcpy(newCache, mCache.get(), mCachedSize); mCache.reset(newCache); mCacheBufferSize = newCacheBufferSize; ALOGV("readAt: current cache window (%lld, %zu), new cache buffer size %zu", (long long) mCachedOffset, mCachedSize, mCacheBufferSize); } } size_t bytesRead = mStream->read(mMemory->pointer(), size); if (bytesRead > size || bytesRead == 0) { size_t bytesToRead = offset + size - mCachedOffset - mCachedSize; size_t bytesRead = mStream->read(mCache.get() + mCachedSize, bytesToRead); if (bytesRead > bytesToRead || bytesRead == 0) { // bytesRead is invalid mEOS = true; return ERROR_END_OF_STREAM; } if (bytesRead < size) { // read some bytes but not all, set EOS and return ERROR_END_OF_STREAM next time bytesRead = 0; } else if (bytesRead < bytesToRead) { // read some bytes but not all, set EOS mEOS = true; } mReadPos += bytesRead; return bytesRead; mCachedSize += bytesRead; ALOGV("readAt: current cache window (%lld, %zu)", (long long) mCachedOffset, mCachedSize); // here bytesAvailable could be negative if offset jumped past EOS. int64_t bytesAvailable = mCachedOffset + mCachedSize - offset; if (bytesAvailable <= 0) { return ERROR_END_OF_STREAM; } if (bytesAvailable < (int64_t)size) { size = bytesAvailable; } memcpy(mMemory->pointer(), mCache.get() + offset - mCachedOffset, size); return size; } status_t HeifDataSource::getSize(off64_t* size) { Loading @@ -166,13 +269,15 @@ HeifDecoderImpl::HeifDecoderImpl() : // output color format should always be set via setOutputColor(), in case // it's not, default to HAL_PIXEL_FORMAT_RGB_565. mOutputColor(HAL_PIXEL_FORMAT_RGB_565), mCurScanline(0) { mCurScanline(0), mFrameDecoded(false) { } HeifDecoderImpl::~HeifDecoderImpl() { } bool HeifDecoderImpl::init(HeifStream* stream, HeifFrameInfo* frameInfo) { mFrameDecoded = false; sp<HeifDataSource> dataSource = new HeifDataSource(stream); if (!dataSource->init()) { return false; Loading Loading @@ -256,6 +361,13 @@ bool HeifDecoderImpl::setOutputColor(HeifColorFormat heifColor) { } bool HeifDecoderImpl::decode(HeifFrameInfo* frameInfo) { // reset scanline pointer mCurScanline = 0; if (mFrameDecoded) { return true; } mFrameMemory = mRetriever->getFrameAtTime(0, IMediaSource::ReadOptions::SEEK_PREVIOUS_SYNC, mOutputColor); if (mFrameMemory == nullptr || mFrameMemory->pointer() == nullptr) { Loading @@ -264,6 +376,12 @@ bool HeifDecoderImpl::decode(HeifFrameInfo* frameInfo) { } VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->pointer()); if (videoFrame->mSize == 0 || mFrameMemory->size() < videoFrame->getFlattenedSize()) { ALOGE("getFrameAtTime: videoFrame size is invalid"); return false; } ALOGV("Decoded dimension %dx%d, display %dx%d, angle %d, rowbytes %d, size %d", videoFrame->mWidth, videoFrame->mHeight, Loading @@ -282,6 +400,7 @@ bool HeifDecoderImpl::decode(HeifFrameInfo* frameInfo) { videoFrame->mIccSize, videoFrame->getFlattenedIccData()); } mFrameDecoded = true; return true; } Loading
media/libheif/HeifDecoderImpl.h +1 −0 Original line number Diff line number Diff line Loading @@ -54,6 +54,7 @@ private: sp<IMemory> mFrameMemory; android_pixel_format_t mOutputColor; size_t mCurScanline; bool mFrameDecoded; }; } // namespace android Loading