Bug 1022434 - Extract crypto information from MP4 demuxer; r=cpearce
☠☠ backed out by a9d2ca71f904 ☠ ☠
authorAnthony Jones <ajones@mozilla.com>
Fri, 18 Jul 2014 14:37:43 +1200
changeset 216747 daa82439f77ff7bac1b577e98b7773ce04bcb3c4
parent 216746 89b195298ca341b4797682c4a3659ab60dc844da
child 216748 8912d2088eee28737b418afd8cd1e246fc97c6e7
push id515
push userraliiev@mozilla.com
push dateMon, 06 Oct 2014 12:51:51 +0000
treeherdermozilla-release@267c7a481bef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce
bugs1022434
milestone33.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1022434 - Extract crypto information from MP4 demuxer; r=cpearce * * * Bug 1022434 - "[EME] Expose decryption data in MP4Samples" []
media/libstagefright/binding/Adts.cpp
media/libstagefright/binding/DecoderData.cpp
media/libstagefright/binding/include/mp4_demuxer/ByteReader.h
media/libstagefright/binding/include/mp4_demuxer/DecoderData.h
media/libstagefright/binding/include/mp4_demuxer/mp4_demuxer.h
media/libstagefright/binding/mp4_demuxer.cpp
media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
--- a/media/libstagefright/binding/Adts.cpp
+++ b/media/libstagefright/binding/Adts.cpp
@@ -51,12 +51,20 @@ Adts::ConvertEsdsToAdts(uint16_t aChanne
   header[2] =
     ((aProfile - 1) << 6) + (aFrequencyIndex << 2) + (aChannelCount >> 2);
   header[3] = ((aChannelCount & 0x3) << 6) + (newSize >> 11);
   header[4] = (newSize & 0x7ff) >> 3;
   header[5] = ((newSize & 7) << 5) + 0x1f;
   header[6] = 0xfc;
 
   aSample->Prepend(&header[0], ArrayLength(header));
+  if (aSample->crypto.valid) {
+    if (aSample->crypto.plain_sizes.Length() == 0) {
+      aSample->crypto.plain_sizes.AppendElement(kADTSHeaderSize);
+      aSample->crypto.encrypted_sizes.AppendElement(aSample->size - kADTSHeaderSize);
+    } else {
+      aSample->crypto.plain_sizes[0] += kADTSHeaderSize;
+    }
+  }
 
   return true;
 }
 }
--- a/media/libstagefright/binding/DecoderData.cpp
+++ b/media/libstagefright/binding/DecoderData.cpp
@@ -29,42 +29,128 @@ static int64_t
 FindInt64(sp<MetaData>& mMetaData, uint32_t mKey)
 {
   int64_t value;
   if (!mMetaData->findInt64(mKey, &value))
     return 0;
   return value;
 }
 
