* 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
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
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 \
--- /dev/null
+/*****************************************************************************
+ * 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;
+}
+
+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 );
+ psz_vdev[sizeof(psz_vdev) - 1] = '\0';
+ 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 */
+ while ( !p_demux->b_die )
+ {
+ struct pollfd pfd[1];
+
+ pfd[0].fd = p_sys->i_vfd;
+ pfd[0].events = POLLIN | POLLPRI;
+
+ if ( poll( pfd, 1, READ_TIMEOUT ) < 0 )
+ {
+ msg_Warn( p_demux, "couldn't poll(): %s", strerror(errno) );
+ close( p_sys->i_vfd );
+ return VLC_EGENERIC;
+ }
+
+ 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",
+ strerror(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 ( p_demux->b_die )
+ {
+ 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",
+ strerror(errno) );
+ close( p_sys->i_vfd );
+ return VLC_EGENERIC;
+ }
+ 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 );
+ psz_adev[sizeof(psz_adev) - 1] = '\0';
+ 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",
+ strerror(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;
+ }
+ 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 = malloc( 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, strerror(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 = malloc( 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, strerror(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
+ 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
+ close( p_sys->i_afd );
+ }
+}
+
+static int Capture( demux_t *p_demux )
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+ struct pollfd pfd[2];
+
+ pfd[0].fd = p_sys->i_vfd;
+ pfd[0].events = POLLIN | POLLPRI;
+ if ( p_sys->i_max_channel != -1 )
+ {
+ pfd[1].fd = p_sys->i_afd;
+ pfd[1].events = POLLIN | POLLPRI;
+ }
+
+ if ( poll( pfd, 1 + (p_sys->i_max_channel != -1), READ_TIMEOUT ) < 0 )
+ {
+ msg_Warn( p_demux, "couldn't poll(): %s", strerror(errno) );
+ return VLC_EGENERIC;
+ }
+
+ 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",
+ strerror(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[1].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",
+ strerror(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[0].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",
+ strerror(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",
+ strerror(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", strerror(errno) );
+ 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[1].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",
+ strerror(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",
+ strerror(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", strerror(errno) );
+ return VLC_EGENERIC;
+ }
+
+ if( HandleAudio( p_demux, p_buffer ) != VLC_SUCCESS )
+ {
+ free( p_buffer );
+ return VLC_ENOMEM;
+ }
+ free( p_buffer );
+#endif
+ }
+
+ return VLC_SUCCESS;
+}
--- /dev/null
+/*****************************************************************************
+ * linsys_sdi.c: SDI capture for Linear Systems/Computer Modules cards
+ *****************************************************************************
+ * Copyright (C) 2009-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 <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_sdi.h"
+
+#undef ZVBI_DEBUG
+#include <libzvbi.h>
+
+#define SDI_DEVICE "/dev/sdirx%u"
+#define SDI_BUFFERS_FILE "/sys/class/sdi/sdirx%u/buffers"
+#define SDI_BUFSIZE_FILE "/sys/class/sdi/sdirx%u/bufsize"
+#define SDI_MODE_FILE "/sys/class/sdi/sdirx%u/mode"
+#define READ_TIMEOUT 80000
+#define RESYNC_TIMEOUT 500000
+#define CLOCK_GAP INT64_C(500000)
+#define START_DATE INT64_C(4294967296)
+
+#define DEMUX_BUFFER_SIZE 1350000
+#define MAX_AUDIOS 4
+#define SAMPLERATE_TOLERANCE 0.1
+
+/*****************************************************************************
+ * Module descriptor
+ *****************************************************************************/
+#define CACHING_TEXT N_("Caching value in ms")
+#define CACHING_LONGTEXT N_( \
+ "Allows you to modify the default caching value for sdi 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...)." )
+#define TELX_TEXT N_("Teletext configuration")
+#define TELX_LONGTEXT N_( \
+ "Allows you to set Teletext configuration (id=line1-lineN with both fields)." )
+#define TELX_LANG_TEXT N_("Teletext language")
+#define TELX_LANG_LONGTEXT N_( \
+ "Allows you to set Teletext language (page=lang/type,...)." )
+
+static int Open ( vlc_object_t * );
+static void Close( vlc_object_t * );
+static int DemuxOpen ( vlc_object_t * );
+static void DemuxClose( vlc_object_t * );
+
+vlc_module_begin()
+ set_description( N_("SDI Input") )
+ set_shortname( N_("sdi") )
+ set_category( CAT_INPUT )
+ set_subcategory( SUBCAT_INPUT_ACCESS )
+
+ add_integer( "linsys-sdi-caching", DEFAULT_PTS_DELAY / 1000,
+ CACHING_TEXT, CACHING_LONGTEXT, true )
+ add_integer( "linsys-sdi-link", 0,
+ LINK_TEXT, LINK_LONGTEXT, true )
+
+ add_integer( "linsys-sdi-id-video", 0, VIDEO_TEXT, VIDEO_LONGTEXT, true )
+ add_string( "linsys-sdi-aspect-ratio", "", VIDEO_ASPECT_TEXT,
+ VIDEO_ASPECT_LONGTEXT, true )
+ add_string( "linsys-sdi-audio", "0=1,1", AUDIO_TEXT, AUDIO_LONGTEXT, true )
+ add_string( "linsys-sdi-telx", "", TELX_TEXT, TELX_LONGTEXT, true )
+ add_string( "linsys-sdi-telx-lang", "", TELX_LANG_TEXT, TELX_LANG_LONGTEXT,
+ true )
+
+ set_capability( "access_demux", 0 )
+ add_shortcut( "linsys-sdi" )
+ set_callbacks( Open, Close )
+
+ add_submodule()
+ set_description( N_("SDI Demux") )
+ set_capability( "demux", 0 )
+ set_callbacks( DemuxOpen, DemuxClose )
+vlc_module_end()
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+typedef struct sdi_audio_t
+{
+ unsigned int i_group, i_pair;
+
+ /* SDI parser */
+ int32_t i_delay;
+ unsigned int i_rate;
+ uint8_t i_block_number;
+ int16_t *p_buffer;
+ unsigned int i_left_samples, i_right_samples, i_nb_samples, i_max_samples;
+
+ /* ES stuff */
+ int i_id;
+ es_out_id_t *p_es;
+} sdi_audio_t;
+
+enum {
+ STATE_NOSYNC,
+ STATE_STARTSYNC,
+ STATE_ANCSYNC,
+ STATE_LINESYNC,
+ STATE_ACTIVESYNC,
+ STATE_VBLANKSYNC,
+ STATE_PICSYNC,
+ STATE_SYNC,
+};
+
+struct demux_sys_t
+{
+ /* device reader */
+ int i_fd;
+ unsigned int i_link;
+ uint8_t **pp_buffers;
+ unsigned int i_buffers, i_current_buffer;
+ unsigned int i_buffer_size;
+
+ /* SDI sync */
+ int i_state;
+ mtime_t i_last_state_change;
+ unsigned int i_anc_size, i_active_size, i_picture_size;
+ unsigned int i_line_offset, i_nb_lines;
+
+ /* SDI parser */
+ unsigned int i_line_buffer;
+ unsigned int i_current_line;
+ uint8_t *p_line_buffer;
+ block_t *p_current_picture;
+ uint8_t *p_y, *p_u, *p_v;
+ uint8_t *p_wss_buffer;
+ uint8_t *p_telx_buffer;
+
+ /* 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_block_size;
+ unsigned int i_telx_line, i_telx_count;
+ char *psz_telx, *psz_telx_lang;
+ bool b_hd, b_vbi;
+ vbi_raw_decoder rd_wss, rd_telx;
+ mtime_t i_next_date;
+ int i_incr;
+
+ /* ES stuff */
+ int i_id_video;
+ es_out_id_t *p_es_video;
+ sdi_audio_t p_audios[MAX_AUDIOS];
+ es_out_id_t *p_es_telx;
+};
+
+static int Control( demux_t *, int, va_list );
+static int DemuxControl( demux_t *, int, va_list );
+static int Demux( demux_t * );
+static int DemuxDemux( demux_t * );
+
+static int InitWSS( demux_t *p_demux );
+static int InitTelx( demux_t *p_demux );
+
+static int HandleSDBuffer( demux_t *p_demux, uint8_t *p_buffer,
+ unsigned int i_buffer_size );
+
+static int InitCapture( demux_t *p_demux );
+static void CloseCapture( demux_t *p_demux );
+static int Capture( demux_t *p_demux );
+
+/*****************************************************************************
+ * DemuxOpen:
+ *****************************************************************************/
+static int DemuxOpen( 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 = DemuxDemux;
+ p_demux->pf_control = DemuxControl;
+ p_demux->p_sys = p_sys = calloc( 1, sizeof( demux_sys_t ) );
+ if( unlikely(!p_sys ) )
+ return VLC_ENOMEM;
+
+ p_sys->i_state = STATE_NOSYNC;
+ p_sys->i_last_state_change = mdate();
+
+ /* SDI AR */
+ char *psz_ar = var_InheritString( p_demux, "linsys-sdi-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-sdi-id-video" );
+
+ /* Audio ES */
+ char *psz_string = psz_parser = var_InheritString( p_demux,
+ "linsys-sdi-audio" );
+ int i = 0;
+
+ 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_group = i_group;
+ p_sys->p_audios[i].i_pair = i_pair;
+ 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 );
+
+ /* Teletext ES */
+ p_sys->psz_telx = var_InheritString( p_demux, "linsys-sdi-telx" );
+
+ p_sys->psz_telx_lang = var_InheritString( p_demux, "linsys-sdi-telx-lang" );
+
+ return VLC_SUCCESS;
+}
+
+static int Open( vlc_object_t *p_this )
+{
+ demux_t *p_demux = (demux_t *)p_this;
+ demux_sys_t *p_sys;
+ int i_ret;
+
+ if ( (i_ret = DemuxOpen( p_this )) != VLC_SUCCESS )
+ return i_ret;
+
+ /* Fill p_demux field */
+ p_demux->pf_demux = Demux;
+ p_demux->pf_control = Control;
+ p_sys = p_demux->p_sys;
+
+ /* Update default_pts to a suitable value for sdi access */
+ var_Create( p_demux, "linsys-sdi-caching", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
+
+ p_sys->i_link = var_InheritInteger( p_demux, "linsys-sdi-link" );
+
+ if( InitCapture( p_demux ) != VLC_SUCCESS )
+ {
+ free( p_sys );
+ return VLC_EGENERIC;
+ }
+
+ return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * DemuxClose:
+ *****************************************************************************/
+static void DemuxClose( vlc_object_t *p_this )
+{
+ demux_t *p_demux = (demux_t *)p_this;
+ demux_sys_t *p_sys = p_demux->p_sys;
+
+ free( p_sys->psz_telx );
+ free( p_sys->psz_telx_lang );
+ free( p_sys );
+}
+
+static void Close( vlc_object_t *p_this )
+{
+ demux_t *p_demux = (demux_t *)p_this;
+
+ CloseCapture( p_demux );
+ DemuxClose( p_this );
+}
+
+/*****************************************************************************
+ * DemuxDemux:
+ *****************************************************************************/
+static int DemuxDemux( demux_t *p_demux )
+{
+ block_t *p_block = stream_Block( p_demux->s, DEMUX_BUFFER_SIZE );
+ int i_ret;
+
+ if ( p_block == NULL )
+ return 0;
+
+ i_ret = HandleSDBuffer( p_demux, p_block->p_buffer, p_block->i_buffer );
+ block_Release( p_block );
+
+ return ( i_ret == VLC_SUCCESS );
+}
+
+static int Demux( demux_t *p_demux )
+{
+ return ( Capture( p_demux ) == VLC_SUCCESS );
+}
+
+/*****************************************************************************
+ * Control:
+ *****************************************************************************/
+static int DemuxControl( demux_t *p_demux, int i_query, va_list args )
+{
+ return demux_vaControlHelper( p_demux->s, -1, -1, 270000000, 1, i_query,
+ args );
+}
+
+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-sdi-caching" ) * 1000;
+ return VLC_SUCCESS;
+
+ /* TODO implement others */
+ default:
+ return VLC_EGENERIC;
+ }
+}
+
+/*****************************************************************************
+ * Video, audio & VBI decoding
+ *****************************************************************************/
+#define WSS_LINE 23
+
+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 int NewFrame( demux_t *p_demux )
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+
+ p_sys->p_current_picture = block_New( p_demux, p_sys->i_block_size );
+ if( unlikely( !p_sys->p_current_picture ) )
+ return VLC_ENOMEM;
+ p_sys->p_y = p_sys->p_current_picture->p_buffer;
+ p_sys->p_u = p_sys->p_y + p_sys->i_width * p_sys->i_height;
+ p_sys->p_v = p_sys->p_u + p_sys->i_width * p_sys->i_height / 4;
+
+ for ( int i = 0; i < MAX_AUDIOS; i++ )
+ {
+ sdi_audio_t *p_audio = &p_sys->p_audios[i];
+ p_audio->i_left_samples = p_audio->i_right_samples = 0;
+ }
+ return VLC_SUCCESS;
+}
+
+static int StartDecode( demux_t *p_demux )
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+ es_format_t fmt;
+ char *psz_parser;
+
+ p_sys->i_next_date = START_DATE;
+ p_sys->i_incr = 1000000 * p_sys->i_frame_rate_base / p_sys->i_frame_rate;
+ p_sys->i_block_size = p_sys->i_width * p_sys->i_height * 3 / 2
+ + sizeof(struct block_extension_t);
+ if( NewFrame( p_demux ) != VLC_SUCCESS )
+ return VLC_ENOMEM;
+
+ /* 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 = p_sys->i_width;
+ fmt.video.i_height = p_sys->i_height;
+ int i_aspect = p_sys->i_forced_aspect ? p_sys->i_forced_aspect
+ : p_sys->i_aspect;
+ fmt.video.i_sar_num = 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 );
+
+ if ( p_sys->b_vbi && InitWSS( p_demux ) != VLC_SUCCESS )
+ p_sys->b_vbi = 0;
+
+ /* Teletext ES */
+ psz_parser = p_sys->psz_telx;
+ if ( psz_parser != NULL && *psz_parser )
+ {
+ if ( !p_sys->b_vbi )
+ {
+ msg_Warn( p_demux, "VBI is unsupported on this input stream" );
+ }
+ else
+ {
+ int i_id;
+ 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';
+
+ p_sys->i_telx_line = strtol( psz_parser, NULL, 0 ) - 1;
+ if ( psz_next != NULL )
+ p_sys->i_telx_count = strtol( psz_next, NULL, 0 )
+ - p_sys->i_telx_line - 1 + 1;
+ else
+ p_sys->i_telx_count = 1;
+
+ if ( InitTelx( p_demux ) == VLC_SUCCESS )
+ {
+ int i_dr_size = 0;
+ uint8_t *p_dr = NULL;
+
+ msg_Dbg( p_demux, "capturing VBI lines %d-%d and %d-%d",
+ p_sys->i_telx_line + 1,
+ p_sys->i_telx_line + 1 + p_sys->i_telx_count - 1,
+ p_sys->i_telx_line + 1 + 313,
+ p_sys->i_telx_line + 1 + 313
+ + p_sys->i_telx_count - 1 );
+
+ es_format_Init( &fmt, SPU_ES, VLC_CODEC_TELETEXT );
+ fmt.i_id = i_id;
+
+ /* Teletext language & type */
+ psz_parser = p_sys->psz_telx_lang;
+
+ while ( (psz_next = strchr( psz_parser, '=' )) != NULL )
+ {
+ int i_page;
+ *psz_next++ = '\0';
+ if ( !psz_next[0] || !psz_next[1] || !psz_next[2] )
+ break;
+ i_page = strtol( psz_parser, NULL, 0 );
+ i_dr_size += 5;
+ p_dr = realloc( p_dr, i_dr_size );
+ p_dr[i_dr_size - 5] = *psz_next++;
+ p_dr[i_dr_size - 4] = *psz_next++;
+ p_dr[i_dr_size - 3] = *psz_next++;
+ if ( *psz_next == '/' )
+ {
+ psz_next++;
+ p_dr[i_dr_size - 2] = strtol( psz_next, &psz_next, 0 )
+ << 3;
+ }
+ else /* subtitle for hearing impaired */
+ p_dr[i_dr_size - 2] = 0x5 << 3;
+ p_dr[i_dr_size - 2] |= (i_page / 100) & 0x7;
+ p_dr[i_dr_size - 1] = i_page % 100;
+
+ if ( *psz_next == ',' )
+ psz_next++;
+ psz_parser = psz_next;
+ }
+
+ fmt.i_extra = i_dr_size;
+ fmt.p_extra = p_dr;
+ p_sys->p_es_telx = es_out_Add( p_demux->out, &fmt );
+ }
+ else
+ p_sys->i_telx_count = 0;
+ }
+ }
+ return VLC_SUCCESS;
+}
+
+static void StopDecode( demux_t *p_demux )
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+
+ if ( p_sys->i_state != STATE_SYNC )
+ return;
+
+ free( p_sys->p_line_buffer );
+
+ block_Release( p_sys->p_current_picture );
+ p_sys->p_current_picture = NULL;
+ es_out_Del( p_demux->out, p_sys->p_es_video );
+
+ if ( p_sys->b_vbi )
+ {
+ free( p_sys->p_wss_buffer );
+ p_sys->p_wss_buffer = NULL;
+ vbi_raw_decoder_destroy( &p_sys->rd_wss );
+
+ if ( p_sys->p_es_telx )
+ {
+ es_out_Del( p_demux->out, p_sys->p_es_telx );
+ free( p_sys->p_telx_buffer );
+ p_sys->p_telx_buffer = NULL;
+ vbi_raw_decoder_destroy( &p_sys->rd_telx );
+ }
+ }
+
+ for ( int i = 0; i < MAX_AUDIOS; i++ )
+ {
+ sdi_audio_t *p_audio = &p_sys->p_audios[i];
+ if ( p_audio->i_group && p_audio->p_es != NULL )
+ {
+ es_out_Del( p_demux->out, p_audio->p_es );
+ p_audio->p_es = NULL;
+ free( p_audio->p_buffer );
+ p_audio->p_buffer = NULL;
+ }
+ }
+}
+
+static void InitVideo( demux_t *p_demux )
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+ int i_total_width = (p_sys->i_anc_size + p_sys->i_active_size) * 4 / 5;
+ p_sys->i_width = (p_sys->i_active_size - 5) * 4 / 10;
+ if ( p_sys->i_nb_lines == 625 )
+ {
+ /* PAL */
+ p_sys->i_frame_rate = 25;
+ p_sys->i_frame_rate_base = 1;
+ p_sys->i_height = 576;
+ p_sys->i_aspect = 4 * VOUT_ASPECT_FACTOR / 3;
+ p_sys->b_hd = false;
+ }
+ else if ( p_sys->i_nb_lines == 525 )
+ {
+ /* NTSC */
+ p_sys->i_frame_rate = 30000;
+ p_sys->i_frame_rate_base = 1001;
+ p_sys->i_height = 480;
+ p_sys->i_aspect = 4 * VOUT_ASPECT_FACTOR / 3;
+ p_sys->b_hd = false;
+ }
+ else if ( p_sys->i_nb_lines == 1125 && i_total_width == 2640 )
+ {
+ /* 1080i50 or 1080p25 */
+ p_sys->i_frame_rate = 25;
+ p_sys->i_frame_rate_base = 1;
+ p_sys->i_height = 1080;
+ p_sys->i_aspect = 16 * VOUT_ASPECT_FACTOR / 9;
+ p_sys->b_hd = true;
+ }
+ else if ( p_sys->i_nb_lines == 1125 && i_total_width == 2200 )
+ {
+ /* 1080i60 or 1080p30 */
+ p_sys->i_frame_rate = 30000;
+ p_sys->i_frame_rate_base = 1001;
+ p_sys->i_height = 1080;
+ p_sys->i_aspect = 16 * VOUT_ASPECT_FACTOR / 9;
+ p_sys->b_hd = true;
+ }
+ else if ( p_sys->i_nb_lines == 750 && i_total_width == 1980 )
+ {
+ /* 720p50 */
+ p_sys->i_frame_rate = 50;
+ p_sys->i_frame_rate_base = 1;
+ p_sys->i_height = 720;
+ p_sys->i_aspect = 16 * VOUT_ASPECT_FACTOR / 9;
+ p_sys->b_hd = true;
+ }
+ else if ( p_sys->i_nb_lines == 750 && i_total_width == 1650 )
+ {
+ /* 720p60 */
+ p_sys->i_frame_rate = 60000;
+ p_sys->i_frame_rate_base = 1001;
+ p_sys->i_height = 720;
+ p_sys->i_aspect = 16 * VOUT_ASPECT_FACTOR / 9;
+ p_sys->b_hd = true;
+ }
+ else
+ {
+ msg_Warn( p_demux, "unable to determine video type" );
+ /* Put sensitive defaults */
+ p_sys->i_frame_rate = 25;
+ p_sys->i_frame_rate_base = 1;
+ p_sys->i_height = p_sys->i_nb_lines;
+ p_sys->i_aspect = 16 * VOUT_ASPECT_FACTOR / 9;
+ p_sys->b_hd = true;
+ }
+ p_sys->b_vbi = !p_sys->b_hd;
+}
+
+static void DecodeVideo( demux_t *p_demux )
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+ struct block_extension_t ext;
+
+ /* 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_sys->p_current_picture->p_buffer[p_sys->i_block_size
+ - sizeof(struct block_extension_t)],
+ &ext, sizeof(struct block_extension_t) );
+
+ p_sys->p_current_picture->i_dts = p_sys->p_current_picture->i_pts
+ = p_sys->i_next_date;
+ es_out_Send( p_demux->out, p_sys->p_es_video, p_sys->p_current_picture );
+}
+
+static int InitWSS( demux_t *p_demux )
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+ vbi_raw_decoder_init( &p_sys->rd_wss );
+
+ p_sys->rd_wss.scanning = 625;
+ p_sys->rd_wss.sampling_format = VBI_PIXFMT_UYVY;
+ p_sys->rd_wss.sampling_rate = 13.5e6;
+ p_sys->rd_wss.bytes_per_line = 720 * 2;
+ p_sys->rd_wss.offset = 9.5e-6 * 13.5e6;
+
+ p_sys->rd_wss.start[0] = 23;
+ p_sys->rd_wss.count[0] = 1;
+ p_sys->rd_wss.start[1] = 0;
+ p_sys->rd_wss.count[1] = 0;
+
+ p_sys->rd_wss.interlaced = FALSE;
+ p_sys->rd_wss.synchronous = TRUE;
+
+ if ( vbi_raw_decoder_add_services( &p_sys->rd_wss,
+ VBI_SLICED_WSS_625,
+ /* strict */ 2 ) == 0 )
+ {
+ msg_Warn( p_demux, "cannot initialize zvbi for WSS" );
+ vbi_raw_decoder_destroy ( &p_sys->rd_telx );
+ return VLC_EGENERIC;
+ }
+
+ p_sys->p_wss_buffer = malloc( p_sys->i_width * 2 );
+ if( !p_sys->p_wss_buffer )
+ {
+ vbi_raw_decoder_destroy ( &p_sys->rd_telx );
+ return VLC_ENOMEM;
+ }
+ return VLC_SUCCESS;
+}
+
+static void DecodeWSS( demux_t *p_demux )
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+ vbi_sliced p_sliced[1];
+
+ if ( vbi_raw_decode( &p_sys->rd_wss, p_sys->p_wss_buffer, p_sliced ) == 0 )
+ {
+ p_sys->i_aspect = 4 * VOUT_ASPECT_FACTOR / 3;
+ }
+ else
+ {
+ unsigned int i_old_aspect = p_sys->i_aspect;
+ uint8_t *p = p_sliced[0].data;
+ int i_aspect, i_parity;
+
+ i_aspect = p[0] & 15;
+ i_parity = i_aspect;
+ i_parity ^= i_parity >> 2;
+ i_parity ^= i_parity >> 1;
+ i_aspect &= 7;
+
+ if ( !(i_parity & 1) )
+ msg_Warn( p_demux, "WSS parity error" );
+ else if ( i_aspect == 7 )
+ p_sys->i_aspect = 16 * VOUT_ASPECT_FACTOR / 9;
+ else
+ p_sys->i_aspect = 4 * VOUT_ASPECT_FACTOR / 3;
+
+ if ( p_sys->i_aspect != i_old_aspect )
+ msg_Dbg( p_demux, "new WSS information (ra=%x md=%x cod=%x hlp=%x rvd=%x sub=%x pos=%x srd=%x c=%x cp=%x)",
+ i_aspect, (p[0] & 0x10) >> 4, (p[0] & 0x20) >> 5,
+ (p[0] & 0x40) >> 6, (p[0] & 0x80) >> 7, p[1] & 0x01,
+ (p[1] >> 1) & 3, (p[1] & 0x08) >> 3, (p[1] & 0x10) >> 4,
+ (p[1] & 0x20) >> 5 );
+ }
+}
+
+static int InitTelx( demux_t *p_demux )
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+ vbi_raw_decoder_init( &p_sys->rd_telx );
+
+ p_sys->rd_telx.scanning = 625;
+ p_sys->rd_telx.sampling_format = VBI_PIXFMT_UYVY;
+ p_sys->rd_telx.sampling_rate = 13.5e6;
+ p_sys->rd_telx.bytes_per_line = 720 * 2;
+ p_sys->rd_telx.offset = 9.5e-6 * 13.5e6;
+
+ p_sys->rd_telx.start[0] = p_sys->i_telx_line + 1;
+ p_sys->rd_telx.count[0] = p_sys->i_telx_count;
+ p_sys->rd_telx.start[1] = p_sys->i_telx_line + 1 + 313;
+ p_sys->rd_telx.count[1] = p_sys->i_telx_count;
+
+ p_sys->rd_telx.interlaced = FALSE;
+ p_sys->rd_telx.synchronous = TRUE;
+
+ if ( vbi_raw_decoder_add_services( &p_sys->rd_telx, VBI_SLICED_TELETEXT_B,
+ 0 ) == 0 )
+ {
+ msg_Warn( p_demux, "cannot initialize zvbi for Teletext" );
+ vbi_raw_decoder_destroy ( &p_sys->rd_telx );
+ return VLC_EGENERIC;
+ }
+
+ p_sys->p_telx_buffer = malloc( p_sys->i_telx_count * p_sys->i_width * 4 );
+ if( !p_sys->p_telx_buffer )
+ {
+ vbi_raw_decoder_destroy ( &p_sys->rd_telx );
+ return VLC_ENOMEM;
+ }
+ return VLC_SUCCESS;
+}
+
+static int DecodeTelx( demux_t *p_demux )
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+ vbi_sliced p_sliced[p_sys->i_telx_count * 2];
+ int i_nb_slices = vbi_raw_decode( &p_sys->rd_telx, p_sys->p_telx_buffer,
+ p_sliced );
+
+ if ( i_nb_slices )
+ {
+ /* 3, 7, 11, 15, etc. */
+ int i_nb_slices_rounded = 3 + (i_nb_slices / 4) * 4;
+ int i;
+ uint8_t *p;
+ block_t *p_block = block_New( p_demux,
+ 1 + i_nb_slices_rounded * 46 );
+ if( unlikely( !p_block ) )
+ return VLC_ENOMEM;
+ p_block->p_buffer[0] = 0x10; /* FIXME ? data_identifier */
+ p = p_block->p_buffer + 1;
+
+ for ( i = 0; i < i_nb_slices; i++ )
+ {
+ int i_line = p_sliced[i].line;
+ p[0] = 0x3; /* FIXME data_unit_id == subtitles */
+ p[1] = 0x2c; /* data_unit_length */
+ /* reserved | field_parity (kind of inverted) | line */
+ p[2] = 0xc0 | (i_line > 313 ? 0 : 0x20) | (i_line % 313);
+ p[3] = 0xe4; /* framing code */
+ for ( int j = 0; j < 42; j++ )
+ p[4 + j] = vbi_rev8( p_sliced[i].data[j] );
+ p += 46;
+ }
+
+ /* Let's stuff */
+ for ( ; i < i_nb_slices_rounded; i++ )
+ {
+ p[0] = 0xff;
+ p[1] = 0x2c;
+ memset( p + 2, 0xff, 44 );
+ p += 46;
+ }
+
+ p_block->i_dts = p_block->i_pts = p_sys->i_next_date;
+ es_out_Send( p_demux->out, p_sys->p_es_telx, p_block );
+ }
+ return VLC_SUCCESS;
+}
+
+static int InitAudio( demux_t *p_demux, sdi_audio_t *p_audio )
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+ es_format_t fmt;
+
+ msg_Dbg( p_demux, "starting audio %u/%u rate:%u delay:%d", p_audio->i_group,
+ p_audio->i_pair, p_audio->i_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 = 6;
+ fmt.audio.i_original_channels = 6;
+ fmt.audio.i_rate = p_audio->i_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_audio->i_nb_samples = p_audio->i_rate * p_sys->i_frame_rate_base
+ / p_sys->i_frame_rate;
+ p_audio->i_max_samples = (float)p_audio->i_nb_samples *
+ (1. + SAMPLERATE_TOLERANCE);
+
+ p_audio->p_buffer = malloc( p_audio->i_max_samples * sizeof(int16_t) * 2 );
+ p_audio->i_left_samples = p_audio->i_right_samples = 0;
+ p_audio->i_block_number = 0;
+
+ if( unlikely( !p_audio->p_buffer ) )
+ return VLC_ENOMEM;
+ return VLC_SUCCESS;
+}
+
+/* Fast and efficient linear resampling routine */
+static void ResampleAudio( int16_t *p_out, int16_t *p_in,
+ unsigned int i_out, unsigned int i_in )
+{
+ unsigned int i_remainder = 0;
+ float f_last_sample = (float)*p_in / 32768.0;
+
+ *p_out = *p_in;
+ p_out += 2;
+ p_in += 2;
+
+ for ( unsigned int i = 1; i < i_in; i++ )
+ {
+ float f_in = (float)*p_in / 32768.0;
+ while ( i_remainder < i_out )
+ {
+ float f_out = f_last_sample;
+ f_out += (f_in - f_last_sample) * i_remainder / i_out;
+ if ( f_out >= 1.0 ) *p_out = 32767;
+ else if ( f_out < -1.0 ) *p_out = -32768;
+ else *p_out = f_out * 32768.0;
+ p_out += 2;
+ i_remainder += i_in;
+ }
+
+ f_last_sample = f_in;
+ p_in += 2;
+ i_remainder -= i_out;
+ }
+}
+
+static int DecodeAudio( demux_t *p_demux, sdi_audio_t *p_audio )
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+ block_t *p_block;
+ int16_t *p_output;
+
+ if ( p_audio->p_buffer == NULL )
+ return VLC_EGENERIC;
+ if ( !p_audio->i_left_samples && !p_audio->i_right_samples )
+ {
+ msg_Warn( p_demux, "no audio %u/%u", p_audio->i_group,
+ p_audio->i_pair );
+ return VLC_EGENERIC;
+ }
+ if ( p_audio->i_left_samples <
+ (float)p_audio->i_nb_samples * (1. - SAMPLERATE_TOLERANCE) ||
+ p_audio->i_left_samples >
+ (float)p_audio->i_nb_samples * (1. + SAMPLERATE_TOLERANCE) )
+ {
+ msg_Warn( p_demux,
+ "left samplerate out of tolerance for audio %u/%u (%u vs. %u)",
+ p_audio->i_group, p_audio->i_pair,
+ p_audio->i_left_samples, p_audio->i_nb_samples );
+ return VLC_EGENERIC;
+ }
+ if ( p_audio->i_right_samples <
+ (float)p_audio->i_nb_samples * (1. - SAMPLERATE_TOLERANCE) ||
+ p_audio->i_right_samples >
+ (float)p_audio->i_nb_samples * (1. + SAMPLERATE_TOLERANCE) )
+ {
+ msg_Warn( p_demux,
+ "right samplerate out of tolerance for audio %u/%u (%u vs. %u)",
+ p_audio->i_group, p_audio->i_pair,
+ p_audio->i_right_samples, p_audio->i_nb_samples );
+ return VLC_EGENERIC;
+ }
+
+ p_block = block_New( p_demux, p_audio->i_nb_samples * sizeof(int16_t) * 2 );
+ if( unlikely( !p_block ) )
+ return VLC_ENOMEM;
+ p_block->i_dts = p_block->i_pts = p_sys->i_next_date
+ + (mtime_t)p_audio->i_delay * INT64_C(1000000) / p_audio->i_rate;
+ p_output = (int16_t *)p_block->p_buffer;
+
+ if ( p_audio->i_left_samples == p_audio->i_nb_samples &&
+ p_audio->i_right_samples == p_audio->i_nb_samples )
+ vlc_memcpy( p_output, p_audio->p_buffer,
+ p_audio->i_nb_samples * sizeof(int16_t) * 2 );
+ else
+ {
+ ResampleAudio( p_output, p_audio->p_buffer,
+ p_audio->i_nb_samples, p_audio->i_left_samples );
+
+ ResampleAudio( p_output + 1, p_audio->p_buffer + 1,
+ p_audio->i_nb_samples, p_audio->i_right_samples );
+ }
+
+ es_out_Send( p_demux->out, p_audio->p_es, p_block );
+ return VLC_SUCCESS;
+}
+
+static int DecodeFrame( demux_t *p_demux )
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+
+ if ( p_sys->b_vbi )
+ {
+ DecodeWSS( p_demux );
+
+ if ( p_sys->i_height == 576 )
+ {
+ /* For PAL, erase first half of line 23, last half of line 623,
+ * and line 624 ; no need to erase chrominance */
+ vlc_memset( p_sys->p_y, 0, p_sys->i_width / 2 );
+ vlc_memset( p_sys->p_y + p_sys->i_width * 574 + p_sys->i_width / 2,
+ 0, p_sys->i_width * 3 / 2 );
+ }
+ }
+
+ if ( p_sys->i_telx_count )
+ if ( DecodeTelx( p_demux ) != VLC_SUCCESS )
+ return VLC_ENOMEM;
+
+ for ( int i = 0; i < MAX_AUDIOS; i++ )
+ {
+ if ( p_sys->p_audios[i].i_group && p_sys->p_audios[i].p_es != NULL )
+ if( DecodeAudio( p_demux, &p_sys->p_audios[i] ) != VLC_SUCCESS )
+ return VLC_EGENERIC;
+ }
+
+ DecodeVideo( p_demux );
+
+ es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_sys->i_next_date );
+ p_sys->i_next_date += p_sys->i_incr;
+
+ if( NewFrame( p_demux ) != VLC_SUCCESS )
+ return VLC_ENOMEM;
+
+ return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * SDI syntax parsing stuff
+ *****************************************************************************/
+#define FIELD_1_VBLANK_EAV 0xB6
+#define FIELD_1_VBLANK_SAV 0xAB
+#define FIELD_1_ACTIVE_EAV 0x9D
+#define FIELD_1_ACTIVE_SAV 0x80
+#define FIELD_2_VBLANK_EAV 0xF1
+#define FIELD_2_VBLANK_SAV 0xEC
+#define FIELD_2_ACTIVE_EAV 0xDA
+#define FIELD_2_ACTIVE_SAV 0xC7
+
+static const uint8_t *FindReferenceCode( uint8_t i_code,
+ const uint8_t *p_parser,
+ const uint8_t *p_end )
+{
+ while ( p_parser <= p_end - 5 )
+ {
+ if ( p_parser[0] == 0xff && p_parser[1] == 0x3 && p_parser[2] == 0x0
+ && p_parser[3] == 0x0 && p_parser[4] == i_code )
+ return p_parser;
+ p_parser += 5;
+ }
+
+ return NULL;
+}
+
+static const uint8_t *CountReference( unsigned int *pi_count, uint8_t i_code,
+ const uint8_t *p_parser,
+ const uint8_t *p_end )
+{
+ const uint8_t *p_tmp = FindReferenceCode( i_code, p_parser, p_end );
+ if ( p_tmp == NULL )
+ {
+ *pi_count += p_end - p_parser;
+ return NULL;
+ }
+ *pi_count += p_tmp - p_parser;
+ return p_tmp;
+}
+
+static const uint8_t *GetLine( demux_t *p_demux, const uint8_t **pp_parser,
+ const uint8_t *p_end )
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+ unsigned int i_total_size = p_sys->i_anc_size + p_sys->i_active_size;
+ const uint8_t *p_tmp;
+
+ if ( p_sys->i_line_buffer )
+ {
+ unsigned int i_remaining = i_total_size - p_sys->i_line_buffer;
+ vlc_memcpy( p_sys->p_line_buffer + p_sys->i_line_buffer,
+ *pp_parser, i_remaining );
+ *pp_parser += i_remaining;
+ p_sys->i_line_buffer = 0;
+
+ return p_sys->p_line_buffer;
+ }
+
+ if ( p_end - *pp_parser < (int)i_total_size )
+ {
+ vlc_memcpy( p_sys->p_line_buffer, *pp_parser,
+ p_end - *pp_parser );
+ p_sys->i_line_buffer = p_end - *pp_parser;
+ return NULL;
+ }
+
+ p_tmp = *pp_parser;
+ *pp_parser += i_total_size;
+ return p_tmp;
+}
+
+#define U (uint16_t)((p_line[0]) | ((p_line[1] & 0x3) << 8))
+#define Y1 (uint16_t)((p_line[1] >> 2) | ((p_line[2] & 0xf) << 6))
+#define V (uint16_t)((p_line[2] >> 4) | ((p_line[3] & 0x3f) << 4))
+#define Y2 (uint16_t)((p_line[3] >> 6) | (p_line[4] << 2))
+
+static void UnpackVBI( const uint8_t *p_line, unsigned int i_size,
+ uint8_t *p_dest )
+{
+ const uint8_t *p_end = p_line + i_size;
+
+ while ( p_line < p_end )
+ {
+ *p_dest++ = (U + 2) / 4;
+ *p_dest++ = (Y1 + 2) / 4;
+ *p_dest++ = (V + 2) / 4;
+ *p_dest++ = (Y2 + 2) / 4;
+ p_line += 5;
+ }
+}
+
+/* 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 + 2) / 4;
+ *p_y++ = (Y1 + 2) / 4;
+ *p_v++ = (V + 2) / 4;
+ *p_y++ = (Y2 + 2) / 4;
+ p_line += 5;
+ }
+}
+
+/* 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 + 2) / 4;
+ *p_u++ = tmp / 4;
+ *p_y++ = (Y1 + 2) / 4;
+ tmp = 3 * *p_v;
+ tmp += (V + 2) / 4;
+ *p_v++ = tmp / 4;
+ *p_y++ = (Y2 + 2) / 4;
+ p_line += 5;
+ }
+}
+
+/* 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 + 2) / 4;
+ *p_u++ = tmp / 4;
+ *p_y++ = (Y1 + 2) / 4;
+ tmp = *p_v;
+ tmp += 3 * (V + 2) / 4;
+ *p_v++ = tmp / 4;
+ *p_y++ = (Y2 + 2) / 4;
+ p_line += 5;
+ }
+}
+
+#undef U
+#undef Y1
+#undef V
+#undef Y2
+
+#define A0 (uint16_t)((p_anc[0]) | ((p_anc[1] & 0x3) << 8))
+#define A1 (uint16_t)((p_anc[1] >> 2) | ((p_anc[2] & 0xf) << 6))
+#define A2 (uint16_t)((p_anc[2] >> 4) | ((p_anc[3] & 0x3f) << 4))
+#define A3 (uint16_t)((p_anc[3] >> 6) | (p_anc[4] << 2))
+
+static void UnpackAnc( const uint8_t *p_anc, unsigned int i_size,
+ uint16_t *p_dest )
+{
+ const uint8_t *p_end = p_anc + i_size;
+
+ while ( p_anc <= p_end - 5 )
+ {
+ *p_dest++ = A0;
+ *p_dest++ = A1;
+ *p_dest++ = A2;
+ *p_dest++ = A3;
+ p_anc += 5;
+ }
+}
+
+#undef A0
+#undef A1
+#undef A2
+#undef A3
+
+static int HasAncillary( const uint8_t *p_anc )
+{
+ return ( (p_anc[0] == 0x0 && p_anc[1] == 0xfc && p_anc[2] == 0xff
+ && (p_anc[3] & 0x3f) == 0x3f) );
+}
+
+static void HandleAudioData( demux_t *p_demux, const uint16_t *p_anc,
+ uint8_t i_data_count, uint8_t i_group,
+ uint8_t i_block_number )
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+
+ if ( i_data_count % 3 )
+ {
+ msg_Warn( p_demux, "malformed audio data for group %u", i_group );
+ return;
+ }
+
+ for ( int i = 0; i < MAX_AUDIOS; i++ )
+ {
+ sdi_audio_t *p_audio = &p_sys->p_audios[i];
+ if ( p_audio->i_group == i_group )
+ {
+ const uint16_t *x = p_anc;
+
+ /* SMPTE 272M says that when parsing a frame, if an audio config
+ * structure is present we will encounter it first. Otherwise
+ * it is assumed to be 48 kHz. */
+ if ( p_audio->p_es == NULL )
+ {
+ p_audio->i_rate = 48000;
+ p_audio->i_delay = 0;
+ if( InitAudio( p_demux, p_audio ) != VLC_SUCCESS )
+ return;
+ }
+
+ if ( i_block_number )
+ {
+ if ( p_audio->i_block_number + 1 != i_block_number )
+ msg_Warn( p_demux,
+ "audio data block discontinuity (%"PRIu8"->%"PRIu8") for group %"PRIu8,
+ p_audio->i_block_number, i_block_number,
+ i_group );
+ if ( i_block_number == 0xff )
+ p_audio->i_block_number = 0;
+ else
+ p_audio->i_block_number = i_block_number;
+ }
+
+ while ( x < p_anc + i_data_count )
+ {
+ if ( ((*x & 0x4) && p_audio->i_pair == 2)
+ || (!(*x & 0x4) && p_audio->i_pair == 1) )
+ {
+ uint32_t i_tmp = (uint32_t)((x[0] & 0x1f1) >> 3)
+ | ((x[1] & 0x1ff) << 6)
+ | ((x[2] & 0x1f) << 15);
+ int32_t i_sample;
+ if ( x[2] & 0x10 )
+ i_sample = i_tmp | 0xfff00000;
+ else
+ i_sample = i_tmp;
+
+ if ( x[0] & 0x2 )
+ {
+ if ( p_audio->i_right_samples < p_audio->i_max_samples )
+ p_audio->p_buffer[2 * p_audio->i_right_samples
+ + 1] = (i_sample + 8) / 16;
+ p_audio->i_right_samples++;
+ }
+ else
+ {
+ if ( p_audio->i_left_samples < p_audio->i_max_samples )
+ p_audio->p_buffer[2 * p_audio->i_left_samples]
+ = (i_sample + 8) / 16;
+ p_audio->i_left_samples++;
+ }
+ }
+ x += 3;
+ }
+ }
+ }
+}
+
+static void HandleAudioConfig( demux_t *p_demux, const uint16_t *p_anc,
+ uint8_t i_data_count, uint8_t i_group )
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+
+ if ( i_data_count != 18 )
+ {
+ msg_Warn( p_demux, "malformed audio config for group %u", i_group );
+ return;
+ }
+
+ for ( int i = 0; i < MAX_AUDIOS; i++ )
+ {
+ sdi_audio_t *p_audio = &p_sys->p_audios[i];
+ if ( p_audio->i_group == i_group && p_audio->p_es == NULL )
+ {
+ unsigned int i_rate;
+
+ if ( p_audio->i_pair == 2 )
+ {
+ i_rate = (p_anc[2] & 0xe0) >> 5;
+ if ( p_anc[7] & 0x1 )
+ {
+ uint32_t i_tmp = ((p_anc[7] & 0x1fe) >> 1)
+ | ((p_anc[8] & 0x1ff) << 8)
+ | ((p_anc[9] & 0x1ff) << 17);
+ if ( p_anc[9] & 0x80 )
+ p_audio->i_delay = i_tmp | 0xfc000000;
+ else
+ p_audio->i_delay = i_tmp;
+ }
+ if ( p_anc[13] & 0x1 )
+ msg_Warn( p_demux, "asymetric audio is not supported" );
+ }
+ else
+ {
+ i_rate = (p_anc[2] & 0xe) >> 1;
+ if ( p_anc[4] & 0x1 )
+ {
+ uint32_t i_tmp = ((p_anc[4] & 0x1fe) >> 1)
+ | ((p_anc[5] & 0x1ff) << 8)
+ | ((p_anc[6] & 0x1ff) << 17);
+ if ( p_anc[6] & 0x80 )
+ p_audio->i_delay = i_tmp | 0xfc000000;
+ else
+ p_audio->i_delay = i_tmp;
+ }
+ if ( p_anc[10] & 0x1 )
+ msg_Warn( p_demux, "asymetric audio is not supported" );
+ }
+
+ switch ( i_rate )
+ {
+ case 0: p_audio->i_rate = 48000; break;
+ case 1: p_audio->i_rate = 44100; break;
+ case 2: p_audio->i_rate = 32000; break;
+ default:
+ msg_Warn( p_demux, "unknown rate for audio %u/%u (%u)",
+ i_group, p_sys->p_audios[i].i_pair, i_rate );
+ continue;
+ }
+
+ if( InitAudio( p_demux, p_audio ) != VLC_SUCCESS )
+ return;
+ }
+ }
+}
+
+/*
+ * Ancillary packet structure:
+ * byte 0: Ancillary Data Flag (0)
+ * byte 1: Ancillary Data Flag (0x3ff)
+ * byte 2: Ancillary Data Flag (0x3ff)
+ * byte 3: Data ID (2 high order bits = parity)
+ * byte 4: Data Block Number 1-255 or 0=unknown (if DID < 0x80)
+ * or Secondary Data ID (if DID >= 0x80)
+ * byte 5: Data Count (10 bits)
+ * byte 6+DC: Checksum
+ */
+static void HandleAncillary( demux_t *p_demux, const uint16_t *p_anc,
+ unsigned int i_size )
+{
+ uint8_t i_data_count;
+
+ if ( i_size < 7
+ || p_anc[0] != 0x0 || p_anc[1] != 0x3ff || p_anc[2] != 0x3ff )
+ return;
+
+ i_data_count = p_anc[5] & 0xff;
+ if ( i_size - 6 < i_data_count )
+ {
+ msg_Warn( p_demux, "malformed ancillary packet (size %u > %u)",
+ i_data_count, i_size - 6 );
+ return;
+ }
+
+ switch ( p_anc[3] ) /* Data ID */
+ {
+ case 0x2ff:
+ HandleAudioData( p_demux, p_anc + 6, i_data_count, 1, p_anc[4] & 0xff );
+ break;
+ case 0x1fd:
+ HandleAudioData( p_demux, p_anc + 6, i_data_count, 2, p_anc[4] & 0xff );
+ break;
+ case 0x1fb:
+ HandleAudioData( p_demux, p_anc + 6, i_data_count, 3, p_anc[4] & 0xff );
+ break;
+ case 0x2f9:
+ HandleAudioData( p_demux, p_anc + 6, i_data_count, 4, p_anc[4] & 0xff );
+ break;
+
+ case 0x1ef:
+ HandleAudioConfig( p_demux, p_anc + 6, i_data_count, 1 );
+ break;
+ case 0x2ee:
+ HandleAudioConfig( p_demux, p_anc + 6, i_data_count, 2 );
+ break;
+ case 0x2ed:
+ HandleAudioConfig( p_demux, p_anc + 6, i_data_count, 3 );
+ break;
+ case 0x1ec:
+ HandleAudioConfig( p_demux, p_anc + 6, i_data_count, 4 );
+ break;
+
+ /* Extended data packets, same order */
+ case 0x1fe:
+ case 0x2fc:
+ case 0x2fa:
+ case 0x1f8:
+
+ default:
+ break;
+
+ case 0x88: /* non-conforming ANC packet */
+ p_anc += 7;
+ i_size -= 7;
+ while ( i_size >= 7 && (p_anc[0] != 0x0 || p_anc[1] != 0x3ff
+ || p_anc[2] != 0x3ff) )
+ {
+ p_anc++;
+ i_size--;
+ }
+ if ( i_size >= 7 )
+ HandleAncillary( p_demux, p_anc, i_size );
+ return;
+ }
+
+ return HandleAncillary( p_demux, p_anc + i_data_count + 7,
+ i_size - i_data_count - 7 );
+
+}
+
+static int HandleSDBuffer( demux_t *p_demux, uint8_t *p_buffer,
+ unsigned int i_buffer_size )
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+ const uint8_t *p_parser = p_buffer;
+ const uint8_t *p_end = p_parser + i_buffer_size;
+ const uint8_t *p_line;
+
+ if ( p_sys->i_state != STATE_SYNC
+ && p_sys->i_last_state_change < mdate() - RESYNC_TIMEOUT )
+ {
+ p_sys->i_state = STATE_NOSYNC;
+ p_sys->i_last_state_change = mdate();
+ return VLC_EGENERIC;
+ }
+
+ switch ( p_sys->i_state )
+ {
+ case STATE_NOSYNC:
+ default:
+ p_parser = FindReferenceCode( FIELD_2_VBLANK_SAV, p_parser, p_end );
+ if ( p_parser == NULL )
+ break;
+ p_sys->i_state = STATE_STARTSYNC;
+ p_sys->i_last_state_change = mdate();
+
+ case STATE_STARTSYNC:
+ p_parser = FindReferenceCode( FIELD_1_VBLANK_EAV, p_parser, p_end );
+ if ( p_parser == NULL )
+ break;
+ p_sys->i_anc_size = 0;
+ p_sys->i_state = STATE_ANCSYNC;
+ p_sys->i_last_state_change = mdate();
+
+ case STATE_ANCSYNC:
+ p_parser = CountReference( &p_sys->i_anc_size,
+ FIELD_1_VBLANK_SAV, p_parser, p_end );
+ if ( p_parser == NULL )
+ break;
+ p_sys->i_active_size = 0;
+ p_sys->i_state = STATE_LINESYNC;
+ p_sys->i_last_state_change = mdate();
+
+ case STATE_LINESYNC:
+ p_parser = CountReference( &p_sys->i_active_size,
+ FIELD_1_VBLANK_EAV, p_parser, p_end );
+ if ( p_parser == NULL )
+ break;
+ p_sys->i_picture_size = p_sys->i_anc_size + p_sys->i_active_size;
+ p_sys->i_state = STATE_ACTIVESYNC;
+ p_sys->i_last_state_change = mdate();
+
+ case STATE_ACTIVESYNC:
+ p_parser = CountReference( &p_sys->i_picture_size,
+ FIELD_1_ACTIVE_EAV, p_parser, p_end );
+ if ( p_parser == NULL )
+ break;
+ p_sys->i_line_offset = p_sys->i_picture_size
+ / (p_sys->i_anc_size + p_sys->i_active_size);
+ p_sys->i_state = STATE_VBLANKSYNC;
+ p_sys->i_last_state_change = mdate();
+
+ case STATE_VBLANKSYNC:
+ p_parser = CountReference( &p_sys->i_picture_size,
+ FIELD_2_ACTIVE_EAV, p_parser, p_end );
+ if ( p_parser == NULL )
+ break;
+ p_sys->i_state = STATE_PICSYNC;
+ p_sys->i_last_state_change = mdate();
+
+ case STATE_PICSYNC:
+ p_parser = CountReference( &p_sys->i_picture_size,
+ FIELD_1_VBLANK_EAV, p_parser, p_end );
+ if ( p_parser == NULL )
+ break;
+
+ if ( p_sys->i_picture_size
+ % (p_sys->i_anc_size + p_sys->i_active_size) )
+ {
+ msg_Warn( p_demux, "wrong picture size (anc=%d active=%d total=%d offset=%d), syncing",
+ p_sys->i_anc_size, p_sys->i_active_size,
+ p_sys->i_picture_size, p_sys->i_line_offset + 1 );
+ p_sys->i_state = STATE_NOSYNC;
+ p_sys->i_last_state_change = mdate();
+ break;
+ }
+
+ p_sys->i_nb_lines = p_sys->i_picture_size
+ / (p_sys->i_anc_size + p_sys->i_active_size);
+ InitVideo( p_demux );
+ msg_Dbg( p_demux,
+ "acquired sync, anc=%d active=%d lines=%d offset=%d",
+ p_sys->i_anc_size, p_sys->i_active_size,
+ p_sys->i_nb_lines, p_sys->i_line_offset + 1 );
+ p_sys->i_state = STATE_SYNC;
+ if( StartDecode( p_demux ) != VLC_SUCCESS )
+ {
+ StopDecode( p_demux );
+ return VLC_ENOMEM;
+ }
+ p_sys->i_current_line = 0;
+ p_sys->p_line_buffer = malloc( p_sys->i_anc_size
+ + p_sys->i_active_size );
+ if( !p_sys->p_line_buffer )
+ {
+ StopDecode( p_demux );
+ return VLC_ENOMEM;
+ }
+ p_sys->i_line_buffer = 0;
+
+ case STATE_SYNC:
+ while ( (p_line = GetLine( p_demux, &p_parser, p_end )) != NULL )
+ {
+ bool b_field = p_sys->b_hd ? false :
+ (p_sys->i_current_line >= p_sys->i_nb_lines / 2);
+ unsigned int i_field_height = p_sys->b_hd ? p_sys->i_height :
+ p_sys->i_height / 2;
+ unsigned int i_field_line = b_field ?
+ p_sys->i_current_line - (p_sys->i_nb_lines + 1) / 2 :
+ p_sys->i_current_line;
+ bool b_vbi = i_field_line < p_sys->i_line_offset ||
+ i_field_line >= p_sys->i_line_offset + i_field_height;
+ unsigned int anc = p_sys->i_anc_size;
+
+ if ( p_line[0] != 0xff || p_line[1] != 0x3
+ || p_line[2] != 0x0 || p_line[3] != 0x0
+ || p_line[anc+0] != 0xff || p_line[anc+1] != 0x3
+ || p_line[anc+2] != 0x0 || p_line[anc+3] != 0x0
+ || (!b_field && b_vbi &&
+ (p_line[4] != FIELD_1_VBLANK_EAV ||
+ p_line[anc+4] != FIELD_1_VBLANK_SAV))
+ || (!b_field && !b_vbi &&
+ (p_line[4] != FIELD_1_ACTIVE_EAV ||
+ p_line[anc+4] != FIELD_1_ACTIVE_SAV))
+ || (b_field && b_vbi &&
+ (p_line[4] != FIELD_2_VBLANK_EAV ||
+ p_line[anc+4] != FIELD_2_VBLANK_SAV))
+ || (b_field && !b_vbi &&
+ (p_line[4] != FIELD_2_ACTIVE_EAV ||
+ p_line[anc+4] != FIELD_2_ACTIVE_SAV)) )
+ {
+ msg_Warn( p_demux, "lost sync line:%u SAV:%x EAV:%x",
+ p_sys->i_current_line + 1, p_line[4], p_line[anc+4] );
+ StopDecode( p_demux );
+ p_sys->i_state = STATE_NOSYNC;
+ p_sys->i_last_state_change = mdate();
+ break;
+ }
+
+ if ( HasAncillary( p_line + 5 ) )
+ {
+ /* HANC */
+ unsigned int i_anc_words = (p_sys->i_anc_size - 5) * 4 / 5;
+ uint16_t p_anc[i_anc_words];
+ UnpackAnc( p_line + 5, p_sys->i_anc_size - 5, p_anc );
+ HandleAncillary( p_demux, p_anc, i_anc_words );
+ }
+
+ if ( !b_vbi )
+ {
+ unsigned int i_active_field_line = i_field_line
+ - p_sys->i_line_offset;
+ unsigned int i_active_line = b_field
+ + i_active_field_line * 2;
+ if ( !(i_active_field_line % 2) && !b_field )
+ Unpack01( p_line + anc + 5, p_sys->i_active_size - 5,
+ p_sys->p_y + p_sys->i_width * i_active_line,
+ p_sys->p_u + (p_sys->i_width / 2)
+ * (i_active_line / 2),
+ p_sys->p_v + (p_sys->i_width / 2)
+ * (i_active_line / 2) );
+ else if ( !(i_active_field_line % 2) )
+ Unpack01( p_line + anc + 5, p_sys->i_active_size - 5,
+ p_sys->p_y + p_sys->i_width * i_active_line,
+ p_sys->p_u + (p_sys->i_width / 2)
+ * (i_active_line / 2 + 1),
+ p_sys->p_v + (p_sys->i_width / 2)
+ * (i_active_line / 2 + 1) );
+ else if ( !b_field )
+ Unpack2( p_line + anc + 5, p_sys->i_active_size - 5,
+ p_sys->p_y + p_sys->i_width * i_active_line,
+ p_sys->p_u + (p_sys->i_width / 2)
+ * (i_active_line / 2 - 1),
+ p_sys->p_v + (p_sys->i_width / 2)
+ * (i_active_line / 2 - 1) );
+ else
+ Unpack3( p_line + anc + 5, p_sys->i_active_size - 5,
+ p_sys->p_y + p_sys->i_width * i_active_line,
+ p_sys->p_u + (p_sys->i_width / 2)
+ * (i_active_line / 2),
+ p_sys->p_v + (p_sys->i_width / 2)
+ * (i_active_line / 2) );
+
+ if ( p_sys->b_vbi && p_sys->i_height == 576
+ && p_sys->i_current_line == p_sys->i_line_offset )
+ {
+ /* Line 23 is half VBI, half active */
+ UnpackVBI( p_line + anc + 5, p_sys->i_active_size - 5,
+ p_sys->p_wss_buffer );
+ }
+ }
+ else if ( p_sys->b_vbi && p_sys->i_telx_count &&
+ i_field_line >= p_sys->i_telx_line &&
+ i_field_line < p_sys->i_telx_line
+ + p_sys->i_telx_count )
+ {
+ UnpackVBI( p_line + anc + 5, p_sys->i_active_size - 5,
+ &p_sys->p_telx_buffer[(i_field_line
+ - p_sys->i_telx_line + b_field * p_sys->i_telx_count)
+ * p_sys->i_width * 2] );
+ }
+ else if ( b_vbi && HasAncillary( p_line + anc + 5 ) )
+ {
+ /* VANC */
+ unsigned int i_anc_words = (p_sys->i_active_size - 5) * 4 / 5;
+ uint16_t p_anc[i_anc_words];
+ UnpackAnc( p_line + 5, p_sys->i_active_size - 5,
+ p_anc );
+ HandleAncillary( p_demux, p_anc, i_anc_words );
+ }
+
+ p_sys->i_current_line++;
+ if ( p_sys->i_current_line == p_sys->i_nb_lines )
+ {
+ p_sys->i_current_line %= p_sys->i_nb_lines;
+ if( DecodeFrame( p_demux ) != VLC_SUCCESS )
+ return VLC_EGENERIC;
+ }
+ }
+ break;
+ }
+
+ return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * Low-level device stuff
+ *****************************************************************************/
+#define MAXLEN 256
+
+static int ReadULSysfs( const char *psz_fmt, unsigned int i_link )
+{
+ char psz_file[MAXLEN], psz_data[MAXLEN];
+ char *psz_tmp;
+ int i_fd;
+ ssize_t i_ret;
+ unsigned int i_data;
+
+ snprintf( psz_file, sizeof(psz_file) - 1, psz_fmt, i_link );
+
+ if ( (i_fd = vlc_open( psz_file, O_RDONLY )) < 0 )
+ return i_fd;
+
+ i_ret = read( i_fd, psz_data, sizeof(psz_data) );
+ close( i_fd );
+
+ if ( i_ret < 0 )
+ return i_ret;
+
+ i_data = strtoul( psz_data, &psz_tmp, 0 );
+ if ( *psz_tmp != '\n' )
+ return -1;
+
+ return i_data;
+}
+
+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;
+}
+
+static int InitCapture( demux_t *p_demux )
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+ const int i_page_size = getpagesize();
+ unsigned int i_bufmemsize;
+ int i_ret;
+ char psz_dev[MAXLEN];
+
+ /* 10-bit mode or nothing */
+ if ( WriteULSysfs( SDI_MODE_FILE, p_sys->i_link, SDI_CTL_MODE_10BIT ) < 0 )
+ {
+ msg_Err( p_demux, "couldn't write file " SDI_MODE_FILE, p_sys->i_link );
+ return VLC_EGENERIC;
+ }
+
+ if ( (i_ret = ReadULSysfs( SDI_BUFFERS_FILE, p_sys->i_link )) < 0 )
+ {
+ msg_Err( p_demux, "couldn't read file " SDI_BUFFERS_FILE,
+ p_sys->i_link );
+ return VLC_EGENERIC;
+ }
+ p_sys->i_buffers = i_ret;
+ p_sys->i_current_buffer = 0;
+
+ if ( (i_ret = ReadULSysfs( SDI_BUFSIZE_FILE, p_sys->i_link )) < 0 )
+ {
+ msg_Err( p_demux, "couldn't read file " SDI_BUFSIZE_FILE,
+ p_sys->i_link );
+ return VLC_EGENERIC;
+ }
+ p_sys->i_buffer_size = i_ret;
+ if ( p_sys->i_buffer_size % 20 )
+ {
+ msg_Err( p_demux, "buffer size must be a multiple of 20" );
+ return VLC_EGENERIC;
+ }
+
+ snprintf( psz_dev, sizeof(psz_dev) - 1, SDI_DEVICE, p_sys->i_link );
+ if ( (p_sys->i_fd = vlc_open( psz_dev, O_RDONLY ) ) < 0 )
+ {
+ msg_Err( p_demux, "couldn't open device %s", psz_dev );
+ return VLC_EGENERIC;
+ }
+
+ i_bufmemsize = ((p_sys->i_buffer_size + i_page_size - 1) / i_page_size)
+ * i_page_size;
+ p_sys->pp_buffers = malloc( p_sys->i_buffers * sizeof(uint8_t *) );
+ if( !p_sys->pp_buffers )
+ return VLC_ENOMEM;
+
+ for ( unsigned int i = 0; i < p_sys->i_buffers; i++ )
+ {
+ if ( (p_sys->pp_buffers[i] = mmap( NULL, p_sys->i_buffer_size,
+ PROT_READ, MAP_SHARED, p_sys->i_fd,
+ i * i_bufmemsize )) == MAP_FAILED )
+ {
+ msg_Err( p_demux, "couldn't mmap(%d): %s", i, strerror(errno) );
+ free( p_sys->pp_buffers );
+ return VLC_EGENERIC;
+ }
+ }
+
+ return VLC_SUCCESS;
+}
+
+static void CloseCapture( demux_t *p_demux )
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+
+ StopDecode( p_demux );
+ for ( unsigned int i = 0; i < p_sys->i_buffers; i++ )
+ munmap( p_sys->pp_buffers[i], p_sys->i_buffer_size );
+ close( p_sys->i_fd );
+ free( p_sys->pp_buffers );
+}
+
+static int Capture( demux_t *p_demux )
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+ struct pollfd pfd;
+
+ pfd.fd = p_sys->i_fd;
+ pfd.events = POLLIN | POLLPRI;
+
+ if ( poll( &pfd, 1, READ_TIMEOUT ) < 0 )
+ {
+ msg_Warn( p_demux, "couldn't poll(): %s", strerror(errno) );
+ return VLC_EGENERIC;
+ }
+
+ if ( pfd.revents & POLLPRI )
+ {
+ unsigned int i_val;
+
+ if ( ioctl( p_sys->i_fd, SDI_IOC_RXGETEVENTS, &i_val ) < 0 )
+ msg_Warn( p_demux, "couldn't SDI_IOC_RXGETEVENTS %s", strerror(errno) );
+ else
+ {
+ if ( i_val & SDI_EVENT_RX_BUFFER )
+ msg_Warn( p_demux, "driver receive buffer queue overrun" );
+ if ( i_val & SDI_EVENT_RX_FIFO )
+ msg_Warn( p_demux, "onboard receive FIFO overrun");
+ if ( i_val & SDI_EVENT_RX_CARRIER )
+ msg_Warn( p_demux, "carrier status change");
+ }
+
+ p_sys->i_next_date += CLOCK_GAP;
+ }
+
+ if ( pfd.revents & POLLIN )
+ {
+ int i_ret;
+
+ if ( ioctl( p_sys->i_fd, SDI_IOC_DQBUF, p_sys->i_current_buffer ) < 0 )
+ {
+ msg_Warn( p_demux, "couldn't SDI_IOC_DQBUF %s", strerror(errno) );
+ return VLC_EGENERIC;
+ }
+
+ i_ret = HandleSDBuffer( p_demux,
+ p_sys->pp_buffers[p_sys->i_current_buffer],
+ p_sys->i_buffer_size );
+
+ if ( ioctl( p_sys->i_fd, SDI_IOC_QBUF, p_sys->i_current_buffer ) < 0 )
+ {
+ msg_Warn( p_demux, "couldn't SDI_IOC_QBUF %s", strerror(errno) );
+ return VLC_EGENERIC;
+ }
+
+ if ( i_ret == VLC_SUCCESS )
+ {
+ p_sys->i_current_buffer++;
+ p_sys->i_current_buffer %= p_sys->i_buffers;
+ }
+ else
+ {
+ /* Reference codes do not start on a multiple of 5. This sometimes
+ * happen. We really don't want to allow this. */
+ msg_Warn( p_demux, "resetting board" );
+ CloseCapture( p_demux );
+ InitCapture( p_demux );
+ }
+ }
+
+ return VLC_SUCCESS;
+}
+
--- /dev/null
+/* sdi.h
+ *
+ * Shared header file for the Linux user-space API for
+ * Linear Systems Ltd. SMPTE 259M-C interface boards.
+ *
+ * Copyright (C) 2004-2010 Linear Systems Ltd.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of Linear Systems Ltd. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY LINEAR SYSTEMS LTD. "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL LINEAR SYSTEMS LTD. OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Linear Systems can be contacted at <http://www.linsys.ca/>.
+ *
+ */
+
+#ifndef _SDI_H
+#define _SDI_H
+
+/* Driver info */
+#define SDI_DRIVER_NAME "sdi"
+
+#define SDI_MAJOR 121 /* Set to 0 for dynamic allocation.
+ * Otherwise, 121 is available.
+ * See /usr/src/linux/Documentation/devices.txt */
+
+#define SDI_TX_BUFFERS_MIN 2 /* This must be at least 2 */
+/* The minimum transmit buffer size must be positive, divisible by 4,
+ * and large enough that the buffers aren't transferred to the onboard FIFOs
+ * too quickly for the machine to handle the interrupts.
+ * This is especially a problem at startup, when the FIFOs are empty.
+ * Relevant factors include onboard FIFO size, PCI bus throughput,
+ * processor speed, and interrupt latency. */
+#define SDI_TX_BUFSIZE_MIN 1024
+#define SDI_RX_BUFFERS_MIN 2 /* This must be at least 2 */
+#define SDI_RX_BUFSIZE_MIN 8 /* This must be positive and divisible by 4 */
+
+#define SDI_TX_BUFFERS 25 /* This must be at least 2 */
+#define SDI_TX_BUFSIZE 1235520 /* This must be positive and divisible by 4 */
+#define SDI_RX_BUFFERS 25 /* This must be at least 2 */
+#define SDI_RX_BUFSIZE 1235520 /* This must be positive and divisible by 4 */
+
+/* Ioctl () definitions */
+#define SDI_IOC_MAGIC '=' /* This ioctl magic number is currently free. See
+ * /usr/src/linux/Documentation/ioctl-number.txt */
+
+#define SDI_IOC_TXGETCAP _IOR(SDI_IOC_MAGIC, 1, unsigned int)
+#define SDI_IOC_TXGETEVENTS _IOR(SDI_IOC_MAGIC, 2, unsigned int)
+#define SDI_IOC_TXGETBUFLEVEL _IOR(SDI_IOC_MAGIC, 3, unsigned int)
+#define SDI_IOC_TXGETTXD _IOR(SDI_IOC_MAGIC, 4, int)
+
+#define SDI_IOC_RXGETCAP _IOR(SDI_IOC_MAGIC, 65, unsigned int)
+#define SDI_IOC_RXGETEVENTS _IOR(SDI_IOC_MAGIC, 66, unsigned int)
+#define SDI_IOC_RXGETBUFLEVEL _IOR(SDI_IOC_MAGIC, 67, unsigned int)
+#define SDI_IOC_RXGETCARRIER _IOR(SDI_IOC_MAGIC, 68, int)
+#define SDI_IOC_RXGETSTATUS _IOR(SDI_IOC_MAGIC, 69, int)
+
+#define SDI_IOC_GETID _IOR(SDI_IOC_MAGIC, 129, unsigned int)
+#define SDI_IOC_GETVERSION _IOR(SDI_IOC_MAGIC, 130, unsigned int)
+/* Provide compatibility with applications compiled for older API */
+#define SDI_IOC_QBUF_DEPRECATED _IOR(SDI_IOC_MAGIC, 131, unsigned int)
+#define SDI_IOC_QBUF_DEPRECATED2 _IOW(SDI_IOC_MAGIC, 131, unsigned int)
+#define SDI_IOC_QBUF _IO(SDI_IOC_MAGIC, 131)
+/* Provide compatibility with applications compiled for older API */
+#define SDI_IOC_DQBUF_DEPRECATED _IOR(SDI_IOC_MAGIC, 132, unsigned int)
+#define SDI_IOC_DQBUF_DEPRECATED2 _IOW(SDI_IOC_MAGIC, 132, unsigned int)
+#define SDI_IOC_DQBUF _IO(SDI_IOC_MAGIC, 132)
+
+/* Transmitter event flag bit locations */
+#define SDI_EVENT_TX_BUFFER_ORDER 0
+#define SDI_EVENT_TX_BUFFER (1 << SDI_EVENT_TX_BUFFER_ORDER)
+#define SDI_EVENT_TX_FIFO_ORDER 1
+#define SDI_EVENT_TX_FIFO (1 << SDI_EVENT_TX_FIFO_ORDER)
+#define SDI_EVENT_TX_DATA_ORDER 2
+#define SDI_EVENT_TX_DATA (1 << SDI_EVENT_TX_DATA_ORDER)
+
+/* Receiver event flag bit locations */
+#define SDI_EVENT_RX_BUFFER_ORDER 0
+#define SDI_EVENT_RX_BUFFER (1 << SDI_EVENT_RX_BUFFER_ORDER)
+#define SDI_EVENT_RX_FIFO_ORDER 1
+#define SDI_EVENT_RX_FIFO (1 << SDI_EVENT_RX_FIFO_ORDER)
+#define SDI_EVENT_RX_CARRIER_ORDER 2
+#define SDI_EVENT_RX_CARRIER (1 << SDI_EVENT_RX_CARRIER_ORDER)
+
+/* Interface capabilities */
+#define SDI_CAP_TX_RXCLKSRC 0x00000001
+
+/* Transmitter clock source settings */
+#define SDI_CTL_TX_CLKSRC_ONBOARD 0
+#define SDI_CTL_TX_CLKSRC_EXT 1
+#define SDI_CTL_TX_CLKSRC_RX 2
+
+/* Mode settings */
+#define SDI_CTL_MODE_8BIT 0
+#define SDI_CTL_MODE_10BIT 1
+
+#endif
+
--- /dev/null
+/* sdiaudio.h
+ *
+ * Shared header file for the Linux user-space API for
+ * Linear Systems Ltd. SMPTE 292M and SMPTE 259M-C Audio interface boards.
+ *
+ * Copyright (C) 2009-2010 Linear Systems Ltd.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of Linear Systems Ltd. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY LINEAR SYSTEMS LTD. "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL LINEAR SYSTEMS LTD. OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Linear Systems can be contacted at <http://www.linsys.ca/>.
+ *
+ */
+
+#ifndef _SDIAUDIO_H
+#define _SDIAUDIO_H
+
+/* Driver info */
+#define SDIAUDIO_DRIVER_NAME "sdiaudio"
+
+#define SDIAUDIO_MAJOR 0 /* Set to 0 for dynamic allocation.
+ * See /usr/src/linux/Documentation/devices.txt */
+
+#define SDIAUDIO_TX_BUFFERS_MIN 2 /* This must be at least 2 */
+/* The minimum transmit buffer size must be positive, divisible by 4,
+ * and large enough that the buffers aren't transferred to the onboard FIFOs
+ * too quickly for the machine to handle the interrupts.
+ * This is especially a problem at startup, when the FIFOs are empty.
+ * Relevant factors include onboard FIFO size, PCI bus throughput,
+ * processor speed, and interrupt latency. */
+#define SDIAUDIO_TX_BUFSIZE_MIN 1024
+#define SDIAUDIO_RX_BUFFERS_MIN 2 /* This must be at least 2 */
+#define SDIAUDIO_RX_BUFSIZE_MIN 8 /* This must be positive and divisible by 4 */
+
+#define SDIAUDIO_TX_BUFFERS 30 /* This must be at least 2 */
+#define SDIAUDIO_TX_BUFSIZE 6400 /* This must be positive and divisible by 4 */
+#define SDIAUDIO_RX_BUFFERS 30 /* This must be at least 2 */
+#define SDIAUDIO_RX_BUFSIZE 6400 /* This must be positive and divisible by 4 */
+
+/* Ioctl () definitions */
+#define SDIAUDIO_IOC_MAGIC '~' /* This ioctl magic number is currently free. See
+ * /usr/src/linux/Documentation/ioctl-number.txt */
+
+#define SDIAUDIO_IOC_TXGETCAP _IOR(SDIAUDIO_IOC_MAGIC, 1, unsigned int)
+#define SDIAUDIO_IOC_TXGETEVENTS _IOR(SDIAUDIO_IOC_MAGIC, 2, unsigned int)
+#define SDIAUDIO_IOC_TXGETBUFLEVEL _IOR(SDIAUDIO_IOC_MAGIC, 3, unsigned int)
+#define SDIAUDIO_IOC_TXGETTXD _IOR(SDIAUDIO_IOC_MAGIC, 4, int)
+
+#define SDIAUDIO_IOC_RXGETCAP _IOR(SDIAUDIO_IOC_MAGIC, 65, unsigned int)
+#define SDIAUDIO_IOC_RXGETEVENTS _IOR(SDIAUDIO_IOC_MAGIC, 66, unsigned int)
+#define SDIAUDIO_IOC_RXGETBUFLEVEL _IOR(SDIAUDIO_IOC_MAGIC, 67, unsigned int)
+#define SDIAUDIO_IOC_RXGETCARRIER _IOR(SDIAUDIO_IOC_MAGIC, 68, int)
+#define SDIAUDIO_IOC_RXGETSTATUS _IOR(SDIAUDIO_IOC_MAGIC, 69, int)
+#define SDIAUDIO_IOC_RXGETAUDIOGR0ERROR _IOR(SDIAUDIO_IOC_MAGIC, 70, unsigned int)
+#define SDIAUDIO_IOC_RXGETAUDIOGR0DELAYA _IOR(SDIAUDIO_IOC_MAGIC, 71, unsigned int)
+#define SDIAUDIO_IOC_RXGETAUDIOGR0DELAYB _IOR(SDIAUDIO_IOC_MAGIC, 72, unsigned int)
+#define SDIAUDIO_IOC_RXGETNONAUDIO _IOR(SDIAUDIO_IOC_MAGIC, 73, unsigned int)
+#define SDIAUDIO_IOC_RXGETAUDSTAT _IOR(SDIAUDIO_IOC_MAGIC, 74, unsigned int)
+#define SDIAUDIO_IOC_RXGETAUDRATE _IOR(SDIAUDIO_IOC_MAGIC, 75, unsigned int)
+
+#define SDIAUDIO_IOC_GETID _IOR(SDIAUDIO_IOC_MAGIC, 129, unsigned int)
+#define SDIAUDIO_IOC_GETVERSION _IOR(SDIAUDIO_IOC_MAGIC, 130, unsigned int)
+/* Provide compatibility with applications compiled for older API */
+#define SDIAUDIO_IOC_QBUF_DEPRECATED _IOW(SDIAUDIO_IOC_MAGIC, 131, unsigned int)
+#define SDIAUDIO_IOC_QBUF _IO(SDIAUDIO_IOC_MAGIC, 131)
+/* Provide compatibility with applications compiled for older API */
+#define SDIAUDIO_IOC_DQBUF_DEPRECATED _IOW(SDIAUDIO_IOC_MAGIC, 132, unsigned int)
+#define SDIAUDIO_IOC_DQBUF _IO(SDIAUDIO_IOC_MAGIC, 132)
+
+/* Transmitter event flag bit locations */
+#define SDIAUDIO_EVENT_TX_BUFFER_ORDER 0
+#define SDIAUDIO_EVENT_TX_BUFFER (1 << SDIAUDIO_EVENT_TX_BUFFER_ORDER)
+#define SDIAUDIO_EVENT_TX_FIFO_ORDER 1
+#define SDIAUDIO_EVENT_TX_FIFO (1 << SDIAUDIO_EVENT_TX_FIFO_ORDER)
+#define SDIAUDIO_EVENT_TX_DATA_ORDER 2
+#define SDIAUDIO_EVENT_TX_DATA (1 << SDIAUDIO_EVENT_TX_DATA_ORDER)
+
+/* Receiver event flag bit locations */
+#define SDIAUDIO_EVENT_RX_BUFFER_ORDER 0
+#define SDIAUDIO_EVENT_RX_BUFFER (1 << SDIAUDIO_EVENT_RX_BUFFER_ORDER)
+#define SDIAUDIO_EVENT_RX_FIFO_ORDER 1
+#define SDIAUDIO_EVENT_RX_FIFO (1 << SDIAUDIO_EVENT_RX_FIFO_ORDER)
+#define SDIAUDIO_EVENT_RX_CARRIER_ORDER 2
+#define SDIAUDIO_EVENT_RX_CARRIER (1 << SDIAUDIO_EVENT_RX_CARRIER_ORDER)
+#define SDIAUDIO_EVENT_RX_DATA_ORDER 3
+#define SDIAUDIO_EVENT_RX_DATA (1 << SDIAUDIO_EVENT_RX_DATA_ORDER)
+
+/* Interface capabilities */
+#define SDIAUDIO_CAP_RX_CD 0x00000001
+#define SDIAUDIO_CAP_RX_DATA 0x00000002
+#define SDIAUDIO_CAP_RX_STATS 0x00000004
+#define SDIAUDIO_CAP_RX_NONAUDIO 0x00000008
+#define SDIAUDIO_CAP_RX_24BIT 0x00000010
+
+/* Audio sample size */
+#define SDIAUDIO_CTL_AUDSAMP_SZ_16 16 /* 16 bit */
+#define SDIAUDIO_CTL_AUDSAMP_SZ_24 24 /* 24 bit */
+#define SDIAUDIO_CTL_AUDSAMP_SZ_32 32 /* 32 bit */
+
+/* Audio channel enable */
+#define SDIAUDIO_CTL_AUDCH_EN_0 0 /* 0 channel/disable audio */
+#define SDIAUDIO_CTL_AUDCH_EN_2 2 /* 2 channel */
+#define SDIAUDIO_CTL_AUDCH_EN_4 4 /* 4 channel */
+#define SDIAUDIO_CTL_AUDCH_EN_6 6 /* 6 channel */
+#define SDIAUDIO_CTL_AUDCH_EN_8 8 /* 8 channel */
+
+#define SDIAUDIO_CTL_PCM_ALLCHANNEL 0x00000000 /* PCM for channel 1 - 8 */
+#define SDIAUDIO_CTL_NONAUDIO_ALLCHANNEL 0x000000ff /* No audio for channel 1 - 8 */
+
+/* Active audio channels status */
+#define SDIAUDIO_CTL_ACT_CHAN_0 0x00 /* no audio control packets */
+#define SDIAUDIO_CTL_ACT_CHAN_2 0x03 /* 2 channels */
+#define SDIAUDIO_CTL_ACT_CHAN_4 0x0f /* 4 channels */
+#define SDIAUDIO_CTL_ACT_CHAN_6 0x3f /* 6 channels */
+#define SDIAUDIO_CTL_ACT_CHAN_8 0xff /* 8 channels */
+
+/* Audio rate */
+#define SDIAUDIO_CTL_SYNC_48_KHZ 0 /* Synchronous, 48 kHz */
+#define SDIAUDIO_CTL_SYNC_44_1_KHZ 2 /* Synchronous, 44.1 kHz */
+#define SDIAUDIO_CTL_SYNC_32_KHZ 4 /* Synchronous, 32 kHz */
+#define SDIAUDIO_CTL_SYNC_96_KHZ 8 /* Synchronous, 96 kHz */
+#define SDIAUDIO_CTL_SYNC_FREE_RUNNING 14 /* Synchronous, free running */
+#define SDIAUDIO_CTL_ASYNC_48_KHZ 1 /* Asynchronous, 48 kHz */
+#define SDIAUDIO_CTL_ASYNC_44_1_KHZ 3 /* Asynchronous, 44.1 kHz */
+#define SDIAUDIO_CTL_ASYNC_32_KHZ 5 /* Asynchronous, 32 kHz */
+#define SDIAUDIO_CTL_ASYNC_96_KHZ 9 /* Asynchronous, 96 kHz */
+#define SDIAUDIO_CTL_ASYNC_FREE_RUNNING 15 /* Asynchronous, free running */
+
+#endif
+
--- /dev/null
+/* sdivideo.h
+ *
+ * Shared header file for the Linux user-space API for
+ * Linear Systems Ltd. SMPTE 292M and SMPTE 259M-C interface boards.
+ *
+ * Copyright (C) 2009-2010 Linear Systems Ltd.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of Linear Systems Ltd. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY LINEAR SYSTEMS LTD. "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL LINEAR SYSTEMS LTD. OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Linear Systems can be contacted at <http://www.linsys.ca/>.
+ *
+ */
+
+#ifndef _SDIVIDEO_H
+#define _SDIVIDEO_H
+
+/* Driver info */
+#define SDIVIDEO_DRIVER_NAME "sdivideo"
+
+#define SDIVIDEO_MAJOR 0 /* Set to 0 for dynamic allocation.
+ * See /usr/src/linux/Documentation/devices.txt */
+
+#define SDIVIDEO_TX_BUFFERS_MIN 2 /* This must be at least 2 */
+/* The minimum transmit buffer size must be positive, divisible by 4,
+ * and large enough that the buffers aren't transferred to the onboard FIFOs
+ * too quickly for the machine to handle the interrupts.
+ * This is especially a problem at startup, when the FIFOs are empty.
+ * Relevant factors include onboard FIFO size, PCI bus throughput,
+ * processor speed, and interrupt latency. */
+#define SDIVIDEO_TX_BUFSIZE_MIN 1024
+#define SDIVIDEO_RX_BUFFERS_MIN 2 /* This must be at least 2 */
+#define SDIVIDEO_RX_BUFSIZE_MIN 8 /* This must be positive and divisible by 4 */
+
+#define SDIVIDEO_TX_BUFFERS 30 /* This must be at least 2 */
+#define SDIVIDEO_TX_BUFSIZE 1843200 /* This must be positive and divisible by 4 */
+#define SDIVIDEO_RX_BUFFERS 30 /* This must be at least 2 */
+#define SDIVIDEO_RX_BUFSIZE 1843200 /* This must be positive and divisible by 4 */
+
+/* Ioctl () definitions */
+#define SDIVIDEO_IOC_MAGIC '=' /* This ioctl magic number is currently free. See
+ * /usr/src/linux/Documentation/ioctl-number.txt */
+
+#define SDIVIDEO_IOC_TXGETCAP _IOR(SDIVIDEO_IOC_MAGIC, 1, unsigned int)
+#define SDIVIDEO_IOC_TXGETEVENTS _IOR(SDIVIDEO_IOC_MAGIC, 2, unsigned int)
+#define SDIVIDEO_IOC_TXGETBUFLEVEL _IOR(SDIVIDEO_IOC_MAGIC, 3, unsigned int)
+#define SDIVIDEO_IOC_TXGETTXD _IOR(SDIVIDEO_IOC_MAGIC, 4, int)
+#define SDIVIDEO_IOC_TXGETREF _IOR(SDIVIDEO_IOC_MAGIC, 5, unsigned int)
+
+#define SDIVIDEO_IOC_RXGETCAP _IOR(SDIVIDEO_IOC_MAGIC, 65, unsigned int)
+#define SDIVIDEO_IOC_RXGETEVENTS _IOR(SDIVIDEO_IOC_MAGIC, 66, unsigned int)
+#define SDIVIDEO_IOC_RXGETBUFLEVEL _IOR(SDIVIDEO_IOC_MAGIC, 67, unsigned int)
+#define SDIVIDEO_IOC_RXGETCARRIER _IOR(SDIVIDEO_IOC_MAGIC, 68, int)
+#define SDIVIDEO_IOC_RXGETSTATUS _IOR(SDIVIDEO_IOC_MAGIC, 69, int)
+#define SDIVIDEO_IOC_RXGETYCRCERROR _IOR(SDIVIDEO_IOC_MAGIC, 70, unsigned int)
+#define SDIVIDEO_IOC_RXGETCCRCERROR _IOR(SDIVIDEO_IOC_MAGIC, 71, unsigned int)
+#define SDIVIDEO_IOC_RXGETVIDSTATUS _IOR(SDIVIDEO_IOC_MAGIC, 72, unsigned int)
+
+#define SDIVIDEO_IOC_GETID _IOR(SDIVIDEO_IOC_MAGIC, 129, unsigned int)
+#define SDIVIDEO_IOC_GETVERSION _IOR(SDIVIDEO_IOC_MAGIC, 130, unsigned int)
+/* Provide compatibility with applications compiled for older API */
+#define SDIVIDEO_IOC_QBUF_DEPRECATED _IOW(SDIVIDEO_IOC_MAGIC, 131, unsigned int)
+#define SDIVIDEO_IOC_QBUF _IO(SDIVIDEO_IOC_MAGIC, 131)
+/* Provide compatibility with applications compiled for older API */
+#define SDIVIDEO_IOC_DQBUF_DEPRECATED _IOW(SDIVIDEO_IOC_MAGIC, 132, unsigned int)
+#define SDIVIDEO_IOC_DQBUF _IO(SDIVIDEO_IOC_MAGIC, 132)
+
+/* Transmitter event flag bit locations */
+#define SDIVIDEO_EVENT_TX_BUFFER_ORDER 0
+#define SDIVIDEO_EVENT_TX_BUFFER (1 << SDIVIDEO_EVENT_TX_BUFFER_ORDER)
+#define SDIVIDEO_EVENT_TX_FIFO_ORDER 1
+#define SDIVIDEO_EVENT_TX_FIFO (1 << SDIVIDEO_EVENT_TX_FIFO_ORDER)
+#define SDIVIDEO_EVENT_TX_DATA_ORDER 2
+#define SDIVIDEO_EVENT_TX_DATA (1 << SDIVIDEO_EVENT_TX_DATA_ORDER)
+#define SDIVIDEO_EVENT_TX_REF_ORDER 3
+#define SDIVIDEO_EVENT_TX_REF (1 << SDIVIDEO_EVENT_TX_REF_ORDER)
+
+/* Receiver event flag bit locations */
+#define SDIVIDEO_EVENT_RX_BUFFER_ORDER 0
+#define SDIVIDEO_EVENT_RX_BUFFER (1 << SDIVIDEO_EVENT_RX_BUFFER_ORDER)
+#define SDIVIDEO_EVENT_RX_FIFO_ORDER 1
+#define SDIVIDEO_EVENT_RX_FIFO (1 << SDIVIDEO_EVENT_RX_FIFO_ORDER)
+#define SDIVIDEO_EVENT_RX_CARRIER_ORDER 2
+#define SDIVIDEO_EVENT_RX_CARRIER (1 << SDIVIDEO_EVENT_RX_CARRIER_ORDER)
+#define SDIVIDEO_EVENT_RX_DATA_ORDER 3
+#define SDIVIDEO_EVENT_RX_DATA (1 << SDIVIDEO_EVENT_RX_DATA_ORDER)
+#define SDIVIDEO_EVENT_RX_STD_ORDER 4
+#define SDIVIDEO_EVENT_RX_STD (1 << SDIVIDEO_EVENT_RX_STD_ORDER)
+
+/* Interface capabilities */
+#define SDIVIDEO_CAP_RX_CD 0x00000001
+#define SDIVIDEO_CAP_RX_DATA 0x00000002
+#define SDIVIDEO_CAP_RX_ERR_COUNT 0x00000004
+#define SDIVIDEO_CAP_RX_VBI 0x00000008
+#define SDIVIDEO_CAP_RX_RAWMODE 0x00000010
+#define SDIVIDEO_CAP_RX_DEINTERLACING 0x00000020
+
+/* Transmitter clock source settings */
+#define SDIVIDEO_CTL_TX_CLKSRC_ONBOARD 0
+#define SDIVIDEO_CTL_TX_CLKSRC_NTSC 1
+#define SDIVIDEO_CTL_TX_CLKSRC_PAL 2
+#define SDIVIDEO_CTL_TX_CLKSRC_525P 3
+#define SDIVIDEO_CTL_TX_CLKSRC_625P 4
+#define SDIVIDEO_CTL_TX_CLKSRC_720P_60 5
+#define SDIVIDEO_CTL_TX_CLKSRC_720P_59_94 6
+#define SDIVIDEO_CTL_TX_CLKSRC_720P_50 7
+#define SDIVIDEO_CTL_TX_CLKSRC_720P_30 8
+#define SDIVIDEO_CTL_TX_CLKSRC_720P_29_97 9
+#define SDIVIDEO_CTL_TX_CLKSRC_720P_25 10
+#define SDIVIDEO_CTL_TX_CLKSRC_720P_24 11
+#define SDIVIDEO_CTL_TX_CLKSRC_720P_23_98 12
+#define SDIVIDEO_CTL_TX_CLKSRC_1080P_60 13
+#define SDIVIDEO_CTL_TX_CLKSRC_1080P_59_94 14
+#define SDIVIDEO_CTL_TX_CLKSRC_1080P_50 15
+#define SDIVIDEO_CTL_TX_CLKSRC_1080P_30 16
+#define SDIVIDEO_CTL_TX_CLKSRC_1080P_29_97 17
+#define SDIVIDEO_CTL_TX_CLKSRC_1080P_25 18
+#define SDIVIDEO_CTL_TX_CLKSRC_1080P_24 19
+#define SDIVIDEO_CTL_TX_CLKSRC_1080P_23_98 20
+#define SDIVIDEO_CTL_TX_CLKSRC_1080I_60 21
+#define SDIVIDEO_CTL_TX_CLKSRC_1080I_59_94 22
+#define SDIVIDEO_CTL_TX_CLKSRC_1080I_50 23
+
+/* Mode settings */
+#define SDIVIDEO_CTL_MODE_UYVY 0
+#define SDIVIDEO_CTL_MODE_V210 1
+#define SDIVIDEO_CTL_MODE_V210_DEINTERLACE 2
+#define SDIVIDEO_CTL_MODE_RAW 3
+
+/* Frame mode settings */
+#define SDIVIDEO_CTL_UNLOCKED 0
+#define SDIVIDEO_CTL_SMPTE_125M_486I_59_94HZ 1
+#define SDIVIDEO_CTL_BT_601_576I_50HZ 2
+#define SDIVIDEO_CTL_SMPTE_260M_1035I_60HZ 5
+#define SDIVIDEO_CTL_SMPTE_260M_1035I_59_94HZ 6
+#define SDIVIDEO_CTL_SMPTE_295M_1080I_50HZ 7
+#define SDIVIDEO_CTL_SMPTE_274M_1080I_60HZ 8
+#define SDIVIDEO_CTL_SMPTE_274M_1080PSF_30HZ 9
+#define SDIVIDEO_CTL_SMPTE_274M_1080I_59_94HZ 10
+#define SDIVIDEO_CTL_SMPTE_274M_1080PSF_29_97HZ 11
+#define SDIVIDEO_CTL_SMPTE_274M_1080I_50HZ 12
+#define SDIVIDEO_CTL_SMPTE_274M_1080PSF_25HZ 13
+#define SDIVIDEO_CTL_SMPTE_274M_1080PSF_24HZ 14
+#define SDIVIDEO_CTL_SMPTE_274M_1080PSF_23_98HZ 15
+#define SDIVIDEO_CTL_SMPTE_274M_1080P_30HZ 16
+#define SDIVIDEO_CTL_SMPTE_274M_1080P_29_97HZ 17
+#define SDIVIDEO_CTL_SMPTE_274M_1080P_25HZ 18
+#define SDIVIDEO_CTL_SMPTE_274M_1080P_24HZ 19
+#define SDIVIDEO_CTL_SMPTE_274M_1080P_23_98HZ 20
+#define SDIVIDEO_CTL_SMPTE_296M_720P_60HZ 21
+#define SDIVIDEO_CTL_SMPTE_296M_720P_59_94HZ 22
+#define SDIVIDEO_CTL_SMPTE_296M_720P_50HZ 23
+#define SDIVIDEO_CTL_SMPTE_296M_720P_30HZ 24
+#define SDIVIDEO_CTL_SMPTE_296M_720P_29_97HZ 25
+#define SDIVIDEO_CTL_SMPTE_296M_720P_25HZ 26
+#define SDIVIDEO_CTL_SMPTE_296M_720P_24HZ 27
+#define SDIVIDEO_CTL_SMPTE_296M_720P_23_98HZ 28
+
+#endif
+
modules/access/http.c
modules/access/imem.c
modules/access/jack.c
+modules/access/linsys/linsys_sdi.c
+modules/access/linsys/linsys_hdsdi.c
modules/access/mms/asf.c
modules/access/mms/asf.h
modules/access/mms/buffer.c