* [FFmpeg-devel] [PATCH 01/18] fftools/cmdutils: fix printing group name in split_commandline() @ 2024-03-06 11:03 Anton Khirnov 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 02/18] fftools/ffmpeg_filter: refactor setting input timebase Anton Khirnov ` (17 more replies) 0 siblings, 18 replies; 38+ messages in thread From: Anton Khirnov @ 2024-03-06 11:03 UTC (permalink / raw) To: ffmpeg-devel --- fftools/cmdutils.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/fftools/cmdutils.c b/fftools/cmdutils.c index 008949a39d..8efec942bd 100644 --- a/fftools/cmdutils.c +++ b/fftools/cmdutils.c @@ -793,7 +793,7 @@ int split_commandline(OptionParseContext *octx, int argc, char *argv[], while (optindex < argc) { const char *opt = argv[optindex++], *arg; const OptionDef *po; - int ret; + int ret, group_idx; av_log(NULL, AV_LOG_DEBUG, "Reading option '%s' ...", opt); @@ -822,14 +822,15 @@ do { \ } while (0) /* named group separators, e.g. -i */ - if ((ret = match_group_separator(groups, nb_groups, opt)) >= 0) { + group_idx = match_group_separator(groups, nb_groups, opt); + if (group_idx >= 0) { GET_ARG(arg); - ret = finish_group(octx, ret, arg); + ret = finish_group(octx, group_idx, arg); if (ret < 0) return ret; av_log(NULL, AV_LOG_DEBUG, " matched as %s with argument '%s'.\n", - groups[ret].name, arg); + groups[group_idx].name, arg); continue; } -- 2.43.0 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 38+ messages in thread
* [FFmpeg-devel] [PATCH 02/18] fftools/ffmpeg_filter: refactor setting input timebase 2024-03-06 11:03 [FFmpeg-devel] [PATCH 01/18] fftools/cmdutils: fix printing group name in split_commandline() Anton Khirnov @ 2024-03-06 11:03 ` Anton Khirnov 2024-03-07 20:37 ` Michael Niedermayer 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 03/18] fftools/ffmpeg_filter: drop unused InputFilterPriv.ist Anton Khirnov ` (16 subsequent siblings) 17 siblings, 1 reply; 38+ messages in thread From: Anton Khirnov @ 2024-03-06 11:03 UTC (permalink / raw) To: ffmpeg-devel Treat it analogously to stream parameters like format/dimensions/etc. This is functionally different from previous code in 2 ways: * for non-CFR video, the frame timebase (set by the decoder) is used rather than the demuxer timebase * for sub2video, AV_TIME_BASE_Q is used, which is hardcoded by the subtitle decoding API These changes should avoid unnecessary and potentially lossy timestamp conversions from decoder timebase into the demuxer one. Changes the timebases used in sub2video tests. --- fftools/ffmpeg_filter.c | 17 ++- tests/ref/fate/sub2video_basic | 182 +++++++++++++------------- tests/ref/fate/sub2video_time_limited | 8 +- 3 files changed, 106 insertions(+), 101 deletions(-) diff --git a/fftools/ffmpeg_filter.c b/fftools/ffmpeg_filter.c index 13c5065191..9d9762a599 100644 --- a/fftools/ffmpeg_filter.c +++ b/fftools/ffmpeg_filter.c @@ -148,6 +148,8 @@ typedef struct InputFilterPriv { // fallback parameters to use when no input is ever sent struct { + AVRational time_base; + int format; int width; @@ -696,6 +698,8 @@ static int ifilter_bind_ist(InputFilter *ifilter, InputStream *ist) palettes for all rectangles are identical or compatible */ ifp->format = AV_PIX_FMT_RGB32; + ifp->time_base = AV_TIME_BASE_Q; + av_log(fgp, AV_LOG_VERBOSE, "sub2video: using %dx%d canvas\n", ifp->width, ifp->height); } @@ -1469,7 +1473,6 @@ static int configure_input_video_filter(FilterGraph *fg, AVFilterGraph *graph, AVFilterContext *last_filter; const AVFilter *buffer_filt = avfilter_get_by_name("buffer"); const AVPixFmtDescriptor *desc; - InputStream *ist = ifp->ist; AVRational fr = ifp->opts.framerate; AVRational sar; AVBPrint args; @@ -1482,9 +1485,6 @@ static int configure_input_video_filter(FilterGraph *fg, AVFilterGraph *graph, if (ifp->type_src == AVMEDIA_TYPE_SUBTITLE) sub2video_prepare(ifp); - ifp->time_base = (ifp->opts.flags & IFILTER_FLAG_CFR) ? - av_inv_q(ifp->opts.framerate) : ist->st->time_base; - sar = ifp->sample_aspect_ratio; if(!sar.den) sar = (AVRational){0,1}; @@ -1575,8 +1575,6 @@ static int configure_input_audio_filter(FilterGraph *fg, AVFilterGraph *graph, char name[255]; int ret, pad_idx = 0; - ifp->time_base = (AVRational){ 1, ifp->sample_rate }; - av_bprint_init(&args, 0, AV_BPRINT_SIZE_AUTOMATIC); av_bprintf(&args, "time_base=%d/%d:sample_rate=%d:sample_fmt=%s", ifp->time_base.num, ifp->time_base.den, @@ -1804,6 +1802,8 @@ int ifilter_parameters_from_dec(InputFilter *ifilter, const AVCodecContext *dec) { InputFilterPriv *ifp = ifp_from_ifilter(ifilter); + ifp->fallback.time_base = dec->pkt_timebase; + if (dec->codec_type == AVMEDIA_TYPE_VIDEO) { ifp->fallback.format = dec->pix_fmt; ifp->fallback.width = dec->width; @@ -1835,6 +1835,10 @@ static int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *fr if (ret < 0) return ret; + ifp->time_base = (ifp->type == AVMEDIA_TYPE_AUDIO) ? (AVRational){ 1, frame->sample_rate } : + (ifp->opts.flags & IFILTER_FLAG_CFR) ? av_inv_q(ifp->opts.framerate) : + frame->time_base; + ifp->format = frame->format; ifp->width = frame->width; @@ -2524,6 +2528,7 @@ static int send_eof(FilterGraphThread *fgt, InputFilter *ifilter, ifp->sample_aspect_ratio = ifp->fallback.sample_aspect_ratio; ifp->color_space = ifp->fallback.color_space; ifp->color_range = ifp->fallback.color_range; + ifp->time_base = ifp->fallback.time_base; ret = av_channel_layout_copy(&ifp->ch_layout, &ifp->fallback.ch_layout); diff --git a/tests/ref/fate/sub2video_basic b/tests/ref/fate/sub2video_basic index a6eb1a34ea..2e4dcb625e 100644 --- a/tests/ref/fate/sub2video_basic +++ b/tests/ref/fate/sub2video_basic @@ -1,95 +1,95 @@ -#tb 0: 1/1000 +#tb 0: 1/1000000 #media_type 0: video #codec_id 0: rawvideo #dimensions 0: 720x480 #sar 0: 0/1 -0, 132499, 132499, 0, 1382400, 0x00000000 -0, 132499, 132499, 0, 1382400, 0x8c93c2ba -0, 137459, 137459, 0, 1382400, 0x00000000 -0, 147355, 147355, 0, 1382400, 0xb02e32ca -0, 152088, 152088, 0, 1382400, 0x00000000 -0, 180797, 180797, 0, 1382400, 0x83b71116 -0, 183357, 183357, 0, 1382400, 0x00000000 -0, 183433, 183433, 0, 1382400, 0x85547fd1 -0, 185799, 185799, 0, 1382400, 0x00000000 -0, 185909, 185909, 0, 1382400, 0x00000000 -0, 185910, 185910, 0, 1382400, 0xb6a8f181 -0, 188606, 188606, 0, 1382400, 0x00000000 -0, 188663, 188663, 0, 1382400, 0xb64d1a2c -0, 189925, 189925, 0, 1382400, 0x00000000 -0, 190014, 190014, 0, 1382400, 0x7b37ecf3 -0, 191675, 191675, 0, 1382400, 0x00000000 -0, 199724, 199724, 0, 1382400, 0xdc025bd1 -0, 201089, 201089, 0, 1382400, 0x00000000 -0, 201175, 201175, 0, 1382400, 0x688b294d -0, 202733, 202733, 0, 1382400, 0x00000000 -0, 202819, 202819, 0, 1382400, 0xa2b33d1b -0, 204684, 204684, 0, 1382400, 0x00000000 -0, 204762, 204762, 0, 1382400, 0xb3e525e3 -0, 206730, 206730, 0, 1382400, 0x00000000 -0, 206806, 206806, 0, 1382400, 0xaa8fbdd7 -0, 208637, 208637, 0, 1382400, 0x00000000 -0, 208716, 208716, 0, 1382400, 0x7b7f26dd -0, 209978, 209978, 0, 1382400, 0x00000000 -0, 210051, 210051, 0, 1382400, 0x15e2f836 -0, 211575, 211575, 0, 1382400, 0x00000000 -0, 211644, 211644, 0, 1382400, 0x0fee9b0c -0, 214306, 214306, 0, 1382400, 0x00000000 -0, 214380, 214380, 0, 1382400, 0x89d62791 -0, 217144, 217144, 0, 1382400, 0x00000000 -0, 217225, 217225, 0, 1382400, 0xa6a9fd74 -0, 219591, 219591, 0, 1382400, 0x00000000 -0, 219652, 219652, 0, 1382400, 0x7896178d -0, 221483, 221483, 0, 1382400, 0x00000000 -0, 223531, 223531, 0, 1382400, 0x01751a52 -0, 225863, 225863, 0, 1382400, 0x00000000 -0, 227510, 227510, 0, 1382400, 0xa3959c6f -0, 230809, 230809, 0, 1382400, 0x00000000 -0, 230872, 230872, 0, 1382400, 0x3d3ea47b -0, 233033, 233033, 0, 1382400, 0x00000000 -0, 233124, 233124, 0, 1382400, 0x593f8b24 -0, 237220, 237220, 0, 1382400, 0x00000000 -0, 237303, 237303, 0, 1382400, 0x171f05ba -0, 240033, 240033, 0, 1382400, 0x00000000 -0, 240106, 240106, 0, 1382400, 0xb014cdf1 -0, 242165, 242165, 0, 1382400, 0x00000000 -0, 273556, 273556, 0, 1382400, 0xd918e667 -0, 275217, 275217, 0, 1382400, 0x00000000 -0, 295445, 295445, 0, 1382400, 0xc9406331 -0, 296776, 296776, 0, 1382400, 0x00000000 -0, 300049, 300049, 0, 1382400, 0xaf08b10d -0, 301949, 301949, 0, 1382400, 0x00000000 -0, 302034, 302034, 0, 1382400, 0x00000000 -0, 302035, 302035, 0, 1382400, 0x853a9d93 -0, 303559, 303559, 0, 1382400, 0x00000000 -0, 304203, 304203, 0, 1382400, 0x7491a87d -0, 305898, 305898, 0, 1382400, 0x00000000 -0, 305947, 305947, 0, 1382400, 0xf7383c58 -0, 307881, 307881, 0, 1382400, 0x00000000 -0, 307957, 307957, 0, 1382400, 0xe66be411 -0, 309720, 309720, 0, 1382400, 0x00000000 -0, 321295, 321295, 0, 1382400, 0xd6850362 -0, 323263, 323263, 0, 1382400, 0x00000000 -0, 323356, 323356, 0, 1382400, 0x3e1ed109 -0, 324584, 324584, 0, 1382400, 0x00000000 -0, 324640, 324640, 0, 1382400, 0x39c1b7bd -0, 326403, 326403, 0, 1382400, 0x00000000 -0, 327193, 327193, 0, 1382400, 0x35b85f2e -0, 328285, 328285, 0, 1382400, 0x00000000 -0, 328360, 328360, 0, 1382400, 0x00000000 -0, 328361, 328361, 0, 1382400, 0x83f103e5 -0, 329885, 329885, 0, 1382400, 0x00000000 -0, 329946, 329946, 0, 1382400, 0xbc1ca9b3 -0, 331106, 331106, 0, 1382400, 0x00000000 -0, 331230, 331230, 0, 1382400, 0x94d4a51e -0, 332857, 332857, 0, 1382400, 0x00000000 -0, 332924, 332924, 0, 1382400, 0xf88cdfde -0, 334687, 334687, 0, 1382400, 0x00000000 -0, 342600, 342600, 0, 1382400, 0xdd51423b -0, 344431, 344431, 0, 1382400, 0x00000000 -0, 346771, 346771, 0, 1382400, 0x08259fa4 -0, 348329, 348329, 0, 1382400, 0x00000000 -0, 357640, 357640, 0, 1382400, 0x1663fa34 -0, 359767, 359767, 0, 1382400, 0x00000000 -0, 359834, 359834, 0, 1382400, 0xda2ceb55 -0, 361096, 361096, 0, 1382400, 0x00000000 +0, 132499000, 132499000, 0, 1382400, 0x00000000 +0, 132499000, 132499000, 0, 1382400, 0x8c93c2ba +0, 137459000, 137459000, 0, 1382400, 0x00000000 +0, 147355000, 147355000, 0, 1382400, 0xb02e32ca +0, 152088000, 152088000, 0, 1382400, 0x00000000 +0, 180797000, 180797000, 0, 1382400, 0x83b71116 +0, 183357000, 183357000, 0, 1382400, 0x00000000 +0, 183433000, 183433000, 0, 1382400, 0x85547fd1 +0, 185799000, 185799000, 0, 1382400, 0x00000000 +0, 185909999, 185909999, 0, 1382400, 0x00000000 +0, 185910000, 185910000, 0, 1382400, 0xb6a8f181 +0, 188606000, 188606000, 0, 1382400, 0x00000000 +0, 188663000, 188663000, 0, 1382400, 0xb64d1a2c +0, 189925000, 189925000, 0, 1382400, 0x00000000 +0, 190014000, 190014000, 0, 1382400, 0x7b37ecf3 +0, 191675000, 191675000, 0, 1382400, 0x00000000 +0, 199724000, 199724000, 0, 1382400, 0xdc025bd1 +0, 201089000, 201089000, 0, 1382400, 0x00000000 +0, 201175000, 201175000, 0, 1382400, 0x688b294d +0, 202733000, 202733000, 0, 1382400, 0x00000000 +0, 202819000, 202819000, 0, 1382400, 0xa2b33d1b +0, 204684000, 204684000, 0, 1382400, 0x00000000 +0, 204762000, 204762000, 0, 1382400, 0xb3e525e3 +0, 206730000, 206730000, 0, 1382400, 0x00000000 +0, 206806000, 206806000, 0, 1382400, 0xaa8fbdd7 +0, 208637000, 208637000, 0, 1382400, 0x00000000 +0, 208716000, 208716000, 0, 1382400, 0x7b7f26dd +0, 209978000, 209978000, 0, 1382400, 0x00000000 +0, 210051000, 210051000, 0, 1382400, 0x15e2f836 +0, 211575000, 211575000, 0, 1382400, 0x00000000 +0, 211644000, 211644000, 0, 1382400, 0x0fee9b0c +0, 214306000, 214306000, 0, 1382400, 0x00000000 +0, 214380000, 214380000, 0, 1382400, 0x89d62791 +0, 217144000, 217144000, 0, 1382400, 0x00000000 +0, 217225000, 217225000, 0, 1382400, 0xa6a9fd74 +0, 219591000, 219591000, 0, 1382400, 0x00000000 +0, 219652000, 219652000, 0, 1382400, 0x7896178d +0, 221483000, 221483000, 0, 1382400, 0x00000000 +0, 223531000, 223531000, 0, 1382400, 0x01751a52 +0, 225863000, 225863000, 0, 1382400, 0x00000000 +0, 227510000, 227510000, 0, 1382400, 0xa3959c6f +0, 230809000, 230809000, 0, 1382400, 0x00000000 +0, 230872000, 230872000, 0, 1382400, 0x3d3ea47b +0, 233033000, 233033000, 0, 1382400, 0x00000000 +0, 233124000, 233124000, 0, 1382400, 0x593f8b24 +0, 237220000, 237220000, 0, 1382400, 0x00000000 +0, 237303000, 237303000, 0, 1382400, 0x171f05ba +0, 240033000, 240033000, 0, 1382400, 0x00000000 +0, 240106000, 240106000, 0, 1382400, 0xb014cdf1 +0, 242165000, 242165000, 0, 1382400, 0x00000000 +0, 273556000, 273556000, 0, 1382400, 0xd918e667 +0, 275217000, 275217000, 0, 1382400, 0x00000000 +0, 295445000, 295445000, 0, 1382400, 0xc9406331 +0, 296776000, 296776000, 0, 1382400, 0x00000000 +0, 300049000, 300049000, 0, 1382400, 0xaf08b10d +0, 301949000, 301949000, 0, 1382400, 0x00000000 +0, 302034999, 302034999, 0, 1382400, 0x00000000 +0, 302035000, 302035000, 0, 1382400, 0x853a9d93 +0, 303559000, 303559000, 0, 1382400, 0x00000000 +0, 304203000, 304203000, 0, 1382400, 0x7491a87d +0, 305898000, 305898000, 0, 1382400, 0x00000000 +0, 305947000, 305947000, 0, 1382400, 0xf7383c58 +0, 307881000, 307881000, 0, 1382400, 0x00000000 +0, 307957000, 307957000, 0, 1382400, 0xe66be411 +0, 309720000, 309720000, 0, 1382400, 0x00000000 +0, 321295000, 321295000, 0, 1382400, 0xd6850362 +0, 323263000, 323263000, 0, 1382400, 0x00000000 +0, 323356000, 323356000, 0, 1382400, 0x3e1ed109 +0, 324584000, 324584000, 0, 1382400, 0x00000000 +0, 324640000, 324640000, 0, 1382400, 0x39c1b7bd +0, 326403000, 326403000, 0, 1382400, 0x00000000 +0, 327193000, 327193000, 0, 1382400, 0x35b85f2e +0, 328285000, 328285000, 0, 1382400, 0x00000000 +0, 328360999, 328360999, 0, 1382400, 0x00000000 +0, 328361000, 328361000, 0, 1382400, 0x83f103e5 +0, 329885000, 329885000, 0, 1382400, 0x00000000 +0, 329946000, 329946000, 0, 1382400, 0xbc1ca9b3 +0, 331106000, 331106000, 0, 1382400, 0x00000000 +0, 331230000, 331230000, 0, 1382400, 0x94d4a51e +0, 332857000, 332857000, 0, 1382400, 0x00000000 +0, 332924000, 332924000, 0, 1382400, 0xf88cdfde +0, 334687000, 334687000, 0, 1382400, 0x00000000 +0, 342600000, 342600000, 0, 1382400, 0xdd51423b +0, 344431000, 344431000, 0, 1382400, 0x00000000 +0, 346771000, 346771000, 0, 1382400, 0x08259fa4 +0, 348329000, 348329000, 0, 1382400, 0x00000000 +0, 357640000, 357640000, 0, 1382400, 0x1663fa34 +0, 359767000, 359767000, 0, 1382400, 0x00000000 +0, 359834000, 359834000, 0, 1382400, 0xda2ceb55 +0, 361096000, 361096000, 0, 1382400, 0x00000000 diff --git a/tests/ref/fate/sub2video_time_limited b/tests/ref/fate/sub2video_time_limited index c7d48d639f..07d50fc697 100644 --- a/tests/ref/fate/sub2video_time_limited +++ b/tests/ref/fate/sub2video_time_limited @@ -1,8 +1,8 @@ -#tb 0: 1/90000 +#tb 0: 1/1000000 #media_type 0: video #codec_id 0: rawvideo #dimensions 0: 1920x1080 #sar 0: 0/1 -0, 6072, 6072, 0, 8294400, 0x00000000 -0, 6072, 6072, 0, 8294400, 0xa87c518f -0, 36101, 36101, 0, 8294400, 0xa87c518f +0, 67467, 67467, 0, 8294400, 0x00000000 +0, 67467, 67467, 0, 8294400, 0xa87c518f +0, 401132, 401132, 0, 8294400, 0xa87c518f -- 2.43.0 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 02/18] fftools/ffmpeg_filter: refactor setting input timebase 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 02/18] fftools/ffmpeg_filter: refactor setting input timebase Anton Khirnov @ 2024-03-07 20:37 ` Michael Niedermayer 2024-03-08 5:34 ` Anton Khirnov 0 siblings, 1 reply; 38+ messages in thread From: Michael Niedermayer @ 2024-03-07 20:37 UTC (permalink / raw) To: FFmpeg development discussions and patches [-- Attachment #1.1: Type: text/plain, Size: 2541 bytes --] On Wed, Mar 06, 2024 at 12:03:03PM +0100, Anton Khirnov wrote: > Treat it analogously to stream parameters like format/dimensions/etc. > This is functionally different from previous code in 2 ways: > * for non-CFR video, the frame timebase (set by the decoder) is used > rather than the demuxer timebase > * for sub2video, AV_TIME_BASE_Q is used, which is hardcoded by the > subtitle decoding API > > These changes should avoid unnecessary and potentially lossy timestamp > conversions from decoder timebase into the demuxer one. > > Changes the timebases used in sub2video tests. > --- > fftools/ffmpeg_filter.c | 17 ++- > tests/ref/fate/sub2video_basic | 182 +++++++++++++------------- > tests/ref/fate/sub2video_time_limited | 8 +- > 3 files changed, 106 insertions(+), 101 deletions(-) breaks: ./ffmpeg -i \[a-s\]_full_metal_panic_fumoffu_-_01_-_the_man_from_the_south_-_a_hostage_with_no_compromises__rs2_\[1080p_bd-rip\]\[BBB48A25\].mkv -filter_complex '[0:s:1]scale=800:600' -t 15 -qscale 2 -y a.avi Press [q] to stop, [?] for help [aac @ 0x561991f96340] This stream seems to incorrectly report its last channel as LFE[3], mapping to LFE[0] [mpeg4 @ 0x561991f7e440] timebase 1/1000000 not supported by MPEG 4 standard, the maximum admitted value for the timebase denominator is 65535 [vost#0:0/mpeg4 @ 0x561991f78f80] Error while opening encoder - maybe incorrect parameters such as bit_rate, rate, width or height. [fc#0 @ 0x561991f4b9c0] Error sending frames to consumers: Invalid argument [fc#0 @ 0x561991f4b9c0] Task finished with error code: -22 (Invalid argument) [fc#0 @ 0x561991f4b9c0] Terminating thread with return code -22 (Invalid argument) [vost#0:0/mpeg4 @ 0x561991f78f80] Could not open encoder before EOF [vost#0:0/mpeg4 @ 0x561991f78f80] Task finished with error code: -22 (Invalid argument) [vost#0:0/mpeg4 @ 0x561991f78f80] Terminating thread with return code -22 (Invalid argument) [libmp3lame @ 0x561992064180] Trying to remove 1152 samples, but the queue is empty [out#0/avi @ 0x561991f98bc0] Nothing was written into output file, because at least one of its streams received no packets. frame= 0 fps=0.0 q=0.0 Lsize= 0KiB time=N/A bitrate=N/A speed=N/A Conversion failed! ill try to find a smaller testcase thx [...] -- Michael GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB Elect your leaders based on what they did after the last election, not based on what they say before an election. [-- Attachment #1.2: signature.asc --] [-- Type: application/pgp-signature, Size: 195 bytes --] [-- Attachment #2: Type: text/plain, Size: 251 bytes --] _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 02/18] fftools/ffmpeg_filter: refactor setting input timebase 2024-03-07 20:37 ` Michael Niedermayer @ 2024-03-08 5:34 ` Anton Khirnov 2024-03-10 3:36 ` Michael Niedermayer 0 siblings, 1 reply; 38+ messages in thread From: Anton Khirnov @ 2024-03-08 5:34 UTC (permalink / raw) To: FFmpeg development discussions and patches Quoting Michael Niedermayer (2024-03-07 21:37:39) > On Wed, Mar 06, 2024 at 12:03:03PM +0100, Anton Khirnov wrote: > > Treat it analogously to stream parameters like format/dimensions/etc. > > This is functionally different from previous code in 2 ways: > > * for non-CFR video, the frame timebase (set by the decoder) is used > > rather than the demuxer timebase > > * for sub2video, AV_TIME_BASE_Q is used, which is hardcoded by the > > subtitle decoding API > > > > These changes should avoid unnecessary and potentially lossy timestamp > > conversions from decoder timebase into the demuxer one. > > > > Changes the timebases used in sub2video tests. > > --- > > fftools/ffmpeg_filter.c | 17 ++- > > tests/ref/fate/sub2video_basic | 182 +++++++++++++------------- > > tests/ref/fate/sub2video_time_limited | 8 +- > > 3 files changed, 106 insertions(+), 101 deletions(-) > > breaks: > > ./ffmpeg -i \[a-s\]_full_metal_panic_fumoffu_-_01_-_the_man_from_the_south_-_a_hostage_with_no_compromises__rs2_\[1080p_bd-rip\]\[BBB48A25\].mkv -filter_complex '[0:s:1]scale=800:600' -t 15 -qscale 2 -y a.avi > Use a constant framerate. -- Anton Khirnov _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 02/18] fftools/ffmpeg_filter: refactor setting input timebase 2024-03-08 5:34 ` Anton Khirnov @ 2024-03-10 3:36 ` Michael Niedermayer 2024-03-10 6:13 ` Anton Khirnov 0 siblings, 1 reply; 38+ messages in thread From: Michael Niedermayer @ 2024-03-10 3:36 UTC (permalink / raw) To: FFmpeg development discussions and patches [-- Attachment #1.1: Type: text/plain, Size: 1735 bytes --] On Fri, Mar 08, 2024 at 06:34:36AM +0100, Anton Khirnov wrote: > Quoting Michael Niedermayer (2024-03-07 21:37:39) > > On Wed, Mar 06, 2024 at 12:03:03PM +0100, Anton Khirnov wrote: > > > Treat it analogously to stream parameters like format/dimensions/etc. > > > This is functionally different from previous code in 2 ways: > > > * for non-CFR video, the frame timebase (set by the decoder) is used > > > rather than the demuxer timebase > > > * for sub2video, AV_TIME_BASE_Q is used, which is hardcoded by the > > > subtitle decoding API > > > > > > These changes should avoid unnecessary and potentially lossy timestamp > > > conversions from decoder timebase into the demuxer one. > > > > > > Changes the timebases used in sub2video tests. > > > --- > > > fftools/ffmpeg_filter.c | 17 ++- > > > tests/ref/fate/sub2video_basic | 182 +++++++++++++------------- > > > tests/ref/fate/sub2video_time_limited | 8 +- > > > 3 files changed, 106 insertions(+), 101 deletions(-) > > > > breaks: > > > > ./ffmpeg -i \[a-s\]_full_metal_panic_fumoffu_-_01_-_the_man_from_the_south_-_a_hostage_with_no_compromises__rs2_\[1080p_bd-rip\]\[BBB48A25\].mkv -filter_complex '[0:s:1]scale=800:600' -t 15 -qscale 2 -y a.avi > > > > Use a constant framerate. why not automatically choose a supported timebase ? "[mpeg4 @ 0x55973c869f00] timebase 1/1000000 not supported by MPEG 4 standard, the maximum admitted value for the timebase denominator is 65535" ? thx [...] -- Michael GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB Good people do not need laws to tell them to act responsibly, while bad people will find a way around the laws. -- Plato [-- Attachment #1.2: signature.asc --] [-- Type: application/pgp-signature, Size: 195 bytes --] [-- Attachment #2: Type: text/plain, Size: 251 bytes --] _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 02/18] fftools/ffmpeg_filter: refactor setting input timebase 2024-03-10 3:36 ` Michael Niedermayer @ 2024-03-10 6:13 ` Anton Khirnov 2024-03-10 19:21 ` Michael Niedermayer 0 siblings, 1 reply; 38+ messages in thread From: Anton Khirnov @ 2024-03-10 6:13 UTC (permalink / raw) To: FFmpeg development discussions and patches Quoting Michael Niedermayer (2024-03-10 04:36:29) > On Fri, Mar 08, 2024 at 06:34:36AM +0100, Anton Khirnov wrote: > > Quoting Michael Niedermayer (2024-03-07 21:37:39) > > > On Wed, Mar 06, 2024 at 12:03:03PM +0100, Anton Khirnov wrote: > > > > Treat it analogously to stream parameters like format/dimensions/etc. > > > > This is functionally different from previous code in 2 ways: > > > > * for non-CFR video, the frame timebase (set by the decoder) is used > > > > rather than the demuxer timebase > > > > * for sub2video, AV_TIME_BASE_Q is used, which is hardcoded by the > > > > subtitle decoding API > > > > > > > > These changes should avoid unnecessary and potentially lossy timestamp > > > > conversions from decoder timebase into the demuxer one. > > > > > > > > Changes the timebases used in sub2video tests. > > > > --- > > > > fftools/ffmpeg_filter.c | 17 ++- > > > > tests/ref/fate/sub2video_basic | 182 +++++++++++++------------- > > > > tests/ref/fate/sub2video_time_limited | 8 +- > > > > 3 files changed, 106 insertions(+), 101 deletions(-) > > > > > > breaks: > > > > > > ./ffmpeg -i \[a-s\]_full_metal_panic_fumoffu_-_01_-_the_man_from_the_south_-_a_hostage_with_no_compromises__rs2_\[1080p_bd-rip\]\[BBB48A25\].mkv -filter_complex '[0:s:1]scale=800:600' -t 15 -qscale 2 -y a.avi > > > > > > > Use a constant framerate. > > why not automatically choose a supported timebase ? > > "[mpeg4 @ 0x55973c869f00] timebase 1/1000000 not supported by MPEG 4 standard, the maximum admitted value for the timebase denominator is 65535" Because I don't want ffmpeg CLI to have codec-specific code for a codec that's been obsolete for 15+ years. One could also potentially do it inside the encoder itself, but it is nontrivial since the computations are spread across a number of places in mpeg4videoenc.c and mpegvideo_enc.c. And again, it seems like a waste of time - there is no reason to encode mpeg4 today. -- Anton Khirnov _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 02/18] fftools/ffmpeg_filter: refactor setting input timebase 2024-03-10 6:13 ` Anton Khirnov @ 2024-03-10 19:21 ` Michael Niedermayer 2024-03-10 22:24 ` Anton Khirnov 0 siblings, 1 reply; 38+ messages in thread From: Michael Niedermayer @ 2024-03-10 19:21 UTC (permalink / raw) To: FFmpeg development discussions and patches [-- Attachment #1.1: Type: text/plain, Size: 2867 bytes --] On Sun, Mar 10, 2024 at 07:13:18AM +0100, Anton Khirnov wrote: > Quoting Michael Niedermayer (2024-03-10 04:36:29) > > On Fri, Mar 08, 2024 at 06:34:36AM +0100, Anton Khirnov wrote: > > > Quoting Michael Niedermayer (2024-03-07 21:37:39) > > > > On Wed, Mar 06, 2024 at 12:03:03PM +0100, Anton Khirnov wrote: > > > > > Treat it analogously to stream parameters like format/dimensions/etc. > > > > > This is functionally different from previous code in 2 ways: > > > > > * for non-CFR video, the frame timebase (set by the decoder) is used > > > > > rather than the demuxer timebase > > > > > * for sub2video, AV_TIME_BASE_Q is used, which is hardcoded by the > > > > > subtitle decoding API > > > > > > > > > > These changes should avoid unnecessary and potentially lossy timestamp > > > > > conversions from decoder timebase into the demuxer one. > > > > > > > > > > Changes the timebases used in sub2video tests. > > > > > --- > > > > > fftools/ffmpeg_filter.c | 17 ++- > > > > > tests/ref/fate/sub2video_basic | 182 +++++++++++++------------- > > > > > tests/ref/fate/sub2video_time_limited | 8 +- > > > > > 3 files changed, 106 insertions(+), 101 deletions(-) > > > > > > > > breaks: > > > > > > > > ./ffmpeg -i \[a-s\]_full_metal_panic_fumoffu_-_01_-_the_man_from_the_south_-_a_hostage_with_no_compromises__rs2_\[1080p_bd-rip\]\[BBB48A25\].mkv -filter_complex '[0:s:1]scale=800:600' -t 15 -qscale 2 -y a.avi > > > > > > > > > > Use a constant framerate. > > > > why not automatically choose a supported timebase ? > > > > "[mpeg4 @ 0x55973c869f00] timebase 1/1000000 not supported by MPEG 4 standard, the maximum admitted value for the timebase denominator is 65535" > > Because I don't want ffmpeg CLI to have codec-specific code for a codec > that's been obsolete for 15+ years. One could also potentially do it > inside the encoder itself, but it is nontrivial since the computations > are spread across a number of places in mpeg4videoenc.c and > mpegvideo_enc.c. And again, it seems like a waste of time - there is no > reason to encode mpeg4 today. This is not mpeg4 specific, its just a new additional case that fails ./ffmpeg -i mm-small.mpg test.dv [dvvideo @ 0x7f868800f100] Found no DV profile for 80x60 yuv420p video. Valid DV profiles are: IMHO ffmpeg should be able to select supported parameters if the user indicated thats what he wants We also do this with pixel formats and not fail and require the user to manually specify it thx [...] -- Michael GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB When the tyrant has disposed of foreign enemies by conquest or treaty, and there is nothing more to fear from them, then he is always stirring up some war or other, in order that the people may require a leader. -- Plato [-- Attachment #1.2: signature.asc --] [-- Type: application/pgp-signature, Size: 195 bytes --] [-- Attachment #2: Type: text/plain, Size: 251 bytes --] _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 02/18] fftools/ffmpeg_filter: refactor setting input timebase 2024-03-10 19:21 ` Michael Niedermayer @ 2024-03-10 22:24 ` Anton Khirnov 2024-03-10 22:29 ` James Almer 0 siblings, 1 reply; 38+ messages in thread From: Anton Khirnov @ 2024-03-10 22:24 UTC (permalink / raw) To: FFmpeg development discussions and patches Quoting Michael Niedermayer (2024-03-10 20:21:47) > On Sun, Mar 10, 2024 at 07:13:18AM +0100, Anton Khirnov wrote: > > Quoting Michael Niedermayer (2024-03-10 04:36:29) > > > On Fri, Mar 08, 2024 at 06:34:36AM +0100, Anton Khirnov wrote: > > > > Quoting Michael Niedermayer (2024-03-07 21:37:39) > > > > > On Wed, Mar 06, 2024 at 12:03:03PM +0100, Anton Khirnov wrote: > > > > > > Treat it analogously to stream parameters like format/dimensions/etc. > > > > > > This is functionally different from previous code in 2 ways: > > > > > > * for non-CFR video, the frame timebase (set by the decoder) is used > > > > > > rather than the demuxer timebase > > > > > > * for sub2video, AV_TIME_BASE_Q is used, which is hardcoded by the > > > > > > subtitle decoding API > > > > > > > > > > > > These changes should avoid unnecessary and potentially lossy timestamp > > > > > > conversions from decoder timebase into the demuxer one. > > > > > > > > > > > > Changes the timebases used in sub2video tests. > > > > > > --- > > > > > > fftools/ffmpeg_filter.c | 17 ++- > > > > > > tests/ref/fate/sub2video_basic | 182 +++++++++++++------------- > > > > > > tests/ref/fate/sub2video_time_limited | 8 +- > > > > > > 3 files changed, 106 insertions(+), 101 deletions(-) > > > > > > > > > > breaks: > > > > > > > > > > ./ffmpeg -i \[a-s\]_full_metal_panic_fumoffu_-_01_-_the_man_from_the_south_-_a_hostage_with_no_compromises__rs2_\[1080p_bd-rip\]\[BBB48A25\].mkv -filter_complex '[0:s:1]scale=800:600' -t 15 -qscale 2 -y a.avi > > > > > > > > > > > > > Use a constant framerate. > > > > > > why not automatically choose a supported timebase ? > > > > > > "[mpeg4 @ 0x55973c869f00] timebase 1/1000000 not supported by MPEG 4 standard, the maximum admitted value for the timebase denominator is 65535" > > > > Because I don't want ffmpeg CLI to have codec-specific code for a codec > > that's been obsolete for 15+ years. One could also potentially do it > > inside the encoder itself, but it is nontrivial since the computations > > are spread across a number of places in mpeg4videoenc.c and > > mpegvideo_enc.c. And again, it seems like a waste of time - there is no > > reason to encode mpeg4 today. > > This is not mpeg4 specific, its just a new additional case that fails The case you reported is mpeg4 specific. > ./ffmpeg -i mm-small.mpg test.dv > [dvvideo @ 0x7f868800f100] Found no DV profile for 80x60 yuv420p video. Valid DV profiles are: There is no mechanism for an encoder to export supported time bases. > IMHO ffmpeg should be able to select supported parameters if the user > indicated thats what he wants > > We also do this with pixel formats and not fail and require the user to manually > specify it AFAIK only a tiny number of obsolete encoders place restrictions on the timebase, so I see little point in spending effort on making them work automagically. -- Anton Khirnov _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 02/18] fftools/ffmpeg_filter: refactor setting input timebase 2024-03-10 22:24 ` Anton Khirnov @ 2024-03-10 22:29 ` James Almer 2024-03-10 22:49 ` Anton Khirnov 0 siblings, 1 reply; 38+ messages in thread From: James Almer @ 2024-03-10 22:29 UTC (permalink / raw) To: ffmpeg-devel On 3/10/2024 7:24 PM, Anton Khirnov wrote: > Quoting Michael Niedermayer (2024-03-10 20:21:47) >> On Sun, Mar 10, 2024 at 07:13:18AM +0100, Anton Khirnov wrote: >>> Quoting Michael Niedermayer (2024-03-10 04:36:29) >>>> On Fri, Mar 08, 2024 at 06:34:36AM +0100, Anton Khirnov wrote: >>>>> Quoting Michael Niedermayer (2024-03-07 21:37:39) >>>>>> On Wed, Mar 06, 2024 at 12:03:03PM +0100, Anton Khirnov wrote: >>>>>>> Treat it analogously to stream parameters like format/dimensions/etc. >>>>>>> This is functionally different from previous code in 2 ways: >>>>>>> * for non-CFR video, the frame timebase (set by the decoder) is used >>>>>>> rather than the demuxer timebase >>>>>>> * for sub2video, AV_TIME_BASE_Q is used, which is hardcoded by the >>>>>>> subtitle decoding API >>>>>>> >>>>>>> These changes should avoid unnecessary and potentially lossy timestamp >>>>>>> conversions from decoder timebase into the demuxer one. >>>>>>> >>>>>>> Changes the timebases used in sub2video tests. >>>>>>> --- >>>>>>> fftools/ffmpeg_filter.c | 17 ++- >>>>>>> tests/ref/fate/sub2video_basic | 182 +++++++++++++------------- >>>>>>> tests/ref/fate/sub2video_time_limited | 8 +- >>>>>>> 3 files changed, 106 insertions(+), 101 deletions(-) >>>>>> >>>>>> breaks: >>>>>> >>>>>> ./ffmpeg -i \[a-s\]_full_metal_panic_fumoffu_-_01_-_the_man_from_the_south_-_a_hostage_with_no_compromises__rs2_\[1080p_bd-rip\]\[BBB48A25\].mkv -filter_complex '[0:s:1]scale=800:600' -t 15 -qscale 2 -y a.avi >>>>>> >>>>> >>>>> Use a constant framerate. >>>> >>>> why not automatically choose a supported timebase ? >>>> >>>> "[mpeg4 @ 0x55973c869f00] timebase 1/1000000 not supported by MPEG 4 standard, the maximum admitted value for the timebase denominator is 65535" >>> >>> Because I don't want ffmpeg CLI to have codec-specific code for a codec >>> that's been obsolete for 15+ years. One could also potentially do it >>> inside the encoder itself, but it is nontrivial since the computations >>> are spread across a number of places in mpeg4videoenc.c and >>> mpegvideo_enc.c. And again, it seems like a waste of time - there is no >>> reason to encode mpeg4 today. >> >> This is not mpeg4 specific, its just a new additional case that fails > > The case you reported is mpeg4 specific. > >> ./ffmpeg -i mm-small.mpg test.dv >> [dvvideo @ 0x7f868800f100] Found no DV profile for 80x60 yuv420p video. Valid DV profiles are: > > There is no mechanism for an encoder to export supported time bases. Could it be added as an extension to AVProfile, or AVCodec? > >> IMHO ffmpeg should be able to select supported parameters if the user >> indicated thats what he wants >> >> We also do this with pixel formats and not fail and require the user to manually >> specify it > > AFAIK only a tiny number of obsolete encoders place restrictions on the > timebase, so I see little point in spending effort on making them work > automagically. > _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 02/18] fftools/ffmpeg_filter: refactor setting input timebase 2024-03-10 22:29 ` James Almer @ 2024-03-10 22:49 ` Anton Khirnov 2024-03-10 23:37 ` Michael Niedermayer 2024-03-11 10:12 ` Tobias Rapp 0 siblings, 2 replies; 38+ messages in thread From: Anton Khirnov @ 2024-03-10 22:49 UTC (permalink / raw) To: FFmpeg development discussions and patches Quoting James Almer (2024-03-10 23:29:27) > On 3/10/2024 7:24 PM, Anton Khirnov wrote: > > Quoting Michael Niedermayer (2024-03-10 20:21:47) > >> On Sun, Mar 10, 2024 at 07:13:18AM +0100, Anton Khirnov wrote: > >>> Quoting Michael Niedermayer (2024-03-10 04:36:29) > >>>> On Fri, Mar 08, 2024 at 06:34:36AM +0100, Anton Khirnov wrote: > >>>>> Quoting Michael Niedermayer (2024-03-07 21:37:39) > >>>>>> On Wed, Mar 06, 2024 at 12:03:03PM +0100, Anton Khirnov wrote: > >>>>>>> Treat it analogously to stream parameters like format/dimensions/etc. > >>>>>>> This is functionally different from previous code in 2 ways: > >>>>>>> * for non-CFR video, the frame timebase (set by the decoder) is used > >>>>>>> rather than the demuxer timebase > >>>>>>> * for sub2video, AV_TIME_BASE_Q is used, which is hardcoded by the > >>>>>>> subtitle decoding API > >>>>>>> > >>>>>>> These changes should avoid unnecessary and potentially lossy timestamp > >>>>>>> conversions from decoder timebase into the demuxer one. > >>>>>>> > >>>>>>> Changes the timebases used in sub2video tests. > >>>>>>> --- > >>>>>>> fftools/ffmpeg_filter.c | 17 ++- > >>>>>>> tests/ref/fate/sub2video_basic | 182 +++++++++++++------------- > >>>>>>> tests/ref/fate/sub2video_time_limited | 8 +- > >>>>>>> 3 files changed, 106 insertions(+), 101 deletions(-) > >>>>>> > >>>>>> breaks: > >>>>>> > >>>>>> ./ffmpeg -i \[a-s\]_full_metal_panic_fumoffu_-_01_-_the_man_from_the_south_-_a_hostage_with_no_compromises__rs2_\[1080p_bd-rip\]\[BBB48A25\].mkv -filter_complex '[0:s:1]scale=800:600' -t 15 -qscale 2 -y a.avi > >>>>>> > >>>>> > >>>>> Use a constant framerate. > >>>> > >>>> why not automatically choose a supported timebase ? > >>>> > >>>> "[mpeg4 @ 0x55973c869f00] timebase 1/1000000 not supported by MPEG 4 standard, the maximum admitted value for the timebase denominator is 65535" > >>> > >>> Because I don't want ffmpeg CLI to have codec-specific code for a codec > >>> that's been obsolete for 15+ years. One could also potentially do it > >>> inside the encoder itself, but it is nontrivial since the computations > >>> are spread across a number of places in mpeg4videoenc.c and > >>> mpegvideo_enc.c. And again, it seems like a waste of time - there is no > >>> reason to encode mpeg4 today. > >> > >> This is not mpeg4 specific, its just a new additional case that fails > > > > The case you reported is mpeg4 specific. > > > >> ./ffmpeg -i mm-small.mpg test.dv > >> [dvvideo @ 0x7f868800f100] Found no DV profile for 80x60 yuv420p video. Valid DV profiles are: > > > > There is no mechanism for an encoder to export supported time bases. > > Could it be added as an extension to AVProfile, or AVCodec? The two cases are actually pretty different: * mpeg4 has a constraint on the range of timebases, and actually does some perverted computations with the timestamps * DV just needs your video to be CFR, with a list of supported framerates; dvenc should probably read AVCodecContext.framerate instead of time_base But most importantly, is there an actual current use case for either of those encoders? They have both been obsolete for close to two decades. It seems silly to add new API that won't actually be useful to anyone. -- Anton Khirnov _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 02/18] fftools/ffmpeg_filter: refactor setting input timebase 2024-03-10 22:49 ` Anton Khirnov @ 2024-03-10 23:37 ` Michael Niedermayer 2024-03-11 6:03 ` Anton Khirnov 2024-03-11 10:12 ` Tobias Rapp 1 sibling, 1 reply; 38+ messages in thread From: Michael Niedermayer @ 2024-03-10 23:37 UTC (permalink / raw) To: FFmpeg development discussions and patches [-- Attachment #1.1: Type: text/plain, Size: 3852 bytes --] On Sun, Mar 10, 2024 at 11:49:12PM +0100, Anton Khirnov wrote: > Quoting James Almer (2024-03-10 23:29:27) > > On 3/10/2024 7:24 PM, Anton Khirnov wrote: > > > Quoting Michael Niedermayer (2024-03-10 20:21:47) > > >> On Sun, Mar 10, 2024 at 07:13:18AM +0100, Anton Khirnov wrote: > > >>> Quoting Michael Niedermayer (2024-03-10 04:36:29) > > >>>> On Fri, Mar 08, 2024 at 06:34:36AM +0100, Anton Khirnov wrote: > > >>>>> Quoting Michael Niedermayer (2024-03-07 21:37:39) > > >>>>>> On Wed, Mar 06, 2024 at 12:03:03PM +0100, Anton Khirnov wrote: > > >>>>>>> Treat it analogously to stream parameters like format/dimensions/etc. > > >>>>>>> This is functionally different from previous code in 2 ways: > > >>>>>>> * for non-CFR video, the frame timebase (set by the decoder) is used > > >>>>>>> rather than the demuxer timebase > > >>>>>>> * for sub2video, AV_TIME_BASE_Q is used, which is hardcoded by the > > >>>>>>> subtitle decoding API > > >>>>>>> > > >>>>>>> These changes should avoid unnecessary and potentially lossy timestamp > > >>>>>>> conversions from decoder timebase into the demuxer one. > > >>>>>>> > > >>>>>>> Changes the timebases used in sub2video tests. > > >>>>>>> --- > > >>>>>>> fftools/ffmpeg_filter.c | 17 ++- > > >>>>>>> tests/ref/fate/sub2video_basic | 182 +++++++++++++------------- > > >>>>>>> tests/ref/fate/sub2video_time_limited | 8 +- > > >>>>>>> 3 files changed, 106 insertions(+), 101 deletions(-) > > >>>>>> > > >>>>>> breaks: > > >>>>>> > > >>>>>> ./ffmpeg -i \[a-s\]_full_metal_panic_fumoffu_-_01_-_the_man_from_the_south_-_a_hostage_with_no_compromises__rs2_\[1080p_bd-rip\]\[BBB48A25\].mkv -filter_complex '[0:s:1]scale=800:600' -t 15 -qscale 2 -y a.avi > > >>>>>> > > >>>>> > > >>>>> Use a constant framerate. > > >>>> > > >>>> why not automatically choose a supported timebase ? > > >>>> > > >>>> "[mpeg4 @ 0x55973c869f00] timebase 1/1000000 not supported by MPEG 4 standard, the maximum admitted value for the timebase denominator is 65535" > > >>> > > >>> Because I don't want ffmpeg CLI to have codec-specific code for a codec > > >>> that's been obsolete for 15+ years. One could also potentially do it > > >>> inside the encoder itself, but it is nontrivial since the computations > > >>> are spread across a number of places in mpeg4videoenc.c and > > >>> mpegvideo_enc.c. And again, it seems like a waste of time - there is no > > >>> reason to encode mpeg4 today. > > >> > > >> This is not mpeg4 specific, its just a new additional case that fails > > > > > > The case you reported is mpeg4 specific. > > > > > >> ./ffmpeg -i mm-small.mpg test.dv > > >> [dvvideo @ 0x7f868800f100] Found no DV profile for 80x60 yuv420p video. Valid DV profiles are: > > > > > > There is no mechanism for an encoder to export supported time bases. > > > > Could it be added as an extension to AVProfile, or AVCodec? > > The two cases are actually pretty different: > * mpeg4 has a constraint on the range of timebases, and actually does > some perverted computations with the timestamps > * DV just needs your video to be CFR, with a list of supported > framerates; dvenc should probably read AVCodecContext.framerate > instead of time_base > > But most importantly, is there an actual current use case for either of > those encoders? They have both been obsolete for close to two decades. > It seems silly to add new API that won't actually be useful to anyone. iam not sugesting to add API specific to mpeg4, rather that maybe it can be done as part of some more generic solution thx [...] -- Michael GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB Good people do not need laws to tell them to act responsibly, while bad people will find a way around the laws. -- Plato [-- Attachment #1.2: signature.asc --] [-- Type: application/pgp-signature, Size: 195 bytes --] [-- Attachment #2: Type: text/plain, Size: 251 bytes --] _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 02/18] fftools/ffmpeg_filter: refactor setting input timebase 2024-03-10 23:37 ` Michael Niedermayer @ 2024-03-11 6:03 ` Anton Khirnov 0 siblings, 0 replies; 38+ messages in thread From: Anton Khirnov @ 2024-03-11 6:03 UTC (permalink / raw) To: FFmpeg development discussions and patches Quoting Michael Niedermayer (2024-03-11 00:37:07) > > > >>> Because I don't want ffmpeg CLI to have codec-specific code for a codec > > > >>> that's been obsolete for 15+ years. One could also potentially do it > > > >>> inside the encoder itself, but it is nontrivial since the computations > > > >>> are spread across a number of places in mpeg4videoenc.c and > > > >>> mpegvideo_enc.c. And again, it seems like a waste of time - there is no > > > >>> reason to encode mpeg4 today. > > > >> > > > >> This is not mpeg4 specific, its just a new additional case that fails > > > > > > > > The case you reported is mpeg4 specific. > > > > > > > >> ./ffmpeg -i mm-small.mpg test.dv > > > >> [dvvideo @ 0x7f868800f100] Found no DV profile for 80x60 yuv420p video. Valid DV profiles are: > > > > > > > > There is no mechanism for an encoder to export supported time bases. > > > > > > Could it be added as an extension to AVProfile, or AVCodec? > > > > The two cases are actually pretty different: > > * mpeg4 has a constraint on the range of timebases, and actually does > > some perverted computations with the timestamps > > * DV just needs your video to be CFR, with a list of supported > > framerates; dvenc should probably read AVCodecContext.framerate > > instead of time_base > > > > But most importantly, is there an actual current use case for either of > > those encoders? They have both been obsolete for close to two decades. > > It seems silly to add new API that won't actually be useful to anyone. > > iam not sugesting to add API specific to mpeg4, rather that maybe > it can be done as part of some more generic solution What kind of a solution? As I said above, I don't know of any other encoders that have similar constraints. -- Anton Khirnov _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 02/18] fftools/ffmpeg_filter: refactor setting input timebase 2024-03-10 22:49 ` Anton Khirnov 2024-03-10 23:37 ` Michael Niedermayer @ 2024-03-11 10:12 ` Tobias Rapp 2024-03-11 12:23 ` Anton Khirnov 1 sibling, 1 reply; 38+ messages in thread From: Tobias Rapp @ 2024-03-11 10:12 UTC (permalink / raw) To: ffmpeg-devel On 10/03/2024 23:49, Anton Khirnov wrote: > Quoting James Almer (2024-03-10 23:29:27) >> On 3/10/2024 7:24 PM, Anton Khirnov wrote: >>> Quoting Michael Niedermayer (2024-03-10 20:21:47) >>>> On Sun, Mar 10, 2024 at 07:13:18AM +0100, Anton Khirnov wrote: >>>>> Quoting Michael Niedermayer (2024-03-10 04:36:29) >>>>>> why not automatically choose a supported timebase ? >>>>>> "[mpeg4 @ 0x55973c869f00] timebase 1/1000000 not supported by MPEG 4 standard, the maximum admitted value for the timebase denominator is 65535" >>>>> Because I don't want ffmpeg CLI to have codec-specific code for a codec >>>>> that's been obsolete for 15+ years. One could also potentially do it >>>>> inside the encoder itself, but it is nontrivial since the computations >>>>> are spread across a number of places in mpeg4videoenc.c and >>>>> mpegvideo_enc.c. And again, it seems like a waste of time - there is no >>>>> reason to encode mpeg4 today. >>>> This is not mpeg4 specific, its just a new additional case that fails >>> The case you reported is mpeg4 specific. >>> >>>> ./ffmpeg -i mm-small.mpg test.dv >>>> [dvvideo @ 0x7f868800f100] Found no DV profile for 80x60 yuv420p video. Valid DV profiles are: >>> There is no mechanism for an encoder to export supported time bases. >> Could it be added as an extension to AVProfile, or AVCodec? > The two cases are actually pretty different: > * mpeg4 has a constraint on the range of timebases, and actually does > some perverted computations with the timestamps > * DV just needs your video to be CFR, with a list of supported > framerates; dvenc should probably read AVCodecContext.framerate > instead of time_base > > But most importantly, is there an actual current use case for either of > those encoders? They have both been obsolete for close to two decades. > It seems silly to add new API that won't actually be useful to anyone. Hardware doesn't get outdated as quickly as software. And there are people that do not switch their full environment to a new codec every decade just to be "in line". Regards, Tobias _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 02/18] fftools/ffmpeg_filter: refactor setting input timebase 2024-03-11 10:12 ` Tobias Rapp @ 2024-03-11 12:23 ` Anton Khirnov 2024-03-11 12:29 ` Martin Storsjö 0 siblings, 1 reply; 38+ messages in thread From: Anton Khirnov @ 2024-03-11 12:23 UTC (permalink / raw) To: FFmpeg development discussions and patches Quoting Tobias Rapp (2024-03-11 11:12:38) > On 10/03/2024 23:49, Anton Khirnov wrote: > > > Quoting James Almer (2024-03-10 23:29:27) > >> On 3/10/2024 7:24 PM, Anton Khirnov wrote: > >>> Quoting Michael Niedermayer (2024-03-10 20:21:47) > >>>> On Sun, Mar 10, 2024 at 07:13:18AM +0100, Anton Khirnov wrote: > >>>>> Quoting Michael Niedermayer (2024-03-10 04:36:29) > >>>>>> why not automatically choose a supported timebase ? > >>>>>> "[mpeg4 @ 0x55973c869f00] timebase 1/1000000 not supported by MPEG 4 standard, the maximum admitted value for the timebase denominator is 65535" > >>>>> Because I don't want ffmpeg CLI to have codec-specific code for a codec > >>>>> that's been obsolete for 15+ years. One could also potentially do it > >>>>> inside the encoder itself, but it is nontrivial since the computations > >>>>> are spread across a number of places in mpeg4videoenc.c and > >>>>> mpegvideo_enc.c. And again, it seems like a waste of time - there is no > >>>>> reason to encode mpeg4 today. > >>>> This is not mpeg4 specific, its just a new additional case that fails > >>> The case you reported is mpeg4 specific. > >>> > >>>> ./ffmpeg -i mm-small.mpg test.dv > >>>> [dvvideo @ 0x7f868800f100] Found no DV profile for 80x60 yuv420p video. Valid DV profiles are: > >>> There is no mechanism for an encoder to export supported time bases. > >> Could it be added as an extension to AVProfile, or AVCodec? > > The two cases are actually pretty different: > > * mpeg4 has a constraint on the range of timebases, and actually does > > some perverted computations with the timestamps > > * DV just needs your video to be CFR, with a list of supported > > framerates; dvenc should probably read AVCodecContext.framerate > > instead of time_base > > > > But most importantly, is there an actual current use case for either of > > those encoders? They have both been obsolete for close to two decades. > > It seems silly to add new API that won't actually be useful to anyone. > > Hardware doesn't get outdated as quickly as software. And there are > people that do not switch their full environment to a new codec every > decade just to be "in line". And your point is...? -- Anton Khirnov _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 02/18] fftools/ffmpeg_filter: refactor setting input timebase 2024-03-11 12:23 ` Anton Khirnov @ 2024-03-11 12:29 ` Martin Storsjö 2024-03-11 13:28 ` Anton Khirnov 0 siblings, 1 reply; 38+ messages in thread From: Martin Storsjö @ 2024-03-11 12:29 UTC (permalink / raw) To: FFmpeg development discussions and patches, FFmpeg development discussions and patches On Mon, 11 Mar 2024, Anton Khirnov wrote: > Quoting Tobias Rapp (2024-03-11 11:12:38) >> On 10/03/2024 23:49, Anton Khirnov wrote: >> >>> Quoting James Almer (2024-03-10 23:29:27) >>>> On 3/10/2024 7:24 PM, Anton Khirnov wrote: >>>>> Quoting Michael Niedermayer (2024-03-10 20:21:47) >>>>>> On Sun, Mar 10, 2024 at 07:13:18AM +0100, Anton Khirnov wrote: >>>>>>> Quoting Michael Niedermayer (2024-03-10 04:36:29) >>>>>>>> why not automatically choose a supported timebase ? >>>>>>>> "[mpeg4 @ 0x55973c869f00] timebase 1/1000000 not supported by MPEG 4 standard, the maximum admitted value for the timebase denominator is 65535" >>>>>>> Because I don't want ffmpeg CLI to have codec-specific code for a codec >>>>>>> that's been obsolete for 15+ years. One could also potentially do it >>>>>>> inside the encoder itself, but it is nontrivial since the computations >>>>>>> are spread across a number of places in mpeg4videoenc.c and >>>>>>> mpegvideo_enc.c. And again, it seems like a waste of time - there is no >>>>>>> reason to encode mpeg4 today. >>>>>> This is not mpeg4 specific, its just a new additional case that fails >>>>> The case you reported is mpeg4 specific. >>>>> >>>>>> ./ffmpeg -i mm-small.mpg test.dv >>>>>> [dvvideo @ 0x7f868800f100] Found no DV profile for 80x60 yuv420p video. Valid DV profiles are: >>>>> There is no mechanism for an encoder to export supported time bases. >>>> Could it be added as an extension to AVProfile, or AVCodec? >>> The two cases are actually pretty different: >>> * mpeg4 has a constraint on the range of timebases, and actually does >>> some perverted computations with the timestamps >>> * DV just needs your video to be CFR, with a list of supported >>> framerates; dvenc should probably read AVCodecContext.framerate >>> instead of time_base >>> >>> But most importantly, is there an actual current use case for either of >>> those encoders? They have both been obsolete for close to two decades. >>> It seems silly to add new API that won't actually be useful to anyone. >> >> Hardware doesn't get outdated as quickly as software. And there are >> people that do not switch their full environment to a new codec every >> decade just to be "in line". > > And your point is...? I think the point is, that one can't just dismiss that anybody would want to encode mpeg4 video any longer, even if it is obsolete. I also would like to keep being able to do that. That said, I haven't followed the discussion closely enough about what to do with the time bases. // Martin _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 02/18] fftools/ffmpeg_filter: refactor setting input timebase 2024-03-11 12:29 ` Martin Storsjö @ 2024-03-11 13:28 ` Anton Khirnov 2024-03-11 14:03 ` Martin Storsjö via ffmpeg-devel 2024-03-11 14:31 ` Tobias Rapp 0 siblings, 2 replies; 38+ messages in thread From: Anton Khirnov @ 2024-03-11 13:28 UTC (permalink / raw) To: FFmpeg development discussions and patches Quoting Martin Storsjö (2024-03-11 13:29:15) > On Mon, 11 Mar 2024, Anton Khirnov wrote: > > > Quoting Tobias Rapp (2024-03-11 11:12:38) > >> On 10/03/2024 23:49, Anton Khirnov wrote: > >> > >>> Quoting James Almer (2024-03-10 23:29:27) > >>>> On 3/10/2024 7:24 PM, Anton Khirnov wrote: > >>>>> Quoting Michael Niedermayer (2024-03-10 20:21:47) > >>>>>> On Sun, Mar 10, 2024 at 07:13:18AM +0100, Anton Khirnov wrote: > >>>>>>> Quoting Michael Niedermayer (2024-03-10 04:36:29) > >>>>>>>> why not automatically choose a supported timebase ? > >>>>>>>> "[mpeg4 @ 0x55973c869f00] timebase 1/1000000 not supported by MPEG 4 standard, the maximum admitted value for the timebase denominator is 65535" > >>>>>>> Because I don't want ffmpeg CLI to have codec-specific code for a codec > >>>>>>> that's been obsolete for 15+ years. One could also potentially do it > >>>>>>> inside the encoder itself, but it is nontrivial since the computations > >>>>>>> are spread across a number of places in mpeg4videoenc.c and > >>>>>>> mpegvideo_enc.c. And again, it seems like a waste of time - there is no > >>>>>>> reason to encode mpeg4 today. > >>>>>> This is not mpeg4 specific, its just a new additional case that fails > >>>>> The case you reported is mpeg4 specific. > >>>>> > >>>>>> ./ffmpeg -i mm-small.mpg test.dv > >>>>>> [dvvideo @ 0x7f868800f100] Found no DV profile for 80x60 yuv420p video. Valid DV profiles are: > >>>>> There is no mechanism for an encoder to export supported time bases. > >>>> Could it be added as an extension to AVProfile, or AVCodec? > >>> The two cases are actually pretty different: > >>> * mpeg4 has a constraint on the range of timebases, and actually does > >>> some perverted computations with the timestamps > >>> * DV just needs your video to be CFR, with a list of supported > >>> framerates; dvenc should probably read AVCodecContext.framerate > >>> instead of time_base > >>> > >>> But most importantly, is there an actual current use case for either of > >>> those encoders? They have both been obsolete for close to two decades. > >>> It seems silly to add new API that won't actually be useful to anyone. > >> > >> Hardware doesn't get outdated as quickly as software. And there are > >> people that do not switch their full environment to a new codec every > >> decade just to be "in line". > > > > And your point is...? > > I think the point is, that one can't just dismiss that anybody would want > to encode mpeg4 video any longer, even if it is obsolete. I also would > like to keep being able to do that. That capability is not going away though, and I'm not arguing that it should. > That said, I haven't followed the discussion closely enough about what to > do with the time bases. The only change is that in some rare cases the automatically selected timebase no longer fits into mpeg4 constraints, so the user has to specify either the framerate or the timebase explicitly. Specifically, the commandline used by Michael involves the extremely obscure case of converting subtitles to video (NOT harsubbing, but really 1 sub -> 1 video). Since subtitle encoding API is hardcoded to AV_TIME_BASE_Q, that timebase gets used for encoding, and the mpeg4 encoder rejects it. If it was hardsubbing (i.e. 1 video + 1 sub -> 1 video), the input video timebase should be used, which would probably work. I don't think it's that big of a deal to require users to specify the timebase or framerate explicitly in such a sitation. Inventing new APIs to cover it automagically seems like a waste of time, unless somebody has actual (not potential) uses for this. -- Anton Khirnov _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 02/18] fftools/ffmpeg_filter: refactor setting input timebase 2024-03-11 13:28 ` Anton Khirnov @ 2024-03-11 14:03 ` Martin Storsjö via ffmpeg-devel 2024-03-11 14:32 ` Anton Khirnov 2024-03-11 14:31 ` Tobias Rapp 1 sibling, 1 reply; 38+ messages in thread From: Martin Storsjö via ffmpeg-devel @ 2024-03-11 14:03 UTC (permalink / raw) To: FFmpeg development discussions and patches, FFmpeg development discussions and patches Cc: Martin Storsjö On Mon, 11 Mar 2024, Anton Khirnov wrote: >> I think the point is, that one can't just dismiss that anybody would want >> to encode mpeg4 video any longer, even if it is obsolete. I also would >> like to keep being able to do that. > > That capability is not going away though, and I'm not arguing that it > should. Ok, good. The generally dismissive arguments about mpeg4 encoding being obsolete and something that nobody should be doing, could be interpreted in such a way. >> That said, I haven't followed the discussion closely enough about what to >> do with the time bases. > > The only change is that in some rare cases the automatically selected > timebase no longer fits into mpeg4 constraints, so the user has to > specify either the framerate or the timebase explicitly. Right, I see. > Specifically, the commandline used by Michael involves the extremely > obscure case of converting subtitles to video (NOT harsubbing, but > really 1 sub -> 1 video). Since subtitle encoding API is hardcoded to > AV_TIME_BASE_Q, that timebase gets used for encoding, and the mpeg4 > encoder rejects it. If it was hardsubbing (i.e. 1 video + 1 sub -> 1 > video), the input video timebase should be used, which would probably > work. > > I don't think it's that big of a deal to require users to specify the > timebase or framerate explicitly in such a sitation. > Inventing new APIs to cover it automagically seems like a waste of time, > unless somebody has actual (not potential) uses for this. Right, I would agree with this. (If someone else would volunteer to add said API I would consider accepting it though.) Is this a usecase that currently works, but would be go away by getting rid of codec specific code in the tools, or is it a nice-to-have new extra feature that is being requested? // Martin _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 02/18] fftools/ffmpeg_filter: refactor setting input timebase 2024-03-11 14:03 ` Martin Storsjö via ffmpeg-devel @ 2024-03-11 14:32 ` Anton Khirnov 2024-03-11 14:37 ` Martin Storsjö 0 siblings, 1 reply; 38+ messages in thread From: Anton Khirnov @ 2024-03-11 14:32 UTC (permalink / raw) To: FFmpeg development discussions and patches; +Cc: Martin Storsjö Quoting Martin Storsjö via ffmpeg-devel (2024-03-11 15:03:15) > On Mon, 11 Mar 2024, Anton Khirnov wrote: > > >> I think the point is, that one can't just dismiss that anybody would want > >> to encode mpeg4 video any longer, even if it is obsolete. I also would > >> like to keep being able to do that. > > > > That capability is not going away though, and I'm not arguing that it > > should. > > Ok, good. The generally dismissive arguments about mpeg4 encoding being > obsolete and something that nobody should be doing, could be interpreted > in such a way. Well it IS obsolete. AFAIK it was never a particularly popular codec, and was only really used by the anime and ripping scenes in early 2000s, and even they dropped it very quickly once x264 appeared. While hardware "divx players" did exist, I don't think they'd still be used today in nontrivial numbers. So I don't see any reason to encode it today, besides testing and academic/historic interest. And again - that does not mean the capability should be removed, but it does mean that we shouldn't insist on tuning it for the smoothest user experience, since this time is then NOT spent doing something actually useful. > > Specifically, the commandline used by Michael involves the extremely > > obscure case of converting subtitles to video (NOT harsubbing, but > > really 1 sub -> 1 video). Since subtitle encoding API is hardcoded to > > AV_TIME_BASE_Q, that timebase gets used for encoding, and the mpeg4 > > encoder rejects it. If it was hardsubbing (i.e. 1 video + 1 sub -> 1 > > video), the input video timebase should be used, which would probably > > work. > > > > I don't think it's that big of a deal to require users to specify the > > timebase or framerate explicitly in such a sitation. > > Inventing new APIs to cover it automagically seems like a waste of time, > > unless somebody has actual (not potential) uses for this. > > Right, I would agree with this. (If someone else would volunteer to add > said API I would consider accepting it though.) > > Is this a usecase that currently works, but would be go away by getting > rid of codec specific code in the tools, or is it a nice-to-have new extra > feature that is being requested? The patch is not removing codec-specific code, rather it is changing the filtering timebase from the demuxer one to the decoder one (which is needed in this set to avoid the assumption that a filter is necessarily fed by a demuxer). The two should be the same in most cases, except * sub2video, where the subtitle decoding API fixes the timebase to AV_TIME_BASE_Q * there is a post-demuxing bsf that changes the timebase, such as setts -- Anton Khirnov _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 02/18] fftools/ffmpeg_filter: refactor setting input timebase 2024-03-11 14:32 ` Anton Khirnov @ 2024-03-11 14:37 ` Martin Storsjö 0 siblings, 0 replies; 38+ messages in thread From: Martin Storsjö @ 2024-03-11 14:37 UTC (permalink / raw) To: FFmpeg development discussions and patches On Mon, 11 Mar 2024, Anton Khirnov wrote: > Well it IS obsolete. AFAIK it was never a particularly popular codec, > and was only really used by the anime and ripping scenes in early 2000s, > and even they dropped it very quickly once x264 appeared. Within the scene of mobile HW, they commonly had HW codecs for H263 and MPEG4 (or SW codecs), with many but not all also supporting H264. So for one specific generation of mobile devices, MPEG4 was the same level of lingua franca that H264 is today. Obviously not a big use case today in nontrivial numbers of course, but it is an example of a "scene" where the codec did have a pretty broad adoption. > And again - that does not mean the capability should be removed, but it > does mean that we shouldn't insist on tuning it for the smoothest user > experience, since this time is then NOT spent doing something actually > useful. I guess that's true. // Martin _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 02/18] fftools/ffmpeg_filter: refactor setting input timebase 2024-03-11 13:28 ` Anton Khirnov 2024-03-11 14:03 ` Martin Storsjö via ffmpeg-devel @ 2024-03-11 14:31 ` Tobias Rapp 1 sibling, 0 replies; 38+ messages in thread From: Tobias Rapp @ 2024-03-11 14:31 UTC (permalink / raw) To: ffmpeg-devel On 11/03/2024 14:28, Anton Khirnov wrote: > Quoting Martin Storsjö (2024-03-11 13:29:15) >> On Mon, 11 Mar 2024, Anton Khirnov wrote: >> >>> Quoting Tobias Rapp (2024-03-11 11:12:38) >>>> On 10/03/2024 23:49, Anton Khirnov wrote: >>>> >>>>> Quoting James Almer (2024-03-10 23:29:27) >>>>>> On 3/10/2024 7:24 PM, Anton Khirnov wrote: >>>>>>> Quoting Michael Niedermayer (2024-03-10 20:21:47) >>>>>>>> On Sun, Mar 10, 2024 at 07:13:18AM +0100, Anton Khirnov wrote: >>>>>>>>> Quoting Michael Niedermayer (2024-03-10 04:36:29) >>>>>>>>>> why not automatically choose a supported timebase ? >>>>>>>>>> "[mpeg4 @ 0x55973c869f00] timebase 1/1000000 not supported by MPEG 4 standard, the maximum admitted value for the timebase denominator is 65535" >>>>>>>>> Because I don't want ffmpeg CLI to have codec-specific code for a codec >>>>>>>>> that's been obsolete for 15+ years. One could also potentially do it >>>>>>>>> inside the encoder itself, but it is nontrivial since the computations >>>>>>>>> are spread across a number of places in mpeg4videoenc.c and >>>>>>>>> mpegvideo_enc.c. And again, it seems like a waste of time - there is no >>>>>>>>> reason to encode mpeg4 today. >>>>>>>> This is not mpeg4 specific, its just a new additional case that fails >>>>>>> The case you reported is mpeg4 specific. >>>>>>> >>>>>>>> ./ffmpeg -i mm-small.mpg test.dv >>>>>>>> [dvvideo @ 0x7f868800f100] Found no DV profile for 80x60 yuv420p video. Valid DV profiles are: >>>>>>> There is no mechanism for an encoder to export supported time bases. >>>>>> Could it be added as an extension to AVProfile, or AVCodec? >>>>> The two cases are actually pretty different: >>>>> * mpeg4 has a constraint on the range of timebases, and actually does >>>>> some perverted computations with the timestamps >>>>> * DV just needs your video to be CFR, with a list of supported >>>>> framerates; dvenc should probably read AVCodecContext.framerate >>>>> instead of time_base >>>>> >>>>> But most importantly, is there an actual current use case for either of >>>>> those encoders? They have both been obsolete for close to two decades. >>>>> It seems silly to add new API that won't actually be useful to anyone. >>>> Hardware doesn't get outdated as quickly as software. And there are >>>> people that do not switch their full environment to a new codec every >>>> decade just to be "in line". >>> And your point is...? >> I think the point is, that one can't just dismiss that anybody would want >> to encode mpeg4 video any longer, even if it is obsolete. I also would >> like to keep being able to do that. > That capability is not going away though, and I'm not arguing that it > should. > >> That said, I haven't followed the discussion closely enough about what to >> do with the time bases. > The only change is that in some rare cases the automatically selected > timebase no longer fits into mpeg4 constraints, so the user has to > specify either the framerate or the timebase explicitly. > > [...] If this is just about having to add some extra parametersin rare use-cases then its fine. I got the impression that encoding into MPEG4 or DV was considered deprecated (or unimportant of additional consideration) in general. Regards, Tobias _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 38+ messages in thread
* [FFmpeg-devel] [PATCH 03/18] fftools/ffmpeg_filter: drop unused InputFilterPriv.ist 2024-03-06 11:03 [FFmpeg-devel] [PATCH 01/18] fftools/cmdutils: fix printing group name in split_commandline() Anton Khirnov 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 02/18] fftools/ffmpeg_filter: refactor setting input timebase Anton Khirnov @ 2024-03-06 11:03 ` Anton Khirnov 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 04/18] fftools/ffmpeg_dec: move scheduler registration from dec_open() to dec_alloc() Anton Khirnov ` (15 subsequent siblings) 17 siblings, 0 replies; 38+ messages in thread From: Anton Khirnov @ 2024-03-06 11:03 UTC (permalink / raw) To: ffmpeg-devel Outside of ifilter_bind_ist(), there are no longer any assumptions about about filter inputs being fed by an InputStream. --- fftools/ffmpeg_filter.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/fftools/ffmpeg_filter.c b/fftools/ffmpeg_filter.c index 9d9762a599..37705297fb 100644 --- a/fftools/ffmpeg_filter.c +++ b/fftools/ffmpeg_filter.c @@ -109,8 +109,6 @@ typedef struct InputFilterPriv { AVFilterContext *filter; - InputStream *ist; - // used to hold submitted input AVFrame *frame; @@ -125,6 +123,7 @@ typedef struct InputFilterPriv { enum AVMediaType type_src; int eof; + int bound; // parameters configured for this input int format; @@ -664,7 +663,8 @@ static int ifilter_bind_ist(InputFilter *ifilter, InputStream *ist) FilterGraphPriv *fgp = fgp_from_fg(ifilter->graph); int ret, dec_idx; - av_assert0(!ifp->ist); + av_assert0(!ifp->bound); + ifp->bound = 1; if (ifp->type != ist->par->codec_type && !(ifp->type == AVMEDIA_TYPE_VIDEO && ist->par->codec_type == AVMEDIA_TYPE_SUBTITLE)) { @@ -673,7 +673,6 @@ static int ifilter_bind_ist(InputFilter *ifilter, InputStream *ist) return AVERROR(EINVAL); } - ifp->ist = ist; ifp->type_src = ist->st->codecpar->codec_type; dec_idx = ist_filter_add(ist, ifilter, filtergraph_is_simple(ifilter->graph), -- 2.43.0 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 38+ messages in thread
* [FFmpeg-devel] [PATCH 04/18] fftools/ffmpeg_dec: move scheduler registration from dec_open() to dec_alloc() 2024-03-06 11:03 [FFmpeg-devel] [PATCH 01/18] fftools/cmdutils: fix printing group name in split_commandline() Anton Khirnov 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 02/18] fftools/ffmpeg_filter: refactor setting input timebase Anton Khirnov 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 03/18] fftools/ffmpeg_filter: drop unused InputFilterPriv.ist Anton Khirnov @ 2024-03-06 11:03 ` Anton Khirnov 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 05/18] fftools/ffmpeg_dec: factor opening the decoder out of dec_open() Anton Khirnov ` (14 subsequent siblings) 17 siblings, 0 replies; 38+ messages in thread From: Anton Khirnov @ 2024-03-06 11:03 UTC (permalink / raw) To: ffmpeg-devel Will be useful in following commits where we will want to create a decoder before having enough information to open it. --- fftools/ffmpeg_dec.c | 51 +++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/fftools/ffmpeg_dec.c b/fftools/ffmpeg_dec.c index 8c92b27cc1..98c4a0c83c 100644 --- a/fftools/ffmpeg_dec.c +++ b/fftools/ffmpeg_dec.c @@ -110,9 +110,26 @@ void dec_free(Decoder **pdec) av_freep(pdec); } -static int dec_alloc(DecoderPriv **pdec) +static const char *dec_item_name(void *obj) +{ + const DecoderPriv *dp = obj; + + return dp->log_name; +} + +static const AVClass dec_class = { + .class_name = "Decoder", + .version = LIBAVUTIL_VERSION_INT, + .parent_log_context_offset = offsetof(DecoderPriv, log_parent), + .item_name = dec_item_name, +}; + +static void *decoder_thread(void *arg); + +static int dec_alloc(DecoderPriv **pdec, Scheduler *sch, int send_end_ts) { DecoderPriv *dp; + int ret = 0; *pdec = NULL; @@ -128,17 +145,24 @@ static int dec_alloc(DecoderPriv **pdec) if (!dp->pkt) goto fail; + dp->dec.class = &dec_class; dp->last_filter_in_rescale_delta = AV_NOPTS_VALUE; dp->last_frame_pts = AV_NOPTS_VALUE; dp->last_frame_tb = (AVRational){ 1, 1 }; dp->hwaccel_pix_fmt = AV_PIX_FMT_NONE; + ret = sch_add_dec(sch, decoder_thread, dp, send_end_ts); + if (ret < 0) + goto fail; + dp->sch = sch; + dp->sch_idx = ret; + *pdec = dp; return 0; fail: dec_free((Decoder**)&dp); - return AVERROR(ENOMEM); + return ret >= 0 ? AVERROR(ENOMEM) : ret; } static AVRational audio_samplerate_update(DecoderPriv *dp, @@ -1042,20 +1066,6 @@ static int hw_device_setup_for_decode(DecoderPriv *dp, return 0; } -static const char *dec_item_name(void *obj) -{ - const DecoderPriv *dp = obj; - - return dp->log_name; -} - -static const AVClass dec_class = { - .class_name = "Decoder", - .version = LIBAVUTIL_VERSION_INT, - .parent_log_context_offset = offsetof(DecoderPriv, log_parent), - .item_name = dec_item_name, -}; - int dec_open(Decoder **pdec, Scheduler *sch, AVDictionary **dec_opts, const DecoderOpts *o) { @@ -1065,18 +1075,11 @@ int dec_open(Decoder **pdec, Scheduler *sch, *pdec = NULL; - ret = dec_alloc(&dp); + ret = dec_alloc(&dp, sch, !!(o->flags & DECODER_FLAG_SEND_END_TS)); if (ret < 0) return ret; - ret = sch_add_dec(sch, decoder_thread, dp, o->flags & DECODER_FLAG_SEND_END_TS); - if (ret < 0) - return ret; - dp->sch = sch; - dp->sch_idx = ret; - dp->flags = o->flags; - dp->dec.class = &dec_class; dp->log_parent = o->log_parent; dp->framerate_in = o->framerate; -- 2.43.0 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 38+ messages in thread
* [FFmpeg-devel] [PATCH 05/18] fftools/ffmpeg_dec: factor opening the decoder out of dec_open() 2024-03-06 11:03 [FFmpeg-devel] [PATCH 01/18] fftools/cmdutils: fix printing group name in split_commandline() Anton Khirnov ` (2 preceding siblings ...) 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 04/18] fftools/ffmpeg_dec: move scheduler registration from dec_open() to dec_alloc() Anton Khirnov @ 2024-03-06 11:03 ` Anton Khirnov 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 06/18] fftools/ffmpeg_opt: merge init_complex_filters() and check_filter_outputs() Anton Khirnov ` (13 subsequent siblings) 17 siblings, 0 replies; 38+ messages in thread From: Anton Khirnov @ 2024-03-06 11:03 UTC (permalink / raw) To: ffmpeg-devel Rename dec_open to dec_init(), as it is more descriptive of its new purpose. Will be useful in following commits, which will add a new path for opening decoders. --- fftools/ffmpeg.h | 2 +- fftools/ffmpeg_dec.c | 61 ++++++++++++++++++++++-------------------- fftools/ffmpeg_demux.c | 2 +- 3 files changed, 34 insertions(+), 31 deletions(-) diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h index 1c5ebcb43f..6b049e329b 100644 --- a/fftools/ffmpeg.h +++ b/fftools/ffmpeg.h @@ -740,7 +740,7 @@ AVBufferRef *hw_device_for_filter(void); * @retval ">=0" non-negative scheduler index on success * @retval "<0" an error code on failure */ -int dec_open(Decoder **pdec, Scheduler *sch, +int dec_init(Decoder **pdec, Scheduler *sch, AVDictionary **dec_opts, const DecoderOpts *o); void dec_free(Decoder **pdec); diff --git a/fftools/ffmpeg_dec.c b/fftools/ffmpeg_dec.c index 98c4a0c83c..af57b2bb9d 100644 --- a/fftools/ffmpeg_dec.c +++ b/fftools/ffmpeg_dec.c @@ -1066,19 +1066,11 @@ static int hw_device_setup_for_decode(DecoderPriv *dp, return 0; } -int dec_open(Decoder **pdec, Scheduler *sch, - AVDictionary **dec_opts, const DecoderOpts *o) +static int dec_open(DecoderPriv *dp, AVDictionary **dec_opts, const DecoderOpts *o) { - DecoderPriv *dp; const AVCodec *codec = o->codec; int ret; - *pdec = NULL; - - ret = dec_alloc(&dp, sch, !!(o->flags & DECODER_FLAG_SEND_END_TS)); - if (ret < 0) - return ret; - dp->flags = o->flags; dp->log_parent = o->log_parent; @@ -1091,39 +1083,31 @@ int dec_open(Decoder **pdec, Scheduler *sch, snprintf(dp->log_name, sizeof(dp->log_name), "dec:%s", codec->name); dp->parent_name = av_strdup(o->name ? o->name : ""); - if (!dp->parent_name) { - ret = AVERROR(ENOMEM); - goto fail; - } + if (!dp->parent_name) + return AVERROR(ENOMEM); if (codec->type == AVMEDIA_TYPE_SUBTITLE && (dp->flags & DECODER_FLAG_FIX_SUB_DURATION)) { for (int i = 0; i < FF_ARRAY_ELEMS(dp->sub_prev); i++) { dp->sub_prev[i] = av_frame_alloc(); - if (!dp->sub_prev[i]) { - ret = AVERROR(ENOMEM); - goto fail; - } + if (!dp->sub_prev[i]) + return AVERROR(ENOMEM); } dp->sub_heartbeat = av_frame_alloc(); - if (!dp->sub_heartbeat) { - ret = AVERROR(ENOMEM); - goto fail; - } + if (!dp->sub_heartbeat) + return AVERROR(ENOMEM); } dp->sar_override = o->par->sample_aspect_ratio; dp->dec_ctx = avcodec_alloc_context3(codec); - if (!dp->dec_ctx) { - ret = AVERROR(ENOMEM); - goto fail; - } + if (!dp->dec_ctx) + return AVERROR(ENOMEM); ret = avcodec_parameters_to_context(dp->dec_ctx, o->par); if (ret < 0) { av_log(dp, AV_LOG_ERROR, "Error initializing the decoder context.\n"); - goto fail; + return ret; } dp->dec_ctx->opaque = dp; @@ -1140,22 +1124,41 @@ int dec_open(Decoder **pdec, Scheduler *sch, av_log(dp, AV_LOG_ERROR, "Hardware device setup failed for decoder: %s\n", av_err2str(ret)); - goto fail; + return ret; } if ((ret = avcodec_open2(dp->dec_ctx, codec, dec_opts)) < 0) { av_log(dp, AV_LOG_ERROR, "Error while opening decoder: %s\n", av_err2str(ret)); - goto fail; + return ret; } ret = check_avoptions(*dec_opts); if (ret < 0) - goto fail; + return ret; dp->dec.subtitle_header = dp->dec_ctx->subtitle_header; dp->dec.subtitle_header_size = dp->dec_ctx->subtitle_header_size; + return 0; +} + +int dec_init(Decoder **pdec, Scheduler *sch, + AVDictionary **dec_opts, const DecoderOpts *o) +{ + DecoderPriv *dp; + int ret; + + *pdec = NULL; + + ret = dec_alloc(&dp, sch, !!(o->flags & DECODER_FLAG_SEND_END_TS)); + if (ret < 0) + return ret; + + ret = dec_open(dp, dec_opts, o); + if (ret < 0) + goto fail; + *pdec = &dp->dec; return dp->sch_idx; diff --git a/fftools/ffmpeg_demux.c b/fftools/ffmpeg_demux.c index 29f4a26224..ae0f635d8c 100644 --- a/fftools/ffmpeg_demux.c +++ b/fftools/ffmpeg_demux.c @@ -935,7 +935,7 @@ static int ist_use(InputStream *ist, int decoding_needed) ds->dec_opts.log_parent = ist; - ret = dec_open(&ist->decoder, d->sch, + ret = dec_init(&ist->decoder, d->sch, &ds->decoder_opts, &ds->dec_opts); if (ret < 0) return ret; -- 2.43.0 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 38+ messages in thread
* [FFmpeg-devel] [PATCH 06/18] fftools/ffmpeg_opt: merge init_complex_filters() and check_filter_outputs() 2024-03-06 11:03 [FFmpeg-devel] [PATCH 01/18] fftools/cmdutils: fix printing group name in split_commandline() Anton Khirnov ` (3 preceding siblings ...) 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 05/18] fftools/ffmpeg_dec: factor opening the decoder out of dec_open() Anton Khirnov @ 2024-03-06 11:03 ` Anton Khirnov 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 07/18] fftools/ffmpeg_filter: move filtergraph input type check to a better place Anton Khirnov ` (12 subsequent siblings) 17 siblings, 0 replies; 38+ messages in thread From: Anton Khirnov @ 2024-03-06 11:03 UTC (permalink / raw) To: ffmpeg-devel The first of these binds inputs of complex filtergraphs to demuxer streams (with a misleading comment claiming it *creates* complex filtergraphs). The second ensures that all filtergraph outputs are connected to an encoder. Merge them into a single function, which simplifies the ffmpeg_filter API, is shorter, and will also be useful in following commits. Also, rename misleadingly-named init_input_filter() to fg_complex_bind_input(). --- fftools/ffmpeg.h | 3 +-- fftools/ffmpeg_filter.c | 38 ++++++++++++++++++-------------------- fftools/ffmpeg_opt.c | 32 +++++++++----------------------- 3 files changed, 28 insertions(+), 45 deletions(-) diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h index 6b049e329b..d3e03543ac 100644 --- a/fftools/ffmpeg.h +++ b/fftools/ffmpeg.h @@ -671,12 +671,11 @@ int find_codec(void *logctx, const char *name, enum AVMediaType type, int encoder, const AVCodec **codec); int parse_and_set_vsync(const char *arg, int *vsync_var, int file_idx, int st_idx, int is_global); -int check_filter_outputs(void); int filtergraph_is_simple(const FilterGraph *fg); int init_simple_filtergraph(InputStream *ist, OutputStream *ost, char *graph_desc, Scheduler *sch, unsigned sch_idx_enc); -int init_complex_filtergraph(FilterGraph *fg); +int fg_finalise_bindings(FilterGraph *fg); /** * Get our axiliary frame data attached to the frame, allocating it diff --git a/fftools/ffmpeg_filter.c b/fftools/ffmpeg_filter.c index 37705297fb..7425e3d2ed 100644 --- a/fftools/ffmpeg_filter.c +++ b/fftools/ffmpeg_filter.c @@ -1096,7 +1096,7 @@ int init_simple_filtergraph(InputStream *ist, OutputStream *ost, return 0; } -static int init_input_filter(FilterGraph *fg, InputFilter *ifilter) +static int fg_complex_bind_input(FilterGraph *fg, InputFilter *ifilter) { FilterGraphPriv *fgp = fgp_from_fg(fg); InputFilterPriv *ifp = ifp_from_ifilter(ifilter); @@ -1162,14 +1162,29 @@ static int init_input_filter(FilterGraph *fg, InputFilter *ifilter) return 0; } -int init_complex_filtergraph(FilterGraph *fg) +int fg_finalise_bindings(FilterGraph *fg) { // bind filtergraph inputs to input streams for (int i = 0; i < fg->nb_inputs; i++) { - int ret = init_input_filter(fg, fg->inputs[i]); + InputFilterPriv *ifp = ifp_from_ifilter(fg->inputs[i]); + int ret; + + if (ifp->bound) + continue; + + ret = fg_complex_bind_input(fg, &ifp->ifilter); if (ret < 0) return ret; } + + for (int i = 0; i < fg->nb_outputs; i++) { + OutputFilter *output = fg->outputs[i]; + if (!output->ost) { + av_log(filtergraphs[i], AV_LOG_FATAL, + "Filter %s has an unconnected output\n", output->name); + return AVERROR(EINVAL); + } + } return 0; } @@ -1436,23 +1451,6 @@ static int configure_output_filter(FilterGraph *fg, AVFilterGraph *graph, } } -int check_filter_outputs(void) -{ - for (int i = 0; i < nb_filtergraphs; i++) { - int n; - for (n = 0; n < filtergraphs[i]->nb_outputs; n++) { - OutputFilter *output = filtergraphs[i]->outputs[n]; - if (!output->ost) { - av_log(filtergraphs[i], AV_LOG_FATAL, - "Filter %s has an unconnected output\n", output->name); - return AVERROR(EINVAL); - } - } - } - - return 0; -} - static void sub2video_prepare(InputFilterPriv *ifp) { ifp->sub2video.last_pts = INT64_MIN; diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c index a9a785a0ac..b550382b4c 100644 --- a/fftools/ffmpeg_opt.c +++ b/fftools/ffmpeg_opt.c @@ -674,18 +674,6 @@ static int opt_streamid(void *optctx, const char *opt, const char *arg) return av_dict_set(&o->streamid, idx_str, p, 0); } -static int init_complex_filters(void) -{ - int i, ret = 0; - - for (i = 0; i < nb_filtergraphs; i++) { - ret = init_complex_filtergraph(filtergraphs[i]); - if (ret < 0) - return ret; - } - return 0; -} - static int opt_target(void *optctx, const char *opt, const char *arg) { OptionsContext *o = optctx; @@ -1264,13 +1252,6 @@ int ffmpeg_parse_options(int argc, char **argv, Scheduler *sch) goto fail; } - /* create the complex filtergraphs */ - ret = init_complex_filters(); - if (ret < 0) { - errmsg = "initializing complex filters"; - goto fail; - } - /* open output files */ ret = open_files(&octx.groups[GROUP_OUTFILE], "output", sch, of_open); if (ret < 0) { @@ -1278,16 +1259,21 @@ int ffmpeg_parse_options(int argc, char **argv, Scheduler *sch) goto fail; } + // bind unbound filtegraph inputs/outputs and check consistency + for (int i = 0; i < nb_filtergraphs; i++) { + ret = fg_finalise_bindings(filtergraphs[i]); + if (ret < 0) { + errmsg = "binding filtergraph inputs/outputs"; + goto fail; + } + } + correct_input_start_times(); ret = apply_sync_offsets(); if (ret < 0) goto fail; - ret = check_filter_outputs(); - if (ret < 0) - goto fail; - fail: uninit_parse_context(&octx); if (ret < 0 && ret != AVERROR_EXIT) { -- 2.43.0 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 38+ messages in thread
* [FFmpeg-devel] [PATCH 07/18] fftools/ffmpeg_filter: move filtergraph input type check to a better place 2024-03-06 11:03 [FFmpeg-devel] [PATCH 01/18] fftools/cmdutils: fix printing group name in split_commandline() Anton Khirnov ` (4 preceding siblings ...) 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 06/18] fftools/ffmpeg_opt: merge init_complex_filters() and check_filter_outputs() Anton Khirnov @ 2024-03-06 11:03 ` Anton Khirnov 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 08/18] fftools/ffmpeg_filter: add logging for binding inputs to demuxer streams Anton Khirnov ` (11 subsequent siblings) 17 siblings, 0 replies; 38+ messages in thread From: Anton Khirnov @ 2024-03-06 11:03 UTC (permalink / raw) To: ffmpeg-devel Perform it right after we figure out what the type is. --- fftools/ffmpeg_filter.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/fftools/ffmpeg_filter.c b/fftools/ffmpeg_filter.c index 7425e3d2ed..eb8d0aee78 100644 --- a/fftools/ffmpeg_filter.c +++ b/fftools/ffmpeg_filter.c @@ -1005,6 +1005,14 @@ int fg_create(FilterGraph **pfg, char *graph_desc, Scheduler *sch) ifp->type = avfilter_pad_get_type(cur->filter_ctx->input_pads, cur->pad_idx); + + if (ifp->type != AVMEDIA_TYPE_VIDEO && ifp->type != AVMEDIA_TYPE_AUDIO) { + av_log(fg, AV_LOG_FATAL, "Only video and audio filters supported " + "currently.\n"); + ret = AVERROR(ENOSYS); + goto fail; + } + ifilter->name = describe_filter_link(fg, cur, 1); if (!ifilter->name) { ret = AVERROR(ENOMEM); @@ -1104,13 +1112,6 @@ static int fg_complex_bind_input(FilterGraph *fg, InputFilter *ifilter) enum AVMediaType type = ifp->type; int i, ret; - // TODO: support other filter types - if (type != AVMEDIA_TYPE_VIDEO && type != AVMEDIA_TYPE_AUDIO) { - av_log(fg, AV_LOG_FATAL, "Only video and audio filters supported " - "currently.\n"); - return AVERROR(ENOSYS); - } - if (ifp->linklabel) { AVFormatContext *s; AVStream *st = NULL; -- 2.43.0 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 38+ messages in thread
* [FFmpeg-devel] [PATCH 08/18] fftools/ffmpeg_filter: add logging for binding inputs to demuxer streams 2024-03-06 11:03 [FFmpeg-devel] [PATCH 01/18] fftools/cmdutils: fix printing group name in split_commandline() Anton Khirnov ` (5 preceding siblings ...) 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 07/18] fftools/ffmpeg_filter: move filtergraph input type check to a better place Anton Khirnov @ 2024-03-06 11:03 ` Anton Khirnov 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 09/18] fftools/ffmpeg: simplify propagating fallback parameters from decoders to filters Anton Khirnov ` (10 subsequent siblings) 17 siblings, 0 replies; 38+ messages in thread From: Anton Khirnov @ 2024-03-06 11:03 UTC (permalink / raw) To: ffmpeg-devel --- fftools/ffmpeg_filter.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fftools/ffmpeg_filter.c b/fftools/ffmpeg_filter.c index eb8d0aee78..7aa9305c88 100644 --- a/fftools/ffmpeg_filter.c +++ b/fftools/ffmpeg_filter.c @@ -1142,6 +1142,10 @@ static int fg_complex_bind_input(FilterGraph *fg, InputFilter *ifilter) return AVERROR(EINVAL); } ist = input_files[file_idx]->streams[st->index]; + + av_log(fg, AV_LOG_VERBOSE, + "Binding input with label '%s' to input stream %d:%d\n", + ifp->linklabel, ist->file->index, ist->index); } else { ist = ist_find_unused(type); if (!ist) { @@ -1149,6 +1153,10 @@ static int fg_complex_bind_input(FilterGraph *fg, InputFilter *ifilter) "unlabeled input pad %s\n", ifilter->name); return AVERROR(EINVAL); } + + av_log(fg, AV_LOG_VERBOSE, + "Binding unlabeled input %d to input stream %d:%d\n", + ifp->index, ist->file->index, ist->index); } av_assert0(ist); -- 2.43.0 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 38+ messages in thread
* [FFmpeg-devel] [PATCH 09/18] fftools/ffmpeg: simplify propagating fallback parameters from decoders to filters 2024-03-06 11:03 [FFmpeg-devel] [PATCH 01/18] fftools/cmdutils: fix printing group name in split_commandline() Anton Khirnov ` (6 preceding siblings ...) 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 08/18] fftools/ffmpeg_filter: add logging for binding inputs to demuxer streams Anton Khirnov @ 2024-03-06 11:03 ` Anton Khirnov 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 10/18] fftools/ffmpeg: remove unncessary casts for *_thread() return values Anton Khirnov ` (9 subsequent siblings) 17 siblings, 0 replies; 38+ messages in thread From: Anton Khirnov @ 2024-03-06 11:03 UTC (permalink / raw) To: ffmpeg-devel Current callstack looks like this: * ifilter_bind_ist() (filter) calls ist_filter_add() (demuxer); * ist_filter_add() opens the decoder, and then calls dec_add_filter() (decoder); * dec_add_filter() calls ifilter_parameters_from_dec() (i.e. back into the filtering code) in order to give post-avcodec_open2() parameters to the filter. This is unnecessarily complicated. Pass the parameters as follows instead: * dec_init() (which opens the decoder) returns post-avcodec_open2() parameters to its caller (i.e. the demuxer) in a parameter-only AVFrame * the demuxer passes these parameters to the filter in InputFilterOptions, together with other filter options --- fftools/ffmpeg.h | 9 ++++--- fftools/ffmpeg_dec.c | 36 ++++++++++++++++++-------- fftools/ffmpeg_demux.c | 25 ++++++++++++++---- fftools/ffmpeg_filter.c | 57 ++++++++++++----------------------------- 4 files changed, 67 insertions(+), 60 deletions(-) diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h index d3e03543ac..1a60f035b0 100644 --- a/fftools/ffmpeg.h +++ b/fftools/ffmpeg.h @@ -259,6 +259,8 @@ typedef struct InputFilterOptions { // a combination of IFILTER_FLAG_* unsigned flags; + + AVFrame *fallback; } InputFilterOptions; typedef struct InputFilter { @@ -735,16 +737,17 @@ AVBufferRef *hw_device_for_filter(void); /** * @param dec_opts Dictionary filled with decoder options. Its ownership * is transferred to the decoder. + * @param param_out If non-NULL, media properties after opening the decoder + * are written here. * * @retval ">=0" non-negative scheduler index on success * @retval "<0" an error code on failure */ int dec_init(Decoder **pdec, Scheduler *sch, - AVDictionary **dec_opts, const DecoderOpts *o); + AVDictionary **dec_opts, const DecoderOpts *o, + AVFrame *param_out); void dec_free(Decoder **pdec); -int dec_add_filter(Decoder *dec, InputFilter *ifilter); - int enc_alloc(Encoder **penc, const AVCodec *codec, Scheduler *sch, unsigned sch_idx); void enc_free(Encoder **penc); diff --git a/fftools/ffmpeg_dec.c b/fftools/ffmpeg_dec.c index af57b2bb9d..7005ada527 100644 --- a/fftools/ffmpeg_dec.c +++ b/fftools/ffmpeg_dec.c @@ -1066,7 +1066,8 @@ static int hw_device_setup_for_decode(DecoderPriv *dp, return 0; } -static int dec_open(DecoderPriv *dp, AVDictionary **dec_opts, const DecoderOpts *o) +static int dec_open(DecoderPriv *dp, AVDictionary **dec_opts, + const DecoderOpts *o, AVFrame *param_out) { const AVCodec *codec = o->codec; int ret; @@ -1140,11 +1141,32 @@ static int dec_open(DecoderPriv *dp, AVDictionary **dec_opts, const DecoderOpts dp->dec.subtitle_header = dp->dec_ctx->subtitle_header; dp->dec.subtitle_header_size = dp->dec_ctx->subtitle_header_size; + if (param_out) { + if (dp->dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) { + param_out->format = dp->dec_ctx->sample_fmt; + param_out->sample_rate = dp->dec_ctx->sample_rate; + + ret = av_channel_layout_copy(¶m_out->ch_layout, &dp->dec_ctx->ch_layout); + if (ret < 0) + return ret; + } else if (dp->dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO) { + param_out->format = dp->dec_ctx->pix_fmt; + param_out->width = dp->dec_ctx->width; + param_out->height = dp->dec_ctx->height; + param_out->sample_aspect_ratio = dp->dec_ctx->sample_aspect_ratio; + param_out->colorspace = dp->dec_ctx->colorspace; + param_out->color_range = dp->dec_ctx->color_range; + } + + param_out->time_base = dp->dec_ctx->pkt_timebase; + } + return 0; } int dec_init(Decoder **pdec, Scheduler *sch, - AVDictionary **dec_opts, const DecoderOpts *o) + AVDictionary **dec_opts, const DecoderOpts *o, + AVFrame *param_out) { DecoderPriv *dp; int ret; @@ -1155,7 +1177,7 @@ int dec_init(Decoder **pdec, Scheduler *sch, if (ret < 0) return ret; - ret = dec_open(dp, dec_opts, o); + ret = dec_open(dp, dec_opts, o, param_out); if (ret < 0) goto fail; @@ -1166,11 +1188,3 @@ fail: dec_free((Decoder**)&dp); return ret; } - -int dec_add_filter(Decoder *dec, InputFilter *ifilter) -{ - DecoderPriv *dp = dp_from_dec(dec); - - // initialize fallback parameters for filtering - return ifilter_parameters_from_dec(ifilter, dp->dec_ctx); -} diff --git a/fftools/ffmpeg_demux.c b/fftools/ffmpeg_demux.c index ae0f635d8c..032a96567f 100644 --- a/fftools/ffmpeg_demux.c +++ b/fftools/ffmpeg_demux.c @@ -81,6 +81,8 @@ typedef struct DemuxStream { AVDictionary *decoder_opts; DecoderOpts dec_opts; char dec_name[16]; + // decoded media properties, as estimated by opening the decoder + AVFrame *decoded_params; AVBSFContext *bsf; @@ -839,6 +841,8 @@ static void ist_free(InputStream **pist) avcodec_parameters_free(&ist->par); + av_frame_free(&ds->decoded_params); + av_bsf_free(&ds->bsf); av_freep(pist); @@ -935,8 +939,12 @@ static int ist_use(InputStream *ist, int decoding_needed) ds->dec_opts.log_parent = ist; + ds->decoded_params = av_frame_alloc(); + if (!ds->decoded_params) + return AVERROR(ENOMEM); + ret = dec_init(&ist->decoder, d->sch, - &ds->decoder_opts, &ds->dec_opts); + &ds->decoder_opts, &ds->dec_opts, ds->decoded_params); if (ret < 0) return ret; ds->sch_idx_dec = ret; @@ -988,10 +996,6 @@ int ist_filter_add(InputStream *ist, InputFilter *ifilter, int is_simple, ist->filters[ist->nb_filters - 1] = ifilter; - ret = dec_add_filter(ist->decoder, ifilter); - if (ret < 0) - return ret; - if (ist->par->codec_type == AVMEDIA_TYPE_VIDEO) { if (ist->framerate.num > 0 && ist->framerate.den > 0) { opts->framerate = ist->framerate; @@ -1027,6 +1031,17 @@ int ist_filter_add(InputStream *ist, InputFilter *ifilter, int is_simple, ds->have_sub2video = 1; } + ret = av_frame_copy_props(opts->fallback, ds->decoded_params); + if (ret < 0) + return ret; + opts->fallback->format = ds->decoded_params->format; + opts->fallback->width = ds->decoded_params->width; + opts->fallback->height = ds->decoded_params->height; + + ret = av_channel_layout_copy(&opts->fallback->ch_layout, &ds->decoded_params->ch_layout); + if (ret < 0) + return ret; + if (copy_ts) { tsoffset = d->f.start_time == AV_NOPTS_VALUE ? 0 : d->f.start_time; if (!start_at_zero && d->f.ctx->start_time != AV_NOPTS_VALUE) diff --git a/fftools/ffmpeg_filter.c b/fftools/ffmpeg_filter.c index 7aa9305c88..6438cbd73b 100644 --- a/fftools/ffmpeg_filter.c +++ b/fftools/ffmpeg_filter.c @@ -675,6 +675,10 @@ static int ifilter_bind_ist(InputFilter *ifilter, InputStream *ist) ifp->type_src = ist->st->codecpar->codec_type; + ifp->opts.fallback = av_frame_alloc(); + if (!ifp->opts.fallback) + return AVERROR(ENOMEM); + dec_idx = ist_filter_add(ist, ifilter, filtergraph_is_simple(ifilter->graph), &ifp->opts); if (dec_idx < 0) @@ -863,9 +867,8 @@ static InputFilter *ifilter_alloc(FilterGraph *fg) ifp->index = fg->nb_inputs - 1; ifp->format = -1; - ifp->fallback.format = -1; - ifp->color_space = ifp->fallback.color_space = AVCOL_SPC_UNSPECIFIED; - ifp->color_range = ifp->fallback.color_range = AVCOL_RANGE_UNSPECIFIED; + ifp->color_space = AVCOL_SPC_UNSPECIFIED; + ifp->color_range = AVCOL_RANGE_UNSPECIFIED; ifp->frame_queue = av_fifo_alloc2(8, sizeof(AVFrame*), AV_FIFO_FLAG_AUTO_GROW); if (!ifp->frame_queue) @@ -895,9 +898,8 @@ void fg_free(FilterGraph **pfg) } av_frame_free(&ifp->sub2video.frame); - av_channel_layout_uninit(&ifp->fallback.ch_layout); - av_frame_free(&ifp->frame); + av_frame_free(&ifp->opts.fallback); av_buffer_unref(&ifp->hw_frames_ctx); av_freep(&ifp->linklabel); @@ -1804,33 +1806,6 @@ fail: return ret; } -int ifilter_parameters_from_dec(InputFilter *ifilter, const AVCodecContext *dec) -{ - InputFilterPriv *ifp = ifp_from_ifilter(ifilter); - - ifp->fallback.time_base = dec->pkt_timebase; - - if (dec->codec_type == AVMEDIA_TYPE_VIDEO) { - ifp->fallback.format = dec->pix_fmt; - ifp->fallback.width = dec->width; - ifp->fallback.height = dec->height; - ifp->fallback.sample_aspect_ratio = dec->sample_aspect_ratio; - ifp->fallback.color_space = dec->colorspace; - ifp->fallback.color_range = dec->color_range; - } else if (dec->codec_type == AVMEDIA_TYPE_AUDIO) { - int ret; - - ifp->fallback.format = dec->sample_fmt; - ifp->fallback.sample_rate = dec->sample_rate; - - ret = av_channel_layout_copy(&ifp->fallback.ch_layout, &dec->ch_layout); - if (ret < 0) - return ret; - } - - return 0; -} - static int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *frame) { InputFilterPriv *ifp = ifp_from_ifilter(ifilter); @@ -2527,17 +2502,17 @@ static int send_eof(FilterGraphThread *fgt, InputFilter *ifilter, } else { if (ifp->format < 0) { // the filtergraph was never configured, use the fallback parameters - ifp->format = ifp->fallback.format; - ifp->sample_rate = ifp->fallback.sample_rate; - ifp->width = ifp->fallback.width; - ifp->height = ifp->fallback.height; - ifp->sample_aspect_ratio = ifp->fallback.sample_aspect_ratio; - ifp->color_space = ifp->fallback.color_space; - ifp->color_range = ifp->fallback.color_range; - ifp->time_base = ifp->fallback.time_base; + ifp->format = ifp->opts.fallback->format; + ifp->sample_rate = ifp->opts.fallback->sample_rate; + ifp->width = ifp->opts.fallback->width; + ifp->height = ifp->opts.fallback->height; + ifp->sample_aspect_ratio = ifp->opts.fallback->sample_aspect_ratio; + ifp->color_space = ifp->opts.fallback->colorspace; + ifp->color_range = ifp->opts.fallback->color_range; + ifp->time_base = ifp->opts.fallback->time_base; ret = av_channel_layout_copy(&ifp->ch_layout, - &ifp->fallback.ch_layout); + &ifp->opts.fallback->ch_layout); if (ret < 0) return ret; -- 2.43.0 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 38+ messages in thread
* [FFmpeg-devel] [PATCH 10/18] fftools/ffmpeg: remove unncessary casts for *_thread() return values 2024-03-06 11:03 [FFmpeg-devel] [PATCH 01/18] fftools/cmdutils: fix printing group name in split_commandline() Anton Khirnov ` (7 preceding siblings ...) 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 09/18] fftools/ffmpeg: simplify propagating fallback parameters from decoders to filters Anton Khirnov @ 2024-03-06 11:03 ` Anton Khirnov 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 11/18] fftools/ffmpeg_enc: drop unnecessary parameter from forced_kf_apply() Anton Khirnov ` (8 subsequent siblings) 17 siblings, 0 replies; 38+ messages in thread From: Anton Khirnov @ 2024-03-06 11:03 UTC (permalink / raw) To: ffmpeg-devel These functions used to be passed directly to pthread_create(), which required them to return void*. This is no longer the case, so they can return a plain int. --- fftools/ffmpeg.h | 4 ++-- fftools/ffmpeg_dec.c | 6 +++--- fftools/ffmpeg_demux.c | 4 ++-- fftools/ffmpeg_enc.c | 4 ++-- fftools/ffmpeg_filter.c | 6 +++--- fftools/ffmpeg_mux.c | 4 ++-- fftools/ffmpeg_sched.c | 2 +- fftools/ffmpeg_sched.h | 2 +- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h index 1a60f035b0..1966817bc3 100644 --- a/fftools/ffmpeg.h +++ b/fftools/ffmpeg.h @@ -832,7 +832,7 @@ void update_benchmark(const char *fmt, ...); const char *opt_match_per_type_str(const SpecifierOptList *sol, char mediatype); -void *muxer_thread(void *arg); -void *encoder_thread(void *arg); +int muxer_thread(void *arg); +int encoder_thread(void *arg); #endif /* FFTOOLS_FFMPEG_H */ diff --git a/fftools/ffmpeg_dec.c b/fftools/ffmpeg_dec.c index 7005ada527..3bf7ab4960 100644 --- a/fftools/ffmpeg_dec.c +++ b/fftools/ffmpeg_dec.c @@ -124,7 +124,7 @@ static const AVClass dec_class = { .item_name = dec_item_name, }; -static void *decoder_thread(void *arg); +static int decoder_thread(void *arg); static int dec_alloc(DecoderPriv **pdec, Scheduler *sch, int send_end_ts) { @@ -789,7 +789,7 @@ fail: return AVERROR(ENOMEM); } -static void *decoder_thread(void *arg) +static int decoder_thread(void *arg) { DecoderPriv *dp = arg; DecThreadContext dt; @@ -884,7 +884,7 @@ static void *decoder_thread(void *arg) finish: dec_thread_uninit(&dt); - return (void*)(intptr_t)ret; + return ret; } static enum AVPixelFormat get_format(AVCodecContext *s, const enum AVPixelFormat *pix_fmts) diff --git a/fftools/ffmpeg_demux.c b/fftools/ffmpeg_demux.c index 032a96567f..47312c9fe1 100644 --- a/fftools/ffmpeg_demux.c +++ b/fftools/ffmpeg_demux.c @@ -675,7 +675,7 @@ static int demux_thread_init(DemuxThreadContext *dt) return 0; } -static void *input_thread(void *arg) +static int input_thread(void *arg) { Demuxer *d = arg; InputFile *f = &d->f; @@ -780,7 +780,7 @@ static void *input_thread(void *arg) finish: demux_thread_uninit(&dt); - return (void*)(intptr_t)ret; + return ret; } static void demux_final_stats(Demuxer *d) diff --git a/fftools/ffmpeg_enc.c b/fftools/ffmpeg_enc.c index bdba50df03..1ddef46d03 100644 --- a/fftools/ffmpeg_enc.c +++ b/fftools/ffmpeg_enc.c @@ -870,7 +870,7 @@ fail: return AVERROR(ENOMEM); } -void *encoder_thread(void *arg) +int encoder_thread(void *arg) { OutputStream *ost = arg; Encoder *e = ost->enc; @@ -948,5 +948,5 @@ void *encoder_thread(void *arg) finish: enc_thread_uninit(&et); - return (void*)(intptr_t)ret; + return ret; } diff --git a/fftools/ffmpeg_filter.c b/fftools/ffmpeg_filter.c index 6438cbd73b..a29008387a 100644 --- a/fftools/ffmpeg_filter.c +++ b/fftools/ffmpeg_filter.c @@ -626,7 +626,7 @@ static int ifilter_has_all_input_formats(FilterGraph *fg) return 1; } -static void *filter_thread(void *arg); +static int filter_thread(void *arg); static char *describe_filter_link(FilterGraph *fg, AVFilterInOut *inout, int in) { @@ -2735,7 +2735,7 @@ fail: return AVERROR(ENOMEM); } -static void *filter_thread(void *arg) +static int filter_thread(void *arg) { FilterGraphPriv *fgp = arg; FilterGraph *fg = &fgp->fg; @@ -2849,7 +2849,7 @@ finish: fg_thread_uninit(&fgt); - return (void*)(intptr_t)ret; + return ret; } void fg_send_command(FilterGraph *fg, double time, const char *target, diff --git a/fftools/ffmpeg_mux.c b/fftools/ffmpeg_mux.c index 6ba54b878b..59befefab2 100644 --- a/fftools/ffmpeg_mux.c +++ b/fftools/ffmpeg_mux.c @@ -404,7 +404,7 @@ fail: return AVERROR(ENOMEM); } -void *muxer_thread(void *arg) +int muxer_thread(void *arg) { Muxer *mux = arg; OutputFile *of = &mux->of; @@ -453,7 +453,7 @@ void *muxer_thread(void *arg) finish: mux_thread_uninit(&mt); - return (void*)(intptr_t)ret; + return ret; } static int of_streamcopy(OutputFile *of, OutputStream *ost, AVPacket *pkt) diff --git a/fftools/ffmpeg_sched.c b/fftools/ffmpeg_sched.c index 1144fce958..cf9b0c836e 100644 --- a/fftools/ffmpeg_sched.c +++ b/fftools/ffmpeg_sched.c @@ -2226,7 +2226,7 @@ static void *task_wrapper(void *arg) int ret; int err = 0; - ret = (intptr_t)task->func(task->func_arg); + ret = task->func(task->func_arg); if (ret < 0) av_log(task->func_arg, AV_LOG_ERROR, "Task finished with error code: %d (%s)\n", ret, av_err2str(ret)); diff --git a/fftools/ffmpeg_sched.h b/fftools/ffmpeg_sched.h index 95f9c1d4db..fc6711f9c3 100644 --- a/fftools/ffmpeg_sched.h +++ b/fftools/ffmpeg_sched.h @@ -102,7 +102,7 @@ typedef struct SchedulerNode { unsigned idx_stream; } SchedulerNode; -typedef void* (*SchThreadFunc)(void *arg); +typedef int (*SchThreadFunc)(void *arg); #define SCH_DSTREAM(file, stream) \ (SchedulerNode){ .type = SCH_NODE_TYPE_DEMUX, \ -- 2.43.0 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 38+ messages in thread
* [FFmpeg-devel] [PATCH 11/18] fftools/ffmpeg_enc: drop unnecessary parameter from forced_kf_apply() 2024-03-06 11:03 [FFmpeg-devel] [PATCH 01/18] fftools/cmdutils: fix printing group name in split_commandline() Anton Khirnov ` (8 preceding siblings ...) 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 10/18] fftools/ffmpeg: remove unncessary casts for *_thread() return values Anton Khirnov @ 2024-03-06 11:03 ` Anton Khirnov 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 12/18] fftools/ffmpeg_enc: merge do_{audio, video}_out into frame_encode() Anton Khirnov ` (7 subsequent siblings) 17 siblings, 0 replies; 38+ messages in thread From: Anton Khirnov @ 2024-03-06 11:03 UTC (permalink / raw) To: ffmpeg-devel Encoder timebase is equal to the frame timebase, so does not need to be passed separately. Also, rename in_picture to frame, which is shorter and more accurate - it always contains a frame, never a field. --- fftools/ffmpeg_enc.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fftools/ffmpeg_enc.c b/fftools/ffmpeg_enc.c index 1ddef46d03..f0a17228fe 100644 --- a/fftools/ffmpeg_enc.c +++ b/fftools/ffmpeg_enc.c @@ -746,16 +746,16 @@ static int do_audio_out(OutputFile *of, OutputStream *ost, } static enum AVPictureType forced_kf_apply(void *logctx, KeyframeForceCtx *kf, - AVRational tb, const AVFrame *in_picture) + const AVFrame *frame) { double pts_time; if (kf->ref_pts == AV_NOPTS_VALUE) - kf->ref_pts = in_picture->pts; + kf->ref_pts = frame->pts; - pts_time = (in_picture->pts - kf->ref_pts) * av_q2d(tb); + pts_time = (frame->pts - kf->ref_pts) * av_q2d(frame->time_base); if (kf->index < kf->nb_pts && - av_compare_ts(in_picture->pts, tb, kf->pts[kf->index], AV_TIME_BASE_Q) >= 0) { + av_compare_ts(frame->pts, frame->time_base, kf->pts[kf->index], AV_TIME_BASE_Q) >= 0) { kf->index++; goto force_keyframe; } else if (kf->pexpr) { @@ -780,7 +780,7 @@ static enum AVPictureType forced_kf_apply(void *logctx, KeyframeForceCtx *kf, kf->expr_const_values[FKF_N_FORCED] += 1; goto force_keyframe; } - } else if (kf->type == KF_FORCE_SOURCE && (in_picture->flags & AV_FRAME_FLAG_KEY)) { + } else if (kf->type == KF_FORCE_SOURCE && (frame->flags & AV_FRAME_FLAG_KEY)) { goto force_keyframe; } @@ -801,7 +801,7 @@ static int do_video_out(OutputFile *of, OutputStream *ost, return AVERROR_EOF; in_picture->quality = enc->global_quality; - in_picture->pict_type = forced_kf_apply(ost, &ost->kf, enc->time_base, in_picture); + in_picture->pict_type = forced_kf_apply(ost, &ost->kf, in_picture); #if FFMPEG_OPT_TOP if (ost->top_field_first >= 0) { -- 2.43.0 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 38+ messages in thread
* [FFmpeg-devel] [PATCH 12/18] fftools/ffmpeg_enc: merge do_{audio, video}_out into frame_encode() 2024-03-06 11:03 [FFmpeg-devel] [PATCH 01/18] fftools/cmdutils: fix printing group name in split_commandline() Anton Khirnov ` (9 preceding siblings ...) 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 11/18] fftools/ffmpeg_enc: drop unnecessary parameter from forced_kf_apply() Anton Khirnov @ 2024-03-06 11:03 ` Anton Khirnov 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 13/18] fftools/ffmpeg_sched: allow encoders to send to multiple destinations Anton Khirnov ` (6 subsequent siblings) 17 siblings, 0 replies; 38+ messages in thread From: Anton Khirnov @ 2024-03-06 11:03 UTC (permalink / raw) To: ffmpeg-devel These functions used to be much longer, but now they are only a couple lines each, some of them duplicated between audio and video. --- fftools/ffmpeg_enc.c | 65 +++++++++++++++----------------------------- 1 file changed, 22 insertions(+), 43 deletions(-) diff --git a/fftools/ffmpeg_enc.c b/fftools/ffmpeg_enc.c index f0a17228fe..88635629f7 100644 --- a/fftools/ffmpeg_enc.c +++ b/fftools/ffmpeg_enc.c @@ -727,24 +727,6 @@ static int encode_frame(OutputFile *of, OutputStream *ost, AVFrame *frame, av_assert0(0); } -static int do_audio_out(OutputFile *of, OutputStream *ost, - AVFrame *frame, AVPacket *pkt) -{ - AVCodecContext *enc = ost->enc_ctx; - - if (!(enc->codec->capabilities & AV_CODEC_CAP_PARAM_CHANGE) && - enc->ch_layout.nb_channels != frame->ch_layout.nb_channels) { - av_log(ost, AV_LOG_ERROR, - "Audio channel count changed and encoder does not support parameter changes\n"); - return 0; - } - - if (!check_recording_time(ost, frame->pts, frame->time_base)) - return AVERROR_EOF; - - return encode_frame(of, ost, frame, pkt); -} - static enum AVPictureType forced_kf_apply(void *logctx, KeyframeForceCtx *kf, const AVFrame *frame) { @@ -791,28 +773,6 @@ force_keyframe: return AV_PICTURE_TYPE_I; } -/* May modify/reset frame */ -static int do_video_out(OutputFile *of, OutputStream *ost, - AVFrame *in_picture, AVPacket *pkt) -{ - AVCodecContext *enc = ost->enc_ctx; - - if (!check_recording_time(ost, in_picture->pts, ost->enc_ctx->time_base)) - return AVERROR_EOF; - - in_picture->quality = enc->global_quality; - in_picture->pict_type = forced_kf_apply(ost, &ost->kf, in_picture); - -#if FFMPEG_OPT_TOP - if (ost->top_field_first >= 0) { - in_picture->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST; - in_picture->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST * (!!ost->top_field_first); - } -#endif - - return encode_frame(of, ost, in_picture, pkt); -} - static int frame_encode(OutputStream *ost, AVFrame *frame, AVPacket *pkt) { OutputFile *of = ost->file; @@ -828,11 +788,30 @@ static int frame_encode(OutputStream *ost, AVFrame *frame, AVPacket *pkt) } if (frame) { - return (type == AVMEDIA_TYPE_VIDEO) ? do_video_out(of, ost, frame, pkt) : - do_audio_out(of, ost, frame, pkt); + if (!check_recording_time(ost, frame->pts, frame->time_base)) + return AVERROR_EOF; + + if (type == AVMEDIA_TYPE_VIDEO) { + frame->quality = ost->enc_ctx->global_quality; + frame->pict_type = forced_kf_apply(ost, &ost->kf, frame); + +#if FFMPEG_OPT_TOP + if (ost->top_field_first >= 0) { + frame->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST; + frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST * (!!ost->top_field_first); + } +#endif + } else { + if (!(ost->enc_ctx->codec->capabilities & AV_CODEC_CAP_PARAM_CHANGE) && + ost->enc_ctx->ch_layout.nb_channels != frame->ch_layout.nb_channels) { + av_log(ost, AV_LOG_ERROR, + "Audio channel count changed and encoder does not support parameter changes\n"); + return 0; + } + } } - return encode_frame(of, ost, NULL, pkt); + return encode_frame(of, ost, frame, pkt); } static void enc_thread_set_name(const OutputStream *ost) -- 2.43.0 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 38+ messages in thread
* [FFmpeg-devel] [PATCH 13/18] fftools/ffmpeg_sched: allow encoders to send to multiple destinations 2024-03-06 11:03 [FFmpeg-devel] [PATCH 01/18] fftools/cmdutils: fix printing group name in split_commandline() Anton Khirnov ` (10 preceding siblings ...) 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 12/18] fftools/ffmpeg_enc: merge do_{audio, video}_out into frame_encode() Anton Khirnov @ 2024-03-06 11:03 ` Anton Khirnov 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 14/18] fftools/ffmpeg_sched: factor initializing nodes into separate function Anton Khirnov ` (5 subsequent siblings) 17 siblings, 0 replies; 38+ messages in thread From: Anton Khirnov @ 2024-03-06 11:03 UTC (permalink / raw) To: ffmpeg-devel Will become useful in following commits. --- fftools/ffmpeg_sched.c | 117 ++++++++++++++++++++++++++++++++++------- 1 file changed, 98 insertions(+), 19 deletions(-) diff --git a/fftools/ffmpeg_sched.c b/fftools/ffmpeg_sched.c index cf9b0c836e..e5435dd866 100644 --- a/fftools/ffmpeg_sched.c +++ b/fftools/ffmpeg_sched.c @@ -104,7 +104,9 @@ typedef struct SchEnc { const AVClass *class; SchedulerNode src; - SchedulerNode dst; + SchedulerNode *dst; + uint8_t *dst_finished; + unsigned nb_dst; // [0] - index of the sync queue in Scheduler.sq_enc, // [1] - index of this encoder in the sq @@ -134,7 +136,9 @@ typedef struct SchEnc { ThreadQueue *queue; // tq_send() to queue returned EOF int in_finished; - int out_finished; + + // temporary storage used by sch_enc_send() + AVPacket *send_pkt; } SchEnc; typedef struct SchDemuxStream { @@ -569,6 +573,11 @@ void sch_free(Scheduler **psch) SchEnc *enc = &sch->enc[i]; tq_free(&enc->queue); + + av_packet_free(&enc->send_pkt); + + av_freep(&enc->dst); + av_freep(&enc->dst_finished); } av_freep(&sch->enc); @@ -819,6 +828,10 @@ int sch_add_enc(Scheduler *sch, SchThreadFunc func, void *ctx, task_init(sch, &enc->task, SCH_NODE_TYPE_ENC, idx, func, ctx); + enc->send_pkt = av_packet_alloc(); + if (!enc->send_pkt) + return AVERROR(ENOMEM); + ret = queue_alloc(&enc->queue, 1, 0, QUEUE_FRAMES); if (ret < 0) return ret; @@ -1048,9 +1061,14 @@ int sch_connect(Scheduler *sch, SchedulerNode src, SchedulerNode dst) enc = &sch->enc[src.idx]; ms = &sch->mux[dst.idx].streams[dst.idx_stream]; - av_assert0(!enc->dst.type && !ms->src.type); - enc->dst = dst; - ms->src = src; + av_assert0(!ms->src.type); + + ret = GROW_ARRAY(enc->dst, enc->nb_dst); + if (ret < 0) + return ret; + + enc->dst[enc->nb_dst - 1] = dst; + ms->src = src; break; } @@ -1339,12 +1357,16 @@ int sch_start(Scheduler *sch) "Encoder not connected to a source\n"); return AVERROR(EINVAL); } - if (!enc->dst.type) { + if (!enc->nb_dst) { av_log(enc, AV_LOG_ERROR, - "Encoder not connected to a sink\n"); + "Encoder not connected to any sink\n"); return AVERROR(EINVAL); } + enc->dst_finished = av_calloc(enc->nb_dst, sizeof(*enc->dst_finished)); + if (!enc->dst_finished) + return AVERROR(ENOMEM); + ret = task_start(&enc->task); if (ret < 0) return ret; @@ -1518,15 +1540,21 @@ static int send_to_enc_sq(Scheduler *sch, SchEnc *enc, AVFrame *frame) // TODO: consider a cleaner way of passing this information through // the pipeline if (!frame) { - SchMux *mux = &sch->mux[enc->dst.idx]; - SchMuxStream *ms = &mux->streams[enc->dst.idx_stream]; + for (unsigned i = 0; i < enc->nb_dst; i++) { + SchMux *mux; + SchMuxStream *ms; - pthread_mutex_lock(&sch->schedule_lock); - ms->source_finished = 1; - schedule_update_locked(sch); + mux = &sch->mux[enc->dst[i].idx]; + ms = &mux->streams[enc->dst[i].idx_stream]; - pthread_mutex_unlock(&sch->schedule_lock); + pthread_mutex_lock(&sch->schedule_lock); + + ms->source_finished = 1; + schedule_update_locked(sch); + + pthread_mutex_unlock(&sch->schedule_lock); + } } pthread_mutex_lock(&sq->lock); @@ -2080,20 +2108,64 @@ int sch_enc_receive(Scheduler *sch, unsigned enc_idx, AVFrame *frame) return ret; } +static int enc_send_to_dst(Scheduler *sch, const SchedulerNode dst, + uint8_t *dst_finished, AVPacket *pkt) +{ + int ret; + + if (*dst_finished) + return AVERROR_EOF; + + if (!pkt) + goto finish; + + ret = send_to_mux(sch, &sch->mux[dst.idx], dst.idx_stream, pkt); + if (ret == AVERROR_EOF) + goto finish; + + return ret; + +finish: + send_to_mux(sch, &sch->mux[dst.idx], dst.idx_stream, NULL); + + *dst_finished = 1; + + return AVERROR_EOF; +} + int sch_enc_send(Scheduler *sch, unsigned enc_idx, AVPacket *pkt) { SchEnc *enc; int ret; + unsigned nb_done = 0; av_assert0(enc_idx < sch->nb_enc); enc = &sch->enc[enc_idx]; - if (enc->out_finished) - return pkt ? AVERROR_EOF : 0; + for (unsigned i = 0; i < enc->nb_dst; i++) { + uint8_t *finished = &enc->dst_finished[i]; + AVPacket *to_send = pkt; - ret = send_to_mux(sch, &sch->mux[enc->dst.idx], enc->dst.idx_stream, pkt); - if (ret < 0) - enc->out_finished = 1; + // sending a packet consumes it, so make a temporary reference if needed + if (i < enc->nb_dst - 1) { + to_send = enc->send_pkt; + + ret = av_packet_ref(to_send, pkt); + if (ret < 0) + return ret; + } + + ret = enc_send_to_dst(sch, enc->dst[i], finished, to_send); + if (ret < 0) { + av_packet_unref(to_send); + if (ret == AVERROR_EOF) { + nb_done++; + ret = 0; + continue; + } + return ret; + } + } return ret; } @@ -2101,10 +2173,17 @@ int sch_enc_send(Scheduler *sch, unsigned enc_idx, AVPacket *pkt) static int enc_done(Scheduler *sch, unsigned enc_idx) { SchEnc *enc = &sch->enc[enc_idx]; + int ret = 0; tq_receive_finish(enc->queue, 0); - return send_to_mux(sch, &sch->mux[enc->dst.idx], enc->dst.idx_stream, NULL); + for (unsigned i = 0; i < enc->nb_dst; i++) { + int err = enc_send_to_dst(sch, enc->dst[i], &enc->dst_finished[i], NULL); + if (err < 0 && err != AVERROR_EOF) + ret = err_merge(ret, err); + } + + return ret; } int sch_filter_receive(Scheduler *sch, unsigned fg_idx, -- 2.43.0 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 38+ messages in thread
* [FFmpeg-devel] [PATCH 14/18] fftools/ffmpeg_sched: factor initializing nodes into separate function 2024-03-06 11:03 [FFmpeg-devel] [PATCH 01/18] fftools/cmdutils: fix printing group name in split_commandline() Anton Khirnov ` (11 preceding siblings ...) 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 13/18] fftools/ffmpeg_sched: allow encoders to send to multiple destinations Anton Khirnov @ 2024-03-06 11:03 ` Anton Khirnov 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 15/18] fftools/ffmpeg_sched: allow connecting encoder output to decoders Anton Khirnov ` (4 subsequent siblings) 17 siblings, 0 replies; 38+ messages in thread From: Anton Khirnov @ 2024-03-06 11:03 UTC (permalink / raw) To: ffmpeg-devel Will be useful in following commits. --- fftools/ffmpeg_sched.c | 151 +++++++++++++++++++++++++---------------- 1 file changed, 91 insertions(+), 60 deletions(-) diff --git a/fftools/ffmpeg_sched.c b/fftools/ffmpeg_sched.c index e5435dd866..5f8ef04680 100644 --- a/fftools/ffmpeg_sched.c +++ b/fftools/ffmpeg_sched.c @@ -1303,11 +1303,65 @@ static void schedule_update_locked(Scheduler *sch) } -int sch_start(Scheduler *sch) +static int start_prepare(Scheduler *sch) { int ret; - sch->transcode_started = 1; + for (unsigned i = 0; i < sch->nb_demux; i++) { + SchDemux *d = &sch->demux[i]; + + for (unsigned j = 0; j < d->nb_streams; j++) { + SchDemuxStream *ds = &d->streams[j]; + + if (!ds->nb_dst) { + av_log(d, AV_LOG_ERROR, + "Demuxer stream %u not connected to any sink\n", j); + return AVERROR(EINVAL); + } + + ds->dst_finished = av_calloc(ds->nb_dst, sizeof(*ds->dst_finished)); + if (!ds->dst_finished) + return AVERROR(ENOMEM); + } + } + + for (unsigned i = 0; i < sch->nb_dec; i++) { + SchDec *dec = &sch->dec[i]; + + if (!dec->src.type) { + av_log(dec, AV_LOG_ERROR, + "Decoder not connected to a source\n"); + return AVERROR(EINVAL); + } + if (!dec->nb_dst) { + av_log(dec, AV_LOG_ERROR, + "Decoder not connected to any sink\n"); + return AVERROR(EINVAL); + } + + dec->dst_finished = av_calloc(dec->nb_dst, sizeof(*dec->dst_finished)); + if (!dec->dst_finished) + return AVERROR(ENOMEM); + } + + for (unsigned i = 0; i < sch->nb_enc; i++) { + SchEnc *enc = &sch->enc[i]; + + if (!enc->src.type) { + av_log(enc, AV_LOG_ERROR, + "Encoder not connected to a source\n"); + return AVERROR(EINVAL); + } + if (!enc->nb_dst) { + av_log(enc, AV_LOG_ERROR, + "Encoder not connected to any sink\n"); + return AVERROR(EINVAL); + } + + enc->dst_finished = av_calloc(enc->nb_dst, sizeof(*enc->dst_finished)); + if (!enc->dst_finished) + return AVERROR(ENOMEM); + } for (unsigned i = 0; i < sch->nb_mux; i++) { SchMux *mux = &sch->mux[i]; @@ -1341,35 +1395,6 @@ int sch_start(Scheduler *sch) QUEUE_PACKETS); if (ret < 0) return ret; - - if (mux->nb_streams_ready == mux->nb_streams) { - ret = mux_init(sch, mux); - if (ret < 0) - return ret; - } - } - - for (unsigned i = 0; i < sch->nb_enc; i++) { - SchEnc *enc = &sch->enc[i]; - - if (!enc->src.type) { - av_log(enc, AV_LOG_ERROR, - "Encoder not connected to a source\n"); - return AVERROR(EINVAL); - } - if (!enc->nb_dst) { - av_log(enc, AV_LOG_ERROR, - "Encoder not connected to any sink\n"); - return AVERROR(EINVAL); - } - - enc->dst_finished = av_calloc(enc->nb_dst, sizeof(*enc->dst_finished)); - if (!enc->dst_finished) - return AVERROR(ENOMEM); - - ret = task_start(&enc->task); - if (ret < 0) - return ret; } for (unsigned i = 0; i < sch->nb_filters; i++) { @@ -1396,6 +1421,41 @@ int sch_start(Scheduler *sch) return AVERROR(EINVAL); } } + } + + return 0; +} + +int sch_start(Scheduler *sch) +{ + int ret; + + ret = start_prepare(sch); + if (ret < 0) + return ret; + + sch->transcode_started = 1; + + for (unsigned i = 0; i < sch->nb_mux; i++) { + SchMux *mux = &sch->mux[i]; + + if (mux->nb_streams_ready == mux->nb_streams) { + ret = mux_init(sch, mux); + if (ret < 0) + return ret; + } + } + + for (unsigned i = 0; i < sch->nb_enc; i++) { + SchEnc *enc = &sch->enc[i]; + + ret = task_start(&enc->task); + if (ret < 0) + return ret; + } + + for (unsigned i = 0; i < sch->nb_filters; i++) { + SchFilterGraph *fg = &sch->filters[i]; ret = task_start(&fg->task); if (ret < 0) @@ -1405,21 +1465,6 @@ int sch_start(Scheduler *sch) for (unsigned i = 0; i < sch->nb_dec; i++) { SchDec *dec = &sch->dec[i]; - if (!dec->src.type) { - av_log(dec, AV_LOG_ERROR, - "Decoder not connected to a source\n"); - return AVERROR(EINVAL); - } - if (!dec->nb_dst) { - av_log(dec, AV_LOG_ERROR, - "Decoder not connected to any sink\n"); - return AVERROR(EINVAL); - } - - dec->dst_finished = av_calloc(dec->nb_dst, sizeof(*dec->dst_finished)); - if (!dec->dst_finished) - return AVERROR(ENOMEM); - ret = task_start(&dec->task); if (ret < 0) return ret; @@ -1431,20 +1476,6 @@ int sch_start(Scheduler *sch) if (!d->nb_streams) continue; - for (unsigned j = 0; j < d->nb_streams; j++) { - SchDemuxStream *ds = &d->streams[j]; - - if (!ds->nb_dst) { - av_log(d, AV_LOG_ERROR, - "Demuxer stream %u not connected to any sink\n", j); - return AVERROR(EINVAL); - } - - ds->dst_finished = av_calloc(ds->nb_dst, sizeof(*ds->dst_finished)); - if (!ds->dst_finished) - return AVERROR(ENOMEM); - } - ret = task_start(&d->task); if (ret < 0) return ret; -- 2.43.0 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 38+ messages in thread
* [FFmpeg-devel] [PATCH 15/18] fftools/ffmpeg_sched: allow connecting encoder output to decoders 2024-03-06 11:03 [FFmpeg-devel] [PATCH 01/18] fftools/cmdutils: fix printing group name in split_commandline() Anton Khirnov ` (12 preceding siblings ...) 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 14/18] fftools/ffmpeg_sched: factor initializing nodes into separate function Anton Khirnov @ 2024-03-06 11:03 ` Anton Khirnov 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 16/18] fftools/ffmpeg: prepare FrameData for having allocated fields Anton Khirnov ` (3 subsequent siblings) 17 siblings, 0 replies; 38+ messages in thread From: Anton Khirnov @ 2024-03-06 11:03 UTC (permalink / raw) To: ffmpeg-devel --- fftools/ffmpeg_sched.c | 212 ++++++++++++++++++++++++++++++++++------- fftools/ffmpeg_sched.h | 8 +- 2 files changed, 181 insertions(+), 39 deletions(-) diff --git a/fftools/ffmpeg_sched.c b/fftools/ffmpeg_sched.c index 5f8ef04680..d1fb942c34 100644 --- a/fftools/ffmpeg_sched.c +++ b/fftools/ffmpeg_sched.c @@ -1051,24 +1051,43 @@ int sch_connect(Scheduler *sch, SchedulerNode src, SchedulerNode dst) } case SCH_NODE_TYPE_ENC: { SchEnc *enc; - SchMuxStream *ms; av_assert0(src.idx < sch->nb_enc); - // encoding packets go to muxing - av_assert0(dst.type == SCH_NODE_TYPE_MUX && - dst.idx < sch->nb_mux && - dst.idx_stream < sch->mux[dst.idx].nb_streams); enc = &sch->enc[src.idx]; - ms = &sch->mux[dst.idx].streams[dst.idx_stream]; - - av_assert0(!ms->src.type); ret = GROW_ARRAY(enc->dst, enc->nb_dst); if (ret < 0) return ret; enc->dst[enc->nb_dst - 1] = dst; - ms->src = src; + + // encoding packets go to muxing or decoding + switch (dst.type) { + case SCH_NODE_TYPE_MUX: { + SchMuxStream *ms; + + av_assert0(dst.idx < sch->nb_mux && + dst.idx_stream < sch->mux[dst.idx].nb_streams); + ms = &sch->mux[dst.idx].streams[dst.idx_stream]; + + av_assert0(!ms->src.type); + ms->src = src; + + break; + } + case SCH_NODE_TYPE_DEC: { + SchDec *dec; + + av_assert0(dst.idx < sch->nb_dec); + dec = &sch->dec[dst.idx]; + + av_assert0(!dec->src.type); + dec->src = src; + + break; + } + default: av_assert0(0); + } break; } @@ -1217,6 +1236,31 @@ int sch_mux_sub_heartbeat_add(Scheduler *sch, unsigned mux_idx, unsigned stream_ return 0; } +static void unchoke_for_stream(Scheduler *sch, SchedulerNode src) +{ + while (1) { + SchFilterGraph *fg; + + // fed directly by a demuxer (i.e. not through a filtergraph) + if (src.type == SCH_NODE_TYPE_DEMUX) { + sch->demux[src.idx].waiter.choked_next = 0; + return; + } + + av_assert0(src.type == SCH_NODE_TYPE_FILTER_OUT); + fg = &sch->filters[src.idx]; + + // the filtergraph contains internal sources and + // requested to be scheduled directly + if (fg->best_input == fg->nb_inputs) { + fg->waiter.choked_next = 0; + return; + } + + src = fg->inputs[fg->best_input].src_sched; + } +} + static void schedule_update_locked(Scheduler *sch) { int64_t dts; @@ -1245,7 +1289,6 @@ static void schedule_update_locked(Scheduler *sch) for (unsigned j = 0; j < mux->nb_streams; j++) { SchMuxStream *ms = &mux->streams[j]; - SchDemux *d; // unblock sources for output streams that are not finished // and not too far ahead of the trailing stream @@ -1256,28 +1299,9 @@ static void schedule_update_locked(Scheduler *sch) if (dts != AV_NOPTS_VALUE && ms->last_dts - dts >= SCHEDULE_TOLERANCE) continue; - // for outputs fed from filtergraphs, consider that filtergraph's - // best_input information, in other cases there is a well-defined - // source demuxer - if (ms->src_sched.type == SCH_NODE_TYPE_FILTER_OUT) { - SchFilterGraph *fg = &sch->filters[ms->src_sched.idx]; - SchFilterIn *fi; - - // the filtergraph contains internal sources and - // requested to be scheduled directly - if (fg->best_input == fg->nb_inputs) { - fg->waiter.choked_next = 0; - have_unchoked = 1; - continue; - } - - fi = &fg->inputs[fg->best_input]; - d = &sch->demux[fi->src_sched.idx]; - } else - d = &sch->demux[ms->src_sched.idx]; - - d->waiter.choked_next = 0; - have_unchoked = 1; + // resolve the source to unchoke + unchoke_for_stream(sch, ms->src_sched); + have_unchoked = 1; } } @@ -1303,6 +1327,105 @@ static void schedule_update_locked(Scheduler *sch) } +enum { + CYCLE_NODE_NEW = 0, + CYCLE_NODE_STARTED, + CYCLE_NODE_DONE, +}; + +static int +check_acyclic_for_output(const Scheduler *sch, SchedulerNode src, + uint8_t *filters_visited, SchedulerNode *filters_stack) +{ + unsigned nb_filters_stack = 0; + + memset(filters_visited, 0, sch->nb_filters * sizeof(*filters_visited)); + + while (1) { + const SchFilterGraph *fg = &sch->filters[src.idx]; + + filters_visited[src.idx] = CYCLE_NODE_STARTED; + + // descend into every input, depth first + if (src.idx_stream < fg->nb_inputs) { + const SchFilterIn *fi = &fg->inputs[src.idx_stream++]; + + // connected to demuxer, no cycles possible + if (fi->src_sched.type == SCH_NODE_TYPE_DEMUX) + continue; + + // otherwise connected to another filtergraph + av_assert0(fi->src_sched.type == SCH_NODE_TYPE_FILTER_OUT); + + // found a cycle + if (filters_visited[fi->src_sched.idx] == CYCLE_NODE_STARTED) + return AVERROR(EINVAL); + + // place current position on stack and descend + av_assert0(nb_filters_stack < sch->nb_filters); + filters_stack[nb_filters_stack++] = src; + src = (SchedulerNode){ .idx = fi->src_sched.idx, .idx_stream = 0 }; + continue; + } + + filters_visited[src.idx] = CYCLE_NODE_DONE; + + // previous search finished, + if (nb_filters_stack) { + src = filters_stack[--nb_filters_stack]; + continue; + } + return 0; + } +} + +static int check_acyclic(Scheduler *sch) +{ + uint8_t *filters_visited = NULL; + SchedulerNode *filters_stack = NULL; + + int ret = 0; + + if (!sch->nb_filters) + return 0; + + filters_visited = av_malloc_array(sch->nb_filters, sizeof(*filters_visited)); + if (!filters_visited) + return AVERROR(ENOMEM); + + filters_stack = av_malloc_array(sch->nb_filters, sizeof(*filters_stack)); + if (!filters_stack) { + ret = AVERROR(ENOMEM); + goto fail; + } + + // trace the transcoding graph upstream from every output stream + // fed by a filtergraph + for (unsigned i = 0; i < sch->nb_mux; i++) { + SchMux *mux = &sch->mux[i]; + + for (unsigned j = 0; j < mux->nb_streams; j++) { + SchMuxStream *ms = &mux->streams[j]; + SchedulerNode src = ms->src_sched; + + if (src.type != SCH_NODE_TYPE_FILTER_OUT) + continue; + src.idx_stream = 0; + + ret = check_acyclic_for_output(sch, src, filters_visited, filters_stack); + if (ret < 0) { + av_log(mux, AV_LOG_ERROR, "Transcoding graph has a cycle\n"); + goto fail; + } + } + } + +fail: + av_freep(&filters_visited); + av_freep(&filters_stack); + return ret; +} + static int start_prepare(Scheduler *sch) { int ret; @@ -1402,14 +1525,21 @@ static int start_prepare(Scheduler *sch) for (unsigned j = 0; j < fg->nb_inputs; j++) { SchFilterIn *fi = &fg->inputs[j]; + SchDec *dec; if (!fi->src.type) { av_log(fg, AV_LOG_ERROR, "Filtergraph input %u not connected to a source\n", j); return AVERROR(EINVAL); } + av_assert0(fi->src.type == SCH_NODE_TYPE_DEC); + dec = &sch->dec[fi->src.idx]; - fi->src_sched = sch->dec[fi->src.idx].src; + switch (dec->src.type) { + case SCH_NODE_TYPE_DEMUX: fi->src_sched = dec->src; break; + case SCH_NODE_TYPE_ENC: fi->src_sched = sch->enc[dec->src.idx].src; break; + default: av_assert0(0); + } } for (unsigned j = 0; j < fg->nb_outputs; j++) { @@ -1423,6 +1553,11 @@ static int start_prepare(Scheduler *sch) } } + // Check that the transcoding graph has no cycles. + ret = check_acyclic(sch); + if (ret < 0) + return ret; + return 0; } @@ -1575,6 +1710,8 @@ static int send_to_enc_sq(Scheduler *sch, SchEnc *enc, AVFrame *frame) SchMux *mux; SchMuxStream *ms; + if (enc->dst[i].type != SCH_NODE_TYPE_MUX) + continue; mux = &sch->mux[enc->dst[i].idx]; ms = &mux->streams[enc->dst[i].idx_stream]; @@ -2150,14 +2287,19 @@ static int enc_send_to_dst(Scheduler *sch, const SchedulerNode dst, if (!pkt) goto finish; - ret = send_to_mux(sch, &sch->mux[dst.idx], dst.idx_stream, pkt); + ret = (dst.type == SCH_NODE_TYPE_MUX) ? + send_to_mux(sch, &sch->mux[dst.idx], dst.idx_stream, pkt) : + tq_send(sch->dec[dst.idx].queue, 0, pkt); if (ret == AVERROR_EOF) goto finish; return ret; finish: - send_to_mux(sch, &sch->mux[dst.idx], dst.idx_stream, NULL); + if (dst.type == SCH_NODE_TYPE_MUX) + send_to_mux(sch, &sch->mux[dst.idx], dst.idx_stream, NULL); + else + tq_send_finish(sch->dec[dst.idx].queue, 0); *dst_finished = 1; diff --git a/fftools/ffmpeg_sched.h b/fftools/ffmpeg_sched.h index fc6711f9c3..a9190bd3d1 100644 --- a/fftools/ffmpeg_sched.h +++ b/fftools/ffmpeg_sched.h @@ -35,9 +35,9 @@ * - demuxers, each containing any number of demuxed streams; demuxed packets * belonging to some stream are sent to any number of decoders (transcoding) * and/or muxers (streamcopy); - * - decoders, which receive encoded packets from some demuxed stream, decode - * them, and send decoded frames to any number of filtergraph inputs - * (audio/video) or encoders (subtitles); + * - decoders, which receive encoded packets from some demuxed stream or + * encoder, decode them, and send decoded frames to any number of filtergraph + * inputs (audio/video) or encoders (subtitles); * - filtergraphs, each containing zero or more inputs (0 in case the * filtergraph contains a lavfi source filter), and one or more outputs; the * inputs and outputs need not have matching media types; @@ -45,7 +45,7 @@ * filtered frames from each output are sent to some encoder; * - encoders, which receive decoded frames from some decoder (subtitles) or * some filtergraph output (audio/video), encode them, and send encoded - * packets to some muxed stream; + * packets to any number of muxed streams or decoders; * - muxers, each containing any number of muxed streams; each muxed stream * receives encoded packets from some demuxed stream (streamcopy) or some * encoder (transcoding); those packets are interleaved and written out by the -- 2.43.0 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 38+ messages in thread
* [FFmpeg-devel] [PATCH 16/18] fftools/ffmpeg: prepare FrameData for having allocated fields 2024-03-06 11:03 [FFmpeg-devel] [PATCH 01/18] fftools/cmdutils: fix printing group name in split_commandline() Anton Khirnov ` (13 preceding siblings ...) 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 15/18] fftools/ffmpeg_sched: allow connecting encoder output to decoders Anton Khirnov @ 2024-03-06 11:03 ` Anton Khirnov 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 17/18] fftools/ffmpeg: add loopback decoding Anton Khirnov ` (2 subsequent siblings) 17 siblings, 0 replies; 38+ messages in thread From: Anton Khirnov @ 2024-03-06 11:03 UTC (permalink / raw) To: ffmpeg-devel Will be useful in following commits. --- fftools/ffmpeg.c | 41 +++++++++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c index 2f01a01e2d..ffd25f4c5b 100644 --- a/fftools/ffmpeg.c +++ b/fftools/ffmpeg.c @@ -402,23 +402,44 @@ InputStream *ist_iter(InputStream *prev) return NULL; } +static void frame_data_free(void *opaque, uint8_t *data) +{ + av_free(data); +} + static int frame_data_ensure(AVBufferRef **dst, int writable) { - if (!*dst) { + AVBufferRef *src = *dst; + + if (!src || (writable && !av_buffer_is_writable(src))) { FrameData *fd; - *dst = av_buffer_allocz(sizeof(*fd)); - if (!*dst) + fd = av_mallocz(sizeof(*fd)); + if (!fd) return AVERROR(ENOMEM); - fd = (FrameData*)((*dst)->data); - fd->dec.frame_num = UINT64_MAX; - fd->dec.pts = AV_NOPTS_VALUE; + *dst = av_buffer_create((uint8_t *)fd, sizeof(*fd), + frame_data_free, NULL, 0); + if (!*dst) { + av_buffer_unref(&src); + av_freep(&fd); + return AVERROR(ENOMEM); + } - for (unsigned i = 0; i < FF_ARRAY_ELEMS(fd->wallclock); i++) - fd->wallclock[i] = INT64_MIN; - } else if (writable) - return av_buffer_make_writable(dst); + if (src) { + const FrameData *fd_src = (const FrameData *)src->data; + + memcpy(fd, fd_src, sizeof(*fd)); + + av_buffer_unref(&src); + } else { + fd->dec.frame_num = UINT64_MAX; + fd->dec.pts = AV_NOPTS_VALUE; + + for (unsigned i = 0; i < FF_ARRAY_ELEMS(fd->wallclock); i++) + fd->wallclock[i] = INT64_MIN; + } + } return 0; } -- 2.43.0 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 38+ messages in thread
* [FFmpeg-devel] [PATCH 17/18] fftools/ffmpeg: add loopback decoding 2024-03-06 11:03 [FFmpeg-devel] [PATCH 01/18] fftools/cmdutils: fix printing group name in split_commandline() Anton Khirnov ` (14 preceding siblings ...) 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 16/18] fftools/ffmpeg: prepare FrameData for having allocated fields Anton Khirnov @ 2024-03-06 11:03 ` Anton Khirnov 2024-03-07 14:42 ` [FFmpeg-devel] [PATCH v2 " Anton Khirnov 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 18/18] fftools/ffmpeg_enc: set AV_PKT_FLAG_TRUSTED on encoded packets Anton Khirnov 2024-03-09 19:08 ` [FFmpeg-devel] [PATCH 01/18] fftools/cmdutils: fix printing group name in split_commandline() Anton Khirnov 17 siblings, 1 reply; 38+ messages in thread From: Anton Khirnov @ 2024-03-06 11:03 UTC (permalink / raw) To: ffmpeg-devel This allows to send an encoder's output back to decoding and feed the result into a complex filtergraph. --- Changelog | 1 + doc/ffmpeg.texi | 49 ++++++++++-- fftools/cmdutils.c | 2 +- fftools/cmdutils.h | 7 +- fftools/ffmpeg.c | 26 +++++++ fftools/ffmpeg.h | 22 ++++++ fftools/ffmpeg_dec.c | 164 +++++++++++++++++++++++++++++++++++++++- fftools/ffmpeg_enc.c | 19 +++++ fftools/ffmpeg_filter.c | 47 +++++++++++- fftools/ffmpeg_opt.c | 13 +++- 10 files changed, 336 insertions(+), 14 deletions(-) diff --git a/Changelog b/Changelog index 0ba3a77c08..22186697fb 100644 --- a/Changelog +++ b/Changelog @@ -31,6 +31,7 @@ version <next>: - removed deprecated ffmpeg CLI options -psnr and -map_channel - DVD-Video demuxer, powered by libdvdnav and libdvdread - ffprobe -show_stream_groups option +- ffmpeg CLI loopback decoders version 6.1: diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi index bee3cd4c70..650011e6d2 100644 --- a/doc/ffmpeg.texi +++ b/doc/ffmpeg.texi @@ -219,6 +219,40 @@ Since there is no decoding or encoding, it is very fast and there is no quality loss. However, it might not work in some cases because of many factors. Applying filters is obviously also impossible, since filters work on uncompressed data. +@section Loopback decoders +While decoders are normally associated with demuxer streams, it is also possible +to create "loopback" decoders that decode the output from some encoder and allow +it to be fed back to complex filtergraphs. This is done with the @code{-dec} +directive, which takes as a parameter the index of the output stream that should +be decoded. Every such directive creates a new loopback decoder, indexed with +successive integers starting at zero. These indices should then be used to refer +to loopback decoders in complex filtergraph link labels, as described in the +documentation for @option{-filter_complex}. + +E.g. the following example: + +@example +ffmpeg -i INPUT \ + -map 0:v:0 -c:v libx264 -crf 45 -f null - \ + -dec 0:0 -filter_complex '[0:v][dec0]hstack[stack]' \ + -map '[stack]' -c:v ffv1 OUTPUT +@end example + +reads an input video and +@itemize +@item +(line 2) encodes it with @code{libx264} at low quality; + +@item +(line 3) decodes this encoded stream and places it side by side with the +original input video; + +@item +(line 4) combined video is then losslessly encoded and written into +@file{OUTPUT}. + +@end itemize + @c man end DETAILED DESCRIPTION @chapter Stream selection @@ -2105,11 +2139,16 @@ type -- see the @option{-filter} options. @var{filtergraph} is a description of the filtergraph, as described in the ``Filtergraph syntax'' section of the ffmpeg-filters manual. -Input link labels must refer to input streams using the -@code{[file_index:stream_specifier]} syntax (i.e. the same as @option{-map} -uses). If @var{stream_specifier} matches multiple streams, the first one will be -used. An unlabeled input will be connected to the first unused input stream of -the matching type. +Input link labels must refer to either input streams or loopback decoders. For +input streams, use the @code{[file_index:stream_specifier]} syntax (i.e. the +same as @option{-map} uses). If @var{stream_specifier} matches multiple streams, +the first one will be used. + +For decoders, the link label must be [dec@var{dec_idx}], where @var{dec_idx} is +the index of the loopback decoder to be connected to given input. + +An unlabeled input will be connected to the first unused input stream of the +matching type. Output link labels are referred to with @option{-map}. Unlabeled outputs are added to the first output file. diff --git a/fftools/cmdutils.c b/fftools/cmdutils.c index 8efec942bd..f3c258bb99 100644 --- a/fftools/cmdutils.c +++ b/fftools/cmdutils.c @@ -528,7 +528,7 @@ static void check_options(const OptionDef *po) { while (po->name) { if (po->flags & OPT_PERFILE) - av_assert0(po->flags & (OPT_INPUT | OPT_OUTPUT)); + av_assert0(po->flags & (OPT_INPUT | OPT_OUTPUT | OPT_DECODER)); if (po->type == OPT_TYPE_FUNC) av_assert0(!(po->flags & (OPT_FLAG_OFFSET | OPT_FLAG_SPEC))); diff --git a/fftools/cmdutils.h b/fftools/cmdutils.h index 86428b3fa4..d0c773663b 100644 --- a/fftools/cmdutils.h +++ b/fftools/cmdutils.h @@ -144,8 +144,8 @@ typedef struct OptionDef { #define OPT_AUDIO (1 << 4) #define OPT_SUBTITLE (1 << 5) #define OPT_DATA (1 << 6) -/* The option is per-file (currently ffmpeg-only). At least one of OPT_INPUT or - * OPT_OUTPUT must be set when this flag is in use. +/* The option is per-file (currently ffmpeg-only). At least one of OPT_INPUT, + * OPT_OUTPUT, OPT_DECODER must be set when this flag is in use. */ #define OPT_PERFILE (1 << 7) @@ -175,6 +175,9 @@ typedef struct OptionDef { * name is stored in u1.name_canon */ #define OPT_HAS_CANON (1 << 14) +/* ffmpeg-only - OPT_PERFILE may apply to standalone decoders */ +#define OPT_DECODER (1 << 15) + union { void *dst_ptr; int (*func_arg)(void *, const char *, const char *); diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c index ffd25f4c5b..d4e5f978f1 100644 --- a/fftools/ffmpeg.c +++ b/fftools/ffmpeg.c @@ -131,6 +131,9 @@ int nb_output_files = 0; FilterGraph **filtergraphs; int nb_filtergraphs; +Decoder **decoders; +int nb_decoders; + #if HAVE_TERMIOS_H /* init terminal so that we can grab keys */ @@ -340,6 +343,10 @@ static void ffmpeg_cleanup(int ret) for (int i = 0; i < nb_input_files; i++) ifile_close(&input_files[i]); + for (int i = 0; i < nb_decoders; i++) + dec_free(&decoders[i]); + av_freep(&decoders); + if (vstats_file) { if (fclose(vstats_file)) av_log(NULL, AV_LOG_ERROR, @@ -404,6 +411,10 @@ InputStream *ist_iter(InputStream *prev) static void frame_data_free(void *opaque, uint8_t *data) { + FrameData *fd = (FrameData *)data; + + avcodec_parameters_free(&fd->par_enc); + av_free(data); } @@ -430,6 +441,21 @@ static int frame_data_ensure(AVBufferRef **dst, int writable) const FrameData *fd_src = (const FrameData *)src->data; memcpy(fd, fd_src, sizeof(*fd)); + fd->par_enc = NULL; + + if (fd_src->par_enc) { + int ret = 0; + + fd->par_enc = avcodec_parameters_alloc(); + ret = fd->par_enc ? + avcodec_parameters_copy(fd->par_enc, fd_src->par_enc) : + AVERROR(ENOMEM); + if (ret < 0) { + av_buffer_unref(dst); + av_buffer_unref(&src); + return ret; + } + } av_buffer_unref(&src); } else { diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h index 1966817bc3..7454089c2d 100644 --- a/fftools/ffmpeg.h +++ b/fftools/ffmpeg.h @@ -331,6 +331,8 @@ typedef struct DecoderOpts { typedef struct Decoder { const AVClass *class; + enum AVMediaType type; + const uint8_t *subtitle_header; int subtitle_header_size; @@ -606,6 +608,8 @@ typedef struct FrameData { int bits_per_raw_sample; int64_t wallclock[LATENCY_PROBE_NB]; + + AVCodecParameters *par_enc; } FrameData; extern InputFile **input_files; @@ -617,6 +621,10 @@ extern int nb_output_files; extern FilterGraph **filtergraphs; extern int nb_filtergraphs; +// standalone decoders (not tied to demuxed streams) +extern Decoder **decoders; +extern int nb_decoders; + extern char *vstats_filename; extern float dts_delta_threshold; @@ -734,6 +742,11 @@ void hw_device_free_all(void); */ AVBufferRef *hw_device_for_filter(void); +/** + * Create a standalone decoder. + */ +int dec_create(const OptionsContext *o, const char *arg, Scheduler *sch); + /** * @param dec_opts Dictionary filled with decoder options. Its ownership * is transferred to the decoder. @@ -748,12 +761,21 @@ int dec_init(Decoder **pdec, Scheduler *sch, AVFrame *param_out); void dec_free(Decoder **pdec); +/* + * Called by filters to connect decoder's output to given filtergraph input. + * + * @param opts filtergraph input options, to be filled by this function + */ +int dec_filter_add(Decoder *dec, InputFilter *ifilter, InputFilterOptions *opts); + int enc_alloc(Encoder **penc, const AVCodec *codec, Scheduler *sch, unsigned sch_idx); void enc_free(Encoder **penc); int enc_open(void *opaque, const AVFrame *frame); +int enc_loopback(Encoder *enc); + /* * Initialize muxing state for the given stream, should be called * after the codec/streamcopy setup has been done. diff --git a/fftools/ffmpeg_dec.c b/fftools/ffmpeg_dec.c index 3bf7ab4960..c41c5748e5 100644 --- a/fftools/ffmpeg_dec.c +++ b/fftools/ffmpeg_dec.c @@ -17,6 +17,7 @@ */ #include "libavutil/avassert.h" +#include "libavutil/avstring.h" #include "libavutil/dict.h" #include "libavutil/error.h" #include "libavutil/log.h" @@ -71,9 +72,16 @@ typedef struct DecoderPriv { Scheduler *sch; unsigned sch_idx; + // this decoder's index in decoders or -1 + int index; void *log_parent; char log_name[32]; char *parent_name; + + struct { + AVDictionary *opts; + const AVCodec *codec; + } standalone_init; } DecoderPriv; static DecoderPriv *dp_from_dec(Decoder *d) @@ -101,6 +109,8 @@ void dec_free(Decoder **pdec) av_frame_free(&dp->frame); av_packet_free(&dp->pkt); + av_dict_free(&dp->standalone_init.opts); + for (int i = 0; i < FF_ARRAY_ELEMS(dp->sub_prev); i++) av_frame_free(&dp->sub_prev[i]); av_frame_free(&dp->sub_heartbeat); @@ -145,6 +155,7 @@ static int dec_alloc(DecoderPriv **pdec, Scheduler *sch, int send_end_ts) if (!dp->pkt) goto fail; + dp->index = -1; dp->dec.class = &dec_class; dp->last_filter_in_rescale_delta = AV_NOPTS_VALUE; dp->last_frame_pts = AV_NOPTS_VALUE; @@ -754,11 +765,56 @@ static int packet_decode(DecoderPriv *dp, AVPacket *pkt, AVFrame *frame) } } +static int dec_open(DecoderPriv *dp, AVDictionary **dec_opts, + const DecoderOpts *o, AVFrame *param_out); + +static int dec_standalone_open(DecoderPriv *dp, const AVPacket *pkt) +{ + DecoderOpts o; + const FrameData *fd; + char name[16]; + + if (!pkt->opaque_ref) + return AVERROR_BUG; + fd = (FrameData *)pkt->opaque_ref->data; + + if (!fd->par_enc) + return AVERROR_BUG; + + memset(&o, 0, sizeof(o)); + + o.par = fd->par_enc; + o.time_base = pkt->time_base; + + o.codec = dp->standalone_init.codec; + if (!o.codec) + o.codec = avcodec_find_decoder(o.par->codec_id); + if (!o.codec) { + const AVCodecDescriptor *desc = avcodec_descriptor_get(o.par->codec_id); + + av_log(dp, AV_LOG_ERROR, "Cannot find a decoder for codec ID '%s'\n", + desc ? desc->name : "?"); + return AVERROR_DECODER_NOT_FOUND; + } + + snprintf(name, sizeof(name), "dec%d", dp->index); + o.name = name; + + return dec_open(dp, &dp->standalone_init.opts, &o, NULL); +} + static void dec_thread_set_name(const DecoderPriv *dp) { - char name[16]; - snprintf(name, sizeof(name), "dec%s:%s", dp->parent_name, - dp->dec_ctx->codec->name); + char name[16] = "dec"; + + if (dp->index >= 0) + av_strlcatf(name, sizeof(name), "%d", dp->index); + else if (dp->parent_name) + av_strlcat(name, dp->parent_name, sizeof(name)); + + if (dp->dec_ctx) + av_strlcatf(name, sizeof(name), ":%s", dp->dec_ctx->codec->name); + ff_thread_setname(name); } @@ -814,6 +870,22 @@ static int decoder_thread(void *arg) av_log(dp, AV_LOG_VERBOSE, "Decoder thread received %s packet\n", flush_buffers ? "flush" : "EOF"); + // this is a standalone decoder that has not been initialized yet + if (!dp->dec_ctx) { + if (flush_buffers) + continue; + if (input_status < 0) { + av_log(dp, AV_LOG_ERROR, + "Cannot initialize a standalone decoder\n"); + ret = input_status; + goto finish; + } + + ret = dec_standalone_open(dp, dt.pkt); + if (ret < 0) + goto finish; + } + ret = packet_decode(dp, have_data ? dt.pkt : NULL, dt.frame); av_packet_unref(dt.pkt); @@ -1075,6 +1147,7 @@ static int dec_open(DecoderPriv *dp, AVDictionary **dec_opts, dp->flags = o->flags; dp->log_parent = o->log_parent; + dp->dec.type = codec->type; dp->framerate_in = o->framerate; dp->hwaccel_id = o->hwaccel_id; @@ -1188,3 +1261,88 @@ fail: dec_free((Decoder**)&dp); return ret; } + +int dec_create(const OptionsContext *o, const char *arg, Scheduler *sch) +{ + DecoderPriv *dp; + + OutputFile *of; + OutputStream *ost; + int of_index, ost_index; + char *p; + + unsigned enc_idx; + int ret; + + ret = dec_alloc(&dp, sch, 0); + if (ret < 0) + return ret; + + dp->index = nb_decoders; + + ret = GROW_ARRAY(decoders, nb_decoders); + if (ret < 0) { + dec_free((Decoder **)&dp); + return ret; + } + + decoders[nb_decoders - 1] = (Decoder *)dp; + + of_index = strtol(arg, &p, 0); + if (of_index < 0 || of_index >= nb_output_files) { + av_log(dp, AV_LOG_ERROR, "Invalid output file index '%d' in %s\n", of_index, arg); + return AVERROR(EINVAL); + } + of = output_files[of_index]; + + ost_index = strtol(p + 1, NULL, 0); + if (ost_index < 0 || ost_index >= of->nb_streams) { + av_log(dp, AV_LOG_ERROR, "Invalid output stream index '%d' in %s\n", ost_index, arg); + return AVERROR(EINVAL); + } + ost = of->streams[ost_index]; + + if (!ost->enc) { + av_log(dp, AV_LOG_ERROR, "Output stream %s has no encoder\n", arg); + return AVERROR(EINVAL); + } + + dp->dec.type = ost->type; + + ret = enc_loopback(ost->enc); + if (ret < 0) + return ret; + enc_idx = ret; + + ret = sch_connect(sch, SCH_ENC(enc_idx), SCH_DEC(dp->sch_idx)); + if (ret < 0) + return ret; + + ret = av_dict_copy(&dp->standalone_init.opts, o->g->codec_opts, 0); + if (ret < 0) + return ret; + + if (o->codec_names.nb_opt) { + const char *name = o->codec_names.opt[o->codec_names.nb_opt - 1].u.str; + dp->standalone_init.codec = avcodec_find_decoder_by_name(name); + if (!dp->standalone_init.codec) { + av_log(dp, AV_LOG_ERROR, "No such decoder: %s\n", name); + return AVERROR_DECODER_NOT_FOUND; + } + } + + return 0; +} + +int dec_filter_add(Decoder *d, InputFilter *ifilter, InputFilterOptions *opts) +{ + DecoderPriv *dp = dp_from_dec(d); + char name[16]; + + snprintf(name, sizeof(name), "dec%d", dp->index); + opts->name = av_strdup(name); + if (!opts->name) + return AVERROR(ENOMEM); + + return dp->sch_idx; +} diff --git a/fftools/ffmpeg_enc.c b/fftools/ffmpeg_enc.c index 88635629f7..d1dae558e1 100644 --- a/fftools/ffmpeg_enc.c +++ b/fftools/ffmpeg_enc.c @@ -49,6 +49,7 @@ struct Encoder { uint64_t packets_encoded; int opened; + int attach_par; Scheduler *sch; unsigned sch_idx; @@ -693,6 +694,18 @@ static int encode_frame(OutputFile *of, OutputStream *ost, AVFrame *frame, return AVERROR(ENOMEM); fd->wallclock[LATENCY_PROBE_ENC_POST] = av_gettime_relative(); + // attach stream parameters to first packet if requested + avcodec_parameters_free(&fd->par_enc); + if (e->attach_par && !e->packets_encoded) { + fd->par_enc = avcodec_parameters_alloc(); + if (!fd->par_enc) + return AVERROR(ENOMEM); + + ret = avcodec_parameters_from_context(fd->par_enc, enc); + if (ret < 0) + return ret; + } + if (enc->codec_type == AVMEDIA_TYPE_VIDEO) { ret = update_video_stats(ost, pkt, !!vstats_filename); if (ret < 0) @@ -929,3 +942,9 @@ finish: return ret; } + +int enc_loopback(Encoder *enc) +{ + enc->attach_par = 1; + return enc->sch_idx; +} diff --git a/fftools/ffmpeg_filter.c b/fftools/ffmpeg_filter.c index a29008387a..cf4f819927 100644 --- a/fftools/ffmpeg_filter.c +++ b/fftools/ffmpeg_filter.c @@ -710,6 +710,34 @@ static int ifilter_bind_ist(InputFilter *ifilter, InputStream *ist) return 0; } +static int ifilter_bind_dec(InputFilterPriv *ifp, Decoder *dec) +{ + FilterGraphPriv *fgp = fgp_from_fg(ifp->ifilter.graph); + int ret, dec_idx; + + av_assert0(!ifp->bound); + ifp->bound = 1; + + if (ifp->type != dec->type) { + av_log(fgp, AV_LOG_ERROR, "Tried to connect %s decoder to %s filtergraph input\n", + av_get_media_type_string(dec->type), av_get_media_type_string(ifp->type)); + return AVERROR(EINVAL); + } + + ifp->type_src = ifp->type; + + dec_idx = dec_filter_add(dec, &ifp->ifilter, &ifp->opts); + if (dec_idx < 0) + return dec_idx; + + ret = sch_connect(fgp->sch, SCH_DEC(dec_idx), + SCH_FILTER_IN(fgp->sch_idx, ifp->index)); + if (ret < 0) + return ret; + + return 0; +} + static int set_channel_layout(OutputFilterPriv *f, OutputStream *ost) { const AVCodec *c = ost->enc_ctx->codec; @@ -1114,7 +1142,24 @@ static int fg_complex_bind_input(FilterGraph *fg, InputFilter *ifilter) enum AVMediaType type = ifp->type; int i, ret; - if (ifp->linklabel) { + if (ifp->linklabel && !strncmp(ifp->linklabel, "dec", 3)) { + // bind to a standalone decoder + int dec_idx; + + dec_idx = strtol(ifp->linklabel + 3, NULL, 0); + if (dec_idx < 0 || dec_idx >= nb_decoders) { + av_log(fg, AV_LOG_ERROR, "Invalid decoder index %d in filtergraph description %s\n", + dec_idx, fgp->graph_desc); + return AVERROR(EINVAL); + } + + ret = ifilter_bind_dec(ifp, decoders[dec_idx]); + if (ret < 0) + av_log(fg, AV_LOG_ERROR, "Error binding a decoder to filtergraph input %s\n", + ifilter->name); + return ret; + } else if (ifp->linklabel) { + // bind to an explicitly specified demuxer stream AVFormatContext *s; AVStream *st = NULL; char *p; diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c index b550382b4c..6cc549ddb4 100644 --- a/fftools/ffmpeg_opt.c +++ b/fftools/ffmpeg_opt.c @@ -1177,11 +1177,13 @@ void show_usage(void) enum OptGroup { GROUP_OUTFILE, GROUP_INFILE, + GROUP_DECODER, }; static const OptionGroupDef groups[] = { [GROUP_OUTFILE] = { "output url", NULL, OPT_OUTPUT }, [GROUP_INFILE] = { "input url", "i", OPT_INPUT }, + [GROUP_DECODER] = { "loopback decoder", "dec", OPT_DECODER }, }; static int open_files(OptionGroupList *l, const char *inout, Scheduler *sch, @@ -1259,6 +1261,13 @@ int ffmpeg_parse_options(int argc, char **argv, Scheduler *sch) goto fail; } + /* create loopback decoders */ + ret = open_files(&octx.groups[GROUP_DECODER], "decoder", sch, dec_create); + if (ret < 0) { + errmsg = "creating loopback decoders"; + goto fail; + } + // bind unbound filtegraph inputs/outputs and check consistency for (int i = 0; i < nb_filtergraphs; i++) { ret = fg_finalise_bindings(filtergraphs[i]); @@ -1367,11 +1376,11 @@ const OptionDef options[] = { { "recast_media", OPT_TYPE_BOOL, OPT_EXPERT, { &recast_media }, "allow recasting stream type in order to force a decoder of different media type" }, - { "c", OPT_TYPE_STRING, OPT_PERSTREAM | OPT_INPUT | OPT_OUTPUT | OPT_HAS_CANON, + { "c", OPT_TYPE_STRING, OPT_PERSTREAM | OPT_INPUT | OPT_OUTPUT | OPT_DECODER | OPT_HAS_CANON, { .off = OFFSET(codec_names) }, "select encoder/decoder ('copy' to copy stream without reencoding)", "codec", .u1.name_canon = "codec", }, - { "codec", OPT_TYPE_STRING, OPT_PERSTREAM | OPT_INPUT | OPT_OUTPUT | OPT_EXPERT | OPT_HAS_ALT, + { "codec", OPT_TYPE_STRING, OPT_PERSTREAM | OPT_INPUT | OPT_OUTPUT | OPT_DECODER | OPT_EXPERT | OPT_HAS_ALT, { .off = OFFSET(codec_names) }, "alias for -c (select encoder/decoder)", "codec", .u1.names_alt = alt_codec, }, -- 2.43.0 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 38+ messages in thread
* [FFmpeg-devel] [PATCH v2 17/18] fftools/ffmpeg: add loopback decoding 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 17/18] fftools/ffmpeg: add loopback decoding Anton Khirnov @ 2024-03-07 14:42 ` Anton Khirnov 0 siblings, 0 replies; 38+ messages in thread From: Anton Khirnov @ 2024-03-07 14:42 UTC (permalink / raw) To: ffmpeg-devel This allows to send an encoder's output back to decoding and feed the result into a complex filtergraph. --- Now using [dec:X] instead of [decX] in filtergraph link labels, as suggested by Marvin on IRC. --- Changelog | 1 + doc/ffmpeg.texi | 49 ++++++++++-- fftools/cmdutils.c | 2 +- fftools/cmdutils.h | 7 +- fftools/ffmpeg.c | 26 +++++++ fftools/ffmpeg.h | 22 ++++++ fftools/ffmpeg_dec.c | 164 +++++++++++++++++++++++++++++++++++++++- fftools/ffmpeg_enc.c | 19 +++++ fftools/ffmpeg_filter.c | 47 +++++++++++- fftools/ffmpeg_opt.c | 13 +++- 10 files changed, 336 insertions(+), 14 deletions(-) diff --git a/Changelog b/Changelog index 0ba3a77c08..22186697fb 100644 --- a/Changelog +++ b/Changelog @@ -31,6 +31,7 @@ version <next>: - removed deprecated ffmpeg CLI options -psnr and -map_channel - DVD-Video demuxer, powered by libdvdnav and libdvdread - ffprobe -show_stream_groups option +- ffmpeg CLI loopback decoders version 6.1: diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi index bee3cd4c70..a38ef834e1 100644 --- a/doc/ffmpeg.texi +++ b/doc/ffmpeg.texi @@ -219,6 +219,40 @@ Since there is no decoding or encoding, it is very fast and there is no quality loss. However, it might not work in some cases because of many factors. Applying filters is obviously also impossible, since filters work on uncompressed data. +@section Loopback decoders +While decoders are normally associated with demuxer streams, it is also possible +to create "loopback" decoders that decode the output from some encoder and allow +it to be fed back to complex filtergraphs. This is done with the @code{-dec} +directive, which takes as a parameter the index of the output stream that should +be decoded. Every such directive creates a new loopback decoder, indexed with +successive integers starting at zero. These indices should then be used to refer +to loopback decoders in complex filtergraph link labels, as described in the +documentation for @option{-filter_complex}. + +E.g. the following example: + +@example +ffmpeg -i INPUT \ + -map 0:v:0 -c:v libx264 -crf 45 -f null - \ + -dec 0:0 -filter_complex '[0:v][dec:0]hstack[stack]' \ + -map '[stack]' -c:v ffv1 OUTPUT +@end example + +reads an input video and +@itemize +@item +(line 2) encodes it with @code{libx264} at low quality; + +@item +(line 3) decodes this encoded stream and places it side by side with the +original input video; + +@item +(line 4) combined video is then losslessly encoded and written into +@file{OUTPUT}. + +@end itemize + @c man end DETAILED DESCRIPTION @chapter Stream selection @@ -2105,11 +2139,16 @@ type -- see the @option{-filter} options. @var{filtergraph} is a description of the filtergraph, as described in the ``Filtergraph syntax'' section of the ffmpeg-filters manual. -Input link labels must refer to input streams using the -@code{[file_index:stream_specifier]} syntax (i.e. the same as @option{-map} -uses). If @var{stream_specifier} matches multiple streams, the first one will be -used. An unlabeled input will be connected to the first unused input stream of -the matching type. +Input link labels must refer to either input streams or loopback decoders. For +input streams, use the @code{[file_index:stream_specifier]} syntax (i.e. the +same as @option{-map} uses). If @var{stream_specifier} matches multiple streams, +the first one will be used. + +For decoders, the link label must be [dec:@var{dec_idx}], where @var{dec_idx} is +the index of the loopback decoder to be connected to given input. + +An unlabeled input will be connected to the first unused input stream of the +matching type. Output link labels are referred to with @option{-map}. Unlabeled outputs are added to the first output file. diff --git a/fftools/cmdutils.c b/fftools/cmdutils.c index 8efec942bd..f3c258bb99 100644 --- a/fftools/cmdutils.c +++ b/fftools/cmdutils.c @@ -528,7 +528,7 @@ static void check_options(const OptionDef *po) { while (po->name) { if (po->flags & OPT_PERFILE) - av_assert0(po->flags & (OPT_INPUT | OPT_OUTPUT)); + av_assert0(po->flags & (OPT_INPUT | OPT_OUTPUT | OPT_DECODER)); if (po->type == OPT_TYPE_FUNC) av_assert0(!(po->flags & (OPT_FLAG_OFFSET | OPT_FLAG_SPEC))); diff --git a/fftools/cmdutils.h b/fftools/cmdutils.h index 86428b3fa4..d0c773663b 100644 --- a/fftools/cmdutils.h +++ b/fftools/cmdutils.h @@ -144,8 +144,8 @@ typedef struct OptionDef { #define OPT_AUDIO (1 << 4) #define OPT_SUBTITLE (1 << 5) #define OPT_DATA (1 << 6) -/* The option is per-file (currently ffmpeg-only). At least one of OPT_INPUT or - * OPT_OUTPUT must be set when this flag is in use. +/* The option is per-file (currently ffmpeg-only). At least one of OPT_INPUT, + * OPT_OUTPUT, OPT_DECODER must be set when this flag is in use. */ #define OPT_PERFILE (1 << 7) @@ -175,6 +175,9 @@ typedef struct OptionDef { * name is stored in u1.name_canon */ #define OPT_HAS_CANON (1 << 14) +/* ffmpeg-only - OPT_PERFILE may apply to standalone decoders */ +#define OPT_DECODER (1 << 15) + union { void *dst_ptr; int (*func_arg)(void *, const char *, const char *); diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c index ffd25f4c5b..d4e5f978f1 100644 --- a/fftools/ffmpeg.c +++ b/fftools/ffmpeg.c @@ -131,6 +131,9 @@ int nb_output_files = 0; FilterGraph **filtergraphs; int nb_filtergraphs; +Decoder **decoders; +int nb_decoders; + #if HAVE_TERMIOS_H /* init terminal so that we can grab keys */ @@ -340,6 +343,10 @@ static void ffmpeg_cleanup(int ret) for (int i = 0; i < nb_input_files; i++) ifile_close(&input_files[i]); + for (int i = 0; i < nb_decoders; i++) + dec_free(&decoders[i]); + av_freep(&decoders); + if (vstats_file) { if (fclose(vstats_file)) av_log(NULL, AV_LOG_ERROR, @@ -404,6 +411,10 @@ InputStream *ist_iter(InputStream *prev) static void frame_data_free(void *opaque, uint8_t *data) { + FrameData *fd = (FrameData *)data; + + avcodec_parameters_free(&fd->par_enc); + av_free(data); } @@ -430,6 +441,21 @@ static int frame_data_ensure(AVBufferRef **dst, int writable) const FrameData *fd_src = (const FrameData *)src->data; memcpy(fd, fd_src, sizeof(*fd)); + fd->par_enc = NULL; + + if (fd_src->par_enc) { + int ret = 0; + + fd->par_enc = avcodec_parameters_alloc(); + ret = fd->par_enc ? + avcodec_parameters_copy(fd->par_enc, fd_src->par_enc) : + AVERROR(ENOMEM); + if (ret < 0) { + av_buffer_unref(dst); + av_buffer_unref(&src); + return ret; + } + } av_buffer_unref(&src); } else { diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h index 1966817bc3..7454089c2d 100644 --- a/fftools/ffmpeg.h +++ b/fftools/ffmpeg.h @@ -331,6 +331,8 @@ typedef struct DecoderOpts { typedef struct Decoder { const AVClass *class; + enum AVMediaType type; + const uint8_t *subtitle_header; int subtitle_header_size; @@ -606,6 +608,8 @@ typedef struct FrameData { int bits_per_raw_sample; int64_t wallclock[LATENCY_PROBE_NB]; + + AVCodecParameters *par_enc; } FrameData; extern InputFile **input_files; @@ -617,6 +621,10 @@ extern int nb_output_files; extern FilterGraph **filtergraphs; extern int nb_filtergraphs; +// standalone decoders (not tied to demuxed streams) +extern Decoder **decoders; +extern int nb_decoders; + extern char *vstats_filename; extern float dts_delta_threshold; @@ -734,6 +742,11 @@ void hw_device_free_all(void); */ AVBufferRef *hw_device_for_filter(void); +/** + * Create a standalone decoder. + */ +int dec_create(const OptionsContext *o, const char *arg, Scheduler *sch); + /** * @param dec_opts Dictionary filled with decoder options. Its ownership * is transferred to the decoder. @@ -748,12 +761,21 @@ int dec_init(Decoder **pdec, Scheduler *sch, AVFrame *param_out); void dec_free(Decoder **pdec); +/* + * Called by filters to connect decoder's output to given filtergraph input. + * + * @param opts filtergraph input options, to be filled by this function + */ +int dec_filter_add(Decoder *dec, InputFilter *ifilter, InputFilterOptions *opts); + int enc_alloc(Encoder **penc, const AVCodec *codec, Scheduler *sch, unsigned sch_idx); void enc_free(Encoder **penc); int enc_open(void *opaque, const AVFrame *frame); +int enc_loopback(Encoder *enc); + /* * Initialize muxing state for the given stream, should be called * after the codec/streamcopy setup has been done. diff --git a/fftools/ffmpeg_dec.c b/fftools/ffmpeg_dec.c index 3bf7ab4960..c41c5748e5 100644 --- a/fftools/ffmpeg_dec.c +++ b/fftools/ffmpeg_dec.c @@ -17,6 +17,7 @@ */ #include "libavutil/avassert.h" +#include "libavutil/avstring.h" #include "libavutil/dict.h" #include "libavutil/error.h" #include "libavutil/log.h" @@ -71,9 +72,16 @@ typedef struct DecoderPriv { Scheduler *sch; unsigned sch_idx; + // this decoder's index in decoders or -1 + int index; void *log_parent; char log_name[32]; char *parent_name; + + struct { + AVDictionary *opts; + const AVCodec *codec; + } standalone_init; } DecoderPriv; static DecoderPriv *dp_from_dec(Decoder *d) @@ -101,6 +109,8 @@ void dec_free(Decoder **pdec) av_frame_free(&dp->frame); av_packet_free(&dp->pkt); + av_dict_free(&dp->standalone_init.opts); + for (int i = 0; i < FF_ARRAY_ELEMS(dp->sub_prev); i++) av_frame_free(&dp->sub_prev[i]); av_frame_free(&dp->sub_heartbeat); @@ -145,6 +155,7 @@ static int dec_alloc(DecoderPriv **pdec, Scheduler *sch, int send_end_ts) if (!dp->pkt) goto fail; + dp->index = -1; dp->dec.class = &dec_class; dp->last_filter_in_rescale_delta = AV_NOPTS_VALUE; dp->last_frame_pts = AV_NOPTS_VALUE; @@ -754,11 +765,56 @@ static int packet_decode(DecoderPriv *dp, AVPacket *pkt, AVFrame *frame) } } +static int dec_open(DecoderPriv *dp, AVDictionary **dec_opts, + const DecoderOpts *o, AVFrame *param_out); + +static int dec_standalone_open(DecoderPriv *dp, const AVPacket *pkt) +{ + DecoderOpts o; + const FrameData *fd; + char name[16]; + + if (!pkt->opaque_ref) + return AVERROR_BUG; + fd = (FrameData *)pkt->opaque_ref->data; + + if (!fd->par_enc) + return AVERROR_BUG; + + memset(&o, 0, sizeof(o)); + + o.par = fd->par_enc; + o.time_base = pkt->time_base; + + o.codec = dp->standalone_init.codec; + if (!o.codec) + o.codec = avcodec_find_decoder(o.par->codec_id); + if (!o.codec) { + const AVCodecDescriptor *desc = avcodec_descriptor_get(o.par->codec_id); + + av_log(dp, AV_LOG_ERROR, "Cannot find a decoder for codec ID '%s'\n", + desc ? desc->name : "?"); + return AVERROR_DECODER_NOT_FOUND; + } + + snprintf(name, sizeof(name), "dec%d", dp->index); + o.name = name; + + return dec_open(dp, &dp->standalone_init.opts, &o, NULL); +} + static void dec_thread_set_name(const DecoderPriv *dp) { - char name[16]; - snprintf(name, sizeof(name), "dec%s:%s", dp->parent_name, - dp->dec_ctx->codec->name); + char name[16] = "dec"; + + if (dp->index >= 0) + av_strlcatf(name, sizeof(name), "%d", dp->index); + else if (dp->parent_name) + av_strlcat(name, dp->parent_name, sizeof(name)); + + if (dp->dec_ctx) + av_strlcatf(name, sizeof(name), ":%s", dp->dec_ctx->codec->name); + ff_thread_setname(name); } @@ -814,6 +870,22 @@ static int decoder_thread(void *arg) av_log(dp, AV_LOG_VERBOSE, "Decoder thread received %s packet\n", flush_buffers ? "flush" : "EOF"); + // this is a standalone decoder that has not been initialized yet + if (!dp->dec_ctx) { + if (flush_buffers) + continue; + if (input_status < 0) { + av_log(dp, AV_LOG_ERROR, + "Cannot initialize a standalone decoder\n"); + ret = input_status; + goto finish; + } + + ret = dec_standalone_open(dp, dt.pkt); + if (ret < 0) + goto finish; + } + ret = packet_decode(dp, have_data ? dt.pkt : NULL, dt.frame); av_packet_unref(dt.pkt); @@ -1075,6 +1147,7 @@ static int dec_open(DecoderPriv *dp, AVDictionary **dec_opts, dp->flags = o->flags; dp->log_parent = o->log_parent; + dp->dec.type = codec->type; dp->framerate_in = o->framerate; dp->hwaccel_id = o->hwaccel_id; @@ -1188,3 +1261,88 @@ fail: dec_free((Decoder**)&dp); return ret; } + +int dec_create(const OptionsContext *o, const char *arg, Scheduler *sch) +{ + DecoderPriv *dp; + + OutputFile *of; + OutputStream *ost; + int of_index, ost_index; + char *p; + + unsigned enc_idx; + int ret; + + ret = dec_alloc(&dp, sch, 0); + if (ret < 0) + return ret; + + dp->index = nb_decoders; + + ret = GROW_ARRAY(decoders, nb_decoders); + if (ret < 0) { + dec_free((Decoder **)&dp); + return ret; + } + + decoders[nb_decoders - 1] = (Decoder *)dp; + + of_index = strtol(arg, &p, 0); + if (of_index < 0 || of_index >= nb_output_files) { + av_log(dp, AV_LOG_ERROR, "Invalid output file index '%d' in %s\n", of_index, arg); + return AVERROR(EINVAL); + } + of = output_files[of_index]; + + ost_index = strtol(p + 1, NULL, 0); + if (ost_index < 0 || ost_index >= of->nb_streams) { + av_log(dp, AV_LOG_ERROR, "Invalid output stream index '%d' in %s\n", ost_index, arg); + return AVERROR(EINVAL); + } + ost = of->streams[ost_index]; + + if (!ost->enc) { + av_log(dp, AV_LOG_ERROR, "Output stream %s has no encoder\n", arg); + return AVERROR(EINVAL); + } + + dp->dec.type = ost->type; + + ret = enc_loopback(ost->enc); + if (ret < 0) + return ret; + enc_idx = ret; + + ret = sch_connect(sch, SCH_ENC(enc_idx), SCH_DEC(dp->sch_idx)); + if (ret < 0) + return ret; + + ret = av_dict_copy(&dp->standalone_init.opts, o->g->codec_opts, 0); + if (ret < 0) + return ret; + + if (o->codec_names.nb_opt) { + const char *name = o->codec_names.opt[o->codec_names.nb_opt - 1].u.str; + dp->standalone_init.codec = avcodec_find_decoder_by_name(name); + if (!dp->standalone_init.codec) { + av_log(dp, AV_LOG_ERROR, "No such decoder: %s\n", name); + return AVERROR_DECODER_NOT_FOUND; + } + } + + return 0; +} + +int dec_filter_add(Decoder *d, InputFilter *ifilter, InputFilterOptions *opts) +{ + DecoderPriv *dp = dp_from_dec(d); + char name[16]; + + snprintf(name, sizeof(name), "dec%d", dp->index); + opts->name = av_strdup(name); + if (!opts->name) + return AVERROR(ENOMEM); + + return dp->sch_idx; +} diff --git a/fftools/ffmpeg_enc.c b/fftools/ffmpeg_enc.c index 88635629f7..d1dae558e1 100644 --- a/fftools/ffmpeg_enc.c +++ b/fftools/ffmpeg_enc.c @@ -49,6 +49,7 @@ struct Encoder { uint64_t packets_encoded; int opened; + int attach_par; Scheduler *sch; unsigned sch_idx; @@ -693,6 +694,18 @@ static int encode_frame(OutputFile *of, OutputStream *ost, AVFrame *frame, return AVERROR(ENOMEM); fd->wallclock[LATENCY_PROBE_ENC_POST] = av_gettime_relative(); + // attach stream parameters to first packet if requested + avcodec_parameters_free(&fd->par_enc); + if (e->attach_par && !e->packets_encoded) { + fd->par_enc = avcodec_parameters_alloc(); + if (!fd->par_enc) + return AVERROR(ENOMEM); + + ret = avcodec_parameters_from_context(fd->par_enc, enc); + if (ret < 0) + return ret; + } + if (enc->codec_type == AVMEDIA_TYPE_VIDEO) { ret = update_video_stats(ost, pkt, !!vstats_filename); if (ret < 0) @@ -929,3 +942,9 @@ finish: return ret; } + +int enc_loopback(Encoder *enc) +{ + enc->attach_par = 1; + return enc->sch_idx; +} diff --git a/fftools/ffmpeg_filter.c b/fftools/ffmpeg_filter.c index a29008387a..47530f13a7 100644 --- a/fftools/ffmpeg_filter.c +++ b/fftools/ffmpeg_filter.c @@ -710,6 +710,34 @@ static int ifilter_bind_ist(InputFilter *ifilter, InputStream *ist) return 0; } +static int ifilter_bind_dec(InputFilterPriv *ifp, Decoder *dec) +{ + FilterGraphPriv *fgp = fgp_from_fg(ifp->ifilter.graph); + int ret, dec_idx; + + av_assert0(!ifp->bound); + ifp->bound = 1; + + if (ifp->type != dec->type) { + av_log(fgp, AV_LOG_ERROR, "Tried to connect %s decoder to %s filtergraph input\n", + av_get_media_type_string(dec->type), av_get_media_type_string(ifp->type)); + return AVERROR(EINVAL); + } + + ifp->type_src = ifp->type; + + dec_idx = dec_filter_add(dec, &ifp->ifilter, &ifp->opts); + if (dec_idx < 0) + return dec_idx; + + ret = sch_connect(fgp->sch, SCH_DEC(dec_idx), + SCH_FILTER_IN(fgp->sch_idx, ifp->index)); + if (ret < 0) + return ret; + + return 0; +} + static int set_channel_layout(OutputFilterPriv *f, OutputStream *ost) { const AVCodec *c = ost->enc_ctx->codec; @@ -1114,7 +1142,24 @@ static int fg_complex_bind_input(FilterGraph *fg, InputFilter *ifilter) enum AVMediaType type = ifp->type; int i, ret; - if (ifp->linklabel) { + if (ifp->linklabel && !strncmp(ifp->linklabel, "dec:", 4)) { + // bind to a standalone decoder + int dec_idx; + + dec_idx = strtol(ifp->linklabel + 4, NULL, 0); + if (dec_idx < 0 || dec_idx >= nb_decoders) { + av_log(fg, AV_LOG_ERROR, "Invalid decoder index %d in filtergraph description %s\n", + dec_idx, fgp->graph_desc); + return AVERROR(EINVAL); + } + + ret = ifilter_bind_dec(ifp, decoders[dec_idx]); + if (ret < 0) + av_log(fg, AV_LOG_ERROR, "Error binding a decoder to filtergraph input %s\n", + ifilter->name); + return ret; + } else if (ifp->linklabel) { + // bind to an explicitly specified demuxer stream AVFormatContext *s; AVStream *st = NULL; char *p; diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c index b550382b4c..6cc549ddb4 100644 --- a/fftools/ffmpeg_opt.c +++ b/fftools/ffmpeg_opt.c @@ -1177,11 +1177,13 @@ void show_usage(void) enum OptGroup { GROUP_OUTFILE, GROUP_INFILE, + GROUP_DECODER, }; static const OptionGroupDef groups[] = { [GROUP_OUTFILE] = { "output url", NULL, OPT_OUTPUT }, [GROUP_INFILE] = { "input url", "i", OPT_INPUT }, + [GROUP_DECODER] = { "loopback decoder", "dec", OPT_DECODER }, }; static int open_files(OptionGroupList *l, const char *inout, Scheduler *sch, @@ -1259,6 +1261,13 @@ int ffmpeg_parse_options(int argc, char **argv, Scheduler *sch) goto fail; } + /* create loopback decoders */ + ret = open_files(&octx.groups[GROUP_DECODER], "decoder", sch, dec_create); + if (ret < 0) { + errmsg = "creating loopback decoders"; + goto fail; + } + // bind unbound filtegraph inputs/outputs and check consistency for (int i = 0; i < nb_filtergraphs; i++) { ret = fg_finalise_bindings(filtergraphs[i]); @@ -1367,11 +1376,11 @@ const OptionDef options[] = { { "recast_media", OPT_TYPE_BOOL, OPT_EXPERT, { &recast_media }, "allow recasting stream type in order to force a decoder of different media type" }, - { "c", OPT_TYPE_STRING, OPT_PERSTREAM | OPT_INPUT | OPT_OUTPUT | OPT_HAS_CANON, + { "c", OPT_TYPE_STRING, OPT_PERSTREAM | OPT_INPUT | OPT_OUTPUT | OPT_DECODER | OPT_HAS_CANON, { .off = OFFSET(codec_names) }, "select encoder/decoder ('copy' to copy stream without reencoding)", "codec", .u1.name_canon = "codec", }, - { "codec", OPT_TYPE_STRING, OPT_PERSTREAM | OPT_INPUT | OPT_OUTPUT | OPT_EXPERT | OPT_HAS_ALT, + { "codec", OPT_TYPE_STRING, OPT_PERSTREAM | OPT_INPUT | OPT_OUTPUT | OPT_DECODER | OPT_EXPERT | OPT_HAS_ALT, { .off = OFFSET(codec_names) }, "alias for -c (select encoder/decoder)", "codec", .u1.names_alt = alt_codec, }, -- 2.43.0 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 38+ messages in thread
* [FFmpeg-devel] [PATCH 18/18] fftools/ffmpeg_enc: set AV_PKT_FLAG_TRUSTED on encoded packets 2024-03-06 11:03 [FFmpeg-devel] [PATCH 01/18] fftools/cmdutils: fix printing group name in split_commandline() Anton Khirnov ` (15 preceding siblings ...) 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 17/18] fftools/ffmpeg: add loopback decoding Anton Khirnov @ 2024-03-06 11:03 ` Anton Khirnov 2024-03-09 19:08 ` [FFmpeg-devel] [PATCH 01/18] fftools/cmdutils: fix printing group name in split_commandline() Anton Khirnov 17 siblings, 0 replies; 38+ messages in thread From: Anton Khirnov @ 2024-03-06 11:03 UTC (permalink / raw) To: ffmpeg-devel This allows using WRAPPED_AVFRAME encoders with loopback decoders in order to connect multiple filtergraphs together. Clear the flag in muxers, since lavf does not need it for anything and it would change the results of framecrc FATE tests. --- fftools/ffmpeg_enc.c | 2 ++ fftools/ffmpeg_mux.c | 1 + 2 files changed, 3 insertions(+) diff --git a/fftools/ffmpeg_enc.c b/fftools/ffmpeg_enc.c index d1dae558e1..c9a12af139 100644 --- a/fftools/ffmpeg_enc.c +++ b/fftools/ffmpeg_enc.c @@ -706,6 +706,8 @@ static int encode_frame(OutputFile *of, OutputStream *ost, AVFrame *frame, return ret; } + pkt->flags |= AV_PKT_FLAG_TRUSTED; + if (enc->codec_type == AVMEDIA_TYPE_VIDEO) { ret = update_video_stats(ost, pkt, !!vstats_filename); if (ret < 0) diff --git a/fftools/ffmpeg_mux.c b/fftools/ffmpeg_mux.c index 59befefab2..e8e5c677b8 100644 --- a/fftools/ffmpeg_mux.c +++ b/fftools/ffmpeg_mux.c @@ -433,6 +433,7 @@ int muxer_thread(void *arg) ost = of->streams[mux->sch_stream_idx[stream_idx]]; mt.pkt->stream_index = ost->index; + mt.pkt->flags &= ~AV_PKT_FLAG_TRUSTED; ret = mux_packet_filter(mux, &mt, ost, ret < 0 ? NULL : mt.pkt, &stream_eof); av_packet_unref(mt.pkt); -- 2.43.0 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 01/18] fftools/cmdutils: fix printing group name in split_commandline() 2024-03-06 11:03 [FFmpeg-devel] [PATCH 01/18] fftools/cmdutils: fix printing group name in split_commandline() Anton Khirnov ` (16 preceding siblings ...) 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 18/18] fftools/ffmpeg_enc: set AV_PKT_FLAG_TRUSTED on encoded packets Anton Khirnov @ 2024-03-09 19:08 ` Anton Khirnov 17 siblings, 0 replies; 38+ messages in thread From: Anton Khirnov @ 2024-03-09 19:08 UTC (permalink / raw) To: FFmpeg development discussions and patches Will push tomorrow if nobody has further comments. -- Anton Khirnov _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 38+ messages in thread
end of thread, other threads:[~2024-03-11 14:37 UTC | newest] Thread overview: 38+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2024-03-06 11:03 [FFmpeg-devel] [PATCH 01/18] fftools/cmdutils: fix printing group name in split_commandline() Anton Khirnov 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 02/18] fftools/ffmpeg_filter: refactor setting input timebase Anton Khirnov 2024-03-07 20:37 ` Michael Niedermayer 2024-03-08 5:34 ` Anton Khirnov 2024-03-10 3:36 ` Michael Niedermayer 2024-03-10 6:13 ` Anton Khirnov 2024-03-10 19:21 ` Michael Niedermayer 2024-03-10 22:24 ` Anton Khirnov 2024-03-10 22:29 ` James Almer 2024-03-10 22:49 ` Anton Khirnov 2024-03-10 23:37 ` Michael Niedermayer 2024-03-11 6:03 ` Anton Khirnov 2024-03-11 10:12 ` Tobias Rapp 2024-03-11 12:23 ` Anton Khirnov 2024-03-11 12:29 ` Martin Storsjö 2024-03-11 13:28 ` Anton Khirnov 2024-03-11 14:03 ` Martin Storsjö via ffmpeg-devel 2024-03-11 14:32 ` Anton Khirnov 2024-03-11 14:37 ` Martin Storsjö 2024-03-11 14:31 ` Tobias Rapp 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 03/18] fftools/ffmpeg_filter: drop unused InputFilterPriv.ist Anton Khirnov 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 04/18] fftools/ffmpeg_dec: move scheduler registration from dec_open() to dec_alloc() Anton Khirnov 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 05/18] fftools/ffmpeg_dec: factor opening the decoder out of dec_open() Anton Khirnov 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 06/18] fftools/ffmpeg_opt: merge init_complex_filters() and check_filter_outputs() Anton Khirnov 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 07/18] fftools/ffmpeg_filter: move filtergraph input type check to a better place Anton Khirnov 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 08/18] fftools/ffmpeg_filter: add logging for binding inputs to demuxer streams Anton Khirnov 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 09/18] fftools/ffmpeg: simplify propagating fallback parameters from decoders to filters Anton Khirnov 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 10/18] fftools/ffmpeg: remove unncessary casts for *_thread() return values Anton Khirnov 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 11/18] fftools/ffmpeg_enc: drop unnecessary parameter from forced_kf_apply() Anton Khirnov 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 12/18] fftools/ffmpeg_enc: merge do_{audio, video}_out into frame_encode() Anton Khirnov 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 13/18] fftools/ffmpeg_sched: allow encoders to send to multiple destinations Anton Khirnov 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 14/18] fftools/ffmpeg_sched: factor initializing nodes into separate function Anton Khirnov 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 15/18] fftools/ffmpeg_sched: allow connecting encoder output to decoders Anton Khirnov 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 16/18] fftools/ffmpeg: prepare FrameData for having allocated fields Anton Khirnov 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 17/18] fftools/ffmpeg: add loopback decoding Anton Khirnov 2024-03-07 14:42 ` [FFmpeg-devel] [PATCH v2 " Anton Khirnov 2024-03-06 11:03 ` [FFmpeg-devel] [PATCH 18/18] fftools/ffmpeg_enc: set AV_PKT_FLAG_TRUSTED on encoded packets Anton Khirnov 2024-03-09 19:08 ` [FFmpeg-devel] [PATCH 01/18] fftools/cmdutils: fix printing group name in split_commandline() Anton Khirnov
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel This inbox may be cloned and mirrored by anyone: git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git # If you have public-inbox 1.1+ installed, you may # initialize and index your mirror using the following commands: public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \ ffmpegdev@gitmailbox.com public-inbox-index ffmpegdev Example config snippet for mirrors. AGPL code for this site: git clone https://public-inbox.org/public-inbox.git