#include "avformat.h"
#include "internal.h"
#include "avio_internal.h"
-#include "url.h"
#define INITIAL_BUFFER_SIZE 32768
char url[MAX_URL_SIZE];
AVIOContext pb;
uint8_t* read_buffer;
- URLContext *input;
+ AVIOContext *input;
AVFormatContext *parent;
int index;
AVFormatContext *ctx;
};
typedef struct HLSContext {
+ AVFormatContext *ctx;
int n_variants;
struct variant **variants;
int cur_seq_no;
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)
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);
}
}
+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)
{
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;
}
}
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);
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);
}
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
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);
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;
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;
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);
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);
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);
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) {
HLSContext *c = s->priv_data;
free_variant_list(c);
+
+ av_dict_free(&c->avio_opts);
+
return 0;
}
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 */