假如看到一个需求播映的视频链接显示是一张图片,你会不会感觉有点懵?假如这张图片写着 png,然后实践格局是 bmp ,你会不会更懵了?假如这个 bmp 还做了加密篡改呢?今日咱们要聊的便是这样一个充溢骚操作的音视频故事。

本篇首要是想经过这个「故事」,更直观地给大家遍及 M3U8 里的一些根底知识

前言

假如你常常触摸音视频,那么关于 M3U8 应该不会陌生, M3U8 简略来说便是 HLS(HTTP Live Streaming) ,指的是苹果开发的依据 HTTP 协议的流媒体解决方案,它能够在普通的 HTTP 的应用上直接提供点播和直播的才干。

在 HLS 里会将视流文件切分红小片(ts)并建立索引文件(M3U8),一般如下图所示,首要会有一个 M3U8 文件,然后对应在 #EXTINF 的 tag 下会有许多 TS 格局切片,这应该是咱们认知中的 M3U8 文件的标准。

音视频骚操作,FFmpeg 如何播放带「图片」的 M3U8 视频,IJKPlyaer 适配非标 TS 文件

详细解说 M3U8 的这儿就不详细翻开,感兴趣的能够看之前共享过的探究移动端音视频与GSYVideoPlayer之旅 。

那么,假如在 M3U8 里的不是 TS 链接,而是 png 链接或许 bmp 链接会是什么状况?今日的主题便是探讨怎么适配带有图片的非标准 M3U8 视频。

咱们不鼓舞「非标」,而是经过「非标」的适配来做科普

为什么 M3U8 里会有图片?

首要,标准的 HLS 协议里 M3U8 文件内肯定是 TS 的切片链接,那么为什么会有 png/bmp 之类的图片链接存在?或许说,为什么会是图片链接

这就不得不说「劳动人民的智慧」,众所周知,假如想让一个视频加载更快,那么最简略的办法便是给视频上 CDN ,可是碍于某些团队或许个人「囊中羞涩」,所以开端有人瞄上了公共图床的 CDN ,然后再结合 M3U8 的特性,一套民间的「免费」 视频 CDN 潜规则就这样悄然流行起来。

假如你想将一个完好视频假装成图片上传公共图床明显不现实,由于体积太大,许多图床也会对图片的巨细做约束,可是假如是 M3U8 ,那么就能够把视频分解成无数 TS ,再把 TS 假装成图片分批上传,这样就能够给视频「依靠」上 CDN 的才干

TS & 图片

如下图所示,这便是一个「非标」的 M3U8 视频链接,能够看到 #EXTINF tag 下会的链接都是 png 格局的后缀,那么这种 png 后缀,会不会影响视频播映呢

音视频骚操作,FFmpeg 如何播放带「图片」的 M3U8 视频,IJKPlyaer 适配非标 TS 文件

答案是不会,由于 FFmpeg 里播映并不是认定后缀,而是经过读取每个 #EXTINF tag 链接的二进制 Header,终究匹配它们的封装和编解码格局。

所以其实在 M3U8 里 #EXTINF tag 下的链接后缀并不重要,能够是 png 或许 bmp ,乃至你写 txt 也是能够的,要点其实是包本身的编码。

那么假如这个视频链接,真的是一个图片呢?如下图所示,能够看到这个 png 本身便是一个完好的图片,不过这个图片的巨细和它本身的质量并不匹配,究竟这样一个图片不可能高达 1.9 MB 。

音视频骚操作,FFmpeg 如何播放带「图片」的 M3U8 视频,IJKPlyaer 适配非标 TS 文件

如下图所示,咱们检查这张图片的二进制,能够看到文件的 Header 的确是 PNG ,可是后边还有相似 FFmpeg Service 这样的描绘,能够的确这便是一个假装成实在 PNG 格局的视频文件。

