当前位置: 首页 > news >正文

[FFmpeg] 输入输出访问 | 管道系统 | AVIOContext 与 URLProtocol | 门面模式

链接:https://trac.ffmpeg.org/

docs:FFmpeg

在这里插入图片描述
FFmpeg 是一个强大的多媒体框架,旨在处理媒体处理的各个阶段。

它就像一个数字媒体工厂,包含以下部门:打包/解包(容器处理)、
转译/压缩(编解码器处理)、管理原始视频帧压缩数据包
连接输入/输出源(输入输出访问)、执行编辑和特效(过滤与处理)、
保持所有内容的同步性(时间与速率管理),并提供灵活定制(配置选项)。

概览

在这里插入图片描述

章节列表

  1. 输入输出访问
  2. 容器处理
  3. 压缩媒体数据包
  4. 原始媒体帧
  5. 编解码器处理
  6. 过滤与处理
  7. 时间与速率管理
  8. 配置选项

第一章:输入输出访问

欢迎来到FFmpeg的第一章~

在FFmpeg能够执行诸如转换视频或提取音频等酷炫操作之前,它需要从某处获取多媒体数据并将其发送到另一处。这个首要关键步骤由FFmpeg的输入输出访问层处理。

可将输入输出访问视为连接FFmpeg与外部世界的"管道系统"。

就像房屋需要管道来进出水一样,FFmpeg需要一种方式来输入输出视频、音频和其他数据。该层提供了处理数据流的标准化方式,无论数据是来自计算机本地文件、网络视频流,甚至是网络摄像头等实时捕获设备。

如果没有这种抽象层,FFmpeg将需要完全不同的代码来读取本地.mp4文件(与从网站下载视频或从麦克风捕获相比)。输入输出访问层隐藏了这些差异,提供了一致的接口。

让我们从一个常见用例开始:将视频文件从一种格式转换为另一种格式。假设您有一个名为input.mp4的视频文件,希望另存为output.avi

在命令行中使用ffmpeg工具(底层使用FFmpeg库)时,通常执行如下操作:

ffmpeg -i input.mp4 output.avi

从输入输出角度分析此命令:

  • -i input.mp4:告知FFmpeg使用input.mp4作为输入源,-i选项是标准输入文件或URL指定参数
  • output.avi:指定输出目标,FFmpeg将其识别为文件名并准备写入数据

在此简单命令中,FFmpeg的输入输出访问层负责:

  1. 打开input.mp4进行读取
  2. 分块读取input.mp4数据
  3. 打开output.avi进行写入
  4. 将处理后的数据写入output.avi

-f选项(-format缩写)可显式指定输入输出格式,这对于没有明确头部的原始视频数据等输入尤为重要:

ffmpeg -f rawvideo -video_size 640x480 -pixel_format yuv420p -i input.yuv output.mp4

此处-f rawvideo告知FFmpeg将input.yuv视为原始视频数据,因为.yuv文件本身不包含尺寸或像素格式等格式信息。输入输出层仍负责打开和读取文件,但需要这些额外参数(-video_size, -pixel_format)来理解读取数据的结构。

因此,输入输出访问层的核心职责是获取字节输入发送字节输出

内部机制:管道系统

当运行ffmpeg -i input.mp4 output.avi时,FFmpeg如何处理文件读取?

FFmpeg使用称为协议(或URLProtocol)的组件来了解如何与不同类型数据源和目的地交互。

当提供input.mp4时,FFmpeg识别其为本地文件,内部通常视为file://input.mp4 URL。

随后FFmpeg查找能处理file:// URL的"协议处理器"——即file协议。同理,output.avi也由file协议处理写入。

这些协议处理器抽象了底层细节

FFmpeg主引擎只需要求输入输出层"打开此URL"、“读取数据”、“写入数据"或"关闭URL”,相应协议处理器执行实际工作,无论是调用文件系统函数、发起网络请求还是与设备驱动交互。

