]> git.sesse.net Git - vlc/blob - modules/stream_filter/smooth/downloader.c
stream_filter: smooth: handle "start_time" and "start time" patterns
[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                   (psz_start = strstr( psz_path, "{start_time}" )) )
84         {
85             psz_start[6] = ' ';
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 ) )
89             {
90                 free( psz_starttime );
91                 free( psz_path );
92                 return false;
93             }
94             free( psz_starttime );
95         }
96         else if ( (psz_start = strstr( psz_path, "{CustomAttributes}" )) )
97         {
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 )
104                     break;
105             FOREACH_END()
106             if ( !psz_attributes ||
107                  ! Replace( &psz_path, psz_start - psz_path, "{CustomAttributes}", psz_attributes ) )
108             {
109                 free( psz_attributes );
110                 free( psz_path );
111                 return false;
112             }
113             free( psz_attributes );
114         }
115         else
116             break;
117     }
118
119     char *psz_url;
120     if( asprintf( &psz_url, "%s/%s", psz_base_url, psz_path ) < 0 )
121     {
122         free( psz_path );
123         return NULL;
124     }
125     free( psz_path );
126     return psz_url;
127 }
128
129 static chunk_t * chunk_Get( sms_stream_t *sms, const uint64_t start_time )
130 {
131     int len = vlc_array_count( sms->chunks );
132     for( int i = 0; i < len; i++ )
133     {
134         chunk_t * chunk = vlc_array_item_at_index( sms->chunks, i );
135         if( !chunk ) return NULL;
136
137         if( chunk->start_time <= start_time &&
138                 chunk->start_time + chunk->duration > start_time )
139         {
140             return chunk;
141         }
142     }
143     return NULL;
144 }
145
146 static unsigned set_track_id( chunk_t *chunk, const unsigned tid )
147 {
148     uint32_t size, type;
149     if( !chunk->data )
150         return 0;
151     uint8_t *slice = chunk->data;
152     if( !slice )
153         return 0;
154
155     SMS_GET4BYTES( size );
156     SMS_GETFOURCC( type );
157     assert( type == ATOM_moof );
158
159     SMS_GET4BYTES( size );
160     SMS_GETFOURCC( type );
161     assert( type == ATOM_mfhd );
162     slice += size - 8;
163
164     SMS_GET4BYTES( size );
165     SMS_GETFOURCC( type );
166     assert( type == ATOM_traf );
167
168     SMS_GET4BYTES( size );
169     SMS_GETFOURCC( type );
170     if( type != ATOM_tfhd )
171         return 0;
172
173     unsigned ret = bswap32( ((uint32_t *)slice)[1] );
174     ((uint32_t *)slice)[1] = bswap32( tid );
175
176     return ret;
177 }
178
179 static int sms_Download( stream_t *s, chunk_t *chunk, char *url )
180 {
181     stream_sys_t *p_sys = s->p_sys;
182
183     stream_t *p_ts = stream_UrlNew( s, url );
184     free( url );
185     if( p_ts == NULL )
186         return VLC_EGENERIC;
187
188     int64_t size = stream_Size( p_ts );
189     if ( size < 0 )
190         return VLC_EGENERIC;
191
192     chunk->size = (uint64_t) size;
193     chunk->offset = p_sys->download.next_chunk_offset;
194     p_sys->download.next_chunk_offset += chunk->size;
195
196     chunk->data = malloc( size );
197
198     if( chunk->data == NULL )
199     {
200         stream_Delete( p_ts );
201         return VLC_ENOMEM;
202     }
203
204     int read = stream_Read( p_ts, chunk->data, size );
205     if( read < size )
206     {
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 );
210     }
211
212     stream_Delete( p_ts );
213
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 );
218
219     return VLC_SUCCESS;
220 }
221
222 #ifdef DISABLE_BANDWIDTH_ADAPTATION
223 static unsigned BandwidthAdaptation( stream_t *s,
224         sms_stream_t *sms, uint64_t bandwidth )
225 {
226     VLC_UNUSED(bandwidth);
227     VLC_UNUSED(s);
228     return sms->download_qlvl;
229 }
230 #else
231
232 static unsigned BandwidthAdaptation( stream_t *s,
233         sms_stream_t *sms, uint64_t bandwidth )
234 {
235     if( sms->type != VIDEO_ES )
236         return sms->download_qlvl;
237
238     uint64_t bw_candidate = 0;
239     quality_level_t *qlevel;
240     unsigned ret = sms->download_qlvl;
241
242     for( unsigned i = 0; i < sms->qlevel_nb; i++ )
243     {
244         qlevel = vlc_array_item_at_index( sms->qlevels, i );
245         if( unlikely( !qlevel ) )
246         {
247             msg_Err( s, "Could no get %uth quality level", i );
248             return 0;
249         }
250
251         if( qlevel->Bitrate < (bandwidth - bandwidth / 3) &&
252                 qlevel->Bitrate > bw_candidate )
253         {
254             bw_candidate = qlevel->Bitrate;
255             ret = qlevel->id;
256         }
257     }
258
259     return ret;
260 }
261 #endif
262
263 static int get_new_chunks( stream_t *s, chunk_t *ck )
264 {
265     stream_sys_t *p_sys = s->p_sys;
266
267     uint8_t *slice = ck->data;
268     if( !slice )
269         return VLC_EGENERIC;
270     uint8_t version, fragment_count;
271     uint32_t size, type, flags;
272     sms_stream_t *sms;
273     UUID_t uuid;
274     TfrfBoxDataFields_t *tfrf_df;
275
276     sms = SMS_GET_SELECTED_ST( ck->type );
277
278     SMS_GET4BYTES( size );
279     SMS_GETFOURCC( type );
280     assert( type == ATOM_moof );
281
282     SMS_GET4BYTES( size );
283     SMS_GETFOURCC( type );
284     assert( type == ATOM_mfhd );
285     slice += size - 8;
286
287     SMS_GET4BYTES( size );
288     SMS_GETFOURCC( type );
289     assert( type == ATOM_traf );
290
291     for(;;)
292     {
293         SMS_GET4BYTES( size );
294         assert( size > 1 );
295         SMS_GETFOURCC( type );
296         if( type == ATOM_mdat )
297         {
298             msg_Err( s, "No uuid box found :-(" );
299             return VLC_EGENERIC;
300         }
301         else if( type == ATOM_uuid )
302         {
303             GetUUID( &uuid, slice);
304             if( !CmpUUID( &uuid, &TfrfBoxUUID ) )
305                 break;
306         }
307         slice += size - 8;
308     }
309
310     slice += 16;
311     SMS_GET1BYTE( version );
312     SMS_GET3BYTES( flags );
313     SMS_GET1BYTE( fragment_count );
314
315     tfrf_df = calloc( fragment_count, sizeof( TfrfBoxDataFields_t ) );
316     if( unlikely( tfrf_df == NULL ) )
317         return VLC_EGENERIC;
318
319     for( uint8_t i = 0; i < fragment_count; i++ )
320     {
321         SMS_GET4or8BYTES( tfrf_df[i].i_fragment_abs_time );
322         SMS_GET4or8BYTES( tfrf_df[i].i_fragment_duration );
323     }
324
325     msg_Dbg( s, "read box: \"tfrf\" version %d, flags 0x%x, "\
326             "fragment count %"PRIu8, version, flags, fragment_count );
327
328     for( uint8_t i = 0; i < fragment_count; i++ )
329     {
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);
334
335         if( !chunk_Get( sms, stime + dur ) )
336             chunk_New( sms, dur, stime );
337     }
338     free( tfrf_df );
339
340     return VLC_SUCCESS;
341 }
342
343 #define STRA_SIZE 334
344 #define SMOO_SIZE (STRA_SIZE * 3 + 24) /* 1026 */
345
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 )
350 {
351     stream_sys_t *p_sys = s->p_sys;
352     sms_stream_t *sms = NULL;
353     uint32_t FourCC;
354
355     /* smoo */
356     memset( smoo_box, 0, SMOO_SIZE );
357     smoo_box[2] = (SMOO_SIZE & 0xff00)>>8;
358     smoo_box[3] = SMOO_SIZE & 0xff;
359     smoo_box[4] = 'u';
360     smoo_box[5] = 'u';
361     smoo_box[6] = 'i';
362     smoo_box[7] = 'd';
363
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 );
369
370     uint8_t *stra_box;
371     for( int i = 0; i < 3; i++ )
372     {
373         sms = NULL;
374         int cat = UNKNOWN_ES;
375         stra_box = smoo_box + i * STRA_SIZE;
376
377         stra_box[26] = (STRA_SIZE & 0xff00)>>8;
378         stra_box[27] = STRA_SIZE & 0xff;
379         stra_box[28] = 'u';
380         stra_box[29] = 'u';
381         stra_box[30] = 'i';
382         stra_box[31] = 'd';
383
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 );
389
390         cat = index_to_es_cat( i );
391         stra_box[48] = cat;
392         sms = SMS_GET_SELECTED_ST( cat );
393
394         stra_box[49] = 0; /* reserved */
395         if( sms == NULL )
396             continue;
397         stra_box[50] = (sms->id & 0xff00)>>8;
398         stra_box[51] = sms->id & 0xff;
399
400         ((uint32_t *)stra_box)[13] = bswap32( sms->timescale );
401         ((uint64_t *)stra_box)[7] = bswap64( p_sys->vod_duration );
402
403         quality_level_t * qlvl = get_qlevel( sms, sms->download_qlvl );
404
405         if ( qlvl )
406         {
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 );
417
418             if( !qlvl->CodecPrivateData )
419                 continue;
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] );
426             free( binary_cpd );
427         }
428     }
429
430     return VLC_SUCCESS;
431 }
432
433 static chunk_t *build_init_chunk( stream_t *s )
434 {
435     chunk_t *ret = calloc( 1, sizeof( chunk_t ) );
436     if( unlikely( ret == NULL ) )
437         goto build_init_chunk_error;
438
439     ret->size = SMOO_SIZE;
440     ret->data = malloc( SMOO_SIZE );
441     if( !ret->data )
442         goto build_init_chunk_error;
443
444     if( build_smoo_box( s, ret->data ) == VLC_SUCCESS)
445         return ret;
446
447     free( ret->data );
448 build_init_chunk_error:
449     free( ret );
450     msg_Err( s, "build_init_chunk failed" );
451     return NULL;
452 }
453
454 static int Download( stream_t *s, sms_stream_t *sms )
455 {
456     stream_sys_t *p_sys = s->p_sys;
457
458     int index = es_cat_to_index( sms->type );
459     if ( unlikely( index == -1 ) )
460     {
461         msg_Err( s, "invalid stream type" );
462         return VLC_EGENERIC;
463     }
464     uint64_t start_time = p_sys->download.lead[index];
465
466     quality_level_t *qlevel = get_qlevel( sms, sms->download_qlvl );
467     if( unlikely( !qlevel ) )
468     {
469         msg_Err( s, "Could not get quality level id %u", sms->download_qlvl );
470         return VLC_EGENERIC;
471     }
472
473
474     chunk_t *chunk = chunk_Get( sms, start_time );
475     if( !chunk )
476     {
477         msg_Warn( s, "Could not find a chunk for stream %s, "\
478                 "start time = %"PRIu64"", sms->name, start_time );
479         return VLC_EGENERIC;
480     }
481     if( chunk->data != NULL )
482     {
483         /* Segment already downloaded */
484         msg_Warn( s, "Segment already downloaded" );
485         return VLC_SUCCESS;
486     }
487
488     chunk->type = sms->type;
489
490     char *url = ConstructUrl( sms->url_template, p_sys->base_url,
491                               qlevel, chunk->start_time );
492     if( !url )
493     {
494         msg_Err( s, "ConstructUrl returned NULL" );
495         return VLC_EGENERIC;
496     }
497
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) )
501     {
502         /* duration in ms */
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 )
507         {
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 );
511         }
512     }
513
514     mtime_t start = mdate();
515     if( sms_Download( s, chunk, url ) != VLC_SUCCESS )
516     {
517         msg_Err( s, "downloaded chunk %u from stream %s at quality"
518             " %u *failed*", chunk->sequence, sms->name, qlevel->Bitrate );
519         return VLC_EGENERIC;
520     }
521     mtime_t duration = mdate() - start;
522
523     unsigned real_id = set_track_id( chunk, sms->id );
524     if( real_id == 0)
525     {
526         msg_Err( s, "tfhd box not found or invalid chunk" );
527         return VLC_EGENERIC;
528     }
529
530     //msg_Dbg( s, "chunk ID was %i and is now %i", real_id, sms->id );
531
532     if( p_sys->b_live )
533         get_new_chunks( s, chunk );
534
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 );
539
540     msg_Info( s, "downloaded chunk %d from stream %s at quality %u",
541                 chunk->sequence, sms->name, qlevel->Bitrate );
542
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 );
547
548     if( sms->type == VIDEO_ES ||
549             ( !SMS_GET_SELECTED_ST( VIDEO_ES ) && sms->type == AUDIO_ES ) )
550     {
551         p_sys->playback.toffset = __MIN( p_sys->playback.toffset,
552                                             (uint64_t)chunk->start_time );
553     }
554
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 )
558         return VLC_EGENERIC;
559     avg_bw = sms_queue_avg( p_sys->bws );
560
561     if( sms->type != VIDEO_ES )
562         return VLC_SUCCESS;
563
564     /* Track could get disabled in mp4 demux if we trigger adaption too soon. */
565     if( chunk->sequence <= 1 )
566         return VLC_SUCCESS;
567
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 ) )
571     {
572         msg_Err( s, "Could not get quality level id %u", new_qlevel_id );
573         return VLC_EGENERIC;
574     }
575
576     if( new_qlevel->Bitrate != qlevel->Bitrate )
577     {
578         msg_Warn( s, "detected %s bandwidth (%u) stream",
579                  (new_qlevel->Bitrate >= qlevel->Bitrate) ? "faster" : "lower",
580                  new_qlevel->Bitrate );
581
582         sms->download_qlvl = new_qlevel_id;
583     }
584
585     if( new_qlevel->MaxWidth != qlevel->MaxWidth ||
586         new_qlevel->MaxHeight != qlevel->MaxHeight )
587     {
588         chunk_t *new_init_ck = build_init_chunk( s );
589         if( !new_init_ck )
590         {
591             return VLC_EGENERIC;
592         }
593
594         new_init_ck->offset = p_sys->download.next_chunk_offset;
595         p_sys->download.next_chunk_offset += new_init_ck->size;
596
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 );
601     }
602     return VLC_SUCCESS;
603 }
604
605 static inline uint64_t get_lead( stream_t *s )
606 {
607     stream_sys_t *p_sys = s->p_sys;
608     uint64_t lead = 0;
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;
613
614     if( video && audio )
615         lead = __MIN( vlead, alead );
616     else if( video )
617         lead = vlead;
618     else
619         lead = alead;
620
621     if( p_sys->playback.toffset < lead )
622         lead -= p_sys->playback.toffset;
623     else
624         lead = 0;
625     return lead;
626 }
627
628 static int next_track( stream_t *s )
629 {
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++ )
634     {
635         tmp = p_sys->download.lead[i];
636         cat = index_to_es_cat( i );
637         if( (!min || tmp < min) && SMS_GET_SELECTED_ST( cat ) )
638         {
639             min = tmp;
640             ret = cat;
641         }
642     }
643     return ret;
644 }
645
646 void* sms_Thread( void *p_this )
647 {
648     stream_t *s = (stream_t *)p_this;
649     stream_sys_t *p_sys = s->p_sys;
650     sms_stream_t *sms = NULL;
651     chunk_t *chunk;
652
653     int canc = vlc_savecancel();
654
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 );
658     if( !p_sys->bws )
659         goto cancel;
660
661     chunk_t *init_ck = build_init_chunk( s );
662     if( !init_ck )
663         goto cancel;
664
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 );
669
670     p_sys->download.next_chunk_offset = init_ck->size;
671
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 */
676
677     uint64_t start_time = 0, lead = 0;
678     int64_t i_pts_delay;
679
680     for( int i = 0; i < 3; i++ )
681     {
682         sms = SMS_GET_SELECTED_ST( index_to_es_cat( i ) );
683         if( sms && vlc_array_count( sms->chunks ) )
684         {
685             chunk = vlc_array_item_at_index( sms->chunks, 0 );
686             p_sys->download.lead[i] = chunk->start_time + p_sys->timescale / 1000;
687             if( !start_time )
688                 start_time = chunk->start_time;
689
690             if( Download( s, sms ) != VLC_SUCCESS )
691                 goto cancel;
692         }
693     }
694
695     while( 1 )
696     {
697         /* XXX replace magic number 10 by a value depending on
698          * LookAheadFragmentCount and DVRWindowLength */
699         vlc_mutex_lock( &p_sys->download.lock_wait );
700
701         if( p_sys->b_close )
702         {
703             vlc_mutex_unlock( &p_sys->download.lock_wait );
704             break;
705         }
706
707         lead = get_lead( s );
708
709         if ( stream_Control( s, STREAM_GET_PTS_DELAY, &i_pts_delay ) != VLC_SUCCESS ||
710              i_pts_delay < 1 )
711         {
712             i_pts_delay = 10 * p_sys->timescale + start_time;
713         }
714
715         while( !p_sys->b_live && ( lead > (uint64_t) i_pts_delay || NO_MORE_CHUNKS ) )
716         {
717             vlc_cond_wait( &p_sys->download.wait, &p_sys->download.lock_wait );
718             lead = get_lead( s );
719
720             if( p_sys->b_close )
721                 break;
722         }
723
724         if( p_sys->b_tseek )
725         {
726             int count = vlc_array_count( p_sys->download.chunks );
727             chunk_t *ck = NULL;
728             for( int i = 0; i < count; i++ )
729             {
730                 ck = vlc_array_item_at_index( p_sys->download.chunks, i );
731                 if( unlikely( !ck ) )
732                     goto cancel;
733                 ck->read_pos = 0;
734                 if( ck->data == NULL )
735                     continue;
736                 FREENULL( ck->data );
737             }
738
739             vlc_array_destroy( p_sys->download.chunks );
740             p_sys->download.chunks = vlc_array_new();
741
742             p_sys->playback.toffset = p_sys->time_pos;
743             for( int i = 0; i < 3; i++ )
744             {
745                 p_sys->download.lead[i] = p_sys->time_pos;
746                 p_sys->download.ck_index[i] = 0;
747             }
748             p_sys->download.next_chunk_offset = 0;
749
750             p_sys->playback.boffset = 0;
751             p_sys->playback.index = 0;
752
753             chunk_t *new_init_ck = build_init_chunk( s );
754             if( !new_init_ck )
755                 goto cancel;
756
757             new_init_ck->offset = p_sys->download.next_chunk_offset;
758             p_sys->download.next_chunk_offset += new_init_ck->size;
759
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;
763         }
764         vlc_mutex_unlock( &p_sys->download.lock_wait );
765
766         sms = SMS_GET_SELECTED_ST( next_track( s ) );
767
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 );
771
772         if ( vlc_array_count( sms->chunks ) &&
773              ( !p_sys->b_live || i_ahead < p_sys->lookahead_count ) )
774         {
775             if( Download( s, sms ) != VLC_SUCCESS )
776                 goto cancel;
777         }
778     }
779
780 cancel:
781     p_sys->b_error = true;
782     msg_Warn(s, "Canceling download thread!");
783     vlc_restorecancel( canc );
784     return NULL;
785 }