使用FFMPEG(Visual Studio/C++)保存视频帧为JPG失败求助
问题描述
我尝试将MP4视频的所有帧保存为单独的JPG文件,现有代码可运行并生成文件,但这些文件无法被识别为图片,无法显示内容。
使用环境:Windows11系统 + Visual Studio2022,FFMPEG版本为5.1。负责保存图片的save_frame_as_jpeg函数改编自相关代码,并根据FFmpeg官方文档将avcodec_encode_video2替换为avcodec_send_frame/avcodec_receive_packet。
我清楚使用命令行ffmpeg -i input.mp4 -vf fps=1 vid_%d.png可实现该功能,但需求要求必须通过代码完成,恳请各位提供帮助,谢谢!
现有代码
// FfmpegTests.cpp : This file contains the 'main' function. Program execution begins and ends there. // #pragma warning(disable : 4996) extern "C" { #include "libavformat/avformat.h" #include "libavcodec/avcodec.h" #include "libavfilter/avfilter.h" #include "libavutil/opt.h" #include "libavutil/avutil.h" #include "libavutil/error.h" #include "libavfilter/buffersrc.h" #include "libavfilter/buffersink.h" #include "libswscale/swscale.h" } #pragma comment(lib, "avcodec.lib") #pragma comment(lib, "avformat.lib") #pragma comment(lib, "avfilter.lib") #pragma comment(lib, "avutil.lib") #pragma comment(lib, "swscale.lib") #include <cstdio> #include <iostream> #include <chrono> #include <thread> static AVFormatContext* fmt_ctx; static AVCodecContext* dec_ctx; AVFilterGraph* filter_graph; AVFilterContext* buffersrc_ctx; AVFilterContext* buffersink_ctx; static int video_stream_index = -1; const char* filter_descr = "scale=78:24,transpose=cclock"; static int64_t last_pts = AV_NOPTS_VALUE; static int open_input_file(const char* filename) { const AVCodec* dec; int ret; if ((ret = avformat_open_input(&fmt_ctx, filename, NULL, NULL)) < 0) { av_log(NULL, AV_LOG_ERROR, "Cannot open input file\n"); return ret; } if ((ret = avformat_find_stream_info(fmt_ctx, NULL)) < 0) { av_log(NULL, AV_LOG_ERROR, "Cannot find stream information\n"); return ret; } /* select the video stream */ ret = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &dec, 0); if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Cannot find a video stream in the input file\n"); return ret; } video_stream_index = ret; /* create decoding context */ dec_ctx = avcodec_alloc_context3(dec); if (!dec_ctx) return AVERROR(ENOMEM); avcodec_parameters_to_context(dec_ctx, fmt_ctx->streams[video_stream_index]->codecpar); /* init the video decoder */ if ((ret = avcodec_open2(dec_ctx, dec, NULL)) < 0) { av_log(NULL, AV_LOG_ERROR, "Cannot open video decoder\n"); return ret; } return 0; } static int init_filters(const char* filters_descr) { char args[512]; int ret = 0; const AVFilter* buffersrc = avfilter_get_by_name("buffer"); const AVFilter* buffersink = avfilter_get_by_name("buffersink"); AVFilterInOut* outputs = avfilter_inout_alloc(); AVFilterInOut* inputs = avfilter_inout_alloc(); AVRational time_base = fmt_ctx->streams[video_stream_index]->time_base; enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE }; filter_graph = avfilter_graph_alloc(); if (!outputs || !inputs || !filter_graph) { ret = AVERROR(ENOMEM); goto end; } /* buffer video source: the decoded frames from the decoder will be inserted here. */ snprintf(args, sizeof(args), "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d", dec_ctx->width, dec_ctx->height, dec_ctx->pix_fmt, time_base.num, time_base.den, dec_ctx->sample_aspect_ratio.num, dec_ctx->sample_aspect_ratio.den); ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in", args, NULL, filter_graph); if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Cannot create buffer source\n"); goto end; } /* buffer video sink: to terminate the filter chain. */ ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out", NULL, NULL, filter_graph); if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Cannot create buffer sink\n"); goto end; } ret = av_opt_set_int_list(buffersink_ctx, "pix_fmts", pix_fmts, AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN); if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Cannot set output pixel format\n"); goto end; } outputs->name = av_strdup("in"); outputs->filter_ctx = buffersrc_ctx; outputs->pad_idx = 0; outputs->next = NULL; inputs->name = av_strdup("out"); inputs->filter_ctx = buffersink_ctx; inputs->pad_idx = 0; inputs->next = NULL; if ((ret = avfilter_graph_parse_ptr(filter_graph, filters_descr, &inputs, &outputs, NULL)) < 0) goto end; if ((ret = avfilter_graph_config(filter_graph, NULL)) < 0) goto end; end: avfilter_inout_free(&inputs); avfilter_inout_free(&outputs); return ret; } static void display_frame(const AVFrame* frame, AVRational time_base) { int x, y; uint8_t* p0, * p; int64_t delay; if (frame->pts != AV_NOPTS_VALUE) { if (last_pts != AV_NOPTS_VALUE) { /* sleep roughly the right amount of time; * usleep is in microseconds, just like AV_TIME_BASE. */ AVRational timeBaseQ; timeBaseQ.num = 1; timeBaseQ.den = AV_TIME_BASE; delay = av_rescale_q(frame->pts - last_pts, time_base, timeBaseQ); if (delay > 0 && delay < 1000000) std::this_thread::sleep_for(std::chrono::microseconds(delay)); } last_pts = frame->pts; } /* Trivial ASCII grayscale display. */ p0 = frame->data[0]; puts("\033c"); for (y = 0; y < frame->height; y++) { p = p0; for (x = 0; x < frame->width; x++) putchar(" .-+#"[*(p++) / 52]); putchar('\n'); p0 += frame->linesize[0]; } fflush(stdout); } int save_frame_as_jpeg(AVCodecContext* pCodecCtx, AVFrame* pFrame, int FrameNo) { int ret = 0; const AVCodec* jpegCodec = avcodec_find_encoder(AV_CODEC_ID_JPEG2000); if (!jpegCodec) { return -1; } AVCodecContext* jpegContext = avcodec_alloc_context3(jpegCodec); if (!jpegContext) { return -1; } jpegContext->pix_fmt = pCodecCtx->pix_fmt; jpegContext->height = pFrame->height; jpegContext->width = pFrame->width; jpegContext->time_base = AVRational{ 1,10 }; ret = avcodec_open2(jpegContext, jpegCodec, NULL); if (ret < 0) { return ret; } FILE* JPEGFile; char JPEGFName[256]; AVPacket packet; packet.data = NULL; packet.size = 0; av_init_packet(&packet); int gotFrame; ret = avcodec_send_frame(jpegContext, pFrame); if (ret < 0) { return ret; } ret = avcodec_receive_packet(jpegContext, &packet); if (ret < 0) { return ret; } sprintf(JPEGFName, "c:\\folder\\dvr-%06d.jpg", FrameNo); JPEGFile = fopen(JPEGFName, "wb"); fwrite(packet.data, 1, packet.size, JPEGFile); fclose(JPEGFile); av_packet_unref(&packet); avcodec_close(jpegContext); return 0; } int main(int argc, char** argv) { AVFrame* frame; AVFrame* filt_frame; AVPacket* packet; int ret; if (argc != 2) { fprintf(stderr, "Usage: %s file\n", argv[0]); exit(1); } frame = av_frame_alloc(); filt_frame = av_frame_alloc(); packet = av_packet_alloc(); if (!frame || !filt_frame || !packet) { fprintf(stderr, "Could not allocate frame or packet\n"); exit(1); } if ((ret = open_input_file(argv[1])) < 0) goto end; if ((ret = init_filters(filter_descr)) < 0) goto end; while (true) { if ((ret = av_read_frame(fmt_ctx, packet)) < 0) break; if (packet->stream_index == video_stream_index) { ret = avcodec_send_packet(dec_ctx, packet); if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Error while sending a packet to the decoder\n"); break; } while (ret >= 0) { ret = avcodec_receive_frame(dec_ctx, frame); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { break; } else if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Error while receiving a frame from the decoder\n"); goto end; } frame->pts = frame->best_effort_timestamp; /* push the decoded frame into the filtergraph */ if (av_buffersrc_add_frame_flags(buffersrc_ctx, frame, AV_BUFFERSRC_FLAG_KEEP_REF) < 0) { av_log(NULL, AV_LOG_ERROR, "Error while feeding the filtergraph\n"); break; } /* pull filtered frames from the filtergraph */ while (1) { ret = av_buffersink_get_frame(buffersink_ctx, filt_frame); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) break; if (ret < 0) goto end; display_frame(filt_frame, buffersink_ctx->inputs[0]->time_base); av_frame_unref(filt_frame); ret = save_frame_as_jpeg(dec_ctx, frame, dec_ctx->frame_number); if (ret < 0) goto end; } av_frame_unref(frame); } } av_packet_unref(packet); } end: avfilter_graph_free(&filter_graph); avcodec_free_context(&dec_ctx); avformat_close_input(&fmt_ctx); av_frame_free(&frame); av_frame_free(&filt_frame); av_packet_free(&packet); if (ret < 0 && ret != AVERROR_EOF) { char errBuf[AV_ERROR_MAX_STRING_SIZE]{0}; int res = av_strerror(ret, errBuf, AV_ERROR_MAX_STRING_SIZE); fprintf(stderr, "Error: %s\n", errBuf); exit(1); } exit(0); }
内容的提问来源于stack exchange,提问作者Diego Satizabal
相关产品推荐
相关产品推荐

