Commit c1412599 authored by Laurent Aimar's avatar Laurent Aimar

Added recording on the fly through sout.

parent d4e0e64e
......@@ -29,9 +29,9 @@
# include "config.h"
#endif
#include <vlc_common.h>
#include <stdio.h>
#include <assert.h>
#include <vlc_common.h>
#include <vlc_input.h>
#include <vlc_es_out.h>
......@@ -40,6 +40,8 @@
#include "input_internal.h"
#include "../stream_output/stream_output.h"
#include <vlc_iso_lang.h>
/* FIXME we should find a better way than including that */
#include "../text/iso-639_def.h"
......@@ -83,6 +85,7 @@ struct es_out_id_t
char *psz_language_code;
decoder_t *p_dec;
decoder_t *p_dec_record;
/* Fields for Video with CC */
bool pb_cc_present[4];
......@@ -134,6 +137,9 @@ struct es_out_sys_t
/* Rate used to rescale ES ts */
int i_rate;
/* Record */
sout_instance_t *p_sout_record;
};
static es_out_id_t *EsOutAdd ( es_out_t *, es_format_t * );
......@@ -266,6 +272,8 @@ es_out_t *input_EsOutNew( input_thread_t *p_input, int i_rate )
p_sys->i_rate = i_rate;
p_sys->p_sout_record = NULL;
return out;
}
......@@ -277,12 +285,14 @@ void input_EsOutDelete( es_out_t *out )
es_out_sys_t *p_sys = out->p_sys;
int i;
if( p_sys->p_sout_record )
input_EsOutSetRecord( out, false );
for( i = 0; i < p_sys->i_es; i++ )
{
if( p_sys->es[i]->p_dec )
{
input_DecoderDelete( p_sys->es[i]->p_dec );
}
free( p_sys->es[i]->psz_language );
free( p_sys->es[i]->psz_language_code );
es_format_Clean( &p_sys->es[i]->fmt );
......@@ -301,7 +311,6 @@ void input_EsOutDelete( es_out_t *out )
free( p_sys->ppsz_sub_language[i] );
free( p_sys->ppsz_sub_language );
}
free( p_sys->es );
/* FIXME duplicate work EsOutProgramDel (but we cannot use it) add a EsOutProgramClean ? */
......@@ -351,7 +360,11 @@ static void EsOutDiscontinuity( es_out_t *out, bool b_flush, bool b_audio )
/* 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, b_flush );
if( es->p_dec_record )
input_DecoderDiscontinuity( es->p_dec_record, b_flush );
}
}
}
void input_EsOutChangeRate( es_out_t *out, int i_rate )
......@@ -366,6 +379,73 @@ void input_EsOutChangeRate( es_out_t *out, int i_rate )
input_ClockSetRate( &p_sys->pgrm[i]->clock, i_rate );
}
int input_EsOutSetRecord( es_out_t *out, bool b_record )
{
es_out_sys_t *p_sys = out->p_sys;
input_thread_t *p_input = p_sys->p_input;
assert( ( b_record && !p_sys->p_sout_record ) || ( !b_record && p_sys->p_sout_record ) );
if( b_record )
{
char *psz_path = var_CreateGetString( p_input, "input-record-path" );
if( !psz_path || *psz_path == '\0' )
{
free( psz_path );
psz_path = strdup( config_GetHomeDir() );
}
char *psz_sout = NULL; // TODO conf
if( !psz_sout && psz_path )
{
char *psz_file = input_CreateFilename( VLC_OBJECT(p_input), psz_path, INPUT_RECORD_PREFIX, NULL );
if( psz_file )
{
if( asprintf( &psz_sout, "#record{dst-prefix='%s'}", psz_file ) < 0 )
psz_sout = NULL;
free( psz_file );
}
}
free( psz_path );
if( !psz_sout )
return VLC_EGENERIC;
p_sys->p_sout_record = sout_NewInstance( p_input, psz_sout );
free( psz_sout );
if( !p_sys->p_sout_record )
return VLC_EGENERIC;
for( int i = 0; i < p_sys->i_es; i++ )
{
es_out_id_t *p_es = p_sys->es[i];
if( !p_es->p_dec || p_es->p_master )
continue;
p_es->p_dec_record = input_DecoderNew( p_input, &p_es->fmt, p_sys->p_sout_record );
}
}
else
{
for( int i = 0; i < p_sys->i_es; i++ )
{
es_out_id_t *p_es = p_sys->es[i];
if( !p_es->p_dec_record )
continue;
input_DecoderDelete( p_es->p_dec_record );
p_es->p_dec_record = NULL;
}
sout_DeleteInstance( p_sys->p_sout_record );
p_sys->p_sout_record = NULL;
}
return VLC_SUCCESS;
}
void input_EsOutSetDelay( es_out_t *out, int i_cat, int64_t i_delay )
{
es_out_sys_t *p_sys = out->p_sys;
......@@ -411,6 +491,8 @@ bool input_EsOutDecodersEmpty( es_out_t *out )
if( es->p_dec && !input_DecoderEmpty( es->p_dec ) )
return false;
if( es->p_dec_record && !input_DecoderEmpty( es->p_dec_record ) )
return false;
}
return true;
}
......@@ -994,9 +1076,10 @@ static es_out_id_t *EsOutAdd( es_out_t *out, es_format_t *fmt )
es->psz_language = LanguageGetName( fmt->psz_language ); /* remember so we only need to do it once */
es->psz_language_code = LanguageGetCode( fmt->psz_language );
es->p_dec = NULL;
es->p_dec_record = NULL;
for( i = 0; i < 4; i++ )
es->pb_cc_present[i] = false;
es->p_master = false;
es->p_master = NULL;
if( es->p_pgrm == p_sys->p_pgrm )
EsOutESVarUpdate( out, es, false );
......@@ -1043,6 +1126,32 @@ static bool EsIsSelected( es_out_id_t *es )
return es->p_dec != NULL;
}
}
static void EsCreateDecoder( es_out_t *out, es_out_id_t *p_es )
{
es_out_sys_t *p_sys = out->p_sys;
input_thread_t *p_input = p_sys->p_input;
p_es->p_dec = input_DecoderNew( p_input, &p_es->fmt, p_input->p->p_sout );
if( p_es->p_dec && !p_es->p_master && p_sys->p_sout_record )
p_es->p_dec_record = input_DecoderNew( p_input, &p_es->fmt, p_sys->p_sout_record );
}
static void EsDestroyDecoder( es_out_t *out, es_out_id_t *p_es )
{
es_out_sys_t *p_sys = out->p_sys;
if( !p_es->p_dec )
return;
input_DecoderDelete( p_es->p_dec );
p_es->p_dec = NULL;
if( p_es->p_dec_record )
{
input_DecoderDelete( p_es->p_dec_record );
p_es->p_dec_record = NULL;
}
}
static void EsSelect( es_out_t *out, es_out_id_t *es )
{
es_out_sys_t *p_sys = out->p_sys;
......@@ -1099,7 +1208,8 @@ static void EsSelect( es_out_t *out, es_out_id_t *es )
}
es->i_preroll_end = -1;
es->p_dec = input_DecoderNew( p_input, &es->fmt, false );
EsCreateDecoder( out, es );
if( es->p_dec == NULL || es->p_pgrm != p_sys->p_pgrm )
return;
}
......@@ -1163,8 +1273,7 @@ static void EsUnselect( es_out_t *out, es_out_id_t *es, bool b_update )
es->pb_cc_present[i] = false;
}
input_DecoderDelete( es->p_dec );
es->p_dec = NULL;
EsDestroyDecoder( out, es );
}
if( !b_update )
......@@ -1450,6 +1559,12 @@ static int EsOutSend( es_out_t *out, es_out_id_t *es, block_t *p_block )
bool pb_cc[4];
bool b_cc_new = false;
int i;
if( es->p_dec_record )
{
block_t *p_dup = block_Duplicate( p_block );
if( p_dup )
input_DecoderDecode( es->p_dec_record, p_dup );
}
input_DecoderDecode( es->p_dec, p_block );
/* Check CC status */
......@@ -1510,7 +1625,8 @@ static void EsOutDel( es_out_t *out, es_out_id_t *es )
{
while( !out->p_sys->p_input->b_die && es->p_dec )
{
if( input_DecoderEmpty( es->p_dec ) )
if( input_DecoderEmpty( es->p_dec ) &&
( !es->p_dec_record || input_DecoderEmpty( es->p_dec_record ) ))
break;
msleep( 20*1000 );
}
......@@ -1824,7 +1940,8 @@ static int EsOutControl( es_out_t *out, int i_query, va_list args )
es_format_t *p_fmt;
es = (es_out_id_t*) va_arg( args, es_out_id_t * );
p_fmt = (es_format_t*) va_arg( args, es_format_t * );
if( es == NULL ) return VLC_EGENERIC;
if( es == NULL )
return VLC_EGENERIC;
if( p_fmt->i_extra )
{
......@@ -1832,13 +1949,12 @@ static int EsOutControl( es_out_t *out, int i_query, va_list args )
es->fmt.p_extra = realloc( es->fmt.p_extra, p_fmt->i_extra );
memcpy( es->fmt.p_extra, p_fmt->p_extra, p_fmt->i_extra );
if( !es->p_dec ) return VLC_SUCCESS;
if( !es->p_dec )
return VLC_SUCCESS;
#if 1
input_DecoderDelete( es->p_dec );
es->p_dec = input_DecoderNew( p_sys->p_input,
&es->fmt, false );
EsDestroyDecoder( out, es );
EsCreateDecoder( out, es );
#else
es->p_dec->fmt_in.i_extra = p_fmt->i_extra;
es->p_dec->fmt_in.p_extra =
......
......@@ -43,6 +43,7 @@
#include <vlc_interface.h>
#include <vlc_url.h>
#include <vlc_charset.h>
#include <vlc_strings.h>
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
......@@ -1946,16 +1947,20 @@ static bool Control( input_thread_t *p_input, int i_type,
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( p_input->p->input.b_can_stream_record )
{
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;
}
else
{
if( input_EsOutSetRecord( p_input->p->p_es_out, 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 );
......@@ -2365,9 +2370,11 @@ static int InputSourceInit( input_thread_t *p_input,
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 );
if( demux_Control( in->p_demux, DEMUX_CAN_RECORD, &in->b_can_stream_record ) )
in->b_can_stream_record = false;
if( !var_CreateGetBool( p_input, "input-record-native" ) )
in->b_can_stream_record = false;
var_SetBool( p_input, "can-record", true ); // Is it still needed ?
/* Get title from demux */
if( !p_input->b_preparsing && in->i_title <= 0 )
......@@ -2929,3 +2936,37 @@ vlc_event_manager_t *input_get_event_manager( input_thread_t *p_input )
{
return &p_input->p->event_manager;
}
/**/
/* TODO FIXME nearly the same logic that snapshot code */
char *input_CreateFilename( vlc_object_t *p_obj, 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 );
char *psz_tmp = str_format( p_obj, psz_prefix );
if( !psz_tmp )
return NULL;
filename_sanitize( psz_tmp );
if( asprintf( &psz_file, "%s"DIR_SEP"%s%s%s",
psz_path, psz_tmp,
psz_extension ? "." : "",
psz_extension ? psz_extension : "" ) < 0 )
psz_file = NULL;
free( psz_tmp );
return psz_file;
}
else
{
psz_file = str_format( p_obj, psz_path );
path_sanitize( psz_file );
return psz_file;
}
}
......@@ -63,7 +63,7 @@ typedef struct
bool b_can_pause;
bool b_can_pace_control;
bool b_can_rate_control;
bool b_can_record;
bool b_can_stream_record;
bool b_rescale_ts;
bool b_eof; /* eof of demuxer */
......@@ -343,6 +343,7 @@ es_out_t *input_EsOutNew( input_thread_t *, int i_rate );
void input_EsOutDelete( es_out_t * );
es_out_id_t *input_EsOutGetFromID( es_out_t *, int i_id );
void input_EsOutSetDelay( es_out_t *, int i_cat, int64_t );
int input_EsOutSetRecord( es_out_t *, bool b_record );
void input_EsOutChangeRate( es_out_t *, int );
void input_EsOutChangeState( es_out_t * );
void input_EsOutChangePosition( es_out_t * );
......@@ -419,6 +420,10 @@ static inline void input_ChangeState( input_thread_t *p_input, int state )
input_ChangeStateWithVarCallback( p_input, state, true );
}
/* Helpers FIXME to export without input_ prefix */
char *input_CreateFilename( vlc_object_t *p_obj, const char *psz_path, const char *psz_prefix, const char *psz_extension );
#define INPUT_RECORD_PREFIX "vlc-record-%Y-%m-%d-%H:%M:%S-$p"
/* Access */
......
......@@ -28,9 +28,9 @@
#include <dirent.h>
#include <vlc_common.h>
#include <vlc_charset.h>
#include <vlc_strings.h>
#include <vlc_osd.h>
#include <vlc_charset.h>
#include <assert.h>
......@@ -731,37 +731,6 @@ static int AStreamControl( stream_t *s, int i_query, va_list args )
/****************************************************************************
* 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 );
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;
......@@ -786,7 +755,7 @@ static int ARecordStart( stream_t *s, const char *psz_extension )
/* Create file name
* TODO allow prefix configuration */
psz_file = ARecordGetFileName( s, psz_path, "vlc-record-%Y-%m-%d-%H:%M:%S-$p", psz_extension );
psz_file = input_CreateFilename( VLC_OBJECT(s), psz_path, INPUT_RECORD_PREFIX, psz_extension );
free( psz_path );
......
......@@ -679,6 +679,11 @@ static const char *const ppsz_clock_descriptions[] =
#define INPUT_RECORD_PATH_LONGTEXT N_( \
"Directory or filename where the records will be stored" )
#define INPUT_RECORD_NATIVE_TEXT N_("Prefer native stream recording")
#define INPUT_RECORD_NATIVE_LONGTEXT N_( \
"When possible, the input stream will be recorded instead of using" \
"the stream output module" )
// DEPRECATED
#define SUB_CAT_LONGTEXT N_( \
"These options allow you to modify the behavior of the subpictures " \
......@@ -1743,6 +1748,8 @@ vlc_module_begin();
add_string( "input-record-path", NULL, NULL, INPUT_RECORD_PATH_TEXT,
INPUT_RECORD_PATH_LONGTEXT, true );
add_bool( "input-record-native", true, NULL, INPUT_RECORD_NATIVE_TEXT,
INPUT_RECORD_NATIVE_LONGTEXT, true );
/* Decoder options */
add_category_hint( N_("Decoders"), CODEC_CAT_LONGTEXT , true );
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment