Tag Parser  6.3.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 "./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 == "V_MPEGH" && part2 == "ISO" && part3 == "HEVC") {
105  } else if(part1 == "A_PCM") {
107  if(part2 == "INT") {
108  if(part3 == "BIG") {
110  } else if(part3 == "LIT") {
112  }
113  } else if (part2 == "FLOAT" && part3 == "IEEE") {
115  }
116  } else if(part1 == "A_MPC") {
118  } else if(part1 == "A_AC3") {
120  } else if(part1 == "A_ALAC") {
122  } else if(part1 == "A_DTS") {
124  if(part2 == "EXPRESS") {
126  } else if(part2 == "LOSSLESS") {
128  }
129  } else if(part1 == "A_VORBIS") {
131  } else if(part1 == "A_FLAC") {
133  } else if(part1 == "A_OPUS") {
135  } else if(part1 == "A_REAL") {
137  } else if(part1 == "A_MS" && part2 == "ACM") {
139  } else if(part1 == "A_AAC") {
141  if(part2 == "MPEG2") {
142  if(part3 == "MAIN") {
144  } else if(part3 == "LC") {
146  } else if(part3 == "SBR") {
149  } else if(part3 == "SSR") {
151  }
152  } else if(part2 == "MPEG4") {
153  if(part3 == "MAIN") {
155  } else if(part3 == "LC") {
157  } else if(part3 == "SBR") {
160  } else if(part3 == "SSR") {
162  } else if(part3 == "LTP") {
164  }
165  }
166  } else if(part1 == "A_QUICKTIME") {
168  } else if(part1 == "A_TTA1") {
170  } else if(part1 == "A_WAVPACK4") {
172  } else if(part1 == "S_TEXT") {
174  if(part2 == "UTF8") {
176  } else if(part2 == "SSA") {
178  } else if(part2 == "ASS") {
180  } else if(part2 == "USF") {
182  } else if(part2 == "WEBVTT") {
184  }
185  } else if(part1 == "S_IMAGE") {
187  if(part2 == "BMP") {
189  }
190  } else if(part1 == "S_VOBSUB") {
192  } else if(part1 == "S_KATE") {
194  } else if(part1 == "B_VOBBTN") {
196  } else if(part1 == "S_DVBSUB") {
198  } else if(part1 == "V_MSWMV") {
200  }
201  return fmt;
202 }
203 
205 {
206  static const string context("parsing header of Matroska track");
207  try {
208  m_trackElement->parse();
209  } catch(const Failure &) {
210  addNotification(NotificationType::Critical, "Unable to parse track element.", context);
211  throw;
212  }
213  // read information about the track from the childs of the track entry element
214  for(EbmlElement *trackInfoElement = m_trackElement->firstChild(), *subElement = nullptr; trackInfoElement; trackInfoElement = trackInfoElement->nextSibling()) {
215  try {
216  trackInfoElement->parse();
217  } catch(const Failure &) {
218  addNotification(NotificationType::Critical, "Unable to parse track information element.", context);
219  break;
220  }
221  uint32 defaultDuration = 0;
222  switch(trackInfoElement->id()) {
224  switch(trackInfoElement->readUInteger()) {
227  break;
230  break;
233  break;
236  break;
239  break;
240  default:
242  }
243  break;
245  for(subElement = trackInfoElement->firstChild(); subElement; subElement = subElement->nextSibling()) {
246  try {
247  subElement->parse();
248  } catch(const Failure &) {
249  addNotification(NotificationType::Critical, "Unable to parse video track element.", context);
250  break;
251  }
252  switch(subElement->id()) {
254  m_displaySize.setWidth(subElement->readUInteger());
255  break;
257  m_displaySize.setHeight(subElement->readUInteger());
258  break;
260  m_pixelSize.setWidth(subElement->readUInteger());
261  break;
263  m_pixelSize.setHeight(subElement->readUInteger());
264  break;
266  m_cropping.setTop(subElement->readUInteger());
267  break;
269  m_cropping.setLeft(subElement->readUInteger());
270  break;
272  m_cropping.setBottom(subElement->readUInteger());
273  break;
275  m_cropping.setRight(subElement->readUInteger());
276  break;
278  m_fps = subElement->readFloat();
279  break;
281  m_interlaced = subElement->readUInteger();
282  break;
284  m_colorSpace = subElement->readUInteger();
285  break;
286  default:
287  ;
288  }
289  }
290  break;
292  for(subElement = trackInfoElement->firstChild(); subElement; subElement = subElement->nextSibling()) {
293  try {
294  subElement->parse();
295  } catch(const Failure &) {
296  addNotification(NotificationType::Critical, "Unable to parse audio track element.", context);
297  break;
298  }
299  switch(subElement->id()) {
301  m_bitsPerSample = subElement->readUInteger();
302  break;
304  m_channelCount = subElement->readUInteger();
305  break;
307  if(!m_samplingFrequency) {
308  m_samplingFrequency = subElement->readFloat();
309  }
310  break;
313  m_extensionSamplingFrequency = subElement->readFloat();
314  }
315  break;
316  default:
317  ;
318  }
319  }
320  break;
322  m_trackNumber = trackInfoElement->readUInteger();
323  break;
325  m_id = trackInfoElement->readUInteger();
326  break;
328  m_name = trackInfoElement->readString();
329  break;
331  m_language = trackInfoElement->readString();
332  break;
334  m_format = codecIdToMediaFormat(m_formatId = trackInfoElement->readString());
335  break;
337  m_formatName = trackInfoElement->readString();
338  break;
340  break; // TODO
342  m_enabled = trackInfoElement->readUInteger();
343  break;
345  m_default = trackInfoElement->readUInteger();
346  break;
348  m_forced = trackInfoElement->readUInteger();
349  break;
351  m_lacing = trackInfoElement->readUInteger();
352  break;
354  defaultDuration = trackInfoElement->readUInteger();
355  break;
356  default:
357  ;
358  }
359  switch(m_mediaType) {
360  case MediaType::Video:
361  if(!m_fps && defaultDuration) {
362  m_fps = 1000000000.0 / static_cast<double>(defaultDuration);
363  }
364  break;
365  default:
366  ;
367  }
368  }
369 
370  // read further information from the CodecPrivate element for some codecs
371  switch(m_format.general) {
372  EbmlElement *codecPrivateElement;
374  if((codecPrivateElement = m_trackElement->childById(MatroskaIds::CodecPrivate))) {
375  // parse bitmap info header to determine actual format
376  if(codecPrivateElement->dataSize() >= 0x28) {
377  m_istream->seekg(codecPrivateElement->dataOffset());
378  BitmapInfoHeader bitmapInfoHeader;
379  bitmapInfoHeader.parse(reader());
380  m_formatId.reserve(m_formatId.size() + 7);
381  m_formatId += " \"";
382  m_formatId += interpretIntegerAsString(bitmapInfoHeader.compression);
383  m_formatId += "\"";
385  } else {
386  addNotification(NotificationType::Critical, "BITMAPINFOHEADER structure (in \"CodecPrivate\"-element) is truncated.", context);
387  }
388  }
389  break;
391  if((codecPrivateElement = m_trackElement->childById(MatroskaIds::CodecPrivate))) {
392  // parse WAVE header to determine actual format
393  if(codecPrivateElement->dataSize() >= 16) {
394  m_istream->seekg(codecPrivateElement->dataOffset());
395  WaveFormatHeader waveFormatHeader;
396  waveFormatHeader.parse(reader());
397  WaveAudioStream::addInfo(waveFormatHeader, *this);
398  } else {
399  addNotification(NotificationType::Critical, "BITMAPINFOHEADER structure (in \"CodecPrivate\"-element) is truncated.", context);
400  }
401  }
402  break;
404  if((codecPrivateElement = m_trackElement->childById(MatroskaIds::CodecPrivate))) {
405  auto audioSpecificConfig = Mp4Track::parseAudioSpecificConfig(*this, *m_istream, codecPrivateElement->dataOffset(), codecPrivateElement->dataSize());
406  m_format += Mpeg4AudioObjectIds::idToMediaFormat(audioSpecificConfig->audioObjectType, audioSpecificConfig->sbrPresent, audioSpecificConfig->psPresent);
407  if(audioSpecificConfig->sampleFrequencyIndex == 0xF) {
408  //m_samplingFrequency = audioSpecificConfig->sampleFrequency;
409  } else if(audioSpecificConfig->sampleFrequencyIndex < sizeof(mpeg4SamplingFrequencyTable)) {
410  //m_samplingFrequency = mpeg4SamplingFrequencyTable[audioSpecificConfig->sampleFrequencyIndex];
411  } else {
412  addNotification(NotificationType::Warning, "Audio specific config has invalid sample frequency index.", context);
413  }
414  if(audioSpecificConfig->extensionSampleFrequencyIndex == 0xF) {
415  //m_extensionSamplingFrequency = audioSpecificConfig->extensionSampleFrequency;
416  } else if(audioSpecificConfig->extensionSampleFrequencyIndex < sizeof(mpeg4SamplingFrequencyTable)) {
417  //m_extensionSamplingFrequency = mpeg4SamplingFrequencyTable[audioSpecificConfig->extensionSampleFrequencyIndex];
418  } else {
419  addNotification(NotificationType::Warning, "Audio specific config has invalid extension sample frequency index.", context);
420  }
421  m_channelConfig = audioSpecificConfig->channelConfiguration;
422  m_extensionChannelConfig = audioSpecificConfig->extensionChannelConfiguration;
423  }
424  break;
426  if((codecPrivateElement = m_trackElement->childById(MatroskaIds::CodecPrivate))) {
427  auto avcConfig = make_unique<Media::AvcConfiguration>();
428  try {
429  m_istream->seekg(codecPrivateElement->dataOffset());
430  avcConfig->parse(m_reader, codecPrivateElement->dataSize());
431  Mp4Track::addInfo(*avcConfig, *this);
432  } catch(const TruncatedDataException &) {
433  addNotification(NotificationType::Critical, "AVC configuration is truncated.", context);
434  } catch(const Failure &) {
435  addNotification(NotificationType::Critical, "AVC configuration is invalid.", context);
436  }
437  }
438  default:
439  ;
440  }
441 
442  // parse format name for unknown formats
444  if(startsWith<string>(m_formatId, "V_") || startsWith<string>(m_formatId, "A_") || startsWith<string>(m_formatId, "S_")) {
445  m_formatName = m_formatId.substr(2);
446  } else {
448  }
449  m_formatName.append(" (unknown)");
450  }
451 
452  // use pixel size as display size if display size not specified
453  if(!m_displaySize.width()) {
455  }
456  if(!m_displaySize.height()) {
458  }
459 }
460 
461 }
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:270
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:271
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:272
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:898
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:551
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:257