X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Fhls.c;h=8ad08baaed41c5af1f7c0551ddd507069c5eb935;hb=0ed678a97abddef6b287b8cb93c24fba662f54a3;hp=c578bf86e3b8d15b28bacf53c59435dd77630e50;hpb=222d4b0accaafde79a1aa61b7227d1af1d2a1695;p=ffmpeg diff --git a/libavformat/hls.c b/libavformat/hls.c index c578bf86e3b..8ad08baaed4 100644 --- a/libavformat/hls.c +++ b/libavformat/hls.c @@ -202,11 +202,6 @@ typedef struct HLSContext { int64_t first_timestamp; int64_t cur_timestamp; AVIOInterruptCB *interrupt_callback; - char *referer; ///< holds HTTP referer set as an AVOption to the HTTP protocol context - char *user_agent; ///< holds HTTP user agent set as an AVOption to the HTTP protocol context - char *cookies; ///< holds HTTP cookie values set in either the initial response or as an AVOption to the HTTP protocol context - char *headers; ///< holds HTTP headers set as an AVOption to the HTTP protocol context - char *http_proxy; ///< holds the address of the HTTP proxy server AVDictionary *avio_opts; int strict_std_compliance; char *allowed_extensions; @@ -216,24 +211,21 @@ typedef struct HLSContext { AVIOContext *playlist_pb; } HLSContext; -static int read_chomp_line(AVIOContext *s, char *buf, int maxlen) +static void free_segment_dynarray(struct segment **segments, int n_segments) { - int len = ff_get_line(s, buf, maxlen); - while (len > 0 && av_isspace(buf[len - 1])) - buf[--len] = '\0'; - return len; + int i; + for (i = 0; i < n_segments; i++) { + av_freep(&segments[i]->key); + av_freep(&segments[i]->url); + av_freep(&segments[i]); + } } static void free_segment_list(struct playlist *pls) { - int i; - for (i = 0; i < pls->n_segments; i++) { - av_freep(&pls->segments[i]->key); - av_freep(&pls->segments[i]->url); - av_freep(&pls->segments[i]); - } - av_freep(&pls->segments); - pls->n_segments = 0; + free_segment_dynarray(pls->segments, pls->n_segments); + av_freep(&pls->segments); + pls->n_segments = 0; } static void free_init_section_list(struct playlist *pls) @@ -275,10 +267,6 @@ static void free_playlist_list(HLSContext *c) av_free(pls); } av_freep(&c->playlists); - av_freep(&c->cookies); - av_freep(&c->user_agent); - av_freep(&c->headers); - av_freep(&c->http_proxy); c->n_playlists = 0; } @@ -601,14 +589,6 @@ static int ensure_playlist(HLSContext *c, struct playlist **pls, const char *url return 0; } -static void update_options(char **dest, const char *name, void *src) -{ - av_freep(dest); - av_opt_get(src, name, AV_OPT_SEARCH_CHILDREN, (uint8_t**)dest); - if (*dest && !strlen(*dest)) - av_freep(dest); -} - static int open_url_keepalive(AVFormatContext *s, AVIOContext **pb, const char *url) { @@ -692,12 +672,8 @@ static int open_url(AVFormatContext *s, AVIOContext **pb, const char *url, if (!(s->flags & AVFMT_FLAG_CUSTOM_IO)) av_opt_get(*pb, "cookies", AV_OPT_SEARCH_CHILDREN, (uint8_t**)&new_cookies); - if (new_cookies) { - av_free(c->cookies); - c->cookies = new_cookies; - } - - av_dict_set(&opts, "cookies", c->cookies, 0); + if (new_cookies) + av_dict_set(&opts, "cookies", new_cookies, AV_DICT_DONT_STRDUP_VAL); } av_dict_free(&tmp); @@ -727,6 +703,9 @@ static int parse_playlist(HLSContext *c, const char *url, char tmp_str[MAX_URL_SIZE]; struct segment *cur_init_section = NULL; int is_http = av_strstart(url, "http", NULL); + struct segment **prev_segments = NULL; + int prev_n_segments = 0; + int prev_start_seq_no = -1; if (is_http && !in && c->http_persistent && c->playlist_pb) { in = c->playlist_pb; @@ -743,16 +722,8 @@ static int parse_playlist(HLSContext *c, const char *url, } if (!in) { -#if 1 AVDictionary *opts = NULL; - /* Some HLS servers don't like being sent the range header */ - av_dict_set(&opts, "seekable", "0", 0); - - // broker prior HTTP options that should be consistent across requests - av_dict_set(&opts, "user_agent", c->user_agent, 0); - av_dict_set(&opts, "cookies", c->cookies, 0); - av_dict_set(&opts, "headers", c->headers, 0); - av_dict_set(&opts, "http_proxy", c->http_proxy, 0); + av_dict_copy(&opts, c->avio_opts, 0); if (c->http_persistent) av_dict_set(&opts, "multiple_requests", "1", 0); @@ -766,30 +737,29 @@ static int parse_playlist(HLSContext *c, const char *url, c->playlist_pb = in; else close_in = 1; -#else - ret = open_in(c, &in, url); - if (ret < 0) - return ret; - close_in = 1; -#endif } if (av_opt_get(in, "location", AV_OPT_SEARCH_CHILDREN, &new_url) >= 0) url = new_url; - read_chomp_line(in, line, sizeof(line)); + ff_get_chomp_line(in, line, sizeof(line)); if (strcmp(line, "#EXTM3U")) { ret = AVERROR_INVALIDDATA; goto fail; } if (pls) { - free_segment_list(pls); + prev_start_seq_no = pls->start_seq_no; + prev_segments = pls->segments; + prev_n_segments = pls->n_segments; + pls->segments = NULL; + pls->n_segments = 0; + pls->finished = 0; pls->type = PLS_TYPE_UNSPECIFIED; } while (!avio_feof(in)) { - read_chomp_line(in, line, sizeof(line)); + ff_get_chomp_line(in, line, sizeof(line)); if (av_strstart(line, "#EXT-X-STREAM-INF:", &ptr)) { is_variant = 1; memset(&variant_info, 0, sizeof(variant_info)); @@ -841,6 +811,27 @@ static int parse_playlist(HLSContext *c, const char *url, ff_parse_key_value(ptr, (ff_parse_key_val_cb) handle_init_section_args, &info); cur_init_section = new_init_section(pls, &info, url); + cur_init_section->key_type = key_type; + if (has_iv) { + memcpy(cur_init_section->iv, iv, sizeof(iv)); + } else { + int seq = pls->start_seq_no + pls->n_segments; + memset(cur_init_section->iv, 0, sizeof(cur_init_section->iv)); + AV_WB32(cur_init_section->iv + 12, seq); + } + + if (key_type != KEY_NONE) { + ff_make_absolute_url(tmp_str, sizeof(tmp_str), url, key); + cur_init_section->key = av_strdup(tmp_str); + if (!cur_init_section->key) { + av_free(cur_init_section); + ret = AVERROR(ENOMEM); + goto fail; + } + } else { + cur_init_section->key = NULL; + } + } else if (av_strstart(line, "#EXT-X-ENDLIST", &ptr)) { if (pls) pls->finished = 1; @@ -924,6 +915,23 @@ static int parse_playlist(HLSContext *c, const char *url, } } } + if (prev_segments) { + if (pls->start_seq_no > prev_start_seq_no && c->first_timestamp != AV_NOPTS_VALUE) { + int64_t prev_timestamp = c->first_timestamp; + int i, diff = pls->start_seq_no - prev_start_seq_no; + for (i = 0; i < prev_n_segments && i < diff; i++) { + c->first_timestamp += prev_segments[i]->duration; + } + av_log(c->ctx, AV_LOG_DEBUG, "Media sequence change (%d -> %d)" + " reflected in first_timestamp: %"PRId64" -> %"PRId64"\n", + prev_start_seq_no, pls->start_seq_no, + prev_timestamp, c->first_timestamp); + } else if (pls->start_seq_no < prev_start_seq_no) { + av_log(c->ctx, AV_LOG_WARNING, "Media sequence changed unexpectedly: %d -> %d\n", + prev_start_seq_no, pls->start_seq_no); + } + free_segment_dynarray(prev_segments, prev_n_segments); + } if (pls) pls->last_load_time = av_gettime_relative(); @@ -952,14 +960,8 @@ static struct segment *next_segment(struct playlist *pls) return pls->segments[n]; } -enum ReadFromURLMode { - READ_NORMAL, - READ_COMPLETE, -}; - static int read_from_url(struct playlist *pls, struct segment *seg, - uint8_t *buf, int buf_size, - enum ReadFromURLMode mode) + uint8_t *buf, int buf_size) { int ret; @@ -967,13 +969,7 @@ static int read_from_url(struct playlist *pls, struct segment *seg, if (seg->size >= 0) buf_size = FFMIN(buf_size, seg->size - pls->cur_seg_offset); - if (mode == READ_COMPLETE) { - ret = avio_read(pls->input, buf, buf_size); - if (ret != buf_size) - av_log(NULL, AV_LOG_ERROR, "Could not read complete segment.\n"); - } else - ret = avio_read(pls->input, buf, buf_size); - + ret = avio_read(pls->input, buf, buf_size); if (ret > 0) pls->cur_seg_offset += ret; @@ -1092,7 +1088,7 @@ static void intercept_id3(struct playlist *pls, uint8_t *buf, while (1) { /* see if we can retrieve enough data for ID3 header */ if (*len < ID3v2_HEADER_SIZE && buf_size >= ID3v2_HEADER_SIZE) { - bytes = read_from_url(pls, seg, buf + *len, ID3v2_HEADER_SIZE - *len, READ_COMPLETE); + bytes = read_from_url(pls, seg, buf + *len, ID3v2_HEADER_SIZE - *len); if (bytes > 0) { if (bytes == ID3v2_HEADER_SIZE - *len) @@ -1144,7 +1140,7 @@ static void intercept_id3(struct playlist *pls, uint8_t *buf, if (remaining > 0) { /* read the rest of the tag in */ - if (read_from_url(pls, seg, pls->id3_buf + id3_buf_pos, remaining, READ_COMPLETE) != remaining) + if (read_from_url(pls, seg, pls->id3_buf + id3_buf_pos, remaining) != remaining) break; id3_buf_pos += remaining; av_log(pls->ctx, AV_LOG_DEBUG, "Stripped additional %d HLS ID3 bytes\n", remaining); @@ -1158,7 +1154,7 @@ static void intercept_id3(struct playlist *pls, uint8_t *buf, /* re-fill buffer for the caller unless EOF */ if (*len >= 0 && (fill_buf || *len == 0)) { - bytes = read_from_url(pls, seg, buf + *len, buf_size - *len, READ_NORMAL); + bytes = read_from_url(pls, seg, buf + *len, buf_size - *len); /* ignore error if we already had some data */ if (bytes >= 0) @@ -1184,14 +1180,6 @@ static int open_input(HLSContext *c, struct playlist *pls, struct segment *seg, int ret; int is_http = 0; - // broker prior HTTP options that should be consistent across requests - av_dict_set(&opts, "user_agent", c->user_agent, 0); - av_dict_set(&opts, "referer", c->referer, 0); - av_dict_set(&opts, "cookies", c->cookies, 0); - av_dict_set(&opts, "headers", c->headers, 0); - av_dict_set(&opts, "http_proxy", c->http_proxy, 0); - av_dict_set(&opts, "seekable", "0", 0); - if (c->http_persistent) av_dict_set(&opts, "multiple_requests", "1", 0); @@ -1208,7 +1196,6 @@ static int open_input(HLSContext *c, struct playlist *pls, struct segment *seg, if (seg->key_type == KEY_NONE) { ret = open_url(pls->parent, in, seg->url, c->avio_opts, opts, &is_http); } else if (seg->key_type == KEY_AES_128) { - AVDictionary *opts2 = NULL; char iv[33], key[33], url[MAX_URL_SIZE]; if (strcmp(seg->key, pls->key_url)) { AVIOContext *pb = NULL; @@ -1233,14 +1220,10 @@ static int open_input(HLSContext *c, struct playlist *pls, struct segment *seg, else snprintf(url, sizeof(url), "crypto:%s", seg->url); - av_dict_copy(&opts2, c->avio_opts, 0); - av_dict_set(&opts2, "key", key, 0); - av_dict_set(&opts2, "iv", iv, 0); - - ret = open_url(pls->parent, in, url, opts2, opts, &is_http); - - av_dict_free(&opts2); + av_dict_set(&opts, "key", key, 0); + av_dict_set(&opts, "iv", iv, 0); + ret = open_url(pls->parent, in, url, c->avio_opts, opts, &is_http); if (ret < 0) { goto cleanup; } @@ -1318,7 +1301,7 @@ static int update_init_section(struct playlist *pls, struct segment *seg) av_fast_malloc(&pls->init_sec_buf, &pls->init_sec_buf_size, sec_size); ret = read_from_url(pls, seg->init_section, pls->init_sec_buf, - pls->init_sec_buf_size, READ_COMPLETE); + pls->init_sec_buf_size); ff_format_io_close(pls->parent, &pls->input); if (ret < 0) @@ -1513,7 +1496,7 @@ reload: } seg = current_segment(v); - ret = read_from_url(v, seg, buf, buf_size, READ_NORMAL); + ret = read_from_url(v, seg, buf, buf_size); if (ret > 0) { if (just_opened && v->is_id3_timestamped != 0) { /* Intercept ID3 tags here, elementary audio streams are required @@ -1661,7 +1644,7 @@ static int save_avio_options(AVFormatContext *s) { HLSContext *c = s->priv_data; static const char * const opts[] = { - "headers", "http_proxy", "user_agent", "user-agent", "cookies", "referer", NULL }; + "headers", "http_proxy", "user_agent", "cookies", "referer", "rw_timeout", NULL }; const char * const * opt = opts; uint8_t *buf; int ret = 0; @@ -1796,7 +1779,6 @@ static int hls_close(AVFormatContext *s) static int hls_read_header(AVFormatContext *s) { - void *u = (s->flags & AVFMT_FLAG_CUSTOM_IO) ? NULL : s->pb; HLSContext *c = s->priv_data; int ret = 0, i; int highest_cur_seq_no = 0; @@ -1809,29 +1791,15 @@ static int hls_read_header(AVFormatContext *s) c->first_timestamp = AV_NOPTS_VALUE; c->cur_timestamp = AV_NOPTS_VALUE; - if (u) { - // get the previous user agent & set back to null if string size is zero - update_options(&c->user_agent, "user_agent", u); - - // get the previous cookies & set back to null if string size is zero - update_options(&c->cookies, "cookies", u); - - // get the previous headers & set back to null if string size is zero - update_options(&c->headers, "headers", u); - - // get the previous http proxt & set back to null if string size is zero - update_options(&c->http_proxy, "http_proxy", u); - } - - if ((ret = parse_playlist(c, s->url, NULL, s->pb)) < 0) - goto fail; - if ((ret = save_avio_options(s)) < 0) goto fail; /* Some HLS servers don't like being sent the range header */ av_dict_set(&c->avio_opts, "seekable", "0", 0); + if ((ret = parse_playlist(c, s->url, NULL, s->pb)) < 0) + goto fail; + if (c->n_variants == 0) { av_log(NULL, AV_LOG_WARNING, "Empty playlist\n"); ret = AVERROR_EOF;