]> granicus.if.org Git - taglib/commitdiff
MP4: AudioProperties improvements
authorTsuda Kageyu <tsuda.kageyu@gmail.com>
Thu, 21 May 2015 03:54:20 +0000 (12:54 +0900)
committerTsuda Kageyu <tsuda.kageyu@gmail.com>
Thu, 18 Jun 2015 08:00:10 +0000 (17:00 +0900)
Add lengthInSeconds(), lengthInMilliseconds() properties. (#503)
Add some tests for audio properties.
Add some supplementary comments.
Move parsing code to read() for consistency with other classes.

taglib/mp4/mp4properties.cpp
taglib/mp4/mp4properties.h
tests/test_mp4.cpp

index 5a41c0814ec8c62aa080f6c3221059cfb6d73be2..e712f5d48d4e0e1d7fbce0e8bb209b5ce416074f 100644 (file)
@@ -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)
+////////////////////////////////////////////////////////////////////////////////
+// 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
 {
-  d = new PropertiesPrivate;
+  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<int>(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;
-}
-
index 2607c366473fef25084b6ee44d11add7e9295a6c..410d534823046a9b27a0c112c8b23b4c680f06eb 100644 (file)
@@ -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;
     };
index dd6e3a038e30f61b2ab0810ef3cf0160d221884e..77ba6aaaf79be4d47146ef1078bac13f7a626701 100644 (file)
@@ -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()