掘金 人工智能 08月18日
一行命令,玩转所有主流音视频格式!一站式音视频处理工具——FFmpeg本地部署教程
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文详细介绍了在Ubuntu 22.04 LTSC环境下部署FFmpeg的过程,包括更新基础软件包、配置国内apt源、安装Miniconda以及FFmpeg本身。此外,还演示了如何使用Gradio构建一个多媒体处理Web应用,该应用支持格式转换、片段剪辑、尺寸调整、音频提取、比特率调整和帧提取等多种功能,并集成了GPU加速(NVIDIA/Intel)以提升处理效率。代码示例涵盖了文件类型检测、GPU支持检测、临时文件清理以及FFmpeg命令的执行与错误处理。

📦 **环境准备与FFmpeg安装**:文章首先指导用户在Ubuntu 22.04 LTSC系统上完成基础环境的准备,包括更新软件包列表、配置国内阿里源以加速下载,并安装了Vim、wget、git等常用工具。随后,详细介绍了Miniconda的安装流程,并配置了清华源以加速pip包的安装。最后,通过`sudo apt install ffmpeg -y`命令成功安装了FFmpeg,为后续的多媒体处理奠定了基础。

🐍 **Python环境与Gradio集成**:为了实现多媒体处理的Web界面,文章创建了一个名为`gradio`的conda虚拟环境,并指定了Python版本为3.12,随后安装了`gradio`和`numpy`依赖库。这使得开发者能够在一个隔离且配置好的环境中开发和运行Gradio应用。

🚀 **多媒体处理功能实现**:核心部分是`ffmpeg_processor.py`脚本,它利用Gradio构建了一个功能丰富的多媒体处理界面。该脚本实现了多种操作,包括文件格式转换、片段剪辑(支持开始/结束时间)、尺寸调整、音频提取、比特率调整以及从视频中提取单帧图像。用户可以上传文件或选择本地文件进行处理。

⚡ **GPU加速支持**:为了提高处理速度,该应用集成了GPU加速功能。通过`check_gpu_support`函数,脚本能检测NVIDIA(CUDA)和Intel(VAAPI)GPU的存在及其对FFmpeg编码器的支持。在执行FFmpeg命令时,如果启用了GPU加速且检测到可用GPU,则会优先使用GPU解码和编码,并在GPU处理失败时提供CPU回退机制,确保了处理的稳定性和效率。

📂 **文件管理与用户体验**:脚本还包含了对临时文件的自动清理机制,以防止磁盘空间被占满。同时,提供了本地文件管理功能,允许用户上传文件或从预设的`local_files`目录中选择文件,并显示详细的文件信息(类型、格式、大小),提升了用户的使用体验和便捷性。

一、介绍

FFmpeg 是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。采用 LGPL 或 GPL 许可证。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库 libavcodec,为了保证高可移植性和编解码质量,libavcodec 里很多 code 都是从头开发的。

二、部署过程

基础环境最低要求说明:

环境名称版本信息 1
Ubuntu22.04.4 LTS
CudaV12.8
Python3.12
NVIDIA CorporationRTX 4090

1. 更新基础软件包

查看系统版本信息

# 查看系统版本信息,包括ID(如ubuntu、centos等)、版本号、名称、版本号ID等cat /etc/os-release

配置 apt 国内源

# 更新软件包列表apt-get update

这个命令用于更新本地软件包索引。它会从所有配置的源中检索最新的软件包列表信息,但不会安装或升级任何软件包。这是安装新软件包或进行软件包升级之前的推荐步骤,因为它确保了您获取的是最新版本的软件包。

# 安装 Vim 编辑器apt-get install -y vim

这个命令用于安装 Vim 文本编辑器。-y 选项表示自动回答所有的提示为“是”,这样在安装过程中就不需要手动确认。Vim 是一个非常强大的文本编辑器,广泛用于编程和配置文件的编辑。

为了安全起见,先备份当前的 sources.list 文件之后,再进行修改:

# 备份现有的软件源列表cp /etc/apt/sources.list /etc/apt/sources.list.bak

这个命令将当前的 sources.list 文件复制为一个名为 sources.list.bak 的备份文件。这是一个好习惯,因为编辑 sources.list 文件时可能会出错,导致无法安装或更新软件包。有了备份,如果出现问题,您可以轻松地恢复原始的文件。

