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和使用方法,实际应用中可能需要根据具体需求进行调整和扩展。