Tag Parser  6.5.0
C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
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 "../id3/id3v2frame.h"
9 
10 #include "../exceptions.h"
11 
12 #include <c++utilities/io/binaryreader.h>
13 #include <c++utilities/io/binarywriter.h>
14 #include <c++utilities/io/catchiofailure.h>
15 #include <c++utilities/conversion/binaryconversion.h>
16 #include <c++utilities/conversion/stringconversion.h>
17 
18 #include <iostream>
19 #include <memory>
20 
21 using namespace std;
22 using namespace IoUtilities;
23 using namespace ConversionUtilities;
24 
25 namespace Media {
26 
35 VorbisCommentField::VorbisCommentField()
36 {}
37 
41 VorbisCommentField::VorbisCommentField(const identifierType &id, const TagValue &value) :
42  TagField<VorbisCommentField>(id, value)
43 {}
44 
48 template<class StreamType>
49 void VorbisCommentField::internalParse(StreamType &stream, uint64 &maxSize)
50 {
51  static const string context("parsing Vorbis comment field");
52  char buff[4];
53  if(maxSize < 4) {
54  addNotification(NotificationType::Critical, "Field expected.", context);
55  throw TruncatedDataException();
56  } else {
57  maxSize -= 4;
58  }
59  stream.read(buff, 4);
60  if(const auto size = LE::toUInt32(buff)) { // read size
61  if(size <= maxSize) {
62  maxSize -= size;
63  // read data
64  auto data = make_unique<char []>(size);
65  stream.read(data.get(), size);
66  uint32 idSize = 0;
67  for(const char *i = data.get(), *end = data.get() + size; i != end && *i != '='; ++i, ++idSize);
68  // extract id
69  setId(string(data.get(), idSize));
70  if(!idSize) {
71  // empty field ID
72  addNotification(NotificationType::Critical, "The field ID is empty.", context);
73  throw InvalidDataException();
74  } else if(id() == VorbisCommentIds::cover()) {
75  // extract cover value
76  try {
77  auto decoded = decodeBase64(data.get() + idSize + 1, size - idSize - 1);
78  stringstream bufferStream(ios_base::in | ios_base::out | ios_base::binary);
79  bufferStream.exceptions(ios_base::failbit | ios_base::badbit);
80  bufferStream.rdbuf()->pubsetbuf(reinterpret_cast<char *>(decoded.first.get()), decoded.second);
81  FlacMetaDataBlockPicture pictureBlock(value());
82  pictureBlock.parse(bufferStream, decoded.second);
83  setTypeInfo(pictureBlock.pictureType());
84  } catch(const TruncatedDataException &) {
85  addNotification(NotificationType::Critical, "METADATA_BLOCK_PICTURE is truncated.", context);
86  throw;
87  } catch(const ConversionException &) {
88  addNotification(NotificationType::Critical, "Base64 coding of METADATA_BLOCK_PICTURE is invalid.", context);
89  throw InvalidDataException();
90  } catch(...) {
91  catchIoFailure();
92  addNotification(NotificationType::Critical, "An IO error occured when reading the METADATA_BLOCK_PICTURE struct.", context);
93  throw Failure();
94  }
95  } else if(id().size() + 1 < size) {
96  // extract other values (as string)
97  setValue(TagValue(string(data.get() + idSize + 1, size - idSize - 1), TagTextEncoding::Utf8));
98  }
99  } else {
100  addNotification(NotificationType::Critical, "Field is truncated.", context);
101  throw TruncatedDataException();
102  }
103  }
104 }
105 
117 {
118  uint64 maxSize = iterator.streamSize() - iterator.currentCharacterOffset();
119  internalParse(iterator, maxSize);
120 }
121 
132 void VorbisCommentField::parse(OggIterator &iterator, uint64 &maxSize)
133 {
134  internalParse(iterator, maxSize);
135 }
136 
147 void VorbisCommentField::parse(istream &stream, uint64 &maxSize)
148 {
149  internalParse(stream, maxSize);
150 }
151 
161 bool VorbisCommentField::make(BinaryWriter &writer, VorbisCommentFlags flags)
162 {
163  static const string context("making Vorbis comment field");
164  if(id().empty()) {
165  addNotification(NotificationType::Critical, "The field ID is empty.", context);
166  }
167  try {
168  // try to convert value to string
169  string valueString;
170  if(id() == VorbisCommentIds::cover()) {
171  if(flags & VorbisCommentFlags::NoCovers) {
172  return false;
173  }
174  // make cover
175  if(value().type() != TagDataType::Picture) {
176  addNotification(NotificationType::Critical, "Assigned value of cover field is not picture data.", context);
177  throw InvalidDataException();
178  }
179  try {
180  FlacMetaDataBlockPicture pictureBlock(value());
181  pictureBlock.setPictureType(typeInfo());
182 
183  const auto requiredSize = pictureBlock.requiredSize();
184  auto buffer = make_unique<char[]>(requiredSize);
185  stringstream bufferStream(ios_base::in | ios_base::out | ios_base::binary);
186  bufferStream.exceptions(ios_base::failbit | ios_base::badbit);
187  bufferStream.rdbuf()->pubsetbuf(buffer.get(), requiredSize);
188 
189  pictureBlock.make(bufferStream);
190  valueString = encodeBase64(reinterpret_cast<byte *>(buffer.get()), requiredSize);
191  } catch(...) {
192  catchIoFailure();
193  addNotification(NotificationType::Critical, "An IO error occured when writing the METADATA_BLOCK_PICTURE struct.", context);
194  throw Failure();
195  }
196  } else {
197  // make normal string value
198  valueString = value().toString();
199  }
200  writer.writeUInt32LE(id().size() + 1 + valueString.size());
201  writer.writeString(id());
202  writer.writeChar('=');
203  writer.writeString(valueString);
204  } catch(const ConversionException &) {
205  addNotification(NotificationType::Critical, "Assigned value can not be converted appropriately.", context);
206  throw InvalidDataException();
207  }
208  return true;
209 }
210 
211 }
The TagValue class wraps values of different types.
Definition: tagvalue.h:64
bool make(IoUtilities::BinaryWriter &writer, VorbisCommentFlags flags=VorbisCommentFlags::None)
Writes the field to a stream using the specified writer.
void setTypeInfo(const typeInfoType &typeInfo)
Sets the type info of the current TagField.
void setPictureType(uint32 pictureType)
Sets the picture type according to the ID3v2 APIC frame.
Definition: flacmetadata.h:299
TAG_PARSER_EXPORT const char * cover()
void setValue(const TagValue &value)
Sets the value of the current TagField.
void parse(OggIterator &iterator)
Parses a field using the specified iterator.
STL namespace.
void addNotification(const Notification &notification)
This method is meant to be called by the derived class to add a notification.
void parse(std::istream &inputStream, uint32 maxSize)
Parses the FLAC "METADATA_BLOCK_PICTURE".
uint64 currentCharacterOffset() const
Returns the offset of the current character in the input stream if the iterator is valid; otherwise a...
Definition: oggiterator.h:211
void make(std::ostream &outputStream)
Makes the FLAC "METADATA_BLOCK_PICTURE".
Contains utility classes helping to read and write streams.
The exception that is thrown when the data to be parsed or to be made seems invalid and therefore can...
Definition: exceptions.h:27
TagValue & value()
Returns the value of the current TagField.
const typeInfoType & typeInfo() const
Returns the type info of the current TagField.
The class inherits from std::exception and serves as base class for exceptions thrown by the elements...
Definition: exceptions.h:11
The TagField class is used by FieldMapBasedTag to store the fields.
uint32 requiredSize() const
Returns the number of bytes make() will write.
The exception that is thrown when the data to be parsed is truncated and therefore can not be parsed ...
Definition: exceptions.h:35
uint32 pictureType() const
Returns the picture type according to the ID3v2 APIC frame.
Definition: flacmetadata.h:291
uint64 streamSize() const
Returns the stream size (which has been specified when constructing the iterator).
Definition: oggiterator.h:114
The OggIterator class helps iterating through all segments of an OGG bitstream.
Definition: oggiterator.h:11
Contains all classes and functions of the TagInfo library.
Definition: exceptions.h:9
The VorbisCommentField class is used by VorbisComment to store the fields.
The FlacMetaDataBlockPicture class is a FLAC "METADATA_BLOCK_PICTURE" parser and maker.
Definition: flacmetadata.h:258
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:320
void setId(const identifierType &id)
Sets the id of the current Tag Field.
VorbisCommentFlags
The VorbisCommentFlags enum specifies flags which controls parsing and making of Vorbis comments...