Tag Parser  6.2.1
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 "./ebmlelement.h"
2 #include "./matroskatrack.h"
3 #include "./matroskacontainer.h"
4 #include "./matroskaid.h"
5 
6 #include "../avi/bitmapinfoheader.h"
7 
8 #include "../wav/waveaudiostream.h"
9 
10 #include "../avc/avcconfiguration.h"
11 
12 #include "../mp4/mp4ids.h"
13 #include "../mp4/mp4track.h"
14 
15 #include "../mediaformat.h"
16 #include "../exceptions.h"
17 
18 #include <c++utilities/conversion/stringconversion.h>
19 
20 using namespace std;
21 using namespace ConversionUtilities;
22 
23 namespace Media {
24 
36 MatroskaTrack::MatroskaTrack(EbmlElement &trackElement) :
37  AbstractTrack(trackElement.stream(), trackElement.startOffset()),
38  m_trackElement(&trackElement)
39 {}
40 
45 {}
46 
48 {
50 }
51 
56 {
57  auto parts = splitString<vector<string> >(codecId, "/", EmptyPartsTreat::Keep, 3);
58  parts.resize(3);
59  const auto &part1 = parts[0], &part2 = parts[1], &part3 = parts[2];
60  MediaFormat fmt;
61  if(part1 == "V_MS" && part2 == "VFW" && part3 == "FOURCC") {
63  } else if(part1 == "V_UNCOMPRESSED") {
65  } else if(part1 == "V_MPEG4") {
67  if(part2 == "ISO") {
68  if(part3 == "SP") {
70  } else if(part3 == "ASP") {
72  } else if(part3 == "AVC") {
74  }
75  } else if(part2 == "MS" && part3 == "V3") {
77  }
78  } else if(part1 == "V_MPEG1") {
80  } else if(part1 == "V_MPEG2") {
82  } else if(part1 == "V_REAL") {
84  } else if(part1 == "V_QUICKTIME") {
86  } else if(part1 == "V_THEORA") {
88  } else if(part1 == "V_PRORES") {
90  } else if(part1 == "V_VP8") {
92  } else if(part1 == "V_VP9") {
94  } else if(part1 == "A_MPEG") {
96  if(part2 == "L1") {
98  } else if(part2 == "L2") {
100  } else if(part2 == "L3") {
102  }
103  } else if(part1 == "A_PCM") {
105  if(part2 == "INT") {
106  if(part3 == "BIG") {
108  } else if(part3 == "LIT") {
110  }
111  } else if (part2 == "FLOAT" && part3 == "IEEE") {
113  }
114  } else if(part1 == "A_MPC") {
116  } else if(part1 == "A_AC3") {
118  } else if(part1 == "A_ALAC") {
120  } else if(part1 == "A_DTS") {
122  if(part2 == "EXPRESS") {
124  } else if(part2 == "LOSSLESS") {
126  }
127  } else if(part1 == "A_VORBIS") {
129  } else if(part1 == "A_FLAC") {
131  } else if(part1 == "A_OPUS") {
133  } else if(part1 == "A_REAL") {
135  } else if(part1 == "A_MS" && part2 == "ACM") {
137  } else if(part1 == "A_AAC") {
139  if(part2 == "MPEG2") {
140  if(part3 == "MAIN") {
142  } else if(part3 == "LC") {
144  } else if(part3 == "SBR") {
147  } else if(part3 == "SSR") {
149  }
150  } else if(part2 == "MPEG4") {
151  if(part3 == "MAIN") {
153  } else if(part3 == "LC") {
155  } else if(part3 == "SBR") {
158  } else if(part3 == "SSR") {
160  } else if(part3 == "LTP") {
162  }
163  }
164  } else if(part1 == "A_QUICKTIME") {
166  } else if(part1 == "A_TTA1") {
168  } else if(part1 == "A_WAVPACK4") {
170  } else if(part1 == "S_TEXT") {
172  if(part2 == "UTF8") {
174  } else if(part2 == "SSA") {
176  } else if(part2 == "ASS") {
178  } else if(part2 == "USF") {
180  } else if(part2 == "WEBVTT") {
182  }
183  } else if(part1 == "S_IMAGE") {
185  if(part2 == "BMP") {
187  }
188  } else if(part1 == "S_VOBSUB") {
190  } else if(part1 == "S_KATE") {
192  } else if(part1 == "B_VOBBTN") {
194  } else if(part1 == "V_MSWMV") {
196  }
197  return fmt;
198 }
199 
201 {
202  static const string context("parsing header of Matroska track");
203  try {
204  m_trackElement->parse();
205  } catch(const Failure &) {
206  addNotification(NotificationType::Critical, "Unable to parse track element.", context);
207  throw;
208  }
209  // read information about the track from the childs of the track entry element
210  for(EbmlElement *trackInfoElement = m_trackElement->firstChild(), *subElement = nullptr; trackInfoElement; trackInfoElement = trackInfoElement->nextSibling()) {
211  try {
212  trackInfoElement->parse();
213  } catch(const Failure &) {
214  addNotification(NotificationType::Critical, "Unable to parse track information element.", context);
215  break;
216  }
217  uint32 defaultDuration = 0;
218  switch(trackInfoElement->id()) {
220  switch(trackInfoElement->readUInteger()) {
223  break;
226  break;
229  break;
232  break;
235  break;
236  default:
238  }
239  break;
241  for(subElement = trackInfoElement->firstChild(); subElement; subElement = subElement->nextSibling()) {
242  try {
243  subElement->parse();
244  } catch(const Failure &) {
245  addNotification(NotificationType::Critical, "Unable to parse video track element.", context);
246  break;
247  }
248  switch(subElement->id()) {
250  m_displaySize.setWidth(subElement->readUInteger());
251  break;
253  m_displaySize.setHeight(subElement->readUInteger());
254  break;
256  m_pixelSize.setWidth(subElement->readUInteger());
257  break;
259  m_pixelSize.setHeight(subElement->readUInteger());
260  break;
262  m_cropping.setTop(subElement->readUInteger());
263  break;
265  m_cropping.setLeft(subElement->readUInteger());
266  break;
268  m_cropping.setBottom(subElement->readUInteger());
269  break;
271  m_cropping.setRight(subElement->readUInteger());
272  break;
274  m_fps = subElement->readFloat();
275  break;
277  m_interlaced = subElement->readUInteger();
278  break;
280  m_colorSpace = subElement->readUInteger();
281  break;
282  default:
283  ;
284  }
285  }
286  break;
288  for(subElement = trackInfoElement->firstChild(); subElement; subElement = subElement->nextSibling()) {
289  try {
290  subElement->parse();
291  } catch(const Failure &) {
292  addNotification(NotificationType::Critical, "Unable to parse audio track element.", context);
293  break;
294  }
295  switch(subElement->id()) {
297  m_bitsPerSample = subElement->readUInteger();
298  break;
300  m_channelCount = subElement->readUInteger();
301  break;
303  if(!m_samplingFrequency) {
304  m_samplingFrequency = subElement->readFloat();
305  }
306  break;
309  m_extensionSamplingFrequency = subElement->readFloat();
310  }
311  break;
312  default:
313  ;
314  }
315  }
316  break;
318  m_trackNumber = trackInfoElement->readUInteger();
319  break;
321  m_id = trackInfoElement->readUInteger();
322  break;
324  m_name = trackInfoElement->readString();
325  break;
327  m_language = trackInfoElement->readString();
328  break;
330  m_format = codecIdToMediaFormat(m_formatId = trackInfoElement->readString());
331  break;
333  m_formatName = trackInfoElement->readString();
334  break;
336  break; // TODO
338  m_enabled = trackInfoElement->readUInteger();
339  break;
341  m_default = trackInfoElement->readUInteger();
342  break;
344  m_forced = trackInfoElement->readUInteger();
345  break;
347  m_lacing = trackInfoElement->readUInteger();
348  break;
350  defaultDuration = trackInfoElement->readUInteger();
351  break;
352  default:
353  ;
354  }
355  switch(m_mediaType) {
356  case MediaType::Video:
357  if(!m_fps && defaultDuration) {
358  m_fps = 1000000000.0 / static_cast<double>(defaultDuration);
359  }
360  break;
361  default:
362  ;
363  }
364  }
365 
366  // read further information from the CodecPrivate element for some codecs
367  switch(m_format.general) {
368  EbmlElement *codecPrivateElement;
370  if((codecPrivateElement = m_trackElement->childById(MatroskaIds::CodecPrivate))) {
371  // parse bitmap info header to determine actual format
372  if(codecPrivateElement->dataSize() >= 0x28) {
373  m_istream->seekg(codecPrivateElement->dataOffset());
374  BitmapInfoHeader bitmapInfoHeader;
375  bitmapInfoHeader.parse(reader());
376  m_formatId.reserve(m_formatId.size() + 7);
377  m_formatId += " \"";
378  m_formatId += interpretIntegerAsString(bitmapInfoHeader.compression);
379  m_formatId += "\"";
381  } else {
382  addNotification(NotificationType::Critical, "BITMAPINFOHEADER structure (in \"CodecPrivate\"-element) is truncated.", context);
383  }
384  }
385  break;
387  if((codecPrivateElement = m_trackElement->childById(MatroskaIds::CodecPrivate))) {
388  // parse WAVE header to determine actual format
389  if(codecPrivateElement->dataSize() >= 16) {
390  m_istream->seekg(codecPrivateElement->dataOffset());
391  WaveFormatHeader waveFormatHeader;
392  waveFormatHeader.parse(reader());
393  WaveAudioStream::addInfo(waveFormatHeader, *this);
394  } else {
395  addNotification(NotificationType::Critical, "BITMAPINFOHEADER structure (in \"CodecPrivate\"-element) is truncated.", context);
396  }
397  }
398  break;
400  if((codecPrivateElement = m_trackElement->childById(MatroskaIds::CodecPrivate))) {
401  auto audioSpecificConfig = Mp4Track::parseAudioSpecificConfig(*this, *m_istream, codecPrivateElement->dataOffset(), codecPrivateElement->dataSize());
402  m_format += Mpeg4AudioObjectIds::idToMediaFormat(audioSpecificConfig->audioObjectType, audioSpecificConfig->sbrPresent, audioSpecificConfig->psPresent);
403  if(audioSpecificConfig->sampleFrequencyIndex == 0xF) {
404  //m_samplingFrequency = audioSpecificConfig->sampleFrequency;
405  } else if(audioSpecificConfig->sampleFrequencyIndex < sizeof(mpeg4SamplingFrequencyTable)) {
406  //m_samplingFrequency = mpeg4SamplingFrequencyTable[audioSpecificConfig->sampleFrequencyIndex];
407  } else {
408  addNotification(NotificationType::Warning, "Audio specific config has invalid sample frequency index.", context);
409  }
410  if(audioSpecificConfig->extensionSampleFrequencyIndex == 0xF) {
411  //m_extensionSamplingFrequency = audioSpecificConfig->extensionSampleFrequency;
412  } else if(audioSpecificConfig->extensionSampleFrequencyIndex < sizeof(mpeg4SamplingFrequencyTable)) {
413  //m_extensionSamplingFrequency = mpeg4SamplingFrequencyTable[audioSpecificConfig->extensionSampleFrequencyIndex];
414  } else {
415  addNotification(NotificationType::Warning, "Audio specific config has invalid extension sample frequency index.", context);
416  }
417  m_channelConfig = audioSpecificConfig->channelConfiguration;
418  m_extensionChannelConfig = audioSpecificConfig->extensionChannelConfiguration;
419  }
420  break;
422  if((codecPrivateElement = m_trackElement->childById(MatroskaIds::CodecPrivate))) {
423  auto avcConfig = make_unique<Media::AvcConfiguration>();
424  try {
425  m_istream->seekg(codecPrivateElement->dataOffset());
426  avcConfig->parse(m_reader, codecPrivateElement->dataSize());
427  Mp4Track::addInfo(*avcConfig, *this);
428  } catch(const TruncatedDataException &) {
429  addNotification(NotificationType::Critical, "AVC configuration is truncated.", context);
430  } catch(const Failure &) {
431  addNotification(NotificationType::Critical, "AVC configuration is invalid.", context);
432  }
433  }
434  default:
435  ;
436  }
437 
438  // parse format name for unknown formats
440  if(startsWith<string>(m_formatId, "V_") || startsWith<string>(m_formatId, "A_") || startsWith<string>(m_formatId, "S_")) {
441  m_formatName = m_formatId.substr(2);
442  } else {
444  }
445  m_formatName.append(" (unknown)");
446  }
447 
448  // use pixel size as display size if display size not specified
449  if(!m_displaySize.width()) {
451  }
452  if(!m_displaySize.height()) {
454  }
455 }
456 
457 }
implementationType * childById(const identifierType &id)
Returns the first child with the specified id.
constexpr uint32 width() const
Returns the width.
Definition: size.h:55
The WaveFormatHeader class parses the WAVEFORMATEX structure defined by MS.
uint64 dataOffset() const
Returns the data offset of the element in the related stream.
void setBottom(uint32 bottom)
Sets the bottom margin to bottom.
Definition: margin.h:91
The EbmlElement class helps to parse EBML files such as Matroska files.
Definition: ebmlelement.h:50
void setWidth(uint32 value)
Sets the width.
Definition: size.h:71
dataSizeType dataSize() const
Returns the data size of the element in byte.
std::string m_formatId
GeneralMediaFormat general
Definition: mediaformat.h:269
void parse()
Parses the header information of the element which is read from the related stream at the start offse...
~MatroskaTrack()
Destroys the track.
TrackType
Specifies the track type.
Definition: abstracttrack.h:28
unsigned char sub
Definition: mediaformat.h:270
The AbstractTrack class parses and stores technical information about video, audio and other kinds of...
Definition: abstracttrack.h:40
STL namespace.
static MediaFormat codecIdToMediaFormat(const std::string &codecId)
Returns the MediaFormat for the specified Matroska codec ID.
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.
void setRight(uint32 right)
Sets the right margin to right.
Definition: margin.h:107
constexpr uint32 height() const
Returns the height.
Definition: size.h:63
uint32 mpeg4SamplingFrequencyTable[13]
Definition: mp4ids.cpp:302
unsigned char extension
Definition: mediaformat.h:271
void parse(IoUtilities::BinaryReader &reader)
Parses the WAVE "fmt " header segment using the specified reader.
IoUtilities::BinaryReader & reader()
Returns a binary reader for the associated stream.
static void addInfo(const AvcConfiguration &avcConfig, AbstractTrack &track)
Adds the information from the specified avcConfig to the specified track.
Definition: mp4track.cpp:889
uint32 m_extensionSamplingFrequency
std::string m_language
void setHeight(uint32 value)
Sets the height.
Definition: size.h:79
implementationType * firstChild()
Returns the first child of the element.
The class inherits from std::exception and serves as base class for exceptions thrown by the elements...
Definition: exceptions.h:11
void setTop(uint32 top)
Sets the top margin to top.
Definition: margin.h:59
void setLeft(uint32 left)
Sets the left margin to left.
Definition: margin.h:75
TAG_PARSER_EXPORT MediaFormat fourccToMediaFormat(uint32 fourccId)
Definition: mp4ids.cpp:46
The exception that is thrown when the data to be parsed is truncated and therefore can not be parsed ...
Definition: exceptions.h:35
TAG_PARSER_EXPORT MediaFormat idToMediaFormat(byte mpeg4AudioObjectId, bool sbrPresent=false, bool psPresent=false)
Definition: mp4ids.cpp:245
static std::unique_ptr< Mpeg4AudioSpecificConfig > parseAudioSpecificConfig(StatusProvider &statusProvider, std::istream &stream, uint64 startOffset, uint64 size)
Parses the audio specific configuration for the track.
Definition: mp4track.cpp:542
TrackType type() const
Returns the type of the track if known; otherwise returns TrackType::Unspecified. ...
The BitmapInfoHeader class parses the BITMAPINFOHEADER structure defined by MS.
static void addInfo(const WaveFormatHeader &waveHeader, AbstractTrack &track)
Adds the information from the specified waveHeader to the specified track.
Contains all classes and functions of the TagInfo library.
Definition: exceptions.h:9
void internalParseHeader()
This method is internally called to parse header information.
std::string m_formatName
std::istream * m_istream
void parse(IoUtilities::BinaryReader &reader)
Parses the BITMAPINFOHEADER structure using the specified reader.
The MediaFormat class specifies the format of media data.
Definition: mediaformat.h:256