]> git.sesse.net Git - vlc/blob - modules/stream_filter/smooth/smooth.c
stream_filter: smooth: missing full cleanup/fix leak
[vlc] / modules / stream_filter / smooth / smooth.c
1 /*****************************************************************************
2  * smooth.c: Smooth Streaming stream filter
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  * Heavily inspired by HLS module of Jean-Paul Saman
9  * <jpsaman _AT_ videolan _DOT_ org>
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2.1 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with this library; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
24  *****************************************************************************/
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include <limits.h>
31
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34
35 #include <assert.h>
36 #include <inttypes.h>
37
38 #include <vlc_xml.h>
39 #include <vlc_charset.h>
40 #include <vlc_stream.h>
41 #include <vlc_es.h>
42 #include <vlc_codecs.h>
43
44 #include "smooth.h"
45 #include "../../demux/mp4/libmp4.h"
46
47 /* I make the assumption that when the demux want to do a *time* seek,
48  * then p_sys->download->boffset > FAKE_STREAM_SIZE, and thus FAKE_STREAM_SIZE
49  * should be small enough. 1000 seems to be a sensible choice. See also
50  * chunk_Seek() comments to understand properly */
51 #define FAKE_STREAM_SIZE 1000
52 /*****************************************************************************
53  * Module descriptor
54  *****************************************************************************/
55 static int  Open( vlc_object_t * );
56 static void Close( vlc_object_t * );
57
58 vlc_module_begin()
59     set_category( CAT_INPUT )
60     set_subcategory( SUBCAT_INPUT_STREAM_FILTER )
61     set_description( N_("Smooth Streaming") )
62     set_shortname( "Smooth Streaming")
63     add_shortcut( "smooth" )
64     set_capability( "stream_filter", 30 )
65     set_callbacks( Open, Close )
66 vlc_module_end()
67
68 static int   Read( stream_t *, void *, unsigned );
69 static int   Peek( stream_t *, const uint8_t **, unsigned );
70 static int   Control( stream_t *, int , va_list );
71
72 static bool isSmoothStreaming( stream_t *s )
73 {
74     const uint8_t *peek;
75     int i_size = stream_Peek( s->p_source, &peek, 512 );
76     if( i_size < 512 )
77         return false;
78
79     char *peeked = malloc( 512 );
80     if( unlikely( peeked == NULL ) )
81         return false;
82
83     memcpy( peeked, peek, 512 );
84     peeked[511] = peeked[510] = '\0';
85
86     char *str;
87
88     if( !memcmp( peeked, "\xFF\xFE", 2 ) )
89     {
90         str = FromCharset( "UTF-16LE", peeked, 512 );
91         free( peeked );
92     }
93     else if( !memcmp( peeked, "\xFE\xFF", 2 ) )
94     {
95         str = FromCharset( "UTF-16BE", peeked, 512 );
96         free( peeked );
97     }
98     else
99         str = peeked;
100
101     if( str == NULL )
102         return false;
103
104     bool ret = strstr( str, "<SmoothStreamingMedia" ) != NULL;
105     free( str );
106     return ret;
107 }
108
109 #if 0
110 static void print_chunk( stream_t *s, chunk_t *ck )
111 {
112     msg_Info( s, "chunk %u type %i: duration is %"PRIu64", stime is %"PRIu64", "\
113             "size is %i, offset is %"PRIu64", read_pos is %i.",
114             ck->sequence, ck->type, ck->duration,
115             ck->start_time, ck->size, ck->offset, ck->read_pos );
116 }
117 #endif
118
119 static int parse_Manifest( stream_t *s )
120 {
121     stream_sys_t *p_sys = s->p_sys;
122     xml_t *vlc_xml = NULL;
123     xml_reader_t *vlc_reader = NULL;
124     int type = UNKNOWN_ES;
125     const char *name, *value;
126     stream_t *st = s->p_source;
127     msg_Dbg( s, "Manifest parsing\n" );
128
129     vlc_xml = xml_Create( st );
130     if( !vlc_xml )
131     {
132         msg_Err( s, "Failed to open XML parser" );
133         return VLC_EGENERIC;
134     }
135
136     vlc_reader = xml_ReaderCreate( vlc_xml, st );
137     if( !vlc_reader )
138     {
139         msg_Err( s, "Failed to open source for parsing" );
140         xml_Delete( vlc_xml );
141         return VLC_EGENERIC;
142     }
143
144     const char *node;
145     uint8_t *WaveFormatEx;
146     sms_stream_t *sms = NULL;
147     quality_level_t *ql = NULL;
148     custom_attrs_t *cp = NULL;
149     int64_t start_time = 0, duration = 0;
150     int64_t computed_start_time = 0, computed_duration = 0;
151     unsigned next_track_id = 1;
152     unsigned next_qid = 1;
153     int loop_count = 0;
154     bool b_weird = false;
155
156 #define TIMESCALE 10000000
157     while( (type = xml_ReaderNextNode( vlc_reader, &node )) > 0 )
158     {
159         switch( type )
160         {
161             case XML_READER_STARTELEM:
162
163                 if( !strcmp( node, "SmoothStreamingMedia" ) )
164                 {
165                     while( (name = xml_ReaderNextAttr( vlc_reader, &value )) )
166                     {
167                         if( !strcmp( name, "Duration" ) )
168                             p_sys->vod_duration = strtoull( value, NULL, 10 );
169                         else if( !strcmp( name, "TimeScale" ) )
170                             p_sys->timescale = strtoull( value, NULL, 10 );
171                     }
172                     if( !p_sys->timescale )
173                         p_sys->timescale = TIMESCALE;
174                 }
175                 else if( !strcmp( node, "StreamIndex" ) )
176                 {
177                     sms_Free( sms );
178                     sms = sms_New();
179                     if( unlikely( !sms ) )
180                     {
181                         xml_ReaderDelete( vlc_reader );
182                         xml_Delete( vlc_xml );
183                         return VLC_ENOMEM;
184                     }
185                     sms->id = next_track_id;
186                     next_track_id++;
187
188                     while( (name = xml_ReaderNextAttr( vlc_reader, &value )) )
189                     {
190                         if( !strcmp( name, "Type" ) )
191                         {
192                             if( !strcmp( value, "video" ) )
193                                 sms->type = VIDEO_ES;
194                             else if( !strcmp( value, "audio" ) )
195                                 sms->type = AUDIO_ES;
196                             else if( !strcmp( value, "text" ) )
197                                 sms->type = SPU_ES;
198                         }
199
200                         else if( !strcmp( name, "Name" ) )
201                             sms->name = strdup( value );
202                         else if( !strcmp( name, "TimeScale" ) )
203                             sms->timescale = strtoull( value, NULL, 10 );
204                         else if( !strcmp( name, "FourCC" ) )
205                             sms->default_FourCC =
206                                 VLC_FOURCC( value[0], value[1], value[2], value[3] );
207
208                         else if( !strcmp( name, "Chunks" ) )
209                         {
210                             sms->vod_chunks_nb = strtol( value, NULL, 10 );
211                             if( sms->vod_chunks_nb == 0 ) /* live */
212                                 sms->vod_chunks_nb = UINT32_MAX;
213                         }
214
215                         else if( !strcmp( name, "QualityLevels" ) )
216                             sms->qlevel_nb = strtoul( value, NULL, 10 );
217                         else if( !strcmp( name, "Url" ) )
218                             sms->url_template = strdup(value);
219                     }
220
221                     if( !sms->timescale )
222                         sms->timescale = TIMESCALE;
223
224                     if( !sms->name )
225                     {
226                         if( sms->type == VIDEO_ES )
227                             sms->name = strdup( "video" );
228                         else if( sms->type == AUDIO_ES )
229                             sms->name = strdup( "audio" );
230                         else if( sms->type == SPU_ES )
231                             sms->name = strdup( "text" );
232                     }
233                 }
234                 else if ( !strcmp( node, "CustomAttributes" ) )
235                 {
236                     if (!sms || !ql || cp)
237                         break;
238                     cp = (custom_attrs_t *) calloc( 1, sizeof(*cp) );
239                 }
240                 else if ( !strcmp( node, "Attribute" ) )
241                 {
242                     if (!sms || !ql || !cp)
243                         break;
244                     while( (name = xml_ReaderNextAttr( vlc_reader, &value )) )
245                     {
246                         if( !strcmp( name, "Name" ) && !cp->psz_key )
247                             cp->psz_key = strdup( value );
248                         else
249                         if( !strcmp( name, "Value" ) && !cp->psz_value )
250                             cp->psz_value = strdup( value );
251                     }
252                 }
253                 else if( !strcmp( node, "QualityLevel" ) )
254                 {
255                     if ( !sms )
256                         break;
257
258                     ql = ql_New();
259                     if( !ql )
260                     {
261                         sms_Free( sms );
262                         xml_ReaderDelete( vlc_reader );
263                         xml_Delete( vlc_xml );
264                         return VLC_ENOMEM;
265                     }
266                     ql->id = next_qid;
267                     next_qid++;
268                     while( (name = xml_ReaderNextAttr( vlc_reader, &value )) )
269                     {
270                         if( !strcmp( name, "Index" ) )
271                             ql->Index = strtol( value, NULL, 10 );
272                         else if( !strcmp( name, "Bitrate" ) )
273                             ql->Bitrate = strtoull( value, NULL, 10 );
274                         else if( !strcmp( name, "PacketSize" ) )
275                             ql->nBlockAlign = strtoull( value, NULL, 10 );
276                         else if( !strcmp( name, "FourCC" ) )
277                             ql->FourCC = VLC_FOURCC( value[0], value[1],
278                                                      value[2], value[3] );
279                         else if( !strcmp( name, "CodecPrivateData" ) )
280                             ql->CodecPrivateData = strdup( value );
281                         else if( !strcmp( name, "WaveFormatEx" ) )
282                         {
283                             WaveFormatEx = decode_string_hex_to_binary( value );
284                             uint16_t data_len = ((uint16_t *)WaveFormatEx)[8];
285                             ql->CodecPrivateData = strndup( value + 36, data_len * 2 );
286
287                             uint16_t wf_tag = ((uint16_t *)WaveFormatEx)[0];
288                             wf_tag_to_fourcc( wf_tag, &ql->FourCC, NULL );
289
290                             ql->Channels = ((uint16_t *)WaveFormatEx)[1];
291                             ql->SamplingRate = ((uint32_t *)WaveFormatEx)[1];
292                             ql->nBlockAlign = ((uint16_t *)WaveFormatEx)[6];
293                             ql->BitsPerSample = ((uint16_t *)WaveFormatEx)[7];
294                             free( WaveFormatEx );
295                         }
296                         else if( !strcmp( name, "MaxWidth" ) || !strcmp( name, "Width" ) )
297                             ql->MaxWidth = strtoul( value, NULL, 10 );
298                         else if( !strcmp( name, "MaxHeight" ) || !strcmp( name, "Height" ) )
299                             ql->MaxHeight = strtoul( value, NULL, 10 );
300                         else if( !strcmp( name, "Channels" ) )
301                             ql->Channels = strtoul( value, NULL, 10 );
302                         else if( !strcmp( name, "SamplingRate" ) )
303                             ql->SamplingRate = strtoul( value, NULL, 10 );
304                         else if( !strcmp( name, "BitsPerSample" ) )
305                             ql->BitsPerSample = strtoul( value, NULL, 10 );
306                     }
307
308                     vlc_array_append( sms->qlevels, ql );
309                 }
310                 else if ( !strcmp( node, "Content" ) && sms && !sms->url_template )
311                 {
312                     /* empty(@Url) && ./Content == manifest embedded content */
313                     sms_Free( sms );
314                     sms = NULL;
315                 }
316                 else if( !strcmp( node, "c" ) )
317                 {
318                     if ( !sms )
319                         break;
320                     loop_count++;
321                     start_time = duration = -1;
322                     while( (name = xml_ReaderNextAttr( vlc_reader, &value )) )
323                     {
324                         if( !strcmp( name, "t" ) )
325                             start_time = strtoull( value, NULL, 10 );
326                         if( !strcmp( name, "d" ) )
327                             duration = strtoull( value, NULL, 10 );
328                     }
329                     if( start_time == -1 )
330                     {
331                         assert( duration != -1 );
332                         computed_start_time += computed_duration;
333                         computed_duration = duration;
334                     }
335                     else if( duration == -1 )
336                     {
337                         assert( start_time != -1 );
338                         /* Handle weird Manifests which give only the start time
339                          * of the first segment. In those cases, we have to look
340                          * at the start time of the second segment to compute
341                          * the duration of the first one. */
342                         if( loop_count == 1 )
343                         {
344                             b_weird = true;
345                             computed_start_time = start_time;
346                             continue;
347                         }
348
349                         computed_duration = start_time - computed_start_time;
350                         if( !b_weird )
351                             computed_start_time = start_time;
352                     }
353                     else
354                     {
355                         if( b_weird )
356                             computed_duration = start_time - computed_start_time;
357                         else
358                         {
359                             computed_start_time = start_time;
360                             computed_duration = duration;
361                         }
362                     }
363
364                     if( unlikely( chunk_New( sms, computed_duration,
365                                         computed_start_time ) == NULL ) )
366                     {
367                         sms_Free( sms );
368                         xml_ReaderDelete( vlc_reader );
369                         xml_Delete( vlc_xml );
370                         return VLC_ENOMEM;
371                     }
372                     if( b_weird && start_time != -1 )
373                         computed_start_time = start_time;
374                 }
375                 break;
376
377             case XML_READER_ENDELEM:
378                 if ( !strcmp( node, "CustomAttributes" ) )
379                 {
380                     if ( cp )
381                     {
382                         ARRAY_APPEND(ql->custom_attrs, cp);
383                         cp = NULL;
384                     }
385                 }
386                 else if ( !strcmp( node, "Attribute" ) )
387                 {
388                     if( !cp->psz_key || !cp->psz_value )
389                     {
390                         free( cp->psz_key );
391                         free( cp->psz_value );
392                         FREENULL( cp );
393                     }
394                 }
395                 else if( strcmp( node, "StreamIndex" ) )
396                     break;
397                 else if ( sms )
398                 {
399                     vlc_array_append( p_sys->sms_streams, sms );
400
401                     computed_start_time = 0;
402                     computed_duration = 0;
403                     loop_count = 0;
404                     if( b_weird && !chunk_New( sms, computed_duration, computed_start_time ) )
405                     {
406                         sms_Free( sms );
407                         xml_ReaderDelete( vlc_reader );
408                         xml_Delete( vlc_xml );
409                         return VLC_ENOMEM;
410                     }
411
412                     b_weird = false;
413                     next_qid = 1;
414
415                     if( sms->qlevel_nb == 0 )
416                         sms->qlevel_nb = vlc_array_count( sms->qlevels );
417
418                     sms = NULL;
419                 }
420                 break;
421
422             case XML_READER_TEXT:
423                 break;
424             default:
425                 sms_Free( sms );
426                 xml_ReaderDelete( vlc_reader );
427                 xml_Delete( vlc_xml );
428                 return VLC_EGENERIC;
429         }
430     }
431 #undef TIMESCALE
432
433     sms_Free( sms );
434     xml_ReaderDelete( vlc_reader );
435     xml_Delete( vlc_xml );
436
437     return VLC_SUCCESS;
438 }
439
440 static void SysCleanup( stream_sys_t *p_sys )
441 {
442     if ( p_sys->sms_streams )
443     {
444         for ( int i=0; i< p_sys->sms_streams->i_count ; i++ )
445             sms_Free( p_sys->sms_streams->pp_elems[i] );
446         vlc_array_destroy( p_sys->sms_streams );
447     }
448     vlc_array_destroy( p_sys->selected_st );
449     vlc_array_destroy( p_sys->download.chunks );
450     if ( p_sys->init_chunks )
451     {
452         for ( int i=0; i< p_sys->init_chunks->i_count ; i++ )
453             chunk_Free( p_sys->init_chunks->pp_elems[i] );
454         vlc_array_destroy( p_sys->init_chunks );
455     }
456     sms_queue_free( p_sys->bws );
457     free( p_sys->base_url );
458 }
459
460 static int Open( vlc_object_t *p_this )
461 {
462     stream_t *s = (stream_t*)p_this;
463     stream_sys_t *p_sys;
464
465     if( !isSmoothStreaming( s ) )
466         return VLC_EGENERIC;
467
468     msg_Info( p_this, "Smooth Streaming (%s)", s->psz_path );
469
470     s->p_sys = p_sys = calloc( 1, sizeof(*p_sys ) );
471     if( unlikely( p_sys == NULL ) )
472         return VLC_ENOMEM;
473
474     char *uri = NULL;
475     if( unlikely( asprintf( &uri, "%s://%s", s->psz_access, s->psz_path ) < 0 ) )
476     {
477         free( p_sys );
478         return VLC_ENOMEM;
479     }
480
481     /* remove the last part of the url */
482     char *pos = strrchr( uri, '/');
483     *pos = '\0';
484     p_sys->base_url = uri;
485
486     /* XXX I don't know wether or not we should allow caching */
487     p_sys->b_cache = false;
488
489     p_sys->sms_streams = vlc_array_new();
490     p_sys->selected_st = vlc_array_new();
491     p_sys->download.chunks = vlc_array_new();
492     p_sys->init_chunks = vlc_array_new();
493     if( unlikely( !p_sys->sms_streams || !p_sys->download.chunks ||
494                   !p_sys->selected_st || !p_sys->init_chunks ) )
495     {
496         SysCleanup( p_sys );
497         free( p_sys );
498         return VLC_ENOMEM;
499     }
500
501     /* Parse SMS ismc content. */
502     if( parse_Manifest( s ) != VLC_SUCCESS )
503     {
504         SysCleanup( p_sys );
505         free( p_sys );
506         return VLC_EGENERIC;
507     }
508
509     if( !p_sys->vod_duration )
510        p_sys->b_live = true;
511
512     p_sys->i_tracks = vlc_array_count( p_sys->sms_streams );
513
514     /* Choose first video / audio / subtitle stream available */
515     sms_stream_t *tmp = NULL, *selected = NULL;
516     for( unsigned i = 0; i < p_sys->i_tracks; i++ )
517     {
518         tmp = vlc_array_item_at_index( p_sys->sms_streams, i );
519         selected = SMS_GET_SELECTED_ST( tmp->type );
520         if( !selected )
521             vlc_array_append( p_sys->selected_st, tmp );
522     }
523
524     /* Choose lowest quality for the first chunks */
525     quality_level_t *wanted, *qlvl;
526     sms_stream_t *sms = NULL;
527
528     for( unsigned i = 0; i < p_sys->i_tracks; i++ )
529     {
530         wanted = qlvl = NULL;
531         sms = vlc_array_item_at_index( p_sys->sms_streams, i );
532         if ( vlc_array_count( sms->qlevels ) )
533         {
534             wanted = vlc_array_item_at_index( sms->qlevels, 0 );
535             for( unsigned i=1; i < sms->qlevel_nb; i++ )
536             {
537                 qlvl = vlc_array_item_at_index( sms->qlevels, i );
538                 if( qlvl->Bitrate < wanted->Bitrate )
539                     wanted = qlvl;
540             }
541             sms->download_qlvl = wanted->id;
542         }
543     }
544
545     vlc_mutex_init( &p_sys->download.lock_wait );
546     vlc_cond_init( &p_sys->download.wait );
547
548     /* */
549     s->pf_read = Read;
550     s->pf_peek = Peek;
551     s->pf_control = Control;
552
553     if( vlc_clone( &p_sys->thread, sms_Thread, s, VLC_THREAD_PRIORITY_INPUT ) )
554     {
555         SysCleanup( p_sys );
556         vlc_mutex_destroy( &p_sys->download.lock_wait );
557         vlc_cond_destroy( &p_sys->download.wait );
558         free( p_sys );
559         return VLC_EGENERIC;
560     }
561
562     return VLC_SUCCESS;
563 }
564
565 static void Close( vlc_object_t *p_this )
566 {
567     stream_t *s = (stream_t*)p_this;
568     stream_sys_t *p_sys = s->p_sys;
569
570     vlc_mutex_lock( &p_sys->download.lock_wait );
571     p_sys->b_close = true;
572     /* Negate the condition variable's predicate */
573     for( int i = 0; i < 3; i++ )
574         p_sys->download.lead[i] = 0;
575     p_sys->playback.toffset = 0;
576     vlc_cond_signal(&p_sys->download.wait);
577     vlc_mutex_unlock( &p_sys->download.lock_wait );
578
579     vlc_join( p_sys->thread, NULL );
580     vlc_mutex_destroy( &p_sys->download.lock_wait );
581     vlc_cond_destroy( &p_sys->download.wait );
582
583     SysCleanup( p_sys );
584     free( p_sys );
585 }
586
587 static chunk_t *get_chunk( stream_t *s, const bool wait )
588 {
589     stream_sys_t *p_sys = s->p_sys;
590     unsigned count;
591     chunk_t *chunk = NULL;
592
593     vlc_mutex_lock( &p_sys->download.lock_wait );
594     count = vlc_array_count( p_sys->download.chunks );
595     while( p_sys->playback.index >= count || p_sys->b_tseek )
596     {
597         /* Yes I know, checking for p_sys->b_die is not reliable,
598          * that's why vlc_object_alive() has been deprecated. But if I
599          * understood well, there is no good solution with a stream_filter
600          * module anyaway. */
601         if( !wait || p_sys->b_error )
602         {
603             vlc_mutex_unlock( &p_sys->download.lock_wait );
604             msg_Warn( s, "get_chunk failed! (playback index %u)",
605                     p_sys->playback.index );
606             return NULL;
607         }
608         if( NO_MORE_CHUNKS )
609         {
610             vlc_mutex_unlock( &p_sys->download.lock_wait );
611             msg_Info( s, "No more chunks, end of the VOD" );
612             return NULL;
613         }
614
615         msg_Dbg( s, "get_chunk is waiting !!!" );
616         vlc_cond_timedwait( &p_sys->download.wait,
617                 &p_sys->download.lock_wait, mdate() + 500000 );
618         count = vlc_array_count( p_sys->download.chunks );
619         msg_Dbg( s, "count is %u, and index is %u", count, p_sys->playback.index );
620     }
621     chunk = vlc_array_item_at_index( p_sys->download.chunks, p_sys->playback.index );
622
623     vlc_mutex_unlock( &p_sys->download.lock_wait );
624
625     return chunk;
626 }
627
628 static unsigned int sms_Read( stream_t *s, uint8_t *p_read, unsigned int i_read )
629 {
630     stream_sys_t *p_sys = s->p_sys;
631     unsigned int copied = 0;
632     chunk_t *chunk = NULL;
633
634     do
635     {
636         chunk = get_chunk( s, true );
637         if( !chunk )
638             return copied;
639
640         if( chunk->read_pos >= chunk->size )
641         {
642             if( chunk->type == VIDEO_ES ||
643                 ( !SMS_GET_SELECTED_ST( VIDEO_ES ) && chunk->type == AUDIO_ES ) )
644             {
645                 vlc_mutex_lock( &p_sys->download.lock_wait );
646                 p_sys->playback.toffset += chunk->duration;
647                 vlc_mutex_unlock( &p_sys->download.lock_wait );
648                 vlc_cond_signal( &p_sys->download.wait);
649             }
650             if( !p_sys->b_cache || p_sys->b_live )
651             {
652                 FREENULL( chunk->data );
653                 chunk->read_pos = 0;
654             }
655
656             chunk->read_pos = 0;
657
658             p_sys->playback.index += 1;
659             msg_Dbg( s, "Incrementing playback index" );
660
661             continue;
662         }
663
664         if( chunk->read_pos == 0 )
665         {
666             const char *verb = p_read == NULL ? "skipping" : "reading";
667             msg_Dbg( s, "%s chunk %u (%u bytes), type %i",
668                         verb, chunk->sequence, i_read, chunk->type );
669             /* check integrity */
670             uint32_t type;
671             uint8_t *slice = chunk->data;
672             SMS_GET4BYTES( type );
673             SMS_GETFOURCC( type );
674             assert( type == ATOM_moof || type == ATOM_uuid );
675         }
676
677         uint64_t len = 0;
678         uint8_t *src = chunk->data + chunk->read_pos;
679         if( i_read <= chunk->size - chunk->read_pos )
680             len = i_read;
681         else
682             len = chunk->size - chunk->read_pos;
683
684         if( len > 0 )
685         {
686             if( p_read ) /* otherwise caller skips data */
687                 memcpy( p_read + copied, src, len );
688             chunk->read_pos += len;
689             copied += len;
690             i_read -= len;
691         }
692
693
694     } while ( i_read > 0 );
695
696     return copied;
697 }
698
699 static int Read( stream_t *s, void *buffer, unsigned i_read )
700 {
701     stream_sys_t *p_sys = s->p_sys;
702     int length = 0;
703     i_read = __MIN(INT_MAX, i_read);
704
705     if( p_sys->b_error )
706         return 0;
707
708     length = sms_Read( s, (uint8_t*) buffer, i_read );
709     if( length == 0 )
710         return 0;
711
712     /* This call to sms_Read will increment p_sys->playback.index
713      * in case the last chunk we read into is entirely read */
714     sms_Read( s, NULL, 0 );
715
716     p_sys->playback.boffset += length;
717     if( length < (int)i_read )
718         msg_Warn( s, "could not read %u bytes, only %u !", i_read, length );
719
720     return length;
721 }
722
723 /* The MP4 demux should never have to to peek outside the current chunk */
724 static int Peek( stream_t *s, const uint8_t **pp_peek, unsigned i_peek )
725 {
726     chunk_t *chunk = get_chunk( s, true );
727     if( !chunk || !chunk->data )
728         return 0;
729
730     int bytes = chunk->size - chunk->read_pos;
731     assert( bytes > 0 );
732
733     if( (unsigned)bytes < i_peek )
734     {
735         msg_Err( s, "could not peek %u bytes, only %i!", i_peek, bytes );
736     }
737     msg_Dbg( s, "peeking at chunk %u!", chunk->sequence );
738     *pp_peek = chunk->data + chunk->read_pos;
739
740     return bytes;
741 }
742
743 /* Normaly a stream_filter is not able to provide *time* seeking, since a
744  * stream_filter operates on a byte stream. Thus, in order to circumvent this
745  * limitation, I treat a STREAM_SET_POSITION request which value "pos" is less
746  * than FAKE_STREAM_SIZE as a *time* seek request, and more precisely a request
747  * to jump at time position: pos / FAKE_STREAM_SIZE * total_video_duration.
748  * For exemple, it pos == 500, it would be interpreted as a request to jump at
749  * the middle of the video.
750  * If pos > 1000, it would be treated as a normal byte seek request. That means
751  * the demux is not able to request a byte seek with 0 <= pos <= 1000
752  * (unless it is in the current chunk), but that doesn't matter in practice.
753  * Of course this a bit hack-ish, but if Smooth Streaming doesn't die, its
754  * implementation will be moved to a access_demux module, and this hack won't
755  * be needed anymore (among others). */
756 static int chunk_Seek( stream_t *s, const uint64_t pos )
757 {
758     stream_sys_t *p_sys = s->p_sys;
759
760     if( pos == p_sys->playback.boffset )
761         return VLC_SUCCESS;
762
763     chunk_t *chunk = get_chunk( s, false );
764     if( chunk == NULL )
765         return VLC_EGENERIC;
766
767     bool inside_chunk = pos >= chunk->offset &&
768             pos < (chunk->offset + chunk->size) ? true : false;
769
770     if( inside_chunk )
771     {
772         chunk->read_pos = pos - chunk->offset;
773         p_sys->playback.boffset = pos;
774         return VLC_SUCCESS;
775     }
776     else
777     {
778         if( p_sys->b_live )
779         {
780             msg_Err( s, "Cannot seek outside the current chunk for a live stream" );
781             return VLC_EGENERIC;
782         }
783
784         msg_Info( s, "Seeking outside the current chunk" );
785         assert( pos <= FAKE_STREAM_SIZE );
786
787         vlc_mutex_lock( &p_sys->download.lock_wait );
788
789         p_sys->b_tseek = true;
790         p_sys->time_pos = p_sys->vod_duration * pos / FAKE_STREAM_SIZE;
791         for( int i = 0; i < 3; i++ )
792             p_sys->download.lead[i] = 0;
793         p_sys->playback.toffset = 0;
794
795         vlc_cond_signal( &p_sys->download.wait);
796         vlc_mutex_unlock( &p_sys->download.lock_wait );
797
798         return VLC_SUCCESS;
799     }
800 }
801
802 static int Control( stream_t *s, int i_query, va_list args )
803 {
804     stream_sys_t *p_sys = s->p_sys;
805
806     switch( i_query )
807     {
808         case STREAM_CAN_SEEK:
809             *(va_arg( args, bool * )) = true;
810             break;
811         case STREAM_CAN_FASTSEEK:
812             *(va_arg( args, bool * )) = false;
813             break;
814         case STREAM_CAN_PAUSE: /* TODO */
815         case STREAM_CAN_CONTROL_PACE:
816             *(va_arg( args, bool * )) = !p_sys->b_live;
817             break;
818         case STREAM_GET_POSITION:
819             *(va_arg( args, uint64_t * )) = p_sys->playback.boffset;
820             break;
821         case STREAM_SET_POSITION:
822             {
823                 uint64_t pos = (uint64_t)va_arg(args, uint64_t);
824                 int ret = chunk_Seek(s, pos);
825                 if( ret == VLC_SUCCESS )
826                     break;
827                 else
828                     return VLC_EGENERIC;
829             }
830         case STREAM_GET_SIZE:
831             *(va_arg( args, uint64_t * )) = FAKE_STREAM_SIZE;
832             break;
833         case STREAM_GET_PTS_DELAY:
834             *va_arg (args, int64_t *) = INT64_C(1000) *
835                 var_InheritInteger(s, "network-caching");
836              break;
837         case STREAM_SET_PAUSE_STATE:
838             return (p_sys->b_live) ? VLC_EGENERIC : VLC_SUCCESS;
839             break;
840         default:
841             return VLC_EGENERIC;
842     }
843     return VLC_SUCCESS;
844 }