Bug 1306477 - Adds support for subsample encrypted WebMs r?cpearce
MozReview-Commit-ID: HzQKShlmcON
--- a/dom/media/webm/WebMDemuxer.cpp
+++ b/dom/media/webm/WebMDemuxer.cpp
@@ -24,16 +24,17 @@
#include "mozilla/Sprintf.h"
#include <algorithm>
#include <stdint.h>
#define VPX_DONT_DEFINE_STDINT_TYPES
#include "vpx/vp8dx.h"
#include "vpx/vpx_decoder.h"
+#include <numeric>
#define WEBM_DEBUG(arg, ...) MOZ_LOG(gMediaDemuxerLog, mozilla::LogLevel::Debug, ("WebMDemuxer(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
extern mozilla::LazyLogModule gMediaDemuxerLog;
namespace mozilla {
using namespace gfx;
@@ -730,17 +731,18 @@ WebMDemuxer::GetNextPacket(TrackInfo::Tr
media::TimeUnit::FromNanoseconds(discardPadding), mInfo.mAudio.mRate);
}
if (discardFrames.isValid()) {
sample->mDiscardPadding = discardFrames.value();
}
}
if (packetEncryption == NESTEGG_PACKET_HAS_SIGNAL_BYTE_UNENCRYPTED
- || packetEncryption == NESTEGG_PACKET_HAS_SIGNAL_BYTE_ENCRYPTED) {
+ || packetEncryption == NESTEGG_PACKET_HAS_SIGNAL_BYTE_ENCRYPTED
+ || packetEncryption == NESTEGG_PACKET_HAS_SIGNAL_BYTE_PARTITIONED) {
nsAutoPtr<MediaRawDataWriter> writer(sample->CreateWriter());
unsigned char const* iv;
size_t ivLength;
nestegg_packet_iv(holder->Packet(), &iv, &ivLength);
writer->mCrypto.mValid = true;
writer->mCrypto.mIVSize = ivLength;
if (ivLength == 0) {
// Frame is not encrypted
@@ -749,18 +751,82 @@ WebMDemuxer::GetNextPacket(TrackInfo::Tr
} else {
// Frame is encrypted
writer->mCrypto.mIV.AppendElements(iv, 8);
// Iv from a sample is 64 bits, must be padded with 64 bits more 0s
// in compliance with spec
for (uint32_t i = 0; i < 8; i++) {
writer->mCrypto.mIV.AppendElement(0);
}
- writer->mCrypto.mPlainSizes.AppendElement(0);
- writer->mCrypto.mEncryptedSizes.AppendElement(length);
+
+ if (packetEncryption == NESTEGG_PACKET_HAS_SIGNAL_BYTE_ENCRYPTED) {
+ writer->mCrypto.mPlainSizes.AppendElement(0);
+ writer->mCrypto.mEncryptedSizes.AppendElement(length);
+ } else if (packetEncryption == NESTEGG_PACKET_HAS_SIGNAL_BYTE_PARTITIONED) {
+ uint8_t numPartitions = 0;
+ const uint32_t* partitions = NULL;
+ nestegg_packet_offsets(holder->Packet(), &partitions, &numPartitions);
+
+ // WebM stores a list of 'partitions' in the data, which alternate
+ // clear, encrypted. The data in the first partition is always clear.
+ // So, and sample might look as follows:
+ // 00|XXXX|000|XX, where | represents a partition, 0 a clear byte and
+ // X an encrypted byte. If the first bytes in sample are unencrypted,
+ // the first partition will be at zero |XXXX|000|XX.
+ //
+ // As GMP expects the lengths of the clear and encrypted chunks of
+ // data, we calculate these from the difference between the last two
+ // partitions.
+ uint32_t lastOffset = 0;
+ bool encrypted = false;
+
+ for (uint8_t i = 0; i < numPartitions; i++) {
+ uint32_t partition = partitions[i];
+ uint32_t currentLength = partition - lastOffset;
+
+ if (encrypted) {
+ writer->mCrypto.mEncryptedSizes.AppendElement(currentLength);
+ } else {
+ writer->mCrypto.mPlainSizes.AppendElement(currentLength);
+ }
+
+ encrypted = !encrypted;
+ lastOffset = partition;
+
+ assert(lastOffset <= length);
+ }
+
+ // Add the data between the last offset and the end of the data.
+ // 000|XXX|000
+ // ^---^
+ if (encrypted) {
+ writer->mCrypto.mEncryptedSizes.AppendElement(length - lastOffset);
+ } else {
+ writer->mCrypto.mPlainSizes.AppendElement(length - lastOffset);
+ }
+
+ // Make sure we have an equal number of encrypted and plain sizes (GMP
+ // expects this). This simple check is sufficient as there are two
+ // possible cases at this point:
+ // 1. The number of samples are even (so we don't need to do anything)
+ // 2. There is one more clear sample than encrypted samples, so add a
+ // zero length encrypted chunk.
+ // There can never be more encrypted partitions than clear partitions
+ // due to the alternating structure of the WebM samples and the
+ // restriction that the first chunk is always clear.
+ if (numPartitions % 2 == 0) {
+ writer->mCrypto.mEncryptedSizes.AppendElement(0);
+ }
+
+ // Assert that the lengths of the encrypted and plain samples add to
+ // the length of the data.
+ assert(((size_t)(std::accumulate(writer->mCrypto.mPlainSizes.begin(), writer->mCrypto.mPlainSizes.end(), 0) \
+ + std::accumulate(writer->mCrypto.mEncryptedSizes.begin(), writer->mCrypto.mEncryptedSizes.end(), 0)) \
+ == length));
+ }
}
}
if (aType == TrackInfo::kVideoTrack) {
sample->mTrackInfo = mSharedVideoTrackInfo;
}
aSamples->Push(sample);
}
return true;