note
视频帧率调整
帧率(fps-frame per second)
例如:原来帧率为30,调整后为1 现象:原来是每秒有30张图像,调整后每秒1张图像,看着图像很慢
实现:在每秒的时间区间里,取一张图像
version
#define LIBAVFILTER_VERSION_MINOR 12
#define LIBAVFILTER_VERSION_MICRO 100
#define LIBAVCODEC_VERSION_MINOR 31
#define LIBAVCODEC_VERSION_MICRO 102
code
void CFfmpegOps::ChangeVideoFPS(const char *in_mp4, const char *out_mp4)
{
AVFormatContext *in_fmt_ctx = nullptr;
const AVInputFormat *in_fmt = nullptr;
AVFormatContext *out_fmt_ctx = nullptr;
const AVOutputFormat *out_fmt = nullptr;
int ret = -1;
int video_stream_index = 0;
const AVCodec *decoder = nullptr;
AVCodecContext *decoder_ctx = nullptr;
const AVCodec* encoder = nullptr;
AVCodecContext* encoder_ctx = nullptr;
AVStream* in_avstream = nullptr;
AVStream* out_avstream = nullptr;
const AVFilter * avfilter_buffer_src = nullptr; // 源,video buffer可设置的参数有
AVFilterContext* avfilter_ctx_buffer_src = nullptr;
const AVFilter * avfilter_fps = nullptr; // 中间节点,video fps可设置的参数包含fps
AVFilterContext* avfilter_ctx_fps = nullptr;
const AVFilter* avfilter_buffer_sink = nullptr; // 终,video buffersink可设置的参数只有pixel formats
AVFilterContext* avfilter_ctx_buffer_sink = nullptr;
AVFilterGraph* avfiltergraph = nullptr;
AVPacket* avpacket_src = nullptr;
AVFrame* avframe_src = nullptr;
AVFrame* avframe_dest = nullptr;
AVPacket* avpacket_dest = nullptr;
AVRational pixel_aspect = {.num = 1, .den = 1};
ret = avformat_open_input(&in_fmt_ctx, in_mp4, nullptr, nullptr);
if (ret < 0)
{
printf("avformat_open_input error(%s)\n", GetFfmpegERR(ret));
goto END;
}
in_fmt = in_fmt_ctx->iformat;
ret = avformat_find_stream_info(in_fmt_ctx, nullptr);
if (ret < 0)
{
printf("avformat_find_stream_info error(%s)\n", GetFfmpegERR(ret));
goto END;
}
ret = av_find_best_stream(in_fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
if (ret < 0)
{
printf("av_find_best_stream error(%s)\n", GetFfmpegERR(ret));
goto END;
}
video_stream_index = ret;
in_avstream = in_fmt_ctx->streams[video_stream_index];
decoder = avcodec_find_decoder(in_avstream->codecpar->codec_id);
if (!decoder)
{
printf("avcodec_find_decoder error\n");
goto END;
}
decoder_ctx = avcodec_alloc_context3(decoder);
if (!decoder_ctx)
{
printf("avcodec_alloc_context3 error\n");
goto END;
}
ret = avcodec_parameters_to_context(decoder_ctx, in_avstream->codecpar);
if (ret < 0)
{
printf("avcodec_parameters_to_context error(%s)\n", GetFfmpegERR(ret));
goto END;
}
ret = avcodec_open2(decoder_ctx, decoder, nullptr);
if (ret < 0)
{
printf("avcodec_open2 error(%s)\n", GetFfmpegERR(ret));
goto END;
}
avfiltergraph = avfilter_graph_alloc();
if (!avfiltergraph)
{
printf("avfilter_graph_alloc error\n");
goto END;
}
avfilter_buffer_src = avfilter_get_by_name("buffer"); // buffer对应video,abuffer对应audio
if (!avfilter_buffer_src)
{
printf("avfilter_get_by_name error\n");
goto END;
}
printf("avfilter_buffer_src->desc:%s\n", avfilter_buffer_src->description);
avfilter_ctx_buffer_src = avfilter_graph_alloc_filter(avfiltergraph, avfilter_buffer_src, avfilter_buffer_src->name);
if (!avfilter_ctx_buffer_src)
{
printf("avfilter_graph_alloc_filter error\n");
goto END;
}
// 设置avfilter_ctx_buffer_src的可选参数
// 参照libavfilter/buffersrc.c文件中对video buffer的可选参数描述
ret = av_opt_set(avfilter_ctx_buffer_src,
"width", std::to_string(in_avstream->codecpar->width).c_str(),
AV_OPT_SEARCH_CHILDREN);
if (ret < 0)
{
printf("av_opt_set error(%s)\n", GetFfmpegERR(ret));
goto END;
}
ret = av_opt_set(avfilter_ctx_buffer_src,
"height", std::to_string(in_avstream->codecpar->height).c_str(),
AV_OPT_SEARCH_CHILDREN);
if (ret < 0)
{
printf("av_opt_set error(%s)\n", GetFfmpegERR(ret));
goto END;
}
ret = av_opt_set(avfilter_ctx_buffer_src,
"pix_fmt", av_get_pix_fmt_name((AVPixelFormat)(in_avstream->codecpar->format)),
AV_OPT_SEARCH_CHILDREN);
if (ret < 0)
{
printf("av_opt_set error(%s)\n", GetFfmpegERR(ret));
goto END;
}
ret = av_opt_set_q(avfilter_ctx_buffer_src,
"time_base", in_avstream->time_base,
AV_OPT_SEARCH_CHILDREN);
if (ret < 0)
{
printf("av_opt_set error(%s)\n", GetFfmpegERR(ret));
goto END;
}
ret = av_opt_set_q(avfilter_ctx_buffer_src,
"frame_rate", in_avstream->r_frame_rate,
AV_OPT_SEARCH_CHILDREN);
if (ret < 0)
{
printf("av_opt_set error(%s)\n", GetFfmpegERR(ret));
goto END;
}
ret = av_opt_set_q(avfilter_ctx_buffer_src,
"pixel_aspect", pixel_aspect,
AV_OPT_SEARCH_CHILDREN);
if (ret < 0)
{
printf("av_opt_set error(%s)\n", GetFfmpegERR(ret));
goto END;
}
// 上面已经设置过参数,初始化就不传参了
ret = avfilter_init_str(avfilter_ctx_buffer_src, nullptr);
if (ret < 0)
{
printf("avfilter_init_str error(%s)\n", GetFfmpegERR(ret));
goto END;
}
avfilter_fps = avfilter_get_by_name("fps");
if (!avfilter_fps)
{
printf("avfilter_get_by_name error\n");
goto END;
}
printf("avfilter_fps->desc:%s\n", avfilter_fps->description);
avfilter_ctx_fps = avfilter_graph_alloc_filter(avfiltergraph, avfilter_fps, avfilter_fps->name);
if (!avfilter_ctx_fps)
{
printf("avfilter_graph_alloc_filter error\n");
goto END;
}
ret = av_opt_set(avfilter_ctx_fps,
"fps", "1",
AV_OPT_SEARCH_CHILDREN);
if (ret < 0)
{
printf("av_opt_set error(%s)\n", GetFfmpegERR(ret));
goto END;
}
ret = avfilter_init_str(avfilter_ctx_fps, nullptr);
if (ret < 0)
{
printf("avfilter_init_str error(%s)\n", GetFfmpegERR(ret));
goto END;
}
avfilter_buffer_sink = avfilter_get_by_name("buffersink");
if (!avfilter_buffer_sink)
{
printf("avfilter_get_by_name error\n");
goto END;
}
printf("avfilter_buffer_sink->desc:%s\n", avfilter_buffer_sink->description);
avfilter_ctx_buffer_sink = avfilter_graph_alloc_filter(avfiltergraph, avfilter_buffer_sink, avfilter_buffer_sink->name);
if (!avfilter_ctx_buffer_sink)
{
printf("avfilter_graph_alloc_filter error\n");
goto END;
}
ret = avfilter_init_str(avfilter_ctx_buffer_sink, nullptr);
if (ret < 0)
{
printf("avfilter_init_str error(%s)\n", GetFfmpegERR(ret));
goto END;
}
// 把avfilter组成链
// avfilter_link的作用,在src和dest之间新建AVFilterLink实例
// srcpad:src的输出通道编号
// destpad:dest的输入通道编号
ret = avfilter_link(avfilter_ctx_buffer_src, 0, avfilter_ctx_fps, 0);
if (ret != 0)
{
printf("avfilter_link error(%s)\n", GetFfmpegERR(ret));
goto END;
}
ret = avfilter_link(avfilter_ctx_fps, 0, avfilter_ctx_buffer_sink, 0);
if (ret != 0)
{
printf("avfilter_link error(%s)\n", GetFfmpegERR(ret));
goto END;
}
ret = avfilter_graph_config(avfiltergraph, nullptr);
if (ret < 0)
{
printf("avfilter_graph_config error(%s)\n", GetFfmpegERR(ret));
goto END;
}
ret = avformat_alloc_output_context2(&out_fmt_ctx, nullptr, nullptr, out_mp4);
if (ret < 0)
{
printf("avformat_alloc_output_context2 error(%s)\n", GetFfmpegERR(ret));
goto END;
}
out_fmt = out_fmt_ctx->oformat;
encoder = avcodec_find_encoder(decoder->id);
if (!encoder)
{
printf("avcodec_find_encoder error\n");
goto END;
}
encoder_ctx = avcodec_alloc_context3(encoder);
if (!encoder_ctx)
{
printf("avcodec_alloc_context3 error\n");
goto END;
}
encoder_ctx->pix_fmt = (AVPixelFormat)(av_buffersink_get_format(avfilter_ctx_buffer_sink));
encoder_ctx->width = av_buffersink_get_w(avfilter_ctx_buffer_sink);
encoder_ctx->height = av_buffersink_get_h(avfilter_ctx_buffer_sink);
encoder_ctx->time_base = av_buffersink_get_time_base(avfilter_ctx_buffer_sink);
encoder_ctx->framerate = av_buffersink_get_frame_rate(avfilter_ctx_buffer_sink);
encoder_ctx->gop_size = decoder_ctx->gop_size;
encoder_ctx->max_b_frames = decoder_ctx->max_b_frames;
if (out_fmt->flags & AVFMT_GLOBALHEADER)
{
encoder_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
ret = avcodec_open2(encoder_ctx, encoder, nullptr);
if (ret < 0)
{
printf("avcodec_open2 error(%s)\n", GetFfmpegERR(ret));
goto END;
}
out_avstream = avformat_new_stream(out_fmt_ctx, encoder);
if (!out_avstream)
{
printf("avformat_new_stream error\n");
goto END;
}
out_avstream->time_base = av_buffersink_get_time_base(avfilter_ctx_buffer_sink);
out_avstream->r_frame_rate = av_buffersink_get_frame_rate(avfilter_ctx_buffer_sink);
out_avstream->avg_frame_rate = av_buffersink_get_frame_rate(avfilter_ctx_buffer_sink);
ret = avcodec_parameters_from_context(out_avstream->codecpar, encoder_ctx);
if (ret < 0)
{
printf("avcodec_parameters_from_context error(%s)\n", GetFfmpegERR(ret));
goto END;
}
ret = avio_open(&(out_fmt_ctx->pb), out_mp4, AVIO_FLAG_WRITE);
if (ret < 0)
{
printf("avio_open error(%s)\n", GetFfmpegERR(ret));
goto END;
}
// 调用avformat_write_header后,out_avstream的时间基发生了变化(10->102400),容器要求?
ret = avformat_write_header(out_fmt_ctx, nullptr);
if (ret < 0)
{
printf("avformat_write_header error(%s)\n", GetFfmpegERR(ret));
goto END;
}
avpacket_src = av_packet_alloc();
if (!avpacket_src)
{
printf("av_packet_alloc error\n");
goto END;
}
avpacket_dest = av_packet_alloc();
if (!avpacket_dest)
{
printf("av_packet_alloc error\n");
goto END;
}
avframe_src = av_frame_alloc();
if (!avframe_src)
{
printf("av_frame_alloc error\n");
goto END;
}
avframe_dest = av_frame_alloc();
if (!avframe_dest)
{
printf("av_frame_alloc error\n");
goto END;
}
while (1)
{
// 从输入文件读取压缩视频帧
ret = av_read_frame(in_fmt_ctx, avpacket_src);
if (ret < 0)
{
printf("av_read_frame error(%s)\n", GetFfmpegERR(ret));
break;
}
if (avpacket_src->stream_index != video_stream_index)
{
av_packet_unref(avpacket_src);
continue;
}
// 把压缩视频帧发送给解码器
ret = avcodec_send_packet(decoder_ctx, avpacket_src);
if (ret != 0)
{
printf("avcodec_send_packet error(%s)\n", GetFfmpegERR(ret));
}
else
{
// 接收解码器的输出视频帧
ret = avcodec_receive_frame(decoder_ctx, avframe_src);
if (ret != 0)
{
printf("avcodec_receive_frame error(%s)\n", GetFfmpegERR(ret));
}
else
{
// 把视频帧交给avfilter
ret = av_buffersrc_write_frame(avfilter_ctx_buffer_src, avframe_src);
if (ret != 0)
{
printf("av_buffersrc_write_frame error(%s)\n", GetFfmpegERR(ret));
}
else
{
// 从avfilter获取视频帧
ret = av_buffersink_get_frame(avfilter_ctx_buffer_sink, avframe_dest);
if (ret < 0)
{
printf("av_buffersink_get_frame error(%s)\n", GetFfmpegERR(ret));
}
else
{
// 把视频帧交给编码器
ret = avcodec_send_frame(encoder_ctx, avframe_dest);
if (ret != 0)
{
printf("avcodec_send_frame error(%s)\n", GetFfmpegERR(ret));
}
else
{
// 从编码器获取压缩视频帧
ret = avcodec_receive_packet(encoder_ctx, avpacket_dest);
if (ret != 0)
{
printf("avcodec_receive_packet error(%s)\n", GetFfmpegERR(ret));
}
else
{
av_packet_rescale_ts(avpacket_dest, encoder_ctx->time_base, out_avstream->time_base);
// 写入到输出文件
ret = av_write_frame(out_fmt_ctx, avpacket_dest);
if (ret < 0)
{
printf("av_write_frame error(%s)\n", GetFfmpegERR(ret));
}
}
}
}
}
}
}
}
ret = av_write_trailer(out_fmt_ctx);
if (ret < 0)
{
printf("av_write_trailer error(%s)\n", GetFfmpegERR(ret));
goto END;
}
END:
if (avframe_dest)
{
av_frame_free(&avframe_dest);
avframe_dest = nullptr;
}
if (avframe_src)
{
av_frame_free(&avframe_src);
avframe_src = nullptr;
}
if (avpacket_src)
{
av_packet_free(&avpacket_src);
avpacket_src = nullptr;
}
if (avpacket_dest)
{
av_packet_free(&avpacket_dest);
avpacket_dest = nullptr;
}
if (avfilter_ctx_buffer_sink)
{
avfilter_free(avfilter_ctx_buffer_sink);
avfilter_ctx_buffer_sink = nullptr;
}
if (avfilter_ctx_fps)
{
avfilter_free(avfilter_ctx_fps);
avfilter_ctx_fps = nullptr;
}
if (avfilter_ctx_buffer_src)
{
avfilter_free(avfilter_ctx_buffer_src);
avfilter_ctx_buffer_src = nullptr;
}
if (avfiltergraph)
{
avfilter_graph_free(&avfiltergraph);
avfiltergraph = nullptr;
}
if (encoder_ctx)
{
avcodec_free_context(&encoder_ctx);
encoder_ctx = nullptr;
}
if (decoder_ctx)
{
avcodec_free_context(&decoder_ctx);
decoder_ctx = nullptr;
}
if (out_fmt_ctx)
{
avformat_free_context(out_fmt_ctx);
out_fmt_ctx = nullptr;
}
if (in_fmt_ctx)
{
avformat_free_context(in_fmt_ctx);
in_fmt_ctx = nullptr;
}
}