音视频入门底子:MPEG2-PS专题(4)——FFmpeg源码中,判断某文件是否为PS文件的实现 [复制链接]
发表于 2025-11-16 02:47:23 | 显示全部楼层 |阅读模式
=================================================================
音视频入门底子: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下令:
  1. ./ffmpeg -i XXX.ps
复制代码
可以判断出某个文件是否为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中:
  1. static int mpegps_probe(const AVProbeData *p)
  2. {
  3.     uint32_t code = -1;
  4.     int i;
  5.     int sys = 0, pspack = 0, priv1 = 0, vid = 0;
  6.     int audio = 0, invalid = 0, score = 0;
  7.     int endpes = 0;
  8.     for (i = 0; i < p->buf_size; i++) {
  9.         code = (code << 8) + p->buf[i];
  10.         if ((code & 0xffffff00) == 0x100) {
  11.             int len  = p->buf[i + 1] << 8 | p->buf[i + 2];
  12.             int pes  = endpes <= i && check_pes(p->buf + i, p->buf + p->buf_size);
  13.             int pack = check_pack_header(p->buf + i);
  14.             if (code == SYSTEM_HEADER_START_CODE)
  15.                 sys++;
  16.             else if (code == PACK_START_CODE && pack)
  17.                 pspack++;
  18.             else if ((code & 0xf0) == VIDEO_ID && pes) {
  19.                 endpes = i + len;
  20.                 vid++;
  21.             }
  22.             // skip pes payload to avoid start code emulation for private
  23.             // and audio streams
  24.             else if ((code & 0xe0) == AUDIO_ID &&  pes) {audio++; i+=len;}
  25.             else if (code == PRIVATE_STREAM_1  &&  pes) {priv1++; i+=len;}
  26.             else if (code == 0x1fd             &&  pes) vid++; //VC1
  27.             else if ((code & 0xf0) == VIDEO_ID && !pes) invalid++;
  28.             else if ((code & 0xe0) == AUDIO_ID && !pes) invalid++;
  29.             else if (code == PRIVATE_STREAM_1  && !pes) invalid++;
  30.         }
  31.     }
  32.     if (vid + audio > invalid + 1) /* invalid VDR files nd short PES streams */
  33.         score = AVPROBE_SCORE_EXTENSION / 2;
  34. //     av_log(NULL, AV_LOG_ERROR, "vid:%d aud:%d sys:%d pspack:%d invalid:%d size:%d \n",
  35. //            vid, audio, sys, pspack, invalid, p->buf_size);
  36.     if (sys > invalid && sys * 9 <= pspack * 10)
  37.         return (audio > 12 || vid > 3 || pspack > 2) ? AVPROBE_SCORE_EXTENSION + 2
  38.                                                      : AVPROBE_SCORE_EXTENSION / 2 + (audio + vid + pspack > 1); // 1 more than mp3
  39.     if (pspack > invalid && (priv1 + vid + audio) * 10 >= pspack * 9)
  40.         return pspack > 2 ? AVPROBE_SCORE_EXTENSION + 2
  41.                           : AVPROBE_SCORE_EXTENSION / 2; // 1 more than .mpg
  42.     if ((!!vid ^ !!audio) && (audio > 4 || vid > 1) && !sys &&
  43.         !pspack && p->buf_size > 2048 && vid + audio > invalid) /* PES stream */
  44.         return (audio > 12 || vid > 6 + 2 * invalid) ? AVPROBE_SCORE_EXTENSION + 2
  45.                                                      : AVPROBE_SCORE_EXTENSION / 2;
  46.     // 02-Penguin.flac has sys:0 priv1:0 pspack:0 vid:0 audio:1
  47.     // mp3_misidentified_2.mp3 has sys:0 priv1:0 pspack:0 vid:0 audio:6
  48.     // Have\ Yourself\ a\ Merry\ Little\ Christmas.mp3 0 0 0 5 0 1 len:21618
  49.     return score;
  50. }
复制代码
该函数的作用就是检测某个文件是否为PS文件。

