Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/VLC/modules/demux/mpeg/   (VLC Media Plyer ©)  Datei vom 23.9.2026 mit Größe 67 kB image not shown  

Quelle  es.c   Sprache: C

 

/*****************************************************************************
 * es.c : Generic audio ES input module for vlc
 *****************************************************************************
 * Copyright (C) 2001-2008 VLC authors and VideoLAN
 *               2022 VideoLabs
 *
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
 *          Gildas Bazin <gbazin@videolan.org>
 *
 * 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.
 *****************************************************************************/


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


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

#include <math.h>

#include <vlc_common.h>
#include <vlc_arrays.h>
#include <vlc_plugin.h>
#include <vlc_demux.h>
#include <vlc_codec.h>
#include <vlc_codecs.h>
#include <vlc_input.h>
#include <vlc_replay_gain.h>

#include "../../packetizer/a52.h"
#include "../../packetizer/dts_header.h"
#include "../../packetizer/mpegaudio.h"
#include "../../meta_engine/ID3Tag.h"
#include "../../meta_engine/ID3Text.h"
#include "../../meta_engine/ID3Meta.h"

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

static int  OpenAudio( vlc_object_t * );
static int  OpenVideo( vlc_object_t * );
static void Close    ( vlc_object_t * );

#define FPS_TEXT N_("Frames per Second")
#define FPS_LONGTEXT N_("This is the frame rate used as a fallback when " \
    "playing MPEG video elementary streams.")

vlc_module_begin ()
    set_subcategory( SUBCAT_INPUT_DEMUX )
    set_description( N_("MPEG-I/II/4 / A52 / DTS / MLP audio" ) )
    set_shortname( N_("Audio ES") )
    set_capability( "demux", 155 )
    set_callbacks( OpenAudio, Close )

    add_shortcut( "mpga", "mp3",
                  "m4a", "mp4a", "aac",
                  "ac3", "a52",
                  "eac3",
                  "dts",
                  "mlp", "thd" )

    add_submodule()
    set_description( N_("MPEG-4 video" ) )
    set_capability( "demux", 7 )
    set_callbacks( OpenVideo, Close )
    add_float( "es-fps", 25, FPS_TEXT, FPS_LONGTEXT )

    add_shortcut( "m4v" )
    add_shortcut( "mp4v" )
vlc_module_end ()

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/

static int Demux  ( demux_t * );
static int Control( demux_t *, int, va_list );

#define WAV_PROBE_SIZE (512*1024)
#define BASE_PROBE_SIZE (8000)
#define WAV_EXTRA_PROBE_SIZE (44000/2*2*2)

typedef struct
{
    vlc_fourcc_t i_codec;
    bool       b_use_word;
    const char *psz_name;
    int  (*pf_probe)( demux_t *p_demux, uint64_t *pi_offset );
    int  (*pf_init)( demux_t *p_demux );
} codec_t;

typedef struct
{
    vlc_tick_t i_time;
    uint64_t i_pos;
    bs_t br;
} sync_table_ctx_t;

typedef struct
{
    uint16_t i_frames_btw_refs;
    uint32_t i_bytes_btw_refs;
    uint32_t i_ms_btw_refs;
    uint8_t i_bits_per_bytes_dev;
    uint8_t i_bits_per_ms_dev;
    uint8_t *p_bits;
    size_t i_bits;
    sync_table_ctx_t current;
} sync_table_t;

typedef struct
{
    uint32_t i_offset;
    seekpoint_t *p_seekpoint;
} chap_entry_t;

typedef struct
{
    vlc_tick_t i_start;
    char *psz_text;
} sylt_entry_t;

typedef struct
{
    char *psz_language;
    size_t i_count;
    size_t i_current; /* index of next lyric to send as SPU */
    sylt_entry_t *p_entry;
    es_out_id_t *p_es;
} sylt_track_t;

/* Mpga specific */
#define XING_FIELD_STREAMFRAMES    (1 << 0)
#define XING_FIELD_STREAMBYTES     (1 << 1)
#define XING_FIELD_TOC             (1 << 2)
#define XING_FIELD_QUALITY         (1 << 3)
#define XING_MIN_TAG_SIZE           42
#define XING_TOC_COUNTBYTES         100
#define XING_MAX_TAG_SIZE          (XING_MIN_TAG_SIZE + 4 + 4 + XING_TOC_COUNTBYTES + 4 + 2)
#define XING_SUB_ETE_NOK_OTAE       XING_MIN_TAG_SIZE

struct xing_info_s
{
    uint32_t i_frames;
    uint32_t i_bytes;
    uint32_t i_quality;
    vlc_fourcc_t infotag;
    vlc_fourcc_t encoder;
    uint8_t  rgi_toc[XING_TOC_COUNTBYTES];
    float f_peak_signal;
    float f_radio_replay_gain;
    float f_audiophile_replay_gain;
    enum
    {
        XING_MODE_UNKNOWN   = 0,
        XING_MODE_CBR       = 1,
        XING_MODE_ABR       = 2,
        XING_MODE_VBR1      = 3,
        XING_MODE_VBR2      = 4,
        XING_MODE_VBR3      = 5,
        XING_MODE_VBR4      = 6,
        XING_MODE_CBR_2PASS = 8,
        XING_MODE_ABR_2PASS = 9,
    } brmode;
    enum
    {
        XING_AUDIOMODE_UNKNOWN    = 0,
        XING_AUDIOMODE_DPL        = 1,
        XING_AUDIOMODE_DPL2       = 2,
        XING_AUDIOMODE_AMBISONICS = 3,
    } audiomode;
    uint8_t  bitrate_avg;
    uint16_t i_delay_samples;
    uint16_t i_padding_samples;
    uint32_t i_music_length;
};

static int ParseXing( const uint8_t *p_buf, size_t i_buf, struct xing_info_s *xing )
{
    if( i_buf < XING_MIN_TAG_SIZE )
        return VLC_EGENERIC;

    vlc_fourcc_t infotag = VLC_FOURCC(p_buf[0], p_buf[1], p_buf[2], p_buf[3]);

    /* L3Enc VBR info */
    if( infotag == VLC_FOURCC('V','B','R','I') )
    {
        if( GetWBE( &p_buf[4] ) != 0x0001 )
            return VLC_EGENERIC;
        xing->i_bytes = GetDWBE( &p_buf[10] );
        xing->i_frames = GetDWBE( &p_buf[14] );
        xing->infotag = infotag;
        return VLC_SUCCESS;
    }
    /* Xing VBR/CBR tags */
    else if( infotag != VLC_FOURCC('X','i','n','g') &&
             infotag != VLC_FOURCC('I','n','f','o') )
    {
        return VLC_EGENERIC;
    }

    xing->infotag = infotag;

    const uint32_t i_flags = GetDWBE( &p_buf[4] );
    /* compute our variable struct size for early checks */
    const unsigned varsz[4] = { ((i_flags & 0x01) ? 4 : 0),
                                ((i_flags & 0x02) ? 4 : 0),
                                ((i_flags & 0x04) ? XING_TOC_COUNTBYTES : 0),
                                ((i_flags & 0x08) ? 4 : 0) };
    const unsigned i_varallsz = varsz[0] + varsz[1] + varsz[2] + varsz[3];
    const unsigned i_tag_total = XING_MIN_TAG_SIZE + i_varallsz;

    if( i_buf < i_tag_total )
        return VLC_EGENERIC;

    if( i_flags & XING_FIELD_STREAMFRAMES )
        xing->i_frames = GetDWBE( &p_buf[8] );
    if( i_flags & XING_FIELD_STREAMBYTES )
        xing->i_bytes = GetDWBE( &p_buf[8 + varsz[0]] );
    if( i_flags & XING_FIELD_TOC )
        memcpy( xing->rgi_toc, &p_buf[8 + varsz[0] + varsz[1]], XING_TOC_COUNTBYTES );
    if( i_flags & XING_FIELD_QUALITY )
        xing->i_quality = GetDWBE( &p_buf[8 + varsz[0] + varsz[1] + varsz[2]] );

    /* pointer past optional members */
    const uint8_t *p_fixed = &p_buf[8 + i_varallsz];

    /* Original Xing encoder header stops here */

    xing->encoder = VLC_FOURCC(p_fixed[0], p_fixed[1], p_fixed[2], p_fixed[3]); /* char version[9] start */

    if( xing->encoder != VLC_FOURCC('L','A','M','E') &&
        xing->encoder != VLC_FOURCC('L','a','v','c') &&
        xing->encoder != VLC_FOURCC('L','a','v','f') )
        return VLC_SUCCESS;

    xing->brmode  = p_fixed[9] & 0x0f; /* version upper / mode lower */
    uint32_t peak_signal  = GetDWBE( &p_fixed[11] );
    xing->f_peak_signal = peak_signal / 8388608.0; /* pow(2, 23) */
    uint16_t gain = GetWBE( &p_fixed[15] );
    xing->f_radio_replay_gain = (gain & 0x1FF) / /* 9bits val stored x10 */
                                ((gain & 0x200) ? -10.0 : 10.0); /* -sign bit on bit 6 */
    gain = GetWBE( &p_fixed[17] );
    xing->f_radio_replay_gain = (gain & 0x1FF) / /* 9bits val stored x10 */
                                ((gain & 0x200) ? -10.0 : 10.0); /* -sign bit on bit 6 */
    /* flags @19 */
    xing->bitrate_avg = (p_fixed[20] != 0xFF) ? p_fixed[20] : 0; /* clipped to 255, so it's unknown from there */
    xing->i_delay_samples = (p_fixed[21] << 4) | (p_fixed[22] >> 4); /* upper 12bits */
    xing->i_padding_samples = ((p_fixed[22] & 0x0F) << 8) | p_fixed[23]; /* lower 12bits */
    xing->audiomode = (p_fixed[26] >> 3) & 0x07; /* over 16bits, 2b unused / 3b mode / 11b preset */
    xing->i_music_length  = GetDWBE( &p_fixed[28] );

    return VLC_SUCCESS;
}

static int MpgaGetCBRSeekpoint( const struct mpga_frameheader_s *mpgah,
                                const struct xing_info_s *xing,
                                uint64_t i_seekablesize,
                                double f_pos, vlc_tick_t *pi_time, uint64_t *pi_offset )
{
    if( xing->infotag != 0 &&
        xing->infotag != VLC_FOURCC('I','n','f','o') &&
        xing->brmode != XING_MODE_CBR &&
        xing->brmode != XING_MODE_CBR_2PASS )
        return -1;

    if( !mpgah->i_sample_rate || !mpgah->i_samples_per_frame || !mpgah->i_frame_size )
        return -1;

    uint32_t i_total_frames = xing->i_frames ? xing->i_frames
                                             : i_seekablesize / mpgah->i_frame_size;

    /* xing must be optional here */
    uint32_t i_frame = i_total_frames * f_pos;
    *pi_offset = i_frame * mpgah->i_frame_size;
    *pi_time = vlc_tick_from_samples( i_frame * mpgah->i_samples_per_frame,
                                      mpgah->i_sample_rate ) + VLC_TICK_0;

    return 0;
}

static int SeekByPosUsingXingTOC( const struct mpga_frameheader_s *mpgah,
                                  const struct xing_info_s *xing,double pos,
                                  vlc_tick_t *pi_time, uint64_t *pi_offset )
{
    if( !xing->rgi_toc[XING_TOC_COUNTBYTES - 1] ||
        !mpgah->i_sample_rate || !mpgah->i_samples_per_frame )
        return -1;

    unsigned syncentry = pos * XING_TOC_COUNTBYTES;
    syncentry = VLC_CLIP(syncentry, 0, XING_TOC_COUNTBYTES - 1);
    unsigned syncframe = syncentry * xing->i_frames / XING_TOC_COUNTBYTES;

    *pi_time = vlc_tick_from_samples( syncframe * mpgah->i_samples_per_frame,
                                      mpgah->i_sample_rate ) + VLC_TICK_0;
    *pi_offset = xing->rgi_toc[syncentry] * xing->i_bytes / 256;

    return 0;
}

static int SeekByTimeUsingXingTOC( const struct mpga_frameheader_s *mpgah,
                                   const struct xing_info_s *xing,
                                   vlc_tick_t *pi_time, uint64_t *pi_offset )
{
    if( !mpgah->i_sample_rate || !mpgah->i_samples_per_frame || !xing->i_frames ||
        *pi_time < VLC_TICK_0 )
        return -1;

    uint32_t sample = samples_from_vlc_tick( *pi_time - VLC_TICK_0, mpgah->i_sample_rate );
    uint64_t totalsamples = mpgah->i_samples_per_frame * (uint64_t) xing->i_frames;

    return SeekByPosUsingXingTOC( mpgah, xing, (double)sample / totalsamples,
                                  pi_time, pi_offset );
}

static int MpgaSeek( const struct mpga_frameheader_s *mpgah,
                     const struct xing_info_s *xing,
                     uint64_t i_seekablesize, double f_pos,
                     vlc_tick_t *pi_time, uint64_t *pi_offset )
{
    if( f_pos >= 0 ) /* Set Pos */
    {
        if( !MpgaGetCBRSeekpoint( mpgah, xing, i_seekablesize,
                                  f_pos, pi_time, pi_offset ) )
        {
             return 0;
        }
    }

    if( *pi_time != VLC_TICK_INVALID &&
        !SeekByTimeUsingXingTOC( mpgah, xing, pi_time, pi_offset ) )
        return 0;

    return SeekByPosUsingXingTOC( mpgah, xing, f_pos, pi_time, pi_offset );
}

typedef struct
{
    codec_t codec;
    vlc_fourcc_t i_original;

    es_out_id_t *p_es;

    bool  b_start;
    decoder_t   *p_packetizer;
    block_t     *p_packetized_data;

    vlc_tick_t  i_pts;
    vlc_tick_t  i_time_offset;
    uint64_t    i_bytes;

    bool        b_big_endian;
    bool        b_estimate_bitrate;
    unsigned    i_bitrate;  /* extracted from Xing header */
    vlc_tick_t  i_duration;

    bool b_initial_sync_failed;

    unsigned i_packet_size;

    uint64_t i_stream_offset;
    unsigned i_demux_flags;

    float   f_fps;

    /* Mpga specific */
    struct mpga_frameheader_s mpgah;
    struct xing_info_s xing;

    audio_replay_gain_t audio_replay_gain;

    sync_table_t mllt;
    struct
    {
        size_t i_count;
        size_t i_current;
        chap_entry_t *p_entry;
    } chapters;

    /* Synchronized lyrics from ID3v2 SYLT, one track per language */
    struct
    {
        size_t i_count;
        sylt_track_t *p_track;
        bool b_meta_sent; /* true once sylt-data extra meta has been emitted */
    } sylt;

    vlc_meta_t *p_meta;
} demux_sys_t;

static int MpgaProbe( demux_t *p_demux, uint64_t *pi_offset );
static int MpgaInit( demux_t *p_demux );

static int AacProbe( demux_t *p_demux, uint64_t *pi_offset );
static int AacInit( demux_t *p_demux );

static int EA52Probe( demux_t *p_demux, uint64_t *pi_offset );
static int A52Probe( demux_t *p_demux, uint64_t *pi_offset );
static int A52Init( demux_t *p_demux );

static int DtsProbe( demux_t *p_demux, uint64_t *pi_offset );
static int DtsInit( demux_t *p_demux );

static int MlpProbe( demux_t *p_demux, uint64_t *pi_offset );
static int ThdProbe( demux_t *p_demux, uint64_t *pi_offset );
static int MlpInit( demux_t *p_demux );

static bool Parse( demux_t *p_demux, block_t **pp_output );
static int SeekByMlltTable( sync_table_t *, vlc_tick_t *, uint64_t * );

static const codec_t p_codecs[] = {
    { VLC_CODEC_MP4A, false, "mp4 audio",  AacProbe,  AacInit },
    { VLC_CODEC_MPGA, false, "mpeg audio", MpgaProbe, MpgaInit },
    { VLC_CODEC_A52, true,  "a52 audio",  A52Probe,  A52Init },
    { VLC_CODEC_EAC3, true,  "eac3 audio", EA52Probe, A52Init },
    { VLC_CODEC_DTS, false, "dts audio",  DtsProbe,  DtsInit },
    { VLC_CODEC_MLP, false, "mlp audio",  MlpProbe,  MlpInit },
    { VLC_CODEC_TRUEHD, false, "TrueHD audio",  ThdProbe,  MlpInit },

    { 0, false, NULL, NULL, NULL }
};

static int VideoInit( demux_t *p_demux );

static const codec_t codec_m4v = {
    VLC_CODEC_MP4V, false, "mp4 video", NULL,  VideoInit
};

/*****************************************************************************
 * OpenCommon: initializes demux structures
 *****************************************************************************/

static int OpenCommon( demux_t *p_demux,
                       int i_cat, const codec_t *p_codec, uint64_t i_bs_offset )
{
    demux_sys_t *p_sys;

    es_format_t fmt;

    DEMUX_INIT_COMMON(); p_sys = p_demux->p_sys;
    memset( p_sys, 0, sizeof( demux_sys_t ) );
    p_sys->codec = *p_codec;
    p_sys->p_es = NULL;
    p_sys->b_start = true;
    p_sys->i_stream_offset = i_bs_offset;
    p_sys->b_estimate_bitrate = true;
    p_sys->i_bitrate = 0;
    p_sys->i_duration = 0;
    p_sys->b_big_endian = false;
    p_sys->f_fps = var_InheritFloat( p_demux, "es-fps" );
    p_sys->p_packetized_data = NULL;
    p_sys->chapters.i_current = 0;
    p_sys->xing.f_peak_signal = NAN;
    p_sys->xing.f_radio_replay_gain = NAN;
    p_sys->xing.f_audiophile_replay_gain = NAN;
    TAB_INIT(p_sys->chapters.i_count, p_sys->chapters.p_entry);
    TAB_INIT(p_sys->sylt.i_count, p_sys->sylt.p_track);

    if( vlc_stream_Seek( p_demux->s, p_sys->i_stream_offset ) )
    {
        free( p_sys );
        return VLC_EGENERIC;
    }

    if( p_sys->codec.pf_init( p_demux ) )
    {
        free( p_sys );
        return VLC_EGENERIC;
    }

    if( vlc_stream_Seek( p_demux->s, p_sys->i_stream_offset ) )
    {
        free( p_sys );
        return VLC_EGENERIC;
    }

    msg_Dbg( p_demux, "detected format %4.4s", (const char*)&p_sys->codec.i_codec );

    /* Load the audio packetizer */
    es_format_Init( &fmt, i_cat, p_sys->codec.i_codec );
    fmt.i_original_fourcc = p_sys->i_original;
    p_sys->p_packetizer = demux_PacketizerNew( VLC_OBJECT(p_demux), &fmt, p_sys->codec.psz_name );
    if( !p_sys->p_packetizer )
    {
        free( p_sys );
        return VLC_EGENERIC;
    }

    es_format_t *p_fmt = &p_sys->p_packetizer->fmt_out;
    replay_gain_Merge( &p_fmt->audio_replay_gain, &p_sys->audio_replay_gain );

    /* Register one SPU ES per SYLT language track */
    for( size_t i = 0; i < p_sys->sylt.i_count; i++ )
    {
        sylt_track_t *p_tk = &p_sys->sylt.p_track[i];
        es_format_t spu_fmt;
        es_format_Init( &spu_fmt, SPU_ES, VLC_CODEC_SUBT );
        spu_fmt.psz_description = strdup( _("Lyrics") );
        spu_fmt.psz_language = p_tk->psz_language
                             ? strdup( p_tk->psz_language ) : NULL;
        p_tk->p_es = es_out_Add( p_demux->out, &spu_fmt );
        es_format_Clean( &spu_fmt );
        msg_Dbg( p_demux, "registered %zu synchronized lyrics entries (lang: %s)",
                 p_tk->i_count,
                 p_tk->psz_language ? p_tk->psz_language : "und" );
    }

    for( ;; )
    {
        if( Parse( p_demux, &p_sys->p_packetized_data ) )
            break;
        if( p_sys->p_packetized_data )
            break;
    }

    return VLC_SUCCESS;
}
static int OpenAudio( vlc_object_t *p_this )
{
    demux_t *p_demux = (demux_t*)p_this;
    for( int i = 0; p_codecs[i].i_codec != 0; i++ )
    {
        uint64_t i_offset;
        if( !p_codecs[i].pf_probe( p_demux, &i_offset ) )
            return OpenCommon( p_demux, AUDIO_ES, &p_codecs[i], i_offset );
    }
    return VLC_EGENERIC;
}
static int OpenVideo( vlc_object_t *p_this )
{
    demux_t *p_demux = (demux_t*)p_this;

    /* Only m4v is supported for the moment */
    bool b_m4v_ext    = demux_IsPathExtension( p_demux, ".m4v" );
    bool b_re4_ext    = !b_m4v_ext && demux_IsPathExtension( p_demux, ".re4" );
    bool b_m4v_forced = demux_IsForced( p_demux, "m4v" ) ||
                        demux_IsForced( p_demux, "mp4v" );

    if( !b_m4v_ext && !b_m4v_forced && !b_re4_ext )
        return VLC_EGENERIC;

    ssize_t i_off = b_re4_ext ? 220 : 0;
    const uint8_t *p_peek;
    if( vlc_stream_Peek( p_demux->s, &p_peek, i_off + 4 ) < i_off + 4 )
        return VLC_EGENERIC;
    if( p_peek[i_off + 0] != 0x00 || p_peek[i_off + 1] != 0x00 || p_peek[i_off + 2] != 0x01 )
    {
        if( !b_m4v_forced)
            return VLC_EGENERIC;
        msg_Warn( p_demux,
                  "this doesn't look like an MPEG ES stream, continuing anyway" );
    }
    return OpenCommon( p_demux, VIDEO_ES, &codec_m4v, i_off );
}

