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