1
0
Fork 0
mirror of https://github.com/ossrs/srs.git synced 2025-02-12 11:21:52 +00:00

hls fmp4: support cbcs subsample encryption.

This commit is contained in:
Jacob Su 2024-09-23 09:32:04 +08:00
parent fd4cf76427
commit 62a544a3fd
4 changed files with 1273 additions and 18 deletions

View file

@ -81,6 +81,7 @@ SrsInitMp4Segment::SrsInitMp4Segment()
{
fw_ = new SrsFileWriter();
init_ = new SrsMp4M2tsInitEncoder();
const_iv_size_ = 0;
}
SrsInitMp4Segment::~SrsInitMp4Segment()
@ -89,6 +90,20 @@ SrsInitMp4Segment::~SrsInitMp4Segment()
srs_freep(fw_);
}
srs_error_t SrsInitMp4Segment::config_cipher(unsigned char* kid, unsigned char* const_iv, uint8_t const_iv_size)
{
srs_error_t err = srs_success;
if (const_iv_size != 8 && const_iv_size != 16) {
return srs_error_new(ERROR_MP4_BOX_STRING, "invalidate const_iv_size");
}
memcpy(kid_, kid, 16);
memcpy(const_iv_, const_iv, const_iv_size);
const_iv_size_ = const_iv_size;
init_->config_encryption(1, 9, kid_, const_iv, const_iv_size);
return err;
}
srs_error_t SrsInitMp4Segment::write(SrsFormat* format, int v_tid, int a_tid)
{
srs_error_t err = srs_success;
@ -153,16 +168,15 @@ srs_error_t SrsInitMp4Segment::init_encoder()
}
SrsHlsM4sSegment::SrsHlsM4sSegment()
SrsHlsM4sSegment::SrsHlsM4sSegment(SrsFileWriter* fw)
{
fw_ = new SrsFileWriter();
fw_ = fw;
enc_ = new SrsFmp4SegmentEncoder();
}
SrsHlsM4sSegment::~SrsHlsM4sSegment()
{
srs_freep(enc_);
srs_freep(fw_);
}
srs_error_t SrsHlsM4sSegment::initialize(int64_t time, uint32_t v_tid, uint32_t a_tid, int sequence_number, std::string m4s_path)
@ -188,6 +202,13 @@ srs_error_t SrsHlsM4sSegment::initialize(int64_t time, uint32_t v_tid, uint32_t
return err;
}
void SrsHlsM4sSegment::config_cipher(unsigned char* key, unsigned char* iv)
{
// TODO: set key and iv to mp4 box
enc_->config_cipher(key, iv);
memcpy(this->iv, iv,16);
}
srs_error_t SrsHlsM4sSegment::write(SrsSharedPtrMessage* shared_msg, SrsFormat* format)
{
srs_error_t err = srs_success;
@ -229,8 +250,9 @@ srs_error_t SrsHlsM4sSegment::reap(uint64_t& dts)
return srs_error_wrap(err, "Flush encoder failed");
}
srs_freep(fw_);
// srs_freep(fw_);
fw_->close();
if ((err = rename()) != srs_success) {
return srs_error_wrap(err, "rename");
}
@ -492,6 +514,10 @@ srs_error_t SrsHlsFmp4Muxer::write_init_mp4(SrsFormat* format, bool has_video, b
init_mp4->set_path(path);
if (hls_keys_) {
init_mp4->config_cipher(kid_, iv_, 16);
}
if (has_video && has_audio) {
if ((err = init_mp4->write(format, video_track_id_, audio_track_id_)) != srs_success) {
return srs_error_wrap(err, "write hls init.mp4 with audio and video");
@ -637,11 +663,7 @@ srs_error_t SrsHlsFmp4Muxer::update_config(SrsRequest* r)
}
}
if(hls_keys_) {
writer_ = new SrsEncFileWriter();
} else {
writer_ = new SrsFileWriter();
}
writer_ = new SrsFileWriter();
return err;
}
@ -656,7 +678,7 @@ srs_error_t SrsHlsFmp4Muxer::segment_open(srs_utime_t basetime)
}
// new segment.
current_ = new SrsHlsM4sSegment();
current_ = new SrsHlsM4sSegment(writer_);
current_->sequence_no = sequence_no_++;
if ((err = write_hls_key()) != srs_success) {
@ -831,7 +853,41 @@ srs_error_t SrsHlsFmp4Muxer::do_segment_close()
srs_error_t SrsHlsFmp4Muxer::write_hls_key()
{
return srs_success;
srs_error_t err = srs_success;
if (hls_keys_ && current_->sequence_no % hls_fragments_per_key_ == 0) {
if (RAND_bytes(key_, 16) < 0) {
return srs_error_wrap(err, "rand key failed.");
}
if (RAND_bytes(kid_, 16) < 0) {
return srs_error_wrap(err, "rand kid failed.");
}
if (RAND_bytes(iv_, 16) < 0) {
return srs_error_wrap(err, "rand iv failed.");
}
string key_file = srs_path_build_stream(hls_key_file_, req_->vhost, req_->app, req_->stream);
key_file = srs_string_replace(key_file, "[seq]", srs_int2str(current_->sequence_no));
string key_url = hls_key_file_path_ + "/" + key_file;
SrsFileWriter fw;
if ((err = fw.open(key_url)) != srs_success) {
return srs_error_wrap(err, "open file %s", key_url.c_str());
}
err = fw.write(key_, 16, NULL);
fw.close();
if (err != srs_success) {
return srs_error_wrap(err, "write key");
}
}
if (hls_keys_) {
current_->config_cipher(key_, iv_);
}
return err;
}
srs_error_t SrsHlsFmp4Muxer::refresh_m3u8()
@ -917,7 +973,7 @@ srs_error_t SrsHlsFmp4Muxer::_refresh_m3u8(std::string m3u8_file)
ss << "#EXT-X-DISCONTINUITY" << SRS_CONSTS_LF;
}
#if 0
#if 1
if(hls_keys_ && ((segment->sequence_no % hls_fragments_per_key_) == 0)) {
char hexiv[33];
srs_data_to_hex(hexiv, segment->iv, 16);
@ -932,7 +988,7 @@ srs_error_t SrsHlsFmp4Muxer::_refresh_m3u8(std::string m3u8_file)
key_path = hls_key_url_ + key_file;
}
ss << "#EXT-X-KEY:METHOD=AES-128,URI=" << "\"" << key_path << "\",IV=0x" << hexiv << SRS_CONSTS_LF;
ss << "#EXT-X-KEY:METHOD=SAMPLE-AES,URI=" << "\"" << key_path << "\",IV=0x" << hexiv << SRS_CONSTS_LF;
}
#endif

View file

