/*---------------------------------------------------------------------- | AP4_StsdAtom::AP4_StsdAtom +---------------------------------------------------------------------*/ AP4_StsdAtom::AP4_StsdAtom(AP4_UI32 size, AP4_UI08 version, AP4_UI32 flags, AP4_ByteStream& stream, AP4_AtomFactory& atom_factory) : AP4_ContainerAtom(AP4_ATOM_TYPE_STSD, size, false, version, flags) { // read the number of entries AP4_UI32 entry_count; stream.ReadUI32(entry_count); // save and switch the factory's context atom_factory.PushContext(m_Type); // read all entries AP4_LargeSize bytes_available = size-AP4_FULL_ATOM_HEADER_SIZE-4; for (unsigned int i=0; i<entry_count; i++) { AP4_Atom* atom; if (AP4_SUCCEEDED(atom_factory.CreateAtomFromStream(stream, bytes_available, atom))) { atom->SetParent(this); m_Children.Add(atom); } } // restore the saved context atom_factory.PopContext(); // initialize the sample description cache m_SampleDescriptions.EnsureCapacity(m_Children.ItemCount()); for (AP4_Ordinal i=0; i<m_Children.ItemCount(); i++) { m_SampleDescriptions.Append(NULL); } }
/*---------------------------------------------------------------------- | 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; }