Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  linsys_sdi.c   Sprache: C

 

/*****************************************************************************
 * 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 Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/


#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

/*****************************************************************************
 * Preamble
 *****************************************************************************/

#include <poll.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <limits.h>

#include <vlc_common.h>
#include <vlc_plugin.h>

#include <vlc_input.h>
#include <vlc_access.h>
#include <vlc_demux.h>
#include <vlc_poll.h>

#include <vlc_fs.h>

#include "linsys_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         VLC_TICK_FROM_MS(500)
#define START_DATE        INT64_C(0x100000000)

#define DEMUX_BUFFER_SIZE 1350000
#define MAX_AUDIOS        4
#define SAMPLERATE_TOLERANCE 0.1f

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/

#define LINK_TEXT N_("Link #")
#define LINK_LONGTEXT N_( \
    "Allows you to set the desired link of the board for the capture (starting at 0)." )
#define VIDEO_TEXT N_("Video ID")
#define VIDEO_LONGTEXT N_( \
    "Allows you to set the ES ID of the video." )
#define VIDEO_ASPECT_TEXT N_("Aspect ratio")
#define VIDEO_ASPECT_LONGTEXT N_( \
    "Allows you to force the aspect ratio of the video." )
#define AUDIO_TEXT N_("Audio configuration")
#define AUDIO_LONGTEXT N_( \
    "Allows you to set audio configuration (id=group,pair:id=group,pair...)." )
#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_subcategory( SUBCAT_INPUT_ACCESS )

    add_integer( "linsys-sdi-link", 0,
        LINK_TEXT, LINK_LONGTEXT )

    add_integer( "linsys-sdi-id-video", 0, VIDEO_TEXT, VIDEO_LONGTEXT )
    add_string( "linsys-sdi-aspect-ratio", "", VIDEO_ASPECT_TEXT,
                VIDEO_ASPECT_LONGTEXT )
    add_string( "linsys-sdi-audio", "0=1,1", AUDIO_TEXT, AUDIO_LONGTEXT )
    add_string( "linsys-sdi-telx", "", TELX_TEXT, TELX_LONGTEXT )
    add_string( "linsys-sdi-telx-lang", "", TELX_LANG_TEXT, TELX_LANG_LONGTEXT )

    set_capability( "access", 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,
};

typedef struct
{
    /* 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;
    vlc_tick_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;
    vlc_tick_t       i_next_date;
    vlc_tick_t       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;
} demux_sys_t;

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;

    if (p_demux->out == NULL)
        return VLC_EGENERIC;

    /* 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 = vlc_tick_now();

    /* SDI AR */
    unsigned int i_num, i_den;
    if ( !var_InheritURational( p_demux, &i_num, &i_den,
                               "linsys-hdsdi-aspect-ratio" ) && i_den != 0 )
        p_sys->i_forced_aspect = p_sys->i_aspect =
                i_num * VOUT_ASPECT_FACTOR / i_den;
    else
        p_sys->i_forced_aspect = 0;

    /* */
    p_sys->i_id_video = var_InheritInteger( p_demux, "linsys-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;

    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 = vlc_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;

    switch( i_query )
    {
        /* Special for access_demux */
        case DEMUX_CAN_PAUSE:
        case DEMUX_CAN_CONTROL_PACE:
            /* TODO */
            pb = va_arg( args, bool * );
            *pb = false;
            return VLC_SUCCESS;

        case DEMUX_GET_PTS_DELAY:
            *va_arg( args, vlc_tick_t * ) =
                VLC_TICK_FROM_MS(var_InheritInteger( p_demux, "live-caching" ));
            return VLC_SUCCESS;

        /* TODO implement others */
        default:
            return VLC_EGENERIC;
    }
}

/*****************************************************************************
 * 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_Alloc( 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 = vlc_tick_from_samples(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 = p[0] & 7;

        if ( !parity(p[0] & 15) )
            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 = vlc_alloc( 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_Alloc( 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 = AOUT_CHANS_STEREO;
    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.f + SAMPLERATE_TOLERANCE);

    p_audio->p_buffer             = vlc_alloc( 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.f;

    *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.f;
        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.f ) *p_out = 32767;
            else if ( f_out < -1.f ) *p_out = -32768;
            else *p_out = f_out * 32768.f;
            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.f - SAMPLERATE_TOLERANCE) ||
        p_audio->i_left_samples >
            (float)p_audio->i_nb_samples * (1.f + 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.f - SAMPLERATE_TOLERANCE) ||
        p_audio->i_right_samples >
            (float)p_audio->i_nb_samples * (1.f + 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_Alloc( 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
        + vlc_tick_from_samples(p_audio->i_delay, 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 )
        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 */

            memset( p_sys->p_y, 0, p_sys->i_width / 2 );
            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_SetPCR( p_demux->out, 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;
        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 )
    {
        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, "asymmetric 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, "asymmetric 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 < vlc_tick_now() - RESYNC_TIMEOUT )
    {
        p_sys->i_state = STATE_NOSYNC;
        p_sys->i_last_state_change = vlc_tick_now();
        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 = vlc_tick_now();
        /* fallthrough */

    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 = vlc_tick_now();
        /* fallthrough */

    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 = vlc_tick_now();
        /* fallthrough */

    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 = vlc_tick_now();
        /* fallthrough */

    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 = vlc_tick_now();
        /* fallthrough */

    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 = vlc_tick_now();
        /* fallthrough */

    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 = vlc_tick_now();
            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 = vlc_tick_now();
                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];
    unsigned int i_data;

    snprintf( psz_file, sizeof(psz_file), psz_fmt, i_link );

    FILE *stream = vlc_fopen( psz_file, "rt" );
    if( stream == NULL )
        return -1;

    int ret = fscanf( stream, "%u", &i_data );
    fclose( stream );

    return (ret == 1 && i_data <= INT_MAX) ? (int)i_data : -1;
}

static int WriteULSysfs( const char *psz_fmt, unsigned int i_link,
                         unsigned int i_buf )
{
    char psz_file[MAXLEN];

    snprintf( psz_file, sizeof(psz_file), psz_fmt, i_link );

    FILE *stream = vlc_fopen( psz_file, "wt" );
    if( stream == NULL )
        return -1;

    int ret = fprintf( stream, "%u\n", i_buf );
    fclose( stream );
    return 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 = vlc_alloc( 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,
                     vlc_strerror_c(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 );
    vlc_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", vlc_strerror_c(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",
                      vlc_strerror_c(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",
                      vlc_strerror_c(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",
                      vlc_strerror_c(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;
}


Messung
CHZG
83979090

¤ Dauer der Verarbeitung: 0.16 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1104856
#Domains=417815