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

Quelle  matroska_segment_parse.cpp   Sprache: C

 

/*****************************************************************************
 * matroska_segment_parse.cpp : matroska demuxer
 *****************************************************************************
 * Copyright (C) 2003-2010 VLC authors and VideoLAN
 *
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
 *          Steve Lhomme <steve.lhomme@free.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.
 *****************************************************************************/

#include "mkv.hpp"
#include "matroska_segment.hpp"
#include "chapters.hpp"
#include "demux.hpp"
#include "Ebml_parser.hpp"
#include "Ebml_dispatcher.hpp"
#include "string_dispatcher.hpp"
#include "util.hpp"
#include "chapter_command_script.hpp"

extern "C" {
#include "../vobsub.h"
#include "../xiph.h"
#include "../windows_audio_commons.h"
#include "../mp4/libmp4.h"
}

#include "vlc_colors.h"

#include <vlc_iso_lang.h>
#include <vlc_codecs.h>
#include <stdexcept>
#include <limits>
#include <algorithm>

namespace mkv {

/* GetFourCC helper */
#define GetFOURCC( p )  __GetFOURCC( (uint8_t*)p )
static vlc_fourcc_t __GetFOURCC( uint8_t *p )
{
    return VLC_FOURCC( p[0], p[1], p[2], p[3] );
}

static inline void fill_extra_data_alac( mkv_track_t *p_tk )
{
    if( p_tk->i_extra_data <= 0 ) return;
    p_tk->fmt.p_extra = malloc( p_tk->i_extra_data + 12 );
    if( unlikely( !p_tk->fmt.p_extra ) ) return;
    p_tk->fmt.i_extra = p_tk->i_extra_data + 12;
    uint8_t *p_extra = static_cast<uint8_t*>( p_tk->fmt.p_extra );
    /* 12 bytes + "ALAC Specific Info (24 bytes) (required)" from
       https://github.com/macosforge/alac/blob/master/ALACMagicCookieDescription.txt */

    SetDWBE( p_extra, p_tk->fmt.i_extra );
    memcpy( p_extra + 4, "alac", 4 );
    SetDWBE( p_extra + 8, p_tk->i_extra_data );
    memcpy( p_extra + 12, p_tk->p_extra_data, p_tk->i_extra_data );
}

static inline void fill_extra_data( mkv_track_t *p_tk, unsigned int offset )
{
    if(p_tk->i_extra_data <= offset) return;
    p_tk->fmt.i_extra = p_tk->i_extra_data - offset;
    p_tk->fmt.p_extra = xmalloc( p_tk->fmt.i_extra );
    if(!p_tk->fmt.p_extra) { p_tk->fmt.i_extra = 0; return; };
    memcpy( p_tk->fmt.p_extra, p_tk->p_extra_data + offset, p_tk->fmt.i_extra );
}

/*****************************************************************************
 * Some functions to manipulate memory
 *****************************************************************************/

static inline const char * ToUTF8Const( const UTFstring &u )
{
    return u.GetUTF8().c_str();
}

static inline char * ToUTF8( const UTFstring &u )
{
    return strdup( ToUTF8Const(u) );
}

/*****************************************************************************
 * ParseSeekHead:
 *****************************************************************************/

void matroska_segment_c::ParseSeekHead( KaxSeekHead *seekhead )
{
    EbmlElement *l;

    i_seekhead_count++;

    if( !sys.b_seekable )
        return;

    EbmlParser eparser ( &es, seekhead, &sys.demuxer );

    while( ( l = eparser.Get() ) != NULL )
    {
        if( MKV_IS_ID( l, KaxSeek ) )
        {
            EbmlId id = EBML_ID(EbmlVoid);
            int64_t i_pos = -1;

#ifdef MKV_DEBUG
            msg_Dbg( &sys.demuxer, "|   |   + Seek" );
#endif
            eparser.Down();
            try
            {
                while( ( l = eparser.Get() ) != NULL )
                {
                    if( unlikely( !l->ValidateSize() ) )
                    {
                        msg_Err( &sys.demuxer,"%s too big... skipping it",  EBML_NAME(l) );
                        continue;
                    }
                    if( MKV_IS_ID( l, KaxSeekID ) )
                    {
                        KaxSeekID &sid = *static_cast<KaxSeekID*>( l );
                        sid.ReadData( es.I_O() );
                        id = EbmlId( sid.GetBuffer(), sid.GetSize() );
                    }
                    else if( MKV_IS_ID( l, KaxSeekPosition ) )
                    {
                        KaxSeekPosition &spos = *static_cast<KaxSeekPosition*>( l );
                        spos.ReadData( es.I_O() );
                        i_pos = (int64_t)segment->GetGlobalPosition( static_cast<uint64_t>( spos ) );
                    }
                    else if ( !MKV_IS_ID( l, EbmlVoid ) && !MKV_IS_ID( l, EbmlCrc32 ))
                    {
                        /* Many mkvmerge files hit this case. It seems to be a broken SeekHead */
                        msg_Dbg( &sys.demuxer, "|   |   + Unknown (%s)", EBML_NAME(l) );
                    }
                }
            }
            catch(...)
            {
                msg_Err( &sys.demuxer,"Error while reading %s",  EBML_NAME(l) );
            }
            eparser.Up();

            if( i_pos >= 0 )
            {
                if( id == EBML_ID(KaxCluster) )
                {
                    _seeker.add_cluster_position( i_pos );
                }
                else if( id == EBML_ID(KaxCues) )
                {
                    msg_Dbg( &sys.demuxer, "|   - cues at %" PRId64, i_pos );
                    LoadSeekHeadItem( EBML_INFO(KaxCues), i_pos );
                }
                else if( id == EBML_ID(KaxInfo) )
                {
                    msg_Dbg( &sys.demuxer, "|   - info at %" PRId64, i_pos );
                    LoadSeekHeadItem( EBML_INFO(KaxInfo), i_pos );
                }
                else if( id == EBML_ID(KaxChapters) )
                {
                    msg_Dbg( &sys.demuxer, "|   - chapters at %" PRId64, i_pos );
                    LoadSeekHeadItem( EBML_INFO(KaxChapters), i_pos );
                }
                else if( id == EBML_ID(KaxTags) )
                {
                    msg_Dbg( &sys.demuxer, "|   - tags at %" PRId64, i_pos );
                    LoadSeekHeadItem( EBML_INFO(KaxTags), i_pos );
                }
                else if( id == EBML_ID(KaxSeekHead) )
                {
                    msg_Dbg( &sys.demuxer, "|   - chained seekhead at %" PRId64, i_pos );
                    LoadSeekHeadItem( EBML_INFO(KaxSeekHead), i_pos );
                }
                else if( id == EBML_ID(KaxTracks) )
                {
                    msg_Dbg( &sys.demuxer, "|   - tracks at %" PRId64, i_pos );
                    LoadSeekHeadItem( EBML_INFO(KaxTracks), i_pos );
                }
                else if( id == EBML_ID(KaxAttachments) )
                {
                    msg_Dbg( &sys.demuxer, "|   - attachments at %" PRId64, i_pos );
                    LoadSeekHeadItem( EBML_INFO(KaxAttachments), i_pos );
                }
#ifdef MKV_DEBUG
                else if( id != EBML_ID(KaxCluster) && id != EBML_ID(EbmlVoid) &&
                         id != EBML_ID(EbmlCrc32))
                    msg_Dbg( &sys.demuxer, "|   - unknown seekhead reference at %" PRId64, i_pos );
#endif
            }
        }
        else if ( !MKV_IS_ID( l, EbmlVoid ) && !MKV_IS_ID( l, EbmlCrc32 ))
            msg_Dbg( &sys.demuxer, "|   |   + ParseSeekHead Unknown (%s)", EBML_NAME(l) );
    }
}


/*****************************************************************************
 * ParseTrackEntry:
 *****************************************************************************/

#define ONLY_FMT(t) if(vars.tk->fmt.i_cat != t ## _ES) return
void matroska_segment_c::ParseTrackEntry( const KaxTrackEntry *m )
{
    bool bSupported = true;

    uint8_t ttype;
    try {
        KaxTrackType & pTrackType = GetMandatoryChild<KaxTrackType>(*m);
        ttype = pTrackType;
    } catch (const MissingMandatory & err) {
        msg_Dbg( &sys.demuxer, "%s", err.what());
        ttype = 0;
    }

    enum es_format_category_e es_cat;
    switch( ttype )
    {
        case track_audio:
            es_cat = AUDIO_ES;
            break;
        case track_video:
            es_cat = VIDEO_ES;
            break;
        case track_subtitle:
        case track_buttons:
            es_cat = SPU_ES;
            break;
        default:
            es_cat = UNKNOWN_ES;
            break;
    }

    /* Init the track */
    mkv_track_t *p_track = new mkv_track_t(es_cat);

    MkvTree( sys.demuxer, 2, "Track Entry" );

    struct MetaDataCapture {
      matroska_segment_c * obj;
      mkv_track_t        * tk;
      demux_t            * p_demuxer;
      bool&                bSupported;
      int                  level;
      std::string          lang;
      bool                 lang_is_ietf;
      struct {
        unsigned int i_crop_right;
        unsigned int i_crop_left;
        unsigned int i_crop_top;
        unsigned int i_crop_bottom;
        unsigned int i_display_unit;
        unsigned int i_display_width;
        unsigned int i_display_height;

        unsigned int chroma_sit_vertical;
        unsigned int chroma_sit_horizontal;
      } track_video_info;

      struct {
          float yaw;
          float pitch;
          float roll;
      } pose;

    } metadata_payload = {
      this, p_track, &sys.demuxer, bSupported, 3, "eng", false, { }, { }
    };

    MKV_SWITCH_CREATE( EbmlTypeDispatcher, MetaDataHandlers, MetaDataCapture )
    {
        MKV_SWITCH_INIT();

        static void debug (MetaDataCapture const& vars, char const * fmt, ...) VLC_FORMAT( 2, 3 )
        {
            va_list args; va_start( args, fmt );
            MkvTree_va( *vars.p_demuxer, vars.level, fmt, args);
            va_end( args );
        }
        E_CASE( KaxTrackNumber, tnum )
        {
            vars.tk->i_number = static_cast<uint32_t>( tnum );
            debug( vars, "Track Number=%u", vars.tk->i_number );
        }
        E_CASE( KaxTrackUID, tuid )
        {
            debug( vars, "Track UID=%x", static_cast<uint32_t>( tuid ) );
        }
        E_CASE( KaxTrackType, ttype )
        {
            const char *psz_type;

            switch( static_cast<uint8_t>( ttype ) )
            {
                case track_audio:
                    psz_type = "audio";
                    break;
                case track_video:
                    psz_type = "video";
                    break;
                case track_subtitle:
                    psz_type = "subtitle";
                    break;
                case track_buttons:
                    psz_type = "buttons";
                    break;
                default:
                    psz_type = "unknown";
                    break;
            }

            debug( vars, "Track Type=%s", psz_type ) ;
        }
        E_CASE( KaxTrackFlagEnabled, fenb ) // UNUSED
        {
            vars.tk->b_enabled = static_cast<uint32_t>( fenb );
            debug( vars, "Track Enabled=%u", vars.tk->b_enabled );
        }
        E_CASE( KaxTrackFlagDefault, fdef )
        {
            vars.tk->b_default = static_cast<uint32_t>( fdef );
            debug( vars, "Track Default=%u", vars.tk->b_default );
        }
        E_CASE( KaxTrackFlagForced, ffor ) // UNUSED
        {
            vars.tk->b_forced = static_cast<uint32_t>( ffor );

            debug( vars, "Track Forced=%u", vars.tk->b_forced );
        }
        E_CASE( KaxTrackFlagLacing, lac ) // UNUSED
        {
            debug( vars, "Track Lacing=%d", static_cast<uint32_t>( lac ) ) ;
        }
        E_CASE( KaxTrackMinCache, cmin ) // UNUSED
        {
            debug( vars, "Track MinCache=%d", static_cast<uint32_t>( cmin ) ) ;
        }
        E_CASE( KaxTrackMaxCache, cmax ) // UNUSED
        {
            debug( vars, "Track MaxCache=%d", static_cast<uint32_t>( cmax ) ) ;
        }
        E_CASE( KaxTrackDefaultDuration, defd )
        {
            vars.tk->i_default_duration = VLC_TICK_FROM_NS(static_cast<uint64_t>(defd));
            debug( vars, "Track Default Duration=%" PRId64, vars.tk->i_default_duration );
        }
        E_CASE( KaxTrackTimestampScale, ttcs )
        {
            vars.tk->f_timecodescale = static_cast<float>( ttcs );
            if ( vars.tk->f_timecodescale <= 0 ) vars.tk->f_timecodescale = 1.0;
            debug( vars, "Track TimeCodeScale=%f", vars.tk->f_timecodescale ) ;
        }
        E_CASE( KaxMaxBlockAdditionID, mbl ) // UNUSED
        {
            debug( vars, "Track Max BlockAdditionID=%d", static_cast<uint32_t>( mbl ) ) ;
        }
        E_CASE( KaxTrackName, tname )
        {
            free(vars.tk->fmt.psz_description);
            vars.tk->fmt.psz_description = ToUTF8( UTFstring( tname ) );
            debug( vars, "Track Name=%s", vars.tk->fmt.psz_description ? vars.tk->fmt.psz_description : "(null)" );
        }
        E_CASE( KaxTrackLanguage, lang )
        {
            const std::string slang ( lang );
            size_t pos = slang.find_first_of( '-' );
            const std::string l = std::string::npos ? slang.substr(0, pos) : slang;
            debug( vars, "Track Language=`%s'", !l.empty() ? l.c_str() : "(null)" );
            if (!vars.lang_is_ietf)
            {
                vars.lang = l;
            }
        }
#if LIBMATROSKA_VERSION >= 0x010406
        E_CASE( KaxLanguageIETF, lang )
        {
            vars.lang_is_ietf = true;
            const std::string slang ( lang );
            size_t pos = slang.find_first_of( '-' );
            vars.lang = pos != std::string::npos ? slang.substr(0, pos) : slang;
            debug( vars, "IETF Track Language=`%s'", !vars.lang.empty() ? vars.lang.c_str() : "(null)" );
        }
#endif
        E_CASE( KaxCodecID, codecid )
        {
            vars.tk->codec = std::string( codecid );
            debug( vars, "Track CodecId=%s", std::string( codecid ).c_str() ) ;
        }
        E_CASE( KaxCodecPrivate, cpriv )
        {
            vars.tk->i_extra_data = cpriv.GetSize();
            if( vars.tk->i_extra_data > 0 )
            {
                free( vars.tk->p_extra_data );
                vars.tk->p_extra_data = static_cast<uint8_t*>( malloc( vars.tk->i_extra_data ) );

                if( likely( vars.tk->p_extra_data ) )
                    memcpy( vars.tk->p_extra_data, cpriv.GetBuffer(),
                            vars.tk->i_extra_data );
            }
            debug( vars, "Track CodecPrivate size=%" PRId64, cpriv.GetSize() );
        }
        E_CASE( KaxCodecName, cname )
        {
            vars.tk->str_codec_name = static_cast<UTFstring const&>( cname ).GetUTF8();
            debug( vars, "Track Codec Name=%s", vars.tk->str_codec_name.c_str() ) ;
        }
        //AttachmentLink
        E_CASE( KaxCodecDecodeAll, cdall ) // UNUSED
        {
            debug( vars, "Track Codec Decode All=%u", static_cast<uint8_t>( cdall ) ) ;
        }
        E_CASE( KaxTrackOverlay, tovr ) // UNUSED
        {
            debug( vars, "Track Overlay=%u", static_cast<uint32_t>( tovr ) ) ;
        }
#if LIBMATROSKA_VERSION >= 0x010401
        E_CASE( KaxCodecDelay, codecdelay )
        {
            vars.tk->i_codec_delay = VLC_TICK_FROM_NS(static_cast<uint64_t>( codecdelay ));
            msg_Dbg( vars.p_demuxer, "|   |   |   + Track Codec Delay=%" PRIu64,
                     vars.tk->i_codec_delay );
        }
        E_CASE( KaxSeekPreRoll, spr )
        {
            vars.tk->i_seek_preroll = VLC_TICK_FROM_NS(static_cast<uint64_t>( spr ));
            debug( vars, "Track Seek Preroll=%" PRIu64, vars.tk->i_seek_preroll );
        }
#endif
        E_CASE( KaxContentEncodings, cencs )
        {
            debug( vars, "Content Encodings" );

            if ( cencs.ListSize () > 1 )
            {
                msg_Err( vars.p_demuxer, "Multiple Compression method not supported" );
                vars.bSupported = false;
            }

            vars.level += 1;
            dispatcher.iterate( cencs.begin(), cencs.end(), &vars );
            vars.level -= 1;
        }
        E_CASE( KaxContentEncoding, cenc )
        {
            debug( vars, "Content Encoding" );

            vars.level += 1;
            dispatcher.iterate( cenc.begin(), cenc.end(), &vars );
            vars.level -= 1;
        }
        E_CASE( KaxContentEncodingOrder, encord )
        {
            debug( vars, "Order: %i", static_cast<uint32_t>( encord ) );
        }
        E_CASE( KaxContentEncodingScope, encscope )
        {
            vars.tk->i_encoding_scope = static_cast<uint32_t>( encscope );
            debug( vars, "Scope: %i", vars.tk->i_encoding_scope );
        }
        E_CASE( KaxContentEncodingType, enctype )
        {
            debug( vars, "Type: %i", static_cast<uint32_t>( enctype ) );
        }
        E_CASE( KaxContentCompression, compr )
        {
            debug( vars, "Content Compression" );
            //Default compression type is 0 (Zlib)
            vars.tk->i_compression_type = MATROSKA_COMPRESSION_ZLIB;

            vars.level += 1;
            dispatcher.iterate( compr.begin(), compr.end(), &vars );
            vars.level -= 1;
        }
        E_CASE( KaxContentCompAlgo, compalg )
        {
            vars.tk->i_compression_type = static_cast<uint32_t>( compalg );
            debug( vars, "Compression Algorithm: %i", vars.tk->i_compression_type );
            if ( ( vars.tk->i_compression_type != MATROSKA_COMPRESSION_ZLIB ) &&
                 ( vars.tk->i_compression_type != MATROSKA_COMPRESSION_LZOX ) &&
                 ( vars.tk->i_compression_type != MATROSKA_COMPRESSION_HEADER ) )
            {
                msg_Err( vars.p_demuxer, "Track Compression method %d not supported", vars.tk->i_compression_type );
                vars.bSupported = false;
            }
        }
        E_CASE( KaxContentCompSettings, kccs )
        {
            debug( vars, "Compression Settings" );
            delete vars.tk->p_compression_data;
            vars.tk->p_compression_data = new KaxContentCompSettings( kccs );
        }
        E_CASE( KaxTrackVideo, tkv )
        {
            ONLY_FMT(VIDEO);
            debug( vars, "Track Video");

            mkv_track_t *tk = vars.tk;

            tk->f_fps = 0.0;

            if( tk->i_default_duration > 1000 ) /* Broken ffmpeg mux info when non set fps */
            {
                tk->fmt.video.i_frame_rate_base = static_cast<unsigned>( tk->i_default_duration );
                tk->fmt.video.i_frame_rate = 1000000;
            }

            vars.level += 1;
            dispatcher.iterate (tkv.begin (), tkv.end (), &vars );
            vars.level -= 1;

            unsigned int i_crop_top    = vars.track_video_info.i_crop_top;
            unsigned int i_crop_right  = vars.track_video_info.i_crop_right;
            unsigned int i_crop_bottom = vars.track_video_info.i_crop_bottom;
            unsigned int i_crop_left   = vars.track_video_info.i_crop_left;

            unsigned int i_display_unit   = vars.track_video_info.i_display_unit;
            unsigned int i_display_width  = vars.track_video_info.i_display_width;
            unsigned int i_display_height = vars.track_video_info.i_display_height;

            if (vars.track_video_info.chroma_sit_vertical || vars.track_video_info.chroma_sit_horizontal)
            {
                switch (vars.track_video_info.chroma_sit_vertical)
                {
                case 0: // unspecified
                case 2: // center
                    if (vars.track_video_info.chroma_sit_horizontal == 1) // left
                        tk->fmt.video.chroma_location = CHROMA_LOCATION_LEFT;
                    else if (vars.track_video_info.chroma_sit_horizontal == 2) // half
                        tk->fmt.video.chroma_location = CHROMA_LOCATION_CENTER;
                    else
                        debug( vars, "unsupported chroma Siting %u/%u",
                               vars.track_video_info.chroma_sit_horizontal,
                               vars.track_video_info.chroma_sit_vertical);
                    break;
                case 1: // top
                    if (vars.track_video_info.chroma_sit_horizontal == 1) // left
                        tk->fmt.video.chroma_location = CHROMA_LOCATION_TOP_LEFT;
                    else if (vars.track_video_info.chroma_sit_horizontal == 2) // half
                        tk->fmt.video.chroma_location = CHROMA_LOCATION_TOP_CENTER;
                    else
                        debug( vars, "unsupported chroma Siting %u/%u",
                               vars.track_video_info.chroma_sit_horizontal,
                               vars.track_video_info.chroma_sit_vertical);
                    break;
                }
            }

            unsigned int h_crop;
            if (add_overflow(i_crop_left, i_crop_right, &h_crop))
            {
                debug( vars, "invalid horizontal crop %u+%u",
                              i_crop_left, i_crop_right );
                i_crop_left = i_crop_right = 0;
            }
            else if (unlikely(h_crop >= tk->fmt.video.i_width))
            {
                debug( vars, "Horizontal crop (left=%u, right=%u) "
                             "greater than the picture width (%u)",
                              i_crop_left, i_crop_right, tk->fmt.video.i_width );
                i_crop_left = i_crop_right = 0;
            }

            unsigned int v_crop;
            if (add_overflow(i_crop_top, i_crop_bottom, &v_crop))
            {
                debug( vars, "invalid vertical crop %u+%u",
                              i_crop_top, i_crop_bottom);
                i_crop_top = i_crop_bottom = 0;
            }
            if (unlikely(v_crop >= tk->fmt.video.i_height))
            {
                debug( vars, "Vertical crop (top=%u, bottom=%u) "
                             "greater than the picture height (%u)",
                              i_crop_top, i_crop_bottom, tk->fmt.video.i_height );
                i_crop_top = i_crop_bottom = 0;
            }

            tk->fmt.video.i_x_offset      = i_crop_left;
            tk->fmt.video.i_y_offset      = i_crop_top;
            tk->fmt.video.i_visible_width = tk->fmt.video.i_width -
                                            (i_crop_left + i_crop_right);
            tk->fmt.video.i_visible_height = tk->fmt.video.i_height -
                                            (i_crop_top + i_crop_bottom);

            if (i_display_unit == 0 && i_display_width == 0)
                i_display_width = tk->fmt.video.i_width;
            if (i_display_unit == 0 && i_display_height == 0)
                i_display_height = tk->fmt.video.i_height;

            if (i_display_height && i_display_width)
            {
                switch (i_display_unit)
                {
                    case 0: // pixels
                    case 1: // centimeters
                    case 2: // inches
                        vlc_ureduce( &tk->fmt.video.i_sar_num, &tk->fmt.video.i_sar_den,
                                     i_display_width * tk->fmt.video.i_visible_height,
                                     i_display_height * tk->fmt.video.i_visible_width, 0);
                        break;
                    case 3: // display aspect ratio
                        vlc_ureduce( &tk->fmt.video.i_sar_den, &tk->fmt.video.i_sar_num,
                                     i_display_height * tk->fmt.video.i_visible_width,
                                     i_display_width * tk->fmt.video.i_visible_height, 0);
                        break;
                }
            }
        }
#if LIBMATROSKA_VERSION >= 0x010400
        E_CASE( KaxVideoAlphaMode, mode )
        {
            ONLY_FMT(VIDEO);
            debug( vars, "Track Video Alpha Mode %u", static_cast<uint8_t>( mode ) ) ;
            vars.tk->b_has_alpha = static_cast<uint8_t>( mode ) == 1;
        }
#endif
#if LIBMATROSKA_VERSION >= 0x010406
        E_CASE( KaxVideoProjection, proj )
        {
            ONLY_FMT(VIDEO);
            debug( vars, "Track Video Projection" ) ;

            vars.level += 1;
            dispatcher.iterate (proj.begin (), proj.end (), &vars );
            vars.level -= 1;
        }
        E_CASE( KaxVideoProjectionType, fint )
        {
            ONLY_FMT(VIDEO);
            switch (static_cast<uint8_t>( fint ))
            {
            case 0:
                vars.tk->fmt.video.projection_mode = PROJECTION_MODE_RECTANGULAR;
                break;
            case 1:
                vars.tk->fmt.video.projection_mode = PROJECTION_MODE_EQUIRECTANGULAR;
                break;
            case 2:
                vars.tk->fmt.video.projection_mode = PROJECTION_MODE_CUBEMAP_LAYOUT_STANDARD;
                break;
            default:
                debug( vars, "Track Video Projection %u not supported", static_cast<uint8_t>( fint ) ) ;
                break;
            }
        }
        E_CASE( KaxVideoProjectionPoseYaw, pose )
        {
            ONLY_FMT(VIDEO);
            vars.pose.yaw = static_cast<float>( pose );
        }
        E_CASE( KaxVideoProjectionPosePitch, pose )
        {
            ONLY_FMT(VIDEO);
            vars.pose.pitch = static_cast<float>( pose );
        }
        E_CASE( KaxVideoProjectionPoseRoll, pose )
        {
            ONLY_FMT(VIDEO);
            vars.pose.roll = static_cast<float>( pose );
        }
#endif
        E_CASE( KaxVideoFlagInterlaced, fint ) // UNUSED
        {
            ONLY_FMT(VIDEO);
            debug( vars, "Track Video Interlaced=%u", static_cast<uint8_t>( fint ) ) ;
        }
        E_CASE( KaxVideoStereoMode, stereo ) // UNUSED
        {
            ONLY_FMT(VIDEO);
            vars.tk->fmt.video.b_multiview_right_eye_first = false;
            switch (static_cast<uint8_t>( stereo ))
            {
            case 0: vars.tk->fmt.video.multiview_mode = MULTIVIEW_2D;         break;
            case 1: vars.tk->fmt.video.multiview_mode = MULTIVIEW_STEREO_SBS; break;
            case 2: vars.tk->fmt.video.multiview_mode = MULTIVIEW_STEREO_TB;  break;
            case 3: vars.tk->fmt.video.multiview_mode = MULTIVIEW_STEREO_TB;
                    vars.tk->fmt.video.b_multiview_right_eye_first = true;    break;
            case 4: vars.tk->fmt.video.multiview_mode = MULTIVIEW_STEREO_CHECKERBOARD;
                    vars.tk->fmt.video.b_multiview_right_eye_first = true;    break;
            case 5: vars.tk->fmt.video.multiview_mode = MULTIVIEW_STEREO_CHECKERBOARD; break;
            case 6: vars.tk->fmt.video.multiview_mode = MULTIVIEW_STEREO_ROW;
                    vars.tk->fmt.video.b_multiview_right_eye_first = true;    break;
            case 7: vars.tk->fmt.video.multiview_mode = MULTIVIEW_STEREO_ROW; break;
            case 8: vars.tk->fmt.video.multiview_mode = MULTIVIEW_STEREO_COL;
                    vars.tk->fmt.video.b_multiview_right_eye_first = true;    break;
            case 9: vars.tk->fmt.video.multiview_mode = MULTIVIEW_STEREO_COL; break;
            case 11: vars.tk->fmt.video.multiview_mode = MULTIVIEW_STEREO_SBS;
                     vars.tk->fmt.video.b_multiview_right_eye_first = true;    break;
            case 13: vars.tk->fmt.video.multiview_mode = MULTIVIEW_STEREO_FRAME; break;
            case 14: vars.tk->fmt.video.multiview_mode = MULTIVIEW_STEREO_FRAME;
                    vars.tk->fmt.video.b_multiview_right_eye_first = true;    break;
            default:
            case 10: case 12:
                debug( vars, " unsupported Stereo Mode=%u", static_cast<uint8_t>( stereo ) ) ;
            }
            debug( vars, "Track Video Stereo Mode=%u", static_cast<uint8_t>( stereo ) ) ;
        }
        E_CASE( KaxVideoPixelWidth, vwidth )
        {
            ONLY_FMT(VIDEO);
            vars.tk->fmt.video.i_width += static_cast<uint32_t>( vwidth );
            debug( vars, "width=%u", vars.tk->fmt.video.i_width );
        }
        E_CASE( KaxVideoPixelHeight, vheight )
        {
            ONLY_FMT(VIDEO);
            vars.tk->fmt.video.i_height += static_cast<uint32_t>( vheight );
            debug( vars, "height=%u", vars.tk->fmt.video.i_height );
        }
        E_CASE( KaxVideoDisplayWidth, vwidth )
        {
            ONLY_FMT(VIDEO);
            vars.track_video_info.i_display_width = static_cast<uint32_t>( vwidth );
            debug( vars, "display width=%u", vars.track_video_info.i_display_width );
        }
        E_CASE( KaxVideoDisplayHeight, vheight )
        {
            ONLY_FMT(VIDEO);
            vars.track_video_info.i_display_height = static_cast<uint32_t>( vheight );
            debug( vars, "display height=%u", vars.track_video_info.i_display_height );
        }
        E_CASE( KaxVideoPixelCropBottom, cropval )
        {
            ONLY_FMT(VIDEO);
            vars.track_video_info.i_crop_bottom = static_cast<uint32_t>( cropval );
            debug( vars, "crop pixel bottom=%u", vars.track_video_info.i_crop_bottom );
        }
        E_CASE( KaxVideoPixelCropTop, cropval )
        {
            ONLY_FMT(VIDEO);
            vars.track_video_info.i_crop_top = static_cast<uint32_t>( cropval );
            debug( vars, "crop pixel top=%u", vars.track_video_info.i_crop_top );
        }
        E_CASE( KaxVideoPixelCropRight, cropval )
        {
            ONLY_FMT(VIDEO);
            vars.track_video_info.i_crop_right = static_cast<uint32_t>( cropval );
            debug( vars, "crop pixel right=%u", vars.track_video_info.i_crop_right );
        }
        E_CASE( KaxVideoPixelCropLeft, cropval )
        {
            ONLY_FMT(VIDEO);
            vars.track_video_info.i_crop_left = static_cast<uint32_t>( cropval );
            debug( vars, "crop pixel left=%u", vars.track_video_info.i_crop_left );
        }
        E_CASE( KaxVideoDisplayUnit, vdmode )
        {
            ONLY_FMT(VIDEO);
            vars.track_video_info.i_display_unit = static_cast<uint8_t>( vdmode );
            const char *psz_unit;
            switch (vars.track_video_info.i_display_unit)
            {
            case 0:  psz_unit = "pixels"; break;
            case 1:  psz_unit = "centimeters"; break;
            case 2:  psz_unit = "inches"; break;
            case 3:  psz_unit = "dar"; break;
            default: psz_unit = "unknown"; break;
            }
            debug( vars, "Track Video Display Unit=%s", psz_unit );
        }
        E_CASE( KaxVideoAspectRatio, ratio ) // UNUSED
        {
            ONLY_FMT(VIDEO);
            debug( vars, "Track Video Aspect Ratio Type=%u", static_cast<uint8_t>( ratio ) ) ;
        }
        E_CASE( KaxVideoFrameRate, vfps )
        {
            ONLY_FMT(VIDEO);
            vars.tk->f_fps = std::max( static_cast<float>(vfps) , 1.0f );
            debug( vars, "fps=%f", vars.tk->f_fps );
        }
        E_CASE( KaxVideoColourSpace, colourspace )
        {
            ONLY_FMT(VIDEO);
            if ( colourspace.ValidateSize() )
            {
                vars.tk->uncompressed_fourcc = GetFOURCC( colourspace.GetBuffer() );
                debug( vars, "Colour Space=%4.4s", (const char*)&vars.tk->uncompressed_fourcc );
            }
        }
#if LIBMATROSKA_VERSION >= 0x010405
        E_CASE( KaxVideoColour, colours)
        {
            debug( vars, "Video Colors");
            if (vars.tk->fmt.i_cat != VIDEO_ES ) {
                msg_Err( vars.p_demuxer, "Video colors elements not allowed for this track" );
                return;
            }
            vars.level += 1;
            dispatcher.iterate (colours.begin (), colours.end (), &vars );
            vars.level -= 1;
        }
        E_CASE( KaxVideoColourRange, range )
        {
            ONLY_FMT(VIDEO);
            const char *name = nullptr;
            switch( static_cast<uint8_t>(range) )
            {
            case 1:
                vars.tk->fmt.video.color_range = COLOR_RANGE_LIMITED;
                name ="limited";
                break;
            case 2:
                vars.tk->fmt.video.color_range = COLOR_RANGE_FULL;
                name ="full";
                break;
            case 3: // Matrix coefficients + Transfer characteristics
            default:
                debug( vars, "Unsupported Colour Range=%d", static_cast<uint8_t>(range) );
            }
            if (name != nullptr) debug( vars, "Range=%s", name );
        }
        E_CASE( KaxVideoColourTransferCharacter, transfer )
        {
            ONLY_FMT(VIDEO);
            vars.tk->fmt.video.transfer = iso_23001_transfer_to_vlc( static_cast<uint8_t>(transfer) );
            const char *name = nullptr;
            switch( static_cast<uint8_t>(transfer) )
            {
            case 1: name = "BT-709";
                break;
            case 4: name = "BT.470BM";
                break;
            case 5: name = "BT.470BG";
                break;
            case 6: name = "SMPTE 170M";
                break;
            case 7: name = "SMPTE 240M";
                break;
            case 8: name = "linear";
                break;
            case 13: name = "sRGB/sYCC";
                break;
            case 16: name = "BT.2100 PQ";
                break;
            case 18: name = "HLG";
                break;
            case 9:  // Log
            case 10: // Log SQRT
            case 11: // IEC 61966-2-4
            case 12: // ITU-R BT.1361 Extended Colour Gamut
            case 14: // ITU-R BT.2020 10 bit
            case 15: // ITU-R BT.2020 12 bit
            case 17: // SMPTE ST 428-1
            default:
                break;
            }
            if (vars.tk->fmt.video.transfer == TRANSFER_FUNC_UNDEF)
                debug( vars, "Unsupported Colour Transfer=%d", static_cast<uint8_t>(transfer) );
            else if (name == nullptr)
                debug( vars, "Colour Transfer=%d", static_cast<uint8_t>(transfer) );
            else
                debug( vars, "Colour Transfer=%s", name );
        }
        E_CASE( KaxVideoColourPrimaries, primaries )
        {
            ONLY_FMT(VIDEO);
            vars.tk->fmt.video.primaries = iso_23001_primaries_to_vlc( static_cast<uint8_t>(primaries) );
            const char *name = nullptr;
            switch( static_cast<uint8_t>(primaries) )
            {
            case 1: name = "BT-709";
                break;
            case 4: name = "BT.470M";
                break;
            case 5: name = "BT.470BG";
                break;
            case 6: name = "SMPTE 170M";
                break;
            case 7: name = "SMPTE 240M";
                break;
            case 9: name = "BT.2020";
                break;
            case 8:  // FILM
            case 10: // SMPTE ST 428-1
            case 22: // JEDEC P22 phosphors
            default:
                break;
            }
            if (vars.tk->fmt.video.primaries == COLOR_PRIMARIES_UNDEF)
                debug( vars, "Unsupported Colour Primaries=%d", static_cast<uint8_t>(primaries) );
            else if (name == nullptr)
                debug( vars, "Colour Primaries=%" PRIu8, static_cast<uint8_t>(primaries) );
            else
                debug( vars, "Colour Primaries=%s", name );
        }
        E_CASE( KaxVideoColourMatrix, matrix )
        {
            ONLY_FMT(VIDEO);
            vars.tk->fmt.video.space = iso_23001_matrix_coeffs_to_vlc( static_cast<uint8_t>(matrix) );
            const char *name = nullptr;
            switch( static_cast<uint8_t>(matrix) )
            {
            case 1: name = "BT-709";
                break;
            case 6: name = "SMPTE 170M";
                break;
            case 7: name = "SMPTE 240M";
                break;
            case 9: name = "BT.2020 Non-constant Luminance";
                break;
            case 10: name = "BT.2020 Constant Luminance";
                break;
            case 0: // Identity
            case 2: // unspecified
            case 3: // reserved
            case 4: // US FCC 73.682
            case 5: // ITU-R BT.470BG
            case 8: // YCoCg
            case 11: // SMPTE ST 2085
            case 12: // Chroma-derived Non-constant Luminance
            case 13: // Chroma-derived Constant Luminance
            case 14: // ITU-R BT.2100
            default:
                break;
            }
            if (vars.tk->fmt.video.space == COLOR_SPACE_UNDEF)
                debug( vars, "Unsupported Colour Matrix=%d", static_cast<uint8_t>(matrix) );
            else if (name == nullptr)
                debug( vars, "Colour Matrix=%d", static_cast<uint8_t>(matrix) );
            else
                debug( vars, "Colour Matrix=%s", name );
        }
        E_CASE( KaxVideoChromaSitHorz, chroma_hor )
        {
            ONLY_FMT(VIDEO);
            const char *name = nullptr;
            vars.track_video_info.chroma_sit_horizontal = static_cast<uint8_t>(chroma_hor);
            switch( static_cast<uint8_t>(chroma_hor) )
            {
            case 0: name = "unspecified"; break;
            case 1: name = "left";        break;
            case 2: name = "center";      break;
            default:
                debug( vars, "Unsupported Horizontal Chroma Siting=%d", static_cast<uint8_t>(chroma_hor) );
            }
            if (name != nullptr) debug( vars, "Chroma Siting Horizontal=%s", name);
        }
        E_CASE( KaxVideoChromaSitVert, chroma_ver )
        {
            ONLY_FMT(VIDEO);
            const char *name = nullptr;
            vars.track_video_info.chroma_sit_vertical = static_cast<uint8_t>(chroma_ver);
            switch( static_cast<uint8_t>(chroma_ver) )
            {
            case 0: name = "unspecified"; break;
            case 1: name = "left";        break;
            case 2: name = "center";      break;
            default:
                debug( vars, "Unsupported Vertical Chroma Siting=%d", static_cast<uint8_t>(chroma_ver) );
            }
            if (name != nullptr) debug( vars, "Chroma Siting Vertical=%s", name);
        }
        E_CASE( KaxVideoColourMaxCLL, maxCLL )
        {
            ONLY_FMT(VIDEO);
            debug( vars, "Video Max Pixel Brightness");
            vars.tk->fmt.video.lighting.MaxCLL = static_cast<uint16_t>(maxCLL);
        }
        E_CASE( KaxVideoColourMaxFALL, maxFALL )
        {
            ONLY_FMT(VIDEO);
            debug( vars, "Video Max Frame Brightness");
            vars.tk->fmt.video.lighting.MaxFALL = static_cast<uint16_t>(maxFALL);
        }
        E_CASE( KaxVideoColourMasterMeta, mastering )
        {
            debug( vars, "Video Mastering Metadata");
            if (vars.tk->fmt.i_cat != VIDEO_ES ) {
                msg_Err( vars.p_demuxer, "Video metadata elements not allowed for this track" );
                return;
            }
            vars.level += 1;
            dispatcher.iterate (mastering.begin (), mastering.end (), &vars );
            vars.level -= 1;
        }
        E_CASE( KaxVideoLuminanceMax, maxLum )
        {
            ONLY_FMT(VIDEO);
            debug( vars, "Video Luminance Max");
            vars.tk->fmt.video.mastering.max_luminance = static_cast<float>(maxLum) * 10000.f;
        }
        E_CASE( KaxVideoLuminanceMin, minLum )
        {
            ONLY_FMT(VIDEO);
            debug( vars, "Video Luminance Min");
            vars.tk->fmt.video.mastering.min_luminance = static_cast<float>(minLum) * 10000.f;
        }
        E_CASE( KaxVideoGChromaX, chroma )
        {
            ONLY_FMT(VIDEO);
            debug( vars, "Video Green Chroma X");
            vars.tk->fmt.video.mastering.primaries[0] = static_cast<float>(chroma) * 50000.f;
        }
        E_CASE( KaxVideoGChromaY, chroma )
        {
            ONLY_FMT(VIDEO);
            debug( vars, "Video Green Chroma Y");
            vars.tk->fmt.video.mastering.primaries[1] = static_cast<float>(chroma) * 50000.f;
        }
        E_CASE( KaxVideoBChromaX, chroma )
        {
            ONLY_FMT(VIDEO);
            debug( vars, "Video Blue Chroma X");
            vars.tk->fmt.video.mastering.primaries[2] = static_cast<float>(chroma) * 50000.f;
        }
        E_CASE( KaxVideoBChromaY, chroma )
        {
            ONLY_FMT(VIDEO);
            debug( vars, "Video Blue Chroma Y");
            vars.tk->fmt.video.mastering.primaries[3] = static_cast<float>(chroma) * 50000.f;
        }
        E_CASE( KaxVideoRChromaX, chroma )
        {
            ONLY_FMT(VIDEO);
            debug( vars, "Video Red Chroma X");
            vars.tk->fmt.video.mastering.primaries[4] = static_cast<float>(chroma) * 50000.f;
        }
        E_CASE( KaxVideoRChromaY, chroma )
        {
            ONLY_FMT(VIDEO);
            debug( vars, "Video Red Chroma Y");
            vars.tk->fmt.video.mastering.primaries[5] = static_cast<float>(chroma) * 50000.f;
        }
        E_CASE( KaxVideoWhitePointChromaX, white )
        {
            ONLY_FMT(VIDEO);
            debug( vars, "Video WhitePoint X");
            vars.tk->fmt.video.mastering.white_point[0] = static_cast<float>(white) * 50000.f;
        }
        E_CASE( KaxVideoWhitePointChromaY, white )
        {
            ONLY_FMT(VIDEO);
            debug( vars, "Video WhitePoint Y");
            vars.tk->fmt.video.mastering.white_point[1] = static_cast<float>(white) * 50000.f;
        }
#endif
        E_CASE( KaxTrackAudio, tka ) {
            ONLY_FMT(AUDIO);
            debug( vars, "Track Audio");
            vars.level += 1;
            dispatcher.iterate( tka.begin(), tka.end(), &vars );
            vars.level -= 1;
        }
        E_CASE( KaxAudioSamplingFreq, afreq )
        {
            ONLY_FMT(AUDIO);
            float const value = static_cast<float>( afreq );

            vars.tk->i_original_rate  = value;
            vars.tk->fmt.audio.i_rate = value;

            debug( vars, "afreq=%d", vars.tk->fmt.audio.i_rate ) ;
        }
        E_CASE( KaxAudioOutputSamplingFreq, afreq )
        {
            ONLY_FMT(AUDIO);
            vars.tk->fmt.audio.i_rate = static_cast<float>( afreq );
            debug( vars, "aoutfreq=%d", vars.tk->fmt.audio.i_rate ) ;
        }
        E_CASE( KaxAudioChannels, achan )
        {
            ONLY_FMT(AUDIO);
            vars.tk->fmt.audio.i_channels = static_cast<uint8_t>( achan );
            debug( vars, "achan=%u", vars.tk->fmt.audio.i_channels );
        }
        E_CASE( KaxAudioBitDepth, abits )
        {
            ONLY_FMT(AUDIO);
            vars.tk->fmt.audio.i_bitspersample = static_cast<uint8_t>( abits );
            debug( vars, "abits=%u", vars.tk->fmt.audio.i_bitspersample);
        }
        E_CASE ( EbmlVoid, ) {
          VLC_UNUSED( vars );
        }
        E_CASE_DEFAULT(element) {
            debug( vars, "Unknown (%s)", EBML_NAME(&element) );
        }
    };

    MetaDataHandlers::Dispatcher().iterate ( m->begin(), m->end(), &metadata_payload );

    if( p_track->i_number == 0 )
    {
        msg_Warn( &sys.demuxer, "Missing KaxTrackNumber, discarding track!" );
        delete p_track;
        return;
    }

    if ( bSupported )
    {
        auto *iso_lang = vlc_find_iso639(metadata_payload.lang.c_str(), false);
        if (iso_lang == nullptr)
            msg_Dbg( &sys.demuxer, "Unknown language '%s'!", metadata_payload.lang.c_str() );
        free(p_track->fmt.psz_language);
        p_track->fmt.psz_language = strdup(metadata_payload.lang.c_str());

        vlc_viewpoint_from_euler( &p_track->fmt.video.pose,
                                  metadata_payload.pose.yaw,
                                  metadata_payload.pose.pitch,
                                  metadata_payload.pose.roll);

#ifdef HAVE_ZLIB
        if( p_track->i_compression_type == MATROSKA_COMPRESSION_ZLIB &&
            p_track->i_encoding_scope & MATROSKA_ENCODING_SCOPE_PRIVATE &&
            p_track->i_extra_data && p_track->p_extra_data &&
            zlib_decompress_extra( &sys.demuxer, *p_track ) )
        {
            msg_Err(&sys.demuxer, "Couldn't handle the track %u compression", p_track->i_number );
            delete p_track;
            return;
        }
#endif
        if( p_track->i_compression_type == MATROSKA_COMPRESSION_LZOX &&
            p_track->i_encoding_scope & MATROSKA_ENCODING_SCOPE_PRIVATE &&
            p_track->i_extra_data && p_track->p_extra_data &&
            !lzo1x_decompress_extra( &sys.demuxer, *p_track ) )
        {
            msg_Err(&sys.demuxer, "Couldn't handle the track %u compression", p_track->i_number );
            delete p_track;
            return;
        }
        if( p_track->i_compression_type == MATROSKA_COMPRESSION_HEADER &&
            p_track->p_compression_data == NULL )
        {
            msg_Err(&sys.demuxer, "Track %u header compression missing header data", p_track->i_number );
            delete p_track;
            return;
        }

        if( !TrackInit( p_track ) )
        {
            msg_Err(&sys.demuxer, "Couldn't init track %u", p_track->i_number );
            delete p_track;
            return;
        }

        tracks.insert( std::make_pair( p_track->i_number, std::unique_ptr<mkv_track_t>(p_track) ) ); // TODO: add warning if two tracks have the same key
    }
    else
    {
        msg_Err( &sys.demuxer, "Track Entry %u not supported", p_track->i_number );
        delete p_track;
    }
}

#undef ONLY_FMT

/*****************************************************************************
 * ParseTracks:
 *****************************************************************************/

void matroska_segment_c::ParseTracks( KaxTracks *tracks )
{
    /* Master elements */
    if ( !ReadMaster( *tracks ) )
        return;

    struct Capture {
      matroska_segment_c * obj;
      demux_t            * p_demuxer;

    } payload = {
      this, &sys.demuxer
    };

    MKV_SWITCH_CREATE( EbmlTypeDispatcher, TrackHandlers, struct Capture )
    {
        MKV_SWITCH_INIT();

        E_CASE( KaxTrackEntry, track_number ) {
            vars.obj->ParseTrackEntry( &track_number );
        }
        E_CASE( EbmlVoid, ) {
            VLC_UNUSED( vars );
        }
        E_CASE_DEFAULT(element) {
            MkvTree( *vars.p_demuxer, 2, "Unknown (%s)", EBML_NAME(&element) );
        }
    };

    TrackHandlers::Dispatcher().iterate( tracks->begin(), tracks->end(), &payload );

    for (auto &track : matroska_segment_c::tracks)
    {
        pcr_shift = std::max(pcr_shift, track.second->i_codec_delay);
    }
}

/*****************************************************************************
 * ParseInfo:
 *****************************************************************************/

void matroska_segment_c::ParseInfo( KaxInfo *info )
{
    EbmlMaster  *m = info;

    if ( !ReadMaster( *info ) )
        return;

    struct InfoHandlerPayload {
        demux_t            * p_demuxer;
        matroska_segment_c * obj;
        EbmlMaster        *&   m;
        double             f_duration;

    } captures = { &sys.demuxer, this, m, -1. };

    MKV_SWITCH_CREATE(EbmlTypeDispatcher, InfoHandlers, InfoHandlerPayload)
    {
        MKV_SWITCH_INIT();

        static void debug (InfoHandlerPayload& vars, char const * fmt, ...) VLC_FORMAT( 2, 3 )
        {
            va_list args; va_start( args, fmt );
            MkvTree_va( *vars.p_demuxer, 2, fmt, args);
            va_end( args );
        }
        E_CASE( KaxSegmentUID, uid )
        {
            if ( vars.obj->p_segment_uid == NULL )
            {
                vars.obj->p_segment_uid = new KaxSegmentUID( uid );
            }
            debug( vars, "UID=%" PRIx64, *reinterpret_cast<uint64_t*>( vars.obj->p_segment_uid->GetBuffer() ) );
        }
        E_CASE( KaxPrevUID, uid )
        {
            if ( vars.obj->p_prev_segment_uid == NULL )
            {
                vars.obj->p_prev_segment_uid = new KaxPrevUID( uid );
                vars.obj->b_ref_external_segments = true;
            }
            debug( vars, "PrevUID=%" PRIx64, *reinterpret_cast<uint64_t*>( vars.obj->p_prev_segment_uid->GetBuffer() ) );
        }
        E_CASE( KaxNextUID, uid )
        {
            if ( vars.obj->p_next_segment_uid == NULL )
            {
                vars.obj->p_next_segment_uid = new KaxNextUID( uid );
                vars.obj->b_ref_external_segments = true;
            }
            debug( vars, "NextUID=%" PRIx64, *reinterpret_cast<uint64_t*>( vars.obj->p_next_segment_uid->GetBuffer() ) );
        }
        E_CASE( KaxTimestampScale, tcs )
        {
            vars.obj->i_timescale = static_cast<uint64_t>( tcs );
            debug( vars, "TimecodeScale=%" PRId64, vars.obj->i_timescale );
        }
        E_CASE( KaxDuration, dur )
        {
            vars.f_duration = static_cast<double>( dur );
            debug( vars, "Duration=%.0f", vars.f_duration );
        }
        E_CASE( KaxMuxingApp, mapp )
        {
            free(vars.obj->psz_muxing_application);
            vars.obj->psz_muxing_application = ToUTF8( UTFstring( mapp ) );
            debug( vars, "Muxing Application=%s", vars.obj->psz_muxing_application );
        }
        E_CASE( KaxWritingApp, wapp )
        {
            free(vars.obj->psz_writing_application);
            vars.obj->psz_writing_application = ToUTF8( UTFstring( wapp ) );
            debug( vars, "Writing Application=%s", vars.obj->psz_writing_application );
        }
        E_CASE( KaxSegmentFilename, sfn )
        {
            debug( vars, "Segment Filename=%s", ToUTF8Const( UTFstring( sfn ) ) );
        }
        E_CASE( KaxTitle, title )
        {
            free(vars.obj->psz_title);
            vars.obj->psz_title = ToUTF8( UTFstring( title ) );
            debug( vars, "Title=%s", vars.obj->psz_title );
        }
        E_CASE( KaxSegmentFamily, uid )
        {
            vars.obj->families.push_back( new KaxSegmentFamily(uid) );
            debug( vars, "Family=%" PRIx64, *reinterpret_cast<uint64_t*>( uid.GetBuffer() ) );
        }
        E_CASE( KaxDateUTC, date )
        {
            struct tm tmres;
            char   buffer[25];
            time_t i_date = date.GetEpochDate();

            if( gmtime_r( &i_date, &tmres ) &&
                strftime( buffer, sizeof(buffer), "%a %b %d %H:%M:%S %Y",
                          &tmres ) )
            {
                debug( vars, "Date=%s", buffer );
            }
        }
        E_CASE( KaxChapterTranslate, trans )
        {
            MKV_SWITCH_CREATE( EbmlTypeDispatcher, TranslationHandler, chapter_translation_c* )
            {
                MKV_SWITCH_INIT();

                E_CASE( KaxChapterTranslateEditionUID, uid )
                {
                    vars->editions.push_back( static_cast<uint64_t>( uid ) );
                }
                E_CASE( KaxChapterTranslateCodec, codec_id )
                {
                    vars->codec_id = static_cast<uint32_t>( codec_id );
                }
                E_CASE( KaxChapterTranslateID, translated_id )
                {
                    delete vars->p_translated;
                    vars->p_translated = new KaxChapterTranslateID( translated_id );
                }
            };
            try
            {
                if ( !vars.obj->ReadMaster( trans ) )
                    return;

                chapter_translation_c *p_translate = new chapter_translation_c();

                TranslationHandler::Dispatcher().iterate(
                    trans.begin(), trans.end(), &p_translate
                );

                if (p_translate->isValid())
                    vars.obj->translations.push_back( p_translate );
                else
                    delete p_translate;
            }
            catch(...)
            {
                msg_Err( vars.p_demuxer, "Error while reading Chapter Translate");
            }
        }
        E_CASE( EbmlVoid, )
        {
            VLC_UNUSED( vars );
        }
        E_CASE_DEFAULT(element)
        {
            debug( vars, "Unknown (%s)", EBML_NAME(&element) );
        }
    };

    InfoHandlers::Dispatcher().iterate( m->begin(), m->end(), &captures );

    if( captures.f_duration != -1. )
        i_duration = VLC_TICK_FROM_NS( captures.f_duration * i_timescale );
}


/*****************************************************************************
 * ParseChapterAtom
 *****************************************************************************/

void matroska_segment_c::ParseChapterAtom( int i_level, KaxChapterAtom *ca, chapter_item_c & chapters )
{
    MkvTree( sys.demuxer, 3, "ChapterAtom (level=%d)", i_level );

    struct ChapterPayload {
        matroska_segment_c * const       obj;
        demux_t            * const p_demuxer;
        chapter_item_c     &        chapters;

        int& i_level;
        int level;

    } payload = {
        this, &sys.demuxer, chapters,
        i_level, 4
    };

    MKV_SWITCH_CREATE( EbmlTypeDispatcher, ChapterAtomHandlers, ChapterPayload )
    {
        MKV_SWITCH_INIT();

        static void debug (ChapterPayload const& vars, char const * fmt, ...) VLC_FORMAT( 2, 3 )
        {
            va_list args; va_start( args, fmt );
            MkvTree_va( *vars.p_demuxer, vars.level, fmt, args);
            va_end( args );
        }
        E_CASE( KaxChapterUID, uid )
        {
            vars.chapters.i_uid = static_cast<uint64_t>( uid );
            debug( vars, "ChapterUID=%" PRIx64, vars.chapters.i_uid );
        }
        E_CASE( KaxChapterFlagHidden, flag )
        {
            vars.chapters.b_display_seekpoint = static_cast<uint8_t>( flag ) == 0;
            debug( vars, "ChapterFlagHidden=%s", vars.chapters.b_display_seekpoint ? "no" : "yes" );
        }
        E_CASE( KaxChapterSegmentUID, uid )
        {
            delete vars.chapters.p_segment_uid;
            vars.chapters.p_segment_uid = new KaxChapterSegmentUID( uid );
            vars.obj->b_ref_external_segments = true;

            debug( vars, "ChapterSegmentUID=%" PRIx64, *reinterpret_cast<uint64_t*>( vars.chapters.p_segment_uid->GetBuffer() ) );
        }
        E_CASE( KaxChapterSegmentEditionUID, euid )
        {
            delete vars.chapters.p_segment_edition_uid;
            vars.chapters.p_segment_edition_uid = new KaxChapterSegmentEditionUID( euid );

            debug( vars, "ChapterSegmentEditionUID=%x",
#if LIBMATROSKA_VERSION < 0x010300
              *reinterpret_cast<uint32_t*>( vars.chapters.p_segment_edition_uid->GetBuffer() )
#else
              static_cast<uint32_t>( *vars.chapters.p_segment_edition_uid )
#endif
            );
        }
        E_CASE( KaxChapterTimeStart, start )
        {
            vars.chapters.i_start_time = VLC_TICK_FROM_NS(static_cast<uint64_t>( start ));
            debug( vars, "ChapterTimeStart=%" PRId64, vars.chapters.i_start_time );
        }
        E_CASE( KaxChapterTimeEnd, end )
        {
            vars.chapters.i_end_time = VLC_TICK_FROM_NS(static_cast<uint64_t>( end ));
            debug( vars, "ChapterTimeEnd=%" PRId64, *vars.chapters.i_end_time );
        }
        E_CASE( KaxChapterDisplay, chapter_display )
        {
            debug( vars, "ChapterDisplay" );

            vars.level += 1;
            dispatcher.iterate( chapter_display.begin(), chapter_display.end(), &vars );
            vars.level -= 1;
        }
        E_CASE( KaxChapterString, name )
        {
            std::string str_name( UTFstring( name ).GetUTF8() );

            for ( int k = 0; k < vars.i_level; k++)
                vars.chapters.str_name += '+';

            vars.chapters.str_name += ' ';
            vars.chapters.str_name += str_name;
            vars.chapters.b_user_display = true;

            debug( vars, "ChapterString=%s", str_name.c_str() );
        }
        E_CASE( KaxChapterLanguage, lang )
        {
            debug( vars, "ChapterLanguage=%s", static_cast<std::string const&>( lang ).c_str() );
        }
        E_CASE( KaxChapterCountry, ct )
        {
            debug( vars, "ChapterCountry=%s", static_cast<std::string const&>( ct ).c_str() );
        }

        E_CASE( KaxChapterProcess, cp )
        {
            debug( vars, "ChapterProcess" );

            if ( !var_InheritBool( vars.p_demuxer, "mkv-use-chapter-codec") )
                return;

            chapter_codec_cmds_c *p_ccodec = NULL;

            for (auto proc : cp)
            {
                if( MKV_CHECKED_PTR_DECL_CONST( p_codec_id, KaxChapterProcessCodecID, proc ) )
                {
                    if ( p_codec_id->GetValue() == MATROSKA_CHAPTER_CODEC_NATIVE )
                    {
                       auto interpreter = vars.obj->sys.GetMatroskaScriptInterpreter();
                       if (unlikely(interpreter == nullptr))
                            debug( vars, "failed to get the Matroska Script interpreter ");
                       else
                            p_ccodec = new matroska_script_codec_c(
                            vlc_object_logger( &vars.obj->sys.demuxer ),
                            vars.obj->sys, *interpreter
                            );
                    }
                    else if ( p_codec_id->GetValue() == MATROSKA_CHAPTER_CODEC_DVD )
                    {
                        auto interepreter = vars.obj->sys.GetDVDInterpretor();
                        if (unlikely(interepreter == nullptr))
                            debug( vars, "failed to get the DVD interpreter ");
                        else
                            p_ccodec = new dvd_chapter_codec_c( vlc_object_logger( &vars.obj->sys.demuxer ), vars.obj->sys, *interepreter );
                    }
                    break;
                }
            }

            if ( p_ccodec != NULL )
            {
                for (auto k : cp)
                {
                    if( MKV_CHECKED_PTR_DECL_CONST( p_private, KaxChapterProcessPrivate, k ) )
                    {
                        p_ccodec->SetPrivate( *p_private );
                    }
                    else if ( MKV_CHECKED_PTR_DECL_CONST( cmd, KaxChapterProcessCommand, k ) )
                    {
                        p_ccodec->AddCommand( *cmd );
                    }
                }
                vars.chapters.codecs.push_back( p_ccodec );
            }
        }
        E_CASE( KaxChapterAtom, atom )
        {
            chapter_item_c *new_sub_chapter = new chapter_item_c();
            new_sub_chapter->p_parent = &vars.chapters;

            vars.obj->ParseChapterAtom( vars.i_level+1, &atom, *new_sub_chapter );
            vars.chapters.sub_chapters.push_back( new_sub_chapter );
        }
    };

    ChapterAtomHandlers::Dispatcher().iterate( ca->begin(), ca->end(), &payload );
}

/*****************************************************************************
 * ParseAttachments:
 *****************************************************************************/

void matroska_segment_c::ParseAttachments( KaxAttachments *attachments )
{
    if ( !ReadMaster( *attachments ))
        return;

    KaxAttached *attachedFile = FindChild<KaxAttached>( *attachments );

    while( attachedFile )
    {
        if (attachedFile->GetSize() > 0 )
        try {
            KaxFileData  &img_data     = GetMandatoryChild<KaxFileData>( *attachedFile );
            std::string attached_filename( UTFstring( GetMandatoryChild<KaxFileName>( *attachedFile ) ).GetUTF8() );
            auto new_attachment = vlc_input_attachment_New( attached_filename.c_str(),
                                                            GetMandatoryChild<KaxMimeType>( *attachedFile ).GetValue().c_str(),
                                                            nullptr,
                                                            img_data.GetBuffer(),
                                                            img_data.GetSize() );
            if (new_attachment)
            {
                msg_Dbg( &sys.demuxer, "|   |   - %s (%s)", new_attachment->psz_name,
                        new_attachment->psz_mime );

                if( !strncmp( new_attachment->psz_mime, "image/", 6 ) )
                {
                    char *psz_url;
                    if( asprintf( &psz_url, "attachment://%s",
                                new_attachment->psz_name ) >= 0 )
                    {
                        if( !sys.meta )
                            sys.meta = vlc_meta_New();
                        vlc_meta_SetArtURL( sys.meta, psz_url );
                        free( psz_url );
                    }
                }
                sys.stored_attachments.push_back( vlc::wrap_cptr( new_attachment,
                                                                &vlc_input_attachment_Release ) );
            }
        } catch (const MissingMandatory & err) {
            msg_Dbg( &sys.demuxer, "%s", err.what());
        }
        attachedFile = FindNextChild<KaxAttached>( *attachments, *attachedFile );
    }
}

/*****************************************************************************
 * ParseChapters:
 *****************************************************************************/

void matroska_segment_c::ParseChapters( KaxChapters *chapters )
{
    if ( !ReadMaster( *chapters ) )
        return;

    MKV_SWITCH_CREATE( EbmlTypeDispatcher, KaxChapterHandler, matroska_segment_c )
    {
        MKV_SWITCH_INIT();
        E_CASE( KaxEditionEntry, entry )
        {
            struct EditionPayload {
                matroska_segment_c * const obj;
                demux_t            * const p_demuxer;
                chapter_edition_c  * const p_edition;

            } data = { &vars, &vars.sys.demuxer, new chapter_edition_c };

            MKV_SWITCH_CREATE( EbmlTypeDispatcher, KaxEditionHandler, EditionPayload )
            {
                MKV_SWITCH_INIT();
                E_CASE( KaxChapterAtom, chapter_atom )
                {
                    chapter_item_c *new_sub_chapter = new chapter_item_c();
                    vars.obj->ParseChapterAtom( 0, &chapter_atom, *new_sub_chapter );
                    vars.p_edition->sub_chapters.push_back( new_sub_chapter );
                }
                E_CASE( KaxEditionUID, euid )
                {
                    vars.p_edition->i_uid = static_cast<uint64_t> ( euid );
                }
                E_CASE( KaxEditionFlagOrdered, flag_ordered )
                {
                    vars.p_edition->b_ordered = var_InheritBool(vars.p_demuxer, "mkv-use-ordered-chapters") && static_cast<uint8_t>( flag_ordered );
                }
                E_CASE( KaxEditionFlagDefault, flag_default )
                {
                    if( static_cast<uint8_t>( flag_default ) )
                        vars.obj->i_default_edition = vars.obj->stored_editions.size();
                }
                E_CASE( KaxEditionFlagHidden, flag_hidden )
                {
                    vars.p_edition->b_hidden = static_cast<uint8_t>( flag_hidden ) != 0;
                }
                E_CASE( EbmlVoid, el )
                {
                    VLC_UNUSED( el );
                    VLC_UNUSED( vars );
                }
                E_CASE_DEFAULT( el )
                {
                    msg_Dbg( vars.p_demuxer, "|   |   |   + Unknown (%s)", EBML_NAME(&el) );
                }
            };
            KaxEditionHandler::Dispatcher().iterate( entry.begin(), entry.end(), &data );

            data.obj->stored_editions.push_back( data.p_edition );
        }
        E_CASE( EbmlVoid, el ) {
            VLC_UNUSED( el );
            VLC_UNUSED( vars );
        }
        E_CASE_DEFAULT( el )
        {
            msg_Dbg( &vars.sys.demuxer, "|   |   + Unknown (%s)", EBML_NAME(&el) );
        }
    };

    KaxChapterHandler::Dispatcher().iterate( chapters->begin(), chapters->end(), this );
}

bool matroska_segment_c::ParseCluster( KaxCluster *cluster, bool b_update_start_time, ScopeMode read_fully )
{
    if (!sys.b_seekable)
        return false;

    if ( !ReadMaster( *cluster, read_fully ) )
        return false;

    auto *p_ctc = FindChild<KaxClusterTimestamp>(*cluster);
    if( p_ctc == nullptr )
    {
        msg_Err( &sys.demuxer, "Detected cluster without mandatory timecode" );
        return false;
    }

    cluster->InitTimestamp( static_cast<uint64_t>( *p_ctc ), i_timescale );
    _seeker.add_cluster( cluster );

    if( b_update_start_time )
        i_mk_start_time = VLC_TICK_FROM_NS( cluster->GlobalTimestamp() );

    return true;
}

#define ONLY_FMT(t) if(vars.p_tk->fmt.i_cat != t ## _ES) \
    throw std::runtime_error( "Mismatching track type" );

bool matroska_segment_c::TrackInit( mkv_track_t * p_tk )
{
    if( p_tk->codec.empty() )
    {
        msg_Err( &sys.demuxer, "Empty codec id" );
        p_tk->fmt.i_codec = VLC_CODEC_UNKNOWN;
        return true;
    }

    struct HandlerPayload {
        matroska_segment_c * obj;
        mkv_track_t        * p_tk;
        es_format_t        * p_fmt;
        demux_t            * p_demuxer;
    } captures = {
        this, p_tk, &p_tk->fmt, &sys.demuxer
    };

    MKV_SWITCH_CREATE( StringDispatcher, TrackCodecHandlers, HandlerPayload )
    {
        MKV_SWITCH_INIT();

        S_CASE("V_MS/VFW/FOURCC") {
            if( vars.p_tk->i_extra_data < (int)sizeof( VLC_BITMAPINFOHEADER ) )
            {
                msg_Err(vars.p_demuxer, "missing/invalid VLC_BITMAPINFOHEADER" );
                vars.p_fmt->i_codec = VLC_CODEC_UNKNOWN;
            }
            else
            {
                ONLY_FMT(VIDEO);
                VLC_BITMAPINFOHEADER *p_bih = (VLC_BITMAPINFOHEADER*)vars.p_tk->p_extra_data;

                vars.p_fmt->video.i_width = GetDWLE( &p_bih->biWidth );
                vars.p_fmt->video.i_height= GetDWLE( &p_bih->biHeight );
                vars.p_fmt->i_codec       = GetFOURCC( &p_bih->biCompression );

                /* Very unlikely yet possible: bug #5659*/
                const size_t min_extra = std::min(static_cast<size_t>(GetDWLE(&p_bih->biSize)),
                                                  vars.p_tk->i_extra_data);
                if ( min_extra > sizeof( VLC_BITMAPINFOHEADER ))
                {
                    vars.p_fmt->i_extra = min_extra - sizeof( VLC_BITMAPINFOHEADER );
                    vars.p_fmt->p_extra = xmalloc( vars.p_fmt->i_extra );
                    if (likely(vars.p_fmt->p_extra != NULL))
                        memcpy( vars.p_fmt->p_extra, &p_bih[1], vars.p_fmt->i_extra );
                    else
                        vars.p_fmt->i_extra = 0;
                }
                else if( vars.p_fmt->i_codec == VLC_FOURCC('W','V','C','1') )
                {
                    vars.p_fmt->video.i_width = 0;
                    vars.p_fmt->video.i_height = 0;
                    vars.p_fmt->b_packetized = false;
                }
            }
            vars.p_tk->b_dts_only = true;
        }
        S_CASE("V_MPEG1") {
            vars.p_fmt->i_codec = VLC_CODEC_MPGV;
            fill_extra_data( vars.p_tk, 0 );
        }
        S_CASE("V_MPEG2") {
            vars.p_fmt->i_codec = VLC_CODEC_MPGV;
            if (vars.obj->psz_muxing_application != NULL &&
                    strstr(vars.obj->psz_muxing_application,"libmakemkv"))
                vars.p_fmt->b_packetized = false;
            fill_extra_data( vars.p_tk, 0 );
        }
        S_CASE("V_THEORA") {
            vars.p_fmt->i_codec = VLC_CODEC_THEORA;
            vars.p_tk->b_pts_only = true;
            fill_extra_data( vars.p_tk, 0 );
        }
        static void v_real_helper (vlc_fourcc_t codec, HandlerPayload& vars)
        {
            vars.p_fmt->i_codec   = codec;

            /* Extract the framerate from the header */
            uint8_t *p = vars.p_tk->p_extra_data;

            if (
                vars.p_tk->i_extra_data >= 26 && !memcmp(p+4, "VIDORV", 6) && strchr("34", p[10]) && p[11] == '0')
            {
                ONLY_FMT(VIDEO);
                vars.p_tk->fmt.video.i_frame_rate      = p[22] << 24 | p[23] << 16 | p[24] << 8 | p[25] << 0;
                vars.p_tk->fmt.video.i_frame_rate_base = 65536;
            }

            fill_extra_data( vars.p_tk, 26 );
        }
        S_CASE("V_REAL/RV10") { v_real_helper (VLC_CODEC_RV10, vars ); }
        S_CASE("V_REAL/RV20") { v_real_helper (VLC_CODEC_RV20, vars ); }
        S_CASE("V_REAL/RV30") { v_real_helper (VLC_CODEC_RV30, vars ); }
        S_CASE("V_REAL/RV40") { v_real_helper (VLC_CODEC_RV40, vars ); }
        S_CASE("V_DIRAC")     {
            vars.p_fmt->i_codec = VLC_CODEC_DIRAC;
        }
        S_CASE("V_VP8") {
            vars.p_fmt->i_codec = VLC_CODEC_VP8;
            if (vars.p_tk->b_has_alpha)
                vars.p_fmt->i_level = 0x1000; // mark as containing alpha data
            vars.p_tk->b_pts_only = true;
        }
        S_CASE("V_VP9") {
            vars.p_fmt->i_codec = VLC_CODEC_VP9;
            if (vars.p_tk->b_has_alpha)
                vars.p_fmt->i_level = 0x1000; // mark as containing alpha data
            vars.p_tk->b_pts_only = true;

            fill_extra_data( vars.p_tk, 0 );
            if (vars.p_tk->fmt.i_extra)
            {
                const auto * VP9CodecFeatures = static_cast<const uint8_t*>(vars.p_tk->fmt.p_extra);
                size_t remain = vars.p_tk->fmt.i_extra;
                while (remain >= 3)
                {
                    uint8_t length = VP9CodecFeatures[1];
                    switch (VP9CodecFeatures[0])
                    {
                        case 1: // Profile
                            if (length == 1)
                                vars.p_fmt->i_profile = VP9CodecFeatures[2];
                            break;
                        case 2: // Level
                            if (length == 1) {
                                if (es_format_HasVpxAlpha(vars.p_fmt))
                                    vars.p_fmt->i_level |= VP9CodecFeatures[2];
                                else
                                    vars.p_fmt->i_level = VP9CodecFeatures[2];
                            }
                            break;
                        case 3: // Bit Depth
                        case 4: // Chroma Subsampling
                        default:
                            break;
                    }
                    if (remain < 1 + 1 + length)
                        break;
                    VP9CodecFeatures += 1 + 1 + length;
                    remain -= 1 + 1 + length;
                }
            }
        }
        S_CASE("V_AV1") {
            vars.p_fmt->i_codec = VLC_CODEC_AV1;
            vars.p_tk->b_pts_only = true;

            fill_extra_data( vars.p_tk, 0 );
            if (vars.p_fmt->i_extra <= 4)
                vars.p_fmt->b_packetized = false; // force full extradata by the packetizer
        }
        S_CASE("V_CAVS") {
            vars.p_fmt->i_codec = VLC_CODEC_CAVS;
        }
        S_CASE("V_AVS2") {
            vars.p_fmt->i_codec = VLC_CODEC_CAVS2;
        }
        S_CASE("V_AVS3") {
            vars.p_fmt->i_codec = VLC_CODEC_CAVS3;
        }
        S_CASE("V_MPEG4/MS/V3") {
            vars.p_fmt->i_codec = VLC_CODEC_DIV3;
        }
        S_CASE("V_MPEG4/ISO/AVC") {
            vars.p_fmt->i_codec = VLC_FOURCC( 'a','v','c','1' );
            fill_extra_data( vars.p_tk, 0 );
        }
        S_CASE_GLOB("V_MPEG4/ISO*") {
            vars.p_fmt->i_codec = VLC_CODEC_MP4V;
            fill_extra_data( vars.p_tk, 0 );
        }
        S_CASE("V_MPEGH/ISO/HEVC") {
            vars.p_tk->fmt.i_codec = VLC_CODEC_HEVC;

            uint8_t* p_extra = (uint8_t*) vars.p_tk->p_extra_data;

            /* HACK: if we found invalid format, made by mkvmerge < 16.0.0,
             *       we try to fix it. They fixed it in 16.0.0. */

            const char* app = vars.obj->psz_writing_application;
            if( p_extra && vars.p_tk->i_extra_data >= 3 &&
                    p_extra[0] == 0 && (p_extra[1] != 0 || p_extra[2] > 1) )
            {
                msg_Warn(vars.p_demuxer,
                        "Invalid HEVC reserved bits in mkv file "
                        "made by %s, fixing it", app ? app : "unknown app");
                p_extra[0] = 0x01;
            }

            fill_extra_data( vars.p_tk, 0 );
        }
        S_CASE("V_MPEGI/ISO/VVC") {
            vars.p_tk->fmt.i_codec = VLC_CODEC_VVC;
            fill_extra_data( vars.p_tk, 0 );
        }
        S_CASE("V_QUICKTIME") {
            ONLY_FMT(VIDEO);
            if( vars.p_tk->i_extra_data > 4 )
            {
                MP4_Box_t *p_box = MP4_BoxNew(ATOM_root);
                if( p_box )
                {
                    stream_t *p_mp4_stream = vlc_stream_MemoryNew( VLC_OBJECT(vars.p_demuxer),
                                                                   vars.p_tk->p_extra_data,
                                                                   vars.p_tk->i_extra_data,
                                                                   true );
                    if( p_mp4_stream )
                    {
                        p_box->i_type = GetFOURCC( vars.p_tk->p_extra_data );
                        p_box->i_size = p_box->i_shortsize = vars.p_tk->i_extra_data;
                        if( MP4_ReadBox_sample_vide( p_mp4_stream, p_box ) )
                        {
                            const MP4_Box_data_sample_vide_t *p_sample = p_box->data.p_sample_vide;
                            vars.p_fmt->i_codec = p_box->i_type;
                            if( p_sample->i_width && p_sample->i_height )
                            {
                                vars.p_tk->fmt.video.i_width = p_sample->i_width;
                                vars.p_tk->fmt.video.i_height = p_sample->i_height;
                            }
                            vars.p_fmt->p_extra = malloc( p_sample->i_qt_image_description );
                            if( vars.p_fmt->p_extra )
                            {
                                vars.p_fmt->i_extra = p_sample->i_qt_image_description;
                                memcpy( vars.p_fmt->p_extra,
                                        p_sample->p_qt_image_description, vars.p_fmt->i_extra );
                            }
                        }
                        vlc_stream_Delete( p_mp4_stream );
                    }
                    MP4_BoxFree( p_box );
                }
            }
            else throw std::runtime_error ("invalid extradata when handling V_QUICKTIME/*");
        }
        S_CASE("V_MJPEG") {
            vars.p_fmt->i_codec = VLC_CODEC_MJPG;
            vars.p_tk->b_pts_only = true;
        }
        S_CASE("V_UNCOMPRESSED") {
            if (vars.p_tk->uncompressed_fourcc == 0)
                msg_Dbg( vars.p_demuxer, "uncompressed format with no FourCC");
            else
            {
                const char *desc = vlc_fourcc_GetDescription(VIDEO_ES, vars.p_tk->uncompressed_fourcc);
                if (desc[0])
                {
                    vars.p_fmt->i_codec = vars.p_tk->uncompressed_fourcc;
                    msg_Dbg( vars.p_demuxer, "uncompressed format codec=%4.4s", (const char*)&vars.p_tk->uncompressed_fourcc );
                }
                else
                {
                    msg_Dbg( vars.p_demuxer, "uncompressed format unknown video codec=%4.4s", (const char*)&vars.p_tk-&gt;uncompressed_fourcc );
                }
            }
        }
        S_CASE("V_FFV1") {
            vars.p_fmt->i_codec = VLC_CODEC_FFV1;
            fill_extra_data( vars.p_tk, 0 );
        }
        S_CASE("V_PRORES") {
            vars.p_fmt->i_codec = VLC_CODEC_PRORES;
            fill_extra_data( vars.p_tk, 0 );
        }
        S_CASE("A_MS/ACM") {
            mkv_track_t * p_tk = vars.p_tk;
            es_format_t * p_fmt = &vars.p_tk->fmt;

            if( p_tk->i_extra_data < sizeof( WAVEFORMATEX ) )
            {
                msg_Err( vars.p_demuxer, "missing/invalid WAVEFORMATEX" );
                p_tk->fmt.i_codec = VLC_CODEC_UNKNOWN;
            }
            else
            {
                ONLY_FMT(AUDIO);

                p_tk->fmt.audio.i_channels      = GetWLE( p_tk->p_extra_data + offsetof(WAVEFORMATEX, nChannels) );
                p_tk->fmt.audio.i_rate         = GetDWLE( p_tk->p_extra_data + offsetof(WAVEFORMATEX, nSamplesPerSec) );
                p_tk->fmt.i_bitrate            = GetDWLE( p_tk->p_extra_data + offsetof(WAVEFORMATEX, nAvgBytesPerSec) ) * 8;
                p_tk->fmt.audio.i_blockalign    = GetWLE( p_tk->p_extra_data + offsetof(WAVEFORMATEX, nBlockAlign) );
                p_tk->fmt.audio.i_bitspersample = GetWLE( p_tk->p_extra_data + offsetof(WAVEFORMATEX, wBitsPerSample) );

                p_tk->fmt.i_extra               = GetWLE( p_tk->p_extra_data + offsetof(WAVEFORMATEX, cbSize) );
                if ( p_tk->fmt.i_extra > p_tk->i_extra_data - sizeof( WAVEFORMATEX ) )
                    p_tk->fmt.i_extra = 0;
                if( p_tk->fmt.i_extra != 0 )
                {
                    p_tk->fmt.p_extra = xmalloc( p_tk->fmt.i_extra );
                    if( p_tk->fmt.p_extra )
                        memcpy( p_tk->fmt.p_extra, p_tk->p_extra_data + sizeof(WAVEFORMATEX), p_tk->fmt.i_extra );
                    else
                        p_tk->fmt.i_extra = 0;
                }

                uint16_t wFormatTag = GetWLE( p_tk->p_extra_data + offsetof(WAVEFORMATEX, wFormatTag) );
                if( wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
                    p_tk->i_extra_data >= sizeof(WAVEFORMATEXTENSIBLE) )
                {
                    GUID subFormat;
                    memcpy(&subFormat, p_tk->p_extra_data + offsetof(WAVEFORMATEXTENSIBLE, SubFormat), sizeof(GUID) );

                    sf_tag_to_fourcc( &subFormat,  &p_tk->fmt.i_codec, NULL);
                    /* FIXME should we use Samples */

                    if( p_tk->fmt.audio.i_channels > 2 &&
                        ( p_tk->fmt.i_codec != VLC_CODEC_UNKNOWN ) )
                    {
                        unsigned i_channel_mask = 0;
                        uint32_t wfextcm = GetDWLE( p_tk->p_extra_data + offsetof(WAVEFORMATEXTENSIBLE, dwChannelMask) );
                        if (wfextcm)
                        {
                            int match;
                            i_channel_mask = getChannelMask( &wfextcm,
                                                             p_tk->fmt.audio.i_channels,
                                                             &match );

                            if( match < p_fmt->audio.i_channels )
                            {
                                int i_missing = p_fmt->audio.i_channels - match;
                                msg_Warn( vars.p_demuxer, "Trying to fill up unspecified position for %d channels", p_fmt->audio.i_channels - match );

                                static const uint32_t pi_pair[] = { AOUT_CHAN_REARLEFT|AOUT_CHAN_REARRIGHT,
                                                                    AOUT_CHAN_MIDDLELEFT|AOUT_CHAN_MIDDLERIGHT,
                                                                    AOUT_CHAN_LEFT|AOUT_CHAN_RIGHT };

                                /* Try to complete with pair */
                                for( unsigned i = 0; i < ARRAY_SIZE(pi_pair); i++ )
                                {
                                    if( i_missing >= 2 && !(i_channel_mask & pi_pair[i] ) )
                                    {
                                        i_missing -= 2;
                                        i_channel_mask |= pi_pair[i];
                                    }
                                }
                                /* Well fill up with what we can */
                                for( unsigned i = 0; pi_channels_aout[i] && i_missing > 0; i++ )
                                {
                                    if( !( i_channel_mask & pi_channels_aout[i] ) )
                                    {
                                        i_channel_mask |= pi_channels_aout[i];
                                        i_missing--;
                                    }
                                }

                                if( i_missing > 0 )
                                {
                                    msg_Err( vars.p_demuxer, "Invalid/unsupported channel mask" );
                                    i_channel_mask = 0;
                                }
                            }
                        }

                        if( i_channel_mask == 0 && p_fmt->audio.i_channels <= AOUT_CHAN_MAX )
                        {
                            /* A dwChannelMask of 0 tells the audio device to render the first
                             * channel to the first port on the device, the second channel to the
                             * second port on the device, and so on. pi_default_channels is
                             * different than pi_channels_aout. Indeed FLC/FRC must be treated a
                             * SL/SR in that case. See "Default Channel Ordering" and "Details
                             * about dwChannelMask" from msdn */


                            static const uint32_t pi_default_channels[] = {
                                AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, AOUT_CHAN_CENTER,
                                AOUT_CHAN_LFE, AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT,
                                AOUT_CHAN_MIDDLELEFT, AOUT_CHAN_MIDDLERIGHT, AOUT_CHAN_REARCENTER };

                            for( unsigned i = 0; i < p_fmt->audio.i_channels &&
                                 i < ARRAY_SIZE(pi_default_channels);
                                 i++ )
                                i_channel_mask |= pi_default_channels[i];
                        }


                        if( i_channel_mask )
                        {
                            if ( p_tk->fmt.i_codec == VLC_FOURCC('a','r','a','w') ||
                                 p_tk->fmt.i_codec == VLC_FOURCC('a','f','l','t') )
                            {
                                p_tk->i_chans_to_reorder = aout_CheckChannelReorder(
                                    pi_channels_aout, NULL,
                                    i_channel_mask,
                                    p_tk->pi_chan_table );
                            }

                            p_tk->fmt.audio.i_physical_channels = i_channel_mask;
                        }

                        p_tk->fmt.i_codec = vlc_fourcc_GetCodecAudio( p_tk->fmt.i_codec,
                                                                      p_tk->fmt.audio.i_bitspersample );
                    }
                }
                else
                {
                    wf_tag_to_fourcc( wFormatTag, &p_tk->fmt.i_codec, NULL );
                    if( wFormatTag == WAVE_FORMAT_AAC_LATM )
                    {
                        p_tk->fmt.i_original_fourcc = VLC_FOURCC('L','A','T','M');
                    }
                    else if( wFormatTag == WAVE_FORMAT_AAC_ADTS )
                    {
                        p_tk->fmt.i_original_fourcc = VLC_FOURCC('A','D','T','S');
                    }
                }

                if( p_tk->fmt.i_codec == VLC_CODEC_UNKNOWN )
                    msg_Err( vars.p_demuxer, "Unrecognized wf tag: 0x%x", wFormatTag );
            }
            p_fmt->b_packetized = !p_fmt->audio.i_blockalign;
        }
        static void A_MPEG_helper_ (HandlerPayload& vars) {
            vars.p_tk->fmt.i_codec = VLC_CODEC_MPGA;
            vars.p_fmt->b_packetized = false;
        }
        S_CASE("A_MPEG/L3") { A_MPEG_helper_(vars); }
        S_CASE("A_MPEG/L2") { A_MPEG_helper_(vars); }
        S_CASE("A_MPEG/L1") { A_MPEG_helper_(vars); }
        S_CASE("A_AC3") {
            ONLY_FMT(AUDIO);
            // the AC-3 default duration cannot be trusted, see #8512
            if ( vars.p_tk->fmt.audio.i_rate == 8000 )
            {
                vars.p_tk->b_no_duration = true;
                vars.p_tk->i_default_duration = 0;
            }

            vars.p_fmt->i_codec = VLC_CODEC_A52;
            vars.p_fmt->b_packetized = false;
        }
        S_CASE("A_ATRAC/AT1") {
            ONLY_FMT(AUDIO);
            vars.p_fmt->i_codec = VLC_CODEC_ATRAC1;
            vars.p_tk->fmt.audio.i_blockalign = vars.p_tk->fmt.audio.i_channels * 212;
        }
        S_CASE("A_EAC3") {
            vars.p_fmt->i_codec = VLC_CODEC_EAC3;
            vars.p_fmt->b_packetized = false;
        }
        S_CASE("A_DTS")  {
            vars.p_fmt->i_codec = VLC_CODEC_DTS;
            vars.p_fmt->b_packetized = false;
        }
        S_CASE("A_MLP")  { vars.p_fmt->i_codec = VLC_CODEC_MLP; }
        S_CASE("A_TRUEHD") { /* FIXME when more samples arrive */
            vars.p_fmt->i_codec = VLC_CODEC_TRUEHD;
            vars.p_fmt->b_packetized = false;
        }
        S_CASE("A_FLAC") {
            vars.p_fmt->i_codec = VLC_CODEC_FLAC;
            fill_extra_data( vars.p_tk, 0 );
        }
        S_CASE("A_VORBIS") {
            vars.p_fmt->i_codec = VLC_CODEC_VORBIS;
            fill_extra_data( vars.p_tk, 0 );
        }
        static void A_OPUS__helper(HandlerPayload& vars) {
            ONLY_FMT(AUDIO);
            vars.p_fmt->i_codec = VLC_CODEC_OPUS;
            vars.p_tk->b_no_duration = true;
            if( !vars.p_tk->fmt.audio.i_rate )
            {
                msg_Err( vars.p_demuxer,"No sampling rate, defaulting to 48kHz");
                vars.p_fmt->audio.i_rate = 48000;
            }
            const uint8_t tags[16] = {'O','p','u','s','T','a','g','s',
                                       0, 0, 0, 0, 0, 0, 0, 0};
            size_t ps[2] = { vars.p_tk->i_extra_data, 16 };
            const void *pkt[2] = { static_cast<const void *>( vars.p_tk->p_extra_data ),
                                   static_cast<const void *>( tags ) };

            if( xiph_PackHeaders( &vars.p_fmt->i_extra,
                &vars.p_fmt->p_extra,
                ps, pkt, 2 ) )
                msg_Err( vars.p_demuxer, "Couldn't pack OPUS headers");
        }
        S_CASE("A_OPUS")                { A_OPUS__helper( vars ); }
        S_CASE("A_OPUS/EXPERIMENTAL")   { A_OPUS__helper( vars ); }
        static void A_AAC_MPEG__helper(HandlerPayload& vars, int i_profile, bool sbr = false) {
            ONLY_FMT(AUDIO);
            int i_srate;

            mkv_track_t * p_tk = vars.p_tk;

            static const unsigned int i_sample_rates[] =
            {
                96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
                16000, 12000, 11025,  8000,  7350,     0,     0,     0
            };

            p_tk->fmt.i_codec = VLC_CODEC_MP4A;

            for( i_srate = 0; i_srate < 13; i_srate++ )
            {
                if( i_sample_rates[i_srate] == p_tk->i_original_rate )
                    break;
            }

            msg_Dbg (vars.p_demuxer, "profile=%d srate=%d", i_profile, i_srate );

            p_tk->fmt.i_extra = sbr ? 5 : 2;
            p_tk->fmt.p_extra = xmalloc( p_tk->fmt.i_extra );
            ((uint8_t*)p_tk->fmt.p_extra)[0] = ((i_profile + 1) << 3) | ((i_srate&0xe) >> 1);
            ((uint8_t*)p_tk->fmt.p_extra)[1] = ((i_srate & 0x1) << 7) | (p_tk->fmt.audio.i_channels << 3);

            if (sbr) {
                int syncExtensionType = 0x2B7;
                int iDSRI;
                for (iDSRI=0; iDSRI<13; iDSRI++)
                    if( i_sample_rates[iDSRI] == p_tk->fmt.audio.i_rate )
                        break;
                ((uint8_t*)p_tk->fmt.p_extra)[2] = (syncExtensionType >> 3) & 0xFF;
                ((uint8_t*)p_tk->fmt.p_extra)[3] = ((syncExtensionType & 0x7) << 5) | 5;
                ((uint8_t*)p_tk->fmt.p_extra)[4] = ((1 & 0x1) << 7) | (iDSRI << 3);
            }

        }
        S_CASE("A_AAC/MPEG2/MAIN")   { A_AAC_MPEG__helper( vars, 0 ); }
        S_CASE("A_AAC/MPEG4/MAIN")   { A_AAC_MPEG__helper( vars, 0 ); }
        S_CASE("A_AAC/MPEG2/LC")     { A_AAC_MPEG__helper( vars, 1 ); }
        S_CASE("A_AAC/MPEG4/LC")     { A_AAC_MPEG__helper( vars, 1 ); }
        S_CASE("A_AAC/MPEG2/SSR")    { A_AAC_MPEG__helper( vars, 2 ); }
        S_CASE("A_AAC/MPEG4/SSR")    { A_AAC_MPEG__helper( vars, 2 ); }
        S_CASE("A_AAC/MPEG4/LTP")    { A_AAC_MPEG__helper( vars, 3 ); }
        S_CASE("A_AAC/MPEG2/LC/SBR") { A_AAC_MPEG__helper( vars, 1, true ); }
        S_CASE("A_AAC/MPEG4/LC/SBR") { A_AAC_MPEG__helper( vars, 1, true ); }
        S_CASE("A_AAC/MPEG4/") { A_AAC_MPEG__helper( vars, 3 ); } // see #4250
        S_CASE("A_AAC/MPEG2/") { A_AAC_MPEG__helper( vars, 3 ); } // backward compatibility
        S_CASE("A_AAC") {
            vars.p_tk->fmt.i_codec = VLC_CODEC_MP4A;
            fill_extra_data( vars.p_tk, 0 );
        }
        S_CASE("A_ALAC") {
            vars.p_tk->fmt.i_codec = VLC_CODEC_ALAC;
            fill_extra_data_alac( vars.p_tk );
        }
        S_CASE("A_WAVPACK4") {
            vars.p_tk->fmt.i_codec = VLC_CODEC_WAVPACK;
            fill_extra_data( vars.p_tk, 0);
        }
        S_CASE("A_TTA1") {
            ONLY_FMT(AUDIO);
            mkv_track_t * p_tk  = vars.p_tk;
            es_format_t * p_fmt = vars.p_fmt;

            p_fmt->i_codec = VLC_CODEC_TTA;
            if( p_tk->i_extra_data > 0 )
            {
                fill_extra_data( p_tk, 0 );
            }
            else
            {
                p_fmt->i_extra = 30;
                p_fmt->p_extra = xmalloc( p_fmt->i_extra );
                uint8_t *p_extra = (uint8_t*)p_fmt->p_extra;
                memcpy( &p_extra[ 0], "TTA1", 4 );
                SetWLE( &p_extra[ 4], 1 );
                SetWLE( &p_extra[ 6], p_fmt->audio.i_channels );
                SetWLE( &p_extra[ 8], p_fmt->audio.i_bitspersample );
                SetDWLE( &p_extra[10], p_fmt->audio.i_rate );
                SetDWLE( &p_extra[14], 0xffffffff );
                memset( &p_extra[18], 0, 30  - 18 );
            }
        }
        static void A_PCM__helper (HandlerPayload& vars, vlc_fourcc_t i_codec) {
            ONLY_FMT(AUDIO);
            vars.p_fmt->i_codec = i_codec;
            vars.p_fmt->audio.i_blockalign = ( vars.p_fmt->audio.i_bitspersample + 7 ) / 8 * vars.p_fmt->audio.i_channels;

        }
        S_CASE("A_PCM/INT/BIG")    { A_PCM__helper ( vars, VLC_FOURCC( 't','w','o','s' ) ); }
        S_CASE("A_PCM/INT/LIT")    { A_PCM__helper ( vars, VLC_FOURCC( 'a','r','a','w' ) ); }
        S_CASE("A_PCM/FLOAT/IEEE") { A_PCM__helper ( vars, VLC_FOURCC( 'a','f','l','t' ) ) ;}
        S_CASE("A_REAL/14_4") {
            ONLY_FMT(AUDIO);
            vars.p_fmt->i_codec = VLC_CODEC_RA_144;
            vars.p_fmt->audio.i_channels = 1;
            vars.p_fmt->audio.i_rate = 8000;
            vars.p_fmt->audio.i_blockalign = 0x14;
        }
        static bool A_REAL__is_valid (HandlerPayload& vars) {
            ONLY_FMT(AUDIO);
            uint8_t *p = vars.p_tk->p_extra_data;

            if (vars.p_tk->i_extra_data <= SIZEOF_REALAUDIO_PRIVATE)
                return false;

            if( memcmp( p, ".ra", 3 ) ) {
                msg_Err( vars.p_demuxer, "Invalid Real ExtraData 0x%4.4s", (char *)p );
                vars.p_tk->fmt.i_codec = VLC_CODEC_UNKNOWN;
                return false;
            }

            return true;
        }
        static void A_REAL__helper (HandlerPayload& vars, vlc_fourcc_t i_codec) {
            mkv_track_t        * p_tk = vars.p_tk;

            p_tk->fmt.i_codec = i_codec;

            Cook_PrivateTrackData * p_realaudio = new Cook_PrivateTrackData(
                p_tk->p_extra_data,
                p_tk->i_extra_data
            );
            p_tk->p_sys = p_realaudio;

            if( unlikely( !p_realaudio ) )
                throw std::runtime_error ("Cook_PrivateTrackData is NULL when handling A_REAL/28_8");

            if( unlikely( !p_realaudio->Init() ) )
                throw std::runtime_error ("Cook_PrivateTrackData::Init() failed when handling A_REAL/28_8");

            if (i_codec == VLC_CODEC_COOK || i_codec == VLC_CODEC_ATRAC3)
                p_tk->fmt.audio.i_blockalign = p_realaudio->i_subpacket_size;
            else // VLC_CODEC_RA_288
                p_tk->fmt.audio.i_blockalign = p_realaudio->coded_frame_size;

            p_tk->fmt.audio.i_rate          = p_realaudio->i_rate;
            p_tk->fmt.audio.i_bitspersample = p_realaudio->i_bitspersample;
            p_tk->fmt.audio.i_channels      = p_realaudio->i_channels;
            msg_Dbg(vars.p_demuxer, "%d channels %d bits %d Hz",p_tk->fmt.audio.i_channels, p_tk->fmt.audio.i_bitspersample, p_tk->fmt.audio.i_rate);

            fill_extra_data( p_tk, p_tk->fmt.i_codec == VLC_CODEC_RA_288 ? 0 : 78);
        }
        S_CASE("A_REAL/COOK") {
            if (!A_REAL__is_valid (vars))
                return;

            A_REAL__helper (vars, VLC_CODEC_COOK);
        }
        S_CASE("A_REAL/ATRC") {
            if (!A_REAL__is_valid (vars))
                return;

            A_REAL__helper (vars, VLC_CODEC_ATRAC3);
        }
        S_CASE("A_REAL/28_8") {
            if (!A_REAL__is_valid (vars))
                return;

            A_REAL__helper (vars, VLC_CODEC_RA_288);
        }
        S_CASE("A_QUICKTIME/QDM2") {
            vars.p_fmt->i_cat   = AUDIO_ES;
            vars.p_fmt->i_codec = VLC_CODEC_QDM2;

            fill_extra_data( vars.p_tk, 0 );
        }
        S_CASE("A_QUICKTIME/QDMC") {
            vars.p_fmt->i_cat   = AUDIO_ES;
            vars.p_fmt->i_codec = VLC_CODEC_QDMC;

            fill_extra_data( vars.p_tk, 0 );
        }
        S_CASE_GLOB("A_QUICKTIME/*") {
            if (vars.p_tk->i_extra_data < 4)
                throw std::runtime_error ("invalid extradata when handling A_QUICKTIME/*");

            vars.p_fmt->i_cat = AUDIO_ES;
            vars.p_fmt->i_codec = GetFOURCC(vars.p_tk->p_extra_data);

            fill_extra_data( vars.p_tk, 0 );
        }
        S_CASE("S_KATE") {
            ONLY_FMT(SPU);
            vars.p_fmt->i_codec = VLC_CODEC_KATE;
            vars.p_fmt->subs.psz_encoding = strdup( "UTF-8" );

            fill_extra_data( vars.p_tk, 0 );
        }
        S_CASE("S_TEXT/ASCII") {
            ONLY_FMT(SPU);
            vars.p_fmt->i_codec = VLC_CODEC_SUBT;
            vars.p_fmt->subs.psz_encoding = strdup( "ASCII" );
        }
        S_CASE("S_TEXT/UTF8") {
            ONLY_FMT(SPU);
            vars.p_tk->fmt.i_codec = VLC_CODEC_SUBT;
            vars.p_tk->fmt.subs.psz_encoding = strdup( "UTF-8" );
        }
        S_CASE("S_TEXT/USF") {
            ONLY_FMT(SPU);
            vars.p_tk->fmt.i_codec = VLC_CODEC_USF;
            vars.p_tk->fmt.subs.psz_encoding = strdup( "UTF-8" );
            fill_extra_data( vars.p_tk, 0 );
        }
        static void SSA__helper (HandlerPayload& vars) {
            ONLY_FMT(SPU);
            vars.p_tk->fmt.i_codec = VLC_CODEC_SSA;
            vars.p_tk->fmt.subs.psz_encoding = strdup( "UTF-8" );
            fill_extra_data( vars.p_tk, 0 );
        }
        S_CASE("S_TEXT/SSA") { SSA__helper( vars ); }
        S_CASE("S_TEXT/ASS") { SSA__helper( vars ); }
        S_CASE("S_SSA")      { SSA__helper( vars ); }
        S_CASE("S_ASS")      { SSA__helper( vars ); }
        S_CASE("S_VOBSUB") {
            ONLY_FMT(SPU);
            mkv_track_t * p_tk = vars.p_tk;

            p_tk->fmt.i_codec = VLC_CODEC_SPU;
            p_tk->b_no_duration = true;
            if( likely( p_tk->i_extra_data && p_tk->fmt.i_cat == SPU_ES ) )
            {
                vobsub_extra_parse( VLC_OBJECT(vars.p_demuxer), &p_tk->fmt.subs,
                                    p_tk->p_extra_data, p_tk->i_extra_data );
            }
        }
        S_CASE("S_DVBSUB")
        {
            ONLY_FMT(SPU);
            vars.p_fmt->i_codec = VLC_CODEC_DVBS;

            if( vars.p_tk->i_extra_data < 4 )
                throw std::runtime_error( "not enough codec data for S_DVBSUB" );

            uint16_t page_id = GetWBE( &vars.p_tk->p_extra_data[0] );
            uint16_t ancillary_id = GetWBE( &vars.p_tk->p_extra_data[2] );

            vars.p_fmt->subs.dvb.i_id = ( ancillary_id << 16 ) | page_id;
        }
        S_CASE("S_HDMV/PGS") {
            vars.p_fmt->i_codec = VLC_CODEC_BD_PG;
        }
        S_CASE("S_HDMV/TEXTST") {
            vars.p_fmt->i_codec = VLC_CODEC_BD_TEXT;
        }
        S_CASE("D_WEBVTT/SUBTITLES") {
            ONLY_FMT(SPU);
            vars.p_fmt->i_codec = VLC_CODEC_WEBVTT;
            vars.p_fmt->subs.psz_encoding = strdup( "UTF-8");
        }
        S_CASE("S_TEXT/WEBVTT") {
            ONLY_FMT(SPU);
            vars.p_fmt->i_codec = VLC_CODEC_WEBVTT;
            vars.p_fmt->subs.psz_encoding = strdup( "UTF-8");
            fill_extra_data( vars.p_tk, 0 );
        }
        S_CASE("B_VOBBTN") {
            vars.p_fmt->i_cat = DATA_ES;
        }
        S_CASE_DEFAULT(str) {
            msg_Err( vars.p_demuxer, "unknown codec id=`%s'", str );
            vars.p_tk->fmt.i_codec = VLC_CODEC_UNKNOWN;
        }
    };

    try {
        TrackCodecHandlers::Dispatcher().send( p_tk->codec.c_str(), &captures );
    }
    catch (std::exception const& e)
    {
        msg_Err( &sys.demuxer, "Error when trying to initiate track (codec: %s): %s",
          p_tk->codec.c_str(), e.what () );
        return false;
    }

    return true;
}

} // namespace

Messung
CHZG
84959892

¤ Dauer der Verarbeitung: 0.52 Sekunden  ¤

*© 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.