使用FFmpeg,如何解码H264数据包

5
我是一名新手使用FFmpeg,正在努力解码H264数据包,这些数据包可以作为uint8_t数组获取。
经过多次尝试,我认为只需要将此数组放入类似下面的AVPacket中即可。请帮我确认是否正确。
AVPacket *avpkt = (AVPacket *)malloc(sizeof(AVPacket) * 1);
av_init_packet(avpkt);  
avpkt->data = ct;   // ct is the array
avpkt->length =....

并通过avcodec_decode_video2()进行解码。

代码的一部分如下:

...
codec = avcodec_find_decoder(CODEC_ID_H264);
gVideoCodecCtx = avcodec_alloc_context();
gFrame = avcodec_alloc_frame();
avcodec_decode_video2(gVideoCodecCtx, gFrame, &frameFinished, packet);
...

我想我已经正确设置了所有必要的属性,但这个函数只返回 -1

我刚刚发现这个 -1 是来自于

ret = avctx->codec->decode(avctx, picture, got_picture_ptr, avpkt);

avcodec_decode_video2()

实际上,我想知道如何通过avcodec_decode_video2()解码没有RTP头的H264数据包?


更新:

好的,我仍在尝试找到解决方法。我现在正在做以下事情

**这个RTP流中的H264流是由FU-A编码的

  1. 接收一个RTP数据包

  2. 查看RTP头的第二个字节是否>0,这意味着这是第一个数据包(可能会跟随其他数据包)

  3. 查看下一个RTP数据包的第二个字节是否>0,那么它就表示前一个帧是完整的NAL,或者如果这个字节<0,则应将该数据包附加到前一个数据包。

  4. 删除所有分组分割指示符(FU)和分组分割头部,使其只有像NAL这样的内容

  5. 尝试使用avcodec_decode_video2()播放它

但它只返回-1.....
我是否也应该删除FU指示符和头?

非常感谢任何建议

3个回答

5
实际上,我想知道是否可以通过avcodec_decode_video2()解码H264数据包(不带RTP头)。如果您使用单个NAL单元模式以外的分组模式,则可能需要预处理RTP有效负载(重新组装分段NAL单元,拆分聚合NAL单元),然后将NAL单元传递给解码器。流中允许的NAL单元类型(STAP、MTAP、FU)取决于分组模式。有关分组模式的更多信息,请阅读 RFC 6184。其次,虽然我对FFMPEG不是那么熟悉,但这可能更多地涉及到H.264解码问题:在解码其他帧之前,您必须始终使用H.264序列(SPS)和图像参数集(PPS)初始化解码器。您做到了吗?

谢谢Ralf,原来Spydroid以单个NAL模式和FU-1模式都发送RTP数据包,所以我想我需要将数据包处理为带有FU-1头的RTP。但是我不知道如何使用FFmpeg库解码RTP。我认为没有适用于RTP级别的函数,这就是为什么我从H264开始这项工作的原因 :( .. 请问我能得到任何建议吗? - Jun
如果FFmpeg在RTP层不起作用,您将不得不在将其传递给FFmpeg之前自己编写该代码(这并不难),或者使用另一个第三方库。Live555(www.live555.com)是一个开源LGPL库,可处理RTP,包括H.264负载特定的分组等其他内容。 - Ralf
@Ralf,我相信单个NAL单元包模式也是RFC6184的一部分,可以参考RFC的第5.6节。我不确定你为什么说它不需要RTP头。如果我错了,请指正我。 - Krishna Oza
@Krishna_Oza 谢谢,确实,我没有表达得很好。更新了答案。 - Ralf

1

我认为如果没有RTP头,你将无法解码H264数据包,因为相当多的视频流信息嵌入在RTP头中。同时,我猜测所有视频流信息都可以复制到RTP视频数据包中。因此,这也取决于流是如何生成的。

Vibgyor


谢谢Vibgyor,实际上发送者是Spydroid开源项目,它的RTP头部似乎没有包含太多信息...它由有效载荷类型、序列号、时间戳和同步源标识符组成。它们中有任何一个代表视频流信息吗?我不这么认为...我仍在努力研究并将更新我的进展。 - Jun

0

这是我的工作代码

bool VideoDecoder::decode(const QByteArray &encoded)
{
    AVPacket packet;
   av_new_packet(&packet, encoded.size());
   memcpy(packet.data, encoded.data(), encoded.size());
   //TODO: use AVPacket directly instead of Packet?
   //TODO: some decoders might in addition need other fields like flags&AV_PKT_FLAG_KEY

   int ret = avcodec_decode_video2(d->codec_ctx, d->frame, &d->got_frame_ptr, &packet);
   av_free_packet(&packet);

   if ((ret < 0) || (!d->got_frame_ptr))
       return false;

    d->sws_ctx = sws_getCachedContext(d->sws_ctx
        , d->codec_ctx->width, d->codec_ctx->height, d->codec_ctx->pix_fmt
        , d->width, d->height, d->pix_fmt
        , (d->width == d->codec_ctx->width && d->height == d->codec_ctx->height) ? SWS_POINT : SWS_BICUBIC
        , NULL, NULL, NULL
        );

    int v_scale_result = sws_scale(
        d->sws_ctx,
        d->frame->data,
        d->frame->linesize,
        0,
        d->codec_ctx->height,
        d->picture.data,
        d->picture.linesize
        );
    Q_UNUSED(v_scale_result);

    if (d->frame->interlaced_frame)
        avpicture_deinterlace(&d->picture, &d->picture, d->pix_fmt, d->width, d->height);
    return true;
}

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