1 /*****************************************************************************
2 * smooth.c: Smooth Streaming stream filter
3 *****************************************************************************
4 * Copyright (C) 1996-2012 VLC authors and VideoLAN
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>
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.
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.
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 *****************************************************************************/
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
39 #include <vlc_charset.h>
40 #include <vlc_stream.h>
42 #include <vlc_codecs.h>
45 #include "../../demux/mp4/libmp4.h"
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 /*****************************************************************************
54 *****************************************************************************/
55 static int Open( vlc_object_t * );
56 static void Close( vlc_object_t * );
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 )
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 );
72 static bool isSmoothStreaming( stream_t *s )
75 int i_size = stream_Peek( s->p_source, &peek, 512 );
79 char *peeked = malloc( 512 );
80 if( unlikely( peeked == NULL ) )
83 memcpy( peeked, peek, 512 );
84 peeked[511] = peeked[510] = '\0';
88 if( !memcmp( peeked, "\xFF\xFE", 2 ) )
90 str = FromCharset( "UTF-16LE", peeked, 512 );
93 else if( !memcmp( peeked, "\xFE\xFF", 2 ) )
95 str = FromCharset( "UTF-16BE", peeked, 512 );
104 bool ret = strstr( str, "<SmoothStreamingMedia" ) != NULL;
110 static void print_chunk( stream_t *s, chunk_t *ck )
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 );
119 static int parse_Manifest( stream_t *s )
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" );
129 vlc_xml = xml_Create( st );
132 msg_Err( s, "Failed to open XML parser" );
136 vlc_reader = xml_ReaderCreate( vlc_xml, st );
139 msg_Err( s, "Failed to open source for parsing" );
140 xml_Delete( vlc_xml );
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;
154 bool b_weird = false;
156 #define TIMESCALE 10000000
157 while( (type = xml_ReaderNextNode( vlc_reader, &node )) > 0 )
161 case XML_READER_STARTELEM:
163 if( !strcmp( node, "SmoothStreamingMedia" ) )
165 while( (name = xml_ReaderNextAttr( vlc_reader, &value )) )
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 );
172 if( !p_sys->timescale )
173 p_sys->timescale = TIMESCALE;
175 else if( !strcmp( node, "StreamIndex" ) )
179 if( unlikely( !sms ) )
181 xml_ReaderDelete( vlc_reader );
182 xml_Delete( vlc_xml );
185 sms->id = next_track_id;
188 while( (name = xml_ReaderNextAttr( vlc_reader, &value )) )
190 if( !strcmp( name, "Type" ) )
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" ) )
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] );
208 else if( !strcmp( name, "Chunks" ) )
210 sms->vod_chunks_nb = strtol( value, NULL, 10 );
211 if( sms->vod_chunks_nb == 0 ) /* live */
212 sms->vod_chunks_nb = UINT32_MAX;
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);
221 if( !sms->timescale )
222 sms->timescale = TIMESCALE;
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" );
234 else if ( !strcmp( node, "CustomAttributes" ) )
236 if (!sms || !ql || cp)
238 cp = (custom_attrs_t *) calloc( 1, sizeof(*cp) );
240 else if ( !strcmp( node, "Attribute" ) )
242 if (!sms || !ql || !cp)
244 while( (name = xml_ReaderNextAttr( vlc_reader, &value )) )
246 if( !strcmp( name, "Name" ) && !cp->psz_key )
247 cp->psz_key = strdup( value );
249 if( !strcmp( name, "Value" ) && !cp->psz_value )
250 cp->psz_value = strdup( value );
253 else if( !strcmp( node, "QualityLevel" ) )
262 xml_ReaderDelete( vlc_reader );
263 xml_Delete( vlc_xml );
268 while( (name = xml_ReaderNextAttr( vlc_reader, &value )) )
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" ) )
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 );
287 uint16_t wf_tag = ((uint16_t *)WaveFormatEx)[0];
288 wf_tag_to_fourcc( wf_tag, &ql->FourCC, NULL );
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 );
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 );
308 vlc_array_append( sms->qlevels, ql );
310 else if ( !strcmp( node, "Content" ) && sms && !sms->url_template )
312 /* empty(@Url) && ./Content == manifest embedded content */
316 else if( !strcmp( node, "c" ) )
321 start_time = duration = -1;
322 while( (name = xml_ReaderNextAttr( vlc_reader, &value )) )
324 if( !strcmp( name, "t" ) )
325 start_time = strtoull( value, NULL, 10 );
326 if( !strcmp( name, "d" ) )
327 duration = strtoull( value, NULL, 10 );
329 if( start_time == -1 )
331 assert( duration != -1 );
332 computed_start_time += computed_duration;
333 computed_duration = duration;
335 else if( duration == -1 )
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 )
345 computed_start_time = start_time;
349 computed_duration = start_time - computed_start_time;
351 computed_start_time = start_time;
356 computed_duration = start_time - computed_start_time;
359 computed_start_time = start_time;
360 computed_duration = duration;
364 if( unlikely( chunk_New( sms, computed_duration,
365 computed_start_time ) == NULL ) )
368 xml_ReaderDelete( vlc_reader );
369 xml_Delete( vlc_xml );
372 if( b_weird && start_time != -1 )
373 computed_start_time = start_time;
377 case XML_READER_ENDELEM:
378 if ( !strcmp( node, "CustomAttributes" ) )
382 ARRAY_APPEND(ql->custom_attrs, cp);
386 else if ( !strcmp( node, "Attribute" ) )
388 if( !cp->psz_key || !cp->psz_value )
391 free( cp->psz_value );
395 else if( strcmp( node, "StreamIndex" ) )
399 vlc_array_append( p_sys->sms_streams, sms );
401 computed_start_time = 0;
402 computed_duration = 0;
404 if( b_weird && !chunk_New( sms, computed_duration, computed_start_time ) )
407 xml_ReaderDelete( vlc_reader );
408 xml_Delete( vlc_xml );
415 if( sms->qlevel_nb == 0 )
416 sms->qlevel_nb = vlc_array_count( sms->qlevels );
422 case XML_READER_TEXT:
426 xml_ReaderDelete( vlc_reader );
427 xml_Delete( vlc_xml );
434 xml_ReaderDelete( vlc_reader );
435 xml_Delete( vlc_xml );
440 static void SysCleanup( stream_sys_t *p_sys )
442 if ( p_sys->sms_streams )
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 );
448 vlc_array_destroy( p_sys->selected_st );
449 vlc_array_destroy( p_sys->download.chunks );
450 if ( p_sys->init_chunks )
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 );
456 sms_queue_free( p_sys->bws );
457 free( p_sys->base_url );
460 static int Open( vlc_object_t *p_this )
462 stream_t *s = (stream_t*)p_this;
465 if( !isSmoothStreaming( s ) )
468 msg_Info( p_this, "Smooth Streaming (%s)", s->psz_path );
470 s->p_sys = p_sys = calloc( 1, sizeof(*p_sys ) );
471 if( unlikely( p_sys == NULL ) )
475 if( unlikely( asprintf( &uri, "%s://%s", s->psz_access, s->psz_path ) < 0 ) )
481 /* remove the last part of the url */
482 char *pos = strrchr( uri, '/');
484 p_sys->base_url = uri;
486 /* XXX I don't know wether or not we should allow caching */
487 p_sys->b_cache = false;
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 ) )
501 /* Parse SMS ismc content. */
502 if( parse_Manifest( s ) != VLC_SUCCESS )
509 if( !p_sys->vod_duration )
510 p_sys->b_live = true;
512 p_sys->i_tracks = vlc_array_count( p_sys->sms_streams );
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++ )
518 tmp = vlc_array_item_at_index( p_sys->sms_streams, i );
519 selected = SMS_GET_SELECTED_ST( tmp->type );
521 vlc_array_append( p_sys->selected_st, tmp );
524 /* Choose lowest quality for the first chunks */
525 quality_level_t *wanted, *qlvl;
526 sms_stream_t *sms = NULL;
528 for( unsigned i = 0; i < p_sys->i_tracks; i++ )
530 wanted = qlvl = NULL;
531 sms = vlc_array_item_at_index( p_sys->sms_streams, i );
532 if ( vlc_array_count( sms->qlevels ) )
534 wanted = vlc_array_item_at_index( sms->qlevels, 0 );
535 for( unsigned i=1; i < sms->qlevel_nb; i++ )
537 qlvl = vlc_array_item_at_index( sms->qlevels, i );
538 if( qlvl->Bitrate < wanted->Bitrate )
541 sms->download_qlvl = wanted->id;
545 vlc_mutex_init( &p_sys->download.lock_wait );
546 vlc_cond_init( &p_sys->download.wait );
551 s->pf_control = Control;
553 if( vlc_clone( &p_sys->thread, sms_Thread, s, VLC_THREAD_PRIORITY_INPUT ) )
556 vlc_mutex_destroy( &p_sys->download.lock_wait );
557 vlc_cond_destroy( &p_sys->download.wait );
565 static void Close( vlc_object_t *p_this )
567 stream_t *s = (stream_t*)p_this;
568 stream_sys_t *p_sys = s->p_sys;
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 );
579 vlc_join( p_sys->thread, NULL );
580 vlc_mutex_destroy( &p_sys->download.lock_wait );
581 vlc_cond_destroy( &p_sys->download.wait );
587 static chunk_t *get_chunk( stream_t *s, const bool wait )
589 stream_sys_t *p_sys = s->p_sys;
591 chunk_t *chunk = NULL;
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 )
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
601 if( !wait || p_sys->b_error )
603 vlc_mutex_unlock( &p_sys->download.lock_wait );
604 msg_Warn( s, "get_chunk failed! (playback index %u)",
605 p_sys->playback.index );
610 vlc_mutex_unlock( &p_sys->download.lock_wait );
611 msg_Info( s, "No more chunks, end of the VOD" );
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 );
621 chunk = vlc_array_item_at_index( p_sys->download.chunks, p_sys->playback.index );
623 vlc_mutex_unlock( &p_sys->download.lock_wait );
628 static unsigned int sms_Read( stream_t *s, uint8_t *p_read, unsigned int i_read )
630 stream_sys_t *p_sys = s->p_sys;
631 unsigned int copied = 0;
632 chunk_t *chunk = NULL;
636 chunk = get_chunk( s, true );
640 if( chunk->read_pos >= chunk->size )
642 if( chunk->type == VIDEO_ES ||
643 ( !SMS_GET_SELECTED_ST( VIDEO_ES ) && chunk->type == AUDIO_ES ) )
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);
650 if( !p_sys->b_cache || p_sys->b_live )
652 FREENULL( chunk->data );
658 p_sys->playback.index += 1;
659 msg_Dbg( s, "Incrementing playback index" );
664 if( chunk->read_pos == 0 )
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 */
671 uint8_t *slice = chunk->data;
672 SMS_GET4BYTES( type );
673 SMS_GETFOURCC( type );
674 assert( type == ATOM_moof || type == ATOM_uuid );
678 uint8_t *src = chunk->data + chunk->read_pos;
679 if( i_read <= chunk->size - chunk->read_pos )
682 len = chunk->size - chunk->read_pos;
686 if( p_read ) /* otherwise caller skips data */
687 memcpy( p_read + copied, src, len );
688 chunk->read_pos += len;
694 } while ( i_read > 0 );
699 static int Read( stream_t *s, void *buffer, unsigned i_read )
701 stream_sys_t *p_sys = s->p_sys;
703 i_read = __MIN(INT_MAX, i_read);
708 length = sms_Read( s, (uint8_t*) buffer, i_read );
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 );
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 );
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 )
726 chunk_t *chunk = get_chunk( s, true );
727 if( !chunk || !chunk->data )
730 int bytes = chunk->size - chunk->read_pos;
733 if( (unsigned)bytes < i_peek )
735 msg_Err( s, "could not peek %u bytes, only %i!", i_peek, bytes );
737 msg_Dbg( s, "peeking at chunk %u!", chunk->sequence );
738 *pp_peek = chunk->data + chunk->read_pos;
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 )
758 stream_sys_t *p_sys = s->p_sys;
760 if( pos == p_sys->playback.boffset )
763 chunk_t *chunk = get_chunk( s, false );
767 bool inside_chunk = pos >= chunk->offset &&
768 pos < (chunk->offset + chunk->size) ? true : false;
772 chunk->read_pos = pos - chunk->offset;
773 p_sys->playback.boffset = pos;
780 msg_Err( s, "Cannot seek outside the current chunk for a live stream" );
784 msg_Info( s, "Seeking outside the current chunk" );
785 assert( pos <= FAKE_STREAM_SIZE );
787 vlc_mutex_lock( &p_sys->download.lock_wait );
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;
795 vlc_cond_signal( &p_sys->download.wait);
796 vlc_mutex_unlock( &p_sys->download.lock_wait );
802 static int Control( stream_t *s, int i_query, va_list args )
804 stream_sys_t *p_sys = s->p_sys;
808 case STREAM_CAN_SEEK:
809 *(va_arg( args, bool * )) = true;
811 case STREAM_CAN_FASTSEEK:
812 *(va_arg( args, bool * )) = false;
814 case STREAM_CAN_PAUSE: /* TODO */
815 case STREAM_CAN_CONTROL_PACE:
816 *(va_arg( args, bool * )) = !p_sys->b_live;
818 case STREAM_GET_POSITION:
819 *(va_arg( args, uint64_t * )) = p_sys->playback.boffset;
821 case STREAM_SET_POSITION:
823 uint64_t pos = (uint64_t)va_arg(args, uint64_t);
824 int ret = chunk_Seek(s, pos);
825 if( ret == VLC_SUCCESS )
830 case STREAM_GET_SIZE:
831 *(va_arg( args, uint64_t * )) = FAKE_STREAM_SIZE;
833 case STREAM_GET_PTS_DELAY:
834 *va_arg (args, int64_t *) = INT64_C(1000) *
835 var_InheritInteger(s, "network-caching");
837 case STREAM_SET_PAUSE_STATE:
838 return (p_sys->b_live) ? VLC_EGENERIC : VLC_SUCCESS;