ffmpeg音视频处理大纲

FFmpeg 是一个功能强大的开源音视频处理工具集,其核心代码以 C 语言实现。下面从源码角度分析 FFmpeg 如何实现转码、压缩、提取、截取、拼接、合并和录屏等功能:

一、FFmpeg 核心架构与数据结构

FFmpeg 的源码结构围绕以下核心组件展开:

  • libavformat:输入 / 输出格式处理(如 MP4、MKV、RTSP 等)
  • libavcodec:编解码库(如 H.264、AAC、VP9 等)
  • libavutil:工具库(内存管理、数学运算、错误处理等)
  • libswscale:图像缩放与格式转换
  • libswresample:音频重采样与格式转换
  • libavfilter:音视频滤镜系统

关键数据结构包括:

  • AVFormatContext:格式上下文,管理输入 / 输出文件
  • AVCodecContext:编解码器上下文,配置编码 / 解码参数
  • AVStream:媒体流(音频 / 视频 / 字幕)
  • AVPacket:压缩数据(编码后的音视频数据)
  • AVFrame:原始数据(解码后的音视频帧)

二、核心功能源码分析

2.1. 转码(Transcoding)

转码是将输入媒体流解码后重新编码为另一种格式的过程。核心流程在ffmpeg.c的transcode()函数中:

// ffmpeg.c: transcode() 简化版流程
static int transcode(void) {// 1. 打开输入文件并读取流信息if (open_input_file(ifile) < 0) exit_program(1);// 2. 打开输出文件并创建输出流if (open_output_file(ofile) < 0) exit_program(1);// 3. 主循环:读取输入包 -> 解码 -> 编码 -> 写入输出while (!received_sigterm) {// 从输入文件读取一个AVPacketret = get_input_packet(ifile, &pkt);// 找到对应的解码器并解码为AVFrameret = decode(ist->dec_ctx, ist->frame, &got_frame, &pkt);// 处理解码后的AVFrame(可能需要滤镜处理)if (got_frame) {// 转换帧格式(如像素格式、采样率等)filter_frame(ist, ist->filter_frame);// 找到对应的编码器并编码为AVPacketret = encode(ost->enc_ctx, &pkt_out, frame, &got_packet);// 将编码后的AVPacket写入输出文件if (got_packet) write_packet(ofile, &pkt_out, ost);}}// 4. 清理资源close_input_file(ifile);close_output_file(ofile);return 0;
}

2.2. 压缩(Compression)

压缩本质是通过编码器控制比特率。在ffmpeg.c中,编码器参数通过AVCodecContext设置:

// ffmpeg.c: open_output_file() 中设置编码器参数
static AVCodecContext *init_output_stream_encode(OutputStream *ost) {AVCodecContext *avctx = ost->enc_ctx;// 设置视频编码器参数if (avctx->codec_type == AVMEDIA_TYPE_VIDEO) {avctx->bit_rate = ost->bitrate;        // 设置目标比特率avctx->width = ost->source_width;      // 分辨率avctx->height = ost->source_height;avctx->time_base = ost->frame_rate;    // 帧率avctx->gop_size = ost->gop_size;       // I帧间隔// ...其他参数}// 设置音频编码器参数else if (avctx->codec_type == AVMEDIA_TYPE_AUDIO) {avctx->bit_rate = ost->bitrate;        // 音频比特率avctx->sample_rate = ost->sample_rate; // 采样率avctx->channels = avctx->codec->channels; // 声道数// ...其他参数}// 打开编码器ret = avcodec_open2(avctx, codec, &opts);return avctx;
}

2.3. 提取(Extraction)

提取特定流(如仅提取音频或视频)通过禁用不需要的流实现:

// ffmpeg.c: open_input_file() 中设置流选择
static int open_input_file(InputFile *f) {// 打开输入文件ret = avformat_open_input(&f->ctx, filename, fmt, &format_opts);// 读取流信息ret = avformat_find_stream_info(f->ctx, NULL);// 选择需要的流(如只选视频流)for (i = 0; i < f->ctx->nb_streams; i++) {AVStream *st = f->ctx->streams[i];if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {f->streams[i]->discard = 0; // 保留视频流} else {f->streams[i]->discard = AVDISCARD_ALL; // 丢弃其他流}}return 0;
}

2.4. 截取(Trimming)

截取通过设置输入文件的开始时间(-ss)和持续时间(-t)实现:

