简介:这是一款面向普通用户与视频处理初学者的TS视频合并工具,专门解决浏览器下载后视频被拆分为0.ts、1.ts、2.ts等零散分片、无法直接播放的问题。使用者只需在电脑浏览器中打开工具页面,输入最后一个分片编号并点击生成,即可将非加密的TS分片快速拼接为完整MP4文件,无需掌握命令行或专业剪辑软件。资源包共5个文件,包含html工具页面、bat批处理脚本、txt使用手册以及两段wmv操作演示视频,压缩包约1.54MB,体积轻巧、解压即用。其中手册与录屏配合,能帮助读者快速理解合并流程与常见问题处理思路,对不熟悉分片命名规则的场景也给出了联系渠道与参考方案。目前已有2633人学习下载,适合需要批量整理本地TS视频、追求简单高效合并方案的用户参考使用。
1. 0.ts、1.ts、2.ts 这类切片视频,为什么合并比下载还容易翻车
手头拿到一批0.ts、1.ts、2.ts命名的视频切片,第一反应往往是「按顺序拼起来不就完了」。真动手才发现:拼接顺序错一位,整段音画对不上;直接二进制相加,播放器能识别时长却拖不动进度条;用转码软件重编码,画质掉一档、耗时翻几倍。这类切片通常来自流媒体缓存或分片下载,命名连续但内部时间戳各自独立,合并的核心不是「拼文件」,而是「让容器层重新认账」。
这篇面向需要把0.ts、1.ts、2.ts等切片还原成完整视频的从业者:做素材归档的、处理离线缓存的、批量整理监控片段的。目标很明确——不重编码、不丢画质、进度条可拖、音画同步,并且能写成脚本批量跑。下面按「先搞懂 ts 切片的结构 → 选对合并方式 → 写可复现的命令 → 避开命名和时间戳的坑」这条线走,每一步都给能直接抄的命令和参数。
2. ts 切片到底特殊在哪:先搞懂容器再谈合并
2.1 ts 是传输流,不是普通视频文件
.ts全称 MPEG-TS(Transport Stream),设计初衷是「在不可靠信道上边传边播」,所以它把视频、音频、字幕拆成一个个 188 字节的定长包(packet),每个包带 PID 标识属于哪路流。这带来两个直接影响合并的后果:
第一,ts 允许在任意包边界切开,所以切片工具能按固定时长(常见 2 秒、5 秒、10 秒)把一路流切成0.ts、1.ts、2.ts。每个切片都是「自包含」的,有自己的 PAT/PMT 表描述流结构。
第二,每个切片内部有 PCR(Program Clock Reference)和 PTS/DTS 时间戳。如果切片是同一路流顺序切出来的,时间戳天然连续,直接拼接就能用;如果是分别下载、时间戳各自从零开始,拼起来就会出现时间跳变,播放器表现为卡顿、花屏或进度条乱跳。
判断你的切片属于哪种,用一条命令就能看:
# 查看单个切片的关键信息:时长、码率、流结构、时间戳起点 ffprobe -v error -show_format -show_streams -select_streams v:0 0.ts重点看输出里的start_time。如果0.ts的start_time是 0,1.ts的start_time也是 0,说明每个切片时间戳独立,直接拼会出问题;如果1.ts的start_time约等于0.ts的时长,说明时间戳连续,可以走无损拼接。
2.2 无损拼接和重编码,差的不是一点性能
合并 ts 有两条路,选错方向后面全是返工:
| 方式 | 原理 | 画质 | 速度 | 适用场景 |
|---|---|---|---|---|
| 无损拼接(concat) | 只重组容器,不碰编码数据 | 零损失 | 秒级 | 切片同源、编码参数一致 |
| 重编码(re-encode) | 解码后重新编码 | 有损失 | 慢 | 切片编码不一致、需剪辑 |
绝大多数0.ts、1.ts场景属于前者——切片本来就是同一路流切出来的,编码参数完全一致。这时候用 concat 协议或 concat demuxer,几秒就能合完几个 G 的文件。只有当你发现切片来自不同来源、分辨率或编码格式不一致时,才被迫重编码。
提示:先跑 ffprobe 对比首尾两个切片的
codec_name、width、height、r_frame_rate,四项全一致才走无损路线,否则拼接结果会花屏或直接失败。
2.3 为什么不能直接 cat 拼接
有人图省事用cat 0.ts 1.ts 2.ts > out.ts。这个操作在纯字节层面确实把文件接上了,但 ts 的包结构里含有连续性计数器(continuity counter)和 PCR,简单拼接会让计数器错乱、时间戳断层。结果就是:文件能打开,但播放器要么只认第一段,要么进度条拖到后面直接崩。血泪经验是——能播不代表能拖,能拖不代表音画同步,别拿 cat 赌。
3. 用 FFmpeg 合并 ts 切片:三种可复现的写法
3.1 方法一:concat 协议,适合切片少、命名规整
FFmpeg 的 concat 协议允许把多个文件当做一个虚拟输入:
# concat 协议:用 | 分隔文件列表,适合切片数量不多的情况 ffmpeg -i "concat:0.ts|1.ts|2.ts|3.ts" -c copy output.ts-c copy是关键,它告诉 FFmpeg 直接复制音视频流,不做解码重编码。这条命令执行速度取决于磁盘 IO,几个 G 的文件通常十几秒内完成。
逻辑说明:concat 协议在字节层面按顺序读取各文件,FFmpeg 负责重新封装成单一 ts。它不修复时间戳,所以要求切片本身时间戳连续。参数上-c copy等价于-codec copy,对音视频都生效;如果只想复制视频流、音频重新编码(极少用),可以拆成-c:v copy -c:a aac。
这个方法有个硬限制:文件多了命令行会超长,Windows 下尤其容易触发长度限制。切片超过二三十个,换方法二。
3.2 方法二:concat demuxer,适合大批量切片
concat demuxer 用一个文本清单文件描述输入,绕开命令行长度问题:
# 第一步:生成清单文件,注意路径写法 # 清单里每行格式为 file '文件名' for f in $(ls *.ts | sort -V); do echo "file '$f'" >> filelist.txt done # 第二步:用 concat demuxer 合并 ffmpeg -f concat -safe 0 -i filelist.txt -c copy output.tssort -V是版本号排序,保证10.ts排在2.ts之后而不是之前——这是命名坑里最常见的一个,普通sort会按字典序把10.ts排到2.ts前面,合并顺序直接错乱。
参数说明:-f concat指定使用 concat demuxer;-safe 0允许清单里使用相对路径以外的路径,不加这个参数遇到绝对路径会报Unsafe file name;-c copy同样是无损复制。清单文件里路径建议用相对路径,且文件名不含单引号,否则需要转义。
3.3 方法三:先转 MP4 再合并,适合要长期归档
ts 容器兼容性好但体积偏大,且部分播放器对 ts 的进度条支持一般。如果目标是归档,可以先无损转封装成 MP4:
# 无损转封装:ts 转 mp4,不重编码 ffmpeg -i 0.ts -c copy 0.mp4 # 或者合并后一次性转 mp4 ffmpeg -f concat -safe 0 -i filelist.txt -c copy -bsf:a aac_adtstoasc output.mp4-bsf:a aac_adtstoasc是 ts 转 mp4 时的关键参数。ts 里的 AAC 音频带 ADTS 头,MP4 容器不认这种格式,需要这个 bitstream filter 把 ADTS 头去掉。不加的话,转出来的 MP4 要么没声音,要么播放器报音频流错误。这是 ts 转 MP4 最经典的翻车点,记住它。
逻辑上,转封装和合并可以一步完成:concat demuxer 读入所有 ts,-c copy复制流,-bsf:a处理音频头,输出 mp4。整个过程不碰编码数据,画质无损,速度也快。
4. 批量合并脚本与参数调优:把重复劳动交给代码
4.1 一个能直接用的批量合并脚本
实际工作里切片往往按文件夹分组,每个文件夹是一段完整视频。下面这个 bash 脚本遍历子目录,各自合并:
#!/bin/bash # 批量合并脚本:遍历当前目录下每个子文件夹,合并其中的 ts 切片 set -e # 任一命令失败立即退出,避免产出半成品 for dir in */; do # 去掉目录名末尾的斜杠,作为输出文件名 name="${dir%/}" # 进入目录,按版本号排序生成清单 cd "$dir" ls *.ts 2>/dev/null | sort -V | awk '{print "file '\''"$0"'\''"}' > filelist.txt # 合并,输出到上级目录 ffmpeg -f concat -safe 0 -i filelist.txt -c copy "../${name}.ts" -y cd .. echo "完成:${name}.ts" done逻辑说明:set -e保证出错即停,不会留下半截文件;sort -V处理数字命名排序;awk生成符合 concat demuxer 格式的清单,注意单引号的转义写法;-y覆盖已存在输出,批量场景下避免交互卡住。
参数上可以加-loglevel warning减少刷屏,加-stats看进度。如果切片特别多(几百个),建议加-threads 0让 FFmpeg 自动用满 CPU——虽然-c copy是 IO 密集,但封装过程仍有少量计算。
4.2 时间戳不连续时的修复参数
如果 ffprobe 发现切片时间戳各自从零开始,直接 concat 会出问题。这时候有两个选择:
一是用-fflags +genpts让 FFmpeg 重新生成时间戳:
# 重新生成 PTS,修复时间戳断层 ffmpeg -fflags +genpts -f concat -safe 0 -i filelist.txt -c copy output.ts+genpts会在缺失或异常时间戳时重新生成,但它不保证音画严格同步,属于「能播但可能轻微漂移」的补救。
二是走重编码,用-vsync cfr强制恒定帧率:
# 重编码并强制恒定帧率,彻底解决时间戳问题,但有画质损失 ffmpeg -f concat -safe 0 -i filelist.txt -c:v libx264 -crf 18 -vsync cfr -c:a aac output.mp4-crf 18是 x264 的视觉无损档位,-vsync cfr把可变帧率压成恒定帧率,代价是文件变大、耗时增加。这是时间戳彻底乱掉时的后悔药,能不用就不用。
4.3 合并后必做的三项验证
合并不是跑完命令就结束,产出必须验证:
第一,看时长。ffprobe -v error -show_entries format=duration -of csv=p=0 output.ts,对比各切片时长之和,误差应在 1 秒内。
第二,看首尾。用播放器拖到开头和结尾各看几秒,确认没有黑屏、花屏、音画不同步。
第三,看进度条。拖动进度条到中间任意位置,能正常跳转且画面正确,说明时间戳连续。如果拖不动或跳转后花屏,回到 4.2 处理时间戳。
5. 合并 ts 切片最容易踩的五个坑
5.1 坑一:文件名排序错乱导致顺序颠倒
现象:合并后视频内容顺序不对,比如第 10 段跑到了第 2 段前面。
原因:ls默认按字典序排序,10.ts排在2.ts之前。切片数量超过 9 个就会触发。
解决:始终用sort -V(版本号排序)或sort -n(数字排序)。生成清单时确认顺序,可以cat filelist.txt肉眼检查一遍再合并。
5.2 坑二:清单文件路径含特殊字符导致读取失败
现象:FFmpeg 报No such file or directory或Unsafe file name,但文件明明存在。
原因:清单里路径含空格、中文、单引号,或用了绝对路径而没加-safe 0。
解决:清单里文件名用单引号包裹,路径尽量用相对路径;必须用绝对路径时加-safe 0。文件名含单引号的,需要写成'\''转义。
5.3 坑三:ts 转 MP4 后没有声音
现象:合并成 MP4 能播,但完全没音频,或播放器提示音频流错误。
原因:ts 里的 AAC 音频带 ADTS 头,MP4 容器不兼容,缺少 bitstream filter 转换。
解决:输出 MP4 时加-bsf:a aac_adtstoasc。如果音频不是 AAC 而是 MP3 或 AC3,这个 filter 不适用,需要先确认音频编码再决定处理方式。
5.4 坑四:切片编码参数不一致导致拼接花屏
现象:合并后中间某段开始花屏、绿屏,或直接卡死。
原因:切片虽然命名连续,但编码参数不同(分辨率、帧率、profile 不一致),-c copy强行拼接导致解码器无法处理。
解决:用 ffprobe 逐个检查切片的codec_name、width、height、r_frame_rate。不一致的切片只能重编码统一参数后再合并,没有无损办法。
5.5 坑五:磁盘空间不足导致输出截断
现象:合并命令跑到一半报错退出,输出文件比预期小很多。
原因:-c copy虽然不重编码,但输出文件大小约等于所有输入之和,临时空间不足会中途失败。
解决:合并前用df -h确认剩余空间大于切片总大小;批量脚本里可以在合并前加空间检查,不足则跳过并告警,避免产出半成品污染目录。
6. 进阶:用 Python 做智能合并与完整性校验
命令行能解决大部分场景,但切片数量上百、需要自动识别分组、合并后校验完整性时,用 Python 包一层更省心。下面这段脚本做三件事:按数字排序、调用 FFmpeg 合并、校验输出时长是否匹配。
import subprocess import re from pathlib import Path def natural_key(path): """按文件名中的数字自然排序,避免 10 排在 2 前面""" return [int(s) if s.isdigit() else s for s in re.split(r'(\d+)', path.name)] def merge_ts(folder: Path, output: Path): # 收集并按自然顺序排序 ts 文件 ts_files = sorted(folder.glob("*.ts"), key=natural_key) if not ts_files: raise ValueError(f"{folder} 下没有 ts 文件") # 生成 concat 清单 list_file = folder / "filelist.txt" with open(list_file, "w", encoding="utf-8") as f: for ts in ts_files: f.write(f"file '{ts.name}'\n") # 调用 ffmpeg 无损合并 cmd = [ "ffmpeg", "-f", "concat", "-safe", "0", "-i", str(list_file), "-c", "copy", "-loglevel", "warning", "-y", str(output) ] subprocess.run(cmd, check=True) # 校验:输出时长与各切片时长之和的误差 def duration(p): r = subprocess.run( ["ffprobe", "-v", "error", "-show_entries", "format=duration", "-of", "csv=p=0", str(p)], capture_output=True, text=True, check=True ) return float(r.stdout.strip()) total = sum(duration(ts) for ts in ts_files) actual = duration(output) diff = abs(total - actual) print(f"切片总时长 {total:.2f}s,输出时长 {actual:.2f}s,误差 {diff:.2f}s") if diff > 1.0: print("警告:时长误差超过 1 秒,检查时间戳是否连续") if __name__ == "__main__": merge_ts(Path("./segments"), Path("./output.ts"))逻辑说明:natural_key用正则把文件名拆成数字和非数字片段,数字转 int 后排序,彻底解决10和2的顺序问题;subprocess.run(check=True)在 FFmpeg 返回非零时抛异常,避免静默失败;时长校验用 ffprobe 分别取切片之和与输出,误差超过 1 秒就告警。
参数上,-loglevel warning只输出警告和错误,批量跑时日志干净;如果切片时间戳不连续,可以在 cmd 里加-fflags +genpts,但记得校验环节要放宽误差阈值,因为重新生成时间戳会改变总时长。
这套脚本我一般会再包一层:遍历多个子目录,每个目录输出一个合并文件,最后生成一份汇总报告,列出每个文件的切片数、总时长、校验结果。跑完一眼就能看出哪个目录有问题,不用逐个打开播放器确认。
一个习惯:合并前永远先跑一遍 ffprobe 检查首尾切片的时间戳和编码参数,确认无误再批量执行。这个前置检查花不了几秒,但能省下大量返工时间——毕竟合并几百个切片跑完才发现顺序错了,那种感觉比翻车还难受。希望帮到你。
本文还有配套的精品资源,点击获取