尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Python提取视频音频:ffmpeg-python实操指南与批量处理技巧

Python提取视频音频:ffmpeg-python实操指南与批量处理技巧 今天2026年3月18日正好有人问我怎么用Python把视频里的音频单独抠出来我索性把这两年做视频处理项目时积累的经验整理成一篇完整的实操记录。这个需求其实比很多人想象中更常见剪映、PR、Final Cut用户想提取BGM自媒体运营要批量采集短视频平台的音频做素材库做语音识别、音频分析的人需要先统一拿到纯净的音频文件甚至有人只是想给本地缓存的视频换一个更小的音频格式方便备份。不管哪种场景用Python来做这件事都是最快、最灵活、最容易自动化的方案。这篇文章我会从最底层的原理讲起再做工具选型最后给完整的可运行代码和一批我实际踩过的坑。目标是让你看完之后不仅能跑通单条视频的音频提取还能直接扩展出批量处理、片段截取、音量调整、静音剔除这些常用功能。1. 视频文件里其实住着两条“数据流”提取音频等于拆墙很多人第一次接触视频转音频脑子里想的是“从视频画面里把声音抠出来”这个直觉会误导你。实际上绝大多数视频文件MP4、MKV、MOV、AVI、TS等内部采用的是容器格式容器里面封装了至少两条独立的数据流一条视频流一条音频流。它们只是被容器“包”在同一个外壳里面彼此之间是平行关系不是包含关系。1.1 容器、视频流、音频流三者的关系拿最常见的MP4文件举例。一个完整MP4文件的结构可以理解成一个快递盒盒子本身是容器里面放了两个独立的包裹一个装画面H.264/H.265编码的视频流一个装声音AAC/MP3/AC-3编码的音频流。你平时播放视频时播放器做的事情是同时拆开这两个包裹再按时间轴把它们同步起来播放。所以“提取音频”本质上不是“从画面里抽声音”而是“把容器拆开把音频流单独拿出来保存成另一个容器”。这也是为什么这项技术叫“demux”demultiplexing而不叫“抽出”。理解了这一点你就能明白两个非常重要的事如果只做解封装demux音频编码格式不会变视频里的音频原本是AAC提取出来就还是AAC原本是MP3提取出来还是MP3。如果你想把音频转成别的格式比如从AAC转成MP3那还需要额外做一次音频转码transcoding这是另一个步骤。1.2 为什么“格式不变”这件事很有用很多人提取音频是为了拿到适合剪辑或播放的格式但如果你暂时不需要转换格式比如视频里的音频本来就是AAC你只想把它单独存成一个M4A文件那就可以选择“流复制”stream copy模式。这个模式下程序不会重新解码再编码音频只是把原有音频流“原封不动”地搬进新文件速度快得惊人几乎不损耗任何音质CPU占用也低。反过来如果你需要把音频转成MP3等格式就必须经过解码再编码的过程速度会慢很多而且理论上会有音质损耗。这个选择后续代码里会具体体现现在你只需要记住先弄清“解封装”和“转码”的区别后面就不会懵。1.3 三个核心需求对应三种动手思路结合实操场景大部分人的需求可以分成三类只需要音频文件和原视频的音质保持一致不在乎格式直接走流复制解封装速度快。需要把音频统一成MP3或WAV等指定格式方便剪辑软件或语音识别工具使用走解码重编码。需要截取片段、批量处理、自动检测静音等在这些基础操作之上叠加ffmpeg的参数组合。后面所有代码都是围绕这三类需求展开的。2. 工具选型为什么我最终选了ffmpeg-python而不是moviepyPython处理音频提取市面上的主流方案大致有四条路直接调用系统里的FFmpeg、用ffmpeg-python、用moviepy、用PyAV。我先给出横向对比再说我的选型逻辑。2.1 四款方案的横向对比方案本质学习成本功能完整度适合场景原生FFmpeg命令行直接调用系统安装的FFmpeg程序中等主要是记参数极高几乎所有音视频操作全能做任何场景尤其是追求极致可控的操作ffmpeg-python对FFmpeg命令行的Python封装用来拼接参数低接口直观极高底层还是FFmpeg需要写Python脚本、做自动化时首选moviepy纯Python的音视频处理库内部也调FFmpeg低API友好较高但遇到复杂滤镜和参数时封装不够快速做简单处理比如只截个片段PyAVPython绑定FFmpeg底层库可以直接操作数据帧高需要理解编解码原理极高甚至可以逐帧处理需要处理原始帧、做自定义算法时使用2.2 我的选型理由一半为了自动化一半为了省心我在实际项目里主要用ffmpeg-python。原因有三个第一你写任何Python脚本最终目的通常不是只跑一次而是要复用、要批量、要纳入更大的处理流程。ffmpeg-python的接口设计非常像在写“参数说明书”代码可读性比一串长长的命令行参数好太多。比如“取视频里的音频并按流复制导出”在命令行里写起来是ffmpeg -i input.mp4 -vn -acodec copy output.m4a用ffmpeg-python写则是import ffmpeg ffmpeg.input(input.mp4).output(output.m4a, vnNone, acodeccopy).run()你能清晰看到每个参数的含义后期要加参数在output里加关键字就行。第二ffmpeg-python底层调用的仍然是FFmpeg所以功能上限完全取决于FFmpeg不会像moviepy那样在某些冷门参数上被“拦一道”。我有一段时间用moviepy做批量截取结果发现它在设置音频比特率时会自动忽略某些值排查了很长时间最后转到ffmpeg-python才解决了。所以如果处理流程复杂与其在封装层反复试探不如直接离底层更近一步。第三Python生态的周边工具丰富我可以把提取音频和后续的语音识别、音频分析、文件管理串在同一个脚本里全程不需要切换到其他语言。2.3 为什么不建议只用PyAVPyAV很强适合做逐帧分析、音视频数据级处理。但如果你只是想把音频提取出来用PyAV属于“杀鸡用牛刀”——你不仅要自己管理解码上下文、帧循环、编码器还要手动处理音频重采样等细节。这些技术对于做音视频算法的人说很值得学但对大多数做内容处理的人来说复杂度和收益不成正比。简单一句话日常项目提取音频ffmpeg-python的性价比最高。3. 环境准备主角是Python幕后英雄是FFmpeg用ffmpeg-python之前你必须先让系统里有一个可用的FFmpeg程序。这一步非常关键因为ffmpeg-python本身不做音视频处理它只负责拼参数、启动FFmpeg进程、收集输出。很多人装上ffmpeg-python就急着跑代码结果报错原因就在FFmpeg没装或者没有被正确识别。3.1 FFmpeg的安装方式Windows从FFmpeg官网或gyan.dev下载Windows构建版本。解压后找到bin目录里面有ffmpeg.exe、ffprobe.exe、ffplay.exe。把bin目录路径加入系统的Path环境变量。保存后重新打开命令行工具让环境变量生效。macOSbrew install ffmpegLinuxDebian/Ubuntu系sudo apt update sudo apt install ffmpegCentOS/RHEL这类系统如果默认源里没有FFmpeg可以考虑用RPM Fusion源或者直接编译安装。不过说实话日常做Python音频处理用Ubuntu/Debian系会省心很多。3.2 验证安装一行命令确认可用装完之后在终端执行ffmpeg -version能看到版本号就是安装成功。再测试一个简单的转码ffmpeg -i test.mp4 -vn test.mp3如果这条命令能正常生成MP3文件说明FFmpeg本体没问题。3.3 Python侧依赖与版本建议接下来安装Python相关的库pip install ffmpeg-python注意ffmpeg-python不是moviepy那种“全家桶”它只负责封装所以依赖非常少。除此之外我建议你在做后续进阶功能时顺手装上pip install pydub pip install tqdmpydub是对音频处理的一个高层封装它在静音检测、音频切片、音量调整这些任务上非常好用底层也是调用FFmpeg。tqdm用来显示批量处理进度条。Python版本方面我用的3.10、3.11、3.12都完全没问题最低建议3.8以上避免语法兼容性问题。3.4 FFmpeg没有被Python识别时怎么办这是最常见的坑。ffmpeg-python默认去系统Path里找ffmpeg命令。如果你用的是Windows并且FFmpeg是绿色解压版但没配Path就会在运行时报类似“FileNotFoundError: [WinError 2] 系统找不到指定的文件”的错误。解决方式有两种第一种老老实实配好Path环境变量然后重开终端再跑脚本。第二种如果因为某种原因实在改不了系统的环境变量可以在代码里手动指定FFmpeg路径ffmpeg /path/to/ffmpeg ffprobe /path/to/ffprobe ffmpeg.input(input.mp4).output( output.mp3, formatmp3 ).run(cmdffmpeg)但注意run里只能指定一个cmd参数如果你同时需要ffprobe能力最好还是把Path配好。这算是我被坑过一次之后总结出的经验能配环境变量就不要硬编码路径硬编码路径的代码换个机器就废了。4. 第一个可运行版本完整代码与参数选择详解下面进入正题我给出一个能直接跑通的完整提取代码。这个版本支持两种模式一种是流复制适合快速提取另一种是转码适合转换成MP3等指定格式。4.1 最简版本直接提取并转成MP3import ffmpeg input_file video.mp4 output_file audio.mp3 ( ffmpeg .input(input_file) .output(output_file, formatmp3, audio_bitrate192k) .run(overwrite_outputTrue) )这段代码做的是读取video.mp4把里面的音频流解码并重新编码成MP3码率设为192k输出到audio.mp3如果文件已存在则覆盖。4.2 流复制版本速度更快、音质无损import ffmpeg input_file video.mp4 output_file audio.m4a ( ffmpeg .input(input_file) .output(output_file, vnNone, acodeccopy) .run(overwrite_outputTrue) )这个版本与前面最大的区别是acodeccopy。它告诉FFmpeg音频部分不要重新编码直接复制原来编码好的数据。因为这个过程不涉及解码和重编码速度非常快一个2小时的视频往往几秒就完成了而且音质完全无损。同时我加了vnNone表示不要视频流。有的版本里你也可以写成-vn在ffmpeg-python里对应的是vnNone。4.3 逐行拆解几个关键参数input()指定输入文件。可以只传文件名也可以附带一些输入级的参数比如指定从哪里开始读取。output()指定输出文件和输出参数。这里的参数全都会映射成FFmpeg的命令行参数。比如formatmp3就是-f mp3audio_bitrate192k就是-b:a 192kvnNone就是-vn。run()真正执行任务。overwrite_outputTrue自动覆盖输出文件如果不加这个参数目标文件存在时FFmpeg会停下并询问在Python里面这种交互式询问很容易卡死进程。所以自动化脚本里我建议一律设置这个参数。4.4 流复制与重编码到底怎么选我把这个选择逻辑做成了一张决策表方便你直接对照场景原视频音频编码目标格式推荐方式速度优先本地备份AACm4a流复制acodec copy需要导入老式剪辑软件AAC/其他mp3转码需要给语音识别工具用任意wav转码为PCM WAV需要极致音质的长期存档任意flac转码无损压缩原视频音频已是MP3MP3mp3流复制也可以但新容器不同注意copy模式并不是万能的。如果原视频里的音频编码格式非常冷门或者输出容器的规范不支持原编码FFmpeg会直接报错。比如你拿一个音频流是AC-3的视频直接acodeccopy输出到MP3容器很可能失败因为MP3容器一般不支持AC-3音频流。这种情况就必须转码。4.5 输出格式的选择逻辑MP3通用性最强几乎所有软件都认但存在有损压缩。码率建议192k或320k太低会影响听感。M4A/AACMP4时代最常见的音频编码苹果生态和安卓生态都支持体积和音质平衡好。若原视频音频是AAC直接流复制到M4A是最省事的做法。WAV无损但有损体积大适用于语音识别、音频分析、专业剪辑。FLAC无损压缩适合音乐存档文件比WAV小很多但部分旧软件不认。如果你不想纠结我的默认建议是用于日常听提取MP3 192k够用用于剪辑或识别提取WAV最保险用于存档FLAC更合适。5. 进阶玩法批量提取、片段截取、音量调整与静音检测提取单个音频只是入门实际工作中我们往往需要批量处理很多视频。这一节我把几个高频场景的代码一次性给全每段代码都经过我实际运行验证你可以直接抄。5.1 批量提取整个目录下的视频音频假设你有一个文件夹里面放了100个视频你需要把每个视频的音频提取成同名MP3可以用glob遍历import ffmpeg import glob from pathlib import Path video_dir ./videos output_dir ./audios Path(output_dir).mkdir(exist_okTrue) video_exts [*.mp4, *.mkv, *.mov, *.avi, *.flv, *.ts] video_files [] for ext in video_exts: video_files.extend(glob.glob(str(Path(video_dir) / ext))) for video_file in video_files: stem Path(video_file).stem out_file Path(output_dir) / f{stem}.mp3 try: ( ffmpeg .input(video_file) .output(str(out_file), formatmp3, audio_bitrate192k) .run(overwrite_outputTrue, quietTrue) ) print(f[OK] {video_file} - {out_file}) except ffmpeg.Error as e: print(f[FAIL] {video_file}: {e.stderr.decode() if e.stderr else e})几个细节值得注意quietTrue让FFmpeg不打印大量日志只保留Python端的print输出批量处理时界面整洁很多。用glob匹配多种格式避免漏掉不同封装格式的视频。每个任务都包了try/except这样单个视频出错不会让整个循环中断。特别是处理大量文件时总会有个别文件编码异常或文件路径有问题错误隔离非常重要。e.stderr.decode()是因为FFmpeg的错误信息通常输出在stderr而且是字节流需要解码成字符串才能看。5.2 只截取某段时间的音频有时候你不需要整个视频的音频只要某一段比如从第10秒到第30秒的内容。这可以用input参数里的ss和t控制import ffmpeg input_file video.mp4 output_file clip.mp3 ( ffmpeg .input(input_file, ss10, t20) .output(output_file, formatmp3) .run(overwrite_outputTrue) )ss10表示从第10秒开始读取t20表示持续20秒也就是截取10到30秒区间。如果希望在更精确的时间点使用毫秒级可以写成ss00:00:10.500这种时间格式。实操中我建议用这种字符串格式因为它是FFmpeg的原生时间格式能精确到毫秒不容易出现浮点数舍入误差。注意这里ss放在input()里是“慢速定位”FFmpeg会先按时间戳精确解码到目标位置精度高但速度较慢也可以把ss放到output()里效果是“快速seek”直接跳到目标位置附近再处理速度快但可能不够精确。对音频提取这类任务建议放在input里优先保证截取点准确。5.3 调整音量单独控制提取出来的人声大小有些视频的音频音量忽大忽小提取后导入剪辑软件还要再调。前端音频项目中常见的“归一化”normalization处理FFmpeg也能直接做import ffmpeg ( ffmpeg .input(video.mp4) .filter(volume, volume1.5) # 音量放大1.5倍 .output(audio_normalized.mp3, formatmp3) .run(overwrite_outputTrue) )除了简单的倍率调整FFmpeg还支持loudnorm滤镜做响度归一化import ffmpeg ( ffmpeg .input(video.mp4) .filter(loudnorm, I-16, TP-1.5, LRA11) .output(audio_loudnorm.mp3, formatmp3) .run(overwrite_outputTrue) )I是平均响度目标TP是真实峰值上限LRA是响度范围。这三个值常用于播客、短视频响度标准。如果你只是做素材预处理用简单的volume滤镜就够了避免引入过多变量。但如果要给视频平台统一响度loudnorm明显更专业。5.4 静音检测与自动分段这个需求来自语音数据集制作。你想把长视频里的语音片段切出来就需要先找音频里的非静音区域。ffmpeg-python里可以用silencedetect滤镜import ffmpeg ( ffmpeg .input(video.mp4) .filter(silencedetect, noise-30dB, duration0.5) .output(pipe:, formatnull) .run(quietTrue) )运行完之后FFmpeg会在stdout里输出silence_start和silence_end之类的信息。但这种方法有一个麻烦ffmpeg-python默认会吞掉日志你需要通过run(capture_stdoutTrue, capture_stderrTrue, quietFalse)来拿到原始输出再处理文本解析。我的建议是如果只想做简单的静音段检测直接命令行会更直观ffmpeg -i video.mp4 -af silencedetectnoise-30dB:duration0.5 -f null -运行后会输出[silencedetect 0x...] silence_start: 12.345 [silencedetect 0x...] silence_end: 14.678 | silence_duration: 2.333然后你再用Python的正则把这些段落解析出来组合成需要截取的片段列表。5.5 从URL直接提取音频除了本地文件FFmpeg也支持从网络URL读取媒体流。比如你想提取一个公开视频链接里的音频可以直接把URL当作输入import ffmpeg url https://example.com/sample.mp4 ( ffmpeg .input(url) .output(remote_audio.mp3, formatmp3) .run(overwrite_outputTrue) )需要注意访问在线视频是否合规、是否需要登录、是否有防盗链直接影响这个操作能不能成功。有的网站会做反爬和防盗链FFmpeg下载时可以用headers参数携带User-Agent等请求头。但在实际项目中建议只处理自己有权限下载的资源或者通过平台官方接口获取原始文件避免因侵权引发问题。6. 我实测踩过的坑路径、编码、内存一个比一个隐蔽这一节分享的全部是真实发生在我项目里的问题。如果你之后也做批量视频处理大概率会碰到其中几个。6.1 FileNotFoundError: ffmpeg 未找到文章前面提过ffmpeg-python只是一个封装器它在运行时会在系统PATH里寻找ffmpeg可执行文件。在Windows上如果没配置好环境变量报错信息中出现“WinError 2”基本就是这个问题。解决方法是安装并配置FFmpeg的Path或者在代码里显式传入ffmpeg路径。但还有一种更隐蔽的情况你配置了Path但当前Python进程的环境变量是旧的。特别是你从PyCharm、Jupyter里运行脚本时IDE可能沿用启动时读到的环境变量而不是最新修改的。所以每次修改完系统Path记得重启IDE或终端再试。6.2 中文路径或文件名导致“No such file or directory”有一次我处理一批从网上下载的视频文件名带着空格和中文结果一半任务报“No such file or directory”。奇怪的是文件明明存在。排查了很久最后确认是Windows下路径中的中文字符编码问题早期版本的FFmpeg对UTF-8路径支持不稳定导致路径解析失败。我的解决办法是避免在路径里使用中文和特殊字符批量处理前先统一重命名文件from pathlib import Path video_file Path(old_path) new_path video_file.parent / fvideo_{idx}.mp4 video_file.rename(new_path)如果你不想改源码路径另一种方式是在代码里把工作目录切到文件所在目录再使用相对路径。相对路径通常比绝对路径更少触发编码问题。6.3 输出MP3失败编码器不支持FFmpeg内置的MP3编码器是libmp3lame。在某些精简版FFmpeg里这个编码器可能没有被包含进去。这时你运行转码MP3的命令会报类似“Unable to find a suitable output format for mp3”或“Unknown encoder libmp3lame”。排查方法ffmpeg -encoders | grep mp3如果有libmp3lame说明MP3编码器可用。如果不行有两个选择一是换用功能完整的FFmpeg构建版二是避开MP3使用AAC或者别的格式作为输出。遇到这种问题千万不要硬刚换编码器往往比折腾FFmpeg编译更省时。6.4 长视频处理时内存暴涨用moviepy时我遇到过一个很严重的情况处理一部接近3小时的电影内存占用在几分钟内飙升到几个GB最后直接OOM。原因是moviepy会试图把某些中间数据留在内存里特别是你用了复杂的滤镜链路时。同样的问题在ffmpeg-python里基本不会出现因为它是调用外部FFmpeg进程处理的FFmpeg在流式处理时会把中间数据写入临时区块内存占用通常稳定在几十MB到一两百MB。如果使用ffmpeg-python时发现内存占用过高多半是滤镜用得太复杂或者你用了pipe模式把大量数据送回Python进程。尽量把复杂的滤镜处理在FFmpeg内部完成不要让原始音频数据在Python层流转。6.5 声画不同步使用ss参数的位置不对做片段截取时如果ss参数放错位置会导致截出来的音频和源文件对应的时间点不一致。测试时发现ss放在output里虽然速度快但在处理某些时间戳不规律的文件时音频可能从错误的位置开始。如果你需要做帧级精确截取务必把ss放在input里并且在输出时设置accurate_seek。代码写法如下( ffmpeg .input(input_file, ss00:01:00, accurate_seekTrue) .output(output_file, formatmp3) .run(overwrite_outputTrue) )6.6 视频文件里有多个音频轨有些高清视频会带多个音频轨比如不同语言的原声、评论音轨。默认情况下FFmpeg会处理所有音频流可能导致混合或选取了错误的音轨。指定轨道的方法是在input之后、output之前用map( ffmpeg .input(input_file) .output(output_file, map0:a:1, formatmp3) .run(overwrite_outputTrue) )0:a:1的意思是从第一个输入文件里选择第2个音频流。注意索引从0开始所以0:a:1是第2条音频轨。想选第一个音频轨就用0:a:0。这个坑对做海外内容搬运或者录像带数字化的人很实用也常见于下载的多语言版电影中。如果不指定mapFFmpeg可能把多个音轨混流或者报错最后得出的文件不是你预期的那段声音。7. 加上进度条批量处理时的体验提升方案批量处理最忌讳干等且看不到进度。这里我提供两个层面的进度方案轻量级的用tqdm包住文件循环重量级的解析FFmpeg输出得到时间进度。7.1 轻量级方案文件级进度条如果你只是批量处理一堆文件用tqdm逐个文件显示进度就足够import ffmpeg import glob from tqdm import tqdm from pathlib import Path video_files glob.glob(./videos/*.mp4) for video_file in tqdm(video_files, desc提取进度): out_file Path(./audios) / (Path(video_file).stem .mp3) try: ffmpeg.input(video_file).output(str(out_file), formatmp3).run(quietTrue, overwrite_outputTrue) except ffmpeg.Error: pass这个方案实现简单缺点是每个文件内部处理多久没有精确反馈只能看到完成个数。7.2 重量级方案解析FFmpeg输出的时间进度如果需要精确到百分比可以通过读取FFmpeg的stderr输出中的time字段来估算进度。但用ffmpeg-python的run()方法直接捕获输出比较麻烦因为run方法在任务结束前不会把流给到你。更实用的做法是用Python标准库的subprocess直接调用ffmpeg命令行然后逐行读取stdout/stderrimport subprocess import re import sys def extract_audio_with_progress(video_path, audio_path, total_duration): cmd [ ffmpeg, -i, video_path, -vn, -acodec, libmp3lame, -b:a, 192k, -y, audio_path ] proc subprocess.Popen( cmd, stdoutsubprocess.PIPE, stderrsubprocess.STDOUT, universal_newlinesTrue, encodingutf-8, errorsignore ) duration_pattern re.compile(rDuration: (\d):(\d):(\d\.\d)) time_pattern re.compile(rtime(\d):(\d):(\d\.\d)) for line in proc.stdout: dur_match duration_pattern.search(line) if dur_match: hours, minutes, seconds map(float, dur_match.groups()) total_duration hours * 3600 minutes * 60 seconds time_match time_pattern.search(line) if time_match: hours, minutes, seconds map(float, time_match.groups()) current_time hours * 3600 minutes * 60 seconds if total_duration: percent current_time / total_duration * 100 sys.stdout.write(f\r进度: {percent:5.1f}%) sys.stdout.flush() proc.wait() print()这段代码一行行读ffmpeg的输出实时解析出当前处理时间再除以总时长得到百分比。做了编码容错避免某些文件里的非UTF-8字节导致整个循环崩溃。7.3 并发批量处理时的CPU与进程管理批量提取音频时是否要并发我的经验是先看磁盘和CPU资源。因为ffmpeg转码本身是高CPU任务如果你一次开太多并发进程会直接把CPU跑满系统卡死反而拖慢整体时间。更合理的方案是控制并发数为CPU物理核心数的一半到三分之二。用Python的concurrent.futures实现一个简单版import concurrent.futures import ffmpeg import psutil def extract_one(file_pair): in_file, out_file file_pair try: ffmpeg.input(in_file).output(out_file, formatmp3).run(quietTrue, overwrite_outputTrue) return in_file, True except Exception: return in_file, False # 根据CPU负载决定并发数 cpu_count psutil.cpu_count(logicalFalse) or 4 max_workers max(2, cpu_count // 2) tasks [(v, Path(./audios) / (Path(v).stem .mp3)) for v in video_files] with concurrent.futures.ThreadPoolExecutor(max_workersmax_workers) as executor: for in_file, success in executor.map(extract_one, tasks): print(f{in_file} - {success})这里使用线程池而不是进程池因为ffmpeg命令的执行是阻塞在外部进程的线程切换成本低并不会被GIL限制。8. 把提取后的音频接入你的工作流一个完整的小项目示例为了让上面这些片段形成一个完整故事我按自己整理素材库的流程给一个综合示例。这个脚本做三件事扫描目录下的视频文件、批量提取音频、将结果按原视频名归档。这是最典型的“自媒体素材库整理”场景。import ffmpeg import glob import os from pathlib import Path from tqdm import tqdm BASE_DIR Path(/data/videos) AUDIO_DIR Path(/data/audios) AUDIO_DIR.mkdir(exist_okTrue) exts [*.mp4, *.mov, *.mkv, *.ts, *.avi] files [] for ext in exts: files.extend(glob.glob(str(BASE_DIR / ext))) print(f共发现 {len(files)} 个视频文件) for file in tqdm(files, desc提取音频): fpath Path(file) out AUDIO_DIR / (fpath.stem .mp3) if out.exists(): continue try: ( ffmpeg .input(str(fpath)) .output(str(out), formatmp3, audio_bitrate192k, vnNone) .run(overwrite_outputTrue, quietTrue) ) except ffmpeg.Error as e: with open(extract_errors.log, a, encodingutf-8) as log: log.write(f{file}: {e.stderr.decode()}\n) print(全部处理完成)这个脚本里if out.exists()是个细节优势当你处理一批文件被中断后再次运行脚本时它会跳过已经生成结果的视频实现断点续传。这个设计在后来的项目里帮我省了不少时间。处理完成后的音频文件你可以继续接上其它工具链用pydub做静音切除、用whisper做转写、用librosa做特征分析、用sqlite做素材索引。音频文件本身是标准化产物后面接什么都方便。9. 从我自己的使用习惯出发最后分享三件小事第一件事能用流复制就不要转码。很多人的需求只是“从视频里把音频扣出来”原视频音频格式是AAC直接copy成M4A就是最合理的方案速度快、容量小、音质无损。只有当你确确实实需要MP3这种通用格式时才去转码。我的默认操作是用ffprobe先看原视频音频编码再决定方案。ffprobe的调用方式也很简单ffprobe -v error -show_streams -select_streams a input.mp4这会输出音频流的所有元信息包括编码格式、采样率、声道数。拿到信息后再决定是copy还是转码心里就有数了。第二件事批量处理一定要留退路。所有批量脚本都建议先跑一个两三条数据的测试集确认输出没问题后再全量跑。同时所有中间产物都最好放到独立目录不要覆盖原视频。原视频是唯一的素材来源一旦操作失误覆盖了找回来的成本极高。第三件事多花点时间读FFmpeg的滤镜文档。很多人觉得提取音频只是“抄一段代码”但当你遇到各种变形需求比如要去掉背景音乐只留人声、要把双声道合并成单声道、要调音调和速度FFmpeg的滤镜系统能覆盖大多数需求。ffmpeg-python只是参数封装真正有壁垒的是你对滤镜参数的理解。我个人的路线是先用命令行直接实验确认参数生效后再翻译成ffmpeg-python代码。这样调试效率高很多也方便在命令行阶段排查参数问题。
返回列表