FFmpeg 不仅提供命令行工具,还提供了丰富的 C/C++ API,允许开发者将音视频处理功能集成到自己的应用程序中。
核心库介绍
FFmpeg 主要包含以下核心库:
| 库名 | 功能 | 用途 |
|---|---|---|
| libavformat | 封装格式处理 | 读写各种音视频容器格式 |
| libavcodec | 编解码器 | 音视频编解码 |
| libavutil | 工具函数 | 通用工具和辅助函数 |
| libswscale | 图像缩放 | 图像缩放和色彩空间转换 |
| libswresample | 音频重采样 | 音频采样率转换和格式转换 |
| libavfilter | 滤镜处理 | 音视频滤镜效果 |
| libavdevice | 设备支持 | 摄像头、麦克风等设备访问 |
基本开发流程
初始化 FFmpeg
c#include <libavformat/avformat.h> #include <libavcodec/avcodec.h> // 注册所有编解码器和封装格式 av_register_all(); avformat_network_init(); // FFmpeg 4.0+ 不需要显式注册
打开输入文件
cAVFormatContext *fmt_ctx = NULL; int ret = avformat_open_input(&fmt_ctx, "input.mp4", NULL, NULL); if (ret < 0) { // 处理错误 } // 获取流信息 ret = avformat_find_stream_info(fmt_ctx, NULL); if (ret < 0) { // 处理错误 }
查找视频流
cint video_stream_index = -1; for (unsigned int i = 0; i < fmt_ctx->nb_streams; i++) { if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { video_stream_index = i; break; } }
初始化解码器
cAVCodecParameters *codec_par = fmt_ctx->streams[video_stream_index]->codecpar; const AVCodec *codec = avcodec_find_decoder(codec_par->codec_id); AVCodecContext *codec_ctx = avcodec_alloc_context3(codec); avcodec_parameters_to_context(codec_ctx, codec_par); ret = avcodec_open2(codec_ctx, codec, NULL); if (ret < 0) { // 处理错误 }
读取和解码帧
cAVPacket *packet = av_packet_alloc(); AVFrame *frame = av_frame_alloc(); while (av_read_frame(fmt_ctx, packet) >= 0) { if (packet->stream_index == video_stream_index) { ret = avcodec_send_packet(codec_ctx, packet); if (ret < 0) { // 处理错误 } while (ret >= 0) { ret = avcodec_receive_frame(codec_ctx, frame); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { break; } else if (ret < 0) { // 处理错误 } // 处理解码后的帧 process_frame(frame); } } av_packet_unref(packet); }
编码输出
c// 初始化编码器 const AVCodec *encoder = avcodec_find_encoder(AV_CODEC_ID_H264); AVCodecContext *enc_ctx = avcodec_alloc_context3(encoder); enc_ctx->width = 1280; enc_ctx->height = 720; enc_ctx->time_base = (AVRational){1, 25}; enc_ctx->framerate = (AVRational){25, 1}; enc_ctx->gop_size = 10; enc_ctx->max_b_frames = 1; enc_ctx->pix_fmt = AV_PIX_FMT_YUV420P; ret = avcodec_open2(enc_ctx, encoder, NULL); if (ret < 0) { // 处理错误 } // 编码帧 AVPacket *enc_packet = av_packet_alloc(); ret = avcodec_send_frame(enc_ctx, frame); if (ret < 0) { // 处理错误 } while (ret >= 0) { ret = avcodec_receive_packet(enc_ctx, enc_packet); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { break; } else if (ret < 0) { // 处理错误 } // 写入输出文件 av_interleaved_write_frame(out_fmt_ctx, enc_packet); av_packet_unref(enc_packet); }
资源清理
cav_frame_free(&frame); av_packet_free(&packet); avcodec_free_context(&codec_ctx); avformat_close_input(&fmt_ctx);
常用 API 函数
格式处理
c// 打开输入 int avformat_open_input(AVFormatContext **ps, const char *url, AVInputFormat *fmt, AVDictionary **options); // 查找流信息 int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options); // 读取帧 int av_read_frame(AVFormatContext *s, AVPacket *pkt); // 写入帧 int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt);
编解码
c// 查找解码器 const AVCodec *avcodec_find_decoder(enum AVCodecID id); // 查找编码器 const AVCodec *avcodec_find_encoder(enum AVCodecID id); // 打开编解码器 int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options); // 发送包到解码器 int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt); // 从解码器接收帧 int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame); // 发送帧到编码器 int avcodec_send_frame(AVCodecContext *avctx, const AVFrame *frame); // 从编码器接收包 int avcodec_receive_packet(AVCodecContext *avctx, AVPacket *avpkt);
图像处理
c// 分配图像 AVFrame *av_frame_alloc(void); // 分配图像数据 int av_frame_get_buffer(AVFrame *frame, int align); // 图像缩放 struct SwsContext *sws_getContext(int srcW, int srcH, enum AVPixelFormat srcFormat, int dstW, int dstH, enum AVPixelFormat dstFormat, int flags, SwsFilter *srcFilter, SwsFilter *dstFilter, const double *param); int sws_scale(struct SwsContext *c, const uint8_t *const srcSlice[], const int srcStride[], int srcSliceY, int srcSliceH, uint8_t *const dst[], const int dstStride[]);
错误处理
c// 获取错误描述 char errbuf[AV_ERROR_MAX_STRING_SIZE]; av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE); fprintf(stderr, "Error: %s\n", errbuf); // 常见错误码 AVERROR(EAGAIN) // 需要更多输入/输出 AVERROR_EOF // 文件结束 AVERROR(EINVAL) // 无效参数 AVERROR(ENOMEM) // 内存不足
最佳实践
- 资源管理:使用 RAII 模式管理资源,确保正确释放
- 错误处理:检查所有 API 调用的返回值
- 线程安全:FFmpeg API 大部分不是线程安全的,需要适当的同步
- 性能优化:使用硬件加速、多线程处理
- 内存管理:及时释放不再使用的资源
FFmpeg API 功能强大但复杂,建议从简单的示例开始,逐步掌握各个模块的使用方法。