FFMPEG api使用
一、播放音视频
1.1 整体架构
1.2 具体代码
1. 创建一个媒体句柄AVFormatContext,这个句柄里面存储了 流信息、视频信息 或 音频信息
AVFormatContext *fmt_ctx = NULL;// 1. 分配上下文
fmt_ctx = avformat_alloc_context();
if (!fmt_ctx) {// 处理内存分配失败
}// 2. 打开文件并填充上下文
if (avformat_open_input(&fmt_ctx, "input.mp4", NULL, NULL) != 0) {// 处理打开失败
}// 3. 使用上下文进行操作(如查找流信息等)// 4. 关闭文件并释放资源
avformat_close_input(&fmt_ctx);
avformat_alloc_context 和 avformat_free_context 配对使用
avformat_open_input 和 avformat_close_input 配对使用
注意可以不使用avformat_alloc_context()分配内存后再使用avformat_open_input()打开文件。只要传入的fmt_ctx==NULL,就会自动创建
使用avformat_close_input关闭解复用器器后,就不用使用avformat_free_context释放AVFormatContext
2. 查看AVFormatContext里面有哪些媒体流
// 获取流的信息
if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {fprintf(stderr, "无法获取流信息\n");avformat_close_input(&fmt_ctx);return -1;
}// 获取了信息才能使用下面方法找流
for (int i = 0; i < fmt_ctx->nb_streams; i++) {if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {target_stream_index = i;printf("找到视频流,索引:%d\n", target_stream_index);break;}
}
使用avformat_find_stream_info()可以获取视频文件信息
3. 从AVFormatContext里面获取一个包AVPacket
AVPacket * packet = av_packet_alloc();while ((ret = av_read_frame(fmt_ctx, packet)) == 0) {// 判断当前包是否属于目标流if (packet->stream_index == target_stream_index) {// 处理目标流的数据包(例如解码、打印信息等)printf("处理视频流数据,PTS: %lld\n", packet->pts);}// 无论是否处理,都需要释放数据包的引用av_packet_unref(packet);
}
avformat_seek_file()定位到音视频文件的位置
4. 常见解码
视频:H.264 -> YUV
音频:AAC -> PCM
通过下面的函数将AVPacket转成AVFrame
// 获取解码参数
AVCodecParameters * codec_par = fmt_ctx->streams[video_stream_idx]->codecpar;
// 根据解码器ID获取解码器
AVCodec * dec = avcodec_find_decoder(codec_par->codec_id);
// 根据解码器创建上下文结构体
AVCodecContext * dec_ctx = avcodec_alloc_context3(dec);int decode_video_packet(AVCodecContext *dec_ctx, AVFrame *frame, AVPacket *pkt) {int ret;// 发送数据包到解码器if (avcodec_send_packet(dec_ctx, pkt) < 0) {fprintf(stderr, "发送视频数据包失败\n");return -1;}// 接收解码后的帧ret = avcodec_receive_frame(dec_ctx, frame);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)return 0;if (ret < 0) {fprintf(stderr, "视频解码失败\n");return -1;}// 简单处理:打印视频帧信息printf("视频帧 - 宽: %d, 高: %d, PTS: %lld\n", frame->width, frame->height, frame->pts);av_frame_unref(frame);return 0;
}
AVCodecParameters *codec_par = fmt_ctx->streams[audio_stream_idx]->codecpar;
AVCodec * dec = avcodec_find_decoder(codec_par->codec_id);
AVCodecContext * dec_ctx = avcodec_alloc_context3(dec);int decode_audio_packet(AVCodecContext *dec_ctx, AVFrame *frame, AVPacket *pkt) {int ret;// 发送数据包到解码器if (avcodec_send_packet(dec_ctx, pkt) < 0) {fprintf(stderr, "发送音频数据包失败\n");return -1;}// 接收解码后的帧ret = avcodec_receive_frame(dec_ctx, frame);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)return 0;if (ret < 0) {fprintf(stderr, "音频解码失败\n");return -1;}// 简单处理:打印音频帧信息printf("音频帧 - 采样率: %d, 声道数: %d, 样本数: %d\n",dec_ctx->sample_rate, dec_ctx->channels, frame->nb_samples);av_frame_unref(frame);return 0;
}
二、FFMPEG整体架构
上层应用(最顶部)
ffplay:一个简单的媒体播放器,基于 FFmpeg 库实现音视频的播放功能,可用于测试和简单的播放场景。
ffprobe:用于查看媒体文件的信息,比如流的类型、编码格式、时长、比特率等元数据,帮助开发者或用户了解媒体文件的详细结构。
ffmpeg:FFmpeg 最核心的命令行工具,集音视频的录制、转换、编码、解码等多种功能于一体,可对媒体文件进行各种复杂的处理操作。
核心库(中间层)
libavutil:提供了一些通用的工具函数,如内存管理、数学运算、数据结构等,是其他 FFmpeg 库的基础支撑库。
libavformat:负责媒体文件的格式处理,包括封装和解封装操作。比如读取 MP4、MKV 等格式的文件,解析出其中的音视频流;或者将编码后的音视频流封装成特定格式的文件。
libavcodec:音视频编解码的核心库,包含了各种音视频编解码器的实现(如 H.264、AAC 等编解码器),用于将压缩的音视频数据解码为原始数据,或者将原始数据编码为压缩格式。
libswscale:主要用于视频像素格式的转换和图像缩放。例如将 YUV 格式的视频帧转换为 RGB 格式,或者对视频图像进行缩放操作,以适配不同的显示需求。
libswresample:专注于音频重采样和格式转换。可以调整音频的采样率、声道数、采样格式等,使音频在不同的设备或场景下能够正常播放。
libavfilter:提供了各种音视频滤镜功能,用于对音视频进行特效处理。比如给视频添加模糊、锐化效果,对音频进行降噪、均衡等处理。
libpostproc:用于对视频进行后期处理,比如去块效应等,提升视频的视觉质量。
编解码器(底层)
fdk - aac:是一种 AAC 音频编解码器,用于 AAC 格式音频的编码和解码,能提供高质量的 AAC 音频编码。
voaac_enc:也是 AAC 音频编码器,用于将原始音频数据编码为 AAC 格式。
x264:非常知名的 H.264 视频编码器,可将原始视频数据高效地编码为 H.264 格式的视频流,在视频压缩领域应用广泛。
三、AVPacket
1.1 AVPacket引用计数
AVPacket *pkt1 = av_packet_alloc();
AVPacket *pkt2 = av_packet_alloc();
// 假设pkt1已经填充好数据
av_packet_ref(pkt2, pkt1);
av_packet_ref(pkt2, pkt1) 后,pkt1 和 pkt2 共享 pkt1 原来指向的数据内存。引用计数+1,当引用技术减少为0的时候就释放内存
有下面的一些函数:
av_init_packet(AVPacket *pkt) 初始化一个已经分配好内存的 AVPacket 结构体。它主要是将 AVPacket 结构体中的成员变量设置为默认的初始状态,如将 pts、dts 设为 AV_NOPTS_VALUE ,将 data 设为 NULL,将数据大小 size 设为 0 等。但它不会为 AVPacket 结构体分配内存,只是对传入的 AVPacket 结构体进行状态初始化。