FFmpeg - 基本 API大全(视频编解码相关的)
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和使用方法,实际应用中可能需要根据具体需求进行调整和扩展。