Commit 203a2937 authored by Christophe Massiot's avatar Christophe Massiot Committed by Jean-Baptiste Kempf

Add support for Linear Systems (HD-)SDI cards

 * modules/access/linsys/*: add support for SDI and HD-SDI cards of
   Linear Systems/Computer Modules
Signed-off-by: Jean-Baptiste Kempf's avatarJean-Baptiste Kempf <jb@videolan.org>
parent c14e81d2
......@@ -18,7 +18,8 @@ Access
* Support for VDR recordings (http://www.tvdr.de/) folders
* Blu-Ray Discs integration using libbluray
* HTTP Live Streaming (IETF draft) playback support
* Blackmagic DeckLink SDI card input support (Linux only currently)
* Blackmagic DeckLink SDI cards input support (Linux only currently)
* Linear Systems (HD-)SDI cards input support (Linux)
* RTP: support for dynamic payload types by specifying the payload format
in an option (no autodetection): only Theora supported for now
......
......@@ -1839,10 +1839,27 @@ fi
dnl
dnl - special access module for dc1394 input
dnl - dv module: digital video module check for libraw1394
dnl - linsys modules: access module check for libzvbi
dnl
PKG_ENABLE_MODULES_VLC([DC1394], [], [libraw1394 >= 2.0.1 libdc1394-2 >= 2.1.0], [dc1394 access module], [auto])
PKG_ENABLE_MODULES_VLC([DV], [access_dv], [libraw1394 >= 2.0.1 libavc1394 >= 0.5.3], [DV input module], [auto])
AC_ARG_ENABLE(linsys,
[ --enable-linsys Linux Linear Systems Ltd. SDI and HD-SDI input cards (default enabled)])
case "${SYS}" in
linux*)
if test "${enable_linsys}" != "no" -a "${SYS}" != "mingw32" -a "${SYS}" != "mingwce"; then
VLC_ADD_PLUGIN([linsys_hdsdi])
PKG_CHECK_MODULES(LINSYS_SDI, zvbi-0.2 >= 0.2.28,
[ VLC_ADD_LIBS([linsys_sdi],[$LINSYS_SDI_LIBS])
VLC_ADD_CFLAGS([linsys_sdi],[$LINSYS_SDI_CFLAGS])
VLC_ADD_PLUGIN([linsys_sdi]) ],
[AC_MSG_WARN([Couldn't find zvbi >= 0.2.28, install libzvbi-dev ?])]
)
fi
;;
esac
dnl
dnl dvdread module: check for libdvdread
dnl
......
......@@ -41,6 +41,8 @@ SOURCES_dc1394 = dc1394.c
SOURCES_pvr = pvr.c videodev2.h
SOURCES_v4l2 = v4l2.c
SOURCES_qtcapture = qtcapture.m
SOURCES_linsys_sdi = linsys/linsys_sdi.c
SOURCES_linsys_hdsdi = linsys/linsys_hdsdi.c
SOURCES_cdda = \
cdda.c \
vcd/cdrom.c \
......
/*****************************************************************************
* 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 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.
*****************************************************************************/
#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 <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_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 READ_TIMEOUT 80000
#define CLOCK_GAP INT64_C(500000)
#define START_DATE INT64_C(4294967296)
#define MAX_AUDIOS 4
/*****************************************************************************
* Module descriptor
*****************************************************************************/
#define CACHING_TEXT N_("Caching value in ms")
#define CACHING_LONGTEXT N_( \
"Allows you to modify the default caching value for hdsdi capture " \
"streams. This value should be set in millisecond units." )
#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( _("HDSDI Input") )
set_shortname( N_("hdsdi") )
set_category( CAT_INPUT )
set_subcategory( SUBCAT_INPUT_ACCESS )
add_integer( "linsys-hdsdi-caching", DEFAULT_PTS_DELAY / 1000,
CACHING_TEXT, CACHING_LONGTEXT, true )
add_integer( "linsys-hdsdi-link", 0,
LINK_TEXT, LINK_LONGTEXT, true )
add_integer( "linsys-hdsdi-id-video", 0,
VIDEO_TEXT, VIDEO_LONGTEXT, true )
add_string( "linsys-hdsdi-aspect-ratio", "",
VIDEO_ASPECT_TEXT, VIDEO_ASPECT_LONGTEXT, true )
add_string( "linsys-hdsdi-audio", "0=1,1",
AUDIO_TEXT, AUDIO_LONGTEXT, true )
set_capability( "access_demux", 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;
struct demux_sys_t
{
/* 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;
mtime_t i_next_vdate, i_next_adate;
int i_incr, i_aincr;
/* ES stuff */
int i_id_video;
es_out_id_t *p_es_video;
hdsdi_audio_t p_audios[MAX_AUDIOS];
};
static int Control( demux_t *, int, va_list );
static int Demux( demux_t * );
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;
/* Fill p_demux field */
p_demux->pf_demux = Demux;
p_demux->pf_control = Control;
p_demux->p_sys = p_sys = calloc( 1, sizeof( demux_sys_t ) );
if( unlikely(!p_sys) )
return VLC_ENOMEM;
/* HDSDI AR */
char *psz_ar = var_InheritString( p_demux, "linsys-hdsdi-aspect-ratio" );
if ( psz_ar != NULL )
{
psz_parser = strchr( psz_ar, ':' );
if ( psz_parser )
{
*psz_parser++ = '\0';
p_sys->i_forced_aspect = p_sys->i_aspect =
strtol( psz_ar, NULL, 0 ) * VOUT_ASPECT_FACTOR
/ strtol( psz_parser, NULL, 0 );
}
else
p_sys->i_forced_aspect = 0;
free( psz_ar );
}
/* */
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;
/* Update default_pts to a suitable value for hdsdi access */
var_Create( p_demux, "linsys-hdsdi-caching", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
p_sys->i_link = var_InheritInteger( p_demux, "linsys-hdsdi-link" );
if( InitCapture( p_demux ) != VLC_SUCCESS )
{
free( p_sys );
return VLC_EGENERIC;
}
return VLC_SUCCESS;
}
/*****************************************************************************
* 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;
CloseCapture( p_demux );
free( p_sys );
}
/*****************************************************************************
* DemuxDemux:
*****************************************************************************/
static int Demux( demux_t *p_demux )
{
return ( Capture( p_demux ) == VLC_SUCCESS );
}
/*****************************************************************************
* Control:
*****************************************************************************/
static int Control( demux_t *p_demux, int i_query, va_list args )
{
bool *pb;
int64_t *pi64;
switch( i_query )
{
/* Special for access_demux */
case DEMUX_CAN_PAUSE:
case DEMUX_CAN_CONTROL_PACE:
/* TODO */
pb = (bool*)va_arg( args, bool * );
*pb = false;
return VLC_SUCCESS;
case DEMUX_GET_PTS_DELAY:
pi64 = (int64_t*)va_arg( args, int64_t * );
*pi64 = (int64_t)var_GetInteger( p_demux, "linsys-hdsdi-caching" ) *1000;
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 = 1000000 * 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_FOURCC('I','4','2','0') );
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_FOURCC('a','r','a','w') );
fmt.i_id = p_audio->i_id;
fmt.audio.i_channels = 2;
fmt.audio.i_physical_channels = 6;
fmt.audio.i_original_channels = 6;
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 = 1000000. * 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_New( p_demux, 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_Control( p_demux->out, ES_OUT_SET_PCR, 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_New( p_demux, 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 + (mtime_t)p_audio->i_delay
* INT64_C(1000000) / 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 );
close( i_fd );
return i_ret;
}