@ -59,7 +59,7 @@ public:
SrsHlsSegment(SrsTsContext* c, SrsAudioCodecId ac, SrsVideoCodecId vc, SrsFileWriter* w);
virtual ~SrsHlsSegment();
public:
void config_cipher(unsigned char* key,unsigned char* iv);
void config_cipher(unsigned char* key, unsigned char* iv);
// replace the placeholder
virtual srs_error_t rename();
};
@ -69,13 +69,18 @@ class SrsInitMp4Segment : public SrsFragment
private:
SrsFileWriter* fw_;
SrsMp4M2tsInitEncoder* init_;
unsigned char kid_[16];
unsigned char const_iv_[16];
uint8_t const_iv_size_;
public:
SrsInitMp4Segment();
virtual ~SrsInitMp4Segment();
public:
virtual srs_error_t config_cipher(unsigned char* kid, unsigned char* const_iv, uint8_t const_iv_size);
// Write the init mp4 file, with the v_tid(video track id) and a_tid (audio track id).
virtual srs_error_t write(SrsFormat* format, int v_tid, int a_tid);
@ -83,7 +88,6 @@ public:
virtual srs_error_t write_audio_only(SrsFormat* format, int a_tid);
private:
virtual srs_error_t init_encoder();
};
// TODO: merge this code with SrsFragmentedMp4 in dash
@ -95,11 +99,14 @@ private:
public:
// sequence number in m3u8.
int sequence_no;
// Will be saved in m3u8 file.
unsigned char iv[16];
public:
SrsHlsM4sSegment();
SrsHlsM4sSegment(SrsFileWriter* fw);
virtual ~SrsHlsM4sSegment();
virtual srs_error_t initialize(int64_t time, uint32_t v_tid, uint32_t a_tid, int sequence_number, std::string m4s_path);
virtual void config_cipher(unsigned char* key, unsigned char* iv);
virtual srs_error_t write(SrsSharedPtrMessage* shared_msg, SrsFormat* format);
virtual srs_error_t reap(uint64_t& dts);
};
@ -305,6 +312,7 @@ private:
std::string hls_key_url_;
// The key and iv.
unsigned char key_[16];
unsigned char kid_[16];
unsigned char iv_[16];
// The underlayer file writer.
SrsFileWriter* writer_;

View file

