The new options reset the timestamps at each new segment, so that the
generated segments will have timestamps starting close to 0.
It is meant to address trac ticket #1425.
... | ... |
@@ -598,8 +598,15 @@ the specified time and the time set by @var{force_key_frames}. |
598 | 598 |
@item segment_times @var{times} |
599 | 599 |
Specify a list of split points. @var{times} contains a list of comma |
600 | 600 |
separated duration specifications, in increasing order. |
601 |
+ |
|
601 | 602 |
@item segment_wrap @var{limit} |
602 | 603 |
Wrap around segment index once it reaches @var{limit}. |
604 |
+ |
|
605 |
+@item reset_timestamps @var{1|0} |
|
606 |
+Reset timestamps at the begin of each segment, so that each segment |
|
607 |
+will start with near-zero timestamps. It is meant to ease the playback |
|
608 |
+of the generated segments. May not work with some combinations of |
|
609 |
+muxers/codecs. It is set to @code{0} by default. |
|
603 | 610 |
@end table |
604 | 611 |
|
605 | 612 |
Some examples follow. |
... | ... |
@@ -34,6 +34,7 @@ |
34 | 34 |
#include "libavutil/avstring.h" |
35 | 35 |
#include "libavutil/parseutils.h" |
36 | 36 |
#include "libavutil/mathematics.h" |
37 |
+#include "libavutil/timestamp.h" |
|
37 | 38 |
|
38 | 39 |
typedef enum { |
39 | 40 |
LIST_TYPE_UNDEFINED = -1, |
... | ... |
@@ -71,8 +72,11 @@ typedef struct { |
71 | 71 |
int64_t time_delta; |
72 | 72 |
int individual_header_trailer; /**< Set by a private option. */ |
73 | 73 |
int write_header_trailer; /**< Set by a private option. */ |
74 |
+ |
|
75 |
+ int reset_timestamps; ///< reset timestamps at the begin of each segment |
|
74 | 76 |
int has_video; |
75 | 77 |
double start_time, end_time; |
78 |
+ int64_t start_pts, start_dts; |
|
76 | 79 |
} SegmentContext; |
77 | 80 |
|
78 | 81 |
static void print_csv_escaped_str(AVIOContext *ctx, const char *str) |
... | ... |
@@ -467,6 +471,7 @@ static int seg_write_packet(AVFormatContext *s, AVPacket *pkt) |
467 | 467 |
|
468 | 468 |
/* if the segment has video, start a new segment *only* with a key video frame */ |
469 | 469 |
if ((st->codec->codec_type == AVMEDIA_TYPE_VIDEO || !seg->has_video) && |
470 |
+ pkt->pts != AV_NOPTS_VALUE && |
|
470 | 471 |
av_compare_ts(pkt->pts, st->time_base, |
471 | 472 |
end_pts-seg->time_delta, AV_TIME_BASE_Q) >= 0 && |
472 | 473 |
pkt->flags & AV_PKT_FLAG_KEY) { |
... | ... |
@@ -485,11 +490,29 @@ static int seg_write_packet(AVFormatContext *s, AVPacket *pkt) |
485 | 485 |
oc = seg->avf; |
486 | 486 |
|
487 | 487 |
seg->start_time = (double)pkt->pts * av_q2d(st->time_base); |
488 |
+ seg->start_pts = av_rescale_q(pkt->pts, st->time_base, AV_TIME_BASE_Q); |
|
489 |
+ seg->start_dts = pkt->dts != AV_NOPTS_VALUE ? |
|
490 |
+ av_rescale_q(pkt->dts, st->time_base, AV_TIME_BASE_Q) : seg->start_pts; |
|
488 | 491 |
} else if (pkt->pts != AV_NOPTS_VALUE) { |
489 | 492 |
seg->end_time = FFMAX(seg->end_time, |
490 | 493 |
(double)(pkt->pts + pkt->duration) * av_q2d(st->time_base)); |
491 | 494 |
} |
492 | 495 |
|
496 |
+ if (seg->reset_timestamps) { |
|
497 |
+ av_log(s, AV_LOG_DEBUG, "start_pts:%s pts:%s start_dts:%s dts:%s", |
|
498 |
+ av_ts2timestr(seg->start_pts, &AV_TIME_BASE_Q), av_ts2timestr(pkt->pts, &st->time_base), |
|
499 |
+ av_ts2timestr(seg->start_dts, &AV_TIME_BASE_Q), av_ts2timestr(pkt->dts, &st->time_base)); |
|
500 |
+ |
|
501 |
+ /* compute new timestamps */ |
|
502 |
+ if (pkt->pts != AV_NOPTS_VALUE) |
|
503 |
+ pkt->pts -= av_rescale_q(seg->start_pts, AV_TIME_BASE_Q, st->time_base); |
|
504 |
+ if (pkt->dts != AV_NOPTS_VALUE) |
|
505 |
+ pkt->dts -= av_rescale_q(seg->start_dts, AV_TIME_BASE_Q, st->time_base); |
|
506 |
+ |
|
507 |
+ av_log(s, AV_LOG_DEBUG, " -> pts:%s dts:%s\n", |
|
508 |
+ av_ts2timestr(pkt->pts, &st->time_base), av_ts2timestr(pkt->dts, &st->time_base)); |
|
509 |
+ } |
|
510 |
+ |
|
493 | 511 |
ret = ff_write_chained(oc, pkt->stream_index, pkt, s); |
494 | 512 |
|
495 | 513 |
fail: |
... | ... |
@@ -548,8 +571,10 @@ static const AVOption options[] = { |
548 | 548 |
{ "segment_time_delta","set approximation value used for the segment times", OFFSET(time_delta_str), AV_OPT_TYPE_STRING, {.str = "0"}, 0, 0, E }, |
549 | 549 |
{ "segment_times", "set segment split time points", OFFSET(times_str),AV_OPT_TYPE_STRING,{.str = NULL}, 0, 0, E }, |
550 | 550 |
{ "segment_wrap", "set number after which the index wraps", OFFSET(segment_idx_wrap), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E }, |
551 |
+ |
|
551 | 552 |
{ "individual_header_trailer", "write header/trailer to each segment", OFFSET(individual_header_trailer), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, E }, |
552 | 553 |
{ "write_header_trailer", "write a header to the first segment and a trailer to the last one", OFFSET(write_header_trailer), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, E }, |
554 |
+ { "reset_timestamps", "reset timestamps at the begin of each segment", OFFSET(reset_timestamps), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, E }, |
|
553 | 555 |
{ NULL }, |
554 | 556 |
}; |
555 | 557 |
|
... | ... |
@@ -31,7 +31,7 @@ |
31 | 31 |
|
32 | 32 |
#define LIBAVFORMAT_VERSION_MAJOR 54 |
33 | 33 |
#define LIBAVFORMAT_VERSION_MINOR 49 |
34 |
-#define LIBAVFORMAT_VERSION_MICRO 100 |
|
34 |
+#define LIBAVFORMAT_VERSION_MICRO 101 |
|
35 | 35 |
|
36 | 36 |
#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ |
37 | 37 |
LIBAVFORMAT_VERSION_MINOR, \ |