Exemplo n.º 1
0
/*----------------------------------------------------------------------
|   AP4_Processor::Process
+---------------------------------------------------------------------*/
AP4_Result
AP4_Processor::Process(AP4_ByteStream&   input, 
                       AP4_ByteStream&   output,
                       AP4_ByteStream*   fragments,
                       ProgressListener* listener,
                       AP4_AtomFactory&  atom_factory)
{
    // read all atoms.
    // keep all atoms except [mdat]
    // keep a ref to [moov]
    // put [moof] atoms in a separate list
    AP4_AtomParent              top_level;
    AP4_MoovAtom*               moov = NULL;
    AP4_ContainerAtom*          mfra = NULL;
    AP4_SidxAtom*               sidx = NULL;
    AP4_List<AP4_AtomLocator>   frags;
    AP4_UI64                    stream_offset = 0;
    bool                        in_fragments = false;
    unsigned int                sidx_count = 0;
    for (AP4_Atom* atom = NULL;
        AP4_SUCCEEDED(atom_factory.CreateAtomFromStream(input, atom));
        input.Tell(stream_offset)) {
        if (atom->GetType() == AP4_ATOM_TYPE_MDAT) {
            delete atom;
            continue;
        } else if (atom->GetType() == AP4_ATOM_TYPE_MOOV) {
            moov = AP4_DYNAMIC_CAST(AP4_MoovAtom, atom);
            if (fragments) break;
        } else if (atom->GetType() == AP4_ATOM_TYPE_MFRA) {
            mfra = AP4_DYNAMIC_CAST(AP4_ContainerAtom, atom);
            continue;
        } else if (atom->GetType() == AP4_ATOM_TYPE_SIDX) {
            // don't keep the index, it is likely to be invalidated, we will recompute it later
            ++sidx_count;
            if (sidx == NULL) {
                sidx = AP4_DYNAMIC_CAST(AP4_SidxAtom, atom);
            } else {
                delete atom;
                continue;
            }
        } else if (atom->GetType() == AP4_ATOM_TYPE_SSIX) {
            // don't keep the index, it is likely to be invalidated
            delete atom;
            continue;
        } else if (!fragments && (in_fragments || atom->GetType() == AP4_ATOM_TYPE_MOOF)) {
            in_fragments = true;
            frags.Add(new AP4_AtomLocator(atom, stream_offset));
			break;
        }
        top_level.AddChild(atom);
    }

    // check that we have at most one sidx (we can't deal with multi-sidx streams here
    if (sidx_count > 1) {
        top_level.RemoveChild(sidx);
        delete sidx;
        sidx = NULL;
    }
    
    // if we have a fragments stream, get the fragment locators from there
    if (fragments) {
        stream_offset = 0;
        for (AP4_Atom* atom = NULL;
            AP4_SUCCEEDED(atom_factory.CreateAtomFromStream(*fragments, atom));
            fragments->Tell(stream_offset)) {
            if (atom->GetType() == AP4_ATOM_TYPE_MDAT) {
                delete atom;
                continue;
            }
            frags.Add(new AP4_AtomLocator(atom, stream_offset));
        }
    }
    
    // initialize the processor
    AP4_Result result = Initialize(top_level, input);
    if (AP4_FAILED(result)) return result;

    // process the tracks if we have a moov atom
    AP4_Array<AP4_SampleLocator> locators;
    AP4_Cardinal                 track_count       = 0;
    AP4_List<AP4_TrakAtom>*      trak_atoms        = NULL;
    AP4_LargeSize                mdat_payload_size = 0;
    AP4_SampleCursor*            cursors           = NULL;
    if (moov) {
        // build an array of track sample locators
        trak_atoms = &moov->GetTrakAtoms();
        track_count = trak_atoms->ItemCount();
        cursors = new AP4_SampleCursor[track_count];
		m_TrackData.SetItemCount(track_count);
		m_StreamData.SetItemCount(1);
		m_StreamData[0].stream = &input;

		unsigned int index = 0;
        for (AP4_List<AP4_TrakAtom>::Item* item = trak_atoms->FirstItem(); item; item=item->GetNext()) {
            AP4_TrakAtom* trak = item->GetData();

            // find the stsd atom
            AP4_ContainerAtom* stbl = AP4_DYNAMIC_CAST(AP4_ContainerAtom, trak->FindChild("mdia/minf/stbl"));
            if (stbl == NULL) continue;
            
            // see if there's an external data source for this track
            AP4_ByteStream* trak_data_stream = &input;
            for (AP4_List<ExternalTrackData>::Item* ditem = m_ExternalTrackData.FirstItem(); ditem; ditem=ditem->GetNext()) {
                ExternalTrackData* tdata = ditem->GetData();
                if (tdata->m_TrackId == trak->GetId()) {
                    trak_data_stream = tdata->m_MediaData;
                    break;
                }
            }
			AP4_ContainerAtom *mvex = AP4_DYNAMIC_CAST(AP4_ContainerAtom, moov->GetChild(AP4_ATOM_TYPE_MVEX));
			AP4_TrexAtom*      trex = NULL;
			if (mvex) {
				for (AP4_List<AP4_Atom>::Item* item = mvex->GetChildren().FirstItem(); item; item = item->GetNext()) {
					AP4_Atom* atom = item->GetData();
					if (atom->GetType() == AP4_ATOM_TYPE_TREX) {
						trex = AP4_DYNAMIC_CAST(AP4_TrexAtom, atom);
						if (trex && trex->GetTrackId() == trak->GetId()) 
							break;
						trex = NULL;
					}
				}
			}
			// create the track handler    
            m_TrackData[index].track_handler	= CreateTrackHandler(trak, trex);
			m_TrackData[index].new_id = trak->GetId();

			cursors[index].m_Locator.m_TrakIndex   = index;
            cursors[index].m_Locator.m_SampleTable = new AP4_AtomSampleTable(stbl, *trak_data_stream);
            cursors[index].m_Locator.m_SampleIndex = 0;
            cursors[index].m_Locator.m_ChunkIndex  = 0;
            if (cursors[index].m_Locator.m_SampleTable->GetSampleCount()) {
                cursors[index].m_Locator.m_SampleTable->GetSample(0, cursors[index].m_Locator.m_Sample);
            } else {
                cursors[index].m_EndReached = true;
            }

            index++;            
        }

        // figure out the layout of the chunks
        for (;;) {
            // see which is the next sample to write
            AP4_UI64 min_offset = (AP4_UI64)(-1);
            int cursor = -1;
            for (unsigned int i=0; i<track_count; i++) {
                if (!cursors[i].m_EndReached &&
                    cursors[i].m_Locator.m_Sample.GetOffset() <= min_offset) {
                    min_offset = cursors[i].m_Locator.m_Sample.GetOffset();
                    cursor = i;
                }
            }

            // stop if all cursors are exhausted
            if (cursor == -1) break;

            // append this locator to the layout list
            AP4_SampleLocator& locator = cursors[cursor].m_Locator;
            locators.Append(locator);

            // move the cursor to the next sample
            locator.m_SampleIndex++;
            if (locator.m_SampleIndex == locator.m_SampleTable->GetSampleCount()) {
                // mark this track as completed
                cursors[cursor].m_EndReached = true;
            } else {
                // get the next sample info
                locator.m_SampleTable->GetSample(locator.m_SampleIndex, locator.m_Sample);
                AP4_Ordinal skip, sdesc;
                locator.m_SampleTable->GetChunkForSample(locator.m_SampleIndex,
                                                         locator.m_ChunkIndex,
                                                         skip, sdesc);
            }
        }

        // update the stbl atoms and compute the mdat size
        int current_track = -1;
        int current_chunk = -1;
        AP4_Position current_chunk_offset = 0;
        AP4_Size current_chunk_size = 0;
        for (AP4_Ordinal i=0; i<locators.ItemCount(); i++) {
            AP4_SampleLocator& locator = locators[i];
            if ((int)locator.m_TrakIndex  != current_track ||
                (int)locator.m_ChunkIndex != current_chunk) {
                // start a new chunk for this track
                current_chunk_offset += current_chunk_size;
                current_chunk_size = 0;
                current_track = locator.m_TrakIndex;
                current_chunk = locator.m_ChunkIndex;
                locator.m_SampleTable->SetChunkOffset(locator.m_ChunkIndex, current_chunk_offset);
            } 
            AP4_Size sample_size;
            TrackHandler* handler = m_TrackData[locator.m_TrakIndex].track_handler;
            if (handler) {
                sample_size = handler->GetProcessedSampleSize(locator.m_Sample);
                locator.m_SampleTable->SetSampleSize(locator.m_SampleIndex, sample_size);
            } else {
                sample_size = locator.m_Sample.GetSize();
            }
            current_chunk_size += sample_size;
            mdat_payload_size  += sample_size;
        }

        // process the tracks (ex: sample descriptions processing)
        for (AP4_Ordinal i=0; i<track_count; i++) {
            TrackHandler* handler = m_TrackData[i].track_handler;
            if (handler)
				handler->ProcessTrack();
        }
    }

    // finalize the processor
    Finalize(top_level);

    if (!fragments) {
        // calculate the size of all atoms combined
        AP4_UI64 atoms_size = 0;
        top_level.GetChildren().Apply(AP4_AtomSizeAdder(atoms_size));

        // see if we need a 64-bit or 32-bit mdat
        AP4_Size mdat_header_size = AP4_ATOM_HEADER_SIZE;
        if (mdat_payload_size+mdat_header_size > 0xFFFFFFFF) {
            // we need a 64-bit size
            mdat_header_size += 8;
        }
        
        // adjust the chunk offsets
        for (AP4_Ordinal i=0; i<track_count; i++) {
            AP4_TrakAtom* trak;
            trak_atoms->Get(i, trak);
            trak->AdjustChunkOffsets(atoms_size+mdat_header_size);
        }

        // write all atoms
        top_level.GetChildren().Apply(AP4_AtomListWriter(output));

        // write mdat header
        if (mdat_payload_size) {
            if (mdat_header_size == AP4_ATOM_HEADER_SIZE) {
                // 32-bit size
                output.WriteUI32((AP4_UI32)(mdat_header_size+mdat_payload_size));
                output.WriteUI32(AP4_ATOM_TYPE_MDAT);
            } else {
                // 64-bit size
                output.WriteUI32(1);
                output.WriteUI32(AP4_ATOM_TYPE_MDAT);
                output.WriteUI64(mdat_header_size+mdat_payload_size);
            }
        }        
    }
    
    // write the samples
    if (moov) {
        if (!fragments) {
#if defined(AP4_DEBUG)
            AP4_Position before;
            output.Tell(before);
#endif
            AP4_Sample     sample;
            AP4_DataBuffer data_in;
            AP4_DataBuffer data_out;
            for (unsigned int i=0; i<locators.ItemCount(); i++) {
                AP4_SampleLocator& locator = locators[i];
                locator.m_Sample.ReadData(data_in);
                TrackHandler* handler = m_TrackData[locator.m_TrakIndex].track_handler;
                if (handler) {
                    result = handler->ProcessSample(data_in, data_out);
                    if (AP4_FAILED(result)) return result;
                    output.Write(data_out.GetData(), data_out.GetDataSize());
                } else {
                    output.Write(data_in.GetData(), data_in.GetDataSize());            
                }

                // notify the progress listener
                if (listener) {
                    listener->OnProgress(i+1, locators.ItemCount());
                }
            }

#if defined(AP4_DEBUG)
            AP4_Position after;
            output.Tell(after);
            AP4_ASSERT(after-before == mdat_payload_size);
#endif
		}
		else
			m_StreamData[0].stream = fragments;
        
        // find the position of the sidx atom
        AP4_Position sidx_position = 0;
		if (sidx) {
			for (AP4_List<AP4_Atom>::Item* item = top_level.GetChildren().FirstItem();
				item;
				item = item->GetNext()) {
				AP4_Atom* atom = item->GetData();
				if (atom->GetType() == AP4_ATOM_TYPE_SIDX) {
					break;
				}
				sidx_position += atom->GetSize();
			}
		}
        
        // process the fragments, if any
		AP4_Array<AP4_Position> moof_offsets, mdat_offsets;
		moof_offsets.SetItemCount(1);
		mdat_offsets.SetItemCount(1);

		while (frags.ItemCount() > 0)
		{
			for (AP4_List<AP4_AtomLocator>::Item *locator(frags.FirstItem()); locator; locator = locator->GetNext())
			{
				AP4_ContainerAtom *moof(AP4_DYNAMIC_CAST(AP4_ContainerAtom, locator->GetData()->m_Atom));
				moof_offsets[0] = locator->GetData()->m_Offset;
				mdat_offsets[0] = moof_offsets[0] + moof->GetSize() + AP4_ATOM_HEADER_SIZE;

				result = ProcessFragment(moof, sidx, sidx_position, output, moof_offsets, mdat_offsets);
				if (AP4_FAILED(result))
					return result;
			}
			frags.DeleteReferences();

			AP4_Atom* atom = NULL;
			input.Tell(stream_offset);
			if (AP4_SUCCEEDED(atom_factory.CreateAtomFromStream(input, atom)))
			{
				if (atom->GetType() == AP4_ATOM_TYPE_MOOF)
					frags.Add(new AP4_AtomLocator(atom, stream_offset));
				else
					delete atom;
			}
		}

		// update the mfra if we have one
		if (mfra) {
			for (AP4_List<AP4_Atom>::Item* mfra_item = mfra->GetChildren().FirstItem(); mfra_item; mfra_item = mfra_item->GetNext())
			{
				if (mfra_item->GetData()->GetType() != AP4_ATOM_TYPE_TFRA)
					continue;
				AP4_TfraAtom* tfra = AP4_DYNAMIC_CAST(AP4_TfraAtom, mfra_item->GetData());
				if (tfra == NULL)
					continue;
				AP4_Array<AP4_TfraAtom::Entry>& entries = tfra->GetEntries();
				AP4_Cardinal entry_count = entries.ItemCount();
				for (unsigned int i = 0; i<entry_count; i++) {
					entries[i].m_MoofOffset = FindFragmentMapEntry(entries[i].m_MoofOffset);
				}
			}
		}

        // update and re-write the sidx if we have one
        if (sidx && sidx_position) {
            AP4_Position where = 0;
            output.Tell(where);
            output.Seek(sidx_position);
            result = sidx->Write(output);
            if (AP4_FAILED(result)) return result;
            output.Seek(where);
        }
        
        if (!fragments) {
            // write the mfra atom at the end if we have one
            if (mfra) {
                mfra->Write(output);
            }
        }
        
        // cleanup
        for (AP4_Ordinal i=0; i<track_count; i++)
            delete cursors[i].m_Locator.m_SampleTable;
        m_TrackData.Clear();
        delete[] cursors;
    }

    // cleanup
    frags.DeleteReferences();
    delete mfra;
    
    return AP4_SUCCESS;
}
Exemplo n.º 2
0
/*----------------------------------------------------------------------
|   AP4_LinearReader::SeekTo
+---------------------------------------------------------------------*/
AP4_Result
AP4_LinearReader::SeekTo(AP4_UI32 time_ms, AP4_UI32* actual_time_ms)
{
    if (actual_time_ms) *actual_time_ms = time_ms; // default
    
    // we only support fragmented sources for now
    if (!m_HasFragments) return AP4_ERROR_NOT_SUPPORTED;
    
    // look for a fragment index
    if (m_Mfra == NULL) {
        if (m_FragmentStream) {
            // get the size of the stream (needed)
            AP4_LargeSize stream_size = 0;
            m_FragmentStream->GetSize(stream_size);

            if (stream_size > 12) {
                // remember where we are
                AP4_Position here;
                m_FragmentStream->Tell(here);
                
                // read the last 12 bytes
                unsigned char mfro[12];
                AP4_Result result = m_FragmentStream->Seek(stream_size-12);
                if (AP4_SUCCEEDED(result)) {
                    result = m_FragmentStream->Read(mfro, 12);
                }
                if (AP4_SUCCEEDED(result) && mfro[0] == 'm' && mfro[1] == 'f' && mfro[2] == 'r' && mfro[3] == 'o') {
                    AP4_UI32 mfra_size = AP4_BytesToUInt32BE(&mfro[8]);
                    if ((AP4_LargeSize)mfra_size < stream_size) {
                        result = m_FragmentStream->Seek(stream_size-mfra_size);
                        if (AP4_SUCCEEDED(result)) {
                            AP4_Atom* mfra = NULL;
                            AP4_LargeSize available = mfra_size;
                            AP4_DefaultAtomFactory::Instance.CreateAtomFromStream(*m_FragmentStream, available, mfra);
                            m_Mfra = AP4_DYNAMIC_CAST(AP4_ContainerAtom, mfra);
                        }
                    }
                }
                if (AP4_SUCCEEDED(result)) {
                    result = m_FragmentStream->Seek(here);
                }
            }
        }
    }
    
    // return now if we have not found an index
    if (m_Mfra == NULL) {
        return AP4_ERROR_NOT_SUPPORTED;
    }
    
    // look for the earliest fragment referenced by an entry with the largest timestamp that's
    // before or equal to the requested time
    int best_entry = -1;
    for (unsigned t=0; t<m_Trackers.ItemCount(); t++) {
        // find the tfra index for this track
        AP4_TfraAtom* tfra = NULL;
        for (AP4_List<AP4_Atom>::Item* item = m_Mfra->GetChildren().FirstItem();
                                       item;
                                       item = item->GetNext()) {
            if (item->GetData()->GetType() == AP4_ATOM_TYPE_TFRA) {
                AP4_TfraAtom* tfra_ = (AP4_TfraAtom*)item->GetData();
                if (tfra_->GetTrackId() == m_Trackers[t]->m_Track->GetId()) {
                    tfra = tfra_;
                    break;
                }
            }
        }
        if (tfra == NULL) {
            return AP4_ERROR_NOT_SUPPORTED;
        }
        AP4_Array<AP4_TfraAtom::Entry>& entries = tfra->GetEntries();

        AP4_UI64 media_time = AP4_ConvertTime(time_ms, 1000, m_Trackers[t]->m_Track->GetMediaTimeScale());
        int entry = -1;
        for (int i=0; i<(int)entries.ItemCount(); i++) {
            if (entries[i].m_Time > media_time) break;
            entry = i;
        }
        if (entry >= 0) {
            if (best_entry == -1) {
                best_entry = entry;
            } else if (entries[entry].m_MoofOffset < entries[best_entry].m_MoofOffset) {
                best_entry = entry;
            }

            // update our position
            if (best_entry >= 0) {
                if (actual_time_ms) {
                    // report the actual time we found (in milliseconds)
                    *actual_time_ms = (AP4_UI32)AP4_ConvertTime(entries[best_entry].m_Time, m_Trackers[t]->m_Track->GetMediaTimeScale(), 1000);
                }
                m_NextFragmentPosition = entries[best_entry].m_MoofOffset;
            }
        }
    }
    
    // check that we found something
    if (best_entry == -1) {
        return AP4_FAILURE;
    }
    
    // flush any queued samples
    FlushQueues();
    
    // reset tracker states
    for (unsigned int i=0; i<m_Trackers.ItemCount(); i++) {
        delete m_Trackers[i]->m_SampleTable;
        delete m_Trackers[i]->m_NextSample;
        m_Trackers[i]->m_SampleTable     = NULL;
        m_Trackers[i]->m_NextSample      = NULL;
        m_Trackers[i]->m_NextSampleIndex = 0;
        m_Trackers[i]->m_Eos             = false;
    }
        
    return AP4_SUCCESS;
}
Exemplo n.º 3
0
/*----------------------------------------------------------------------
|   AP4_Processor::ProcessFragments
+---------------------------------------------------------------------*/
AP4_Result
AP4_Processor::ProcessFragments(AP4_MoovAtom*              moov, 
                                AP4_List<AP4_MoofLocator>& moofs, 
                                AP4_ContainerAtom*         mfra,
                                AP4_ByteStream&            input, 
                                AP4_ByteStream&            output)
{
    // FIXME: this only works for non-changing moofs 
 
    for (AP4_List<AP4_MoofLocator>::Item* item = moofs.FirstItem();
                                          item;
                                          item = item->GetNext()) {
        AP4_MoofLocator*   locator     = item->GetData();
        AP4_ContainerAtom* moof        = locator->m_Moof;
        AP4_UI64           moof_offset = locator->m_Offset;
        AP4_UI64           mdat_payload_offset = moof_offset+moof->GetSize()+8;
        AP4_MovieFragment* fragment    = new AP4_MovieFragment(moof);
        AP4_Sample         sample;
        AP4_DataBuffer     sample_data_in;
        AP4_DataBuffer     sample_data_out;
        AP4_Result         result;
    
        // process all the traf atoms
        AP4_Array<AP4_Processor::FragmentHandler*> handlers;
        for (;AP4_Atom* atom = moof->GetChild(AP4_ATOM_TYPE_TRAF, handlers.ItemCount());) {
            AP4_ContainerAtom* traf = AP4_DYNAMIC_CAST(AP4_ContainerAtom, atom);
            AP4_Processor::FragmentHandler* handler = CreateFragmentHandler(traf);
            if (handler) result = handler->ProcessFragment();
            handlers.Append(handler);
        }
             
        // write the moof
        AP4_UI64 moof_out_start = 0;
        output.Tell(moof_out_start);
        bool moof_has_changed = false;
        moof->Write(output);
            
        // process all track runs
        for (unsigned int i=0; i<handlers.ItemCount(); i++) {
            AP4_FragmentSampleTable* sample_table = NULL;
            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));
            AP4_TfhdAtom* tfhd      = AP4_DYNAMIC_CAST(AP4_TfhdAtom, traf->GetChild(AP4_ATOM_TYPE_TFHD, i));
            
            // create a sample table object so we can read the sample data
            result = fragment->CreateSampleTable(moov, tfhd->GetTrackId(), &input, moof_offset, mdat_payload_offset, sample_table);
            if (AP4_FAILED(result)) return result;

            // compute the mdat size
            AP4_UI64 mdat_size = 0;
            for (unsigned int j=0; j<sample_table->GetSampleCount(); j++) {
                result = sample_table->GetSample(j, sample);
                if (AP4_FAILED(result)) return result;
                mdat_size += sample.GetSize();
            }
            
            // write an mdat header
            if (mdat_size > 0xFFFFFFFF-8) {
                // we don't support large mdat fragments
                return AP4_ERROR_OUT_OF_RANGE;
            }
            if (mdat_size) {
                output.WriteUI32((AP4_UI32)(8+mdat_size));
                output.WriteUI32(AP4_ATOM_TYPE_MDAT);
            }
            
#if defined(AP4_DEBUG)
            AP4_Position before;
            output.Tell(before);
#endif
            
            // write the mdat
            for (unsigned int j=0; j<sample_table->GetSampleCount(); j++) {
                result = sample_table->GetSample(j, sample);
                if (AP4_FAILED(result)) return result;
                sample.ReadData(sample_data_in);
                
                // 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;

                    // give the handler a chance to update the atoms
                    result = handler->FinishFragment();
                    if (AP4_SUCCEEDED(result)) moof_has_changed = true;
                } else {
                    // write the sample data (unmodified)
                    result = output.Write(sample_data_in.GetData(), sample_data_in.GetDataSize());
                    if (AP4_FAILED(result)) return result;
                }
            }
            
