Added recording on the fly through sout.
authorLaurent Aimar <fenrir@videolan.org>
Tue, 26 Aug 2008 21:10:56 +0000 (23:10 +0200)
committerLaurent Aimar <fenrir@videolan.org>
Tue, 26 Aug 2008 22:00:40 +0000 (00:00 +0200)
src/input/es_out.c
src/input/input.c
src/input/input_internal.h
src/input/stream.c
src/libvlc-module.c

index 547f993..59e455e 100644 (file)
@@ -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 =
index 1fad35a..fbcd1c2 100644 (file)
@@ -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->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;
+    }
+}
+
index b7dfd01..1b8e335 100644 (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 */
 
index 6bdae32..2b9c793 100644 (file)
@@ -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 );
 
index a7d6310..6c27680 100644 (file)
@@ -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 );