LGPL
[vlc.git] / 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     const char *needle = "<SmoothStreamingMedia";
76     const char *encoding = NULL;
77     bool ret = false;
78
79     int i_size = stream_Peek( s->p_source, &peek, 512 );
80     if( i_size < 512 )
81         return false;
82
83     char *peeked = malloc( 512 );
84     if( unlikely( !peeked ) )
85         return false;
86
87     memcpy( peeked, peek, 512 );
88     peeked[511] = peeked[510] = '\0';
89
90     if( strstr( (const char *)peeked, needle ) != NULL )
91         ret = true;
92     else
93     /* maybe it's utf-16 encoding, should we also test other encodings? */
94     {
95         if( !memcmp( peeked, "\xFF\xFE", 2 ) )
96             encoding = "UTF-16LE";
97         else if( !memcmp( peeked, "\xFE\xFF", 2 ) )
98             encoding = "UTF-16BE";
99         else
100         {
101             free( peeked );
102             return false;
103         }
104         peeked = FromCharset( encoding, peeked, 512 );
105
106         if( strstr( peeked, needle ) != NULL )
107             ret = true;
108     }
109     free( peeked );
110     return ret;
111 }
112
113 #if 0
114 static void print_chunk( stream_t *s, chunk_t *ck )
115 {
116     msg_Info( s, "chunk %u type %i: duration is %"PRIu64", stime is %"PRIu64", "\
117             "size is %i, offset is %"PRIu64", read_pos is %i.",
118             ck->sequence, ck->type, ck->duration,
119             ck->start_time, ck->size, ck->offset, ck->read_pos );
120 }
121 #endif
122
123 static int parse_Manifest( stream_t *s )
124 {
125     stream_sys_t *p_sys = s->p_sys;
126     xml_t *vlc_xml = NULL;
127     xml_reader_t *vlc_reader = NULL;
128     int type = UNKNOWN_ES;
129     const char *name, *value;
130     stream_t *st = s->p_source;
131     msg_Dbg( s, "Manifest parsing\n" );
132
133     vlc_xml = xml_Create( st );
134     if( !vlc_xml )
135     {
136         msg_Err( s, "Failed to open XML parser" );
137         return VLC_EGENERIC;
138     }
139
140     vlc_reader = xml_ReaderCreate( vlc_xml, st );
141     if( !vlc_reader )
142     {
143         msg_Err( s, "Failed to open source for parsing" );
144         xml_Delete( vlc_xml );
145         return VLC_EGENERIC;
146     }
147
148     const char *node;
149     uint8_t *WaveFormatEx;
150     sms_stream_t *sms = NULL;
151     quality_level_t *ql = NULL;
152     int64_t start_time = 0, duration = 0;
153     int64_t computed_start_time = 0, computed_duration = 0;
154     unsigned next_track_id = 1;
155     unsigned next_qid = 1;
156     int loop_count = 0;
157     bool b_weird = false;
158
159 #define TIMESCALE 10000000
160     while( (type = xml_ReaderNextNode( vlc_reader, &node )) > 0 )
161     {
162         switch( type )
163         {
164             case XML_READER_STARTELEM:
165
166                 if( !strcmp( node, "SmoothStreamingMedia" ) )
167                 {
168                     while( (name = xml_ReaderNextAttr( vlc_reader, &value )) )
169                     {
170                         if( !strcmp( name, "Duration" ) )
171                             p_sys->vod_duration = strtoull( value, NULL, 10 );
172                         if( !strcmp( name, "TimeScale" ) )
173                             p_sys->timescale = strtoull( value, NULL, 10 );
174                     }
175                     if( !p_sys->timescale )
176                         p_sys->timescale = TIMESCALE;
177                 }
178
179
180                 if( !strcmp( node, "StreamIndex" ) )
181                 {
182                     sms = sms_New();
183                     if( unlikely( !sms ) )
184                         return VLC_ENOMEM;
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                         if( !strcmp( name, "Name" ) )
201                             sms->name = strdup( value );
202                         if( !strcmp( name, "TimeScale" ) )
203                             sms->timescale = strtoull( value, NULL, 10 );
204                         if( !strcmp( name, "FourCC" ) )
205                             sms->default_FourCC =
206                                 VLC_FOURCC( value[0], value[1], value[2], value[3] );
207
208                         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                         if( !strcmp( name, "QualityLevels" ) )
216                             sms->qlevel_nb = strtoul( value, NULL, 10 );
217                         if( !strcmp( name, "Url" ) )
218                             sms->url_template = strdup(value);
219                     }
220
221                     if( sms && !sms->timescale )
222                         sms->timescale = TIMESCALE;
223                     if( !sms->name )
224                     {
225                         if( sms->type == VIDEO_ES )
226                             sms->name = strdup( "video" );
227                         else if( sms->type == AUDIO_ES )
228                             sms->name = strdup( "audio" );
229                         else if( sms->type == SPU_ES )
230                             sms->name = strdup( "text" );
231                     }
232
233                     vlc_array_append( p_sys->sms_streams, sms );
234                 }
235
236                 if( !strcmp( node, "QualityLevel" ) )
237                 {
238                     ql = ql_New();
239                     if( !ql )
240                         return VLC_ENOMEM;
241                     ql->id = next_qid;
242                     next_qid++;
243                     while( (name = xml_ReaderNextAttr( vlc_reader, &value )) )
244                     {
245                         if( !strcmp( name, "Index" ) )
246                             ql->Index = strtol( value, NULL, 10 );
247                         if( !strcmp( name, "Bitrate" ) )
248                             ql->Bitrate = strtoull( value, NULL, 10 );
249                         if( !strcmp( name, "PacketSize" ) )
250                             ql->nBlockAlign = strtoull( value, NULL, 10 );
251                         if( !strcmp( name, "FourCC" ) )
252                             ql->FourCC = VLC_FOURCC( value[0], value[1],
253                                                      value[2], value[3] );
254                         if( !strcmp( name, "CodecPrivateData" ) )
255                             ql->CodecPrivateData = strdup( value );
256                         if( !strcmp( name, "WaveFormatEx" ) )
257                         {
258                             WaveFormatEx = decode_string_hex_to_binary( value );
259                             uint16_t data_len = ((uint16_t *)WaveFormatEx)[8];
260                             ql->CodecPrivateData = strndup( value + 36, data_len * 2 );
261
262                             uint16_t wf_tag = ((uint16_t *)WaveFormatEx)[0];
263                             wf_tag_to_fourcc( wf_tag, &ql->FourCC, NULL );
264
265                             ql->Channels = ((uint16_t *)WaveFormatEx)[1];
266                             ql->SamplingRate = ((uint32_t *)WaveFormatEx)[1];
267                             ql->nBlockAlign = ((uint16_t *)WaveFormatEx)[6];
268                             ql->BitsPerSample = ((uint16_t *)WaveFormatEx)[7];
269                             free( WaveFormatEx );
270                         }
271                         if( !strcmp( name, "MaxWidth" ) || !strcmp( name, "Width" ) )
272                             ql->MaxWidth = strtoul( value, NULL, 10 );
273                         if( !strcmp( name, "MaxHeight" ) || !strcmp( name, "Height" ) )
274                             ql->MaxHeight = strtoul( value, NULL, 10 );
275                         if( !strcmp( name, "Channels" ) )
276                             ql->Channels = strtoul( value, NULL, 10 );
277                         if( !strcmp( name, "SamplingRate" ) )
278                             ql->SamplingRate = strtoul( value, NULL, 10 );
279                         if( !strcmp( name, "BitsPerSample" ) )
280                             ql->BitsPerSample = strtoul( value, NULL, 10 );
281                     }
282                     vlc_array_append( sms->qlevels, ql );
283                 }
284
285                 if( !strcmp( node, "c" ) )
286                 {
287                     loop_count++;
288                     start_time = duration = -1;
289                     while( (name = xml_ReaderNextAttr( vlc_reader, &value )) )
290                     {
291                         if( !strcmp( name, "t" ) )
292                             start_time = strtoull( value, NULL, 10 );
293                         if( !strcmp( name, "d" ) )
294                             duration = strtoull( value, NULL, 10 );
295                     }
296                     if( start_time == -1 )
297                     {
298                         assert( duration != -1 );
299                         computed_start_time += computed_duration;
300                         computed_duration = duration;
301                     }
302                     else if( duration == -1 )
303                     {
304                         assert( start_time != -1 );
305                         /* Handle weird Manifests which give only the start time
306                          * of the first segment. In those cases, we have to look
307                          * at the start time of the second segment to compute
308                          * the duration of the first one. */
309                         if( loop_count == 1 )
310                         {
311                             b_weird = true;
312                             computed_start_time = start_time;
313                             continue;
314                         }
315
316                         computed_duration = start_time - computed_start_time;
317                         if( !b_weird )
318                             computed_start_time = start_time;
319                     }
320                     else
321                     {
322                         if( b_weird )
323                             computed_duration = start_time - computed_start_time;
324                         else
325                         {
326                             computed_start_time = start_time;
327                             computed_duration = duration;
328                         }
329                     }
330
331                     if( unlikely( chunk_New( sms, computed_duration,
332                                         computed_start_time ) == NULL ) )
333                     {
334                         return VLC_ENOMEM;
335                     }
336                     if( b_weird && start_time != -1 )
337                         computed_start_time = start_time;
338                 }
339                 break;
340
341             case XML_READER_ENDELEM:
342                 if( strcmp( node, "StreamIndex" ) )
343                     break;
344
345                 computed_start_time = 0;
346                 computed_duration = 0;
347                 loop_count = 0;
348                 if( b_weird && !chunk_New( sms, computed_duration, computed_start_time ) )
349                     return VLC_ENOMEM;
350
351                 b_weird = false;
352                 next_qid = 1;
353
354                 if( sms->qlevel_nb == 0 )
355                     sms->qlevel_nb = vlc_array_count( sms->qlevels );
356                 break;
357
358             case XML_READER_NONE:
359                 break;
360             case XML_READER_TEXT:
361                 break;
362             default:
363                 return VLC_EGENERIC;
364         }
365     }
366 #undef TIMESCALE
367
368     xml_ReaderDelete( vlc_reader );
369     xml_Delete( vlc_xml );
370
371     return VLC_SUCCESS;
372 }
373
374 static int Open( vlc_object_t *p_this )
375 {
376     stream_t *s = (stream_t*)p_this;
377     stream_sys_t *p_sys;
378
379     if( !isSmoothStreaming( s ) )
380         return VLC_EGENERIC;
381
382     msg_Info( p_this, "Smooth Streaming (%s)", s->psz_path );
383
384     s->p_sys = p_sys = calloc( 1, sizeof(*p_sys ) );
385     if( unlikely( p_sys == NULL ) )
386         return VLC_ENOMEM;
387
388     char *uri = NULL;
389     if( unlikely( asprintf( &uri, "%s://%s", s->psz_access, s->psz_path ) < 0 ) )
390     {
391         free( p_sys );
392         return VLC_ENOMEM;
393     }
394
395     /* remove the last part of the url */
396     char *pos = strrchr( uri, '/');
397     *pos = '\0';
398     p_sys->base_url = uri;
399
400     /* XXX I don't know wether or not we should allow caching */
401     p_sys->b_cache = false;
402
403     p_sys->sms_streams = vlc_array_new();
404     p_sys->selected_st = vlc_array_new();
405     p_sys->download.chunks = vlc_array_new();
406     p_sys->init_chunks = vlc_array_new();
407     if( unlikely( !p_sys->sms_streams || !p_sys->download.chunks ||
408                   !p_sys->selected_st || !p_sys->init_chunks ) )
409     {
410         free( p_sys );
411         return VLC_ENOMEM;
412     }
413
414     /* Parse SMS ismc content. */
415     if( parse_Manifest( s ) != VLC_SUCCESS )
416     {
417         free( p_sys );
418         return VLC_EGENERIC;
419     }
420
421     if( !p_sys->vod_duration )
422        p_sys->b_live = true;
423
424     p_sys->i_tracks = vlc_array_count( p_sys->sms_streams );
425
426     /* Choose first video / audio / subtitle stream available */
427     sms_stream_t *tmp = NULL, *selected = NULL;
428     for( unsigned i = 0; i < p_sys->i_tracks; i++ )
429     {
430         tmp = vlc_array_item_at_index( p_sys->sms_streams, i );
431         selected = SMS_GET_SELECTED_ST( tmp->type );
432         if( !selected )
433             vlc_array_append( p_sys->selected_st, tmp );
434     }
435
436     /* Choose lowest quality for the first chunks */
437     quality_level_t *wanted, *qlvl;
438     sms_stream_t *sms = NULL;
439
440     for( unsigned i = 0; i < p_sys->i_tracks; i++ )
441     {
442         wanted = qlvl = NULL;
443         sms = vlc_array_item_at_index( p_sys->sms_streams, i );
444         wanted = vlc_array_item_at_index( sms->qlevels, 0 );
445         for( unsigned i=1; i < sms->qlevel_nb; i++ )
446         {
447             qlvl = vlc_array_item_at_index( sms->qlevels, i );
448             if( qlvl->Bitrate < wanted->Bitrate )
449                 wanted = qlvl;
450         }
451         sms->download_qlvl = wanted->id;
452     }
453
454     vlc_mutex_init( &p_sys->download.lock_wait );
455     vlc_cond_init( &p_sys->download.wait );
456
457     /* */
458     s->pf_read = Read;
459     s->pf_peek = Peek;
460     s->pf_control = Control;
461
462     if( vlc_clone( &p_sys->thread, sms_Thread, s, VLC_THREAD_PRIORITY_INPUT ) )
463     {
464         free( p_sys );
465         vlc_mutex_destroy( &p_sys->download.lock_wait );
466         vlc_cond_destroy( &p_sys->download.wait );
467         return VLC_EGENERIC;
468     }
469
470     return VLC_SUCCESS;
471 }
472
473 static void Close( vlc_object_t *p_this )
474 {
475     stream_t *s = (stream_t*)p_this;
476     stream_sys_t *p_sys = s->p_sys;
477
478     vlc_mutex_lock( &p_sys->download.lock_wait );
479     p_sys->b_close = true;
480     /* Negate the condition variable's predicate */
481     for( int i = 0; i < 3; i++ )
482         p_sys->download.lead[i] = 0;
483     p_sys->playback.toffset = 0;
484     vlc_cond_signal(&p_sys->download.wait);
485     vlc_mutex_unlock( &p_sys->download.lock_wait );
486
487     vlc_join( p_sys->thread, NULL );
488     vlc_mutex_destroy( &p_sys->download.lock_wait );
489     vlc_cond_destroy( &p_sys->download.wait );
490
491     /* Free sms streams */
492     sms_stream_t *sms;
493     for( int i = 0; i < vlc_array_count( p_sys->sms_streams ); i++ )
494     {
495         sms = vlc_array_item_at_index( p_sys->sms_streams, i );
496         if( sms )
497             sms_Free( sms );
498     }
499     /* Free downloaded chunks */
500     chunk_t *chunk;
501     for( int i = 0; i < vlc_array_count( p_sys->init_chunks ); i++ )
502     {
503         chunk = vlc_array_item_at_index( p_sys->init_chunks, i );
504         chunk_Free( chunk );
505     }
506
507     sms_queue_free( p_sys->bws );
508     vlc_array_destroy( p_sys->sms_streams );
509     vlc_array_destroy( p_sys->selected_st );
510     vlc_array_destroy( p_sys->download.chunks );
511     vlc_array_destroy( p_sys->init_chunks );
512
513     free( p_sys->base_url );
514     free( p_sys );
515 }
516
517 static chunk_t *get_chunk( stream_t *s, const bool wait )
518 {
519     stream_sys_t *p_sys = s->p_sys;
520     unsigned count;
521     chunk_t *chunk = NULL;
522
523     vlc_mutex_lock( &p_sys->download.lock_wait );
524     count = vlc_array_count( p_sys->download.chunks );
525     while( p_sys->playback.index >= count || p_sys->b_tseek )
526     {
527         /* Yes I know, checking for p_sys->b_die is not reliable,
528          * that's why vlc_object_alive() has been deprecated. But if I
529          * understood well, there is no good solution with a stream_filter
530          * module anyaway. */
531         if( !wait || p_sys->b_error )
532         {
533             vlc_mutex_unlock( &p_sys->download.lock_wait );
534             msg_Warn( s, "get_chunk failed! (playback index %u)",
535                     p_sys->playback.index );
536             return NULL;
537         }
538         if( NO_MORE_CHUNKS )
539         {
540             vlc_mutex_unlock( &p_sys->download.lock_wait );
541             msg_Info( s, "No more chunks, end of the VOD" );
542             return NULL;
543         }
544
545         msg_Dbg( s, "get_chunk is waiting !!!" );
546         vlc_cond_timedwait( &p_sys->download.wait,
547                 &p_sys->download.lock_wait, mdate() + 500000 );
548         count = vlc_array_count( p_sys->download.chunks );
549         msg_Dbg( s, "count is %u, and index is %u", count, p_sys->playback.index );
550     }
551     chunk = vlc_array_item_at_index( p_sys->download.chunks, p_sys->playback.index );
552
553     vlc_mutex_unlock( &p_sys->download.lock_wait );
554
555     return chunk;
556 }
557
558 static int sms_Read( stream_t *s, uint8_t *p_read, int i_read )
559 {
560     stream_sys_t *p_sys = s->p_sys;
561     int copied = 0;
562     chunk_t *chunk = NULL;
563
564     do
565     {
566         chunk = get_chunk( s, true );
567         if( !chunk )
568             return copied;
569
570         if( chunk->read_pos >= (int)chunk->size )
571         {
572             if( chunk->type == VIDEO_ES ||
573                 ( !SMS_GET_SELECTED_ST( VIDEO_ES ) && chunk->type == AUDIO_ES ) )
574             {
575                 vlc_mutex_lock( &p_sys->download.lock_wait );
576                 p_sys->playback.toffset += chunk->duration;
577                 vlc_mutex_unlock( &p_sys->download.lock_wait );
578                 vlc_cond_signal( &p_sys->download.wait);
579             }
580             if( !p_sys->b_cache || p_sys->b_live )
581             {
582                 FREENULL( chunk->data );
583                 chunk->read_pos = 0;
584             }
585
586             chunk->read_pos = 0;
587
588             p_sys->playback.index += 1;
589             msg_Dbg( s, "Incrementing playback index" );
590
591             continue;
592         }
593
594         if( chunk->read_pos == 0 )
595         {
596             const char *verb = p_read == NULL ? "skipping" : "reading";
597             msg_Dbg( s, "%s chunk %u (%u bytes), type %i",
598                         verb, chunk->sequence, i_read, chunk->type );
599             /* check integrity */
600             uint32_t type;
601             uint8_t *slice = chunk->data;
602             SMS_GET4BYTES( type );
603             SMS_GETFOURCC( type );
604             assert( type == ATOM_moof || type == ATOM_uuid );
605         }
606
607         int len = -1;
608         uint8_t *src = chunk->data + chunk->read_pos;
609         if( i_read <= chunk->size - chunk->read_pos )
610             len = i_read;
611         else
612             len = chunk->size - chunk->read_pos;
613
614         if( len > 0 )
615         {
616             if( p_read ) /* otherwise caller skips data */
617                 memcpy( p_read + copied, src, len );
618             chunk->read_pos += len;
619             copied += len;
620             i_read -= len;
621         }
622
623
624     } while ( i_read > 0 );
625
626     return copied;
627 }
628
629 static int Read( stream_t *s, void *buffer, unsigned i_read )
630 {
631     stream_sys_t *p_sys = s->p_sys;
632     int length = 0;
633
634     if( p_sys->b_error )
635         return 0;
636
637     length = sms_Read( s, (uint8_t*) buffer, i_read );
638     if( length < 0 )
639         return 0;
640
641     /* This call to sms_Read will increment p_sys->playback.index
642      * in case the last chunk we read into is entirely read */
643     sms_Read( s, NULL, 0 );
644
645     p_sys->playback.boffset += length;
646     if( (unsigned)length < i_read )
647         msg_Warn( s, "could not read %i bytes, only %i!", i_read, length );
648
649     return length;
650 }
651
652 /* The MP4 demux should never have to to peek outside the current chunk */
653 static int Peek( stream_t *s, const uint8_t **pp_peek, unsigned i_peek )
654 {
655     chunk_t *chunk = get_chunk( s, true );
656     if( !chunk || !chunk->data )
657         return 0;
658
659     int bytes = chunk->size - chunk->read_pos;
660     assert( bytes > 0 );
661
662     if( (unsigned)bytes < i_peek )
663     {
664         msg_Err( s, "could not peek %u bytes, only %i!", i_peek, bytes );
665     }
666     msg_Dbg( s, "peeking at chunk %u!", chunk->sequence );
667     *pp_peek = chunk->data + chunk->read_pos;
668
669     return bytes;
670 }
671
672 /* Normaly a stream_filter is not able to provide *time* seeking, since a
673  * stream_filter operates on a byte stream. Thus, in order to circumvent this
674  * limitation, I treat a STREAM_SET_POSITION request which value "pos" is less
675  * than FAKE_STREAM_SIZE as a *time* seek request, and more precisely a request
676  * to jump at time position: pos / FAKE_STREAM_SIZE * total_video_duration.
677  * For exemple, it pos == 500, it would be interpreted as a request to jump at
678  * the middle of the video.
679  * If pos > 1000, it would be treated as a normal byte seek request. That means
680  * the demux is not able to request a byte seek with 0 <= pos <= 1000
681  * (unless it is in the current chunk), but that doesn't matter in practice.
682  * Of course this a bit hack-ish, but if Smooth Streaming doesn't die, its
683  * implementation will be moved to a access_demux module, and this hack won't
684  * be needed anymore (among others). */
685 static int chunk_Seek( stream_t *s, const uint64_t pos )
686 {
687     stream_sys_t *p_sys = s->p_sys;
688
689     if( pos == p_sys->playback.boffset )
690         return VLC_SUCCESS;
691
692     chunk_t *chunk = get_chunk( s, false );
693     if( chunk == NULL )
694         return VLC_EGENERIC;
695
696     bool inside_chunk = pos >= chunk->offset &&
697             pos < (chunk->offset + chunk->size) ? true : false;
698
699     if( inside_chunk )
700     {
701         chunk->read_pos = pos - chunk->offset;
702         p_sys->playback.boffset = pos;
703         return VLC_SUCCESS;
704     }
705     else
706     {
707         if( p_sys->b_live )
708         {
709             msg_Err( s, "Cannot seek outside the current chunk for a live stream" );
710             return VLC_EGENERIC;
711         }
712
713         msg_Info( s, "Seeking outside the current chunk" );
714         assert( pos <= FAKE_STREAM_SIZE );
715
716         vlc_mutex_lock( &p_sys->download.lock_wait );
717
718         p_sys->b_tseek = true;
719         p_sys->time_pos = p_sys->vod_duration * pos / FAKE_STREAM_SIZE;
720         for( int i = 0; i < 3; i++ )
721             p_sys->download.lead[i] = 0;
722         p_sys->playback.toffset = 0;
723
724         vlc_cond_signal( &p_sys->download.wait);
725         vlc_mutex_unlock( &p_sys->download.lock_wait );
726
727         return VLC_SUCCESS;
728     }
729 }
730
731 static int Control( stream_t *s, int i_query, va_list args )
732 {
733     stream_sys_t *p_sys = s->p_sys;
734
735     switch( i_query )
736     {
737         case STREAM_CAN_SEEK:
738             *(va_arg( args, bool * )) = true;
739             break;
740         case STREAM_CAN_FASTSEEK:
741             *(va_arg( args, bool * )) = false;
742             break;
743         case STREAM_GET_POSITION:
744             *(va_arg( args, uint64_t * )) = p_sys->playback.boffset;
745             break;
746         case STREAM_SET_POSITION:
747             {
748                 uint64_t pos = (uint64_t)va_arg(args, uint64_t);
749                 int ret = chunk_Seek(s, pos);
750                 if( ret == VLC_SUCCESS )
751                     break;
752                 else
753                     return VLC_EGENERIC;
754             }
755         case STREAM_GET_SIZE:
756             *(va_arg( args, uint64_t * )) = FAKE_STREAM_SIZE;
757             break;
758         default:
759             return VLC_EGENERIC;
760     }
761     return VLC_SUCCESS;
762 }