diff --git a/taglib/fileref.cpp b/taglib/fileref.cpp index a7e690b7..d20007c8 100644 --- a/taglib/fileref.cpp +++ b/taglib/fileref.cpp @@ -134,7 +134,7 @@ namespace File *file = nullptr; - if(ext == "MP3") + if(ext == "MP3" || ext == "AAC") file = new MPEG::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle); else if(ext == "OGG") file = new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle); @@ -410,6 +410,7 @@ StringList FileRef::defaultFileExtensions() l.append("wv"); l.append("spx"); l.append("tta"); + l.append("aac"); l.append("m4a"); l.append("m4r"); l.append("m4b"); diff --git a/taglib/mpeg/mpegfile.cpp b/taglib/mpeg/mpegfile.cpp index 39f44353..9c4166d2 100644 --- a/taglib/mpeg/mpegfile.cpp +++ b/taglib/mpeg/mpegfile.cpp @@ -479,7 +479,7 @@ void MPEG::File::read(bool readProperties, Properties::ReadStyle readStyle) } if(readProperties) - d->properties = std::make_unique(this); + d->properties = std::make_unique(this, readStyle); // Make sure that we have our default tag types available. diff --git a/taglib/mpeg/mpegheader.cpp b/taglib/mpeg/mpegheader.cpp index dd2411d3..d87c38df 100644 --- a/taglib/mpeg/mpegheader.cpp +++ b/taglib/mpeg/mpegheader.cpp @@ -45,6 +45,7 @@ public: int sampleRate { 0 }; bool isPadded { false }; ChannelMode channelMode { Stereo }; + ChannelConfiguration channelConfiguration { Custom }; bool isCopyrighted { false }; bool isOriginal { false }; int frameLength { 0 }; @@ -104,6 +105,17 @@ MPEG::Header::ChannelMode MPEG::Header::channelMode() const return d->channelMode; } +MPEG::Header::ChannelConfiguration MPEG::Header::channelConfiguration() const +{ + return d->channelConfiguration; +} + +bool MPEG::Header::isADTS() const +{ + // See detection in parse(). + return d->layer == 0 && (d->version == Version2 || d->version == Version4); +} + bool MPEG::Header::isCopyrighted() const { return d->isCopyrighted; @@ -177,89 +189,143 @@ void MPEG::Header::parse(File *file, offset_t offset, bool checkLength) d->layer = 2; else if(layerBits == 3) d->layer = 1; - else - return; + else { + // layerBits == 0 is reserved in the + // + // MPEG Audio Layer I/II/III frame header, for + // ADTS + // they are always set to 0. + // Bit 1 of versionBits is bit 4 of the 2nd header word. For ADTS + // it must be set to 1, therefore these three bits are used to detect + // that this header is from an ADTS file. + if(versionBits == 2) { + d->version = Version4; + d->layer = 0; + } + else if(versionBits == 3) { + d->version = Version2; + d->layer = 0; + } + else { + return; + } + } d->protectionEnabled = (static_cast(data[1] & 0x01) == 0); - // Set the bitrate + if(isADTS()) { + static constexpr std::array sampleRates { + 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, + 16000, 12000, 11025, 8000, 7350, 0, 0, 0 + }; - static constexpr std::array bitrates { - std::array { - // Version 1 - std::array { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0 }, // layer 1 - std::array { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0 }, // layer 2 - std::array { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0 } // layer 3 - }, - std::array { - // Version 2 or 2.5 - std::array { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0 }, // layer 1 - std::array { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // layer 2 - std::array { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 } // layer 3 - }, - }; + const int sampleRateIndex = (static_cast(data[2]) >> 2) & 0x0F; + d->sampleRate = sampleRates[sampleRateIndex]; + d->samplesPerFrame = 1024; - const int versionIndex = (d->version == Version1) ? 0 : 1; - const int layerIndex = (d->layer > 0) ? d->layer - 1 : 0; + d->channelConfiguration = static_cast( + ((static_cast(data[3]) >> 6) & 0x03) | + ((static_cast(data[2]) << 2) & 0x04)); + d->channelMode = d->channelConfiguration == FrontCenter ? SingleChannel : Stereo; - // The bitrate index is encoded as the first 4 bits of the 3rd byte, - // i.e. 1111xxxx + // TODO: Add mode extension for completeness - const int bitrateIndex = (static_cast(data[2]) >> 4) & 0x0F; + d->isOriginal = (static_cast(data[3]) & 0x20) != 0; + d->isCopyrighted = (static_cast(data[3]) & 0x04) != 0; - d->bitrate = bitrates[versionIndex][layerIndex][bitrateIndex]; + // Calculate the frame length + const ByteVector frameLengthData = file->readBlock(2); - if(d->bitrate == 0) - return; + if(frameLengthData.size() >= 2) { + d->frameLength = (static_cast(data[3]) & 0x3) << 11 | + (static_cast(frameLengthData[0]) << 3) | + (static_cast(frameLengthData[1]) >> 5); - // Set the sample rate - - static constexpr std::array sampleRates { - std::array { 44100, 48000, 32000, 0 }, // Version 1 - std::array { 22050, 24000, 16000, 0 }, // Version 2 - std::array { 11025, 12000, 8000, 0 } // Version 2.5 - }; - - // The sample rate index is encoded as two bits in the 3rd byte, i.e. xxxx11xx - - const int samplerateIndex = (static_cast(data[2]) >> 2) & 0x03; - - d->sampleRate = sampleRates[d->version][samplerateIndex]; - - if(d->sampleRate == 0) { - return; + d->bitrate = static_cast(d->frameLength * d->sampleRate / 1024.0 + 0.5) * 8 / 1024; + } } + else { + // Not ADTS - // The channel mode is encoded as a 2 bit value at the end of the 3rd byte, - // i.e. xxxxxx11 + // Set the bitrate - d->channelMode = static_cast((static_cast(data[3]) >> 6) & 0x03); + static constexpr std::array bitrates { + std::array { + // Version 1 + std::array { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0 }, // layer 1 + std::array { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0 }, // layer 2 + std::array { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0 } // layer 3 + }, + std::array { + // Version 2 or 2.5 + std::array { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0 }, // layer 1 + std::array { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // layer 2 + std::array { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 } // layer 3 + }, + }; - // TODO: Add mode extension for completeness + const int versionIndex = (d->version == Version1) ? 0 : 1; + const int layerIndex = (d->layer > 0) ? d->layer - 1 : 0; - d->isOriginal = ((static_cast(data[3]) & 0x04) != 0); - d->isCopyrighted = ((static_cast(data[3]) & 0x08) != 0); - d->isPadded = ((static_cast(data[2]) & 0x02) != 0); + // The bitrate index is encoded as the first 4 bits of the 3rd byte, + // i.e. 1111xxxx - // Samples per frame + const int bitrateIndex = (static_cast(data[2]) >> 4) & 0x0F; - static constexpr std::array samplesPerFrame { - // MPEG1, 2/2.5 - std::pair(384, 384), // Layer I - std::pair(1152, 1152), // Layer II - std::pair(1152, 576), // Layer III - }; + d->bitrate = bitrates[versionIndex][layerIndex][bitrateIndex]; - d->samplesPerFrame = versionIndex ? samplesPerFrame[layerIndex].second : samplesPerFrame[layerIndex].first; + if(d->bitrate == 0) + return; - // Calculate the frame length + // Set the sample rate - static constexpr std::array paddingSize { 4, 1, 1 }; + static constexpr std::array sampleRates { + std::array { 44100, 48000, 32000, 0 }, // Version 1 + std::array { 22050, 24000, 16000, 0 }, // Version 2 + std::array { 11025, 12000, 8000, 0 } // Version 2.5 + }; - d->frameLength = d->samplesPerFrame * d->bitrate * 125 / d->sampleRate; + // The sample rate index is encoded as two bits in the 3rd byte, i.e. xxxx11xx - if(d->isPadded) - d->frameLength += paddingSize[layerIndex]; + const int samplerateIndex = (static_cast(data[2]) >> 2) & 0x03; + + d->sampleRate = sampleRates[d->version][samplerateIndex]; + + if(d->sampleRate == 0) { + return; + } + + // The channel mode is encoded as a 2 bit value at the end of the 3rd byte, + // i.e. xxxxxx11 + + d->channelMode = static_cast((static_cast(data[3]) >> 6) & 0x03); + + // TODO: Add mode extension for completeness + + d->isOriginal = ((static_cast(data[3]) & 0x04) != 0); + d->isCopyrighted = ((static_cast(data[3]) & 0x08) != 0); + d->isPadded = ((static_cast(data[2]) & 0x02) != 0); + + // Samples per frame + + static constexpr std::array samplesPerFrame { + // MPEG1, 2/2.5 + std::pair(384, 384), // Layer I + std::pair(1152, 1152), // Layer II + std::pair(1152, 576), // Layer III + }; + + d->samplesPerFrame = versionIndex ? samplesPerFrame[layerIndex].second : samplesPerFrame[layerIndex].first; + + // Calculate the frame length + + static constexpr std::array paddingSize { 4, 1, 1 }; + + d->frameLength = d->samplesPerFrame * d->bitrate * 125 / d->sampleRate; + + if(d->isPadded) + d->frameLength += paddingSize[layerIndex]; + } if(checkLength) { diff --git a/taglib/mpeg/mpegheader.h b/taglib/mpeg/mpegheader.h index bba706a3..33a13b4f 100644 --- a/taglib/mpeg/mpegheader.h +++ b/taglib/mpeg/mpegheader.h @@ -84,7 +84,9 @@ namespace TagLib { //! MPEG Version 2 Version2 = 1, //! MPEG Version 2.5 - Version2_5 = 2 + Version2_5 = 2, + //! MPEG Version 4 + Version4 = 3 }; /*! @@ -137,6 +139,31 @@ namespace TagLib { */ ChannelMode channelMode() const; + /*! + * MPEG-4 channel configuration. + */ + enum ChannelConfiguration { + Custom = 0, + FrontCenter = 1, + FrontLeftRight = 2, + FrontCenterLeftRight = 3, + FrontCenterLeftRightBackCenter = 4, + FrontCenterLeftRightBackLeftRight = 5, + FrontCenterLeftRightBackLeftRightLFE = 6, + FrontCenterLeftRightSideLeftRightBackLeftRightLFE = 7 + }; + + /*! + * Returns the MPEG-4 channel configuration. + */ + ChannelConfiguration channelConfiguration() const; + + /*! + * Returns true if this is the header of an Audio Data Transport Stream + * (ADTS), usually AAC. + */ + bool isADTS() const; + /*! * Returns true if the copyrighted bit is set. */ diff --git a/taglib/mpeg/mpegproperties.cpp b/taglib/mpeg/mpegproperties.cpp index 5f030d81..64bb43c3 100644 --- a/taglib/mpeg/mpegproperties.cpp +++ b/taglib/mpeg/mpegproperties.cpp @@ -44,6 +44,7 @@ public: int layer { 0 }; Header::Version version { Header::Version1 }; Header::ChannelMode channelMode { Header::Stereo }; + Header::ChannelConfiguration channelConfiguration { Header::Custom }; bool protectionEnabled { false }; bool isCopyrighted { false }; bool isOriginal { false }; @@ -57,7 +58,7 @@ MPEG::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style), d(std::make_unique()) { - read(file); + read(file, style); } MPEG::Properties::~Properties() = default; @@ -107,6 +108,17 @@ MPEG::Header::ChannelMode MPEG::Properties::channelMode() const return d->channelMode; } +MPEG::Header::ChannelConfiguration MPEG::Properties::channelConfiguration() const +{ + return d->channelConfiguration; +} + +bool MPEG::Properties::isADTS() const +{ + return d->layer == 0 && + (d->version == Header::Version2 || d->version == Header::Version4); +} + bool MPEG::Properties::isCopyrighted() const { return d->isCopyrighted; @@ -121,7 +133,7 @@ bool MPEG::Properties::isOriginal() const // private members //////////////////////////////////////////////////////////////////////////////// -void MPEG::Properties::read(File *file) +void MPEG::Properties::read(File *file, ReadStyle readStyle) { // Only the first valid frame is required if we have a VBR header. @@ -152,33 +164,113 @@ void MPEG::Properties::read(File *file) d->length = static_cast(length + 0.5); d->bitrate = static_cast(d->xingHeader->totalSize() * 8.0 / length + 0.5); } - else if(firstHeader.bitrate() > 0) { - - // Since there was no valid VBR header found, we hope that we're in a constant - // bitrate file. - - // TODO: Make this more robust with audio property detection for VBR without a - // Xing header. - - d->bitrate = firstHeader.bitrate(); - - // Look for the last MPEG audio frame to calculate the stream length. - - const offset_t lastFrameOffset = file->lastFrameOffset(); - if(lastFrameOffset < 0) { - debug("MPEG::Properties::read() -- Could not find an MPEG frame in the stream."); + else { + int bitrate = firstHeader.bitrate(); + if(firstHeader.isADTS()) { + // ADTS is probably VBR, so to get the real length, we would have to go + // through all frames, count the frames in numFrames and sum their + // header.frameLength() to totalFrameSize, and finally calculate + // d->length = 1000LL * numFrames * firstHeader.samplesPerFrame() / firstHeader.sampleRate(); + // d->bitrate = d->length > 0 ? totalFrameSize * 8 / d->length : 0; + // + // With Fast read style, we do not try to estimate the length and just set + // it and the bitrate to zero. + // With Average read style, in order to come faster to an estimate which + // is accurate enough, we stop when the average bytes/frame rate is stable + // for 10 frames and then calculate the length from the estimated bitrate + // and the stream length. + if(readStyle == Fast) { + bitrate = 0; + d->length = 0; + } + else { + Header header(firstHeader); + unsigned long long totalFrameSize = header.frameLength(); + unsigned long long bytesPerFrame = 0; + unsigned long long lastBytesPerFrame = 0; + offset_t offset = firstFrameOffset; + offset_t nextOffset; + int numFrames = 1; + int sameBytesPerFrameCount = 0; + while((nextOffset = file->nextFrameOffset(offset + header.frameLength())) > offset) { + offset = nextOffset; + header = Header(file, offset, false); + totalFrameSize += header.frameLength(); + ++numFrames; + bytesPerFrame = totalFrameSize / numFrames; + if(readStyle != Accurate) { + if(bytesPerFrame == lastBytesPerFrame) { + if(++sameBytesPerFrameCount >= 10) { + break; + } + } + else { + sameBytesPerFrameCount = 0; + } + lastBytesPerFrame = bytesPerFrame; + } + } + bitrate = firstHeader.samplesPerFrame() != 0 + ? static_cast((bytesPerFrame * 8 * firstHeader.sampleRate()) + / 1000 / firstHeader.samplesPerFrame()) + : 0; + } } - else - { - const Header lastHeader(file, lastFrameOffset, false); - const offset_t streamLength = lastFrameOffset - firstFrameOffset + lastHeader.frameLength(); - if (streamLength > 0) - d->length = static_cast(streamLength * 8.0 / d->bitrate + 0.5); + else if(firstHeader.bitrate() > 0) { + // Since there was no valid VBR header found, we hope that we're in a constant + // bitrate file. + + // TODO: Make this more robust with audio property detection for VBR without a + // Xing header. + bitrate = firstHeader.bitrate(); + } + if(bitrate > 0) { + d->bitrate = bitrate; + + // Look for the last MPEG audio frame to calculate the stream length. + + const offset_t lastFrameOffset = file->lastFrameOffset(); + if(lastFrameOffset < 0) { + debug("MPEG::Properties::read() -- Could not find an MPEG frame in the stream."); + } + else + { + const Header lastHeader(file, lastFrameOffset, false); + const offset_t streamLength = lastFrameOffset - firstFrameOffset + lastHeader.frameLength(); + if (streamLength > 0) + d->length = static_cast(streamLength * 8.0 / d->bitrate + 0.5); + } } } d->sampleRate = firstHeader.sampleRate(); - d->channels = firstHeader.channelMode() == Header::SingleChannel ? 1 : 2; + d->channelConfiguration = firstHeader.channelConfiguration(); + switch(d->channelConfiguration) { + case Header::FrontCenter: + d->channels = 1; + break; + case Header::FrontLeftRight: + d->channels = 2; + break; + case Header::FrontCenterLeftRight: + d->channels = 3; + break; + case Header::FrontCenterLeftRightBackCenter: + d->channels = 4; + break; + case Header::FrontCenterLeftRightBackLeftRight: + d->channels = 5; + break; + case Header::FrontCenterLeftRightBackLeftRightLFE: + d->channels = 6; + break; + case Header::FrontCenterLeftRightSideLeftRightBackLeftRightLFE: + d->channels = 8; + break; + case Header::Custom: + default: + d->channels = firstHeader.channelMode() == Header::SingleChannel ? 1 : 2; + } d->version = firstHeader.version(); d->layer = firstHeader.layer(); d->protectionEnabled = firstHeader.protectionEnabled(); diff --git a/taglib/mpeg/mpegproperties.h b/taglib/mpeg/mpegproperties.h index d22df33b..a3812cc0 100644 --- a/taglib/mpeg/mpegproperties.h +++ b/taglib/mpeg/mpegproperties.h @@ -109,6 +109,16 @@ namespace TagLib { */ Header::ChannelMode channelMode() const; + /*! + * Returns the MPEG-4 channel configuration. + */ + Header::ChannelConfiguration channelConfiguration() const; + + /*! + * Returns true for an Audio Data Transport Stream (ADTS), usually AAC. + */ + bool isADTS() const; + /*! * Returns true if the copyrighted bit is set. */ @@ -120,7 +130,7 @@ namespace TagLib { bool isOriginal() const; private: - void read(File *file); + void read(File *file, ReadStyle readStyle); class PropertiesPrivate; std::unique_ptr d; diff --git a/tests/data/empty1s.aac b/tests/data/empty1s.aac new file mode 100644 index 00000000..53d74776 Binary files /dev/null and b/tests/data/empty1s.aac differ diff --git a/tests/test_mpeg.cpp b/tests/test_mpeg.cpp index 530b4428..54d833f8 100644 --- a/tests/test_mpeg.cpp +++ b/tests/test_mpeg.cpp @@ -25,6 +25,7 @@ #include #include +#include #include "tstring.h" #include "tpropertymap.h" @@ -49,6 +50,7 @@ class TestMPEG : public CppUnit::TestFixture CPPUNIT_TEST(testAudioPropertiesXingHeaderVBR); CPPUNIT_TEST(testAudioPropertiesVBRIHeader); CPPUNIT_TEST(testAudioPropertiesNoVBRHeaders); + CPPUNIT_TEST(testAudioPropertiesADTS); CPPUNIT_TEST(testSkipInvalidFrames1); CPPUNIT_TEST(testSkipInvalidFrames2); CPPUNIT_TEST(testSkipInvalidFrames3); @@ -83,6 +85,7 @@ public: CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(MPEG::XingHeader::Xing, f.audioProperties()->xingHeader()->type()); + CPPUNIT_ASSERT(!f.audioProperties()->isADTS()); } void testAudioPropertiesXingHeaderVBR() @@ -95,6 +98,7 @@ public: CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(MPEG::XingHeader::Xing, f.audioProperties()->xingHeader()->type()); + CPPUNIT_ASSERT(!f.audioProperties()->isADTS()); } void testAudioPropertiesVBRIHeader() @@ -107,6 +111,7 @@ public: CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(MPEG::XingHeader::VBRI, f.audioProperties()->xingHeader()->type()); + CPPUNIT_ASSERT(!f.audioProperties()->isADTS()); } void testAudioPropertiesNoVBRHeaders() @@ -119,6 +124,7 @@ public: CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT(!f.audioProperties()->xingHeader()); + CPPUNIT_ASSERT(!f.audioProperties()->isADTS()); const offset_t last = f.lastFrameOffset(); const MPEG::Header lastHeader(&f, last, false); @@ -127,6 +133,37 @@ public: CPPUNIT_ASSERT_EQUAL(209, lastHeader.frameLength()); } + void testAudioPropertiesADTS() + { + const std::array readStyles = { + MPEG::Properties::Fast, + MPEG::Properties::Average, + MPEG::Properties::Accurate + }; + for(auto readStyle : readStyles) { + MPEG::File f(TEST_FILE_PATH_C("empty1s.aac"), true, readStyle); + CPPUNIT_ASSERT(f.audioProperties()); + CPPUNIT_ASSERT_EQUAL(readStyle == MPEG::Properties::Fast ? 0 : 1, + f.audioProperties()->lengthInSeconds()); + CPPUNIT_ASSERT_EQUAL(readStyle == MPEG::Properties::Fast ? 0 : 1176, + f.audioProperties()->lengthInMilliseconds()); + CPPUNIT_ASSERT_EQUAL(readStyle == MPEG::Properties::Fast ? 0 : 1, + f.audioProperties()->bitrate()); + CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->channels()); + CPPUNIT_ASSERT_EQUAL(MPEG::Header::FrontCenter, + f.audioProperties()->channelConfiguration()); + CPPUNIT_ASSERT_EQUAL(11025, f.audioProperties()->sampleRate()); + CPPUNIT_ASSERT(!f.audioProperties()->xingHeader()); + CPPUNIT_ASSERT(f.audioProperties()->isADTS()); + + const offset_t last = f.lastFrameOffset(); + const MPEG::Header lastHeader(&f, last, false); + + CPPUNIT_ASSERT_EQUAL(static_cast(136), last); + CPPUNIT_ASSERT_EQUAL(11, lastHeader.frameLength()); + } + } + void testSkipInvalidFrames1() { MPEG::File f(TEST_FILE_PATH_C("invalid-frames1.mp3")); @@ -137,6 +174,7 @@ public: CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT(!f.audioProperties()->xingHeader()); + CPPUNIT_ASSERT(!f.audioProperties()->isADTS()); } void testSkipInvalidFrames2() @@ -149,6 +187,7 @@ public: CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT(!f.audioProperties()->xingHeader()); + CPPUNIT_ASSERT(!f.audioProperties()->isADTS()); } void testSkipInvalidFrames3() @@ -161,6 +200,7 @@ public: CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT(!f.audioProperties()->xingHeader()); + CPPUNIT_ASSERT(!f.audioProperties()->isADTS()); } void testVersion2DurationWithXingHeader()