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