[14/14] TEST/EXAMPLE ONLY: dashenc: Allow skipping writing some segments

Message ID 1419862828-30060-14-git-send-email-martin@martin.st
State Deferred
Headers show

Commit Message

Martin Storsjö Dec. 29, 2014, 2:20 p.m.
To show how to handle random-access writing, allow skipping writing
earlier segments. No packets for the skipped segments are sent to
the actual mp4 muxer.
---
I don't think this really should be committed, but it does serve as
an example on how to use the frag_discont/stream_start_times options,
and for verifying that the later segment files produced in this setup
are identical to the ones created in a normal setup.
---
 libavformat/dashenc.c | 26 +++++++++++++++++++++++---
 1 file changed, 23 insertions(+), 3 deletions(-)

Comments

Derek Buitenhuis Dec. 29, 2014, 5:24 p.m. | #1
On 12/29/2014 2:20 PM, Martin Storsjö wrote:
> To show how to handle random-access writing, allow skipping writing
> earlier segments. No packets for the skipped segments are sent to
> the actual mp4 muxer.

[...]

> I don't think this really should be committed, but it does serve as
> an example on how to use the frag_discont/stream_start_times options,
> and for verifying that the later segment files produced in this setup
> are identical to the ones created in a normal setup.

A good place, I think, to have such an example, is in the offline
documentation for the movenc options, in a code block. I am not sure
how much of a pain in the ass that is, though. We really don't have a
good place for arbitrary example code, do we? (The example encoders and
stuff are super generic... I mean a place for little blocks of useful
example code.)

- Derek

Patch

diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c
index 0b9570d..0cce6f5 100644
--- a/libavformat/dashenc.c
+++ b/libavformat/dashenc.c
@@ -94,6 +94,7 @@  typedef struct DASHContext {
     const char *init_seg_name;
     const char *media_seg_name;
     int start_segment;
+    int skip_segments;
 } DASHContext;
 
 static int dash_write(void *opaque, uint8_t *buf, int buf_size)
@@ -546,7 +547,8 @@  static int dash_write_header(AVFormatContext *s)
      * timestamps back to zero, just make them nonnegative. The fragment
      * timestamp offset written in the tfdt atoms assume that the stream
      * starts (in the fragments that haven't been written) at dts=0. */
-    if (c->start_segment > 1 && s->avoid_negative_ts == AVFMT_AVOID_NEG_TS_AUTO)
+    if (c->start_segment > 1 &&
+        s->avoid_negative_ts == AVFMT_AVOID_NEG_TS_AUTO)
         s->avoid_negative_ts = AVFMT_AVOID_NEG_TS_MAKE_NON_NEGATIVE;
 
     av_strlcpy(c->dirname, s->filename, sizeof(c->dirname));
@@ -634,8 +636,10 @@  static int dash_write_header(AVFormatContext *s)
             goto fail;
         os->init_start_pos = 0;
 
-        if (c->start_segment > 1) {
+        if (c->start_segment > 1 || c->skip_segments > 0) {
             av_dict_set(&opts, "movflags", "frag_custom+dash+delay_moov+frag_discont", 0);
+            if (c->start_segment == 1)
+                av_dict_set(&opts, "use_editlist", "1", 0);
         } else {
             av_dict_set(&opts, "movflags", "frag_custom+dash+delay_moov", 0);
             av_dict_set(&opts, "use_editlist", "1", 0);
@@ -665,6 +669,10 @@  static int dash_write_header(AVFormatContext *s)
         os->segment_index = c->start_segment;
     }
 
+    // Set os->segment_index to start as usual, but set start_segment
+    // to match the first number that actually is written.
+    c->start_segment = FFMAX(c->skip_segments + 1, c->start_segment);
+
     if (!c->has_video && c->min_seg_duration <= 0) {
         av_log(s, AV_LOG_WARNING, "no video stream and no min seg duration set\n");
         ret = AVERROR(EINVAL);
@@ -772,6 +780,11 @@  static int dash_flush(AVFormatContext *s, int final, int stream)
             if (c->has_video && os->segment_index > cur_flush_segment_index)
                 continue;
         }
+        if (os->segment_index <= c->skip_segments) {
+            os->packets_written = 0;
+            os->segment_index++;
+            continue;
+        }
 
         if (!os->init_range_length) {
             av_write_frame(os->ctx, NULL);
@@ -856,8 +869,12 @@  static int dash_write_packet(AVFormatContext *s, AVPacket *pkt)
         pkt->dts  = 0;
     }
 
-    if (os->first_pts == AV_NOPTS_VALUE)
+    if (os->first_pts == AV_NOPTS_VALUE) {
+        char buf[100];
         os->first_pts = pkt->pts;
+        snprintf(buf, sizeof(buf), "%"PRId64",%"PRId64, pkt->dts, pkt->pts);
+        av_opt_set(os->ctx, "stream_start_times", buf, AV_OPT_SEARCH_CHILDREN);
+    }
 
     if ((!c->has_video || st->codec->codec_type == AVMEDIA_TYPE_VIDEO) &&
         pkt->flags & AV_PKT_FLAG_KEY && os->packets_written &&
@@ -899,6 +916,8 @@  static int dash_write_packet(AVFormatContext *s, AVPacket *pkt)
     else
         os->max_pts = FFMAX(os->max_pts, pkt->pts + pkt->duration);
     os->packets_written++;
+    if (os->segment_index <= c->skip_segments)
+        return 0;
     return ff_write_chained(os->ctx, 0, pkt, s);
 }
 
@@ -949,6 +968,7 @@  static const AVOption options[] = {
     { "init_seg_name", "DASH-templated name to used for the initialization segment", OFFSET(init_seg_name), AV_OPT_TYPE_STRING, {.str = "init-stream$RepresentationID$.m4s"}, 0, 0, E },
     { "media_seg_name", "DASH-templated name to used for the media segments", OFFSET(media_seg_name), AV_OPT_TYPE_STRING, {.str = "chunk-stream$RepresentationID$-$Number%05d$.m4s"}, 0, 0, E },
     { "start_segment", "Segment number of the first segment", OFFSET(start_segment), AV_OPT_TYPE_INT, {.i64 = 1}, 1, INT_MAX, E },
+    { "skip_segments", "Skip segments", OFFSET(skip_segments), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, E },
     { NULL },
 };