Commit aa407ea9 authored by Laurent Aimar's avatar Laurent Aimar

Added record support at the stream_t level in core.

The record access_filter will become useless as soon as all demuxers that can
support it will be modified.
The record support is half done. I will also add es_out_t record functionnalities,
and a way to selected one of them if both are supported.
parent b08b5692
...@@ -124,8 +124,14 @@ enum demux_query_e ...@@ -124,8 +124,14 @@ enum demux_query_e
/* Attachments */ /* Attachments */
DEMUX_GET_ATTACHMENTS, /* arg1=input_attachment_t***, int* res=can fail */ DEMUX_GET_ATTACHMENTS, /* arg1=input_attachment_t***, int* res=can fail */
/* RECORD you should accept it only if the stream can be recorded without
* any modification or header addition. */
DEMUX_CAN_RECORD, /* arg1=bool* res=can fail(assume false) */
DEMUX_SET_RECORD_STATE, /* arg1=bool res=can fail */
/* II. Specific access_demux queries */ /* II. Specific access_demux queries */
DEMUX_CAN_PAUSE, /* arg1= bool* can fail (assume false)*/ DEMUX_CAN_PAUSE = 0x1000, /* arg1= bool* can fail (assume false)*/
DEMUX_SET_PAUSE_STATE, /* arg1= bool can fail */ DEMUX_SET_PAUSE_STATE, /* arg1= bool can fail */
DEMUX_GET_PTS_DELAY, /* arg1= int64_t* cannot fail */ DEMUX_GET_PTS_DELAY, /* arg1= int64_t* cannot fail */
......
...@@ -524,7 +524,11 @@ enum input_query_e ...@@ -524,7 +524,11 @@ enum input_query_e
INPUT_GET_ATTACHMENT, /* arg1=input_attachment_t**, arg2=char* res=can fail */ INPUT_GET_ATTACHMENT, /* arg1=input_attachment_t**, arg2=char* res=can fail */
/* On the fly input slave */ /* On the fly input slave */
INPUT_ADD_SLAVE /* arg1= char * */ INPUT_ADD_SLAVE, /* arg1= char * */
/* On the fly record while playing */
INPUT_SET_RECORD_STATE, /* arg1=bool res=can fail */
INPUT_GET_RECORD_STATE, /* arg1=bool* res=can fail */
}; };
VLC_EXPORT( int, input_vaControl,( input_thread_t *, int i_query, va_list ) ); VLC_EXPORT( int, input_vaControl,( input_thread_t *, int i_query, va_list ) );
......
...@@ -64,7 +64,11 @@ enum stream_query_e ...@@ -64,7 +64,11 @@ enum stream_query_e
STREAM_CONTROL_ACCESS, /* arg1= int i_access_query, args res: can fail STREAM_CONTROL_ACCESS, /* arg1= int i_access_query, args res: can fail
if access unreachable or access control answer */ if access unreachable or access control answer */
STREAM_GET_CONTENT_TYPE, /**< arg1= char ** res=can file */ STREAM_GET_CONTENT_TYPE, /**< arg1= char ** res=can fail */
/* SET_RECORD:
* XXX only data read through stream_Read/Block will be recorded */
STREAM_SET_RECORD_STATE, /**< arg1=bool, arg2=const char *psz_ext (if arg1 is true) res=can fail */
}; };
VLC_EXPORT( int, stream_Read, ( stream_t *s, void *p_read, int i_read ) ); VLC_EXPORT( int, stream_Read, ( stream_t *s, void *p_read, int i_read ) );
......
...@@ -810,6 +810,19 @@ static void Run( intf_thread_t *p_intf ) ...@@ -810,6 +810,19 @@ static void Run( intf_thread_t *p_intf )
{ {
osd_MenuActivate( VLC_OBJECT(p_intf) ); osd_MenuActivate( VLC_OBJECT(p_intf) );
} }
else if( i_action == ACTIONID_RECORD )
{
if( var_GetBool( p_input, "can-record" ) )
{
const bool b_record = !var_GetBool( p_input, "record" );
if( b_record )
vout_OSDMessage( p_intf, DEFAULT_CHAN, _("Recording") );
else
vout_OSDMessage( p_intf, DEFAULT_CHAN, _("Recording done") );
var_SetBool( p_input, "record", b_record );
}
}
} }
if( p_vout ) if( p_vout )
vlc_object_release( p_vout ); vlc_object_release( p_vout );
......
...@@ -370,6 +370,9 @@ struct demux_sys_t ...@@ -370,6 +370,9 @@ struct demux_sys_t
/* */ /* */
bool b_meta; bool b_meta;
/* */
bool b_start_record;
}; };
static int Demux ( demux_t *p_demux ); static int Demux ( demux_t *p_demux );
...@@ -642,6 +645,7 @@ static int Open( vlc_object_t *p_this ) ...@@ -642,6 +645,7 @@ static int Open( vlc_object_t *p_this )
p_sys->b_udp_out = false; p_sys->b_udp_out = false;
p_sys->i_ts_read = 50; p_sys->i_ts_read = 50;
p_sys->csa = NULL; p_sys->csa = NULL;
p_sys->b_start_record = false;
/* Init PAT handler */ /* Init PAT handler */
pat = &p_sys->pid[0]; pat = &p_sys->pid[0];
...@@ -1091,6 +1095,13 @@ static int Demux( demux_t *p_demux ) ...@@ -1091,6 +1095,13 @@ static int Demux( demux_t *p_demux )
} }
} }
if( p_sys->b_start_record )
{
/* Enable recording once synchronized */
stream_Control( p_demux->s, STREAM_SET_RECORD_STATE, true, "ts" );
p_sys->b_start_record = false;
}
if( p_sys->b_udp_out ) if( p_sys->b_udp_out )
{ {
memcpy( &p_sys->buffer[i_pkt * p_sys->i_packet_size], memcpy( &p_sys->buffer[i_pkt * p_sys->i_packet_size],
...@@ -1190,6 +1201,7 @@ static int Control( demux_t *p_demux, int i_query, va_list args ) ...@@ -1190,6 +1201,7 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
{ {
demux_sys_t *p_sys = p_demux->p_sys; demux_sys_t *p_sys = p_demux->p_sys;
double f, *pf; double f, *pf;
bool b_bool, *pb_bool;
int64_t i64; int64_t i64;
int64_t *pi64; int64_t *pi64;
int i_int; int i_int;
...@@ -1386,6 +1398,19 @@ static int Control( demux_t *p_demux, int i_query, va_list args ) ...@@ -1386,6 +1398,19 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
return VLC_SUCCESS; return VLC_SUCCESS;
} }
case DEMUX_CAN_RECORD:
pb_bool = (bool*)va_arg( args, bool * );
*pb_bool = true;
return VLC_SUCCESS;
case DEMUX_SET_RECORD_STATE:
b_bool = (bool)va_arg( args, int );
if( !b_bool )
stream_Control( p_demux->s, STREAM_SET_RECORD_STATE, false );
p_sys->b_start_record = b_bool;
return VLC_SUCCESS;
case DEMUX_GET_FPS: case DEMUX_GET_FPS:
case DEMUX_SET_TIME: case DEMUX_SET_TIME:
default: default:
......
...@@ -372,13 +372,12 @@ void AdvControlsWidget::enableInput( bool enable ) ...@@ -372,13 +372,12 @@ void AdvControlsWidget::enableInput( bool enable )
input_item_t *p_item = input_GetItem( THEMIM->getInput() ); input_item_t *p_item = input_GetItem( THEMIM->getInput() );
i_input_id = p_item->i_id; i_input_id = p_item->i_id;
if( var_Type( THEMIM->getInput(), "record-toggle" ) == VLC_VAR_VOID ) recordButton->setVisible( var_GetBool( THEMIM->getInput(), "can-record" ) );
recordButton->setVisible( true );
else
recordButton->setVisible( false );
} }
else else
{
recordButton->setVisible( false ); recordButton->setVisible( false );
}
ABButton->setEnabled( enable ); ABButton->setEnabled( enable );
recordButton->setEnabled( enable ); recordButton->setEnabled( enable );
...@@ -464,8 +463,8 @@ void AdvControlsWidget::record() ...@@ -464,8 +463,8 @@ void AdvControlsWidget::record()
if( p_input ) if( p_input )
{ {
/* This method won't work fine if the stream can't be cut anywhere */ /* This method won't work fine if the stream can't be cut anywhere */
if( var_Type( p_input, "record-toggle" ) == VLC_VAR_VOID ) const bool b_recording = var_GetBool( p_input, "record" );
var_TriggerCallback( p_input, "record-toggle" ); var_SetBool( p_input, "record", !b_recording );
#if 0 #if 0
else else
{ {
......
...@@ -63,6 +63,7 @@ int input_vaControl( input_thread_t *p_input, int i_query, va_list args ) ...@@ -63,6 +63,7 @@ int input_vaControl( input_thread_t *p_input, int i_query, va_list args )
int *pi_bkmk; int *pi_bkmk;
int i_int, *pi_int; int i_int, *pi_int;
bool b_bool, *pb_bool;
double f, *pf; double f, *pf;
int64_t i_64, *pi_64; int64_t i_64, *pi_64;
...@@ -598,6 +599,15 @@ int input_vaControl( input_thread_t *p_input, int i_query, va_list args ) ...@@ -598,6 +599,15 @@ int input_vaControl( input_thread_t *p_input, int i_query, va_list args )
return VLC_EGENERIC; return VLC_EGENERIC;
} }
case INPUT_SET_RECORD_STATE:
b_bool = (bool)va_arg( args, int );
var_SetBool( p_input, "record", b_bool );
return VLC_SUCCESS;
case INPUT_GET_RECORD_STATE:
pb_bool = (bool*)va_arg( args, bool* );
*pb_bool = var_GetBool( p_input, "record" );
return VLC_SUCCESS;
default: default:
msg_Err( p_input, "unknown query in input_vaControl" ); msg_Err( p_input, "unknown query in input_vaControl" );
......
...@@ -734,33 +734,34 @@ static void VoutFlushPicture( vout_thread_t *p_vout ) ...@@ -734,33 +734,34 @@ static void VoutFlushPicture( vout_thread_t *p_vout )
vlc_mutex_unlock( &p_vout->picture_lock ); vlc_mutex_unlock( &p_vout->picture_lock );
} }
static void DecoderOptimizePtsDelay( decoder_t *p_dec )
static void optimize_video_pts( decoder_t *p_dec )
{ {
picture_t * oldest_pict = NULL; input_thread_t *p_input = p_dec->p_owner->p_input;
picture_t * youngest_pict = NULL; vout_thread_t *p_vout = p_dec->p_owner->p_vout;
int i; input_thread_private_t *p_priv = p_input->p;
input_thread_t * p_input = p_dec->p_owner->p_input; picture_t *p_old = NULL;
vout_thread_t * p_vout = p_dec->p_owner->p_vout; picture_t *p_young = NULL;
input_thread_private_t * p_priv = p_input->p; int i;
/* Enable with --auto-adjust-pts-delay */ /* Enable with --auto-adjust-pts-delay */
if( !p_priv->pts_adjust.auto_adjust ) return; if( !p_priv->pts_adjust.b_auto_adjust )
return;
for( i = 0; i < I_RENDERPICTURES; i++ ) for( i = 0; i < I_RENDERPICTURES; i++ )
{ {
picture_t * pic = PP_RENDERPICTURE[i]; picture_t *p_pic = PP_RENDERPICTURE[i];
if( pic->i_status != READY_PICTURE )
if( p_pic->i_status != READY_PICTURE )
continue; continue;
if( !oldest_pict || pic->date < oldest_pict->date ) if( !p_old || p_pic->date < p_old->date )
oldest_pict = pic; p_old = p_pic;
if( !youngest_pict || pic->date > youngest_pict->date ) if( !p_young || p_pic->date > p_young->date )
youngest_pict = pic; p_young = p_pic;
} }
if( !youngest_pict || !oldest_pict ) if( !p_young || !p_old )
return; return;
/* Try to find if we can reduce the pts /* Try to find if we can reduce the pts
...@@ -775,59 +776,58 @@ static void optimize_video_pts( decoder_t *p_dec ) ...@@ -775,59 +776,58 @@ static void optimize_video_pts( decoder_t *p_dec )
* pts<->dts delay in the muxed stream. That is * pts<->dts delay in the muxed stream. That is
* why we may end up in having a negative pts_delay, * why we may end up in having a negative pts_delay,
* to compensate that artificial delay. */ * to compensate that artificial delay. */
mtime_t buffer_size = youngest_pict->date - oldest_pict->date; const mtime_t i_buffer_length = p_young->date - p_old->date;
int64_t pts_slide = 0; int64_t i_pts_slide = 0;
if( buffer_size < 10000 ) if( i_buffer_length < 10000 )
{ {
if( p_priv->pts_adjust.i_num_faulty > 10 ) if( p_priv->pts_adjust.i_num_faulty > 10 )
{ {
pts_slide = __MAX(p_input->i_pts_delay *3 / 2, 10000); i_pts_slide = __MAX(p_input->i_pts_delay *3 / 2, 10000);
p_priv->pts_adjust.i_num_faulty = 0; p_priv->pts_adjust.i_num_faulty = 0;
} }
if( p_priv->pts_adjust.to_high ) if( p_priv->pts_adjust.b_to_high )
{ {
p_priv->pts_adjust.to_high = !p_priv->pts_adjust.to_high; p_priv->pts_adjust.b_to_high = !p_priv->pts_adjust.b_to_high;
p_priv->pts_adjust.i_num_faulty = 0; p_priv->pts_adjust.i_num_faulty = 0;
} }
p_priv->pts_adjust.i_num_faulty++; p_priv->pts_adjust.i_num_faulty++;
} }
else if( buffer_size > 100000 ) else if( i_buffer_length > 100000 )
{ {
if( p_priv->pts_adjust.i_num_faulty > 25 ) if( p_priv->pts_adjust.i_num_faulty > 25 )
{ {
pts_slide = -buffer_size/2; i_pts_slide = -i_buffer_length/2;
p_priv->pts_adjust.i_num_faulty = 0; p_priv->pts_adjust.i_num_faulty = 0;
} }
if( p_priv->pts_adjust.to_high ) if( p_priv->pts_adjust.b_to_high )
{ {
p_priv->pts_adjust.to_high = !p_priv->pts_adjust.to_high; p_priv->pts_adjust.b_to_high = !p_priv->pts_adjust.b_to_high;
p_priv->pts_adjust.i_num_faulty = 0; p_priv->pts_adjust.i_num_faulty = 0;
} }
p_priv->pts_adjust.i_num_faulty++; p_priv->pts_adjust.i_num_faulty++;
} }
if( pts_slide ) if( i_pts_slide != 0 )
{ {
mtime_t origi_delay = p_input->i_pts_delay; const mtime_t i_pts_delay_org = p_input->i_pts_delay;
p_input->i_pts_delay += pts_slide; p_input->i_pts_delay += i_pts_slide;
/* Don't play with the pts delay for more than -2<->3sec */ /* Don't play with the pts delay for more than -2<->3sec */
if( p_input->i_pts_delay < -2000000 ) if( p_input->i_pts_delay < -2000000 )
p_input->i_pts_delay = -2000000; p_input->i_pts_delay = -2000000;
else if( p_input->i_pts_delay > 3000000 ) else if( p_input->i_pts_delay > 3000000 )
p_input->i_pts_delay = 3000000; p_input->i_pts_delay = 3000000;
pts_slide = p_input->i_pts_delay - origi_delay; i_pts_slide = p_input->i_pts_delay - i_pts_delay_org;
msg_Dbg( p_input, "Sliding the pts by %dms pts delay at %dms picture buffer was %dms", msg_Dbg( p_input, "Sliding the pts by %dms pts delay at %dms picture buffer was %dms",
(int)pts_slide/1000, (int)p_input->i_pts_delay/1000, (int)buffer_size/1000); (int)i_pts_slide/1000, (int)p_input->i_pts_delay/1000, (int)i_buffer_length/1000);
vlc_mutex_lock( &p_vout->picture_lock ); vlc_mutex_lock( &p_vout->picture_lock );
/* Slide all the picture */ /* Slide all the picture */
for( i = 0; i < I_RENDERPICTURES; i++ ) for( i = 0; i < I_RENDERPICTURES; i++ )
PP_RENDERPICTURE[i]->date += pts_slide; PP_RENDERPICTURE[i]->date += i_pts_slide;
/* FIXME: slide aout/spu */ /* FIXME: slide aout/spu */
vlc_mutex_unlock( &p_vout->picture_lock ); vlc_mutex_unlock( &p_vout->picture_lock );
} }
} }
...@@ -872,7 +872,7 @@ static void DecoderDecodeVideo( decoder_t *p_dec, block_t *p_block ) ...@@ -872,7 +872,7 @@ static void DecoderDecodeVideo( decoder_t *p_dec, block_t *p_block )
vout_DatePicture( p_vout, p_pic, p_pic->date ); vout_DatePicture( p_vout, p_pic, p_pic->date );
optimize_video_pts( p_dec ); DecoderOptimizePtsDelay( p_dec );
vout_DisplayPicture( p_vout, p_pic ); vout_DisplayPicture( p_vout, p_pic );
} }
......
...@@ -280,6 +280,8 @@ int demux_vaControlHelper( stream_t *s, ...@@ -280,6 +280,8 @@ int demux_vaControlHelper( stream_t *s,
case DEMUX_GET_TITLE_INFO: case DEMUX_GET_TITLE_INFO:
case DEMUX_SET_GROUP: case DEMUX_SET_GROUP:
case DEMUX_GET_ATTACHMENTS: case DEMUX_GET_ATTACHMENTS:
case DEMUX_CAN_RECORD:
case DEMUX_SET_RECORD_STATE:
return VLC_EGENERIC; return VLC_EGENERIC;
default: default:
...@@ -526,6 +528,7 @@ static int DStreamControl( stream_t *s, int i_query, va_list args ) ...@@ -526,6 +528,7 @@ static int DStreamControl( stream_t *s, int i_query, va_list args )
case STREAM_CONTROL_ACCESS: case STREAM_CONTROL_ACCESS:
case STREAM_GET_CONTENT_TYPE: case STREAM_GET_CONTENT_TYPE:
case STREAM_SET_RECORD_STATE:
return VLC_EGENERIC; return VLC_EGENERIC;
default: default:
......
...@@ -112,6 +112,7 @@ static void AppendAttachment( int *pi_attachment, input_attachment_t ***ppp_atta ...@@ -112,6 +112,7 @@ static void AppendAttachment( int *pi_attachment, input_attachment_t ***ppp_atta
* - seekable (if you can seek, it doesn't say if 'bar display' has be shown * - seekable (if you can seek, it doesn't say if 'bar display' has be shown
* or not, for that check position != 0.0) * or not, for that check position != 0.0)
* - can-pause * - can-pause
* - can-record (if a stream can be recorded while playing)
* - teletext-es to get the index of spu track that is teletext --1 if no teletext) * - teletext-es to get the index of spu track that is teletext --1 if no teletext)
* * For intf callback upon changes * * For intf callback upon changes
* - intf-change * - intf-change
...@@ -184,6 +185,7 @@ static input_thread_t *Create( vlc_object_t *p_parent, input_item_t *p_item, ...@@ -184,6 +185,7 @@ static input_thread_t *Create( vlc_object_t *p_parent, input_item_t *p_item,
p_input->p->i_title_offset = p_input->p->i_seekpoint_offset = 0; p_input->p->i_title_offset = p_input->p->i_seekpoint_offset = 0;
p_input->i_state = INIT_S; p_input->i_state = INIT_S;
p_input->p->i_rate = INPUT_RATE_DEFAULT; p_input->p->i_rate = INPUT_RATE_DEFAULT;
p_input->p->b_recording = false;
TAB_INIT( p_input->p->i_bookmark, p_input->p->bookmark ); TAB_INIT( p_input->p->i_bookmark, p_input->p->bookmark );
TAB_INIT( p_input->p->i_attachment, p_input->p->attachment ); TAB_INIT( p_input->p->i_attachment, p_input->p->attachment );
p_input->p->p_es_out = NULL; p_input->p->p_es_out = NULL;
...@@ -235,6 +237,8 @@ static input_thread_t *Create( vlc_object_t *p_parent, input_item_t *p_item, ...@@ -235,6 +237,8 @@ static input_thread_t *Create( vlc_object_t *p_parent, input_item_t *p_item,
/* Create Objects variables for public Get and Set */ /* Create Objects variables for public Get and Set */
input_ControlVarInit( p_input ); input_ControlVarInit( p_input );
/* */
p_input->p->pts_adjust.b_auto_adjust = var_GetBool( p_input, "auto-adjust-pts-delay" );
p_input->p->input.i_cr_average = var_GetInteger( p_input, "cr-average" ); p_input->p->input.i_cr_average = var_GetInteger( p_input, "cr-average" );
if( !p_input->b_preparsing ) if( !p_input->b_preparsing )
...@@ -1452,6 +1456,12 @@ static bool Control( input_thread_t *p_input, int i_type, ...@@ -1452,6 +1456,12 @@ static bool Control( input_thread_t *p_input, int i_type,
case INPUT_CONTROL_SET_POSITION_OFFSET: case INPUT_CONTROL_SET_POSITION_OFFSET:
{ {
double f_pos; double f_pos;
if( p_input->p->b_recording )
{
msg_Err( p_input, "INPUT_CONTROL_SET_POSITION(_OFFSET) ignored while recording" );
break;
}
if( i_type == INPUT_CONTROL_SET_POSITION ) if( i_type == INPUT_CONTROL_SET_POSITION )
{ {
f_pos = val.f_float; f_pos = val.f_float;
...@@ -1489,6 +1499,12 @@ static bool Control( input_thread_t *p_input, int i_type, ...@@ -1489,6 +1499,12 @@ static bool Control( input_thread_t *p_input, int i_type,
int64_t i_time; int64_t i_time;
int i_ret; int i_ret;
if( p_input->p->b_recording )
{
msg_Err( p_input, "INPUT_CONTROL_SET_TIME(_OFFSET) ignored while recording" );
break;
}
if( i_type == INPUT_CONTROL_SET_TIME ) if( i_type == INPUT_CONTROL_SET_TIME )
{ {
i_time = val.i_time; i_time = val.i_time;
...@@ -1744,6 +1760,11 @@ static bool Control( input_thread_t *p_input, int i_type, ...@@ -1744,6 +1760,11 @@ static bool Control( input_thread_t *p_input, int i_type,
case INPUT_CONTROL_SET_TITLE: case INPUT_CONTROL_SET_TITLE:
case INPUT_CONTROL_SET_TITLE_NEXT: case INPUT_CONTROL_SET_TITLE_NEXT:
case INPUT_CONTROL_SET_TITLE_PREV: case INPUT_CONTROL_SET_TITLE_PREV:
if( p_input->p->b_recording )
{
msg_Err( p_input, "INPUT_CONTROL_SET_TITLE(*) ignored while recording" );
break;
}
if( p_input->p->input.b_title_demux && if( p_input->p->input.b_title_demux &&
p_input->p->input.i_title > 0 ) p_input->p->input.i_title > 0 )
{ {
...@@ -1791,6 +1812,12 @@ static bool Control( input_thread_t *p_input, int i_type, ...@@ -1791,6 +1812,12 @@ static bool Control( input_thread_t *p_input, int i_type,
case INPUT_CONTROL_SET_SEEKPOINT: case INPUT_CONTROL_SET_SEEKPOINT:
case INPUT_CONTROL_SET_SEEKPOINT_NEXT: case INPUT_CONTROL_SET_SEEKPOINT_NEXT:
case INPUT_CONTROL_SET_SEEKPOINT_PREV: case INPUT_CONTROL_SET_SEEKPOINT_PREV:
if( p_input->p->b_recording )
{
msg_Err( p_input, "INPUT_CONTROL_SET_SEEKPOINT(*) ignored while recording" );
break;
}
if( p_input->p->input.b_title_demux && if( p_input->p->input.b_title_demux &&
p_input->p->input.i_title > 0 ) p_input->p->input.i_title > 0 )
{ {
...@@ -1918,6 +1945,24 @@ static bool Control( input_thread_t *p_input, int i_type, ...@@ -1918,6 +1945,24 @@ static bool Control( input_thread_t *p_input, int i_type,
} }
break; break;
case INPUT_CONTROL_SET_RECORD_STATE:
if( p_input->p->input.b_can_record )
{
if( !!p_input->p->b_recording != !!val.b_bool )
{
if( demux_Control( p_input->p->input.p_demux,
DEMUX_SET_RECORD_STATE, val.b_bool ) )
val.b_bool = false;
p_input->p->b_recording = val.b_bool;
}
var_Change( p_input, "record", VLC_VAR_SETVALUE, &val, NULL );
b_force_update = true;
}
break;
case INPUT_CONTROL_SET_BOOKMARK: case INPUT_CONTROL_SET_BOOKMARK:
default: default:
msg_Err( p_input, "not yet implemented" ); msg_Err( p_input, "not yet implemented" );
...@@ -2056,8 +2101,8 @@ static void UpdateItemLength( input_thread_t *p_input, int64_t i_length ) ...@@ -2056,8 +2101,8 @@ static void UpdateItemLength( input_thread_t *p_input, int64_t i_length )
*****************************************************************************/ *****************************************************************************/
static input_source_t *InputSourceNew( input_thread_t *p_input ) static input_source_t *InputSourceNew( input_thread_t *p_input )
{ {
(void)p_input; VLC_UNUSED(p_input);
input_source_t *in = (input_source_t*) malloc( sizeof( input_source_t ) ); input_source_t *in = malloc( sizeof( input_source_t ) );
if( in ) if( in )
memset( in, 0, sizeof( input_source_t ) ); memset( in, 0, sizeof( input_source_t ) );
return in; return in;
...@@ -2320,6 +2365,10 @@ static int InputSourceInit( input_thread_t *p_input, ...@@ -2320,6 +2365,10 @@ static int InputSourceInit( input_thread_t *p_input,
goto error; goto error;
} }
if( demux_Control( in->p_demux, DEMUX_CAN_RECORD, &in->b_can_record ) )
in->b_can_record = false;
var_SetBool( p_input, "can-record", in->b_can_record );
/* Get title from demux */ /* Get title from demux */
if( !p_input->b_preparsing && in->i_title <= 0 ) if( !p_input->b_preparsing && in->i_title <= 0 )
{ {
......
...@@ -63,6 +63,7 @@ typedef struct ...@@ -63,6 +63,7 @@ typedef struct
bool b_can_pause; bool b_can_pause;
bool b_can_pace_control; bool b_can_pace_control;
bool b_can_rate_control; bool b_can_rate_control;
bool b_can_record;
bool b_rescale_ts; bool b_rescale_ts;
bool b_eof; /* eof of demuxer */ bool b_eof; /* eof of demuxer */
...@@ -84,6 +85,7 @@ struct input_thread_private_t ...@@ -84,6 +85,7 @@ struct input_thread_private_t
bool b_can_rate_control; bool b_can_rate_control;
int i_rate; int i_rate;
bool b_recording;
/* */ /* */
int64_t i_start; /* :start-time,0 by default */ int64_t i_start; /* :start-time,0 by default */
int64_t i_stop; /* :stop-time, 0 if none */ int64_t i_stop; /* :stop-time, 0 if none */
...@@ -116,10 +118,11 @@ struct input_thread_private_t ...@@ -116,10 +118,11 @@ struct input_thread_private_t
input_source_t **slave; input_source_t **slave;
/* pts delay fixup */ /* pts delay fixup */
struct { struct
{
int i_num_faulty; int i_num_faulty;
bool to_high; bool b_to_high;
bool auto_adjust; bool b_auto_adjust;
} pts_adjust; } pts_adjust;
/* Stats counters */ /* Stats counters */
...@@ -191,6 +194,8 @@ enum input_control_e ...@@ -191,6 +194,8 @@ enum input_control_e
INPUT_CONTROL_SET_SPU_DELAY, INPUT_CONTROL_SET_SPU_DELAY,
INPUT_CONTROL_ADD_SLAVE, INPUT_CONTROL_ADD_SLAVE,
INPUT_CONTROL_SET_RECORD_STATE,
}; };
/* Internal helpers */ /* Internal helpers */
......
...@@ -25,7 +25,12 @@ ...@@ -25,7 +25,12 @@
# include "config.h" # include "config.h"
#endif #endif
#include <dirent.h>
#include <vlc_common.h> #include <vlc_common.h>
#include <vlc_charset.h>
#include <vlc_strings.h>
#include <vlc_osd.h>
#include <assert.h> #include <assert.h>
...@@ -186,6 +191,14 @@ struct stream_sys_t ...@@ -186,6 +191,14 @@ struct stream_sys_t
/* Preparse mode ? */ /* Preparse mode ? */
bool b_quick; bool b_quick;
/* */
struct
{
bool b_active;
FILE *f; /* TODO it could be replaced by access_output_t one day */
} record;
}; };
/* Method 1: */ /* Method 1: */
...@@ -212,6 +225,8 @@ static int AStreamControl( stream_t *s, int i_query, va_list ); ...@@ -212,6 +225,8 @@ static int AStreamControl( stream_t *s, int i_query, va_list );
static void AStreamDestroy( stream_t *s ); static void AStreamDestroy( stream_t *s );
static void UStreamDestroy( stream_t *s ); static void UStreamDestroy( stream_t *s );
static int ASeek( stream_t *s, int64_t i_pos ); static int ASeek( stream_t *s, int64_t i_pos );
static int ARecordSetState( stream_t *s, bool b_record, const char *psz_extension );
static void ARecordWrite( stream_t *s, const uint8_t *p_buffer, size_t i_buffer );
/**************************************************************************** /****************************************************************************
* Method 3 helpers: * Method 3 helpers:
...@@ -314,6 +329,8 @@ stream_t *stream_AccessNew( access_t *p_access, bool b_quick ) ...@@ -314,6 +329,8 @@ stream_t *stream_AccessNew( access_t *p_access, bool b_quick )
else else
p_sys->method = Stream; p_sys->method = Stream;
p_sys->record.b_active = false;
p_sys->i_pos = p_access->info.i_pos; p_sys->i_pos = p_access->info.i_pos;
/* Stats */ /* Stats */
...@@ -512,9 +529,15 @@ static void AStreamDestroy( stream_t *s ) ...@@ -512,9 +529,15 @@ static void AStreamDestroy( stream_t *s )
vlc_object_detach( s ); vlc_object_detach( s );
if( p_sys->method == Block ) block_ChainRelease( p_sys->block.p_first ); if( p_sys->record.b_active )
else if ( p_sys->method == Immediate ) free( p_sys->immediate.p_buffer ); ARecordSetState( s, false, NULL );
else free( p_sys->stream.p_buffer );
if( p_sys->method == Block )
block_ChainRelease( p_sys->block.p_first );
else if ( p_sys->method == Immediate )
free( p_sys->immediate.p_buffer );
else
free( p_sys->stream.p_buffer );
free( p_sys->p_peek ); free( p_sys->p_peek );
...@@ -617,6 +640,8 @@ static int AStreamControl( stream_t *s, int i_query, va_list args ) ...@@ -617,6 +640,8 @@ static int AStreamControl( stream_t *s, int i_query, va_list args )
access_t *p_access = p_sys->p_access; access_t *p_access = p_sys->p_access;
bool *p_bool; bool *p_bool;
bool b_bool;
const char *psz_string;
int64_t *pi_64, i_64; int64_t *pi_64, i_64;
int i_int; int i_int;
...@@ -677,6 +702,12 @@ static int AStreamControl( stream_t *s, int i_query, va_list args ) ...@@ -677,6 +702,12 @@ static int AStreamControl( stream_t *s, int i_query, va_list args )
case STREAM_GET_CONTENT_TYPE: case STREAM_GET_CONTENT_TYPE:
return access_Control( p_access, ACCESS_GET_CONTENT_TYPE, return access_Control( p_access, ACCESS_GET_CONTENT_TYPE,
va_arg( args, char ** ) ); va_arg( args, char ** ) );
case STREAM_SET_RECORD_STATE:
b_bool = (bool)va_arg( args, int );
psz_string = NULL;
if( b_bool )
psz_string = (const char*)va_arg( args, const char* );
return ARecordSetState( s, b_bool, psz_string );
default: default:
msg_Err( s, "invalid stream_vaControl query=0x%x", i_query ); msg_Err( s, "invalid stream_vaControl query=0x%x", i_query );
...@@ -685,7 +716,120 @@ static int AStreamControl( stream_t *s, int i_query, va_list args ) ...@@ -685,7 +716,120 @@ static int AStreamControl( stream_t *s, int i_query, va_list args )
return VLC_SUCCESS; return VLC_SUCCESS;
} }
/****************************************************************************
* ARecord*: record stream functions
****************************************************************************/
/* TODO FIXME nearly the same logic that snapshot code */
static char *ARecordGetFileName( stream_t *s, const char *psz_path, const char *psz_prefix, const char *psz_extension )
{
char *psz_file;
DIR *path;
path = utf8_opendir( psz_path );
if( path )
{
closedir( path );
const char *psz_prefix = "vlc-record-%Y-%m-%d-%H:%M:%S-$p"; // TODO allow conf ?
char *psz_tmp = str_format( s, psz_prefix );
if( !psz_tmp )
return NULL;
filename_sanitize( psz_tmp );
if( asprintf( &psz_file, "%s"DIR_SEP"%s.%s",
psz_path, psz_tmp, psz_extension ) < 0 )
psz_file = NULL;
free( psz_tmp );
return psz_file;
}
else
{
psz_file = str_format( s, psz_path );
path_sanitize( psz_file );
return psz_file;
}
}
static int ARecordStart( stream_t *s, const char *psz_extension )
{
stream_sys_t *p_sys = s->p_sys;
DIR *path;
char *psz_file;
FILE *f;
/* */
if( !psz_extension )
psz_extension = "dat";
/* Retreive path */
char *psz_path = var_CreateGetString( s, "input-record-path" );
if( !psz_path || *psz_path == '\0' )
{
free( psz_path );
psz_path = strdup( config_GetHomeDir() );
}
if( !psz_path )
return VLC_ENOMEM;
/* Create file name
* TODO allow prefix configuration */
psz_file = ARecordGetFileName( s, psz_path, "vlc-record-%Y-%m-%d-%H:%M:%S-$p", psz_extension );
free( psz_path );
if( !psz_file )
return VLC_ENOMEM;
f = utf8_fopen( psz_file, "wb" );
if( !f )
{
free( psz_file );
return VLC_EGENERIC;
}
msg_Dbg( s, "Recording into %s", psz_file );
free( psz_file );
/* */
p_sys->record.f = f;
p_sys->record.b_active = true;
return VLC_SUCCESS;
}
static int ARecordStop( stream_t *s )
{
stream_sys_t *p_sys = s->p_sys;
assert( p_sys->record.b_active );
msg_Dbg( s, "Recording completed" );
fclose( p_sys->record.f );
p_sys->record.b_active = false;
return VLC_SUCCESS;
}
static int ARecordSetState( stream_t *s, bool b_record, const char *psz_extension )
{
stream_sys_t *p_sys = s->p_sys;
if( !!p_sys->record.b_active == !!b_record )
return VLC_SUCCESS;
if( b_record )
return ARecordStart( s, psz_extension );
else
return ARecordStop( s );
}
static void ARecordWrite( stream_t *s, const uint8_t *p_buffer, size_t i_buffer )
{
stream_sys_t *p_sys = s->p_sys;
assert( p_sys->record.b_active );
if( i_buffer )
fwrite( p_buffer, 1, i_buffer, p_sys->record.f );
}
/**************************************************************************** /****************************************************************************
* Method 1: * Method 1:
...@@ -821,6 +965,9 @@ static int AStreamReadBlock( stream_t *s, void *p_read, unsigned int i_read ) ...@@ -821,6 +965,9 @@ static int AStreamReadBlock( stream_t *s, void *p_read, unsigned int i_read )
} }
} }
if( p_sys->record.b_active && i_data > 0 )
ARecordWrite( s, p_read, i_data );
p_sys->i_pos += i_data; p_sys->i_pos += i_data;
return i_data; return i_data;
} }
...@@ -1165,6 +1312,9 @@ static int AStreamReadStream( stream_t *s, void *p_read, unsigned int i_read ) ...@@ -1165,6 +1312,9 @@ static int AStreamReadStream( stream_t *s, void *p_read, unsigned int i_read )
} }
} }
if( p_sys->record.b_active && i_data > 0 )
ARecordWrite( s, p_read, i_data );
return i_data; return i_data;
} }
...@@ -1552,6 +1702,9 @@ static int AStreamReadImmediate( stream_t *s, void *p_read, unsigned int i_read ...@@ -1552,6 +1702,9 @@ static int AStreamReadImmediate( stream_t *s, void *p_read, unsigned int i_read
} }
} }
if( p_sys->record.b_active && i_copy > 0 )
ARecordWrite( s, p_read, i_copy );
p_sys->i_pos += i_to_read; p_sys->i_pos += i_to_read;
return i_to_read + i_copy; return i_to_read + i_copy;
......
...@@ -61,6 +61,10 @@ static int EsDelayCallback ( vlc_object_t *p_this, char const *psz_cmd, ...@@ -61,6 +61,10 @@ static int EsDelayCallback ( vlc_object_t *p_this, char const *psz_cmd,
static int BookmarkCallback( vlc_object_t *p_this, char const *psz_cmd, static int BookmarkCallback( vlc_object_t *p_this, char const *psz_cmd,
vlc_value_t oldval, vlc_value_t newval, void * ); vlc_value_t oldval, vlc_value_t newval, void * );
static int RecordCallback( vlc_object_t *p_this, char const *psz_cmd,
vlc_value_t oldval, vlc_value_t newval,
void *p_data );
typedef struct typedef struct
{ {
const char *psz_name; const char *psz_name;
...@@ -93,6 +97,7 @@ static const vlc_input_callback_t p_input_callbacks[] = ...@@ -93,6 +97,7 @@ static const vlc_input_callback_t p_input_callbacks[] =
CALLBACK( "video-es", ESCallback ), CALLBACK( "video-es", ESCallback ),
CALLBACK( "audio-es", ESCallback ), CALLBACK( "audio-es", ESCallback ),
CALLBACK( "spu-es", ESCallback ), CALLBACK( "spu-es", ESCallback ),
CALLBACK( "record", RecordCallback ),
CALLBACK( NULL, NULL ) CALLBACK( NULL, NULL )
}; };
...@@ -189,9 +194,6 @@ void input_ControlVarInit ( input_thread_t *p_input ) ...@@ -189,9 +194,6 @@ void input_ControlVarInit ( input_thread_t *p_input )
val.i_time = 0; val.i_time = 0;
var_Change( p_input, "spu-delay", VLC_VAR_SETVALUE, &val, NULL ); var_Change( p_input, "spu-delay", VLC_VAR_SETVALUE, &val, NULL );
p_input->p->pts_adjust.auto_adjust = var_CreateGetBool(
p_input, "auto-adjust-pts-delay" );
/* Video ES */ /* Video ES */
var_Create( p_input, "video-es", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE ); var_Create( p_input, "video-es", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
text.psz_string = _("Video Track"); text.psz_string = _("Video Track");
...@@ -404,8 +406,6 @@ void input_ControlVarTitle( input_thread_t *p_input, int i_title ) ...@@ -404,8 +406,6 @@ void input_ControlVarTitle( input_thread_t *p_input, int i_title )
*****************************************************************************/ *****************************************************************************/
void input_ConfigVarInit ( input_thread_t *p_input ) void input_ConfigVarInit ( input_thread_t *p_input )
{ {
vlc_value_t val;
/* Create Object Variables for private use only */ /* Create Object Variables for private use only */
if( !p_input->b_preparsing ) if( !p_input->b_preparsing )
...@@ -460,14 +460,22 @@ void input_ConfigVarInit ( input_thread_t *p_input ) ...@@ -460,14 +460,22 @@ void input_ConfigVarInit ( input_thread_t *p_input )
VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
var_Create( p_input, "clock-synchro", var_Create( p_input, "clock-synchro",
VLC_VAR_INTEGER | VLC_VAR_DOINHERIT); VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
var_Create( p_input, "auto-adjust-pts-delay",
VLC_VAR_BOOL | VLC_VAR_DOINHERIT);
} }
var_Create( p_input, "seekable", VLC_VAR_BOOL ); var_Create( p_input, "seekable", VLC_VAR_BOOL );
val.b_bool = true; /* Fixed later*/ var_SetBool( p_input, "seekable", true ); /* Fixed later*/
var_Change( p_input, "seekable", VLC_VAR_SETVALUE, &val, NULL );
var_Create( p_input, "can-pause", VLC_VAR_BOOL ); var_Create( p_input, "can-pause", VLC_VAR_BOOL );
val.b_bool = true; /* Fixed later*/ var_SetBool( p_input, "can-pause", true ); /* Fixed later*/
var_Change( p_input, "can-pause", VLC_VAR_SETVALUE, &val, NULL );
var_Create( p_input, "can-record", VLC_VAR_BOOL );
var_SetBool( p_input, "can-record", false ); /* Fixed later*/
var_Create( p_input, "record", VLC_VAR_BOOL );
var_SetBool( p_input, "record", false );
var_Create( p_input, "teletext-es", VLC_VAR_INTEGER ); var_Create( p_input, "teletext-es", VLC_VAR_INTEGER );
var_SetInteger( p_input, "teletext-es", -1 ); var_SetInteger( p_input, "teletext-es", -1 );
...@@ -776,3 +784,15 @@ static int BookmarkCallback( vlc_object_t *p_this, char const *psz_cmd, ...@@ -776,3 +784,15 @@ static int BookmarkCallback( vlc_object_t *p_this, char const *psz_cmd,
return VLC_SUCCESS; return VLC_SUCCESS;
} }
static int RecordCallback( 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_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data);
input_ControlPush( p_input, INPUT_CONTROL_SET_RECORD_STATE, &newval );
return VLC_SUCCESS;
}
...@@ -675,6 +675,10 @@ static const char *const ppsz_clock_descriptions[] = ...@@ -675,6 +675,10 @@ static const char *const ppsz_clock_descriptions[] =
"the form \"{name=bookmark-name,time=optional-time-offset," \ "the form \"{name=bookmark-name,time=optional-time-offset," \
"bytes=optional-byte-offset},{...}\"") "bytes=optional-byte-offset},{...}\"")
#define INPUT_RECORD_PATH_TEXT N_("Record directory or filename")
#define INPUT_RECORD_PATH_LONGTEXT N_( \
"Directory or filename where the records will be stored" )
// DEPRECATED // DEPRECATED
#define SUB_CAT_LONGTEXT N_( \ #define SUB_CAT_LONGTEXT N_( \
"These options allow you to modify the behavior of the subpictures " \ "These options allow you to modify the behavior of the subpictures " \
...@@ -1737,6 +1741,9 @@ vlc_module_begin(); ...@@ -1737,6 +1741,9 @@ vlc_module_begin();
add_bool( "network-synchronisation", false, NULL, NETSYNC_TEXT, add_bool( "network-synchronisation", false, NULL, NETSYNC_TEXT,
NETSYNC_LONGTEXT, true ); NETSYNC_LONGTEXT, true );
add_string( "input-record-path", NULL, NULL, INPUT_RECORD_PATH_TEXT,
INPUT_RECORD_PATH_LONGTEXT, true );
/* Decoder options */ /* Decoder options */
add_category_hint( N_("Decoders"), CODEC_CAT_LONGTEXT , true ); add_category_hint( N_("Decoders"), CODEC_CAT_LONGTEXT , true );
add_string( "codec", NULL, NULL, CODEC_TEXT, add_string( "codec", NULL, NULL, CODEC_TEXT,
......
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