从二进制字节看能够发现这便是一个 TS 封装的视频文件,由于在它的二进制代码里,有0x47 最初,长度为 188 字节,并且经过 0xFF 进行填充的规律 packet 存在

后边咱们会详细解释。

音视频骚操作,FFmpeg 如何播放带「图片」的 M3U8 视频,IJKPlyaer 适配非标 TS 文件

那么这个 PNG 能够正常播映吗?答案是能够的。那为什么分明是 PNG 的 Header ,却能够被解析成视频?

首要 FFmpeg 在播映前,会依据前面提到的 0x47 / 188 这个特征去辨认这是一个 TS 封装的视频,之后在 mpegts.c 的对应封装处理逻辑里,会针对辨认 0x47 作为包的开端方位去解析,所以 PNG 包部分会被疏忽。

0x47 是一个 TS 包的固定 header ,一般一个 TS 包是 188 字节,不行长度一般会用 0xFF 填充,而 FFmpeg 会针对每个格局去做辨认,核算它们的 score ,依据每种格局的 score 决议它可能是什么格局,比方 mpegts.c 里是 mpegts_probe 函数,它经过 analyze 函数就会找到 0x47 开端做一系列的判别。

音视频骚操作,FFmpeg 如何播放带「图片」的 M3U8 视频,IJKPlyaer 适配非标 TS 文件

别的还一个叫 mpegts_read_header的函数会读取数据头信息,比方解分出 TS 流当中的数据包巨细,节目信息,PMT表,Video PID,Audio PID 等等,这些也是 TS 流播映的重要依据。

而在 mpegts.c 里最重要的 read_packet 函数也是,读取的时分会读取 TS_PACKET_SIZE(188)的巨细,然后判别包的首字节是不是 0x47 ,假如不是就经过 mpegts_resync 从头同步一下去测验寻找 0x47

音视频骚操作,FFmpeg 如何播放带「图片」的 M3U8 视频,IJKPlyaer 适配非标 TS 文件

可能这时分仔细的你现已发现了「盲点」,前面 PNG 的 Header 二进制里不便是 89 50 4E 47 吗?这儿不也是有 0x47 ?,这种状况下 mpegts.c 在解包的时分不就会「紊乱」了吗

如下图所示,由于假如从图片的 0x47 开端算, 以 188 的包长度核算,下一个包不就找不到 0x47 了吗?

音视频骚操作,FFmpeg 如何播放带「图片」的 M3U8 视频,IJKPlyaer 适配非标 TS 文件

答案是会,可是有办法确保它不会。这就不得不提 mpegts_resync 函数,在前面截图的代码里有 if ((*data)[0] != 0x47) 时会调用 mpegts_resync ,如下代码所示,它的关键作用是:

  • 首要经过 avio_seek 往回移动 -FFMIN(seekback, pos) 的巨细,对应到上面的图片,便是把指针移回读取上图黄色标示的数据开端的 FF 方位,也便是还没读取这一块数据的时分。
  • for 循环里经过 avio_r8 让指针往前逐渐读字节,当遇到 0x47 就停下来,让指针回到 0x47 ,然后调用 reanalyze 从头分析数据。
  • 能够看到,在经过 mpegts_resync 函数同步之后,指针回从头被同步到上图黄色标准里的 0x47 方位,再从头履行 ffio_read_indirect 翻开一组 188 的数据,然后让 TS 包解析回归正常