@ -15,6 +15,7 @@
#include <srs_kernel_buffer.hpp>
#include <srs_core_deprecated.hpp>
#include <openssl/aes.h>
#include <string.h>
#include <sstream>
#include <iomanip>
@ -4776,6 +4777,664 @@ stringstream& SrsMp4SegmentIndexBox::dumps_detail(stringstream& ss, SrsMp4DumpCo
return ss;
}
SrsMp4SampleAuxiliaryInfoSizeBox::SrsMp4SampleAuxiliaryInfoSizeBox()
{
type = SrsMp4BoxTypeSAIZ;
}
SrsMp4SampleAuxiliaryInfoSizeBox::~SrsMp4SampleAuxiliaryInfoSizeBox()
{
}
int SrsMp4SampleAuxiliaryInfoSizeBox::nb_header()
{
int size = SrsMp4FullBox::nb_header();
if (flags & 0x01) {
size += 8; // add sizeof(aux_info_type) + sizeof(aux_info_type_parameter);
}
size += 1; // sizeof(default_sample_info_size);
size += 4; // sizeof(sample_count);
if (default_sample_info_size == 0) {
size += sample_info_sizes.size();
}
return size;
}
srs_error_t SrsMp4SampleAuxiliaryInfoSizeBox::encode_header(SrsBuffer* buf)
{
srs_error_t err = srs_success;
if ((err = SrsMp4FullBox::encode_header(buf)) != srs_success) {
return srs_error_wrap(err, "encode header");
}
if (flags & 0x01) {
buf->write_4bytes(aux_info_type);
buf->write_4bytes(aux_info_type_parameter);
}
buf->write_1bytes(default_sample_info_size);
if (default_sample_info_size == 0) {
buf->write_4bytes(sample_info_sizes.size());
vector<uint8_t>::iterator it;
for (it = sample_info_sizes.begin(); it != sample_info_sizes.end(); ++it)
{
buf->write_1bytes(*it);
}
} else {
buf->write_4bytes(sample_count);
}
return err;
}
srs_error_t SrsMp4SampleAuxiliaryInfoSizeBox::decode_header(SrsBuffer* buf)
{
srs_error_t err = srs_success;
if ((err = SrsMp4FullBox::decode_header(buf)) != srs_success) {
return srs_error_wrap(err, "decode header");
}
if (flags & 0x01) {
aux_info_type = buf->read_4bytes();
aux_info_type_parameter = buf->read_4bytes();
}
default_sample_info_size = buf->read_1bytes();
sample_count = buf->read_4bytes();
if (default_sample_info_size == 0) {
for (int i = 0; i < sample_count; i++) {
sample_info_sizes.push_back(buf->read_1bytes());
}
}
return err;
}
std::stringstream& SrsMp4SampleAuxiliaryInfoSizeBox::dumps_detail(std::stringstream& ss, SrsMp4DumpContext dc)
{
ss << "default_sample_info_size=" << default_sample_info_size << ", sample_count=" << sample_count;
return ss;
}
SrsMp4SampleAuxiliaryInfoOffsetBox::SrsMp4SampleAuxiliaryInfoOffsetBox()
{
type = SrsMp4BoxTypeSAIO;
}
SrsMp4SampleAuxiliaryInfoOffsetBox::~SrsMp4SampleAuxiliaryInfoOffsetBox()
{
}
int SrsMp4SampleAuxiliaryInfoOffsetBox::nb_header()
{
int size = SrsMp4FullBox::nb_header();
if (flags & 0x01) {
size += 8; // sizeof(aux_info_type) + sizeof(aux_info_type_parameter);
}
size += 4; // sizeof(entry_count);
if (version == 0) {
size += offsets.size() * 4;
} else {
size += offsets.size() * 8;
}
return size;
}
srs_error_t SrsMp4SampleAuxiliaryInfoOffsetBox::encode_header(SrsBuffer* buf)
{
srs_error_t err = srs_success;
if ((err = SrsMp4FullBox::encode_header(buf)) != srs_success) {
return srs_error_wrap(err, "encode header");
}
if (flags & 0x01) {
buf->write_4bytes(aux_info_type);
buf->write_4bytes(aux_info_type_parameter);
}
buf->write_4bytes(offsets.size());
vector<uint64_t>::iterator it;
for (it = offsets.begin(); it != offsets.end(); ++it)
{
if (version == 0) {
buf->write_4bytes(*it);
} else {
buf->write_8bytes(*it);
}
}
return err;
}
srs_error_t SrsMp4SampleAuxiliaryInfoOffsetBox::decode_header(SrsBuffer* buf)
{
srs_error_t err = srs_success;
if ((err = SrsMp4FullBox::decode_header(buf)) != srs_success) {
return srs_error_wrap(err, "decode header");
}
if (flags & 0x01) {
aux_info_type = buf->read_4bytes();
aux_info_type_parameter = buf->read_4bytes();
}
uint32_t entry_count = buf->read_4bytes();
for (int i = 0; i < entry_count; i++)
{
if (version == 0) {
offsets.push_back(buf->read_4bytes());
} else {
offsets.push_back(buf->read_8bytes());
}
}
return err;
}
std::stringstream& SrsMp4SampleAuxiliaryInfoOffsetBox::dumps_detail(std::stringstream& ss, SrsMp4DumpContext dc)
{
ss << "entry_count=" << offsets.size();
return ss;
}
SrsMp4SubSampleEncryptionInfo::SrsMp4SubSampleEncryptionInfo()
{
bytes_of_clear_data = 0;
bytes_of_protected_data = 0;
}
SrsMp4SubSampleEncryptionInfo::~SrsMp4SubSampleEncryptionInfo()
{
}
uint64_t SrsMp4SubSampleEncryptionInfo::nb_bytes()
{
// sizeof(bytes_of_clear_data) + sizeof(bytes_of_protected_data);
return 6;
}
srs_error_t SrsMp4SubSampleEncryptionInfo::encode(SrsBuffer* buf)
{
buf->write_2bytes(bytes_of_clear_data);
buf->write_4bytes(bytes_of_protected_data);
return srs_success;
}
srs_error_t SrsMp4SubSampleEncryptionInfo::decode(SrsBuffer* buf)
{
bytes_of_clear_data = buf->read_2bytes();
bytes_of_protected_data = buf->read_4bytes();
return srs_success;
}
std::stringstream& SrsMp4SubSampleEncryptionInfo::dumps(std::stringstream& ss, SrsMp4DumpContext dc)
{
ss << "bytes_of_clear_data=" << bytes_of_clear_data << ", bytes_of_protected_data=" << bytes_of_protected_data;
return ss;
}
SrsMp4SampleEncryptionEntry::SrsMp4SampleEncryptionEntry(SrsMp4FullBox* senc, uint8_t per_sample_iv_size)
{
senc_ = senc;
srs_assert(per_sample_iv_size == 0 || per_sample_iv_size == 8 || per_sample_iv_size == 16);
per_sample_iv_size_ = per_sample_iv_size;
iv_ = (uint8_t*) malloc(per_sample_iv_size);
}
SrsMp4SampleEncryptionEntry::~SrsMp4SampleEncryptionEntry()
{
srs_freep(iv_);
}
srs_error_t SrsMp4SampleEncryptionEntry::set_iv(uint8_t* iv, uint8_t iv_size)
{
srs_assert(iv_size == per_sample_iv_size_);
memcpy(iv_, iv, iv_size);
return srs_success;
}
uint64_t SrsMp4SampleEncryptionEntry::nb_bytes()
{
uint64_t size = per_sample_iv_size_;
if (senc_->flags & SrsMp4CencSampleEncryptionUseSubSample) {
size += 2; // size of subsample_count
size += subsample_infos.size() * 6;
}
return size;
}
srs_error_t SrsMp4SampleEncryptionEntry::encode(SrsBuffer* buf)
{
if (per_sample_iv_size_ != 0) {
buf->write_bytes((char*) iv_, per_sample_iv_size_);
}
if (senc_->flags & SrsMp4CencSampleEncryptionUseSubSample) {
buf->write_2bytes(subsample_infos.size());
vector<SrsMp4SubSampleEncryptionInfo>::iterator it;
for (it = subsample_infos.begin(); it != subsample_infos.end(); ++it) {
(*it).encode(buf);
}
}
return srs_success;
}
srs_error_t SrsMp4SampleEncryptionEntry::decode(SrsBuffer* buf)
{
if (per_sample_iv_size_ > 0) {
buf->read_bytes((char*)iv_, per_sample_iv_size_);
}
if (senc_->flags & SrsMp4CencSampleEncryptionUseSubSample) {
uint16_t subsample_count = buf->read_2bytes();
for (uint16_t i = 0; i < subsample_count; i++) {
SrsMp4SubSampleEncryptionInfo info;
info.decode(buf);
subsample_infos.push_back(info);
}
}
return srs_success;
}
std::stringstream& SrsMp4SampleEncryptionEntry::dumps(std::stringstream& ss, SrsMp4DumpContext dc)
{
// TODO: dump what?
ss << "iv=" << iv_ << endl;
vector<SrsMp4SubSampleEncryptionInfo>::iterator it;
for (it = subsample_infos.begin(); it != subsample_infos.end(); ++it) {
(*it).dumps(ss, dc);
ss << endl;
}
return ss;
}
SrsMp4SampleEncryptionBox::SrsMp4SampleEncryptionBox(uint8_t per_sample_iv_size)
{
version = 0;
flags = SrsMp4CencSampleEncryptionUseSubSample;
type = SrsMp4BoxTypeSENC;
srs_assert(per_sample_iv_size == 0 || per_sample_iv_size == 8 || per_sample_iv_size == 16);
per_sample_iv_size_ = per_sample_iv_size;
}
SrsMp4SampleEncryptionBox::~SrsMp4SampleEncryptionBox()
{
vector<SrsMp4SampleEncryptionEntry*>::iterator it;
for (it = entries.begin(); it != entries.end(); it++)
{
SrsMp4SampleEncryptionEntry* entry = *it;
srs_freep(entry);
}
entries.clear();
}
int SrsMp4SampleEncryptionBox::nb_header()
{
int size = SrsMp4FullBox::nb_header() + 4;
vector<SrsMp4SampleEncryptionEntry*>::iterator it;
for (it = entries.begin(); it < entries.end(); it++)
{
size += (*it)->nb_bytes();
}
return size;
}
srs_error_t SrsMp4SampleEncryptionBox::encode_header(SrsBuffer* buf)
{
srs_error_t err = srs_success;
if ((err = SrsMp4FullBox::encode_header(buf)) != srs_success) {
return srs_error_wrap(err, "encode header");
}
buf->write_4bytes(entries.size());
vector<SrsMp4SampleEncryptionEntry*>::iterator it;
for (it = entries.begin(); it != entries.end(); it++)
{
(*it)->encode(buf);
}
return err;
}
srs_error_t SrsMp4SampleEncryptionBox::decode_header(SrsBuffer* buf)
{
srs_error_t err = srs_success;
if ((err = SrsMp4FullBox::decode_header(buf)) != srs_success) {
return srs_error_wrap(err, "decode header");
}
vector<SrsMp4SampleEncryptionEntry*>::iterator it;
for (it = entries.begin(); it != entries.end(); it++)
{
SrsMp4SampleEncryptionEntry* entry = *it;
srs_freep(entry);
}
entries.clear();
int32_t size = buf->read_4bytes();
for (int i = 0; i < size; i++) {
SrsMp4SampleEncryptionEntry *entry = new SrsMp4SampleEncryptionEntry(this, per_sample_iv_size_);
entry->decode(buf);
entries.push_back(entry);
}
return err;
}
std::stringstream& SrsMp4SampleEncryptionBox::dumps_detail(std::stringstream& ss, SrsMp4DumpContext dc)
{
ss << "sample_count=" << entries.size() << endl;
return ss;
}
SrsMp4ProtectionSchemeInfoBox::SrsMp4ProtectionSchemeInfoBox()
{
type = SrsMp4BoxTypeSINF;
}
SrsMp4ProtectionSchemeInfoBox::~SrsMp4ProtectionSchemeInfoBox()
{
}
SrsMp4OriginalFormatBox* SrsMp4ProtectionSchemeInfoBox::frma()
{
SrsMp4Box* box = get(SrsMp4BoxTypeFRMA);
return dynamic_cast<SrsMp4OriginalFormatBox*>(box);
}
void SrsMp4ProtectionSchemeInfoBox::set_frma(SrsMp4OriginalFormatBox* v)
{
remove(SrsMp4BoxTypeFRMA);
boxes.push_back(v);
}
SrsMp4SchemeTypeBox* SrsMp4ProtectionSchemeInfoBox::schm()
{
SrsMp4Box* box = get(SrsMp4BoxTypeSCHM);
return dynamic_cast<SrsMp4SchemeTypeBox*>(box);
}
void SrsMp4ProtectionSchemeInfoBox::set_schm(SrsMp4SchemeTypeBox* v)
{
remove(SrsMp4BoxTypeSCHM);
boxes.push_back(v);
}
SrsMp4SchemeInfoBox* SrsMp4ProtectionSchemeInfoBox::schi()
{
SrsMp4Box* box = get(SrsMp4BoxTypeSCHI);
return dynamic_cast<SrsMp4SchemeInfoBox*>(box);
}
void SrsMp4ProtectionSchemeInfoBox::set_schi(SrsMp4SchemeInfoBox* v)
{
remove(SrsMp4BoxTypeSCHI);
boxes.push_back(v);
}
SrsMp4OriginalFormatBox::SrsMp4OriginalFormatBox(uint32_t original_format)
{
type = SrsMp4BoxTypeFRMA;
data_format_ = original_format;
}
SrsMp4OriginalFormatBox::~SrsMp4OriginalFormatBox()
{
}
int SrsMp4OriginalFormatBox::nb_header()
{
return SrsMp4Box::nb_header() + 4;
}
srs_error_t SrsMp4OriginalFormatBox::encode_header(SrsBuffer* buf)
{
srs_error_t err = srs_success;
if ((err = SrsMp4Box::encode_header(buf)) != srs_success) {
return srs_error_wrap(err, "encode header");
}
buf->write_4bytes(data_format_);
return err;
}
srs_error_t SrsMp4OriginalFormatBox::decode_header(SrsBuffer* buf)
{
srs_error_t err = srs_success;
if ((err = SrsMp4Box::decode_header(buf)) != srs_success) {
return srs_error_wrap(err, "decode header");
}
data_format_ = buf->read_4bytes();
return err;
}
std::stringstream& SrsMp4OriginalFormatBox::dumps_detail(std::stringstream& ss, SrsMp4DumpContext dc)
{
ss << "original format=" << data_format_ << endl;
return ss;
}
SrsMp4SchemeTypeBox::SrsMp4SchemeTypeBox()
{
type = SrsMp4BoxTypeSCHM;
}
SrsMp4SchemeTypeBox::~SrsMp4SchemeTypeBox()
{
}
void SrsMp4SchemeTypeBox::set_scheme_uri(char* uri, uint32_t uri_size)
{
srs_assert(uri_size < SCHM_SCHEME_URI_MAX_SIZE);
memcpy(scheme_uri, uri, uri_size);
scheme_uri_size = uri_size;
scheme_uri[uri_size] = '\0';
}
int SrsMp4SchemeTypeBox::nb_header()
{
int size = SrsMp4FullBox::nb_header() + 4 + 4; // sizeof(scheme_type) + sizeof(scheme_version)
if (flags & 0x01) {
size += scheme_uri_size;
}
return size;
}
srs_error_t SrsMp4SchemeTypeBox::encode_header(SrsBuffer* buf)
{
srs_error_t err = srs_success;
if ((err = SrsMp4FullBox::encode_header(buf)) != srs_success) {
return srs_error_wrap(err, "encode header");
}
buf->write_4bytes(scheme_type);
buf->write_4bytes(scheme_version);
if (flags & 0x01) {
buf->write_bytes(scheme_uri, scheme_uri_size);
buf->write_1bytes(0);
}
return err;
}
srs_error_t SrsMp4SchemeTypeBox::decode_header(SrsBuffer* buf)
{
srs_error_t err = srs_success;
if ((err = SrsMp4FullBox::decode_header(buf)) != srs_success) {
return srs_error_wrap(err, "encode header");
}
scheme_type = buf->read_4bytes();
scheme_version = buf->read_4bytes();
if (flags & 0x01) {
memset(scheme_uri, 0, SCHM_SCHEME_URI_MAX_SIZE);
int s = 0;
while (s < SCHM_SCHEME_URI_MAX_SIZE-1) {
char c = buf->read_1bytes();
scheme_uri[s] = c;
s++;
if (c == '\0') {
break;
}
}
scheme_uri_size = s;
}
return err;
}
std::stringstream& SrsMp4SchemeTypeBox::dumps_detail(std::stringstream& ss, SrsMp4DumpContext dc)
{
ss << "scheme_type=" << scheme_type << ", scheme_version=" << scheme_version << endl;
if (flags & 0x01) {
ss << "scheme_uri=" << scheme_uri << endl;
}
return ss;
}
SrsMp4SchemeInfoBox::SrsMp4SchemeInfoBox()
{
type = SrsMp4BoxTypeSCHI;
}
SrsMp4SchemeInfoBox::~SrsMp4SchemeInfoBox()
{
}
SrsMp4TrackEncryptionBox::SrsMp4TrackEncryptionBox()
{
type = SrsMp4BoxTypeTENC;
}
SrsMp4TrackEncryptionBox::~SrsMp4TrackEncryptionBox()
{
}
void SrsMp4TrackEncryptionBox::set_default_constant_IV(uint8_t* iv, uint8_t iv_size)
{
srs_assert(iv_size == 8 || iv_size == 16);
memcpy(default_constant_IV, iv, iv_size);
default_constant_IV_size = iv_size;
}
int SrsMp4TrackEncryptionBox::nb_header()
{
int size = SrsMp4FullBox::nb_header();
size += 1; // sizeof(reserved)
size += 1; // sizeof(reserved_2) or sizeof(default_crypt_byte_block) + sizeof(default_skip_byte_block);
size += 1; // sizeof(default_isProtected);
size += 1; // sizeof(default_Per_Sample_IV_Size;
size += 16; // sizeof(default_KID);
if (default_is_protected == 1 && default_per_sample_IV_size == 0) {
size += 1 + default_constant_IV_size; // sizeof(default_constant_IV_size) + sizeof(default_constant_IV);
}
return size;
}
srs_error_t SrsMp4TrackEncryptionBox::encode_header(SrsBuffer* buf)
{
srs_error_t err = srs_success;
if ((err = SrsMp4FullBox::encode_header(buf)) != srs_success) {
return srs_error_wrap(err, "encode header");
}
buf->write_1bytes(reserved);
if (version == 0) {
buf->write_1bytes(reserved_2);
} else {
buf->write_1bytes( (default_crypt_byte_block << 4) | (default_skip_byte_block & 0x0F));
}
buf->write_1bytes(default_is_protected);
buf->write_1bytes(default_per_sample_IV_size);
buf->write_bytes((char*)default_KID, 16);
if (default_is_protected == 1 && default_per_sample_IV_size == 0) {
buf->write_1bytes(default_constant_IV_size);
buf->write_bytes((char*)default_constant_IV, default_constant_IV_size);
}
return err;
}
srs_error_t SrsMp4TrackEncryptionBox::decode_header(SrsBuffer* buf)
{
srs_error_t err = srs_success;
if ((err = SrsMp4FullBox::decode_header(buf)) != srs_success) {
return srs_error_wrap(err, "encode header");
}
reserved = buf->read_1bytes();
if (version == 0) {
reserved_2 = buf->read_1bytes();
} else {
uint8_t v = buf->read_1bytes();
default_crypt_byte_block = v >> 4;
default_skip_byte_block = v & 0x0f;
}
default_is_protected = buf->read_1bytes();
default_per_sample_IV_size = buf->read_1bytes();
buf->read_bytes((char*)default_KID, 16);
if (default_is_protected == 1 && default_per_sample_IV_size == 0) {
default_constant_IV_size = buf->read_1bytes();
srs_assert(default_constant_IV_size == 8 || default_constant_IV_size == 16);
buf->read_bytes((char*) default_constant_IV, default_constant_IV_size);
}
return err;
}
std::stringstream& SrsMp4TrackEncryptionBox::dumps_detail(std::stringstream& ss, SrsMp4DumpContext dc)
{
if (version != 0) {
ss << "default_crypt_byte_block=" << default_crypt_byte_block << ", default_skip_byte_block=" << default_skip_byte_block << endl;
}
ss << "default_isProtected=" << default_is_protected << ", default_per_sample_IV_size=" << default_per_sample_IV_size << endl;
return ss;
}
SrsMp4Sample::SrsMp4Sample()
{
type = SrsFrameTypeForbidden;
@ -6177,6 +6836,10 @@ SrsMp4ObjectType SrsMp4Encoder::get_audio_object_type()
SrsMp4M2tsInitEncoder::SrsMp4M2tsInitEncoder()
{
writer = NULL;
crypt_byte_block_ = 0;
skip_byte_block_ = 0;
iv_size_ = 0;
is_protected_ = false;
}
SrsMp4M2tsInitEncoder::~SrsMp4M2tsInitEncoder()
@ -6189,6 +6852,18 @@ srs_error_t SrsMp4M2tsInitEncoder::initialize(ISrsWriter* w)
return srs_success;
}
void SrsMp4M2tsInitEncoder::config_encryption(uint8_t crypt_byte_block, uint8_t skip_byte_block, unsigned char* kid, unsigned char* iv, uint8_t iv_size)
{
srs_assert(crypt_byte_block + skip_byte_block == 10);
srs_assert(iv_size == 8 || iv_size == 16);
crypt_byte_block_ = crypt_byte_block;
skip_byte_block_ = skip_byte_block;
memcpy(kid_, kid, 16);
memcpy(iv_, iv, iv_size);
iv_size_ = iv_size;
is_protected_ = true;
}
srs_error_t SrsMp4M2tsInitEncoder::write(SrsFormat* format, bool video, int tid)
{
srs_error_t err = srs_success;
@ -6280,6 +6955,10 @@ srs_error_t SrsMp4M2tsInitEncoder::write(SrsFormat* format, bool video, int tid)
avc1->set_avcC(avcC);
avcC->avc_config = format->vcodec->avc_extra_data;
if (is_protected_ && ((err = config_sample_description_encryption(avc1)) != srs_success)) {
return srs_error_wrap(err, "encrypt avc1 box");
}
} else {
SrsMp4VisualSampleEntry* hev1 = new SrsMp4VisualSampleEntry(SrsMp4BoxTypeHEV1);
stsd->append(hev1);
@ -6292,6 +6971,10 @@ srs_error_t SrsMp4M2tsInitEncoder::write(SrsFormat* format, bool video, int tid)
hev1->set_hvcC(hvcC);
hvcC->hevc_config = format->vcodec->avc_extra_data;
if (is_protected_ && ((err = config_sample_description_encryption(hev1)) != srs_success)) {
return srs_error_wrap(err, "encrypt hev1 box");
}
}
SrsMp4DecodingTime2SampleBox* stts = new SrsMp4DecodingTime2SampleBox();
@ -6382,6 +7065,10 @@ srs_error_t SrsMp4M2tsInitEncoder::write(SrsFormat* format, bool video, int tid)
SrsMp4EsdsBox* esds = new SrsMp4EsdsBox();
mp4a->set_esds(esds);
if (is_protected_ && ((err = config_sample_description_encryption(mp4a)) != srs_success)) {
return srs_error_wrap(err, "encrypt mp4a box");
}
SrsMp4ES_Descriptor* es = esds->es;
es->ES_ID = 0x02;
@ -6518,6 +7205,10 @@ srs_error_t SrsMp4M2tsInitEncoder::write(SrsFormat* format, int v_tid, int a_tid
avc1->set_avcC(avcC);
avcC->avc_config = format->vcodec->avc_extra_data;
if (is_protected_ && ((err = config_sample_description_encryption(avc1)) != srs_success)) {
return srs_error_wrap(err, "encrypt avc1 box");
}
} else {
SrsMp4VisualSampleEntry* hev1 = new SrsMp4VisualSampleEntry(SrsMp4BoxTypeHEV1);
stsd->append(hev1);
@ -6530,6 +7221,10 @@ srs_error_t SrsMp4M2tsInitEncoder::write(SrsFormat* format, int v_tid, int a_tid
hev1->set_hvcC(hvcC);
hvcC->hevc_config = format->vcodec->avc_extra_data;
if (is_protected_ && ((err = config_sample_description_encryption(hev1)) != srs_success)) {
return srs_error_wrap(err, "encrypt hev1 box");
}
}
SrsMp4DecodingTime2SampleBox* stts = new SrsMp4DecodingTime2SampleBox();
@ -6614,6 +7309,9 @@ srs_error_t SrsMp4M2tsInitEncoder::write(SrsFormat* format, int v_tid, int a_tid
SrsMp4EsdsBox* esds = new SrsMp4EsdsBox();
mp4a->set_esds(esds);
if (is_protected_ && ((err = config_sample_description_encryption(mp4a)) != srs_success)) {
return srs_error_wrap(err, "encrypt mp4a box.");
}
SrsMp4ES_Descriptor* es = esds->es;
es->ES_ID = 0x02;
@ -6672,6 +7370,60 @@ srs_error_t SrsMp4M2tsInitEncoder::write(SrsFormat* format, int v_tid, int a_tid
return err;
}
/**
* box->type = 'encv' or 'enca'
* |encv|
* | |sinf|
* | | |frma|
* | | |schm|
* | | |schi|
* | | | |tenc|
*/
srs_error_t SrsMp4M2tsInitEncoder::config_sample_description_encryption(SrsMp4SampleEntry* box)
{
srs_error_t err = srs_success;
bool is_video_sample = false;
SrsMp4BoxType original_type = box->type;
if (original_type == SrsMp4BoxTypeAVC1 || original_type == SrsMp4BoxTypeHEV1)
{
box->type = SrsMp4BoxTypeENCV;
is_video_sample = true;
} else if (original_type == SrsMp4BoxTypeMP4A) {
box->type = SrsMp4BoxTypeENCA;
} else {
return srs_error_new(ERROR_MP4_BOX_ILLEGAL_TYPE, "unknown sample type 0x%x to encrypt", original_type);
}
SrsMp4ProtectionSchemeInfoBox* sinf = new SrsMp4ProtectionSchemeInfoBox();
box->append(sinf);
SrsMp4OriginalFormatBox* frma = new SrsMp4OriginalFormatBox(original_type);
sinf->set_frma(frma);
SrsMp4SchemeTypeBox* schm = new SrsMp4SchemeTypeBox();
schm->scheme_type = SrsMp4CENSchemeCBCS;
schm->scheme_version = 0x00010000;
sinf->set_schm(schm);
SrsMp4SchemeInfoBox* schi = new SrsMp4SchemeInfoBox();
SrsMp4TrackEncryptionBox* tenc = new SrsMp4TrackEncryptionBox();
tenc->version = 1;
tenc->default_crypt_byte_block = is_video_sample ? crypt_byte_block_ : 0 ;
tenc->default_skip_byte_block = is_video_sample ? skip_byte_block_ : 0;
tenc->default_is_protected = 1;
tenc->default_per_sample_IV_size = 0;
tenc->default_constant_IV_size = iv_size_;
memcpy(tenc->default_constant_IV, iv_, iv_size_);
memcpy(tenc->default_KID, kid_, 16);
schi->append(tenc);
sinf->set_schi(schi);
return err;
}
SrsMp4M2tsSegmentEncoder::SrsMp4M2tsSegmentEncoder()
{
writer = NULL;
@ -6879,12 +7631,19 @@ SrsFmp4SegmentEncoder::SrsFmp4SegmentEncoder()
mdat_video_bytes_ = 0;
audio_samples_ = new SrsMp4SampleManager();
video_samples_ = new SrsMp4SampleManager();
memset(iv_,0,16);
key_ = (unsigned char*)new AES_KEY();
do_sample_encryption_ = false;
}
SrsFmp4SegmentEncoder::~SrsFmp4SegmentEncoder()
{
srs_freep(audio_samples_);
srs_freep(video_samples_);
AES_KEY* k = (AES_KEY*)key_;
srs_freep(k);
}
@ -6901,6 +7660,21 @@ srs_error_t SrsFmp4SegmentEncoder::initialize(ISrsWriter* w, uint32_t sequence,
return err;
}
srs_error_t SrsFmp4SegmentEncoder::config_cipher(unsigned char* key, unsigned char* iv)
{
srs_error_t err = srs_success;
memcpy(this->iv_, iv, 16);
AES_KEY* k = (AES_KEY*)this->key_;
if (AES_set_encrypt_key(key, 16 * 8, k)) {
return srs_error_new(ERROR_SYSTEM_FILE_WRITE, "set aes key failed");
}
do_sample_encryption_ = true;
return err;
}
srs_error_t SrsFmp4SegmentEncoder::write_sample(SrsMp4HandlerType ht, uint16_t ft,
uint32_t dts, uint32_t pts, uint8_t* sample, uint32_t nb_sample)
{
@ -6982,6 +7756,22 @@ srs_error_t SrsFmp4SegmentEncoder::flush(uint64_t& dts)
if ((err = video_samples_->write(traf, dts)) != srs_success) {
return srs_error_wrap(err, "write samples");
}
// TODO: write senc, and optional saiz & saio
if (do_sample_encryption_) {
SrsMp4SampleEncryptionBox* senc = new SrsMp4SampleEncryptionBox(0);
// video_samples_;
vector<SrsMp4Sample*>::iterator it;
// write video sample data
for (it = video_samples_->samples.begin(); it != video_samples_->samples.end(); ++it) {
SrsMp4Sample* sample = *it;
// TODO: parse hevc|avc, nalu slice header, and calculate
// sample->data;
// sample->nb_data;
}
traf->append(senc);
}
}
// write audio traf
@ -7009,6 +7799,13 @@ srs_error_t SrsFmp4SegmentEncoder::flush(uint64_t& dts)
if ((err = audio_samples_->write(traf, dts)) != srs_success) {
return srs_error_wrap(err, "write samples");
}
// TODO: write senc, and optional saiz & saio
if (do_sample_encryption_) {
SrsMp4SampleEncryptionBox* senc = new SrsMp4SampleEncryptionBox(0);
// this->iv_;
traf->append(senc);
}
}
// @remark Remember the data_offset of turn is size(moof)+header(mdat)
@ -7051,6 +7848,7 @@ srs_error_t SrsFmp4SegmentEncoder::flush(uint64_t& dts)
SrsMp4Sample* sample = *it;
// TODO: FIXME: Ensure all bytes are writen.
// TODO: do cbcs encryption here. sample are nalu_length + nalu data.
if ((err = writer_->write(sample->data, sample->nb_data, NULL)) != srs_success) {
return srs_error_wrap(err, "write sample");
}
@ -7061,6 +7859,7 @@ srs_error_t SrsFmp4SegmentEncoder::flush(uint64_t& dts)
SrsMp4Sample* sample = *it;
// TODO: FIXME: Ensure all bytes are writen.
// TODO: do cbcs encryption here
if ((err = writer_->write(sample->data, sample->nb_data, NULL)) != srs_success) {
return srs_error_wrap(err, "write sample");
}

View file

@ -113,6 +113,27 @@ enum SrsMp4BoxType
SrsMp4BoxTypeSIDX = 0x73696478, // 'sidx'
SrsMp4BoxTypeHEV1 = 0x68657631, // 'hev1'
SrsMp4BoxTypeHVCC = 0x68766343, // 'hvcC'
SrsMp4BoxTypeSENC = 0x73656e63, // 'senc'
SrsMp4BoxTypeSAIZ = 0x7361697a, // 'saiz'
SrsMp4BoxTypeSAIO = 0x7361696f, // 'saio'
SrsMp4BoxTypeENCV = 0x656e6376, // 'encv'
SrsMp4BoxTypeENCA = 0x656e6361, // 'enca'
SrsMp4BoxTypeSINF = 0x73696e66, // 'sinf'
SrsMp4BoxTypeSCHI = 0x73636869, // 'schi'
SrsMp4BoxTypeTENC = 0x74656e63, // 'tenc'
SrsMp4BoxTypeFRMA = 0x66726d61, // 'frma'
SrsMp4BoxTypeSCHM = 0x7363686d, // 'schm'
};
// Common encryption scheme types
// @see ISO-IEC-23001-7.pdf, 4.2
enum SrsMp4CENSchemeType
{
SrsMp4CENSchemeCENC = 0x63656e63, // 'cenc'
SrsMp4CENSchemeCBC1 = 0x63626331, // 'cbc1'
SrsMp4CENSchemeCENS = 0x63656e73, // 'cens'
SrsMp4CENSchemeCBCS = 0x63626373, // 'cbcs'
SrsMp4CENSchemeSVE1 = 0x73766531, // 'sve1'
};
// 8.4.3.3 Semantics
@ -1868,6 +1889,348 @@ public:
virtual std::stringstream& dumps_detail(std::stringstream& ss, SrsMp4DumpContext dc);
};
// Sample auxiliary information sizes box (saiz)
// @see ISO_IEC_14496-12-base-format-2012.pdf, 8.7.8, page 62
// @see https://github.com/gpac/mp4box.js/blob/master/src/parsing/saiz.js
// Syntax
// aligned(8) class SampleAuxiliaryInformationSizesBox extends FullBox('saiz', version=0, flags)
// {
// if (flags & 1) {
// unsigned int(32) aux_info_type;
// unsigned int(32) aux_info_type_parameter;
// }
// unsigned int(8) default_sample_info_size;
// unsigned int(32) sample_count;
// if (default_sample_info_size == 0) {
// unsigned int(8) sample_info_size[sample_count];
// }
// }
class SrsMp4SampleAuxiliaryInfoSizeBox: public SrsMp4FullBox
{
public:
uint32_t aux_info_type;
uint32_t aux_info_type_parameter;
uint8_t default_sample_info_size;
uint32_t sample_count;
std::vector<uint8_t> sample_info_sizes;
public:
SrsMp4SampleAuxiliaryInfoSizeBox();
virtual ~SrsMp4SampleAuxiliaryInfoSizeBox();
protected:
virtual int nb_header();
virtual srs_error_t encode_header(SrsBuffer* buf);
virtual srs_error_t decode_header(SrsBuffer* buf);
public:
virtual std::stringstream& dumps_detail(std::stringstream& ss, SrsMp4DumpContext dc);
};
// Sample auxiliary information offsets box (saio)
// @see ISO_IEC_14496-12-base-format-2012.pdf, 8.7.9, page 63
// @see https://github.com/gpac/mp4box.js/blob/master/src/parsing/saio.js
// Syntax
// aligned(8) class SampleAuxiliaryInformationOffsetsBox extends FullBox('saio', version, flags)
// {
// if (flags & 1) {
// unsigned int(32) aux_info_type;
// unsigned int(32) aux_info_type_parameter;
// }
// unsigned int(32) entry_count;
// if (version == 0) {
// unsigned int(32) offset[entry_count];
// } else {
// unsigned int(64) offset[entry_count];
// }
// }
class SrsMp4SampleAuxiliaryInfoOffsetBox: public SrsMp4FullBox
{
public:
uint32_t aux_info_type;
uint32_t aux_info_type_parameter;
// uint32_t entry_count;
std::vector<uint64_t> offsets;
public:
SrsMp4SampleAuxiliaryInfoOffsetBox();
virtual ~SrsMp4SampleAuxiliaryInfoOffsetBox();
protected:
virtual int nb_header();
virtual srs_error_t encode_header(SrsBuffer* buf);
virtual srs_error_t decode_header(SrsBuffer* buf);
public:
virtual std::stringstream& dumps_detail(std::stringstream& ss, SrsMp4DumpContext dc);
};
enum SrsMp4CencSampleEncryptionFlags
{
SrsMp4CencSampleEncryptionTrackDefault = 0x01,
SrsMp4CencSampleEncryptionUseSubSample = 0x02,
};
struct SrsMp4SubSampleEncryptionInfo : public ISrsCodec
{
uint16_t bytes_of_clear_data;
uint32_t bytes_of_protected_data;
SrsMp4SubSampleEncryptionInfo();
virtual ~SrsMp4SubSampleEncryptionInfo();
virtual uint64_t nb_bytes();
virtual srs_error_t encode(SrsBuffer* buf);
virtual srs_error_t decode(SrsBuffer* buf);
virtual std::stringstream& dumps(std::stringstream& ss, SrsMp4DumpContext dc);
};
class SrsMp4SampleEncryptionEntry : public ISrsCodec
{
public:
// if flags && 0x02
std::vector<SrsMp4SubSampleEncryptionInfo> subsample_infos;
public:
SrsMp4SampleEncryptionEntry(SrsMp4FullBox* senc, uint8_t per_sample_iv_size);
virtual ~SrsMp4SampleEncryptionEntry();
virtual srs_error_t set_iv(uint8_t* iv, uint8_t iv_size);
virtual uint64_t nb_bytes();
virtual srs_error_t encode(SrsBuffer* buf);
virtual srs_error_t decode(SrsBuffer* buf);
virtual std::stringstream& dumps(std::stringstream& ss, SrsMp4DumpContext dc);
private:
SrsMp4FullBox* senc_;
uint8_t per_sample_iv_size_;
uint8_t* iv_;
};
// Sample encryption box (senc)
// @see ISO-IEC-23001-7.pdf 7.2.1
// @see https://cdn.standards.iteh.ai/samples/84637/c960c91d60ae4da7a2f9380bd7e08642/ISO-IEC-FDIS-23001-7.pdf
// CENC SAI: sample auxiliary information associated with a sample and containing cryptographic information
// such as initialization vector or subsample information
// @see ISO-IEC-23001-7.pdf 7.2.2
// Syntax
// aligned(8) class SampleEncryptionBox extend FullBox(`senc`, version=0, flags)
// {
// unsigned int(32) sample_count;
// {
// unsigned int(Per_Sample_IV_Size*8) InitializationVector;
// if (flags & 0x000002)
// {
// unsigned int(16) subsample_count;
// {
// unsigned int(16) BytesOfClearData;
// unsigned int(32) BytesOfProtectedData;
// } [ subsample_count ]
// }
// } [ sample_count ]
// }
class SrsMp4SampleEncryptionBox: public SrsMp4FullBox
{
public:
std::vector<SrsMp4SampleEncryptionEntry*> entries;
private:
uint8_t per_sample_iv_size_;
public:
// @see ISO-IEC-23001-7.pdf 9.1
// Per_Sample_IV_Size has supported values: 0, 8, 16.
SrsMp4SampleEncryptionBox(uint8_t per_sample_iv_size);
virtual ~SrsMp4SampleEncryptionBox();
protected:
virtual int nb_header();
virtual srs_error_t encode_header(SrsBuffer* buf);
virtual srs_error_t decode_header(SrsBuffer* buf);
public:
virtual std::stringstream& dumps_detail(std::stringstream& ss, SrsMp4DumpContext dc);
};
// Original Format Box (frma)
// @see ISO_IEC_14496-12-base-format-2012.pdf, 8.12.2, page 81
// aligned(8) class OriginalFormatBox(codingname) extends Box ('frma') {
// unsigned int(32) data_format = codingname;
// }
class SrsMp4OriginalFormatBox : public SrsMp4Box
{
private:
uint32_t data_format_;
public:
SrsMp4OriginalFormatBox(uint32_t original_format);
virtual ~SrsMp4OriginalFormatBox();
protected:
virtual int nb_header();
virtual srs_error_t encode_header(SrsBuffer* buf);
virtual srs_error_t decode_header(SrsBuffer* buf);
public:
virtual std::stringstream& dumps_detail(std::stringstream& ss, SrsMp4DumpContext dc);
};
// Scheme Type Box (schm)
// @see ISO_IEC_14496-12-base-format-2012.pdf, 8.12.5, page 81
// aligned(8) class SchemeTypeBox extends FullBox('schm', 0, flags) {
// unsigned int(32) scheme_type; // 4CC identifying the scheme
// unsigned int(32) scheme_version; // scheme version
// if (flags & 0x000001) {
// unsigned int(8) scheme_uri[]; // browser uri
// }
// }
// @see @see ISO-IEC-23001-7.pdf 4.1
// the scheme_version field SHALL be set to 0x00010000 (Major version 1, Minor version 0).
#define SCHM_SCHEME_URI_MAX_SIZE 128
class SrsMp4SchemeTypeBox : public SrsMp4FullBox
{
public:
uint32_t scheme_type;
uint32_t scheme_version;
char scheme_uri[SCHM_SCHEME_URI_MAX_SIZE];
uint32_t scheme_uri_size;
public:
SrsMp4SchemeTypeBox();
virtual ~SrsMp4SchemeTypeBox();
public:
virtual void set_scheme_uri(char* uri, uint32_t uri_size);
protected:
virtual int nb_header();
virtual srs_error_t encode_header(SrsBuffer* buf);
virtual srs_error_t decode_header(SrsBuffer* buf);
public:
virtual std::stringstream& dumps_detail(std::stringstream& ss, SrsMp4DumpContext dc);
};
// Scheme Information Box (schi)
// @see ISO_IEC_14496-12-base-format-2012.pdf, 8.12.6, page 82
// aligned(8) class SchemeInformationBox extends Box('schi') {
// Box scheme_specific_data[];
// }
class SrsMp4SchemeInfoBox : public SrsMp4Box
{
public:
SrsMp4SchemeInfoBox();
virtual ~SrsMp4SchemeInfoBox();
};
// Protection Scheme Information Box (sinf)
// @see ISO_IEC_14496-12-base-format-2012.pdf, 8.12.1, page 80
// aligned(8) class ProtectionSchemeInfoBox(fmt) extends Box('sinf') {
// OriginalFormatBox(fmt) original_format; // frma
// SchemeTypeBox scheme_type_box; // optional
// SchemeInformationBox info; // optional
// }
class SrsMp4ProtectionSchemeInfoBox : public SrsMp4Box
{
public:
SrsMp4ProtectionSchemeInfoBox();
virtual ~SrsMp4ProtectionSchemeInfoBox();
public:
// Get the Original Format Box (frma)
virtual SrsMp4OriginalFormatBox* frma();
virtual void set_frma(SrsMp4OriginalFormatBox* v);
// Get the Scheme Type Box (schm)
virtual SrsMp4SchemeTypeBox* schm();
virtual void set_schm(SrsMp4SchemeTypeBox* v);
// Get the Scheme Information Box (schi)
virtual SrsMp4SchemeInfoBox* schi();
virtual void set_schi(SrsMp4SchemeInfoBox* v);
};
// Track Encryption box (tenc)
// @see ISO-IEC-23001-7.pdf 8.2
// aligned(8) class TrackEncryptionBox extends FullBox('tenc', version, flags=0) {
// unsigned int(8) reserved = 0;
// if (version == 0) {
// unsigned int(8) reserved = 0;
// } else { // version is 1 or greater
// unsigned int(4) default_crypt_byte_block;
// unsigned int(4) default_skip_byte_block;
// }
// unsigned int(8) default_isProtected;
// unsigned int(8) default_Per_Sample_IV_Size;
// unsigned int(8)[16] default_KID;
// if (default_isProtected == 1 && default_Per_Sample_IV_Size == 0) {
// unsigned int(8) default_constant_IV_size;
// unsigned int(8)[default_constant_IV_size] default_constant_IV;
// }
// }
// @see https://developer.apple.com/documentation/http-live-streaming/about-the-common-media-application-format-with-http-live-streaming-hls
// For fragmented MPEG-4 Segments, an EXT-X-KEY tag with a METHOD=SAMPLE-AES attribute indicates that
// the Segment is encrypted using the `cbcs` scheme in ISO/IEC 23001-7.
// HLS supports unencrypted and encrypted with 'cbcs'.
// @see ISO-IEC-23001-7.pdf 10.4.1 Definition
// 'cbcs' AES-CBC subsample pattern encryption scheme.
// The 'scheme_type' field of the scheme Type Box('schm') SHALL be set to 'cbcs'.
// the version of the Track Encryption Box('tenc') SHALL be 1.
// Encrypted video tracks using NAL Structured Video conforming to ISO/IEC 14496-15 SHALL be
// protected using Subsample encryption specified in 9.5, and SHALL use pattern encryption as specified
// in 9.6. As a result, the fields crypt_byte_block and skip_byte_block SHALL NOT be 0.
// Constant IVs SHALL be used; 'default_Per_Sample_IV_Size' and 'Per_Sample_IV_Size', SHALL be 0.
// Tracks other than video are protected using whole-block full-sample encryption as specified in 9.7 and
// hence skip_byte_block SHALL be 0.
// Pattern Block length, i.e. crypt_byte_block + skip_byte_block SHOULD equal 10.
// For all video NAL units, including in 'avc1', the slice header SHALL be unencrypted.
// The first complete byte of video slice data(following the video slice header) SHALL begin a single
// Subsample protected byte range indicated by the start of BytesOfProtectedData, which extends to
// the end of the video NAL.
// NOTE 1 For AVC VCL NAL units, the encryption pattern starts at an offset rounded to the next byte after
// the slice header, i.e. on the first full byte of slice data. For HEVC, the encryption pattern starts after
// the byte_alignment() field that terminates the slice_segment_header(), i.e. on the first byte of slice data.
//
// @see ISO-IEC-23001-7.pdf 10.4.2 'cbcs' AES-CBC mode pattern encryption scheme application(informative)
// An encrypt:skip pattern of 1:9(i.e. 10% partial encryption) is recommended. Even though the syntax
// allows many different encryption patterns, a pattern of ten Blocks is recommended. This means that the
// skipped Blocks will be (10-N). The number of encrypted cipher blocks N can span multiple contiguous
// 16-byte Blocks(e.g. three encrypted Blocks followed by seven unencrypted Blocks would result in 30%
// partial encryption of the video data).
// For example, to achieve 10 % encryption, the first Block of the pattern is encrypted and the following
// nine Blocks are left unencrypted. The pattern is repeated every 160 bytes of the protected range, until
// the end of the range. If the protected range of the slice body is not a multiple of the pattern length
// (e.g. 160 bytes), then the pattern sequence applies to the included whole 16-byte Blocks and a partial
// 16-byte Block that may remain where the pattern is terminated by the byte length of the range
// BytesOfProtectedData, is left unencrypted.
//
// @see ISO-IEC-23001-7.pdf 9.7 Whole-block full sample encryption
// In whole-block full sample encryption, the entire sample is protected. Every sample is encrypted
// starting at offset 0(there is no unprotected preamble) up to the last 16-byte boundary, leaving any
// trailing 0-15 bytes in the clear. The IV is reset at every sample.
class SrsMp4TrackEncryptionBox : public SrsMp4FullBox
{
public:
uint8_t reserved;
uint8_t reserved_2;
uint8_t default_crypt_byte_block;
uint8_t default_skip_byte_block;
uint8_t default_is_protected;
uint8_t default_per_sample_IV_size;
uint8_t default_KID[16];
uint8_t default_constant_IV_size;
uint8_t default_constant_IV[16];
public:
SrsMp4TrackEncryptionBox();
virtual ~SrsMp4TrackEncryptionBox();
public:
virtual void set_default_constant_IV(uint8_t* iv, uint8_t iv_size);
protected:
virtual int nb_header();
virtual srs_error_t encode_header(SrsBuffer* buf);
virtual srs_error_t decode_header(SrsBuffer* buf);
public:
virtual std::stringstream& dumps_detail(std::stringstream& ss, SrsMp4DumpContext dc);
};
// TODO: add SchemeTypeBox(schm), set scheme_type=cbcs
// Generally, a MP4 sample contains a frame, for example, a video frame or audio frame.
class SrsMp4Sample
{
@ -2115,12 +2478,24 @@ class SrsMp4M2tsInitEncoder
{
private:
ISrsWriter* writer;
private:
uint8_t crypt_byte_block_;
uint8_t skip_byte_block_;
unsigned char kid_[16];
unsigned char iv_[16];
uint8_t iv_size_;
bool is_protected_;
public:
SrsMp4M2tsInitEncoder();
virtual ~SrsMp4M2tsInitEncoder();
public:
// Initialize the encoder with a writer w.
virtual srs_error_t initialize(ISrsWriter* w);
// set encryption
// TODO: review kid(map to a key) and iv, which are shared between audio/video tracks.
virtual void config_encryption(uint8_t crypt_byte_block, uint8_t skip_byte_block, unsigned char* kid, unsigned char* iv, uint8_t iv_size);
// Write the sequence header.
// TODO: merge this method to its sibling.
virtual srs_error_t write(SrsFormat* format, bool video, int tid);
@ -2142,6 +2517,18 @@ public:
* Write the sequence header with both video and audio track.
*/
virtual srs_error_t write(SrsFormat* format, int v_tid, int a_tid);
private:
/**
* box->type = 'encv' or 'enca'
* |encv|
* | |sinf|
* | | |frma|
* | | |schm|
* | | |schi|
* | | | |tenc|
*/
virtual srs_error_t config_sample_description_encryption(SrsMp4SampleEntry* box);
};
// A fMP4 encoder, to cache segments then flush to disk, because the fMP4 should write
@ -2200,12 +2587,17 @@ private:
uint64_t mdat_video_bytes_;
SrsMp4SampleManager* audio_samples_;
SrsMp4SampleManager* video_samples_;
unsigned char* key_;
unsigned char iv_[16];
bool do_sample_encryption_;
public:
SrsFmp4SegmentEncoder();
virtual ~SrsFmp4SegmentEncoder();
public:
// Initialize the encoder with a writer w.
virtual srs_error_t initialize(ISrsWriter* w, uint32_t sequence, srs_utime_t basetime, uint32_t v_tid, uint32_t a_tid);
// config cipher
virtual srs_error_t config_cipher(unsigned char* key, unsigned char* iv);
// Cache a sample.
// @param ht, The sample handler type, audio/soun or video/vide.
// @param ft, The frame type. For video, it's SrsVideoAvcFrameType.