Tag Parser  8.2.0
C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
matroskatrack.cpp
Go to the documentation of this file.
1 #include "./matroskatrack.h"
2 #include "./ebmlelement.h"
3 #include "./matroskacontainer.h"
4 #include "./matroskaid.h"
5 #include "./matroskatag.h"
6 
7 #include "../avi/bitmapinfoheader.h"
8 
9 #include "../wav/waveaudiostream.h"
10 
11 #include "../avc/avcconfiguration.h"
12 
13 #include "../mp4/mp4ids.h"
14 #include "../mp4/mp4track.h"
15 
16 #include "../exceptions.h"
17 #include "../mediaformat.h"
18 
19 #include <c++utilities/conversion/stringconversion.h>
20 
21 using namespace std;
22 using namespace ConversionUtilities;
23 
24 namespace TagParser {
25 
37 MatroskaTrack::MatroskaTrack(EbmlElement &trackElement)
38  : AbstractTrack(trackElement.stream(), trackElement.startOffset())
39  , m_trackElement(&trackElement)
40 {
41 }
42 
47 {
48 }
49 
51 {
53 }
54 
59 {
60  auto parts = splitString<vector<string>>(codecId, "/", EmptyPartsTreat::Keep, 3);
61  parts.resize(3);
62  const auto &part1 = parts[0], &part2 = parts[1], &part3 = parts[2];
63  MediaFormat fmt;
64  if (part1 == "V_MS" && part2 == "VFW" && part3 == "FOURCC") {
66  } else if (part1 == "V_UNCOMPRESSED") {
68  } else if (part1 == "V_MPEG4") {
70  if (part2 == "ISO") {
71  if (part3 == "SP") {
73  } else if (part3 == "ASP") {
75  } else if (part3 == "AVC") {
77  }
78  } else if (part2 == "MS" && part3 == "V3") {
80  }
81  } else if (part1 == "V_MPEG1") {
83  } else if (part1 == "V_MPEG2") {
85  } else if (part1 == "V_REAL") {
87  } else if (part1 == "V_QUICKTIME") {
89  } else if (part1 == "V_THEORA") {
91  } else if (part1 == "V_PRORES") {
93  } else if (part1 == "V_VP8") {
95  } else if (part1 == "V_VP9") {
97  } else if (part1 == "V_AV1") {
99  } else if (part1 == "A_MPEG") {
101  if (part2 == "L1") {
103  } else if (part2 == "L2") {
105  } else if (part2 == "L3") {
107  }
108  } else if (part1 == "V_MPEGH" && part2 == "ISO" && part3 == "HEVC") {
110  } else if (part1 == "A_PCM") {
112  if (part2 == "INT") {
113  if (part3 == "BIG") {
115  } else if (part3 == "LIT") {
117  }
118  } else if (part2 == "FLOAT" && part3 == "IEEE") {
120  }
121  } else if (part1 == "A_MPC") {
123  } else if (part1 == "A_AC3") {
125  } else if (part1 == "A_ALAC") {
127  } else if (part1 == "A_DTS") {
129  if (part2 == "EXPRESS") {
131  } else if (part2 == "LOSSLESS") {
133  }
134  } else if (part1 == "A_VORBIS") {
136  } else if (part1 == "A_FLAC") {
138  } else if (part1 == "A_OPUS") {
140  } else if (part1 == "A_REAL") {
142  } else if (part1 == "A_MS" && part2 == "ACM") {
144  } else if (part1 == "A_AAC") {
146  if (part2 == "MPEG2") {
147  if (part3 == "MAIN") {
149  } else if (part3 == "LC") {
151  } else if (part3 == "SBR") {
154  } else if (part3 == "SSR") {
156  }
157  } else if (part2 == "MPEG4") {
158  if (part3 == "MAIN") {
160  } else if (part3 == "LC") {
162  } else if (part3 == "SBR") {
165  } else if (part3 == "SSR") {
167  } else if (part3 == "LTP") {
169  }
170  }
171  } else if (part1 == "A_QUICKTIME") {
173  } else if (part1 == "A_TTA1") {
175  } else if (part1 == "A_WAVPACK4") {
177  } else if (part1 == "S_TEXT") {
179  if (part2 == "UTF8") {
181  } else if (part2 == "SSA") {
183  } else if (part2 == "ASS") {
185  } else if (part2 == "USF") {
187  } else if (part2 == "WEBVTT") {
189  }
190  } else if (part1 == "S_IMAGE") {
192  if (part2 == "BMP") {
194  }
195  } else if (part1 == "S_VOBSUB") {
197  } else if (part1 == "S_KATE") {
199  } else if (part1 == "B_VOBBTN") {
201  } else if (part1 == "S_DVBSUB") {
203  } else if (part1 == "V_MSWMV") {
205  }
206  return fmt;
207 }
208 
210 
211 template <typename PropertyType, typename ConversionFunction>
212 void MatroskaTrack::assignPropertyFromTagValue(const std::unique_ptr<MatroskaTag> &tag, const char *fieldId, PropertyType &property,
213  const ConversionFunction &conversionFunction, Diagnostics &diag)
214 {
215  const TagValue &value = tag->value(fieldId);
216  if (!value.isEmpty()) {
217  try {
218  property = conversionFunction(value);
219  } catch (const ConversionException &) {
220  string message;
221  try {
222  message = argsToString("Ignoring invalid value \"", value.toString(TagTextEncoding::Utf8), "\" of \"", fieldId, '\"', '.');
223  } catch (const ConversionException &) {
224  message = argsToString("Ignoring invalid value of \"", fieldId, '\"', '.');
225  }
226  diag.emplace_back(DiagLevel::Warning, message, argsToString("reading track statatistic from \"", tag->toString(), '\"'));
227  }
228  }
229 }
230 
231 template <typename NumberType, Traits::EnableIf<std::is_integral<NumberType>> * = nullptr> NumberType tagValueToNumber(const TagValue &tagValue)
232 {
233  // optimization for Latin1/UTF-8 strings
234  if (tagValue.type() == TagDataType::Text) {
235  switch (tagValue.dataEncoding()) {
238  return bufferToNumber<NumberType>(tagValue.dataPointer(), tagValue.dataSize());
239  default:;
240  }
241  }
242  // generic conversion
243  return stringToNumber<NumberType>(tagValue.toString(TagTextEncoding::Utf8));
244 }
245 
246 template <typename NumberType, Traits::EnableIf<std::is_floating_point<NumberType>> * = nullptr>
247 NumberType tagValueToBitrate(const TagValue &tagValue)
248 {
249  return stringToNumber<NumberType>(tagValue.toString(TagTextEncoding::Utf8)) / 1000;
250 }
251 
253 
263 void MatroskaTrack::readStatisticsFromTags(const std::vector<std::unique_ptr<MatroskaTag>> &tags, Diagnostics &diag)
264 {
265  using namespace std::placeholders;
266  using namespace MatroskaTagIds::TrackSpecific;
267  for (const auto &tag : tags) {
268  const TagTarget &target = tag->target();
269  if (find(target.tracks().cbegin(), target.tracks().cend(), id()) == target.tracks().cend()) {
270  continue;
271  }
272  assignPropertyFromTagValue(tag, numberOfBytes(), m_size, &tagValueToNumber<uint64>, diag);
273  assignPropertyFromTagValue(tag, numberOfFrames(), m_sampleCount, &tagValueToNumber<uint64>, diag);
274  assignPropertyFromTagValue(tag, MatroskaTagIds::TrackSpecific::duration(), m_duration, bind(&TagValue::toTimeSpan, _1), diag);
275  assignPropertyFromTagValue(tag, MatroskaTagIds::TrackSpecific::bitrate(), m_bitrate, &tagValueToBitrate<double>, diag);
276  assignPropertyFromTagValue(tag, writingDate(), m_modificationTime, bind(&TagValue::toDateTime, _1), diag);
277  if (m_creationTime.isNull()) {
279  }
280  }
281 }
282 
284 {
285  static const string context("parsing header of Matroska track");
286  try {
287  m_trackElement->parse(diag);
288  } catch (const Failure &) {
289  diag.emplace_back(DiagLevel::Critical, "Unable to parse track element.", context);
290  throw;
291  }
292  // read information about the track from the childs of the track entry element
293  for (EbmlElement *trackInfoElement = m_trackElement->firstChild(), *subElement = nullptr; trackInfoElement;
294  trackInfoElement = trackInfoElement->nextSibling()) {
295  try {
296  trackInfoElement->parse(diag);
297  } catch (const Failure &) {
298  diag.emplace_back(DiagLevel::Critical, "Unable to parse track information element.", context);
299  break;
300  }
301  uint32 defaultDuration = 0;
302  switch (trackInfoElement->id()) {
304  switch (trackInfoElement->readUInteger()) {
307  break;
310  break;
313  break;
316  break;
319  break;
320  default:
322  }
323  break;
325  for (subElement = trackInfoElement->firstChild(); subElement; subElement = subElement->nextSibling()) {
326  try {
327  subElement->parse(diag);
328  } catch (const Failure &) {
329  diag.emplace_back(DiagLevel::Critical, "Unable to parse video track element.", context);
330  break;
331  }
332  switch (subElement->id()) {
334  m_displaySize.setWidth(subElement->readUInteger());
335  break;
337  m_displaySize.setHeight(subElement->readUInteger());
338  break;
340  m_pixelSize.setWidth(subElement->readUInteger());
341  break;
343  m_pixelSize.setHeight(subElement->readUInteger());
344  break;
346  m_cropping.setTop(subElement->readUInteger());
347  break;
349  m_cropping.setLeft(subElement->readUInteger());
350  break;
352  m_cropping.setBottom(subElement->readUInteger());
353  break;
355  m_cropping.setRight(subElement->readUInteger());
356  break;
358  m_fps = subElement->readFloat();
359  break;
361  m_interlaced = subElement->readUInteger();
362  break;
364  m_colorSpace = subElement->readUInteger();
365  break;
366  default:;
367  }
368  }
369  break;
371  for (subElement = trackInfoElement->firstChild(); subElement; subElement = subElement->nextSibling()) {
372  try {
373  subElement->parse(diag);
374  } catch (const Failure &) {
375  diag.emplace_back(DiagLevel::Critical, "Unable to parse audio track element.", context);
376  break;
377  }
378  switch (subElement->id()) {
380  m_bitsPerSample = subElement->readUInteger();
381  break;
383  m_channelCount = subElement->readUInteger();
384  break;
386  if (!m_samplingFrequency) {
387  m_samplingFrequency = subElement->readFloat();
388  }
389  break;
392  m_extensionSamplingFrequency = subElement->readFloat();
393  }
394  break;
395  default:;
396  }
397  }
398  break;
400  m_trackNumber = trackInfoElement->readUInteger();
401  break;
403  m_id = trackInfoElement->readUInteger();
404  break;
406  m_name = trackInfoElement->readString();
407  break;
409  m_language = trackInfoElement->readString();
410  break;
412  m_format = codecIdToMediaFormat(m_formatId = trackInfoElement->readString());
413  break;
415  m_formatName = trackInfoElement->readString();
416  break;
418  break; // TODO
420  m_enabled = trackInfoElement->readUInteger();
421  break;
423  m_default = trackInfoElement->readUInteger();
424  break;
426  m_forced = trackInfoElement->readUInteger();
427  break;
429  m_lacing = trackInfoElement->readUInteger();
430  break;
432  defaultDuration = trackInfoElement->readUInteger();
433  break;
434  default:;
435  }
436  switch (m_mediaType) {
437  case MediaType::Video:
438  if (!m_fps && defaultDuration) {
439  m_fps = 1000000000.0 / defaultDuration;
440  }
441  break;
442  default:;
443  }
444  }
445 
446  // read further information from the CodecPrivate element for some codecs
447  EbmlElement *codecPrivateElement;
448  switch (m_format.general) {
450  if ((codecPrivateElement = m_trackElement->childById(MatroskaIds::CodecPrivate, diag))) {
451  // parse bitmap info header to determine actual format
452  if (codecPrivateElement->dataSize() >= 0x28) {
453  m_istream->seekg(static_cast<streamoff>(codecPrivateElement->dataOffset()));
454  BitmapInfoHeader bitmapInfoHeader;
455  bitmapInfoHeader.parse(reader());
456  m_formatId.reserve(m_formatId.size() + 7);
457  m_formatId += " \"";
458  m_formatId += interpretIntegerAsString(bitmapInfoHeader.compression);
459  m_formatId += "\"";
461  } else {
462  diag.emplace_back(DiagLevel::Critical, "BITMAPINFOHEADER structure (in \"CodecPrivate\"-element) is truncated.", context);
463  }
464  }
465  break;
467  if ((codecPrivateElement = m_trackElement->childById(MatroskaIds::CodecPrivate, diag))) {
468  // parse WAVE header to determine actual format
469  if (codecPrivateElement->dataSize() >= 16) {
470  m_istream->seekg(static_cast<streamoff>(codecPrivateElement->dataOffset()));
471  WaveFormatHeader waveFormatHeader;
472  waveFormatHeader.parse(reader());
473  WaveAudioStream::addInfo(waveFormatHeader, *this);
474  } else {
475  diag.emplace_back(DiagLevel::Critical, "BITMAPINFOHEADER structure (in \"CodecPrivate\"-element) is truncated.", context);
476  }
477  }
478  break;
480  if ((codecPrivateElement = m_trackElement->childById(MatroskaIds::CodecPrivate, diag))) {
481  auto audioSpecificConfig
482  = Mp4Track::parseAudioSpecificConfig(*m_istream, codecPrivateElement->dataOffset(), codecPrivateElement->dataSize(), diag);
484  audioSpecificConfig->audioObjectType, audioSpecificConfig->sbrPresent, audioSpecificConfig->psPresent);
485  if (audioSpecificConfig->sampleFrequencyIndex == 0xF) {
486  //m_samplingFrequency = audioSpecificConfig->sampleFrequency;
487  } else if (audioSpecificConfig->sampleFrequencyIndex < sizeof(mpeg4SamplingFrequencyTable)) {
488  //m_samplingFrequency = mpeg4SamplingFrequencyTable[audioSpecificConfig->sampleFrequencyIndex];
489  } else {
490  diag.emplace_back(DiagLevel::Warning, "Audio specific config has invalid sample frequency index.", context);
491  }
492  if (audioSpecificConfig->extensionSampleFrequencyIndex == 0xF) {
493  //m_extensionSamplingFrequency = audioSpecificConfig->extensionSampleFrequency;
494  } else if (audioSpecificConfig->extensionSampleFrequencyIndex < sizeof(mpeg4SamplingFrequencyTable)) {
495  //m_extensionSamplingFrequency = mpeg4SamplingFrequencyTable[audioSpecificConfig->extensionSampleFrequencyIndex];
496  } else {
497  diag.emplace_back(DiagLevel::Warning, "Audio specific config has invalid extension sample frequency index.", context);
498  }
499  m_channelConfig = audioSpecificConfig->channelConfiguration;
500  m_extensionChannelConfig = audioSpecificConfig->extensionChannelConfiguration;
501  }
502  break;
504  if ((codecPrivateElement = m_trackElement->childById(MatroskaIds::CodecPrivate, diag))) {
505  auto avcConfig = make_unique<TagParser::AvcConfiguration>();
506  try {
507  m_istream->seekg(static_cast<streamoff>(codecPrivateElement->dataOffset()));
508  avcConfig->parse(m_reader, codecPrivateElement->dataSize(), diag);
509  Mp4Track::addInfo(*avcConfig, *this);
510  } catch (const TruncatedDataException &) {
511  diag.emplace_back(DiagLevel::Critical, "AVC configuration is truncated.", context);
512  } catch (const Failure &) {
513  diag.emplace_back(DiagLevel::Critical, "AVC configuration is invalid.", context);
514  }
515  }
516  break;
517  default:;
518  }
519 
520  // parse format name for unknown formats
522  if (startsWith<string>(m_formatId, "V_") || startsWith<string>(m_formatId, "A_") || startsWith<string>(m_formatId, "S_")) {
523  m_formatName = m_formatId.substr(2);
524  } else {
526  }
527  m_formatName.append(" (unknown)");
528  }
529 
530  // use pixel size as display size if display size not specified
531  if (!m_displaySize.width()) {
533  }
534  if (!m_displaySize.height()) {
536  }
537 
538  // set English if no language has been specified (it is default value of MatroskaIds::TrackLanguage)
539  if (m_language.empty()) {
540  m_language = "eng";
541  }
542 }
543 
555 MatroskaTrackHeaderMaker::MatroskaTrackHeaderMaker(const MatroskaTrack &track, Diagnostics &diag)
556  : m_track(track)
557  , m_dataSize(0)
558 {
559  VAR_UNUSED(diag);
560 
561  // calculate size for recognized elements
562  m_dataSize += 2 + 1 + EbmlElement::calculateUIntegerLength(m_track.id());
563  m_dataSize += 1 + 1 + EbmlElement::calculateUIntegerLength(m_track.trackNumber());
564  m_dataSize += 1 + 1 + EbmlElement::calculateUIntegerLength(m_track.isEnabled());
565  m_dataSize += 1 + 1 + EbmlElement::calculateUIntegerLength(m_track.isDefault());
566  m_dataSize += 2 + 1 + EbmlElement::calculateUIntegerLength(m_track.isForced());
567  if (!m_track.name().empty()) {
568  m_dataSize += 2 + EbmlElement::calculateSizeDenotationLength(m_track.name().size()) + m_track.name().size();
569  }
570  if (!m_track.language().empty()) {
571  m_dataSize += 3 + EbmlElement::calculateSizeDenotationLength(m_track.language().size()) + m_track.language().size();
572  }
573 
574  // calculate size for other elements
575  for (EbmlElement *trackInfoElement = m_track.m_trackElement->firstChild(); trackInfoElement; trackInfoElement = trackInfoElement->nextSibling()) {
576  switch (trackInfoElement->id()) {
584  // skip recognized elements
585  break;
586  default:
587  trackInfoElement->makeBuffer();
588  m_dataSize += trackInfoElement->totalSize();
589  }
590  }
591  m_sizeDenotationLength = EbmlElement::calculateSizeDenotationLength(m_dataSize);
592  m_requiredSize = 1 + m_sizeDenotationLength + m_dataSize;
593 }
594 
602 void MatroskaTrackHeaderMaker::make(ostream &stream) const
603 {
604  // make ID and size
605  char buffer[9];
606  *buffer = static_cast<char>(MatroskaIds::TrackEntry);
607  EbmlElement::makeSizeDenotation(m_dataSize, buffer + 1);
608  stream.write(buffer, 1 + m_sizeDenotationLength);
609 
610  // make recognized elements
616  if (!m_track.name().empty()) {
618  }
619  if (!m_track.language().empty()) {
621  }
622 
623  // make other elements
624  for (EbmlElement *trackInfoElement = m_track.m_trackElement->firstChild(); trackInfoElement; trackInfoElement = trackInfoElement->nextSibling()) {
625  switch (trackInfoElement->id()) {
633  // skip recognized elements
634  break;
635  default:
636  trackInfoElement->copyBuffer(stream);
637  }
638  }
639 }
640 
641 } // namespace TagParser
const std::string & language() const
Returns the language of the track if known; otherwise returns an empty string.
constexpr TAG_PARSER_EXPORT const char * numberOfBytes()
void setBottom(uint32 bottom)
Sets the bottom margin to bottom.
Definition: margin.h:91
bool isDefault() const
Returns true if the track is denoted as default; otherwise returns false.
The exception that is thrown when the data to be parsed is truncated and therefore can not be parsed ...
Definition: exceptions.h:39
IoUtilities::BinaryReader m_reader
const std::string name() const
Returns the track name if known; otherwise returns an empty string.
uint32 trackNumber() const
Returns the track number if known; otherwise returns 0.
static void makeSimpleElement(std::ostream &stream, IdentifierType id, uint64 content)
Makes a simple EBML element.
void setWidth(uint32 value)
Sets the width.
Definition: size.h:75
ImplementationType * firstChild()
Returns the first child of the element.
constexpr TAG_PARSER_EXPORT const char * numberOfFrames()
static MediaFormat codecIdToMediaFormat(const std::string &codecId)
Returns the MediaFormat for the specified Matroska codec ID.
static byte calculateSizeDenotationLength(uint64 size)
Returns the length of the size denotation for the specified size in byte.
static void addInfo(const AvcConfiguration &avcConfig, AbstractTrack &track)
Adds the information from the specified avcConfig to the specified track.
Definition: mp4track.cpp:1020
The MediaFormat class specifies the format of media data.
Definition: mediaformat.h:244
static void addInfo(const WaveFormatHeader &waveHeader, AbstractTrack &track)
Adds the information from the specified waveHeader to the specified track.
The WaveFormatHeader class parses the WAVEFORMATEX structure defined by MS.
DataSizeType dataSize() const
Returns the data size of the element in byte.
GeneralMediaFormat general
Definition: mediaformat.h:257
IoUtilities::BinaryReader & reader()
Returns a binary reader for the associated stream.
void setLeft(uint32 left)
Sets the left margin to left.
Definition: margin.h:75
void parse(IoUtilities::BinaryReader &reader)
Parses the WAVE "fmt " header segment using the specified reader.
static byte calculateUIntegerLength(uint64 integer)
Returns the length of the specified unsigned integer in byte.
The EbmlElement class helps to parse EBML files such as Matroska files.
Definition: ebmlelement.h:31
static std::unique_ptr< Mpeg4AudioSpecificConfig > parseAudioSpecificConfig(std::istream &stream, uint64 startOffset, uint64 size, Diagnostics &diag)
Parses the audio specific configuration for the track.
Definition: mp4track.cpp:654
~MatroskaTrack() override
Destroys the track.
void setRight(uint32 right)
Sets the right margin to right.
Definition: margin.h:107
void setHeight(uint32 value)
Sets the height.
Definition: size.h:83
TAG_PARSER_EXPORT MediaFormat idToMediaFormat(byte mpeg4AudioObjectId, bool sbrPresent=false, bool psPresent=false)
Definition: mp4ids.cpp:359
bool isEmpty() const
Returns an indication whether an value is assigned.
Definition: tagvalue.h:389
uint64 dataOffset() const
Returns the data offset of the element in the related stream.
ChronoUtilities::TimeSpan toTimeSpan() const
Converts the value of the current TagValue object to its equivalent TimeSpan representation.
Definition: tagvalue.cpp:291
constexpr uint32 height() const
Returns the height.
Definition: size.h:67
void internalParseHeader(Diagnostics &diag) override
This method is internally called to parse header information.
ChronoUtilities::TimeSpan m_duration
void setTop(uint32 top)
Sets the top margin to top.
Definition: margin.h:59
static byte makeSizeDenotation(uint64 size, char *buff)
Makes the size denotation for the specified size and stores it to buff.
The TagTarget class specifies the target of a tag.
Definition: tagtarget.h:21
TAG_PARSER_EXPORT MediaFormat fourccToMediaFormat(uint32 fourccId)
Definition: mp4ids.cpp:46
std::istream * m_istream
constexpr TAG_PARSER_EXPORT const char * bitrate()
The track's bit rate in bits per second.
constexpr TAG_PARSER_EXPORT const char * duration()
bool isEnabled() const
Returns true if the track is denoted as enabled; otherwise returns false.
uint64 id() const
Returns the track ID if known; otherwise returns 0.
The BitmapInfoHeader class parses the BITMAPINFOHEADER structure defined by MS.
ChronoUtilities::DateTime toDateTime() const
Converts the value of the current TagValue object to its equivalent DateTime representation.
Definition: tagvalue.cpp:319
ChronoUtilities::DateTime m_creationTime
unsigned char extension
Definition: mediaformat.h:259
void parse(IoUtilities::BinaryReader &reader)
Parses the BITMAPINFOHEADER structure using the specified reader.
const IdContainerType & tracks() const
Returns the tracks.
Definition: tagtarget.h:104
Implementation of TagParser::AbstractTrack for the Matroska container.
Definition: matroskatrack.h:46
The AbstractTrack class parses and stores technical information about video, audio and other kinds of...
Definition: abstracttrack.h:40
TrackType type() const override
Returns the type of the track if known; otherwise returns TrackType::Unspecified.
bool isForced() const
Returns true if the track is denoted as forced; otherwise returns false.
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:365
ChronoUtilities::DateTime m_modificationTime
constexpr uint32 width() const
Returns the width.
Definition: size.h:59
The TagValue class wraps values of different types.
Definition: tagvalue.h:65
void readStatisticsFromTags(const std::vector< std::unique_ptr< MatroskaTag >> &tags, Diagnostics &diag)
Reads track-specific statistics from the specified tags.
The class inherits from std::exception and serves as base class for exceptions thrown by the elements...
Definition: exceptions.h:11
ImplementationType * childById(const IdentifierType &id, Diagnostics &diag)
Returns the first child with the specified id.
Contains all classes and functions of the TagInfo library.
Definition: aaccodebook.h:9
TrackType
Specifies the track type.
Definition: abstracttrack.h:28
constexpr TAG_PARSER_EXPORT const char * writingDate()
The Diagnostics class is a container for DiagMessage.
Definition: diagnostics.h:156
uint32 mpeg4SamplingFrequencyTable[13]
Definition: mp4ids.cpp:415
void make(std::ostream &stream) const
Saves the header for the track (specified when constructing the object) to the specified stream (make...