1 /*****************************************************************************
2 * hds.c: Http Dynamic Streaming (HDS) stream filter
3 *****************************************************************************
5 * Author: Jonathan Thambidurai <jonathan _AT_ fastly _DOT_ com>
6 * Heavily inspired by SMooth module of Frédéric Yhuel <fyhuel _AT_ viotech _DOT_ net>
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 *****************************************************************************/
27 #include <limits.h> /* INT_MAX */
29 #include <vlc_common.h>
30 #include <vlc_plugin.h>
31 #include <vlc_stream.h>
32 #include <vlc_strings.h> /* b64_decode */
34 #include <vlc_charset.h> /* FromCharset */
35 #include <vlc_es.h> /* UNKNOWN_ES */
37 typedef struct chunk_s
39 int64_t duration; /* chunk duration in afrt timescale units */
43 uint32_t frun_entry; /* Used to speed things up in vod situations */
47 uint32_t mdat_pos; /* position in the mdat */
58 typedef struct segment_run_s
60 uint32_t first_segment;
61 uint32_t fragments_per_segment;
64 typedef struct fragment_run_s
66 uint32_t fragment_number_start;
67 uint32_t fragment_duration;
68 uint64_t fragment_timestamp;
72 typedef struct hds_stream_s
74 /* linked-list of chunks */
76 chunk_t *chunks_livereadpos;
77 chunk_t *chunks_downloadpos;
79 char* quality_segment_modifier;
81 /* we can make this configurable */
82 uint64_t download_leadtime;
84 /* in timescale units */
85 uint32_t total_duration;
87 uint32_t afrt_timescale;
89 /* these two values come from the abst */
91 uint64_t live_current_time;
93 vlc_mutex_t abst_lock;
98 /* can be left as null */
101 /* this comes from the manifest media section */
104 /* this comes from the bootstrap info */
107 #define MAX_HDS_SERVERS 10
108 char* server_entries[MAX_HDS_SERVERS];
109 uint8_t server_entry_count;
111 #define MAX_HDS_SEGMENT_RUNS 256
112 segment_run_t segment_runs[MAX_HDS_SEGMENT_RUNS];
113 uint8_t segment_run_count;
115 #define MAX_HDS_FRAGMENT_RUNS 10000
116 fragment_run_t fragment_runs[MAX_HDS_FRAGMENT_RUNS];
117 uint32_t fragment_run_count;
120 /* this is effectively just a sanity check mechanism */
121 #define MAX_REQUEST_SIZE (50*1024*1024)
125 char *base_url; /* URL common part for chunks */
126 vlc_thread_t live_thread;
127 vlc_thread_t dl_thread;
129 /* we pend on peek until some number of segments arrives; otherwise
130 * the downstream system dies in case of playback */
131 uint64_t chunk_count;
133 vlc_array_t *hds_streams; /* available streams */
135 uint32_t flv_header_bytes_sent;
136 uint64_t duration_seconds;
142 typedef struct _bootstrap_info {
150 typedef struct _media_info {
156 /*****************************************************************************
158 *****************************************************************************/
159 static int Open( vlc_object_t * );
160 static void Close( vlc_object_t * );
163 set_category( CAT_INPUT )
164 set_subcategory( SUBCAT_INPUT_STREAM_FILTER )
165 set_description( N_("HTTP Dynamic Streaming") )
166 set_shortname( "Dynamic Streaming")
167 add_shortcut( "hds" )
168 set_capability( "stream_filter", 30 )
169 set_callbacks( Open, Close )
172 static int Read( stream_t *, void *, unsigned );
173 static int Peek( stream_t *, const uint8_t **, unsigned );
174 static int Control( stream_t *, int , va_list );
176 static inline bool isFQUrl( char* url )
178 return ( NULL != vlc_strcasestr( url, "https://") ||
179 NULL != vlc_strcasestr( url, "http://" ) );
182 static bool isHDS( stream_t *s )
185 int i_size = stream_Peek( s->p_source, (const uint8_t**) &peek, 200 );
191 if( !memcmp( peek, "\xFF\xFE", 2 ) )
193 str = FromCharset( "UTF-16LE", peek, i_size );
195 else if( !memcmp( peek, "\xFE\xFF", 2 ) )
197 str = FromCharset( "UTF-16BE", peek, i_size );
200 str = strndup( peek, i_size );
205 bool ret = strstr( str, "<manifest" ) != NULL;
210 static uint8_t* parse_asrt( vlc_object_t* p_this,
215 uint8_t* data_p = data;
217 uint32_t asrt_len = 0;
218 asrt_len = U32_AT( data_p );
219 if( asrt_len > data_end - data ||
220 data_end - data < 14 )
222 msg_Err( p_this, "Not enough asrt data (%"PRIu32", %lu)", asrt_len, data_end - data );
226 data_p += sizeof(asrt_len);
228 if( 0 != memcmp( "asrt", data_p, 4 ) )
230 msg_Err( p_this, "Cant find asrt in bootstrap" );
235 /* ignore flags and versions (we don't handle multiple updates) */
238 uint8_t quality_entry_count = *data_p;
239 bool quality_found = false;
242 if( ! s->quality_segment_modifier )
244 quality_found = true;
247 while( quality_entry_count-- > 0 )
249 char* str_start = (char*) data_p;
250 data_p = memchr( data_p, '\0', data_end - data_p );
253 msg_Err( p_this, "Couldn't find quality entry string in asrt" );
258 if( ! quality_found )
260 if( ! strncmp( str_start, s->quality_segment_modifier,
261 strlen(s->quality_segment_modifier) ) )
263 quality_found = true;
267 if( data_p >= data_end )
269 msg_Err( p_this, "Premature end of asrt in quality entries" );
274 if( data_end - data_p < 4 )
276 msg_Err( p_this, "Premature end of asrt after quality entries" );
280 uint32_t segment_run_entry_count = U32_AT( data_p );
281 data_p += sizeof(segment_run_entry_count);
283 if( data_end - data_p < 8 * segment_run_entry_count )
285 msg_Err( p_this, "Not enough data in asrt for segment run entries" );
289 if( segment_run_entry_count >= MAX_HDS_SEGMENT_RUNS )
291 msg_Err( p_this, "Too many segment runs" );
295 while( segment_run_entry_count-- > 0 )
299 s->segment_runs[s->segment_run_count].first_segment = U32_AT(data_p);
305 s->segment_runs[s->segment_run_count].fragments_per_segment = U32_AT(data_p);
309 s->segment_run_count++;
315 static uint8_t* parse_afrt( vlc_object_t* p_this,
320 uint8_t* data_p = data;
322 uint32_t afrt_len = U32_AT( data_p );
323 if( afrt_len > data_end - data ||
324 data_end - data < 9 )
326 msg_Err( p_this, "Not enough afrt data %u, %ld", afrt_len, data_end - data );
329 data_p += sizeof(afrt_len);
331 if( 0 != memcmp( data_p, "afrt", 4 ) )
333 msg_Err( p_this, "Cant find afrt in bootstrap" );
338 /* ignore flags and versions (we don't handle multiple updates) */
341 if( data_end - data_p < 9 )
343 msg_Err( p_this, "afrt is too short" );
347 s->afrt_timescale = U32_AT( data_p );
350 bool quality_found = false;
351 if( ! s->quality_segment_modifier )
353 quality_found = true;
356 uint32_t quality_entry_count = *data_p;
358 while( quality_entry_count-- > 0 )
360 char* str_start = (char*)data_p;
361 data_p = memchr( data_p, '\0', data_end - data_p );
364 msg_Err( p_this, "Couldn't find quality entry string in afrt" );
369 if( ! quality_found )
371 if( ! strncmp( str_start, s->quality_segment_modifier,
372 strlen(s->quality_segment_modifier) ) )
374 quality_found = true;
379 if( data_end - data_p < 5 )
381 msg_Err( p_this, "No more space in afrt after quality entries" );
385 uint32_t fragment_run_entry_count = U32_AT( data_p );
386 data_p += sizeof(uint32_t);
388 while(fragment_run_entry_count-- > 0)
390 if( data_end - data_p < 16 )
392 msg_Err( p_this, "Not enough data in afrt" );
396 if( s->fragment_run_count >= MAX_HDS_FRAGMENT_RUNS )
398 msg_Err( p_this, "Too many fragment runs, exiting" );
402 s->fragment_runs[s->fragment_run_count].fragment_number_start = U32_AT(data_p);
405 s->fragment_runs[s->fragment_run_count].fragment_timestamp = U64_AT( data_p );
408 s->fragment_runs[s->fragment_run_count].fragment_duration = U32_AT( data_p );
411 s->fragment_runs[s->fragment_run_count].discont = 0;
412 if( s->fragment_runs[s->fragment_run_count].fragment_duration == 0 )
414 /* discontinuity flag */
415 s->fragment_runs[s->fragment_run_count].discont = *(data_p++);
418 s->fragment_run_count++;
424 static inline chunk_t* chunk_new()
426 chunk_t* chunk = calloc(1, sizeof(chunk_t));
430 static void chunk_free( chunk_t * chunk )
432 FREENULL( chunk->data );
436 static void parse_BootstrapData( vlc_object_t* p_this,
441 uint8_t* data_p = data;
443 uint32_t abst_len = U32_AT( data_p );
444 if( abst_len > data_end - data
445 || data_end - data < 29 /* min size of data */ )
447 msg_Warn( p_this, "Not enough bootstrap data" );
450 data_p += sizeof(abst_len);
452 if( 0 != memcmp( data_p, "abst", 4 ) )
454 msg_Warn( p_this, "Cant find abst in bootstrap" );
462 /* we ignore the version */
465 /* some flags we don't care about here because they are
471 s->timescale = U32_AT( data_p );
472 data_p += sizeof(s->timescale);
474 s->live_current_time = U64_AT( data_p );
475 data_p += sizeof(s->live_current_time);
477 /* smtpe time code offset */
480 s->movie_id = strndup( (char*)data_p, data_end - data_p );
481 data_p += ( strlen( s->movie_id ) + 1 );
483 if( data_end - data_p < 4 ) {
484 msg_Warn( p_this, "Not enough bootstrap after Movie Identifier" );
488 uint8_t server_entry_count = 0;
489 server_entry_count = (uint8_t) *data_p;
492 s->server_entry_count = 0;
493 while( server_entry_count-- > 0 )
495 if( s->server_entry_count < MAX_HDS_SERVERS )
497 s->server_entries[s->server_entry_count++] = strndup( (char*)data_p,
499 data_p += strlen( s->server_entries[s->server_entry_count-1] ) + 1;
503 msg_Warn( p_this, "Too many servers" );
504 data_p = memchr( data_p, '\0', data_end - data_p );
507 msg_Err( p_this, "Couldn't find server entry" );
513 if( data_p >= data_end )
515 msg_Warn( p_this, "Premature end of bootstrap info while reading servers" );
520 if( data_end - data_p < 3 ) {
521 msg_Warn( p_this, "Not enough bootstrap after Servers" );
525 s->quality_segment_modifier = NULL;
527 uint8_t quality_entry_count = *data_p;
530 if( quality_entry_count > 1 )
532 msg_Err( p_this, "I don't know what to do with multiple quality levels in the bootstrap - shouldn't this be handled at the manifest level?" );
536 s->quality_segment_modifier = NULL;
537 while( quality_entry_count-- > 0 )
539 if( s->quality_segment_modifier )
541 s->quality_segment_modifier = strndup( (char*)data_p, data_end - data_p );
543 data_p += strnlen( (char*)data_p, data_end - data_p ) + 1;
546 if( data_end - data_p < 2 ) {
547 msg_Warn( p_this, "Not enough bootstrap after quality entries" );
551 /* ignoring "DrmData" */
552 data_p = memchr( data_p, '\0', data_end - data_p );
555 msg_Err( p_this, "Couldn't find DRM Data" );
560 if( data_end - data_p < 2 ) {
561 msg_Warn( p_this, "Not enough bootstrap after drm data" );
565 /* ignoring "metadata" */
566 data_p = memchr( data_p, '\0', data_end - data_p );
569 msg_Err( p_this, "Couldn't find metadata");
574 if( data_end - data_p < 2 ) {
575 msg_Warn( p_this, "Not enough bootstrap after drm data" );
579 uint8_t asrt_count = *data_p;
582 s->segment_run_count = 0;
583 while( asrt_count-- > 0 &&
585 (data_p = parse_asrt( p_this, s, data_p, data_end )) );
589 msg_Warn( p_this, "Couldn't find afrt data" );
593 uint8_t afrt_count = *data_p;
596 s->fragment_run_count = 0;
597 while( afrt_count-- > 0 &&
599 (data_p = parse_afrt( p_this, s, data_p, data_end )) );
602 /* this only works with ANSI characters - this is ok
603 for the bootstrapinfo field which this function is
604 exclusively used for since it is merely a base64 encoding
606 static bool is_whitespace( char c )
616 /* see above note for is_whitespace */
617 static void whitespace_substr( char** start,
620 while( is_whitespace( **start ) && *start != *end ) {
627 while( is_whitespace(*(*end - 1) ) ) {
632 /* returns length (could be zero, indicating all of remaining data) */
633 /* ptr is to start of data, right after 'mdat' string */
634 static uint32_t find_chunk_mdat( vlc_object_t* p_this,
635 uint8_t* chunkdata, uint8_t* chunkdata_end,
638 uint8_t* boxname = NULL;
639 uint8_t* boxdata = NULL;
640 uint64_t boxsize = 0;
643 if( chunkdata_end < chunkdata ||
644 chunkdata_end - chunkdata < 8 )
646 msg_Err( p_this, "Couldn't find mdat in box 1!" );
651 boxsize = (uint64_t)U32_AT( chunkdata );
659 if( chunkdata_end - chunkdata >= 12 )
661 boxsize = U64_AT(chunkdata);
666 msg_Err( p_this, "Couldn't find mdat in box 2!");
671 chunkdata += (boxsize - 16);
676 chunkdata += (boxsize - 8);
678 } while ( 0 != memcmp( boxname, "mdat", 4 ) );
682 return chunkdata_end - ((uint8_t*)boxdata);
685 /* returns data ptr if valid (updating the chunk itself
686 tells the reader that the chunk is safe to read, which is not yet correct)*/
687 static uint8_t* download_chunk( stream_t *s,
689 hds_stream_t* stream, chunk_t* chunk )
691 const char* quality = "";
692 char* server_base = sys->base_url;
693 if( stream->server_entry_count > 0 &&
694 strlen(stream->server_entries[0]) > 0 )
696 server_base = stream->server_entries[0];
699 if( stream->quality_segment_modifier )
701 quality = stream->quality_segment_modifier;
704 const char* movie_id = "";
705 if( stream->url && strlen(stream->url) > 0 )
707 if( isFQUrl( stream->url ) )
709 server_base = stream->url;
713 movie_id = stream->url;
718 if( 0 > asprintf( &fragment_url, "%s/%s%sSeg%u-Frag%u",
723 chunk->frag_num ) ) {
724 msg_Err(s, "Failed to allocate memory for fragment url" );
728 msg_Info(s, "Downloading fragment %s", fragment_url );
730 stream_t* download_stream = stream_UrlNew( s, fragment_url );
731 if( ! download_stream )
733 msg_Err(s, "Failed to download fragment %s", fragment_url );
734 free( fragment_url );
735 chunk->failed = true;
738 free( fragment_url );
740 int64_t size = stream_Size( download_stream );
741 chunk->data_len = (uint32_t) size;
743 if( size > MAX_REQUEST_SIZE )
745 msg_Err(s, "Strangely-large chunk of %"PRIi64" Bytes", size );
749 uint8_t* data = malloc( size );
752 msg_Err(s, "Couldn't allocate chunk" );
756 int read = stream_Read( download_stream, data,
758 chunk->data_len = read;
762 msg_Err( s, "Requested %"PRIi64" bytes, "\
763 "but only got %d", size, read );
764 data = realloc( chunk->data, read );
767 chunk->failed = true;
772 chunk->failed = false;
775 stream_Delete( download_stream );
779 static void* download_thread( void* p )
781 vlc_object_t* p_this = (vlc_object_t*)p;
782 stream_t* s = (stream_t*) p_this;
783 stream_sys_t* sys = s->p_sys;
785 if ( vlc_array_count( sys->hds_streams ) == 0 )
788 // TODO: Change here for selectable stream
789 hds_stream_t* hds_stream = sys->hds_streams->pp_elems[0];
791 int canc = vlc_savecancel();
793 vlc_mutex_lock( & hds_stream->dl_lock );
795 while( ! sys->closed )
797 if( ! hds_stream->chunks_downloadpos )
799 chunk_t* chunk = hds_stream->chunks_head;
800 while(chunk && chunk->data )
805 if( chunk && ! chunk->data )
806 hds_stream->chunks_downloadpos = chunk;
809 while( hds_stream->chunks_downloadpos )
811 chunk_t *chunk = hds_stream->chunks_downloadpos;
813 uint8_t *data = download_chunk( (stream_t*)p_this,
818 if( ! chunk->failed )
821 find_chunk_mdat( p_this,
823 data + chunk->data_len,
824 & chunk->mdat_data );
825 if( chunk->mdat_len == 0 ) {
826 chunk->mdat_len = chunk->data_len - (chunk->mdat_data - data);
828 hds_stream->chunks_downloadpos = chunk->next;
835 vlc_cond_wait( & hds_stream->dl_cond,
836 & hds_stream->dl_lock );
839 vlc_mutex_unlock( & hds_stream->dl_lock );
841 vlc_restorecancel( canc );
845 static chunk_t* generate_new_chunk(
846 vlc_object_t* p_this,
848 hds_stream_t* hds_stream )
850 stream_t* s = (stream_t*) p_this;
851 stream_sys_t *sys = s->p_sys;
852 chunk_t *chunk = chunk_new();
853 unsigned int frun_entry = 0;
856 msg_Err( p_this, "Couldn't allocate new chunk!" );
862 chunk->timestamp = last_chunk->timestamp + last_chunk->duration;
863 chunk->frag_num = last_chunk->frag_num + 1;
867 frun_entry = last_chunk->frun_entry;
872 fragment_run_t* first_frun = hds_stream->fragment_runs;
875 chunk->timestamp = (hds_stream->live_current_time * ((uint64_t)hds_stream->afrt_timescale)) / ((uint64_t)hds_stream->timescale);
879 chunk->timestamp = first_frun->fragment_timestamp;
880 chunk->frag_num = first_frun->fragment_number_start;
884 for( ; frun_entry < hds_stream->fragment_run_count;
887 /* check for discontinuity first */
888 if( hds_stream->fragment_runs[frun_entry].fragment_duration == 0 )
890 if( frun_entry == hds_stream->fragment_run_count - 1 )
892 msg_Err( p_this, "Discontinuity but can't find next timestamp!");
896 chunk->frag_num = hds_stream->fragment_runs[frun_entry+1].fragment_number_start;
897 chunk->duration = hds_stream->fragment_runs[frun_entry+1].fragment_duration;
898 chunk->timestamp = hds_stream->fragment_runs[frun_entry+1].fragment_timestamp;
904 if( chunk->frag_num == 0 )
906 if( frun_entry == hds_stream->fragment_run_count - 1 ||
907 ( chunk->timestamp >= hds_stream->fragment_runs[frun_entry].fragment_timestamp &&
908 chunk->timestamp < hds_stream->fragment_runs[frun_entry+1].fragment_timestamp )
911 fragment_run_t* frun = hds_stream->fragment_runs + frun_entry;
912 chunk->frag_num = frun->fragment_number_start + ( (chunk->timestamp - frun->fragment_timestamp) /
913 frun->fragment_duration );
914 chunk->duration = frun->fragment_duration;
919 if( hds_stream->fragment_runs[frun_entry].fragment_number_start <=
921 (frun_entry == hds_stream->fragment_run_count - 1 ||
922 hds_stream->fragment_runs[frun_entry+1].fragment_number_start > chunk->frag_num ) )
924 chunk->duration = hds_stream->fragment_runs[frun_entry].fragment_duration;
925 chunk->timestamp = hds_stream->fragment_runs[frun_entry].fragment_timestamp +
926 chunk->duration * (chunk->frag_num - hds_stream->fragment_runs[frun_entry].fragment_number_start);
931 if( frun_entry == hds_stream->fragment_run_count )
933 msg_Err( p_this, "Couldn'd find the fragment run!" );
939 unsigned int segment = 0;
940 uint64_t fragments_accum = chunk->frag_num;
941 for( srun_entry = 0; srun_entry < hds_stream->segment_run_count;
944 segment = hds_stream->segment_runs[srun_entry].first_segment +
945 (chunk->frag_num - fragments_accum ) / hds_stream->segment_runs[srun_entry].fragments_per_segment;
947 if( srun_entry + 1 == hds_stream->segment_run_count ||
948 hds_stream->segment_runs[srun_entry+1].first_segment > segment )
954 (hds_stream->segment_runs[srun_entry+1].first_segment -
955 hds_stream->segment_runs[srun_entry].first_segment) *
956 hds_stream->segment_runs[srun_entry].fragments_per_segment );
959 chunk->seg_num = segment;
960 chunk->frun_entry = frun_entry;
964 if( (chunk->timestamp + chunk->duration) / hds_stream->afrt_timescale >= sys->duration_seconds )
973 static void maintain_live_chunks(
974 vlc_object_t* p_this,
975 hds_stream_t* hds_stream
978 if( ! hds_stream->chunks_head )
980 /* just start with the earliest in the abst
981 * maybe it would be better to use the currentMediaTime?
982 * but then we are right on the edge of buffering, esp for
984 hds_stream->chunks_head = generate_new_chunk(
985 p_this, 0, hds_stream );
986 hds_stream->chunks_livereadpos = hds_stream->chunks_head;
989 chunk_t* chunk = hds_stream->chunks_head;
991 while( chunk && ( chunk->timestamp * ((uint64_t)hds_stream->timescale) )
992 / ((uint64_t)hds_stream->afrt_timescale)
993 <= hds_stream->live_current_time )
1001 chunk->next = generate_new_chunk( p_this, chunk, hds_stream );
1002 chunk = chunk->next;
1008 vlc_cond_signal( & hds_stream->dl_cond );
1010 chunk = hds_stream->chunks_head;
1011 while( chunk && chunk->data && chunk->mdat_pos >= chunk->mdat_len && chunk->next )
1013 chunk_t* next_chunk = chunk->next;
1014 chunk_free( chunk );
1018 if( ! hds_stream->chunks_livereadpos )
1019 hds_stream->chunks_livereadpos = hds_stream->chunks_head;
1021 hds_stream->chunks_head = chunk;
1025 static void* live_thread( void* p )
1027 vlc_object_t* p_this = (vlc_object_t*)p;
1028 stream_t* s = (stream_t*) p_this;
1029 stream_sys_t* sys = s->p_sys;
1031 if ( vlc_array_count( sys->hds_streams ) == 0 )
1034 // TODO: Change here for selectable stream
1035 hds_stream_t* hds_stream = sys->hds_streams->pp_elems[0];
1037 int canc = vlc_savecancel();
1041 if( hds_stream->abst_url &&
1042 ( isFQUrl( hds_stream->abst_url ) ) )
1044 if( !( abst_url = strdup( hds_stream->abst_url ) ) )
1049 char* server_base = sys->base_url;
1052 if( 0 > asprintf( &abst_url, "%s/%s",
1054 hds_stream->abst_url ) )
1060 mtime_t last_dl_start_time;
1062 while( ! sys->closed )
1064 last_dl_start_time = mdate();
1065 stream_t* download_stream = stream_UrlNew( p_this, abst_url );
1066 if( ! download_stream )
1068 msg_Err( p_this, "Failed to download abst %s", abst_url );
1072 int64_t size = stream_Size( download_stream );
1073 uint8_t* data = malloc( size );
1074 int read = stream_Read( download_stream, data,
1078 msg_Err( p_this, "Requested %"PRIi64" bytes, " \
1079 "but only got %d", size, read );
1084 vlc_mutex_lock( & hds_stream->abst_lock );
1085 parse_BootstrapData( p_this, hds_stream,
1086 data, data + read );
1087 vlc_mutex_unlock( & hds_stream->abst_lock );
1088 maintain_live_chunks( p_this, hds_stream );
1093 stream_Delete( download_stream );
1096 mwait( last_dl_start_time + ( ((int64_t)hds_stream->fragment_runs[hds_stream->fragment_run_count-1].fragment_duration) * 1000000LL) / ((int64_t)hds_stream->afrt_timescale) );
1103 vlc_restorecancel( canc );
1107 static int parse_Manifest( stream_t *s )
1109 xml_t *vlc_xml = NULL;
1110 xml_reader_t *vlc_reader = NULL;
1111 int type = UNKNOWN_ES;
1112 stream_t *st = s->p_source;
1114 msg_Dbg( s, "Manifest parsing\n" );
1116 vlc_xml = xml_Create( st );
1119 msg_Err( s, "Failed to open XML parser" );
1120 return VLC_EGENERIC;
1123 vlc_reader = xml_ReaderCreate( vlc_xml, st );
1126 msg_Err( s, "Failed to open source for parsing" );
1127 xml_Delete( vlc_xml );
1128 return VLC_EGENERIC;
1133 stream_sys_t *sys = s->p_sys;
1135 sys->duration_seconds = 0;
1137 #define MAX_BOOTSTRAP_INFO 10
1138 bootstrap_info bootstraps[MAX_BOOTSTRAP_INFO];
1139 uint8_t bootstrap_idx = 0;
1140 memset( bootstraps, 0, sizeof(bootstrap_info) * MAX_BOOTSTRAP_INFO );
1142 #define MAX_MEDIA_ELEMENTS 10
1143 media_info medias[MAX_MEDIA_ELEMENTS];
1144 uint8_t media_idx = 0;
1145 memset( medias, 0, sizeof(media_info) * MAX_MEDIA_ELEMENTS );
1147 #define MAX_XML_DEPTH 256
1148 char* element_stack[256];
1149 uint8_t current_element_idx = 0;
1150 char* current_element = NULL;
1151 memset( element_stack, 0, sizeof(char*) * MAX_XML_DEPTH );
1153 const char* attr_name;
1154 const char* attr_value;
1156 char* media_id = NULL;
1158 #define TIMESCALE 10000000
1159 while( (type = xml_ReaderNextNode( vlc_reader, (const char**) &node )) > 0 )
1163 case XML_READER_STARTELEM:
1164 if( current_element_idx == 0 && element_stack[current_element_idx] == 0 ) {
1165 if( !( element_stack[current_element_idx] = strdup( node ) ) )
1168 if ( !( element_stack[++current_element_idx] = strdup( node ) ) )
1173 case XML_READER_ENDELEM:
1174 if( ! strcmp( current_element, "bootstrapInfo") ) {
1175 if( bootstrap_idx + 1 == MAX_BOOTSTRAP_INFO ) {
1176 msg_Warn( (vlc_object_t*) s, "Too many bootstraps, ignoring" );
1182 free( current_element );
1183 element_stack[current_element_idx--] = 0;
1187 if( ! element_stack[current_element_idx] ) {
1191 current_element = element_stack[current_element_idx];
1193 if( type == XML_READER_STARTELEM && ! strcmp( current_element, "media") )
1195 if( media_idx == MAX_MEDIA_ELEMENTS )
1197 msg_Err( (vlc_object_t*) s, "Too many media elements, quitting" );
1198 return VLC_EGENERIC;
1201 while( ( attr_name = xml_ReaderNextAttr( vlc_reader, &attr_value )) )
1203 if( !strcmp(attr_name, "streamId" ) )
1205 if( !( medias[media_idx].stream_id = strdup( attr_value ) ) )
1208 else if( !strcmp(attr_name, "url" ) )
1210 if( !( medias[media_idx].media_url = strdup( attr_value ) ) )
1213 else if( !strcmp(attr_name, "bootstrapInfoId" ) )
1215 if( !( medias[media_idx].bootstrap_id = strdup( attr_value ) ) )
1223 else if( type == XML_READER_STARTELEM && ! strcmp( current_element, "bootstrapInfo") )
1225 while( ( attr_name = xml_ReaderNextAttr( vlc_reader, &attr_value )) )
1227 if( !strcmp(attr_name, "url" ) )
1229 if( !( bootstraps[bootstrap_idx].url = strdup( attr_value ) ) )
1232 else if( !strcmp(attr_name, "id" ) )
1234 if( !( bootstraps[bootstrap_idx].id = strdup( attr_value ) ) )
1237 else if( !strcmp(attr_name, "profile" ) )
1239 if( !( bootstraps[bootstrap_idx].profile = strdup( attr_value ) ) )
1245 else if( type == XML_READER_TEXT )
1247 if( ! strcmp( current_element, "bootstrapInfo" ) )
1250 char* end = start + strlen(start);
1251 whitespace_substr( &start, &end );
1254 bootstraps[bootstrap_idx].data_len =
1255 vlc_b64_decode_binary( (uint8_t**)&bootstraps[bootstrap_idx].data, start );
1256 if( ! bootstraps[bootstrap_idx].data )
1258 msg_Err( (vlc_object_t*) s, "Couldn't decode bootstrap info" );
1261 else if( ! strcmp( current_element, "duration" ) )
1263 double shutup_gcc = atof( node );
1264 sys->duration_seconds = (uint64_t) shutup_gcc;
1266 else if( ! strcmp( current_element, "id" ) )
1268 if( ! strcmp( element_stack[current_element_idx-1], "manifest" ) )
1270 if( !( media_id = strdup( node ) ) )
1277 xml_ReaderDelete( vlc_reader );
1278 xml_Delete( vlc_xml );
1280 for( int i = 0; i <= media_idx; i++ )
1282 for( int j = 0; j < bootstrap_idx; j++ )
1284 if( ( ! medias[i].bootstrap_id && ! bootstraps[j].id ) ||
1285 (medias[i].bootstrap_id && bootstraps[j].id &&
1286 ! strcmp( medias[i].bootstrap_id, bootstraps[j].id ) ) )
1288 hds_stream_t* new_stream = malloc(sizeof(hds_stream_t));
1289 memset( new_stream, 0, sizeof(hds_stream_t));
1291 vlc_mutex_init( & new_stream->abst_lock );
1292 vlc_mutex_init( & new_stream->dl_lock );
1293 vlc_cond_init( & new_stream->dl_cond );
1295 if( sys->duration_seconds )
1304 if( medias[i].media_url )
1306 if( !(new_stream->url = strdup( medias[i].media_url ) ) )
1315 parse_BootstrapData( (vlc_object_t*)s,
1318 bootstraps[j].data + bootstraps[j].data_len );
1320 new_stream->download_leadtime = 15;
1322 new_stream->chunks_head = generate_new_chunk(
1323 (vlc_object_t*) s, 0, new_stream );
1324 chunk_t* chunk = new_stream->chunks_head;
1325 uint64_t total_duration = chunk->duration;
1326 while( chunk && total_duration/new_stream->afrt_timescale < new_stream->download_leadtime )
1328 chunk->next = generate_new_chunk(
1329 (vlc_object_t*) s, chunk, new_stream );
1330 chunk = chunk->next;
1332 total_duration += chunk->duration;
1337 if( !(new_stream->abst_url = strdup( bootstraps[j].url ) ) )
1344 vlc_array_append( sys->hds_streams, new_stream );
1346 msg_Info( (vlc_object_t*)s, "New track with quality_segment(%s), timescale(%u), movie_id(%s), segment_run_count(%d), fragment_run_count(%u)",
1347 new_stream->quality_segment_modifier?"":new_stream->quality_segment_modifier, new_stream->timescale,
1348 new_stream->movie_id, new_stream->segment_run_count, new_stream->fragment_run_count );
1354 for( int i = 0; i < MAX_MEDIA_ELEMENTS; i++ )
1356 FREENULL( medias[media_idx].stream_id );
1357 FREENULL( medias[media_idx].media_url );
1358 FREENULL( medias[media_idx].bootstrap_id );
1361 for( int i = 0; i < MAX_BOOTSTRAP_INFO; i++ )
1363 FREENULL( bootstraps[i].data );
1364 FREENULL( bootstraps[i].id );
1365 FREENULL( bootstraps[i].url );
1366 FREENULL( bootstraps[i].profile );
1369 FREENULL( media_id );
1375 static void hds_free( hds_stream_t *p_stream )
1377 FREENULL( p_stream->quality_segment_modifier );
1379 FREENULL( p_stream->abst_url );
1381 FREENULL( p_stream->url );
1382 FREENULL( p_stream->movie_id );
1383 for( int i = 0; i < p_stream->server_entry_count; i++ )
1385 FREENULL( p_stream->server_entries[i] );
1388 chunk_t* chunk = p_stream->chunks_head;
1391 chunk_t* next = chunk->next;
1392 chunk_free( chunk );
1399 static void SysCleanup( stream_sys_t *p_sys )
1401 if ( p_sys->hds_streams )
1403 for ( int i=0; i< p_sys->hds_streams->i_count ; i++ )
1404 hds_free( p_sys->hds_streams->pp_elems[i] );
1405 vlc_array_destroy( p_sys->hds_streams );
1407 free( p_sys->base_url );
1410 static int Open( vlc_object_t *p_this )
1412 stream_t *s = (stream_t*)p_this;
1413 stream_sys_t *p_sys;
1416 return VLC_EGENERIC;
1418 msg_Info( p_this, "HTTP Dynamic Streaming (%s)", s->psz_path );
1420 s->p_sys = p_sys = calloc( 1, sizeof(*p_sys ) );
1421 if( unlikely( p_sys == NULL ) )
1424 char *uri_without_query = NULL;
1425 size_t pathlen = strcspn( s->psz_path, "?" );
1426 if( unlikely( ( pathlen > INT_MAX ) ||
1427 ( asprintf( &uri_without_query, "%s://%.*s", s->psz_access,
1428 (int)pathlen, s->psz_path ) < 0 ) ) )
1434 /* remove the last part of the url */
1435 char *pos = strrchr( uri_without_query, '/');
1437 p_sys->base_url = uri_without_query;
1439 p_sys->flv_header_bytes_sent = 0;
1441 p_sys->hds_streams = vlc_array_new();
1443 if( parse_Manifest( s ) != VLC_SUCCESS )
1450 s->pf_control = Control;
1452 if( vlc_clone( &p_sys->dl_thread, download_thread, s, VLC_THREAD_PRIORITY_INPUT ) )
1458 msg_Info( p_this, "Live stream detected" );
1460 if( vlc_clone( &p_sys->live_thread, live_thread, s, VLC_THREAD_PRIORITY_INPUT ) )
1469 SysCleanup( p_sys );
1471 return VLC_EGENERIC;
1474 static void Close( vlc_object_t *p_this )
1476 stream_t *s = (stream_t*)p_this;
1477 stream_sys_t *p_sys = s->p_sys;
1479 // TODO: Change here for selectable stream
1480 hds_stream_t *stream = vlc_array_count(p_sys->hds_streams) ?
1481 s->p_sys->hds_streams->pp_elems[0] : NULL;
1483 p_sys->closed = true;
1485 vlc_cond_signal( & stream->dl_cond );
1487 vlc_join( p_sys->dl_thread, NULL );
1491 vlc_mutex_destroy( &stream->dl_lock );
1492 vlc_cond_destroy( &stream->dl_cond );
1493 vlc_mutex_destroy( &stream->abst_lock );
1498 vlc_join( p_sys->live_thread, NULL );
1501 SysCleanup( p_sys );
1505 static unsigned char flv_header[] = {
1510 0x5, //indicates audio and video
1514 0x9, // length of header
1518 0x0, // initial "trailer"
1521 static int send_flv_header( stream_sys_t* p_sys, void* buffer, unsigned i_read,
1524 uint32_t to_be_read = i_read;
1525 if( to_be_read > 13 - p_sys->flv_header_bytes_sent ) {
1526 to_be_read = 13 - p_sys->flv_header_bytes_sent;
1529 memcpy( buffer, flv_header + p_sys->flv_header_bytes_sent, to_be_read );
1533 p_sys->flv_header_bytes_sent += to_be_read;
1538 static unsigned read_chunk_data(
1539 vlc_object_t* p_this,
1540 uint8_t* buffer, unsigned read_len,
1541 hds_stream_t* stream,
1545 stream_t* s = (stream_t*) p_this;
1546 stream_sys_t* sys = s->p_sys;
1547 chunk_t* chunk = stream->chunks_head;
1548 uint8_t* buffer_start = buffer;
1551 if( chunk && chunk->eof && chunk->mdat_pos >= chunk->mdat_len ) {
1556 while( chunk && chunk->data && read_len > 0 && ! (chunk->eof && chunk->mdat_pos >= chunk->mdat_len ) )
1558 /* in the live case, it is necessary to store the next
1559 * pointer here, since as soon as we increment the mdat_pos, that
1560 * chunk may be deleted */
1561 chunk_t* next = chunk->next;
1563 if( chunk->mdat_pos < chunk->mdat_len )
1565 unsigned cp_len = chunk->mdat_len - chunk->mdat_pos;
1566 if( cp_len > read_len )
1568 memcpy( buffer, chunk->mdat_data + chunk->mdat_pos,
1573 chunk->mdat_pos += cp_len;
1576 if( ! sys->live && (chunk->mdat_pos >= chunk->mdat_len || chunk->failed) )
1583 /* make sure there is at least one chunk in the queue */
1584 if( ! chunk->next && ! chunk->eof )
1586 chunk->next = generate_new_chunk( p_this, chunk, stream );
1592 chunk_free( chunk );
1594 stream->chunks_head = chunk;
1597 else if( sys->live && (chunk->mdat_pos >= chunk->mdat_len || chunk->failed) )
1605 stream->chunks_livereadpos = chunk;
1608 /* new chunk generation is handled by a different thread in live case */
1611 chunk = stream->chunks_head;
1614 uint64_t total_duration = chunk->duration;
1615 while( chunk && total_duration/stream->afrt_timescale < stream->download_leadtime && ! chunk->eof )
1617 if( ! chunk->next && ! chunk->eof )
1619 chunk->next = generate_new_chunk( p_this, chunk, stream );
1625 chunk = chunk->next;
1628 total_duration += chunk->duration;
1635 vlc_cond_signal( & stream->dl_cond );
1638 return ( ((uint8_t*)buffer) - ((uint8_t*)buffer_start));
1641 static int Read( stream_t *s, void *buffer, unsigned i_read )
1643 stream_sys_t *p_sys = s->p_sys;
1645 if ( vlc_array_count( p_sys->hds_streams ) == 0 )
1648 // TODO: change here for selectable stream
1649 hds_stream_t *stream = s->p_sys->hds_streams->pp_elems[0];
1652 uint8_t *buffer_uint8 = (uint8_t*) buffer;
1654 unsigned hdr_bytes = send_flv_header( p_sys, buffer, i_read, false );
1655 length += hdr_bytes;
1656 i_read -= hdr_bytes;
1657 buffer_uint8 += hdr_bytes;
1660 while( i_read > 0 && ! eof )
1662 int tmp_length = read_chunk_data( (vlc_object_t*)s, buffer_uint8, i_read, stream, &eof );
1663 buffer_uint8 += tmp_length;
1664 i_read -= tmp_length;
1665 length += tmp_length;
1671 static int Peek( stream_t *s, const uint8_t **pp_peek, unsigned i_peek )
1673 stream_sys_t *p_sys = s->p_sys;
1675 if ( vlc_array_count( p_sys->hds_streams ) == 0 )
1678 // TODO: change here for selectable stream
1679 hds_stream_t *stream = p_sys->hds_streams->pp_elems[0];
1681 if( p_sys->flv_header_bytes_sent < 13 )
1683 *pp_peek = flv_header + p_sys->flv_header_bytes_sent;
1684 return 13 - p_sys->flv_header_bytes_sent;
1687 if( stream->chunks_head && ! stream->chunks_head->failed && stream->chunks_head->data )
1689 // TODO: change here for selectable stream
1690 chunk_t* chunk = stream->chunks_head;
1691 *pp_peek = chunk->mdat_data + chunk->mdat_pos;
1692 if( chunk->mdat_len - chunk->mdat_pos < i_peek )
1694 return chunk->mdat_len - chunk->mdat_pos;
1706 static int Control( stream_t *s, int i_query, va_list args )
1710 case STREAM_CAN_SEEK:
1711 *(va_arg( args, bool * )) = false;
1713 case STREAM_CAN_FASTSEEK:
1714 case STREAM_CAN_PAUSE: /* TODO */
1715 *(va_arg( args, bool * )) = false;
1717 case STREAM_CAN_CONTROL_PACE:
1718 *(va_arg( args, bool * )) = true;
1720 case STREAM_GET_PTS_DELAY:
1721 *va_arg (args, int64_t *) = INT64_C(1000) *
1722 var_InheritInteger(s, "network-caching");
1725 return VLC_EGENERIC;