/* XXX: try to find a better synchro over several packets (use
* get_packet_size() ?) */
staticintmpegts_resync(AVFormatContext*s,intseekback,constuint8_t*current_packet)
{
 MpegTSContext*ts=s->priv_data;
 AVIOContext*pb=s->pb;
 intc,i;
 uint64_tpos=avio_tell(pb);
​
 avio_seek(pb,-FFMIN(seekback,pos),SEEK_CUR);
​
 //Special case for files like 01c56b0dc1.ts
 if(current_packet[0]==0x80&&current_packet[12]==0x47) {
   avio_seek(pb,12,SEEK_CUR);
   return0;
 }
​
 for(i=0;i<ts->resync_size;i++) {
   c=avio_r8(pb);
   if(avio_feof(pb))
     returnAVERROR_EOF;
   if(c==0x47) {
     avio_seek(pb,-1,SEEK_CUR);
     reanalyze(s->priv_data);
     return0;
   }
 }
 av_log(s,AV_LOG_ERROR,
    "max resync size reached, could not find sync byte\n");
 /* no sync found */
 returnAVERROR_INVALIDDATA;
}

所以上述这个 PNG 图片尽管会有一点「冗余」的过错数据,可是终究仍是能够被 mpegts.c 正常解析,然后播映。

所以 M3U8 里有图片链接,是由于「劳动人民」需求「免费 CDN」,而链接后缀和前置格局不大会影响视 TS 封装的播映,现有的 IJKPlayer 封装的 FFmpeg 就支撑播映假装成图片的 TS 视频链接。

正文

对,这儿开端才是正文,前面的 png 操作还算是比较「惯例」,可是接下来的一些特别案例,便是假如你不适配,大概就播映不了的场景。

由于把 TS 假装成图片是一种「非标准」的做法,天然就存在林林总总的「骚操作」,例如下面这个 M3U8,就包含有 bmp、png、ts 三种格局的链接。

音视频骚操作,FFmpeg 如何播放带「图片」的 M3U8 视频,IJKPlyaer 适配非标 TS 文件

最风趣的事,尽管链接上写的时分 png ,可是实践这个链接的 header 描绘里也是一个 bmp ,然后这个 bmp 的数据仍是还被 AES-128 加密。

咱们下载这个 M3U8 里其间一个 bmp,如下图所示,经过巨细能够很明显看到它也是一个假装成 bmp 的视频链接,可是它有点特别,由于:它经过了 M3U8 的 AES-128 加密,一起它的二进制组成也有些特别

音视频骚操作,FFmpeg 如何播放带「图片」的 M3U8 视频,IJKPlyaer 适配非标 TS 文件

如下图所示,检查这个加密的 bmp 文件的二进制,能够看到从 Header 看它的确是 bmp 格局,一起由于 TS 视频的数据被 AES-128 加密了,所以此刻咱们看不到原始的 TS 封装信息,可是由于它地点的 M3U8 里有可用的加密 key,所以咱们能够直接经过一些东西来下载和解密。

音视频骚操作,FFmpeg 如何播放带「图片」的 M3U8 视频,IJKPlyaer 适配非标 TS 文件

比方咱们能够经过开源的 M3U8-Downloader 来下载得到一个解密后 bmp,如下图所示是上面的 bmp 文件经过下载解密之后的二进制格局,能够看到此刻现已能够看到一些咱们了解的信息,比方 H264 的描绘,比方 0x47和很多 0xFF 填充。

音视频骚操作,FFmpeg 如何播放带「图片」的 M3U8 视频,IJKPlyaer 适配非标 TS 文件

别的能够看到,此刻的 BMP 由于 「AES-128」的解密作用下,此刻的 bmp 现已不是一个正常的图片格局,无法以图片的方式翻开检查。

由于 Header 没了。

音视频骚操作,FFmpeg 如何播放带「图片」的 M3U8 视频,IJKPlyaer 适配非标 TS 文件

一起,此刻的假装 TS 封装在解密后仍然不是 0x47 最初,所以如下图所示,视频在播映时,会找到咱们蓝色选中第二行里的 0x47 的方位,然后开端往后读取一个 188 长度的 TS 包进行解析播映。

音视频骚操作,FFmpeg 如何播放带「图片」的 M3U8 视频,IJKPlyaer 适配非标 TS 文件

可是问题来了,此刻播映出来的视频,会呈现没有画面的状况。为什么会有这种状况?这就要提到前面提到的 mpegts_resync

