如何使用Android MediaCodec对相机数据(YUV420sp)进行编码

14

感谢您的关注!我想使用Android MediaCodec API对从相机获取的视频帧进行编码,但很遗憾,我还没有成功。我对MediaCodec API仍不熟悉。以下是我的代码,我需要您的帮助来找出我应该做什么。

1、相机设置:

Parameters parameters = mCamera.getParameters();
parameters.setPreviewFormat(ImageFormat.NV21);
parameters.setPreviewSize(320, 240);
mCamera.setParameters(parameters);

2、设置编码器:

private void initCodec() {
    try {
        fos = new FileOutputStream(mVideoFile, false);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }
    mMediaCodec = MediaCodec.createEncoderByType("video/avc");
    MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc",
            320,
            240);
    mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 125000);
    mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 15);
    mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,
            MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar);
    mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5);
    mMediaCodec.configure(mediaFormat,
            null,
            null,
            MediaCodec.CONFIGURE_FLAG_ENCODE);
    mMediaCodec.start();
    inputBuffers = mMediaCodec.getInputBuffers();
    outputBuffers = mMediaCodec.getOutputBuffers();
}

private void encode(byte[] data) {
    int inputBufferIndex = mMediaCodec.dequeueInputBuffer(0);
    if (inputBufferIndex >= 0) {
        ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
        inputBuffer.clear();
        inputBuffer.put(data);
        mMediaCodec.queueInputBuffer(inputBufferIndex, 0, data.length, 0, 0);
    } else {
        return;
    }

    MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
    int outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo, 0);
    Log.i(TAG, "outputBufferIndex-->" + outputBufferIndex);
    do {
        if (outputBufferIndex >= 0) {
            ByteBuffer outBuffer = outputBuffers[outputBufferIndex];
            System.out.println("buffer info-->" + bufferInfo.offset + "--"
                    + bufferInfo.size + "--" + bufferInfo.flags + "--"
                    + bufferInfo.presentationTimeUs);
            byte[] outData = new byte[bufferInfo.size];
            outBuffer.get(outData);
            try {
                if (bufferInfo.offset != 0) {
                    fos.write(outData, bufferInfo.offset, outData.length
                            - bufferInfo.offset);
                } else {
                    fos.write(outData, 0, outData.length);
                }
                fos.flush();
                Log.i(TAG, "out data -- > " + outData.length);
                mMediaCodec.releaseOutputBuffer(outputBufferIndex, false);
                outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo,
                        0);
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
            outputBuffers = mMediaCodec.getOutputBuffers();
        } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
            MediaFormat format = mMediaCodec.getOutputFormat();
        }
    } while (outputBufferIndex >= 0);
}
我猜问题出在编码方法中,该方法将被用于相机预览回调中,例如:
initCodec();

//mCamera.setPreviewCallback(new MyPreviewCallback());
mCamera.setPreviewCallback(new PreviewCallback() {
    @Override
    public void onPreviewFrame(byte[] data, Camera camera) {
        encode(data);
    }
});

我不知道如何正确使用MediaCodec API,你能给我一些关于它的建议或链接吗?

谢谢!


你只是用它来录制没有音频的视频吗?我想知道如何使用MediaMuxer来录制视频和音频。 - user924
2个回答

8
我已经解决了这个问题,具体如下:

private synchronized void encode(byte[] data)
{
    inputBuffers = mMediaCodec.getInputBuffers();// here changes
    outputBuffers = mMediaCodec.getOutputBuffers();

    int inputBufferIndex = mMediaCodec.dequeueInputBuffer(-1);
    Log.i(TAG, "inputBufferIndex-->" + inputBufferIndex);
    //......

接下来,您会发现您的编码视频颜色不正确,更多信息,请访问此处 MediaCodec和相机:颜色空间不匹配


1
我收到了这个错误,该如何解决呢...java.lang.NoClassDefFoundError: android.media.MediaCodec,错误指出这一行:mMediaCodec = MediaCodec.createEncoderByType("video/avc")。 - Aravi
1
@Aravi 看起来你正在尝试在 Android API < 16 上使用 MediaCodec。 - SILINIK

7
相机输出的YUV420格式与MediaCodec AVC编码器接受的格式不兼容。在最好的情况下,它基本上是NV12与NV21(U和V平面被颠倒),需要手动重新排序。在最坏的情况下,从Android 4.2开始,编码器输入格式可能是特定于设备的。
最好使用MediaRecorder将相机硬件连接到编码器。
更新: 现在可以将相机的Surface预览传递给MediaCodec,而不是使用ByteBuffer中的YUV数据。这更快,更可移植。请参见此处的CameraToMpegTest示例here

谢谢您的帮助。我录制了一个视频文件,但是文件数据不正确。起始数据是正确的,它们是H.264帧数据,但大约三帧后,H.264帧数据全部填充为0。我认为可能是文件缓冲区错误,但它不可能是0。 - Read Mark
嗨,@fadden。关于“最好使用MediaRecorder将相机硬件连接到编码器”,有没有示例代码?谢谢。 - Jerikc XIONG
MediaRecorder,Surface 不允许在录制之前处理帧,你认为这为什么更快?我认为作者知道 MediaRecorder,但正如您所看到的,他问的是相机帧。 - user924

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接