mirror of
https://github.com/axiomatic-systems/Bento4.git
synced 2026-01-19 00:05:12 +08:00
2311 lines
91 KiB
C++
2311 lines
91 KiB
C++
/*****************************************************************
|
|
|
|
|
| AP4 - Common Encryption Support
|
|
|
|
|
| Copyright 2002-2011 Axiomatic Systems, LLC
|
|
|
|
|
|
|
|
| This file is part of Bento4/AP4 (MP4 Processing Library).
|
|
|
|
|
| Unless you have obtained Bento4 under a difference license,
|
|
| this version of Bento4 is Bento4|GPL.
|
|
| Bento4|GPL is free software; you can redistribute it and/or modify
|
|
| it under the terms of the GNU General Public License as published by
|
|
| the Free Software Foundation; either version 2, or (at your option)
|
|
| any later version.
|
|
|
|
|
| Bento4|GPL is distributed in the hope that it will be useful,
|
|
| but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
| GNU General Public License for more details.
|
|
|
|
|
| You should have received a copy of the GNU General Public License
|
|
| along with Bento4|GPL; see the file COPYING. If not, write to the
|
|
| Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
|
| 02111-1307, USA.
|
|
|
|
|
****************************************************************/
|
|
|
|
/*----------------------------------------------------------------------
|
|
| includes
|
|
+---------------------------------------------------------------------*/
|
|
#include "Ap4CommonEncryption.h"
|
|
#include "Ap4Utils.h"
|
|
#include "Ap4FrmaAtom.h"
|
|
#include "Ap4SchmAtom.h"
|
|
#include "Ap4TencAtom.h"
|
|
#include "Ap4SencAtom.h"
|
|
#include "Ap4FragmentSampleTable.h"
|
|
#include "Ap4FtypAtom.h"
|
|
#include "Ap4StsdAtom.h"
|
|
#include "Ap4TrakAtom.h"
|
|
#include "Ap4HdlrAtom.h"
|
|
#include "Ap4TfhdAtom.h"
|
|
#include "Ap4SaizAtom.h"
|
|
#include "Ap4SaioAtom.h"
|
|
#include "Ap4Piff.h"
|
|
#include "Ap4StreamCipher.h"
|
|
#include "Ap4SaioAtom.h"
|
|
#include "Ap4SaizAtom.h"
|
|
#include "Ap4TrunAtom.h"
|
|
#include "Ap4Marlin.h"
|
|
#include "Ap4PsshAtom.h"
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencSampleEncrypter::~AP4_CencSampleEncrypter
|
|
+---------------------------------------------------------------------*/
|
|
AP4_CencSampleEncrypter::~AP4_CencSampleEncrypter()
|
|
{
|
|
delete m_Cipher;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencCtrSampleEncrypter::EncryptSampleData
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_CencCtrSampleEncrypter::EncryptSampleData(AP4_DataBuffer& data_in,
|
|
AP4_DataBuffer& data_out,
|
|
AP4_DataBuffer& /* sample_infos */)
|
|
{
|
|
// the output has the same size as the input
|
|
data_out.SetDataSize(data_in.GetDataSize());
|
|
|
|
// setup direct pointers to the buffers
|
|
const AP4_UI08* in = data_in.GetData();
|
|
AP4_UI08* out = data_out.UseData();
|
|
|
|
// setup the IV
|
|
m_Cipher->SetIV(m_Iv);
|
|
|
|
// process the sample data
|
|
if (data_in.GetDataSize()) {
|
|
AP4_Size out_size = data_out.GetDataSize();
|
|
AP4_Result result = m_Cipher->ProcessBuffer(in, data_in.GetDataSize(), out, &out_size, false);
|
|
if (AP4_FAILED(result)) return result;
|
|
}
|
|
|
|
// update the IV
|
|
unsigned int block_count = (data_in.GetDataSize()+15)/16;
|
|
AP4_UI64 counter = AP4_BytesToUInt64BE(&m_Iv[8]);
|
|
AP4_BytesFromUInt64BE(&m_Iv[8], counter+block_count);
|
|
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencCtrSubSampleEncrypter::GetSubSampleMap
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_CencCtrSubSampleEncrypter::GetSubSampleMap(AP4_DataBuffer& sample_data,
|
|
AP4_Array<AP4_UI16>& bytes_of_cleartext_data,
|
|
AP4_Array<AP4_UI32>& bytes_of_encrypted_data)
|
|
{
|
|
// setup direct pointers to the buffers
|
|
const AP4_UI08* in = sample_data.GetData();
|
|
|
|
// process the sample data, one NALU at a time
|
|
const AP4_UI08* in_end = sample_data.GetData()+sample_data.GetDataSize();
|
|
while ((AP4_Size)(in_end-in) > 1+m_NaluLengthSize) {
|
|
unsigned int nalu_length;
|
|
switch (m_NaluLengthSize) {
|
|
case 1:
|
|
nalu_length = *in;
|
|
break;
|
|
|
|
case 2:
|
|
nalu_length = AP4_BytesToUInt16BE(in);
|
|
break;
|
|
|
|
case 4:
|
|
nalu_length = AP4_BytesToUInt32BE(in);
|
|
break;
|
|
|
|
default:
|
|
return AP4_ERROR_INVALID_FORMAT;
|
|
}
|
|
|
|
unsigned int chunk_size = m_NaluLengthSize+nalu_length;
|
|
unsigned int cleartext_size = chunk_size%16;
|
|
unsigned int block_count = chunk_size/16;
|
|
if (in+chunk_size > in_end) {
|
|
return AP4_ERROR_INVALID_FORMAT;
|
|
}
|
|
if (cleartext_size < m_NaluLengthSize+1) {
|
|
AP4_ASSERT(block_count);
|
|
--block_count;
|
|
cleartext_size += 16;
|
|
}
|
|
|
|
// move the pointers
|
|
in += chunk_size;
|
|
|
|
// store the info
|
|
bytes_of_cleartext_data.Append(cleartext_size);
|
|
bytes_of_encrypted_data.Append(block_count*16);
|
|
}
|
|
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencCtrSubSampleEncrypter::EncryptSampleData
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_CencCtrSubSampleEncrypter::EncryptSampleData(AP4_DataBuffer& data_in,
|
|
AP4_DataBuffer& data_out,
|
|
AP4_DataBuffer& sample_infos)
|
|
{
|
|
// the output has the same size as the input
|
|
data_out.SetDataSize(data_in.GetDataSize());
|
|
|
|
// check some basics
|
|
if (data_in.GetDataSize() == 0) return AP4_SUCCESS;
|
|
|
|
// setup direct pointers to the buffers
|
|
const AP4_UI08* in = data_in.GetData();
|
|
AP4_UI08* out = data_out.UseData();
|
|
|
|
// setup the IV
|
|
unsigned int total_encrypted = 0;
|
|
m_Cipher->SetIV(m_Iv);
|
|
|
|
// get the subsample map
|
|
AP4_Array<AP4_UI16> bytes_of_cleartext_data;
|
|
AP4_Array<AP4_UI32> bytes_of_encrypted_data;
|
|
AP4_Result result = GetSubSampleMap(data_in, bytes_of_cleartext_data, bytes_of_encrypted_data);
|
|
if (AP4_FAILED(result)) return result;
|
|
|
|
// process the data
|
|
for (unsigned int i=0; i<bytes_of_cleartext_data.ItemCount(); i++) {
|
|
// copy the cleartext portion
|
|
AP4_CopyMemory(out, in, bytes_of_cleartext_data[i]);
|
|
|
|
// encrypt the rest
|
|
if (bytes_of_encrypted_data[i]) {
|
|
AP4_Size out_size = bytes_of_encrypted_data[i];
|
|
m_Cipher->ProcessBuffer(in+bytes_of_cleartext_data[i],
|
|
bytes_of_encrypted_data[i],
|
|
out+bytes_of_cleartext_data[i],
|
|
&out_size);
|
|
total_encrypted += bytes_of_encrypted_data[i];
|
|
}
|
|
|
|
// move the pointers
|
|
in += bytes_of_cleartext_data[i]+bytes_of_encrypted_data[i];
|
|
out += bytes_of_cleartext_data[i]+bytes_of_encrypted_data[i];
|
|
}
|
|
|
|
// update the IV
|
|
AP4_UI64 counter = AP4_BytesToUInt64BE(&m_Iv[8]);
|
|
AP4_BytesFromUInt64BE(&m_Iv[8], counter+(total_encrypted+15)/16);
|
|
|
|
// encode the sample infos
|
|
unsigned int sample_info_count = bytes_of_cleartext_data.ItemCount();
|
|
sample_infos.SetDataSize(2+sample_info_count*6);
|
|
AP4_UI08* infos = sample_infos.UseData();
|
|
AP4_BytesFromUInt16BE(infos, sample_info_count);
|
|
for (unsigned int i=0; i<sample_info_count; i++) {
|
|
AP4_BytesFromUInt16BE(&infos[2+i*6], bytes_of_cleartext_data[i]);
|
|
AP4_BytesFromUInt32BE(&infos[2+i*6+2], bytes_of_encrypted_data[i]);
|
|
}
|
|
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencCbcSubSampleEncrypter::GetSubSampleMap
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_CencCbcSubSampleEncrypter::GetSubSampleMap(AP4_DataBuffer& sample_data,
|
|
AP4_Array<AP4_UI16>& bytes_of_cleartext_data,
|
|
AP4_Array<AP4_UI32>& bytes_of_encrypted_data)
|
|
{
|
|
// setup direct pointers to the buffers
|
|
const AP4_UI08* in = sample_data.GetData();
|
|
|
|
// process the sample data, one NALU at a time
|
|
const AP4_UI08* in_end = sample_data.GetData()+sample_data.GetDataSize();
|
|
while ((AP4_Size)(in_end-in) > 1+m_NaluLengthSize) {
|
|
unsigned int nalu_length;
|
|
switch (m_NaluLengthSize) {
|
|
case 1:
|
|
nalu_length = *in;
|
|
break;
|
|
|
|
case 2:
|
|
nalu_length = AP4_BytesToUInt16BE(in);
|
|
break;
|
|
|
|
case 4:
|
|
nalu_length = AP4_BytesToUInt32BE(in);
|
|
break;
|
|
|
|
default:
|
|
return AP4_ERROR_INVALID_FORMAT;
|
|
}
|
|
|
|
unsigned int chunk_size = m_NaluLengthSize+nalu_length;
|
|
unsigned int cleartext_size = chunk_size%16;
|
|
unsigned int block_count = chunk_size/16;
|
|
if (cleartext_size < m_NaluLengthSize+1) {
|
|
AP4_ASSERT(block_count);
|
|
--block_count;
|
|
cleartext_size += 16;
|
|
}
|
|
|
|
// move the pointers
|
|
in += chunk_size;
|
|
|
|
// store the info
|
|
bytes_of_cleartext_data.Append(cleartext_size);
|
|
bytes_of_encrypted_data.Append(block_count*16);
|
|
}
|
|
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencCbcSampleEncrypter::EncryptSampleData
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_CencCbcSampleEncrypter::EncryptSampleData(AP4_DataBuffer& data_in,
|
|
AP4_DataBuffer& data_out,
|
|
AP4_DataBuffer& /* sample_infos */)
|
|
{
|
|
// the output has the same size as the input
|
|
data_out.SetDataSize(data_in.GetDataSize());
|
|
|
|
// setup direct pointers to the buffers
|
|
const AP4_UI08* in = data_in.GetData();
|
|
AP4_UI08* out = data_out.UseData();
|
|
|
|
// setup the IV
|
|
m_Cipher->SetIV(m_Iv);
|
|
|
|
// process the sample data
|
|
unsigned int block_count = data_in.GetDataSize()/16;
|
|
if (block_count) {
|
|
AP4_Size out_size = data_out.GetDataSize();
|
|
AP4_Result result = m_Cipher->ProcessBuffer(in, block_count*16, out, &out_size, false);
|
|
if (AP4_FAILED(result)) return result;
|
|
in += block_count*16;
|
|
out += block_count*16;
|
|
|
|
// update the IV (last cipherblock emitted)
|
|
AP4_CopyMemory(m_Iv, out-16, 16);
|
|
}
|
|
|
|
// any partial block at the end remains in the clear
|
|
unsigned int partial = data_in.GetDataSize()%16;
|
|
if (partial) {
|
|
AP4_CopyMemory(out, in, partial);
|
|
}
|
|
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencCbcSubSampleEncrypter::EncryptSampleData
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_CencCbcSubSampleEncrypter::EncryptSampleData(AP4_DataBuffer& data_in,
|
|
AP4_DataBuffer& data_out,
|
|
AP4_DataBuffer& sample_infos)
|
|
{
|
|
// the output has the same size as the input
|
|
data_out.SetDataSize(data_in.GetDataSize());
|
|
|
|
// check some basics
|
|
if (data_in.GetDataSize() == 0) return AP4_SUCCESS;
|
|
|
|
// setup direct pointers to the buffers
|
|
const AP4_UI08* in = data_in.GetData();
|
|
AP4_UI08* out = data_out.UseData();
|
|
|
|
// setup the IV
|
|
m_Cipher->SetIV(m_Iv);
|
|
|
|
// get the subsample map
|
|
AP4_Array<AP4_UI16> bytes_of_cleartext_data;
|
|
AP4_Array<AP4_UI32> bytes_of_encrypted_data;
|
|
AP4_Result result = GetSubSampleMap(data_in, bytes_of_cleartext_data, bytes_of_encrypted_data);
|
|
if (AP4_FAILED(result)) return result;
|
|
|
|
for (unsigned int i=0; i<bytes_of_cleartext_data.ItemCount(); i++) {
|
|
// copy the cleartext portion
|
|
AP4_CopyMemory(out, in, bytes_of_cleartext_data[i]);
|
|
|
|
// encrypt the rest
|
|
if (bytes_of_encrypted_data[i]) {
|
|
AP4_Size out_size = bytes_of_encrypted_data[i];
|
|
m_Cipher->ProcessBuffer(in+bytes_of_cleartext_data[i],
|
|
bytes_of_encrypted_data[i],
|
|
out+bytes_of_cleartext_data[i],
|
|
&out_size, false);
|
|
|
|
// update the IV (last cipherblock emitted)
|
|
AP4_CopyMemory(m_Iv, out+bytes_of_cleartext_data[i]+bytes_of_encrypted_data[i]-16, 16);
|
|
}
|
|
|
|
// move the pointers
|
|
in += bytes_of_cleartext_data[i]+bytes_of_encrypted_data[i];
|
|
out += bytes_of_cleartext_data[i]+bytes_of_encrypted_data[i];
|
|
}
|
|
|
|
// encode the sample infos
|
|
unsigned int sample_info_count = bytes_of_cleartext_data.ItemCount();
|
|
sample_infos.SetDataSize(2+sample_info_count*6);
|
|
AP4_UI08* infos = sample_infos.UseData();
|
|
AP4_BytesFromUInt16BE(infos, sample_info_count);
|
|
for (unsigned int i=0; i<sample_info_count; i++) {
|
|
AP4_BytesFromUInt16BE(&infos[2+i*6], bytes_of_cleartext_data[i]);
|
|
AP4_BytesFromUInt32BE(&infos[2+i*6+2], bytes_of_encrypted_data[i]);
|
|
}
|
|
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencTrackEncrypter
|
|
+---------------------------------------------------------------------*/
|
|
class AP4_CencTrackEncrypter : public AP4_Processor::TrackHandler {
|
|
public:
|
|
// constructor
|
|
AP4_CencTrackEncrypter(AP4_CencVariant varient,
|
|
AP4_UI32 default_algorithm_id,
|
|
AP4_UI08 default_iv_size,
|
|
const AP4_UI08* default_kid,
|
|
AP4_SampleEntry* sample_entry,
|
|
AP4_UI32 format);
|
|
|
|
// methods
|
|
virtual AP4_Result ProcessTrack();
|
|
virtual AP4_Result ProcessSample(AP4_DataBuffer& data_in,
|
|
AP4_DataBuffer& data_out);
|
|
|
|
private:
|
|
// members
|
|
AP4_CencVariant m_Variant;
|
|
AP4_SampleEntry* m_SampleEntry;
|
|
AP4_UI32 m_Format;
|
|
AP4_UI32 m_DefaultAlgorithmId;
|
|
AP4_UI08 m_DefaultIvSize;
|
|
AP4_UI08 m_DefaultKid[16];
|
|
};
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencTrackEncrypter::AP4_CencTrackEncrypter
|
|
+---------------------------------------------------------------------*/
|
|
AP4_CencTrackEncrypter::AP4_CencTrackEncrypter(
|
|
AP4_CencVariant variant,
|
|
AP4_UI32 default_algorithm_id,
|
|
AP4_UI08 default_iv_size,
|
|
const AP4_UI08* default_kid,
|
|
AP4_SampleEntry* sample_entry,
|
|
AP4_UI32 format) :
|
|
m_Variant(variant),
|
|
m_SampleEntry(sample_entry),
|
|
m_Format(format),
|
|
m_DefaultAlgorithmId(default_algorithm_id),
|
|
m_DefaultIvSize(default_iv_size)
|
|
{
|
|
// copy the KID
|
|
AP4_CopyMemory(m_DefaultKid, default_kid, 16);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencTrackEncrypter::ProcessTrack
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_CencTrackEncrypter::ProcessTrack()
|
|
{
|
|
// original format
|
|
AP4_FrmaAtom* frma = new AP4_FrmaAtom(m_SampleEntry->GetType());
|
|
|
|
// scheme info
|
|
AP4_SchmAtom* schm = NULL;
|
|
AP4_Atom* tenc = NULL;
|
|
switch (m_Variant) {
|
|
case AP4_CENC_VARIANT_PIFF_CBC:
|
|
case AP4_CENC_VARIANT_PIFF_CTR:
|
|
schm = new AP4_SchmAtom(AP4_PROTECTION_SCHEME_TYPE_PIFF,
|
|
AP4_PROTECTION_SCHEME_VERSION_PIFF_11);
|
|
tenc = new AP4_PiffTrackEncryptionAtom(m_DefaultAlgorithmId,
|
|
m_DefaultIvSize,
|
|
m_DefaultKid);
|
|
break;
|
|
|
|
case AP4_CENC_VARIANT_MPEG:
|
|
schm = new AP4_SchmAtom(AP4_PROTECTION_SCHEME_TYPE_CENC,
|
|
AP4_PROTECTION_SCHEME_VERSION_CENC_10);
|
|
tenc = new AP4_TencAtom(m_DefaultAlgorithmId,
|
|
m_DefaultIvSize,
|
|
m_DefaultKid);
|
|
break;
|
|
}
|
|
|
|
// populate the schi container
|
|
AP4_ContainerAtom* schi = new AP4_ContainerAtom(AP4_ATOM_TYPE_SCHI);
|
|
schi->AddChild(tenc);
|
|
|
|
// populate the sinf container
|
|
AP4_ContainerAtom* sinf = new AP4_ContainerAtom(AP4_ATOM_TYPE_SINF);
|
|
sinf->AddChild(frma);
|
|
sinf->AddChild(schm);
|
|
sinf->AddChild(schi);
|
|
|
|
// add the sinf atom to the sample description
|
|
m_SampleEntry->AddChild(sinf);
|
|
|
|
// change the atom type of the sample description
|
|
m_SampleEntry->SetType(m_Format);
|
|
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencTrackEncrypter::ProcessSample
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_CencTrackEncrypter::ProcessSample(AP4_DataBuffer& data_in,
|
|
AP4_DataBuffer& data_out)
|
|
{
|
|
return data_out.SetData(data_in.GetData(), data_in.GetDataSize());
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencFragmentEncrypter
|
|
+---------------------------------------------------------------------*/
|
|
class AP4_CencFragmentEncrypter : public AP4_Processor::FragmentHandler {
|
|
public:
|
|
// constructor
|
|
AP4_CencFragmentEncrypter(AP4_CencVariant variant,
|
|
AP4_ContainerAtom* traf,
|
|
AP4_CencEncryptingProcessor::Encrypter* encrypter);
|
|
|
|
// methods
|
|
virtual AP4_Result ProcessFragment();
|
|
virtual AP4_Result ProcessSample(AP4_DataBuffer& data_in,
|
|
AP4_DataBuffer& data_out);
|
|
virtual AP4_Result PrepareForSamples(AP4_FragmentSampleTable* sample_table);
|
|
virtual AP4_Result FinishFragment();
|
|
|
|
private:
|
|
// members
|
|
AP4_CencVariant m_Variant;
|
|
AP4_ContainerAtom* m_Traf;
|
|
AP4_CencSampleEncryption* m_SampleEncryptionAtom;
|
|
AP4_SaizAtom* m_Saiz;
|
|
AP4_SaioAtom* m_Saio;
|
|
AP4_CencEncryptingProcessor::Encrypter* m_Encrypter;
|
|
};
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencFragmentEncrypter::AP4_CencFragmentEncrypter
|
|
+---------------------------------------------------------------------*/
|
|
AP4_CencFragmentEncrypter::AP4_CencFragmentEncrypter(AP4_CencVariant variant,
|
|
AP4_ContainerAtom* traf,
|
|
AP4_CencEncryptingProcessor::Encrypter* encrypter) :
|
|
m_Variant(variant),
|
|
m_Traf(traf),
|
|
m_SampleEncryptionAtom(NULL),
|
|
m_Saiz(NULL),
|
|
m_Saio(NULL),
|
|
m_Encrypter(encrypter)
|
|
{
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencFragmentEncrypter::ProcessFragment
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_CencFragmentEncrypter::ProcessFragment()
|
|
{
|
|
// create a sample encryption atom
|
|
m_SampleEncryptionAtom = NULL;
|
|
m_Saiz = NULL;
|
|
m_Saio = NULL;
|
|
switch (m_Variant) {
|
|
case AP4_CENC_VARIANT_PIFF_CBC:
|
|
case AP4_CENC_VARIANT_PIFF_CTR:
|
|
m_SampleEncryptionAtom = new AP4_PiffSampleEncryptionAtom();
|
|
break;
|
|
|
|
case AP4_CENC_VARIANT_MPEG:
|
|
m_SampleEncryptionAtom = new AP4_SencAtom();
|
|
m_Saiz = new AP4_SaizAtom();
|
|
m_Saio = new AP4_SaioAtom();
|
|
break;
|
|
|
|
default:
|
|
return AP4_ERROR_INTERNAL;
|
|
}
|
|
if (m_Encrypter->m_SampleEncrypter->UseSubSamples()) {
|
|
m_SampleEncryptionAtom->GetOuter().SetFlags(
|
|
m_SampleEncryptionAtom->GetOuter().GetFlags() |
|
|
AP4_CENC_SAMPLE_ENCRYPTION_FLAG_USE_SUB_SAMPLE_ENCRYPTION);
|
|
}
|
|
|
|
// this is mostly for testing: forces the clients to parse saio/saiz instead
|
|
// on relying on 'senc'
|
|
if (AP4_GlobalOptions::GetBool("cenc.no-senc")) {
|
|
m_SampleEncryptionAtom->GetOuter().SetType(AP4_ATOM_TYPE('s', 'e', 'n', 'C'));
|
|
}
|
|
|
|
// add the child atoms to the traf container
|
|
if (m_Saiz) m_Traf->AddChild(m_Saiz);
|
|
if (m_Saio) m_Traf->AddChild(m_Saio);
|
|
m_Traf->AddChild(&m_SampleEncryptionAtom->GetOuter());
|
|
|
|
// set the default-base-is-moof flag
|
|
AP4_TfhdAtom* tfhd = AP4_DYNAMIC_CAST(AP4_TfhdAtom, m_Traf->GetChild(AP4_ATOM_TYPE_TFHD));
|
|
if (tfhd) {
|
|
tfhd->SetFlags(tfhd->GetFlags() | AP4_TFHD_FLAG_DEFAULT_BASE_IS_MOOF);
|
|
}
|
|
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencFragmentEncrypter::PrepareForSamples
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_CencFragmentEncrypter::PrepareForSamples(AP4_FragmentSampleTable* sample_table) {
|
|
AP4_Cardinal sample_count = sample_table->GetSampleCount();
|
|
|
|
// resize the saio atom if we have one
|
|
if (m_Saio) {
|
|
m_Saio->AddEntry(0); // we'll compute the offset later
|
|
}
|
|
|
|
if (!m_Encrypter->m_SampleEncrypter->UseSubSamples()) {
|
|
m_SampleEncryptionAtom->SetSampleInfosSize(sample_count*m_SampleEncryptionAtom->GetIvSize());
|
|
if (m_Saiz) {
|
|
m_Saiz->SetDefaultSampleInfoSize(m_SampleEncryptionAtom->GetIvSize());
|
|
m_Saiz->SetSampleCount(sample_count);
|
|
}
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
// resize the saiz atom if we have one
|
|
if (m_Saiz) {
|
|
m_Saiz->SetSampleCount(sample_count);
|
|
}
|
|
|
|
// prepare for samples
|
|
AP4_Sample sample;
|
|
AP4_DataBuffer sample_data;
|
|
AP4_Array<AP4_UI16> bytes_of_cleartext_data;
|
|
AP4_Array<AP4_UI32> bytes_of_encrypted_data;
|
|
AP4_Size sample_info_size = sample_count*(m_SampleEncryptionAtom->GetIvSize()+2);
|
|
for (unsigned int i=0; i<sample_count; i++) {
|
|
AP4_Result result = sample_table->GetSample(i, sample);
|
|
if (AP4_FAILED(result)) return result;
|
|
result = sample.ReadData(sample_data);
|
|
if (AP4_FAILED(result)) return result;
|
|
bytes_of_cleartext_data.SetItemCount(0);
|
|
bytes_of_encrypted_data.SetItemCount(0);
|
|
result = m_Encrypter->m_SampleEncrypter->GetSubSampleMap(sample_data,
|
|
bytes_of_cleartext_data,
|
|
bytes_of_encrypted_data);
|
|
if (AP4_FAILED(result)) return result;
|
|
sample_info_size += 2+bytes_of_cleartext_data.ItemCount()*6;
|
|
|
|
if (m_Saiz) {
|
|
m_Saiz->SetSampleInfoSize(i, m_SampleEncryptionAtom->GetIvSize()+2+bytes_of_cleartext_data.ItemCount()*6);
|
|
}
|
|
}
|
|
m_SampleEncryptionAtom->SetSampleInfosSize(sample_info_size);
|
|
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencFragmentEncrypter::ProcessSample
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_CencFragmentEncrypter::ProcessSample(AP4_DataBuffer& data_in,
|
|
AP4_DataBuffer& data_out)
|
|
{
|
|
// copy the current IV
|
|
AP4_UI08 iv[16];
|
|
AP4_CopyMemory(iv, m_Encrypter->m_SampleEncrypter->GetIv(), 16);
|
|
|
|
// encrypt the sample
|
|
AP4_DataBuffer sample_infos;
|
|
AP4_Result result = m_Encrypter->m_SampleEncrypter->EncryptSampleData(data_in, data_out, sample_infos);
|
|
if (AP4_FAILED(result)) return result;
|
|
|
|
// update the sample info
|
|
m_SampleEncryptionAtom->AddSampleInfo(iv, sample_infos);
|
|
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencFragmentEncrypter::FinishFragment
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_CencFragmentEncrypter::FinishFragment()
|
|
{
|
|
if (!m_Saio) return AP4_SUCCESS;
|
|
|
|
// compute the saio offsets
|
|
AP4_ContainerAtom* moof = AP4_DYNAMIC_CAST(AP4_ContainerAtom, m_Traf->GetParent());
|
|
if (moof == NULL) return AP4_ERROR_INTERNAL;
|
|
|
|
AP4_UI64 traf_offset = moof->GetHeaderSize();
|
|
AP4_List<AP4_Atom>::Item* child = moof->GetChildren().FirstItem();
|
|
while (child) {
|
|
if (AP4_DYNAMIC_CAST(AP4_ContainerAtom, child->GetData()) == m_Traf) {
|
|
// NOTE: here we assume that the sample auxiliary info is stored
|
|
// in the 'senc' child atom of the traf, and that it is the last child
|
|
AP4_Atom* senc = m_Traf->GetChild(AP4_ATOM_TYPE_SENC);
|
|
if (senc == NULL) senc = m_Traf->GetChild(AP4_ATOM_TYPE('s', 'e', 'n', 'C')); // sometimes used instead of 'senc' for testing
|
|
if (senc) {
|
|
AP4_UI64 saio_offset = traf_offset+m_Traf->GetSize()-senc->GetSize()+AP4_FULL_ATOM_HEADER_SIZE+4;
|
|
m_Saio->SetEntry(0, saio_offset);
|
|
}
|
|
} else {
|
|
traf_offset += child->GetData()->GetSize();
|
|
}
|
|
child = child->GetNext();
|
|
}
|
|
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencEncryptingProcessor:AP4_CencEncryptingProcessor
|
|
+---------------------------------------------------------------------*/
|
|
AP4_CencEncryptingProcessor::AP4_CencEncryptingProcessor(AP4_CencVariant variant,
|
|
AP4_BlockCipherFactory* block_cipher_factory) :
|
|
m_Variant(variant)
|
|
{
|
|
// create a block cipher factory if none is given
|
|
if (block_cipher_factory == NULL) {
|
|
m_BlockCipherFactory = &AP4_DefaultBlockCipherFactory::Instance;
|
|
} else {
|
|
m_BlockCipherFactory = block_cipher_factory;
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencEncryptingProcessor:~AP4_CencEncryptingProcessor
|
|
+---------------------------------------------------------------------*/
|
|
AP4_CencEncryptingProcessor::~AP4_CencEncryptingProcessor()
|
|
{
|
|
m_Encrypters.DeleteReferences();
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencEncryptingProcessor::Initialize
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_CencEncryptingProcessor::Initialize(AP4_AtomParent& top_level,
|
|
AP4_ByteStream& /*stream*/,
|
|
AP4_Processor::ProgressListener* /*listener*/)
|
|
{
|
|
AP4_FtypAtom* ftyp = AP4_DYNAMIC_CAST(AP4_FtypAtom, top_level.GetChild(AP4_ATOM_TYPE_FTYP));
|
|
if (ftyp) {
|
|
// remove the atom, it will be replaced with a new one
|
|
top_level.RemoveChild(ftyp);
|
|
|
|
// keep the existing brand and compatible brands
|
|
AP4_Array<AP4_UI32> compatible_brands;
|
|
compatible_brands.EnsureCapacity(ftyp->GetCompatibleBrands().ItemCount()+1);
|
|
for (unsigned int i=0; i<ftyp->GetCompatibleBrands().ItemCount(); i++) {
|
|
compatible_brands.Append(ftyp->GetCompatibleBrands()[i]);
|
|
}
|
|
|
|
// add the compatible brand if it is not already there
|
|
if (m_Variant == AP4_CENC_VARIANT_PIFF_CBC || m_Variant == AP4_CENC_VARIANT_PIFF_CTR) {
|
|
if (!ftyp->HasCompatibleBrand(AP4_PIFF_BRAND)) {
|
|
compatible_brands.Append(AP4_PIFF_BRAND);
|
|
}
|
|
} else if (m_Variant == AP4_CENC_VARIANT_MPEG) {
|
|
if (!ftyp->HasCompatibleBrand(AP4_FILE_BRAND_ISO6)) {
|
|
compatible_brands.Append(AP4_FILE_BRAND_ISO6);
|
|
}
|
|
}
|
|
|
|
// create a replacement
|
|
AP4_FtypAtom* new_ftyp = new AP4_FtypAtom(ftyp->GetMajorBrand(),
|
|
ftyp->GetMinorVersion(),
|
|
&compatible_brands[0],
|
|
compatible_brands.ItemCount());
|
|
delete ftyp;
|
|
ftyp = new_ftyp;
|
|
} else {
|
|
AP4_UI32 compat = AP4_FILE_BRAND_ISO6;
|
|
if (m_Variant == AP4_CENC_VARIANT_PIFF_CBC || m_Variant == AP4_CENC_VARIANT_PIFF_CTR) {
|
|
compat = AP4_PIFF_BRAND;
|
|
}
|
|
ftyp = new AP4_FtypAtom(AP4_FTYP_BRAND_MP42, 0, &compat, 1);
|
|
}
|
|
|
|
// insert the ftyp atom as the first child
|
|
AP4_Result result = top_level.AddChild(ftyp, 0);
|
|
if (AP4_FAILED(result)) return result;
|
|
|
|
// check if we need to create a Marln 'mkid' table
|
|
AP4_MkidAtom* mkid = NULL;
|
|
if (m_Variant == AP4_CENC_VARIANT_MPEG) {
|
|
const AP4_List<AP4_TrackPropertyMap::Entry>& prop_entries = m_PropertyMap.GetEntries();
|
|
for (unsigned int i=0; i<prop_entries.ItemCount(); i++) {
|
|
AP4_TrackPropertyMap::Entry* entry = NULL;
|
|
prop_entries.Get(i, entry);
|
|
if (entry && entry->m_Name == "ContentId") {
|
|
if (mkid == NULL) mkid = new AP4_MkidAtom();
|
|
const char* kid_hex = m_PropertyMap.GetProperty(entry->m_TrackId, "KID");
|
|
|
|
// check that no other track has the same KID
|
|
bool duplicate = false;
|
|
for (unsigned int j=0; j<i; j++) {
|
|
AP4_TrackPropertyMap::Entry* entry2 = NULL;
|
|
prop_entries.Get(j, entry2);
|
|
if (entry2->m_Name == "KID" && entry2->m_Value == kid_hex && entry2->m_TrackId != entry->m_TrackId) {
|
|
duplicate = true;
|
|
break;
|
|
}
|
|
}
|
|
if (duplicate) continue;
|
|
if (kid_hex && AP4_StringLength(kid_hex) == 32) {
|
|
AP4_UI08 kid[16];
|
|
AP4_ParseHex(kid_hex, kid, 16);
|
|
mkid->AddEntry(kid, entry->m_Value.GetChars());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (mkid) {
|
|
// find the moov container
|
|
AP4_ContainerAtom* moov = AP4_DYNAMIC_CAST(AP4_ContainerAtom, top_level.GetChild(AP4_ATOM_TYPE_MOOV));
|
|
if (moov) {
|
|
// create a 'marl' container
|
|
AP4_ContainerAtom* marl = new AP4_ContainerAtom(AP4_ATOM_TYPE_MARL);
|
|
marl->AddChild(mkid);
|
|
|
|
// see if we need to pad the 'pssh' container
|
|
const char* padding_str = m_PropertyMap.GetProperty(0, "PsshPadding");
|
|
AP4_UI32 padding = 0;
|
|
if (padding_str) {
|
|
padding = AP4_ParseIntegerU(padding_str);
|
|
}
|
|
|
|
// create a 'pssh' atom to contain the 'marl' atom
|
|
AP4_PsshAtom* pssh = new AP4_PsshAtom(AP4_MARLIN_PSSH_SYSTEM_ID);
|
|
pssh->SetData(*marl);
|
|
if (padding > marl->GetSize()+32 && padding < 1024*1024) {
|
|
padding -= marl->GetSize()+32;
|
|
AP4_UI08* data = new AP4_UI08[padding];
|
|
AP4_SetMemory(data, 0, padding);
|
|
pssh->SetPadding(data, padding);
|
|
delete[] data;
|
|
}
|
|
|
|
// add the 'pssh' atom at the end of the 'moov' container
|
|
// but before any 'free' atom, if any
|
|
int position = -1;
|
|
int current = 0;
|
|
for (AP4_List<AP4_Atom>::Item* child = moov->GetChildren().FirstItem();
|
|
child;
|
|
child = child->GetNext(), ++current) {
|
|
if (child->GetData()->GetType() == AP4_ATOM_TYPE_FREE) {
|
|
position = current;
|
|
}
|
|
}
|
|
moov->AddChild(pssh, position);
|
|
}
|
|
}
|
|
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencEncryptingProcessor:CreateTrackHandler
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Processor::TrackHandler*
|
|
AP4_CencEncryptingProcessor::CreateTrackHandler(AP4_TrakAtom* trak)
|
|
{
|
|
// find the stsd atom
|
|
AP4_StsdAtom* stsd = AP4_DYNAMIC_CAST(AP4_StsdAtom, trak->FindChild("mdia/minf/stbl/stsd"));
|
|
|
|
// avoid tracks with no stsd atom (should not happen)
|
|
if (stsd == NULL) return NULL;
|
|
|
|
// only look at the first sample description
|
|
AP4_SampleEntry* entry = stsd->GetSampleEntry(0);
|
|
if (entry == NULL) return NULL;
|
|
|
|
// create a handler for this track if we have a key for it and we know
|
|
// how to map the type
|
|
const AP4_DataBuffer* key;
|
|
const AP4_DataBuffer* iv;
|
|
if (AP4_FAILED(m_KeyMap.GetKeyAndIv(trak->GetId(), key, iv))) {
|
|
return NULL;
|
|
}
|
|
if (iv == NULL || iv->GetDataSize() != 16) {
|
|
return NULL;
|
|
}
|
|
|
|
AP4_UI32 format = 0;
|
|
switch (entry->GetType()) {
|
|
case AP4_ATOM_TYPE_MP4A:
|
|
format = AP4_ATOM_TYPE_ENCA;
|
|
break;
|
|
|
|
case AP4_ATOM_TYPE_MP4V:
|
|
case AP4_ATOM_TYPE_AVC1:
|
|
format = AP4_ATOM_TYPE_ENCV;
|
|
break;
|
|
|
|
default: {
|
|
// try to find if this is audio or video
|
|
AP4_HdlrAtom* hdlr = AP4_DYNAMIC_CAST(AP4_HdlrAtom, trak->FindChild("mdia/hdlr"));
|
|
if (hdlr) {
|
|
switch (hdlr->GetHandlerType()) {
|
|
case AP4_HANDLER_TYPE_SOUN:
|
|
format = AP4_ATOM_TYPE_ENCA;
|
|
break;
|
|
|
|
case AP4_HANDLER_TYPE_VIDE:
|
|
format = AP4_ATOM_TYPE_ENCV;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (format == 0) return NULL;
|
|
|
|
// get the track properties
|
|
AP4_UI08 kid[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
|
|
const char* kid_hex = m_PropertyMap.GetProperty(trak->GetId(), "KID");
|
|
if (kid_hex && AP4_StringLength(kid_hex) == 32) {
|
|
AP4_ParseHex(kid_hex, kid, 16);
|
|
}
|
|
|
|
// create the encrypter
|
|
AP4_Processor::TrackHandler* track_encrypter;
|
|
AP4_UI32 algorithm_id = 0;
|
|
switch (m_Variant) {
|
|
case AP4_CENC_VARIANT_PIFF_CTR:
|
|
case AP4_CENC_VARIANT_MPEG:
|
|
algorithm_id = AP4_CENC_ALGORITHM_ID_CTR;
|
|
break;
|
|
|
|
case AP4_CENC_VARIANT_PIFF_CBC:
|
|
algorithm_id = AP4_CENC_ALGORITHM_ID_CBC;
|
|
break;
|
|
|
|
default:
|
|
return NULL;
|
|
}
|
|
track_encrypter = new AP4_CencTrackEncrypter(m_Variant,
|
|
algorithm_id,
|
|
16,
|
|
kid,
|
|
entry,
|
|
format);
|
|
|
|
// create a block cipher
|
|
AP4_BlockCipher* block_cipher = NULL;
|
|
AP4_BlockCipher::CipherMode mode;
|
|
AP4_BlockCipher::CtrParams ctr_params;
|
|
const void* mode_params = NULL;
|
|
switch (algorithm_id) {
|
|
case AP4_CENC_ALGORITHM_ID_CBC:
|
|
mode = AP4_BlockCipher::CBC;
|
|
break;
|
|
|
|
case AP4_CENC_ALGORITHM_ID_CTR:
|
|
mode = AP4_BlockCipher::CTR;
|
|
ctr_params.counter_size = 8;
|
|
mode_params = &ctr_params;
|
|
break;
|
|
|
|
default: return NULL;
|
|
}
|
|
AP4_Result result = m_BlockCipherFactory->CreateCipher(AP4_BlockCipher::AES_128,
|
|
AP4_BlockCipher::ENCRYPT,
|
|
mode,
|
|
mode_params,
|
|
key->GetData(),
|
|
key->GetDataSize(),
|
|
block_cipher);
|
|
if (AP4_FAILED(result)) return NULL;
|
|
|
|
// add a new cipher state for this track
|
|
AP4_CencSampleEncrypter* sample_encrypter = NULL;
|
|
AP4_StreamCipher* stream_cipher = NULL;
|
|
switch (algorithm_id) {
|
|
case AP4_CENC_ALGORITHM_ID_CBC:
|
|
stream_cipher = new AP4_CbcStreamCipher(block_cipher);
|
|
if (entry->GetType() == AP4_ATOM_TYPE_AVC1) {
|
|
AP4_AvccAtom* avcc = AP4_DYNAMIC_CAST(AP4_AvccAtom, entry->GetChild(AP4_ATOM_TYPE_AVCC));
|
|
if (avcc == NULL) return NULL;
|
|
sample_encrypter = new AP4_CencCbcSubSampleEncrypter(stream_cipher, avcc->GetNaluLengthSize());
|
|
} else {
|
|
sample_encrypter = new AP4_CencCbcSampleEncrypter(stream_cipher);
|
|
}
|
|
break;
|
|
|
|
case AP4_CENC_ALGORITHM_ID_CTR:
|
|
stream_cipher = new AP4_CtrStreamCipher(block_cipher, 16);
|
|
if (entry->GetType() == AP4_ATOM_TYPE_AVC1) {
|
|
AP4_AvccAtom* avcc = AP4_DYNAMIC_CAST(AP4_AvccAtom, entry->GetChild(AP4_ATOM_TYPE_AVCC));
|
|
if (avcc == NULL) return NULL;
|
|
sample_encrypter = new AP4_CencCtrSubSampleEncrypter(stream_cipher, avcc->GetNaluLengthSize());
|
|
} else {
|
|
sample_encrypter = new AP4_CencCtrSampleEncrypter(stream_cipher);
|
|
}
|
|
break;
|
|
}
|
|
sample_encrypter->SetIv(iv->GetData());
|
|
m_Encrypters.Add(new Encrypter(trak->GetId(), sample_encrypter));
|
|
|
|
return track_encrypter;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencEncryptingProcessor:CreateFragmentHandler
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Processor::FragmentHandler*
|
|
AP4_CencEncryptingProcessor::CreateFragmentHandler(AP4_ContainerAtom* traf,
|
|
AP4_ByteStream& /* moof_data */,
|
|
AP4_Position /* moof_offset */)
|
|
{
|
|
// get the traf header (tfhd) for this track fragment so we can get the track ID
|
|
AP4_TfhdAtom* tfhd = AP4_DYNAMIC_CAST(AP4_TfhdAtom, traf->GetChild(AP4_ATOM_TYPE_TFHD));
|
|
if (tfhd == NULL) return NULL;
|
|
|
|
// lookup the encrypter for this track
|
|
Encrypter* encrypter = NULL;
|
|
for (AP4_List<Encrypter>::Item* item = m_Encrypters.FirstItem();
|
|
item;
|
|
item = item->GetNext()) {
|
|
if (item->GetData()->m_TrackId == tfhd->GetTrackId()) {
|
|
encrypter = item->GetData();
|
|
break;
|
|
}
|
|
}
|
|
if (encrypter == NULL) return NULL;
|
|
return new AP4_CencFragmentEncrypter(m_Variant, traf, encrypter);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencSampleDecrypter::Create
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_CencSampleDecrypter::Create(AP4_ProtectedSampleDescription* sample_description,
|
|
AP4_ContainerAtom* traf,
|
|
AP4_ByteStream& aux_info_data,
|
|
AP4_Position aux_info_data_offset,
|
|
const AP4_UI08* key,
|
|
AP4_Size key_size,
|
|
AP4_BlockCipherFactory* block_cipher_factory,
|
|
AP4_CencSampleDecrypter*& decrypter)
|
|
{
|
|
AP4_SaioAtom* saio = NULL;
|
|
AP4_SaizAtom* saiz = NULL;
|
|
AP4_CencSampleEncryption* sample_encryption_atom = NULL;
|
|
return Create(sample_description,
|
|
traf,
|
|
aux_info_data,
|
|
aux_info_data_offset,
|
|
key,
|
|
key_size,
|
|
block_cipher_factory,
|
|
saio,
|
|
saiz,
|
|
sample_encryption_atom,
|
|
decrypter);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencSampleDecrypter::Create
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_CencSampleDecrypter::Create(AP4_ProtectedSampleDescription* sample_description,
|
|
AP4_ContainerAtom* traf,
|
|
AP4_ByteStream& aux_info_data,
|
|
AP4_Position aux_info_data_offset,
|
|
const AP4_UI08* key,
|
|
AP4_Size key_size,
|
|
AP4_BlockCipherFactory* block_cipher_factory,
|
|
AP4_SaioAtom*& saio,
|
|
AP4_SaizAtom*& saiz,
|
|
AP4_CencSampleEncryption*& sample_encryption_atom,
|
|
AP4_CencSampleDecrypter*& decrypter)
|
|
{
|
|
// default return values
|
|
saio = NULL;
|
|
saiz = NULL;
|
|
sample_encryption_atom = NULL;
|
|
decrypter = NULL;
|
|
|
|
// create the sample info table
|
|
AP4_CencSampleInfoTable* sample_info_table = NULL;
|
|
AP4_UI32 algorithm_id = 0;
|
|
AP4_Result result = AP4_CencSampleInfoTable::Create(sample_description,
|
|
traf,
|
|
saio,
|
|
saiz,
|
|
sample_encryption_atom,
|
|
algorithm_id,
|
|
aux_info_data,
|
|
aux_info_data_offset,
|
|
sample_info_table);
|
|
if (AP4_FAILED(result)) {
|
|
return result;
|
|
}
|
|
|
|
// we now have all the info we need to create the decrypter
|
|
return Create(sample_info_table, algorithm_id, key, key_size, block_cipher_factory, decrypter);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencSampleDecrypter::Create
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_CencSampleDecrypter::Create(AP4_CencSampleInfoTable* sample_info_table,
|
|
AP4_UI32 algorithm_id,
|
|
const AP4_UI08* key,
|
|
AP4_Size key_size,
|
|
AP4_BlockCipherFactory* block_cipher_factory,
|
|
AP4_CencSampleDecrypter*& decrypter)
|
|
{
|
|
// check the parameters
|
|
if (key == NULL) return AP4_ERROR_INVALID_PARAMETERS;
|
|
|
|
// use the default cipher factory if none was passed
|
|
if (block_cipher_factory == NULL) {
|
|
block_cipher_factory = &AP4_DefaultBlockCipherFactory::Instance;
|
|
}
|
|
|
|
// create the cipher
|
|
AP4_StreamCipher* stream_cipher = NULL;
|
|
bool full_blocks_only = false;
|
|
unsigned int iv_size = sample_info_table->GetIvSize();
|
|
switch (algorithm_id) {
|
|
case AP4_CENC_ALGORITHM_ID_NONE:
|
|
break;
|
|
|
|
case AP4_CENC_ALGORITHM_ID_CTR:
|
|
if (iv_size != 8 && iv_size != 16) {
|
|
return AP4_ERROR_INVALID_FORMAT;
|
|
} else {
|
|
// create the block cipher
|
|
AP4_BlockCipher* block_cipher = NULL;
|
|
AP4_BlockCipher::CtrParams ctr_params;
|
|
ctr_params.counter_size = iv_size;
|
|
AP4_Result result = block_cipher_factory->CreateCipher(AP4_BlockCipher::AES_128,
|
|
AP4_BlockCipher::DECRYPT,
|
|
AP4_BlockCipher::CTR,
|
|
&ctr_params,
|
|
key,
|
|
key_size,
|
|
block_cipher);
|
|
if (AP4_FAILED(result)) return result;
|
|
|
|
// create the stream cipher
|
|
stream_cipher = new AP4_CtrStreamCipher(block_cipher, 8);
|
|
}
|
|
break;
|
|
|
|
case AP4_CENC_ALGORITHM_ID_CBC:
|
|
if (iv_size != 16) {
|
|
return AP4_ERROR_INVALID_FORMAT;
|
|
} else {
|
|
// create the block cipher
|
|
AP4_BlockCipher* block_cipher = NULL;
|
|
AP4_Result result = block_cipher_factory->CreateCipher(AP4_BlockCipher::AES_128,
|
|
AP4_BlockCipher::DECRYPT,
|
|
AP4_BlockCipher::CBC,
|
|
NULL,
|
|
key,
|
|
key_size,
|
|
block_cipher);
|
|
if (AP4_FAILED(result)) return result;
|
|
|
|
// create the stream cipher
|
|
stream_cipher = new AP4_CbcStreamCipher(block_cipher);
|
|
|
|
// with CBC, we only use full blocks
|
|
full_blocks_only = true;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return AP4_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
// create the decrypter
|
|
decrypter = new AP4_CencSampleDecrypter(stream_cipher,
|
|
full_blocks_only,
|
|
sample_info_table);
|
|
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencSampleDecrypter::~AP4_CencSampleDecrypter
|
|
+---------------------------------------------------------------------*/
|
|
AP4_CencSampleDecrypter::~AP4_CencSampleDecrypter()
|
|
{
|
|
delete m_SampleInfoTable;
|
|
delete m_Cipher;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencSampleDecrypter::SetSampleIndex
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_CencSampleDecrypter::SetSampleIndex(AP4_Ordinal sample_index)
|
|
{
|
|
m_SampleCursor = sample_index;
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencSampleDecrypter::DecryptSampleData
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_CencSampleDecrypter::DecryptSampleData(AP4_DataBuffer& data_in,
|
|
AP4_DataBuffer& data_out,
|
|
const AP4_UI08* iv)
|
|
{
|
|
// the output has the same size as the input
|
|
data_out.SetDataSize(data_in.GetDataSize());
|
|
|
|
// shortcut for NULL ciphers
|
|
if (m_Cipher == NULL) {
|
|
AP4_CopyMemory(data_out.UseData(), data_in.GetData(), data_in.GetDataSize());
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
// setup direct pointers to the buffers
|
|
const AP4_UI08* in = data_in.GetData();
|
|
AP4_UI08* out = data_out.UseData();
|
|
|
|
// setup the IV
|
|
unsigned int sample_cursor = m_SampleCursor++;
|
|
unsigned char iv_block[16];
|
|
if (iv == NULL) {
|
|
iv = m_SampleInfoTable->GetIv(sample_cursor);
|
|
}
|
|
if (iv == NULL) return AP4_ERROR_INVALID_FORMAT;
|
|
unsigned int iv_size = m_SampleInfoTable->GetIvSize();
|
|
AP4_CopyMemory(iv_block, iv, iv_size);
|
|
if (iv_size != 16) AP4_SetMemory(&iv_block[iv_size], 0, 16-iv_size);
|
|
m_Cipher->SetIV(iv_block);
|
|
|
|
if (m_SampleInfoTable->HasSubSampleInfo()) {
|
|
// process the sample data, one sub-sample at a time
|
|
const AP4_UI08* in_end = data_in.GetData()+data_in.GetDataSize();
|
|
unsigned int subsample_count = m_SampleInfoTable->GetSubsampleCount(sample_cursor);
|
|
for (unsigned int i=0; i<subsample_count; i++) {
|
|
AP4_UI16 cleartext_size = 0;
|
|
AP4_UI32 encrypted_size = 0;
|
|
AP4_Result result = m_SampleInfoTable->GetSubsampleInfo(sample_cursor, i, cleartext_size, encrypted_size);
|
|
if (AP4_FAILED(result)) return AP4_ERROR_INVALID_FORMAT;
|
|
if ((unsigned int)(in_end-in) < cleartext_size+encrypted_size) {
|
|
return AP4_ERROR_INVALID_FORMAT;
|
|
}
|
|
// copy the cleartext portion
|
|
if (cleartext_size) {
|
|
AP4_CopyMemory(out, in, cleartext_size);
|
|
}
|
|
|
|
// decrypt the rest
|
|
if (encrypted_size) {
|
|
AP4_Result result = m_Cipher->ProcessBuffer(in+cleartext_size, encrypted_size, out+cleartext_size, &encrypted_size, false);
|
|
if (AP4_FAILED(result)) return result;
|
|
}
|
|
|
|
// move the pointers
|
|
in += cleartext_size+encrypted_size;
|
|
out += cleartext_size+encrypted_size;
|
|
}
|
|
} else {
|
|
if (m_FullBlocksOnly) {
|
|
unsigned int block_count = data_in.GetDataSize()/16;
|
|
if (block_count) {
|
|
AP4_Size out_size = data_out.GetDataSize();
|
|
AP4_Result result = m_Cipher->ProcessBuffer(in, block_count*16, out, &out_size, false);
|
|
if (AP4_FAILED(result)) return result;
|
|
AP4_ASSERT(out_size == block_count*16);
|
|
in += block_count*16;
|
|
out += block_count*16;
|
|
}
|
|
|
|
// any partial block at the end remains in the clear
|
|
unsigned int partial = data_in.GetDataSize()%16;
|
|
if (partial) {
|
|
AP4_CopyMemory(out, in, partial);
|
|
}
|
|
} else {
|
|
// process the entire sample data at once
|
|
AP4_Size encrypted_size = data_in.GetDataSize();
|
|
AP4_Result result = m_Cipher->ProcessBuffer(in, encrypted_size, out, &encrypted_size, false);
|
|
if (AP4_FAILED(result)) return result;
|
|
}
|
|
}
|
|
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencTrackDecrypter
|
|
+---------------------------------------------------------------------*/
|
|
class AP4_CencTrackDecrypter : public AP4_Processor::TrackHandler {
|
|
public:
|
|
AP4_IMPLEMENT_DYNAMIC_CAST(AP4_CencTrackDecrypter)
|
|
|
|
// constructor
|
|
static AP4_Result Create(const unsigned char* key,
|
|
AP4_Size key_size,
|
|
AP4_ProtectedSampleDescription* sample_description,
|
|
AP4_SampleEntry* sample_entry,
|
|
AP4_CencTrackDecrypter*& decrypter);
|
|
|
|
// methods
|
|
virtual AP4_Result ProcessSample(AP4_DataBuffer& data_in,
|
|
AP4_DataBuffer& data_out);
|
|
virtual AP4_Result ProcessTrack();
|
|
|
|
// accessors
|
|
AP4_ProtectedSampleDescription* GetSampleDescription() {
|
|
return m_SampleDescription;
|
|
}
|
|
|
|
private:
|
|
// constructor
|
|
AP4_CencTrackDecrypter(AP4_ProtectedSampleDescription* sample_description,
|
|
AP4_SampleEntry* sample_entry,
|
|
AP4_UI32 original_format);
|
|
|
|
// members
|
|
AP4_ProtectedSampleDescription* m_SampleDescription;
|
|
AP4_SampleEntry* m_SampleEntry;
|
|
AP4_UI32 m_OriginalFormat;
|
|
};
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencTrackDecrypter Dynamic Cast Anchor
|
|
+---------------------------------------------------------------------*/
|
|
AP4_DEFINE_DYNAMIC_CAST_ANCHOR(AP4_CencTrackDecrypter)
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencTrackDecrypter::Create
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_CencTrackDecrypter::Create(const unsigned char* key,
|
|
AP4_Size /* key_size */,
|
|
AP4_ProtectedSampleDescription* sample_description,
|
|
AP4_SampleEntry* sample_entry,
|
|
AP4_CencTrackDecrypter*& decrypter)
|
|
{
|
|
decrypter = NULL;
|
|
|
|
// check and set defaults
|
|
if (key == NULL) {
|
|
return AP4_ERROR_INVALID_PARAMETERS;
|
|
}
|
|
|
|
// instantiate the object
|
|
decrypter = new AP4_CencTrackDecrypter(sample_description,
|
|
sample_entry,
|
|
sample_description->GetOriginalFormat());
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencTrackDecrypter::AP4_CencTrackDecrypter
|
|
+---------------------------------------------------------------------*/
|
|
AP4_CencTrackDecrypter::AP4_CencTrackDecrypter(AP4_ProtectedSampleDescription* sample_description,
|
|
AP4_SampleEntry* sample_entry,
|
|
AP4_UI32 original_format) :
|
|
m_SampleDescription(sample_description),
|
|
m_SampleEntry(sample_entry),
|
|
m_OriginalFormat(original_format)
|
|
{
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencTrackDecrypter::ProcessSample
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_CencTrackDecrypter::ProcessSample(AP4_DataBuffer& data_in,
|
|
AP4_DataBuffer& data_out)
|
|
{
|
|
data_out.SetData(data_in.GetData(), data_in.GetDataSize());
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencTrackDecrypter::ProcessTrack
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_CencTrackDecrypter::ProcessTrack()
|
|
{
|
|
m_SampleEntry->SetType(m_OriginalFormat);
|
|
m_SampleEntry->DeleteChild(AP4_ATOM_TYPE_SINF);
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencFragmentDecrypter
|
|
+---------------------------------------------------------------------*/
|
|
class AP4_CencFragmentDecrypter : public AP4_Processor::FragmentHandler {
|
|
public:
|
|
// constructor
|
|
AP4_CencFragmentDecrypter(AP4_CencSampleDecrypter* sample_decrypter,
|
|
AP4_SaioAtom* saio_atom,
|
|
AP4_SaizAtom* saiz_atom,
|
|
AP4_CencSampleEncryption* sample_encryption_atom) :
|
|
m_SampleDecrypter(sample_decrypter),
|
|
m_SaioAtom(saio_atom),
|
|
m_SaizAtom(saiz_atom),
|
|
m_SampleEncryptionAtom(sample_encryption_atom) {}
|
|
|
|
// methods
|
|
virtual AP4_Result ProcessFragment();
|
|
virtual AP4_Result FinishFragment();
|
|
virtual AP4_Result ProcessSample(AP4_DataBuffer& data_in,
|
|
AP4_DataBuffer& data_out);
|
|
|
|
private:
|
|
// members
|
|
AP4_CencSampleDecrypter* m_SampleDecrypter;
|
|
AP4_SaioAtom* m_SaioAtom;
|
|
AP4_SaizAtom* m_SaizAtom;
|
|
AP4_CencSampleEncryption* m_SampleEncryptionAtom;
|
|
};
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencFragmentDecrypter::ProcessFragment
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_CencFragmentDecrypter::ProcessFragment()
|
|
{
|
|
// detach the sample encryption atom
|
|
if (m_SampleDecrypter) {
|
|
if (m_SaioAtom) m_SaioAtom->Detach();
|
|
if (m_SaizAtom) m_SaizAtom->Detach();
|
|
if (m_SampleEncryptionAtom) m_SampleEncryptionAtom->GetOuter().Detach();
|
|
}
|
|
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencFragmentDecrypter::FinishFragment
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_CencFragmentDecrypter::FinishFragment()
|
|
{
|
|
if (m_SampleDecrypter) {
|
|
delete m_SaioAtom;
|
|
m_SaioAtom = NULL;
|
|
delete m_SaizAtom;
|
|
m_SaizAtom = NULL;
|
|
delete m_SampleEncryptionAtom;
|
|
m_SampleEncryptionAtom = NULL;
|
|
}
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencFragmentDecrypter::ProcessSample
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_CencFragmentDecrypter::ProcessSample(AP4_DataBuffer& data_in,
|
|
AP4_DataBuffer& data_out)
|
|
{
|
|
// decrypt the sample
|
|
return m_SampleDecrypter->DecryptSampleData(data_in, data_out, NULL);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencDecryptingProcessor::AP4_CencDecryptingProcessor
|
|
+---------------------------------------------------------------------*/
|
|
AP4_CencDecryptingProcessor::AP4_CencDecryptingProcessor(const AP4_ProtectionKeyMap* key_map,
|
|
AP4_BlockCipherFactory* block_cipher_factory) :
|
|
m_KeyMap(key_map)
|
|
{
|
|
if (block_cipher_factory) {
|
|
m_BlockCipherFactory = block_cipher_factory;
|
|
} else {
|
|
m_BlockCipherFactory = &AP4_DefaultBlockCipherFactory::Instance;
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencDecryptingProcessor:CreateTrackHandler
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Processor::TrackHandler*
|
|
AP4_CencDecryptingProcessor::CreateTrackHandler(AP4_TrakAtom* trak)
|
|
{
|
|
// find the stsd atom
|
|
AP4_StsdAtom* stsd = AP4_DYNAMIC_CAST(AP4_StsdAtom, trak->FindChild("mdia/minf/stbl/stsd"));
|
|
|
|
// avoid tracks with no stsd atom (should not happen)
|
|
if (stsd == NULL) return NULL;
|
|
|
|
// we only look at the first sample description
|
|
AP4_SampleDescription* desc = stsd->GetSampleDescription(0);
|
|
AP4_SampleEntry* entry = stsd->GetSampleEntry(0);
|
|
if (desc == NULL || entry == NULL) return NULL;
|
|
if (m_KeyMap == NULL) return NULL;
|
|
if (desc->GetType() == AP4_SampleDescription::TYPE_PROTECTED) {
|
|
// create a handler for this track
|
|
AP4_ProtectedSampleDescription* protected_desc =
|
|
static_cast<AP4_ProtectedSampleDescription*>(desc);
|
|
if (protected_desc->GetSchemeType() == AP4_PROTECTION_SCHEME_TYPE_PIFF ||
|
|
protected_desc->GetSchemeType() == AP4_PROTECTION_SCHEME_TYPE_CENC) {
|
|
const AP4_DataBuffer* key = m_KeyMap->GetKey(trak->GetId());
|
|
if (key) {
|
|
AP4_CencTrackDecrypter* handler = NULL;
|
|
AP4_Result result = AP4_CencTrackDecrypter::Create(key->GetData(),
|
|
key->GetDataSize(),
|
|
protected_desc,
|
|
entry,
|
|
handler);
|
|
if (AP4_FAILED(result)) return NULL;
|
|
return handler;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencDecryptingProcessor::CreateFragmentHandler
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Processor::FragmentHandler*
|
|
AP4_CencDecryptingProcessor::CreateFragmentHandler(AP4_ContainerAtom* traf,
|
|
AP4_ByteStream& moof_data,
|
|
AP4_Position moof_offset)
|
|
{
|
|
// find the matching track handler to get to the track sample description
|
|
const AP4_DataBuffer* key = NULL;
|
|
AP4_ProtectedSampleDescription* sample_description = NULL;
|
|
for (unsigned int i=0; i<m_TrackIds.ItemCount(); i++) {
|
|
AP4_TfhdAtom* tfhd = AP4_DYNAMIC_CAST(AP4_TfhdAtom, traf->GetChild(AP4_ATOM_TYPE_TFHD));
|
|
if (tfhd && m_TrackIds[i] == tfhd->GetTrackId()) {
|
|
// look for the Track Encryption Box
|
|
AP4_CencTrackDecrypter* track_decrypter =
|
|
AP4_DYNAMIC_CAST(AP4_CencTrackDecrypter, m_TrackHandlers[i]);
|
|
if (track_decrypter) {
|
|
sample_description = track_decrypter->GetSampleDescription();
|
|
}
|
|
|
|
// get the matching key
|
|
key = m_KeyMap->GetKey(tfhd->GetTrackId());
|
|
|
|
break;
|
|
}
|
|
}
|
|
if (sample_description == NULL) return NULL;
|
|
|
|
// create the sample decrypter for the fragment
|
|
AP4_CencSampleDecrypter* sample_decrypter = NULL;
|
|
AP4_SaioAtom* saio = NULL;
|
|
AP4_SaizAtom* saiz = NULL;
|
|
AP4_CencSampleEncryption* sample_encryption_atom = NULL;
|
|
AP4_Result result = AP4_CencSampleDecrypter::Create(
|
|
sample_description,
|
|
traf,
|
|
moof_data,
|
|
moof_offset,
|
|
key->GetData(),
|
|
key->GetDataSize(),
|
|
NULL,
|
|
saio,
|
|
saiz,
|
|
sample_encryption_atom,
|
|
sample_decrypter);
|
|
if (AP4_FAILED(result)) return NULL;
|
|
|
|
return new AP4_CencFragmentDecrypter(sample_decrypter, saio, saiz, sample_encryption_atom);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencTrackEncryption Dynamic Cast Anchor
|
|
+---------------------------------------------------------------------*/
|
|
AP4_DEFINE_DYNAMIC_CAST_ANCHOR(AP4_CencTrackEncryption)
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencTrackEncryption::AP4_CencTrackEncryption
|
|
+---------------------------------------------------------------------*/
|
|
AP4_CencTrackEncryption::AP4_CencTrackEncryption() :
|
|
m_DefaultAlgorithmId(0),
|
|
m_DefaultIvSize(0)
|
|
{
|
|
AP4_SetMemory(m_DefaultKid, 0, 16);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencTrackEncryption::AP4_CencTrackEncryption
|
|
+---------------------------------------------------------------------*/
|
|
AP4_CencTrackEncryption::AP4_CencTrackEncryption(AP4_UI32 default_algorithm_id,
|
|
AP4_UI08 default_iv_size,
|
|
const AP4_UI08* default_kid) :
|
|
m_DefaultAlgorithmId(default_algorithm_id),
|
|
m_DefaultIvSize(default_iv_size)
|
|
{
|
|
AP4_CopyMemory(m_DefaultKid, default_kid, 16);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencTrackEncryption::AP4_CencTrackEncryption
|
|
+---------------------------------------------------------------------*/
|
|
AP4_CencTrackEncryption::AP4_CencTrackEncryption(AP4_ByteStream& stream)
|
|
{
|
|
stream.ReadUI24(m_DefaultAlgorithmId);
|
|
stream.ReadUI08(m_DefaultIvSize);
|
|
AP4_SetMemory(m_DefaultKid, 0, 16);
|
|
stream.Read(m_DefaultKid, 16);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencTrackEncryption::DoInspectFields
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_CencTrackEncryption::DoInspectFields(AP4_AtomInspector& inspector)
|
|
{
|
|
inspector.AddField("default_AlgorithmID", m_DefaultAlgorithmId);
|
|
inspector.AddField("default_IV_size", m_DefaultIvSize);
|
|
inspector.AddField("default_KID", m_DefaultKid, 16);
|
|
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencTrackEncryption::DoWriteFields
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_CencTrackEncryption::DoWriteFields(AP4_ByteStream& stream)
|
|
{
|
|
AP4_Result result;
|
|
|
|
// write the fields
|
|
result = stream.WriteUI24(m_DefaultAlgorithmId);
|
|
if (AP4_FAILED(result)) return result;
|
|
result = stream.WriteUI08(m_DefaultIvSize);
|
|
if (AP4_FAILED(result)) return result;
|
|
result = stream.Write(m_DefaultKid, 16);
|
|
if (AP4_FAILED(result)) return result;
|
|
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencSampleEncryption Dynamic Cast Anchor
|
|
+---------------------------------------------------------------------*/
|
|
AP4_DEFINE_DYNAMIC_CAST_ANCHOR(AP4_CencSampleEncryption)
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencSampleInfoTable::Create
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_CencSampleInfoTable::Create(AP4_ProtectedSampleDescription* sample_description,
|
|
AP4_ContainerAtom* traf,
|
|
AP4_UI32& algorithm_id,
|
|
AP4_ByteStream& aux_info_data,
|
|
AP4_Position aux_info_data_offset,
|
|
AP4_CencSampleInfoTable*& sample_info_table)
|
|
{
|
|
AP4_SaioAtom* saio;
|
|
AP4_SaizAtom* saiz;
|
|
AP4_CencSampleEncryption* sample_encryption_atom;
|
|
return Create(sample_description,
|
|
traf,
|
|
saio,
|
|
saiz,
|
|
sample_encryption_atom,
|
|
algorithm_id,
|
|
aux_info_data,
|
|
aux_info_data_offset,
|
|
sample_info_table);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencSampleInfoTable::Create
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_CencSampleInfoTable::Create(AP4_ProtectedSampleDescription* sample_description,
|
|
AP4_ContainerAtom* traf,
|
|
AP4_SaioAtom*& saio,
|
|
AP4_SaizAtom*& saiz,
|
|
AP4_CencSampleEncryption*& sample_encryption_atom,
|
|
AP4_UI32& algorithm_id,
|
|
AP4_ByteStream& aux_info_data,
|
|
AP4_Position aux_info_data_offset,
|
|
AP4_CencSampleInfoTable*& sample_info_table)
|
|
{
|
|
// default return values
|
|
saio = NULL;
|
|
saiz = NULL;
|
|
sample_encryption_atom = NULL;
|
|
sample_info_table = NULL;
|
|
unsigned int iv_size = 0;
|
|
|
|
// check the scheme
|
|
if (sample_description->GetSchemeType() == AP4_PROTECTION_SCHEME_TYPE_PIFF) {
|
|
// we don't support PIFF 1.0 anymore!
|
|
if (sample_description->GetSchemeVersion() != AP4_PROTECTION_SCHEME_VERSION_PIFF_11) {
|
|
return AP4_ERROR_NOT_SUPPORTED;
|
|
}
|
|
} else if (sample_description->GetSchemeType() == AP4_PROTECTION_SCHEME_TYPE_CENC) {
|
|
if (sample_description->GetSchemeVersion() != AP4_PROTECTION_SCHEME_VERSION_CENC_10) {
|
|
return AP4_ERROR_NOT_SUPPORTED;
|
|
}
|
|
} else {
|
|
return AP4_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
// get the scheme info atom
|
|
AP4_ContainerAtom* schi = sample_description->GetSchemeInfo()->GetSchiAtom();
|
|
if (schi == NULL) return AP4_ERROR_INVALID_FORMAT;
|
|
|
|
// look for a track encryption atom
|
|
AP4_CencTrackEncryption* track_encryption_atom =
|
|
AP4_DYNAMIC_CAST(AP4_CencTrackEncryption, schi->GetChild(AP4_ATOM_TYPE_TENC));
|
|
if (track_encryption_atom == NULL) {
|
|
track_encryption_atom = AP4_DYNAMIC_CAST(AP4_CencTrackEncryption, schi->GetChild(AP4_UUID_PIFF_TRACK_ENCRYPTION_ATOM));
|
|
}
|
|
|
|
// look for a sample encryption atom
|
|
if (traf) {
|
|
sample_encryption_atom = AP4_DYNAMIC_CAST(AP4_SencAtom, traf->GetChild(AP4_ATOM_TYPE_SENC));
|
|
if (sample_encryption_atom == NULL) {
|
|
sample_encryption_atom = AP4_DYNAMIC_CAST(AP4_PiffSampleEncryptionAtom, traf->GetChild(AP4_UUID_PIFF_SAMPLE_ENCRYPTION_ATOM));
|
|
}
|
|
}
|
|
|
|
// parse the crypto params
|
|
if (sample_encryption_atom &&
|
|
sample_encryption_atom->GetOuter().GetFlags() & AP4_CENC_SAMPLE_ENCRYPTION_FLAG_OVERRIDE_TRACK_ENCRYPTION_DEFAULTS) {
|
|
algorithm_id = sample_encryption_atom->GetAlgorithmId();
|
|
iv_size = sample_encryption_atom->GetIvSize();
|
|
} else {
|
|
if (track_encryption_atom == NULL) return AP4_ERROR_INVALID_FORMAT;
|
|
algorithm_id = track_encryption_atom->GetDefaultAlgorithmId();
|
|
iv_size = track_encryption_atom->GetDefaultIvSize();
|
|
}
|
|
|
|
// try to create a sample info table from senc
|
|
if (sample_info_table == NULL && sample_encryption_atom) {
|
|
AP4_Result result = sample_encryption_atom->CreateSampleInfoTable(iv_size, sample_info_table);
|
|
if (AP4_FAILED(result)) return result;
|
|
}
|
|
|
|
// try to create a sample info table from saio/saiz
|
|
if (traf) {
|
|
for (AP4_List<AP4_Atom>::Item* child = traf->GetChildren().FirstItem();
|
|
child;
|
|
child = child->GetNext()) {
|
|
if (child->GetData()->GetType() == AP4_ATOM_TYPE_SAIO) {
|
|
saio = AP4_DYNAMIC_CAST(AP4_SaioAtom, child->GetData());
|
|
if (saio->GetAuxInfoType() != 0 && saio->GetAuxInfoType() != AP4_PROTECTION_SCHEME_TYPE_CENC) {
|
|
saio = NULL;
|
|
}
|
|
} else if (child->GetData()->GetType() == AP4_ATOM_TYPE_SAIZ) {
|
|
saiz = AP4_DYNAMIC_CAST(AP4_SaizAtom, child->GetData());
|
|
if (saiz->GetAuxInfoType() != 0 && saiz->GetAuxInfoType() != AP4_PROTECTION_SCHEME_TYPE_CENC) {
|
|
saiz = NULL;
|
|
}
|
|
}
|
|
}
|
|
if (sample_info_table == NULL && saio && saiz) {
|
|
AP4_Result result = Create(iv_size,
|
|
*traf,
|
|
*saio,
|
|
*saiz,
|
|
aux_info_data,
|
|
aux_info_data_offset,
|
|
sample_info_table);
|
|
if (AP4_FAILED(result)) return result;
|
|
}
|
|
}
|
|
|
|
if (sample_info_table == NULL) {
|
|
return AP4_ERROR_INVALID_FORMAT;
|
|
}
|
|
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencSampleInfoTable::Create
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_CencSampleInfoTable::Create(unsigned int iv_size,
|
|
AP4_ContainerAtom& traf,
|
|
AP4_SaioAtom& saio,
|
|
AP4_SaizAtom& saiz,
|
|
AP4_ByteStream& aux_info_data,
|
|
AP4_Position aux_info_data_offset,
|
|
AP4_CencSampleInfoTable*& sample_info_table)
|
|
{
|
|
AP4_Result result = AP4_SUCCESS;
|
|
|
|
// remember where we are in the data stream so that we can return to that point
|
|
// before returning
|
|
AP4_Position position_before = 0;
|
|
aux_info_data.Tell(position_before);
|
|
|
|
// count the samples
|
|
unsigned int sample_info_count = 0;
|
|
for (AP4_List<AP4_Atom>::Item* item = traf.GetChildren().FirstItem();
|
|
item;
|
|
item = item->GetNext()) {
|
|
AP4_Atom* atom = item->GetData();
|
|
if (atom->GetType() == AP4_ATOM_TYPE_TRUN) {
|
|
AP4_TrunAtom* trun = AP4_DYNAMIC_CAST(AP4_TrunAtom, atom);
|
|
sample_info_count += trun->GetEntries().ItemCount();
|
|
}
|
|
}
|
|
|
|
// create the table
|
|
AP4_CencSampleInfoTable* table = new AP4_CencSampleInfoTable(sample_info_count, iv_size);
|
|
|
|
// process each sample's auxiliary info
|
|
AP4_Ordinal saio_index = 0;
|
|
AP4_Ordinal saiz_index = 0;
|
|
AP4_DataBuffer info;
|
|
for (AP4_List<AP4_Atom>::Item* item = traf.GetChildren().FirstItem();
|
|
item;
|
|
item = item->GetNext()) {
|
|
AP4_Atom* atom = item->GetData();
|
|
if (atom->GetType() == AP4_ATOM_TYPE_TRUN) {
|
|
AP4_TrunAtom* trun = AP4_DYNAMIC_CAST(AP4_TrunAtom, atom);
|
|
|
|
if (saio_index == 0) {
|
|
aux_info_data.Seek(aux_info_data_offset+saio.GetEntries()[0]);
|
|
} else {
|
|
if (saio.GetEntries().ItemCount() > 1) {
|
|
if (saio_index >= saio.GetEntries().ItemCount()) {
|
|
result = AP4_ERROR_INVALID_FORMAT;
|
|
goto end;
|
|
}
|
|
aux_info_data.Seek(aux_info_data_offset+saio.GetEntries()[saio_index]);
|
|
}
|
|
}
|
|
++saio_index;
|
|
|
|
for (unsigned int i=0; i<trun->GetEntries().ItemCount(); i++) {
|
|
AP4_UI08 info_size = 0;
|
|
result = saiz.GetSampleInfoSize(saiz_index, info_size);
|
|
if (AP4_FAILED(result)) goto end;
|
|
|
|
info.SetDataSize(info_size);
|
|
result = aux_info_data.Read(info.UseData(), info_size);
|
|
if (AP4_FAILED(result)) goto end;
|
|
|
|
const AP4_UI08* info_data = info.GetData();
|
|
table->SetIv(saiz_index, info_data);
|
|
if (info_size > iv_size+2) {
|
|
AP4_UI16 subsample_count = AP4_BytesToUInt16BE(info_data+iv_size);
|
|
if (info_size < iv_size+2+subsample_count*6) {
|
|
// not enough data
|
|
goto end;
|
|
}
|
|
table->AddSubSampleData(subsample_count, info_data+iv_size+2);
|
|
}
|
|
saiz_index++;
|
|
}
|
|
}
|
|
}
|
|
result = AP4_SUCCESS;
|
|
|
|
end:
|
|
if (AP4_SUCCEEDED(result)) {
|
|
sample_info_table = table;
|
|
} else {
|
|
delete table;
|
|
sample_info_table = NULL;
|
|
}
|
|
aux_info_data.Seek(position_before);
|
|
return result;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencSampleInfoTable::Create
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_CencSampleInfoTable::Create(const AP4_UI08* serialized,
|
|
unsigned int serialized_size,
|
|
AP4_CencSampleInfoTable*& sample_info_table)
|
|
{
|
|
sample_info_table = NULL;
|
|
|
|
if (serialized_size < 4+4) {
|
|
return AP4_ERROR_INVALID_FORMAT;
|
|
}
|
|
AP4_UI32 sample_count = AP4_BytesToUInt32BE(serialized); serialized += 4; serialized_size -= 4;
|
|
AP4_UI32 iv_size = AP4_BytesToUInt32BE(serialized); serialized += 4; serialized_size -= 4;
|
|
|
|
if (serialized_size < sample_count*iv_size) {
|
|
return AP4_ERROR_INVALID_FORMAT;
|
|
}
|
|
AP4_CencSampleInfoTable* table = new AP4_CencSampleInfoTable(sample_count, iv_size);
|
|
table->m_IvData.SetData(serialized, sample_count*iv_size);
|
|
serialized += sample_count*iv_size;
|
|
serialized_size -= sample_count*iv_size;
|
|
|
|
if (serialized_size < 4) {
|
|
delete table;
|
|
return AP4_ERROR_INVALID_FORMAT;
|
|
}
|
|
AP4_UI32 item_count = AP4_BytesToUInt32BE(serialized); serialized += 4; serialized_size -= 4;
|
|
if (serialized_size < item_count*(2+4)) {
|
|
delete table;
|
|
return AP4_ERROR_INVALID_FORMAT;
|
|
}
|
|
table->m_BytesOfCleartextData.SetItemCount(item_count);
|
|
table->m_BytesOfEncryptedData.SetItemCount(item_count);
|
|
for (unsigned int i=0; i<item_count; i++) {
|
|
table->m_BytesOfCleartextData[i] = AP4_BytesToUInt16BE(serialized);
|
|
serialized += 2;
|
|
serialized_size -= 2;
|
|
}
|
|
for (unsigned int i=0; i<item_count; i++) {
|
|
table->m_BytesOfEncryptedData[i] = AP4_BytesToUInt32BE(serialized);
|
|
serialized += 4;
|
|
serialized_size -= 4;
|
|
}
|
|
|
|
if (serialized_size < 4) {
|
|
delete table;
|
|
return AP4_ERROR_INVALID_FORMAT;
|
|
}
|
|
AP4_UI32 use_subsamples = AP4_BytesToUInt32BE(serialized) != 0; serialized += 4; serialized_size -= 4;
|
|
if (!use_subsamples) {
|
|
sample_info_table = table;
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
if (serialized_size < sample_count*(4+4)) {
|
|
delete table;
|
|
return AP4_ERROR_INVALID_FORMAT;
|
|
}
|
|
table->m_SubSampleMapStarts.SetItemCount(sample_count);
|
|
table->m_SubSampleMapLengths.SetItemCount(sample_count);
|
|
for (unsigned int i=0; i<sample_count; i++) {
|
|
table->m_SubSampleMapStarts[i] = AP4_BytesToUInt32BE(serialized);
|
|
serialized += 4;
|
|
serialized_size -= 4;
|
|
}
|
|
for (unsigned int i=0; i<sample_count; i++) {
|
|
table->m_SubSampleMapLengths[i] = AP4_BytesToUInt32BE(serialized);
|
|
serialized += 4;
|
|
serialized_size -= 4;
|
|
}
|
|
|
|
sample_info_table = table;
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencSampleInfoTable::AP4_CencSampleInfoTable
|
|
+---------------------------------------------------------------------*/
|
|
AP4_CencSampleInfoTable::AP4_CencSampleInfoTable(AP4_UI32 sample_count,
|
|
AP4_UI08 iv_size) :
|
|
m_SampleCount(sample_count),
|
|
m_IvSize(iv_size)
|
|
{
|
|
m_IvData.SetDataSize(m_IvSize*sample_count);
|
|
AP4_SetMemory(m_IvData.UseData(), 0, m_IvSize*sample_count);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencSampleInfoTable::Serialize
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_CencSampleInfoTable::Serialize(AP4_DataBuffer& buffer)
|
|
{
|
|
unsigned int size = 4 +
|
|
4 +
|
|
m_SampleCount*m_IvSize +
|
|
4 +
|
|
m_BytesOfCleartextData.ItemCount()*2 +
|
|
m_BytesOfEncryptedData.ItemCount()*4 +
|
|
4;
|
|
bool use_subsamples = m_SubSampleMapStarts.ItemCount() != 0;
|
|
if (use_subsamples) {
|
|
size += m_SampleCount*(4+4);
|
|
}
|
|
|
|
// sanity check
|
|
if (m_IvData.GetDataSize() != m_SampleCount*m_IvSize ||
|
|
m_BytesOfCleartextData.ItemCount() != m_BytesOfEncryptedData.ItemCount() ||
|
|
m_SubSampleMapStarts.ItemCount() != m_SubSampleMapLengths.ItemCount()) {
|
|
return AP4_ERROR_INTERNAL;
|
|
}
|
|
if (use_subsamples && m_SubSampleMapStarts.ItemCount() != m_SampleCount) {
|
|
return AP4_ERROR_INTERNAL;
|
|
}
|
|
|
|
buffer.SetDataSize(size);
|
|
AP4_UI08* data = buffer.UseData();
|
|
|
|
AP4_BytesFromUInt32BE(data, m_SampleCount); data += 4;
|
|
AP4_BytesFromUInt32BE(data, m_IvSize); data += 4;
|
|
AP4_CopyMemory(data, m_IvData.GetData(), m_SampleCount*m_IvSize); data += m_SampleCount*m_IvSize;
|
|
AP4_BytesFromUInt32BE(data, m_BytesOfCleartextData.ItemCount()); data += 4;
|
|
for (unsigned int i=0; i<m_BytesOfCleartextData.ItemCount(); i++) {
|
|
AP4_BytesFromUInt16BE(data, m_BytesOfCleartextData[i]); data += 2;
|
|
}
|
|
for (unsigned int i=0; i<m_BytesOfEncryptedData.ItemCount(); i++) {
|
|
AP4_BytesFromUInt32BE(data, m_BytesOfEncryptedData[i]); data += 4;
|
|
}
|
|
AP4_BytesFromUInt32BE(data, use_subsamples?1:0); data += 4;
|
|
if (use_subsamples) {
|
|
for (unsigned int i=0; i<m_SampleCount; i++) {
|
|
AP4_BytesFromUInt32BE(data, m_SubSampleMapStarts[i]); data += 4;
|
|
}
|
|
for (unsigned int i=0; i<m_SampleCount; i++) {
|
|
AP4_BytesFromUInt32BE(data, m_SubSampleMapLengths[i]); data += 4;
|
|
}
|
|
}
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencSampleInfoTable::SetIv
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_CencSampleInfoTable::SetIv(AP4_Ordinal sample_index, const AP4_UI08* iv)
|
|
{
|
|
if (sample_index >= m_SampleCount) return AP4_ERROR_OUT_OF_RANGE;
|
|
AP4_UI08* dst = m_IvData.UseData()+(m_IvSize*sample_index);
|
|
AP4_CopyMemory(dst, iv, m_IvSize);
|
|
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencSampleInfoTable::GetIv
|
|
+---------------------------------------------------------------------*/
|
|
const AP4_UI08*
|
|
AP4_CencSampleInfoTable::GetIv(AP4_Ordinal sample_index)
|
|
{
|
|
if (sample_index >= m_SampleCount) return NULL;
|
|
return m_IvData.GetData()+(m_IvSize*sample_index);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencSampleInfoTable::AddSubSampleData
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_CencSampleInfoTable::AddSubSampleData(AP4_Cardinal subsample_count,
|
|
const AP4_UI08* subsample_data)
|
|
{
|
|
unsigned int current = m_SubSampleMapStarts.ItemCount();
|
|
if (current == 0) {
|
|
m_SubSampleMapStarts.Append(0);
|
|
} else {
|
|
m_SubSampleMapStarts.Append(m_SubSampleMapStarts[current-1]+
|
|
m_SubSampleMapLengths[current-1]);
|
|
}
|
|
m_SubSampleMapLengths.Append(subsample_count);
|
|
for (unsigned int i=0; i<subsample_count; i++) {
|
|
m_BytesOfCleartextData.Append(AP4_BytesToUInt16BE(subsample_data));
|
|
m_BytesOfEncryptedData.Append(AP4_BytesToUInt32BE(subsample_data+2));
|
|
subsample_data += 6;
|
|
}
|
|
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencSampleInfoTable::GetSubsampleInfo
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_CencSampleInfoTable::GetSubsampleInfo(AP4_Cardinal sample_index,
|
|
AP4_Cardinal subsample_index,
|
|
AP4_UI16& bytes_of_cleartext_data,
|
|
AP4_UI32& bytes_of_encrypted_data)
|
|
{
|
|
if (sample_index >= m_SampleCount ||
|
|
subsample_index >= m_SubSampleMapLengths[sample_index]) {
|
|
return AP4_ERROR_OUT_OF_RANGE;
|
|
}
|
|
unsigned int target = m_SubSampleMapStarts[sample_index]+subsample_index;
|
|
if (target >= m_BytesOfCleartextData.ItemCount() || target >= m_BytesOfEncryptedData.ItemCount()) {
|
|
return AP4_ERROR_OUT_OF_RANGE;
|
|
}
|
|
bytes_of_cleartext_data = m_BytesOfCleartextData[target];
|
|
bytes_of_encrypted_data = m_BytesOfEncryptedData[target];
|
|
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencSampleEncryption::AP4_CencSampleEncryption
|
|
+---------------------------------------------------------------------*/
|
|
AP4_CencSampleEncryption::AP4_CencSampleEncryption(AP4_Atom& outer,
|
|
AP4_Size size,
|
|
AP4_ByteStream& stream) :
|
|
m_Outer(outer)
|
|
{
|
|
if (outer.GetFlags() & AP4_CENC_SAMPLE_ENCRYPTION_FLAG_OVERRIDE_TRACK_ENCRYPTION_DEFAULTS) {
|
|
stream.ReadUI24(m_AlgorithmId);
|
|
stream.ReadUI08(m_IvSize);
|
|
stream.Read (m_Kid, 16);
|
|
} else {
|
|
m_AlgorithmId = 0;
|
|
m_IvSize = 0;
|
|
AP4_SetMemory(m_Kid, 0, 16);
|
|
}
|
|
|
|
stream.ReadUI32(m_SampleInfoCount);
|
|
|
|
AP4_Size payload_size = size-m_Outer.GetHeaderSize()-4;
|
|
m_SampleInfos.SetDataSize(payload_size);
|
|
stream.Read(m_SampleInfos.UseData(), payload_size);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencSampleEncryption::AP4_CencSampleEncryption
|
|
+---------------------------------------------------------------------*/
|
|
AP4_CencSampleEncryption::AP4_CencSampleEncryption(AP4_Atom& outer) :
|
|
m_Outer(outer),
|
|
m_AlgorithmId(0),
|
|
m_IvSize(16),
|
|
m_SampleInfoCount(0),
|
|
m_SampleInfoCursor(0)
|
|
{
|
|
AP4_SetMemory(m_Kid, 0, 16);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencSampleEncryption::AP4_CencSampleEncryption
|
|
+---------------------------------------------------------------------*/
|
|
AP4_CencSampleEncryption::AP4_CencSampleEncryption(AP4_Atom& outer,
|
|
AP4_UI32 algorithm_id,
|
|
AP4_UI08 iv_size,
|
|
const AP4_UI08* kid) :
|
|
m_Outer(outer),
|
|
m_AlgorithmId(algorithm_id),
|
|
m_IvSize(iv_size),
|
|
m_SampleInfoCount(0),
|
|
m_SampleInfoCursor(0)
|
|
{
|
|
AP4_CopyMemory(m_Kid, kid, 16);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencSampleEncryption::AddSampleInfo
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_CencSampleEncryption::AddSampleInfo(const AP4_UI08* iv,
|
|
AP4_DataBuffer& subsample_info)
|
|
{
|
|
unsigned int added_size = m_IvSize+subsample_info.GetDataSize();
|
|
|
|
if (m_SampleInfoCursor+added_size > m_SampleInfos.GetDataSize()) {
|
|
// too much data!
|
|
return AP4_ERROR_OUT_OF_RANGE;
|
|
}
|
|
AP4_UI08* info = m_SampleInfos.UseData()+m_SampleInfoCursor;
|
|
AP4_CopyMemory(info, iv, m_IvSize);
|
|
if (subsample_info.GetDataSize()) {
|
|
AP4_CopyMemory(info+m_IvSize, subsample_info.GetData(), subsample_info.GetDataSize());
|
|
}
|
|
m_SampleInfoCursor += added_size;
|
|
++m_SampleInfoCount;
|
|
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencSampleEncryption::SetSampleInfosSize
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_CencSampleEncryption::SetSampleInfosSize(AP4_Size size)
|
|
{
|
|
m_SampleInfos.SetDataSize(size);
|
|
if (m_Outer.GetFlags() & AP4_CENC_SAMPLE_ENCRYPTION_FLAG_OVERRIDE_TRACK_ENCRYPTION_DEFAULTS) {
|
|
m_Outer.SetSize(m_Outer.GetHeaderSize()+20+4+size);
|
|
} else {
|
|
m_Outer.SetSize(m_Outer.GetHeaderSize()+4+size);
|
|
}
|
|
if (m_Outer.GetParent()) {
|
|
AP4_AtomParent* parent = AP4_DYNAMIC_CAST(AP4_AtomParent, m_Outer.GetParent());
|
|
if (parent) {
|
|
parent->OnChildChanged(&m_Outer);
|
|
}
|
|
}
|
|
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencSampleEncryption::CreateSampleInfoTable
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_CencSampleEncryption::CreateSampleInfoTable(AP4_Size default_iv_size,
|
|
AP4_CencSampleInfoTable*& table)
|
|
{
|
|
AP4_Size iv_size = default_iv_size;
|
|
if (m_Outer.GetFlags() & AP4_CENC_SAMPLE_ENCRYPTION_FLAG_OVERRIDE_TRACK_ENCRYPTION_DEFAULTS) {
|
|
iv_size = m_IvSize;
|
|
}
|
|
bool has_subsamples = false;
|
|
if (m_Outer.GetFlags() & AP4_CENC_SAMPLE_ENCRYPTION_FLAG_USE_SUB_SAMPLE_ENCRYPTION) {
|
|
has_subsamples = true;
|
|
}
|
|
|
|
// create the table
|
|
AP4_Result result = AP4_ERROR_INVALID_FORMAT;
|
|
table = new AP4_CencSampleInfoTable(m_SampleInfoCount, iv_size);
|
|
const AP4_UI08* data = m_SampleInfos.GetData();
|
|
AP4_UI32 data_size = m_SampleInfos.GetDataSize();
|
|
for (unsigned int i=0; i<m_SampleInfoCount; i++) {
|
|
if (data_size < iv_size) goto end;
|
|
table->SetIv(i, data);
|
|
data += iv_size;
|
|
data_size -= iv_size;
|
|
if (has_subsamples) {
|
|
if (data_size < 2) goto end;
|
|
AP4_UI16 subsample_count = AP4_BytesToUInt16BE(data);
|
|
data += 2;
|
|
data_size -= 2;
|
|
|
|
if (data_size < subsample_count*(unsigned int)6) goto end;
|
|
|
|
result = table->AddSubSampleData(subsample_count, data);
|
|
if (AP4_FAILED(result)) goto end;
|
|
|
|
data += 6*subsample_count;
|
|
data_size -= 6*subsample_count;
|
|
}
|
|
}
|
|
result = AP4_SUCCESS;
|
|
|
|
end:
|
|
if (AP4_FAILED(result)) {
|
|
delete table;
|
|
table = NULL;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencSampleEncryption::DoInspectFields
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_CencSampleEncryption::DoInspectFields(AP4_AtomInspector& inspector)
|
|
{
|
|
if (m_Outer.GetFlags() & AP4_CENC_SAMPLE_ENCRYPTION_FLAG_OVERRIDE_TRACK_ENCRYPTION_DEFAULTS) {
|
|
inspector.AddField("AlgorithmID", m_AlgorithmId);
|
|
inspector.AddField("IV_size", m_IvSize);
|
|
inspector.AddField("KID", m_Kid, 16);
|
|
}
|
|
|
|
inspector.AddField("sample info count", m_SampleInfoCount);
|
|
|
|
if (inspector.GetVerbosity() < 2) {
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
// since we don't know the IV size necessarily (we don't have the context), we
|
|
// will try to guess the IV size (we'll try 16 and 8)
|
|
unsigned int iv_size = m_IvSize;
|
|
if (iv_size == 0) {
|
|
if (m_Outer.GetFlags() & AP4_CENC_SAMPLE_ENCRYPTION_FLAG_USE_SUB_SAMPLE_ENCRYPTION) {
|
|
bool data_ok = false;
|
|
for (unsigned int k=1; k<=2; k++) {
|
|
data_ok = true;
|
|
iv_size = 8*k;
|
|
const AP4_UI08* info = m_SampleInfos.GetData();
|
|
AP4_Size info_size = m_SampleInfos.GetDataSize();
|
|
for (unsigned int i=0; i<m_SampleInfoCount; i++) {
|
|
if (info_size < iv_size+2) {
|
|
data_ok = false;
|
|
break;
|
|
}
|
|
info += iv_size;
|
|
info_size -= iv_size;
|
|
unsigned int num_entries = AP4_BytesToInt16BE(info);
|
|
info += 2;
|
|
info_size -= 2;
|
|
if (info_size < num_entries*6) {
|
|
data_ok = false;
|
|
break;
|
|
}
|
|
info += num_entries*6;
|
|
info_size -= num_entries*6;
|
|
}
|
|
}
|
|
if (data_ok == false) return AP4_SUCCESS;
|
|
} else {
|
|
iv_size = m_SampleInfos.GetDataSize()/m_SampleInfoCount;
|
|
if (iv_size*m_SampleInfoCount != m_SampleInfos.GetDataSize()) {
|
|
// not a multiple...
|
|
return AP4_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
inspector.AddField("IV Size (inferred)", iv_size);
|
|
|
|
const AP4_UI08* info = m_SampleInfos.GetData();
|
|
for (unsigned int i=0; i<m_SampleInfoCount; i++) {
|
|
char header[64];
|
|
AP4_FormatString(header, sizeof(header), "entry %04d", i);
|
|
inspector.AddField(header, info, iv_size);
|
|
info += iv_size;
|
|
if (m_Outer.GetFlags() & AP4_CENC_SAMPLE_ENCRYPTION_FLAG_USE_SUB_SAMPLE_ENCRYPTION) {
|
|
unsigned int num_entries = AP4_BytesToInt16BE(info);
|
|
info += 2;
|
|
for (unsigned int j=0; j<num_entries; j++) {
|
|
unsigned int bocd = AP4_BytesToUInt16BE(info);
|
|
AP4_FormatString(header, sizeof(header), "sub-entry %04d.%d bytes of clear data", i, j);
|
|
inspector.AddField(header, bocd);
|
|
unsigned int boed = AP4_BytesToUInt32BE(info+2);
|
|
AP4_FormatString(header, sizeof(header), "sub-entry %04d.%d bytes of encrypted data", i, j);
|
|
inspector.AddField(header, boed);
|
|
info += 6;
|
|
}
|
|
}
|
|
}
|
|
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_CencSampleEncryption::DoWriteFields
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_CencSampleEncryption::DoWriteFields(AP4_ByteStream& stream)
|
|
{
|
|
AP4_Result result;
|
|
|
|
// optional fields
|
|
if (m_Outer.GetFlags() & AP4_CENC_SAMPLE_ENCRYPTION_FLAG_OVERRIDE_TRACK_ENCRYPTION_DEFAULTS) {
|
|
result = stream.WriteUI24(m_AlgorithmId);
|
|
if (AP4_FAILED(result)) return result;
|
|
result = stream.WriteUI08(m_IvSize);
|
|
if (AP4_FAILED(result)) return result;
|
|
result = stream.Write(m_Kid, 16);
|
|
if (AP4_FAILED(result)) return result;
|
|
}
|
|
|
|
// IVs
|
|
result = stream.WriteUI32(m_SampleInfoCount);
|
|
if (AP4_FAILED(result)) return result;
|
|
if (m_SampleInfos.GetDataSize()) {
|
|
stream.Write(m_SampleInfos.GetData(), m_SampleInfos.GetDataSize());
|
|
if (AP4_FAILED(result)) return result;
|
|
}
|
|
|
|
return AP4_SUCCESS;
|
|
}
|
|
|