Commit 44a1dd65 authored by Laurent Aimar's avatar Laurent Aimar

Moved stream record to its own module.

parent 584da504
......@@ -423,6 +423,12 @@ struct input_thread_t
input_thread_private_t *p;
};
/**
* Record prefix string.
* TODO make it configurable.
*/
#define INPUT_RECORD_PREFIX "vlc-record-%Y-%m-%d-%H:%M:%S-$ N-$ p"
/*****************************************************************************
* Input events and variables
*****************************************************************************/
......@@ -689,4 +695,9 @@ VLC_EXPORT( void, input_DecoderDecode,( decoder_t *, block_t * ) );
*/
VLC_EXPORT( void, input_SplitMRL, ( const char **ppsz_access, const char **ppsz_demux, char **ppsz_path, char *psz_dup ) );
/**
* This function creates a sane filename path.
*/
VLC_EXPORT( char *, input_CreateFilename, ( vlc_object_t *, const char *psz_path, const char *psz_prefix, const char *psz_extension ) );
#endif
SOURCES_stream_filter_record = record.c
libvlc_LTLIBRARIES += \
libstream_filter_record_plugin.la \
$(NULL)
/*****************************************************************************
* record.c
*****************************************************************************
* Copyright (C) 2008 Laurent Aimar
* $Id$
*
* Author: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <vlc_common.h>
#include <vlc_plugin.h>
#include <assert.h>
#include <vlc_stream.h>
#include <vlc_input.h>
#include <vlc_charset.h>
/*****************************************************************************
* Module descriptor
*****************************************************************************/
static int Open ( vlc_object_t * );
static void Close( vlc_object_t * );
vlc_module_begin()
set_category( CAT_INPUT )
set_subcategory( SUBCAT_INPUT_STREAM_FILTER )
set_description( N_("Internal stream record") )
set_capability( "stream_filter", 0 )
set_callbacks( Open, Close )
vlc_module_end()
/*****************************************************************************
*
*****************************************************************************/
struct stream_sys_t
{
bool b_active;
FILE *f; /* TODO it could be replaced by access_output_t one day */
bool b_error;
};
/****************************************************************************
* Local prototypes
****************************************************************************/
static int Read ( stream_t *, void *p_read, unsigned int i_read );
static int Peek ( stream_t *, const uint8_t **pp_peek, unsigned int i_peek );
static int Control( stream_t *, int i_query, va_list );
static int Start ( stream_t *, const char *psz_extension );
static int Stop ( stream_t * );
static void Write ( stream_t *, const uint8_t *p_buffer, size_t i_buffer );
/****************************************************************************
* Open
****************************************************************************/
static int Open ( vlc_object_t *p_this )
{
stream_t *s = (stream_t*)p_this;
stream_sys_t *p_sys;
/* */
s->p_sys = p_sys = malloc( sizeof( *p_sys ) );
if( !p_sys )
return VLC_ENOMEM;
p_sys->b_active = false;
/* */
s->pf_read = Read;
s->pf_peek = Peek;
s->pf_control = Control;
return VLC_SUCCESS;
}
/****************************************************************************
* Close
****************************************************************************/
static void Close( vlc_object_t *p_this )
{
stream_t *s = (stream_t*)p_this;
stream_sys_t *p_sys = s->p_sys;
if( p_sys->b_active )
Stop( s );
free( p_sys );
}
/****************************************************************************
* Stream filters functions
****************************************************************************/
static int Read( stream_t *s, void *p_read, unsigned int i_read )
{
stream_sys_t *p_sys = s->p_sys;
void *p_record = p_read;
/* Allocate a temporary buffer for record when no p_read */
if( p_sys->b_active && !p_record )
p_record = malloc( i_read );
/* */
const int i_record = stream_Read( s->p_source, p_record, i_read );
/* Dump read data */
if( p_sys->b_active )
{
if( p_record && i_record > 0 )
Write( s, p_record, i_record );
if( !p_read )
free( p_record );
}
return i_record;
}
static int Peek( stream_t *s, const uint8_t **pp_peek, unsigned int i_peek )
{
return stream_Peek( s->p_source, pp_peek, i_peek );
}
static int Control( stream_t *s, int i_query, va_list args )
{
if( i_query != STREAM_SET_RECORD_STATE )
return stream_vaControl( s->p_source, i_query, args );
bool b_active = (bool)va_arg( args, int );
const char *psz_extension = NULL;
if( b_active )
psz_extension = (const char*)va_arg( args, const char* );
if( !s->p_sys->b_active == !b_active )
return VLC_SUCCESS;
if( b_active )
return Start( s, psz_extension );
else
return Stop( s );
}
/****************************************************************************
* Helpers
****************************************************************************/
static int Start( stream_t *s, const char *psz_extension )
{
stream_sys_t *p_sys = s->p_sys;
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 = input_CreateFilename( VLC_OBJECT(s), psz_path, INPUT_RECORD_PREFIX, 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->f = f;
p_sys->b_active = true;
p_sys->b_error = false;
return VLC_SUCCESS;
}
static int Stop( stream_t *s )
{
stream_sys_t *p_sys = s->p_sys;
assert( p_sys->b_active );
msg_Dbg( s, "Recording completed" );
fclose( p_sys->f );
p_sys->b_active = false;
return VLC_SUCCESS;
}
static void Write( stream_t *s, const uint8_t *p_buffer, size_t i_buffer )
{
stream_sys_t *p_sys = s->p_sys;
assert( p_sys->b_active );
if( i_buffer > 0 )
{
const bool b_previous_error = p_sys->b_error;
const size_t i_written = fwrite( p_buffer, 1, i_buffer, p_sys->f );
p_sys->b_error = i_written != i_buffer;
/* TODO maybe a intf_UserError or something like that ? */
if( p_sys->b_error && !b_previous_error )
msg_Err( s, "Failed to record data (begin)" );
else if( !p_sys->b_error && b_previous_error )
msg_Err( s, "Failed to record data (end)" );
}
}
......@@ -2573,6 +2573,18 @@ static int InputSourceInit( input_thread_t *p_input,
}
free( psz_tmp );
/* Add record filter if usefull */
if( var_GetBool( p_input, "input-record-native" ) )
{
stream_t *p_filter;
p_filter = stream_FilterNew( in->p_stream, "stream_filter_record" );
if( p_filter )
in->p_stream = p_filter;
else
var_SetBool( p_input, "input-record-native", false );
}
/* Open a demuxer */
if( *psz_demux == '\0' && *in->p_access->psz_demux )
{
......
......@@ -231,9 +231,4 @@ void input_ConfigVarInit ( input_thread_t * );
char **subtitles_Detect( input_thread_t *, char* path, const char *fname );
int subtitles_Filter( const char *);
/* 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-$ N-$ p"
#endif
......@@ -185,15 +185,6 @@ struct stream_sys_t
/* Preparse mode ? */
bool b_quick;
/* */
struct
{
bool b_active;
FILE *f; /* TODO it could be replaced by access_output_t one day */
bool b_error;
} record;
};
/* Method 1: */
......@@ -215,8 +206,6 @@ static int AStreamControl( stream_t *s, int i_query, va_list );
static void AStreamDestroy( stream_t *s );
static void UStreamDestroy( stream_t *s );
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 );
/****************************************************************************
* stream_CommonNew: create an empty stream structure
......@@ -321,8 +310,6 @@ stream_t *stream_AccessNew( access_t *p_access, bool b_quick )
else
p_sys->method = STREAM_METHOD_STREAM;
p_sys->record.b_active = false;
p_sys->i_pos = p_access->info.i_pos;
/* Stats */
......@@ -498,9 +485,6 @@ static void AStreamDestroy( stream_t *s )
vlc_object_detach( s );
if( p_sys->record.b_active )
ARecordSetState( s, false, NULL );
if( p_sys->method == STREAM_METHOD_BLOCK )
block_ChainRelease( p_sys->block.p_first );
else
......@@ -605,8 +589,6 @@ static int AStreamControl( stream_t *s, int i_query, va_list args )
access_t *p_access = p_sys->p_access;
bool *p_bool;
bool b_bool;
const char *psz_string;
int64_t *pi_64, i_64;
int i_int;
......@@ -683,12 +665,6 @@ static int AStreamControl( stream_t *s, int i_query, va_list args )
return access_Control( p_access, ACCESS_GET_CONTENT_TYPE,
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:
msg_Err( s, "invalid stream_vaControl query=0x%x", i_query );
return VLC_EGENERIC;
......@@ -696,100 +672,6 @@ static int AStreamControl( stream_t *s, int i_query, va_list args )
return VLC_SUCCESS;
}
/****************************************************************************
* ARecord*: record stream functions
****************************************************************************/
static int ARecordStart( stream_t *s, const char *psz_extension )
{
stream_sys_t *p_sys = s->p_sys;
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 = input_CreateFilename( VLC_OBJECT(s), psz_path, INPUT_RECORD_PREFIX, 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;
p_sys->record.b_error = false;
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 > 0 )
{
const bool b_previous_error = p_sys->record.b_error;
const size_t i_written = fwrite( p_buffer, 1, i_buffer, p_sys->record.f );
p_sys->record.b_error = i_written != i_buffer;
/* TODO maybe a intf_UserError or something like that ? */
if( p_sys->record.b_error && !b_previous_error )
msg_Err( s, "Failed to record data (begin)" );
else if( !p_sys->record.b_error && b_previous_error )
msg_Err( s, "Failed to record data (end)" );
}
}
/****************************************************************************
* Method 1:
****************************************************************************/
......@@ -864,16 +746,12 @@ static int AStreamReadBlock( stream_t *s, void *p_read, unsigned int i_read )
stream_sys_t *p_sys = s->p_sys;
uint8_t *p_data = p_read;
uint8_t *p_record = p_data;
unsigned int i_data = 0;
/* It means EOF */
if( p_sys->block.p_current == NULL )
return 0;
if( p_sys->record.b_active && !p_data )
p_record = p_data = malloc( i_read );
if( p_data == NULL )
{
/* seek within this stream if possible, else use plain old read and discard */
......@@ -918,14 +796,6 @@ static int AStreamReadBlock( stream_t *s, void *p_read, unsigned int i_read )
}
}
if( p_sys->record.b_active )
{
if( i_data > 0 && p_record != NULL)
ARecordWrite( s, p_record, i_data );
if( !p_read )
free( p_record );
}
p_sys->i_pos += i_data;
return i_data;
}
......@@ -1205,15 +1075,11 @@ static int AStreamReadStream( stream_t *s, void *p_read, unsigned int i_read )
stream_track_t *tk = &p_sys->stream.tk[p_sys->stream.i_tk];
uint8_t *p_data = (uint8_t *)p_read;
uint8_t *p_record = p_data;
unsigned int i_data = 0;
if( tk->i_start >= tk->i_end )
return 0; /* EOF */
if( p_sys->record.b_active && !p_data )
p_record = p_data = malloc( i_read );
if( p_data == NULL )
{
/* seek within this stream if possible, else use plain old read and discard */
......@@ -1277,14 +1143,6 @@ static int AStreamReadStream( stream_t *s, void *p_read, unsigned int i_read )
}
}
if( p_sys->record.b_active )
{
if( i_data > 0 && p_record != NULL)
ARecordWrite( s, p_record, i_data );
if( !p_read )
free( p_record );
}
return i_data;
}
......
......@@ -156,6 +156,7 @@ image_HandlerDelete
InitMD5
input_Control
__input_CreateThread
input_CreateFilename
input_DecoderDecode
input_DecoderDelete
input_DecoderNew
......
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