Commit 68d21786 authored by Laurent Aimar's avatar Laurent Aimar

* all: rework of the input.

parent 7d01a7e7
......@@ -25,33 +25,7 @@
#include <vlc/vlc.h>
#include <vlc/input.h>
#include "ninput.h"
int access_vaControl( input_thread_t *p_input, int i_query, va_list args )
{
if( p_input->pf_access_control )
{
return p_input->pf_access_control( p_input, i_query, args );
}
return VLC_EGENERIC;
}
int access_Control( input_thread_t *p_input, int i_query, ... )
{
va_list args;
int i_result;
va_start( args, i_query );
i_result = access_vaControl( p_input, i_query, args );
va_end( args );
return i_result;
}
int access_vaControlDefault( input_thread_t *p_input, int i_query, va_list args )
{
return VLC_EGENERIC;
}
#include "input_internal.h"
/*****************************************************************************
* access2_New:
......
......@@ -24,14 +24,11 @@
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <string.h> /* memcpy(), memset() */
#include <stdlib.h>
#include <vlc/vlc.h>
#include "stream_control.h"
#include "input_ext-intf.h"
#include "input_ext-dec.h"
#include "input_ext-plugins.h"
#include <vlc/input.h>
#include "input_internal.h"
/*
* DISCUSSION : SYNCHRONIZATION METHOD
......@@ -67,7 +64,8 @@
* new_average = (old_average * c_average + new_sample_value) / (c_average +1)
*/
static void ClockNewRef( pgrm_descriptor_t * p_pgrm,
static void ClockNewRef( input_clock_t * p_pgrm,
mtime_t i_clock, mtime_t i_sysdate );
/*****************************************************************************
......@@ -84,19 +82,19 @@ static void ClockNewRef( pgrm_descriptor_t * p_pgrm,
/*****************************************************************************
* ClockToSysdate: converts a movie clock to system date
*****************************************************************************/
static mtime_t ClockToSysdate( input_thread_t * p_input,
pgrm_descriptor_t * p_pgrm, mtime_t i_clock )
static mtime_t ClockToSysdate( input_thread_t *p_input,
input_clock_t *cl, mtime_t i_clock )
{
mtime_t i_sysdate = 0;
if( p_pgrm->i_synchro_state == SYNCHRO_OK )
if( cl->i_synchro_state == SYNCHRO_OK )
{
i_sysdate = (mtime_t)(i_clock - p_pgrm->cr_ref)
* (mtime_t)p_input->stream.control.i_rate
i_sysdate = (mtime_t)(i_clock - cl->cr_ref)
* (mtime_t)p_input->i_rate
* (mtime_t)300;
i_sysdate /= 27;
i_sysdate /= 1000;
i_sysdate += (mtime_t)p_pgrm->sysdate_ref;
i_sysdate += (mtime_t)cl->sysdate_ref;
}
return( i_sysdate );
......@@ -107,46 +105,54 @@ static mtime_t ClockToSysdate( input_thread_t * p_input,
*****************************************************************************
* Caution : the synchro state must be SYNCHRO_OK for this to operate.
*****************************************************************************/
static mtime_t ClockCurrent( input_thread_t * p_input,
pgrm_descriptor_t * p_pgrm )
static mtime_t ClockCurrent( input_thread_t *p_input,
input_clock_t *cl )
{
return( (mdate() - p_pgrm->sysdate_ref) * 27 * DEFAULT_RATE
/ p_input->stream.control.i_rate / 300
+ p_pgrm->cr_ref );
return( (mdate() - cl->sysdate_ref) * 27 * INPUT_RATE_DEFAULT
/ p_input->i_rate / 300
+ cl->cr_ref );
}
/*****************************************************************************
* ClockNewRef: writes a new clock reference
*****************************************************************************/
static void ClockNewRef( pgrm_descriptor_t * p_pgrm,
static void ClockNewRef( input_clock_t *cl,
mtime_t i_clock, mtime_t i_sysdate )
{
p_pgrm->cr_ref = i_clock;
p_pgrm->sysdate_ref = i_sysdate ;
cl->cr_ref = i_clock;
cl->sysdate_ref = i_sysdate ;
}
/*****************************************************************************
* input_ClockInit: reinitializes the clock reference after a stream
* discontinuity
*****************************************************************************/
void input_ClockInit( pgrm_descriptor_t * p_pgrm )
void input_ClockInit( input_clock_t *cl, vlc_bool_t b_master, int i_cr_average )
{
p_pgrm->last_cr = 0;
p_pgrm->last_pts = 0;
p_pgrm->cr_ref = 0;
p_pgrm->sysdate_ref = 0;
p_pgrm->delta_cr = 0;
p_pgrm->c_average_count = 0;
cl->i_synchro_state = SYNCHRO_START;
cl->last_cr = 0;
cl->last_pts = 0;
cl->cr_ref = 0;
cl->sysdate_ref = 0;
cl->delta_cr = 0;
cl->c_average_count = 0;
cl->i_cr_average = i_cr_average;
cl->b_master = b_master;
}
#if 0
/*****************************************************************************
* input_ClockManageControl: handles the messages from the interface
*****************************************************************************
* Returns UNDEF_S if nothing happened, PAUSE_S if the stream was paused
*****************************************************************************/
int input_ClockManageControl( input_thread_t * p_input,
pgrm_descriptor_t * p_pgrm, mtime_t i_clock )
input_clock_t *cl, mtime_t i_clock )
{
#if 0
vlc_value_t val;
int i_return_value = UNDEF_S;
......@@ -221,36 +227,32 @@ int input_ClockManageControl( input_thread_t * p_input,
vlc_mutex_unlock( &p_input->stream.stream_lock );
return( i_return_value );
#endif
return UNDEF_S;
}
#endif
/*****************************************************************************
* input_ClockManageRef: manages a clock reference
* input_ClockSetPCR: manages a clock reference
*****************************************************************************/
void input_ClockManageRef( input_thread_t * p_input,
pgrm_descriptor_t * p_pgrm, mtime_t i_clock )
void input_ClockSetPCR( input_thread_t *p_input,
input_clock_t *cl, mtime_t i_clock )
{
/* take selected program if none specified */
if( !p_pgrm )
{
p_pgrm = p_input->stream.p_selected_program;
}
if( ( p_pgrm->i_synchro_state != SYNCHRO_OK ) ||
( i_clock == 0 && p_pgrm->last_cr != 0 ) )
if( ( cl->i_synchro_state != SYNCHRO_OK ) ||
( i_clock == 0 && cl->last_cr != 0 ) )
{
/* Feed synchro with a new reference point. */
ClockNewRef( p_pgrm, i_clock,
p_pgrm->last_pts + CR_MEAN_PTS_GAP > mdate() ?
p_pgrm->last_pts + CR_MEAN_PTS_GAP : mdate() );
p_pgrm->i_synchro_state = SYNCHRO_OK;
ClockNewRef( cl, i_clock,
cl->last_pts + CR_MEAN_PTS_GAP > mdate() ?
cl->last_pts + CR_MEAN_PTS_GAP : mdate() );
cl->i_synchro_state = SYNCHRO_OK;
if( p_input->stream.b_pace_control
&& p_input->stream.p_selected_program == p_pgrm )
if( p_input->b_can_pace_control && cl->b_master )
{
p_pgrm->last_cr = i_clock;
cl->last_cr = i_clock;
if( !p_input->b_out_pace_control )
{
mtime_t i_wakeup = ClockToSysdate( p_input, p_pgrm, i_clock );
mtime_t i_wakeup = ClockToSysdate( p_input, cl, i_clock );
while( (i_wakeup - mdate()) / CLOCK_FREQ > 1 )
{
msleep( CLOCK_FREQ );
......@@ -261,37 +263,38 @@ void input_ClockManageRef( input_thread_t * p_input,
}
else
{
p_pgrm->last_cr = 0;
p_pgrm->delta_cr = 0;
p_pgrm->c_average_count = 0;
cl->last_cr = 0;
cl->delta_cr = 0;
cl->c_average_count = 0;
}
}
else
{
if ( p_pgrm->last_cr != 0 &&
( (p_pgrm->last_cr - i_clock) > CR_MAX_GAP
|| (p_pgrm->last_cr - i_clock) < - CR_MAX_GAP ) )
if ( cl->last_cr != 0 &&
( (cl->last_cr - i_clock) > CR_MAX_GAP
|| (cl->last_cr - i_clock) < - CR_MAX_GAP ) )
{
/* Stream discontinuity, for which we haven't received a
* warning from the stream control facilities (dd-edited
* stream ?). */
msg_Warn( p_input, "clock gap, unexpected stream discontinuity" );
input_ClockInit( p_pgrm );
p_pgrm->i_synchro_state = SYNCHRO_START;
input_ClockInit( cl, cl->b_master, cl->i_cr_average );
/* FIXME needed ? */
#if 0
input_EscapeDiscontinuity( p_input );
#endif
}
p_pgrm->last_cr = i_clock;
cl->last_cr = i_clock;
if( p_input->stream.b_pace_control
&& p_input->stream.p_selected_program == p_pgrm )
if( p_input->b_can_pace_control && cl->b_master )
{
/* Wait a while before delivering the packets to the decoder.
* In case of multiple programs, we arbitrarily follow the
* clock of the selected program. */
if( !p_input->b_out_pace_control )
{
mtime_t i_wakeup = ClockToSysdate( p_input, p_pgrm, i_clock );
mtime_t i_wakeup = ClockToSysdate( p_input, cl, i_clock );
while( (i_wakeup - mdate()) / CLOCK_FREQ > 1 )
{
msleep( CLOCK_FREQ );
......@@ -299,30 +302,32 @@ void input_ClockManageRef( input_thread_t * p_input,
}
mwait( i_wakeup );
}
/* FIXME Not needed anymore ? */
#if 0
/* Now take into account interface changes. */
input_ClockManageControl( p_input, p_pgrm, i_clock );
input_ClockManageControl( p_input, cl, i_clock );
#endif
}
else
{
/* Smooth clock reference variations. */
mtime_t i_extrapoled_clock = ClockCurrent( p_input, p_pgrm );
mtime_t i_extrapoled_clock = ClockCurrent( p_input, cl );
/* Bresenham algorithm to smooth variations. */
if( p_pgrm->c_average_count == p_input->i_cr_average )
if( cl->c_average_count == cl->i_cr_average )
{
p_pgrm->delta_cr = ( p_pgrm->delta_cr
* (p_input->i_cr_average - 1)
cl->delta_cr = ( cl->delta_cr
* (cl->i_cr_average - 1)
+ ( i_extrapoled_clock - i_clock ) )
/ p_input->i_cr_average;
/ cl->i_cr_average;
}
else
{
p_pgrm->delta_cr = ( p_pgrm->delta_cr
* p_pgrm->c_average_count
cl->delta_cr = ( cl->delta_cr
* cl->c_average_count
+ ( i_extrapoled_clock - i_clock ) )
/ (p_pgrm->c_average_count + 1);
p_pgrm->c_average_count++;
/ (cl->c_average_count + 1);
cl->c_average_count++;
}
}
}
......@@ -332,23 +337,12 @@ void input_ClockManageRef( input_thread_t * p_input,
* input_ClockGetTS: manages a PTS or DTS
*****************************************************************************/
mtime_t input_ClockGetTS( input_thread_t * p_input,
pgrm_descriptor_t * p_pgrm, mtime_t i_ts )
input_clock_t *cl, mtime_t i_ts )
{
/* take selected program if none specified */
if( !p_pgrm )
{
p_pgrm = p_input->stream.p_selected_program;
}
if( p_pgrm->i_synchro_state == SYNCHRO_OK )
{
p_pgrm->last_pts = ClockToSysdate( p_input, p_pgrm,
i_ts + p_pgrm->delta_cr );
return( p_pgrm->last_pts + p_input->i_pts_delay );
}
else
{
if( cl->i_synchro_state != SYNCHRO_OK )
return 0;
}
cl->last_pts = ClockToSysdate( p_input, cl, i_ts + cl->delta_cr );
return cl->last_pts + p_input->i_pts_delay;
}
......@@ -22,22 +22,12 @@
*****************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <vlc/vlc.h>
#include <vlc/input.h>
#include "input_internal.h"
#include "vlc_playlist.h"
#include "ninput.h"
#include "../../modules/demux/util/sub.h"
struct input_thread_sys_t
{
/* subtitles */
int i_sub;
subtitle_demux_t **sub;
int64_t i_stop_time;
};
static void UpdateBookmarksOption( input_thread_t * );
......@@ -64,7 +54,6 @@ int input_Control( input_thread_t *p_input, int i_query, ... )
int input_vaControl( input_thread_t *p_input, int i_query, va_list args )
{
int i_ret;
seekpoint_t *p_bkmk, ***ppp_bkmk;
int i_bkmk = 0;
int *pi_bkmk;
......@@ -75,54 +64,50 @@ int input_vaControl( input_thread_t *p_input, int i_query, va_list args )
double f, *pf;
int64_t i_64, *pi_64;
vlc_mutex_lock( &p_input->stream.stream_lock );
switch( i_query )
{
case INPUT_GET_POSITION:
pf = (double*)va_arg( args, double * );
*pf = var_GetFloat( p_input, "position" );
i_ret = VLC_SUCCESS;
break;
return VLC_SUCCESS;
case INPUT_SET_POSITION:
f = (double)va_arg( args, double );
i_ret = var_SetFloat( p_input, "position", f );
break;
return var_SetFloat( p_input, "position", f );
case INPUT_GET_LENGTH:
pi_64 = (int64_t*)va_arg( args, int64_t * );
*pi_64 = var_GetTime( p_input, "length" );
i_ret = VLC_SUCCESS;
break;
return VLC_SUCCESS;
case INPUT_GET_TIME:
pi_64 = (int64_t*)va_arg( args, int64_t * );
*pi_64 = var_GetTime( p_input, "time" );
i_ret = VLC_SUCCESS;
break;
return VLC_SUCCESS;
case INPUT_SET_TIME:
i_64 = (int64_t)va_arg( args, int64_t );
i_ret = var_SetTime( p_input, "time", i_64 );
break;
return var_SetTime( p_input, "time", i_64 );
case INPUT_GET_RATE:
pi_int = (int*)va_arg( args, int * );
*pi_int = var_GetInteger( p_input, "rate" );
i_ret = VLC_SUCCESS;
break;
return VLC_SUCCESS;
case INPUT_SET_RATE:
i_int = (int)va_arg( args, int );
i_ret = var_SetInteger( p_input, "rate", i_int );
break;
return var_SetInteger( p_input, "rate", i_int );
case INPUT_GET_STATE:
pi_int = (int*)va_arg( args, int * );
*pi_int = var_GetInteger( p_input, "state" );
i_ret = VLC_SUCCESS;
break;
return VLC_SUCCESS;
case INPUT_SET_STATE:
i_int = (int)va_arg( args, int );
i_ret = var_SetInteger( p_input, "state", i_int );
break;
return var_SetInteger( p_input, "state", i_int );
#if 0
case INPUT_ADD_OPTION:
{
psz_option = (char *)va_arg( args, char * );
......@@ -130,7 +115,7 @@ int input_vaControl( input_thread_t *p_input, int i_query, va_list args )
i_ret = VLC_EGENERIC;
vlc_mutex_lock( &p_input->p_item->lock );
/* Check if option already exists */
/* Check if option already exists */
for( i = 0; i < p_input->p_item->i_options; i++ )
{
if( !strncmp( p_input->p_item->ppsz_options[i], psz_option,
......@@ -500,22 +485,33 @@ int input_vaControl( input_thread_t *p_input, int i_query, va_list args )
i_ret = VLC_EGENERIC;
}
break;
#endif
case INPUT_GET_BOOKMARKS:
case INPUT_CLEAR_BOOKMARKS:
case INPUT_ADD_BOOKMARK:
case INPUT_CHANGE_BOOKMARK:
case INPUT_DEL_BOOKMARK:
case INPUT_SET_BOOKMARK:
case INPUT_ADD_OPTION:
case INPUT_ADD_INFO:
case INPUT_GET_INFO:
case INPUT_SET_NAME:
case INPUT_GET_SUBDELAY:
case INPUT_SET_SUBDELAY:
/* FIXME */
msg_Err( p_input, "unimplemented query in input_vaControl" );
default:
msg_Err( p_input, "unknown query in input_vaControl" );
i_ret = VLC_EGENERIC;
break;
return VLC_EGENERIC;
}
vlc_mutex_unlock( &p_input->stream.stream_lock );
return i_ret;
}
static void UpdateBookmarksOption( input_thread_t *p_input )
{
int i, i_len = 0;
char *psz_value = NULL, *psz_next = NULL;
/* FIXME */
#if 0
vlc_mutex_unlock( &p_input->stream.stream_lock );
for( i = 0; i < p_input->i_bookmarks; i++ )
......@@ -546,4 +542,5 @@ static void UpdateBookmarksOption( input_thread_t *p_input )
psz_value ? psz_value : "" );
vlc_mutex_lock( &p_input->stream.stream_lock );
#endif
}
/*****************************************************************************
* input_dec.c: Functions for the management of decoders
* decoder.c: Functions for the management of decoders
*****************************************************************************
* Copyright (C) 1999-2004 VideoLAN
* $Id$
*
* Authors: Christophe Massiot <massiot@via.ecp.fr>
* Gildas Bazin <gbazin@netcourrier.com>
* Laurent Aimar <fenrir@via.ecp.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -26,25 +27,20 @@
* Preamble
*****************************************************************************/
#include <stdlib.h>
#include <string.h> /* memcpy(), memset() */
#include <vlc/vlc.h>
#include <vlc/decoder.h>
#include <vlc/vout.h>
#include <vlc/input.h>
#include "stream_output.h"
#include "input_internal.h"
#include "input_ext-intf.h"
#include "input_ext-plugins.h"
#include "codecs.h"
static void input_NullPacket( input_thread_t *, es_descriptor_t * );
static decoder_t * CreateDecoder( input_thread_t *, es_format_t *, int );
static void DeleteDecoder( decoder_t * );
static decoder_t * CreateDecoder( input_thread_t *, es_descriptor_t *, int );
static int DecoderThread( decoder_t * );
static int DecoderDecode( decoder_t * p_dec, block_t *p_block );
static void DeleteDecoder( decoder_t * );
/* Buffers allocation callbacks for the decoders */
static aout_buffer_t *aout_new_buffer( decoder_t *, int );
......@@ -81,10 +77,6 @@ struct decoder_owner_sys_t
/* fifo */
block_fifo_t *p_fifo;
/* */
input_buffers_t *p_method_data;
es_descriptor_t *p_es_descriptor;
};
......@@ -95,16 +87,17 @@ struct decoder_owner_sys_t
* \param p_es the es descriptor
* \return the spawned decoder object
*/
decoder_t * input_RunDecoder( input_thread_t * p_input, es_descriptor_t * p_es )
decoder_t *input_DecoderNew( input_thread_t *p_input,
es_format_t *fmt, vlc_bool_t b_force_decoder )
{
decoder_t *p_dec = NULL;
vlc_value_t val;
/* If we are in sout mode, search for packetizer module */
if( !p_es->b_force_decoder && p_input->stream.p_sout )
if( p_input->p_sout && !b_force_decoder )
{
/* Create the decoder configuration structure */
p_dec = CreateDecoder( p_input, p_es, VLC_OBJECT_PACKETIZER );
p_dec = CreateDecoder( p_input, fmt, VLC_OBJECT_PACKETIZER );
if( p_dec == NULL )
{
msg_Err( p_input, "could not create packetizer" );
......@@ -114,7 +107,7 @@ decoder_t * input_RunDecoder( input_thread_t * p_input, es_descriptor_t * p_es )
else
{
/* Create the decoder configuration structure */
p_dec = CreateDecoder( p_input, p_es, VLC_OBJECT_DECODER );
p_dec = CreateDecoder( p_input, fmt, VLC_OBJECT_DECODER );
if( p_dec == NULL )
{
msg_Err( p_input, "could not create decoder" );
......@@ -133,7 +126,8 @@ decoder_t * input_RunDecoder( input_thread_t * p_input, es_descriptor_t * p_es )
return NULL;
}
if( !p_es->b_force_decoder && p_input->stream.p_sout && p_input->stream.b_pace_control )
if( p_input->p_sout && p_input->input.b_can_pace_control &&
!b_force_decoder )
{
msg_Dbg( p_input, "stream out mode -> no decoder thread" );
p_dec->p_owner->b_own_thread = VLC_FALSE;
......@@ -147,14 +141,10 @@ decoder_t * input_RunDecoder( input_thread_t * p_input, es_descriptor_t * p_es )
if( p_dec->p_owner->b_own_thread )
{
int i_priority;
if ( p_es->i_cat == AUDIO_ES )
{
if( fmt->i_cat == AUDIO_ES )
i_priority = VLC_THREAD_PRIORITY_AUDIO;
}
else
{
i_priority = VLC_THREAD_PRIORITY_VIDEO;
}
/* Spawn the decoder thread */
if( vlc_thread_create( p_dec, "decoder", DecoderThread,
......@@ -169,14 +159,6 @@ decoder_t * input_RunDecoder( input_thread_t * p_input, es_descriptor_t * p_es )
}
}
/* Select a new ES */
INSERT_ELEM( p_input->stream.pp_selected_es,
p_input->stream.i_selected_es_number,
p_input->stream.i_selected_es_number,
p_es );
p_input->stream.b_changed = 1;
return p_dec;
}
......@@ -187,10 +169,8 @@ decoder_t * input_RunDecoder( input_thread_t * p_input, es_descriptor_t * p_es )
* \param p_es the es descriptor
* \return nothing
*/
void input_EndDecoder( input_thread_t * p_input, es_descriptor_t * p_es )
void input_DecoderDelete( decoder_t *p_dec )
{
decoder_t *p_dec = p_es->p_dec;
p_dec->b_die = VLC_TRUE;
if( p_dec->p_owner->b_own_thread )
......@@ -198,7 +178,7 @@ void input_EndDecoder( input_thread_t * p_input, es_descriptor_t * p_es )
/* Make sure the thread leaves the function by
* sending it an empty block. */
block_t *p_block = block_New( p_dec, 0 );
input_DecodeBlock( p_dec, p_block );
input_DecoderDecode( p_dec, p_block );
vlc_thread_join( p_dec );
......@@ -215,55 +195,6 @@ void input_EndDecoder( input_thread_t * p_input, es_descriptor_t * p_es )
/* Delete the decoder */
vlc_object_destroy( p_dec );
/* Tell the input there is no more decoder */
p_es->p_dec = NULL;
p_input->stream.b_changed = 1;
}
/**
* Put a PES in the decoder's fifo.
*
* \param p_dec the decoder object
* \param p_pes the pes packet
* \return nothing
*/
void input_DecodePES( decoder_t * p_dec, pes_packet_t * p_pes )
{
data_packet_t *p_data;
int i_size = 0;
for( p_data = p_pes->p_first; p_data != NULL; p_data = p_data->p_next )
{
i_size += p_data->p_payload_end - p_data->p_payload_start;
}
if( i_size > 0 )
{
block_t *p_block = block_New( p_dec, i_size );
if( p_block )
{
uint8_t *p_buffer = p_block->p_buffer;
for( p_data = p_pes->p_first; p_data; p_data = p_data->p_next )
{
int i_copy = p_data->p_payload_end - p_data->p_payload_start;
memcpy( p_buffer, p_data->p_payload_start, i_copy );
p_buffer += i_copy;
}
p_block->i_pts = p_pes->i_pts;
p_block->i_dts = p_pes->i_dts;
if( p_pes->b_discontinuity )
p_block->i_flags |= BLOCK_FLAG_DISCONTINUITY;
p_block->i_rate = p_pes->i_rate;
input_DecodeBlock( p_dec, p_block );
}
}
input_DeletePES( p_dec->p_owner->p_method_data, p_pes );
}
/**
......@@ -272,7 +203,7 @@ void input_DecodePES( decoder_t * p_dec, pes_packet_t * p_pes )
* \param p_dec the decoder object
* \param p_block the data block
*/
void input_DecodeBlock( decoder_t * p_dec, block_t *p_block )
void input_DecoderDecode( decoder_t * p_dec, block_t *p_block )
{
if( p_dec->p_owner->b_own_thread )
{
......@@ -301,6 +232,25 @@ void input_DecodeBlock( decoder_t * p_dec, block_t *p_block )
}
}
void input_DecoderDiscontinuity( decoder_t * p_dec )
{
block_t *p_null;
/* Empty the fifo */
if( p_dec->p_owner->b_own_thread )
{
block_FifoEmpty( p_dec->p_owner->p_fifo );
}
/* Send a special block */
p_null = block_New( p_dec, 128 );
p_null->i_flags |= BLOCK_FLAG_DISCONTINUITY;
memset( p_null->p_buffer, 0, p_null->i_buffer );
input_DecoderDecode( p_dec, p_null );
}
#if 0
/**
* Create a NULL packet for padding in case of a data loss
*
......@@ -311,6 +261,7 @@ void input_DecodeBlock( decoder_t * p_dec, block_t *p_block )
static void input_NullPacket( input_thread_t * p_input,
es_descriptor_t * p_es )
{
#if 0
block_t *p_block = block_New( p_input, PADDING_PACKET_SIZE );
if( p_block )
{
......@@ -319,6 +270,7 @@ static void input_NullPacket( input_thread_t * p_input,
block_FifoPut( p_es->p_dec->p_owner->p_fifo, p_block );
}
#endif
}
/**
......@@ -329,6 +281,7 @@ static void input_NullPacket( input_thread_t * p_input,
*/
void input_EscapeDiscontinuity( input_thread_t * p_input )
{
#if 0
unsigned int i_es, i;
for( i_es = 0; i_es < p_input->stream.i_selected_es_number; i_es++ )
......@@ -343,6 +296,7 @@ void input_EscapeDiscontinuity( input_thread_t * p_input )
}
}
}
#endif
}
/**
......@@ -353,6 +307,7 @@ void input_EscapeDiscontinuity( input_thread_t * p_input )
*/
void input_EscapeAudioDiscontinuity( input_thread_t * p_input )
{
#if 0
unsigned int i_es, i;
for( i_es = 0; i_es < p_input->stream.i_selected_es_number; i_es++ )
......@@ -367,7 +322,9 @@ void input_EscapeAudioDiscontinuity( input_thread_t * p_input )
}
}
}
#endif
}
#endif
/**
* Create a decoder object
......@@ -377,8 +334,8 @@ void input_EscapeAudioDiscontinuity( input_thread_t * p_input )
* \param i_object_type Object type as define in include/vlc_objects.h
* \return the decoder object
*/
static decoder_t * CreateDecoder( input_thread_t * p_input,
es_descriptor_t * p_es, int i_object_type )
static decoder_t * CreateDecoder( input_thread_t *p_input,
es_format_t *fmt, int i_object_type )
{
decoder_t *p_dec;
......@@ -397,67 +354,12 @@ static decoder_t * CreateDecoder( input_thread_t * p_input,
/* Initialize the decoder fifo */
p_dec->p_module = NULL;
es_format_Copy( &p_dec->fmt_in, &p_es->fmt );
if( p_es->p_waveformatex )
{
#define p_wf ((WAVEFORMATEX *)p_es->p_waveformatex)
p_dec->fmt_in.audio.i_channels = p_wf->nChannels;
p_dec->fmt_in.audio.i_rate = p_wf->nSamplesPerSec;
p_dec->fmt_in.i_bitrate = p_wf->nAvgBytesPerSec * 8;
p_dec->fmt_in.audio.i_blockalign = p_wf->nBlockAlign;
p_dec->fmt_in.audio.i_bitspersample = p_wf->wBitsPerSample;
p_dec->fmt_in.i_extra = p_wf->cbSize;
p_dec->fmt_in.p_extra = NULL;
if( p_wf->cbSize )
{
p_dec->fmt_in.p_extra = malloc( p_wf->cbSize );
memcpy( p_dec->fmt_in.p_extra, &p_wf[1], p_wf->cbSize );
}
}
if( p_es->p_bitmapinfoheader )
{
#define p_bih ((BITMAPINFOHEADER *) p_es->p_bitmapinfoheader)
p_dec->fmt_in.i_extra = p_bih->biSize - sizeof(BITMAPINFOHEADER);
p_dec->fmt_in.p_extra = NULL;
if( p_dec->fmt_in.i_extra )
{
p_dec->fmt_in.p_extra = malloc( p_dec->fmt_in.i_extra );
memcpy( p_dec->fmt_in.p_extra, &p_bih[1], p_dec->fmt_in.i_extra );
}
p_dec->fmt_in.video.i_width = p_bih->biWidth;
p_dec->fmt_in.video.i_height = p_bih->biHeight;
}
/* FIXME
* - 1: beurk
* - 2: I'm not sure there isn't any endian problem here (spu)... */
if( p_es->i_cat == SPU_ES && p_es->p_demux_data )
{
if( ( p_es->i_fourcc == VLC_FOURCC( 's', 'p', 'u', ' ' ) ||
p_es->i_fourcc == VLC_FOURCC( 's', 'p', 'u', 'b' ) ) &&
*((uint32_t*)p_es->p_demux_data) == 0xBeef )
{
memcpy( p_dec->fmt_in.subs.spu.palette,
p_es->p_demux_data, 17 * 4 );
}
else if( p_es->i_fourcc == VLC_FOURCC( 'd', 'v', 'b', 's' ) &&
p_es->p_spuinfo )
{
dvb_spuinfo_t *p_dvbs = (dvb_spuinfo_t*)p_es->p_spuinfo;
p_dec->fmt_in.subs.dvb.i_id = p_dvbs->i_id;
}
}
p_dec->fmt_in.i_cat = p_es->i_cat;
p_dec->fmt_in.i_codec = p_es->i_fourcc;
p_dec->fmt_out = null_es_format;
es_format_Copy( &p_dec->fmt_in, fmt );
es_format_Copy( &p_dec->fmt_out, &null_es_format );
/* Allocate our private structure for the decoder */
p_dec->p_owner = (decoder_owner_sys_t*)malloc(sizeof(decoder_owner_sys_t));
p_dec->p_owner = malloc( sizeof( decoder_owner_sys_t ) );
if( p_dec->p_owner == NULL )
{
msg_Err( p_dec, "out of memory" );
......@@ -468,10 +370,9 @@ static decoder_t * CreateDecoder( input_thread_t * p_input,
p_dec->p_owner->p_aout = NULL;
p_dec->p_owner->p_aout_input = NULL;
p_dec->p_owner->p_vout = NULL;
p_dec->p_owner->p_sout = p_input->stream.p_sout;
p_dec->p_owner->p_sout = p_input->p_sout;
p_dec->p_owner->p_sout_input = NULL;
p_dec->p_owner->p_packetizer = NULL;
p_dec->p_owner->p_es_descriptor = p_es;
/* decoder fifo */
......@@ -480,8 +381,6 @@ static decoder_t * CreateDecoder( input_thread_t * p_input,
msg_Err( p_dec, "out of memory" );
return NULL;
}
p_dec->p_owner->p_method_data = p_input->p_method_data;
/* Set buffers allocation callbacks for the decoders */
p_dec->pf_aout_buffer_new = aout_new_buffer;
p_dec->pf_aout_buffer_del = aout_del_buffer;
......@@ -506,11 +405,12 @@ static decoder_t * CreateDecoder( input_thread_t * p_input,
vlc_object_create( p_input, VLC_OBJECT_PACKETIZER );
if( p_dec->p_owner->p_packetizer )
{
p_dec->p_owner->p_packetizer->fmt_in = null_es_format;
p_dec->p_owner->p_packetizer->fmt_out = null_es_format;
es_format_Copy( &p_dec->p_owner->p_packetizer->fmt_in,
&p_dec->fmt_in );
es_format_Copy( &p_dec->p_owner->p_packetizer->fmt_out,
&null_es_format );
vlc_object_attach( p_dec->p_owner->p_packetizer, p_input );
p_dec->p_owner->p_packetizer->p_module =
......@@ -591,13 +491,9 @@ static int DecoderDecode( decoder_t *p_dec, block_t *p_block )
if( !p_dec->p_owner->p_sout_input )
{
es_format_Copy( &p_dec->p_owner->sout, &p_dec->fmt_out );
if( p_dec->p_owner->p_es_descriptor->p_pgrm )
{
p_dec->p_owner->sout.i_group =
p_dec->p_owner->p_es_descriptor->p_pgrm->i_number;
}
p_dec->p_owner->sout.i_id =
p_dec->p_owner->p_es_descriptor->i_id - 1;
p_dec->p_owner->sout.i_group =p_dec->fmt_in.i_group;
p_dec->p_owner->sout.i_id = p_dec->fmt_in.i_id;
if( p_dec->fmt_in.psz_language )
{
p_dec->p_owner->sout.psz_language =
......
......@@ -25,148 +25,11 @@
#include <vlc/vlc.h>
#include <vlc/input.h>
#include "ninput.h"
int demux_vaControl( input_thread_t *p_input, int i_query, va_list args )
{
if( p_input->pf_demux_control )
{
return p_input->pf_demux_control( p_input, i_query, args );
}
return VLC_EGENERIC;
}
int demux_Control( input_thread_t *p_input, int i_query, ... )
{
va_list args;
int i_result;
va_start( args, i_query );
i_result = demux_vaControl( p_input, i_query, args );
va_end( args );
return i_result;
}
static void SeekOffset( input_thread_t *p_input, int64_t i_pos );
int demux_vaControlDefault( input_thread_t *p_input, int i_query,
va_list args )
{
int i_ret;
double f, *pf;
int64_t i64, *pi64;
vlc_mutex_lock( &p_input->stream.stream_lock );
switch( i_query )
{
case DEMUX_GET_POSITION:
pf = (double*)va_arg( args, double * );
if( p_input->stream.p_selected_area->i_size <= 0 )
{
*pf = 0.0;
}
else
{
*pf = (double)p_input->stream.p_selected_area->i_tell /
(double)p_input->stream.p_selected_area->i_size;
}
i_ret = VLC_SUCCESS;
break;
case DEMUX_SET_POSITION:
f = (double)va_arg( args, double );
if( p_input->stream.b_seekable && p_input->pf_seek != NULL &&
f >= 0.0 && f <= 1.0 )
{
SeekOffset( p_input, (int64_t)(f *
(double)p_input->stream.p_selected_area->i_size) );
i_ret = VLC_SUCCESS;
}
else
{
i_ret = VLC_EGENERIC;
}
break;
case DEMUX_GET_TIME:
pi64 = (int64_t*)va_arg( args, int64_t * );
if( p_input->stream.i_mux_rate > 0 )
{
*pi64 = (int64_t)1000000 *
( p_input->stream.p_selected_area->i_tell / 50 ) /
p_input->stream.i_mux_rate;
i_ret = VLC_SUCCESS;
}
else
{
*pi64 = 0;
i_ret = VLC_EGENERIC;
}
break;
case DEMUX_SET_TIME:
i64 = (int64_t)va_arg( args, int64_t );
if( p_input->stream.i_mux_rate > 0 &&
p_input->stream.b_seekable &&
p_input->pf_seek != NULL && i64 >= 0 )
{
SeekOffset( p_input, i64 * 50 *
(int64_t)p_input->stream.i_mux_rate /
(int64_t)1000000 );
i_ret = VLC_SUCCESS;
}
else
{
i_ret = VLC_EGENERIC;
}
break;
case DEMUX_GET_LENGTH:
pi64 = (int64_t*)va_arg( args, int64_t * );
if( p_input->stream.i_mux_rate > 0 )
{
*pi64 = (int64_t)1000000 *
( p_input->stream.p_selected_area->i_size / 50 ) /
p_input->stream.i_mux_rate;
i_ret = VLC_SUCCESS;
}
else
{
*pi64 = 0;
i_ret = VLC_EGENERIC;
}
break;
case DEMUX_GET_FPS:
i_ret = VLC_EGENERIC;
break;
case DEMUX_GET_META:
i_ret = VLC_EGENERIC;
break;
default:
msg_Err( p_input, "unknown query in demux_vaControlDefault" );
i_ret = VLC_EGENERIC;
break;
}
vlc_mutex_unlock( &p_input->stream.stream_lock );
return i_ret;
}
static void SeekOffset( input_thread_t *p_input, int64_t i_pos )
{
/* Reinitialize buffer manager. */
input_AccessReinit( p_input );
vlc_mutex_unlock( &p_input->stream.stream_lock );
p_input->pf_seek( p_input, i_pos );
vlc_mutex_lock( &p_input->stream.stream_lock );
}
#include "input_internal.h"
/*****************************************************************************
* demux2_New:
* if s is NULL then load a access_demux
*****************************************************************************/
demux_t *__demux2_New( vlc_object_t *p_obj,
char *psz_access, char *psz_demux, char *psz_path,
......@@ -198,8 +61,12 @@ demux_t *__demux2_New( vlc_object_t *p_obj,
p_demux->info.i_title = 0;
p_demux->info.i_seekpoint = 0;
psz_module = p_demux->psz_demux;
if( *psz_module == '\0' && strrchr( p_demux->psz_path, '.' ) )
if( s )
psz_module = p_demux->psz_demux;
else
psz_module = p_demux->psz_access;
if( s && *psz_module == '\0' && strrchr( p_demux->psz_path, '.' ) )
{
/* XXX: add only file without any problem here and with strong detection.
* - no .mp3, .a52, ... (aac is added as it works only by file ext anyway
......@@ -241,9 +108,18 @@ demux_t *__demux2_New( vlc_object_t *p_obj,
/* Before module_Need (for var_Create...) */
vlc_object_attach( p_demux, p_obj );
p_demux->p_module =
module_Need( p_demux, "demux2", psz_module,
!strcmp( psz_module, p_demux->psz_demux ) ? VLC_TRUE : VLC_FALSE );
if( s )
{
p_demux->p_module =
module_Need( p_demux, "demux2", psz_module,
!strcmp( psz_module, p_demux->psz_demux ) ? VLC_TRUE : VLC_FALSE );
}
else
{
p_demux->p_module =
module_Need( p_demux, "access_demux", psz_module,
!strcmp( psz_module, p_demux->psz_access ) ? VLC_TRUE : VLC_FALSE );
}
if( p_demux->p_module == NULL )
{
......@@ -358,3 +234,263 @@ int demux2_vaControlHelper( stream_t *s,
}
}
/****************************************************************************
* stream_Demux*: create a demuxer for an outpout stream (allow demuxer chain)
****************************************************************************/
typedef struct
{
/* Data buffer */
vlc_mutex_t lock;
int i_buffer;
int i_buffer_size;
uint8_t *p_buffer;
int64_t i_pos;
/* Demuxer */
char *psz_name;
es_out_t *out;
demux_t *p_demux;
} d_stream_sys_t;
static int DStreamRead ( stream_t *, void *p_read, int i_read );
static int DStreamPeek ( stream_t *, uint8_t **pp_peek, int i_peek );
static int DStreamControl( stream_t *, int i_query, va_list );
static int DStreamThread ( stream_t * );
stream_t *__stream_DemuxNew( vlc_object_t *p_obj, char *psz_demux, es_out_t *out )
{
/* We create a stream reader, and launch a thread */
stream_t *s;
d_stream_sys_t *p_sys;
if( psz_demux == NULL || *psz_demux == '\0' )
{
return NULL;
}
s = vlc_object_create( p_obj, VLC_OBJECT_STREAM );
s->pf_block = NULL;
s->pf_read = DStreamRead;
s->pf_peek = DStreamPeek;
s->pf_control= DStreamControl;
s->p_sys = malloc( sizeof( d_stream_sys_t) );
p_sys = (d_stream_sys_t*)s->p_sys;
vlc_mutex_init( s, &p_sys->lock );
p_sys->i_buffer = 0;
p_sys->i_buffer_size = 1000000;
p_sys->p_buffer = malloc( p_sys->i_buffer_size );
p_sys->i_pos = 0;
p_sys->psz_name = strdup( psz_demux );
p_sys->out = out;
p_sys->p_demux = NULL;
if( vlc_thread_create( s, "stream out", DStreamThread, VLC_THREAD_PRIORITY_INPUT, VLC_FALSE ) )
{
vlc_mutex_destroy( &p_sys->lock );
vlc_object_destroy( s );
free( p_sys );
return NULL;
}
return s;
}
void stream_DemuxSend( stream_t *s, block_t *p_block )
{
d_stream_sys_t *p_sys = (d_stream_sys_t*)s->p_sys;
if( p_block->i_buffer > 0 )
{
vlc_mutex_lock( &p_sys->lock );
/* Realloc if needed */
if( p_sys->i_buffer + p_block->i_buffer > p_sys->i_buffer_size )
{
if( p_sys->i_buffer_size > 5000000 )
{
vlc_mutex_unlock( &p_sys->lock );
msg_Err( s, "stream_DemuxSend: buffer size > 5000000" );
block_Release( p_block );
return;
}
/* I know, it's more than needed but that's perfect */
p_sys->i_buffer_size += p_block->i_buffer;
/* FIXME won't work with PEEK -> segfault */
p_sys->p_buffer = realloc( p_sys->p_buffer, p_sys->i_buffer_size );
msg_Dbg( s, "stream_DemuxSend: realloc to %d", p_sys->i_buffer_size );
}
/* copy data */
memcpy( &p_sys->p_buffer[p_sys->i_buffer], p_block->p_buffer, p_block->i_buffer );
p_sys->i_buffer += p_block->i_buffer;
vlc_mutex_unlock( &p_sys->lock );
}
block_Release( p_block );
}
void stream_DemuxDelete( stream_t *s )
{
d_stream_sys_t *p_sys = (d_stream_sys_t*)s->p_sys;
s->b_die = VLC_TRUE;
vlc_mutex_lock( &p_sys->lock );
if( p_sys->p_demux )
{
p_sys->p_demux->b_die = VLC_TRUE;
}
vlc_mutex_unlock( &p_sys->lock );
vlc_thread_join( s );
if( p_sys->p_demux )
{
demux2_Delete( p_sys->p_demux );
}
vlc_mutex_destroy( &p_sys->lock );
free( p_sys->psz_name );
free( p_sys->p_buffer );
free( p_sys );
vlc_object_destroy( s );
}
static int DStreamRead ( stream_t *s, void *p_read, int i_read )
{
d_stream_sys_t *p_sys = (d_stream_sys_t*)s->p_sys;
int i_copy;
//msg_Dbg( s, "DStreamRead: wanted %d bytes", i_read );
for( ;; )
{
vlc_mutex_lock( &p_sys->lock );
//msg_Dbg( s, "DStreamRead: buffer %d", p_sys->i_buffer );
if( p_sys->i_buffer >= i_read || s->b_die )
{
break;
}
vlc_mutex_unlock( &p_sys->lock );
msleep( 10000 );
}
//msg_Dbg( s, "DStreamRead: read %d buffer %d", i_read, p_sys->i_buffer );
i_copy = __MIN( i_read, p_sys->i_buffer );
if( i_copy > 0 )
{
if( p_read )
{
memcpy( p_read, p_sys->p_buffer, i_copy );
}
p_sys->i_buffer -= i_copy;
p_sys->i_pos += i_copy;
if( p_sys->i_buffer > 0 )
{
memmove( p_sys->p_buffer, &p_sys->p_buffer[i_copy], p_sys->i_buffer );
}
}
vlc_mutex_unlock( &p_sys->lock );
return i_copy;
}
static int DStreamPeek ( stream_t *s, uint8_t **pp_peek, int i_peek )
{
d_stream_sys_t *p_sys = (d_stream_sys_t*)s->p_sys;
int i_copy;
//msg_Dbg( s, "DStreamPeek: wanted %d bytes", i_peek );
for( ;; )
{
vlc_mutex_lock( &p_sys->lock );
//msg_Dbg( s, "DStreamPeek: buffer %d", p_sys->i_buffer );
if( p_sys->i_buffer >= i_peek || s->b_die )
{
break;
}
vlc_mutex_unlock( &p_sys->lock );
msleep( 10000 );
}
*pp_peek = p_sys->p_buffer;
i_copy = __MIN( i_peek, p_sys->i_buffer );
vlc_mutex_unlock( &p_sys->lock );
return i_copy;
}
static int DStreamControl( stream_t *s, int i_query, va_list args )
{
d_stream_sys_t *p_sys = (d_stream_sys_t*)s->p_sys;
int64_t *p_i64;
vlc_bool_t *p_b;
int *p_int;
switch( i_query )
{
case STREAM_GET_SIZE:
p_i64 = (int64_t*) va_arg( args, int64_t * );
*p_i64 = 0;
return VLC_SUCCESS;
case STREAM_CAN_SEEK:
p_b = (vlc_bool_t*) va_arg( args, vlc_bool_t * );
*p_b = VLC_FALSE;
return VLC_SUCCESS;
case STREAM_CAN_FASTSEEK:
p_b = (vlc_bool_t*) va_arg( args, vlc_bool_t * );
*p_b = VLC_FALSE;
return VLC_SUCCESS;
case STREAM_GET_POSITION:
p_i64 = (int64_t*) va_arg( args, int64_t * );
*p_i64 = p_sys->i_pos;
return VLC_SUCCESS;
case STREAM_SET_POSITION:
return VLC_EGENERIC;
case STREAM_GET_MTU:
p_int = (int*) va_arg( args, int * );
*p_int = 0;
return VLC_SUCCESS;
default:
msg_Err( s, "invalid DStreamControl query=0x%x", i_query );
return VLC_EGENERIC;
}
}
static int DStreamThread ( stream_t *s )
{
d_stream_sys_t *p_sys = (d_stream_sys_t*)s->p_sys;
demux_t *p_demux;
/* Create the demuxer */
if( ( p_demux = demux2_New( s, "", p_sys->psz_name, "", s, p_sys->out ) ) == NULL )
{
return VLC_EGENERIC;
}
vlc_mutex_lock( &p_sys->lock );
p_sys->p_demux = p_demux;
vlc_mutex_unlock( &p_sys->lock );
/* Main loop */
while( !s->b_die && !p_demux->b_die )
{
if( p_demux->pf_demux( p_demux ) <= 0 )
{
break;
}
}
p_demux->b_die = VLC_TRUE;
return VLC_SUCCESS;
}
......@@ -30,26 +30,53 @@
#include <vlc/input.h>
#include <vlc/decoder.h>
#include "input_internal.h"
#include "vlc_playlist.h"
#include "codecs.h"
#include "iso_lang.h"
/*****************************************************************************
* Local prototypes
*****************************************************************************/
typedef struct
{
/* Program ID */
int i_id;
/* Number of es for this pgrm */
int i_es;
vlc_bool_t b_selected;
/* Clock for this program */
input_clock_t clock;
} es_out_pgrm_t;
struct es_out_id_t
{
int i_channel;
es_descriptor_t *p_es;
/* ES ID */
int i_id;
es_out_pgrm_t *p_pgrm;
/* Channel in the track type */
int i_channel;
es_format_t fmt;
char *psz_description;
decoder_t *p_dec;
};
struct es_out_sys_t
{
input_thread_t *p_input;
/* all programs */
int i_pgrm;
es_out_pgrm_t **pgrm;
es_out_pgrm_t *p_pgrm; /* Master program */
/* all es */
int i_id;
int i_es;
es_out_id_t **es;
......@@ -75,16 +102,17 @@ struct es_out_sys_t
static es_out_id_t *EsOutAdd ( es_out_t *, es_format_t * );
static int EsOutSend ( es_out_t *, es_out_id_t *, block_t * );
static void EsOutDel ( es_out_t *, es_out_id_t * );
static void EsOutSelect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_force );
static int EsOutControl( es_out_t *, int i_query, va_list );
static void EsSelect( es_out_t *out, es_out_id_t *es );
static void EsUnselect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_update );
static char *LanguageGetName( const char *psz_code );
/**
* Create a new es_out structure
*
* \param p_input The related input thread
* \return the new es_out_t
*/
/*****************************************************************************
* input_EsOutNew:
*****************************************************************************/
es_out_t *input_EsOutNew( input_thread_t *p_input )
{
es_out_t *out = malloc( sizeof( es_out_t ) );
......@@ -102,8 +130,12 @@ es_out_t *input_EsOutNew( input_thread_t *p_input )
p_sys->b_active = VLC_FALSE;
p_sys->i_mode = ES_OUT_MODE_AUTO;
p_sys->i_id = 1;
p_sys->i_pgrm = 0;
p_sys->pgrm = NULL;
p_sys->p_pgrm = NULL;
p_sys->i_id = 0;
p_sys->i_es = 0;
p_sys->es = NULL;
......@@ -124,12 +156,9 @@ es_out_t *input_EsOutNew( input_thread_t *p_input )
return out;
}
/**
* Deletes an es_out structure
*
* \param out the es_out structure to destroy
* \return nothing
*/
/*****************************************************************************
* input_EsOutDelete:
*****************************************************************************/
void input_EsOutDelete( es_out_t *out )
{
es_out_sys_t *p_sys = out->p_sys;
......@@ -137,191 +166,249 @@ void input_EsOutDelete( es_out_t *out )
for( i = 0; i < p_sys->i_es; i++ )
{
if( p_sys->es[i]->p_dec )
{
input_DecoderDelete( p_sys->es[i]->p_dec );
}
if( p_sys->es[i]->psz_description )
free( p_sys->es[i]->psz_description );
es_format_Clean( &p_sys->es[i]->fmt );
free( p_sys->es[i] );
}
if( p_sys->es )
{
free( p_sys->es );
for( i = 0; i < p_sys->i_pgrm; i++ )
{
free( p_sys->pgrm[i] );
}
if( p_sys->pgrm )
free( p_sys->pgrm );
free( p_sys );
free( out );
}
/**
* Add a program
*
* \param out the es_out
* \param i_group ...
* \return a program descriptor for the new program
*/
static pgrm_descriptor_t *EsOutAddProgram( es_out_t *out, int i_group )
es_out_id_t *input_EsOutGetFromID( es_out_t *out, int i_id )
{
input_thread_t *p_input = out->p_sys->p_input;
pgrm_descriptor_t *p_pgrm;
es_descriptor_t *p_pmt;
/* FIXME we should use a object variable but a lot of place in src/input
* have to be changed */
int i_select = config_GetInt( p_input, "program" );
int i;
if( i_id < 0 )
{
/* Special HACK, -i_id is tha cat of the stream */
return (es_out_id_t*)((uint8_t*)NULL-i_id);
}
/* create it */
p_pgrm = input_AddProgram( p_input, i_group, 0 );
for( i = 0; i < out->p_sys->i_es; i++ )
{
if( out->p_sys->es[i]->i_id == i_id )
return out->p_sys->es[i];
}
return NULL;
}
/* XXX welcome to kludge, add a dummy es, if you want to understand
* why have a look at input_SetProgram. Basicaly, it assume the first
* es to be the PMT, how that is stupid, nevertheless it is needed for
* the old ts demuxer */
p_pmt = input_AddES( p_input, p_pgrm, 0, UNKNOWN_ES, NULL, 0 );
p_pmt->i_fourcc = VLC_FOURCC( 'n', 'u', 'l', 'l' );
void input_EsOutDiscontinuity( es_out_t *out, vlc_bool_t b_audio )
{
es_out_sys_t *p_sys = out->p_sys;
int i;
/* Select i_select or the first by default */
if( p_input->stream.p_selected_program == NULL &&
( i_select <= 0 || i_select == i_group ) )
for( i = 0; i < p_sys->i_es; i++ )
{
p_input->stream.p_selected_program = p_pgrm;
}
es_out_id_t *es = p_sys->es[i];
return p_pgrm;
/* Send a dummy block to let decoder know that
* there is a discontinuity */
if( es->p_dec && ( !b_audio || es->fmt.i_cat == AUDIO_ES ) )
{
input_DecoderDiscontinuity( es->p_dec );
}
}
}
/**
* Select an ES given the current mode
* XXX: you need to take a the lock before (stream.stream_lock)
/*****************************************************************************
*
* \param out The es_out structure
* \param es es_out_id structure
* \param b_force ...
* \return nothing
*/
static void EsOutSelect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_force )
*****************************************************************************/
static void EsOutESVarUpdate( es_out_t *out, es_out_id_t *es )
{
es_out_sys_t *p_sys = out->p_sys;
input_thread_t *p_input = p_sys->p_input;
vlc_value_t val, text;
int i_cat = es->p_es->i_cat;
char *psz_var;
if( !p_sys->b_active ||
( !b_force && es->p_es->fmt.i_priority < 0 ) )
{
if( es->fmt.i_cat == AUDIO_ES )
psz_var = "audio-es";
else if( es->fmt.i_cat == VIDEO_ES )
psz_var = "video-es";
else if( es->fmt.i_cat == SPU_ES )
psz_var = "spu-es";
else
return;
/* Get the number of ES already added */
var_Change( p_input, psz_var, VLC_VAR_CHOICESCOUNT, &val, NULL );
if( val.i_int == 0 )
{
vlc_value_t val2;
/* First one, we need to add the "Disable" choice */
val2.i_int = -1; text.psz_string = _("Disable");
var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val2, &text );
val.i_int++;
}
if( p_sys->i_mode == ES_OUT_MODE_ALL || b_force )
/* Take care of the ES description */
if( es->psz_description && *es->psz_description )
{
if( !es->p_es->p_dec )
{
input_SelectES( p_input, es->p_es );
}
text.psz_string = strdup( es->psz_description );
}
else if( p_sys->i_mode == ES_OUT_MODE_AUTO )
else
{
int i_wanted = -1;
text.psz_string = malloc( strlen( _("Track %i") ) + 20 );
sprintf( text.psz_string, _("Track %i"), val.i_int );
}
if( es->p_es->p_pgrm != NULL &&
es->p_es->p_pgrm != p_input->stream.p_selected_program )
{
return;
}
val.i_int = es->i_id;
var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val, &text );
if( i_cat == AUDIO_ES )
{
if( p_sys->p_es_audio &&
p_sys->p_es_audio->p_es->fmt.i_priority >=
es->p_es->fmt.i_priority )
{
return;
}
i_wanted = p_sys->i_audio_last >= 0 ?
p_sys->i_audio_last : es->i_channel;
}
else if( i_cat == SPU_ES )
{
if( p_sys->p_es_sub &&
p_sys->p_es_sub->p_es->fmt.i_priority >=
es->p_es->fmt.i_priority )
{
return;
}
i_wanted = p_sys->i_sub_last;
}
else if( i_cat == VIDEO_ES )
{
i_wanted = es->i_channel;
}
free( text.psz_string );
var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );
}
/* EsOutProgramSelect:
* Select a program and update the object variable
*/
static void EsOutProgramSelect( es_out_t *out, es_out_pgrm_t *p_pgrm )
{
es_out_sys_t *p_sys = out->p_sys;
input_thread_t *p_input = p_sys->p_input;
vlc_value_t val;
int i;
if( p_sys->p_pgrm == p_pgrm )
return; /* Nothing to do */
if( p_sys->p_pgrm )
{
es_out_pgrm_t *old = p_sys->p_pgrm;
msg_Dbg( p_input, "Unselecting program id=%d", old->i_id );
if( i_wanted == es->i_channel && es->p_es->p_dec == NULL )
for( i = 0; i < p_sys->i_es; i++ )
{
input_SelectES( p_input, es->p_es );
if( p_sys->es[i]->p_pgrm == old && p_sys->es[i]->p_dec &&
p_sys->i_mode != ES_OUT_MODE_ALL )
EsUnselect( out, p_sys->es[i], VLC_TRUE );
}
}
/* FIXME TODO handle priority here */
if( es->p_es->p_dec )
msg_Dbg( p_input, "Selecting program id=%d", p_pgrm->i_id );
/* Mark it selected */
p_pgrm->b_selected = VLC_TRUE;
/* Switch master stream */
if( p_sys->p_pgrm && p_sys->p_pgrm->clock.b_master )
{
if( i_cat == AUDIO_ES )
{
if( p_sys->i_mode == ES_OUT_MODE_AUTO &&
p_sys->p_es_audio && p_sys->p_es_audio->p_es->p_dec )
{
input_UnselectES( p_input, p_sys->p_es_audio->p_es );
}
p_sys->p_es_audio = es;
}
else if( i_cat == SPU_ES )
{
if( p_sys->i_mode == ES_OUT_MODE_AUTO &&
p_sys->p_es_sub && p_sys->p_es_sub->p_es->p_dec )
{
input_UnselectES( p_input, p_sys->p_es_sub->p_es );
}
p_sys->p_es_sub = es;
}
else if( i_cat == VIDEO_ES )
{
p_sys->p_es_video = es;
}
p_sys->p_pgrm->clock.b_master = VLC_FALSE;
}
p_pgrm->clock.b_master = VLC_TRUE;
p_sys->p_pgrm = p_pgrm;
/* Update "program" */
val.i_int = p_pgrm->i_id;
var_Change( p_input, "program", VLC_VAR_SETVALUE, &val, NULL );
/* Update "es-*" */
var_Change( p_input, "audio-es", VLC_VAR_CLEARCHOICES, NULL, NULL );
var_Change( p_input, "video-es", VLC_VAR_CLEARCHOICES, NULL, NULL );
var_Change( p_input, "spu-es", VLC_VAR_CLEARCHOICES, NULL, NULL );
for( i = 0; i < p_sys->i_es; i++ )
{
EsOutESVarUpdate( out, p_sys->es[i] );
EsOutSelect( out, p_sys->es[i], VLC_FALSE );
}
var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );
}
/* EsOutAddProgram:
* Add a program
*/
static es_out_pgrm_t *EsOutProgramAdd( es_out_t *out, int i_group )
{
es_out_sys_t *p_sys = out->p_sys;
input_thread_t *p_input = p_sys->p_input;
vlc_value_t val;
es_out_pgrm_t *p_pgrm = malloc( sizeof( es_out_pgrm_t ) );
/* Init */
p_pgrm->i_id = i_group;
p_pgrm->i_es = 0;
p_pgrm->b_selected = VLC_FALSE;
input_ClockInit( &p_pgrm->clock, VLC_FALSE, p_input->input.i_cr_average );
/* Append it */
TAB_APPEND( p_sys->i_pgrm, p_sys->pgrm, p_pgrm );
/* Update "program" variable */
val.i_int = i_group;
var_Change( p_input, "program", VLC_VAR_ADDCHOICE, &val, NULL );
if( i_group == var_GetInteger( p_input, "program" ) )
{
EsOutProgramSelect( out, p_pgrm );
}
else
{
var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );
}
return p_pgrm;
}
/**
* Add an es_out
*
* \param out the es_out to add
* \param fmt the es_format of the es_out
* \return an es_out id
/* EsOutAdd:
* Add an es_out
*/
static es_out_id_t *EsOutAdd( es_out_t *out, es_format_t *fmt )
{
es_out_sys_t *p_sys = out->p_sys;
input_thread_t *p_input = p_sys->p_input;
es_out_id_t *es = malloc( sizeof( es_out_id_t ) );
pgrm_descriptor_t *p_pgrm = NULL;
char psz_cat[sizeof( _("Stream ") ) + 10];
char *psz_description;
es_out_pgrm_t *p_pgrm = NULL;
int i;
vlc_mutex_lock( &p_input->stream.stream_lock );
if( fmt->i_group >= 0 )
if( fmt->i_group < 0 )
{
/* search program */
p_pgrm = input_FindProgram( p_input, fmt->i_group );
msg_Err( p_input, "invakud group number" );
return NULL;
}
if( p_pgrm == NULL )
/* Search the program */
for( i = 0; i < p_sys->i_pgrm; i++ )
{
if( fmt->i_group == p_sys->pgrm[i]->i_id )
{
/* Create it */
p_pgrm = EsOutAddProgram( out, fmt->i_group );
p_pgrm = p_sys->pgrm[i];
break;
}
}
if( fmt->i_id < 0 )
if( p_pgrm == NULL )
{
fmt->i_id = out->p_sys->i_id - 1;
/* Create a new one */
p_pgrm = EsOutProgramAdd( out, fmt->i_group );
}
psz_description = LanguageGetName( fmt->psz_language );
es->p_es = input_AddES( p_input, p_pgrm, fmt->i_id + 1,
fmt->i_cat, psz_description, 0 );
es->p_es->i_stream_id = fmt->i_id;
es->p_es->i_fourcc = fmt->i_codec;
/* Increase ref count for program */
p_pgrm->i_es++;
/* Set up ES */
if( fmt->i_id < 0 )
fmt->i_id = out->p_sys->i_id;
es->i_id = fmt->i_id;
es->p_pgrm = p_pgrm;
es_format_Copy( &es->fmt, fmt );
switch( fmt->i_cat )
{
case AUDIO_ES:
......@@ -340,9 +427,14 @@ static es_out_id_t *EsOutAdd( es_out_t *out, es_format_t *fmt )
es->i_channel = 0;
break;
}
es->psz_description = LanguageGetName( fmt->psz_language );
es->p_dec = NULL;
if( es->p_pgrm == p_sys->p_pgrm )
EsOutESVarUpdate( out, es );
#if 0
/* Add stream info */
vlc_mutex_unlock( &p_input->stream.stream_lock );
sprintf( psz_cat, _("Stream %d"), out->p_sys->i_id - 1 );
input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Codec"),
......@@ -406,19 +498,12 @@ static es_out_id_t *EsOutAdd( es_out_t *out, es_format_t *fmt )
default:
break;
}
vlc_mutex_lock( &p_input->stream.stream_lock );
free( psz_description );
#endif
es_format_Copy( &es->p_es->fmt, fmt );
/* Select it if needed */
EsOutSelect( out, es, VLC_FALSE );
/* Apply mode
* XXX change that when we do group too */
if( 1 )
{
EsOutSelect( out, es, VLC_FALSE );
}
vlc_mutex_unlock( &p_input->stream.stream_lock );
TAB_APPEND( out->p_sys->i_es, out->p_sys->es, es );
p_sys->i_id++; /* always incremented */
......@@ -438,6 +523,186 @@ static es_out_id_t *EsOutAdd( es_out_t *out, es_format_t *fmt )
return es;
}
static void EsSelect( es_out_t *out, es_out_id_t *es )
{
es_out_sys_t *p_sys = out->p_sys;
input_thread_t *p_input = p_sys->p_input;
vlc_value_t val;
char *psz_var;
if( es->p_dec )
{
msg_Warn( p_input, "ES 0x%x is already selected", es->i_id );
return;
}
if( es->fmt.i_cat == VIDEO_ES || es->fmt.i_cat == SPU_ES )
{
if( !var_GetBool( p_input, "video" ) || ( p_input->p_sout && !var_GetBool( p_input, "sout-video" ) ) )
{
msg_Dbg( p_input, "video is disabled, not selecting ES 0x%x", es->i_id );
return;
}
}
else if( es->fmt.i_cat == AUDIO_ES )
{
var_Get( p_input, "audio", &val );
if( !var_GetBool( p_input, "audio" ) || ( p_input->p_sout && !var_GetBool( p_input, "sout-audio" ) ) )
{
msg_Dbg( p_input, "audio is disabled, not selecting ES 0x%x", es->i_id );
return;
}
}
es->p_dec = input_DecoderNew( p_input, &es->fmt, VLC_FALSE );
if( es->p_dec == NULL || es->p_pgrm != p_sys->p_pgrm )
return;
if( es->fmt.i_cat == VIDEO_ES )
psz_var = "video-es";
else if( es->fmt.i_cat == AUDIO_ES )
psz_var = "audio-es";
else if( es->fmt.i_cat == SPU_ES )
psz_var = "spu-es";
else
return;
/* Mark it as selected */
val.i_int = es->i_id;
var_Change( p_input, psz_var, VLC_VAR_SETVALUE, &val, NULL );
var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );
}
static void EsUnselect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_update )
{
es_out_sys_t *p_sys = out->p_sys;
input_thread_t *p_input = p_sys->p_input;
vlc_value_t val;
char *psz_var;
if( es->p_dec == NULL )
{
msg_Warn( p_input, "ES 0x%x is already unselected", es->i_id );
return;
}
input_DecoderDelete( es->p_dec );
es->p_dec = NULL;
if( !b_update )
return;
/* Update var */
if( es->p_dec == NULL )
return;
if( es->fmt.i_cat == VIDEO_ES )
psz_var = "video-es";
else if( es->fmt.i_cat == AUDIO_ES )
psz_var = "audio-es";
else if( es->fmt.i_cat == SPU_ES )
psz_var = "spu-es";
else
return;
/* Mark it as selected */
val.i_int = -1;
var_Change( p_input, psz_var, VLC_VAR_SETVALUE, &val, NULL );
var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );
}
/**
* Select an ES given the current mode
* XXX: you need to take a the lock before (stream.stream_lock)
*
* \param out The es_out structure
* \param es es_out_id structure
* \param b_force ...
* \return nothing
*/
static void EsOutSelect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_force )
{
es_out_sys_t *p_sys = out->p_sys;
int i_cat = es->fmt.i_cat;
if( !p_sys->b_active ||
( !b_force && es->fmt.i_priority < 0 ) )
{
return;
}
if( p_sys->i_mode == ES_OUT_MODE_ALL || b_force )
{
if( !es->p_dec )
EsSelect( out, es );
}
else if( p_sys->i_mode == ES_OUT_MODE_AUTO )
{
int i_wanted = -1;
if( es->p_pgrm != p_sys->p_pgrm )
return;
if( i_cat == AUDIO_ES )
{
if( p_sys->p_es_audio &&
p_sys->p_es_audio->fmt.i_priority >= es->fmt.i_priority )
{
return;
}
i_wanted = p_sys->i_audio_last >= 0 ?
p_sys->i_audio_last : es->i_channel;
}
else if( i_cat == SPU_ES )
{
if( p_sys->p_es_sub &&
p_sys->p_es_sub->fmt.i_priority >=
es->fmt.i_priority )
{
return;
}
i_wanted = p_sys->i_sub_last;
}
else if( i_cat == VIDEO_ES )
{
i_wanted = es->i_channel;
}
if( i_wanted == es->i_channel && es->p_dec == NULL )
EsSelect( out, es );
}
/* FIXME TODO handle priority here */
if( es->p_dec )
{
if( i_cat == AUDIO_ES )
{
if( p_sys->i_mode == ES_OUT_MODE_AUTO &&
p_sys->p_es_audio && p_sys->p_es_audio->p_dec )
{
EsUnselect( out, p_sys->p_es_audio, VLC_FALSE );
}
p_sys->p_es_audio = es;
}
else if( i_cat == SPU_ES )
{
if( p_sys->i_mode == ES_OUT_MODE_AUTO &&
p_sys->p_es_sub && p_sys->p_es_sub->p_dec )
{
EsUnselect( out, p_sys->p_es_sub, VLC_FALSE );
}
p_sys->p_es_sub = es;
}
else if( i_cat == VIDEO_ES )
{
p_sys->p_es_video = es;
}
}
}
/**
* Send a block for the given es_out
*
......@@ -448,37 +713,30 @@ static es_out_id_t *EsOutAdd( es_out_t *out, es_format_t *fmt )
static int EsOutSend( es_out_t *out, es_out_id_t *es, block_t *p_block )
{
es_out_sys_t *p_sys = out->p_sys;
pgrm_descriptor_t *p_pgrm = es->p_es->p_pgrm;
input_thread_t *p_input = p_sys->p_input;
if( p_pgrm == NULL )
{
p_pgrm = p_sys->p_input->stream.p_selected_program;
}
es_out_pgrm_t *p_pgrm = es->p_pgrm;
/* +11 -> avoid null value with non null dts/pts */
if( p_block->i_dts > 0 && p_pgrm )
if( p_block->i_dts > 0 )
{
p_block->i_dts =
input_ClockGetTS( p_input, p_pgrm, ( p_block->i_dts + 11 ) * 9 / 100 );
input_ClockGetTS( p_input, &p_pgrm->clock, ( p_block->i_dts + 11 ) * 9 / 100 );
}
if( p_block->i_pts > 0 && p_pgrm )
if( p_block->i_pts > 0 )
{
p_block->i_pts =
input_ClockGetTS( p_input, p_pgrm, ( p_block->i_pts + 11 )* 9 / 100 );
input_ClockGetTS( p_input, &p_pgrm->clock, ( p_block->i_pts + 11 )* 9 / 100 );
}
vlc_mutex_lock( &out->p_sys->p_input->stream.stream_lock );
p_block->i_rate = out->p_sys->p_input->stream.control.i_rate;
if( es->p_es->p_dec &&
(es->p_es->i_cat!=AUDIO_ES || !p_sys->p_input->stream.control.b_mute) )
p_block->i_rate = p_input->i_rate;
/* TODO handle mute */
if( es->p_dec && ( es->fmt.i_cat != AUDIO_ES || p_input->i_rate == INPUT_RATE_DEFAULT ) )
{
vlc_mutex_unlock( &out->p_sys->p_input->stream.stream_lock );
input_DecodeBlock( es->p_es->p_dec, p_block );
input_DecoderDecode( es->p_dec, p_block );
}
else
{
vlc_mutex_unlock( &out->p_sys->p_input->stream.stream_lock );
block_Release( p_block );
}
......@@ -492,9 +750,23 @@ static void EsOutDel( es_out_t *out, es_out_id_t *es )
{
es_out_sys_t *p_sys = out->p_sys;
/* We don't try to reselect */
if( es->p_dec )
EsUnselect( out, es, es->p_pgrm == p_sys->p_pgrm );
TAB_REMOVE( p_sys->i_es, p_sys->es, es );
switch( es->p_es->i_cat )
es->p_pgrm->i_es--;
if( es->p_pgrm->i_es == 0 )
{
msg_Err( p_sys->p_input, "Program doesn't have es anymore, clenaing TODO ?" );
}
if( p_sys->p_es_audio == es ) p_sys->p_es_audio = NULL;
if( p_sys->p_es_video == es ) p_sys->p_es_video = NULL;
if( p_sys->p_es_sub == es ) p_sys->p_es_sub = NULL;
switch( es->fmt.i_cat )
{
case AUDIO_ES:
p_sys->i_audio--;
......@@ -507,30 +779,10 @@ static void EsOutDel( es_out_t *out, es_out_id_t *es )
break;
}
/* We don't try to reselect */
vlc_mutex_lock( &p_sys->p_input->stream.stream_lock );
if( es->p_es->p_dec )
{
input_UnselectES( p_sys->p_input, es->p_es );
}
if( es->p_es->p_waveformatex )
{
free( es->p_es->p_waveformatex );
es->p_es->p_waveformatex = NULL;
}
if( es->p_es->p_bitmapinfoheader )
{
free( es->p_es->p_bitmapinfoheader );
es->p_es->p_bitmapinfoheader = NULL;
}
input_DelES( p_sys->p_input, es->p_es );
if( es->psz_description )
free( es->psz_description );
if( p_sys->p_es_audio == es ) p_sys->p_es_audio = NULL;
if( p_sys->p_es_video == es ) p_sys->p_es_video = NULL;
if( p_sys->p_es_sub == es ) p_sys->p_es_sub = NULL;
vlc_mutex_unlock( &p_sys->p_input->stream.stream_lock );
es_format_Clean( &es->fmt );
free( es );
}
......@@ -554,42 +806,34 @@ static int EsOutControl( es_out_t *out, int i_query, va_list args )
switch( i_query )
{
case ES_OUT_SET_ES_STATE:
vlc_mutex_lock( &p_sys->p_input->stream.stream_lock );
es = (es_out_id_t*) va_arg( args, es_out_id_t * );
b = (vlc_bool_t) va_arg( args, vlc_bool_t );
if( b && es->p_es->p_dec == NULL )
if( b && es->p_dec == NULL )
{
input_SelectES( p_sys->p_input, es->p_es );
vlc_mutex_unlock( &p_sys->p_input->stream.stream_lock );
return es->p_es->p_dec ? VLC_SUCCESS : VLC_EGENERIC;
EsSelect( out, es );
return es->p_dec ? VLC_SUCCESS : VLC_EGENERIC;
}
else if( !b && es->p_es->p_dec )
else if( !b && es->p_dec )
{
input_UnselectES( p_sys->p_input, es->p_es );
vlc_mutex_unlock( &p_sys->p_input->stream.stream_lock );
EsUnselect( out, es, es->p_pgrm == p_sys->p_pgrm );
return VLC_SUCCESS;
}
vlc_mutex_unlock( &p_sys->p_input->stream.stream_lock );
return VLC_SUCCESS;
case ES_OUT_GET_ES_STATE:
es = (es_out_id_t*) va_arg( args, es_out_id_t * );
pb = (vlc_bool_t*) va_arg( args, vlc_bool_t * );
*pb = es->p_es->p_dec ? VLC_TRUE : VLC_FALSE;
*pb = es->p_dec ? VLC_TRUE : VLC_FALSE;
return VLC_SUCCESS;
case ES_OUT_SET_ACTIVE:
{
b = (vlc_bool_t) va_arg( args, vlc_bool_t );
p_sys->b_active = b;
/* Needed ? */
if( b )
{
vlc_value_t val;
val.b_bool = VLC_TRUE;
var_Set( p_sys->p_input, "intf-change", val );
}
var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );
return VLC_SUCCESS;
}
......@@ -603,28 +847,20 @@ static int EsOutControl( es_out_t *out, int i_query, va_list args )
if( i == ES_OUT_MODE_NONE || i == ES_OUT_MODE_ALL ||
i == ES_OUT_MODE_AUTO )
{
vlc_value_t val;
p_sys->i_mode = i;
/* Reapply policy mode */
vlc_mutex_lock( &p_sys->p_input->stream.stream_lock );
for( i = 0; i < p_sys->i_es; i++ )
{
if( p_sys->es[i]->p_es->p_dec )
if( p_sys->es[i]->p_dec )
{
input_UnselectES( p_sys->p_input, p_sys->es[i]->p_es );
EsUnselect( out, p_sys->es[i], p_sys->es[i]->p_pgrm == p_sys->p_pgrm );
}
}
for( i = 0; i < p_sys->i_es; i++ )
{
EsOutSelect( out, p_sys->es[i], VLC_FALSE );
}
vlc_mutex_unlock( &p_sys->p_input->stream.stream_lock );
val.b_bool = VLC_TRUE;
var_Set( p_sys->p_input, "intf-change", val );
return VLC_SUCCESS;
}
return VLC_EGENERIC;
......@@ -636,65 +872,119 @@ static int EsOutControl( es_out_t *out, int i_query, va_list args )
case ES_OUT_SET_ES:
es = (es_out_id_t*) va_arg( args, es_out_id_t * );
/* Special case NULL, NULL+i_cat */
if( es == NULL )
{
for( i = 0; i < p_sys->i_es; i++ )
{
vlc_mutex_lock( &p_sys->p_input->stream.stream_lock );
if( p_sys->es[i]->p_es->p_dec )
{
input_UnselectES( p_sys->p_input, p_sys->es[i]->p_es );
}
vlc_mutex_unlock( &p_sys->p_input->stream.stream_lock );
if( p_sys->es[i]->p_dec )
EsUnselect( out, p_sys->es[i],
p_sys->es[i]->p_pgrm == p_sys->p_pgrm );
}
}
else if( es == (es_out_id_t*)((uint8_t*)NULL+AUDIO_ES) )
{
for( i = 0; i < p_sys->i_es; i++ )
{
if( p_sys->es[i]->p_dec && p_sys->es[i]->fmt.i_cat == AUDIO_ES )
EsUnselect( out, p_sys->es[i], p_sys->es[i]->p_pgrm == p_sys->p_pgrm );
}
}
else if( es == (es_out_id_t*)((uint8_t*)NULL+VIDEO_ES) )
{
for( i = 0; i < p_sys->i_es; i++ )
{
if( p_sys->es[i]->p_dec && p_sys->es[i]->fmt.i_cat == VIDEO_ES )
EsUnselect( out, p_sys->es[i], p_sys->es[i]->p_pgrm == p_sys->p_pgrm );
}
}
else if( es == (es_out_id_t*)((uint8_t*)NULL+SPU_ES) )
{
for( i = 0; i < p_sys->i_es; i++ )
{
if( p_sys->es[i]->p_dec && p_sys->es[i]->fmt.i_cat == SPU_ES )
EsUnselect( out, p_sys->es[i], p_sys->es[i]->p_pgrm == p_sys->p_pgrm );
}
}
else
{
vlc_mutex_lock( &p_sys->p_input->stream.stream_lock );
EsOutSelect( out, es, VLC_TRUE );
vlc_mutex_unlock( &p_sys->p_input->stream.stream_lock );
for( i = 0; i < p_sys->i_es; i++ )
{
if( es == p_sys->es[i] )
{
EsOutSelect( out, es, VLC_TRUE );
break;
}
}
}
return VLC_SUCCESS;
case ES_OUT_SET_PCR:
case ES_OUT_SET_GROUP_PCR:
{
pgrm_descriptor_t *p_pgrm = NULL;
int64_t i_pcr;
es_out_pgrm_t *p_pgrm = NULL;
int i_group = 0;
int64_t i_pcr;
if( i_query == ES_OUT_SET_PCR )
{
p_pgrm = p_sys->p_input->stream.p_selected_program;
p_pgrm = p_sys->p_pgrm;
}
else
{
int i_group = (int)va_arg( args, int );
p_pgrm = input_FindProgram( p_sys->p_input, i_group );
if( p_pgrm == NULL )
int i;
i_group = (int)va_arg( args, int );
for( i = 0; i < p_sys->i_pgrm; i++ )
{
/* we create the requested program */
p_pgrm = EsOutAddProgram( out, i_group );
if( p_sys->pgrm[i]->i_id == i_group )
{
p_pgrm = p_sys->pgrm[i];
break;
}
}
}
if( p_pgrm == NULL )
p_pgrm = EsOutProgramAdd( out, i_group ); /* Create it */
i_pcr = (int64_t)va_arg( args, int64_t );
/* search program */
if( p_pgrm )
{
/* 11 is a vodoo trick to avoid non_pcr*9/100 to be null */
input_ClockManageRef( p_sys->p_input, p_pgrm, (i_pcr + 11 ) * 9 / 100);
}
input_ClockSetPCR( p_sys->p_input, &p_pgrm->clock, (i_pcr + 11 ) * 9 / 100);
return VLC_SUCCESS;
}
case ES_OUT_RESET_PCR:
for( i = 0; i < p_sys->p_input->stream.i_pgrm_number; i++ )
for( i = 0; i < p_sys->i_pgrm; i++ )
{
p_sys->p_input->stream.pp_programs[i]->i_synchro_state =
SYNCHRO_REINIT;
p_sys->p_input->stream.pp_programs[i]->last_pts = 0;
p_sys->pgrm[i]->clock.i_synchro_state = SYNCHRO_REINIT;
p_sys->pgrm[i]->clock.last_pts = 0;
}
return VLC_SUCCESS;
case ES_OUT_GET_GROUP:
pi = (int*) va_arg( args, int* );
if( p_sys->p_pgrm )
*pi = p_sys->p_pgrm->i_id;
else
*pi = -1; /* FIXME */
return VLC_SUCCESS;
case ES_OUT_SET_GROUP:
{
int j;
i = (int) va_arg( args, int );
for( j = 0; j < p_sys->i_pgrm; j++ )
{
es_out_pgrm_t *p_pgrm = p_sys->pgrm[j];
if( p_pgrm->i_id == i )
{
EsOutProgramSelect( out, p_pgrm );
return VLC_SUCCESS;
}
}
return VLC_EGENERIC;
}
default:
msg_Err( p_sys->p_input, "unknown query in es_out_Control" );
return VLC_EGENERIC;
......
/*****************************************************************************
* input.c: input thread
* Read a stream, demultiplex and parse it before sending it to
* decoders.
*****************************************************************************
* Copyright (C) 1998-2004 VideoLAN
* $Id$
*
* Authors: Christophe Massiot <massiot@via.ecp.fr>
* Laurent Aimar <fenrir@via.ecp.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -33,64 +32,63 @@
#include <vlc/decoder.h>
#include <vlc/vout.h>
#ifdef HAVE_SYS_TIMES_H
# include <sys/times.h>
#endif
#include "input_internal.h"
#include "stream_output.h"
#include "vlc_interface.h"
#include "codecs.h"
#include "vlc_meta.h"
#include "../../modules/demux/util/sub.h"
/*****************************************************************************
* Local prototypes
*****************************************************************************/
struct input_thread_sys_t
{
/* subtitles */
int i_sub;
subtitle_demux_t **sub;
static int Run ( input_thread_t *p_input );
static int Init ( input_thread_t *p_input );
static void Error( input_thread_t *p_input );
static void End ( input_thread_t *p_input );
static inline int ControlPopNoLock( input_thread_t *, int *, vlc_value_t * );
static void ControlReduce( input_thread_t * );
static vlc_bool_t Control( input_thread_t *, int, vlc_value_t );
int64_t i_stop_time;
};
static int RunThread ( input_thread_t *p_input );
static int InitThread ( input_thread_t *p_input );
static void ErrorThread( input_thread_t *p_input );
static void EndThread ( input_thread_t *p_input );
static void UpdateFromAccess( input_thread_t * );
static void UpdateFromDemux( input_thread_t * );
static void ParseOption( input_thread_t *p_input, const char *psz_option );
static void DecodeUrl ( char * );
/*****************************************************************************
* Callbacks
*****************************************************************************/
static int PositionCallback( vlc_object_t *p_this, char const *psz_cmd,
vlc_value_t oldval, vlc_value_t newval, void *p_data );
static int TimeCallback ( vlc_object_t *p_this, char const *psz_cmd,
vlc_value_t oldval, vlc_value_t newval, void *p_data );
static int StateCallback ( vlc_object_t *p_this, char const *psz_cmd,
vlc_value_t oldval, vlc_value_t newval, void *p_data );
static int RateCallback ( vlc_object_t *p_this, char const *psz_cmd,
vlc_value_t oldval, vlc_value_t newval, void *p_data );
static int BookmarkCallback( vlc_object_t *p_this, char const *psz_cmd,
vlc_value_t oldval, vlc_value_t newval, void *p_data );
/*****************************************************************************
* input_CreateThread: creates a new input thread
*****************************************************************************
* This function creates a new input, and returns a pointer
* to its description. On error, it returns NULL.
*
* Variables for _public_ use:
* * Get and Set:
* - state
* - rate,rate-slower, rate-faster
* - position, position-offset
* - time, time-offset
* - title,title-next,title-prev
* - chapter,chapter-next, chapter-prev
* - program, audio-es, video-es, spu-es
* - bookmark
* * Get only:
* - length
* - bookmarks
* * For intf callback upon changes
* - intf-change
* TODO explain when Callback is called
* TODO complete this list (?)
*****************************************************************************/
input_thread_t *__input_CreateThread( vlc_object_t *p_parent,
input_item_t *p_item )
{
input_thread_t *p_input; /* thread descriptor */
vlc_value_t val;
int i;
/* Allocate descriptor */
......@@ -101,8 +99,40 @@ input_thread_t *__input_CreateThread( vlc_object_t *p_parent,
return NULL;
}
/* Store pointer to input item descriptor */
p_input->p_item = p_item;
/* Init Common fields */
p_input->b_eof = VLC_FALSE;
p_input->b_can_pace_control = VLC_TRUE;
p_input->i_start = 0;
p_input->i_time = 0;
p_input->i_stop = 0;
p_input->i_title = 0;
p_input->title = NULL;
p_input->i_state = INIT_S;
p_input->i_rate = INPUT_RATE_DEFAULT;
p_input->i_bookmark = 0;
p_input->bookmark = NULL;
p_input->p_es_out = NULL;
p_input->p_sout = NULL;
p_input->b_out_pace_control = VLC_FALSE;
p_input->i_pts_delay = 0;
/* Init Input fields */
p_input->input.p_item = p_item;
p_input->input.p_access = NULL;
p_input->input.p_stream = NULL;
p_input->input.p_demux = NULL;
p_input->input.b_title_demux = VLC_FALSE;
p_input->input.i_title = 0;
p_input->input.title = NULL;
p_input->input.b_can_pace_control = VLC_TRUE;
p_input->input.b_eof = VLC_FALSE;
p_input->input.i_cr_average = 0;
/* Init control buffer */
vlc_mutex_init( p_input, &p_input->lock_control );
p_input->i_control = 0;
p_input->p_sys = NULL;
/* Parse input options */
vlc_mutex_lock( &p_item->lock );
......@@ -113,166 +143,15 @@ input_thread_t *__input_CreateThread( vlc_object_t *p_parent,
}
vlc_mutex_unlock( &p_item->lock );
/* Create a few object variables we'll need later on */
var_Create( p_input, "video", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
var_Create( p_input, "audio", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
var_Create( p_input, "audio-channel", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
var_Create( p_input, "spu-channel", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
var_Create( p_input, "sub-file", VLC_VAR_FILE | VLC_VAR_DOINHERIT );
var_Create( p_input, "sub-autodetect-file", VLC_VAR_BOOL |
VLC_VAR_DOINHERIT );
var_Create( p_input, "sub-autodetect-path", VLC_VAR_STRING |
VLC_VAR_DOINHERIT );
var_Create( p_input, "sub-autodetect-fuzzy", VLC_VAR_INTEGER |
VLC_VAR_DOINHERIT );
var_Create( p_input, "sout", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
var_Create( p_input, "sout-all", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
var_Create( p_input, "sout-audio", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
var_Create( p_input, "sout-video", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
var_Create( p_input, "sout-keep", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
/* repeat variable */
var_Create( p_input, "input-repeat", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
/* start/stop time */
var_Create( p_input, "start-time", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
var_Create( p_input, "stop-time", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
/* decoders */
var_Create( p_input, "minimize-threads", VLC_VAR_BOOL|VLC_VAR_DOINHERIT );
/* play status */
/* position variable */
var_Create( p_input, "position", VLC_VAR_FLOAT ); /* position 0.0->1.0 */
var_Create( p_input, "position-offset", VLC_VAR_FLOAT ); /* relative */
val.f_float = 0.0;
var_Change( p_input, "position", VLC_VAR_SETVALUE, &val, NULL );
var_AddCallback( p_input, "position", PositionCallback, NULL );
var_AddCallback( p_input, "position-offset", PositionCallback, NULL );
/* time variable */
var_Create( p_input, "time", VLC_VAR_TIME );
var_Create( p_input, "time-offset", VLC_VAR_TIME ); /* relative */
val.i_time = 0;
var_Change( p_input, "time", VLC_VAR_SETVALUE, &val, NULL );
var_AddCallback( p_input, "time", TimeCallback, NULL );
var_AddCallback( p_input, "time-offset", TimeCallback, NULL );
/* length variable */
var_Create( p_input, "length", VLC_VAR_TIME );
val.i_time = 0;
var_Change( p_input, "length", VLC_VAR_SETVALUE, &val, NULL );
/* rate variable */
var_Create( p_input, "rate", VLC_VAR_INTEGER );
var_Create( p_input, "rate-slower", VLC_VAR_VOID );
var_Create( p_input, "rate-faster", VLC_VAR_VOID );
val.i_int = DEFAULT_RATE;
var_Change( p_input, "rate", VLC_VAR_SETVALUE, &val, NULL );
var_AddCallback( p_input, "rate", RateCallback, NULL );
var_AddCallback( p_input, "rate-slower", RateCallback, NULL );
var_AddCallback( p_input, "rate-faster", RateCallback, NULL );
/* state variable */
var_Create( p_input, "state", VLC_VAR_INTEGER );
val.i_int = INIT_S;
var_Change( p_input, "state", VLC_VAR_SETVALUE, &val, NULL );
var_AddCallback( p_input, "state", StateCallback, NULL );
/* state variable */
var_Create( p_input, "demuxed-id3", VLC_VAR_BOOL );
val.b_bool = VLC_FALSE;
var_Change( p_input, "demuxed-id3", VLC_VAR_SETVALUE, &val, NULL );
/* Initialize thread properties */
p_input->b_eof = 0;
p_input->b_out_pace_control = VLC_FALSE;
p_input->p_sys = NULL;
/* Set target */
vlc_mutex_lock( &p_item->lock );
p_input->psz_source = strdup( p_item->psz_uri );
vlc_mutex_unlock( &p_item->lock );
/* Stream */
p_input->s = NULL;
/* es out */
p_input->p_es_out = NULL;
/* Demux */
p_input->p_demux = NULL;
p_input->pf_demux = NULL;
p_input->pf_rewind = NULL;
p_input->pf_demux_control = demux_vaControlDefault;
p_input->i_cr_average = config_GetInt( p_input, "cr-average" );
/* Access */
p_input->p_access = NULL;
p_input->pf_access_control = NULL;
p_input->i_bufsize = 0;
p_input->i_mtu = 0;
p_input->i_pts_delay = DEFAULT_PTS_DELAY;
/* Initialize statistics */
p_input->c_loops = 0;
p_input->stream.c_packets_read = 0;
p_input->stream.c_packets_trashed = 0;
/* Set locks. */
vlc_mutex_init( p_input, &p_input->stream.stream_lock );
vlc_cond_init( p_input, &p_input->stream.stream_wait );
vlc_mutex_init( p_input, &p_input->stream.control.control_lock );
/* Initialize stream description */
p_input->stream.b_changed = 0;
p_input->stream.i_es_number = 0;
p_input->stream.i_selected_es_number = 0;
p_input->stream.i_pgrm_number = 0;
p_input->stream.i_new_status = p_input->stream.i_new_rate = 0;
p_input->stream.b_new_mute = MUTE_NO_CHANGE;
p_input->stream.i_mux_rate = 0;
p_input->stream.b_seekable = 0;
p_input->stream.p_sout = NULL;
/* no stream, no program, no area, no es */
p_input->stream.p_new_program = NULL;
p_input->stream.i_area_nb = 0;
p_input->stream.pp_areas = NULL;
p_input->stream.p_selected_area = NULL;
p_input->stream.p_new_area = NULL;
p_input->stream.pp_selected_es = NULL;
p_input->stream.p_removed_es = NULL;
p_input->stream.p_newly_selected_es = NULL;
/* By default there is one area in a stream */
input_AddArea( p_input, 0, 1 );
p_input->stream.p_selected_area = p_input->stream.pp_areas[0];
/* Initialize stream control properties. */
p_input->stream.control.i_status = INIT_S;
p_input->stream.control.i_rate = DEFAULT_RATE;
p_input->stream.control.b_mute = 0;
p_input->stream.control.b_grayscale = config_GetInt( p_input, "grayscale");
msg_Info( p_input, "playlist item `%s'", p_input->psz_source );
/* Create Object Variables for private use only */
input_ConfigVarInit( p_input );
/* Bookmarks */
var_Create( p_input, "bookmarks", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
var_Create( p_input, "bookmark", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE |
VLC_VAR_ISCOMMAND );
val.psz_string = _("Bookmark");
var_Change( p_input, "bookmark", VLC_VAR_SETTEXT, &val, NULL );
var_AddCallback( p_input, "bookmark", BookmarkCallback, NULL );
p_input->i_bookmarks = 0;
p_input->pp_bookmarks = NULL;
/* Create Objects variables for public Get and Set */
input_ControlVarInit( p_input );
p_input->input.i_cr_average = var_GetInteger( p_input, "cr-average" );
#if 0
/* TODO */
var_Get( p_input, "bookmarks", &val );
if( val.psz_string )
{
......@@ -319,15 +198,18 @@ input_thread_t *__input_CreateThread( vlc_object_t *p_parent,
}
free( val.psz_string );
}
#endif
/* Now we can attach our new input */
vlc_object_attach( p_input, p_parent );
/* Create thread and wait for its readiness. */
if( vlc_thread_create( p_input, "input", RunThread,
if( vlc_thread_create( p_input, "input", Run,
VLC_THREAD_PRIORITY_INPUT, VLC_TRUE ) )
{
msg_Err( p_input, "cannot create input thread" );
free( p_input );
vlc_object_detach( p_input );
vlc_object_destroy( p_input );
return NULL;
}
......@@ -341,34 +223,40 @@ input_thread_t *__input_CreateThread( vlc_object_t *p_parent,
*****************************************************************************/
void input_StopThread( input_thread_t *p_input )
{
demux_t *p_demux;
access_t *p_access;
vlc_list_t *p_list;
int i;
/* Make the thread exit from a possible vlc_cond_wait() */
vlc_mutex_lock( &p_input->stream.stream_lock );
/* Set die for input */
p_input->b_die = VLC_TRUE;
/* Request thread destruction */
/* We cannot touch p_input fields directly (we can from another thread),
* so use the vlc_object_find way, it's perfectly safe */
/* Temporary demux2 hack */
p_demux = (demux_t *)vlc_object_find( p_input, VLC_OBJECT_DEMUX, FIND_CHILD );
if( p_demux )
/* Set die for all access */
p_list = vlc_list_find( p_input, VLC_OBJECT_ACCESS, FIND_CHILD );
for( i = 0; i < p_list->i_count; i++ )
{
p_demux->b_die = 1;
vlc_object_release( p_demux );
p_list->p_values[i].p_object->b_die = VLC_TRUE;
}
vlc_list_release( p_list );
/* Temporary access2 hack */
p_access = (access_t *)vlc_object_find( p_input, VLC_OBJECT_ACCESS, FIND_CHILD );
if( p_access )
/* Set die for all stream */
p_list = vlc_list_find( p_input, VLC_OBJECT_STREAM, FIND_CHILD );
for( i = 0; i < p_list->i_count; i++ )
{
p_access->b_die = 1;
vlc_object_release( p_access );
p_list->p_values[i].p_object->b_die = VLC_TRUE;
}
vlc_list_release( p_list );
p_input->b_die = 1;
/* Set die for all demux */
p_list = vlc_list_find( p_input, VLC_OBJECT_DEMUX, FIND_CHILD );
for( i = 0; i < p_list->i_count; i++ )
{
p_list->p_values[i].p_object->b_die = VLC_TRUE;
}
vlc_list_release( p_list );
vlc_cond_signal( &p_input->stream.stream_wait );
vlc_mutex_unlock( &p_input->stream.stream_lock );
input_ControlPush( p_input, INPUT_CONTROL_SET_DIE, NULL );
}
/*****************************************************************************
......@@ -381,106 +269,192 @@ void input_DestroyThread( input_thread_t *p_input )
/* Join the thread */
vlc_thread_join( p_input );
/* Destroy Mutex locks */
vlc_mutex_destroy( &p_input->stream.control.control_lock );
vlc_cond_destroy( &p_input->stream.stream_wait );
vlc_mutex_destroy( &p_input->stream.stream_lock );
/* Delete input lock (only after thread joined) */
vlc_mutex_destroy( &p_input->lock_control );
/* TODO: maybe input_DestroyThread should also delete p_input instead
* of the playlist but I'm not sure if it's possible */
}
/*****************************************************************************
* RunThread: main thread loop
* Run: main thread loop
*****************************************************************************
* Thread in charge of processing the network packets and demultiplexing.
*****************************************************************************/
static int RunThread( input_thread_t *p_input )
static int Run( input_thread_t *p_input )
{
vlc_value_t val;
mtime_t i_update_next = -1;
int64_t i_intf_update = 0;
/* Signal right now, otherwise we'll get stuck in a peek */
/* Signal that the thread is launched */
vlc_thread_ready( p_input );
if( InitThread( p_input ) )
if( Init( p_input ) )
{
/* If we failed, wait before we are killed, and exit */
p_input->b_error = 1;
p_input->b_error = VLC_TRUE;
ErrorThread( p_input );
Error( p_input );
/* Tell we're dead */
p_input->b_dead = 1;
p_input->b_dead = VLC_TRUE;
return 0;
}
/* initialization is complete */
vlc_mutex_lock( &p_input->stream.stream_lock );
p_input->stream.b_changed = 1;
p_input->stream.control.i_status = PLAYING_S;
vlc_mutex_unlock( &p_input->stream.stream_lock );
val.i_int = PLAYING_S;
var_Change( p_input, "state", VLC_VAR_SETVALUE, &val, NULL );
/* Main loop */
while( !p_input->b_die && !p_input->b_error && !p_input->b_eof )
{
unsigned int i, i_count;
p_input->c_loops++;
vlc_mutex_lock( &p_input->stream.stream_lock );
vlc_bool_t b_force_update = VLC_FALSE;
int i_ret;
int i_type;
vlc_value_t val;
if( p_input->stream.p_new_program )
/* Do the read */
if( p_input->i_state != PAUSE_S )
{
if( p_input->pf_set_program != NULL )
{
/* Reinitialize buffer manager. */
input_AccessReinit( p_input );
p_input->pf_set_program( p_input,
p_input->stream.p_new_program );
if( p_input->i_stop <= 0 || p_input->i_time < p_input->i_stop )
i_ret=p_input->input.p_demux->pf_demux(p_input->input.p_demux);
else
i_ret = 0; /* EOF */
/* Escape all decoders for the stream discontinuity they
* will encounter. */
input_EscapeDiscontinuity( p_input );
if( i_ret > 0 )
{
/* TODO */
if( p_input->input.b_title_demux &&
p_input->input.p_demux->info.i_update )
{
UpdateFromDemux( p_input );
b_force_update = VLC_TRUE;
}
else if( !p_input->input.b_title_demux &&
p_input->input.p_access &&
p_input->input.p_access->info.i_update )
{
UpdateFromAccess( p_input );
b_force_update = VLC_TRUE;
}
}
else if( i_ret == 0 ) /* EOF */
{
vlc_value_t repeat;
for( i = 0; i < p_input->stream.i_pgrm_number; i++ )
var_Get( p_input, "input-repeat", &repeat );
if( repeat.i_int == 0 )
{
/* End of file - we do not set b_die because only the
* playlist is allowed to do so. */
msg_Dbg( p_input, "EOF reached" );
p_input->b_eof = VLC_TRUE;
p_input->input.b_eof = VLC_TRUE;
}
else
{
pgrm_descriptor_t * p_pgrm
= p_input->stream.pp_programs[i];
msg_Dbg( p_input, "repeating the same input (%d)", repeat.i_int );
if( repeat.i_int > 0 )
{
repeat.i_int--;
var_Set( p_input, "input-repeat", repeat );
}
/* Reinitialize synchro. */
p_pgrm->i_synchro_state = SYNCHRO_REINIT;
/* Seek to title 0 position 0(start) */
val.i_int = 0;
input_ControlPush( p_input, INPUT_CONTROL_SET_TITLE, &val );
if( p_input->i_start > 0 )
{
val.i_time = p_input->i_start;
input_ControlPush( p_input, INPUT_CONTROL_SET_TIME,
&val );
}
else
{
val.f_float = 0.0;
input_ControlPush( p_input, INPUT_CONTROL_SET_POSITION,
&val );
}
}
}
p_input->stream.p_new_program = NULL;
else if( i_ret < 0 )
{
p_input->b_error = VLC_TRUE;
}
}
if( p_input->stream.p_new_area )
else
{
if( p_input->stream.b_seekable && p_input->pf_set_area != NULL )
{
input_AccessReinit( p_input );
/* Small wait */
msleep( 10*1000 );
}
p_input->pf_set_area( p_input, p_input->stream.p_new_area );
/* Handle control */
vlc_mutex_lock( &p_input->lock_control );
ControlReduce( p_input );
while( !ControlPopNoLock( p_input, &i_type, &val ) )
{
msg_Dbg( p_input, "control type=%d", i_type );
if( Control( p_input, i_type, val ) )
b_force_update = VLC_TRUE;
}
vlc_mutex_unlock( &p_input->lock_control );
/* Escape all decoders for the stream discontinuity they
* will encounter. */
input_EscapeDiscontinuity( p_input );
if( b_force_update ||
i_intf_update < mdate() )
{
vlc_value_t val;
double f_pos;
int64_t i_time, i_length;
/* update input status variables */
if( !demux2_Control( p_input->input.p_demux, DEMUX_GET_POSITION, &f_pos ) )
{
val.f_float = (float)f_pos;
var_Change( p_input, "position", VLC_VAR_SETVALUE, &val, NULL );
}
if( !demux2_Control( p_input->input.p_demux, DEMUX_GET_TIME, &i_time ) )
{
p_input->i_time = i_time;
val.i_time = i_time;
var_Change( p_input, "time", VLC_VAR_SETVALUE, &val, NULL );
}
if( !demux2_Control( p_input->input.p_demux, DEMUX_GET_LENGTH, &i_length ) )
{
vlc_value_t old_val;
var_Get( p_input, "length", &old_val );
val.i_time = i_length;
var_Change( p_input, "length", VLC_VAR_SETVALUE, &val, NULL );
for( i = 0; i < p_input->stream.i_pgrm_number; i++ )
if( old_val.i_time != val.i_time )
{
pgrm_descriptor_t * p_pgrm
= p_input->stream.pp_programs[i];
/* TODO */
#if 0
char psz_buffer[MSTRTIME_MAX_SIZE];
/* Reinitialize synchro. */
p_pgrm->i_synchro_state = SYNCHRO_REINIT;
vlc_mutex_lock( &p_input->p_item->lock );
p_input->p_item->i_duration = i_length;
vlc_mutex_unlock( &p_input->p_item->lock );
input_Control( p_input, INPUT_ADD_INFO, _("General"), _("Duration"),
msecstotimestr( psz_buffer, i_length / 1000 ) );
#endif
}
}
p_input->stream.p_new_area = NULL;
var_SetBool( p_input, "intf-change", VLC_TRUE );
i_intf_update = mdate() + I64C(150000);
}
}
/* Wait we are asked to die */
if( !p_input->b_die )
{
Error( p_input );
}
/* Clean up */
End( p_input );
return 0;
#if 0
while( !p_input->b_die && !p_input->b_error && !p_input->b_eof )
{
if( p_input->stream.p_selected_area->i_seek != NO_SEEK )
{
if( p_input->stream.p_selected_area->i_size > 0 )
......@@ -532,66 +506,10 @@ static int RunThread( input_thread_t *p_input )
p_input->stream.p_selected_area->i_seek = NO_SEEK;
}
if( p_input->stream.p_removed_es )
{
input_UnselectES( p_input, p_input->stream.p_removed_es );
p_input->stream.p_removed_es = NULL;
}
if( p_input->stream.p_newly_selected_es )
{
input_SelectES( p_input, p_input->stream.p_newly_selected_es );
p_input->stream.p_newly_selected_es = NULL;
}
if( p_input->stream.b_new_mute != MUTE_NO_CHANGE )
{
if( p_input->stream.b_new_mute )
{
input_EscapeAudioDiscontinuity( p_input );
}
vlc_mutex_lock( &p_input->stream.control.control_lock );
p_input->stream.control.b_mute = p_input->stream.b_new_mute;
vlc_mutex_unlock( &p_input->stream.control.control_lock );
p_input->stream.b_new_mute = MUTE_NO_CHANGE;
}
vlc_mutex_unlock( &p_input->stream.stream_lock );
/* Read and demultiplex some data. */
i_count = p_input->pf_demux( p_input );
if( i_count == 0 )
{
vlc_value_t repeat;
var_Get( p_input, "input-repeat", &repeat );
if( repeat.i_int == 0 || p_input->stream.i_area_nb <= 0 )
{
/* End of file - we do not set b_die because only the
* playlist is allowed to do so. */
msg_Info( p_input, "EOF reached" );
p_input->b_eof = 1;
}
else
{
msg_Dbg( p_input, "repeating the same input (%d)", repeat.i_int );
if( repeat.i_int > 0 )
{
repeat.i_int--;
var_Set( p_input, "input-repeat", repeat );
}
p_input->stream.p_new_area = p_input->stream.pp_areas[0];
p_input->stream.p_new_area->i_seek = 0;
}
}
else if( i_count < 0 )
{
p_input->b_error = 1;
}
XXXXX
if( !p_input->b_error && !p_input->b_eof && i_update_next < mdate() )
{
......@@ -655,285 +573,363 @@ static int RunThread( input_thread_t *p_input )
EndThread( p_input );
return 0;
#endif
}
/*****************************************************************************
* InitThread: init the input Thread
* Init: init the input Thread
*****************************************************************************/
static int InitThread( input_thread_t * p_input )
static int Init( input_thread_t * p_input )
{
vlc_meta_t *p_meta = NULL, *p_meta_user = NULL;
// float f_fps;
double f_fps;
mtime_t i_length;
/* Parse source string. Syntax : [[<access>][/<demux>]:][<source>] */
char * psz_parser = p_input->psz_dupsource = strdup(p_input->psz_source);
vlc_value_t val, val1;
int64_t i_microsecondperframe;
subtitle_demux_t *p_sub_toselect = NULL;
char *psz_sub_file = NULL;
char *psz_dup = strdup( p_input->input.p_item->psz_uri );
char *psz_access = NULL;
char *psz_demux = NULL;
char *psz_path = NULL;
char *psz;
vlc_value_t val;
/* Skip the plug-in names */
while( *psz_parser && *psz_parser != ':' )
{
psz_parser++;
}
/* Open access/stream/demux */
psz = strchr( psz_dup, ':' );
#if defined( WIN32 ) || defined( UNDER_CE )
if( psz_parser - p_input->psz_dupsource == 1 )
{
msg_Warn( p_input, "drive letter %c: found in source string",
p_input->psz_dupsource[0] ) ;
psz_parser = "";
}
#endif
if( !*psz_parser )
if( psz - psz_dup == 1 )
{
p_input->psz_access = p_input->psz_demux = "";
p_input->psz_name = p_input->psz_source;
free( p_input->psz_dupsource );
p_input->psz_dupsource = NULL;
msg_Warn( p_input, "drive letter %c: found in source string", psz_dup[0] );
}
else
#endif
if( psz )
{
*psz_parser++ = '\0';
/* let's skip '//' */
if( psz_parser[0] == '/' && psz_parser[1] == '/' )
{
psz_parser += 2 ;
}
*psz++ = '\0';
if( psz[0] == '/' && psz[1] == '/' )
psz += 2;
p_input->psz_name = psz_parser ;
psz_path = psz;
/* Come back to parse the access and demux plug-ins */
psz_parser = p_input->psz_dupsource;
if( !*psz_parser )
{
/* No access */
p_input->psz_access = "";
}
else if( *psz_parser == '/' )
{
/* No access */
p_input->psz_access = "";
psz_parser++;
}
else
psz = strchr( psz_dup, '/' );
if( psz )
{
p_input->psz_access = psz_parser;
while( *psz_parser && *psz_parser != '/' )
{
psz_parser++;
}
if( *psz_parser == '/' )
{
*psz_parser++ = '\0';
}
*psz++ = '\0';
psz_demux = psz;
}
if( !*psz_parser )
{
/* No demux */
p_input->psz_demux = "";
}
else
{
p_input->psz_demux = psz_parser;
}
psz_access = psz_dup;
}
msg_Dbg( p_input, "access `%s', demux `%s', name `%s'",
p_input->psz_access, p_input->psz_demux, p_input->psz_name );
if( input_AccessInit( p_input ) == -1 )
else
{
free( p_input->psz_source );
if( p_input->psz_dupsource != NULL )
{
free( p_input->psz_dupsource );
}
return VLC_EGENERIC;
psz_path = psz_dup;
}
/* Initialize optional stream output. (before demuxer)*/
var_Get( p_input, "sout", &val );
if( val.psz_string != NULL )
if( psz_access == NULL ) psz_access = "";
if( psz_demux == NULL ) psz_demux = "";
if( psz_path == NULL ) psz_path = "";
msg_Dbg( p_input, "`%s' gives access `%s' demux `%s' path `%s'",
p_input->input.p_item->psz_uri,
psz_access, psz_demux, psz_path );
/* Initialize optional stream output. (before access/demuxer) */
psz = var_GetString( p_input, "sout" );
if( *psz )
{
if ( *val.psz_string && (p_input->stream.p_sout =
sout_NewInstance( p_input, val.psz_string )) == NULL )
p_input->p_sout = sout_NewInstance( p_input, psz );
if( p_input->p_sout == NULL )
{
msg_Err( p_input, "cannot start stream output instance, aborting" );
free( val.psz_string );
free( psz );
free( psz_dup );
input_AccessEnd( p_input );
free( p_input->psz_source );
if( p_input->psz_dupsource != NULL )
{
free( p_input->psz_dupsource );
}
return VLC_EGENERIC;
}
free( val.psz_string );
}
free( psz );
/* Create es out */
p_input->p_es_out = input_EsOutNew( p_input );
es_out_Control( p_input->p_es_out, ES_OUT_SET_ACTIVE, VLC_FALSE );
es_out_Control( p_input->p_es_out, ES_OUT_SET_MODE, ES_OUT_MODE_NONE );
/* Find and open appropriate access module */
p_input->p_access = module_Need( p_input, "access",
p_input->psz_access, VLC_TRUE );
/* Maybe we had an encoded url */
if( !p_input->p_access && strchr( p_input->psz_name, '%' ) )
/* Try access_demux if no demux given */
if( *psz_access && *psz_demux == '\0' )
{
DecodeUrl( p_input->psz_name );
p_input->input.p_demux = demux2_New( p_input, psz_access, psz_demux, psz_path,
NULL, p_input->p_es_out );
}
msg_Dbg( p_input, "retying with %s", p_input->psz_name );
p_input->p_access = module_Need( p_input, "access",
p_input->psz_access, VLC_TRUE );
if( p_input->input.p_demux )
{
/* Get infos from access_demux */
demux2_Control( p_input->input.p_demux,
DEMUX_GET_PTS_DELAY, &p_input->i_pts_delay );
p_input->input.b_title_demux = VLC_TRUE;
if( demux2_Control( p_input->input.p_demux,
DEMUX_GET_TITLE_INFO,
&p_input->input.title, &p_input->input.i_title ) )
{
p_input->input.i_title = 0;
p_input->input.title = NULL;
}
demux2_Control( p_input->input.p_demux, DEMUX_CAN_CONTROL_PACE,
&p_input->input.b_can_pace_control );
demux2_Control( p_input->input.p_demux, DEMUX_CAN_PAUSE,
&p_input->input.b_can_pause );
}
else
{
/* Now try a real access */
p_input->input.p_access = access2_New( p_input, psz_access, psz_demux, psz_path );
/* Access failed, URL encoded ? */
if( p_input->input.p_access == NULL && strchr( psz_path, '%' ) )
{
DecodeUrl( psz_path );
msg_Dbg( p_input, "retying with access `%s' demux `%s' path `%s'",
psz_access, psz_demux, psz_path );
p_input->input.p_access = access2_New( p_input, psz_access, psz_demux, psz_path );
}
#ifndef WIN32 /* Remove this gross hack from the win32 build as colons
* are forbidden in filenames on Win32. */
* are forbidden in filenames on Win32. */
/* Maybe we got something like: /Volumes/toto:titi/gabu.mpg */
if ( p_input->p_access == NULL
&& (*p_input->psz_demux || *p_input->psz_access) )
{
p_input->psz_access = p_input->psz_demux = "";
p_input->psz_name = p_input->psz_source;
free( p_input->psz_dupsource);
p_input->psz_dupsource = NULL;
/* Maybe we got something like: /Volumes/toto:titi/gabu.mpg */
if( p_input->input.p_access == NULL &&
*psz_access == '\0' && ( *psz_demux || *psz_path ) )
{
free( psz_dup );
psz_dup = strdup( p_input->input.p_item->psz_uri );
psz_access = "";
psz_demux = "";
psz_path = psz_dup;
p_input->p_access = module_Need( p_input, "access",
p_input->psz_access, VLC_TRUE );
}
p_input->input.p_access = access2_New( p_input, psz_access, psz_demux, psz_path );
}
#endif
if( p_input->p_access == NULL )
{
msg_Err( p_input, "no suitable access module for `%s/%s://%s'",
p_input->psz_access, p_input->psz_demux, p_input->psz_name );
if ( p_input->stream.p_sout != NULL )
if( p_input->input.p_access == NULL )
{
sout_DeleteInstance( p_input->stream.p_sout );
msg_Err( p_input, "no suitable access module for `%s'",
p_input->input.p_item->psz_uri );
goto error;
}
input_AccessEnd( p_input );
free( p_input->psz_source );
if( p_input->psz_dupsource != NULL )
/* Get infos from access */
access2_Control( p_input->input.p_access,
ACCESS_GET_PTS_DELAY, &p_input->i_pts_delay );
p_input->input.b_title_demux = VLC_FALSE;
if( access2_Control( p_input->input.p_access,
ACCESS_GET_TITLE_INFO,
&p_input->input.title, &p_input->input.i_title ) )
{
free( p_input->psz_dupsource );
p_input->input.i_title = 0;
p_input->input.title = NULL;
}
access2_Control( p_input->input.p_access, ACCESS_CAN_CONTROL_PACE,
&p_input->input.b_can_pace_control );
access2_Control( p_input->input.p_access, ACCESS_CAN_PAUSE,
&p_input->input.b_can_pace_control );
/* Create the stream_t */
p_input->input.p_stream = stream_AccessNew( p_input->input.p_access );
if( p_input->input.p_stream == NULL )
{
msg_Warn( p_input, "cannot create a stream_t from access" );
goto error;
}
input_EsOutDelete( p_input->p_es_out );
return VLC_EGENERIC;
}
/* Waiting for stream. */
if( p_input->i_mtu )
{
p_input->i_bufsize = p_input->i_mtu;
/* Open a demuxer */
if( *psz_demux == '\0' && *p_input->input.p_access->psz_demux )
{
psz_demux = p_input->input.p_access->psz_demux;
}
p_input->input.p_demux = demux2_New( p_input, psz_access, psz_demux, psz_path,
p_input->input.p_stream,
p_input->p_es_out );
if( p_input->input.p_demux == NULL )
{
msg_Err( p_input, "no suitable demux module for `%s/%s://%s'",
psz_access, psz_demux, psz_path );
goto error;
}
/* TODO get title from demux */
if( p_input->input.i_title <= 0 )
{
if( demux2_Control( p_input->input.p_demux, DEMUX_GET_TITLE_INFO,
&p_input->input.title, &p_input->input.i_title ) )
{
p_input->input.i_title = 0;
p_input->input.title = NULL;
}
else
{
p_input->input.b_title_demux = VLC_TRUE;
}
}
}
else
/* Create global title (for now, just a copy) */
p_input->i_title = p_input->input.i_title;
if( p_input->i_title > 0 )
{
p_input->i_bufsize = INPUT_DEFAULT_BUFSIZE;
int i;
p_input->title = malloc( sizeof( input_title_t *) * p_input->i_title );
for( i = 0; i < p_input->i_title; i++ )
{
p_input->title[i] = vlc_input_title_Duplicate( p_input->input.title[i] );
}
/* Setup variables */
input_ControlVarNavigation( p_input );
input_ControlVarTitle( p_input, 0 );
}
/* Global flag */
p_input->b_can_pace_control = p_input->input.b_can_pace_control;
p_input->b_can_pause = p_input->input.b_can_pause;
/* Fix pts delay */
if( p_input->i_pts_delay <= 0 )
p_input->i_pts_delay = DEFAULT_PTS_DELAY;
/* If the desynchronisation requested by the user is < 0, we need to
* cache more data. */
var_Create( p_input, "audio-desync", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
var_Get( p_input, "audio-desync", &val );
if( val.i_int < 0 )
p_input->i_pts_delay -= (val.i_int * 1000);
if( p_input->p_current_data == NULL && p_input->pf_read != NULL )
{
while( !input_FillBuffer( p_input ) )
{
if( p_input->b_die || p_input->b_error || p_input->b_eof )
{
module_Unneed( p_input, p_input->p_access );
if ( p_input->stream.p_sout != NULL )
{
sout_DeleteInstance( p_input->stream.p_sout );
}
input_AccessEnd( p_input );
free( p_input->psz_source );
if( p_input->psz_dupsource != NULL )
{
free( p_input->psz_dupsource );
}
input_EsOutDelete( p_input->p_es_out );
return VLC_EGENERIC;
}
}
}
/* Init input_thread_sys_t */
p_input->p_sys = malloc( sizeof( input_thread_sys_t ) );
p_input->p_sys->i_sub = 0;
p_input->p_sys->sub = NULL;
/* Create the stream_t facilities */
p_input->s = input_StreamNew( p_input );
if( p_input->s == NULL )
{
/* should never occur yet */
/* TODO: check meta data from users */
msg_Err( p_input, "cannot create stream_t" );
/* TODO: get meta data from demuxer */
module_Unneed( p_input, p_input->p_access );
if ( p_input->stream.p_sout != NULL )
/* Init length */
if( !demux2_Control( p_input->input.p_demux, DEMUX_GET_LENGTH, &val.i_time ) && val.i_time > 0 )
{
var_Change( p_input, "length", VLC_VAR_SETVALUE, &val, NULL );
/* TODO update playlist meta data */
}
/* Start time*/
/* Set start time */
p_input->i_start = (int64_t)var_GetInteger( p_input, "start-time" ) *
I64C(1000000);
p_input->i_stop = (int64_t)var_GetInteger( p_input, "stop-time" ) *
I64C(1000000);
if( p_input->i_start > 0 )
{
if( p_input->i_start >= val.i_time )
{
sout_DeleteInstance( p_input->stream.p_sout );
msg_Warn( p_input, "invalid start-time ignored" );
}
input_AccessEnd( p_input );
free( p_input->psz_source );
if( p_input->psz_dupsource != NULL )
else
{
free( p_input->psz_dupsource );
vlc_value_t s;
msg_Dbg( p_input, "start-time: %ds",
(int)( p_input->i_start / I64C(1000000) ) );
s.i_time = p_input->i_start;
input_ControlPush( p_input, INPUT_CONTROL_SET_TIME, &s );
}
input_EsOutDelete( p_input->p_es_out );
return VLC_EGENERIC;
}
if( p_input->i_stop > 0 && p_input->i_stop <= p_input->i_start )
{
msg_Warn( p_input, "invalid stop-time ignored" );
p_input->i_stop = 0;
}
/* Find and open appropriate demux module */
p_input->p_demux =
module_Need( p_input, "demux",
(p_input->psz_demux && *p_input->psz_demux) ?
p_input->psz_demux : "$demux",
(p_input->psz_demux && *p_input->psz_demux) ?
VLC_TRUE : VLC_FALSE );
if( p_input->p_demux == NULL )
{
msg_Err( p_input, "no suitable demux module for `%s/%s://%s'",
p_input->psz_access, p_input->psz_demux, p_input->psz_name );
/* TODO: do subtitle loading */
input_StreamDelete( p_input->s );
module_Unneed( p_input, p_input->p_access );
if ( p_input->stream.p_sout != NULL )
{
sout_DeleteInstance( p_input->stream.p_sout );
}
input_AccessEnd( p_input );
free( p_input->psz_source );
if( p_input->psz_dupsource != NULL )
{
free( p_input->psz_dupsource );
/* Set up es_out */
es_out_Control( p_input->p_es_out, ES_OUT_SET_ACTIVE, VLC_TRUE );
val.b_bool = VLC_FALSE;
if( p_input->p_sout )
{
var_Get( p_input, "sout-all", &val );
}
es_out_Control( p_input->p_es_out, ES_OUT_SET_MODE,
val.b_bool ? ES_OUT_MODE_ALL : ES_OUT_MODE_AUTO );
/* TODO select forced subs */
#if 0
if( p_sub_toselect )
{
es_out_Control( p_input->p_es_out, ES_OUT_SET_ES,
p_sub_toselect->p_es, VLC_TRUE );
}
#endif
if( p_input->p_sout )
{
if( p_input->p_sout->i_out_pace_nocontrol > 0 )
{
p_input->b_out_pace_control = VLC_FALSE;
}
input_EsOutDelete( p_input->p_es_out );
return VLC_EGENERIC;
else
{
p_input->b_out_pace_control = VLC_TRUE;
}
msg_Dbg( p_input, "starting in %s mode",
p_input->b_out_pace_control ? "asynch" : "synch" );
}
msg_Dbg( p_input, "`%s' sucessfully opened",
p_input->input.p_item->psz_uri );
/* initialization is complete */
p_input->i_state = PLAYING_S;
val.i_int = PLAYING_S;
var_Change( p_input, "state", VLC_VAR_SETVALUE, &val, NULL );
return VLC_SUCCESS;
error:
if( p_input->input.p_demux )
demux2_Delete( p_input->input.p_demux );
if( p_input->input.p_stream )
stream_AccessDelete( p_input->input.p_stream );
if( p_input->input.p_access )
access2_Delete( p_input->input.p_access );
if( p_input->p_es_out )
input_EsOutDelete( p_input->p_es_out );
if( p_input->p_sout )
sout_DeleteInstance( p_input->p_sout );
/* Mark them deleted */
p_input->input.p_demux = NULL;
p_input->input.p_stream = NULL;
p_input->input.p_access = NULL;
p_input->p_es_out = NULL;
p_input->p_sout = NULL;
return VLC_EGENERIC;
#if 0
vlc_meta_t *p_meta = NULL, *p_meta_user = NULL;
// float f_fps;
double f_fps;
mtime_t i_length;
FIXME
p_input->input.i_cr_average = config_GetInt( p_input, "cr-average" );
p_input->stream.control.i_status = INIT_S;
p_input->stream.control.i_rate = DEFAULT_RATE;
/* Init input_thread_sys_t */
p_input->p_sys = malloc( sizeof( input_thread_sys_t ) );
p_input->p_sys->i_sub = 0;
p_input->p_sys->sub = NULL;
p_input->p_sys->i_stop_time = 0;
/* Get meta information from user */
var_Create( p_input, "meta-title", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
var_Create( p_input, "meta-author", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
......@@ -948,37 +944,37 @@ static int InitThread( input_thread_t * p_input )
vlc_value_t val;
var_Get( p_input, "meta-title", &val );
if( val.psz_string && *val.psz_string )
if( *val.psz_string )
vlc_meta_Add( p_meta_user, VLC_META_TITLE, val.psz_string );
if( val.psz_string ) free( val.psz_string );
free( val.psz_string );
var_Get( p_input, "meta-author", &val );
if( val.psz_string && *val.psz_string )
if( *val.psz_string )
vlc_meta_Add( p_meta_user, VLC_META_AUTHOR, val.psz_string );
if( val.psz_string ) free( val.psz_string );
free( val.psz_string );
var_Get( p_input, "meta-artist", &val );
if( val.psz_string && *val.psz_string )
if( *val.psz_string )
vlc_meta_Add( p_meta_user, VLC_META_ARTIST, val.psz_string );
if( val.psz_string ) free( val.psz_string );
free( val.psz_string );
var_Get( p_input, "meta-genre", &val );
if( val.psz_string && *val.psz_string )
if( *val.psz_string )
vlc_meta_Add( p_meta_user, VLC_META_GENRE, val.psz_string );
if( val.psz_string ) free( val.psz_string );
free( val.psz_string );
var_Get( p_input, "meta-copyright", &val );
if( val.psz_string && *val.psz_string )
if( *val.psz_string )
vlc_meta_Add( p_meta_user, VLC_META_COPYRIGHT, val.psz_string );
if( val.psz_string ) free( val.psz_string );
free( val.psz_string );
var_Get( p_input, "meta-description", &val );
if( val.psz_string && *val.psz_string )
if( *val.psz_string )
vlc_meta_Add( p_meta_user, VLC_META_DESCRIPTION, val.psz_string );
if( val.psz_string ) free( val.psz_string );
free( val.psz_string );
var_Get( p_input, "meta-date", &val );
if( val.psz_string && *val.psz_string )
if( *val.psz_string )
vlc_meta_Add( p_meta_user, VLC_META_DATE, val.psz_string );
if( val.psz_string ) free( val.psz_string );
free( val.psz_string );
var_Get( p_input, "meta-url", &val );
if( val.psz_string && *val.psz_string )
if( *val.psz_string )
vlc_meta_Add( p_meta_user, VLC_META_URL, val.psz_string );
if( val.psz_string ) free( val.psz_string );
free( val.psz_string );
}
/* Get meta informations from demuxer */
......@@ -1077,25 +1073,6 @@ static int InitThread( input_thread_t * p_input )
}
}
/* Set stop-time and check validity */
var_Get( p_input, "stop-time", &val );
if( val.i_int > 0 )
{
vlc_value_t start;
var_Get( p_input, "start-time", &start );
if( start.i_int >= val.i_int )
{
msg_Warn( p_input, "invalid stop-time, ignored (stop-time < "
"start-time)" );
}
else
{
p_input->p_sys->i_stop_time = (int64_t)val.i_int * I64C(1000000);
msg_Dbg( p_input, "stop-time %ds", val.i_int );
}
}
/* Get fps */
if( demux_Control( p_input, DEMUX_GET_FPS, &f_fps ) || f_fps < 0.1 )
{
......@@ -1161,30 +1138,15 @@ static int InitThread( input_thread_t * p_input )
es_out_Control( p_input->p_es_out, ES_OUT_SET_ES,
p_sub_toselect->p_es, VLC_TRUE );
}
if( p_input->stream.p_sout )
{
if( p_input->stream.p_sout->i_out_pace_nocontrol > 0 )
{
p_input->b_out_pace_control = VLC_FALSE;
}
else
{
p_input->b_out_pace_control = VLC_TRUE;
}
msg_Dbg( p_input, "starting in %s mode",
p_input->b_out_pace_control ? "asynch" : "synch" );
}
return VLC_SUCCESS;
#endif
}
/*****************************************************************************
* ErrorThread: RunThread() error loop
* Error: RunThread() error loop
*****************************************************************************
* This function is called when an error occured during thread main's loop.
*****************************************************************************/
static void ErrorThread( input_thread_t *p_input )
static void Error( input_thread_t *p_input )
{
while( !p_input->b_die )
{
......@@ -1194,55 +1156,39 @@ static void ErrorThread( input_thread_t *p_input )
}
/*****************************************************************************
* EndThread: end the input thread
* End: end the input thread
*****************************************************************************/
static void EndThread( input_thread_t * p_input )
static void End( input_thread_t * p_input )
{
int i, j;
#ifdef HAVE_SYS_TIMES_H
/* Display statistics */
struct tms cpu_usage;
times( &cpu_usage );
msg_Dbg( p_input, "%ld loops consuming user: %ld, system: %ld",
p_input->c_loops, cpu_usage.tms_utime, cpu_usage.tms_stime );
#else
msg_Dbg( p_input, "%ld loops", p_input->c_loops );
#endif
vlc_value_t val;
msg_Dbg( p_input, "closing `%s'",
p_input->input.p_item->psz_uri );
/* DumpStream: printf some info for debugging purpose */
#define S p_input->stream
msg_Dbg( p_input, "dumping stream ID 0x%x [OK:%ld/D:%ld]", S.i_stream_id,
S.c_packets_read, S.c_packets_trashed );
#undef S
for( i = 0; i < p_input->stream.i_pgrm_number; i++ )
{
#define P p_input->stream.pp_programs[i]
msg_Dbg( p_input, "dumping program 0x%x, version %d (%s)",
P->i_number, P->i_version,
P->b_is_ok ? "complete" : "partial" );
#undef P
for( j = 0; j < p_input->stream.pp_programs[i]->i_es_number; j++ )
{
#define ES p_input->stream.pp_programs[i]->pp_es[j]
msg_Dbg( p_input, "ES 0x%x, "
"stream 0x%x, fourcc `%4.4s', %s [OK:%ld/ERR:%ld]",
ES->i_id, ES->i_stream_id, (char*)&ES->i_fourcc,
ES->p_dec != NULL ? "selected" : "not selected",
ES->c_packets, ES->c_invalid_packets );
#undef ES
}
}
/* We are at the end */
p_input->i_state = END_S;
val.i_int = END_S;
var_Change( p_input, "state", VLC_VAR_SETVALUE, &val, NULL );
/* Clean control variables */
input_ControlVarClean( p_input );
/* Free demultiplexer's data */
if( p_input->p_demux ) module_Unneed( p_input, p_input->p_demux );
/* Unload all modules */
if( p_input->input.p_demux )
demux2_Delete( p_input->input.p_demux );
/* Free all ES and destroy all decoder threads */
input_EndStream( p_input );
if( p_input->input.p_stream )
stream_AccessDelete( p_input->input.p_stream );
if( p_input->input.p_access )
access2_Delete( p_input->input.p_access );
if( p_input->p_es_out )
input_EsOutDelete( p_input->p_es_out );
/* Close optional stream output instance */
if( p_input->stream.p_sout )
if( p_input->p_sout )
{
vlc_object_t *p_pl =
vlc_object_find( p_input, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
......@@ -1252,20 +1198,20 @@ static void EndThread( input_thread_t * p_input )
{
/* attach sout to the playlist */
msg_Warn( p_input, "keeping sout" );
vlc_object_detach( p_input->stream.p_sout );
vlc_object_attach( p_input->stream.p_sout, p_pl );
vlc_object_detach( p_input->p_sout );
vlc_object_attach( p_input->p_sout, p_pl );
}
else
{
msg_Warn( p_input, "destroying sout" );
sout_DeleteInstance( p_input->stream.p_sout );
sout_DeleteInstance( p_input->p_sout );
}
if( p_pl )
{
vlc_object_release( p_pl );
}
}
/* TODO subs */
#if 0
/* Destroy subtitles demuxers */
if( p_input->p_sys )
{
......@@ -1278,30 +1224,471 @@ static void EndThread( input_thread_t * p_input )
free( p_input->p_sys->sub );
}
/* Free input_thread_sys_t */
free( p_input->p_sys );
}
#endif
/* Free input_thread_sys_t */
free( p_input->p_sys );
/* Destroy the stream_t facilities */
if( p_input->s ) input_StreamDelete( p_input->s );
/* Tell we're dead */
p_input->b_dead = VLC_TRUE;
}
/* Destroy es out */
if( p_input->p_es_out ) input_EsOutDelete( p_input->p_es_out );
/*****************************************************************************
* Control
*****************************************************************************/
static inline int ControlPopNoLock( input_thread_t *p_input,
int *pi_type, vlc_value_t *p_val )
{
if( p_input->i_control <= 0 )
{
return VLC_EGENERIC;
}
/* Close the access plug-in */
if( p_input->p_access ) module_Unneed( p_input, p_input->p_access );
*pi_type = p_input->control[0].i_type;
*p_val = p_input->control[0].val;
input_AccessEnd( p_input );
p_input->i_control--;
if( p_input->i_control > 0 )
{
int i;
/* Free info structures XXX destroy es before 'cause vorbis */
msg_Dbg( p_input, "freeing info structures...");
for( i = 0; i < p_input->i_control; i++ )
{
p_input->control[i].i_type = p_input->control[i+1].i_type;
p_input->control[i].val = p_input->control[i+1].val;
}
}
free( p_input->psz_source );
if( p_input->psz_dupsource != NULL ) free( p_input->psz_dupsource );
return VLC_SUCCESS;
}
/* Tell we're dead */
p_input->b_dead = 1;
static void ControlReduce( input_thread_t *p_input )
{
int i;
for( i = 1; i < p_input->i_control; i++ )
{
const int i_lt = p_input->control[i-1].i_type;
const int i_ct = p_input->control[i].i_type;
/* XXX We can't merge INPUT_CONTROL_SET_ES */
msg_Dbg( p_input, "[%d/%d] l=%d c=%d", i, p_input->i_control, i_lt, i_ct );
if( i_lt == i_ct &&
( i_ct == INPUT_CONTROL_SET_STATE ||
i_ct == INPUT_CONTROL_SET_RATE ||
i_ct == INPUT_CONTROL_SET_POSITION ||
i_ct == INPUT_CONTROL_SET_TIME ||
i_ct == INPUT_CONTROL_SET_PROGRAM ||
i_ct == INPUT_CONTROL_SET_TITLE ||
i_ct == INPUT_CONTROL_SET_SEEKPOINT ||
i_ct == INPUT_CONTROL_SET_BOOKMARK ) )
{
int j;
msg_Dbg( p_input, "merged at %d", i );
/* Remove the i-1 */
for( j = i; j < p_input->i_control; j++ )
p_input->control[j-1] = p_input->control[j];
p_input->i_control--;
}
else
{
/* TODO but that's not that important
- merge SET_X with SET_X_CMD
- remove SET_SEEKPOINT/SET_POSITION/SET_TIME before a SET_TITLE
- remove SET_SEEKPOINT/SET_POSITION/SET_TIME before another among them
- ?
*/
}
}
}
static vlc_bool_t Control( input_thread_t *p_input, int i_type, vlc_value_t val )
{
vlc_bool_t b_force_update = VLC_FALSE;
switch( i_type )
{
case INPUT_CONTROL_SET_DIE:
msg_Dbg( p_input, "control: INPUT_CONTROL_SET_DIE proceed" );
/* Mark all submodules to die */
if( p_input->input.p_access )
p_input->input.p_access->b_die = VLC_TRUE;
if( p_input->input.p_stream )
p_input->input.p_stream->b_die = VLC_TRUE;
p_input->input.p_demux->b_die = VLC_TRUE;
p_input->b_die = VLC_TRUE;
break;
case INPUT_CONTROL_SET_POSITION:
case INPUT_CONTROL_SET_POSITION_OFFSET:
{
double f_pos;
if( i_type == INPUT_CONTROL_SET_POSITION )
{
f_pos = val.f_float;
}
else
{
/* Should not fail */
demux2_Control( p_input->input.p_demux, DEMUX_GET_POSITION, &f_pos );
f_pos += val.f_float;
}
if( f_pos < 0.0 ) f_pos = 0.0;
if( f_pos > 1.0 ) f_pos = 1.0;
if( demux2_Control( p_input->input.p_demux, DEMUX_SET_POSITION, f_pos ) )
{
msg_Err( p_input, "INPUT_CONTROL_SET_POSITION(_OFFSET) %2.1f%% failed",
f_pos * 100 );
}
else
{
input_EsOutDiscontinuity( p_input->p_es_out, VLC_FALSE );
es_out_Control( p_input->p_es_out, ES_OUT_RESET_PCR );
b_force_update = VLC_TRUE;
}
break;
}
case INPUT_CONTROL_SET_TIME:
case INPUT_CONTROL_SET_TIME_OFFSET:
{
int64_t i_time;
int i_ret;
if( i_type == INPUT_CONTROL_SET_TIME )
{
i_time = val.i_time;
}
else
{
/* Should not fail */
demux2_Control( p_input->input.p_demux,
DEMUX_GET_TIME, &i_time );
i_time += val.i_time;
}
if( i_time < 0 ) i_time = 0;
i_ret = demux2_Control( p_input->input.p_demux,
DEMUX_SET_TIME, i_time );
if( i_ret )
{
int64_t i_length;
/* Emulate it with a SET_POS */
demux2_Control( p_input->input.p_demux,
DEMUX_GET_LENGTH, &i_length );
if( i_length > 0 )
{
double f_pos = (double)i_time / (double)i_length;
i_ret = demux2_Control( p_input->input.p_demux,
DEMUX_SET_POSITION, f_pos );
}
}
if( i_ret )
{
msg_Err( p_input, "INPUT_CONTROL_SET_TIME(_OFFSET) %lld failed",
i_time );
}
else
{
input_EsOutDiscontinuity( p_input->p_es_out, VLC_FALSE );
es_out_Control( p_input->p_es_out, ES_OUT_RESET_PCR );
b_force_update = VLC_TRUE;
}
break;
}
case INPUT_CONTROL_SET_STATE:
if( ( val.i_int == PLAYING_S && p_input->i_state == PAUSE_S ) ||
( val.i_int == PAUSE_S && p_input->i_state == PAUSE_S ) )
{
int i_ret;
if( p_input->input.p_access )
i_ret = access2_Control( p_input->input.p_access,
ACCESS_SET_PAUSE_STATE, VLC_FALSE );
else
i_ret = demux2_Control( p_input->input.p_demux,
DEMUX_SET_PAUSE_STATE, VLC_FALSE );
if( i_ret )
{
/* FIXME What to do ? */
msg_Warn( p_input, "cannot unset pause -> EOF" );
input_ControlPush( p_input, INPUT_CONTROL_SET_DIE, NULL );
}
b_force_update = VLC_TRUE;
/* Switch to play */
p_input->i_state = PLAYING_S;
val.i_int = PLAYING_S;
var_Change( p_input, "state", VLC_VAR_SETVALUE, &val, NULL );
/* Reset clock */
es_out_Control( p_input->p_es_out, ES_OUT_RESET_PCR );
}
else if( val.i_int == PAUSE_S && p_input->i_state == PLAYING_S && p_input->b_can_pause )
{
int i_ret;
if( p_input->input.p_access )
i_ret = access2_Control( p_input->input.p_access,
ACCESS_SET_PAUSE_STATE, VLC_TRUE );
else
i_ret = demux2_Control( p_input->input.p_demux,
DEMUX_SET_PAUSE_STATE, VLC_TRUE );
b_force_update = VLC_TRUE;
if( i_ret )
{
msg_Warn( p_input, "cannot set pause state" );
val.i_int = p_input->i_state;
}
else
{
val.i_int = PAUSE_S;
}
/* Switch to new state */
p_input->i_state = val.i_int;
var_Change( p_input, "state", VLC_VAR_SETVALUE, &val, NULL );
}
else if( val.i_int == PAUSE_S && !p_input->b_can_pause )
{
b_force_update = VLC_TRUE;
/* Correct "state" value */
val.i_int = p_input->i_state;
var_Change( p_input, "state", VLC_VAR_SETVALUE, &val, NULL );
}
else if( val.i_int != PLAYING_S && val.i_int != PAUSE_S )
{
msg_Err( p_input, "invalid state in INPUT_CONTROL_SET_STATE" );
}
break;
case INPUT_CONTROL_SET_RATE:
case INPUT_CONTROL_SET_RATE_SLOWER:
case INPUT_CONTROL_SET_RATE_FASTER:
{
int i_rate;
if( i_type == INPUT_CONTROL_SET_RATE_SLOWER )
i_rate = p_input->i_rate * 2;
else if( i_type == INPUT_CONTROL_SET_RATE_FASTER )
i_rate = p_input->i_rate / 2;
else
i_rate = val.i_int;
if( i_rate < INPUT_RATE_MIN )
{
msg_Dbg( p_input, "cannot set rate faster" );
i_rate = INPUT_RATE_MIN;
}
else if( i_rate > INPUT_RATE_MAX )
{
msg_Dbg( p_input, "cannot set rate slower" );
i_rate = INPUT_RATE_MAX;
}
if( i_rate != INPUT_RATE_DEFAULT &&
( !p_input->b_can_pace_control || !p_input->b_out_pace_control ) )
{
msg_Dbg( p_input, "cannot change rate" );
i_rate = INPUT_RATE_DEFAULT;
}
if( i_rate != p_input->i_rate )
{
p_input->i_rate = i_rate;
val.i_int = i_rate;
var_Change( p_input, "rate", VLC_VAR_SETVALUE, &val, NULL );
/* We haven't send data to decoder when rate != default */
if( i_rate == INPUT_RATE_DEFAULT )
input_EsOutDiscontinuity( p_input->p_es_out, VLC_TRUE );
/* Reset clock */
es_out_Control( p_input->p_es_out, ES_OUT_RESET_PCR );
b_force_update = VLC_TRUE;
}
break;
}
case INPUT_CONTROL_SET_PROGRAM:
/* No need to force update, es_out does it if needed */
es_out_Control( p_input->p_es_out,
ES_OUT_SET_GROUP, val.i_int );
break;
case INPUT_CONTROL_SET_ES:
/* No need to force update, es_out does it if needed */
es_out_Control( p_input->p_es_out,
ES_OUT_SET_ES, input_EsOutGetFromID( p_input->p_es_out, val.i_int ) );
break;
case INPUT_CONTROL_SET_TITLE:
case INPUT_CONTROL_SET_TITLE_NEXT:
case INPUT_CONTROL_SET_TITLE_PREV:
if( p_input->input.b_title_demux &&
p_input->input.i_title > 0 )
{
/* TODO */
/* FIXME handle demux title */
demux_t *p_demux = p_input->input.p_demux;
int i_title;
if( i_type == INPUT_CONTROL_SET_TITLE_PREV )
i_title = p_demux->info.i_title - 1;
else if( i_type == INPUT_CONTROL_SET_TITLE_NEXT )
i_title = p_demux->info.i_title + 1;
else
i_title = val.i_int;
if( i_title >= 0 && i_title < p_input->input.i_title )
{
demux2_Control( p_demux, DEMUX_SET_TITLE, i_title );
input_EsOutDiscontinuity( p_input->p_es_out, VLC_FALSE );
es_out_Control( p_input->p_es_out, ES_OUT_RESET_PCR );
input_ControlVarTitle( p_input, i_title );
}
}
else if( p_input->input.i_title > 0 )
{
access_t *p_access = p_input->input.p_access;
int i_title;
if( i_type == INPUT_CONTROL_SET_TITLE_PREV )
i_title = p_access->info.i_title - 1;
else if( i_type == INPUT_CONTROL_SET_TITLE_NEXT )
i_title = p_access->info.i_title + 1;
else
i_title = val.i_int;
if( i_title >= 0 && i_title < p_input->input.i_title )
{
access2_Control( p_access, ACCESS_SET_TITLE, i_title );
stream_AccessReset( p_input->input.p_stream );
input_EsOutDiscontinuity( p_input->p_es_out, VLC_FALSE );
es_out_Control( p_input->p_es_out, ES_OUT_RESET_PCR );
}
}
break;
case INPUT_CONTROL_SET_SEEKPOINT:
case INPUT_CONTROL_SET_SEEKPOINT_NEXT:
case INPUT_CONTROL_SET_SEEKPOINT_PREV:
if( p_input->input.b_title_demux &&
p_input->input.i_title > 0 )
{
demux_t *p_demux = p_input->input.p_demux;
int i_seekpoint;
if( i_type == INPUT_CONTROL_SET_SEEKPOINT_PREV )
i_seekpoint = p_demux->info.i_seekpoint - 1;
else if( i_type == INPUT_CONTROL_SET_TITLE_NEXT )
i_seekpoint = p_demux->info.i_seekpoint + 1;
else
i_seekpoint = val.i_int;
if( i_seekpoint >= 0 &&
i_seekpoint < p_input->input.title[p_demux->info.i_title]->i_seekpoint )
{
demux2_Control( p_demux, DEMUX_SET_SEEKPOINT, i_seekpoint );
input_EsOutDiscontinuity( p_input->p_es_out, VLC_FALSE );
es_out_Control( p_input->p_es_out, ES_OUT_RESET_PCR );
}
}
else if( p_input->input.i_title > 0 )
{
access_t *p_access = p_input->input.p_access;
int i_seekpoint;
if( i_type == INPUT_CONTROL_SET_SEEKPOINT_PREV )
i_seekpoint = p_access->info.i_seekpoint - 1;
else if( i_type == INPUT_CONTROL_SET_TITLE_NEXT )
i_seekpoint = p_access->info.i_seekpoint + 1;
else
i_seekpoint = val.i_int;
if( i_seekpoint >= 0 &&
i_seekpoint < p_input->input.title[p_access->info.i_title]->i_seekpoint )
{
access2_Control( p_access, ACCESS_SET_SEEKPOINT, i_seekpoint );
stream_AccessReset( p_input->input.p_stream );
input_EsOutDiscontinuity( p_input->p_es_out, VLC_FALSE );
es_out_Control( p_input->p_es_out, ES_OUT_RESET_PCR );
}
}
break;
case INPUT_CONTROL_SET_BOOKMARK:
default:
msg_Err( p_input, "not yet implemented" );
break;
}
return b_force_update;
}
/*****************************************************************************
* UpdateFromDemux:
*****************************************************************************/
static void UpdateFromDemux( input_thread_t *p_input )
{
demux_t *p_demux = p_input->input.p_demux;
vlc_value_t v;
if( p_demux->info.i_update & INPUT_UPDATE_TITLE )
{
v.i_int = p_demux->info.i_title;
var_Change( p_input, "title", VLC_VAR_SETVALUE, &v, NULL );
input_ControlVarTitle( p_input, p_demux->info.i_title );
p_demux->info.i_update &= ~INPUT_UPDATE_TITLE;
}
if( p_demux->info.i_update & INPUT_UPDATE_SEEKPOINT )
{
v.i_int = p_demux->info.i_seekpoint;
var_Change( p_input, "chapter", VLC_VAR_SETVALUE, &v, NULL);
p_demux->info.i_update &= ~INPUT_UPDATE_SEEKPOINT;
}
p_demux->info.i_update &= ~INPUT_UPDATE_SIZE;
}
/*****************************************************************************
* UpdateFromAccess:
*****************************************************************************/
static void UpdateFromAccess( input_thread_t *p_input )
{
access_t *p_access = p_input->input.p_access;
vlc_value_t v;
if( p_access->info.i_update & INPUT_UPDATE_TITLE )
{
v.i_int = p_access->info.i_title;
var_Change( p_input, "title", VLC_VAR_SETVALUE, &v, NULL );
input_ControlVarTitle( p_input, p_access->info.i_title );
p_access->info.i_update &= ~INPUT_UPDATE_TITLE;
}
if( p_access->info.i_update & INPUT_UPDATE_SEEKPOINT )
{
v.i_int = p_access->info.i_seekpoint;
var_Change( p_input, "chapter", VLC_VAR_SETVALUE, &v, NULL);
p_access->info.i_update &= ~INPUT_UPDATE_SEEKPOINT;
}
p_access->info.i_update &= ~INPUT_UPDATE_SIZE;
}
/*****************************************************************************
* DecodeUrl: decode a given encoded url
*****************************************************************************/
......@@ -1438,273 +1825,4 @@ static void ParseOption( input_thread_t *p_input, const char *psz_option )
return;
}
/*****************************************************************************
* input_SetStatus: change the reading status
*****************************************************************************/
/* Status changing methods */
enum
{
INPUT_STATUS_END = 0,
INPUT_STATUS_PLAY = 1,
INPUT_STATUS_PAUSE = 2,
INPUT_STATUS_FASTER = 3,
INPUT_STATUS_SLOWER = 4
};
static void input_SetStatus( input_thread_t *p_input, int i_mode )
{
vlc_mutex_lock( &p_input->stream.stream_lock );
switch( i_mode )
{
case INPUT_STATUS_END:
p_input->stream.i_new_status = PLAYING_S;
p_input->b_eof = 1;
msg_Dbg( p_input, "end of stream" );
break;
case INPUT_STATUS_PLAY:
p_input->stream.i_new_status = PLAYING_S;
msg_Dbg( p_input, "playing at normal rate" );
break;
case INPUT_STATUS_PAUSE:
/* XXX: we don't need to check i_status, because input_clock.c
* does it for us */
p_input->stream.i_new_status = PAUSE_S;
msg_Dbg( p_input, "toggling pause" );
break;
case INPUT_STATUS_FASTER:
if( p_input->stream.control.i_rate * 4 <= DEFAULT_RATE )
{
msg_Dbg( p_input, "can not play any faster" );
}
else
{
p_input->stream.i_new_status = FORWARD_S;
p_input->stream.i_new_rate =
p_input->stream.control.i_rate / 2;
if ( p_input->stream.i_new_rate < DEFAULT_RATE )
{
msg_Dbg( p_input, "playing at %i:1 fast forward",
DEFAULT_RATE / p_input->stream.i_new_rate );
}
else if ( p_input->stream.i_new_rate > DEFAULT_RATE )
{
msg_Dbg( p_input, "playing at 1:%i slow motion",
p_input->stream.i_new_rate / DEFAULT_RATE );
}
else if ( p_input->stream.i_new_rate == DEFAULT_RATE )
{
p_input->stream.i_new_status = PLAYING_S;
msg_Dbg( p_input, "playing at normal rate" );
}
}
break;
case INPUT_STATUS_SLOWER:
if( p_input->stream.control.i_rate >= 8 * DEFAULT_RATE )
{
msg_Dbg( p_input, "can not play any slower" );
}
else
{
p_input->stream.i_new_status = FORWARD_S;
p_input->stream.i_new_rate =
p_input->stream.control.i_rate * 2;
if ( p_input->stream.i_new_rate < DEFAULT_RATE )
{
msg_Dbg( p_input, "playing at %i:1 fast forward",
DEFAULT_RATE / p_input->stream.i_new_rate );
}
else if ( p_input->stream.i_new_rate > DEFAULT_RATE )
{
msg_Dbg( p_input, "playing at 1:%i slow motion",
p_input->stream.i_new_rate / DEFAULT_RATE );
}
else if ( p_input->stream.i_new_rate == DEFAULT_RATE )
{
p_input->stream.i_new_status = PLAYING_S;
msg_Dbg( p_input, "playing at normal rate" );
}
}
break;
default:
break;
}
vlc_cond_signal( &p_input->stream.stream_wait );
vlc_mutex_unlock( &p_input->stream.stream_lock );
}
/*****************************************************************************
* input_SetRate:
*****************************************************************************/
static void input_SetRate( input_thread_t *p_input, int i_rate )
{
vlc_mutex_lock( &p_input->stream.stream_lock );
if( i_rate * 8 < DEFAULT_RATE )
{
msg_Dbg( p_input, "can not play faster than 8x" );
vlc_mutex_unlock( &p_input->stream.stream_lock );
return;
}
if( i_rate > DEFAULT_RATE * 8 )
{
msg_Dbg( p_input, "can not play slower than 1/8x" );
vlc_mutex_unlock( &p_input->stream.stream_lock );
return;
}
p_input->stream.i_new_status = FORWARD_S;
p_input->stream.i_new_rate = i_rate;
if ( p_input->stream.i_new_rate < DEFAULT_RATE )
{
msg_Dbg( p_input, "playing at %i:1 fast forward",
DEFAULT_RATE / p_input->stream.i_new_rate );
}
else if ( p_input->stream.i_new_rate > DEFAULT_RATE )
{
msg_Dbg( p_input, "playing at 1:%i slow motion",
p_input->stream.i_new_rate / DEFAULT_RATE );
}
else if ( p_input->stream.i_new_rate == DEFAULT_RATE )
{
p_input->stream.i_new_status = PLAYING_S;
msg_Dbg( p_input, "playing at normal rate" );
}
vlc_cond_signal( &p_input->stream.stream_wait );
vlc_mutex_unlock( &p_input->stream.stream_lock );
}
/*****************************************************************************
* Callbacks (position, time, state, rate )
*****************************************************************************/
static int PositionCallback( vlc_object_t *p_this, char const *psz_cmd,
vlc_value_t oldval, vlc_value_t newval,
void *p_data )
{
input_thread_t *p_input = (input_thread_t *)p_this;
msg_Dbg( p_input, "cmd=%s old=%f new=%f", psz_cmd,
oldval.f_float, newval.f_float );
if( !strcmp( psz_cmd, "position-offset" ) )
{
vlc_value_t val;
var_Get( p_input, "position", &val );
newval.f_float += val.f_float;
}
var_Change( p_input, "position", VLC_VAR_SETVALUE, &newval, NULL );
vlc_mutex_lock( &p_input->stream.stream_lock );
p_input->stream.p_selected_area->i_seek =
(int64_t)( newval.f_float *
(double)p_input->stream.p_selected_area->i_size );
if( p_input->stream.p_selected_area->i_seek < 0 )
{
p_input->stream.p_selected_area->i_seek = 0;
}
vlc_mutex_unlock( &p_input->stream.stream_lock );
return VLC_SUCCESS;
}
static int TimeCallback( vlc_object_t *p_this, char const *psz_cmd,
vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
input_thread_t *p_input = (input_thread_t *)p_this;
vlc_value_t val;
/* FIXME TODO FIXME */
msg_Dbg( p_input, "cmd=%s old=%lld new=%lld", psz_cmd,
oldval.i_time, newval.i_time );
var_Get( p_input, "length", &val );
if( val.i_time > 0 )
{
val.f_float = (double)newval.i_time / (double)val.i_time;
if( !strcmp( psz_cmd, "time-offset" ) )
{
vlc_value_t t;
var_Set( p_input, "position-offset", val );
var_Get( p_input, "time", &t );
t.i_time += newval.i_time;
var_Change( p_input, "time", VLC_VAR_SETVALUE, &t, NULL );
}
else
{
var_Set( p_input, "position", val );
var_Change( p_input, "time", VLC_VAR_SETVALUE, &newval, NULL );
}
}
else
{
msg_Warn( p_input, "TimeCallback: length <= 0 -> can't seek" );
}
return VLC_SUCCESS;
}
static int StateCallback( vlc_object_t *p_this, char const *psz_cmd,
vlc_value_t oldval, vlc_value_t newval,
void *p_data )
{
input_thread_t *p_input = (input_thread_t *)p_this;
msg_Dbg( p_input, "cmd=%s old=%d new=%d",
psz_cmd, oldval.i_int, newval.i_int );
switch( newval.i_int )
{
case PLAYING_S:
input_SetStatus( p_input, INPUT_STATUS_PLAY );
return VLC_SUCCESS;
case PAUSE_S:
input_SetStatus( p_input, INPUT_STATUS_PAUSE );
return VLC_SUCCESS;
case END_S:
input_SetStatus( p_input, INPUT_STATUS_END );
return VLC_SUCCESS;
default:
msg_Err( p_input, "cannot set new state (invalid)" );
return VLC_EGENERIC;
}
}
static int RateCallback( vlc_object_t *p_this, char const *psz_cmd,
vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
input_thread_t *p_input = (input_thread_t *)p_this;
if( !strcmp( psz_cmd, "rate-slower" ) )
{
input_SetStatus( p_input, INPUT_STATUS_SLOWER );
}
else if( !strcmp( psz_cmd, "rate-faster" ) )
{
input_SetStatus( p_input, INPUT_STATUS_FASTER );
}
else
{
msg_Dbg( p_input, "cmd=%s old=%d new=%d",
psz_cmd, oldval.i_int, newval.i_int );
input_SetRate( p_input, newval.i_int );
}
return VLC_SUCCESS;
}
static int BookmarkCallback( vlc_object_t *p_this, char const *psz_cmd,
vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
input_thread_t *p_input = (input_thread_t *)p_this;
return input_Control( p_input, INPUT_SET_BOOKMARK, newval );
}
/*****************************************************************************
* input_ext-plugins.c: useful functions for access and demux plug-ins
*****************************************************************************
* Copyright (C) 2001-2004 VideoLAN
* $Id$
*
* Authors: Christophe Massiot <massiot@via.ecp.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <stdlib.h>
#include <string.h>
#include <vlc/vlc.h>
#include "stream_control.h"
#include "input_ext-intf.h"
#include "input_ext-dec.h"
#include "input_ext-plugins.h"
/*
* Buffers management : internal functions
*
* All functions are static, but exported versions with mutex protection
* start with input_*. Not all of these exported functions are actually used,
* but they are included here for completeness.
*/
#define BUFFERS_CACHE_SIZE 500
#define DATA_CACHE_SIZE 1000
#define PES_CACHE_SIZE 1000
/*****************************************************************************
* data_buffer_t: shared data type
*****************************************************************************/
struct data_buffer_t
{
data_buffer_t * p_next;
/* number of data packets this buffer is referenced from - when it falls
* down to 0, the buffer is freed */
int i_refcount;
/* size of the current buffer (starting right after this byte) */
size_t i_size;
};
/*****************************************************************************
* input_buffers_t: defines a LIFO per data type to keep
*****************************************************************************/
#define PACKETS_LIFO( TYPE, NAME ) \
struct \
{ \
TYPE * p_stack; \
unsigned int i_depth; \
} NAME;
struct input_buffers_t
{
vlc_mutex_t lock;
PACKETS_LIFO( pes_packet_t, pes )
PACKETS_LIFO( data_packet_t, data )
PACKETS_LIFO( data_buffer_t, buffers )
size_t i_allocated;
};
/*****************************************************************************
* input_BuffersInit: initialize the cache structures, return a pointer to it
*****************************************************************************/
void * __input_BuffersInit( vlc_object_t *p_this )
{
input_buffers_t * p_buffers = malloc( sizeof( input_buffers_t ) );
if( p_buffers == NULL )
{
return NULL;
}
memset( p_buffers, 0, sizeof( input_buffers_t ) );
vlc_mutex_init( p_this, &p_buffers->lock );
return p_buffers;
}
/*****************************************************************************
* input_BuffersEnd: free all cached structures
*****************************************************************************/
#define BUFFERS_END_PACKETS_LOOP \
while( p_packet != NULL ) \
{ \
p_next = p_packet->p_next; \
free( p_packet ); \
p_packet = p_next; \
}
void input_BuffersEnd( input_thread_t * p_input, input_buffers_t * p_buffers )
{
if( p_buffers != NULL )
{
msg_Dbg( p_input, "pes: %d packets", p_buffers->pes.i_depth );
msg_Dbg( p_input, "data: %d packets", p_buffers->data.i_depth );
msg_Dbg( p_input, "buffers: %d packets", p_buffers->buffers.i_depth );
{
/* Free PES */
pes_packet_t * p_next, * p_packet = p_buffers->pes.p_stack;
BUFFERS_END_PACKETS_LOOP;
}
{
/* Free data packets */
data_packet_t * p_next, * p_packet = p_buffers->data.p_stack;
BUFFERS_END_PACKETS_LOOP;
}
{
/* Free buffers */
data_buffer_t * p_next, * p_buf = p_buffers->buffers.p_stack;
while( p_buf != NULL )
{
p_next = p_buf->p_next;
p_buffers->i_allocated -= p_buf->i_size;
free( p_buf );
p_buf = p_next;
}
}
if( p_buffers->i_allocated )
{
msg_Warn( p_input, "%u bytes have not been freed, "
"expect memory leak", p_buffers->i_allocated );
}
vlc_mutex_destroy( &p_buffers->lock );
free( p_buffers );
}
}
/*****************************************************************************
* input_NewBuffer: return a pointer to a data buffer of the appropriate size
*****************************************************************************/
static inline data_buffer_t * NewBuffer( input_buffers_t * p_buffers,
size_t i_size )
{
data_buffer_t * p_buf;
/* Safety check */
if( p_buffers->i_allocated > INPUT_MAX_ALLOCATION )
{
return NULL;
}
if( p_buffers->buffers.p_stack != NULL )
{
/* Take the buffer from the cache */
p_buf = p_buffers->buffers.p_stack;
p_buffers->buffers.p_stack = p_buf->p_next;
p_buffers->buffers.i_depth--;
/* Reallocate the packet if it is too small or too large */
if( p_buf->i_size < i_size || p_buf->i_size > 3 * i_size )
{
p_buffers->i_allocated -= p_buf->i_size;
free( p_buf );
p_buf = malloc( sizeof(input_buffers_t) + i_size );
if( p_buf == NULL )
{
return NULL;
}
p_buf->i_size = i_size;
p_buffers->i_allocated += i_size;
}
}
else
{
/* Allocate a new buffer */
p_buf = malloc( sizeof(input_buffers_t) + i_size );
if( p_buf == NULL )
{
return NULL;
}
p_buf->i_size = i_size;
p_buffers->i_allocated += i_size;
}
/* Initialize data */
p_buf->p_next = NULL;
p_buf->i_refcount = 0;
return p_buf;
}
data_buffer_t * input_NewBuffer( input_buffers_t * p_buffers, size_t i_size )
{
data_buffer_t * p_buf;
vlc_mutex_lock( &p_buffers->lock );
p_buf = NewBuffer( p_buffers, i_size );
vlc_mutex_unlock( &p_buffers->lock );
return p_buf;
}
/*****************************************************************************
* input_ReleaseBuffer: put a buffer back into the cache
*****************************************************************************/
static inline void ReleaseBuffer( input_buffers_t * p_buffers,
data_buffer_t * p_buf )
{
/* Decrement refcount */
if( --p_buf->i_refcount > 0 )
{
return;
}
if( p_buffers->buffers.i_depth < BUFFERS_CACHE_SIZE )
{
/* Cache not full : store the buffer in it */
p_buf->p_next = p_buffers->buffers.p_stack;
p_buffers->buffers.p_stack = p_buf;
p_buffers->buffers.i_depth++;
}
else
{
p_buffers->i_allocated -= p_buf->i_size;
free( p_buf );
}
}
void input_ReleaseBuffer( input_buffers_t * p_buffers, data_buffer_t * p_buf )
{
vlc_mutex_lock( &p_buffers->lock );
ReleaseBuffer( p_buffers, p_buf );
vlc_mutex_unlock( &p_buffers->lock );
}
/*****************************************************************************
* input_ShareBuffer: allocate a data_packet_t pointing to a given buffer
*****************************************************************************/
static inline data_packet_t * ShareBuffer( input_buffers_t * p_buffers,
data_buffer_t * p_buf )
{
data_packet_t * p_data;
if( p_buffers->data.p_stack != NULL )
{
/* Take the packet from the cache */
p_data = p_buffers->data.p_stack;
p_buffers->data.p_stack = p_data->p_next;
p_buffers->data.i_depth--;
}
else
{
/* Allocate a new packet */
p_data = malloc( sizeof(data_packet_t) );
if( p_data == NULL )
{
return NULL;
}
}
p_data->p_buffer = p_buf;
p_data->p_next = NULL;
p_data->b_discard_payload = 0;
p_data->p_payload_start = p_data->p_demux_start
= (byte_t *)p_buf + sizeof(input_buffers_t);
p_data->p_payload_end = p_data->p_demux_start + p_buf->i_size;
p_buf->i_refcount++;
return p_data;
}
data_packet_t * input_ShareBuffer( input_buffers_t * p_buffers,
data_buffer_t * p_buf )
{
data_packet_t * p_data;
vlc_mutex_lock( &p_buffers->lock );
p_data = ShareBuffer( p_buffers, p_buf );
vlc_mutex_unlock( &p_buffers->lock );
return p_data;
}
/*****************************************************************************
* input_NewPacket: allocate a packet along with a buffer
*****************************************************************************/
static inline data_packet_t * NewPacket( input_buffers_t * p_buffers,
size_t i_size )
{
data_buffer_t * p_buf;
data_packet_t * p_data;
p_buf = NewBuffer( p_buffers, i_size );
if( p_buf == NULL )
{
return NULL;
}
p_data = ShareBuffer( p_buffers, p_buf );
if( p_data == NULL )
{
ReleaseBuffer( p_buffers, p_buf );
}
return p_data;
}
data_packet_t * input_NewPacket( input_buffers_t * p_buffers, size_t i_size )
{
data_packet_t * p_data;
vlc_mutex_lock( &p_buffers->lock );
p_data = NewPacket( p_buffers, i_size );
vlc_mutex_unlock( &p_buffers->lock );
return p_data;
}
/*****************************************************************************
* input_DeletePacket: deallocate a packet and its buffers
*****************************************************************************/
static inline void DeletePacket( input_buffers_t * p_buffers,
data_packet_t * p_data )
{
while( p_data != NULL )
{
data_packet_t * p_next = p_data->p_next;
ReleaseBuffer( p_buffers, p_data->p_buffer );
if( p_buffers->data.i_depth < DATA_CACHE_SIZE )
{
/* Cache not full : store the packet in it */
p_data->p_next = p_buffers->data.p_stack;
p_buffers->data.p_stack = p_data;
p_buffers->data.i_depth++;
}
else
{
free( p_data );
}
p_data = p_next;
}
}
void input_DeletePacket( input_buffers_t * p_buffers, data_packet_t * p_data )
{
vlc_mutex_lock( &p_buffers->lock );
DeletePacket( p_buffers, p_data );
vlc_mutex_unlock( &p_buffers->lock );
}
/*****************************************************************************
* input_NewPES: return a pointer to a new PES packet
*****************************************************************************/
static inline pes_packet_t * NewPES( input_buffers_t * p_buffers )
{
pes_packet_t * p_pes;
if( p_buffers->pes.p_stack != NULL )
{
/* Take the packet from the cache */
p_pes = p_buffers->pes.p_stack;
p_buffers->pes.p_stack = p_pes->p_next;
p_buffers->pes.i_depth--;
}
else
{
/* Allocate a new packet */
p_pes = malloc( sizeof(pes_packet_t) );
if( p_pes == NULL )
{
return NULL;
}
}
p_pes->p_next = NULL;
p_pes->b_data_alignment = p_pes->b_discontinuity = VLC_FALSE;
p_pes->i_pts = p_pes->i_dts = 0;
p_pes->p_first = p_pes->p_last = NULL;
p_pes->i_pes_size = 0;
p_pes->i_nb_data = 0;
return p_pes;
}
pes_packet_t * input_NewPES( input_buffers_t * p_buffers )
{
pes_packet_t * p_pes;
vlc_mutex_lock( &p_buffers->lock );
p_pes = NewPES( p_buffers );
vlc_mutex_unlock( &p_buffers->lock );
return p_pes;
}
/*****************************************************************************
* input_DeletePES: put a pes and all data packets and all buffers back into
* the cache
*****************************************************************************/
static inline void DeletePES( input_buffers_t * p_buffers,
pes_packet_t * p_pes )
{
while( p_pes != NULL )
{
pes_packet_t * p_next = p_pes->p_next;
/* Delete all data packets */
if( p_pes->p_first != NULL )
{
DeletePacket( p_buffers, p_pes->p_first );
}
if( p_buffers->pes.i_depth < PES_CACHE_SIZE )
{
/* Cache not full : store the packet in it */
p_pes->p_next = p_buffers->pes.p_stack;
p_buffers->pes.p_stack = p_pes;
p_buffers->pes.i_depth++;
}
else
{
free( p_pes );
}
p_pes = p_next;
}
}
void input_DeletePES( input_buffers_t * p_buffers, pes_packet_t * p_pes )
{
vlc_mutex_lock( &p_buffers->lock );
DeletePES( p_buffers, p_pes );
vlc_mutex_unlock( &p_buffers->lock );
}
/*
* Buffers management : external functions
*
* These functions make the glu between the access plug-in (pf_read) and
* the demux plug-in (pf_demux). We fill in a large buffer (approx. 10s kB)
* with a call to pf_read, then allow the demux plug-in to have a peep at
* it (input_Peek), and to split it in data_packet_t (input_SplitBuffer).
*/
/*****************************************************************************
* input_FillBuffer: fill in p_data_buffer with data from pf_read
*****************************************************************************/
ssize_t input_FillBuffer( input_thread_t * p_input )
{
ptrdiff_t i_remains = p_input->p_last_data - p_input->p_current_data;
data_buffer_t * p_buf = NULL;
ssize_t i_ret;
vlc_mutex_lock( &p_input->p_method_data->lock );
while( p_buf == NULL )
{
p_buf = NewBuffer( p_input->p_method_data,
i_remains + p_input->i_bufsize );
if( p_buf == NULL )
{
vlc_mutex_unlock( &p_input->p_method_data->lock );
msg_Err( p_input,
"failed allocating a new buffer (decoder stuck?)" );
msleep( INPUT_IDLE_SLEEP );
if( p_input->b_die || p_input->b_error || p_input->b_eof )
{
return -1;
}
vlc_mutex_lock( &p_input->p_method_data->lock );
}
}
p_buf->i_refcount = 1;
if( p_input->p_data_buffer != NULL )
{
if( i_remains )
{
p_input->p_vlc->pf_memcpy( (byte_t *)p_buf + sizeof(data_buffer_t),
p_input->p_current_data,
(size_t)i_remains );
}
ReleaseBuffer( p_input->p_method_data, p_input->p_data_buffer );
}
p_input->p_data_buffer = p_buf;
p_input->p_current_data = (byte_t *)p_buf + sizeof(data_buffer_t);
p_input->p_last_data = p_input->p_current_data + i_remains;
/* Do not hold the lock during pf_read (blocking call). */
vlc_mutex_unlock( &p_input->p_method_data->lock );
i_ret = p_input->pf_read( p_input,
(byte_t *)p_buf + sizeof(data_buffer_t)
+ i_remains,
p_input->i_bufsize );
if( i_ret < 0 && i_remains == 0 )
{
/* Our internal buffers are empty, we can signal the error */
return -1;
}
if( i_ret < 0 ) i_ret = 0;
p_input->p_last_data += i_ret;
return (ssize_t)i_remains + i_ret;
}
/*****************************************************************************
* input_Peek: give a pointer to the next available bytes in the buffer
* (min. i_size bytes)
* Returns the number of bytes read, or -1 in case of error
*****************************************************************************/
ssize_t input_Peek( input_thread_t * p_input, byte_t ** pp_byte,
size_t i_size )
{
ssize_t i_data = p_input->p_last_data - p_input->p_current_data;
while( i_data < (ssize_t)i_size )
{
/* Go to the next buffer */
ssize_t i_ret = input_FillBuffer( p_input );
if( i_ret < 0 )
{
return -1;
}
if( i_ret == i_data )
{
/* We didn't get anymore data, must be the EOF */
i_size = i_data;
break;
}
i_data = i_ret;
}
*pp_byte = p_input->p_current_data;
return i_size;
}
/*****************************************************************************
* input_SplitBuffer: give a pointer to a data packet containing i_size bytes
* Returns the number of bytes read, or -1 in case of error
*****************************************************************************/
ssize_t input_SplitBuffer( input_thread_t * p_input,
data_packet_t ** pp_data, size_t i_size )
{
ssize_t i_data = p_input->p_last_data - p_input->p_current_data;
while( i_data < (ssize_t)i_size )
{
/* Go to the next buffer */
ssize_t i_ret = input_FillBuffer( p_input );
if( i_ret < 0 )
{
return -1;
}
if( i_ret == i_data )
{
/* We didn't get anymore data, must be the EOF */
i_size = i_data;
break;
}
i_data = i_ret;
}
if( i_size < 0)
{
return 0;
}
*pp_data = input_ShareBuffer( p_input->p_method_data,
p_input->p_data_buffer );
(*pp_data)->p_demux_start = (*pp_data)->p_payload_start
= p_input->p_current_data;
(*pp_data)->p_payload_end = (*pp_data)->p_demux_start + i_size;
p_input->p_current_data += i_size;
/* Update stream position */
vlc_mutex_lock( &p_input->stream.stream_lock );
p_input->stream.p_selected_area->i_tell += i_size;
vlc_mutex_unlock( &p_input->stream.stream_lock );
return i_size;
}
/*****************************************************************************
* input_AccessInit: initialize access plug-in wrapper structures
*****************************************************************************/
int input_AccessInit( input_thread_t * p_input )
{
p_input->p_method_data = input_BuffersInit( p_input );
if( p_input->p_method_data == NULL ) return -1;
p_input->p_data_buffer = NULL;
p_input->p_current_data = NULL;
p_input->p_last_data = NULL;
return 0;
}
/*****************************************************************************
* input_AccessReinit: reinit structures before a random seek
*****************************************************************************/
void input_AccessReinit( input_thread_t * p_input )
{
if( p_input->p_data_buffer != NULL )
{
ReleaseBuffer( p_input->p_method_data, p_input->p_data_buffer );
}
p_input->p_data_buffer = NULL;
p_input->p_current_data = NULL;
p_input->p_last_data = NULL;
}
/*****************************************************************************
* input_AccessEnd: free access plug-in wrapper structures
*****************************************************************************/
void input_AccessEnd( input_thread_t * p_input )
{
if( p_input->p_data_buffer != NULL )
{
ReleaseBuffer( p_input->p_method_data, p_input->p_data_buffer );
}
input_BuffersEnd( p_input, p_input->p_method_data );
}
/*****************************************************************************
* input_programs.c: es_descriptor_t, pgrm_descriptor_t management
*****************************************************************************
* Copyright (C) 1999-2004 VideoLAN
* $Id$
*
* Authors: Christophe Massiot <massiot@via.ecp.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <stdlib.h>
#include <string.h> /* memcpy(), memset() */
#include <vlc/vlc.h>
#include "stream_control.h"
#include "input_ext-intf.h"
#include "input_ext-dec.h"
#include "input_ext-plugins.h"
/*
* NOTICE : all of these functions expect you to have taken the lock on
* p_input->stream.lock
*/
/* Navigation callbacks */
static int ProgramCallback( vlc_object_t *, char const *,
vlc_value_t, vlc_value_t, void * );
static int TitleCallback( vlc_object_t *, char const *,
vlc_value_t, vlc_value_t, void * );
static int ChapterCallback( vlc_object_t *, char const *,
vlc_value_t, vlc_value_t, void * );
static int NavigationCallback( vlc_object_t *, char const *,
vlc_value_t, vlc_value_t, void * );
static int ESCallback( vlc_object_t *, char const *,
vlc_value_t, vlc_value_t, void * );
/*****************************************************************************
* input_InitStream: init the stream descriptor of the given input
*****************************************************************************/
int input_InitStream( input_thread_t * p_input, size_t i_data_len )
{
vlc_value_t text,val;
p_input->stream.i_stream_id = 0;
/* initialized to 0 since we don't give the signal to the interface
* before the end of input initialization */
p_input->stream.b_changed = 0;
p_input->stream.pp_es = NULL;
p_input->stream.pp_selected_es = NULL;
p_input->stream.p_removed_es = NULL;
p_input->stream.p_newly_selected_es = NULL;
p_input->stream.i_pgrm_number = 0;
p_input->stream.pp_programs = NULL;
p_input->stream.p_selected_program = NULL;
p_input->stream.p_new_program = NULL;
if( i_data_len )
{
if ( (p_input->stream.p_demux_data = malloc( i_data_len )) == NULL )
{
msg_Err( p_input, "out of memory" );
return 1;
}
memset( p_input->stream.p_demux_data, 0, i_data_len );
}
else
{
p_input->stream.p_demux_data = NULL;
}
var_Create( p_input, "intf-change", VLC_VAR_BOOL );
val.b_bool = VLC_TRUE;
var_Set( p_input, "intf-change", val );
/* Create a few object variables used for navigation in the interfaces */
var_Create( p_input, "program", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE |
VLC_VAR_DOINHERIT );
var_Get( p_input, "program", &val );
if( val.i_int <= 0 )
var_Change( p_input, "program", VLC_VAR_DELCHOICE, &val, NULL );
text.psz_string = _("Program");
var_Change( p_input, "program", VLC_VAR_SETTEXT, &text, NULL );
var_Create( p_input, "title", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
text.psz_string = _("Title");
var_Change( p_input, "title", VLC_VAR_SETTEXT, &text, NULL );
var_Create( p_input, "chapter", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
text.psz_string = _("Chapter");
var_Change( p_input, "chapter", VLC_VAR_SETTEXT, &text, NULL );
var_Create( p_input, "navigation", VLC_VAR_VARIABLE | VLC_VAR_HASCHOICE );
text.psz_string = _("Navigation");
var_Change( p_input, "navigation", VLC_VAR_SETTEXT, &text, NULL );
var_Create( p_input, "video-es", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
text.psz_string = _("Video Track");
var_Change( p_input, "video-es", VLC_VAR_SETTEXT, &text, NULL );
var_Create( p_input, "audio-es", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
text.psz_string = _("Audio Track");
var_Change( p_input, "audio-es", VLC_VAR_SETTEXT, &text, NULL );
var_Create( p_input, "spu-es", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
text.psz_string = _("Subtitles Track");
var_Change( p_input, "spu-es", VLC_VAR_SETTEXT, &text, NULL );
var_AddCallback( p_input, "program", ProgramCallback, NULL );
var_AddCallback( p_input, "title", TitleCallback, NULL );
var_AddCallback( p_input, "chapter", ChapterCallback, NULL );
var_AddCallback( p_input, "video-es", ESCallback, NULL );
var_AddCallback( p_input, "audio-es", ESCallback, NULL );
var_AddCallback( p_input, "spu-es", ESCallback, NULL );
return VLC_SUCCESS;
}
/*****************************************************************************
* input_EndStream: free all stream descriptors
*****************************************************************************/
void input_EndStream( input_thread_t * p_input )
{
vlc_mutex_lock( &p_input->stream.stream_lock );
/* Free all programs and associated ES, and associated decoders. */
while( p_input->stream.i_pgrm_number )
{
input_DelProgram( p_input, p_input->stream.pp_programs[0] );
}
/* Free standalone ES */
while( p_input->stream.i_es_number )
{
input_DelES( p_input, p_input->stream.pp_es[0] );
}
/* Free all areas */
while( p_input->stream.i_area_nb )
{
input_DelArea( p_input, p_input->stream.pp_areas[0] );
}
/* Free selected ES */
if( p_input->stream.pp_selected_es != NULL )
{
free( p_input->stream.pp_selected_es );
}
if( p_input->stream.p_demux_data != NULL )
{
free( p_input->stream.p_demux_data );
}
vlc_mutex_unlock( &p_input->stream.stream_lock );
/* Free navigation variables */
var_Destroy( p_input, "program" );
var_Destroy( p_input, "title" );
var_Destroy( p_input, "chapter" );
var_Destroy( p_input, "video-es" );
var_Destroy( p_input, "audio-es" );
var_Destroy( p_input, "spu-es" );
var_Destroy( p_input, "intf-change" );
}
/*****************************************************************************
* input_FindProgram: returns a pointer to a program described by its ID
*****************************************************************************/
pgrm_descriptor_t * input_FindProgram( input_thread_t * p_input,
uint16_t i_pgrm_id )
{
unsigned int i;
for( i = 0; i < p_input->stream.i_pgrm_number; i++ )
{
if( p_input->stream.pp_programs[i]->i_number == i_pgrm_id )
{
return p_input->stream.pp_programs[i];
}
}
return NULL;
}
/*****************************************************************************
* input_AddProgram: add and init a program descriptor
*****************************************************************************
* This program descriptor will be referenced in the given stream descriptor
*****************************************************************************/
pgrm_descriptor_t * input_AddProgram( input_thread_t * p_input,
uint16_t i_pgrm_id, size_t i_data_len )
{
/* Where to add the pgrm */
pgrm_descriptor_t * p_pgrm = malloc( sizeof(pgrm_descriptor_t) );
vlc_value_t val;
if( p_pgrm == NULL )
{
msg_Err( p_input, "out of memory" );
return NULL;
}
/* Init this entry */
p_pgrm->i_number = i_pgrm_id;
p_pgrm->b_is_ok = 0;
p_pgrm->i_version = 0;
p_pgrm->i_es_number = 0;
p_pgrm->pp_es = NULL;
input_ClockInit( p_pgrm );
p_pgrm->i_synchro_state = SYNCHRO_START;
if( i_data_len )
{
p_pgrm->p_demux_data = malloc( i_data_len );
if( p_pgrm->p_demux_data == NULL )
{
msg_Err( p_input, "out of memory" );
return NULL;
}
memset( p_pgrm->p_demux_data, 0, i_data_len );
}
else
{
p_pgrm->p_demux_data = NULL;
}
/* Add an entry to the list of program associated with the stream */
INSERT_ELEM( p_input->stream.pp_programs,
p_input->stream.i_pgrm_number,
p_input->stream.i_pgrm_number,
p_pgrm );
val.i_int = i_pgrm_id;
var_Change( p_input, "program", VLC_VAR_ADDCHOICE, &val, NULL );
return p_pgrm;
}
/*****************************************************************************
* input_DelProgram: destroy a program descriptor
*****************************************************************************
* All ES descriptions referenced in the descriptor will be deleted.
*****************************************************************************/
void input_DelProgram( input_thread_t * p_input, pgrm_descriptor_t * p_pgrm )
{
unsigned int i_pgrm_index;
vlc_value_t val;
/* Find the program in the programs table */
for( i_pgrm_index = 0; i_pgrm_index < p_input->stream.i_pgrm_number;
i_pgrm_index++ )
{
if( p_input->stream.pp_programs[i_pgrm_index] == p_pgrm )
break;
}
/* If the program wasn't found, do nothing */
if( i_pgrm_index == p_input->stream.i_pgrm_number )
{
msg_Err( p_input, "program does not belong to this input" );
return;
}
val.i_int = p_input->stream.pp_programs[i_pgrm_index]->i_number;
var_Change( p_input, "program", VLC_VAR_DELCHOICE, &val, NULL );
/* Free the structures that describe the es that belongs to that program */
while( p_pgrm->i_es_number )
{
input_DelES( p_input, p_pgrm->pp_es[0] );
}
/* Free the demux data */
if( p_pgrm->p_demux_data != NULL )
{
free( p_pgrm->p_demux_data );
}
/* Remove this program from the stream's list of programs */
REMOVE_ELEM( p_input->stream.pp_programs,
p_input->stream.i_pgrm_number,
i_pgrm_index );
if( p_pgrm == p_input->stream.p_selected_program )
p_input->stream.p_selected_program = NULL;
/* Free the description of this program */
free( p_pgrm );
}
/****************************************************************************
* input_ChangeProgram: interface request a program change (internal)
****************************************************************************/
static int input_ChangeProgram( input_thread_t * p_input, uint16_t i_program_number )
{
pgrm_descriptor_t * p_program;
vlc_value_t val;
vlc_mutex_lock( &p_input->stream.stream_lock );
p_program = input_FindProgram( p_input, i_program_number );
if ( p_program == NULL )
{
msg_Err( p_input, "could not find selected program" );
return -1;
}
p_input->stream.p_new_program = p_program;
vlc_mutex_unlock( &p_input->stream.stream_lock );
/* Update the navigation variables without triggering a callback */
val.i_int = i_program_number;
var_Change( p_input, "program", VLC_VAR_SETVALUE, &val, NULL );
return 0;
}
/*****************************************************************************
* input_AddArea: add and init an area descriptor
*****************************************************************************
* This area descriptor will be referenced in the given stream descriptor
*****************************************************************************/
input_area_t * input_AddArea( input_thread_t * p_input,
uint16_t i_area_id, uint16_t i_part_nb )
{
/* Where to add the pgrm */
input_area_t * p_area = malloc( sizeof(input_area_t) );
vlc_value_t val;
int i;
if( p_area == NULL )
{
msg_Err( p_input, "out of memory" );
return NULL;
}
/* Init this entry */
p_area->i_id = i_area_id;
p_area->i_part_nb = i_part_nb;
p_area->i_part= 0;
p_area->i_start = 0;
p_area->i_size = 0;
p_area->i_tell = 0;
p_area->i_seek = NO_SEEK;
/* Add an entry to the list of program associated with the stream */
INSERT_ELEM( p_input->stream.pp_areas,
p_input->stream.i_area_nb,
p_input->stream.i_area_nb,
p_area );
/* Don't add empty areas */
if( i_part_nb == 0 )
return NULL;
/* Take care of the navigation variables */
val.i_int = i_area_id;
var_Change( p_input, "title", VLC_VAR_ADDCHOICE, &val, NULL );
val.psz_string = malloc( sizeof("title ") + 5 );
if( val.psz_string )
{
vlc_value_t val2, text, text2;
sprintf( val.psz_string, "title %2i", i_area_id );
var_Destroy( p_input, val.psz_string );
var_Create( p_input, val.psz_string, VLC_VAR_INTEGER |
VLC_VAR_HASCHOICE | VLC_VAR_ISCOMMAND );
var_AddCallback( p_input, val.psz_string, NavigationCallback,
(void *)(int)i_area_id );
text.psz_string = malloc( strlen( _("Title %i") ) + 20 );
if( text.psz_string )
sprintf( text.psz_string, _("Title %i"), i_area_id );
var_Change( p_input, "navigation", VLC_VAR_ADDCHOICE, &val, &text );
if( text.psz_string ) free( text.psz_string );
text2.psz_string = malloc( strlen( _("Chapter %i") ) + 20 );
for( i = 1; i <= i_part_nb; i++ )
{
val2.i_int = i;
if( text2.psz_string )
sprintf( text2.psz_string, _("Chapter %i"), i );
var_Change( p_input, val.psz_string,
VLC_VAR_ADDCHOICE, &val2, &text2 );
}
if( text2.psz_string ) free( text2.psz_string );
free( val.psz_string );
}
if( p_input->stream.i_area_nb == 2 )
{
vlc_value_t text;
/* Add another bunch of navigation object variables */
var_Create( p_input, "next-title", VLC_VAR_VOID );
text.psz_string = _("Next title");
var_Change( p_input, "next-title", VLC_VAR_SETTEXT, &text, NULL );
var_Create( p_input, "prev-title", VLC_VAR_VOID );
text.psz_string = _("Previous title");
var_Change( p_input, "prev-title", VLC_VAR_SETTEXT, &text, NULL );
var_AddCallback( p_input, "next-title", TitleCallback, NULL );
var_AddCallback( p_input, "prev-title", TitleCallback, NULL );
var_Create( p_input, "next-chapter", VLC_VAR_VOID );
text.psz_string = _("Next chapter");
var_Change( p_input, "next-chapter", VLC_VAR_SETTEXT, &text, NULL );
var_Create( p_input, "prev-chapter", VLC_VAR_VOID );
text.psz_string = _("Previous chapter");
var_Change( p_input, "prev-chapter", VLC_VAR_SETTEXT, &text, NULL );
var_AddCallback( p_input, "next-chapter", ChapterCallback, NULL );
var_AddCallback( p_input, "prev-chapter", ChapterCallback, NULL );
}
return p_area;
}
/*****************************************************************************
* input_SetProgram: changes the current program
*****************************************************************************/
int input_SetProgram( input_thread_t * p_input, pgrm_descriptor_t * p_new_prg )
{
unsigned int i_es_index;
int i_required_audio_es;
int i_required_spu_es;
int i_audio_es = 0;
int i_spu_es = 0;
vlc_value_t val;
if ( p_input->stream.p_selected_program )
{
for ( i_es_index = 1 ; /* 0 should be the PMT */
i_es_index < p_input->stream.p_selected_program->
i_es_number ;
i_es_index ++ )
{
#define p_es p_input->stream.p_selected_program->pp_es[i_es_index]
if ( p_es->p_dec ) /* if the ES was selected */
{
input_UnselectES( p_input , p_es );
}
#undef p_es
}
}
/* Get the number of the required audio stream */
var_Get( p_input, "audio", &val );
if( val.b_bool )
{
/* Default is the first one */
var_Get( p_input, "audio-channel", &val );
i_required_audio_es = val.i_int;
if( i_required_audio_es < 0 )
{
i_required_audio_es = 1;
}
}
else
{
i_required_audio_es = 0;
}
/* Same thing for subtitles */
var_Get( p_input, "video", &val );
if( val.b_bool )
{
/* for spu, default is none */
var_Get( p_input, "spu-channel", &val );
i_required_spu_es = val.i_int;
if( i_required_spu_es < 0 )
{
i_required_spu_es = 0;
}
}
else
{
i_required_spu_es = 0;
}
for( i_es_index = 0 ; i_es_index < p_new_prg->i_es_number ; i_es_index ++ )
{
switch( p_new_prg->pp_es[i_es_index]->i_cat )
{
case VIDEO_ES:
msg_Dbg( p_input, "selecting video ES %x",
p_new_prg->pp_es[i_es_index]->i_id );
input_SelectES( p_input, p_new_prg->pp_es[i_es_index] );
break;
case AUDIO_ES:
i_audio_es += 1;
if( i_audio_es <= i_required_audio_es )
{
msg_Dbg( p_input, "selecting audio ES %x",
p_new_prg->pp_es[i_es_index]->i_id );
input_SelectES( p_input, p_new_prg->pp_es[i_es_index]);
}
break;
/* Not sure this one is fully specification-compliant */
case SPU_ES :
i_spu_es += 1;
if( i_spu_es <= i_required_spu_es )
{
msg_Dbg( p_input, "selecting spu ES %x",
p_new_prg->pp_es[i_es_index]->i_id );
input_SelectES( p_input, p_new_prg->pp_es[i_es_index] );
}
break;
default :
msg_Dbg( p_input, "ES %x has unknown type",
p_new_prg->pp_es[i_es_index]->i_id );
break;
}
}
p_input->stream.p_selected_program = p_new_prg;
/* Update the navigation variables without triggering a callback */
val.i_int = p_new_prg->i_number;
var_Change( p_input, "program", VLC_VAR_SETVALUE, &val, NULL );
return( 0 );
}
/*****************************************************************************
* input_DelArea: destroy a area descriptor
*****************************************************************************
* All ES descriptions referenced in the descriptor will be deleted.
*****************************************************************************/
void input_DelArea( input_thread_t * p_input, input_area_t * p_area )
{
unsigned int i_area_index;
vlc_value_t val;
/* Find the area in the areas table */
for( i_area_index = 0; i_area_index < p_input->stream.i_area_nb;
i_area_index++ )
{
if( p_input->stream.pp_areas[i_area_index] == p_area )
break;
}
/* If the area wasn't found, do nothing */
if( i_area_index == p_input->stream.i_area_nb )
{
msg_Err( p_input, "area does not belong to this input" );
return;
}
/* Take care of the navigation variables */
val.psz_string = malloc( sizeof("title ") + 5 );
if( val.psz_string )
{
sprintf( val.psz_string, "title %i", p_area->i_id );
var_Change( p_input, "navigation", VLC_VAR_DELCHOICE, &val, NULL );
var_Destroy( p_input, val.psz_string );
free( val.psz_string );
}
/* Remove this area from the stream's list of areas */
REMOVE_ELEM( p_input->stream.pp_areas,
p_input->stream.i_area_nb,
i_area_index );
/* Free the description of this area */
free( p_area );
if( p_input->stream.i_area_nb == 1 )
{
/* Del unneeded navigation object variables */
var_Destroy( p_input, "next-title" );
var_Destroy( p_input, "prev-title" );
var_Destroy( p_input, "next-chapter" );
var_Destroy( p_input, "prev-chapter" );
}
}
/****************************************************************************
* input_ChangeArea: interface request an area change (internal)
****************************************************************************/
static int input_ChangeArea( input_thread_t * p_input, input_area_t * p_area )
{
vlc_mutex_lock( &p_input->stream.stream_lock );
p_input->stream.p_new_area = p_area;
vlc_mutex_unlock( &p_input->stream.stream_lock );
return 0;
}
/*****************************************************************************
* input_FindES: returns a pointer to an ES described by its ID
*****************************************************************************/
es_descriptor_t * input_FindES( input_thread_t * p_input, uint16_t i_es_id )
{
unsigned int i;
for( i = 0; i < p_input->stream.i_es_number; i++ )
{
if( p_input->stream.pp_es[i]->i_id == i_es_id )
{
return p_input->stream.pp_es[i];
}
}
return NULL;
}
/*****************************************************************************
* input_AddES:
*****************************************************************************
* Reserve a slot in the table of ES descriptors for the ES and add it to the
* list of ES of p_pgrm. If p_pgrm if NULL, then the ES is considered as stand
* alone (PSI ?)
*****************************************************************************/
es_descriptor_t * input_AddES( input_thread_t * p_input,
pgrm_descriptor_t * p_pgrm, uint16_t i_es_id,
int i_category, char const *psz_desc,
size_t i_data_len )
{
es_descriptor_t * p_es;
vlc_value_t val, text;
char *psz_var = NULL;
p_es = (es_descriptor_t *)malloc( sizeof(es_descriptor_t) );
if( p_es == NULL )
{
msg_Err( p_input, "out of memory" );
return( NULL);
}
INSERT_ELEM( p_input->stream.pp_es,
p_input->stream.i_es_number,
p_input->stream.i_es_number,
p_es );
/* Init its values */
p_es->i_id = i_es_id;
p_es->i_stream_id = 0;
p_es->p_pes = NULL;
p_es->p_dec = NULL;
p_es->i_cat = i_category;
p_es->i_demux_fd = 0;
p_es->c_packets = 0;
p_es->c_invalid_packets = 0;
p_es->b_force_decoder = VLC_FALSE;
es_format_Init( &p_es->fmt, UNKNOWN_ES, 0 );
p_es->fmt.b_packetized = VLC_FALSE; /* Only there for old mpeg demuxers */
if( i_data_len )
{
p_es->p_demux_data = malloc( i_data_len );
if( p_es->p_demux_data == NULL )
{
msg_Err( p_input, "out of memory" );
return( NULL );
}
memset( p_es->p_demux_data, 0, i_data_len );
}
else
{
p_es->p_demux_data = NULL;
}
p_es->p_waveformatex = NULL;
p_es->p_bitmapinfoheader = NULL;
p_es->p_spuinfo = NULL;
/* Add this ES to the program definition if one is given */
if( p_pgrm )
{
INSERT_ELEM( p_pgrm->pp_es,
p_pgrm->i_es_number,
p_pgrm->i_es_number,
p_es );
p_es->p_pgrm = p_pgrm;
}
else
{
p_es->p_pgrm = NULL;
}
switch( i_category )
{
case AUDIO_ES:
psz_var = "audio-es";
break;
case SPU_ES:
psz_var = "spu-es";
break;
case VIDEO_ES:
psz_var = "video-es";
break;
}
if( psz_var )
{
/* Get the number of ES already added */
var_Change( p_input, psz_var, VLC_VAR_CHOICESCOUNT, &val, NULL );
if( val.i_int == 0 )
{
vlc_value_t val2;
/* First one, we need to add the "Disable" choice */
val2.i_int = -1; text.psz_string = _("Disable");
var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val2, &text );
val.i_int++;
}
/* Take care of the ES description */
if( psz_desc && *psz_desc )
{
p_es->psz_desc = strdup( psz_desc );
}
else
{
p_es->psz_desc = malloc( strlen( _("Track %i") ) + 20 );
if( p_es->psz_desc )
sprintf( p_es->psz_desc, _("Track %i"), val.i_int );
}
val.i_int = p_es->i_id;
text.psz_string = p_es->psz_desc;
var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val, &text );
}
else p_es->psz_desc = NULL;
return p_es;
}
/*****************************************************************************
* input_DelES:
*****************************************************************************/
void input_DelES( input_thread_t * p_input, es_descriptor_t * p_es )
{
unsigned int i_index, i_es_index;
pgrm_descriptor_t * p_pgrm;
char * psz_var = NULL;
vlc_value_t val;
/* Find the ES in the ES table */
for( i_es_index = 0; i_es_index < p_input->stream.i_es_number;
i_es_index++ )
{
if( p_input->stream.pp_es[i_es_index] == p_es )
break;
}
/* If the ES wasn't found, do nothing */
if( i_es_index == p_input->stream.i_es_number )
{
msg_Err( p_input, "ES does not belong to this input" );
return;
}
/* Remove es from its associated variable */
switch( p_es->i_cat )
{
case AUDIO_ES:
psz_var = "audio-es";
break;
case SPU_ES:
psz_var = "spu-es";
break;
case VIDEO_ES:
psz_var = "video-es";
break;
}
if( psz_var )
{
val.i_int = p_es->i_id;
var_Change( p_input, psz_var, VLC_VAR_DELCHOICE, &val, NULL );
/* Remove the "Disable" entry if needed */
var_Change( p_input, psz_var, VLC_VAR_CHOICESCOUNT, &val, NULL );
if( val.i_int == 1 )
{
val.i_int = -1;
var_Change( p_input, psz_var, VLC_VAR_DELCHOICE, &val, NULL );
}
}
/* Kill associated decoder, if any. */
if( p_es->p_dec != NULL )
{
input_UnselectES( p_input, p_es );
}
/* Remove this ES from the description of the program if it is associated
* to one */
p_pgrm = p_es->p_pgrm;
if( p_pgrm )
{
for( i_index = 0; i_index < p_pgrm->i_es_number; i_index++ )
{
if( p_pgrm->pp_es[i_index] == p_es )
{
REMOVE_ELEM( p_pgrm->pp_es,
p_pgrm->i_es_number,
i_index );
break;
}
}
}
/* Free the demux data */
if( p_es->p_demux_data != NULL )
{
free( p_es->p_demux_data );
}
if( p_es->p_waveformatex )
{
free( p_es->p_waveformatex );
}
if( p_es->p_bitmapinfoheader )
{
free( p_es->p_bitmapinfoheader );
}
if( p_es->p_spuinfo )
{
free( p_es->p_spuinfo );
}
/* Free the description string */
if( p_es->psz_desc != NULL )
{
free( p_es->psz_desc );
}
/* Clean the es format */
es_format_Clean( &p_es->fmt );
/* Find the ES in the ES table */
for( i_es_index = 0; i_es_index < p_input->stream.i_es_number;
i_es_index++ )
{
if( p_input->stream.pp_es[i_es_index] == p_es )
break;
}
/* Remove this ES from the stream's list of ES */
REMOVE_ELEM( p_input->stream.pp_es,
p_input->stream.i_es_number,
i_es_index );
/* Free the ES */
free( p_es );
}
/*****************************************************************************
* input_SelectES: selects an ES and spawns the associated decoder
*****************************************************************************
* Remember we are still supposed to have stream_lock when entering this
* function ?
*****************************************************************************/
int input_SelectES( input_thread_t * p_input, es_descriptor_t * p_es )
{
vlc_value_t val;
char *psz_var = NULL;
if( p_es == NULL )
{
msg_Err( p_input, "nothing to do in input_SelectES" );
return -1;
}
if( p_es->i_cat == VIDEO_ES || p_es->i_cat == SPU_ES )
{
var_Get( p_input, "video", &val );
if( val.b_bool && p_input->stream.p_sout )
{
var_Get( p_input, "sout-video", &val );
}
if( !val.b_bool )
{
msg_Dbg( p_input, "video is disabled, not selecting ES 0x%x",
p_es->i_id );
return -1;
}
}
if( p_es->i_cat == AUDIO_ES )
{
var_Get( p_input, "audio", &val );
if( val.b_bool && p_input->stream.p_sout )
{
var_Get( p_input, "sout-audio", &val );
}
if( !val.b_bool )
{
msg_Dbg( p_input, "audio is disabled, not selecting ES 0x%x",
p_es->i_id );
return -1;
}
}
msg_Dbg( p_input, "selecting ES 0x%x", p_es->i_id );
if( p_es->p_dec != NULL )
{
msg_Err( p_input, "ES 0x%x is already selected", p_es->i_id );
return -1;
}
/* Release the lock, not to block the input thread during
* the creation of the thread. */
vlc_mutex_unlock( &p_input->stream.stream_lock );
p_es->p_dec = input_RunDecoder( p_input, p_es );
vlc_mutex_lock( &p_input->stream.stream_lock );
if( p_es->p_dec == NULL )
{
return -1;
}
/* Update the es variable without triggering a callback */
switch( p_es->i_cat )
{
case AUDIO_ES:
psz_var = "audio-es";
break;
case SPU_ES:
psz_var = "spu-es";
break;
case VIDEO_ES:
psz_var = "video-es";
break;
}
if( psz_var )
{
val.i_int = p_es->i_id;
var_Change( p_input, psz_var, VLC_VAR_SETVALUE, &val, NULL );
}
return 0;
}
/*****************************************************************************
* input_UnselectES: removes an ES from the list of selected ES
*****************************************************************************/
int input_UnselectES( input_thread_t * p_input, es_descriptor_t * p_es )
{
unsigned int i_index = 0;
vlc_value_t val;
char *psz_var = NULL;
if( p_es == NULL )
{
msg_Err( p_input, "nothing to do in input_UnselectES" );
return -1;
}
msg_Dbg( p_input, "unselecting ES 0x%x", p_es->i_id );
if( p_es->p_dec == NULL )
{
msg_Err( p_input, "ES 0x%x is not selected", p_es->i_id );
return( -1 );
}
/* Update the es variable without triggering a callback */
switch( p_es->i_cat )
{
case AUDIO_ES:
psz_var = "audio-es";
break;
case SPU_ES:
psz_var = "spu-es";
break;
case VIDEO_ES:
psz_var = "video-es";
break;
}
if( psz_var )
{
val.i_int = -1;
var_Change( p_input, psz_var, VLC_VAR_SETVALUE, &val, NULL );
}
/* FIXME: input_UnselectES() shouldn't actually be entered with the
* input lock, the locking should be done here and only where necessary. */
vlc_mutex_unlock( &p_input->stream.stream_lock );
/* Actually unselect the ES */
input_EndDecoder( p_input, p_es );
vlc_mutex_lock( &p_input->stream.stream_lock );
p_es->p_pes = NULL;
if( ( p_es->p_dec == NULL ) &&
( p_input->stream.i_selected_es_number > 0 ) )
{
while( ( i_index < p_input->stream.i_selected_es_number - 1 ) &&
( p_input->stream.pp_selected_es[i_index] != p_es ) )
{
i_index++;
}
/* XXX: no need to memmove, we have unsorted data */
REMOVE_ELEM( p_input->stream.pp_selected_es,
p_input->stream.i_selected_es_number,
i_index );
if( p_input->stream.i_selected_es_number == 0 )
{
msg_Dbg( p_input, "no more selected ES" );
return 1;
}
}
return 0;
}
/*****************************************************************************
* Navigation callback: a bunch of navigation variables are used as an
* alternative to the navigation API.
*****************************************************************************/
static int ProgramCallback( vlc_object_t *p_this, char const *psz_cmd,
vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
input_thread_t *p_input = (input_thread_t *)p_this;
vlc_value_t val;
if( oldval.i_int == newval.i_int )
return VLC_SUCCESS;
vlc_mutex_lock( &p_input->stream.stream_lock );
if( ( newval.i_int > 0 ) )
{
vlc_mutex_unlock( &p_input->stream.stream_lock );
input_ChangeProgram( p_input, (uint16_t)newval.i_int );
var_SetInteger( p_input, "state", PLAYING_S );
vlc_mutex_lock( &p_input->stream.stream_lock );
}
vlc_mutex_unlock( &p_input->stream.stream_lock );
val.b_bool = VLC_TRUE;
var_Set( p_input, "intf-change", val );
return VLC_SUCCESS;
}
static int TitleCallback( vlc_object_t *p_this, char const *psz_cmd,
vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
input_thread_t *p_input = (input_thread_t *)p_this;
input_area_t *p_area;
vlc_value_t val, val_list;
int i, i_step = 0;
if( !strcmp( psz_cmd, "next-title" ) ) i_step++;
else if( !strcmp( psz_cmd, "prev-title" ) ) i_step--;
if( !i_step && oldval.i_int == newval.i_int ) return VLC_SUCCESS;
/* Sanity check should have already been done by var_Set(). */
vlc_mutex_lock( &p_input->stream.stream_lock );
if( i_step )
{
var_Get( p_this, "title", &newval );
var_Change( p_this, "title", VLC_VAR_GETCHOICES, &val_list, NULL );
for( i = 0; i < val_list.p_list->i_count; i++ )
{
if( val_list.p_list->p_values[i].i_int == newval.i_int &&
i + i_step >= 0 && i + i_step < val_list.p_list->i_count )
{
newval.i_int = val_list.p_list->p_values[i + i_step].i_int;
break;
}
}
var_Change( p_this, "title", VLC_VAR_FREELIST, &val_list, NULL );
}
p_area = p_input->stream.pp_areas[newval.i_int];
p_area->i_part = 1;
vlc_mutex_unlock( &p_input->stream.stream_lock );
input_ChangeArea( p_input, p_area );
var_SetInteger( p_input, "state", PLAYING_S );
val.b_bool = VLC_TRUE;
var_Set( p_input, "intf-change", val );
return VLC_SUCCESS;
}
static int ChapterCallback( vlc_object_t *p_this, char const *psz_cmd,
vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
input_thread_t *p_input = (input_thread_t *)p_this;
input_area_t *p_area;
vlc_value_t val, val_list;
int i, i_step = 0;
if( !strcmp( psz_cmd, "next-chapter" ) ) i_step++;
else if( !strcmp( psz_cmd, "prev-chapter" ) ) i_step--;
if( !i_step && oldval.i_int == newval.i_int ) return VLC_SUCCESS;
/* Sanity check should have already been done by var_Set(). */
vlc_mutex_lock( &p_input->stream.stream_lock );
if( i_step )
{
var_Get( p_this, "chapter", &newval );
var_Change( p_this, "chapter", VLC_VAR_GETCHOICES, &val_list, NULL );
for( i = 0; i < val_list.p_list->i_count; i++ )
{
if( val_list.p_list->p_values[i].i_int == newval.i_int &&
i + i_step >= 0 && i + i_step < val_list.p_list->i_count )
{
newval.i_int = val_list.p_list->p_values[i + i_step].i_int;
break;
}
}
var_Change( p_this, "chapter", VLC_VAR_FREELIST, &val_list, NULL );
}
p_area = p_input->stream.p_selected_area;
p_input->stream.p_selected_area->i_part = newval.i_int;
vlc_mutex_unlock( &p_input->stream.stream_lock );
input_ChangeArea( p_input, p_area );
var_SetInteger( p_input, "state", PLAYING_S );
val.b_bool = VLC_TRUE;
var_Set( p_input, "intf-change", val );
return VLC_SUCCESS;
}
static int NavigationCallback( vlc_object_t *p_this, char const *psz_cmd,
vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
input_thread_t *p_input = (input_thread_t *)p_this;
uint16_t i_area_id = (int)p_data;
vlc_value_t val;
vlc_mutex_lock( &p_input->stream.stream_lock );
if( p_input->stream.p_selected_area->i_id == i_area_id &&
oldval.i_int == newval.i_int )
{
/* Nothing to do */
vlc_mutex_unlock( &p_input->stream.stream_lock );
return VLC_SUCCESS;
}
if( ( i_area_id < p_input->stream.i_area_nb ) && ( newval.i_int > 0 ) &&
( (uint16_t)newval.i_int <=
p_input->stream.pp_areas[i_area_id]->i_part_nb ) )
{
input_area_t *p_area = p_input->stream.pp_areas[i_area_id];
p_area->i_part = newval.i_int;
vlc_mutex_unlock( &p_input->stream.stream_lock );
input_ChangeArea( p_input, p_area );
var_SetInteger( p_input, "state", PLAYING_S );
vlc_mutex_lock( &p_input->stream.stream_lock );
}
vlc_mutex_unlock( &p_input->stream.stream_lock );
val.b_bool = VLC_TRUE;
var_Set( p_input, "intf-change", val );
return VLC_SUCCESS;
}
static int ESCallback( vlc_object_t *p_this, char const *psz_cmd,
vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
input_thread_t *p_input = (input_thread_t *)p_this;
unsigned int i;
vlc_value_t val;
unsigned int i_cat = UNKNOWN_ES;
es_descriptor_t *p_es = NULL;
vlc_mutex_lock( &p_input->stream.stream_lock );
/* First search old es type */
for( i = 0 ; i < p_input->stream.i_es_number ; i++ )
{
if( p_input->stream.pp_es[i]->i_id == oldval.i_int )
{
i_cat = p_input->stream.pp_es[i]->i_cat;
}
}
/* Unselect all old ES */
for( i = 0 ; i < p_input->stream.i_es_number ; i++ )
{
if( p_input->stream.pp_es[i]->i_cat == i_cat &&
p_input->stream.pp_es[i]->i_id != newval.i_int &&
p_input->stream.pp_es[i]->p_dec != NULL )
{
input_UnselectES( p_input, p_input->stream.pp_es[i] );
}
}
/* Select new ES */
for( i = 0 ; i < p_input->stream.i_es_number ; i++ )
{
if( p_input->stream.pp_es[i]->i_id == newval.i_int )
{
p_es = p_input->stream.pp_es[i];
if( p_es->p_dec == NULL )
{
input_SelectES( p_input, p_es );
}
}
}
if( p_es )
{
/* Fix value (mainly for multiple selected ES */
val.i_int = p_es->i_id;
switch( p_es->i_cat )
{
case AUDIO_ES:
var_Change( p_input, "audio-es", VLC_VAR_SETVALUE, &val, NULL );
break;
case SPU_ES:
var_Change( p_input, "spu-es", VLC_VAR_SETVALUE, &val, NULL );
break;
case VIDEO_ES:
var_Change( p_input, "video-es", VLC_VAR_SETVALUE, &val, NULL );
break;
}
}
vlc_mutex_unlock( &p_input->stream.stream_lock );
val.b_bool = VLC_TRUE;
var_Set( p_input, "intf-change", val );
return VLC_SUCCESS;
}
......@@ -25,576 +25,1138 @@
#include <vlc/vlc.h>
#include <vlc/input.h>
#include "ninput.h"
#include "input_internal.h"
/****************************************************************************
* stream_ReadLine:
****************************************************************************/
/**
* Read from the stream untill first newline.
* \param s Stream handle to read from
* \return A null-terminated string. This must be freed,
/* TODO:
* - tune the 2 methods
* - compute cost for seek
* - improve stream mode seeking with closest segments
* - ...
*/
/* FIXME don't use stupid MAX_LINE -> do the same than net_ReadLine */
#define MAX_LINE 1024
char *stream_ReadLine( stream_t *s )
/* Two methods:
* - using pf_block
* One linked list of data read
* - using pf_read
* More complex scheme using mutliple track to avoid seeking
*/
/* How many track we have, currently only used for stream mode */
#define STREAM_CACHE_TRACK 3
/* Max size of our cache 4Mo per track */
#define STREAM_CACHE_SIZE (4*STREAM_CACHE_TRACK*1024*1024)
/* How many data we try to prebuffer */
#define STREAM_CACHE_PREBUFFER_SIZE (32767)
#define STREAM_CACHE_PREBUFFER_LENGTH (100*1000) /* Maximum time we take to pre-buffer */
/* Method1: Simple, for pf_block.
* We get blocks and put them in the linked list.
* We release blocks once the total size is bigger than CACHE_BLOCK_SIZE
*/
#define STREAM_DATA_WAIT 40000 /* Time between before a pf_block retry */
/* Method2: A bit more complex, for pf_read
* - We use ring buffers, only one if unseekable, all if seekable
* - Upon seek date current ring, then search if one ring match the pos,
* yes: switch to it, seek the access to match the end of the ring
* no: search the ring with i_end the closer to i_pos,
* if close enough, read data and use this ring
* else use the oldest ring, seek and use it.
*
* TODO: - with access non seekable: use all space available for only one ring, but
* we have to support seekable/non-seekable switch on the fly.
* - compute a good value for i_read_size
* - ?
*/
#define STREAM_READ_ATONCE 32767
#define STREAM_CACHE_TRACK_SIZE (STREAM_CACHE_SIZE/STREAM_CACHE_TRACK)
typedef struct
{
uint8_t *p_data;
char *p_line;
int i_data;
int i = 0;
i_data = stream_Peek( s, &p_data, MAX_LINE );
int64_t i_date;
while( i < i_data && p_data[i] != '\n' && p_data[i] != '\r' )
int64_t i_start;
int64_t i_end;
uint8_t *p_buffer;
} stream_track_t;
struct stream_sys_t
{
access_t *p_access;
vlc_bool_t b_block; /* Block method (1) or stream */
int64_t i_pos; /* Current reading offset */
/* Method 1: pf_block */
struct
{
i++;
}
if( i_data <= 0 )
int64_t i_start; /* Offset of block for p_first */
int i_offset; /* Offset for data in p_current */
block_t *p_current; /* Current block */
int i_size; /* Total amount of data in the list */
block_t *p_first;
block_t **pp_last;
} block;
/* Method 2: for pf_read */
struct
{
return NULL;
}
else
int i_offset; /* Buffer ofset in the current track */
int i_tk; /* Current track */
stream_track_t tk[STREAM_CACHE_TRACK];
/* Global buffer */
uint8_t *p_buffer;
/* */
int i_used; /* Used since last read */
int i_read_size;
} stream;
/* Peek temporary buffer */
int i_peek;
uint8_t *p_peek;
/* Stat for both method */
struct
{
p_line = malloc( i + 1 );
if( p_line == NULL )
{
msg_Err( s, "out of memory" );
return NULL;
}
i = stream_Read( s, p_line, i + 1 );
p_line[ i - 1 ] = '\0';
vlc_bool_t b_fastseek; /* From access */
return p_line;
}
}
/* Stat about reading data */
int64_t i_read_count;
int64_t i_bytes;
int64_t i_read_time;
/* Stat about seek */
int i_seek_count;
int64_t i_seek_time;
} stat;
};
/* Method 1: */
static int AStreamReadBlock( stream_t *, void *p_read, int i_read );
static int AStreamPeekBlock( stream_t *, uint8_t **p_peek, int i_read );
static int AStreamSeekBlock( stream_t *s, int64_t i_pos );
static void AStreamPrebufferBlock( stream_t * );
/* TODO: one day we should create a special module stream
* when we would have a access wrapper, and stream filter
* (like caching, progessive, gunzip, ... )
*/
/* Method 2 */
static int AStreamReadStream( stream_t *, void *p_read, int i_read );
static int AStreamPeekStream( stream_t *, uint8_t **pp_peek, int i_read );
static int AStreamSeekStream( stream_t *s, int64_t i_pos );
static void AStreamPrebufferStream( stream_t * );
/* private stream_sys_t for input_Stream* */
typedef struct
{
input_thread_t *p_input;
} input_stream_sys_t;
/* Common */
static int AStreamControl( stream_t *, int i_query, va_list );
/* private pf_* functions declarations */
static int IStreamRead ( stream_t *, void *p_read, int i_read );
static int IStreamPeek ( stream_t *, uint8_t **pp_peek, int i_peek );
static int IStreamControl( stream_t *, int i_query, va_list );
/****************************************************************************
* input_StreamNew: create a wrapper for p_input access
* stream_AccessNew: create a stream from a access
****************************************************************************/
stream_t *input_StreamNew( input_thread_t *p_input )
stream_t *stream_AccessNew( access_t *p_access )
{
stream_t *s = vlc_object_create( p_input, sizeof( stream_t ) );
input_stream_sys_t *p_sys;
if( s )
stream_t *s = vlc_object_create( p_access, VLC_OBJECT_STREAM );
stream_sys_t *p_sys;
if( !s )
return NULL;
/* Attach it now, needed for b_die */
vlc_object_attach( s, p_access );
s->pf_block = NULL;
s->pf_read = NULL; /* Set up later */
s->pf_peek = NULL;
s->pf_control= AStreamControl;
s->p_sys = p_sys = malloc( sizeof( stream_sys_t ) );
/* Common field */
p_sys->p_access = p_access;
p_sys->b_block = p_access->pf_block ? VLC_TRUE : VLC_FALSE;
p_sys->i_pos = p_access->info.i_pos;
/* Stats */
access2_Control( p_access, ACCESS_CAN_FASTSEEK, &p_sys->stat.b_fastseek );
p_sys->stat.i_bytes = 0;
p_sys->stat.i_read_time = 0;
p_sys->stat.i_read_count = 0;
p_sys->stat.i_seek_count = 0;
p_sys->stat.i_seek_time = 0;
/* Peek */
p_sys->i_peek = 0;
p_sys->p_peek = NULL;
if( p_sys->b_block )
{
s->pf_read = AStreamReadBlock;
s->pf_peek = AStreamPeekBlock;
/* Init all fields of p_sys->block */
p_sys->block.i_start = p_sys->i_pos;
p_sys->block.i_offset = 0;
p_sys->block.p_current = NULL;
p_sys->block.i_size = 0;
p_sys->block.p_first = NULL;
p_sys->block.pp_last = &p_sys->block.p_first;
/* Do the prebuffering */
AStreamPrebufferBlock( s );
if( p_sys->block.i_size <= 0 )
{
msg_Err( s, "cannot pre fill buffer" );
goto error;
}
}
else
{
s->pf_block = NULL;
s->pf_read = IStreamRead;
s->pf_peek = IStreamPeek;
s->pf_control= IStreamControl;
int i;
s->pf_read = AStreamReadStream;
s->pf_peek = AStreamPeekStream;
/* Allocate/Setup our tracks */
p_sys->stream.i_offset = 0;
p_sys->stream.i_tk = 0;
p_sys->stream.p_buffer = malloc( STREAM_CACHE_SIZE );
p_sys->stream.i_used = 0;
access2_Control( p_access, ACCESS_GET_MTU, &p_sys->stream.i_read_size );
if( p_sys->stream.i_read_size <= 0 )
p_sys->stream.i_read_size = STREAM_READ_ATONCE;
else if( p_sys->stream.i_read_size <= 256 )
p_sys->stream.i_read_size = 256;
for( i = 0; i < STREAM_CACHE_TRACK; i++ )
{
p_sys->stream.tk[i].i_date = 0;
p_sys->stream.tk[i].i_start = p_sys->i_pos;
p_sys->stream.tk[i].i_end = p_sys->i_pos;
p_sys->stream.tk[i].p_buffer=
&p_sys->stream.p_buffer[i * STREAM_CACHE_TRACK_SIZE];
}
/* Do the prebuffering */
AStreamPrebufferStream( s );
s->p_sys = malloc( sizeof( input_stream_sys_t ) );
p_sys = (input_stream_sys_t*)s->p_sys;
p_sys->p_input = p_input;
if( p_sys->stream.tk[p_sys->stream.i_tk].i_end <= 0 )
{
msg_Err( s, "cannot pre fill buffer" );
goto error;
}
}
return s;
error:
if( p_sys->b_block )
{
/* Nothing yet */
}
else
{
free( p_sys->stream.p_buffer );
}
free( s->p_sys );
vlc_object_detach( s );
vlc_object_destroy( s );
return NULL;
}
/****************************************************************************
* input_StreamDelete:
* stream_AccessDelete:
****************************************************************************/
void input_StreamDelete( stream_t *s )
void stream_AccessDelete( stream_t *s )
{
stream_sys_t *p_sys = s->p_sys;
vlc_object_detach( s );
if( p_sys->b_block )
{
block_ChainRelease( p_sys->block.p_first );
}
else
{
free( p_sys->stream.p_buffer );
}
if( p_sys->p_peek )
free( p_sys->p_peek );
free( s->p_sys );
vlc_object_destroy( s );
}
/****************************************************************************
* stream_AccessReset:
****************************************************************************/
void stream_AccessReset( stream_t *s )
{
stream_sys_t *p_sys = s->p_sys;
p_sys->i_pos = p_sys->p_access->info.i_pos;
if( p_sys->b_block )
{
block_ChainRelease( p_sys->block.p_first );
/* Init all fields of p_sys->block */
p_sys->block.i_start = p_sys->i_pos;
p_sys->block.i_offset = 0;
p_sys->block.p_current = NULL;
p_sys->block.i_size = 0;
p_sys->block.p_first = NULL;
p_sys->block.pp_last = &p_sys->block.p_first;
/* Do the prebuffering */
AStreamPrebufferBlock( s );
}
else
{
int i;
/* Setup our tracks */
p_sys->stream.i_offset = 0;
p_sys->stream.i_tk = 0;
p_sys->stream.i_used = 0;
for( i = 0; i < STREAM_CACHE_TRACK; i++ )
{
p_sys->stream.tk[i].i_date = 0;
p_sys->stream.tk[i].i_start = p_sys->i_pos;
p_sys->stream.tk[i].i_end = p_sys->i_pos;
}
/* Do the prebuffering */
AStreamPrebufferStream( s );
}
}
/****************************************************************************
* IStreamControl:
* AStreamControl:
****************************************************************************/
static int IStreamControl( stream_t *s, int i_query, va_list args )
static int AStreamControl( stream_t *s, int i_query, va_list args )
{
input_stream_sys_t * p_sys = (input_stream_sys_t*)s->p_sys;
input_thread_t *p_input = p_sys->p_input;
stream_sys_t *p_sys = s->p_sys;
access_t *p_access = p_sys->p_access;
vlc_bool_t *p_b;
int64_t *p_i64, i64;
int *p_int;
vlc_bool_t *p_bool;
int64_t *pi_64, i_64;
int i_int;
switch( i_query )
{
case STREAM_GET_SIZE:
p_i64 = (int64_t*) va_arg( args, int64_t * );
vlc_mutex_lock( &p_input->stream.stream_lock );
*p_i64 = p_input->stream.p_selected_area->i_size;
vlc_mutex_unlock( &p_input->stream.stream_lock );
return VLC_SUCCESS;
pi_64 = (int64_t*)va_arg( args, int64_t * );
*pi_64 = p_access->info.i_size;
break;
case STREAM_CAN_SEEK:
p_b = (vlc_bool_t*) va_arg( args, vlc_bool_t * );
vlc_mutex_lock( &p_input->stream.stream_lock );
*p_b = p_input->stream.b_seekable;
vlc_mutex_unlock( &p_input->stream.stream_lock );
return VLC_SUCCESS;
p_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t * );
access2_Control( p_access, ACCESS_CAN_SEEK, p_bool );
break;
case STREAM_CAN_FASTSEEK:
p_b = (vlc_bool_t*) va_arg( args, vlc_bool_t * );
vlc_mutex_lock( &p_input->stream.stream_lock );
*p_b = p_input->stream.b_seekable &&
p_input->stream.i_method == INPUT_METHOD_FILE;
vlc_mutex_unlock( &p_input->stream.stream_lock );
return VLC_SUCCESS;
p_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t * );
access2_Control( p_access, ACCESS_CAN_FASTSEEK, p_bool );
break;
case STREAM_GET_POSITION:
p_i64 = (int64_t*) va_arg( args, int64_t * );
vlc_mutex_lock( &p_input->stream.stream_lock );
*p_i64 = p_input->stream.p_selected_area->i_tell;
vlc_mutex_unlock( &p_input->stream.stream_lock );
return VLC_SUCCESS;
pi_64 = (int64_t*)va_arg( args, int64_t * );
*pi_64 = p_sys->i_pos;
break;
case STREAM_SET_POSITION:
{
int64_t i_skip;
i64 = (int64_t) va_arg( args, int64_t );
vlc_mutex_lock( &p_input->stream.stream_lock );
if( i64 < 0 ||
( p_input->stream.p_selected_area->i_size > 0 &&
p_input->stream.p_selected_area->i_size < i64 ) )
{
vlc_mutex_unlock( &p_input->stream.stream_lock );
msg_Warn( s, "seek out of bound" );
return VLC_EGENERIC;
}
i_skip = i64 - p_input->stream.p_selected_area->i_tell;
if( i_skip == 0 )
{
vlc_mutex_unlock( &p_input->stream.stream_lock );
return VLC_SUCCESS;
}
if( i_skip > 0 && i_skip < p_input->p_last_data -
p_input->p_current_data - 1 )
{
/* We can skip without reading/seeking */
p_input->p_current_data += i_skip;
p_input->stream.p_selected_area->i_tell = i64;
vlc_mutex_unlock( &p_input->stream.stream_lock );
return VLC_SUCCESS;
}
vlc_mutex_unlock( &p_input->stream.stream_lock );
if( p_input->stream.b_seekable &&
( p_input->stream.i_method == INPUT_METHOD_FILE ||
i_skip < 0 || i_skip >= ( p_input->i_mtu > 0 ?
p_input->i_mtu : 4096 ) ) )
{
input_AccessReinit( p_input );
p_input->pf_seek( p_input, i64 );
return VLC_SUCCESS;
}
if( i_skip > 0 )
{
data_packet_t *p_data;
if( i_skip > 1000 )
{
msg_Warn( s, "will skip "I64Fd" bytes, slow", i_skip );
}
while( i_skip > 0 )
{
int i_read;
i_read = input_SplitBuffer( p_input, &p_data,
__MIN( (int)p_input->i_bufsize, i_skip ) );
if( i_read < 0 )
{
return VLC_EGENERIC;
}
i_skip -= i_read;
input_DeletePacket( p_input->p_method_data, p_data );
if( i_read == 0 && i_skip > 0 )
{
return VLC_EGENERIC;
}
}
}
return VLC_SUCCESS;
}
i_64 = (int64_t)va_arg( args, int64_t );
if( p_sys->b_block )
return AStreamSeekBlock( s, i_64 );
else
return AStreamSeekStream( s, i_64 );
case STREAM_GET_MTU:
p_int = (int*) va_arg( args, int * );
*p_int = p_input->i_mtu;
return VLC_SUCCESS;
return VLC_EGENERIC;
case STREAM_CONTROL_ACCESS:
{
int i_int = (int) va_arg( args, int );
i_int = (int) va_arg( args, int );
if( i_int != ACCESS_SET_PRIVATE_ID_STATE )
{
msg_Err( s, "Hey, what are you thinking ?"
"DON'T USE STREAM_CONTROL_ACCESS !!!" );
return VLC_EGENERIC;
}
if( p_input->pf_access_control )
{
return p_input->pf_access_control( p_input, i_int, args );
}
return VLC_EGENERIC;
}
return access2_Control( p_access, i_int, args );
default:
msg_Err( s, "invalid stream_vaControl query=0x%x", i_query );
return VLC_EGENERIC;
}
return VLC_SUCCESS;
}
/****************************************************************************
* IStreamRead:
* Method 1:
****************************************************************************/
static int IStreamRead( stream_t *s, void *p_data, int i_data )
static void AStreamPrebufferBlock( stream_t *s )
{
input_stream_sys_t * p_sys = (input_stream_sys_t*)s->p_sys;
input_thread_t *p_input = p_sys->p_input;
uint8_t *p = (uint8_t*)p_data;
stream_sys_t *p_sys = s->p_sys;
access_t *p_access = p_sys->p_access;
int i_read = 0;
int64_t i_first = 0;
int64_t i_start;
if( p_data == NULL && i_data > 0 )
msg_Dbg( s, "pre buffering" );
i_start = mdate();
for( ;; )
{
int64_t i_pos;
int64_t i_date = mdate();
block_t *b;
stream_Control( s, STREAM_GET_POSITION, &i_pos );
if( s->b_die ||
p_sys->block.i_size > STREAM_CACHE_PREBUFFER_SIZE ||
( i_first > 0 && i_first + STREAM_CACHE_PREBUFFER_LENGTH < i_date ) )
{
int64_t i_byterate;
/* Update stat */
p_sys->stat.i_bytes = p_sys->block.i_size;
p_sys->stat.i_read_time = i_date - i_start;
i_byterate = ( I64C(1000000) * p_sys->stat.i_bytes ) /
(p_sys->stat.i_read_time+1);
msg_Dbg( s, "prebuffering done %lld bytes in %llds - %lld kbytes/s",
p_sys->stat.i_bytes,
p_sys->stat.i_read_time / I64C(1000000),
i_byterate / 1024 );
break;
}
i_pos += i_data;
if( stream_Control( s, STREAM_SET_POSITION, i_pos ) )
/* Fetch a block */
if( ( b = p_access->pf_block( p_access ) ) == NULL )
{
return 0;
if( p_access->info.b_eof )
break;
msleep( STREAM_DATA_WAIT );
continue;
}
return i_data;
if( i_first == 0 )
i_first = mdate();
/* Append the block */
p_sys->block.i_size += b->i_buffer;
*p_sys->block.pp_last = b;
p_sys->block.pp_last = &b->p_next;
p_sys->stat.i_read_count++;
}
while( i_data > 0 && !p_input->b_die )
p_sys->block.p_current = p_sys->block.p_first;
}
static int AStreamRefillBlock( stream_t *s );
static int AStreamReadBlock( stream_t *s, void *p_read, int i_read )
{
stream_sys_t *p_sys = s->p_sys;
uint8_t *p_data= (uint8_t*)p_read;
int i_data = 0;
/* It means EOF */
if( p_sys->block.p_current == NULL )
return 0;
while( i_data < i_read )
{
ssize_t i_count = p_input->p_last_data - p_input->p_current_data;
int i_current = p_sys->block.p_current->i_buffer - p_sys->block.i_offset;
int i_copy = __MIN( i_current, i_read - i_data);
if( i_count <= 0 )
/* Copy data */
if( p_data )
{
/* Go to the next buffer */
i_count = input_FillBuffer( p_input );
memcpy( p_data, &p_sys->block.p_current->p_buffer[p_sys->block.i_offset], i_copy );
p_data += i_copy;
}
i_data += i_copy;
if( i_count < 0 ) return -1;
else if( i_count == 0 )
p_sys->block.i_offset += i_copy;
if( p_sys->block.i_offset >= p_sys->block.p_current->i_buffer )
{
/* Current block is now empty, switch to next */
if( p_sys->block.p_current )
{
p_sys->block.i_offset = 0;
p_sys->block.p_current = p_sys->block.p_current->p_next;
}
/*Get a new block */
if( AStreamRefillBlock( s ) )
{
/* We reached the EOF */
break;
}
}
i_count = __MIN( i_data, i_count );
memcpy( p, p_input->p_current_data, i_count );
p_input->p_current_data += i_count;
p += i_count;
i_data -= i_count;
i_read += i_count;
/* Update stream position */
vlc_mutex_lock( &p_input->stream.stream_lock );
p_input->stream.p_selected_area->i_tell += i_count;
vlc_mutex_unlock( &p_input->stream.stream_lock );
}
return i_read;
p_sys->i_pos += i_data;
return i_data;
}
/****************************************************************************
* IStreamPeek:
****************************************************************************/
static int IStreamPeek( stream_t *s, uint8_t **pp_peek, int i_peek )
static int AStreamPeekBlock( stream_t *s, uint8_t **pp_peek, int i_read )
{
input_stream_sys_t * p_sys = (input_stream_sys_t*)s->p_sys;
return input_Peek( p_sys->p_input, pp_peek, i_peek );
stream_sys_t *p_sys = s->p_sys;
uint8_t *p_data;
int i_data = 0;
block_t *b;
int i_offset;
if( p_sys->block.p_current == NULL )
return 0; /* EOF */
/* We can directly give a pointer over our buffer */
if( i_read <= p_sys->block.p_current->i_buffer - p_sys->block.i_offset )
{
*pp_peek = &p_sys->block.p_current->p_buffer[p_sys->block.i_offset];
return i_read;
}
/* We need to create a local copy */
if( p_sys->i_peek < i_read )
{
if( p_sys->p_peek )
free( p_sys->p_peek );
p_sys->i_peek = i_read;
p_sys->p_peek = malloc( p_sys->i_peek );
}
/* Fill enough data */
while( p_sys->block.i_size - (p_sys->i_pos - p_sys->block.i_start) < i_read )
{
block_t **pp_last = p_sys->block.pp_last;
if( AStreamRefillBlock( s ) )
break;
/* Our buffer are probably filled enough, don't try anymore */
if( pp_last == p_sys->block.pp_last )
break;
}
/* Copy what we have */
b = p_sys->block.p_current;
i_offset = p_sys->block.i_offset;
p_data = p_sys->p_peek;
while( b && i_data < i_read )
{
int i_current = b->i_buffer - i_offset;
int i_copy = __MIN( i_current, i_read - i_data );
memcpy( p_data, &b->p_buffer[i_offset], i_copy );
i_data += i_copy;
p_data += i_copy;
i_offset += i_copy;
if( i_offset >= b->i_buffer )
{
i_offset = 0;
b = b->p_next;
}
}
*pp_peek = p_sys->p_peek;
return i_data;
}
/****************************************************************************
* stream_Demux*: create a demuxer for an outpout stream (allow demuxer chain)
****************************************************************************/
typedef struct
static int AStreamSeekBlock( stream_t *s, int64_t i_pos )
{
/* Data buffer */
vlc_mutex_t lock;
int i_buffer;
int i_buffer_size;
uint8_t *p_buffer;
stream_sys_t *p_sys = s->p_sys;
access_t *p_access = p_sys->p_access;
int64_t i_offset = i_pos - p_sys->block.i_start;
vlc_bool_t b_seek;
int64_t i_pos;
/* We already have thoses data, just update p_current/i_offset */
if( i_offset >= 0 && i_offset < p_sys->block.i_size )
{
block_t *b = p_sys->block.p_first;
int i_current = 0;
/* Demuxer */
char *psz_name;
es_out_t *out;
demux_t *p_demux;
} d_stream_sys_t;
while( i_current + b->i_buffer < i_offset )
{
i_current += b->i_buffer;
b = b->p_next;
}
static int DStreamRead ( stream_t *, void *p_read, int i_read );
static int DStreamPeek ( stream_t *, uint8_t **pp_peek, int i_peek );
static int DStreamControl( stream_t *, int i_query, va_list );
static int DStreamThread ( stream_t * );
p_sys->block.p_current = b;
p_sys->block.i_offset = i_offset - i_current;
p_sys->i_pos = i_pos;
stream_t *__stream_DemuxNew( vlc_object_t *p_obj, char *psz_demux, es_out_t *out )
{
/* We create a stream reader, and launch a thread */
stream_t *s;
d_stream_sys_t *p_sys;
return VLC_SUCCESS;
}
if( psz_demux == NULL || *psz_demux == '\0' )
/* We may need to seek or to read data */
if( i_offset < 0 )
{
return NULL;
vlc_bool_t b_aseek;
access2_Control( p_access, ACCESS_CAN_SEEK, &b_aseek );
if( !b_aseek )
{
msg_Err( s, "backward seek impossible (access non seekable)" );
return VLC_EGENERIC;
}
b_seek = VLC_TRUE;
}
else
{
vlc_bool_t b_aseek, b_aseekfast;
s = vlc_object_create( p_obj, sizeof( stream_t ) );
s->pf_block = NULL;
s->pf_read = DStreamRead;
s->pf_peek = DStreamPeek;
s->pf_control= DStreamControl;
s->p_sys = malloc( sizeof( d_stream_sys_t) );
p_sys = (d_stream_sys_t*)s->p_sys;
vlc_mutex_init( s, &p_sys->lock );
p_sys->i_buffer = 0;
p_sys->i_buffer_size = 1000000;
p_sys->p_buffer = malloc( p_sys->i_buffer_size );
p_sys->i_pos = 0;
p_sys->psz_name = strdup( psz_demux );
p_sys->out = out;
p_sys->p_demux = NULL;
if( vlc_thread_create( s, "stream out", DStreamThread, VLC_THREAD_PRIORITY_INPUT, VLC_FALSE ) )
{
vlc_mutex_destroy( &p_sys->lock );
vlc_object_destroy( s );
free( p_sys );
return NULL;
access2_Control( p_access, ACCESS_CAN_SEEK, &b_aseek );
access2_Control( p_access, ACCESS_CAN_FASTSEEK, &b_aseekfast );
if( !b_aseek )
{
b_seek = VLC_FALSE;
msg_Warn( s, "%lld bytes need to be skipped (access non seekable)",
i_offset - p_sys->block.i_size );
}
else
{
int64_t i_skip = i_offset - p_sys->block.i_size;
/* Avg bytes per packets */
int i_avg = p_sys->stat.i_bytes / p_sys->stat.i_read_count;
/* TODO compute a seek cost instead of fixed threshold */
int i_th = b_aseekfast ? 1 : 5;
if( i_skip <= i_th * i_avg &&
i_skip < STREAM_CACHE_SIZE )
b_seek = VLC_FALSE;
else
b_seek = VLC_TRUE;
msg_Dbg( s, "b_seek=%d th*avg=%d skip=%lld",
b_seek, i_th*i_avg, i_skip );
}
}
return s;
}
if( b_seek )
{
int64_t i_start, i_end;
/* Do the access seek */
i_start = mdate();
if( p_access->pf_seek( p_access, i_pos ) )
return VLC_EGENERIC;
i_end = mdate();
void stream_DemuxSend( stream_t *s, block_t *p_block )
{
d_stream_sys_t *p_sys = (d_stream_sys_t*)s->p_sys;
/* Release data */
block_ChainRelease( p_sys->block.p_first );
/* Reinit */
p_sys->block.i_start = p_sys->i_pos = i_pos;
p_sys->block.i_offset = 0;
p_sys->block.p_current = NULL;
p_sys->block.i_size = 0;
p_sys->block.p_first = NULL;
p_sys->block.pp_last = &p_sys->block.p_first;
if( p_block->i_buffer > 0 )
/* Refill a block */
if( AStreamRefillBlock( s ) )
{
msg_Err( s, "cannot re fill buffer" );
return VLC_EGENERIC;
}
/* Update stat */
p_sys->stat.i_seek_time += i_end - i_start;
p_sys->stat.i_seek_count++;
return VLC_SUCCESS;
}
else
{
vlc_mutex_lock( &p_sys->lock );
/* Realloc if needed */
if( p_sys->i_buffer + p_block->i_buffer > p_sys->i_buffer_size )
/* Read enought data */
while( p_sys->block.i_start + p_sys->block.i_size < i_pos )
{
if( p_sys->i_buffer_size > 5000000 )
if( AStreamRefillBlock( s ) )
{
msg_Err( s, "can't read enough data in seek" );
return VLC_EGENERIC;
}
while( p_sys->block.p_current &&
p_sys->i_pos + p_sys->block.p_current->i_buffer < i_pos )
{
vlc_mutex_unlock( &p_sys->lock );
msg_Err( s, "stream_DemuxSend: buffer size > 5000000" );
block_Release( p_block );
return;
p_sys->i_pos += p_sys->block.p_current->i_buffer;
p_sys->block.p_current = p_sys->block.p_current->p_next;
}
/* I know, it's more than needed but that's perfect */
p_sys->i_buffer_size += p_block->i_buffer;
/* FIXME won't work with PEEK -> segfault */
p_sys->p_buffer = realloc( p_sys->p_buffer, p_sys->i_buffer_size );
msg_Dbg( s, "stream_DemuxSend: realloc to %d", p_sys->i_buffer_size );
}
/* copy data */
memcpy( &p_sys->p_buffer[p_sys->i_buffer], p_block->p_buffer, p_block->i_buffer );
p_sys->i_buffer += p_block->i_buffer;
p_sys->block.i_offset = i_pos - p_sys->i_pos;
p_sys->i_pos = i_pos;
vlc_mutex_unlock( &p_sys->lock );
/* TODO read data */
return VLC_SUCCESS;
}
block_Release( p_block );
return VLC_EGENERIC;
}
void stream_DemuxDelete( stream_t *s )
static int AStreamRefillBlock( stream_t *s )
{
d_stream_sys_t *p_sys = (d_stream_sys_t*)s->p_sys;
stream_sys_t *p_sys = s->p_sys;
access_t *p_access = p_sys->p_access;
int64_t i_start, i_stop;
block_t *b;
/* Release data */
while( p_sys->block.i_size >= STREAM_CACHE_SIZE &&
p_sys->block.p_first != p_sys->block.p_current )
{
block_t *b = p_sys->block.p_first;
s->b_die = VLC_TRUE;
p_sys->block.i_start += b->i_buffer;
p_sys->block.i_size -= b->i_buffer;
p_sys->block.p_first = b->p_next;
vlc_mutex_lock( &p_sys->lock );
if( p_sys->p_demux )
block_Release( b );
}
if( p_sys->block.i_size >= STREAM_CACHE_SIZE &&
p_sys->block.p_current == p_sys->block.p_first &&
p_sys->block.p_current->p_next ) /* At least 2 packets */
{
p_sys->p_demux->b_die = VLC_TRUE;
/* Enough data, don't read more */
return VLC_SUCCESS;
}
vlc_mutex_unlock( &p_sys->lock );
vlc_thread_join( s );
if( p_sys->p_demux )
/* Now read a new block */
i_start = mdate();
for( ;; )
{
demux2_Delete( p_sys->p_demux );
if( s->b_die )
return VLC_EGENERIC;
/* Fetch a block */
if( ( b = p_access->pf_block( p_access ) ) )
break;
if( p_access->info.b_eof )
return VLC_EGENERIC;
msleep( STREAM_DATA_WAIT );
}
vlc_mutex_destroy( &p_sys->lock );
free( p_sys->psz_name );
free( p_sys->p_buffer );
free( p_sys );
vlc_object_destroy( s );
i_stop = mdate();
/* Append the block */
p_sys->block.i_size += b->i_buffer;
*p_sys->block.pp_last = b;
p_sys->block.pp_last = &b->p_next;
/* Fix p_current */
if( p_sys->block.p_current == NULL )
p_sys->block.p_current = b;
/* Update stat */
p_sys->stat.i_bytes += b->i_buffer;
p_sys->stat.i_read_time += i_stop - i_start;
p_sys->stat.i_read_count++;
return VLC_SUCCESS;
}
static int DStreamRead ( stream_t *s, void *p_read, int i_read )
/****************************************************************************
* Method 2:
****************************************************************************/
static int AStreamRefillStream( stream_t *s );
static int AStreamReadStream( stream_t *s, void *p_read, int i_read )
{
d_stream_sys_t *p_sys = (d_stream_sys_t*)s->p_sys;
int i_copy;
stream_sys_t *p_sys = s->p_sys;
stream_track_t *tk = &p_sys->stream.tk[p_sys->stream.i_tk];
//msg_Dbg( s, "DStreamRead: wanted %d bytes", i_read );
for( ;; )
{
vlc_mutex_lock( &p_sys->lock );
//msg_Dbg( s, "DStreamRead: buffer %d", p_sys->i_buffer );
if( p_sys->i_buffer >= i_read || s->b_die )
{
break;
}
vlc_mutex_unlock( &p_sys->lock );
msleep( 10000 );
}
uint8_t *p_data = (uint8_t*)p_read;
int i_data = 0;
//msg_Dbg( s, "DStreamRead: read %d buffer %d", i_read, p_sys->i_buffer );
if( tk->i_start >= tk->i_end )
return 0; /* EOF */
i_copy = __MIN( i_read, p_sys->i_buffer );
if( i_copy > 0 )
/*msg_Dbg( s, "AStreamReadStream: %d pos=%lld tk=%d start=%lld offset=%d end=%lld",
i_read,
p_sys->i_pos,
p_sys->stream.i_tk,
tk->i_start, p_sys->stream.i_offset, tk->i_end );*/
while( i_data < i_read )
{
if( p_read )
int i_off = (tk->i_start + p_sys->stream.i_offset) % STREAM_CACHE_TRACK_SIZE;
int i_current = __MIN( tk->i_end - tk->i_start - p_sys->stream.i_offset, STREAM_CACHE_TRACK_SIZE - i_off );
int i_copy = __MIN( i_current, i_read - i_data );
/* Copy data */
//msg_Dbg( s, "AStreamReadStream: copy %d", i_copy );
if( p_data )
{
memcpy( p_read, p_sys->p_buffer, i_copy );
memcpy( p_data, &tk->p_buffer[i_off], i_copy );
p_data += i_copy;
}
p_sys->i_buffer -= i_copy;
i_data += i_copy;
p_sys->stream.i_offset += i_copy;
/* Update pos now */
p_sys->i_pos += i_copy;
if( p_sys->i_buffer > 0 )
/* */
p_sys->stream.i_used += i_copy;
if( tk->i_start + p_sys->stream.i_offset >= tk->i_end ||
p_sys->stream.i_used >= p_sys->stream.i_read_size )
{
memmove( p_sys->p_buffer, &p_sys->p_buffer[i_copy], p_sys->i_buffer );
if( AStreamRefillStream( s ) )
{
/* Eof */
if( tk->i_start >= tk->i_end )
break;
}
}
}
vlc_mutex_unlock( &p_sys->lock );
return i_copy;
return i_data;
}
static int DStreamPeek ( stream_t *s, uint8_t **pp_peek, int i_peek )
static int AStreamPeekStream( stream_t *s, uint8_t **pp_peek, int i_read )
{
d_stream_sys_t *p_sys = (d_stream_sys_t*)s->p_sys;
int i_copy;
stream_sys_t *p_sys = s->p_sys;
stream_track_t *tk = &p_sys->stream.tk[p_sys->stream.i_tk];
int64_t i_off;
//msg_Dbg( s, "DStreamPeek: wanted %d bytes", i_peek );
for( ;; )
if( tk->i_start >= tk->i_end )
return 0; /* EOF */
/*msg_Dbg( s, "AStreamPeekStream: %d pos=%lld tk=%d start=%lld offset=%d end=%lld",
i_read,
p_sys->i_pos,
p_sys->stream.i_tk,
tk->i_start, p_sys->stream.i_offset, tk->i_end );*/
/* Avoid problem, but that should *never* happen */
if( i_read > STREAM_CACHE_TRACK_SIZE / 2 )
i_read = STREAM_CACHE_TRACK_SIZE / 2;
while( tk->i_end - tk->i_start - p_sys->stream.i_offset < i_read )
{
vlc_mutex_lock( &p_sys->lock );
//msg_Dbg( s, "DStreamPeek: buffer %d", p_sys->i_buffer );
if( p_sys->i_buffer >= i_peek || s->b_die )
{
if( AStreamRefillStream( s ) )
break;
}
vlc_mutex_unlock( &p_sys->lock );
msleep( 10000 );
}
*pp_peek = p_sys->p_buffer;
i_copy = __MIN( i_peek, p_sys->i_buffer );
vlc_mutex_unlock( &p_sys->lock );
if( tk->i_end - tk->i_start - p_sys->stream.i_offset < i_read )
i_read = tk->i_end - tk->i_start - p_sys->stream.i_offset;
return i_copy;
/* Now, direct pointer or a copy ? */
i_off = (tk->i_start + p_sys->stream.i_offset) % STREAM_CACHE_TRACK_SIZE;
if( i_off + i_read <= STREAM_CACHE_TRACK_SIZE )
{
*pp_peek = &tk->p_buffer[i_off];
return i_read;
}
if( p_sys->i_peek < i_read )
{
if( p_sys->p_peek ) free( p_sys->p_peek );
p_sys->i_peek = i_read;
p_sys->p_peek = malloc( i_read );
}
memcpy( p_sys->p_peek, &tk->p_buffer[i_off], STREAM_CACHE_TRACK_SIZE - i_off );
memcpy( &p_sys->p_peek[STREAM_CACHE_TRACK_SIZE - i_off], &tk->p_buffer[0], i_read - (STREAM_CACHE_TRACK_SIZE - i_off) );
*pp_peek = p_sys->p_peek;
return i_read;
}
static int DStreamControl( stream_t *s, int i_query, va_list args )
static int AStreamSeekStream( stream_t *s, int64_t i_pos )
{
d_stream_sys_t *p_sys = (d_stream_sys_t*)s->p_sys;
int64_t *p_i64;
vlc_bool_t *p_b;
int *p_int;
switch( i_query )
stream_sys_t *p_sys = s->p_sys;
access_t *p_access = p_sys->p_access;
vlc_bool_t b_aseek;
vlc_bool_t b_afastseek;
int i_maxth;
int i_new;
int i;
/*
msg_Dbg( s, "AStreamSeekStream: to %lld pos=%lld tk=%d start=%lld offset=%d end=%lld",
i_pos,
p_sys->i_pos,
p_sys->stream.i_tk,
p_sys->stream.tk[p_sys->stream.i_tk].i_start, p_sys->stream.i_offset, p_sys->stream.tk[p_sys->stream.i_tk].i_end );*/
/* Seek in our current track ? */
if( i_pos >= p_sys->stream.tk[p_sys->stream.i_tk].i_start &&
i_pos < p_sys->stream.tk[p_sys->stream.i_tk].i_end )
{
case STREAM_GET_SIZE:
p_i64 = (int64_t*) va_arg( args, int64_t * );
*p_i64 = 0;
return VLC_SUCCESS;
//msg_Dbg( s, "AStreamSeekStream: current track" );
p_sys->i_pos = i_pos;
p_sys->stream.i_offset = i_pos - p_sys->stream.tk[p_sys->stream.i_tk].i_start;
return VLC_SUCCESS;
}
case STREAM_CAN_SEEK:
p_b = (vlc_bool_t*) va_arg( args, vlc_bool_t * );
*p_b = VLC_FALSE;
return VLC_SUCCESS;
access2_Control( p_access, ACCESS_CAN_SEEK, &b_aseek );
if( !b_aseek )
{
/* We can't do nothing */
return VLC_EGENERIC;
}
case STREAM_CAN_FASTSEEK:
p_b = (vlc_bool_t*) va_arg( args, vlc_bool_t * );
*p_b = VLC_FALSE;
return VLC_SUCCESS;
/* Date the current track */
p_sys->stream.tk[p_sys->stream.i_tk].i_date = mdate();
case STREAM_GET_POSITION:
p_i64 = (int64_t*) va_arg( args, int64_t * );
*p_i64 = p_sys->i_pos;
return VLC_SUCCESS;
/* Try to reuse already read data */
for( i = 0; i < STREAM_CACHE_TRACK; i++ )
{
stream_track_t *tk = &p_sys->stream.tk[i];
case STREAM_SET_POSITION:
return VLC_EGENERIC;
if( i_pos >= tk->i_start && i_pos <= tk->i_end )
{
/*msg_Dbg( s, "AStreamSeekStream: reusing %d start=%lld end=%lld",
i,
tk->i_start, tk->i_end );*/
/* Seek at the end of the buffer */
if( p_access->pf_seek( p_access, tk->i_end ) )
return VLC_EGENERIC;
/* That's it */
p_sys->i_pos = i_pos;
p_sys->stream.i_tk = i;
p_sys->stream.i_offset = i_pos - tk->i_start;
if( p_sys->stream.i_used < 1024 )
p_sys->stream.i_used = 1024;
if( AStreamRefillStream( s ) )
return VLC_EGENERIC;
case STREAM_GET_MTU:
p_int = (int*) va_arg( args, int * );
*p_int = 0;
return VLC_SUCCESS;
}
}
default:
msg_Err( s, "invalid DStreamControl query=0x%x", i_query );
return VLC_EGENERIC;
access2_Control( p_access, ACCESS_CAN_SEEK, &b_afastseek );
/* FIXME compute seek cost (instead of static 'stupid' value) */
i_maxth = __MIN( p_sys->stream.i_read_size, STREAM_READ_ATONCE / 2 );
if( !b_afastseek )
i_maxth *= 3;
/* FIXME TODO */
#if 0
/* Search closest segment TODO */
for( i = 0; i < STREAM_CACHE_TRACK; i++ )
{
stream_track_t *tk = &p_sys->stream.tk[i];
if( i_pos + i_maxth >= tk->i_start )
{
msg_Dbg( s, "good segment before current pos, TODO" );
}
if( i_pos - i_maxth <= tk->i_end )
{
msg_Dbg( s, "good segment after current pos, TODO" );
}
}
#endif
/* Nothing good, seek and choose oldest segment */
if( p_access->pf_seek( p_access, i_pos ) )
return VLC_EGENERIC;
p_sys->i_pos = i_pos;
i_new = 0;
for( i = 1; i < STREAM_CACHE_TRACK; i++ )
{
if( p_sys->stream.tk[i].i_date < p_sys->stream.tk[i_new].i_date )
i_new = i;
}
/* Reset the segment */
p_sys->stream.i_tk = i_new;
p_sys->stream.i_offset = 0;
p_sys->stream.tk[i_new].i_start = i_pos;
p_sys->stream.tk[i_new].i_end = i_pos;
/* Read data */
if( p_sys->stream.i_used < STREAM_READ_ATONCE / 2 )
p_sys->stream.i_used = STREAM_READ_ATONCE / 2;
if( AStreamRefillStream( s ) )
return VLC_EGENERIC;
return VLC_SUCCESS;
}
static int DStreamThread ( stream_t *s )
static int AStreamRefillStream( stream_t *s )
{
d_stream_sys_t *p_sys = (d_stream_sys_t*)s->p_sys;
demux_t *p_demux;
stream_sys_t *p_sys = s->p_sys;
access_t *p_access = p_sys->p_access;
stream_track_t *tk = &p_sys->stream.tk[p_sys->stream.i_tk];
/* We read but won't increase i_start after initial start+offset */
int i_toread = __MIN( p_sys->stream.i_used, STREAM_CACHE_TRACK_SIZE - (tk->i_end - tk->i_start - p_sys->stream.i_offset ) );
int64_t i_start, i_stop;
/* Create the demuxer */
//msg_Dbg( s, "AStreamRefillStream: toread=%d", i_toread );
if( ( p_demux = demux2_New( s, "", p_sys->psz_name, "", s, p_sys->out ) ) == NULL )
i_start = mdate();
while( i_toread > 0 )
{
return VLC_EGENERIC;
int i_off = tk->i_end % STREAM_CACHE_TRACK_SIZE;
int i_read;
if( s->b_die )
return VLC_EGENERIC;
i_read = __MIN( i_toread, STREAM_CACHE_TRACK_SIZE - i_off );
i_read = p_access->pf_read( p_access, &tk->p_buffer[i_off], i_read );
//msg_Dbg( s, "AStreamRefillStream: read=%d", i_read );
if( i_read < 0 )
{
msleep( STREAM_DATA_WAIT );
continue;
}
else if( i_read == 0 )
{
return VLC_EGENERIC;
}
/* Update end */
tk->i_end += i_read;
/* Windows of STREAM_CACHE_TRACK_SIZE */
if( tk->i_end - tk->i_start > STREAM_CACHE_TRACK_SIZE )
{
int i_invalid = tk->i_end - tk->i_start - STREAM_CACHE_TRACK_SIZE;
tk->i_start += i_invalid;
p_sys->stream.i_offset -= i_invalid;
}
i_toread -= i_read;
p_sys->stream.i_used -= i_read;
p_sys->stat.i_bytes += i_read;
p_sys->stat.i_read_count++;
}
i_stop = mdate();
p_sys->stat.i_read_time += i_stop - i_start;
return VLC_SUCCESS;
}
static void AStreamPrebufferStream( stream_t *s )
{
stream_sys_t *p_sys = s->p_sys;
access_t *p_access = p_sys->p_access;
vlc_mutex_lock( &p_sys->lock );
p_sys->p_demux = p_demux;
vlc_mutex_unlock( &p_sys->lock );
int64_t i_first = 0;
int64_t i_start;
/* Main loop */
while( !s->b_die && !p_demux->b_die )
msg_Dbg( s, "pre buffering" );
i_start = mdate();
for( ;; )
{
if( p_demux->pf_demux( p_demux ) <= 0 )
stream_track_t *tk = &p_sys->stream.tk[p_sys->stream.i_tk];
int64_t i_date = mdate();
int i_read;
if( s->b_die ||
tk->i_end >= STREAM_CACHE_PREBUFFER_SIZE ||
( i_first > 0 && i_first + STREAM_CACHE_PREBUFFER_LENGTH < i_date ) )
{
int64_t i_byterate;
/* Update stat */
p_sys->stat.i_bytes = tk->i_end - tk->i_start;
p_sys->stat.i_read_time = i_date - i_start;
i_byterate = ( I64C(1000000) * p_sys->stat.i_bytes ) /
(p_sys->stat.i_read_time+1);
msg_Dbg( s, "prebuffering done %lld bytes in %llds - %lld kbytes/s",
p_sys->stat.i_bytes,
p_sys->stat.i_read_time / I64C(1000000),
i_byterate / 1024 );
break;
}
/* */
i_read = __MIN( p_sys->stream.i_read_size, STREAM_CACHE_TRACK_SIZE - tk->i_end );
i_read = p_access->pf_read( p_access, &tk->p_buffer[tk->i_end], i_read );
if( i_read < 0 )
{
msleep( STREAM_DATA_WAIT );
continue;
}
else if( i_read == 0 )
{
/* EOF */
break;
}
if( i_first == 0 )
i_first = mdate();
tk->i_end += i_read;
p_sys->stat.i_read_count++;
}
p_demux->b_die = VLC_TRUE;
return VLC_SUCCESS;
}
/****************************************************************************
* stream_ReadLine:
****************************************************************************/
/**
* Read from the stream untill first newline.
* \param s Stream handle to read from
* \return A null-terminated string. This must be freed,
*/
/* FIXME don't use stupid MAX_LINE -> do the same than net_ReadLine */
#define MAX_LINE 1024
char *stream_ReadLine( stream_t *s )
{
uint8_t *p_data;
char *p_line;
int i_data;
int i = 0;
i_data = stream_Peek( s, &p_data, MAX_LINE );
while( i < i_data && p_data[i] != '\n' && p_data[i] != '\r' )
{
i++;
}
if( i_data <= 0 )
{
return NULL;
}
else
{
p_line = malloc( i + 1 );
if( p_line == NULL )
{
msg_Err( s, "out of memory" );
return NULL;
}
i = stream_Read( s, p_line, i + 1 );
p_line[ i - 1 ] = '\0';
return p_line;
}
}
......@@ -31,8 +31,6 @@
#include <vlc/vlc.h>
#include <vlc/input.h>
#include "ninput.h"
#ifdef HAVE_DIRENT_H
# include <dirent.h>
#else
......
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