FFmpeg I/O处理流程:

在这里插入图片描述

  • FFmpeg告知输入输出层打开目标,输入输出层使用正确协议(如本例的file协议)与外部资源交互。
  • 随后FFmpeg循环请求输入数据并提供输出数据直至完成,最终关闭所有资源。

超越本地文件:多样化数据源与目的地

输入输出访问层的强大之处在于其通过统一方式处理多种不同类型数据源和目的地的能力。

除本地文件外,FFmpeg还能读写:

  • 网络流媒体:HTTP、RTMP、RTSP、TCP、UDP等,只需提供不同URL:

    ffmpeg -i http://example.com/video.mp4 output.avi
    ffmpeg -i input.mp4 rtmp://publish.example.com/app/streamkey
    

    此类情况使用httprtmp等专用协议,详见FFmpeg文档协议选项。

  • 捕获/播放设备:摄像头、麦克风、屏幕捕获、视频/音频卡,通过操作系统特定的设备名或URL访问:

    ffmpeg -f video4linux2 -i /dev/video0 output.mp4 # Linux摄像头
    ffmpeg -f dshow -i video="集成摄像头":audio="麦克风" output.mp4 # Windows摄像头+麦克风
    

    使用设备特定的输入输出处理器(参见输入设备和输出设备)。

  • 标准输入/输出(管道):通过stdin读取、stdout写入,实现命令行工具链式调用:

    cat input.ts | ffmpeg -i pipe:0 output.mp4 # 从stdin读取
    ffmpeg -i input.mp4 -f avi pipe:1 | ffplay pipe:0 # 写入stdout
    

    通常使用pipefd协议。

核心思想保持不变:FFmpeg通过输入输出层获取原始数据字节,无需关心字节获取方式(通过各种上层协议的制定,实现了很好的抽象),只需确保可读写。


🎢协议解析结构体:AVIOContext与自定义数据源处理

在这里插入图片描述

AVIOContext 与 URLProtocol

  • AVIOContext 是 FFmpeg 中用于抽象 I/O 操作的核心结构体,负责数据的读写缓冲和协议处理。

  • 它通过 URLProtocol 实现与具体协议(如文件、HTTP、RTMP等)的交互。

URLProtocol 是协议实现的接口,定义了一组标准函数(如打开、读取、写入、关闭等)。每种协议(如 file、http)需要实现自己的 URLProtocol 实例

协作流程

AVIOContext 初始化时绑定一个 URLProtocol

当用户通过 AVIOContext 读写数据时,AVIOContext 会调用对应 URLProtocol 的函数。

例如,读取 HTTP 数据时:

  1. AVIOContext 处理缓冲状态管理
  2. 实际网络请求由 HTTP 协议的 URLProtocol 实现完成。

联系

  • AVIOContext 是上层抽象,提供统一的 I/O 接口。
  • URLProtocol 是底层实现,处理具体协议的细节。
  • 开发者通常只需操作 AVIOContext,无需直接调用 URLProtocol。

FFmpeg支持多种协议(HTTP、RTMP、RTSP、文件等)的多媒体数据获取。其协议解析模块通过三个核心结构体实现灵活的数据处理:

在这里插入图片描述

核心结构体解析

1. AVIOContext

头文件libavformat/avio.h
https://github.com/FFmpeg/FFmpeg/blob/master/libavformat/avio.h

关键字段

  • buffer:数据缓冲区
  • buffer_size:缓冲区容量
  • pos:当前读取偏移量
  • opaque:指向用户数据或URLContext
  • read_packet/write_packet读写回调函数

缓冲区关系
在这里插入图片描述

2. URLContext

典型实现(以RTP协议为例):

typedef struct RTPContext 
{URLContext *rtp_hd, *rtcp_hd;int ttl;int buffer_size;// ...其他协议特定字段
} RTPContext;const URLProtocol ff_rtp_protocol = 
{.name = "rtp",.url_open = rtp_open,.url_read = rtp_read,.priv_data_size = sizeof(RTPContext),// ...其他回调函数
};