static void IncreaseChapter( demux_t *p_demux, vlc_tick_t i_time )
{
    demux_sys_t *p_sys = p_demux->p_sys;
    while( p_sys->chapters.i_current + 1 < p_sys->chapters.i_count )
    {
        const chap_entry_t *p = &p_sys->chapters.p_entry[p_sys->chapters.i_current + 1];
        if( (p->i_offset != UINT32_MAX && vlc_stream_Tell( p_demux->s ) < p->i_offset) ||
            (i_time == VLC_TICK_INVALID || p->p_seekpoint->i_time_offset + VLC_TICK_0 > i_time) )
            break;
        ++p_sys->chapters.i_current;
        p_sys->i_demux_flags |= INPUT_UPDATE_SEEKPOINT;
    }
}

/* Export all SYLT tracks as input item extra meta for the Qt display layer.
 * Called once on the first Demux invocation (deferred from Open) so that
 * the in-band metadata pass (InputSourceMeta / TagLib) has already run and
 * set title/artist/album before we call es_out_SetMeta, which sets the
 * ITEM_PREPARSED flag and would otherwise prevent those passes from running.
 *
 * Encoding: entries are packed as "ms\x1Ftext\x1E" (ASCII Unit Separator 0x1F
 * between timestamp and text; ASCII Record Separator 0x1E after each entry).
 * "sylt-data" carries the first (default) track for backward compatibility;
 * "sylt-data:xxx" carries each language for future language-aware UI. */

static void SyltEmitMeta( demux_t *p_demux )
{
    demux_sys_t *p_sys = p_demux->p_sys;

    for( size_t tk = 0; tk < p_sys->sylt.i_count; tk++ )
    {
        const sylt_track_t *p_tk = &p_sys->sylt.p_track[tk];

        /* 20 bytes for PRId64 decimal digits + 2 ASCII separator bytes per entry */
        size_t i_total = 1; /* null terminator */
        for( size_t i = 0; i < p_tk->i_count; i++ )
            i_total += 22 + strlen( p_tk->p_entry[i].psz_text );

        char *psz_data = malloc( i_total );
        if( !psz_data )
            continue;

        char *p = psz_data;
        *p = '\0';
        for( size_t i = 0; i < p_tk->i_count; i++ )
            p += sprintf( p, "%" PRId64 "\x1F%s\x1E",
                          MS_FROM_VLC_TICK( p_tk->p_entry[i].i_start ),
                          p_tk->p_entry[i].psz_text );

        vlc_meta_t *p_meta = vlc_meta_New();
        if( p_meta )
        {
            char psz_key[32];
            snprintf( psz_key, sizeof(psz_key), "sylt-data:%s",
                      p_tk->psz_language ? p_tk->psz_language : "und" );
            vlc_meta_SetExtra( p_meta, psz_key, psz_data );
            if( tk == 0 ) /* default key for single-language backward compat */
                vlc_meta_SetExtra( p_meta, "sylt-data", psz_data );
            es_out_SetMeta( p_demux->out, p_meta );
            vlc_meta_Delete( p_meta );
        }
        free( psz_data );
    }
    p_sys->sylt.b_meta_sent = true;
}

/*****************************************************************************
 * Demux: reads and demuxes data packets
 *****************************************************************************
 * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
 *****************************************************************************/

static int Demux( demux_t *p_demux )
{
    int ret = 1;
    demux_sys_t *p_sys = p_demux->p_sys;

    /* Emit sylt-data meta on the first Demux call, after InputSourceMeta has
     * already run and set title/artist/album. Doing this in Open would set the
     * ITEM_PREPARSED flag prematurely and prevent those metadata passes. */

    if( unlikely( !p_sys->sylt.b_meta_sent ) && p_sys->sylt.i_count > 0 )
        SyltEmitMeta( p_demux );

    block_t *p_block_out = p_sys->p_packetized_data;
    if( p_block_out )
        p_sys->p_packetized_data = NULL;
    else
        ret = Parse( p_demux, &p_block_out ) ? 0 : 1;

    /* Update chapter if any */
    IncreaseChapter( p_demux,
                     p_block_out ? p_sys->i_time_offset + p_block_out->i_dts
                                 : VLC_TICK_INVALID );

    while( p_block_out )
    {
        block_t *p_next = p_block_out->p_next;

        /* Correct timestamp */
        if( p_sys->p_packetizer->fmt_out.i_cat == VIDEO_ES )
        {
            if( p_block_out->i_pts == VLC_TICK_INVALID &&
                p_block_out->i_dts == VLC_TICK_INVALID )
                p_block_out->i_dts = VLC_TICK_0 + p_sys->i_pts + VLC_TICK_FROM_SEC(1) / p_sys->f_fps;
            if( p_block_out->i_dts != VLC_TICK_INVALID )
                p_sys->i_pts = p_block_out->i_dts - VLC_TICK_0;
        }
        else
        {
            p_sys->i_pts = p_block_out->i_pts - VLC_TICK_0;
        }

        if( p_block_out->i_pts != VLC_TICK_INVALID )
        {
            p_block_out->i_pts += p_sys->i_time_offset;
        }
        if( p_block_out->i_dts != VLC_TICK_INVALID )
        {
            p_block_out->i_dts += p_sys->i_time_offset;
            es_out_SetPCR( p_demux->out, p_block_out->i_dts );
        }

        /* Send synchronized lyrics as subtitle blocks */
        if( p_sys->sylt.i_count > 0 && p_block_out->i_dts != VLC_TICK_INVALID )
        {
            vlc_tick_t i_audio_time = p_block_out->i_dts; /* already offset-adjusted above */
            for( size_t tk = 0; tk < p_sys->sylt.i_count; tk++ )
            {
                sylt_track_t *p_tk = &p_sys->sylt.p_track[tk];
                if( !p_tk->p_es )
                    continue;
                while( p_tk->i_current < p_tk->i_count )
                {
                    const sylt_entry_t *e = &p_tk->p_entry[p_tk->i_current];
                    if( VLC_TICK_0 + e->i_start > i_audio_time )
                        break;

                    size_t i_len = strlen( e->psz_text );
                    block_t *p_spu = block_Alloc( i_len + 1 );
                    if( p_spu )
                    {
                        memcpy( p_spu->p_buffer, e->psz_text, i_len + 1 );
                        p_spu->i_dts =
                        p_spu->i_pts = VLC_TICK_0 + e->i_start;
                        if( p_tk->i_current + 1 < p_tk->i_count )
                            p_spu->i_length = p_tk->p_entry[p_tk->i_current + 1].i_start
                                            - e->i_start;
                        else
                            p_spu->i_length = VLC_TICK_FROM_SEC( 5 );
                        es_out_Send( p_demux->out, p_tk->p_es, p_spu );
                    }
                    p_tk->i_current++;
                }
            }
        }
        /* Re-estimate bitrate */
        if( p_sys->b_estimate_bitrate && p_sys->i_pts > VLC_TICK_FROM_MS(500) )
            p_sys->i_bitrate = 8 * CLOCK_FREQ * p_sys->i_bytes
                                   / (p_sys->i_pts - 1);
        p_sys->i_bytes += p_block_out->i_buffer;


        p_block_out->p_next = NULL;
        if( likely(p_sys->p_es) )
            es_out_Send( p_demux->out, p_sys->p_es, p_block_out );
        else
            block_Release( p_block_out );

        p_block_out = p_next;
    }
    return ret;
}

