Tag Parser 12.4.0
C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
Loading...
Searching...
No Matches
overallmp4.cpp
Go to the documentation of this file.
1#include "./helper.h"
2#include "./overall.h"
3
4#include "../abstracttrack.h"
5#include "../mp4/mp4atom.h"
7#include "../mp4/mp4ids.h"
8#include "../mp4/mp4tag.h"
9
10#include <c++utilities/application/commandlineutils.h>
11
12#include <limits>
13
14#include "resources/config.h"
15
16using namespace CppUtilities;
17
28
32void OverallTests::checkMp4Testfile1()
33{
34 CPPUNIT_ASSERT_EQUAL(ContainerFormat::Mp4, m_fileInfo.containerFormat());
35 const auto tracks = m_fileInfo.tracks();
36 CPPUNIT_ASSERT_EQUAL(1_st, tracks.size());
37 for (const auto &track : tracks) {
38 switch (track->id()) {
39 case 1:
40 CPPUNIT_ASSERT_EQUAL(MediaType::Audio, track->mediaType());
41 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Aac, track->format().general);
42 CPPUNIT_ASSERT_EQUAL(2012, track->creationTime().year());
43 CPPUNIT_ASSERT_EQUAL(44100u, track->samplingFrequency());
44 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint8_t>(Mpeg4ChannelConfigs::FrontLeftFrontRight), track->channelConfig());
45 break;
46 default:
47 CPPUNIT_FAIL("unknown track ID");
48 }
49 }
50 const auto tags = m_fileInfo.tags();
51 switch (m_tagStatus) {
53 CPPUNIT_ASSERT_EQUAL(1_st, tags.size());
54 CPPUNIT_ASSERT_EQUAL("Danse Macabre, Op.40"s, tags.front()->value(KnownField::Title).toString());
55 CPPUNIT_ASSERT_EQUAL("Saint-Saƫns"s, tags.front()->value(KnownField::Artist).toString());
56 CPPUNIT_ASSERT_EQUAL("Classical"s, tags.front()->value(KnownField::Genre).toString());
57 CPPUNIT_ASSERT_EQUAL(
58 "qaac 1.32, CoreAudioToolbox 7.9.7.3, AAC-LC Encoder, TVBR q63, Quality 96"s, tags.front()->value(KnownField::Encoder).toString());
59 CPPUNIT_ASSERT_EQUAL(10, tags.front()->value(KnownField::TrackPosition).toPositionInSet().position());
60 break;
62 checkMp4TestMetaData();
63 break;
65 CPPUNIT_ASSERT_EQUAL(0_st, tracks.size());
66 }
67 CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Information);
68}
69
73void OverallTests::checkMp4Testfile2()
74{
75 CPPUNIT_ASSERT_EQUAL(ContainerFormat::Mp4, m_fileInfo.containerFormat());
76 const auto tracks = m_fileInfo.tracks();
77 CPPUNIT_ASSERT_EQUAL(5_st, tracks.size());
78 for (const auto &track : tracks) {
79 switch (track->id()) {
80 case 1:
81 CPPUNIT_ASSERT_EQUAL(MediaType::Video, track->mediaType());
82 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Avc, track->format().general);
83 CPPUNIT_ASSERT_EQUAL(static_cast<unsigned char>(SubFormats::AvcHighProfile), track->format().sub);
84 CPPUNIT_ASSERT_EQUAL(4.0, track->version());
85 CPPUNIT_ASSERT_EQUAL(2013, track->creationTime().year());
86 CPPUNIT_ASSERT(track->pixelSize() == Size(1920, 750));
87 break;
88 case 2:
89 CPPUNIT_ASSERT_EQUAL(MediaType::Audio, track->mediaType());
90 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Aac, track->format().general);
91 CPPUNIT_ASSERT_EQUAL(static_cast<unsigned char>(SubFormats::AacMpeg4LowComplexityProfile), track->format().sub);
92 CPPUNIT_ASSERT(!(track->format().extension & ExtensionFormats::SpectralBandReplication));
93 CPPUNIT_ASSERT(!(track->format().extension & ExtensionFormats::ParametricStereo));
94 CPPUNIT_ASSERT_EQUAL(Locale("eng"sv, LocaleFormat::ISO_639_2_T), track->locale());
95 CPPUNIT_ASSERT_EQUAL(2013, track->creationTime().year());
96 CPPUNIT_ASSERT_EQUAL(48000u, track->samplingFrequency());
97 CPPUNIT_ASSERT_EQUAL(static_cast<unsigned char>(Mpeg4ChannelConfigs::FrontLeftFrontRight), track->channelConfig());
98 break;
99 case 3:
100 CPPUNIT_ASSERT_EQUAL(MediaType::Audio, track->mediaType());
101 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Ac3, track->format().general);
102 CPPUNIT_ASSERT_EQUAL(Locale("eng"sv, LocaleFormat::ISO_639_2_T), track->locale());
103 CPPUNIT_ASSERT_EQUAL(2013, track->creationTime().year());
104 break;
105 case 4:
106 CPPUNIT_ASSERT_EQUAL(MediaType::Audio, track->mediaType());
107 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::DtsHd, track->format().general);
108 CPPUNIT_ASSERT_EQUAL(Locale("eng"sv, LocaleFormat::ISO_639_2_T), track->locale());
109 CPPUNIT_ASSERT_EQUAL(2013, track->creationTime().year());
110 break;
111 case 6:
112 CPPUNIT_ASSERT_EQUAL(MediaType::Text, track->mediaType());
113 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::TimedText, track->format().general);
114 CPPUNIT_ASSERT_EQUAL(2013, track->creationTime().year());
115 break;
116 default:
117 CPPUNIT_FAIL("unknown track ID");
118 }
119 }
120 const auto tags = m_fileInfo.tags();
121 switch (m_tagStatus) {
123 CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
124 break;
126 checkMp4TestMetaData();
127 break;
129 CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
130 }
131 CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Information);
132}
133
137void OverallTests::checkMp4Testfile3()
138{
139 CPPUNIT_ASSERT_EQUAL(ContainerFormat::Mp4, m_fileInfo.containerFormat());
140 CPPUNIT_ASSERT(m_fileInfo.container() != nullptr);
141 CPPUNIT_ASSERT_EQUAL("dash"s, m_fileInfo.container()->documentType());
142 const auto tracks = m_fileInfo.tracks();
143 CPPUNIT_ASSERT_EQUAL(1_st, tracks.size());
144 for (const auto &track : tracks) {
145 switch (track->id()) {
146 case 1:
147 CPPUNIT_ASSERT_EQUAL(MediaType::Video, track->mediaType());
148 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Avc, track->format().general);
149 CPPUNIT_ASSERT_EQUAL(static_cast<unsigned char>(SubFormats::AvcMainProfile), track->format().sub);
150 CPPUNIT_ASSERT_EQUAL(3.1, track->version());
151 CPPUNIT_ASSERT_EQUAL(2014, track->creationTime().year());
152 CPPUNIT_ASSERT_EQUAL(Size(854, 480), track->pixelSize());
153 CPPUNIT_ASSERT_EQUAL("YUV 4:2:0"s, string(track->chromaFormat()));
154 break;
155 default:
156 CPPUNIT_FAIL("unknown track ID");
157 }
158 }
159 const auto tags = m_fileInfo.tags();
160 switch (m_tagStatus) {
162 CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
163 break;
165 checkMp4TestMetaData();
166 break;
168 CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
169 }
170
171 for (const auto &msg : m_diag) {
172 if (msg.level() != DiagLevel::Warning) {
173 continue;
174 }
175 if (m_mode & Mp4TestFlags::TagsBeforeData) {
176 CPPUNIT_FAIL("No warnings expected when putting tags before data.");
177 } else {
178 CPPUNIT_ASSERT_EQUAL("Sorry, but putting index/tags at the end is not possible when dealing with DASH files."s, msg.message());
179 }
180 }
181 CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Warning);
182}
183
187void OverallTests::checkMp4Testfile4()
188{
189 CPPUNIT_ASSERT_EQUAL(ContainerFormat::Mp4, m_fileInfo.containerFormat());
190 CPPUNIT_ASSERT(m_fileInfo.container() != nullptr);
191 CPPUNIT_ASSERT_EQUAL("M4A "s, m_fileInfo.container()->documentType());
192 const auto tracks = m_fileInfo.tracks();
193 CPPUNIT_ASSERT_EQUAL(1_st, tracks.size());
194 for (const auto &track : tracks) {
195 switch (track->id()) {
196 case 1:
197 CPPUNIT_ASSERT_EQUAL(MediaType::Audio, track->mediaType());
198 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Alac, track->format().general);
199 CPPUNIT_ASSERT_EQUAL(2008, track->creationTime().year());
200 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint16_t>(2), track->channelCount());
201 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint16_t>(16), track->bitsPerSample());
202 break;
203 default:
204 CPPUNIT_FAIL("unknown track ID");
205 }
206 }
207 const auto tags = m_fileInfo.tags();
208 switch (m_tagStatus) {
210 CPPUNIT_ASSERT_EQUAL(1_st, tags.size());
211 CPPUNIT_ASSERT_EQUAL("Sad Song"s, tags.front()->value(KnownField::Title).toString());
212 CPPUNIT_ASSERT_EQUAL("Oasis"s, tags.front()->value(KnownField::Artist).toString());
213 CPPUNIT_ASSERT_EQUAL("Don't Go Away (Apple Lossless)"s, tags.front()->value(KnownField::Album).toString());
214 CPPUNIT_ASSERT_EQUAL("Alternative & Punk"s, tags.front()->value(KnownField::Genre).toString());
215 CPPUNIT_ASSERT_EQUAL("iTunes v7.5.0.20"s, tags.front()->value(KnownField::Encoder).toString());
216 CPPUNIT_ASSERT_EQUAL("1998"s, tags.front()->value(KnownField::RecordDate).toString());
217 CPPUNIT_ASSERT(tags.front()->value(KnownField::Comment).isEmpty());
218 CPPUNIT_ASSERT_EQUAL(0x58f3_st, tags.front()->value(KnownField::Cover).dataSize());
219 CPPUNIT_ASSERT_EQUAL(0xFFD8FFE000104A46ul, BE::toInt<std::uint64_t>(tags.front()->value(KnownField::Cover).dataPointer()));
220 CPPUNIT_ASSERT_EQUAL(PositionInSet(3, 4), tags.front()->value(KnownField::TrackPosition).toPositionInSet());
221 CPPUNIT_ASSERT_EQUAL(PositionInSet(1, 1), tags.front()->value(KnownField::DiskPosition).toPositionInSet());
222 break;
224 checkMp4TestMetaData();
225 break;
227 CPPUNIT_ASSERT_EQUAL(0_st, tracks.size());
228 }
229 CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Information);
230}
231
235void OverallTests::checkMp4Testfile5()
236{
237 CPPUNIT_ASSERT_EQUAL(ContainerFormat::Mp4, m_fileInfo.containerFormat());
238 CPPUNIT_ASSERT(m_fileInfo.container() != nullptr);
239 CPPUNIT_ASSERT_EQUAL("mp42"s, m_fileInfo.container()->documentType());
240 const auto tracks = m_fileInfo.tracks();
241 CPPUNIT_ASSERT_EQUAL(1_st, tracks.size());
242 for (const auto &track : tracks) {
243 switch (track->id()) {
244 case 1:
245 CPPUNIT_ASSERT_EQUAL(MediaType::Audio, track->mediaType());
246 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Aac, track->format().general);
247 CPPUNIT_ASSERT_EQUAL(static_cast<unsigned char>(SubFormats::AacMpeg4LowComplexityProfile), track->format().sub);
248 CPPUNIT_ASSERT(track->format().extension & ExtensionFormats::SpectralBandReplication);
249 CPPUNIT_ASSERT(track->format().extension & ExtensionFormats::ParametricStereo);
250 CPPUNIT_ASSERT_EQUAL(2014, track->creationTime().year());
251 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint16_t>(2), track->channelCount());
252 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint8_t>(Mpeg4ChannelConfigs::FrontCenter), track->channelConfig());
253 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint8_t>(Mpeg4ChannelConfigs::FrontLeftFrontRight), track->extensionChannelConfig());
254 CPPUNIT_ASSERT_EQUAL(24000u, track->samplingFrequency());
255 CPPUNIT_ASSERT_EQUAL(48000u, track->extensionSamplingFrequency());
256 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint16_t>(16), track->bitsPerSample());
257 break;
258 default:
259 CPPUNIT_FAIL("unknown track ID");
260 }
261 }
262 const auto tags = m_fileInfo.tags();
263 switch (m_tagStatus) {
265 CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
266 break;
268 checkMp4TestMetaData();
269 break;
271 CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
272 }
273 CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Information);
274}
275
279void OverallTests::checkMp4Testfile6()
280{
281 CPPUNIT_ASSERT_EQUAL(ContainerFormat::Mp4, m_fileInfo.containerFormat());
282 const auto tracks = m_fileInfo.tracks();
283 if (m_mode & Mp4TestFlags::RemoveTagOrTrack) {
284 CPPUNIT_ASSERT_EQUAL(4_st, tracks.size());
285 } else {
286 CPPUNIT_ASSERT_EQUAL(6_st, tracks.size());
287 }
288 bool track2Present = false, track5Present = false;
289 for (const auto &track : tracks) {
290 switch (track->id()) {
291 case 1:
292 CPPUNIT_ASSERT_EQUAL(MediaType::Video, track->mediaType());
293 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Avc, track->format().general);
294 CPPUNIT_ASSERT_EQUAL(static_cast<unsigned char>(SubFormats::AvcHighProfile), track->format().sub);
295 CPPUNIT_ASSERT_EQUAL(4.0, track->version());
296 CPPUNIT_ASSERT_EQUAL(2013, track->creationTime().year());
297 CPPUNIT_ASSERT_EQUAL(Size(1920, 750), track->pixelSize());
298 break;
299 case 2:
300 CPPUNIT_ASSERT(track2Present = !track2Present);
301 CPPUNIT_ASSERT_EQUAL(MediaType::Audio, track->mediaType());
302 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Aac, track->format().general);
303 CPPUNIT_ASSERT_EQUAL(static_cast<unsigned char>(SubFormats::AacMpeg4LowComplexityProfile), track->format().sub);
304 CPPUNIT_ASSERT(!(track->format().extension & ExtensionFormats::SpectralBandReplication));
305 CPPUNIT_ASSERT(!(track->format().extension & ExtensionFormats::ParametricStereo));
306 CPPUNIT_ASSERT_EQUAL(Locale("ger"sv, LocaleFormat::ISO_639_2_T), track->locale());
307 CPPUNIT_ASSERT_EQUAL("test"s, track->name());
308 CPPUNIT_ASSERT_EQUAL(2013, track->creationTime().year());
309 CPPUNIT_ASSERT_EQUAL(48000u, track->samplingFrequency());
310 CPPUNIT_ASSERT_EQUAL(static_cast<unsigned char>(Mpeg4ChannelConfigs::FrontLeftFrontRight), track->channelConfig());
311 break;
312 case 3:
313 CPPUNIT_ASSERT_EQUAL(MediaType::Audio, track->mediaType());
314 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Ac3, track->format().general);
315 CPPUNIT_ASSERT_EQUAL(Locale("eng"sv, LocaleFormat::ISO_639_2_T), track->locale());
316 CPPUNIT_ASSERT_EQUAL(2013, track->creationTime().year());
317 break;
318 case 4:
319 CPPUNIT_ASSERT_EQUAL(MediaType::Audio, track->mediaType());
320 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::DtsHd, track->format().general);
321 CPPUNIT_ASSERT_EQUAL(Locale("eng"sv, LocaleFormat::ISO_639_2_T), track->locale());
322 CPPUNIT_ASSERT_EQUAL(2013, track->creationTime().year());
323 break;
324 case 5:
325 CPPUNIT_ASSERT(track5Present = !track5Present);
326 CPPUNIT_ASSERT_EQUAL(MediaType::Audio, track->mediaType());
327 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Aac, track->format().general);
328 CPPUNIT_ASSERT_EQUAL(2012, track->creationTime().year());
329 CPPUNIT_ASSERT_EQUAL(44100u, track->samplingFrequency());
330 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint8_t>(Mpeg4ChannelConfigs::FrontLeftFrontRight), track->channelConfig());
331 CPPUNIT_ASSERT_EQUAL("new track"s, track->name());
332 break;
333 case 6:
334 CPPUNIT_ASSERT_EQUAL(MediaType::Text, track->mediaType());
335 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::TimedText, track->format().general);
336 CPPUNIT_ASSERT_EQUAL(2013, track->creationTime().year());
337 break;
338 default:
339 CPPUNIT_FAIL("unknown track ID");
340 }
341 }
342 if (m_mode & Mp4TestFlags::RemoveTagOrTrack) {
343 CPPUNIT_ASSERT(!track2Present);
344 CPPUNIT_ASSERT(!track5Present);
345 } else {
346 CPPUNIT_ASSERT(track2Present);
347 CPPUNIT_ASSERT(track5Present);
348 }
349
350 CPPUNIT_ASSERT_EQUAL(0_st, m_fileInfo.tags().size());
351 CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Information);
352}
353
357void OverallTests::checkMp4Testfile7()
358{
359 CPPUNIT_ASSERT_EQUAL(ContainerFormat::Mp4, m_fileInfo.containerFormat());
360 CPPUNIT_ASSERT(m_fileInfo.container() != nullptr);
361 CPPUNIT_ASSERT_EQUAL("nvr1"s, m_fileInfo.container()->documentType());
362 const auto tracks = m_fileInfo.tracks();
363 CPPUNIT_ASSERT_EQUAL(3_st, tracks.size());
364 for (const auto &track : tracks) {
365 switch (track->id()) {
366 case 1:
367 CPPUNIT_ASSERT_EQUAL("VideoHandle"s, track->name());
368 CPPUNIT_ASSERT_EQUAL(MediaType::Video, track->mediaType());
369 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Avc, track->format().general);
370 CPPUNIT_ASSERT_EQUAL(static_cast<unsigned char>(SubFormats::AvcBaselineProfile), track->format().sub);
371 CPPUNIT_ASSERT_EQUAL(static_cast<unsigned char>(0), track->format().extension);
372 CPPUNIT_ASSERT_EQUAL(4.0, track->version());
373 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint16_t>(0), track->channelCount());
374 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint8_t>(0), track->channelConfig());
375 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint8_t>(0), track->extensionChannelConfig());
376 CPPUNIT_ASSERT_EQUAL(0u, track->samplingFrequency());
377 CPPUNIT_ASSERT_EQUAL(0u, track->extensionSamplingFrequency());
378 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint16_t>(24), track->depth());
379 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint64_t>(51), track->sampleCount());
380 CPPUNIT_ASSERT_EQUAL(1920u, track->pixelSize().width());
381 CPPUNIT_ASSERT_EQUAL(1080u, track->pixelSize().height());
382 CPPUNIT_ASSERT_EQUAL(72u, track->resolution().width());
383 CPPUNIT_ASSERT_EQUAL(72u, track->resolution().height());
384 CPPUNIT_ASSERT_EQUAL(DateTime::fromDateAndTime(2018, 7, 8, 20, 3, 52), track->creationTime());
385 CPPUNIT_ASSERT_EQUAL(DateTime::fromDateAndTime(2018, 7, 8, 20, 3, 52), track->modificationTime());
386 CPPUNIT_ASSERT_EQUAL("YUV 4:2:0"s, string(track->chromaFormat()));
387 CPPUNIT_ASSERT_EQUAL(1, track->duration().seconds());
388 break;
389 case 2:
390 CPPUNIT_ASSERT_EQUAL("SoundHandle"s, track->name());
391 CPPUNIT_ASSERT_EQUAL(MediaType::Audio, track->mediaType());
392 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Aac, track->format().general);
393 CPPUNIT_ASSERT_EQUAL(static_cast<unsigned char>(SubFormats::AacMpeg4LowComplexityProfile), track->format().sub);
394 CPPUNIT_ASSERT_EQUAL(static_cast<unsigned char>(0), track->format().extension);
395 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint16_t>(2), track->channelCount());
396 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint8_t>(Mpeg4ChannelConfigs::FrontLeftFrontRight), track->channelConfig());
397 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint8_t>(0), track->extensionChannelConfig());
398 CPPUNIT_ASSERT_EQUAL(48000u, track->samplingFrequency());
399 CPPUNIT_ASSERT_EQUAL(0u, track->extensionSamplingFrequency());
400 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint16_t>(16), track->bitsPerSample());
401 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint64_t>(76), track->sampleCount());
402 CPPUNIT_ASSERT_EQUAL(DateTime::fromDateAndTime(2018, 7, 8, 20, 3, 52), track->creationTime());
403 CPPUNIT_ASSERT_EQUAL(DateTime::fromDateAndTime(2018, 7, 8, 20, 3, 52), track->modificationTime());
404 CPPUNIT_ASSERT_EQUAL(1, track->duration().seconds());
405 CPPUNIT_ASSERT_EQUAL(256.0, track->bitrate());
406 break;
407 case 3:
408 CPPUNIT_ASSERT_EQUAL("MetaHandler"s, track->name());
409 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Unknown, track->format().general);
410 CPPUNIT_ASSERT_EQUAL("urim"s, track->formatId());
411 break;
412 default:
413 CPPUNIT_FAIL("unknown track ID");
414 }
415 }
416 const auto tags = m_fileInfo.tags();
417 switch (m_tagStatus) {
419 CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
420 break;
422 checkMp4TestMetaData();
423 break;
425 CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
426 }
427 CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Information);
428}
429
433void OverallTests::checkMp4TestMetaData()
434{
435 // check whether a tag is assigned
436 const auto tags = m_fileInfo.tags();
437 Mp4Tag *tag = m_fileInfo.mp4Tag();
438 CPPUNIT_ASSERT_EQUAL(1_st, tags.size());
439 CPPUNIT_ASSERT(tag != nullptr);
440
441 // check test meta data
442 CPPUNIT_ASSERT_EQUAL(m_testTitle, tag->value(KnownField::Title));
443 CPPUNIT_ASSERT_EQUAL(m_testComment.toString(), tag->value(KnownField::Comment).toString()); // loss of description is ok
444 CPPUNIT_ASSERT_EQUAL(m_testAlbum, tag->value(KnownField::Album));
445 CPPUNIT_ASSERT_EQUAL(m_preservedMetaData.front(), tag->value(KnownField::Artist));
446 CPPUNIT_ASSERT_EQUAL(m_testPosition, tag->value(KnownField::TrackPosition));
447 CPPUNIT_ASSERT_EQUAL(m_testPosition, tag->value(KnownField::DiskPosition));
448
449 // TODO: check more fields
450 m_preservedMetaData.pop();
451}
452
456void OverallTests::checkMp4Constraints()
457{
458 using namespace Mp4TestFlags;
459
460 CPPUNIT_ASSERT(m_fileInfo.container());
461 if (m_mode & PaddingConstraints) {
462 if (m_mode & ForceRewring) {
463 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint64_t>(4096), m_fileInfo.paddingSize());
464 } else {
465 CPPUNIT_ASSERT(m_fileInfo.paddingSize() >= 1024);
466 CPPUNIT_ASSERT(m_fileInfo.paddingSize() <= (4096 + 1024));
467 }
468 if (!(m_mode & RemoveTagOrTrack) && (m_fileInfo.container()->documentType() != "dash")
469 && ((m_mode & ForceRewring) || (m_mode & ForceTagPos))) {
470 const ElementPosition currentTagPos = m_fileInfo.container()->determineTagPosition(m_diag);
471 if (currentTagPos == ElementPosition::Keep) {
472 CPPUNIT_ASSERT_EQUAL(m_expectedTagPos, m_fileInfo.container()->determineIndexPosition(m_diag));
473 }
474 }
475 }
476}
477
481void OverallTests::setMp4TestMetaData()
482{
483 // ensure a tag exists
484 Tag *tag = m_fileInfo.container()->createTag();
485
486 // assign test meta data
487 tag->setValue(KnownField::Title, m_testTitle);
488 tag->setValue(KnownField::Comment, m_testComment);
489 tag->setValue(KnownField::Album, m_testAlbum);
490 m_preservedMetaData.push(tag->value(KnownField::Artist));
491 tag->setValue(KnownField::TrackPosition, m_testPosition);
492 tag->setValue(KnownField::DiskPosition, m_testPosition);
493 // TODO: set more fields
494}
495
503void OverallTests::alterMp4Tracks()
504{
505 m_additionalFileInfo.setPath(testFilePath("mtx-test-data/mp4/10-DanseMacabreOp.40.m4a"));
506 m_additionalFileInfo.reopen(true);
507 m_additionalFileInfo.parseContainerFormat(m_diag, m_progress);
508 m_additionalFileInfo.parseTracks(m_diag, m_progress);
509 CPPUNIT_ASSERT_EQUAL(ContainerFormat::Mp4, m_additionalFileInfo.containerFormat());
510 CPPUNIT_ASSERT_EQUAL(ContainerFormat::Mp4, m_fileInfo.containerFormat());
511 const auto &tracks = m_additionalFileInfo.tracks();
512 CPPUNIT_ASSERT_EQUAL(1_st, tracks.size());
513 CPPUNIT_ASSERT_EQUAL(TrackType::Mp4Track, tracks[0]->type());
514 auto *track = static_cast<Mp4Track *>(tracks[0]);
515 CPPUNIT_ASSERT(static_cast<Mp4Container *>(m_additionalFileInfo.container())->removeTrack(track));
516 CPPUNIT_ASSERT_EQUAL(0_st, m_additionalFileInfo.trackCount());
517 track->setName("new track");
518 auto *container = static_cast<Mp4Container *>(m_fileInfo.container());
519 CPPUNIT_ASSERT_EQUAL(5_st, container->trackCount());
520 container->addTrack(track);
521 CPPUNIT_ASSERT_EQUAL(6_st, container->trackCount());
522 auto &secondTrack = container->tracks()[1];
523 secondTrack->setLocale(Locale("ger"sv, LocaleFormat::ISO_639_2_T));
524 secondTrack->setName("test");
525}
526
531{
532 cerr << endl << "MP4 parser" << endl;
533 m_fileInfo.setForceFullParse(false);
534 m_tagStatus = TagStatus::Original;
535 parseFile(testFilePath("mtx-test-data/mp4/10-DanseMacabreOp.40.m4a"), &OverallTests::checkMp4Testfile1);
536 parseFile(testFilePath("mtx-test-data/mp4/1080p-DTS-HD-7.1.mp4"), &OverallTests::checkMp4Testfile2);
537 parseFile(testFilePath("mtx-test-data/mp4/dash/dragon-age-inquisition-H1LkM6IVlm4-video.mp4"), &OverallTests::checkMp4Testfile3);
538 parseFile(testFilePath("mtx-test-data/alac/othertest-itunes.m4a"), &OverallTests::checkMp4Testfile4);
539 parseFile(testFilePath("mtx-test-data/aac/he-aacv2-ps.m4a"), &OverallTests::checkMp4Testfile5);
540 parseFile(testFilePath("mp4/android-8.1-camera-recoding.mp4"), &OverallTests::checkMp4Testfile7);
541}
542
548{
549 // full parse is required to determine padding
550 m_fileInfo.setForceFullParse(true);
551
552 // do the test under different conditions
553 for (m_mode = 0; m_mode != 0x20; ++m_mode) {
554 using namespace Mp4TestFlags;
555
556 // setup test conditions
557
558 m_fileInfo.setForceRewrite(m_mode & ForceRewring);
559 if (m_mode & KeepTagPos) {
560 m_fileInfo.setTagPosition(ElementPosition::Keep);
561 } else {
562 m_fileInfo.setTagPosition(m_mode & TagsBeforeData ? ElementPosition::BeforeData : ElementPosition::AfterData);
563 }
564 m_fileInfo.setIndexPosition(m_fileInfo.tagPosition());
565 m_fileInfo.setPreferredPadding(m_mode & PaddingConstraints ? 4096 : 0);
566 m_fileInfo.setMinPadding(m_mode & PaddingConstraints ? 1024 : 0);
567 m_fileInfo.setMaxPadding(m_mode & PaddingConstraints ? (4096 + 1024) : numeric_limits<size_t>::max());
568 m_fileInfo.setForceTagPosition(m_mode & ForceTagPos);
569 m_fileInfo.setForceIndexPosition(m_mode & ForceTagPos);
570
571 // print test conditions
572 list<string> testConditions;
573 if (m_mode & ForceRewring) {
574 testConditions.emplace_back("forcing rewrite");
575 }
576 if (m_mode & KeepTagPos) {
577 if (m_mode & RemoveTagOrTrack) {
578 testConditions.emplace_back("removing tag");
579 } else {
580 testConditions.emplace_back("keeping tag position");
581 }
582 } else if (m_mode & TagsBeforeData) {
583 testConditions.emplace_back("tags before data");
584 } else {
585 testConditions.emplace_back("tags after data");
586 }
587 if (m_mode & PaddingConstraints) {
588 testConditions.emplace_back("padding constraints");
589 }
590 if (m_mode & ForceTagPos) {
591 testConditions.emplace_back("forcing tag position");
592 }
593 cerr << endl << "MP4 maker - testmode " << m_mode << ": " << joinStrings(testConditions, ", ") << endl;
594
595 // do actual tests
596 // -> either remove tags or set test meta data
597 m_tagStatus = (m_mode & RemoveTagOrTrack) ? TagStatus::Removed : TagStatus::TestMetaDataPresent;
598 void (OverallTests::*modifyRoutine)(void) = (m_mode & RemoveTagOrTrack) ? &OverallTests::removeAllTags : &OverallTests::setMp4TestMetaData;
599 makeFile(workingCopyPath("mtx-test-data/mp4/10-DanseMacabreOp.40.m4a"), modifyRoutine, &OverallTests::checkMp4Testfile1);
600 makeFile(workingCopyPath("mtx-test-data/mp4/1080p-DTS-HD-7.1.mp4"), modifyRoutine, &OverallTests::checkMp4Testfile2);
601 makeFile(
602 workingCopyPath("mtx-test-data/mp4/dash/dragon-age-inquisition-H1LkM6IVlm4-video.mp4"), modifyRoutine, &OverallTests::checkMp4Testfile3);
603 makeFile(workingCopyPath("mtx-test-data/alac/othertest-itunes.m4a"), modifyRoutine, &OverallTests::checkMp4Testfile4);
604 makeFile(workingCopyPath("mtx-test-data/aac/he-aacv2-ps.m4a"), modifyRoutine, &OverallTests::checkMp4Testfile5);
605 makeFile(workingCopyPath("mp4/android-8.1-camera-recoding.mp4"), modifyRoutine, &OverallTests::checkMp4Testfile7);
606 // -> add/remove tracks
607 modifyRoutine = (m_mode & RemoveTagOrTrack) ? &OverallTests::removeSecondTrack : &OverallTests::alterMp4Tracks;
608 m_fileInfo.setTagPosition(ElementPosition::Keep);
609 makeFile(workingCopyPath("mtx-test-data/mp4/1080p-DTS-HD-7.1.mp4"), modifyRoutine, &OverallTests::checkMp4Testfile6);
610 }
611}
612
618{
619 static constexpr auto hugePadding = static_cast<std::size_t>(std::numeric_limits<std::uint32_t>::max()) + 1;
620 static constexpr auto expectedChunkCount = std::uint32_t{ 435 };
621
622 std::cerr << "\nMaking MP4 file with 64-bit offsets\n";
623 if (std::numeric_limits<std::size_t>::max() <= std::numeric_limits<std::uint32_t>::max()) {
624 std::cerr << " - skipping test with 64-bit offsets, std::size_t is not big enough\n";
625 return;
626 }
627 if (!CppUtilities::isEnvVariableSet(PROJECT_VARNAME_UPPER "_TEST_WITH_BIG_FILES").value_or(false)) {
628 std::cerr << " - skipping test with 64-bit offsets, set " PROJECT_VARNAME_UPPER "_TEST_WITH_BIG_FILES=1 to enable\n";
629 return;
630 }
631
632 m_diag.clear();
633 m_fileInfo.setForceFullParse(true);
634 m_fileInfo.setPath(workingCopyPath("mtx-test-data/mp4/10-DanseMacabreOp.40.m4a"));
635 std::cerr << " - testing with file " << m_fileInfo.path() << "\n";
636
637 m_fileInfo.open();
638 m_fileInfo.parseEverything(m_diag, m_progress);
639
640 auto *container = dynamic_cast<Mp4Container *>(m_fileInfo.container());
641 CPPUNIT_ASSERT(container);
642 auto *firstElement = container->firstElement();
643 CPPUNIT_ASSERT(firstElement);
644 auto *sampleTable = firstElement->subelementByPath(
646 CPPUNIT_ASSERT(sampleTable);
647 auto *chunkOffsetTable = sampleTable->childById(Mp4AtomIds::ChunkOffset, m_diag);
648 auto *chunkOffsetTable64 = sampleTable->childById(Mp4AtomIds::ChunkOffset64, m_diag);
649 CPPUNIT_ASSERT_MESSAGE("file has initially a 32-bit chunk offset table", chunkOffsetTable);
650 CPPUNIT_ASSERT_MESSAGE("file has initially no 64-bit chunk offset table", !chunkOffsetTable64);
651 CPPUNIT_ASSERT_EQUAL_MESSAGE("initially no padding present", 3724_st, m_fileInfo.paddingSize());
652 auto *track = container->track(0);
653 CPPUNIT_ASSERT(track);
654 CPPUNIT_ASSERT_EQUAL(expectedChunkCount, track->chunkCount());
655 CPPUNIT_ASSERT_EQUAL(4u, track->chunkOffsetSize());
656
657 m_fileInfo.setForceTagPosition(true);
658 m_fileInfo.setTagPosition(ElementPosition::BeforeData);
659 m_fileInfo.setMinPadding(hugePadding);
660 m_fileInfo.setPreferredPadding(hugePadding);
661 m_fileInfo.applyChanges(m_diag, m_progress);
662 m_fileInfo.clearParsingResults();
663 m_fileInfo.parseEverything(m_diag, m_progress);
664
665 container = dynamic_cast<Mp4Container *>(m_fileInfo.container());
666 CPPUNIT_ASSERT(container);
667 firstElement = container->firstElement();
668 CPPUNIT_ASSERT(firstElement);
669 sampleTable = firstElement->subelementByPath(
671 CPPUNIT_ASSERT(sampleTable);
672 chunkOffsetTable = sampleTable->childById(Mp4AtomIds::ChunkOffset, m_diag);
673 chunkOffsetTable64 = sampleTable->childById(Mp4AtomIds::ChunkOffset64, m_diag);
674 CPPUNIT_ASSERT_MESSAGE("the 32-bit chunk offset table is no longer present", !chunkOffsetTable);
675 CPPUNIT_ASSERT_MESSAGE("the chunk offset table has been converted to 64-bit", chunkOffsetTable64);
676 CPPUNIT_ASSERT_EQUAL_MESSAGE("padding present", hugePadding, m_fileInfo.paddingSize());
677 track = container->track(0);
678 CPPUNIT_ASSERT(track);
679 CPPUNIT_ASSERT_EQUAL(expectedChunkCount, track->chunkCount());
680 CPPUNIT_ASSERT_EQUAL(8u, track->chunkOffsetSize());
681 CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Information);
682}
The OverallTests class tests reading and writing tags and parsing technical information for all suppo...
Definition overall.h:40
void testMp4MakingWith64BitOffsets()
Tests the MP4 maker via MediaFileInfo with a big padding to require 64-bit chunk offsets.
void testMp4Making()
Tests the MP4 maker via MediaFileInfo.
void testMp4Parsing()
Tests the MP4 parser via MediaFileInfo.
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.
const std::string & documentType() const
Returns a string that describes the document type if available; otherwise returns an empty string.
virtual Tag * createTag(const TagTarget &target=TagTarget())
Creates and returns a tag for the specified target.
const std::string & path() const
Returns the path of the current file.
void reopen(bool readOnly=false)
Opens a std::fstream for the current file.
void open(bool readOnly=false)
Opens a std::fstream for the current file.
void setPath(std::string_view path)
Sets the current file.
DiagLevel level() const
Returns the worst diag level present in the container.
bool removeTrack(AbstractTrack *track) override
Removes the specified track to the container.
void parseEverything(Diagnostics &diag, AbortableProgressFeedback &progress)
Parses the container format, the tracks and the tag information of the current file.
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.
std::size_t trackCount() const
Returns the number of tracks that could be parsed.
void setMinPadding(std::size_t minPadding)
Sets the minimum padding to be written before the data blocks when applying changes.
void parseTracks(Diagnostics &diag, AbortableProgressFeedback &progress)
Parses the tracks of the current file.
void parseContainerFormat(Diagnostics &diag, AbortableProgressFeedback &progress)
Parses the container format of the current file.
void setForceTagPosition(bool forceTagPosition)
Sets whether tagPosition() is forced.
void clearParsingResults()
Clears all parsing results and assigned/created/changed information such as detected container format...
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.
ElementPosition tagPosition() const
Returns 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.
void applyChanges(Diagnostics &diag, AbortableProgressFeedback &progress)
Applies assigned/changed tag information to 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.
Mp4Tag * mp4Tag() const
Returns a pointer to the assigned MP4 tag or nullptr if none is assigned.
Implementation of GenericContainer<MediaFileInfo, Mp4Tag, Mp4Track, Mp4Atom>.
Implementation of TagParser::Tag for the MP4 container.
const TagValue & value(KnownField value) const override
Returns the value of the specified field.
Definition mp4tag.cpp:59
Implementation of TagParser::AbstractTrack for the MP4 container.
The Size class defines the size of a two-dimensional object using integer point precision.
Definition size.h:17
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.
virtual const TagValue & value(KnownField field) const =0
Returns the value of the specified field.
ElementPosition
Definition settings.h:13
@ TestMetaDataPresent
The Locale struct specifies a language and/or a country using one or more LocaleDetail objects.