Python视频转码案例如何转换视频格式

wen python案例 28

本文目录导读:

Python视频转码案例如何转换视频格式

  1. 基础环境安装
  2. 使用moviepy进行视频格式转换
  3. 使用ffmpeg-python进行转换
  4. 实用工具函数
  5. 完整示例:GUI版本
  6. 性能优化建议

我来分享几个Python视频转码的实用案例,主要使用moviepyffmpeg-python库。

基础环境安装

pip install moviepy ffmpeg-python
# 还需要安装FFmpeg(系统层面)

使用moviepy进行视频格式转换

基础转换案例

from moviepy.editor import VideoFileClip
import os
def convert_video_basic(input_path, output_path):
    """
    基础视频格式转换
    """
    try:
        # 加载视频
        video = VideoFileClip(input_path)
        # 转换并保存
        video.write_videofile(
            output_path,
            codec='libx264',  # 视频编码
            audio_codec='aac',  # 音频编码
            fps=30,  # 帧率
            bitrate='2000k'  # 比特率
        )
        video.close()
        print(f"转换完成: {output_path}")
    except Exception as e:
        print(f"转换失败: {e}")
# 使用示例
convert_video_basic('input.avi', 'output.mp4')

批量转换案例

import glob
from moviepy.editor import VideoFileClip
from concurrent.futures import ThreadPoolExecutor
import os
def batch_convert_videos(input_dir, output_dir, target_format='mp4'):
    """
    批量转换视频格式
    """
    # 创建输出目录
    os.makedirs(output_dir, exist_ok=True)
    # 获取所有视频文件
    video_extensions = ['*.avi', '*.mov', '*.mkv', '*.flv', '*.wmv']
    video_files = []
    for ext in video_extensions:
        video_files.extend(glob.glob(os.path.join(input_dir, ext)))
    def convert_single(video_path):
        try:
            # 生成输出文件名
            filename = os.path.basename(video_path)
            name_without_ext = os.path.splitext(filename)[0]
            output_path = os.path.join(output_dir, f"{name_without_ext}.{target_format}")
            # 转换视频
            video = VideoFileClip(video_path)
            video.write_videofile(
                output_path,
                codec='libx264',
                audio_codec='aac',
                preset='medium',
                logger=None  # 关闭日志
            )
            video.close()
            print(f"转换成功: {filename}")
        except Exception as e:
            print(f"转换失败 {video_path}: {e}")
    # 多线程处理
    with ThreadPoolExecutor(max_workers=4) as executor:
        executor.map(convert_single, video_files)
# 使用示例
batch_convert_videos('./input_videos/', './output_videos/')

使用ffmpeg-python进行转换

高级转换案例

import ffmpeg
import os
def convert_video_ffmpeg(input_path, output_path, options=None):
    """
    使用ffmpeg进行高级视频转换
    """
    if options is None:
        options = {
            'vcodec': 'libx264',
            'acodec': 'aac',
            'b:v': '2000k',
            'b:a': '192k',
            'vf': 'scale=1920:1080',  # 调整分辨率
            'r': 30  # 帧率
        }
    try:
        # 构建ffmpeg命令
        stream = ffmpeg.input(input_path)
        # 应用输出选项
        stream = ffmpeg.output(stream, output_path, **options)
        # 执行转换
        ffmpeg.run(stream, overwrite_output=True, capture_stderr=True)
        print(f"转换完成: {output_path}")
    except ffmpeg.Error as e:
        print(f"FFmpeg错误: {e.stderr.decode()}")
# 使用示例
options = {
    'vcodec': 'libx264',       # 视频编码
    'acodec': 'aac',           # 音频编码
    'b:v': '3000k',            # 视频比特率
    'b:a': '192k',             # 音频比特率
    'vf': 'scale=-2:720',      # 保持宽高比缩放到720p
    'r': 24,                   # 帧率
    'preset': 'fast',          # 编码速度
    'crf': 23                  # 质量(0-51, 越低越好)
}
convert_video_ffmpeg('input.mp4', 'output.mkv', options)

视频压缩案例

def compress_video(input_path, output_path, target_size_mb=100):
    """
    压缩视频到目标大小
    """
    try:
        # 获取原视频信息
        probe = ffmpeg.probe(input_path)
        video_info = next(s for s in probe['streams'] if s['codec_type'] == 'video')
        # 获取视频时长
        duration = float(probe['format']['duration'])
        # 计算目标比特率
        total_bits = target_size_mb * 8 * 1024 * 1024
        video_bitrate = int((total_bits / duration) * 0.9)  # 90%给视频
        # 构建压缩参数
        output_params = {
            'vcodec': 'libx264',
            'acodec': 'aac',
            'b:v': f'{video_bitrate}k',
            'maxrate': f'{int(video_bitrate * 1.5)}k',
            'bufsize': f'{int(video_bitrate * 2)}k',
            'preset': 'slow',
            'crf': 28
        }
        # 执行压缩
        stream = ffmpeg.output(
            ffmpeg.input(input_path),
            output_path,
            **output_params
        )
        ffmpeg.run(stream, overwrite_output=True)
        # 显示压缩结果
        original_size = os.path.getsize(input_path) / (1024 * 1024)
        compressed_size = os.path.getsize(output_path) / (1024 * 1024)
        ratio = (1 - compressed_size / original_size) * 100
        print(f"原视频大小: {original_size:.2f} MB")
        print(f"压缩后大小: {compressed_size:.2f} MB")
        print(f"压缩率: {ratio:.1f}%")
    except Exception as e:
        print(f"压缩失败: {e}")
