Tag Parser 12.5.1
C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
Loading...
Searching...
No Matches
overallogg.cpp
Go to the documentation of this file.
1#include "./helper.h"
2#include "./overall.h"
3
4#include "../abstracttrack.h"
5#include "../tag.h"
9
10#include <c++utilities/io/misc.h>
11
12#include <algorithm>
13
14using namespace CppUtilities;
15
19void OverallTests::checkOggTestfile1()
20{
21 CPPUNIT_ASSERT_EQUAL(ContainerFormat::Ogg, m_fileInfo.containerFormat());
22 const auto tracks = m_fileInfo.tracks();
23 CPPUNIT_ASSERT_EQUAL(2_st, tracks.size());
24 for (const auto &track : tracks) {
25 switch (track->id()) {
26 case 897658443:
27 CPPUNIT_ASSERT_EQUAL(MediaType::Video, track->mediaType());
28 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Theora, track->format().general);
29 break;
30 case 1755441791:
31 CPPUNIT_ASSERT_EQUAL(MediaType::Audio, track->mediaType());
32 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Vorbis, track->format().general);
33 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint16_t>(2), track->channelCount());
34 CPPUNIT_ASSERT_EQUAL(44100u, track->samplingFrequency());
35 CPPUNIT_ASSERT_EQUAL(4, track->duration().minutes());
36 break;
37 default:
38 CPPUNIT_FAIL("unknown track ID");
39 }
40 }
41 const auto tags = m_fileInfo.tags();
42 switch (m_tagStatus) {
44 CPPUNIT_ASSERT(m_fileInfo.hasAnyTag());
45 CPPUNIT_ASSERT_EQUAL(1_st, tags.size());
46 CPPUNIT_ASSERT_EQUAL("ffmpeg2theora 0.13"s, tags.front()->value(KnownField::Encoder).toString());
47 CPPUNIT_ASSERT_EQUAL(std::vector<std::uint64_t>{ 0x68a1ea7f }, tags.front()->target().tracks());
48 // Theora tags are currently not supported and hence only the Vorbis comment is
49 // taken into account here
50 break;
52 checkOggTestMetaData();
53 break;
55 CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
56 }
57
58 CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Information);
59}
60
64void OverallTests::checkOggTestfile2()
65{
66 CPPUNIT_ASSERT_EQUAL(ContainerFormat::Ogg, m_fileInfo.containerFormat());
67 const auto tracks = m_fileInfo.tracks();
68 CPPUNIT_ASSERT_EQUAL(1_st, tracks.size());
69 for (const auto &track : tracks) {
70 switch (track->id()) {
71 case 1375632254:
72 CPPUNIT_ASSERT_EQUAL(MediaType::Audio, track->mediaType());
73 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Opus, track->format().general);
74 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint16_t>(2), track->channelCount());
75 CPPUNIT_ASSERT_EQUAL(48000u, track->samplingFrequency());
76 CPPUNIT_ASSERT_EQUAL(1, track->duration().minutes());
77 break;
78 default:
79 CPPUNIT_FAIL("unknown track ID");
80 }
81 }
82 const auto tags = m_fileInfo.tags();
83 switch (m_tagStatus) {
85 CPPUNIT_ASSERT(m_fileInfo.hasAnyTag());
86 CPPUNIT_ASSERT_EQUAL(1_st, tags.size());
87 CPPUNIT_ASSERT_EQUAL("opusenc from opus-tools 0.1.6"s, tags.front()->value(KnownField::Encoder).toString());
88 break;
90 checkOggTestMetaData();
91 break;
93 CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
94 }
95
96 CPPUNIT_ASSERT(m_diag.level() <= DiagLevel::Information);
97}
98
102void OverallTests::checkOggTestfile3()
103{
104 CPPUNIT_ASSERT_EQUAL(ContainerFormat::Ogg, m_fileInfo.containerFormat());
105 const auto tracks = m_fileInfo.tracks();
106 CPPUNIT_ASSERT_EQUAL(1_st, tracks.size());
107 for (const auto &track : tracks) {
108 switch (track->id()) {
109 case 1843569915:
110 CPPUNIT_ASSERT_EQUAL(MediaType::Audio, track->mediaType());
111 CPPUNIT_ASSERT_EQUAL(GeneralMediaFormat::Opus, track->format().general);
112 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint16_t>(2), track->channelCount());
113 CPPUNIT_ASSERT_EQUAL(48000u, track->samplingFrequency());
114 CPPUNIT_ASSERT_EQUAL(TimeSpan::fromSeconds(19.461), track->duration());
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(m_fileInfo.hasAnyTag());
124 CPPUNIT_ASSERT_EQUAL(1_st, tags.size());
125 CPPUNIT_ASSERT_EQUAL("Lavf58.76.100"s, tags.front()->value(KnownField::Encoder).toString());
126 CPPUNIT_ASSERT_EQUAL("eng"s, tags.front()->value(KnownField::Language).toString());
127 [[fallthrough]];
129 checkOggTestMetaDataCover();
130 break;
132 CPPUNIT_ASSERT_EQUAL(0_st, tags.size());
133 }
134
135 if (m_tagStatus != TagStatus::Original) {
136 CPPUNIT_ASSERT_MESSAGE("no warnings for non-broken file", m_diag.level() <= DiagLevel::Information);
137 return;
138 }
139 CPPUNIT_ASSERT_EQUAL_MESSAGE("warning present", DiagLevel::Warning, m_diag.level());
140 CPPUNIT_ASSERT(std::find_if(m_diag.begin(), m_diag.end(), [](const auto &msg) {
141 return startsWith(msg.message(), "3 bytes left in last segment");
142 }) != m_diag.end());
143 CPPUNIT_ASSERT(std::find_if(m_diag.begin(), m_diag.end(), [](const auto &msg) {
144 return startsWith(msg.message(), "Tag spans over 6 pages but absolute granule position of unfinished page at");
145 }) != m_diag.end());
146 CPPUNIT_ASSERT(std::find_if(m_diag.begin(), m_diag.end(), [](const auto &msg) {
147 return startsWith(msg.message(), "The tag is continued in Ogg page at");
148 }) != m_diag.end());
149}
150
154void OverallTests::checkOggTestMetaData()
155{
156 // check whether a tag is assigned
157 const auto tags = m_fileInfo.tags();
158 const auto *const tag = m_fileInfo.vorbisComment();
159 CPPUNIT_ASSERT_EQUAL(1_st, tags.size());
160 CPPUNIT_ASSERT(tag != nullptr);
161
162 // check test meta data
163 CPPUNIT_ASSERT_EQUAL(m_testTitle, tag->value(KnownField::Title));
164 CPPUNIT_ASSERT_EQUAL(m_testComment.toString(), tag->value(KnownField::Comment).toString()); // loss of description is ok
165 CPPUNIT_ASSERT_EQUAL(m_testAlbum, tag->value(KnownField::Album));
166 CPPUNIT_ASSERT_EQUAL(m_preservedMetaData.front(), tag->value(KnownField::Artist));
167 CPPUNIT_ASSERT_EQUAL(m_testPosition, tag->value(KnownField::TrackPosition));
168 CPPUNIT_ASSERT_EQUAL(m_testPosition, tag->value(KnownField::DiskPosition));
169 // TODO: check more fields
170 m_preservedMetaData.pop();
171}
172
173void OverallTests::checkOggTestMetaDataCover()
174{
175 // check whether a tag is assigned
176 const auto tags = m_fileInfo.tags();
177 const auto *const tag = m_fileInfo.vorbisComment();
178 CPPUNIT_ASSERT_EQUAL(1_st, tags.size());
179 CPPUNIT_ASSERT(tag != nullptr);
180
181 const auto expectedCoverData = readFile(testFilePath("ogg/example-cover.png"));
182 CPPUNIT_ASSERT_EQUAL_MESSAGE("expected cover assigned", std::string_view(expectedCoverData), tag->value(KnownField::Cover).data());
183}
184
185void OverallTests::setOggTestMetaData()
186{
187 // ensure a tag exists
188 auto *const tag = m_fileInfo.createVorbisComment();
189
190 // assign test meta data
191 tag->setValue(KnownField::Title, m_testTitle);
192 tag->setValue(KnownField::Comment, m_testComment);
193 tag->setValue(KnownField::Album, m_testAlbum);
194 m_preservedMetaData.push(tag->value(KnownField::Artist));
195 tag->setValue(KnownField::TrackPosition, m_testPosition);
196 tag->setValue(KnownField::DiskPosition, m_testPosition);
197 // TODO: set more fields
198}
199
200void OverallTests::setOggTestMetaDataCover()
201{
202 auto *const tag = m_fileInfo.createVorbisComment();
203 const auto cover = readFile(testFilePath("ogg/example-cover.png"));
204 tag->setValue(KnownField::Cover, TagValue(cover.data(), cover.size(), TagDataType::Picture));
205}
206
212{
213 cerr << endl << "Ogg parser" << endl;
214 m_fileInfo.setForceFullParse(false);
215 m_tagStatus = TagStatus::Original;
216 parseFile(testFilePath("mtx-test-data/ogg/qt4dance_medium.ogg"), &OverallTests::checkOggTestfile1);
217 parseFile(testFilePath("mtx-test-data/opus/v-opus.ogg"), &OverallTests::checkOggTestfile2);
218 parseFile(testFilePath("ogg/noise-broken-segment-termination.opus"), &OverallTests::checkOggTestfile3);
219}
220
228{
229 // full parse is required to determine padding
230 m_fileInfo.setForceFullParse(true);
231
232 // do the test under different conditions
233 for (m_mode = 0; m_mode != 0x2; ++m_mode) {
234 using namespace SimpleTestFlags;
235
236 // no need to setup test conditions because the Ogg maker
237 // doesn't take those settings into account (currently)
238
239 // print test conditions
240 list<string> testConditions;
241 if (m_mode & RemoveTag) {
242 testConditions.emplace_back("removing tag");
243 } else {
244 testConditions.emplace_back("modifying tag");
245 }
246 cerr << endl << "Ogg maker - testmode " << m_mode << ": " << joinStrings(testConditions, ", ") << endl;
247
248 // do actual tests
250 const auto modifyRoutine = (m_mode & RemoveTag) ? &OverallTests::removeAllTags : &OverallTests::setOggTestMetaData;
251 const auto modifyRoutineCover = (m_mode & RemoveTag) ? &OverallTests::removeAllTags : &OverallTests::setOggTestMetaDataCover;
252 makeFile(workingCopyPath("mtx-test-data/ogg/qt4dance_medium.ogg"), modifyRoutine, &OverallTests::checkOggTestfile1);
253 makeFile(workingCopyPath("mtx-test-data/opus/v-opus.ogg"), modifyRoutine, &OverallTests::checkOggTestfile2);
254 makeFile(workingCopyPath("ogg/noise-without-cover.opus"), modifyRoutineCover, &OverallTests::checkOggTestfile3);
255 }
256}
257
262{
263 const auto context = std::string();
264 const auto trackNumberFieldId = std::string(VorbisCommentIds::trackNumber());
265 const auto trackTotalFieldId = std::string(VorbisCommentIds::trackTotal());
266 const auto diskNumberFieldId = std::string(VorbisCommentIds::diskNumber());
267 const auto diskTotalFieldId = std::string(VorbisCommentIds::diskTotal());
268
269 auto diag = Diagnostics();
270 auto vc = VorbisComment();
271 auto trackNumber = VorbisCommentField(trackNumberFieldId, TagValue(5));
272 auto trackTotal = VorbisCommentField(trackTotalFieldId, TagValue(20));
273 auto &fields = vc.fields();
274 fields.insert(std::make_pair(trackNumberFieldId, std::move(trackNumber)));
275 fields.insert(std::make_pair(trackTotalFieldId, std::move(trackTotal)));
276 vc.convertTotalFields(context, diag);
277
278 const auto convertedValues = vc.values(trackNumberFieldId);
279 CPPUNIT_ASSERT_EQUAL_MESSAGE("the two fileds have been combined into one", 1_st, fields.size());
280 CPPUNIT_ASSERT_EQUAL_MESSAGE("there is exactly one track number value", 1_st, convertedValues.size());
281 const auto convertedTrackNumber = convertedValues.front()->toPositionInSet();
282 CPPUNIT_ASSERT_EQUAL(PositionInSet(5, 20), convertedTrackNumber);
283 CPPUNIT_ASSERT_EQUAL(0_st, diag.size());
284
285 auto diskNumber = VorbisCommentField(diskNumberFieldId, TagValue("invalid pos"));
286 auto diskTotal = VorbisCommentField(diskTotalFieldId, TagValue("invalid total"));
287 auto diskTotal2 = VorbisCommentField(diskTotalFieldId, TagValue(42));
288 fields.insert(std::make_pair(diskNumberFieldId, std::move(diskNumber)));
289 fields.insert(std::make_pair(diskTotalFieldId, std::move(diskTotal)));
290 fields.insert(std::make_pair(diskTotalFieldId, std::move(diskTotal2)));
291 vc.convertTotalFields(context, diag);
292
293 const auto newDiskNumberValues = vc.values(diskNumberFieldId);
294 const auto newDiskTotalValues = vc.values(diskTotalFieldId);
295 CPPUNIT_ASSERT_EQUAL_MESSAGE("invalid fields have not been combined", 4_st, fields.size());
296 CPPUNIT_ASSERT_EQUAL_MESSAGE("invalid disk position has been preserved and valid disk total converted", 2_st, newDiskNumberValues.size());
297 CPPUNIT_ASSERT_EQUAL_MESSAGE("invalid disk total has been preserved", 1_st, newDiskTotalValues.size());
298 const auto preservedDiskNumber = newDiskNumberValues[0]->toString();
299 const auto convertedDiskTotal = newDiskNumberValues[1]->toPositionInSet();
300 const auto preservedDiskTotal = newDiskTotalValues[0]->toString();
301 CPPUNIT_ASSERT_EQUAL("invalid pos"s, preservedDiskNumber);
302 CPPUNIT_ASSERT_EQUAL(PositionInSet(0, 42), convertedDiskTotal);
303 CPPUNIT_ASSERT_EQUAL("invalid total"s, preservedDiskTotal);
304 CPPUNIT_ASSERT_EQUAL(3_st, diag.size());
305}
void testOggParsing()
Tests the Ogg parser via MediaFileInfo.
void testVorbisCommentFieldHandling()
Tests the Vorbis Comment specifc handling of certain fields done in VorbisComment::convertTotalFields...
void testOggMaking()
Tests the Ogg maker via MediaFileInfo.
The Diagnostics class is a container for DiagMessage.
The TagValue class wraps values of different types.
Definition tagvalue.h:147
The VorbisCommentField class is used by VorbisComment to store the fields.
constexpr TAG_PARSER_EXPORT std::string_view trackNumber()
constexpr TAG_PARSER_EXPORT std::string_view diskTotal()
constexpr TAG_PARSER_EXPORT std::string_view trackTotal()
constexpr TAG_PARSER_EXPORT std::string_view diskNumber()
constexpr TAG_PARSER_EXPORT std::string_view cover()
@ Original
Definition overall.h:28
@ Removed
Definition overall.h:28
@ TestMetaDataPresent
Definition overall.h:28