[FFmpeg-devel] [PATCH v2 2/2] ffmpeg: add option -isync

Gyan Doshi ffmpeg at gyani.pro
Mon Jun 27 16:25:48 EEST 2022


Ping for the set.

On 2022-06-25 01:59 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 not all inputs have timestamps, the wallclock times at the time of
> reception of inputs shall be used.
> ---
>   doc/ffmpeg.texi      | 16 ++++++++++++
>   fftools/ffmpeg.h     |  2 ++
>   fftools/ffmpeg_opt.c | 59 ++++++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 77 insertions(+)
>
> diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi
> index d943f4d6f5..8fc85d3a15 100644
> --- a/doc/ffmpeg.texi
> +++ b/doc/ffmpeg.texi
> @@ -518,6 +518,22 @@ 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.
>   
> + at 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 referenced 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 at least one of the inputs has no starting
> +timestamp then the wallclock time at time of reception of the inputs is used as a best-effort
> +sync basis.
> +
> +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 69a368b8d1..dc74de6684 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..de858efbe9 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,58 @@ 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 sync_fpw = 0;
> +
> +        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 {
> +            self_start_time = self->ctx->first_pkt_wallclock;
> +            ref_start_time  =  ref->ctx->first_pkt_wallclock;
> +            sync_fpw = 1;
> +        }
> +
> +        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"d us to sync with Input #%d", i, adjustment, self->input_sync_ref);
> +        if (sync_fpw) av_log(NULL, AV_LOG_INFO, " using reception wallclock time. Sync may not be obtained");
> +        av_log(NULL, AV_LOG_INFO, ".\n");
> +    }
> +
> +    return 0;
> +}
> +
>   static int opt_filter_threads(void *optctx, const char *opt, const char *arg)
>   {
>       av_free(filter_nbthreads);
> @@ -1305,6 +1358,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 +3543,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 +3659,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" },



More information about the ffmpeg-devel mailing list