【问题标题】:Android: How to use MediaMuxer with video/mp4v-es instead of video/avc?Android:如何使用 MediaMuxer 与 video/mp4v-es 而不是 video/avc?
【发布时间】:2014-12-04 19:23:55
【问题描述】:

我希望能够在某些设备上使用 mp4v-es 而不是 avc。编码器使用 avc 运行良好,但是当我用 mp4v-es 替换它时,muxer 报告:

E/MPEG4Writer(12517): Missing codec specific data

MediaMuxer error "Failed to stop the muxer",视频无法播放。不同之处在于我向复用器添加了正确的轨道/格式,而没有收到任何错误:

...else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
           MediaFormat newFormat = encoder.getOutputFormat();
           mTrackIndex[encID] = mMuxer.addTrack(newFormat);

处理 mp4v-es 与处理 avc 有什么不同吗?一提,我只是跳过“bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG”,因为它不需要avc。谢谢。

【问题讨论】:

  • newFormat 有 csd-0 和 csd-1 吗?
  • @Marlon:新格式为:{height=720, mime=video/mp4v-es, csd-0=java.nio.ByteArrayBuffer[position=0,limit=30,capacity=30] , what=1869968451, width=1280} 和 csd-0: 000001B006000001B58913000001000000012000C48881F4528045A1463F。 csd-1 不存在,但我认为它只出现在 H264 中。
  • stackoverflow.com/questions/21341169/… 看起来可能需要 csd-1
  • encoderMPEG4Writer,我不认为csd 必须有2 个缓冲区。 MPEG4Writer 只能处理一个缓冲区。当没有CSD 时出现错误,即Missing codec specific data。对于video/mp4v-esMPEG4 视频基本流,MPEG4Writer 期望将数据打包为ESDS 格式,而AVCC 如下所示:androidxref.com/5.0.0_r2/xref/frameworks/av/media/…
  • csd 在创建新的Track 时被读取(参考:MPEG4Writer.cpp 以上,第 1370 行)。对于轨道,视频编码器是源,因此,您的编码器应支持getFormat,其中数据应以ESDS 格式打包。

标签: android video-encoding android-mediacodec mediamuxer


【解决方案1】:

正如 Ganesh 指出的那样,不幸的是,如果不修改平台源代码,目前看来这是不可能的。

实际上有两种方法可以将编解码器特定的数据传递给内部 MPEG4Writer 类,但如果不进行修改,它们都不能真正工作。

正如 Ganesh 发现的那样,将 MediaFormat 键重新映射到内部格式的逻辑似乎缺少对除 H264 之外的任何其他视频编解码器的编解码器特定数据的处理。修复此问题的经过测试的修改如下:

diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index 25afc5b..304fe59 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -549,14 +549,14 @@ void convertMessageToMetaData(const sp<AMessage> &msg, sp<MetaData> &meta) {
     // reassemble the csd data into its original form
     sp<ABuffer> csd0;
     if (msg->findBuffer("csd-0", &csd0)) {
-        if (mime.startsWith("video/")) { // do we need to be stricter than this?
+        if (mime == MEDIA_MIMETYPE_VIDEO_AVC) {
             sp<ABuffer> csd1;
             if (msg->findBuffer("csd-1", &csd1)) {
                 char avcc[1024]; // that oughta be enough, right?
                 size_t outsize = reassembleAVCC(csd0, csd1, avcc);
                 meta->setData(kKeyAVCC, kKeyAVCC, avcc, outsize);
             }
-        } else if (mime.startsWith("audio/")) {
+        } else if (mime == MEDIA_MIMETYPE_AUDIO_AAC || mime == MEDIA_MIMETYPE_VIDEO_MPEG4) {
             int csd0size = csd0->size();
             char esds[csd0size + 31];
             reassembleESDS(csd0, esds);

其次,原则上您可以将相同的缓冲区(设置了MediaCodec.BUFFER_FLAG_CODEC_CONFIG 标志)传递给MediaMuxer.writeSampleData,而不是将编解码器特定数据作为csd-0 传递给MediaFormat。此方法目前不起作用,因为此方法根本不检查编解码器配置标志 - 它可以通过此修改来修复:

diff --git a/media/libstagefright/MediaMuxer.cpp b/media/libstagefright/MediaMuxer.cpp
index c7c6f34..d612e01 100644
--- a/media/libstagefright/MediaMuxer.cpp
+++ b/media/libstagefright/MediaMuxer.cpp
@@ -193,6 +193,9 @@ status_t MediaMuxer::writeSampleData(const sp<ABuffer> &buffer, size_t trackInde
     if (flags & MediaCodec::BUFFER_FLAG_SYNCFRAME) {
         sampleMetaData->setInt32(kKeyIsSyncFrame, true);
     }
+    if (flags & MediaCodec::BUFFER_FLAG_CODECCONFIG) {
+        sampleMetaData->setInt32(kKeyIsCodecConfig, true);
+    }

     sp<MediaAdapter> currentTrack = mTrackList[trackIndex];
     // This pushBuffer will wait until the mediaBuffer is consumed.

据我所知,在不修改平台源的情况下,目前无法在使用公共 API 的情况下使用 MediaMuxer 对 MPEG4 视频进行多路复用。鉴于上述 Utils.cpp 中的问题,您不能对任何需要编解码器特定数据的视频格式进行多路复用,H264 除外。如果 VP8 是一个选项,您可以将其复用到 webm 文件中(连同 vorbis 音频),但 VP8 的硬件编码器可能比 MPEG4 的硬件编码器少得多。

【讨论】:

  • 此问题已在上游b.android.com/90138 报告,修复已提交至android-review.googlesource.com/120945
  • 感谢您报告此问题并将其修复到主流树中。
  • 好吧,修复尚未合并。实际上,我发现了 2 个等待审核的不同更改,它们的作用几乎相同,它们已由其他人提交(似乎与此处的这篇文章无关)但没有人合并甚至评论过。
  • 我的修复至少干净地合并到他们的内部树中(根据几天前的 buildbot),所以我认为他们还没有修复它。但通常他们可能需要几个月的时间才能对通过审核系统发送的补丁做出反应。
  • 我在 android-review.googlesource.com/120945 中的修复现已合并到 AOSP master 中,因此它有望成为下一个主要版本的一部分。
【解决方案2】:

我认为您有能力修改 Stagefright 源,因此,我为您的问题提出了解决方案,但需要自定义。

背景:

encoder 完成编码时,第一个缓冲区将包含csd 信息,该信息通常带有OMX_BUFFERFLAG_CODECCONFIG 标志。当这样一个缓冲区返回到MediaCodec时,它应该在MediaCodec::amendOutputFormatWithCodecSpecificData中存储与csd-0相同的内容。

现在,当将此缓冲区提供给MediaMuxer 时,同样会作为addTrack 的一部分进行处理,其中会调用convertMessageToMetadata。如果您参考相同的实现,我们可以观察到只有AVC 被处理为video 并且默认为audio 用于ESDS 创建。

编辑:

在这里,我的建议是修改此line 并尝试您的实验

} 
if (mime.startsWith("audio/") || (!strcmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4)) {

有了这个改变,我觉得它也应该适用于MPEG4 视频轨道。更改是将else if 转换为if,因为之前对video 的检查也会尝试处理数据,但仅限于AVC

【讨论】:

  • 不会在第 552 行编码:“if (mime.startsWith("video/")) {" 获取所有视频缓冲区?无论如何,我需要能够使用 MediaCodec 公共 API 导出 mp4v-es(以便我可以根据需要操作缓冲区内容),但不能使用 jni 和本机代码。
  • @user1592546..是的,你是对的。我们可以通过删除else if 中的else 来克服这个问题,这样就有2 个检查。为了优化这一点,对于第一个video 检查,我们可以添加另一个部分来验证MIME 类型是否为AVC。请参阅上面我编辑的答案。
  • 我会接受 mstorsjo 的回答,以确保完整性、进一步参考并提供完整解决方案的链接。无论如何,这个答案为这个问题提供了更多的洞察力,值得赞赏。
  • @user1592546.. 这很好。我很高兴您的问题得到了解决。
  • 好吧,它仍然不能按照我想要的方式工作 :) 即只有 java 端编码,但我必须接受。
猜你喜欢
  • 2015-11-21
  • 1970-01-01
  • 2014-05-21
  • 1970-01-01
  • 2017-06-24
  • 2016-03-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多