]> git.sesse.net Git - ffmpeg/blobdiff - libavformat/applehttp.c
lavf: rename AVInputFormat.value to raw_codec_id.
[ffmpeg] / libavformat / applehttp.c
index 38f33a24f35e6f2460fe3a0b9364fa7e131982f4..32a51fede1adac15f2fd6817512b3b00db5a9754 100644 (file)
@@ -27,6 +27,7 @@
 
 #include "libavutil/avstring.h"
 #include "libavutil/intreadwrite.h"
+#include "libavutil/mathematics.h"
 #include "libavutil/opt.h"
 #include "libavutil/dict.h"
 #include "avformat.h"
@@ -98,6 +99,8 @@ typedef struct AppleHTTPContext {
     int cur_seq_no;
     int end_of_segment;
     int first_packet;
+    int64_t first_timestamp;
+    AVIOInterruptCB *interrupt_callback;
 } AppleHTTPContext;
 
 static int read_chomp_line(AVIOContext *s, char *buf, int maxlen)
@@ -129,7 +132,7 @@ static void free_variant_list(AppleHTTPContext *c)
             ffurl_close(var->input);
         if (var->ctx) {
             var->ctx->pb = NULL;
-            av_close_input_file(var->ctx);
+            avformat_close_input(&var->ctx);
         }
         av_free(var);
     }
