From a7f3388119ca7f53e7316f61e8ad9409a4cb5bb9 Mon Sep 17 00:00:00 2001 From: gnattu Date: Sun, 22 Dec 2024 15:06:36 +0800 Subject: [PATCH 1/2] fftools/ffprobe: add only_first_vframe option to show first video frame Currently, ffprobe can only read frames at a specific interval, and users have no options to select only video frames without explicitly selecting a stream. This option instructs show_frames to pick the first video frame and prints its information. This will be useful for extracting tricky metadata, such as the HDR10plus ST2094 metadata. --- ...085-add-first-vframe-only-to-ffprobe.patch | 44 +++++++++++++++++++ debian/patches/series | 1 + 2 files changed, 45 insertions(+) create mode 100644 debian/patches/0085-add-first-vframe-only-to-ffprobe.patch diff --git a/debian/patches/0085-add-first-vframe-only-to-ffprobe.patch b/debian/patches/0085-add-first-vframe-only-to-ffprobe.patch new file mode 100644 index 0000000000..6195f1ac72 --- /dev/null +++ b/debian/patches/0085-add-first-vframe-only-to-ffprobe.patch @@ -0,0 +1,44 @@ +Index: FFmpeg/fftools/ffprobe.c +=================================================================== +--- FFmpeg.orig/fftools/ffprobe.c ++++ FFmpeg/fftools/ffprobe.c +@@ -146,6 +146,8 @@ static int show_private_data + #define SHOW_OPTIONAL_FIELDS_ALWAYS 1 + static int show_optional_fields = SHOW_OPTIONAL_FIELDS_AUTO; + ++static int only_show_first_video_frame = 0; ++ + static char *output_format; + static char *stream_specifier; + static char *show_data_hash; +@@ -3159,6 +3161,14 @@ static int read_interval_packets(WriterC + } + + frame_count++; ++ ++ if (only_show_first_video_frame) { ++ AVCodecParameters *par = ifile->streams[pkt->stream_index].st->codecpar; ++ if (par->codec_type != AVMEDIA_TYPE_VIDEO) { ++ continue; ++ } ++ } ++ + if (do_read_packets) { + if (do_show_packets) + show_packet(w, ifile, pkt, i++); +@@ -3179,6 +3189,7 @@ static int read_interval_packets(WriterC + + while (process_frame(w, ifile, frame, pkt, &packet_new) > 0); + } ++ if (only_show_first_video_frame) break; + } + av_packet_unref(pkt); + } +@@ -4587,6 +4598,7 @@ static const OptionDef real_options[] = + { "print_filename", OPT_TYPE_FUNC, OPT_FUNC_ARG, {.func_arg = opt_print_filename}, "override the printed input filename", "print_file"}, + { "find_stream_info", OPT_TYPE_BOOL, OPT_INPUT | OPT_EXPERT, { &find_stream_info }, + "read and decode the streams to fill missing information with heuristics" }, ++ { "only_first_vframe", OPT_TYPE_BOOL, 0, { &only_show_first_video_frame }, "only show first video frame when show_frames is used" }, + { NULL, }, + }; + diff --git a/debian/patches/series b/debian/patches/series index bddc6b0850..8fc89ca83f 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -82,3 +82,4 @@ 0082-fix-ass-incorrect-null-copy.patch 0083-default-to-input-timebase-for-streamcopy.patch 0084-fix-a-race-condition-in-vaapi-csc-to-yuv420p.patch +0085-add-first-vframe-only-to-ffprobe.patch From fce96f7d1f0bddb82dc402323951f8742455b84d Mon Sep 17 00:00:00 2001 From: gnattu Date: Sun, 22 Dec 2024 18:01:27 +0800 Subject: [PATCH 2/2] fftools/ffprobe: handle multiple video streams --- ...085-add-first-vframe-only-to-ffprobe.patch | 96 +++++++++++++++++-- 1 file changed, 89 insertions(+), 7 deletions(-) diff --git a/debian/patches/0085-add-first-vframe-only-to-ffprobe.patch b/debian/patches/0085-add-first-vframe-only-to-ffprobe.patch index 6195f1ac72..e343f340a4 100644 --- a/debian/patches/0085-add-first-vframe-only-to-ffprobe.patch +++ b/debian/patches/0085-add-first-vframe-only-to-ffprobe.patch @@ -11,30 +11,112 @@ Index: FFmpeg/fftools/ffprobe.c static char *output_format; static char *stream_specifier; static char *show_data_hash; -@@ -3159,6 +3161,14 @@ static int read_interval_packets(WriterC +@@ -3086,9 +3088,10 @@ static int read_interval_packets(WriterC + AVFormatContext *fmt_ctx = ifile->fmt_ctx; + AVPacket *pkt = NULL; + AVFrame *frame = NULL; +- int ret = 0, i = 0, frame_count = 0; ++ int ret = 0, i = 0, frame_count = 0, nb_video_streams = 0; + int64_t start = -INT64_MAX, end = interval->end; + int has_start = 0, has_end = interval->has_end && !interval->end_is_offset; ++ unsigned char *checked_stream = NULL; + + av_log(NULL, AV_LOG_VERBOSE, "Processing read interval "); + log_read_interval(interval, NULL, AV_LOG_VERBOSE); +@@ -3127,12 +3130,51 @@ static int read_interval_packets(WriterC + ret = AVERROR(ENOMEM); + goto end; + } ++ ++ if (only_show_first_video_frame) { ++ int si = 0; ++ checked_stream = av_calloc(nb_streams, sizeof(unsigned char)); ++ if (!checked_stream) { ++ ret = AVERROR(ENOMEM); ++ goto end; ++ } ++ for (si = 0; si < nb_streams; si ++) { ++ AVCodecParameters *par = ifile->streams[si].st->codecpar; ++ if (par->codec_type == AVMEDIA_TYPE_VIDEO) { ++ nb_video_streams++; ++ selected_streams[si] = 1; ++ } ++ } ++ } + while (!av_read_frame(fmt_ctx, pkt)) { + if (fmt_ctx->nb_streams > nb_streams) { + REALLOCZ_ARRAY_STREAM(nb_streams_frames, nb_streams, fmt_ctx->nb_streams); + REALLOCZ_ARRAY_STREAM(nb_streams_packets, nb_streams, fmt_ctx->nb_streams); + REALLOCZ_ARRAY_STREAM(selected_streams, nb_streams, fmt_ctx->nb_streams); +- nb_streams = fmt_ctx->nb_streams; ++ ++ if (checked_stream) { ++ unsigned char *checked_stream_extended = NULL; ++ int si = 0; ++ checked_stream_extended = av_calloc(fmt_ctx->nb_streams, sizeof(unsigned char)); ++ if (!checked_stream) { ++ ret = AVERROR(ENOMEM); ++ goto end; ++ } ++ ++ memcpy(checked_stream_extended, checked_stream, nb_streams * sizeof(unsigned char)); ++ av_freep(&checked_stream); ++ checked_stream = checked_stream_extended; ++ ++ nb_video_streams = 0; ++ for (si = 0; si < fmt_ctx->nb_streams; si ++) { ++ AVCodecParameters *par = ifile->streams[si].st->codecpar; ++ if (par->codec_type == AVMEDIA_TYPE_VIDEO) { ++ nb_video_streams++; ++ selected_streams[si] = 1; ++ } ++ } ++ } ++ nb_streams = (int)fmt_ctx->nb_streams; + } + if (selected_streams[pkt->stream_index]) { + AVRational tb = ifile->streams[pkt->stream_index].st->time_base; +@@ -3159,6 +3201,12 @@ static int read_interval_packets(WriterC } frame_count++; + + if (only_show_first_video_frame) { + AVCodecParameters *par = ifile->streams[pkt->stream_index].st->codecpar; -+ if (par->codec_type != AVMEDIA_TYPE_VIDEO) { -+ continue; -+ } ++ if (par->codec_type != AVMEDIA_TYPE_VIDEO) continue; + } + if (do_read_packets) { if (do_show_packets) show_packet(w, ifile, pkt, i++); -@@ -3179,6 +3189,7 @@ static int read_interval_packets(WriterC +@@ -3179,6 +3227,16 @@ static int read_interval_packets(WriterC while (process_frame(w, ifile, frame, pkt, &packet_new) > 0); } -+ if (only_show_first_video_frame) break; ++ if (only_show_first_video_frame) { ++ int nb_checked_streams = 0, si = 0; ++ checked_stream[pkt->stream_index] = 1; ++ for (si = 0; si < nb_streams; si ++) { ++ nb_checked_streams += checked_stream[si]; ++ } ++ if (nb_checked_streams >= nb_video_streams) { ++ break; ++ } ++ } } av_packet_unref(pkt); } -@@ -4587,6 +4598,7 @@ static const OptionDef real_options[] = +@@ -3196,6 +3254,9 @@ static int read_interval_packets(WriterC + end: + av_frame_free(&frame); + av_packet_free(&pkt); ++ if (checked_stream) { ++ av_freep(&checked_stream); ++ } + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "Could not read packets in interval "); + log_read_interval(interval, NULL, AV_LOG_ERROR); +@@ -4587,6 +4648,7 @@ static const OptionDef real_options[] = { "print_filename", OPT_TYPE_FUNC, OPT_FUNC_ARG, {.func_arg = opt_print_filename}, "override the printed input filename", "print_file"}, { "find_stream_info", OPT_TYPE_BOOL, OPT_INPUT | OPT_EXPERT, { &find_stream_info }, "read and decode the streams to fill missing information with heuristics" },