/*****************************************************************************
 * Close: frees unused data
 *****************************************************************************/

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;

    if( p_sys->p_packetized_data )
        block_ChainRelease( p_sys->p_packetized_data );
    for( size_t i=0; i< p_sys->chapters.i_count; i++ )
        vlc_seekpoint_Delete( p_sys->chapters.p_entry[i].p_seekpoint );
    TAB_CLEAN( p_sys->chapters.i_count, p_sys->chapters.p_entry );
    for( size_t i = 0; i < p_sys->sylt.i_count; i++ )
    {
        sylt_track_t *p_tk = &p_sys->sylt.p_track[i];
        for( size_t j = 0; j < p_tk->i_count; j++ )
            free( p_tk->p_entry[j].psz_text );
        TAB_CLEAN( p_tk->i_count, p_tk->p_entry );
        free( p_tk->psz_language );
    }
    TAB_CLEAN( p_sys->sylt.i_count, p_sys->sylt.p_track );
    if( p_sys->mllt.p_bits )
        free( p_sys->mllt.p_bits );
    if( p_sys->p_meta )
        vlc_meta_Delete( p_sys->p_meta );
    demux_PacketizerDestroy( p_sys->p_packetizer );
    free( p_sys );
}

/*****************************************************************************
 * Time seek:
 *****************************************************************************/

static void PostSeekCleanup( demux_sys_t *p_sys, vlc_tick_t i_time )
{
    /* Fix time_offset */
    if( i_time >= 0 )
        p_sys->i_time_offset = i_time - p_sys->i_pts;
    /* And reset buffered data */
    if( p_sys->p_packetized_data )
        block_ChainRelease( p_sys->p_packetized_data );
    p_sys->p_packetized_data = NULL;
    /* Reset chapter if any */
    p_sys->chapters.i_current = 0;
    p_sys->i_demux_flags |= INPUT_UPDATE_SEEKPOINT;
    /* Reset synchronized lyrics position */
    for( size_t tk = 0; tk < p_sys->sylt.i_count; tk++ )
    {
        sylt_track_t *p_tk = &p_sys->sylt.p_track[tk];
        p_tk->i_current = 0;
        if( i_time >= 0 )
        {
            for( size_t i = 0; i < p_tk->i_count; i++ )
            {
                if( VLC_TICK_0 + p_tk->p_entry[i].i_start > i_time )
                    break;
                p_tk->i_current = i;
            }
        }
    }
}

static int MovetoTimePos( demux_t *p_demux, vlc_tick_t i_time, uint64_t i_pos )
{
    demux_sys_t *p_sys  = p_demux->p_sys;
    int i_ret = vlc_stream_Seek( p_demux->s, p_sys->i_stream_offset + i_pos );
    if( i_ret != VLC_SUCCESS )
        return i_ret;
    PostSeekCleanup( p_sys, i_time );
    return VLC_SUCCESS;
}

/*****************************************************************************
 * Control:
 *****************************************************************************/

static int Control( demux_t *p_demux, int i_query, va_list args )
{
    demux_sys_t *p_sys  = p_demux->p_sys;
    bool *pb_bool;

    switch( i_query )
    {
        case DEMUX_HAS_UNSUPPORTED_META:
            pb_bool = va_arg( args, bool * );
            *pb_bool = true;
            return VLC_SUCCESS;

        case DEMUX_GET_META:
            if( p_sys->p_meta == NULL )
                return VLC_EGENERIC;
            vlc_meta_Merge( va_arg( args, vlc_meta_t * ), p_sys->p_meta );
            return VLC_SUCCESS;

        case DEMUX_GET_TIME:
            *va_arg( args, vlc_tick_t * ) = p_sys->i_pts + p_sys->i_time_offset;
            return VLC_SUCCESS;

        case DEMUX_GET_LENGTH:
        {
            if( p_sys->i_duration > 0 )
            {
                *va_arg( args, vlc_tick_t * ) = p_sys->i_duration;
                return VLC_SUCCESS;
            }
            /* compute length from bitrate */
            va_list ap;
            int i_ret;

            va_copy ( ap, args );
            i_ret = demux_vaControlHelper( p_demux->s, p_sys->i_stream_offset,
                                    -1, p_sys->i_bitrate, 1, i_query, ap );
            va_end( ap );

            /* No bitrate, we can't have it precisely, but we can compute
             * a raw approximation with time/position */

            if( i_ret && !p_sys->i_bitrate )
            {
                uint64_t i_stream_size;
                if( vlc_stream_GetSize( p_demux->s, &i_stream_size ) != VLC_SUCCESS )
                    return VLC_EGENERIC;

                uint64_t i_pos = vlc_stream_Tell( p_demux->s );
                float f_pos = (double)i_pos / (double)i_stream_size;
                /* The first few seconds are guaranteed to be very whacky,
                 * don't bother trying ... Too bad */

                if( f_pos < 0.01f ||
                    (p_sys->i_pts + p_sys->i_time_offset) < VLC_TICK_FROM_SEC(8) )
                {
                    return VLC_EGENERIC;
                }

                *va_arg( args, vlc_tick_t * ) =
                    (p_sys->i_pts + p_sys->i_time_offset) / f_pos;
                return VLC_SUCCESS;
            }
            return i_ret;
        }

        case DEMUX_SET_TIME:
        case DEMUX_SET_POSITION:
        {
            vlc_tick_t i_time;
            double f_pos;
            uint64_t i_offset;

            va_list ap;
            va_copy ( ap, args ); /* don't break args for helper fallback */
            if( i_query == DEMUX_SET_TIME )
            {
                i_time = va_arg(ap, vlc_tick_t);
                f_pos = p_sys->i_duration ? i_time / (double) p_sys->i_duration : -1.0;
                if( f_pos > 1.0 )
                    f_pos = 1.0;
            }
            else
            {
                f_pos = va_arg(ap, double);
                i_time = p_sys->i_duration ? p_sys->i_duration * f_pos : VLC_TICK_INVALID;
            }
            va_end( ap );

            /* Try to use ID3 table */
            if( !SeekByMlltTable( &p_sys->mllt, &i_time, &i_offset ) )
                return MovetoTimePos( p_demux, i_time, i_offset );

            if( p_sys->codec.i_codec == VLC_CODEC_MPGA )
            {
                uint64_t streamsize;
                if( !vlc_stream_GetSize( p_demux->s, &streamsize ) &&
                    streamsize > p_sys->i_stream_offset )
                    streamsize -= p_sys->i_stream_offset;
                else
                    streamsize = 0;

                if( !MpgaSeek( &p_sys->mpgah, &p_sys->xing,
                           streamsize, f_pos, &i_time, &i_offset ) )
                {
                    return MovetoTimePos( p_demux, i_time, i_offset );
                }
            }

            /* fallback on bitrate / file position seeking */
            i_time = VLC_TICK_INVALID;
            int ret = demux_vaControlHelper( p_demux->s, p_sys->i_stream_offset, -1,
                                             p_sys->i_bitrate, 1, i_query, args );
            if( ret != VLC_SUCCESS )
                return ret; /* not much we can do */

            if( p_sys->i_bitrate > 0 )
            {
                i_offset = vlc_stream_Tell( p_demux->s ); /* new pos */
                i_time = VLC_TICK_0;
                if( likely(i_offset > p_sys->i_stream_offset) )
                {
                    i_offset -= p_sys->i_stream_offset;
                    i_time += vlc_tick_from_samples( i_offset * 8, p_sys->i_bitrate );
                }
            }
            PostSeekCleanup( p_sys, i_time );

            /* FIXME TODO: implement a high precision seek (with mp3 parsing)
             * needed for multi-input */

            return VLC_SUCCESS;
        }

        case DEMUX_GET_TITLE_INFO:
        {
            if( p_sys->chapters.i_count == 0 )
                return VLC_EGENERIC;
            input_title_t **pp_title = malloc( sizeof(*pp_title) );
            if( !pp_title )
                return VLC_EGENERIC;
            *pp_title = vlc_input_title_New();
            if( !*pp_title )
            {
                free( pp_title );
                return VLC_EGENERIC;
            }
            (*pp_title)->seekpoint = vlc_alloc( p_sys->chapters.i_count, sizeof(seekpoint_t *) );
            if( !(*pp_title)->seekpoint )
            {
                free( *pp_title );
                free( pp_title );
                return VLC_EGENERIC;
            }
            for( size_t i=0; i<p_sys->chapters.i_count; i++ )
            {
                seekpoint_t *s = vlc_seekpoint_Duplicate( p_sys->chapters.p_entry[i].p_seekpoint );
                if( s )
                    (*pp_title)->seekpoint[(*pp_title)->i_seekpoint++] = s;
            }
            *(va_arg( args, input_title_t *** )) = pp_title;
            *(va_arg( args, int* )) = 1;
            *(va_arg( args, int* )) = 0;
            *(va_arg( args, int* )) = 0;
            return VLC_SUCCESS;
        }
        break;

        case DEMUX_GET_TITLE:
            if( p_sys->chapters.i_count == 0 )
                return VLC_EGENERIC;
            *(va_arg( args, int* )) = 0;
            return VLC_SUCCESS;

        case DEMUX_GET_SEEKPOINT:
            if( p_sys->chapters.i_count == 0 )
                return VLC_EGENERIC;
            *(va_arg( args, int* )) = p_sys->chapters.i_current;
            return VLC_SUCCESS;

        case DEMUX_SET_TITLE:
            return va_arg( args, int) == 0 ? VLC_SUCCESS : VLC_EGENERIC;

        case DEMUX_SET_SEEKPOINT:
        {
            int i = va_arg( args, int );
            if( (size_t)i>=p_sys->chapters.i_count )
                return VLC_EGENERIC;
            const chap_entry_t *p = &p_sys->chapters.p_entry[i];
            if( p_sys->chapters.p_entry[i].i_offset == UINT32_MAX )
                return demux_Control( p_demux, DEMUX_SET_TIME, p->p_seekpoint->i_time_offset );
            int i_ret= MovetoTimePos( p_demux, p->p_seekpoint->i_time_offset, p->i_offset );
            if( i_ret == VLC_SUCCESS )
                p_sys->chapters.i_current = i;
            return i_ret;
        }

        case DEMUX_TEST_AND_CLEAR_FLAGS:
        {
            unsigned *restrict flags = va_arg( args, unsigned * );
            *flags &= p_sys->i_demux_flags;
            p_sys->i_demux_flags &= ~*flags;
            return VLC_SUCCESS;
        }
    }

    return demux_vaControlHelper( p_demux->s, p_sys->i_stream_offset, -1,
                                  p_sys->i_bitrate, 1, i_query, args );
}

