FFmpeg 是一个强大的多媒体处理库,下面我将介绍其基本 API 并结合网络流/本地文件解码示例说明每个 API 的功能和用法。
一、核心 API 分类
1. 格式处理 API (libavformat)
2. 编解码 API (libavcodec)
3. 实用工具 API (libavutil)
4. 图像缩放/像素格式转换 API (libswscale)
二、基本 API 详解及示例
1. 初始化相关 API
c
// 注册所有编解码器和格式 (4.0+已废弃,但许多示例仍保留)
av_register_all();
// 初始化网络库 (用于网络流)
avformat_network_init();
2. 打开输入流 API
c
AVFormatContext *pFormatCtx = NULL;
// 本地文件打开
const char *url = "input.mp4";
if (avformat_open_input(&pFormatCtx, url, NULL, NULL) != 0) {
printf("无法打开输入文件\n");
return -1;
}
// 网络流打开 (如RTMP)
const char *rtmp_url = "rtmp://live.example.com/app/stream";
AVDictionary *options = NULL;
av_dict_set(&options, "rtsp_transport", "tcp", 0); // 设置RTSP over TCP
av_dict_set(&options, "stimeout", "5000000", 0); // 设置超时5秒
if (avformat_open_input(&pFormatCtx, rtmp_url, NULL, &options) != 0) {
printf("无法打开网络流\n");
return -1;
}
3. 获取流信息 API
c
if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
printf("无法获取流信息\n");
return -1;
}
// 打印流信息
av_dump_format(pFormatCtx, 0, url, 0);
4. 查找视频流 API
c
int videoStream = -1;
for (int i = 0; i < pFormatCtx->nb_streams; i++) {
if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
videoStream = i;
break;
}
}
if (videoStream == -1) {
printf("未找到视频流\n");
return -1;
}
5. 编解码器设置 API
c
// 获取编解码器参数
AVCodecParameters *pCodecParams = pFormatCtx->streams[videoStream]->codecpar;
// 查找解码器
const AVCodec *pCodec = avcodec_find_decoder(pCodecParams->codec_id);
if (!pCodec) {
printf("不支持的解码器\n");
return -1;
}
// 创建解码器上下文
AVCodecContext *pCodecCtx = avcodec_alloc_context3(pCodec);
if (!pCodecCtx) {
printf("无法分配编解码器上下文\n");
return -1;
}
// 复制参数到上下文
if (avcodec_parameters_to_context(pCodecCtx, pCodecParams) < 0) {
printf("无法复制编解码器参数\n");
return -1;
}
// 打开解码器
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
printf("无法打开解码器\n");
return -1;
}
6. 帧和包处理 API
c
// 分配帧
AVFrame *pFrame = av_frame_alloc();
if (!pFrame) {
printf("无法分配帧\n");
return -1;
}
// 初始化包
AVPacket packet;
av_init_packet(&packet);
packet.data = NULL;
packet.size = 0;
7. 解码循环 API
c
while (av_read_frame(pFormatCtx, &packet) >= 0) {
if (packet.stream_index == videoStream) {
// 发送数据包到解码器
int ret = avcodec_send_packet(pCodecCtx, &packet);
if (ret < 0) {
printf("发送解码包出错\n");
continue;
}
// 接收解码后的帧
while (ret >= 0) {
ret = avcodec_receive_frame(pCodecCtx, pFrame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
} else if (ret < 0) {
printf("解码出错\n");
return -1;
}
// 成功解码一帧,可以处理帧数据
printf("解码帧: width=%d, height=%d, format=%d, pts=%lld\n",
pFrame->width, pFrame->height, pFrame->format, pFrame->pts);
// 这里可以添加帧处理代码...
}
}
av_packet_unref(&packet); // 释放包
}
8. 刷新解码器 API
c
// 发送空包刷新解码器
avcodec_send_packet(pCodecCtx, NULL);
// 接收剩余的帧
while (avcodec_receive_frame(pCodecCtx, pFrame) == 0) {
printf("刷新解码器获取的帧: pts=%lld\n", pFrame->pts);
// 处理帧...
}
9. 资源释放 API
c
av_frame_free(&pFrame);
avcodec_free_context(&pCodecCtx);
avformat_close_input(&pFormatCtx);
avformat_network_deinit(); // 如果之前调用了avformat_network_init()
三、完整网络流解码示例
c
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <stdio.h>
int main() {
AVFormatContext *pFormatCtx = NULL;
AVCodecContext *pCodecCtx = NULL;
const AVCodec *pCodec = NULL;
AVFrame *pFrame = NULL;
AVPacket packet;
int videoStream = -1;
// 初始化网络库
avformat_network_init();
// 打开网络流 (这里使用RTMP示例)
const char *url = "rtmp://live.example.com/app/stream";
AVDictionary *options = NULL;
av_dict_set(&options, "rtsp_transport", "tcp", 0);
av_dict_set(&options, "stimeout", "5000000", 0);
if (avformat_open_input(&pFormatCtx, url, NULL, &options) != 0) {
printf("无法打开网络流\n");
return -1;
}
// 获取流信息
if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
printf("无法获取流信息\n");
return -1;
}
// 查找视频流
for (int i = 0; i < pFormatCtx->nb_streams; i++) {
if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
videoStream = i;
break;
}
}
if (videoStream == -1) {
printf("未找到视频流\n");
return -1;
}
// 设置解码器
AVCodecParameters *pCodecParams = pFormatCtx->streams[videoStream]->codecpar;
pCodec = avcodec_find_decoder(pCodecParams->codec_id);
if (!pCodec) {
printf("不支持的解码器\n");
return -1;
}
pCodecCtx = avcodec_alloc_context3(pCodec);
avcodec_parameters_to_context(pCodecCtx, pCodecParams);
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
printf("无法打开解码器\n");
return -1;
}
// 准备帧和包
pFrame = av_frame_alloc();
av_init_packet(&packet);
// 解码循环
while (av_read_frame(pFormatCtx, &packet) >= 0) {
if (packet.stream_index == videoStream) {
if (avcodec_send_packet(pCodecCtx, &packet) < 0) {
printf("发送解码包出错\n");
continue;
}
while (avcodec_receive_frame(pCodecCtx, pFrame) == 0) {
printf("解码帧: width=%d, height=%d, format=%d, pts=%lld\n",
pFrame->width, pFrame->height, pFrame->format, pFrame->pts);
// 实际应用中这里可以处理帧数据
}
}
av_packet_unref(&packet);
}
// 刷新解码器
avcodec_send_packet(pCodecCtx, NULL);
while (avcodec_receive_frame(pCodecCtx, pFrame) == 0) {
printf("刷新解码器获取的帧\n");
}
// 清理
av_frame_free(&pFrame);
avcodec_free_context(&pCodecCtx);
avformat_close_input(&pFormatCtx);
avformat_network_deinit();
return 0;
}
四、关键 API 功能总结
API | 功能描述 | 常用场景 |
---|---|---|
av_register_all() |
注册所有编解码器和格式 | 旧版本初始化 (4.0+已废弃) |
avformat_network_init() |
初始化网络库 | 处理网络流前调用 |
avformat_open_input() |
打开媒体文件或流 | 本地文件/网络流输入 |
avformat_find_stream_info() |
获取流信息 | 打开输入后获取详细信息 |
avcodec_find_decoder() |
查找解码器 | 根据codec_id查找解码器 |
avcodec_alloc_context3() |
创建编解码器上下文 | 解码器/编码器设置 |
avcodec_parameters_to_context() |
复制参数到上下文 | 设置解码器参数 |
avcodec_open2() |
打开编解码器 | 初始化编解码器 |
av_read_frame() |
读取数据包 | 解码循环中获取数据 |
avcodec_send_packet() |
发送数据包到解码器 | 现代解码流程 |
avcodec_receive_frame() |
接收解码后的帧 | 现代解码流程 |
av_packet_unref() |
释放数据包 | 处理完包后释放资源 |
av_frame_free() |
释放帧 | 处理完帧后释放资源 |
五、不同场景下的注意事项
-
本地文件解码:
-
不需要调用
avformat_network_init()
-
通常不需要设置超时等网络参数
-
-
网络流解码:
-
必须调用
avformat_network_init()
-
建议设置合理的超时参数
-
对于RTSP流,建议强制使用TCP传输
-
-
实时流处理:
-
可能需要设置
realtime
标志 -
可能需要禁用缓冲
av_dict_set(&options, "fflags", "nobuffer", 0)
-
-
错误处理:
-
所有API调用都应检查返回值
-
网络流需要更健壮的错误恢复机制
-
这个指南涵盖了FFmpeg解码的基本API和使用方法,实际应用中可能需要根据具体需求进行调整和扩展。