Commit dcd57350 authored by xhwang@chromium.org's avatar xhwang@chromium.org

Drop DecryptConfig::data_offset_.

This CL drops DecryptConfig::data_offset_ and moves the offset parsing code into WebM demuxers. This allows us to remove offset passing/calculation code in several layers of the media pipeline.

Background:
We used to have HMAC check in encrypted WebM RFC, which requires us to keep the IV together with the encrypted frame, hence the offset. Now the HMAC check is dropped from the RFC so offset is not necessary anymore.

BUG=298569

Review URL: https://codereview.chromium.org/119203003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@243672 0039d316-1c4b-4281-b951-d872f2087c98
parent 3b10e258
......@@ -450,14 +450,8 @@ void MediaSourceDelegate::OnBufferReady(
}
data->access_units[index].timestamp = buffer->timestamp();
{ // No local variable in switch-case scope.
int data_offset = buffer->decrypt_config() ?
buffer->decrypt_config()->data_offset() : 0;
DCHECK_LT(data_offset, buffer->data_size());
data->access_units[index].data = std::vector<uint8>(
buffer->data() + data_offset,
buffer->data() + buffer->data_size() - data_offset);
}
data->access_units[index].data.assign(
buffer->data(), buffer->data() + buffer->data_size());
#if !defined(GOOGLE_TV)
// Vorbis needs 4 extra bytes padding on Android. Check
// NuMediaExtractor.cpp in Android source code.
......
......@@ -106,7 +106,8 @@ static bool MakeEncryptedBlockInfo(
const media::DecryptConfig* decrypt_config =
encrypted_buffer->decrypt_config();
block_info->data_offset = decrypt_config->data_offset();
// TODO(xhwang): Drop |data_offset| in PP_EncryptedBlockInfo.
block_info->data_offset = 0;
if (!CopyStringToArray(decrypt_config->key_id(), block_info->key_id) ||
!CopyStringToArray(decrypt_config->iv(), block_info->iv))
......
......@@ -10,16 +10,13 @@ namespace media {
DecryptConfig::DecryptConfig(const std::string& key_id,
const std::string& iv,
const int data_offset,
const std::vector<SubsampleEntry>& subsamples)
: key_id_(key_id),
iv_(iv),
data_offset_(data_offset),
subsamples_(subsamples) {
CHECK_GT(key_id.size(), 0u);
CHECK(iv.size() == static_cast<size_t>(DecryptConfig::kDecryptionKeySize) ||
iv.empty());
CHECK_GE(data_offset, 0);
}
DecryptConfig::~DecryptConfig() {}
......
......@@ -38,23 +38,16 @@ class MEDIA_EXPORT DecryptConfig {
// |iv| is the initialization vector defined by the encrypted format.
// Currently |iv| must be 16 bytes as defined by WebM and ISO. Or must be
// empty which signals an unencrypted frame.
// |data_offset| is the amount of data that should be discarded from the
// head of the sample buffer before applying subsample information. A
// decrypted buffer will be shorter than an encrypted buffer by this amount.
// |subsamples| defines the clear and encrypted portions of the sample as
// described above. A decrypted buffer will be equal in size to the sum
// of the subsample sizes.
//
// |data_offset| is applied before |subsamples|.
DecryptConfig(const std::string& key_id,
const std::string& iv,
const int data_offset,
const std::vector<SubsampleEntry>& subsamples);
~DecryptConfig();
const std::string& key_id() const { return key_id_; }
const std::string& iv() const { return iv_; }
int data_offset() const { return data_offset_; }
const std::vector<SubsampleEntry>& subsamples() const { return subsamples_; }
private:
......@@ -63,11 +56,6 @@ class MEDIA_EXPORT DecryptConfig {
// Initialization vector.
const std::string iv_;
// TODO(fgalligan): Remove |data_offset_| if there is no plan to use it in
// the future.
// Amount of data to be discarded before applying subsample information.
const int data_offset_;
// Subsample information. May be empty for some formats, meaning entire frame
// (less data ignored by data_offset_) is encrypted.
const std::vector<SubsampleEntry> subsamples_;
......
......@@ -141,11 +141,8 @@ static scoped_refptr<DecoderBuffer> DecryptData(const DecoderBuffer& input,
return NULL;
}
const int data_offset = input.decrypt_config()->data_offset();
const char* sample =
reinterpret_cast<const char*>(input.data() + data_offset);
DCHECK_GT(input.data_size(), data_offset);
size_t sample_size = static_cast<size_t>(input.data_size() - data_offset);
const char* sample = reinterpret_cast<const char*>(input.data());
size_t sample_size = static_cast<size_t>(input.data_size());
DCHECK_GT(sample_size, 0U) << "No sample data to be decrypted.";
if (sample_size == 0)
......@@ -333,9 +330,8 @@ void AesDecryptor::Decrypt(StreamType stream_type,
scoped_refptr<DecoderBuffer> decrypted;
// An empty iv string signals that the frame is unencrypted.
if (encrypted->decrypt_config()->iv().empty()) {
int data_offset = encrypted->decrypt_config()->data_offset();
decrypted = DecoderBuffer::CopyFrom(encrypted->data() + data_offset,
encrypted->data_size() - data_offset);
decrypted = DecoderBuffer::CopyFrom(encrypted->data(),
encrypted->data_size());
} else {
const std::string& key_id = encrypted->decrypt_config()->key_id();
DecryptionKey* key = GetKey(key_id);
......
This diff is collapsed.
......@@ -85,7 +85,7 @@ static scoped_refptr<media::DecoderBuffer> CopyDecoderBufferFrom(
return media::DecoderBuffer::CreateEOSBuffer();
}
// TODO(tomfinegan): Get rid of this copy.
// TODO(xhwang): Get rid of this copy.
scoped_refptr<media::DecoderBuffer> output_buffer =
media::DecoderBuffer::CopyFrom(input_buffer.data, input_buffer.data_size);
......@@ -97,12 +97,12 @@ static scoped_refptr<media::DecoderBuffer> CopyDecoderBufferFrom(
subsamples.push_back(subsample);
}
DCHECK_EQ(input_buffer.data_offset, 0u);
scoped_ptr<media::DecryptConfig> decrypt_config(new media::DecryptConfig(
std::string(reinterpret_cast<const char*>(input_buffer.key_id),
input_buffer.key_id_size),
std::string(reinterpret_cast<const char*>(input_buffer.iv),
input_buffer.iv_size),
input_buffer.data_offset,
subsamples));
output_buffer->set_decrypt_config(decrypt_config.Pass());
......
......@@ -40,7 +40,6 @@ static scoped_refptr<DecoderBuffer> CreateFakeEncryptedBuffer() {
std::string(reinterpret_cast<const char*>(kFakeKeyId),
arraysize(kFakeKeyId)),
std::string(reinterpret_cast<const char*>(kFakeIv), arraysize(kFakeIv)),
0,
std::vector<SubsampleEntry>())));
return buffer;
}
......
......@@ -211,9 +211,8 @@ void DecryptingDemuxerStream::DecryptBuffer(
// An empty iv string signals that the frame is unencrypted.
if (buffer->decrypt_config()->iv().empty()) {
DVLOG(2) << "DoDecryptBuffer() - clear buffer.";
int data_offset = buffer->decrypt_config()->data_offset();
scoped_refptr<DecoderBuffer> decrypted = DecoderBuffer::CopyFrom(
buffer->data() + data_offset, buffer->data_size() - data_offset);
buffer->data(), buffer->data_size());
decrypted->set_timestamp(buffer->timestamp());
decrypted->set_duration(buffer->duration());
state_ = kIdle;
......
......@@ -38,7 +38,7 @@ static scoped_refptr<DecoderBuffer> CreateFakeEncryptedStreamBuffer(
buffer->set_decrypt_config(scoped_ptr<DecryptConfig>(new DecryptConfig(
std::string(reinterpret_cast<const char*>(kFakeKeyId),
arraysize(kFakeKeyId)),
iv, 0, std::vector<SubsampleEntry>())));
iv, std::vector<SubsampleEntry>())));
return buffer;
}
......
......@@ -37,7 +37,6 @@ static scoped_refptr<DecoderBuffer> CreateFakeEncryptedBuffer() {
std::string(reinterpret_cast<const char*>(kFakeKeyId),
arraysize(kFakeKeyId)),
std::string(reinterpret_cast<const char*>(kFakeIv), arraysize(kFakeIv)),
0,
std::vector<SubsampleEntry>())));
return buffer;
}
......
......@@ -151,7 +151,6 @@ void FakeDemuxerStream::DoRead() {
buffer->set_decrypt_config(scoped_ptr<DecryptConfig>(
new DecryptConfig(std::string(kKeyId, kKeyId + arraysize(kKeyId)),
std::string(kIv, kIv + arraysize(kIv)),
0,
std::vector<SubsampleEntry>())));
}
buffer->set_timestamp(current_timestamp_);
......
......@@ -116,6 +116,7 @@ void FFmpegDemuxerStream::EnqueuePacket(ScopedAVPacket packet) {
// keep this generic so that other side_data types in the future can be
// handled the same way as well.
av_packet_split_side_data(packet.get());
scoped_refptr<DecoderBuffer> buffer;
if (type() == DemuxerStream::TEXT) {
......@@ -145,14 +146,30 @@ void FFmpegDemuxerStream::EnqueuePacket(ScopedAVPacket packet) {
AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL,
&side_data_size);
scoped_ptr<DecryptConfig> decrypt_config;
int data_offset = 0;
if ((type() == DemuxerStream::AUDIO && audio_config_.is_encrypted()) ||
(type() == DemuxerStream::VIDEO && video_config_.is_encrypted())) {
if (!WebMCreateDecryptConfig(
packet->data, packet->size,
reinterpret_cast<const uint8*>(encryption_key_id_.data()),
encryption_key_id_.size(),
&decrypt_config,
&data_offset)) {
LOG(ERROR) << "Creation of DecryptConfig failed.";
}
}
// If a packet is returned by FFmpeg's av_parser_parse2() the packet will
// reference inner memory of FFmpeg. As such we should transfer the packet
// into memory we control.
if (side_data_size > 0) {
buffer = DecoderBuffer::CopyFrom(packet.get()->data, packet.get()->size,
buffer = DecoderBuffer::CopyFrom(packet.get()->data + data_offset,
packet.get()->size - data_offset,
side_data, side_data_size);
} else {
buffer = DecoderBuffer::CopyFrom(packet.get()->data, packet.get()->size);
buffer = DecoderBuffer::CopyFrom(packet.get()->data + data_offset,
packet.get()->size - data_offset);
}
int skip_samples_size = 0;
......@@ -171,17 +188,9 @@ void FFmpegDemuxerStream::EnqueuePacket(ScopedAVPacket packet) {
discard_padding_samples * 1000000.0 /
audio_decoder_config().samples_per_second()));
}
}
if ((type() == DemuxerStream::AUDIO && audio_config_.is_encrypted()) ||
(type() == DemuxerStream::VIDEO && video_config_.is_encrypted())) {
scoped_ptr<DecryptConfig> config(WebMCreateDecryptConfig(
packet->data, packet->size,
reinterpret_cast<const uint8*>(encryption_key_id_.data()),
encryption_key_id_.size()));
if (!config)
LOG(ERROR) << "Creation of DecryptConfig failed.";
buffer->set_decrypt_config(config.Pass());
if (decrypt_config)
buffer->set_decrypt_config(decrypt_config.Pass());
}
buffer->set_timestamp(ConvertStreamTimestamp(
......
......@@ -493,7 +493,6 @@ bool MP4StreamParser::EnqueueSample(BufferQueue* audio_buffers,
decrypt_config.reset(new DecryptConfig(
decrypt_config->key_id(),
decrypt_config->iv(),
decrypt_config->data_offset(),
subsamples));
}
// else, use the existing config.
......@@ -502,7 +501,7 @@ bool MP4StreamParser::EnqueueSample(BufferQueue* audio_buffers,
// The media pipeline requires a DecryptConfig with an empty |iv|.
// TODO(ddorwin): Refactor so we do not need a fake key ID ("1");
decrypt_config.reset(
new DecryptConfig("1", "", 0, std::vector<SubsampleEntry>()));
new DecryptConfig("1", "", std::vector<SubsampleEntry>()));
}
scoped_refptr<StreamParserBuffer> stream_buf =
......
......@@ -451,7 +451,6 @@ scoped_ptr<DecryptConfig> TrackRunIterator::GetDecryptConfig() {
std::string(reinterpret_cast<const char*>(&kid[0]), kid.size()),
std::string(reinterpret_cast<const char*>(cenc_info.iv),
arraysize(cenc_info.iv)),
0, // No offset to start of media data in MP4 using CENC.
cenc_info.subsamples));
}
......
......@@ -345,13 +345,30 @@ bool WebMClusterParser::OnBlock(bool is_simple_block, int track_num,
scoped_refptr<StreamParserBuffer> buffer;
if (!is_text) {
buffer = StreamParserBuffer::CopyFrom(data, size,
additional, additional_size,
is_keyframe);
// Every encrypted Block has a signal byte and IV prepended to it. Current
// encrypted WebM request for comments specification is here
// http://wiki.webmproject.org/encryption/webm-encryption-rfc
scoped_ptr<DecryptConfig> decrypt_config;
int data_offset = 0;
if (!encryption_key_id.empty() &&
!WebMCreateDecryptConfig(
data, size,
reinterpret_cast<const uint8*>(encryption_key_id.data()),
encryption_key_id.size(),
&decrypt_config, &data_offset)) {
return false;
}
buffer = StreamParserBuffer::CopyFrom(
data + data_offset, size - data_offset,
additional, additional_size,
is_keyframe);
if (decrypt_config)
buffer->set_decrypt_config(decrypt_config.Pass());
} else {
std::string id, settings, content;
WebMWebVTTParser::Parse(data, size,
&id, &settings, &content);
WebMWebVTTParser::Parse(data, size, &id, &settings, &content);
std::vector<uint8> side_data;
MakeSideData(id.begin(), id.end(),
......@@ -366,19 +383,6 @@ bool WebMClusterParser::OnBlock(bool is_simple_block, int track_num,
is_keyframe);
}
// Every encrypted Block has a signal byte and IV prepended to it. Current
// encrypted WebM request for comments specification is here
// http://wiki.webmproject.org/encryption/webm-encryption-rfc
if (!encryption_key_id.empty()) {
scoped_ptr<DecryptConfig> config(WebMCreateDecryptConfig(
data, size,
reinterpret_cast<const uint8*>(encryption_key_id.data()),
encryption_key_id.size()));
if (!config)
return false;
buffer->set_decrypt_config(config.Pass());
}
buffer->set_timestamp(timestamp);
if (cluster_start_time_ == kNoTimestamp())
cluster_start_time_ = timestamp;
......
......@@ -183,13 +183,11 @@ static bool VerifyTextBuffers(
return true;
}
static bool VerifyEncryptedBuffer(
static void VerifyEncryptedBuffer(
scoped_refptr<StreamParserBuffer> buffer) {
EXPECT_TRUE(buffer->decrypt_config());
EXPECT_EQ(static_cast<unsigned long>(DecryptConfig::kDecryptionKeySize),
buffer->decrypt_config()->iv().length());
const uint8* data = buffer->data();
return data[0] & kWebMFlagEncryptedFrame;
}
static void AppendToEnd(const WebMClusterParser::BufferQueue& src,
......@@ -508,7 +506,7 @@ TEST_F(WebMClusterParserTest, ParseEncryptedBlock) {
EXPECT_EQ(cluster->size(), result);
ASSERT_EQ(1UL, parser_->video_buffers().size());
scoped_refptr<StreamParserBuffer> buffer = parser_->video_buffers()[0];
EXPECT_TRUE(VerifyEncryptedBuffer(buffer));
VerifyEncryptedBuffer(buffer);
}
TEST_F(WebMClusterParserTest, ParseBadEncryptedBlock) {
......
......@@ -24,12 +24,13 @@ std::string GenerateWebMCounterBlock(const uint8* iv, int iv_size) {
} // namespace anonymous
scoped_ptr<DecryptConfig> WebMCreateDecryptConfig(
const uint8* data, int data_size,
const uint8* key_id, int key_id_size) {
bool WebMCreateDecryptConfig(const uint8* data, int data_size,
const uint8* key_id, int key_id_size,
scoped_ptr<DecryptConfig>* decrypt_config,
int* data_offset) {
if (data_size < kWebMSignalByteSize) {
DVLOG(1) << "Got a block from an encrypted stream with no data.";
return scoped_ptr<DecryptConfig>();
return false;
}
uint8 signal_byte = data[0];
......@@ -43,18 +44,19 @@ scoped_ptr<DecryptConfig> WebMCreateDecryptConfig(
if (signal_byte & kWebMFlagEncryptedFrame) {
if (data_size < kWebMSignalByteSize + kWebMIvSize) {
DVLOG(1) << "Got an encrypted block with not enough data " << data_size;
return scoped_ptr<DecryptConfig>();
return false;
}
counter_block = GenerateWebMCounterBlock(data + frame_offset, kWebMIvSize);
frame_offset += kWebMIvSize;
}
scoped_ptr<DecryptConfig> config(new DecryptConfig(
decrypt_config->reset(new DecryptConfig(
std::string(reinterpret_cast<const char*>(key_id), key_id_size),
counter_block,
frame_offset,
std::vector<SubsampleEntry>()));
return config.Pass();
*data_offset = frame_offset;
return true;
}
} // namespace media
......@@ -16,16 +16,17 @@ namespace media {
// information.
const char kWebMEncryptInitDataType[] = "video/webm";
// Returns an initialized DecryptConfig, which can be sent to the Decryptor if
// the stream has potentially encrypted frames. Every encrypted Block has a
// signal byte, and if the frame is encrypted, an initialization vector
// prepended to the frame. Leaving the IV empty will tell the decryptor that the
// frame is unencrypted. Returns NULL if |data| is invalid. Current encrypted
// WebM request for comments specification is here
// Fills an initialized DecryptConfig, which can be sent to the Decryptor if
// the stream has potentially encrypted frames. Also sets |data_offset| which
// indicates where the encrypted data starts. Leaving the IV empty will tell
// the decryptor that the frame is unencrypted. Returns true if |data| is valid,
// false otherwise, in which case |decrypt_config| and |data_offset| will not be
// changed. Current encrypted WebM request for comments specification is here
// http://wiki.webmproject.org/encryption/webm-encryption-rfc
scoped_ptr<DecryptConfig> WebMCreateDecryptConfig(
const uint8* data, int data_size,
const uint8* key_id, int key_id_size);
bool WebMCreateDecryptConfig(const uint8* data, int data_size,
const uint8* key_id, int key_id_size,
scoped_ptr<DecryptConfig>* decrypt_config,
int* data_offset);
} // namespace media
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment