// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "media/formats/webm/webm_content_encodings_client.h" #include "base/logging.h" #include "base/stl_util.h" #include "media/formats/webm/webm_constants.h" namespace media { WebMContentEncodingsClient::WebMContentEncodingsClient( const scoped_refptr& media_log) : media_log_(media_log), content_encryption_encountered_(false), content_encodings_ready_(false) { } WebMContentEncodingsClient::~WebMContentEncodingsClient() { STLDeleteElements(&content_encodings_); } const ContentEncodings& WebMContentEncodingsClient::content_encodings() const { DCHECK(content_encodings_ready_); return content_encodings_; } WebMParserClient* WebMContentEncodingsClient::OnListStart(int id) { if (id == kWebMIdContentEncodings) { DCHECK(!cur_content_encoding_.get()); DCHECK(!content_encryption_encountered_); STLDeleteElements(&content_encodings_); content_encodings_ready_ = false; return this; } if (id == kWebMIdContentEncoding) { DCHECK(!cur_content_encoding_.get()); DCHECK(!content_encryption_encountered_); cur_content_encoding_.reset(new ContentEncoding()); return this; } if (id == kWebMIdContentEncryption) { DCHECK(cur_content_encoding_.get()); if (content_encryption_encountered_) { MEDIA_LOG(ERROR, media_log_) << "Unexpected multiple ContentEncryption."; return NULL; } content_encryption_encountered_ = true; return this; } if (id == kWebMIdContentEncAESSettings) { DCHECK(cur_content_encoding_.get()); return this; } // This should not happen if WebMListParser is working properly. DCHECK(false); return NULL; } // Mandatory occurrence restriction is checked in this function. Multiple // occurrence restriction is checked in OnUInt and OnBinary. bool WebMContentEncodingsClient::OnListEnd(int id) { if (id == kWebMIdContentEncodings) { // ContentEncoding element is mandatory. Check this! if (content_encodings_.empty()) { MEDIA_LOG(ERROR, media_log_) << "Missing ContentEncoding."; return false; } content_encodings_ready_ = true; return true; } if (id == kWebMIdContentEncoding) { DCHECK(cur_content_encoding_.get()); // // Specify default values to missing mandatory elements. // if (cur_content_encoding_->order() == ContentEncoding::kOrderInvalid) { // Default value of encoding order is 0, which should only be used on the // first ContentEncoding. if (!content_encodings_.empty()) { MEDIA_LOG(ERROR, media_log_) << "Missing ContentEncodingOrder."; return false; } cur_content_encoding_->set_order(0); } if (cur_content_encoding_->scope() == ContentEncoding::kScopeInvalid) cur_content_encoding_->set_scope(ContentEncoding::kScopeAllFrameContents); if (cur_content_encoding_->type() == ContentEncoding::kTypeInvalid) cur_content_encoding_->set_type(ContentEncoding::kTypeCompression); // Check for elements valid in spec but not supported for now. if (cur_content_encoding_->type() == ContentEncoding::kTypeCompression) { MEDIA_LOG(ERROR, media_log_) << "ContentCompression not supported."; return false; } // Enforce mandatory elements without default values. DCHECK(cur_content_encoding_->type() == ContentEncoding::kTypeEncryption); if (!content_encryption_encountered_) { MEDIA_LOG(ERROR, media_log_) << "ContentEncodingType is encryption but" << " ContentEncryption is missing."; return false; } content_encodings_.push_back(cur_content_encoding_.release()); content_encryption_encountered_ = false; return true; } if (id == kWebMIdContentEncryption) { DCHECK(cur_content_encoding_.get()); // Specify default value for elements that are not present. if (cur_content_encoding_->encryption_algo() == ContentEncoding::kEncAlgoInvalid) { cur_content_encoding_->set_encryption_algo( ContentEncoding::kEncAlgoNotEncrypted); } return true; } if (id == kWebMIdContentEncAESSettings) { if (cur_content_encoding_->cipher_mode() == ContentEncoding::kCipherModeInvalid) cur_content_encoding_->set_cipher_mode(ContentEncoding::kCipherModeCtr); return true; } // This should not happen if WebMListParser is working properly. DCHECK(false); return false; } // Multiple occurrence restriction and range are checked in this function. // Mandatory occurrence restriction is checked in OnListEnd. bool WebMContentEncodingsClient::OnUInt(int id, int64_t val) { DCHECK(cur_content_encoding_.get()); if (id == kWebMIdContentEncodingOrder) { if (cur_content_encoding_->order() != ContentEncoding::kOrderInvalid) { MEDIA_LOG(ERROR, media_log_) << "Unexpected multiple ContentEncodingOrder."; return false; } if (val != static_cast(content_encodings_.size())) { // According to the spec, encoding order starts with 0 and counts upwards. MEDIA_LOG(ERROR, media_log_) << "Unexpected ContentEncodingOrder."; return false; } cur_content_encoding_->set_order(val); return true; } if (id == kWebMIdContentEncodingScope) { if (cur_content_encoding_->scope() != ContentEncoding::kScopeInvalid) { MEDIA_LOG(ERROR, media_log_) << "Unexpected multiple ContentEncodingScope."; return false; } if (val == ContentEncoding::kScopeInvalid || val > ContentEncoding::kScopeMax) { MEDIA_LOG(ERROR, media_log_) << "Unexpected ContentEncodingScope."; return false; } if (val & ContentEncoding::kScopeNextContentEncodingData) { MEDIA_LOG(ERROR, media_log_) << "Encoded next ContentEncoding is not " "supported."; return false; } cur_content_encoding_->set_scope(static_cast(val)); return true; } if (id == kWebMIdContentEncodingType) { if (cur_content_encoding_->type() != ContentEncoding::kTypeInvalid) { MEDIA_LOG(ERROR, media_log_) << "Unexpected multiple ContentEncodingType."; return false; } if (val == ContentEncoding::kTypeCompression) { MEDIA_LOG(ERROR, media_log_) << "ContentCompression not supported."; return false; } if (val != ContentEncoding::kTypeEncryption) { MEDIA_LOG(ERROR, media_log_) << "Unexpected ContentEncodingType " << val << "."; return false; } cur_content_encoding_->set_type(static_cast(val)); return true; } if (id == kWebMIdContentEncAlgo) { if (cur_content_encoding_->encryption_algo() != ContentEncoding::kEncAlgoInvalid) { MEDIA_LOG(ERROR, media_log_) << "Unexpected multiple ContentEncAlgo."; return false; } if (val < ContentEncoding::kEncAlgoNotEncrypted || val > ContentEncoding::kEncAlgoAes) { MEDIA_LOG(ERROR, media_log_) << "Unexpected ContentEncAlgo " << val << "."; return false; } cur_content_encoding_->set_encryption_algo( static_cast(val)); return true; } if (id == kWebMIdAESSettingsCipherMode) { if (cur_content_encoding_->cipher_mode() != ContentEncoding::kCipherModeInvalid) { MEDIA_LOG(ERROR, media_log_) << "Unexpected multiple AESSettingsCipherMode."; return false; } if (val != ContentEncoding::kCipherModeCtr) { MEDIA_LOG(ERROR, media_log_) << "Unexpected AESSettingsCipherMode " << val << "."; return false; } cur_content_encoding_->set_cipher_mode( static_cast(val)); return true; } // This should not happen if WebMListParser is working properly. DCHECK(false); return false; } // Multiple occurrence restriction is checked in this function. Mandatory // restriction is checked in OnListEnd. bool WebMContentEncodingsClient::OnBinary(int id, const uint8_t* data, int size) { DCHECK(cur_content_encoding_.get()); DCHECK(data); DCHECK_GT(size, 0); if (id == kWebMIdContentEncKeyID) { if (!cur_content_encoding_->encryption_key_id().empty()) { MEDIA_LOG(ERROR, media_log_) << "Unexpected multiple ContentEncKeyID"; return false; } cur_content_encoding_->SetEncryptionKeyId(data, size); return true; } // This should not happen if WebMListParser is working properly. DCHECK(false); return false; } } // namespace media