// ffmpeg.c: transcode_init() 中处理时间选项
static int transcode_init(void) {// 处理输入文件的起始时间(-ss)if (ifile->start_time != AV_NOPTS_VALUE) {if (ifile->ctx->start_time != AV_NOPTS_VALUE) {ifile->ts_offset = ifile->ctx->start_time;}avformat_seek_file(ifile->ctx, -1, INT64_MIN, ifile->start_time, INT64_MAX, 0);}// 处理持续时间(-t)if (ofile->recording_time != INT64_MAX) {ost->max_frames = av_rescale_q(ofile->recording_time,AV_TIME_BASE_Q,ost->st->time_base);}return 0;
}

2.5. 拼接(Concatenation)

拼接多个媒体文件通过concat协议或滤镜实现。核心是使用libavfilter的concat滤镜:

// ffmpeg_filter.c: configure_filtergraph() 中配置concat滤镜
static int configure_filtergraph(FilterGraph *fg) {// 创建concat滤镜上下文AVFilter *filter = avfilter_get_by_name("concat");AVFilterContext *concat_ctx;// 设置滤镜参数(输入流数量、是否同步等)avfilter_graph_create_filter(&concat_ctx, filter, "concat",options, NULL, fg->graph);// 将多个输入流连接到concat滤镜for (i = 0; i < nb_inputs; i++) {avfilter_link(inputs[i]->filter, 0, concat_ctx, i);}// 配置滤镜图并生效return avfilter_graph_config(fg->graph, NULL);
}

2.6. 合并(Muxing)

合并多个流到一个容器(如音视频合并)通过创建多个输出流实现:

// ffmpeg.c: open_output_file() 中创建输出流
static int open_output_file(OutputFile *of) {// 创建输出格式上下文ret = avformat_alloc_output_context2(&of->ctx, NULL, fmt, filename);// 为每种媒体类型创建输出流for (i = 0; i < nb_output_streams; i++) {OutputStream *ost = output_streams[i];AVStream *st = avformat_new_stream(of->ctx, ost->enc);ost->st = st;// 复制流参数(如编解码器参数)ret = avcodec_parameters_from_context(st->codecpar, ost->enc_ctx);}// 打开输出文件if (!(of->ctx->oformat->flags & AVFMT_NOFILE)) {ret = avio_open(&of->ctx->pb, filename, AVIO_FLAG_WRITE);}// 写入文件头ret = avformat_write_header(of->ctx, &opts);return 0;
}

2.7. 录屏(Screen Recording)

录屏通过libavdevice访问系统设备实现。以 Linux 的 X11 录屏为例:

// ffmpeg.c: open_input_file() 中使用avdevice打开录屏设备
static int open_input_file(InputFile *f) {// 注册设备avdevice_register_all();// 打开X11录屏设备AVInputFormat *iformat = av_find_input_format("x11grab");ret = avformat_open_input(&f->ctx, display_name, iformat, &format_opts);// 设置录屏参数(帧率、分辨率等)if (f->framerate.num) {av_dict_set(&format_opts, "framerate", av_get_time_base_q(), 0);}if (f->video_size) {av_dict_set(&format_opts, "video_size", f->video_size, 0);}// 读取流信息ret = avformat_find_stream_info(f->ctx, NULL);return 0;
}

三、关键技术点解析

3.1. 时间戳处理

FFmpeg 使用复杂的时间戳转换机制,确保音视频同步:

  • AV_TIME_BASE:全局时间基(通常为 1000000,表示微秒)
  • AVStream.time_base:每个流的时间基,用于时间戳换算
  • 关键函数:av_rescale_q() 用于不同时间基之间的转换

3.2. 滤镜系统

libavfilter实现了强大的滤镜链处理:

  • 滤镜图(FilterGraph):由多个滤镜上下文(FilterContext)连接而成
  • 关键函数:avfilter_graph_create_filter()、avfilter_link()

3.3. 多线程处理

FFmpeg 支持编解码多线程:

  • 编码器多线程:通过AVCodecContext.thread_count和thread_type控制
  • 帧级多线程:并行处理多个帧
  • 片级多线程:并行处理同一帧的不同分片

四、总结

FFmpeg 通过模块化设计实现了强大的音视频处理能力:

  • 转码:通过解码 + 编码 + 格式转换实现
  • 压缩:通过编码器参数控制比特率
  • 提取:通过流选择机制丢弃不需要的流
  • 截取:通过时间戳控制实现
  • 拼接:通过 concat 滤镜或协议实现
  • 合并:通过复用器(Muxer)将多个流写入同一容器
  • 录屏:通过设备驱动(libavdevice)获取系统音视频输入

