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 文件的命名依据; - 核心逻辑:
- 优先读取
entry.json中的title字段; - 若文件不存在 / 读取失败,降级使用文件夹名作为默认标题;
- 自动清理标题中的系统非法字符(如
/、:、*等),避免文件命名报错;
- 优先读取
- 容错设计:捕获所有读取异常,保证脚本不中断,提升鲁棒性。
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文件; - 实现逻辑:
- 通过
rglob递归查找所有video.m4s文件; - 逐个校验同目录下是否存在
audio.m4s,仅保留成对文件; - 对缺失音频文件的情况给出提示,对无有效文件对的场景明确报错;
- 通过
- 返回值:元组列表
[(视频文件路径, 音频文件路径), ...],为后续合并提供数据源。
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:仅输出错误信息,简化控制台输出;
- 容错设计:
- 优先尝试无损转换;
- 若失败(如音频编码不兼容),自动降级为 “视频无损 + 音频重新编码为 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()}")
- 功能:脚本入口模块,整合所有子功能实现批量转换;
- 执行流程:
- 前置校验:检查 FFmpeg 安装状态、输入目录是否存在;
- 初始化:创建输出目录(不存在则自动创建);
- 数据源准备:调用
find_m4s_files获取有效 M4S 文件对; - 循环处理:逐个调用
merge_m4s_to_mp4转换文件,自动处理文件名(多文件时添加序号避免重复); - 结果反馈:输出转换进度和最终结果路径。
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. 使用步骤
- 修改脚本中
INPUT_DIRECTORY为你的 M4S 文件所在目录; - 修改
OUTPUT_DIRECTORY为 MP4 文件输出目录(可选,默认生成bilibili_mp4_output文件夹); - 运行脚本:
python 脚本名.py。
3. 注意事项
- 脚本会递归扫描输入目录下所有子文件夹的 M4S 文件,无需手动指定子目录;
- 输出文件名优先使用
entry.json中的视频标题,无该文件时使用文件夹名; - 多文件转换时会自动添加序号(如
视频标题_1.mp4、视频标题_2.mp4),避免文件名重复。
总结
- 该脚本基于 FFmpeg 的 “流复制” 特性实现 M4S 到 MP4 的无损批量转换,核心优势是速度快、画质无损失;
- 脚本采用模块化设计,包含环境检查、标题解析、文件查找、格式转换、批量执行五大核心模块,容错性强(支持无损 / 兼容两种转换模式);
- 使用门槛低,用户仅需配置输入 / 输出目录即可完成批量转换,适配 B 站下载的 M4S 文件场景。

685

被折叠的 条评论
为什么被折叠?



