/*---------------------------------------------------------------------- | AP4_Track::GetHandlerType +---------------------------------------------------------------------*/ AP4_UI32 AP4_Track::GetHandlerType() { if (m_TrakAtom) { AP4_HdlrAtom* hdlr = AP4_DYNAMIC_CAST(AP4_HdlrAtom, m_TrakAtom->FindChild("mdia/hdlr")); if (hdlr) { return hdlr->GetHandlerType(); } } return 0; }
/*---------------------------------------------------------------------- | AP4_Track::AP4_Track +---------------------------------------------------------------------*/ AP4_Track::AP4_Track(AP4_TrakAtom& atom, AP4_ByteStream& sample_stream, AP4_UI32 movie_time_scale) : m_TrakAtom(&atom), m_TrakAtomIsOwned(false), m_Type(TYPE_UNKNOWN), m_SampleTable(NULL), m_SampleTableIsOwned(true), m_MovieTimeScale(movie_time_scale), m_MediaTimeScale(0) { // find the handler type AP4_Atom* sub = atom.FindChild("mdia/hdlr"); if (sub) { AP4_HdlrAtom* hdlr = dynamic_cast<AP4_HdlrAtom*>(sub); if (hdlr) { AP4_Atom::Type type = hdlr->GetHandlerType(); if (type == AP4_HANDLER_TYPE_SOUN) { m_Type = TYPE_AUDIO; } else if (type == AP4_HANDLER_TYPE_VIDE) { m_Type = TYPE_VIDEO; } else if (type == AP4_HANDLER_TYPE_TEXT) { m_Type = TYPE_TEXT; } else if (type == AP4_HANDLER_TYPE_TX3G) { m_Type = TYPE_TEXT; } else if (type == AP4_HANDLER_TYPE_SUBP) { m_Type = TYPE_SUBP; } else if (type == AP4_HANDLER_TYPE_HINT) { m_Type = TYPE_HINT; } } } // get the media time scale sub = atom.FindChild("mdia/mdhd"); if (sub) { AP4_MdhdAtom* mdhd = dynamic_cast<AP4_MdhdAtom*>(sub); if (mdhd) { m_MediaTimeScale = mdhd->GetTimeScale(); } } // create a facade for the stbl atom AP4_ContainerAtom* stbl = dynamic_cast<AP4_ContainerAtom*>( atom.FindChild("mdia/minf/stbl")); if (stbl) { m_SampleTable = DNew AP4_AtomSampleTable(stbl, sample_stream); } }
/*---------------------------------------------------------------------- | AP4_Track::AP4_Track +---------------------------------------------------------------------*/ AP4_Track::AP4_Track(AP4_TrakAtom& atom, AP4_ByteStream& sample_stream, AP4_UI32 movie_time_scale) : m_TrakAtom(&atom), m_TrakAtomIsOwned(false), m_Type(TYPE_UNKNOWN), m_SampleTable(NULL), m_SampleTableIsOwned(true), m_MovieTimeScale(movie_time_scale) { // find the handler type AP4_Atom* sub = atom.FindChild("mdia/hdlr"); if (sub) { AP4_HdlrAtom* hdlr = AP4_DYNAMIC_CAST(AP4_HdlrAtom, sub); if (hdlr) { AP4_UI32 type = hdlr->GetHandlerType(); if (type == AP4_HANDLER_TYPE_SOUN) { m_Type = TYPE_AUDIO; } else if (type == AP4_HANDLER_TYPE_VIDE) { m_Type = TYPE_VIDEO; } else if (type == AP4_HANDLER_TYPE_HINT) { m_Type = TYPE_HINT; } else if (type == AP4_HANDLER_TYPE_ODSM || type == AP4_HANDLER_TYPE_SDSM) { m_Type = TYPE_SYSTEM; } else if (type == AP4_HANDLER_TYPE_TEXT || type == AP4_HANDLER_TYPE_TX3G) { m_Type = TYPE_TEXT; } else if (type == AP4_HANDLER_TYPE_JPEG) { m_Type = TYPE_JPEG; } else if (type == AP4_HANDLER_TYPE_SUBT) { m_Type = TYPE_SUBTITLES; } } } // create a facade for the stbl atom AP4_ContainerAtom* stbl = AP4_DYNAMIC_CAST(AP4_ContainerAtom, atom.FindChild("mdia/minf/stbl")); if (stbl) { m_SampleTable = new AP4_AtomSampleTable(stbl, sample_stream); } }
/*---------------------------------------------------------------------- | AP4_OmaDcfEncryptingProcessor:CreateTrackHandler +---------------------------------------------------------------------*/ AP4_Processor::TrackHandler* AP4_OmaDcfEncryptingProcessor::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; AP4_UI32 format = 0; if (AP4_SUCCEEDED(m_KeyMap.GetKeyAndIv(trak->GetId(), key, iv))) { switch (entry->GetType()) { case AP4_ATOM_TYPE_MP4A: format = AP4_ATOM_TYPE_ENCA; break; case AP4_ATOM_TYPE_MP4V: case AP4_ATOM_TYPE_AVC1: case AP4_ATOM_TYPE_AVC2: case AP4_ATOM_TYPE_AVC3: case AP4_ATOM_TYPE_AVC4: case AP4_ATOM_TYPE_HEV1: case AP4_ATOM_TYPE_HVC1: 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) { const char* content_id = m_PropertyMap.GetProperty(trak->GetId(), "ContentId"); const char* rights_issuer_url = m_PropertyMap.GetProperty(trak->GetId(), "RightsIssuerUrl"); AP4_DataBuffer textual_headers; AP4_Result result = m_PropertyMap.GetTextualHeaders(trak->GetId(), textual_headers); if (AP4_FAILED(result)) textual_headers.SetDataSize(0); // create the block cipher AP4_BlockCipher* block_cipher = NULL; AP4_BlockCipher::CtrParams ctr_params; AP4_BlockCipher::CipherMode cipher_mode; const void* cipher_params = NULL; if (m_CipherMode == AP4_OMA_DCF_CIPHER_MODE_CBC) { cipher_mode = AP4_BlockCipher::CBC; } else if (m_CipherMode == AP4_OMA_DCF_CIPHER_MODE_CTR) { cipher_mode = AP4_BlockCipher::CTR; ctr_params.counter_size = 16; cipher_params = &ctr_params; } else { return NULL; } result = m_BlockCipherFactory->CreateCipher(AP4_BlockCipher::AES_128, AP4_BlockCipher::ENCRYPT, cipher_mode, cipher_params, key->GetData(), key->GetDataSize(), block_cipher); if (AP4_FAILED(result)) return NULL; return new AP4_OmaDcfTrackEncrypter(m_CipherMode, block_cipher, iv->GetData(), entry, format, content_id, rights_issuer_url, textual_headers.GetData(), textual_headers.GetDataSize()); } } return NULL; }
/*---------------------------------------------------------------------- | AP4_MarlinIpmpEncryptingProcessor::Initialize +---------------------------------------------------------------------*/ AP4_Result AP4_MarlinIpmpEncryptingProcessor::Initialize( AP4_AtomParent& top_level, AP4_ByteStream& /*stream*/, AP4_Processor::ProgressListener* /*listener = NULL*/) { // get the moov atom AP4_MoovAtom* moov = AP4_DYNAMIC_CAST(AP4_MoovAtom, top_level.GetChild(AP4_ATOM_TYPE_MOOV)); if (moov == NULL) return AP4_ERROR_INVALID_FORMAT; // deal with the file type 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 MGSV compatible brand if it is not already there if (!ftyp->HasCompatibleBrand(AP4_MARLIN_BRAND_MGSV)) { compatible_brands.Append(AP4_MARLIN_BRAND_MGSV); } // create a replacement for the major brand AP4_FtypAtom* new_ftyp = new AP4_FtypAtom(AP4_MARLIN_BRAND_MGSV, 0x13c078c, //AP4_MARLIN_BRAND_MGSV_MAJOR_VERSION, &compatible_brands[0], compatible_brands.ItemCount()); delete ftyp; ftyp = new_ftyp; } else { AP4_UI32 isom = AP4_FTYP_BRAND_ISOM; ftyp = new AP4_FtypAtom(AP4_MARLIN_BRAND_MGSV, 0, &isom, 1); } // insert the ftyp atom as the first child top_level.AddChild(ftyp, 0); // create and 'mpod' track reference atom AP4_TrefTypeAtom* mpod = new AP4_TrefTypeAtom(AP4_ATOM_TYPE_MPOD); // look for an available track ID, starting at 1 unsigned int od_track_id = 0; unsigned int od_track_position = 0; for (AP4_List<AP4_TrakAtom>::Item* trak_item = moov->GetTrakAtoms().FirstItem(); trak_item; trak_item = trak_item->GetNext()) { AP4_TrakAtom* trak = trak_item->GetData(); if (trak) { od_track_position++; if (trak->GetId() >= od_track_id) { od_track_id = trak->GetId()+1; } // if the track is encrypted, reference it in the mpod if (m_KeyMap.GetKey(trak->GetId())) { mpod->AddTrackId(trak->GetId()); } //m_SinfEntries.Add(new SinfEntry(trak->GetId(), NULL)); } } // check that there was at least one track in the file if (od_track_id == 0) return AP4_ERROR_INVALID_FORMAT; // create an initial object descriptor AP4_InitialObjectDescriptor* iod = // FIXME: get real values from the property map new AP4_InitialObjectDescriptor(AP4_DESCRIPTOR_TAG_MP4_IOD, 1022, // object descriptor id false, 0xFE, // OD profile level (0xFE = No OD profile specified) 0xFF, // scene profile level 0xFE, // audio profile level 0xFE, // visual profile level 0xFF); // graphics profile // create an ES_ID_Inc subdescriptor and add it to the initial object descriptor AP4_EsIdIncDescriptor* es_id_inc = new AP4_EsIdIncDescriptor(od_track_id); iod->AddSubDescriptor(es_id_inc); // create an iods atom to hold the initial object descriptor AP4_IodsAtom* iods = new AP4_IodsAtom(iod); // add the iods atom to the moov atom (try to put it just after mvhd) int iods_position = 0; int item_position = 0; for (AP4_List<AP4_Atom>::Item* moov_item = moov->GetChildren().FirstItem(); moov_item; moov_item = moov_item->GetNext()) { ++item_position; if (moov_item->GetData()->GetType() == AP4_ATOM_TYPE_MVHD) { iods_position = item_position; break; } } AP4_Result result = moov->AddChild(iods, iods_position); if (AP4_FAILED(result)) { delete iods; return result; } // create a sample table for the OD track AP4_SyntheticSampleTable* od_sample_table = new AP4_SyntheticSampleTable(); // create the sample description for the OD track AP4_MpegSystemSampleDescription* od_sample_description; od_sample_description = new AP4_MpegSystemSampleDescription(AP4_STREAM_TYPE_OD, AP4_OTI_MPEG4_SYSTEM, NULL, 32768, // buffer size 1024, // max bitrate 512); // avg bitrate od_sample_table->AddSampleDescription(od_sample_description, true); // create the OD descriptor update AP4_DescriptorUpdateCommand od_update(AP4_COMMAND_TAG_OBJECT_DESCRIPTOR_UPDATE); for (unsigned int i=0; i<mpod->GetTrackIds().ItemCount(); i++) { AP4_ObjectDescriptor* od = new AP4_ObjectDescriptor(AP4_DESCRIPTOR_TAG_MP4_OD, 256+i); // descriptor id = 256+i od->AddSubDescriptor(new AP4_EsIdRefDescriptor(i+1)); // index into mpod (1-based) od->AddSubDescriptor(new AP4_IpmpDescriptorPointer(i+1)); // descriptor id = i+1 od_update.AddDescriptor(od); } // create the IPMP descriptor update AP4_DescriptorUpdateCommand ipmp_update(AP4_COMMAND_TAG_IPMP_DESCRIPTOR_UPDATE); for (unsigned int i=0; i<mpod->GetTrackIds().ItemCount(); i++) { // create the ipmp descriptor AP4_IpmpDescriptor* ipmp_descriptor = new AP4_IpmpDescriptor(i+1, AP4_MARLIN_IPMPS_TYPE_MGSV); // create the sinf container AP4_ContainerAtom* sinf = new AP4_ContainerAtom(AP4_ATOM_TYPE_SINF); // add the scheme type atom sinf->AddChild(new AP4_SchmAtom(m_UseGroupKey? AP4_PROTECTION_SCHEME_TYPE_MARLIN_ACGK: AP4_PROTECTION_SCHEME_TYPE_MARLIN_ACBC, 0x0100, NULL, true)); // create the 'schi' container AP4_ContainerAtom* schi = new AP4_ContainerAtom(AP4_ATOM_TYPE_SCHI); // add the content ID const char* content_id = m_PropertyMap.GetProperty(mpod->GetTrackIds()[i], "ContentId"); if (content_id) { // add the content ID (8id_) schi->AddChild(new AP4_NullTerminatedStringAtom(AP4_ATOM_TYPE_8ID_, content_id)); } // find what the track type is (necessary for the next step) and the key const AP4_DataBuffer* key = NULL; AP4_Track::Type track_type = AP4_Track::TYPE_UNKNOWN; for (AP4_List<AP4_TrakAtom>::Item* trak_item = moov->GetTrakAtoms().FirstItem(); trak_item; trak_item = trak_item->GetNext()) { AP4_TrakAtom* trak = trak_item->GetData(); if (trak->GetId() == mpod->GetTrackIds()[i]) { // find the handler type AP4_Atom* sub = trak->FindChild("mdia/hdlr"); if (sub) { AP4_HdlrAtom* hdlr = AP4_DYNAMIC_CAST(AP4_HdlrAtom, sub); if (hdlr) { AP4_UI32 type = hdlr->GetHandlerType(); if (type == AP4_HANDLER_TYPE_SOUN) { track_type = AP4_Track::TYPE_AUDIO; } else if (type == AP4_HANDLER_TYPE_VIDE) { track_type = AP4_Track::TYPE_VIDEO; } } } // find the key key = m_KeyMap.GetKey(trak->GetId()); break; } } // group key if (m_UseGroupKey && key) { // find the group key const AP4_DataBuffer* group_key = m_KeyMap.GetKey(0); if (group_key) { AP4_DataBuffer wrapped_key; result = AP4_AesKeyWrap(group_key->GetData(), key->GetData(), key->GetDataSize(), wrapped_key); if (AP4_FAILED(result)) return result; AP4_UnknownAtom* gkey = new AP4_UnknownAtom(AP4_ATOM_TYPE_GKEY, wrapped_key.GetData(), wrapped_key.GetDataSize()); schi->AddChild(gkey); } } // create and add the security attributes (satr) if (track_type != AP4_Track::TYPE_UNKNOWN && key != NULL && key != NULL) { AP4_ContainerAtom* satr = new AP4_ContainerAtom(AP4_ATOM_TYPE_SATR); switch (track_type) { case AP4_Track::TYPE_AUDIO: satr->AddChild(new AP4_NullTerminatedStringAtom(AP4_ATOM_TYPE_STYP, AP4_MARLIN_IPMP_STYP_AUDIO)); break; case AP4_Track::TYPE_VIDEO: satr->AddChild(new AP4_NullTerminatedStringAtom(AP4_ATOM_TYPE_STYP, AP4_MARLIN_IPMP_STYP_VIDEO)); break; default: break; } // add the signed attributes, if any const char* signed_attributes = m_PropertyMap.GetProperty(mpod->GetTrackIds()[i], "SignedAttributes"); if (signed_attributes) { // decode the hex-encoded data unsigned int size = (unsigned int)AP4_StringLength(signed_attributes)/2; AP4_DataBuffer attributes_atoms; attributes_atoms.SetDataSize(size); if (AP4_SUCCEEDED(AP4_ParseHex(signed_attributes, attributes_atoms.UseData(), size))) { // parse all the atoms encoded in the data and add them to the 'schi' container AP4_MemoryByteStream* mbs = new AP4_MemoryByteStream(attributes_atoms.GetData(), attributes_atoms.GetDataSize()); do { AP4_Atom* atom = NULL; result = AP4_DefaultAtomFactory::Instance.CreateAtomFromStream(*mbs, atom); if (AP4_SUCCEEDED(result) && atom) { satr->AddChild(atom); } } while (AP4_SUCCEEDED(result)); mbs->Release(); } } // compute the hmac AP4_MemoryByteStream* mbs = new AP4_MemoryByteStream(); satr->Write(*mbs); AP4_Hmac* digester = NULL; AP4_Hmac::Create(AP4_Hmac::SHA256, key->GetData(), key->GetDataSize(), digester); digester->Update(mbs->GetData(), mbs->GetDataSize()); AP4_DataBuffer hmac_value; digester->Final(hmac_value); AP4_Atom* hmac = new AP4_UnknownAtom(AP4_ATOM_TYPE_HMAC, hmac_value.GetData(), hmac_value.GetDataSize()); schi->AddChild(satr); schi->AddChild(hmac); mbs->Release(); } sinf->AddChild(schi); // serialize the sinf atom to a buffer and set it as the ipmp data AP4_MemoryByteStream* sinf_data = new AP4_MemoryByteStream((AP4_Size)sinf->GetSize()); sinf->Write(*sinf_data); ipmp_descriptor->SetData(sinf_data->GetData(), sinf_data->GetDataSize()); sinf_data->Release(); ipmp_update.AddDescriptor(ipmp_descriptor); } // add the sample with the descriptors and updates AP4_MemoryByteStream* sample_data = new AP4_MemoryByteStream(); od_update.Write(*sample_data); ipmp_update.Write(*sample_data); od_sample_table->AddSample(*sample_data, 0, sample_data->GetDataSize(), 0, 0, 0, 0, true); // create the OD track AP4_TrakAtom* od_track = new AP4_TrakAtom(od_sample_table, AP4_HANDLER_TYPE_ODSM, "Bento4 Marlin OD Handler", od_track_id, 0, 0, 1, 1000, 1, 0, "und", 0, 0); // add an entry in the processor's stream table to indicate that the // media data for the OD track is not in the file stream, but in our // memory stream. m_ExternalTrackData.Add(new ExternalTrackData(od_track_id, sample_data)); sample_data->Release(); // add a tref track reference atom AP4_ContainerAtom* tref = new AP4_ContainerAtom(AP4_ATOM_TYPE_TREF); tref->AddChild(mpod); od_track->AddChild(tref, 1); // add after 'tkhd' // add the track to the moov atoms (just after the last track) moov->AddChild(od_track, od_track_position); return AP4_SUCCESS; }