用Python的PyAV库5分钟掌握视频封装与解封装实战

最近在整理视频素材时,发现手头有一堆零散的音频、视频和字幕文件需要整合。这时候就需要用到视频封装(Muxing)技术了。而有时候又需要从已有的视频文件中提取出独立的音轨或字幕,这就涉及到解封装(Demuxing)。本文将带你用Python的PyAV库快速实现这些操作,无需复杂配置,5分钟就能上手。

1. 环境准备与PyAV安装

PyAV是FFmpeg的Python绑定,提供了处理音视频的高级接口。相比直接调用FFmpeg命令行,PyAV的API更加友好,适合在Python项目中集成。

安装PyAV非常简单:

pip install av

注意:PyAV依赖FFmpeg,如果遇到安装问题,可能需要先安装FFmpeg。在Ubuntu上可以运行 sudo apt-get install ffmpeg ,Mac用户可以用 brew install ffmpeg

验证安装是否成功:

import av
print(av.__version__)

如果看到版本号输出,说明安装成功。接下来我们就可以开始实战了。

2. 视频封装实战:合并音视频和字幕

假设我们有一个视频文件 video.h264 、一个音频文件 audio.aac 和一个字幕文件 subtitle.srt ,现在要把它们合并成一个MP4文件。

import av

# 创建输出容器
output_container = av.open('output.mp4', mode='w')

# 添加视频流
video_stream = output_container.add_stream('h264', rate=30)
video_stream.width = 1280
video_stream.height = 720
video_stream.pix_fmt = 'yuv420p'

# 添加音频流
audio_stream = output_container.add_stream('aac', rate=44100)
audio_stream.channels = 2

# 添加字幕流
subtitle_stream = output_container.add_stream('mov_text')

# 打开输入文件
video_input = av.open('video.h264')
audio_input = av.open('audio.aac')
subtitle_input = av.open('subtitle.srt')

# 封装过程
for packet in video_input.demux():
    if packet.stream.type == 'video':
        for frame in packet.decode():
            frame.pts = None  # 让PyAV自动处理时间戳
            for packet_out in video_stream.encode(frame):
                output_container.mux(packet_out)

for packet in audio_input.demux():
    if packet.stream.type == 'audio':
        for frame in packet.decode():
            frame.pts = None
            for packet_out in audio_stream.encode(frame):
                output_container.mux(packet_out)

for packet in subtitle_input.demux():
    if packet.stream.type == 'subtitle':
        output_container.mux(packet)

# 关闭所有文件
output_container.close()
video_input.close()
audio_input.close()
subtitle_input.close()

关键参数说明:

  • add_stream() : 添加流时指定的编码器需要与输入格式兼容
  • rate : 视频帧率或音频采样率
  • pix_fmt : 视频像素格式,'yuv420p'是最广泛兼容的格式

3. 视频解封装实战:提取音视频和字幕

现在我们来演示如何从一个MP4文件中提取出视频、音频和字幕。

import av

input_container = av.open('input.mp4')

# 获取各流
video_stream = None
audio_stream = None
subtitle_stream = None

for stream in input_container.streams:
    if stream.type == 'video' and not video_stream:
        video_stream = stream
    elif stream.type == 'audio' and not audio_stream:
        audio_stream = stream
    elif stream.type == 'subtitle' and not subtitle_stream:
        subtitle_stream = stream

# 提取视频为H.264格式
if video_stream:
    video_output = av.open('video_out.h264', 'w')
    video_out_stream = video_output.add_stream('h264')
    
    for packet in input_container.demux(video_stream):
        if packet.dts is None:
            continue
        video_output.mux(packet)
    
    video_output.close()

# 提取音频为AAC格式
if audio_stream:
    audio_output = av.open('audio_out.aac', 'w')
    audio_out_stream = audio_output.add_stream('aac')
    
    for packet in input_container.demux(audio_stream):
        if packet.dts is None:
            continue
        audio_output.mux(packet)
    
    audio_output.close()

# 提取字幕为SRT格式
if subtitle_stream:
    subtitle_output = open('subtitle_out.srt', 'w')
    
    for packet in input_container.demux(subtitle_stream):
        if packet.dts is None:
            continue
        subtitle = packet.decode()[0]
        subtitle_output.write(subtitle.text + '\n')
    
    subtitle_output.close()

input_container.close()

4. 常见问题与解决方案

在实际操作中,你可能会遇到以下问题:

问题1:时间戳不同步导致音画不同步

解决方案:

  • 封装时重置时间戳(设置 frame.pts = None
  • 确保所有输入流的持续时间一致
  • 使用 av.align_streams() 对齐流

问题2:格式不兼容导致封装失败

常见不兼容情况:

容器格式 支持的视频编码 支持的音频编码 支持的字幕格式
MP4 H.264, H.265 AAC, MP3 mov_text
MKV 几乎所有格式 几乎所有格式 SRT, ASS
MOV H.264, ProRes AAC, PCM mov_text

问题3:封装后文件过大

优化建议:

  • 调整视频码率: video_stream.bit_rate = 1000000 (1Mbps)
  • 使用更高效的编码器,如H.265
  • 降低音频采样率或使用更高效的音频编码

5. 高级技巧与应用场景

批量处理多个文件

import os
from glob import glob

def batch_mux(video_dir, audio_dir, output_dir):
    video_files = glob(os.path.join(video_dir, '*.h264'))
    for vfile in video_files:
        base = os.path.basename(vfile).split('.')[0]
        afile = os.path.join(audio_dir, f'{base}.aac')
        if os.path.exists(afile):
            output_file = os.path.join(output_dir, f'{base}.mp4')
            # 调用封装函数...

实时流处理

PyAV也可以用于实时流处理,比如直播推流:

output = av.open('rtmp://live.twitch.tv/app/STREAM_KEY', 'w', format='flv')
# 添加流并推送帧...

硬件加速

如果你的系统支持硬件加速,可以启用它来提高处理速度:

# 启用CUDA加速
container = av.open('input.mp4', options={'hwaccel': 'cuda'})

掌握了PyAV的封装和解封装技术,你可以轻松实现:

  • 自制视频剪辑工具
  • 视频格式转换工具
  • 音视频分析工具
  • 直播流处理系统

更多推荐