/*****************************************************************************
 * Makes a link list of buffer of parsed data
 * Returns true if EOF
 *****************************************************************************/

static bool Parse( demux_t *p_demux, block_t **pp_output )
{
    demux_sys_t *p_sys = p_demux->p_sys;
    block_t *p_block_in, *p_block_out;

    *pp_output = NULL;

    if( p_sys->codec.b_use_word )
    {
        /* Make sure we are word aligned */
        int64_t i_pos = vlc_stream_Tell( p_demux->s );
        if( (i_pos & 1) && vlc_stream_Read( p_demux->s, NULL, 1 ) != 1 )
            return true;
    }

    p_block_in = vlc_stream_Block( p_demux->s, p_sys->i_packet_size );
    bool b_eof = p_block_in == NULL;

    if( p_block_in )
    {
        if( p_sys->codec.b_use_word && !p_sys->b_big_endian && p_block_in->i_buffer > 0 )
        {
            /* Convert to big endian */
            block_t *old = p_block_in;
            p_block_in = block_Alloc( p_block_in->i_buffer );
            if( p_block_in )
            {
                block_CopyProperties( p_block_in, old );
                swab( old->p_buffer, p_block_in->p_buffer, old->i_buffer );
            }
            block_Release( old );
        }

        if( p_block_in )
        {
            p_block_in->i_pts =
            p_block_in->i_dts = (p_sys->b_start || p_sys->b_initial_sync_failed) ?
                                 VLC_TICK_0 : VLC_TICK_INVALID;
        }
    }
    p_sys->b_initial_sync_failed = p_sys->b_start; /* Only try to resync once */

    decoder_t *p_packetizer = p_sys->p_packetizer;
    while( ( p_block_out = p_packetizer->pf_packetize( p_packetizer, p_block_in ? &p_block_in : NULL ) ) )
    {
        p_sys->b_initial_sync_failed = false;
        while( p_block_out )
        {
            if( !p_sys->p_es )
            {
                es_format_t fmt;
                es_format_Init( &fmt, p_packetizer->fmt_out.i_cat, p_packetizer->fmt_out.i_codec );
                es_format_Copy( &fmt, &p_packetizer->fmt_out );

                switch( p_sys->xing.audiomode )
                {
                    case XING_AUDIOMODE_AMBISONICS:
                        fmt.audio.channel_type = AUDIO_CHANNEL_TYPE_AMBISONICS;
                        break;
                    default:
                        break;
                }

                fmt.b_packetized = true;
                fmt.i_id = 0;
                p_sys->p_es = es_out_Add( p_demux->out, &fmt );

                /* Use the bitrate as initual value */
                if( p_sys->b_estimate_bitrate )
                    p_sys->i_bitrate = fmt.i_bitrate;

                es_format_Clean( &fmt );
            }

            block_t *p_next = p_block_out->p_next;
            p_block_out->p_next = NULL;

            block_ChainLastAppend( &pp_output, p_block_out );

            p_block_out = p_next;
        }
    }

    if( p_sys->b_initial_sync_failed )
        msg_Dbg( p_demux, "did not sync on first block" );
    p_sys->b_start = false;

    return b_eof;
}

/* Check to apply to WAVE fmt header */
static int GenericFormatCheck( int i_format, const uint8_t *p_head )
{
    if ( i_format == WAVE_FORMAT_PCM )
    {
        if( GetWLE( p_head /* nChannels */ ) != 2 )
            return VLC_EGENERIC;
        if( GetDWLE( p_head + 2 /* nSamplesPerSec */ ) != 44100 )
            return VLC_EGENERIC;
    }
    return VLC_SUCCESS;
}

/*****************************************************************************
 * Wav header skipper
 *****************************************************************************/

static int WavSkipHeader( demux_t *p_demux, uint64_t *pi_skip,
                          const uint16_t rgi_twocc[],
                          int (*pf_format_check)( int, const uint8_t * ) )
{
    const uint8_t *p_peek;
    size_t i_peek = 0;
    uint32_t i_len;

    /* */
    *pi_skip = 0;

    /* Check if we are dealing with a WAV file */
    if( vlc_stream_Peek( p_demux->s, &p_peek, 12+8 ) != 12 + 8 )
        return VLC_SUCCESS;

    if( memcmp( p_peek, "RIFF", 4 ) || memcmp( &p_peek[8], "WAVE", 4 ) )
        return VLC_SUCCESS;

    /* Find the wave format header */
    i_peek = 12 + 8;
    while( memcmp( p_peek + i_peek - 8, "fmt ", 4 ) )
    {
        i_len = GetDWLE( p_peek + i_peek - 4 );
        if( i_len > WAV_PROBE_SIZE || i_peek + i_len > WAV_PROBE_SIZE )
            return VLC_EGENERIC;

        i_peek += i_len + 8;
        if( vlc_stream_Peek( p_demux->s, &p_peek, i_peek ) != (ssize_t) i_peek )
            return VLC_EGENERIC;
    }

    /* Sanity check the wave format header */
    i_len = GetDWLE( p_peek + i_peek - 4 );
    if( i_len > WAV_PROBE_SIZE )
        return VLC_EGENERIC;

    i_peek += i_len + 8;
    if( vlc_stream_Peek( p_demux->s, &p_peek, i_peek ) != (ssize_t) i_peek )
        return VLC_EGENERIC;
    const uint16_t i_twocc = GetWLE( p_peek + i_peek - i_len - 8 /* wFormatTag */ );
    int i_format_idx;
    for( i_format_idx = 0; rgi_twocc[i_format_idx] != WAVE_FORMAT_UNKNOWN; i_format_idx++ )
    {
        if( i_twocc == rgi_twocc[i_format_idx] )
            break;
    }
    if( rgi_twocc[i_format_idx] == WAVE_FORMAT_UNKNOWN )
        return VLC_EGENERIC;

    if( pf_format_check &&
        pf_format_check( i_twocc, p_peek + i_peek - i_len - 6 ) != VLC_SUCCESS )
            return VLC_EGENERIC;

    /* Skip the wave header */
    while( memcmp( p_peek + i_peek - 8, "data", 4 ) )
    {
        i_len = GetDWLE( p_peek + i_peek - 4 );
        if( i_len > WAV_PROBE_SIZE || i_peek + i_len > WAV_PROBE_SIZE )
            return VLC_EGENERIC;

        i_peek += i_len + 8;
        if( vlc_stream_Peek( p_demux->s, &p_peek, i_peek ) != (ssize_t) i_peek )
            return VLC_EGENERIC;
    }
    *pi_skip = i_peek;
    return VLC_SUCCESS;
}

static int GenericProbe( demux_t *p_demux, uint64_t *pi_offset,
                         const char * ppsz_name[],
                         int (*pf_check)( const uint8_t *, unsigned * ),
                         unsigned i_check_size,
                         unsigned i_base_probing,
                         unsigned i_wav_extra_probing,
                         bool b_use_word,
                         const uint16_t pi_twocc[],
                         int (*pf_format_check)( int, const uint8_t * ) )
{
    bool   b_forced_demux;

    uint64_t i_offset;
    const uint8_t *p_peek;
    uint64_t i_skip;

    b_forced_demux = false;
    for( size_t i = 0; ppsz_name[i] != NULL; i++ )
    {
        b_forced_demux |= demux_IsForced( p_demux, ppsz_name[i] );
    }

    i_offset = vlc_stream_Tell( p_demux->s );

    if( WavSkipHeader( p_demux, &i_skip, pi_twocc, pf_format_check ) )
    {
        if( !b_forced_demux )
            return VLC_EGENERIC;
    }
    const bool b_wav = i_skip > 0;

    /* peek the beginning
     * It is common that wav files have some sort of garbage at the beginning
     * We will accept probing 0.5s of data in this case.
     */

    const size_t i_probe = i_skip + i_check_size + i_base_probing + ( b_wav ? i_wav_extra_probing : 0);
    const ssize_t i_peek = vlc_stream_Peek( p_demux->s, &p_peek, i_probe );

    if( i_peek < 0 || (size_t)i_peek < i_skip + i_check_size )
        return VLC_EGENERIC;

    for( ;; )
    {
        /* i_peek > 0 so we can cast into size_t. */
        if( i_skip + i_check_size > (size_t)i_peek )
        {
            if( !b_forced_demux )
                return VLC_EGENERIC;
            break;
        }
        unsigned i_samples = 0;
        int i_size = pf_check( &p_peek[i_skip], &i_samples );
        if( i_size >= 0 )
        {
            if( i_size == 0 || /* 0 sized frame ?? */
                i_skip == 0 /* exact match from start, we're not WAVE either, so skip multiple checks (would break if padding) */ )
                break;

            /* If we have the frame size, check the next frame for
             * extra robustness
             * The second test is because some .wav have paddings
             */

            bool b_ok = false;
            for( int t = 0; t < 1 + !!b_wav; t++ )
            {
                if( t == 1 )
                {
                    if(!i_samples)
                        break;
                    i_size = i_samples * 2 * 2;
                }

                if( i_skip + i_check_size + i_size <= (size_t)i_peek )
                {
                    b_ok = pf_check( &p_peek[i_skip+i_size], NULL ) >= 0;
                    if( b_ok )
                        break;
                }
            }
            if( b_ok )
                break;
        }
        if( b_use_word )
            i_skip += ((i_offset + i_skip) % 2 == 0) ? 2 : 1;
        else
            i_skip++;
        if( !b_wav && !b_forced_demux )
            return VLC_EGENERIC;
    }

    *pi_offset = i_offset + i_skip;
    return VLC_SUCCESS;
}

