在高版本的ffmpeg库中使用AVStream::codec成员时,编译和运行时都出现一堆警告:
main.cpp:151: warning: ‘AVStream::codec’ is deprecated (declared at ……\Other_libs\ffmpeg3.2\include/libavformat/avformat.h:893)
和
Using AVStream.codec … deprecated, use AVStream.codecpar instead
-
从提示来看,需要使用AVStream.codecpar代替AVStream.codec,前者的类型是AVCodecParameters,后者的类型是AVCodecContext。事实上,AVCodecContext结构体仍然是编解码时不可或缺的结构体。换言之,AVCodecContext结构体的重要成员仍然需要设置,才能使用(特别是编码),比如bit_rate。
之所以使用AVStream.codec,是因为解码时很容易获得源视频的一些基本信息。
const char *filename = "a.mp4";
AVFormatContext fmt_ctx = nullptr;
avformat_open_input(&fmt_ctx, filename, nullptr, nullptr);//打开一个视频
avformat_find_stream_info(fmt_ctx, nullptr);//读取视频,然后得到流和码率等信息
for(size_t i = 0; i < fmt_ctx->nb_streams; ++i)
{
AVStream *stream = m_fmt_ctx->streams[i];
AVCodecContext *codec_ctx = stream->codec;
//此时就可以通过stream和codec_ctx的结构体成员获取流的绝大部分信息,相当方便
}
上面的代码十分简洁,又能获取大部分流相关的信息。编码时直接从源视频的AVCodecContext中复制成员值,并作简单的修改即可。也正因为如此,多数人不肯离开这温床。
事实上,无论是编解码,使用AVCodecParameters的代码都相当简洁。
解码
const char *filename = "a.mp4";
AVFormatContext fmt_ctx = nullptr;
avformat_open_input(&fmt_ctx, filename, nullptr, nullptr);//打开一个视频
avformat_find_stream_info(fmt_ctx, nullptr);//读取视频,然后得到流和码率等信息
for(size_t i = 0; i < fmt_ctx->nb_streams; ++i)
{
AVStream *stream = fmt_ctx->streams[i];
AVCodec *codec = avcodec_find_decoder(stream->codecpar->codec_id);
AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);//需要使用avcodec_free_context释放
//事实上codecpar包含了大部分解码器相关的信息,这里是直接从AVCodecParameters复制到AVCodecContext
avcodec_parameters_to_context(codec_ctx, stream->codecpar);
av_codec_set_pkt_timebase(codec_ctx, stream->time_base);
avcodec_open2(codec_ctx, codec, nullptr);
}
视频的帧率,应该从AVStream结构的avg_frame_rate成员获取,而非AVCodecContext结构体。如果avg_frame_rate为{0, 1},那么再尝试从r_frame_rate成员获取。
编码
编码时,需要分配一个AVCodecContext,然后为成员变量设置适当的值,最后将设置值复制到AVCodecParameters。
const char *filename = "b.mp4";
AVFormatContext *fmt_ctx = nullptr;
avformat_alloc_output_context2(&fmt_ctx, nullptr, nullptr, filename); //需要调用avformat_free_context释放
//new一个流并挂到fmt_ctx名下,调用avformat_free_context时会释放该流
AVStream *stream = avformat_new_stream(fmt_ctx, nullptr);
AVCodec *codec = avcodec_find_encoder(fmt_ctx->oformat->video_codec);//音频为audio_codec
AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);
codec_ctx->video_type = AVMEDIA_TYPE_VIDEO;
codec_ctx->codec_id = m_fmt_ctx->oformat->video_codec;
codec_ctx->width = 1280;//你想要的宽度
codec_ctx->height = 720;//你想要的高度
codec_ctx->format = AV_PIX_FMT_YUV420P;//受codec->pix_fmts数组限制
codec_ctx->gop_size = 12;
codec_ctx->time_base = AVRational{1, 25};//应该根据帧率设置
codec_ctx->bit_rate = 1400 * 1000;
avcodec_open2(codec_ctx, codec, nullptr);
//将AVCodecContext的成员复制到AVCodecParameters结构体。前后两行不能调换顺序
avcodec_parameters_from_context(stream->codecpar, codec_ctx);
av_stream_set_r_frame_rate(stream, {1, 25});