Commit f67cdb74 authored by Frédéric Yhuel's avatar Frédéric Yhuel Committed by Jean-Baptiste Kempf

Smooth Streaming: clean / factorize code

and prepare subtitle support
Signed-off-by: default avatarJean-Baptiste Kempf <jb@videolan.org>
parent f2cc02bf
......@@ -140,14 +140,8 @@ static int sms_Download( stream_t *s, chunk_t *chunk, char *url )
stream_Delete( p_ts );
vlc_mutex_lock( &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;
int index = es_cat_to_index( chunk->type );
p_sys->download.lead[index] += chunk->duration;
vlc_mutex_unlock( &p_sys->download.lock_wait );
return VLC_SUCCESS;
......@@ -205,12 +199,7 @@ static int get_new_chunks( stream_t *s, chunk_t *ck )
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;
sms = SMS_GET_SELECTED_ST( ck->type );
SMS_GET4BYTES( size );
SMS_GETFOURCC( type );
......@@ -307,6 +296,7 @@ static int build_smoo_box( stream_t *s, uint8_t *smoo_box )
for( int i = 0; i < 3; i++ )
{
sms = NULL;
int cat = UNKNOWN_ES;
stra_box = smoo_box + i * STRA_SIZE;
stra_box[26] = (STRA_SIZE & 0xff00)>>8;
......@@ -322,21 +312,9 @@ static int build_smoo_box( stream_t *s, uint8_t *smoo_box )
((uint32_t *)stra_box)[10] = bswap32( 0x96c7bf25 );
((uint32_t *)stra_box)[11] = bswap32( 0xf97e2447 );
if( i == 0)
{
stra_box[48] = VIDEO_ES;
sms = p_sys->vstream;
}
else if( i == 1 )
{
stra_box[48] = AUDIO_ES;
sms = p_sys->astream;
}
else if( i == 2 )
{
stra_box[48] = SPU_ES;
sms = p_sys->tstream;
}
cat = index_to_es_cat( i );
stra_box[48] = cat;
sms = SMS_GET_SELECTED_ST( cat );
stra_box[49] = 0; /* reserved */
if( sms == NULL )
......@@ -359,6 +337,8 @@ static int build_smoo_box( stream_t *s, uint8_t *smoo_box )
((uint32_t *)stra_box)[23] = bswap32( qlvl->AudioTag );
((uint16_t *)stra_box)[48] = bswap16( qlvl->nBlockAlign );
if( !qlvl->CodecPrivateData )
continue;
stra_box[98] = stra_box[99] = stra_box[100] = 0; /* reserved */
assert( strlen( qlvl->CodecPrivateData ) < 512 );
stra_box[101] = strlen( qlvl->CodecPrivateData ) / 2;
......@@ -396,16 +376,8 @@ static int Download( stream_t *s, sms_stream_t *sms )
{
stream_sys_t *p_sys = s->p_sys;
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;
}
int index = es_cat_to_index( sms->type );
int64_t start_time = p_sys->download.lead[index];
quality_level_t *qlevel = get_qlevel( sms, sms->download_qlvl );
if( unlikely( !qlevel ) )
......@@ -485,21 +457,15 @@ static int Download( stream_t *s, sms_stream_t *sms )
chunk->sequence, sms->name, qlevel->Bitrate );
uint64_t actual_lead = chunk->start_time + chunk->duration;
if( sms->type == AUDIO_ES )
{
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 = 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 )
int ind = es_cat_to_index( sms->type );
p_sys->download.ck_index[ind] = chunk->sequence;
p_sys->download.lead[ind] = __MIN( p_sys->download.lead[ind], actual_lead );
if( sms->type == VIDEO_ES ||
( !SMS_GET_SELECTED_ST( VIDEO_ES ) && sms->type == AUDIO_ES ) )
{
p_sys->download.sindex = chunk->sequence;
p_sys->download.tlead = __MIN( p_sys->download.tlead, actual_lead );
p_sys->playback.toffset = __MIN( p_sys->playback.toffset,
(uint64_t)chunk->start_time );
}
unsigned dur_ms = __MAX( 1, duration / 1000 );
......@@ -555,30 +521,49 @@ static inline int64_t get_lead( stream_t *s )
{
stream_sys_t *p_sys = s->p_sys;
int64_t lead = 0;
if( p_sys->vstream && p_sys->astream )
lead = __MIN( p_sys->download.vlead, p_sys->download.alead );
else if( p_sys->vstream )
lead = p_sys->download.vlead;
int64_t alead = p_sys->download.lead[es_cat_to_index( AUDIO_ES )];
int64_t vlead = p_sys->download.lead[es_cat_to_index( VIDEO_ES )];
bool video = SMS_GET_SELECTED_ST( VIDEO_ES ) ? true : false;
bool audio = SMS_GET_SELECTED_ST( AUDIO_ES ) ? true : false;
if( video && audio )
lead = __MIN( vlead, alead );
else if( video )
lead = vlead;
else
lead = p_sys->download.alead;
lead = alead;
lead -= p_sys->playback.toffset;
return lead;
}
static int next_track( stream_t *s )
{
stream_sys_t *p_sys = s->p_sys;
uint64_t tmp, min = 0;
int cat, ret = UNKNOWN_ES;
for( int i = 0; i < 3; i++ )
{
tmp = p_sys->download.lead[i];
cat = index_to_es_cat( i );
if( (!min || tmp < min) && SMS_GET_SELECTED_ST( cat ) )
{
min = tmp;
ret = cat;
}
}
return ret;
}
void* sms_Thread( void *p_this )
{
stream_t *s = (stream_t *)p_this;
stream_sys_t *p_sys = s->p_sys;
sms_stream_t *sms = NULL;
chunk_t *chunk;
int canc = vlc_savecancel();
sms_stream_t *vsms = p_sys->vstream;
sms_stream_t *asms = p_sys->astream;
if( !vsms && !asms )
goto cancel;
/* 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 );
......@@ -595,57 +580,31 @@ void* sms_Thread( void *p_this )
p_sys->download.next_chunk_offset = init_ck->size;
chunk_t *audio_chunk, *video_chunk;
if( vsms )
video_chunk = vlc_array_item_at_index( vsms->chunks, 0 );
if( asms )
audio_chunk = vlc_array_item_at_index( asms->chunks, 0 );
if( (vsms && !video_chunk) || (asms && !audio_chunk) )
goto cancel;
/* Sometimes, the video stream is cut into pieces of one exact length,
/* XXX 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 */
if( asms && vsms )
{
int64_t amid, vmid;
amid = audio_chunk->duration / 2;
vmid = video_chunk->duration / 2;
if( audio_chunk->start_time > video_chunk->start_time + vmid )
{
video_chunk = vlc_array_item_at_index( vsms->chunks, 1 );
}
else if( video_chunk->start_time > audio_chunk->start_time + amid )
{
audio_chunk = vlc_array_item_at_index( asms->chunks, 1 );
}
}
int64_t start_time = 0, lead = 0;
if( p_sys->b_live )
for( int i = 0; i < 3; i++ )
{
p_sys->download.vlead = vsms ?
video_chunk->start_time + p_sys->timescale / 1000 : 0;
p_sys->download.alead = asms ?
audio_chunk->start_time + p_sys->timescale / 1000 : 0;
}
sms = SMS_GET_SELECTED_ST( index_to_es_cat( i ) );
if( sms )
{
chunk = vlc_array_item_at_index( sms->chunks, 0 );
p_sys->download.lead[i] = chunk->start_time + p_sys->timescale / 1000;
if( !start_time )
start_time = chunk->start_time;
if( vsms && Download( s, vsms ) != VLC_SUCCESS )
{
goto cancel;
}
if( asms && Download( s, asms ) != VLC_SUCCESS )
{
goto cancel;
if( Download( s, sms ) != VLC_SUCCESS )
goto cancel;
}
}
int64_t lead = 0;
int64_t start_time = vsms ? video_chunk->start_time : audio_chunk->start_time;
while( 1 )
{
/* XXX replace magic number 20 by a value depending on
/* XXX replace magic number 10 by a value depending on
* LookAheadFragmentCount and DVRWindowLength */
vlc_mutex_lock( &p_sys->download.lock_wait );
......@@ -655,13 +614,9 @@ void* sms_Thread( void *p_this )
break;
}
bool no_more_chunks = !p_sys->b_live &&
(!vsms || p_sys->download.vindex >= vsms->vod_chunks_nb - 1) &&
(!asms || p_sys->download.aindex >= asms->vod_chunks_nb - 1);
lead = get_lead( s );
while( lead > 10 * p_sys->timescale + start_time || no_more_chunks )
while( lead > 10 * p_sys->timescale + start_time || NO_MORE_CHUNKS )
{
vlc_cond_wait( &p_sys->download.wait, &p_sys->download.lock_wait );
lead = get_lead( s );
......@@ -690,8 +645,11 @@ void* sms_Thread( void *p_this )
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;
for( int i = 0; i < 3; i++ )
{
p_sys->download.lead[i] = p_sys->time_pos;
p_sys->download.ck_index[i] = 0;
}
p_sys->download.next_chunk_offset = 0;
p_sys->playback.boffset = 0;
......@@ -709,16 +667,9 @@ void* sms_Thread( void *p_this )
}
vlc_mutex_unlock( &p_sys->download.lock_wait );
if( !vsms || (asms && p_sys->download.alead < p_sys->download.vlead) )
{
if( Download( s, asms ) != VLC_SUCCESS )
break;
}
else if( !asms || (vsms && p_sys->download.vlead <= p_sys->download.alead ) )
{
if( Download( s, vsms ) != VLC_SUCCESS )
break;
}
sms = SMS_GET_SELECTED_ST( next_track( s ) );
if( Download( s, sms ) != VLC_SUCCESS )
goto cancel;
}
cancel:
......
......@@ -404,8 +404,10 @@ static int Open( vlc_object_t *p_this )
p_sys->b_cache = false;
p_sys->sms_streams = vlc_array_new();
p_sys->selected_st = vlc_array_new();
p_sys->download.chunks = vlc_array_new();
if( unlikely( !p_sys->sms_streams || !p_sys->download.chunks ) )
if( unlikely( !p_sys->sms_streams || !p_sys->download.chunks ||
!p_sys->selected_st ) )
{
free( p_sys );
return VLC_ENOMEM;
......@@ -423,34 +425,14 @@ static int Open( vlc_object_t *p_this )
p_sys->i_tracks = vlc_array_count( p_sys->sms_streams );
/* FIXME */
p_sys->i_selected_tracks = 2; /* one video track and one audio track */
/* Choose first video stream available */
sms_stream_t *vsms = NULL;
/* Choose first video / audio / subtitle stream available */
sms_stream_t *tmp = NULL, *selected = NULL;
for( unsigned i = 0; i < p_sys->i_tracks; i++ )
{
vsms = vlc_array_item_at_index( p_sys->sms_streams, i );
if( vsms->type == VIDEO_ES )
{
msg_Dbg( s, "Video stream chosen is %s", vsms->name );
p_sys->vstream = vsms;
break;
}
}
/* Choose first audio stream available */
sms_stream_t *asms = NULL;
for( unsigned i = 0; i < p_sys->i_tracks; i++ )
{
asms = vlc_array_item_at_index( p_sys->sms_streams, i );
//if( asms->type == AUDIO_ES && !strcmp( asms->name, "audio_eng" ) )
if( asms->type == AUDIO_ES )
{
msg_Dbg( s, "Audio stream chosen is %s", asms->name );
p_sys->astream = asms;
break;
}
tmp = vlc_array_item_at_index( p_sys->sms_streams, i );
selected = SMS_GET_SELECTED_ST( tmp->type );
if( !selected )
vlc_array_append( p_sys->selected_st, tmp );
}
/* Choose lowest quality for the first chunks */
......@@ -498,7 +480,8 @@ static void Close( vlc_object_t *p_this )
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;
for( int i = 0; i < 3; i++ )
p_sys->download.lead[i] = 0;
p_sys->playback.toffset = 0;
vlc_cond_signal(&p_sys->download.wait);
vlc_mutex_unlock( &p_sys->download.lock_wait );
......@@ -544,9 +527,7 @@ static chunk_t *get_chunk( stream_t *s, const bool wait )
p_sys->playback.index );
return NULL;
}
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) )
if( NO_MORE_CHUNKS )
{
vlc_mutex_unlock( &p_sys->download.lock_wait );
msg_Info( s, "No more chunks, end of the VOD" );
......@@ -580,7 +561,8 @@ static int sms_Read( stream_t *s, uint8_t *p_read, int i_read )
if( chunk->read_pos >= (int)chunk->size )
{
if( chunk->type == VIDEO_ES || !p_sys->vstream )
if( chunk->type == VIDEO_ES ||
( !SMS_GET_SELECTED_ST( VIDEO_ES ) && chunk->type == AUDIO_ES ) )
{
vlc_mutex_lock( &p_sys->download.lock_wait );
p_sys->playback.toffset += chunk->duration;
......@@ -727,7 +709,8 @@ static int chunk_Seek( stream_t *s, const uint64_t pos )
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;
for( int i = 0; i < 3; i++ )
p_sys->download.lead[i] = 0;
p_sys->playback.toffset = 0;
vlc_cond_signal( &p_sys->download.wait);
......
......@@ -89,12 +89,9 @@ struct stream_sys_t
char *base_url; /* URL common part for chunks */
vlc_thread_t thread; /* SMS chunk download thread */
vlc_array_t *sms_streams; /* array of sms_stream_t */
sms_stream_t *vstream; /* current video stream */
sms_stream_t *astream; /* current audio stream */
sms_stream_t *tstream; /* current text stream */
vlc_array_t *sms_streams; /* available streams */
vlc_array_t *selected_st; /* selected streams */
unsigned i_tracks; /* Total number of tracks in the Manifest */
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 */
int64_t time_pos;
......@@ -103,13 +100,10 @@ struct stream_sys_t
/* Download */
struct sms_download_s
{
uint64_t alead; // how much audio/video/text data is
uint64_t vlead; // available (downloaded),
uint64_t tlead; // in seconds / TimeScale
uint64_t lead[3]; /* how much audio/video/text data is available
(downloaded), in seconds / TimeScale */
unsigned aindex; /* current audio chunk for download */
unsigned vindex; /* video */
unsigned sindex; /* spu */
unsigned ck_index[3]; /* current chunk for download */
uint64_t next_chunk_offset;
vlc_array_t *chunks; /* chunks that have been downloaded */
......@@ -164,6 +158,12 @@ struct stream_sys_t
slice += 4; \
} while(0)
#define SMS_GET_SELECTED_ST( cat ) \
sms_get_stream_by_cat( p_sys->selected_st, cat )
#define NO_MORE_CHUNKS !p_sys->b_live && \
no_more_chunks( p_sys->download.ck_index, p_sys->selected_st )
sms_queue_t *sms_queue_init( const int );
int sms_queue_put( sms_queue_t *, const uint64_t );
uint64_t sms_queue_avg( sms_queue_t *);
......@@ -176,5 +176,9 @@ 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 * );
sms_stream_t * sms_get_stream_by_cat( vlc_array_t *, int );
bool no_more_chunks( unsigned[], vlc_array_t *);
int index_to_es_cat( int );
int es_cat_to_index( int );
#endif
......@@ -202,3 +202,63 @@ uint64_t sms_queue_avg( sms_queue_t *queue )
}
return sum / queue->length;
}
sms_stream_t * sms_get_stream_by_cat( vlc_array_t *streams, int i_cat )
{
sms_stream_t *ret = NULL;
int count = vlc_array_count( streams );
assert( count >= 0 && count <= 3 );
for( int i = 0; i < count; i++ )
{
ret = vlc_array_item_at_index( streams, i );
if( ret->type == i_cat )
return ret;
}
return NULL;
}
int es_cat_to_index( int i_cat )
{
switch( i_cat )
{
case VIDEO_ES:
return 0;
case AUDIO_ES:
return 1;
case SPU_ES:
return 2;
default:
return -1;
}
}
int index_to_es_cat( int index )
{
switch( index )
{
case 0:
return VIDEO_ES;
case 1:
return AUDIO_ES;
case 2:
return SPU_ES;
default:
return -1;
}
}
bool no_more_chunks( unsigned *indexes, vlc_array_t *streams )
{
sms_stream_t *sms = NULL;
int count = vlc_array_count( streams );
unsigned ck_index;
for( int i = 0; i < count; i++ )
{
sms = vlc_array_item_at_index( streams, i );
ck_index = indexes[es_cat_to_index( sms->type )];
if( ck_index < sms->vod_chunks_nb - 1 )
return false;
}
return true;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment