Commit f9d6ca04 authored by Antoine Cellerier's avatar Antoine Cellerier

[PATCH] v4l2 mpeg2 controls by Dennis Lou. Thanks.

parent 4a48618f
...@@ -36,7 +36,6 @@ ...@@ -36,7 +36,6 @@
/* /*
* TODO: Tuner partial implementation. * TODO: Tuner partial implementation.
* TODO: Add more MPEG stream params
*/ */
/***************************************************************************** /*****************************************************************************
...@@ -49,6 +48,7 @@ ...@@ -49,6 +48,7 @@
#include <vlc_input.h> #include <vlc_input.h>
#include <vlc_vout.h> #include <vlc_vout.h>
#include <ctype.h>
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
...@@ -183,6 +183,15 @@ static void AccessClose( vlc_object_t * ); ...@@ -183,6 +183,15 @@ static void AccessClose( vlc_object_t * );
"Audio method to use: 0 to disable audio, 1 for OSS, 2 for ALSA, " \ "Audio method to use: 0 to disable audio, 1 for OSS, 2 for ALSA, " \
"3 for ALSA or OSS (ALSA is prefered)." ) "3 for ALSA or OSS (ALSA is prefered)." )
#endif #endif
#define AUDIO_VOLUME_TEXT N_( "Volume" )
#define AUDIO_VOLUME_LONGTEXT N_( \
"Volume of the audio input (if supported by v4l2 driver)." )
#define AUDIO_BALANCE_TEXT N_( "Balance" )
#define AUDIO_BALANCE_LONGTEXT N_( \
"Balance of the audio input (if supported by v4l2 driver)." )
#define AUDIO_MUTE_TEXT N_( "Mute" )
#define AUDIO_MUTE_LONGTEXT N_( \
"Mute audio input (if supported by v4l2 driver)." )
#define STEREO_TEXT N_( "Stereo" ) #define STEREO_TEXT N_( "Stereo" )
#define STEREO_LONGTEXT N_( \ #define STEREO_LONGTEXT N_( \
"Capture the audio stream in stereo." ) "Capture the audio stream in stereo." )
...@@ -194,6 +203,13 @@ static void AccessClose( vlc_object_t * ); ...@@ -194,6 +203,13 @@ static void AccessClose( vlc_object_t * );
#define CACHING_LONGTEXT N_( \ #define CACHING_LONGTEXT N_( \
"Caching value for V4L2 captures. This " \ "Caching value for V4L2 captures. This " \
"value should be set in milliseconds." ) "value should be set in milliseconds." )
#define S_CTRLS_TEXT N_("v4l2 driver controls")
#define S_CTRLS_LONGTEXT N_( \
"Set the v4l2 driver controls to the values specified using a comma " \
"separated list optionally encapsulated by curly braces " \
"(e.g.: {video_bitrate=6000000,audio_crc=0,stream_type=3} ). " \
"To list available controls, increase verbosity (-vvv) " \
"or use the v4l2-ctl application." )
typedef enum { typedef enum {
IO_METHOD_READ, IO_METHOD_READ,
...@@ -295,6 +311,14 @@ vlc_module_begin(); ...@@ -295,6 +311,14 @@ vlc_module_begin();
HCENTER_LONGTEXT, VLC_TRUE ); HCENTER_LONGTEXT, VLC_TRUE );
add_integer( CFG_PREFIX "vcenter", -1, NULL, VCENTER_TEXT, add_integer( CFG_PREFIX "vcenter", -1, NULL, VCENTER_TEXT,
VCENTER_LONGTEXT, VLC_TRUE ); VCENTER_LONGTEXT, VLC_TRUE );
add_integer( CFG_PREFIX "audio-volume", -1, NULL, AUDIO_VOLUME_TEXT,
AUDIO_VOLUME_LONGTEXT, VLC_TRUE );
add_integer( CFG_PREFIX "audio-balance", -1, NULL, AUDIO_BALANCE_TEXT,
AUDIO_BALANCE_LONGTEXT, VLC_TRUE );
add_bool( CFG_PREFIX "audio-mute", VLC_FALSE, NULL, AUDIO_MUTE_TEXT,
AUDIO_MUTE_LONGTEXT, VLC_TRUE );
add_string( CFG_PREFIX "set-ctrls", NULL, NULL, S_CTRLS_TEXT,
S_CTRLS_LONGTEXT, VLC_TRUE );
add_shortcut( "v4l2" ); add_shortcut( "v4l2" );
set_capability( "access_demux", 10 ); set_capability( "access_demux", 10 );
...@@ -315,6 +339,7 @@ vlc_module_end(); ...@@ -315,6 +339,7 @@ vlc_module_end();
static void CommonClose( vlc_object_t *, demux_sys_t * ); static void CommonClose( vlc_object_t *, demux_sys_t * );
static void ParseMRL( demux_sys_t *, char *, vlc_object_t * ); static void ParseMRL( demux_sys_t *, char *, vlc_object_t * );
static void GetV4L2Params( demux_sys_t *, vlc_object_t * ); static void GetV4L2Params( demux_sys_t *, vlc_object_t * );
static void SetAvailControlsByString( vlc_object_t *, demux_sys_t *, int );
static int DemuxControl( demux_t *, int, va_list ); static int DemuxControl( demux_t *, int, va_list );
static int AccessControl( access_t *, int, va_list ); static int AccessControl( access_t *, int, va_list );
...@@ -503,6 +528,8 @@ struct demux_sys_t ...@@ -503,6 +528,8 @@ struct demux_sys_t
size_t i_alsa_frame_size; size_t i_alsa_frame_size;
int i_alsa_chunk_size; int i_alsa_chunk_size;
#endif #endif
char *psz_set_ctrls;
}; };
static int FindMainDevice( vlc_object_t *p_this, demux_sys_t *p_sys, static int FindMainDevice( vlc_object_t *p_this, demux_sys_t *p_sys,
...@@ -675,6 +702,8 @@ static void GetV4L2Params( demux_sys_t *p_sys, vlc_object_t *p_obj ) ...@@ -675,6 +702,8 @@ static void GetV4L2Params( demux_sys_t *p_sys, vlc_object_t *p_obj )
p_sys->i_pts = var_CreateGetInteger( p_obj, "v4l2-caching" ); p_sys->i_pts = var_CreateGetInteger( p_obj, "v4l2-caching" );
p_sys->psz_set_ctrls = var_CreateGetString( p_obj, "v4l2-set-ctrls" );
p_sys->psz_device = p_sys->psz_vdev = p_sys->psz_adev = NULL; p_sys->psz_device = p_sys->psz_vdev = p_sys->psz_adev = NULL;
p_sys->i_fd_video = -1; p_sys->i_fd_video = -1;
p_sys->i_fd_audio = -1; p_sys->i_fd_audio = -1;
...@@ -891,9 +920,33 @@ static void ParseMRL( demux_sys_t *p_sys, char *psz_path, vlc_object_t *p_obj ) ...@@ -891,9 +920,33 @@ static void ParseMRL( demux_sys_t *p_sys, char *psz_path, vlc_object_t *p_obj )
p_sys->i_pts = strtol( psz_parser + strlen( "caching=" ), p_sys->i_pts = strtol( psz_parser + strlen( "caching=" ),
&psz_parser, 0 ); &psz_parser, 0 );
} }
else if( !strncmp( psz_parser, "set-ctrls=", strlen( "set-ctrls=" )) )
{
int i_len;
psz_parser += strlen( "set-ctrls=" );
if( strchr( psz_parser, ':' ) )
{
i_len = strchr( psz_parser, ':' ) - psz_parser;
}
else else
{ {
msg_Warn( p_obj, "unknown option" ); i_len = strlen( psz_parser );
}
p_sys->psz_set_ctrls = strndup( psz_parser, i_len );
psz_parser += i_len;
}
else
{
char *psz_unk = strchr( psz_parser, ':' );
if (psz_unk)
psz_unk = strndup( psz_parser, psz_unk - psz_parser );
else
psz_unk = strdup( psz_parser);
msg_Warn( p_obj, "unknown option %s", psz_unk );
free (psz_unk);
} }
while( *psz_parser && *psz_parser != ':' ) while( *psz_parser && *psz_parser != ':' )
...@@ -1019,6 +1072,7 @@ static void CommonClose( vlc_object_t *p_this, demux_sys_t *p_sys ) ...@@ -1019,6 +1072,7 @@ static void CommonClose( vlc_object_t *p_this, demux_sys_t *p_sys )
free( p_sys->p_tuners ); free( p_sys->p_tuners );
free( p_sys->p_codecs ); free( p_sys->p_codecs );
free( p_sys->psz_requested_chroma ); free( p_sys->psz_requested_chroma );
free( p_sys->psz_set_ctrls );
free( p_sys ); free( p_sys );
} }
...@@ -1722,6 +1776,7 @@ static int OpenVideoDev( vlc_object_t *p_obj, demux_sys_t *p_sys, vlc_bool_t b_d ...@@ -1722,6 +1776,7 @@ static int OpenVideoDev( vlc_object_t *p_obj, demux_sys_t *p_sys, vlc_bool_t b_d
/* if MPEG encoder card, no need to do anything else after this */ /* if MPEG encoder card, no need to do anything else after this */
ControlList( p_obj, i_fd, ControlList( p_obj, i_fd,
var_GetBool( p_obj, "v4l2-controls-reset" ), b_demux ); var_GetBool( p_obj, "v4l2-controls-reset" ), b_demux );
SetAvailControlsByString( p_obj, p_sys, i_fd );
if( VLC_FALSE == b_demux) if( VLC_FALSE == b_demux)
{ {
return i_fd; return i_fd;
...@@ -2717,6 +2772,12 @@ static vlc_bool_t ProbeAudioDev( vlc_object_t *p_this, demux_sys_t *p_sys, ...@@ -2717,6 +2772,12 @@ static vlc_bool_t ProbeAudioDev( vlc_object_t *p_this, demux_sys_t *p_sys,
return VLC_FALSE; return VLC_FALSE;
} }
static void name2var( unsigned char *name )
{
for( ; *name; name++ )
*name = (*name == ' ') ? '_' : tolower( *name );
}
/***************************************************************************** /*****************************************************************************
* Print a user-class v4l2 control's details, create the relevant variable, * Print a user-class v4l2 control's details, create the relevant variable,
* change the value if needed. * change the value if needed.
...@@ -2752,12 +2813,8 @@ static void ControlListPrint( vlc_object_t *p_obj, int i_fd, ...@@ -2752,12 +2813,8 @@ static void ControlListPrint( vlc_object_t *p_obj, int i_fd,
} }
else else
{ {
char *psz_buf;
psz_name = strdup( (const char *)queryctrl.name ); psz_name = strdup( (const char *)queryctrl.name );
for( psz_buf = psz_name; *psz_buf; psz_buf++ ) name2var( (unsigned char *)psz_name );
{
if( *psz_buf == ' ' ) *psz_buf = '-';
}
i_val = -1; i_val = -1;
} }
...@@ -2863,6 +2920,10 @@ static void ControlListPrint( vlc_object_t *p_obj, int i_fd, ...@@ -2863,6 +2920,10 @@ static void ControlListPrint( vlc_object_t *p_obj, int i_fd,
var_Change( p_obj, psz_name, VLC_VAR_SETTEXT, &val, NULL ); var_Change( p_obj, psz_name, VLC_VAR_SETTEXT, &val, NULL );
val.i_int = queryctrl.id; val.i_int = queryctrl.id;
val2.psz_string = (char *)psz_name; val2.psz_string = (char *)psz_name;
var_Change( p_obj, "allcontrols", VLC_VAR_ADDCHOICE, &val, &val2 );
/* bad things happen changing MPEG mid-stream
* so don't add to Ext Settings GUI */
if( V4L2_CTRL_ID2CLASS( queryctrl.id ) != V4L2_CTRL_CLASS_MPEG )
var_Change( p_obj, "controls", VLC_VAR_ADDCHOICE, &val, &val2 ); var_Change( p_obj, "controls", VLC_VAR_ADDCHOICE, &val, &val2 );
switch( var_Type( p_obj, psz_name ) & VLC_VAR_TYPE ) switch( var_Type( p_obj, psz_name ) & VLC_VAR_TYPE )
...@@ -2904,9 +2965,12 @@ static int ControlList( vlc_object_t *p_obj, int i_fd, ...@@ -2904,9 +2965,12 @@ static int ControlList( vlc_object_t *p_obj, int i_fd,
memset( &queryctrl, 0, sizeof( queryctrl ) ); memset( &queryctrl, 0, sizeof( queryctrl ) );
/* A list of available controls (aka the variable name) will be /* A list of available controls (aka the variable name) will be
* stored as choices in the "controls" variable. We'll thus be able * stored as choices in the "allcontrols" variable. We'll thus be able
* to use those to create an appropriate interface */ * to use those to create an appropriate interface
* A list of available controls that can be changed mid-stream will
* be stored in the "controls" variable */
var_Create( p_obj, "controls", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE ); var_Create( p_obj, "controls", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
var_Create( p_obj, "allcontrols", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
var_Create( p_obj, "controls-update", VLC_VAR_VOID | VLC_VAR_ISCOMMAND ); var_Create( p_obj, "controls-update", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
...@@ -2932,6 +2996,12 @@ static int ControlList( vlc_object_t *p_obj, int i_fd, ...@@ -2932,6 +2996,12 @@ static int ControlList( vlc_object_t *p_obj, int i_fd,
queryctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL; queryctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL;
while( ioctl( i_fd, VIDIOC_QUERYCTRL, &queryctrl ) >= 0 ) while( ioctl( i_fd, VIDIOC_QUERYCTRL, &queryctrl ) >= 0 )
{ {
if( queryctrl.type == V4L2_CTRL_TYPE_CTRL_CLASS )
{
msg_Dbg( p_obj, "%s", queryctrl.name );
queryctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
continue;
}
switch( V4L2_CTRL_ID2CLASS( queryctrl.id ) ) switch( V4L2_CTRL_ID2CLASS( queryctrl.id ) )
{ {
case V4L2_CTRL_CLASS_USER: case V4L2_CTRL_CLASS_USER:
...@@ -2939,6 +3009,7 @@ static int ControlList( vlc_object_t *p_obj, int i_fd, ...@@ -2939,6 +3009,7 @@ static int ControlList( vlc_object_t *p_obj, int i_fd,
queryctrl.name, queryctrl.id ); queryctrl.name, queryctrl.id );
break; break;
case V4L2_CTRL_CLASS_MPEG: case V4L2_CTRL_CLASS_MPEG:
name2var( queryctrl.name );
msg_Dbg( p_obj, "Available MPEG control: %s (%x)", msg_Dbg( p_obj, "Available MPEG control: %s (%x)",
queryctrl.name, queryctrl.id ); queryctrl.name, queryctrl.id );
break; break;
...@@ -2993,6 +3064,79 @@ static int ControlList( vlc_object_t *p_obj, int i_fd, ...@@ -2993,6 +3064,79 @@ static int ControlList( vlc_object_t *p_obj, int i_fd,
return VLC_SUCCESS; return VLC_SUCCESS;
} }
static void SetAvailControlsByString( vlc_object_t *p_obj, demux_sys_t *p_sys,
int i_fd )
{
char *psz_parser = p_sys->psz_set_ctrls;
vlc_value_t val, text, name;
if( psz_parser == NULL )
return;
if( *psz_parser == '{' )
psz_parser++;
int i_ret = var_Change( p_obj, "allcontrols", VLC_VAR_GETCHOICES,
&val, &text );
if( i_ret < 0 )
{
msg_Err( p_obj, "Oops, can't find 'allcontrols' variable." );
return;
}
while( *psz_parser && *psz_parser != '}' )
{
char *psz_delim, *psz_assign;
while( *psz_parser == ',' || *psz_parser == ' ' )
psz_parser++;
psz_delim = strchr( psz_parser, ',' );
if( psz_delim == NULL )
psz_delim = strchr( psz_parser, '}' );
if( psz_delim == NULL )
psz_delim = psz_parser + strlen( psz_parser );
psz_assign = memchr( psz_parser, '=', psz_delim - psz_parser );
if( psz_assign == NULL )
{
char *psz_name = strndup( psz_parser, psz_delim - psz_parser );
msg_Err( p_obj, "%s missing '='", psz_name );
free( psz_name );
psz_parser = psz_delim + 1;
continue;
}
for( int i = 0;
i < val.p_list->i_count ;//&& psz_parser < psz_assign;
i++ )
{
const char *psz_var = text.p_list->p_values[i].psz_string;
int i_cid = val.p_list->p_values[i].i_int;
var_Change( p_obj, psz_var, VLC_VAR_GETTEXT, &name, NULL );
const char *psz_name = name.psz_string;
int i_availstrlen = strlen( psz_name );
int i_parsestrlen = psz_assign - psz_parser;
int i_maxstrlen = __MAX( i_availstrlen, i_parsestrlen);
if( !strncasecmp( psz_name, psz_parser, i_maxstrlen ) )
{
Control( p_obj, i_fd, psz_name, i_cid,
strtol( ++psz_assign, &psz_parser, 0) );
}
}
if( psz_parser < psz_assign )
{
char *psz_name = strndup( psz_parser, psz_assign - psz_parser );
msg_Err( p_obj, "Control %s not available", psz_name );
free( psz_name );
psz_parser = ( *psz_delim ) ? ( psz_delim + 1 ) : psz_delim;
}
}
}
/***************************************************************************** /*****************************************************************************
* Reset all user-class v4l2 controls to their default value * Reset all user-class v4l2 controls to their default value
*****************************************************************************/ *****************************************************************************/
...@@ -3064,6 +3208,8 @@ static int Control( vlc_object_t *p_obj, int i_fd, ...@@ -3064,6 +3208,8 @@ static int Control( vlc_object_t *p_obj, int i_fd,
{ {
struct v4l2_queryctrl queryctrl; struct v4l2_queryctrl queryctrl;
struct v4l2_control control; struct v4l2_control control;
struct v4l2_ext_control ext_control;
struct v4l2_ext_controls ext_controls;
if( i_value == -1 ) if( i_value == -1 )
return VLC_SUCCESS; return VLC_SUCCESS;
...@@ -3075,25 +3221,54 @@ static int Control( vlc_object_t *p_obj, int i_fd, ...@@ -3075,25 +3221,54 @@ static int Control( vlc_object_t *p_obj, int i_fd,
if( ioctl( i_fd, VIDIOC_QUERYCTRL, &queryctrl ) < 0 if( ioctl( i_fd, VIDIOC_QUERYCTRL, &queryctrl ) < 0
|| queryctrl.flags & V4L2_CTRL_FLAG_DISABLED ) || queryctrl.flags & V4L2_CTRL_FLAG_DISABLED )
{ {
msg_Dbg( p_obj, "%s (%x) control is not supported.", psz_name, msg_Dbg( p_obj, "%s (%x) control is not supported.", psz_name, i_cid );
i_cid );
return VLC_EGENERIC; return VLC_EGENERIC;
} }
memset( &control, 0, sizeof( control ) ); memset( &control, 0, sizeof( control ) );
memset( &ext_control, 0, sizeof( ext_control ) );
memset( &ext_controls, 0, sizeof( ext_controls ) );
control.id = i_cid; control.id = i_cid;
ext_control.id = i_cid;
ext_controls.ctrl_class = V4L2_CTRL_CLASS_MPEG;
ext_controls.count = 1;
ext_controls.controls = &ext_control;
if( i_value >= 0 ) if( i_value >= 0 )
{
if( V4L2_CTRL_ID2CLASS( i_cid ) == V4L2_CTRL_CLASS_USER )
{ {
control.value = i_value; control.value = i_value;
if( ioctl( i_fd, VIDIOC_S_CTRL, &control ) < 0 ) if( ioctl( i_fd, VIDIOC_S_CTRL, &control ) < 0 )
{ {
msg_Err( p_obj, "unable to set %s to %d (%m)", psz_name, msg_Err( p_obj, "unable to set %s (%x) to %d (%m)",
i_value ); psz_name, i_cid, i_value );
return VLC_EGENERIC; return VLC_EGENERIC;
} }
} }
if( ioctl( i_fd, VIDIOC_G_CTRL, &control ) >= 0 ) else
{
ext_control.value = i_value;
if( ioctl( i_fd, VIDIOC_S_EXT_CTRLS, &ext_controls ) < 0 )
{
msg_Err( p_obj, "unable to set %s (%x) to %d (%m)",
psz_name, i_cid, i_value );
return VLC_EGENERIC;
}
}
}
int i_ret;
if( V4L2_CTRL_ID2CLASS( i_cid ) == V4L2_CTRL_CLASS_USER )
i_ret = ioctl( i_fd, VIDIOC_G_CTRL, &control );
else
{
i_ret = ioctl( i_fd, VIDIOC_G_EXT_CTRLS, &ext_controls );
control.value = ext_control.value;
}
if( i_ret >= 0 )
{ {
vlc_value_t val; vlc_value_t val;
msg_Dbg( p_obj, "video %s: %d", psz_name, control.value ); msg_Dbg( p_obj, "video %s: %d", psz_name, control.value );
......
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