Tag Parser  6.2.2
C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
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 "../exceptions.h"
7 
8 #include <c++utilities/io/binaryreader.h>
9 #include <c++utilities/io/binarywriter.h>
10 #include <c++utilities/io/copy.h>
11 
12 #include <map>
13 #include <memory>
14 
15 using namespace std;
16 using namespace IoUtilities;
17 using namespace ConversionUtilities;
18 
19 namespace Media {
20 
26 const TagValue &VorbisComment::value(KnownField field) const
27 {
28  switch(field) {
29  case KnownField::Vendor:
30  return vendor();
31  default:
33  }
34 }
35 
36 bool VorbisComment::setValue(KnownField field, const TagValue &value)
37 {
38  switch(field) {
39  case KnownField::Vendor:
40  setVendor(value);
41  return true;
42  default:
44  }
45 }
46 
47 string VorbisComment::fieldId(KnownField field) const
48 {
49  using namespace VorbisCommentIds;
50  switch(field) {
51  case KnownField::Album: return album();
52  case KnownField::Artist: return artist();
53  case KnownField::Comment: return comment();
54  case KnownField::Cover: return cover();
55  case KnownField::Year: return date();
56  case KnownField::Title: return title();
57  case KnownField::Genre: return genre();
59  case KnownField::DiskPosition: return diskNumber();
60  case KnownField::PartNumber: return partNumber();
61  case KnownField::Composer: return composer();
62  case KnownField::Encoder: return encoder();
63  case KnownField::EncoderSettings: return encoderSettings();
64  case KnownField::Description: return description();
65  case KnownField::RecordLabel: return label();
66  case KnownField::Performers: return performer();
67  case KnownField::Language: return language();
68  case KnownField::Lyricist: return lyricist();
69  default: return string();
70  }
71 }
72 
73 KnownField VorbisComment::knownField(const string &id) const
74 {
75  using namespace VorbisCommentIds;
76  static const map<string, KnownField> fieldMap({
86  {partNumber(), KnownField::PartNumber},
89  {encoderSettings(), KnownField::EncoderSettings},
94  });
95  try {
96  return fieldMap.at(id);
97  } catch(out_of_range &) {
98  return KnownField::Invalid;
99  }
100 }
101 
105 template<class StreamType>
106 void VorbisComment::internalParse(StreamType &stream, uint64 maxSize, VorbisCommentFlags flags)
107 {
108  // prepare parsing
109  invalidateStatus();
110  static const string context("parsing Vorbis comment");
111  uint64 startOffset = static_cast<uint64>(stream.tellg());
112  try {
113  // read signature: 0x3 + "vorbis"
114  char sig[8];
115  bool skipSignature = flags & VorbisCommentFlags::NoSignature;
116  if(!skipSignature) {
117  CHECK_MAX_SIZE(7);
118  stream.read(sig, 7);
119  skipSignature = (ConversionUtilities::BE::toUInt64(sig) & 0xffffffffffffff00u) == 0x03766F7262697300u;
120  }
121  if(skipSignature) {
122  // read vendor (length prefixed string)
123  {
124  CHECK_MAX_SIZE(4);
125  stream.read(sig, 4);
126  const auto vendorSize = LE::toUInt32(sig);
127  if(vendorSize <= maxSize) {
128  auto buff = make_unique<char []>(vendorSize);
129  stream.read(buff.get(), vendorSize);
130  m_vendor.assignData(move(buff), vendorSize, TagDataType::Text, TagTextEncoding::Utf8);
131  // TODO: Is the vendor string actually UTF-8 (like the field values)?
132  } else {
133  addNotification(NotificationType::Critical, "Vendor information is truncated.", context);
134  throw TruncatedDataException();
135  }
136  maxSize -= vendorSize;
137  }
138  // read field count
139  CHECK_MAX_SIZE(4);
140  stream.read(sig, 4);
141  uint32 fieldCount = LE::toUInt32(sig);
142  VorbisCommentField field;
143  const string &fieldId = field.id();
144  for(uint32 i = 0; i < fieldCount; ++i) {
145  // read fields
146  try {
147  field.parse(stream, maxSize);
148  fields().insert(pair<fieldType::identifierType, fieldType>(fieldId, field));
149  } catch(const TruncatedDataException &) {
150  addNotifications(field);
151  throw;
152  } catch(const Failure &) {
153  // nothing to do here since notifications will be added anyways
154  }
155  addNotifications(field);
156  field.invalidateNotifications();
157  }
158  if(!(flags & VorbisCommentFlags::NoFramingByte)) {
159  stream.ignore(); // skip framing byte
160  }
161  m_size = static_cast<uint32>(static_cast<uint64>(stream.tellg()) - startOffset);
162  } else {
163  addNotification(NotificationType::Critical, "Signature is invalid.", context);
164  throw InvalidDataException();
165  }
166  } catch(const TruncatedDataException &) {
167  m_size = static_cast<uint32>(static_cast<uint64>(stream.tellg()) - startOffset);
168  addNotification(NotificationType::Critical, "Vorbis comment is truncated.", context);
169  throw;
170  }
171 }
172 
180 void VorbisComment::parse(OggIterator &iterator, VorbisCommentFlags flags)
181 {
182  internalParse(iterator, iterator.streamSize(), flags);
183 }
184 
192 void VorbisComment::parse(istream &stream, uint64 maxSize, VorbisCommentFlags flags)
193 {
194  internalParse(stream, maxSize, flags);
195 }
196 
204 void VorbisComment::make(std::ostream &stream, VorbisCommentFlags flags)
205 {
206  // prepare making
207  invalidateStatus();
208  static const string context("making Vorbis comment");
209  string vendor;
210  try {
211  m_vendor.toString(vendor);
212  } catch(const ConversionException &) {
213  addNotification(NotificationType::Warning, "Can not convert the assigned vendor to string.", context);
214  }
215  BinaryWriter writer(&stream);
216  if(!(flags & VorbisCommentFlags::NoSignature)) {
217  // write signature
218  static const char sig[7] = {0x03, 0x76, 0x6F, 0x72, 0x62, 0x69, 0x73};
219  stream.write(sig, sizeof(sig));
220  }
221  // write vendor
222  writer.writeUInt32LE(vendor.size());
223  writer.writeString(vendor);
224  // write field count later
225  const auto fieldCountOffset = stream.tellp();
226  writer.writeUInt32LE(0);
227  // write fields
228  uint32 fieldsWritten = 0;
229  for(auto i : fields()) {
230  VorbisCommentField &field = i.second;
231  if(!field.value().isEmpty()) {
232  try {
233  if(field.make(writer, flags)) {
234  ++fieldsWritten;
235  }
236  } catch(const Failure &) {
237  // nothing to do here since notifications will be added anyways
238  }
239  // add making notifications
240  addNotifications(context, field);
241  field.invalidateNotifications();
242  }
243  }
244  // write field count
245  const auto framingByteOffset = stream.tellp();
246  stream.seekp(fieldCountOffset);
247  writer.writeUInt32LE(fieldsWritten);
248  stream.seekp(framingByteOffset);
249  // write framing byte
250  if(!(flags & VorbisCommentFlags::NoFramingByte)) {
251  stream.put(0x01);
252  }
253 }
254 
255 }
The TagValue class wraps values of different types.
Definition: tagvalue.h:63
bool make(IoUtilities::BinaryWriter &writer, VorbisCommentFlags flags=VorbisCommentFlags::None)
Writes the field to a stream using the specified writer.
TAG_PARSER_EXPORT const char * trackNumber()
TAG_PARSER_EXPORT const char * language()
bool isEmpty() const
Returns an indication whether an value is assigned.
Definition: tagvalue.h:340
TAG_PARSER_EXPORT const char * cover()
void parse(OggIterator &iterator)
Parses a field using the specified iterator.
KnownField
Specifies the field.
Definition: tag.h:42
TAG_PARSER_EXPORT const char * encoder()
STL namespace.
const identifierType & id() const
Returns the id of the current TagField.
TAG_PARSER_EXPORT const char * encoderSettings()
TAG_PARSER_EXPORT const char * title()
TAG_PARSER_EXPORT const char * artist()
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
TAG_PARSER_EXPORT const char * diskNumber()
TAG_PARSER_EXPORT const char * date()
TagValue & value()
Returns the value 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
TAG_PARSER_EXPORT const char * album()
The FieldMapBasedTag provides a generic implementation of Tag which stores the tag fields using std::...
Definition: fieldbasedtag.h:25
TAG_PARSER_EXPORT const char * composer()
The exception that is thrown when the data to be parsed is truncated and therefore can not be parsed ...
Definition: exceptions.h:35
void invalidateNotifications()
Invalidates the object&#39;s notifications.
TAG_PARSER_EXPORT const char * description()
uint64 streamSize() const
Returns the stream size (which has been specified when constructing the iterator).
Definition: oggiterator.h:112
TAG_PARSER_EXPORT const char * partNumber()
The OggIterator class helps iterating through all segments of an OGG bitstream.
Definition: oggiterator.h:11
TAG_PARSER_EXPORT const char * lyricist()
Contains all classes and functions of the TagInfo library.
Definition: exceptions.h:9
The VorbisCommentField class is used by VorbisComment to store the fields.
TAG_PARSER_EXPORT const char * comment()
TAG_PARSER_EXPORT const char * genre()
TAG_PARSER_EXPORT const char * performer()
#define CHECK_MAX_SIZE(sizeDenotation)
Throws TruncatedDataException() if the specified sizeDenotation exceeds maxSize; otherwise maxSize is...
Definition: exceptions.h:70
VorbisCommentFlags
The VorbisCommentFlags enum specifies flags which controls parsing and making of Vorbis comments...