源码中最核心的逻辑位于ffmpeg.c的transcode()函数,它串联了整个处理流程。理解 FFmpeg 的架构和数据结构是深入掌握其功能的关键。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/914585.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/914585.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网络安全小练习

一、docker搭建 1.安装 2.改变镜像源&#xff08;推荐国内镜像源&#xff1a;阿里云镜像源&#xff09; 登录阿里云容器镜像源服务&#xff08; 阿里云登录 - 欢迎登录阿里云&#xff0c;安全稳定的云计算服务平台 &#xff09; 复制系统分配的专属地址 配置 sudo mkdir …

数据结构——顺序表的相关操作

一、顺序表基础认知​1.顺序表的定义与特点​顺序表是数据结构中一种线性存储结构&#xff0c;它将数据元素按照逻辑顺序依次存储在一片连续的物理内存空间中。简单来说&#xff0c;就是用一段地址连续的存储单元依次存放线性表的元素&#xff0c;且元素之间的逻辑关系通过物理…

2025最新国产用例管理工具评测:Gitee Test、禅道、蓝凌测试、TestOps 哪家更懂研发协同?

在快节奏的 DevOps 时代&#xff0c;测试用例管理已不再是 QA 的独角戏&#xff0c;而是穿透需求—开发—测试—交付全流程的核心枢纽。想象一下&#xff0c;如果用例结构混乱&#xff0c;覆盖不全&#xff0c;甚至丢失版本变更历史&#xff0c;不仅协作乱&#xff0c;还影响交…

在线评测系统开发交流

https://space.bilibili.com/700332132?spm_id_from333.788.0.0 实验内容爬虫Web系统设计数据分析实验指导爬虫Web系统设计自然语言处理与信息检索数据可视化评分标准FAQ实验二&#xff1a;在线评测系统实验概述实验内容Step1&#xff1a;题目管理Step2&#xff1a;题目评测S…

Linux操作系统从入门到实战(十)Linux开发工具(下)make/Makefile的推导过程与扩展语法

Linux操作系统从入门到实战&#xff08;十&#xff09;Linux开发工具&#xff08;下&#xff09;make/Makefile的推导过程与扩展语法前言一、 make/Makefile的推导过程1. 先看一个完整的Makefile示例2. make的工作流程&#xff08;1&#xff09;寻找Makefile文件&#xff08;2&…

NFS磁盘共享

步骤&#xff1a;注意事项‌&#xff1a;确保服务端防火墙关闭&#xff0c;或者允许2049端口通信&#xff0c;客户端需具备读写权限。服务器端安装NFS服务器&#xff1a;sudo apt-get install nfs-kernel-server # Debian/Ubuntu sudo yum install nfs-utils # Ce…

ORA-06413: 连接未打开

System.Data.OracleClient.OracleException:ORA-06413: 连接未打开 oracle 报错 ORA-06413: 连接未打开 db.Open();的报错链接未打开&#xff0c;System.Data.OracleClient.OracleException HResult0x80131938 MessageORA-06413: 连接未打开 关于ORA-06413错误&#xff08;…

【PCIe 总线及设备入门学习专栏 5.1.2 -- PCIe EP core_rst_n 与 app_rst_n】

文章目录 app_rst_n 和 core_rst_n 的作用1. core_rst_n — PCIe 控制器内部逻辑复位作用控制方式2. app_rst_n — 应用层/用户逻辑复位作用特点两者关系图示:示例流程(Synopsys EP)rst_sync[3] 的作用详解(复位同步逻辑)为什么使用 rst_sync[3]?图示说明Synopsys 官方手…

Python初学者笔记第二十期 -- (文件IO)

第29节课 文件IO 在编程中&#xff0c;文件 I/O&#xff08;输入/输出&#xff09;允许程序与外部文件进行数据交互。Python 提供了丰富且易用的文件 I/O 操作方法&#xff0c;能让开发者轻松实现文件的读取、写入和修改等操作。 IO交互方向 从硬盘文件 -> 读取数据 -> 内…

Java JUC包概述

