随机
Enter 搜索 ↑↓ 切换 Esc 清空

ffmpeg_video_hvc1

脚本

HEVC(hvc1) QSV 转码

补充说明

该脚本基于 Intel QSV(Quick Sync Video)硬件加速,将视频转码为 HEVC/H.265 并写入 hvc1 标签。特别适配 SR‑IOV VF 虚拟 GPU 环境(CPU 侧解码 + hevc_qsv 硬件编码,VBR 码率模式),并支持 setsid 后台静默执行,兼容 SSH 断线 / curl 管道调用。

提供「交互式 / curl 调用 / 目录批量 / 单文件」四种模式:交互模式列出当前目录 mp4 / mkv / mov / avi 视频,可选单个或输入 666 批量全部;命令行传入目录即目录批量、传入文件即单文件转码,均可指定输出目录、后缀开关与前后台开关。

脚本逐个串行转码,避免多个 ffmpeg 同时抢占 QSV 硬件队列导致花屏/卡死;输出文件已存在时自动跳过,可安全断点续跑。

一键脚本

bash <(curl -sL gitee.com/meimolihan/cmdbox/raw/master/sh/ffmpeg_video_hvc1.sh)
bash <(curl -sL ../sh/ffmpeg_video_hvc1.sh)

交互模式示例(先 cd 到视频目录再执行):

cd /vol2/1000/media/电影/我的片单
bash ffmpeg_video_hvc1.sh

命令行模式示例:

# 单个视频转码(默认输出到源文件同目录、添加后缀、后台运行)
bash ffmpeg_video_hvc1.sh "/vol2/1000/media/电影/示例.mp4"

# 单个视频:指定输出目录、使用原文件名(不加后缀)、前台运行
bash ffmpeg_video_hvc1.sh "/vol2/1000/media/电影/示例.mp4" "/vol2/1000/media/转码输出" false false

# 目录批量:源目录 → 输出目录,添加后缀、后台运行
bash ffmpeg_video_hvc1.sh "/vol2/1000/media/电影/我的片单" "/vol2/1000/media/转码输出" true true

强制停止转码

# 杀掉所有 ffmpeg 进程
pkill -9 -f ffmpeg && echo "已杀干净" || echo "没有找到 ffmpeg 进程"

# 终止全部hevc_qsv转码进程
pkill -f "hevc_qsv"

# 释放任务锁文件(异常强杀后使用)
rm -f /tmp/ffmpeg_batch_encode.lock

手动安装 ffmpeg

脚本 install_deps 会自动检测 ffmpeg 与 hevc_qsv 编码器并可选校验 vainfo;缺失时按发行版自动安装(apt/dnf/yum/pacman/zypper/apk/brew,不依赖 sudo)。也可手动安装:

# Debian/Ubuntu
sudo apt-get update && sudo apt-get install -y ffmpeg vainfo

# CentOS/RHEL
sudo yum install -y epel-release && sudo yum install -y ffmpeg vainfo

# Arch Linux
sudo pacman -S --noconfirm ffmpeg vainfo

# Alpine
sudo apk add ffmpeg vainfo

# macOS
brew install ffmpeg

功能特点

参数说明

模式 参数 说明 默认值
交互 序号 选择单个视频转码 —
交互 666 批量转码所有视频 —
交互 保存路径 转码输出目录,回车等于当前目录 当前目录
交互 命名方式 1/2 原文件名 / 添加默认后缀 2
交互 后台运行 y/N 是否 setsid 后台转码 否
命令行 $1 源视频路径或源目录(必填,自动识别) —
命令行 $2 输出目录(可选) 源文件同目录 / 源目录同级
命令行 $3 后缀开关 true/false true
命令行 $4 后台开关 true/false true

输出命名与日志

项目 规则
添加后缀 名称_HEVC-hvc1.扩展名
原文件名 名称.扩展名
单文件日志 输出目录/名称.log
后台总日志 输出目录/batch_main.log

