Support encrypted E-AC3 in TS
Implemented E-AC3 MPEG-2 Stream Encryption Format for HTTP Live Streaming specified in https://goo.gl/1sgcwY Issue #279 Change-Id: I36c1a05e3d0529ff810eaf52bdca45414baa93eb
This commit is contained in:
parent
09cdb2bbec
commit
61e36d7d21
|
@ -280,10 +280,20 @@ Status EncryptionHandler::ProcessMediaSample(
|
|||
}
|
||||
DCHECK_EQ(decrypt_config->GetTotalSizeOfSubsamples(),
|
||||
clear_sample->data_size());
|
||||
} else if (codec_ == kCodecEAC3 &&
|
||||
protection_scheme_ == kAppleSampleAesProtectionScheme) {
|
||||
if (!SampleAesEncryptEac3Frame(clear_sample->data(),
|
||||
clear_sample->data_size(),
|
||||
&cipher_sample_data.get()[0])) {
|
||||
return Status(error::ENCRYPTION_FAILURE,
|
||||
"Failed to encrypt E-AC3 frame.");
|
||||
}
|
||||
} else {
|
||||
memcpy(&cipher_sample_data.get()[0], clear_sample->data(),
|
||||
std::min(clear_sample->data_size(), leading_clear_bytes_size_));
|
||||
if (clear_sample->data_size() > leading_clear_bytes_size_) {
|
||||
// The residual block is left unecrypted (copied without encryption). No
|
||||
// need to do special handling here.
|
||||
EncryptBytes(clear_sample->data() + leading_clear_bytes_size_,
|
||||
clear_sample->data_size() - leading_clear_bytes_size_,
|
||||
&cipher_sample_data.get()[leading_clear_bytes_size_]);
|
||||
|
@ -320,6 +330,7 @@ Status EncryptionHandler::SetupProtectionPattern(StreamType stream_type) {
|
|||
case kCodecAAC:
|
||||
FALLTHROUGH_INTENDED;
|
||||
case kCodecAC3:
|
||||
case kCodecEAC3:
|
||||
// Audio is whole sample encrypted. We could not use a
|
||||
// crypto_byte_block_ of 1 here as if there is one crypto block
|
||||
// remaining, it need not be encrypted for video but it needs to be
|
||||
|
@ -327,11 +338,12 @@ Status EncryptionHandler::SetupProtectionPattern(StreamType stream_type) {
|
|||
crypt_byte_block_ = 0u;
|
||||
skip_byte_block_ = 0u;
|
||||
leading_clear_bytes_size_ = kAudioLeadingClearBytesSize;
|
||||
min_protected_data_size_ = leading_clear_bytes_size_ + 1u;
|
||||
min_protected_data_size_ = leading_clear_bytes_size_ + 15u;
|
||||
break;
|
||||
default:
|
||||
return Status(error::ENCRYPTION_FAILURE,
|
||||
"Only AAC/AC3 and H264 are supported in Sample AES.");
|
||||
return Status(
|
||||
error::ENCRYPTION_FAILURE,
|
||||
"Only AAC/AC3/EAC3 and H264 are supported in Sample AES.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -388,8 +400,17 @@ bool EncryptionHandler::CreateEncryptor(const EncryptionKey& encryption_key) {
|
|||
break;
|
||||
case kAppleSampleAesProtectionScheme:
|
||||
if (crypt_byte_block_ == 0 && skip_byte_block_ == 0) {
|
||||
encryptor.reset(
|
||||
new AesCbcEncryptor(kNoPadding, AesCryptor::kUseConstantIv));
|
||||
auto constant_iv_flag = AesCryptor::kUseConstantIv;
|
||||
if (codec_ == kCodecEAC3) {
|
||||
// MPEG-2 Stream Encryption Format for HTTP Live Streaming 2.3.1.3
|
||||
// Enhanced AC-3: Within an Enhanced AC-3 audio frame, the AES-128
|
||||
// cipher block chaining (CBC) initialization vector (IV) is not reset
|
||||
// at syncframe boundaries. The IV is reset at the beginning of each
|
||||
// audio frame.
|
||||
// We'll manage the reset of IV outside of AesCryptor.
|
||||
constant_iv_flag = AesCryptor::kDontUseConstantIv;
|
||||
}
|
||||
encryptor.reset(new AesCbcEncryptor(kNoPadding, constant_iv_flag));
|
||||
} else {
|
||||
encryptor.reset(new AesPatternCryptor(
|
||||
crypt_byte_block_, skip_byte_block_,
|
||||
|
@ -547,6 +568,35 @@ bool EncryptionHandler::EncryptNalFrame(const uint8_t* source,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool EncryptionHandler::SampleAesEncryptEac3Frame(const uint8_t* source,
|
||||
size_t source_size,
|
||||
uint8_t* dest) {
|
||||
DCHECK(source);
|
||||
DCHECK(dest);
|
||||
|
||||
std::vector<size_t> syncframe_sizes;
|
||||
if (!ExtractEac3SyncframeSizes(source, source_size, &syncframe_sizes))
|
||||
return false;
|
||||
|
||||
// MPEG-2 Stream Encryption Format for HTTP Live Streaming 2.3.1.3 Enhanced
|
||||
// AC-3: The IV is reset at the beginning of each audio frame.
|
||||
encryptor_->SetIv(encryptor_->iv());
|
||||
|
||||
for (size_t syncframe_size : syncframe_sizes) {
|
||||
memcpy(dest, source, std::min(syncframe_size, leading_clear_bytes_size_));
|
||||
if (syncframe_size > leading_clear_bytes_size_) {
|
||||
// The residual block is left unecrypted (copied without encryption). No
|
||||
// need to do special handling here.
|
||||
EncryptBytes(source + leading_clear_bytes_size_,
|
||||
syncframe_size - leading_clear_bytes_size_,
|
||||
dest + leading_clear_bytes_size_);
|
||||
}
|
||||
source += syncframe_size;
|
||||
dest += syncframe_size;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void EncryptionHandler::EncryptBytes(const uint8_t* source,
|
||||
size_t source_size,
|
||||
uint8_t* dest) {
|
||||
|
@ -556,6 +606,48 @@ void EncryptionHandler::EncryptBytes(const uint8_t* source,
|
|||
CHECK(encryptor_->Crypt(source, source_size, dest));
|
||||
}
|
||||
|
||||
bool EncryptionHandler::ExtractEac3SyncframeSizes(
|
||||
const uint8_t* source,
|
||||
size_t source_size,
|
||||
std::vector<size_t>* syncframe_sizes) {
|
||||
DCHECK(source);
|
||||
DCHECK(syncframe_sizes);
|
||||
|
||||
syncframe_sizes->clear();
|
||||
BufferReader frame(source, source_size);
|
||||
// ASTC Standard A/52:2012 Annex E: Enhanced AC-3.
|
||||
while (frame.HasBytes(1)) {
|
||||
uint16_t syncword;
|
||||
if (!frame.Read2(&syncword)) {
|
||||
LOG(ERROR) << "Not enough bytes for syncword.";
|
||||
return false;
|
||||
}
|
||||
if (syncword != 0x0B77) {
|
||||
LOG(ERROR) << "Invalid E-AC3 frame. Seeing 0x" << std::hex << syncword
|
||||
<< std::dec
|
||||
<< ". The sync frame does not start with "
|
||||
"the valid syncword 0x0B77.";
|
||||
return false;
|
||||
}
|
||||
uint16_t stream_type_and_syncframe_size;
|
||||
if (!frame.Read2(&stream_type_and_syncframe_size)) {
|
||||
LOG(ERROR) << "Not enough bytes for syncframe size.";
|
||||
return false;
|
||||
}
|
||||
// frmsiz = least significant 11 bits. syncframe_size is (frmsiz + 1) * 2.
|
||||
const size_t syncframe_size =
|
||||
((stream_type_and_syncframe_size & 0x7FF) + 1) * 2;
|
||||
if (!frame.SkipBytes(syncframe_size - sizeof(syncword) -
|
||||
sizeof(stream_type_and_syncframe_size))) {
|
||||
LOG(ERROR) << "Not enough bytes for syncframe. Expecting "
|
||||
<< syncframe_size << " bytes.";
|
||||
return false;
|
||||
}
|
||||
syncframe_sizes->push_back(syncframe_size);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void EncryptionHandler::InjectVpxParserForTesting(
|
||||
std::unique_ptr<VPxParser> vpx_parser) {
|
||||
vpx_parser_ = std::move(vpx_parser);
|
||||
|
|
|
@ -60,10 +60,22 @@ class EncryptionHandler : public MediaHandler {
|
|||
size_t source_size,
|
||||
uint8_t* dest,
|
||||
DecryptConfig* decrypt_config);
|
||||
// Encrypt an E-AC3 frame with size |source_size| according to SAMPLE-AES
|
||||
// specification. |dest| should have at least |source_size| bytes.
|
||||
bool SampleAesEncryptEac3Frame(const uint8_t* source,
|
||||
size_t source_size,
|
||||
uint8_t* dest);
|
||||
// Encrypt an array with size |source_size|. |dest| should have at
|
||||
// least |source_size| bytes.
|
||||
void EncryptBytes(const uint8_t* source, size_t source_size, uint8_t* dest);
|
||||
|
||||
// An E-AC3 frame comprises of one or more syncframes. This function extracts
|
||||
// the syncframe sizes from the source bytes.
|
||||
// Returns false if the frame is not well formed.
|
||||
bool ExtractEac3SyncframeSizes(const uint8_t* source,
|
||||
size_t source_size,
|
||||
std::vector<size_t>* syncframe_sizes);
|
||||
|
||||
// Testing injections.
|
||||
void InjectVpxParserForTesting(std::unique_ptr<VPxParser> vpx_parser);
|
||||
void InjectVideoSliceHeaderParserForTesting(
|
||||
|
@ -82,7 +94,9 @@ class EncryptionHandler : public MediaHandler {
|
|||
uint8_t nalu_length_size_ = 0;
|
||||
// For Sample AES, 32 bytes for Video and 16 bytes for audio.
|
||||
size_t leading_clear_bytes_size_ = 0;
|
||||
// For Sample AES, 48+1 bytes for video NAL and 16+1 bytes for audio.
|
||||
// For Sample AES, if the data size is less than this value, none of the bytes
|
||||
// are encrypted. The size is 48+1 bytes for video NAL and 16+15 bytes for
|
||||
// audio according to MPEG-2 Stream Encryption Format for HTTP Live Streaming.
|
||||
size_t min_protected_data_size_ = 0;
|
||||
// Remaining clear lead in the stream's time scale.
|
||||
int64_t remaining_clear_lead_ = 0;
|
||||
|
|
|
@ -57,9 +57,10 @@ bool PesPacketGenerator::Initialize(const StreamInfo& stream_info) {
|
|||
audio_stream_id_ = kAacAudioStreamId;
|
||||
adts_converter_.reset(new AACAudioSpecificConfig());
|
||||
return adts_converter_->Parse(audio_stream_info.codec_config());
|
||||
} else if (audio_stream_info.codec() == Codec::kCodecAC3) {
|
||||
} else if (audio_stream_info.codec() == Codec::kCodecAC3 ||
|
||||
audio_stream_info.codec() == Codec::kCodecEAC3) {
|
||||
audio_stream_id_ = kAc3AudioStreamId;
|
||||
// No converter needed for AC3.
|
||||
// No converter needed for AC3 and E-AC3.
|
||||
return true;
|
||||
}
|
||||
NOTIMPLEMENTED() << "Audio codec " << audio_stream_info.codec()
|
||||
|
|
Loading…
Reference in New Issue