# 编辑软件源列表文件vim /etc/apt/sources.list

这个命令使用 Vim 编辑器打开 sources.list 文件,以便您可以编辑它。这个文件包含了 APT(Advanced Package Tool)用于安装和更新软件包的软件源列表。通过编辑这个文件,您可以添加新的软件源、更改现有软件源的优先级或禁用某些软件源。

在 Vim 中,您可以使用方向键来移动光标,i 键进入插入模式(可以开始编辑文本),Esc 键退出插入模式,:wq 命令保存更改并退出 Vim,或 :q! 命令不保存更改并退出 Vim。

编辑 sources.list 文件时,请确保您了解自己在做什么,特别是如果您正在添加新的软件源。错误的源可能会导致软件包安装失败或系统安全问题。如果您不确定,最好先搜索并找到可靠的源信息,或者咨询有经验的 Linux 用户。

使用 Vim 编辑器打开 sources.list 文件,复制以下代码替换 sources.list 里面的全部代码,配置 apt 国内阿里源。

deb http://mirrors.aliyun.com/ubuntu/ jammy main restricted universe multiversedeb-src http://mirrors.aliyun.com/ubuntu/ jammy main restricted universe multiversedeb http://mirrors.aliyun.com/ubuntu/ jammy-security main restricted universe multiversedeb-src http://mirrors.aliyun.com/ubuntu/ jammy-security main restricted universe multiversedeb http://mirrors.aliyun.com/ubuntu/ jammy-updates main restricted universe multiversedeb-src http://mirrors.aliyun.com/ubuntu/ jammy-updates main restricted universe multiversedeb http://mirrors.aliyun.com/ubuntu/ jammy-backports main restricted universe multiversedeb-src http://mirrors.aliyun.com/ubuntu/ jammy-backports main restricted universe multiverse

安装常用软件和工具

# 更新源列表,输入以下命令:apt-get update# 更新系统软件包,输入以下命令:apt-get upgrade# 安装常用软件和工具,输入以下命令:apt-get -y install vim wget git git-lfs unzip lsof net-tools gcc cmake build-essential

出现以下页面,说明国内 apt 源已替换成功,且能正常安装 apt 软件和工具

2. 安装 Miniconda

# 下载 Miniconda 安装脚本wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh# 运行 Miniconda 安装脚本bash Miniconda3-latest-Linux-x86_64.sh# 初次安装需要激活 base 环境source ~/.bashrc

按下回车键(enter)

输入 yes

输入 yes

安装成功如下图所示

pip 配置清华源加速

# 编辑 /etc/pip.conf 文件vim  /etc/pip.conf

加入以下代码

[global]index-url = https://pypi.tuna.tsinghua.edu.cn/simple

注意事项:

3. 安装FFmpeg

# 安装ffmpegsudo apt updatesudo apt install ffmpeg -y

4. 创建虚拟环境

# 创建一个名为 gradio的新虚拟环境(名字可自定义),并指定 Python 版本为 3.12conda create -n gradio python=3.12 -y

5. 安装gradio依赖库

# 激活 gradio 虚拟环境conda activate gradio# 在 gradio 环境中安装依赖pip install gradio numpy

6. 编写并运行 ffmpeg_processor.py 文件

