使用MediaCodec和MediaMuxer录制视频,但是比特率和帧率不正确。

6

我写了一个演示程序,使用MediaCodec和MediaMuxer来录制视频。

我用我的演示程序录制了一个视频,并使用ffprobe来检查视频,结果如下:

  Duration: 00:00:06.86, start: 0.000000, bitrate: 723 kb/s
Stream #0:0(eng): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 320x240, 619 kb/s, SAR 1:1 DAR 4:3, 30.02 fps, 30 tbr, 90k tbn, 180k tbc (default)
Metadata:
  creation_time   : 2015-06-05 13:19:24
  handler_name    : VideoHandle
Stream #0:1(eng): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, mono, fltp, 96 kb/s (default)
Metadata:
  creation_time   : 2015-06-05 13:19:24
  handler_name    : SoundHandle

这段代码包含视频和音频信息,我发现音频属性与源代码中设置的一致,但视频属性不正确。我的视频设置源代码如下:

        MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, mWidth, mHeight);
    format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
            MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
    format.setInteger(MediaFormat.KEY_BIT_RATE, 384000);
    format.setInteger(MediaFormat.KEY_FRAME_RATE, 19);
    format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5);
    if (VERBOSE) Log.d(TAG, "format: " + format);
    mVideoEncoder = MediaCodec.createEncoderByType(MIME_TYPE);
    mVideoEncoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
    mInputSurface = mVideoEncoder.createInputSurface();
    mVideoEncoder.start();

视频的宽度和高度是正确的,但比特率和帧率却比我在源代码中设置的要高。这导致视频文件大小比我预期的要大得多。 接着,我修改了源代码以删除音频录制线程,只录制视频。但这并没有产生任何影响,比特率和帧率仍然很高。 谁能告诉我原因并给我一些建议呢?
还有另一个问题。我偶尔会录制出一个损坏的视频,可以通过系统播放器播放,但视频开头是黑色的,正常画面要在1或2秒后才出现。我不知道如何在stackoverflow中上传文件,我可以把损坏的视频文件发送给任何需要它的人。 有人遇到过这个问题吗?
补充: 我发现另一个奇怪的事情: 我的视频编码配置:
private int mWidth = 480;
private int mHeight = 848;
private int mVideoBitrate = 1200 * 1000;

    MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, 480, 848);

    format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
            MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
    format.setInteger(MediaFormat.KEY_BIT_RATE, 1200000);
    format.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
    format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);

但实际的视频信息是:

  Duration: 00:00:06.01, start: 0.000000, bitrate: 6491 kb/s
Stream #0:0(eng): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, mono, fltp, 15 kb/s (default)
Metadata:
  creation_time   : 2015-09-30 15:44:18
  handler_name    : SoundHandle
Stream #0:1(eng): Video: h264 (Baseline) (avc1 / 0x31637661), yuv420p, 480x848, 6484 kb/s, SAR 1:1 DAR 30:53, 16 fps, 30 tbr, 90k tbn, 180k tbc (default)
Metadata:
  creation_time   : 2015-09-30 15:44:18
  handler_name    : VideoHandle

嗨@dragonfly,你成功完成了音频和视频录制吗? - Ali
是的,我终于做到了。 - dragonfly
我真诚地请求您,能否请与我分享演示。@dragonfly - Ali
@Ali 你可以从这个项目中获得你想要的东西:https://github.com/google/grafika - dragonfly
兄弟,我使用了这个 Grafika(ContinuesCaptureActivity)并创建了演示,但问题是这个演示没有音频,所以我该如何添加音频呢?请查看这个问题:https://dev59.com/L7Dla4cB1Zd3GeqP2hXo。 - Ali
1个回答

7
看起来你期望的帧率(19fps)与实际帧率(30fps)不匹配。编码器试图满足以19fps提交的帧的比特率要求,但它们到达得更快,因此错过了,实际比特率更高。我假设30fps值是从视频文件中实际呈现时间戳确定的,这些时间戳是通过传递给MediaMuxer的呈现时间戳设置的。如果您想要19fps视频,则需要生成间隔为(1000/19)毫秒的时间戳。
如果您的输入视频是30fps,则需要在编码过程中删除帧以达到19fps。MediaCodec不会为您删除帧-它会对您传递的所有内容进行编码。
我不知道为什么你会在视频开头得到一个破碎的部分。

1
如果MediaCodec对每个输入的帧进行编码,那么目标视频的帧速率将由诸如相机之类的输入源的速率确定。换句话说,MediaCodec无法确定帧速率,但为什么MediaCodec允许应用程序通过MediaFormat.KEY_FRAME_RATE设置帧速率呢?这是一个错误还是我做出了错误的假设? - dragonfly
2
您需要提供帧率的估计值,以便编解码器可以配置适当的压缩级别,以实现目标比特率。KEY_FRAME_RATE是您的应用程序告诉编解码器即将到来的内容,而不是告诉编解码器要生成什么的一种方式。它也被纳入关键帧频率中--如果您请求30fps并且每秒钟有一个关键帧,那么您将获得每30帧一个关键帧。请注意,H.264视频流没有时间戳--这些时间戳在.mp4文件中,由MediaMuxer添加--因此,某些编解码器可能只是无需检查地传递PTS值。 - fadden
1
谢谢,有了你的帮助,我成功地录制了一个19 fps的视频。起初,在drainEncoder的过程中,我跳过了一些帧,结果视频的图像非常模糊,就像马赛克一样。然后我改变了方法,在SurfaceTexture.OnFrameAvailableListener中跳过了一些帧,录制的视频变得正常了。 - dragonfly
嗨,fadden。因为您的好心,我最终完成了我的工作。但是还有一件事情让我担心。我在http://stackoverflow.com/questions/30709672/worrying-about-the-compatibility-of-android-mediacodec-and-mediamuxer-since-api上创建了一个问题。您能给我一些建议吗? - dragonfly
1
@Sunshinetpu:编码器将对您提供的每个帧进行编码。如果您想将60fps视频转换为30fps视频,则需要不将一半的帧传递给编码器。使用设置为“false”的渲染标志调用解码器的releaseOutputBuffer()应该可以解决问题——该帧不会在编码器的输入表面上呈现。如果您无法控制解码器,则情况会更加困难。您可以使用SurfaceTexture接收帧,然后使用GLES将每个其他帧呈现到编码器的Surface上。(bigflake和Grafika有一些有用的代码,但您需要将这些代码组合在一起。) - fadden
显示剩余6条评论

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