# 使用示例
compress_video('input.mp4', 'compressed.mp4', target_size_mb=50)

实用工具函数

def get_video_info(filepath):
    """
    获取视频信息
    """
    try:
        video = VideoFileClip(filepath)
        info = {
            'filename': os.path.basename(filepath),
            'duration': video.duration,
            'size': os.path.getsize(filepath) / (1024 * 1024),  # MB
            'width': video.size[0],
            'height': video.size[1],
            'fps': video.fps,
            'audio': video.audio is not None
        }
        video.close()
        return info
    except Exception as e:
        return {'error': str(e)}
def supported_formats():
    """
    返回支持转换的格式列表
    """
    return {
        '输入格式': ['mp4', 'avi', 'mov', 'mkv', 'flv', 'wmv', 'webm'],
        '输出格式': ['mp4', 'avi', 'mov', 'mkv', 'webm', 'gif'],
        '推荐编码': {
            'mp4': 'libx264 + aac',
            'webm': 'libvpx + libvorbis',
            'avi': 'mpeg4 + mp3'
        }
    }
# 使用示例
print(get_video_info('input.mp4'))
print(supported_formats())

完整示例:GUI版本

import tkinter as tk
from tkinter import filedialog, ttk
import threading
from moviepy.editor import VideoFileClip
class VideoConverterGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("视频格式转换工具")
        self.root.geometry("500x400")
        # 创建界面元素
        self.create_widgets()
    def create_widgets(self):
        # 文件选择
        tk.Label(self.root, text="选择视频文件:").pack(pady=10)
        self.file_path = tk.StringVar()
        tk.Entry(self.root, textvariable=self.file_path, width=50).pack(padx=20)
        tk.Button(self.root, text="浏览", command=self.select_file).pack(pady=5)
        # 输出格式选择
        tk.Label(self.root, text="输出格式:").pack(pady=10)
        self.output_format = tk.StringVar(value="mp4")
        formats = ["mp4", "avi", "mov", "mkv", "webm"]
        ttk.Combobox(self.root, textvariable=self.output_format, 
                     values=formats, state="readonly").pack()
        # 质量设置
        tk.Label(self.root, text="视频质量:").pack(pady=10)
        self.quality = tk.IntVar(value=50)
        quality_scale = tk.Scale(self.root, from_=10, to=100, 
                                 variable=self.quality, orient=tk.HORIZONTAL,
                                 length=300)
        quality_scale.pack()
        # 转换按钮
        self.convert_btn = tk.Button(self.root, text="开始转换", 
                                     command=self.start_conversion)
        self.convert_btn.pack(pady=20)
        # 进度条
        self.progress = ttk.Progressbar(self.root, length=300, mode='determinate')
        self.progress.pack(pady=10)
        # 状态显示
        self.status_label = tk.Label(self.root, text="就绪")
        self.status_label.pack()
    def select_file(self):
        filename = filedialog.askopenfilename(
            title="选择视频文件",
            filetypes=[("视频文件", "*.mp4 *.avi *.mov *.mkv *.flv *.wmv")]
        )
        if filename:
            self.file_path.set(filename)
    def start_conversion(self):
        if not self.file_path.get():
            self.status_label.config(text="请先选择视频文件")
            return
        # 在新线程中执行转换
        thread = threading.Thread(target=self.convert_video)
        thread.daemon = True
        thread.start()
    def convert_video(self):
        try:
            self.convert_btn.config(state="disabled")
            self.status_label.config(text="正在转换...")
            input_path = self.file_path.get()
            base_name = os.path.splitext(input_path)[0]
            output_path = f"{base_name}.{self.output_format.get()}"
            # 根据质量设置比特率
            bitrate = f"{self.quality.get() * 100}k"
            video = VideoFileClip(input_path)
            video.write_videofile(
                output_path,
                codec='libx264',
                audio_codec='aac',
                bitrate=bitrate,
                logger=None
            )
            video.close()
            self.status_label.config(text=f"转换完成: {os.path.basename(output_path)}")
        except Exception as e:
            self.status_label.config(text=f"转换失败: {str(e)}")
        finally:
            self.convert_btn.config(state="normal")
            self.progress['value'] = 100
if __name__ == "__main__":
    root = tk.Tk()
    app = VideoConverterGUI(root)
    root.mainloop()

性能优化建议

  1. 使用硬件加速

    options = {
     'vcodec': 'h264_nvenc',  # NVIDIA GPU加速
     # 或 'vcodec': 'h264_amf'  # AMD GPU加速
    }
  2. 多线程处理

    def convert_batch(input_files):
     with ThreadPoolExecutor(max_workers=4) as executor:
         list(executor.map(process_video, input_files))
  3. 内存优化

    # 处理大文件时分段读取
    video = VideoFileClip(input_path, audio=True, target_resolution=(1080, 1920))

这些案例涵盖了从基础到高级的视频转码需求,你可以根据具体需求选择合适的实现方式,需要注意的是,视频转码是计算密集型任务,建议对处理时间有合理预期。

抱歉,评论功能暂时关闭!