ffmpeg 简介

FFmpeg是一个开源的音视频处理工具,支持录制、转换数字音频、视频,并能将其转化为流。它包含了一个完整的解决方案,能够处理几乎所有的音视频格式。

FFmpeg的核心是libavcodec库,它提供了多种音视频编解码器,支持从简单的音频转码到复杂的视频处理任务。FFmpeg还提供了丰富的命令行工具,用户可以通过命令行轻松实现音视频的转换、剪辑、合并等操作。

FFmpeg的跨平台特性使其能够在Linux、Windows和macOS等多种操作系统上运行。它的强大功能和灵活性使其成为音视频处理领域的事实标准。

Source

必要条件

  • 安装FFmpeg:确保你的系统上已经安装了FFmpeg。

转码命令

# Intel QuickSync (hevc_qsv)
# 参数:
#   -global_quality: 1–51(类似 CRF,数值越大质量越低,文件越小,默认23)
#   -preset: ultrafast | fast | medium | slow | slower | veryslow(速度↑/压缩率↓,默认medium)
ffmpeg -y -i input.mp4 \
  -c:v hevc_qsv -global_quality 23 -preset medium \
  -c:a copy -c:s copy \
  output_h265_intel.mp4
# AMD AMF (hevc_amf)
# 参数:
#   -qp:       0–51(质量参数,数值越大压缩越强,默认23)
#   -quality:  speed | balanced | quality(speed=最快最低质;balanced=中;quality=最高质,默认balanced)
ffmpeg -y -i input.mp4 \
  -c:v hevc_amf -rc cqp -qp 23 -quality balanced \
  -c:a copy -c:s copy \
  output_h265_amd.mp4
# NVIDIA NVENC (hevc_nvenc)
# 参数:
#   -cq:     0–51(质量参数,数值越大质量越低,文件越小,默认23)
#   -preset: ultrafast | fast | medium | slow | slower | veryslow(同x265预设,默认medium)
ffmpeg -y -i input.mp4 \
  -c:v hevc_nvenc -cq 23 -preset medium \
  -c:a copy -c:s copy \
  output_h265_nvidia.mp4
# 软件 x265 (libx265)
# 参数:
#   -crf:    0–51(数值越大压缩越强,默认23)
#   -preset: ultrafast | fast | medium | slow | slower | veryslow(速度↑/压缩率↓,默认medium)
ffmpeg -y -i input.mp4 \
  -c:v libx265 -crf 23 -preset medium \
  -c:a copy -c:s copy \
  output_h265_x265.mp4

