* v4l.c : Video4Linux input module for vlc
*****************************************************************************
* Copyright (C) 2002 VideoLAN
- * $Id: v4l.c,v 1.30 2003/11/15 23:21:35 fenrir Exp $
+ * $Id: v4l.c,v 1.36 2003/12/04 16:49:43 sam Exp $
*
* Author: Laurent Aimar <fenrir@via.ecp.fr>
* Paul Forgey <paulf at aphrodite dot com>
#include <sys/soundcard.h>
/*****************************************************************************
- * Local prototypes
+ * Module descriptior
*****************************************************************************/
-static int AccessOpen ( vlc_object_t * );
-static void AccessClose ( vlc_object_t * );
-static int Read ( input_thread_t *, byte_t *, size_t );
-
-static void ParseMRL ( input_thread_t * );
-static int OpenVideoDev( input_thread_t *, char * );
-static int OpenAudioDev( input_thread_t *, char * );
+static int AccessOpen ( vlc_object_t * );
+static void AccessClose( vlc_object_t * );
static int DemuxOpen ( vlc_object_t * );
static void DemuxClose ( vlc_object_t * );
-static int Demux ( input_thread_t * );
-/*****************************************************************************
- * Module descriptior
- *****************************************************************************/
#define CACHING_TEXT N_("Caching value in ms")
#define CACHING_LONGTEXT N_( \
"Allows you to modify the default caching value for v4l streams. This " \
#define ADEV_TEXT N_("Audio device name")
#define ADEV_LONGTEXT N_( \
"Specify the name of the audio device that will be used. " \
- "If you don't specify anything, no video device will be used.")
+ "If you don't specify anything, no audio device will be used.")
+#define CHROMA_TEXT N_("Video input chroma format")
+#define CHROMA_LONGTEXT N_( \
+ "Force the Video4Linux video device to use a specific chroma format " \
+ "(eg. I420 (default), RV24, etc.)")
vlc_module_begin();
set_description( _("Video4Linux input") );
VLC_FALSE );
add_string( "v4l-adev", "/dev/dsp", 0, ADEV_TEXT, ADEV_LONGTEXT,
VLC_FALSE );
+ add_string( "v4l-chroma", NULL, NULL, CHROMA_TEXT, CHROMA_LONGTEXT,
+ VLC_TRUE );
add_submodule();
set_description( _("Video4Linux demuxer") );
vlc_module_end();
-/****************************************************************************
- * I. Access Part
- ****************************************************************************/
+/*****************************************************************************
+ * Access: local prototypes
+ *****************************************************************************/
+static int Read ( input_thread_t *, byte_t *, size_t );
+
+static void ParseMRL ( input_thread_t * );
+static int OpenVideoDev( input_thread_t *, char * );
+static int OpenAudioDev( input_thread_t *, char * );
+
#define MJPEG_BUFFER_SIZE (256*1024)
struct quicktime_mjpeg_app1
uint32_t i_data_offset; /* following SOS marker data */
};
+static struct
+{
+ int i_v4l;
+ int i_fourcc;
+
+} v4lchroma_to_fourcc[] =
+{
+ { VIDEO_PALETTE_GREY, VLC_FOURCC( 'G', 'R', 'E', 'Y' ) },
+ { VIDEO_PALETTE_HI240, VLC_FOURCC( 'I', '2', '4', '0' ) },
+ { VIDEO_PALETTE_RGB565, VLC_FOURCC( 'R', 'V', '1', '6' ) },
+ { VIDEO_PALETTE_RGB555, VLC_FOURCC( 'R', 'V', '1', '5' ) },
+ { VIDEO_PALETTE_RGB24, VLC_FOURCC( 'R', 'V', '2', '4' ) },
+ { VIDEO_PALETTE_RGB32, VLC_FOURCC( 'R', 'V', '3', '2' ) },
+ { VIDEO_PALETTE_YUV422, VLC_FOURCC( 'I', '4', '2', '2' ) },
+ { VIDEO_PALETTE_YUYV, VLC_FOURCC( 'Y', 'U', 'Y', 'V' ) },
+ { VIDEO_PALETTE_UYVY, VLC_FOURCC( 'U', 'Y', 'V', 'Y' ) },
+ { VIDEO_PALETTE_YUV420, VLC_FOURCC( 'I', '4', '2', 'N' ) },
+ { VIDEO_PALETTE_YUV411, VLC_FOURCC( 'I', '4', '1', 'N' ) },
+ { VIDEO_PALETTE_RAW, VLC_FOURCC( 'G', 'R', 'A', 'W' ) },
+ { VIDEO_PALETTE_YUV422P, VLC_FOURCC( 'I', '4', '2', '2' ) },
+ { VIDEO_PALETTE_YUV420P, VLC_FOURCC( 'I', '4', '2', '0' ) },
+ { VIDEO_PALETTE_YUV411P, VLC_FOURCC( 'I', '4', '1', '1' ) },
+ { 0, 0 }
+};
+
struct access_sys_t
{
/* Devices */
{
p_sys->psz_device = strdup( psz_dup );
}
+ if( psz_dup ) free( psz_dup );
}
/*****************************************************************************
/* Find out video format used by device */
if( ioctl( i_fd, VIDIOCGPICT, &p_sys->vid_picture ) == 0 )
{
- int i_chroma = VLC_FOURCC( ' ', ' ', ' ', ' ' );
struct video_picture vid_picture = p_sys->vid_picture;
+ vlc_value_t val;
+ int i;
+
+ vid_picture.palette = 0;
+ p_sys->i_fourcc = 0;
+
+ var_Create( p_input, "v4l-chroma",
+ VLC_VAR_STRING | VLC_VAR_DOINHERIT );
+ var_Get( p_input, "v4l-chroma", &val );
+ if( val.psz_string && strlen( val.psz_string ) >= 4 )
+ {
+ int i_chroma =
+ VLC_FOURCC( val.psz_string[0], val.psz_string[1],
+ val.psz_string[2], val.psz_string[3] );
+
+ /* Find out v4l chroma code */
+ for( i = 0; v4lchroma_to_fourcc[i].i_v4l != 0; i++ )
+ {
+ if( v4lchroma_to_fourcc[i].i_fourcc == i_chroma )
+ {
+ vid_picture.palette = v4lchroma_to_fourcc[i].i_v4l;
+ break;
+ }
+ }
+ }
+ if( val.psz_string ) free( val.psz_string );
- /* Try to set the format to something easy to encode */
- vid_picture.palette = VIDEO_PALETTE_YUV420P;
- if( ioctl( i_fd, VIDIOCSPICT, &vid_picture ) == 0 )
+ if( vid_picture.palette &&
+ !ioctl( i_fd, VIDIOCSPICT, &vid_picture ) )
{
p_sys->vid_picture = vid_picture;
}
else
{
- vid_picture.palette = VIDEO_PALETTE_YUV422P;
+ /* Try to set the format to something easy to encode */
+ vid_picture.palette = VIDEO_PALETTE_YUV420P;
if( ioctl( i_fd, VIDIOCSPICT, &vid_picture ) == 0 )
{
p_sys->vid_picture = vid_picture;
}
+ else
+ {
+ vid_picture.palette = VIDEO_PALETTE_YUV422P;
+ if( ioctl( i_fd, VIDIOCSPICT, &vid_picture ) == 0 )
+ {
+ p_sys->vid_picture = vid_picture;
+ }
+ }
}
/* Find out final format */
- switch( p_sys->vid_picture.palette )
+ for( i = 0; v4lchroma_to_fourcc[i].i_v4l != 0; i++ )
{
- case VIDEO_PALETTE_GREY:
- i_chroma = VLC_FOURCC( 'G', 'R', 'E', 'Y' );
- break;
- case VIDEO_PALETTE_HI240:
- i_chroma = VLC_FOURCC( 'I', '2', '4', '0' );
- break;
- case VIDEO_PALETTE_RGB565:
- i_chroma = VLC_FOURCC( 'R', 'V', '1', '6' );
- break;
- case VIDEO_PALETTE_RGB555:
- i_chroma = VLC_FOURCC( 'R', 'V', '1', '5' );
- break;
- case VIDEO_PALETTE_RGB24:
- i_chroma = VLC_FOURCC( 'R', 'V', '2', '4' );
- break;
- case VIDEO_PALETTE_RGB32:
- i_chroma = VLC_FOURCC( 'R', 'V', '3', '2' );
- break;
- case VIDEO_PALETTE_YUV422:
- i_chroma = VLC_FOURCC( 'I', '4', '2', '2' );
- break;
- case VIDEO_PALETTE_YUYV:
- i_chroma = VLC_FOURCC( 'Y', 'U', 'Y', 'V' );
- break;
- case VIDEO_PALETTE_UYVY:
- i_chroma = VLC_FOURCC( 'U', 'Y', 'V', 'Y' );
- break;
- case VIDEO_PALETTE_YUV420:
- i_chroma = VLC_FOURCC( 'I', '4', '2', 'N' );
- break;
- case VIDEO_PALETTE_YUV411:
- i_chroma = VLC_FOURCC( 'I', '4', '1', 'N' );
- break;
- case VIDEO_PALETTE_RAW:
- i_chroma = VLC_FOURCC( 'G', 'R', 'A', 'W' );
- break;
- case VIDEO_PALETTE_YUV422P:
- i_chroma = VLC_FOURCC( 'I', '4', '2', '2' );
- break;
- case VIDEO_PALETTE_YUV420P:
- i_chroma = VLC_FOURCC( 'I', '4', '2', '0' );
- break;
- case VIDEO_PALETTE_YUV411P:
- i_chroma = VLC_FOURCC( 'I', '4', '1', '1' );
- break;
+ if( v4lchroma_to_fourcc[i].i_v4l == p_sys->vid_picture.palette)
+ {
+ p_sys->i_fourcc = v4lchroma_to_fourcc[i].i_fourcc;
+ break;
+ }
}
- p_sys->i_fourcc = i_chroma;
+
}
else
{
return i_data;
}
+
+
+/*****************************************************************************
+ * Demux: local prototypes
+ *****************************************************************************/
+struct demux_sys_t
+{
+ int i_es;
+ es_out_id_t **es;
+};
+
+static int Demux ( input_thread_t * );
+
/****************************************************************************
- * I. Demux Part
+ * DemuxOpen:
****************************************************************************/
static int DemuxOpen( vlc_object_t *p_this )
{
input_thread_t *p_input = (input_thread_t *)p_this;
+ demux_sys_t *p_sys;
uint8_t *p_peek;
- int i_streams;
+ int i_es;
int i;
- data_packet_t *p_pk;
-
- /* Initialize access plug-in structures. */
- if( p_input->i_mtu == 0 )
- {
- /* Improve speed. */
- p_input->i_bufsize = INPUT_DEFAULT_BUFSIZE ;
- }
-
/* a little test to see if it's a v4l stream */
- if( input_Peek( p_input, &p_peek, 8 ) < 8 )
+ if( stream_Peek( p_input->s, &p_peek, 8 ) < 8 )
{
msg_Warn( p_input, "v4l plugin discarded (cannot peek)" );
return VLC_EGENERIC;
}
- if( strncmp( p_peek, ".v4l", 4 ) || GetDWBE( &p_peek[4] ) <= 0 )
+ if( strncmp( p_peek, ".v4l", 4 ) ||
+ ( i_es = GetDWBE( &p_peek[4] ) ) <= 0 )
{
msg_Warn( p_input, "v4l plugin discarded (not a valid stream)" );
return VLC_EGENERIC;
}
- /* create one program */
vlc_mutex_lock( &p_input->stream.stream_lock );
if( input_InitStream( p_input, 0 ) == -1)
{
msg_Err( p_input, "cannot init stream" );
return( VLC_EGENERIC );
}
- if( input_AddProgram( p_input, 0, 0) == NULL )
- {
- vlc_mutex_unlock( &p_input->stream.stream_lock );
- msg_Err( p_input, "cannot add program" );
- return VLC_EGENERIC;
- }
+ p_input->stream.i_mux_rate = 0 / 50;
+ vlc_mutex_unlock( &p_input->stream.stream_lock );
- p_input->stream.p_selected_program = p_input->stream.pp_programs[0];
- p_input->stream.i_mux_rate = 0;
+ p_input->pf_demux = Demux;
+ p_input->pf_demux_control = demux_vaControlDefault;
+ p_input->p_demux_data = p_sys = malloc( sizeof( demux_sys_t ) );
+ p_sys->i_es = 0;
+ p_sys->es = NULL;
- i_streams = GetDWBE( &p_peek[4] );
- if( input_Peek( p_input, &p_peek, 8 + 20 * i_streams )
- < 8 + 20 * i_streams )
+ if( stream_Peek( p_input->s, &p_peek, 8 + 20 * i_es ) < 8 + 20 * i_es )
{
msg_Err( p_input, "v4l plugin discarded (cannot peek)" );
return VLC_EGENERIC;
}
p_peek += 8;
- for( i = 0; i < i_streams; i++ )
+ for( i = 0; i < i_es; i++ )
{
- es_descriptor_t *p_es;
+ es_format_t fmt;
if( !strncmp( p_peek, "auds", 4 ) )
{
-#define wf ((WAVEFORMATEX*)p_es->p_waveformatex)
- p_es = input_AddES( p_input, p_input->stream.pp_programs[0],
- i + 1, AUDIO_ES, NULL, 0 );
- p_es->i_stream_id = i + 1;
- p_es->i_fourcc =
- VLC_FOURCC( p_peek[4], p_peek[5], p_peek[6], p_peek[7] );
-
- p_es->p_waveformatex= malloc( sizeof( WAVEFORMATEX ) );
-
- wf->wFormatTag = WAVE_FORMAT_UNKNOWN;
- wf->nChannels = GetDWBE( &p_peek[8] );
- wf->nSamplesPerSec = GetDWBE( &p_peek[12] );
- wf->wBitsPerSample = GetDWBE( &p_peek[16] );
- wf->nBlockAlign = wf->wBitsPerSample * wf->nChannels / 8;
- wf->nAvgBytesPerSec = wf->nBlockAlign * wf->nSamplesPerSec;
- wf->cbSize = 0;
-
- msg_Dbg( p_input, "added new audio es %d channels %dHz",
- wf->nChannels, wf->nSamplesPerSec );
-
- input_SelectES( p_input, p_es );
-#undef wf
+ es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC( p_peek[4], p_peek[5],
+ p_peek[6], p_peek[7] ) );
+
+ fmt.audio.i_channels = GetDWBE( &p_peek[8] );
+ fmt.audio.i_rate = GetDWBE( &p_peek[12] );
+ fmt.audio.i_bitspersample = GetDWBE( &p_peek[16] );
+ 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_input, "new audio es %d channels %dHz",
+ fmt.audio.i_channels, fmt.audio.i_rate );
+
+ TAB_APPEND( p_sys->i_es, p_sys->es,
+ es_out_Add( p_input->p_es_out, &fmt ) );
}
else if( !strncmp( p_peek, "vids", 4 ) )
{
-#define bih ((BITMAPINFOHEADER*)p_es->p_bitmapinfoheader)
- p_es = input_AddES( p_input, p_input->stream.pp_programs[0],
- i + 1, VIDEO_ES, NULL, 0 );
- p_es->i_stream_id = i + 1;
- p_es->i_fourcc =
- VLC_FOURCC( p_peek[4], p_peek[5], p_peek[6], p_peek[7] );
-
- p_es->p_bitmapinfoheader = malloc( sizeof( BITMAPINFOHEADER ) );
-
- bih->biSize = sizeof( BITMAPINFOHEADER );
- bih->biWidth = GetDWBE( &p_peek[8] );
- bih->biHeight = GetDWBE( &p_peek[12] );
- bih->biPlanes = 0;
- bih->biBitCount = 0;
- bih->biCompression = 0;
- bih->biSizeImage= 0;
- bih->biXPelsPerMeter = 0;
- bih->biYPelsPerMeter = 0;
- bih->biClrUsed = 0;
- bih->biClrImportant = 0;
+ es_format_Init( &fmt, VIDEO_ES, VLC_FOURCC( p_peek[4], p_peek[5],
+ p_peek[6], p_peek[7] ) );
+ fmt.video.i_width = GetDWBE( &p_peek[8] );
+ fmt.video.i_height = GetDWBE( &p_peek[12] );
msg_Dbg( p_input, "added new video es %4.4s %dx%d",
- (char*)&p_es->i_fourcc, bih->biWidth, bih->biHeight );
-
- input_SelectES( p_input, p_es );
-#undef bih
+ (char*)&fmt.i_codec,
+ fmt.video.i_width, fmt.video.i_height );
+ TAB_APPEND( p_sys->i_es, p_sys->es,
+ es_out_Add( p_input->p_es_out, &fmt ) );
}
p_peek += 20;
}
- p_input->stream.p_selected_program->b_is_ok = 1;
- vlc_mutex_unlock( &p_input->stream.stream_lock );
+ /* Skip header */
+ stream_Read( p_input->s, NULL, 8 + 20 * i_es );
- if( input_SplitBuffer( p_input, &p_pk, 8 + i_streams * 20 ) > 0 )
- {
- input_DeletePacket( p_input->p_method_data, p_pk );
- }
-
- p_input->pf_demux = Demux;
- p_input->pf_demux_control = demux_vaControlDefault;
return VLC_SUCCESS;
}
+/****************************************************************************
+ * DemuxClose:
+ ****************************************************************************/
static void DemuxClose( vlc_object_t *p_this )
{
- return;
-}
+ input_thread_t *p_input = (input_thread_t *)p_this;
+ demux_sys_t *p_sys = p_input->p_demux_data;
-#define MAX_PACKETS_IN_FIFO 3
+ if( p_sys->i_es > 0 )
+ {
+ free( p_sys->es );
+ }
+ free( p_sys );
+}
+/****************************************************************************
+ * Demux:
+ ****************************************************************************/
static int Demux( input_thread_t *p_input )
{
- es_descriptor_t *p_es;
- pes_packet_t *p_pes;
+ demux_sys_t *p_sys = p_input->p_demux_data;
+ block_t *p_block;
- int i_stream;
+ int i_es;
int i_size;
uint8_t *p_peek;
mtime_t i_pts;
- if( input_Peek( p_input, &p_peek, 16 ) < 16 )
+ if( stream_Peek( p_input->s, &p_peek, 16 ) < 16 )
{
msg_Warn( p_input, "cannot peek (EOF ?)" );
- return( 0 );
+ return 0;
}
- i_stream = GetDWBE( &p_peek[0] );
- i_size = GetDWBE( &p_peek[4] );
- i_pts = GetQWBE( &p_peek[8] );
-
- p_es = p_input->stream.p_selected_program->pp_es[i_stream];
- if( !p_es )
+ i_es = GetDWBE( &p_peek[0] );
+ if( i_es < 0 || i_es >= p_sys->i_es )
{
msg_Err( p_input, "cannot find ES" );
+ return -1;
}
- p_pes = input_NewPES( p_input->p_method_data );
- if( p_pes == NULL )
+ i_size = GetDWBE( &p_peek[4] );
+ i_pts = GetQWBE( &p_peek[8] );
+
+ if( ( p_block = stream_Block( p_input->s, 16 + i_size ) ) == NULL )
{
- msg_Warn( p_input, "cannot allocate PES" );
- msleep( 1000 );
- return( 1 );
+ msg_Warn( p_input, "cannot read data" );
+ return 0;
}
- i_size += 16;
- while( i_size > 0 )
- {
- data_packet_t *p_data;
- int i_read;
- if( (i_read = input_SplitBuffer( p_input, &p_data,
- __MIN( i_size, 10000 ) ) ) <= 0 )
- {
- input_DeletePES( p_input->p_method_data, p_pes );
- return( 0 );
- }
- if( !p_pes->p_first )
- {
- p_pes->p_first = p_data;
- p_pes->i_nb_data = 1;
- p_pes->i_pes_size = i_read;
- }
- else
- {
- p_pes->p_last->p_next = p_data;
- p_pes->i_nb_data++;
- p_pes->i_pes_size += i_read;
- }
- p_pes->p_last = p_data;
- i_size -= i_read;
- }
- p_pes->p_first->p_payload_start += 16;
- p_pes->i_pes_size -= 16;
+ p_block->p_buffer += 16;
+ p_block->i_buffer -= 16;
- if( p_es && p_es->p_decoder_fifo )
- {
- vlc_mutex_lock( &p_es->p_decoder_fifo->data_lock );
- if( p_es->p_decoder_fifo->i_depth >= MAX_PACKETS_IN_FIFO )
- {
- /* Wait for the decoder. */
- vlc_cond_wait( &p_es->p_decoder_fifo->data_wait,
- &p_es->p_decoder_fifo->data_lock );
- }
- vlc_mutex_unlock( &p_es->p_decoder_fifo->data_lock );
- p_pes->i_pts = p_pes->i_dts = i_pts + p_input->i_pts_delay;
+ p_block->i_dts =
+ p_block->i_pts = i_pts + p_input->i_pts_delay;
- input_DecodePES( p_es->p_decoder_fifo, p_pes );
- }
- else
- {
- input_DeletePES( p_input->p_method_data, p_pes );
- }
+ es_out_Send( p_input->p_es_out, p_sys->es[i_es], p_block );
return 1;
}