Files
Bento4/Source/C++/Core/Ap4Protection.cpp
2018-01-01 17:39:41 -08:00

1481 lines
55 KiB
C++

/*****************************************************************
|
| AP4 - Protected Stream Support
|
| Copyright 2002-2008 Axiomatic Systems, LLC
|
|
| This file is part of Bento4/AP4 (MP4 Atom 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 "Ap4Protection.h"
#include "Ap4SchmAtom.h"
#include "Ap4StsdAtom.h"
#include "Ap4FtypAtom.h"
#include "Ap4Sample.h"
#include "Ap4StreamCipher.h"
#include "Ap4IsfmAtom.h"
#include "Ap4FrmaAtom.h"
#include "Ap4IkmsAtom.h"
#include "Ap4IsfmAtom.h"
#include "Ap4IsltAtom.h"
#include "Ap4Utils.h"
#include "Ap4TrakAtom.h"
#include "Ap4IsmaCryp.h"
#include "Ap4AesBlockCipher.h"
#include "Ap4OmaDcf.h"
#include "Ap4Marlin.h"
#include "Ap4Piff.h"
#include "Ap4CommonEncryption.h"
/*----------------------------------------------------------------------
| dynamic cast support
+---------------------------------------------------------------------*/
AP4_DEFINE_DYNAMIC_CAST_ANCHOR(AP4_ProtectedSampleDescription)
/*----------------------------------------------------------------------
| AP4_EncaSampleEntry::AP4_EncaSampleEntry
+---------------------------------------------------------------------*/
AP4_EncaSampleEntry::AP4_EncaSampleEntry(AP4_UI32 type,
AP4_Size size,
AP4_ByteStream& stream,
AP4_AtomFactory& atom_factory) :
AP4_AudioSampleEntry(type, size, stream, atom_factory)
{
}
/*----------------------------------------------------------------------
| AP4_EncaSampleEntry::AP4_EncaSampleEntry
+---------------------------------------------------------------------*/
AP4_EncaSampleEntry::AP4_EncaSampleEntry(AP4_Size size,
AP4_ByteStream& stream,
AP4_AtomFactory& atom_factory) :
AP4_AudioSampleEntry(AP4_ATOM_TYPE_ENCA, size, stream, atom_factory)
{
}
/*----------------------------------------------------------------------
| AP4_EncaSampleEntry::ToSampleDescription
+---------------------------------------------------------------------*/
AP4_SampleDescription*
AP4_EncaSampleEntry::ToSampleDescription()
{
// get the original sample format
AP4_FrmaAtom* frma = AP4_DYNAMIC_CAST(AP4_FrmaAtom, FindChild("sinf/frma"));
// get the schi atom
AP4_ContainerAtom* schi;
schi = AP4_DYNAMIC_CAST(AP4_ContainerAtom, FindChild("sinf/schi"));
// get the scheme info
AP4_SchmAtom* schm = AP4_DYNAMIC_CAST(AP4_SchmAtom, FindChild("sinf/schm"));
AP4_UI32 original_format = frma?frma->GetOriginalFormat():AP4_ATOM_TYPE_MP4A;
if (schm) {
// create the original sample description
return new AP4_ProtectedSampleDescription(
m_Type,
ToTargetSampleDescription(original_format),
original_format,
schm->GetSchemeType(),
schm->GetSchemeVersion(),
schm->GetSchemeUri().GetChars(),
schi);
} else if (schi) {
// try to see if we can guess the protection scheme from the 'schi' contents
AP4_Atom* odkm = schi->GetChild(AP4_ATOM_TYPE_ODKM);
if (odkm) {
// create the original sample description
return new AP4_ProtectedSampleDescription(
m_Type,
ToTargetSampleDescription(original_format),
original_format,
AP4_PROTECTION_SCHEME_TYPE_OMA,
AP4_PROTECTION_SCHEME_VERSION_OMA_20,
NULL,
schi);
}
}
// unknown scheme
return NULL;
}
/*----------------------------------------------------------------------
| AP4_EncaSampleEntry::ToTargetSampleDescription
+---------------------------------------------------------------------*/
AP4_SampleDescription*
AP4_EncaSampleEntry::ToTargetSampleDescription(AP4_UI32 format)
{
switch (format) {
case AP4_ATOM_TYPE_MP4A: {
AP4_EsdsAtom* esds = AP4_DYNAMIC_CAST(AP4_EsdsAtom, GetChild(AP4_ATOM_TYPE_ESDS));
if (esds == NULL) {
// check if this is a quicktime style sample description
if (m_QtVersion > 0) {
esds = AP4_DYNAMIC_CAST(AP4_EsdsAtom, FindChild("wave/esds"));
}
}
return new AP4_MpegAudioSampleDescription(
GetSampleRate(),
GetSampleSize(),
GetChannelCount(),
esds);
}
default:
return new AP4_GenericAudioSampleDescription(
format,
GetSampleRate(),
GetSampleSize(),
GetChannelCount(),
this);
}
}
/*----------------------------------------------------------------------
| AP4_EncvSampleEntry::AP4_EncvSampleEntry
+---------------------------------------------------------------------*/
AP4_EncvSampleEntry::AP4_EncvSampleEntry(AP4_UI32 type,
AP4_Size size,
AP4_ByteStream& stream,
AP4_AtomFactory& atom_factory) :
AP4_VisualSampleEntry(type, size, stream, atom_factory)
{
}
/*----------------------------------------------------------------------
| AP4_EncvSampleEntry::AP4_EncvSampleEntry
+---------------------------------------------------------------------*/
AP4_EncvSampleEntry::AP4_EncvSampleEntry(AP4_Size size,
AP4_ByteStream& stream,
AP4_AtomFactory& atom_factory) :
AP4_VisualSampleEntry(AP4_ATOM_TYPE_ENCV, size, stream, atom_factory)
{
}
/*----------------------------------------------------------------------
| AP4_EncvSampleEntry::ToSampleDescription
+---------------------------------------------------------------------*/
AP4_SampleDescription*
AP4_EncvSampleEntry::ToSampleDescription()
{
// get the original sample format
AP4_FrmaAtom* frma = AP4_DYNAMIC_CAST(AP4_FrmaAtom, FindChild("sinf/frma"));
// get the schi atom
AP4_ContainerAtom* schi;
schi = AP4_DYNAMIC_CAST(AP4_ContainerAtom, FindChild("sinf/schi"));
// get the scheme info
AP4_SchmAtom* schm = AP4_DYNAMIC_CAST(AP4_SchmAtom, FindChild("sinf/schm"));
AP4_UI32 original_format = frma?frma->GetOriginalFormat():AP4_ATOM_TYPE_MP4V;
if (schm) {
// create the sample description
return new AP4_ProtectedSampleDescription(
m_Type,
ToTargetSampleDescription(original_format),
original_format,
schm->GetSchemeType(),
schm->GetSchemeVersion(),
schm->GetSchemeUri().GetChars(),
schi);
} else if (schi) {
// try to see if we can guess the protection scheme from the 'schi' contents
AP4_Atom* odkm = schi->GetChild(AP4_ATOM_TYPE_ODKM);
if (odkm) {
// create the original sample description
return new AP4_ProtectedSampleDescription(
m_Type,
ToTargetSampleDescription(original_format),
original_format,
AP4_PROTECTION_SCHEME_TYPE_OMA,
AP4_PROTECTION_SCHEME_VERSION_OMA_20,
NULL,
schi);
}
}
// unknown scheme
return NULL;
}
/*----------------------------------------------------------------------
| AP4_EncvSampleEntry::ToTargetSampleDescription
+---------------------------------------------------------------------*/
AP4_SampleDescription*
AP4_EncvSampleEntry::ToTargetSampleDescription(AP4_UI32 format)
{
switch (format) {
case AP4_SAMPLE_FORMAT_AVC1:
case AP4_SAMPLE_FORMAT_AVC2:
case AP4_SAMPLE_FORMAT_AVC3:
case AP4_SAMPLE_FORMAT_AVC4:
case AP4_SAMPLE_FORMAT_DVAV:
case AP4_SAMPLE_FORMAT_DVA1:
return new AP4_AvcSampleDescription(
format,
m_Width,
m_Height,
m_Depth,
m_CompressorName.GetChars(),
this);
case AP4_SAMPLE_FORMAT_HVC1:
case AP4_SAMPLE_FORMAT_HEV1:
case AP4_SAMPLE_FORMAT_DVHE:
case AP4_SAMPLE_FORMAT_DVH1:
return new AP4_HevcSampleDescription(
format,
m_Width,
m_Height,
m_Depth,
m_CompressorName.GetChars(),
this);
case AP4_ATOM_TYPE_MP4V:
return new AP4_MpegVideoSampleDescription(
m_Width,
m_Height,
m_Depth,
m_CompressorName.GetChars(),
AP4_DYNAMIC_CAST(AP4_EsdsAtom, GetChild(AP4_ATOM_TYPE_ESDS)));
default:
return new AP4_GenericVideoSampleDescription(
format,
m_Width,
m_Height,
m_Depth,
m_CompressorName.GetChars(),
this);
}
}
/*----------------------------------------------------------------------
| AP4_DrmsSampleEntry::AP4_DrmsSampleEntry
+---------------------------------------------------------------------*/
AP4_DrmsSampleEntry::AP4_DrmsSampleEntry(AP4_Size size,
AP4_ByteStream& stream,
AP4_AtomFactory& atom_factory) :
AP4_EncaSampleEntry(AP4_ATOM_TYPE_DRMS, size, stream, atom_factory)
{
}
/*----------------------------------------------------------------------
| AP4_DrmiSampleEntry::AP4_DrmiSampleEntry
+---------------------------------------------------------------------*/
AP4_DrmiSampleEntry::AP4_DrmiSampleEntry(AP4_Size size,
AP4_ByteStream& stream,
AP4_AtomFactory& atom_factory) :
AP4_EncvSampleEntry(AP4_ATOM_TYPE_DRMI, size, stream, atom_factory)
{
}
/*----------------------------------------------------------------------
| AP4_ProtectionSchemeInfo::~AP4_ProtectionSchemeInfo
+---------------------------------------------------------------------*/
AP4_ProtectionSchemeInfo::~AP4_ProtectionSchemeInfo()
{
delete m_SchiAtom;
}
/*----------------------------------------------------------------------
| AP4_ProtectionSchemeInfo::AP4_ProtectionSchemeInfo
+---------------------------------------------------------------------*/
AP4_ProtectionSchemeInfo::AP4_ProtectionSchemeInfo(AP4_ContainerAtom* schi)
{
if (schi) {
m_SchiAtom = static_cast<AP4_ContainerAtom*>(schi->Clone());
} else {
m_SchiAtom = NULL;
}
}
/*----------------------------------------------------------------------
| AP4_ProtectionKeyMap::AP4_ProtectionKeyMap
+---------------------------------------------------------------------*/
AP4_ProtectionKeyMap::AP4_ProtectionKeyMap()
{
}
/*----------------------------------------------------------------------
| AP4_ProtectionKeyMap::~AP4_ProtectionKeyMap
+---------------------------------------------------------------------*/
AP4_ProtectionKeyMap::~AP4_ProtectionKeyMap()
{
m_KeyEntries.DeleteReferences();
}
/*----------------------------------------------------------------------
| AP4_ProtectionKeyMap::SetKey
+---------------------------------------------------------------------*/
AP4_Result
AP4_ProtectionKeyMap::SetKey(AP4_UI32 track_id,
const AP4_UI08* key,
AP4_Size key_size,
const AP4_UI08* iv,
AP4_Size iv_size)
{
KeyEntry* entry = GetEntry(track_id);
if (entry == NULL) {
m_KeyEntries.Add(new KeyEntry(track_id, key, key_size, iv, iv_size));
} else {
entry->SetKey(key, key_size, iv, iv_size);
}
return AP4_SUCCESS;
}
/*----------------------------------------------------------------------
| AP4_ProtectionKeyMap::SetKeyForKid
+---------------------------------------------------------------------*/
AP4_Result
AP4_ProtectionKeyMap::SetKeyForKid(const AP4_UI08* kid,
const AP4_UI08* key,
AP4_Size key_size,
const AP4_UI08* iv,
AP4_Size iv_size)
{
KeyEntry* entry = GetEntryByKid(kid);
if (entry == NULL) {
m_KeyEntries.Add(new KeyEntry(kid, key, key_size, iv, iv_size));
} else {
entry->SetKey(key, key_size, iv, iv_size);
}
return AP4_SUCCESS;
}
/*----------------------------------------------------------------------
| AP4_ProtectionKeyMap::SetKey
+---------------------------------------------------------------------*/
AP4_Result
AP4_ProtectionKeyMap::SetKeys(const AP4_ProtectionKeyMap& key_map)
{
AP4_List<KeyEntry>::Item* item = key_map.m_KeyEntries.FirstItem();
while (item) {
KeyEntry* entry = item->GetData();
m_KeyEntries.Add(new KeyEntry(entry->m_TrackId,
entry->m_Key.GetData(),
entry->m_Key.GetDataSize(),
entry->m_IV.GetData(),
entry->m_IV.GetDataSize()));
item = item->GetNext();
}
return AP4_SUCCESS;
}
/*----------------------------------------------------------------------
| AP4_ProtectionKeyMap::GetKeyAndIv
+---------------------------------------------------------------------*/
AP4_Result
AP4_ProtectionKeyMap::GetKeyAndIv(AP4_UI32 track_id,
const AP4_DataBuffer*& key,
const AP4_DataBuffer*& iv)
{
KeyEntry* entry = GetEntry(track_id);
if (entry) {
key = &entry->m_Key;
iv = &entry->m_IV;
return AP4_SUCCESS;
} else {
key = NULL;
iv = NULL;
return AP4_ERROR_NO_SUCH_ITEM;
}
}
/*----------------------------------------------------------------------
| AP4_ProtectionKeyMap::GetKeyAndIvByKid
+---------------------------------------------------------------------*/
AP4_Result
AP4_ProtectionKeyMap::GetKeyAndIvByKid(const AP4_UI08* kid,
const AP4_DataBuffer*& key,
const AP4_DataBuffer*& iv)
{
KeyEntry* entry = GetEntryByKid(kid);
if (entry) {
key = &entry->m_Key;
iv = &entry->m_IV;
return AP4_SUCCESS;
} else {
key = NULL;
iv = NULL;
return AP4_ERROR_NO_SUCH_ITEM;
}
}
/*----------------------------------------------------------------------
| AP4_ProtectionKeyMap::GetKey
+---------------------------------------------------------------------*/
const AP4_DataBuffer*
AP4_ProtectionKeyMap::GetKey(AP4_UI32 track_id) const
{
KeyEntry* entry = GetEntry(track_id);
if (entry) {
return &entry->m_Key;
} else {
return NULL;
}
}
/*----------------------------------------------------------------------
| AP4_ProtectionKeyMap::GetKeyByKid
+---------------------------------------------------------------------*/
const AP4_DataBuffer*
AP4_ProtectionKeyMap::GetKeyByKid(const AP4_UI08* kid) const
{
KeyEntry* entry = GetEntryByKid(kid);
if (entry) {
return &entry->m_Key;
} else {
return NULL;
}
}
/*----------------------------------------------------------------------
| AP4_ProtectionKeyMap::GetEntry
+---------------------------------------------------------------------*/
AP4_ProtectionKeyMap::KeyEntry*
AP4_ProtectionKeyMap::GetEntry(AP4_UI32 track_id) const
{
AP4_List<KeyEntry>::Item* item = m_KeyEntries.FirstItem();
while (item) {
KeyEntry* entry = item->GetData();
if (entry->m_TrackId == track_id) return entry;
item = item->GetNext();
}
return NULL;
}
/*----------------------------------------------------------------------
| AP4_ProtectionKeyMap::GetEntryByKid
+---------------------------------------------------------------------*/
AP4_ProtectionKeyMap::KeyEntry*
AP4_ProtectionKeyMap::GetEntryByKid(const AP4_UI08* kid) const
{
AP4_List<KeyEntry>::Item* item = m_KeyEntries.FirstItem();
while (item) {
KeyEntry* entry = item->GetData();
if (AP4_CompareMemory(entry->m_KID, kid, 16) == 0) return entry;
item = item->GetNext();
}
return NULL;
}
/*----------------------------------------------------------------------
| AP4_ProtectionKeyMap::KeyEntry::KeyEntry
+---------------------------------------------------------------------*/
AP4_ProtectionKeyMap::KeyEntry::KeyEntry(AP4_UI32 track_id,
const AP4_UI08* key,
AP4_Size key_size,
const AP4_UI08* iv,
AP4_Size iv_size) :
m_TrackId(track_id)
{
AP4_SetMemory(m_KID, 0, 16);
SetKey(key, key_size, iv, iv_size);
}
/*----------------------------------------------------------------------
| AP4_ProtectionKeyMap::KeyEntry::KeyEntry
+---------------------------------------------------------------------*/
AP4_ProtectionKeyMap::KeyEntry::KeyEntry(const AP4_UI08* kid,
const AP4_UI08* key,
AP4_Size key_size,
const AP4_UI08* iv,
AP4_Size iv_size) :
m_TrackId(0)
{
AP4_CopyMemory(m_KID, kid, 16);
SetKey(key, key_size, iv, iv_size);
}
/*----------------------------------------------------------------------
| AP4_ProtectionKeyMap::KeyEntry::SetKey
+---------------------------------------------------------------------*/
void
AP4_ProtectionKeyMap::KeyEntry::SetKey(const AP4_UI08* key, AP4_Size key_size,
const AP4_UI08* iv, AP4_Size iv_size)
{
if (key) {
m_Key.SetData(key, key_size);
}
if (iv) {
m_IV.SetData(iv, iv_size);
} else {
m_IV.SetDataSize(16);
AP4_SetMemory(m_IV.UseData(), 0, 16);
}
}
/*----------------------------------------------------------------------
| AP4_TrackPropertyMap::~AP4_TrackPropertyMap
+---------------------------------------------------------------------*/
AP4_TrackPropertyMap::~AP4_TrackPropertyMap()
{
m_Entries.DeleteReferences();
}
/*----------------------------------------------------------------------
| AP4_TrackPropertyMap::SetProperty
+---------------------------------------------------------------------*/
AP4_Result
AP4_TrackPropertyMap::SetProperty(AP4_UI32 track_id,
const char* name,
const char* value)
{
return m_Entries.Add(new Entry(track_id, name, value));
}
/*----------------------------------------------------------------------
| AP4_TrackPropertyMap::SetProperties
+---------------------------------------------------------------------*/
AP4_Result
AP4_TrackPropertyMap::SetProperties(const AP4_TrackPropertyMap& properties)
{
AP4_List<Entry>::Item* item = properties.m_Entries.FirstItem();
while (item) {
Entry* entry = item->GetData();
m_Entries.Add(new Entry(entry->m_TrackId,
entry->m_Name.GetChars(),
entry->m_Value.GetChars()));
item = item->GetNext();
}
return AP4_SUCCESS;
}
/*----------------------------------------------------------------------
| AP4_TrackPropertyMap::GetProperty
+---------------------------------------------------------------------*/
const char*
AP4_TrackPropertyMap::GetProperty(AP4_UI32 track_id, const char* name)
{
AP4_List<Entry>::Item* item = m_Entries.FirstItem();
while (item) {
Entry* entry = item->GetData();
if (entry->m_TrackId == track_id &&
AP4_CompareStrings(entry->m_Name.GetChars(), name) == 0) {
return entry->m_Value.GetChars();
}
item = item->GetNext();
}
// not found
return NULL;
}
/*----------------------------------------------------------------------
| AP4_TrackPropertyMap::GetTextualHeaders
+---------------------------------------------------------------------*/
AP4_Result
AP4_TrackPropertyMap::GetTextualHeaders(AP4_UI32 track_id, AP4_DataBuffer& textual_headers)
{
AP4_Size buffer_size = 0;
AP4_Result result = AP4_SUCCESS;
AP4_Byte* data_buffer;
// get the size needed for the textual headers
AP4_List<Entry>::Item* item = m_Entries.FirstItem();
while (item) {
Entry* entry = item->GetData();
if (entry->m_TrackId == track_id) {
const char* name = entry->m_Name.GetChars();
if (AP4_CompareStrings(name, "ContentId") != 0 &&
AP4_CompareStrings(name, "RightsIssuerUrl") != 0 &&
AP4_CompareStrings(name, "KID") != 0) {
buffer_size += (entry->m_Name.GetLength() +
entry->m_Value.GetLength() +
2); // colon + nul
}
}
item = item->GetNext();
}
result = textual_headers.SetDataSize(buffer_size);
AP4_CHECK(result);
data_buffer = textual_headers.UseData();
// set the textual headers
item = m_Entries.FirstItem();
while (item) {
Entry* entry = item->GetData();
if (entry->m_TrackId == track_id) {
const char* name = entry->m_Name.GetChars();
const char* value = NULL;
AP4_Size name_len = 0;
AP4_Size value_len = 0;
if (AP4_CompareStrings(name, "ContentId") != 0 &&
AP4_CompareStrings(name, "RightsIssuerUrl") != 0 &&
AP4_CompareStrings(name, "KID") != 0) {
name_len = entry->m_Name.GetLength();
value = entry->m_Value.GetChars();
value_len = entry->m_Value.GetLength();
// format is name:value\0
if (name && value) {
AP4_CopyMemory(data_buffer, name, name_len);
data_buffer[name_len] = ':';
data_buffer += (1+name_len);
AP4_CopyMemory(data_buffer, value, value_len);
data_buffer[value_len] = '\0';
data_buffer += (1+value_len);
}
}
}
item = item->GetNext();
}
// success path
return AP4_SUCCESS;
}
/*----------------------------------------------------------------------
| AP4_ProtectedSampleDescription::AP4_ProtectedSampleDescription
+---------------------------------------------------------------------*/
AP4_ProtectedSampleDescription::AP4_ProtectedSampleDescription(
AP4_UI32 format,
AP4_SampleDescription* original_sample_description,
AP4_UI32 original_format,
AP4_UI32 scheme_type,
AP4_UI32 scheme_version,
const char* scheme_uri,
AP4_ContainerAtom* schi,
bool transfer_ownership_of_original /* = true */) :
AP4_SampleDescription(TYPE_PROTECTED, format, NULL),
m_OriginalSampleDescription(original_sample_description),
m_OriginalSampleDescriptionIsOwned(transfer_ownership_of_original),
m_OriginalFormat(original_format),
m_SchemeType(scheme_type),
m_SchemeVersion(scheme_version),
m_SchemeUri(scheme_uri)
{
m_SchemeInfo = new AP4_ProtectionSchemeInfo(schi);
}
/*----------------------------------------------------------------------
| AP4_ProtectedSampleDescription::~AP4_ProtectedSampleDescription
+---------------------------------------------------------------------*/
AP4_ProtectedSampleDescription::~AP4_ProtectedSampleDescription()
{
delete m_SchemeInfo;
if (m_OriginalSampleDescriptionIsOwned) delete m_OriginalSampleDescription;
}
/*----------------------------------------------------------------------
| AP4_ProtectedSampleDescription::ToAtom
+---------------------------------------------------------------------*/
AP4_Atom*
AP4_ProtectedSampleDescription::ToAtom() const
{
// construct the atom for the original sample description
if (m_OriginalSampleDescription == NULL) return NULL;
AP4_Atom* atom = m_OriginalSampleDescription->ToAtom();
// switch the atom type
atom->SetType(m_Format);
// check that the constructed atom is a container
AP4_ContainerAtom* container = AP4_DYNAMIC_CAST(AP4_ContainerAtom, atom);
if (container == NULL) return atom; // not a container ?? return now.
// create the sinf atom
AP4_ContainerAtom* sinf = new AP4_ContainerAtom(AP4_ATOM_TYPE_SINF);
// create and add a frma atom
AP4_FrmaAtom* frma = new AP4_FrmaAtom(m_OriginalFormat);
sinf->AddChild(frma);
// create and add a schm atom
AP4_SchmAtom* schm = new AP4_SchmAtom(m_SchemeType, m_SchemeVersion, m_SchemeUri.GetChars());
sinf->AddChild(schm);
// add the schi atom
if (m_SchemeInfo && m_SchemeInfo->GetSchiAtom()) {
sinf->AddChild(m_SchemeInfo->GetSchiAtom()->Clone());
}
// add the sinf to the returned atom
container->AddChild(sinf);
return atom;
}
/*----------------------------------------------------------------------
| AP4_SampleDecrypter:Create
+---------------------------------------------------------------------*/
AP4_SampleDecrypter*
AP4_SampleDecrypter::Create(AP4_ProtectedSampleDescription* sample_description,
const AP4_UI08* key,
AP4_Size key_size,
AP4_BlockCipherFactory* block_cipher_factory)
{
if (sample_description == NULL || key == NULL) return NULL;
// select the block cipher factory
if (block_cipher_factory == NULL) {
block_cipher_factory = &AP4_DefaultBlockCipherFactory::Instance;
}
switch(sample_description->GetSchemeType()) {
case AP4_PROTECTION_SCHEME_TYPE_OMA: {
AP4_OmaDcfSampleDecrypter* decrypter = NULL;
AP4_Result result = AP4_OmaDcfSampleDecrypter::Create(sample_description,
key,
key_size,
block_cipher_factory,
decrypter);
if (AP4_FAILED(result)) return NULL;
return decrypter;
}
case AP4_PROTECTION_SCHEME_TYPE_IAEC: {
AP4_IsmaCipher* decrypter = NULL;
AP4_Result result = AP4_IsmaCipher::CreateSampleDecrypter(sample_description,
key,
key_size,
block_cipher_factory,
decrypter);
if (AP4_FAILED(result)) return NULL;
return decrypter;
}
default:
return NULL;
}
// unreachable - return NULL;
}
/*----------------------------------------------------------------------
| AP4_SampleDecrypter:Create
+---------------------------------------------------------------------*/
AP4_SampleDecrypter*
AP4_SampleDecrypter::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)
{
if (sample_description == NULL || traf == NULL || key == NULL) return NULL;
// select the block cipher factory
if (block_cipher_factory == NULL) {
block_cipher_factory = &AP4_DefaultBlockCipherFactory::Instance;
}
switch(sample_description->GetSchemeType()) {
case AP4_PROTECTION_SCHEME_TYPE_PIFF:
case AP4_PROTECTION_SCHEME_TYPE_CENC: {
AP4_CencSampleDecrypter* decrypter = NULL;
AP4_Result result = AP4_CencSampleDecrypter::Create(sample_description,
traf,
aux_info_data,
aux_info_data_offset,
key,
key_size,
block_cipher_factory,
decrypter);
if (AP4_FAILED(result)) return NULL;
return decrypter;
}
default:
return NULL;
}
// unreachable - return NULL;
}
/*----------------------------------------------------------------------
| AP4_StandardDecryptingProcessor:AP4_StandardDecryptingProcessor
+---------------------------------------------------------------------*/
AP4_StandardDecryptingProcessor::AP4_StandardDecryptingProcessor(
const AP4_ProtectionKeyMap* key_map /* = NULL */,
AP4_BlockCipherFactory* block_cipher_factory /* = NULL */)
{
if (key_map) {
// copy the keys
m_KeyMap.SetKeys(*key_map);
}
if (block_cipher_factory == NULL) {
m_BlockCipherFactory = &AP4_DefaultBlockCipherFactory::Instance;
} else {
m_BlockCipherFactory = block_cipher_factory;
}
}
/*----------------------------------------------------------------------
| AP4_StandardDecryptingProcessor:Initialize
+---------------------------------------------------------------------*/
AP4_Result
AP4_StandardDecryptingProcessor::Initialize(AP4_AtomParent& top_level,
AP4_ByteStream& /*stream*/,
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 except for the ones we want to remove
AP4_Array<AP4_UI32> compatible_brands;
compatible_brands.EnsureCapacity(ftyp->GetCompatibleBrands().ItemCount());
for (unsigned int i=0; i<ftyp->GetCompatibleBrands().ItemCount(); i++) {
if (ftyp->GetCompatibleBrands()[i] != AP4_OMA_DCF_BRAND_OPF2) {
compatible_brands.Append(ftyp->GetCompatibleBrands()[i]);
}
}
// create a replacement for the major brand
top_level.AddChild(new AP4_FtypAtom(ftyp->GetMajorBrand(),
ftyp->GetMinorVersion(),
&compatible_brands[0],
compatible_brands.ItemCount()), 0);
delete ftyp;
}
return AP4_SUCCESS;
}
/*----------------------------------------------------------------------
| AP4_StandardDecryptingProcessor:CreateTrackHandler
+---------------------------------------------------------------------*/
AP4_Processor::TrackHandler*
AP4_StandardDecryptingProcessor::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 (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_OMA) {
const AP4_DataBuffer* key = m_KeyMap.GetKey(trak->GetId());
if (key) {
AP4_OmaDcfTrackDecrypter* handler = NULL;
AP4_Result result = AP4_OmaDcfTrackDecrypter::Create(key->GetData(),
key->GetDataSize(),
protected_desc,
entry,
m_BlockCipherFactory,
handler);
if (AP4_FAILED(result)) return NULL;
return handler;
}
} else if (protected_desc->GetSchemeType() == AP4_PROTECTION_SCHEME_TYPE_IAEC) {
const AP4_DataBuffer* key = m_KeyMap.GetKey(trak->GetId());
if (key) {
AP4_IsmaTrackDecrypter* handler = NULL;
AP4_Result result = AP4_IsmaTrackDecrypter::Create(key->GetData(),
key->GetDataSize(),
protected_desc,
entry,
m_BlockCipherFactory,
handler);
if (AP4_FAILED(result)) return NULL;
return handler;
}
}
}
return NULL;
}
/*----------------------------------------------------------------------
| AP4_DecryptingStream::AP4_DecryptingStream
+---------------------------------------------------------------------*/
AP4_Result
AP4_DecryptingStream::Create(AP4_BlockCipher::CipherMode mode,
AP4_ByteStream& encrypted_stream,
AP4_LargeSize cleartext_size,
const AP4_UI08* iv,
AP4_Size iv_size,
const AP4_UI08* key,
AP4_Size key_size,
AP4_BlockCipherFactory* block_cipher_factory,
AP4_ByteStream*& stream)
{
// default return value
stream = NULL;
// default cipher settings
if (block_cipher_factory == NULL) {
block_cipher_factory = &AP4_DefaultBlockCipherFactory::Instance;
}
// get the encrypted size (includes padding)
AP4_LargeSize encrypted_size = 0;
AP4_Result result = encrypted_stream.GetSize(encrypted_size);
if (AP4_FAILED(result)) return result;
// check IV
if (iv == NULL || iv_size != 16) return AP4_ERROR_INVALID_PARAMETERS;
// check that the encrypted size is consistent with the cipher mode
AP4_BlockCipher::CtrParams ctr_params;
const void* mode_params = NULL;
if (mode == AP4_BlockCipher::CBC) {
// we need at least 16 bytes of data+padding
// we also need a multiple of the block size
if (encrypted_size < 16 || ((encrypted_size % 16) != 0)) {
return AP4_ERROR_INVALID_FORMAT;
}
} else if (mode == AP4_BlockCipher::CTR) {
ctr_params.counter_size = 16;
mode_params = &ctr_params;
} else {
return AP4_ERROR_NOT_SUPPORTED;
}
// create the stream cipher
AP4_BlockCipher* block_cipher;
result = block_cipher_factory->CreateCipher(AP4_BlockCipher::AES_128,
AP4_BlockCipher::DECRYPT,
mode,
mode_params,
key,
key_size,
block_cipher);
if (AP4_FAILED(result)) return result;
// keep a reference to the source stream
encrypted_stream.AddReference();
// create the cipher according to the mode
AP4_StreamCipher* stream_cipher = NULL;
switch (mode) {
case AP4_BlockCipher::CBC:
stream_cipher = new AP4_CbcStreamCipher(block_cipher);
break;
case AP4_BlockCipher::CTR:
stream_cipher = new AP4_CtrStreamCipher(block_cipher, AP4_CIPHER_BLOCK_SIZE);
break;
default:
// should never occur
return AP4_ERROR_NOT_SUPPORTED;
}
// set the IV
stream_cipher->SetIV(iv);
// create the stream
stream = new AP4_DecryptingStream(cleartext_size,
&encrypted_stream,
encrypted_size,
stream_cipher);
return AP4_SUCCESS;
}
/*----------------------------------------------------------------------
| AP4_DecryptingStream::~AP4_DecryptingStream
+---------------------------------------------------------------------*/
AP4_DecryptingStream::~AP4_DecryptingStream()
{
delete m_StreamCipher;
m_EncryptedStream->Release();
}
/*----------------------------------------------------------------------
| AP4_DecryptingStream::AddReference
+---------------------------------------------------------------------*/
void
AP4_DecryptingStream::AddReference()
{
++m_ReferenceCount;
}
/*----------------------------------------------------------------------
| AP4_DecryptingStream::Release
+---------------------------------------------------------------------*/
void
AP4_DecryptingStream::Release()
{
if (--m_ReferenceCount == 0) delete this;
}
/*----------------------------------------------------------------------
| AP4_DecryptingStream::ReadPartial
+---------------------------------------------------------------------*/
AP4_Result
AP4_DecryptingStream::ReadPartial(void* buffer,
AP4_Size bytes_to_read,
AP4_Size& bytes_read)
{
bytes_read = 0;
// never read more than what's available
AP4_LargeSize available = m_CleartextSize-m_CleartextPosition;
if (available < bytes_to_read) {
if (available == 0) {
return AP4_ERROR_EOS;
}
bytes_to_read = (AP4_Size)available;
}
if (m_BufferFullness) {
// we have some leftovers
AP4_Size chunk = bytes_to_read;
if (chunk > m_BufferFullness) chunk = m_BufferFullness;
AP4_CopyMemory(buffer, &m_Buffer[m_BufferOffset], chunk);
buffer = (char*)buffer+chunk;
m_CleartextPosition += chunk;
available -= chunk;
bytes_to_read -= chunk;
m_BufferFullness -= chunk;
m_BufferOffset += chunk;
bytes_read += chunk;
}
if (bytes_to_read == 0) return AP4_SUCCESS;
// seek to the right place in the input
m_EncryptedStream->Seek(m_EncryptedPosition);
while (bytes_to_read) {
// read from the source
AP4_UI08 encrypted[1024];
AP4_Size encrypted_read = 0;
AP4_Result result = m_EncryptedStream->ReadPartial(encrypted, sizeof(encrypted), encrypted_read);
if (result == AP4_ERROR_EOS) {
if (bytes_read == 0) {
return AP4_ERROR_EOS;
} else {
return AP4_SUCCESS;
}
} else if (result != AP4_SUCCESS) {
return result;
} else {
m_EncryptedPosition += encrypted_read;
}
bool is_last_buffer = (m_EncryptedPosition >= m_EncryptedSize);
AP4_Size buffer_size = sizeof(m_Buffer);
result = m_StreamCipher->ProcessBuffer(encrypted,
encrypted_read,
m_Buffer,
&buffer_size,
is_last_buffer);
if (AP4_FAILED(result)) return result;
m_BufferOffset = 0;
m_BufferFullness = buffer_size;
AP4_Size chunk = bytes_to_read;
if (chunk > m_BufferFullness) chunk = m_BufferFullness;
if (chunk) AP4_CopyMemory(buffer, &m_Buffer[m_BufferOffset], chunk);
buffer = (char*)buffer+chunk;
m_CleartextPosition += chunk;
available -= chunk;
bytes_to_read -= chunk;
m_BufferFullness -= chunk;
m_BufferOffset += chunk;
bytes_read += chunk;
}
return AP4_SUCCESS;
}
/*----------------------------------------------------------------------
| AP4_DecryptingStream::WritePartial
+---------------------------------------------------------------------*/
AP4_Result
AP4_DecryptingStream::WritePartial(const void* /* buffer */,
AP4_Size /* bytes_to_write */,
AP4_Size& /* bytes_written */)
{
return AP4_ERROR_NOT_SUPPORTED;
}
/*----------------------------------------------------------------------
| AP4_DecryptingStream::Seek
+---------------------------------------------------------------------*/
AP4_Result
AP4_DecryptingStream::Seek(AP4_Position position)
{
AP4_Cardinal preroll = 0;
// check for no-op seek requests
if (position == m_CleartextPosition) {
return AP4_SUCCESS;
}
// check bounds
if (position > m_CleartextSize) {
return AP4_ERROR_INVALID_PARAMETERS;
}
// try to put the stream cipher at the right offset
AP4_CHECK(m_StreamCipher->SetStreamOffset(position, &preroll));
// seek in the source stream
AP4_CHECK(m_EncryptedStream->Seek(position-preroll));
// if we need to, process the preroll bytes
if (preroll > 0) {
AP4_Size out_size = 0;
AP4_UI08 buffer[2*AP4_CIPHER_BLOCK_SIZE]; // bigger than preroll
AP4_CHECK(m_EncryptedStream->Read(buffer, preroll));
AP4_CHECK(m_StreamCipher->ProcessBuffer(buffer, preroll, buffer, &out_size));
AP4_ASSERT(out_size == 0); // we're just feeding prerolled bytes,
// there can be no output
}
// update the counters
m_CleartextPosition = position;
m_EncryptedPosition = position;
m_BufferFullness = 0;
m_BufferOffset = 0;
return AP4_SUCCESS;
}
/*----------------------------------------------------------------------
| AP4_DecryptingStream::Tell
+---------------------------------------------------------------------*/
AP4_Result
AP4_DecryptingStream::Tell(AP4_Position& position)
{
position = m_CleartextPosition;
return AP4_SUCCESS;
}
/*----------------------------------------------------------------------
| AP4_DecryptingStream::GetSize
+---------------------------------------------------------------------*/
AP4_Result
AP4_DecryptingStream::GetSize(AP4_LargeSize& size)
{
size = m_CleartextSize;
return AP4_SUCCESS;
}
/*----------------------------------------------------------------------
| AP4_EncryptingStream::AP4_EncryptingStream
+---------------------------------------------------------------------*/
AP4_Result
AP4_EncryptingStream::Create(AP4_BlockCipher::CipherMode mode,
AP4_ByteStream& cleartext_stream,
const AP4_UI08* iv,
AP4_Size iv_size,
const AP4_UI08* key,
AP4_Size key_size,
bool prepend_iv,
AP4_BlockCipherFactory* block_cipher_factory,
AP4_ByteStream*& stream)
{
// default return value
stream = NULL;
// get the cleartext size
AP4_LargeSize cleartext_size = 0;
AP4_Result result = cleartext_stream.GetSize(cleartext_size);
if (AP4_FAILED(result)) return result;
// check IV
if (iv == NULL || iv_size != 16) return AP4_ERROR_INVALID_PARAMETERS;
// compute the encrypted size and set cipher params
AP4_LargeSize encrypted_size = cleartext_size;
const void* mode_params = NULL;
AP4_BlockCipher::CtrParams ctr_params;
if (mode == AP4_BlockCipher::CBC) {
encrypted_size += (16-(cleartext_size%16)); // with padding
} else {
ctr_params.counter_size = 16;
mode_params = &ctr_params;
}
// create the stream cipher
AP4_BlockCipher* block_cipher;
result = block_cipher_factory->CreateCipher(AP4_BlockCipher::AES_128,
AP4_BlockCipher::ENCRYPT,
mode,
mode_params,
key,
key_size,
block_cipher);
if (AP4_FAILED(result)) return result;
// keep a reference to the source stream
cleartext_stream.AddReference();
// create the cipher according to the mode
AP4_StreamCipher* stream_cipher = NULL;
switch (mode) {
case AP4_BlockCipher::CBC:
stream_cipher = new AP4_CbcStreamCipher(block_cipher);
break;
case AP4_BlockCipher::CTR:
stream_cipher = new AP4_CtrStreamCipher(block_cipher, AP4_CIPHER_BLOCK_SIZE);
break;
default:
// should never occur
AP4_ASSERT(0);
}
// set the IV
stream_cipher->SetIV(iv);
// create the stream
AP4_EncryptingStream* enc_stream = new AP4_EncryptingStream(cleartext_size,
&cleartext_stream,
encrypted_size,
stream_cipher);
stream = enc_stream;
// deal with the prepended IV if required
if (prepend_iv) {
enc_stream->m_EncryptedSize += 16;
enc_stream->m_BufferFullness = 16;
AP4_CopyMemory(enc_stream->m_Buffer, iv, 16);
}
return AP4_SUCCESS;
}
/*----------------------------------------------------------------------
| AP4_EncryptingStream::~AP4_EncryptingStream
+---------------------------------------------------------------------*/
AP4_EncryptingStream::~AP4_EncryptingStream()
{
delete m_StreamCipher;
m_CleartextStream->Release();
}
/*----------------------------------------------------------------------
| AP4_EncryptingStream::AddReference
+---------------------------------------------------------------------*/
void
AP4_EncryptingStream::AddReference()
{
++m_ReferenceCount;
}
/*----------------------------------------------------------------------
| AP4_EncryptingStream::Release
+---------------------------------------------------------------------*/
void
AP4_EncryptingStream::Release()
{
if (--m_ReferenceCount == 0) delete this;
}
/*----------------------------------------------------------------------
| AP4_EncryptingStream::ReadPartial
+---------------------------------------------------------------------*/
AP4_Result
AP4_EncryptingStream::ReadPartial(void* buffer,
AP4_Size bytes_to_read,
AP4_Size& bytes_read)
{
bytes_read = 0;
// never read more than what's available
AP4_LargeSize available = m_EncryptedSize-m_EncryptedPosition;
if (available < bytes_to_read) {
if (available == 0) return AP4_ERROR_EOS;
bytes_to_read = (AP4_Size)available;
}
if (m_BufferFullness) {
// we have some leftovers
AP4_Size chunk = bytes_to_read;
if (chunk > m_BufferFullness) chunk = m_BufferFullness;
AP4_CopyMemory(buffer, &m_Buffer[m_BufferOffset], chunk);
buffer = (char*)buffer+chunk;
m_EncryptedPosition += chunk;
available -= chunk;
bytes_to_read -= chunk;
m_BufferFullness -= chunk;
m_BufferOffset += chunk;
bytes_read += chunk;
}
// seek to the right place in the input
m_CleartextStream->Seek(m_CleartextPosition);
while (bytes_to_read) {
// read from the source
AP4_UI08 cleartext[1024];
AP4_Size cleartext_read = 0;
AP4_Result result = m_CleartextStream->ReadPartial(cleartext, sizeof(cleartext), cleartext_read);
if (result == AP4_ERROR_EOS) {
if (bytes_read == 0) {
return AP4_ERROR_EOS;
} else {
return AP4_SUCCESS;
}
} else if (result != AP4_SUCCESS) {
return result;
} else {
m_CleartextPosition += cleartext_read;
}
bool is_last_buffer = (m_CleartextPosition >= m_CleartextSize);
AP4_Size buffer_size = sizeof(m_Buffer);
result = m_StreamCipher->ProcessBuffer(cleartext,
cleartext_read,
m_Buffer,
&buffer_size,
is_last_buffer);
if (AP4_FAILED(result)) return result;
m_BufferOffset = 0;
m_BufferFullness = buffer_size;
AP4_Size chunk = bytes_to_read;
if (chunk > m_BufferFullness) chunk = m_BufferFullness;
if (chunk) {
AP4_CopyMemory(buffer, &m_Buffer[m_BufferOffset], chunk);
buffer = (char*)buffer+chunk;
m_EncryptedPosition += chunk;
available -= chunk;
bytes_to_read -= chunk;
m_BufferFullness -= chunk;
m_BufferOffset += chunk;
bytes_read += chunk;
}
}
return AP4_SUCCESS;
}
/*----------------------------------------------------------------------
| AP4_EncryptingStream::WritePartial
+---------------------------------------------------------------------*/
AP4_Result
AP4_EncryptingStream::WritePartial(const void* /* buffer */,
AP4_Size /* bytes_to_write */,
AP4_Size& /* bytes_written */)
{
return AP4_ERROR_NOT_SUPPORTED;
}
/*----------------------------------------------------------------------
| AP4_EncryptingStream::Seek
+---------------------------------------------------------------------*/
AP4_Result
AP4_EncryptingStream::Seek(AP4_Position position)
{
if (position == m_EncryptedPosition) {
return AP4_SUCCESS;
} else {
return AP4_ERROR_NOT_SUPPORTED;
}
}
/*----------------------------------------------------------------------
| AP4_EncryptingStream::Tell
+---------------------------------------------------------------------*/
AP4_Result
AP4_EncryptingStream::Tell(AP4_Position& position)
{
position = m_EncryptedPosition;
return AP4_SUCCESS;
}
/*----------------------------------------------------------------------
| AP4_EncryptingStream::GetSize
+---------------------------------------------------------------------*/
AP4_Result
AP4_EncryptingStream::GetSize(AP4_LargeSize& size)
{
size = m_EncryptedSize;
return AP4_SUCCESS;
}
/*----------------------------------------------------------------------
| AP4_DefaultBlockCipherFactory::Instance
+---------------------------------------------------------------------*/
AP4_DefaultBlockCipherFactory AP4_DefaultBlockCipherFactory::Instance;
/*----------------------------------------------------------------------
| AP4_DefaultBlockCipherFactory
+---------------------------------------------------------------------*/
AP4_Result
AP4_DefaultBlockCipherFactory::CreateCipher(AP4_BlockCipher::CipherType type,
AP4_BlockCipher::CipherDirection direction,
AP4_BlockCipher::CipherMode mode,
const void* mode_params,
const AP4_UI08* key,
AP4_Size key_size,
AP4_BlockCipher*& cipher)
{
// setup default return vaule
cipher = NULL;
switch (type) {
case AP4_BlockCipher::AES_128:
// check cipher parameters
if (key == NULL || key_size != AP4_AES_BLOCK_SIZE) {
return AP4_ERROR_INVALID_PARAMETERS;
}
// create the cipher
{
AP4_AesBlockCipher* aes_cipher = NULL;
AP4_Result result = AP4_AesBlockCipher::Create(key, direction, mode, mode_params, aes_cipher);
if (AP4_FAILED(result)) return result;
cipher = aes_cipher;
return AP4_SUCCESS;
}
default:
// not supported
return AP4_ERROR_NOT_SUPPORTED;
}
}
/*----------------------------------------------------------------------
| AP4_DefaultBlockCipherFactory::AP4_DefaultBlockCipherFactory
+---------------------------------------------------------------------*/
AP4_DefaultBlockCipherFactory::AP4_DefaultBlockCipherFactory() :
m_Initialized(true)
{
}
/*----------------------------------------------------------------------
| AP4_DefaultBlockCipherFactory::Initialize
+---------------------------------------------------------------------*/
AP4_Result
AP4_DefaultBlockCipherFactory::Initialize()
{
m_Initialized = true;
return AP4_SUCCESS;
}