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, ×tamp, &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