阅读量:0
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; } }