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

Commit d2181387 authored by Chong Zhang's avatar Chong Zhang
Browse files

Fix MediaMetadataRetriever seek option OPTION_CLOSEST

When seeking with OPTION_CLOSEST, extractor returns the actual
closest target time via kKeyTargetTime, and read sample is the
sync frame before that. extractVideoFrame() needs to decode to
target time.

bug: 25783325
Change-Id: I94d6c196e2378b5efb608829b11aacf252d1fd46
parent 3b6c92f8
Loading
Loading
Loading
Loading
+19 −4
Original line number Diff line number Diff line
@@ -254,6 +254,10 @@ static VideoFrame *extractVideoFrame(
    bool isAvcOrHevc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)
            || !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC);

    bool isSeekingClosest = (seekMode == MediaSource::ReadOptions::SEEK_CLOSEST);
    bool firstSample = true;
    int64_t targetTimeUs = -1ll;

    do {
        size_t inputIndex = -1;
        int64_t ptsUs = 0ll;
@@ -280,6 +284,11 @@ static VideoFrame *extractVideoFrame(
                haveMoreInputs = false;
                break;
            }
            if (firstSample && isSeekingClosest) {
                mediaBuffer->meta_data()->findInt64(kKeyTargetTime, &targetTimeUs);
                ALOGV("Seeking closest: targetTimeUs=%lld", (long long)targetTimeUs);
            }
            firstSample = false;

            if (mediaBuffer->range_length() > codecBuffer->capacity()) {
                ALOGE("buffer size (%zu) too large for codec input size (%zu)",
@@ -292,8 +301,9 @@ static VideoFrame *extractVideoFrame(
                memcpy(codecBuffer->data(),
                        (const uint8_t*)mediaBuffer->data() + mediaBuffer->range_offset(),
                        mediaBuffer->range_length());
                if (isAvcOrHevc && IsIDR(codecBuffer)) {
                    // Only need to decode one IDR frame.
                if (isAvcOrHevc && IsIDR(codecBuffer) && !isSeekingClosest) {
                    // Only need to decode one IDR frame, unless we're seeking with CLOSEST
                    // option, in which case we need to actually decode to targetTimeUs.
                    haveMoreInputs = false;
                    flags |= MediaCodec::BUFFER_FLAG_EOS;
                }
@@ -340,8 +350,13 @@ static VideoFrame *extractVideoFrame(
                    ALOGV("Timed-out waiting for output.. retries left = %zu", retriesLeft);
                    err = OK;
                } else if (err == OK) {
                    ALOGV("Received an output buffer");
                    done = true;
                    // If we're seeking with CLOSEST option and obtained a valid targetTimeUs
                    // from the extractor, decode to the specified frame. Otherwise we're done.
                    done = (targetTimeUs < 0ll) || (timeUs >= targetTimeUs);
                    ALOGV("Received an output buffer, timeUs=%lld", (long long)timeUs);
                    if (!done) {
                        err = decoder->releaseOutputBuffer(index);
                    }
                } else {
                    ALOGW("Received error %d (%s) instead of output", err, asString(err));
                    done = true;