4 #include "../abstracttrack.h" 5 #include "../mp4/mp4container.h" 6 #include "../mp4/mp4ids.h" 7 #include "../mp4/mp4tag.h" 9 using namespace ChronoUtilities;
25 void OverallTests::checkMp4Testfile1()
28 const auto tracks = m_fileInfo.tracks();
29 CPPUNIT_ASSERT_EQUAL(1_st, tracks.size());
30 for (
const auto &track : tracks) {
31 switch (track->id()) {
35 CPPUNIT_ASSERT_EQUAL(2012, track->creationTime().year());
36 CPPUNIT_ASSERT_EQUAL(44100u, track->samplingFrequency());
40 CPPUNIT_FAIL(
"unknown track ID");
43 const auto tags = m_fileInfo.tags();
44 switch (m_tagStatus) {
46 CPPUNIT_ASSERT_EQUAL(1_st, tags.size());
47 CPPUNIT_ASSERT_EQUAL(
"Danse Macabre, Op.40"s, tags.front()->value(
KnownField::Title).toString());
48 CPPUNIT_ASSERT_EQUAL(
"Saint-Saƫns"s, tags.front()->value(
KnownField::Artist).toString());
49 CPPUNIT_ASSERT_EQUAL(
"Classical"s, tags.front()->value(
KnownField::Genre).toString());
51 "qaac 1.32, CoreAudioToolbox 7.9.7.3, AAC-LC Encoder, TVBR q63, Quality 96"s, tags.front()->value(
KnownField::Encoder).toString());
55 checkMp4TestMetaData();
58 CPPUNIT_ASSERT_EQUAL(0_st, tracks.size());
60 CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Information);
66 void OverallTests::checkMp4Testfile2()
69 const auto tracks = m_fileInfo.tracks();
70 CPPUNIT_ASSERT_EQUAL(5_st, tracks.size());
71 for (
const auto &track : tracks) {
72 switch (track->id()) {
77 CPPUNIT_ASSERT_EQUAL(4.0, track->version());
78 CPPUNIT_ASSERT_EQUAL(2013, track->creationTime().year());
79 CPPUNIT_ASSERT(track->pixelSize() ==
Size(1920, 750));
87 CPPUNIT_ASSERT_EQUAL(
"eng"s, track->language());
88 CPPUNIT_ASSERT_EQUAL(2013, track->creationTime().year());
89 CPPUNIT_ASSERT_EQUAL(48000u, track->samplingFrequency());
95 CPPUNIT_ASSERT_EQUAL(
"eng"s, track->language());
96 CPPUNIT_ASSERT_EQUAL(2013, track->creationTime().year());
100 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::DtsHd, track->format().general);
101 CPPUNIT_ASSERT_EQUAL(
"eng"s, track->language());
102 CPPUNIT_ASSERT_EQUAL(2013, track->creationTime().year());
105 CPPUNIT_ASSERT_EQUAL(MediaType::Text, track->mediaType());
106 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::TimedText, track->format().general);
107 CPPUNIT_ASSERT_EQUAL(2013, track->creationTime().year());
110 CPPUNIT_FAIL(
"unknown track ID");
113 const auto tags = m_fileInfo.tags();
114 switch (m_tagStatus) {
116 CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
119 checkMp4TestMetaData();
122 CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
124 CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Information);
130 void OverallTests::checkMp4Testfile3()
133 CPPUNIT_ASSERT(m_fileInfo.container() !=
nullptr);
134 CPPUNIT_ASSERT_EQUAL(
"dash"s, m_fileInfo.container()->documentType());
135 const auto tracks = m_fileInfo.tracks();
136 CPPUNIT_ASSERT_EQUAL(1_st, tracks.size());
137 for (
const auto &track : tracks) {
138 switch (track->id()) {
143 CPPUNIT_ASSERT_EQUAL(3.1, track->version());
144 CPPUNIT_ASSERT_EQUAL(2014, track->creationTime().year());
145 CPPUNIT_ASSERT_EQUAL(
Size(854, 480), track->pixelSize());
146 CPPUNIT_ASSERT_EQUAL(
"YUV 4:2:0"s,
string(track->chromaFormat()));
149 CPPUNIT_FAIL(
"unknown track ID");
152 const auto tags = m_fileInfo.tags();
153 switch (m_tagStatus) {
155 CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
158 checkMp4TestMetaData();
161 CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
164 for (
const auto &msg : m_diag) {
165 if (msg.level() != DiagLevel::Warning) {
169 CPPUNIT_FAIL(
"No warnings expected when putting tags before data.");
171 CPPUNIT_ASSERT_EQUAL(
"Sorry, but putting index/tags at the end is not possible when dealing with DASH files."s, msg.message());
174 CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Warning);
180 void OverallTests::checkMp4Testfile4()
183 CPPUNIT_ASSERT(m_fileInfo.container() !=
nullptr);
184 CPPUNIT_ASSERT_EQUAL(
"M4A "s, m_fileInfo.container()->documentType());
185 const auto tracks = m_fileInfo.tracks();
186 CPPUNIT_ASSERT_EQUAL(1_st, tracks.size());
187 for (
const auto &track : tracks) {
188 switch (track->id()) {
192 CPPUNIT_ASSERT_EQUAL(2008, track->creationTime().year());
193 CPPUNIT_ASSERT_EQUAL(static_cast<uint16>(2), track->channelCount());
194 CPPUNIT_ASSERT_EQUAL(static_cast<uint16>(16), track->bitsPerSample());
197 CPPUNIT_FAIL(
"unknown track ID");
200 const auto tags = m_fileInfo.tags();
201 switch (m_tagStatus) {
203 CPPUNIT_ASSERT_EQUAL(1_st, tags.size());
204 CPPUNIT_ASSERT_EQUAL(
"Sad Song"s, tags.front()->value(
KnownField::Title).toString());
206 CPPUNIT_ASSERT_EQUAL(
"Don't Go Away (Apple Lossless)"s, tags.front()->value(
KnownField::Album).toString());
207 CPPUNIT_ASSERT_EQUAL(
"Alternative & Punk"s, tags.front()->value(
KnownField::Genre).toString());
208 CPPUNIT_ASSERT_EQUAL(
"iTunes v7.5.0.20"s, tags.front()->value(
KnownField::Encoder).toString());
209 CPPUNIT_ASSERT_EQUAL(
"1998"s, tags.front()->value(
KnownField::Year).toString());
211 CPPUNIT_ASSERT_EQUAL(0x58f3_st, tags.front()->value(
KnownField::Cover).dataSize());
212 CPPUNIT_ASSERT_EQUAL(0xFFD8FFE000104A46ul, BE::toUInt64(tags.front()->value(
KnownField::Cover).dataPointer()));
217 checkMp4TestMetaData();
220 CPPUNIT_ASSERT_EQUAL(0_st, tracks.size());
222 CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Information);
228 void OverallTests::checkMp4Testfile5()
231 CPPUNIT_ASSERT(m_fileInfo.container() !=
nullptr);
232 CPPUNIT_ASSERT_EQUAL(
"mp42"s, m_fileInfo.container()->documentType());
233 const auto tracks = m_fileInfo.tracks();
234 CPPUNIT_ASSERT_EQUAL(1_st, tracks.size());
235 for (
const auto &track : tracks) {
236 switch (track->id()) {
243 CPPUNIT_ASSERT_EQUAL(2014, track->creationTime().year());
244 CPPUNIT_ASSERT_EQUAL(static_cast<uint16>(2), track->channelCount());
247 CPPUNIT_ASSERT_EQUAL(24000u, track->samplingFrequency());
248 CPPUNIT_ASSERT_EQUAL(48000u, track->extensionSamplingFrequency());
249 CPPUNIT_ASSERT_EQUAL(static_cast<uint16>(16), track->bitsPerSample());
252 CPPUNIT_FAIL(
"unknown track ID");
255 const auto tags = m_fileInfo.tags();
256 switch (m_tagStatus) {
258 CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
261 checkMp4TestMetaData();
264 CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
266 CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Information);
272 void OverallTests::checkMp4Testfile6()
275 const auto tracks = m_fileInfo.tracks();
277 CPPUNIT_ASSERT_EQUAL(4_st, tracks.size());
279 CPPUNIT_ASSERT_EQUAL(6_st, tracks.size());
281 bool track2Present =
false, track5Present =
false;
282 for (
const auto &track : tracks) {
283 switch (track->id()) {
288 CPPUNIT_ASSERT_EQUAL(4.0, track->version());
289 CPPUNIT_ASSERT_EQUAL(2013, track->creationTime().year());
290 CPPUNIT_ASSERT_EQUAL(
Size(1920, 750), track->pixelSize());
293 CPPUNIT_ASSERT(track2Present = !track2Present);
299 CPPUNIT_ASSERT_EQUAL(
"ger"s, track->language());
300 CPPUNIT_ASSERT_EQUAL(
"test"s, track->name());
301 CPPUNIT_ASSERT_EQUAL(2013, track->creationTime().year());
302 CPPUNIT_ASSERT_EQUAL(48000u, track->samplingFrequency());
308 CPPUNIT_ASSERT_EQUAL(
"eng"s, track->language());
309 CPPUNIT_ASSERT_EQUAL(2013, track->creationTime().year());
313 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::DtsHd, track->format().general);
314 CPPUNIT_ASSERT_EQUAL(
"eng"s, track->language());
315 CPPUNIT_ASSERT_EQUAL(2013, track->creationTime().year());
318 CPPUNIT_ASSERT(track5Present = !track5Present);
321 CPPUNIT_ASSERT_EQUAL(2012, track->creationTime().year());
322 CPPUNIT_ASSERT_EQUAL(44100u, track->samplingFrequency());
324 CPPUNIT_ASSERT_EQUAL(
"new track"s, track->name());
327 CPPUNIT_ASSERT_EQUAL(MediaType::Text, track->mediaType());
328 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::TimedText, track->format().general);
329 CPPUNIT_ASSERT_EQUAL(2013, track->creationTime().year());
332 CPPUNIT_FAIL(
"unknown track ID");
336 CPPUNIT_ASSERT(!track2Present);
337 CPPUNIT_ASSERT(!track5Present);
339 CPPUNIT_ASSERT(track2Present);
340 CPPUNIT_ASSERT(track5Present);
343 CPPUNIT_ASSERT_EQUAL(0_st, m_fileInfo.tags().size());
344 CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Information);
350 void OverallTests::checkMp4Testfile7()
353 CPPUNIT_ASSERT(m_fileInfo.container() !=
nullptr);
354 CPPUNIT_ASSERT_EQUAL(
"nvr1"s, m_fileInfo.container()->documentType());
355 const auto tracks = m_fileInfo.tracks();
356 CPPUNIT_ASSERT_EQUAL(3_st, tracks.size());
357 for (
const auto &track : tracks) {
358 switch (track->id()) {
360 CPPUNIT_ASSERT_EQUAL(
"VideoHandle"s, track->name());
364 CPPUNIT_ASSERT_EQUAL(static_cast<unsigned char>(0), track->format().extension);
365 CPPUNIT_ASSERT_EQUAL(4.0, track->version());
366 CPPUNIT_ASSERT_EQUAL(static_cast<uint16>(0), track->channelCount());
367 CPPUNIT_ASSERT_EQUAL(static_cast<byte>(0), track->channelConfig());
368 CPPUNIT_ASSERT_EQUAL(static_cast<byte>(0), track->extensionChannelConfig());
369 CPPUNIT_ASSERT_EQUAL(0u, track->samplingFrequency());
370 CPPUNIT_ASSERT_EQUAL(0u, track->extensionSamplingFrequency());
371 CPPUNIT_ASSERT_EQUAL(static_cast<uint16>(24), track->depth());
372 CPPUNIT_ASSERT_EQUAL(static_cast<uint64>(51), track->sampleCount());
373 CPPUNIT_ASSERT_EQUAL(1920u, track->pixelSize().width());
374 CPPUNIT_ASSERT_EQUAL(1080u, track->pixelSize().height());
375 CPPUNIT_ASSERT_EQUAL(72u, track->resolution().width());
376 CPPUNIT_ASSERT_EQUAL(72u, track->resolution().height());
377 CPPUNIT_ASSERT_EQUAL(DateTime::fromDateAndTime(2018, 7, 8, 20, 3, 52), track->creationTime());
378 CPPUNIT_ASSERT_EQUAL(DateTime::fromDateAndTime(2018, 7, 8, 20, 3, 52), track->modificationTime());
379 CPPUNIT_ASSERT_EQUAL(
"YUV 4:2:0"s,
string(track->chromaFormat()));
380 CPPUNIT_ASSERT_EQUAL(1, track->duration().seconds());
383 CPPUNIT_ASSERT_EQUAL(
"SoundHandle"s, track->name());
387 CPPUNIT_ASSERT_EQUAL(static_cast<unsigned char>(0), track->format().extension);
388 CPPUNIT_ASSERT_EQUAL(static_cast<uint16>(2), track->channelCount());
390 CPPUNIT_ASSERT_EQUAL(static_cast<byte>(0), track->extensionChannelConfig());
391 CPPUNIT_ASSERT_EQUAL(48000u, track->samplingFrequency());
392 CPPUNIT_ASSERT_EQUAL(0u, track->extensionSamplingFrequency());
393 CPPUNIT_ASSERT_EQUAL(static_cast<uint16>(16), track->bitsPerSample());
394 CPPUNIT_ASSERT_EQUAL(static_cast<uint64>(76), track->sampleCount());
395 CPPUNIT_ASSERT_EQUAL(DateTime::fromDateAndTime(2018, 7, 8, 20, 3, 52), track->creationTime());
396 CPPUNIT_ASSERT_EQUAL(DateTime::fromDateAndTime(2018, 7, 8, 20, 3, 52), track->modificationTime());
397 CPPUNIT_ASSERT_EQUAL(1, track->duration().seconds());
398 CPPUNIT_ASSERT_EQUAL(256.0, track->bitrate());
401 CPPUNIT_ASSERT_EQUAL(
"MetaHandler"s, track->name());
402 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Unknown, track->format().general);
403 CPPUNIT_ASSERT_EQUAL(
"urim"s, track->formatId());
406 CPPUNIT_FAIL(
"unknown track ID");
409 const auto tags = m_fileInfo.tags();
410 switch (m_tagStatus) {
412 CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
415 checkMp4TestMetaData();
418 CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
420 CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Information);
426 void OverallTests::checkMp4TestMetaData()
429 const auto tags = m_fileInfo.tags();
430 Mp4Tag *tag = m_fileInfo.mp4Tag();
431 CPPUNIT_ASSERT_EQUAL(1_st, tags.size());
432 CPPUNIT_ASSERT(tag !=
nullptr);
443 m_preservedMetaData.pop();
449 void OverallTests::checkMp4Constraints()
453 CPPUNIT_ASSERT(m_fileInfo.container());
456 CPPUNIT_ASSERT_EQUAL(static_cast<uint64>(4096), m_fileInfo.paddingSize());
458 CPPUNIT_ASSERT(m_fileInfo.paddingSize() >= 1024);
459 CPPUNIT_ASSERT(m_fileInfo.paddingSize() <= (4096 + 1024));
461 if (!(m_mode &
RemoveTagOrTrack) && (m_fileInfo.container()->documentType() !=
"dash")
463 const ElementPosition currentTagPos = m_fileInfo.container()->determineTagPosition(m_diag);
464 if (currentTagPos == ElementPosition::Keep) {
465 CPPUNIT_ASSERT_EQUAL(m_expectedTagPos, m_fileInfo.container()->determineIndexPosition(m_diag));
474 void OverallTests::setMp4TestMetaData()
477 Tag *tag = m_fileInfo.container()->createTag();
496 void OverallTests::alterMp4Tracks()
498 m_additionalFileInfo.setPath(TestUtilities::testFilePath(
"mtx-test-data/mp4/10-DanseMacabreOp.40.m4a"));
499 m_additionalFileInfo.reopen(
true);
500 m_additionalFileInfo.parseContainerFormat(m_diag);
501 m_additionalFileInfo.parseTracks(m_diag);
504 const auto &tracks = m_additionalFileInfo.tracks();
505 CPPUNIT_ASSERT_EQUAL(1_st, tracks.size());
506 CPPUNIT_ASSERT_EQUAL(TrackType::Mp4Track, tracks[0]->type());
507 auto *track = static_cast<Mp4Track *>(tracks[0]);
508 CPPUNIT_ASSERT(static_cast<Mp4Container *>(m_additionalFileInfo.container())->removeTrack(track));
509 CPPUNIT_ASSERT_EQUAL(0_st, m_additionalFileInfo.trackCount());
510 track->setName(
"new track");
511 auto *container = static_cast<Mp4Container *>(m_fileInfo.container());
512 CPPUNIT_ASSERT_EQUAL(5_st, container->trackCount());
513 container->addTrack(track);
514 CPPUNIT_ASSERT_EQUAL(6_st, container->trackCount());
515 auto &secondTrack = container->tracks()[1];
516 secondTrack->setLanguage(
"ger");
517 secondTrack->setName(
"test");
525 cerr << endl <<
"MP4 parser" << endl;
526 m_fileInfo.setForceFullParse(
false);
528 parseFile(TestUtilities::testFilePath(
"mtx-test-data/mp4/10-DanseMacabreOp.40.m4a"), &OverallTests::checkMp4Testfile1);
529 parseFile(TestUtilities::testFilePath(
"mtx-test-data/mp4/1080p-DTS-HD-7.1.mp4"), &OverallTests::checkMp4Testfile2);
530 parseFile(TestUtilities::testFilePath(
"mtx-test-data/mp4/dash/dragon-age-inquisition-H1LkM6IVlm4-video.mp4"), &OverallTests::checkMp4Testfile3);
531 parseFile(TestUtilities::testFilePath(
"mtx-test-data/alac/othertest-itunes.m4a"), &OverallTests::checkMp4Testfile4);
532 parseFile(TestUtilities::testFilePath(
"mtx-test-data/aac/he-aacv2-ps.m4a"), &OverallTests::checkMp4Testfile5);
533 parseFile(TestUtilities::testFilePath(
"mp4/android-8.1-camera-recoding.mp4"), &OverallTests::checkMp4Testfile7);
543 m_fileInfo.setForceFullParse(
true);
546 for (m_mode = 0; m_mode != 0x20; ++m_mode) {
553 m_fileInfo.setTagPosition(ElementPosition::Keep);
555 m_fileInfo.setTagPosition(m_mode &
TagsBeforeData ? ElementPosition::BeforeData : ElementPosition::AfterData);
557 m_fileInfo.setIndexPosition(m_fileInfo.tagPosition());
560 m_fileInfo.setMaxPadding(m_mode &
PaddingConstraints ? (4096 + 1024) : numeric_limits<size_t>::max());
561 m_fileInfo.setForceTagPosition(m_mode &
ForceTagPos);
562 m_fileInfo.setForceIndexPosition(m_mode &
ForceTagPos);
565 list<string> testConditions;
567 testConditions.emplace_back(
"forcing rewrite");
571 testConditions.emplace_back(
"removing tag");
573 testConditions.emplace_back(
"keeping tag position");
576 testConditions.emplace_back(
"tags before data");
578 testConditions.emplace_back(
"tags after data");
581 testConditions.emplace_back(
"padding constraints");
584 testConditions.emplace_back(
"forcing tag position");
586 cerr << endl <<
"MP4 maker - testmode " << m_mode <<
": " << joinStrings(testConditions,
", ") << endl;
591 void (
OverallTests::*modifyRoutine)(void) = (m_mode &
RemoveTagOrTrack) ? &OverallTests::removeAllTags : &OverallTests::setMp4TestMetaData;
592 makeFile(TestUtilities::workingCopyPath(
"mtx-test-data/mp4/10-DanseMacabreOp.40.m4a"), modifyRoutine, &OverallTests::checkMp4Testfile1);
593 makeFile(TestUtilities::workingCopyPath(
"mtx-test-data/mp4/1080p-DTS-HD-7.1.mp4"), modifyRoutine, &OverallTests::checkMp4Testfile2);
594 makeFile(TestUtilities::workingCopyPath(
"mtx-test-data/mp4/dash/dragon-age-inquisition-H1LkM6IVlm4-video.mp4"), modifyRoutine,
595 &OverallTests::checkMp4Testfile3);
596 makeFile(TestUtilities::workingCopyPath(
"mtx-test-data/alac/othertest-itunes.m4a"), modifyRoutine, &OverallTests::checkMp4Testfile4);
597 makeFile(TestUtilities::workingCopyPath(
"mtx-test-data/aac/he-aacv2-ps.m4a"), modifyRoutine, &OverallTests::checkMp4Testfile5);
598 makeFile(TestUtilities::workingCopyPath(
"mp4/android-8.1-camera-recoding.mp4"), modifyRoutine, &OverallTests::checkMp4Testfile7);
600 modifyRoutine = (m_mode &
RemoveTagOrTrack) ? &OverallTests::removeSecondTrack : &OverallTests::alterMp4Tracks;
601 m_fileInfo.setTagPosition(ElementPosition::Keep);
602 makeFile(TestUtilities::workingCopyPath(
"mtx-test-data/mp4/1080p-DTS-HD-7.1.mp4"), modifyRoutine, &OverallTests::checkMp4Testfile6);
Implementation of TagParser::Tag for the MP4 container.
The Tag class is used to store, read and write tag information.
The PositionInSet class describes the position of an element in a set which consists of a certain num...
virtual bool setValue(KnownField field, const TagValue &value)=0
Assigns the given value to the specified field.
The Size class defines the size of a two-dimensional object using integer point precision.
The OverallTests class tests reading and writing tags and parsing technical information for all suppo...
void testMp4Parsing()
Tests the MP4 parser via MediaFileInfo.
std::string toString(TagTextEncoding encoding=TagTextEncoding::Unspecified) const
Converts the value of the current TagValue object to its equivalent std::string representation.
void testMp4Making()
Tests the MP4 maker via MediaFileInfo.
virtual const TagValue & value(KnownField field) const =0
Returns the value of the specified field.
const TagValue & value(KnownField value) const override
Returns the value of the specified field.