Message ID | 20220714081613.173-1-ffmpeg@gyani.pro |
---|---|
State | New |
Headers | show |
Series | [FFmpeg-devel,v4] ffmpeg: add option -isync | expand |
Context | Check | Description |
---|---|---|
andriy/make_x86 | success | Make finished |
andriy/make_fate_x86 | success | Make fate finished |
Pushed as 882aac99d2a7d15492ce1da9859676b0c04295b8 and cherry-picked to 5.1 On 2022-07-14 01:46 pm, Gyan Doshi wrote: > This is a per-file input option that adjusts an input's timestamps > with reference to another input, so that emitted packet timestamps > account for the difference between the start times of the two inputs. > > Typical use case is to sync two or more live inputs such as from capture > devices. Both the target and reference input source timestamps should be > based on the same clock source. > > If either input lacks starting timestamps, then no sync adjustment is made. > --- > v4 change: fallback on reception timestamp removed. > > doc/ffmpeg.texi | 15 +++++++++++ > fftools/ffmpeg.h | 2 ++ > fftools/ffmpeg_opt.c | 60 ++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 77 insertions(+) > > diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi > index 1a534ff1cc..767df69b7f 100644 > --- a/doc/ffmpeg.texi > +++ b/doc/ffmpeg.texi > @@ -518,6 +518,21 @@ see @ref{time duration syntax,,the Time duration section in the ffmpeg-utils(1) > Like the @code{-ss} option but relative to the "end of file". That is negative > values are earlier in the file, 0 is at EOF. > > +@item -isync @var{input_index} (@emph{input}) > +Assign an input as a sync source. > + > +This will take the difference between the start times of the target and reference inputs and > +offset the timestamps of the target file by that difference. The source timestamps of the two > +inputs should derive from the same clock source for expected results. If @code{copyts} is set > +then @code{start_at_zero} must also be set. If either of the inputs has no starting timestamp > +then no sync adjustment is made. > + > +Acceptable values are those that refer to a valid ffmpeg input index. If the sync reference is > +the target index itself or @var{-1}, then no adjustment is made to target timestamps. A sync > +reference may not itself be synced to any other input. > + > +Default value is @var{-1}. > + > @item -itsoffset @var{offset} (@emph{input}) > Set the input time offset. > > diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h > index 99d31c346e..391a35cf50 100644 > --- a/fftools/ffmpeg.h > +++ b/fftools/ffmpeg.h > @@ -118,6 +118,7 @@ typedef struct OptionsContext { > float readrate; > int accurate_seek; > int thread_queue_size; > + int input_sync_ref; > > SpecifierOpt *ts_scale; > int nb_ts_scale; > @@ -410,6 +411,7 @@ typedef struct InputFile { > at the moment when looping happens */ > AVRational time_base; /* time base of the duration */ > int64_t input_ts_offset; > + int input_sync_ref; > > int64_t ts_offset; > int64_t last_ts; > diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c > index e08455478f..359d8f60b8 100644 > --- a/fftools/ffmpeg_opt.c > +++ b/fftools/ffmpeg_opt.c > @@ -235,6 +235,7 @@ static void init_options(OptionsContext *o) > o->chapters_input_file = INT_MAX; > o->accurate_seek = 1; > o->thread_queue_size = -1; > + o->input_sync_ref = -1; > } > > static int show_hwaccels(void *optctx, const char *opt, const char *arg) > @@ -287,6 +288,59 @@ static int parse_and_set_vsync(const char *arg, int *vsync_var, int file_idx, in > return 0; > } > > +static int apply_sync_offsets(void) > +{ > + for (int i = 0; i < nb_input_files; i++) { > + InputFile *ref, *self = input_files[i]; > + int64_t adjustment; > + int64_t self_start_time, ref_start_time, self_seek_start, ref_seek_start; > + int start_times_set = 1; > + > + if (self->input_sync_ref == -1 || self->input_sync_ref == i) continue; > + if (self->input_sync_ref >= nb_input_files || self->input_sync_ref < -1) { > + av_log(NULL, AV_LOG_FATAL, "-isync for input %d references non-existent input %d.\n", i, self->input_sync_ref); > + exit_program(1); > + } > + > + if (copy_ts && !start_at_zero) { > + av_log(NULL, AV_LOG_FATAL, "Use of -isync requires that start_at_zero be set if copyts is set.\n"); > + exit_program(1); > + } > + > + ref = input_files[self->input_sync_ref]; > + if (ref->input_sync_ref != -1 && ref->input_sync_ref != self->input_sync_ref) { > + av_log(NULL, AV_LOG_ERROR, "-isync for input %d references a resynced input %d. Sync not set.\n", i, self->input_sync_ref); > + continue; > + } > + > + if (self->ctx->start_time_realtime != AV_NOPTS_VALUE && ref->ctx->start_time_realtime != AV_NOPTS_VALUE) { > + self_start_time = self->ctx->start_time_realtime; > + ref_start_time = ref->ctx->start_time_realtime; > + } else if (self->ctx->start_time != AV_NOPTS_VALUE && ref->ctx->start_time != AV_NOPTS_VALUE) { > + self_start_time = self->ctx->start_time; > + ref_start_time = ref->ctx->start_time; > + } else { > + start_times_set = 0; > + } > + > + if (start_times_set) { > + self_seek_start = self->start_time == AV_NOPTS_VALUE ? 0 : self->start_time; > + ref_seek_start = ref->start_time == AV_NOPTS_VALUE ? 0 : ref->start_time; > + > + adjustment = (self_start_time - ref_start_time) + !copy_ts*(self_seek_start - ref_seek_start) + ref->input_ts_offset; > + > + self->ts_offset += adjustment; > + > + av_log(NULL, AV_LOG_INFO, "Adjusted ts offset for Input #%d by %"PRId64" us to sync with Input #%d.\n", i, adjustment, self->input_sync_ref); > + } else { > + av_log(NULL, AV_LOG_INFO, "Unable to identify start times for Inputs #%d and %d both. No sync adjustment made.\n", > + i, self->input_sync_ref); > + } > + } > + > + return 0; > +} > + > static int opt_filter_threads(void *optctx, const char *opt, const char *arg) > { > av_free(filter_nbthreads); > @@ -1305,6 +1359,7 @@ static int open_input_file(OptionsContext *o, const char *filename) > f->ist_index = nb_input_streams - ic->nb_streams; > f->start_time = o->start_time; > f->recording_time = o->recording_time; > + f->input_sync_ref = o->input_sync_ref; > f->input_ts_offset = o->input_ts_offset; > f->ts_offset = o->input_ts_offset - (copy_ts ? (start_at_zero && ic->start_time != AV_NOPTS_VALUE ? ic->start_time : 0) : timestamp); > f->nb_streams = ic->nb_streams; > @@ -3489,6 +3544,8 @@ int ffmpeg_parse_options(int argc, char **argv) > goto fail; > } > > + apply_sync_offsets(); > + > /* create the complex filtergraphs */ > ret = init_complex_filters(); > if (ret < 0) { > @@ -3603,6 +3660,9 @@ const OptionDef options[] = { > { "accurate_seek", OPT_BOOL | OPT_OFFSET | OPT_EXPERT | > OPT_INPUT, { .off = OFFSET(accurate_seek) }, > "enable/disable accurate seeking with -ss" }, > + { "isync", HAS_ARG | OPT_INT | OPT_OFFSET | > + OPT_EXPERT | OPT_INPUT, { .off = OFFSET(input_sync_ref) }, > + "Indicate the input index for sync reference", "sync ref" }, > { "itsoffset", HAS_ARG | OPT_TIME | OPT_OFFSET | > OPT_EXPERT | OPT_INPUT, { .off = OFFSET(input_ts_offset) }, > "set the input ts offset", "time_off" },
On Thu, Jul 14, 2022 at 12:21 PM Gyan Doshi <ffmpeg@gyani.pro> wrote: > Pushed as 882aac99d2a7d15492ce1da9859676b0c04295b8 > and cherry-picked to 5.1 > Why? > > On 2022-07-14 01:46 pm, Gyan Doshi wrote: > > This is a per-file input option that adjusts an input's timestamps > > with reference to another input, so that emitted packet timestamps > > account for the difference between the start times of the two inputs. > > > > Typical use case is to sync two or more live inputs such as from capture > > devices. Both the target and reference input source timestamps should be > > based on the same clock source. > > > > If either input lacks starting timestamps, then no sync adjustment is > made. > > --- > > v4 change: fallback on reception timestamp removed. > > > > doc/ffmpeg.texi | 15 +++++++++++ > > fftools/ffmpeg.h | 2 ++ > > fftools/ffmpeg_opt.c | 60 ++++++++++++++++++++++++++++++++++++++++++++ > > 3 files changed, 77 insertions(+) > > > > diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi > > index 1a534ff1cc..767df69b7f 100644 > > --- a/doc/ffmpeg.texi > > +++ b/doc/ffmpeg.texi > > @@ -518,6 +518,21 @@ see @ref{time duration syntax,,the Time duration > section in the ffmpeg-utils(1) > > Like the @code{-ss} option but relative to the "end of file". That is > negative > > values are earlier in the file, 0 is at EOF. > > > > +@item -isync @var{input_index} (@emph{input}) > > +Assign an input as a sync source. > > + > > +This will take the difference between the start times of the target and > reference inputs and > > +offset the timestamps of the target file by that difference. The source > timestamps of the two > > +inputs should derive from the same clock source for expected results. > If @code{copyts} is set > > +then @code{start_at_zero} must also be set. If either of the inputs has > no starting timestamp > > +then no sync adjustment is made. > > + > > +Acceptable values are those that refer to a valid ffmpeg input index. > If the sync reference is > > +the target index itself or @var{-1}, then no adjustment is made to > target timestamps. A sync > > +reference may not itself be synced to any other input. > > + > > +Default value is @var{-1}. > > + > > @item -itsoffset @var{offset} (@emph{input}) > > Set the input time offset. > > > > diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h > > index 99d31c346e..391a35cf50 100644 > > --- a/fftools/ffmpeg.h > > +++ b/fftools/ffmpeg.h > > @@ -118,6 +118,7 @@ typedef struct OptionsContext { > > float readrate; > > int accurate_seek; > > int thread_queue_size; > > + int input_sync_ref; > > > > SpecifierOpt *ts_scale; > > int nb_ts_scale; > > @@ -410,6 +411,7 @@ typedef struct InputFile { > > at the moment when looping happens */ > > AVRational time_base; /* time base of the duration */ > > int64_t input_ts_offset; > > + int input_sync_ref; > > > > int64_t ts_offset; > > int64_t last_ts; > > diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c > > index e08455478f..359d8f60b8 100644 > > --- a/fftools/ffmpeg_opt.c > > +++ b/fftools/ffmpeg_opt.c > > @@ -235,6 +235,7 @@ static void init_options(OptionsContext *o) > > o->chapters_input_file = INT_MAX; > > o->accurate_seek = 1; > > o->thread_queue_size = -1; > > + o->input_sync_ref = -1; > > } > > > > static int show_hwaccels(void *optctx, const char *opt, const char > *arg) > > @@ -287,6 +288,59 @@ static int parse_and_set_vsync(const char *arg, int > *vsync_var, int file_idx, in > > return 0; > > } > > > > +static int apply_sync_offsets(void) > > +{ > > + for (int i = 0; i < nb_input_files; i++) { > > + InputFile *ref, *self = input_files[i]; > > + int64_t adjustment; > > + int64_t self_start_time, ref_start_time, self_seek_start, > ref_seek_start; > > + int start_times_set = 1; > > + > > + if (self->input_sync_ref == -1 || self->input_sync_ref == i) > continue; > > + if (self->input_sync_ref >= nb_input_files || > self->input_sync_ref < -1) { > > + av_log(NULL, AV_LOG_FATAL, "-isync for input %d references > non-existent input %d.\n", i, self->input_sync_ref); > > + exit_program(1); > > + } > > + > > + if (copy_ts && !start_at_zero) { > > + av_log(NULL, AV_LOG_FATAL, "Use of -isync requires that > start_at_zero be set if copyts is set.\n"); > > + exit_program(1); > > + } > > + > > + ref = input_files[self->input_sync_ref]; > > + if (ref->input_sync_ref != -1 && ref->input_sync_ref != > self->input_sync_ref) { > > + av_log(NULL, AV_LOG_ERROR, "-isync for input %d references > a resynced input %d. Sync not set.\n", i, self->input_sync_ref); > > + continue; > > + } > > + > > + if (self->ctx->start_time_realtime != AV_NOPTS_VALUE && > ref->ctx->start_time_realtime != AV_NOPTS_VALUE) { > > + self_start_time = self->ctx->start_time_realtime; > > + ref_start_time = ref->ctx->start_time_realtime; > > + } else if (self->ctx->start_time != AV_NOPTS_VALUE && > ref->ctx->start_time != AV_NOPTS_VALUE) { > > + self_start_time = self->ctx->start_time; > > + ref_start_time = ref->ctx->start_time; > > + } else { > > + start_times_set = 0; > > + } > > + > > + if (start_times_set) { > > + self_seek_start = self->start_time == AV_NOPTS_VALUE ? 0 : > self->start_time; > > + ref_seek_start = ref->start_time == AV_NOPTS_VALUE ? 0 : > ref->start_time; > > + > > + adjustment = (self_start_time - ref_start_time) + > !copy_ts*(self_seek_start - ref_seek_start) + ref->input_ts_offset; > > + > > + self->ts_offset += adjustment; > > + > > + av_log(NULL, AV_LOG_INFO, "Adjusted ts offset for Input #%d > by %"PRId64" us to sync with Input #%d.\n", i, adjustment, > self->input_sync_ref); > > + } else { > > + av_log(NULL, AV_LOG_INFO, "Unable to identify start times > for Inputs #%d and %d both. No sync adjustment made.\n", > > + i, self->input_sync_ref); > > + } > > + } > > + > > + return 0; > > +} > > + > > static int opt_filter_threads(void *optctx, const char *opt, const > char *arg) > > { > > av_free(filter_nbthreads); > > @@ -1305,6 +1359,7 @@ static int open_input_file(OptionsContext *o, > const char *filename) > > f->ist_index = nb_input_streams - ic->nb_streams; > > f->start_time = o->start_time; > > f->recording_time = o->recording_time; > > + f->input_sync_ref = o->input_sync_ref; > > f->input_ts_offset = o->input_ts_offset; > > f->ts_offset = o->input_ts_offset - (copy_ts ? (start_at_zero && > ic->start_time != AV_NOPTS_VALUE ? ic->start_time : 0) : timestamp); > > f->nb_streams = ic->nb_streams; > > @@ -3489,6 +3544,8 @@ int ffmpeg_parse_options(int argc, char **argv) > > goto fail; > > } > > > > + apply_sync_offsets(); > > + > > /* create the complex filtergraphs */ > > ret = init_complex_filters(); > > if (ret < 0) { > > @@ -3603,6 +3660,9 @@ const OptionDef options[] = { > > { "accurate_seek", OPT_BOOL | OPT_OFFSET | OPT_EXPERT | > > OPT_INPUT, { > .off = OFFSET(accurate_seek) }, > > "enable/disable accurate seeking with -ss" }, > > + { "isync", HAS_ARG | OPT_INT | OPT_OFFSET | > > + OPT_EXPERT | OPT_INPUT, { > .off = OFFSET(input_sync_ref) }, > > + "Indicate the input index for sync reference", "sync ref" }, > > { "itsoffset", HAS_ARG | OPT_TIME | OPT_OFFSET | > > OPT_EXPERT | OPT_INPUT, { > .off = OFFSET(input_ts_offset) }, > > "set the input ts offset", "time_off" }, > > _______________________________________________ > 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". >
On 2022-07-14 04:01 pm, Paul B Mahol wrote: > On Thu, Jul 14, 2022 at 12:21 PM Gyan Doshi <ffmpeg@gyani.pro> wrote: > >> Pushed as 882aac99d2a7d15492ce1da9859676b0c04295b8 >> and cherry-picked to 5.1 >> > Why? The cherry-pick? Because 5.1 hasn't been tagged yet as already explained in my last msg in v3 thread. Regards, Gyan
diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi index 1a534ff1cc..767df69b7f 100644 --- a/doc/ffmpeg.texi +++ b/doc/ffmpeg.texi @@ -518,6 +518,21 @@ see @ref{time duration syntax,,the Time duration section in the ffmpeg-utils(1) Like the @code{-ss} option but relative to the "end of file". That is negative values are earlier in the file, 0 is at EOF. +@item -isync @var{input_index} (@emph{input}) +Assign an input as a sync source. + +This will take the difference between the start times of the target and reference inputs and +offset the timestamps of the target file by that difference. The source timestamps of the two +inputs should derive from the same clock source for expected results. If @code{copyts} is set +then @code{start_at_zero} must also be set. If either of the inputs has no starting timestamp +then no sync adjustment is made. + +Acceptable values are those that refer to a valid ffmpeg input index. If the sync reference is +the target index itself or @var{-1}, then no adjustment is made to target timestamps. A sync +reference may not itself be synced to any other input. + +Default value is @var{-1}. + @item -itsoffset @var{offset} (@emph{input}) Set the input time offset. diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h index 99d31c346e..391a35cf50 100644 --- a/fftools/ffmpeg.h +++ b/fftools/ffmpeg.h @@ -118,6 +118,7 @@ typedef struct OptionsContext { float readrate; int accurate_seek; int thread_queue_size; + int input_sync_ref; SpecifierOpt *ts_scale; int nb_ts_scale; @@ -410,6 +411,7 @@ typedef struct InputFile { at the moment when looping happens */ AVRational time_base; /* time base of the duration */ int64_t input_ts_offset; + int input_sync_ref; int64_t ts_offset; int64_t last_ts; diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c index e08455478f..359d8f60b8 100644 --- a/fftools/ffmpeg_opt.c +++ b/fftools/ffmpeg_opt.c @@ -235,6 +235,7 @@ static void init_options(OptionsContext *o) o->chapters_input_file = INT_MAX; o->accurate_seek = 1; o->thread_queue_size = -1; + o->input_sync_ref = -1; } static int show_hwaccels(void *optctx, const char *opt, const char *arg) @@ -287,6 +288,59 @@ static int parse_and_set_vsync(const char *arg, int *vsync_var, int file_idx, in return 0; } +static int apply_sync_offsets(void) +{ + for (int i = 0; i < nb_input_files; i++) { + InputFile *ref, *self = input_files[i]; + int64_t adjustment; + int64_t self_start_time, ref_start_time, self_seek_start, ref_seek_start; + int start_times_set = 1; + + if (self->input_sync_ref == -1 || self->input_sync_ref == i) continue; + if (self->input_sync_ref >= nb_input_files || self->input_sync_ref < -1) { + av_log(NULL, AV_LOG_FATAL, "-isync for input %d references non-existent input %d.\n", i, self->input_sync_ref); + exit_program(1); + } + + if (copy_ts && !start_at_zero) { + av_log(NULL, AV_LOG_FATAL, "Use of -isync requires that start_at_zero be set if copyts is set.\n"); + exit_program(1); + } + + ref = input_files[self->input_sync_ref]; + if (ref->input_sync_ref != -1 && ref->input_sync_ref != self->input_sync_ref) { + av_log(NULL, AV_LOG_ERROR, "-isync for input %d references a resynced input %d. Sync not set.\n", i, self->input_sync_ref); + continue; + } + + if (self->ctx->start_time_realtime != AV_NOPTS_VALUE && ref->ctx->start_time_realtime != AV_NOPTS_VALUE) { + self_start_time = self->ctx->start_time_realtime; + ref_start_time = ref->ctx->start_time_realtime; + } else if (self->ctx->start_time != AV_NOPTS_VALUE && ref->ctx->start_time != AV_NOPTS_VALUE) { + self_start_time = self->ctx->start_time; + ref_start_time = ref->ctx->start_time; + } else { + start_times_set = 0; + } + + if (start_times_set) { + self_seek_start = self->start_time == AV_NOPTS_VALUE ? 0 : self->start_time; + ref_seek_start = ref->start_time == AV_NOPTS_VALUE ? 0 : ref->start_time; + + adjustment = (self_start_time - ref_start_time) + !copy_ts*(self_seek_start - ref_seek_start) + ref->input_ts_offset; + + self->ts_offset += adjustment; + + av_log(NULL, AV_LOG_INFO, "Adjusted ts offset for Input #%d by %"PRId64" us to sync with Input #%d.\n", i, adjustment, self->input_sync_ref); + } else { + av_log(NULL, AV_LOG_INFO, "Unable to identify start times for Inputs #%d and %d both. No sync adjustment made.\n", + i, self->input_sync_ref); + } + } + + return 0; +} + static int opt_filter_threads(void *optctx, const char *opt, const char *arg) { av_free(filter_nbthreads); @@ -1305,6 +1359,7 @@ static int open_input_file(OptionsContext *o, const char *filename) f->ist_index = nb_input_streams - ic->nb_streams; f->start_time = o->start_time; f->recording_time = o->recording_time; + f->input_sync_ref = o->input_sync_ref; f->input_ts_offset = o->input_ts_offset; f->ts_offset = o->input_ts_offset - (copy_ts ? (start_at_zero && ic->start_time != AV_NOPTS_VALUE ? ic->start_time : 0) : timestamp); f->nb_streams = ic->nb_streams; @@ -3489,6 +3544,8 @@ int ffmpeg_parse_options(int argc, char **argv) goto fail; } + apply_sync_offsets(); + /* create the complex filtergraphs */ ret = init_complex_filters(); if (ret < 0) { @@ -3603,6 +3660,9 @@ const OptionDef options[] = { { "accurate_seek", OPT_BOOL | OPT_OFFSET | OPT_EXPERT | OPT_INPUT, { .off = OFFSET(accurate_seek) }, "enable/disable accurate seeking with -ss" }, + { "isync", HAS_ARG | OPT_INT | OPT_OFFSET | + OPT_EXPERT | OPT_INPUT, { .off = OFFSET(input_sync_ref) }, + "Indicate the input index for sync reference", "sync ref" }, { "itsoffset", HAS_ARG | OPT_TIME | OPT_OFFSET | OPT_EXPERT | OPT_INPUT, { .off = OFFSET(input_ts_offset) }, "set the input ts offset", "time_off" },