FFmpeg 工具介绍及 M4S 转 MP4 批量处理脚本解析

FFmpeg 工具介绍及 M4S 转 MP4 批量处理脚本解析

一、FFmpeg 工具概述

FFmpeg 是一款跨平台的开源音视频处理工具集,被广泛应用于音视频的编解码、格式转换、剪辑、合并、流媒体处理等场景。它支持几乎所有主流的音视频格式,具备高性能、轻量级、灵活性强的特点,是音视频处理领域的工业级标准工具。

核心优势:

  • 格式兼容性极强:支持 MP4、FLV、M4S、MKV、AAC、MP3 等数百种音视频格式的转换与处理;
  • 无损处理能力:支持直接复制音视频流(-c copy),无需重新编码,处理速度快且无画质 / 音质损失;
  • 命令行灵活可控:通过参数组合可实现定制化的音视频处理逻辑,适配不同场景需求;
  • 跨平台支持:可运行在 Windows、macOS、Linux 等主流操作系统。

在本脚本中,FFmpeg 主要用于将 B 站下载的分离式 M4S 音频 / 视频文件合并为通用的 MP4 格式,核心利用其 “流复制” 特性实现无损转换。

二、M4S 转 MP4 批量处理脚本模块解析

该脚本专为解决 B 站下载的 M4S 格式文件(视频 video.m4s + 音频 audio.m4s 分离存储)批量转换为 MP4 格式设计,整体遵循模块化、高容错、易用性的设计原则,各模块功能及实现逻辑如下:

1. 环境检查模块:check_ffmpeg_installed

def check_ffmpeg_installed() -> bool:
    """检查ffmpeg是否已安装并配置到环境变量"""
    try:
        subprocess.run(
            ["ffmpeg", "-version"],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            check=True
        )
        return True
    except (subprocess.CalledProcessError, FileNotFoundError):
        return False
  • 功能:校验系统是否已安装 FFmpeg 且配置到环境变量(可全局调用);
  • 实现逻辑:通过执行 ffmpeg -version 命令,捕获 “命令不存在”(FileNotFoundError)或 “执行失败”(CalledProcessError)异常,返回布尔值;
  • 核心作用:作为脚本执行的前置校验,避免后续因 FFmpeg 缺失导致批量转换失败。

2. 标题解析模块:get_video_title

def get_video_title(cache_dir: Path) -> str:
    """从entry.json读取视频标题,读取失败则返回文件夹名"""
    entry_json = cache_dir / "entry.json"
    default_title = cache_dir.name

    if not entry_json.exists():
        return default_title

    try:
        with open(entry_json, "r", encoding="utf-8") as f:
            data = json.load(f)
            title = data.get("title", default_title)
            # 清理文件名非法字符
            illegal_chars = ['/', '\\', ':', '*', '?', '"', '<', '>', '|']
            for char in illegal_chars:
                title = title.replace(char, "_")
            return title
    except Exception as e:
        print(f"⚠️ 读取{cache_dir}标题失败:{str(e)},使用默认标题")
        return default_title
  • 功能:从 B 站下载目录的 entry.json 文件中读取视频原始标题,作为输出 MP4 文件的命名依据;
  • 核心逻辑
    1. 优先读取 entry.json 中的 title 字段;
    2. 若文件不存在 / 读取失败,降级使用文件夹名作为默认标题;
    3. 自动清理标题中的系统非法字符(如 /:* 等),避免文件命名报错;
  • 容错设计:捕获所有读取异常,保证脚本不中断,提升鲁棒性。

3. 文件查找模块:find_m4s_files

