Tag Parser: vorbis/vorbiscommentfield.cpp Source File

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

Loading...
Searching...
No Matches
vorbiscommentfield.cpp
Go to the documentation of this file.
1#include "./vorbiscommentfield.h"
2#include "./vorbiscommentids.h"
3
4#include "../flac/flacmetadata.h"
5
6#include "../ogg/oggiterator.h"
7
8#include "../diagnostics.h"
9#include "../exceptions.h"
10
11#include <c++utilities/conversion/binaryconversion.h>
12#include <c++utilities/conversion/stringbuilder.h>
13#include <c++utilities/conversion/stringconversion.h>
14#include <c++utilities/io/binaryreader.h>
15#include <c++utilities/io/binarywriter.h>
16
17#include <iostream>
18#include <memory>
19
20using namespace std;
21using namespace CppUtilities;
22
23namespace TagParser {
24
29
33VorbisCommentField::VorbisCommentField()
34{
35}
36
40 VorbisCommentField::VorbisCommentField(const IdentifierType &id, const TagValue &value)
41 : TagField<VorbisCommentField>(id, value)
42{
43}
44
48template <class StreamType> void VorbisCommentField::internalParse(StreamType &stream, std::uint64_t &maxSize, Diagnostics &diag)
49{
50 static const string context("parsing Vorbis comment field");
51 char buff[4];
52 if (maxSize < 4) {
53 diag.emplace_back(DiagLevel::Critical, argsToString("Field expected at ", static_cast<std::streamoff>(stream.tellg()), '.'), context);
54 throw TruncatedDataException();
55 } else {
56 maxSize -= 4;
57 }
58 stream.read(buff, 4);
59 if (const auto size = LE::toUInt32(buff)) { // read size
60 if (size <= maxSize) {
61 maxSize -= size;
62 // read data
63 auto data = make_unique<char[]>(size);
64 stream.read(data.get(), size);
65 std::uint32_t idSize = 0;
66 for (const char *i = data.get(), *end = data.get() + size; i != end && *i != '='; ++i, ++idSize)
67 ;
68 // extract id
69 setId(string(data.get(), idSize));
70 if (!idSize) {
71 // empty field ID
72 diag.emplace_back(
73 DiagLevel::Critical, argsToString("The field ID at ", static_cast<std::streamoff>(stream.tellg()), " is empty."), context);
74 throw InvalidDataException();
75 } else if (id() == VorbisCommentIds::cover()) {
76 // extract cover value
77 try {
78 auto decoded = decodeBase64(data.get() + idSize + 1, size - idSize - 1);
79 stringstream bufferStream(ios_base::in | ios_base::out | ios_base::binary);
80 bufferStream.exceptions(ios_base::failbit | ios_base::badbit);
81#if defined(__GLIBCXX__) && !defined(_LIBCPP_VERSION)
82 bufferStream.rdbuf()->pubsetbuf(reinterpret_cast<char *>(decoded.first.get()), decoded.second);
83#else
84 bufferStream.write(reinterpret_cast<const char *>(decoded.first.get()), decoded.second);
85#endif
86 FlacMetaDataBlockPicture pictureBlock(value());
87 pictureBlock.parse(bufferStream, decoded.second);
88 setTypeInfo(pictureBlock.pictureType());
89 } catch (const TruncatedDataException &) {
90 diag.emplace_back(DiagLevel::Critical, "METADATA_BLOCK_PICTURE is truncated.", context);
91 throw;
92 } catch (const ConversionException &) {
93 diag.emplace_back(DiagLevel::Critical, "Base64 coding of METADATA_BLOCK_PICTURE is invalid.", context);
94 throw InvalidDataException();
95 } catch (const std::ios_base::failure &failure) {
96 diag.emplace_back(DiagLevel::Critical,
97 argsToString("An IO error occurred when reading the METADATA_BLOCK_PICTURE struct: ", failure.what()), context);
98 throw Failure();
99 }
100 } else if (id().size() + 1 < size) {
101 const auto str = std::string_view(data.get() + idSize + 1, size - idSize - 1);
102 if (id() == VorbisCommentIds::rating()) {
103 try {
104 // set rating as Popularity to preserve the scale information
105 value().assignPopularity(Popularity{ .rating = stringToNumber<double>(str), .scale = TagType::VorbisComment });
106 } catch (const ConversionException &) {
107 // fallback to text
108 value().assignText(str, TagTextEncoding::Utf8);
109 diag.emplace_back(DiagLevel::Warning, argsToString("The rating is not a number."), context);
110 }
111 } else {
112 // extract other values (as string)
113 value().assignText(str, TagTextEncoding::Utf8);
114 }
115 }
116 } else {
117 diag.emplace_back(DiagLevel::Critical, argsToString("Field at ", static_cast<std::streamoff>(stream.tellg()), " is truncated."), context);
118 throw TruncatedDataException();
119 }
120 }
121}
122
133 void VorbisCommentField::parse(OggIterator &iterator, Diagnostics &diag)
134{
135 std::uint64_t maxSize = iterator.streamSize() - iterator.currentCharacterOffset();
136 internalParse(iterator, maxSize, diag);
137}
138
149 void VorbisCommentField::parse(OggIterator &iterator, std::uint64_t &maxSize, Diagnostics &diag)
150{
151 internalParse(iterator, maxSize, diag);
152}
153
164 void VorbisCommentField::parse(istream &stream, std::uint64_t &maxSize, Diagnostics &diag)
165{
166 internalParse(stream, maxSize, diag);
167}
168
178 bool VorbisCommentField::make(BinaryWriter &writer, VorbisCommentFlags flags, Diagnostics &diag)
179{
180 static const string context("making Vorbis comment field");
181 if (id().empty()) {
182 diag.emplace_back(DiagLevel::Critical, "The field ID is empty.", context);
183 }
184 try {
185 // try to convert value to string
186 string valueString;
187 if (id() == VorbisCommentIds::cover()) {
188 if (flags & VorbisCommentFlags::NoCovers) {
189 return false;
190 }
191 // make cover
192 if (value().type() != TagDataType::Picture) {
193 diag.emplace_back(DiagLevel::Critical, "Assigned value of cover field is not picture data.", context);
194 throw InvalidDataException();
195 }
196 try {
197 FlacMetaDataBlockPicture pictureBlock(value());
198 pictureBlock.setPictureType(typeInfo());
199
200 const auto requiredSize = pictureBlock.requiredSize();
201 auto buffer = make_unique<char[]>(requiredSize);
202 stringstream bufferStream(ios_base::in | ios_base::out | ios_base::binary);
203 bufferStream.exceptions(ios_base::failbit | ios_base::badbit);
204#if defined(__GLIBCXX__) && !defined(_LIBCPP_VERSION)
205 bufferStream.rdbuf()->pubsetbuf(buffer.get(), requiredSize);
206#endif
207 pictureBlock.make(bufferStream);
208#if !(defined(__GLIBCXX__) && !defined(_LIBCPP_VERSION))
209 bufferStream.read(buffer.get(), static_cast<std::streamsize>(requiredSize));
210#endif
211 valueString = encodeBase64(reinterpret_cast<std::uint8_t *>(buffer.get()), requiredSize);
212
213 } catch (const Failure &) {
214 diag.emplace_back(DiagLevel::Critical, "Unable to make METADATA_BLOCK_PICTURE struct from the assigned value.", context);
215 throw;
216 } catch (const std::ios_base::failure &failure) {
217 diag.emplace_back(DiagLevel::Critical,
218 argsToString("An IO error occurred when writing the METADATA_BLOCK_PICTURE struct: ", failure.what()), context);
219 throw Failure();
220 }
221 } else if (value().type() == TagDataType::Popularity) {
222 valueString = value().toScaledPopularity(TagType::VorbisComment).toString();
223 } else {
224 // make normal string value
225 valueString = value().toString(TagTextEncoding::Utf8);
226 }
227 const auto size(valueString.size() + id().size() + 1);
228 if (size > numeric_limits<std::uint32_t>::max()) {
229 diag.emplace_back(DiagLevel::Critical, "Assigned value exceeds the maximum size.", context);
230 throw InvalidDataException();
231 }
232 writer.writeUInt32LE(static_cast<std::uint32_t>(size));
233 writer.writeString(id());
234 writer.writeChar('=');
235 writer.writeString(valueString);
236 } catch (const ConversionException &) {
237 diag.emplace_back(DiagLevel::Critical, "Assigned value can not be converted appropriately.", context);
238 throw InvalidDataException();
239 }
240 return true;
241}
242
243} // namespace TagParser
TagParser::Diagnostics
The Diagnostics class is a container for DiagMessage.
Definition diagnostics.h:156
TagParser::Failure
The class inherits from std::exception and serves as base class for exceptions thrown by the elements...
Definition exceptions.h:11
TagParser::FlacMetaDataBlockPicture
The FlacMetaDataBlockPicture class is a FLAC "METADATA_BLOCK_PICTURE" parser and maker.
Definition flacmetadata.h:248
TagParser::FlacMetaDataBlockPicture::requiredSize
std::uint32_t requiredSize() const
Returns the number of bytes make() will write.
Definition flacmetadata.cpp:112
TagParser::FlacMetaDataBlockPicture::make
void make(std::ostream &outputStream)
Makes the FLAC "METADATA_BLOCK_PICTURE".
Definition flacmetadata.cpp:125
TagParser::FlacMetaDataBlockPicture::setPictureType
void setPictureType(std::uint32_t pictureType)
Sets the picture type according to the ID3v2 APIC frame.
Definition flacmetadata.h:289
TagParser::InvalidDataException
The exception that is thrown when the data to be parsed or to be made seems invalid and therefore can...
Definition exceptions.h:25
TagParser::OggIterator
The OggIterator class helps iterating through all segments of an Ogg bitstream.
Definition oggiterator.h:11
TagParser::OggIterator::currentCharacterOffset
std::uint64_t currentCharacterOffset() const
Returns the offset of the current character in the input stream if the iterator is valid; otherwise a...
Definition oggiterator.h:222
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< VorbisCommentField >::TagField
TagField()
Definition generictagfield.h:84
TagParser::TagField< VorbisCommentField >::setTypeInfo
void setTypeInfo(const TypeInfoType &typeInfo)
Definition generictagfield.h:196
TagParser::TagField< VorbisCommentField >::IdentifierType
typename TagFieldTraits< VorbisCommentField >::IdentifierType IdentifierType
Definition generictagfield.h:33
TagParser::TagField< VorbisCommentField >::typeInfo
const TypeInfoType & typeInfo() const
Definition generictagfield.h:188
TagParser::TagField< VorbisCommentField >::id
IdentifierType & id()
Definition generictagfield.h:116
TagParser::TagField< VorbisCommentField >::setId
void setId(const IdentifierType &id)
Definition generictagfield.h:140
TagParser::TagField< VorbisCommentField >::value
TagValue & value()
Definition generictagfield.h:156
TagParser::TagValue
The TagValue class wraps values of different types.
Definition tagvalue.h:147
TagParser::TagValue::assignText
void assignText(const char *text, std::size_t textSize, TagTextEncoding textEncoding=TagTextEncoding::Latin1, TagTextEncoding convertTo=TagTextEncoding::Unspecified)
Assigns a copy of the given text.
Definition tagvalue.cpp:1209
TagParser::TagValue::assignPopularity
void assignPopularity(const Popularity &value)
Assigns the specified popularity value.
Definition tagvalue.cpp:1331
TagParser::TagValue::toScaledPopularity
Popularity toScaledPopularity(TagType scale=TagType::Unspecified) const
Converts the value of the current TagValue object to its equivalent Popularity representation using t...
Definition tagvalue.cpp:901
TagParser::TagValue::toString
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
TagParser::TruncatedDataException
The exception that is thrown when the data to be parsed is truncated and therefore can not be parsed ...
Definition exceptions.h:39
TagParser::VorbisCommentField::parse
void parse(OggIterator &iterator, Diagnostics &diag)
Parses a field using the specified iterator.
Definition vorbiscommentfield.cpp:133
TagParser::VorbisCommentField::VorbisCommentField
VorbisCommentField()
Constructs a new Vorbis comment field.
Definition vorbiscommentfield.cpp:33
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
diagnostics.h
exceptions.h
flacmetadata.h
CppUtilities
Definition abstractcontainer.h:16
TagParser::VorbisCommentIds::rating
constexpr TAG_PARSER_EXPORT std::string_view rating()
Definition vorbiscommentids.h:161
TagParser::VorbisCommentIds::cover
constexpr TAG_PARSER_EXPORT std::string_view cover()
Definition vorbiscommentids.h:201
TagParser
Contains all classes and functions of the TagInfo library.
Definition aaccodebook.h:10
TagParser::TagTextEncoding::Utf8
@ Utf8
Definition tagvalue.h:31
TagParser::TagType::VorbisComment
@ VorbisComment
Definition tagtype.h:17
TagParser::DiagLevel::Warning
@ Warning
Definition diagnostics.h:20
TagParser::DiagLevel::Critical
@ Critical
Definition diagnostics.h:21
TagParser::TagDataType::Picture
@ Picture
Definition tagvalue.h:126
TagParser::TagDataType::Popularity
@ Popularity
Definition tagvalue.h:129
TagParser::VorbisCommentFlags
VorbisCommentFlags
The VorbisCommentFlags enum specifies flags which controls parsing and making of Vorbis comments.
Definition vorbiscommentfield.h:18
TagParser::VorbisCommentFlags::NoCovers
@ NoCovers
Definition vorbiscommentfield.h:22
oggiterator.h
TagParser::Popularity::toString
std::string toString() const
Returns the popularity as string in the format "rating" if only a rating is present or in the format ...
Definition tagvalue.cpp:1503
vorbiscommentfield.h
vorbiscommentids.h