1 /*****************************************************************************
2 * downloader.c: download thread
3 *****************************************************************************
4 * Copyright (C) 1996-2012 VLC authors and VideoLAN
7 * Author: Frédéric Yhuel <fyhuel _AT_ viotech _DOT_ net>
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 *****************************************************************************/
27 #include <vlc_common.h>
29 #include <vlc_stream.h>
31 #include <vlc_charset.h>
34 #include "../../demux/mp4/libmp4.h"
36 static bool Replace( char **ppsz_string, off_t off, const char *psz_old,
39 const size_t i_oldlen = strlen( psz_old );
40 const size_t i_newlen = strlen( psz_new );
41 size_t i_stringlen = strlen( *ppsz_string );
43 if ( i_newlen > i_oldlen )
45 i_stringlen += i_newlen - i_oldlen;
46 char *psz_realloc = realloc( *ppsz_string, i_stringlen + 1 );
49 *ppsz_string = psz_realloc;
51 memmove( *ppsz_string + off + i_newlen,
52 *ppsz_string + off + i_oldlen,
53 i_stringlen - off - i_newlen );
54 strncpy( *ppsz_string + off, psz_new, i_newlen );
55 (*ppsz_string)[i_stringlen] = 0;
60 static char *ConstructUrl( const char *psz_template, const char *psz_base_url,
61 const quality_level_t *p_qlevel, const uint64_t i_start_time )
63 char *psz_path = strdup( psz_template );
70 if ( (psz_start = strstr( psz_path, "{bitrate}" )) )
72 char *psz_bitrate = NULL;
73 if ( us_asprintf( &psz_bitrate, "%u", p_qlevel->Bitrate ) < 0 ||
74 ! Replace( &psz_path, psz_start - psz_path, "{bitrate}", psz_bitrate ) )
82 else if ( (psz_start = strstr( psz_path, "{start time}" )) ||
83 (psz_start = strstr( psz_path, "{start_time}" )) )
86 char *psz_starttime = NULL;
87 if ( us_asprintf( &psz_starttime, "%"PRIu64, i_start_time ) < 0 ||
88 ! Replace( &psz_path, psz_start - psz_path, "{start time}", psz_starttime ) )
90 free( psz_starttime );
94 free( psz_starttime );
96 else if ( (psz_start = strstr( psz_path, "{CustomAttributes}" )) )
98 char *psz_attributes = NULL;
99 FOREACH_ARRAY( const custom_attrs_t *p_attrs, p_qlevel->custom_attrs )
100 if ( asprintf( &psz_attributes,
101 psz_attributes ? "%s,%s=%s" : "%s%s=%s",
102 psz_attributes ? psz_attributes : "",
103 p_attrs->psz_key, p_attrs->psz_value ) < 0 )
106 if ( !psz_attributes ||
107 ! Replace( &psz_path, psz_start - psz_path, "{CustomAttributes}", psz_attributes ) )
109 free( psz_attributes );
113 free( psz_attributes );
120 if( asprintf( &psz_url, "%s/%s", psz_base_url, psz_path ) < 0 )
129 static chunk_t * chunk_Get( sms_stream_t *sms, const uint64_t start_time )
131 int len = vlc_array_count( sms->chunks );
132 for( int i = 0; i < len; i++ )
134 chunk_t * chunk = vlc_array_item_at_index( sms->chunks, i );
135 if( !chunk ) return NULL;
137 if( chunk->start_time <= start_time &&
138 chunk->start_time + chunk->duration > start_time )
146 static unsigned set_track_id( chunk_t *chunk, const unsigned tid )
151 uint8_t *slice = chunk->data;
155 SMS_GET4BYTES( size );
156 SMS_GETFOURCC( type );
157 assert( type == ATOM_moof );
159 SMS_GET4BYTES( size );
160 SMS_GETFOURCC( type );
161 assert( type == ATOM_mfhd );
164 SMS_GET4BYTES( size );
165 SMS_GETFOURCC( type );
166 assert( type == ATOM_traf );
168 SMS_GET4BYTES( size );
169 SMS_GETFOURCC( type );
170 if( type != ATOM_tfhd )
173 unsigned ret = bswap32( ((uint32_t *)slice)[1] );
174 ((uint32_t *)slice)[1] = bswap32( tid );
179 static int sms_Download( stream_t *s, chunk_t *chunk, char *url )
181 stream_sys_t *p_sys = s->p_sys;
183 stream_t *p_ts = stream_UrlNew( s, url );
188 int64_t size = stream_Size( p_ts );
192 chunk->size = (uint64_t) size;
193 chunk->offset = p_sys->download.next_chunk_offset;
194 p_sys->download.next_chunk_offset += chunk->size;
196 chunk->data = malloc( size );
198 if( chunk->data == NULL )
200 stream_Delete( p_ts );
204 int read = stream_Read( p_ts, chunk->data, size );
207 msg_Warn( s, "sms_Download: I requested %"PRIi64" bytes, "\
208 "but I got only %i", size, read );
209 chunk->data = realloc( chunk->data, read );
212 stream_Delete( p_ts );
214 vlc_mutex_lock( &p_sys->download.lock_wait );
215 int index = es_cat_to_index( chunk->type );
216 p_sys->download.lead[index] += chunk->duration;
217 vlc_mutex_unlock( &p_sys->download.lock_wait );
222 #ifdef DISABLE_BANDWIDTH_ADAPTATION
223 static unsigned BandwidthAdaptation( stream_t *s,
224 sms_stream_t *sms, uint64_t bandwidth )
226 VLC_UNUSED(bandwidth);
228 return sms->download_qlvl;
232 static unsigned BandwidthAdaptation( stream_t *s,
233 sms_stream_t *sms, uint64_t bandwidth )
235 if( sms->type != VIDEO_ES )
236 return sms->download_qlvl;
238 uint64_t bw_candidate = 0;
239 quality_level_t *qlevel;
240 unsigned ret = sms->download_qlvl;
242 for( unsigned i = 0; i < sms->qlevel_nb; i++ )
244 qlevel = vlc_array_item_at_index( sms->qlevels, i );
245 if( unlikely( !qlevel ) )
247 msg_Err( s, "Could no get %uth quality level", i );
251 if( qlevel->Bitrate < (bandwidth - bandwidth / 3) &&
252 qlevel->Bitrate > bw_candidate )
254 bw_candidate = qlevel->Bitrate;
263 static int get_new_chunks( stream_t *s, chunk_t *ck )
265 stream_sys_t *p_sys = s->p_sys;
267 uint8_t *slice = ck->data;
270 uint8_t version, fragment_count;
271 uint32_t size, type, flags;
274 TfrfBoxDataFields_t *tfrf_df;
276 sms = SMS_GET_SELECTED_ST( ck->type );
278 SMS_GET4BYTES( size );
279 SMS_GETFOURCC( type );
280 assert( type == ATOM_moof );
282 SMS_GET4BYTES( size );
283 SMS_GETFOURCC( type );
284 assert( type == ATOM_mfhd );
287 SMS_GET4BYTES( size );
288 SMS_GETFOURCC( type );
289 assert( type == ATOM_traf );
293 SMS_GET4BYTES( size );
295 SMS_GETFOURCC( type );
296 if( type == ATOM_mdat )
298 msg_Err( s, "No uuid box found :-(" );
301 else if( type == ATOM_uuid )
303 GetUUID( &uuid, slice);
304 if( !CmpUUID( &uuid, &TfrfBoxUUID ) )
311 SMS_GET1BYTE( version );
312 SMS_GET3BYTES( flags );
313 SMS_GET1BYTE( fragment_count );
315 tfrf_df = calloc( fragment_count, sizeof( TfrfBoxDataFields_t ) );
316 if( unlikely( tfrf_df == NULL ) )
319 for( uint8_t i = 0; i < fragment_count; i++ )
321 SMS_GET4or8BYTES( tfrf_df[i].i_fragment_abs_time );
322 SMS_GET4or8BYTES( tfrf_df[i].i_fragment_duration );
325 msg_Dbg( s, "read box: \"tfrf\" version %d, flags 0x%x, "\
326 "fragment count %"PRIu8, version, flags, fragment_count );
328 for( uint8_t i = 0; i < fragment_count; i++ )
330 uint64_t dur = tfrf_df[i].i_fragment_duration;
331 uint64_t stime = tfrf_df[i].i_fragment_abs_time;
332 msg_Dbg( s, "\"tfrf\" fragment duration %"PRIu64", "\
333 "fragment abs time %"PRIu64, dur, stime);
335 if( !chunk_Get( sms, stime + dur ) )
336 chunk_New( sms, dur, stime );
343 #define STRA_SIZE 334
344 #define SMOO_SIZE (STRA_SIZE * 3 + 24) /* 1026 */
346 /* SmooBox is a very simple MP4 box, used only to pass information
347 * to the demux layer. As this box is not aimed to travel accross networks,
348 * simplicity of the design is better than compactness */
349 static int build_smoo_box( stream_t *s, uint8_t *smoo_box )
351 stream_sys_t *p_sys = s->p_sys;
352 sms_stream_t *sms = NULL;
356 memset( smoo_box, 0, SMOO_SIZE );
357 smoo_box[2] = (SMOO_SIZE & 0xff00)>>8;
358 smoo_box[3] = SMOO_SIZE & 0xff;
364 /* UUID is e1da72ba-24d7-43c3-a6a5-1b5759a1a92c */
365 ((uint32_t *)smoo_box)[2] = bswap32( 0xe1da72ba );
366 ((uint32_t *)smoo_box)[3] = bswap32( 0x24d743c3 );
367 ((uint32_t *)smoo_box)[4] = bswap32( 0xa6a51b57 );
368 ((uint32_t *)smoo_box)[5] = bswap32( 0x59a1a92c );
371 for( int i = 0; i < 3; i++ )
374 int cat = UNKNOWN_ES;
375 stra_box = smoo_box + i * STRA_SIZE;
377 stra_box[26] = (STRA_SIZE & 0xff00)>>8;
378 stra_box[27] = STRA_SIZE & 0xff;
384 /* UUID is b03ef770-33bd-4bac-96c7-bf25f97e2447 */
385 ((uint32_t *)stra_box)[8] = bswap32( 0xb03ef770 );
386 ((uint32_t *)stra_box)[9] = bswap32( 0x33bd4bac );
387 ((uint32_t *)stra_box)[10] = bswap32( 0x96c7bf25 );
388 ((uint32_t *)stra_box)[11] = bswap32( 0xf97e2447 );
390 cat = index_to_es_cat( i );
392 sms = SMS_GET_SELECTED_ST( cat );
394 stra_box[49] = 0; /* reserved */
397 stra_box[50] = (sms->id & 0xff00)>>8;
398 stra_box[51] = sms->id & 0xff;
400 ((uint32_t *)stra_box)[13] = bswap32( sms->timescale );
401 ((uint64_t *)stra_box)[7] = bswap64( p_sys->vod_duration );
403 quality_level_t * qlvl = get_qlevel( sms, sms->download_qlvl );
407 FourCC = qlvl->FourCC ? qlvl->FourCC : sms->default_FourCC;
408 ((uint32_t *)stra_box)[16] = bswap32( FourCC );
409 ((uint32_t *)stra_box)[17] = bswap32( qlvl->Bitrate );
410 ((uint32_t *)stra_box)[18] = bswap32( qlvl->MaxWidth );
411 ((uint32_t *)stra_box)[19] = bswap32( qlvl->MaxHeight );
412 ((uint32_t *)stra_box)[20] = bswap32( qlvl->SamplingRate );
413 ((uint32_t *)stra_box)[21] = bswap32( qlvl->Channels );
414 ((uint32_t *)stra_box)[22] = bswap32( qlvl->BitsPerSample );
415 ((uint32_t *)stra_box)[23] = bswap32( qlvl->AudioTag );
416 ((uint16_t *)stra_box)[48] = bswap16( qlvl->nBlockAlign );
418 if( !qlvl->CodecPrivateData )
420 stra_box[98] = stra_box[99] = stra_box[100] = 0; /* reserved */
421 stra_box[101] = strlen( qlvl->CodecPrivateData ) / 2;
422 if ( stra_box[101] > STRA_SIZE - 102 )
423 stra_box[101] = STRA_SIZE - 102;
424 uint8_t *binary_cpd = decode_string_hex_to_binary( qlvl->CodecPrivateData );
425 memcpy( stra_box + 102, binary_cpd, stra_box[101] );
433 static chunk_t *build_init_chunk( stream_t *s )
435 chunk_t *ret = calloc( 1, sizeof( chunk_t ) );
436 if( unlikely( ret == NULL ) )
437 goto build_init_chunk_error;
439 ret->size = SMOO_SIZE;
440 ret->data = malloc( SMOO_SIZE );
442 goto build_init_chunk_error;
444 if( build_smoo_box( s, ret->data ) == VLC_SUCCESS)
448 build_init_chunk_error:
450 msg_Err( s, "build_init_chunk failed" );
454 static int Download( stream_t *s, sms_stream_t *sms )
456 stream_sys_t *p_sys = s->p_sys;
458 int index = es_cat_to_index( sms->type );
459 if ( unlikely( index == -1 ) )
461 msg_Err( s, "invalid stream type" );
464 uint64_t start_time = p_sys->download.lead[index];
466 quality_level_t *qlevel = get_qlevel( sms, sms->download_qlvl );
467 if( unlikely( !qlevel ) )
469 msg_Err( s, "Could not get quality level id %u", sms->download_qlvl );
474 chunk_t *chunk = chunk_Get( sms, start_time );
477 msg_Warn( s, "Could not find a chunk for stream %s, "\
478 "start time = %"PRIu64"", sms->name, start_time );
481 if( chunk->data != NULL )
483 /* Segment already downloaded */
484 msg_Warn( s, "Segment already downloaded" );
488 chunk->type = sms->type;
490 char *url = ConstructUrl( sms->url_template, p_sys->base_url,
491 qlevel, chunk->start_time );
494 msg_Err( s, "ConstructUrl returned NULL" );
498 /* sanity check - can we download this chunk on time? */
499 uint64_t avg_bw = sms_queue_avg( p_sys->bws );
500 if( (avg_bw > 0) && (qlevel->Bitrate > 0) )
503 unsigned chunk_duration = chunk->duration * 1000 / sms->timescale;
504 uint64_t size = chunk_duration * qlevel->Bitrate / 1000; /* bits */
505 unsigned estimated = size * 1000 / avg_bw;
506 if( estimated > chunk_duration )
508 msg_Warn( s,"downloading of chunk %d would take %d ms, "\
509 "which is longer than its playback (%d ms)",
510 chunk->sequence, estimated, chunk_duration );
514 mtime_t start = mdate();
515 if( sms_Download( s, chunk, url ) != VLC_SUCCESS )
517 msg_Err( s, "downloaded chunk %u from stream %s at quality"
518 " %u *failed*", chunk->sequence, sms->name, qlevel->Bitrate );
521 mtime_t duration = mdate() - start;
523 unsigned real_id = set_track_id( chunk, sms->id );
526 msg_Err( s, "tfhd box not found or invalid chunk" );
530 //msg_Dbg( s, "chunk ID was %i and is now %i", real_id, sms->id );
533 get_new_chunks( s, chunk );
535 vlc_mutex_lock( &p_sys->download.lock_wait );
536 vlc_array_append( p_sys->download.chunks, chunk );
537 vlc_cond_signal( &p_sys->download.wait );
538 vlc_mutex_unlock( &p_sys->download.lock_wait );
540 msg_Info( s, "downloaded chunk %d from stream %s at quality %u",
541 chunk->sequence, sms->name, qlevel->Bitrate );
543 uint64_t actual_lead = chunk->start_time + chunk->duration;
544 int ind = es_cat_to_index( sms->type );
545 p_sys->download.ck_index[ind] = chunk->sequence;
546 p_sys->download.lead[ind] = __MIN( p_sys->download.lead[ind], actual_lead );
548 if( sms->type == VIDEO_ES ||
549 ( !SMS_GET_SELECTED_ST( VIDEO_ES ) && sms->type == AUDIO_ES ) )
551 p_sys->playback.toffset = __MIN( p_sys->playback.toffset,
552 (uint64_t)chunk->start_time );
555 unsigned dur_ms = __MAX( 1, duration / 1000 );
556 uint64_t bw = chunk->size * 8 * 1000 / dur_ms; /* bits / s */
557 if( sms_queue_put( p_sys->bws, bw ) != VLC_SUCCESS )
559 avg_bw = sms_queue_avg( p_sys->bws );
561 if( sms->type != VIDEO_ES )
564 /* Track could get disabled in mp4 demux if we trigger adaption too soon. */
565 if( chunk->sequence <= 1 )
568 unsigned new_qlevel_id = BandwidthAdaptation( s, sms, avg_bw );
569 quality_level_t *new_qlevel = get_qlevel( sms, new_qlevel_id );
570 if( unlikely( !new_qlevel ) )
572 msg_Err( s, "Could not get quality level id %u", new_qlevel_id );
576 if( new_qlevel->Bitrate != qlevel->Bitrate )
578 msg_Warn( s, "detected %s bandwidth (%u) stream",
579 (new_qlevel->Bitrate >= qlevel->Bitrate) ? "faster" : "lower",
580 new_qlevel->Bitrate );
582 sms->download_qlvl = new_qlevel_id;
585 if( new_qlevel->MaxWidth != qlevel->MaxWidth ||
586 new_qlevel->MaxHeight != qlevel->MaxHeight )
588 chunk_t *new_init_ck = build_init_chunk( s );
594 new_init_ck->offset = p_sys->download.next_chunk_offset;
595 p_sys->download.next_chunk_offset += new_init_ck->size;
597 vlc_mutex_lock( &p_sys->download.lock_wait );
598 vlc_array_append( p_sys->download.chunks, new_init_ck );
599 vlc_array_append( p_sys->init_chunks, new_init_ck );
600 vlc_mutex_unlock( &p_sys->download.lock_wait );
605 static inline uint64_t get_lead( stream_t *s )
607 stream_sys_t *p_sys = s->p_sys;
609 uint64_t alead = p_sys->download.lead[es_cat_to_index( AUDIO_ES )];
610 uint64_t vlead = p_sys->download.lead[es_cat_to_index( VIDEO_ES )];
611 bool video = SMS_GET_SELECTED_ST( VIDEO_ES ) ? true : false;
612 bool audio = SMS_GET_SELECTED_ST( AUDIO_ES ) ? true : false;
615 lead = __MIN( vlead, alead );
621 if( p_sys->playback.toffset < lead )
622 lead -= p_sys->playback.toffset;
628 static int next_track( stream_t *s )
630 stream_sys_t *p_sys = s->p_sys;
631 uint64_t tmp, min = 0;
632 int cat, ret = UNKNOWN_ES;
633 for( int i = 0; i < 3; i++ )
635 tmp = p_sys->download.lead[i];
636 cat = index_to_es_cat( i );
637 if( (!min || tmp < min) && SMS_GET_SELECTED_ST( cat ) )
646 void* sms_Thread( void *p_this )
648 stream_t *s = (stream_t *)p_this;
649 stream_sys_t *p_sys = s->p_sys;
650 sms_stream_t *sms = NULL;
653 int canc = vlc_savecancel();
655 /* We compute the average bandwidth of the 4 last downloaded
656 * chunks, but feel free to replace '4' by whatever you wish */
657 p_sys->bws = sms_queue_init( 4 );
661 chunk_t *init_ck = build_init_chunk( s );
665 vlc_mutex_lock( &p_sys->download.lock_wait );
666 vlc_array_append( p_sys->download.chunks, init_ck );
667 vlc_array_append( p_sys->init_chunks, init_ck );
668 vlc_mutex_unlock( &p_sys->download.lock_wait );
670 p_sys->download.next_chunk_offset = init_ck->size;
672 /* XXX Sometimes, the video stream is cut into pieces of one exact length,
673 * while the audio stream fragments can't be made to match exactly,
674 * and for some reason the n^th advertised video fragment is related to
675 * the n+1^th advertised audio chunk or vice versa */
677 uint64_t start_time = 0, lead = 0;
680 for( int i = 0; i < 3; i++ )
682 sms = SMS_GET_SELECTED_ST( index_to_es_cat( i ) );
683 if( sms && vlc_array_count( sms->chunks ) )
685 chunk = vlc_array_item_at_index( sms->chunks, 0 );
686 p_sys->download.lead[i] = chunk->start_time + p_sys->timescale / 1000;
688 start_time = chunk->start_time;
690 if( Download( s, sms ) != VLC_SUCCESS )
697 /* XXX replace magic number 10 by a value depending on
698 * LookAheadFragmentCount and DVRWindowLength */
699 vlc_mutex_lock( &p_sys->download.lock_wait );
703 vlc_mutex_unlock( &p_sys->download.lock_wait );
707 lead = get_lead( s );
709 if ( stream_Control( s, STREAM_GET_PTS_DELAY, &i_pts_delay ) != VLC_SUCCESS ||
712 i_pts_delay = 10 * p_sys->timescale + start_time;
715 while( !p_sys->b_live && ( lead > (uint64_t) i_pts_delay || NO_MORE_CHUNKS ) )
717 vlc_cond_wait( &p_sys->download.wait, &p_sys->download.lock_wait );
718 lead = get_lead( s );
726 int count = vlc_array_count( p_sys->download.chunks );
728 for( int i = 0; i < count; i++ )
730 ck = vlc_array_item_at_index( p_sys->download.chunks, i );
731 if( unlikely( !ck ) )
734 if( ck->data == NULL )
736 FREENULL( ck->data );
739 vlc_array_destroy( p_sys->download.chunks );
740 p_sys->download.chunks = vlc_array_new();
742 p_sys->playback.toffset = p_sys->time_pos;
743 for( int i = 0; i < 3; i++ )
745 p_sys->download.lead[i] = p_sys->time_pos;
746 p_sys->download.ck_index[i] = 0;
748 p_sys->download.next_chunk_offset = 0;
750 p_sys->playback.boffset = 0;
751 p_sys->playback.index = 0;
753 chunk_t *new_init_ck = build_init_chunk( s );
757 new_init_ck->offset = p_sys->download.next_chunk_offset;
758 p_sys->download.next_chunk_offset += new_init_ck->size;
760 vlc_array_append( p_sys->download.chunks, new_init_ck );
761 vlc_array_append( p_sys->init_chunks, new_init_ck );
762 p_sys->b_tseek = false;
764 vlc_mutex_unlock( &p_sys->download.lock_wait );
766 sms = SMS_GET_SELECTED_ST( next_track( s ) );
768 vlc_mutex_lock( &p_sys->download.lock_wait );
769 unsigned i_ahead = ahead_chunks_count( p_sys, sms );
770 vlc_mutex_unlock( &p_sys->download.lock_wait );
772 if ( vlc_array_count( sms->chunks ) &&
773 ( !p_sys->b_live || i_ahead < p_sys->lookahead_count ) )
775 if( Download( s, sms ) != VLC_SUCCESS )
781 p_sys->b_error = true;
782 msg_Warn(s, "Canceling download thread!");
783 vlc_restorecancel( canc );