=================================================================
音视频入门底子:MPEG2-PS专题系列文章:
音视频入门底子:MPEG2-PS专题(1)——MPEG2-PS官方文档下载
音视频入门底子:MPEG2-PS专题(2)——使用FFmpeg下令天生ps文件
音视频入门底子:MPEG2-PS专题(3)——MPEG2-PS格式简介
音视频入门底子:MPEG2-PS专题(4)——FFmpeg源码中,判断某文件是否为PS文件的实现
音视频入门底子:MPEG2-PS专题(5)——FFmpeg源码中,分析PS流中的PES流的实现
音视频入门底子:MPEG2-PS专题(6)——FFmpeg源码中,获取PS流的视频信息的实现
音视频入门底子:MPEG2-PS专题(7)——通过FFprobe表现PS流每个packet的信息
音视频入门底子:MPEG2-PS专题(8)——使用Wireshark分析GB28181的PS流
=================================================================
一、弁言
通过FFmpeg下令:
可以判断出某个文件是否为PS文件:
以是FFmpeg是怎样判断出某个文件是否为PS文件呢?它内部着实是通过mpegps_probe函数来判断的。从《FFmpeg源码:av_probe_input_format3函数和AVInputFormat布局体分析(FFmpeg源码5.0.3版本)》和《7.0.1版本的FFmpeg源码中av_probe_input_format3函数和AVInputFormat布局体的改变》
中可以知道:FFmpeg源码中实现容器格式检测的函数是av_probe_input_format3函数,其内部通过循环while ((fmt1 = av_demuxer_iterate(&i))) 拿到全部容器格式对应的AVInputFormat布局,然后通过score = fmt1->read_probe(&lpd)语句实验差别容器格式对应的分析函数,根据是否能被分析,以及匹配水平,来判断出这是哪种容器格式。而PS文件对应的分析函数就是mpegps_probe函数。
二、mpegps_probe函数的界说
mpegps_probe函数界说在FFmpeg源码(本文演示用的FFmpeg源码版本为7.0.1)的源文件libavformat/mpeg.c中:
- static int mpegps_probe(const AVProbeData *p)
- {
- uint32_t code = -1;
- int i;
- int sys = 0, pspack = 0, priv1 = 0, vid = 0;
- int audio = 0, invalid = 0, score = 0;
- int endpes = 0;
- for (i = 0; i < p->buf_size; i++) {
- code = (code << 8) + p->buf[i];
- if ((code & 0xffffff00) == 0x100) {
- int len = p->buf[i + 1] << 8 | p->buf[i + 2];
- int pes = endpes <= i && check_pes(p->buf + i, p->buf + p->buf_size);
- int pack = check_pack_header(p->buf + i);
- if (code == SYSTEM_HEADER_START_CODE)
- sys++;
- else if (code == PACK_START_CODE && pack)
- pspack++;
- else if ((code & 0xf0) == VIDEO_ID && pes) {
- endpes = i + len;
- vid++;
- }
- // skip pes payload to avoid start code emulation for private
- // and audio streams
- else if ((code & 0xe0) == AUDIO_ID && pes) {audio++; i+=len;}
- else if (code == PRIVATE_STREAM_1 && pes) {priv1++; i+=len;}
- else if (code == 0x1fd && pes) vid++; //VC1
- else if ((code & 0xf0) == VIDEO_ID && !pes) invalid++;
- else if ((code & 0xe0) == AUDIO_ID && !pes) invalid++;
- else if (code == PRIVATE_STREAM_1 && !pes) invalid++;
- }
- }
- if (vid + audio > invalid + 1) /* invalid VDR files nd short PES streams */
- score = AVPROBE_SCORE_EXTENSION / 2;
- // av_log(NULL, AV_LOG_ERROR, "vid:%d aud:%d sys:%d pspack:%d invalid:%d size:%d \n",
- // vid, audio, sys, pspack, invalid, p->buf_size);
- if (sys > invalid && sys * 9 <= pspack * 10)
- return (audio > 12 || vid > 3 || pspack > 2) ? AVPROBE_SCORE_EXTENSION + 2
- : AVPROBE_SCORE_EXTENSION / 2 + (audio + vid + pspack > 1); // 1 more than mp3
- if (pspack > invalid && (priv1 + vid + audio) * 10 >= pspack * 9)
- return pspack > 2 ? AVPROBE_SCORE_EXTENSION + 2
- : AVPROBE_SCORE_EXTENSION / 2; // 1 more than .mpg
- if ((!!vid ^ !!audio) && (audio > 4 || vid > 1) && !sys &&
- !pspack && p->buf_size > 2048 && vid + audio > invalid) /* PES stream */
- return (audio > 12 || vid > 6 + 2 * invalid) ? AVPROBE_SCORE_EXTENSION + 2
- : AVPROBE_SCORE_EXTENSION / 2;
- // 02-Penguin.flac has sys:0 priv1:0 pspack:0 vid:0 audio:1
- // mp3_misidentified_2.mp3 has sys:0 priv1:0 pspack:0 vid:0 audio:6
- // Have\ Yourself\ a\ Merry\ Little\ Christmas.mp3 0 0 0 5 0 1 len:21618
- return score;
- }
复制代码 该函数的作用就是检测某个文件是否为PS文件。
形参p:输入型参数,为AVProbeData范例的指针。
AVProbeData布局体声明在libavformat/avformat.h中:
- /**
- * This structure contains the data a format has to probe a file.
- */
- typedef struct AVProbeData {
- const char *filename;
- unsigned char *buf; /**< Buffer must have AVPROBE_PADDING_SIZE of extra allocated bytes filled with zero. */
- int buf_size; /**< Size of buf except extra allocated bytes */
- const char *mime_type; /**< mime_type, when known. */
- } AVProbeData;
复制代码
p->filename为:须要被推测格式的文件的路径。
p->buf:指向“存放从路径为p->filename的PS文件中读取出来的二进制数据”的缓冲区。
p->buf_size:缓冲区p->buf的巨细,单位为字节。注:FFmpeg判断某个文件的格式时不会读取完备个文件,只会读取它前面的一部门,比如最开始的2048个字节。只要根据前面的这些字节就充足判断出它的格式了,以是p->buf_size的值一样平常就是2048。
p->mime_type:一样平常为NULL,可忽略。
返回值:返回一个范例为整形的分值。返回0表现该文件完全不符合PS格式。返回的值越靠近100表现该文件越符合PS格式。
三、mpegps_probe函数的内部实现分析
mpegps_probe函数中起首会界说整形变量。此中,变量sys表现这段长度为p->buf_size的码流中system header的个数;pspack为这段码流中pack header的个数;priv1为这段码流中私有流的个数;vid为这段码流中视频流的个数:
- int sys = 0, pspack = 0, priv1 = 0, vid = 0;
复制代码
不绝读取出存放在数组p->buf中的二进制码流数据:
- for (i = 0; i < p->buf_size; i++) {
- code = (code << 8) + p->buf[i];
- if ((code & 0xffffff00) == 0x100) {
- //...
- }
- }
复制代码
宏SYSTEM_HEADER_START_CODE界说在libavformat/mpeg.h中,值为0x000001bb,表现system header的起始码(system header的system_header_start_code属性):
- #define SYSTEM_HEADER_START_CODE ((unsigned int)0x000001bb)
复制代码
根据是否是system header的起始码,判断是否读取到了PS流的system header,如果读取到了,让变量sys的值加1:
- if (code == SYSTEM_HEADER_START_CODE)
- sys++;
复制代码
宏PACK_START_CODE界说在libavformat/mpeg.h中,值为0x000001ba,表现pack header的起始码(pack header的pack_start_code属性):
- #define PACK_START_CODE ((unsigned int)0x000001ba)
复制代码
根据是否是pack header的起始码,判断是否读取到了PS流的pack header,如果读取到了,让变量pspack的值加1:
- int pack = check_pack_header(p->buf + i);
- //...
- else if (code == PACK_START_CODE && pack)
- pspack++;
复制代码
查抄是否读取到了PES流。如果读取到了,通过PES packet中的stream_id属性判断内里的ES流是否为视频流。如果是视频流,让变量vid的值加1:
- int pes = endpes <= i && check_pes(p->buf + i, p->buf + p->buf_size);
- //...
- else if ((code & 0xf0) == VIDEO_ID && pes) {
- endpes = i + len;
- vid++;
- }
复制代码
返回终极表现符合水平的分数:
- if (sys > invalid && sys * 9 <= pspack * 10)
- return (audio > 12 || vid > 3 || pspack > 2) ? AVPROBE_SCORE_EXTENSION + 2
- : AVPROBE_SCORE_EXTENSION / 2 + (audio + vid + pspack > 1); // 1 more than mp3
- if (pspack > invalid && (priv1 + vid + audio) * 10 >= pspack * 9)
- return pspack > 2 ? AVPROBE_SCORE_EXTENSION + 2
- : AVPROBE_SCORE_EXTENSION / 2; // 1 more than .mpg
- if ((!!vid ^ !!audio) && (audio > 4 || vid > 1) && !sys &&
- !pspack && p->buf_size > 2048 && vid + audio > invalid) /* PES stream */
- return (audio > 12 || vid > 6 + 2 * invalid) ? AVPROBE_SCORE_EXTENSION + 2
- : AVPROBE_SCORE_EXTENSION / 2;
- // 02-Penguin.flac has sys:0 priv1:0 pspack:0 vid:0 audio:1
- // mp3_misidentified_2.mp3 has sys:0 priv1:0 pspack:0 vid:0 audio:6
- // Have\ Yourself\ a\ Merry\ Little\ Christmas.mp3 0 0 0 5 0 1 len:21618
- return score;
复制代码
四、总结
从上面我们可以知道,FFmpeg检测某个文件是否为PS文件,是通过判断这段码流中,读取到的system header、pack header、PES流中视频流的个数等信息实现的。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |