Tag Parser  7.1.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 == "A_MPEG") {
99  if (part2 == "L1") {
101  } else if (part2 == "L2") {
103  } else if (part2 == "L3") {
105  }
106  } else if (part1 == "V_MPEGH" && part2 == "ISO" && part3 == "HEVC") {
108  } else if (part1 == "A_PCM") {
110  if (part2 == "INT") {
111  if (part3 == "BIG") {
113  } else if (part3 == "LIT") {
115  }
116  } else if (part2 == "FLOAT" && part3 == "IEEE") {
118  }
119  } else if (part1 == "A_MPC") {
121  } else if (part1 == "A_AC3") {
123  } else if (part1 == "A_ALAC") {
125  } else if (part1 == "A_DTS") {
127  if (part2 == "EXPRESS") {
129  } else if (part2 == "LOSSLESS") {
131  }
132  } else if (part1 == "A_VORBIS") {
134  } else if (part1 == "A_FLAC") {
136  } else if (part1 == "A_OPUS") {
138  } else if (part1 == "A_REAL") {
140  } else if (part1 == "A_MS" && part2 == "ACM") {
142  } else if (part1 == "A_AAC") {
144  if (part2 == "MPEG2") {
145  if (part3 == "MAIN") {
147  } else if (part3 == "LC") {
149  } else if (part3 == "SBR") {
152  } else if (part3 == "SSR") {
154  }
155  } else if (part2 == "MPEG4") {
156  if (part3 == "MAIN") {
158  } else if (part3 == "LC") {
160  } else if (part3 == "SBR") {
163  } else if (part3 == "SSR") {
165  } else if (part3 == "LTP") {
167  }
168  }
169  } else if (part1 == "A_QUICKTIME") {
171  } else if (part1 == "A_TTA1") {
173  } else if (part1 == "A_WAVPACK4") {
175  } else if (part1 == "S_TEXT") {
177  if (part2 == "UTF8") {
179  } else if (part2 == "SSA") {
181  } else if (part2 == "ASS") {
183  } else if (part2 == "USF") {
185  } else if (part2 == "WEBVTT") {
187  }
188  } else if (part1 == "S_IMAGE") {
190  if (part2 == "BMP") {
192  }
193  } else if (part1 == "S_VOBSUB") {
195  } else if (part1 == "S_KATE") {
197  } else if (part1 == "B_VOBBTN") {
199  } else if (part1 == "S_DVBSUB") {
201  } else if (part1 == "V_MSWMV") {
203  }
204  return fmt;
205 }
206 
208 
209 template <typename PropertyType, typename ConversionFunction>
210 void MatroskaTrack::assignPropertyFromTagValue(const std::unique_ptr<MatroskaTag> &tag, const char *fieldId, PropertyType &property,
211  const ConversionFunction &conversionFunction, Diagnostics &diag)
212 {
213  const TagValue &value = tag->value(fieldId);
214  if (!value.isEmpty()) {
215  try {
216  property = conversionFunction(value);
217  } catch (const ConversionException &) {
218  string message;
219  try {
220  message = argsToString("Ignoring invalid value \"", value.toString(TagTextEncoding::Utf8), "\" of \"", fieldId, '\"', '.');
221  } catch (const ConversionException &) {
222  message = argsToString("Ignoring invalid value of \"", fieldId, '\"', '.');
223  }
224  diag.emplace_back(DiagLevel::Warning, message, argsToString("reading track statatistic from \"", tag->toString(), '\"'));
225  }
226  }
227 }
228 
229 template <typename NumberType, Traits::EnableIf<std::is_integral<NumberType>> * = nullptr> NumberType tagValueToNumber(const TagValue &tagValue)
230 {
231  // optimization for Latin1/UTF-8 strings
232  if (tagValue.type() == TagDataType::Text) {
233  switch (tagValue.dataEncoding()) {
236  return bufferToNumber<NumberType>(tagValue.dataPointer(), tagValue.dataSize());
237  default:;
238  }
239  }
240  // generic conversion
241  return stringToNumber<NumberType>(tagValue.toString(TagTextEncoding::Utf8));
242 }
243 
244 template <typename NumberType, Traits::EnableIf<std::is_floating_point<NumberType>> * = nullptr>
245 NumberType tagValueToBitrate(const TagValue &tagValue)
246 {
247  return stringToNumber<NumberType>(tagValue.toString(TagTextEncoding::Utf8)) / 1000;
248 }
249 
251 
261 void MatroskaTrack::readStatisticsFromTags(const std::vector<std::unique_ptr<MatroskaTag>> &tags, Diagnostics &diag)
262 {
263  using namespace std::placeholders;
264  using namespace MatroskaTagIds::TrackSpecific;
265  for (const auto &tag : tags) {
266  const TagTarget &target = tag->target();
267  if (find(target.tracks().cbegin(), target.tracks().cend(), id()) == target.tracks().cend()) {
268  continue;
269  }
270  assignPropertyFromTagValue(tag, numberOfBytes(), m_size, &tagValueToNumber<uint64>, diag);
271  assignPropertyFromTagValue(tag, numberOfFrames(), m_sampleCount, &tagValueToNumber<uint64>, diag);
272  assignPropertyFromTagValue(tag, MatroskaTagIds::TrackSpecific::duration(), m_duration, bind(&TagValue::toTimeSpan, _1), diag);
273  assignPropertyFromTagValue(tag, MatroskaTagIds::TrackSpecific::bitrate(), m_bitrate, &tagValueToBitrate<double>, diag);
274  assignPropertyFromTagValue(tag, writingDate(), m_modificationTime, bind(&TagValue::toDateTime, _1), diag);
275  if (m_creationTime.isNull()) {
277  }
278  }
279 }
280 
282 {
283  static const string context("parsing header of Matroska track");
284  try {
285  m_trackElement->parse(diag);
286  } catch (const Failure &) {
287  diag.emplace_back(DiagLevel::Critical, "Unable to parse track element.", context);
288  throw;
289  }
290  // read information about the track from the childs of the track entry element
291  for (EbmlElement *trackInfoElement = m_trackElement->firstChild(), *subElement = nullptr; trackInfoElement;
292  trackInfoElement = trackInfoElement->nextSibling()) {
293  try {
294  trackInfoElement->parse(diag);
295  } catch (const Failure &) {
296  diag.emplace_back(DiagLevel::Critical, "Unable to parse track information element.", context);
297  break;
298  }
299  uint32 defaultDuration = 0;
300  switch (trackInfoElement->id()) {
302  switch (trackInfoElement->readUInteger()) {
305  break;
308  break;
311  break;
314  break;
317  break;
318  default:
320  }
321  break;
323  for (subElement = trackInfoElement->firstChild(); subElement; subElement = subElement->nextSibling()) {
324  try {
325  subElement->parse(diag);
326  } catch (const Failure &) {
327  diag.emplace_back(DiagLevel::Critical, "Unable to parse video track element.", context);
328  break;
329  }
330  switch (subElement->id()) {
332  m_displaySize.setWidth(subElement->readUInteger());
333  break;
335  m_displaySize.setHeight(subElement->readUInteger());
336  break;
338  m_pixelSize.setWidth(subElement->readUInteger());
339  break;
341  m_pixelSize.setHeight(subElement->readUInteger());
342  break;
344  m_cropping.setTop(subElement->readUInteger());
345  break;
347  m_cropping.setLeft(subElement->readUInteger());
348  break;
350  m_cropping.setBottom(subElement->readUInteger());
351  break;
353  m_cropping.setRight(subElement->readUInteger());
354  break;
356  m_fps = subElement->readFloat();
357  break;
359  m_interlaced = subElement->readUInteger();
360  break;
362  m_colorSpace = subElement->readUInteger();
363  break;
364  default:;
365  }
366  }
367  break;
369  for (subElement = trackInfoElement->firstChild(); subElement; subElement = subElement->nextSibling()) {
370  try {
371  subElement->parse(diag);
372  } catch (const Failure &) {
373  diag.emplace_back(DiagLevel::Critical, "Unable to parse audio track element.", context);
374  break;
375  }
376  switch (subElement->id()) {
378  m_bitsPerSample = subElement->readUInteger();
379  break;
381  m_channelCount = subElement->readUInteger();
382  break;
384  if (!m_samplingFrequency) {
385  m_samplingFrequency = subElement->readFloat();
386  }
387  break;
390  m_extensionSamplingFrequency = subElement->readFloat();
391  }
392  break;
393  default:;
394  }
395  }
396  break;
398  m_trackNumber = trackInfoElement->readUInteger();
399  break;
401  m_id = trackInfoElement->readUInteger();
402  break;
404  m_name = trackInfoElement->readString();
405  break;
407  m_language = trackInfoElement->readString();
408  break;
410  m_format = codecIdToMediaFormat(m_formatId = trackInfoElement->readString());
411  break;
413  m_formatName = trackInfoElement->readString();
414  break;
416  break; // TODO
418  m_enabled = trackInfoElement->readUInteger();
419  break;
421  m_default = trackInfoElement->readUInteger();
422  break;
424  m_forced = trackInfoElement->readUInteger();
425  break;
427  m_lacing = trackInfoElement->readUInteger();
428  break;
430  defaultDuration = trackInfoElement->readUInteger();
431  break;
432  default:;
433  }
434  switch (m_mediaType) {
435  case MediaType::Video:
436  if (!m_fps && defaultDuration) {
437  m_fps = 1000000000.0 / defaultDuration;
438  }
439  break;
440  default:;
441  }
442  }
443 
444  // read further information from the CodecPrivate element for some codecs
445  EbmlElement *codecPrivateElement;
446  switch (m_format.general) {
448  if ((codecPrivateElement = m_trackElement->childById(MatroskaIds::CodecPrivate, diag))) {
449  // parse bitmap info header to determine actual format
450  if (codecPrivateElement->dataSize() >= 0x28) {
451  m_istream->seekg(static_cast<streamoff>(codecPrivateElement->dataOffset()));
452  BitmapInfoHeader bitmapInfoHeader;
453  bitmapInfoHeader.parse(reader());
454  m_formatId.reserve(m_formatId.size() + 7);
455  m_formatId += " \"";
456  m_formatId += interpretIntegerAsString(bitmapInfoHeader.compression);
457  m_formatId += "\"";
459  } else {
460  diag.emplace_back(DiagLevel::Critical, "BITMAPINFOHEADER structure (in \"CodecPrivate\"-element) is truncated.", context);
461  }
462  }
463  break;
465  if ((codecPrivateElement = m_trackElement->childById(MatroskaIds::CodecPrivate, diag))) {
466  // parse WAVE header to determine actual format
467  if (codecPrivateElement->dataSize() >= 16) {
468  m_istream->seekg(static_cast<streamoff>(codecPrivateElement->dataOffset()));
469  WaveFormatHeader waveFormatHeader;
470  waveFormatHeader.parse(reader());
471  WaveAudioStream::addInfo(waveFormatHeader, *this);
472  } else {
473  diag.emplace_back(DiagLevel::Critical, "BITMAPINFOHEADER structure (in \"CodecPrivate\"-element) is truncated.", context);
474  }
475  }
476  break;
478  if ((codecPrivateElement = m_trackElement->childById(MatroskaIds::CodecPrivate, diag))) {
479  auto audioSpecificConfig
480  = Mp4Track::parseAudioSpecificConfig(*m_istream, codecPrivateElement->dataOffset(), codecPrivateElement->dataSize(), diag);
482  audioSpecificConfig->audioObjectType, audioSpecificConfig->sbrPresent, audioSpecificConfig->psPresent);
483  if (audioSpecificConfig->sampleFrequencyIndex == 0xF) {
484  //m_samplingFrequency = audioSpecificConfig->sampleFrequency;
485  } else if (audioSpecificConfig->sampleFrequencyIndex < sizeof(mpeg4SamplingFrequencyTable)) {
486  //m_samplingFrequency = mpeg4SamplingFrequencyTable[audioSpecificConfig->sampleFrequencyIndex];
487  } else {
488  diag.emplace_back(DiagLevel::Warning, "Audio specific config has invalid sample frequency index.", context);
489  }
490  if (audioSpecificConfig->extensionSampleFrequencyIndex == 0xF) {
491  //m_extensionSamplingFrequency = audioSpecificConfig->extensionSampleFrequency;
492  } else if (audioSpecificConfig->extensionSampleFrequencyIndex < sizeof(mpeg4SamplingFrequencyTable)) {
493  //m_extensionSamplingFrequency = mpeg4SamplingFrequencyTable[audioSpecificConfig->extensionSampleFrequencyIndex];
494  } else {
495  diag.emplace_back(DiagLevel::Warning, "Audio specific config has invalid extension sample frequency index.", context);
496  }
497  m_channelConfig = audioSpecificConfig->channelConfiguration;
498  m_extensionChannelConfig = audioSpecificConfig->extensionChannelConfiguration;
499  }
500  break;
502  if ((codecPrivateElement = m_trackElement->childById(MatroskaIds::CodecPrivate, diag))) {
503  auto avcConfig = make_unique<TagParser::AvcConfiguration>();
504  try {
505  m_istream->seekg(static_cast<streamoff>(codecPrivateElement->dataOffset()));
506  avcConfig->parse(m_reader, codecPrivateElement->dataSize());
507  Mp4Track::addInfo(*avcConfig, *this);
508  } catch (const TruncatedDataException &) {
509  diag.emplace_back(DiagLevel::Critical, "AVC configuration is truncated.", context);
510  } catch (const Failure &) {
511  diag.emplace_back(DiagLevel::Critical, "AVC configuration is invalid.", context);
512  }
513  }
514  break;
515  default:;
516  }
517 
518  // parse format name for unknown formats
520  if (startsWith<string>(m_formatId, "V_") || startsWith<string>(m_formatId, "A_") || startsWith<string>(m_formatId, "S_")) {
521  m_formatName = m_formatId.substr(2);
522  } else {
524  }
525  m_formatName.append(" (unknown)");
526  }
527 
528  // use pixel size as display size if display size not specified
529  if (!m_displaySize.width()) {
531  }
532  if (!m_displaySize.height()) {
534  }
535 
536  // set English if no language has been specified (it is default value of MatroskaIds::TrackLanguage)
537  if (m_language.empty()) {
538  m_language = "eng";
539  }
540 }
541 
553 MatroskaTrackHeaderMaker::MatroskaTrackHeaderMaker(const MatroskaTrack &track, Diagnostics &diag)
554  : m_track(track)
555  , m_dataSize(0)
556 {
557  VAR_UNUSED(diag);
558 
559  // calculate size for recognized elements
560  m_dataSize += 2 + 1 + EbmlElement::calculateUIntegerLength(m_track.id());
561  m_dataSize += 1 + 1 + EbmlElement::calculateUIntegerLength(m_track.trackNumber());
562  m_dataSize += 1 + 1 + EbmlElement::calculateUIntegerLength(m_track.isEnabled());
563  m_dataSize += 1 + 1 + EbmlElement::calculateUIntegerLength(m_track.isDefault());
564  m_dataSize += 2 + 1 + EbmlElement::calculateUIntegerLength(m_track.isForced());
565  if (!m_track.name().empty()) {
566  m_dataSize += 2 + EbmlElement::calculateSizeDenotationLength(m_track.name().size()) + m_track.name().size();
567  }
568  if (!m_track.language().empty()) {
569  m_dataSize += 3 + EbmlElement::calculateSizeDenotationLength(m_track.language().size()) + m_track.language().size();
570  }
571 
572  // calculate size for other elements
573  for (EbmlElement *trackInfoElement = m_track.m_trackElement->firstChild(); trackInfoElement; trackInfoElement = trackInfoElement->nextSibling()) {
574  switch (trackInfoElement->id()) {
582  // skip recognized elements
583  break;
584  default:
585  trackInfoElement->makeBuffer();
586  m_dataSize += trackInfoElement->totalSize();
587  }
588  }
589  m_sizeDenotationLength = EbmlElement::calculateSizeDenotationLength(m_dataSize);
590  m_requiredSize = 1 + m_sizeDenotationLength + m_dataSize;
591 }
592 
600 void MatroskaTrackHeaderMaker::make(ostream &stream) const
601 {
602  // make ID and size
603  char buffer[9];
604  *buffer = static_cast<char>(MatroskaIds::TrackEntry);
605  EbmlElement::makeSizeDenotation(m_dataSize, buffer + 1);
606  stream.write(buffer, 1 + m_sizeDenotationLength);
607 
608  // make recognized elements
614  if (!m_track.name().empty()) {
616  }
617  if (!m_track.language().empty()) {
619  }
620 
621  // make other elements
622  for (EbmlElement *trackInfoElement = m_track.m_trackElement->firstChild(); trackInfoElement; trackInfoElement = trackInfoElement->nextSibling()) {
623  switch (trackInfoElement->id()) {
631  // skip recognized elements
632  break;
633  default:
634  trackInfoElement->copyBuffer(stream);
635  }
636  }
637 }
638 
639 } // namespace TagParser
const std::string & language() const
Returns the language of the track if known; otherwise returns an empty string.
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:32
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
TAG_PARSER_EXPORT const char * numberOfBytes()
ImplementationType * firstChild()
Returns the first child of the element.
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:1016
TAG_PARSER_EXPORT const char * writingDate()
The MediaFormat class specifies the format of media data.
Definition: mediaformat.h:243
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.
TAG_PARSER_EXPORT const char * bitrate()
The track&#39;s bit rate in bits per second.
DataSizeType dataSize() const
Returns the data size of the element in byte.
STL namespace.
GeneralMediaFormat general
Definition: mediaformat.h:256
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:651
~MatroskaTrack() override
Destroys the track.
void setRight(uint32 right)
Sets the right margin to right.
Definition: margin.h:107
TAG_PARSER_EXPORT const char * duration()
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:352
bool isEmpty() const
Returns an indication whether an value is assigned.
Definition: tagvalue.h:384
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
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:258
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
TAG_PARSER_EXPORT const char * numberOfFrames()
The AbstractTrack class parses and stores technical information about video, audio and other kinds of...
Definition: abstracttrack.h:39
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:360
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:64
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
The Diagnostics class is a container for DiagMessage.
Definition: diagnostics.h:154
uint32 mpeg4SamplingFrequencyTable[13]
Definition: mp4ids.cpp:408
void make(std::ostream &stream) const
Saves the header for the track (specified when constructing the object) to the specified stream (make...