Coverage Report

Created: 2026-08-25 06:40

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
14.8k
[[nodiscard]] static size_t returnTagValue(const byte* buf, size_t size) {
583
14.8k
  enforce(size > 0 && size <= 8, Exiv2::ErrorCode::kerCorruptedMetadata);
584
585
14.8k
  size_t b0 = buf[0] & (0xff >> size);
586
14.8k
  size_t tag = b0 << ((size - 1) * 8);
587
17.4k
  for (size_t i = 1; i < size; ++i) {
588
2.63k
    tag |= static_cast<size_t>(buf[i]) << ((size - i - 1) * 8);
589
2.63k
  }
590
591
14.8k
  return tag;
592
14.8k
}
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
142
    Image(ImageType::mkv, mdNone, std::move(io), params) {
601
142
}  // MatroskaVideo::MatroskaVideo
602
603
142
std::string MatroskaVideo::mimeType() const {
604
142
  return "video/matroska";
605
142
}
606
607
0
void MatroskaVideo::writeMetadata() {
608
0
}
609
610
142
void MatroskaVideo::readMetadata() {
611
142
  if (io_->open() != 0)
612
0
    throw Error(ErrorCode::kerDataSourceOpenFailed, io_->path(), strError());
613
614
  // Ensure that this is the correct image type
615
142
  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
142
  IoCloser closer(*io_);
622
142
  clearMetadata();
623
142
  continueTraversing_ = true;
624
142
  height_ = width_ = 1;
625
626
142
  xmpData_["Xmp.video.FileSize"] = io_->size() / bytesMB;
627
142
  xmpData_["Xmp.video.MimeType"] = mimeType();
628
629
7.63k
  while (continueTraversing_)
630
7.49k
    decodeBlock();
631
632
142
  xmpData_["Xmp.video.AspectRatio"] = getAspectRatio(width_, height_);
633
142
}
634
635
7.49k
void MatroskaVideo::decodeBlock() {
636
7.49k
  byte buf[8];
637
7.49k
  io_->read(buf, 1);
638
639
7.49k
  if (io_->eof()) {
640
5
    continueTraversing_ = false;
641
5
    return;
642
5
  }
643
644
7.48k
  uint32_t block_size = findBlockSize(buf[0]);  // 0-8
645
7.48k
  if (block_size > 0)
646
7.46k
    io_->readOrThrow(buf + 1, block_size - 1, ErrorCode::kerCorruptedMetadata);
647
648
7.48k
  auto tag_id = returnTagValue(buf, block_size);
649
7.48k
  const MatroskaTag* tag = Exiv2::find(matroskaTags, tag_id);
650
651
7.48k
  if (!tag) {
652
82
    continueTraversing_ = false;
653
82
    return;
654
82
  }
655
656
  // tag->dump(std::cout);
657
658
7.40k
  if (tag->_id == Cues || tag->_id == Cluster) {
659
9
    continueTraversing_ = false;
660
9
    return;
661
9
  }
662
663
7.39k
  io_->readOrThrow(buf, 1, ErrorCode::kerCorruptedMetadata);
664
7.39k
  block_size = findBlockSize(buf[0]);  // 0-8
665
666
7.39k
  if (block_size > 0)
667
7.37k
    io_->readOrThrow(buf + 1, block_size - 1, ErrorCode::kerCorruptedMetadata);
668
7.39k
  size_t size = returnTagValue(buf, block_size);
669
670
7.39k
  if (tag->isComposite() && !tag->isSkipped())
671
3.62k
    return;
672
673
3.77k
  const size_t bufMaxSize = 200;
674
675
3.77k
#ifndef SUPPRESS_WARNINGS
676
3.77k
  if (!tag->isSkipped() && size > bufMaxSize) {
677
17
    EXV_WARNING << "Size " << size << " of Matroska tag 0x" << std::hex << tag->_id << std::dec << " is greater than "
678
17
                << bufMaxSize << ": ignoring it.\n";
679
17
  }
680
3.77k
#endif
681
3.77k
  if (tag->isSkipped() || size > bufMaxSize) {
682
483
    io_->seek(size, BasicIo::cur);
683
483
    return;
684
483
  }
685
686
3.29k
  DataBuf buf2(bufMaxSize + 1);
687
3.29k
  io_->readOrThrow(buf2.data(), size, ErrorCode::kerCorruptedMetadata);
688
3.29k
  switch (tag->_type) {
689
559
    case InternalField:
690
559
      decodeInternalTags(tag, buf2.data());
691
559
      break;
692
351
    case String:
693
351
    case Utf8:
694
351
      decodeStringTags(tag, buf2.data());
695
351
      break;
696
146
    case Integer:
697
176
    case UInteger:
698
176
      decodeIntegerTags(tag, buf2.data());
699
176
      break;
700
1.02k
    case Boolean:
701
1.02k
      decodeBooleanTags(tag, buf2.data());
702
1.02k
      break;
703
31
    case Date:
704
31
      decodeDateTags(tag, buf2.data(), size);
705
31
      break;
706
1.10k
    case Float:
707
1.10k
      decodeFloatTags(tag, buf2.data());
708
1.10k
      break;
709
0
    case Binary:
710
0
      break;
711
0
    case Master:
712
0
      break;
713
0
    default:
714
0
      break;
715
3.29k
  }
716
3.29k
}  // MatroskaVideo::decodeBlock
717
718
559
void MatroskaVideo::decodeInternalTags(const MatroskaTag* tag, const byte* buf) {
719
559
  uint64_t key = getULongLong(buf, bigEndian);
720
559
  if (!key)
721
1
    return;
722
723
558
  auto internalMt = [=]() -> const MatroskaTag* {
724
558
    switch (tag->_id) {
725
32
      case Xmp_video_VideoScanTpye:
726
32
        return Exiv2::find(videoScanType, key);
727
79
      case Xmp_audio_ChannelType:
728
79
        return Exiv2::find(audioChannels, key);
729
2
      case Xmp_video_ContentCompressAlgo:
730
2
        return Exiv2::find(compressionAlgorithm, key);
731
2
      case Xmp_video_ContentEncryptAlgo:
732
2
        return Exiv2::find(encryptionAlgorithm, key);
733
0
      case Xmp_video_ContentSignAlgo_1:
734
0
      case Xmp_video_ContentSignAlgo_2:
735
0
        return Exiv2::find(contentSignatureAlgorithm, key);
736
0
      case Xmp_video_ContentSignHashAlgo_1:
737
1
      case Xmp_video_ContentSignHashAlgo_2:
738
1
        return Exiv2::find(contentSignatureHashAlgorithm, key);
739
2
      case Xmp_video_ContentEncodingType:
740
2
        return Exiv2::find(encodingType, key);
741
2
      case Xmp_video_DisplayUnit:
742
2
        return Exiv2::find(displayUnit, key);
743
3
      case Xmp_video_AspectRatioType:
744
3
        return Exiv2::find(aspectRatioType, key);
745
435
      case Xmp_video_PhysicalEquivalent:
746
435
        return Exiv2::find(chapterPhysicalEquivalent, key);
747
0
      case Xmp_video_TranslateCodec:
748
0
        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
558
    }
757
0
    return nullptr;
758
558
  }();
759
558
  if (internalMt) {
760
416
    xmpData_[tag->_label] = internalMt->_label;
761
416
  } else {
762
142
    xmpData_[tag->_label] = key;
763
142
  }
764
558
}
765
766
351
void MatroskaVideo::decodeStringTags(const MatroskaTag* tag, const byte* buf) {
767
351
  if (tag->_id == TrackNumber) {
768
248
    track_count_++;
769
248
    xmpData_[tag->_label] = track_count_;
770
248
  } else {
771
103
    xmpData_[tag->_label] = buf;
772
103
  }
773
351
}
774
775
176
void MatroskaVideo::decodeIntegerTags(const MatroskaTag* tag, const byte* buf) {
776
176
  uint64_t value = getULongLong(buf, bigEndian);
777
176
  if (!value)
778
0
    return;
779
780
176
  if (tag->_id == Xmp_video_Width_1 || tag->_id == Xmp_video_Width_2)
781
22
    width_ = value;
782
176
  if (tag->_id == Xmp_video_Height_1 || tag->_id == Xmp_video_Height_2)
783
108
    height_ = value;
784
176
  xmpData_[tag->_label] = value;
785
176
}
786
787
1.02k
void MatroskaVideo::decodeBooleanTags(const MatroskaTag* tag, const byte* buf) {
788
1.02k
  const MatroskaTag* internalMt = nullptr;
789
1.02k
  uint64_t key = getULongLong(buf, bigEndian);
790
1.02k
  if (!key)
791
60
    return;
792
793
969
  switch (tag->_id) {
794
341
    case TrackType:  // this tags is used internally only to deduce the type of track (video or audio)
795
341
      if (auto f = Exiv2::find(matroskaTrackType, key)) {
796
149
        stream_ = f->_id;
797
149
      }
798
341
      break;
799
274
    case TrackUsed:
800
274
      internalMt = Exiv2::find(trackEnable, key);
801
274
      break;
802
1
    case TrackDefault:
803
1
      internalMt = Exiv2::find(defaultOn, key);
804
1
      break;
805
7
    case TrackForced:
806
7
      internalMt = Exiv2::find(trackForced, key);
807
7
      break;
808
153
    case TrackLacing:
809
153
      internalMt = Exiv2::find(trackLacing, key);
810
153
      break;
811
193
    case CodecDecodeAll:
812
193
      internalMt = Exiv2::find(codecDecodeAll, key);
813
193
      break;
814
0
    case CodecSettings:
815
0
      internalMt = Exiv2::find(codecSettings, key);
816
0
      break;
817
0
    case Xmp_video_TagDefault:
818
0
      internalMt = tag;
819
0
      break;
820
0
    default:
821
0
      break;
822
969
  }
823
824
969
  if (internalMt) {
825
0
    xmpData_[internalMt->_label] = "Yes";
826
0
  }
827
969
}
828
829
31
void MatroskaVideo::decodeDateTags(const MatroskaTag* tag, const byte* buf, size_t size) {
830
31
  int64_t duration_in_ms = 0;
831
31
  uint64_t value;
832
31
  switch (tag->_id) {
833
7
    case Xmp_video_Duration:
834
7
      if (size <= 4) {
835
4
        duration_in_ms = std::llround(getFloat(buf, bigEndian) * time_code_scale_ * 1000.0);
836
4
      } else {
837
3
        duration_in_ms = std::llround(getDouble(buf, bigEndian) * time_code_scale_ * 1000);
838
3
      }
839
7
      xmpData_[tag->_label] = duration_in_ms;
840
7
      break;
841
16
    case Xmp_video_DateUTC:
842
16
      value = getULongLong(buf, bigEndian);
843
16
      if (!value)
844
0
        return;
845
16
      duration_in_ms = value / 1000000000;
846
16
      xmpData_[tag->_label] = duration_in_ms;
847
16
      break;
848
849
8
    case TimecodeScale:
850
8
      value = getULongLong(buf, bigEndian);
851
8
      if (!value)
852
2
        return;
853
6
      time_code_scale_ = static_cast<double>(value) / static_cast<double>(1000000000);
854
6
      xmpData_[tag->_label] = time_code_scale_;
855
6
      break;
856
0
    default:
857
0
      break;
858
31
  }
859
31
}
860
861
1.10k
void MatroskaVideo::decodeFloatTags(const MatroskaTag* tag, const byte* buf) {
862
1.10k
  xmpData_[tag->_label] = getFloat(buf, bigEndian);
863
864
1.10k
  switch (tag->_id) {
865
1.10k
    case Xmp_audio_SampleRate:
866
1.10k
    case Xmp_audio_OutputSampleRate:
867
1.10k
      xmpData_[tag->_label] = getFloat(buf, bigEndian);
868
1.10k
      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
1.10k
  }
895
1.10k
}
896
897
14.8k
uint32_t MatroskaVideo::findBlockSize(byte b) {
898
14.8k
  if (b & 128)
899
13.2k
    return 1;
900
1.60k
  if (b & 64)
901
1.09k
    return 2;
902
511
  if (b & 32)
903
195
    return 3;
904
316
  if (b & 16)
905
200
    return 4;
906
116
  if (b & 8)
907
4
    return 5;
908
112
  if (b & 4)
909
45
    return 6;
910
67
  if (b & 2)
911
1
    return 7;
912
66
  if (b & 1)
913
43
    return 8;
914
23
  return 0;
915
66
}
916
917
142
Image::UniquePtr newMkvInstance(BasicIo::UniquePtr io, const ImageCtorParams& params) {
918
142
  auto image = std::make_unique<MatroskaVideo>(std::move(io), params);
919
142
  if (!image->good()) {
920
0
    return nullptr;
921
0
  }
922
142
  return image;
923
142
}
924
925
2.42k
bool isMkvType(BasicIo& iIo, bool advance) {
926
2.42k
  bool result = true;
927
2.42k
  byte tmpBuf[4];
928
2.42k
  iIo.read(tmpBuf, 4);
929
930
2.42k
  if (iIo.error() || iIo.eof())
931
0
    return false;
932
933
2.42k
  if (0x1a != tmpBuf[0] || 0x45 != tmpBuf[1] || 0xdf != tmpBuf[2] || 0xa3 != tmpBuf[3]) {
934
2.00k
    result = false;
935
2.00k
  }
936
937
2.42k
  if (!advance || !result)
938
2.42k
    iIo.seek(0, BasicIo::beg);
939
2.42k
  return result;
940
2.42k
}
941
942
}  // namespace Exiv2