形参p:输入型参数,为AVProbeData范例的指针。
AVProbeData布局体声明在libavformat/avformat.h中:
  1. /**
  2. * This structure contains the data a format has to probe a file.
  3. */
  4. typedef struct AVProbeData {
  5.     const char *filename;
  6.     unsigned char *buf; /**< Buffer must have AVPROBE_PADDING_SIZE of extra allocated bytes filled with zero. */
  7.     int buf_size;       /**< Size of buf except extra allocated bytes */
  8.     const char *mime_type; /**< mime_type, when known. */
  9. } 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为这段码流中视频流的个数:
  1.     int sys = 0, pspack = 0, priv1 = 0, vid = 0;
复制代码


不绝读取出存放在数组p->buf中的二进制码流数据:
  1.     for (i = 0; i < p->buf_size; i++) {
  2.         code = (code << 8) + p->buf[i];
  3.         if ((code & 0xffffff00) == 0x100) {
  4.     //...
  5.         }
  6.     }
复制代码


宏SYSTEM_HEADER_START_CODE界说在libavformat/mpeg.h中,值为0x000001bb,表现system header的起始码(system header的system_header_start_code属性):
  1. #define SYSTEM_HEADER_START_CODE    ((unsigned int)0x000001bb)
复制代码


根据是否是system header的起始码,判断是否读取到了PS流的system header,如果读取到了,让变量sys的值加1:
  1.             if (code == SYSTEM_HEADER_START_CODE)
  2.                 sys++;
复制代码


宏PACK_START_CODE界说在libavformat/mpeg.h中,值为0x000001ba,表现pack header的起始码(pack header的pack_start_code属性):
  1. #define PACK_START_CODE             ((unsigned int)0x000001ba)
复制代码


根据是否是pack header的起始码,判断是否读取到了PS流的pack header,如果读取到了,让变量pspack的值加1:
  1.           int pack = check_pack_header(p->buf + i);  
  2.         //...
  3.           else if (code == PACK_START_CODE && pack)
  4.                 pspack++;
复制代码


查抄是否读取到了PES流。如果读取到了,通过PES packet中的stream_id属性判断内里的ES流是否为视频流。如果是视频流,让变量vid的值加1:
  1.             int pes  = endpes <= i && check_pes(p->buf + i, p->buf + p->buf_size);
  2.         //...
  3.             else if ((code & 0xf0) == VIDEO_ID && pes) {
  4.                 endpes = i + len;
  5.                 vid++;
  6.             }
复制代码


返回终极表现符合水平的分数:
  1.     if (sys > invalid && sys * 9 <= pspack * 10)
  2.         return (audio > 12 || vid > 3 || pspack > 2) ? AVPROBE_SCORE_EXTENSION + 2
  3.                                                      : AVPROBE_SCORE_EXTENSION / 2 + (audio + vid + pspack > 1); // 1 more than mp3
  4.     if (pspack > invalid && (priv1 + vid + audio) * 10 >= pspack * 9)
  5.         return pspack > 2 ? AVPROBE_SCORE_EXTENSION + 2
  6.                           : AVPROBE_SCORE_EXTENSION / 2; // 1 more than .mpg
  7.     if ((!!vid ^ !!audio) && (audio > 4 || vid > 1) && !sys &&
  8.         !pspack && p->buf_size > 2048 && vid + audio > invalid) /* PES stream */
  9.         return (audio > 12 || vid > 6 + 2 * invalid) ? AVPROBE_SCORE_EXTENSION + 2
  10.                                                      : AVPROBE_SCORE_EXTENSION / 2;
  11.     // 02-Penguin.flac has sys:0 priv1:0 pspack:0 vid:0 audio:1
  12.     // mp3_misidentified_2.mp3 has sys:0 priv1:0 pspack:0 vid:0 audio:6
  13.     // Have\ Yourself\ a\ Merry\ Little\ Christmas.mp3 0 0 0 5 0 1 len:21618
  14.     return score;
复制代码



四、总结

从上面我们可以知道,FFmpeg检测某个文件是否为PS文件,是通过判断这段码流中,读取到的system header、pack header、PES流中视频流的个数等信息实现的。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表