#编写ffmpeg_processor.pyvim ffmpeg_processor.py#以下代码片段是一个gradio网页的示例import gradio as grimport subprocessimport osimport uuidimport mimetypesimport jsonimport shleximport timefrom datetime import timedeltafrom pathlib import Path# 临时文件目录配置TEMP_DIR = "temp_files"os.makedirs(TEMP_DIR, exist_ok=True)CLEANUP_THRESHOLD = timedelta(hours=1)  # 清理1小时前的临时文件# 本地文件目录配置LOCAL_FILES_DIR = "local_files"os.makedirs(LOCAL_FILES_DIR, exist_ok=True)# GPU支持检测def check_gpu_support():    """检测GPU支持情况"""    try:        # 检查NVIDIA GPU        result = subprocess.run(['nvidia-smi'], capture_output=True, text=True, timeout=5)        if result.returncode == 0:            # 检查FFmpeg是否支持CUDA            ffmpeg_result = subprocess.run(['ffmpeg', '-hwaccels'], capture_output=True, text=True, timeout=5)            if 'cuda' in ffmpeg_result.stdout.lower():                # 检查编码器支持                encoders_result = subprocess.run(['ffmpeg', '-encoders'], capture_output=True, text=True, timeout=5)                if 'h264_nvenc' in encoders_result.stdout.lower():                    return 'nvidia'        # 检查Intel GPU        lspci_result = subprocess.run(['lspci'], capture_output=True, text=True, timeout=5)        if 'intel' in lspci_result.stdout.lower() or 'display' in lspci_result.stdout.lower():            ffmpeg_result = subprocess.run(['ffmpeg', '-hwaccels'], capture_output=True, text=True, timeout=5)            if 'vaapi' in ffmpeg_result.stdout.lower():                return 'intel'    except:        pass    return 'cpu'# 检测GPU类型GPU_TYPE = check_gpu_support()def clean_temp_files():    """清理过期的临时文件"""    now = time.time()    for filename in os.listdir(TEMP_DIR):        file_path = os.path.join(TEMP_DIR, filename)        file_time = os.path.getmtime(file_path)        # 删除超过阈值的文件        if now - file_time > CLEANUP_THRESHOLD.total_seconds():            try:                os.remove(file_path)            except Exception as e:                print(f"清理临时文件失败: {file_path}, 错误: {e}")def get_file_type(file_path):    """获取文件类型(视频/音频/图片)"""    if not file_path:        return None      mime_type, _ = mimetypes.guess_type(file_path)    if mime_type:        if mime_type.startswith('video'):            return 'video'        elif mime_type.startswith('audio'):            return 'audio'        elif mime_type.startswith('image'):            return 'image'    return 'unknown'def get_available_formats(file_type):    """根据文件类型获取可用转换格式"""    if file_type == 'video':        return ["mp4", "avi", "mov", "mkv", "flv", "webm", "gif"]    elif file_type == 'audio':        return ["mp3", "wav", "flac", "aac", "ogg", "m4a"]    elif file_type == 'image':        return ["jpg", "jpeg", "png", "bmp", "webp", "gif"]    return ["mp4", "avi", "mov", "mp3", "wav", "jpg", "png"]  # 默认格式def get_gpu_encoding_params(codec='h264'):    """获取GPU编码参数"""    if GPU_TYPE == 'nvidia':        if codec == 'h264':            return ['-c:v', 'h264_nvenc']        elif codec == 'hevc':            return ['-c:v', 'hevc_nvenc']    elif GPU_TYPE == 'intel':        if codec == 'h264':            return ['-c:v', 'h264_vaapi', '-vaapi_device', '/dev/dri/renderD128']        elif codec == 'hevc':            return ['-c:v', 'hevc_vaapi', '-vaapi_device', '/dev/dri/renderD128']    return []  # CPU编码def run_ffmpeg_command(cmd_parts, input_file_path, output_ext, use_gpu=False, fallback_to_cpu=True):    """执行FFmpeg命令并返回输出文件路径"""    output_filename = f"{uuid.uuid4()}.{output_ext}"    output_path = os.path.join(TEMP_DIR, output_filename)      # 尝试GPU加速    if use_gpu and GPU_TYPE != 'cpu':        # 构建GPU命令        gpu_command = ['ffmpeg', '-y']          # GPU解码支持        if GPU_TYPE == 'nvidia':            gpu_command.extend(['-hwaccel', 'cuda', '-hwaccel_output_format', 'cuda'])        elif GPU_TYPE == 'intel':            gpu_command.extend(['-hwaccel', 'vaapi', '-hwaccel_device', '/dev/dri/renderD128'])          gpu_command.extend(['-i', input_file_path])        gpu_command.extend(cmd_parts)        gpu_command.append(output_path)          try:            print(f"尝试GPU加速命令: {' '.join(gpu_command)}")            result = subprocess.run(                gpu_command,                check=True,                capture_output=True,                text=True,                timeout=21600  # 360分钟超时            )              if os.path.exists(output_path):                print("GPU加速处理成功")                return output_path        except subprocess.CalledProcessError as e:            error_msg = e.stderr if e.stderr else e.stdout            print(f"GPU加速失败: {error_msg}")            if not fallback_to_cpu:                raise gr.Error(f"GPU处理失败: {error_msg[:200]}...")        except Exception as e:            print(f"GPU加速异常: {str(e)}")            if not fallback_to_cpu:                raise gr.Error(f"GPU处理异常: {str(e)}")          # 如果GPU失败且允许回退,则继续使用CPU      # CPU处理(或GPU回退)    print("使用CPU处理")    cpu_command = ['ffmpeg', '-y', '-i', input_file_path]    cpu_command.extend(cmd_parts)    cpu_command.append(output_path)      try:        print(f"执行CPU命令: {' '.join(cpu_command)}")        result = subprocess.run(            cpu_command,            check=True,            capture_output=True,            text=True,            timeout=21600  # 360分钟超时        )          # 验证输出文件        if not os.path.exists(output_path):            raise gr.Error(f"输出文件未生成: {output_path}")          return output_path    except subprocess.CalledProcessError as e:        error_msg = f"FFmpeg执行失败: {e.stderr if e.stderr else e.stdout}"        print(f"错误命令: {' '.join(shlex.quote(arg) for arg in cpu_command)}")        print(error_msg)        raise gr.Error(f"处理失败: {error_msg[:200]}...")  # 截断长错误信息    except Exception as e:        raise gr.Error(f"发生未知错误: {str(e)}")def process_media(file_info_dict, operation, format_convert_val, clip_start_val, clip_end_val, clip_format_val,                  resize_width_val, resize_height_val, extract_audio_format_val,                  bitrate_input_val, extract_frame_time_val, extract_frame_format_val,                  use_gpu_accel):    """处理多媒体文件的主函数"""    if file_info_dict is None or not file_info_dict.get('path'):        raise gr.Error("请先选择文件")      use_gpu = use_gpu_accel and GPU_TYPE != 'cpu'      # 获取文件路径和类型    file_path = file_info_dict['path']    file_type = file_info_dict.get('type') or get_file_type(file_path)      # 获取原始文件扩展名(不含点)    orig_ext = os.path.splitext(file_path)[1][1:].lower() if file_path else "mp4"    if not orig_ext:  # 处理无扩展名的情况        orig_ext = "mp4"    # 根据操作类型选择处理方式    try:        if operation == "格式转换":            output_ext = format_convert_val            if output_ext == "original":                output_ext = orig_ext              # GPU加速编码            if use_gpu and file_type == 'video' and output_ext in ['mp4', 'mov', 'avi']:                gpu_params = get_gpu_encoding_params('h264')                if gpu_params:                    return run_ffmpeg_command(gpu_params, file_path, output_ext, use_gpu)              return run_ffmpeg_command([], file_path, output_ext, use_gpu)          elif operation == "片段剪辑":            start = float(clip_start_val)            end = float(clip_end_val)            output_ext = clip_format_val            if output_ext == "original":                output_ext = orig_ext              # GPU加速编码            if use_gpu and file_type == 'video' and output_ext in ['mp4', 'mov', 'avi']:                # 对于片段剪辑,先尝试流复制(最快),如果失败再用GPU编码                try:                    return run_ffmpeg_command([                        "-ss", str(start),                         "-to", str(end),                        "-c", "copy"  # 使用流复制提高速度                    ], file_path, output_ext, False, False)  # 不使用GPU,不回退                except:                    # 如果流复制失败,使用GPU编码                    gpu_params = get_gpu_encoding_params('h264')                    if gpu_params:                        return run_ffmpeg_command([                            "-ss", str(start),                             "-to", str(end)                        ] + gpu_params, file_path, output_ext, use_gpu)              return run_ffmpeg_command([                "-ss", str(start),                 "-to", str(end),                "-c", "copy"  # 使用流复制提高速度            ], file_path, output_ext, use_gpu)          elif operation == "调整尺寸":            width = int(resize_width_val)            height = int(resize_height_val)              # GPU加速缩放            if use_gpu and file_type == 'video':                if GPU_TYPE == 'nvidia':                    # 使用scale_npp而不是scale_cuda,更稳定                    return run_ffmpeg_command([                        "-vf", f"scale_npp={width}:{height}:force_original_aspect_ratio=decrease",                        "-c:v", "h264_nvenc"                    ], file_path, orig_ext, use_gpu)                elif GPU_TYPE == 'intel':                    return run_ffmpeg_command([                        "-vf", f"scale_vaapi={width}:{height}:force_original_aspect_ratio=decrease",                        "-c:v", "h264_vaapi",                        "-vaapi_device", "/dev/dri/renderD128"                    ], file_path, orig_ext, use_gpu)              return run_ffmpeg_command([                "-vf", f"scale={width}:{height}:force_original_aspect_ratio=decrease",                "-c:a", "copy"  # 保留原始音频            ], file_path, orig_ext, use_gpu)          elif operation == "提取音频":            output_ext = extract_audio_format_val            if output_ext == "original":                output_ext = "mp3"            audio_codec = "copy" if output_ext == orig_ext and file_type == 'audio' else "libmp3lame"            return run_ffmpeg_command([                "-vn",          # 禁用视频流                "-acodec", audio_codec            ], file_path, output_ext, use_gpu)          elif operation == "调整比特率":            bitrate = bitrate_input_val            # 根据文件类型选择比特率参数            bitrate_param = "-b:a" if file_type == 'audio' else "-b:v"              # GPU加速编码            if use_gpu and file_type == 'video':                gpu_params = get_gpu_encoding_params('h264')                if gpu_params:                    return run_ffmpeg_command([bitrate_param, bitrate] + gpu_params, file_path, orig_ext, use_gpu)              return run_ffmpeg_command([bitrate_param, bitrate], file_path, orig_ext, use_gpu)          elif operation == "提取帧":            timestamp = float(extract_frame_time_val)            output_ext = extract_frame_format_val            if output_ext == "original":                output_ext = "jpg"            return run_ffmpeg_command([                "-ss", str(timestamp),                 "-vframes", "1",                "-q:v", "2"  # 控制图片质量            ], file_path, output_ext, use_gpu)          else:            raise gr.Error("未选择有效操作")      except Exception as e:        # 如果GPU处理失败,自动回退到CPU处理        if use_gpu:            print(f"GPU处理失败,回退到CPU处理: {str(e)}")            # 重新调用函数,但禁用GPU            return process_media(file_info_dict, operation, format_convert_val, clip_start_val, clip_end_val, clip_format_val,                               resize_width_val, resize_height_val, extract_audio_format_val,                               bitrate_input_val, extract_frame_time_val, extract_frame_format_val,                               False)        else:            raise edef get_local_files():    """获取本地文件列表"""    files = []    if os.path.exists(LOCAL_FILES_DIR):        for file in os.listdir(LOCAL_FILES_DIR):            file_path = os.path.join(LOCAL_FILES_DIR, file)            if os.path.isfile(file_path):                files.append(file)    return filesdef refresh_local_files():    """刷新本地文件列表"""    return gr.update(choices=get_local_files())def use_local_file(selected_file):    """使用本地文件"""    if selected_file:        file_path = os.path.join(LOCAL_FILES_DIR, selected_file)        if os.path.exists(file_path):            file_type = get_file_type(file_path)            file_ext = os.path.splitext(file_path)[1][1:].lower() or "未知"            file_size = f"{os.path.getsize(file_path)/1024/1024:.2f} MB"              info_text = f"""            **文件信息:**              - 类型: {file_type or '未知'}              - 格式: {file_ext}              - 大小: {file_size}            """            return {"path": file_path, "type": file_type, "ext": file_ext}, info_text    return {"path": None, "type": None, "ext": None}, "**文件信息:** 未选择文件"# 定义操作参数OPERATIONS = {    "格式转换": {        "description": "将文件转换为其他格式",        "params": [            {                "name": "format",                 "label": "目标格式",                 "type": "dropdown",                 "default": "mp4",            }        ]    },    "片段剪辑": {        "description": "截取文件中的一段",        "params": [            {"name": "start_time", "label": "开始时间(秒)", "type": "number", "default": 0, "minimum": 0},            {"name": "end_time", "label": "结束时间(秒)", "type": "number", "default": 10, "minimum": 0},            {"name": "format", "label": "输出格式", "type": "dropdown", "default": "original"}        ]    },    "调整尺寸": {        "description": "调整视频/图片尺寸",        "params": [            {"name": "width", "label": "宽度", "type": "number", "default": 640, "minimum": 10},            {"name": "height", "label": "高度", "type": "number", "default": 480, "minimum": 10}        ]    },    "提取音频": {        "description": "从视频中提取音频",        "params": [            {"name": "format", "label": "音频格式", "type": "dropdown", "default": "mp3"}        ]    },    "调整比特率": {        "description": "调整视频/音频质量",        "params": [            {"name": "bitrate", "label": "比特率", "type": "text",              "default": "128k", "placeholder": "例如: 1M, 500k"}        ]    },    "提取帧": {        "description": "从视频中提取单帧图像",        "params": [            {"name": "timestamp", "label": "截取时间点(秒)", "type": "number", "default": 5, "minimum": 0},            {"name": "format", "label": "图片格式", "type": "dropdown", "default": "jpg"}        ]    }}# 创建Gradio界面with gr.Blocks(title="FFmpeg多媒体处理器") as demo:    gr.Markdown(f"""    # 🎥 FFmpeg多媒体处理工具    **上传文件或选择本地文件进行处理**      **系统信息**: GPU加速: {'✅ 已启用 (' + GPU_TYPE.upper() + ')' if GPU_TYPE != 'cpu' else '❌ 未检测到GPU'}    **注意事项**:①不是所有格式都支持GPU加速,不支持时默认调用CPU;②程序超时设置为6小时,小型文件处理过久请尝试重启;③本地文件请上传至/local_files/;④    """)      # 状态变量    file_info = gr.State({"path": None, "type": None, "ext": None})    current_operation = gr.State("格式转换")      with gr.Tabs():        with gr.TabItem("上传文件"):            with gr.Row():                with gr.Column(scale=3):                    file_input = gr.File(                        label="上传文件",                         type="filepath",                        file_types=["video", "audio", "image"]                    )                with gr.Column(scale=2):                    operation_select = gr.Dropdown(                        label="选择操作",                        choices=list(OPERATIONS.keys()),                        value="格式转换"                    )          with gr.TabItem("本地文件"):            with gr.Row():                with gr.Column(scale=3):                    local_file_dropdown = gr.Dropdown(                        label="选择本地文件",                        choices=get_local_files(),                        interactive=True                    )                    with gr.Row():                        refresh_btn = gr.Button("刷新文件列表")                        use_local_btn = gr.Button("使用选中文件", variant="primary")                with gr.Column(scale=2):                    operation_select_local = gr.Dropdown(                        label="选择操作",                        choices=list(OPERATIONS.keys()),                        value="格式转换"                    )      # 文件信息展示    file_info_display = gr.Markdown("**文件信息:** 未选择文件")      # 操作描述区域    operation_info = gr.Markdown()      # GPU加速选项    with gr.Row():        use_gpu_accel = gr.Checkbox(            label=f"使用GPU加速 ({'CUDA' if GPU_TYPE == 'nvidia' else 'VAAPI' if GPU_TYPE == 'intel' else '不支持'})",            value=False,            interactive=GPU_TYPE != 'cpu'        )        gpu_info_text = gr.Markdown(f"*GPU类型: {GPU_TYPE.upper() if GPU_TYPE != 'cpu' else '无'}*")      # 动态参数区域 - 预先创建所有可能的参数组件    with gr.Column() as params_container:        # 格式转换参数        with gr.Group(visible=True) as format_convert_group:            format_convert_dropdown = gr.Dropdown(                label="目标格式",                choices=["mp4", "avi", "mov", "mkv", "flv", "webm", "gif", "mp3", "wav", "flac", "aac", "ogg", "m4a", "jpg", "jpeg", "png", "bmp", "webp", "original"],                value="mp4",                interactive=True            )          # 片段剪辑参数        with gr.Group(visible=False) as clip_group:            clip_start = gr.Number(                label="开始时间(秒)",                value=0,                minimum=0,                interactive=True            )            clip_end = gr.Number(                label="结束时间(秒)",                value=10,                minimum=0,                interactive=True            )            clip_format = gr.Dropdown(                label="输出格式",                choices=["mp4", "avi", "mov", "mkv", "flv", "webm", "gif", "mp3", "wav", "flac", "aac", "ogg", "m4a", "jpg", "jpeg", "png", "bmp", "webp", "original"],                value="original",                interactive=True            )          # 调整尺寸参数        with gr.Group(visible=False) as resize_group:            resize_width = gr.Number(                label="宽度",                value=640,                minimum=10,                interactive=True            )            resize_height = gr.Number(                label="高度",                value=480,                minimum=10,                interactive=True            )          # 提取音频参数        with gr.Group(visible=False) as extract_audio_group:            extract_audio_format = gr.Dropdown(                label="音频格式",                choices=["mp3", "wav", "flac", "aac", "ogg", "m4a", "original"],                value="mp3",                interactive=True            )          # 调整比特率参数        with gr.Group(visible=False) as bitrate_group:            bitrate_input = gr.Textbox(                label="比特率",                value="128k",                placeholder="例如: 1M, 500k",                interactive=True            )          # 提取帧参数        with gr.Group(visible=False) as extract_frame_group:            extract_frame_time = gr.Number(                label="截取时间点(秒)",                value=5,                minimum=0,                interactive=True            )            extract_frame_format = gr.Dropdown(                label="图片格式",                choices=["jpg", "jpeg", "png", "bmp", "webp", "gif", "original"],                value="jpg",                interactive=True            )      # 所有参数组的引用    all_param_groups = {        "格式转换": format_convert_group,        "片段剪辑": clip_group,        "调整尺寸": resize_group,        "提取音频": extract_audio_group,        "调整比特率": bitrate_group,        "提取帧": extract_frame_group    }      # 输出区域    with gr.Row():        output_file = gr.File(label="处理结果", interactive=False)        with gr.Column():            submit_btn = gr.Button("开始处理", variant="primary")            status_text = gr.Markdown("**处理状态:** 等待操作", elem_id="status-text")    # 更新文件信息    def update_file_info(file_path):        """更新文件信息并返回显示内容"""        if not file_path:            return {"path": None, "type": None, "ext": None}, "**文件信息:** 未选择文件"          file_type = get_file_type(file_path)        file_ext = os.path.splitext(file_path)[1][1:].lower() or "未知"        file_size = f"{os.path.getsize(file_path)/1024/1024:.2f} MB"          info_text = f"""        **文件信息:**          - 类型: {file_type or '未知'}          - 格式: {file_ext}          - 大小: {file_size}        """        return {"path": file_path, "type": file_type, "ext": file_ext}, info_text      # 更新参数UI和格式选项    def update_params_and_formats(operation, file_info_dict):        """更新参数UI可见性和格式选项"""        file_type = file_info_dict.get("type") if file_info_dict else None          # 隐藏所有参数组        group_updates = []        for op_name, group in all_param_groups.items():            if op_name == operation:                group_updates.append(gr.update(visible=True))            else:                group_updates.append(gr.update(visible=False))          # 根据文件类型更新格式选项        video_formats = ["mp4", "avi", "mov", "mkv", "flv", "webm", "gif", "original"]        audio_formats = ["mp3", "wav", "flac", "aac", "ogg", "m4a", "original"]        image_formats = ["jpg", "jpeg", "png", "bmp", "webp", "gif", "original"]          if file_type == 'video':            format_choices = video_formats        elif file_type == 'audio':            format_choices = audio_formats        elif file_type == 'image':            format_choices = image_formats        else:            format_choices = video_formats + audio_formats + image_formats + ["original"]          # 更新各个格式下拉框的选项        format_updates = []        if operation == "格式转换":            format_updates.append(gr.update(choices=format_choices))            format_updates.append(gr.update())  # clip_format            format_updates.append(gr.update())  # extract_audio_format            format_updates.append(gr.update())  # extract_frame_format        elif operation == "片段剪辑":            format_updates.append(gr.update())  # format_convert_dropdown            format_updates.append(gr.update(choices=format_choices))            format_updates.append(gr.update())  # extract_audio_format            format_updates.append(gr.update())  # extract_frame_format        elif operation == "提取音频":            audio_only_formats = ["mp3", "wav", "flac", "aac", "ogg", "m4a", "original"]            format_updates.append(gr.update())  # format_convert_dropdown            format_updates.append(gr.update())  # clip_format            format_updates.append(gr.update(choices=audio_only_formats))            format_updates.append(gr.update())  # extract_frame_format        elif operation == "提取帧":            image_only_formats = ["jpg", "jpeg", "png", "bmp", "webp", "gif", "original"]            format_updates.append(gr.update())  # format_convert_dropdown            format_updates.append(gr.update())  # clip_format            format_updates.append(gr.update())  # extract_audio_format            format_updates.append(gr.update(choices=image_only_formats))        else:            format_updates = [gr.update(), gr.update(), gr.update(), gr.update()]          return group_updates + format_updates      # 更新操作描述    def update_operation_info(operation):        return f"**{operation}**: {OPERATIONS[operation]['description']}"      # 清理临时文件并启动    clean_temp_files()      # 界面事件处理    # 文件上传时更新信息和格式选项    file_input.upload(        update_file_info,        inputs=file_input,        outputs=[file_info, file_info_display]    ).then(        lambda op: update_operation_info(op),        inputs=operation_select,        outputs=operation_info    ).then(        update_params_and_formats,        inputs=[operation_select, file_info],        outputs=[            format_convert_group, clip_group, resize_group,             extract_audio_group, bitrate_group, extract_frame_group,            format_convert_dropdown, clip_format,             extract_audio_format, extract_frame_format        ]    )      # 本地文件操作    refresh_btn.click(        refresh_local_files,        outputs=local_file_dropdown    )      use_local_btn.click(        use_local_file,        inputs=local_file_dropdown,        outputs=[file_info, file_info_display]    ).then(        lambda op: update_operation_info(op),        inputs=operation_select_local,        outputs=operation_info    ).then(        update_params_and_formats,        inputs=[operation_select_local, file_info],        outputs=[            format_convert_group, clip_group, resize_group,             extract_audio_group, bitrate_group, extract_frame_group,            format_convert_dropdown, clip_format,             extract_audio_format, extract_frame_format        ]    )      # 操作变更时更新参数可见性和格式选项    operation_select.change(        update_operation_info,        inputs=operation_select,        outputs=operation_info    ).then(        update_params_and_formats,        inputs=[operation_select, file_info],        outputs=[            format_convert_group, clip_group, resize_group,             extract_audio_group, bitrate_group, extract_frame_group,            format_convert_dropdown, clip_format,             extract_audio_format, extract_frame_format        ]    )      operation_select_local.change(        update_operation_info,        inputs=operation_select_local,        outputs=operation_info    ).then(        update_params_and_formats,        inputs=[operation_select_local, file_info],        outputs=[            format_convert_group, clip_group, resize_group,             extract_audio_group, bitrate_group, extract_frame_group,            format_convert_dropdown, clip_format,             extract_audio_format, extract_frame_format        ]    )      # 文件信息变更时更新格式选项    file_info.change(        update_params_and_formats,        inputs=[operation_select, file_info],        outputs=[            format_convert_group, clip_group, resize_group,             extract_audio_group, bitrate_group, extract_frame_group,            format_convert_dropdown, clip_format,             extract_audio_format, extract_frame_format        ]    )      # 提交处理    submit_btn.click(        lambda: gr.update(value="**处理状态:** 处理中..."),        outputs=status_text    ).then(        process_media,        inputs=[            file_info,            operation_select,            format_convert_dropdown,            clip_start, clip_end, clip_format,            resize_width, resize_height,            extract_audio_format,            bitrate_input,            extract_frame_time, extract_frame_format,            use_gpu_accel        ],        outputs=output_file    ).then(        lambda: gr.update(value="**处理状态:** 处理完成!"),        outputs=status_text    )      # 初始加载    demo.load(        lambda: f"**格式转换**: {OPERATIONS['格式转换']['description']}",        outputs=operation_info    )if __name__ == "__main__":    # 清理旧文件并启动    clean_temp_files()    print(f"请将需要处理的本地文件放入 {LOCAL_FILES_DIR} 目录中")    print(f"本地文件目录: {os.path.abspath(LOCAL_FILES_DIR)}")    demo.launch(        server_name="0.0.0.0",        server_port=8080,        show_error=True,        share=True      )#激活虚拟环境conda activate gradio#运行ffmpeg_processor.pypython ffmpeg_processor.py

三、网页演示

出现以下 gradio 页面,代表已搭建完成。

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

FFmpeg Gradio Ubuntu GPU加速 多媒体处理
相关文章