]> git.sesse.net Git - vlc/blob - modules/stream_filter/smooth/downloader.c
stream_filter: smooth: merge trackid fix with chunk read
[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 int sms_Download( stream_t *s, chunk_t *chunk, char *url )
139 {
140     stream_t *p_ts = stream_UrlNew( s, url );
141     free( url );
142     if( p_ts == NULL )
143         return VLC_EGENERIC;
144
145     int64_t size = stream_Size( p_ts );
146     if ( size < 0 )
147     {
148         stream_Delete( p_ts );
149         return VLC_EGENERIC;
150     }
151
152     uint8_t *p_data = malloc( size );
153     if( p_data == NULL )
154     {
155         stream_Delete( p_ts );
156         return VLC_ENOMEM;
157     }
158
159     int read = stream_Read( p_ts, p_data, size );
160     if( read < size && read > 0 )
161     {
162         msg_Warn( s, "sms_Download: I requested %"PRIi64" bytes, "\
163                 "but I got only %i", size, read );
164         p_data = realloc( p_data, read );
165     }
166
167     chunk->data = p_data;
168     chunk->size = (uint64_t) (read > 0 ? read : 0);
169
170     stream_Delete( p_ts );
171
172     return VLC_SUCCESS;
173 }
174
175 #ifdef DISABLE_BANDWIDTH_ADAPTATION
176 static quality_level_t *
177 BandwidthAdaptation( stream_t *s, sms_stream_t *sms,
178                      uint64_t obw, uint64_t i_duration,
179                      bool b_starved )
180 {
181     VLC_UNUSED(obw);
182     VLC_UNUSED(s);
183     VLC_UNUSED(i_duration);
184     VLC_UNUSED(b_starved);
185     return sms->current_qlvl;
186 }
187 #else
188
189 static quality_level_t *
190 BandwidthAdaptation( stream_t *s, sms_stream_t *sms,
191                      uint64_t obw, uint64_t i_duration,
192                      bool b_starved )
193 {
194     quality_level_t *ret = NULL;
195
196     assert( sms->current_qlvl );
197     if ( sms->qlevels.i_size < 2 )
198         return sms->qlevels.p_elems[0];
199
200     if ( b_starved )
201     {
202         //TODO: do something on starvation post first buffering
203         //   s->p_sys->i_probe_length *= 2;
204     }
205
206     /* PASS 1 */
207     quality_level_t *lowest = sms->qlevels.p_elems[0];
208     FOREACH_ARRAY( quality_level_t *qlevel, sms->qlevels );
209     if ( qlevel->Bitrate >= obw )
210     {
211         qlevel->i_validation_length -= i_duration;
212         qlevel->i_validation_length = __MAX(qlevel->i_validation_length, - s->p_sys->i_probe_length);
213     }
214     else
215     {
216         qlevel->i_validation_length += i_duration;
217         qlevel->i_validation_length = __MIN(qlevel->i_validation_length, s->p_sys->i_probe_length);
218     }
219     if ( qlevel->Bitrate < lowest->Bitrate )
220         lowest = qlevel;
221     FOREACH_END();
222
223     /* PASS 2 */
224     if ( sms->current_qlvl->i_validation_length == s->p_sys->i_probe_length )
225     {
226         /* might upgrade */
227         ret = sms->current_qlvl;
228     }
229     else if ( sms->current_qlvl->i_validation_length >= 0 )
230     {
231         /* do nothing */
232         ret = sms->current_qlvl;
233         msg_Dbg( s, "bw current:%uKB/s avg:%"PRIu64"KB/s qualified %"PRId64"%%",
234                 (ret->Bitrate) / (8 * 1024),
235                  obw / (8 * 1024),
236                  ( ret->i_validation_length*1000 / s->p_sys->i_probe_length ) /10 );
237         return ret;
238     }
239     else
240     {
241         /* downgrading */
242         ret = lowest;
243     }
244
245     /* might upgrade */
246     FOREACH_ARRAY( quality_level_t *qlevel, sms->qlevels );
247     if( qlevel->Bitrate <= obw &&
248             ret->Bitrate <= qlevel->Bitrate &&
249             qlevel->i_validation_length >= 0 &&
250             qlevel->i_validation_length >= ret->i_validation_length )
251     {
252         ret = qlevel;
253     }
254     FOREACH_END();
255
256     msg_Dbg( s, "bw reselected:%uKB/s avg:%"PRIu64"KB/s qualified %"PRId64"%%",
257             (ret->Bitrate) / (8 * 1024),
258              obw / (8 * 1024),
259              ( ret->i_validation_length*1000 / s->p_sys->i_probe_length ) /10 );
260
261     return ret;
262 }
263 #endif
264
265 static int parse_chunk( stream_t *s, chunk_t *ck, sms_stream_t *sms )
266 {
267     if( ck->size < 24 )
268         return VLC_EGENERIC;
269
270     stream_t *ck_s = stream_MemoryNew( s, ck->data, ck->size, true );
271     if ( !ck_s )
272         return VLC_EGENERIC;
273
274     MP4_Box_t root_box = { 0 };
275     root_box.i_type = ATOM_root;
276     root_box.i_size = ck->size;
277     if ( MP4_ReadBoxContainerChildren( ck_s, &root_box, 0 ) != 1 )
278     {
279         stream_Delete( ck_s );
280         return VLC_EGENERIC;
281     }
282 #ifndef NDEBUG
283     MP4_BoxDumpStructure( ck_s, &root_box );
284 #endif
285
286     /* Do track ID fixup */
287     const MP4_Box_t *tfhd_box = MP4_BoxGet( &root_box, "moof/traf/tfhd" );
288     if ( tfhd_box )
289         SetDWBE( &ck->data[tfhd_box->i_pos + 8 + 4], sms->id );
290
291     if ( s->p_sys->b_live )
292     {
293         const MP4_Box_t *uuid_box = MP4_BoxGet( &root_box, "moof/traf/uuid" );
294         while( uuid_box && uuid_box->i_type == ATOM_uuid )
295         {
296             if ( !CmpUUID( &uuid_box->i_uuid, &TfrfBoxUUID ) )
297                 break;
298             uuid_box = uuid_box->p_next;
299         }
300
301         if ( uuid_box )
302         {
303             const MP4_Box_data_tfrf_t *p_tfrfdata = uuid_box->data.p_tfrf;
304             for ( uint8_t i=0; i<p_tfrfdata->i_fragment_count; i++ )
305             {
306                 uint64_t dur = p_tfrfdata->p_tfrf_data_fields[i].i_fragment_duration;
307                 uint64_t stime = p_tfrfdata->p_tfrf_data_fields[i].i_fragment_abs_time;
308                 msg_Dbg( s, "\"tfrf\" fragment duration %"PRIu64", "
309                             "fragment abs time %"PRIu64, dur, stime );
310                 if( !chunk_Get( sms, stime + dur ) )
311                     chunk_AppendNew( sms, dur, stime );
312             }
313         }
314     }
315
316     MP4_Box_t *p_box = root_box.p_first;
317     while( p_box )
318     {
319         MP4_BoxFree( ck_s, p_box );
320         p_box = p_box->p_next;
321     }
322     stream_Delete( ck_s );
323
324     return VLC_SUCCESS;
325 }
326
327 #define STRA_SIZE 334
328 //#define SMOO_SIZE (STRA_SIZE * 3 + 24) /* 1026 */
329
330 /* SmooBox is a very simple MP4 box, used only to pass information
331  * to the demux layer. As this box is not aimed to travel accross networks,
332  * simplicity of the design is better than compactness */
333 static int build_smoo_box( stream_t *s, chunk_t *p_chunk )
334 {
335     stream_sys_t *p_sys = s->p_sys;
336     uint32_t FourCC;
337
338     /* smoo */
339     assert(p_sys->sms_selected.i_size);
340     size_t i_size = p_sys->sms_selected.i_size * STRA_SIZE + 24;
341     p_chunk->data = calloc( 1, i_size );
342     if ( !p_chunk->data )
343         return VLC_EGENERIC;
344     p_chunk->size = i_size;
345     uint8_t *smoo_box = p_chunk->data;
346
347     smoo_box[2] = (i_size & 0xff00)>>8;
348     smoo_box[3] = i_size & 0xff;
349     smoo_box[4] = 'u';
350     smoo_box[5] = 'u';
351     smoo_box[6] = 'i';
352     smoo_box[7] = 'd';
353
354     /* UUID is e1da72ba-24d7-43c3-a6a5-1b5759a1a92c */
355     ((uint32_t *)smoo_box)[2] = bswap32( 0xe1da72ba );
356     ((uint32_t *)smoo_box)[3] = bswap32( 0x24d743c3 );
357     ((uint32_t *)smoo_box)[4] = bswap32( 0xa6a51b57 );
358     ((uint32_t *)smoo_box)[5] = bswap32( 0x59a1a92c );
359
360     int i = 0;
361     FOREACH_ARRAY( sms_stream_t *sms, p_sys->sms_selected );
362     uint8_t *stra_box = smoo_box + i++ * STRA_SIZE;
363
364     stra_box[26] = (STRA_SIZE & 0xff00)>>8;
365     stra_box[27] = STRA_SIZE & 0xff;
366     stra_box[28] = 'u';
367     stra_box[29] = 'u';
368     stra_box[30] = 'i';
369     stra_box[31] = 'd';
370
371     /* UUID is b03ef770-33bd-4bac-96c7-bf25f97e2447 */
372     ((uint32_t *)stra_box)[8] = bswap32( 0xb03ef770 );
373     ((uint32_t *)stra_box)[9] = bswap32( 0x33bd4bac );
374     ((uint32_t *)stra_box)[10] = bswap32( 0x96c7bf25 );
375     ((uint32_t *)stra_box)[11] = bswap32( 0xf97e2447 );
376
377     stra_box[48] = sms->type;
378     stra_box[49] = 0; /* reserved */
379     stra_box[50] = (sms->id & 0xff00)>>8;
380     stra_box[51] = sms->id & 0xff;
381
382     ((uint32_t *)stra_box)[13] = bswap32( sms->timescale );
383     ((uint64_t *)stra_box)[7] = bswap64( p_sys->vod_duration );
384
385     const quality_level_t *qlvl = sms->current_qlvl;
386     if ( qlvl )
387     {
388         FourCC = qlvl->FourCC ? qlvl->FourCC : sms->default_FourCC;
389         ((uint32_t *)stra_box)[16] = bswap32( FourCC );
390         ((uint32_t *)stra_box)[17] = bswap32( qlvl->Bitrate );
391         ((uint32_t *)stra_box)[18] = bswap32( qlvl->MaxWidth );
392         ((uint32_t *)stra_box)[19] = bswap32( qlvl->MaxHeight );
393         ((uint32_t *)stra_box)[20] = bswap32( qlvl->SamplingRate );
394         ((uint32_t *)stra_box)[21] = bswap32( qlvl->Channels );
395         ((uint32_t *)stra_box)[22] = bswap32( qlvl->BitsPerSample );
396         ((uint32_t *)stra_box)[23] = bswap32( qlvl->AudioTag );
397         ((uint16_t *)stra_box)[48] = bswap16( qlvl->nBlockAlign );
398
399         if( !qlvl->CodecPrivateData )
400             continue;
401         stra_box[98] = stra_box[99] = stra_box[100] = 0; /* reserved */
402         stra_box[101] = strlen( qlvl->CodecPrivateData ) / 2;
403         if ( stra_box[101] > STRA_SIZE - 102 )
404             stra_box[101] = STRA_SIZE - 102;
405         uint8_t *binary_cpd = decode_string_hex_to_binary( qlvl->CodecPrivateData );
406         memcpy( stra_box + 102, binary_cpd, stra_box[101] );
407         free( binary_cpd );
408     }
409     FOREACH_END();
410
411     return VLC_SUCCESS;
412 }
413
414 static chunk_t *build_init_chunk( stream_t *s )
415 {
416     chunk_t *ret = calloc( 1, sizeof( chunk_t ) );
417     if( unlikely( ret == NULL ) )
418         goto build_init_chunk_error;
419
420     if( build_smoo_box( s, ret ) == VLC_SUCCESS )
421         return ret;
422
423 build_init_chunk_error:
424     free( ret );
425     msg_Err( s, "build_init_chunk failed" );
426     return NULL;
427 }
428
429 static int Download( stream_t *s, sms_stream_t *sms )
430 {
431     stream_sys_t *p_sys = s->p_sys;
432
433     assert( sms->p_nextdownload );
434     assert( sms->p_nextdownload->data == NULL );
435     assert( sms->current_qlvl );
436
437     chunk_t *chunk = sms->p_nextdownload;
438     if( !chunk )
439     {
440         msg_Warn( s, "Could not find a chunk for stream %s", sms->name );
441         return VLC_EGENERIC;
442     }
443     if( chunk->data != NULL )
444     {
445         /* Segment already downloaded */
446         msg_Warn( s, "Segment already downloaded" );
447         return VLC_SUCCESS;
448     }
449
450     chunk->type = sms->type;
451
452     char *url = ConstructUrl( sms->url_template, p_sys->download.base_url,
453                               sms->current_qlvl, chunk->start_time );
454     if( !url )
455     {
456         msg_Err( s, "ConstructUrl returned NULL" );
457         return VLC_EGENERIC;
458     }
459
460     /* sanity check - can we download this chunk on time? */
461     if( (sms->i_obw > 0) && (sms->current_qlvl->Bitrate > 0) )
462     {
463         /* duration in ms */
464         unsigned chunk_duration = chunk->duration * 1000 / sms->timescale;
465         uint64_t size = chunk_duration * sms->current_qlvl->Bitrate / 1000; /* bits */
466         unsigned estimated = size * 1000 / sms->i_obw;
467         if( estimated > chunk_duration )
468         {
469             msg_Warn( s,"downloading of chunk @%"PRIu64" would take %d ms, "
470                         "which is longer than its playback (%d ms)",
471                         chunk->start_time, estimated, chunk_duration );
472         }
473     }
474
475     mtime_t duration = mdate();
476     if( sms_Download( s, chunk, url ) != VLC_SUCCESS )
477     {
478         msg_Err( s, "downloaded chunk @%"PRIu64" from stream %s at quality"
479                     " %u *failed*", chunk->start_time, sms->name, sms->current_qlvl->Bitrate );
480         return VLC_EGENERIC;
481     }
482     duration = mdate() - duration;
483
484     parse_chunk( s, chunk, sms );
485
486     msg_Info( s, "downloaded chunk @%"PRIu64" from stream %s at quality %u",
487                  chunk->start_time, sms->name, sms->current_qlvl->Bitrate );
488
489     if (likely( duration ))
490         bw_stats_put( sms, chunk->size * 8 * CLOCK_FREQ / duration ); /* bits / s */
491
492     /* Track could get disabled in mp4 demux if we trigger adaption too soon.
493        And we don't need adaptation on last chunk */
494     if( sms->p_chunks == NULL || sms->p_chunks == sms->p_lastchunk )
495         return VLC_SUCCESS;
496
497     bool b_starved = false;
498     vlc_mutex_lock( &p_sys->playback.lock );
499     if ( &p_sys->playback.b_underrun )
500     {
501         p_sys->playback.b_underrun = false;
502         bw_stats_underrun( sms );
503         b_starved = true;
504     }
505     vlc_mutex_unlock( &p_sys->playback.lock );
506
507     quality_level_t *new_qlevel = BandwidthAdaptation( s, sms, sms->i_obw,
508                                                        duration, b_starved );
509     assert(new_qlevel);
510
511     if( sms->qlevels.i_size < 2 )
512     {
513         assert(new_qlevel == sms->current_qlvl);
514         return VLC_SUCCESS;
515     }
516
517     vlc_mutex_lock( &p_sys->playback.lock );
518     if ( p_sys->playback.init.p_datachunk == NULL && /* Don't chain/nest reinits */
519          new_qlevel != sms->current_qlvl )
520     {
521         msg_Warn( s, "detected %s bandwidth (%u) stream",
522                   (new_qlevel->Bitrate >= sms->current_qlvl->Bitrate) ? "faster" : "lower",
523                   new_qlevel->Bitrate );
524
525         quality_level_t *qlvl_backup = sms->current_qlvl;
526         sms->current_qlvl = new_qlevel;
527         chunk_t *new_init_ck = build_init_chunk( s );
528         if( new_init_ck )
529         {
530             p_sys->playback.init.p_datachunk = new_init_ck;
531             p_sys->playback.init.p_startchunk = chunk->p_next; /* to send before that one */
532             assert( chunk->p_next && chunk != sms->p_lastchunk );
533         }
534         else
535             sms->current_qlvl = qlvl_backup;
536     }
537     vlc_mutex_unlock( &p_sys->playback.lock );
538
539     return VLC_SUCCESS;
540 }
541
542 static inline bool reached_download_limit( sms_stream_t *sms, unsigned int i_max_chunks,
543                                            uint64_t i_max_time )
544 {
545     if ( !sms->p_nextdownload )
546         return true;
547
548     if ( sms->p_playback == sms->p_nextdownload ) /* chunk can be > max_time */
549         return false;
550
551     const chunk_t *p_head = sms->p_playback;
552     if ( !p_head )
553         return true;
554
555     unsigned int i_chunk_count = 0;
556     uint64_t i_total_time = 0;
557     const chunk_t *p_tail = sms->p_nextdownload;
558     while( p_head )
559     {
560         i_total_time += p_head->duration * CLOCK_FREQ / sms->timescale;
561         if ( i_max_time && i_total_time >= i_max_time )
562         {
563             return true;
564         }
565         else if ( i_max_chunks && i_chunk_count >= i_max_chunks )
566         {
567             return true;
568         }
569
570         if ( p_head == p_tail )
571             break;
572
573         p_head = p_head->p_next;
574         i_chunk_count += 1;
575     }
576
577     return false;
578 }
579
580 static bool all_reached_download_limit( stream_sys_t *p_sys, unsigned int i_max_chunks,
581                                         uint64_t i_max_time )
582 {
583     FOREACH_ARRAY( sms_stream_t *sms, p_sys->sms_selected );
584     vlc_mutex_lock( &sms->chunks_lock );
585     bool b_ret = reached_download_limit( sms, i_max_chunks, i_max_time );
586     vlc_mutex_unlock( &sms->chunks_lock );
587     if ( ! b_ret )
588         return false;
589     FOREACH_END();
590     return true;
591 }
592
593 /* Returns the first download chunk by time in the download queue */
594 static sms_stream_t *next_download_stream( stream_sys_t *p_sys )
595 {
596     sms_stream_t *p_candidate = NULL;
597     FOREACH_ARRAY( sms_stream_t *sms, p_sys->sms_selected );
598     vlc_mutex_lock( &sms->chunks_lock );
599     if ( !sms->p_nextdownload )
600     {
601         vlc_mutex_unlock( &sms->chunks_lock );
602         continue;
603     }
604     if ( p_candidate == NULL ||
605          sms->p_nextdownload->start_time < p_candidate->p_nextdownload->start_time )
606         p_candidate = sms;
607     vlc_mutex_unlock( &sms->chunks_lock );
608     FOREACH_END();
609     return p_candidate;
610 }
611
612 void* sms_Thread( void *p_this )
613 {
614     stream_t *s = (stream_t *)p_this;
615     stream_sys_t *p_sys = s->p_sys;
616
617     int canc = vlc_savecancel();
618
619     chunk_t *init_ck = build_init_chunk( s );
620     if( !init_ck )
621         goto cancel;
622
623     vlc_mutex_lock( &p_sys->playback.lock );
624     p_sys->playback.init.p_datachunk = init_ck;
625     p_sys->playback.init.p_startchunk = NULL; /* before any */
626     vlc_mutex_unlock( &p_sys->playback.lock );
627     vlc_cond_signal( &p_sys->playback.wait ); /* demuxer in Open() can start reading */
628
629     int64_t i_pts_delay = 0;
630
631     /* Buffer up first chunks */
632     int i_initial_buffering = p_sys->download.lookahead_count;
633     if ( !i_initial_buffering )
634         i_initial_buffering = 3;
635     for( int i = 0; i < i_initial_buffering; i++ )
636     {
637         FOREACH_ARRAY( sms_stream_t *sms, p_sys->sms_selected );
638         vlc_mutex_lock( &sms->chunks_lock );
639         if ( sms->p_nextdownload )
640         {
641             mtime_t duration = mdate();
642             if( Download( s, sms ) != VLC_SUCCESS )
643             {
644                 vlc_mutex_unlock( &sms->chunks_lock );
645                 goto cancel;
646             }
647             duration = mdate() - duration;
648             if (likely( duration ))
649                 bw_stats_put( sms, sms->p_nextdownload->size * 8 * CLOCK_FREQ / duration ); /* bits / s */
650             sms->p_nextdownload = sms->p_nextdownload->p_next;
651         }
652         vlc_mutex_unlock( &sms->chunks_lock );
653         FOREACH_END();
654     }
655     vlc_cond_signal( &p_sys->playback.wait );
656
657     while( !p_sys->b_close )
658     {
659         if ( !p_sys->b_live || !p_sys->download.lookahead_count )
660             stream_Control( s, STREAM_GET_PTS_DELAY, &i_pts_delay );
661
662         vlc_mutex_lock( &p_sys->lock );
663         while( all_reached_download_limit( p_sys,
664                                            p_sys->download.lookahead_count,
665                                            i_pts_delay ) )
666         {
667             vlc_cond_wait( &p_sys->download.wait, &p_sys->lock );
668             if( p_sys->b_close )
669                 break;
670         }
671
672         if( p_sys->b_close )
673         {
674             vlc_mutex_unlock( &p_sys->lock );
675             break;
676         }
677
678         sms_stream_t *sms = next_download_stream( p_sys );
679         if ( sms )
680         {
681             vlc_mutex_lock( &sms->chunks_lock );
682             vlc_mutex_unlock( &p_sys->lock );
683
684             if( Download( s, sms ) != VLC_SUCCESS )
685             {
686                 vlc_mutex_unlock( &sms->chunks_lock );
687                 break;
688             }
689             vlc_cond_signal( &p_sys->playback.wait );
690
691             if ( sms->p_nextdownload ) /* could have been modified by seek */
692                 sms->p_nextdownload = sms->p_nextdownload->p_next;
693
694             vlc_mutex_unlock( &sms->chunks_lock );
695         }
696         else
697             vlc_mutex_unlock( &p_sys->lock );
698
699         vlc_testcancel();
700     }
701
702 cancel:
703     p_sys->b_error = true;
704     msg_Dbg(s, "Canceling download thread!");
705     vlc_restorecancel( canc );
706     return NULL;
707 }