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

Posted

技术标签:

【中文标题】如何使用 Android MediaCodec 编码相机数据(YUV420sp)【英文标题】:How to use Android MediaCodec encode Camera data(YUV420sp) 【发布时间】:2013-06-18 10:07:21 【问题描述】:

感谢您的关注! 我想使用 android MediaCodec APIs 对从相机获取的视频帧进行编码, 不幸的是,我没有成功做到这一点!我对 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);

我猜是编码器方法的问题,该方法会在Camera Preview Callback中使用,比如

initCodec();

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

我只是不知道如何正确使用 MediaCodec API。你能给我一些建议或链接吗?

谢谢!

【问题讨论】:

你是用它来录制没有音频的视频吗,我想知道如何使用它和MediaMuxer一起录制视频和音频 【参考方案1】:

我已经解决了问题。如下:

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 and Camera: colorspaces don't match

【讨论】:

我得到了这个错误。如何解决这个 pblm....java.lang.NoClassDefFoundError: android.media.MediaCodec 错误指出这一行:mMediaCodec = MediaCodec.createEncoderByType("video/avc" ); @Aravi 看来您尝试在具有 API 的 Android 上使用 MediaCodec 【参考方案2】:

摄像机输出的 YUV420 格式与 MediaCodec AVC 编码器接受的格式不兼容。在最好的情况下,它本质上是 NV12 与 NV21(U 和 V 平面颠倒),需要手动重新排序。在最坏的情况下,从 Android 4.2 开始,编码器输入格式可能是特定于设备的。

您最好使用MediaRecorder 将相机硬件连接到编码器。

更新: 现在可以将相机的 Surface 预览传递给 MediaCodec,而不是使用 ByteBuffer 中的 YUV 数据。这更快,更便携。请参阅 CameraToMpegTest 示例 here。

【讨论】:

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

以上是关于如何使用 Android MediaCodec 编码相机数据(YUV420sp)的主要内容,如果未能解决你的问题,请参考以下文章

Android 音视频 - MediaCodec 编解码音视频

android硬编解码MediaCodec

Android 音视频编解码 -- 视频编码和H264格式原理讲解

Android 音视频编解码 -- 视频编码和H264格式原理讲解

如何使用 Android MediaCodec 生成 AAC ADTS 基本流

Android MediaCodec+OpenGL视频编解码实践笔记