+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
+ *****************************************************************************/
+static int ControlReset( vlc_object_t *p_obj, int i_fd )
+{
+ struct v4l2_queryctrl queryctrl;
+ int i_cid;
+ memset( &queryctrl, 0, sizeof( queryctrl ) );
+
+ queryctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL;
+ if( v4l2_ioctl( i_fd, VIDIOC_QUERYCTRL, &queryctrl ) >= 0 )
+ {
+ /* Extended control API supported */
+ queryctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL;
+ while( v4l2_ioctl( i_fd, VIDIOC_QUERYCTRL, &queryctrl ) >= 0 )
+ {
+ if( queryctrl.type == V4L2_CTRL_TYPE_CTRL_CLASS
+ || V4L2_CTRL_ID2CLASS( queryctrl.id ) == V4L2_CTRL_CLASS_MPEG )
+ {
+ queryctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
+ continue;
+ }
+ struct v4l2_control control;
+ memset( &control, 0, sizeof( control ) );
+ control.id = queryctrl.id;
+ if( v4l2_ioctl( i_fd, VIDIOC_G_CTRL, &control ) >= 0
+ && queryctrl.default_value != control.value )
+ {
+ int i;
+ for( i = 0; controls[i].psz_name != NULL; i++ )
+ if( controls[i].i_cid == queryctrl.id ) break;
+ name2var( queryctrl.name );
+ Control( p_obj, i_fd,
+ controls[i].psz_name ? : (const char *)queryctrl.name,
+ queryctrl.id, queryctrl.default_value );
+ }
+ queryctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
+ }
+ }
+ else
+ {
+
+ /* public controls */
+ for( i_cid = V4L2_CID_BASE;
+ i_cid < V4L2_CID_LASTP1;
+ i_cid ++ )
+ {
+ queryctrl.id = i_cid;
+ if( v4l2_ioctl( i_fd, VIDIOC_QUERYCTRL, &queryctrl ) >= 0 )
+ {
+ struct v4l2_control control;
+ if( queryctrl.flags & V4L2_CTRL_FLAG_DISABLED )
+ continue;
+ memset( &control, 0, sizeof( control ) );
+ control.id = queryctrl.id;
+ if( v4l2_ioctl( i_fd, VIDIOC_G_CTRL, &control ) >= 0
+ && queryctrl.default_value != control.value )
+ {
+ int i;
+ for( i = 0; controls[i].psz_name != NULL; i++ )
+ if( controls[i].i_cid == queryctrl.id ) break;
+ name2var( queryctrl.name );
+ Control( p_obj, i_fd,
+ controls[i].psz_name ? : (const char *)queryctrl.name,
+ queryctrl.id, queryctrl.default_value );
+ }
+ }
+ }
+
+ /* private controls */
+ for( i_cid = V4L2_CID_PRIVATE_BASE;
+ ;
+ i_cid ++ )
+ {
+ queryctrl.id = i_cid;
+ if( v4l2_ioctl( i_fd, VIDIOC_QUERYCTRL, &queryctrl ) >= 0 )
+ {
+ struct v4l2_control control;
+ if( queryctrl.flags & V4L2_CTRL_FLAG_DISABLED )
+ continue;
+ memset( &control, 0, sizeof( control ) );
+ control.id = queryctrl.id;
+ if( v4l2_ioctl( i_fd, VIDIOC_G_CTRL, &control ) >= 0
+ && queryctrl.default_value != control.value )
+ {
+ name2var( queryctrl.name );
+ Control( p_obj, i_fd, (const char *)queryctrl.name,
+ queryctrl.id, queryctrl.default_value );
+ }
+ }
+ else
+ break;
+ }
+ }
+ return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * Issue user-class v4l2 controls
+ *****************************************************************************/
+static int Control( vlc_object_t *p_obj, int i_fd,
+ const char *psz_name, int i_cid, int i_value )
+{
+ struct v4l2_queryctrl queryctrl;
+ struct v4l2_control control;
+ struct v4l2_ext_control ext_control;
+ struct v4l2_ext_controls ext_controls;
+
+ if( i_value == -1 )
+ return VLC_SUCCESS;
+
+ memset( &queryctrl, 0, sizeof( queryctrl ) );
+
+ queryctrl.id = i_cid;
+
+ if( v4l2_ioctl( i_fd, VIDIOC_QUERYCTRL, &queryctrl ) < 0
+ || queryctrl.flags & V4L2_CTRL_FLAG_DISABLED )
+ {
+ msg_Dbg( p_obj, "%s (%x) control is not supported.", psz_name, i_cid );
+ return VLC_EGENERIC;
+ }
+
+ memset( &control, 0, sizeof( control ) );
+ memset( &ext_control, 0, sizeof( ext_control ) );
+ memset( &ext_controls, 0, sizeof( ext_controls ) );
+ control.id = i_cid;
+ ext_control.id = i_cid;
+ ext_controls.ctrl_class = V4L2_CTRL_ID2CLASS( i_cid );
+ ext_controls.count = 1;
+ ext_controls.controls = &ext_control;
+
+ int i_ret = -1;
+
+ if( i_value >= 0 )
+ {
+ ext_control.value = i_value;
+ if( v4l2_ioctl( i_fd, VIDIOC_S_EXT_CTRLS, &ext_controls ) < 0 )
+ {
+ control.value = i_value;
+ if( v4l2_ioctl( i_fd, VIDIOC_S_CTRL, &control ) < 0 )
+ {
+ msg_Err( p_obj, "unable to set %s (%x) to %d (%m)",
+ psz_name, i_cid, i_value );
+ return VLC_EGENERIC;
+ }
+ i_ret = v4l2_ioctl( i_fd, VIDIOC_G_CTRL, &control );
+ }
+ else
+ {
+ i_ret = v4l2_ioctl( i_fd, VIDIOC_G_EXT_CTRLS, &ext_controls );
+ control.value = ext_control.value;
+ }
+ }
+
+ if( i_ret >= 0 )
+ {
+ vlc_value_t val;
+ msg_Dbg( p_obj, "video %s: %d", psz_name, control.value );
+ switch( var_Type( p_obj, psz_name ) & VLC_VAR_TYPE )
+ {
+ case VLC_VAR_BOOL:
+ val.b_bool = control.value;
+ var_Change( p_obj, psz_name, VLC_VAR_SETVALUE, &val, NULL );
+ var_SetVoid( p_obj, "controls-update" );
+ break;
+ case VLC_VAR_INTEGER:
+ val.i_int = control.value;
+ var_Change( p_obj, psz_name, VLC_VAR_SETVALUE, &val, NULL );
+ var_SetVoid( p_obj, "controls-update" );
+ break;
+ }
+ }
+ return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * On the fly change settings callback
+ *****************************************************************************/
+static int DemuxControlCallback( vlc_object_t *p_this,
+ const char *psz_var, vlc_value_t oldval, vlc_value_t newval,
+ void *p_data )
+{
+ (void)oldval;
+ demux_t *p_demux = (demux_t*)p_this;
+ demux_sys_t *p_sys = p_demux->p_sys;
+ int i_cid = (long int)p_data;
+
+ int i_fd = p_sys->i_fd;
+
+ if( i_fd < 0 )
+ return VLC_EGENERIC;
+
+ Control( p_this, i_fd, psz_var, i_cid, newval.i_int );
+
+ return VLC_EGENERIC;
+}
+
+static int DemuxControlResetCallback( vlc_object_t *p_this,
+ const char *psz_var, vlc_value_t oldval, vlc_value_t newval, void *p_data )
+{
+ (void)psz_var; (void)oldval; (void)newval; (void)p_data;
+ demux_t *p_demux = (demux_t*)p_this;
+ demux_sys_t *p_sys = p_demux->p_sys;
+
+ int i_fd = p_sys->i_fd;
+
+ if( i_fd < 0 )
+ return VLC_EGENERIC;
+
+ ControlReset( p_this, i_fd );
+
+ return VLC_EGENERIC;
+}
+
+static int AccessControlCallback( vlc_object_t *p_this,
+ const char *psz_var, vlc_value_t oldval, vlc_value_t newval,
+ void *p_data )
+{
+ (void)oldval;
+ access_t *p_access = (access_t *)p_this;
+ demux_sys_t *p_sys = (demux_sys_t *) p_access->p_sys;
+ int i_cid = (long int)p_data;
+
+ int i_fd = p_sys->i_fd;
+
+ if( i_fd < 0 )
+ return VLC_EGENERIC;
+
+ Control( p_this, i_fd, psz_var, i_cid, newval.i_int );
+
+ return VLC_EGENERIC;
+}
+
+static int AccessControlResetCallback( vlc_object_t *p_this,
+ const char *psz_var, vlc_value_t oldval, vlc_value_t newval, void *p_data )
+{
+ (void)psz_var; (void)oldval; (void)newval; (void)p_data;
+ access_t *p_access = (access_t *)p_this;
+ demux_sys_t *p_sys = (demux_sys_t *) p_access->p_sys;
+
+ int i_fd = p_sys->i_fd;
+
+ if( i_fd < 0 )
+ return VLC_EGENERIC;
+
+ ControlReset( p_this, i_fd );
+
+ return VLC_EGENERIC;
+}