]> git.sesse.net Git - vlc/blob - modules/stream_filter/smooth/downloader.c
stream_filter: smooth: missing terminating zero
[vlc] / modules / stream_filter / smooth / downloader.c
1 /*****************************************************************************
2  * downloader.c: download thread
3  *****************************************************************************
4  * Copyright (C) 1996-2012 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Author: Frédéric Yhuel <fyhuel _AT_ viotech _DOT_ net>
8  *
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.
13  *
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.
18  *
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  *****************************************************************************/
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27 #include <vlc_common.h>
28 #include <assert.h>
29 #include <vlc_stream.h>
30 #include <vlc_es.h>
31 #include <vlc_charset.h>
32
33 #include "smooth.h"
34 #include "../../demux/mp4/libmp4.h"
35
36 static bool Replace( char **ppsz_string, off_t off, const char *psz_old,
37                      const char *psz_new )
38 {
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 );
42
43     if ( i_newlen > i_oldlen )
44     {
45         i_stringlen += i_newlen - i_oldlen;
46         char *psz_realloc = realloc( *ppsz_string, i_stringlen + 1 );
47         if( !psz_realloc )
48             return false;
49         *ppsz_string = psz_realloc;
50     }
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;
56
57     return true;
58 }
59
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 )
62 {
63     char *psz_path = strdup( psz_template );
64     if ( !psz_path )
65         return NULL;
66
67     char *psz_start;
68     while( true )
69     {
70         if ( (psz_start = strstr( psz_path, "{bitrate}" )) )
71         {
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 ) )
75             {
76                 free( psz_bitrate );
77                 free( psz_path );
78                 return false;
79             }
80             free( psz_bitrate );
81         }
82         else if ( (psz_start = strstr( psz_path, "{start time}" )) )
83         {
84             char *psz_starttime = NULL;
85             if ( us_asprintf( &psz_starttime, "%"PRIu64, i_start_time ) < 0 ||
86                  ! Replace( &psz_path, psz_start - psz_path, "{start time}", psz_starttime ) )
87             {
88                 free( psz_starttime );
89                 free( psz_path );
90                 return false;
91             }
92             free( psz_starttime );
93         }
94         else if ( (psz_start = strstr( psz_path, "{CustomAttributes}" )) )
95         {
96             char *psz_attributes = NULL;
97             FOREACH_ARRAY( const custom_attrs_t *p_attrs, p_qlevel->custom_attrs )
98             if ( asprintf( &psz_attributes,
99                            psz_attributes ? "%s,%s=%s" : "%s%s=%s",
100                            psz_attributes ? psz_attributes : "",
101                            p_attrs->psz_key, p_attrs->psz_value ) < 0 )
102                     break;
103             FOREACH_END()
104             if ( !psz_attributes ||
105                  ! Replace( &psz_path, psz_start - psz_path, "{CustomAttributes}", psz_attributes ) )
106             {
107                 free( psz_attributes );
108                 free( psz_path );
109                 return false;
110             }
111             free( psz_attributes );
112         }
113         else
114             break;
115     }
116
117     char *psz_url;
118     if( asprintf( &psz_url, "%s/%s", psz_base_url, psz_path ) < 0 )
119     {
120         free( psz_path );
121         return NULL;
122     }
123     free( psz_path );
124     return psz_url;
125 }
126
127 static chunk_t * chunk_Get( sms_stream_t *sms, const uint64_t start_time )
128 {
129     int len = vlc_array_count( sms->chunks );
130     for( int i = 0; i < len; i++ )
131     {
132         chunk_t * chunk = vlc_array_item_at_index( sms->chunks, i );
133         if( !chunk ) return NULL;
134
135         if( chunk->start_time <= start_time &&
136                 chunk->start_time + chunk->duration > start_time )
137         {
138             return chunk;
139         }
140     }
141     return NULL;
142 }
143
144 static unsigned set_track_id( chunk_t *chunk, const unsigned tid )
145 {
146     uint32_t size, type;
147     if( !chunk->data )
148         return 0;
149     uint8_t *slice = chunk->data;
150     if( !slice )
151         return 0;
152
153     SMS_GET4BYTES( size );
154     SMS_GETFOURCC( type );
155     assert( type == ATOM_moof );
156
157     SMS_GET4BYTES( size );
158     SMS_GETFOURCC( type );
159     assert( type == ATOM_mfhd );
160     slice += size - 8;
161
162     SMS_GET4BYTES( size );
163     SMS_GETFOURCC( type );
164     assert( type == ATOM_traf );
165
166     SMS_GET4BYTES( size );
167     SMS_GETFOURCC( type );
168     if( type != ATOM_tfhd )
169         return 0;
170
171     unsigned ret = bswap32( ((uint32_t *)slice)[1] );
172     ((uint32_t *)slice)[1] = bswap32( tid );
173
174     return ret;
175 }
176
177 static int sms_Download( stream_t *s, chunk_t *chunk, char *url )
178 {
179     stream_sys_t *p_sys = s->p_sys;
180
181     stream_t *p_ts = stream_UrlNew( s, url );
182     free( url );
183     if( p_ts == NULL )
184         return VLC_EGENERIC;
185
186     int64_t size = stream_Size( p_ts );
187     if ( size < 0 )
188         return VLC_EGENERIC;
189
190     chunk->size = (uint64_t) size;
191     chunk->offset = p_sys->download.next_chunk_offset;
192     p_sys->download.next_chunk_offset += chunk->size;
193
194     chunk->data = malloc( size );
195
196     if( chunk->data == NULL )
197     {
198         stream_Delete( p_ts );
199         return VLC_ENOMEM;
200     }
201
202     int read = stream_Read( p_ts, chunk->data, size );
203     if( read < size )
204     {
205         msg_Warn( s, "sms_Download: I requested %"PRIi64" bytes, "\
206                 "but I got only %i", size, read );
207         chunk->data = realloc( chunk->data, read );
208     }
209
210     stream_Delete( p_ts );
211
212     vlc_mutex_lock( &p_sys->download.lock_wait );
213     int index = es_cat_to_index( chunk->type );
214     p_sys->download.lead[index] += chunk->duration;
215     vlc_mutex_unlock( &p_sys->download.lock_wait );
216
217     return VLC_SUCCESS;
218 }
219
220 #ifdef DISABLE_BANDWIDTH_ADAPTATION
221 static unsigned BandwidthAdaptation( stream_t *s,
222         sms_stream_t *sms, uint64_t bandwidth )
223 {
224     VLC_UNUSED(bandwidth);
225     VLC_UNUSED(s);
226     return sms->download_qlvl;
227 }
228 #else
229
230 static unsigned BandwidthAdaptation( stream_t *s,
231         sms_stream_t *sms, uint64_t bandwidth )
232 {
233     if( sms->type != VIDEO_ES )
234         return sms->download_qlvl;
235
236     uint64_t bw_candidate = 0;
237     quality_level_t *qlevel;
238     unsigned ret = sms->download_qlvl;
239
240     for( unsigned i = 0; i < sms->qlevel_nb; i++ )
241     {
242         qlevel = vlc_array_item_at_index( sms->qlevels, i );
243         if( unlikely( !qlevel ) )
244         {
245             msg_Err( s, "Could no get %uth quality level", i );
246             return 0;
247         }
248
249         if( qlevel->Bitrate < (bandwidth - bandwidth / 3) &&
250                 qlevel->Bitrate > bw_candidate )
251         {
252             bw_candidate = qlevel->Bitrate;
253             ret = qlevel->id;
254         }
255     }
256
257     return ret;
258 }
259 #endif
260
261 static int get_new_chunks( stream_t *s, chunk_t *ck )
262 {
263     stream_sys_t *p_sys = s->p_sys;
264
265     uint8_t *slice = ck->data;
266     if( !slice )
267         return VLC_EGENERIC;
268     uint8_t version, fragment_count;
269     uint32_t size, type, flags;
270     sms_stream_t *sms;
271     UUID_t uuid;
272     TfrfBoxDataFields_t *tfrf_df;
273
274     sms = SMS_GET_SELECTED_ST( ck->type );
275
276     SMS_GET4BYTES( size );
277     SMS_GETFOURCC( type );
278     assert( type == ATOM_moof );
279
280     SMS_GET4BYTES( size );
281     SMS_GETFOURCC( type );
282     assert( type == ATOM_mfhd );
283     slice += size - 8;
284
285     SMS_GET4BYTES( size );
286     SMS_GETFOURCC( type );
287     assert( type == ATOM_traf );
288
289     for(;;)
290     {
291         SMS_GET4BYTES( size );
292         assert( size > 1 );
293         SMS_GETFOURCC( type );
294         if( type == ATOM_mdat )
295         {
296             msg_Err( s, "No uuid box found :-(" );
297             return VLC_EGENERIC;
298         }
299         else if( type == ATOM_uuid )
300         {
301             GetUUID( &uuid, slice);
302             if( !CmpUUID( &uuid, &TfrfBoxUUID ) )
303                 break;
304         }
305         slice += size - 8;
306     }
307
308     slice += 16;
309     SMS_GET1BYTE( version );
310     SMS_GET3BYTES( flags );
311     SMS_GET1BYTE( fragment_count );
312
313     tfrf_df = calloc( fragment_count, sizeof( TfrfBoxDataFields_t ) );
314     if( unlikely( tfrf_df == NULL ) )
315         return VLC_EGENERIC;
316
317     for( uint8_t i = 0; i < fragment_count; i++ )
318     {
319         SMS_GET4or8BYTES( tfrf_df[i].i_fragment_abs_time );
320         SMS_GET4or8BYTES( tfrf_df[i].i_fragment_duration );
321     }
322
323     msg_Dbg( s, "read box: \"tfrf\" version %d, flags 0x%x, "\
324             "fragment count %"PRIu8, version, flags, fragment_count );
325
326     for( uint8_t i = 0; i < fragment_count; i++ )
327     {
328         uint64_t dur = tfrf_df[i].i_fragment_duration;
329         uint64_t stime = tfrf_df[i].i_fragment_abs_time;
330         msg_Dbg( s, "\"tfrf\" fragment duration %"PRIu64", "\
331                     "fragment abs time %"PRIu64, dur, stime);
332
333         if( !chunk_Get( sms, stime + dur ) )
334             chunk_New( sms, dur, stime );
335     }
336     free( tfrf_df );
337
338     return VLC_SUCCESS;
339 }
340
341 #define STRA_SIZE 334
342 #define SMOO_SIZE (STRA_SIZE * 3 + 24) /* 1026 */
343
344 /* SmooBox is a very simple MP4 box, used only to pass information
345  * to the demux layer. As this box is not aimed to travel accross networks,
346  * simplicity of the design is better than compactness */
347 static int build_smoo_box( stream_t *s, uint8_t *smoo_box )
348 {
349     stream_sys_t *p_sys = s->p_sys;
350     sms_stream_t *sms = NULL;
351     uint32_t FourCC;
352
353     /* smoo */
354     memset( smoo_box, 0, SMOO_SIZE );
355     smoo_box[2] = (SMOO_SIZE & 0xff00)>>8;
356     smoo_box[3] = SMOO_SIZE & 0xff;
357     smoo_box[4] = 'u';
358     smoo_box[5] = 'u';
359     smoo_box[6] = 'i';
360     smoo_box[7] = 'd';
361
362     /* UUID is e1da72ba-24d7-43c3-a6a5-1b5759a1a92c */
363     ((uint32_t *)smoo_box)[2] = bswap32( 0xe1da72ba );
364     ((uint32_t *)smoo_box)[3] = bswap32( 0x24d743c3 );
365     ((uint32_t *)smoo_box)[4] = bswap32( 0xa6a51b57 );
366     ((uint32_t *)smoo_box)[5] = bswap32( 0x59a1a92c );
367
368     uint8_t *stra_box;
369     for( int i = 0; i < 3; i++ )
370     {
371         sms = NULL;
372         int cat = UNKNOWN_ES;
373         stra_box = smoo_box + i * STRA_SIZE;
374
375         stra_box[26] = (STRA_SIZE & 0xff00)>>8;
376         stra_box[27] = STRA_SIZE & 0xff;
377         stra_box[28] = 'u';
378         stra_box[29] = 'u';
379         stra_box[30] = 'i';
380         stra_box[31] = 'd';
381
382         /* UUID is b03ef770-33bd-4bac-96c7-bf25f97e2447 */
383         ((uint32_t *)stra_box)[8] = bswap32( 0xb03ef770 );
384         ((uint32_t *)stra_box)[9] = bswap32( 0x33bd4bac );
385         ((uint32_t *)stra_box)[10] = bswap32( 0x96c7bf25 );
386         ((uint32_t *)stra_box)[11] = bswap32( 0xf97e2447 );
387
388         cat = index_to_es_cat( i );
389         stra_box[48] = cat;
390         sms = SMS_GET_SELECTED_ST( cat );
391
392         stra_box[49] = 0; /* reserved */
393         if( sms == NULL )
394             continue;
395         stra_box[50] = (sms->id & 0xff00)>>8;
396         stra_box[51] = sms->id & 0xff;
397
398         ((uint32_t *)stra_box)[13] = bswap32( sms->timescale );
399         ((uint64_t *)stra_box)[7] = bswap64( p_sys->vod_duration );
400
401         quality_level_t * qlvl = get_qlevel( sms, sms->download_qlvl );
402
403         if ( qlvl )
404         {
405             FourCC = qlvl->FourCC ? qlvl->FourCC : sms->default_FourCC;
406             ((uint32_t *)stra_box)[16] = bswap32( FourCC );
407             ((uint32_t *)stra_box)[17] = bswap32( qlvl->Bitrate );
408             ((uint32_t *)stra_box)[18] = bswap32( qlvl->MaxWidth );
409             ((uint32_t *)stra_box)[19] = bswap32( qlvl->MaxHeight );
410             ((uint32_t *)stra_box)[20] = bswap32( qlvl->SamplingRate );
411             ((uint32_t *)stra_box)[21] = bswap32( qlvl->Channels );
412             ((uint32_t *)stra_box)[22] = bswap32( qlvl->BitsPerSample );
413             ((uint32_t *)stra_box)[23] = bswap32( qlvl->AudioTag );
414             ((uint16_t *)stra_box)[48] = bswap16( qlvl->nBlockAlign );
415
416             if( !qlvl->CodecPrivateData )
417                 continue;
418             stra_box[98] = stra_box[99] = stra_box[100] = 0; /* reserved */
419             assert( strlen( qlvl->CodecPrivateData ) < 512 );
420             stra_box[101] = strlen( qlvl->CodecPrivateData ) / 2;
421             uint8_t *binary_cpd = decode_string_hex_to_binary( qlvl->CodecPrivateData );
422             memcpy( stra_box + 102, binary_cpd, stra_box[101] );
423             free( binary_cpd );
424         }
425     }
426
427     return VLC_SUCCESS;
428 }
429
430 static chunk_t *build_init_chunk( stream_t *s )
431 {
432     chunk_t *ret = calloc( 1, sizeof( chunk_t ) );
433     if( unlikely( ret == NULL ) )
434         goto build_init_chunk_error;
435
436     ret->size = SMOO_SIZE;
437     ret->data = malloc( SMOO_SIZE );
438     if( !ret->data )
439         goto build_init_chunk_error;
440
441     if( build_smoo_box( s, ret->data ) == VLC_SUCCESS)
442         return ret;
443
444     free( ret->data );
445 build_init_chunk_error:
446     free( ret );
447     msg_Err( s, "build_init_chunk failed" );
448     return NULL;
449 }
450
451 static int Download( stream_t *s, sms_stream_t *sms )
452 {
453     stream_sys_t *p_sys = s->p_sys;
454
455     int index = es_cat_to_index( sms->type );
456     if ( unlikely( index == -1 ) )
457     {
458         msg_Err( s, "invalid stream type" );
459         return VLC_EGENERIC;
460     }
461     uint64_t start_time = p_sys->download.lead[index];
462
463     quality_level_t *qlevel = get_qlevel( sms, sms->download_qlvl );
464     if( unlikely( !qlevel ) )
465     {
466         msg_Err( s, "Could not get quality level id %u", sms->download_qlvl );
467         return VLC_EGENERIC;
468     }
469
470
471     chunk_t *chunk = chunk_Get( sms, start_time );
472     if( !chunk )
473     {
474         msg_Warn( s, "Could not find a chunk for stream %s, "\
475                 "start time = %"PRIu64"", sms->name, start_time );
476         return VLC_EGENERIC;
477     }
478     if( chunk->data != NULL )
479     {
480         /* Segment already downloaded */
481         msg_Warn( s, "Segment already downloaded" );
482         return VLC_SUCCESS;
483     }
484
485     chunk->type = sms->type;
486
487     char *url = ConstructUrl( sms->url_template, p_sys->base_url,
488                               qlevel, chunk->start_time );
489     if( !url )
490     {
491         msg_Err( s, "ConstructUrl returned NULL" );
492         return VLC_EGENERIC;
493     }
494
495     /* sanity check - can we download this chunk on time? */
496     uint64_t avg_bw = sms_queue_avg( p_sys->bws );
497     if( (avg_bw > 0) && (qlevel->Bitrate > 0) )
498     {
499         /* duration in ms */
500         unsigned chunk_duration = chunk->duration * 1000 / sms->timescale;
501         uint64_t size = chunk_duration * qlevel->Bitrate / 1000; /* bits */
502         unsigned estimated = size * 1000 / avg_bw;
503         if( estimated > chunk_duration )
504         {
505             msg_Warn( s,"downloading of chunk %d would take %d ms, "\
506                     "which is longer than its playback (%d ms)",
507                         chunk->sequence, estimated, chunk_duration );
508         }
509     }
510
511     mtime_t start = mdate();
512     if( sms_Download( s, chunk, url ) != VLC_SUCCESS )
513     {
514         msg_Err( s, "downloaded chunk %u from stream %s at quality"
515             " %u *failed*", chunk->sequence, sms->name, qlevel->Bitrate );
516         return VLC_EGENERIC;
517     }
518     mtime_t duration = mdate() - start;
519
520     unsigned real_id = set_track_id( chunk, sms->id );
521     if( real_id == 0)
522     {
523         msg_Err( s, "tfhd box not found or invalid chunk" );
524         return VLC_EGENERIC;
525     }
526
527     //msg_Dbg( s, "chunk ID was %i and is now %i", real_id, sms->id );
528
529     if( p_sys->b_live )
530         get_new_chunks( s, chunk );
531
532     vlc_mutex_lock( &p_sys->download.lock_wait );
533     vlc_array_append( p_sys->download.chunks, chunk );
534     vlc_cond_signal( &p_sys->download.wait );
535     vlc_mutex_unlock( &p_sys->download.lock_wait );
536
537     msg_Info( s, "downloaded chunk %d from stream %s at quality %u",
538                 chunk->sequence, sms->name, qlevel->Bitrate );
539
540     uint64_t actual_lead = chunk->start_time + chunk->duration;
541     int ind = es_cat_to_index( sms->type );
542     p_sys->download.ck_index[ind] = chunk->sequence;
543     p_sys->download.lead[ind] = __MIN( p_sys->download.lead[ind], actual_lead );
544
545     if( sms->type == VIDEO_ES ||
546             ( !SMS_GET_SELECTED_ST( VIDEO_ES ) && sms->type == AUDIO_ES ) )
547     {
548         p_sys->playback.toffset = __MIN( p_sys->playback.toffset,
549                                             (uint64_t)chunk->start_time );
550     }
551
552     unsigned dur_ms = __MAX( 1, duration / 1000 );
553     uint64_t bw = chunk->size * 8 * 1000 / dur_ms; /* bits / s */
554     if( sms_queue_put( p_sys->bws, bw ) != VLC_SUCCESS )
555         return VLC_EGENERIC;
556     avg_bw = sms_queue_avg( p_sys->bws );
557
558     if( sms->type != VIDEO_ES )
559         return VLC_SUCCESS;
560
561     /* Track could get disabled in mp4 demux if we trigger adaption too soon. */
562     if( chunk->sequence <= 1 )
563         return VLC_SUCCESS;
564
565     unsigned new_qlevel_id = BandwidthAdaptation( s, sms, avg_bw );
566     quality_level_t *new_qlevel = get_qlevel( sms, new_qlevel_id );
567     if( unlikely( !new_qlevel ) )
568     {
569         msg_Err( s, "Could not get quality level id %u", new_qlevel_id );
570         return VLC_EGENERIC;
571     }
572
573     if( new_qlevel->Bitrate != qlevel->Bitrate )
574     {
575         msg_Warn( s, "detected %s bandwidth (%u) stream",
576                  (new_qlevel->Bitrate >= qlevel->Bitrate) ? "faster" : "lower",
577                  new_qlevel->Bitrate );
578
579         sms->download_qlvl = new_qlevel_id;
580     }
581
582     if( new_qlevel->MaxWidth != qlevel->MaxWidth ||
583         new_qlevel->MaxHeight != qlevel->MaxHeight )
584     {
585         chunk_t *new_init_ck = build_init_chunk( s );
586         if( !new_init_ck )
587         {
588             return VLC_EGENERIC;
589         }
590
591         new_init_ck->offset = p_sys->download.next_chunk_offset;
592         p_sys->download.next_chunk_offset += new_init_ck->size;
593
594         vlc_mutex_lock( &p_sys->download.lock_wait );
595         vlc_array_append( p_sys->download.chunks, new_init_ck );
596         vlc_array_append( p_sys->init_chunks, new_init_ck );
597         vlc_mutex_unlock( &p_sys->download.lock_wait );
598     }
599     return VLC_SUCCESS;
600 }
601
602 static inline uint64_t get_lead( stream_t *s )
603 {
604     stream_sys_t *p_sys = s->p_sys;
605     uint64_t lead = 0;
606     uint64_t alead = p_sys->download.lead[es_cat_to_index( AUDIO_ES )];
607     uint64_t vlead = p_sys->download.lead[es_cat_to_index( VIDEO_ES )];
608     bool video = SMS_GET_SELECTED_ST( VIDEO_ES ) ? true : false;
609     bool audio = SMS_GET_SELECTED_ST( AUDIO_ES ) ? true : false;
610
611     if( video && audio )
612         lead = __MIN( vlead, alead );
613     else if( video )
614         lead = vlead;
615     else
616         lead = alead;
617
618     if( p_sys->playback.toffset < lead )
619         lead -= p_sys->playback.toffset;
620     else
621         lead = 0;
622     return lead;
623 }
624
625 static int next_track( stream_t *s )
626 {
627     stream_sys_t *p_sys = s->p_sys;
628     uint64_t tmp, min = 0;
629     int cat, ret = UNKNOWN_ES;
630     for( int i = 0; i < 3; i++ )
631     {
632         tmp = p_sys->download.lead[i];
633         cat = index_to_es_cat( i );
634         if( (!min || tmp < min) && SMS_GET_SELECTED_ST( cat ) )
635         {
636             min = tmp;
637             ret = cat;
638         }
639     }
640     return ret;
641 }
642
643 void* sms_Thread( void *p_this )
644 {
645     stream_t *s = (stream_t *)p_this;
646     stream_sys_t *p_sys = s->p_sys;
647     sms_stream_t *sms = NULL;
648     chunk_t *chunk;
649
650     int canc = vlc_savecancel();
651
652     /* We compute the average bandwidth of the 4 last downloaded
653      * chunks, but feel free to replace '4' by whatever you wish */
654     p_sys->bws = sms_queue_init( 4 );
655     if( !p_sys->bws )
656         goto cancel;
657
658     chunk_t *init_ck = build_init_chunk( s );
659     if( !init_ck )
660         goto cancel;
661
662     vlc_mutex_lock( &p_sys->download.lock_wait );
663     vlc_array_append( p_sys->download.chunks, init_ck );
664     vlc_array_append( p_sys->init_chunks, init_ck );
665     vlc_mutex_unlock( &p_sys->download.lock_wait );
666
667     p_sys->download.next_chunk_offset = init_ck->size;
668
669     /* XXX Sometimes, the video stream is cut into pieces of one exact length,
670      * while the audio stream fragments can't be made to match exactly,
671      * and for some reason the n^th advertised video fragment is related to
672      * the n+1^th advertised audio chunk or vice versa */
673
674     uint64_t start_time = 0, lead = 0;
675     int64_t i_pts_delay;
676
677     for( int i = 0; i < 3; i++ )
678     {
679         sms = SMS_GET_SELECTED_ST( index_to_es_cat( i ) );
680         if( sms && vlc_array_count( sms->chunks ) )
681         {
682             chunk = vlc_array_item_at_index( sms->chunks, 0 );
683             p_sys->download.lead[i] = chunk->start_time + p_sys->timescale / 1000;
684             if( !start_time )
685                 start_time = chunk->start_time;
686
687             if( Download( s, sms ) != VLC_SUCCESS )
688                 goto cancel;
689         }
690     }
691
692     while( 1 )
693     {
694         /* XXX replace magic number 10 by a value depending on
695          * LookAheadFragmentCount and DVRWindowLength */
696         vlc_mutex_lock( &p_sys->download.lock_wait );
697
698         if( p_sys->b_close )
699         {
700             vlc_mutex_unlock( &p_sys->download.lock_wait );
701             break;
702         }
703
704         lead = get_lead( s );
705
706         if ( stream_Control( s, STREAM_GET_PTS_DELAY, &i_pts_delay ) != VLC_SUCCESS ||
707              i_pts_delay < 1 )
708         {
709             i_pts_delay = 10 * p_sys->timescale + start_time;
710         }
711
712         while( !p_sys->b_live && ( lead > (uint64_t) i_pts_delay || NO_MORE_CHUNKS ) )
713         {
714             vlc_cond_wait( &p_sys->download.wait, &p_sys->download.lock_wait );
715             lead = get_lead( s );
716
717             if( p_sys->b_close )
718                 break;
719         }
720
721         if( p_sys->b_tseek )
722         {
723             int count = vlc_array_count( p_sys->download.chunks );
724             chunk_t *ck = NULL;
725             for( int i = 0; i < count; i++ )
726             {
727                 ck = vlc_array_item_at_index( p_sys->download.chunks, i );
728                 if( unlikely( !ck ) )
729                     goto cancel;
730                 ck->read_pos = 0;
731                 if( ck->data == NULL )
732                     continue;
733                 FREENULL( ck->data );
734             }
735
736             vlc_array_destroy( p_sys->download.chunks );
737             p_sys->download.chunks = vlc_array_new();
738
739             p_sys->playback.toffset = p_sys->time_pos;
740             for( int i = 0; i < 3; i++ )
741             {
742                 p_sys->download.lead[i] = p_sys->time_pos;
743                 p_sys->download.ck_index[i] = 0;
744             }
745             p_sys->download.next_chunk_offset = 0;
746
747             p_sys->playback.boffset = 0;
748             p_sys->playback.index = 0;
749
750             chunk_t *new_init_ck = build_init_chunk( s );
751             if( !new_init_ck )
752                 goto cancel;
753
754             new_init_ck->offset = p_sys->download.next_chunk_offset;
755             p_sys->download.next_chunk_offset += new_init_ck->size;
756
757             vlc_array_append( p_sys->download.chunks, new_init_ck );
758             vlc_array_append( p_sys->init_chunks, new_init_ck );
759             p_sys->b_tseek = false;
760         }
761         vlc_mutex_unlock( &p_sys->download.lock_wait );
762
763         sms = SMS_GET_SELECTED_ST( next_track( s ) );
764
765         vlc_mutex_lock( &p_sys->download.lock_wait );
766         unsigned i_ahead = ahead_chunks_count( p_sys, sms );
767         vlc_mutex_unlock( &p_sys->download.lock_wait );
768
769         if ( vlc_array_count( sms->chunks ) &&
770              ( !p_sys->b_live || i_ahead < p_sys->lookahead_count ) )
771         {
772             if( Download( s, sms ) != VLC_SUCCESS )
773                 goto cancel;
774         }
775     }
776
777 cancel:
778     p_sys->b_error = true;
779     msg_Warn(s, "Canceling download thread!");
780     vlc_restorecancel( canc );
781     return NULL;
782 }