/*---------------------------------------------------------------------- | AP4_FragmentSampleTable::AP4_FragmentSampleTable +---------------------------------------------------------------------*/ AP4_FragmentSampleTable::AP4_FragmentSampleTable(AP4_ContainerAtom* traf, AP4_TrexAtom* trex, AP4_Cardinal internal_track_id, AP4_ByteStream* sample_stream, AP4_Position moof_offset, AP4_Position mdat_payload_offset, AP4_UI64 dts_origin) :m_InternalTrackId(internal_track_id) { AP4_TfhdAtom* tfhd = AP4_DYNAMIC_CAST(AP4_TfhdAtom, traf->GetChild(AP4_ATOM_TYPE_TFHD)); if (tfhd == NULL) return; // count all the samples and reserve space for them unsigned int sample_count = 0; for (AP4_List<AP4_Atom>::Item* item = traf->GetChildren().FirstItem(); item; item = item->GetNext()) { AP4_Atom* atom = item->GetData(); if (atom->GetType() == AP4_ATOM_TYPE_TRUN) { AP4_TrunAtom* trun = AP4_DYNAMIC_CAST(AP4_TrunAtom, atom); if (trun) sample_count += trun->GetEntries().ItemCount(); } } m_Samples.EnsureCapacity(sample_count); // check if we have a timecode base AP4_TfdtAtom* tfdt = AP4_DYNAMIC_CAST(AP4_TfdtAtom, traf->GetChild(AP4_ATOM_TYPE_TFDT)); if (tfdt) { dts_origin = tfdt->GetBaseMediaDecodeTime(); } // process all the trun atoms for (AP4_List<AP4_Atom>::Item* item = traf->GetChildren().FirstItem(); item; item = item->GetNext()) { AP4_Atom* atom = item->GetData(); if (atom->GetType() == AP4_ATOM_TYPE_TRUN) { AP4_TrunAtom* trun = AP4_DYNAMIC_CAST(AP4_TrunAtom, atom); if (trun) { AP4_Result result = AddTrun(trun, tfhd, trex, sample_stream, moof_offset, mdat_payload_offset, dts_origin); if (AP4_FAILED(result)) return; } } } }
/*---------------------------------------------------------------------- | AutoDetectAudioFragmentDuration +---------------------------------------------------------------------*/ static unsigned int AutoDetectAudioFragmentDuration(AP4_ByteStream& stream, TrackCursor* cursor) { // remember where we are in the stream AP4_Position where = 0; stream.Tell(where); AP4_LargeSize stream_size = 0; stream.GetSize(stream_size); AP4_LargeSize bytes_available = stream_size-where; AP4_UI64 fragment_count = 0; AP4_UI32 last_fragment_size = 0; AP4_Atom* atom = NULL; while (AP4_SUCCEEDED(AP4_DefaultAtomFactory::Instance.CreateAtomFromStream(stream, bytes_available, atom))) { if (atom && atom->GetType() == AP4_ATOM_TYPE_MOOF) { AP4_ContainerAtom* moof = AP4_DYNAMIC_CAST(AP4_ContainerAtom, atom); AP4_TfhdAtom* tfhd = AP4_DYNAMIC_CAST(AP4_TfhdAtom, moof->FindChild("traf/tfhd")); if (tfhd && tfhd->GetTrackId() == cursor->m_Track->GetId()) { ++fragment_count; AP4_TrunAtom* trun = AP4_DYNAMIC_CAST(AP4_TrunAtom, moof->FindChild("traf/trun")); if (trun) { last_fragment_size = trun->GetEntries().ItemCount(); } } } delete atom; atom = NULL; } // restore the stream to its original position stream.Seek(where); // decide if we can infer an fragment size if (fragment_count == 0 || cursor->m_Samples->GetSampleCount() == 0) { return 0; } // don't count the last fragment if we have more than one if (fragment_count > 1 && last_fragment_size) { --fragment_count; } if (fragment_count <= 1 || cursor->m_Samples->GetSampleCount() < last_fragment_size) { last_fragment_size = 0; } AP4_Sample sample; AP4_UI64 total_duration = 0; for (unsigned int i=0; i<cursor->m_Samples->GetSampleCount()-last_fragment_size; i++) { cursor->m_Samples->GetSample(i, sample); total_duration += sample.GetDuration(); } return (unsigned int)AP4_ConvertTime(total_duration/fragment_count, cursor->m_Track->GetMediaTimeScale(), 1000); }
/*---------------------------------------------------------------------- | AP4_Processor::ProcessFragments +---------------------------------------------------------------------*/ AP4_Result AP4_Processor::ProcessFragment( AP4_ContainerAtom* moof, AP4_SidxAtom* sidx, AP4_Position sidx_position, AP4_ByteStream& output, AP4_Array<AP4_Position>& moof_positions, AP4_Array<AP4_Position>& mdat_positions) { unsigned int fragment_index = 0; //AP4_UI64 mdat_payload_offset = atom_offset+atom->GetSize()+AP4_ATOM_HEADER_SIZE; AP4_Sample sample; AP4_DataBuffer sample_data_in; AP4_DataBuffer sample_data_out; AP4_Result result = AP4_SUCCESS; // parse the moof //AP4_MovieFragment* fragment = new AP4_MovieFragment(moof); // process all the traf atoms AP4_Array<AP4_Processor::FragmentHandler*> handlers; AP4_Array<AP4_FragmentSampleTable*> sample_tables; for (; AP4_Atom* child = moof->GetChild(AP4_ATOM_TYPE_TRAF, handlers.ItemCount());) { AP4_TrafAtom* traf = AP4_DYNAMIC_CAST(AP4_TrafAtom, child); PERTRACK &track_data(m_TrackData[traf->GetInternalTrackId()]); AP4_TrakAtom* trak = track_data.track_handler->GetTrakAtom(); AP4_TrexAtom* trex = track_data.track_handler->GetTrexAtom(); // create the handler for this traf AP4_Processor::FragmentHandler* handler = CreateFragmentHandler(trak, trex, traf, *(m_StreamData[track_data.streamId].stream), moof_positions[track_data.streamId]); if (handler) { result = handler->ProcessFragment(); if (AP4_FAILED(result)) return result; } handlers.Append(handler); // create a sample table object so we can read the sample data AP4_FragmentSampleTable* sample_table = new AP4_FragmentSampleTable( traf, trex, traf->GetInternalTrackId(), m_StreamData[track_data.streamId].stream, moof_positions[traf->GetInternalTrackId()], mdat_positions[traf->GetInternalTrackId()], 0); sample_tables.Append(sample_table); // let the handler look at the samples before we process them if (handler) result = handler->PrepareForSamples(sample_table); if (AP4_FAILED(result)) return result; } output.Buffer(); // write the moof AP4_UI64 moof_out_start = 0; output.Tell(moof_out_start); moof->Write(output); // remember the location of this fragment FragmentMapEntry map_entry = { moof_positions[0], moof_out_start }; fragment_map_.Append(map_entry); // write an mdat header AP4_Position mdat_out_start; AP4_UI64 mdat_size = AP4_ATOM_HEADER_SIZE; output.Tell(mdat_out_start); output.WriteUI32(0); output.WriteUI32(AP4_ATOM_TYPE_MDAT); // process all track runs for (unsigned int i=0; i<handlers.ItemCount(); i++) { AP4_Processor::FragmentHandler* handler = handlers[i]; // get the track ID AP4_ContainerAtom* traf = AP4_DYNAMIC_CAST(AP4_ContainerAtom, moof->GetChild(AP4_ATOM_TYPE_TRAF, i)); if (traf == NULL) continue; AP4_TfhdAtom* tfhd = AP4_DYNAMIC_CAST(AP4_TfhdAtom, traf->GetChild(AP4_ATOM_TYPE_TFHD)); // compute the base data offset AP4_UI64 base_data_offset; if (tfhd->GetFlags() & AP4_TFHD_FLAG_BASE_DATA_OFFSET_PRESENT) { base_data_offset = mdat_out_start+AP4_ATOM_HEADER_SIZE; } else { base_data_offset = moof_out_start; } // build a list of all trun atoms AP4_Array<AP4_TrunAtom*> truns; for (AP4_List<AP4_Atom>::Item* child_item = traf->GetChildren().FirstItem(); child_item; child_item = child_item->GetNext()) { AP4_Atom* child_atom = child_item->GetData(); if (child_atom->GetType() == AP4_ATOM_TYPE_TRUN) { AP4_TrunAtom* trun = AP4_DYNAMIC_CAST(AP4_TrunAtom, child_atom); truns.Append(trun); } } AP4_Ordinal trun_index = 0; AP4_Ordinal trun_sample_index = 0; AP4_TrunAtom* trun = truns[0]; trun->SetDataOffset((AP4_SI32)((mdat_out_start+mdat_size)-base_data_offset)); // write the mdat for (unsigned int j=0; j<sample_tables[i]->GetSampleCount(); j++, trun_sample_index++) { // advance the trun index if necessary if (trun_sample_index >= trun->GetEntries().ItemCount()) { trun = truns[++trun_index]; trun->SetDataOffset((AP4_SI32)((mdat_out_start+mdat_size)-base_data_offset)); trun_sample_index = 0; } // get the next sample result = sample_tables[i]->GetSample(j, sample); if (AP4_FAILED(result)) return result; sample.ReadData(sample_data_in); m_TrackData[sample_tables[i]->GetInteralTrackId()].dts = sample.GetDts(); // process the sample data if (handler) { result = handler->ProcessSample(sample_data_in, sample_data_out); if (AP4_FAILED(result)) return result; // write the sample data result = output.Write(sample_data_out.GetData(), sample_data_out.GetDataSize()); if (AP4_FAILED(result)) return result; // update the mdat size mdat_size += sample_data_out.GetDataSize(); // update the trun entry trun->UseEntries()[trun_sample_index].sample_size = sample_data_out.GetDataSize(); } else { // write the sample data (unmodified) result = output.Write(sample_data_in.GetData(), sample_data_in.GetDataSize()); if (AP4_FAILED(result)) return result; // update the mdat size mdat_size += sample_data_in.GetDataSize(); } } if (handler) { // update the tfhd header if (tfhd->GetFlags() & AP4_TFHD_FLAG_BASE_DATA_OFFSET_PRESENT) { tfhd->SetBaseDataOffset(mdat_out_start+AP4_ATOM_HEADER_SIZE); } if (tfhd->GetFlags() & AP4_TFHD_FLAG_DEFAULT_SAMPLE_SIZE_PRESENT) { tfhd->SetDefaultSampleSize(trun->GetEntries()[0].sample_size); } // give the handler a chance to update the atoms handler->FinishFragment(); } } // update the mdat header AP4_Position mdat_out_end; output.Tell(mdat_out_end); #if defined(AP4_DEBUG) AP4_ASSERT(mdat_out_end-mdat_out_start == mdat_size); #endif if (AP4_FAILED(result = output.Seek(mdat_out_start))) return result; output.WriteUI32((AP4_UI32)mdat_size); output.Seek(mdat_out_end); // update the moof if needed if (AP4_FAILED(result = output.Seek(moof_out_start))) return result; moof->Write(output); output.Seek(mdat_out_end); // update the sidx if we have one if (sidx && fragment_index < sidx->GetReferences().ItemCount()) { if (fragment_index == 0) { sidx->SetFirstOffset(moof_out_start-(sidx_position+sidx->GetSize())); } AP4_LargeSize fragment_size = mdat_out_end-moof_out_start; AP4_SidxAtom::Reference& sidx_ref = sidx->UseReferences()[fragment_index]; sidx_ref.m_ReferencedSize = (AP4_UI32)fragment_size; } // cleanup //delete fragment; for (unsigned int i=0; i<handlers.ItemCount(); i++) { delete handlers[i]; } for (unsigned int i=0; i<sample_tables.ItemCount(); i++) { delete sample_tables[i]; } if (AP4_FAILED(result = output.Flush())) return result; return AP4_SUCCESS; }
/*---------------------------------------------------------------------- | AP4_Movie::ProcessMoof +---------------------------------------------------------------------*/ void AP4_Movie::ProcessMoof(AP4_ContainerAtom* moof, AP4_ByteStream& stream) { if (moof) { AP4_Offset offset = 0; stream.Tell(offset); AP4_Offset moof_offset = offset - moof->GetSize(); AP4_Offset mdat_payload_offset = offset + 8; AP4_MfhdAtom* mfhd = AP4_DYNAMIC_CAST(AP4_MfhdAtom, moof->GetChild(AP4_ATOM_TYPE_MFHD)); if (mfhd) { for (AP4_List<AP4_Atom>::Item* item = moof->GetChildren().FirstItem(); item; item = item->GetNext()) { AP4_Atom* atom = item->GetData(); if (atom->GetType() == AP4_ATOM_TYPE_TRAF) { AP4_ContainerAtom* traf = AP4_DYNAMIC_CAST(AP4_ContainerAtom, atom); if (traf) { AP4_TfhdAtom* tfhd = AP4_DYNAMIC_CAST(AP4_TfhdAtom, traf->GetChild(AP4_ATOM_TYPE_TFHD)); if (!tfhd) { continue; } AP4_Track* track = GetTrack(tfhd->GetTrackId()); if (!track) { continue; } AP4_TfdtAtom* tfdt = AP4_DYNAMIC_CAST(AP4_TfdtAtom, traf->GetChild(AP4_ATOM_TYPE_TFDT)); AP4_TrexAtom* trex = NULL; AP4_ContainerAtom* mvex = AP4_DYNAMIC_CAST(AP4_ContainerAtom, m_MoovAtom->GetChild(AP4_ATOM_TYPE_MVEX)); if (mvex) { for (AP4_List<AP4_Atom>::Item* child_item = mvex->GetChildren().FirstItem(); child_item; child_item = child_item->GetNext()) { AP4_Atom* child_atom = child_item->GetData(); if (child_atom->GetType() == AP4_ATOM_TYPE_TREX) { trex = AP4_DYNAMIC_CAST(AP4_TrexAtom, child_atom); if (trex && trex->GetTrackId() == tfhd->GetTrackId()) break; trex = NULL; } } } AP4_FragmentSampleTable* sampleTable = track->GetFragmentSampleTable(); AP4_Cardinal sample_count = 0; for (AP4_List<AP4_Atom>::Item* child_item = traf->GetChildren().FirstItem(); child_item; child_item = child_item->GetNext()) { AP4_Atom* child_atom = child_item->GetData(); if (child_atom->GetType() == AP4_ATOM_TYPE_TRUN) { AP4_TrunAtom* trun = AP4_DYNAMIC_CAST(AP4_TrunAtom, child_atom); if (trun) { sample_count += trun->GetEntries().ItemCount(); } } } if (!sample_count) { return; } if (sampleTable->GetSampleCount() == 0) { track->CreateFragmentFromStdSamples(); } if (AP4_FAILED(sampleTable->EnsureCapacity(sample_count + sampleTable->GetSampleCount()))) { return; } AP4_UI64 dts_origin = tfdt ? tfdt->GetBaseMediaDecodeTime() : 0; for (AP4_List<AP4_Atom>::Item* child_item = traf->GetChildren().FirstItem(); child_item; child_item = child_item->GetNext()) { AP4_Atom* child_atom = child_item->GetData(); if (child_atom->GetType() == AP4_ATOM_TYPE_TRUN) { AP4_TrunAtom* trun = AP4_DYNAMIC_CAST(AP4_TrunAtom, child_atom); if (trun) { sampleTable->AddTrun(trun, tfhd, trex, stream, dts_origin, moof_offset, mdat_payload_offset); } } } } } } } } }