转码命令详解

脚本底层实际执行的 ffmpeg 转码命令(参数来自顶部「可配置参数区」):

ffmpeg -threads auto -probesize 32M -avioflags direct \
    -i "input.mkv" -y \
    -c:v hevc_qsv -preset fast -b:v 2600k -maxrate 5200k -bufsize 10400k -tag:v hvc1 \
    -c:a copy "output_HEVC-hvc1.mp4"

各参数说明:

参数 作用
-threads auto 输入线程数自动(FFMPEG_THREADS 配置区可调)
-probesize 32M 加大探测缓冲,提高流信息识别成功率
-avioflags direct 直读 IO,减少缓冲开销
-c:v hevc_qsv 使用 QSV 硬件 HEVC/H.265 编码器
-preset fast 编码速度预设(越慢画质越好)
-b:v 2600k 目标视频码率
-maxrate 5200k 峰值码率上限(两倍目标)
-bufsize 10400k VBV 缓冲大小(两倍 maxrate)
-tag:v hvc1 写入 hvc1 标签,Apple/主流播放器兼容性最好
-c:a copy 音频流直接拷贝,不重新编码,保证音质与速度

该版本默认不启用 -movflags +faststart,与目标 ffmpeg 参考命令严格对齐(如需网页秒开可在配置区将 MOVFLAGS_FASTSTART=true);亦不显式指定 -qsv_device,由 QSV 驱动自动选择设备,因此可正常跑在 SR‑IOV VF 虚拟 GPU 上。

QSV 支持自检

# 确认系统已识别 Intel 显卡
lspci | grep -i vga

# 确认 ffmpeg 包含 hevc_qsv 编码器
ffmpeg -hide_banner -h encoder=hevc_qsv

若为无 QSV 硬件的纯软件环境,可去掉 -c:v hevc_qsv,改用 -c:v libx265 -preset fast -crf 23 保底转码。

注意事项

脚本源码

#!/bin/bash
# ==============================================================================
# HEVC hvc1 QSV批量转码脚本|setsid后台静默执行
# 适配SR‑IOV VF虚拟GPU|CPU解码 + hevc_qsv硬件编码 VBR模式
# 模式:交互式 / curl调用 / 目录批量 / 单文件
# ==============================================================================
set -uo pipefail

########################### 【可配置参数区】###########################
LOCKFILE="/tmp/ffmpeg_batch_encode.lock"
# QSV编码参数【已对齐目标ffmpeg命令】
QSV_PRESET="fast"
QSV_BV="2600k"
QSV_MAXRATE="5200k"
QSV_BUFSIZE="10400k"
# mp4 faststart:已禁用,匹配参考命令无‑movflags +faststart
MOVFLAGS_FASTSTART=false
# ffmpeg输入参数
FFMPEG_THREADS="auto"
PROBESIZE="32M"
AVIOFLAGS="direct"
######################################################################

list_color_init() {
    export gl_hui=$'\033[38;5;59m'
    export gl_hong=$'\033[38;5;9m'
    export gl_lv=$'\033[38;5;10m'
    export gl_huang=$'\033[38;5;11m'
    export gl_lan=$'\033[38;5;32m'
    export gl_bai=$'\033[38;5;15m'
    export gl_zi=$'\033[38;5;13m'
    export gl_bufan=$'\033[38;5;14m'
}
list_color_init

break_end() {
    echo -e "${gl_lv}操作完成${gl_bai}"
    echo -e "${gl_bai}按任意键继续 ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}\c"
    read -r -n 1 -s -r -p ""
    echo ""
    clear
}

