【问题标题】:Decode h264 with MediaCodec使用 MediaCodec 解码 h264
【发布时间】:2018-10-17 23:47:07
【问题描述】:

我正在解码从 android 上的 wifi cam 接收到的原始 h264。

使用套接字获取原始数据包并将接收到的数据解析为 NAL 单元。

我也有 SPS 和 PPS 单元(在MediaFormat 中设置为 csd-0 和 csd-1)。

从我在线阅读的所有帖子和信息中,如果我向解码器提供正确的数据,我仍然无法获得。

This is an example of the resulting video when decoding 除了底部看起来还不错。

我还注意到一些奇怪的事情,当我移动相机时,提要似乎运行几乎完全流畅(底部没有垃圾),一旦我放下它,垃圾视频返回(我原以为会是其他方式...)

我正在将 h264 数据解析为以 AUD 开头的块,每个块以 AUD 开头并在另一个块开始时结束。

例子:

[0, 0, 0, 1, 9, 48, 0, 0, 0, 1, 6, 1, 9, 0, 12, 8, 36, 104, 0, 0, 3, 0, 1, -128, 0, 0, 0, 1, 33, -32, 96, 97, 92, -97, 71, 89, -31, 127, -120, 11, 23, ..., 0, 0, 1, -64, ... , -59, 2, -32, 62, -111, -64, 0, 0, 1, -32, 0, 0, -124, -128, 5, 33, 0, 1, -60, -31]

[0, 0, 0, 1, 9, 48, 0, 0, 0, 1, 6, 1, 9, 0, 14, 8, 36, 104, 0, 0, 3, 0, 1, -128, 0, 0, 0, 1, 33, -32, 112, 113, 92, -97, 72, 24, 96, 80, 2, 88, 70, ..., 98, -75, 27, 0, 0, 1, -64, 1, 82, ... , 119, 2, -32, 62, -111, -64, 0, 0, 1, -32, 0, 0, -124, -128, 5, 33, 0, 1, -31, 1]

每隔几帧我就会得到一个带有 SPS 和 PPS 的块

0, 0, 0, 1, 9, 16, 0, 0, 0, 1, 39, SPS, 0, 0, 0, 1, 40, PPS, 0, 0, 0, 1, 6, 0, 13, -128, -89, 89, -128, 8, 117, 0, -89, 89, -128, 8, 117, 64, 1, 9, 0, 16, 8, 36, 104, 0, 0, 3, 0, 1, 6, 1, -60, -128, 0, 0, 0, 1, 37, -72, 1, 0, 1, -1, -16, 13, ... , 108, 60, -83, 101, 0, 0, 1, -32, 0, 0, -124, -128, 5, 33, 0, 3, 25, 65

据我了解,每个解析的“块”(以 AUD 开头)都是一个访问单元,这就是我放入缓冲区并提供给解码器的内容。

我是否向解码器提供了正确的输入?

我怎样才能摆脱底部的垃圾视频?

什么可能导致垃圾部分?

我还尝试修剪每个块的部分,例如修剪开头 - [0, 0, 0, 1, 9, 48, 0, 0, 0, 1, 6, 1, 9, 0, 12, 8, 36, 104, 0, 0, 3, 0, 1, -128] 并且仅从 0 ,0 ,0 ,1, 33 部分传递,但这并没有太大的区别。

【问题讨论】:

  • 您正在以某种方式破坏数据。我想不出其他解释。根据损坏的位置,它很可能被截断。
  • 你能解释一下截断是什么意思吗?
  • 似乎每 5 秒(I 帧间隔?)您的数据会被截断,直到您移动相机,在这种情况下,图像之间的差异比相机不移动时更重要,所以 NALU包含更多数据

标签: android decode h.264 android-mediacodec


【解决方案1】:

“垃圾部分”看起来像编码器填充部分。在内部,所有 h264 视频的编码都是 16 的倍数,但会单独标记应该显示的确切尺寸。

您不是在描述如何在屏幕上显示解码后的数据,如果您使用 SurfaceTexture 作为输出,或者手动解释 YUV 缓冲区。

如果手动解释缓冲区,则需要应用裁剪字段,如下所述:https://developer.android.com/reference/android/media/MediaCodec#data-types

关键部分是这样的:

视频帧的大小(旋转前)可以这样计算:

MediaFormat format = decoder.getOutputFormat(…);
int width = format.getInteger(MediaFormat.KEY_WIDTH);
if (format.containsKey("crop-left") && format.containsKey("crop-right")) {
    width = format.getInteger("crop-right") + 1 - format.getInteger("crop-left");
}
int height = format.getInteger(MediaFormat.KEY_HEIGHT);
if (format.containsKey("crop-top") && format.containsKey("crop-bottom")) {
    height = format.getInteger("crop-bottom") + 1 - format.getInteger("crop-top");
}

【讨论】:

  • 我正在使用 SurfaceTexture 进行输出,有什么可以这样做的吗?
  • 不能是填充。否则失真将是恒定的。
  • @szatmary,有什么想法会导致这种情况吗?
  • 我的猜测是数据包截断。可能解析传输不正确。
  • 对对对,重看视频后,明显不是padding。截断或解析不正确也是我的猜测。
【解决方案2】:

如果您通过 RTP/UDP 连接接收流,我的经验表明,某些数据包会在 Wifi 上丢失,并可能导致一些失真。如果您尝试使用 RTP over TCP(交错格式),那么您应该有一个非常可靠的流,并且不会丢失数据包。这可以显示问题是否与您的解析或丢包问题有关。

【讨论】:

  • 它是 RTP/UDP,问题确实出在我解析接收到的数据的方式上。一些数据包丢失导致损坏的帧。我用一个大缓冲区进行了测试,缓冲了一大块接收到的数据,然后才开始处理(提取帧然后解码),视频看起来不错。谢谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-02-06
  • 2013-04-25
  • 2021-08-30
  • 1970-01-01
  • 2023-03-22
  • 2019-06-21
  • 2014-01-04
相关资源
最近更新 更多