+template <typename T, size_t N>
+static bool
+FindData(sp<MetaData>& aMetaData, uint32_t aKey, mozilla::Vector<T, N>* aDest)
+{
+  const void* data;
+  size_t size;
+  uint32_t type;
+
+  aDest->clear();
+  // There's no point in checking that the type matches anything because it
+  // isn't set consistently in the MPEG4Extractor.
+  if (!aMetaData->findData(aKey, &type, &data, &size) || size % sizeof(T)) {
+    return false;
+  }
+
+  aDest->append(reinterpret_cast<const T*>(data), size / sizeof(T));
+  return true;
+}
+
+template <typename T>
+static bool
+FindData(sp<MetaData>& aMetaData, uint32_t aKey, nsTArray<T>* aDest)
+{
+  const void* data;
+  size_t size;
+  uint32_t type;
+
+  aDest->Clear();
+  // There's no point in checking that the type matches anything because it
+  // isn't set consistently in the MPEG4Extractor.
+  if (!aMetaData->findData(aKey, &type, &data, &size) || size % sizeof(T)) {
+    return false;
+  }
+
+  aDest->AppendElements(reinterpret_cast<const T*>(data), size / sizeof(T));
+  return true;
+}
+
+bool
+CryptoFile::DoUpdate(sp<MetaData>& aMetaData)
+{
+  const void* data;
+  size_t size;
+  uint32_t type;
+
+  // There's no point in checking that the type matches anything because it
+  // isn't set consistently in the MPEG4Extractor.
+  if (!aMetaData->findData(kKeyPssh, &type, &data, &size)) {
+    return false;
+  }
+
+  ByteReader reader(reinterpret_cast<const uint8_t*>(data), size);
+  while (reader.Remaining()) {
+    PsshInfo psshInfo;
+    if (!reader.ReadArray(psshInfo.uuid, 16)) {
+      return false;
+    }
+
+    if (!reader.CanReadType<uint32_t>()) {
+      return false;
+    }
+    auto length = reader.ReadType<uint32_t>();
+
+    if (!reader.ReadArray(psshInfo.data, length)) {
+      return false;
+    }
+    pssh.AppendElement(psshInfo);
+  }
+  return true;
+}
+
+void
+CryptoTrack::Update(sp<MetaData>& aMetaData)
+{
+  valid = aMetaData->findInt32(kKeyCryptoMode, &mode) &&
+          aMetaData->findInt32(kKeyCryptoDefaultIVSize, &iv_size) &&
+          FindData(aMetaData, kKeyCryptoKey, &key);
+}
+
+void
+CryptoSample::Update(sp<MetaData>& aMetaData)
+{
+  CryptoTrack::Update(aMetaData);
+  valid = valid && FindData(aMetaData, kKeyPlainSizes, &plain_sizes) &&
+          FindData(aMetaData, kKeyEncryptedSizes, &encrypted_sizes) &&
+          FindData(aMetaData, kKeyCryptoIV, &iv);
+}
+
 void
 AudioDecoderConfig::Update(sp<MetaData>& aMetaData, const char* aMimeType)
 {
   // aMimeType points to a string from MediaDefs.cpp so we don't need to copy it
   mime_type = aMimeType;
   duration = FindInt64(aMetaData, kKeyDuration);
   channel_count = FindInt32(aMetaData, kKeyChannelCount);
   bits_per_sample = FindInt32(aMetaData, kKeySampleSize);
   samples_per_second = FindInt32(aMetaData, kKeySampleRate);
   frequency_index = Adts::GetFrequencyIndex(samples_per_second);
   aac_profile = FindInt32(aMetaData, kKeyAACProfile);
 
-  const void* data;
-  size_t size;
-  uint32_t type;
+  if (FindData(aMetaData, kKeyESDS, &extra_data)) {
+    ESDS esds(&extra_data[0], extra_data.length());
 
-  if (aMetaData->findData(kKeyESDS, &type, &data, &size)) {
-    extra_data.clear();
-    extra_data.append(reinterpret_cast<const uint8_t*>(data), size);
-
-    ESDS esds(&extra_data[0], extra_data.length());
+    const void* data;
+    size_t size;
     if (esds.getCodecSpecificInfo(&data, &size) == OK) {
       audio_specific_config.append(reinterpret_cast<const uint8_t*>(data),
                                    size);
     }
   }
+
+  crypto.Update(aMetaData);
 }
 
 bool
 AudioDecoderConfig::IsValid()
 {
   return channel_count > 0 && samples_per_second > 0 && frequency_index > 0 &&
          aac_profile > 0;
 }
@@ -73,28 +159,24 @@ void
 VideoDecoderConfig::Update(sp<MetaData>& aMetaData, const char* aMimeType)
 {
   // aMimeType points to a string from MediaDefs.cpp so we don't need to copy it
   mime_type = aMimeType;
   duration = FindInt64(aMetaData, kKeyDuration);
   display_width = FindInt32(aMetaData, kKeyDisplayWidth);
   display_height = FindInt32(aMetaData, kKeyDisplayHeight);
 
-  const void* data;
-  size_t size;
-  uint32_t type;
-
-  if (aMetaData->findData(kKeyAVCC, &type, &data, &size) && size >= 7) {
-    extra_data.clear();
-    extra_data.append(reinterpret_cast<const uint8_t*>(data), size);
+  if (FindData(aMetaData, kKeyAVCC, &extra_data) && extra_data.length() >= 7) {
     // Set size of the NAL length to 4. The demuxer formats its output with
     // this NAL length size.
     extra_data[4] |= 3;
     annex_b = AnnexB::ConvertExtraDataToAnnexB(extra_data);
   }
+
+  crypto.Update(aMetaData);
 }
 
 bool
 VideoDecoderConfig::IsValid()
 {
   return display_width > 0 && display_height > 0;
 }
 
@@ -121,16 +203,18 @@ MP4Sample::Update()
 {
   sp<MetaData> m = mMediaBuffer->meta_data();
   composition_timestamp = FindInt64(m, kKeyTime);
   duration = FindInt64(m, kKeyDuration);
   byte_offset = FindInt64(m, kKey64BitFileOffset);
   is_sync_point = FindInt32(m, kKeyIsSyncFrame);
   data = reinterpret_cast<uint8_t*>(mMediaBuffer->data());
   size = mMediaBuffer->range_length();
+
+  crypto.Update(m);
 }
 
 void
 MP4Sample::Pad(size_t aPaddingBytes)
 {
   MOZ_ASSERT(data == mMediaBuffer->data());
 
   size_t newSize = size + aPaddingBytes;
--- a/media/libstagefright/binding/include/mp4_demuxer/ByteReader.h
+++ b/media/libstagefright/binding/include/mp4_demuxer/ByteReader.h
@@ -1,24 +1,25 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef BYTE_READER_H_
 #define BYTE_READER_H_
 
 #include "mozilla/Vector.h"
+#include "nsTArray.h"
 
 namespace mp4_demuxer
 {
 
 class ByteReader
 {
 public:
-  ByteReader(mozilla::Vector<uint8_t>& aData)
+  ByteReader(const mozilla::Vector<uint8_t>& aData)
     : mPtr(&aData[0]), mRemaining(aData.length())
   {
   }
   ByteReader(const uint8_t* aData, size_t aSize)
     : mPtr(aData), mRemaining(aSize)
   {
   }
 
@@ -67,15 +68,40 @@ public:
     mRemaining -= aCount;
 
     const uint8_t* result = mPtr;
     mPtr += aCount;
 
     return result;
   }
 
+  template <typename T> bool CanReadType() { return mRemaining >= sizeof(T); }
+
+  template <typename T> T ReadType()
+  {
+    auto ptr = Read(sizeof(T));
+    if (!ptr) {
+      MOZ_ASSERT(false);
+      return 0;
+    }
+    return *reinterpret_cast<const T*>(ptr);
+  }
+
+  template <typename T>
+  bool ReadArray(nsTArray<T>& aDest, size_t aLength)
+  {
+    auto ptr = Read(aLength * sizeof(T));
+    if (!ptr) {
+      return false;
+    }
+
+    aDest.Clear();
+    aDest.AppendElements(reinterpret_cast<const T*>(ptr), aLength);
+    return true;
+  }
+
 private:
   const uint8_t* mPtr;
   size_t mRemaining;
 };
 }
 
 #endif
--- a/media/libstagefright/binding/include/mp4_demuxer/DecoderData.h
+++ b/media/libstagefright/binding/include/mp4_demuxer/DecoderData.h
@@ -2,30 +2,76 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef DECODER_DATA_H_
 #define DECODER_DATA_H_
 
 #include "mozilla/Types.h"
 #include "mozilla/Vector.h"
+#include "nsTArray.h"
 #include "nsAutoPtr.h"
 
 namespace stagefright
 {
 template <typename T> class sp;
 class MetaData;
 class MediaBuffer;
 }
 
 namespace mp4_demuxer
 {
 
 class MP4Demuxer;
 
+struct PsshInfo
+{
+  PsshInfo() {}
+  PsshInfo(const PsshInfo& aOther) : uuid(aOther.uuid), data(aOther.data) {}
+  nsTArray<uint8_t> uuid;
+  nsTArray<uint8_t> data;
+};
+
+class CryptoFile
+{
+public:
+  void Update(stagefright::sp<stagefright::MetaData>& aMetaData)
+  {
+    valid = DoUpdate(aMetaData);
+  }
+
+  bool valid;
+  nsTArray<PsshInfo> pssh;
+
+private:
+  bool DoUpdate(stagefright::sp<stagefright::MetaData>& aMetaData);
+};
+
+class CryptoTrack
+{
+public:
+  CryptoTrack() : valid(false) {}
+  void Update(stagefright::sp<stagefright::MetaData>& aMetaData);
+
+  bool valid;
+  int32_t mode;
+  int32_t iv_size;
+  nsTArray<uint8_t> key;
+};
+
+class CryptoSample : public CryptoTrack
+{
+public:
+  void Update(stagefright::sp<stagefright::MetaData>& aMetaData);
+
+  nsTArray<uint16_t> plain_sizes;
+  nsTArray<uint32_t> encrypted_sizes;
+  nsTArray<uint8_t> iv;
+};
+
 class AudioDecoderConfig
 {
 public:
   AudioDecoderConfig()
     : mime_type(nullptr)
     , duration(0)
     , channel_count(0)
     , bits_per_sample(0)
@@ -37,16 +83,17 @@ public:
   const char* mime_type;
   int64_t duration;
   uint32_t channel_count;
   uint32_t bits_per_sample;
   uint32_t samples_per_second;
   int8_t frequency_index;
   mozilla::Vector<uint8_t> extra_data;
   mozilla::Vector<uint8_t> audio_specific_config;
+  CryptoTrack crypto;
 
   void Update(stagefright::sp<stagefright::MetaData>& aMetaData, const char* aMimeType);
   bool IsValid();
 
 private:
   friend class MP4Demuxer;
   int8_t aac_profile;
 };
@@ -64,16 +111,17 @@ public:
 
   const char* mime_type;
   int64_t duration;
   int32_t display_width;
   int32_t display_height;
 
   mozilla::Vector<uint8_t> extra_data; // Unparsed AVCDecoderConfig payload.
   mozilla::Vector<uint8_t> annex_b;    // Parsed version for sample prepend.
+  CryptoTrack crypto;
 
   void Update(stagefright::sp<stagefright::MetaData>& aMetaData, const char* aMimeType);
   bool IsValid();
 };
 
 typedef int64_t Microseconds;
 
 class MP4Sample
@@ -89,16 +137,18 @@ public:
   Microseconds composition_timestamp;
   Microseconds duration;
   int64_t byte_offset;
   bool is_sync_point;
 
   uint8_t* data;
   size_t size;
 
+  CryptoSample crypto;
+
   void Prepend(const uint8_t* aData, size_t aSize);
 
 private:
   nsAutoPtr<uint8_t> extra_buffer;
 };
 }
 
 #endif
