[vlc-devel] [PATCH] Add Smooth Streaming module
Frédéric Yhuel
fyhuel at viotech.net
Wed Jul 18 17:27:20 CEST 2012
Test it:
***** Live *****
- http://demo.anevia.com:3128/live/disk1/live1/ss-ss/manifest
- http://demo.anevia.com:3129/live/disk1/live2/ss-ss/manifest
***** VOD *****
- http://demo.anevia.com:3131/vod/disk1/content2/ss-ss/Manifest
- http://demo.anevia.com:3130/vod/disk1/content1/ss-ss/Manifest
This one is WVC1/WMAP content, it kinda works:
- http://mediadl.microsoft.com/mediadl/iisnet/smoothmedia/Experience/BigBuckBunny_720p.ism/Manifest
---
modules/stream_filter/Modules.am | 3 +-
modules/stream_filter/smooth/downloader.c | 626 ++++++++++++++++-------------
modules/stream_filter/smooth/smooth.c | 614 ++++++++++++++--------------
modules/stream_filter/smooth/smooth.h | 58 +--
modules/stream_filter/smooth/utils.c | 56 ++-
5 files changed, 741 insertions(+), 616 deletions(-)
diff --git a/modules/stream_filter/Modules.am b/modules/stream_filter/Modules.am
index 6d8d94a..e14f84e 100644
--- a/modules/stream_filter/Modules.am
+++ b/modules/stream_filter/Modules.am
@@ -1,5 +1,4 @@
-SUBDIRS = dash
-SUBDIRS = smooth
+SUBDIRS = dash smooth
SOURCES_decomp = decomp.c
SOURCES_stream_filter_record = record.c
diff --git a/modules/stream_filter/smooth/downloader.c b/modules/stream_filter/smooth/downloader.c
index 37c2ff4..66dee81 100644
--- a/modules/stream_filter/smooth/downloader.c
+++ b/modules/stream_filter/smooth/downloader.c
@@ -35,11 +35,10 @@
static char *ConstructUrl( const char *template, const char *base_url,
uint64_t bandwidth, uint64_t start_time )
{
- char *frag;
- char *end;
- char *qual;
+ char *frag, *end, *qual;
char *url_template = strdup( template );
char *saveptr = NULL;
+
qual = strtok_r( url_template, "{", &saveptr );
strtok_r( NULL, "}", &saveptr );
frag = strtok_r( NULL, "{", &saveptr );
@@ -49,7 +48,9 @@ static char *ConstructUrl( const char *template, const char *base_url,
if( asprintf( &url, "%s/%s%"PRIu64"%s%"PRIu64"%s", base_url, qual,
bandwidth, frag, start_time, end) < 0 )
+ {
return NULL;
+ }
free( url_template );
return url;
@@ -57,119 +58,130 @@ static char *ConstructUrl( const char *template, const char *base_url,
static chunk_t * chunk_Get( sms_stream_t *sms, int64_t start_time )
{
- vlc_mutex_lock( &sms->lock );
int len = vlc_array_count( sms->chunks );
for( int i = 0; i < len; i++ )
{
chunk_t * chunk = vlc_array_item_at_index( sms->chunks, i );
+ if( !chunk ) return NULL;
+
if( chunk->start_time <= start_time &&
chunk->start_time + chunk->duration > start_time )
{
- vlc_mutex_unlock( &sms->lock );
return chunk;
}
}
- vlc_mutex_unlock( &sms->lock );
return NULL;
}
+static unsigned set_track_id( chunk_t *chunk, unsigned tid )
+{
+ uint32_t size, type;
+ assert( chunk->data );
+ uint8_t *slice = chunk->data->p_buffer;
+ assert( slice );
+
+ SMS_GET4BYTES( size );
+ SMS_GETFOURCC( type );
+ assert( type == ATOM_moof );
+
+ SMS_GET4BYTES( size );
+ SMS_GETFOURCC( type );
+ assert( type == ATOM_mfhd );
+ slice += size - 8;
+
+ SMS_GET4BYTES( size );
+ SMS_GETFOURCC( type );
+ assert( type == ATOM_traf );
+
+ SMS_GET4BYTES( size );
+ SMS_GETFOURCC( type );
+ assert( type == ATOM_tfhd );
+
+ unsigned ret = bswap32( ((uint32_t *)slice)[1] );
+ ((uint32_t *)slice)[1] = bswap32( tid );
+
+ return ret;
+}
+
static int sms_Download( stream_t *s, chunk_t *chunk, char *url )
{
- assert( chunk );
stream_sys_t *p_sys = s->p_sys;
- msg_Dbg( s, "chunk url is %s\n", url );
- if( url == NULL )
- return VLC_ENOMEM;
-
stream_t *p_ts = stream_UrlNew( s, url );
free( url );
if( p_ts == NULL )
return VLC_EGENERIC;
- chunk->size = stream_Size( p_ts );
- assert( chunk->size > 0 );
+ int64_t size = stream_Size( p_ts );
+
+ chunk->size = size;
+ chunk->offset = p_sys->download.next_chunk_offset;
+ p_sys->download.next_chunk_offset += chunk->size;
+
+ chunk->data = block_Alloc( size );
- chunk->data = block_Alloc( chunk->size );
if( chunk->data == NULL )
{
stream_Delete( p_ts );
return VLC_ENOMEM;
}
- msg_Dbg( s, "sms_Download: gonna dl %i bytes", (int)chunk->size );
-
- enum{ BLOCK_SIZE = 8192 };
- int len, read = 0;
- int to_read = chunk->size;
- uint8_t *dst = chunk->data->p_buffer;
- int64_t size;
- while( to_read > 0 && vlc_object_alive( s ) )
+ int read = stream_Read( p_ts, chunk->data->p_buffer, size );
+ if( read < size )
{
- size = stream_Size( p_ts );
- if( size != chunk->size )
- {
- msg_Err( s, "chunk size changed! old is %"PRIi64", new is %"PRIi64,
- chunk->size, size );
- return VLC_EGENERIC;
- }
- len = __MIN( to_read, BLOCK_SIZE );
- read = stream_Read( p_ts, dst, len );
- if( read < len )
- {
- msg_Err( s, "sms_Download: I requested %i bytes, "\
- "but I got only %i", len, read );
- return VLC_EGENERIC;
- }
- dst += len;
- to_read -= len;
+ msg_Warn( s, "sms_Download: I requested %"PRIi64" bytes, "\
+ "but I got only %i", size, read );
+ chunk->data = block_Realloc( chunk->data, 0, read );
}
- msg_Dbg( s, "sms_Download: I dld %i bytes", read );
+
+ stream_Delete( p_ts );
vlc_mutex_lock( &p_sys->download.lock_wait );
- vlc_array_append( p_sys->download.dld_chunks, chunk );
- vlc_mutex_unlock( &p_sys->download.lock_wait );
+
if( chunk->type == AUDIO_ES )
p_sys->download.alead += chunk->duration;
else if( chunk->type == VIDEO_ES )
p_sys->download.vlead += chunk->duration;
else if( chunk->type == SPU_ES )
p_sys->download.tlead += chunk->duration;
- stream_Delete( p_ts );
+
+ vlc_mutex_unlock( &p_sys->download.lock_wait );
return VLC_SUCCESS;
}
#ifdef DISABLE_BANDWIDTH_ADAPTATION
-static uint16_t BandwidthAdaptation( stream_t *s,
+static unsigned BandwidthAdaptation( stream_t *s,
sms_stream_t *sms, uint64_t *bandwidth )
{
return sms->download_qlvl;
}
#else
-static uint16_t BandwidthAdaptation( stream_t *s,
- sms_stream_t *sms, uint64_t *bandwidth )
+static unsigned BandwidthAdaptation( stream_t *s,
+ sms_stream_t *sms, uint64_t bandwidth )
{
if( sms->type != VIDEO_ES )
return sms->download_qlvl;
+
uint64_t bw_candidate = 0;
quality_level_t *qlevel;
- uint16_t ret = sms->download_qlvl;
- msg_Dbg( s, "bw is %"PRIu64"", *bandwidth );
+ unsigned ret = sms->download_qlvl;
+ //msg_Dbg( s, "bw is %"PRIu64"", *bandwidth );
for( unsigned i = 0; i < sms->qlevel_nb; i++ )
{
qlevel = vlc_array_item_at_index( sms->qlevels, i );
- if( qlevel->Bitrate < (*bandwidth - *bandwidth / 3) &&
+ assert( qlevel );
+
+ if( qlevel->Bitrate < (bandwidth - bandwidth / 3) &&
qlevel->Bitrate > bw_candidate )
{
bw_candidate = qlevel->Bitrate;
ret = qlevel->id;
}
}
- if( bw_candidate )
- *bandwidth = bw_candidate;
+
return ret;
}
#endif
@@ -179,17 +191,19 @@ static int get_new_chunks( stream_t *s, chunk_t *ck )
stream_sys_t *p_sys = s->p_sys;
uint8_t *slice = ck->data->p_buffer;
+ assert( slice );
uint8_t version, fragment_count;
uint32_t size, type, flags;
sms_stream_t *sms;
+ UUID_t uuid;
+ TfrfBoxDataFields_t *tfrf_df;
+
if( ck->type == AUDIO_ES )
sms = p_sys->astream;
else if ( ck->type == VIDEO_ES )
sms = p_sys->vstream;
else
return 0;
- UUID_t uuid;
- TfrfBoxDataFields_t *tfrf_df;
SMS_GET4BYTES( size );
SMS_GETFOURCC( type );
@@ -203,164 +217,147 @@ static int get_new_chunks( stream_t *s, chunk_t *ck )
SMS_GET4BYTES( size );
SMS_GETFOURCC( type );
assert( type == ATOM_traf );
+
for(;;)
{
- if( slice > ck->data->p_buffer + ck->data->i_buffer - 8 )
- {
- msg_Err( s, "No uuid box found :-(" );
- return VLC_EGENERIC;
- }
SMS_GET4BYTES( size );
assert( size > 1 );
SMS_GETFOURCC( type );
- if( type == ATOM_uuid )
- break;
- else
- slice += size - 8;
- }
-
- GetUUID( &uuid, slice);
- slice += 16;
- if( !CmpUUID( &uuid, &TfrfBoxUUID ) )
- {
- SMS_GET1BYTE( version );
- SMS_GET3BYTES( flags );
-
- SMS_GET1BYTE( fragment_count );
- tfrf_df = calloc( fragment_count, sizeof( TfrfBoxDataFields_t ) );
- if( unlikely( tfrf_df == NULL ) )
- return VLC_EGENERIC;
- for( uint8_t i = 0; i < fragment_count; i++ )
+ if( type == ATOM_mdat )
{
- SMS_GET4or8BYTES( tfrf_df[i].i_fragment_abs_time );
- SMS_GET4or8BYTES( tfrf_df[i].i_fragment_duration );
+ msg_Err( s, "No uuid box found :-(" );
+ return VLC_EGENERIC;
}
-
- msg_Dbg( s, "read box: \"tfrf\" version %d, flags 0x%x, "\
- "fragment count %"PRIu8, version, flags, fragment_count );
-
- for( uint8_t i = 0; i < fragment_count; i++ )
+ else if( type == ATOM_uuid )
{
- int64_t dur = tfrf_df[i].i_fragment_duration;
- int64_t stime = tfrf_df[i].i_fragment_abs_time;
- msg_Dbg( s, "\"tfrf\" fragment duration %"PRIu64", "\
- "fragment abs time %"PRIu64, dur, stime);
-
- if( !chunk_Get( sms, stime ) )
- chunk_New( sms, dur, stime );
+ GetUUID( &uuid, slice);
+ if( !CmpUUID( &uuid, &TfrfBoxUUID ) )
+ break;
}
- free( tfrf_df );
-
+ slice += size - 8;
}
- else if( !CmpUUID( &uuid, &TfxdBoxUUID ) )
- {
- msg_Err( s, "TfxdBox parsing no yet implemented" );
+
+ slice += 16;
+ SMS_GET1BYTE( version );
+ SMS_GET3BYTES( flags );
+ SMS_GET1BYTE( fragment_count );
+
+ tfrf_df = calloc( fragment_count, sizeof( TfrfBoxDataFields_t ) );
+ if( unlikely( tfrf_df == NULL ) )
return VLC_EGENERIC;
- }
- else
+
+ for( uint8_t i = 0; i < fragment_count; i++ )
{
- msg_Err( s, "Unknown uuid box :-(" );
- return VLC_EGENERIC;
+ SMS_GET4or8BYTES( tfrf_df[i].i_fragment_abs_time );
+ SMS_GET4or8BYTES( tfrf_df[i].i_fragment_duration );
}
- return VLC_SUCCESS;
-}
+ msg_Dbg( s, "read box: \"tfrf\" version %d, flags 0x%x, "\
+ "fragment count %"PRIu8, version, flags, fragment_count );
-/* Set track ID */
-static uint16_t set_track_id( chunk_t *chunk, uint32_t tid )
-{
- uint8_t *slice = chunk->data->p_buffer;
- uint32_t type;
- for( size_t stop = chunk->data->i_buffer; stop > 0; )
+ for( uint8_t i = 0; i < fragment_count; i++ )
{
- slice = memchr( slice , 't', stop );
- if( slice == NULL )
- return 0;
- SMS_GETFOURCC( type );
- if( type == ATOM_tfhd )
- break;
- else
- {
- slice = __MAX( slice - 4 + 1, chunk->data->p_buffer + 1 );
- stop = (size_t)chunk->data->i_buffer -
- (size_t)(slice - chunk->data->p_buffer);
- }
+ int64_t dur = tfrf_df[i].i_fragment_duration;
+ int64_t stime = tfrf_df[i].i_fragment_abs_time;
+ msg_Dbg( s, "\"tfrf\" fragment duration %"PRIu64", "\
+ "fragment abs time %"PRIu64, dur, stime);
+
+ if( !chunk_Get( sms, stime + dur ) )
+ chunk_New( sms, dur, stime );
}
- if( slice > chunk->data->p_buffer + chunk->data->i_buffer - 12 )
- return 0;
+ free( tfrf_df );
- uint32_t ret = bswap32( ((uint32_t *)slice)[1] );
- ((uint32_t *)slice)[1] = bswap32( tid );
- return ret;
+ return VLC_SUCCESS;
}
-#define STRA_SIZE 312
-#define SMOO_SIZE (STRA_SIZE * 3 + 8)
+#define STRA_SIZE 338
+#define SMOO_SIZE (STRA_SIZE * 3 + 24) /* 1038 */
+
+/* SmooBox is a very simple MP4 box, used only to pass information
+ * to the demux layer. As this box is not aimed to travel accross networks,
+ * simplicity of the design is better than compactness */
static int build_smoo_box( stream_t *s, uint8_t *smoo_box )
{
stream_sys_t *p_sys = s->p_sys;
- sms_stream_t *sms;
+ sms_stream_t *sms = NULL;
+ bool disable_audio = var_InheritBool( s, "smooth-disable-audio" );
/* smoo */
memset( smoo_box, 0, SMOO_SIZE );
smoo_box[2] = (SMOO_SIZE & 0xff00)>>8;
smoo_box[3] = SMOO_SIZE & 0xff;
- smoo_box[4] = 's';
- smoo_box[5] = 'm';
- smoo_box[6] = 'o';
- smoo_box[7] = 'o';
+ smoo_box[4] = 'u';
+ smoo_box[5] = 'u';
+ smoo_box[6] = 'i';
+ smoo_box[7] = 'd';
+
+ /* UUID is e1da72ba-24d7-43c3-a6a5-1b5759a1a92c */
+ ((uint32_t *)smoo_box)[2] = bswap32( 0xe1da72ba );
+ ((uint32_t *)smoo_box)[3] = bswap32( 0x24d743c3 );
+ ((uint32_t *)smoo_box)[4] = bswap32( 0xa6a51b57 );
+ ((uint32_t *)smoo_box)[5] = bswap32( 0x59a1a92c );
uint8_t *stra_box;
for( int i = 0; i < 3; i++ )
{
+ sms = NULL;
stra_box = smoo_box + i * STRA_SIZE;
- stra_box[10] = (STRA_SIZE & 0xff00)>>8;
- stra_box[11] = STRA_SIZE & 0xff;
- stra_box[12] = 's';
- stra_box[13] = 't';
- stra_box[14] = 'r';
- stra_box[15] = 'a';
+ stra_box[26] = (STRA_SIZE & 0xff00)>>8;
+ stra_box[27] = STRA_SIZE & 0xff;
+ stra_box[28] = 'u';
+ stra_box[29] = 'u';
+ stra_box[30] = 'i';
+ stra_box[31] = 'd';
+
+ /* UUID is b03ef770-33bd-4bac-96c7-bf25f97e2447 */
+ ((uint32_t *)stra_box)[8] = bswap32( 0xb03ef770 );
+ ((uint32_t *)stra_box)[9] = bswap32( 0x33bd4bac );
+ ((uint32_t *)stra_box)[10] = bswap32( 0x96c7bf25 );
+ ((uint32_t *)stra_box)[11] = bswap32( 0xf97e2447 );
if( i == 0)
{
- stra_box[16] = VIDEO_ES;
+ stra_box[48] = VIDEO_ES;
sms = p_sys->vstream;
}
else if( i == 1 )
{
- stra_box[16] = AUDIO_ES;
- sms = p_sys->astream;
+ stra_box[48] = AUDIO_ES;
+ if( !disable_audio )
+ sms = p_sys->astream;
}
else if( i == 2 )
{
- stra_box[16] = SPU_ES;
+ stra_box[48] = SPU_ES;
sms = p_sys->tstream;
}
- stra_box[17] = 0; /* reserved */
+ stra_box[49] = 0; /* reserved */
if( sms == NULL )
continue;
- stra_box[18] = (sms->id & 0xff00)>>8;
- stra_box[19] = sms->id & 0xff;
+ stra_box[50] = (sms->id & 0xff00)>>8;
+ stra_box[51] = sms->id & 0xff;
- ((uint32_t *)stra_box)[5] = bswap32( sms->timescale );
+ ((uint32_t *)stra_box)[13] = bswap32( sms->timescale );
+ ((uint64_t *)stra_box)[7] = bswap64( p_sys->vod_duration );
quality_level_t * qlvl = get_qlevel( sms, sms->download_qlvl );
- ((uint32_t *)stra_box)[6] = bswap32( qlvl->FourCC );
- ((uint32_t *)stra_box)[7] = bswap32( qlvl->Bitrate );
- ((uint32_t *)stra_box)[8] = bswap32( qlvl->MaxWidth );
- ((uint32_t *)stra_box)[9] = bswap32( qlvl->MaxHeight );
- ((uint32_t *)stra_box)[10] = bswap32( qlvl->SamplingRate );
- ((uint32_t *)stra_box)[11] = bswap32( qlvl->Channels );
- ((uint32_t *)stra_box)[12] = bswap32( qlvl->BitsPerSample );
- ((uint32_t *)stra_box)[13] = bswap32( qlvl->PacketSize );
- ((uint32_t *)stra_box)[14] = bswap32( qlvl->AudioTag );
-
- stra_box[60] = stra_box[61] = stra_box[62] = 0; /* reserved */
- stra_box[63] = strlen( qlvl->CodecPrivateData );
- char *dst = (char *)(stra_box + 64);
+ ((uint32_t *)stra_box)[16] = bswap32( qlvl->FourCC );
+ ((uint32_t *)stra_box)[17] = bswap32( qlvl->Bitrate );
+ ((uint32_t *)stra_box)[18] = bswap32( qlvl->MaxWidth );
+ ((uint32_t *)stra_box)[19] = bswap32( qlvl->MaxHeight );
+ ((uint32_t *)stra_box)[20] = bswap32( qlvl->SamplingRate );
+ ((uint32_t *)stra_box)[21] = bswap32( qlvl->Channels );
+ ((uint32_t *)stra_box)[22] = bswap32( qlvl->BitsPerSample );
+ ((uint32_t *)stra_box)[23] = bswap32( qlvl->PacketSize );
+ ((uint32_t *)stra_box)[24] = bswap32( qlvl->AudioTag );
+ ((uint16_t *)stra_box)[50] = bswap16( qlvl->nBlockAlign );
+
+ stra_box[102] = stra_box[103] = stra_box[104] = 0; /* reserved */
+ stra_box[105] = strlen( qlvl->CodecPrivateData );
+ char *dst = (char *)(stra_box + 106);
strncpy( dst, qlvl->CodecPrivateData, 256 );
}
@@ -372,31 +369,36 @@ static chunk_t *build_init_chunk( stream_t *s )
chunk_t *ret = calloc( 1, sizeof( chunk_t ) );
if( unlikely( ret == NULL ) )
return NULL;
- vlc_mutex_init( &ret->lock );
+
ret->size = SMOO_SIZE;
ret->data = block_Alloc( SMOO_SIZE );
if( !ret->data )
return NULL;
+
build_smoo_box( s, ret->data->p_buffer );
return ret;
}
-static int Download( stream_t *s, sms_stream_t *sms, uint64_t *next_chunk_offset )
+static int Download( stream_t *s, sms_stream_t *sms )
{
stream_sys_t *p_sys = s->p_sys;
- assert( sms );
-
+ static unsigned bw_count = 0;
int64_t start_time;
+
if( sms->type == AUDIO_ES )
start_time = p_sys->download.alead;
else if ( sms->type == VIDEO_ES )
start_time = p_sys->download.vlead;
else
+ {
return VLC_EGENERIC;
+ }
quality_level_t *qlevel = get_qlevel( sms, sms->download_qlvl );
+ assert( qlevel );
+
chunk_t *chunk = chunk_Get( sms, start_time );
if( !chunk )
{
@@ -404,29 +406,26 @@ static int Download( stream_t *s, sms_stream_t *sms, uint64_t *next_chunk_offset
"start time = %"PRIu64"", sms->name, start_time );
return VLC_EGENERIC;
}
-
- vlc_mutex_lock( &chunk->lock );
if( chunk->data != NULL )
{
/* Segment already downloaded */
- vlc_mutex_unlock( &chunk->lock );
+ msg_Warn( s, "Segment already downloaded" );
return VLC_SUCCESS;
}
chunk->type = sms->type;
- char *url = ConstructUrl( sms->url_template,
- p_sys->base_url,
- qlevel->Bitrate,
- chunk->start_time );
+
+ char *url = ConstructUrl( sms->url_template, p_sys->base_url,
+ qlevel->Bitrate, chunk->start_time );
/* sanity check - can we download this chunk on time? */
uint64_t avg_bw = sms_queue_avg( p_sys->bws );
if( (avg_bw > 0) && (qlevel->Bitrate > 0) )
{
/* duration in ms */
- uint32_t chunk_duration = chunk->duration * 1000 / sms->timescale;
+ unsigned chunk_duration = chunk->duration * 1000 / sms->timescale;
uint64_t size = chunk_duration * qlevel->Bitrate / 1000; /* bits */
- uint32_t estimated = (uint32_t)(size * 1000 / avg_bw);
+ unsigned estimated = size * 1000 / avg_bw;
if( estimated > chunk_duration )
{
msg_Warn( s,"downloading of chunk %d would take %d ms, "\
@@ -438,61 +437,87 @@ static int Download( stream_t *s, sms_stream_t *sms, uint64_t *next_chunk_offset
mtime_t start = mdate();
if( sms_Download( s, chunk, url ) != VLC_SUCCESS )
{
- msg_Err( s, "downloaded chunk %"PRIu32" from stream %s at quality\
- %"PRIu32" failed", chunk->sequence, sms->name, qlevel->Bitrate );
- vlc_mutex_unlock( &chunk->lock );
+ msg_Err( s, "downloaded chunk %u from stream %s at quality\
+ %u failed", chunk->sequence, sms->name, qlevel->Bitrate );
return VLC_EGENERIC;
}
mtime_t duration = mdate() - start;
- chunk->offset = *next_chunk_offset;
- *next_chunk_offset += chunk->size;
- if( p_sys->b_live )
- get_new_chunks( s, chunk );
- uint16_t real_id = set_track_id( chunk, sms->id );
+ unsigned real_id = set_track_id( chunk, sms->id );
if( real_id == 0)
{
- vlc_mutex_unlock( &chunk->lock );
msg_Err( s, "tfhd box not found or invalid chunk" );
return VLC_EGENERIC;
}
- msg_Dbg( s, "chunk ID was %i and is now %i", real_id, sms->id );
- vlc_mutex_unlock( &chunk->lock );
- msg_Dbg( s, "downloaded chunk %d from stream %s at quality %"PRIu32"",
+ //msg_Dbg( s, "chunk ID was %i and is now %i", real_id, sms->id );
+
+ if( p_sys->b_live )
+ get_new_chunks( s, chunk );
+
+ vlc_mutex_lock( &p_sys->download.lock_wait );
+ vlc_array_append( p_sys->download.chunks, chunk );
+ vlc_cond_signal( &p_sys->download.wait );
+ vlc_mutex_unlock( &p_sys->download.lock_wait );
+
+ msg_Info( s, "downloaded chunk %d from stream %s at quality %u",
chunk->sequence, sms->name, qlevel->Bitrate );
+
+ uint64_t actual_lead = chunk->start_time + chunk->duration;
if( sms->type == AUDIO_ES )
- p_sys->download.aindex++;
+ {
+ p_sys->download.aindex = chunk->sequence;
+ p_sys->download.alead = __MIN( p_sys->download.alead, actual_lead );
+ }
else if( sms->type == VIDEO_ES )
- p_sys->download.vindex++;
+ {
+ p_sys->download.vindex = chunk->sequence;
+ p_sys->download.vlead = __MIN( p_sys->download.vlead, actual_lead );
+ p_sys->playback.toffset = __MIN( p_sys->playback.toffset, (uint64_t)chunk->start_time );
+ }
else if( sms->type == SPU_ES )
- p_sys->download.sindex++;
-
- if( sms->type != VIDEO_ES )
- return VLC_SUCCESS;
+ {
+ p_sys->download.sindex = chunk->sequence;
+ p_sys->download.tlead = __MIN( p_sys->download.tlead, actual_lead );
+ }
unsigned dur_ms = __MAX( 1, duration / 1000 );
uint64_t bw = chunk->size * 8 * 1000 / dur_ms; /* bits / s */
sms_queue_put( p_sys->bws, bw );
avg_bw = sms_queue_avg( p_sys->bws );
- if( ( (qlevel->Bitrate < avg_bw - avg_bw / 3) || (qlevel->Bitrate > avg_bw) )
- && chunk->sequence > 1 )
+
+ if( sms->type != VIDEO_ES )
+ return VLC_SUCCESS;
+
+ /* Track could get disabled in mp4 demux if we trigger adaption too soon. */
+ if( chunk->sequence <= 1 )
+ return VLC_SUCCESS;
+
+ unsigned new_qlevel_id = BandwidthAdaptation( s, sms, avg_bw );
+ quality_level_t *new_qlevel = get_qlevel( sms, new_qlevel_id );
+ assert( new_qlevel );
+
+ fprintf( p_sys->bw_record, "%u %"PRIu64"\n", bw_count, bw );
+ fprintf( p_sys->avg_bw_rcd, "%u %"PRIu64"\n", bw_count, avg_bw );
+ fprintf( p_sys->ql_lvl_rcd, "%u %u\n", bw_count, new_qlevel->Bitrate );
+ bw_count++;
+
+ if( new_qlevel->Bitrate != qlevel->Bitrate )
{
- uint16_t new_qlevel_id = BandwidthAdaptation( s, sms, &avg_bw );
- quality_level_t *new_qlevel = get_qlevel( sms, new_qlevel_id );
+ msg_Warn( s, "detected %s bandwidth (%u) stream",
+ (new_qlevel->Bitrate >= qlevel->Bitrate) ? "faster" : "lower", new_qlevel->Bitrate );
- /* FIXME: we need an average here */
- if( (new_qlevel) && (new_qlevel->Bitrate != qlevel->Bitrate) )
- {
- msg_Warn( s, "detected %s bandwidth (%"PRIu64") stream",
- (avg_bw >= qlevel->Bitrate) ? "faster" : "lower", avg_bw );
- sms->download_qlvl = new_qlevel_id;
+ sms->download_qlvl = new_qlevel_id;
- chunk_t *new_init_ck = build_init_chunk( s );
- vlc_mutex_lock( &p_sys->download.lock_wait );
- vlc_array_append( p_sys->download.dld_chunks, new_init_ck );
- vlc_mutex_unlock( &p_sys->download.lock_wait );
- }
+ chunk_t *new_init_ck = build_init_chunk( s );
+ assert( new_init_ck );
+
+ new_init_ck->offset = p_sys->download.next_chunk_offset;
+ p_sys->download.next_chunk_offset += new_init_ck->size;
+
+ vlc_mutex_lock( &p_sys->download.lock_wait );
+ vlc_array_append( p_sys->download.chunks, new_init_ck );
+ vlc_mutex_unlock( &p_sys->download.lock_wait );
}
return VLC_SUCCESS;
}
@@ -501,127 +526,176 @@ void* sms_Thread( void *p_this )
{
stream_t *s = (stream_t *)p_this;
stream_sys_t *p_sys = s->p_sys;
- int64_t lead=0, time_left=0;
- uint64_t next_chunk_offset = 0;
- sms_stream_t *vsms, *asms;
- vsms = p_sys->vstream;
- asms = p_sys->astream;
- assert( vsms );
- assert( asms );
+
+ const char *bw_record_path = "/tmp/smooth_bw_record.dat";
+ const char *ql_lvl_rcd_path = "/tmp/smooth_ql_lvl_rcd.dat";
+ const char *avg_bw_rcd_path = "/tmp/smooth_avg_bw_rcd.dat";
+ p_sys->bw_record = fopen( bw_record_path, "w" );
+ p_sys->ql_lvl_rcd = fopen( ql_lvl_rcd_path, "w" );
+ p_sys->avg_bw_rcd = fopen( avg_bw_rcd_path, "w" );
+ assert( p_sys->bw_record );
+ assert( p_sys->ql_lvl_rcd );
+ assert( p_sys->avg_bw_rcd );
+
+ sms_stream_t *vsms = p_sys->vstream;
+ sms_stream_t *asms = p_sys->astream;
+ assert( vsms ); assert( asms );
/* We compute the average bandwidth of the 4 last downloaded
* chunks, but feel free to replace '4' by whatever you wish */
p_sys->bws = sms_queue_init( 4 );
chunk_t *init_ck = build_init_chunk( s );
+ assert( init_ck );
+
vlc_mutex_lock( &p_sys->download.lock_wait );
- vlc_array_append( p_sys->download.dld_chunks, init_ck );
+ vlc_array_append( p_sys->download.chunks, init_ck );
vlc_mutex_unlock( &p_sys->download.lock_wait );
+ p_sys->download.next_chunk_offset = init_ck->size;
+
int canc = vlc_savecancel();
- vlc_mutex_lock( &asms->lock );
- vlc_mutex_lock( &vsms->lock );
- chunk_t *third_video_chunk = vlc_array_item_at_index( vsms->chunks, 0 );
- chunk_t *third_audio_chunk = vlc_array_item_at_index( asms->chunks, 0 );
+ chunk_t *video_chunk = vlc_array_item_at_index( vsms->chunks, 0 );
+ chunk_t *audio_chunk = vlc_array_item_at_index( asms->chunks, 0 );
+ assert( video_chunk ); assert( audio_chunk );
/* Sometimes, the video stream is cut into pieces of one exact length,
* while the audio stream fragments can't be made to match exactly,
* and for some reason the n^th advertised video fragment is related to
* the n+1^th advertised audio chunk or vice versa */
- int64_t amid = third_audio_chunk->duration / 2;
- int64_t vmid = third_video_chunk->duration / 2;
+ int64_t amid = audio_chunk->duration / 2;
+ int64_t vmid = video_chunk->duration / 2;
- if( third_audio_chunk->start_time > third_video_chunk->start_time + vmid )
+ if( audio_chunk->start_time > video_chunk->start_time + vmid )
{
- third_video_chunk = vlc_array_item_at_index( vsms->chunks, 1 );
+ video_chunk = vlc_array_item_at_index( vsms->chunks, 1 );
}
- else if ( third_video_chunk->start_time >
- third_audio_chunk->start_time + amid )
+ else if ( video_chunk->start_time > audio_chunk->start_time + amid )
{
- third_audio_chunk = vlc_array_item_at_index( asms->chunks, 1 );
+ audio_chunk = vlc_array_item_at_index( asms->chunks, 1 );
}
- vlc_mutex_unlock( &asms->lock );
- vlc_mutex_unlock( &vsms->lock );
- bool disable_audio = var_CreateGetBool( s, "smooth-disable-audio" );
+
+ bool disable_audio = var_InheritBool( s, "smooth-disable-audio" );
if( p_sys->b_live )
{
- p_sys->download.vlead = third_video_chunk->start_time +
- p_sys->timescale / 1000;
+ p_sys->download.vlead = video_chunk->start_time + p_sys->timescale / 1000;
if( disable_audio )
p_sys->download.alead = UINT64_MAX;
else
- p_sys->download.alead = third_audio_chunk->start_time +
- p_sys->timescale / 1000;
+ p_sys->download.alead = audio_chunk->start_time + p_sys->timescale / 1000;
}
- if( Download( s, vsms, &next_chunk_offset ) != VLC_SUCCESS )
+ if( Download( s, vsms ) != VLC_SUCCESS )
{
- p_sys->b_error = true;
goto cancel;
}
if( !disable_audio )
{
- if( Download( s, asms, &next_chunk_offset ) != VLC_SUCCESS )
- {
- p_sys->b_error = true;
+ if( Download( s, asms ) != VLC_SUCCESS )
goto cancel;
- }
}
- while( vlc_object_alive( s ) )
+ int64_t lead = 0;
+
+ while( 1 )
{
- /* Is there a new chunk to process? */
- if( p_sys->download.aindex >= (asms->vod_chunks_nb - 1) &&
- p_sys->download.vindex >= (vsms->vod_chunks_nb - 1) &&
- !p_sys->b_live )
- break;
- time_left = p_sys->vod_duration - p_sys->playback.toffset;
- if( time_left > 60 * p_sys->timescale || p_sys->b_live )
+ /* XXX replace magic number 20 by a value depending on
+ * LookAheadFragmentCount and DVRWindowLength */
+ vlc_mutex_lock( &p_sys->download.lock_wait );
+
+ if( p_sys->b_close )
{
- /* wait */
- vlc_mutex_lock( &p_sys->download.lock_wait );
- if( p_sys->download.vlead >= p_sys->playback.toffset )
- lead = p_sys->download.vlead - p_sys->playback.toffset;
- else
- lead = 0;
- while( lead > 20 * p_sys->timescale + third_video_chunk->start_time )
- {
- msg_Dbg( s, "sms_Thread is waiting!" );
- msg_Dbg( s, "toffset is %"PRIu64" and vlead is %"PRIu64,
- p_sys->playback.toffset,
- p_sys->download.vlead - third_video_chunk->start_time );
- vlc_cond_timedwait( &p_sys->download.wait,
- &p_sys->download.lock_wait, mdate() + 100000 );
- lead = p_sys->download.vlead - p_sys->playback.toffset;
- if( !vlc_object_alive( s ) )
- break;
- }
vlc_mutex_unlock( &p_sys->download.lock_wait );
+ break;
}
- msg_Dbg( s, "I escaped the waiting cond !!!" );
- if( p_sys->download.alead < p_sys->download.vlead )
- if( Download( s, asms, &next_chunk_offset ) != VLC_SUCCESS )
- {
- p_sys->b_error = true;
- break;
- }
+ lead = __MIN( p_sys->download.vlead, p_sys->download.alead )
+ - p_sys->playback.toffset;
+
+ while( (lead > 10 * p_sys->timescale + video_chunk->start_time) ||
+ /* If there is no new chunk to process, we wait */
+ (
+ !p_sys->b_live &&
+ p_sys->download.aindex >= (asms->vod_chunks_nb -1) &&
+ p_sys->download.vindex >= (vsms->vod_chunks_nb - 1)
+ )
+ )
+ {
+#if 0
+ msg_Info( s, "sms_Thread is waiting!" );
+ msg_Info( s, "toffset is %"PRIu64" vlead is %"PRIu64", alead is %"PRIu64", "\
+ "and lead is %"PRIi64,
+ p_sys->playback.toffset,
+ p_sys->download.vlead - video_chunk->start_time,
+ p_sys->download.alead - video_chunk->start_time,
+ lead );
+#endif
+ vlc_cond_wait( &p_sys->download.wait, &p_sys->download.lock_wait );
+ lead = __MIN( p_sys->download.vlead, p_sys->download.alead )
+ - p_sys->playback.toffset;
- if( p_sys->download.vlead <= p_sys->download.alead )
- if( Download( s, vsms, &next_chunk_offset) != VLC_SUCCESS )
+ if( p_sys->b_close )
+ break;
+ }
+
+ if( p_sys->b_tseek )
+ {
+ int count = vlc_array_count( p_sys->download.chunks );
+ chunk_t *ck = NULL;
+ for( int i = 0; i < count; i++ )
{
- p_sys->b_error = true;
- break;
+ ck = vlc_array_item_at_index( p_sys->download.chunks, i );
+ assert( ck );
+ ck->read_pos = 0;
+ if( ck->data == NULL )
+ continue;
+ block_Release( ck->data );
+ ck->data = NULL;
}
- /* download succeeded */
- vlc_mutex_lock( &p_sys->download.lock_wait );
- vlc_cond_signal( &p_sys->download.wait );
+ vlc_array_destroy( p_sys->download.chunks );
+ p_sys->download.chunks = vlc_array_new();
+
+ p_sys->playback.toffset = p_sys->time_pos;
+ p_sys->download.vlead = p_sys->download.alead = p_sys->time_pos;
+ p_sys->download.aindex = p_sys->download.vindex = 0;
+ p_sys->download.next_chunk_offset = 0;
+
+ p_sys->playback.boffset = 0;
+ p_sys->playback.index = 0;
+
+ chunk_t *new_init_ck = build_init_chunk( s );
+ assert( new_init_ck );
+
+ new_init_ck->offset = p_sys->download.next_chunk_offset;
+ p_sys->download.next_chunk_offset += new_init_ck->size;
+
+ vlc_array_append( p_sys->download.chunks, new_init_ck );
+ p_sys->b_tseek = false;
+ }
vlc_mutex_unlock( &p_sys->download.lock_wait );
+
+ if( p_sys->download.alead < p_sys->download.vlead )
+ {
+ if( Download( s, asms ) != VLC_SUCCESS )
+ break;
+ }
+ else if( p_sys->download.vlead <= p_sys->download.alead )
+ {
+ if( Download( s, vsms ) != VLC_SUCCESS )
+ break;
+ }
+ /* let reader thread breath. That avoids video track
+ * to get de-selected in certain cases */
+ msleep( 100000 );
}
cancel:
+ assert( fclose( p_sys->bw_record ) != EOF );
+ assert( fclose( p_sys->ql_lvl_rcd ) != EOF );
+ assert( fclose( p_sys->avg_bw_rcd ) != EOF );
+ p_sys->b_error = true;
msg_Warn(s, "Canceling download thread!");
vlc_restorecancel( canc );
return NULL;
diff --git a/modules/stream_filter/smooth/smooth.c b/modules/stream_filter/smooth/smooth.c
index e56dda1..9f21c84 100644
--- a/modules/stream_filter/smooth/smooth.c
+++ b/modules/stream_filter/smooth/smooth.c
@@ -39,12 +39,19 @@
#include <vlc_charset.h>
#include <vlc_stream.h>
#include <vlc_es.h>
+#include <vlc_codecs.h>
#include "smooth.h"
+#include "../../demux/mp4/libmp4.h"
#define DA_TEXT N_("Disable audio stream")
#define DA_LONGTEXT N_("Disable audio stream")
+/* I make the assumption that when the demux want to do a *time* seek,
+ * then p_sys->download->boffset > FAKE_STREAM_SIZE, and thus FAKE_STREAM_SIZE
+ * should be small enough. 1000 seems to be a sensible choice. See also
+ * chunk_Seek() comments to understand properly */
+#define FAKE_STREAM_SIZE 1000
/*****************************************************************************
* Module descriptor
*****************************************************************************/
@@ -63,33 +70,63 @@ vlc_module_begin()
change_safe()
vlc_module_end()
-static int Read( stream_t *, void *, unsigned int );
-static int Peek( stream_t *, const uint8_t **, unsigned int );
+static int Read( stream_t *, void *, unsigned );
+static int Peek( stream_t *, const uint8_t **, unsigned );
static int Control( stream_t *, int , va_list );
static bool isSmoothStreaming( stream_t *s )
{
const uint8_t *peek;
- const char *conv_peek;
+ char *peeked = malloc( 512 );
+ if( unlikely( !peeked ) )
+ return false;
const char *needle = "<SmoothStreamingMedia";
+ const char *encoding = NULL;
+ bool ret = false;
- int i_size = stream_Peek( s->p_source, &peek, 1024 );
- if( i_size < 1024 )
+ int i_size = stream_Peek( s->p_source, &peek, 512 );
+ if( i_size < 512 )
+ {
+ free( peeked );
return false;
+ }
+
+ memcpy( peeked, peek, 512 );
+ peeked[511] = peeked[510] = '\0';
- if( strstr( (const char*)peek, needle ) != NULL )
- return true;
+ if( strstr( (const char *)peeked, needle ) != NULL )
+ ret = true;
else
/* maybe it's utf-16 encoding, should we also test other encodings? */
{
- conv_peek = FromCharset( "UTF-16", peek, 1024 );
- if( conv_peek != NULL )
- if( strstr( conv_peek, needle ) != NULL )
- return true;
+ if( !memcmp( peeked, "\xFF\xFE", 2 ) )
+ encoding = "UTF-16LE";
+ else if( !memcmp( peeked, "\xFE\xFF", 2 ) )
+ encoding = "UTF-16BE";
+ else
+ {
+ free( peeked );
+ return false;
+ }
+ peeked = FromCharset( encoding, peeked, 512 );
+
+ if( strstr( peeked, needle ) != NULL )
+ ret = true;
}
- return false;
+ free( peeked );
+ return ret;
}
+#if 0
+static void print_chunk( stream_t *s, chunk_t *ck )
+{
+ msg_Info( s, "chunk %u type %i: duration is %"PRIu64", stime is %"PRIu64", "\
+ "size is %i, offset is %"PRIu64", read_pos is %i.",
+ ck->sequence, ck->type, ck->duration,
+ ck->start_time, ck->size, ck->offset, ck->read_pos );
+}
+#endif
+
static int parse_Manifest( stream_t *s )
{
stream_sys_t *p_sys = s->p_sys;
@@ -111,25 +148,30 @@ static int parse_Manifest( stream_t *s )
if( !vlc_reader )
{
msg_Err( s, "Failed to open source for parsing" );
+ xml_Delete( vlc_xml );
+ return VLC_EGENERIC;
}
const char *node;
char *stream_name = NULL;
+ uint8_t *WaveFormatEx;
int stream_type = UNKNOWN_ES;
sms_stream_t *sms = NULL;
quality_level_t *ql = NULL;
int64_t start_time = 0, duration = 0;
int64_t computed_start_time = 0, computed_duration = 0;
- uint32_t next_track_id = 1;
- uint32_t next_qid = 1;
- enum{ TIMESCALE = 10000000 };
- uint32_t Subtype = 0;
+ unsigned next_track_id = 1;
+ unsigned next_qid = 1;
+ int loop_count = 0;
+ bool b_weird = false;
+#define TIMESCALE 10000000
while( (type = xml_ReaderNextNode( vlc_reader, &node )) > 0 )
{
switch( type )
{
case XML_READER_STARTELEM:
+
if( !strcmp( node, "SmoothStreamingMedia" ) )
{
while( (name = xml_ReaderNextAttr( vlc_reader, &value )) )
@@ -146,7 +188,6 @@ static int parse_Manifest( stream_t *s )
if( !strcmp( node, "StreamIndex" ) )
{
- Subtype = 0;
sms = sms_New();
if( unlikely( !sms ) )
return VLC_ENOMEM;
@@ -181,9 +222,6 @@ static int parse_Manifest( stream_t *s )
sms->qlevel_nb = strtoul( value, NULL, 10 );
if( !strcmp( name, "Url" ) )
sms->url_template = strdup(value);
- if( !strcmp( name, "Subtype" ) )
- if( !strcmp( value, "WmaPro" ) )
- Subtype = VLC_FOURCC('W', 'M', 'A', 'P' );
}
if( sms && !sms->timescale )
@@ -201,16 +239,6 @@ static int parse_Manifest( stream_t *s )
sms->name = stream_name;
sms->type = stream_type;
vlc_array_append( p_sys->sms_streams, sms );
-
- for( unsigned i = 0; i < p_sys->i_tracks; i++ )
- {
- sms = vlc_array_item_at_index( p_sys->sms_streams, i );
- if( (sms->type == stream_type) &&
- !(strcmp( stream_name, sms->name )) )
- break;
- else if( i == p_sys->i_tracks - 1 )
- msg_Err( s, "Updating of the manifest failed" );
- }
}
if( !strcmp( node, "QualityLevel" ) )
@@ -232,10 +260,22 @@ static int parse_Manifest( stream_t *s )
if( !strcmp( name, "CodecPrivateData" ) )
ql->CodecPrivateData = strdup( value );
if( !strcmp( name, "WaveFormatEx" ) )
- ql->CodecPrivateData = strdup( value );
- if( !strcmp( name, "MaxWidth" ) )
+ {
+ WaveFormatEx = decode_string_hex_to_binary( value );
+ uint16_t data_len = ((uint16_t *)WaveFormatEx)[8];
+ ql->CodecPrivateData = strndup( value + 36, data_len * 2 );
+
+ uint16_t wf_tag = ((uint16_t *)WaveFormatEx)[0];
+ wf_tag_to_fourcc( wf_tag, &ql->FourCC, NULL );
+
+ ql->Channels = ((uint16_t *)WaveFormatEx)[1];
+ ql->SamplingRate = ((uint32_t *)WaveFormatEx)[1];
+ ql->nBlockAlign = ((uint16_t *)WaveFormatEx)[6];
+ ql->BitsPerSample = ((uint16_t *)WaveFormatEx)[7];
+ }
+ if( !strcmp( name, "MaxWidth" ) || !strcmp( name, "Width" ) )
ql->MaxWidth = strtoul( value, NULL, 10 );
- if( !strcmp( name, "MaxHeight" ) )
+ if( !strcmp( name, "MaxHeight" ) || !strcmp( name, "Height" ) )
ql->MaxHeight = strtoul( value, NULL, 10 );
if( !strcmp( name, "Channels" ) )
ql->Channels = strtoul( value, NULL, 10 );
@@ -244,13 +284,12 @@ static int parse_Manifest( stream_t *s )
if( !strcmp( name, "BitsPerSample" ) )
ql->BitsPerSample = strtoul( value, NULL, 10 );
}
- if( !ql->FourCC )
- ql->FourCC = Subtype;
vlc_array_append( sms->qlevels, ql );
}
if( !strcmp( node, "c" ) )
{
+ loop_count++;
start_time = duration = -1;
while( (name = xml_ReaderNextAttr( vlc_reader, &value )) )
{
@@ -268,34 +307,61 @@ static int parse_Manifest( stream_t *s )
else if( duration == -1 )
{
assert( start_time != -1 );
+ /* Handle weird Manifests which give only the start time
+ * of the first segment. In those cases, we have to look
+ * at the start time of the second segment to compute
+ * the duration of the first one. */
+ if( loop_count == 1 )
+ {
+ b_weird = true;
+ computed_start_time = start_time;
+ continue;
+ }
+
computed_duration = start_time - computed_start_time;
- computed_start_time = start_time;
+ if( !b_weird )
+ computed_start_time = start_time;
}
else
{
- computed_start_time = start_time;
- computed_duration = duration;
+ if( b_weird )
+ computed_duration = start_time - computed_start_time;
+ else
+ {
+ computed_start_time = start_time;
+ computed_duration = duration;
+ }
}
if( unlikely( chunk_New( sms, computed_duration,
computed_start_time ) == NULL ) )
+ {
return VLC_ENOMEM;
+ }
+ if( b_weird && start_time != -1 )
+ computed_start_time = start_time;
}
break;
case XML_READER_ENDELEM:
- if( !strcmp( node, "StreamIndex" ) )
- {
- stream_name = NULL;
- stream_type = UNKNOWN_ES;
- computed_start_time = 0;
- computed_duration = 0;
- next_qid = 1;
-
- if( sms->qlevel_nb == 0 )
- sms->qlevel_nb = vlc_array_count( sms->qlevels );
- }
+ if( strcmp( node, "StreamIndex" ) )
+ break;
+
+ stream_name = NULL;
+ stream_type = UNKNOWN_ES;
+ computed_start_time = 0;
+ computed_duration = 0;
+ loop_count = 0;
+ if( b_weird && !chunk_New( sms, computed_duration, computed_start_time ) )
+ return VLC_ENOMEM;
+
+ b_weird = false;
+ next_qid = 1;
+
+ if( sms->qlevel_nb == 0 )
+ sms->qlevel_nb = vlc_array_count( sms->qlevels );
break;
+
case XML_READER_NONE:
break;
case XML_READER_TEXT:
@@ -304,6 +370,7 @@ static int parse_Manifest( stream_t *s )
return VLC_EGENERIC;
}
}
+#undef TIMESCALE
xml_ReaderDelete( vlc_reader );
xml_Delete( vlc_xml );
@@ -321,50 +388,36 @@ static int Open( vlc_object_t *p_this )
msg_Info( p_this, "Smooth Streaming (%s)", s->psz_path );
- /* */
s->p_sys = p_sys = calloc( 1, sizeof(*p_sys ) );
if( unlikely( p_sys == NULL ) )
return VLC_ENOMEM;
char *uri = NULL;
- if( asprintf( &uri,"%s://%s", s->psz_access, s->psz_path ) < 0)
+ if( unlikely( asprintf( &uri, "%s://%s", s->psz_access, s->psz_path ) < 0 ) )
{
free( p_sys );
return VLC_ENOMEM;
}
- p_sys->ismc = uri;
/* remove the last part of the url */
- char *base_url = strdup( uri );
- char *pos = strrchr( base_url, '/');
+ char *pos = strrchr( uri, '/');
*pos = '\0';
- p_sys->base_url = base_url;
+ p_sys->base_url = uri;
- p_sys->download.vlead = 0;
bool disable_audio = var_InheritBool( s, "smooth-disable-audio" );
- if( disable_audio )
- p_sys->download.alead = UINT64_MAX;
- else
- p_sys->download.alead = 0;
- p_sys->download.tlead = 0;
- p_sys->playback.boffset = 0;
- /* UINT32_MAX is the index value of the initialization chunk */
- p_sys->download.vindex = 0;
- p_sys->download.aindex = 0;
- p_sys->b_live = false;
- p_sys->b_cache = true;
- p_sys->b_error = false;
+ p_sys->download.alead = disable_audio ? UINT64_MAX : 0;
+
+ /* XXX I don't know wether or not we should allow caching */
+ p_sys->b_cache = false;
p_sys->sms_streams = vlc_array_new();
- p_sys->download.dld_chunks = vlc_array_new();
- if( p_sys->sms_streams == NULL )
+ p_sys->download.chunks = vlc_array_new();
+ if( unlikely( !p_sys->sms_streams || !p_sys->download.chunks ) )
{
free( p_sys );
return VLC_ENOMEM;
}
- p_sys->playback.peek_index = p_sys->playback.index = 0;
-
/* Parse SMS ismc content. */
if( parse_Manifest( s ) != VLC_SUCCESS )
{
@@ -376,11 +429,13 @@ static int Open( vlc_object_t *p_this )
p_sys->b_live = true;
p_sys->i_tracks = vlc_array_count( p_sys->sms_streams );
- p_sys->i_selected_tracks = 2;
+
+ /* FIXME */
+ p_sys->i_selected_tracks = 2; /* one video track and one audio track */
if( disable_audio )
p_sys->i_selected_tracks -= 1;
- /* Choose first video stream available FIXME */
+ /* Choose first video stream available */
sms_stream_t *vsms = NULL;
for( unsigned i = 0; i < p_sys->i_tracks; i++ )
{
@@ -393,7 +448,7 @@ static int Open( vlc_object_t *p_this )
}
p_sys->vstream = vsms;
- /* Choose first audio stream available FIXME */
+ /* Choose first audio stream available */
sms_stream_t *asms = NULL;
for( unsigned i = 0; i < p_sys->i_tracks; i++ )
{
@@ -407,29 +462,23 @@ static int Open( vlc_object_t *p_this )
}
p_sys->astream = asms;
- /* Choose SMS quality to start with */
- quality_level_t *wanted = vlc_array_item_at_index( vsms->qlevels, 0 );
- quality_level_t *qlvl = NULL;
- for( uint32_t i=1; i < vsms->qlevel_nb; i++ )
- {
- qlvl = vlc_array_item_at_index( vsms->qlevels, i );
- if( qlvl->Bitrate < wanted->Bitrate )
- wanted = qlvl;
- }
- vsms->download_qlvl = wanted->id;
+ /* Choose lowest quality for the first chunks */
+ quality_level_t *wanted, *qlvl;
+ sms_stream_t *sms = NULL;
- wanted = vlc_array_item_at_index( asms->qlevels, 0 );
- for( uint32_t i = 1; i < asms->qlevel_nb; i++)
+ for( unsigned i = 0; i < p_sys->i_tracks; i++ )
{
- qlvl = vlc_array_item_at_index( asms->qlevels, i );
- if( qlvl->Bitrate < wanted->Bitrate )
- wanted = qlvl;
+ wanted = qlvl = NULL;
+ sms = vlc_array_item_at_index( p_sys->sms_streams, i );
+ wanted = vlc_array_item_at_index( sms->qlevels, 0 );
+ for( unsigned i=1; i < sms->qlevel_nb; i++ )
+ {
+ qlvl = vlc_array_item_at_index( sms->qlevels, i );
+ if( qlvl->Bitrate < wanted->Bitrate )
+ wanted = qlvl;
+ }
+ sms->download_qlvl = wanted->id;
}
- asms->download_qlvl = wanted->id;
-
-
-
- p_sys->playback.toffset = 0;
vlc_mutex_init( &p_sys->download.lock_wait );
vlc_cond_init( &p_sys->download.wait );
@@ -441,6 +490,7 @@ static int Open( vlc_object_t *p_this )
if( vlc_clone( &p_sys->thread, sms_Thread, s, VLC_THREAD_PRIORITY_INPUT ) )
{
+ free( p_sys );
vlc_mutex_destroy( &p_sys->download.lock_wait );
vlc_cond_destroy( &p_sys->download.wait );
return VLC_EGENERIC;
@@ -454,254 +504,141 @@ static void Close( vlc_object_t *p_this )
stream_t *s = (stream_t*)p_this;
stream_sys_t *p_sys = s->p_sys;
- msg_Dbg( s, "Joining dl thread" );
+ vlc_mutex_lock( &p_sys->download.lock_wait );
+ p_sys->b_close = true;
+ /* Negate the condition variable's predicate */
+ p_sys->download.vlead = p_sys->download.alead = 0;
+ p_sys->playback.toffset = 0;
+ vlc_cond_signal(&p_sys->download.wait);
+ vlc_mutex_unlock( &p_sys->download.lock_wait );
+
vlc_join( p_sys->thread, NULL );
vlc_mutex_destroy( &p_sys->download.lock_wait );
vlc_cond_destroy( &p_sys->download.wait );
- msg_Dbg( s, "dl thread joined" );
/* Free sms streams */
+ sms_stream_t *sms;
for( int i = 0; i < vlc_array_count( p_sys->sms_streams ); i++ )
{
- sms_stream_t *sms;
- sms = (sms_stream_t *)vlc_array_item_at_index( p_sys->sms_streams, i );
- if( sms) sms_Free( sms );
+ sms = vlc_array_item_at_index( p_sys->sms_streams, i );
+ if( sms)
+ sms_Free( sms );
}
+
vlc_array_destroy( p_sys->sms_streams );
+ vlc_array_destroy( p_sys->download.chunks );
- if( p_sys->peeked )
- block_Release( p_sys->peeked );
+ free( p_sys->base_url );
free( p_sys );
}
-static chunk_t *get_chunk( stream_t *s, uint32_t chunk_index, bool wait )
+static chunk_t *get_chunk( stream_t *s, bool wait )
{
stream_sys_t *p_sys = s->p_sys;
- int count;
+ unsigned count;
chunk_t *chunk = NULL;
vlc_mutex_lock( &p_sys->download.lock_wait );
- count = vlc_array_count( p_sys->download.dld_chunks );
- while( chunk_index >= (uint32_t)count )
+ count = vlc_array_count( p_sys->download.chunks );
+ while( p_sys->playback.index >= count || p_sys->b_tseek )
{
- if( !wait || !vlc_object_alive( s ) )
- goto fail;
-
- msg_Dbg( s, "get_chunk is waiting !!!" );
- vlc_cond_timedwait( &p_sys->download.wait,
- &p_sys->download.lock_wait, mdate() + 100000 );
- count = vlc_array_count( p_sys->download.dld_chunks );
- }
- chunk = vlc_array_item_at_index( p_sys->download.dld_chunks, chunk_index );
- vlc_mutex_unlock( &p_sys->download.lock_wait );
- return chunk;
-
-fail:
- vlc_mutex_unlock( &p_sys->download.lock_wait );
- msg_Warn( s, "get_chunk failed! (chunk_index %"PRIu32")", chunk_index );
- return NULL;
-}
-
-static uint64_t GetStreamSize( stream_t *s )
-{
- /* The returned size will be very approximative, but bitrate adaptation
- * make computations a tad tricky anyway */
- stream_sys_t *p_sys = s->p_sys;
-
- if( p_sys->b_live )
- {
- chunk_t *chunk = get_chunk( s, p_sys->playback.index, false );
- return chunk->size;
- }
-
- uint32_t video_chunk_nb = p_sys->vstream->vod_chunks_nb;
- vlc_mutex_lock( &p_sys->vstream->lock );
- chunk_t *first_video_chunk = vlc_array_item_at_index(
- p_sys->vstream->chunks, 0 );
- vlc_mutex_unlock( &p_sys->vstream->lock );
- uint64_t chunk_duration = first_video_chunk->duration;
- uint64_t total_duration = video_chunk_nb * chunk_duration /
- p_sys->timescale;
- uint64_t bitrate = 500000 /* FIXME */;
- uint64_t size = bitrate * total_duration;
-
- return size;
-}
-
-static int chunk_Seek( stream_t *s, const uint64_t pos )
-{
- stream_sys_t *p_sys = s->p_sys;
-
- if( pos == p_sys->playback.boffset )
- return VLC_SUCCESS;
- chunk_t *chunk = get_chunk( s, p_sys->playback.index, false );
- if( chunk == NULL )
- goto fail;
- if( pos < chunk->offset )
- {
- if( p_sys->b_live || !p_sys->b_cache )
+ /* Yes I know, checking for p_sys->b_die is not reliable,
+ * that's why vlc_object_alive() has been deprecated. But if I
+ * understood well, there is no good solution with a stream_filter
+ * module anyaway. */
+ if( !wait || s->b_die || p_sys->b_error )
{
- msg_Warn( s, "Can't seek backward outside the current chunk" );
- return VLC_EGENERIC;
+ vlc_mutex_unlock( &p_sys->download.lock_wait );
+ msg_Warn( s, "get_chunk failed! (playback index %u)",
+ p_sys->playback.index );
+ return NULL;
}
- else
+ if( !p_sys->b_live &&
+ p_sys->download.aindex >= (p_sys->vstream->vod_chunks_nb -1) &&
+ p_sys->download.vindex >= (p_sys->astream->vod_chunks_nb -1) )
{
- msg_Dbg( s, "Seeking backward outside the current chunk" );
- chunk->read_pos = 0;
- p_sys->playback.index--;
- msg_Dbg( s, "Seeking backward, to dld chunk %"PRIu32"",
- p_sys->playback.index);
- chunk = get_chunk( s, p_sys->playback.index, false );
- if( chunk == NULL )
- goto fail;
- chunk->read_pos = 0;
- p_sys->playback.boffset = chunk->offset;
- return chunk_Seek( s, pos );
+ vlc_mutex_unlock( &p_sys->download.lock_wait );
+ msg_Info( s, "No more chunks, end of the VOD" );
+ return NULL;
}
- }
- else if( pos < chunk->offset + chunk->read_pos )
- {
- chunk->read_pos = pos - chunk->offset;
- }
- else
- {
- assert( pos >= p_sys->playback.boffset );
- int len = pos - p_sys->playback.boffset;
- int skipped = Read( s, NULL, len);
- if( skipped == len )
- goto success;
- }
-
-success:
- chunk = get_chunk( s, p_sys->playback.index, false );
- if( chunk == NULL )
- return VLC_EGENERIC;
- msg_Dbg( s, "Seek successful! We are now at chunk %"PRIu32" type "\
- "%i, offset %"PRIu64" + %i", chunk->sequence,
- chunk->type, chunk->offset, chunk->read_pos );
- return VLC_SUCCESS;
-fail:
- msg_Warn( s, "chunk_Seek failed, maybe not enough dld chunks");
- return VLC_EGENERIC;
-}
+ msg_Dbg( s, "get_chunk is waiting !!!" );
+ vlc_cond_timedwait( &p_sys->download.wait,
+ &p_sys->download.lock_wait, mdate() + 500000 );
+ count = vlc_array_count( p_sys->download.chunks );
+ msg_Dbg( s, "count is %u, and index is %u", count, p_sys->playback.index );
+ }
+ chunk = vlc_array_item_at_index( p_sys->download.chunks, p_sys->playback.index );
-static int Control( stream_t *s, int i_query, va_list args )
-{
- stream_sys_t *p_sys = s->p_sys;
+ vlc_mutex_unlock( &p_sys->download.lock_wait );
- switch( i_query )
- {
- case STREAM_CAN_SEEK:
- *(va_arg( args, bool * )) = true;
- break;
- case STREAM_GET_POSITION:
- *(va_arg( args, uint64_t * )) = p_sys->playback.boffset;
- break;
- case STREAM_SET_POSITION:
- if( true )
- {
- uint64_t pos = (uint64_t)va_arg(args, uint64_t);
- if (chunk_Seek(s, pos) == VLC_SUCCESS)
- {
- p_sys->playback.boffset = pos;
- break;
- }
- }
- return VLC_EGENERIC;
- case STREAM_GET_SIZE:
- *(va_arg( args, uint64_t * )) = GetStreamSize( s );
- break;
- default:
- return VLC_EGENERIC;
- }
- return VLC_SUCCESS;
+ return chunk;
}
-static int sms_Read( stream_t *s, uint8_t *p_read, unsigned int i_read,
- bool peek )
+static int sms_Read( stream_t *s, uint8_t *p_read, int i_read )
{
stream_sys_t *p_sys = s->p_sys;
int copied = 0;
chunk_t *chunk = NULL;
- int loop_count = 0;
- uint32_t *chunk_index;
- int *position;
- int peek_pos = 0;
-
- if( peek )
- chunk_index = &p_sys->playback.peek_index;
- else
- chunk_index = &p_sys->playback.index;
do
{
- chunk = get_chunk( s, *chunk_index, true );
+ chunk = get_chunk( s, true );
if( !chunk )
return copied;
- if( peek )
+ if( chunk->read_pos >= (int)chunk->size )
{
- if( loop_count == 0 ) /* we start at read position */
- peek_pos = chunk->read_pos;
- position = &peek_pos;
- }
- else
- position = &chunk->read_pos;
-
- vlc_mutex_lock( &chunk->lock );
-
- loop_count++;
- if( *position >= (int)chunk->size )
- {
- if( chunk->type == VIDEO_ES && !peek )
+ if( chunk->type == VIDEO_ES )
{
- p_sys->playback.toffset += chunk->duration;
- /* signal download thread */
vlc_mutex_lock( &p_sys->download.lock_wait );
- vlc_cond_signal( &p_sys->download.wait );
+ p_sys->playback.toffset += chunk->duration;
vlc_mutex_unlock( &p_sys->download.lock_wait );
+ vlc_cond_signal( &p_sys->download.wait);
}
-
- if( !peek && (!p_sys->b_cache || p_sys->b_live) )
+ if( !p_sys->b_cache || p_sys->b_live )
{
block_Release( chunk->data );
chunk->data = NULL;
}
- else if( chunk->size > 0 )
- *position = 0;
- *chunk_index += 1;
+ chunk->read_pos = 0;
+
+ p_sys->playback.index += 1;
msg_Dbg( s, "Incrementing playback index" );
- vlc_mutex_unlock( &chunk->lock );
continue;
}
- if( *position == 0 )
+ if( chunk->read_pos == 0 )
{
const char *verb = p_read == NULL ? "skipping" : "reading";
- msg_Dbg( s, "%s chunk %"PRIu32" (%u bytes), type %i",
- peek ? "peeking at" : verb,
- chunk->sequence, i_read, chunk->type );
+ msg_Dbg( s, "%s chunk %u (%u bytes), type %i",
+ verb, chunk->sequence, i_read, chunk->type );
+ /* check integrity */
+ uint32_t type;
+ uint8_t *slice = chunk->data->p_buffer;
+ SMS_GET4BYTES( type );
+ SMS_GETFOURCC( type );
+ assert( type == ATOM_moof || type == ATOM_uuid );
}
int len = -1;
- uint8_t *src = chunk->data->p_buffer + *position;
- if( i_read <= (chunk->size - *position) )
+ uint8_t *src = chunk->data->p_buffer + chunk->read_pos;
+ if( i_read <= chunk->size - chunk->read_pos )
len = i_read;
else
- len = chunk->size - *position;
+ len = chunk->size - chunk->read_pos;
if( len > 0 )
{
if( p_read ) /* otherwise caller skips data */
memcpy( p_read + copied, src, len );
- *position += len;
+ chunk->read_pos += len;
copied += len;
i_read -= len;
}
- vlc_mutex_unlock( &chunk->lock );
+
} while ( i_read > 0 );
@@ -716,39 +653,126 @@ static int Read( stream_t *s, void *buffer, unsigned i_read )
if( p_sys->b_error )
return 0;
- if( !buffer ) msg_Dbg(s, "Caller skips data (%i bytes)", i_read );
- else msg_Dbg( s, "Demuxer wants to read %i bytes", i_read );
-
- length = sms_Read( s, (uint8_t*) buffer, i_read, false );
+ length = sms_Read( s, (uint8_t*) buffer, i_read );
if( length < 0 )
return 0;
+ /* This call to sms_Read will increment p_sys->playback.index
+ * in case the last chunk we read into is entirely read */
+ sms_Read( s, NULL, 0 );
+
p_sys->playback.boffset += length;
if( (unsigned)length < i_read )
- msg_Warn( s, "could not read %u bytes, only %i!", i_read, length );
+ msg_Warn( s, "could not read %i bytes, only %i!", i_read, length );
+
return length;
}
-static int Peek( stream_t *s, const uint8_t **pp_peek, unsigned int i_peek )
+/* The MP4 demux should never have to to peek outside the current chunk */
+static int Peek( stream_t *s, const uint8_t **pp_peek, unsigned i_peek )
{
stream_sys_t *p_sys = s->p_sys;
- msg_Dbg( s, "Demuxer wants to peek %i bytes", i_peek );
- block_t *peeked = p_sys->peeked;
- p_sys->playback.peek_index = p_sys->playback.index;
- if( peeked == NULL )
- peeked = block_Alloc ( i_peek );
- else if( peeked->i_buffer < i_peek)
- peeked = block_Realloc( peeked, 0, i_peek );
-
- if( (p_sys->peeked = peeked) == NULL )
+ chunk_t *chunk = get_chunk( s, true );
+ if( chunk )
+ assert( chunk->data );
+ else
return 0;
- int val = sms_Read( s, peeked->p_buffer, i_peek, true );
+ int bytes = chunk->size - chunk->read_pos;
+ assert( bytes > 0 );
+
+ if( (unsigned)bytes < i_peek )
+ {
+ msg_Err( s, "could not peek %u bytes, only %i!", i_peek, bytes );
+ }
+ msg_Dbg( s, "peeking at chunk %u!", chunk->sequence );
+ *pp_peek = chunk->data->p_buffer + chunk->read_pos;
+
+ return bytes;
+}
+
+/* Not being able to distinguish between a time seek and a byte seek is
+ * annoying. However, if FAKE_STREAM_SIZE is small enough, say 1000,
+ * then we can reasonably assume that pos > 1000 means that the demux
+ * wants to *byte* seek. This is important because if we treat a time seek
+ * as a byte seek, then the demux would get bytes of the current chunk, during
+ * the next call to Peek() or Read(), instead of a new initialization chunk,
+ * followed by the new downloaded chunks, as it is normally the case for a time
+ * seek. And that would result in a bug */
+static int chunk_Seek( stream_t *s, const uint64_t pos )
+{
+ stream_sys_t *p_sys = s->p_sys;
+
+ if( pos == p_sys->playback.boffset )
+ return VLC_SUCCESS;
+
+ chunk_t *chunk = get_chunk( s, false );
+ if( chunk == NULL )
+ return VLC_EGENERIC;
- *pp_peek = peeked->p_buffer;
- if( (unsigned)val < i_peek )
- msg_Warn( s, "could not peek %u bytes, only %i!", i_peek, val );
+ bool inside_chunk = pos >= chunk->offset &&
+ pos < (chunk->offset + chunk->size) ? true : false;
- return val;
+ assert( !end_of_chunk );
+
+ if( inside_chunk )
+ {
+ chunk->read_pos = pos - chunk->offset;
+ p_sys->playback.boffset = pos;
+ return VLC_SUCCESS;
+ }
+ else
+ {
+ if( p_sys->b_live )
+ {
+ msg_Err( s, "Cannot seek outside the current chunk for a live stream" );
+ return VLC_EGENERIC;
+ }
+
+ msg_Info( s, "Seeking outside the current chunk" );
+ assert( pos <= FAKE_STREAM_SIZE );
+
+ vlc_mutex_lock( &p_sys->download.lock_wait );
+
+ p_sys->b_tseek = true;
+ p_sys->time_pos = p_sys->vod_duration * pos / FAKE_STREAM_SIZE;
+ p_sys->download.vlead = p_sys->download.alead = 0;
+ p_sys->playback.toffset = 0;
+
+ vlc_cond_signal( &p_sys->download.wait);
+ vlc_mutex_unlock( &p_sys->download.lock_wait );
+
+ return VLC_SUCCESS;
+ }
+}
+
+static int Control( stream_t *s, int i_query, va_list args )
+{
+ stream_sys_t *p_sys = s->p_sys;
+
+ switch( i_query )
+ {
+ case STREAM_CAN_SEEK:
+ *(va_arg( args, bool * )) = true;
+ break;
+ case STREAM_GET_POSITION:
+ *(va_arg( args, uint64_t * )) = p_sys->playback.boffset;
+ break;
+ case STREAM_SET_POSITION:
+ {
+ uint64_t pos = (uint64_t)va_arg(args, uint64_t);
+ int ret = chunk_Seek(s, pos);
+ if( ret == VLC_SUCCESS )
+ break;
+ else
+ return VLC_EGENERIC;
+ }
+ case STREAM_GET_SIZE:
+ *(va_arg( args, uint64_t * )) = FAKE_STREAM_SIZE;
+ break;
+ default:
+ return VLC_EGENERIC;
+ }
+ return VLC_SUCCESS;
}
diff --git a/modules/stream_filter/smooth/smooth.h b/modules/stream_filter/smooth/smooth.h
index 2499960..5a593d0 100644
--- a/modules/stream_filter/smooth/smooth.h
+++ b/modules/stream_filter/smooth/smooth.h
@@ -43,13 +43,12 @@ typedef struct chunk_s
{
int64_t duration; /* chunk duration (seconds / TimeScale) */
int64_t start_time; /* PTS (seconds / TimeScale) */
- int64_t size; /* chunk size in bytes */
- uint32_t sequence; /* unique sequence number */
+ int size; /* chunk size in bytes */
+ unsigned sequence; /* unique sequence number */
uint64_t offset; /* offset in the media */
int read_pos; /* position in the chunk */
int type; /* video, audio, or subtitles */
- vlc_mutex_t lock;
block_t *data;
} chunk_t;
@@ -57,16 +56,17 @@ typedef struct quality_level_s
{
int Index;
uint32_t FourCC;
- uint32_t Bitrate;
- uint32_t MaxWidth;
- uint32_t MaxHeight;
+ unsigned Bitrate;
+ unsigned MaxWidth;
+ unsigned MaxHeight;
unsigned SamplingRate;
unsigned Channels;
unsigned BitsPerSample;
- uint32_t PacketSize;
- uint32_t AudioTag;
+ unsigned PacketSize;
+ unsigned AudioTag;
+ unsigned nBlockAlign;
char *CodecPrivateData; /* hex encoded string */
- uint16_t id;
+ unsigned id;
} quality_level_t;
@@ -74,27 +74,25 @@ typedef struct sms_stream_s
{
vlc_array_t *qlevels; /* list of available Quality Levels */
vlc_array_t *chunks; /* list of chunks */
- uint32_t vod_chunks_nb; /* total num of chunks of the VOD stream */
- uint32_t timescale;
- uint32_t qlevel_nb; /* number of quality levels */
- uint32_t id; /* track id, will be set arbitrarily */
+ unsigned vod_chunks_nb; /* total num of chunks of the VOD stream */
+ unsigned timescale;
+ unsigned qlevel_nb; /* number of quality levels */
+ unsigned id; /* track id, will be set arbitrarily */
char *name;
char *url_template;
int type;
- uint16_t download_qlvl; /* current quality level ID for Download() */
- vlc_mutex_t lock;
+ unsigned download_qlvl; /* current quality level ID for Download() */
} sms_stream_t;
struct stream_sys_t
{
- char *ismc; /* Manifest url */
+ FILE *bw_record;
+ FILE *avg_bw_rcd;
+ FILE *ql_lvl_rcd;
char *base_url; /* URL common part for chunks */
vlc_thread_t thread; /* SMS chunk download thread */
- block_t *peeked;
-
- /* */
vlc_array_t *sms_streams; /* array of sms_stream_t */
sms_stream_t *vstream; /* current video stream */
sms_stream_t *astream; /* current audio stream */
@@ -103,7 +101,8 @@ struct stream_sys_t
unsigned i_selected_tracks;
sms_queue_t *bws; /* Measured bandwidths of the N last chunks */
uint64_t vod_duration; /* total duration of the VOD media */
- uint32_t timescale;
+ int64_t time_pos;
+ unsigned timescale;
/* Download */
struct sms_download_s
@@ -112,12 +111,13 @@ struct stream_sys_t
uint64_t vlead; // available (downloaded),
uint64_t tlead; // in seconds / TimeScale
- uint32_t aindex; /* current audio chunk for download */
- uint32_t vindex; /* video */
- uint32_t sindex; /* spu */
+ unsigned aindex; /* current audio chunk for download */
+ unsigned vindex; /* video */
+ unsigned sindex; /* spu */
- vlc_array_t *dld_chunks; /* chunks that have been downloaded */
- vlc_mutex_t lock_wait; /* protect chunk download counter */
+ uint64_t next_chunk_offset;
+ vlc_array_t *chunks; /* chunks that have been downloaded */
+ vlc_mutex_t lock_wait; /* protect chunk download counter. */
vlc_cond_t wait; /* some condition to wait on */
} download;
@@ -126,14 +126,15 @@ struct stream_sys_t
{
uint64_t boffset; /* current byte offset in media */
uint64_t toffset; /* current time offset in media */
- uint32_t index; /* current chunk for playback */
- uint32_t peek_index;
+ unsigned index; /* current chunk for playback */
} playback;
/* state */
bool b_cache; /* can cache files */
bool b_live; /* live stream? or vod? */
bool b_error; /* parsing error */
+ bool b_close; /* set by Close() */
+ bool b_tseek; /* time seeking */
};
#define SMS_GET4BYTES( dst ) do { \
@@ -170,7 +171,7 @@ struct stream_sys_t
sms_queue_t *sms_queue_init( int );
void sms_queue_put( sms_queue_t *, uint64_t );
uint64_t sms_queue_avg( sms_queue_t *);
-quality_level_t *get_qlevel( sms_stream_t *, uint16_t );
+quality_level_t *get_qlevel( sms_stream_t *, unsigned );
void* sms_Thread( void *);
quality_level_t * ql_New( void );
void ql_Free( quality_level_t *);
@@ -178,5 +179,6 @@ chunk_t *chunk_New( sms_stream_t* , uint64_t , uint64_t );
void chunk_Free( chunk_t *);
sms_stream_t * sms_New( void );
void sms_Free( sms_stream_t *);
+uint8_t *decode_string_hex_to_binary( const char * );
#endif
diff --git a/modules/stream_filter/smooth/utils.c b/modules/stream_filter/smooth/utils.c
index 3eeac36..5a6bd99 100644
--- a/modules/stream_filter/smooth/utils.c
+++ b/modules/stream_filter/smooth/utils.c
@@ -27,14 +27,46 @@
#include <vlc_common.h>
#include <vlc_es.h>
#include <vlc_block.h>
+#include <assert.h>
#include "smooth.h"
+static int hex_digit( char c )
+{
+ if (c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+ else if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ else if (c >= '0' && c<= '9')
+ return c - '0';
+ else
+ return -1;
+}
+
+uint8_t *decode_string_hex_to_binary( const char *psz_src )
+{
+ int i = 0, j = 0, first_digit, second_digit;
+ int i_len = strlen( psz_src );
+ uint8_t *p_data = malloc ( i_len / 2 );
+
+ if( !p_data )
+ return NULL;
+
+ while( i < i_len )
+ {
+ first_digit = hex_digit( psz_src[i++] );
+ second_digit = hex_digit( psz_src[i++] );
+ p_data[j++] = ( first_digit << 4 ) | second_digit;
+ }
+
+ return p_data;
+}
+
quality_level_t * ql_New( void )
{
quality_level_t *ql = calloc( 1, sizeof( quality_level_t ) );
- if( unlikely( ql == NULL ) )
- return NULL;
+ if( unlikely( !ql ) ) return NULL;
+
ql->Index = -1;
return ql;
}
@@ -56,18 +88,13 @@ chunk_t *chunk_New( sms_stream_t* sms, uint64_t duration,\
chunk->duration = duration;
chunk->start_time = start_time;
chunk->type = UNKNOWN_ES;
- vlc_mutex_lock( &sms->lock );
chunk->sequence = vlc_array_count( sms->chunks );
vlc_array_append( sms->chunks, chunk );
- vlc_mutex_unlock( &sms->lock );
- vlc_mutex_init( &chunk->lock );
return chunk;
}
void chunk_Free( chunk_t *chunk )
{
- vlc_mutex_destroy( &chunk->lock );
-
if( chunk->data )
block_Release( chunk->data );
free( chunk );
@@ -77,12 +104,11 @@ void chunk_Free( chunk_t *chunk )
sms_stream_t * sms_New( void )
{
sms_stream_t *sms = calloc( 1, sizeof( sms_stream_t ) );
- if( unlikely( sms == NULL ) )
- return NULL;
+ if( unlikely( !sms ) ) return NULL;
+
sms->qlevels = vlc_array_new();
sms->chunks = vlc_array_new();
sms->type = UNKNOWN_ES;
- vlc_mutex_init( &sms->lock );
return sms;
}
@@ -92,8 +118,7 @@ void sms_Free( sms_stream_t *sms )
{
for( int n = 0; n < vlc_array_count( sms->qlevels ); n++ )
{
- quality_level_t *qlevel =\
- (quality_level_t *)vlc_array_item_at_index( sms->qlevels, n );
+ quality_level_t *qlevel = vlc_array_item_at_index( sms->qlevels, n );
if( qlevel ) ql_Free( qlevel );
}
vlc_array_destroy( sms->qlevels );
@@ -103,8 +128,7 @@ void sms_Free( sms_stream_t *sms )
{
for( int n = 0; n < vlc_array_count( sms->chunks ); n++ )
{
- chunk_t *chunk =\
- (chunk_t *)vlc_array_item_at_index( sms->chunks, n );
+ chunk_t *chunk = vlc_array_item_at_index( sms->chunks, n );
if( chunk) chunk_Free( chunk );
}
vlc_array_destroy( sms->chunks );
@@ -114,7 +138,7 @@ void sms_Free( sms_stream_t *sms )
sms = NULL;
}
-quality_level_t *get_qlevel( sms_stream_t *sms, uint16_t qid )
+quality_level_t *get_qlevel( sms_stream_t *sms, unsigned qid )
{
quality_level_t *qlevel = NULL;
for( unsigned i = 0; i < sms->qlevel_nb; i++ )
@@ -149,6 +173,8 @@ void sms_queue_put( sms_queue_t *queue, uint64_t value )
FREENULL( last );
item_t *new = malloc( sizeof( item_t ) );
+ assert( new );
+
new->value = value;
new->next = queue->first;
queue->first = new;
--
1.7.10.4
More information about the vlc-devel
mailing list