diff --git a/taglib/mp4/mp4properties.cpp b/taglib/mp4/mp4properties.cpp index 5a41c081..e712f5d4 100644 --- a/taglib/mp4/mp4properties.cpp +++ b/taglib/mp4/mp4properties.cpp @@ -34,7 +34,14 @@ using namespace TagLib; class MP4::Properties::PropertiesPrivate { public: - PropertiesPrivate() : length(0), bitrate(0), sampleRate(0), channels(0), bitsPerSample(0), encrypted(false), codec(MP4::Properties::Unknown) {} + PropertiesPrivate() : + length(0), + bitrate(0), + sampleRate(0), + channels(0), + bitsPerSample(0), + encrypted(false), + codec(MP4::Properties::Unknown) {} int length; int bitrate; @@ -45,11 +52,83 @@ public: Codec codec; }; -MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style) - : AudioProperties(style) -{ - d = new PropertiesPrivate; +//////////////////////////////////////////////////////////////////////////////// +// public members +//////////////////////////////////////////////////////////////////////////////// +MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style) : + AudioProperties(style), + d(new PropertiesPrivate()) +{ + read(file, atoms); +} + +MP4::Properties::~Properties() +{ + delete d; +} + +int +MP4::Properties::channels() const +{ + return d->channels; +} + +int +MP4::Properties::sampleRate() const +{ + return d->sampleRate; +} + +int +MP4::Properties::length() const +{ + return lengthInSeconds(); +} + +int +MP4::Properties::lengthInSeconds() const +{ + return d->length / 1000; +} + +int +MP4::Properties::lengthInMilliseconds() const +{ + return d->length; +} + +int +MP4::Properties::bitrate() const +{ + return d->bitrate; +} + +int +MP4::Properties::bitsPerSample() const +{ + return d->bitsPerSample; +} + +bool +MP4::Properties::isEncrypted() const +{ + return d->encrypted; +} + +MP4::Properties::Codec +MP4::Properties::codec() const +{ + return d->codec; +} + +//////////////////////////////////////////////////////////////////////////////// +// private members +//////////////////////////////////////////////////////////////////////////////// + +void +MP4::Properties::read(File *file, Atoms *atoms) +{ MP4::Atom *moov = atoms->find("moov"); if(!moov) { debug("MP4: Atom 'moov' not found"); @@ -59,9 +138,9 @@ MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style) MP4::Atom *trak = 0; ByteVector data; - MP4::AtomList trakList = moov->findall("trak"); - for (unsigned int i = 0; i < trakList.size(); i++) { - trak = trakList[i]; + const MP4::AtomList trakList = moov->findall("trak"); + for(MP4::AtomList::ConstIterator it = trakList.begin(); it != trakList.end(); ++it) { + trak = *it; MP4::Atom *hdlr = trak->find("mdia", "hdlr"); if(!hdlr) { debug("MP4: Atom 'trak.mdia.hdlr' not found"); @@ -74,7 +153,7 @@ MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style) } trak = 0; } - if (!trak) { + if(!trak) { debug("MP4: No audio tracks"); return; } @@ -87,25 +166,28 @@ MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style) file->seek(mdhd->offset); data = file->readBlock(mdhd->length); - uint version = data[8]; + + const uint version = data[8]; + long long unit; + long long length; if(version == 1) { - if (data.size() < 36 + 8) { + if(data.size() < 36 + 8) { debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected"); return; } - const long long unit = data.toLongLong(28U); - const long long length = data.toLongLong(36U); - d->length = unit ? int(length / unit) : 0; + unit = data.toLongLong(28U); + length = data.toLongLong(36U); } else { - if (data.size() < 24 + 4) { + if(data.size() < 24 + 4) { debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected"); return; } - const unsigned int unit = data.toUInt(20U); - const unsigned int length = data.toUInt(24U); - d->length = unit ? length / unit : 0; + unit = data.toUInt(20U); + length = data.toUInt(24U); } + if(unit > 0 && length > 0) + d->length = static_cast(length * 1000.0 / unit); MP4::Atom *atom = trak->find("mdia", "minf", "stbl", "stsd"); if(!atom) { @@ -135,7 +217,7 @@ MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style) } } } - else if (data.mid(20, 4) == "alac") { + else if(data.mid(20, 4) == "alac") { if (atom->length == 88 && data.mid(56, 4) == "alac") { d->codec = ALAC; d->bitsPerSample = data.at(69); @@ -150,50 +232,3 @@ MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style) d->encrypted = true; } } - -MP4::Properties::~Properties() -{ - delete d; -} - -int -MP4::Properties::channels() const -{ - return d->channels; -} - -int -MP4::Properties::sampleRate() const -{ - return d->sampleRate; -} - -int -MP4::Properties::length() const -{ - return d->length; -} - -int -MP4::Properties::bitrate() const -{ - return d->bitrate; -} - -int -MP4::Properties::bitsPerSample() const -{ - return d->bitsPerSample; -} - -bool -MP4::Properties::isEncrypted() const -{ - return d->encrypted; -} - -MP4::Properties::Codec MP4::Properties::codec() const -{ - return d->codec; -} - diff --git a/taglib/mp4/mp4properties.h b/taglib/mp4/mp4properties.h index 2607c366..410d5348 100644 --- a/taglib/mp4/mp4properties.h +++ b/taglib/mp4/mp4properties.h @@ -49,17 +49,66 @@ namespace TagLib { Properties(File *file, Atoms *atoms, ReadStyle style = Average); virtual ~Properties(); + /*! + * Returns the length of the file in seconds. The length is rounded down to + * the nearest whole second. + * + * \note This method is just an alias of lengthInSeconds(). + * + * \deprecated + */ virtual int length() const; + + /*! + * Returns the length of the file in seconds. The length is rounded down to + * the nearest whole second. + * + * \see lengthInMilliseconds() + */ + // BIC: make virtual + int lengthInSeconds() const; + + /*! + * Returns the length of the file in milliseconds. + * + * \see lengthInSeconds() + */ + // BIC: make virtual + int lengthInMilliseconds() const; + + /*! + * Returns the average bit rate of the file in kb/s. + */ virtual int bitrate() const; + + /*! + * Returns the sample rate in Hz. + */ virtual int sampleRate() const; + + /*! + * Returns the number of audio channels. + */ virtual int channels() const; + + /*! + * Returns the number of bits per audio sample. + */ virtual int bitsPerSample() const; + + /*! + * Returns whether or not the file is encrypted. + */ bool isEncrypted() const; - //! Audio codec used in the MP4 file + /*! + * Returns the codec used in the file. + */ Codec codec() const; private: + void read(File *file, Atoms *atoms); + class PropertiesPrivate; PropertiesPrivate *d; }; diff --git a/tests/test_mp4.cpp b/tests/test_mp4.cpp index dd6e3a03..77ba6aaa 100644 --- a/tests/test_mp4.cpp +++ b/tests/test_mp4.cpp @@ -36,23 +36,31 @@ public: void testPropertiesAAC() { MP4::File f(TEST_FILE_PATH_C("has-tags.m4a")); + CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->length()); + CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); + CPPUNIT_ASSERT_EQUAL(3707, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT_EQUAL(16, ((MP4::Properties *)f.audioProperties())->bitsPerSample()); - CPPUNIT_ASSERT_EQUAL(MP4::Properties::AAC, ((MP4::Properties *)f.audioProperties())->codec()); + CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); + CPPUNIT_ASSERT_EQUAL(false, f.audioProperties()->isEncrypted()); + CPPUNIT_ASSERT_EQUAL(MP4::Properties::AAC, f.audioProperties()->codec()); } void testPropertiesALAC() { MP4::File f(TEST_FILE_PATH_C("empty_alac.m4a")); + CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->length()); + CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); + CPPUNIT_ASSERT_EQUAL(3705, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); - CPPUNIT_ASSERT_EQUAL(16, ((MP4::Properties *)f.audioProperties())->bitsPerSample()); - CPPUNIT_ASSERT_EQUAL(MP4::Properties::ALAC, ((MP4::Properties *)f.audioProperties())->codec()); + CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); + CPPUNIT_ASSERT_EQUAL(false, f.audioProperties()->isEncrypted()); + CPPUNIT_ASSERT_EQUAL(MP4::Properties::ALAC, f.audioProperties()->codec()); } void testCheckValid()