@@ -201,14 +204,15 @@ static int parse_playlist(AppleHTTPContext *c, const char *url,
     enum KeyType key_type = KEY_NONE;
     uint8_t iv[16] = "";
     int has_iv = 0;
-    char key[MAX_URL_SIZE];
+    char key[MAX_URL_SIZE] = "";
     char line[1024];
     const char *ptr;
     int close_in = 0;
 
     if (!in) {
         close_in = 1;
-        if ((ret = avio_open(&in, url, AVIO_FLAG_READ)) < 0)
+        if ((ret = avio_open2(&in, url, AVIO_FLAG_READ,
+                              c->interrupt_callback, NULL)) < 0)
             return ret;
     }
 
@@ -321,13 +325,15 @@ static int open_input(struct variant *var)
 {
     struct segment *seg = var->segments[var->cur_seq_no - var->start_seq_no];
     if (seg->key_type == KEY_NONE) {
-        return ffurl_open(&var->input, seg->url, AVIO_FLAG_READ);
+        return ffurl_open(&var->input, seg->url, AVIO_FLAG_READ,
+                          &var->parent->interrupt_callback, NULL);
     } else if (seg->key_type == KEY_AES_128) {
         char iv[33], key[33], url[MAX_URL_SIZE];
         int ret;
         if (strcmp(seg->key, var->key_url)) {
             URLContext *uc;
-            if (ffurl_open(&uc, seg->key, AVIO_FLAG_READ) == 0) {
+            if (ffurl_open(&uc, seg->key, AVIO_FLAG_READ,
+                           &var->parent->interrupt_callback, NULL) == 0) {
                 if (ffurl_read_complete(uc, var->key, sizeof(var->key))
                     != sizeof(var->key)) {
                     av_log(NULL, AV_LOG_ERROR, "Unable to read key file %s\n",
@@ -347,11 +353,12 @@ static int open_input(struct variant *var)
             snprintf(url, sizeof(url), "crypto+%s", seg->url);
         else
             snprintf(url, sizeof(url), "crypto:%s", seg->url);
-        if ((ret = ffurl_alloc(&var->input, url, AVIO_FLAG_READ)) < 0)
+        if ((ret = ffurl_alloc(&var->input, url, AVIO_FLAG_READ,
+                               &var->parent->interrupt_callback)) < 0)
             return ret;
-        av_set_string3(var->input->priv_data, "key", key, 0, NULL);
-        av_set_string3(var->input->priv_data, "iv", iv, 0, NULL);
-        if ((ret = ffurl_connect(var->input)) < 0) {
+        av_opt_set(var->input->priv_data, "key", key, 0);
+        av_opt_set(var->input->priv_data, "iv", iv, 0);
+        if ((ret = ffurl_connect(var->input, NULL)) < 0) {
             ffurl_close(var->input);
             var->input = NULL;
             return ret;
@@ -369,13 +376,23 @@ static int read_data(void *opaque, uint8_t *buf, int buf_size)
 
 restart:
     if (!v->input) {
-reload:
-        /* If this is a live stream and target_duration has elapsed since
+        /* If this is a live stream and the reload interval has elapsed since
          * the last playlist reload, reload the variant playlists now. */
+        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 &&
-            av_gettime() - v->last_load_time >= v->target_duration*1000000 &&
-            (ret = parse_playlist(c, v->url, v, NULL)) < 0)
+            av_gettime() - v->last_load_time >= reload_interval) {
+            if ((ret = parse_playlist(c, v->url, v, NULL)) < 0)
                 return ret;
+            /* 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;
+        }
         if (v->cur_seq_no < v->start_seq_no) {
             av_log(NULL, AV_LOG_WARNING,
                    "skipping %d segments ahead, expired from playlists\n",
@@ -385,9 +402,8 @@ reload:
         if (v->cur_seq_no >= v->start_seq_no + v->n_segments) {
             if (v->finished)
                 return AVERROR_EOF;
-            while (av_gettime() - v->last_load_time <
-                   v->target_duration*1000000) {
-                if (url_interrupt_cb())
+            while (av_gettime() - v->last_load_time < reload_interval) {
+                if (ff_check_interrupt(c->interrupt_callback))
                     return AVERROR_EXIT;
                 usleep(100*1000);
             }
@@ -411,7 +427,7 @@ reload:
     c->end_of_segment = 1;
     c->cur_seq_no = v->cur_seq_no;
 
-    if (v->ctx) {
+    if (v->ctx && v->ctx->nb_streams) {
         v->needed = 0;
         for (i = v->stream_offset; i < v->stream_offset + v->ctx->nb_streams;
              i++) {
@@ -427,11 +443,13 @@ reload:
     goto restart;
 }
 
-static int applehttp_read_header(AVFormatContext *s, AVFormatParameters *ap)
+static int applehttp_read_header(AVFormatContext *s)
 {
     AppleHTTPContext *c = s->priv_data;
     int ret = 0, i, j, stream_offset = 0;
 
+    c->interrupt_callback = &s->interrupt_callback;
+
     if ((ret = parse_playlist(c, s->filename, NULL, s->pb)) < 0)
         goto fail;
 
@@ -494,8 +512,15 @@ static int applehttp_read_header(AVFormatContext *s, AVFormatParameters *ap)
         v->pb.seekable = 0;
         ret = av_probe_input_buffer(&v->pb, &in_fmt, v->segments[0]->url,
                                     NULL, 0, 0);
-        if (ret < 0)
+        if (ret < 0) {
+            /* Free the ctx - it isn't initialized properly at this point,
+             * so avformat_close_input shouldn't be called. If
+             * avformat_open_input fails below, it frees and zeros the
+             * context, so it doesn't need any special treatment like this. */
+            avformat_free_context(v->ctx);
+            v->ctx = NULL;
             goto fail;
+        }
         v->ctx->pb       = &v->pb;
         ret = avformat_open_input(&v->ctx, v->segments[0]->url, in_fmt, NULL);
         if (ret < 0)
@@ -504,11 +529,12 @@ static int applehttp_read_header(AVFormatContext *s, AVFormatParameters *ap)
         snprintf(bitrate_str, sizeof(bitrate_str), "%d", v->bandwidth);
         /* Create new AVStreams for each stream in this variant */
         for (j = 0; j < v->ctx->nb_streams; j++) {
-            AVStream *st = av_new_stream(s, i);
+            AVStream *st = avformat_new_stream(s, NULL);
             if (!st) {
                 ret = AVERROR(ENOMEM);
                 goto fail;
             }
+            st->id = i;
             avcodec_copy_context(st->codec, v->ctx->streams[j]->codec);
             if (v->bandwidth)
                 av_dict_set(&st->metadata, "variant_bitrate", bitrate_str,
@@ -518,6 +544,7 @@ static int applehttp_read_header(AVFormatContext *s, AVFormatParameters *ap)
     }
 
     c->first_packet = 1;
+    c->first_timestamp = AV_NOPTS_VALUE;
 
     return 0;
 fail:
@@ -582,6 +609,9 @@ start:
                 if (!var->pb.eof_reached)
                     return ret;
                 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 */
@@ -630,7 +660,10 @@ static int applehttp_read_seek(AVFormatContext *s, int stream_index,
     for (i = 0; i < c->n_variants; i++) {
         /* Reset reading */
         struct variant *var = c->variants[i];
-        int64_t pos = 0;
+        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);
         if (var->input) {
             ffurl_close(var->input);
             var->input = NULL;
@@ -667,12 +700,12 @@ static int applehttp_probe(AVProbeData *p)
 }
 
 AVInputFormat ff_applehttp_demuxer = {
-    "applehttp",
-    NULL_IF_CONFIG_SMALL("Apple HTTP Live Streaming format"),
-    sizeof(AppleHTTPContext),
-    applehttp_probe,
-    applehttp_read_header,
-    applehttp_read_packet,
-    applehttp_close,
-    applehttp_read_seek,
+    .name           = "applehttp",
+    .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,
 };