* v4l.c : Video4Linux input module for vlc
*****************************************************************************
* Copyright (C) 2002 VideoLAN
- * $Id: v4l.c,v 1.16 2003/05/15 22:27:36 massiot Exp $
+ * $Id: v4l.c,v 1.17 2003/05/31 01:23:29 fenrir Exp $
*
* Author: Samuel Hocevar <sam@zoy.org>
+ * 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 <fcntl.h>
#include <linux/videodev.h>
+#include "videodev_mjpeg.h"
#include <sys/soundcard.h>
/****************************************************************************
* 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 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;
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->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 != ':' )
msg_Err( p_input, "cannot set audio" );
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" );
+ 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)" );
+ 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" );
+ 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" );
+ 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" );
+ 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" );
+ 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;
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
+ p_sys->i_chroma = VLC_FOURCC( 'I','4','2','0' );
+ }
+ 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;
+ 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_chroma = 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_codec = VLC_FOURCC( 'm','j','p','g' );
+ p_sys->p_encoder = NULL;
+ 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 );
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;
- }
-
- /* 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;
+ msg_Dbg( p_input, "v4l device uses chroma: %4.4s",
+ (char*)&p_sys->i_chroma );
+ /* Allocate mmap buffer */
+ if( ioctl( p_sys->fd, VIDIOCGMBUF, &p_sys->vid_mbuf ) < 0 )
+ {
+ msg_Err( p_input, "mmap unsupported" );
+ goto failed;
+ }
- p_enc->p_module = module_Need( p_enc, "video encoder",
- "$video-encoder" );
- if( !p_enc->p_module )
+ 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_Warn( p_input, "no suitable encoder to %4.4s",
- (char*)&p_enc->i_codec );
- vlc_object_destroy( p_enc );
+ /* FIXME -> normal read */
+ msg_Err( p_input, "mmap failed" );
goto failed;
}
- /* *** init the codec *** */
- if( p_enc->pf_init( p_enc ) )
+ /* 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, "failed to initialize video encoder plugin" );
- vlc_object_destroy( p_enc );
+ msg_Warn( p_input, "%4.4s refused", (char*)&p_sys->i_chroma );
+ msg_Err( p_input, "chroma selection failed" );
goto failed;
}
- /* *** alloacted buffer *** */
- if( p_enc->i_buffer_size <= 0 )
+ /* encoder part */
+ if( p_sys->i_codec != VLC_FOURCC( 0, 0, 0, 0 ) )
{
- p_enc->i_buffer_size = 1024 * 1024;// * p_enc->i_width * p_enc->i_height;
+ 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 )
+ {
+ msg_Warn( p_input, "no suitable encoder to %4.4s",
+ (char*)&p_enc->i_codec );
+ vlc_object_destroy( p_enc );
+ goto failed;
+ }
+
+ /* *** init the codec *** */
+ if( p_enc->pf_init( p_enc ) )
+ {
+ msg_Err( p_input, "failed to initialize video encoder plugin" );
+ vlc_object_destroy( p_enc );
+ 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 ) ) )
+ {
+ msg_Err( p_input, "out of memory" );
+ goto failed;
+ }
+#undef p_enc
}
- 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 ) ) )
+ else
{
- msg_Err( p_input, "out of memory" );
- goto failed;
+ p_sys->i_codec = p_sys->i_chroma;
+ p_sys->p_encoder = NULL;
}
-#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;
p_input->pf_set_area = NULL;
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 )
{
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;
+
+ /* look for EOI */
+ p = memmem( p_field, sync.length, &tag, 2 );
+
+ if( p )
+ {
+ p += 2; /* data immediately following EOI */
+ /* UNALIGNED! */
+ p_app1 = (struct quicktime_mjpeg_app1 *)(p_field + 6);
+
+ i_size = ((uint32_t)(p - p_field));
+ i_size = hton32( i_size );
+ memcpy( &p_app1->i_field_size, &i_size, 4 );
+
+ 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 )
+ {
+ /* 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 += 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_frame = GrabCapture( p_input );
+
+ if( !p_frame )
+ return -1;
if( p_sys->p_encoder )
{
int i;
+ /* notice we can't get here if we are using mjpeg */
- p_sys->pic.p[0].p_pixels = p_sys->p_video_mmap +
- p_sys->vid_mbuf.offsets[p_sys->i_frame_pos];
+ p_sys->pic.p[0].p_pixels = p_frame;
for( i = 1; i < p_sys->pic.i_planes; i++ )
{
}
else
{
- p_sys->p_video_frame = p_sys->p_video_mmap +
- p_sys->vid_mbuf.offsets[p_sys->i_frame_pos];
+ p_sys->p_video_frame = p_frame;
}
*pp_data = p_sys->p_video_frame;
--- /dev/null
+/*****************************************************************************
+ * Copyright (C) lavrec (see http://mjpeg.sourceforge.net)
+ * ( XXX This file was get from the driver-zoran package and it is under GPL)
+ *
+ * $Id: videodev_mjpeg.h,v 1.1 2003/05/31 01:23:29 fenrir Exp $
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
+ *****************************************************************************/
+
+/* These are the MJPEG API extensions for the Video4Linux API,
+ first introduced by the Iomega Buz driver by Rainer Johanni
+ <rainer@johanni.de>
+*/
+
+/* This is identical with the mgavideo internal params struct,
+ please tell me if you change this struct here ! <gz@lysator.liu.se) */
+struct mjpeg_params
+{
+
+ /* The following parameters can only be queried */
+
+ int major_version; /* Major version number of driver */
+ int minor_version; /* Minor version number of driver */
+
+ /* Main control parameters */
+
+ int input; /* Input channel: 0 = Composite, 1 = S-VHS */
+ int norm; /* Norm: VIDEO_MODE_PAL or VIDEO_MODE_NTSC */
+ int decimation; /* decimation of captured video,
+ enlargement of video played back.
+ Valid values are 1, 2, 4 or 0.
+ 0 is a special value where the user
+ has full control over video scaling */
+
+ /* The following parameters only have to be set if decimation==0,
+ for other values of decimation they provide the data how the image is captured */
+
+ int HorDcm; /* Horizontal decimation: 1, 2 or 4 */
+ int VerDcm; /* Vertical decimation: 1 or 2 */
+ int TmpDcm; /* Temporal decimation: 1 or 2,
+ if TmpDcm==2 in capture every second frame is dropped,
+ in playback every frame is played twice */
+ int field_per_buff; /* Number of fields per buffer: 1 or 2 */
+ int img_x; /* start of image in x direction */
+ int img_y; /* start of image in y direction */
+ int img_width; /* image width BEFORE decimation,
+ must be a multiple of HorDcm*16 */
+ int img_height; /* image height BEFORE decimation,
+ must be a multiple of VerDcm*8 */
+
+ /* --- End of parameters for decimation==0 only --- */
+
+ /* JPEG control parameters */
+
+ int quality; /* Measure for quality of compressed images.
+ Scales linearly with the size of the compressed images.
+ Must be beetween 0 and 100, 100 is a compression
+ ratio of 1:4 */
+
+ int odd_even; /* Which field should come first ???
+ This is more aptly named "top_first",
+ i.e. (odd_even==1) --> top-field-first */
+
+ int APPn; /* Number of APP segment to be written, must be 0..15 */
+ int APP_len; /* Length of data in JPEG APPn segment */
+ char APP_data[60]; /* Data in the JPEG APPn segment. */
+
+ int COM_len; /* Length of data in JPEG COM segment */
+ char COM_data[60]; /* Data in JPEG COM segment */
+
+ unsigned long jpeg_markers; /* Which markers should go into the JPEG output.
+ Unless you exactly know what you do, leave them untouched.
+ Inluding less markers will make the resulting code
+ smaller, but there will be fewer aplications
+ which can read it.
+ The presence of the APP and COM marker is
+ influenced by APP0_len and COM_len ONLY! */
+#define JPEG_MARKER_DHT (1<<3) /* Define Huffman Tables */
+#define JPEG_MARKER_DQT (1<<4) /* Define Quantization Tables */
+#define JPEG_MARKER_DRI (1<<5) /* Define Restart Interval */
+#define JPEG_MARKER_COM (1<<6) /* Comment segment */
+#define JPEG_MARKER_APP (1<<7) /* App segment, driver will allways use APP0 */
+
+ int VFIFO_FB; /* Flag for enabling Video Fifo Feedback.
+ If this flag is turned on and JPEG decompressing
+ is going to the screen, the decompress process
+ is stopped every time the Video Fifo is full.
+ This enables a smooth decompress to the screen
+ but the video output signal will get scrambled */
+
+ /* Misc */
+
+ char reserved[312]; /* Makes 512 bytes for this structure */
+};
+
+struct mjpeg_requestbuffers
+{
+ unsigned long count; /* Number of buffers for MJPEG grabbing */
+ unsigned long size; /* Size PER BUFFER in bytes */
+};
+
+struct mjpeg_sync
+{
+ unsigned long frame; /* Frame (0 - n) for double buffer */
+ unsigned long length; /* number of code bytes in buffer (capture only) */
+ unsigned long seq; /* frame sequence number */
+ struct timeval timestamp; /* timestamp */
+};
+
+struct mjpeg_status
+{
+ int input; /* Input channel, has to be set prior to BUZIOC_G_STATUS */
+ int signal; /* Returned: 1 if valid video signal detected */
+ int norm; /* Returned: VIDEO_MODE_PAL or VIDEO_MODE_NTSC */
+ int color; /* Returned: 1 if color signal detected */
+};
+
+/*
+Private IOCTL to set up for displaying MJPEG
+*/
+#define MJPIOC_G_PARAMS _IOR ('v', BASE_VIDIOCPRIVATE+0, struct mjpeg_params)
+#define MJPIOC_S_PARAMS _IOWR('v', BASE_VIDIOCPRIVATE+1, struct mjpeg_params)
+#define MJPIOC_REQBUFS _IOWR('v', BASE_VIDIOCPRIVATE+2, struct mjpeg_requestbuffers)
+#define MJPIOC_QBUF_CAPT _IOW ('v', BASE_VIDIOCPRIVATE+3, int)
+#define MJPIOC_QBUF_PLAY _IOW ('v', BASE_VIDIOCPRIVATE+4, int)
+#define MJPIOC_SYNC _IOR ('v', BASE_VIDIOCPRIVATE+5, struct mjpeg_sync)
+#define MJPIOC_G_STATUS _IOWR('v', BASE_VIDIOCPRIVATE+6, struct mjpeg_status)