1

播放 .ogg 文件时,在调用 MediaExtractor.seekTo() 后,.dequeueOutputBuffer() 总是超时,MediaCodec.INFO_TRY_AGAIN_LATER。这会导致问题,因为我正在尝试创建近乎无缝的搜索。最大阻塞时间无关紧要,无论设置多长时间它总是超时。

所有 .ogg 文件都会发生这种情况,而没有其他音频文件类型。

这是相关代码,超时发生在

final int res = codec.dequeueOutputBuffer(info, TIMEOUT_US);

每次使用 .ogg 文件调用 seekTo() 后都会发生这种情况,有没有办法纠正这个问题?

public MediaCodecMp3Decoder(String fullPath) throws IOException
    {
        extractor = new MediaExtractor();
        extractor.setDataSource(fullPath);

        format = extractor.getTrackFormat(0);
        String mime = format.getString(MediaFormat.KEY_MIME);
        durationUs = format.getLong(MediaFormat.KEY_DURATION);

        codec = MediaCodec.createDecoderByType(mime);
        codec.configure(format, null, null, 0);
        codec.start();
        codecInputBuffers = codec.getInputBuffers();
        codecOutputBuffers = codec.getOutputBuffers();

        extractor.selectTrack(0);
        info = new MediaCodec.BufferInfo();
    }


public byte[] decodeChunk()
    {
        advanceInput();

        final int res = codec.dequeueOutputBuffer(info, TIMEOUT_US);
        if (res >= 0)
        {
            int outputBufIndex = res;
            ByteBuffer buf = codecOutputBuffers[outputBufIndex];
            if(chunk == null || chunk.length != info.size)
            {
                chunk = new byte[info.size];
            }
            buf.get(chunk);
            buf.clear();
            codec.releaseOutputBuffer(outputBufIndex, false);
        }
        if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0)
        {
            sawOutputEOS = true;
        }
        else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED)
        {
            codecOutputBuffers = codec.getOutputBuffers();
        }
        else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED)
        {
            format = codec.getOutputFormat();
            Log.d("MP3", "Output format has changed to " + format);
        }

        return chunk;
    }

private void advanceInput()
{
    boolean sawInputEOS = false;

    int inputBufIndex = codec.dequeueInputBuffer(TIMEOUT_US);
    if (inputBufIndex >= 0)
    {
        ByteBuffer dstBuf = codecInputBuffers[inputBufIndex];

        int sampleSize = extractor.readSampleData(dstBuf, 0);
        long presentationTimeUs = 0;

        if (sampleSize < 0)
        {
            sawInputEOS = true;
            sampleSize = 0;
        }
        else
        {
            presentationTimeUs = extractor.getSampleTime();
            currentTimeUs += presentationTimeUs - lastPresentationTime;
            lastPresentationTime = presentationTimeUs;
        }

        codec.queueInputBuffer(inputBufIndex,
                0,
                sampleSize,
                presentationTimeUs,
                sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
        if (!sawInputEOS)
        {
            extractor.advance();
        }
    }
}

public void seek(long timeInUs)
    {
        extractor.seekTo(timeInUs, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
        lastPresentationTime = currentTimeUs = timeInUs;
        codec.flush();
    }

这是在 seekTo() 之前和之后唯一的 logcat,AudioTrack 上的缓冲区不足是由于我目前为 dequeueOutputBuffer() 设置的 1 秒超时

03-01 13:48:25.042: I/AudioFlinger(125): BUFFER TIMEOUT: remove(4099) from active list on thread 0x40b42008
03-01 13:48:25.312: W/AudioTrack(29349): releaseBuffer() track 0x6a110c10 name=s:125;n:3;f:-1 disabled due to previous underrun, restarting

另外我应该注意到对 seekTo() 和 decodeChunk() 的调用发生在不同的线程上,但它们在同一个对象上同步。

synchronized (decodeLock)
{
    decoder.seek(timeInUs);
}

synchronized (decodeLock)
{
    input = decoder.decodeChunk();
...
}
4

1 回答 1

0

seek调用的实现codec.flush将丢弃输出队列中的任何解码缓冲区。如果decoder.seek就在之前,decoder.decodeChunk则输出队列将为空。确保在调用seek.

此外,某些解码器(芯片组/OEM 相关)将具有输入阈值。在期望任何输出缓冲区之前尝试advanceInput多次调用。

例如,请参阅 MediaCodecTrackRenderer.java中的 ExoPlayer 的输入/输出工作循环。

于 2015-06-28T01:33:23.907 回答