首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >从PyAV帧解析数据包时的h264不一致性

从PyAV帧解析数据包时的h264不一致性
EN

Stack Overflow用户
提问于 2022-04-03 09:29:16
回答 1查看 556关注 0票数 2

当生成H.264帧并使用pyAV对其进行解码时,只有在两次调用parse方法时才会从帧中解析数据包。

考虑使用以下测试H.264输入,使用:创建

ffmpeg -f lavfi -i testsrc=duration=10:size=1280x720:rate=30 -f image2 -vcodec libx264 -bsf h264_mp4toannexb -force_key_frames source -x264-params keyint=1:scenecut=0 "frame-%4d.h264"

现在,使用pyAV解析第一个框架:

代码语言:javascript
复制
import av
codec = av.CodecContext.create('h264', 'r')
with open('/path/to/frame-0001.h264', 'rb') as file_handler:
    chunk = file_handler.read()
    packets = codec.parse(chunk) # This line needs to be invoked twice to parse packets

除非再次调用最后一行(packets = codec.parse(chunk)),否则数据包将保持空。

另外,对于不同的实际例子,我无法描述,从数据包中解码帧似乎也需要几个解码调用:

代码语言:javascript
复制
packet = packets[0]
frames = codec.decode(packet) # This line needs to be invoked 2-3 times to actually receive frames.

有人知道pyAV这种不一致的行为吗?

(在macOS蒙特雷12.3.1、ffmpeg 4.4.1、pyAV 9.0.2上使用Python3.8.12)

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-04-03 13:27:54

这是一种预期的PyAV行为。不仅是,它是底层libav的预期行为。一个数据包不能保证一个帧,在生成一个帧之前可能需要多个分组。这一点在FFmpeg视频解码器实例中很明显。

代码语言:javascript
复制
    while (ret >= 0) {
        ret = avcodec_receive_frame(dec_ctx, frame);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
            return;

如果它需要更多的数据包来形成一个帧,它会抛出EAGAIN错误。

编辑

实际上,上面的示例并不是一个很好的示例,因为它只存在于EAGAIN上。要检索框架,应该使用continue on EAGAIN

代码语言:javascript
复制
    while (ret >= 0) {
        ret = avcodec_receive_frame(dec_ctx, frame);
        if (AVERROR(EAGAIN))
            continue;
        if (ret == AVERROR_EOF)
            return;

编辑

codec.parse() ‘s

有时需要额外调用的解码是一个众所周知的事实,但是需要刷新的解析器就不那么常见了。以下是PyAV和FFmpeg之间的区别:

PyAV使用av_parser_parse2()解析输入数据,如下面的[参考文献]

代码语言:javascript
复制
        while True:

            with nogil:
                consumed = lib.av_parser_parse2(
                    self.parser,
                    self.ptr,
                    &out_data, &out_size,
                    in_data, in_size,
                    lib.AV_NOPTS_VALUE, lib.AV_NOPTS_VALUE,
                    0
                )
            err_check(consumed)

            # ...snip...

            if not in_size:
                # This was a flush. Only one packet should ever be returned.
                break

            in_data += consumed
            in_size -= consumed

            if not in_size:
                # Aaaand now we're done.
                break

因此,它读取直到输入数据100%被消耗,并注意它没有在缓冲区的末尾调用av_parser_parse2 (这是有意义的,因为输入数据可能只是流数据的一部分。

相反,FFmpeg不直接调用av_parser_parse2并使用parse_packet,您可以看到它是如何处理类似情况的:

代码语言:javascript
复制
while (size > 0 || (flush && got_output)) {
   int64_t next_pts = pkt->pts;
   int64_t next_dts = pkt->dts;
   int len;

   len = av_parser_parse2(sti->parser, sti->avctx,
                          &out_pkt->data, &out_pkt->size, data, size,
                          pkt->pts, pkt->dts, pkt->pos);

它还调用av_parser_parse2在输入数据流耗尽后刷新流。因此,您需要在PyAV中执行相同的操作:在读取所有帧之后,最后一次调用codec.parse()来刷新最后一个数据包。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71724612

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档