#if defined(AP4_DEBUG)
            AP4_Position after;
            output.Tell(after);
            AP4_ASSERT(after-before == mdat_size);
#endif
            delete sample_table;
        }
        
        // update the moof if needed
        AP4_UI64 mdat_out_end = 0;
        output.Tell(mdat_out_end);
        if (moof_has_changed) {
            output.Seek(moof_out_start);
            moof->Write(output);
            output.Seek(mdat_out_end);
        }
                
        // update the mfra if we have one
        if (mfra) {
            for (AP4_List<AP4_Atom>::Item* mfra_item = mfra->GetChildren().FirstItem();
                                           mfra_item;
                                           mfra_item = mfra_item->GetNext()) {
                if (mfra_item->GetData()->GetType() != AP4_ATOM_TYPE_TFRA) continue;
                AP4_TfraAtom* tfra = AP4_DYNAMIC_CAST(AP4_TfraAtom, mfra_item->GetData());
                if (tfra == NULL) continue;
                AP4_Array<AP4_TfraAtom::Entry>& entries     = tfra->GetEntries();
                AP4_Cardinal                    entry_count = entries.ItemCount();
                for (unsigned int i=0; i<entry_count; i++) {
                    if (entries[i].m_MoofOffset == locator->m_Offset) {
                        entries[i].m_MoofOffset = moof_out_start;
                    }
                }
            }
        }

        delete fragment;
    }
        
    return AP4_SUCCESS;
}