* commit '811bd0784679dfcb4ed02043a37c92f9df10500e':
avconv: make input -ss accurate when transcoding
Conflicts:
Changelog
doc/ffmpeg.texi
ffmpeg.h
ffmpeg_filter.c
Merged-by: Michael Niedermayer <michaelni@gmx.at>
... | ... |
@@ -8,6 +8,9 @@ version <next> |
8 | 8 |
- ffprobe -show_programs option |
9 | 9 |
- compand filter |
10 | 10 |
- RTMP seek support |
11 |
+- when transcoding with ffmpeg (i.e. not streamcopying), -ss is now accurate |
|
12 |
+ even when used as an input option. Previous behavior can be restored with |
|
13 |
+ the -noaccurate_seek option. |
|
11 | 14 |
|
12 | 15 |
|
13 | 16 |
version 2.0: |
... | ... |
@@ -272,9 +272,15 @@ Set the file size limit, expressed in bytes. |
272 | 272 |
|
273 | 273 |
@item -ss @var{position} (@emph{input/output}) |
274 | 274 |
When used as an input option (before @code{-i}), seeks in this input file to |
275 |
-@var{position}. When used as an output option (before an output filename), |
|
276 |
-decodes but discards input until the timestamps reach @var{position}. This is |
|
277 |
-slower, but more accurate. |
|
275 |
+@var{position}. Note the in most formats it is not possible to seek exactly, so |
|
276 |
+@command{ffmpeg} will seek to the closest seek point before @var{position}. |
|
277 |
+When transcoding and @option{-accurate_seek} is enabled (the default), this |
|
278 |
+extra segment between the seek point and @var{position} will be decoded and |
|
279 |
+discarded. When doing stream copy or when @option{-noaccurate_seek} is used, it |
|
280 |
+will be preserved. |
|
281 |
+ |
|
282 |
+When used as an output option (before an output filename), decodes but discards |
|
283 |
+input until the timestamps reach @var{position}. |
|
278 | 284 |
|
279 | 285 |
@var{position} may be either in seconds or in @code{hh:mm:ss[.xxx]} form. |
280 | 286 |
|
... | ... |
@@ -1060,6 +1066,12 @@ This option is similar to @option{-filter_complex}, the only difference is that |
1060 | 1060 |
its argument is the name of the file from which a complex filtergraph |
1061 | 1061 |
description is to be read. |
1062 | 1062 |
|
1063 |
+@item -accurate_seek (@emph{input}) |
|
1064 |
+This option enables or disables accurate seeking in input files with the |
|
1065 |
+@option{-ss} option. It is enabled by default, so seeking is accurate when |
|
1066 |
+transcoding. Use @option{-noaccurate_seek} to disable it, which may be useful |
|
1067 |
+e.g. when copying some streams and transcoding the others. |
|
1068 |
+ |
|
1063 | 1069 |
@item -override_ffserver (@emph{global}) |
1064 | 1070 |
Overrides the input specifications from ffserver. Using this option you can |
1065 | 1071 |
map any input stream to ffserver and control many aspects of the encoding from |
... | ... |
@@ -93,6 +93,7 @@ typedef struct OptionsContext { |
93 | 93 |
/* input options */ |
94 | 94 |
int64_t input_ts_offset; |
95 | 95 |
int rate_emu; |
96 |
+ int accurate_seek; |
|
96 | 97 |
|
97 | 98 |
SpecifierOpt *ts_scale; |
98 | 99 |
int nb_ts_scale; |
... | ... |
@@ -282,10 +283,12 @@ typedef struct InputFile { |
282 | 282 |
int ist_index; /* index of first stream in input_streams */ |
283 | 283 |
int64_t ts_offset; |
284 | 284 |
int64_t last_ts; |
285 |
+ int64_t start_time; /* user-specified start time in AV_TIME_BASE or AV_NOPTS_VALUE */ |
|
285 | 286 |
int nb_streams; /* number of stream that ffmpeg is aware of; may be different |
286 | 287 |
from ctx.nb_streams if new streams appear during av_read_frame() */ |
287 | 288 |
int nb_streams_warn; /* number of streams that the user was warned of */ |
288 | 289 |
int rate_emu; |
290 |
+ int accurate_seek; |
|
289 | 291 |
|
290 | 292 |
#if HAVE_PTHREADS |
291 | 293 |
pthread_t thread; /* thread reading from this file */ |
... | ... |
@@ -276,21 +276,22 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in) |
276 | 276 |
ist->filters[ist->nb_filters - 1] = fg->inputs[fg->nb_inputs - 1]; |
277 | 277 |
} |
278 | 278 |
|
279 |
-static int insert_trim(OutputStream *ost, AVFilterContext **last_filter, int *pad_idx) |
|
279 |
+static int insert_trim(int64_t start_time, int64_t duration, |
|
280 |
+ AVFilterContext **last_filter, int *pad_idx, |
|
281 |
+ const char *filter_name) |
|
280 | 282 |
{ |
281 |
- OutputFile *of = output_files[ost->file_index]; |
|
282 | 283 |
AVFilterGraph *graph = (*last_filter)->graph; |
283 | 284 |
AVFilterContext *ctx; |
284 | 285 |
const AVFilter *trim; |
285 |
- const char *name = ost->st->codec->codec_type == AVMEDIA_TYPE_VIDEO ? "trim" : "atrim"; |
|
286 |
- char filter_name[128]; |
|
286 |
+ enum AVMediaType type = avfilter_pad_get_type((*last_filter)->output_pads, *pad_idx); |
|
287 |
+ const char *name = (type == AVMEDIA_TYPE_VIDEO) ? "trim" : "atrim"; |
|
287 | 288 |
int ret = 0; |
288 | 289 |
|
289 |
- if (of->recording_time == INT64_MAX && of->start_time == AV_NOPTS_VALUE) |
|
290 |
+ if (duration == INT64_MAX && start_time == AV_NOPTS_VALUE) |
|
290 | 291 |
return 0; |
291 | 292 |
|
292 | 293 |
// Use with duration and without output starttime is buggy with trim filters |
293 |
- if (of->start_time == AV_NOPTS_VALUE) |
|
294 |
+ if (start_time == AV_NOPTS_VALUE) |
|
294 | 295 |
return 0; |
295 | 296 |
|
296 | 297 |
trim = avfilter_get_by_name(name); |
... | ... |
@@ -300,18 +301,16 @@ static int insert_trim(OutputStream *ost, AVFilterContext **last_filter, int *pa |
300 | 300 |
return AVERROR_FILTER_NOT_FOUND; |
301 | 301 |
} |
302 | 302 |
|
303 |
- snprintf(filter_name, sizeof(filter_name), "%s for output stream %d:%d", |
|
304 |
- name, ost->file_index, ost->index); |
|
305 | 303 |
ctx = avfilter_graph_alloc_filter(graph, trim, filter_name); |
306 | 304 |
if (!ctx) |
307 | 305 |
return AVERROR(ENOMEM); |
308 | 306 |
|
309 |
- if (of->recording_time != INT64_MAX) { |
|
310 |
- ret = av_opt_set_double(ctx, "duration", (double)of->recording_time / 1e6, |
|
307 |
+ if (duration != INT64_MAX) { |
|
308 |
+ ret = av_opt_set_double(ctx, "duration", (double)duration / 1e6, |
|
311 | 309 |
AV_OPT_SEARCH_CHILDREN); |
312 | 310 |
} |
313 |
- if (ret >= 0 && of->start_time != AV_NOPTS_VALUE) { |
|
314 |
- ret = av_opt_set_double(ctx, "start", (double)of->start_time / 1e6, |
|
311 |
+ if (ret >= 0 && start_time != AV_NOPTS_VALUE) { |
|
312 |
+ ret = av_opt_set_double(ctx, "start", (double)start_time / 1e6, |
|
315 | 313 |
AV_OPT_SEARCH_CHILDREN); |
316 | 314 |
} |
317 | 315 |
if (ret < 0) { |
... | ... |
@@ -336,6 +335,7 @@ static int configure_output_video_filter(FilterGraph *fg, OutputFilter *ofilter, |
336 | 336 |
{ |
337 | 337 |
char *pix_fmts; |
338 | 338 |
OutputStream *ost = ofilter->ost; |
339 |
+ OutputFile *of = output_files[ost->file_index]; |
|
339 | 340 |
AVCodecContext *codec = ost->st->codec; |
340 | 341 |
AVFilterContext *last_filter = out->filter_ctx; |
341 | 342 |
int pad_idx = out->pad_idx; |
... | ... |
@@ -408,7 +408,10 @@ static int configure_output_video_filter(FilterGraph *fg, OutputFilter *ofilter, |
408 | 408 |
pad_idx = 0; |
409 | 409 |
} |
410 | 410 |
|
411 |
- ret = insert_trim(ost, &last_filter, &pad_idx); |
|
411 |
+ snprintf(name, sizeof(name), "trim for output stream %d:%d", |
|
412 |
+ ost->file_index, ost->index); |
|
413 |
+ ret = insert_trim(of->start_time, of->recording_time, |
|
414 |
+ &last_filter, &pad_idx, name); |
|
412 | 415 |
if (ret < 0) |
413 | 416 |
return ret; |
414 | 417 |
|
... | ... |
@@ -422,9 +425,9 @@ static int configure_output_video_filter(FilterGraph *fg, OutputFilter *ofilter, |
422 | 422 |
static int configure_output_audio_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out) |
423 | 423 |
{ |
424 | 424 |
OutputStream *ost = ofilter->ost; |
425 |
+ OutputFile *of = output_files[ost->file_index]; |
|
425 | 426 |
AVCodecContext *codec = ost->st->codec; |
426 | 427 |
AVFilterContext *last_filter = out->filter_ctx; |
427 |
- OutputFile *of = output_files[ost->file_index]; |
|
428 | 428 |
int pad_idx = out->pad_idx; |
429 | 429 |
char *sample_fmts, *sample_rates, *channel_layouts; |
430 | 430 |
char name[255]; |
... | ... |
@@ -534,7 +537,10 @@ static int configure_output_audio_filter(FilterGraph *fg, OutputFilter *ofilter, |
534 | 534 |
} |
535 | 535 |
} |
536 | 536 |
|
537 |
- ret = insert_trim(ost, &last_filter, &pad_idx); |
|
537 |
+ snprintf(name, sizeof(name), "trim for output stream %d:%d", |
|
538 |
+ ost->file_index, ost->index); |
|
539 |
+ ret = insert_trim(of->start_time, of->recording_time, |
|
540 |
+ &last_filter, &pad_idx, name); |
|
538 | 541 |
if (ret < 0) |
539 | 542 |
return ret; |
540 | 543 |
|
... | ... |
@@ -615,13 +621,14 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter, |
615 | 615 |
AVFilterContext *last_filter; |
616 | 616 |
const AVFilter *buffer_filt = avfilter_get_by_name("buffer"); |
617 | 617 |
InputStream *ist = ifilter->ist; |
618 |
+ InputFile *f = input_files[ist->file_index]; |
|
618 | 619 |
AVRational tb = ist->framerate.num ? av_inv_q(ist->framerate) : |
619 | 620 |
ist->st->time_base; |
620 | 621 |
AVRational fr = ist->framerate; |
621 | 622 |
AVRational sar; |
622 | 623 |
AVBPrint args; |
623 | 624 |
char name[255]; |
624 |
- int ret; |
|
625 |
+ int ret, pad_idx = 0; |
|
625 | 626 |
|
626 | 627 |
if (!fr.num) |
627 | 628 |
fr = av_guess_frame_rate(input_files[ist->file_index]->ctx, ist->st, NULL); |
... | ... |
@@ -688,6 +695,13 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter, |
688 | 688 |
last_filter = yadif; |
689 | 689 |
} |
690 | 690 |
|
691 |
+ snprintf(name, sizeof(name), "trim for input stream %d:%d", |
|
692 |
+ ist->file_index, ist->st->index); |
|
693 |
+ ret = insert_trim(((f->start_time == AV_NOPTS_VALUE) || !f->accurate_seek) ? |
|
694 |
+ AV_NOPTS_VALUE : 0, INT64_MAX, &last_filter, &pad_idx, name); |
|
695 |
+ if (ret < 0) |
|
696 |
+ return ret; |
|
697 |
+ |
|
691 | 698 |
if ((ret = avfilter_link(last_filter, 0, in->filter_ctx, in->pad_idx)) < 0) |
692 | 699 |
return ret; |
693 | 700 |
return 0; |
... | ... |
@@ -699,9 +713,10 @@ static int configure_input_audio_filter(FilterGraph *fg, InputFilter *ifilter, |
699 | 699 |
AVFilterContext *last_filter; |
700 | 700 |
const AVFilter *abuffer_filt = avfilter_get_by_name("abuffer"); |
701 | 701 |
InputStream *ist = ifilter->ist; |
702 |
+ InputFile *f = input_files[ist->file_index]; |
|
702 | 703 |
AVBPrint args; |
703 | 704 |
char name[255]; |
704 |
- int ret; |
|
705 |
+ int ret, pad_idx = 0; |
|
705 | 706 |
|
706 | 707 |
av_bprint_init(&args, 0, AV_BPRINT_SIZE_AUTOMATIC); |
707 | 708 |
av_bprintf(&args, "time_base=%d/%d:sample_rate=%d:sample_fmt=%s", |
... | ... |
@@ -776,6 +791,14 @@ static int configure_input_audio_filter(FilterGraph *fg, InputFilter *ifilter, |
776 | 776 |
snprintf(args, sizeof(args), "%f", audio_volume / 256.); |
777 | 777 |
AUTO_INSERT_FILTER_INPUT("-vol", "volume", args); |
778 | 778 |
} |
779 |
+ |
|
780 |
+ snprintf(name, sizeof(name), "trim for input stream %d:%d", |
|
781 |
+ ist->file_index, ist->st->index); |
|
782 |
+ ret = insert_trim(((f->start_time == AV_NOPTS_VALUE) || !f->accurate_seek) ? |
|
783 |
+ AV_NOPTS_VALUE : 0, INT64_MAX, &last_filter, &pad_idx, name); |
|
784 |
+ if (ret < 0) |
|
785 |
+ return ret; |
|
786 |
+ |
|
779 | 787 |
if ((ret = avfilter_link(last_filter, 0, in->filter_ctx, in->pad_idx)) < 0) |
780 | 788 |
return ret; |
781 | 789 |
|
... | ... |
@@ -150,6 +150,7 @@ static void init_options(OptionsContext *o, int is_input) |
150 | 150 |
o->start_time = AV_NOPTS_VALUE; |
151 | 151 |
o->limit_filesize = UINT64_MAX; |
152 | 152 |
o->chapters_input_file = INT_MAX; |
153 |
+ o->accurate_seek = 1; |
|
153 | 154 |
} |
154 | 155 |
|
155 | 156 |
/* return a copy of the input with the stream specifiers removed from the keys */ |
... | ... |
@@ -853,9 +854,11 @@ static int open_input_file(OptionsContext *o, const char *filename) |
853 | 853 |
|
854 | 854 |
f->ctx = ic; |
855 | 855 |
f->ist_index = nb_input_streams - ic->nb_streams; |
856 |
+ f->start_time = o->start_time; |
|
856 | 857 |
f->ts_offset = o->input_ts_offset - (copy_ts ? 0 : timestamp); |
857 | 858 |
f->nb_streams = ic->nb_streams; |
858 | 859 |
f->rate_emu = o->rate_emu; |
860 |
+ f->accurate_seek = o->accurate_seek; |
|
859 | 861 |
|
860 | 862 |
/* check if all codec options have been used */ |
861 | 863 |
unused_opts = strip_specifiers(o->g->codec_opts); |
... | ... |
@@ -2619,6 +2622,9 @@ const OptionDef options[] = { |
2619 | 2619 |
{ "ss", HAS_ARG | OPT_TIME | OPT_OFFSET | |
2620 | 2620 |
OPT_INPUT | OPT_OUTPUT, { .off = OFFSET(start_time) }, |
2621 | 2621 |
"set the start time offset", "time_off" }, |
2622 |
+ { "accurate_seek", OPT_BOOL | OPT_OFFSET | OPT_EXPERT | |
|
2623 |
+ OPT_INPUT, { .off = OFFSET(accurate_seek) }, |
|
2624 |
+ "enable/disable accurate seeking with -ss" }, |
|
2622 | 2625 |
{ "itsoffset", HAS_ARG | OPT_TIME | OPT_OFFSET | |
2623 | 2626 |
OPT_EXPERT | OPT_INPUT, { .off = OFFSET(input_ts_offset) }, |
2624 | 2627 |
"set the input ts offset", "time_off" }, |