由于从第一个 0x47 开端读取,那么第二个包就会是上图画出来的赤色部分,由于不是 0x47 最初,所以会经过 mpegts_resync 函数找到绿色的 0x47 ,然后持续往后读取。

这样乍一看没有什么问题,可是其实疏忽了黄色部分的 0x47 ,假如仔细去数,你就会发现黄色部分的 0x47 到绿色的 0x47 ,刚好便是 188 的长度,所以其实这部分应该是一个完好的 TS 包,并且是很重要的一个包,也是由于它没被正确读取,所以导致了播映呈现没有画面的状况。

那么这个包是什么,为什么它会这么重要?

TS & PAT & PMT

咱们前面会呈现画面无法被解析,其实便是由于咱们说被「丢掉」的包导致的,它刚好便是 TS 里的 PAT 包 :

  • PAT (Program Association Table)首要的作用便是指明晰 PMT 表的 PID 值
  • PMT(Program Map Table)首要的作用便是指明晰音视频流的 PID 值
  • PID 确定 TS 包中的数据归于什么类型

所以由于 PAT 没有被正确的解析,所以没有得到正确 PMT,然后没有找到正确的视频编码包的 PID,所以呈现了没有画面的状况

这也是为什么 PAT 包那么重要,简略来说,正常状况下解析一个 TS 封装的流程为:

TS 流里每个 packet 一般都是 188 个字节,解析 TS 需求先解析每个 packet ,然后需求从一个 packet 中解分出 PAT 的 PID,PAT 的 PID 一般为 0,然后从 PAT 包中解分出 PMT 的 PID,再依据 PMT 的 PID 找到 PMT 包,在从 PMT 包中解分出 Video 和 Audio 的PID,然后依据PID找出相应的音视频包。

如下图所示,一般 TS 包的 header 首要由 4 个字节组成,其间 sync_byte 是一个字节(8b),固定为 0x47 ,而 PID 是一个 13b 的二进制,一般 PID 为 0 的 packet 就会被认定为是 PAT。

音视频骚操作,FFmpeg 如何播放带「图片」的 M3U8 视频,IJKPlyaer 适配非标 TS 文件

比方前面被咱们疏忽的 47 40 00 10 它对应二进制是 0100 0111 0100 0000 0000 0000 0001 0000 ,按照上面拆分:

sync_byte(1B) 0x47 / 0100 0111
transport_error_indicator (1b) 传输过错指示符,一般都为 0,这儿也是 0
payload_unit_start_indicator(1b) 负载单元开端标示符,一个完好的数据包开端时标记为1,这儿刚好是 1
transport_priority(1b) 传输优先级,0为低优先级,1为高优先级,一般取 0,这儿刚好是 0
PID(13b) 这儿刚好便是 0 0000 0000 0000,也便是 0,PID 为 0 就阐明这个 TS 包是 M3U8 里的 PAT 包
Transport_scrambling_control(2b) 传输加扰操控,00表明未加密,这儿是 00
Adaptation_field_control(2b) 00保留;01 为无自适应域,这儿为 01
Continuity_counter(4b) 表明该计数器为 0,PID 相同的包的计数因该是接连,递加计数器,从0-f,开端值不一定取 0,但 PID相同的包计数器有必要是接连,这儿是 0000

所以能够看到,被咱们疏忽的 47 40 00 10 最初的包,刚好便是最重要的 PAT 包,这也是为什么这个视频播映是会没有画面的原因,由于终究对应视频留的 PID 没有被解分出来。

然后咱们再去看这个 PAT 表里的数据,如下图所示是 PAT 的内容部分的结构示意图,咱们首要需求的是 Program Number(PMT) 的 PID ,在 N loop 部分前有 64b ,也便是 8 个字节,后边 N loop 部分才是开端循环的实践节目表,其间一个节目是 32b ,也便是 4 个字节,最终 CRC 完毕标志为 32b ,也便是 4 个字节。

