
简介这是一款面向普通用户与音视频爱好者的TS视频合并工具专门解决浏览器下载视频后碎片化、无法直接播放的问题。当视频以0.ts、1.ts、2.ts等连续序号形式散落在文件夹中时只需打开工具页面输入末尾序号并生成合并脚本即可将碎片快速拼接为完整MP4文件无需掌握命令行或专业剪辑软件属于零门槛的实用小工具。资源包共5个文件包含html工具页面、bat批处理脚本、txt使用手册以及两段wmv操作演示视频压缩包约1.54MB体积轻巧、解压即用。目前已有2633人学习下载说明该方案在同类需求中具备一定认可度。需要留意的是工具仅支持非加密且序号连续的ts文件若文件名带有随机前缀则需另行联系作者处理。整体而言配套的图文手册与视频教程能帮助读者快速理解合并原理与排错思路适合需要批量整理本地缓存视频的用户参考使用。1. 从一堆 0.ts、1.ts 说起这类分片视频到底怎么无损合并手里拿到一个文件夹里面躺着0.ts、1.ts、2.ts……一直到几百号每个文件几兆到几十兆不等播放器单独打开能放但拖进度条就断想发给别人又没法一个个传。这就是典型的 HLS 分片落地形态视频被切成等长的 MPEG-TS 片段靠一个播放列表串起来顺序播放。你要做的不是转码而是把这些碎片按正确顺序拼回一个完整文件最好还不重新编码、不掉画质、几分钟搞定。这件事的门槛比想象中低但坑比想象中多。顺序错一个整段音画就乱时间戳没对齐合并后进度条会跳封装格式选错某些播放器直接黑屏。下面按「先搞懂 TS 是什么 → 选工具 → 写命令 → 批量脚本 → 排错 → 进阶校验」的路径走一遍新手能照着复现熟手能直接拿去改。2. 先弄明白 TS 分片和合并的底层逻辑为什么不能直接 cat2.1 MPEG-TS 的结构决定了「拼接」这件事的可行性MPEG-TS 全称 MPEG Transport Stream设计初衷就是为传输而生。它把音视频数据打成固定 188 字节的包每个包带 PID 标识属于哪路流再靠 PTS/DTS 时间戳决定播放顺序。关键点在于TS 本身就是为「流式拼接」设计的每个分片都是自包含的理论上按顺序首尾相接就能还原成一条连续流。这也是为什么很多老工程师第一反应是cat 0.ts 1.ts 2.ts out.ts。这个做法在纯 TS 流上确实经常能work因为 TS 没有全局文件头不需要像 MP4 那样在开头写 moov box。但它有两个致命前提分片必须严格按序、且时间戳连续。一旦中间缺片或者时间戳有跳变cat 出来的文件播放器就会卡死或音画不同步。2.2 直接 cat 会在什么情况下翻车我踩过的典型场景有三个。第一分片不是从 0 开始编号比如从 100.ts 开始说明前面还有内容直接 cat 会丢头。第二分片之间存在重叠或间隙某些下载工具会重复写入边界帧cat 后出现花屏。第三音视频 PID 在不同分片里不一致这种情况少见但一旦出现cat 出来的文件只有画面没声音。所以结论是cat 可以作为快速验证手段但不能作为生产方案。真正可靠的合并要么用 ffmpeg 的 concat 协议要么先转成 MPEG-TS 再走 concat demuxer让工具去处理时间戳和边界。2.3 合并前必须先做的三件事动手之前花两分钟做检查能省掉后面半小时的排错。第一确认分片完整性。看编号是否连续有没有0.ts到N.ts中间缺号。缺号意味着数据不完整合并出来必然有断点。第二确认分片格式。用ffprobe抽查一两个文件看容器是不是 mpegts、编码是不是 h264/aac。有些站点会把分片伪装成 .ts 但实际是 fMP4 片段这种就不能按 TS 处理。第三确认播放列表。如果目录里有.m3u8文件直接读它里面#EXTINF的顺序就是权威顺序比按文件名排序靠谱得多。文件名排序在遇到10.ts和2.ts时会按字典序排成10在2前面这是新手最常翻的车。# 抽查分片格式确认是 mpegts 容器 ffprobe -v error -show_entries formatformat_name -of defaultnoprint_wrappers1 0.ts # 查看分片时长和编码确认音视频流都存在 ffprobe -v error -show_entries streamindex,codec_name,codec_type -of csv 0.ts # 如果有 m3u8直接看它的分片顺序 grep -E \.ts playlist.m3u8 | head -20ffprobe的-v error只输出错误避免刷屏format_name返回mpegts就说明是标准 TS。stream那行会列出每个流的 codec正常应该看到一个 h264 视频流和一个 aac 音频流。如果只有视频没有音频说明分片本身有问题合并也救不回来。3. 用 ffmpeg 合并 TSconcat 协议和 concat demuxer 怎么选3.1 两种合并方式的本质区别ffmpeg 提供两条路。第一条是concat 协议写法是concat:0.ts|1.ts|2.ts它把多个文件当成一个虚拟输入流底层直接做字节级拼接。优点是快几乎不消耗 CPU因为不解码。缺点是要求所有分片的编码参数完全一致且对时间戳跳变容忍度低。第二条是concat demuxer需要先写一个列表文件然后ffmpeg -f concat -i list.txt -c copy out.mp4。它会解析每个分片的时间戳并重新对齐容错性更好还能顺便换封装格式。缺点是比协议方式稍慢但依然是 copy 模式不重新编码。我的选择习惯是分片来自同一个源、编号连续、格式统一用 concat 协议一行命令搞定分片来源混杂、时间戳有跳变、或者要输出 MP4用 concat demuxer。3.2 concat 协议的最小可用命令# 最简形式按顺序列出所有分片-c copy 表示不重新编码 ffmpeg -i concat:0.ts|1.ts|2.ts|3.ts -c copy -bsf:a aac_adtstoasc output.mp4这里有几个参数必须说清楚。-c copy是核心它告诉 ffmpeg 直接复制音视频流不做解码再编码所以速度快、画质无损。-bsf:a aac_adtstoasc是音频比特流过滤器作用是把 AAC 的 ADTS 头转成 ASC 格式因为 TS 里的 AAC 带 ADTS 头而 MP4 容器要求 ASC 格式不加这个参数输出 MP4 时音频会报错或直接丢失。如果输出还是 .ts 格式这个过滤器可以不加。分片数量少的时候手写管道符没问题但几百个分片就不现实了。这时候用 shell 拼字符串# 用 seq 生成 0 到 299 的分片列表拼成 concat 协议字符串 files$(seq -f %g.ts 0 299 | paste -sd |) ffmpeg -i concat:$files -c copy -bsf:a aac_adtstoasc output.mp4seq -f %g.ts 0 299生成0.ts到299.tspaste -sd |用竖线把它们连成一行。这个写法比手敲可靠但前提是编号连续且从 0 开始。如果编号有跳号得先手动核对。3.3 concat demuxer 的列表文件写法# 生成 concat 列表file 后面跟路径单引号防止特殊字符 for f in $(ls -v *.ts); do echo file $PWD/$f done list.txt # 用 concat demuxer 合并输出 MP4 ffmpeg -f concat -safe 0 -i list.txt -c copy -bsf:a aac_adtstoasc output.mp4ls -v是关键它做的是自然排序version sort会把2.ts排在10.ts前面避免字典序的坑。-f concat指定使用 concat 解复用器-safe 0允许列表里出现绝对路径不加这个ffmpeg 会拒绝非相对路径。列表文件里每行file 路径路径用单引号包住防止空格和特殊字符出问题。提示如果分片文件名里有单引号需要转义成\这种情况少见但遇到会报语法错误。3.4 输出格式怎么选TS 还是 MP4如果只是自己存档、用支持 TS 的播放器看直接输出.ts最省事不需要aac_adtstoasc也不会有封装兼容问题。但如果要发给别人、上传到常见平台、或者用手机默认播放器打开输出 MP4 兼容性最好。输出 MP4 时注意一点-c copy模式下如果原视频是 h264、音频是 aacMP4 完全支持直接封装没问题。但如果视频是 h265老设备可能放不了这不是合并的问题是编码兼容性问题需要重新编码才能解决而重新编码就违背了「无损快速合并」的初衷。4. 批量合并脚本从单目录到多目录的自动化处理4.1 单目录批量合并的 shell 脚本实际场景往往不是合并一个视频而是一批。比如下载了十几个课程每个课程一个文件夹里面都是 TS 分片。手敲命令不现实写个脚本一次处理。#!/bin/bash # 遍历当前目录下所有子目录每个子目录当作一个视频的分片集合 for dir in */; do # 去掉目录名末尾的斜杠作为输出文件名 name${dir%/} # 进入目录按自然序生成列表 cd $dir || continue if ls *.ts /dev/null 21; then for f in $(ls -v *.ts); do echo file $PWD/$f done list.txt # 输出到上级目录文件名用目录名 ffmpeg -f concat -safe 0 -i list.txt -c copy -bsf:a aac_adtstoasc ../${name}.mp4 -y echo 完成: ${name}.mp4 else echo 跳过: ${dir} 没有 ts 文件 fi cd .. done这个脚本的逻辑是外层循环遍历所有子目录内层用ls -v生成自然序列表然后调用 ffmpeg 合并输出到上级目录并以目录名命名。-y表示覆盖已存在文件避免交互确认卡住脚本。ls *.ts /dev/null 21用来判断目录里有没有 TS 文件没有就跳过防止空目录报错。4.2 处理编号不连续的分片编号不连续是常见情况比如0.ts、1.ts、3.ts缺了2.ts。这时候直接合并会丢一段内容但有时候缺的那片本来就没下载成功你只能接受。脚本层面要做的是检测缺号并提示而不是默默合并。# 检测编号连续性输出缺失的编号 prev-1 for f in $(ls -v *.ts); do num${f%.ts} # 只处理纯数字文件名 if [[ $num ~ ^[0-9]$ ]]; then if [[ $prev -ge 0 $((num - prev)) -ne 1 ]]; then echo 警告: $((prev1)) 到 $((num-1)) 之间缺片 fi prev$num fi done这段逻辑遍历所有 TS 文件提取文件名里的数字检查相邻编号差值是否为 1。不是 1 就说明中间有缺号打印警告。[[ $num ~ ^[0-9]$ ]]用来过滤掉非纯数字命名的文件避免误判。4.3 用 Python 做更可控的批量处理shell 脚本适合简单场景但如果要处理几百个目录、需要记录日志、需要跳过已完成的Python 更合适。import os import subprocess from pathlib import Path def merge_ts_dir(src_dir, out_file): 合并单个目录下的 TS 分片返回是否成功 ts_files sorted( [f for f in os.listdir(src_dir) if f.endswith(.ts)], keylambda x: int(x[:-3]) if x[:-3].isdigit() else 0 ) if not ts_files: return False list_path os.path.join(src_dir, list.txt) with open(list_path, w, encodingutf-8) as fp: for f in ts_files: fp.write(ffile {os.path.join(src_dir, f)}\n) cmd [ ffmpeg, -f, concat, -safe, 0, -i, list_path, -c, copy, -bsf:a, aac_adtstoasc, out_file, -y ] result subprocess.run(cmd, capture_outputTrue) return result.returncode 0 # 遍历当前目录下所有子目录 for d in sorted(os.listdir(.)): if os.path.isdir(d): out f{d}.mp4 if os.path.exists(out): print(f已存在跳过: {out}) continue ok merge_ts_dir(d, out) print(f{成功 if ok else 失败}: {out})关键点在排序函数keylambda x: int(x[:-3])把文件名去掉.ts后转成整数排序这样10.ts会正确排在2.ts后面。subprocess.run用capture_outputTrue捕获输出避免 ffmpeg 的日志刷屏同时通过returncode判断成功与否。跳过已存在文件的逻辑让脚本可以中断后重跑不用从头再来。5. 合并 TS 分片最容易踩的坑排查清单5.1 合并后音画不同步进度条乱跳现象合并出来的文件能播放但声音比画面快或慢几秒拖进度条时时间显示异常。原因分片之间的 PTS 时间戳不连续concat 协议直接拼接时没有重新对齐时间戳播放器按错误的时间轴解码。解决改用 concat demuxer它会重新计算时间戳。如果还不行加-fflags genpts让 ffmpeg 重新生成 PTSffmpeg -f concat -safe 0 -i list.txt -c copy -fflags genpts -bsf:a aac_adtstoasc output.mp45.2 输出 MP4 后没有声音现象合并过程没报错但输出文件只有画面音频轨缺失。原因TS 里的 AAC 音频带 ADTS 头MP4 容器不认ffmpeg 在 copy 模式下不会自动转换导致音频流被丢弃。解决加上-bsf:a aac_adtstoasc。如果加了还是没声音用ffprobe检查原分片有没有音频流可能源头就没有。5.3 分片顺序错乱画面跳来跳去现象合并后内容顺序不对比如第 10 分钟的内容跑到了第 2 分钟。原因用了字典序排序10.ts排在了2.ts前面。解决用ls -v或 Python 的整数排序。如果目录里有 m3u8直接按 m3u8 里的顺序生成列表那是最权威的。5.4 ffmpeg 报 Non-monotonous DTS 警告现象合并时刷大量Non-monotonous DTS in output stream警告输出文件可能仍有问题。原因分片的时间戳有回退或重复常见于从不同来源下载的分片混在一起。解决这种警告在 copy 模式下无法完全消除因为 ffmpeg 不能改时间戳。可行的办法是先转成 MPEG-TS 中间格式再合并或者接受警告但检查输出是否可正常播放。如果严重影响播放只能重新编码用-c:v libx264 -c:a aac牺牲速度换正确性。5.5 合并后文件巨大或异常小现象输出文件比所有分片加起来还大或者小得离谱。原因文件巨大通常是误用了重新编码且码率设太高文件异常小通常是 concat 列表只包含了一部分分片或者路径写错导致 ffmpeg 跳过了文件。解决检查 list.txt 的行数是否等于分片数检查每条路径是否真实存在。用-c copy确认没有触发编码。正常情况输出大小应该约等于所有分片大小之和。6. 合并之后的校验与进阶技巧别让几小时下载毁在最后一步合并完成不等于万事大吉。我一般会做三件事校验时长、抽查关键帧、确认音视频轨完整。时长校验最直接用ffprobe读输出文件的 duration和所有分片时长之和对比误差在几秒内算正常。如果差太多说明有分片没被合并进去。# 查看合并后文件的总时长和流信息 ffprobe -v error -show_entries formatduration -show_entries streamcodec_type,codec_name -of defaultnoprint_wrappers1 output.mp4抽查关键帧用ffmpeg截几张图看画面是否正常、有没有花屏。尤其是分片边界处最容易出问题。# 在 10%、50%、90% 位置各截一帧检查画面 ffmpeg -ss 00:10:00 -i output.mp4 -frames:v 1 check1.png -y ffmpeg -ss 00:30:00 -i output.mp4 -frames:v 1 check2.png -y进阶一点的做法是如果分片数量特别多上千个concat 列表会很长ffmpeg 解析列表本身会变慢。这时候可以分批合并先每 100 个分片合并成一个中间 TS再把中间 TS 合并成最终文件。这样每步的列表都短整体更快而且中间产物还能作为断点续做的依据。另一个技巧是保留原始分片直到确认输出无误。我吃过亏合并完觉得没问题就把分片删了结果发现输出文件在某个播放器上不兼容想重来已经没有原料了。分片占的空间虽然不小但在确认最终文件能正常播放、时长正确、音画同步之前别急着清理。最后一个习惯合并命令和 list.txt 一起存到输出目录旁边。过几个月回头看能立刻知道这个文件是怎么来的、用的什么参数。这比事后回忆靠谱得多。希望帮到你。本文还有配套的精品资源点击获取