def find_m4s_files(target_dir: Path) -> List[Tuple[Path, Path]]:
    """
    递归查找目标目录下的m4s文件对(video.m4s + audio.m4s)
    返回格式:[(video_file, audio_file), ...]
    """
    m4s_pairs = []

    # 查找所有video.m4s文件
    video_files = list(target_dir.rglob("video.m4s"))
    for video_file in video_files:
        # 查找同目录下的audio.m4s
        audio_file = video_file.parent / "audio.m4s"
        if audio_file.exists():
            m4s_pairs.append((video_file, audio_file))
        else:
            print(f"⚠️ {video_file.parent} 目录下未找到audio.m4s,跳过")

    if not m4s_pairs:
        print(f"❌ 在{target_dir}中未找到有效的video.m4s+audio.m4s文件对")
    return m4s_pairs
  • 功能:递归扫描指定目录,找出所有成对存在的 video.m4s 和 audio.m4s 文件;
  • 实现逻辑
    1. 通过 rglob 递归查找所有 video.m4s 文件;
    2. 逐个校验同目录下是否存在 audio.m4s,仅保留成对文件;
    3. 对缺失音频文件的情况给出提示,对无有效文件对的场景明确报错;
  • 返回值:元组列表 [(视频文件路径, 音频文件路径), ...],为后续合并提供数据源。

4. 格式转换模块:merge_m4s_to_mp4

