Commit 4dfaa105 authored by Rémi Denis-Courmont's avatar Rémi Denis-Courmont

Partially segregate access_demux V4L2 code

parent 21ecdaab
......@@ -132,6 +132,7 @@ libvlc_LTLIBRARIES += $(LTLIBaccess_shm)
libv4l2_plugin_la_SOURCES = \
v4l2/video.c \
v4l2/demux.c \
v4l2/access.c \
v4l2/controls.c \
v4l2/v4l2.h
......
/*****************************************************************************
* demux.c : V4L2 raw video demux module for vlc
*****************************************************************************
* Copyright (C) 2002-2011 the VideoLAN team
*
* Authors: Benjamin Pracht <bigben at videolan dot org>
* Richard Hosking <richard at hovis dot net>
* Antoine Cellerier <dionoea at videolan d.t org>
* Dennis Lou <dlou99 at yahoo 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
* 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "v4l2.h"
#include <vlc_demux.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <poll.h>
static int DemuxControl( demux_t *, int, va_list );
static int Demux( demux_t * );
int DemuxOpen( vlc_object_t *obj )
{
demux_t *demux = (demux_t *)obj;
demux_sys_t *sys = calloc( 1, sizeof( demux_sys_t ) );
if( unlikely(sys == NULL) )
return VLC_ENOMEM;
demux->p_sys = sys;
ParseMRL( obj, demux->psz_location );
sys->i_fd = OpenVideo( obj, sys, true );
if( sys->i_fd == -1 )
{
free( sys );
return VLC_EGENERIC;
}
demux->pf_demux = Demux;
demux->pf_control = DemuxControl;
demux->info.i_update = 0;
demux->info.i_title = 0;
demux->info.i_seekpoint = 0;
return VLC_SUCCESS;
}
void DemuxClose( vlc_object_t *obj )
{
demux_t *demux = (demux_t *)obj;
demux_sys_t *sys = demux->p_sys;
int fd = sys->i_fd;
/* Stop video capture */
switch( sys->io )
{
case IO_METHOD_READ:
/* Nothing to do */
break;
case IO_METHOD_MMAP:
case IO_METHOD_USERPTR:
{
/* NOTE: Some buggy drivers hang if buffers are not unmapped before
* streamoff */
for( unsigned i = 0; i < sys->i_nbuffers; i++ )
{
struct v4l2_buffer buf = {
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
.memory = ( sys->io == IO_METHOD_USERPTR ) ?
V4L2_MEMORY_USERPTR : V4L2_MEMORY_MMAP,
};
v4l2_ioctl( fd, VIDIOC_DQBUF, &buf );
}
enum v4l2_buf_type buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
v4l2_ioctl( sys->i_fd, VIDIOC_STREAMOFF, &buf_type );
break;
}
}
/* Free Video Buffers */
if( sys->p_buffers ) {
switch( sys->io )
{
case IO_METHOD_READ:
free( sys->p_buffers[0].start );
break;
case IO_METHOD_MMAP:
for( unsigned i = 0; i < sys->i_nbuffers; ++i )
v4l2_munmap( sys->p_buffers[i].start,
sys->p_buffers[i].length );
break;
case IO_METHOD_USERPTR:
for( unsigned i = 0; i < sys->i_nbuffers; ++i )
free( sys->p_buffers[i].start );
break;
}
free( sys->p_buffers );
}
v4l2_close( fd );
free( sys );
}
static int DemuxControl( demux_t *demux, int query, va_list args )
{
switch( query )
{
/* Special for access_demux */
case DEMUX_CAN_PAUSE:
case DEMUX_CAN_SEEK:
case DEMUX_CAN_CONTROL_PACE:
*va_arg( args, bool * ) = false;
return VLC_SUCCESS;
case DEMUX_GET_PTS_DELAY:
*va_arg(args,int64_t *) = INT64_C(1000)
* var_InheritInteger( demux, "live-caching" );
return VLC_SUCCESS;
case DEMUX_GET_TIME:
*va_arg( args, int64_t * ) = mdate();
return VLC_SUCCESS;
/* TODO implement others */
default:
return VLC_EGENERIC;
}
return VLC_EGENERIC;
}
static int Demux( demux_t *demux )
{
demux_sys_t *sys = demux->p_sys;
struct pollfd fd;
fd.fd = sys->i_fd;
fd.events = POLLIN|POLLPRI;
/* Wait for data */
/* FIXME: remove timeout */
while( poll( &fd, 1, 500 ) == -1 )
if( errno != EINTR )
{
msg_Err( demux, "poll error: %m" );
return -1;
}
if( fd.revents )
{
block_t *p_block = GrabVideo( VLC_OBJECT(demux), sys );
if( p_block )
{
es_out_Control( demux->out, ES_OUT_SET_PCR, p_block->i_pts );
es_out_Send( demux->out, sys->p_es, p_block );
}
}
return 1;
}
static float GetMaxFPS( int fd, uint32_t pixel_format,
uint32_t width, uint32_t height )
{
#ifdef VIDIOC_ENUM_FRAMEINTERVALS
/* This is new in Linux 2.6.19 */
struct v4l2_frmivalenum fie = {
.pixel_format = pixel_format,
.width = width,
.height = height,
};
if( v4l2_ioctl( fd, VIDIOC_ENUM_FRAMEINTERVALS, &fie ) < 0 )
return -1.;
switch( fie.type )
{
case V4L2_FRMIVAL_TYPE_DISCRETE:
{
float max = -1.;
do
{
float fps = (float)fie.discrete.denominator
/ (float)fie.discrete.numerator;
if( fps > max )
max = fps;
fie.index++;
} while( v4l2_ioctl( fd, VIDIOC_ENUM_FRAMEINTERVALS, &fie ) >= 0 );
return max;
}
case V4L2_FRMSIZE_TYPE_STEPWISE:
case V4L2_FRMIVAL_TYPE_CONTINUOUS:
return __MAX( (float)fie.stepwise.max.denominator
/ (float)fie.stepwise.max.numerator,
(float)fie.stepwise.min.denominator
/ (float)fie.stepwise.min.numerator );
}
#endif
return -1.;
}
float GetAbsoluteMaxFrameRate( vlc_object_t *obj, int fd,
uint32_t pixel_format )
{
#ifdef VIDIOC_ENUM_FRAMESIZES
/* This is new in Linux 2.6.19 */
struct v4l2_frmsizeenum fse = {
.pixel_format = pixel_format
};
if( v4l2_ioctl( fd, VIDIOC_ENUM_FRAMESIZES, &fse ) < 0 )
return -1.;
float max = -1.;
switch( fse.type )
{
case V4L2_FRMSIZE_TYPE_DISCRETE:
do
{
float fps = GetMaxFPS( fd, pixel_format, fse.discrete.width,
fse.discrete.height );
if( fps > max )
max = fps;
fse.index++;
} while( v4l2_ioctl( fd, VIDIOC_ENUM_FRAMESIZES, &fse ) >= 0 );
break;
case V4L2_FRMSIZE_TYPE_STEPWISE:
for( uint32_t width = fse.stepwise.min_width,
height = fse.stepwise.min_height;
width <= fse.stepwise.max_width
&& height <= fse.stepwise.max_width;
width += fse.stepwise.step_width,
height += fse.stepwise.step_height )
{
float fps = GetMaxFPS( fd, pixel_format, width, height );
if( fps > max )
max = fps;
}
break;
case V4L2_FRMSIZE_TYPE_CONTINUOUS:
/* FIXME */
msg_Err( obj, "V4L2_FRMSIZE_TYPE_CONTINUOUS support incorrect" );
max = GetMaxFPS( fd, pixel_format, fse.stepwise.max_width,
fse.stepwise.max_height );
break;
}
return max;
#else
return -1.;
#endif
}
void GetMaxDimensions( vlc_object_t *obj, int fd, uint32_t pixel_format,
float fps_min, uint32_t *pwidth, uint32_t *pheight )
{
*pwidth = 0;
*pheight = 0;
#ifdef VIDIOC_ENUM_FRAMESIZES
/* This is new in Linux 2.6.19 */
struct v4l2_frmsizeenum fse = {
.pixel_format = pixel_format
};
if( v4l2_ioctl( fd, VIDIOC_ENUM_FRAMESIZES, &fse ) < 0 )
return;
switch( fse.type )
{
case V4L2_FRMSIZE_TYPE_DISCRETE:
do
{
float fps = GetMaxFPS( fd, pixel_format, fse.discrete.width,
fse.discrete.height );
if( fps >= fps_min && fse.discrete.width > *pwidth )
{
*pwidth = fse.discrete.width;
*pheight = fse.discrete.height;
}
fse.index++;
} while( v4l2_ioctl( fd, VIDIOC_ENUM_FRAMESIZES, &fse ) >= 0 );
break;
case V4L2_FRMSIZE_TYPE_STEPWISE:
for( uint32_t width = fse.stepwise.min_width,
height = fse.stepwise.min_height;
width <= fse.stepwise.max_width
&& height <= fse.stepwise.max_width;
width += fse.stepwise.step_width,
height += fse.stepwise.step_height )
{
float fps = GetMaxFPS( fd, pixel_format, width, height );
if( fps >= fps_min && width > *pwidth )
{
*pwidth = width;
*pheight = height;
}
}
break;
case V4L2_FRMSIZE_TYPE_CONTINUOUS:
{
float fps = GetMaxFPS( fd, pixel_format, fse.stepwise.max_width,
fse.stepwise.max_height );
msg_Err( obj, "V4L2_FRMSIZE_TYPE_CONTINUOUS support incorrect" );
if( fps >= fps_min && fse.stepwise.max_width > *pwidth )
{
*pwidth = fse.stepwise.max_width;
*pheight = fse.stepwise.max_height;
}
break;
}
}
#endif
}
......@@ -71,11 +71,24 @@ struct demux_sys_t
#endif
};
struct buffer_t
{
void * start;
size_t length;
};
/* video.c */
void ParseMRL(vlc_object_t *, const char *);
int OpenVideo(vlc_object_t *, demux_sys_t *, bool);
block_t* GrabVideo(vlc_object_t *, demux_sys_t *);
/* demux.c */
int DemuxOpen(vlc_object_t *);
void DemuxClose(vlc_object_t *);
float GetAbsoluteMaxFrameRate(vlc_object_t *, int fd, uint32_t fmt);
void GetMaxDimensions(vlc_object_t *, int fd, uint32_t fmt, float fps_min,
uint32_t *pwidth, uint32_t *pheight);
/* access.c */
int AccessOpen(vlc_object_t *);
void AccessClose(vlc_object_t *);
......@@ -54,9 +54,6 @@
* Module descriptior
*****************************************************************************/
static int DemuxOpen ( vlc_object_t * );
static void DemuxClose( vlc_object_t * );
#define DEVICE_TEXT N_( "Device" )
#define DEVICE_LONGTEXT N_( \
"Video device (Default: /dev/video0)." )
......@@ -403,9 +400,6 @@ vlc_module_end ()
* Access: local prototypes
*****************************************************************************/
static int DemuxControl( demux_t *, int, va_list );
static int Demux( demux_t * );
static block_t* ProcessVideoFrame( vlc_object_t *p_demux, uint8_t *p_frame, size_t );
static const struct
......@@ -465,44 +459,6 @@ static const uint32_t p_chroma_fallbacks[] =
V4L2_PIX_FMT_YUYV, V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_BGR24,
V4L2_PIX_FMT_BGR32, V4L2_PIX_FMT_MJPEG, V4L2_PIX_FMT_JPEG };
struct buffer_t
{
void * start;
size_t length;
};
/*****************************************************************************
* DemuxOpen: opens v4l2 device, access_demux callback
*****************************************************************************
*
* url: <video device>::::
*
*****************************************************************************/
static int DemuxOpen( vlc_object_t *p_this )
{
demux_t *p_demux = (demux_t*)p_this;
demux_sys_t *p_sys;
/* Set up p_demux */
p_demux->pf_control = DemuxControl;
p_demux->pf_demux = Demux;
p_demux->info.i_update = 0;
p_demux->info.i_title = 0;
p_demux->info.i_seekpoint = 0;
p_demux->p_sys = p_sys = calloc( 1, sizeof( demux_sys_t ) );
if( p_sys == NULL ) return VLC_ENOMEM;
ParseMRL( p_this, p_demux->psz_location );
p_sys->i_fd = OpenVideo( p_this, p_sys, true );
if( p_sys->i_fd == -1 )
{
free( p_sys );
return VLC_EGENERIC;
}
return VLC_SUCCESS;
}
/**
* Parses a V4L2 MRL into VLC object variables.
*/
......@@ -531,145 +487,6 @@ void ParseMRL( vlc_object_t *obj, const char *mrl )
}
}
/*****************************************************************************
* Close: close device, free resources
*****************************************************************************/
static void DemuxClose( vlc_object_t *p_this )
{
struct v4l2_buffer buf;
enum v4l2_buf_type buf_type;
unsigned int i;
demux_t *p_demux = (demux_t *)p_this;
demux_sys_t *p_sys = p_demux->p_sys;
/* Stop video capture */
if( p_sys->i_fd >= 0 )
{
switch( p_sys->io )
{
case IO_METHOD_READ:
/* Nothing to do */
break;
case IO_METHOD_MMAP:
case IO_METHOD_USERPTR:
/* Some drivers 'hang' internally if this is not done before streamoff */
for( unsigned int i = 0; i < p_sys->i_nbuffers; i++ )
{
memset( &buf, 0, sizeof(buf) );
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = ( p_sys->io == IO_METHOD_USERPTR ) ?
V4L2_MEMORY_USERPTR : V4L2_MEMORY_MMAP;
v4l2_ioctl( p_sys->i_fd, VIDIOC_DQBUF, &buf ); /* ignore result */
}
buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if( v4l2_ioctl( p_sys->i_fd, VIDIOC_STREAMOFF, &buf_type ) < 0 ) {
msg_Err( p_this, "VIDIOC_STREAMOFF failed" );
}
break;
}
}
/* Free Video Buffers */
if( p_sys->p_buffers ) {
switch( p_sys->io )
{
case IO_METHOD_READ:
free( p_sys->p_buffers[0].start );
break;
case IO_METHOD_MMAP:
for( i = 0; i < p_sys->i_nbuffers; ++i )
{
if( v4l2_munmap( p_sys->p_buffers[i].start, p_sys->p_buffers[i].length ) )
{
msg_Err( p_this, "munmap failed" );
}
}
break;
case IO_METHOD_USERPTR:
for( i = 0; i < p_sys->i_nbuffers; ++i )
{
free( p_sys->p_buffers[i].start );
}
break;
}
free( p_sys->p_buffers );
}
v4l2_close( p_sys->i_fd );
free( p_sys );
}
/*****************************************************************************
* DemuxControl:
*****************************************************************************/
static int DemuxControl( demux_t *p_demux, int i_query, va_list args )
{
switch( i_query )
{
/* Special for access_demux */
case DEMUX_CAN_PAUSE:
case DEMUX_CAN_SEEK:
case DEMUX_CAN_CONTROL_PACE:
*va_arg( args, bool * ) = false;
return VLC_SUCCESS;
case DEMUX_GET_PTS_DELAY:
*va_arg(args,int64_t *) = INT64_C(1000)
* var_InheritInteger( p_demux, "live-caching" );
return VLC_SUCCESS;
case DEMUX_GET_TIME:
*va_arg( args, int64_t * ) = mdate();
return VLC_SUCCESS;
/* TODO implement others */
default:
return VLC_EGENERIC;
}
return VLC_EGENERIC;
}
/*****************************************************************************
* Demux: Processes the audio or video frame
*****************************************************************************/
static int Demux( demux_t *p_demux )
{
demux_sys_t *p_sys = p_demux->p_sys;
struct pollfd fd;
fd.fd = p_sys->i_fd;
fd.events = POLLIN|POLLPRI;
fd.revents = 0;
/* Wait for data */
/* Timeout after 0.5 seconds since I don't know if pf_demux can be blocking. */
while( poll( &fd, 1, 500 ) == -1 )
if( errno != EINTR )
{
msg_Err( p_demux, "poll error: %m" );
return -1;
}
if( fd.revents )
{
block_t *p_block = GrabVideo( VLC_OBJECT(p_demux), p_sys );
if( p_block )
{
es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_block->i_pts );
es_out_Send( p_demux->out, p_sys->p_es, p_block );
}
}
return 1;
}
/*****************************************************************************
* GrabVideo: Grab a video frame
*****************************************************************************/
......@@ -951,166 +768,6 @@ static bool IsPixelFormatSupported( struct v4l2_fmtdesc *codecs, size_t n,
return false;
}
static float GetMaxFrameRate( int i_fd, uint32_t i_pixel_format,
uint32_t i_width, uint32_t i_height )
{
#ifdef VIDIOC_ENUM_FRAMEINTERVALS
/* This is new in Linux 2.6.19 */
struct v4l2_frmivalenum frmival;
memset( &frmival, 0, sizeof(frmival) );
frmival.pixel_format = i_pixel_format;
frmival.width = i_width;
frmival.height = i_height;
if( v4l2_ioctl( i_fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival ) >= 0 )
{
switch( frmival.type )
{
case V4L2_FRMIVAL_TYPE_DISCRETE:
{
float f_fps_max = -1;
do
{
float f_fps = (float)frmival.discrete.denominator
/ (float)frmival.discrete.numerator;
if( f_fps > f_fps_max ) f_fps_max = f_fps;
frmival.index++;
} while( v4l2_ioctl( i_fd, VIDIOC_ENUM_FRAMEINTERVALS,
&frmival ) >= 0 );
return f_fps_max;
}
case V4L2_FRMSIZE_TYPE_STEPWISE:
case V4L2_FRMIVAL_TYPE_CONTINUOUS:
return __MAX( (float)frmival.stepwise.max.denominator
/ (float)frmival.stepwise.max.numerator,
(float)frmival.stepwise.min.denominator
/ (float)frmival.stepwise.min.numerator );
}
}
#endif
return -1.;
}
static float GetAbsoluteMaxFrameRate( demux_t *p_demux, int i_fd,
uint32_t i_pixel_format )
{
float f_fps_max = -1.;
#ifdef VIDIOC_ENUM_FRAMESIZES
/* This is new in Linux 2.6.19 */
struct v4l2_frmsizeenum frmsize;
memset( &frmsize, 0, sizeof(frmsize) );
frmsize.pixel_format = i_pixel_format;
if( v4l2_ioctl( i_fd, VIDIOC_ENUM_FRAMESIZES, &frmsize ) >= 0 )
{
switch( frmsize.type )
{
case V4L2_FRMSIZE_TYPE_DISCRETE:
do
{
frmsize.index++;
float f_fps = GetMaxFrameRate( i_fd, i_pixel_format,
frmsize.discrete.width,
frmsize.discrete.height );
if( f_fps > f_fps_max ) f_fps_max = f_fps;
} while( v4l2_ioctl( i_fd, VIDIOC_ENUM_FRAMESIZES,
&frmsize ) >= 0 );
break;
case V4L2_FRMSIZE_TYPE_STEPWISE:
{
uint32_t i_width = frmsize.stepwise.min_width;
uint32_t i_height = frmsize.stepwise.min_height;
for( ;
i_width <= frmsize.stepwise.max_width &&
i_height <= frmsize.stepwise.max_width;
i_width += frmsize.stepwise.step_width,
i_height += frmsize.stepwise.step_height )
{
float f_fps = GetMaxFrameRate( i_fd, i_pixel_format,
i_width, i_height );
if( f_fps > f_fps_max ) f_fps_max = f_fps;
}
break;
}
case V4L2_FRMSIZE_TYPE_CONTINUOUS:
/* FIXME */
msg_Err( p_demux, "GetAbsoluteMaxFrameRate implementation for V4L2_FRMSIZE_TYPE_CONTINUOUS isn't correct" );
f_fps_max = GetMaxFrameRate( i_fd, i_pixel_format,
frmsize.stepwise.max_width,
frmsize.stepwise.max_height );
break;
}
}
#endif
return f_fps_max;
}
static void GetMaxDimensions( demux_t *p_demux, int i_fd,
uint32_t i_pixel_format, float f_fps_min,
uint32_t *pi_width, uint32_t *pi_height )
{
*pi_width = 0;
*pi_height = 0;
#ifdef VIDIOC_ENUM_FRAMESIZES
/* This is new in Linux 2.6.19 */
struct v4l2_frmsizeenum frmsize;
memset( &frmsize, 0, sizeof(frmsize) );
frmsize.pixel_format = i_pixel_format;