Android 视频精准搜索

Android Precise seeking of video

我正在努力使用 MediaExtractor 的 seekTo() 进行精确搜索。虽然我可以毫无问题地寻求同步帧,但我想寻求特定时间。 This question led me to some ideas how to do this, but I'm not sure if they are valid. Basicly, I would have to seek to closest previous sync frame and then advance() the extractor until target time is reached. Every frame in the process would be fed to the decoder, i.e the first I-frame and the rest P-frames. This is related code snippet (based on google/grafika 的 MoviePlayer):

extractor.seekTo((long) seekTarget[threadNr], MediaExtractor.SEEK_TO_PREVIOUS_SYNC);

...

while (extractor.getSampleTime() < (long) seekTarget[threadNr]) {
    Log.d(TAG, "Thread " + threadNr + " advanced to timestamp " + extractor.getSampleTime());

    int inputBufIndex = decoder.dequeueInputBuffer(TIMEOUT_USEC);
    if (inputBufIndex >= 0) {
        ByteBuffer inBufer = decoderInputBuffers[inputBufIndex];
        int chunkSize = extractor.readSampleData(inBufer, 0);

        if (chunkSize < 0) {
            // End of stream -- send empty frame with EOS flag set.
            decoder.queueInputBuffer(inputBufIndex, 0, 0, 0L,
                    MediaCodec.BUFFER_FLAG_END_OF_STREAM);
            inputDone = true;
            if (VERBOSE) Log.d(TAG, "sent input EOS");
        } else {
            if (extractor.getSampleTrackIndex() != trackIndex) {
                Log.w(TAG, "WEIRD: got sample from track " +
                        extractor.getSampleTrackIndex() + ", expected " + trackIndex);
            }

            long presentationTimeUs = extractor.getSampleTime();
            decoder.queueInputBuffer(inputBufIndex, 0, chunkSize,
                    presentationTimeUs, 0 /*flags*/);
            if (VERBOSE) {
                Log.d(TAG, "submitted frame " + inputChunk + " to dec, size=" +
                        chunkSize + " inputBufIndex: " + inputBufIndex);
            }
            inputChunk++;
            extractor.advance();
        }
    }
}

如您所想,通常我会排队处理大量帧,但现在我可以接受内存消耗或最终延迟。问题是 dequeueInputBuffer() 方法只在循环中工作一段时间,最终卡在返回 -1 处,根据文档,这意味着缓冲区不可用。如果我将 TIMEOUT_USEC 更改为 -1,则会出现无限循环。

有人可以告诉我这种方法是否正确,或者为什么有时我无法访问 inputBuffer

您似乎没有从输出端提取缓冲区。 MediaCodec 解码器不会丢帧,因此当其内部缓冲区填满时,它将停止为您提供输入缓冲区。

您需要通过请求输出缓冲区来耗尽解码器。释放缓冲区时,将 "render" 标志设置为 false,这样它就不会出现在屏幕上。