Tag Parser  9.1.2
C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
overallmkv.cpp
Go to the documentation of this file.
1 #include <c++utilities/chrono/format.h>
2 
3 #include "./overall.h"
4 
5 #include "../abstracttrack.h"
6 #include "../matroska/matroskacontainer.h"
7 #include "../mp4/mp4ids.h"
8 #include "../mpegaudio/mpegaudioframe.h"
9 
10 #include <c++utilities/chrono/timespan.h>
11 #include <c++utilities/conversion/binaryconversion.h>
12 #include <c++utilities/conversion/stringconversion.h>
13 #include <c++utilities/io/misc.h>
14 
15 #include <cstring>
16 #include <fstream>
17 
18 using namespace CppUtilities;
19 
20 namespace MkvTestFlags {
21 enum TestFlag {
22  ForceRewring = 0x1,
23  KeepTagPos = 0x2,
26  KeepIndexPos = 0x4,
29  ForceTagPos = 0x10,
30  ForceIndexPos = 0x20,
31 };
32 }
33 
37 void OverallTests::checkMkvTestfile1()
38 {
39  CPPUNIT_ASSERT_EQUAL(ContainerFormat::Matroska, m_fileInfo.containerFormat());
40  CPPUNIT_ASSERT_EQUAL(TimeSpan::fromMinutes(1) + TimeSpan::fromSeconds(27) + TimeSpan::fromMilliseconds(336), m_fileInfo.duration());
41  const auto tracks = m_fileInfo.tracks();
42  CPPUNIT_ASSERT_EQUAL(2_st, tracks.size());
43  for (const auto &track : tracks) {
44  switch (track->id()) {
45  case 2422994868:
46  CPPUNIT_ASSERT_EQUAL(MediaType::Video, track->mediaType());
47  CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::MicrosoftMpeg4, track->format().general);
48  break;
49  case 3653291187:
50  CPPUNIT_ASSERT_EQUAL(MediaType::Audio, track->mediaType());
51  CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Mpeg1Audio, track->format().general);
52  CPPUNIT_ASSERT_EQUAL(48000u, track->samplingFrequency());
53  break;
54  default:
55  CPPUNIT_FAIL("unknown track ID");
56  }
57  }
58  const auto tags = m_fileInfo.tags();
59  switch (m_tagStatus) {
61  CPPUNIT_ASSERT_EQUAL(1_st, tags.size());
62  CPPUNIT_ASSERT_EQUAL("Big Buck Bunny - test 1"s, tags.front()->value(KnownField::Title).toString());
63  CPPUNIT_ASSERT_EQUAL(TagValue(), tags.front()->value(KnownField::Artist));
64  CPPUNIT_ASSERT_EQUAL(
65  "Matroska Validation File1, basic MPEG4.2 and MP3 with only SimpleBlock"s, tags.front()->value(KnownField::Comment).toString());
66  CPPUNIT_ASSERT_EQUAL("2010"s, tags.front()->value(KnownField::Year).toString());
67  break;
69  checkMkvTestMetaData();
70  break;
71  case TagStatus::Removed:
72  CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
73  }
74  CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Information);
75 }
76 
80 void OverallTests::checkMkvTestfile2()
81 {
82  CPPUNIT_ASSERT_EQUAL(ContainerFormat::Matroska, m_fileInfo.containerFormat());
83  CPPUNIT_ASSERT_EQUAL(TimeSpan::fromSeconds(47) + TimeSpan::fromMilliseconds(509), m_fileInfo.duration());
84  const auto tracks = m_fileInfo.tracks();
85  CPPUNIT_ASSERT_EQUAL(2_st, tracks.size());
86  for (const auto &track : tracks) {
87  switch (track->id()) {
88  case 1863976627:
89  CPPUNIT_ASSERT_EQUAL(MediaType::Video, track->mediaType());
90  CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Avc, track->format().general);
91  CPPUNIT_ASSERT_EQUAL(Size(1354, 576), track->displaySize());
92  break;
93  case 3134325680:
94  CPPUNIT_ASSERT_EQUAL(MediaType::Audio, track->mediaType());
95  CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Aac, track->format().general);
96  CPPUNIT_ASSERT_EQUAL(48000u, track->samplingFrequency());
97  break;
98  default:
99  CPPUNIT_FAIL("unknown track ID");
100  }
101  }
102  const auto tags = m_fileInfo.tags();
103  switch (m_tagStatus) {
104  case TagStatus::Original:
105  CPPUNIT_ASSERT_EQUAL(1_st, tags.size());
106  CPPUNIT_ASSERT_EQUAL("Elephant Dream - test 2"s, tags.front()->value(KnownField::Title).toString());
107  CPPUNIT_ASSERT_EQUAL(TagValue(), tags.front()->value(KnownField::Artist));
108  CPPUNIT_ASSERT_EQUAL("Matroska Validation File 2, 100,000 timecode scale, odd aspect ratio, and CRC-32. Codecs are AVC and AAC"s,
109  tags.front()->value(KnownField::Comment).toString());
110  break;
112  checkMkvTestMetaData();
113  break;
114  case TagStatus::Removed:
115  CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
116  }
117  CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Information);
118 }
119 
123 void OverallTests::checkMkvTestfile3()
124 {
125  CPPUNIT_ASSERT_EQUAL(ContainerFormat::Matroska, m_fileInfo.containerFormat());
126  CPPUNIT_ASSERT_EQUAL(TimeSpan::fromSeconds(49) + TimeSpan::fromMilliseconds(64), m_fileInfo.duration());
127  const auto tracks = m_fileInfo.tracks();
128  CPPUNIT_ASSERT_EQUAL(2_st, tracks.size());
129  for (const auto &track : tracks) {
130  switch (track->id()) {
131  case 3927961528:
132  CPPUNIT_ASSERT_EQUAL(MediaType::Video, track->mediaType());
133  CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Avc, track->format().general);
134  CPPUNIT_ASSERT_EQUAL(Size(1024, 576), track->displaySize());
135  break;
136  case 3391885737:
137  CPPUNIT_ASSERT_EQUAL(MediaType::Audio, track->mediaType());
138  CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Mpeg1Audio, track->format().general);
139  CPPUNIT_ASSERT_EQUAL(48000u, track->samplingFrequency());
140  break;
141  default:
142  CPPUNIT_FAIL("unknown track ID");
143  }
144  }
145  const auto tags = m_fileInfo.tags();
146  switch (m_tagStatus) {
147  case TagStatus::Original:
148  CPPUNIT_ASSERT_EQUAL(1_st, tags.size());
149  CPPUNIT_ASSERT_EQUAL("Elephant Dream - test 3"s, tags.front()->value(KnownField::Title).toString());
150  CPPUNIT_ASSERT_EQUAL(TagValue(), tags.front()->value(KnownField::Artist));
151  CPPUNIT_ASSERT_EQUAL("Matroska Validation File 3, header stripping on the video track and no SimpleBlock"s,
152  tags.front()->value(KnownField::Comment).toString());
153  break;
155  checkMkvTestMetaData();
156  break;
157  case TagStatus::Removed:
158  CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
159  }
160  CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Information);
161 }
162 
167 void OverallTests::checkMkvTestfile4()
168 {
169  CPPUNIT_ASSERT_EQUAL(ContainerFormat::Matroska, m_fileInfo.containerFormat());
170  CPPUNIT_ASSERT_EQUAL(TimeSpan(), m_fileInfo.duration());
171  // this file is messed up, it should contain tags but it doesn't
172  const auto tracks = m_fileInfo.tracks();
173  CPPUNIT_ASSERT_EQUAL(2_st, tracks.size());
174  for (const auto &track : tracks) {
175  switch (track->id()) {
176  case 1368622492:
177  CPPUNIT_ASSERT_EQUAL(MediaType::Video, track->mediaType());
178  CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Theora, track->format().general);
179  CPPUNIT_ASSERT_EQUAL(Size(1280, 720), track->displaySize());
180  break;
181  case 3171450505:
182  CPPUNIT_ASSERT_EQUAL(MediaType::Audio, track->mediaType());
183  CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Vorbis, track->format().general);
184  CPPUNIT_ASSERT_EQUAL(48000u, track->samplingFrequency());
185  CPPUNIT_ASSERT_EQUAL(static_cast<std::uint16_t>(2u), track->channelCount());
186  switch (m_tagStatus) {
187  case TagStatus::Original:
188  case TagStatus::Removed:
189  CPPUNIT_ASSERT_EQUAL("und"s, track->language());
190  CPPUNIT_ASSERT_EQUAL(string(), track->name());
191  CPPUNIT_ASSERT(track->isEnabled());
192  CPPUNIT_ASSERT(!track->isForced());
193  CPPUNIT_ASSERT(!track->isDefault());
194  break;
196  CPPUNIT_ASSERT_EQUAL("ger"s, track->language());
197  CPPUNIT_ASSERT_EQUAL("the name"s, track->name());
198  CPPUNIT_ASSERT(track->isEnabled());
199  CPPUNIT_ASSERT(track->isForced());
200  CPPUNIT_ASSERT(track->isDefault());
201  break;
202  }
203  break;
204  default:
205  CPPUNIT_FAIL("unknown track ID");
206  }
207  }
208  const auto tags = m_fileInfo.tags();
209  switch (m_tagStatus) {
210  case TagStatus::Original:
211  case TagStatus::Removed:
212  CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
213  break;
215  checkMkvTestMetaData();
216  break;
217  }
218 
219  // tolerate critical notifications here because live stream feature used by the file is not supported in v6 yet
220  CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Critical);
221 }
222 
226 void OverallTests::checkMkvTestfile5()
227 {
228  CPPUNIT_ASSERT_EQUAL(ContainerFormat::Matroska, m_fileInfo.containerFormat());
229  CPPUNIT_ASSERT_EQUAL(TimeSpan::fromSeconds(46) + TimeSpan::fromMilliseconds(665), m_fileInfo.duration());
230  const auto tracks = m_fileInfo.tracks();
231  CPPUNIT_ASSERT_EQUAL(11_st, tracks.size());
232  for (const auto &track : tracks) {
233  switch (track->id()) {
234  case 1258329745:
235  CPPUNIT_ASSERT_EQUAL(MediaType::Video, track->mediaType());
236  CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Avc, track->format().general);
237  CPPUNIT_ASSERT_EQUAL(Size(1024, 576), track->displaySize());
238  break;
239  case 3452711582:
240  CPPUNIT_ASSERT_EQUAL(MediaType::Audio, track->mediaType());
241  CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Aac, track->format().general);
242  CPPUNIT_ASSERT_EQUAL(48000u, track->samplingFrequency());
243  CPPUNIT_ASSERT_EQUAL(static_cast<std::uint8_t>(Mpeg4ChannelConfigs::FrontLeftFrontRight), track->channelConfig());
244  break;
245  case 3554194305:
246  CPPUNIT_ASSERT_EQUAL(MediaType::Text, track->mediaType());
247  CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::TextSubtitle, track->format().general);
248  CPPUNIT_ASSERT_EQUAL("ger"s, track->language());
249  break;
250  default:;
251  }
252  }
253  const auto tags = m_fileInfo.tags();
254  switch (m_tagStatus) {
255  case TagStatus::Original:
256  CPPUNIT_ASSERT_EQUAL(1_st, tags.size());
257  CPPUNIT_ASSERT_EQUAL("Big Buck Bunny - test 8"s, tags.front()->value(KnownField::Title).toString());
258  CPPUNIT_ASSERT_EQUAL(TagValue(), tags.front()->value(KnownField::Artist));
259  CPPUNIT_ASSERT_EQUAL("Matroska Validation File 8, secondary audio commentary track, misc subtitle tracks"s,
260  tags.front()->value(KnownField::Comment).toString());
261  break;
263  checkMkvTestMetaData();
264  break;
265  case TagStatus::Removed:
266  CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
267  }
268  CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Information);
269 }
270 
274 void OverallTests::checkMkvTestfile6()
275 {
276  CPPUNIT_ASSERT_EQUAL(ContainerFormat::Matroska, m_fileInfo.containerFormat());
277  CPPUNIT_ASSERT_EQUAL(TimeSpan::fromMinutes(1) + TimeSpan::fromSeconds(27) + TimeSpan::fromMilliseconds(336), m_fileInfo.duration());
278  const auto tracks = m_fileInfo.tracks();
279  CPPUNIT_ASSERT_EQUAL(2_st, tracks.size());
280  for (const auto &track : tracks) {
281  switch (track->id()) {
282  case 2422994868:
283  CPPUNIT_ASSERT_EQUAL(MediaType::Video, track->mediaType());
284  CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::MicrosoftMpeg4, track->format().general);
285  CPPUNIT_ASSERT_EQUAL(Size(854, 480), track->displaySize());
286  break;
287  case 3653291187:
288  CPPUNIT_ASSERT_EQUAL(MediaType::Audio, track->mediaType());
289  CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Mpeg1Audio, track->format().general);
290  CPPUNIT_ASSERT_EQUAL(48000u, track->samplingFrequency());
291  CPPUNIT_ASSERT_EQUAL(static_cast<std::uint8_t>(MpegChannelMode::Stereo), track->channelConfig());
292  break;
293  default:
294  CPPUNIT_FAIL("unknown track ID");
295  }
296  }
297  const auto tags = m_fileInfo.tags();
298  switch (m_tagStatus) {
299  case TagStatus::Original:
300  CPPUNIT_ASSERT_EQUAL(1_st, tags.size());
301  CPPUNIT_ASSERT_EQUAL("Big Buck Bunny - test 6"s, tags.front()->value(KnownField::Title).toString());
302  CPPUNIT_ASSERT_EQUAL(TagValue(), tags.front()->value(KnownField::Artist));
303  CPPUNIT_ASSERT_EQUAL("Matroska Validation File 6, random length to code the size of Clusters and Blocks, no Cues for seeking"s,
304  tags.front()->value(KnownField::Comment).toString());
305  break;
307  checkMkvTestMetaData();
308  break;
309  case TagStatus::Removed:
310  CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
311  }
312  CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Information);
313 }
314 
318 void OverallTests::checkMkvTestfile7()
319 {
320  CPPUNIT_ASSERT_EQUAL(ContainerFormat::Matroska, m_fileInfo.containerFormat());
321  CPPUNIT_ASSERT_EQUAL(TimeSpan::fromSeconds(37) + TimeSpan::fromMilliseconds(43), m_fileInfo.duration());
322  const auto tracks = m_fileInfo.tracks();
323  CPPUNIT_ASSERT_EQUAL(2_st, tracks.size());
324  for (const auto &track : tracks) {
325  switch (track->id()) {
326  case 568001708:
327  CPPUNIT_ASSERT_EQUAL(MediaType::Video, track->mediaType());
328  CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Avc, track->format().general);
329  CPPUNIT_ASSERT_EQUAL(Size(1024, 576), track->displaySize());
330  CPPUNIT_ASSERT_EQUAL("YUV 4:2:0"s, string(track->chromaFormat()));
331  break;
332  case 2088735154:
333  CPPUNIT_ASSERT_EQUAL(MediaType::Audio, track->mediaType());
334  CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Aac, track->format().general);
335  CPPUNIT_ASSERT_EQUAL(48000u, track->samplingFrequency());
336  CPPUNIT_ASSERT_EQUAL(static_cast<std::uint8_t>(Mpeg4ChannelConfigs::FrontLeftFrontRight), track->channelConfig());
337  break;
338  default:
339  CPPUNIT_FAIL("unknown track ID");
340  }
341  }
342  const auto tags = m_fileInfo.tags();
343  switch (m_tagStatus) {
344  case TagStatus::Original:
345  CPPUNIT_ASSERT_EQUAL(1_st, tags.size());
346  CPPUNIT_ASSERT_EQUAL("Big Buck Bunny - test 7"s, tags.front()->value(KnownField::Title).toString());
347  CPPUNIT_ASSERT_EQUAL(TagValue(), tags.front()->value(KnownField::Artist));
348  CPPUNIT_ASSERT_EQUAL(
349  "Matroska Validation File 7, junk elements are present at the beggining or end of clusters, the parser should skip it. There is also a damaged element at 451418"s,
350  tags.front()->value(KnownField::Comment).toString());
351  break;
353  checkMkvTestMetaData();
354  break;
355  case TagStatus::Removed:
356  CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
357  }
358 
359  for (const auto &msg : m_diag) {
360  if (msg.level() != DiagLevel::Warning) {
361  continue;
362  }
363  CPPUNIT_ASSERT(startsWith(msg.context(), "parsing header of EBML element 0xEA \"cue codec state\" at"));
364  CPPUNIT_ASSERT_EQUAL("Data of EBML element seems to be truncated; unable to parse siblings of that element."s, msg.message());
365  }
366  CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Warning);
367 }
368 
372 void OverallTests::checkMkvTestfile8()
373 {
374  CPPUNIT_ASSERT_EQUAL(ContainerFormat::Matroska, m_fileInfo.containerFormat());
375  CPPUNIT_ASSERT_EQUAL(TimeSpan::fromSeconds(47) + TimeSpan::fromMilliseconds(341), m_fileInfo.duration());
376  const auto tracks = m_fileInfo.tracks();
377  CPPUNIT_ASSERT_EQUAL(2_st, tracks.size());
378  for (const auto &track : tracks) {
379  switch (track->id()) {
380  case 568001708:
381  CPPUNIT_ASSERT_EQUAL(MediaType::Video, track->mediaType());
382  CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Avc, track->format().general);
383  CPPUNIT_ASSERT_EQUAL(Size(1024, 576), track->displaySize());
384  CPPUNIT_ASSERT_EQUAL("YUV 4:2:0"s, string(track->chromaFormat()));
385  break;
386  case 2088735154:
387  CPPUNIT_ASSERT_EQUAL(MediaType::Audio, track->mediaType());
388  CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Aac, track->format().general);
389  CPPUNIT_ASSERT_EQUAL(48000u, track->samplingFrequency());
390  CPPUNIT_ASSERT_EQUAL(static_cast<std::uint8_t>(Mpeg4ChannelConfigs::FrontLeftFrontRight), track->channelConfig());
391  break;
392  default:
393  CPPUNIT_FAIL("unknown track ID");
394  }
395  }
396  const auto tags = m_fileInfo.tags();
397  switch (m_tagStatus) {
398  case TagStatus::Original:
399  CPPUNIT_ASSERT_EQUAL(1_st, tags.size());
400  CPPUNIT_ASSERT_EQUAL("Big Buck Bunny - test 8"s, tags.front()->value(KnownField::Title).toString());
401  CPPUNIT_ASSERT_EQUAL(TagValue(), tags.front()->value(KnownField::Artist));
402  CPPUNIT_ASSERT_EQUAL(
403  "Matroska Validation File 8, audio missing between timecodes 6.019s and 6.360s"s, tags.front()->value(KnownField::Comment).toString());
404  break;
406  checkMkvTestMetaData();
407  break;
408  case TagStatus::Removed:
409  CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
410  }
411  CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Information);
412 }
413 
417 void OverallTests::checkMkvTestfileHandbrakeChapters()
418 {
419  CPPUNIT_ASSERT_EQUAL(ContainerFormat::Matroska, m_fileInfo.containerFormat());
420  CPPUNIT_ASSERT_EQUAL(TimeSpan::fromSeconds(27) + TimeSpan::fromMilliseconds(569), m_fileInfo.duration());
421  const auto tracks = m_fileInfo.tracks();
422  CPPUNIT_ASSERT_EQUAL(2_st, tracks.size());
423  for (const auto &track : tracks) {
424  switch (track->id()) {
425  case 1:
426  CPPUNIT_ASSERT_EQUAL(MediaType::Video, track->mediaType());
427  CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Avc, track->format().general);
428  CPPUNIT_ASSERT_EQUAL(4.0, track->version());
429  CPPUNIT_ASSERT_EQUAL(Size(1280, 544), track->pixelSize());
430  CPPUNIT_ASSERT_EQUAL(Size(1280, 544), track->displaySize());
431  CPPUNIT_ASSERT_EQUAL(23u, track->fps());
432  break;
433  case 2:
434  CPPUNIT_ASSERT_EQUAL(MediaType::Audio, track->mediaType());
435  CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Aac, track->format().general);
436  CPPUNIT_ASSERT_EQUAL(44100u, track->samplingFrequency());
437  CPPUNIT_ASSERT_EQUAL(static_cast<std::uint8_t>(Mpeg4ChannelConfigs::FrontLeftFrontRight), track->channelConfig());
438  break;
439  default:
440  CPPUNIT_FAIL(argsToString("unknown track ID ", track->id()));
441  }
442  }
443  const auto chapters = m_fileInfo.chapters();
444  CPPUNIT_ASSERT_EQUAL(2_st, chapters.size());
445  for (const auto &chapter : chapters) {
446  switch (chapter->id()) {
447  case 1:
448  CPPUNIT_ASSERT_EQUAL("Kapitel 01"s, static_cast<const string &>(chapter->names().at(0)));
449  CPPUNIT_ASSERT_EQUAL(static_cast<std::int64_t>(0), chapter->startTime().totalTicks());
450  CPPUNIT_ASSERT_EQUAL(15, chapter->endTime().seconds());
451  break;
452  case 2:
453  CPPUNIT_ASSERT_EQUAL("Kapitel 02"s, static_cast<const string &>(chapter->names().at(0)));
454  CPPUNIT_ASSERT_EQUAL(15, chapter->startTime().seconds());
455  CPPUNIT_ASSERT_EQUAL(27, chapter->endTime().seconds());
456  break;
457  default:
458  CPPUNIT_FAIL(argsToString("unknown chapter ID ", chapter->id()));
459  }
460  }
461  const auto tags = m_fileInfo.tags();
462  switch (m_tagStatus) {
463  case TagStatus::Original:
464  CPPUNIT_ASSERT_EQUAL(2_st, tags.size());
465  CPPUNIT_ASSERT(tags[0]->target().isEmpty());
466  CPPUNIT_ASSERT_EQUAL(""s, static_cast<MatroskaTag *>(tags[0])->value("CREATION_TIME").toString());
467  CPPUNIT_ASSERT_EQUAL("Lavf55.12.0"s, tags[0]->value(KnownField::Encoder).toString());
468  CPPUNIT_ASSERT_EQUAL(static_cast<TagTarget::IdType>(2), tags[1]->target().tracks().at(0));
469  CPPUNIT_ASSERT_EQUAL("eng"s, tags[1]->value(KnownField::Language).toString());
470  break;
472  checkMkvTestMetaData();
473  break;
474  case TagStatus::Removed:
475  CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
476  }
477  CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Information);
478 }
479 
483 void OverallTests::checkMkvTestfileNestedTags()
484 {
485  CPPUNIT_ASSERT_EQUAL(ContainerFormat::Matroska, m_fileInfo.containerFormat());
486  const auto tags = m_fileInfo.tags();
487  bool generalTagFound = false;
488  switch (m_tagStatus) {
489  case TagStatus::Original:
491  CPPUNIT_ASSERT_EQUAL(5_st, tags.size());
492  for (const Tag *tag : tags) {
493  CPPUNIT_ASSERT(tag->type() == TagType::MatroskaTag);
494  const auto *mkvTag = static_cast<const MatroskaTag *>(tag);
495  const auto &target = mkvTag->target();
496  if (target.level() == 50 && target.tracks().empty()) {
497  generalTagFound = true;
498  CPPUNIT_ASSERT_EQUAL("Vanilla Sky"s, tag->value(KnownField::Title).toString());
499  const auto &fields = mkvTag->fields();
500  const auto &artistField = fields.find(mkvTag->fieldId(KnownField::Artist));
501  CPPUNIT_ASSERT(artistField != fields.end());
502  CPPUNIT_ASSERT_EQUAL("Test artist"s, artistField->second.value().toString());
503  const auto &nestedFields = artistField->second.nestedFields();
504  CPPUNIT_ASSERT_EQUAL(1_st, nestedFields.size());
505  CPPUNIT_ASSERT_EQUAL("ADDRESS"s, nestedFields[0].idToString());
506  CPPUNIT_ASSERT_EQUAL("Test address"s, nestedFields[0].value().toString());
507  }
508  }
509  CPPUNIT_ASSERT(generalTagFound);
510  break;
511  case TagStatus::Removed:
512  CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
513  }
514 
515  // the file contains in fact the unknown element [44][B4]
516  // TODO: find out what this element is about (its data is only the single byte 0x01)
517  for (const auto &msg : m_diag) {
518  if (msg.level() != DiagLevel::Warning) {
519  continue;
520  }
521  CPPUNIT_ASSERT(startsWith(msg.message(), "\"SimpleTag\"-element contains unknown element 0x44B4 at"));
522  }
523  CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Warning);
524 }
525 
529 void OverallTests::checkMkvTestMetaData()
530 {
531  // check tags
532  const auto tags = m_fileInfo.tags();
533  const auto tracks = m_fileInfo.tracks();
534  CPPUNIT_ASSERT_EQUAL(2_st, tags.size());
535  CPPUNIT_ASSERT_EQUAL(m_testTitle.toString(), tags.front()->value(KnownField::Title).toString());
536  CPPUNIT_ASSERT(tags.front()->value(KnownField::Artist).isEmpty());
537  CPPUNIT_ASSERT_EQUAL(m_testComment.toString(), tags.front()->value(KnownField::Comment).toString());
538  CPPUNIT_ASSERT_EQUAL(static_cast<std::uint64_t>(30), tags[1]->target().level());
539  CPPUNIT_ASSERT_EQUAL(tracks.at(0)->id(), tags[1]->target().tracks().at(0));
540  CPPUNIT_ASSERT_EQUAL(m_testAlbum.toString(), tags[1]->value(KnownField::Album).toString());
541  CPPUNIT_ASSERT_EQUAL(m_testPartNumber.toInteger(), tags[1]->value(KnownField::PartNumber).toInteger());
542  CPPUNIT_ASSERT_EQUAL(m_testTotalParts.toInteger(), tags[1]->value(KnownField::TotalParts).toInteger());
543 
544  // check attachments
545  const auto attachments = m_fileInfo.attachments();
546  CPPUNIT_ASSERT_EQUAL(1_st, attachments.size());
547  CPPUNIT_ASSERT_EQUAL("image/png"s, attachments[0]->mimeType());
548  CPPUNIT_ASSERT_EQUAL("cover.jpg"s, attachments[0]->name());
549  const StreamDataBlock *attachmentData = attachments[0]->data();
550  CPPUNIT_ASSERT(attachmentData != nullptr);
551  if (m_testCover.empty()) {
552  m_testCover = readFile(testFilePath("matroska_wave1/logo3_256x256.png"), 20000);
553  }
554  CPPUNIT_ASSERT_EQUAL(m_testCover.size(), static_cast<size_t>(attachmentData->size()));
555  istream &attachmentSteam = attachmentData->stream();
556  attachmentSteam.seekg(attachmentData->startOffset());
557  for (char expectedChar : m_testCover) {
558  CPPUNIT_ASSERT_EQUAL(expectedChar, static_cast<char>(attachmentSteam.get()));
559  }
560 }
561 
565 void OverallTests::checkMkvConstraints()
566 {
567  using namespace MkvTestFlags;
568 
569  CPPUNIT_ASSERT(m_fileInfo.container());
570  if (m_mode & PaddingConstraints) {
571  if (m_mode & ForceRewring) {
572  CPPUNIT_ASSERT_EQUAL(static_cast<std::uint64_t>(4096), m_fileInfo.paddingSize());
573  } else {
574  CPPUNIT_ASSERT(m_fileInfo.paddingSize() >= 1024);
575  CPPUNIT_ASSERT(m_fileInfo.paddingSize() <= (4096 + 1024));
576  }
577  if (!(m_mode & RemoveTag) && (m_expectedTagPos != ElementPosition::Keep) && ((m_mode & ForceRewring) || (m_mode & ForceTagPos))) {
578  CPPUNIT_ASSERT_EQUAL(m_expectedTagPos, m_fileInfo.container()->determineTagPosition(m_diag));
579  }
580  if ((m_expectedIndexPos != ElementPosition::Keep) && ((m_mode & ForceRewring) || (m_mode & ForceIndexPos))) {
581  CPPUNIT_ASSERT_EQUAL(m_expectedIndexPos, m_fileInfo.container()->determineIndexPosition(m_diag));
582  }
583  }
584 }
585 
589 void OverallTests::setMkvTestMetaData()
590 {
591  CPPUNIT_ASSERT_EQUAL(ContainerFormat::Matroska, m_fileInfo.containerFormat());
592  auto *container = static_cast<MatroskaContainer *>(m_fileInfo.container());
593 
594  // change the present tag
595  const string fileName(m_fileInfo.fileName());
596  if (fileName == "test4.mkv") {
597  // test4.mkv has no tag, so one must be created first
598  container->createTag(TagTarget(50));
599  // also change language, name, forced and default of track "3171450505" to German
600  MatroskaTrack *track = container->trackById(3171450505);
601  CPPUNIT_ASSERT(track);
602  track->setLanguage("ger");
603  track->setName("the name");
604  track->setDefault(true);
605  track->setEnabled(true);
606  track->setForced(true);
607  } else if (fileName == "handbrake-chapters-2.mkv") {
608  // remove 2nd tag
609  m_fileInfo.removeTag(m_fileInfo.tags().at(1));
610  }
611  Tag *firstTag = m_fileInfo.tags().at(0);
612  firstTag->setValue(KnownField::Title, m_testTitle);
613  firstTag->setValue(KnownField::Comment, m_testComment);
614  // add an additional tag targeting the first track
616  trackIds.emplace_back(m_fileInfo.tracks().at(0)->id());
617  Tag *newTag = container->createTag(TagTarget(30, trackIds));
618  CPPUNIT_ASSERT_MESSAGE("create tag", newTag);
619  newTag->setValue(KnownField::Album, m_testAlbum);
620  newTag->setValue(KnownField::PartNumber, m_testPartNumber);
621  newTag->setValue(KnownField::TotalParts, m_testTotalParts);
622  // assign an attachment
623  AbstractAttachment *const attachment = container->createAttachment();
624  CPPUNIT_ASSERT_MESSAGE("create attachment", attachment);
625  attachment->setFile(testFilePath("matroska_wave1/logo3_256x256.png"), m_diag);
626  attachment->setMimeType("image/png");
627  attachment->setName("cover.jpg");
628 }
629 
634 {
635  cerr << endl << "Matroska parser" << endl;
636  m_fileInfo.setForceFullParse(false);
637  m_tagStatus = TagStatus::Original;
638  parseFile(testFilePath("matroska_wave1/test1.mkv"), &OverallTests::checkMkvTestfile1);
639  parseFile(testFilePath("matroska_wave1/test2.mkv"), &OverallTests::checkMkvTestfile2);
640  parseFile(testFilePath("matroska_wave1/test3.mkv"), &OverallTests::checkMkvTestfile3);
641  parseFile(testFilePath("matroska_wave1/test4.mkv"), &OverallTests::checkMkvTestfile4);
642  parseFile(testFilePath("matroska_wave1/test5.mkv"), &OverallTests::checkMkvTestfile5);
643  parseFile(testFilePath("matroska_wave1/test6.mkv"), &OverallTests::checkMkvTestfile6);
644  parseFile(testFilePath("matroska_wave1/test7.mkv"), &OverallTests::checkMkvTestfile7);
645  parseFile(testFilePath("matroska_wave1/test8.mkv"), &OverallTests::checkMkvTestfile8);
646  parseFile(testFilePath("mtx-test-data/mkv/handbrake-chapters-2.mkv"), &OverallTests::checkMkvTestfileHandbrakeChapters);
647  parseFile(testFilePath("mkv/nested-tags.mkv"), &OverallTests::checkMkvTestfileNestedTags);
648 }
649 
658 {
659  // full parse is required to determine padding
660  m_fileInfo.setForceFullParse(true);
661 
662  // do the test under different conditions
663  for (m_mode = 0; m_mode != 0x100; ++m_mode) {
664  using namespace MkvTestFlags;
665 
666  // setup test conditions
667  m_fileInfo.setForceRewrite(m_mode & ForceRewring);
668  if (m_mode & KeepTagPos) {
669  m_fileInfo.setTagPosition(ElementPosition::Keep);
670  } else {
671  m_fileInfo.setTagPosition(m_mode & TagsBeforeData ? ElementPosition::BeforeData : ElementPosition::AfterData);
672  }
673  if (m_mode & KeepIndexPos) {
674  if (m_mode & IndexBeforeData) {
675  continue;
676  }
677  m_fileInfo.setIndexPosition(ElementPosition::Keep);
678  } else {
679  m_fileInfo.setIndexPosition(m_mode & IndexBeforeData ? ElementPosition::BeforeData : ElementPosition::AfterData);
680  }
681  m_fileInfo.setPreferredPadding(m_mode & PaddingConstraints ? 4096 : 0);
682  m_fileInfo.setMinPadding(m_mode & PaddingConstraints ? 1024 : 0);
683  m_fileInfo.setMaxPadding(m_mode & PaddingConstraints ? (4096 + 1024) : numeric_limits<size_t>::max());
684  m_fileInfo.setForceTagPosition(m_mode & ForceTagPos);
685  m_fileInfo.setForceIndexPosition(m_mode & ForceIndexPos);
686 
687  // print test conditions
688  list<string> testConditions;
689  if (m_mode & ForceRewring) {
690  testConditions.emplace_back("forcing rewrite");
691  }
692  if (m_mode & KeepTagPos) {
693  if (m_mode & RemoveTag) {
694  testConditions.emplace_back("removing tag");
695  } else {
696  testConditions.emplace_back("keeping tag position");
697  }
698  } else if (m_mode & TagsBeforeData) {
699  testConditions.emplace_back("tags before data");
700  } else {
701  testConditions.emplace_back("tags after data");
702  }
703  if (m_mode & KeepIndexPos) {
704  testConditions.emplace_back("keeping index position");
705  } else if (m_mode & IndexBeforeData) {
706  testConditions.emplace_back("index before data");
707  } else {
708  testConditions.emplace_back("index after data");
709  }
710  if (m_mode & PaddingConstraints) {
711  testConditions.emplace_back("padding constraints");
712  }
713  if (m_mode & ForceTagPos) {
714  testConditions.emplace_back("forcing tag position");
715  }
716  if (m_mode & ForceIndexPos) {
717  testConditions.emplace_back("forcing index position");
718  }
719  cerr << endl << "Matroska maker - testmode " << m_mode << ": " << joinStrings(testConditions, ", ") << endl;
720 
721  // do actual tests
722  m_tagStatus = (m_mode & RemoveTag) ? TagStatus::Removed : TagStatus::TestMetaDataPresent;
723  void (OverallTests::*modifyRoutine)(void) = (m_mode & RemoveTag) ? &OverallTests::removeAllTags : &OverallTests::setMkvTestMetaData;
724  makeFile(workingCopyPath("matroska_wave1/test1.mkv"), modifyRoutine, &OverallTests::checkMkvTestfile1);
725  makeFile(workingCopyPath("matroska_wave1/test2.mkv"), modifyRoutine, &OverallTests::checkMkvTestfile2);
726  makeFile(workingCopyPath("matroska_wave1/test3.mkv"), modifyRoutine, &OverallTests::checkMkvTestfile3);
727  makeFile(workingCopyPath("matroska_wave1/test4.mkv"), modifyRoutine, &OverallTests::checkMkvTestfile4);
728  makeFile(workingCopyPath("matroska_wave1/test5.mkv"), modifyRoutine, &OverallTests::checkMkvTestfile5);
729  makeFile(workingCopyPath("matroska_wave1/test6.mkv"), modifyRoutine, &OverallTests::checkMkvTestfile6);
730  makeFile(workingCopyPath("matroska_wave1/test7.mkv"), modifyRoutine, &OverallTests::checkMkvTestfile7);
731  makeFile(workingCopyPath("matroska_wave1/test8.mkv"), modifyRoutine, &OverallTests::checkMkvTestfile8);
732  makeFile(workingCopyPath("mtx-test-data/mkv/handbrake-chapters-2.mkv"), modifyRoutine, &OverallTests::checkMkvTestfileHandbrakeChapters);
733  }
734 }
735 
741 {
742  cerr << endl << "Matroska maker - rewrite file with nested tags" << endl;
743  m_fileInfo.setMinPadding(0);
744  m_fileInfo.setMaxPadding(0);
745  m_fileInfo.setTagPosition(ElementPosition::BeforeData);
746  m_fileInfo.setIndexPosition(ElementPosition::BeforeData);
747  makeFile(workingCopyPath("mkv/nested-tags.mkv"), &OverallTests::noop, &OverallTests::checkMkvTestfileNestedTags);
748 }
TagParser::AbstractAttachment::setMimeType
void setMimeType(const std::string &mimeType)
Sets the MIME-type of the attachment.
Definition: abstractattachment.h:191
TagParser::Mpeg4ElementaryStreamObjectIds::Aac
@ Aac
Definition: mp4ids.h:453
TagStatus::TestMetaDataPresent
@ TestMetaDataPresent
TagParser::TagDataType::TimeSpan
@ TimeSpan
TagParser::Mp4TagAtomIds::Album
@ Album
Definition: mp4ids.h:86
TagParser::AbstractAttachment
The AbstractAttachment class parses and stores attachment information.
Definition: abstractattachment.h:105
TagParser::StreamDataBlock::startOffset
std::istream::pos_type startOffset() const
Returns the absolute start offset of the data block in the stream.
Definition: abstractattachment.h:53
TagParser::StreamDataBlock::stream
std::istream & stream() const
Returns the associated stream.
Definition: abstractattachment.h:45
TagParser::Mp4TagAtomIds::Encoder
@ Encoder
Definition: mp4ids.h:97
TagStatus::Original
@ Original
TagParser::StreamDataBlock::size
std::istream::pos_type size() const
Returns the size of the data block.
Definition: abstractattachment.h:69
OverallTests::testMkvParsing
void testMkvParsing()
Tests the Matroska parser via MediaFileInfo.
Definition: overallmkv.cpp:633
OverallTests::testMkvMakingWithDifferentSettings
void testMkvMakingWithDifferentSettings()
Tests the Matroska maker via MediaFileInfo.
Definition: overallmkv.cpp:657
TagParser::AbstractAttachment::setName
void setName(const std::string &name)
Sets the (file) name of the attachment.
Definition: abstractattachment.h:175
MkvTestFlags::IndexBeforeData
@ IndexBeforeData
Definition: overallmkv.cpp:27
TagParser::Tag
The Tag class is used to store, read and write tag information.
Definition: tag.h:98
MkvTestFlags::ForceRewring
@ ForceRewring
Definition: overallmkv.cpp:22
TagParser::TagTarget::IdContainerType
std::vector< IdType > IdContainerType
Definition: tagtarget.h:23
TagParser::AbstractTrack::setForced
void setForced(bool forced)
Sets whether the track is forced.
Definition: abstracttrack.h:640
TagParser::Mpeg4ElementaryStreamObjectIds::Avc
@ Avc
Definition: mp4ids.h:449
overall.h
TagParser::MatroskaIds::Title
@ Title
Definition: matroskaid.h:54
TagParser::AbstractTrack::setEnabled
void setEnabled(bool enabled)
Sets whether the track is enabled.
Definition: abstracttrack.h:606
TagParser::Mpeg4ElementaryStreamObjectIds::Mpeg1Audio
@ Mpeg1Audio
Definition: mp4ids.h:465
TagParser::SubFormats::TextSubtitle
TextSubtitle
Definition: mediaformat.h:226
TagParser::Mp4TagAtomIds::Year
@ Year
Definition: mp4ids.h:122
TagParser::StreamDataBlock
The StreamDataBlock class is a reference to a certain data block of a stream.
Definition: abstractattachment.h:15
TagParser::Tag::target
const TagTarget & target() const
Returns the target of tag.
Definition: tag.h:174
MkvTestFlags::RemoveTag
@ RemoveTag
Definition: overallmkv.cpp:25
TagParser::MatroskaTrack
Implementation of TagParser::AbstractTrack for the Matroska container.
Definition: matroskatrack.h:46
CppUtilities
Definition: abstractcontainer.h:15
TagParser::Size
The Size class defines the size of a two-dimensional object using integer point precision.
Definition: size.h:16
MkvTestFlags::KeepTagPos
@ KeepTagPos
Definition: overallmkv.cpp:23
TagParser::TagTarget::IdType
std::uint64_t IdType
Definition: tagtarget.h:22
MkvTestFlags::ForceTagPos
@ ForceTagPos
Definition: overallmkv.cpp:29
TagStatus::Removed
@ Removed
TagParser::TagTarget
The TagTarget class specifies the target of a tag.
Definition: tagtarget.h:20
TagParser::TagValue
The TagValue class wraps values of different types. It is meant to be assigned to a tag field.
Definition: tagvalue.h:75
TagParser::MatroskaTag
Implementation of TagParser::Tag for the Matroska container.
Definition: matroskatag.h:58
TagParser::AbstractTrack::setDefault
void setDefault(bool isDefault)
Sets whether the track is a default track.
Definition: abstracttrack.h:623
OverallTests::testMkvMakingNestedTags
void testMkvMakingNestedTags()
Tests making a Matroska file with nested tags via MediaFileInfo.
Definition: overallmkv.cpp:740
MkvTestFlags::TagsBeforeData
@ TagsBeforeData
Definition: overallmkv.cpp:24
TagParser::AbstractTrack::setName
void setName(const std::string &name)
Sets the name.
Definition: abstracttrack.h:363
TagParser::MatroskaTrackType::Video
@ Video
Definition: matroskaid.h:403
OverallTests
The OverallTests class tests reading and writing tags and parsing technical information for all suppo...
Definition: overall.h:40
TagParser::MatroskaContainer
Implementation of GenericContainer<MediaFileInfo, MatroskaTag, MatroskaTrack, EbmlElement>.
Definition: matroskacontainer.h:24
MkvTestFlags
Definition: overallmkv.cpp:20
TagParser::Mp4TagAtomIds::Artist
@ Artist
Definition: mp4ids.h:88
MkvTestFlags::KeepIndexPos
@ KeepIndexPos
Definition: overallmkv.cpp:26
MkvTestFlags::TestFlag
TestFlag
Definition: overallmkv.cpp:21
MkvTestFlags::PaddingConstraints
@ PaddingConstraints
Definition: overallmkv.cpp:28
TagParser::Mp4TagAtomIds::Comment
@ Comment
Definition: mp4ids.h:91
TagParser::Mpeg4ChannelConfigs::FrontLeftFrontRight
@ FrontLeftFrontRight
Definition: mp4ids.h:616
TagParser::Tag::setValue
virtual bool setValue(KnownField field, const TagValue &value)=0
Assigns the given value to the specified field.
MkvTestFlags::ForceIndexPos
@ ForceIndexPos
Definition: overallmkv.cpp:30
TagParser::MatroskaTrackType::Audio
@ Audio
Definition: matroskaid.h:403
TagParser::AbstractAttachment::setFile
void setFile(const std::string &path, Diagnostics &diag)
Sets the data, name and MIME-type for the specified path.
Definition: abstractattachment.cpp:170
TagParser::AbstractTrack::setLanguage
void setLanguage(const std::string &language)
Sets the language of the track.
Definition: abstracttrack.h:422