音视频骚操作,FFmpeg 如何播放带「图片」的 M3U8 视频,IJKPlyaer 适配非标 TS 文件

所以回到二进制里,黄色部分便是需求固定字节,然后赤色下划线的 00 01 EF FF 便是节目表,该 PAT 里只有一个节目单,其间 00 01 是 number ,也便是节目 number 为 01 , PID 是 FFF, 也便是该节目的 PID 是 4095

黄色前还有一个 00 归于 adapter 区的,由于前面 Adaptation_field_control 是 01。

音视频骚操作,FFmpeg 如何播放带「图片」的 M3U8 视频,IJKPlyaer 适配非标 TS 文件

然后咱们在看下一个 TS 包,如下图赤色部分,它的 Header 是 47 4F FF 01 ,对应的二进制便是 0100 0111 0100 1111 1111 1111 000 0001 ,那么它的 PID 便是 0 1111 1111 1111 这 13 位,也便是 FFF (4095)。

音视频骚操作,FFmpeg 如何播放带「图片」的 M3U8 视频,IJKPlyaer 适配非标 TS 文件

所以,到这儿一切都清晰了,由于疏忽的是 PAT 包,所以会导致后边这个 PMT ID 4095 不被解析为特别的 TS 包,然后获取不到对应的节目数据

那 PMT 怎么读取出流信息?如下图所示是一个 PMT 的 TS 包结构,咱们直接看 N loop 部分,一个 loop 大概要 40b ,也便是 5 个字节,其间咱们首要是 stream type 和 elementary PID。

音视频骚操作,FFmpeg 如何播放带「图片」的 M3U8 视频,IJKPlyaer 适配非标 TS 文件

其间 stream type 对应的字节代表了流的详细类型,比方 0x0f 便是 aac 音频, 0x1b 便是 h264 的视频,所以 TS 里能够经过 PMT 得到需求当然封装详细的音视频解码格局。

音视频骚操作,FFmpeg 如何播放带「图片」的 M3U8 视频,IJKPlyaer 适配非标 TS 文件

那么回到二进制里,如下图所示,结合 PMT 的结构,能够看到有两个 stream ,其间 stream_type h.264 编码对应 0x1b,aac 编码对应 0x0f ,而 E100 : 111[0 0001 0000 0000] ,后 13 位也便是 256,所以视频的 PID 是 256 ,也便是 h264 的视频 pid 是 256 ,而 acc 的音频的 PID 是 257。

音视频骚操作,FFmpeg 如何播放带「图片」的 M3U8 视频,IJKPlyaer 适配非标 TS 文件

咱们再看下一个包,能够看到这个包里有 264 的描绘,它的 header 是 47 41 00 31 ,也便是 0100 0111 0100 0001 0000 0000 0011 0001 ,对应的 PID 便是 0 0001 0000 0000 ,也便是 256,这就和前面的 PMT 持续对应上了。

音视频骚操作,FFmpeg 如何播放带「图片」的 M3U8 视频,IJKPlyaer 适配非标 TS 文件

更直观一点,咱们简略写一个 python 脚本,输出下一切的 PID ,能够看到除了 0 和 4095 ,剩下的就都是 256 和 257 这样的流数据包,所以到这儿就能够完全对应上: PID 为 0 的包是 PAT ,经过 PAT 得到 PMT 的 PID ,找到 PID 就能够得到 stream type 和 stream pid ,然后就能够找到对应的 stream pid 的 TS 包去读取音视频流数据

# 导入需求的模块
importsys
​
# 定义常量
TS_PACKET_SIZE=188# 翻开 TS 文件
withopen(sys.argv[1],'rb')asts_file:
 pids=[]
 # 循环读取 TS 数据包
 whileTrue:
   ts_packet=ts_file.read(TS_PACKET_SIZE)
   ifnotts_packet:
     break
