前言
AVCodecContext是FFmpeg库中的一个核心结构体,它代表了编解码器的上下文信息,是进行音频或视频编解码操作时必不可少的组件。
AVCodecContext存储了编解码器的配置参数、状态信息以及与编解码过程相关的资源。
无论是在编码还是解码过程中,都需要一个相应的AVCodecContext实例来管理和控制编解码过程。
提示:以下是本篇文章正文内容,下面案例可供参考
一、结构体原型
typedef struct AVCodecContext {
const AVClass *av_class;
AVMediaType codec_type; //编解码器处理的媒体类型,如AVMEDIA_TYPE_VIDEO或AVMEDIA_TYPE_AUDIO
const AVCodec *codec; // 指向当前编解码器的指针
AVCodecID codec_id; // 编解码器的ID,例如CODEC_ID_H264
int width, height; // 视频的情况下,宽和高
AVRational time_base; // 时间基,用于计算时间戳
int bit_rate; // 编码时的目标比特率
/* 更多编解码相关的参数... */
AVFrame *frame; // 可选的,用于存储原始数据的帧
AVFrame *coded_frame; // 编码后的帧,解码时使用
/* 更多功能性、控制和状态字段... */
} AVCodecContext;
参数说明
*const AVClass av_class:
同AVFormatContext中的av_class,用于日志记录和运行时类型信息。
AVMediaType codec_type:
指定编解码器处理的媒体类型,如音频或视频。
*const AVCodec codec:
指向已初始化的编解码器结构体,这个编解码器用于执行实际的编解码操作。
AVCodecID codec_id:
编解码器的唯一标识符,例如对于H.264视频编码器,其值为AV_CODEC_ID_H264。
int width, height:
视频编解码时,指定帧的宽度和高度。
AVRational time_base:
定义时间戳的单位,例如1/1000表示每毫秒一个时间单位。
int bit_rate:
编码时的目标比特率,单位通常是比特每秒。
**AVFrame frame, coded_frame:
frame通常用于存放原始数据,coded_frame用于存放编码或解码后的数据。在编码时,原始数据放入frame,编码后数据转移到coded_frame;解码时相反。
返回值
如果ts_a小于ts_b,返回负数。
如果ts_a等于ts_b,返回0。
如果ts_a大于ts_b,返回正数
二、使用场景
1、编码流程:
1、查找并分配编解码器:使用avcodec_find_encoder(codec_id)查找编解码器,然后通过avcodec_alloc_context3(codec)分配并初始化AVCodecContext。
2、配置编解码器上下文:设置AVCodecContext的参数,如分辨率、比特率、时间基等。
3、打开编解码器:调用avcodec_open2(context, codec, NULL)来初始化编解码器。
4、编码循环:准备数据帧,调用avcodec_send_frame送入编码器,再通过avcodec_receive_packet获取编码后的数据包。
2、解码流程:
1、查找并分配解码器:与编码流程相似,使用avcodec_find_decoder(codec_id)和avcodec_alloc_context3(codec)。
2、配置解码器上下文:根据输入流的参数配置AVCodecContext。
3、打开解码器:调用avcodec_open2。
4、解码循环:接收数据包通过avcodec_send_packet送入解码器,然后使用avcodec_receive_frame获取解码后的帧。
三、编码使用实例
这个例子将展示如何使用AVCodecContext和其他相关结构体来编码一帧原始图像数据为H.264视频流。
1、首先,我们初始化FFmpeg库,找到H.264编码器,并为编码器创建和配置AVCodecContext。
2、接着,我们创建输出文件的AVFormatContext,添加一个视频流,并打开输出文件。
3、之后,我们为一帧原始图像数据创建AVFrame结构体,并简单地填充一些测试数据。
4、利用avcodec_encode_video2()函数尝试编码这一帧数据,得到的编码数据包通过av_interleaved_write_frame()写入输出文件。
5、最后,我们完成编码后清理资源,关闭编码器和输出文件。
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>
int main() {
const char *out_filename = "output.h264";
const AVCodecID codec_id = AV_CODEC_ID_H264;
// 初始化FFmpeg库
av_register_all();
avcodec_register_all();
// 找到编码器
AVCodec *codec = avcodec_find_encoder(codec_id);
if (!codec) {
printf("Codec not found\n");
return -1;
}
// 创建并初始化编码上下文
AVCodecContext *c = avcodec_alloc_context3(codec);
if (!c) {
printf("Could not allocate video codec context\n");
return -1;
}
// 设置编码参数
c->bit_rate = 400000; // 比特率
c->width = 640; // 宽度
c->height = 480; // 高度
c->time_base = (AVRational){1, 25}; // 25帧每秒
c->gop_size = 12; // I帧间隔
c->max_b_frames = 1; // 最大B帧数
c->pix_fmt = AV_PIX_FMT_YUV420P; // 像素格式
// 打开编码器
if (avcodec_open2(c, codec, NULL) < 0) {
printf("Could not open codec\n");
return -1;
}
// 创建输出文件及格式上下文
AVFormatContext *fmt_ctx = NULL;
if (avformat_alloc_output_context2(&fmt_ctx, NULL, NULL, out_filename) < 0) {
printf("Could not create output context\n");
return -1;
}
// 添加视频流到输出上下文
AVStream *stream = avformat_new_stream(fmt_ctx, codec);
if (!stream) {
printf("Could not create new stream\n");
return -1;
}
avcodec_parameters_from_context(stream->codecpar, c);
// 打开输出文件
if (avio_open(&fmt_ctx->pb, out_filename, AVIO_FLAG_WRITE) < 0) {
printf("Could not open output file\n");
return -1;
}
// 写头部信息
avformat_write_header(fmt_ctx, NULL);
// 准备一帧原始图像数据(这里简化处理,实际应用中可能来自摄像头、文件等)
AVFrame *frame = av_frame_alloc();
frame->format = c->pix_fmt;
frame->width = c->width;
frame->height = c->height;
av_frame_get_buffer(frame, 0);
// 填充帧数据(此处仅为演示,实际应用中应根据真实图像数据填充)
uint8_t *image_data = frame->data[0];
size_t image_size = c->width * c->height;
memset(image_data, 128, image_size); // 简单地填充灰阶数据
// 编码循环(简化版,实际应用中需要循环处理多帧)
AVPacket pkt = {0};
av_init_packet(&pkt);
pkt.data = NULL; // packet data will be allocated by the encoder
pkt.size = 0;
// 将帧送入编码器
int got_packet = 0;
if (avcodec_encode_video2(c, &pkt, frame, &got_packet) < 0) {
printf("Error encoding frame\n");
return -1;
}
// 如果有编码好的数据包,则写入输出文件
if (got_packet) {
av_packet_rescale_ts(&pkt, c->time_base, stream->time_base);
pkt.stream_index = stream->index;
if (av_interleaved_write_frame(fmt_ctx, &pkt) < 0) {
printf("Error while writing output packet\n");
}
}
// 写尾部信息并清理
av_write_trailer(fmt_ctx);
avcodec_close(c);
avformat_free_context(fmt_ctx);
av_frame_free(&frame);
return 0;
}
四、解码使用实例
这个例子将会使用FFmpeg库来打开一个视频文件,找到解码器,初始化AVCodecContext,并进行基本的解码流程。
1、首先,通过av_register_all()和avformat_network_init()注册FFmpeg的所有组件并初始化网络支持。
2、使用avformat_open_input()打开视频文件,并通过avformat_find_stream_info()获取流信息。
3、查找视频流,并根据视频流的编解码器ID查找解码器。
4、为找到的解码器创建AVCodecContext实例,并使用avcodec_parameters_to_context()从流的AVCodecParameters复制参数到解码器上下文中。
5、通过avcodec_open2()打开解码器,准备进行解码操作。
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
int main(int argc, char **argv) {
const char *filename = "example.mp4"; // 待解码的视频文件名
if (argc > 1) filename = argv[1];
av_register_all(); // 注册所有组件
avformat_network_init(); // 初始化网络组件,如果视频来源于网络
AVFormatContext *fmt_ctx = NULL;
if (avformat_open_input(&fmt_ctx, filename, NULL, NULL) != 0) {
printf("Could not open input file.\n");
return -1;
}
if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {
printf("Failed to retrieve stream information.\n");
return -1;
}
AVCodecContext *dec_ctx = NULL;
AVCodec *dec = NULL;
int video_stream_index = -1;
for (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;
}
}
if (video_stream_index == -1) {
printf("Didn't find a video stream.\n");
return -1;
}
dec = avcodec_find_decoder(fmt_ctx->streams[video_stream_index]->codecpar->codec_id);
if (!dec) {
printf("Decoder not found.\n");
return -1;
}
dec_ctx = avcodec_alloc_context3(dec);
if (avcodec_parameters_to_context(dec_ctx, fmt_ctx->streams[video_stream_index]->codecpar) < 0) {
printf("Failed to copy codec parameters to decoder context.\n");
return -1;
}
if (avcodec_open2(dec_ctx, dec, NULL) < 0) {
printf("Failed to open codec.\n");
return -1;
}
// 这里开始解码循环,实际应用中你需要处理AVPacket和AVFrame
// 由于解码循环较为复杂,这里省略以保持示例简洁
// 清理
avcodec_free_context(&dec_ctx);
avformat_close_input(&fmt_ctx);
return 0;
}