]> git.sesse.net Git - ffmpeg/blobdiff - libavformat/hls.c
rmdec: add missing brackets to compound statement
[ffmpeg] / libavformat / hls.c
index 0c836c75363a90ad469e6e585a479f3bebd2fe8a..c9da4e30ecf5974157201d51bab921f2118a7452 100644 (file)
@@ -34,7 +34,6 @@
 #include "avformat.h"
 #include "internal.h"
 #include "avio_internal.h"
-#include "url.h"
 
 #define INITIAL_BUFFER_SIZE 32768
 
@@ -73,7 +72,7 @@ struct variant {
     char url[MAX_URL_SIZE];
     AVIOContext pb;
     uint8_t* read_buffer;
-    URLContext *input;
+    AVIOContext *input;
     AVFormatContext *parent;
     int index;
     AVFormatContext *ctx;
@@ -94,6 +93,7 @@ struct variant {
 };
 
 typedef struct HLSContext {
+    AVFormatContext *ctx;
     int n_variants;
     struct variant **variants;
     int cur_seq_no;
@@ -103,6 +103,7 @@ typedef struct HLSContext {
     int64_t seek_timestamp;
     int seek_flags;
     AVIOInterruptCB *interrupt_callback;
+    AVDictionary *avio_opts;
 } HLSContext;
 
 static int read_chomp_line(AVIOContext *s, char *buf, int maxlen)
@@ -128,10 +129,10 @@ static void free_variant_list(HLSContext *c)
     for (i = 0; i < c->n_variants; i++) {
         struct variant *var = c->variants[i];
         free_segment_list(var);
-        av_free_packet(&var->pkt);
+        av_packet_unref(&var->pkt);
         av_free(var->pb.buffer);
         if (var->input)
-            ffurl_close(var->input);
+            ff_format_io_close(c->ctx, &var->input);
         if (var->ctx) {
             var->ctx->pb = NULL;
             avformat_close_input(&var->ctx);
@@ -199,6 +200,34 @@ static void handle_key_args(struct key_info *info, const char *key,
     }
 }
 
+static int open_in(HLSContext *c, AVIOContext **in, const char *url)
+{
+    AVDictionary *tmp = NULL;
+    int ret;
+
+    av_dict_copy(&tmp, c->avio_opts, 0);
+
+    ret = c->ctx->io_open(c->ctx, in, url, AVIO_FLAG_READ, &tmp);
+
+    av_dict_free(&tmp);
+    return ret;
+}
+
+static int open_url(AVFormatContext *s, AVIOContext **pb, const char *url,
+                    const AVDictionary *opts)
+{
+    AVDictionary *tmp = NULL;
+    int ret;
+
+    av_dict_copy(&tmp, opts, 0);
+
+    ret = s->io_open(s, pb, url, AVIO_FLAG_READ, &tmp);
+
+    av_dict_free(&tmp);
+
+    return ret;
+}
+
 static int parse_playlist(HLSContext *c, const char *url,
                           struct variant *var, AVIOContext *in)
 {
@@ -211,14 +240,18 @@ static int parse_playlist(HLSContext *c, const char *url,
     char line[1024];
     const char *ptr;
     int close_in = 0;
+    uint8_t *new_url = NULL;
 
     if (!in) {
-        close_in = 1;
-        if ((ret = avio_open2(&in, url, AVIO_FLAG_READ,
-                              c->interrupt_callback, NULL)) < 0)
+        ret = open_in(c, &in, url);
+        if (ret < 0)
             return ret;
+        close_in = 1;
     }
 
+    if (av_opt_get(in, "location", AV_OPT_SEARCH_CHILDREN, &new_url) >= 0)
+        url = new_url;
+
     read_chomp_line(in, line, sizeof(line));
     if (strcmp(line, "#EXTM3U")) {
         ret = AVERROR_INVALIDDATA;
@@ -316,33 +349,34 @@ static int parse_playlist(HLSContext *c, const char *url,
         }
     }
     if (var)
-        var->last_load_time = av_gettime();
+        var->last_load_time = av_gettime_relative();
 
 fail:
+    av_free(new_url);
     if (close_in)
-        avio_close(in);
+        ff_format_io_close(c->ctx, &in);
     return ret;
 }
 
 static int open_input(struct variant *var)
 {
+    HLSContext *c = var->parent->priv_data;
     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,
-                          &var->parent->interrupt_callback, NULL);
+        return open_url(var->parent, &var->input, seg->url, c->avio_opts);
     } else if (seg->key_type == KEY_AES_128) {
+        AVDictionary *opts = NULL;
         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,
-                           &var->parent->interrupt_callback, NULL) == 0) {
-                if (ffurl_read_complete(uc, var->key, sizeof(var->key))
-                    != sizeof(var->key)) {
+            AVIOContext *pb;
+            if (open_url(var->parent, &pb, seg->key, c->avio_opts) == 0) {
+                ret = avio_read(pb, var->key, sizeof(var->key));
+                if (ret != sizeof(var->key)) {
                     av_log(NULL, AV_LOG_ERROR, "Unable to read key file %s\n",
                            seg->key);
                 }
-                ffurl_close(uc);
+                ff_format_io_close(var->parent, &pb);
             } else {
                 av_log(NULL, AV_LOG_ERROR, "Unable to open key file %s\n",
                        seg->key);
@@ -356,17 +390,14 @@ 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,
-                               &var->parent->interrupt_callback)) < 0)
-            return ret;
-        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;
-        }
-        return 0;
+
+        av_dict_copy(&opts, c->avio_opts, 0);
+        av_dict_set(&opts, "key", key, 0);
+        av_dict_set(&opts, "iv", iv, 0);
+
+        ret = open_url(var->parent, &var->input, url, opts);
+        av_dict_free(&opts);
+        return ret;
     }
     return AVERROR(ENOSYS);
 }
