libavcodec
:主要实现一系列编码器的实现
一编码/解码常见结构体
(1) AVCodec
:编码器信息
(2) AVCodecContext:编码器上下文
(3)AVFrame
:音视频的原始帧数据,对应的AVPacket
里面的帧是压缩后的帧.
2 内存分配和释放
av_frame_alloc()\av_frame_free()
生成和释放:AVFrame
// 分配编解码器的上下文
avcodec_alloc_context3()/avcodec_free_context
二 解码
avcodec_find_decoder
:查找解码器
AVCodec *codec;
// 获取解码器实例, st 是AVStream
codec = avcodec_find_decoder(st->codecpar->codec_id)(旧版本)
// codec = avcodec_find_decoder(AV_CODEC_ID_MPEG1VIDEO)(新版本)
// 新版本
AVCodecParameters *codec_param = ctx->streams[ret]->codecpar;
const AVCodec *codec = avcodec_find_decoder(codec_param->codec_id);
avcodec_open2
:打开解码器
avcodec_decode_video2
:视频解码(已废弃).
注意在新版本中如ffmpeg4.3.1
中视频解码使用avcodec_send_packet
和avcodec_receive_frame
实现了后台解码,源码如下:
av_log_set_level(AV_LOG_INFO);
AVFormatContext *ctx = NULL;
ctx = avformat_alloc_context();
// 打开文件
int ret = avformat_open_input(&ctx, "./Desktop/ffmpeg/基础一/打开本地文件和网络文件/scale.MP4", NULL, NULL);
if (ret != 0)
{
av_log(NULL, AV_LOG_ERROR, "视频文件打开失败\n");
goto __fail;
}
if (avformat_find_stream_info(ctx, NULL) < 0)
{
/* code */
av_log(NULL, AV_LOG_ERROR, "获取视频流失败\n");
goto __fail;
}
AVPacket *pkt = NULL;
pkt = av_packet_alloc();
// streams 数量
int streamCount = ctx->nb_streams;
// 获取所有的AVStream 一般是2路 音频和视频 [0]音频 [1]视频
for (int i = 0; i < streamCount; i++)
{
// 获取streams
AVStream *stream = ctx->streams[i];
// index
int stramIndex = stream->index;
av_log(NULL, AV_LOG_ERROR, "stramIndex=%d\n", stramIndex);
}
// 获取streams 取第一个进行解码
AVStream *stream = ctx->streams[1];
// index
int stramIndex = stream->index;
av_log(NULL, AV_LOG_ERROR, "codec_type=%d\n", stream->codecpar->codec_type);
AVCodecContext *codec_content = NULL;
codec_content = avcodec_alloc_context3(NULL);
AVCodecParameters *codecpar = NULL;
codecpar = avcodec_parameters_alloc();
// 拷贝一份AVCodecParameters
avcodec_parameters_copy(codecpar, stream->codecpar);
avcodec_parameters_to_context(codec_content, codecpar);
const AVCodec *avCodec = avcodec_find_decoder(codec_content->codec_id);
int isopen = avcodec_open2(codec_content, avCodec, NULL);
if (isopen)
{
av_log(NULL, AV_LOG_ERROR, "avcodec_open2解码器打开失败\n");
goto __fail;
}
// getStream(ctx, fdsfds);
while (av_read_frame(ctx, pkt) >= 0)
{
// av_log(NULL, AV_LOG_INFO, "读取视频流成功\n");
// int send = avcodec_send_packet(codec_content, pkt);
// 在解码前 必须知道是哪路流
if (pkt->stream_index == stramIndex)
{
int send = avcodec_send_packet(codec_content, pkt);
if (send)
{
av_log(NULL, AV_LOG_ERROR, "avcodec_send_packet失败\n");
continue;
}
while (1)
{
AVFrame *av_frame = NULL;
av_frame = av_frame_alloc();
if (avcodec_receive_frame(codec_content, av_frame))
{
break;
}
// 视频解码后的数据
int width = av_frame->width;
int height = av_frame->height;
enum AVPixelFormat format = (enum AVPixelFormat)av_frame->format;
char *str = (char *) malloc(128);
str = av_get_pix_fmt_string(str,128,format);
av_log(NULL, AV_LOG_ERROR, "width = %d,height = %d pix_fmt= %s\n", width, height,str);
av_log(NULL, AV_LOG_ERROR, "解码成功!!!!\n");
free(str);
}
}
}
// 可能还有缓存帧 需要把缓存解码 不然会有丢失原来视频帧的情况
int last_send = avcodec_send_packet(codec_content, NULL);
if (last_send)
{
av_log(NULL, AV_LOG_ERROR, "avcodec_send_packet缓存帧失败或者没有\n");
goto __fail;
}
while (1)
{
AVFrame *av_frame = NULL;
av_frame = av_frame_alloc();
av_log(NULL, AV_LOG_ERROR, "%d\n",avcodec_receive_frame(codec_content, av_frame));
if (avcodec_receive_frame(codec_content, av_frame))
{
break;
}
av_log(NULL, AV_LOG_ERROR, "缓存帧解码成功!!!!\n");
}
__fail:
avformat_close_input(&ctx);
if (ctx)
{
avformat_free_context(ctx);
ctx = NULL;
}
if (pkt)
{
av_packet_free(&pkt);
pkt = NULL;
}
if (codec_content)
{
avcodec_free_context(&codec_content);
codec_content = NULL;
}
if (codecpar)
{
avcodec_parameters_free(&codecpar);
codecpar = NULL;
}
三 编码
avcodec_find_encoder_by_name
:查找编码器,和解码不同的是,通过名字查找.
avcodec_open2
:编码的时候需要设置一些参数,分辨率,帧率等
avcodec_encode_video2
:编码 编解码ffmpeg用的是第三方库比如lib264
,ffmpeg只是二次封装
H264编码一些参数设置
AVCodecContext * codec = NULL;
c = avcodec_alloc_context3(codec);
//libx264
// 查询编解码器
codec = avcodec_find_encoder_by_name(codec_name);
// 码率
c->bit_rate = 400000;
// 分辨率
c->width = 352;
c->height = 288;
// 时间基 随帧率变化 1/25
c->time_base = (AVRational){1, 25};
// 帧率越大 码率越大 流畅度越好
c->framerate = (AVRational){25, 1};
// gop_size:多少帧产生一个关键帧,比如 120帧 c->gop_size = 10 120分成10组,10帧有个关键帧,后面都是参考帧 gop_size设置很小的话:码率增加 关键帧就多了 设置太大 码率减少,关键帧少了
c->gop_size = 10;
// 1 b帧:前后参考帧 max_b_frames 压缩率非常高
// 2 p帧:向前参考帧 参考帧越多 ,压缩率越高 同样编码时长越大,实时性有影响
// 3 i帧:关键帧
c->max_b_frames = 1;
// YUV 420
c->pix_fmt = AV_PIX_FMT_YUV420P;
AAC 编码的一些设置参数设置
AVCodecContext * codec = NULL;
c = avcodec_alloc_context3(codec);
// 比特率64k
c->bit_rate = 64000;
// 采样格式 16位的
c->sample_fmt = AV_SAMPLE_FMT_S16;
// 采样率
c->sample_rate = select_sample_rate(codec);
c->channel_layout = select_channel_layout(codec);
// 通道
c->channels = av_get_channel_layout_nb_channels(c->channel_layout);
结语:在学习ffmpeg其他内容之前,调研一下SDL
.主要为SDL安装,编译程序,窗口创建,SDL事件处理,纹理渲染等.