简体   繁体   English

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

[英]How to use Android MediaCodec encode Camera data(YUV420sp)

Thank you for your focus! 谢谢你的关注! I want to use Android MediaCodec APIs to encode the video frame which aquired from Camera, unfortunately, I have not success to do that! 我想使用Android MediaCodec API对从Camera获取的视频帧进行编码,遗憾的是,我没有成功做到这一点! I still not familiar with the MediaCodec API。 The follow is my codes,I need your help to figure out what I should do. 我仍然不熟悉MediaCodec API。以下是我的代码,我需要你的帮助来弄清楚我应该做什么。

1、The Camera setting: 1,相机设置:

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

2、Set the encoder: 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);
}

I guess the problem occurred in the encoder method,the method will be used in the Camera Preview Callback ,like 我猜这个问题发生在编码器方法中,该方法将用于Camera Preview Callback,就像

initCodec();

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

I just have no idea how to do it correctly with the MediaCodec API.Can you give me some advice or links about it? 我只是不知道如何使用MediaCodec API正确地完成它。你能给我一些建议或链接吗?

Thank you! 谢谢!

I have solved the problem.As follows: 我已经解决了这个问题。如下:

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

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

And next,you will find your encoded video color is not right, for more information,please go to here MediaCodec and Camera: colorspaces don't match 接下来,你会发现你的编码视频颜色不对,有关更多信息,请转到此处MediaCodec和相机:颜色空间不匹配

The YUV420 formats output by the camera are incompatible with the formats accepted by the MediaCodec AVC encoder. 相机输出的YUV420格式与MediaCodec AVC编码器接受的格式不兼容。 In the best case, it's essentially NV12 vs. NV21 (U and V planes are reversed), requiring a manual reordering. 在最好的情况下,它基本上是NV12与NV21(U和V平面相反),需要手动重新排序。 In the worst case, as of Android 4.2, the encoder input format may be device-specific. 在最坏的情况下,从Android 4.2开始,编码器输入格式可能是特定于设备的。

You're better off using MediaRecorder to connect the camera hardware to the encoder. 您最好使用MediaRecorder将相机硬件连接到编码器。

Update: It's now possible to pass the camera's Surface preview to MediaCodec , instead of using the YUV data in the ByteBuffer . 更新:现在可以将相机的Surface预览传递给MediaCodec ,而不是使用ByteBuffer的YUV数据。 This is faster and more portable. 这更快,更便携。 See the CameraToMpegTest sample here . 请在此处查看CameraToMpegTest示例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM