我想解碼mp4文件並將解碼輸出饋送到編碼器並將其保存爲新的mp4文件。這聽起來有些不必要/奇怪。這也可以使用編碼器輸入表面作爲解碼器輸出表面來完成。但是,我想對此做一些修改,以便實現某些不同(通過改變幀的順序),一旦我得到這個工作(使用ByteBuffer)。無法使用MediaCodec將解碼器輸出饋送到編碼器輸入
但是,當我嘗試這個時,我得到下面的錯誤,當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
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 APIs。已經遠遠地達到了這個目標。但是真的停留在這裏。任何幫助,這是高度讚賞。