无法使用MediaCodec将解码器输出馈送到编码器输入

时间:2016-08-25 10:14:54

标签: java android video encoding mediacodec

我想解码mp4文件并将解码后的输出提供给编码器并将其保存为新的mp4文件。这可能听起来有些不必要/怪异。这也可以使用编码器输入表面作为解码器输出表面来完成。但是一旦我开始工作(使用ByteBuffer),我想对它做一些修改以实现不同的东西(通过改变帧的顺序)。

但是当我尝试这个时,我会在调用Encoder.dequeueOutputBuffer(bufferInfo, TIME_OUT_US)时收到以下错误。

frameworks/av/media/libstagefright/ACodec.cpp:4886 CHECK_EQ( mCodec->mOMX->emptyBuffer( mCodec->mNode, bufferID, 0, buffer->size(), flags, timeUs),(status_t)OK) failed: -2147483648 vs. 0
A/libc: Fatal signal 6 (SIGABRT), code -6 in tid 31543 (CodecLooper)

准备编码器

// parameters for the encoder
private static final String MIME_TYPE = "video/avc";    // H.264 Advanced Video Coding
private static final int FRAME_RATE = 15;               // 15fps
private static final int IFRAME_INTERVAL = 10; 
private int mBitRate = 2000000;

private void prepareEncoder() throws IOException {

    MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, mVideoWidth, mVideoHeight);

    // Set some properties.  Failing to specify some of these can cause the MediaCodec
    // configure() call to throw an unhelpful exception.
    format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
            MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
    format.setInteger(MediaFormat.KEY_BIT_RATE, mBitRate);
    format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);
    format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
    Log.d(TAG, "format: " + format);

    mEncoder = MediaCodec.createEncoderByType(MIME_TYPE);
    mEncoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);

//        mEncoderInputSurface = mEncoder.createInputSurface();

    mEncoder.start();

    Log.d(TAG, "prepareEncoder: Done");

}

处理解码器输出

它基本上将输出缓冲区数据复制到ArrayList,该ArrayList作为队列供以后由编码器

使用
private boolean doDecoderOutput(MediaCodec.BufferInfo bufferInfo) {

    int decoderStatus = mDecoder.dequeueOutputBuffer(bufferInfo, TIME_OUT_US);

    if (decoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
        //Output buffer not available will try later
        return false;
    } else if (decoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
        //Output buffer has changed
        //TODO this is deprecated
        mDecoderOutputBuffers = mDecoder.getOutputBuffers();
        return false;
    } else if (decoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
        //TODO what?
        return false;
    } else if (decoderStatus < 0) {
        //Unknown status
    } else {
        //decoderStatus > 0

        boolean endOfStream = (bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;

        ByteBuffer decoderOutputBuffer = mDecoderOutputBuffers[decoderStatus];

        if (!endOfStream) {
            if((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
                /*decoderOutputBuffer.position(bufferInfo.offset);
                decoderOutputBuffer.limit(bufferInfo.offset + bufferInfo.size);*/
                mEncoderInputQueue.add(new VideoChunk(decoderOutputBuffer, bufferInfo.flags, bufferInfo.presentationTimeUs));
                Log.d(TAG, "doDecoderOutput: " + "no config");
            }else{
                Log.d("ReverseTask", "doDecoderOutput : found config");
            }
        } else {
            mEncoderInputQueue.add(null);//This informs encoder that EOS has reached
        }

        Log.d(TAG, "doDecoderOutput: " + mDecoderOutputCount);
        mDecoderOutputCount++;

        mDecoder.releaseOutputBuffer(decoderStatus, true);

        if (endOfStream) {
            //End of output stream
            Log.d(TAG, "doDecoderOutput: End of stream. Frame no :" + mDecoderOutputCount);
            return true;
        }
    }
    return false;
}

处理编码器输入

private boolean doEncoderInput() {
    if (mEncoderInputQueue.isEmpty()) {
        //No frames queued for encode
        return false;
    }
    int inputBufferIndex = mEncoder.dequeueInputBuffer(TIME_OUT_US);

    if (inputBufferIndex < 0) {
        //Input buffer not available. Try again later.
        return false;
    }

    Log.d(TAG, "doEncoderInput: " + mEncoderInputCount);
    mEncoderInputCount++;

    ByteBuffer encoderInputBuffer = mEncoderInputBuffers[inputBufferIndex];

    VideoChunk videoChunk = mEncoderInputQueue.remove(0);
    if (videoChunk == null) {
        //End of stream
        Log.d(TAG, "doEncoderInput: End of stream. Frame no " + mEncoderInputCount);
        mEncoder.queueInputBuffer(inputBufferIndex, 0, 0, 0L, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
    } else {

        Log.d(TAG, "doEncoderInput: chunk length : " + videoChunk.getLength() + " buffer capacity : " + encoderInputBuffer.capacity());
        videoChunk.copyTo(encoderInputBuffer);

        mEncoder.queueInputBuffer(inputBufferIndex, 0, videoChunk.getLength(), videoChunk.getPresentationTimeUs(), 0);//videoChunk.getFlags());
        videoChunk.release();
        Log.d(TAG, "doEncoderInput: sent encoder input");
    }
    return true;
}

处理编码器输出。

最终我应该使用Muxer将流保存到文件中。在这里,我只是忽略编码器输出,直到我得到这堆代码没有错误。

private boolean doEncoderOutput(MediaCodec.BufferInfo bufferInfo) {

    Log.d("ReverseTask", "doEncoderOutput : start");
    int encoderStatus = mEncoder.dequeueOutputBuffer(bufferInfo, TIME_OUT_US);

    Log.d("ReverseTask", "doEncoderOutput : encoder status " + encoderStatus);
    if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
        //Output buffer not available will try later
        return false;
    } else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
        //Output buffer has changed
        //TODO this is deprecated
        mEncoderOutputBuffers = mEncoder.getOutputBuffers();
        return false;
    } else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
        //TODO what?
        return false;
    } else if (encoderStatus < 0) {
        //Unknown status
    } else {
        //encoderStatus > 0

        Log.d(TAG, "doEncoderOutput: " + mEncoderOutputCount);
        mEncoderOutputCount++;

        ByteBuffer encoderOutputBuffer = mEncoderOutputBuffers[encoderStatus];


        Log.d(TAG, "doEncoderOutput: releasing output buffer");
        mEncoder.releaseOutputBuffer(encoderStatus, false);

        if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
            //End of output stream
            Log.d(TAG, "doEncoderOutput: End of stream. Frame no :" + mEncoderOutputCount);
            return true;
        }
    }
    return false;
}

过去1-2周我一直在学习使用MediaCodec API。已经远远地到达了这个地方。但真的卡在这里。对此的任何帮助都非常感谢。

1 个答案:

答案 0 :(得分:0)

A/libc: Fatal signal 6 (SIGABRT)

如果您将未初始化的BufferInfo传递给dequeueOutputBuffer(...),则会出现此错误。所以,请确保在使用之前调用bufferInfo = new MediaCodec.BufferInfo()