Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9c5f5b75 authored by android-build-team Robot's avatar android-build-team Robot Committed by Android (Google) Code Review
Browse files

Merge "Prepare code to allow image decoder to be reused" into pi-dev

parents 70376b12 f49d8f7b
Loading
Loading
Loading
Loading
+0 −4
Original line number Diff line number Diff line
@@ -48,7 +48,6 @@ MetadataRetrieverClient::MetadataRetrieverClient(pid_t pid)
{
    ALOGV("MetadataRetrieverClient constructor pid(%d)", pid);
    mPid = pid;
    mThumbnail = NULL;
    mAlbumArt = NULL;
    mRetriever = NULL;
}
@@ -77,7 +76,6 @@ void MetadataRetrieverClient::disconnect()
    ALOGV("disconnect from pid %d", mPid);
    Mutex::Autolock lock(mLock);
    mRetriever.clear();
    mThumbnail.clear();
    mAlbumArt.clear();
    IPCThreadState::self()->flushCommands();
}
@@ -201,7 +199,6 @@ sp<IMemory> MetadataRetrieverClient::getFrameAtTime(
            (long long)timeUs, option, colorFormat, metaOnly);
    Mutex::Autolock lock(mLock);
    Mutex::Autolock glock(sLock);
    mThumbnail.clear();
    if (mRetriever == NULL) {
        ALOGE("retriever is not initialized");
        return NULL;
@@ -220,7 +217,6 @@ sp<IMemory> MetadataRetrieverClient::getImageAtIndex(
            index, colorFormat, metaOnly, thumbnail);
    Mutex::Autolock lock(mLock);
    Mutex::Autolock glock(sLock);
    mThumbnail.clear();
    if (mRetriever == NULL) {
        ALOGE("retriever is not initialized");
        return NULL;
+1 −2
Original line number Diff line number Diff line
@@ -73,9 +73,8 @@ private:
    sp<MediaMetadataRetrieverBase>         mRetriever;
    pid_t                                  mPid;

    // Keep the shared memory copy of album art and capture frame (for thumbnail)
    // Keep the shared memory copy of album art
    sp<IMemory>                            mAlbumArt;
    sp<IMemory>                            mThumbnail;
};

}; // namespace android
+146 −130
Original line number Diff line number Diff line
@@ -42,7 +42,6 @@ namespace android {
static const int64_t kBufferTimeOutUs = 10000ll; // 10 msec
static const size_t kRetryCount = 50; // must be >0

//static
sp<IMemory> allocVideoFrame(const sp<MetaData>& trackMeta,
        int32_t width, int32_t height, int32_t dstBpp, bool metaOnly = false) {
    int32_t rotationAngle;
@@ -94,7 +93,6 @@ sp<IMemory> allocVideoFrame(const sp<MetaData>& trackMeta,
    return frameMem;
}

//static
bool findThumbnailInfo(
        const sp<MetaData> &trackMeta, int32_t *width, int32_t *height,
        uint32_t *type = NULL, const void **data = NULL, size_t *size = NULL) {
@@ -107,30 +105,15 @@ bool findThumbnailInfo(
                type ?: &dummyType, data ?: &dummyData, size ?: &dummySize);
}

//static
sp<IMemory> FrameDecoder::getMetadataOnly(
        const sp<MetaData> &trackMeta, int colorFormat, bool thumbnail) {
    OMX_COLOR_FORMATTYPE dstFormat;
    int32_t dstBpp;
    if (!getDstColorFormat(
            (android_pixel_format_t)colorFormat, &dstFormat, &dstBpp)) {
        return NULL;
bool findGridInfo(const sp<MetaData> &trackMeta,
        int32_t *tileWidth, int32_t *tileHeight, int32_t *gridRows, int32_t *gridCols) {
    return trackMeta->findInt32(kKeyTileWidth, tileWidth) && (*tileWidth > 0)
        && trackMeta->findInt32(kKeyTileHeight, tileHeight) && (*tileHeight > 0)
        && trackMeta->findInt32(kKeyGridRows, gridRows) && (*gridRows > 0)
        && trackMeta->findInt32(kKeyGridCols, gridCols) && (*gridCols > 0);
}

    int32_t width, height;
    if (thumbnail) {
        if (!findThumbnailInfo(trackMeta, &width, &height)) {
            return NULL;
        }
    } else {
        CHECK(trackMeta->findInt32(kKeyWidth, &width));
        CHECK(trackMeta->findInt32(kKeyHeight, &height));
    }
    return allocVideoFrame(trackMeta, width, height, dstBpp, true /*metaOnly*/);
}

//static
bool FrameDecoder::getDstColorFormat(
bool getDstColorFormat(
        android_pixel_format_t colorFormat,
        OMX_COLOR_FORMATTYPE *dstFormat,
        int32_t *dstBpp) {
@@ -162,46 +145,57 @@ bool FrameDecoder::getDstColorFormat(
    return false;
}

sp<IMemory> FrameDecoder::extractFrame(
        int64_t frameTimeUs, int option, int colorFormat) {
//static
sp<IMemory> FrameDecoder::getMetadataOnly(
        const sp<MetaData> &trackMeta, int colorFormat, bool thumbnail) {
    OMX_COLOR_FORMATTYPE dstFormat;
    int32_t dstBpp;
    if (!getDstColorFormat(
            (android_pixel_format_t)colorFormat, &mDstFormat, &mDstBpp)) {
            (android_pixel_format_t)colorFormat, &dstFormat, &dstBpp)) {
        return NULL;
    }

    status_t err = extractInternal(frameTimeUs, 1, option);
    if (err != OK) {
    int32_t width, height;
    if (thumbnail) {
        if (!findThumbnailInfo(trackMeta, &width, &height)) {
            return NULL;
        }

    return mFrames.size() > 0 ? mFrames[0] : NULL;
    } else {
        CHECK(trackMeta->findInt32(kKeyWidth, &width));
        CHECK(trackMeta->findInt32(kKeyHeight, &height));
    }

status_t FrameDecoder::extractFrames(
        int64_t frameTimeUs, size_t numFrames, int option, int colorFormat,
        std::vector<sp<IMemory> >* frames) {
    if (!getDstColorFormat(
            (android_pixel_format_t)colorFormat, &mDstFormat, &mDstBpp)) {
        return ERROR_UNSUPPORTED;
    return allocVideoFrame(trackMeta, width, height, dstBpp, true /*metaOnly*/);
}

    status_t err = extractInternal(frameTimeUs, numFrames, option);
    if (err != OK) {
        return err;
FrameDecoder::FrameDecoder(
        const AString &componentName,
        const sp<MetaData> &trackMeta,
        const sp<IMediaSource> &source)
    : mComponentName(componentName),
      mTrackMeta(trackMeta),
      mSource(source),
      mDstFormat(OMX_COLOR_Format16bitRGB565),
      mDstBpp(2),
      mHaveMoreInputs(true),
      mFirstSample(true) {
}

    for (size_t i = 0; i < mFrames.size(); i++) {
        frames->push_back(mFrames[i]);
FrameDecoder::~FrameDecoder() {
    if (mDecoder != NULL) {
        mDecoder->release();
        mSource->stop();
    }
    return OK;
}

status_t FrameDecoder::extractInternal(
        int64_t frameTimeUs, size_t numFrames, int option) {
status_t FrameDecoder::init(
        int64_t frameTimeUs, size_t numFrames, int option, int colorFormat) {
    if (!getDstColorFormat(
            (android_pixel_format_t)colorFormat, &mDstFormat, &mDstBpp)) {
        return ERROR_UNSUPPORTED;
    }

    MediaSource::ReadOptions options;
    sp<AMessage> videoFormat = onGetFormatAndSeekOptions(
            frameTimeUs, numFrames, option, &options);
            frameTimeUs, numFrames, option, &mReadOptions);
    if (videoFormat == NULL) {
        ALOGE("video format or seek mode not supported");
        return ERROR_UNSUPPORTED;
@@ -217,7 +211,8 @@ status_t FrameDecoder::extractInternal(
        return (decoder.get() == NULL) ? NO_MEMORY : err;
    }

    err = decoder->configure(videoFormat, NULL /* surface */, NULL /* crypto */, 0 /* flags */);
    err = decoder->configure(
            videoFormat, NULL /* surface */, NULL /* crypto */, 0 /* flags */);
    if (err != OK) {
        ALOGW("configure returned error %d (%s)", err, asString(err));
        decoder->release();
@@ -237,43 +232,46 @@ status_t FrameDecoder::extractInternal(
        decoder->release();
        return err;
    }
    mDecoder = decoder;

    Vector<sp<MediaCodecBuffer> > inputBuffers;
    err = decoder->getInputBuffers(&inputBuffers);
    return OK;
}

sp<IMemory> FrameDecoder::extractFrame() {
    status_t err = extractInternal();
    if (err != OK) {
        ALOGW("failed to get input buffers: %d (%s)", err, asString(err));
        decoder->release();
        mSource->stop();
        return err;
        return NULL;
    }

    Vector<sp<MediaCodecBuffer> > outputBuffers;
    err = decoder->getOutputBuffers(&outputBuffers);
    return mFrames.size() > 0 ? mFrames[0] : NULL;
}

status_t FrameDecoder::extractFrames(std::vector<sp<IMemory> >* frames) {
    status_t err = extractInternal();
    if (err != OK) {
        ALOGW("failed to get output buffers: %d (%s)", err, asString(err));
        decoder->release();
        mSource->stop();
        return err;
    }

    sp<AMessage> outputFormat = NULL;
    bool haveMoreInputs = true;
    size_t index, offset, size;
    int64_t timeUs;
    size_t retriesLeft = kRetryCount;
    for (size_t i = 0; i < mFrames.size(); i++) {
        frames->push_back(mFrames[i]);
    }
    return OK;
}

status_t FrameDecoder::extractInternal() {
    status_t err = OK;
    bool done = false;
    bool firstSample = true;
    size_t retriesLeft = kRetryCount;
    do {
        size_t inputIndex = -1;
        size_t index;
        int64_t ptsUs = 0ll;
        uint32_t flags = 0;
        sp<MediaCodecBuffer> codecBuffer = NULL;

        // Queue as many inputs as we possibly can, then block on dequeuing
        // outputs. After getting each output, come back and queue the inputs
        // again to keep the decoder busy.
        while (haveMoreInputs) {
            err = decoder->dequeueInputBuffer(&inputIndex, 0);
        while (mHaveMoreInputs) {
            err = mDecoder->dequeueInputBuffer(&index, 0);
            if (err != OK) {
                ALOGV("Timed out waiting for input");
                if (retriesLeft) {
@@ -281,16 +279,21 @@ status_t FrameDecoder::extractInternal(
                }
                break;
            }
            codecBuffer = inputBuffers[inputIndex];
            sp<MediaCodecBuffer> codecBuffer;
            err = mDecoder->getInputBuffer(index, &codecBuffer);
            if (err != OK) {
                ALOGE("failed to get input buffer %zu", index);
                break;
            }

            MediaBufferBase *mediaBuffer = NULL;

            err = mSource->read(&mediaBuffer, &options);
            options.clearSeekTo();
            err = mSource->read(&mediaBuffer, &mReadOptions);
            mReadOptions.clearSeekTo();
            if (err != OK) {
                ALOGW("Input Error or EOS");
                haveMoreInputs = false;
                if (!firstSample && err == ERROR_END_OF_STREAM) {
                mHaveMoreInputs = false;
                if (!mFirstSample && err == ERROR_END_OF_STREAM) {
                    err = OK;
                }
                break;
@@ -299,7 +302,7 @@ status_t FrameDecoder::extractInternal(
            if (mediaBuffer->range_length() > codecBuffer->capacity()) {
                ALOGE("buffer size (%zu) too large for codec input size (%zu)",
                        mediaBuffer->range_length(), codecBuffer->capacity());
                haveMoreInputs = false;
                mHaveMoreInputs = false;
                err = BAD_VALUE;
            } else {
                codecBuffer->setRange(0, mediaBuffer->range_length());
@@ -309,45 +312,46 @@ status_t FrameDecoder::extractInternal(
                        (const uint8_t*)mediaBuffer->data() + mediaBuffer->range_offset(),
                        mediaBuffer->range_length());

                onInputReceived(codecBuffer, mediaBuffer->meta_data(), firstSample, &flags);
                firstSample = false;
                onInputReceived(codecBuffer, mediaBuffer->meta_data(), mFirstSample, &flags);
                mFirstSample = false;
            }

            mediaBuffer->release();

            if (haveMoreInputs && inputIndex < inputBuffers.size()) {
            if (mHaveMoreInputs) {
                ALOGV("QueueInput: size=%zu ts=%" PRId64 " us flags=%x",
                        codecBuffer->size(), ptsUs, flags);

                err = decoder->queueInputBuffer(
                        inputIndex,
                err = mDecoder->queueInputBuffer(
                        index,
                        codecBuffer->offset(),
                        codecBuffer->size(),
                        ptsUs,
                        flags);

                if (flags & MediaCodec::BUFFER_FLAG_EOS) {
                    haveMoreInputs = false;
                    mHaveMoreInputs = false;
                }
            }
        }

        while (err == OK) {
            size_t offset, size;
            // wait for a decoded buffer
            err = decoder->dequeueOutputBuffer(
            err = mDecoder->dequeueOutputBuffer(
                    &index,
                    &offset,
                    &size,
                    &timeUs,
                    &ptsUs,
                    &flags,
                    kBufferTimeOutUs);

            if (err == INFO_FORMAT_CHANGED) {
                ALOGV("Received format change");
                err = decoder->getOutputFormat(&outputFormat);
                err = mDecoder->getOutputFormat(&mOutputFormat);
            } else if (err == INFO_OUTPUT_BUFFERS_CHANGED) {
                ALOGV("Output buffers changed");
                err = decoder->getOutputBuffers(&outputBuffers);
                err = OK;
            } else {
                if (err == -EAGAIN /* INFO_TRY_AGAIN_LATER */ && --retriesLeft > 0) {
                    ALOGV("Timed-out waiting for output.. retries left = %zu", retriesLeft);
@@ -355,12 +359,15 @@ status_t FrameDecoder::extractInternal(
                } else if (err == OK) {
                    // If we're seeking with CLOSEST option and obtained a valid targetTimeUs
                    // from the extractor, decode to the specified frame. Otherwise we're done.
                    ALOGV("Received an output buffer, timeUs=%lld", (long long)timeUs);
                    sp<MediaCodecBuffer> videoFrameBuffer = outputBuffers.itemAt(index);

                    err = onOutputReceived(videoFrameBuffer, outputFormat, timeUs, &done);

                    decoder->releaseOutputBuffer(index);
                    ALOGV("Received an output buffer, timeUs=%lld", (long long)ptsUs);
                    sp<MediaCodecBuffer> videoFrameBuffer;
                    err = mDecoder->getOutputBuffer(index, &videoFrameBuffer);
                    if (err != OK) {
                        ALOGE("failed to get output buffer %zu", index);
                        break;
                    }
                    err = onOutputReceived(videoFrameBuffer, mOutputFormat, ptsUs, &done);
                    mDecoder->releaseOutputBuffer(index);
                } else {
                    ALOGW("Received error %d (%s) instead of output", err, asString(err));
                    done = true;
@@ -370,9 +377,6 @@ status_t FrameDecoder::extractInternal(
        }
    } while (err == OK && !done);

    mSource->stop();
    decoder->release();

    if (err != OK) {
        ALOGE("failed to get video frame (err %d)", err);
    }
@@ -380,6 +384,20 @@ status_t FrameDecoder::extractInternal(
    return err;
}

//////////////////////////////////////////////////////////////////////

VideoFrameDecoder::VideoFrameDecoder(
        const AString &componentName,
        const sp<MetaData> &trackMeta,
        const sp<IMediaSource> &source)
    : FrameDecoder(componentName, trackMeta, source),
      mIsAvcOrHevc(false),
      mSeekMode(MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC),
      mTargetTimeUs(-1ll),
      mNumFrames(0),
      mNumFramesDecoded(0) {
}

sp<AMessage> VideoFrameDecoder::onGetFormatAndSeekOptions(
        int64_t frameTimeUs, size_t numFrames, int seekMode, MediaSource::ReadOptions *options) {
    mSeekMode = static_cast<MediaSource::ReadOptions::SeekMode>(seekMode);
@@ -511,33 +529,47 @@ status_t VideoFrameDecoder::onOutputReceived(
    return ERROR_UNSUPPORTED;
}

////////////////////////////////////////////////////////////////////////

ImageDecoder::ImageDecoder(
        const AString &componentName,
        const sp<MetaData> &trackMeta,
        const sp<IMediaSource> &source)
    : FrameDecoder(componentName, trackMeta, source),
      mFrame(NULL),
      mWidth(0),
      mHeight(0),
      mGridRows(1),
      mGridCols(1),
      mTilesDecoded(0) {
}

sp<AMessage> ImageDecoder::onGetFormatAndSeekOptions(
        int64_t frameTimeUs, size_t /*numFrames*/,
        int /*seekMode*/, MediaSource::ReadOptions *options) {
    sp<MetaData> overrideMeta;
    mThumbnail = false;
    if (frameTimeUs < 0) {
        uint32_t type;
        const void *data;
        size_t size;
        int32_t thumbWidth, thumbHeight;

        // if we have a stand-alone thumbnail, set up the override meta,
        // and set seekTo time to -1.
        if (!findThumbnailInfo(trackMeta(),
                &thumbWidth, &thumbHeight, &type, &data, &size)) {
        if (!findThumbnailInfo(trackMeta(), &mWidth, &mHeight, &type, &data, &size)) {
            ALOGE("Thumbnail not available");
            return NULL;
        }
        overrideMeta = new MetaData(*(trackMeta()));
        overrideMeta->remove(kKeyDisplayWidth);
        overrideMeta->remove(kKeyDisplayHeight);
        overrideMeta->setInt32(kKeyWidth, thumbWidth);
        overrideMeta->setInt32(kKeyHeight, thumbHeight);
        overrideMeta->setInt32(kKeyWidth, mWidth);
        overrideMeta->setInt32(kKeyHeight, mHeight);
        overrideMeta->setData(kKeyHVCC, type, data, size);
        options->setSeekTo(-1);
        mThumbnail = true;
    } else {
        CHECK(trackMeta()->findInt32(kKeyWidth, &mWidth));
        CHECK(trackMeta()->findInt32(kKeyHeight, &mHeight));

        options->setSeekTo(frameTimeUs);
    }

@@ -545,17 +577,10 @@ sp<AMessage> ImageDecoder::onGetFormatAndSeekOptions(
    if (overrideMeta == NULL) {
        // check if we're dealing with a tiled heif
        int32_t tileWidth, tileHeight, gridRows, gridCols;
        if (trackMeta()->findInt32(kKeyTileWidth, &tileWidth) && tileWidth > 0
         && trackMeta()->findInt32(kKeyTileHeight, &tileHeight) && tileHeight > 0
         && trackMeta()->findInt32(kKeyGridRows, &gridRows) && gridRows > 0
         && trackMeta()->findInt32(kKeyGridCols, &gridCols) && gridCols > 0) {
            int32_t width, height;
            CHECK(trackMeta()->findInt32(kKeyWidth, &width));
            CHECK(trackMeta()->findInt32(kKeyHeight, &height));

            if (width <= tileWidth * gridCols && height <= tileHeight * gridRows) {
        if (findGridInfo(trackMeta(), &tileWidth, &tileHeight, &gridRows, &gridCols)) {
            if (mWidth <= tileWidth * gridCols && mHeight <= tileHeight * gridRows) {
                ALOGV("grid: %dx%d, tile size: %dx%d, picture size: %dx%d",
                        gridCols, gridRows, tileWidth, tileHeight, width, height);
                        gridCols, gridRows, tileWidth, tileHeight, mWidth, mHeight);

                overrideMeta = new MetaData(*(trackMeta()));
                overrideMeta->setInt32(kKeyWidth, tileWidth);
@@ -563,8 +588,8 @@ sp<AMessage> ImageDecoder::onGetFormatAndSeekOptions(
                mGridCols = gridCols;
                mGridRows = gridRows;
            } else {
                ALOGE("bad grid: %dx%d, tile size: %dx%d, picture size: %dx%d",
                        gridCols, gridRows, tileWidth, tileHeight, width, height);
                ALOGE("ignore bad grid: %dx%d, tile size: %dx%d, picture size: %dx%d",
                        gridCols, gridRows, tileWidth, tileHeight, mWidth, mHeight);
            }
        }
        if (overrideMeta == NULL) {
@@ -600,17 +625,8 @@ status_t ImageDecoder::onOutputReceived(
    CHECK(outputFormat->findInt32("width", &width));
    CHECK(outputFormat->findInt32("height", &height));

    int32_t imageWidth, imageHeight;
    if (mThumbnail) {
        CHECK(trackMeta()->findInt32(kKeyThumbnailWidth, &imageWidth));
        CHECK(trackMeta()->findInt32(kKeyThumbnailHeight, &imageHeight));
    } else {
        CHECK(trackMeta()->findInt32(kKeyWidth, &imageWidth));
        CHECK(trackMeta()->findInt32(kKeyHeight, &imageHeight));
    }

    if (mFrame == NULL) {
        sp<IMemory> frameMem = allocVideoFrame(trackMeta(), imageWidth, imageHeight, dstBpp());
        sp<IMemory> frameMem = allocVideoFrame(trackMeta(), mWidth, mHeight, dstBpp());
        mFrame = static_cast<VideoFrame*>(frameMem->pointer());

        addFrame(frameMem);
@@ -638,12 +654,12 @@ status_t ImageDecoder::onOutputReceived(

    // apply crop on bottom-right
    // TODO: need to move this into the color converter itself.
    if (dstRight >= imageWidth) {
        crop_right = imageWidth - dstLeft - 1;
    if (dstRight >= mWidth) {
        crop_right = mWidth - dstLeft - 1;
        dstRight = dstLeft + crop_right;
    }
    if (dstBottom >= imageHeight) {
        crop_bottom = imageHeight - dstTop - 1;
    if (dstBottom >= mHeight) {
        crop_bottom = mHeight - dstTop - 1;
        dstBottom = dstTop + crop_bottom;
    }

+17 −14
Original line number Diff line number Diff line
@@ -193,12 +193,14 @@ sp<IMemory> StagefrightMetadataRetriever::getImageAtIndex(
    for (size_t i = 0; i < matchingCodecs.size(); ++i) {
        const AString &componentName = matchingCodecs[i];
        ImageDecoder decoder(componentName, trackMeta, source);
        sp<IMemory> frame = decoder.extractFrame(
                thumbnail ? -1 : 0 /*frameTimeUs*/, 0 /*seekMode*/, colorFormat);
        int64_t frameTimeUs = thumbnail ? -1 : 0;
        if (decoder.init(frameTimeUs, 1 /*numFrames*/, 0 /*option*/, colorFormat) == OK) {
            sp<IMemory> frame = decoder.extractFrame();

            if (frame != NULL) {
                return frame;
            }
        }
        ALOGV("%s failed to extract thumbnail, trying next decoder.", componentName.c_str());
    }

@@ -307,18 +309,19 @@ status_t StagefrightMetadataRetriever::getFrameInternal(
    for (size_t i = 0; i < matchingCodecs.size(); ++i) {
        const AString &componentName = matchingCodecs[i];
        VideoFrameDecoder decoder(componentName, trackMeta, source);
        if (decoder.init(timeUs, numFrames, option, colorFormat) == OK) {
            if (outFrame != NULL) {
            *outFrame = decoder.extractFrame(timeUs, option, colorFormat);
                *outFrame = decoder.extractFrame();
                if (*outFrame != NULL) {
                    return OK;
                }
            } else if (outFrames != NULL) {
            status_t err = decoder.extractFrames(
                    timeUs, numFrames, option, colorFormat, outFrames);
                status_t err = decoder.extractFrames(outFrames);
                if (err == OK) {
                    return OK;
                }
            }
        }
        ALOGV("%s failed to extract frame, trying next decoder.", componentName.c_str());
    }

+18 −32
Original line number Diff line number Diff line
@@ -32,32 +32,26 @@ struct AMessage;
class MediaCodecBuffer;
class IMediaSource;
class VideoFrame;
struct MediaCodec;

struct FrameDecoder {
    FrameDecoder(
            const AString &componentName,
            const sp<MetaData> &trackMeta,
            const sp<IMediaSource> &source) :
                mComponentName(componentName),
                mTrackMeta(trackMeta),
                mSource(source),
                mDstFormat(OMX_COLOR_Format16bitRGB565),
                mDstBpp(2) {}
            const sp<IMediaSource> &source);

    sp<IMemory> extractFrame(int64_t frameTimeUs, int option, int colorFormat);
    status_t init(
            int64_t frameTimeUs, size_t numFrames, int option, int colorFormat);

    status_t extractFrames(
            int64_t frameTimeUs,
            size_t numFrames,
            int option,
            int colorFormat,
            std::vector<sp<IMemory> >* frames);
    sp<IMemory> extractFrame();

    status_t extractFrames(std::vector<sp<IMemory> >* frames);

    static sp<IMemory> getMetadataOnly(
            const sp<MetaData> &trackMeta, int colorFormat, bool thumbnail = false);

protected:
    virtual ~FrameDecoder() {}
    virtual ~FrameDecoder();

    virtual sp<AMessage> onGetFormatAndSeekOptions(
            int64_t frameTimeUs,
@@ -92,13 +86,13 @@ private:
    OMX_COLOR_FORMATTYPE mDstFormat;
    int32_t mDstBpp;
    std::vector<sp<IMemory> > mFrames;
    MediaSource::ReadOptions mReadOptions;
    sp<MediaCodec> mDecoder;
    sp<AMessage> mOutputFormat;
    bool mHaveMoreInputs;
    bool mFirstSample;

    static bool getDstColorFormat(
            android_pixel_format_t colorFormat,
            OMX_COLOR_FORMATTYPE *dstFormat,
            int32_t *dstBpp);

    status_t extractInternal(int64_t frameTimeUs, size_t numFrames, int option);
    status_t extractInternal();

    DISALLOW_EVIL_CONSTRUCTORS(FrameDecoder);
};
@@ -107,13 +101,7 @@ struct VideoFrameDecoder : public FrameDecoder {
    VideoFrameDecoder(
            const AString &componentName,
            const sp<MetaData> &trackMeta,
            const sp<IMediaSource> &source) :
                FrameDecoder(componentName, trackMeta, source),
                mIsAvcOrHevc(false),
                mSeekMode(MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC),
                mTargetTimeUs(-1ll),
                mNumFrames(0),
                mNumFramesDecoded(0) {}
            const sp<IMediaSource> &source);

protected:
    virtual sp<AMessage> onGetFormatAndSeekOptions(
@@ -146,10 +134,7 @@ struct ImageDecoder : public FrameDecoder {
    ImageDecoder(
            const AString &componentName,
            const sp<MetaData> &trackMeta,
            const sp<IMediaSource> &source) :
                FrameDecoder(componentName, trackMeta, source),
                mFrame(NULL), mGridRows(1), mGridCols(1),
                mTilesDecoded(0), mThumbnail(false) {}
            const sp<IMediaSource> &source);

protected:
    virtual sp<AMessage> onGetFormatAndSeekOptions(
@@ -172,10 +157,11 @@ protected:

private:
    VideoFrame *mFrame;
    int32_t mWidth;
    int32_t mHeight;
    int32_t mGridRows;
    int32_t mGridCols;
    int32_t mTilesDecoded;
    bool mThumbnail;
};

}  // namespace android