Coverage Report

Created: 2026-10-28 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/exiv2/src/matroskavideo.cpp
Line
Count
Source
1
// ***************************************************************** -*- C++ -*-
2
/*
3
 * Copyright (C) 2004-2021 Exiv2 authors
4
 * This program is part of the Exiv2 distribution.
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 2
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA.
19
 */
20
// *****************************************************************************
21
// included header files
22
#include "matroskavideo.hpp"
23
#include "basicio.hpp"
24
#include "config.h"
25
#include "enforce.hpp"
26
#include "error.hpp"
27
#include "futils.hpp"
28
#include "helper_functions.hpp"
29
30
// + standard includes
31
#include <cmath>
32
#include <cstdlib>
33
#include <iostream>
34
// *****************************************************************************
35
// class member definitions
36
namespace Exiv2::Internal {
37
38
/*!
39
  Tag Look-up list for Matroska Type Video Files
40
  The Tags have been categorized in 4 categories. Which are
41
  mentioned as a comment in front of them.
42
  s   --  Tag to be Skipped
43
  sd  --  Tag to be Skipped along with its data
44
  u   --  Tag used directly for storing metadata
45
  ui  --  Tag used only internally
46
47
  see : https://www.matroska.org/technical/elements.html
48
        https://matroska.sourceforge.net/technical/specs/chapters/index.html
49
 */
50
51
enum matroskaEnum : uint64_t {
52
  ChapterDisplay = 0x0000,
53
  TrackType = 0x0003,
54
  ChapterString = 0x0005,
55
  Video_Audio_CodecID = 0x0006,
56
  TrackDefault = 0x0008,
57
  ChapterTrackNumber = 0x0009,
58
  Slices = 0x000e,
59
  ChapterTrack = 0x000f,
60
  ChapterTimeStart = 0x0011,
61
  ChapterTimeEnd = 0x0012,
62
  CueRefTime = 0x0016,
63
  CueRefCluster = 0x0017,
64
  ChapterFlagHidden = 0x0018,
65
  Xmp_video_VideoScanTpye = 0x001a,
66
  BlockDuration = 0x001b,
67
  TrackLacing = 0x001c,
68
  Xmp_audio_ChannelType = 0x001f,
69
  BlockGroup = 0x0020,
70
  Block = 0x0021,
71
  BlockVirtual = 0x0022,
72
  SimpleBlock = 0x0023,
73
  CodecState = 0x0024,
74
  BlockAdditional = 0x0025,
75
  BlockMore = 0x0026,
76
  Position = 0x0027,
77
  CodecDecodeAll = 0x002a,
78
  PrevSize = 0x002b,
79
  TrackEntry = 0x002e,
80
  EncryptedBlock = 0x002f,
81
  Xmp_video_Width_1 = 0x0030,
82
  CueTime = 0x0033,
83
  Xmp_audio_SampleRate = 0x0035,
84
  ChapterAtom = 0x0036,
85
  CueTrackPositions = 0x0037,
86
  TrackUsed = 0x0039,
87
  Xmp_video_Height_1 = 0x003a,
88
  CuePoint = 0x003b,
89
  CRC_32 = 0x003f,
90
  BlockAdditionalID = 0x004b,
91
  LaceNumber = 0x004c,
92
  FrameNumber = 0x004d,
93
  Delay = 0x004e,
94
  ClusterDuration = 0x004f,
95
  TrackNumber = 0x0057,
96
  CueReference = 0x005b,
97
  Video = 0x0060,
98
  Audio = 0x0061,
99
  Timecode = 0x0067,
100
  TimeSlice = 0x0068,
101
  CueCodecState = 0x006a,
102
  CueRefCodecState = 0x006b,
103
  Void = 0x006c,
104
  BlockAddID = 0x006e,
105
  CueClusterPosition = 0x0071,
106
  CueTrack = 0x0077,
107
  ReferencePriority = 0x007a,
108
  ReferenceBlock = 0x007b,
109
  ReferenceVirtual = 0x007d,
110
  Xmp_video_ContentCompressAlgo = 0x0254,
111
  ContentCompressionSettings = 0x0255,
112
  Xmp_video_DocType = 0x0282,
113
  Xmp_video_DocTypeReadVersion = 0x0285,
114
  Xmp_video_EBMLVersion = 0x0286,
115
  Xmp_video_DocTypeVersion = 0x0287,
116
  EBMLMaxIDLength = 0x02f2,
117
  EBMLMaxSizeLength = 0x02f3,
118
  Xmp_video_EBMLReadVersion = 0x02f7,
119
  ChapterLanguage = 0x037c,
120
  ChapterCountry = 0x037e,
121
  SegmentFamily = 0x0444,
122
  Xmp_video_DateUTC = 0x0461,
123
  Xmp_video_TagLanguage = 0x047a,
124
  Xmp_video_TagDefault = 0x0484,
125
  TagBinary = 0x0485,
126
  Xmp_video_TagString = 0x0487,
127
  Xmp_video_Duration = 0x0489,
128
  ChapterProcessPrivate = 0x050d,
129
  ChapterFlagEnabled = 0x0598,
130
  Xmp_video_TagName = 0x05a3,
131
  EditionEntry = 0x05b9,
132
  EditionUID = 0x05bc,
133
  EditionFlagHidden = 0x05bd,
134
  EditionFlagDefault = 0x05db,
135
  EditionFlagOrdered = 0x05dd,
136
  Xmp_video_AttachFileData = 0x065c,
137
  Xmp_video_AttachFileMIME = 0x0660,
138
  Xmp_video_AttachFileName = 0x066e,
139
  AttachedFileReferral = 0x0675,
140
  Xmp_video_AttachFileDesc = 0x067e,
141
  Xmp_video_AttachFileUID = 0x06ae,
142
  Xmp_video_ContentEncryptAlgo = 0x07e1,
143
  ContentEncryptionKeyID = 0x07e2,
144
  ContentSignature = 0x07e3,
145
  ContentSignatureKeyID = 0x07e4,
146
  Xmp_video_ContentSignAlgo_1 = 0x07e5,
147
  Xmp_video_ContentSignHashAlgo_1 = 0x07e6,
148
  Xmp_video_MuxingApp = 0x0d80,
149
  Seek = 0x0dbb,
150
  ContentEncodingOrder = 0x1031,
151
  ContentEncodingScope = 0x1032,
152
  Xmp_video_ContentEncodingType = 0x1033,
153
  ContentCompression = 0x1034,
154
  ContentEncryption = 0x1035,
155
  CueRefNumber = 0x135f,
156
  Xmp_video_TrackName = 0x136e,
157
  CueBlockNumber = 0x1378,
158
  TrackOffset = 0x137f,
159
  SeekID = 0x13ab,
160
  SeekPosition = 0x13ac,
161
  Stereo3DMode = 0x13b8,
162
  Xmp_video_CropBottom = 0x14aa,
163
  Xmp_video_Width_2 = 0x14b0,
164
  Xmp_video_DisplayUnit = 0x14b2,
165
  Xmp_video_AspectRatioType = 0x14b3,
166
  Xmp_video_Height_2 = 0x14ba,
167
  Xmp_video_CropTop = 0x14bb,
168
  Xmp_video_CropLeft = 0x14cc,
169
  Xmp_video_CropRight = 0x14dd,
170
  TrackForced = 0x15aa,
171
  MaxBlockAdditionID = 0x15ee,
172
  Xmp_video_WritingApp = 0x1741,
173
  SilentTracks = 0x1854,
174
  SilentTrackNumber = 0x18d7,
175
  AttachedFile = 0x21a7,
176
  ContentEncoding = 0x2240,
177
  Xmp_audio_BitsPerSample = 0x2264,
178
  CodecPrivate = 0x23a2,
179
  Targets = 0x23c0,
180
  Xmp_video_PhysicalEquivalent = 0x23c3,
181
  TagChapterUID = 0x23c4,
182
  TagTrackUID = 0x23c5,
183
  TagAttachmentUID = 0x23c6,
184
  TagEditionUID = 0x23c9,
185
  Xmp_video_TargetType = 0x23ca,
186
  SignedElement = 0x2532,
187
  TrackTranslate = 0x2624,
188
  TrackTranslateTrackID = 0x26a5,
189
  TrackTranslateCodec = 0x26bf,
190
  TrackTranslateEditionUID = 0x26fc,
191
  SimpleTag = 0x27c8,
192
  TargetTypeValue = 0x28ca,
193
  ChapterProcessCommand = 0x2911,
194
  ChapterProcessTime = 0x2922,
195
  ChapterTranslate = 0x2924,
196
  ChapterProcessData = 0x2933,
197
  ChapterProcess = 0x2944,
198
  ChapterProcessCodecID = 0x2955,
199
  ChapterTranslateID = 0x29a5,
200
  Xmp_video_TranslateCodec = 0x29bf,
201
  ChapterTranslateEditionUID = 0x29fc,
202
  ContentEncodings = 0x2d80,
203
  MinCache = 0x2de7,
204
  MaxCache = 0x2df8,
205
  ChapterSegmentUID = 0x2e67,
206
  ChapterSegmentEditionUID = 0x2ebc,
207
  TrackOverlay = 0x2fab,
208
  Tag = 0x3373,
209
  SegmentFileName = 0x3384,
210
  SegmentUID = 0x33a4,
211
  ChapterUID = 0x33c4,
212
  TrackUID = 0x33c5,
213
  TrackAttachmentUID = 0x3446,
214
  BlockAdditions = 0x35a1,
215
  Xmp_audio_OutputSampleRate = 0x38b5,
216
  Xmp_video_Title = 0x3ba9,
217
  ChannelPositions = 0x3d7b,
218
  SignatureElements = 0x3e5b,
219
  SignatureElementList = 0x3e7b,
220
  Xmp_video_ContentSignAlgo_2 = 0x3e8a,
221
  Xmp_video_ContentSignHashAlgo_2 = 0x3e9a,
222
  SignaturePublicKey = 0x3ea5,
223
  Signature = 0x3eb5,
224
  TrackLanguage = 0x2b59c,
225
  TrackTimecodeScale = 0x3314f,
226
  Xmp_video_FrameRate = 0x383e3,
227
  VideoFrameRate_DefaultDuration = 0x3e383,
228
  Video_Audio_CodecName = 0x58688,
229
  CodecDownloadURL = 0x6b240,
230
  TimecodeScale = 0xad7b1,
231
  ColorSpace = 0xeb524,
232
  Xmp_video_OpColor = 0xfb523,
233
  CodecSettings = 0x1a9697,
234
  CodecInfoURL = 0x1b4040,
235
  PrevFileName = 0x1c83ab,
236
  PrevUID = 0x1cb923,
237
  NextFileName = 0x1e83bb,
238
  NextUID = 0x1eb923,
239
  Chapters = 0x43a770,
240
  SeekHead = 0x14d9b74,
241
  Tags = 0x254c367,
242
  Info = 0x549a966,
243
  Tracks = 0x654ae6b,
244
  SegmentHeader = 0x8538067,
245
  Attachments = 0x941a469,
246
  EBMLHeader = 0xa45dfa3,
247
  SignatureSlot = 0xb538667,
248
  Cues = 0xc53bb6b,
249
  Cluster = 0xf43b675
250
};
251
252
const MatroskaTag matroskaTags[]{
253
    {ChapterDisplay, "ChapterDisplay", Master, Composite},
254
    {TrackType, "TrackType", Boolean, Process},
255
    {ChapterString, "ChapterString", String, Skip},
256
    {Video_Audio_CodecID, "Video.Audio.CodecID", InternalField, Skip},  // process
257
    {TrackDefault, "TrackDefault", Boolean, Process},
258
    {ChapterTrackNumber, "ChapterTrackNumber", UInteger, Skip},
259
    {Slices, "Slices", Master, Composite},
260
    {ChapterTrack, "ChapterTrack", Master, Composite},
261
    {ChapterTimeStart, "ChapterTimeStart", UInteger, Skip},
262
    {ChapterTimeEnd, "ChapterTimeEnd", UInteger, Skip},
263
    {CueRefTime, "CueRefTime", UInteger, Skip},
264
    {CueRefCluster, "CueRefCluster", UInteger, Skip},
265
    {ChapterFlagHidden, "ChapterFlagHidden", UInteger, Skip},
266
    {Xmp_video_VideoScanTpye, "Xmp.video.VideoScanTpye", InternalField, Process},
267
    {BlockDuration, "BlockDuration", UInteger, Skip},
268
    {TrackLacing, "TrackLacing", Boolean, Process},
269
    {Xmp_audio_ChannelType, "Xmp.audio.ChannelType", InternalField, Process},
270
    {BlockGroup, "BlockGroup", Master, Composite},
271
    {Block, "Block", Binary, Skip},
272
    {BlockVirtual, "BlockVirtual", Binary, Skip},
273
    {SimpleBlock, "SimpleBlock", Binary, Skip},
274
    {CodecState, "CodecState", Binary, Skip},
275
    {BlockAdditional, "BlockAdditional", UInteger, Skip},
276
    {BlockMore, "BlockMore", Master, Composite},
277
    {Position, "Position", UInteger, Skip},
278
    {CodecDecodeAll, "CodecDecodeAll", Boolean, Process},
279
    {PrevSize, "PrevSize", UInteger, Skip},
280
    {TrackEntry, "TrackEntry", Master, Composite},
281
    {EncryptedBlock, "EncryptedBlock", Binary, Skip},
282
    {Xmp_video_Width_1, "Xmp.video.Width", UInteger, Process},
283
    {CueTime, "CueTime", UInteger, Skip},
284
    {Xmp_audio_SampleRate, "Xmp.audio.SampleRate", Float, Process},
285
    {ChapterAtom, "ChapterAtom", Master, Composite},
286
    {CueTrackPositions, "CueTrackPositions", Master, Composite},
287
    {TrackUsed, "TrackUsed", Boolean, Process},
288
    {Xmp_video_Height_1, "Xmp.video.Height", Integer, Process},
289
    {CuePoint, "CuePoint", Master, Composite},
290
    {CRC_32, "CRC_32", Binary, Skip},
291
    {BlockAdditionalID, "BlockAdditionalID", UInteger, Skip},
292
    {LaceNumber, "LaceNumber", UInteger, Skip},
293
    {FrameNumber, "FrameNumber", UInteger, Skip},
294
    {Delay, "Delay", UInteger, Skip},
295
    {ClusterDuration, "ClusterDuration", Float, Skip},
296
    {TrackNumber, "Xmp.video.TotalStream", String, Process},
297
    {CueReference, "CueReference", Master, Composite},
298
    {Video, "Video", Master, Composite},
299
    {Audio, "Audio", Master, Composite},
300
    {Timecode, "Timecode", UInteger, Skip},
301
    {TimeSlice, "TimeSlice", Master, Composite},
302
    {CueCodecState, "CueCodecState", UInteger, Skip},
303
    {CueRefCodecState, "CueRefCodecState", UInteger, Skip},
304
    {Void, "Void", Binary, Skip},
305
    {BlockAddID, "BlockAddID", UInteger, Skip},
306
    {CueClusterPosition, "CueClusterPosition", UInteger, Skip},
307
    {CueTrack, "CueTrack", UInteger, Skip},
308
    {ReferencePriority, "ReferencePriority", UInteger, Skip},
309
    {ReferenceBlock, "ReferenceBlock", Integer, Skip},
310
    {ReferenceVirtual, "ReferenceVirtual", Integer, Skip},
311
    {Xmp_video_ContentCompressAlgo, "Xmp.video.ContentCompressAlgo", InternalField, Process},
312
    {ContentCompressionSettings, "ContentCompressionSettings", Binary, Skip},
313
    {Xmp_video_DocType, "Xmp.video.DocType", String, Process},
314
    {Xmp_video_DocTypeReadVersion, "Xmp.video.DocTypeReadVersion", Integer, Process},
315
    {Xmp_video_EBMLVersion, "Xmp.video.EBMLVersion", Integer, Process},
316
    {Xmp_video_DocTypeVersion, "Xmp.video.DocTypeVersion", Integer, Process},
317
    {EBMLMaxIDLength, "EBMLMaxIDLength", UInteger, Skip},
318
    {EBMLMaxSizeLength, "EBMLMaxSizeLength", UInteger, Skip},
319
    {Xmp_video_EBMLReadVersion, "Xmp.video.EBMLReadVersion", UInteger, Process},
320
    {ChapterLanguage, "ChapterLanguage", String, Skip},
321
    {ChapterCountry, "ChapterCountry", Utf8, Skip},
322
    {SegmentFamily, "SegmentFamily", Binary, Skip},
323
    {Xmp_video_DateUTC, "Xmp.video.DateUTC", Date, Process},
324
    {Xmp_video_TagLanguage, "Xmp.video.TagLanguage", String, Process},
325
    {Xmp_video_TagDefault, "Xmp.video.TagDefault", Boolean, Process},
326
    {TagBinary, "TagBinary", Binary, Skip},
327
    {Xmp_video_TagString, "Xmp.video.TagString", String, Process},
328
    {Xmp_video_Duration, "Xmp.video.Duration", Date, Process},
329
    {ChapterProcessPrivate, "ChapterProcessPrivate", Master, Skip},
330
    {ChapterFlagEnabled, "ChapterFlagEnabled", Boolean, Skip},
331
    {Xmp_video_TagName, "Xmp.video.TagName", String, Process},
332
    {EditionEntry, "EditionEntry", Master, Composite},
333
    {EditionUID, "EditionUID", UInteger, Skip},
334
    {EditionFlagHidden, "EditionFlagHidden", Boolean, Skip},
335
    {EditionFlagDefault, "EditionFlagDefault", Boolean, Skip},
336
    {EditionFlagOrdered, "EditionFlagOrdered", Boolean, Skip},
337
    {Xmp_video_AttachFileData, "Xmp.video.AttachFileData", String, Process},
338
    {Xmp_video_AttachFileMIME, "Xmp.video.AttachFileMIME", String, Process},
339
    {Xmp_video_AttachFileName, "Xmp.video.AttachFileName", String, Process},
340
    {AttachedFileReferral, "AttachedFileReferral", Binary, Skip},
341
    {Xmp_video_AttachFileDesc, "Xmp.video.AttachFileDesc", String, Process},
342
    {Xmp_video_AttachFileUID, "Xmp.video.AttachFileUID", UInteger, Process},
343
    {Xmp_video_ContentEncryptAlgo, "Xmp.video.ContentEncryptAlgo", InternalField, Process},
344
    {ContentEncryptionKeyID, "ContentEncryptionKeyID", Binary, Skip},
345
    {ContentSignature, "ContentSignature", Binary, Skip},
346
    {ContentSignatureKeyID, "ContentSignatureKeyID", Binary, Skip},
347
    {Xmp_video_ContentSignAlgo_1, "Xmp.video.ContentSignAlgo", InternalField, Process},
348
    {Xmp_video_ContentSignHashAlgo_1, "Xmp.video.ContentSignHashAlgo", InternalField, Process},
349
    {Xmp_video_MuxingApp, "Xmp.video.MuxingApp", String, Process},
350
    {Seek, "Seek", Master, Composite},
351
    {ContentEncodingOrder, "ContentEncodingOrder", UInteger, Skip},
352
    {ContentEncodingScope, "ContentEncodingScope", UInteger, Skip},
353
    {Xmp_video_ContentEncodingType, "Xmp.video.ContentEncodingType", InternalField, Process},
354
    {ContentCompression, "ContentCompression", Master, Composite},
355
    {ContentEncryption, "ContentEncryption", Master, Composite},
356
    {CueRefNumber, "CueRefNumber", UInteger, Skip},
357
    {Xmp_video_TrackName, "Xmp.video.TrackName", String, Process},
358
    {CueBlockNumber, "CueBlockNumber", UInteger, Skip},
359
    {TrackOffset, "TrackOffset", Integer, Skip},
360
    {SeekID, "SeekID", Binary, Skip},
361
    {SeekPosition, "SeekPosition", UInteger, Skip},
362
    {Stereo3DMode, "Stereo3DMode", UInteger, Skip},
363
    {Xmp_video_CropBottom, "Xmp.video.CropBottom", Integer, Process},
364
    {Xmp_video_Width_2, "Xmp.video.Width", Integer, Process},
365
    {Xmp_video_DisplayUnit, "Xmp.video.DisplayUnit", InternalField, Process},
366
    {Xmp_video_AspectRatioType, "Xmp.video.AspectRatioType", InternalField, Process},
367
    {Xmp_video_Height_2, "Xmp.video.Height", Integer, Process},
368
    {Xmp_video_CropTop, "Xmp.video.CropTop", Integer, Process},
369
    {Xmp_video_CropLeft, "Xmp.video.CropLeft", Integer, Process},
370
    {Xmp_video_CropRight, "Xmp.video.CropRight", Integer, Process},
371
    {TrackForced, "TrackForced", Boolean, Process},
372
    {MaxBlockAdditionID, "MaxBlockAdditionID", UInteger, Skip},
373
    {Xmp_video_WritingApp, "Xmp.video.WritingApp", String, Process},
374
    {SilentTracks, "SilentTracks", Master, Composite},
375
    {SilentTrackNumber, "SilentTrackNumber", UInteger, Skip},
376
    {AttachedFile, "AttachedFile", Master, Composite},
377
    {ContentEncoding, "ContentEncoding", Master, Composite},
378
    {Xmp_audio_BitsPerSample, "Xmp.audio.BitsPerSample", Integer, Process},
379
    {CodecPrivate, "CodecPrivate", Binary, Skip},
380
    {Targets, "Targets", Master, Composite},
381
    {Xmp_video_PhysicalEquivalent, "Xmp.video.PhysicalEquivalent", InternalField, Process},
382
    {TagChapterUID, "TagChapterUID", UInteger, Skip},
383
    {TagTrackUID, "TagTrackUID", UInteger, Skip},
384
    {TagAttachmentUID, "TagAttachmentUID", UInteger, Skip},
385
    {TagEditionUID, "TagEditionUID", UInteger, Skip},
386
    {Xmp_video_TargetType, "Xmp.video.TargetType", String, Process},
387
    {SignedElement, "SignedElement", Binary, Skip},
388
    {TrackTranslate, "TrackTranslate", Master, Composite},
389
    {TrackTranslateTrackID, "TrackTranslateTrackID", Binary, Skip},
390
    {TrackTranslateCodec, "TrackTranslateCodec", UInteger, Skip},
391
    {TrackTranslateEditionUID, "TrackTranslateEditionUID", UInteger, Skip},
392
    {SimpleTag, "SimpleTag", Master, Composite},
393
    {TargetTypeValue, "TargetTypeValue", UInteger, Skip},
394
    {ChapterProcessCommand, "ChapterProcessCommand", Master, Composite},
395
    {ChapterProcessTime, "ChapterProcessTime", UInteger, Skip},
396
    {ChapterTranslate, "ChapterTranslate", Master, Composite},
397
    {ChapterProcessData, "ChapterProcessData", Binary, Skip},
398
    {ChapterProcess, "ChapterProcess", Master, Composite},
399
    {ChapterProcessCodecID, "ChapterProcessCodecID", UInteger, Skip},
400
    {ChapterTranslateID, "ChapterTranslateID", Binary, Skip},
401
    {Xmp_video_TranslateCodec, "Xmp.video.TranslateCodec", InternalField, Process},
402
    {ChapterTranslateEditionUID, "ChapterTranslateEditionUID", UInteger, Skip},
403
    {ContentEncodings, "ContentEncodings", Master, Composite},
404
    {MinCache, "MinCache", UInteger, Skip},
405
    {MaxCache, "MaxCache", UInteger, Skip},
406
    {ChapterSegmentUID, "ChapterSegmentUID", Binary, Skip},
407
    {ChapterSegmentEditionUID, "ChapterSegmentEditionUID", UInteger, Skip},
408
    {TrackOverlay, "TrackOverlay", UInteger, Skip},
409
    {Tag, "Tag", Master, Composite},
410
    {SegmentFileName, "SegmentFileName", Utf8, Skip},
411
    {SegmentUID, "SegmentUID", Binary, Skip},
412
    {ChapterUID, "ChapterUID", UInteger, Skip},
413
    {TrackUID, "TrackUID", UInteger, Skip},
414
    {TrackAttachmentUID, "TrackAttachmentUID", UInteger, Skip},
415
    {BlockAdditions, "BlockAdditions", Master, Composite},
416
    {Xmp_audio_OutputSampleRate, "Xmp.audio.OutputSampleRate", Float, Process},
417
    {Xmp_video_Title, "Xmp.video.Title", String, Process},
418
    {ChannelPositions, "ChannelPositions", Binary, Skip},
419
    {SignatureElements, "SignatureElements", Master, Composite},
420
    {SignatureElementList, "SignatureElementList", Master, Composite},
421
    {Xmp_video_ContentSignAlgo_2, "Xmp.video.ContentSignAlgo", InternalField, Process},
422
    {Xmp_video_ContentSignHashAlgo_2, "Xmp.video.ContentSignHashAlgo", InternalField, Process},
423
    {SignaturePublicKey, "SignaturePublicKey", Binary, Skip},
424
    {Signature, "Signature", Binary, Skip},
425
    {TrackLanguage, "TrackLanguage", String,
426
     Skip},  // Process :  see values here https://www.loc.gov/standards/iso639-2/php/code_list.php
427
    {TrackTimecodeScale, "TrackTimecodeScale", Float, Skip},
428
    {Xmp_video_FrameRate, "Xmp.video.FrameRate", Float, Process},
429
    {VideoFrameRate_DefaultDuration, "VideoFrameRate.DefaultDuration", Float, Skip},
430
    {Video_Audio_CodecName, "Video.Audio.CodecName", InternalField, Process},
431
    {CodecDownloadURL, "CodecDownloadURL", InternalField, Process},
432
    {TimecodeScale, "Xmp.video.TimecodeScale", Date, Process},
433
    {ColorSpace, "ColorSpace", String, Process},
434
    {Xmp_video_OpColor, "Xmp.video.OpColor", Float, Skip},
435
    {CodecSettings, "CodecSettings", Boolean, Process},
436
    {CodecInfoURL, "CodecInfoURL", InternalField, Process},
437
    {PrevFileName, "PrevFileName", Utf8, Skip},
438
    {PrevUID, "PrevUID", Binary, Skip},
439
    {NextFileName, "NextFileName", Utf8, Skip},
440
    {NextUID, "NextUID", Binary, Skip},
441
    {Chapters, "Chapters", Master, Skip},
442
    {SeekHead, "SeekHead", Master, Composite},
443
    {Tags, "Tags", Master, Composite},
444
    {Info, "Info", Master, Composite},
445
    {Tracks, "Tracks", Master, Composite},
446
    {SegmentHeader, "SegmentHeader", Master, Composite},
447
    {Attachments, "Attachments", Master, Composite},
448
    {EBMLHeader, "EBMLHeader", Master, Composite},
449
    {SignatureSlot, "SignatureSlot", Master, Composite},
450
    {Cues, "Cues", Master, Composite},
451
    {Cluster, "Cluster", Master, Composite},
452
};
453
454
const MatroskaTag matroskaTrackType[] = {
455
    {0x1, "Video"},     {0x2, "Audio"},    {0x3, "Complex"},  {0x10, "Logo"},
456
    {0x11, "Subtitle"}, {0x12, "Buttons"}, {0x20, "Control"},
457
};
458
459
const MatroskaTag compressionAlgorithm[] = {
460
    {0, "zlib "},
461
    {1, "bzlib"},
462
    {2, "lzo1x"},
463
    {3, "Header Stripping"},
464
};
465
466
const MatroskaTag audioChannels[] = {
467
    {1, "Mono"},
468
    {2, "Stereo"},
469
    {5, "5.1 Surround Sound"},
470
    {7, "7.1 Surround Sound"},
471
};
472
473
const MatroskaTag displayUnit[] = {
474
    {0x0, "Pixels"}, {0x1, "cm"}, {0x2, "inches"}, {0x3, "display aspect ratio"}, {0x2, "unknown"},
475
};
476
477
const MatroskaTag encryptionAlgorithm[] = {
478
    {0, "Not Encrypted"}, {1, "DES"}, {2, "3DES"}, {3, "Twofish"}, {4, "Blowfish"}, {5, "AES"},
479
};
480
481
const MatroskaTag chapterPhysicalEquivalent[] = {
482
    {10, "Index"}, {20, "Track"}, {30, "Session"}, {40, "Layer"}, {50, "Side"}, {60, "CD / DVD"}, {70, "Set / Package"},
483
};
484
485
const MatroskaTag encodingType[] = {
486
    {0, "Compression"},
487
    {1, "Encryption"},
488
};
489
490
const MatroskaTag videoScanType[] = {
491
    {0, "Progressive"},
492
    {1, "Interlaced"},
493
};
494
495
const MatroskaTag chapterTranslateCodec[] = {
496
    {0, "Matroska Script"},
497
    {1, "DVD Menu"},
498
};
499
500
const MatroskaTag aspectRatioType[] = {
501
    {0, "Free Resizing"},
502
    {1, "Keep Aspect Ratio"},
503
    {2, "Fixed"},
504
};
505
506
const MatroskaTag contentSignatureAlgorithm[] = {
507
    {0, "Not Signed"},
508
    {1, "RSA"},
509
};
510
511
const MatroskaTag contentSignatureHashAlgorithm[] = {
512
    {0, "Not Signed"},
513
    {1, "SHA1-160"},
514
    {2, "MD5"},
515
};
516
517
const MatroskaTag trackEnable[] = {
518
    {0x1, "Xmp.video.Enabled"},
519
    {0x2, "Xmp.audio.Enabled"},
520
    {0x11, "Xmp.video.SubTEnabled"},
521
};
522
523
const MatroskaTag defaultOn[] = {
524
    {0x1, "Xmp.video.DefaultOn"},
525
    {0x2, "Xmp.audio.DefaultOn"},
526
    {0x11, "Xmp.video.SubTDefaultOn"},
527
};
528
529
const MatroskaTag trackForced[] = {
530
    {0x1, "Xmp.video.TrackForced"},
531
    {0x2, "Xmp.audio.TrackForced"},
532
    {0x11, "Xmp.video.SubTTrackForced"},
533
};
534
535
const MatroskaTag trackLacing[] = {
536
    {0x1, "Xmp.video.TrackLacing"},
537
    {0x2, "Xmp.audio.TrackLacing"},
538
    {0x11, "Xmp.video.SubTTrackLacing"},
539
};
540
541
const MatroskaTag codecDecodeAll[] = {
542
    {0x1, "Xmp.video.CodecDecodeAll"},
543
    {0x2, "Xmp.audio.CodecDecodeAll"},
544
    {0x11, "Xmp.video.SubTCodecDecodeAll"},
545
};
546
547
const MatroskaTag codecDownloadUrl[] = {
548
    {0x1, "Xmp.video.CodecDownloadUrl"},
549
    {0x2, "Xmp.audio.CodecDownloadUrl"},
550
    {0x11, "Xmp.video.SubTCodecDownloadUrl"},
551
};
552
553
const MatroskaTag codecSettings[] = {
554
    {0x1, "Xmp.video.CodecSettings"},
555
    {0x2, "Xmp.audio.CodecSettings"},
556
    {0x11, "Xmp.video.SubTCodecSettings"},
557
};
558
559
const MatroskaTag trackCodec[] = {
560
    {0x1, "Xmp.video.Codec"},
561
    {0x2, "Xmp.audio.Compressor"},
562
    {0x11, "Xmp.video.SubTCodec"},
563
};
564
565
const MatroskaTag codecInfo[] = {
566
    {0x1, "Xmp.video.CodecInfo"},
567
    {0x2, "Xmp.audio.CodecInfo"},
568
    {0x11, "Xmp.video.SubTCodecInfo"},
569
};
570
571
const MatroskaTag streamRate[] = {
572
    {0x1, "Xmp.video.FrameRate"},
573
    {0x2, "Xmp.audio.DefaultDuration"},
574
};
575
576
/*!
577
  @brief Function used to calculate Tags, Tags may comprise of more than
578
      one byte. The first byte calculates size of the Tag and the remaining
579
      bytes are used to calculate the rest of the Tag.
580
      Returns Tag Value.
581
 */
582
45.7k
[[nodiscard]] static size_t returnTagValue(const byte* buf, size_t size) {
583
45.7k
  enforce(size > 0 && size <= 8, Exiv2::ErrorCode::kerCorruptedMetadata);
584
585
45.7k
  size_t b0 = buf[0] & (0xff >> size);
586
45.7k
  size_t tag = b0 << ((size - 1) * 8);
587
75.4k
  for (size_t i = 1; i < size; ++i) {
588
29.7k
    tag |= static_cast<size_t>(buf[i]) << ((size - i - 1) * 8);
589
29.7k
  }
590
591
45.7k
  return tag;
592
45.7k
}
593
}  // namespace Exiv2::Internal
594
595
namespace Exiv2 {
596
597
using namespace Exiv2::Internal;
598
599
MatroskaVideo::MatroskaVideo(BasicIo::UniquePtr io, const ImageCtorParams& params) :
600
3.11k
    Image(ImageType::mkv, mdNone, std::move(io), params) {
601
3.11k
}  // MatroskaVideo::MatroskaVideo
602
603
5.27k
std::string MatroskaVideo::mimeType() const {
604
5.27k
  return "video/matroska";
605
5.27k
}
606
607
0
void MatroskaVideo::writeMetadata() {
608
0
}
609
610
3.11k
void MatroskaVideo::readMetadata() {
611
3.11k
  if (io_->open() != 0)
612
0
    throw Error(ErrorCode::kerDataSourceOpenFailed, io_->path(), strError());
613
614
  // Ensure that this is the correct image type
615
3.11k
  if (!isMkvType(*io_, false)) {
616
0
    if (io_->error() || io_->eof())
617
0
      throw Error(ErrorCode::kerFailedToReadImageData);
618
0
    throw Error(ErrorCode::kerNotAnImage, "Matroska");
619
0
  }
620
621
3.11k
  IoCloser closer(*io_);
622
3.11k
  clearMetadata();
623
3.11k
  continueTraversing_ = true;
624
3.11k
  height_ = width_ = 1;
625
626
3.11k
  xmpData_["Xmp.video.FileSize"] = io_->size() / bytesMB;
627
3.11k
  xmpData_["Xmp.video.MimeType"] = mimeType();
628
629
27.5k
  while (continueTraversing_)
630
24.4k
    decodeBlock();
631
632
3.11k
  xmpData_["Xmp.video.AspectRatio"] = getAspectRatio(width_, height_);
633
3.11k
}
634
635
24.4k
void MatroskaVideo::decodeBlock() {
636
24.4k
  byte buf[8];
637
24.4k
  io_->read(buf, 1);
638
639
24.4k
  if (io_->eof()) {
640
344
    continueTraversing_ = false;
641
344
    return;
642
344
  }
643
644
24.1k
  uint32_t block_size = findBlockSize(buf[0]);  // 0-8
645
24.1k
  if (block_size > 0)
646
23.5k
    io_->readOrThrow(buf + 1, block_size - 1, ErrorCode::kerCorruptedMetadata);
647
648
24.1k
  auto tag_id = returnTagValue(buf, block_size);
649
24.1k
  const MatroskaTag* tag = Exiv2::find(matroskaTags, tag_id);
650
651
24.1k
  if (!tag) {
652
1.85k
    continueTraversing_ = false;
653
1.85k
    return;
654
1.85k
  }
655
656
  // tag->dump(std::cout);
657
658
22.2k
  if (tag->_id == Cues || tag->_id == Cluster) {
659
14
    continueTraversing_ = false;
660
14
    return;
661
14
  }
662
663
22.2k
  io_->readOrThrow(buf, 1, ErrorCode::kerCorruptedMetadata);
664
22.2k
  block_size = findBlockSize(buf[0]);  // 0-8
665
666
22.2k
  if (block_size > 0)
667
21.6k
    io_->readOrThrow(buf + 1, block_size - 1, ErrorCode::kerCorruptedMetadata);
668
22.2k
  size_t size = returnTagValue(buf, block_size);
669
670
22.2k
  if (tag->isComposite() && !tag->isSkipped())
671
8.56k
    return;
672
673
13.6k
  const size_t bufMaxSize = 200;
674
675
13.6k
#ifndef SUPPRESS_WARNINGS
676
13.6k
  if (!tag->isSkipped() && size > bufMaxSize) {
677
176
    EXV_WARNING << "Size " << size << " of Matroska tag 0x" << std::hex << tag->_id << std::dec << " is greater than "
678
176
                << bufMaxSize << ": ignoring it.\n";
679
176
  }
680
13.6k
#endif
681
13.6k
  if (tag->isSkipped() || size > bufMaxSize) {
682
2.80k
    io_->seek(size, BasicIo::cur);
683
2.80k
    return;
684
2.80k
  }
685
686
10.8k
  DataBuf buf2(bufMaxSize + 1);
687
10.8k
  io_->readOrThrow(buf2.data(), size, ErrorCode::kerCorruptedMetadata);
688
10.8k
  switch (tag->_type) {
689
3.61k
    case InternalField:
690
3.61k
      decodeInternalTags(tag, buf2.data());
691
3.61k
      break;
692
554
    case String:
693
554
    case Utf8:
694
554
      decodeStringTags(tag, buf2.data());
695
554
      break;
696
1.34k
    case Integer:
697
1.96k
    case UInteger:
698
1.96k
      decodeIntegerTags(tag, buf2.data());
699
1.96k
      break;
700
3.46k
    case Boolean:
701
3.46k
      decodeBooleanTags(tag, buf2.data());
702
3.46k
      break;
703
143
    case Date:
704
143
      decodeDateTags(tag, buf2.data(), size);
705
143
      break;
706
328
    case Float:
707
328
      decodeFloatTags(tag, buf2.data());
708
328
      break;
709
0
    case Binary:
710
0
      break;
711
0
    case Master:
712
0
      break;
713
0
    default:
714
0
      break;
715
10.8k
  }
716
10.8k
}  // MatroskaVideo::decodeBlock
717
718
3.61k
void MatroskaVideo::decodeInternalTags(const MatroskaTag* tag, const byte* buf) {
719
3.61k
  uint64_t key = getULongLong(buf, bigEndian);
720
3.61k
  if (!key)
721
26
    return;
722
723
3.59k
  auto internalMt = [=]() -> const MatroskaTag* {
724
3.59k
    switch (tag->_id) {
725
1.81k
      case Xmp_video_VideoScanTpye:
726
1.81k
        return Exiv2::find(videoScanType, key);
727
313
      case Xmp_audio_ChannelType:
728
313
        return Exiv2::find(audioChannels, key);
729
169
      case Xmp_video_ContentCompressAlgo:
730
169
        return Exiv2::find(compressionAlgorithm, key);
731
126
      case Xmp_video_ContentEncryptAlgo:
732
126
        return Exiv2::find(encryptionAlgorithm, key);
733
38
      case Xmp_video_ContentSignAlgo_1:
734
62
      case Xmp_video_ContentSignAlgo_2:
735
62
        return Exiv2::find(contentSignatureAlgorithm, key);
736
47
      case Xmp_video_ContentSignHashAlgo_1:
737
143
      case Xmp_video_ContentSignHashAlgo_2:
738
143
        return Exiv2::find(contentSignatureHashAlgorithm, key);
739
95
      case Xmp_video_ContentEncodingType:
740
95
        return Exiv2::find(encodingType, key);
741
214
      case Xmp_video_DisplayUnit:
742
214
        return Exiv2::find(displayUnit, key);
743
123
      case Xmp_video_AspectRatioType:
744
123
        return Exiv2::find(aspectRatioType, key);
745
0
      case Xmp_video_PhysicalEquivalent:
746
0
        return Exiv2::find(chapterPhysicalEquivalent, key);
747
530
      case Xmp_video_TranslateCodec:
748
530
        return Exiv2::find(chapterTranslateCodec, key);
749
0
      case Video_Audio_CodecID:
750
0
        return Exiv2::find(trackCodec, key);
751
0
      case Video_Audio_CodecName:
752
0
        return Exiv2::find(codecInfo, key);
753
0
      case CodecDownloadURL:
754
0
      case CodecInfoURL:
755
0
        return Exiv2::find(codecDownloadUrl, key);
756
3.59k
    }
757
0
    return nullptr;
758
3.59k
  }();
759
3.59k
  if (internalMt) {
760
163
    xmpData_[tag->_label] = internalMt->_label;
761
3.42k
  } else {
762
3.42k
    xmpData_[tag->_label] = key;
763
3.42k
  }
764
3.59k
}
765
766
554
void MatroskaVideo::decodeStringTags(const MatroskaTag* tag, const byte* buf) {
767
554
  if (tag->_id == TrackNumber) {
768
74
    track_count_++;
769
74
    xmpData_[tag->_label] = track_count_;
770
480
  } else {
771
480
    xmpData_[tag->_label] = buf;
772
480
  }
773
554
}
774
775
1.96k
void MatroskaVideo::decodeIntegerTags(const MatroskaTag* tag, const byte* buf) {
776
1.96k
  uint64_t value = getULongLong(buf, bigEndian);
777
1.96k
  if (!value)
778
221
    return;
779
780
1.74k
  if (tag->_id == Xmp_video_Width_1 || tag->_id == Xmp_video_Width_2)
781
442
    width_ = value;
782
1.74k
  if (tag->_id == Xmp_video_Height_1 || tag->_id == Xmp_video_Height_2)
783
1.12k
    height_ = value;
784
1.74k
  xmpData_[tag->_label] = value;
785
1.74k
}
786
787
3.46k
void MatroskaVideo::decodeBooleanTags(const MatroskaTag* tag, const byte* buf) {
788
3.46k
  const MatroskaTag* internalMt = nullptr;
789
3.46k
  uint64_t key = getULongLong(buf, bigEndian);
790
3.46k
  if (!key)
791
49
    return;
792
793
3.41k
  switch (tag->_id) {
794
806
    case TrackType:  // this tags is used internally only to deduce the type of track (video or audio)
795
806
      if (auto f = Exiv2::find(matroskaTrackType, key)) {
796
148
        stream_ = f->_id;
797
148
      }
798
806
      break;
799
673
    case TrackUsed:
800
673
      internalMt = Exiv2::find(trackEnable, key);
801
673
      break;
802
458
    case TrackDefault:
803
458
      internalMt = Exiv2::find(defaultOn, key);
804
458
      break;
805
155
    case TrackForced:
806
155
      internalMt = Exiv2::find(trackForced, key);
807
155
      break;
808
386
    case TrackLacing:
809
386
      internalMt = Exiv2::find(trackLacing, key);
810
386
      break;
811
756
    case CodecDecodeAll:
812
756
      internalMt = Exiv2::find(codecDecodeAll, key);
813
756
      break;
814
165
    case CodecSettings:
815
165
      internalMt = Exiv2::find(codecSettings, key);
816
165
      break;
817
18
    case Xmp_video_TagDefault:
818
18
      internalMt = tag;
819
18
      break;
820
0
    default:
821
0
      break;
822
3.41k
  }
823
824
3.41k
  if (internalMt) {
825
394
    xmpData_[internalMt->_label] = "Yes";
826
394
  }
827
3.41k
}
828
829
143
void MatroskaVideo::decodeDateTags(const MatroskaTag* tag, const byte* buf, size_t size) {
830
143
  int64_t duration_in_ms = 0;
831
143
  uint64_t value;
832
143
  switch (tag->_id) {
833
56
    case Xmp_video_Duration:
834
56
      if (size <= 4) {
835
10
        duration_in_ms = std::llround(getFloat(buf, bigEndian) * time_code_scale_ * 1000.0);
836
46
      } else {
837
46
        duration_in_ms = std::llround(getDouble(buf, bigEndian) * time_code_scale_ * 1000);
838
46
      }
839
56
      xmpData_[tag->_label] = duration_in_ms;
840
56
      break;
841
65
    case Xmp_video_DateUTC:
842
65
      value = getULongLong(buf, bigEndian);
843
65
      if (!value)
844
18
        return;
845
47
      duration_in_ms = value / 1000000000;
846
47
      xmpData_[tag->_label] = duration_in_ms;
847
47
      break;
848
849
22
    case TimecodeScale:
850
22
      value = getULongLong(buf, bigEndian);
851
22
      if (!value)
852
6
        return;
853
16
      time_code_scale_ = static_cast<double>(value) / static_cast<double>(1000000000);
854
16
      xmpData_[tag->_label] = time_code_scale_;
855
16
      break;
856
0
    default:
857
0
      break;
858
143
  }
859
143
}
860
861
328
void MatroskaVideo::decodeFloatTags(const MatroskaTag* tag, const byte* buf) {
862
328
  xmpData_[tag->_label] = getFloat(buf, bigEndian);
863
864
328
  switch (tag->_id) {
865
322
    case Xmp_audio_SampleRate:
866
328
    case Xmp_audio_OutputSampleRate:
867
328
      xmpData_[tag->_label] = getFloat(buf, bigEndian);
868
328
      break;
869
0
    case VideoFrameRate_DefaultDuration:
870
0
    case Xmp_video_FrameRate: {
871
0
      uint64_t key = getULongLong(buf, bigEndian);
872
0
      if (!key)
873
0
        return;
874
0
      if (auto internalMt = Exiv2::find(streamRate, key)) {
875
0
        double frame_rate = 0;
876
0
        switch (stream_) {
877
0
          case 1:  // video
878
0
            frame_rate = 1000000000.0 / static_cast<double>(key);
879
0
            break;
880
0
          case 2:  // audio
881
0
            frame_rate = static_cast<double>(key) / 1000;
882
0
            break;
883
0
          default:
884
0
            break;
885
0
        }
886
0
        if (std::isgreater(frame_rate, 0.0))
887
0
          xmpData_[internalMt->_label] = frame_rate;
888
0
      } else
889
0
        xmpData_[tag->_label] = "Variable Bit Rate";
890
0
    } break;
891
0
    default:
892
0
      xmpData_[tag->_label] = getFloat(buf, bigEndian);
893
0
      break;
894
328
  }
895
328
}
896
897
45.7k
uint32_t MatroskaVideo::findBlockSize(byte b) {
898
45.7k
  if (b & 128)
899
31.8k
    return 1;
900
13.8k
  if (b & 64)
901
6.65k
    return 2;
902
7.24k
  if (b & 32)
903
862
    return 3;
904
6.37k
  if (b & 16)
905
4.52k
    return 4;
906
1.84k
  if (b & 8)
907
123
    return 5;
908
1.72k
  if (b & 4)
909
82
    return 6;
910
1.64k
  if (b & 2)
911
278
    return 7;
912
1.36k
  if (b & 1)
913
774
    return 8;
914
592
  return 0;
915
1.36k
}
916
917
3.11k
Image::UniquePtr newMkvInstance(BasicIo::UniquePtr io, const ImageCtorParams& params) {
918
3.11k
  auto image = std::make_unique<MatroskaVideo>(std::move(io), params);
919
3.11k
  if (!image->good()) {
920
0
    return nullptr;
921
0
  }
922
3.11k
  return image;
923
3.11k
}
924
925
16.2k
bool isMkvType(BasicIo& iIo, bool advance) {
926
16.2k
  bool result = true;
927
16.2k
  byte tmpBuf[4];
928
16.2k
  iIo.read(tmpBuf, 4);
929
930
16.2k
  if (iIo.error() || iIo.eof())
931
263
    return false;
932
933
15.9k
  if (0x1a != tmpBuf[0] || 0x45 != tmpBuf[1] || 0xdf != tmpBuf[2] || 0xa3 != tmpBuf[3]) {
934
6.63k
    result = false;
935
6.63k
  }
936
937
15.9k
  if (!advance || !result)
938
15.9k
    iIo.seek(0, BasicIo::beg);
939
15.9k
  return result;
940
16.2k
}
941
942
}  // namespace Exiv2