From 07fec85d814e9aa2f61a25ece9eb3cc5e2483b10 Mon Sep 17 00:00:00 2001
From: Stefano Sabatini <stefasab@gmail.com>
Date: Thu, 29 Nov 2012 13:45:50 +0100
Subject: [PATCH] lavf/segment: add reset_timestamps option
The new options reset the timestamps at each new segment, so that the
generated segments will have timestamps starting from 0.
It is meant to address trac ticket #1425.
---
doc/muxers.texi | 7 +++++++
libavformat/segment.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 61 insertions(+), 2 deletions(-)
diff --git a/doc/muxers.texi b/doc/muxers.texi
index 1c8f93b..be22298 100644
a
|
b
|
the specified time and the time set by @var{force_key_frames}. |
588 | 588 | @item segment_times @var{times} |
589 | 589 | Specify a list of split points. @var{times} contains a list of comma |
590 | 590 | separated duration specifications, in increasing order. |
| 591 | |
591 | 592 | @item segment_wrap @var{limit} |
592 | 593 | Wrap around segment index once it reaches @var{limit}. |
| 594 | |
| 595 | @item reset_timestamps @var{1|0} |
| 596 | Reset timestamps at the begin of each segment, so that each segment |
| 597 | will start with near-zero timestamps. It is meant to ease the playback |
| 598 | of the generated segments. May not work with some combinations of |
| 599 | muxers/codecs. It is set to @code{0} by default. |
593 | 600 | @end table |
594 | 601 | |
595 | 602 | Some examples follow. |
diff --git a/libavformat/segment.c b/libavformat/segment.c
index 1ad410e..7c8eb28 100644
a
|
b
|
|
35 | 35 | #include "libavutil/avstring.h" |
36 | 36 | #include "libavutil/parseutils.h" |
37 | 37 | #include "libavutil/mathematics.h" |
| 38 | #include "libavutil/timestamp.h" |
38 | 39 | |
39 | 40 | typedef enum { |
40 | 41 | LIST_TYPE_UNDEFINED = -1, |
… |
… |
typedef enum { |
49 | 50 | #define SEGMENT_LIST_FLAG_CACHE 1 |
50 | 51 | #define SEGMENT_LIST_FLAG_LIVE 2 |
51 | 52 | |
| 53 | struct stream_timestamps { |
| 54 | int64_t last_out_pts, last_out_dts, start_pts, start_dts; |
| 55 | }; |
| 56 | |
52 | 57 | typedef struct { |
53 | 58 | const AVClass *class; /**< Class for private options. */ |
54 | 59 | int segment_idx; ///< index of the segment file to write, starting from 0 |
… |
… |
typedef struct { |
72 | 77 | int64_t time_delta; |
73 | 78 | int individual_header_trailer; /**< Set by a private option. */ |
74 | 79 | int write_header_trailer; /**< Set by a private option. */ |
| 80 | |
| 81 | int reset_timestamps; ///< reset timestamps at the begin of each segment |
| 82 | struct stream_timestamps *timestamps; |
| 83 | int nb_timestamps; |
75 | 84 | int has_video; |
76 | 85 | double start_time, end_time; |
77 | 86 | } SegmentContext; |
… |
… |
static int segment_start(AVFormatContext *s, int write_header) |
131 | 140 | { |
132 | 141 | SegmentContext *seg = s->priv_data; |
133 | 142 | AVFormatContext *oc = seg->avf; |
134 | | int err = 0; |
| 143 | int err = 0, i; |
135 | 144 | |
136 | 145 | if (write_header) { |
137 | 146 | avformat_free_context(oc); |
… |
… |
static int segment_start(AVFormatContext *s, int write_header) |
163 | 172 | if ((err = avformat_write_header(oc, NULL)) < 0) |
164 | 173 | return err; |
165 | 174 | } |
| 175 | if (seg->reset_timestamps) |
| 176 | for (i = 0; i < seg->nb_timestamps; i++) |
| 177 | seg->timestamps[i].start_pts = seg->timestamps[i].start_dts = AV_NOPTS_VALUE; |
166 | 178 | |
167 | 179 | return 0; |
168 | 180 | } |
… |
… |
static int seg_write_header(AVFormatContext *s) |
379 | 391 | if (seg->list_type == LIST_TYPE_EXT) |
380 | 392 | av_log(s, AV_LOG_WARNING, "'ext' list type option is deprecated in favor of 'csv'\n"); |
381 | 393 | |
382 | | for (i = 0; i < s->nb_streams; i++) |
| 394 | seg->timestamps = av_malloc(sizeof(*seg->timestamps) * s->nb_streams); |
| 395 | if (!seg->timestamps) { |
| 396 | ret = AVERROR(ENOMEM); |
| 397 | goto fail; |
| 398 | } |
| 399 | seg->nb_timestamps = s->nb_streams; |
| 400 | |
| 401 | for (i = 0; i < seg->nb_timestamps; i++) { |
383 | 402 | seg->has_video += |
384 | 403 | (s->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO); |
| 404 | seg->timestamps[i].last_out_pts = seg->timestamps[i].start_pts = |
| 405 | seg->timestamps[i].last_out_dts = seg->timestamps[i].start_dts = AV_NOPTS_VALUE; |
| 406 | } |
385 | 407 | |
386 | 408 | if (seg->has_video > 1) |
387 | 409 | av_log(s, AV_LOG_WARNING, |
… |
… |
static int seg_write_packet(AVFormatContext *s, AVPacket *pkt) |
486 | 508 | (double)(pkt->pts + pkt->duration) * av_q2d(st->time_base)); |
487 | 509 | } |
488 | 510 | |
| 511 | if (seg->reset_timestamps) { |
| 512 | struct stream_timestamps *ts = &seg->timestamps[pkt->stream_index]; |
| 513 | |
| 514 | /* compute initial timestamps */ |
| 515 | if (ts->start_pts == AV_NOPTS_VALUE) |
| 516 | ts->start_pts = pkt->pts != AV_NOPTS_VALUE ? pkt->pts : ts->last_out_pts; |
| 517 | if (ts->start_dts == AV_NOPTS_VALUE) |
| 518 | ts->start_dts = pkt->dts != AV_NOPTS_VALUE ? pkt->dts : ts->last_out_dts; |
| 519 | |
| 520 | av_log(s, AV_LOG_DEBUG, "start_pts:%s pts:%s start_dts:%s dts:%s", |
| 521 | av_ts2str(ts->start_pts), av_ts2str(pkt->pts), av_ts2str(ts->start_dts), av_ts2str(pkt->dts)); |
| 522 | |
| 523 | /* compute last timestamps */ |
| 524 | if (pkt->pts != AV_NOPTS_VALUE) |
| 525 | ts->last_out_pts = pkt->pts + (pkt->duration <= 0 ? 1 : pkt->duration); |
| 526 | if (pkt->dts != AV_NOPTS_VALUE) |
| 527 | ts->last_out_dts = pkt->dts + (pkt->duration <= 0 ? 1 : pkt->duration); |
| 528 | |
| 529 | /* compute new timestamps */ |
| 530 | if (ts->start_pts != AV_NOPTS_VALUE && pkt->pts != AV_NOPTS_VALUE) |
| 531 | pkt->pts -= ts->start_pts; |
| 532 | if (ts->start_dts != AV_NOPTS_VALUE && pkt->dts != AV_NOPTS_VALUE) |
| 533 | pkt->dts -= ts->start_dts; |
| 534 | |
| 535 | av_log(s, AV_LOG_DEBUG, " -> pts:%s dts:%s\n", av_ts2str(pkt->pts), av_ts2str(pkt->dts)); |
| 536 | } |
| 537 | |
489 | 538 | ret = ff_write_chained(oc, pkt->stream_index, pkt, s); |
490 | 539 | |
491 | 540 | fail: |
… |
… |
fail: |
518 | 567 | |
519 | 568 | av_opt_free(seg); |
520 | 569 | av_freep(&seg->times); |
| 570 | av_freep(&seg->timestamps); |
521 | 571 | |
522 | 572 | avformat_free_context(oc); |
523 | 573 | return ret; |
… |
… |
static const AVOption options[] = { |
544 | 594 | { "segment_time_delta","set approximation value used for the segment times", OFFSET(time_delta_str), AV_OPT_TYPE_STRING, {.str = "0"}, 0, 0, E }, |
545 | 595 | { "segment_times", "set segment split time points", OFFSET(times_str),AV_OPT_TYPE_STRING,{.str = NULL}, 0, 0, E }, |
546 | 596 | { "segment_wrap", "set number after which the index wraps", OFFSET(segment_idx_wrap), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E }, |
| 597 | |
547 | 598 | { "individual_header_trailer", "write header/trailer to each segment", OFFSET(individual_header_trailer), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, E }, |
548 | 599 | { "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 }, |
| 600 | { "reset_timestamps", "reset timestamps at the begin of each segment", OFFSET(reset_timestamps), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, E }, |
549 | 601 | { NULL }, |
550 | 602 | }; |
551 | 603 | |