/*****************************************************************************
 * Mpeg I/II Audio
 *****************************************************************************/

static int MpgaCheckSync( const uint8_t *p_peek )
{
    uint32_t h = GetDWBE( p_peek );

    if( ((( h >> 21 )&0x07FF) != 0x07FF )   /* header sync */
        || (((h >> 19)&0x03) == 1 )         /* valid version ID ? */
        || (((h >> 17)&0x03) == 0 )         /* valid layer ?*/
        || (((h >> 12)&0x0F) == 0x0F )      /* valid bitrate ?*/
        || (((h >> 10) & 0x03) == 0x03 )    /* valid sampling freq ? */
        || ((h & 0x03) == 0x02 ))           /* valid emphasis ? */
    {
        return false;
    }
    return true;
}

#define MPGA_VERSION( h )   ( 1 - (((h)>>19)&0x01) )
#define MPGA_MODE(h)        (((h)>> 6)&0x03)

static int MpgaProbe( demux_t *p_demux, uint64_t *pi_offset )
{
    const uint16_t rgi_twocc[] = { WAVE_FORMAT_MPEG, WAVE_FORMAT_MPEGLAYER3, WAVE_FORMAT_UNKNOWN };
    bool   b_forced;
    bool   b_forced_demux;
    uint64_t i_offset;

    const uint8_t *p_peek;
    uint64_t i_skip;
    ssize_t i_peek;

    b_forced = demux_IsPathExtension( p_demux, ".mp3" );
    b_forced_demux = demux_IsForced( p_demux, "mp3" ) ||
                     demux_IsForced( p_demux, "mpga" );

    i_offset = vlc_stream_Tell( p_demux->s );

    if( WavSkipHeader( p_demux, &i_skip, rgi_twocc, NULL ) )
    {
        if( !b_forced_demux )
            return VLC_EGENERIC;

        return VLC_EGENERIC;
    }

    i_peek = vlc_stream_Peek( p_demux->s, &p_peek, i_skip + 4 );
    if( i_peek <= 0 || (uint64_t) i_peek < i_skip + 4 )
        return VLC_EGENERIC;

    if( !MpgaCheckSync( &p_peek[i_skip] ) )
    {
        bool b_ok = false;

        if( !b_forced_demux && !b_forced )
            return VLC_EGENERIC;

        i_peek = vlc_stream_Peek( p_demux->s, &p_peek, i_skip + 8096 );
        while( i_peek > 0 && i_skip + 4 < (uint64_t) i_peek )
        {
            if( MpgaCheckSync( &p_peek[i_skip] ) )
            {
                b_ok = true;
                break;
            }
            i_skip++;
        }
        if( !b_ok && !b_forced_demux )
            return VLC_EGENERIC;
    }
    *pi_offset = i_offset + i_skip;
    return VLC_SUCCESS;
}

static int SeekByMlltTable( sync_table_t *mllt, vlc_tick_t *pi_time, uint64_t *pi_offset )
{
    if( !mllt->p_bits )
        return -1;

    sync_table_ctx_t *p_cur = &mllt->current;

    /* reset or init context */
    if( *pi_time < p_cur->i_time || !p_cur->br.p )
    {
        p_cur->i_time = 0;
        p_cur->i_pos = 0;
        bs_init(&p_cur->br, mllt->p_bits, mllt->i_bits);
    }

    while(!bs_eof(&p_cur->br))
    {
        const uint32_t i_bytesdev = bs_read(&p_cur->br, mllt->i_bits_per_bytes_dev);
        const uint32_t i_msdev = bs_read(&p_cur->br, mllt->i_bits_per_ms_dev);
        if(bs_error(&p_cur->br))
            break;
        const vlc_tick_t i_deltatime = VLC_TICK_FROM_MS(mllt->i_ms_btw_refs + i_msdev);
        if( p_cur->i_time + i_deltatime > *pi_time )
            break;
        p_cur->i_time += i_deltatime;
        p_cur->i_pos += mllt->i_bytes_btw_refs + i_bytesdev;
    }
    *pi_time = p_cur->i_time;
    *pi_offset = p_cur->i_pos;

    return 0;
}

