Tag Parser  7.0.3
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 ChronoUtilities;
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<uint16>(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<byte>(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<byte>(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<byte>(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<byte>(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<byte>(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(0l, 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(2_st, 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(30_st, 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(4096_st, 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 *attachment = container->createAttachment();
624  CPPUNIT_ASSERT_MESSAGE("create attachment", attachment);
625  attachment->setFile(TestUtilities::testFilePath("matroska_wave1/logo3_256x256.png"), m_diag);
626  attachment->setMimeType("image/png");
627  attachment->setName("cover.jpg");
628 }
629 
635 void OverallTests::createMkvWithNestedTags()
636 {
637 #ifdef PLATFORM_UNIX
638  m_nestedTagsMkvPath = workingCopyPathMode("mtx-test-data/mkv/nested-tags.mkv", WorkingCopyMode::NoCopy);
639  remove(m_nestedTagsMkvPath.data());
640 
641  cerr << "\n\n- Create testfile \"" << m_nestedTagsMkvPath << "\" with mkvmerge" << endl;
642  const string tagsMkvPath(testFilePath("mtx-test-data/mkv/tags.mkv"));
643  const string tagsXmlPath(testFilePath("mkv/nested-tags.xml"));
644  const char *const mkvmergeArgs[] = { "--ui-language en_US", "--output", m_nestedTagsMkvPath.data(), "--no-global-tags", "--language", "0:und",
645  "--default-track", "0:yes", "--language", "1:und", "--default-track", "1:yes", "(", tagsMkvPath.data(), ")", "--global-tags",
646  tagsXmlPath.data(), "--track-order", "0:0,0:1", nullptr };
647  string mkvmergeOutput, mkvmergeErrors;
648  int res = execHelperApp("/bin/mkvmerge", mkvmergeArgs, mkvmergeOutput, mkvmergeErrors);
649  cout << mkvmergeOutput << endl;
650  cerr << mkvmergeErrors << endl;
651  if (res) {
652  cerr << "- failure (exit code " << res << "); unable to test nested tags" << endl;
653  remove(m_nestedTagsMkvPath.data());
654  m_nestedTagsMkvPath.clear();
655  }
656 #endif
657 }
658 
663 {
664  cerr << endl << "Matroska parser" << endl;
665  m_fileInfo.setForceFullParse(false);
666  m_tagStatus = TagStatus::Original;
667  parseFile(TestUtilities::testFilePath("matroska_wave1/test1.mkv"), &OverallTests::checkMkvTestfile1);
668  parseFile(TestUtilities::testFilePath("matroska_wave1/test2.mkv"), &OverallTests::checkMkvTestfile2);
669  parseFile(TestUtilities::testFilePath("matroska_wave1/test3.mkv"), &OverallTests::checkMkvTestfile3);
670  parseFile(TestUtilities::testFilePath("matroska_wave1/test4.mkv"), &OverallTests::checkMkvTestfile4);
671  parseFile(TestUtilities::testFilePath("matroska_wave1/test5.mkv"), &OverallTests::checkMkvTestfile5);
672  parseFile(TestUtilities::testFilePath("matroska_wave1/test6.mkv"), &OverallTests::checkMkvTestfile6);
673  parseFile(TestUtilities::testFilePath("matroska_wave1/test7.mkv"), &OverallTests::checkMkvTestfile7);
674  parseFile(TestUtilities::testFilePath("matroska_wave1/test8.mkv"), &OverallTests::checkMkvTestfile8);
675  parseFile(TestUtilities::testFilePath("mtx-test-data/mkv/handbrake-chapters-2.mkv"), &OverallTests::checkMkvTestfileHandbrakeChapters);
676  createMkvWithNestedTags();
677  if (!m_nestedTagsMkvPath.empty()) {
678  parseFile(m_nestedTagsMkvPath, &OverallTests::checkMkvTestfileNestedTags);
679  }
680 }
681 
682 #ifdef PLATFORM_UNIX
683 
690 void OverallTests::testMkvMakingWithDifferentSettings()
691 {
692  // full parse is required to determine padding
693  m_fileInfo.setForceFullParse(true);
694 
695  // do the test under different conditions
696  for (m_mode = 0; m_mode != 0x100; ++m_mode) {
697  using namespace MkvTestFlags;
698 
699  // setup test conditions
700  m_fileInfo.setForceRewrite(m_mode & ForceRewring);
701  if (m_mode & KeepTagPos) {
702  m_fileInfo.setTagPosition(ElementPosition::Keep);
703  } else {
704  m_fileInfo.setTagPosition(m_mode & TagsBeforeData ? ElementPosition::BeforeData : ElementPosition::AfterData);
705  }
706  if (m_mode & KeepIndexPos) {
707  if (m_mode & IndexBeforeData) {
708  continue;
709  }
710  m_fileInfo.setIndexPosition(ElementPosition::Keep);
711  } else {
712  m_fileInfo.setIndexPosition(m_mode & IndexBeforeData ? ElementPosition::BeforeData : ElementPosition::AfterData);
713  }
714  m_fileInfo.setPreferredPadding(m_mode & PaddingConstraints ? 4096 : 0);
715  m_fileInfo.setMinPadding(m_mode & PaddingConstraints ? 1024 : 0);
716  m_fileInfo.setMaxPadding(m_mode & PaddingConstraints ? (4096 + 1024) : numeric_limits<size_t>::max());
717  m_fileInfo.setForceTagPosition(m_mode & ForceTagPos);
718  m_fileInfo.setForceIndexPosition(m_mode & ForceIndexPos);
719 
720  // print test conditions
721  list<string> testConditions;
722  if (m_mode & ForceRewring) {
723  testConditions.emplace_back("forcing rewrite");
724  }
725  if (m_mode & KeepTagPos) {
726  if (m_mode & RemoveTag) {
727  testConditions.emplace_back("removing tag");
728  } else {
729  testConditions.emplace_back("keeping tag position");
730  }
731  } else if (m_mode & TagsBeforeData) {
732  testConditions.emplace_back("tags before data");
733  } else {
734  testConditions.emplace_back("tags after data");
735  }
736  if (m_mode & KeepIndexPos) {
737  testConditions.emplace_back("keeping index position");
738  } else if (m_mode & IndexBeforeData) {
739  testConditions.emplace_back("index before data");
740  } else {
741  testConditions.emplace_back("index after data");
742  }
743  if (m_mode & PaddingConstraints) {
744  testConditions.emplace_back("padding constraints");
745  }
746  if (m_mode & ForceTagPos) {
747  testConditions.emplace_back("forcing tag position");
748  }
749  if (m_mode & ForceIndexPos) {
750  testConditions.emplace_back("forcing index position");
751  }
752  cerr << endl << "Matroska maker - testmode " << m_mode << ": " << joinStrings(testConditions, ", ") << endl;
753 
754  // do actual tests
755  m_tagStatus = (m_mode & RemoveTag) ? TagStatus::Removed : TagStatus::TestMetaDataPresent;
756  void (OverallTests::*modifyRoutine)(void) = (m_mode & RemoveTag) ? &OverallTests::removeAllTags : &OverallTests::setMkvTestMetaData;
757  makeFile(TestUtilities::workingCopyPath("matroska_wave1/test1.mkv"), modifyRoutine, &OverallTests::checkMkvTestfile1);
758  makeFile(TestUtilities::workingCopyPath("matroska_wave1/test2.mkv"), modifyRoutine, &OverallTests::checkMkvTestfile2);
759  makeFile(TestUtilities::workingCopyPath("matroska_wave1/test3.mkv"), modifyRoutine, &OverallTests::checkMkvTestfile3);
760  makeFile(TestUtilities::workingCopyPath("matroska_wave1/test4.mkv"), modifyRoutine, &OverallTests::checkMkvTestfile4);
761  makeFile(TestUtilities::workingCopyPath("matroska_wave1/test5.mkv"), modifyRoutine, &OverallTests::checkMkvTestfile5);
762  makeFile(TestUtilities::workingCopyPath("matroska_wave1/test6.mkv"), modifyRoutine, &OverallTests::checkMkvTestfile6);
763  makeFile(TestUtilities::workingCopyPath("matroska_wave1/test7.mkv"), modifyRoutine, &OverallTests::checkMkvTestfile7);
764  makeFile(TestUtilities::workingCopyPath("matroska_wave1/test8.mkv"), modifyRoutine, &OverallTests::checkMkvTestfile8);
765  makeFile(TestUtilities::workingCopyPath("mtx-test-data/mkv/handbrake-chapters-2.mkv"), modifyRoutine,
766  &OverallTests::checkMkvTestfileHandbrakeChapters);
767  }
768 }
769 
774 void OverallTests::testMkvMakingNestedTags()
775 {
776  createMkvWithNestedTags();
777  if (!m_nestedTagsMkvPath.empty()) {
778  cerr << endl << "Matroska maker - rewrite file with nested tags" << endl;
779  m_fileInfo.setMinPadding(0);
780  m_fileInfo.setMaxPadding(0);
781  m_fileInfo.setTagPosition(ElementPosition::BeforeData);
782  m_fileInfo.setIndexPosition(ElementPosition::BeforeData);
783  makeFile(m_nestedTagsMkvPath, &OverallTests::noop, &OverallTests::checkMkvTestfileNestedTags);
784  }
785 }
786 #endif
void setFile(const std::string &path, Diagnostics &diag)
Sets the data, name and MIME-type for the specified path.
std::vector< IdType > IdContainerType
Definition: tagtarget.h:24
virtual bool setValue(KnownField field, const TagValue &value)=0
Assigns the given value to the specified field.
const TagTarget & target() const
Returns the target of tag.
Definition: tag.h:232
void setForced(bool forced)
Sets whether the track is forced.
The Size class defines the size of a two-dimensional object using integer point precision.
Definition: size.h:16
The OverallTests class tests reading and writing tags and parsing technical information for all suppo...
Definition: overall.h:42
TagType * createTag(const TagTarget &target=TagTarget()) override
Creates and returns a tag for the specified target.
void setName(const std::string &name)
Sets the name.
void setMimeType(const std::string &mimeType)
Sets the MIME-type of the attachment.
std::istream::pos_type startOffset() const
Returns the absolute start offset of the data block in the stream.
void testMkvParsing()
Tests the Matroska parser via MediaFileInfo.
Definition: overallmkv.cpp:662
std::istream::pos_type size() const
Returns the size of the data block.
std::istream & stream() const
Returns the associated stream.
void setName(const std::string &name)
Sets the (file) name of the attachment.
void setEnabled(bool enabled)
Sets whether the track is enabled.
void setLanguage(const std::string &language)
Sets the language of the track.
void setDefault(bool isDefault)
Sets whether the track is a default track.