)
簡介這是一款面向普通用戶與音視頻愛好者的TS視頻合并工具專門解決瀏覽器下載視頻后碎片化、無法直接播放的問題。當視頻以0.ts、1.ts、2.ts等連續(xù)序號形式散落在文件夾中時只需打開工具頁面輸入末尾序號并生成合并腳本即可將碎片快速拼接為完整MP4文件無需掌握命令行或?qū)I(yè)剪輯軟件屬于零門檻的實用小工具。資源包共5個文件包含html工具頁面、bat批處理腳本、txt使用手冊以及兩段wmv操作演示視頻壓縮包約1.54MB體積輕巧、解壓即用。目前已有2633人學習下載說明該方案在同類需求中具備一定認可度。需要留意的是工具僅支持非加密且序號連續(xù)的ts文件若文件名帶有隨機前綴則需另行聯(lián)系作者處理。整體而言配套的圖文手冊與視頻教程能幫助讀者快速理解合并原理與排錯思路適合需要批量整理本地緩存視頻的用戶參考使用。1. 從一堆 0.ts、1.ts 說起這類分片視頻到底怎么無損合并手里拿到一個文件夾里面躺著0.ts、1.ts、2.ts……一直到幾百號每個文件幾兆到幾十兆不等播放器單獨打開能放但拖進度條就斷想發(fā)給別人又沒法一個個傳。這就是典型的 HLS 分片落地形態(tài)視頻被切成等長的 MPEG-TS 片段靠一個播放列表串起來順序播放。你要做的不是轉(zhuǎn)碼而是把這些碎片按正確順序拼回一個完整文件最好還不重新編碼、不掉畫質(zhì)、幾分鐘搞定。這件事的門檻比想象中低但坑比想象中多。順序錯一個整段音畫就亂時間戳沒對齊合并后進度條會跳封裝格式選錯某些播放器直接黑屏。下面按「先搞懂 TS 是什么 → 選工具 → 寫命令 → 批量腳本 → 排錯 → 進階校驗」的路徑走一遍新手能照著復現(xiàn)熟手能直接拿去改。2. 先弄明白 TS 分片和合并的底層邏輯為什么不能直接 cat2.1 MPEG-TS 的結(jié)構(gòu)決定了「拼接」這件事的可行性MPEG-TS 全稱 MPEG Transport Stream設(shè)計初衷就是為傳輸而生。它把音視頻數(shù)據(jù)打成固定 188 字節(jié)的包每個包帶 PID 標識屬于哪路流再靠 PTS/DTS 時間戳決定播放順序。關(guān)鍵點在于TS 本身就是為「流式拼接」設(shè)計的每個分片都是自包含的理論上按順序首尾相接就能還原成一條連續(xù)流。這也是為什么很多老工程師第一反應(yīng)是cat 0.ts 1.ts 2.ts out.ts。這個做法在純 TS 流上確實經(jīng)常能work因為 TS 沒有全局文件頭不需要像 MP4 那樣在開頭寫 moov box。但它有兩個致命前提分片必須嚴格按序、且時間戳連續(xù)。一旦中間缺片或者時間戳有跳變cat 出來的文件播放器就會卡死或音畫不同步。2.2 直接 cat 會在什么情況下翻車我踩過的典型場景有三個。第一分片不是從 0 開始編號比如從 100.ts 開始說明前面還有內(nèi)容直接 cat 會丟頭。第二分片之間存在重疊或間隙某些下載工具會重復寫入邊界幀cat 后出現(xiàn)花屏。第三音視頻 PID 在不同分片里不一致這種情況少見但一旦出現(xiàn)cat 出來的文件只有畫面沒聲音。所以結(jié)論是cat 可以作為快速驗證手段但不能作為生產(chǎn)方案。真正可靠的合并要么用 ffmpeg 的 concat 協(xié)議要么先轉(zhuǎn)成 MPEG-TS 再走 concat demuxer讓工具去處理時間戳和邊界。2.3 合并前必須先做的三件事動手之前花兩分鐘做檢查能省掉后面半小時的排錯。第一確認分片完整性??淳幪柺欠襁B續(xù)有沒有0.ts到N.ts中間缺號。缺號意味著數(shù)據(jù)不完整合并出來必然有斷點。第二確認分片格式。用ffprobe抽查一兩個文件看容器是不是 mpegts、編碼是不是 h264/aac。有些站點會把分片偽裝成 .ts 但實際是 fMP4 片段這種就不能按 TS 處理。第三確認播放列表。如果目錄里有.m3u8文件直接讀它里面#EXTINF的順序就是權(quán)威順序比按文件名排序靠譜得多。文件名排序在遇到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正常應(yīng)該看到一個 h264 視頻流和一個 aac 音頻流。如果只有視頻沒有音頻說明分片本身有問題合并也救不回來。3. 用 ffmpeg 合并 TSconcat 協(xié)議和 concat demuxer 怎么選3.1 兩種合并方式的本質(zhì)區(qū)別ffmpeg 提供兩條路。第一條是concat 協(xié)議寫法是concat:0.ts|1.ts|2.ts它把多個文件當成一個虛擬輸入流底層直接做字節(jié)級拼接。優(yōu)點是快幾乎不消耗 CPU因為不解碼。缺點是要求所有分片的編碼參數(shù)完全一致且對時間戳跳變?nèi)萑潭鹊?。第二條是concat demuxer需要先寫一個列表文件然后ffmpeg -f concat -i list.txt -c copy out.mp4。它會解析每個分片的時間戳并重新對齊容錯性更好還能順便換封裝格式。缺點是比協(xié)議方式稍慢但依然是 copy 模式不重新編碼。我的選擇習慣是分片來自同一個源、編號連續(xù)、格式統(tǒng)一用 concat 協(xié)議一行命令搞定分片來源混雜、時間戳有跳變、或者要輸出 MP4用 concat demuxer。3.2 concat 協(xié)議的最小可用命令# 最簡形式按順序列出所有分片-c copy 表示不重新編碼 ffmpeg -i concat:0.ts|1.ts|2.ts|3.ts -c copy -bsf:a aac_adtstoasc output.mp4這里有幾個參數(shù)必須說清楚。-c copy是核心它告訴 ffmpeg 直接復制音視頻流不做解碼再編碼所以速度快、畫質(zhì)無損。-bsf:a aac_adtstoasc是音頻比特流過濾器作用是把 AAC 的 ADTS 頭轉(zhuǎn)成 ASC 格式因為 TS 里的 AAC 帶 ADTS 頭而 MP4 容器要求 ASC 格式不加這個參數(shù)輸出 MP4 時音頻會報錯或直接丟失。如果輸出還是 .ts 格式這個過濾器可以不加。分片數(shù)量少的時候手寫管道符沒問題但幾百個分片就不現(xiàn)實了。這時候用 shell 拼字符串# 用 seq 生成 0 到 299 的分片列表拼成 concat 協(xié)議字符串 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 |用豎線把它們連成一行。這個寫法比手敲可靠但前提是編號連續(xù)且從 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是關(guān)鍵它做的是自然排序version sort會把2.ts排在10.ts前面避免字典序的坑。-f concat指定使用 concat 解復用器-safe 0允許列表里出現(xiàn)絕對路徑不加這個ffmpeg 會拒絕非相對路徑。列表文件里每行file 路徑路徑用單引號包住防止空格和特殊字符出問題。提示如果分片文件名里有單引號需要轉(zhuǎn)義成\這種情況少見但遇到會報語法錯誤。3.4 輸出格式怎么選TS 還是 MP4如果只是自己存檔、用支持 TS 的播放器看直接輸出.ts最省事不需要aac_adtstoasc也不會有封裝兼容問題。但如果要發(fā)給別人、上傳到常見平臺、或者用手機默認播放器打開輸出 MP4 兼容性最好。輸出 MP4 時注意一點-c copy模式下如果原視頻是 h264、音頻是 aacMP4 完全支持直接封裝沒問題。但如果視頻是 h265老設(shè)備可能放不了這不是合并的問題是編碼兼容性問題需要重新編碼才能解決而重新編碼就違背了「無損快速合并」的初衷。4. 批量合并腳本從單目錄到多目錄的自動化處理4.1 單目錄批量合并的 shell 腳本實際場景往往不是合并一個視頻而是一批。比如下載了十幾個課程每個課程一個文件夾里面都是 TS 分片。手敲命令不現(xiàn)實寫個腳本一次處理。#!/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這個腳本的邏輯是外層循環(huán)遍歷所有子目錄內(nèi)層用ls -v生成自然序列表然后調(diào)用 ffmpeg 合并輸出到上級目錄并以目錄名命名。-y表示覆蓋已存在文件避免交互確認卡住腳本。ls *.ts /dev/null 21用來判斷目錄里有沒有 TS 文件沒有就跳過防止空目錄報錯。4.2 處理編號不連續(xù)的分片編號不連續(xù)是常見情況比如0.ts、1.ts、3.ts缺了2.ts。這時候直接合并會丟一段內(nèi)容但有時候缺的那片本來就沒下載成功你只能接受。腳本層面要做的是檢測缺號并提示而不是默默合并。# 檢測編號連續(xù)性輸出缺失的編號 prev-1 for f in $(ls -v *.ts); do num${f%.ts} # 只處理純數(shù)字文件名 if [[ $num ~ ^[0-9]$ ]]; then if [[ $prev -ge 0 $((num - prev)) -ne 1 ]]; then echo 警告: $((prev1)) 到 $((num-1)) 之間缺片 fi prev$num fi done這段邏輯遍歷所有 TS 文件提取文件名里的數(shù)字檢查相鄰編號差值是否為 1。不是 1 就說明中間有缺號打印警告。[[ $num ~ ^[0-9]$ ]]用來過濾掉非純數(shù)字命名的文件避免誤判。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 fgv5xshg9wyt.mp4 if os.path.exists(out): print(f已存在跳過: {out}) continue ok merge_ts_dir(d, out) print(f{成功 if ok else 失敗}: {out})關(guān)鍵點在排序函數(shù)keylambda x: int(x[:-3])把文件名去掉.ts后轉(zhuǎn)成整數(shù)排序這樣10.ts會正確排在2.ts后面。subprocess.run用capture_outputTrue捕獲輸出避免 ffmpeg 的日志刷屏同時通過returncode判斷成功與否。跳過已存在文件的邏輯讓腳本可以中斷后重跑不用從頭再來。5. 合并 TS 分片最容易踩的坑排查清單5.1 合并后音畫不同步進度條亂跳現(xiàn)象合并出來的文件能播放但聲音比畫面快或慢幾秒拖進度條時時間顯示異常。原因分片之間的 PTS 時間戳不連續(xù)concat 協(xié)議直接拼接時沒有重新對齊時間戳播放器按錯誤的時間軸解碼。解決改用 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 后沒有聲音現(xiàn)象合并過程沒報錯但輸出文件只有畫面音頻軌缺失。原因TS 里的 AAC 音頻帶 ADTS 頭MP4 容器不認ffmpeg 在 copy 模式下不會自動轉(zhuǎn)換導致音頻流被丟棄。解決加上-bsf:a aac_adtstoasc。如果加了還是沒聲音用ffprobe檢查原分片有沒有音頻流可能源頭就沒有。5.3 分片順序錯亂畫面跳來跳去現(xiàn)象合并后內(nèi)容順序不對比如第 10 分鐘的內(nèi)容跑到了第 2 分鐘。原因用了字典序排序10.ts排在了2.ts前面。解決用ls -v或 Python 的整數(shù)排序。如果目錄里有 m3u8直接按 m3u8 里的順序生成列表那是最權(quán)威的。5.4 ffmpeg 報 Non-monotonous DTS 警告現(xiàn)象合并時刷大量Non-monotonous DTS in output stream警告輸出文件可能仍有問題。原因分片的時間戳有回退或重復常見于從不同來源下載的分片混在一起。解決這種警告在 copy 模式下無法完全消除因為 ffmpeg 不能改時間戳??尚械霓k法是先轉(zhuǎn)成 MPEG-TS 中間格式再合并或者接受警告但檢查輸出是否可正常播放。如果嚴重影響播放只能重新編碼用-c:v libx264 -c:a aac犧牲速度換正確性。5.5 合并后文件巨大或異常小現(xiàn)象輸出文件比所有分片加起來還大或者小得離譜。原因文件巨大通常是誤用了重新編碼且碼率設(shè)太高文件異常小通常是 concat 列表只包含了一部分分片或者路徑寫錯導致 ffmpeg 跳過了文件。解決檢查 list.txt 的行數(shù)是否等于分片數(shù)檢查每條路徑是否真實存在。用-c copy確認沒有觸發(fā)編碼。正常情況輸出大小應(yīng)該約等于所有分片大小之和。6. 合并之后的校驗與進階技巧別讓幾小時下載毀在最后一步合并完成不等于萬事大吉。我一般會做三件事校驗時長、抽查關(guān)鍵幀、確認音視頻軌完整。時長校驗最直接用ffprobe讀輸出文件的 duration和所有分片時長之和對比誤差在幾秒內(nèi)算正常。如果差太多說明有分片沒被合并進去。# 查看合并后文件的總時長和流信息 ffprobe -v error -show_entries formatduration -show_entries streamcodec_type,codec_name -of defaultnoprint_wrappers1 output.mp4抽查關(guān)鍵幀用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進階一點的做法是如果分片數(shù)量特別多上千個concat 列表會很長ffmpeg 解析列表本身會變慢。這時候可以分批合并先每 100 個分片合并成一個中間 TS再把中間 TS 合并成最終文件。這樣每步的列表都短整體更快而且中間產(chǎn)物還能作為斷點續(xù)做的依據(jù)。另一個技巧是保留原始分片直到確認輸出無誤。我吃過虧合并完覺得沒問題就把分片刪了結(jié)果發(fā)現(xiàn)輸出文件在某個播放器上不兼容想重來已經(jīng)沒有原料了。分片占的空間雖然不小但在確認最終文件能正常播放、時長正確、音畫同步之前別急著清理。最后一個習慣合并命令和 list.txt 一起存到輸出目錄旁邊。過幾個月回頭看能立刻知道這個文件是怎么來的、用的什么參數(shù)。這比事后回憶靠譜得多。希望幫到你。本文還有配套的精品資源點擊獲取