Java 的 java.util.concurrent&#xff08;简称 JUC&#xff09;包是 JDK 5 及以后引入的并发编程工具包&#xff0c;旨在解决传统线程模型&#xff08;如 synchronized、wait/notify&#xff09;的局限性&#xff0c;提供更灵活、高效、可扩展的并发编程组件。它极大简化了多线…

LeetCode--44.通配符匹配

前言&#xff1a;不知不觉又断更一天了&#xff0c;其实昨天就把这道题写得差不多了&#xff0c;只是刚好在力扣里面看见了一种新的解法&#xff0c;本来想写出来的&#xff0c;但是我把它推到今天了&#xff0c;因为太晚了&#xff0c;但是今天又睡懒觉了&#xff0c;所以我直…

WHAT - 依赖管理工具 CocoaPods

文章目录1. 什么是 CocoaPods&#xff1f;2. 如何安装 CocoaPods&#xff1f;(1) 确保已安装 Ruby&#xff08;macOS 默认自带&#xff09;(2) 安装 CocoaPods(3) 验证安装3. 在 React Native 项目中使用 CocoaPods(1) 进入 iOS 目录(2) 初始化 Podfile&#xff08;如果不存在&…

C++ Boost Aiso TCP 网络聊天(服务端客户端一体化)

代码功能说明: 程序模式: 主动连接模式:当用户指定对端 IP 和端口时,尝试连接到对端被动监听模式:当用户未指定对端 IP 时,等待其他节点连接线程模型: 主线程:处理用户输入和消息发送接收线程:后台接收并显示对端消息关键组件: std::atomic<bool> connected:原…

WeakAuras 5.12.9 Ekkles lua

3.45猎人宝宝狼 技能恢复宏已知3.45BUG RL技能位会清空&#xff0c;小退大退 BB技能全部激活&#xff0c;修复以前可用宏一键恢复状态-------方法一&#xff1a;宏命令---------------------------------------------------------#showtooltip 狂怒之嚎 /petautocaston [btn:1]…

对于编写PID过程中的问题

当stm32RCT6使用位置环pid控制麦轮转动一定路程时&#xff0c;在这个时间段内想让一边轮胎速度加大应该怎么做&#xff1f;比如我pid的目标脉冲值为9000&#xff0c;在运行到3000的时候车偏左了&#xff0c;那我应该怎样让他回正&#xff0c;我想到的办法是增加其最大的脉冲值&…

LeetCode|Day13|88. 合并两个有序数组|Python刷题笔记

LeetCode&#xff5c;Day13&#xff5c;88. 合并两个有序数组&#xff5c;Python刷题笔记 &#x1f5d3;️ 本文属于【LeetCode 简单题百日计划】系列 &#x1f449; 点击查看系列总目录 >> &#x1f4cc; 题目简介 题号&#xff1a;88. 合并两个有序数组 难度&#xf…

【C++】初识C++(1)

个人主页&#xff1a;我要成为c嘎嘎大王 希望这篇小小文章可以让你有所收获&#xff01; 目录 前言 一、C的第一个程序 二、命名空间 2.1 namespace 的价值 2.2 namespace 的定义 2.2.1 正常的命名空间定义 2.2.2 命名空间可以嵌套 2.2.3 匿名命名空间 2.2.4 同名的name…

在新闻资讯 APP 中添加不同新闻分类页面,通过 ViewPager2 实现滑动切换

在新闻资讯 APP 中添加不同新闻分类页面&#xff0c;通过 ViewPager2 实现滑动切换 核心组件的作用 ViewPager2&#xff1a;是 ViewPager 的升级版&#xff0c;基于RecyclerView实现&#xff0c;支持水平 / 垂直滑动、RTL&#xff08;从右到左&#xff09;布局&#xff0c;且修…

vuex操作state为什么要使用mutations作为规范而不是直接修改state

1. 状态变更的可追踪性 (Trackable Changes)Devtools 集成&#xff1a;Vue Devtools 可以捕获每次 mutation 的执行记录&#xff0c;记录变更前后的 state 快照、参数和调用栈。直接修改 state&#xff1a;Devtools 无法检测到变更来源&#xff0c;导致调试困难&#xff08;如无…

Spring AI 系列之九 - RAG-入门

之前做个几个大模型的应用&#xff0c;都是使用Python语言&#xff0c;后来有一个项目使用了Java&#xff0c;并使用了Spring AI框架。随着Spring AI不断地完善&#xff0c;最近它发布了1.0正式版&#xff0c;意味着它已经能很好的作为企业级生产环境的使用。对于Java开发者来说…