abspath() {
    local p="$1"
    if command -v realpath &>/dev/null; then
        realpath "$p" 2>/dev/null || readlink -f "$p" 2>/dev/null || echo "$p"
    else
        case "$p" in
            /*) echo "$p" ;;
            *)  echo "$PWD/$p" ;;
        esac
    fi
}

lock_acquire() {
    if [[ -f "${LOCKFILE}" ]]; then
        local oldpid
        oldpid=$(<"${LOCKFILE}")
        if [[ -n "${oldpid}" ]] && kill -0 "${oldpid}" 2>/dev/null; then
            echo -e "\033[38;5;9m⚠️ 检测到已有转码任务(PID:${oldpid})运行,拒绝重复启动\033[0m"
            exit 1
        else
            echo -e "${gl_huang}⚠️ 锁文件残留,进程已死亡,自动清理旧锁${gl_bai}"
            rm -f "${LOCKFILE}"
        fi
    fi
    trap 'rm -f "${LOCKFILE}"' EXIT INT TERM HUP QUIT
    echo "$$" > "${LOCKFILE}"
}

install_deps() {
    echo -e "${gl_zi}>>> 检查依赖${gl_bai}"
    if command -v ffmpeg &>/dev/null; then
        echo -e "${gl_lv}ffmpeg 已安装: $(command -v ffmpeg)${gl_bai}"
        if ffmpeg -h encoder=hevc_qsv >/dev/null 2>&1; then
            echo -e "${gl_lv}hevc_qsv 硬件编码器 ✅${gl_bai}"
            if command -v vainfo &>/dev/null; then
                if vainfo >/dev/null 2>&1; then
                    echo -e "${gl_lv}VA‑API硬件环境正常 ✅${gl_bai}"
                else
                    echo -e "${gl_huang}⚠ vainfo检测异常:VA‑API驱动/权限异常,QSV可能失效${gl_bai}"
                fi
            else
                echo -e "${gl_huang}ℹ 未安装vainfo,跳过硬件校验${gl_bai}"
            fi
        else
            echo -e "${gl_hong}❌ ffmpeg不支持hevc_qsv,硬件转码不可用${gl_bai}"
            exit 1
        fi
        return 0
    fi
    echo -e "${gl_huang}ffmpeg未找到,尝试自动安装${gl_bai}"
    if command -v apt &>/dev/null; then
        apt update && apt install -y ffmpeg vainfo
    elif command -v dnf &>/dev/null; then
        dnf install -y ffmpeg vainfo
    elif command -v yum &>/dev/null; then
        yum install -y epel-release && yum install -y ffmpeg vainfo
    elif command -v pacman &>/dev/null; then
        pacman -S --noconfirm ffmpeg vainfo
    elif command -v zypper &>/dev/null; then
        zypper install -y ffmpeg vainfo
    elif command -v apk &>/dev/null; then
        apk add ffmpeg vainfo
    elif command -v brew &>/dev/null; then
        brew install ffmpeg
    else
        echo -e "${gl_hong}无法自动安装ffmpeg,请手动安装后重试${gl_bai}"
        exit 1
    fi
    if ! command -v ffmpeg &>/dev/null; then
        echo -e "${gl_hong}ffmpeg安装失败${gl_bai}"
        exit 1
    fi
    echo -e "${gl_lv}ffmpeg安装成功${gl_bai}"
    return 0
}

scan_videos() {
    local dir="$1"
    local exts=(mp4 mkv mov avi)
    local find_args=()
    for ext in "${exts[@]}"; do
        find_args+=(-iname "*.${ext}")
        find_args+=(-o)
    done
    if (( ${#find_args[@]} > 0 )); then
        unset 'find_args[${#find_args[@]}-1]'
    fi
    find "$dir" -maxdepth 1 -type f \( "${find_args[@]}" \) -print0 2>/dev/null | sort -z
}

do_encode() {
    local src="$1"
    local dst="$2"
    local log="$3"
    local fname
    fname=$(basename "$src")
    mkdir -p "$(dirname "$log")" 2>/dev/null
    # 前置校验
    if [[ ! -s "${src}" ]]; then
        echo "[$(date '+%Y-%m-%d %H:%M:%S')] ✘ 源文件为空或不存在: ${src}" | tee -a "$log"
        return 1
    fi
    if [[ ! -w "$(dirname "$dst")" ]]; then
        echo "[$(date '+%Y-%m-%d %H:%M:%S')] ✘ 输出目录无写入权限 $(dirname "$dst")" | tee -a "$log"
        return 1
    fi
    echo "------------------------------------------------------------" >> "$log"
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] >>> 开始转码: ${fname}" | tee -a "$log"
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] 输入: ${src}" >> "$log"
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] 输出: ${dst}" >> "$log"
    # 组装ffmpeg参数|严格对齐目标命令:无movflags faststart,音频 c:a copy
    local ffmpeg_cmd=(
        ffmpeg
        -threads "${FFMPEG_THREADS}"
        -probesize "${PROBESIZE}"
        -avioflags "${AVIOFLAGS}"
        -i "$src"
        -y
        -c:v hevc_qsv
        -preset "${QSV_PRESET}"
        -b:v "${QSV_BV}"
        -maxrate "${QSV_MAXRATE}"
        -bufsize "${QSV_BUFSIZE}"
        -tag:v hvc1
    )

    ffmpeg_cmd+=(-c:a copy "$dst")
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] ffmpeg_cmd: ${ffmpeg_cmd[*]}" >> "$log"
    "${ffmpeg_cmd[@]}" >> "$log" 2>&1
    local ret=$?
    if [[ $ret -eq 0 ]]; then
        echo "[$(date '+%Y-%m-%d %H:%M:%S')] ✔ 完成: ${dst}" | tee -a "$log"
    else
        echo "[$(date '+%Y-%m-%d %H:%M:%S')] ✘ 失败: ${src} (exit=${ret})" | tee -a "$log"
    fi
    return "${ret}"
}

run_queue() {
    local out_dir="$1"
    local use_suffix="$2"
    shift 2
    local files=("$@")
    local ok=0 fail=0
    local total=${#files[@]}

    if mount | grep -qE "cifs|nfs|smb"; then
        echo -e "${gl_huang}⚠ 检测到网络文件系统,IO等待wa可能升高${gl_bai}"
    fi
    for ((i=0; i<total; i++)); do
        local src="${files[$i]}"
        local fname=$(basename "$src")
        local name="${fname%.*}"
        local ext="${fname##*.}"
        local dst
        if [[ "$use_suffix" == "true" ]]; then
            dst="${out_dir}/${name}_HEVC-hvc1.${ext}"
        else
            dst="${out_dir}/${fname}"
        fi
        local log="${out_dir}/${name}.log"
        echo -e "${gl_bufan}[$((i+1))/${total}]${gl_bai} ${fname}"
        echo -e "${gl_hui}------------------------------------------------------------${gl_bai}"
        if [[ -f "$dst" ]]; then
            echo -e "${gl_huang}跳过(已存在): ${dst}${gl_bai}"
            ((ok++))
            continue
        fi
        if do_encode "$src" "$dst" "$log"; then
            ((ok++))
        else
            ((fail++))
        fi
    done
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    echo -e "${gl_hui}转码结束:成功 ${gl_lv}${ok}${gl_hui} / 失败 ${gl_hong}${fail}${gl_hui} / 总计 ${total}${gl_bai}"
    return 0
}

launch_bg() {
    local out_dir="$1"
    local use_suffix="$2"
    local tmpfiles="$3"
    env \
        "PATH=$PATH" \
        "LD_LIBRARY_PATH=${LD_LIBRARY_PATH:-}" \
        "LIBVA_DRIVERS_PATH=${LIBVA_DRIVERS_PATH:-}" \
        "LIBVA_DRIVER_NAME=${LIBVA_DRIVER_NAME:-}" \
        "LOCKFILE=${LOCKFILE}" \
        "QSV_PRESET=${QSV_PRESET}" \
        "QSV_BV=${QSV_BV}" \
        "QSV_MAXRATE=${QSV_MAXRATE}" \
        "QSV_BUFSIZE=${QSV_BUFSIZE}" \
        "MOVFLAGS_FASTSTART=${MOVFLAGS_FASTSTART}" \
        "FFMPEG_THREADS=${FFMPEG_THREADS}" \
        "PROBESIZE=${PROBESIZE}" \
        "AVIOFLAGS=${AVIOFLAGS}" \
    setsid bash -c "
        out_dir='${out_dir}'
        use_suffix='${use_suffix}'
        tmpfiles='${tmpfiles}'
        files_list=()
        while IFS= read -r -d '' f; do
            [[ -n \"\$f\" ]] && files_list+=(\"\$f\")
        done < \"\$tmpfiles\"
        $(declare -f list_color_init abspath do_encode run_queue)
        list_color_init
        run_queue \"\$out_dir\" \"\$use_suffix\" \"\${files_list[@]}\"
        rm -f \"\$tmpfiles\"
    " > "${out_dir}/batch_main.log" 2>&1 &
    disown
    echo "$!"
}

interactive_mode() {
    clear
    echo -e "${gl_zi}>>> 交互式 HEVC hvc1 QSV转码${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    echo -e "${gl_huang}当前目录: ${gl_lv}${PWD}${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    echo -e ""
    echo -e "${gl_hui}>>> 视频文件列表${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    local files=()
    while IFS= read -r -d '' f; do
        [[ -n "$f" ]] && files+=("$f")
    done < <(scan_videos "$PWD")
    local cnt=${#files[@]}
    if (( cnt == 0 )); then
        echo -e "${gl_hong}>>> [WARN] 当前目录未找到视频文件${gl_bai}"
        break_end
        return 1
    fi
    for ((i=0; i<cnt; i++)); do
        printf "${gl_bufan}%${#cnt}d.${gl_bai}   %s\n" "$((i+1))" "$(basename "${files[$i]}")"
    done
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    echo -e "${gl_huang}666.${gl_bai} 批量转码全部视频"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    read -r -e -p "$(echo -e "${gl_bai}请输入序号选择单个视频,或输入 ${gl_huang}666${gl_bai} 处理全部: ${gl_bai}")" input
    local selected=()
    if [[ "$input" == "666" ]]; then
        selected=("${files[@]}")
    elif [[ "$input" =~ ^[0-9]+$ ]]; then
        local idx=$((input - 1))
        if (( idx >= 0 && idx < cnt )); then
            selected=("${files[$idx]}")
        else
            echo -e "${gl_hong}序号超出范围${gl_bai}"
            return 1
        fi
    else
        echo -e "${gl_hong}无效输入${gl_bai}"
        return 1
    fi
    read -r -e -p "$(echo -e "${gl_bai}转码后保存路径(${gl_huang}回车${gl_lv}=${gl_huang}当前目录${gl_bai}): ${gl_bai}")" out_dir
    out_dir="${out_dir:-$PWD}"
    mkdir -p "$out_dir" || { echo -e "${gl_hong}无法创建目录${gl_bai}"; return 1; }
    local use_suffix="true"
    local abs_src abs_out
    abs_src=$(abspath "$PWD")
    abs_out=$(abspath "$out_dir")
    if [[ "$abs_src" == "$abs_out" ]]; then
        use_suffix="true"
        echo -e "${gl_huang}输出到当前目录,强制添加后缀${gl_bai}"
    else
        echo -e ""
        echo -e "${gl_hui}命名方式:${gl_bai}"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        echo -e "  ${gl_bufan}1.  ${gl_bai}使用原视频文件名"
        echo -e "  ${gl_bufan}2.  ${gl_bai}添加后缀 (_HEVC-hvc1)"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        read -r -e -p "$(echo -e "${gl_bai}请输入你的选择 [${gl_huang}1${gl_bai}/${gl_lv}2${gl_bai}],默认 ${gl_lv}2${gl_bai}: ")" naming
        naming="${naming:-2}"
        [[ "$naming" == "1" ]] && use_suffix="false" || use_suffix="true"
    fi
    local bg_choice
    read -r -e -p "$(echo -e "${gl_bai}是否在后台运行?(${gl_lv}y${gl_bai}/${gl_hong}N${gl_bai}): ${gl_bai}")" bg_choice
    if [[ "$bg_choice" =~ ^[Yy]$ ]]; then
        echo -e "${gl_lv}正在后台启动  ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
        echo -e "${gl_hui}日志将保存在: ${gl_lan}${out_dir}${gl_bai}"
        local tmpfiles
        tmpfiles=$(mktemp /tmp/encode_XXXXXX.files)
        printf '%s\0' "${selected[@]}" > "$tmpfiles"
        local pid
        pid=$(launch_bg "$out_dir" "$use_suffix" "$tmpfiles")
        echo -e ""
        echo -e "${gl_lv}后台任务已启动 (PID: ${pid})${gl_bai}"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        echo -e "${gl_hui}查看总日志: tail -f ${out_dir}/batch_main.log${gl_bai}"
        echo -e "${gl_hui}查看单文件:  tail -f ${out_dir}/<视频名>.log${gl_bai}"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        return 0
    fi
    run_queue "$out_dir" "$use_suffix" "${selected[@]}"
    break_end
}

cli_mode() {
    local src="$1"
    local out_dir="${2:-$(dirname "$src")}"
    local use_suffix_str="${3:-true}"
    local run_bg_str="${4:-true}"
    local use_suffix="true"
    [[ "$use_suffix_str" == "false" ]] && use_suffix="false"
    local run_bg="true"
    [[ "$run_bg_str" == "false" ]] && run_bg="false"
    mkdir -p "$out_dir" || return 1
    if [[ -d "$src" ]]; then
        clear
        echo -e "${gl_zi}>>> 命令行目录批量模式 QSV‑hvc1${gl_bai}"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        echo -e "${gl_hui}源 目 录: ${gl_bai}${src}"
        echo -e "${gl_hui}输出目录: ${gl_bai}${out_dir}"
        echo -e "${gl_hui}后台运行: ${gl_bai}${run_bg}"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        local files=()
        while IFS= read -r -d '' f; do
            [[ -n "$f" ]] && files+=("$f")
        done < <(scan_videos "$src")
        local cnt=${#files[@]}
        if (( cnt == 0 )); then
            echo -e "${gl_hong}错误:目录内未找到支持的视频文件${gl_bai}"
            return 1
        fi
        echo -e "${gl_lv}找到 ${gl_huang}${cnt}${gl_lv} 个待处理视频${gl_bai}"
        if [[ "$run_bg" == "true" ]]; then
            local tmpfiles
            tmpfiles=$(mktemp /tmp/encode_XXXXXX.files)
            printf '%s\0' "${files[@]}" > "$tmpfiles"
            local pid
            pid=$(launch_bg "$out_dir" "$use_suffix" "$tmpfiles")
            echo -e "${gl_lv}✅ 后台任务已启动 (PID: ${pid})${gl_bai}"
            echo -e "${gl_hui}总 日 志:tail -f ${out_dir}/batch_main.log${gl_bai}"
            echo -e "${gl_hui}强制停止:pkill -9 -f ffmpeg; rm -f ${LOCKFILE}"
            return 0
        fi
        run_queue "$out_dir" "$use_suffix" "${files[@]}"
        return $?
    fi
    if [[ ! -f "$src" ]]; then
        echo -e "${gl_hong}错误:文件不存在: ${src}${gl_bai}"
        return 1
    fi
    local fname=$(basename "$src")
    local name="${fname%.*}"
    local ext="${fname##*.}"
    local dst
    if [[ "$use_suffix" == "true" ]]; then
        dst="${out_dir}/${name}_HEVC-hvc1.${ext}"
    else
        dst="${out_dir}/${fname}"
    fi
    local log="${out_dir}/${name}.log"
    echo -e ""
    echo -e "${gl_zi}>>> 命令行单文件转码 QSV‑hvc1${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    echo -e "${gl_hui}输入: ${gl_bai}${src}"
    echo -e "${gl_hui}输出: ${gl_bai}${dst}"
    echo -e "${gl_hui}日志: ${gl_bai}${log}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    if [[ -f "$dst" ]]; then
        echo -e "${gl_huang}跳过(已存在): ${dst}${gl_bai}"
        return 0
    fi
    if [[ "$run_bg" == "true" ]]; then
        env \
            "PATH=$PATH" "LD_LIBRARY_PATH=${LD_LIBRARY_PATH:-}" \
            "QSV_PRESET=${QSV_PRESET}" "QSV_BV=${QSV_BV}" \
            "QSV_MAXRATE=${QSV_MAXRATE}" "QSV_BUFSIZE=${QSV_BUFSIZE}" \
            "MOVFLAGS_FASTSTART=${MOVFLAGS_FASTSTART}" "FFMPEG_THREADS=${FFMPEG_THREADS}" \
            "PROBESIZE=${PROBESIZE}" "AVIOFLAGS=${AVIOFLAGS}" \
        setsid bash -c "
            $(declare -f list_color_init abspath do_encode)
            list_color_init
            do_encode '${src}' '${dst}' '${log}'
        " >/dev/null 2>&1 &
        disown
        echo -e "${gl_lv}✅ 单文件后台启动 PID:$!${gl_bai}"
        echo -e "${gl_hui}查看日志: ${gl_lan}tail -f ${log}${gl_bai}"
        echo -e "${gl_hui}结束进程: ${gl_lan}pkill -9 -f ffmpeg${gl_bai}"
        return 0
    fi
    do_encode "$src" "$dst" "$log"
    return $?
}

main() {
    lock_acquire
    install_deps || exit 1
    if [[ $# -ge 1 ]]; then
        cli_mode "$@"
    else
        interactive_mode
    fi
}
main "$@"

视频截图对比(清晰度)

# ====================== 全局变量定义 ======================
SRC_VIDEO="/vol2/1000/tmp/071222.mp4"          # 原始源视频绝对路径
DST_VIDEO="/vol2/1000/tmp/071222_HEVC-hvc1.mp4"  # 转码后视频绝对路径
OUT_SNAP="/vol2/1000/tmp/compare_snapshot.png" # 输出对比截图保存绝对路径
SNAP_TIME="00:03:26"                           # 截图时间点,源视频与转码视频同一时刻
TEXT_LEFT="H.264"                               # 左侧画面文字标注
TEXT_RIGHT="HEVC hvc1"                          # 右侧画面文字标注

# ====================== 生成左右并排对比截图 ======================
ffmpeg \
-ss "${SNAP_TIME}" -i "${SRC_VIDEO}" \
-ss "${SNAP_TIME}" -i "${DST_VIDEO}" \
-filter_complex \
"[0:v]scale=960:-1,drawtext=fontsize=32:text='${TEXT_LEFT}':x=10:y=10:fontcolor=white:bordercolor=black:borderw=2[left];
 [1:v]scale=960:-1,drawtext=fontsize=32:text='${TEXT_RIGHT}':x=10:y=10:fontcolor=white:bordercolor=black:borderw=2[right];
 [left][right]hstack[v]" \
-map "[v]" \
-frames:v 1 \
-y "${OUT_SNAP}"

创建本地脚本

new_script="ffmpeg_video_hvc1.sh"

cat > "$new_script" << 'EOF'
#!/bin/bash

# 粘贴上方脚本源码

EOF

# 保留本地脚本,去掉 rm -f "$new_script"
chmod +x "$new_script" && ./"$new_script" && rm -f "$new_script"

相关命令