From 1f4800c9bef14854cc4ebfa8ae2a53dae74be044 Mon Sep 17 00:00:00 2001 From: Martchus Date: Wed, 15 Jul 2015 00:10:24 +0200 Subject: [PATCH] added detection of ADTS files --- abstracttrack.h | 3 +- adts/adtsstream.cpp | 40 ++++++++++++++ adts/adtsstream.h | 43 +++++++++++++++ mediafileinfo.cpp | 86 +++++++++++------------------- mediafileinfo.h | 16 +++++- mp4/mp4ids.cpp | 57 +++++++++++++++++++- mp4/mp4ids.h | 20 +++++++ mp4/mp4track.cpp | 14 ++--- mpegaudio/mpegaudioframe.cpp | 9 ++-- mpegaudio/mpegaudioframe.h | 6 ++- mpegaudio/mpegaudioframestream.cpp | 25 +-------- mpegaudio/mpegaudioframestream.h | 17 ++++++ signature.cpp | 9 +++- signature.h | 1 + 14 files changed, 249 insertions(+), 97 deletions(-) create mode 100644 adts/adtsstream.cpp create mode 100644 adts/adtsstream.h diff --git a/abstracttrack.h b/abstracttrack.h index 4b40c6b..59fff59 100644 --- a/abstracttrack.h +++ b/abstracttrack.h @@ -32,7 +32,8 @@ enum class TrackType MpegAudioFrameStream, /**< The track is a Media::MpegAudioFrameStream. */ Mp4Track, /**< The track is a Media::Mp4Track. */ WaveAudioStream, /**< The track is a Media::WaveAudioStream. */ - OggStream /**< The track is a Media::OggStream. */ + OggStream, /**< The track is a Media::OggStream. */ + AdtsStream /**< The track is a Media::AdtsStream. */ }; class LIB_EXPORT AbstractTrack : public StatusProvider diff --git a/adts/adtsstream.cpp b/adts/adtsstream.cpp new file mode 100644 index 0000000..adcfd7a --- /dev/null +++ b/adts/adtsstream.cpp @@ -0,0 +1,40 @@ +#include "adtsstream.h" + +#include "../mp4/mp4ids.h" +#include "../exceptions.h" + +#include + +using namespace std; + +namespace Media { + +/*! + * \class Media::AdtsStream + * \brief Implementation of Media::AbstractTrack for ADTS streams. + */ + +void AdtsStream::internalParseHeader() +{ + //static const string context("parsing ADTS frame header"); + if(!m_istream) { + throw NoDataFoundException(); + } + // get size + m_istream->seekg(-128, ios_base::end); + if(m_reader.readUInt24BE() == 0x544147) { + m_size = static_cast(m_istream->tellg()) - 3u - m_startOffset; + } else { + m_size = static_cast(m_istream->tellg()) + 125u - m_startOffset; + } + m_istream->seekg(m_startOffset, ios_base::beg); + // parse frame header + m_firstFrame.parseHeader(m_reader); + m_format = Mpeg4AudioObjectIds::idToMediaFormat(m_firstFrame.mpeg4AudioObjectId()); + m_channelCount = Mpeg4ChannelConfigs::channelCount(m_firstFrame.mpeg4ChannelConfig()); + byte sampleRateIndex = m_firstFrame.mpeg4SampleRateIndex(); + m_sampleRate = sampleRateIndex < sizeof(mpeg4SampleRateTable) ? mpeg4SampleRateTable[sampleRateIndex] : 0; +} + +} // namespace Media + diff --git a/adts/adtsstream.h b/adts/adtsstream.h new file mode 100644 index 0000000..bc46f0a --- /dev/null +++ b/adts/adtsstream.h @@ -0,0 +1,43 @@ +#ifndef MEDIA_ADTSSTREAM_H +#define MEDIA_ADTSSTREAM_H + +#include "adtsframe.h" +#include "../abstracttrack.h" + +namespace Media { + +class LIB_EXPORT AdtsStream : public AbstractTrack +{ +public: + AdtsStream(std::iostream &stream, uint64 startOffset); + ~AdtsStream(); + + TrackType type() const; + +protected: + void internalParseHeader(); + +private: + AdtsFrame m_firstFrame; +}; + +/*! + * \brief Constructs a new track for the \a stream at the specified \a startOffset. + */ +inline AdtsStream::AdtsStream(std::iostream &stream, uint64 startOffset) : + AbstractTrack(stream, startOffset) +{ + m_mediaType = MediaType::Audio; +} + +inline AdtsStream::~AdtsStream() +{} + +inline TrackType AdtsStream::type() const +{ + return TrackType::AdtsStream; +} + +} // namespace Media + +#endif // MEDIA_ADTSSTREAM_H diff --git a/mediafileinfo.cpp b/mediafileinfo.cpp index 11b80c3..054279c 100644 --- a/mediafileinfo.cpp +++ b/mediafileinfo.cpp @@ -5,8 +5,8 @@ #include "id3/id3v1tag.h" #include "id3/id3v2tag.h" #include "wav/waveaudiostream.h" -#include "mpegaudio/mpegaudioframe.h" #include "mpegaudio/mpegaudioframestream.h" +#include "adts/adtsstream.h" #include "mp4/mp4container.h" #include "mp4/mp4atom.h" #include "mp4/mp4tag.h" @@ -131,7 +131,7 @@ startParsingSignature: stream().seekg(m_containerOffset, ios_base::beg); stream().read(buff, sizeof(buff)); // skip zero bytes/padding - if(buff[0] == 0 && buff[1] == 0 && buff[2] == 0 && buff[3] == 0) { + if(!(*reinterpret_cast(buff))) { // give up after 0x100 bytes if(m_paddingSize >= 0x100u) { m_containerParsed = true; @@ -143,11 +143,10 @@ startParsingSignature: goto startParsingSignature; // read signature again } if(m_paddingSize) { - addNotification(NotificationType::Warning, ConversionUtilities::numberToString(m_paddingSize) + " empty bytes skipped.", context); + addNotification(NotificationType::Warning, ConversionUtilities::numberToString(m_paddingSize) + " zero-bytes skipped at the beginning of the file.", context); } // parse signature - m_containerFormat = parseSignature(buff, sizeof(buff)); - switch(m_containerFormat) { + switch(m_containerFormat = parseSignature(buff, sizeof(buff))) { case ContainerFormat::Id2v2Tag: m_actualId3v2TagOffsets.push_back(m_containerOffset); if(m_actualId3v2TagOffsets.size() == 2) { @@ -233,16 +232,18 @@ void MediaFileInfo::parseTracks() } else { switch(m_containerFormat) { case ContainerFormat::RiffWave: - m_waveAudioStream = make_unique(stream(), m_containerOffset); - m_waveAudioStream->parseHeader(); + m_singleTrack = make_unique(stream(), m_containerOffset); break; case ContainerFormat::MpegAudioFrames: - m_mpegAudioFrameStream = make_unique(stream(), m_containerOffset); - m_mpegAudioFrameStream->parseHeader(); + m_singleTrack = make_unique(stream(), m_containerOffset); + break; + case ContainerFormat::Adts: + m_singleTrack = make_unique(stream(), m_containerOffset); break; default: throw NotImplementedException(); } + m_singleTrack->parseHeader(); } } catch (NotImplementedException &) { addNotification(NotificationType::Information, "Parsing tracks is not implemented for the container format of the file.", context); @@ -339,28 +340,32 @@ void MediaFileInfo::parseTags() void MediaFileInfo::parseChapters() { static const string context("parsing chapters"); - if(m_container) { - try { + try { + if(m_container) { m_container->parseChapters(); - } catch (NotImplementedException &) { - addNotification(NotificationType::Information, "Parsing chapters is not implemented for the container format of the file.", context); - } catch (Failure &) { - addNotification(NotificationType::Critical, "Unable to parse chapters.", context); + } else { + throw NotImplementedException(); } + } catch (NotImplementedException &) { + addNotification(NotificationType::Information, "Parsing chapters is not implemented for the container format of the file.", context); + } catch (Failure &) { + addNotification(NotificationType::Critical, "Unable to parse chapters.", context); } } void MediaFileInfo::parseAttachments() { static const string context("parsing attachments"); - if(m_container) { - try { + try { + if(m_container) { m_container->parseAttachments(); - } catch (NotImplementedException &) { - addNotification(NotificationType::Information, "Parsing attachments is not implemented for the container format of the file.", context); - } catch (Failure &) { - addNotification(NotificationType::Critical, "Unable to parse attachments.", context); + } else { + throw NotImplementedException(); } + } catch (NotImplementedException &) { + addNotification(NotificationType::Information, "Parsing attachments is not implemented for the container format of the file.", context); + } catch (Failure &) { + addNotification(NotificationType::Critical, "Unable to parse attachments.", context); } } @@ -540,8 +545,8 @@ const char *MediaFileInfo::containerFormatAbbreviation() const mediaType = hasTracksOfType(MediaType::Video) ? MediaType::Video : MediaType::Audio; break; case ContainerFormat::MpegAudioFrames: - if(m_mpegAudioFrameStream) { - version = m_mpegAudioFrameStream->format().sub; + if(m_singleTrack) { + version = m_singleTrack->format().sub; } break; default: @@ -627,29 +632,6 @@ const char *MediaFileInfo::mimeType() const } } -/*! - * \brief Returns the number of tracks that could be parsed. - * - * parseTracks() needs to be called before. Otherwise this - * method always returns zero. - * - * \sa parseTracks() - */ -size_t MediaFileInfo::trackCount() const -{ - size_t c = 0; - if(m_waveAudioStream) { - ++c; - } - if(m_mpegAudioFrameStream) { - ++c; - } - if(m_container) { - c += m_container->trackCount(); - } - return c; -} - /*! * \brief Returns the tracks for the current file. * @@ -665,11 +647,8 @@ size_t MediaFileInfo::trackCount() const vector MediaFileInfo::tracks() const { vector res; - if(m_waveAudioStream) { - res.push_back(m_waveAudioStream.get()); - } - if(m_mpegAudioFrameStream) { - res.push_back(m_mpegAudioFrameStream.get()); + if(m_singleTrack) { + res.push_back(m_singleTrack.get()); } if(m_container) { for(size_t i = 0, count = m_container->trackCount(); i < count; ++i) { @@ -692,7 +671,7 @@ bool MediaFileInfo::hasTracksOfType(MediaType type) const if(!areTracksParsed()) { return false; } - if(type == MediaType::Audio && (m_waveAudioStream || m_mpegAudioFrameStream)) { + if(type == MediaType::Audio && m_singleTrack) { return true; } else if(m_container) { for(size_t i = 0, count = m_container->trackCount(); i < count; ++i) { @@ -1142,8 +1121,7 @@ void MediaFileInfo::clearParsingResults() m_actualId3v2TagOffsets.clear(); m_actualExistingId3v1Tag = false; m_container.reset(); - m_waveAudioStream.reset(); - m_mpegAudioFrameStream.reset(); + m_singleTrack.reset(); } /*! diff --git a/mediafileinfo.h b/mediafileinfo.h index 79d1fae..87555d6 100644 --- a/mediafileinfo.h +++ b/mediafileinfo.h @@ -142,8 +142,7 @@ private: std::unique_ptr m_container; // fields related to the tracks bool m_tracksParsed; - std::unique_ptr m_waveAudioStream; - std::unique_ptr m_mpegAudioFrameStream; + std::unique_ptr m_singleTrack; // fields related to the tag bool m_tagParsed; std::unique_ptr m_id3v1Tag; @@ -203,6 +202,19 @@ inline bool MediaFileInfo::areTracksParsed() const return m_tracksParsed; } +/*! + * \brief Returns the number of tracks that could be parsed. + * + * parseTracks() needs to be called before. Otherwise this + * method always returns zero. + * + * \sa parseTracks() + */ +inline size_t MediaFileInfo::trackCount() const +{ + return m_singleTrack ? 1 : 0 + m_container->trackCount(); +} + /*! * \brief Returns whether the chapters have been parsed yet. */ diff --git a/mp4/mp4ids.cpp b/mp4/mp4ids.cpp index 4b694cb..8a56b21 100644 --- a/mp4/mp4ids.cpp +++ b/mp4/mp4ids.cpp @@ -223,7 +223,7 @@ const char *streamTypeName(byte streamTypeId) } /*! - * \brief Encapsulates all supported MPEG-4 audio object format IDs. + * \brief Encapsulates all supported MPEG-4 audio object type IDs. * \sa http://wiki.multimedia.cx/index.php?title=MPEG-4_Audio */ namespace Mpeg4AudioObjectIds { @@ -279,6 +279,61 @@ LIB_EXPORT MediaFormat idToMediaFormat(byte mpeg4AudioObjectId, bool sbrPresent, } +uint32 mpeg4SampleRateTable[] = { + 96000, 88200, 64000, 48000, 44100, 32000, + 24000, 22050, 16000, 12000, 11025, 8000, 7350 +}; + +namespace Mpeg4ChannelConfigs { + +const char *channelConfigString(byte config) +{ + switch(config) { + case AotSpecificConfig: + return "defined in AOT Specific Config"; + case FrontCenter: + return "1 channel: front-center"; + case FrontLeftFrontRight: + return "2 channels: front-left, front-right"; + case FrontCenterFrontLeftFrontRight: + return "3 channels: front-center, front-left, front-right"; + case FrontCenterFrontLeftFrontRightBackCenter: + return "4 channels: front-center, front-left, front-right, back-center"; + case FrontCenterFrontLeftFrontRightBackLeftBackRight: + return "5 channels: front-center, front-left, front-right, back-left, back-right"; + case FrontCenterFrontLeftFrontRightBackLeftBackRightLFEChannel: + return "6 channels: front-center, front-left, front-right, back-left, back-right, LFE-channel"; + case FrontCenterFrontLeftFrontRightSideLeftSideRightBackLeftBackRightLFEChannel: + return "8 channels: front-center, front-left, front-right, side-left, side-right, back-left, back-right, LFE-channel"; + default: + return "unknown"; + } +} + +byte channelCount(byte config) +{ + switch(config) { + case FrontCenter: + return 1; + case FrontLeftFrontRight: + return 2; + case FrontCenterFrontLeftFrontRight: + return 3; + case FrontCenterFrontLeftFrontRightBackCenter: + return 4; + case FrontCenterFrontLeftFrontRightBackLeftBackRight: + return 5; + case FrontCenterFrontLeftFrontRightBackLeftBackRightLFEChannel: + return 6; + case FrontCenterFrontLeftFrontRightSideLeftSideRightBackLeftBackRightLFEChannel: + return 8; + default: + return 0; + } +} + +} + /*! * \brief Encapsulates MPEG-4 video (14496-2) codes. */ diff --git a/mp4/mp4ids.h b/mp4/mp4ids.h index 67bf62b..2bcf5d6 100644 --- a/mp4/mp4ids.h +++ b/mp4/mp4ids.h @@ -585,6 +585,26 @@ LIB_EXPORT MediaFormat idToMediaFormat(byte mpeg4AudioObjectId, bool sbrPresent } +extern uint32 mpeg4SampleRateTable[13]; + +namespace Mpeg4ChannelConfigs { +enum Mpeg4ChannelConfig : byte +{ + AotSpecificConfig = 0, + FrontCenter, + FrontLeftFrontRight, + FrontCenterFrontLeftFrontRight, + FrontCenterFrontLeftFrontRightBackCenter, + FrontCenterFrontLeftFrontRightBackLeftBackRight, + FrontCenterFrontLeftFrontRightBackLeftBackRightLFEChannel, + FrontCenterFrontLeftFrontRightSideLeftSideRightBackLeftBackRightLFEChannel +}; + +LIB_EXPORT const char *channelConfigString(byte config); +LIB_EXPORT byte channelCount(byte config); + +} + namespace Mpeg4VideoCodes { enum KnownValue : byte { VideoObjectStart = 0x00, diff --git a/mp4/mp4track.cpp b/mp4/mp4track.cpp index 65f1eed..40dc6a7 100644 --- a/mp4/mp4track.cpp +++ b/mp4/mp4track.cpp @@ -26,10 +26,6 @@ namespace Media { DateTime startDate = DateTime::fromDate(1904, 1, 1); -uint32 sampleRateTable[] = { - 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000 -}; - MediaFormat fmtTable[] = { GeneralMediaFormat::Unknown, MediaFormat(GeneralMediaFormat::Aac, SubFormats::AacMpeg4MainProfile), @@ -1369,15 +1365,15 @@ void Mp4Track::internalParseHeader() m_format += Mpeg4AudioObjectIds::idToMediaFormat(m_esInfo->audioSpecificConfig->audioObjectType, m_esInfo->audioSpecificConfig->sbrPresent, m_esInfo->audioSpecificConfig->psPresent); if(m_esInfo->audioSpecificConfig->sampleFrequencyIndex == 0xF) { m_sampleRate = m_esInfo->audioSpecificConfig->sampleFrequency; - } else if(m_esInfo->audioSpecificConfig->sampleFrequencyIndex < sizeof(sampleRateTable)) { - m_sampleRate = sampleRateTable[m_esInfo->audioSpecificConfig->sampleFrequencyIndex]; + } else if(m_esInfo->audioSpecificConfig->sampleFrequencyIndex < sizeof(mpeg4SampleRateTable)) { + m_sampleRate = mpeg4SampleRateTable[m_esInfo->audioSpecificConfig->sampleFrequencyIndex]; } else { addNotification(NotificationType::Warning, "Audio specific config has invalid sample frequency index.", context); } if(m_esInfo->audioSpecificConfig->extensionSampleFrequencyIndex == 0xF) { m_extensionSampleRate = m_esInfo->audioSpecificConfig->extensionSampleFrequency; - } else if(m_esInfo->audioSpecificConfig->extensionSampleFrequencyIndex < sizeof(sampleRateTable)) { - m_extensionSampleRate = sampleRateTable[m_esInfo->audioSpecificConfig->extensionSampleFrequencyIndex]; + } else if(m_esInfo->audioSpecificConfig->extensionSampleFrequencyIndex < sizeof(mpeg4SampleRateTable)) { + m_extensionSampleRate = mpeg4SampleRateTable[m_esInfo->audioSpecificConfig->extensionSampleFrequencyIndex]; } else { addNotification(NotificationType::Warning, "Audio specific config has invalid extension sample frequency index.", context); } @@ -1397,7 +1393,7 @@ void Mp4Track::internalParseHeader() MpegAudioFrame frame; m_istream->seekg(m_stcoAtom->dataOffset() + 8); m_istream->seekg(m_chunkOffsetSize == 8 ? reader.readUInt64BE() : reader.readUInt32BE()); - frame.parseHeader(*m_istream); + frame.parseHeader(reader); MpegAudioFrameStream::addInfo(frame, *this); break; } default: diff --git a/mpegaudio/mpegaudioframe.cpp b/mpegaudio/mpegaudioframe.cpp index 2fc4ee7..dbf1fc6 100644 --- a/mpegaudio/mpegaudioframe.cpp +++ b/mpegaudio/mpegaudioframe.cpp @@ -28,15 +28,14 @@ const int MpegAudioFrame::m_bitrateTable[0x2][0x3][0xF] = const uint32 MpegAudioFrame::m_sync = 0xFFE00000u; /*! - * \brief Parses the header read from the specified \a stream at the current position. + * \brief Parses the header read using the specified \a reader. * \throws Throws InvalidDataException if the data read from the stream is * no valid frame header. */ -void MpegAudioFrame::parseHeader(istream &stream) +void MpegAudioFrame::parseHeader(BinaryReader &reader) { - BinaryReader reader(&stream); m_header = reader.readUInt32BE(); - stream.seekg(m_xingHeaderOffset - 4, ios_base::cur); + reader.stream()->seekg(m_xingHeaderOffset - 4, ios_base::cur); m_xingHeader = reader.readUInt64BE(); m_xingHeaderFlags = static_cast(m_xingHeader & 0xffffffffuL); if(isXingHeaderAvailable()) { @@ -47,7 +46,7 @@ void MpegAudioFrame::parseHeader(istream &stream) m_xingBytesfield = reader.readUInt32BE(); } if(isXingTocFieldPresent()) { - stream.seekg(64, ios_base::cur); + reader.stream()->seekg(64, ios_base::cur); } if(isXingQualityIndicatorFieldPresent()) { m_xingQualityIndicator = reader.readUInt32BE(); diff --git a/mpegaudio/mpegaudioframe.h b/mpegaudio/mpegaudioframe.h index 2181408..ac6c45d 100644 --- a/mpegaudio/mpegaudioframe.h +++ b/mpegaudio/mpegaudioframe.h @@ -6,6 +6,10 @@ #include +namespace IoUtilities { +class BinaryReader; +} + namespace Media { @@ -35,7 +39,7 @@ class LIB_EXPORT MpegAudioFrame public: MpegAudioFrame(); - void parseHeader(std::istream &stream); + void parseHeader(IoUtilities::BinaryReader &reader); bool isValid() const; double mpegVersion() const; diff --git a/mpegaudio/mpegaudioframestream.cpp b/mpegaudio/mpegaudioframestream.cpp index fedd5ad..ac219ab 100644 --- a/mpegaudio/mpegaudioframestream.cpp +++ b/mpegaudio/mpegaudioframestream.cpp @@ -14,30 +14,9 @@ namespace Media { /*! * \class Media::MpegAudioFrameStream - * \brief Implementation of Media::AbstractTrack for a stream of - * MPEG audio frames (run-of-the-mill MP3 file). + * \brief Implementation of Media::AbstractTrack MPEG audio streams. */ -/*! - * \brief Constructs a new track for the \a stream at the specified \a startOffset. - */ -MpegAudioFrameStream::MpegAudioFrameStream(iostream &stream, uint64 startOffset) : - AbstractTrack(stream, startOffset) -{ - m_mediaType = MediaType::Audio; -} - -/*! - * \brief Destroys the track. - */ -MpegAudioFrameStream::~MpegAudioFrameStream() -{} - -TrackType MpegAudioFrameStream::type() const -{ - return TrackType::MpegAudioFrameStream; -} - /*! * \brief Adds the information from the specified \a frame to the specified \a track. */ @@ -66,7 +45,7 @@ void MpegAudioFrameStream::internalParseHeader() // parse frame header m_frames.emplace_back(); MpegAudioFrame &frame = m_frames.back(); - frame.parseHeader(*m_istream); + frame.parseHeader(m_reader); addInfo(frame, *this); if(frame.isXingBytesfieldPresent()) { uint32 xingSize = frame.xingBytesfield(); diff --git a/mpegaudio/mpegaudioframestream.h b/mpegaudio/mpegaudioframestream.h index 6ab4f05..1809f97 100644 --- a/mpegaudio/mpegaudioframestream.h +++ b/mpegaudio/mpegaudioframestream.h @@ -26,6 +26,23 @@ private: std::list m_frames; }; +/*! + * \brief Constructs a new track for the \a stream at the specified \a startOffset. + */ +inline MpegAudioFrameStream::MpegAudioFrameStream(std::iostream &stream, uint64 startOffset) : + AbstractTrack(stream, startOffset) +{ + m_mediaType = MediaType::Audio; +} + +inline MpegAudioFrameStream::~MpegAudioFrameStream() +{} + +inline TrackType MpegAudioFrameStream::type() const +{ + return TrackType::MpegAudioFrameStream; +} + } #endif // MPEGAUDIOFRAMESTREAM_H diff --git a/signature.cpp b/signature.cpp index fa478ef..54c6f0c 100644 --- a/signature.cpp +++ b/signature.cpp @@ -66,7 +66,7 @@ enum Sig24 : uint32 Bzip2 = 0x425A68u, Gzip = 0x1F8B08u, Id3v2 = 0x494433u, - Utf8Text = 0xEFBBBFu + Utf8Text = 0xEFBBBFu, }; /*! @@ -74,6 +74,8 @@ enum Sig24 : uint32 */ enum Sig16 : uint16 { + Adts = 0xFFF0u, + AdtsMask = 0xFFF6u, Jpeg = 0xffd8u, Lha = 0x1FA0u, Lzw = 0x1F9Du, @@ -202,6 +204,9 @@ ContainerFormat parseSignature(const char *buffer, int bufferSize) default: ; } + if(((sig >> 48) & AdtsMask) == Adts) { + return ContainerFormat::Adts; + } return ContainerFormat::Unknown; } @@ -282,6 +287,8 @@ const char *containerFormatAbbreviation(ContainerFormat containerFormat, MediaTy const char *containerFormatName(ContainerFormat containerFormat) { switch(containerFormat) { + case ContainerFormat::Adts: + return "Audio Data Transport Stream"; case ContainerFormat::Asf: return "Advanced Systems Format"; case ContainerFormat::Elf: diff --git a/signature.h b/signature.h index 6d0ff44..72b8447 100644 --- a/signature.h +++ b/signature.h @@ -14,6 +14,7 @@ namespace Media { enum class ContainerFormat { Unknown, /**< unknown container format */ + Adts, /** < Audio Data Transport Stream */ Asf, /**< Advanced Systems Format */ Bzip2, /** bzip2 compressed file */ Elf, /**< Executable and Linkable Format */