matroskaenc: Fix writing TRACKDEFAULTFLAG
[ffmpeg.git] / libavformat / hls.c
index 13b9b27..ea16f8a 100644 (file)
@@ -30,9 +30,9 @@
 #include "libavutil/mathematics.h"
 #include "libavutil/opt.h"
 #include "libavutil/dict.h"
+#include "libavutil/time.h"
 #include "avformat.h"
 #include "internal.h"
-#include <unistd.h>
 #include "avio_internal.h"
 #include "url.h"
 
@@ -42,7 +42,7 @@
  * An apple http stream consists of a playlist with media segment files,
  * played sequentially. There may be several playlists with the same
  * video content, in different bandwidth variants, that are played in
- * parallel (preferrably only one bandwidth variant at a time). In this case,
+ * parallel (preferably only one bandwidth variant at a time). In this case,
  * the user supplied the url to a main playlist that only lists the variant
  * playlists.
  *
@@ -56,7 +56,7 @@ enum KeyType {
 };
 
 struct segment {
-    int duration;
+    int64_t duration;
     char url[MAX_URL_SIZE];
     char key[MAX_URL_SIZE];
     enum KeyType key_type;
@@ -81,7 +81,7 @@ struct variant {
     int stream_offset;
 
     int finished;
-    int target_duration;
+    int64_t target_duration;
     int start_seq_no;
     int n_segments;
     struct segment **segments;
@@ -93,20 +93,22 @@ struct variant {
     uint8_t key[16];
 };
 
-typedef struct AppleHTTPContext {
+typedef struct HLSContext {
     int n_variants;
     struct variant **variants;
     int cur_seq_no;
     int end_of_segment;
     int first_packet;
     int64_t first_timestamp;
+    int64_t seek_timestamp;
+    int seek_flags;
     AVIOInterruptCB *interrupt_callback;
-} AppleHTTPContext;
+} HLSContext;
 
 static int read_chomp_line(AVIOContext *s, char *buf, int maxlen)
 {
     int len = ff_get_line(s, buf, maxlen);
-    while (len > 0 && isspace(buf[len - 1]))
+    while (len > 0 && av_isspace(buf[len - 1]))
         buf[--len] = '\0';
     return len;
 }
@@ -120,7 +122,7 @@ static void free_segment_list(struct variant *var)
     var->n_segments = 0;
 }
 
-static void free_variant_list(AppleHTTPContext *c)
+static void free_variant_list(HLSContext *c)
 {
     int i;
     for (i = 0; i < c->n_variants; i++) {
@@ -150,7 +152,7 @@ static void reset_packet(AVPacket *pkt)
     pkt->data = NULL;
 }
 
-static struct variant *new_variant(AppleHTTPContext *c, int bandwidth,
+static struct variant *new_variant(HLSContext *c, int bandwidth,
                                    const char *url, const char *base)
 {
     struct variant *var = av_mallocz(sizeof(struct variant));
@@ -197,10 +199,11 @@ static void handle_key_args(struct key_info *info, const char *key,
     }
 }
 
-static int parse_playlist(AppleHTTPContext *c, const char *url,
+static int parse_playlist(HLSContext *c, const char *url,
                           struct variant *var, AVIOContext *in)
 {
-    int ret = 0, duration = 0, is_segment = 0, is_variant = 0, bandwidth = 0;
+    int ret = 0, is_segment = 0, is_variant = 0, bandwidth = 0;
+    int64_t duration = 0;
     enum KeyType key_type = KEY_NONE;
     uint8_t iv[16] = "";
     int has_iv = 0;
@@ -255,7 +258,7 @@ static int parse_playlist(AppleHTTPContext *c, const char *url,
                     goto fail;
                 }
             }
-            var->target_duration = atoi(ptr);
+            var->target_duration = atoi(ptr) * AV_TIME_BASE;
         } else if (av_strstart(line, "#EXT-X-MEDIA-SEQUENCE:", &ptr)) {
             if (!var) {
                 var = new_variant(c, 0, url, NULL);
@@ -270,7 +273,7 @@ static int parse_playlist(AppleHTTPContext *c, const char *url,
                 var->finished = 1;
         } else if (av_strstart(line, "#EXTINF:", &ptr)) {
             is_segment = 1;
-            duration   = atoi(ptr);
+            duration   = atof(ptr) * AV_TIME_BASE;
         } else if (av_strstart(line, "#", NULL)) {
             continue;
         } else if (line[0]) {
@@ -371,7 +374,7 @@ static int open_input(struct variant *var)
 static int read_data(void *opaque, uint8_t *buf, int buf_size)
 {
     struct variant *v = opaque;
-    AppleHTTPContext *c = v->parent->priv_data;
+    HLSContext *c = v->parent->priv_data;
     int ret, i;
 
 restart:
@@ -381,7 +384,6 @@ restart:
         int64_t reload_interval = v->n_segments > 0 ?
                                   v->segments[v->n_segments - 1]->duration :
                                   v->target_duration;
-        reload_interval *= 1000000;
 
 reload:
         if (!v->finished &&
@@ -391,7 +393,7 @@ reload:
             /* If we need to reload the playlist again below (if
              * there's still no more segments), switch to a reload
              * interval of half the target duration. */
-            reload_interval = v->target_duration * 500000;
+            reload_interval = v->target_duration / 2;
         }
         if (v->cur_seq_no < v->start_seq_no) {
             av_log(NULL, AV_LOG_WARNING,
@@ -405,7 +407,7 @@ reload:
             while (av_gettime() - v->last_load_time < reload_interval) {
                 if (ff_check_interrupt(c->interrupt_callback))
                     return AVERROR_EXIT;
-                usleep(100*1000);
+                av_usleep(100*1000);
             }
             /* Enough time has elapsed since the last reload */
             goto reload;
@@ -418,8 +420,6 @@ reload:
     ret = ffurl_read(v->input, buf, buf_size);
     if (ret > 0)
         return ret;
-    if (ret < 0 && ret != AVERROR_EOF)
-        return ret;
     ffurl_close(v->input);
     v->input = NULL;
     v->cur_seq_no++;
@@ -427,7 +427,8 @@ reload:
     c->end_of_segment = 1;
     c->cur_seq_no = v->cur_seq_no;
 
-    if (v->ctx && v->ctx->nb_streams) {
+    if (v->ctx && v->ctx->nb_streams &&
+        v->parent->nb_streams >= v->stream_offset + v->ctx->nb_streams) {
         v->needed = 0;
         for (i = v->stream_offset; i < v->stream_offset + v->ctx->nb_streams;
              i++) {
@@ -443,9 +444,9 @@ reload:
     goto restart;
 }
 
-static int applehttp_read_header(AVFormatContext *s)
+static int hls_read_header(AVFormatContext *s)
 {
-    AppleHTTPContext *c = s->priv_data;
+    HLSContext *c = s->priv_data;
     int ret = 0, i, j, stream_offset = 0;
 
     c->interrupt_callback = &s->interrupt_callback;
@@ -480,7 +481,7 @@ static int applehttp_read_header(AVFormatContext *s)
         int64_t duration = 0;
         for (i = 0; i < c->variants[0]->n_segments; i++)
             duration += c->variants[0]->segments[i]->duration;
-        s->duration = duration * AV_TIME_BASE;
+        s->duration = duration;
     }
 
     /* Open the demuxer for each variant */
@@ -488,6 +489,8 @@ static int applehttp_read_header(AVFormatContext *s)
         struct variant *v = c->variants[i];
         AVInputFormat *in_fmt = NULL;
         char bitrate_str[20];
+        AVProgram *program;
+
         if (v->n_segments == 0)
             continue;
 
@@ -522,19 +525,33 @@ static int applehttp_read_header(AVFormatContext *s)
             goto fail;
         }
         v->ctx->pb       = &v->pb;
+        v->stream_offset = stream_offset;
         ret = avformat_open_input(&v->ctx, v->segments[0]->url, in_fmt, NULL);
         if (ret < 0)
             goto fail;
-        v->stream_offset = stream_offset;
+
+        v->ctx->ctx_flags &= ~AVFMTCTX_NOHEADER;
+        ret = avformat_find_stream_info(v->ctx, NULL);
+        if (ret < 0)
+            goto fail;
         snprintf(bitrate_str, sizeof(bitrate_str), "%d", v->bandwidth);
+
+        program = av_new_program(s, i);
+        if (!program)
+            goto fail;
+        av_dict_set(&program->metadata, "variant_bitrate", bitrate_str, 0);
+
         /* Create new AVStreams for each stream in this variant */
         for (j = 0; j < v->ctx->nb_streams; j++) {
             AVStream *st = avformat_new_stream(s, NULL);
+            AVStream *ist = v->ctx->streams[j];
             if (!st) {
                 ret = AVERROR(ENOMEM);
                 goto fail;
             }
+            ff_program_add_stream_index(s, i, stream_offset + j);
             st->id = i;
+            avpriv_set_pts_info(st, ist->pts_wrap_bits, ist->time_base.num, ist->time_base.den);
             avcodec_copy_context(st->codec, v->ctx->streams[j]->codec);
             if (v->bandwidth)
                 av_dict_set(&st->metadata, "variant_bitrate", bitrate_str,
@@ -545,6 +562,7 @@ static int applehttp_read_header(AVFormatContext *s)
 
     c->first_packet = 1;
     c->first_timestamp = AV_NOPTS_VALUE;
+    c->seek_timestamp  = AV_NOPTS_VALUE;
 
     return 0;
 fail:
@@ -554,7 +572,7 @@ fail:
 
 static int recheck_discard_flags(AVFormatContext *s, int first)
 {
-    AppleHTTPContext *c = s->priv_data;
+    HLSContext *c = s->priv_data;
     int i, changed = 0;
 
     /* Check if any new streams are needed */
@@ -587,9 +605,9 @@ static int recheck_discard_flags(AVFormatContext *s, int first)
     return changed;
 }
 
-static int applehttp_read_packet(AVFormatContext *s, AVPacket *pkt)
+static int hls_read_packet(AVFormatContext *s, AVPacket *pkt)
 {
-    AppleHTTPContext *c = s->priv_data;
+    HLSContext *c = s->priv_data;
     int ret, i, minvariant = -1;
 
     if (c->first_packet) {
@@ -604,21 +622,69 @@ start:
         /* Make sure we've got one buffered packet from each open variant
          * stream */
         if (var->needed && !var->pkt.data) {
-            ret = av_read_frame(var->ctx, &var->pkt);
-            if (ret < 0) {
-                if (!var->pb.eof_reached)
-                    return ret;
+            while (1) {
+                int64_t ts_diff;
+                AVStream *st;
+                ret = av_read_frame(var->ctx, &var->pkt);
+                if (ret < 0) {
+                    if (!var->pb.eof_reached)
+                        return ret;
+                    reset_packet(&var->pkt);
+                    break;
+                } else {
+                    if (c->first_timestamp == AV_NOPTS_VALUE &&
+                        var->pkt.dts       != AV_NOPTS_VALUE)
+                        c->first_timestamp = av_rescale_q(var->pkt.dts,
+                            var->ctx->streams[var->pkt.stream_index]->time_base,
+                            AV_TIME_BASE_Q);
+                }
+
+                if (c->seek_timestamp == AV_NOPTS_VALUE)
+                    break;
+
+                if (var->pkt.dts == AV_NOPTS_VALUE) {
+                    c->seek_timestamp = AV_NOPTS_VALUE;
+                    break;
+                }
+
+                st = var->ctx->streams[var->pkt.stream_index];
+                ts_diff = av_rescale_rnd(var->pkt.dts, AV_TIME_BASE,
+                                         st->time_base.den, AV_ROUND_DOWN) -
+                          c->seek_timestamp;
+                if (ts_diff >= 0 && (c->seek_flags  & AVSEEK_FLAG_ANY ||
+                                     var->pkt.flags & AV_PKT_FLAG_KEY)) {
+                    c->seek_timestamp = AV_NOPTS_VALUE;
+                    break;
+                }
+                av_free_packet(&var->pkt);
                 reset_packet(&var->pkt);
-            } else {
-                if (c->first_timestamp == AV_NOPTS_VALUE)
-                    c->first_timestamp = var->pkt.dts;
             }
         }
-        /* Check if this stream has the packet with the lowest dts */
+        /* Check if this stream still is on an earlier segment number, or
+         * has the packet with the lowest dts */
         if (var->pkt.data) {
-            if (minvariant < 0 ||
-                var->pkt.dts < c->variants[minvariant]->pkt.dts)
+            struct variant *minvar = c->variants[minvariant];
+            if (minvariant < 0 || var->cur_seq_no < minvar->cur_seq_no) {
                 minvariant = i;
+            } else if (var->cur_seq_no == minvar->cur_seq_no) {
+                int64_t dts     =    var->pkt.dts;
+                int64_t mindts  = minvar->pkt.dts;
+                AVStream *st    =    var->ctx->streams[var->pkt.stream_index];
+                AVStream *minst = minvar->ctx->streams[minvar->pkt.stream_index];
+
+                if (dts == AV_NOPTS_VALUE) {
+                    minvariant = i;
+                } else if (mindts != AV_NOPTS_VALUE) {
+                    if (st->start_time    != AV_NOPTS_VALUE)
+                        dts    -= st->start_time;
+                    if (minst->start_time != AV_NOPTS_VALUE)
+                        mindts -= minst->start_time;
+
+                    if (av_compare_ts(dts, st->time_base,
+                                      mindts, minst->time_base) < 0)
+                        minvariant = i;
+                }
+            }
         }
     }
     if (c->end_of_segment) {
@@ -635,35 +701,44 @@ start:
     return AVERROR_EOF;
 }
 
-static int applehttp_close(AVFormatContext *s)
+static int hls_close(AVFormatContext *s)
 {
-    AppleHTTPContext *c = s->priv_data;
+    HLSContext *c = s->priv_data;
 
     free_variant_list(c);
     return 0;
 }
 
-static int applehttp_read_seek(AVFormatContext *s, int stream_index,
+static int hls_read_seek(AVFormatContext *s, int stream_index,
                                int64_t timestamp, int flags)
 {
-    AppleHTTPContext *c = s->priv_data;
+    HLSContext *c = s->priv_data;
     int i, j, ret;
 
     if ((flags & AVSEEK_FLAG_BYTE) || !c->variants[0]->finished)
         return AVERROR(ENOSYS);
 
-    timestamp = av_rescale_rnd(timestamp, 1, stream_index >= 0 ?
+    c->seek_flags     = flags;
+    c->seek_timestamp = stream_index < 0 ? timestamp :
+                        av_rescale_rnd(timestamp, AV_TIME_BASE,
+                                       s->streams[stream_index]->time_base.den,
+                                       flags & AVSEEK_FLAG_BACKWARD ?
+                                       AV_ROUND_DOWN : AV_ROUND_UP);
+    timestamp = av_rescale_rnd(timestamp, AV_TIME_BASE, stream_index >= 0 ?
                                s->streams[stream_index]->time_base.den :
                                AV_TIME_BASE, flags & AVSEEK_FLAG_BACKWARD ?
                                AV_ROUND_DOWN : AV_ROUND_UP);
+    if (s->duration < c->seek_timestamp) {
+        c->seek_timestamp = AV_NOPTS_VALUE;
+        return AVERROR(EIO);
+    }
+
     ret = AVERROR(EIO);
     for (i = 0; i < c->n_variants; i++) {
         /* Reset reading */
         struct variant *var = c->variants[i];
-        int64_t pos = c->first_timestamp == AV_NOPTS_VALUE ? 0 :
-                      av_rescale_rnd(c->first_timestamp, 1,
-                          stream_index >= 0 ? s->streams[stream_index]->time_base.den : AV_TIME_BASE,
-                          flags & AVSEEK_FLAG_BACKWARD ? AV_ROUND_DOWN : AV_ROUND_UP);
+        int64_t pos = c->first_timestamp == AV_NOPTS_VALUE ?
+                      0 : c->first_timestamp;
         if (var->input) {
             ffurl_close(var->input);
             var->input = NULL;
@@ -671,6 +746,10 @@ static int applehttp_read_seek(AVFormatContext *s, int stream_index,
         av_free_packet(&var->pkt);
         reset_packet(&var->pkt);
         var->pb.eof_reached = 0;
+        /* Clear any buffered data */
+        var->pb.buf_end = var->pb.buf_ptr = var->pb.buffer;
+        /* Reset the pos, to let the mpegts demuxer know we've seeked. */
+        var->pb.pos = 0;
 
         /* Locate the segment that contains the target timestamp */
         for (j = 0; j < var->n_segments; j++) {
@@ -682,11 +761,13 @@ static int applehttp_read_seek(AVFormatContext *s, int stream_index,
             }
             pos += var->segments[j]->duration;
         }
+        if (ret)
+            c->seek_timestamp = AV_NOPTS_VALUE;
     }
     return ret;
 }
 
-static int applehttp_probe(AVProbeData *p)
+static int hls_probe(AVProbeData *p)
 {
     /* Require #EXTM3U at the start, and either one of the ones below
      * somewhere for a proper match. */
@@ -700,12 +781,12 @@ static int applehttp_probe(AVProbeData *p)
 }
 
 AVInputFormat ff_hls_demuxer = {
-    .name           = "hls",
-    .long_name      = NULL_IF_CONFIG_SMALL("Apple HTTP Live Streaming format"),
-    .priv_data_size = sizeof(AppleHTTPContext),
-    .read_probe     = applehttp_probe,
-    .read_header    = applehttp_read_header,
-    .read_packet    = applehttp_read_packet,
-    .read_close     = applehttp_close,
-    .read_seek      = applehttp_read_seek,
+    .name           = "hls,applehttp",
+    .long_name      = NULL_IF_CONFIG_SMALL("Apple HTTP Live Streaming"),
+    .priv_data_size = sizeof(HLSContext),
+    .read_probe     = hls_probe,
+    .read_header    = hls_read_header,
+    .read_packet    = hls_read_packet,
+    .read_close     = hls_close,
+    .read_seek      = hls_read_seek,
 };