/*****************************************************************************
* linsys_hdsdi.c: HDSDI capture for Linear Systems/Computer Modules cards
*****************************************************************************
* Copyright (C) 2010-2011 VideoLAN
*
* Authors: Christophe Massiot <massiot@via.ecp.fr>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <poll.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/eventfd.h>
#include <fcntl.h>
#include <errno.h>
#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_input.h>
#include <vlc_access.h>
#include <vlc_demux.h>
#include <vlc_poll.h>
#include <vlc_fs.h>
#include "linsys_sdivideo.h"
#include "linsys_sdiaudio.h"
#undef HAVE_MMAP_SDIVIDEO
#undef HAVE_MMAP_SDIAUDIO
#define SDIVIDEO_DEVICE "/dev/sdivideorx%u"
#define SDIVIDEO_BUFFERS_FILE "/sys/class/sdivideo/sdivideorx%u/buffers"
#define SDIVIDEO_BUFSIZE_FILE "/sys/class/sdivideo/sdivideorx%u/bufsize"
#define SDIVIDEO_MODE_FILE "/sys/class/sdivideo/sdivideorx%u/mode"
#define SDIAUDIO_DEVICE "/dev/sdiaudiorx%u"
#define SDIAUDIO_BUFFERS_FILE "/sys/class/sdiaudio/sdiaudiorx%u/buffers"
#define SDIAUDIO_BUFSIZE_FILE "/sys/class/sdiaudio/sdiaudiorx%u/bufsize"
#define SDIAUDIO_SAMPLESIZE_FILE "/sys/class/sdiaudio/sdiaudiorx%u/sample_size"
#define SDIAUDIO_CHANNELS_FILE "/sys/class/sdiaudio/sdiaudiorx%u/channels"
#define NB_VBUFFERS 2
#define CLOCK_GAP VLC_TICK_FROM_MS(500)
#define START_DATE INT64_C(0x100000000)
#define MAX_AUDIOS 4
/*****************************************************************************
* Module descriptor
*****************************************************************************/
#define LINK_TEXT N_("Link #")
#define LINK_LONGTEXT N_( \
"Allows you to set the desired link of the board for the capture (starting at 0)." )
#define VIDEO_TEXT N_("Video ID")
#define VIDEO_LONGTEXT N_( \
"Allows you to set the ES ID of the video." )
#define VIDEO_ASPECT_TEXT N_("Aspect ratio")
#define VIDEO_ASPECT_LONGTEXT N_( \
"Allows you to force the aspect ratio of the video." )
#define AUDIO_TEXT N_("Audio configuration")
#define AUDIO_LONGTEXT N_( \
"Allows you to set audio configuration (id=group,pair:id=group,pair...)." )
static int Open ( vlc_object_t * );
static void Close( vlc_object_t * );
vlc_module_begin()
set_description( N_("HD-SDI Input") )
set_shortname( N_("HD-SDI") )
set_subcategory( SUBCAT_INPUT_ACCESS )
add_integer( "linsys-hdsdi-link", 0,
LINK_TEXT, LINK_LONGTEXT )
add_integer( "linsys-hdsdi-id-video", 0,
VIDEO_TEXT, VIDEO_LONGTEXT )
add_string( "linsys-hdsdi-aspect-ratio", "",
VIDEO_ASPECT_TEXT, VIDEO_ASPECT_LONGTEXT )
add_string( "linsys-hdsdi-audio", "0=1,1",
AUDIO_TEXT, AUDIO_LONGTEXT )
set_capability( "access", 0 )
add_shortcut( "linsys-hdsdi" )
set_callbacks( Open, Close )
vlc_module_end()
/*****************************************************************************
* Local prototypes
*****************************************************************************/
typedef struct hdsdi_audio_t
{
int i_channel; /* i_group * 2 + i_pair */
/* HDSDI parser */
int32_t i_delay;
/* ES stuff */
int i_id;
es_out_id_t *p_es;
} hdsdi_audio_t;
typedef struct
{
/* video device reader */
int i_vfd;
unsigned int i_link;
unsigned int i_standard;
#ifdef HAVE_MMAP_SDIVIDEO
uint8_t **pp_vbuffers;
unsigned int i_vbuffers, i_current_vbuffer;
#endif
unsigned int i_vbuffer_size;
/* audio device reader */
int i_afd;
int i_max_channel;
unsigned int i_sample_rate;
#ifdef HAVE_MMAP_SDIAUDIO
uint8_t **pp_abuffers;
unsigned int i_abuffers, i_current_abuffer;
#endif
unsigned int i_abuffer_size;
/* picture decoding */
unsigned int i_frame_rate, i_frame_rate_base;
unsigned int i_width, i_height, i_aspect, i_forced_aspect;
unsigned int i_vblock_size, i_ablock_size;
vlc_tick_t i_next_vdate, i_next_adate;
vlc_tick_t i_incr, i_aincr;
/* ES stuff */
int i_id_video;
es_out_id_t *p_es_video;
hdsdi_audio_t p_audios[MAX_AUDIOS];
pthread_t thread;
int evfd;
} demux_sys_t;
static int Control( demux_t *, int, va_list );
static void *Demux( void * );
static int InitCapture( demux_t *p_demux );
static void CloseCapture( demux_t *p_demux );
static int Capture( demux_t *p_demux );
/*****************************************************************************
* DemuxOpen:
*****************************************************************************/
static int Open( vlc_object_t *p_this )
{
demux_t *p_demux = (demux_t *)p_this;
demux_sys_t *p_sys;
char *psz_parser;
if (p_demux->out == NULL)
return VLC_EGENERIC;
/* Fill p_demux field */
p_demux->p_sys = p_sys = calloc( 1, sizeof( demux_sys_t ) );
if( unlikely(!p_sys) )
return VLC_ENOMEM;
/* HDSDI AR */
unsigned int i_num, i_den;
if ( !var_InheritURational( p_demux, &i_num, &i_den,
"linsys-hdsdi-aspect-ratio" ) && i_den != 0 )
p_sys->i_forced_aspect = p_sys->i_aspect =
i_num * VOUT_ASPECT_FACTOR / i_den;
else
p_sys->i_forced_aspect = 0;
/* */
p_sys->i_id_video = var_InheritInteger( p_demux, "linsys-hdsdi-id-video" );
/* Audio ES */
char *psz_string = psz_parser = var_InheritString( p_demux,
"linsys-hdsdi-audio" );
int i = 0;
p_sys->i_max_channel = -1;
while ( psz_parser != NULL && *psz_parser )
{
int i_id, i_group, i_pair;
char *psz_next = strchr( psz_parser, '=' );
if ( psz_next != NULL )
{
*psz_next = '\0';
i_id = strtol( psz_parser, NULL, 0 );
psz_parser = psz_next + 1;
}
else
i_id = 0;
psz_next = strchr( psz_parser, ':' );
if ( psz_next != NULL )
{
*psz_next = '\0';
psz_next++;
}
if ( sscanf( psz_parser, "%d,%d", &i_group, &i_pair ) == 2 )
{
p_sys->p_audios[i].i_channel = (i_group - 1) * 2 + (i_pair - 1);
if ( p_sys->p_audios[i].i_channel > p_sys->i_max_channel )
p_sys->i_max_channel = p_sys->p_audios[i].i_channel;
p_sys->p_audios[i].i_id = i_id;
i++;
}
else
msg_Warn( p_demux, "malformed audio configuration (%s)",
psz_parser );
psz_parser = psz_next;
}
free( psz_string );
for ( ; i < MAX_AUDIOS; i++ )
p_sys->p_audios[i].i_channel = -1;
p_sys->i_link = var_InheritInteger( p_demux, "linsys-hdsdi-link" );
p_sys->evfd = eventfd( 0, EFD_CLOEXEC );
if( p_sys->evfd == -1 )
goto error;
if( pthread_create( &p_sys->thread, NULL, Demux, p_demux ) )
{
vlc_close( p_sys->evfd );
goto error;
}
p_demux->pf_demux = NULL;
p_demux->pf_control = Control;
return VLC_SUCCESS;
error:
free( p_sys );
return VLC_EGENERIC;
}
/*****************************************************************************
* DemuxClose:
*****************************************************************************/
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;
write( p_sys->evfd, &(uint64_t){ 1 }, sizeof (uint64_t));
pthread_join( p_sys->thread, NULL );
vlc_close( p_sys->evfd );
free( p_sys );
}
/*****************************************************************************
* DemuxDemux:
*****************************************************************************/
static void *Demux( void *opaque )
{
demux_t *p_demux = opaque;
if( InitCapture( p_demux ) != VLC_SUCCESS )
return NULL;
while( Capture( p_demux ) == VLC_SUCCESS );
CloseCapture( p_demux );
return NULL;
}
/*****************************************************************************
* Control:
*****************************************************************************/
static int Control( demux_t *p_demux, int i_query, va_list args )
{
bool *pb;
switch( i_query )
{
/* Special for access_demux */
case DEMUX_CAN_PAUSE:
case DEMUX_CAN_CONTROL_PACE:
/* TODO */
pb = va_arg( args, bool * );
*pb = false;
return VLC_SUCCESS;
case DEMUX_GET_PTS_DELAY:
*va_arg( args, vlc_tick_t * ) =
VLC_TICK_FROM_MS(var_InheritInteger( p_demux, "live-caching" ));
return VLC_SUCCESS;
/* TODO implement others */
default:
return VLC_EGENERIC;
}
}
/*****************************************************************************
* HDSDI syntax parsing stuff
*****************************************************************************/
#define U (uint16_t)(p_line[0])
#define Y1 (uint16_t)(p_line[1])
#define V (uint16_t)(p_line[2])
#define Y2 (uint16_t)(p_line[3])
/* For lines 0 [4] or 1 [4] */
static void Unpack01( const uint8_t *p_line, unsigned int i_size,
uint8_t *p_y, uint8_t *p_u, uint8_t *p_v )
{
const uint8_t *p_end = p_line + i_size;
while ( p_line < p_end )
{
*p_u++ = U;
*p_y++ = Y1;
*p_v++ = V;
*p_y++ = Y2;
p_line += 4;
}
}
/* For lines 2 [4] */
static void Unpack2( const uint8_t *p_line, unsigned int i_size,
uint8_t *p_y, uint8_t *p_u, uint8_t *p_v )
{
const uint8_t *p_end = p_line + i_size;
while ( p_line < p_end )
{
uint16_t tmp;
tmp = 3 * *p_u;
tmp += U;
*p_u++ = tmp / 4;
*p_y++ = Y1;
tmp = 3 * *p_v;
tmp += V;
*p_v++ = tmp / 4;
*p_y++ = Y2;
p_line += 4;
}
}
/* For lines 3 [4] */
static void Unpack3( const uint8_t *p_line, unsigned int i_size,
uint8_t *p_y, uint8_t *p_u, uint8_t *p_v )
{
const uint8_t *p_end = p_line + i_size;
while ( p_line < p_end )
{
uint16_t tmp;
tmp = *p_u;
tmp += 3 * U;
*p_u++ = tmp / 4;
*p_y++ = Y1;
tmp = *p_v;
tmp += 3 * V;
*p_v++ = tmp / 4;
*p_y++ = Y2;
p_line += 4;
}
}
#undef U
#undef Y1
#undef V
#undef Y2
static void SparseCopy( int16_t *p_dest, const int16_t *p_src,
size_t i_nb_samples, size_t i_offset, size_t i_stride )
{
for ( size_t i = 0; i < i_nb_samples; i++ )
{
p_dest[2 * i] = p_src[i_offset];
p_dest[2 * i + 1] = p_src[i_offset + 1];
i_offset += 2 * i_stride;
}
}
/*****************************************************************************
* Video & audio decoding
*****************************************************************************/
struct block_extension_t
{
bool b_progressive; /**< is it a progressive frame ? */
bool b_top_field_first; /**< which field is first */
unsigned int i_nb_fields; /**< # of displayed fields */
unsigned int i_aspect; /**< aspect ratio of frame */
};
static void StopDecode( demux_t *p_demux )
{
demux_sys_t *p_sys = p_demux->p_sys;
es_out_Del( p_demux->out, p_sys->p_es_video );
for ( int i = 0; i < MAX_AUDIOS; i++ )
{
hdsdi_audio_t *p_audio = &p_sys->p_audios[i];
if ( p_audio->i_channel != -1 && p_audio->p_es != NULL )
{
es_out_Del( p_demux->out, p_audio->p_es );
p_audio->p_es = NULL;
}
}
}
static int InitVideo( demux_t *p_demux )
{
demux_sys_t *p_sys = p_demux->p_sys;
es_format_t fmt;
msg_Dbg( p_demux, "found standard %d", p_sys->i_standard );
switch ( p_sys->i_standard )
{
case SDIVIDEO_CTL_BT_601_576I_50HZ:
/* PAL */
p_sys->i_frame_rate = 25;
p_sys->i_frame_rate_base = 1;
p_sys->i_width = 720;
p_sys->i_height = 576;
p_sys->i_aspect = 4 * VOUT_ASPECT_FACTOR / 3;
break;
case SDIVIDEO_CTL_SMPTE_296M_720P_50HZ:
p_sys->i_frame_rate = 50;
p_sys->i_frame_rate_base = 1;
p_sys->i_width = 1280;
p_sys->i_height = 720;
p_sys->i_aspect = 16 * VOUT_ASPECT_FACTOR / 9;
break;
case SDIVIDEO_CTL_SMPTE_296M_720P_60HZ:
p_sys->i_frame_rate = 60;
p_sys->i_frame_rate_base = 1;
p_sys->i_width = 1280;
p_sys->i_height = 720;
p_sys->i_aspect = 16 * VOUT_ASPECT_FACTOR / 9;
break;
case SDIVIDEO_CTL_SMPTE_295M_1080I_50HZ:
case SDIVIDEO_CTL_SMPTE_274M_1080I_50HZ:
case SDIVIDEO_CTL_SMPTE_274M_1080PSF_25HZ:
/* 1080i50 or 1080p25 */
p_sys->i_frame_rate = 25;
p_sys->i_frame_rate_base = 1;
p_sys->i_width = 1920;
p_sys->i_height = 1080;
p_sys->i_aspect = 16 * VOUT_ASPECT_FACTOR / 9;
break;
case SDIVIDEO_CTL_SMPTE_274M_1080I_59_94HZ:
p_sys->i_frame_rate = 30000;
p_sys->i_frame_rate_base = 1001;
p_sys->i_width = 1920;
p_sys->i_height = 1080;
p_sys->i_aspect = 16 * VOUT_ASPECT_FACTOR / 9;
break;
case SDIVIDEO_CTL_SMPTE_274M_1080I_60HZ:
p_sys->i_frame_rate = 30;
p_sys->i_frame_rate_base = 1;
p_sys->i_width = 1920;
p_sys->i_height = 1080;
p_sys->i_aspect = 16 * VOUT_ASPECT_FACTOR / 9;
break;
default:
msg_Err( p_demux, "unsupported standard %d", p_sys->i_standard );
return VLC_EGENERIC;
}
p_sys->i_next_vdate = START_DATE;
p_sys->i_incr = vlc_tick_from_samples(p_sys->i_frame_rate_base, p_sys->i_frame_rate);
p_sys->i_vblock_size = p_sys->i_width * p_sys->i_height * 3 / 2
+ sizeof(struct block_extension_t);
/* Video ES */
es_format_Init( &fmt, VIDEO_ES, VLC_CODEC_I420 );
fmt.i_id = p_sys->i_id_video;
fmt.video.i_frame_rate = p_sys->i_frame_rate;
fmt.video.i_frame_rate_base = p_sys->i_frame_rate_base;
fmt.video.i_width = fmt.video.i_visible_width = p_sys->i_width;
fmt.video.i_height = fmt.video.i_visible_height = p_sys->i_height;
fmt.video.i_sar_num = p_sys->i_aspect * fmt.video.i_height
/ fmt.video.i_width;
fmt.video.i_sar_den = VOUT_ASPECT_FACTOR;
p_sys->p_es_video = es_out_Add( p_demux->out, &fmt );
return VLC_SUCCESS;
}
static int InitAudio( demux_t *p_demux )
{
demux_sys_t *p_sys = p_demux->p_sys;
es_format_t fmt;
for ( int i = 0; i < MAX_AUDIOS; i++ )
{
hdsdi_audio_t *p_audio = &p_sys->p_audios[i];
if ( p_audio->i_channel == -1 ) continue;
msg_Dbg( p_demux, "starting audio %u/%u rate:%u delay:%d",
1 + p_audio->i_channel / 2, 1 + (p_audio->i_channel % 2),
p_sys->i_sample_rate, p_audio->i_delay );
es_format_Init( &fmt, AUDIO_ES, VLC_CODEC_S16L );
fmt.i_id = p_audio->i_id;
fmt.audio.i_channels = 2;
fmt.audio.i_physical_channels = AOUT_CHANS_STEREO;
fmt.audio.i_rate = p_sys->i_sample_rate;
fmt.audio.i_bitspersample = 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;
p_audio->p_es = es_out_Add( p_demux->out, &fmt );
}
p_sys->i_next_adate = START_DATE;
p_sys->i_ablock_size = p_sys->i_sample_rate * 4 * p_sys->i_frame_rate_base / p_sys->i_frame_rate;
p_sys->i_aincr = vlc_tick_from_samples(p_sys->i_ablock_size, p_sys->i_sample_rate * 4);
return VLC_SUCCESS;
}
static int HandleVideo( demux_t *p_demux, const uint8_t *p_buffer )
{
demux_sys_t *p_sys = p_demux->p_sys;
block_t *p_current_picture = block_Alloc( p_sys->i_vblock_size );
if( unlikely( !p_current_picture ) )
return VLC_ENOMEM;
uint8_t *p_y = p_current_picture->p_buffer;
uint8_t *p_u = p_y + p_sys->i_width * p_sys->i_height;
uint8_t *p_v = p_u + p_sys->i_width * p_sys->i_height / 4;
unsigned int i_total_size = p_sys->i_width * 2;
unsigned int i_current_line;
struct block_extension_t ext;
for ( i_current_line = 0; i_current_line < p_sys->i_height;
i_current_line++ )
{
bool b_field = (i_current_line >= p_sys->i_height / 2);
unsigned int i_field_line = b_field ?
i_current_line - (p_sys->i_height + 1) / 2 :
i_current_line;
unsigned int i_real_line = b_field + i_field_line * 2;
const uint8_t *p_line = p_buffer + i_current_line * p_sys->i_width * 2;
if ( !(i_field_line % 2) && !b_field )
Unpack01( p_line, i_total_size,
p_y + p_sys->i_width * i_real_line,
p_u + (p_sys->i_width / 2) * (i_real_line / 2),
p_v + (p_sys->i_width / 2) * (i_real_line / 2) );
else if ( !(i_field_line % 2) )
Unpack01( p_line, i_total_size,
p_y + p_sys->i_width * i_real_line,
p_u + (p_sys->i_width / 2) * (i_real_line / 2 + 1),
p_v + (p_sys->i_width / 2) * (i_real_line / 2 + 1) );
else if ( !b_field )
Unpack2( p_line, i_total_size,
p_y + p_sys->i_width * i_real_line,
p_u + (p_sys->i_width / 2) * (i_real_line / 2 - 1),
p_v + (p_sys->i_width / 2) * (i_real_line / 2 - 1) );
else
Unpack3( p_line, i_total_size,
p_y + p_sys->i_width * i_real_line,
p_u + (p_sys->i_width / 2) * (i_real_line / 2),
p_v + (p_sys->i_width / 2) * (i_real_line / 2) );
}
/* FIXME: progressive formats ? */
ext.b_progressive = false;
ext.i_nb_fields = 2;
ext.b_top_field_first = true;
ext.i_aspect = p_sys->i_forced_aspect ? p_sys->i_forced_aspect :
p_sys->i_aspect;
memcpy( &p_current_picture->p_buffer[p_sys->i_vblock_size
- sizeof(struct block_extension_t)],
&ext, sizeof(struct block_extension_t) );
p_current_picture->i_dts = p_current_picture->i_pts = p_sys->i_next_vdate;
es_out_Send( p_demux->out, p_sys->p_es_video, p_current_picture );
es_out_SetPCR( p_demux->out, p_sys->i_next_vdate );
p_sys->i_next_vdate += p_sys->i_incr;
return VLC_SUCCESS;
}
static int HandleAudio( demux_t *p_demux, const uint8_t *p_buffer )
{
demux_sys_t *p_sys = p_demux->p_sys;
for ( int i = 0; i < MAX_AUDIOS; i++ )
{
hdsdi_audio_t *p_audio = &p_sys->p_audios[i];
if ( p_audio->i_channel != -1 && p_audio->p_es != NULL )
{
block_t *p_block = block_Alloc( p_sys->i_ablock_size );
if( unlikely( !p_block ) )
return VLC_ENOMEM;
SparseCopy( (int16_t *)p_block->p_buffer, (const int16_t *)p_buffer,
p_sys->i_ablock_size / 4,
p_audio->i_channel * 2, p_sys->i_max_channel + 1 );
p_block->i_dts = p_block->i_pts
= p_sys->i_next_adate + vlc_tick_from_samples(p_audio->i_delay,
p_sys->i_sample_rate);
p_block->i_length = p_sys->i_aincr;
es_out_Send( p_demux->out, p_audio->p_es, p_block );
}
}
p_sys->i_next_adate += p_sys->i_aincr;
return VLC_SUCCESS;
}
/*****************************************************************************
* Low-level device stuff
*****************************************************************************/
#define MAXLEN 256
static ssize_t WriteULSysfs( const char *psz_fmt, unsigned int i_link,
unsigned int i_buf )
{
char psz_file[MAXLEN], psz_data[MAXLEN];
int i_fd;
ssize_t i_ret;
snprintf( psz_file, sizeof(psz_file) -1, psz_fmt, i_link );
snprintf( psz_data, sizeof(psz_data) -1, "%u\n", i_buf );
if ( (i_fd = vlc_open( psz_file, O_WRONLY )) < 0 )
return i_fd;
i_ret = write( i_fd, psz_data, strlen(psz_data) + 1 );
vlc_close( i_fd );
return i_ret;
}
static int InitCapture( demux_t *p_demux )
{
demux_sys_t *p_sys = p_demux->p_sys;
#ifdef HAVE_MMAP_SDIVIDEO
const int i_page_size = getpagesize();
unsigned int i_bufmemsize;
#endif
char psz_vdev[MAXLEN];
snprintf( psz_vdev, sizeof(psz_vdev), SDIVIDEO_DEVICE, p_sys->i_link );
if ( (p_sys->i_vfd = vlc_open( psz_vdev, O_RDONLY ) ) < 0 )
{
msg_Err( p_demux, "couldn't open device %s", psz_vdev );
return VLC_EGENERIC;
}
/* Wait for standard to settle down */
struct pollfd pfd[2];
pfd[0].fd = p_sys->i_vfd;
pfd[0].events = POLLPRI;
pfd[1].fd = p_sys->evfd;
pfd[1].events = POLLIN;
for( ;; )
{
if( poll( pfd, 2, -1 ) < 0 )
continue;
if ( pfd[0].revents & POLLPRI )
{
unsigned int i_val;
if ( ioctl( p_sys->i_vfd, SDIVIDEO_IOC_RXGETEVENTS, &i_val ) < 0 )
msg_Warn( p_demux, "couldn't SDIVIDEO_IOC_RXGETEVENTS: %s",
vlc_strerror_c(errno) );
else
{
if ( i_val & SDIVIDEO_EVENT_RX_BUFFER )
msg_Warn( p_demux, "driver receive buffer queue overrun" );
if ( i_val & SDIVIDEO_EVENT_RX_FIFO )
msg_Warn( p_demux, "onboard receive FIFO overrun");
if ( i_val & SDIVIDEO_EVENT_RX_CARRIER )
msg_Warn( p_demux, "carrier status change");
if ( i_val & SDIVIDEO_EVENT_RX_DATA )
msg_Warn( p_demux, "data status change");
if ( i_val & SDIVIDEO_EVENT_RX_STD )
{
msg_Warn( p_demux, "standard status change");
break;
}
}
}
if( pfd[1].revents )
{
vlc_close( p_sys->i_vfd );
return VLC_EGENERIC;
}
}
if ( ioctl( p_sys->i_vfd, SDIVIDEO_IOC_RXGETVIDSTATUS, &p_sys->i_standard )
< 0 )
{
msg_Warn( p_demux, "couldn't SDIVIDEO_IOC_RXGETVIDSTATUS: %s",
vlc_strerror_c(errno) );
vlc_close( p_sys->i_vfd );
return VLC_EGENERIC;
}
vlc_close( p_sys->i_vfd );
if ( InitVideo( p_demux ) != VLC_SUCCESS )
return VLC_EGENERIC;
p_sys->i_vbuffer_size = p_sys->i_height * p_sys->i_width * 2;
/* First open the audio for synchronization reasons */
if ( p_sys->i_max_channel != -1 )
{
unsigned int i_rate;
char psz_adev[MAXLEN];
snprintf( psz_adev, sizeof(psz_adev), SDIAUDIO_DEVICE, p_sys->i_link );
if ( (p_sys->i_afd = vlc_open( psz_adev, O_RDONLY ) ) < 0 )
{
msg_Err( p_demux, "couldn't open device %s", psz_adev );
return VLC_EGENERIC;
}
if ( ioctl( p_sys->i_afd, SDIAUDIO_IOC_RXGETAUDRATE, &i_rate ) < 0 )
{
msg_Warn( p_demux, "couldn't SDIAUDIO_IOC_RXGETAUDRATE: %s",
vlc_strerror_c(errno) );
return VLC_EGENERIC;
}
switch ( i_rate )
{
case SDIAUDIO_CTL_ASYNC_48_KHZ:
case SDIAUDIO_CTL_SYNC_48_KHZ:
p_sys->i_sample_rate = 48000;
break;
case SDIAUDIO_CTL_ASYNC_44_1_KHZ:
case SDIAUDIO_CTL_SYNC_44_1_KHZ:
p_sys->i_sample_rate = 44100;
break;
case SDIAUDIO_CTL_ASYNC_32_KHZ:
case SDIAUDIO_CTL_SYNC_32_KHZ:
p_sys->i_sample_rate = 32000;
break;
case SDIAUDIO_CTL_ASYNC_96_KHZ:
case SDIAUDIO_CTL_SYNC_96_KHZ:
p_sys->i_sample_rate = 96000;
break;
case SDIAUDIO_CTL_ASYNC_FREE_RUNNING:
case SDIAUDIO_CTL_SYNC_FREE_RUNNING:
default:
msg_Err( p_demux, "unknown sample rate %u", i_rate );
return VLC_EGENERIC;
}
vlc_close( p_sys->i_afd );
if ( InitAudio( p_demux ) != VLC_SUCCESS )
return VLC_EGENERIC;
p_sys->i_abuffer_size = p_sys->i_ablock_size
* (1 + p_sys->i_max_channel);
/* Use 16-bit audio */
if ( WriteULSysfs( SDIAUDIO_SAMPLESIZE_FILE, p_sys->i_link,
SDIAUDIO_CTL_AUDSAMP_SZ_16 ) < 0 )
{
msg_Err( p_demux, "couldn't write file " SDIAUDIO_SAMPLESIZE_FILE,
p_sys->i_link );
return VLC_EGENERIC;
}
if ( WriteULSysfs( SDIAUDIO_CHANNELS_FILE, p_sys->i_link,
(p_sys->i_max_channel + 1) * 2 ) < 0 )
{
msg_Err( p_demux, "couldn't write file " SDIAUDIO_CHANNELS_FILE,
p_sys->i_link );
return VLC_EGENERIC;
}
#ifdef HAVE_MMAP_SDIAUDIO
if ( (p_sys->i_abuffers = ReadULSysfs( SDIAUDIO_BUFFERS_FILE,
p_sys->i_link )) < 0 )
{
msg_Err( p_demux, "couldn't read file " SDIAUDIO_BUFFERS_FILE,
p_sys->i_link );
return VLC_EGENERIC;
}
p_sys->i_current_abuffer = 0;
#endif
if ( WriteULSysfs( SDIAUDIO_BUFSIZE_FILE, p_sys->i_link,
p_sys->i_abuffer_size ) < 0 )
{
msg_Err( p_demux, "couldn't write file " SDIAUDIO_BUFSIZE_FILE,
p_sys->i_link );
return VLC_EGENERIC;
}
if ( (p_sys->i_afd = open( psz_adev, O_RDONLY ) ) < 0 )
{
msg_Err( p_demux, "couldn't open device %s", psz_adev );
return VLC_EGENERIC;
}
#ifdef HAVE_MMAP_SDIAUDIO
i_bufmemsize = ((p_sys->i_abuffer_size + i_page_size - 1) / i_page_size)
* i_page_size;
p_sys->pp_abuffers = vlc_alloc( p_sys->i_abuffers, sizeof(uint8_t *) );
if( unlikely( !p_sys->pp_abuffers ) )
return VLC_ENOMEM;
for ( unsigned int i = 0; i < p_sys->i_abuffers; i++ )
{
if ( (p_sys->pp_abuffers[i] = mmap( NULL, p_sys->i_abuffer_size,
PROT_READ, MAP_SHARED, p_sys->i_afd,
i * i_bufmemsize )) == MAP_FAILED )
{
msg_Err( p_demux, "couldn't mmap(%d): %s", i,
vlc_strerror_c(errno) );
return VLC_EGENERIC;
}
}
#endif
}
/* Use 8-bit video */
if ( WriteULSysfs( SDIVIDEO_MODE_FILE, p_sys->i_link,
SDIVIDEO_CTL_MODE_UYVY ) < 0 )
{
msg_Err( p_demux, "couldn't write file " SDIVIDEO_MODE_FILE,
p_sys->i_link );
return VLC_EGENERIC;
}
if ( WriteULSysfs( SDIVIDEO_BUFFERS_FILE, p_sys->i_link,
NB_VBUFFERS ) < 0 )
{
msg_Err( p_demux, "couldn't write file " SDIVIDEO_BUFFERS_FILE,
p_sys->i_link );
return VLC_EGENERIC;
}
#ifdef HAVE_MMAP_SDIVIDEO
p_sys->i_vbuffers = NB_VBUFFERS;
#endif
if ( WriteULSysfs( SDIVIDEO_BUFSIZE_FILE, p_sys->i_link,
p_sys->i_vbuffer_size ) < 0 )
{
msg_Err( p_demux, "couldn't write file " SDIVIDEO_BUFSIZE_FILE,
p_sys->i_link );
return VLC_EGENERIC;
}
if ( (p_sys->i_vfd = open( psz_vdev, O_RDONLY ) ) < 0 )
{
msg_Err( p_demux, "couldn't open device %s", psz_vdev );
return VLC_EGENERIC;
}
#ifdef HAVE_MMAP_SDIVIDEO
p_sys->i_current_vbuffer = 0;
i_bufmemsize = ((p_sys->i_vbuffer_size + i_page_size - 1) / i_page_size)
* i_page_size;
p_sys->pp_vbuffers = vlc_alloc( p_sys->i_vbuffers, sizeof(uint8_t *) );
if( unlikely( !p_sys->pp_vbuffers ) )
return VLC_ENOMEM;
for ( unsigned int i = 0; i < p_sys->i_vbuffers; i++ )
{
if ( (p_sys->pp_vbuffers[i] = mmap( NULL, p_sys->i_vbuffer_size,
PROT_READ, MAP_SHARED, p_sys->i_vfd,
i * i_bufmemsize )) == MAP_FAILED )
{
msg_Err( p_demux, "couldn't mmap(%d): %s", i,
vlc_strerror_c(errno) );
return VLC_EGENERIC;
}
}
#endif
return VLC_SUCCESS;
}
static void CloseCapture( demux_t *p_demux )
{
demux_sys_t *p_sys = p_demux->p_sys;
StopDecode( p_demux );
#ifdef HAVE_MMAP_SDIVIDEO
for ( unsigned int i = 0; i < p_sys->i_vbuffers; i++ )
munmap( p_sys->pp_vbuffers[i], p_sys->i_vbuffer_size );
free( p_sys->pp_vbuffers );
#endif
vlc_close( p_sys->i_vfd );
if ( p_sys->i_max_channel != -1 )
{
#ifdef HAVE_MMAP_SDIAUDIO
for ( unsigned int i = 0; i < p_sys->i_abuffers; i++ )
munmap( p_sys->pp_abuffers[i], p_sys->i_abuffer_size );
free( p_sys->pp_abuffers );
#endif
vlc_close( p_sys->i_afd );
}
}
static int Capture( demux_t *p_demux )
{
demux_sys_t *p_sys = p_demux->p_sys;
struct pollfd pfd[3];
pfd[0].fd = p_sys->evfd;
pfd[0].events = POLLIN;
pfd[1].fd = p_sys->i_vfd;
pfd[1].events = POLLIN | POLLPRI;
if ( p_sys->i_max_channel != -1 )
{
pfd[2].fd = p_sys->i_afd;
pfd[2].events = POLLIN | POLLPRI;
}
if( poll( pfd, 2 + (p_sys->i_max_channel != -1), -1 ) < 0 )
return VLC_SUCCESS;
if( pfd[0].revents )
return VLC_EGENERIC; /* Stop! */
if( pfd[1].revents & POLLPRI )
{
unsigned int i_val;
if ( ioctl( p_sys->i_vfd, SDIVIDEO_IOC_RXGETEVENTS, &i_val ) < 0 )
msg_Warn( p_demux, "couldn't SDIVIDEO_IOC_RXGETEVENTS: %s",
vlc_strerror_c(errno) );
else
{
if ( i_val & SDIVIDEO_EVENT_RX_BUFFER )
msg_Warn( p_demux, "driver receive buffer queue overrun" );
if ( i_val & SDIVIDEO_EVENT_RX_FIFO )
msg_Warn( p_demux, "onboard receive FIFO overrun");
if ( i_val & SDIVIDEO_EVENT_RX_CARRIER )
msg_Warn( p_demux, "carrier status change");
if ( i_val & SDIVIDEO_EVENT_RX_DATA )
msg_Warn( p_demux, "data status change");
if ( i_val & SDIVIDEO_EVENT_RX_STD )
msg_Warn( p_demux, "standard status change");
}
p_sys->i_next_adate += CLOCK_GAP;
p_sys->i_next_vdate += CLOCK_GAP;
}
if( p_sys->i_max_channel != -1 && (pfd[2].revents & POLLPRI) )
{
unsigned int i_val;
if ( ioctl( p_sys->i_afd, SDIAUDIO_IOC_RXGETEVENTS, &i_val ) < 0 )
msg_Warn( p_demux, "couldn't SDIAUDIO_IOC_RXGETEVENTS: %s",
vlc_strerror_c(errno) );
else
{
if ( i_val & SDIAUDIO_EVENT_RX_BUFFER )
msg_Warn( p_demux, "driver receive buffer queue overrun" );
if ( i_val & SDIAUDIO_EVENT_RX_FIFO )
msg_Warn( p_demux, "onboard receive FIFO overrun");
if ( i_val & SDIAUDIO_EVENT_RX_CARRIER )
msg_Warn( p_demux, "carrier status change");
if ( i_val & SDIAUDIO_EVENT_RX_DATA )
msg_Warn( p_demux, "data status change");
}
p_sys->i_next_adate += CLOCK_GAP;
p_sys->i_next_vdate += CLOCK_GAP;
}
if( pfd[1].revents & POLLIN )
{
#ifdef HAVE_MMAP_SDIVIDEO
if ( ioctl( p_sys->i_vfd, SDIVIDEO_IOC_DQBUF, p_sys->i_current_vbuffer )
< 0 )
{
msg_Warn( p_demux, "couldn't SDIVIDEO_IOC_DQBUF: %s",
vlc_strerror_c(errno) );
return VLC_EGENERIC;
}
if( HandleVideo( p_demux, p_sys->pp_vbuffers[p_sys->i_current_vbuffer] ) != VLC_SUCCESS )
return VLC_ENOMEM;
if ( ioctl( p_sys->i_vfd, SDIVIDEO_IOC_QBUF, p_sys->i_current_vbuffer )
< 0 )
{
msg_Warn( p_demux, "couldn't SDIVIDEO_IOC_QBUF: %s",
vlc_strerror_c(errno) );
return VLC_EGENERIC;
}
p_sys->i_current_vbuffer++;
p_sys->i_current_vbuffer %= p_sys->i_vbuffers;
#else
uint8_t *p_buffer = malloc( p_sys->i_vbuffer_size );
if( unlikely( !p_buffer ) )
return VLC_ENOMEM;
if ( read( p_sys->i_vfd, p_buffer, p_sys->i_vbuffer_size ) < 0 )
{
msg_Warn( p_demux, "couldn't read: %s", vlc_strerror_c(errno) );
free( p_buffer );
return VLC_EGENERIC;
}
if( HandleVideo( p_demux, p_buffer ) != VLC_SUCCESS )
{
free( p_buffer );
return VLC_ENOMEM;
}
free( p_buffer );
#endif
}
if( p_sys->i_max_channel != -1 && (pfd[2].revents & POLLIN) )
{
#ifdef HAVE_MMAP_SDIAUDIO
if ( ioctl( p_sys->i_afd, SDIAUDIO_IOC_DQBUF, p_sys->i_current_abuffer )
< 0 )
{
msg_Warn( p_demux, "couldn't SDIAUDIO_IOC_DQBUF: %s",
vlc_strerror_c(errno) );
return VLC_EGENERIC;
}
if( HandleAudio( p_demux, p_sys->pp_abuffers[p_sys->i_current_abuffer] ) != VLC_SUCCESS )
return VLC_ENOMEM;
if ( ioctl( p_sys->i_afd, SDIAUDIO_IOC_QBUF, p_sys->i_current_abuffer )
< 0 )
{
msg_Warn( p_demux, "couldn't SDIAUDIO_IOC_QBUF: %s",
vlc_strerror_c(errno) );
return VLC_EGENERIC;
}
p_sys->i_current_abuffer++;
p_sys->i_current_abuffer %= p_sys->i_abuffers;
#else
uint8_t *p_buffer = malloc( p_sys->i_abuffer_size );
if( unlikely( !p_buffer ) )
return VLC_ENOMEM;
if ( read( p_sys->i_afd, p_buffer, p_sys->i_abuffer_size ) < 0 )
{
msg_Warn( p_demux, "couldn't read: %s", vlc_strerror_c(errno) );
free( p_buffer );
return VLC_EGENERIC;
}
if( HandleAudio( p_demux, p_buffer ) != VLC_SUCCESS )
{
free( p_buffer );
return VLC_ENOMEM;
}
free( p_buffer );
#endif
}
return VLC_SUCCESS;
}
¤ Dauer der Verarbeitung: 0.13 Sekunden
¤
*© Formatika GbR, Deutschland
|
|