ffmpeg中获取字节流格式的两种方式

来源:互联网 发布:如何自学算命知乎 编辑:程序博客网 时间:2024/05/15 06:13

本文验证下面的帖子

http://www.mentby.com/Group/ffmpeg-user/h264-byte-patterns-format.html

内容为

The h.264 standard is complicated, so the simple algorithm you imply 
with this question will almost never work.


The first problem you will face is that there are two different binary 
formats defined by the standard: mpeg4 (also called atom), and Annex B. 
You somehow need to figure out (or be told) which type of stream you are 
receiving.


Then you need to know if your SPS and PPS are in-band or out-of-band. If 
they are out-of-band you need to obtain them.


Once you have these things, the rules for finding start-of-frame are 
*also* complicated (and are described in the standard.)


For Annex B format, ffmpeg already implements these rules in the 
h264_parser in libavcodec. Also, ffmpeg has a bitstream filter for 
converting from mpeg4 to Annex B format, which might be helpful to you 
(h264_mp4toannexb).


Writing an h264 parser yourself is going to take a lot of knowledge and 
effort. You should consider using what ffmpeg already provides.


以下两种情况涉及到H264流和FLV流,为了使数据具有可参照性,请用以下命令行

./ffmpeg -i /home/test.264 -vcodec copy /home/test.flv 

将原始H264 字节流封装为flv容器格式。视频信息会完全相同。

1,如果由H264字节流格式去解码,在每次读取一段数据之后(不管每次读多少都可以),都调用av_parser_parse2去分帧,这个函数会将每次读取的264数据进行存储分析,直到得到完整的H264数据为止,然后送出,此时我们可以用avcodec_decode_video2来解码。

那么送出的数据有什么规律呢? 对于IDR帧,除了IDR自身的数据外,会连同前面的SPS,PPS,SEI一并合在一起。在avcodec_decode_video2函数里面,包含了对SPS等的解析。

如果是P帧或者B帧,则不会送SPS,PPS等参赛信息。这个也很合理,因为本来就只有IDR帧才需要带参数信息。


2,另外一种情况呢?比如用flv,mp4打包后的video数据,该如何处理?这个时候我们用av_read_frame()函数来反复对flv文件进行读取,读出来的肯定是一个packet,此时我们也可以直接调用avcodec_decode_video2,进行解码。那么packet中的data究竟是什么,我们可以在avcodec_decode_video2函数中看到区别,在H264.C的decode_nal_units中,我们看到如下代码。

        if(buf_index >= next_avc) {
            if(buf_index >= buf_size) break;
            nalsize = 0;
            for(i = 0; i < h->nal_length_size; i++)
                nalsize = (nalsize << 8) | buf[buf_index++];
            if(nalsize <= 0 || nalsize > buf_size - buf_index){
                av_log(h->s.avctx, AV_LOG_ERROR, "AVC: nal size %d\n", nalsize);
                break;
            }
            next_avc= buf_index + nalsize;
        } else {
            // start code prefix search
            for(; buf_index + 3 < next_avc; buf_index++){
                // This should always succeed in the first iteration.
                if(buf[buf_index] == 0 && buf[buf_index+1] == 0 && buf[buf_index+2] == 1)
                    break;
            }


            if(buf_index+3 >= buf_size) break;


            buf_index+=3;
            if(buf_index >= next_avc) continue;
        }


这段代码的If部分就是对flv的data数据进行解析,else部分则是对H264字节的数据进行解析。所以avcodec_decode_video2能处理这两种情况。


那么,我们应该怎么样从flv的packet  data中,获取到字节流格式呢?最简单的方法是用ffmpeg自身的函数

AVBitStreamFilter ff_h264_mp4toannexb_bsf = {
    "h264_mp4toannexb",
    sizeof(H264BSFContext),
    h264_mp4toannexb_filter,
};

这个filter的作用,就是将flv的packet data转换为符合原始H264字节流格式的文件。最后得到的文件,其IDR帧前面,一定是SPS和PPS。

但是文件开头处也是参数集。

即若原始H264为SPS,PPS,SEI,IDR,................则还原后为SPS,PPS,SEI,SPS,PPS,IDR

原始H264为SPS,PPS,IDR,................则还原后为SPS,PPS,SPS,PPS,IDR

这样得到的H264流虽然参数集会出现重复现象, 但是却不违背H264协议,因为参数集本来就是描述视频信息用的。

但是有些情况下,IDR前面可能不需要或者不应该有SEI,比如硬件解码器的情况下。





原创粉丝点击