​
   # 提取 PID 并输出
   pid=(ts_packet[1]&0x1F)<<8|ts_packet[2]
   pids.append(pid)
 print(pids)

音视频骚操作,FFmpeg 如何播放带「图片」的 M3U8 视频,IJKPlyaer 适配非标 TS 文件

所以前面的的「奇奇怪怪」的编码,刚好会让 FFmpeg 疏忽掉 PAT 数据,然后导致加载到节目表而导致没有画面。

开端适配

依据这个逻辑,我觉得应该是首要解决 PAT 包被疏忽的问题,所以如下代码所示,在 mpegts.cread_packet 里我增加了 if((*data)[0] == 0x47 && (*data)[188] != 0x47) 的判别,假如包是以 0x47 最初,可是下一个包不是 0x47 ,那么就在包内从头去寻找一个能「首尾相接」的 0x47 TS 包,然后从头 ffio_read_indirect

staticintread_packet(AVFormatContext*s,uint8_t*buf,intraw_packet_size,
          constuint8_t**data)
{
 AVIOContext*pb=s->pb;
 intlen;
 len=ffio_read_indirect(pb,buf,TS_PACKET_SIZE,data);
 if(len!=TS_PACKET_SIZE)
   returnlen<0?len:AVERROR_EOF
​
​
 for(;;) {
   len=ffio_read_indirect(pb,buf,TS_PACKET_SIZE,data);
   if(len!=TS_PACKET_SIZE)
     returnlen<0?len:AVERROR_EOF;
   /* check packet sync byte */
   if((*data)[0]!=0x47) {
     /* find a new packet start */
​
     if(mpegts_resync(s,raw_packet_size,*data)<0)
       returnAVERROR(EAGAIN);
     else
       continue;
   }else{
     ///// ///// ///// 增加的部分 ///// ///// /////
     if((*data)[0]==0x47&&(*data)[188]!=0x47) {
       for(inti=0;i<TS_PACKET_SIZE;i++) {
         if((*data)[i]==0x47&&(*data)[i+188]==0x47) {
           avio_seek(pb,i,SEEK_CUR);
           avio_seek(pb,-TS_PACKET_SIZE,SEEK_CUR);
           reanalyze(s->priv_data);
           len=ffio_read_indirect(pb,buf,TS_PACKET_SIZE,data);
           if(len!=TS_PACKET_SIZE)
              returnlen<0?len:AVERROR_EOF;
           return0;
         }
       }
     }else{
       break;
     }
   }
 }
 return0;
}

从头打包之后,它的确能够解分出画面了,而画面却呈现花屏,花屏肯定是播映过程中呈现了丢包,导致 IBP 帧解析呈现反常,所以咱们上面的写法存在问题。

音视频骚操作,FFmpeg 如何播放带「图片」的 M3U8 视频,IJKPlyaer 适配非标 TS 文件

而恰巧这时分咱们发现,前面 bmp 填充部分的长度,刚好也是 188 字节,这种巧合让我不由怀疑,是不是其实咱们不需求疏忽这个前置字节?

音视频骚操作,FFmpeg 如何播放带「图片」的 M3U8 视频,IJKPlyaer 适配非标 TS 文件

所以咱们直接无视 0x47 的最初,直接读取解析(由于后续一些解析逻辑也会判别 0x47 ,所以这儿咱们强行无视),然后从头打包之后,咱们惊喜的发现能够播映了,也不会花屏了,可是又有新的问题呈现:一个 TS 播映完了它不会切换到下一个 TS 。

音视频骚操作,FFmpeg 如何播放带「图片」的 M3U8 视频,IJKPlyaer 适配非标 TS 文件

然后咱们再去看这个 TS 文件的结尾,本来文件结尾填充了很多的 0x00 字节,然后导致读取时无法正常触发完毕标识。

