]> granicus.if.org Git - taglib/commitdiff
ASF: Enable ASF::Properties to get the audio codec information.
authorTsuda Kageyu <tsuda.kageyu@gmail.com>
Thu, 21 May 2015 10:05:05 +0000 (19:05 +0900)
committerTsuda Kageyu <tsuda.kageyu@gmail.com>
Sat, 20 Jun 2015 17:42:47 +0000 (02:42 +0900)
taglib/asf/asffile.cpp
taglib/asf/asffile.h
taglib/asf/asfproperties.cpp
taglib/asf/asfproperties.h
tests/data/lossless.wma [new file with mode: 0644]
tests/test_asf.cpp

index e8414ff4760ae86b13ec889fc7dab3224ba6ffd3..179649faac0784c0add0835611dacb166c6e18ac 100644 (file)
@@ -58,17 +58,18 @@ public:
 
 namespace
 {
-  static const ByteVector headerGuid("\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16);
-  static const ByteVector filePropertiesGuid("\xA1\xDC\xAB\x8C\x47\xA9\xCF\x11\x8E\xE4\x00\xC0\x0C\x20\x53\x65", 16);
-  static const ByteVector streamPropertiesGuid("\x91\x07\xDC\xB7\xB7\xA9\xCF\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65", 16);
-  static const ByteVector contentDescriptionGuid("\x33\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16);
-  static const ByteVector extendedContentDescriptionGuid("\x40\xA4\xD0\xD2\x07\xE3\xD2\x11\x97\xF0\x00\xA0\xC9\x5E\xA8\x50", 16);
-  static const ByteVector headerExtensionGuid("\xb5\x03\xbf_.\xa9\xcf\x11\x8e\xe3\x00\xc0\x0c Se", 16);
-  static const ByteVector metadataGuid("\xEA\xCB\xF8\xC5\xAF[wH\204g\xAA\214D\xFAL\xCA", 16);
-  static const ByteVector metadataLibraryGuid("\224\034#D\230\224\321I\241A\x1d\x13NEpT", 16);
-  static const ByteVector contentEncryptionGuid("\xFB\xB3\x11\x22\x23\xBD\xD2\x11\xB4\xB7\x00\xA0\xC9\x55\xFC\x6E", 16);
-  static const ByteVector extendedContentEncryptionGuid("\x14\xE6\x8A\x29\x22\x26 \x17\x4C\xB9\x35\xDA\xE0\x7E\xE9\x28\x9C", 16);
-  static const ByteVector advancedContentEncryptionGuid("\xB6\x9B\x07\x7A\xA4\xDA\x12\x4E\xA5\xCA\x91\xD3\x8D\xC1\x1A\x8D", 16);
+  const ByteVector headerGuid("\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16);
+  const ByteVector filePropertiesGuid("\xA1\xDC\xAB\x8C\x47\xA9\xCF\x11\x8E\xE4\x00\xC0\x0C\x20\x53\x65", 16);
+  const ByteVector streamPropertiesGuid("\x91\x07\xDC\xB7\xB7\xA9\xCF\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65", 16);
+  const ByteVector contentDescriptionGuid("\x33\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16);
+  const ByteVector extendedContentDescriptionGuid("\x40\xA4\xD0\xD2\x07\xE3\xD2\x11\x97\xF0\x00\xA0\xC9\x5E\xA8\x50", 16);
+  const ByteVector headerExtensionGuid("\xb5\x03\xbf_.\xa9\xcf\x11\x8e\xe3\x00\xc0\x0c Se", 16);
+  const ByteVector metadataGuid("\xEA\xCB\xF8\xC5\xAF[wH\204g\xAA\214D\xFAL\xCA", 16);
+  const ByteVector metadataLibraryGuid("\224\034#D\230\224\321I\241A\x1d\x13NEpT", 16);
+  const ByteVector codecListGuid("\x40\x52\xd1\x86\x1d\x31\xd0\x11\xa3\xa4\x00\xa0\xc9\x03\x48\xf6", 16);
+  const ByteVector contentEncryptionGuid("\xFB\xB3\x11\x22\x23\xBD\xD2\x11\xB4\xB7\x00\xA0\xC9\x55\xFC\x6E", 16);
+  const ByteVector extendedContentEncryptionGuid("\x14\xE6\x8A\x29\x22\x26 \x17\x4C\xB9\x35\xDA\xE0\x7E\xE9\x28\x9C", 16);
+  const ByteVector advancedContentEncryptionGuid("\xB6\x9B\x07\x7A\xA4\xDA\x12\x4E\xA5\xCA\x91\xD3\x8D\xC1\x1A\x8D", 16);
 }
 
 class ASF::File::BaseObject
@@ -148,6 +149,13 @@ public:
   ByteVector render(ASF::File *file);
 };
 
+class ASF::File::CodecListObject : public ASF::File::BaseObject
+{
+public:
+  ByteVector guid();
+  void parse(ASF::File *file, uint size);
+};
+
 ASF::File::HeaderExtensionObject::~HeaderExtensionObject()
 {
   for(unsigned int i = 0; i < objects.size(); i++) {
@@ -209,6 +217,7 @@ void ASF::File::StreamPropertiesObject::parse(ASF::File *file, uint size)
     return;
   }
 
+  file->d->properties->setCodec(data.toUShort(54, false));
   file->d->properties->setChannels(data.toUShort(56, false));
   file->d->properties->setSampleRate(data.toUInt(58, false));
   file->d->properties->setBitrate(static_cast<int>(data.toUInt(62, false) * 8.0 / 1000.0 + 0.5));
@@ -377,6 +386,61 @@ ByteVector ASF::File::HeaderExtensionObject::render(ASF::File *file)
   return BaseObject::render(file);
 }
 
+ByteVector ASF::File::CodecListObject::guid()
+{
+  return codecListGuid;
+}
+
+void ASF::File::CodecListObject::parse(ASF::File *file, uint size)
+{
+  BaseObject::parse(file, size);
+  if(data.size() <= 20) {
+    debug("ASF::File::CodecListObject::parse() -- data is too short.");
+    return;
+  }
+
+  uint pos = 16;
+
+  const int count = data.toUInt(pos, false);
+  pos += 4;
+
+  for(int i = 0; i < count; ++i) {
+
+    if(pos >= data.size())
+      break;
+
+    const int type = data.toUShort(pos, false);
+    pos += 2;
+
+    int nameLength = data.toUShort(pos, false);
+    pos += 2;
+
+    const uint namePos = pos;
+    pos += nameLength * 2;
+
+    const int descLength = data.toUShort(pos, false);
+    pos += 2;
+
+    const uint descPos = pos;
+    pos += descLength * 2;
+
+    const int infoLength = data.toUShort(pos, false);
+    pos += 2 + infoLength * 2;
+
+    if(type == 2) {
+      // First audio codec found.
+
+      const String name(data.mid(namePos, nameLength * 2), String::UTF16LE);
+      file->d->properties->setCodecName(name.stripWhiteSpace());
+
+      const String desc(data.mid(descPos, descLength * 2), String::UTF16LE);
+      file->d->properties->setCodecDescription(desc.stripWhiteSpace());
+
+      break;
+    }
+  }
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // public members
 ////////////////////////////////////////////////////////////////////////////////
@@ -491,6 +555,9 @@ void ASF::File::read(bool /*readProperties*/, Properties::ReadStyle /*properties
     else if(guid == headerExtensionGuid) {
       obj = new HeaderExtensionObject();
     }
+    else if(guid == codecListGuid) {
+      obj = new CodecListObject();
+    }
     else {
       if(guid == contentEncryptionGuid ||
          guid == extendedContentEncryptionGuid ||
index 94b2d076f239fb5488847c4ae51b62514327db9a..30d49bc131822db1fec051b1460ddbcad66395da 100644 (file)
@@ -134,6 +134,7 @@ namespace TagLib {
       class ContentDescriptionObject;
       class ExtendedContentDescriptionObject;
       class HeaderExtensionObject;
+      class CodecListObject;
       class MetadataObject;
       class MetadataLibraryObject;
 
index 1b7c6ec19fb43972d8177c5a6064088cf586afe3..a836da30b4576634a0e0b06894ab11f31467a05b 100644 (file)
@@ -38,6 +38,7 @@ public:
     sampleRate(0),
     channels(0),
     bitsPerSample(0),
+    codec(ASF::Properties::Unknown),
     encrypted(false) {}
 
   int length;
@@ -45,6 +46,9 @@ public:
   int sampleRate;
   int channels;
   int bitsPerSample;
+  ASF::Properties::Codec codec;
+  String codecName;
+  String codecDescription;
   bool encrypted;
 };
 
@@ -98,6 +102,21 @@ int ASF::Properties::bitsPerSample() const
   return d->bitsPerSample;
 }
 
+ASF::Properties::Codec ASF::Properties::codec() const
+{
+  return d->codec;
+}
+
+String ASF::Properties::codecName() const
+{
+  return d->codecName;
+}
+
+String ASF::Properties::codecDescription() const
+{
+  return d->codecDescription;
+}
+
 bool ASF::Properties::isEncrypted() const
 {
   return d->encrypted;
@@ -137,6 +156,38 @@ void ASF::Properties::setBitsPerSample(int value)
   d->bitsPerSample = value;
 }
 
+void ASF::Properties::setCodec(int value)
+{
+  switch(value)
+  {
+  case 0x0160:
+    d->codec = WMA1;
+    break;
+  case 0x0161:
+    d->codec = WMA2;
+    break;
+  case 0x0162:
+    d->codec = WMA9Pro;
+    break;
+  case 0x0163:
+    d->codec = WMA9Lossless;
+    break;
+  default:
+    d->codec = Unknown;
+    break;
+  }
+}
+
+void ASF::Properties::setCodecName(const String &value)
+{
+  d->codecName = value;
+}
+
+void ASF::Properties::setCodecDescription(const String &value)
+{
+  d->codecDescription = value;
+}
+
 void ASF::Properties::setEncrypted(bool value)
 {
   d->encrypted = value;
index 5275aa1a3208962792f9e201c1cfabe46e740f1d..b89349b363488b585ac215938f74a3c1184182e7 100644 (file)
@@ -40,7 +40,38 @@ namespace TagLib {
     public:
 
       /*!
-       * Create an instance of ASF::Properties.
+       * Audio codec types can be used in ASF file.
+       */
+      enum Codec
+      {
+        /*!
+         * Couldn't detect the codec.
+         */
+        Unknown = 0,
+
+        /*!
+         * Windows Media Audio 1
+         */
+        WMA1,
+
+        /*!
+         * Windows Media Audio 2 or above
+         */
+        WMA2,
+
+        /*!
+         * Windows Media Audio 9 Professional
+         */
+        WMA9Pro,
+
+        /*!
+         * Windows Media Audio 9 Lossless
+         */
+        WMA9Lossless,
+      };
+
+      /*!
+       * Creates an instance of ASF::Properties.
        */
       Properties();
 
@@ -85,6 +116,7 @@ namespace TagLib {
        * Returns the sample rate in Hz.
        */
       virtual int sampleRate() const;
+
       /*!
        * Returns the number of audio channels.
        */
@@ -95,6 +127,33 @@ namespace TagLib {
        */
       int bitsPerSample() const;
 
+      /*!
+       * Returns the codec used in the file.
+       *
+       * \see codecName()
+       * \see codecDescription()
+       */
+      Codec codec() const;
+
+      /*!
+       * Returns the concrete codec name, for example "Windows Media Audio 9.1"
+       * used in the file if available, otherwise an empty string.
+       *
+       * \see codec()
+       * \see codecDescription()
+       */
+      String codecName() const;
+
+      /*!
+       * Returns the codec description, typically contains the encoder settings,
+       * for example "VBR Quality 50, 44kHz, stereo 1-pass VBR" if available,
+       * otherwise an empty string.
+       *
+       * \see codec()
+       * \see codecName()
+       */
+      String codecDescription() const;
+
       /*!
        * Returns whether or not the file is encrypted.
        */
@@ -109,6 +168,9 @@ namespace TagLib {
       void setSampleRate(int value);
       void setChannels(int value);
       void setBitsPerSample(int value);
+      void setCodec(int value);
+      void setCodecName(const String &value);
+      void setCodecDescription(const String &value);
       void setEncrypted(bool value);
 #endif
 
diff --git a/tests/data/lossless.wma b/tests/data/lossless.wma
new file mode 100644 (file)
index 0000000..e29befc
Binary files /dev/null and b/tests/data/lossless.wma differ
index 82df4d1dba784bb4b440241f02510693c250eac5..663ae5832a39cc2d06a8ccdcfd5632dd15e2c8c3 100644 (file)
@@ -15,6 +15,7 @@ class TestASF : public CppUnit::TestFixture
 {
   CPPUNIT_TEST_SUITE(TestASF);
   CPPUNIT_TEST(testAudioProperties);
+  CPPUNIT_TEST(testLosslessProperties);
   CPPUNIT_TEST(testRead);
   CPPUNIT_TEST(testSaveMultipleValues);
   CPPUNIT_TEST(testSaveStream);
@@ -39,6 +40,26 @@ public:
     CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels());
     CPPUNIT_ASSERT_EQUAL(48000, f.audioProperties()->sampleRate());
     CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample());
+    CPPUNIT_ASSERT_EQUAL(ASF::Properties::WMA2, f.audioProperties()->codec());
+    CPPUNIT_ASSERT_EQUAL(String("Windows Media Audio 9.1"), f.audioProperties()->codecName());
+    CPPUNIT_ASSERT_EQUAL(String("64 kbps, 48 kHz, stereo 2-pass CBR"), f.audioProperties()->codecDescription());
+    CPPUNIT_ASSERT_EQUAL(false, f.audioProperties()->isEncrypted());
+  }
+
+  void testLosslessProperties()
+  {
+    ASF::File f(TEST_FILE_PATH_C("lossless.wma"));
+    CPPUNIT_ASSERT(f.audioProperties());
+    CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->length());
+    CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds());
+    CPPUNIT_ASSERT_EQUAL(3549, f.audioProperties()->lengthInMilliseconds());
+    CPPUNIT_ASSERT_EQUAL(1152, f.audioProperties()->bitrate());
+    CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels());
+    CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate());
+    CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample());
+    CPPUNIT_ASSERT_EQUAL(ASF::Properties::WMA9Lossless, f.audioProperties()->codec());
+    CPPUNIT_ASSERT_EQUAL(String("Windows Media Audio 9.2 Lossless"), f.audioProperties()->codecName());
+    CPPUNIT_ASSERT_EQUAL(String("VBR Quality 100, 44 kHz, 2 channel 16 bit 1-pass VBR"), f.audioProperties()->codecDescription());
     CPPUNIT_ASSERT_EQUAL(false, f.audioProperties()->isEncrypted());
   }