You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
exiv2/src/matroskavideo.cpp

974 lines
38 KiB
C++

// ***************************************************************** -*- C++ -*-
/*
* Copyright (C) 2004-2021 Exiv2 authors
* This program is part of the Exiv2 distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA.
*/
// *****************************************************************************
// included header files
#include "config.h"
// #ifdef EXV_ENABLE_VIDEO
#include "basicio.hpp"
#include "error.hpp"
#include "futils.hpp"
#include "matroskavideo.hpp"
#include "tags.hpp"
#include "tags_int.hpp"
// + standard includes
#include <array>
#include <cassert>
#include <cmath>
#include <cstdlib>
#include <iostream>
#include <limits>
// *****************************************************************************
// class member definitions
namespace Exiv2::Internal {
/*!
Tag Look-up list for Matroska Type Video Files
The Tags have been categorized in 4 categories. Which are
mentioned as a comment in front of them.
s -- Tag to be Skipped
sd -- Tag to be Skipped along with its data
u -- Tag used directly for storing metadata
ui -- Tag used only internally
see : https://www.matroska.org/technical/elements.html
https://matroska.sourceforge.net/technical/specs/chapters/index.html
*/
enum matroskaEnum : uint64_t {
ChapterDisplay = 0x0000,
TrackType = 0x0003,
ChapterString = 0x0005,
Video_Audio_CodecID = 0x0006,
TrackDefault = 0x0008,
ChapterTrackNumber = 0x0009,
Slices = 0x000e,
ChapterTrack = 0x000f,
ChapterTimeStart = 0x0011,
ChapterTimeEnd = 0x0012,
CueRefTime = 0x0016,
CueRefCluster = 0x0017,
ChapterFlagHidden = 0x0018,
Xmp_video_VideoScanTpye = 0x001a,
BlockDuration = 0x001b,
TrackLacing = 0x001c,
Xmp_audio_ChannelType = 0x001f,
BlockGroup = 0x0020,
Block = 0x0021,
BlockVirtual = 0x0022,
SimpleBlock = 0x0023,
CodecState = 0x0024,
BlockAdditional = 0x0025,
BlockMore = 0x0026,
Position = 0x0027,
CodecDecodeAll = 0x002a,
PrevSize = 0x002b,
TrackEntry = 0x002e,
EncryptedBlock = 0x002f,
Xmp_video_Width_1 = 0x0030,
CueTime = 0x0033,
Xmp_audio_SampleRate = 0x0035,
ChapterAtom = 0x0036,
CueTrackPositions = 0x0037,
TrackUsed = 0x0039,
Xmp_video_Height_1 = 0x003a,
CuePoint = 0x003b,
CRC_32 = 0x003f,
BlockAdditionalID = 0x004b,
LaceNumber = 0x004c,
FrameNumber = 0x004d,
Delay = 0x004e,
ClusterDuration = 0x004f,
TrackNumber = 0x0057,
CueReference = 0x005b,
Video = 0x0060,
Audio = 0x0061,
Timecode = 0x0067,
TimeSlice = 0x0068,
CueCodecState = 0x006a,
CueRefCodecState = 0x006b,
Void = 0x006c,
BlockAddID = 0x006e,
CueClusterPosition = 0x0071,
CueTrack = 0x0077,
ReferencePriority = 0x007a,
ReferenceBlock = 0x007b,
ReferenceVirtual = 0x007d,
Xmp_video_ContentCompressAlgo = 0x0254,
ContentCompressionSettings = 0x0255,
Xmp_video_DocType = 0x0282,
Xmp_video_DocTypeReadVersion = 0x0285,
Xmp_video_EBMLVersion = 0x0286,
Xmp_video_DocTypeVersion = 0x0287,
EBMLMaxIDLength = 0x02f2,
EBMLMaxSizeLength = 0x02f3,
Xmp_video_EBMLReadVersion = 0x02f7,
ChapterLanguage = 0x037c,
ChapterCountry = 0x037e,
SegmentFamily = 0x0444,
Xmp_video_DateUTC = 0x0461,
Xmp_video_TagLanguage = 0x047a,
Xmp_video_TagDefault = 0x0484,
TagBinary = 0x0485,
Xmp_video_TagString = 0x0487,
Xmp_video_Duration = 0x0489,
ChapterProcessPrivate = 0x050d,
ChapterFlagEnabled = 0x0598,
Xmp_video_TagName = 0x05a3,
EditionEntry = 0x05b9,
EditionUID = 0x05bc,
EditionFlagHidden = 0x05bd,
EditionFlagDefault = 0x05db,
EditionFlagOrdered = 0x05dd,
Xmp_video_AttachFileData = 0x065c,
Xmp_video_AttachFileMIME = 0x0660,
Xmp_video_AttachFileName = 0x066e,
AttachedFileReferral = 0x0675,
Xmp_video_AttachFileDesc = 0x067e,
Xmp_video_AttachFileUID = 0x06ae,
Xmp_video_ContentEncryptAlgo = 0x07e1,
ContentEncryptionKeyID = 0x07e2,
ContentSignature = 0x07e3,
ContentSignatureKeyID = 0x07e4,
Xmp_video_ContentSignAlgo_1 = 0x07e5,
Xmp_video_ContentSignHashAlgo_1 = 0x07e6,
Xmp_video_MuxingApp = 0x0d80,
Seek = 0x0dbb,
ContentEncodingOrder = 0x1031,
ContentEncodingScope = 0x1032,
Xmp_video_ContentEncodingType = 0x1033,
ContentCompression = 0x1034,
ContentEncryption = 0x1035,
CueRefNumber = 0x135f,
Xmp_video_TrackName = 0x136e,
CueBlockNumber = 0x1378,
TrackOffset = 0x137f,
SeekID = 0x13ab,
SeekPosition = 0x13ac,
Stereo3DMode = 0x13b8,
Xmp_video_CropBottom = 0x14aa,
Xmp_video_Width_2 = 0x14b0,
Xmp_video_DisplayUnit = 0x14b2,
Xmp_video_AspectRatioType = 0x14b3,
Xmp_video_Height_2 = 0x14ba,
Xmp_video_CropTop = 0x14bb,
Xmp_video_CropLeft = 0x14cc,
Xmp_video_CropRight = 0x14dd,
TrackForced = 0x15aa,
MaxBlockAdditionID = 0x15ee,
Xmp_video_WritingApp = 0x1741,
SilentTracks = 0x1854,
SilentTrackNumber = 0x18d7,
AttachedFile = 0x21a7,
ContentEncoding = 0x2240,
Xmp_audio_BitsPerSample = 0x2264,
CodecPrivate = 0x23a2,
Targets = 0x23c0,
Xmp_video_PhysicalEquivalent = 0x23c3,
TagChapterUID = 0x23c4,
TagTrackUID = 0x23c5,
TagAttachmentUID = 0x23c6,
TagEditionUID = 0x23c9,
Xmp_video_TargetType = 0x23ca,
SignedElement = 0x2532,
TrackTranslate = 0x2624,
TrackTranslateTrackID = 0x26a5,
TrackTranslateCodec = 0x26bf,
TrackTranslateEditionUID = 0x26fc,
SimpleTag = 0x27c8,
TargetTypeValue = 0x28ca,
ChapterProcessCommand = 0x2911,
ChapterProcessTime = 0x2922,
ChapterTranslate = 0x2924,
ChapterProcessData = 0x2933,
ChapterProcess = 0x2944,
ChapterProcessCodecID = 0x2955,
ChapterTranslateID = 0x29a5,
Xmp_video_TranslateCodec = 0x29bf,
ChapterTranslateEditionUID = 0x29fc,
ContentEncodings = 0x2d80,
MinCache = 0x2de7,
MaxCache = 0x2df8,
ChapterSegmentUID = 0x2e67,
ChapterSegmentEditionUID = 0x2ebc,
TrackOverlay = 0x2fab,
Tag = 0x3373,
SegmentFileName = 0x3384,
SegmentUID = 0x33a4,
ChapterUID = 0x33c4,
TrackUID = 0x33c5,
TrackAttachmentUID = 0x3446,
BlockAdditions = 0x35a1,
Xmp_audio_OutputSampleRate = 0x38b5,
Xmp_video_Title = 0x3ba9,
ChannelPositions = 0x3d7b,
SignatureElements = 0x3e5b,
SignatureElementList = 0x3e7b,
Xmp_video_ContentSignAlgo_2 = 0x3e8a,
Xmp_video_ContentSignHashAlgo_2 = 0x3e9a,
SignaturePublicKey = 0x3ea5,
Signature = 0x3eb5,
TrackLanguage = 0x2b59c,
TrackTimecodeScale = 0x3314f,
Xmp_video_FrameRate = 0x383e3,
VideoFrameRate_DefaultDuration = 0x3e383,
Video_Audio_CodecName = 0x58688,
CodecDownloadURL = 0x6b240,
TimecodeScale = 0xad7b1,
ColorSpace = 0xeb524,
Xmp_video_OpColor = 0xfb523,
CodecSettings = 0x1a9697,
CodecInfoURL = 0x1b4040,
PrevFileName = 0x1c83ab,
PrevUID = 0x1cb923,
NextFileName = 0x1e83bb,
NextUID = 0x1eb923,
Chapters = 0x43a770,
SeekHead = 0x14d9b74,
Tags = 0x254c367,
Info = 0x549a966,
Tracks = 0x654ae6b,
SegmentHeader = 0x8538067,
Attachments = 0x941a469,
EBMLHeader = 0xa45dfa3,
SignatureSlot = 0xb538667,
Cues = 0xc53bb6b,
Cluster = 0xf43b675
};
const std::array<MatroskaTag, 198> matroskaTags = {
MatroskaTag(ChapterDisplay, "ChapterDisplay", Master, Composite),
MatroskaTag(TrackType, "TrackType", Boolean, Process),
MatroskaTag(ChapterString, "ChapterString", String, Skip),
MatroskaTag(Video_Audio_CodecID, "Video.Audio.CodecID", InternalField, Skip), // process
MatroskaTag(TrackDefault, "TrackDefault", Boolean, Process),
MatroskaTag(ChapterTrackNumber, "ChapterTrackNumber", UInteger, Skip),
MatroskaTag(Slices, "Slices", Master, Composite),
MatroskaTag(ChapterTrack, "ChapterTrack", Master, Composite),
MatroskaTag(ChapterTimeStart, "ChapterTimeStart", UInteger, Skip),
MatroskaTag(ChapterTimeEnd, "ChapterTimeEnd", UInteger, Skip),
MatroskaTag(CueRefTime, "CueRefTime", UInteger, Skip),
MatroskaTag(CueRefCluster, "CueRefCluster", UInteger, Skip),
MatroskaTag(ChapterFlagHidden, "ChapterFlagHidden", UInteger, Skip),
MatroskaTag(Xmp_video_VideoScanTpye, "Xmp.video.VideoScanTpye", InternalField, Process),
MatroskaTag(BlockDuration, "BlockDuration", UInteger, Skip),
MatroskaTag(TrackLacing, "TrackLacing", Boolean, Process),
MatroskaTag(Xmp_audio_ChannelType, "Xmp.audio.ChannelType", InternalField, Process),
MatroskaTag(BlockGroup, "BlockGroup", Master, Composite),
MatroskaTag(Block, "Block", Binary, Skip),
MatroskaTag(BlockVirtual, "BlockVirtual", Binary, Skip),
MatroskaTag(SimpleBlock, "SimpleBlock", Binary, Skip),
MatroskaTag(CodecState, "CodecState", Binary, Skip),
MatroskaTag(BlockAdditional, "BlockAdditional", UInteger, Skip),
MatroskaTag(BlockMore, "BlockMore", Master, Composite),
MatroskaTag(Position, "Position", UInteger, Skip),
MatroskaTag(CodecDecodeAll, "CodecDecodeAll", Boolean, Process),
MatroskaTag(PrevSize, "PrevSize", UInteger, Skip),
MatroskaTag(TrackEntry, "TrackEntry", Master, Composite),
MatroskaTag(EncryptedBlock, "EncryptedBlock", Binary, Skip),
MatroskaTag(Xmp_video_Width_1, "Xmp.video.Width", UInteger, Process),
MatroskaTag(CueTime, "CueTime", UInteger, Skip),
MatroskaTag(Xmp_audio_SampleRate, "Xmp.audio.SampleRate", Float, Process),
MatroskaTag(ChapterAtom, "ChapterAtom", Master, Composite),
MatroskaTag(CueTrackPositions, "CueTrackPositions", Master, Composite),
MatroskaTag(TrackUsed, "TrackUsed", Boolean, Process),
MatroskaTag(Xmp_video_Height_1, "Xmp.video.Height", Integer, Process),
MatroskaTag(CuePoint, "CuePoint", Master, Composite),
MatroskaTag(CRC_32, "CRC_32", Binary, Skip),
MatroskaTag(BlockAdditionalID, "BlockAdditionalID", UInteger, Skip),
MatroskaTag(LaceNumber, "LaceNumber", UInteger, Skip),
MatroskaTag(FrameNumber, "FrameNumber", UInteger, Skip),
MatroskaTag(Delay, "Delay", UInteger, Skip),
MatroskaTag(ClusterDuration, "ClusterDuration", Float, Skip),
MatroskaTag(TrackNumber, "Xmp.video.TotalStream", String, Process),
MatroskaTag(CueReference, "CueReference", Master, Composite),
MatroskaTag(Video, "Video", Master, Composite),
MatroskaTag(Audio, "Audio", Master, Composite),
MatroskaTag(Timecode, "Timecode", UInteger, Skip),
MatroskaTag(TimeSlice, "TimeSlice", Master, Composite),
MatroskaTag(CueCodecState, "CueCodecState", UInteger, Skip),
MatroskaTag(CueRefCodecState, "CueRefCodecState", UInteger, Skip),
MatroskaTag(Void, "Void", Binary, Skip),
MatroskaTag(BlockAddID, "BlockAddID", UInteger, Skip),
MatroskaTag(CueClusterPosition, "CueClusterPosition", UInteger, Skip),
MatroskaTag(CueTrack, "CueTrack", UInteger, Skip),
MatroskaTag(ReferencePriority, "ReferencePriority", UInteger, Skip),
MatroskaTag(ReferenceBlock, "ReferenceBlock", Integer, Skip),
MatroskaTag(ReferenceVirtual, "ReferenceVirtual", Integer, Skip),
MatroskaTag(Xmp_video_ContentCompressAlgo, "Xmp.video.ContentCompressAlgo", InternalField, Process),
MatroskaTag(ContentCompressionSettings, "ContentCompressionSettings", Binary, Skip),
MatroskaTag(Xmp_video_DocType, "Xmp.video.DocType", String, Process),
MatroskaTag(Xmp_video_DocTypeReadVersion, "Xmp.video.DocTypeReadVersion", Integer, Process),
MatroskaTag(Xmp_video_EBMLVersion, "Xmp.video.EBMLVersion", Integer, Process),
MatroskaTag(Xmp_video_DocTypeVersion, "Xmp.video.DocTypeVersion", Integer, Process),
MatroskaTag(EBMLMaxIDLength, "EBMLMaxIDLength", UInteger, Skip),
MatroskaTag(EBMLMaxSizeLength, "EBMLMaxSizeLength", UInteger, Skip),
MatroskaTag(Xmp_video_EBMLReadVersion, "Xmp.video.EBMLReadVersion", UInteger, Process),
MatroskaTag(ChapterLanguage, "ChapterLanguage", String, Skip),
MatroskaTag(ChapterCountry, "ChapterCountry", Utf8, Skip),
MatroskaTag(SegmentFamily, "SegmentFamily", Binary, Skip),
MatroskaTag(Xmp_video_DateUTC, "Xmp.video.DateUTC", Date, Process),
MatroskaTag(Xmp_video_TagLanguage, "Xmp.video.TagLanguage", String, Process),
MatroskaTag(Xmp_video_TagDefault, "Xmp.video.TagDefault", Boolean, Process),
MatroskaTag(TagBinary, "TagBinary", Binary, Skip),
MatroskaTag(Xmp_video_TagString, "Xmp.video.TagString", String, Process),
MatroskaTag(Xmp_video_Duration, "Xmp.video.Duration", Date, Process),
MatroskaTag(ChapterProcessPrivate, "ChapterProcessPrivate", Master, Skip),
MatroskaTag(ChapterFlagEnabled, "ChapterFlagEnabled", Boolean, Skip),
MatroskaTag(Xmp_video_TagName, "Xmp.video.TagName", String, Process),
MatroskaTag(EditionEntry, "EditionEntry", Master, Composite),
MatroskaTag(EditionUID, "EditionUID", UInteger, Skip),
MatroskaTag(EditionFlagHidden, "EditionFlagHidden", Boolean, Skip),
MatroskaTag(EditionFlagDefault, "EditionFlagDefault", Boolean, Skip),
MatroskaTag(EditionFlagOrdered, "EditionFlagOrdered", Boolean, Skip),
MatroskaTag(Xmp_video_AttachFileData, "Xmp.video.AttachFileData", String, Process),
MatroskaTag(Xmp_video_AttachFileMIME, "Xmp.video.AttachFileMIME", String, Process),
MatroskaTag(Xmp_video_AttachFileName, "Xmp.video.AttachFileName", String, Process),
MatroskaTag(AttachedFileReferral, "AttachedFileReferral", Binary, Skip),
MatroskaTag(Xmp_video_AttachFileDesc, "Xmp.video.AttachFileDesc", String, Process),
MatroskaTag(Xmp_video_AttachFileUID, "Xmp.video.AttachFileUID", UInteger, Process),
MatroskaTag(Xmp_video_ContentEncryptAlgo, "Xmp.video.ContentEncryptAlgo", InternalField, Process),
MatroskaTag(ContentEncryptionKeyID, "ContentEncryptionKeyID", Binary, Skip),
MatroskaTag(ContentSignature, "ContentSignature", Binary, Skip),
MatroskaTag(ContentSignatureKeyID, "ContentSignatureKeyID", Binary, Skip),
MatroskaTag(Xmp_video_ContentSignAlgo_1, "Xmp.video.ContentSignAlgo", InternalField, Process),
MatroskaTag(Xmp_video_ContentSignHashAlgo_1, "Xmp.video.ContentSignHashAlgo", InternalField, Process),
MatroskaTag(Xmp_video_MuxingApp, "Xmp.video.MuxingApp", String, Process),
MatroskaTag(Seek, "Seek", Master, Composite),
MatroskaTag(ContentEncodingOrder, "ContentEncodingOrder", UInteger, Skip),
MatroskaTag(ContentEncodingScope, "ContentEncodingScope", UInteger, Skip),
MatroskaTag(Xmp_video_ContentEncodingType, "Xmp.video.ContentEncodingType", InternalField, Process),
MatroskaTag(ContentCompression, "ContentCompression", Master, Composite),
MatroskaTag(ContentEncryption, "ContentEncryption", Master, Composite),
MatroskaTag(CueRefNumber, "CueRefNumber", UInteger, Skip),
MatroskaTag(Xmp_video_TrackName, "Xmp.video.TrackName", String, Process),
MatroskaTag(CueBlockNumber, "CueBlockNumber", UInteger, Skip),
MatroskaTag(TrackOffset, "TrackOffset", Integer, Skip),
MatroskaTag(SeekID, "SeekID", Binary, Skip),
MatroskaTag(SeekPosition, "SeekPosition", UInteger, Skip),
MatroskaTag(Stereo3DMode, "Stereo3DMode", UInteger, Skip),
MatroskaTag(Xmp_video_CropBottom, "Xmp.video.CropBottom", Integer, Process),
MatroskaTag(Xmp_video_Width_2, "Xmp.video.Width", Integer, Process),
MatroskaTag(Xmp_video_DisplayUnit, "Xmp.video.DisplayUnit", InternalField, Process),
MatroskaTag(Xmp_video_AspectRatioType, "Xmp.video.AspectRatioType", InternalField, Process),
MatroskaTag(Xmp_video_Height_2, "Xmp.video.Height", Integer, Process),
MatroskaTag(Xmp_video_CropTop, "Xmp.video.CropTop", Integer, Process),
MatroskaTag(Xmp_video_CropLeft, "Xmp.video.CropLeft", Integer, Process),
MatroskaTag(Xmp_video_CropRight, "Xmp.video.CropRight", Integer, Process),
MatroskaTag(TrackForced, "TrackForced", Boolean, Process),
MatroskaTag(MaxBlockAdditionID, "MaxBlockAdditionID", UInteger, Skip),
MatroskaTag(Xmp_video_WritingApp, "Xmp.video.WritingApp", String, Process),
MatroskaTag(SilentTracks, "SilentTracks", Master, Composite),
MatroskaTag(SilentTrackNumber, "SilentTrackNumber", UInteger, Skip),
MatroskaTag(AttachedFile, "AttachedFile", Master, Composite),
MatroskaTag(ContentEncoding, "ContentEncoding", Master, Composite),
MatroskaTag(Xmp_audio_BitsPerSample, "Xmp.audio.BitsPerSample", Integer, Process),
MatroskaTag(CodecPrivate, "CodecPrivate", Binary, Skip),
MatroskaTag(Targets, "Targets", Master, Composite),
MatroskaTag(Xmp_video_PhysicalEquivalent, "Xmp.video.PhysicalEquivalent", InternalField, Process),
MatroskaTag(TagChapterUID, "TagChapterUID", UInteger, Skip),
MatroskaTag(TagTrackUID, "TagTrackUID", UInteger, Skip),
MatroskaTag(TagAttachmentUID, "TagAttachmentUID", UInteger, Skip),
MatroskaTag(TagEditionUID, "TagEditionUID", UInteger, Skip),
MatroskaTag(Xmp_video_TargetType, "Xmp.video.TargetType", String, Process),
MatroskaTag(SignedElement, "SignedElement", Binary, Skip),
MatroskaTag(TrackTranslate, "TrackTranslate", Master, Composite),
MatroskaTag(TrackTranslateTrackID, "TrackTranslateTrackID", Binary, Skip),
MatroskaTag(TrackTranslateCodec, "TrackTranslateCodec", UInteger, Skip),
MatroskaTag(TrackTranslateEditionUID, "TrackTranslateEditionUID", UInteger, Skip),
MatroskaTag(SimpleTag, "SimpleTag", Master, Composite),
MatroskaTag(TargetTypeValue, "TargetTypeValue", UInteger, Skip),
MatroskaTag(ChapterProcessCommand, "ChapterProcessCommand", Master, Composite),
MatroskaTag(ChapterProcessTime, "ChapterProcessTime", UInteger, Skip),
MatroskaTag(ChapterTranslate, "ChapterTranslate", Master, Composite),
MatroskaTag(ChapterProcessData, "ChapterProcessData", Binary, Skip),
MatroskaTag(ChapterProcess, "ChapterProcess", Master, Composite),
MatroskaTag(ChapterProcessCodecID, "ChapterProcessCodecID", UInteger, Skip),
MatroskaTag(ChapterTranslateID, "ChapterTranslateID", Binary, Skip),
MatroskaTag(Xmp_video_TranslateCodec, "Xmp.video.TranslateCodec", InternalField, Process),
MatroskaTag(ChapterTranslateEditionUID, "ChapterTranslateEditionUID", UInteger, Skip),
MatroskaTag(ContentEncodings, "ContentEncodings", Master, Composite),
MatroskaTag(MinCache, "MinCache", UInteger, Skip),
MatroskaTag(MaxCache, "MaxCache", UInteger, Skip),
MatroskaTag(ChapterSegmentUID, "ChapterSegmentUID", Binary, Skip),
MatroskaTag(ChapterSegmentEditionUID, "ChapterSegmentEditionUID", UInteger, Skip),
MatroskaTag(TrackOverlay, "TrackOverlay", UInteger, Skip),
MatroskaTag(Tag, "Tag", Master, Composite),
MatroskaTag(SegmentFileName, "SegmentFileName", Utf8, Skip),
MatroskaTag(SegmentUID, "SegmentUID", Binary, Skip),
MatroskaTag(ChapterUID, "ChapterUID", UInteger, Skip),
MatroskaTag(TrackUID, "TrackUID", UInteger, Skip),
MatroskaTag(TrackAttachmentUID, "TrackAttachmentUID", UInteger, Skip),
MatroskaTag(BlockAdditions, "BlockAdditions", Master, Composite),
MatroskaTag(Xmp_audio_OutputSampleRate, "Xmp.audio.OutputSampleRate", Float, Process),
MatroskaTag(Xmp_video_Title, "Xmp.video.Title", String, Process),
MatroskaTag(ChannelPositions, "ChannelPositions", Binary, Skip),
MatroskaTag(SignatureElements, "SignatureElements", Master, Composite),
MatroskaTag(SignatureElementList, "SignatureElementList", Master, Composite),
MatroskaTag(Xmp_video_ContentSignAlgo_2, "Xmp.video.ContentSignAlgo", InternalField, Process),
MatroskaTag(Xmp_video_ContentSignHashAlgo_2, "Xmp.video.ContentSignHashAlgo", InternalField, Process),
MatroskaTag(SignaturePublicKey, "SignaturePublicKey", Binary, Skip),
MatroskaTag(Signature, "Signature", Binary, Skip),
MatroskaTag(TrackLanguage, "TrackLanguage", String,
Skip), // Process : see values here https://www.loc.gov/standards/iso639-2/php/code_list.php
MatroskaTag(TrackTimecodeScale, "TrackTimecodeScale", Float, Skip),
MatroskaTag(Xmp_video_FrameRate, "Xmp.video.FrameRate", Float, Process),
MatroskaTag(VideoFrameRate_DefaultDuration, "VideoFrameRate.DefaultDuration", Float, Skip),
MatroskaTag(Video_Audio_CodecName, "Video.Audio.CodecName", InternalField, Process),
MatroskaTag(CodecDownloadURL, "CodecDownloadURL", InternalField, Process),
MatroskaTag(TimecodeScale, "Xmp.video.TimecodeScale", Date, Process),
MatroskaTag(ColorSpace, "ColorSpace", String, Process),
MatroskaTag(Xmp_video_OpColor, "Xmp.video.OpColor", Float, Skip),
MatroskaTag(CodecSettings, "CodecSettings", Boolean, Process),
MatroskaTag(CodecInfoURL, "CodecInfoURL", InternalField, Process),
MatroskaTag(PrevFileName, "PrevFileName", Utf8, Skip),
MatroskaTag(PrevUID, "PrevUID", Binary, Skip),
MatroskaTag(NextFileName, "NextFileName", Utf8, Skip),
MatroskaTag(NextUID, "NextUID", Binary, Skip),
MatroskaTag(Chapters, "Chapters", Master, Skip),
MatroskaTag(SeekHead, "SeekHead", Master, Composite),
MatroskaTag(Tags, "Tags", Master, Composite),
MatroskaTag(Info, "Info", Master, Composite),
MatroskaTag(Tracks, "Tracks", Master, Composite),
MatroskaTag(SegmentHeader, "SegmentHeader", Master, Composite),
MatroskaTag(Attachments, "Attachments", Master, Composite),
MatroskaTag(EBMLHeader, "EBMLHeader", Master, Composite),
MatroskaTag(SignatureSlot, "SignatureSlot", Master, Composite),
MatroskaTag(Cues, "Cues", Master, Composite),
MatroskaTag(Cluster, "Cluster", Master, Composite)};
std::array<MatroskaTag, 7> matroskaTrackType = {
MatroskaTag(0x1, "Video"), MatroskaTag(0x2, "Audio"), MatroskaTag(0x3, "Complex"), MatroskaTag(0x10, "Logo"),
MatroskaTag(0x11, "Subtitle"), MatroskaTag(0x12, "Buttons"), MatroskaTag(0x20, "Control")};
const std::array<MatroskaTag, 4> compressionAlgorithm = {MatroskaTag(0, "zlib "), MatroskaTag(1, "bzlib"),
MatroskaTag(2, "lzo1x"), MatroskaTag(3, "Header Stripping")};
const std::array<MatroskaTag, 4> audioChannels = {MatroskaTag(1, "Mono"), MatroskaTag(2, "Stereo"),
MatroskaTag(5, "5.1 Surround Sound"),
MatroskaTag(7, "7.1 Surround Sound")};
const std::array<MatroskaTag, 5> displayUnit = {MatroskaTag(0x0, "Pixels"), MatroskaTag(0x1, "cm"),
MatroskaTag(0x2, "inches"), MatroskaTag(0x3, "display aspect ratio"),
MatroskaTag(0x2, "unknown")};
const std::array<MatroskaTag, 6> encryptionAlgorithm = {MatroskaTag(0, "Not Encrypted"), MatroskaTag(1, "DES"),
MatroskaTag(2, "3DES"), MatroskaTag(3, "Twofish"),
MatroskaTag(4, "Blowfish"), MatroskaTag(5, "AES")};
const std::array<MatroskaTag, 7> chapterPhysicalEquivalent = {
MatroskaTag(10, "Index"), MatroskaTag(20, "Track"), MatroskaTag(30, "Session"), MatroskaTag(40, "Layer"),
MatroskaTag(50, "Side"), MatroskaTag(60, "CD / DVD"), MatroskaTag(70, "Set / Package")};
const std::array<MatroskaTag, 2> encodingType = {MatroskaTag(0, "Compression"), MatroskaTag(1, "Encryption")};
const std::array<MatroskaTag, 2> videoScanType = {MatroskaTag(0, "Progressive"), MatroskaTag(1, "Interlaced")};
const std::array<MatroskaTag, 2> chapterTranslateCodec = {MatroskaTag(0, "Matroska Script"),
MatroskaTag(1, "DVD Menu")};
const std::array<MatroskaTag, 3> aspectRatioType = {MatroskaTag(0, "Free Resizing"),
MatroskaTag(1, "Keep Aspect Ratio"), MatroskaTag(2, "Fixed")};
const std::array<MatroskaTag, 2> contentSignatureAlgorithm = {MatroskaTag(0, "Not Signed"), MatroskaTag(1, "RSA")};
const std::array<MatroskaTag, 3> contentSignatureHashAlgorithm = {MatroskaTag(0, "Not Signed"),
MatroskaTag(1, "SHA1-160"), MatroskaTag(2, "MD5")};
const std::array<MatroskaTag, 3> trackEnable = {MatroskaTag(0x1, "Xmp.video.Enabled"),
MatroskaTag(0x2, "Xmp.audio.Enabled"),
MatroskaTag(0x11, "Xmp.video.SubTEnabled")};
const std::array<MatroskaTag, 3> defaultOn = {MatroskaTag(0x1, "Xmp.video.DefaultOn"),
MatroskaTag(0x2, "Xmp.audio.DefaultOn"),
MatroskaTag(0x11, "Xmp.video.SubTDefaultOn")};
const std::array<MatroskaTag, 3> trackForced = {MatroskaTag(0x1, "Xmp.video.TrackForced"),
MatroskaTag(0x2, "Xmp.audio.TrackForced"),
MatroskaTag(0x11, "Xmp.video.SubTTrackForced")};
const std::array<MatroskaTag, 3> trackLacing = {MatroskaTag(0x1, "Xmp.video.TrackLacing"),
MatroskaTag(0x2, "Xmp.audio.TrackLacing"),
MatroskaTag(0x11, "Xmp.video.SubTTrackLacing")};
const std::array<MatroskaTag, 3> codecDecodeAll = {MatroskaTag(0x1, "Xmp.video.CodecDecodeAll"),
MatroskaTag(0x2, "Xmp.audio.CodecDecodeAll"),
MatroskaTag(0x11, "Xmp.video.SubTCodecDecodeAll")};
const std::array<MatroskaTag, 3> codecDownloadUrl = {MatroskaTag(0x1, "Xmp.video.CodecDownloadUrl"),
MatroskaTag(0x2, "Xmp.audio.CodecDownloadUrl"),
MatroskaTag(0x11, "Xmp.video.SubTCodecDownloadUrl")};
const std::array<MatroskaTag, 3> codecSettings = {MatroskaTag(0x1, "Xmp.video.CodecSettings"),
MatroskaTag(0x2, "Xmp.audio.CodecSettings"),
MatroskaTag(0x11, "Xmp.video.SubTCodecSettings")};
const std::array<MatroskaTag, 3> trackCodec = {MatroskaTag(0x1, "Xmp.video.Codec"),
MatroskaTag(0x2, "Xmp.audio.Compressor"),
MatroskaTag(0x11, "Xmp.video.SubTCodec")};
const std::array<MatroskaTag, 3> codecInfo = {MatroskaTag(0x1, "Xmp.video.CodecInfo"),
MatroskaTag(0x2, "Xmp.audio.CodecInfo"),
MatroskaTag(0x11, "Xmp.video.SubTCodecInfo")};
const std::array<MatroskaTag, 2> streamRate = {MatroskaTag(0x1, "Xmp.video.FrameRate"),
MatroskaTag(0x2, "Xmp.audio.DefaultDuration")};
/*!
@brief Function used to calculate Tags, Tags may comprise of more than
one byte. The first byte calculates size of the Tag and the remaining
bytes are used to calculate the rest of the Tag.
Returns Tag Value.
*/
[[nodiscard]] size_t returnTagValue(const byte* buf, size_t size) {
assert(size > 0 && size <= 8);
size_t b0 = buf[0] & (0xff >> size);
size_t tag = b0 << ((size - 1) * 8);
for (size_t i = 1; i < size; ++i) {
tag |= static_cast<size_t>(buf[i]) << ((size - i - 1) * 8);
}
return tag;
}
/*!
@brief Function used to convert buffer data into Integeral information,
information stored in BigEndian format
*/
[[nodiscard]] bool convertToUint64(const byte* buf, size_t size, uint64_t& value) {
uint64_t ret = 0;
for (size_t i = 0; i < size; ++i) {
ret |= static_cast<uint64_t>(buf[i]) << ((size - i - 1) * 8);
}
if (ret < std::numeric_limits<uint64_t>::min())
return false;
if (ret > std::numeric_limits<uint64_t>::max())
return false;
value = ret;
return true;
}
} // namespace Exiv2::Internal
namespace Exiv2 {
using namespace Exiv2::Internal;
MatroskaVideo::MatroskaVideo(BasicIo::UniquePtr io) : Image(ImageType::mkv, mdNone, std::move(io)) {
} // MatroskaVideo::MatroskaVideo
std::string MatroskaVideo::mimeType() const {
return "video/matroska";
}
void MatroskaVideo::writeMetadata() {
}
void MatroskaVideo::readMetadata() {
if (io_->open() != 0)
throw Error(ErrorCode::kerDataSourceOpenFailed, io_->path(), strError());
// Ensure that this is the correct image type
if (!isMkvType(*io_, false)) {
if (io_->error() || io_->eof())
throw Error(ErrorCode::kerFailedToReadImageData);
throw Error(ErrorCode::kerNotAnImage, "Matroska");
}
IoCloser closer(*io_);
clearMetadata();
continueTraversing_ = true;
height_ = width_ = 1;
xmpData_["Xmp.video.FileName"] = io_->path();
xmpData_["Xmp.video.FileSize"] = io_->size() / bytesMB;
xmpData_["Xmp.video.MimeType"] = mimeType();
while (continueTraversing_)
decodeBlock();
aspectRatio();
}
void MatroskaVideo::decodeBlock() {
byte buf[8];
io_->read(buf, 1);
if (io_->eof()) {
continueTraversing_ = false;
return;
}
uint32_t block_size = findBlockSize(buf[0]); // 0-8
if (block_size > 0)
io_->read(buf + 1, block_size - 1);
auto tag_id = returnTagValue(buf, block_size);
const MatroskaTag* tag = findTag(matroskaTags, tag_id);
if (!tag) {
continueTraversing_ = false;
return;
}
// tag->dump(std::cout);
if (tag->_id == Cues || tag->_id == Cluster) {
continueTraversing_ = false;
return;
}
io_->read(buf, 1);
block_size = findBlockSize(buf[0]); // 0-8
if (block_size > 0)
io_->read(buf + 1, block_size - 1);
size_t size = returnTagValue(buf, block_size);
if (tag->isComposite() && !tag->isSkipped())
return;
const size_t bufMaxSize = 200;
#ifndef SUPPRESS_WARNINGS
if (!tag->isSkipped() && size > bufMaxSize) {
EXV_WARNING << "Size " << size << " of Matroska tag 0x" << std::hex << tag->_id << std::dec << " is greater than "
<< bufMaxSize << ": ignoring it.\n";
}
#endif
if (tag->isSkipped() || size > bufMaxSize) {
io_->seek(size, BasicIo::cur);
return;
}
DataBuf buf2(bufMaxSize + 1);
io_->read(buf2.data(), size);
switch (tag->_type) {
case InternalField:
decodeInternalTags(tag, buf2.data(), size);
break;
case String:
case Utf8:
decodeStringTags(tag, buf2.data());
break;
case Integer:
case UInteger:
decodeIntegerTags(tag, buf2.data(), size);
break;
case Boolean:
decodeBooleanTags(tag, buf2.data(), size);
break;
case Date:
decodeDateTags(tag, buf2.data(), size);
break;
case Float:
decodeFloatTags(tag, buf2.data(), size);
break;
case Binary:
break;
case Master:
break;
default:
break;
}
} // MatroskaVideo::decodeBlock
void MatroskaVideo::decodeInternalTags(const MatroskaTag* tag, const byte* buf, size_t size) {
const MatroskaTag* internalMt = nullptr;
uint64_t key = 0;
if (!convertToUint64(buf, size, key))
return;
switch (tag->_id) {
case Xmp_video_VideoScanTpye:
internalMt = findTag(videoScanType, key);
break;
case Xmp_audio_ChannelType:
internalMt = findTag(audioChannels, key);
break;
case Xmp_video_ContentCompressAlgo:
internalMt = findTag(compressionAlgorithm, key);
break;
case Xmp_video_ContentEncryptAlgo:
internalMt = findTag(encryptionAlgorithm, key);
break;
case Xmp_video_ContentSignAlgo_1:
case Xmp_video_ContentSignAlgo_2:
internalMt = findTag(contentSignatureAlgorithm, key);
break;
case Xmp_video_ContentSignHashAlgo_1:
case Xmp_video_ContentSignHashAlgo_2:
internalMt = findTag(contentSignatureHashAlgorithm, key);
break;
case Xmp_video_ContentEncodingType:
internalMt = findTag(encodingType, key);
break;
case Xmp_video_DisplayUnit:
internalMt = findTag(displayUnit, key);
break;
case Xmp_video_AspectRatioType:
internalMt = findTag(aspectRatioType, key);
break;
case Xmp_video_PhysicalEquivalent:
internalMt = findTag(chapterPhysicalEquivalent, key);
break;
case Xmp_video_TranslateCodec:
internalMt = findTag(chapterTranslateCodec, key);
break;
case Video_Audio_CodecID:
internalMt = findTag(trackCodec, key);
break;
case Video_Audio_CodecName:
internalMt = findTag(codecInfo, key);
break;
case CodecDownloadURL:
case CodecInfoURL:
internalMt = findTag(codecDownloadUrl, key);
break;
default:
break;
}
if (internalMt) {
xmpData_[tag->_label] = internalMt->_label;
} else {
xmpData_[tag->_label] = key;
}
}
void MatroskaVideo::decodeStringTags(const MatroskaTag* tag, const byte* buf) {
if (tag->_id == TrackNumber) {
track_count_++;
xmpData_[tag->_label] = track_count_;
} else {
xmpData_[tag->_label] = buf;
}
}
void MatroskaVideo::decodeIntegerTags(const MatroskaTag* tag, const byte* buf, size_t size) {
uint64_t value = 0;
if (!convertToUint64(buf, size, value))
return;
if (tag->_id == Xmp_video_Width_1 || tag->_id == Xmp_video_Width_2)
width_ = value;
if (tag->_id == Xmp_video_Height_1 || tag->_id == Xmp_video_Height_2)
height_ = value;
xmpData_[tag->_label] = value;
}
void MatroskaVideo::decodeBooleanTags(const MatroskaTag* tag, const byte* buf, size_t size) {
std::string str("No");
const MatroskaTag* internalMt = nullptr;
uint64_t key = 0;
if (!convertToUint64(buf, size, key))
return;
switch (tag->_id) {
case TrackType: // this tags is used internally only to deduce the type of track (video or audio)
internalMt = findTag(matroskaTrackType, key);
stream_ = internalMt->_id;
internalMt = nullptr;
break;
case TrackUsed:
internalMt = findTag(trackEnable, key);
break;
case TrackDefault:
internalMt = findTag(defaultOn, key);
break;
case TrackForced:
internalMt = findTag(trackForced, key);
break;
case TrackLacing:
internalMt = findTag(trackLacing, key);
break;
case CodecDecodeAll:
internalMt = findTag(codecDecodeAll, key);
break;
case CodecSettings:
internalMt = findTag(codecSettings, key);
break;
case Xmp_video_TagDefault:
internalMt = tag;
break;
default:
break;
}
if (internalMt) {
str = "Yes";
xmpData_[internalMt->_label] = str;
}
}
void MatroskaVideo::decodeDateTags(const MatroskaTag* tag, const byte* buf, size_t size) {
int64_t duration_in_ms = 0;
uint64_t value = 0;
switch (tag->_id) {
case Xmp_video_Duration:
if (size <= 4) {
duration_in_ms =
static_cast<int64_t>(getFloat(buf, bigEndian) * static_cast<float>(time_code_scale_) * 1000.0f);
} else {
duration_in_ms = static_cast<int64_t>(getDouble(buf, bigEndian) * time_code_scale_ * 1000);
}
xmpData_[tag->_label] = duration_in_ms;
break;
case Xmp_video_DateUTC:
if (!convertToUint64(buf, size, value))
return;
duration_in_ms = value / 1000000000;
xmpData_[tag->_label] = duration_in_ms;
break;
case TimecodeScale:
if (!convertToUint64(buf, size, value))
return;
time_code_scale_ = static_cast<double>(value) / static_cast<double>(1000000000);
xmpData_[tag->_label] = time_code_scale_;
break;
default:
break;
}
}
void MatroskaVideo::decodeFloatTags(const MatroskaTag* tag, const byte* buf, size_t size) {
xmpData_[tag->_label] = getFloat(buf, bigEndian);
double frame_rate = 0;
switch (tag->_id) {
case Xmp_audio_SampleRate:
case Xmp_audio_OutputSampleRate:
xmpData_[tag->_label] = getFloat(buf, bigEndian);
break;
case VideoFrameRate_DefaultDuration:
case Xmp_video_FrameRate: {
uint64_t key = 0;
if (!convertToUint64(buf, size, key))
return;
const MatroskaTag* internalMt = findTag(streamRate, key);
if (internalMt) {
switch (stream_) {
case 1: // video
frame_rate = static_cast<double>(1000000000) / static_cast<double>(key);
break;
case 2: // audio
frame_rate = static_cast<double>(key / 1000);
break;
default:
break;
}
if (frame_rate)
xmpData_[internalMt->_label] = frame_rate;
} else
xmpData_[tag->_label] = "Variable Bit Rate";
} break;
default:
xmpData_[tag->_label] = getFloat(buf, bigEndian);
break;
}
}
void MatroskaVideo::aspectRatio() {
double aspectRatio = static_cast<double>(width_) / static_cast<double>(height_);
aspectRatio = floor(aspectRatio * 10) / 10;
xmpData_["Xmp.video.AspectRatio"] = aspectRatio;
auto aR = static_cast<int>((aspectRatio * 10.0) + 0.1);
switch (aR) {
case 13:
xmpData_["Xmp.video.AspectRatio"] = "4:3";
break;
case 17:
xmpData_["Xmp.video.AspectRatio"] = "16:9";
break;
case 10:
xmpData_["Xmp.video.AspectRatio"] = "1:1";
break;
case 16:
xmpData_["Xmp.video.AspectRatio"] = "16:10";
break;
case 22:
xmpData_["Xmp.video.AspectRatio"] = "2.21:1";
break;
case 23:
xmpData_["Xmp.video.AspectRatio"] = "2.35:1";
break;
case 12:
xmpData_["Xmp.video.AspectRatio"] = "5:4";
break;
default:
xmpData_["Xmp.video.AspectRatio"] = aspectRatio;
break;
}
}
uint32_t MatroskaVideo::findBlockSize(byte b) {
if (b & 128)
return 1;
if (b & 64)
return 2;
if (b & 32)
return 3;
if (b & 16)
return 4;
if (b & 8)
return 5;
if (b & 4)
return 6;
if (b & 2)
return 7;
if (b & 1)
return 8;
return 0;
}
Image::UniquePtr newMkvInstance(BasicIo::UniquePtr io, bool /*create*/) {
Image::UniquePtr image(new MatroskaVideo(std::move(io)));
if (!image->good()) {
image.reset();
}
return image;
}
bool isMkvType(BasicIo& iIo, bool advance) {
bool result = true;
byte tmpBuf[4];
iIo.read(tmpBuf, 4);
if (iIo.error() || iIo.eof())
return false;
if (0x1a != tmpBuf[0] || 0x45 != tmpBuf[1] || 0xdf != tmpBuf[2] || 0xa3 != tmpBuf[3]) {
result = false;
}
if (!advance || !result)
iIo.seek(0, BasicIo::beg);
return result;
}
} // namespace Exiv2