Python 脚本

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
将视频文件转码为H.265编码。
用法: python convert_to_h265.py <视频文件路径> [--intel] [--amd] [--nvidia]
使用 --intel 参数启用 Intel QuickSync 硬件加速
使用 --amd 参数启用 AMD AMF 硬件加速
使用 --nvidia 参数启用 NVIDIA NVENC 硬件加速
"""

import argparse
import os
import subprocess
import sys


def convert_to_h265(
    input_file,
    use_intel=False,
    use_amd=False,
    use_nvidia=False,
    crf=23,
    preset="medium",
    bitrate=None,
):
    """
    使用ffmpeg将视频转码为H.265格式

    参数:
        input_file: 输入视频文件路径
        use_intel: 是否使用Intel QuickSync硬件加速
        use_amd: 是否使用AMD AMF硬件加速
        use_nvidia: 是否使用NVIDIA NVENC硬件加速
    """
    # 检查文件是否存在
    if not os.path.isfile(input_file):
        print(f"错误: 文件不存在 - {input_file}")
        return False

    # 构建输出文件路径
    file_dir = os.path.dirname(input_file)
    file_name, file_ext = os.path.splitext(os.path.basename(input_file))
    output_file = os.path.join(file_dir, f"{file_name}_h265{file_ext}")

    # 将 Windows 路径反斜杠改为正斜杠,避免 \0 被误识别
    input_file = input_file.replace("\\", "/")
    output_file = output_file.replace("\\", "/")

    # 构建 ffmpeg 命令
    ffmpeg_cmd = [
        "ffmpeg",
        "-y",  # 如果目标已存在则强制覆盖
        "-i",
        input_file,
    ]

    # 硬件/软件编码选择
    if use_intel:
        ffmpeg_cmd.extend(
            [
                "-c:v",
                "hevc_qsv",  # Intel QuickSync HEVC/H.265编码器
                "-global_quality",
                str(crf),  # QSV 对应的质量参数
                "-preset",
                preset,
            ]
        )
    elif use_amd:
        # 将 --preset 映射到 AMF 的 quality 参数
        amf_quality = {
            "ultrafast": "speed",
            "fast": "speed",
            "medium": "balanced",
            "slow": "quality",
            "slower": "quality",
            "veryslow": "quality",
        }.get(preset, "balanced")

        ffmpeg_cmd.extend(
            [
                "-c:v",
                "hevc_amf",  # AMD HEVC/H.265编码器
                "-rc",
                "cqp",  # AMF 使用 CQP 模式
                "-qp",
                str(crf),  # AMF 的质量参数
                "-quality",
                amf_quality,  # AMF 支持的 quality 选项
            ]
        )
    elif use_nvidia:
        ffmpeg_cmd.extend(
            [
                "-c:v",
                "hevc_nvenc",  # NVIDIA NVENC H.265编码器
                "-cq",
                str(crf),  # NVENC 的质量参数
                "-preset",
                preset,
            ]
        )
    else:
        # 软件 x265 编码
        ffmpeg_cmd.extend(
            [
                "-c:v",
                "libx265",  # 软件x265编码器
                "-crf",
                str(crf),
                "-preset",
                preset,
            ]
        )

    # 如果指定了固定码率,则覆盖质量参数
    if bitrate:
        ffmpeg_cmd.extend(["-b:v", bitrate])

    # 添加通用的编码参数
    ffmpeg_cmd.extend(
        [
            "-c:a",
            "copy",  # 复制音频流
            "-c:s",
            "copy",  # 复制字幕流 (如果有)
            output_file,
        ]
    )

    print(f"正在转码: {input_file}")
    print(f"输出文件: {output_file}")
    print("转码中,请稍候...")

    try:

        # 执行ffmpeg命令
        process = subprocess.Popen(
            ffmpeg_cmd,
            stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT,
            universal_newlines=True,
        )

        # 安全地处理并显示进度信息
        if process.stdout:  # 确保stdout不是None
            for line in process.stdout:
                # 仅显示包含关键字的行以减少输出量
                if "frame=" in line or "speed=" in line or "error" in line.lower():
                    print(f"\r{line.strip()}", end="")
        else:
            # 如果无法获取输出流,提供一个替代方案
            print("正在处理中,请等待...")

        # 等待进程完成
        return_code = process.wait()

        if return_code == 0:
            print(f"\n\n转码完成!输出文件: {output_file}")
            print(f"原始文件大小: {os.path.getsize(input_file) / (1024*1024):.2f} MB")
            print(
                f"转码后文件大小: {os.path.getsize(output_file) / (1024*1024):.2f} MB"
            )
            return True
        else:
            print(f"\n转码失败,ffmpeg返回错误代码: {return_code}")
            return False

    except FileNotFoundError:
        print("错误: 找不到ffmpeg。请确保ffmpeg已安装并添加到系统PATH中。")
        return False
    except Exception as e:
        print(f"发生错误: {str(e)}")
        return False


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="将视频转码为H.265格式")
    parser.add_argument("input_file", help="输入视频文件路径")
    parser.add_argument(
        "--intel",
        action="store_true",
        help="使用 Intel QuickSync硬件加速",
    )
    parser.add_argument(
        "--amd",
        action="store_true",
        help="使用 AMD GPU 硬件加速",
    )
    parser.add_argument(
        "--nvidia",
        action="store_true",
        help="使用 NVIDIA NVENC 硬件加速",
    )

    # 新增质量/码率可调参数
    parser.add_argument(
        "--crf",
        type=int,
        default=23,
        help="质量参数(CRF或等效值,数值越大文件越小)",
    )
    parser.add_argument(
        "--preset",
        default="medium",
        choices=[
            "ultrafast",
            "fast",
            "medium",
            "slow",
            "slower",
            "veryslow",
        ],
        help="编码preset",
    )
    parser.add_argument(
        "--bitrate",
        help="目标码率,如2000k,优先于CRF/质量参数",
    )

    args = parser.parse_args()
    if sum(bool(x) for x in (args.intel, args.amd, args.nvidia)) > 1:
        print("错误: 只能指定一个硬件加速选项")
        sys.exit(1)

    convert_to_h265(
        args.input_file,
        use_intel=args.intel,
        use_amd=args.amd,
        use_nvidia=args.nvidia,
        crf=args.crf,
        preset=args.preset,
        bitrate=args.bitrate,
    )