/***************************************************************************** * matroska_segment_parse.cpp : matroska demuxer ***************************************************************************** * Copyright (C) 2003-2010 VLC authors and VideoLAN * $Id$ * * Authors: Laurent Aimar * Steve Lhomme * * 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" extern "C" { #include "../vobsub.h" #include "../xiph.h" #include "../windows_audio_commons.h" #include "../mp4/libmp4.h" } #include #include #include /* 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( p_tk->fmt.p_extra ); /* See "ALAC Specific Info (36 bytes) (required)" from alac.macosforge.org/trac/browser/trunk/ALACMagicCookieDescription.txt */ SetDWBE( p_extra, p_tk->fmt.i_extra ); memcpy( p_extra + 4, "alac", 4 ); SetDWBE( p_extra + 8, 0 ); memcpy( p_extra + 12, p_tk->p_extra_data, p_tk->fmt.i_extra - 12 ); } 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 char * ToUTF8( const UTFstring &u ) { return strdup( u.GetUTF8().c_str() ); } /***************************************************************************** * ParseSeekHead: *****************************************************************************/ void matroska_segment_c::ParseSeekHead( KaxSeekHead *seekhead ) { EbmlElement *l; bool b_seekable; i_seekhead_count++; vlc_stream_Control( sys.demuxer.s, STREAM_CAN_SEEK, &b_seekable ); if( !b_seekable ) return; EbmlParser eparser ( &es, seekhead, &sys.demuxer, var_InheritBool( &sys.demuxer, "mkv-use-dummy" ) ); 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", typeid(*l).name() ); continue; } if( MKV_IS_ID( l, KaxSeekID ) ) { KaxSeekID &sid = *static_cast( l ); sid.ReadData( es.I_O() ); id = EbmlId( sid.GetBuffer(), sid.GetSize() ); } else if( MKV_IS_ID( l, KaxSeekPosition ) ) { KaxSeekPosition &spos = *static_cast( l ); spos.ReadData( es.I_O() ); i_pos = (int64_t)segment->GetGlobalPosition( static_cast( 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)", typeid(*l).name() ); } } } catch(...) { msg_Err( &sys.demuxer,"Error while reading %s", typeid(*l).name() ); } 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)", typeid(*l).name() ); } } /***************************************************************************** * ParseTrackEntry: *****************************************************************************/ void matroska_segment_c::ParseTrackEntry( const KaxTrackEntry *m ) { bool bSupported = true; /* Init the track */ mkv_track_t track; EbmlUInteger *pTrackType = static_cast(m->FindElt(EBML_INFO(KaxTrackType))); uint8 ttype; if (likely(pTrackType != NULL)) ttype = (uint8) *pTrackType; else ttype = 0; switch( ttype ) { case track_audio: es_format_Init( &track.fmt, AUDIO_ES, 0); track.fmt.audio.i_channels = 1; track.fmt.audio.i_rate = 8000; break; case track_video: es_format_Init( &track.fmt, VIDEO_ES, 0); break; case track_subtitle: es_format_Init( &track.fmt, SPU_ES, 0); break; case track_buttons: es_format_Init( &track.fmt, SPU_ES, 0); break; default: es_format_Init( &track.fmt, UNKNOWN_ES, 0); break; } MkvTree( sys.demuxer, 2, "Track Entry" ); struct MetaDataCapture { matroska_segment_c * obj; mkv_track_t * tk; demux_t * p_demuxer; bool& bSupported; int level; 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; } track_video_info; } metadata_payload = { this, &track, &sys.demuxer, bSupported, 3, { } }; MKV_SWITCH_CREATE( EbmlTypeDispatcher, MetaDataHandlers, MetaDataCapture ) { MKV_SWITCH_INIT(); static void debug (MetaDataCapture const& vars, char const * fmt, ...) { 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( tnum ); debug( vars, "Track Number=%u", vars.tk->i_number ); } E_CASE( KaxTrackUID, tuid ) { debug( vars, "Track UID=%u", static_cast( tuid ) ); } E_CASE( KaxTrackType, ttype ) { const char *psz_type; switch( static_cast( 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( fenb ); debug( vars, "Track Enabled=%u", vars.tk->b_enabled ); } E_CASE( KaxTrackFlagDefault, fdef ) { vars.tk->b_default = static_cast( fdef ); debug( vars, "Track Default=%u", vars.tk->b_default ); } E_CASE( KaxTrackFlagForced, ffor ) // UNUSED { vars.tk->b_forced = static_cast( ffor ); debug( vars, "Track Forced=%u", vars.tk->b_forced ); } E_CASE( KaxTrackFlagLacing, lac ) // UNUSED { debug( vars, "Track Lacing=%d", static_cast( lac ) ) ; } E_CASE( KaxTrackMinCache, cmin ) // UNUSED { debug( vars, "Track MinCache=%d", static_cast( cmin ) ) ; } E_CASE( KaxTrackMaxCache, cmax ) // UNUSED { debug( vars, "Track MaxCache=%d", static_cast( cmax ) ) ; } E_CASE( KaxTrackDefaultDuration, defd ) { vars.tk->i_default_duration = static_cast(defd); debug( vars, "Track Default Duration=%" PRId64, vars.tk->i_default_duration ); vars.tk->i_default_duration /= 1000; } E_CASE( KaxTrackTimecodeScale, ttcs ) { vars.tk->f_timecodescale = static_cast( 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( mbl ) ) ; } E_CASE( KaxTrackName, tname ) { 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 ) { free( vars.tk->fmt.psz_language ); const std::string slang ( lang ); size_t pos = slang.find_first_of( '-' ); vars.tk->fmt.psz_language = pos != std::string::npos ? strndup( slang.c_str (), pos ) : strdup( slang.c_str() ); debug( vars, "Track Language=`%s'", vars.tk->fmt.psz_language ? vars.tk->fmt.psz_language : "(null)" ); } 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 ) { vars.tk->p_extra_data = static_cast( 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( 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( cdall ) ) ; } E_CASE( KaxTrackOverlay, tovr ) // UNUSED { debug( vars, "Track Overlay=%u", static_cast( tovr ) ) ; } #if LIBMATROSKA_VERSION >= 0x010401 E_CASE( KaxCodecDelay, codecdelay ) { vars.tk->i_codec_delay = static_cast( codecdelay ) / 1000; msg_Dbg( vars.p_demuxer, "| | | + Track Codec Delay =%" PRIu64, vars.tk->i_codec_delay ); } E_CASE( KaxSeekPreRoll, spr ) { vars.tk->i_seek_preroll = static_cast( spr ) / 1000; 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(), Payload( vars ) ); vars.level -= 1; } E_CASE( KaxContentEncoding, cenc ) { debug( vars, "Content Encoding" ); vars.level += 1; dispatcher.iterate( cenc.begin(), cenc.end(), Payload( vars ) ); vars.level -= 1; } E_CASE( KaxContentEncodingOrder, encord ) { debug( vars, "Order: %i", static_cast( encord ) ); } E_CASE( KaxContentEncodingScope, encscope ) { vars.tk->i_encoding_scope = static_cast( encscope ); debug( vars, "Scope: %i", vars.tk->i_encoding_scope ); } E_CASE( KaxContentEncodingType, enctype ) { debug( vars, "Type: %i", static_cast( 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(), Payload( vars ) ); vars.level -= 1; } E_CASE( KaxContentCompAlgo, compalg ) { vars.tk->i_compression_type = static_cast( 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_HEADER ) ) { msg_Err( vars.p_demuxer, "Track Compression method %d not supported", vars.tk->i_compression_type ); vars.bSupported = false; } } E_CASE( KaxContentCompSettings, kccs ) { vars.tk->p_compression_data = new KaxContentCompSettings( kccs ); } E_CASE( KaxTrackVideo, tkv ) { debug( vars, "Track Video"); mkv_track_t *tk = vars.tk; if (tk->fmt.i_cat != VIDEO_ES ) { msg_Err( vars.p_demuxer, "Video elements not allowed for this track" ); } else { 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( tk->i_default_duration ); tk->fmt.video.i_frame_rate = 1000000; } vars.level += 1; dispatcher.iterate (tkv.begin (), tkv.end (), Payload( 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; VLC_UNUSED(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( i_display_height && i_display_width ) { tk->fmt.video.i_sar_num = i_display_width * tk->fmt.video.i_height; tk->fmt.video.i_sar_den = i_display_height * tk->fmt.video.i_width; } tk->fmt.video.i_visible_width = tk->fmt.video.i_width; tk->fmt.video.i_visible_height = tk->fmt.video.i_height; if( i_crop_left || i_crop_right || i_crop_top || i_crop_bottom ) { tk->fmt.video.i_x_offset = i_crop_left; tk->fmt.video.i_y_offset = i_crop_top; tk->fmt.video.i_visible_width -= i_crop_left + i_crop_right; tk->fmt.video.i_visible_height -= i_crop_top + i_crop_bottom; } /* FIXME: i_display_* allows you to not only set DAR, but also a zoom factor. we do not support this atm */ } } #if LIBMATROSKA_VERSION >= 0x010406 E_CASE( KaxVideoProjection, proj ) { debug( vars, "Track Video Projection" ) ; vars.level += 1; dispatcher.iterate (proj.begin (), proj.end (), Payload( vars ) ); vars.level -= 1; } E_CASE( KaxVideoProjectionType, fint ) { switch (static_cast( 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( fint ) ) ; break; } } E_CASE( KaxVideoProjectionPoseYaw, pose ) { vars.tk->fmt.video.pose.f_yaw_degrees = static_cast( pose ); } E_CASE( KaxVideoProjectionPosePitch, pose ) { vars.tk->fmt.video.pose.f_pitch_degrees = static_cast( pose ); } E_CASE( KaxVideoProjectionPoseRoll, pose ) { vars.tk->fmt.video.pose.f_roll_degrees = static_cast( pose ); } #endif E_CASE( KaxVideoFlagInterlaced, fint ) // UNUSED { debug( vars, "Track Video Interlaced=%u", static_cast( fint ) ) ; } E_CASE( KaxVideoStereoMode, stereo ) // UNUSED { debug( vars, "Track Video Stereo Mode=%u", static_cast( stereo ) ) ; } E_CASE( KaxVideoPixelWidth, vwidth ) { vars.tk->fmt.video.i_width += static_cast( vwidth ); debug( vars, "width=%d", vars.tk->fmt.video.i_width ); } E_CASE( KaxVideoPixelHeight, vheight ) { vars.tk->fmt.video.i_height += static_cast( vheight ); debug( vars, "height=%d", vars.tk->fmt.video.i_height ); } E_CASE( KaxVideoDisplayWidth, vwidth ) { vars.track_video_info.i_display_width = static_cast( vwidth ); debug( vars, "display width=%d", vars.track_video_info.i_display_width ); } E_CASE( KaxVideoDisplayHeight, vheight ) { vars.track_video_info.i_display_height = static_cast( vheight ); debug( vars, "display height=%d", vars.track_video_info.i_display_height ); } E_CASE( KaxVideoPixelCropBottom, cropval ) { vars.track_video_info.i_crop_bottom = static_cast( cropval ); debug( vars, "crop pixel bottom=%d", vars.track_video_info.i_crop_bottom ); } E_CASE( KaxVideoPixelCropTop, cropval ) { vars.track_video_info.i_crop_top = static_cast( cropval ); debug( vars, "crop pixel top=%d", vars.track_video_info.i_crop_top ); } E_CASE( KaxVideoPixelCropRight, cropval ) { vars.track_video_info.i_crop_right = static_cast( cropval ); debug( vars, "crop pixel right=%d", vars.track_video_info.i_crop_right ); } E_CASE( KaxVideoPixelCropLeft, cropval ) { vars.track_video_info.i_crop_left = static_cast( cropval ); debug( vars, "crop pixel left=%d", vars.track_video_info.i_crop_left ); } E_CASE( KaxVideoDisplayUnit, vdmode ) { vars.track_video_info.i_display_unit = static_cast( vdmode ); debug( vars, "Track Video Display Unit=%s", vars.track_video_info.i_display_unit == 0 ? "pixels" : ( vars.track_video_info.i_display_unit == 1 ? "centimeters": "inches" ) ); } E_CASE( KaxVideoAspectRatio, ratio ) // UNUSED { debug( vars, "Track Video Aspect Ratio Type=%u", static_cast( ratio ) ) ; } E_CASE( KaxVideoFrameRate, vfps ) { vars.tk->f_fps = __MAX( static_cast( vfps ), 1 ); debug( vars, "fps=%f", vars.tk->f_fps ); } E_CASE( KaxVideoColourSpace, colourspace ) { if ( colourspace.ValidateSize() ) { char clrspc[5]; vars.tk->fmt.i_codec = GetFOURCC( colourspace.GetBuffer() ); vlc_fourcc_to_char( vars.tk->fmt.i_codec, clrspc ); clrspc[4] = '\0'; debug( vars, "Colour Space=%s", clrspc ); } } E_CASE( KaxTrackAudio, tka ) { debug( vars, "Track Audio"); if (vars.tk->fmt.i_cat != AUDIO_ES ) { msg_Err( vars.p_demuxer, "Audio elements not allowed for this track" ); } else { vars.level += 1; dispatcher.iterate( tka.begin(), tka.end(), Payload( vars )); vars.level -= 1; } } E_CASE( KaxAudioSamplingFreq, afreq ) { float const value = static_cast( 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 ) { vars.tk->fmt.audio.i_rate = static_cast( afreq ); debug( vars, "aoutfreq=%d", vars.tk->fmt.audio.i_rate ) ; } E_CASE( KaxAudioChannels, achan ) { vars.tk->fmt.audio.i_channels = static_cast( achan ); debug( vars, "achan=%u", vars.tk->fmt.audio.i_channels ); } E_CASE( KaxAudioBitDepth, abits ) { vars.tk->fmt.audio.i_bitspersample = static_cast( 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)", typeid(element).name() ); } }; MetaDataHandlers::Dispatcher().iterate ( m->begin(), m->end(), MetaDataHandlers::Payload( metadata_payload ) ); if( track.i_number == 0 ) { msg_Warn( &sys.demuxer, "Missing KaxTrackNumber, discarding track!" ); es_format_Clean( &track.fmt ); free(track.p_extra_data); return; } if ( bSupported ) { #ifdef HAVE_ZLIB_H if( track.i_compression_type == MATROSKA_COMPRESSION_ZLIB && track.i_encoding_scope & MATROSKA_ENCODING_SCOPE_PRIVATE && track.i_extra_data && track.p_extra_data && zlib_decompress_extra( &sys.demuxer, &track ) ) // zlib_decompress_extra will clean the track itself return; #endif if( TrackInit( &track ) ) { msg_Err(&sys.demuxer, "Couldn't init track %u", track.i_number ); es_format_Clean( &track.fmt ); free(track.p_extra_data); return; } tracks.insert( std::make_pair( track.i_number, track ) ); // TODO: add warning if two tracks have the same key } else { msg_Err( &sys.demuxer, "Track Entry %u not supported", track.i_number ); es_format_Clean( &track.fmt ); free(track.p_extra_data); } } /***************************************************************************** * ParseTracks: *****************************************************************************/ void matroska_segment_c::ParseTracks( KaxTracks *tracks ) { EbmlElement *el; int i_upper_level = 0; /* Master elements */ if( unlikely( tracks->IsFiniteSize() && tracks->GetSize() >= SIZE_MAX ) ) { msg_Err( &sys.demuxer, "Track too big, aborting" ); return; } try { tracks->Read( es, EBML_CONTEXT(tracks), i_upper_level, el, true ); } catch(...) { msg_Err( &sys.demuxer, "Couldn't read 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)", typeid(element).name() ); } }; TrackHandlers::Dispatcher().iterate( tracks->begin(), tracks->end(), TrackHandlers::Payload( payload ) ); } /***************************************************************************** * ParseInfo: *****************************************************************************/ void matroska_segment_c::ParseInfo( KaxInfo *info ) { EbmlElement *el; EbmlMaster *m; int i_upper_level = 0; /* Master elements */ m = static_cast(info); if( unlikely( m->IsFiniteSize() && m->GetSize() >= SIZE_MAX ) ) { msg_Err( &sys.demuxer, "Info too big, aborting" ); return; } try { m->Read( es, EBML_CONTEXT(info), i_upper_level, el, true ); } catch(...) { msg_Err( &sys.demuxer, "Couldn't read info" ); return; } struct InfoHandlerPayload { demux_t * p_demuxer; matroska_segment_c * obj; EbmlElement *& el; EbmlMaster *& m; int& i_upper_level; } captures = { &sys.demuxer, this, el, m, i_upper_level }; MKV_SWITCH_CREATE(EbmlTypeDispatcher, InfoHandlers, InfoHandlerPayload) { MKV_SWITCH_INIT(); static void debug (InfoHandlerPayload& vars, char const * fmt, ...) { 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=%d", *reinterpret_cast( 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=%d", *reinterpret_cast( 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=%d", *reinterpret_cast( vars.obj->p_next_segment_uid->GetBuffer() ) ); } E_CASE( KaxTimecodeScale, tcs ) { vars.obj->i_timescale = static_cast( tcs ); debug( vars, "TimecodeScale=%" PRId64, vars.obj->i_timescale ); } E_CASE( KaxDuration, dur ) { vars.obj->i_duration = mtime_t( static_cast( dur ) ); debug( vars, "Duration=%" PRId64, vars.obj->i_duration ); } E_CASE( KaxMuxingApp, mapp ) { vars.obj->psz_muxing_application = ToUTF8( UTFstring( mapp ) ); debug( vars, "Muxing Application=%s", vars.obj->psz_muxing_application ); } E_CASE( KaxWritingApp, wapp ) { vars.obj->psz_writing_application = ToUTF8( UTFstring( wapp ) ); debug( vars, "Writing Application=%s", vars.obj->psz_writing_application ); } E_CASE( KaxSegmentFilename, sfn ) { vars.obj->psz_segment_filename = ToUTF8( UTFstring( sfn ) ); debug( vars, "Segment Filename=%s", vars.obj->psz_segment_filename ); } E_CASE( KaxTitle, 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=%d", *reinterpret_cast( 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 ) ) { vars.obj->psz_date_utc = strdup( buffer ); debug( vars, "Date=%s", vars.obj->psz_date_utc ); } } 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( uid ) ); } E_CASE( KaxChapterTranslateCodec, codec_id ) { vars->codec_id = static_cast( codec_id ); } E_CASE( KaxChapterTranslateID, translated_id ) { vars->p_translated = new KaxChapterTranslateID( translated_id ); } }; try { if( unlikely( trans.IsFiniteSize() && trans.GetSize() >= SIZE_MAX ) ) { msg_Err( vars.p_demuxer, "Chapter translate too big, aborting" ); return; } trans.Read( vars.obj->es, EBML_CONTEXT(&trans), vars.i_upper_level, vars.el, true ); chapter_translation_c *p_translate = new chapter_translation_c(); TranslationHandler::Dispatcher().iterate( trans.begin(), trans.end(), TranslationHandler::Payload( p_translate ) ); vars.obj->translations.push_back( 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)", typeid(element).name() ); } }; InfoHandlers::Dispatcher().iterate( m->begin(), m->end(), InfoHandlers::Payload( captures ) ); if( i_duration != -1 ) i_duration = mtime_t( static_cast( i_duration * i_timescale ) / 10e5 ); } /***************************************************************************** * 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, ...) { 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( uid ); debug( vars, "ChapterUID=%" PRIu64, vars.chapters.i_uid ); } E_CASE( KaxChapterFlagHidden, flag ) { vars.chapters.b_display_seekpoint = static_cast( flag ) == 0; debug( vars, "ChapterFlagHidden=%s", vars.chapters.b_display_seekpoint ? "no" : "yes" ); } E_CASE( KaxChapterSegmentUID, uid ) { vars.chapters.p_segment_uid = new KaxChapterSegmentUID( uid ); vars.obj->b_ref_external_segments = true; debug( vars, "ChapterSegmentUID=%u", *reinterpret_cast( vars.chapters.p_segment_uid->GetBuffer() ) ); } E_CASE( KaxChapterSegmentEditionUID, euid ) { vars.chapters.p_segment_edition_uid = new KaxChapterSegmentEditionUID( euid ); debug( vars, "ChapterSegmentEditionUID=%u", #if LIBMATROSKA_VERSION < 0x010300 *reinterpret_cast( vars.chapters.p_segment_edition_uid->GetBuffer() ) #else static_cast( *vars.chapters.p_segment_edition_uid ) #endif ); } E_CASE( KaxChapterTimeStart, start ) { vars.chapters.i_start_time = static_cast( start ) / INT64_C(1000); debug( vars, "ChapterTimeStart=%" PRId64, vars.chapters.i_start_time ); } E_CASE( KaxChapterTimeEnd, end ) { vars.chapters.i_end_time = static_cast( end ) / INT64_C(1000); 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(), Payload( vars ) ); vars.level -= 1; } E_CASE( KaxChapterString, name ) { char *psz_tmp_utf8 = ToUTF8( UTFstring( name ) ); for ( int k = 0; k < vars.i_level; k++) vars.chapters.psz_name += '+'; vars.chapters.psz_name += ' '; vars.chapters.psz_name += psz_tmp_utf8; vars.chapters.b_user_display = true; debug( vars, "ChapterString=%s", psz_tmp_utf8 ); free( psz_tmp_utf8 ); } E_CASE( KaxChapterLanguage, lang ) { debug( vars, "ChapterLanguage=%s", static_cast( lang ).c_str() ); } E_CASE( KaxChapterCountry, ct ) { debug( vars, "ChapterCountry=%s", static_cast( ct ).c_str() ); } E_CASE( KaxChapterProcess, cp ) { debug( vars, "ChapterProcess" ); chapter_codec_cmds_c *p_ccodec = NULL; for( size_t j = 0; j < cp.ListSize(); j++ ) { if( MKV_CHECKED_PTR_DECL( p_codec_id, KaxChapterProcessCodecID, cp[j] ) ) { if ( static_cast(*p_codec_id) == 0 ) p_ccodec = new matroska_script_codec_c( vars.obj->sys ); else if ( static_cast(*p_codec_id) == 1 ) p_ccodec = new dvd_chapter_codec_c( vars.obj->sys ); break; } } if ( p_ccodec != NULL ) { for( size_t j = 0; j < cp.ListSize(); j++ ) { EbmlElement *k= cp[j]; if( MKV_CHECKED_PTR_DECL( p_private, KaxChapterProcessPrivate, k ) ) { p_ccodec->SetPrivate( *p_private ); } else if ( MKV_CHECKED_PTR_DECL( 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(), ChapterAtomHandlers::Payload( payload ) ); } /***************************************************************************** * ParseAttachments: *****************************************************************************/ void matroska_segment_c::ParseAttachments( KaxAttachments *attachments ) { EbmlElement *el; int i_upper_level = 0; if( unlikely( attachments->IsFiniteSize() && attachments->GetSize() >= SIZE_MAX ) ) { msg_Err( &sys.demuxer, "Attachments too big, aborting" ); return; } try { attachments->Read( es, EBML_CONTEXT(attachments), i_upper_level, el, true ); } catch(...) { msg_Err( &sys.demuxer, "Error while reading attachments" ); return; } KaxAttached *attachedFile = FindChild( *attachments ); while( attachedFile && ( attachedFile->GetSize() > 0 ) ) { KaxFileData &img_data = GetChild( *attachedFile ); char *psz_tmp_utf8 = ToUTF8( UTFstring( GetChild( *attachedFile ) ) ); std::string attached_filename(psz_tmp_utf8); free(psz_tmp_utf8); attachment_c *new_attachment = new attachment_c( attached_filename, GetChild( *attachedFile ), img_data.GetSize() ); msg_Dbg( &sys.demuxer, "| | - %s (%s)", new_attachment->fileName(), new_attachment->mimeType() ); if( new_attachment->init() ) { memcpy( new_attachment->p_data, img_data.GetBuffer(), img_data.GetSize() ); sys.stored_attachments.push_back( new_attachment ); if( !strncmp( new_attachment->mimeType(), "image/", 6 ) ) { char *psz_url; if( asprintf( &psz_url, "attachment://%s", new_attachment->fileName() ) == -1 ) continue; if( !sys.meta ) sys.meta = vlc_meta_New(); vlc_meta_SetArtURL( sys.meta, psz_url ); free( psz_url ); } } else { delete new_attachment; } attachedFile = &GetNextChild( *attachments, *attachedFile ); } } /***************************************************************************** * ParseChapters: *****************************************************************************/ void matroska_segment_c::ParseChapters( KaxChapters *chapters ) { if( unlikely( chapters->IsFiniteSize() && chapters->GetSize() >= SIZE_MAX ) ) { msg_Err( &sys.demuxer, "Chapters too big, aborting" ); return; } try { EbmlElement *el; int i_upper_level = 0; chapters->Read( es, EBML_CONTEXT(chapters), i_upper_level, el, true ); } catch(...) { msg_Err( &sys.demuxer, "Error while reading 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 ( euid ); } E_CASE( KaxEditionFlagOrdered, flag_ordered ) { vars.p_edition->b_ordered = var_InheritBool(vars.p_demuxer, "mkv-use-ordered-chapters") && static_cast( flag_ordered ); } E_CASE( KaxEditionFlagDefault, flag_default ) { if( static_cast( flag_default ) ) vars.obj->i_default_edition = vars.obj->stored_editions.size(); } E_CASE( KaxEditionFlagHidden, flag_hidden ) { VLC_UNUSED( flag_hidden ); // TODO: FIXME: implement VLC_UNUSED( vars ); } E_CASE( EbmlVoid, el ) { VLC_UNUSED( el ); VLC_UNUSED( vars ); } E_CASE_DEFAULT( el ) { msg_Dbg( vars.p_demuxer, "| | | + Unknown (%s)", typeid(el).name() ); } }; KaxEditionHandler::Dispatcher().iterate( entry.begin(), entry.end(), KaxEditionHandler::Payload( 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)", typeid(el).name() ); } }; KaxChapterHandler::Dispatcher().iterate( chapters->begin(), chapters->end(), KaxChapterHandler::Payload( *this ) ); } bool matroska_segment_c::ParseCluster( KaxCluster *cluster, bool b_update_start_time, ScopeMode read_fully ) { if( unlikely( cluster->IsFiniteSize() && cluster->GetSize() >= SIZE_MAX ) ) { msg_Err( &sys.demuxer, "Cluster too big, aborting" ); return false; } try { EbmlElement *el; int i_upper_level = 0; cluster->Read( es, EBML_CONTEXT(cluster), i_upper_level, el, true, read_fully ); } catch(...) { msg_Err( &sys.demuxer, "Error while reading cluster" ); return false; } bool b_has_timecode = false; for( unsigned int i = 0; i < cluster->ListSize(); ++i ) { if( MKV_CHECKED_PTR_DECL( p_ctc, KaxClusterTimecode, (*cluster)[i] ) ) { cluster->InitTimecode( static_cast( *p_ctc ), i_timescale ); _seeker.add_cluster( cluster ); b_has_timecode = true; break; } } if( !b_has_timecode ) { msg_Err( &sys.demuxer, "Detected cluster without mandatory timecode" ); return false; } if( b_update_start_time ) i_mk_start_time = cluster->GlobalTimecode() / INT64_C( 1000 ); return true; } int32_t 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 0; } 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 { 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 ); vars.p_fmt->i_extra = GetDWLE( &p_bih->biSize ) - sizeof( VLC_BITMAPINFOHEADER ); if( vars.p_fmt->i_extra > 0 ) { /* Very unlikely yet possible: bug #5659*/ size_t maxlen = vars.p_tk->i_extra_data - sizeof( VLC_BITMAPINFOHEADER ); vars.p_fmt->i_extra = ( (unsigned)vars.p_fmt->i_extra < maxlen )? vars.p_fmt->i_extra : maxlen; vars.p_fmt->p_extra = xmalloc( vars.p_fmt->i_extra ); memcpy( vars.p_fmt->p_extra, &p_bih[1], vars.p_fmt->i_extra ); } 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; 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_tk->b_dts_only = true; 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') { 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; vars.p_tk->b_pts_only = true; } S_CASE("V_VP9") { vars.p_fmt->i_codec = VLC_CODEC_VP9; vars.p_fmt->b_packetized = false; vars.p_tk->b_pts_only = true; fill_extra_data( vars.p_tk, 0 ); } S_CASE("V_AV1") { vars.p_fmt->i_codec = VLC_CODEC_AV1; vars.p_fmt->b_packetized = false; vars.p_tk->b_pts_only = true; fill_extra_data( vars.p_tk, 0 ); } 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; fill_extra_data( vars.p_tk, 0 ); } S_CASE("V_QUICKTIME") { 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; } S_CASE("V_UNCOMPRESSED") { msg_Dbg( vars.p_demuxer, "uncompressed format detected"); } S_CASE("V_FFV1") { vars.p_fmt->i_codec = VLC_CODEC_FFV1; 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 < (int)sizeof( WAVEFORMATEX ) ) { msg_Err( vars.p_demuxer, "missing/invalid WAVEFORMATEX" ); p_tk->fmt.i_codec = VLC_CODEC_UNKNOWN; } else { WAVEFORMATEX *p_wf = (WAVEFORMATEX*)p_tk->p_extra_data; p_tk->fmt.audio.i_channels = GetWLE( &p_wf->nChannels ); p_tk->fmt.audio.i_rate = GetDWLE( &p_wf->nSamplesPerSec ); p_tk->fmt.i_bitrate = GetDWLE( &p_wf->nAvgBytesPerSec ) * 8; p_tk->fmt.audio.i_blockalign = GetWLE( &p_wf->nBlockAlign );; p_tk->fmt.audio.i_bitspersample = GetWLE( &p_wf->wBitsPerSample ); p_tk->fmt.i_extra = GetWLE( &p_wf->cbSize ); 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_wf[1], p_tk->fmt.i_extra ); else p_tk->fmt.i_extra = 0; } if( p_wf->wFormatTag == WAVE_FORMAT_EXTENSIBLE && p_tk->i_extra_data >= sizeof(WAVEFORMATEXTENSIBLE) ) { WAVEFORMATEXTENSIBLE * p_wext = (WAVEFORMATEXTENSIBLE*) p_wf; sf_tag_to_fourcc( &p_wext->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 ) ) { uint32_t wfextcm = GetDWLE( &p_wext->dwChannelMask ); int match; unsigned i_channel_mask = getChannelMask( &wfextcm, p_tk->fmt.audio.i_channels, &match ); p_tk->fmt.i_codec = vlc_fourcc_GetCodecAudio( p_tk->fmt.i_codec, p_tk->fmt.audio.i_bitspersample ); if( i_channel_mask ) { 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 = p_tk->fmt.audio.i_original_channels = i_channel_mask; } } } else { wf_tag_to_fourcc( GetWLE( &p_wf->wFormatTag ), &p_tk->fmt.i_codec, NULL ); if( p_wf->wFormatTag == WAVE_FORMAT_AAC_LATM ) { p_tk->fmt.i_original_fourcc = VLC_FOURCC('L','A','T','M'); } else if( p_wf->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", GetWLE( &p_wf->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") { // 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_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 ); } S_CASE("A_OPUS") { vars.p_fmt->i_codec = VLC_CODEC_OPUS; 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}; unsigned ps[2] = { vars.p_tk->i_extra_data, 16 }; const void *pkt[2] = { static_cast( vars.p_tk->p_extra_data ), static_cast( 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"); } static void A_AAC_MPEG__helper(HandlerPayload& vars, int i_profile, bool sbr = false) { 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") { 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, uint32_t i_codec) { 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") { 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) { uint8_t *p = vars.p_tk->p_extra_data; if (vars.p_tk->i_extra_data <= 28) 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, uint32_t i_codec) { mkv_track_t * p_tk = vars.p_tk; real_audio_private * priv = (real_audio_private*) p_tk->p_extra_data; p_tk->fmt.i_codec = i_codec; /* FIXME RALF and SIPR */ uint16_t version = (uint16_t) hton16(priv->version); p_tk->p_sys = new Cook_PrivateTrackData( hton16( priv->sub_packet_h ), hton16( priv->frame_size ), hton16( priv->sub_packet_size ) ); if( unlikely( !p_tk->p_sys ) ) throw std::runtime_error ("p_tk->p_sys is NULL when handling A_REAL/28_8"); if( unlikely( p_tk->p_sys->Init() ) ) throw std::runtime_error ("p_tk->p_sys->Init() failed when handling A_REAL/28_8"); if( version == 4 ) { real_audio_private_v4 * v4 = (real_audio_private_v4*) priv; p_tk->fmt.audio.i_channels = hton16(v4->channels); p_tk->fmt.audio.i_bitspersample = hton16(v4->sample_size); p_tk->fmt.audio.i_rate = hton16(v4->sample_rate); } else if( version == 5 ) { real_audio_private_v5 * v5 = (real_audio_private_v5*) priv; p_tk->fmt.audio.i_channels = hton16(v5->channels); p_tk->fmt.audio.i_bitspersample = hton16(v5->sample_size); p_tk->fmt.audio.i_rate = hton16(v5->sample_rate); } 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; real_audio_private * priv = (real_audio_private*) vars.p_tk->p_extra_data; vars.p_tk->fmt.audio.i_blockalign = hton16(priv->sub_packet_size); A_REAL__helper (vars, VLC_CODEC_COOK); } S_CASE("A_REAL/ATRC") { if (!A_REAL__is_valid (vars)) return; real_audio_private * priv = (real_audio_private*) vars.p_tk->p_extra_data; vars.p_tk->fmt.audio.i_blockalign = hton16(priv->sub_packet_size); 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_FOURCC('Q','D','M','C'); 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") { 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") { vars.p_fmt->i_codec = VLC_CODEC_SUBT; vars.p_fmt->subs.psz_encoding = strdup( "ASCII" ); } S_CASE("S_TEXT/UTF8") { vars.p_tk->fmt.i_codec = VLC_CODEC_SUBT; vars.p_tk->fmt.subs.psz_encoding = strdup( "UTF-8" ); } S_CASE("S_TEXT/USF") { vars.p_tk->fmt.i_codec = VLC_FOURCC( 'u', 's', 'f', ' ' ); vars.p_tk->fmt.subs.psz_encoding = strdup( "UTF-8" ); fill_extra_data( vars.p_tk, 0 ); } static void SSA__helper (HandlerPayload& vars) { 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") { mkv_track_t * p_tk = vars.p_tk; p_tk->fmt.i_codec = VLC_CODEC_SPU; p_tk->b_no_duration = true; if( p_tk->i_extra_data ) { char *psz_start; char *psz_buf = (char *)malloc( p_tk->i_extra_data + 1); if( psz_buf != NULL ) { memcpy( psz_buf, p_tk->p_extra_data , p_tk->i_extra_data ); psz_buf[p_tk->i_extra_data] = '\0'; if (p_tk->fmt.i_cat == SPU_ES) { psz_start = strstr( psz_buf, "size:" ); if( psz_start && vobsub_size_parse( psz_start, &p_tk->fmt.subs.spu.i_original_frame_width, &p_tk->fmt.subs.spu.i_original_frame_height ) == VLC_SUCCESS ) { msg_Dbg( vars.p_demuxer, "original frame size vobsubs: %dx%d", p_tk->fmt.subs.spu.i_original_frame_width, p_tk->fmt.subs.spu.i_original_frame_height ); } else { msg_Warn( vars.p_demuxer, "reading original frame size for vobsub failed" ); } psz_start = strstr( psz_buf, "palette:" ); if( psz_start && vobsub_palette_parse( psz_start, &p_tk->fmt.subs.spu.palette[1] ) == VLC_SUCCESS ) { p_tk->fmt.subs.spu.palette[0] = SPU_PALETTE_DEFINED; msg_Dbg( vars.p_demuxer, "vobsub palette read" ); } else { msg_Warn( vars.p_demuxer, "reading original palette failed" ); } } free( psz_buf ); } } } 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") { vars.p_fmt->i_codec = VLC_CODEC_SUBT; vars.p_fmt->subs.psz_encoding = strdup( "UTF-8"); } S_CASE("B_VOBBTN") { vars.p_fmt->i_cat = NAV_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(), TrackCodecHandlers::Payload( 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 0; }