+ p_sys->psz_device = p_sys->psz_vdev = p_sys->psz_adev = NULL;
+ p_sys->fd_video = -1;
+ p_sys->fd_audio = -1;
+
+ ParseMRL( p_access );
+
+ /* Find main device (video or audio) */
+ if( p_sys->psz_device && *p_sys->psz_device )
+ {
+ msg_Dbg( p_access, "main device=`%s'", p_sys->psz_device );
+
+ /* Try to open as video device */
+ p_sys->fd_video = OpenVideoDev( p_access, p_sys->psz_device );
+
+ if( p_sys->fd_video < 0 )
+ {
+ /* Try to open as audio device */
+ p_sys->fd_audio = OpenAudioDev( p_access, p_sys->psz_device );
+ if( p_sys->fd_audio >= 0 )
+ {
+ if( p_sys->psz_adev ) free( p_sys->psz_adev );
+ p_sys->psz_adev = p_sys->psz_device;
+ p_sys->psz_device = NULL;
+ }
+ }
+ else
+ {
+ if( p_sys->psz_vdev ) free( p_sys->psz_vdev );
+ p_sys->psz_vdev = p_sys->psz_device;
+ p_sys->psz_device = NULL;
+ }
+ }
+
+ /* If no device opened, only continue if the access was forced */
+ if( p_sys->fd_video < 0 && p_sys->fd_audio < 0 )
+ {
+ if( strcmp( p_access->psz_access, "v4l" ) )
+ {
+ AccessClose( p_this );
+ return VLC_EGENERIC;
+ }
+ }
+
+ /* Find video device */
+ if( p_sys->fd_video < 0 )
+ {
+ if( !p_sys->psz_vdev || !*p_sys->psz_vdev )
+ {
+ if( p_sys->psz_vdev ) free( p_sys->psz_vdev );
+ p_sys->psz_vdev = var_CreateGetString( p_access, "v4l-vdev" );;
+ }
+
+ if( p_sys->psz_vdev && *p_sys->psz_vdev )
+ {
+ p_sys->fd_video = OpenVideoDev( p_access, p_sys->psz_vdev );
+ }
+ }
+
+ /* Find audio device */
+ if( p_sys->fd_audio < 0 )
+ {
+ if( !p_sys->psz_adev || !*p_sys->psz_adev )
+ {
+ if( p_sys->psz_adev ) free( p_sys->psz_adev );
+ p_sys->psz_adev = var_CreateGetString( p_access, "v4l-adev" );;
+ }
+
+ if( p_sys->psz_adev && *p_sys->psz_adev )
+ {
+ p_sys->fd_audio = OpenAudioDev( p_access, p_sys->psz_adev );
+ }
+ }
+
+ if( p_sys->fd_video < 0 && p_sys->fd_audio < 0 )
+ {
+ AccessClose( p_this );
+ return VLC_EGENERIC;
+ }
+
+ msg_Dbg( p_access, "v4l grabbing started" );
+
+ /* create header */
+ p_sys->i_streams = 0;
+ p_sys->i_header_size = 8;
+ p_sys->i_header_pos = 8;
+ p_sys->p_header = malloc( p_sys->i_header_size );
+
+ memcpy( p_sys->p_header, ".v4l", 4 );
+
+ if( p_sys->fd_video >= 0 )
+ {
+ p_sys->i_header_size += 20;
+ p_sys->p_header = realloc( p_sys->p_header, p_sys->i_header_size );
+
+ SetDWBE( &p_sys->p_header[4], ++p_sys->i_streams );
+
+ memcpy( &p_sys->p_header[p_sys->i_header_pos], "vids", 4 );
+ memcpy( &p_sys->p_header[p_sys->i_header_pos+4],
+ &p_sys->i_fourcc, 4 );
+ SetDWBE( &p_sys->p_header[p_sys->i_header_pos+8], p_sys->i_width );
+ SetDWBE( &p_sys->p_header[p_sys->i_header_pos+12], p_sys->i_height );
+ SetDWBE( &p_sys->p_header[p_sys->i_header_pos+16], 0 );
+
+ p_sys->i_header_pos = p_sys->i_header_size;
+ }
+
+ if( p_sys->fd_audio >= 0 )
+ {
+ p_sys->i_header_size += 20;
+ p_sys->p_header = realloc( p_sys->p_header, p_sys->i_header_size );
+
+ SetDWBE( &p_sys->p_header[4], ++p_sys->i_streams );
+
+ memcpy( &p_sys->p_header[p_sys->i_header_pos], "auds", 4 );
+ memcpy( &p_sys->p_header[p_sys->i_header_pos+4], "araw", 4 );
+ SetDWBE( &p_sys->p_header[p_sys->i_header_pos+8],
+ p_sys->b_stereo ? 2 : 1 );
+ SetDWBE( &p_sys->p_header[p_sys->i_header_pos+12],
+ p_sys->i_sample_rate );
+ SetDWBE( &p_sys->p_header[p_sys->i_header_pos+16], 16 );
+
+ p_sys->i_header_pos = p_sys->i_header_size;
+ }
+ p_sys->i_header_pos = 0;
+
+ /* Update default_pts to a suitable value for access */
+ var_Create( p_access, "v4l-caching", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
+
+ /* Force v4l demuxer */
+ p_access->psz_demux = strdup( "v4l" );
+
+ return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * AccessClose: close device, free resources
+ *****************************************************************************/
+static void AccessClose( vlc_object_t *p_this )
+{
+ access_t *p_access = (access_t *)p_this;
+ access_sys_t *p_sys = p_access->p_sys;
+
+ if( p_sys->psz_device ) free( p_sys->psz_device );
+ if( p_sys->psz_vdev ) free( p_sys->psz_vdev );
+ if( p_sys->psz_adev ) free( p_sys->psz_adev );
+ if( p_sys->fd_video >= 0 ) close( p_sys->fd_video );
+ if( p_sys->fd_audio >= 0 ) close( p_sys->fd_audio );
+
+ if( p_sys->p_header ) free( p_sys->p_header );
+ if( p_sys->p_audio_frame ) free( p_sys->p_audio_frame );
+
+ if( p_sys->b_mjpeg )
+ {
+ int i_noframe = -1;
+ ioctl( p_sys->fd_video, MJPIOC_QBUF_CAPT, &i_noframe );
+ }
+
+ if( p_sys->p_video_mmap && p_sys->p_video_mmap != MAP_FAILED )
+ {
+ if( p_sys->b_mjpeg )
+ munmap( p_sys->p_video_mmap, p_sys->mjpeg_buffers.size *
+ p_sys->mjpeg_buffers.count );
+ else
+ munmap( p_sys->p_video_mmap, p_sys->vid_mbuf.size );
+ }
+
+ free( p_sys );
+}
+
+/*****************************************************************************
+ * AccessControl:
+ *****************************************************************************/
+static int AccessControl( access_t *p_access, int i_query, va_list args )
+{
+ vlc_bool_t *pb_bool;
+ int *pi_int;
+ int64_t *pi_64;
+
+ switch( i_query )
+ {
+ /* */
+ case ACCESS_CAN_SEEK:
+ case ACCESS_CAN_FASTSEEK:
+ case ACCESS_CAN_PAUSE:
+ case ACCESS_CAN_CONTROL_PACE:
+ pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
+ *pb_bool = VLC_FALSE;
+ break;
+
+ /* */
+ case ACCESS_GET_MTU:
+ pi_int = (int*)va_arg( args, int * );
+ *pi_int = 0;
+ break;
+
+ case ACCESS_GET_PTS_DELAY:
+ pi_64 = (int64_t*)va_arg( args, int64_t * );
+ *pi_64 = (int64_t)var_GetInteger( p_access, "v4l-caching" ) * I64C(1000);
+ break;
+
+ /* */
+ case ACCESS_SET_PAUSE_STATE:
+ case ACCESS_GET_TITLE_INFO:
+ case ACCESS_SET_TITLE:
+ case ACCESS_SET_SEEKPOINT:
+ return VLC_EGENERIC;
+
+ default:
+ msg_Err( p_access, "unimplemented query in control" );
+ return VLC_EGENERIC;
+
+ }
+ return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * AccessRead:
+ *****************************************************************************/
+static int AccessRead( access_t *p_access, uint8_t *p_buffer, int i_len )
+{
+ access_sys_t *p_sys = p_access->p_sys;
+ int i_data = 0;
+ int i_stream;
+ mtime_t i_pts;
+
+ while( i_len > 0 )
+ {
+ /* First copy header if any */
+ if( p_sys->i_header_pos < p_sys->i_header_size )
+ {
+ int i_copy;
+
+ i_copy = __MIN( p_sys->i_header_size - p_sys->i_header_pos,
+ (int)i_len );
+ memcpy( p_buffer, &p_sys->p_header[p_sys->i_header_pos], i_copy );
+ p_sys->i_header_pos += i_copy;
+
+ p_buffer += i_copy;
+ i_len -= i_copy;
+ i_data += i_copy;
+ }
+
+ /* then data */
+ if( i_len > 0 && p_sys->i_data_pos < p_sys->i_data_size )
+ {
+ int i_copy;
+
+ i_copy = __MIN( p_sys->i_data_size - p_sys->i_data_pos,
+ (int)i_len );
+
+ memcpy( p_buffer, &p_sys->p_data[p_sys->i_data_pos], i_copy );
+ p_sys->i_data_pos += i_copy;
+
+ p_buffer += i_copy;
+ i_len -= i_copy;
+ i_data += i_copy;
+ }
+
+ /* The caller got what he wanted */
+ if( i_len == 0 )
+ {
+ return i_data;
+ }
+
+ /* Read no more than one frame at a time.
+ * That kills latency, especially for encoded v4l streams */
+ if( p_sys->i_data_size && p_sys->i_data_pos == p_sys->i_data_size )
+ {
+ p_sys->i_data_pos = 0; p_sys->i_data_size = 0;
+ return i_data;
+ }
+
+ /* Re-fill data by grabbing audio/video */
+ p_sys->i_data_pos = p_sys->i_data_size = 0;
+
+ /* Try grabbing audio frames first */
+ i_stream = p_sys->i_streams - 1;
+ if( p_sys->fd_audio < 0 ||
+ GrabAudio( p_access, &p_sys->p_data,
+ &p_sys->i_data_size, &i_pts ) != VLC_SUCCESS )
+ {
+ int i_ret = VLC_ETIMEOUT;
+
+ /* Try grabbing video frame */
+ i_stream = 0;
+ if( p_sys->fd_video > 0 )
+ {
+ i_ret = GrabVideo( p_access, &p_sys->p_data,
+ &p_sys->i_data_size, &i_pts );
+ }
+
+ /* No video or timeout */
+ if( i_ret == VLC_ETIMEOUT )
+ {
+ /* Sleep so we do not consume all the cpu, 10ms seems
+ * like a good value (100fps) */
+ msleep( 10000 );
+ continue;
+ }
+ else if( i_ret != VLC_SUCCESS )
+ {
+ msg_Err( p_access, "Error during capture!" );
+ return -1;
+ }
+ }
+
+ /* create pseudo header */
+ p_sys->i_header_size = 16;
+ p_sys->i_header_pos = 0;
+ SetDWBE( &p_sys->p_header[0], i_stream );
+ SetDWBE( &p_sys->p_header[4], p_sys->i_data_size );
+ SetQWBE( &p_sys->p_header[8], i_pts );
+ }
+
+ return i_data;
+}
+
+/*****************************************************************************
+ * ParseMRL: parse the options contained in the MRL
+ *****************************************************************************/
+static void ParseMRL( access_t *p_access )
+{
+ access_sys_t *p_sys = p_access->p_sys;
+
+ char *psz_dup = strdup( p_access->psz_path );
+ char *psz_parser = psz_dup;