def merge_m4s_to_mp4(video_file: Path, audio_file: Path, output_mp4: Path) -> None:
    """
    合并m4s文件为mp4(无损转换)
    :param video_file: video.m4s路径
    :param audio_file: audio.m4s路径
    :param output_mp4: 输出mp4路径
    """
    # ffmpeg命令:直接复制音视频流,无需重新编码
    cmd = [
        "ffmpeg",
        "-i", str(video_file),  # 输入视频流
        "-i", str(audio_file),  # 输入音频流
        "-c:v", "copy",  # 复制视频编码(无损)
        "-c:a", "copy",  # 复制音频编码(无损)
        "-strict", "experimental",  # 兼容B站特殊音频格式
        "-y",  # 覆盖已存在的文件
        "-loglevel", "error",  # 只输出错误信息,减少冗余
        str(output_mp4)
    ]

    try:
        subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        print(f"✅ 成功转换:{output_mp4.name}")
    except subprocess.CalledProcessError as e:
        # 若无损转换失败,尝试重新编码音频(兼容模式)
        print(f"⚠️ 无损转换失败,尝试兼容模式:{str(e.stderr[:200])}")
        compat_cmd = [
            "ffmpeg",
            "-i", str(video_file),
            "-i", str(audio_file),
            "-c:v", "copy",  # 视频仍无损
            "-c:a", "aac",  # 音频重新编码为AAC
            "-y",
            "-loglevel", "error",
            str(output_mp4)
        ]
        subprocess.run(compat_cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        print(f"✅ 兼容模式转换成功:{output_mp4.name}")
  • 功能:核心转换逻辑,调用 FFmpeg 合并 M4S 文件为 MP4;
  • 核心参数说明
    • -c:v copy/-c:a copy:直接复制音视频流,无重新编码,实现无损、高速转换;
    • -strict experimental:兼容 B 站部分特殊编码的音频格式;
    • -y:自动覆盖已存在的输出文件,无需手动确认;
    • -loglevel error:仅输出错误信息,简化控制台输出;
  • 容错设计
    1. 优先尝试无损转换;
    2. 若失败(如音频编码不兼容),自动降级为 “视频无损 + 音频重新编码为 AAC” 的兼容模式,保证转换成功率。

5. 批量执行模块:batch_convert_m4s

def batch_convert_m4s(input_dir: str, output_dir: str = "./bilibili_mp4_output") -> None:
    """
    批量转换m4s文件为mp4
    :param input_dir: 包含m4s文件的目录(单个文件夹路径)
    :param output_dir: 输出mp4的目录(默认当前目录下bilibili_mp4_output)
    """
    # 前置检查
    if not check_ffmpeg_installed():
        raise RuntimeError("❌ 未找到ffmpeg,请先安装并配置到环境变量")

    input_path = Path(input_dir)
    output_path = Path(output_dir)

    # 校验输入目录
    if not input_path.exists():
        raise FileNotFoundError(f"❌ 输入目录不存在:{input_dir}")

    # 创建输出目录
    output_path.mkdir(exist_ok=True)
    print(f"📁 输出目录:{output_path.absolute()}")

    # 查找m4s文件对
    m4s_pairs = find_m4s_files(input_path)
    if not m4s_pairs:
        return

    # 逐对转换
    total = len(m4s_pairs)
    print(f"\n🚀 共找到{total}个可转换的视频文件对,开始转换...")

    for idx, (video_file, audio_file) in enumerate(m4s_pairs, 1):
        # 获取视频标题(取最近的上级目录作为缓存目录)
        cache_dir = video_file.parent
        while not (cache_dir / "entry.json").exists() and cache_dir != input_path:
            cache_dir = cache_dir.parent

        title = get_video_title(cache_dir)
        # 避免重复文件名,添加序号
        output_mp4 = output_path / f"{title}_{idx if total > 1 else ''}.mp4"

        print(f"\n[{idx}/{total}] 处理:{title}")
        merge_m4s_to_mp4(video_file, audio_file, output_mp4)

    print(f"\n🎉 转换完成!所有文件已保存至:{output_path.absolute()}")
  • 功能:脚本入口模块,整合所有子功能实现批量转换;
  • 执行流程
    1. 前置校验:检查 FFmpeg 安装状态、输入目录是否存在;
    2. 初始化:创建输出目录(不存在则自动创建);
    3. 数据源准备:调用 find_m4s_files 获取有效 M4S 文件对;
    4. 循环处理:逐个调用 merge_m4s_to_mp4 转换文件,自动处理文件名(多文件时添加序号避免重复);
    5. 结果反馈:输出转换进度和最终结果路径。

6. 脚本入口

if __name__ == "__main__":
    # ===================== 配置区(修改这里)=====================
    INPUT_DIRECTORY = "/Users/用户/Movies/"  # 你的m4s文件所在目录
    OUTPUT_DIRECTORY = "/Users/用户/Movies/"  # 输出MP4的目录
    # ============================================================

    try:
        batch_convert_m4s(INPUT_DIRECTORY, OUTPUT_DIRECTORY)
    except Exception as e:
        print(f"\n❌ 程序执行失败:{str(e)}")
  • 功能:脚本直接运行的入口,用户只需修改 INPUT_DIRECTORY 和 OUTPUT_DIRECTORY 两个配置项即可使用;
  • 异常处理:捕获所有未预期的异常并输出错误信息,避免脚本崩溃。

三、脚本使用说明

1. 前置条件

  • 安装 FFmpeg 并配置到系统环境变量(可通过 ffmpeg -version 验证);
  • Python 3.8+ 环境(支持 Path 类的完整特性)。

2. 使用步骤

  1. 修改脚本中 INPUT_DIRECTORY 为你的 M4S 文件所在目录;
  2. 修改 OUTPUT_DIRECTORY 为 MP4 文件输出目录(可选,默认生成 bilibili_mp4_output 文件夹);
  3. 运行脚本:python 脚本名.py

3. 注意事项

  • 脚本会递归扫描输入目录下所有子文件夹的 M4S 文件,无需手动指定子目录;
  • 输出文件名优先使用 entry.json 中的视频标题,无该文件时使用文件夹名;
  • 多文件转换时会自动添加序号(如 视频标题_1.mp4视频标题_2.mp4),避免文件名重复。

总结

  1. 该脚本基于 FFmpeg 的 “流复制” 特性实现 M4S 到 MP4 的无损批量转换,核心优势是速度快、画质无损失;
  2. 脚本采用模块化设计,包含环境检查、标题解析、文件查找、格式转换、批量执行五大核心模块,容错性强(支持无损 / 兼容两种转换模式);
  3. 使用门槛低,用户仅需配置输入 / 输出目录即可完成批量转换,适配 B 站下载的 M4S 文件场景。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值