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