static int ID3TAG_Parse_Handler( uint32_t i_tag, const uint8_t *p_payload, size_t i_payload, void *p_priv )
{
    demux_t *p_demux = (demux_t *) p_priv;
    demux_sys_t *p_sys = p_demux->p_sys;

    if( p_sys->p_meta == NULL )
        p_sys->p_meta = vlc_meta_New();
    if( p_sys->p_meta != NULL )
    {
        ID3HandleTag( p_payload, i_payload, i_tag, p_sys->p_meta, NULL );
        if( i_tag == VLC_FOURCC('T', 'X', 'X', 'X') )
            vlc_replay_gain_CopyFromMeta( &p_sys->audio_replay_gain, p_sys->p_meta );
    }

    if( i_tag == VLC_FOURCC('M', 'L', 'L', 'T') )
    {
        if( i_payload > 20 )
        {
            p_sys->mllt.i_frames_btw_refs = GetWBE(p_payload);
            p_sys->mllt.i_bytes_btw_refs = GetDWBE(&p_payload[1]) & 0x00FFFFFF;
            p_sys->mllt.i_ms_btw_refs = GetDWBE(&p_payload[4]) & 0x00FFFFFF;
            if( !p_sys->mllt.i_frames_btw_refs || !p_sys->mllt.i_bytes_btw_refs ||
                    !p_sys->mllt.i_ms_btw_refs ||
                    p_payload[8] > 31 || p_payload[9] > 31 || /* bits length sanity check */
                    ((p_payload[8] + p_payload[9]) % 4) || p_payload[8] + p_payload[9] < 4 )
                return VLC_EGENERIC;
            p_sys->mllt.i_bits_per_bytes_dev = p_payload[8];
            p_sys->mllt.i_bits_per_ms_dev = p_payload[9];
            p_sys->mllt.p_bits = malloc(i_payload - 10);
            if( likely(p_sys->mllt.p_bits) )
            {
                p_sys->mllt.i_bits = i_payload - 10;
                memcpy(p_sys->mllt.p_bits, &p_payload[10], p_sys->mllt.i_bits);
                msg_Dbg(p_demux, "read MLLT sync table with %zu entries",
                        (p_sys->mllt.i_bits * 8) / (p_sys->mllt.i_bits_per_bytes_dev + p_sys->mllt.i_bits_per_ms_dev) );
            }
        }
        return VLC_SUCCESS;
    }
    else if ( i_tag == VLC_FOURCC('C', 'H', 'A', 'P') && i_payload >= 17 )
    {
        char *psz_title = strndup( (const char *)p_payload, i_payload - 16 );
        size_t i_offset = psz_title ? strlen( psz_title ) : 0;
        if( p_payload[i_offset] != 0 )
        {
            free( psz_title );
            return VLC_SUCCESS;
        }
        chap_entry_t e;
        e.p_seekpoint = vlc_seekpoint_New();
        if( e.p_seekpoint )
        {
            e.p_seekpoint->psz_name = psz_title;
            e.p_seekpoint->i_time_offset = VLC_TICK_FROM_MS(GetDWBE(&p_payload[1 + i_offset]));
            e.i_offset = GetDWBE(&p_payload[1 + i_offset + 8]);
            p_payload += i_offset + 1 + 16;
            i_payload -= i_offset + 1 + 16;
            if( 12 < i_payload && !memcmp("TIT2", p_payload, 4) )
            {
                psz_title = NULL; /* optional alloc */
                const char *psz = ID3TextConvert(&p_payload[10], i_payload-12, &psz_title);
                if( psz ) /* replace with TIT2 */
                {
                    free( e.p_seekpoint->psz_name );
                    e.p_seekpoint->psz_name = (psz_title) ? psz_title : strdup( psz );
                }
            }
            TAB_APPEND(p_sys->chapters.i_count, p_sys->chapters.p_entry, e);
        } else free( psz_title );
    }
    else if( i_tag == VLC_FOURCC('S', 'Y', 'L', 'T') )
    {
        /* SYLT frame: encoding(1) + language(3) + timestamp_fmt(1) + content_type(1) + descriptor(null-term) + events */
        if( i_payload < 6 )
            return VLC_SUCCESS;

        const uint8_t i_encoding = p_payload[0];
        const uint8_t i_timestamp_fmt = p_payload[4];
        if( i_timestamp_fmt != 0x02 ) /* only millisecond timestamps are supported */
        {
            msg_Warn( p_demux, "SYLT: unsupported timestamp format %u, skipping",
                      (unsigned) i_timestamp_fmt );
            return VLC_SUCCESS;
        }

        /* Parse language (3-byte ISO 639-2 code, may not be null-terminated) */
        char psz_language[4];
        memcpy( psz_language, p_payload + 1, 3 );
        psz_language[3] = '\0';

        /* Find or create a track entry for this language */
        sylt_track_t *p_tk = NULL;
        for( size_t i = 0; i < p_sys->sylt.i_count; i++ )
        {
            if( strcmp( p_sys->sylt.p_track[i].psz_language
                            ? p_sys->sylt.p_track[i].psz_language : "",
                        psz_language ) == 0 )
            {
                p_tk = &p_sys->sylt.p_track[i];
                break;
            }
        }
        if( !p_tk )
        {
            sylt_track_t new_tk;
            new_tk.psz_language = strdup( psz_language );
            new_tk.i_count   = 0;
            new_tk.i_current = 0;
            new_tk.p_entry   = NULL;
            new_tk.p_es      = NULL;
            TAB_APPEND( p_sys->sylt.i_count, p_sys->sylt.p_track, new_tk );
            p_tk = &p_sys->sylt.p_track[p_sys->sylt.i_count - 1];
        }

        /* skip fixed header: language(3) + timestamp_fmt(1) + content_type(1) = 5 bytes after encoding */
        const uint8_t *p = p_payload + 6;
        size_t i_remaining = i_payload - 6;

        bool b_utf16 = (i_encoding == ID3_ENCODING_UTF16 ||
                        i_encoding == ID3_ENCODING_UTF16BE);

        /* Skip content descriptor (null-terminated string) */
        if( b_utf16 ) /* UTF-16 */
        {
            while( i_remaining >= 2 && (p[0] != 0 || p[1] != 0) )
            {
                p += 2;
                i_remaining -= 2;
            }
            if( i_remaining < 2 )
                return VLC_SUCCESS;
            p += 2;
            i_remaining -= 2;
        }
        else /* ISO-8859-1 or UTF-8 */
        {
            while( i_remaining > 0 && *p != 0 )
            {
                p++;
                i_remaining--;
            }
            if( i_remaining == 0 )
                return VLC_SUCCESS;
            p++;
            i_remaining--;
        }

        /* Parse repeated [text (null-terminated)] [timestamp (4 bytes BE ms)] */
        while( i_remaining > 4 )
        {
            const uint8_t *p_text_start = p;
            size_t i_text_len = 0;

            if( b_utf16 ) /* UTF-16 */
            {
                while( i_remaining - i_text_len >= 2 + 4 &&
                       (p_text_start[i_text_len] != 0 || p_text_start[i_text_len + 1] != 0) )
                    i_text_len += 2;

                if( i_remaining - i_text_len < 2 + 4 )
                    break;
                i_text_len += 2; /* include null terminator */
            }
            else /* ISO-8859-1 or UTF-8 */
            {
                while( i_text_len < i_remaining - 4 && p_text_start[i_text_len] != 0 )
                    i_text_len++;

                if( i_text_len >= i_remaining - 4 )
                    break;
                i_text_len++; /* include null terminator */
            }

            /* Convert text to UTF-8 */
            char *psz_allocated = NULL;
            size_t i_null_size = b_utf16 ? 2 : 1;
            const char *psz_text = ID3TextConv( p_text_start, i_text_len - i_null_size,
                                                i_encoding, &psz_allocated );

            p += i_text_len;
            i_remaining -= i_text_len;

            uint32_t i_timestamp_ms = GetDWBE( p );
            p += 4;
            i_remaining -= 4;

            if( psz_text && *psz_text )
            {
                sylt_entry_t entry;
                entry.i_start = VLC_TICK_FROM_MS( i_timestamp_ms );
                entry.psz_text = psz_allocated ? psz_allocated : strdup( psz_text );
                if( entry.psz_text )
                    TAB_APPEND( p_tk->i_count, p_tk->p_entry, entry );
                else
                    free( psz_allocated );
            }
            else
            {
                free( psz_allocated );
            }
        }
    }
    else if( i_tag == VLC_FOURCC('U', 'S', 'L', 'T') )
    {
        /* USLT frame: encoding(1) + language(3) + content descriptor + lyrics/text */
        if( i_payload < 4 )
            return VLC_SUCCESS;

        const uint8_t i_encoding = p_payload[0];
        char psz_language[4];
        memcpy( psz_language, p_payload + 1, 3 );
        psz_language[3] = '\0';

        const uint8_t *p = p_payload + 4;
        size_t i_remaining = i_payload - 4;

        bool b_utf16 = (i_encoding == ID3_ENCODING_UTF16 ||
                        i_encoding == ID3_ENCODING_UTF16BE);

        if( b_utf16 )
        {
            while( i_remaining >= 2 && (p[0] != 0 || p[1] != 0) )
            {
                p += 2;
                i_remaining -= 2;
            }
            if( i_remaining < 2 )
                return VLC_SUCCESS;
            p += 2;
            i_remaining -= 2;
        }
        else
        {
            while( i_remaining > 0 && *p != 0 )
            {
                p++;
                i_remaining--;
            }
            if( i_remaining == 0 )
                return VLC_SUCCESS;
            p++;
            i_remaining--;
        }

        char *psz_allocated = NULL;
        const char *psz_text = ID3TextConv( p, i_remaining, i_encoding, &psz_allocated );
        if( psz_text && *psz_text )
        {
            if( p_sys->p_meta == NULL )
                p_sys->p_meta = vlc_meta_New();
            if( p_sys->p_meta != NULL )
            {
                char psz_key[32];
                snprintf( psz_key, sizeof(psz_key), "Lyrics:%s", psz_language );
                vlc_meta_SetExtra( p_sys->p_meta, psz_key, psz_text );

                /* Provide a synthetic uslt-data entry at t=0 so the Qt lyrics
                 * panel can display USLT text using the same timed-lyrics path. */

                char *psz_sylt;
                if( asprintf( &psz_sylt, "0\x1F%s\x1E", psz_text ) >= 0 )
                {
                    vlc_meta_SetExtra( p_sys->p_meta, "uslt-data", psz_sylt );
                    free( psz_sylt );
                }
            }
        }
        free( psz_allocated );
    }

    return VLC_SUCCESS;
}

static int ID3Parse( demux_t *p_demux,
                     int (*pf_callback)(uint32_t, const uint8_t *, size_t, void *) )
{
    const block_t *p_tags = NULL;

    if( vlc_stream_Control( p_demux->s, STREAM_GET_TAGS, &p_tags ) != VLC_SUCCESS )
        return VLC_EGENERIC;

    for( ; p_tags; p_tags = p_tags->p_next )
        ID3TAG_Parse( p_tags->p_buffer, p_tags->i_buffer, pf_callback, (void *) p_demux );

    return VLC_SUCCESS;
}

static int MpgaInit( demux_t *p_demux )
{
    demux_sys_t *p_sys = p_demux->p_sys;

    const uint8_t *p_peek;
    int i_peek;

    /* */
    p_sys->i_packet_size = 1024;

    ID3Parse( p_demux, ID3TAG_Parse_Handler );

    /* Load a potential xing header */
    i_peek = vlc_stream_Peek( p_demux->s, &p_peek, 4 + 1024 );
    if( i_peek < 4 + 21 )
        return VLC_SUCCESS;

    const uint32_t header = GetDWBE( p_peek );
    if( !MpgaCheckSync( p_peek ) || mpga_decode_frameheader( header, &p_sys->mpgah ) )
        return VLC_SUCCESS;

    if( p_sys->mpgah.i_layer == 3 )
        p_sys->codec.i_codec = VLC_CODEC_MP3;

    /* Xing header */
    int i_skip;

    if( MPGA_VERSION( header ) == 0 )
        i_skip = MPGA_MODE( header ) != 3 ? 36 : 21;
    else
        i_skip = MPGA_MODE( header ) != 3 ? 21 : 13;

    if( i_skip >= i_peek )
        return VLC_SUCCESS;

    struct xing_info_s *xing = &p_sys->xing;
    if( ParseXing( &p_peek[i_skip], i_peek - i_skip, xing ) == VLC_SUCCESS )
    {
        const uint64_t i_total_samples = xing->i_frames * (uint64_t) p_sys->mpgah.i_samples_per_frame;
        const uint64_t i_dropped_samples = xing->i_delay_samples + xing->i_padding_samples;

        if( p_sys->mpgah.i_sample_rate && i_dropped_samples < i_total_samples )
        {
            uint64_t i_stream_size;
            /* We only set duration if we can't check (then compute it using bitrate/size)
               or if we verified the file isn't truncated */

            if( xing->i_bytes == 0 ||
                vlc_stream_GetSize( p_demux->s, &i_stream_size ) ||
               (i_stream_size >= xing->i_bytes &&
                i_stream_size <= xing->i_bytes + p_sys->mpgah.i_frame_size) )
            {
                p_sys->i_duration = vlc_tick_from_samples( i_total_samples - i_dropped_samples,
                                                           p_sys->mpgah.i_sample_rate );
            }
        }

        unsigned i_bitrate = 0;
        if( xing->brmode == XING_MODE_ABR || xing->brmode == XING_MODE_ABR_2PASS )
            i_bitrate = xing->bitrate_avg * 1000;

        if( !i_bitrate && p_sys->mpgah.i_sample_rate &&
            xing->i_bytes > p_sys->mpgah.i_frame_size )
        {
            unsigned d = vlc_tick_from_samples( i_total_samples, p_sys->mpgah.i_sample_rate );
            if( d )
                i_bitrate = (xing->i_bytes - p_sys->mpgah.i_frame_size) * 8 * CLOCK_FREQ / d;
        }

        if( i_bitrate )
        {
            p_sys->i_bitrate = i_bitrate;
            p_sys->b_estimate_bitrate = false;
        }

        if( isfinite(xing->f_radio_replay_gain) )
        {
            p_sys->audio_replay_gain.pb_gain[AUDIO_REPLAY_GAIN_TRACK] = true;
            p_sys->audio_replay_gain.pf_gain[AUDIO_REPLAY_GAIN_TRACK] = xing->f_radio_replay_gain;
        }

        if( isfinite(xing->f_peak_signal) )
        {
            p_sys->audio_replay_gain.pb_peak[AUDIO_REPLAY_GAIN_TRACK] = true;
            p_sys->audio_replay_gain.pf_peak[AUDIO_REPLAY_GAIN_TRACK] = xing->f_peak_signal;
        }

        if( isfinite(xing->f_audiophile_replay_gain) )
        {
            p_sys->audio_replay_gain.pb_gain[AUDIO_REPLAY_GAIN_ALBUM] = true;
            p_sys->audio_replay_gain.pf_gain[AUDIO_REPLAY_GAIN_ALBUM] = xing->f_audiophile_replay_gain;
        }

        msg_Dbg( p_demux, "Using '%4.4s' infotag"
                          "(%"PRIu32" bytes, %"PRIu32" frames, %u samples/frame)",
                          (char *) &xing->infotag,
                          xing->i_bytes, xing->i_frames,
                          p_sys->mpgah.i_samples_per_frame );

        /* We'll need to skip that part for playback
         * and avoid using it as container frame could be different rate/size */

        p_sys->i_stream_offset += p_sys->mpgah.i_frame_size;
    }

    return VLC_SUCCESS;
}

