ffmpeg 合并 TS 文件完整教程(附批量脚本+常见报错解决)


目录

  1. 不想装软件?直接用在线工具
  2. [前置准备:安装 FFmpeg](#前置准备:安装 FFmpeg)
  3. 方法一:concat协议(无损、最快)
  4. 方法二:批处理脚本一键合并
  5. 方法三:Python批量合并多文件夹
  6. 常见报错与解决
  7. 小结

零、不想装软件?在线工具直接搞定

如果你现在就有 TS 文件要合并,不想折腾安装配置,可以先试试这个方案。

市面上大多数在线 TS 合并工具都是"上传到服务器→合并→下载",这就带来了两个问题:一是你的视频文件会经过别人的服务器(隐私隐患),二是大文件上传下载慢得让人抓狂。

这里推荐一个不走寻常路的在线工具 ------ ezwebtools TS文件合并 。它用的是 FFmpeg.wasm,把 FFmpeg 编译成 WebAssembly 直接在浏览器里跑,意味着你的文件根本不用上传到服务器,全程在本地浏览器完成。几个核心功能点:

功能 说明
本地处理,隐私安全 基于 FFmpeg.wasm,文件不上传服务器,所有操作在浏览器本地完成
加密 TS 解密 支持 AES-128 加密的 TS 文件,上传 .key 密钥文件即可自动解密合并
智能排序 自动识别文件名中的数字序列,不会出现 1.ts → 10.ts → 2.ts 的顺序错误
流复制无损 底层走 concat 协议 + -c copy,不重新编码,画质零损失
手动拖拽调整 页面上可以拖拽文件调整顺序,所见即所得

局限性也得说清楚:因为跑的是 WebAssembly 而非原生 FFmpeg,处理几个 GB 的大文件时速度不如本地安装版。另外建议用 Chrome / Edge 最新版,老浏览器可能兼容性不好。

结论:几百 MB 的 TS 文件、偶尔用一次、或者遇到加密文件,直接用 ezwebtools 最省心。需要频繁处理大文件的话,接着往下看,把 FFmpeg 装好更划算。


一、前置准备:安装 FFmpeg

TS 文件合并这件事,说到底就是 FFmpeg 的活。不管你用什么 GUI 工具,背后大概率还是调它。所以第一步先装上。

Windows 安装步骤:

  1. 打开 FFmpeg 官网下载页
  2. 找到 Windows 区域,选 gyan.dev 或者 BtbN 的链接
  3. 下载 ffmpeg-release-full.7z,用 7-Zip 解压到 C:\ffmpeg
  4. C:\ffmpeg\bin 加到系统环境变量 Path 里:
  1. 打开终端(Win+R → powershell),输入 ffmpeg -version,看到版本信息就说明装好了。
bash 复制代码
PS C:\Users\Admin> ffmpeg -version
ffmpeg version 6.1.1-full_build-www.gyan.dev Copyright (c) 2000-2023 ...

二、方法一:concat协议合并(无损、最快)

这是最推荐的方法。-c copy 表示流复制,不会重新编码,所以速度极快、画质无损

步骤1:生成文件列表

先把目录下所有 .ts 文件的文件名写到一个文本文件里:

bash 复制代码
# Windows PowerShell
Get-ChildItem *.ts | ForEach-Object { "file '$($_.Name)'" } | Out-File -Encoding utf8 filelist.txt -NoNewline

执行完后,当前目录会生成一个 filelist.txt,内容大概长这样:

bash 复制代码
file 'segment_000.ts'
file 'segment_001.ts'
file 'segment_002.ts'
file 'segment_003.ts'
...

注意 :PowerShell 生成的文件列表默认是按文件名字典序排列的。如果 TS 文件名是 1.ts, 2.ts, ..., 10.ts,字典序会把 10.ts 排在 2.ts 前面。如果你是用工具从 M3U8 下载的,建议直接用 M3U8 文件里的顺序来生成列表。这个坑后面会专门讲。

步骤2:执行合并

bash 复制代码
ffmpeg -f concat -safe 0 -i filelist.txt -c copy output.mp4

参数解释:

参数 含义
-f concat 使用 concat demuxer(拼接解复用器)
-safe 0 允许使用绝对路径和特殊字符
-i filelist.txt 指定输入文件列表
-c copy 流复制,不重新编码
output.mp4 输出文件名

什么情况下这个方法会失败?

如果你的 TS 文件编码参数不一致(比如分辨率不同、编码格式不同),concat demuxer 会报错。这种情况改用 concat 滤镜,后面会讲。


三、方法二:批处理脚本一键合并

每次都要手动敲命令太麻烦了。我写了一个现成的 bat 脚本,保存后双击就能用:

bat 复制代码
@echo off
chcp 65001 >nul
title TS文件合并工具
setlocal enabledelayedexpansion

echo ========================================
echo          TS 文件合并工具 v1.0
echo ========================================
echo.

:: 检查 FFmpeg
where ffmpeg >nul 2>&1
if %errorlevel% neq 0 (
    echo [错误] 未检测到 FFmpeg,请先安装并配置环境变量!
    echo 下载地址: https://ffmpeg.org/download.html
    pause
    exit /b 1
)

:: 扫描 TS 文件
set count=0
for %%i in (*.ts) do set /a count+=1

if %count%==0 (
    echo [提示] 当前目录没有找到 .ts 文件,请把脚本放到 TS 文件目录下运行。
    pause
    exit /b 1
)

echo [信息] 发现 %count% 个 TS 文件,开始生成列表...

:: 生成文件列表
set listfile=%TEMP%\ts_merge_list_%RANDOM%.txt
(for %%i in (*.ts) do (
    echo file '%%~nxi'
)) > "%listfile%"

:: 让用户输入输出文件名
set /p outname="请输入输出文件名(默认 merged.mp4):"
if "!outname!"=="" set outname=merged.mp4

echo.
echo [执行] 开始合并,请勿关闭窗口...
ffmpeg -f concat -safe 0 -i "%listfile%" -c copy "!outname!"

:: 清理临时文件
del "%listfile%" 2>nul

if exist "!outname!" (
    echo.
    echo ========================================
    echo  合并完成!输出文件:!outname!
    echo ========================================
) else (
    echo.
    echo [失败] 合并未成功,请检查上面的错误信息。
)

pause

使用方法:

  1. 把上面代码复制到记事本,另存为 merge_ts.bat(编码选 ANSI 或 UTF-8 with BOM)
  2. merge_ts.bat 丢到你的 TS 文件所在的文件夹
  3. 双击运行,按提示输入输出文件名(直接回车默认 merged.mp4
  4. 等待完成

四、方法三:Python 批量合并多文件夹

如果你下载了很多视频,每个都在单独的文件夹里,一个文件夹一个文件夹地跑脚本就太慢了。下面这个 Python 脚本可以一键遍历所有子文件夹,自动合并每个文件夹里的 TS 文件:

python 复制代码
"""
TS文件批量合并工具
功能:遍历指定目录下的所有子文件夹,自动合并每个文件夹内的 TS 文件
依赖:Python 3.7+, FFmpeg
"""

import os
import re
import subprocess
from pathlib import Path


def natural_sort_key(name: str) -> list:
    """
    自然排序:让 1.ts, 2.ts, ..., 10.ts 按数字大小而非字典序排列
    """
    return [int(s) if s.isdigit() else s.lower() for s in re.split(r'(\d+)', name)]


def merge_ts_in_folder(folder: str, output_name: str = "merged.mp4") -> bool:
    """
    合并单个文件夹内的所有 TS 文件
    """
    folder = Path(folder)
    ts_files = sorted(
        [f.name for f in folder.glob("*.ts")],
        key=natural_sort_key
    )

    if not ts_files:
        return False

    total = len(ts_files)
    print(f"\n[{folder.name}] 发现 {total} 个 TS 文件,开始合并...")

    # 写入文件列表
    list_file = folder / "_ffmpeg_list.txt"
    with open(list_file, "w", encoding="utf-8") as f:
        for name in ts_files:
            f.write(f"file '{name}'\n")

    # 执行 FFmpeg
    output_path = folder / output_name
    cmd = [
        "ffmpeg", "-y",
        "-f", "concat", "-safe", "0",
        "-i", str(list_file),
        "-c", "copy",
        str(output_path)
    ]

    result = subprocess.run(cmd, capture_output=True, text=True)

    # 清理临时文件
    list_file.unlink(missing_ok=True)

    if result.returncode != 0:
        # 取出最后几行错误信息
        err = result.stderr.strip().split("\n")[-3:]
        print(f"  [失败] {' | '.join(err)}")
        return False

    size_mb = output_path.stat().st_size / (1024 * 1024)
    print(f"  [完成] {output_name} ({size_mb:.1f} MB)")
    return True


def batch_merge(base_dir: str, recursive: bool = True):
    """
    批量合并 base_dir 下所有子文件夹的 TS 文件
    """
    base = Path(base_dir)
    if not base.exists():
        print(f"错误:目录不存在 -> {base_dir}")
        return

    if recursive:
        subdirs = [str(d) for d in base.iterdir() if d.is_dir()]
    else:
        subdirs = [str(base)]

    if not subdirs:
        print("未找到子文件夹。")
        return

    success = 0
    fail = 0
    skip = 0

    for sub in subdirs:
        has_ts = any(Path(sub).glob("*.ts"))
        if not has_ts:
            skip += 1
            continue

        ok = merge_ts_in_folder(sub)
        if ok:
            success += 1
        else:
            fail += 1

    print(f"\n{'='*50}")
    print(f"处理完毕:成功 {success} / 失败 {fail} / 跳过 {skip}")
    print(f"{'='*50}")


if __name__ == "__main__":
    # 把这里改成你自己的目录
    TARGET_DIR = r"D:\Downloads\videos"
    batch_merge(TARGET_DIR, recursive=True)

使用说明:

  1. 把脚本最后一行 TARGET_DIR 改成你的视频下载目录
  2. 运行前确保 FFmpeg 在 PATH 中
  3. 默认会递归处理所有子文件夹,不想递归的话把 recursive=True 改成 False

如果你的 TS 文件是跟着 M3U8 一起下载的,可以直接解析 M3U8 获取文件名和顺序,比用 glob 更靠谱。代码就不展开了,需求多的话评论区说一下,我再补一版。


五、常见报错与解决

报错1:Unsafe file name

bash 复制代码
[concat @ 000001a2b3c4d5e0] Unsafe file name 'C:\videos\segment_000.ts'
filelist.txt: Operation not permitted

原因-safe 0 参数没加,或者路径里有特殊字符。

解决 :确保命令里有 -safe 0。如果还不行,把 filelist.txt 里的路径改成相对路径。


报错2:Stream mapping 后直接退出

bash 复制代码
Stream mapping:
  Stream #0:0 -> #0:0 (copy)
  Stream #0:1 -> #0:1 (copy)
Press [q] to stop, [?] for help
[mp4 @ 000001...] Could not find tag for codec none in stream #0

原因:TS 文件的某一路流(通常是数据流或字幕流)无法写入 MP4。

解决:只复制视频和音频流,忽略其他流:

bash 复制代码
ffmpeg -f concat -safe 0 -i filelist.txt -c copy -map 0:v -map 0:a output.mp4

报错3:合并后视频正常但没声音

原因:MP4 容器不支持源文件的音频编码格式(常见于 AC3、DTS、PCM_bluray)。

解决A(推荐):改用 MKV 容器,兼容性更好:

bash 复制代码
ffmpeg -f concat -safe 0 -i filelist.txt -c copy output.mkv

解决B:音频转码为 AAC,视频保持原样:

bash 复制代码
ffmpeg -f concat -safe 0 -i filelist.txt -c:v copy -c:a aac -b:a 192k output.mp4

报错4:Packet lossInvalid data found

原因:某个 TS 片段在下载/录制时损坏了。

解决

  1. 先找到是哪个文件有问题------打开 filelist.txt,一个一个注释掉,看合并到哪个报错。
  2. 或者用 FFmpeg 单独测试每个文件:
bash 复制代码
for %i in (*.ts) do @(ffmpeg -v error -i "%i" -f null - 2>&1 | findstr /i error && echo [损坏] %i)

找到损坏文件后,看体积------如果特别小(几百字节),大概率是空文件,直接删掉;如果体积正常但损坏,用下面命令试着修复:

bash 复制代码
ffmpeg -i broken.ts -c copy fixed.ts

报错5:文件顺序乱了

现象:合并出来的视频播放到中间跳到别的画面,然后又跳回来。

原因 :PowerShell 的 Sort-Object 或文件系统的默认排序是字典序,1.ts → 10.ts → 100.ts → 2.ts

解决 :用自然排序重新生成列表。要么用上面 Python 脚本里的 natural_sort_key,要么手动在 filelist.txt 里调整顺序。如果你有 M3U8 索引文件,可以直接从 M3U8 里提取文件顺序。


报错6:音画不同步

现象:合并完后口型和声音对不上,而且越到后面越明显。

原因:源 TS 文件的时间戳(PTS/DTS)有问题,concat 时没有重新生成。

解决 :加 -fflags +genpts 强制正时视频时间戳:

bash 复制代码
ffmpeg -fflags +genpts -f concat -safe 0 -i filelist.txt -c copy output.mp4

如果还不行,只能走重新编码路线了:

bash 复制代码
ffmpeg -f concat -safe 0 -i filelist.txt -c:v libx264 -crf 18 -c:a aac -b:a 192k output.mp4

六、什么时候必须重新编码?

以下场景不建议用 -c copy,老老实实走编码路线:

  • TS 片段来自不同相机/设备(编码参数不一致)
  • 需要剪辑/裁剪/加水印
  • 输出格式与源编码不兼容(比如源是 HEVC 但播放器只支持 H.264)
  • concat demuxer 死活报错且找不到原因
bash 复制代码
# 重新编码合并(画质优先)
ffmpeg -f concat -safe 0 -i filelist.txt -c:v libx264 -crf 18 -preset medium -c:a aac -b:a 192k output.mp4
  • -crf 18:画质优先,数字越小画质越好(0=无损,18=视觉无损,28=平衡)
  • -preset medium:编码速度,fast 快但文件大,slow 文件小但慢

七、小结

场景 推荐方案
临时合并几个 TS,文件参数一致 方法一(concat 协议 + -c copy
不想装任何软件,文件不大 ezwebtools 在线工具(浏览器本地处理,隐私安全)
经常需要合并,想省事 方法二(bat 脚本,双击即用)
批量处理几十个文件夹 方法三(Python 脚本)
合并后有问题(没声音/不同步) 查第五章对应报错
源文件参数不一致 重新编码(第六章)

个人建议:如果你只是偶尔合并几个 TS 文件,文件也不大(几百 MB),直接打开 ezwebtools 的 TS 合并工具,拖进去点合并就完事了,浏览器本地跑 FFmpeg,不经过服务器,画质无损还不用担心隐私问题。

如果需要频繁处理、或者文件太大(几个 GB),花 10 分钟把 FFmpeg 装好,把 -c copy 这条命令记住。看起来是命令行,但比你在网上到处找所谓"合并软件"省的时间多得多------关键是画质一点不丢。

有问题欢迎在评论区留言,我看到都会回。


原创文章,转载请注明出处。欢迎关注,不定期更新实用工具教程。