Tag Parser 11.0.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 <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
18using namespace CppUtilities;
19
20namespace MkvTestFlags {
31};
32}
33
37void 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::ReleaseDate).toString());
67 break;
69 checkMkvTestMetaData();
70 break;
72 CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
73 }
74 CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Information);
75}
76
80void 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) {
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;
115 CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
116 }
117 CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Information);
118}
119
123void 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) {
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;
158 CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
159 }
160 CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Information);
161}
162
167void 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) {
189 CPPUNIT_ASSERT_EQUAL(Locale("und"sv, LocaleFormat::ISO_639_2_B), track->locale());
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(Locale("ger"sv, LocaleFormat::ISO_639_2_B), track->locale());
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) {
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
226void 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(Locale("ger"sv, LocaleFormat::ISO_639_2_B), track->locale());
249 break;
250 default:;
251 }
252 }
253 const auto tags = m_fileInfo.tags();
254 switch (m_tagStatus) {
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;
266 CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
267 }
268 CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Information);
269}
270
274void 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) {
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;
310 CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
311 }
312 CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Information);
313}
314
318void 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) {
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( // note: Typo "beggining" is present in `test7.mkv` from https://matroska.org/downloads/test_suite.html, do not fix it.
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;
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
372void 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) {
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;
409 CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
410 }
411 CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Information);
412}
413
417void 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) {
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;
475 CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
476 }
477 CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Information);
478}
479
483void 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) {
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;
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
529void 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(static_cast<std::streamoff>(attachmentData->startOffset()), std::ios_base::beg);
557 for (char expectedChar : m_testCover) {
558 CPPUNIT_ASSERT_EQUAL(expectedChar, static_cast<char>(attachmentSteam.get()));
559 }
560}
561
565void 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
589void 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->setLocale(Locale("ger"sv, LocaleFormat::ISO_639_2_B));
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, m_progress);
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
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}
The OverallTests class tests reading and writing tags and parsing technical information for all suppo...
Definition: overall.h:40
void testMkvMakingNestedTags()
Tests making a Matroska file with nested tags via MediaFileInfo.
Definition: overallmkv.cpp:740
void testMkvParsing()
Tests the Matroska parser via MediaFileInfo.
Definition: overallmkv.cpp:633
void testMkvMakingWithDifferentSettings()
Tests the Matroska maker via MediaFileInfo.
Definition: overallmkv.cpp:657
The AbstractAttachment class parses and stores attachment information.
void setFile(std::string_view path, Diagnostics &diag, AbortableProgressFeedback &progress)
Sets the data, name and MIME-type for the specified path.
void setName(std::string_view name)
Sets the (file) name of the attachment.
void setMimeType(std::string_view mimeType)
Sets the MIME-type of the attachment.
virtual ElementPosition determineTagPosition(Diagnostics &diag) const
Determines the position of the tags inside the file.
virtual ElementPosition determineIndexPosition(Diagnostics &diag) const
Determines the position of the index.
void setEnabled(bool enabled)
Sets whether the track is enabled.
void setDefault(bool isDefault)
Sets whether the track is a default track.
void setName(std::string_view name)
Sets the name.
void setForced(bool forced)
Sets whether the track is forced.
void setLocale(const Locale &locale)
Sets the locale of the track.
static std::string fileName(std::string_view path, bool cutExtension=false)
Returns the file name of the given file.
DiagLevel level() const
Returns the worst diag level present in the container.
Definition: diagnostics.cpp:55
Implementation of GenericContainer<MediaFileInfo, MatroskaTag, MatroskaTrack, EbmlElement>.
Implementation of TagParser::Tag for the Matroska container.
Definition: matroskatag.h:72
Implementation of TagParser::AbstractTrack for the Matroska container.
Definition: matroskatrack.h:48
std::uint64_t paddingSize() const
Returns the padding size.
std::vector< AbstractTrack * > tracks() const
Returns the tracks for the current file.
void setForceRewrite(bool forceRewrite)
Sets whether forcing rewriting (when applying changes) is enabled.
void setMaxPadding(std::size_t maxPadding)
Sets the maximum padding to be written before the data blocks when applying changes.
void setMinPadding(std::size_t minPadding)
Sets the minimum padding to be written before the data blocks when applying changes.
CppUtilities::TimeSpan duration() const
Returns the overall duration of the file if known; otherwise returns a TimeSpan with zero ticks.
void setForceTagPosition(bool forceTagPosition)
Sets whether tagPosition() is forced.
void setForceFullParse(bool forceFullParse)
Sets whether forcing a full parse is enabled.
void tags(std::vector< Tag * > &tags) const
Stores all tags assigned to the current file in the specified vector.
void setTagPosition(ElementPosition tagPosition)
Sets the position (in the output file) where the tag information is written when applying changes.
AbstractContainer * container() const
Returns the container for the current file.
ContainerFormat containerFormat() const
Returns the container format of the current file.
std::vector< AbstractChapter * > chapters() const
Returns all chapters assigned to the current file.
std::vector< AbstractAttachment * > attachments() const
Returns all attachments assigned to the current file.
bool removeTag(Tag *tag)
Removes a possibly assigned tag from the current file.
void setIndexPosition(ElementPosition indexPosition)
Sets the position (in the output file) where the index is written when applying changes.
void setPreferredPadding(std::size_t preferredPadding)
Sets the padding to be written before the data block when applying changes and the file needs to be r...
void setForceIndexPosition(bool forceTagPosition)
Sets whether indexPosition() is forced.
The Size class defines the size of a two-dimensional object using integer point precision.
Definition: size.h:17
The StreamDataBlock class is a reference to a certain data block of a stream.
std::uint64_t startOffset() const
Returns the absolute start offset of the data block in the stream.
std::uint64_t size() const
Returns the size of the data block.
std::istream & stream() const
Returns the associated stream.
The TagTarget class specifies the target of a tag.
Definition: tagtarget.h:20
std::vector< IdType > IdContainerType
Definition: tagtarget.h:23
std::uint64_t IdType
Definition: tagtarget.h:22
The TagValue class wraps values of different types.
Definition: tagvalue.h:95
std::int32_t toInteger() const
Converts the value of the current TagValue object to its equivalent integer representation.
Definition: tagvalue.cpp:322
std::string toString(TagTextEncoding encoding=TagTextEncoding::Unspecified) const
Converts the value of the current TagValue object to its equivalent std::string representation.
Definition: tagvalue.h:485
The Tag class is used to store, read and write tag information.
Definition: tag.h:108
const TagTarget & target() const
Returns the target of tag.
Definition: tag.h:184
virtual bool setValue(KnownField field, const TagValue &value)=0
Assigns the given value to the specified field.
@ TestMetaDataPresent
The Locale struct specifies a language and/or a country using one or more LocaleDetail objects.
Definition: localehelper.h:61