--- a/media/libstagefright/binding/include/mp4_demuxer/mp4_demuxer.h
+++ b/media/libstagefright/binding/include/mp4_demuxer/mp4_demuxer.h
@@ -47,24 +47,26 @@ public:
   void SeekAudio(Microseconds aTime);
   void SeekVideo(Microseconds aTime);
 
   // DemuxAudioSample and DemuxVideoSample functions
   // return nullptr on end of stream or error.
   MP4Sample* DemuxAudioSample();
   MP4Sample* DemuxVideoSample();
 
+  const CryptoFile& Crypto() { return mCrypto; }
   const AudioDecoderConfig& AudioConfig() { return mAudioConfig; }
   const VideoDecoderConfig& VideoConfig() { return mVideoConfig; }
 
   void ConvertByteRangesToTime(
     const nsTArray<mozilla::MediaByteRange>& aByteRanges,
     nsTArray<Interval<Microseconds> >* aIntervals);
 
 private:
   AudioDecoderConfig mAudioConfig;
   VideoDecoderConfig mVideoConfig;
+  CryptoFile mCrypto;
 
   nsAutoPtr<StageFrightPrivate> mPrivate;
 };
 }
 
 #endif
--- a/media/libstagefright/binding/mp4_demuxer.cpp
+++ b/media/libstagefright/binding/mp4_demuxer.cpp
@@ -106,16 +106,18 @@ MP4Demuxer::Init()
     } else if (!mPrivate->mVideo.get() && !strncmp(mimeType, "video/", 6)) {
       mPrivate->mVideo = e->getTrack(i);
       mPrivate->mVideo->start();
       mVideoConfig.Update(metaData, mimeType);
       auto index = mPrivate->mVideo->exportIndex();
       mPrivate->mVideoIndex.Init(index);
     }
   }
