Tag Parser 12.5.1
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
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}
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.
Implementation of GenericContainer<MediaFileInfo, Mp4Tag, Mp4Track, Mp4Atom>.
const TagValue & value(KnownField value) const override
Returns the value of the specified field.
Definition mp4tag.cpp:59
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
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
@ Original
Definition overall.h:28
@ Removed
Definition overall.h:28
@ TestMetaDataPresent
Definition overall.h:28