From 9f12607a0de3c3254d9c2b7af98d317ddfea32a2 Mon Sep 17 00:00:00 2001 From: Martchus Date: Fri, 31 Jul 2015 01:09:41 +0200 Subject: [PATCH] added channel config to AbstractTrack and the implementations --- abstracttrack.cpp | 19 +++++++++++++++++ abstracttrack.h | 22 +++++++++++++++---- adts/adtsframe.cpp | 7 +++++- adts/adtsframe.h | 34 +++++++++++++++++++++++------- adts/adtsstream.cpp | 4 ++-- mp4/mp4ids.cpp | 8 ++++++- mp4/mp4track.cpp | 1 + mpegaudio/mpegaudioframe.cpp | 28 ++++++++++++++++++------ mpegaudio/mpegaudioframe.h | 4 +++- mpegaudio/mpegaudioframestream.cpp | 5 +++-- 10 files changed, 106 insertions(+), 26 deletions(-) diff --git a/abstracttrack.cpp b/abstracttrack.cpp index 0cc4d17..5a20f63 100644 --- a/abstracttrack.cpp +++ b/abstracttrack.cpp @@ -1,3 +1,6 @@ +#include "mp4/mp4ids.h" +#include "mpegaudio/mpegaudioframe.h" + #include "abstracttrack.h" #include "exceptions.h" #include "mediaformat.h" @@ -45,6 +48,7 @@ AbstractTrack::AbstractTrack(istream &inputStream, ostream &outputStream, uint64 m_bitsPerSample(0), m_bytesPerSecond(0), m_channelCount(0), + m_channelConfig(0), m_sampleCount(0), m_quality(0), m_depth(0), @@ -99,6 +103,21 @@ const char *AbstractTrack::mediaTypeName() const } } +/*! + * \brief Returns a string with the channel configuration if available; otherwise returns nullptr. + */ +const char *AbstractTrack::channelConfigString() const +{ + switch(m_format.general) { + case GeneralMediaFormat::Aac: + return Mpeg4ChannelConfigs::channelConfigString(m_channelConfig); + case GeneralMediaFormat::Mpeg1Audio: case GeneralMediaFormat::Mpeg2Audio: + return mpegChannelModeString(static_cast(m_channelConfig)); + default: + return nullptr; + } +} + /*! * \brief Returns a label for the track. */ diff --git a/abstracttrack.h b/abstracttrack.h index 59fff59..0f9ed6f 100644 --- a/abstracttrack.h +++ b/abstracttrack.h @@ -69,10 +69,12 @@ public: const ChronoUtilities::DateTime &creationTime() const; const ChronoUtilities::DateTime &modificationTime() const; const std::string &language() const; - uint32 sampleRate() const; - uint32 extensionSampleRate() const; + uint32 samplingFrequency() const; + uint32 extensionSamplingFrequency() const; uint16 bitsPerSample() const; uint16 channelCount() const; + byte channelConfig() const; + const char *channelConfigString() const; uint64 sampleCount() const; int quality() const; const Size &pixelSize() const; @@ -126,6 +128,7 @@ protected: uint16 m_bitsPerSample; uint32 m_bytesPerSecond; uint16 m_channelCount; + byte m_channelConfig; uint16 m_chunkSize; uint64 m_sampleCount; int m_quality; @@ -366,7 +369,7 @@ inline const std::string &AbstractTrack::language() const /*! * \brief Returns the number of samples per second if known; otherwise returns 0. */ -inline uint32 AbstractTrack::sampleRate() const +inline uint32 AbstractTrack::samplingFrequency() const { return m_sampleRate; } @@ -375,7 +378,7 @@ inline uint32 AbstractTrack::sampleRate() const * \brief Returns the number of samples per second if known; otherwise returns 0. * \remarks This sample rate value takes extensions like SBR into account. */ -inline uint32 AbstractTrack::extensionSampleRate() const +inline uint32 AbstractTrack::extensionSamplingFrequency() const { return m_extensionSampleRate; } @@ -398,6 +401,17 @@ inline uint16 AbstractTrack::channelCount() const return m_channelCount; } +/*! + * \brief Returns the channel configuration. + * + * This is the MPEG-4 channel config for MPEG-4 audio. + * \sa Mpeg4ChannelConfigs::channelConfigString() + */ +inline byte AbstractTrack::channelConfig() const +{ + return m_channelConfig; +} + /*! * \brief Returns the number of samples if known; otherwise returns 0. */ diff --git a/adts/adtsframe.cpp b/adts/adtsframe.cpp index 4a49ecb..f8142bb 100644 --- a/adts/adtsframe.cpp +++ b/adts/adtsframe.cpp @@ -21,10 +21,15 @@ namespace Media { void AdtsFrame::parseHeader(IoUtilities::BinaryReader &reader) { m_header1 = reader.readUInt16BE(); - if(!isValid()) { + // check whether syncword is present + if((m_header1 & 0xFFF6u) != 0xFFF0u) { throw InvalidDataException(); } m_header2 = hasCrc() ? reader.readUInt56BE() : (reader.readUInt40BE() << 2); + // check whether frame length is ok + if(totalSize() < headerSize()) { + throw InvalidDataException(); + } } } // namespace Media diff --git a/adts/adtsframe.h b/adts/adtsframe.h index a0c0c9e..9b31cbd 100644 --- a/adts/adtsframe.h +++ b/adts/adtsframe.h @@ -21,9 +21,11 @@ public: bool isMpeg4() const; bool hasCrc() const; byte mpeg4AudioObjectId() const; - byte mpeg4SampleRateIndex() const; + byte mpeg4SamplingFrequencyIndex() const; byte mpeg4ChannelConfig() const; - uint16 length() const; + uint16 totalSize() const; + byte headerSize() const; + uint16 dataSize() const; uint16 bufferFullness() const; byte frameCount() const; uint16 crc() const; @@ -45,7 +47,7 @@ inline AdtsFrame::AdtsFrame() : */ inline bool AdtsFrame::isValid() const { - return (m_header1 & 0xFFF6u) == 0xFFF0u; + return ((m_header1 & 0xFFF6u) == 0xFFF0u) && (totalSize() >= headerSize()); } /*! @@ -57,11 +59,11 @@ inline bool AdtsFrame::isMpeg4() const } /*! - * \brief Returns whether a CRC-16 checksum is present. + * \brief Returns whether a CRC-16 checksum is present ("protection absent" bit is NOT set). */ inline bool AdtsFrame::hasCrc() const { - return m_header1 & 0x1u; + return (m_header1 & 0x1u) == 0; } /*! @@ -78,7 +80,7 @@ inline byte AdtsFrame::mpeg4AudioObjectId() const * \brief Returns the MPEG-4 sample rate index. * \sa Media::mpeg4SampleRateTable */ -inline byte AdtsFrame::mpeg4SampleRateIndex() const +inline byte AdtsFrame::mpeg4SamplingFrequencyIndex() const { return (m_header2 >> 0x32) & 0xFu; } @@ -94,13 +96,29 @@ inline byte AdtsFrame::mpeg4ChannelConfig() const } /*! - * \brief Returns the length of the frame (including the header) in bytes. + * \brief Returns the size of the frame (including the header) in bytes. */ -inline uint16 AdtsFrame::length() const +inline uint16 AdtsFrame::totalSize() const { return (m_header2 >> 0x1D) & 0x1FFFu; } +/*! + * \brief Retruns the header size in bytes (9 if CRC is present; otherwise 7). + */ +inline byte AdtsFrame::headerSize() const +{ + return hasCrc() ? 9 : 7; +} + +/*! + * \brief Returns the data size (total size minus header size) in bytes. + */ +inline uint16 AdtsFrame::dataSize() const +{ + return totalSize() - headerSize(); +} + /*! * \brief Returns the buffer fullness. */ diff --git a/adts/adtsstream.cpp b/adts/adtsstream.cpp index adcfd7a..242d4d0 100644 --- a/adts/adtsstream.cpp +++ b/adts/adtsstream.cpp @@ -31,8 +31,8 @@ void AdtsStream::internalParseHeader() // 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_channelCount = Mpeg4ChannelConfigs::channelCount(m_channelConfig = m_firstFrame.mpeg4ChannelConfig()); + byte sampleRateIndex = m_firstFrame.mpeg4SamplingFrequencyIndex(); m_sampleRate = sampleRateIndex < sizeof(mpeg4SampleRateTable) ? mpeg4SampleRateTable[sampleRateIndex] : 0; } diff --git a/mp4/mp4ids.cpp b/mp4/mp4ids.cpp index 8a56b21..4000b22 100644 --- a/mp4/mp4ids.cpp +++ b/mp4/mp4ids.cpp @@ -286,6 +286,9 @@ uint32 mpeg4SampleRateTable[] = { namespace Mpeg4ChannelConfigs { +/*! + * \brief Returns the string representation for the specified MPEG-4 channel config. + */ const char *channelConfigString(byte config) { switch(config) { @@ -306,10 +309,13 @@ const char *channelConfigString(byte config) case FrontCenterFrontLeftFrontRightSideLeftSideRightBackLeftBackRightLFEChannel: return "8 channels: front-center, front-left, front-right, side-left, side-right, back-left, back-right, LFE-channel"; default: - return "unknown"; + return nullptr; } } +/*! + * \brief Returns the channel count for the specified MPEG-4 channel config. + */ byte channelCount(byte config) { switch(config) { diff --git a/mp4/mp4track.cpp b/mp4/mp4track.cpp index 0b11199..9974c73 100644 --- a/mp4/mp4track.cpp +++ b/mp4/mp4track.cpp @@ -1378,6 +1378,7 @@ void Mp4Track::internalParseHeader() } else { addNotification(NotificationType::Warning, "Audio specific config has invalid extension sample frequency index.", context); } + m_channelConfig = m_esInfo->audioSpecificConfig->channelConfiguration; } if(m_esInfo->videoSpecificConfig) { // check the video specific config for useful information diff --git a/mpegaudio/mpegaudioframe.cpp b/mpegaudio/mpegaudioframe.cpp index dbf1fc6..5c20675 100644 --- a/mpegaudio/mpegaudioframe.cpp +++ b/mpegaudio/mpegaudioframe.cpp @@ -8,6 +8,20 @@ using namespace IoUtilities; namespace Media { +/*! + * \brief Returns the string representation for the specified \a channelMode. + */ +const char *mpegChannelModeString(MpegChannelMode channelMode) +{ + switch(channelMode) { + case MpegChannelMode::Stereo: return "2 channels: stereo"; + case MpegChannelMode::JointStereo: return "2 channels: joint stereo"; + case MpegChannelMode::DualChannel: return "2 channels: dual channel"; + case MpegChannelMode::SingleChannel: return "1 channel: single channel"; + case MpegChannelMode::Unspecifed: return nullptr; + } +} + const uint64 MpegAudioFrame::m_xingHeaderOffset = 0x24; /*! @@ -35,6 +49,9 @@ const uint32 MpegAudioFrame::m_sync = 0xFFE00000u; void MpegAudioFrame::parseHeader(BinaryReader &reader) { m_header = reader.readUInt32BE(); + if(!isValid()) { + throw InvalidDataException(); + } reader.stream()->seekg(m_xingHeaderOffset - 4, ios_base::cur); m_xingHeader = reader.readUInt64BE(); m_xingHeaderFlags = static_cast(m_xingHeader & 0xffffffffuL); @@ -52,9 +69,6 @@ void MpegAudioFrame::parseHeader(BinaryReader &reader) m_xingQualityIndicator = reader.readUInt32BE(); } } - if(!isValid()) { - throw InvalidDataException(); - } } /*! @@ -92,9 +106,9 @@ int MpegAudioFrame::layer() const } /*! - * \brief Returns the sampel rate of the frame if known; otherwise returns 0. + * \brief Returns the sampeling frequency of the frame if known; otherwise returns 0. */ -uint32 MpegAudioFrame::sampelRate() const +uint32 MpegAudioFrame::samplingFrequency() const { switch (m_header & 0xc00u) { case 0xc00u: @@ -186,10 +200,10 @@ uint32 MpegAudioFrame::size() const { switch (m_header & 0x60000u) { case 0x60000u: - return static_cast(((static_cast(bitrate()) * 1024.0 / 8.0) / static_cast(sampelRate())) * static_cast(sampleCount()) + static_cast(paddingSize())); + return static_cast(((static_cast(bitrate()) * 1024.0 / 8.0) / static_cast(samplingFrequency())) * static_cast(sampleCount()) + static_cast(paddingSize())); case 0x40000u: case 0x20000u: - return static_cast(((static_cast(bitrate()) * 1024.0 / 8.0) / static_cast(sampelRate())) * static_cast(sampleCount()) + static_cast(paddingSize())); + return static_cast(((static_cast(bitrate()) * 1024.0 / 8.0) / static_cast(samplingFrequency())) * static_cast(sampleCount()) + static_cast(paddingSize())); default: return 0; } diff --git a/mpegaudio/mpegaudioframe.h b/mpegaudio/mpegaudioframe.h index ac6c45d..bc73e7e 100644 --- a/mpegaudio/mpegaudioframe.h +++ b/mpegaudio/mpegaudioframe.h @@ -25,6 +25,8 @@ enum class MpegChannelMode Unspecifed /**< used to indicate that the channel mode is unknown */ }; +LIB_EXPORT const char *mpegChannelModeString(MpegChannelMode channelMode); + enum class XingHeaderFlags { None = 0x0u, /**< No Xing frames are present */ @@ -46,7 +48,7 @@ public: int layer() const; bool isProtectedByCrc() const; uint32 bitrate() const; - uint32 sampelRate() const; + uint32 samplingFrequency() const; uint32 paddingSize() const; MpegChannelMode channelMode() const; bool hasCopyright() const; diff --git a/mpegaudio/mpegaudioframestream.cpp b/mpegaudio/mpegaudioframestream.cpp index ac219ab..e37add2 100644 --- a/mpegaudio/mpegaudioframestream.cpp +++ b/mpegaudio/mpegaudioframestream.cpp @@ -25,7 +25,8 @@ void MpegAudioFrameStream::addInfo(const MpegAudioFrame &frame, AbstractTrack &t track.m_version = frame.mpegVersion(); track.m_format = MediaFormat(GeneralMediaFormat::Mpeg1Audio, frame.layer()); track.m_channelCount = frame.channelMode() == MpegChannelMode::SingleChannel ? 1 : 2; - track.m_sampleRate = frame.sampelRate(); + track.m_channelConfig = static_cast(frame.channelMode()); + track.m_sampleRate = frame.samplingFrequency(); } void MpegAudioFrameStream::internalParseHeader() @@ -55,7 +56,7 @@ void MpegAudioFrameStream::internalParseHeader() } } m_bitrate = frame.isXingFramefieldPresent() - ? ((static_cast(m_size) * 8.0) / (static_cast(frame.xingFrameCount() * frame.sampleCount()) / static_cast(frame.sampelRate())) / 1024.0) + ? ((static_cast(m_size) * 8.0) / (static_cast(frame.xingFrameCount() * frame.sampleCount()) / static_cast(frame.samplingFrequency())) / 1024.0) : frame.bitrate(); m_bytesPerSecond = m_bitrate * 125; m_duration = TimeSpan::fromSeconds(static_cast(m_size) / (m_bitrate * 128.0));