+  sp<MetaData> metaData = e->getMetaData();
+  mCrypto.Update(metaData);
 
   return mPrivate->mAudio.get() || mPrivate->mVideo.get();
 }
 
 bool
 MP4Demuxer::HasValidAudio()
 {
   return mPrivate->mAudio.get() && mAudioConfig.IsValid();
--- a/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
@@ -132,22 +132,22 @@ private:
         uint32_t mDefaultSampleFlags;
 
         uint64_t mDataOffset;
     };
     TrackFragmentHeaderInfo mTrackFragmentHeaderInfo;
 
     struct Sample {
         off64_t offset;
-        size_t size;
+        uint32_t size;
         uint32_t duration;
         uint32_t ctsOffset;
         uint8_t iv[16];
-        Vector<size_t> clearsizes;
-        Vector<size_t> encryptedsizes;
+        Vector<uint16_t> clearsizes;
+        Vector<uint32_t> encryptedsizes;
     };
     Vector<Sample> mCurrentSamples;
 
     MPEG4Source(const MPEG4Source &);
     MPEG4Source &operator=(const MPEG4Source &);
 };
 
 // This custom data source wraps an existing one and satisfies requests
@@ -1517,17 +1517,18 @@ status_t MPEG4Extractor::parseChunk(off6
                 // Should be version 0, flags 0.
                 return ERROR_MALFORMED;
             }
 
             mLastTrack->meta->setData(
                     kKeyESDS, kTypeESDS, &buffer[4], chunk_data_size - 4);
 
             if (mPath.size() >= 2
-                    && mPath[mPath.size() - 2] == FOURCC('m', 'p', '4', 'a')) {
+                    && (mPath[mPath.size() - 2] == FOURCC('m', 'p', '4', 'a') ||
+                       (mPath[mPath.size() - 2] == FOURCC('e', 'n', 'c', 'a')))) {
                 // Information from the ESDS must be relied on for proper
                 // setup of sample rate and channel count for MPEG4 Audio.
                 // The generic header appears to only contain generic
                 // information...
 
                 status_t err = updateAudioTrackInfoFromESDS_MPEG4Audio(
                         &buffer[4], chunk_data_size - 4);