音视频骚操作,FFmpeg 如何播放带「图片」的 M3U8 视频,IJKPlyaer 适配非标 TS 文件

所以咱们再次简略修正下,当遇到 0x00 最初的包时,咱们用 mpegts_resync 函数处理一下,假如找不到正常的包,咱们就能够直接返回 AVERROR(EAGAIN) 完毕这个 TS 的播映。

音视频骚操作,FFmpeg 如何播放带「图片」的 M3U8 视频,IJKPlyaer 适配非标 TS 文件

到这儿咱们能够看到这个 M3U8 能够正常播映了,对应的 stream 也能够被解析呈现,尽管这儿的修正很简略粗犷,可是这样的修正,就能够在兼容正规协议的状况下,也能够适配到这种「民间非标」支撑,要点是经过这个比如,能够形象的遍及 TS 封装里的根底概念。

音视频骚操作,FFmpeg 如何播放带「图片」的 M3U8 视频,IJKPlyaer 适配非标 TS 文件

以上修正需求调整 FFmpeg 的源码,然后从头构建动态库。

别的这个格局的文件的 ExoPlayer 下也是无法被播映,首要是由于前面说的读错了 0x47 的包头方位,由于后边 0xFF 太多,会导致超过两个 TS_PACKET_SIZE 的判别,然后抛出反常,假如要想 ExoPlayer 也支撑播映,能够从这点切入去修正源码。

音视频骚操作,FFmpeg 如何播放带「图片」的 M3U8 视频,IJKPlyaer 适配非标 TS 文件

别的你会发现浏览器是能够播映这类链接,由于如 hls.js 在这方面的检测没有那么谨慎。

最终

上述播映调整是依据 IJKPlayer 上的 FFmpeg 版本进行,尽管如今 IJKPlayer 现已没有维护,可是依据 IJKPlayer 做一些调整优化仍是很方便。

当然,由于 IJKPlayer 整体构建环境比较老,所以假如你从头构建编译,能够参阅 GSYVideoPlayer 下的 编译 IJKPlayer so 相关支撑 ,现在文档现已支撑到 Mac M1/M2 下的环境 。

还有调试这类 TS 文件,个人建议使用本地播映 M3U8 来进行测试,这样咱们能够更方便在播映时动态修正本地的 TS 二进制字节,例如能够修正 M3U8 为下面的文件格局。

音视频骚操作,FFmpeg 如何播放带「图片」的 M3U8 视频,IJKPlyaer 适配非标 TS 文件

当然,假如播映本地 M3U8 遇到了下方相似的过错提示,能够参阅下方代码增加 "allowed_extensions", "ALL" 到 IJKPlayer 里来临时答应播映。

音视频骚操作,FFmpeg 如何播放带「图片」的 M3U8 视频,IJKPlyaer 适配非标 TS 文件

(IjkMediaPlayer.OPT_CATEGORY_FORMAT,"protocol_whitelist","crypto,file,http,https,tcp,tls,udp,rtmp,rtsp");
(IjkMediaPlayer.OPT_CATEGORY_FORMAT,"allowed_extensions","ALL");
(IjkMediaPlayer.OPT_CATEGORY_PLAYER,"enable-accurate-seek",1);

好了,本篇到这儿就完毕了,经过解说适配对 TS 封装一系列的骚操作,信任大家对 TS 的一些根底概念都有了一定的认识,最终总结一下;

  • 关于 TS 解码,视频的后缀格局和封装 header 并不会实践影响播映作用
  • TS 封装是以 0x47 最初,188 字节长度,会用0xFF 做冗余填充的包格局
  • TS 封装里 PID 是唯一标识,而 PID 为 0 的包是 PAT 包
  • PAT 包很重要,由于经过 PAT 包才干找到 PMT 包,找到 PMT 包才干正确获取音视频的 PID
  • FFmpeg 的 mpegts.c 里, mpegts_resync 函数能够帮助你从头同步到 TS packet 的包头