* v4l.c : Video4Linux input module for vlc
*****************************************************************************
* Copyright (C) 2002 VideoLAN
- * $Id: v4l.c,v 1.14 2003/05/08 19:21:39 fenrir Exp $
+ * $Id: v4l.c,v 1.24 2003/09/07 22:49:05 fenrir Exp $
*
- * Author: Samuel Hocevar <sam@zoy.org>
+ * Author: Laurent Aimar <fenrir@via.ecp.fr>
+ * Paul Forgey <paulf at aphrodite dot com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#include <vlc/input.h>
#include <vlc/vout.h>
#include <codecs.h>
-#include "encoder.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/videodev.h>
+#include "videodev_mjpeg.h"
#include <sys/soundcard.h>
/*****************************************************************************
* Module descriptior
*****************************************************************************/
-#define CACHING_TEXT N_("caching value in ms")
+#define CACHING_TEXT N_("Caching value in ms")
#define CACHING_LONGTEXT N_( \
"Allows you to modify the default caching value for v4l streams. This " \
"value should be set in miliseconds units." )
/****************************************************************************
* I. Access Part
****************************************************************************/
+#define MJPEG_BUFFER_SIZE (256*1024)
+
+struct quicktime_mjpeg_app1
+{
+ uint32_t i_reserved; /* set to 0 */
+ uint32_t i_tag; /* 'mjpg' */
+ uint32_t i_field_size; /* offset following EOI */
+ uint32_t i_padded_field_size; /* offset following EOI+pad */
+ uint32_t i_next_field; /* offset to next field */
+ uint32_t i_DQT_offset;
+ uint32_t i_DHT_offset;
+ uint32_t i_SOF_offset;
+ uint32_t i_SOS_offset;
+ uint32_t i_data_offset; /* following SOS marker data */
+};
+
struct access_sys_t
{
char *psz_video_device;
int fd;
- vlc_fourcc_t i_codec; // if i_codec != i_chroma then we need a compressor
- video_encoder_t *p_encoder;
picture_t pic;
+ int i_fourcc;
int i_channel;
int i_audio;
int i_norm;
int i_tuner;
int i_frequency;
- int i_chroma;
int i_width;
int i_height;
+ vlc_bool_t b_mjpeg;
+ int i_decimation;
+ int i_quality;
+
struct video_capability vid_cap;
struct video_mbuf vid_mbuf;
+ struct mjpeg_requestbuffers mjpeg_buffers;
uint8_t *p_video_mmap;
int i_frame_pos;
+
struct video_mmap vid_mmap;
struct video_picture vid_picture;
SetDWBE( &p[4], qw&0xffffffff);
}
-static uint32_t GetDWBE( uint8_t *p_buff )
-{
- return( ( p_buff[0] << 24 ) + ( p_buff[1] << 16 ) +
- ( p_buff[2] << 8 ) + p_buff[3] );
-}
-static uint64_t GetQWBE( uint8_t *p )
-{
- return( ( (uint64_t)GetDWBE( p ) << 32) | (uint64_t)GetDWBE( &p[4] ) );
-}
-
/*****************************************************************************
* Open: open device:
*****************************************************************************
input_thread_t *p_input = (input_thread_t *)p_this;
access_sys_t *p_sys;
char *psz_dup, *psz_parser;
+ struct mjpeg_params mjpeg;
+ int i;
struct video_channel vid_channel;
p_sys->i_width = 0;
p_sys->i_height = 0;
+ p_sys->b_mjpeg = VLC_FALSE;
+ p_sys->i_decimation = 1;
+ p_sys->i_quality = 100;
+
p_sys->i_frame_pos = 0;
- p_sys->i_codec = VLC_FOURCC( 0, 0, 0, 0 );
p_sys->i_video_frame_size_allocated = 0;
p_sys->psz_adev = NULL;
p_sys->fd_audio = -1;
p_sys->i_frequency =
strtol( psz_parser + strlen( "frequency=" ),
&psz_parser, 0 );
+ if( p_sys->i_frequency < 30000 )
+ {
+ msg_Warn( p_input, "v4l syntax has changed : 'frequency' is now channel frequency in kHz");
+ }
}
else if( !strncmp( psz_parser, "audio=", strlen( "audio=" ) ) )
{
p_sys->i_tuner = strtol( psz_parser + strlen( "tuner=" ),
&psz_parser, 0 );
}
- else if( !strncmp( psz_parser, "codec=", strlen( "codec=" ) ) )
- {
- psz_parser += strlen( "codec=" );
- if( !strncmp( psz_parser, "mpeg4", strlen( "mpeg4" ) ) )
- {
- p_sys->i_codec = VLC_FOURCC( 'm', 'p', '4', 'v' );
- }
- else if( !strncmp( psz_parser, "mpeg1", strlen( "mpeg1" ) ) )
- {
- p_sys->i_codec = VLC_FOURCC( 'm', 'p', '1', 'v' );
- }
- else
- {
- msg_Warn( p_input, "unknow codec" );
- }
- }
else if( !strncmp( psz_parser, "adev=", strlen( "adev=" ) ) )
{
int i_len;
p_sys->b_stereo = VLC_FALSE;
}
+ else if( !strncmp( psz_parser, "mjpeg", strlen( "mjpeg" ) ) )
+ {
+ psz_parser += strlen( "mjpeg" );
+
+ p_sys->b_mjpeg = VLC_TRUE;
+ }
+ else if( !strncmp( psz_parser, "decimation=",
+ strlen( "decimation=" ) ) )
+ {
+ p_sys->i_decimation =
+ strtol( psz_parser + strlen( "decimation=" ),
+ &psz_parser, 0 );
+ }
+ else if( !strncmp( psz_parser, "quality=",
+ strlen( "quality=" ) ) )
+ {
+ p_sys->i_quality =
+ strtol( psz_parser + strlen( "quality=" ),
+ &psz_parser, 0 );
+ }
else
{
- msg_Warn( p_input, "unknow option" );
+ msg_Warn( p_input, "unknown option" );
}
while( *psz_parser && *psz_parser != ':' )
if( ( p_sys->fd = open( p_sys->psz_video_device, O_RDWR ) ) < 0 )
{
- msg_Err( p_input, "cannot open device" );
+ msg_Err( p_input, "cannot open device (%s)", strerror( errno ) );
goto failed;
}
if( ioctl( p_sys->fd, VIDIOCGCAP, &p_sys->vid_cap ) < 0 )
{
- msg_Err( p_input, "cannot get capabilities" );
+ msg_Err( p_input, "cannot get capabilities (%s)", strerror( errno ) );
goto failed;
}
if( p_sys->i_width < p_sys->vid_cap.minwidth ||
p_sys->i_width > p_sys->vid_cap.maxwidth )
{
- msg_Dbg( p_input, "invalid width" );
+ msg_Dbg( p_input, "invalid width %i", p_sys->i_width );
p_sys->i_width = 0;
}
if( p_sys->i_height < p_sys->vid_cap.minheight ||
p_sys->i_height > p_sys->vid_cap.maxheight )
{
- msg_Dbg( p_input, "invalid height" );
+ msg_Dbg( p_input, "invalid height %i", p_sys->i_height );
p_sys->i_height = 0;
}
- if( !( p_sys->vid_cap.type&VID_TYPE_CAPTURE ) )
+ if( !( p_sys->vid_cap.type & VID_TYPE_CAPTURE ) )
{
msg_Err( p_input, "cannot grab" );
goto failed;
vid_channel.channel = p_sys->i_channel;
if( ioctl( p_sys->fd, VIDIOCGCHAN, &vid_channel ) < 0 )
{
- msg_Err( p_input, "cannot get channel infos" );
+ msg_Err( p_input, "cannot get channel infos (%s)",
+ strerror( errno ) );
goto failed;
}
msg_Dbg( p_input,
vid_channel.norm = p_sys->i_norm;
if( ioctl( p_sys->fd, VIDIOCSCHAN, &vid_channel ) < 0 )
{
- msg_Err( p_input, "cannot set channel" );
+ msg_Err( p_input, "cannot set channel (%s)", strerror( errno ) );
goto failed;
}
vid_tuner.tuner = p_sys->i_tuner;
if( ioctl( p_sys->fd, VIDIOCGTUNER, &vid_tuner ) < 0 )
{
- msg_Err( p_input, "cannot get tuner" );
+ msg_Err( p_input, "cannot get tuner (%s)", strerror( errno ) );
goto failed;
}
msg_Dbg( p_input, "tuner %s low=%d high=%d, flags=0x%x "
//vid_tuner.mode = p_sys->i_norm; /* FIXME FIXME to be checked FIXME FIXME */
if( ioctl( p_sys->fd, VIDIOCSTUNER, &vid_tuner ) < 0 )
{
- msg_Err( p_input, "cannot set tuner" );
+ msg_Err( p_input, "cannot set tuner (%s)", strerror( errno ) );
goto failed;
}
}
#endif
+
+ // show a warning if frequency is < than 30000. User is certainly usint old syntax.
+
+
/* set frequency */
if( p_sys->i_frequency >= 0 )
{
- if( ioctl( p_sys->fd, VIDIOCSFREQ, &p_sys->i_frequency ) < 0 )
+ int driver_frequency = p_sys->i_frequency * 16 /1000;
+ if( ioctl( p_sys->fd, VIDIOCSFREQ, &driver_frequency ) < 0 )
{
- msg_Err( p_input, "cannot set frequency" );
+ msg_Err( p_input, "cannot set frequency (%s)",
+ strerror( errno ) );
goto failed;
}
- msg_Dbg( p_input, "frequency %d", p_sys->i_frequency );
+ msg_Dbg( p_input, "frequency %d (%d)", p_sys->i_frequency,
+ driver_frequency );
}
}
vid_audio.audio = p_sys->i_audio;
if( ioctl( p_sys->fd, VIDIOCGAUDIO, &vid_audio ) < 0 )
{
- msg_Err( p_input, "cannot get audio" );
+ msg_Err( p_input, "cannot get audio (%s)", strerror( errno ) );
goto failed;
}
if( ioctl( p_sys->fd, VIDIOCSAUDIO, &vid_audio ) < 0 )
{
- msg_Err( p_input, "cannot set audio" );
+ msg_Err( p_input, "cannot set audio (%s)", strerror( errno ) );
goto failed;
}
+ }
- if( p_sys->psz_adev )
- {
- int i_format;
- if( ( p_sys->fd_audio = open( p_sys->psz_adev, O_RDONLY|O_NONBLOCK ) ) < 0 )
- {
- msg_Err( p_input, "cannot open audio device" );
- goto failed;
- }
+ }
- i_format = AFMT_S16_LE;
- if( ioctl( p_sys->fd_audio, SNDCTL_DSP_SETFMT, &i_format ) < 0
- || i_format != AFMT_S16_LE )
- {
- msg_Err( p_input,
- "cannot set audio format (16b little endian)" );
- goto failed;
- }
+ if( p_sys->psz_adev )
+ {
+ int i_format;
+ if( ( p_sys->fd_audio = open( p_sys->psz_adev, O_RDONLY|O_NONBLOCK ) ) < 0 )
+ {
+ msg_Err( p_input, "cannot open audio device (%s)",
+ strerror( errno ) );
+ goto failed;
+ }
- if( ioctl( p_sys->fd_audio, SNDCTL_DSP_STEREO,
- &p_sys->b_stereo ) < 0 )
- {
- msg_Err( p_input, "cannot set audio channels count" );
- goto failed;
- }
+ i_format = AFMT_S16_LE;
+ if( ioctl( p_sys->fd_audio, SNDCTL_DSP_SETFMT, &i_format ) < 0
+ || i_format != AFMT_S16_LE )
+ {
+ msg_Err( p_input, "cannot set audio format (16b little endian) "
+ "(%s)", strerror( errno ) );
+ goto failed;
+ }
- if( ioctl( p_sys->fd_audio, SNDCTL_DSP_SPEED,
- &p_sys->i_sample_rate ) < 0 )
- {
- msg_Err( p_input, "cannot set audio sample rate" );
- goto failed;
- }
+ if( ioctl( p_sys->fd_audio, SNDCTL_DSP_STEREO,
+ &p_sys->b_stereo ) < 0 )
+ {
+ msg_Err( p_input, "cannot set audio channels count (%s)",
+ strerror( errno ) );
+ goto failed;
+ }
- msg_Dbg( p_input,
- "adev=`%s' %s %dHz",
- p_sys->psz_adev,
- p_sys->b_stereo ? "stereo" : "mono",
- p_sys->i_sample_rate );
+ if( ioctl( p_sys->fd_audio, SNDCTL_DSP_SPEED,
+ &p_sys->i_sample_rate ) < 0 )
+ {
+ msg_Err( p_input, "cannot set audio sample rate (%s)",
+ strerror( errno ) );
+ goto failed;
+ }
- p_sys->i_audio_frame_size = 0;
- p_sys->i_audio_frame_size_allocated = 6*1024;
- p_sys->p_audio_frame =
- malloc( p_sys->i_audio_frame_size_allocated );
- }
+ msg_Dbg( p_input,
+ "adev=`%s' %s %dHz",
+ p_sys->psz_adev,
+ p_sys->b_stereo ? "stereo" : "mono",
+ p_sys->i_sample_rate );
+
+ p_sys->i_audio_frame_size = 0;
+ p_sys->i_audio_frame_size_allocated = 6*1024;
+ p_sys->p_audio_frame =
+ malloc( p_sys->i_audio_frame_size_allocated );
+ }
+
+ /* establish basic params with input and norm before feeling width
+ * or height */
+ if( p_sys->b_mjpeg )
+ {
+ struct quicktime_mjpeg_app1 *p_app1;
+ int32_t i_offset;
+
+ if( ioctl( p_sys->fd, MJPIOC_G_PARAMS, &mjpeg ) < 0 )
+ {
+ msg_Err( p_input, "cannot get mjpeg params (%s)",
+ strerror( errno ) );
+ goto failed;
}
+ mjpeg.input = p_sys->i_channel;
+ mjpeg.norm = p_sys->i_norm;
+ mjpeg.decimation = p_sys->i_decimation;
+
+ if( p_sys->i_width )
+ mjpeg.img_width = p_sys->i_width / p_sys->i_decimation;
+ if( p_sys->i_height )
+ mjpeg.img_height = p_sys->i_height / p_sys->i_decimation;
+
+ /* establish Quicktime APP1 marker while we are here */
+ mjpeg.APPn = 1;
+ mjpeg.APP_len = 40;
+
+ /* aligned */
+ p_app1 = (struct quicktime_mjpeg_app1 *)mjpeg.APP_data;
+ p_app1->i_reserved = 0;
+ p_app1->i_tag = VLC_FOURCC( 'm','j','p','g' );
+ p_app1->i_field_size = 0;
+ p_app1->i_padded_field_size = 0;
+ p_app1->i_next_field = 0;
+ /* XXX WARNING XXX */
+ /* these's nothing magic about these values. We are dangerously
+ * assuming the encoder card is encoding mjpeg-a and is not throwing
+ * in marker tags we aren't expecting. It's bad enough we have to
+ * search through the jpeg output for every frame we grab just to
+ * find the first field's end marker, so we take this risk to boost
+ * performance.
+ * This is really something the driver could do for us because this
+ * does conform to standards outside of Apple Quicktime.
+ */
+ i_offset = 0x2e;
+ p_app1->i_DQT_offset = hton32( i_offset );
+ i_offset = 0xb4;
+ p_app1->i_DHT_offset = hton32( i_offset );
+ i_offset = 0x258;
+ p_app1->i_SOF_offset = hton32( i_offset );
+ i_offset = 0x26b;
+ p_app1->i_SOS_offset = hton32( i_offset );
+ i_offset = 0x279;
+ p_app1->i_data_offset = hton32( i_offset );
+
+ /* SOF and SOS aren't specified by the mjpeg API because they aren't
+ * optional. They will be present in the output. */
+ mjpeg.jpeg_markers = JPEG_MARKER_DHT | JPEG_MARKER_DQT;
+
+ if( ioctl( p_sys->fd, MJPIOC_S_PARAMS, &mjpeg ) < 0 )
+ {
+ msg_Err( p_input, "cannot set mjpeg params (%s)",
+ strerror( errno ) );
+ goto failed;
+ }
+
+ p_sys->i_width = mjpeg.img_width * mjpeg.HorDcm;
+ p_sys->i_height = mjpeg.img_height * mjpeg.VerDcm *
+ mjpeg.field_per_buff;
}
- /* fix width/heigh */
- if( p_sys->i_width == 0 || p_sys->i_height == 0 )
+ /* fix width/height */
+ if( !p_sys->b_mjpeg && ( p_sys->i_width == 0 || p_sys->i_height == 0 ) )
{
struct video_window vid_win;
if( ioctl( p_sys->fd, VIDIOCGWIN, &vid_win ) < 0 )
{
- msg_Err( p_input, "cannot get win" );
+ msg_Err( p_input, "cannot get win (%s)", strerror( errno ) );
goto failed;
}
p_sys->i_width = vid_win.width;
p_sys->p_video_frame = NULL;
- /* Find out video format used by device */
- if( ioctl( p_sys->fd, VIDIOCGPICT, &p_sys->vid_picture ) == 0 )
+ if( !p_sys->b_mjpeg )
{
- int i_chroma, i;
- struct video_picture vid_picture = p_sys->vid_picture;
-
- /* Try to set the format to something easy to encode */
- vid_picture.palette = VIDEO_PALETTE_YUV420P;
- if( ioctl( p_sys->fd, VIDIOCSPICT, &vid_picture ) == 0 )
- {
- p_sys->vid_picture = vid_picture;
- }
- else
+ /* Find out video format used by device */
+ if( ioctl( p_sys->fd, VIDIOCGPICT, &p_sys->vid_picture ) == 0 )
{
- vid_picture.palette = VIDEO_PALETTE_YUV422P;
+ int i_chroma = VLC_FOURCC( ' ', ' ', ' ', ' ' );
+ struct video_picture vid_picture = p_sys->vid_picture;
+
+ /* Try to set the format to something easy to encode */
+ vid_picture.palette = VIDEO_PALETTE_YUV420P;
if( ioctl( p_sys->fd, VIDIOCSPICT, &vid_picture ) == 0 )
{
p_sys->vid_picture = vid_picture;
}
+ else
+ {
+ vid_picture.palette = VIDEO_PALETTE_YUV422P;
+ if( ioctl( p_sys->fd, VIDIOCSPICT, &vid_picture ) == 0 )
+ {
+ p_sys->vid_picture = vid_picture;
+ }
+ }
+
+ /* Find out final format */
+ switch( p_sys->vid_picture.palette )
+ {
+ 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;
+ }
+ p_sys->i_fourcc = i_chroma;
}
+ else
+ {
+ msg_Err( p_input, "ioctl VIDIOCGPICT failed" );
+ goto failed;
+ }
+ }
- /* Find out final format */
- switch( p_sys->vid_picture.palette )
+
+ if( p_sys->b_mjpeg )
+ {
+ int i;
+
+ p_sys->mjpeg_buffers.count = 8;
+ p_sys->mjpeg_buffers.size = MJPEG_BUFFER_SIZE;
+
+ if( ioctl( p_sys->fd, MJPIOC_REQBUFS, &p_sys->mjpeg_buffers ) < 0 )
{
- 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;
+ msg_Err( p_input, "mmap unsupported" );
+ goto failed;
}
- p_sys->i_chroma = i_chroma;
+ p_sys->p_video_mmap = mmap( 0,
+ p_sys->mjpeg_buffers.size * p_sys->mjpeg_buffers.count,
+ PROT_READ | PROT_WRITE, MAP_SHARED, p_sys->fd, 0 );
+ if( p_sys->p_video_mmap == MAP_FAILED )
+ {
+ msg_Err( p_input, "mmap failed" );
+ goto failed;
+ }
+ p_sys->i_fourcc = VLC_FOURCC( 'm','j','p','g' );
+ p_sys->i_frame_pos = -1;
+
+ /* queue up all the frames */
+ for( i = 0; i < (int)p_sys->mjpeg_buffers.count; i++ )
+ {
+ if( ioctl( p_sys->fd, MJPIOC_QBUF_CAPT, &i ) < 0 )
+ {
+ msg_Err( p_input, "unable to queue frame" );
+ goto failed;
+ }
+ }
+ }
+ else
+ {
/* Fill in picture_t fields */
vout_InitPicture( VLC_OBJECT(p_input), &p_sys->pic,
- p_sys->i_width, p_sys->i_height, p_sys->i_chroma );
+ p_sys->i_width, p_sys->i_height, p_sys->i_fourcc );
if( !p_sys->pic.i_planes )
{
msg_Err( p_input, "unsupported chroma" );
msg_Dbg( p_input, "v4l device uses frame size: %i",
p_sys->i_video_frame_size );
- msg_Dbg( p_input, "v4l device uses chroma: %4.4s", (char*)&i_chroma );
- }
- else
- {
- msg_Err( p_input, "ioctl VIDIOCGPICT failed" );
- goto failed;
- }
-
- /* Allocate mmap buffer */
- if( ioctl( p_sys->fd, VIDIOCGMBUF, &p_sys->vid_mbuf ) < 0 )
- {
- msg_Err( p_input, "mmap unsupported" );
- goto failed;
- }
-
- p_sys->p_video_mmap = mmap( 0, p_sys->vid_mbuf.size,
- PROT_READ|PROT_WRITE, MAP_SHARED,
- p_sys->fd, 0 );
- if( p_sys->p_video_mmap == MAP_FAILED )
- {
- /* FIXME -> normal read */
- msg_Err( p_input, "mmap failed" );
- goto failed;
- }
-
- /* init grabbing */
- p_sys->vid_mmap.frame = 0;
- p_sys->vid_mmap.width = p_sys->i_width;
- p_sys->vid_mmap.height = p_sys->i_height;
- p_sys->vid_mmap.format = p_sys->vid_picture.palette;
- if( ioctl( p_sys->fd, VIDIOCMCAPTURE, &p_sys->vid_mmap ) < 0 )
- {
- msg_Warn( p_input, "%4.4s refused", (char*)&p_sys->i_chroma );
- msg_Err( p_input, "chroma selection failed" );
- goto failed;
- }
+ msg_Dbg( p_input, "v4l device uses chroma: %4.4s",
+ (char*)&p_sys->i_fourcc );
- /* encoder part */
- if( p_sys->i_codec != VLC_FOURCC( 0, 0, 0, 0 ) )
- {
- msg_Dbg( p_input,
- "need a rencoder from %4.4s to %4.4s",
- (char*)&p_sys->i_chroma,
- (char*)&p_sys->i_codec );
-#define p_enc p_sys->p_encoder
- p_enc = vlc_object_create( p_input, sizeof( video_encoder_t ) );
- p_enc->i_codec = p_sys->i_codec;
- p_enc->i_chroma= p_sys->i_chroma;
- p_enc->i_width = p_sys->i_width;
- p_enc->i_height= p_sys->i_height;
- p_enc->i_aspect= 0;
-
-
- p_enc->p_module = module_Need( p_enc, "video encoder",
- "$video-encoder" );
- if( !p_enc->p_module )
+ /* Allocate mmap buffer */
+ if( ioctl( p_sys->fd, VIDIOCGMBUF, &p_sys->vid_mbuf ) < 0 )
{
- msg_Warn( p_input, "no suitable encoder to %4.4s",
- (char*)&p_enc->i_codec );
- vlc_object_destroy( p_enc );
+ msg_Err( p_input, "mmap unsupported" );
goto failed;
}
- /* *** init the codec *** */
- if( p_enc->pf_init( p_enc ) )
+ p_sys->p_video_mmap = mmap( 0, p_sys->vid_mbuf.size,
+ PROT_READ|PROT_WRITE, MAP_SHARED,
+ p_sys->fd, 0 );
+ if( p_sys->p_video_mmap == MAP_FAILED )
{
- msg_Err( p_input, "failed to initialize video encoder plugin" );
- vlc_object_destroy( p_enc );
+ /* FIXME -> normal read */
+ msg_Err( p_input, "mmap failed" );
goto failed;
}
- /* *** alloacted buffer *** */
- if( p_enc->i_buffer_size <= 0 )
- {
- p_enc->i_buffer_size = 1024 * 1024;// * p_enc->i_width * p_enc->i_height;
- }
- p_sys->i_video_frame_size = p_enc->i_buffer_size;
- p_sys->i_video_frame_size_allocated = p_enc->i_buffer_size;
- if( !( p_sys->p_video_frame = malloc( p_enc->i_buffer_size ) ) )
+ /* init grabbing */
+ p_sys->vid_mmap.frame = 0;
+ p_sys->vid_mmap.width = p_sys->i_width;
+ p_sys->vid_mmap.height = p_sys->i_height;
+ p_sys->vid_mmap.format = p_sys->vid_picture.palette;
+ if( ioctl( p_sys->fd, VIDIOCMCAPTURE, &p_sys->vid_mmap ) < 0 )
{
- msg_Err( p_input, "out of memory" );
+ msg_Warn( p_input, "%4.4s refused", (char*)&p_sys->i_fourcc );
+ msg_Err( p_input, "chroma selection failed" );
goto failed;
}
-#undef p_enc
}
- else
- {
- p_sys->i_codec = p_sys->i_chroma;
- p_sys->p_encoder = NULL;
- }
-
p_input->pf_read = Read;
p_input->pf_seek = NULL;
SetDWBE( &p_sys->p_header[4], 1 );
memcpy( &p_sys->p_header[ 8], "vids", 4 );
- memcpy( &p_sys->p_header[12], &p_sys->i_codec, 4 );
+ memcpy( &p_sys->p_header[12], &p_sys->i_fourcc, 4 );
SetDWBE( &p_sys->p_header[16], p_sys->i_width );
SetDWBE( &p_sys->p_header[20], p_sys->i_height );
SetDWBE( &p_sys->p_header[24], 0 );
msg_Info( p_input, "v4l grabbing stoped" );
+ if( p_sys->b_mjpeg )
+ {
+ int i_noframe = -1;
+ ioctl( p_sys->fd, MJPIOC_QBUF_CAPT, &i_noframe );
+ }
+
free( p_sys->psz_video_device );
close( p_sys->fd );
- if( p_sys->p_video_mmap )
+ if( p_sys->p_video_mmap && p_sys->p_video_mmap != MAP_FAILED )
{
- munmap( p_sys->p_video_mmap, p_sys->vid_mbuf.size );
+ 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 );
}
if( p_sys->fd_audio >= 0 )
{
close( p_sys->fd_audio );
}
- if( p_sys->p_encoder )
- {
- p_sys->p_encoder->pf_end( p_sys->p_encoder );
-
- module_Unneed( p_sys->p_encoder,
- p_sys->p_encoder->p_module );
- vlc_object_destroy( p_sys->p_encoder );
-
- free( p_sys->p_video_frame );
- }
free( p_sys );
}
return VLC_SUCCESS;
}
-static int GrabVideo( input_thread_t * p_input,
- uint8_t **pp_data,
- int *pi_data,
- mtime_t *pi_pts )
+static uint8_t *GrabCapture( input_thread_t *p_input )
{
- access_sys_t *p_sys = p_input->p_access_data;
-
+ access_sys_t *p_sys = p_input->p_access_data;
p_sys->vid_mmap.frame = ( p_sys->i_frame_pos + 1 ) %
p_sys->vid_mbuf.frames;
for( ;; )
if( errno != EAGAIN )
{
msg_Err( p_input, "failed while grabbing new frame" );
- return( -1 );
+ return( NULL );
}
msg_Dbg( p_input, "another try ?" );
}
while( ioctl(p_sys->fd, VIDIOCSYNC, &p_sys->i_frame_pos) < 0 &&
( errno == EAGAIN || errno == EINTR ) );
-
p_sys->i_frame_pos = p_sys->vid_mmap.frame;
+ /* leave i_video_frame_size alone */
+ return p_sys->p_video_mmap + p_sys->vid_mbuf.offsets[p_sys->i_frame_pos];
+}
+
+static uint8_t *GrabMJPEG( input_thread_t *p_input )
+{
+ access_sys_t *p_sys = p_input->p_access_data;
+ struct mjpeg_sync sync;
+ uint8_t *p_frame, *p_field, *p;
+ uint16_t tag;
+ uint32_t i_size;
+ struct quicktime_mjpeg_app1 *p_app1 = NULL;
+
+ /* re-queue the last frame we sync'd */
+ if( p_sys->i_frame_pos != -1 )
+ while( ioctl( p_sys->fd, MJPIOC_QBUF_CAPT, &p_sys->i_frame_pos ) < 0 &&
+ ( errno == EAGAIN || errno == EINTR ) );
+
+ /* sync on the next frame */
+ while( ioctl( p_sys->fd, MJPIOC_SYNC, &sync ) < 0 &&
+ ( errno == EAGAIN || errno == EINTR ) );
+
+ p_sys->i_frame_pos = sync.frame;
+ p_frame = p_sys->p_video_mmap + p_sys->mjpeg_buffers.size * sync.frame;
+
+ /* p_frame now points to the data. fix up the Quicktime APP1 marker */
+ tag = 0xffd9;
+ tag = hton16( tag );
+ p_field = p_frame;
- if( p_sys->p_encoder )
+ /* look for EOI */
+ p = memmem( p_field, sync.length, &tag, 2 );
+
+ if( p )
{
- int i;
+ p += 2; /* data immediately following EOI */
+ /* UNALIGNED! */
+ p_app1 = (struct quicktime_mjpeg_app1 *)(p_field + 6);
- p_sys->pic.p[0].p_pixels = p_sys->p_video_mmap +
- p_sys->vid_mbuf.offsets[p_sys->i_frame_pos];
+ i_size = ((uint32_t)(p - p_field));
+ i_size = hton32( i_size );
+ memcpy( &p_app1->i_field_size, &i_size, 4 );
- for( i = 1; i < p_sys->pic.i_planes; i++ )
+ while( *p == 0xff && *(p+1) == 0xff )
+ p++;
+
+ i_size = ((uint32_t)(p - p_field));
+ i_size = hton32( i_size );
+ memcpy( &p_app1->i_padded_field_size, &i_size, 4 );
+ }
+
+ tag = 0xffd8;
+ tag = hton16( tag );
+ p_field = memmem( p, sync.length - (size_t)(p - p_frame), &tag, 2 );
+
+ if( p_field )
+ {
+ i_size = (uint32_t)(p_field - p_frame);
+ i_size = hton32( i_size );
+ memcpy( &p_app1->i_next_field, &i_size, 4 );
+
+ /* UNALIGNED! */
+ p_app1 = (struct quicktime_mjpeg_app1 *)(p_field + 6);
+ tag = 0xffd9;
+ tag = hton16( tag );
+ p = memmem( p_field, sync.length - (size_t)(p_field - p_frame),
+ &tag, 2 );
+
+ if( !p )
{
- p_sys->pic.p[i].p_pixels = p_sys->pic.p[i-1].p_pixels +
- p_sys->pic.p[i-1].i_pitch * p_sys->pic.p[i-1].i_lines;
+ /* sometimes the second field doesn't have the EOI. just put it
+ * there
+ */
+ p = p_frame + sync.length;
+ memcpy( p, &tag, 2 );
+ sync.length += 2;
}
- p_sys->i_video_frame_size = p_sys->i_video_frame_size_allocated;
- p_sys->p_encoder->pf_encode( p_sys->p_encoder, &p_sys->pic,
- p_sys->p_video_frame,
- &p_sys->i_video_frame_size );
+ p += 2;
+ i_size = (uint32_t)(p - p_field);
+ i_size = hton32( i_size );
+ memcpy( &p_app1->i_field_size, &i_size, 4 );
+ i_size = (uint32_t)(sync.length - (uint32_t)(p_field - p_frame));
+ i_size = hton32( i_size );
+ memcpy( &p_app1->i_padded_field_size, &i_size, 4 );
}
+
+ p_sys->i_video_frame_size = sync.length;
+ return p_frame;
+}
+
+static int GrabVideo( input_thread_t * p_input,
+ uint8_t **pp_data,
+ int *pi_data,
+ mtime_t *pi_pts )
+{
+ access_sys_t *p_sys = p_input->p_access_data;
+ uint8_t *p_frame;
+
+ if( p_sys->b_mjpeg )
+ p_frame = GrabMJPEG( p_input );
else
- {
- p_sys->p_video_frame = p_sys->p_video_mmap +
- p_sys->vid_mbuf.offsets[p_sys->i_frame_pos];
- }
+ p_frame = GrabCapture( p_input );
+
+ if( !p_frame )
+ return -1;
+
+ p_sys->p_video_frame = p_frame;
*pp_data = p_sys->p_video_frame;
*pi_data = p_sys->i_video_frame_size;
}
p_input->pf_demux = Demux;
+ p_input->pf_demux_control = demux_vaControlDefault;
return VLC_SUCCESS;
}
{
msg_Err( p_input, "cannot find ES" );
}
-#if 0
- if( p_es == NULL || p_es->p_decoder_fifo == NULL )
- {
- msg_Err( p_input, "cannot find decoder" );
- return 1;
- }
-#endif
+
p_pes = input_NewPES( p_input->p_method_data );
if( p_pes == NULL )
{