Tag Parser  6.3.0
C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
flacstream.cpp
Go to the documentation of this file.
1 #include "./flacstream.h"
2 #include "./flacmetadata.h"
3 
4 #include "../vorbis/vorbiscomment.h"
5 
6 #include "../exceptions.h"
7 #include "../mediafileinfo.h"
8 #include "../mediaformat.h"
9 
10 #include "resources/config.h"
11 
12 #include <c++utilities/io/copy.h>
13 
14 #include <sstream>
15 
16 using namespace std;
17 using namespace IoUtilities;
18 using namespace ConversionUtilities;
19 using namespace ChronoUtilities;
20 
21 namespace Media {
22 
33 FlacStream::FlacStream(MediaFileInfo &mediaFileInfo, uint64 startOffset) :
34  AbstractTrack(mediaFileInfo.stream(), startOffset),
35  m_mediaFileInfo(mediaFileInfo),
36  m_paddingSize(0),
37  m_streamOffset(0)
38 {
40 }
41 
47 {
48  if(!m_vorbisComment) {
49  m_vorbisComment = make_unique<VorbisComment>();
50  }
51  return m_vorbisComment.get();
52 }
53 
59 {
60  if(m_vorbisComment) {
61  m_vorbisComment.reset();
62  return true;
63  } else {
64  return false;
65  }
66 }
67 
69 {
70  static const string context("parsing raw FLAC header");
71  if(!m_istream) {
72  throw NoDataFoundException();
73  }
74 
75  m_istream->seekg(m_startOffset, ios_base::beg);
76  char buffer[0x22];
77 
78  // check signature
79  if(m_reader.readUInt32BE() == 0x664C6143) {
81 
82  // parse meta data blocks
83  for(FlacMetaDataBlockHeader header; !header.isLast(); ) {
84  // parse block header
85  m_istream->read(buffer, 4);
86  header.parseHeader(buffer);
87 
88  // remember start offset
89  const auto startOffset = m_istream->tellg();
90 
91  // parse relevant meta data
92  switch(static_cast<FlacMetaDataBlockType>(header.type())) {
94  if(header.dataSize() >= 0x22) {
95  m_istream->read(buffer, 0x22);
96  FlacMetaDataBlockStreamInfo streamInfo;
97  streamInfo.parse(buffer);
98  m_channelCount = streamInfo.channelCount();
100  m_sampleCount = streamInfo.totalSampleCount();
101  m_bitsPerSample = streamInfo.bitsPerSample();
102  m_duration = TimeSpan::fromSeconds(static_cast<double>(m_sampleCount) / m_samplingFrequency);
103  } else {
104  addNotification(NotificationType::Critical, "\"METADATA_BLOCK_STREAMINFO\" is truncated and will be ignored.", context);
105  }
106  break;
107 
109  // parse Vorbis comment
110  // if more than one comment exist, simply thread those comments as one
111  if(!m_vorbisComment) {
112  m_vorbisComment = make_unique<VorbisComment>();
113  }
114  try {
115  m_vorbisComment->parse(*m_istream, header.dataSize(), VorbisCommentFlags::NoSignature | VorbisCommentFlags::NoFramingByte);
116  } catch(const Failure &) {
117  // error is logged via notifications, just continue with the next metadata block
118  }
119  break;
120 
122  try {
123  // parse the cover
124  VorbisCommentField coverField;
125  coverField.setId(m_vorbisComment->fieldId(KnownField::Cover));
126  FlacMetaDataBlockPicture picture(coverField.value());
127  picture.parse(*m_istream, header.dataSize());
128  coverField.setTypeInfo(picture.pictureType());
129 
130  if(coverField.value().isEmpty()) {
131  addNotification(NotificationType::Warning, "\"METADATA_BLOCK_PICTURE\" contains no picture.", context);
132  } else {
133  // add the cover to the Vorbis comment
134  if(!m_vorbisComment) {
135  // create one if none exists yet
136  m_vorbisComment = make_unique<VorbisComment>();
137  m_vorbisComment->setVendor(TagValue(APP_NAME " v" APP_VERSION, TagTextEncoding::Utf8));
138  }
139  m_vorbisComment->fields().insert(make_pair(coverField.id(), move(coverField)));
140  }
141 
142  } catch(const TruncatedDataException &) {
143  addNotification(NotificationType::Critical, "\"METADATA_BLOCK_PICTURE\" is truncated and will be ignored.", context);
144  }
145  break;
146 
148  m_paddingSize += 4 + header.dataSize();
149  break;
150 
151  default:
152  ;
153  }
154 
155  // seek to next block
156  m_istream->seekg(startOffset + static_cast<decltype(startOffset)>(header.dataSize()));
157 
158  // TODO: check first FLAC frame
159  }
160 
161  m_streamOffset = m_istream->tellg();
162 
163  } else {
164  addNotification(NotificationType::Critical, "Signature (fLaC) not found.", context);
165  throw InvalidDataException();
166  }
167 }
168 
181 {
182  istream &originalStream = m_mediaFileInfo.stream();
183  originalStream.seekg(m_startOffset + 4);
184  CopyHelper<512> copy;
185 
186  // write signature
187  BE::getBytes(static_cast<uint32>(0x664C6143u), copy.buffer());
188  outputStream.write(copy.buffer(), 4);
189 
190  uint32 lastStartOffset = 0;
191 
192  // write meta data blocks which don't need to be adjusted
193  for(FlacMetaDataBlockHeader header; !header.isLast(); ) {
194  // parse block header
195  m_istream->read(copy.buffer(), 4);
196  header.parseHeader(copy.buffer());
197 
198  // parse relevant meta data
199  switch(static_cast<FlacMetaDataBlockType>(header.type())) {
203  m_istream->seekg(header.dataSize(), ios_base::cur);
204  break; // written separately/ignored
205  default:
206  m_istream->seekg(-4, ios_base::cur);
207  lastStartOffset = outputStream.tellp();
208  copy.copy(originalStream, outputStream, 4 + header.dataSize());
209  }
210  }
211 
212  // write Vorbis comment
213  if(m_vorbisComment) {
214  // leave 4 bytes space for the "METADATA_BLOCK_HEADER"
215  lastStartOffset = outputStream.tellp();
216  outputStream.write(copy.buffer(), 4);
217 
218  // determine cover ID since covers must be written separately
219  const auto coverId = m_vorbisComment->fieldId(KnownField::Cover);
220 
221  // write Vorbis comment
223 
224  // write "METADATA_BLOCK_HEADER"
225  const uint32 endOffset = outputStream.tellp();
228  header.setDataSize(endOffset - lastStartOffset - 4);
229  header.setLast(!m_vorbisComment->hasField(coverId));
230  outputStream.seekp(lastStartOffset);
231  header.makeHeader(outputStream);
232  outputStream.seekp(endOffset);
233 
234  // write cover fields separately as "METADATA_BLOCK_PICTURE"
235  if(!header.isLast()) {
237  const auto coverFields = m_vorbisComment->fields().equal_range(coverId);
238  for(auto i = coverFields.first; i != coverFields.second; ) {
239  lastStartOffset = outputStream.tellp();
240  FlacMetaDataBlockPicture pictureBlock(i->second.value());
241  pictureBlock.setPictureType(i->second.typeInfo());
242  header.setDataSize(pictureBlock.requiredSize());
243  header.setLast(++i == coverFields.second);
244  header.makeHeader(outputStream);
245  pictureBlock.make(outputStream);
246  }
247  }
248  }
249 
250  return lastStartOffset;
251 }
252 
257 void FlacStream::makePadding(ostream &stream, uint32 size, bool isLast)
258 {
259  // make header
262  header.setLast(isLast);
263  header.setDataSize(size -= 4);
264  header.makeHeader(stream);
265 
266  // write zeroes
267  for(; size; --size) {
268  stream.put(0);
269  }
270 }
271 
272 }
The TagValue class wraps values of different types.
Definition: tagvalue.h:63
std::ostream & outputStream()
Returns the associated output stream.
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
Implementation of Media::Tag for Vorbis comments.
Definition: vorbiscomment.h:15
bool isEmpty() const
Returns an indication whether an value is assigned.
Definition: tagvalue.h:340
void internalParseHeader()
This method is internally called to parse header information.
Definition: flacstream.cpp:68
byte isLast() const
Returns whether this is the last metadata block before the audio blocks.
Definition: flacmetadata.h:72
The FlacMetaDataBlockHeader class is a FLAC "METADATA_BLOCK_HEADER" parser and maker.
Definition: flacmetadata.h:38
The AbstractTrack class parses and stores technical information about video, audio and other kinds of...
Definition: abstracttrack.h:40
void parse(const char *buffer)
Parses the FLAC "METADATA_BLOCK_STREAMINFO" which is read using the specified iterator.
STL namespace.
static void makePadding(std::ostream &stream, uint32 size, bool isLast)
Writes padding of the specified size to the specified stream.
Definition: flacstream.cpp:257
IoUtilities::BinaryReader m_reader
void addNotification(const Notification &notification)
This protected method is meant to be called by the derived class to add a notification.
const identifierType & id() const
Returns the id of the current TagField.
void parse(std::istream &inputStream, uint32 maxSize)
Parses the FLAC "METADATA_BLOCK_PICTURE".
The FlacMetaDataBlockStreamInfo class is a FLAC "METADATA_BLOCK_STREAMINFO" parser.
Definition: flacmetadata.h:119
VorbisComment * createVorbisComment()
Creates a new Vorbis comment for the stream.
Definition: flacstream.cpp:46
uint32 makeHeader(std::ostream &stream)
Writes the FLAC metadata header to the specified outputStream.
Definition: flacstream.cpp:180
IoUtilities::NativeFileStream & stream()
Returns the std::fstream for the current instance.
Definition: basicfileinfo.h:80
Contains utility classes helping to read and write streams.
void makeHeader(std::ostream &outputStream)
Writes the header to the specified outputStream.
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.
byte bitsPerSample() const
Returns the bits per sample.
Definition: flacmetadata.h:229
The class inherits from std::exception and serves as base class for exceptions thrown by the elements...
Definition: exceptions.h:11
uint64 startOffset() const
Returns the start offset of the track in the associated stream.
The exception that is thrown when the data to be parsed is truncated and therefore can not be parsed ...
Definition: exceptions.h:35
uint64 totalSampleCount() const
Returns the total samples in stream.
Definition: flacmetadata.h:242
The MediaFileInfo class allows to read and write tag information providing a container/tag format ind...
Definition: mediafileinfo.h:52
ChronoUtilities::TimeSpan m_duration
byte channelCount() const
Returns the number of channels.
Definition: flacmetadata.h:217
void setLast(byte last)
Sets whether this is the last metadata block before the audio blocks.
Definition: flacmetadata.h:80
The exception that is thrown when the data to be parsed holds no parsable information.
Definition: exceptions.h:19
Contains all classes and functions of the TagInfo library.
Definition: exceptions.h:9
uint32 samplingFrequency() const
Returns the sampling frequency in Hz.
Definition: flacmetadata.h:207
The VorbisCommentField class is used by VorbisComment to store the fields.
uint64 size() const
Returns the size in bytes if known; otherwise returns 0.
std::istream * m_istream
bool removeVorbisComment()
Removes the assigned Vorbis comment if one is assigned; does nothing otherwise.
Definition: flacstream.cpp:58
The FlacMetaDataBlockPicture class is a FLAC "METADATA_BLOCK_PICTURE" parser and maker.
Definition: flacmetadata.h:258
void setId(const identifierType &id)
Sets the id of the current Tag Field.
void setType(FlacMetaDataBlockType type)
Sets the block type.
Definition: flacmetadata.h:97
void setDataSize(uint32 dataSize)
Sets the length in bytes of the meta data (excluding the size of the header itself).
Definition: flacmetadata.h:114