/*---------------------------------------------------------------------- | AP4_OmaDcfAtomDecrypter::DecryptAtoms +---------------------------------------------------------------------*/ AP4_Result AP4_OmaDcfAtomDecrypter::DecryptAtoms(AP4_AtomParent& atoms, AP4_Processor::ProgressListener* /*listener*/, AP4_BlockCipherFactory* block_cipher_factory, AP4_ProtectionKeyMap& key_map) { // default factory if (block_cipher_factory == NULL) { block_cipher_factory = &AP4_DefaultBlockCipherFactory::Instance; } unsigned int index = 1; for (AP4_List<AP4_Atom>::Item* item = atoms.GetChildren().FirstItem(); item; item = item->GetNext()) { AP4_Atom* atom = item->GetData(); if (atom->GetType() != AP4_ATOM_TYPE_ODRM) continue; // check that we have the key const AP4_DataBuffer* key = key_map.GetKey(index++); if (key == NULL) return AP4_ERROR_INVALID_PARAMETERS; // check that we have all the atoms we need AP4_ContainerAtom* odrm = AP4_DYNAMIC_CAST(AP4_ContainerAtom, atom); if (odrm == NULL) continue; // not enough info AP4_OdheAtom* odhe = AP4_DYNAMIC_CAST(AP4_OdheAtom, odrm->GetChild(AP4_ATOM_TYPE_ODHE)); if (odhe == NULL) continue; // not enough info AP4_OddaAtom* odda = AP4_DYNAMIC_CAST(AP4_OddaAtom, odrm->GetChild(AP4_ATOM_TYPE_ODDA));; if (odda == NULL) continue; // not enough info AP4_OhdrAtom* ohdr = AP4_DYNAMIC_CAST(AP4_OhdrAtom, odhe->GetChild(AP4_ATOM_TYPE_OHDR)); if (ohdr == NULL) continue; // not enough info // do nothing if the atom is not encrypted if (ohdr->GetEncryptionMethod() == AP4_OMA_DCF_ENCRYPTION_METHOD_NULL) { continue; } // create the byte stream AP4_ByteStream* cipher_stream = NULL; AP4_Result result = CreateDecryptingStream(*odrm, key->GetData(), key->GetDataSize(), block_cipher_factory, cipher_stream); if (AP4_SUCCEEDED(result)) { // replace the odda atom's payload with the decrypted stream odda->SetEncryptedPayload(*cipher_stream, ohdr->GetPlaintextLength()); cipher_stream->Release(); // the atom will now be in the clear ohdr->SetEncryptionMethod(AP4_OMA_DCF_ENCRYPTION_METHOD_NULL); ohdr->SetPaddingScheme(AP4_OMA_DCF_PADDING_SCHEME_NONE); } } return AP4_SUCCESS; }
/*---------------------------------------------------------------------- | AP4_OmaDcfEncryptingProcessor::Initialize +---------------------------------------------------------------------*/ AP4_Result AP4_OmaDcfEncryptingProcessor::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 OMA compatible brand if it is not already there if (!ftyp->HasCompatibleBrand(AP4_OMA_DCF_BRAND_OPF2)) { compatible_brands.Append(AP4_OMA_DCF_BRAND_OPF2); } // 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 opf2 = AP4_OMA_DCF_BRAND_OPF2; ftyp = new AP4_FtypAtom(AP4_FTYP_BRAND_ISOM, 0, &opf2, 1); } // insert the ftyp atom as the first child return top_level.AddChild(ftyp, 0); }
/*---------------------------------------------------------------------- | AP4_AtomFactory::CreateAtomsFromStream +---------------------------------------------------------------------*/ AP4_Result AP4_AtomFactory::CreateAtomsFromStream(AP4_ByteStream& stream, AP4_LargeSize bytes_available, AP4_AtomParent& atoms) { AP4_Result result; do { AP4_Atom* atom = NULL; result = CreateAtomFromStream(stream, bytes_available, atom); if (AP4_SUCCEEDED(result) && atom != NULL) { atoms.AddChild(atom); } } while (AP4_SUCCEEDED(result)); return AP4_SUCCESS; }
/*---------------------------------------------------------------------- | AP4_OmaDcfDecryptingProcessor:Initialize +---------------------------------------------------------------------*/ AP4_Result AP4_OmaDcfDecryptingProcessor::Initialize(AP4_AtomParent& top_level, AP4_ByteStream& /* stream */, ProgressListener* listener) { // decide which processor to instantiate based on the file type AP4_FtypAtom* ftyp = AP4_DYNAMIC_CAST(AP4_FtypAtom, top_level.GetChild(AP4_ATOM_TYPE_FTYP)); if (ftyp) { if (ftyp->GetMajorBrand() == AP4_OMA_DCF_BRAND_ODCF || ftyp->HasCompatibleBrand(AP4_OMA_DCF_BRAND_ODCF)) { return AP4_OmaDcfAtomDecrypter::DecryptAtoms(top_level, listener, m_BlockCipherFactory, m_KeyMap); } else { return AP4_ERROR_INVALID_FORMAT; } } else { return AP4_SUCCESS; } }
/*---------------------------------------------------------------------- | AP4_MarlinIpmpDecryptingProcessor:Initialize +---------------------------------------------------------------------*/ AP4_Result AP4_MarlinIpmpDecryptingProcessor::Initialize(AP4_AtomParent& top_level, AP4_ByteStream& stream, ProgressListener* /*listener*/) { AP4_Result result = AP4_MarlinIpmpParser::Parse(top_level, stream, m_SinfEntries, true); if (AP4_FAILED(result)) return result; // update the file type AP4_FtypAtom* ftyp = AP4_DYNAMIC_CAST(AP4_FtypAtom, top_level.GetChild(AP4_ATOM_TYPE_FTYP)); if (ftyp) { ftyp->SetMajorBrandAndVersion(AP4_FTYP_BRAND_MP42, 1); for (unsigned int i=0; i<ftyp->GetCompatibleBrands().ItemCount(); i++) { if (ftyp->GetCompatibleBrands()[i] == AP4_MARLIN_BRAND_MGSV) { ftyp->GetCompatibleBrands()[i] = AP4_FTYP_BRAND_MP42; } } } return AP4_SUCCESS; }
/*---------------------------------------------------------------------- | 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; AP4_List<AP4_TrakAtom>::Item* trak_item = moov->GetTrakAtoms().FirstItem(); while (trak_item) { 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)); } trak_item = trak_item->GetNext(); } // 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* item = moov->GetChildren().FirstItem(); item; ++item) { ++item_position; if (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(AP4_PROTECTION_SCHEME_TYPE_MARLIN_ACBC, 0x0100, NULL, true)); // setup the scheme info atom const char* content_id = m_PropertyMap.GetProperty(mpod->GetTrackIds()[i], "ContentId"); if (content_id) { AP4_ContainerAtom* schi = new AP4_ContainerAtom(AP4_ATOM_TYPE_SCHI); schi->AddChild(new AP4_NullTerminatedStringAtom(AP4_ATOM_TYPE_8ID_, content_id)); 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; }
/*---------------------------------------------------------------------- | AP4_MarlinIpmpParser:Parse +---------------------------------------------------------------------*/ AP4_Result AP4_MarlinIpmpParser::Parse(AP4_AtomParent& top_level, AP4_ByteStream& stream, AP4_List<SinfEntry>& sinf_entries, bool remove_od_data) { // check the file type AP4_FtypAtom* ftyp = AP4_DYNAMIC_CAST(AP4_FtypAtom, top_level.GetChild(AP4_ATOM_TYPE_FTYP)); if (ftyp == NULL || (ftyp->GetMajorBrand() != AP4_MARLIN_BRAND_MGSV && !ftyp->HasCompatibleBrand(AP4_MARLIN_BRAND_MGSV))) { return AP4_ERROR_INVALID_FORMAT; } // check the initial object descriptor and get the OD Track ID AP4_IodsAtom* iods = AP4_DYNAMIC_CAST(AP4_IodsAtom, top_level.FindChild("moov/iods")); AP4_UI32 od_track_id = 0; if (iods == NULL) return AP4_ERROR_INVALID_FORMAT; const AP4_ObjectDescriptor* od = iods->GetObjectDescriptor(); if (od == NULL) return AP4_ERROR_INVALID_FORMAT; AP4_EsIdIncDescriptor* es_id_inc = AP4_DYNAMIC_CAST(AP4_EsIdIncDescriptor, od->FindSubDescriptor(AP4_DESCRIPTOR_TAG_ES_ID_INC)); if (es_id_inc == NULL) return AP4_ERROR_INVALID_FORMAT; od_track_id = es_id_inc->GetTrackId(); // find the track pointed to by the descriptor AP4_MoovAtom* moov = AP4_DYNAMIC_CAST(AP4_MoovAtom, top_level.GetChild(AP4_ATOM_TYPE_MOOV)); if (moov == NULL) return AP4_ERROR_INVALID_FORMAT; AP4_TrakAtom* od_trak = NULL; AP4_List<AP4_TrakAtom>::Item* trak_item = moov->GetTrakAtoms().FirstItem(); while (trak_item) { AP4_TrakAtom* trak = trak_item->GetData(); if (trak) { if (trak->GetId() == od_track_id) { od_trak = trak; } else { sinf_entries.Add(new SinfEntry(trak->GetId(), NULL)); } } trak_item = trak_item->GetNext(); } // check that we have found the OD track if (od_trak == NULL) return AP4_ERROR_INVALID_FORMAT; // look for the 'mpod' trak references AP4_TrefTypeAtom* track_references; track_references = AP4_DYNAMIC_CAST(AP4_TrefTypeAtom, od_trak->FindChild("tref/mpod")); if (track_references == NULL) return AP4_ERROR_INVALID_FORMAT; // create an AP4_Track object from the trak atom and check that it has samples AP4_Track* od_track = new AP4_Track(*od_trak, stream, 0); if (od_track->GetSampleCount() < 1) { delete od_track; return AP4_ERROR_INVALID_FORMAT; } // get the first sample (in this version, we only look at a single OD command) AP4_Sample od_sample; AP4_Result result = od_track->GetSample(0, od_sample); if (AP4_FAILED(result)) { delete od_track; return AP4_ERROR_INVALID_FORMAT; } // adapt the sample data into a byte stream for parsing AP4_DataBuffer sample_data; od_sample.ReadData(sample_data); AP4_MemoryByteStream* sample_stream = new AP4_MemoryByteStream(sample_data); // look for one ObjectDescriptorUpdate command and // one IPMP_DescriptorUpdate command AP4_DescriptorUpdateCommand* od_update = NULL; AP4_DescriptorUpdateCommand* ipmp_update = NULL; do { AP4_Command* command = NULL; result = AP4_CommandFactory::CreateCommandFromStream(*sample_stream, command); if (AP4_SUCCEEDED(result)) { // found a command in the sample, check the type switch (command->GetTag()) { case AP4_COMMAND_TAG_OBJECT_DESCRIPTOR_UPDATE: if (od_update == NULL) { od_update = AP4_DYNAMIC_CAST(AP4_DescriptorUpdateCommand, command); } break; case AP4_COMMAND_TAG_IPMP_DESCRIPTOR_UPDATE: if (ipmp_update == NULL) { ipmp_update = AP4_DYNAMIC_CAST(AP4_DescriptorUpdateCommand, command); } break; default: break; } } } while (AP4_SUCCEEDED(result)); sample_stream->Release(); sample_stream = NULL; // check that we have what we need if (od_update == NULL || ipmp_update == NULL) { delete od_track; return AP4_ERROR_INVALID_FORMAT; } // process all the object descriptors in the od update for (AP4_List<AP4_Descriptor>::Item* od_item = od_update->GetDescriptors().FirstItem(); od_item; od_item = od_item->GetNext()) { od = AP4_DYNAMIC_CAST(AP4_ObjectDescriptor, od_item->GetData()); if (od == NULL) continue; // find which track this od references AP4_EsIdRefDescriptor* es_id_ref; es_id_ref = AP4_DYNAMIC_CAST(AP4_EsIdRefDescriptor, od->FindSubDescriptor(AP4_DESCRIPTOR_TAG_ES_ID_REF)); if (es_id_ref == NULL || es_id_ref->GetRefIndex() > track_references->GetTrackIds().ItemCount() || es_id_ref->GetRefIndex() == 0) { continue; } AP4_UI32 track_id = track_references->GetTrackIds()[es_id_ref->GetRefIndex()-1]; SinfEntry* sinf_entry = NULL; for (AP4_List<SinfEntry>::Item* sinf_entry_item = sinf_entries.FirstItem(); sinf_entry_item; sinf_entry_item = sinf_entry_item->GetNext()) { sinf_entry = sinf_entry_item->GetData(); if (sinf_entry->m_TrackId == track_id) { break; // match } else { sinf_entry = NULL; // no match } } if (sinf_entry == NULL) continue; // no matching entry if (sinf_entry->m_Sinf != NULL) continue; // entry already populated // see what ipmp descriptor this od points to AP4_IpmpDescriptorPointer* ipmpd_pointer; ipmpd_pointer = AP4_DYNAMIC_CAST(AP4_IpmpDescriptorPointer, od->FindSubDescriptor(AP4_DESCRIPTOR_TAG_IPMP_DESCRIPTOR_POINTER)); if (ipmpd_pointer == NULL) continue; // no pointer // find the ipmp descriptor referenced by the pointer AP4_IpmpDescriptor* ipmpd = NULL; for (AP4_List<AP4_Descriptor>::Item* ipmpd_item = ipmp_update->GetDescriptors().FirstItem(); ipmpd_item; ipmpd_item = ipmpd_item->GetNext()) { // check that this descriptor is of the right type ipmpd = AP4_DYNAMIC_CAST(AP4_IpmpDescriptor, ipmpd_item->GetData()); if (ipmpd == NULL || ipmpd->GetIpmpsType() != AP4_MARLIN_IPMPS_TYPE_MGSV) continue; // check the descriptor id if (ipmpd->GetDescriptorId() == ipmpd_pointer->GetDescriptorId()) { break; // match } else { ipmpd = NULL; // no match } } if (ipmpd == NULL) continue; // no matching entry // parse the ipmp data into one or more 'sinf' atoms, and keep the one with the // right type AP4_MemoryByteStream* data = new AP4_MemoryByteStream(ipmpd->GetData().GetData(), ipmpd->GetData().GetDataSize()); AP4_LargeSize bytes_available = ipmpd->GetData().GetDataSize(); do { AP4_Atom* atom = NULL; // setup the factory with a context so we can instantiate an 'schm' // atom with a slightly different format than the standard 'schm' AP4_AtomFactory* factory = &AP4_MarlinIpmpAtomFactory::Instance; factory->PushContext(AP4_ATOM_TYPE('m','r','l','n')); // parse the next atom in the stream result = factory->CreateAtomFromStream(*data, bytes_available, atom); factory->PopContext(); if (AP4_FAILED(result) || atom == NULL) break; // check that what we have parsed is indeed an 'sinf' of the right type if (atom->GetType() == AP4_ATOM_TYPE_SINF) { AP4_ContainerAtom* sinf = AP4_DYNAMIC_CAST(AP4_ContainerAtom, atom); AP4_SchmAtom* schm = AP4_DYNAMIC_CAST(AP4_SchmAtom, sinf->FindChild("schm")); if (schm->GetSchemeType() == AP4_PROTECTION_SCHEME_TYPE_MARLIN_ACBC && schm->GetSchemeVersion() == 0x0100) { // store the sinf in the entry for that track sinf_entry->m_Sinf = sinf; break; } } delete atom; } while (AP4_SUCCEEDED(result)); data->Release(); } // remove the iods atom and the OD track if required if (remove_od_data) { od_trak->Detach(); delete od_trak; iods->Detach(); delete iods; } // cleanup delete od_track; return AP4_SUCCESS; }
/*---------------------------------------------------------------------- | DcfParser_ParseV2Header +---------------------------------------------------------------------*/ static BLT_Result DcfParser_ParseV2Header(DcfParser* self, ATX_InputStream* stream) { /* rewind the byte stream */ ATX_InputStream_Seek(stream, 0); /* parse the atoms from the stream */ AP4_ByteStream* mp4_stream = new ATX_InputStream_To_AP4_ByteStream_Adapter(stream); AP4_AtomParent atoms; AP4_Result result = AP4_DefaultAtomFactory::Instance.CreateAtomsFromStream(*mp4_stream, atoms); mp4_stream->Release(); AP4_ByteStream* decrypting_stream = NULL; AP4_ContainerAtom* odrm = dynamic_cast<AP4_ContainerAtom*>(atoms.GetChild(AP4_ATOM_TYPE_ODRM)); if (odrm) { AP4_OdheAtom* odhe = dynamic_cast<AP4_OdheAtom*>(odrm->GetChild(AP4_ATOM_TYPE_ODHE)); AP4_OddaAtom* odda = dynamic_cast<AP4_OddaAtom*>(odrm->GetChild(AP4_ATOM_TYPE_ODDA)); if (odhe && odda) { const char* content_id = ""; /* get the content ID */ AP4_OhdrAtom* ohdr = dynamic_cast<AP4_OhdrAtom*>(odhe->GetChild(AP4_ATOM_TYPE_OHDR)); if (ohdr) { content_id = ohdr->GetContentId().GetChars(); } /* get the content key */ NPT_DataBuffer key; result = DcfParser_GetContentKey(self, content_id, key); if (BLT_FAILED(result)) { ATX_LOG_FINE_2("GetKeyForContent(%s) returned %d", content_id, result); return BLT_ERROR_NO_MEDIA_KEY; } /* create the decrypting stream */ result = AP4_OmaDcfAtomDecrypter::CreateDecryptingStream(*odrm, key.GetData(), key.GetDataSize(), self->cipher_factory, decrypting_stream); if (AP4_SUCCEEDED(result)) { /* update the content type */ ATX_CopyStringN(self->input.content_type, odhe->GetContentType().GetChars(), sizeof(self->input.content_type)); /* update the encrypted size */ self->input.encrypted_size = odda->GetEncryptedDataLength(); } } } /* check that we have found what we needed in the atoms */ if (decrypting_stream == NULL) return BLT_ERROR_INVALID_MEDIA_FORMAT; /* update the output size */ AP4_LargeSize plaintext_size = 0; if (AP4_SUCCEEDED(decrypting_stream->GetSize(plaintext_size))) { self->output.size = plaintext_size; } else { self->output.size = self->input.encrypted_size; } /* create a reverse adapter */ result = AP4_ByteStream_To_ATX_InputStream_Adapter_Create(decrypting_stream, &self->output.stream); decrypting_stream->Release(); return BLT_SUCCESS; }
/*---------------------------------------------------------------------- | 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; }