Python调用FFmpeg处理音视频文件方法

作者:技术拾光

时间:2026-07-27

浏览:495

Python调用FFmpeg处理音视频文件主要有三种方式:subprocess.run执行一次性任务、Popen实时获取进度、配合Pathlib批量处理。需注意设置超时、检查FFmpeg路径、处理文件锁及中文路径编码问题,不同版本进度格式需做兼容匹配。

文章目录

在后端开发中,经常需要程序自动处理音视频文件,比如压缩用户上传的视频、从录屏中提取音频,或者将多张图片合成视频。FFmpeg 是这类任务的事实标准,但如何在 Python 中高效、稳定地调用它,这里总结了常见问题和解决方案。

Python调用FFmpeg处理音视频文件方法

一、基础方式:subprocess.call

最简单的做法是直接传递命令字符串:

import subprocess

# 提取音频
subprocess.call([
    'ffmpeg', '-i', 'input.mp4',
    '-vn', '-c:a', 'mp3', 'output.mp3'
])

call 会阻塞直到命令执行完,返回退出码。优点是简单,缺点是如果命令耗时较长(例如压缩一个 2GB 的视频),程序界面会卡住。

二、推荐方式:subprocess.run

Python 3.5 及以上版本推荐使用 run,它返回 CompletedProcess 对象,可以获取退出码、标准输出和标准错误:

import subprocess

result = subprocess.run(
    ['ffmpeg', '-i', 'input.mp4', '-vf', 'scale=1280:720', 'output.mp4'],
    capture_output=True,
    text=True,
    timeout=300
)

if result.returncode == 0:
    print('转换成功')
else:
    print('转换失败:', result.stderr)

有一个容易忽略的点:timeout 参数。如果不加,当 FFmpeg 因某些原因卡住(如输入文件损坏),Python 进程也会一直挂起。设置合理的超时时间是好习惯。

三、进阶:实时获取处理进度

FFmpeg 处理大文件时,进度信息会输出到 stderr,格式类似:

frame=  120 fps=30.0 time=00:00:04.00 bitrate=128.0kbits/s speed=1.0x

如果需要实时显示进度条,可以通过管道逐行读取 stderr:

import subprocess
import re

def convert_with_progress(input_path, output_path):
    cmd = [
        'ffmpeg', '-i', input_path,
        '-c:v', 'libx264', '-preset', 'medium',
        '-progress', 'pipe:1',  # 进度信息输出到 stdout
        output_path
    ]
    
    process = subprocess.Popen(
        cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
        universal_newlines=True, bufsize=1
    )
    
    for line in process.stdout:
        match = re.search(r'out_time_us=(d+)', line)
        if match:
            us = int(match.group(1))
            seconds = us / 1_000_000
            print(f'已处理: {seconds:.1f} 秒', end='r')
    
    process.wait()
    if process.returncode == 0:
        print('n处理完成')
    else:
        error = process.stderr.read()
        print(f'n处理失败: {error[:200]}...')

convert_with_progress('large_video.mp4', 'compressed.mp4')

这里使用 -progress pipe:1 让 FFmpeg 将进度信息输出到 stdout,便于 Python 读取。bufsize=1 确保逐行读取,而不是等缓冲区满。

四、批量处理:结合 Pathlib

处理文件夹下的所有文件时,使用 pathlib 搭配 FFmpeg 比 os.path 更顺手:

from pathlib import Path
import subprocess

def batch_convert_to_mp3(input_dir: str, output_dir: str, bitrate='192k'):
    src = Path(input_dir)
    dst = Path(output_dir)
    dst.mkdir(parents=True, exist_ok=True)
    
    files = list(src.glob('*.*'))
    for i, file in enumerate(files, 1):
        if file.suffix.lower() not in ('.mp4', '.mkv', '.mov', '.a vi'):
            continue
        
        output = dst / f'{file.stem}.mp3'
        print(f'[{i}/{len(files)}] {file.name}')
        
        subprocess.run([
            'ffmpeg', '-y', '-i', str(file),
            '-vn', '-c:a', 'libmp3lame',
            '-b:a', bitrate,
            str(output)
        ], capture_output=True)

batch_convert_to_mp3('./videos', './audios')

-y 参数自动覆盖已有文件,避免交互式确认导致脚本卡住。这在批量处理时几乎必加。

五、常见错误及处理

1. FFmpeg 不在 PATH 里

Windows 上最容易遇到。建议在脚本开头检查:

import shutil

ffmpeg_path = shutil.which('ffmpeg')
if not ffmpeg_path:
    raise RuntimeError('未找到 FFmpeg,请确认已安装并加入系统 PATH')

2. 输入文件被占用

Windows 上如果文件正被其他程序使用(例如刚用播放器打开),FFmpeg 会报 Permission denied。建议操作前先尝试用只读模式打开文件,确认未被锁定:

def is_file_locked(filepath):
    try:
        with open(filepath, 'rb') as f:
            return False
    except PermissionError:
        return True

3. 中文字符路径

Windows 下路径包含中文时,建议使用 Path 对象的 resolve() 获取绝对路径传给 FFmpeg,避免编码问题。

4. 进度信息解析失败

不同版本的 FFmpeg 输出的进度格式可能有细微差异。建议做两层匹配,先试新版格式再试旧版:

match = re.search(r'out_time_us=(d+)', line)
if not match:
    match = re.search(r'time=(d+):(d+):(d+).(d+)', line)

总结

Python 调用 FFmpeg 的核心模式有三种:subprocess.run 用于一次性任务、Popen 用于读取进度、配合 pathlib 进行批量处理。大部分问题集中在路径编码、超时处理、文件锁这三个方面,编写脚本时注意避开即可。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章