FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/hls.c
Date: 2026-08-26 16:21:34
Exec Total Coverage
Lines: 1008 1698 59.4%
Functions: 46 57 80.7%
Branches: 588 1278 46.0%

Line Branch Exec Source
1 /*
2 * Apple HTTP Live Streaming demuxer
3 * Copyright (c) 2010 Martin Storsjo
4 * Copyright (c) 2013 Anssi Hannula
5 * Copyright (c) 2021 Nachiket Tarate
6 *
7 * This file is part of FFmpeg.
8 *
9 * FFmpeg is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * FFmpeg is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with FFmpeg; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24 /**
25 * @file
26 * Apple HTTP Live Streaming demuxer
27 * https://www.rfc-editor.org/rfc/rfc8216.txt
28 */
29
30 #include "config_components.h"
31
32 #include "libavformat/http.h"
33 #include "libavutil/aes.h"
34 #include "libavutil/avstring.h"
35 #include "libavutil/avassert.h"
36 #include "libavutil/intreadwrite.h"
37 #include "libavutil/mathematics.h"
38 #include "libavutil/mem.h"
39 #include "libavutil/opt.h"
40 #include "libavutil/dict.h"
41 #include "libavutil/time.h"
42 #include "avformat.h"
43 #include "demux.h"
44 #include "internal.h"
45 #include "avio_internal.h"
46 #include "id3v2.h"
47 #include "url.h"
48
49 #include "hls_sample_encryption.h"
50
51 #define INITIAL_BUFFER_SIZE 32768
52
53 #define MAX_FIELD_LEN 64
54 #define MAX_CHARACTERISTICS_LEN 512
55
56 #define MPEG_TIME_BASE 90000
57 #define MPEG_TIME_BASE_Q (AVRational){1, MPEG_TIME_BASE}
58
59 /*
60 * An apple http stream consists of a playlist with media segment files,
61 * played sequentially. There may be several playlists with the same
62 * video content, in different bandwidth variants, that are played in
63 * parallel (preferably only one bandwidth variant at a time). In this case,
64 * the user supplied the url to a main playlist that only lists the variant
65 * playlists.
66 *
67 * If the main playlist doesn't point at any variants, we still create
68 * one anonymous toplevel variant for this, to maintain the structure.
69 */
70
71 enum KeyType {
72 KEY_NONE,
73 KEY_AES_128,
74 KEY_SAMPLE_AES
75 };
76
77 struct segment {
78 int64_t duration;
79 int64_t url_offset;
80 int64_t size;
81 char *url;
82 char *key;
83 enum KeyType key_type;
84 uint8_t iv[16];
85 /* associated Media Initialization Section, treated as a segment */
86 struct segment *init_section;
87 };
88
89 struct rendition;
90
91 enum PlaylistType {
92 PLS_TYPE_UNSPECIFIED,
93 PLS_TYPE_EVENT,
94 PLS_TYPE_VOD
95 };
96
97 /*
98 * Each playlist has its own demuxer. If it currently is active,
99 * it has an open AVIOContext too, and potentially an AVPacket
100 * containing the next packet from this stream.
101 */
102 struct playlist {
103 char url[MAX_URL_SIZE];
104 FFIOContext pb;
105 uint8_t* read_buffer;
106 AVIOContext *input;
107 int input_read_done;
108 int input_reuse;
109 AVIOContext *input_next;
110 int input_next_requested;
111 AVFormatContext *parent;
112 int index;
113 AVFormatContext *ctx;
114 AVPacket *pkt;
115 int has_noheader_flag;
116
117 /* main demuxer streams associated with this playlist
118 * indexed by the subdemuxer stream indexes */
119 AVStream **main_streams;
120 int n_main_streams;
121
122 int finished;
123 enum PlaylistType type;
124 int64_t target_duration;
125 int64_t start_seq_no;
126 int time_offset_flag;
127 int64_t start_time_offset;
128 int n_segments;
129 struct segment **segments;
130 int needed;
131 int broken;
132 int64_t cur_seq_no;
133 int64_t last_seq_no;
134 int64_t first_read_seq_no;
135 int m3u8_hold_counters;
136 int64_t cur_seg_offset;
137 int64_t last_load_time;
138
139 /* Currently active Media Initialization Section */
140 struct segment *cur_init_section;
141 uint8_t *init_sec_buf;
142 unsigned int init_sec_buf_size;
143 unsigned int init_sec_data_len;
144 unsigned int init_sec_buf_read_offset;
145
146 char key_url[MAX_URL_SIZE];
147 uint8_t key[16];
148
149 /* ID3 timestamp handling (elementary audio streams have ID3 timestamps
150 * (and possibly other ID3 tags) in the beginning of each segment) */
151 int is_id3_timestamped; /* -1: not yet known */
152 int64_t id3_mpegts_timestamp; /* in mpegts tb */
153 int64_t id3_offset; /* in stream original tb */
154 uint8_t* id3_buf; /* temp buffer for id3 parsing */
155 unsigned int id3_buf_size;
156 AVDictionary *id3_initial; /* data from first id3 tag */
157 int id3_found; /* ID3 tag found at some point */
158 int id3_changed; /* ID3 tag data has changed at some point */
159 ID3v2ExtraMeta *id3_deferred_extra; /* stored here until subdemuxer is opened */
160
161 HLSAudioSetupInfo audio_setup_info;
162
163 /* Constant offset of this playlist's DTS baseline relative to
164 * c->first_timestamp. */
165 int64_t ts_offset;
166 int64_t seek_timestamp;
167 int seek_flags;
168 int seek_stream_index; /* into subdemuxer stream array */
169
170 /* Renditions associated with this playlist, if any.
171 * Alternative rendition playlists have a single rendition associated
172 * with them, and variant main Media Playlists may have
173 * multiple (playlist-less) renditions associated with them. */
174 int n_renditions;
175 struct rendition **renditions;
176
177 /* Media Initialization Sections (EXT-X-MAP) associated with this
178 * playlist, if any. */
179 int n_init_sections;
180 struct segment **init_sections;
181 int is_subtitle; /* Indicates if it's a subtitle playlist */
182
183 /* Some HLS streams repeat the current timed-ID3 metadata at every segment
184 * boundary so late-joining clients get a fresh copy. Track the last seen
185 * value to avoid raising AVSTREAM_EVENT_FLAG_METADATA_UPDATED spuriously
186 * on those duplicates. timed_id3_stream_index is the subdemuxer stream
187 * index of the first timed-ID3 stream seen; -1 if none. */
188 AVDictionary *timed_id3_metadata;
189 int timed_id3_stream_index;
190 };
191
192 /*
193 * Renditions are e.g. alternative subtitle or audio streams.
194 * The rendition may either be an external playlist or it may be
195 * contained in the main Media Playlist of the variant (in which case
196 * playlist is NULL).
197 */
198 struct rendition {
199 enum AVMediaType type;
200 struct playlist *playlist;
201 char group_id[MAX_FIELD_LEN];
202 char language[MAX_FIELD_LEN];
203 char name[MAX_FIELD_LEN];
204 int disposition;
205 };
206
207 struct variant {
208 int bandwidth;
209
210 /* every variant contains at least the main Media Playlist in index 0 */
211 int n_playlists;
212 struct playlist **playlists;
213
214 char audio_group[MAX_FIELD_LEN];
215 char video_group[MAX_FIELD_LEN];
216 char subtitles_group[MAX_FIELD_LEN];
217 };
218
219 typedef struct HLSContext {
220 AVClass *class;
221 AVFormatContext *ctx;
222 int n_variants;
223 struct variant **variants;
224 int n_playlists;
225 struct playlist **playlists;
226 int n_renditions;
227 struct rendition **renditions;
228
229 int64_t cur_seq_no;
230 int m3u8_hold_counters;
231 int live_start_index;
232 int prefer_x_start;
233 int first_packet;
234 int64_t first_timestamp;
235 struct playlist *first_timestamp_pls;
236 int first_timestamp_locked;
237 int64_t cur_timestamp;
238 AVIOInterruptCB *interrupt_callback;
239 AVDictionary *avio_opts;
240 AVDictionary *seg_format_opts;
241 char *allowed_extensions;
242 char *allowed_segment_extensions;
243 int extension_picky;
244 int max_reload;
245 int http_persistent;
246 int http_multiple;
247 int http_seekable;
248 int seg_max_retry;
249 AVIOContext *playlist_pb;
250 HLSCryptoContext crypto_ctx;
251 } HLSContext;
252
253 17 static void free_segment_dynarray(struct segment **segments, int n_segments)
254 {
255 int i;
256
2/2
✓ Branch 0 taken 80 times.
✓ Branch 1 taken 17 times.
97 for (i = 0; i < n_segments; i++) {
257 80 av_freep(&segments[i]->key);
258 80 av_freep(&segments[i]->url);
259 80 av_freep(&segments[i]);
260 }
261 17 }
262
263 17 static void free_segment_list(struct playlist *pls)
264 {
265 17 free_segment_dynarray(pls->segments, pls->n_segments);
266 17 av_freep(&pls->segments);
267 17 pls->n_segments = 0;
268 17 }
269
270 17 static void free_init_section_list(struct playlist *pls)
271 {
272 int i;
273
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 17 times.
18 for (i = 0; i < pls->n_init_sections; i++) {
274 1 av_freep(&pls->init_sections[i]->key);
275 1 av_freep(&pls->init_sections[i]->url);
276 1 av_freep(&pls->init_sections[i]);
277 }
278 17 av_freep(&pls->init_sections);
279 17 pls->n_init_sections = 0;
280 17 }
281
282 16 static void free_playlist_list(HLSContext *c)
283 {
284 int i;
285
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 16 times.
33 for (i = 0; i < c->n_playlists; i++) {
286 17 struct playlist *pls = c->playlists[i];
287 17 free_segment_list(pls);
288 17 free_init_section_list(pls);
289 17 av_freep(&pls->main_streams);
290 17 av_freep(&pls->renditions);
291 17 av_freep(&pls->id3_buf);
292 17 av_dict_free(&pls->id3_initial);
293 17 av_dict_free(&pls->timed_id3_metadata);
294 17 ff_id3v2_free_extra_meta(&pls->id3_deferred_extra);
295 17 av_freep(&pls->init_sec_buf);
296 17 av_packet_free(&pls->pkt);
297 17 av_freep(&pls->pb.pub.buffer);
298 17 ff_format_io_close(c->ctx, &pls->input);
299 17 pls->input_read_done = 0;
300 17 pls->input_reuse = 0;
301 17 ff_format_io_close(c->ctx, &pls->input_next);
302 17 pls->input_next_requested = 0;
303
1/2
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
17 if (pls->ctx) {
304 17 pls->ctx->pb = NULL;
305 17 avformat_close_input(&pls->ctx);
306 }
307 17 av_free(pls);
308 }
309 16 av_freep(&c->playlists);
310 16 c->n_playlists = 0;
311 16 }
312
313 16 static void free_variant_list(HLSContext *c)
314 {
315 int i;
316
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 16 times.
32 for (i = 0; i < c->n_variants; i++) {
317 16 struct variant *var = c->variants[i];
318 16 av_freep(&var->playlists);
319 16 av_free(var);
320 }
321 16 av_freep(&c->variants);
322 16 c->n_variants = 0;
323 16 }
324
325 16 static void free_rendition_list(HLSContext *c)
326 {
327 int i;
328
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 16 times.
17 for (i = 0; i < c->n_renditions; i++)
329 1 av_freep(&c->renditions[i]);
330 16 av_freep(&c->renditions);
331 16 c->n_renditions = 0;
332 16 }
333
334 17 static struct playlist *new_playlist(HLSContext *c, const char *url,
335 const char *base)
336 {
337 struct playlist *pls;
338 char abs_url[MAX_URL_SIZE];
339
340 17 ff_make_absolute_url(abs_url, sizeof(abs_url), base, url);
341
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (!abs_url[0])
342 return NULL;
343
344
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 17 times.
18 for (int i = 0; i < c->n_playlists; i++) {
345
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!strcmp(c->playlists[i]->url, abs_url))
346 return c->playlists[i];
347 }
348
349 17 pls = av_mallocz(sizeof(struct playlist));
350
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (!pls)
351 return NULL;
352 17 pls->pkt = av_packet_alloc();
353
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (!pls->pkt) {
354 av_free(pls);
355 return NULL;
356 }
357 17 av_strlcpy(pls->url, abs_url, sizeof(pls->url));
358 17 pls->ts_offset = AV_NOPTS_VALUE;
359 17 pls->seek_timestamp = AV_NOPTS_VALUE;
360 17 pls->first_read_seq_no = -1;
361
362 17 pls->is_id3_timestamped = -1;
363 17 pls->id3_mpegts_timestamp = AV_NOPTS_VALUE;
364 17 pls->timed_id3_stream_index = -1;
365
366 17 dynarray_add(&c->playlists, &c->n_playlists, pls);
367 17 return pls;
368 }
369
370 struct variant_info {
371 char bandwidth[20];
372 /* variant group ids: */
373 char audio[MAX_FIELD_LEN];
374 char video[MAX_FIELD_LEN];
375 char subtitles[MAX_FIELD_LEN];
376 };
377
378 16 static struct variant *new_variant(HLSContext *c, struct variant_info *info,
379 const char *url, const char *base)
380 {
381 struct variant *var;
382 struct playlist *pls;
383
384 16 pls = new_playlist(c, url, base);
385
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 if (!pls)
386 return NULL;
387
388 16 var = av_mallocz(sizeof(struct variant));
389
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 if (!var)
390 return NULL;
391
392
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 15 times.
16 if (info) {
393 1 var->bandwidth = atoi(info->bandwidth);
394 1 strcpy(var->audio_group, info->audio);
395 1 strcpy(var->video_group, info->video);
396 1 strcpy(var->subtitles_group, info->subtitles);
397 }
398
399 16 dynarray_add(&c->variants, &c->n_variants, var);
400 16 dynarray_add(&var->playlists, &var->n_playlists, pls);
401 16 return var;
402 }
403
404 4 static void handle_variant_args(void *context, const char *key,
405 int key_len, char **dest, int *dest_len)
406 {
407 4 struct variant_info *info = context;
408
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
4 if (!strncmp(key, "BANDWIDTH=", key_len)) {
409 1 *dest = info->bandwidth;
410 1 *dest_len = sizeof(info->bandwidth);
411
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 } else if (!strncmp(key, "AUDIO=", key_len)) {
412 1 *dest = info->audio;
413 1 *dest_len = sizeof(info->audio);
414
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 } else if (!strncmp(key, "VIDEO=", key_len)) {
415 *dest = info->video;
416 *dest_len = sizeof(info->video);
417
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 } else if (!strncmp(key, "SUBTITLES=", key_len)) {
418 *dest = info->subtitles;
419 *dest_len = sizeof(info->subtitles);
420 }
421 4 }
422
423 struct key_info {
424 char uri[MAX_URL_SIZE];
425 char method[11];
426 char iv[35];
427 };
428
429 static void handle_key_args(void *context, const char *key,
430 int key_len, char **dest, int *dest_len)
431 {
432 struct key_info *info = context;
433 if (!strncmp(key, "METHOD=", key_len)) {
434 *dest = info->method;
435 *dest_len = sizeof(info->method);
436 } else if (!strncmp(key, "URI=", key_len)) {
437 *dest = info->uri;
438 *dest_len = sizeof(info->uri);
439 } else if (!strncmp(key, "IV=", key_len)) {
440 *dest = info->iv;
441 *dest_len = sizeof(info->iv);
442 }
443 }
444
445 struct init_section_info {
446 char uri[MAX_URL_SIZE];
447 char byterange[32];
448 };
449
450 1 static struct segment *new_init_section(struct playlist *pls,
451 struct init_section_info *info,
452 const char *url_base)
453 {
454 struct segment *sec;
455 1 char tmp_str[MAX_URL_SIZE], *ptr = tmp_str;
456
457
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!info->uri[0])
458 return NULL;
459
460 1 sec = av_mallocz(sizeof(*sec));
461
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!sec)
462 return NULL;
463
464
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (!av_strncasecmp(info->uri, "data:", 5)) {
465 ptr = info->uri;
466 } else {
467 1 ff_make_absolute_url(tmp_str, sizeof(tmp_str), url_base, info->uri);
468
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!tmp_str[0]) {
469 av_free(sec);
470 return NULL;
471 }
472 }
473 1 sec->url = av_strdup(ptr);
474
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!sec->url) {
475 av_free(sec);
476 return NULL;
477 }
478
479
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (info->byterange[0]) {
480 sec->size = strtoll(info->byterange, NULL, 10);
481 ptr = strchr(info->byterange, '@');
482 if (ptr)
483 sec->url_offset = strtoll(ptr+1, NULL, 10);
484 if (sec->size < 0 || sec->url_offset < 0 || sec->size > INT64_MAX - sec->url_offset) {
485 av_freep(&sec->url);
486 av_free(sec);
487 return NULL;
488 }
489 } else {
490 /* the entire file is the init section */
491 1 sec->size = -1;
492 }
493
494 1 dynarray_add(&pls->init_sections, &pls->n_init_sections, sec);
495
496 1 return sec;
497 }
498
499 1 static void handle_init_section_args(void *context, const char *key,
500 int key_len, char **dest, int *dest_len)
501 {
502 1 struct init_section_info *info = context;
503
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (!strncmp(key, "URI=", key_len)) {
504 1 *dest = info->uri;
505 1 *dest_len = sizeof(info->uri);
506 } else if (!strncmp(key, "BYTERANGE=", key_len)) {
507 *dest = info->byterange;
508 *dest_len = sizeof(info->byterange);
509 }
510 1 }
511
512 struct rendition_info {
513 char type[16];
514 char uri[MAX_URL_SIZE];
515 char group_id[MAX_FIELD_LEN];
516 char language[MAX_FIELD_LEN];
517 char assoc_language[MAX_FIELD_LEN];
518 char name[MAX_FIELD_LEN];
519 char defaultr[4];
520 char forced[4];
521 char characteristics[MAX_CHARACTERISTICS_LEN];
522 };
523
524 1 static struct rendition *new_rendition(HLSContext *c, struct rendition_info *info,
525 const char *url_base)
526 {
527 struct rendition *rend;
528 1 enum AVMediaType type = AVMEDIA_TYPE_UNKNOWN;
529 char *characteristic;
530 char *chr_ptr;
531 char *saveptr;
532
533
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (!strcmp(info->type, "AUDIO"))
534 1 type = AVMEDIA_TYPE_AUDIO;
535 else if (!strcmp(info->type, "VIDEO"))
536 type = AVMEDIA_TYPE_VIDEO;
537 else if (!strcmp(info->type, "SUBTITLES"))
538 type = AVMEDIA_TYPE_SUBTITLE;
539 else if (!strcmp(info->type, "CLOSED-CAPTIONS"))
540 /* CLOSED-CAPTIONS is ignored since we do not support CEA-608 CC in
541 * AVC SEI RBSP anyway */
542 return NULL;
543
544
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (type == AVMEDIA_TYPE_UNKNOWN) {
545 av_log(c->ctx, AV_LOG_WARNING, "Can't support the type: %s\n", info->type);
546 return NULL;
547 }
548
549 /* URI is mandatory for subtitles as per spec */
550
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1 if (type == AVMEDIA_TYPE_SUBTITLE && !info->uri[0]) {
551 av_log(c->ctx, AV_LOG_ERROR, "The URI tag is REQUIRED for subtitle.\n");
552 return NULL;
553 }
554
555 1 rend = av_mallocz(sizeof(struct rendition));
556
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!rend)
557 return NULL;
558
559 1 dynarray_add(&c->renditions, &c->n_renditions, rend);
560
561 1 rend->type = type;
562 1 strcpy(rend->group_id, info->group_id);
563 1 strcpy(rend->language, info->language);
564 1 strcpy(rend->name, info->name);
565
566 /* add the playlist if this is an external rendition */
567
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (info->uri[0]) {
568 1 rend->playlist = new_playlist(c, info->uri, url_base);
569
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (rend->playlist) {
570
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (type == AVMEDIA_TYPE_SUBTITLE) {
571 rend->playlist->is_subtitle = 1;
572 rend->playlist->is_id3_timestamped = 0;
573 }
574 1 dynarray_add(&rend->playlist->renditions,
575 &rend->playlist->n_renditions, rend);
576 }
577 }
578
579
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (info->assoc_language[0]) {
580 size_t langlen = strlen(rend->language);
581 if (langlen < sizeof(rend->language) - 3) {
582 size_t assoc_len;
583 rend->language[langlen] = ',';
584 assoc_len = av_strlcpy(rend->language + langlen + 1,
585 info->assoc_language,
586 sizeof(rend->language) - langlen - 1);
587 if (langlen + assoc_len + 2 > sizeof(rend->language)) // truncation occurred
588 av_log(c->ctx, AV_LOG_WARNING, "Truncated rendition language: %s\n",
589 info->assoc_language);
590 }
591 }
592
593
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (!strcmp(info->defaultr, "YES"))
594 1 rend->disposition |= AV_DISPOSITION_DEFAULT;
595
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!strcmp(info->forced, "YES"))
596 rend->disposition |= AV_DISPOSITION_FORCED;
597
598 1 chr_ptr = info->characteristics;
599
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 while ((characteristic = av_strtok(chr_ptr, ",", &saveptr))) {
600 if (!strcmp(characteristic, "public.accessibility.describes-music-and-sound"))
601 rend->disposition |= AV_DISPOSITION_HEARING_IMPAIRED;
602 else if (!strcmp(characteristic, "public.accessibility.describes-video"))
603 rend->disposition |= AV_DISPOSITION_VISUAL_IMPAIRED;
604
605 chr_ptr = NULL;
606 }
607
608 1 return rend;
609 }
610
611 6 static void handle_rendition_args(void *vinfo, const char *key,
612 int key_len, char **dest, int *dest_len)
613 {
614 6 struct rendition_info *info = vinfo;
615
616
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5 times.
6 if (!strncmp(key, "TYPE=", key_len)) {
617 1 *dest = info->type;
618 1 *dest_len = sizeof(info->type);
619
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
5 } else if (!strncmp(key, "URI=", key_len)) {
620 1 *dest = info->uri;
621 1 *dest_len = sizeof(info->uri);
622
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
4 } else if (!strncmp(key, "GROUP-ID=", key_len)) {
623 1 *dest = info->group_id;
624 1 *dest_len = sizeof(info->group_id);
625
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 } else if (!strncmp(key, "LANGUAGE=", key_len)) {
626 *dest = info->language;
627 *dest_len = sizeof(info->language);
628
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 } else if (!strncmp(key, "ASSOC-LANGUAGE=", key_len)) {
629 *dest = info->assoc_language;
630 *dest_len = sizeof(info->assoc_language);
631
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 } else if (!strncmp(key, "NAME=", key_len)) {
632 1 *dest = info->name;
633 1 *dest_len = sizeof(info->name);
634
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 } else if (!strncmp(key, "DEFAULT=", key_len)) {
635 1 *dest = info->defaultr;
636 1 *dest_len = sizeof(info->defaultr);
637
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 } else if (!strncmp(key, "FORCED=", key_len)) {
638 *dest = info->forced;
639 *dest_len = sizeof(info->forced);
640
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 } else if (!strncmp(key, "CHARACTERISTICS=", key_len)) {
641 *dest = info->characteristics;
642 *dest_len = sizeof(info->characteristics);
643 }
644 /*
645 * ignored:
646 * - AUTOSELECT: client may autoselect based on e.g. system language
647 * - INSTREAM-ID: EIA-608 closed caption number ("CC1".."CC4")
648 */
649 6 }
650
651 /* used by parse_playlist to allocate a new variant+playlist when the
652 * playlist is detected to be a Media Playlist (not Master Playlist)
653 * and we have no parent Master Playlist (parsing of which would have
654 * allocated the variant and playlist already)
655 * *pls == NULL => Master Playlist or parentless Media Playlist
656 * *pls != NULL => parented Media Playlist, playlist+variant allocated */
657 115 static int ensure_playlist(HLSContext *c, struct playlist **pls, const char *url)
658 {
659
2/2
✓ Branch 0 taken 100 times.
✓ Branch 1 taken 15 times.
115 if (*pls)
660 100 return 0;
661
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
15 if (!new_variant(c, NULL, url, NULL))
662 return AVERROR(ENOMEM);
663 15 *pls = c->playlists[c->n_playlists - 1];
664 15 return 0;
665 }
666
667 50 static int is_native_http(AVIOContext *pb)
668 {
669 #if !CONFIG_HTTP_PROTOCOL
670 return 0;
671 #else
672 50 URLContext *uc = ffio_geturlcontext(pb);
673
2/4
✓ Branch 0 taken 50 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 50 times.
50 return uc && !strncmp(uc->prot->name, "http", 4);
674 #endif
675 }
676
677 static int open_url_keepalive(AVFormatContext *s, AVIOContext **pb,
678 const char *url, AVDictionary **options)
679 {
680 #if !CONFIG_HTTP_PROTOCOL
681 return AVERROR_PROTOCOL_NOT_FOUND;
682 #else
683 int ret;
684 URLContext *uc = ffio_geturlcontext(*pb);
685 av_assert0(uc);
686 (*pb)->eof_reached = 0;
687 ret = ff_http_do_new_request2(uc, url, options);
688 if (ret < 0) {
689 ff_format_io_close(s, pb);
690 }
691 return ret;
692 #endif
693 }
694
695 111 static int open_url(AVFormatContext *s, AVIOContext **pb, const char *url,
696 AVDictionary **opts, AVDictionary *opts2, int *is_http_out)
697 {
698 111 HLSContext *c = s->priv_data;
699 111 AVDictionary *tmp = NULL;
700 111 const char *proto_name = NULL;
701 int ret;
702 111 int is_http = 0;
703
704
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 111 times.
111 if (av_strstart(url, "crypto", NULL)) {
705 if (url[6] == '+' || url[6] == ':')
706 proto_name = avio_find_protocol_name(url + 7);
707
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 111 times.
111 } else if (av_strstart(url, "data", NULL)) {
708 if (url[4] == '+' || url[4] == ':')
709 proto_name = avio_find_protocol_name(url + 5);
710 }
711
712
1/2
✓ Branch 0 taken 111 times.
✗ Branch 1 not taken.
111 if (!proto_name)
713 111 proto_name = avio_find_protocol_name(url);
714
715
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 111 times.
111 if (!proto_name)
716 return AVERROR_INVALIDDATA;
717
718 // only http(s) & file are allowed
719
1/2
✓ Branch 1 taken 111 times.
✗ Branch 2 not taken.
111 if (av_strstart(proto_name, "file", NULL)) {
720
2/4
✓ Branch 0 taken 111 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 111 times.
111 if (strcmp(c->allowed_extensions, "ALL") && !av_match_ext(url, c->allowed_extensions)) {
721 av_log(s, AV_LOG_ERROR,
722 "Filename extension of \'%s\' is not a common multimedia extension, blocked for security reasons.\n"
723 "If you wish to override this adjust allowed_extensions, you can set it to \'ALL\' to allow all\n",
724 url);
725 return AVERROR_INVALIDDATA;
726 }
727 } else if (av_strstart(proto_name, "http", NULL)) {
728 is_http = 1;
729 } else if (av_strstart(proto_name, "data", NULL)) {
730 ;
731 } else
732 return AVERROR_INVALIDDATA;
733
734
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 111 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
111 if (!strncmp(proto_name, url, strlen(proto_name)) && url[strlen(proto_name)] == ':')
735 ;
736
1/6
✗ Branch 1 not taken.
✓ Branch 2 taken 111 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
111 else if (av_strstart(url, "crypto", NULL) && !strncmp(proto_name, url + 7, strlen(proto_name)) && url[7 + strlen(proto_name)] == ':')
737 ;
738
1/6
✗ Branch 1 not taken.
✓ Branch 2 taken 111 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
111 else if (av_strstart(url, "data", NULL) && !strncmp(proto_name, url + 5, strlen(proto_name)) && url[5 + strlen(proto_name)] == ':')
739 ;
740
2/4
✓ Branch 0 taken 111 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 111 times.
111 else if (strcmp(proto_name, "file") || !strncmp(url, "file,", 5))
741 return AVERROR_INVALIDDATA;
742
743 111 av_dict_copy(&tmp, *opts, 0);
744 111 av_dict_copy(&tmp, opts2, 0);
745
746
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 111 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
111 if (is_http && c->http_persistent && is_native_http(*pb)) {
747 ret = open_url_keepalive(c->ctx, pb, url, &tmp);
748 if (ret == AVERROR_EXIT) {
749 av_dict_free(&tmp);
750 return ret;
751 } else if (ret < 0) {
752 if (ret != AVERROR_EOF)
753 av_log(s, AV_LOG_WARNING,
754 "keepalive request failed for '%s' with error: '%s' when opening url, retrying with new connection\n",
755 url, av_err2str(ret));
756 av_dict_copy(&tmp, *opts, 0);
757 av_dict_copy(&tmp, opts2, 0);
758 ret = s->io_open(s, pb, url, AVIO_FLAG_READ, &tmp);
759 }
760 } else {
761
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 111 times.
111 av_assert0(!*pb);
762 111 ret = s->io_open(s, pb, url, AVIO_FLAG_READ, &tmp);
763 }
764
1/2
✓ Branch 0 taken 111 times.
✗ Branch 1 not taken.
111 if (ret >= 0) {
765 // update cookies on http response with setcookies.
766 111 char *new_cookies = NULL;
767
768
1/2
✓ Branch 0 taken 111 times.
✗ Branch 1 not taken.
111 if (!(s->flags & AVFMT_FLAG_CUSTOM_IO))
769 111 av_opt_get(*pb, "cookies", AV_OPT_SEARCH_CHILDREN, (uint8_t**)&new_cookies);
770
771
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 111 times.
111 if (new_cookies)
772 av_dict_set(opts, "cookies", new_cookies, AV_DICT_DONT_STRDUP_VAL);
773 }
774
775 111 av_dict_free(&tmp);
776
777
2/2
✓ Branch 0 taken 109 times.
✓ Branch 1 taken 2 times.
111 if (is_http_out)
778 109 *is_http_out = is_http;
779
780 111 return ret;
781 }
782
783 160 static int test_segment(AVFormatContext *s, const AVInputFormat *in_fmt, struct playlist *pls, struct segment *seg)
784 {
785 160 HLSContext *c = s->priv_data;
786 160 int matchA = 3;
787 160 int matchF = 0;
788
789
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 160 times.
160 if (!c->extension_picky)
790 return 0;
791
792
1/2
✓ Branch 0 taken 160 times.
✗ Branch 1 not taken.
160 if (strcmp(c->allowed_segment_extensions, "ALL"))
793 320 matchA = av_match_ext (seg->url, c->allowed_segment_extensions)
794
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 160 times.
160 + 2*(ff_match_url_ext(seg->url, c->allowed_segment_extensions) > 0);
795
796
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 160 times.
160 if (!matchA) {
797 av_log(s, AV_LOG_ERROR, "URL %s is not in allowed_segment_extensions, consider updating hls.c and submitting a patch to ffmpeg-devel, if this should be added\n", seg->url);
798 return AVERROR_INVALIDDATA;
799 }
800
801
2/2
✓ Branch 0 taken 80 times.
✓ Branch 1 taken 80 times.
160 if (in_fmt) {
802
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 76 times.
80 if (in_fmt->extensions) {
803 4 matchF = av_match_ext( seg->url, in_fmt->extensions)
804
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 + 2*(ff_match_url_ext(seg->url, in_fmt->extensions) > 0);
805 // Youtube uses aac files with .ts extension
806
3/4
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
4 if(av_match_name("mp4", in_fmt->name) || av_match_name("aac", in_fmt->name)) {
807 1 matchF |= av_match_ext( seg->url, "ts,m2t,m2ts,mts,mpg,m4s,mpeg,mpegts,cmfv,cmfa")
808
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 + 2*(ff_match_url_ext(seg->url, "ts,m2t,m2ts,mts,mpg,m4s,mpeg,mpegts,cmfv,cmfa") > 0);
809 }
810
1/2
✓ Branch 0 taken 76 times.
✗ Branch 1 not taken.
76 } else if (!strcmp(in_fmt->name, "mpegts")) {
811 76 const char *str = "ts,m2t,m2ts,mts,mpg,m4s,mpeg,mpegts"
812 ",html" // https://flash1.bogulus.cfd/
813 ;
814 152 matchF = av_match_ext( seg->url, str)
815
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 76 times.
76 + 2*(ff_match_url_ext(seg->url, str) > 0);
816 } else if (!strcmp(in_fmt->name, "webvtt")) {
817 matchF = av_match_ext( seg->url, "vtt,webvtt")
818 + 2*(ff_match_url_ext(seg->url, "vtt,webvtt") > 0);
819 }
820
821
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 80 times.
80 if (!(matchA & matchF)) {
822 av_log(s, AV_LOG_ERROR, "detected format %s extension %s mismatches allowed extensions in url %s\n", in_fmt->name, in_fmt->extensions ? in_fmt->extensions : "none", seg->url);
823 return AVERROR_INVALIDDATA;
824 }
825 }
826
827 160 return 0;
828 }
829
830 18 static int parse_playlist(HLSContext *c, const char *url,
831 struct playlist *pls, AVIOContext *in)
832 {
833 18 int ret = 0, is_segment = 0, is_variant = 0;
834 18 int64_t duration = 0;
835 18 enum KeyType key_type = KEY_NONE;
836 18 uint8_t iv[16] = "";
837 18 int has_iv = 0;
838 18 char key[MAX_URL_SIZE] = "";
839 char line[MAX_URL_SIZE];
840 const char *ptr;
841 18 int close_in = 0;
842 18 int64_t seg_offset = 0;
843 18 int64_t seg_size = -1;
844 18 uint8_t *new_url = NULL;
845 struct variant_info variant_info;
846 char tmp_str[MAX_URL_SIZE];
847 18 struct segment *cur_init_section = NULL;
848 18 int is_http = av_strstart(url, "http", NULL);
849 18 struct segment **prev_segments = NULL;
850 18 int prev_n_segments = 0;
851 18 int64_t prev_start_seq_no = -1;
852 18 int64_t load_start = av_gettime_relative();
853
854
1/8
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
18 if (is_http && !in && c->http_persistent && c->playlist_pb) {
855 in = c->playlist_pb;
856 ret = open_url_keepalive(c->ctx, &c->playlist_pb, url, NULL);
857 if (ret == AVERROR_EXIT) {
858 return ret;
859 } else if (ret < 0) {
860 if (ret != AVERROR_EOF)
861 av_log(c->ctx, AV_LOG_WARNING,
862 "keepalive request failed for '%s' with error: '%s' when parsing playlist\n",
863 url, av_err2str(ret));
864 in = NULL;
865 }
866 }
867
868
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 16 times.
18 if (!in) {
869 2 AVDictionary *opts = NULL;
870 2 av_dict_copy(&opts, c->avio_opts, 0);
871
872
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (c->http_persistent)
873 2 av_dict_set(&opts, "multiple_requests", "1", 0);
874
875 2 ret = open_url(c->ctx, &in, url, &opts, NULL, NULL);
876 2 av_dict_free(&opts);
877
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (ret < 0)
878 return ret;
879
880
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
2 if (is_http && c->http_persistent && is_native_http(in))
881 c->playlist_pb = in;
882 else
883 2 close_in = 1;
884 }
885
886
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
18 if (av_opt_get(in, "location", AV_OPT_SEARCH_CHILDREN, &new_url) >= 0)
887 url = new_url;
888
889 18 ff_get_chomp_line(in, line, sizeof(line));
890
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 if (strcmp(line, "#EXTM3U")) {
891 ret = AVERROR_INVALIDDATA;
892 goto fail;
893 }
894
895
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 16 times.
18 if (pls) {
896 2 prev_start_seq_no = pls->start_seq_no;
897 2 prev_segments = pls->segments;
898 2 prev_n_segments = pls->n_segments;
899 2 pls->segments = NULL;
900 2 pls->n_segments = 0;
901
902 2 pls->finished = 0;
903 2 pls->type = PLS_TYPE_UNSPECIFIED;
904 }
905
2/2
✓ Branch 1 taken 273 times.
✓ Branch 2 taken 18 times.
291 while (!avio_feof(in)) {
906 273 ff_get_chomp_line(in, line, sizeof(line));
907
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 272 times.
273 if (av_strstart(line, "#EXT-X-STREAM-INF:", &ptr)) {
908
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (pls) {
909 av_log(c->ctx, AV_LOG_ERROR,
910 "Master Playlist tag found in a Media Playlist\n");
911 ret = AVERROR_INVALIDDATA;
912 goto fail;
913 }
914 1 is_variant = 1;
915 1 memset(&variant_info, 0, sizeof(variant_info));
916 1 ff_parse_key_value(ptr, handle_variant_args, &variant_info);
917
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 272 times.
272 } else if (av_strstart(line, "#EXT-X-KEY:", &ptr)) {
918 struct key_info info = {{0}};
919 ff_parse_key_value(ptr, handle_key_args, &info);
920 key_type = KEY_NONE;
921 has_iv = 0;
922 if (!strcmp(info.method, "AES-128"))
923 key_type = KEY_AES_128;
924 if (!strcmp(info.method, "SAMPLE-AES"))
925 key_type = KEY_SAMPLE_AES;
926 if (!av_strncasecmp(info.iv, "0x", 2)) {
927 ff_hex_to_data(iv, info.iv + 2);
928 has_iv = 1;
929 }
930 av_strlcpy(key, info.uri, sizeof(key));
931
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 271 times.
272 } else if (av_strstart(line, "#EXT-X-MEDIA:", &ptr)) {
932 1 struct rendition_info info = {{0}};
933
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (pls) {
934 av_log(c->ctx, AV_LOG_ERROR,
935 "Master Playlist tag found in a Media Playlist\n");
936 ret = AVERROR_INVALIDDATA;
937 goto fail;
938 }
939 1 ff_parse_key_value(ptr, handle_rendition_args, &info);
940 1 new_rendition(c, &info, url);
941
2/2
✓ Branch 1 taken 17 times.
✓ Branch 2 taken 254 times.
271 } else if (av_strstart(line, "#EXT-X-TARGETDURATION:", &ptr)) {
942 int64_t t;
943 17 ret = ensure_playlist(c, &pls, url);
944
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (ret < 0)
945 goto fail;
946 17 t = strtoll(ptr, NULL, 10);
947
2/4
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 17 times.
17 if (t < 0 || t >= INT64_MAX / AV_TIME_BASE) {
948 ret = AVERROR_INVALIDDATA;
949 goto fail;
950 }
951 17 pls->target_duration = t * AV_TIME_BASE;
952
2/2
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 238 times.
254 } else if (av_strstart(line, "#EXT-X-MEDIA-SEQUENCE:", &ptr)) {
953 uint64_t seq_no;
954 16 ret = ensure_playlist(c, &pls, url);
955
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 if (ret < 0)
956 goto fail;
957 16 seq_no = strtoull(ptr, NULL, 10);
958
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 if (seq_no > INT64_MAX/2) {
959 av_log(c->ctx, AV_LOG_DEBUG, "MEDIA-SEQUENCE higher than "
960 "INT64_MAX/2, mask out the highest bit\n");
961 seq_no &= INT64_MAX/2;
962 }
963 16 pls->start_seq_no = seq_no;
964
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 237 times.
238 } else if (av_strstart(line, "#EXT-X-PLAYLIST-TYPE:", &ptr)) {
965 1 ret = ensure_playlist(c, &pls, url);
966
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (ret < 0)
967 goto fail;
968
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (!strcmp(ptr, "EVENT"))
969 1 pls->type = PLS_TYPE_EVENT;
970 else if (!strcmp(ptr, "VOD"))
971 pls->type = PLS_TYPE_VOD;
972
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 236 times.
237 } else if (av_strstart(line, "#EXT-X-MAP:", &ptr)) {
973 1 struct init_section_info info = {{0}};
974 1 ret = ensure_playlist(c, &pls, url);
975
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (ret < 0)
976 goto fail;
977 1 ff_parse_key_value(ptr, handle_init_section_args, &info);
978 1 cur_init_section = new_init_section(pls, &info, url);
979
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!cur_init_section) {
980 ret = AVERROR(ENOMEM);
981 goto fail;
982 }
983 1 cur_init_section->key_type = key_type;
984
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (has_iv) {
985 memcpy(cur_init_section->iv, iv, sizeof(iv));
986 } else {
987 1 int64_t seq = pls->start_seq_no + pls->n_segments;
988 1 memset(cur_init_section->iv, 0, sizeof(cur_init_section->iv));
989 1 AV_WB64(cur_init_section->iv + 8, seq);
990 }
991
992
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (key_type != KEY_NONE) {
993 ff_make_absolute_url(tmp_str, sizeof(tmp_str), url, key);
994 if (!tmp_str[0]) {
995 av_free(cur_init_section);
996 ret = AVERROR_INVALIDDATA;
997 goto fail;
998 }
999 cur_init_section->key = av_strdup(tmp_str);
1000 if (!cur_init_section->key) {
1001 av_free(cur_init_section);
1002 ret = AVERROR(ENOMEM);
1003 goto fail;
1004 }
1005 } else {
1006 1 cur_init_section->key = NULL;
1007 }
1008
1009
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 236 times.
236 } else if (av_strstart(line, "#EXT-X-START:", &ptr)) {
1010 const char *time_offset_value = NULL;
1011 ret = ensure_playlist(c, &pls, url);
1012 if (ret < 0) {
1013 goto fail;
1014 }
1015 if (av_strstart(ptr, "TIME-OFFSET=", &time_offset_value)) {
1016 double offset = strtod(time_offset_value, NULL) * AV_TIME_BASE;
1017 if (offset >= -0x1p63 && offset < 0x1p63) {
1018 pls->start_time_offset = offset;
1019 pls->time_offset_flag = 1;
1020 } else {
1021 av_log(c->ctx, AV_LOG_WARNING, "TIME-OFFSET value is"
1022 "invalid, it will be ignored");
1023 }
1024 } else {
1025 av_log(c->ctx, AV_LOG_WARNING, "#EXT-X-START value is"
1026 "invalid, it will be ignored");
1027 continue;
1028 }
1029
2/2
✓ Branch 1 taken 15 times.
✓ Branch 2 taken 221 times.
236 } else if (av_strstart(line, "#EXT-X-ENDLIST", &ptr)) {
1030
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 if (pls)
1031 15 pls->finished = 1;
1032
2/2
✓ Branch 1 taken 80 times.
✓ Branch 2 taken 141 times.
221 } else if (av_strstart(line, "#EXTINF:", &ptr)) {
1033 80 double d = atof(ptr) * AV_TIME_BASE;
1034
3/6
✓ Branch 0 taken 80 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 80 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 80 times.
80 if (d < 0 || d > INT64_MAX || isnan(d)) {
1035 av_log(c->ctx, AV_LOG_WARNING, "EXTINF %f unsupported\n", d / AV_TIME_BASE);
1036 d = 0;
1037 }
1038 80 duration = d;
1039 80 is_segment = 1;
1040
2/2
✓ Branch 1 taken 20 times.
✓ Branch 2 taken 121 times.
141 } else if (av_strstart(line, "#EXT-X-BYTERANGE:", &ptr)) {
1041 20 seg_size = strtoll(ptr, NULL, 10);
1042 20 ptr = strchr(ptr, '@');
1043
1/2
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
20 if (ptr)
1044 20 seg_offset = strtoll(ptr+1, NULL, 10);
1045
2/4
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 20 times.
20 if (seg_size < 0 || seg_offset > INT64_MAX - seg_size) {
1046 ret = AVERROR_INVALIDDATA;
1047 goto fail;
1048 }
1049
2/2
✓ Branch 1 taken 21 times.
✓ Branch 2 taken 100 times.
121 } else if (av_strstart(line, "#", NULL)) {
1050 21 av_log(c->ctx, AV_LOG_VERBOSE, "Skip ('%s')\n", line);
1051 21 continue;
1052
2/2
✓ Branch 0 taken 81 times.
✓ Branch 1 taken 19 times.
100 } else if (line[0]) {
1053
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 80 times.
81 if (is_variant) {
1054
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (!new_variant(c, &variant_info, line, url)) {
1055 ret = AVERROR(ENOMEM);
1056 goto fail;
1057 }
1058 1 is_variant = 0;
1059 }
1060
2/2
✓ Branch 0 taken 80 times.
✓ Branch 1 taken 1 times.
81 if (is_segment) {
1061 struct segment *seg;
1062 80 ret = ensure_playlist(c, &pls, url);
1063
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 80 times.
80 if (ret < 0)
1064 goto fail;
1065 80 seg = av_malloc(sizeof(struct segment));
1066
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 80 times.
80 if (!seg) {
1067 ret = AVERROR(ENOMEM);
1068 goto fail;
1069 }
1070
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 80 times.
80 if (has_iv) {
1071 memcpy(seg->iv, iv, sizeof(iv));
1072 } else {
1073 80 uint64_t seq = pls->start_seq_no + (uint64_t)pls->n_segments;
1074 80 memset(seg->iv, 0, sizeof(seg->iv));
1075 80 AV_WB64(seg->iv + 8, seq);
1076 }
1077
1078
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 80 times.
80 if (key_type != KEY_NONE) {
1079 ff_make_absolute_url(tmp_str, sizeof(tmp_str), url, key);
1080 if (!tmp_str[0]) {
1081 ret = AVERROR_INVALIDDATA;
1082 av_free(seg);
1083 goto fail;
1084 }
1085 seg->key = av_strdup(tmp_str);
1086 if (!seg->key) {
1087 av_free(seg);
1088 ret = AVERROR(ENOMEM);
1089 goto fail;
1090 }
1091 } else {
1092 80 seg->key = NULL;
1093 }
1094
1095 80 ff_make_absolute_url(tmp_str, sizeof(tmp_str), url, line);
1096
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 80 times.
80 if (!tmp_str[0]) {
1097 ret = AVERROR_INVALIDDATA;
1098 if (seg->key)
1099 av_free(seg->key);
1100 av_free(seg);
1101 goto fail;
1102 }
1103 80 seg->url = av_strdup(tmp_str);
1104
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 80 times.
80 if (!seg->url) {
1105 av_free(seg->key);
1106 av_free(seg);
1107 ret = AVERROR(ENOMEM);
1108 goto fail;
1109 }
1110
1111
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 80 times.
80 ret = test_segment(c->ctx, pls->ctx ? pls->ctx->iformat : NULL, pls, seg);
1112
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 80 times.
80 if (ret < 0) {
1113 av_free(seg->url);
1114 av_free(seg->key);
1115 av_free(seg);
1116 goto fail;
1117 }
1118
1119
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 80 times.
80 if (duration < 0.001 * AV_TIME_BASE) {
1120 av_log(c->ctx, AV_LOG_WARNING, "Cannot get correct #EXTINF value of segment %s,"
1121 " set to default value to 1ms.\n", seg->url);
1122 duration = 0.001 * AV_TIME_BASE;
1123 }
1124 80 seg->duration = duration;
1125 80 seg->key_type = key_type;
1126 80 dynarray_add(&pls->segments, &pls->n_segments, seg);
1127 80 is_segment = 0;
1128
1129 80 seg->size = seg_size;
1130
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 60 times.
80 if (seg_size >= 0) {
1131 20 seg->url_offset = seg_offset;
1132 20 seg_offset += seg_size;
1133 20 seg_size = -1;
1134 } else {
1135 60 seg->url_offset = 0;
1136 60 seg_offset = 0;
1137 }
1138
1139 80 seg->init_section = cur_init_section;
1140 }
1141 }
1142 }
1143
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 if (prev_segments) {
1144 if (pls->start_seq_no > prev_start_seq_no && c->first_timestamp != AV_NOPTS_VALUE &&
1145 c->first_timestamp_pls == pls) {
1146 int64_t prev_timestamp = c->first_timestamp;
1147 int i;
1148 int64_t diff = pls->start_seq_no - prev_start_seq_no;
1149 for (i = 0; i < prev_n_segments && i < diff; i++) {
1150 c->first_timestamp += prev_segments[i]->duration;
1151 }
1152 av_log(c->ctx, AV_LOG_DEBUG, "Media sequence change (%"PRId64" -> %"PRId64")"
1153 " reflected in first_timestamp: %"PRId64" -> %"PRId64"\n",
1154 prev_start_seq_no, pls->start_seq_no,
1155 prev_timestamp, c->first_timestamp);
1156 } else if (pls->start_seq_no < prev_start_seq_no) {
1157 av_log(c->ctx, AV_LOG_WARNING, "Media sequence changed unexpectedly: %"PRId64" -> %"PRId64"\n",
1158 prev_start_seq_no, pls->start_seq_no);
1159 }
1160 free_segment_dynarray(prev_segments, prev_n_segments);
1161 av_freep(&prev_segments);
1162 }
1163
1164 18 fail:
1165
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 1 times.
18 if (pls)
1166 17 pls->last_load_time = load_start;
1167 18 av_free(new_url);
1168
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 16 times.
18 if (close_in)
1169 2 ff_format_io_close(c->ctx, &in);
1170 18 c->ctx->ctx_flags = c->ctx->ctx_flags & ~(unsigned)AVFMTCTX_UNSEEKABLE;
1171
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 if (!c->n_variants || !c->variants[0]->n_playlists ||
1172
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 16 times.
18 !c->variants[0]->playlists[0]->n_segments)
1173 2 c->ctx->ctx_flags |= AVFMTCTX_UNSEEKABLE;
1174
1175
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 if (c->n_variants && c->variants[0]->n_playlists &&
1176
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 17 times.
18 c->variants[0]->playlists[0]->type == PLS_TYPE_EVENT &&
1177
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 !c->variants[0]->playlists[0]->finished) {
1178 1 struct playlist *p = c->variants[0]->playlists[0];
1179 1 int64_t duration = 0;
1180
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 1 times.
8 for (int i = 0; i < p->n_segments; i++)
1181 7 duration += p->segments[i]->duration;
1182 1 c->ctx->duration = duration;
1183 }
1184
1185 18 return ret;
1186 }
1187
1188 8008 static struct segment *current_segment(struct playlist *pls)
1189 {
1190 8008 int64_t n = pls->cur_seq_no - pls->start_seq_no;
1191
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 8003 times.
8008 if (n >= pls->n_segments)
1192 5 return NULL;
1193 8003 return pls->segments[n];
1194 }
1195
1196 475 static struct segment *next_segment(struct playlist *pls)
1197 {
1198 475 int64_t n = pls->cur_seq_no - pls->start_seq_no + 1;
1199
2/2
✓ Branch 0 taken 79 times.
✓ Branch 1 taken 396 times.
475 if (n >= pls->n_segments)
1200 79 return NULL;
1201 396 return pls->segments[n];
1202 }
1203
1204 /* True if 'next' can be reached by seeking the open 'in' instead of reopening.
1205 * An unencrypted, contiguous byte range directly following 'cur' in the same
1206 * seekable resource (i.e. consecutive EXT-X-BYTERANGE segments). */
1207 17 static int segment_reusable(AVIOContext *in, const struct segment *cur,
1208 const struct segment *next)
1209 {
1210
3/4
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✓ Branch 3 taken 2 times.
17 return in && cur && next &&
1211
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 (in->seekable & AVIO_SEEKABLE_NORMAL) &&
1212
2/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
15 cur->size >= 0 && next->size >= 0 &&
1213
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 next->url_offset == cur->url_offset + cur->size &&
1214
2/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
15 cur->key_type == KEY_NONE && next->key_type == KEY_NONE &&
1215
2/4
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
49 next->init_section == cur->init_section &&
1216
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 !strcmp(next->url, cur->url);
1217 }
1218
1219 458 static int read_from_url(struct playlist *pls, struct segment *seg,
1220 uint8_t *buf, int buf_size)
1221 {
1222 int ret;
1223
1224 /* limit read if the segment was only a part of a file */
1225
2/2
✓ Branch 0 taken 100 times.
✓ Branch 1 taken 358 times.
458 if (seg->size >= 0)
1226 100 buf_size = FFMIN(buf_size, seg->size - pls->cur_seg_offset);
1227
1228 458 ret = avio_read(pls->input, buf, buf_size);
1229
2/2
✓ Branch 0 taken 393 times.
✓ Branch 1 taken 65 times.
458 if (ret > 0)
1230 393 pls->cur_seg_offset += ret;
1231
1232 458 return ret;
1233 }
1234
1235 /* Parse the raw ID3 data and pass contents to caller */
1236 static void parse_id3(AVFormatContext *s, AVIOContext *pb,
1237 AVDictionary **metadata, int64_t *dts, HLSAudioSetupInfo *audio_setup_info,
1238 ID3v2ExtraMetaAPIC **apic, ID3v2ExtraMeta **extra_meta)
1239 {
1240 static const char id3_priv_owner_ts[] = "com.apple.streaming.transportStreamTimestamp";
1241 static const char id3_priv_owner_audio_setup[] = "com.apple.streaming.audioDescription";
1242 ID3v2ExtraMeta *meta;
1243
1244 ff_id3v2_read_dict(NULL, pb, metadata, ID3v2_DEFAULT_MAGIC, extra_meta);
1245 for (meta = *extra_meta; meta; meta = meta->next) {
1246 if (!strcmp(meta->tag, "PRIV")) {
1247 ID3v2ExtraMetaPRIV *priv = &meta->data.priv;
1248 if (priv->datasize == 8 && !av_strncasecmp(priv->owner, id3_priv_owner_ts, 44)) {
1249 /* 33-bit MPEG timestamp */
1250 int64_t ts = AV_RB64(priv->data);
1251 av_log(s, AV_LOG_DEBUG, "HLS ID3 audio timestamp %"PRId64"\n", ts);
1252 if ((ts & ~((1ULL << 33) - 1)) == 0)
1253 *dts = ts;
1254 else
1255 av_log(s, AV_LOG_ERROR, "Invalid HLS ID3 audio timestamp %"PRId64"\n", ts);
1256 } else if (priv->datasize >= 8 && !av_strncasecmp(priv->owner, id3_priv_owner_audio_setup, 36)) {
1257 ff_hls_senc_read_audio_setup_info(audio_setup_info, priv->data, priv->datasize);
1258 }
1259 } else if (!strcmp(meta->tag, "APIC") && apic)
1260 *apic = &meta->data.apic;
1261 }
1262 }
1263
1264 /* Check if the ID3 metadata contents have changed */
1265 static int id3_has_changed_values(struct playlist *pls, AVDictionary *metadata,
1266 ID3v2ExtraMetaAPIC *apic)
1267 {
1268 const AVDictionaryEntry *entry = NULL;
1269 const AVDictionaryEntry *oldentry;
1270 /* check that no keys have changed values */
1271 while ((entry = av_dict_iterate(metadata, entry))) {
1272 oldentry = av_dict_get(pls->id3_initial, entry->key, NULL, AV_DICT_MATCH_CASE);
1273 if (!oldentry || strcmp(oldentry->value, entry->value) != 0)
1274 return 1;
1275 }
1276
1277 /* check if apic appeared */
1278 if (apic && (pls->ctx->nb_streams != 2 || !pls->ctx->streams[1]->attached_pic.data))
1279 return 1;
1280
1281 if (apic) {
1282 int size = pls->ctx->streams[1]->attached_pic.size;
1283 if (size != apic->buf->size - AV_INPUT_BUFFER_PADDING_SIZE)
1284 return 1;
1285
1286 if (memcmp(apic->buf->data, pls->ctx->streams[1]->attached_pic.data, size) != 0)
1287 return 1;
1288 }
1289
1290 return 0;
1291 }
1292
1293 /* Parse ID3 data and handle the found data */
1294 static void handle_id3(AVIOContext *pb, struct playlist *pls)
1295 {
1296 AVDictionary *metadata = NULL;
1297 ID3v2ExtraMetaAPIC *apic = NULL;
1298 ID3v2ExtraMeta *extra_meta = NULL;
1299 int64_t timestamp = AV_NOPTS_VALUE;
1300
1301 parse_id3(pls->ctx, pb, &metadata, &timestamp, &pls->audio_setup_info, &apic, &extra_meta);
1302
1303 if (timestamp != AV_NOPTS_VALUE) {
1304 pls->id3_mpegts_timestamp = timestamp;
1305 pls->id3_offset = 0;
1306 }
1307
1308 if (!pls->id3_found) {
1309 /* initial ID3 tags */
1310 av_assert0(!pls->id3_deferred_extra);
1311 pls->id3_found = 1;
1312
1313 /* get picture attachment and set text metadata */
1314 if (pls->ctx->nb_streams)
1315 ff_id3v2_parse_apic(pls->ctx, extra_meta);
1316 else
1317 /* demuxer not yet opened, defer picture attachment */
1318 pls->id3_deferred_extra = extra_meta;
1319
1320 ff_id3v2_parse_priv_dict(&metadata, extra_meta);
1321 av_dict_copy(&pls->ctx->metadata, metadata, 0);
1322 pls->id3_initial = metadata;
1323
1324 } else {
1325 if (!pls->id3_changed && id3_has_changed_values(pls, metadata, apic)) {
1326 avpriv_report_missing_feature(pls->parent, "Changing ID3 metadata in HLS audio elementary stream");
1327 pls->id3_changed = 1;
1328 }
1329 av_dict_free(&metadata);
1330 }
1331
1332 if (!pls->id3_deferred_extra)
1333 ff_id3v2_free_extra_meta(&extra_meta);
1334 }
1335
1336 16 static void intercept_id3(struct playlist *pls, uint8_t *buf,
1337 int buf_size, int *len)
1338 {
1339 /* intercept id3 tags, we do not want to pass them to the raw
1340 * demuxer on all segment switches */
1341 int bytes;
1342 16 int id3_buf_pos = 0;
1343 16 int fill_buf = 0;
1344 16 struct segment *seg = current_segment(pls);
1345
1346 /* gather all the id3 tags */
1347 while (1) {
1348 /* see if we can retrieve enough data for ID3 header */
1349
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
16 if (*len < ID3v2_HEADER_SIZE && buf_size >= ID3v2_HEADER_SIZE) {
1350 bytes = read_from_url(pls, seg, buf + *len, ID3v2_HEADER_SIZE - *len);
1351 if (bytes > 0) {
1352
1353 if (bytes == ID3v2_HEADER_SIZE - *len)
1354 /* no EOF yet, so fill the caller buffer again after
1355 * we have stripped the ID3 tags */
1356 fill_buf = 1;
1357
1358 *len += bytes;
1359
1360 } else if (*len <= 0) {
1361 /* error/EOF */
1362 *len = bytes;
1363 fill_buf = 0;
1364 }
1365 }
1366
1367
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 if (*len < ID3v2_HEADER_SIZE)
1368 break;
1369
1370
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
16 if (ff_id3v2_match(buf, ID3v2_DEFAULT_MAGIC)) {
1371 int64_t maxsize = seg->size >= 0 ? seg->size : 1024*1024;
1372 int taglen = ff_id3v2_tag_len(buf);
1373 int tag_got_bytes = FFMIN(taglen, *len);
1374 int remaining = taglen - tag_got_bytes;
1375
1376 if (taglen > maxsize) {
1377 av_log(pls->parent, AV_LOG_ERROR, "Too large HLS ID3 tag (%d > %"PRId64" bytes)\n",
1378 taglen, maxsize);
1379 break;
1380 }
1381
1382 /*
1383 * Copy the id3 tag to our temporary id3 buffer.
1384 * We could read a small id3 tag directly without memcpy, but
1385 * we would still need to copy the large tags, and handling
1386 * both of those cases together with the possibility for multiple
1387 * tags would make the handling a bit complex.
1388 */
1389 pls->id3_buf = av_fast_realloc(pls->id3_buf, &pls->id3_buf_size, id3_buf_pos + taglen);
1390 if (!pls->id3_buf)
1391 break;
1392 memcpy(pls->id3_buf + id3_buf_pos, buf, tag_got_bytes);
1393 id3_buf_pos += tag_got_bytes;
1394
1395 /* strip the intercepted bytes */
1396 *len -= tag_got_bytes;
1397 memmove(buf, buf + tag_got_bytes, *len);
1398 av_log(pls->parent, AV_LOG_DEBUG, "Stripped %d HLS ID3 bytes\n", tag_got_bytes);
1399
1400 if (remaining > 0) {
1401 /* read the rest of the tag in */
1402 if (read_from_url(pls, seg, pls->id3_buf + id3_buf_pos, remaining) != remaining)
1403 break;
1404 id3_buf_pos += remaining;
1405 av_log(pls->parent, AV_LOG_DEBUG, "Stripped additional %d HLS ID3 bytes\n", remaining);
1406 }
1407
1408 } else {
1409 /* no more ID3 tags */
1410 16 break;
1411 }
1412 }
1413
1414 /* re-fill buffer for the caller unless EOF */
1415
3/6
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 16 times.
16 if (*len >= 0 && (fill_buf || *len == 0)) {
1416 bytes = read_from_url(pls, seg, buf + *len, buf_size - *len);
1417
1418 /* ignore error if we already had some data */
1419 if (bytes >= 0)
1420 *len += bytes;
1421 else if (*len == 0)
1422 *len = bytes;
1423 }
1424
1425
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 if (pls->id3_buf) {
1426 /* Now parse all the ID3 tags */
1427 FFIOContext id3ioctx;
1428 ffio_init_read_context(&id3ioctx, pls->id3_buf, id3_buf_pos);
1429 handle_id3(&id3ioctx.pub, pls);
1430 }
1431
1432
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 if (pls->is_id3_timestamped == -1)
1433 16 pls->is_id3_timestamped = (pls->id3_mpegts_timestamp != AV_NOPTS_VALUE);
1434 16 }
1435
1436 static int read_key(HLSContext *c, struct playlist *pls, struct segment *seg)
1437 {
1438 AVIOContext *pb = NULL;
1439
1440 int ret = open_url(pls->parent, &pb, seg->key, &c->avio_opts, NULL, NULL);
1441 if (ret < 0) {
1442 av_log(pls->parent, AV_LOG_ERROR, "Unable to open key file %s, %s\n",
1443 seg->key, av_err2str(ret));
1444 return ret;
1445 }
1446
1447 ret = avio_read(pb, pls->key, sizeof(pls->key));
1448 ff_format_io_close(pls->parent, &pb);
1449 if (ret != sizeof(pls->key)) {
1450 if (ret < 0) {
1451 av_log(pls->parent, AV_LOG_ERROR, "Unable to read key file %s, %s\n",
1452 seg->key, av_err2str(ret));
1453 } else {
1454 av_log(pls->parent, AV_LOG_ERROR, "Unable to read key file %s, read bytes %d != %zu\n",
1455 seg->key, ret, sizeof(pls->key));
1456 ret = AVERROR_INVALIDDATA;
1457 }
1458
1459 return ret;
1460 }
1461
1462 av_strlcpy(pls->key_url, seg->key, sizeof(pls->key_url));
1463
1464 return 0;
1465 }
1466
1467 124 static int open_input(HLSContext *c, struct playlist *pls, struct segment *seg, AVIOContext **in)
1468 {
1469 124 AVDictionary *opts = NULL;
1470 int ret;
1471 124 int is_http = 0;
1472
1473 /* Reuse a kept-open connection to the same resource by seeking instead of
1474 * reopening. For contiguous ranges the seek is a no-op and issues no request. */
1475
4/6
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 109 times.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 15 times.
✗ Branch 5 not taken.
124 if (*in && pls->input_reuse && seg->key_type == KEY_NONE &&
1476
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 ((*in)->seekable & AVIO_SEEKABLE_NORMAL)) {
1477 15 int64_t seek_ret = avio_seek(*in, seg->url_offset, SEEK_SET);
1478 15 pls->input_reuse = 0;
1479
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 if (seek_ret >= 0) {
1480 15 av_log(pls->parent, AV_LOG_VERBOSE,
1481 "HLS reusing connection for url '%s', offset %"PRId64", playlist %d\n",
1482 seg->url, seg->url_offset, pls->index);
1483 15 pls->input_read_done = 0;
1484 15 pls->cur_seg_offset = 0;
1485 15 return 0;
1486 }
1487 /* Seek failed: drop the connection and reopen. */
1488 ff_format_io_close(pls->parent, in);
1489 }
1490 109 pls->input_reuse = 0;
1491
1492
1/2
✓ Branch 0 taken 109 times.
✗ Branch 1 not taken.
109 if (c->http_persistent)
1493 109 av_dict_set(&opts, "multiple_requests", "1", 0);
1494
1495
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 104 times.
109 if (seg->size >= 0) {
1496 /* Restrict the request to the wanted byte range. The end is extended
1497 * across following contiguous segments of the same resource so one
1498 * connection serves the whole run. */
1499 5 int64_t end_offset = seg->url_offset + seg->size;
1500
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 if (seg == current_segment(pls)) {
1501 5 int64_t n = pls->cur_seq_no - pls->start_seq_no + 1;
1502
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 2 times.
20 while (n < pls->n_segments) {
1503 18 struct segment *ns = pls->segments[n];
1504
3/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✓ Branch 3 taken 3 times.
18 if (ns->size < 0 || ns->url_offset != end_offset ||
1505
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 ns->key_type != KEY_NONE ||
1506
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 ns->init_section != seg->init_section ||
1507
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 strcmp(ns->url, seg->url))
1508 break;
1509 15 end_offset = ns->url_offset + ns->size;
1510 15 n++;
1511 }
1512 }
1513 5 av_dict_set_int(&opts, "offset", seg->url_offset, 0);
1514 5 av_dict_set_int(&opts, "end_offset", end_offset, 0);
1515 }
1516
1517 109 av_log(pls->parent, AV_LOG_VERBOSE, "HLS request for url '%s', offset %"PRId64", playlist %d\n",
1518 seg->url, seg->url_offset, pls->index);
1519
1520
2/4
✓ Branch 0 taken 109 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 109 times.
109 if (seg->key_type == KEY_AES_128 || seg->key_type == KEY_SAMPLE_AES) {
1521 if (strcmp(seg->key, pls->key_url)) {
1522 ret = read_key(c, pls, seg);
1523 if (ret < 0)
1524 goto cleanup;
1525 }
1526 }
1527
1528
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 109 times.
109 if (seg->key_type == KEY_AES_128) {
1529 char iv[33], key[33], url[MAX_URL_SIZE];
1530 ff_data_to_hex(iv, seg->iv, sizeof(seg->iv), 0);
1531 ff_data_to_hex(key, pls->key, sizeof(pls->key), 0);
1532 if (strstr(seg->url, "://"))
1533 snprintf(url, sizeof(url), "crypto+%s", seg->url);
1534 else
1535 snprintf(url, sizeof(url), "crypto:%s", seg->url);
1536
1537 av_dict_set(&opts, "key", key, 0);
1538 av_dict_set(&opts, "iv", iv, 0);
1539
1540 ret = open_url(pls->parent, in, url, &c->avio_opts, opts, &is_http);
1541 if (ret < 0) {
1542 goto cleanup;
1543 }
1544 ret = 0;
1545 } else {
1546 109 ret = open_url(pls->parent, in, seg->url, &c->avio_opts, opts, &is_http);
1547 }
1548
1549 /* Seek to the requested position. If this was a HTTP request, the offset
1550 * should already be where want it to, but this allows e.g. local testing
1551 * without a HTTP server.
1552 *
1553 * This is not done for HTTP at all as avio_seek() does internal bookkeeping
1554 * of file offset which is out-of-sync with the actual offset when "offset"
1555 * AVOption is used with http protocol, causing the seek to not be a no-op
1556 * as would be expected. Wrong offset received from the server will not be
1557 * noticed without the call, though.
1558 */
1559
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 109 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 109 times.
✓ Branch 4 taken 109 times.
✗ Branch 5 not taken.
109 if (ret == 0 && !is_http && seg->url_offset) {
1560 int64_t seekret = avio_seek(*in, seg->url_offset, SEEK_SET);
1561 if (seekret < 0) {
1562 av_log(pls->parent, AV_LOG_ERROR, "Unable to seek to offset %"PRId64" of HLS segment '%s'\n", seg->url_offset, seg->url);
1563 ret = seekret;
1564 ff_format_io_close(pls->parent, in);
1565 }
1566 }
1567
1568 109 cleanup:
1569 109 av_dict_free(&opts);
1570 109 pls->cur_seg_offset = 0;
1571 109 return ret;
1572 }
1573
1574 123 static int update_init_section(struct playlist *pls, struct segment *seg)
1575 {
1576 static const int max_init_section_size = 1024*1024;
1577 123 HLSContext *c = pls->parent->priv_data;
1578 int64_t sec_size;
1579 int64_t urlsize;
1580 int ret;
1581
1582
2/2
✓ Branch 0 taken 122 times.
✓ Branch 1 taken 1 times.
123 if (seg->init_section == pls->cur_init_section)
1583 122 return 0;
1584
1585 1 pls->cur_init_section = NULL;
1586
1587
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!seg->init_section)
1588 return 0;
1589
1590 1 ret = open_input(c, pls, seg->init_section, &pls->input);
1591
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (ret < 0) {
1592 av_log(pls->parent, AV_LOG_WARNING,
1593 "Failed to open an initialization section in playlist %d\n",
1594 pls->index);
1595 return ret;
1596 }
1597
1598
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (seg->init_section->size >= 0)
1599 sec_size = seg->init_section->size;
1600
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 else if ((urlsize = avio_size(pls->input)) >= 0)
1601 1 sec_size = urlsize;
1602 else
1603 sec_size = max_init_section_size;
1604
1605 1 av_log(pls->parent, AV_LOG_DEBUG,
1606 "Downloading an initialization section of size %"PRId64"\n",
1607 sec_size);
1608
1609 1 sec_size = FFMIN(sec_size, max_init_section_size);
1610
1611 1 av_fast_malloc(&pls->init_sec_buf, &pls->init_sec_buf_size, sec_size);
1612
1613 1 ret = read_from_url(pls, seg->init_section, pls->init_sec_buf,
1614 1 pls->init_sec_buf_size);
1615 1 ff_format_io_close(pls->parent, &pls->input);
1616
1617
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (ret < 0)
1618 return ret;
1619
1620 1 pls->cur_init_section = seg->init_section;
1621 1 pls->init_sec_data_len = ret;
1622 1 pls->init_sec_buf_read_offset = 0;
1623
1624 /* spec says audio elementary streams do not have media initialization
1625 * sections, so there should be no ID3 timestamps */
1626 1 pls->is_id3_timestamped = 0;
1627
1628 1 return 0;
1629 }
1630
1631 164 static int64_t default_reload_interval(struct playlist *pls)
1632 {
1633 164 return pls->n_segments > 0 ?
1634
1/2
✓ Branch 0 taken 164 times.
✗ Branch 1 not taken.
164 pls->segments[pls->n_segments - 1]->duration :
1635 pls->target_duration;
1636 }
1637
1638 7216 static int playlist_needed(struct playlist *pls)
1639 {
1640 7216 AVFormatContext *s = pls->parent;
1641 int i, j;
1642 7216 int stream_needed = 0;
1643 int first_st;
1644
1645 /* If there is no context or streams yet, the playlist is needed */
1646
4/6
✓ Branch 0 taken 7216 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 40 times.
✓ Branch 3 taken 7176 times.
✓ Branch 4 taken 40 times.
✗ Branch 5 not taken.
7216 if ((!pls->ctx || !pls->n_main_streams) && !pls->is_subtitle)
1647 40 return 1;
1648
1649 /* check if any of the streams in the playlist are needed */
1650
1/2
✓ Branch 0 taken 7176 times.
✗ Branch 1 not taken.
7176 for (i = 0; i < pls->n_main_streams; i++) {
1651
1/2
✓ Branch 0 taken 7176 times.
✗ Branch 1 not taken.
7176 if (pls->main_streams[i]->discard < AVDISCARD_ALL) {
1652 7176 stream_needed = 1;
1653 7176 break;
1654 }
1655 }
1656
1657 /* If all streams in the playlist were discarded, the playlist is not
1658 * needed (regardless of whether whole programs are discarded or not). */
1659
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7176 times.
7176 if (!stream_needed)
1660 return 0;
1661
1662 /* Otherwise, check if all the programs (variants) this playlist is in are
1663 * discarded. Since all streams in the playlist are part of the same programs
1664 * we can just check the programs of the first stream. */
1665
1666 7176 first_st = pls->main_streams[0]->index;
1667
1668
1/2
✓ Branch 0 taken 7176 times.
✗ Branch 1 not taken.
7176 for (i = 0; i < s->nb_programs; i++) {
1669 7176 AVProgram *program = s->programs[i];
1670
1/2
✓ Branch 0 taken 7176 times.
✗ Branch 1 not taken.
7176 if (program->discard < AVDISCARD_ALL) {
1671
1/2
✓ Branch 0 taken 7373 times.
✗ Branch 1 not taken.
7373 for (j = 0; j < program->nb_stream_indexes; j++) {
1672
2/2
✓ Branch 0 taken 7176 times.
✓ Branch 1 taken 197 times.
7373 if (program->stream_index[j] == first_st) {
1673 /* playlist is in an undiscarded program */
1674 7176 return 1;
1675 }
1676 }
1677 }
1678 }
1679
1680 /* some streams were not discarded but all the programs were */
1681 return 0;
1682 }
1683
1684 499 static int reload_playlist(struct playlist *v, HLSContext *c)
1685 {
1686 499 int ret = 0;
1687 499 int reload_count = 0;
1688
1689 499 v->needed = playlist_needed(v);
1690
1691
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 499 times.
499 if (!v->needed)
1692 return AVERROR_EOF;
1693
1694
5/6
✓ Branch 0 taken 350 times.
✓ Branch 1 taken 149 times.
✓ Branch 2 taken 350 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 15 times.
✓ Branch 5 taken 335 times.
499 if (!v->input || (c->http_persistent && v->input_read_done)) {
1695 int64_t reload_interval;
1696
1697 /* Check that the playlist is still needed before opening a new
1698 * segment. */
1699 164 v->needed = playlist_needed(v);
1700
1701
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 164 times.
164 if (!v->needed) {
1702 av_log(v->parent, AV_LOG_INFO, "No longer receiving playlist %d ('%s')\n",
1703 v->index, v->url);
1704 return AVERROR_EOF;
1705 }
1706
1707 /* If this is a live stream and the reload interval has elapsed since
1708 * the last playlist reload, reload the playlists now. */
1709 164 reload_interval = default_reload_interval(v);
1710
1711 164 reload:
1712 164 reload_count++;
1713
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 164 times.
164 if (reload_count > c->max_reload)
1714 return AVERROR_EOF;
1715
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 159 times.
164 if (!v->finished &&
1716
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
5 av_gettime_relative() - v->last_load_time >= reload_interval) {
1717 if ((ret = parse_playlist(c, v->url, v, NULL)) < 0) {
1718 if (ret != AVERROR_EXIT)
1719 av_log(v->parent, AV_LOG_WARNING, "Failed to reload playlist %d\n",
1720 v->index);
1721 return ret;
1722 }
1723 /* If we need to reload the playlist again below (if
1724 * there's still no more segments), switch to a reload
1725 * interval of half the target duration. */
1726 reload_interval = v->target_duration / 2;
1727 }
1728
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 164 times.
164 if (v->cur_seq_no < v->start_seq_no) {
1729 av_log(v->parent, AV_LOG_WARNING,
1730 "skipping %"PRId64" segments ahead, expired from playlists\n",
1731 v->start_seq_no - v->cur_seq_no);
1732 v->cur_seq_no = v->start_seq_no;
1733 }
1734
2/2
✓ Branch 0 taken 70 times.
✓ Branch 1 taken 94 times.
164 if (v->cur_seq_no > v->last_seq_no) {
1735 70 v->last_seq_no = v->cur_seq_no;
1736 70 v->m3u8_hold_counters = 0;
1737
1/2
✓ Branch 0 taken 94 times.
✗ Branch 1 not taken.
94 } else if (v->last_seq_no == v->cur_seq_no) {
1738 94 v->m3u8_hold_counters++;
1739
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 94 times.
94 if (v->m3u8_hold_counters >= c->m3u8_hold_counters) {
1740 return AVERROR_EOF;
1741 }
1742 } else {
1743 av_log(v->parent, AV_LOG_WARNING, "The m3u8 list sequence may have been wrapped.\n");
1744 }
1745
2/2
✓ Branch 0 taken 41 times.
✓ Branch 1 taken 123 times.
164 if (v->cur_seq_no >= v->start_seq_no + v->n_segments) {
1746
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 41 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
41 if (v->finished || v->is_subtitle)
1747 41 return AVERROR_EOF;
1748 while (av_gettime_relative() - v->last_load_time < reload_interval) {
1749 if (ff_check_interrupt(c->interrupt_callback))
1750 return AVERROR_EXIT;
1751 av_usleep(100*1000);
1752 }
1753 /* Enough time has elapsed since the last reload */
1754 goto reload;
1755 }
1756
1757 }
1758 458 return ret;
1759 }
1760
1761 434 static int read_data_continuous(void *opaque, uint8_t *buf, int buf_size)
1762 {
1763 434 struct playlist *v = opaque;
1764 434 HLSContext *c = v->parent->priv_data;
1765 int ret;
1766 434 int just_opened = 0;
1767 434 int segment_retries = 0;
1768 struct segment *seg;
1769
1770
2/4
✓ Branch 0 taken 434 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 434 times.
434 if (c->http_persistent && v->input_read_done) {
1771 av_assert0(v->input);
1772 ret = reload_playlist(v, c);
1773 if (ret < 0)
1774 return ret;
1775 }
1776
1777 434 v->input_read_done = 0;
1778
1779 65 restart:
1780 499 ret = reload_playlist(v, c);
1781
2/2
✓ Branch 0 taken 41 times.
✓ Branch 1 taken 458 times.
499 if (ret < 0)
1782 41 return ret;
1783
1784 458 seg = current_segment(v);
1785
1786
4/4
✓ Branch 0 taken 350 times.
✓ Branch 1 taken 108 times.
✓ Branch 2 taken 15 times.
✓ Branch 3 taken 335 times.
458 if (!v->input || v->input_read_done) {
1787 /* load/update Media Initialization Section, if any */
1788 123 ret = update_init_section(v, seg);
1789
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 123 times.
123 if (ret)
1790 return ret;
1791
1792
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 123 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
123 if (c->http_multiple == 1 && v->input_next_requested) {
1793 FFSWAP(AVIOContext *, v->input, v->input_next);
1794 v->cur_seg_offset = 0;
1795 v->input_next_requested = 0;
1796 ret = 0;
1797 } else {
1798 123 ret = open_input(c, v, seg, &v->input);
1799 }
1800
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 123 times.
123 if (ret < 0) {
1801 if (ff_check_interrupt(c->interrupt_callback))
1802 return AVERROR_EXIT;
1803 av_log(v->parent, AV_LOG_WARNING, "Failed to open segment %"PRId64" of playlist %d\n",
1804 v->cur_seq_no,
1805 v->index);
1806 if (segment_retries >= c->seg_max_retry) {
1807 av_log(v->parent, AV_LOG_WARNING, "Segment %"PRId64" of playlist %d failed too many times, skipping\n",
1808 v->cur_seq_no,
1809 v->index);
1810 v->cur_seq_no++;
1811 segment_retries = 0;
1812 } else {
1813 segment_retries++;
1814 }
1815 goto restart;
1816 }
1817 123 segment_retries = 0;
1818 123 just_opened = 1;
1819
2/2
✓ Branch 0 taken 70 times.
✓ Branch 1 taken 53 times.
123 if (v->first_read_seq_no < 0)
1820 70 v->first_read_seq_no = v->cur_seq_no;
1821 }
1822
1823
1/2
✓ Branch 0 taken 458 times.
✗ Branch 1 not taken.
458 if (c->http_multiple == -1) {
1824 458 uint8_t *http_version_opt = NULL;
1825 458 int r = av_opt_get(v->input, "http_version", AV_OPT_SEARCH_CHILDREN, &http_version_opt);
1826
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 458 times.
458 if (r >= 0) {
1827 c->http_multiple = (!strncmp((const char *)http_version_opt, "1.1", 3) || !strncmp((const char *)http_version_opt, "2.0", 3));
1828 av_freep(&http_version_opt);
1829 }
1830 }
1831
1832 458 seg = next_segment(v);
1833
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 458 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
458 if (c->http_multiple == 1 && !v->input_next_requested &&
1834 seg && seg->key_type == KEY_NONE && av_strstart(seg->url, "http", NULL) &&
1835 !segment_reusable(v->input, current_segment(v), seg)) {
1836 ret = open_input(c, v, seg, &v->input_next);
1837 if (ret < 0) {
1838 if (ff_check_interrupt(c->interrupt_callback))
1839 return AVERROR_EXIT;
1840 av_log(v->parent, AV_LOG_WARNING, "Failed to open segment %"PRId64" of playlist %d\n",
1841 v->cur_seq_no + 1,
1842 v->index);
1843 } else {
1844 v->input_next_requested = 1;
1845 }
1846 }
1847
1848
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 457 times.
458 if (v->init_sec_buf_read_offset < v->init_sec_data_len) {
1849 /* Push init section out first before first actual segment */
1850 1 int copy_size = FFMIN(v->init_sec_data_len - v->init_sec_buf_read_offset, buf_size);
1851 1 memcpy(buf, v->init_sec_buf, copy_size);
1852 1 v->init_sec_buf_read_offset += copy_size;
1853 1 return copy_size;
1854 }
1855
1856 457 seg = current_segment(v);
1857 457 ret = read_from_url(v, seg, buf, buf_size);
1858
2/2
✓ Branch 0 taken 392 times.
✓ Branch 1 taken 65 times.
457 if (ret > 0) {
1859
4/4
✓ Branch 0 taken 122 times.
✓ Branch 1 taken 270 times.
✓ Branch 2 taken 16 times.
✓ Branch 3 taken 106 times.
392 if (just_opened && v->is_id3_timestamped != 0) {
1860 /* Intercept ID3 tags here, elementary audio streams are required
1861 * to convey timestamps using them in the beginning of each segment. */
1862 16 intercept_id3(v, buf, buf_size, &ret);
1863 }
1864
1865 392 return ret;
1866 }
1867
4/4
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 48 times.
✓ Branch 4 taken 15 times.
✓ Branch 5 taken 2 times.
65 if (ret == 0 && segment_reusable(v->input, seg, next_segment(v))) {
1868 /* Clean boundary, and the next segment continues this resource. Keep
1869 * the connection open and read it as a whole. Note that splitting
1870 * segments in these cases is useful for dynamic variant/quality
1871 * switching, but this is not supported by our HLS demuxer, so we
1872 * join the ranges. */
1873 15 v->input_reuse = 1;
1874 15 v->input_read_done = 1;
1875
2/4
✓ Branch 0 taken 50 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 50 times.
50 } else if (c->http_persistent && is_native_http(v->input) &&
1876 seg->key_type == KEY_NONE && av_strstart(seg->url, "http", NULL)) {
1877 v->input_read_done = 1;
1878 } else {
1879 50 ff_format_io_close(v->parent, &v->input);
1880 }
1881 65 v->cur_seq_no++;
1882
1883 65 c->cur_seq_no = v->cur_seq_no;
1884
1885 65 goto restart;
1886 }
1887
1888 static int read_data_subtitle_segment(void *opaque, uint8_t *buf, int buf_size)
1889 {
1890 struct playlist *v = opaque;
1891 HLSContext *c = v->parent->priv_data;
1892 int ret;
1893 struct segment *seg;
1894
1895 if (!v->needed || v->cur_seq_no - v->start_seq_no >= v->n_segments) {
1896 return AVERROR_EOF;
1897 } else {
1898 seg = current_segment(v);
1899 }
1900
1901 if (!v->input) {
1902 ret = open_input(c, v, seg, &v->input);
1903 if (ret < 0) {
1904 if (ff_check_interrupt(c->interrupt_callback))
1905 return AVERROR_EXIT;
1906 av_log(v->parent, AV_LOG_WARNING, "Failed to open segment of playlist %d\n",
1907 v->index);
1908 return ret;
1909 }
1910 if (v->first_read_seq_no < 0)
1911 v->first_read_seq_no = v->cur_seq_no;
1912 }
1913
1914 return read_from_url(v, seg, buf, buf_size);
1915 }
1916
1917 static int nested_io_open(AVFormatContext *s, AVIOContext **pb, const char *url,
1918 int flags, AVDictionary **opts)
1919 {
1920 av_log(s, AV_LOG_ERROR,
1921 "A HLS playlist item '%s' referred to an external file '%s'. "
1922 "Opening this file was forbidden for security reasons\n",
1923 s->url, url);
1924 return AVERROR(EPERM);
1925 }
1926
1927 static int init_subtitle_context(struct playlist *pls)
1928 {
1929 HLSContext *c = pls->parent->priv_data;
1930 const AVInputFormat *in_fmt;
1931 AVDictionary *opts = NULL;
1932 int ret;
1933
1934 if (!(pls->ctx = avformat_alloc_context()))
1935 return AVERROR(ENOMEM);
1936
1937 pls->read_buffer = av_malloc(INITIAL_BUFFER_SIZE);
1938 if (!pls->read_buffer) {
1939 avformat_free_context(pls->ctx);
1940 pls->ctx = NULL;
1941 return AVERROR(ENOMEM);
1942 }
1943
1944 ffio_init_context(&pls->pb, pls->read_buffer, INITIAL_BUFFER_SIZE, 0, pls,
1945 read_data_subtitle_segment, NULL, NULL);
1946 pls->pb.pub.seekable = 0;
1947 pls->ctx->pb = &pls->pb.pub;
1948 pls->ctx->io_open = nested_io_open;
1949
1950 ret = ff_copy_whiteblacklists(pls->ctx, pls->parent);
1951 if (ret < 0)
1952 return ret;
1953
1954 in_fmt = av_find_input_format("webvtt");
1955 av_dict_copy(&opts, c->seg_format_opts, 0);
1956 ret = avformat_open_input(&pls->ctx, current_segment(pls)->url, in_fmt, &opts);
1957 av_dict_free(&opts);
1958
1959 return ret;
1960 }
1961
1962 static int read_subtitle_packet(struct playlist *v, AVPacket *pkt)
1963 {
1964 HLSContext *c = v->parent->priv_data;
1965 int ret;
1966
1967 restart:
1968 ret = reload_playlist(v, c);
1969 if (ret < 0)
1970 return ret;
1971
1972 if (v->input && !v->ctx)
1973 ff_format_io_close(v->parent, &v->input);
1974
1975 if (!v->input && !v->ctx) {
1976 ret = init_subtitle_context(v);
1977 if (ret < 0)
1978 return ret;
1979 }
1980
1981 ret = av_read_frame(v->ctx, v->pkt);
1982 if (!ret) {
1983 return ret;
1984 }
1985 ff_format_io_close(v->parent, &v->input);
1986 v->cur_seq_no++;
1987 c->cur_seq_no = v->cur_seq_no;
1988
1989 avformat_close_input(&v->ctx);
1990
1991 goto restart;
1992 }
1993
1994 1 static void add_renditions_to_variant(HLSContext *c, struct variant *var,
1995 enum AVMediaType type, const char *group_id)
1996 {
1997 int i;
1998
1999
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 for (i = 0; i < c->n_renditions; i++) {
2000 1 struct rendition *rend = c->renditions[i];
2001
2002
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 if (rend->type == type && !strcmp(rend->group_id, group_id)) {
2003
2004
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (rend->playlist)
2005 /* rendition is an external playlist
2006 * => add the playlist to the variant */
2007 1 dynarray_add(&var->playlists, &var->n_playlists, rend->playlist);
2008 else
2009 /* rendition is part of the variant main Media Playlist
2010 * => add the rendition to the main Media Playlist */
2011 dynarray_add(&var->playlists[0]->renditions,
2012 &var->playlists[0]->n_renditions,
2013 rend);
2014 }
2015 }
2016 1 }
2017
2018 51 static void add_metadata_from_renditions(AVFormatContext *s, struct playlist *pls,
2019 enum AVMediaType type)
2020 {
2021 51 int rend_idx = 0;
2022 int i;
2023
2024
2/2
✓ Branch 0 taken 51 times.
✓ Branch 1 taken 35 times.
86 for (i = 0; i < pls->n_main_streams; i++) {
2025 51 AVStream *st = pls->main_streams[i];
2026
2027
2/2
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 16 times.
51 if (st->codecpar->codec_type != type)
2028 35 continue;
2029
2030
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 16 times.
17 for (; rend_idx < pls->n_renditions; rend_idx++) {
2031 1 struct rendition *rend = pls->renditions[rend_idx];
2032
2033
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (rend->type != type)
2034 continue;
2035
2036
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (rend->language[0])
2037 av_dict_set(&st->metadata, "language", rend->language, 0);
2038
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (rend->name[0])
2039 1 av_dict_set(&st->metadata, "comment", rend->name, 0);
2040
2041 1 st->disposition |= rend->disposition;
2042 }
2043
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 if (rend_idx >=pls->n_renditions)
2044 16 break;
2045 }
2046 51 }
2047
2048 /* if timestamp was in valid range: returns 1 and sets seq_no
2049 * if not: returns 0 and sets seq_no to closest segment */
2050 53 static int find_timestamp_in_playlist(HLSContext *c, struct playlist *pls,
2051 int64_t timestamp, int64_t *seq_no,
2052 int64_t *seg_start_ts)
2053 {
2054 int i;
2055 106 int64_t pos = c->first_timestamp == AV_NOPTS_VALUE ?
2056
1/2
✓ Branch 0 taken 53 times.
✗ Branch 1 not taken.
53 0 : c->first_timestamp;
2057
2058
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 33 times.
53 if (timestamp < pos) {
2059 /* Seeking before the start of the playlist, clamp to the first segment. */
2060 20 *seq_no = pls->start_seq_no;
2061
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 9 times.
20 if (seg_start_ts)
2062 11 *seg_start_ts = pos;
2063 20 return 1;
2064 }
2065
2066
1/2
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
39 for (i = 0; i < pls->n_segments; i++) {
2067 39 int64_t diff = pos + pls->segments[i]->duration - timestamp;
2068
2/2
✓ Branch 0 taken 33 times.
✓ Branch 1 taken 6 times.
39 if (diff > 0) {
2069 33 *seq_no = pls->start_seq_no + i;
2070
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 17 times.
33 if (seg_start_ts) {
2071 16 *seg_start_ts = pos;
2072 }
2073 33 return 1;
2074 }
2075 6 pos += pls->segments[i]->duration;
2076 }
2077
2078 *seq_no = pls->start_seq_no + pls->n_segments - 1;
2079
2080 if (!pls->finished && pls->n_segments > 0) {
2081 if (seg_start_ts)
2082 *seg_start_ts = pos - pls->segments[pls->n_segments - 1]->duration;
2083 return 1;
2084 }
2085
2086 return 0;
2087 }
2088
2089 17 static int64_t select_cur_seq_no(HLSContext *c, struct playlist *pls)
2090 {
2091 int64_t seq_no;
2092
2093
3/6
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
17 if (!pls->finished && !c->first_packet &&
2094 av_gettime_relative() - pls->last_load_time >= default_reload_interval(pls))
2095 /* reload the playlist since it was suspended */
2096 parse_playlist(c, pls->url, pls, NULL);
2097
2098 /* If playback is already in progress (we are just selecting a new
2099 * playlist) and this is a complete file, find the matching segment
2100 * by counting durations. */
2101
3/4
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 15 times.
17 if (pls->finished && c->cur_timestamp != AV_NOPTS_VALUE) {
2102 find_timestamp_in_playlist(c, pls, c->cur_timestamp, &seq_no, NULL);
2103 return seq_no;
2104 }
2105
2106
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 15 times.
17 if (!pls->finished) {
2107
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!c->first_packet && /* we are doing a segment selection during playback */
2108 c->cur_seq_no >= pls->start_seq_no &&
2109 c->cur_seq_no < pls->start_seq_no + pls->n_segments)
2110 /* While spec 3.4.3 says that we cannot assume anything about the
2111 * content at the same sequence number on different playlists,
2112 * in practice this seems to work and doing it otherwise would
2113 * require us to download a segment to inspect its timestamps. */
2114 return c->cur_seq_no;
2115
2116 /* If this is a live stream, start live_start_index segments from the
2117 * start or end */
2118
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (c->live_start_index < 0)
2119 2 seq_no = pls->start_seq_no + FFMAX(pls->n_segments +
2120 c->live_start_index, 0);
2121 else
2122 seq_no = pls->start_seq_no + FFMIN(c->live_start_index,
2123 pls->n_segments - 1);
2124
2125 /* If #EXT-X-START in playlist, need to recalculate */
2126
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
2 if (pls->time_offset_flag && c->prefer_x_start) {
2127 int64_t start_timestamp;
2128 int64_t playlist_duration = 0;
2129 int64_t cur_timestamp = c->cur_timestamp == AV_NOPTS_VALUE ? 0 :
2130 c->cur_timestamp;
2131
2132 for (int i = 0; i < pls->n_segments; i++)
2133 playlist_duration += pls->segments[i]->duration;
2134
2135 /* If the absolute value of TIME-OFFSET exceeds
2136 * the duration of the playlist, it indicates either the end of the
2137 * playlist (if positive) or the beginning of the playlist (if
2138 * negative). */
2139 if (pls->start_time_offset >=0 &&
2140 pls->start_time_offset > playlist_duration)
2141 start_timestamp = cur_timestamp + playlist_duration;
2142 else if (pls->start_time_offset >= 0 &&
2143 pls->start_time_offset <= playlist_duration)
2144 start_timestamp = cur_timestamp + pls->start_time_offset;
2145 else if (pls->start_time_offset < 0 &&
2146 pls->start_time_offset < -playlist_duration)
2147 start_timestamp = cur_timestamp;
2148 else if (pls->start_time_offset < 0 &&
2149 pls->start_time_offset > -playlist_duration)
2150 start_timestamp = cur_timestamp + playlist_duration +
2151 pls->start_time_offset;
2152 else
2153 start_timestamp = cur_timestamp;
2154
2155 find_timestamp_in_playlist(c, pls, start_timestamp, &seq_no, NULL);
2156 }
2157 2 return seq_no;
2158 }
2159
2160 /* Otherwise just start on the first segment. */
2161 15 return pls->start_seq_no;
2162 }
2163
2164 17 static void add_stream_to_programs(AVFormatContext *s, struct playlist *pls, AVStream *stream)
2165 {
2166 17 HLSContext *c = s->priv_data;
2167 int i, j;
2168 17 int bandwidth = -1;
2169
2170
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 17 times.
34 for (i = 0; i < c->n_variants; i++) {
2171 17 struct variant *v = c->variants[i];
2172
2173
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 17 times.
36 for (j = 0; j < v->n_playlists; j++) {
2174
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 17 times.
19 if (v->playlists[j] != pls)
2175 2 continue;
2176
2177 17 av_program_add_stream_index(s, i, stream->index);
2178
2179
1/2
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
17 if (bandwidth < 0)
2180 17 bandwidth = v->bandwidth;
2181 else if (bandwidth != v->bandwidth)
2182 bandwidth = -1; /* stream in multiple variants with different bandwidths */
2183 }
2184 }
2185
2186
1/2
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
17 if (bandwidth >= 0)
2187 17 av_dict_set_int(&stream->metadata, "variant_bitrate", bandwidth, 0);
2188 17 }
2189
2190 28 static int set_stream_info_from_input_stream(AVStream *st, struct playlist *pls, AVStream *ist)
2191 {
2192 int err;
2193
2194 28 err = avcodec_parameters_copy(st->codecpar, ist->codecpar);
2195
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 if (err < 0)
2196 return err;
2197
2198
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 if (pls->is_id3_timestamped) /* custom timestamps via id3 */
2199 avpriv_set_pts_info(st, 33, 1, MPEG_TIME_BASE);
2200 else
2201 28 avpriv_set_pts_info(st, ist->pts_wrap_bits, ist->time_base.num, ist->time_base.den);
2202
2203 // copy disposition
2204 28 st->disposition = ist->disposition;
2205
2206 28 av_dict_copy(&st->metadata, ist->metadata, 0);
2207
2208 28 ffstream(st)->need_context_update = 1;
2209
2210 28 return 0;
2211 }
2212
2213 /* add new subdemuxer streams to our context, if any */
2214 6437 static int update_streams_from_subdemuxer(AVFormatContext *s, struct playlist *pls)
2215 {
2216 6437 HLSContext *c = s->priv_data;
2217 int err;
2218
2219
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 6437 times.
12891 while (pls->n_main_streams < pls->ctx->nb_streams) {
2220 17 int ist_idx = pls->n_main_streams;
2221 17 AVStream *st = avformat_new_stream(s, NULL);
2222 17 AVStream *ist = pls->ctx->streams[ist_idx];
2223
2224
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (!st)
2225 return AVERROR(ENOMEM);
2226
2227 17 st->id = pls->index;
2228 17 dynarray_add(&pls->main_streams, &pls->n_main_streams, st);
2229
2230 17 add_stream_to_programs(s, pls, st);
2231
2232 17 err = set_stream_info_from_input_stream(st, pls, ist);
2233
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (err < 0)
2234 return err;
2235
2236 /* Match the start_time of streams created before playback began. */
2237
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (c->first_timestamp != AV_NOPTS_VALUE)
2238 st->start_time = av_rescale_q(c->first_timestamp,
2239 AV_TIME_BASE_Q, st->time_base);
2240
2241
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 16 times.
17 if (ist->codecpar->codec_id == AV_CODEC_ID_TIMED_ID3) {
2242
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (pls->timed_id3_stream_index < 0)
2243 1 pls->timed_id3_stream_index = ist_idx;
2244 else
2245 av_log(s, AV_LOG_WARNING,
2246 "Playlist has multiple timed ID3 streams; "
2247 "only stream %d will be used for metadata propagation\n",
2248 pls->timed_id3_stream_index);
2249 }
2250 }
2251
2252 6437 return 0;
2253 }
2254
2255 30 static void update_noheader_flag(AVFormatContext *s)
2256 {
2257 30 HLSContext *c = s->priv_data;
2258 30 int flag_needed = 0;
2259 int i;
2260
2261
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 16 times.
48 for (i = 0; i < c->n_playlists; i++) {
2262 32 struct playlist *pls = c->playlists[i];
2263
2264
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 18 times.
32 if (pls->has_noheader_flag) {
2265 14 flag_needed = 1;
2266 14 break;
2267 }
2268 }
2269
2270
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 16 times.
30 if (flag_needed)
2271 14 s->ctx_flags |= AVFMTCTX_NOHEADER;
2272 else
2273 16 s->ctx_flags &= ~AVFMTCTX_NOHEADER;
2274 30 }
2275
2276 16 static int hls_close(AVFormatContext *s)
2277 {
2278 16 HLSContext *c = s->priv_data;
2279
2280 16 free_playlist_list(c);
2281 16 free_variant_list(c);
2282 16 free_rendition_list(c);
2283
2284
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 if (c->crypto_ctx.aes_ctx)
2285 av_free(c->crypto_ctx.aes_ctx);
2286
2287 16 av_dict_free(&c->avio_opts);
2288 16 ff_format_io_close(c->ctx, &c->playlist_pb);
2289
2290 16 return 0;
2291 }
2292
2293 16 static int hls_read_header(AVFormatContext *s)
2294 {
2295 16 HLSContext *c = s->priv_data;
2296 16 int ret = 0, i;
2297 16 int64_t highest_cur_seq_no = 0;
2298
2299 16 c->ctx = s;
2300 16 c->interrupt_callback = &s->interrupt_callback;
2301
2302 16 c->first_packet = 1;
2303 16 c->first_timestamp = AV_NOPTS_VALUE;
2304 16 c->first_timestamp_pls = NULL;
2305 16 c->first_timestamp_locked = 0;
2306 16 c->cur_timestamp = AV_NOPTS_VALUE;
2307
2308
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
16 if ((ret = ffio_copy_url_options(s->pb, &c->avio_opts)) < 0)
2309 return ret;
2310
2311 /* http_persistent and http_multiple auto-detection both rely on the
2312 * AVIOContext being backed by the builtin URLContext. Neither works
2313 * when io_open is overridden with a custom callback. */
2314
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
16 if (!ffio_geturlcontext(s->pb)) {
2315 if (c->http_persistent) {
2316 av_log(s, AV_LOG_WARNING, "Disabling http_persistent due to custom io_open.\n");
2317 c->http_persistent = 0;
2318 }
2319 /* Only auto-detection is disabled, enabling http_multiple can still work
2320 * with custom io_open. */
2321 if (c->http_multiple == -1) {
2322 av_log(s, AV_LOG_WARNING, "Disabling http_multiple due to custom io_open.\n");
2323 c->http_multiple = 0;
2324 }
2325 }
2326
2327 /* XXX: Some HLS servers don't like being sent the range header,
2328 in this case, we need to set http_seekable = 0 to disable
2329 the range header */
2330 16 av_dict_set_int(&c->avio_opts, "seekable", c->http_seekable, 0);
2331
2332
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
16 if ((ret = parse_playlist(c, s->url, NULL, s->pb)) < 0)
2333 return ret;
2334
2335
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 if (c->n_variants == 0) {
2336 av_log(s, AV_LOG_WARNING, "Empty playlist\n");
2337 return AVERROR_EOF;
2338 }
2339 /* If the playlist only contained playlists (Master Playlist),
2340 * parse each individual playlist. */
2341
3/4
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 15 times.
16 if (c->n_playlists > 1 || c->playlists[0]->n_segments == 0) {
2342
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 for (i = 0; i < c->n_playlists; i++) {
2343 2 struct playlist *pls = c->playlists[i];
2344 2 pls->m3u8_hold_counters = 0;
2345
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if ((ret = parse_playlist(c, pls->url, pls, NULL)) < 0) {
2346 av_log(s, AV_LOG_WARNING, "parse_playlist error %s [%s]\n", av_err2str(ret), pls->url);
2347 pls->broken = 1;
2348 if (c->n_playlists > 1)
2349 continue;
2350 return ret;
2351 }
2352 }
2353 }
2354
2355
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 16 times.
32 for (i = 0; i < c->n_variants; i++) {
2356
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 if (c->variants[i]->playlists[0]->n_segments == 0) {
2357 av_log(s, AV_LOG_WARNING, "Empty segment [%s]\n", c->variants[i]->playlists[0]->url);
2358 c->variants[i]->playlists[0]->broken = 1;
2359 }
2360 }
2361
2362 /* Calculate the total duration of the stream if all segments are
2363 * available (finished or EVENT playlists). */
2364
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 14 times.
16 if (c->variants[0]->playlists[0]->finished ||
2365
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 c->variants[0]->playlists[0]->type == PLS_TYPE_EVENT) {
2366 15 int64_t duration = 0;
2367
2/2
✓ Branch 0 taken 72 times.
✓ Branch 1 taken 15 times.
87 for (i = 0; i < c->variants[0]->playlists[0]->n_segments; i++)
2368 72 duration += c->variants[0]->playlists[0]->segments[i]->duration;
2369 15 s->duration = duration;
2370 }
2371
2372 /* Associate renditions with variants */
2373
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 16 times.
32 for (i = 0; i < c->n_variants; i++) {
2374 16 struct variant *var = c->variants[i];
2375
2376
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 15 times.
16 if (var->audio_group[0])
2377 1 add_renditions_to_variant(c, var, AVMEDIA_TYPE_AUDIO, var->audio_group);
2378
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 if (var->video_group[0])
2379 add_renditions_to_variant(c, var, AVMEDIA_TYPE_VIDEO, var->video_group);
2380
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 if (var->subtitles_group[0])
2381 add_renditions_to_variant(c, var, AVMEDIA_TYPE_SUBTITLE, var->subtitles_group);
2382 }
2383
2384 /* Create a program for each variant */
2385
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 16 times.
32 for (i = 0; i < c->n_variants; i++) {
2386 16 struct variant *v = c->variants[i];
2387 AVProgram *program;
2388
2389 16 program = av_new_program(s, i);
2390
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 if (!program)
2391 return AVERROR(ENOMEM);
2392 16 av_dict_set_int(&program->metadata, "variant_bitrate", v->bandwidth, 0);
2393 }
2394
2395 /* Select the starting segments */
2396
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 16 times.
33 for (i = 0; i < c->n_playlists; i++) {
2397 17 struct playlist *pls = c->playlists[i];
2398
2399
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (pls->n_segments == 0)
2400 continue;
2401
2402 17 pls->cur_seq_no = select_cur_seq_no(c, pls);
2403 17 highest_cur_seq_no = FFMAX(highest_cur_seq_no, pls->cur_seq_no);
2404 }
2405
2406 16 av_dict_set(&c->seg_format_opts, "prefer_hls_mpegts_pts", "1", 0);
2407
2408 /* Open the demuxer for each playlist */
2409
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 16 times.
33 for (i = 0; i < c->n_playlists; i++) {
2410 17 struct playlist *pls = c->playlists[i];
2411 17 const AVInputFormat *in_fmt = NULL;
2412 char *url;
2413 17 AVDictionary *options = NULL;
2414 17 struct segment *seg = NULL;
2415
2416
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 17 times.
17 if (!(pls->ctx = avformat_alloc_context()))
2417 return AVERROR(ENOMEM);
2418
2419
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (pls->n_segments == 0)
2420 continue;
2421
2422 17 pls->index = i;
2423 17 pls->needed = 1;
2424 17 pls->parent = s;
2425
2426 /*
2427 * If this is a live stream and this playlist looks like it is one segment
2428 * behind, try to sync it up so that every substream starts at the same
2429 * time position (so e.g. avformat_find_stream_info() will see packets from
2430 * all active streams within the first few seconds). This is not very generic,
2431 * though, as the sequence numbers are technically independent.
2432 */
2433
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
17 if (!pls->finished && pls->cur_seq_no == highest_cur_seq_no - 1 &&
2434 highest_cur_seq_no < pls->start_seq_no + pls->n_segments) {
2435 pls->cur_seq_no = highest_cur_seq_no;
2436 }
2437
2438 17 pls->read_buffer = av_malloc(INITIAL_BUFFER_SIZE);
2439
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (!pls->read_buffer){
2440 avformat_free_context(pls->ctx);
2441 pls->ctx = NULL;
2442 return AVERROR(ENOMEM);
2443 }
2444
2445
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (pls->is_subtitle)
2446 ffio_init_context(&pls->pb, (unsigned char*)av_strdup("WEBVTT\n"), (int)strlen("WEBVTT\n"), 0, pls,
2447 NULL, NULL, NULL);
2448 else
2449 17 ffio_init_context(&pls->pb, pls->read_buffer, INITIAL_BUFFER_SIZE, 0, pls,
2450 read_data_continuous, NULL, NULL);
2451
2452 /*
2453 * If encryption scheme is SAMPLE-AES, try to read ID3 tags of
2454 * external audio track that contains audio setup information
2455 */
2456 17 seg = current_segment(pls);
2457
2/6
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 17 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
17 if (seg && seg->key_type == KEY_SAMPLE_AES && pls->n_renditions > 0 &&
2458 pls->renditions[0]->type == AVMEDIA_TYPE_AUDIO) {
2459 uint8_t buf[HLS_MAX_ID3_TAGS_DATA_LEN];
2460 if ((ret = avio_read(&pls->pb.pub, buf, HLS_MAX_ID3_TAGS_DATA_LEN)) < 0) {
2461 /* Fail if error was not end of file */
2462 if (ret != AVERROR_EOF) {
2463 avformat_free_context(pls->ctx);
2464 pls->ctx = NULL;
2465 return ret;
2466 }
2467 }
2468 ret = 0;
2469 /* Reset reading */
2470 ff_format_io_close(pls->parent, &pls->input);
2471 pls->input = NULL;
2472 pls->input_read_done = 0;
2473 pls->input_reuse = 0;
2474 ff_format_io_close(pls->parent, &pls->input_next);
2475 pls->input_next = NULL;
2476 pls->input_next_requested = 0;
2477 pls->cur_seg_offset = 0;
2478 pls->cur_init_section = NULL;
2479 /* Reset EOF flag */
2480 pls->pb.pub.eof_reached = 0;
2481 /* Clear any buffered data */
2482 pls->pb.pub.buf_end = pls->pb.pub.buf_ptr = pls->pb.pub.buffer;
2483 /* Reset the position */
2484 pls->pb.pub.pos = 0;
2485 }
2486
2487 /*
2488 * If encryption scheme is SAMPLE-AES and audio setup information is present in external audio track,
2489 * use that information to find the media format, otherwise probe input data
2490 */
2491 17 seg = current_segment(pls);
2492
2/6
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 17 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
17 if (seg && seg->key_type == KEY_SAMPLE_AES && pls->is_id3_timestamped &&
2493 pls->audio_setup_info.codec_id != AV_CODEC_ID_NONE) {
2494 av_assert1(pls->audio_setup_info.codec_id == AV_CODEC_ID_AAC ||
2495 pls->audio_setup_info.codec_id == AV_CODEC_ID_AC3 ||
2496 pls->audio_setup_info.codec_id == AV_CODEC_ID_EAC3);
2497 // Keep this list in sync with ff_hls_senc_read_audio_setup_info()
2498 in_fmt = av_find_input_format(pls->audio_setup_info.codec_id == AV_CODEC_ID_AAC ? "aac" :
2499 pls->audio_setup_info.codec_id == AV_CODEC_ID_AC3 ? "ac3" : "eac3");
2500 } else {
2501
1/2
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
17 pls->ctx->probesize = s->probesize > 0 ? s->probesize : 1024 * 4;
2502
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 pls->ctx->max_analyze_duration = s->max_analyze_duration > 0 ? s->max_analyze_duration : 4 * AV_TIME_BASE;
2503 17 pls->ctx->interrupt_callback = s->interrupt_callback;
2504 17 url = av_strdup(pls->segments[0]->url);
2505 17 ret = av_probe_input_buffer(&pls->pb.pub, &in_fmt, url, NULL, 0, 0);
2506
2507
2/2
✓ Branch 0 taken 80 times.
✓ Branch 1 taken 17 times.
97 for (int n = 0; n < pls->n_segments; n++)
2508
1/2
✓ Branch 0 taken 80 times.
✗ Branch 1 not taken.
80 if (ret >= 0)
2509 80 ret = test_segment(s, in_fmt, pls, pls->segments[n]);
2510
2511
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (ret < 0) {
2512 /* Free the ctx - it isn't initialized properly at this point,
2513 * so avformat_close_input shouldn't be called. If
2514 * avformat_open_input fails below, it frees and zeros the
2515 * context, so it doesn't need any special treatment like this. */
2516 av_log(s, AV_LOG_ERROR, "Error when loading first segment '%s'\n", url);
2517 avformat_free_context(pls->ctx);
2518 pls->ctx = NULL;
2519 av_free(url);
2520 return ret;
2521 }
2522 17 av_free(url);
2523 }
2524
2525 17 seg = current_segment(pls);
2526
3/4
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 16 times.
17 if (seg && seg->key_type == KEY_SAMPLE_AES) {
2527 if (strstr(in_fmt->name, "mov")) {
2528 char key[33];
2529 ff_data_to_hex(key, pls->key, sizeof(pls->key), 0);
2530 av_dict_set(&options, "decryption_key", key, 0);
2531 } else if (!c->crypto_ctx.aes_ctx) {
2532 c->crypto_ctx.aes_ctx = av_aes_alloc();
2533 if (!c->crypto_ctx.aes_ctx) {
2534 avformat_free_context(pls->ctx);
2535 pls->ctx = NULL;
2536 return AVERROR(ENOMEM);
2537 }
2538 }
2539 }
2540
2541 17 pls->ctx->pb = &pls->pb.pub;
2542 17 pls->ctx->io_open = nested_io_open;
2543 17 pls->ctx->flags |= s->flags & ~AVFMT_FLAG_CUSTOM_IO;
2544
2545
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 17 times.
17 if ((ret = ff_copy_whiteblacklists(pls->ctx, s)) < 0)
2546 return ret;
2547
2548 17 av_dict_copy(&options, c->seg_format_opts, 0);
2549
2550 17 ret = avformat_open_input(&pls->ctx, pls->segments[0]->url, in_fmt, &options);
2551 17 av_dict_free(&options);
2552
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (ret < 0)
2553 return ret;
2554
2555
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
17 if (pls->id3_deferred_extra && pls->ctx->nb_streams == 1) {
2556 ff_id3v2_parse_apic(pls->ctx, pls->id3_deferred_extra);
2557 avformat_queue_attached_pictures(pls->ctx);
2558 ff_id3v2_parse_priv(pls->ctx, pls->id3_deferred_extra);
2559 ff_id3v2_free_extra_meta(&pls->id3_deferred_extra);
2560 }
2561
2562
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (pls->is_id3_timestamped == -1)
2563 av_log(s, AV_LOG_WARNING, "No expected HTTP requests have been made\n");
2564
2565 /*
2566 * For ID3 timestamped raw audio streams we need to detect the packet
2567 * durations to calculate timestamps in fill_timing_for_id3_timestamped_stream(),
2568 * but for other streams we can rely on our user calling avformat_find_stream_info()
2569 * on us if they want to.
2570 */
2571
4/6
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 16 times.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
17 if (pls->is_id3_timestamped || (pls->n_renditions > 0 && pls->renditions[0]->type == AVMEDIA_TYPE_AUDIO)) {
2572 1 seg = current_segment(pls);
2573
2/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
1 if (seg && seg->key_type == KEY_SAMPLE_AES && pls->audio_setup_info.setup_data_length > 0 &&
2574 pls->ctx->nb_streams == 1)
2575 ret = ff_hls_senc_parse_audio_setup_info(pls->ctx->streams[0], &pls->audio_setup_info);
2576 else
2577 1 ret = avformat_find_stream_info(pls->ctx, NULL);
2578
2579
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (ret < 0)
2580 return ret;
2581 }
2582
2583 17 pls->has_noheader_flag = !!(pls->ctx->ctx_flags & AVFMTCTX_NOHEADER);
2584
2585 /* Create new AVStreams for each stream in this playlist */
2586 17 ret = update_streams_from_subdemuxer(s, pls);
2587
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (ret < 0)
2588 return ret;
2589
2590 /*
2591 * Copy any metadata from playlist to main streams, but do not set
2592 * event flags.
2593 */
2594
1/2
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
17 if (pls->n_main_streams)
2595 17 av_dict_copy(&pls->main_streams[0]->metadata, pls->ctx->metadata, 0);
2596
2597
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (pls->is_subtitle) {
2598 avformat_free_context(pls->ctx);
2599 pls->ctx = NULL;
2600 pls->needed = 0;
2601 pls->main_streams[0]->discard = AVDISCARD_ALL;
2602 }
2603
2604 17 add_metadata_from_renditions(s, pls, AVMEDIA_TYPE_AUDIO);
2605 17 add_metadata_from_renditions(s, pls, AVMEDIA_TYPE_VIDEO);
2606 17 add_metadata_from_renditions(s, pls, AVMEDIA_TYPE_SUBTITLE);
2607 }
2608
2609 16 update_noheader_flag(s);
2610
2611 16 return 0;
2612 }
2613
2614 6432 static int recheck_discard_flags(AVFormatContext *s, int first)
2615 {
2616 6432 HLSContext *c = s->priv_data;
2617 6432 int i, changed = 0;
2618 int cur_needed;
2619
2620 /* Check if any new streams are needed */
2621
2/2
✓ Branch 0 taken 6553 times.
✓ Branch 1 taken 6432 times.
12985 for (i = 0; i < c->n_playlists; i++) {
2622 6553 struct playlist *pls = c->playlists[i];
2623
2624 6553 cur_needed = playlist_needed(c->playlists[i]);
2625
2626
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6553 times.
6553 if (pls->broken) {
2627 continue;
2628 }
2629
2/4
✓ Branch 0 taken 6553 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 6553 times.
6553 if (cur_needed && !pls->needed) {
2630 pls->needed = 1;
2631 changed = 1;
2632 pls->cur_seq_no = select_cur_seq_no(c, pls);
2633 pls->first_read_seq_no = -1;
2634 pls->pb.pub.eof_reached = 0;
2635 if (c->cur_timestamp != AV_NOPTS_VALUE) {
2636 /* catch up */
2637 pls->seek_timestamp = c->cur_timestamp +
2638 (pls->ts_offset != AV_NOPTS_VALUE ? pls->ts_offset : 0);
2639 pls->seek_flags = AVSEEK_FLAG_ANY;
2640 pls->seek_stream_index = -1;
2641 }
2642 av_log(s, AV_LOG_INFO, "Now receiving playlist %d, segment %"PRId64"\n", i, pls->cur_seq_no);
2643
3/6
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 6536 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 17 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
6553 } else if (first && !cur_needed && pls->needed) {
2644 ff_format_io_close(pls->parent, &pls->input);
2645 pls->input_read_done = 0;
2646 pls->input_reuse = 0;
2647 ff_format_io_close(pls->parent, &pls->input_next);
2648 pls->input_next_requested = 0;
2649 if (pls->is_subtitle)
2650 avformat_close_input(&pls->ctx);
2651 pls->needed = 0;
2652 changed = 1;
2653 av_log(s, AV_LOG_INFO, "No longer receiving playlist %d\n", i);
2654 }
2655 }
2656 6432 return changed;
2657 }
2658
2659 static void fill_timing_for_id3_timestamped_stream(struct playlist *pls)
2660 {
2661 if (pls->id3_offset >= 0) {
2662 pls->pkt->dts = pls->id3_mpegts_timestamp +
2663 av_rescale_q(pls->id3_offset,
2664 pls->ctx->streams[pls->pkt->stream_index]->time_base,
2665 MPEG_TIME_BASE_Q);
2666 if (pls->pkt->duration)
2667 pls->id3_offset += pls->pkt->duration;
2668 else
2669 pls->id3_offset = -1;
2670 } else {
2671 /* there have been packets with unknown duration
2672 * since the last id3 tag, should not normally happen */
2673 pls->pkt->dts = AV_NOPTS_VALUE;
2674 }
2675
2676 if (pls->pkt->duration)
2677 pls->pkt->duration = av_rescale_q(pls->pkt->duration,
2678 pls->ctx->streams[pls->pkt->stream_index]->time_base,
2679 MPEG_TIME_BASE_Q);
2680
2681 pls->pkt->pts = AV_NOPTS_VALUE;
2682 }
2683
2684 3866 static AVRational get_timebase(struct playlist *pls)
2685 {
2686
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3866 times.
3866 if (pls->is_id3_timestamped)
2687 return MPEG_TIME_BASE_Q;
2688
2689 3866 return pls->ctx->streams[pls->pkt->stream_index]->time_base;
2690 }
2691
2692 121 static int compare_ts_with_wrapdetect(int64_t ts_a, struct playlist *pls_a,
2693 int64_t ts_b, struct playlist *pls_b)
2694 {
2695 121 int64_t scaled_ts_a = av_rescale_q(ts_a, get_timebase(pls_a), MPEG_TIME_BASE_Q);
2696 121 int64_t scaled_ts_b = av_rescale_q(ts_b, get_timebase(pls_b), MPEG_TIME_BASE_Q);
2697
2698 121 return av_compare_mod(scaled_ts_a, scaled_ts_b, 1LL << 33);
2699 }
2700
2701 /**
2702 * Check whether any entry in @p update differs from the corresponding entry
2703 * in @p current.
2704 *
2705 * HLS segments can repeat timed ID3 metadata in every segment so that
2706 * listeners joining mid-stream immediately receive the current track
2707 * information. Only signal a metadata change when values actually differ
2708 * to avoid spurious updates on every segment boundary.
2709 */
2710 2 static int metadata_has_changed(const AVDictionary *current,
2711 const AVDictionary *update)
2712 {
2713
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
2 if (av_dict_count(current) != av_dict_count(update))
2714 1 return 1;
2715 1 const AVDictionaryEntry *e = NULL;
2716
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
2 while ((e = av_dict_iterate(update, e))) {
2717 1 const AVDictionaryEntry *cur = av_dict_get(current, e->key, NULL, 0);
2718
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 if (!cur || strcmp(cur->value, e->value))
2719 return 1;
2720 }
2721 1 return 0;
2722 }
2723
2724 6432 static int hls_read_packet(AVFormatContext *s, AVPacket *pkt)
2725 {
2726 6432 HLSContext *c = s->priv_data;
2727 6432 int ret, i, minplaylist = -1;
2728
2729 6432 recheck_discard_flags(s, c->first_packet);
2730 6432 c->first_packet = 0;
2731
2732
2/2
✓ Branch 0 taken 6553 times.
✓ Branch 1 taken 6432 times.
12985 for (i = 0; i < c->n_playlists; i++) {
2733 6553 struct playlist *pls = c->playlists[i];
2734 /* Make sure we've got one buffered packet from each open playlist
2735 * stream */
2736
3/4
✓ Branch 0 taken 6553 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6459 times.
✓ Branch 3 taken 94 times.
6553 if (pls->needed && !pls->pkt->data) {
2737 573 while (1) {
2738 int64_t ts_diff;
2739 AVRational tb;
2740 7032 struct segment *seg = NULL;
2741
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7032 times.
7032 if (pls->is_subtitle)
2742 ret = read_subtitle_packet(pls, pls->pkt);
2743 else
2744 7032 ret = av_read_frame(pls->ctx, pls->pkt);
2745
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 7020 times.
7032 if (ret < 0) {
2746
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
12 if (!avio_feof(&pls->pb.pub) && ret != AVERROR_EOF)
2747 return ret;
2748 6459 break;
2749 }
2750
2751 /* stream_index check prevents matching picture attachments etc. */
2752
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 7020 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
7020 if (pls->is_id3_timestamped && pls->pkt->stream_index == 0) {
2753 /* audio elementary streams are id3 timestamped */
2754 fill_timing_for_id3_timestamped_stream(pls);
2755 }
2756
2757
1/2
✓ Branch 0 taken 7020 times.
✗ Branch 1 not taken.
7020 if (pls->pkt->dts != AV_NOPTS_VALUE &&
2758
2/2
✓ Branch 0 taken 7003 times.
✓ Branch 1 taken 17 times.
7020 (pls->ts_offset == AV_NOPTS_VALUE ||
2759
2/2
✓ Branch 0 taken 6252 times.
✓ Branch 1 taken 751 times.
7003 (!c->first_timestamp_locked &&
2760
2/2
✓ Branch 0 taken 6232 times.
✓ Branch 1 taken 20 times.
6252 c->first_timestamp_pls == pls &&
2761
2/2
✓ Branch 0 taken 2981 times.
✓ Branch 1 taken 3251 times.
6232 pls->cur_seq_no <= pls->first_read_seq_no + 1))) {
2762 2998 int adjusted = 0;
2763 /* Packet timestamp rebased onto the start of the segment list. */
2764 2998 int64_t seg_idx = pls->first_read_seq_no - pls->start_seq_no;
2765
1/2
✓ Branch 0 taken 2998 times.
✗ Branch 1 not taken.
2998 int64_t ts = av_rescale_q(pls->pkt->pts != AV_NOPTS_VALUE ?
2766 2998 pls->pkt->pts : pls->pkt->dts,
2767 2998 get_timebase(pls), AV_TIME_BASE_Q);
2768
2769
3/4
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 2998 times.
✓ Branch 2 taken 36 times.
✗ Branch 3 not taken.
3034 for (int64_t k = 0; k < seg_idx && k < pls->n_segments; k++)
2770 36 ts -= pls->segments[k]->duration;
2771
2772
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 2982 times.
2998 if (c->first_timestamp == AV_NOPTS_VALUE) {
2773 16 c->first_timestamp = ts;
2774 16 c->first_timestamp_pls = pls;
2775 16 adjusted = 1;
2776
2/2
✓ Branch 0 taken 2981 times.
✓ Branch 1 taken 1 times.
2982 } else if (c->first_timestamp_pls == pls) {
2777 /* first_timestamp came from the first packet in mux
2778 * order, but another stream in the same segment may
2779 * start earlier. Lower the estimate while packets of
2780 * that segment are still arriving. */
2781 2981 int64_t delta = c->first_timestamp - ts;
2782
2783
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2981 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
2981 if (delta > 0 && delta <= pls->target_duration) {
2784 c->first_timestamp = ts;
2785 for (int k = 0; k < c->n_playlists; k++)
2786 if (c->playlists[k]->ts_offset != AV_NOPTS_VALUE)
2787 c->playlists[k]->ts_offset += delta;
2788 adjusted = 1;
2789 }
2790 }
2791
2792
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 2981 times.
2998 if (pls->ts_offset == AV_NOPTS_VALUE)
2793 17 pls->ts_offset = ts - c->first_timestamp;
2794
2795
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 2982 times.
2998 if (adjusted) {
2796
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 16 times.
33 for (unsigned k = 0; k < s->nb_streams; k++)
2797 17 s->streams[k]->start_time =
2798 17 av_rescale_q(c->first_timestamp, AV_TIME_BASE_Q,
2799 17 s->streams[k]->time_base);
2800 16 av_log(s, AV_LOG_DEBUG,
2801 "First timestamp %"PRId64" from playlist %d\n",
2802 c->first_timestamp, pls->index);
2803 }
2804 }
2805
2806 7020 seg = current_segment(pls);
2807
3/6
✓ Branch 0 taken 7016 times.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 7016 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
7020 if (seg && seg->key_type == KEY_SAMPLE_AES && !strstr(pls->ctx->iformat->name, "mov")) {
2808 enum AVCodecID codec_id = pls->ctx->streams[pls->pkt->stream_index]->codecpar->codec_id;
2809 memcpy(c->crypto_ctx.iv, seg->iv, sizeof(seg->iv));
2810 memcpy(c->crypto_ctx.key, pls->key, sizeof(pls->key));
2811 ff_hls_senc_decrypt_frame(codec_id, &c->crypto_ctx, pls->pkt);
2812 }
2813
2814
2/2
✓ Branch 0 taken 6394 times.
✓ Branch 1 taken 626 times.
7020 if (pls->seek_timestamp == AV_NOPTS_VALUE)
2815 6394 break;
2816
2817
2/2
✓ Branch 0 taken 341 times.
✓ Branch 1 taken 285 times.
626 if (pls->seek_stream_index < 0 ||
2818
1/2
✓ Branch 0 taken 341 times.
✗ Branch 1 not taken.
341 pls->seek_stream_index == pls->pkt->stream_index) {
2819
2820
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 626 times.
626 if (pls->pkt->dts == AV_NOPTS_VALUE) {
2821 pls->seek_timestamp = AV_NOPTS_VALUE;
2822 53 break;
2823 }
2824
2825 626 tb = get_timebase(pls);
2826 626 ts_diff = av_rescale_q_rnd(pls->pkt->dts, tb,
2827 626 AV_TIME_BASE_Q, AV_ROUND_DOWN) -
2828 626 pls->seek_timestamp;
2829
4/4
✓ Branch 0 taken 90 times.
✓ Branch 1 taken 536 times.
✓ Branch 2 taken 64 times.
✓ Branch 3 taken 26 times.
626 if (ts_diff >= 0 && (pls->seek_flags & AVSEEK_FLAG_ANY ||
2830
2/2
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 37 times.
64 pls->pkt->flags & AV_PKT_FLAG_KEY)) {
2831 53 pls->seek_timestamp = AV_NOPTS_VALUE;
2832 53 break;
2833 }
2834 }
2835 573 av_packet_unref(pls->pkt);
2836 }
2837 }
2838 /* Check if this stream has the packet with the lowest dts */
2839
2/2
✓ Branch 0 taken 6541 times.
✓ Branch 1 taken 12 times.
6553 if (pls->pkt->data) {
2840 6541 struct playlist *minpls = minplaylist < 0 ?
2841
2/2
✓ Branch 0 taken 121 times.
✓ Branch 1 taken 6420 times.
6541 NULL : c->playlists[minplaylist];
2842
2/2
✓ Branch 0 taken 6420 times.
✓ Branch 1 taken 121 times.
6541 if (minplaylist < 0) {
2843 6420 minplaylist = i;
2844 } else {
2845 121 int64_t dts = pls->pkt->dts;
2846 121 int64_t mindts = minpls->pkt->dts;
2847
2848
2/4
✓ Branch 0 taken 121 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 121 times.
✗ Branch 3 not taken.
121 if (dts == AV_NOPTS_VALUE ||
2849
2/2
✓ Branch 1 taken 39 times.
✓ Branch 2 taken 82 times.
121 (mindts != AV_NOPTS_VALUE && compare_ts_with_wrapdetect(dts, pls, mindts, minpls) < 0))
2850 39 minplaylist = i;
2851 }
2852 }
2853 }
2854
2855 /* If we got a packet, return it */
2856
2/2
✓ Branch 0 taken 6420 times.
✓ Branch 1 taken 12 times.
6432 if (minplaylist >= 0) {
2857 6420 struct playlist *pls = c->playlists[minplaylist];
2858 AVStream *ist;
2859 AVStream *st;
2860
2861 6420 ret = update_streams_from_subdemuxer(s, pls);
2862
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6420 times.
6420 if (ret < 0) {
2863 av_packet_unref(pls->pkt);
2864 return ret;
2865 }
2866
2867 // If sub-demuxer reports updated metadata, copy it to the first stream
2868 // and set its AVSTREAM_EVENT_FLAG_METADATA_UPDATED flag.
2869
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 6419 times.
6420 if (pls->ctx->event_flags & AVFMT_EVENT_FLAG_METADATA_UPDATED) {
2870
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (pls->n_main_streams) {
2871 1 st = pls->main_streams[0];
2872 1 av_dict_copy(&st->metadata, pls->ctx->metadata, 0);
2873 1 st->event_flags |= AVSTREAM_EVENT_FLAG_METADATA_UPDATED;
2874 }
2875 1 pls->ctx->event_flags &= ~AVFMT_EVENT_FLAG_METADATA_UPDATED;
2876 }
2877
2878 /* check if noheader flag has been cleared by the subdemuxer */
2879
3/4
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 6406 times.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
6420 if (pls->has_noheader_flag && !(pls->ctx->ctx_flags & AVFMTCTX_NOHEADER)) {
2880 14 pls->has_noheader_flag = 0;
2881 14 update_noheader_flag(s);
2882 }
2883
2884
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6420 times.
6420 if (pls->pkt->stream_index >= pls->n_main_streams) {
2885 av_log(s, AV_LOG_ERROR, "stream index inconsistency: index %d, %d main streams, %d subdemuxer streams\n",
2886 pls->pkt->stream_index, pls->n_main_streams, pls->ctx->nb_streams);
2887 av_packet_unref(pls->pkt);
2888 return AVERROR_BUG;
2889 }
2890
2891 6420 ist = pls->ctx->streams[pls->pkt->stream_index];
2892 6420 st = pls->main_streams[pls->pkt->stream_index];
2893
2894 // Propagate timed-ID3 metadata changes to the main stream, suppressing
2895 // duplicate packets that repeat unchanged metadata at segment boundaries.
2896
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 6418 times.
6420 if (ist->codecpar->codec_id == AV_CODEC_ID_TIMED_ID3 &&
2897
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 pls->pkt->stream_index == pls->timed_id3_stream_index &&
2898
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 ist->event_flags & AVSTREAM_EVENT_FLAG_METADATA_UPDATED) {
2899 2 ist->event_flags &= ~AVSTREAM_EVENT_FLAG_METADATA_UPDATED;
2900
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
2 if (metadata_has_changed(pls->timed_id3_metadata, ist->metadata)) {
2901 1 int ret = av_dict_copy(&st->metadata, ist->metadata, 0);
2902
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (ret < 0)
2903 return ret;
2904 1 av_dict_free(&pls->timed_id3_metadata);
2905 1 ret = av_dict_copy(&pls->timed_id3_metadata, ist->metadata, 0);
2906
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (ret < 0)
2907 return ret;
2908 1 st->event_flags |= AVSTREAM_EVENT_FLAG_METADATA_UPDATED;
2909 }
2910 }
2911
2912 6420 av_packet_move_ref(pkt, pls->pkt);
2913 6420 pkt->stream_index = st->index;
2914
2915
1/2
✓ Branch 0 taken 6420 times.
✗ Branch 1 not taken.
6420 if (pkt->dts != AV_NOPTS_VALUE)
2916 6420 c->cur_timestamp = av_rescale_q(pkt->dts,
2917 ist->time_base,
2918 6420 AV_TIME_BASE_Q);
2919
2920 /* There may be more situations where this would be useful, but this at least
2921 * handles newly probed codecs properly (i.e. request_probe by mpegts). */
2922
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 6409 times.
6420 if (ist->codecpar->codec_id != st->codecpar->codec_id) {
2923 11 ret = set_stream_info_from_input_stream(st, pls, ist);
2924
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 if (ret < 0) {
2925 return ret;
2926 }
2927 }
2928
2929 6420 return 0;
2930 }
2931 12 return AVERROR_EOF;
2932 }
2933
2934 27 static int hls_read_seek(AVFormatContext *s, int stream_index,
2935 int64_t timestamp, int flags)
2936 {
2937 27 HLSContext *c = s->priv_data;
2938 27 struct playlist *seek_pls = NULL;
2939 int i, j;
2940 int stream_subdemuxer_index;
2941 int64_t first_timestamp, seek_timestamp, duration;
2942 int64_t seq_no, seg_start_ts;
2943 27 int snapped_to_segment = 0;
2944
2945
2/4
✓ Branch 0 taken 27 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 27 times.
27 if ((flags & AVSEEK_FLAG_BYTE) || (c->ctx->ctx_flags & AVFMTCTX_UNSEEKABLE))
2946 return AVERROR(ENOSYS);
2947
2948 27 seek_timestamp = av_rescale_q_rnd(timestamp,
2949 27 s->streams[stream_index]->time_base,
2950 27 AV_TIME_BASE_Q, AV_ROUND_DOWN);
2951
2952 /* find the playlist with the specified stream */
2953
2/2
✓ Branch 0 taken 53 times.
✓ Branch 1 taken 27 times.
80 for (i = 0; i < c->n_playlists; i++) {
2954 53 struct playlist *pls = c->playlists[i];
2955
2/2
✓ Branch 0 taken 53 times.
✓ Branch 1 taken 26 times.
79 for (j = 0; j < pls->n_main_streams; j++) {
2956
2/2
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 26 times.
53 if (pls->main_streams[j] == s->streams[stream_index]) {
2957 27 seek_pls = pls;
2958 27 stream_subdemuxer_index = j;
2959 27 break;
2960 }
2961 }
2962 }
2963
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 27 times.
27 if (!seek_pls)
2964 return AVERROR(EIO);
2965
2966 /* Live and EVENT playlists may have gained segments since the last load,
2967 * and sliding-window playlists may also have expired some. Refresh before
2968 * resolving a seek that may fall outside the loaded window, rate limited
2969 * to the minimum reload interval (RFC 8216 6.3.4). */
2970
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 26 times.
27 if (!seek_pls->finished) {
2971 1 int64_t elapsed = av_gettime_relative() - seek_pls->last_load_time;
2972 2 int64_t start = c->first_timestamp == AV_NOPTS_VALUE ?
2973
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 0 : c->first_timestamp;
2974 1 int64_t end = start;
2975 int need_refresh;
2976
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 1 times.
8 for (i = 0; i < seek_pls->n_segments; i++)
2977 7 end += seek_pls->segments[i]->duration;
2978 1 need_refresh = seek_timestamp >= end;
2979
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (seek_pls->type == PLS_TYPE_UNSPECIFIED)
2980 need_refresh |= seek_timestamp < start + elapsed +
2981 seek_pls->target_duration;
2982
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
1 if (need_refresh && elapsed >= default_reload_interval(seek_pls))
2983 parse_playlist(c, seek_pls->url, seek_pls, NULL);
2984 }
2985
2986 54 first_timestamp = c->first_timestamp == AV_NOPTS_VALUE ?
2987
1/2
✓ Branch 0 taken 27 times.
✗ Branch 1 not taken.
27 0 : c->first_timestamp;
2988
2989 54 duration = s->duration == AV_NOPTS_VALUE ?
2990
1/2
✓ Branch 0 taken 27 times.
✗ Branch 1 not taken.
27 0 : s->duration;
2991
2992 /* Only finished playlists cannot grow past the known duration. */
2993
3/4
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 26 times.
✗ Branch 3 not taken.
27 if (seek_pls->finished &&
2994
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
26 0 < duration && duration < seek_timestamp - first_timestamp)
2995 return AVERROR(EIO);
2996
2997 /* check if the timestamp is valid for the playlist with the
2998 * specified stream index */
2999
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 27 times.
27 if (!find_timestamp_in_playlist(c, seek_pls, seek_timestamp, &seq_no, &seg_start_ts))
3000 return AVERROR(EIO);
3001
3002
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 9 times.
27 if (s->streams[stream_index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
3003
3/4
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
18 flags & AVSEEK_FLAG_BACKWARD && !(flags & AVSEEK_FLAG_ANY)) {
3004 /* Seeking to start of segment ensures we seek to a keyframe located
3005 * before the given timestamp. */
3006 9 seek_timestamp = seg_start_ts;
3007 9 snapped_to_segment = 1;
3008 }
3009
3010 27 av_log(s, AV_LOG_DEBUG, "Seek to %"PRId64" mapped to segment %"PRId64
3011 " of playlist %d (start %"PRId64")\n",
3012 seek_timestamp, seq_no, seek_pls->index, seg_start_ts);
3013
3014 /* Keep timeline start fixed from now on. */
3015 27 c->first_timestamp_locked = 1;
3016
3017 /* set segment now so we do not need to search again below */
3018 27 seek_pls->cur_seq_no = seq_no;
3019 27 seek_pls->seek_stream_index = stream_subdemuxer_index;
3020
3021 /* Reset PTS wrap detection so backward seeks don't get misinterpreted
3022 * as a forward PTS wrap. */
3023
2/2
✓ Branch 0 taken 53 times.
✓ Branch 1 taken 27 times.
80 for (i = 0; i < (int)s->nb_streams; i++)
3024 53 ffstream(s->streams[i])->pts_wrap_reference = AV_NOPTS_VALUE;
3025
3026
2/2
✓ Branch 0 taken 53 times.
✓ Branch 1 taken 27 times.
80 for (i = 0; i < c->n_playlists; i++) {
3027 /* Reset reading */
3028 53 struct playlist *pls = c->playlists[i];
3029 53 AVIOContext *const pb = &pls->pb.pub;
3030 53 ff_format_io_close(pls->parent, &pls->input);
3031 53 pls->input_read_done = 0;
3032 53 pls->first_read_seq_no = -1;
3033 53 pls->input_reuse = 0;
3034 53 ff_format_io_close(pls->parent, &pls->input_next);
3035 53 pls->input_next_requested = 0;
3036 53 av_packet_unref(pls->pkt);
3037 53 pb->eof_reached = 0;
3038 /* Clear any buffered data */
3039 53 pb->buf_end = pb->buf_ptr = pb->buffer;
3040 /* Reset the pos, to let the mpegts/mov demuxer know we've seeked. */
3041 53 pb->pos = 0;
3042 /* Flush the packet queue of the subdemuxer. */
3043
1/2
✓ Branch 0 taken 53 times.
✗ Branch 1 not taken.
53 if (pls->ctx) {
3044 53 ff_read_frame_flush(pls->ctx);
3045
2/2
✓ Branch 0 taken 53 times.
✓ Branch 1 taken 53 times.
106 for (j = 0; j < (int)pls->ctx->nb_streams; j++)
3046 53 ffstream(pls->ctx->streams[j])->pts_wrap_reference = AV_NOPTS_VALUE;
3047 }
3048
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 53 times.
53 if (pls->is_subtitle)
3049 avformat_close_input(&pls->ctx);
3050
3051 /* Reset the init segment so it's re-fetched and served appropriately */
3052 53 pls->cur_init_section = NULL;
3053
3054 /* The discard in hls_read_packet compares this playlist's packet DTS
3055 * against seek_timestamp, but seek_timestamp is on c->first_timestamp's
3056 * baseline (whichever stream produced the first packet). Streams can
3057 * have different DTS baselines, so translate the threshold onto this
3058 * playlist's own baseline using its captured offset. */
3059
1/2
✓ Branch 0 taken 53 times.
✗ Branch 1 not taken.
53 if (pls->ts_offset != AV_NOPTS_VALUE)
3060 53 pls->seek_timestamp = seek_timestamp + pls->ts_offset;
3061 else
3062 pls->seek_timestamp = seek_timestamp;
3063 53 pls->seek_flags = flags;
3064
3065
4/4
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 26 times.
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 18 times.
53 if (pls == seek_pls && snapped_to_segment) {
3066 /* The first keyframe of the target segment may have a slightly
3067 * lower DTS than the EXTINF-derived seg_start_ts. Relax the cutoff
3068 * to not discard the keyframe and land one segment late. */
3069 9 int64_t idx = seq_no - pls->start_seq_no;
3070
2/4
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
9 if (idx >= 0 && idx < pls->n_segments)
3071 9 pls->seek_timestamp -= pls->segments[idx]->duration;
3072 }
3073
3074
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 27 times.
53 if (pls != seek_pls) {
3075 /* set closest segment seq_no for playlists not handled above */
3076 26 find_timestamp_in_playlist(c, pls, seek_timestamp, &pls->cur_seq_no, NULL);
3077 /* seek the playlist to the given position without taking
3078 * keyframes into account since this playlist does not have the
3079 * specified stream where we should look for the keyframes */
3080 26 pls->seek_stream_index = -1;
3081 26 pls->seek_flags |= AVSEEK_FLAG_ANY;
3082 }
3083
3084 53 pls->last_seq_no = pls->cur_seq_no;
3085 }
3086
3087 27 c->cur_timestamp = seek_timestamp;
3088
3089 27 return 0;
3090 }
3091
3092 7934 static int hls_probe(const AVProbeData *p)
3093 {
3094 /* Require #EXTM3U at the start, and either one of the ones below
3095 * somewhere for a proper match. */
3096
2/2
✓ Branch 0 taken 7918 times.
✓ Branch 1 taken 16 times.
7934 if (strncmp(p->buf, "#EXTM3U", 7))
3097 7918 return 0;
3098
3099
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 1 times.
16 if (strstr(p->buf, "#EXT-X-STREAM-INF:") ||
3100
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 strstr(p->buf, "#EXT-X-TARGETDURATION:") ||
3101 strstr(p->buf, "#EXT-X-MEDIA-SEQUENCE:")) {
3102
3103
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
16 int mime_ok = p->mime_type && !(
3104 av_strcasecmp(p->mime_type, "application/vnd.apple.mpegurl") &&
3105 av_strcasecmp(p->mime_type, "audio/mpegurl")
3106 );
3107
3108
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
16 int mime_x = p->mime_type && !(
3109 av_strcasecmp(p->mime_type, "audio/x-mpegurl") &&
3110 av_strcasecmp(p->mime_type, "application/x-mpegurl")
3111 );
3112
3113
2/4
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
16 if (!mime_ok &&
3114
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 !mime_x &&
3115
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
16 !av_match_ext (p->filename, "m3u8,m3u") &&
3116 ff_match_url_ext(p->filename, "m3u8,m3u") <= 0) {
3117 av_log(NULL, AV_LOG_ERROR, "Not detecting m3u8/hls with non standard extension and non standard mime type\n");
3118 return 0;
3119 }
3120
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 if (mime_x)
3121 av_log(NULL, AV_LOG_WARNING, "mime type is not rfc8216 compliant\n");
3122
3123 16 return AVPROBE_SCORE_MAX;
3124 }
3125 return 0;
3126 }
3127
3128 #define OFFSET(x) offsetof(HLSContext, x)
3129 #define FLAGS AV_OPT_FLAG_DECODING_PARAM
3130 static const AVOption hls_options[] = {
3131 {"live_start_index", "segment index to start live streams at (negative values are from the end)",
3132 OFFSET(live_start_index), AV_OPT_TYPE_INT, {.i64 = -3}, INT_MIN, INT_MAX, FLAGS},
3133 {"prefer_x_start", "prefer to use #EXT-X-START if it's in playlist instead of live_start_index",
3134 OFFSET(prefer_x_start), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS},
3135 {"allowed_extensions", "List of file extensions that hls is allowed to access",
3136 OFFSET(allowed_extensions), AV_OPT_TYPE_STRING,
3137 {.str = "3gp,aac,avi,ac3,eac3,flac,mkv,m3u8,m4a,m4s,m4v,mpg,mov,mp2,mp3,mp4,mpeg,mpegts,ogg,ogv,oga,ts,vob,vtt,wav,webvtt"
3138 ",cmfv,cmfa" // Ticket11526 www.nicovideo.jp
3139 ",ec3" // part of Ticket11435 (Elisa Viihde (Finnish online recording service))
3140 ",fmp4" // https://github.com/yt-dlp/yt-dlp/issues/12700
3141 },
3142 INT_MIN, INT_MAX, FLAGS},
3143 {"allowed_segment_extensions", "List of file extensions that hls is allowed to access",
3144 OFFSET(allowed_segment_extensions), AV_OPT_TYPE_STRING,
3145 {.str = "3gp,aac,avi,ac3,eac3,flac,mkv,m3u8,m4a,m4s,m4v,mpg,mov,mp2,mp3,mp4,mpeg,mpegts,ogg,ogv,oga,ts,vob,vtt,wav,webvtt"
3146 ",cmfv,cmfa" // Ticket11526 www.nicovideo.jp
3147 ",ec3" // part of Ticket11435 (Elisa Viihde (Finnish online recording service))
3148 ",fmp4" // https://github.com/yt-dlp/yt-dlp/issues/12700
3149 ",html" // https://flash1.bogulus.cfd/
3150 },
3151 INT_MIN, INT_MAX, FLAGS},
3152 {"extension_picky", "Be picky with all extensions matching",
3153 OFFSET(extension_picky), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, FLAGS},
3154 {"max_reload", "Maximum number of times a insufficient list is attempted to be reloaded",
3155 OFFSET(max_reload), AV_OPT_TYPE_INT, {.i64 = 100}, 0, INT_MAX, FLAGS},
3156 {"m3u8_hold_counters", "The maximum number of times to load m3u8 when it refreshes without new segments",
3157 OFFSET(m3u8_hold_counters), AV_OPT_TYPE_INT, {.i64 = 1000}, 0, INT_MAX, FLAGS},
3158 {"http_persistent", "Use persistent HTTP connections",
3159 OFFSET(http_persistent), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, FLAGS },
3160 {"http_multiple", "Use multiple HTTP connections for fetching segments",
3161 OFFSET(http_multiple), AV_OPT_TYPE_BOOL, {.i64 = -1}, -1, 1, FLAGS},
3162 {"http_seekable", "Use HTTP partial requests, 0 = disable, 1 = enable, -1 = auto",
3163 OFFSET(http_seekable), AV_OPT_TYPE_BOOL, { .i64 = -1}, -1, 1, FLAGS},
3164 {"seg_format_options", "Set options for segment demuxer",
3165 OFFSET(seg_format_opts), AV_OPT_TYPE_DICT, {.str = NULL}, 0, 0, FLAGS},
3166 {"seg_max_retry", "Maximum number of times to reload a segment on error.",
3167 OFFSET(seg_max_retry), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, FLAGS},
3168 {NULL}
3169 };
3170
3171 static const AVClass hls_class = {
3172 .class_name = "hls demuxer",
3173 .item_name = av_default_item_name,
3174 .option = hls_options,
3175 .version = LIBAVUTIL_VERSION_INT,
3176 };
3177
3178 const FFInputFormat ff_hls_demuxer = {
3179 .p.name = "hls",
3180 .p.long_name = NULL_IF_CONFIG_SMALL("Apple HTTP Live Streaming"),
3181 .p.priv_class = &hls_class,
3182 .p.flags = AVFMT_NOGENSEARCH | AVFMT_TS_DISCONT | AVFMT_NO_BYTE_SEEK | AVFMT_SHOW_IDS,
3183 .priv_data_size = sizeof(HLSContext),
3184 .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP,
3185 .read_probe = hls_probe,
3186 .read_header = hls_read_header,
3187 .read_packet = hls_read_packet,
3188 .read_close = hls_close,
3189 .read_seek = hls_read_seek,
3190 };
3191