3. URLProtocol

作为协议实现的基类,定义标准操作接口:

  • url_open:建立协议连接
  • url_read/url_write:数据读写
  • priv_data_size:协议私有数据大小

URLProtocol是FFMPEG操作文件的结构(包括文件,网络数据流等等),包括open、close、read、write、seek等操作。

定义了协议相关的回调函数,类似于url协议的虚函数定义

也类似门面模式,具体的回调函数的方法实现在ff_rtp_protocol等具体的对象中。

  • 门面模式通过一个统一的高层接口简化复杂子系统调用,像前台接待一样隐藏内部细节

应用场景

标准协议处理

使用avformat_open_input即可完成:

AVFormatContext *fmt_ctx = NULL;
avformat_open_input(&fmt_ctx, "rtsp://example.com/stream", NULL, NULL);

自定义数据源处理

当需要处理以下特殊场景时需直接操作AVIOContext:

  1. 内存数据源
struct buffer_data {uint8_t *ptr; size_t size;
};static int read_packet(void *opaque, uint8_t *buf, int buf_size) {struct buffer_data *bd = opaque;// 实现自定义读取逻辑
}
  1. 实时流处理(代码示例)
// 创建自定义AVIOContext
avio_ctx = avio_alloc_context(avio_ctx_buffer, avio_ctx_buffer_size,0, &bd, &read_packet, NULL, NULL);// 关联到FormatContext
fmt_ctx->pb = avio_ctx;
avformat_open_input(&fmt_ctx, NULL, NULL, NULL);
  1. 加密传输
static int decrypt_packet(void *opaque, uint8_t *buf, int buf_size) {// 解密逻辑实现return decrypt_data(buf, buf_size);
}

示例代码

#include <libavformat/avformat.h>struct buffer_data {uint8_t *ptr;size_t size; 
};static int read_packet(void *opaque, uint8_t *buf, int buf_size) {struct buffer_data *bd = opaque;// 实现内存到缓冲区的拷贝逻辑
}int main() {// 初始化AVFormatContextAVFormatContext *fmt_ctx = avformat_alloc_context();// 创建自定义IO上下文AVIOContext *avio_ctx = avio_alloc_context(av_malloc(4096), 4096, 0, &bd, &read_packet, NULL, NULL);fmt_ctx->pb = avio_ctx;avformat_open_input(&fmt_ctx, NULL, NULL, NULL);// 后续处理流程...
}

运行测试前置条件:

sudo apt-get update
sudo apt-get install libavformat-dev libavutil-dev

运行:

gcc -o ffmpeg_buffer_test ffmpeg_buffer_test.c -lavformat -lavutil

在这里插入图片描述

架构设计思想

FFmpeg通过分层设计实现协议处理的灵活性:

  1. 高层接口avformat_open_input封装常规操作
  2. 扩展层AVIOContext提供自定义I/O接入点
  3. 协议实现层URLProtocol定义标准协议接口

这种设计使得开发者既能快速处理标准协议,又能通过自定义AVIOContext实现特殊需求,在易用性和灵活性之间取得平衡。


C语言API:AVIOContext与URLProtocol

对于使用FFmpeg库(libavformat、libavcodec等)的开发人员,输入输出访问层主要通过AVIOContext结构体和URLProtocol相关函数管理。

  • AVIOContext:表示I/O操作上下文,包含读写缓冲区及指向底层I/O函数的指针
  • URLProtocol:定义特定协议处理器,包含协议名("file""http""rtmp")及实现打开、读写、寻址、关闭等操作的函数指针

多数应用不直接操作URLProtocol,而是通过avio_open2等函数为给定URL创建AVIOContext

