Tag Parser: vorbis/vorbiscomment.cpp Source File

Tag Parser 12.5.2 C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags

Loading...
Searching...
No Matches
vorbiscomment.cpp
Go to the documentation of this file.
1#include "./vorbiscomment.h"
2#include "./vorbiscommentids.h"
3
4#include "../ogg/oggiterator.h"
5
6#include "../diagnostics.h"
7#include "../exceptions.h"
8
9#include <c++utilities/conversion/stringbuilder.h>
10#include <c++utilities/io/binaryreader.h>
11#include <c++utilities/io/binarywriter.h>
12#include <c++utilities/io/copy.h>
13
14#include <map>
15#include <memory>
16
17using namespace std;
18using namespace CppUtilities;
19
20namespace TagParser {
21
26
27 const TagValue &VorbisComment::value(KnownField field) const
28{
29 switch (field) {
30 case KnownField::Vendor:
31 return vendor();
32 default:
33 return FieldMapBasedTag<VorbisComment>::value(field);
34 }
35}
36
37 bool VorbisComment::setValue(KnownField field, const TagValue &value)
38{
39 switch (field) {
40 case KnownField::Vendor:
41 setVendor(value);
42 return true;
43 default:
44 return FieldMapBasedTag<VorbisComment>::setValue(field, value);
45 }
46}
47
48 VorbisComment::IdentifierType VorbisComment::internallyGetFieldId(KnownField field) const
49{
50 using namespace VorbisCommentIds;
51 switch (field) {
52 case KnownField::Album:
53 return std::string(album());
54 case KnownField::Artist:
55 return std::string(artist());
56 case KnownField::Comment:
57 return std::string(comment());
58 case KnownField::Cover:
59 return std::string(cover());
60 case KnownField::RecordDate:
61 return std::string(date());
62 case KnownField::Title:
63 return std::string(title());
64 case KnownField::Genre:
65 return std::string(genre());
66 case KnownField::TrackPosition:
67 return std::string(trackNumber());
68 case KnownField::DiskPosition:
69 return std::string(diskNumber());
70 case KnownField::PartNumber:
71 return std::string(partNumber());
72 case KnownField::Composer:
73 return std::string(composer());
74 case KnownField::Encoder:
75 return std::string(encoder());
76 case KnownField::EncodedBy:
77 return std::string(encodedBy());
78 case KnownField::EncoderSettings:
79 return std::string(encoderSettings());
80 case KnownField::Description:
81 return std::string(description());
82 case KnownField::Grouping:
83 return std::string(grouping());
84 case KnownField::RecordLabel:
85 return std::string(label());
86 case KnownField::Performers:
87 return std::string(performer());
88 case KnownField::Language:
89 return std::string(language());
90 case KnownField::Lyricist:
91 return std::string(lyricist());
92 case KnownField::Lyrics:
93 return std::string(lyrics());
94 case KnownField::AlbumArtist:
95 return std::string(albumArtist());
96 case KnownField::Conductor:
97 return std::string(conductor());
98 case KnownField::Copyright:
99 return std::string(copyright());
100 case KnownField::License:
101 return std::string(license());
102 case KnownField::Director:
103 return std::string(director());
104 case KnownField::ISRC:
105 return std::string(isrc());
106 case KnownField::Rating:
107 return std::string(rating());
108 case KnownField::Bpm:
109 return std::string(bpm());
110 case KnownField::Publisher:
111 return std::string(publisher());
112 case KnownField::PublisherWebpage:
113 return std::string(publisherWebpage());
114 case KnownField::PerformerWebpage:
115 return std::string(website());
116 case KnownField::Arranger:
117 return std::string(arranger());
118 default:
119 return std::string();
120 }
121}
122
123 KnownField VorbisComment::internallyGetKnownField(const IdentifierType &id) const
124{
125 using namespace VorbisCommentIds;
126 // clang-format off
127 static const std::map<std::string_view, KnownField, CaseInsensitiveStringComparer> fieldMap({
128 { album(), KnownField::Album },
129 { artist(), KnownField::Artist },
130 { comment(), KnownField::Comment },
131 { cover(), KnownField::Cover },
132 { date(), KnownField::RecordDate },
133 { year(), KnownField::RecordDate },
134 { title(), KnownField::Title },
135 { genre(), KnownField::Genre },
136 { trackNumber(), KnownField::TrackPosition },
137 { diskNumber(), KnownField::DiskPosition },
138 { partNumber(), KnownField::PartNumber },
139 { composer(), KnownField::Composer },
140 { encoder(), KnownField::Encoder },
141 { encodedBy(), KnownField::EncodedBy },
142 { encoderSettings(), KnownField::EncoderSettings },
143 { description(), KnownField::Description },
144 { grouping(), KnownField::Grouping },
145 { label(), KnownField::RecordLabel },
146 { performer(), KnownField::Performers },
147 { lyricist(), KnownField::Lyricist },
148 { lyrics(), KnownField::Lyrics },
149 { albumArtist(), KnownField::AlbumArtist },
150 { conductor(), KnownField::Conductor },
151 { copyright(), KnownField::Copyright },
152 { license(), KnownField::License },
153 { director(), KnownField::Director },
154 { isrc(), KnownField::ISRC },
155 { rating(), KnownField::Rating },
156 { bpm(), KnownField::Bpm },
157 { publisher(), KnownField::Publisher },
158 { publisherWebpage(), KnownField::PublisherWebpage },
159 { website(), KnownField::PerformerWebpage },
160 { arranger(), KnownField::Arranger },
161 });
162 // clang-format on
163 const auto knownField(fieldMap.find(id));
164 return knownField != fieldMap.cend() ? knownField->second : KnownField::Invalid;
165}
166
168void VorbisComment::extendPositionInSetField(std::string_view field, std::string_view totalField, const std::string &diagContext, Diagnostics &diag)
169{
170 auto totalValues = std::vector<std::int32_t>();
171 auto fieldsIter = fields().equal_range(std::string(totalField));
172 auto fieldsDist = std::distance(fieldsIter.first, fieldsIter.second);
173 if (!fieldsDist) {
174 return;
175 }
176 totalValues.reserve(static_cast<std::size_t>(fieldsDist));
177 for (; fieldsIter.first != fieldsIter.second;) {
178 try {
179 totalValues.emplace_back(fieldsIter.first->second.value().toInteger());
180 fields().erase(fieldsIter.first++);
181 } catch (const ConversionException &e) {
182 diag.emplace_back(DiagLevel::Warning, argsToString("Unable to parse \"", totalField, "\" as integer: ", e.what()), diagContext);
183 totalValues.emplace_back(0);
184 ++fieldsIter.first;
185 }
186 }
187
188 auto totalIter = totalValues.begin(), totalEnd = totalValues.end();
189 for (fieldsIter = fields().equal_range(std::string(field)); fieldsIter.first != fieldsIter.second && totalIter != totalEnd;
190 ++fieldsIter.first, ++totalIter) {
191 auto &v = fieldsIter.first->second.value();
192 try {
193 auto p = v.toPositionInSet();
194 if (p.total() && p.total() != *totalIter) {
195 diag.emplace_back(DiagLevel::Warning,
196 argsToString("The \"", totalField, "\" field value (", *totalIter, ") does not match \"", field, "\" field value (", p.total(),
197 "). Discarding the former in favor of the latter."),
198 diagContext);
199 } else {
200 p.setTotal(*totalIter);
201 v.assignPosition(p);
202 }
203 } catch (const ConversionException &e) {
204 diag.emplace_back(DiagLevel::Warning,
205 argsToString("Unable to parse \"", field, "\" as position in set for incorporating \"", totalField, "\": ", e.what()), diagContext);
206 }
207 }
208 if (totalIter != totalEnd) {
209 diag.emplace_back(
210 DiagLevel::Warning, argsToString("Vorbis Comment contains more \"", totalField, "\" fields than \"", field, "\" fields."), diagContext);
211 }
212 for (; totalIter != totalEnd; ++totalIter) {
213 fields().insert(std::make_pair(field, VorbisCommentField(std::string(field), TagValue(PositionInSet(0, *totalIter)))));
214 }
215}
217
221void VorbisComment::convertTotalFields(const std::string &diagContext, Diagnostics &diag)
222{
223 extendPositionInSetField(VorbisCommentIds::trackNumber(), VorbisCommentIds::trackTotal(), diagContext, diag);
224 extendPositionInSetField(VorbisCommentIds::diskNumber(), VorbisCommentIds::diskTotal(), diagContext, diag);
225 extendPositionInSetField(VorbisCommentIds::partNumber(), VorbisCommentIds::partTotal(), diagContext, diag);
226}
227
231template <class StreamType>
232void VorbisComment::internalParse(StreamType &stream, std::uint64_t maxSize, VorbisCommentFlags flags, std::uint64_t &padding, Diagnostics &diag)
233{
234 // prepare parsing
235 static const string context("parsing Vorbis comment");
236 const auto startOffset = static_cast<std::uint64_t>(stream.tellg());
237 try {
238 // read signature: 0x3 + "vorbis"
239 char sig[8];
240 bool skipSignature = flags & VorbisCommentFlags::NoSignature;
241 if (!skipSignature) {
242 CHECK_MAX_SIZE(7)
243 stream.read(sig, 7);
244 skipSignature = (BE::toInt<std::uint64_t>(sig) & 0xffffffffffffff00u) == 0x03766F7262697300u;
245 }
246 if (skipSignature) {
247 // read vendor (length prefixed string)
248 {
249 CHECK_MAX_SIZE(4)
250 stream.read(sig, 4);
251 const auto vendorSize = LE::toUInt32(sig);
252 if (vendorSize <= maxSize) {
253 auto buff = make_unique<char[]>(vendorSize);
254 stream.read(buff.get(), vendorSize);
255 m_vendor.assignData(std::move(buff), vendorSize, TagDataType::Text, TagTextEncoding::Utf8);
256 // TODO: Is the vendor string actually UTF-8 (like the field values)?
257 } else {
258 diag.emplace_back(DiagLevel::Critical, "Vendor information is truncated.", context);
259 throw TruncatedDataException();
260 }
261 maxSize -= vendorSize;
262 }
263 // read field count
264 CHECK_MAX_SIZE(4)
265 stream.read(sig, 4);
266 std::uint32_t fieldCount = LE::toUInt32(sig);
267 for (std::uint32_t i = 0; i < fieldCount; ++i) {
268 // read fields
269 VorbisCommentField field;
270 try {
271 field.parse(stream, maxSize, diag);
272 fields().emplace(field.id(), std::move(field));
273 } catch (const TruncatedDataException &) {
274 throw;
275 } catch (const Failure &) {
276 // nothing to do here since notifications will be added anyways
277 }
278 }
279 if (!(flags & VorbisCommentFlags::NoFramingByte)) {
280 stream.ignore(); // skip framing byte
281 }
282 m_size = static_cast<std::uint64_t>(stream.tellg()) - startOffset;
283 // turn "YEAR" into "DATE" (unless "DATE" exists)
284 // note: "DATE" is an official field and "YEAR" only an unofficial one but present in some files. In consistency with
285 // MediaInfo and VLC player it is treated like "DATE" here.
286 static const auto dateFieldId = std::string(VorbisCommentIds::date()), yearFieldId = std::string(VorbisCommentIds::year());
287 if (fields().find(dateFieldId) == fields().end()) {
288 const auto [first, end] = fields().equal_range(yearFieldId);
289 for (auto i = first; i != end; ++i) {
290 fields().emplace(dateFieldId, std::move(i->second));
291 }
292 fields().erase(first, end);
293 }
294 } else {
295 diag.emplace_back(DiagLevel::Critical, "Signature is invalid.", context);
296 throw InvalidDataException();
297 }
298 } catch (const TruncatedDataException &) {
299 m_size = static_cast<std::uint64_t>(stream.tellg()) - startOffset;
300 diag.emplace_back(DiagLevel::Critical, "Vorbis comment is truncated.", context);
301 throw;
302 }
303
304 // warn if there are bytes left in the last segment of the Ogg packet containing the comment
305 if constexpr (std::is_same_v<std::decay_t<StreamType>, OggIterator>) {
306 auto bytesRemaining = std::uint64_t();
307 if (stream) {
308 bytesRemaining = stream.remainingBytesInCurrentSegment();
309 if (stream.currentPage().isLastSegmentUnconcluded()) {
310 stream.nextSegment();
311 if (stream) {
312 bytesRemaining += stream.remainingBytesInCurrentSegment();
313 }
314 }
315 }
316 if (bytesRemaining) {
317 diag.emplace_back(DiagLevel::Information, argsToString(bytesRemaining, " bytes left in last segment."), context);
318 padding += bytesRemaining;
319 }
320 }
321
322 if (flags & VorbisCommentFlags::ConvertTotalFields) {
323 convertTotalFields(context, diag);
324 }
325}
326
334 void VorbisComment::parse(OggIterator &iterator, VorbisCommentFlags flags, Diagnostics &diag)
335{
336 auto padding = std::uint64_t();
337 internalParse(iterator, iterator.streamSize(), flags, padding, diag);
338}
339
347 void VorbisComment::parse(OggIterator &iterator, VorbisCommentFlags flags, std::uint64_t &padding, Diagnostics &diag)
348{
349 internalParse(iterator, iterator.streamSize(), flags, padding, diag);
350}
351
359 void VorbisComment::parse(istream &stream, std::uint64_t maxSize, VorbisCommentFlags flags, Diagnostics &diag)
360{
361 auto padding = std::uint64_t();
362 internalParse(stream, maxSize, flags, padding, diag);
363}
364
366static std::uint32_t makeField(VorbisCommentField &field, BinaryWriter &writer, VorbisCommentFlags flags, Diagnostics &diag)
367{
368 if (field.value().isEmpty()) {
369 return 0;
370 }
371 try {
372 if (field.make(writer, flags, diag)) {
373 return 1;
374 }
375 } catch (const Failure &) {
376 }
377 return 0;
378}
380
388 void VorbisComment::make(std::ostream &stream, VorbisCommentFlags flags, Diagnostics &diag)
389{
390 // prepare making
391 static const string context("making Vorbis comment");
392 string vendor;
393 try {
394 m_vendor.toString(vendor);
395 } catch (const ConversionException &) {
396 diag.emplace_back(DiagLevel::Warning, "Can not convert the assigned vendor to string.", context);
397 }
398 BinaryWriter writer(&stream);
399 if (!(flags & VorbisCommentFlags::NoSignature)) {
400 // write signature
401 static const char sig[7] = { 0x03, 0x76, 0x6F, 0x72, 0x62, 0x69, 0x73 };
402 stream.write(sig, sizeof(sig));
403 }
404 // write vendor
405 writer.writeUInt32LE(static_cast<std::uint32_t>(vendor.size()));
406 writer.writeString(vendor);
407 // write field count later
408 const auto fieldCountOffset = stream.tellp();
409 writer.writeUInt32LE(0);
410 // write fields
411 std::uint32_t fieldsWritten = 0;
412 static const auto coverId = std::string(VorbisCommentIds::cover());
413 for (auto &i : fields()) {
414 if (i.first != coverId) { // write cover at the end
415 fieldsWritten += makeField(i.second, writer, flags, diag);
416 }
417 }
418 if (const auto cover = fields().find(coverId); cover != fields().end()) {
419 fieldsWritten += makeField(cover->second, writer, flags, diag);
420 }
421 // write field count
422 const auto framingByteOffset = stream.tellp();
423 stream.seekp(fieldCountOffset);
424 writer.writeUInt32LE(fieldsWritten);
425 stream.seekp(framingByteOffset);
426 // write framing byte
427 if (!(flags & VorbisCommentFlags::NoFramingByte)) {
428 stream.put(0x01);
429 }
430}
431
432} // namespace TagParser
TagParser::Diagnostics
The Diagnostics class is a container for DiagMessage.
Definition diagnostics.h:156
TagParser::FieldMapBasedTag::setValue
bool setValue(const IdentifierType &id, const TagValue &value)
Assigns the given value to the field with the specified id.
Definition fieldbasedtag.h:253
TagParser::FieldMapBasedTag< VorbisComment >::fields
const std::multimap< IdentifierType, FieldType, Compare > & fields() const
TagParser::FieldMapBasedTag< VorbisComment >::IdentifierType
typename FieldMapBasedTagTraits< VorbisComment >::FieldType::IdentifierType IdentifierType
Definition fieldbasedtag.h:35
TagParser::FieldMapBasedTag::value
const TagValue & value(const IdentifierType &id) const
Returns the value of the field with the specified id.
Definition fieldbasedtag.h:161
TagParser::FieldMapBasedTag< VorbisComment >::knownField
KnownField knownField(const IdentifierType &id) const
Definition fieldbasedtag.h:365
TagParser::OggIterator
The OggIterator class helps iterating through all segments of an Ogg bitstream.
Definition oggiterator.h:11
TagParser::OggIterator::streamSize
std::uint64_t streamSize() const
Returns the stream size (which has been specified when constructing the iterator).
Definition oggiterator.h:117
TagParser::TagField::value
TagValue & value()
Returns the value of the current TagField.
Definition generictagfield.h:156
TagParser::TagValue
The TagValue class wraps values of different types.
Definition tagvalue.h:147
TagParser::TagValue::isEmpty
bool isEmpty() const
Returns whether no or an empty value is assigned.
Definition tagvalue.h:490
TagParser::Tag::m_size
std::uint64_t m_size
Definition tag.h:217
TagParser::Tag::fieldCount
virtual std::size_t fieldCount() const =0
Returns the number of present fields.
TagParser::VorbisCommentField
The VorbisCommentField class is used by VorbisComment to store the fields.
Definition vorbiscommentfield.h:46
TagParser::VorbisCommentField::make
bool make(CppUtilities::BinaryWriter &writer, VorbisCommentFlags flags, Diagnostics &diag)
Writes the field to a stream using the specified writer.
Definition vorbiscommentfield.cpp:178
TagParser::VorbisComment::make
void make(std::ostream &stream, VorbisCommentFlags flags, Diagnostics &diag)
Writes tag information to the specified stream.
Definition vorbiscomment.cpp:388
TagParser::VorbisComment::vendor
const TagValue & vendor() const
Returns the vendor.
Definition vorbiscomment.h:83
TagParser::VorbisComment::parse
void parse(OggIterator &iterator, VorbisCommentFlags flags, Diagnostics &diag)
Parses tag information using the specified Ogg iterator.
Definition vorbiscomment.cpp:334
TagParser::VorbisComment::internallyGetFieldId
IdentifierType internallyGetFieldId(KnownField field) const
Definition vorbiscomment.cpp:48
TagParser::VorbisComment::setVendor
void setVendor(const TagValue &vendor)
Sets the vendor.
Definition vorbiscomment.h:92
TagParser::VorbisComment::value
const TagValue & value(KnownField field) const override
Returns the value of the specified field.
Definition vorbiscomment.cpp:27
TagParser::VorbisComment::setValue
bool setValue(KnownField field, const TagValue &value) override
Assigns the given value to the specified field.
Definition vorbiscomment.cpp:37
TagParser::VorbisComment::internallyGetKnownField
KnownField internallyGetKnownField(const IdentifierType &id) const
Definition vorbiscomment.cpp:123
diagnostics.h
exceptions.h
CHECK_MAX_SIZE
#define CHECK_MAX_SIZE(sizeDenotation)
Throws TruncatedDataException() if the specified sizeDenotation exceeds maxSize; otherwise maxSize is...
Definition exceptions.h:70
CppUtilities
Definition abstractcontainer.h:16
TagParser::VorbisCommentIds
Encapsulates Vorbis comment field names.
Definition vorbiscommentids.h:19
TagParser::VorbisCommentIds::trackNumber
constexpr TAG_PARSER_EXPORT std::string_view trackNumber()
Definition vorbiscommentids.h:21
TagParser::VorbisCommentIds::diskTotal
constexpr TAG_PARSER_EXPORT std::string_view diskTotal()
Definition vorbiscommentids.h:33
TagParser::VorbisCommentIds::year
constexpr TAG_PARSER_EXPORT std::string_view year()
Definition vorbiscommentids.h:177
TagParser::VorbisCommentIds::partTotal
constexpr TAG_PARSER_EXPORT std::string_view partTotal()
Definition vorbiscommentids.h:45
TagParser::VorbisCommentIds::partNumber
constexpr TAG_PARSER_EXPORT std::string_view partNumber()
Definition vorbiscommentids.h:41
TagParser::VorbisCommentIds::trackTotal
constexpr TAG_PARSER_EXPORT std::string_view trackTotal()
Definition vorbiscommentids.h:25
TagParser::VorbisCommentIds::diskNumber
constexpr TAG_PARSER_EXPORT std::string_view diskNumber()
Definition vorbiscommentids.h:29
TagParser::VorbisCommentIds::cover
constexpr TAG_PARSER_EXPORT std::string_view cover()
Definition vorbiscommentids.h:201
TagParser::VorbisCommentIds::date
constexpr TAG_PARSER_EXPORT std::string_view date()
Definition vorbiscommentids.h:173
TagParser
Contains all classes and functions of the TagInfo library.
Definition aaccodebook.h:10
TagParser::KnownField
KnownField
Specifies the field.
Definition tag.h:37
TagParser::KnownField::Grouping
@ Grouping
Definition tag.h:59
TagParser::KnownField::Comment
@ Comment
Definition tag.h:43
TagParser::KnownField::Lyricist
@ Lyricist
Definition tag.h:46
TagParser::KnownField::EncodedBy
@ EncodedBy
Definition tag.h:90
TagParser::KnownField::Performers
@ Performers
Definition tag.h:53
TagParser::KnownField::TrackPosition
@ TrackPosition
Definition tag.h:47
TagParser::KnownField::Publisher
@ Publisher
Definition tag.h:95
TagParser::KnownField::Conductor
@ Conductor
Definition tag.h:71
TagParser::KnownField::Artist
@ Artist
Definition tag.h:41
TagParser::KnownField::EncoderSettings
@ EncoderSettings
Definition tag.h:56
TagParser::KnownField::Language
@ Language
Definition tag.h:55
TagParser::KnownField::Invalid
@ Invalid
Definition tag.h:38
TagParser::KnownField::Copyright
@ Copyright
Definition tag.h:133
TagParser::KnownField::License
@ License
Definition tag.h:135
TagParser::KnownField::Director
@ Director
Definition tag.h:72
TagParser::KnownField::Encoder
@ Encoder
Definition tag.h:51
TagParser::KnownField::Genre
@ Genre
Definition tag.h:42
TagParser::KnownField::AlbumArtist
@ AlbumArtist
Definition tag.h:66
TagParser::KnownField::Lyrics
@ Lyrics
Definition tag.h:57
TagParser::KnownField::Bpm
@ Bpm
Definition tag.h:44
TagParser::KnownField::Arranger
@ Arranger
Definition tag.h:70
TagParser::KnownField::ISRC
@ ISRC
Definition tag.h:118
TagParser::KnownField::Description
@ Description
Definition tag.h:64
TagParser::KnownField::Title
@ Title
Definition tag.h:39
TagParser::KnownField::Composer
@ Composer
Definition tag.h:62
TagParser::KnownField::Album
@ Album
Definition tag.h:40
TagParser::KnownField::Cover
@ Cover
Definition tag.h:61
TagParser::KnownField::RecordDate
@ RecordDate
Definition tag.h:52
TagParser::KnownField::Rating
@ Rating
Definition tag.h:63
TagParser::KnownField::PerformerWebpage
@ PerformerWebpage
Definition tag.h:140
TagParser::KnownField::Vendor
@ Vendor
Definition tag.h:65
TagParser::KnownField::PartNumber
@ PartNumber
Definition tag.h:49
TagParser::KnownField::PublisherWebpage
@ PublisherWebpage
Definition tag.h:137
TagParser::KnownField::DiskPosition
@ DiskPosition
Definition tag.h:48
TagParser::KnownField::RecordLabel
@ RecordLabel
Definition tag.h:60
TagParser::TagTextEncoding::Utf8
@ Utf8
Definition tagvalue.h:31
TagParser::DiagLevel::Warning
@ Warning
Definition diagnostics.h:20
TagParser::DiagLevel::Critical
@ Critical
Definition diagnostics.h:21
TagParser::DiagLevel::Information
@ Information
Definition diagnostics.h:19
TagParser::TagDataType::PositionInSet
@ PositionInSet
Definition tagvalue.h:122
TagParser::TagDataType::Text
@ Text
Definition tagvalue.h:120
TagParser::VorbisCommentFlags
VorbisCommentFlags
The VorbisCommentFlags enum specifies flags which controls parsing and making of Vorbis comments.
Definition vorbiscommentfield.h:18
TagParser::VorbisCommentFlags::ConvertTotalFields
@ ConvertTotalFields
Definition vorbiscommentfield.h:23
TagParser::VorbisCommentFlags::NoSignature
@ NoSignature
Definition vorbiscommentfield.h:20
TagParser::VorbisCommentFlags::NoFramingByte
@ NoFramingByte
Definition vorbiscommentfield.h:21
oggiterator.h
vorbiscomment.h
vorbiscommentids.h