1. 从零搞懂TS视频格式:它到底是什么,为什么还没被淘汰
第一次接触TS格式的人,多半是在某个下载文件夹里看到一个后缀为.ts的文件,双击打不开,拖进播放器倒是能放,但拖进度条的时候要么卡顿要么直接跳回开头。更让人困惑的是,同样一段视频,别人给你的是MP4,你自己录出来的却是TS,明明看起来内容一样,用起来差别却很大。这个现象背后其实藏着一整套关于视频封装、流式传输和容错设计的工程逻辑,值得花点时间彻底捋清楚。
TS的全称是Transport Stream,中文一般叫“传输流”,它是MPEG-2标准体系里定义的一种容器格式。注意这里的关键词是“容器”,不是“编码”。很多人会把容器和编码混为一谈,觉得TS就是一种视频压缩方式,其实不是。容器好比一个快递箱,编码才是箱子里的货物。TS这个箱子最擅长的场景是“边打包边发货”,也就是把视频、音频、字幕等数据切成一个个固定长度的小包,按顺序连续不断地送出去,接收端收到多少就能播多少,不需要等整个文件下载完。这个特性决定了它在直播、数字电视广播、监控录像、屏幕录制等场景里长期占据主导地位。
那为什么现在大家更熟悉MP4而不是TS?因为MP4的设计目标是“文件存储与点播”,它的索引信息(moov box)通常放在文件末尾或者开头,播放器需要先读到索引才能知道每一帧在哪里,所以更适合下载完成后本地播放。TS则相反,它把索引信息分散在每一个数据包里,牺牲了一部分存储效率,换来了极强的抗丢包和随机接入能力。你可以把TS理解成“一列编好号的火车车厢”,每节车厢都自带编号和说明,丢了一节也不影响后面的车厢继续被识别;而MP4更像“一本装订好的书”,目录丢了整本书就不好翻。
理解了这一点,很多实际问题就迎刃而解了。比如为什么直播流普遍用TS而不是MP4?因为直播过程中网络抖动是常态,TS的包结构允许播放器在丢包后快速恢复,而MP4一旦索引损坏可能整个文件都废了。再比如为什么监控设备默认输出TS?因为监控需要7×24小时不间断写入,TS的追加写入特性天然适合这种场景,断电时最多损失最后一个包,不会像MP4那样因为索引没写完导致整个文件无法播放。
这篇文章面向的读者范围很广:如果你只是想知道“TS文件怎么转成MP4”,后面有专门的操作章节;如果你是开发者,需要在自己的项目里做TS的封装、解析或转封装,那关于包结构、PAT/PMT表、PCR时钟的部分会更有价值;如果你是从业者,正在做直播、点播、监控回传相关的系统设计,那关于选型对比和常见坑的部分应该能帮你少走弯路。我会尽量用生活化的类比把底层原理讲清楚,同时给出可以直接抄作业的命令和配置,让不同基础的读者都能拿到自己需要的东西。
2. TS格式的核心设计逻辑:为什么它要设计成这样
2.1 固定长度包与188字节的由来
TS最显著的特征就是它的数据被切成了固定188字节的包。这个数字不是随便定的,而是工程妥协的结果。188字节 = 4字节包头 + 184字节负载。包头里包含了同步字节(0x47)、传输错误指示、负载单元起始指示、传输优先级、PID(包标识符)、加扰控制、自适应字段控制、连续计数器等信息。184字节的负载刚好能容纳一个MPEG-2视频或音频的PES包片段,同时留出足够的空间给前向纠错码(在DVB等广播标准中,188字节外面还会再加16字节的Reed-Solomon校验,变成204字节)。
为什么是固定长度而不是可变长度?因为固定长度意味着接收端可以用硬件电路做极高速的解析,不需要复杂的缓冲区管理。你可以想象成流水线上的标准托盘,每个托盘大小一样,机械臂抓取的时候不需要判断大小,效率极高。这个设计在数字电视广播时代是刚需,因为那时候的机顶盒芯片算力有限,必须用最简单的逻辑处理每秒几兆甚至几十兆的数据流。
注意:188字节是TS的标准包长,但在某些专业广播设备里你会看到192字节或204字节的变体,那是因为在188字节基础上加了额外的前向纠错或时间戳字段。普通用户遇到的TS文件基本都是188字节标准包。
2.2 PID机制:TS流里的“快递单号”
TS流里同时跑着视频、音频、字幕、节目信息等多种数据,接收端怎么知道哪个包属于哪个轨道?靠的就是PID。每个TS包头部有一个13位的PID字段,取值范围0到8191。不同的PID代表不同的数据流,比如PID 0x0000固定用于PAT(节目关联表),PID 0x0001用于CAT(条件接收表),PID 0x1FFF用于空包(填充用)。视频流、音频流各自分配一个或多个PID。
这个机制的好处是解复用极其简单:接收端只需要根据PID过滤自己关心的包,其他包直接丢弃。就像快递分拣中心,每个包裹上都有目的地编码,分拣员只看编码就能决定往哪个方向扔,不需要拆开包裹看内容。这也是为什么TS流可以被多路复用——多个节目的数据可以混在同一个物理通道里传输,接收端通过PID筛选出自己要看的那一路。
2.3 PAT和PMT:TS流的“目录系统”
光有PID还不够,接收端还需要知道“哪个PID是视频,哪个PID是音频,这个节目叫什么名字”。这些信息由PAT和PMT两张表来提供。PAT(Program Association Table)告诉接收端当前流里有哪些节目,每个节目对应的PMT的PID是多少。PMT(Program Map Table)则详细描述了一个节目里包含哪些流,每个流的PID、类型(视频/音频/字幕)、编码格式等信息。
这个结构很像一本书的目录:PAT是总目录,告诉你这本书有几个章节;PMT是每个章节的详细目录,告诉你这一章里有哪些小节。播放器拿到TS流后,第一件事就是找PID 0x0000的包解析PAT,然后根据PAT找到PMT,再根据PMT找到视频和音频的PID,最后才开始解码播放。如果PAT或PMT丢失或损坏,播放器就会“找不到流”,表现为黑屏或只有声音没有画面。
2.4 PCR时钟:音视频同步的基准
TS流里还有一个关键概念叫PCR(Program Clock Reference),它是整个节目时间轴的基准时钟。视频和音频的PTS(显示时间戳)和DTS(解码时间戳)都是相对于PCR来计算的。接收端通过PCR恢复出27MHz的系统时钟,然后用这个时钟去驱动音视频解码和渲染,保证唇音同步。
PCR通常由视频流所在的PID携带,每隔不超过100毫秒就要插入一个PCR值。如果PCR间隔太大或者抖动太厉害,接收端的时钟恢复就会出问题,表现为音视频不同步、画面卡顿或者播放速度异常。这也是为什么TS流在传输过程中对网络抖动比较敏感——不是数据丢了,而是时钟参考被破坏了。
2.5 为什么TS适合流式传输而MP4不适合
把上面几点串起来看,TS的设计哲学就很清晰了:它假设传输信道是不可靠的,随时可能丢包、乱序、中断,所以它把所有的元信息都打散到每个包里,让接收端在任何位置接入都能快速恢复。MP4则假设文件是完整存储在本地磁盘上的,所以它把元信息集中放在文件头或文件尾,换取更高的存储效率和更灵活的索引能力。
这个差异在实际应用中表现为:TS流可以从任意位置开始播放(只要等到下一个关键帧),MP4必须从头或从索引指定的位置开始;TS流丢包后最多影响当前一小段,MP4索引损坏可能导致整个文件报废;TS流的文件体积通常比同样内容的MP4大5%到15%,因为包头和填充包有额外开销。
3. TS格式的典型应用场景与选型对比
3.1 直播与数字电视广播
直播是TS最经典的应用场景。无论是传统的DVB、ATSC、ISDB数字电视广播,还是基于IP网络的直播流,TS都是默认的封装格式。原因很简单:广播信道是单向的、不可靠的,接收端(机顶盒、电视)算力有限,必须用最简单的逻辑实现快速频道切换和抗干扰。TS的固定包长、PID过滤、PAT/PMT表结构刚好满足这些需求。
在IP直播场景里,TS通常被封装在UDP或RTP之上传输。UDP本身不保证可靠性,但TS的容错设计可以在一定程度上弥补。比如一个TS包丢了,接收端可以通过连续计数器发现丢包,然后等待下一个关键帧恢复画面,而不是整个流崩溃。这也是为什么很多直播平台在弱网环境下宁愿用TS over UDP也不用基于TCP的协议——TCP的重传机制在直播场景里反而会导致更大的延迟。
3.2 监控录像与长时间录制
监控设备对封装格式的要求很特殊:需要7×24小时不间断写入,断电时不能丢失已录制的数据,单个文件不能太大(否则检索和回放都很麻烦)。TS的追加写入特性完美匹配这些需求。监控摄像头通常会把视频切成一个个固定时长的TS文件(比如每5分钟一个),文件名里带上时间戳,检索的时候按时间找文件即可。断电时最多损失最后一个文件里未写完的部分,之前的文件都是完整的。
相比之下,如果用MP4做监控录制,断电时正在写入的文件因为索引没写完,整个文件可能都无法播放。这就是为什么你去看大多数监控设备的录像文件,后缀都是.ts或者.mp4但内部其实是TS流。
3.3 屏幕录制与游戏录制
很多录屏软件和游戏录制工具默认输出TS格式,原因和监控类似:录制过程中如果程序崩溃或系统断电,TS文件至少还能播放已录制的部分。而且TS的流式特性允许录制软件边录边写,不需要在内存里缓存整个文件。录制完成后再把TS转成MP4,既保证了录制过程的稳定性,又保证了最终文件的兼容性。
3.4 TS与MP4、MKV、FLV的选型对比
| 特性 | TS | MP4 | MKV | FLV |
|---|---|---|---|---|
| 设计目标 | 流式传输 | 文件存储 | 多轨道容器 | 网络流媒体 |
| 元信息位置 | 分散在每个包 | 集中在文件头/尾 | 集中在文件头 | 集中在文件头 |
| 抗丢包能力 | 强 | 弱 | 弱 | 中 |
| 随机接入 | 支持(等关键帧) | 支持(需索引) | 支持(需索引) | 支持(需索引) |
| 文件体积开销 | 较大(5%-15%) | 小 | 中 | 小 |
| 多轨道支持 | 有限 | 有限 | 极强 | 有限 |
| 断电恢复 | 好 | 差 | 差 | 中 |
| 浏览器兼容 | 需转封装 | 原生支持 | 需插件 | 需转封装 |
| 典型场景 | 直播、监控、录制 | 点播、分享 | 高清收藏 | 旧版网页视频 |
选型的时候可以按这个逻辑走:如果是直播或需要边写边播,优先TS;如果是本地存储和分享,优先MP4;如果需要多音轨多字幕,优先MKV;如果是兼容老旧网页播放器,才考虑FLV。现在FLV基本已经被HLS和DASH取代了,新项目不建议再用。
4. TS文件实操:解析、转封装与常见问题处理
4.1 用FFmpeg快速查看TS文件信息
拿到一个TS文件,第一步是搞清楚它里面有什么。FFmpeg的ffprobe工具是最顺手的:
ffprobe -v error -show_format -show_streams -print_format json input.ts这条命令会输出JSON格式的流信息,包括每个流的编码格式、分辨率、帧率、码率、PID等。重点看几个字段:codec_name告诉你视频是H.264还是H.265,codec_type区分视频/音频/字幕,id就是PID。如果输出里出现多个视频流,说明这个TS文件里混了多个节目,需要指定-program参数来选择。
如果只想快速看一眼,可以用:
ffprobe -v error -show_entries stream=index,codec_name,codec_type -of csv input.ts输出会更简洁,适合批量处理时用。
4.2 TS转MP4:无损转封装与重编码的选择
TS转MP4是最常见的需求。这里有一个关键决策:是“转封装”还是“重编码”。转封装(remux)只是把容器从TS换成MP4,视频和音频数据本身不变,速度极快,几GB的文件几秒钟就能完成,而且画质无损。重编码(transcode)则是把视频重新压缩一遍,速度慢、画质有损失,但可以改变编码格式、分辨率、码率。
绝大多数情况下,你需要的只是转封装:
ffmpeg -i input.ts -c copy -bsf:a aac_adtstoasc output.mp4这里的-c copy表示所有流直接复制,不重新编码。-bsf:a aac_adtstoasc是一个比特流过滤器,专门处理AAC音频从TS封装转到MP4封装时的格式差异。TS里的AAC音频带有ADTS头,MP4里不需要,所以要去掉。如果不加这个参数,转出来的MP4可能没有声音或者声音异常。
注意:如果TS里的视频是H.265(HEVC),转成MP4后部分老设备可能无法播放。H.265在MP4里的封装标准是后来才完善的,兼容性不如H.264。如果目标设备比较老,建议重编码成H.264。
如果确实需要重编码,比如要压缩体积或者统一格式:
ffmpeg -i input.ts -c:v libx264 -crf 23 -preset medium -c:a aac -b:a 128k output.mp4-crf 23是H.264的恒定质量参数,数值越小画质越好体积越大,18到28是比较常用的范围。-preset medium控制编码速度,越慢压缩率越高,medium是速度和压缩率的平衡点。
4.3 处理多节目TS流
有些TS文件里混了多个节目(比如从卫星接收机导出的整段转发器数据),直接转封装会报错或者只输出第一个节目。这时候需要先看清楚有几个节目:
ffprobe -v error -show_programs input.ts输出里会列出每个节目的program_id和包含的流。然后指定要提取的节目:
ffmpeg -i input.ts -map 0:p:1 -c copy output.mp4-map 0:p:1表示选择第一个输入的第1个节目(注意FFmpeg的节目编号从0开始还是从1开始取决于版本,实测一下最稳妥)。如果只想提取特定PID的流,可以用-map 0:0x101这样的写法,但PID在不同文件里不一样,不如按节目号来得方便。
4.4 TS文件播放卡顿或无法拖拽的排查
TS文件播放卡顿通常有几个原因。第一是PCR抖动太大,这在使用软件录制或者网络传输不稳定时很常见。可以用ffprobe看PCR间隔:
ffprobe -v error -show_packets -select_streams v -of csv input.ts | head -50看输出里的pts_time字段,如果相邻两个视频包的PTS间隔忽大忽小,说明时间戳有问题。这种情况转封装成MP4通常能解决,因为MP4不依赖PCR。
第二是文件本身不完整,比如下载中断或者录制时断电。TS文件因为没有全局索引,不完整也能播放,但播放器在拖拽时找不到关键帧就会卡住。可以用ffmpeg -i input.ts -c copy -f null -来检查文件是否能完整解码,如果有报错说明文件有损坏。
第三是播放器不支持TS里的某些编码格式。比如TS里用了AC-3音频,而播放器只支持AAC,就会只有画面没有声音。转封装时把音频重编码成AAC即可:
ffmpeg -i input.ts -c:v copy -c:a aac -b:a 128k output.mp44.5 批量处理TS文件的脚本思路
如果你有一堆TS文件要转MP4,一个个敲命令太累。Linux/macOS下可以用一个简单的for循环:
for f in *.ts; do ffmpeg -i "$f" -c copy -bsf:a aac_adtstoasc "${f%.ts}.mp4" doneWindows下可以用PowerShell:
Get-ChildItem -Filter *.ts | ForEach-Object { $out = $_.BaseName + ".mp4" ffmpeg -i $_.FullName -c copy -bsf:a aac_adtstoasc $out }如果文件很多,建议加一个-loglevel error参数减少输出,并且用-y自动覆盖同名文件。批量处理前最好先拿一个文件测试,确认转出来的MP4能正常播放再跑全量。
5. 深入TS包结构:开发者需要知道的细节
5.1 TS包头字段逐位解析
TS包头4个字节,共32位,字段分布如下:
| 位范围 | 字段名 | 说明 |
|---|---|---|
| 0-7 | sync_byte | 固定0x47,用于包同步 |
| 8 | transport_error_indicator | 1表示包在传输中出错 |
| 9 | payload_unit_start_indicator | 1表示负载以PES包或PSI表起始 |
| 10 | transport_priority | 1表示高优先级 |
| 11-23 | PID | 包标识符,13位 |
| 24-25 | transport_scrambling_control | 加扰控制 |
| 26-27 | adaptation_field_control | 自适应字段控制 |
| 28-31 | continuity_counter | 连续计数器,用于检测丢包 |
continuity_counter是排查丢包的关键。同一个PID的包,这个值应该从0到15循环递增。如果接收端发现某个PID的计数器跳变,说明中间有丢包。这个机制在监控和直播质量分析里非常有用。
5.2 自适应字段的作用
当adaptation_field_control为10或11时,包头后面会跟一个自适应字段。这个字段可以携带PCR、随机接入指示、不连续指示、拼接点等信息。PCR就是通过自适应字段传输的。自适应字段的长度可变,如果负载数据不够184字节,会用自适应字段填充到满,这就是为什么TS流里会有“填充包”。
开发者解析TS流时,必须正确处理自适应字段,否则会把填充数据当成有效负载。这也是很多自制TS解析器容易出错的地方。
5.3 PSI/SI表的解析顺序
PSI(Program Specific Information)包括PAT、PMT、CAT、NIT等表。解析顺序是:先找PID 0x0000的PAT,解析出每个节目的PMT PID;然后找对应PMT PID的包,解析出视频、音频、字幕的PID和类型;最后根据这些PID过滤出实际的音视频数据。
PSI表本身也是被切成TS包传输的,一个表可能跨多个包。每个表的第一个包payload_unit_start_indicator为1,后面跟着一个指针字段指示表的起始位置。解析的时候需要先拼装完整的表数据,再按表结构解析。
5.4 时间戳与音视频同步的实现
TS里的PTS和DTS是33位值,单位是90kHz。PCR是42位值,由33位基值和9位扩展值组成,单位是27MHz。音视频同步的基本逻辑是:接收端用PCR恢复出系统时钟,然后比较视频和音频的PTS,如果视频PTS落后于音频,就丢帧或加速解码;如果视频PTS超前,就等待或重复帧。
实际实现中,播放器通常会维护一个缓冲区,把音视频数据先缓存几百毫秒,然后根据PTS和系统时钟的差值动态调整播放速度。这个缓冲区的存在也是为什么直播会有几秒延迟的原因之一。
6. 常见问题速查与避坑经验
6.1 TS转MP4后没有声音怎么办
最常见的原因是AAC音频的ADTS头没有去掉。解决方法就是前面提到的加-bsf:a aac_adtstoasc参数。如果加了还是没声音,检查一下音频编码是不是AC-3或E-AC-3,这两种格式在MP4里的兼容性不好,需要重编码成AAC:
ffmpeg -i input.ts -c:v copy -c:a aac -b:a 128k output.mp4还有一种情况是TS里有多条音频流,转封装时默认只选了第一条,而第一条可能是空轨道或者评论音轨。用-map 0:a:0明确指定要哪一条。
6.2 转出来的MP4播放时音画不同步
音画不同步通常是因为时间戳处理有问题。TS里的PTS可能不是从0开始的,转封装时FFmpeg默认会保留原始时间戳,导致MP4的起始时间不是0。加-avoid_negative_ts make_zero参数可以强制时间戳从0开始:
ffmpeg -i input.ts -c copy -bsf:a aac_adtstoasc -avoid_negative_ts make_zero output.mp4如果还是不同步,可能是原始TS流的PCR就有问题,这种情况只能重编码,让FFmpeg重新生成时间戳。
6.3 TS文件体积异常大
TS文件比同样内容的MP4大是正常的,但如果大得离谱(比如大了一倍),可能是流里包含了大量空包或者重复数据。用ffprobe看一下码率,如果码率远高于视频实际需要的码率,说明流里有冗余。转封装成MP4通常能去掉大部分冗余,因为MP4不会保留空包。
另一种可能是TS里包含了多个节目或者多个音轨,而你只需要其中一个。用-map参数只提取需要的流即可。
6.4 播放器无法识别TS文件
有些播放器(尤其是移动端的)对TS支持不好,表现为能打开但黑屏,或者直接提示格式不支持。这不是文件的问题,是播放器的问题。解决方法有两个:一是换支持TS的播放器,比如VLC、PotPlayer、IINA;二是把TS转成MP4再播放。
如果是在网页里播放,浏览器原生不支持TS,必须转成MP4或者用HLS.js之类的库把TS切片喂给MSE(Media Source Extensions)。HLS就是基于TS切片的流媒体协议,苹果设备原生支持,其他平台需要JS库。
6.5 录制TS时如何避免文件损坏
录制TS时最大的风险是断电或程序崩溃导致文件不完整。虽然TS比MP4抗造,但也不是完全免疫。几个实用建议:第一,定期切分文件,比如每5分钟或每500MB切一个新文件,这样即使最后一个文件坏了,前面的都还在;第二,录制完成后用ffmpeg -i input.ts -c copy -f null -验证一下文件完整性;第三,如果录制软件支持,开启“边录边写索引”功能,有些软件会额外生成一个索引文件,方便回放时快速定位。
6.6 常见问题速查表
| 问题现象 | 可能原因 | 解决方法 |
|---|---|---|
| 转MP4后无声音 | ADTS头未去除 | 加-bsf:a aac_adtstoasc |
| 转MP4后音画不同步 | 时间戳非零起始 | 加-avoid_negative_ts make_zero |
| 播放卡顿 | PCR抖动或文件损坏 | 转封装成MP4或重编码 |
| 无法拖拽进度 | 缺少关键帧或索引 | 转封装成MP4 |
| 文件体积异常大 | 包含空包或多节目 | 用-map提取需要的流 |
| 播放器黑屏 | 播放器不支持TS | 换VLC或转MP4 |
| 录制文件损坏 | 断电或程序崩溃 | 定期切分文件,录制后验证 |
| 多节目流报错 | 未指定节目号 | 用-map 0:p:N指定节目 |
7. 我在实际处理TS文件时踩过的坑
第一个坑是以为所有TS文件都是H.264+AAC。实际上TS只是一个容器,里面可以装MPEG-2、H.264、H.265、AV1等各种视频编码,音频也可以是MP2、AC-3、AAC、Opus等。有一次我拿到一个TS文件,转MP4后画面正常但没声音,折腾了半天才发现音频是AC-3,MP4封装不支持,必须重编码。从那以后我养成了一个习惯:拿到任何TS文件,先ffprobe看一眼编码格式,再决定转封装还是重编码。
第二个坑是忽略了-bsf:a aac_adtstoasc这个参数。早期我转TS到MP4时经常遇到没声音的问题,后来查资料才知道是AAC的ADTS头在作怪。这个参数现在已经是我的肌肉记忆了,只要TS里有AAC音频,转MP4必加。
第三个坑是批量处理时没加错误处理。有一次我用for循环转几百个TS文件,中间有一个文件损坏,FFmpeg报错退出,后面的文件全没转。后来我改成在循环里加|| true,让单个文件失败不影响整体:
for f in *.ts; do ffmpeg -i "$f" -c copy -bsf:a aac_adtstoasc "${f%.ts}.mp4" || echo "Failed: $f" done第四个坑是以为转封装一定比重新编码快。大多数情况确实如此,但如果TS里的视频编码是MPEG-2而你想转成H.264,那就必须重编码,速度取决于CPU性能。有一次我处理一个两小时的MPEG-2 TS文件,以为几秒钟就能搞定,结果跑了四十多分钟。所以转之前一定要看清楚编码格式,对时间有个合理预期。
第五个坑是忽略了磁盘空间。TS转MP4虽然不重新编码,但输出文件大小和输入差不多,如果磁盘剩余空间不够,转一半会失败。批量处理前先算一下总大小,留出至少1.5倍的空间比较稳妥。
最后分享一个小技巧:如果你经常需要处理TS文件,可以写一个Shell函数或者批处理脚本,把常用的转封装命令封装起来,用的时候直接传文件名就行。比如:
ts2mp4() { ffmpeg -i "$1" -c copy -bsf:a aac_adtstoasc -avoid_negative_ts make_zero "${1%.ts}.mp4" }这样每次只需要敲ts2mp4 input.ts,省去了记参数的麻烦。对于需要重编码的情况,可以再写一个ts2mp4_reencode函数,把常用的CRF和preset参数固化进去。这种小工具积累多了,处理各种格式转换的效率会高很多。