Tag Parser 12.3.1
C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
Loading...
Searching...
No Matches
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"
7#include "../mp4/mp4ids.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
33
37void OverallTests::checkMkvTestfile1()
38{
39 CPPUNIT_ASSERT_EQUAL(ContainerFormat::Matroska, m_fileInfo.containerFormat());
40 CPPUNIT_ASSERT_EQUAL(TimeSpan::fromMinutes(1.0) + TimeSpan::fromSeconds(27.0) + TimeSpan::fromMilliseconds(336.0), 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 CPPUNIT_ASSERT(track->isEnabled());
49 CPPUNIT_ASSERT(!track->isForced());
50 CPPUNIT_ASSERT(track->isDefault());
51 break;
52 case 3653291187:
53 CPPUNIT_ASSERT_EQUAL(MediaType::Audio, track->mediaType());
54 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Mpeg1Audio, track->format().general);
55 CPPUNIT_ASSERT_EQUAL(48000u, track->samplingFrequency());
56 CPPUNIT_ASSERT(track->isEnabled());
57 CPPUNIT_ASSERT(!track->isForced());
58 CPPUNIT_ASSERT(track->isDefault());
59 break;
60 default:
61 CPPUNIT_FAIL("unknown track ID");
62 }
63 }
64 const auto tags = m_fileInfo.tags();
65 switch (m_tagStatus) {
67 CPPUNIT_ASSERT_EQUAL(1_st, tags.size());
68 CPPUNIT_ASSERT_EQUAL("Big Buck Bunny - test 1"s, tags.front()->value(KnownField::Title).toString());
69 CPPUNIT_ASSERT_EQUAL(TagValue(), tags.front()->value(KnownField::Artist));
70 CPPUNIT_ASSERT_EQUAL(
71 "Matroska Validation File1, basic MPEG4.2 and MP3 with only SimpleBlock"s, tags.front()->value(KnownField::Comment).toString());
72 CPPUNIT_ASSERT_EQUAL("2010"s, tags.front()->value(KnownField::ReleaseDate).toString());
73 break;
75 checkMkvTestMetaData();
76 break;
78 CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
79 }
80 CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Information);
81}
82
86void OverallTests::checkMkvTestfile2()
87{
88 CPPUNIT_ASSERT_EQUAL(ContainerFormat::Matroska, m_fileInfo.containerFormat());
89 CPPUNIT_ASSERT_EQUAL(TimeSpan::fromSeconds(47.0) + TimeSpan::fromMilliseconds(509.0), m_fileInfo.duration());
90 const auto tracks = m_fileInfo.tracks();
91 CPPUNIT_ASSERT_EQUAL(2_st, tracks.size());
92 for (const auto &track : tracks) {
93 switch (track->id()) {
94 case 1863976627:
95 CPPUNIT_ASSERT_EQUAL(MediaType::Video, track->mediaType());
96 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Avc, track->format().general);
97 CPPUNIT_ASSERT_EQUAL(Size(1354, 576), track->displaySize());
98 CPPUNIT_ASSERT(track->isEnabled());
99 CPPUNIT_ASSERT(!track->isForced());
100 CPPUNIT_ASSERT(track->isDefault());
101 break;
102 case 3134325680:
103 CPPUNIT_ASSERT_EQUAL(MediaType::Audio, track->mediaType());
104 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Aac, track->format().general);
105 CPPUNIT_ASSERT_EQUAL(48000u, track->samplingFrequency());
106 CPPUNIT_ASSERT(track->isEnabled());
107 CPPUNIT_ASSERT(!track->isForced());
108 CPPUNIT_ASSERT(track->isDefault());
109 break;
110 default:
111 CPPUNIT_FAIL("unknown track ID");
112 }
113 }
114 const auto tags = m_fileInfo.tags();
115 switch (m_tagStatus) {
117 CPPUNIT_ASSERT_EQUAL(1_st, tags.size());
118 CPPUNIT_ASSERT_EQUAL("Elephant Dream - test 2"s, tags.front()->value(KnownField::Title).toString());
119 CPPUNIT_ASSERT_EQUAL(TagValue(), tags.front()->value(KnownField::Artist));
120 CPPUNIT_ASSERT_EQUAL("Matroska Validation File 2, 100,000 timecode scale, odd aspect ratio, and CRC-32. Codecs are AVC and AAC"s,
121 tags.front()->value(KnownField::Comment).toString());
122 break;
124 checkMkvTestMetaData();
125 break;
127 CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
128 }
129 CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Information);
130}
131
135void OverallTests::checkMkvTestfile3()
136{
137 CPPUNIT_ASSERT_EQUAL(ContainerFormat::Matroska, m_fileInfo.containerFormat());
138 CPPUNIT_ASSERT_EQUAL(TimeSpan::fromSeconds(49.0) + TimeSpan::fromMilliseconds(64.0), m_fileInfo.duration());
139 const auto tracks = m_fileInfo.tracks();
140 CPPUNIT_ASSERT_EQUAL(2_st, tracks.size());
141 for (const auto &track : tracks) {
142 switch (track->id()) {
143 case 3927961528:
144 CPPUNIT_ASSERT_EQUAL(MediaType::Video, track->mediaType());
145 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Avc, track->format().general);
146 CPPUNIT_ASSERT_EQUAL(Size(1024, 576), track->displaySize());
147 CPPUNIT_ASSERT(track->isEnabled());
148 CPPUNIT_ASSERT(!track->isForced());
149 CPPUNIT_ASSERT(track->isDefault());
150 break;
151 case 3391885737:
152 CPPUNIT_ASSERT_EQUAL(MediaType::Audio, track->mediaType());
153 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Mpeg1Audio, track->format().general);
154 CPPUNIT_ASSERT_EQUAL(48000u, track->samplingFrequency());
155 CPPUNIT_ASSERT(track->isEnabled());
156 CPPUNIT_ASSERT(!track->isForced());
157 CPPUNIT_ASSERT(track->isDefault());
158 break;
159 default:
160 CPPUNIT_FAIL("unknown track ID");
161 }
162 }
163 const auto tags = m_fileInfo.tags();
164 switch (m_tagStatus) {
166 CPPUNIT_ASSERT_EQUAL(1_st, tags.size());
167 CPPUNIT_ASSERT_EQUAL("Elephant Dream - test 3"s, tags.front()->value(KnownField::Title).toString());
168 CPPUNIT_ASSERT_EQUAL(TagValue(), tags.front()->value(KnownField::Artist));
169 CPPUNIT_ASSERT_EQUAL("Matroska Validation File 3, header stripping on the video track and no SimpleBlock"s,
170 tags.front()->value(KnownField::Comment).toString());
171 break;
173 checkMkvTestMetaData();
174 break;
176 CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
177 }
178 CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Information);
179}
180
185void OverallTests::checkMkvTestfile4()
186{
187 CPPUNIT_ASSERT_EQUAL(ContainerFormat::Matroska, m_fileInfo.containerFormat());
188 CPPUNIT_ASSERT_EQUAL(TimeSpan(), m_fileInfo.duration());
189 // this file is messed up, it should contain tags but it doesn't
190 const auto tracks = m_fileInfo.tracks();
191 CPPUNIT_ASSERT_EQUAL(2_st, tracks.size());
192 for (const auto &track : tracks) {
193 switch (track->id()) {
194 case 1368622492:
195 CPPUNIT_ASSERT_EQUAL(MediaType::Video, track->mediaType());
196 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Theora, track->format().general);
197 CPPUNIT_ASSERT_EQUAL(Size(1280, 720), track->displaySize());
198 break;
199 case 3171450505:
200 CPPUNIT_ASSERT_EQUAL(MediaType::Audio, track->mediaType());
201 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Vorbis, track->format().general);
202 CPPUNIT_ASSERT_EQUAL(48000u, track->samplingFrequency());
203 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint16_t>(2u), track->channelCount());
204 switch (m_tagStatus) {
207 CPPUNIT_ASSERT_EQUAL(Locale("und"sv, LocaleFormat::ISO_639_2_B), track->locale());
208 CPPUNIT_ASSERT_EQUAL(string(), track->name());
209 CPPUNIT_ASSERT(track->isEnabled());
210 CPPUNIT_ASSERT(!track->isForced());
211 CPPUNIT_ASSERT(track->isDefault());
212 break;
214 CPPUNIT_ASSERT_EQUAL(Locale("ger"sv, LocaleFormat::ISO_639_2_B), track->locale());
215 CPPUNIT_ASSERT_EQUAL("the name"s, track->name());
216 CPPUNIT_ASSERT(track->isEnabled());
217 CPPUNIT_ASSERT(track->isForced());
218 CPPUNIT_ASSERT(!track->isDefault());
219 break;
220 }
221 break;
222 default:
223 CPPUNIT_FAIL("unknown track ID");
224 }
225 }
226 const auto tags = m_fileInfo.tags();
227 switch (m_tagStatus) {
230 CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
231 break;
233 checkMkvTestMetaData();
234 break;
235 }
236
237 // tolerate critical notifications here because live stream feature used by the file is not supported in v6 yet
238 CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Critical);
239}
240
244void OverallTests::checkMkvTestfile5()
245{
246 CPPUNIT_ASSERT_EQUAL(ContainerFormat::Matroska, m_fileInfo.containerFormat());
247 CPPUNIT_ASSERT_EQUAL(TimeSpan::fromSeconds(46.0) + TimeSpan::fromMilliseconds(665.0), m_fileInfo.duration());
248 const auto tracks = m_fileInfo.tracks();
249 CPPUNIT_ASSERT_EQUAL(11_st, tracks.size());
250 for (const auto &track : tracks) {
251 switch (track->id()) {
252 case 1258329745:
253 CPPUNIT_ASSERT_EQUAL(MediaType::Video, track->mediaType());
254 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Avc, track->format().general);
255 CPPUNIT_ASSERT_EQUAL(Size(1024, 576), track->displaySize());
256 CPPUNIT_ASSERT_EQUAL(true, track->isDefault());
257 CPPUNIT_ASSERT_EQUAL(true, track->isEnabled());
258 CPPUNIT_ASSERT_EQUAL(false, track->isForced());
259 break;
260 case 3452711582:
261 CPPUNIT_ASSERT_EQUAL(MediaType::Audio, track->mediaType());
262 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Aac, track->format().general);
263 CPPUNIT_ASSERT_EQUAL(48000u, track->samplingFrequency());
264 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint8_t>(Mpeg4ChannelConfigs::FrontLeftFrontRight), track->channelConfig());
265 CPPUNIT_ASSERT_EQUAL(true, track->isDefault());
266 CPPUNIT_ASSERT_EQUAL(true, track->isEnabled());
267 CPPUNIT_ASSERT_EQUAL(false, track->isForced());
268 break;
269 case 3554194305:
270 CPPUNIT_ASSERT_EQUAL(MediaType::Text, track->mediaType());
271 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::TextSubtitle, track->format().general);
272 CPPUNIT_ASSERT_EQUAL(Locale("ger"sv, LocaleFormat::ISO_639_2_B), track->locale());
273 break;
274 default:;
275 }
276 }
277 const auto tags = m_fileInfo.tags();
278 switch (m_tagStatus) {
280 CPPUNIT_ASSERT_EQUAL(1_st, tags.size());
281 CPPUNIT_ASSERT_EQUAL("Big Buck Bunny - test 8"s, tags.front()->value(KnownField::Title).toString());
282 CPPUNIT_ASSERT_EQUAL(TagValue(), tags.front()->value(KnownField::Artist));
283 CPPUNIT_ASSERT_EQUAL("Matroska Validation File 8, secondary audio commentary track, misc subtitle tracks"s,
284 tags.front()->value(KnownField::Comment).toString());
285 break;
287 checkMkvTestMetaData();
288 break;
290 CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
291 }
292 CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Information);
293}
294
298void OverallTests::checkMkvTestfile6()
299{
300 CPPUNIT_ASSERT_EQUAL(ContainerFormat::Matroska, m_fileInfo.containerFormat());
301 CPPUNIT_ASSERT_EQUAL(TimeSpan::fromMinutes(1.0) + TimeSpan::fromSeconds(27.0) + TimeSpan::fromMilliseconds(336.0), m_fileInfo.duration());
302 const auto tracks = m_fileInfo.tracks();
303 CPPUNIT_ASSERT_EQUAL(2_st, tracks.size());
304 for (const auto &track : tracks) {
305 switch (track->id()) {
306 case 2422994868:
307 CPPUNIT_ASSERT_EQUAL(MediaType::Video, track->mediaType());
308 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::MicrosoftMpeg4, track->format().general);
309 CPPUNIT_ASSERT_EQUAL(Size(854, 480), track->displaySize());
310 CPPUNIT_ASSERT_EQUAL(false, track->isDefault());
311 CPPUNIT_ASSERT_EQUAL(true, track->isEnabled());
312 CPPUNIT_ASSERT_EQUAL(false, track->isForced());
313 break;
314 case 3653291187:
315 CPPUNIT_ASSERT_EQUAL(MediaType::Audio, track->mediaType());
316 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Mpeg1Audio, track->format().general);
317 CPPUNIT_ASSERT_EQUAL(48000u, track->samplingFrequency());
318 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint8_t>(MpegChannelMode::Stereo), track->channelConfig());
319 CPPUNIT_ASSERT_EQUAL(false, track->isDefault());
320 CPPUNIT_ASSERT_EQUAL(true, track->isEnabled());
321 CPPUNIT_ASSERT_EQUAL(false, track->isForced());
322 break;
323 default:
324 CPPUNIT_FAIL("unknown track ID");
325 }
326 }
327 const auto tags = m_fileInfo.tags();
328 switch (m_tagStatus) {
330 CPPUNIT_ASSERT_EQUAL(1_st, tags.size());
331 CPPUNIT_ASSERT_EQUAL("Big Buck Bunny - test 6"s, tags.front()->value(KnownField::Title).toString());
332 CPPUNIT_ASSERT_EQUAL(TagValue(), tags.front()->value(KnownField::Artist));
333 CPPUNIT_ASSERT_EQUAL("Matroska Validation File 6, random length to code the size of Clusters and Blocks, no Cues for seeking"s,
334 tags.front()->value(KnownField::Comment).toString());
335 break;
337 checkMkvTestMetaData();
338 break;
340 CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
341 }
342 CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Information);
343}
344
348void OverallTests::checkMkvTestfile7()
349{
350 CPPUNIT_ASSERT_EQUAL(ContainerFormat::Matroska, m_fileInfo.containerFormat());
351 CPPUNIT_ASSERT_EQUAL(TimeSpan::fromSeconds(37.0) + TimeSpan::fromMilliseconds(43.0), m_fileInfo.duration());
352 const auto tracks = m_fileInfo.tracks();
353 CPPUNIT_ASSERT_EQUAL(2_st, tracks.size());
354 for (const auto &track : tracks) {
355 switch (track->id()) {
356 case 568001708:
357 CPPUNIT_ASSERT_EQUAL(MediaType::Video, track->mediaType());
358 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Avc, track->format().general);
359 CPPUNIT_ASSERT_EQUAL(Size(1024, 576), track->displaySize());
360 CPPUNIT_ASSERT_EQUAL("YUV 4:2:0"s, string(track->chromaFormat()));
361 CPPUNIT_ASSERT_EQUAL(false, track->isDefault());
362 CPPUNIT_ASSERT_EQUAL(true, track->isEnabled());
363 CPPUNIT_ASSERT_EQUAL(false, track->isForced());
364 break;
365 case 2088735154:
366 CPPUNIT_ASSERT_EQUAL(MediaType::Audio, track->mediaType());
367 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Aac, track->format().general);
368 CPPUNIT_ASSERT_EQUAL(48000u, track->samplingFrequency());
369 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint8_t>(Mpeg4ChannelConfigs::FrontLeftFrontRight), track->channelConfig());
370 CPPUNIT_ASSERT_EQUAL(false, track->isDefault());
371 CPPUNIT_ASSERT_EQUAL(true, track->isEnabled());
372 CPPUNIT_ASSERT_EQUAL(false, track->isForced());
373 break;
374 default:
375 CPPUNIT_FAIL("unknown track ID");
376 }
377 }
378 const auto tags = m_fileInfo.tags();
379 switch (m_tagStatus) {
381 CPPUNIT_ASSERT_EQUAL(1_st, tags.size());
382 CPPUNIT_ASSERT_EQUAL("Big Buck Bunny - test 7"s, tags.front()->value(KnownField::Title).toString());
383 CPPUNIT_ASSERT_EQUAL(TagValue(), tags.front()->value(KnownField::Artist));
384 CPPUNIT_ASSERT_EQUAL( // note: Typo "beggining" is present in `test7.mkv` from https://matroska.org/downloads/test_suite.html, do not fix it.
385 "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,
386 tags.front()->value(KnownField::Comment).toString());
387 break;
389 checkMkvTestMetaData();
390 break;
392 CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
393 }
394
395 for (const auto &msg : m_diag) {
396 if (msg.level() != DiagLevel::Warning) {
397 continue;
398 }
399 CPPUNIT_ASSERT(startsWith(msg.context(), "parsing header of EBML element 0xEA \"cue codec state\" at"));
400 CPPUNIT_ASSERT_EQUAL("Data of EBML element seems to be truncated; unable to parse siblings of that element."s, msg.message());
401 }
402 CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Warning);
403}
404
408void OverallTests::checkMkvTestfile8()
409{
410 CPPUNIT_ASSERT_EQUAL(ContainerFormat::Matroska, m_fileInfo.containerFormat());
411 CPPUNIT_ASSERT_EQUAL(TimeSpan::fromSeconds(47.0) + TimeSpan::fromMilliseconds(341.0), m_fileInfo.duration());
412 const auto tracks = m_fileInfo.tracks();
413 CPPUNIT_ASSERT_EQUAL(2_st, tracks.size());
414 for (const auto &track : tracks) {
415 switch (track->id()) {
416 case 568001708:
417 CPPUNIT_ASSERT_EQUAL(MediaType::Video, track->mediaType());
418 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Avc, track->format().general);
419 CPPUNIT_ASSERT_EQUAL(Size(1024, 576), track->displaySize());
420 CPPUNIT_ASSERT_EQUAL("YUV 4:2:0"s, string(track->chromaFormat()));
421 CPPUNIT_ASSERT_EQUAL(false, track->isDefault());
422 CPPUNIT_ASSERT_EQUAL(true, track->isEnabled());
423 CPPUNIT_ASSERT_EQUAL(false, track->isForced());
424 break;
425 case 2088735154:
426 CPPUNIT_ASSERT_EQUAL(MediaType::Audio, track->mediaType());
427 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Aac, track->format().general);
428 CPPUNIT_ASSERT_EQUAL(48000u, track->samplingFrequency());
429 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint8_t>(Mpeg4ChannelConfigs::FrontLeftFrontRight), track->channelConfig());
430 CPPUNIT_ASSERT_EQUAL(false, track->isDefault());
431 CPPUNIT_ASSERT_EQUAL(true, track->isEnabled());
432 CPPUNIT_ASSERT_EQUAL(false, track->isForced());
433 break;
434 default:
435 CPPUNIT_FAIL("unknown track ID");
436 }
437 }
438 const auto tags = m_fileInfo.tags();
439 switch (m_tagStatus) {
441 CPPUNIT_ASSERT_EQUAL(1_st, tags.size());
442 CPPUNIT_ASSERT_EQUAL("Big Buck Bunny - test 8"s, tags.front()->value(KnownField::Title).toString());
443 CPPUNIT_ASSERT_EQUAL(TagValue(), tags.front()->value(KnownField::Artist));
444 CPPUNIT_ASSERT_EQUAL(
445 "Matroska Validation File 8, audio missing between timecodes 6.019s and 6.360s"s, tags.front()->value(KnownField::Comment).toString());
446 break;
448 checkMkvTestMetaData();
449 break;
451 CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
452 }
453 CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Information);
454}
455
459void OverallTests::checkMkvTestfileHandbrakeChapters()
460{
461 CPPUNIT_ASSERT_EQUAL(ContainerFormat::Matroska, m_fileInfo.containerFormat());
462 CPPUNIT_ASSERT_EQUAL(TimeSpan::fromSeconds(27.0) + TimeSpan::fromMilliseconds(569.0), m_fileInfo.duration());
463 const auto tracks = m_fileInfo.tracks();
464 CPPUNIT_ASSERT_EQUAL(2_st, tracks.size());
465 for (const auto &track : tracks) {
466 switch (track->id()) {
467 case 1:
468 CPPUNIT_ASSERT_EQUAL(MediaType::Video, track->mediaType());
469 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Avc, track->format().general);
470 CPPUNIT_ASSERT_EQUAL(4.0, track->version());
471 CPPUNIT_ASSERT_EQUAL(Size(1280, 544), track->pixelSize());
472 CPPUNIT_ASSERT_EQUAL(Size(1280, 544), track->displaySize());
473 CPPUNIT_ASSERT_EQUAL(23u, track->fps());
474 CPPUNIT_ASSERT_EQUAL(true, track->isDefault());
475 CPPUNIT_ASSERT_EQUAL(true, track->isEnabled());
476 CPPUNIT_ASSERT_EQUAL(false, track->isForced());
477 break;
478 case 2:
479 CPPUNIT_ASSERT_EQUAL(MediaType::Audio, track->mediaType());
480 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Aac, track->format().general);
481 CPPUNIT_ASSERT_EQUAL(44100u, track->samplingFrequency());
482 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint8_t>(Mpeg4ChannelConfigs::FrontLeftFrontRight), track->channelConfig());
483 CPPUNIT_ASSERT_EQUAL(true, track->isDefault());
484 CPPUNIT_ASSERT_EQUAL(true, track->isEnabled());
485 CPPUNIT_ASSERT_EQUAL(false, track->isForced());
486 break;
487 default:
488 CPPUNIT_FAIL(argsToString("unknown track ID ", track->id()));
489 }
490 }
491 const auto chapters = m_fileInfo.chapters();
492 CPPUNIT_ASSERT_EQUAL(2_st, chapters.size());
493 for (const auto &chapter : chapters) {
494 switch (chapter->id()) {
495 case 1:
496 CPPUNIT_ASSERT_EQUAL("Kapitel 01"s, static_cast<const string &>(chapter->names().at(0)));
497 CPPUNIT_ASSERT_EQUAL(static_cast<std::int64_t>(0), chapter->startTime().totalTicks());
498 CPPUNIT_ASSERT_EQUAL(15, chapter->endTime().seconds());
499 break;
500 case 2:
501 CPPUNIT_ASSERT_EQUAL("Kapitel 02"s, static_cast<const string &>(chapter->names().at(0)));
502 CPPUNIT_ASSERT_EQUAL(15, chapter->startTime().seconds());
503 CPPUNIT_ASSERT_EQUAL(27, chapter->endTime().seconds());
504 break;
505 default:
506 CPPUNIT_FAIL(argsToString("unknown chapter ID ", chapter->id()));
507 }
508 }
509 const auto tags = m_fileInfo.tags();
510 switch (m_tagStatus) {
512 CPPUNIT_ASSERT_EQUAL(2_st, tags.size());
513 CPPUNIT_ASSERT(tags[0]->target().isEmpty());
514 CPPUNIT_ASSERT_EQUAL(""s, static_cast<MatroskaTag *>(tags[0])->value("CREATION_TIME").toString());
515 CPPUNIT_ASSERT_EQUAL("Lavf55.12.0"s, tags[0]->value(KnownField::Encoder).toString());
516 CPPUNIT_ASSERT_EQUAL(static_cast<TagTarget::IdType>(2), tags[1]->target().tracks().at(0));
517 CPPUNIT_ASSERT_EQUAL("eng"s, tags[1]->value(KnownField::Language).toString());
518 break;
520 checkMkvTestMetaData();
521 break;
523 CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
524 }
525 CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Information);
526}
527
531void OverallTests::checkMkvTestfileNestedTags()
532{
533 CPPUNIT_ASSERT_EQUAL(ContainerFormat::Matroska, m_fileInfo.containerFormat());
534 const auto tags = m_fileInfo.tags();
535 bool generalTagFound = false;
536 switch (m_tagStatus) {
539 CPPUNIT_ASSERT_EQUAL(5_st, tags.size());
540 for (const Tag *tag : tags) {
541 CPPUNIT_ASSERT(tag->type() == TagType::MatroskaTag);
542 const auto *mkvTag = static_cast<const MatroskaTag *>(tag);
543 const auto &target = mkvTag->target();
544 if (target.level() == 50 && target.tracks().empty()) {
545 generalTagFound = true;
546 CPPUNIT_ASSERT_EQUAL("Vanilla Sky"s, tag->value(KnownField::Title).toString());
547 const auto &fields = mkvTag->fields();
548 const auto &artistField = fields.find(mkvTag->fieldId(KnownField::Artist));
549 CPPUNIT_ASSERT(artistField != fields.end());
550 CPPUNIT_ASSERT_EQUAL("Test artist"s, artistField->second.value().toString());
551 const auto &nestedFields = artistField->second.nestedFields();
552 CPPUNIT_ASSERT_EQUAL(1_st, nestedFields.size());
553 CPPUNIT_ASSERT_EQUAL("ADDRESS"s, nestedFields[0].idToString());
554 CPPUNIT_ASSERT_EQUAL("Test address"s, nestedFields[0].value().toString());
555 }
556 }
557 CPPUNIT_ASSERT(generalTagFound);
558 break;
560 CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
561 }
562
563 // the file contains in fact the unknown element [44][B4]
564 // TODO: find out what this element is about (its data is only the single byte 0x01)
565 for (const auto &msg : m_diag) {
566 if (msg.level() != DiagLevel::Warning) {
567 continue;
568 }
569 CPPUNIT_ASSERT(startsWith(msg.message(), "\"SimpleTag\"-element contains unknown element 0x44B4 at"));
570 }
571 CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Warning);
572}
573
577void OverallTests::checkMkvTestMetaData()
578{
579 // check tags
580 const auto tags = m_fileInfo.tags();
581 const auto tracks = m_fileInfo.tracks();
582 CPPUNIT_ASSERT_EQUAL(2_st, tags.size());
583 CPPUNIT_ASSERT_EQUAL(m_testTitle.toString(), tags.front()->value(KnownField::Title).toString());
584 CPPUNIT_ASSERT(tags.front()->value(KnownField::Artist).isEmpty());
585 CPPUNIT_ASSERT_EQUAL(m_testComment.toString(), tags.front()->value(KnownField::Comment).toString());
586 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint64_t>(30), tags[1]->target().level());
587 CPPUNIT_ASSERT_EQUAL(tracks.at(0)->id(), tags[1]->target().tracks().at(0));
588 CPPUNIT_ASSERT_EQUAL(m_testAlbum.toString(), tags[1]->value(KnownField::Album).toString());
589 CPPUNIT_ASSERT_EQUAL(m_testPartNumber.toInteger(), tags[1]->value(KnownField::PartNumber).toInteger());
590 CPPUNIT_ASSERT_EQUAL(m_testTotalParts.toInteger(), tags[1]->value(KnownField::TotalParts).toInteger());
591
592 // check attachments
593 const auto attachments = m_fileInfo.attachments();
594 CPPUNIT_ASSERT_EQUAL(1_st, attachments.size());
595 CPPUNIT_ASSERT_EQUAL("image/png"s, attachments[0]->mimeType());
596 CPPUNIT_ASSERT_EQUAL("cover.jpg"s, attachments[0]->name());
597 const StreamDataBlock *attachmentData = attachments[0]->data();
598 CPPUNIT_ASSERT(attachmentData != nullptr);
599 if (m_testCover.empty()) {
600 m_testCover = readFile(testFilePath("matroska_wave1/logo3_256x256.png"), 20000);
601 }
602 CPPUNIT_ASSERT_EQUAL(m_testCover.size(), static_cast<size_t>(attachmentData->size()));
603 istream &attachmentSteam = attachmentData->stream();
604 attachmentSteam.seekg(static_cast<std::streamoff>(attachmentData->startOffset()), std::ios_base::beg);
605 for (char expectedChar : m_testCover) {
606 CPPUNIT_ASSERT_EQUAL(expectedChar, static_cast<char>(attachmentSteam.get()));
607 }
608}
609
613void OverallTests::checkMkvConstraints()
614{
615 using namespace MkvTestFlags;
616
617 CPPUNIT_ASSERT(m_fileInfo.container());
618 if (m_mode & PaddingConstraints) {
619 if (m_mode & ForceRewring) {
620 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint64_t>(4096), m_fileInfo.paddingSize());
621 } else {
622 CPPUNIT_ASSERT(m_fileInfo.paddingSize() >= 1024);
623 CPPUNIT_ASSERT(m_fileInfo.paddingSize() <= (4096 + 1024));
624 }
625 if (!(m_mode & RemoveTag) && (m_expectedTagPos != ElementPosition::Keep) && ((m_mode & ForceRewring) || (m_mode & ForceTagPos))) {
626 CPPUNIT_ASSERT_EQUAL(m_expectedTagPos, m_fileInfo.container()->determineTagPosition(m_diag));
627 }
628 if ((m_expectedIndexPos != ElementPosition::Keep) && ((m_mode & ForceRewring) || (m_mode & ForceIndexPos))) {
629 CPPUNIT_ASSERT_EQUAL(m_expectedIndexPos, m_fileInfo.container()->determineIndexPosition(m_diag));
630 }
631 }
632}
633
637void OverallTests::setMkvTestMetaData()
638{
639 CPPUNIT_ASSERT_EQUAL(ContainerFormat::Matroska, m_fileInfo.containerFormat());
640 auto *container = static_cast<MatroskaContainer *>(m_fileInfo.container());
641
642 // change the present tag
643 const string fileName(m_fileInfo.fileName());
644 if (fileName == "test4.mkv") {
645 // test4.mkv has no tag, so one must be created first
646 container->createTag(TagTarget(50));
647 // also change language, name, forced and default of track "3171450505" to German
648 MatroskaTrack *track = container->trackById(3171450505);
649 CPPUNIT_ASSERT(track);
650 track->setLocale(Locale("ger"sv, LocaleFormat::ISO_639_2_B));
651 track->setName("the name");
652 track->setDefault(false);
653 track->setEnabled(true);
654 track->setForced(true);
655 } else if (fileName == "handbrake-chapters-2.mkv") {
656 // remove 2nd tag
657 m_fileInfo.removeTag(m_fileInfo.tags().at(1));
658 }
659 Tag *firstTag = m_fileInfo.tags().at(0);
660 firstTag->setValue(KnownField::Title, m_testTitle);
661 firstTag->setValue(KnownField::Comment, m_testComment);
662 // add an additional tag targeting the first track
664 trackIds.emplace_back(m_fileInfo.tracks().at(0)->id());
665 Tag *newTag = container->createTag(TagTarget(30, trackIds));
666 CPPUNIT_ASSERT_MESSAGE("create tag", newTag);
667 newTag->setValue(KnownField::Album, m_testAlbum);
668 newTag->setValue(KnownField::PartNumber, m_testPartNumber);
669 newTag->setValue(KnownField::TotalParts, m_testTotalParts);
670 // assign an attachment
671 AbstractAttachment *const attachment = container->createAttachment();
672 CPPUNIT_ASSERT_MESSAGE("create attachment", attachment);
673 attachment->setFile(testFilePath("matroska_wave1/logo3_256x256.png"), m_diag, m_progress);
674 attachment->setMimeType("image/png");
675 attachment->setName("cover.jpg");
676}
677
682{
683 cerr << endl << "Matroska parser" << endl;
684 m_fileInfo.setForceFullParse(false);
685 m_tagStatus = TagStatus::Original;
686 parseFile(testFilePath("matroska_wave1/test1.mkv"), &OverallTests::checkMkvTestfile1);
687 parseFile(testFilePath("matroska_wave1/test2.mkv"), &OverallTests::checkMkvTestfile2);
688 parseFile(testFilePath("matroska_wave1/test3.mkv"), &OverallTests::checkMkvTestfile3);
689 parseFile(testFilePath("matroska_wave1/test4.mkv"), &OverallTests::checkMkvTestfile4);
690 parseFile(testFilePath("matroska_wave1/test5.mkv"), &OverallTests::checkMkvTestfile5);
691 parseFile(testFilePath("matroska_wave1/test6.mkv"), &OverallTests::checkMkvTestfile6);
692 parseFile(testFilePath("matroska_wave1/test7.mkv"), &OverallTests::checkMkvTestfile7);
693 parseFile(testFilePath("matroska_wave1/test8.mkv"), &OverallTests::checkMkvTestfile8);
694 parseFile(testFilePath("mtx-test-data/mkv/handbrake-chapters-2.mkv"), &OverallTests::checkMkvTestfileHandbrakeChapters);
695 parseFile(testFilePath("mkv/nested-tags.mkv"), &OverallTests::checkMkvTestfileNestedTags);
696}
697
706{
707 // full parse is required to determine padding
708 m_fileInfo.setForceFullParse(true);
709
710 // do the test under different conditions
711 for (m_mode = 0; m_mode != 0x100; ++m_mode) {
712 using namespace MkvTestFlags;
713
714 // setup test conditions
715 m_fileInfo.setForceRewrite(m_mode & ForceRewring);
716 if (m_mode & KeepTagPos) {
717 m_fileInfo.setTagPosition(ElementPosition::Keep);
718 } else {
719 m_fileInfo.setTagPosition(m_mode & TagsBeforeData ? ElementPosition::BeforeData : ElementPosition::AfterData);
720 }
721 if (m_mode & KeepIndexPos) {
722 if (m_mode & IndexBeforeData) {
723 continue;
724 }
725 m_fileInfo.setIndexPosition(ElementPosition::Keep);
726 } else {
727 m_fileInfo.setIndexPosition(m_mode & IndexBeforeData ? ElementPosition::BeforeData : ElementPosition::AfterData);
728 }
729 m_fileInfo.setPreferredPadding(m_mode & PaddingConstraints ? 4096 : 0);
730 m_fileInfo.setMinPadding(m_mode & PaddingConstraints ? 1024 : 0);
731 m_fileInfo.setMaxPadding(m_mode & PaddingConstraints ? (4096 + 1024) : numeric_limits<size_t>::max());
732 m_fileInfo.setForceTagPosition(m_mode & ForceTagPos);
733 m_fileInfo.setForceIndexPosition(m_mode & ForceIndexPos);
734
735 // print test conditions
736 list<string> testConditions;
737 if (m_mode & ForceRewring) {
738 testConditions.emplace_back("forcing rewrite");
739 }
740 if (m_mode & KeepTagPos) {
741 if (m_mode & RemoveTag) {
742 testConditions.emplace_back("removing tag");
743 } else {
744 testConditions.emplace_back("keeping tag position");
745 }
746 } else if (m_mode & TagsBeforeData) {
747 testConditions.emplace_back("tags before data");
748 } else {
749 testConditions.emplace_back("tags after data");
750 }
751 if (m_mode & KeepIndexPos) {
752 testConditions.emplace_back("keeping index position");
753 } else if (m_mode & IndexBeforeData) {
754 testConditions.emplace_back("index before data");
755 } else {
756 testConditions.emplace_back("index after data");
757 }
758 if (m_mode & PaddingConstraints) {
759 testConditions.emplace_back("padding constraints");
760 }
761 if (m_mode & ForceTagPos) {
762 testConditions.emplace_back("forcing tag position");
763 }
764 if (m_mode & ForceIndexPos) {
765 testConditions.emplace_back("forcing index position");
766 }
767 cerr << endl << "Matroska maker - testmode " << m_mode << ": " << joinStrings(testConditions, ", ") << endl;
768
769 // do actual tests
770 m_tagStatus = (m_mode & RemoveTag) ? TagStatus::Removed : TagStatus::TestMetaDataPresent;
771 void (OverallTests::*modifyRoutine)(void) = (m_mode & RemoveTag) ? &OverallTests::removeAllTags : &OverallTests::setMkvTestMetaData;
772 makeFile(workingCopyPath("matroska_wave1/test1.mkv"), modifyRoutine, &OverallTests::checkMkvTestfile1);
773 makeFile(workingCopyPath("matroska_wave1/test2.mkv"), modifyRoutine, &OverallTests::checkMkvTestfile2);
774 makeFile(workingCopyPath("matroska_wave1/test3.mkv"), modifyRoutine, &OverallTests::checkMkvTestfile3);
775 makeFile(workingCopyPath("matroska_wave1/test4.mkv"), modifyRoutine, &OverallTests::checkMkvTestfile4);
776 makeFile(workingCopyPath("matroska_wave1/test5.mkv"), modifyRoutine, &OverallTests::checkMkvTestfile5);
777 makeFile(workingCopyPath("matroska_wave1/test6.mkv"), modifyRoutine, &OverallTests::checkMkvTestfile6);
778 makeFile(workingCopyPath("matroska_wave1/test7.mkv"), modifyRoutine, &OverallTests::checkMkvTestfile7);
779 makeFile(workingCopyPath("matroska_wave1/test8.mkv"), modifyRoutine, &OverallTests::checkMkvTestfile8);
780 makeFile(workingCopyPath("mtx-test-data/mkv/handbrake-chapters-2.mkv"), modifyRoutine, &OverallTests::checkMkvTestfileHandbrakeChapters);
781 }
782}
783
789{
790 cerr << endl << "Matroska maker - rewrite file with nested tags" << endl;
791 m_fileInfo.setMinPadding(0);
792 m_fileInfo.setMaxPadding(0);
793 m_fileInfo.setTagPosition(ElementPosition::BeforeData);
794 m_fileInfo.setIndexPosition(ElementPosition::BeforeData);
795 makeFile(workingCopyPath("mkv/nested-tags.mkv"), &OverallTests::noop, &OverallTests::checkMkvTestfileNestedTags);
796}
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.
void testMkvParsing()
Tests the Matroska parser via MediaFileInfo.
void testMkvMakingWithDifferentSettings()
Tests the Matroska maker via MediaFileInfo.
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.
Implementation of GenericContainer<MediaFileInfo, MatroskaTag, MatroskaTrack, EbmlElement>.
Implementation of TagParser::Tag for the Matroska container.
Implementation of TagParser::AbstractTrack for the Matroska container.
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.
std::vector< IdType > IdContainerType
Definition tagtarget.h:23
std::uint64_t IdType
Definition tagtarget.h:22
The TagValue class wraps values of different types.
std::int32_t toInteger() const
Converts the value of the current TagValue object to its equivalent integer representation.
Definition tagvalue.cpp:540
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:450
The Tag class is used to store, read and write tag information.
virtual bool setValue(KnownField field, const TagValue &value)=0
Assigns the given value to the specified field.
const TagTarget & target() const
Definition tag.h:246
@ TestMetaDataPresent
The Locale struct specifies a language and/or a country using one or more LocaleDetail objects.