From 433d18a1d99dbfca48ca1b16e38a2a032d140a7a Mon Sep 17 00:00:00 2001 From: James Almer Date: Mon, 20 Oct 2025 19:04:22 -0300 Subject: [PATCH] fftools/ffmpeg_filter: handle metadata from stream group in relevant the filtergraphs Signed-off-by: James Almer --- fftools/ffmpeg.h | 13 ++++++- fftools/ffmpeg_filter.c | 85 ++++++++++++++++++++++++++++++++++++++--- fftools/ffmpeg_opt.c | 2 +- 3 files changed, 93 insertions(+), 7 deletions(-) diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h index 56dea32ac8..c866980251 100644 --- a/fftools/ffmpeg.h +++ b/fftools/ffmpeg.h @@ -300,6 +300,8 @@ enum OFilterFlags { // produce 24-bit audio OFILTER_FLAG_AUDIO_24BIT = (1 << 1), OFILTER_FLAG_AUTOSCALE = (1 << 2), + OFILTER_FLAG_AUTOROTATE = (1 << 3), + OFILTER_FLAG_CROP = (1 << 4), }; typedef struct OutputFilterOptions { @@ -333,6 +335,11 @@ typedef struct OutputFilterOptions { enum AVColorRange color_range; enum AVAlphaMode alpha_mode; + unsigned crop_top; + unsigned crop_bottom; + unsigned crop_left; + unsigned crop_right; + enum VideoSyncMethod vsync_method; AVRational frame_rate; AVRational max_frame_rate; @@ -348,6 +355,9 @@ typedef struct OutputFilterOptions { const enum AVColorRange *color_ranges; const enum AVAlphaMode *alpha_modes; + AVFrameSideData **side_data; + int nb_side_data; + // for simple filtergraphs only, view specifier passed // along to the decoder const ViewSpecifier *vs; @@ -827,7 +837,8 @@ int ofilter_bind_enc(OutputFilter *ofilter, * @param graph_desc Graph description; an av_malloc()ed string, filtergraph * takes ownership of it. */ -int fg_create(FilterGraph **pfg, char *graph_desc, Scheduler *sch); +int fg_create(FilterGraph **pfg, char *graph_desc, Scheduler *sch, + const OutputFilterOptions *opts); void fg_free(FilterGraph **pfg); diff --git a/fftools/ffmpeg_filter.c b/fftools/ffmpeg_filter.c index c8aec3d3f7..b0244fa774 100644 --- a/fftools/ffmpeg_filter.c +++ b/fftools/ffmpeg_filter.c @@ -204,6 +204,11 @@ typedef struct OutputFilterPriv { enum AVColorRange color_range; enum AVAlphaMode alpha_mode; + unsigned crop_top; + unsigned crop_bottom; + unsigned crop_left; + unsigned crop_right; + AVFrameSideData **side_data; int nb_side_data; @@ -228,6 +233,8 @@ typedef struct OutputFilterPriv { const enum AVColorRange *color_ranges; const enum AVAlphaMode *alpha_modes; + int32_t displaymatrix[9]; + AVRational enc_timebase; int64_t trim_start_us; int64_t trim_duration_us; @@ -818,7 +825,7 @@ int ofilter_bind_enc(OutputFilter *ofilter, unsigned sched_idx_enc, ofp->needed = ofilter->bound = 1; av_freep(&ofilter->linklabel); - ofp->flags = opts->flags; + ofp->flags |= opts->flags; ofp->ts_offset = opts->ts_offset; ofp->enc_timebase = opts->output_tb; @@ -1078,7 +1085,8 @@ static const AVClass fg_class = { .category = AV_CLASS_CATEGORY_FILTER, }; -int fg_create(FilterGraph **pfg, char *graph_desc, Scheduler *sch) +int fg_create(FilterGraph **pfg, char *graph_desc, Scheduler *sch, + const OutputFilterOptions *opts) { FilterGraphPriv *fgp; FilterGraph *fg; @@ -1175,11 +1183,13 @@ int fg_create(FilterGraph **pfg, char *graph_desc, Scheduler *sch) const enum AVMediaType type = avfilter_pad_get_type(cur->filter_ctx->output_pads, cur->pad_idx); OutputFilter *const ofilter = ofilter_alloc(fg, type); + OutputFilterPriv *ofp; if (!ofilter) { ret = AVERROR(ENOMEM); goto fail; } + ofp = ofp_from_ofilter(ofilter); ofilter->linklabel = cur->name; cur->name = NULL; @@ -1189,6 +1199,25 @@ int fg_create(FilterGraph **pfg, char *graph_desc, Scheduler *sch) ret = AVERROR(ENOMEM); goto fail; } + + // opts should only be needed in this function to fill fields from filtergraphs + // whose output is meant to be treated as if it was stream, e.g. merged HEIF + // tile groups. + if (opts) { + ofp->flags = opts->flags; + ofp->side_data = opts->side_data; + ofp->nb_side_data = opts->nb_side_data; + + ofp->crop_top = opts->crop_top; + ofp->crop_bottom = opts->crop_bottom; + ofp->crop_left = opts->crop_left; + ofp->crop_right = opts->crop_right; + + const AVFrameSideData *sd = av_frame_side_data_get(ofp->side_data, ofp->nb_side_data, + AV_FRAME_DATA_DISPLAYMATRIX); + if (sd) + memcpy(ofp->displaymatrix, sd->data, sizeof(ofp->displaymatrix)); + } } if (!fg->nb_outputs) { @@ -1225,7 +1254,7 @@ int fg_create_simple(FilterGraph **pfg, FilterGraphPriv *fgp; int ret; - ret = fg_create(pfg, graph_desc, sch); + ret = fg_create(pfg, graph_desc, sch, NULL); if (ret < 0) return ret; fg = *pfg; @@ -1603,6 +1632,53 @@ static int configure_output_video_filter(FilterGraphPriv *fgp, AVFilterGraph *gr if (ret < 0) return ret; + if (ofp->flags & OFILTER_FLAG_CROP) { + char crop_buf[64]; + snprintf(crop_buf, sizeof(crop_buf), "w=iw-%u-%u:h=ih-%u-%u:x=%u:y=%u", + ofp->crop_left, ofp->crop_right, + ofp->crop_top, ofp->crop_bottom, + ofp->crop_left, ofp->crop_top); + ret = insert_filter(&last_filter, &pad_idx, "crop", crop_buf); + if (ret < 0) + return ret; + } + + if (ofp->flags & OFILTER_FLAG_AUTOROTATE) { + int32_t *displaymatrix = ofp->displaymatrix; + double theta; + + theta = get_rotation(displaymatrix); + + if (fabs(theta - 90) < 1.0) { + ret = insert_filter(&last_filter, &pad_idx, "transpose", + displaymatrix[3] > 0 ? "cclock_flip" : "clock"); + } else if (fabs(theta - 180) < 1.0) { + if (displaymatrix[0] < 0) { + ret = insert_filter(&last_filter, &pad_idx, "hflip", NULL); + if (ret < 0) + return ret; + } + if (displaymatrix[4] < 0) { + ret = insert_filter(&last_filter, &pad_idx, "vflip", NULL); + } + } else if (fabs(theta - 270) < 1.0) { + ret = insert_filter(&last_filter, &pad_idx, "transpose", + displaymatrix[3] < 0 ? "clock_flip" : "cclock"); + } else if (fabs(theta) > 1.0) { + char rotate_buf[64]; + snprintf(rotate_buf, sizeof(rotate_buf), "%f*PI/180", theta); + ret = insert_filter(&last_filter, &pad_idx, "rotate", rotate_buf); + } else if (fabs(theta) < 1.0) { + if (displaymatrix && displaymatrix[4] < 0) { + ret = insert_filter(&last_filter, &pad_idx, "vflip", NULL); + } + } + if (ret < 0) + return ret; + + av_frame_side_data_remove(&ofp->side_data, &ofp->nb_side_data, AV_FRAME_DATA_DISPLAYMATRIX); + } + if ((ofp->width || ofp->height) && (ofp->flags & OFILTER_FLAG_AUTOSCALE)) { char args[255]; AVFilterContext *filter; @@ -2100,12 +2176,11 @@ static int configure_filtergraph(FilterGraph *fg, FilterGraphThread *fgt) ret = av_buffersink_get_ch_layout(sink, &ofp->ch_layout); if (ret < 0) goto fail; - av_frame_side_data_free(&ofp->side_data, &ofp->nb_side_data); sd = av_buffersink_get_side_data(sink, &nb_sd); if (nb_sd) for (int j = 0; j < nb_sd; j++) { ret = av_frame_side_data_clone(&ofp->side_data, &ofp->nb_side_data, - sd[j], 0); + sd[j], AV_FRAME_SIDE_DATA_FLAG_REPLACE); if (ret < 0) { av_frame_side_data_free(&ofp->side_data, &ofp->nb_side_data); goto fail; diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c index 381360c16a..f51523d2e0 100644 --- a/fftools/ffmpeg_opt.c +++ b/fftools/ffmpeg_opt.c @@ -1496,7 +1496,7 @@ int ffmpeg_parse_options(int argc, char **argv, Scheduler *sch) /* create complex filtergraphs */ for (int i = 0; i < go.nb_filtergraphs; i++) { - ret = fg_create(NULL, go.filtergraphs[i], sch); + ret = fg_create(NULL, go.filtergraphs[i], sch, NULL); go.filtergraphs[i] = NULL; if (ret < 0) goto fail;