/*****************************************************************************
 * AAC
 *****************************************************************************/

static int AacProbe( demux_t *p_demux, uint64_t *pi_offset )
{
    bool   b_forced;
    bool   b_forced_demux;

    uint64_t i_offset;
    const uint8_t *p_peek;

    b_forced = demux_IsPathExtension( p_demux, ".aac" ) ||
               demux_IsPathExtension( p_demux, ".aacp" );
    b_forced_demux = demux_IsForced( p_demux, "m4a" ) ||
                     demux_IsForced( p_demux, "aac" ) ||
                     demux_IsForced( p_demux, "mp4a" );

    if( !b_forced_demux && !b_forced )
        return VLC_EGENERIC;

    i_offset = vlc_stream_Tell( p_demux->s );

    /* peek the beginning (10 is for adts header) */
    if( vlc_stream_Peek( p_demux->s, &p_peek, 10 ) < 10 )
        return VLC_EGENERIC;

    if( !strncmp( (char *)p_peek, "ADIF", 4 ) )
    {
        msg_Err( p_demux, "ADIF file. Not yet supported. (Please report)" );
        return VLC_EGENERIC;
    }

    *pi_offset = i_offset;
    return VLC_SUCCESS;
}
static int AacInit( demux_t *p_demux )
{
    demux_sys_t *p_sys = p_demux->p_sys;

    p_sys->i_packet_size = 4096;
    p_sys->i_original = VLC_FOURCC('H','E','A','D');

    return VLC_SUCCESS;
}


/*****************************************************************************
 * A52
 *****************************************************************************/

static int A52CheckSync( const uint8_t *p_peek, bool *p_big_endian, unsigned *pi_samples, bool b_eac3 )
{
    vlc_a52_header_t header;
    uint8_t p_tmp[VLC_A52_MIN_HEADER_SIZE];

    *p_big_endian =  p_peek[0] == 0x0b && p_peek[1] == 0x77;
    if( !*p_big_endian )
    {
        swab( p_peek, p_tmp, VLC_A52_MIN_HEADER_SIZE );
        p_peek = p_tmp;
    }

    if( vlc_a52_header_Parse( &header, p_peek, VLC_A52_MIN_HEADER_SIZE ) )
        return VLC_EGENERIC;

    if( !header.b_eac3 != !b_eac3 )
        return VLC_EGENERIC;
    if( pi_samples )
        *pi_samples = header.i_samples;
    return header.i_size;
}
static int EA52CheckSyncProbe( const uint8_t *p_peek, unsigned *pi_samples )
{
    bool b_dummy;
    return A52CheckSync( p_peek, &b_dummy, pi_samples, true );
}

static int EA52Probe( demux_t *p_demux, uint64_t *pi_offset )
{
    const char *ppsz_name[] = { "eac3", NULL };
    const uint16_t rgi_twocc[] = { WAVE_FORMAT_PCM, WAVE_FORMAT_A52, WAVE_FORMAT_UNKNOWN };

    return GenericProbe( p_demux, pi_offset, ppsz_name, EA52CheckSyncProbe,
                         VLC_A52_MIN_HEADER_SIZE,
                         1920 + VLC_A52_MIN_HEADER_SIZE + 1,
                         WAV_EXTRA_PROBE_SIZE,
                         true, rgi_twocc, GenericFormatCheck );
}

static int A52CheckSyncProbe( const uint8_t *p_peek, unsigned *pi_samples )
{
    bool b_dummy;
    return A52CheckSync( p_peek, &b_dummy, pi_samples, false );
}

static int A52Probe( demux_t *p_demux, uint64_t *pi_offset )
{
    const char *ppsz_name[] = { "a52", "ac3", NULL };
    const uint16_t rgi_twocc[] = { WAVE_FORMAT_PCM, WAVE_FORMAT_A52, WAVE_FORMAT_UNKNOWN };

    return GenericProbe( p_demux, pi_offset, ppsz_name, A52CheckSyncProbe,
                         VLC_A52_MIN_HEADER_SIZE,
                         1920 + VLC_A52_MIN_HEADER_SIZE + 1,
                         WAV_EXTRA_PROBE_SIZE,
                         true, rgi_twocc, GenericFormatCheck );
}

static int A52Init( demux_t *p_demux )
{
    demux_sys_t *p_sys = p_demux->p_sys;

    p_sys->b_big_endian = false;
    p_sys->i_packet_size = 1024;

    const uint8_t *p_peek;

    /* peek the beginning */
    if( vlc_stream_Peek( p_demux->s, &p_peek, VLC_A52_MIN_HEADER_SIZE ) >= VLC_A52_MIN_HEADER_SIZE )
    {
        A52CheckSync( p_peek, &p_sys->b_big_endian, NULL, true );
    }
    return VLC_SUCCESS;
}

/*****************************************************************************
 * DTS
 *****************************************************************************/

static int DtsCheckSync( const uint8_t *p_peek, unsigned *pi_samples )
{
    VLC_UNUSED(pi_samples);

    vlc_dts_header_t dts;
    if( vlc_dts_header_Parse( &dts, p_peek, VLC_DTS_HEADER_SIZE ) == VLC_SUCCESS
     && dts.i_frame_size > 0 && dts.i_frame_size <= 8192 )
    {
        if( pi_samples )
            *pi_samples = dts.i_frame_length;
        return dts.i_frame_size;
    }
    else
        return VLC_EGENERIC;
}

static int DtsProbe( demux_t *p_demux, uint64_t *pi_offset )
{
    const char *ppsz_name[] = { "dts", NULL };
    const uint16_t rgi_twocc[] = { WAVE_FORMAT_PCM, WAVE_FORMAT_DTSINC_DTS, WAVE_FORMAT_UNKNOWN };

    return GenericProbe( p_demux, pi_offset, ppsz_name, DtsCheckSync,
                         VLC_DTS_HEADER_SIZE,
                         16384 + VLC_DTS_HEADER_SIZE + 1,
                         WAV_EXTRA_PROBE_SIZE,
                         false, rgi_twocc, NULL );
}
static int DtsInit( demux_t *p_demux )
{
    demux_sys_t *p_sys = p_demux->p_sys;

    p_sys->i_packet_size = 16384;

    return VLC_SUCCESS;
}

/*****************************************************************************
 * MLP
 *****************************************************************************/

static int MlpCheckSync( const uint8_t *p_peek, unsigned *pi_samples )
{
    if( p_peek[4+0] != 0xf8 || p_peek[4+1] != 0x72 || p_peek[4+2] != 0x6f )
        return -1;

    if( p_peek[4+3] != 0xbb )
        return -1;

    /* TODO checksum and real size for robustness */
    VLC_UNUSED(pi_samples);
    return 0;
}
static int ThdCheckSync( const uint8_t *p_peek, unsigned *pi_samples )
{
    if( p_peek[4+0] != 0xf8 || p_peek[4+1] != 0x72 || p_peek[4+2] != 0x6f )
        return -1;

    if( p_peek[4+3] != 0xba )
        return -1;

    /* TODO checksum and real size for robustness */
    VLC_UNUSED(pi_samples);
    return 0;
}
static int MlpProbe( demux_t *p_demux, uint64_t *pi_offset )
{
    const char *ppsz_name[] = { "mlp", NULL };
    const uint16_t rgi_twocc[] = { WAVE_FORMAT_PCM, WAVE_FORMAT_UNKNOWN };

    return GenericProbe( p_demux, pi_offset, ppsz_name, MlpCheckSync,
                         4+28+16*4, BASE_PROBE_SIZE, WAV_EXTRA_PROBE_SIZE,
                         false, rgi_twocc, GenericFormatCheck );
}
static int ThdProbe( demux_t *p_demux, uint64_t *pi_offset )
{
    const char *ppsz_name[] = { "thd", NULL };
    const uint16_t rgi_twocc[] = { WAVE_FORMAT_PCM, WAVE_FORMAT_UNKNOWN };

    return GenericProbe( p_demux, pi_offset, ppsz_name, ThdCheckSync,
                         4+28+16*4, BASE_PROBE_SIZE, WAV_EXTRA_PROBE_SIZE,
                         false, rgi_twocc, GenericFormatCheck );
}
static int MlpInit( demux_t *p_demux )

{
    demux_sys_t *p_sys = p_demux->p_sys;

    p_sys->i_packet_size = 4096;

    return VLC_SUCCESS;
}

/*****************************************************************************
 * Video
 *****************************************************************************/

static int VideoInit( demux_t *p_demux )
{
    demux_sys_t *p_sys = p_demux->p_sys;

    p_sys->i_packet_size = 4096;

    return VLC_SUCCESS;
}

Messung
CHZG
84979592

¤ Dauer der Verarbeitung: 0.22 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.