// API函数使用示例(非完整代码)
AVFormatContext* fmt_ctx = avformat_alloc_context(); // 媒体文件格式上下文
AVIOContext* avio_ctx = NULL; // I/O操作上下文
char* input_url = "input.mp4";
int ret;// 使用avio_open2打开输入URL并创建AVIOContext
ret = avio_open2(&avio_ctx, input_url, AVIO_FLAG_READ, NULL, NULL);
if (ret < 0) {// 错误处理
}fmt_ctx->pb = avio_ctx; // 将AVIOContext关联到格式上下文// ...后续读取数据...
uint8_t buffer[4096];
int bytes_read = avio_read(avio_ctx, buffer, sizeof(buffer));// ...关闭资源...
avio_close(avio_ctx);
// 注意:avio_context_free在关闭后释放AVIOContext本身

该代码段展示了核心函数:

  • avio_open2初始化
  • avio_read/avio_write数据传输
  • avio_close终止操作。

doc/APIchanges文件记录了库接口变更,包含诸多AVIOContext相关更新,例如avio_open2的引入、支持更多参数等。

示例代码如

  • avio_list_dir.c展示了目录协议交互
  • avio_read_callback.c演示了自定义I/O上下文

这些API细节主要面向使用FFmpeg库的开发人员,证实输入输出访问层(AVIOContextURLProtocol)是FFmpeg架构的基础组成部分,专门负责原始数据字节的输入输出。

平时调api,最多用到avio,具体的数据结构和urlprotocol的实现我们并不关心,我们只需要调用,并且能输入和输出数据


总结

本章我们了解到,在FFmpeg解码、编码或处理多媒体数据前,必须首先读取数据,处理完成后写出数据。

这是输入输出访问层的职责,它通过特定协议处理器为本地文件、网络流和硬件设备等多样化数据源/目的地提供统一处理方式

现在我们理解FFmpeg如何获取原始字节,下一步是解析这些字节

多媒体数据通常封装在容器格式(如MP4MKVAVI)中,这些容器提供结构和元数据。

下一章我们将探讨FFmpeg如何处理这些容器。

容器处理


(抽象和协议的重要性)

统一接口设计 --前文传送:

[xiaozhi-esp32] 应用层(9种state) | 音频编解码层 | 双循环架构

[xiaozhi-esp32] 音频处理 | Display | 纯虚锁的抽象思想

[Subtitle Edit] 字幕数据模型Paragraph | 核心逻辑库(libse)

[shadPS4] 内存管理 | 权限管理 |文件系统 | 挂载和句柄

http://www.lryc.cn/news/593739.html

相关文章:

  • 外观设计模式
  • 零基础学习性能测试第二章-linux服务器监控:CPU监控
  • Redis字符串操作指南:从入门到实战应用
  • SQLShift:一款异构数据库存储过程迁移工具
  • c++ 基本语法易错与技巧总结
  • 模型的评估与选择
  • 【52】MFC入门到精通——(CComboBox)下拉框选项顺序与初始化不一致,默认显示项也不一致
  • yolov8-pos/yolov11-pos openvino C++部署
  • bash方式启动模型训练
  • OpenCV特征点提取算法orb、surf、sift对比
  • 相机参数的格式与作用
  • 算法基础知识总结
  • MYSQL 第一次作业
  • 量子计算与AI融合的技术突破与实践路径
  • scalelsd 笔记 线段识别 本地部署 模型架构
  • 【面试题】大厂高压面经实录丨第三期
  • SpringBoot服装推荐系统实战
  • 石子问题(区间dp)
  • 泛型机制详解
  • Java中缓存的使用浅讲
  • 从代码学习深度强化学习 - SAC PyTorch版
  • openmv小车追小球
  • PCA主成分分析
  • xss-labs1-8题
  • lvs笔记
  • JAVA高级第六章 输入和输出处理(一)
  • python类Keys
  • OpenCV 官翻 2 - 图像处理
  • CAN通信驱动开发注意事项
  • 使用C#对象将WinRiver项目文件进行复杂的XML序列化和反序列化实例详解