+ p_sys->i_sample_rate = 44100;
+ p_sys->b_stereo = VLC_TRUE;
+
+ p_sys->psz_device = p_sys->psz_vdev = p_sys->psz_adev = NULL;
+ p_sys->fd_video = -1;
+ p_sys->fd_audio = -1;
+
+ p_sys->p_es_video = p_sys->p_es_audio = 0;
+ p_sys->p_block_audio = 0;
+
+ ParseMRL( p_demux );
+
+ /* Find main device (video or audio) */
+ if( p_sys->psz_device && *p_sys->psz_device )
+ {
+ msg_Dbg( p_demux, "main device=`%s'", p_sys->psz_device );
+
+ /* Try to open as video device */
+ p_sys->fd_video = OpenVideoDev( p_demux, p_sys->psz_device );
+
+ if( p_sys->fd_video < 0 )
+ {
+ /* Try to open as audio device */
+ p_sys->fd_audio = OpenAudioDev( p_demux, 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_demux->psz_access, "v4l" ) )
+ {
+ Close( 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_demux, "v4l-vdev" );;
+ }
+
+ if( p_sys->psz_vdev && *p_sys->psz_vdev )
+ {
+ p_sys->fd_video = OpenVideoDev( p_demux, 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_demux, "v4l-adev" );;
+ }
+
+ if( p_sys->psz_adev && *p_sys->psz_adev )
+ {
+ p_sys->fd_audio = OpenAudioDev( p_demux, p_sys->psz_adev );
+ }
+ }
+
+ if( p_sys->fd_video < 0 && p_sys->fd_audio < 0 )
+ {
+ Close( p_this );
+ return VLC_EGENERIC;
+ }
+
+ msg_Dbg( p_demux, "v4l grabbing started" );
+
+ /* Declare elementary streams */
+ if( p_sys->fd_video >= 0 )
+ {
+ es_format_t fmt;
+ es_format_Init( &fmt, VIDEO_ES, p_sys->i_fourcc );
+ fmt.video.i_width = p_sys->i_width;
+ fmt.video.i_height = p_sys->i_height;
+ fmt.video.i_aspect = 4 * VOUT_ASPECT_FACTOR / 3;
+
+ /* Setup rgb mask for RGB formats */
+ if( p_sys->i_fourcc == VLC_FOURCC('R','V','2','4') )
+ {
+ /* This is in BGR format */
+ fmt.video.i_bmask = 0x00ff0000;
+ fmt.video.i_gmask = 0x0000ff00;
+ fmt.video.i_rmask = 0x000000ff;
+ }
+
+ msg_Dbg( p_demux, "added new video es %4.4s %dx%d",
+ (char*)&fmt.i_codec, fmt.video.i_width, fmt.video.i_height );
+ p_sys->p_es_video = es_out_Add( p_demux->out, &fmt );
+ }
+
+ if( p_sys->fd_audio >= 0 )
+ {
+ es_format_t fmt;
+ es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC('a','r','a','w') );
+
+ fmt.audio.i_channels = p_sys->b_stereo ? 2 : 1;
+ fmt.audio.i_rate = p_sys->i_sample_rate;
+ fmt.audio.i_bitspersample = 16; // FIXME ?
+ fmt.audio.i_blockalign = fmt.audio.i_channels *
+ fmt.audio.i_bitspersample / 8;
+ fmt.i_bitrate = fmt.audio.i_channels * fmt.audio.i_rate *
+ fmt.audio.i_bitspersample;
+
+ msg_Dbg( p_demux, "new audio es %d channels %dHz",
+ fmt.audio.i_channels, fmt.audio.i_rate );
+
+ p_sys->p_es_audio = es_out_Add( p_demux->out, &fmt );
+ }
+
+ /* Update default_pts to a suitable value for access */
+ var_Create( p_demux, "v4l-caching", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
+
+ return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * Close: close device, free resources
+ *****************************************************************************/
+static void Close( vlc_object_t *p_this )
+{
+ demux_t *p_demux = (demux_t *)p_this;
+ demux_sys_t *p_sys = p_demux->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_block_audio ) block_Release( p_sys->p_block_audio );
+
+ 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 );
+}
+
+/*****************************************************************************
+ * Control:
+ *****************************************************************************/
+static int Control( demux_t *p_demux, int i_query, va_list args )
+{
+ vlc_bool_t *pb;
+ int64_t *pi64;
+
+ switch( i_query )
+ {
+ /* Special for access_demux */
+ case DEMUX_CAN_PAUSE:
+ case DEMUX_SET_PAUSE_STATE:
+ case DEMUX_CAN_CONTROL_PACE:
+ pb = (vlc_bool_t*)va_arg( args, vlc_bool_t * );
+ *pb = VLC_FALSE;
+ return VLC_SUCCESS;
+
+ case DEMUX_GET_PTS_DELAY:
+ pi64 = (int64_t*)va_arg( args, int64_t * );
+ *pi64 = (int64_t)var_GetInteger( p_demux, "v4l-caching" ) * 1000;
+ return VLC_SUCCESS;
+
+ case DEMUX_GET_TIME:
+ pi64 = (int64_t*)va_arg( args, int64_t * );
+ *pi64 = mdate();
+ return VLC_SUCCESS;
+
+ /* TODO implement others */
+ default:
+ return VLC_EGENERIC;
+ }
+
+ return VLC_EGENERIC;
+}
+
+/*****************************************************************************
+ * Demux:
+ *****************************************************************************/
+static int Demux( demux_t *p_demux )
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+ es_out_id_t *p_es = p_sys->p_es_audio;
+ block_t *p_block = NULL;
+
+ /* Try grabbing audio frames first */
+ if( p_sys->fd_audio < 0 || !( p_block = GrabAudio( p_demux ) ) )
+ {
+ /* Try grabbing video frame */
+ p_es = p_sys->p_es_video;
+ if( p_sys->fd_video > 0 ) p_block = GrabVideo( p_demux );
+ }
+
+ if( !p_block )
+ {
+ /* Sleep so we do not consume all the cpu, 10ms seems
+ * like a good value (100fps) */
+ msleep( 10000 );
+ return 1;
+ }
+
+ es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_block->i_pts );
+ es_out_Send( p_demux->out, p_es, p_block );