使用 Android MediaCodec 从摄像头编码 H.264

2024-02-28

我正在尝试让它在 Android 4.1 上运行(使用升级的 Asus Transformer 平板电脑)。谢谢亚历克斯对我之前问题的回答 https://stackoverflow.com/a/13420558/726156,我已经能够将一些原始 H.264 数据写入文件,但该文件只能用ffplay -f h264,似乎它丢失了有关帧速率(极快播放)的所有信息。此外,颜色空间看起来不正确(atm 使用编码器侧相机的默认设置)。

public class AvcEncoder {

private MediaCodec mediaCodec;
private BufferedOutputStream outputStream;

public AvcEncoder() { 
    File f = new File(Environment.getExternalStorageDirectory(), "Download/video_encoded.264");
    touch (f);
    try {
        outputStream = new BufferedOutputStream(new FileOutputStream(f));
        Log.i("AvcEncoder", "outputStream initialized");
    } catch (Exception e){ 
        e.printStackTrace();
    }

    mediaCodec = 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);
    mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
    mediaCodec.start();
}

public void close() {
    try {
        mediaCodec.stop();
        mediaCodec.release();
        outputStream.flush();
        outputStream.close();
    } catch (Exception e){ 
        e.printStackTrace();
    }
}

// called from Camera.setPreviewCallbackWithBuffer(...) in other class
public void offerEncoder(byte[] input) {
    try {
        ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers();
        ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers();
        int inputBufferIndex = mediaCodec.dequeueInputBuffer(-1);
        if (inputBufferIndex >= 0) {
            ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
            inputBuffer.clear();
            inputBuffer.put(input);
            mediaCodec.queueInputBuffer(inputBufferIndex, 0, input.length, 0, 0);
        }

        MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
        int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo,0);
        while (outputBufferIndex >= 0) {
            ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
            byte[] outData = new byte[bufferInfo.size];
            outputBuffer.get(outData);
            outputStream.write(outData, 0, outData.length);
            Log.i("AvcEncoder", outData.length + " bytes written");

            mediaCodec.releaseOutputBuffer(outputBufferIndex, false);
            outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);

        }
    } catch (Throwable t) {
        t.printStackTrace();
    }

}

将编码器类型更改为“video/mp4”显然可以解决帧速率问题,但由于主要目标是提供流媒体服务,因此这不是一个好的解决方案。

我知道考虑到 SPS 和 PPS NALU,我删除了 Alex 的一些代码,但我希望这不是必需的,因为该信息也来自outData我假设编码器会正确格式化它。如果不是这种情况,我应该如何在我的文件/流中安排不同类型的 NALU?

那么,为了制作有效的、工作的 H.264 流,我在这里缺少什么?我应该使用哪些设置来匹配相机的色彩空间和编码器的色彩空间?

我感觉这更像是一个与 H.264 相关的问题,而不是 Android/MediaCodec 主题。或者我仍然没有正确使用 MediaCodec API?

提前致谢。


对于您的快速播放 - 帧速率问题,您无需在此处执行任何操作。由于它是一个流解决方案,必须提前告知另一方帧速率或每帧的时间戳。这两者都不是基本流的一部分。要么选择预先确定的帧速率,要么传递一些 sdp 或类似的东西,或者使用现有的协议(如 rtsp)。在第二种情况下,时间戳是以 rtp 等形式发送的流的一部分。然后客户端必须支付rtp流并播放它。这就是基本流的工作原理。 [如果您有固定速率编码器,请修复您的帧速率,或者提供时间戳]

本地 PC 播放速度会很快,因为它不知道 fps。通过在输入之前给出 fps 参数,例如

ffplay -fps 30 in.264

您可以在PC上控制播放。

至于无法播放的文件:是否有SPS和PPS。此外,您还应该启用 NAL 标头 - 附件 b 格式。我对 android 不太了解,但这是任何 h.264 基本流在不在任何容器中并且需要转储并稍后播放时都可以播放的要求。 如果android默认是mp4,但默认的annexb headers将被关闭,所以也许有一个开关来启用它。或者,如果您要逐帧获取数据,只需自己添加即可。

至于颜色格式:我猜默认应该可以。所以尽量不要设置它。 如果没有,请尝试 422 Planar 或 UVYV / VYUY 交错格式。通常相机就是其中之一。 (但不是必须的,这些可能是我遇到比较多的)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 Android MediaCodec 从摄像头编码 H.264 的相关文章

随机推荐