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(),
|
DCHECK_EQ(decrypt_config->GetTotalSizeOfSubsamples(),
|
||||||
clear_sample->data_size());
|
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 {
|
} else {
|
||||||
memcpy(&cipher_sample_data.get()[0], clear_sample->data(),
|
memcpy(&cipher_sample_data.get()[0], clear_sample->data(),
|
||||||
std::min(clear_sample->data_size(), leading_clear_bytes_size_));
|
std::min(clear_sample->data_size(), leading_clear_bytes_size_));
|
||||||
if (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_,
|
EncryptBytes(clear_sample->data() + leading_clear_bytes_size_,
|
||||||
clear_sample->data_size() - leading_clear_bytes_size_,
|
clear_sample->data_size() - leading_clear_bytes_size_,
|
||||||
&cipher_sample_data.get()[leading_clear_bytes_size_]);
|
&cipher_sample_data.get()[leading_clear_bytes_size_]);
|
||||||
|
@ -320,6 +330,7 @@ Status EncryptionHandler::SetupProtectionPattern(StreamType stream_type) {
|
||||||
case kCodecAAC:
|
case kCodecAAC:
|
||||||
FALLTHROUGH_INTENDED;
|
FALLTHROUGH_INTENDED;
|
||||||
case kCodecAC3:
|
case kCodecAC3:
|
||||||
|
case kCodecEAC3:
|
||||||
// Audio is whole sample encrypted. We could not use a
|
// Audio is whole sample encrypted. We could not use a
|
||||||
// crypto_byte_block_ of 1 here as if there is one crypto block
|
// 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
|
// 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;
|
crypt_byte_block_ = 0u;
|
||||||
skip_byte_block_ = 0u;
|
skip_byte_block_ = 0u;
|
||||||
leading_clear_bytes_size_ = kAudioLeadingClearBytesSize;
|
leading_clear_bytes_size_ = kAudioLeadingClearBytesSize;
|
||||||
min_protected_data_size_ = leading_clear_bytes_size_ + 1u;
|
min_protected_data_size_ = leading_clear_bytes_size_ + 15u;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return Status(error::ENCRYPTION_FAILURE,
|
return Status(
|
||||||
"Only AAC/AC3 and H264 are supported in Sample AES.");
|
error::ENCRYPTION_FAILURE,
|
||||||
|
"Only AAC/AC3/EAC3 and H264 are supported in Sample AES.");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -388,8 +400,17 @@ bool EncryptionHandler::CreateEncryptor(const EncryptionKey& encryption_key) {
|
||||||
break;
|
break;
|
||||||
case kAppleSampleAesProtectionScheme:
|
case kAppleSampleAesProtectionScheme:
|
||||||
if (crypt_byte_block_ == 0 && skip_byte_block_ == 0) {
|
if (crypt_byte_block_ == 0 && skip_byte_block_ == 0) {
|
||||||
encryptor.reset(
|
auto constant_iv_flag = AesCryptor::kUseConstantIv;
|
||||||
new AesCbcEncryptor(kNoPadding, 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 {
|
} else {
|
||||||
encryptor.reset(new AesPatternCryptor(
|
encryptor.reset(new AesPatternCryptor(
|
||||||
crypt_byte_block_, skip_byte_block_,
|
crypt_byte_block_, skip_byte_block_,
|
||||||
|
@ -547,6 +568,35 @@ bool EncryptionHandler::EncryptNalFrame(const uint8_t* source,
|
||||||
return true;
|
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,
|
void EncryptionHandler::EncryptBytes(const uint8_t* source,
|
||||||
size_t source_size,
|
size_t source_size,
|
||||||
uint8_t* dest) {
|
uint8_t* dest) {
|
||||||
|
@ -556,6 +606,48 @@ void EncryptionHandler::EncryptBytes(const uint8_t* source,
|
||||||
CHECK(encryptor_->Crypt(source, source_size, dest));
|
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(
|
void EncryptionHandler::InjectVpxParserForTesting(
|
||||||
std::unique_ptr<VPxParser> vpx_parser) {
|
std::unique_ptr<VPxParser> vpx_parser) {
|
||||||
vpx_parser_ = std::move(vpx_parser);
|
vpx_parser_ = std::move(vpx_parser);
|
||||||
|
|
|
@ -60,10 +60,22 @@ class EncryptionHandler : public MediaHandler {
|
||||||
size_t source_size,
|
size_t source_size,
|
||||||
uint8_t* dest,
|
uint8_t* dest,
|
||||||
DecryptConfig* decrypt_config);
|
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
|
// Encrypt an array with size |source_size|. |dest| should have at
|
||||||
// least |source_size| bytes.
|
// least |source_size| bytes.
|
||||||
void EncryptBytes(const uint8_t* source, size_t source_size, uint8_t* dest);
|
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.
|
// Testing injections.
|
||||||
void InjectVpxParserForTesting(std::unique_ptr<VPxParser> vpx_parser);
|
void InjectVpxParserForTesting(std::unique_ptr<VPxParser> vpx_parser);
|
||||||
void InjectVideoSliceHeaderParserForTesting(
|
void InjectVideoSliceHeaderParserForTesting(
|
||||||
|
@ -82,7 +94,9 @@ class EncryptionHandler : public MediaHandler {
|
||||||
uint8_t nalu_length_size_ = 0;
|
uint8_t nalu_length_size_ = 0;
|
||||||
// For Sample AES, 32 bytes for Video and 16 bytes for audio.
|
// For Sample AES, 32 bytes for Video and 16 bytes for audio.
|
||||||
size_t leading_clear_bytes_size_ = 0;
|
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;
|
size_t min_protected_data_size_ = 0;
|
||||||
// Remaining clear lead in the stream's time scale.
|
// Remaining clear lead in the stream's time scale.
|
||||||
int64_t remaining_clear_lead_ = 0;
|
int64_t remaining_clear_lead_ = 0;
|
||||||
|
|
|
@ -57,9 +57,10 @@ bool PesPacketGenerator::Initialize(const StreamInfo& stream_info) {
|
||||||
audio_stream_id_ = kAacAudioStreamId;
|
audio_stream_id_ = kAacAudioStreamId;
|
||||||
adts_converter_.reset(new AACAudioSpecificConfig());
|
adts_converter_.reset(new AACAudioSpecificConfig());
|
||||||
return adts_converter_->Parse(audio_stream_info.codec_config());
|
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;
|
audio_stream_id_ = kAc3AudioStreamId;
|
||||||
// No converter needed for AC3.
|
// No converter needed for AC3 and E-AC3.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
NOTIMPLEMENTED() << "Audio codec " << audio_stream_info.codec()
|
NOTIMPLEMENTED() << "Audio codec " << audio_stream_info.codec()
|
||||||
|
|
Loading…
Reference in New Issue