From c452b009ae5c46d853beab528dce7dc5b81b8401 Mon Sep 17 00:00:00 2001 From: Martchus Date: Sun, 7 Jun 2015 00:18:28 +0200 Subject: [PATCH] improved media format detection; other minor improvements --- abstracttrack.cpp | 64 +----- abstracttrack.h | 3 +- avc/avcinfo.cpp | 9 + genericfileelement.h | 112 +++++++++- matroska/ebmlelement.cpp | 9 +- matroska/ebmlelement.h | 6 + matroska/matroskatrack.cpp | 154 ++++++++++++- matroska/matroskatrack.h | 2 + mediafileinfo.cpp | 23 +- mediaformat.cpp | 334 +++++++++++++++++++++++------ mediaformat.h | 208 ++++++++++++++---- mp4/mp4atom.cpp | 9 +- mp4/mp4atom.h | 6 + mp4/mp4ids.cpp | 150 ++++++++++++- mp4/mp4ids.h | 245 ++++++++++++++++++--- mp4/mp4tag.cpp | 11 +- mp4/mp4track.cpp | 245 +++++++++++++-------- mp4/mp4track.h | 99 ++++++++- mp4/mpeg4descriptor.cpp | 87 +++++++- mp4/mpeg4descriptor.h | 102 ++++++++- mpegaudio/mpegaudioframestream.cpp | 16 +- ogg/oggstream.cpp | 10 +- signature.cpp | 12 +- size.h | 2 +- tagparser.pro | 6 +- wav/waveaudiostream.cpp | 12 +- 26 files changed, 1583 insertions(+), 353 deletions(-) diff --git a/abstracttrack.cpp b/abstracttrack.cpp index 0bf257d..f866cd9 100644 --- a/abstracttrack.cpp +++ b/abstracttrack.cpp @@ -32,7 +32,7 @@ AbstractTrack::AbstractTrack(istream &inputStream, ostream &outputStream, uint64 m_writer(BinaryWriter(&outputStream)), m_startOffset(startOffset), m_headerValid(false), - m_format(MediaFormat::Unknown), + m_format(), m_mediaType(MediaType::Unknown), m_version(0.0), m_size(0), @@ -79,22 +79,13 @@ AbstractTrack::~AbstractTrack() /*! * \brief Returns the format of the track as C-style string if known; otherwise * returns the format abbreviation or an empty string. + * \remarks + * - The caller must not free the returned string. + * - The string might get invalidated when the track is (re)parsed. */ const char *AbstractTrack::formatName() const { - if(!m_formatName.empty()) { - return m_formatName.c_str(); - } - const char *formatName = mediaFormatName(m_format); - if(*formatName == 0) { - if(!m_formatId.empty()) { - return m_formatId.c_str(); - } else { - return "unknown"; - } - } else { - return formatName; - } + return m_format || m_formatName.empty() ? m_format.name() : m_formatName.c_str(); } /*! @@ -103,43 +94,8 @@ const char *AbstractTrack::formatName() const */ const char *AbstractTrack::formatAbbreviation() const { - if(!m_formatId.empty()) { - return m_formatId.c_str(); - } - switch(m_format) { - case MediaFormat::Pcm: - return "PCM"; - case MediaFormat::Mpeg1: - return "MPEG-1"; - case MediaFormat::Mpeg2: - return "MPEG-2"; - case MediaFormat::MpegL1: - return "MP1"; - case MediaFormat::MpegL2: - return "MP2"; - case MediaFormat::MpegL3: - return "MP3"; - case MediaFormat::Aac: - return "AAC"; - case MediaFormat::Png: - return "PNG"; - case MediaFormat::Jpeg: - return "JPEG"; - case MediaFormat::Mpeg4Avc: - return "AVC"; - case MediaFormat::Mpeg4Asp: - return "ASP"; - case MediaFormat::Mpeg4: - return "MPEG-4"; - case MediaFormat::Gif: - return "GIF"; - case MediaFormat::Tiff: - return "TIFF"; - case MediaFormat::Ac3: - return "AC3"; - default: - return ""; - } + const char *abbr = m_format.abbreviation(); + return *abbr || m_formatId.empty() ? m_format.abbreviation() : m_formatId.c_str(); } /*! @@ -148,11 +104,11 @@ const char *AbstractTrack::formatAbbreviation() const const char *AbstractTrack::mediaTypeName() const { switch(m_mediaType) { - case MediaType::Acoustic: + case MediaType::Audio: return "Audio"; - case MediaType::Visual: + case MediaType::Video: return "Video"; - case MediaType::Textual: + case MediaType::Text: return "Subititle"; case MediaType::Hint: return "Hint"; diff --git a/abstracttrack.h b/abstracttrack.h index ae54d38..6dcd8fe 100644 --- a/abstracttrack.h +++ b/abstracttrack.h @@ -4,6 +4,7 @@ #include "statusprovider.h" #include "size.h" #include "margin.h" +#include "mediaformat.h" #include #include @@ -17,7 +18,7 @@ namespace Media { enum class MediaType; -enum class MediaFormat; +enum class GeneralMediaFormat; /*! * \brief Specifies the track type. diff --git a/avc/avcinfo.cpp b/avc/avcinfo.cpp index 82f46a6..bcf7891 100644 --- a/avc/avcinfo.cpp +++ b/avc/avcinfo.cpp @@ -4,6 +4,15 @@ namespace Media { +/*! + * \struct Media::SpsInfo + * \brief The SpsInfo struct holds the sequence parameter set. + */ + +/*! + * \brief SpsInfo::parse + * \param stream + */ void SpsInfo::parse(std::istream &stream) { static auto highLevelProfileIds = std::unordered_map { diff --git a/genericfileelement.h b/genericfileelement.h index 2dfd8b4..9007fd2 100644 --- a/genericfileelement.h +++ b/genericfileelement.h @@ -27,6 +27,72 @@ namespace Media { template class GenericFileElement; + +/*! + * \class Media::FileElementIterator + * \brief The FileElementIterator class helps iterating through the childs of a FileElement. + */ +template +class FileElementIterator +{ +public: + FileElementIterator(ImplementationType *element = nullptr); + + ImplementationType *operator *(); + const ImplementationType *operator *() const; + operator bool() const; + FileElementIterator &operator ++(); + +private: + ImplementationType *m_current; +}; + +/*! + * \brief Constructs a new iterator for the specified \a element. + */ +template +inline FileElementIterator::FileElementIterator(ImplementationType *element) : + m_current(element) +{} + +/*! + * \brief Returns a reference to the current element. + */ +template +inline ImplementationType *FileElementIterator::operator *() +{ + return m_current; +} + +/*! + * \brief Returns a reference the current element (constant). + */ +template +inline const ImplementationType *FileElementIterator::operator *() const +{ + return m_current; +} + +/*! + * \brief Moves to the next sibling. + */ +template +inline FileElementIterator &FileElementIterator::operator ++() +{ + m_current->parse(); // ensure the current element has been parsed + m_current = m_current->nextSibling(); + return *this; +} + +/*! + * \brief Returns whether the iterator points to an element. + */ +template +inline FileElementIterator::operator bool() const +{ + return m_current != nullptr; +} + /*! * \class Media::FileElementTraits * \brief Defines traits for the specified \a ImplementationType. @@ -66,7 +132,7 @@ public: /*! * \brief Specifies the type used to store data sizes. */ - typedef uint64 dataSizeType; + typedef typename FileElementTraits::dataSizeType dataSizeType; /*! * \brief Specifies the type of the actual implementation. @@ -106,6 +172,10 @@ public: implementationType* subelementByPath(std::list &path); implementationType* childById(const identifierType &id); implementationType* siblingById(const identifierType &id, bool includeThis = false); + FileElementIterator begin(); + FileElementIterator end(); + const FileElementIterator begin() const; + const FileElementIterator end() const; bool isParent() const; bool isPadding() const; uint64 firstChildOffset() const; @@ -140,6 +210,7 @@ private: /*! * \brief Constructs a new top level file element with the specified \a container at the specified \a startOffset. + * \remarks The available size is obtained using the stream of the \a container. */ template GenericFileElement::GenericFileElement(GenericFileElement::containerType &container, uint64 startOffset) : @@ -339,6 +410,9 @@ inline uint64 GenericFileElement::totalSize() const /*! * \brief Returns maximum total size. + * + * This is usually the size of the file for top-level elements and + * the remaining size of the parent for non-top-level elements. */ template inline uint64 GenericFileElement::maxTotalSize() const @@ -528,6 +602,42 @@ typename GenericFileElement::implementationType *GenericFile return nullptr; } +/*! + * \brief Returns an iterator for iterating over the element's childs. + */ +template +FileElementIterator::implementationType> GenericFileElement::begin() +{ + return FileElementIterator(firstChild()); +} + +/*! + * \brief Returns an iterator for iterating over the element's childs (constant). + */ +template +const FileElementIterator::implementationType> GenericFileElement::begin() const +{ + return FileElementIterator(firstChild()); +} + +/*! + * \brief Returns an invalid iterator. + */ +template +FileElementIterator::implementationType> GenericFileElement::end() +{ + return FileElementIterator(); +} + +/*! + * \brief Returns an invalid iterator. + */ +template +const FileElementIterator::implementationType> GenericFileElement::end() const +{ + return FileElementIterator(); +} + /*! * \brief Returns an indication whether this instance is a parent element. */ diff --git a/matroska/ebmlelement.cpp b/matroska/ebmlelement.cpp index 2259456..1b6cc0c 100644 --- a/matroska/ebmlelement.cpp +++ b/matroska/ebmlelement.cpp @@ -34,6 +34,13 @@ EbmlElement::EbmlElement(MatroskaContainer &container, uint64 startOffset) : GenericFileElement(container, startOffset) {} +/*! + * \brief Constructs a new top level element with the specified \a container at the specified \a startOffset. + */ +EbmlElement::EbmlElement(MatroskaContainer &container, uint64 startOffset, uint64 maxSize) : + GenericFileElement(container, startOffset, maxSize) +{} + /*! * \brief Constructs a new sub level element with the specified \a parent at the specified \a startOffset. */ @@ -129,7 +136,7 @@ void EbmlElement::internalParse() if(parent()) { m_nextSibling.reset(new EbmlElement(*(parent()), startOffset() + totalSize())); } else { - m_nextSibling = make_unique(container(), startOffset() + totalSize()); + m_nextSibling.reset(new EbmlElement(container(), startOffset() + totalSize(), maxTotalSize() - totalSize())); } } else { m_nextSibling.reset(); diff --git a/matroska/ebmlelement.h b/matroska/ebmlelement.h index 9f66eff..1af8b33 100644 --- a/matroska/ebmlelement.h +++ b/matroska/ebmlelement.h @@ -35,6 +35,11 @@ public: */ typedef uint32 identifierType; + /*! + * \brief The type used to store element sizes is an unsigned 64-bit integer. + */ + typedef uint64 dataSizeType; + /*! * \brief The implementation type is EbmlElement. */ @@ -67,6 +72,7 @@ public: protected: EbmlElement(EbmlElement &parent, uint64 startOffset); + EbmlElement(MatroskaContainer &container, uint64 startOffset, uint64 maxSize); void internalParse(); diff --git a/matroska/matroskatrack.cpp b/matroska/matroskatrack.cpp index f766043..ca30c25 100644 --- a/matroska/matroskatrack.cpp +++ b/matroska/matroskatrack.cpp @@ -6,7 +6,10 @@ #include "../mediaformat.h" #include "../exceptions.h" +#include + using namespace std; +using namespace ConversionUtilities; namespace Media { @@ -37,6 +40,144 @@ TrackType MatroskaTrack::type() const return TrackType::MatroskaTrack; } +/*! + * \brief Returns the MediaFormat for the specified Matroska codec ID. + */ +MediaFormat MatroskaTrack::codecIdToMediaFormat(const string &codecId) +{ + auto parts = splitString >(codecId, "/", EmptyPartsTreat::Keep, 3); + parts.resize(3); + const auto &part1 = parts[0], &part2 = parts[1], &part3 = parts[2]; + MediaFormat fmt; + if(part1 == "V_MS" && part2 == "VFW" && part3 == "FOURCC") { + fmt.general = GeneralMediaFormat::MicrosoftVideoCodecManager; + } else if(part1 == "V_UNCOMPRESSED") { + fmt.general = GeneralMediaFormat::UncompressedVideoFrames; + } else if(part1 == "V_MPEG4") { + fmt.general = GeneralMediaFormat::Mpeg4Video; + if(part2 == "ISO") { + if(part3 == "SP") { + fmt.sub = SubFormats::Mpeg4Sp; + } else if(part3 == "ASP") { + fmt.sub = SubFormats::Mpeg4Asp; + } else if(part3 == "AP") { + fmt.sub = SubFormats::Mpeg4Avc; + } + } else if(part2 == "MS" && part3 == "V3") { + fmt.sub = SubFormats::Mpeg4MsV3; + } + } else if(part1 == "V_MPEG1") { + fmt.general = GeneralMediaFormat::Mpeg1Video; + } else if(part1 == "V_MPEG2") { + fmt.general = GeneralMediaFormat::Mpeg2Video; + } else if(part1 == "V_REAL") { + fmt.general = GeneralMediaFormat::RealVideo; + } else if(part1 == "V_QUICKTIME") { + fmt.general = GeneralMediaFormat::QuicktimeVideo; + } else if(part1 == "V_THEORA") { + fmt.general = GeneralMediaFormat::Theora; + } else if(part1 == "V_PRORES") { + fmt.general = GeneralMediaFormat::ProRes; + } else if(part1 == "A_MPEG") { + fmt.general = GeneralMediaFormat::Mpeg1Audio; + if(part2 == "L1") { + fmt.sub = SubFormats::Mpeg1Layer1; + } else if(part2 == "L2") { + fmt.sub = SubFormats::Mpeg1Layer2; + } else if(part2 == "L3") { + fmt.sub = SubFormats::Mpeg1Layer3; + } + } else if(part1 == "A_PCM") { + fmt.general = GeneralMediaFormat::Pcm; + if(part2 == "INT") { + if(part3 == "BIG") { + fmt.sub = SubFormats::PcmIntBe; + } else if(part3 == "LIT") { + fmt.sub = SubFormats::PcmIntLe; + } + } else if (part2 == "FLOAT" && part3 == "IEEE") { + fmt.sub = SubFormats::PcmFloatIeee; + } + } else if(part1 == "A_MPC") { + fmt.general = GeneralMediaFormat::Mpc; + } else if(part1 == "A_AC3") { + fmt.general = GeneralMediaFormat::Ac3; + } else if(part1 == "A_ALAC") { + fmt.general = GeneralMediaFormat::Alac; + } else if(part1 == "A_DTS") { + fmt.general = GeneralMediaFormat::Dts; + if(part2 == "EXPRESS") { + fmt.sub = SubFormats::DtsExpress; + } else if(part2 == "LOSSLESS") { + fmt.sub = SubFormats::DtsLossless; + } + } else if(part1 == "A_VORBIS") { + fmt.general = GeneralMediaFormat::Vorbis; + } else if(part1 == "A_FLAC") { + fmt.general = GeneralMediaFormat::Flac; + } else if(part1 == "A_REAL") { + fmt.general = GeneralMediaFormat::RealAudio; + } else if(part1 == "A_MS" && part2 == "ACM") { + fmt.general = GeneralMediaFormat::MicrosoftAudioCodecManager; + } else if(part1 == "A_AAC") { + fmt.general = GeneralMediaFormat::Aac; + if(part2 == "MPEG2") { + if(part3 == "MAIN") { + fmt.sub = SubFormats::AacMpeg2MainProfile; + } else if(part3 == "LC") { + fmt.sub = SubFormats::AacMpeg2LowComplexityProfile; + } else if(part3 == "SBR") { + fmt.sub = SubFormats::AacMpeg2SpectralBandReplicationProfile; + } else if(part3 == "SSR") { + fmt.sub = SubFormats::AacMpeg2ScalableSamplingRateProfile; + } + } else if(part2 == "MPEG4") { + if(part3 == "MAIN") { + fmt.sub = SubFormats::AacMpeg4MainProfile; + } else if(part3 == "LC") { + fmt.sub = SubFormats::AacMpeg4LowComplexityProfile; + } else if(part3 == "SBR") { + fmt.sub = SubFormats::AacMpeg4SpectralBandReplicationProfile; + } else if(part3 == "SSR") { + fmt.sub = SubFormats::AacMpeg4ScalableSamplingRateProfile; + } else if(part3 == "LTP") { + fmt.sub = SubFormats::AacMpeg4LongTermPredictionProfile; + } + } + } else if(part1 == "A_QUICKTIME") { + fmt.general = GeneralMediaFormat::QuicktimeAudio; + } else if(part1 == "A_TTA1") { + fmt.general = GeneralMediaFormat::Tta; + } else if(part1 == "A_WAVPACK4") { + fmt.general = GeneralMediaFormat::WavPack; + } else if(part1 == "S_TEXT") { + fmt.general = GeneralMediaFormat::TextSubtitle; + if(part2 == "UTF8") { + fmt.sub = SubFormats::TextSubBasicUtf8; + } else if(part2 == "SSA") { + fmt.sub = SubFormats::TextSubSubtitlesFormat; + } else if(part2 == "ASS") { + fmt.sub = SubFormats::TextSubAdvancedSubtitlesFormat; + } else if(part2 == "USF") { + fmt.sub = SubFormats::TextSubUniversalSubtitleFormat; + } + } else if(part1 == "S_IMAGE") { + fmt.general = GeneralMediaFormat::ImageSubtitle; + if(part2 == "BMP") { + fmt.sub = SubFormats::ImgSubBmp; + } + } else if(part1 == "S_VOBSUB") { + fmt.general = GeneralMediaFormat::VobSub; + } else if(part1 == "S_KATE") { + fmt.general = GeneralMediaFormat::OggKate; + } else if(part1 == "B_VOBBTN") { + fmt.general = GeneralMediaFormat::VobBtn; + } else if(part1 == "V_MSWMV") { + fmt.general = GeneralMediaFormat::Vc1; + } + return fmt; +} + void MatroskaTrack::internalParseHeader() { const string context("parsing header of Matroska track"); @@ -61,13 +202,13 @@ void MatroskaTrack::internalParseHeader() case MatroskaIds::TrackType: switch(trackInfoElement->readUInteger()) { case MatroskaTrackType::Video: - m_mediaType = MediaType::Visual; + m_mediaType = MediaType::Video; break; case MatroskaTrackType::Audio: - m_mediaType = MediaType::Acoustic; + m_mediaType = MediaType::Audio; break; case MatroskaTrackType::Subtitle: - m_mediaType = MediaType::Textual; + m_mediaType = MediaType::Text; break; default: m_mediaType = MediaType::Unknown; @@ -158,7 +299,10 @@ void MatroskaTrack::internalParseHeader() m_language = trackInfoElement->readString(); break; case MatroskaIds::CodecID: - m_formatId = trackInfoElement->readString(); + m_format = codecIdToMediaFormat(m_formatId = trackInfoElement->readString()); + if(m_formatName.empty()) { + m_formatName = m_format ? string(m_format.name()) : m_formatId; + } break; case MatroskaIds::CodecName: m_formatName = trackInfoElement->readString(); @@ -181,7 +325,7 @@ void MatroskaTrack::internalParseHeader() default: ; } switch(m_mediaType) { - case MediaType::Visual: + case MediaType::Video: if(m_fps == 0 && defaultDuration != 0) { m_fps = 1000000000.0 / static_cast(defaultDuration); } diff --git a/matroska/matroskatrack.h b/matroska/matroskatrack.h index deea1da..06c9684 100644 --- a/matroska/matroskatrack.h +++ b/matroska/matroskatrack.h @@ -18,6 +18,8 @@ public: TrackType type() const; + static MediaFormat codecIdToMediaFormat(const std::string &codecId); + protected: void internalParseHeader(); diff --git a/mediafileinfo.cpp b/mediafileinfo.cpp index 859b1df..11b80c3 100644 --- a/mediafileinfo.cpp +++ b/mediafileinfo.cpp @@ -537,18 +537,11 @@ const char *MediaFileInfo::containerFormatAbbreviation() const case ContainerFormat::Ogg: case ContainerFormat::Matroska: case ContainerFormat::Mp4: - mediaType = hasTracksOfType(MediaType::Visual) ? MediaType::Visual : MediaType::Acoustic; + mediaType = hasTracksOfType(MediaType::Video) ? MediaType::Video : MediaType::Audio; break; case ContainerFormat::MpegAudioFrames: if(m_mpegAudioFrameStream) { - switch(m_mpegAudioFrameStream->format()) { - case MediaFormat::MpegL1: - version = 1; - case MediaFormat::MpegL2: - version = 2; - default: - version = 3; - } + version = m_mpegAudioFrameStream->format().sub; } break; default: @@ -597,17 +590,17 @@ const char *MediaFileInfo::mimeType() const case ContainerFormat::MpegAudioFrames: return "audio/mpeg"; case ContainerFormat::Mp4: - if(hasTracksOfType(MediaType::Visual)) { + if(hasTracksOfType(MediaType::Video)) { return "video/mp4"; } return "audio/mp4"; case ContainerFormat::Ogg: - if(hasTracksOfType(MediaType::Visual)) { + if(hasTracksOfType(MediaType::Video)) { return "video/ogg"; } return "audio/ogg"; case ContainerFormat::Matroska: - if(hasTracksOfType(MediaType::Visual)) { + if(hasTracksOfType(MediaType::Video)) { return "video/x-matroska"; } return "audio/x-matroska"; @@ -699,7 +692,7 @@ bool MediaFileInfo::hasTracksOfType(MediaType type) const if(!areTracksParsed()) { return false; } - if(type == MediaType::Acoustic && (m_waveAudioStream || m_mpegAudioFrameStream)) { + if(type == MediaType::Audio && (m_waveAudioStream || m_mpegAudioFrameStream)) { return true; } else if(m_container) { for(size_t i = 0, count = m_container->trackCount(); i < count; ++i) { @@ -907,6 +900,7 @@ bool MediaFileInfo::areChaptersSupported() const } switch(m_containerFormat) { case ContainerFormat::Matroska: + case ContainerFormat::Webm: return true; default: return false; @@ -923,6 +917,7 @@ bool MediaFileInfo::areAttachmentsSupported() const } switch(m_containerFormat) { case ContainerFormat::Matroska: + case ContainerFormat::Webm: return true; default: return false; @@ -943,6 +938,7 @@ bool MediaFileInfo::areTracksSupported() const case ContainerFormat::RiffWave: case ContainerFormat::Ogg: case ContainerFormat::Matroska: + case ContainerFormat::Webm: return true; default: return false; @@ -962,6 +958,7 @@ bool MediaFileInfo::areTagsSupported() const case ContainerFormat::MpegAudioFrames: case ContainerFormat::Ogg: case ContainerFormat::Matroska: + case ContainerFormat::Webm: return true; default: return false; diff --git a/mediaformat.cpp b/mediaformat.cpp index d774edf..1972f68 100644 --- a/mediaformat.cpp +++ b/mediaformat.cpp @@ -2,74 +2,282 @@ namespace Media { +using namespace SubFormats; + /*! - * \brief Returns the name of the specified media format as C-style string. + * \class Media::MediaFormat + * \brief The MediaFormat class specifies the format of media data. + */ + +/*! + * \brief Returns the name of the media format as C-style string. * * Returns an empty string if no name is available. */ -const char *mediaFormatName(MediaFormat mediaFormat) +const char *MediaFormat::name() const { - switch(mediaFormat) { - case MediaFormat::Pcm: - return "Puls-Code-Modulation"; - case MediaFormat::Mpeg1: - return "MPEG-1"; - case MediaFormat::Mpeg2: - return "MPEG-2"; - case MediaFormat::MpegL1: - return "MPEG-1 Layer 1"; - case MediaFormat::MpegL2: - return "MPEG-1 Layer 2"; - case MediaFormat::MpegL3: - return "MPEG-1 Layer 3"; - case MediaFormat::Aac: - return "Advanced Audio Coding"; - case MediaFormat::Vorbis: - return "Vorbis"; - case MediaFormat::Png: - return "Portable Network Graphics"; - case MediaFormat::Jpeg: - return "JPEG File Interchange Format"; - case MediaFormat::Mpeg4Sp: - return "H.264/MPEG-4 Simple profile"; - case MediaFormat::Mpeg4Avc: - return "H.264/MPEG-4 Advanced Video Coding"; - case MediaFormat::Mpeg4Asp: - return "H.263/MPEG-4 Advanced Simple Profile"; - case MediaFormat::Mpeg4: - return "MPEG-4"; - case MediaFormat::Gif: - return "Graphics Interchange Format"; - case MediaFormat::Tiff: - return "Tagged Image File Format"; - case MediaFormat::UncompressedRgb: - return "Uncompressed RGB"; - case MediaFormat::AdpcmAcm: - return "Microsoft ADPCM-ACM code 2"; - case MediaFormat::ImaadpcmAcm: - return "DVI/Intel IMAADPCM-ACM code 17"; - case MediaFormat::Ac3: - return "Dolby Digital (AC-3)"; - case MediaFormat::Ac4: - return "Dolby Digital (AC-4)"; - case MediaFormat::RealVideo: - return "Real Video"; - case MediaFormat::RealAudio: - return "Real Audio"; - case MediaFormat::QuicktimeVideo: - return "Quicktime video"; - case MediaFormat::QuicktimeAudio: - return "Quicktime audio"; - case MediaFormat::Dts: - return "Digital Theatre System"; - case MediaFormat::Theora: - return "Theora"; - case MediaFormat::ProRes: - return "Apple ProRes"; - case MediaFormat::Alac: - return "Apple lossless audio codec"; - default: - return ""; + switch(general) { + case GeneralMediaFormat::Aac: + switch(sub) { + case AacMpeg2MainProfile: return "Advanced Audio Coding Main Profile"; + case AacMpeg2LowComplexityProfile: return "Advanced Audio Coding Low Complexity Profile"; + case AacMpeg2SpectralBandReplicationProfile: return "Advanced Audio Coding Low Complexity with Spectral Band Replication Profile"; + case AacMpeg2ScalableSamplingRateProfile: return "Advanced Audio Coding Scalable Sampling Rate Profile"; + case AacMpeg4MainProfile: return "Advanced Audio Coding Main Profile"; + case AacMpeg4LowComplexityProfile: return "Advanced Audio Coding Low Complexity Profile"; + case AacMpeg4SpectralBandReplicationProfile: return "Advanced Audio Coding Low Complexity with Spectral Band Replication Profile"; + case AacMpeg4ScalableSamplingRateProfile: return "Advanced Audio Coding Scaleable Sampling Rate Profile"; + case AacMpeg4LongTermPredictionProfile: return "Advanced Audio Coding Scalable Sampling Rate Profile"; + default: return "Advanced Audio Coding"; + } + case GeneralMediaFormat::Ac3: return "Dolby Digital"; + case GeneralMediaFormat::Ac4: return "AC-4"; + case GeneralMediaFormat::AdpcmAcm: return "ADPCM ACM"; + case GeneralMediaFormat::AfxStream: return "AFX Stream"; + case GeneralMediaFormat::Alac: return "Apple Lossless Audio Codec"; + case GeneralMediaFormat::Als: return "ALS"; + case GeneralMediaFormat::Bitmap: return "Windows Bitmap"; + case GeneralMediaFormat::Dirac: return "Dirac"; + case GeneralMediaFormat::Dts: return "DTS"; + switch(sub) { + case DtsLossless: return "DTS Lossless"; + case DtsExpress: return "DTS Express"; + default: return "DTS"; + } + case GeneralMediaFormat::DtsHd: return "DTS-HD"; + switch(sub) { + case DtsHdHighResolution: return "DTS-HD High Resolution"; + case DtsHdMasterAudio: return "DTS-HD Master Audio"; + case DtsExpress: return "DTS-HD Express"; + default: return "DTS-HD"; + } + case GeneralMediaFormat::EAc3: return "Dolby Digital Plus"; + case GeneralMediaFormat::Evrc: return "EVRC"; + case GeneralMediaFormat::Flac: return "Free Lossless Audio Codec"; + case GeneralMediaFormat::FontDataStream: return "Font Data Stream"; + case GeneralMediaFormat::Gif: return "GIF"; + case GeneralMediaFormat::Gpp2Cmf: return "3GPP2 Compact Multimedia Format (CMF)"; + case GeneralMediaFormat::ImaadpcmAcm: return "IMAADPCM ACM"; + case GeneralMediaFormat::ImageSubtitle: + switch(sub) { + case SubFormats::ImgSubBmp: return "Bitmap subtitle"; + default: return "Image subtitle"; + } + case GeneralMediaFormat::InteractionStream: return "Interaction Stream"; + case GeneralMediaFormat::Jpeg: return "JPEG"; + case GeneralMediaFormat::OggKate: return "Karaoke And Text Encapsulation"; + case GeneralMediaFormat::MicrosoftAudioCodecManager: return "Microsoft Audio Codec Manager"; + case GeneralMediaFormat::MicrosoftVideoCodecManager: return "Microsoft Video Codec Manager"; + case GeneralMediaFormat::Mpeg1Audio: + switch(sub) { + case Mpeg1Layer1: return "MPEG-1 Layer 1"; + case Mpeg1Layer2: return "MPEG-1 Layer 2"; + case Mpeg1Layer3: return "MPEG-1 Layer 3"; + default: return "MPEG-1 Audio"; + } + case GeneralMediaFormat::Mpeg1Video: return "MPEG-1 Video"; + case GeneralMediaFormat::Mpeg2Audio: + switch(sub) { + case Mpeg1Layer1: return "MPEG-2 Layer 1"; + case Mpeg1Layer2: return "MPEG-2 Layer 2"; + case Mpeg1Layer3: return "MPEG-2 Layer 3"; + default: return "MPEG-2 Audio"; + } + case GeneralMediaFormat::Mpeg2Video: + switch(sub) { + case Mpeg2SimpleProfile: return "MPEG-2 Video Simple Profile"; + case Mpeg2MainProfile: return "MPEG-2 Video Main Profile"; + case Mpeg2SnrProfile: return "MPEG-2 Video SNR Profile"; + case Mpeg2SpatialProfile: return "MPEG-2 Video Spatial Profile"; + case Mpeg2HighProfile: return "MPEG-2 Video High Profile"; + case Mpeg2422Profile: return "MPEG-2 Video 422 Profile"; + default: return "MPEG-2 Video"; + } + case GeneralMediaFormat::Mpeg4Video: + switch(sub) { + case Mpeg4Sp: return "MPEG-4 Simple Profile"; + case Mpeg4Asp: return "MPEG-4 Advanced Simple Profile"; + case Mpeg4Avc: return "MPEG-4 Advanced Video Coding"; + case Mpeg4AvcParams: return "Parameter for MPEG-4 Advanced Video Coding"; + case Mpeg4MsV3: return "MPEG-4 Microsoft V3"; + default: return "MPEG-4 Visual"; + } + case GeneralMediaFormat::Mpc: return "Musepack SV8"; + case GeneralMediaFormat::Pcm: + switch(sub) { + case PcmIntBe: return "Pulse Code Modulation (integer, big endian)"; + case PcmIntLe: return "Pulse Code Modulation (integer, little endian)"; + case PcmFloatIeee: return "Pulse Code Modulation (float, IEEE)"; + default: return "Pulse Code Modulation"; + } + case GeneralMediaFormat::Png: return "Portable Network Graphics"; + case GeneralMediaFormat::ProRes: return "ProRes"; + case GeneralMediaFormat::Qcelp: return "QCELP"; + case GeneralMediaFormat::QuicktimeAudio: return "Quicktime Audio"; + case GeneralMediaFormat::QuicktimeVideo: return "Quicktime Video"; + case GeneralMediaFormat::RealAudio: return "Real Audio"; + case GeneralMediaFormat::RealVideo: return "Real Video"; + case GeneralMediaFormat::Sa0c: return "SAOC"; + case GeneralMediaFormat::Smv: return "SMV"; + case GeneralMediaFormat::StreamingTextStream: return "Streaming Text Stream"; + case GeneralMediaFormat::SynthesizedTextureStream: return "Synthesized Texture Stream"; + case GeneralMediaFormat::Systems: return "Systems"; + case GeneralMediaFormat::TextSubtitle: + switch(sub) { + case SubFormats::TextSubBasicUtf8: return "UTF-8 Plain Text subtitles"; + case SubFormats::TextSubSubtitlesFormat: return "Subtitles Format"; + case SubFormats::TextSubAdvancedSubtitlesFormat: return "Advanced Subtitles Format"; + case SubFormats::TextSubUniversalSubtitleFormat: return "Universal Subtitle Format"; + default: return "Text subtitle"; + } + case GeneralMediaFormat::Theora: return "Theora"; + case GeneralMediaFormat::Tiff: return "Tagged Image File Format"; + case GeneralMediaFormat::Tta: return "The True Audio"; + case GeneralMediaFormat::UncompressedVideoFrames: return "uncompressed video frames"; + case GeneralMediaFormat::Vc1: return "Windows Media Video"; + case GeneralMediaFormat::VobBtn: return "VobBtn Buttons"; + case GeneralMediaFormat::VobSub: return "VobSub"; + case GeneralMediaFormat::Vorbis: return "Vorbis"; + case GeneralMediaFormat::WavPack: return "WavPack"; + default: return "unknown"; + } +} + +/*! + * \brief Returns the abbreviation of the media format as C-style string. + * + * Returns an empty string if no abbreviation is available. + */ +const char *MediaFormat::abbreviation() const +{ + switch(general) { + case GeneralMediaFormat::Aac: + switch(sub) { + case AacMpeg2MainProfile: return "MPEG-2 AAC Main"; + case AacMpeg2LowComplexityProfile: return "MPEG-2 AAC-LC"; + case AacMpeg2SpectralBandReplicationProfile: return "MPEG-2-SBR"; + case AacMpeg2ScalableSamplingRateProfile: return "MPEG-2 AAC-SSR"; + case AacMpeg4MainProfile: return "MPEG-4 AAC Main"; + case AacMpeg4LowComplexityProfile: return "MPEG-4 AAC-LC"; + case AacMpeg4SpectralBandReplicationProfile: return "MPEG-4 AAC-SBR"; + case AacMpeg4ScalableSamplingRateProfile: return "MPEG-4 AAC-SSR"; + case AacMpeg4LongTermPredictionProfile: return "MPEG-4 AAC-LTP"; + default: return "AAC"; + } + case GeneralMediaFormat::Ac3: return "AC-3"; + case GeneralMediaFormat::Ac4: return "AC-4"; + case GeneralMediaFormat::AdpcmAcm: return "ADPCM ACM"; + case GeneralMediaFormat::AfxStream: return "AFX"; + case GeneralMediaFormat::Alac: return "ALAC"; + case GeneralMediaFormat::Als: return "ALS"; + case GeneralMediaFormat::Bitmap: return "BMP"; + case GeneralMediaFormat::Dirac: return "Dirac"; + case GeneralMediaFormat::Dts: return "DTS"; + switch(sub) { + case DtsLossless: return "DTS Lossless"; + case DtsExpress: return "DTS LBR"; + default: return "DTS"; + } + case GeneralMediaFormat::DtsHd: return "DTS-HD"; + switch(sub) { + case DtsHdHighResolution: return "DTS-HD High Resolution"; + case DtsHdMasterAudio: return "DTS-HD Master Audio"; + case DtsExpress: return "DTS-HD Express"; + default: return "DTS-HD"; + } + case GeneralMediaFormat::EAc3: return "E-AC-3"; + case GeneralMediaFormat::Evrc: return "EVRC"; + case GeneralMediaFormat::Flac: return "FLAC"; + case GeneralMediaFormat::FontDataStream: return "FDS"; + case GeneralMediaFormat::Gif: return "GIF"; + case GeneralMediaFormat::Gpp2Cmf: return "3GPP2 CMF"; + case GeneralMediaFormat::ImaadpcmAcm: return "IMAADPCM ACM"; + case GeneralMediaFormat::ImageSubtitle: + switch(sub) { + case SubFormats::ImgSubBmp: return "BMP subtitle"; + default: return "Image subtitle"; + } + case GeneralMediaFormat::InteractionStream: return "Interaction Stream"; + case GeneralMediaFormat::Jpeg: return "JPEG"; + case GeneralMediaFormat::OggKate: return "OggKate"; + case GeneralMediaFormat::MicrosoftAudioCodecManager: return "MS ACM"; + case GeneralMediaFormat::MicrosoftVideoCodecManager: return "MS VCM"; + case GeneralMediaFormat::Mpeg1Audio: + switch(sub) { + case Mpeg1Layer1: return "MP1"; + case Mpeg1Layer2: return "MP2"; + case Mpeg1Layer3: return "MP3"; + default: return "MPEG-1 Audio"; + } + case GeneralMediaFormat::Mpeg1Video: return "MP1"; + case GeneralMediaFormat::Mpeg2Audio: + switch(sub) { + case Mpeg1Layer1: return "MP1"; + case Mpeg1Layer2: return "MP2"; + case Mpeg1Layer3: return "MP3"; + default: return "MPEG-2 Audio"; + } + case GeneralMediaFormat::Mpeg2Video: + switch(sub) { + case Mpeg2SimpleProfile: return "MPEG-2 SP"; + case Mpeg2MainProfile: return "MPEG-2 Main"; + case Mpeg2SnrProfile: return "MPEG-2 SNR"; + case Mpeg2SpatialProfile: return "MPEG-2 Spatial"; + case Mpeg2HighProfile: return "MPEG-2 High"; + case Mpeg2422Profile: return "MPEG-2 422"; + default: return "MPEG-2 Video"; + } + case GeneralMediaFormat::Mpeg4Video: + switch(sub) { + case Mpeg4Sp: return "MPEG-4 SP"; + case Mpeg4Asp: return "H.263"; + case Mpeg4Avc: return "H.264"; + case Mpeg4AvcParams: return "H.264 params"; + case Mpeg4MsV3: return "MPEG-4 MS V3"; + default: return "MPEG-4 Visual"; + } + case GeneralMediaFormat::Mpc: return "MPC"; + case GeneralMediaFormat::Pcm: + switch(sub) { + case PcmIntBe: return "PCM (int, BE)"; + case PcmIntLe: return "PCM (int, LE)"; + case PcmFloatIeee: return "PCM IEEE"; + default: return "PCM"; + } + case GeneralMediaFormat::Png: return "PNG"; + case GeneralMediaFormat::ProRes: return "ProRes"; + case GeneralMediaFormat::Qcelp: return "QCELP"; + case GeneralMediaFormat::QuicktimeAudio: return "Quicktime Audio"; + case GeneralMediaFormat::QuicktimeVideo: return "Quicktime Video"; + case GeneralMediaFormat::RealAudio: return "Real Audio"; + case GeneralMediaFormat::RealVideo: return "Real Video"; + case GeneralMediaFormat::Sa0c: return "SAOC"; + case GeneralMediaFormat::Smv: return "SMV"; + case GeneralMediaFormat::StreamingTextStream: return "Streaming Text Stream"; + case GeneralMediaFormat::SynthesizedTextureStream: return "Synthesized Texture Stream"; + case GeneralMediaFormat::Systems: + switch(sub) { + case 2: return "Systems v2"; + default: return "Systems"; + } + case GeneralMediaFormat::TextSubtitle: + switch(sub) { + case SubFormats::TextSubBasicUtf8: return "UTF-8 Sub"; + case SubFormats::TextSubSubtitlesFormat: return "SSA"; + case SubFormats::TextSubAdvancedSubtitlesFormat: return "ASS"; + case SubFormats::TextSubUniversalSubtitleFormat: return "USF"; + default: return "Text subtitle"; + } + case GeneralMediaFormat::Theora: return "Theora"; + case GeneralMediaFormat::Tiff: return "TIFF"; + case GeneralMediaFormat::Tta: return "TTA"; + case GeneralMediaFormat::UncompressedVideoFrames: return "uncompressed video frames"; + case GeneralMediaFormat::Vc1: return "VC-1"; + case GeneralMediaFormat::VobBtn: return "VobBtn"; + case GeneralMediaFormat::VobSub: return "VobSub"; + case GeneralMediaFormat::Vorbis: return "Vorbis"; + case GeneralMediaFormat::WavPack: return "WavPack"; + default: return ""; } } diff --git a/mediaformat.h b/mediaformat.h index bfa3eaa..4dc93aa 100644 --- a/mediaformat.h +++ b/mediaformat.h @@ -1,61 +1,185 @@ #ifndef MEDIAFORMAT_H #define MEDIAFORMAT_H +#include + +#include + namespace Media { /*! - * \brief The MediaType enum specifies the type of media data (acoustic, visual, textual, ...). + * \brief The MediaType enum specifies the type of media data (audio, video, text, ...). */ enum class MediaType { - Acoustic, - Visual, - Textual, - Hint, - Unknown + Unknown, + Audio, + Video, + Text, + Hint }; /*! - * \brief The MediaFormat enum specifies the format of media data (PCM, MPEG-4, PNG, ...). + * \brief The GeneralMediaFormat enum specifies the general format of media data (PCM, MPEG-4, PNG, ...). */ -enum class MediaFormat +enum class GeneralMediaFormat { - Pcm, - Mpeg1, - Mpeg2, - MpegL1, - MpegL2, - MpegL3, - Aac, - Alac, - Vorbis, - Png, - Jpeg, - Bitmap, - Mpeg4Sp, - Mpeg4Avc, - Mpeg4Asp, - Mpeg4, - Gif, - Tiff, - UncompressedRgb, - AdpcmAcm, - ImaadpcmAcm, - Ac3, - Ac4, - Dts, - Flac, - RealAudio, - RealVideo, - QuicktimeVideo, - QuicktimeAudio, - Theora, - ProRes, - VobSub, - Unknown + Unknown, + Aac, /**< Advanced Video Coding */ + Ac3, /**< Dolby Digital */ + Ac4, /**< AC-4 */ + AdpcmAcm, /**< ADPCM ACM */ + AfxStream, /**< AFX Stream */ + Alac, /**< Apple Lossless Audio Codec */ + Als, /**< ALS */ + Bitmap, /**< Windows Bitmap */ + Dirac, /**< Dirac */ + Dts, /**< DTS */ + DtsHd, /**< DTS-HD */ + EAc3, /**< Dolby Digital Plus */ + Evrc, /**< EVRC */ + Flac, /**< FLAC */ + FontDataStream, /**< Font Data Stream */ + Gif, /**< GIF */ + Gpp2Cmf, /**< 3GPP2 Compact Multimedia Format (CMF) */ + ImaadpcmAcm, /**< IMAADPCM ACM */ + ImageSubtitle, /**< Image subtitle */ + InteractionStream, /**< Interaction Stream */ + Jpeg, /**< JPEG */ + OggKate, /**< Karaoke And Text Encapsulation */ + MicrosoftAudioCodecManager, /**< Microsoft Audio Codec Manager (ACM) */ + MicrosoftVideoCodecManager, /**< Microsoft Video Codec Manager (VCM) */ + Mpeg1Audio, /**< MPEG-1 Audio */ + Mpeg1Video, /**< MPEG-1 Vudio */ + Mpeg2Audio, /**< MPEG-2 Audio */ + Mpeg2Video, /**< MPEG-2 Vudio */ + Mpeg4Video, /**< MPEG-4 */ + Mpc, /**< Musepack */ + Pcm, /**< Pulse Code Modulation */ + Png, /**< PNG */ + ProRes, /**< ProRes */ + Qcelp, /**< QCELP */ + QuicktimeAudio, /**< Quicktime Audio */ + QuicktimeVideo, /**< Quicktime Video */ + RealAudio, /**< Real Audio */ + RealVideo, /**< Real Video */ + Sa0c, /**< SAOC */ + Smv, /**< SMV */ + StreamingTextStream, /**< Streaming Text Stream */ + SynthesizedTextureStream, /**< Synthesized Texture Stream */ + Systems, /**< Systems */ + TextSubtitle, /**< Text subtitle */ + Theora, /**< Theora */ + Tiff, /**< TIFF */ + Tta, /**< The True Audio lessles audio compressor */ + UncompressedVideoFrames, /**< uncompressed RGB */ + Vc1, /**< VC-1 */ + VobBtn, /**< VobBtn */ + VobSub, /**< VobSub */ + Vorbis, /**< Vorbis */ + WavPack /**< WavPack */ }; -const char *mediaFormatName(MediaFormat mediaFormat); +/*! + * \brief Encapsulates sub formats. + * + * For instance "Layer 3" is a sub format of MPEG-1 audio. + */ +namespace SubFormats { + +enum : unsigned char { + None +}; + +enum Mpeg1AudioLayer : unsigned char { + Mpeg1Layer1 = 1, + Mpeg1Layer2, + Mpeg1Layer3 +}; + +enum AacProfile : unsigned char { + AacMpeg2MainProfile = 1, + AacMpeg2LowComplexityProfile, + AacMpeg2SpectralBandReplicationProfile, + AacMpeg2ScalableSamplingRateProfile, + AacMpeg4MainProfile, + AacMpeg4LowComplexityProfile, + AacMpeg4SpectralBandReplicationProfile, + AacMpeg4ScalableSamplingRateProfile, + AacMpeg4LongTermPredictionProfile +}; + +enum Mpeg2VideoProfile : unsigned char { + Mpeg2SimpleProfile = 1, + Mpeg2MainProfile, + Mpeg2SnrProfile, + Mpeg2SpatialProfile, + Mpeg2HighProfile, + Mpeg2422Profile +}; + +enum Mpeg4VideoProfile : unsigned char { + Mpeg4Sp = 1, + Mpeg4Asp, + Mpeg4Avc, + Mpeg4AvcParams, + Mpeg4MsV3 +}; + +enum DtsSpecifier : unsigned char { + DtsExpress = 1, + DtsLossless, + DtsHdHighResolution, + DtsHdMasterAudio, +}; + +enum PcmVersion : unsigned char { + PcmIntBe = 1, + PcmIntLe, + PcmFloatIeee +}; + +enum TextSubtitle : unsigned char { + TextSubBasicUtf8 = 1, + TextSubSubtitlesFormat, + TextSubAdvancedSubtitlesFormat, + TextSubUniversalSubtitleFormat +}; + +enum ImageSubtitle : unsigned char { + ImgSubBmp = 1 +}; + +} + +class LIB_EXPORT MediaFormat +{ +public: + MediaFormat(GeneralMediaFormat general = GeneralMediaFormat::Unknown, unsigned char sub = 0); + + const char *name() const; + const char *abbreviation() const; + operator bool() const; + + GeneralMediaFormat general; + unsigned char sub; +}; + +/*! + * \brief Constructs a new media format. + */ +inline MediaFormat::MediaFormat(GeneralMediaFormat general, unsigned char sub) : + general(general), + sub(sub) +{} + +/*! + * \brief Returns whether the media format is known. + */ +inline MediaFormat::operator bool() const +{ + return general != GeneralMediaFormat::Unknown; +} } diff --git a/mp4/mp4atom.cpp b/mp4/mp4atom.cpp index 64519ac..807f790 100644 --- a/mp4/mp4atom.cpp +++ b/mp4/mp4atom.cpp @@ -28,6 +28,13 @@ Mp4Atom::Mp4Atom(GenericFileElement::containerType &container, uint64 startOffse GenericFileElement(container, startOffset) {} +/*! + * \brief Constructs a new top level atom with the specified \a container at the specified \a startOffset. + */ +Mp4Atom::Mp4Atom(GenericFileElement::containerType &container, uint64 startOffset, uint64 maxSize) : + GenericFileElement(container, startOffset, maxSize) +{} + /*! * \brief Constructs a new sub level atom with the specified \a parent at the specified \a startOffset. */ @@ -98,7 +105,7 @@ void Mp4Atom::internalParse() if(parent()) { sibling = new Mp4Atom(*(parent()), startOffset() + totalSize()); } else { - sibling = new Mp4Atom(container(), startOffset() + totalSize()); + sibling = new Mp4Atom(container(), startOffset() + totalSize(), maxTotalSize() - totalSize()); } } m_nextSibling.reset(sibling); diff --git a/mp4/mp4atom.h b/mp4/mp4atom.h index d7fc20e..f48a4cc 100644 --- a/mp4/mp4atom.h +++ b/mp4/mp4atom.h @@ -35,6 +35,11 @@ public: */ typedef uint32 identifierType; + /*! + * \brief The type used to store element sizes is an unsigned 64-bit integer. + */ + typedef uint64 dataSizeType; + /*! * \brief The implementation type is Mp4Atom. */ @@ -56,6 +61,7 @@ public: static void seekBackAndWriteAtomSize(std::ostream &stream, const std::ostream::pos_type &startOffset, bool denote64BitSize = false); protected: + Mp4Atom(containerType& container, uint64 startOffset, uint64 maxSize); Mp4Atom(implementationType &parent, uint64 startOffset); void internalParse(); diff --git a/mp4/mp4ids.cpp b/mp4/mp4ids.cpp index 043eedc..ce955da 100644 --- a/mp4/mp4ids.cpp +++ b/mp4/mp4ids.cpp @@ -1,5 +1,7 @@ #include "mp4ids.h" +#include "../mediaformat.h" + namespace Media { /*! @@ -35,15 +37,157 @@ namespace Mp4MediaTypeIds { } /*! - * \brief Encapsulates all supported MP4 media format IDs. + * \brief Encapsulates all supported MP4 media format IDs (aka "FOURCCs"). + * \sa http://wiki.multimedia.cx/?title=QuickTime_container */ namespace Mp4FormatIds { + +MediaFormat fourccToMediaFormat(uint32 fourccId) +{ + switch(fourccId) { + case Mp4FormatIds::Mpeg4Video: + return GeneralMediaFormat::Mpeg4Video; + case Mp4FormatIds::Avc1: + case Mp4FormatIds::Avc2: + case Mp4FormatIds::Avc3: + case Mp4FormatIds::Avc4: + return MediaFormat(GeneralMediaFormat::Mpeg4Video, SubFormats::Mpeg4Avc); + case Mp4FormatIds::H263: + return MediaFormat(GeneralMediaFormat::Mpeg4Video, SubFormats::Mpeg4Asp); + case Mp4FormatIds::Tiff: + return GeneralMediaFormat::Tiff; + case Mp4FormatIds::Raw: + return GeneralMediaFormat::UncompressedVideoFrames; + case Mp4FormatIds::Jpeg: + return GeneralMediaFormat::Jpeg; + case Mp4FormatIds::Gif: + return GeneralMediaFormat::Gif; + case Mp4FormatIds::AdpcmAcm: + return GeneralMediaFormat::AdpcmAcm; + case Mp4FormatIds::ImaadpcmAcm: + return GeneralMediaFormat::ImaadpcmAcm; + case Mp4FormatIds::Mp3CbrOnly: + return MediaFormat(GeneralMediaFormat::Mpeg1Audio, SubFormats::Mpeg1Layer3); + case Mp4FormatIds::Mpeg4Audio: + return GeneralMediaFormat::Aac; + case Mp4FormatIds::Alac: + return GeneralMediaFormat::Alac; + case Mp4FormatIds::Ac3: + return GeneralMediaFormat::Ac3; + case Mp4FormatIds::Ac4: + return GeneralMediaFormat::Ac4; + default: + return GeneralMediaFormat::Unknown; + } +} + } /*! - * \brief Encapsulates all supported MP4 media format configuration box IDs. + * \brief Encapsulates all supported MP4 media format description extensions. + * \sa https://developer.apple.com/library/mac/documentation/QuickTime/QTFF/QTFFChap3/qtff3.html */ -namespace Mp4FormatConfigurationIds { +namespace Mp4FormatExtensionIds { +} + +/*! + * \brief Encapsulates all supported MPEG-4 elementary stream object IDs. + */ +namespace Mpeg4ElementaryStreamObjectIds { + +/*! + * \brief Returns the Media::MediaFormat denoted by the specified MPEG-4 stream ID. + */ +MediaFormat streamObjectTypeFormat(byte streamObjectTypeId) +{ + switch(streamObjectTypeId) { + case SystemsIso144961: return GeneralMediaFormat::Systems; + case SystemsIso144961v2: return MediaFormat(GeneralMediaFormat::Systems, 2); + case InteractionStream: return GeneralMediaFormat::InteractionStream; + case AfxStream: return GeneralMediaFormat::AfxStream; + case FontDataStream: return GeneralMediaFormat::FontDataStream; + case SynthesizedTextureStream: return GeneralMediaFormat::SynthesizedTextureStream; + case StreamingTextStream: return GeneralMediaFormat::StreamingTextStream; + case Mpeg4Visual: return GeneralMediaFormat::Mpeg4Video; + case Avc: return MediaFormat(GeneralMediaFormat::Mpeg4Video, SubFormats::Mpeg4Avc); + case ParameterSetsForAvc: return MediaFormat(GeneralMediaFormat::Mpeg4Video, SubFormats::Mpeg4AvcParams); + case Als: return GeneralMediaFormat::Als; + case Sa0c: return GeneralMediaFormat::Sa0c; + case Aac: return GeneralMediaFormat::Aac; + case Mpeg2VideoSimpleProfile: return MediaFormat(GeneralMediaFormat::Mpeg4Video, SubFormats::Mpeg2SimpleProfile); + case Mpeg2VideoMainProfile: return MediaFormat(GeneralMediaFormat::Mpeg4Video, SubFormats::Mpeg2SnrProfile); + case Mpeg2VideoSnrProfile: return MediaFormat(GeneralMediaFormat::Mpeg4Video, SubFormats::Mpeg2SpatialProfile); + case Mpeg2VideoSpatialProfile: return MediaFormat(GeneralMediaFormat::Mpeg4Video, SubFormats::Mpeg2HighProfile); + case Mpeg2VideoHighProfile: return MediaFormat(GeneralMediaFormat::Mpeg4Video, SubFormats::Mpeg2HighProfile); + case Mpeg2Video422Profile: return MediaFormat(GeneralMediaFormat::Mpeg4Video, SubFormats::Mpeg2SimpleProfile); + case AacMainProfile: return MediaFormat(GeneralMediaFormat::Aac, SubFormats::AacMpeg2MainProfile); + case AacLowComplexityProfile: return MediaFormat(GeneralMediaFormat::Aac, SubFormats::AacMpeg2LowComplexityProfile); + case AacScaleableSamplingRateProfile: return MediaFormat(GeneralMediaFormat::Aac, SubFormats::AacMpeg2ScalableSamplingRateProfile); + case Mpeg2Audio: return GeneralMediaFormat::Mpeg2Audio; + case Mpeg1Video: return GeneralMediaFormat::Mpeg1Video; + case Mpeg1Audio: return GeneralMediaFormat::Mpeg1Audio; + case Jpeg: return GeneralMediaFormat::Jpeg; + case Png: return GeneralMediaFormat::Png; + case Evrc: case PrivateEvrc: return GeneralMediaFormat::Evrc; + case Smv: return GeneralMediaFormat::Smv; + case Gpp2Cmf: return GeneralMediaFormat::Gpp2Cmf; + case Vc1: return GeneralMediaFormat::Vc1; + case Dirac: return GeneralMediaFormat::Dirac; + case Ac3: case PrivateAc3: return GeneralMediaFormat::Ac3; + case EAc3: return GeneralMediaFormat::EAc3; + case Dts: case PrivateDts: return GeneralMediaFormat::Dts; + case DtsHdHighResolution: return MediaFormat(GeneralMediaFormat::DtsHd, SubFormats::DtsHdHighResolution); + case DtsHdMasterAudio: return MediaFormat(GeneralMediaFormat::DtsHd, SubFormats::DtsHdMasterAudio); + case DtsHdExpress: return MediaFormat(GeneralMediaFormat::DtsHd, SubFormats::DtsExpress); + case PrivateOgg: case PrivateOgg2: return GeneralMediaFormat::Vorbis; + case PrivateQcelp: return GeneralMediaFormat::Qcelp; + default: return MediaFormat(); + } +} + +} + +/*! + * \brief Encapsulates all known MPEG-4 descriptor IDs. + */ +namespace Mpeg4DescriptorIds { +} + +/*! + * \brief Returns the name of the stream type denoted by the specified MPEG-4 stream type ID. + */ +namespace Mpeg4ElementaryStreamTypeIds { + +/*! + * \brief Returns the name of the stream type denoted by the specified MPEG-4 stream type ID. + */ +const char *streamTypeName(byte streamTypeId) +{ + switch(streamTypeId) { + case ObjectDescriptor: return "object descriptor"; + case ClockReference: return "clock reference"; + case SceneDescriptor: return "scene descriptor"; + case Visual: return "visual"; + case Audio: return "audio"; + case Mpeg7: return "MPEG-7"; + case Ipmps: return "IMPS"; + case ObjectContentInfo: return "object content info"; + case MpegJava: return "MPEG Java"; + case Interaction: return "interaction"; + case Ipmp: return "IPMP"; + case FontData: return "font data"; + case StreamingText: return "streaming text"; + default: ""; + } +} + +} + +/*! + * \brief Encapsulates all supported MPEG-4 audio object format IDs. + * \sa http://wiki.multimedia.cx/index.php?title=MPEG-4_Audio + */ +namespace Mpeg4AudioObjectIds { } } diff --git a/mp4/mp4ids.h b/mp4/mp4ids.h index 91a9953..7931810 100644 --- a/mp4/mp4ids.h +++ b/mp4/mp4ids.h @@ -6,6 +6,8 @@ namespace Media { +class MediaFormat; + namespace Mp4AtomIds { enum KnownValue : uint32 { AvcConfiguration = 0x61766343, @@ -66,8 +68,8 @@ enum KnownValue : uint32 { TrackExtends = 0x74726578, TrackFragmentRun = 0x7472756E, UserData = 0x75647461, - DataEntryUrl = 0x75726C20, - DataEntryUrn = 0x75726E20, + DataEntryUrl = 0x75726C20, + DataEntryUrn = 0x75726E20, VideoMediaHeader = 0x766D6864, Wide = 0x77696465 }; @@ -125,39 +127,228 @@ extern const char *cdec; namespace Mp4MediaTypeIds { enum KnownValue : uint32 { - Sound = 0x736f756e, - Video = 0x76696465, - Hint = 0x68696e74, - Meta = 0x6d657461 + Sound = 0x736f756e, /**< Sound/Audio */ + Video = 0x76696465, /**< Video */ + Hint = 0x68696e74, /**< Hint */ + Meta = 0x6d657461 /**< Meta */ }; } namespace Mp4FormatIds { enum KnownValue : uint32 { - Mpeg4Visual = 0x6d703476, - Avc1 = 0x61766331, - Avc2 = 0x61766332, - Avc3 = 0x61766333, - Avc4 = 0x61766334, - H263 = 0x68323633, - Tiff = 0x74696666, - Jpeg = 0x6a706567, - Raw = 0x72617720, - Gif = 0x67696620, - Mp3 = 0x2e6d7033, - Mpeg4Audio = 0x6d703461, - Alac = 0x616C6163, - Ac3 = 0x61632d33, - Ac4 = 0x61632d34, - AdpcmAcm = 0x6D730002, - ImaadpcmAcm = 0x6D730011, - Mp3CbrOnly = 0x6D730055 + Cinepak = 0x63766964, /**< Cinepak */ + Mpeg4Video = 0x6d703476, /**< MPEG-4 video */ + Graphics = 0x736D6320, /**< Graphics */ + Animation = 0x726C6520, /**< Animation */ + AppleVideo = 0x72707A61, /**< Apple video */ + Png = 0x706E6720, /**< Portable Network Graphics */ + Avc1 = 0x61766331, /**< H.264/MPEG-4 AVC video */ + Avc2 = 0x61766332, /**< H.264/MPEG-4 AVC video */ + Avc3 = 0x61766333, /**< H.264/MPEG-4 AVC video */ + Avc4 = 0x61766334, /**< H.264/MPEG-4 AVC video */ + H263 = 0x68323633, /**< H.263/MPEG-4 ASP video */ + Tiff = 0x74696666, /**< Tagged Image File Format */ + Jpeg = 0x6a706567, /**< JPEG */ + Raw = 0x72617720, /**< Uncompressed RGB */ + Gif = 0x67696620, /**< CompuServe Graphics Interchange Format */ + NtscDv25Video = 0x64766320, /**< NTSC DV-25 video */ + PalDv25Video = 0x64766370, /**< PAL DV-25 video */ + MotionJpegA = 0x6D6A7061, /**< Motion-JPEG (format A) */ + MotionJpegB = 0x6D6A7062, /**< Motion-JPEG (format B) */ + Mp3 = 0x2e6d7033, /**< MPEG-1 Layer 3 */ + Mpeg4Audio = 0x6d703461, /**< MPEG-4 audio */ + Mpeg4Stream = 0x6d703473, /**< MPEG-4 stream (other then video/audio) */ + Alac = 0x616C6163, /**< Apple Losless Audio Codec */ + Ac3 = 0x61632d33, /**< Dolby Digital */ + Ac4 = 0x61632d34, /**< ? */ + AdpcmAcm = 0x6D730002, /**< ? */ + ImaadpcmAcm = 0x6D730011, /**< ? */ + Mp3CbrOnly = 0x6D730055 /**< MPEG-1 Layer 3 (constant bitrate only) */ +}; + +MediaFormat fourccToMediaFormat(uint32 fourccId); + +} + +namespace Mp4FormatExtensionIds { +enum KnownValue : uint32 { + GammaLevel = 0x67616D61, /**< A 32-bit fixed-point number indicating the gamma level at which the image was captured. The decompressor can use this value to gamma-correct at display time. */ + FieldHandling = 0x6669656C, /**< Two 8-bit integers that define field handling. */ + DefaultQuantizationTable = 0x6D6A7174, /**< The default quantization table for a Motion-JPEG data stream. */ + DefaultHuffmanTable = 0x6D6A6874, /**< The default Huffman table for a Motion-JPEG data stream. */ + Mpeg4ElementaryStreamDescriptor = 0x65736473, /**< An MPEG-4 elementary stream descriptor atom. This extension is required for MPEG-4 video. */ + Mpeg4ElementaryStreamDescriptor2 = 0x6D346473, /**< Alternative if encoded to AVC stanard. */ + AvcConfiguration = 0x61766343, /**< An H.264 AVCConfigurationBox. This extension is required for H.264 video as defined in ISO/IEC 14496-15. */ + PixelAspectRatio = 0x70617370, /**< Pixel aspect ratio. This extension is mandatory for video formats that use non-square pixels. */ + ColorParameters = 0x636F6C72, /**< An image description extension required for all uncompressed Y´CbCr video types. */ + CleanAperature = 0x636C6170 /**< Spatial relationship of Y´CbCr components relative to a canonical image center. */ }; } -namespace Mp4FormatConfigurationIds { -enum KnownValue : uint32 { - AvcC = 0x61766343 +namespace Mpeg4ElementaryStreamObjectIds { +enum KnownValue : byte { + SystemsIso144961 = 0x01, /**< Systems */ + SystemsIso144961v2, /**< Systems (version 2) */ + InteractionStream, /**< Interaction Stream */ + AfxStream = 0x05, /**< AFX Stream */ + FontDataStream, /**< Font Data Stream */ + SynthesizedTextureStream, /**< Synthesized Texture Stream */ + StreamingTextStream, /**< Streaming Text Stream */ + Mpeg4Visual = 0x20, /**< MPEG-4 Visual */ + Avc, /**< Advanced Video Coding */ + ParameterSetsForAvc, /**< Parameter Sets for Advanced Video Coding */ + Als = 0x24, /**< ALS */ + Sa0c = 0x2B, /**< SAOC */ + Aac = 0x40, /**< Audio ISO/IEC 14496-3 (AAC) */ + Mpeg2VideoSimpleProfile = 0x60, /**< MPEG-2 Video Simple Profile */ + Mpeg2VideoMainProfile, /**< MPEG-2 Video Main Profile */ + Mpeg2VideoSnrProfile, /**< MPEG-2 Video SNR Profile */ + Mpeg2VideoSpatialProfile, /**< MPEG-2 Video Spatial Profile */ + Mpeg2VideoHighProfile, /**< MPEG-2 Video High Profile */ + Mpeg2Video422Profile, /**< MPEG-2 Video 422 Profile */ + AacMainProfile, /**< Advanced Audio Coding Main Profile */ + AacLowComplexityProfile, /**< Advanced Audio Coding Low Complexity Profile */ + AacScaleableSamplingRateProfile, /**< Advanced Audio Coding Scaleable Sampling Rate Profile */ + Mpeg2Audio, /**< MPEG-2 Audio */ + Mpeg1Video, /**< MPEG-1 Video */ + Mpeg1Audio, /**< MPEG-1 Audio */ + Jpeg, /**< JPEG */ + Png, /**< PNG */ + Evrc = 0xA0, /**< EVRC */ + Smv, /**< SMV */ + Gpp2Cmf, /**< 3GPP2 Compact Multimedia Format (CMF) */ + Vc1, /**< VC-1 */ + Dirac, /**< Dirac */ + Ac3, /**< AC-3 */ + EAc3, /**< E-AC-3 */ + Dts, /**< DTS */ + DtsHdHighResolution, /**< DTS-HD High Resolution */ + DtsHdMasterAudio, /**< DTS-HD Master Audio */ + DtsHdExpress, /**< DTS-HD Express */ + PrivateEvrc = 0xD1, /**< EVRC */ + PrivateAc3 = 0xD3, /**< AC-3 */ + PrivateDts, /**< DTS */ + PrivateOgg = 0xDD, /**< Ogg */ + PrivateOgg2, /**< Ogg */ + PrivateQcelp = 0xE1 /**< QCELP */ +}; + +MediaFormat streamObjectTypeFormat(byte streamObjectTypeId); + +} + +namespace Mpeg4ElementaryStreamTypeIds { +enum KnownValue : byte { + ObjectDescriptor = 0x01, + ClockReference, + SceneDescriptor, + Visual, + Audio, + Mpeg7, + Ipmps, + ObjectContentInfo, + MpegJava, + Interaction, + Ipmp, + FontData, + StreamingText +}; + +const char *streamTypeName(byte streamTypeId); + +} + +namespace Mpeg4DescriptorIds { +enum KnownValue : byte { + ObjectDescr = 0x01, + InitialObjectDescr, + ElementaryStreamDescr, + DecoderConfigDescr, + DecoderSpecificInfo, + SlConfigDescr, + ContentIdentDescr, + SupplContentIdentDescr, + IpiDescPointer, + IpmpDescPointer, + IpmpDescr, + QoSDescr, + RegistrationDescr, + EsIdInc, + EsIdRef, + Mp4I0d, + Mp40d, + IplDescrPointerRef, + ExtendedProfileLevelDescr, + ProfileLevelIndicationIndexDescr, + ContentClassificationDescr = 0x40, + KeyWordDescr, + RatingDescr, + LanguageDescr, + ShortTextualDescr, + ExpandedTextualDescr, + ContentCreatorNameDescr, + ContentCreationDateDescr, + IcicCreatorDateDescr, + SmpteCameraPositionDescr, + SegmentDescr, + MediaTimeDescr, + IpmpToolsListDescr = 0x60, + IpmpToolTag, + FlexMuxTimingDescr, + FlexMuxCodeTableDescr, + ExtSlConfigDescr, + FlexMuxIdentDescr, + DependencyPointer, + DependencyMaker, + FlexMuxChannelDescr, + UserPrivate = 0xC0 +}; +} + +namespace Mpeg4AudioObjectIds { +enum KnownValue : byte { + Null = 0, + AacMain, + AacLc, /**< low complexity */ + AacSsr, /**< scalable sample rate */ + AacLtp, /**< long term prediction */ + Sbr, /**< spectral band replication */ + AacScalable, + TwinVq, + Celp, /**< code excited linear prediction */ + Hxvc, /**< harmonic vector excitation coding */ + Ttsi = 12, /**< text-to-speech interface */ + MainSynthesis, + WavetableSynthesis, + GeneralMidi, + AlgorithmicSynthesisAudioEffects, + ErAacLc, /**< error resillent AAC LC */ + ErAacLtp = 19, + ErAacScalable, + ErTwinVq, + ErBsac, + ErAacLd, + ErCelp, + ErHvxc, + ErHiln, + ErParametric, + Ssc, + Ps, + MpegSurround, + EscapeValue, + Layer1, + Layer2, + Layer3, + Dst, + Als, /**< audio lossless */ + Sls, /**< scalable lossless */ + ErAacEld, /**< enhanced low delay */ + SmrSimple, /**< symbolic music representation */ + SmrMain, + UsacNoSbr, /**< unified speech and audio coding */ + Saoc, /**< spatial audio object coding (no SBR) */ + LdMpegSurround, + Usac /**< unified speech and audio coding */ }; } diff --git a/mp4/mp4tag.cpp b/mp4/mp4tag.cpp index c8add3a..fdcd3c7 100644 --- a/mp4/mp4tag.cpp +++ b/mp4/mp4tag.cpp @@ -213,22 +213,17 @@ void Mp4Tag::parse(Mp4Atom &metaAtom) addNotification(NotificationType::Critical, "Unable to parse child atoms of meta atom (stores hdlr and ilst atoms).", context); } if(subAtom) { - Mp4Atom *child = subAtom->firstChild(); Mp4TagField tagField; - while(child) { + for(Mp4Atom *child : *subAtom) { try { child->parse(); tagField.invalidateNotifications(); tagField.reparse(*child); fields().insert(pair(child->id(), tagField)); - addNotifications(context, *child); - addNotifications(context, tagField); - child = child->nextSibling(); } catch(Failure &) { - addNotifications(context, *child); - addNotifications(context, tagField); - break; } + addNotifications(context, *child); + addNotifications(context, tagField); } } else { addNotification(NotificationType::Warning, "No ilst atom found (stores attached meta information).", context); diff --git a/mp4/mp4track.cpp b/mp4/mp4track.cpp index eb0c44a..3c7022a 100644 --- a/mp4/mp4track.cpp +++ b/mp4/mp4track.cpp @@ -2,6 +2,7 @@ #include "mp4container.h" #include "mp4track.h" #include "mp4ids.h" +#include "mpeg4descriptor.h" #include "../exceptions.h" #include "../mediaformat.h" @@ -21,6 +22,19 @@ 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), + MediaFormat(GeneralMediaFormat::Aac, SubFormats::AacMpeg4LowComplexityProfile), + MediaFormat(GeneralMediaFormat::Aac, SubFormats::AacMpeg4ScalableSamplingRateProfile), + MediaFormat(GeneralMediaFormat::Aac, SubFormats::AacMpeg4LongTermPredictionProfile), + MediaFormat(GeneralMediaFormat::Aac, SubFormats::AacMpeg4SpectralBandReplicationProfile) +}; + /*! * \class Media::Mp4Track * \brief Implementation of Media::AbstractTrack for the MP4 container. @@ -46,6 +60,7 @@ Mp4Track::Mp4Track(Mp4Atom &trakAtom) : m_stcoAtom(nullptr), m_stszAtom(nullptr), m_codecConfigAtom(nullptr), + m_esDescAtom(nullptr), m_framesPerSample(1), m_chunkOffsetSize(4), m_chunkCount(0), @@ -394,37 +409,28 @@ vector Mp4Track::readChunkSizes() /*! * \brief Reads the AVC configuration for the track. - * \remarks Ensure that the format is MediaFormat::Mpeg4Avc before calling. + * \remarks + * - Returns an empty configuration for non-AVC tracks. + * - Notifications might be added. */ -AvcConfiguration Mp4Track::readAvcConfiguration() +AvcConfiguration Mp4Track::parseAvcConfiguration() { AvcConfiguration config; - try { - auto configSize = m_codecConfigAtom->dataSize(); - if(m_codecConfigAtom && configSize >= 5) { - // skip first byte (is always 1) - m_istream->seekg(m_codecConfigAtom->dataOffset() + 1); - // read profile, IDC level, NALU size length - config.profileIdc = m_reader.readByte(); - config.profileCompat = m_reader.readByte(); - config.levelIdc = m_reader.readByte(); - config.naluSizeLength = m_reader.readByte() & 0x03; - // read SPS infos - if((configSize -= 5) >= 3) { - byte entryCount = m_reader.readByte() & 0x0f; - uint16 entrySize; - while(entryCount && configSize) { - if((entrySize = m_reader.readUInt16BE()) <= configSize) { - // TODO: read entry - configSize -= entrySize; - } else { - throw TruncatedDataException(); - } - --entryCount; - } - // read PPS infos + if(m_codecConfigAtom) { + try { + auto configSize = m_codecConfigAtom->dataSize(); + if(m_codecConfigAtom && configSize >= 5) { + // skip first byte (is always 1) + m_istream->seekg(m_codecConfigAtom->dataOffset() + 1); + // read profile, IDC level, NALU size length + config.profileIdc = m_reader.readByte(); + config.profileCompat = m_reader.readByte(); + config.levelIdc = m_reader.readByte(); + config.naluSizeLength = m_reader.readByte() & 0x03; + // read SPS infos if((configSize -= 5) >= 3) { - entryCount = m_reader.readByte(); + byte entryCount = m_reader.readByte() & 0x0f; + uint16 entrySize; while(entryCount && configSize) { if((entrySize = m_reader.readUInt16BE()) <= configSize) { // TODO: read entry @@ -434,19 +440,107 @@ AvcConfiguration Mp4Track::readAvcConfiguration() } --entryCount; } - // TODO: read trailer - return config; + // read PPS infos + if((configSize -= 5) >= 3) { + entryCount = m_reader.readByte(); + while(entryCount && configSize) { + if((entrySize = m_reader.readUInt16BE()) <= configSize) { + // TODO: read entry + configSize -= entrySize; + } else { + throw TruncatedDataException(); + } + --entryCount; + } + // TODO: read trailer + return config; + } } } + throw TruncatedDataException(); + } catch (TruncatedDataException &) { + addNotification(NotificationType::Critical, "AVC configuration is truncated.", "parsing AVC configuration"); } - throw TruncatedDataException(); - } catch (TruncatedDataException &) { - addNotification(NotificationType::Critical, "AVC configuration is truncated.", "parsing AVC configuration"); } return config; } /*! + * \brief Reads the MPEG-4 elementary stream descriptor for the track. + * \remarks + * - Notifications might be added. + * \sa mpeg4ElementaryStreamInfo() + */ +void Mp4Track::parseMpeg4ElementaryStreamInfo() +{ + static const string context("parsing MPEG-4 elementary stream descriptor"); + if(m_esDescAtom) { + if(m_esDescAtom->dataSize() >= 12) { + m_istream->seekg(m_esDescAtom->dataOffset()); + // read version/flags + if(m_reader.readUInt32BE() != 0) { + addNotification(NotificationType::Warning, "Unknown version/flags.", context); + } + // read extended descriptor + Mpeg4Descriptor esDesc(m_esDescAtom->container(), m_istream->tellg(), m_esDescAtom->dataSize() - 4); + try { + esDesc.parse(); + // check ID + if(esDesc.id() != Mpeg4DescriptorIds::ElementaryStreamDescr) { + addNotification(NotificationType::Critical, "Invalid descriptor found.", context); + throw Failure(); + } + // read stream info + m_istream->seekg(esDesc.dataOffset()); + m_esInfo = make_unique(); + m_esInfo->id = m_reader.readUInt16BE(); + m_esInfo->esDescFlags = m_reader.readByte(); + if(m_esInfo->dependencyFlag()) { + m_esInfo->dependsOnId = m_reader.readUInt16BE(); + } + if(m_esInfo->urlFlag()) { + m_esInfo->url = m_reader.readString(m_reader.readByte()); + } + if(m_esInfo->ocrFlag()) { + m_esInfo->ocrId = m_reader.readUInt16BE(); + } + for(Mpeg4Descriptor *esDescChild = esDesc.denoteFirstChild(static_cast(m_istream->tellg()) - esDesc.startOffset()); esDescChild; esDescChild = esDescChild->nextSibling()) { + esDescChild->parse(); + switch(esDescChild->id()) { + case Mpeg4DescriptorIds::DecoderConfigDescr: + // read decoder config descriptor + m_istream->seekg(esDescChild->dataOffset()); + m_esInfo->objectTypeId = m_reader.readByte(); + m_esInfo->decCfgDescFlags = m_reader.readByte(); + m_esInfo->bufferSize = m_reader.readUInt24BE(); + m_esInfo->maxBitrate = m_reader.readUInt32BE(); + m_esInfo->averageBitrate = m_reader.readUInt32BE(); + for(Mpeg4Descriptor *decCfgDescChild = esDescChild->denoteFirstChild(13); decCfgDescChild; decCfgDescChild = decCfgDescChild->nextSibling()) { + decCfgDescChild->parse(); + switch(esDescChild->id()) { + case Mpeg4DescriptorIds::DecoderSpecificInfo: + // read decoder specific info + + break; + } + } + break; + case Mpeg4DescriptorIds::SlConfigDescr: + // uninteresting + break; + } + } + } catch (Failure &) { + // notifications will be added in any case + } + addNotifications(esDesc); + } else { + addNotification(NotificationType::Warning, "Elementary stream descriptor atom (esds) is truncated.", context); + } + } +} + + /*! * \brief Updates the chunk offsets of the track. This is necessary when the mdat atom (which contains * the actual chunk data) is moved. * \param oldMdatOffsets Specifies a vector holding the old offsets of the "mdat"-atoms. @@ -648,16 +742,16 @@ void Mp4Track::makeMedia() writer().writeUInt32BE(Mp4AtomIds::HandlerReference); writer().writeUInt64BE(0); // version, flags, pre defined switch(m_mediaType) { - case MediaType::Visual: + case MediaType::Video: outputStream().write("vide", 4); break; - case MediaType::Acoustic: + case MediaType::Audio: outputStream().write("soun", 4); break; case MediaType::Hint: outputStream().write("hint", 4); break; - case MediaType::Textual: + case MediaType::Text: outputStream().write("meta", 4); break; default: @@ -730,6 +824,7 @@ void Mp4Track::makeMediaInfo() /*! * \brief Makes the sample table (stbl atom) for the track. The data is written to the assigned output stream * at the current position. + * \remarks Not fully implemented yet. */ void Mp4Track::makeSampleTable() { @@ -917,13 +1012,13 @@ void Mp4Track::internalParseHeader() m_istream->seekg(m_hdlrAtom->startOffset() + 16); // seek to beg, skip size, name, version, flags and reserved bytes string trackTypeStr = reader.readString(4); if(trackTypeStr == "soun") { - m_mediaType = MediaType::Acoustic; + m_mediaType = MediaType::Audio; } else if(trackTypeStr == "vide") { - m_mediaType = MediaType::Visual; + m_mediaType = MediaType::Video; } else if(trackTypeStr == "hint") { m_mediaType = MediaType::Hint; } else if(trackTypeStr == "meta") { - m_mediaType = MediaType::Textual; + m_mediaType = MediaType::Text; } else { m_mediaType = MediaType::Unknown; } @@ -941,69 +1036,37 @@ void Mp4Track::internalParseHeader() if((codecConfigContainerAtom = m_stsdAtom->firstChild())) { try { codecConfigContainerAtom->parse(); - switch(codecConfigContainerAtom->id()) { - case Mp4FormatIds::Mpeg4Visual: - m_format = MediaFormat::Mpeg4; - break; - case Mp4FormatIds::Avc1: - case Mp4FormatIds::Avc2: - case Mp4FormatIds::Avc3: - case Mp4FormatIds::Avc4: - m_format = MediaFormat::Mpeg4Avc; - m_codecConfigAtom = codecConfigContainerAtom->childById(Mp4AtomIds::AvcConfiguration); - break; - case Mp4FormatIds::H263: - m_format = MediaFormat::Mpeg4Asp; - break; - case Mp4FormatIds::Tiff: - m_format = MediaFormat::Tiff; - break; - case Mp4FormatIds::Raw: - m_format = MediaFormat::UncompressedRgb; - break; - case Mp4FormatIds::Jpeg: - m_format = MediaFormat::Jpeg; - break; - case Mp4FormatIds::Gif: - m_format = MediaFormat::Gif; - break; - case Mp4FormatIds::AdpcmAcm: - m_format = MediaFormat::AdpcmAcm; - break; - case Mp4FormatIds::ImaadpcmAcm: - m_format = MediaFormat::ImaadpcmAcm; - break; - case Mp4FormatIds::Mp3CbrOnly: - m_format = MediaFormat::MpegL3; - break; - case Mp4FormatIds::Mpeg4Audio: - m_format = MediaFormat::Aac; - break; - case Mp4FormatIds::Alac: - m_format = MediaFormat::Alac; - break; - case Mp4FormatIds::Ac3: - m_format = MediaFormat::Ac3; - break; - case Mp4FormatIds::Ac4: - m_format = MediaFormat::Ac4; - break; - default: - // format id is unknown - m_format = MediaFormat::Unknown; - m_formatId = interpretIntegerAsString(codecConfigContainerAtom->id()); + // parse FOURCC + m_formatId = interpretIntegerAsString(codecConfigContainerAtom->id()); + m_format = Mp4FormatIds::fourccToMediaFormat(codecConfigContainerAtom->id()); + // parse AVC configuration + m_codecConfigAtom = codecConfigContainerAtom->childById(Mp4AtomIds::AvcConfiguration); + // parse MPEG-4 elementary stream descriptor + m_esDescAtom = codecConfigContainerAtom->childById(Mp4FormatExtensionIds::Mpeg4ElementaryStreamDescriptor); + if(!m_esDescAtom) { + m_esDescAtom = codecConfigContainerAtom->childById(Mp4FormatExtensionIds::Mpeg4ElementaryStreamDescriptor2); + } + try { + parseMpeg4ElementaryStreamInfo(); + if(m_esInfo) { + auto mediaFormat = Mpeg4ElementaryStreamObjectIds::streamObjectTypeFormat(m_esInfo->objectTypeId); + if(mediaFormat) { + m_format = mediaFormat; + } + } + } catch(Failure &) { } // seek to start offset of additional atom and skip reserved bytes and data reference index m_istream->seekg(codecConfigContainerAtom->startOffset() + 8 + 6 + 2); switch(m_mediaType) { - case MediaType::Acoustic: + case MediaType::Audio: m_istream->seekg(8, ios_base::cur); // skip reserved bytes m_channelCount = reader.readUInt16BE(); m_bitsPerSample = reader.readUInt16BE(); m_istream->seekg(4, ios_base::cur); // skip reserved bytes m_samplesPerSecond = reader.readUInt32BE() >> 16; break; - case MediaType::Visual: + case MediaType::Video: m_istream->seekg(16, ios_base::cur); // skip reserved bytes m_pixelSize.setWidth(reader.readUInt16BE()); m_pixelSize.setHeight(reader.readUInt16BE()); diff --git a/mp4/mp4track.h b/mp4/mp4track.h index a77445a..7123cd1 100644 --- a/mp4/mp4track.h +++ b/mp4/mp4track.h @@ -6,12 +6,79 @@ #include "../abstracttrack.h" #include +#include namespace Media { class Mp4Atom; +class LIB_EXPORT Mpeg4ElementaryStreamInfo +{ +public: + Mpeg4ElementaryStreamInfo(); + + uint16 id; + byte esDescFlags; + uint16 dependsOnId; + std::string url; + uint16 ocrId; + byte objectTypeId; + byte decCfgDescFlags; + uint32 bufferSize; + uint32 maxBitrate; + uint32 averageBitrate; + + bool dependencyFlag() const; + bool urlFlag() const; + bool ocrFlag() const; + byte priority() const; + byte streamTypeId() const; + bool upstream() const; +}; + +inline Mpeg4ElementaryStreamInfo::Mpeg4ElementaryStreamInfo() : + id(0), + esDescFlags(0), + dependsOnId(0), + ocrId(0), + objectTypeId(0), + decCfgDescFlags(0), + bufferSize(0), + maxBitrate(0), + averageBitrate(0) +{} + +inline bool Mpeg4ElementaryStreamInfo::dependencyFlag() const +{ + return esDescFlags & 0x80; +} + +inline bool Mpeg4ElementaryStreamInfo::urlFlag() const +{ + return esDescFlags & 0x40; +} + +inline bool Mpeg4ElementaryStreamInfo::ocrFlag() const +{ + return esDescFlags & 0x20; +} + +inline byte Mpeg4ElementaryStreamInfo::priority() const +{ + return esDescFlags & 0x1F; +} + +inline byte Mpeg4ElementaryStreamInfo::streamTypeId() const +{ + return decCfgDescFlags >> 2; +} + +inline bool Mpeg4ElementaryStreamInfo::upstream() const +{ + return decCfgDescFlags & 0x02; +} + class LIB_EXPORT Mp4Track : public AbstractTrack { public: @@ -25,10 +92,13 @@ public: unsigned int chunkOffsetSize() const; uint32 chunkCount() const; uint32 sampleToChunkEntryCount() const; + const Mpeg4ElementaryStreamInfo *mpeg4ElementaryStreamInfo() const; std::vector readChunkOffsets(); std::vector > readSampleToChunkTable(); std::vector readChunkSizes(); - AvcConfiguration readAvcConfiguration(); + AvcConfiguration parseAvcConfiguration(); + bool hasMpeg4ElementaryStreamDesc() const; + void parseMpeg4ElementaryStreamInfo(); void updateChunkOffsets(const std::vector &oldMdatOffsets, const std::vector &newMdatOffsets); void updateChunkOffset(uint32 chunkIndex, uint64 offset); void makeTrack(); @@ -56,11 +126,13 @@ private: Mp4Atom *m_stcoAtom; Mp4Atom *m_stszAtom; Mp4Atom *m_codecConfigAtom; + Mp4Atom *m_esDescAtom; uint16 m_framesPerSample; std::vector m_sampleSizes; unsigned int m_chunkOffsetSize; uint32 m_chunkCount; uint32 m_sampleToChunkEntryCount; + std::unique_ptr m_esInfo; }; /*! @@ -109,6 +181,31 @@ inline uint32 Mp4Track::sampleToChunkEntryCount() const return m_sampleToChunkEntryCount; } +/*! + * \brief Returns information about the MPEG-4 elementary stream. + * \remarks + * - The Mp4Track::readMpeg4ElementaryStreamInfo() method must be called before + * to parse the information. This is done when parsing the track. + * - The information is only available, if the track has an MPEG-4 elementary stream + * descriptor atom. + * - The track keeps ownership over the returned object. + * \sa + * - readMpeg4ElementaryStreamInfo() + * - hasMpeg4ElementaryStreamDesc() + */ +inline const Mpeg4ElementaryStreamInfo *Mp4Track::mpeg4ElementaryStreamInfo() const +{ + return m_esInfo.get(); +} + +/*! + * \brief Returns whether the track has an MPEG-4 elementary stream descriptor atom. + */ +inline bool Mp4Track::hasMpeg4ElementaryStreamDesc() const +{ + return m_esDescAtom != nullptr; +} + } #endif // MP4TRACK_H diff --git a/mp4/mpeg4descriptor.cpp b/mp4/mpeg4descriptor.cpp index 353dd9c..3449fa9 100644 --- a/mp4/mpeg4descriptor.cpp +++ b/mp4/mpeg4descriptor.cpp @@ -1,7 +1,88 @@ -#include "mpeg4streamdescriptor.h" +#include "mpeg4descriptor.h" +#include "mp4container.h" -Mpeg4StreamDescriptor::Mpeg4StreamDescriptor() +#include "mp4ids.h" + +#include +#include + +using namespace std; +using namespace ConversionUtilities; + +namespace Media { + +/*! + * \class Media::Mpeg4Descriptor + * \brief The Mpeg4Descriptor class helps to parse MPEG-4 descriptors. + */ + +/*! + * \brief Constructs a new top level descriptor with the specified \a container at the specified \a startOffset + * and with the specified \a maxSize. + */ +Mpeg4Descriptor::Mpeg4Descriptor(containerType &container, uint64 startOffset, uint64 maxSize) : + GenericFileElement(container, startOffset, maxSize) +{} + +/*! + * \brief Constructs a new sub level descriptor with the specified \a parent at the specified \a startOffset. + */ +Mpeg4Descriptor::Mpeg4Descriptor(implementationType &parent, uint64 startOffset) : + GenericFileElement(parent, startOffset) +{} + +/*! + * \brief Returns the parsing context. + */ +string Mpeg4Descriptor::parsingContext() const { - + return "parsing " + idToString() + " descriptor at " + numberToString(startOffset()); } +/*! + * \brief Converts the specified atom \a ID to a printable string. + */ +std::string Mpeg4Descriptor::idToString() const +{ + return "0x" + ConversionUtilities::numberToString(id(), 16); +} + +/*! + * \brief Parses the MPEG-4 descriptor. + * \remarks Does not detect the first child. + */ +void Mpeg4Descriptor::internalParse() +{ + invalidateStatus(); + if(maxTotalSize() < 4) { + addNotification(NotificationType::Critical, "Descriptor is smaller then 4 byte and hence invalid. The maximum size within the encloding element is " + numberToString(maxTotalSize()) + ".", "parsing MPEG-4 descriptor"); + throw TruncatedDataException(); + } + stream().seekg(startOffset()); + // read ID + m_idLength = m_sizeLength = 1; + m_id = reader().readByte(); + // read data size + byte tmp = reader().readByte() & 0x80; + m_dataSize = tmp & 0x7F; + while(tmp & 0x80) { + m_dataSize = (m_dataSize << 7) | ((tmp = reader().readByte()) & 0x7F); + ++m_sizeLength; + } + // check whether the denoted data size exceeds the available data size + if(maxTotalSize() < totalSize()) { + addNotification(NotificationType::Warning, "The descriptor seems to be truncated; unable to parse siblings of that ", parsingContext()); + m_dataSize = maxTotalSize(); // using max size instead + } + m_firstChild.reset(); + implementationType *sibling = nullptr; + if(totalSize() < maxTotalSize()) { + if(parent()) { + sibling = new implementationType(*(parent()), startOffset() + totalSize()); + } else { + sibling = new implementationType(container(), startOffset() + totalSize(), maxTotalSize() - totalSize()); + } + } +} + +} diff --git a/mp4/mpeg4descriptor.h b/mp4/mpeg4descriptor.h index 3cf1958..bf2eb2f 100644 --- a/mp4/mpeg4descriptor.h +++ b/mp4/mpeg4descriptor.h @@ -1,11 +1,103 @@ -#ifndef MPEG4STREAMDESCRIPTOR_H -#define MPEG4STREAMDESCRIPTOR_H +#ifndef MPEG4DESCRIPTOR_H +#define MPEG4DESCRIPTOR_H +#include "../genericfileelement.h" -class Mpeg4StreamDescriptor +#include + +namespace Media { + +class Mp4Container; +class Mpeg4Descriptor; + +/*! + * \brief Defines traits for the GenericFileElement implementation Mpeg4Descriptor. + */ +template <> +class LIB_EXPORT FileElementTraits { public: - Mpeg4StreamDescriptor(); + /*! + * \brief The container type used to store such elements is Mp4Container. + */ + typedef Mp4Container containerType; + + /*! + * \brief The type used to store atom IDs is an unsigned 32-bit integer. + */ + typedef byte identifierType; + + /*! + * \brief The type used to store element sizes is an unsigned 32-bit integer. + */ + typedef uint32 dataSizeType; + + /*! + * \brief The implementation type is Mp4Atom. + */ + typedef Mpeg4Descriptor implementationType; }; -#endif // MPEG4STREAMDESCRIPTOR_H +class LIB_EXPORT Mpeg4Descriptor : public GenericFileElement +{ + friend class GenericFileElement; + +public: + Mpeg4Descriptor(containerType& container, uint64 startOffset, uint64 maxSize); + + std::string idToString() const; + bool isParent() const; + bool isPadding() const; + uint64 firstChildOffset() const; + implementationType *denoteFirstChild(uint32 offset); + +protected: + Mpeg4Descriptor(implementationType &parent, uint64 startOffset); + + void internalParse(); + +private: + std::string parsingContext() const; +}; + +/*! + * \brief Returns an indication whether the descriptor contains sub descriptors. + * + * \remarks Returns true if a first child has been denoted (via denoteFirstChild()). + */ +inline bool Mpeg4Descriptor::isParent() const +{ + return m_firstChild != nullptr; +} + +/*! + * \brief Returns always false for MPEG-4 descriptors. + */ +inline bool Mpeg4Descriptor::isPadding() const +{ + return false; +} + +/*! + * \brief Returns the offset of the first child (relative to the start offset of this descriptor). + * + * \remarks The first child must be denoted (via denoteFirstChild()). + */ +inline uint64 Mpeg4Descriptor::firstChildOffset() const +{ + return firstChild() ? firstChild()->startOffset() - startOffset() : 0; +} + +/*! + * \brief Denotes the first child to start at the specified \a offset (relative to the start offset of this descriptor). + * \remarks A new first child is constructed. A possibly existing subtree is invalidated. + */ +inline Mpeg4Descriptor::implementationType *Mpeg4Descriptor::denoteFirstChild(uint32 relativeFirstChildOffset) +{ + m_firstChild.reset(new implementationType(static_cast(*this), startOffset() + relativeFirstChildOffset)); + return m_firstChild.get(); +} + +} + +#endif // MPEG4DESCRIPTOR_H diff --git a/mpegaudio/mpegaudioframestream.cpp b/mpegaudio/mpegaudioframestream.cpp index ee0def9..3f52979 100644 --- a/mpegaudio/mpegaudioframestream.cpp +++ b/mpegaudio/mpegaudioframestream.cpp @@ -24,7 +24,7 @@ namespace Media { MpegAudioFrameStream::MpegAudioFrameStream(iostream &stream, uint64 startOffset) : AbstractTrack(stream, startOffset) { - m_mediaType = MediaType::Acoustic; + m_mediaType = MediaType::Audio; } /*! @@ -56,19 +56,7 @@ void MpegAudioFrameStream::internalParseHeader() MpegAudioFrame frame; frame.parseHeader(*m_istream); m_version = frame.mpegVersion(); - switch(frame.layer()) { - case 1: - m_format = MediaFormat::MpegL1; - break; - case 2: - m_format = MediaFormat::MpegL2; - break; - case 3: - m_format = MediaFormat::MpegL3; - break; - default: - m_format = MediaFormat::Unknown; - } + m_format = MediaFormat(GeneralMediaFormat::Mpeg1Audio, frame.layer()); m_channelCount = frame.channelMode() == MpegChannelMode::SingleChannel ? 1 : 2; m_samplesPerSecond = frame.samperate(); if(frame.isXingBytesfieldPresent()) { diff --git a/ogg/oggstream.cpp b/ogg/oggstream.cpp index 7a3949c..ffeb641 100644 --- a/ogg/oggstream.cpp +++ b/ogg/oggstream.cpp @@ -61,12 +61,12 @@ void OggStream::internalParseHeader() if((sig & 0x00ffffffffffff00u) == 0x00766F7262697300u) { // Vorbis header detected // set Vorbis as format - switch(m_format) { - case MediaFormat::Unknown: - m_format = MediaFormat::Vorbis; - m_mediaType = MediaType::Acoustic; + switch(m_format.general) { + case GeneralMediaFormat::Unknown: + m_format = GeneralMediaFormat::Vorbis; + m_mediaType = MediaType::Audio; break; - case MediaFormat::Vorbis: + case GeneralMediaFormat::Vorbis: break; default: addNotification(NotificationType::Warning, "Stream format is inconsistent.", context); diff --git a/signature.cpp b/signature.cpp index 857b65e..fa478ef 100644 --- a/signature.cpp +++ b/signature.cpp @@ -226,14 +226,14 @@ const char *containerFormatAbbreviation(ContainerFormat containerFormat, MediaTy case ContainerFormat::Lzw: return "lzw"; case ContainerFormat::Mp4: switch(mediaType) { - case MediaType::Acoustic: + case MediaType::Audio: return "m4a"; default: return "mp4"; } case ContainerFormat::Ogg: return "ogg"; switch(mediaType) { - case MediaType::Visual: + case MediaType::Video: return "ogv"; default: return "ogg"; @@ -244,7 +244,7 @@ const char *containerFormatAbbreviation(ContainerFormat containerFormat, MediaTy case ContainerFormat::Rar: return "rar"; case ContainerFormat::Matroska: switch(mediaType) { - case MediaType::Acoustic: + case MediaType::Audio: return "mka"; default: return "mkv"; @@ -393,21 +393,21 @@ const char *containerMimeType(ContainerFormat containerFormat, MediaType mediaTy return "audio/mpeg"; case ContainerFormat::Mp4: switch(mediaType) { - case MediaType::Acoustic: + case MediaType::Audio: return "audio/mp4"; default: return "video/mp4"; } case ContainerFormat::Ogg: switch(mediaType) { - case MediaType::Acoustic: + case MediaType::Audio: return "audio/ogg"; default: return "video/ogg"; } case ContainerFormat::Matroska: switch(mediaType) { - case MediaType::Acoustic: + case MediaType::Audio: return "audio/x-matroska"; default: return "video/x-matroska"; diff --git a/size.h b/size.h index 5a602f2..7803f7a 100644 --- a/size.h +++ b/size.h @@ -94,7 +94,7 @@ inline constexpr bool Size::isNull() const inline std::string Size::toString() const { std::stringstream res; - res << "width: " << m_width << "; height: " << m_height; + res << "width: " << m_width << ", height: " << m_height; return std::string(res.str()); } diff --git a/tagparser.pro b/tagparser.pro index 0b055dc..e4bafdf 100644 --- a/tagparser.pro +++ b/tagparser.pro @@ -67,7 +67,8 @@ SOURCES += \ abstractattachment.cpp \ matroska/matroskaattachment.cpp \ mediaformat.cpp \ - avc/avcconfiguration.cpp + avc/avcconfiguration.cpp \ + mp4/mpeg4descriptor.cpp HEADERS += \ abstractcontainer.h \ @@ -132,7 +133,8 @@ HEADERS += \ matroska/matroskaattachment.h \ mediaformat.h \ avc/avcconfiguration.h \ - generictagfield.h + generictagfield.h \ + mp4/mpeg4descriptor.h LIBS += -lz diff --git a/wav/waveaudiostream.cpp b/wav/waveaudiostream.cpp index cae44a6..ca7bc82 100644 --- a/wav/waveaudiostream.cpp +++ b/wav/waveaudiostream.cpp @@ -19,7 +19,7 @@ namespace Media { WaveAudioStream::WaveAudioStream(iostream &stream, uint64 startOffset) : AbstractTrack(stream, startOffset) { - m_mediaType = MediaType::Acoustic; + m_mediaType = MediaType::Audio; } /*! @@ -46,16 +46,16 @@ void WaveAudioStream::internalParseHeader() if(restHeaderLen >= 16u) { switch(m_reader.readUInt16LE()) { case 0x0001u: - m_format = MediaFormat::Pcm; + m_format = GeneralMediaFormat::Pcm; break; case 0x0050u: - m_format = MediaFormat::MpegL2; + m_format = MediaFormat(GeneralMediaFormat::Mpeg1Audio, SubFormats::Mpeg1Layer2); break; case 0x0055u: - m_format = MediaFormat::MpegL3; + m_format = MediaFormat(GeneralMediaFormat::Mpeg1Audio, SubFormats::Mpeg1Layer3); break; default: - m_format = MediaFormat::Unknown; + m_format = GeneralMediaFormat::Unknown; } m_channelCount = m_reader.readUInt16LE(); m_samplesPerSecond = m_reader.readUInt32LE(); @@ -64,7 +64,7 @@ void WaveAudioStream::internalParseHeader() m_bitsPerSample = m_reader.readUInt16LE(); m_bitrate = m_bitsPerSample * m_samplesPerSecond * m_channelCount; } else { - m_format = MediaFormat::Unknown; + m_format = GeneralMediaFormat::Unknown; } if(restHeaderLen > 16u) { m_istream->seekg(m_dataOffset, ios_base::beg);