@@ -387,7 +418,7 @@ restart:
 
 reload:
         if (!v->finished &&
-            av_gettime() - v->last_load_time >= reload_interval) {
+            av_gettime_relative() - 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
@@ -404,7 +435,7 @@ 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 < reload_interval) {
+            while (av_gettime_relative() - v->last_load_time < reload_interval) {
                 if (ff_check_interrupt(c->interrupt_callback))
                     return AVERROR_EXIT;
                 av_usleep(100*1000);
@@ -417,11 +448,10 @@ reload:
         if (ret < 0)
             return ret;
     }
-    ret = ffurl_read(v->input, buf, buf_size);
+    ret = avio_read(v->input, buf, buf_size);
     if (ret > 0)
         return ret;
-    ffurl_close(v->input);
-    v->input = NULL;
+    ff_format_io_close(c->ctx, &v->input);
     v->cur_seq_no++;
 
     c->end_of_segment = 1;
@@ -444,16 +474,51 @@ reload:
     goto restart;
 }
 
+static int save_avio_options(AVFormatContext *s)
+{
+    HLSContext *c = s->priv_data;
+    static const char * const opts[] = { "headers", "user_agent", NULL };
+    const char * const *opt = opts;
+    uint8_t *buf;
+    int ret = 0;
+
+    while (*opt) {
+        if (av_opt_get(s->pb, *opt, AV_OPT_SEARCH_CHILDREN, &buf) >= 0) {
+            ret = av_dict_set(&c->avio_opts, *opt, buf,
+                              AV_DICT_DONT_STRDUP_VAL);
+            if (ret < 0)
+                return ret;
+        }
+        opt++;
+    }
+
+    return ret;
+}
+
+static int nested_io_open(AVFormatContext *s, AVIOContext **pb, const char *url,
+                          int flags, AVDictionary **opts)
+{
+    av_log(s, AV_LOG_ERROR,
+           "A HLS playlist item '%s' referred to an external file '%s'. "
+           "Opening this file was forbidden for security reasons\n",
+           s->filename, url);
+    return AVERROR(EPERM);
+}
+
 static int hls_read_header(AVFormatContext *s)
 {
     HLSContext *c = s->priv_data;
     int ret = 0, i, j, stream_offset = 0;
 
+    c->ctx                = s;
     c->interrupt_callback = &s->interrupt_callback;
 
     if ((ret = parse_playlist(c, s->filename, NULL, s->pb)) < 0)
         goto fail;
 
+    if ((ret = save_avio_options(s)) < 0)
+        goto fail;
+
     if (c->n_variants == 0) {
         av_log(NULL, AV_LOG_WARNING, "Empty playlist\n");
         ret = AVERROR_EOF;
@@ -525,8 +590,14 @@ static int hls_read_header(AVFormatContext *s)
             goto fail;
         }
         v->ctx->pb       = &v->pb;
+        v->ctx->io_open  = nested_io_open;
         v->stream_offset = stream_offset;
         ret = avformat_open_input(&v->ctx, v->segments[0]->url, in_fmt, NULL);
+        if (ret < 0)
+            goto fail;
+
+        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);
@@ -547,7 +618,7 @@ static int hls_read_header(AVFormatContext *s)
             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);
+            avcodec_parameters_copy(st->codecpar, v->ctx->streams[j]->codecpar);
             if (v->bandwidth)
                 av_dict_set(&st->metadata, "variant_bitrate", bitrate_str,
                                  0);
@@ -590,8 +661,7 @@ static int recheck_discard_flags(AVFormatContext *s, int first)
             av_log(s, AV_LOG_INFO, "Now receiving variant %d\n", i);
         } else if (first && !v->cur_needed && v->needed) {
             if (v->input)
-                ffurl_close(v->input);
-            v->input = NULL;
+                ff_format_io_close(s, &v->input);
             v->needed = 0;
             changed = 1;
             av_log(s, AV_LOG_INFO, "No longer receiving variant %d\n", i);
@@ -651,12 +721,15 @@ start:
                     c->seek_timestamp = AV_NOPTS_VALUE;
                     break;
                 }
+                av_packet_unref(&var->pkt);
+                reset_packet(&var->pkt);
             }
         }
         /* Check if this stream still is on an earlier segment number, or
          * has the packet with the lowest dts */
         if (var->pkt.data) {
-            struct variant *minvar = c->variants[minvariant];
+            struct variant *minvar = minvariant < 0 ?
+                                     NULL : 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) {
@@ -699,6 +772,9 @@ static int hls_close(AVFormatContext *s)
     HLSContext *c = s->priv_data;
 
     free_variant_list(c);
+
+    av_dict_free(&c->avio_opts);
+
     return 0;
 }
 
@@ -732,11 +808,9 @@ static int hls_read_seek(AVFormatContext *s, int stream_index,
         struct variant *var = c->variants[i];
         int64_t pos = c->first_timestamp == AV_NOPTS_VALUE ?
                       0 : c->first_timestamp;
-        if (var->input) {
-            ffurl_close(var->input);
-            var->input = NULL;
-        }
-        av_free_packet(&var->pkt);
+        if (var->input)
+            ff_format_io_close(s, &var->input);
+        av_packet_unref(&var->pkt);
         reset_packet(&var->pkt);
         var->pb.eof_reached = 0;
         /* Clear any buffered data */