1 /*****************************************************************
2 |
3 |    AP4 - File Processor
4 |
5 |    Copyright 2002-2008 Axiomatic Systems, LLC
6 |
7 |
8 |    This file is part of Bento4/AP4 (MP4 Atom Processing Library).
9 |
10 |    Unless you have obtained Bento4 under a difference license,
11 |    this version of Bento4 is Bento4|GPL.
12 |    Bento4|GPL is free software; you can redistribute it and/or modify
13 |    it under the terms of the GNU General Public License as published by
14 |    the Free Software Foundation; either version 2, or (at your option)
15 |    any later version.
16 |
17 |    Bento4|GPL is distributed in the hope that it will be useful,
18 |    but WITHOUT ANY WARRANTY; without even the implied warranty of
19 |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 |    GNU General Public License for more details.
21 |
22 |    You should have received a copy of the GNU General Public License
23 |    along with Bento4|GPL; see the file COPYING.  If not, write to the
24 |    Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
25 |    02111-1307, USA.
26 |
27 ****************************************************************/
28 
29 /*----------------------------------------------------------------------
30 |   includes
31 +---------------------------------------------------------------------*/
32 #include "Ap4Processor.h"
33 #include "Ap4AtomSampleTable.h"
34 #include "Ap4MovieFragment.h"
35 #include "Ap4FragmentSampleTable.h"
36 #include "Ap4TfhdAtom.h"
37 #include "Ap4AtomFactory.h"
38 #include "Ap4Movie.h"
39 #include "Ap4Array.h"
40 #include "Ap4Sample.h"
41 #include "Ap4TrakAtom.h"
42 #include "Ap4TfraAtom.h"
43 #include "Ap4TrunAtom.h"
44 #include "Ap4TrexAtom.h"
45 #include "Ap4TkhdAtom.h"
46 #include "Ap4SidxAtom.h"
47 #include "Ap4DataBuffer.h"
48 #include "Ap4Debug.h"
49 
50 /*----------------------------------------------------------------------
51 |   types
52 +---------------------------------------------------------------------*/
53 struct AP4_SampleLocator {
AP4_SampleLocatorAP4_SampleLocator54     AP4_SampleLocator() :
55         m_TrakIndex(0),
56         m_SampleTable(NULL),
57         m_SampleIndex(0),
58         m_ChunkIndex(0) {}
59     AP4_Ordinal          m_TrakIndex;
60     AP4_AtomSampleTable* m_SampleTable;
61     AP4_Ordinal          m_SampleIndex;
62     AP4_Ordinal          m_ChunkIndex;
63     AP4_Sample           m_Sample;
64 };
65 
66 struct AP4_SampleCursor {
AP4_SampleCursorAP4_SampleCursor67     AP4_SampleCursor() : m_EndReached(false) {}
68     AP4_SampleLocator m_Locator;
69     bool              m_EndReached;
70 };
71 
72 struct AP4_AtomLocator {
AP4_AtomLocatorAP4_AtomLocator73     AP4_AtomLocator(AP4_Atom* atom, AP4_UI64 offset) :
74         m_Atom(atom),
75         m_Offset(offset) {}
76     AP4_Atom* m_Atom;
77     AP4_UI64  m_Offset;
78 };
79 
80 /*----------------------------------------------------------------------
81 |   AP4_DefaultFragmentHandler
82 +---------------------------------------------------------------------*/
83 class AP4_DefaultFragmentHandler: public AP4_Processor::FragmentHandler {
84 public:
AP4_DefaultFragmentHandler(AP4_Processor::TrackHandler * track_handler)85     AP4_DefaultFragmentHandler(AP4_Processor::TrackHandler* track_handler) :
86         m_TrackHandler(track_handler) {}
87     AP4_Result ProcessSample(AP4_DataBuffer& data_in,
88                              AP4_DataBuffer& data_out);
89 
90 private:
91     AP4_Processor::TrackHandler* m_TrackHandler;
92 };
93 
94 /*----------------------------------------------------------------------
95 |   AP4_DefaultFragmentHandler::ProcessSample
96 +---------------------------------------------------------------------*/
97 AP4_Result
ProcessSample(AP4_DataBuffer & data_in,AP4_DataBuffer & data_out)98 AP4_DefaultFragmentHandler::ProcessSample(AP4_DataBuffer& data_in, AP4_DataBuffer& data_out)
99 {
100     if (m_TrackHandler == NULL) {
101         data_out.SetData(data_in.GetData(), data_in.GetDataSize());
102         return AP4_SUCCESS;
103     }
104     return m_TrackHandler->ProcessSample(data_in, data_out);
105 }
106 
107 /*----------------------------------------------------------------------
108 |   FragmentMapEntry
109 +---------------------------------------------------------------------*/
110 typedef struct {
111     AP4_UI64 before;
112     AP4_UI64 after;
113 } FragmentMapEntry;
114 
115 /*----------------------------------------------------------------------
116 |   FindFragmentMapEntry
117 +---------------------------------------------------------------------*/
118 static const FragmentMapEntry*
FindFragmentMapEntry(AP4_Array<FragmentMapEntry> & fragment_map,AP4_UI64 fragment_offset)119 FindFragmentMapEntry(AP4_Array<FragmentMapEntry>& fragment_map, AP4_UI64 fragment_offset) {
120     int first = 0;
121     int last = fragment_map.ItemCount();
122     while (first < last) {
123         int middle = (last+first)/2;
124         AP4_UI64 middle_value = fragment_map[middle].before;
125         if (fragment_offset < middle_value) {
126             last = middle;
127         } else if (fragment_offset > middle_value) {
128             first = middle+1;
129         } else {
130             return &fragment_map[middle];
131         }
132     }
133 
134     return NULL;
135 }
136 
137 /*----------------------------------------------------------------------
138 |   AP4_Processor::ProcessFragments
139 +---------------------------------------------------------------------*/
140 AP4_Result
ProcessFragments(AP4_MoovAtom * moov,AP4_List<AP4_AtomLocator> & atoms,AP4_ContainerAtom * mfra,AP4_SidxAtom * sidx,AP4_Position sidx_position,AP4_ByteStream & input,AP4_ByteStream & output)141 AP4_Processor::ProcessFragments(AP4_MoovAtom*              moov,
142                                 AP4_List<AP4_AtomLocator>& atoms,
143                                 AP4_ContainerAtom*         mfra,
144                                 AP4_SidxAtom*              sidx,
145                                 AP4_Position               sidx_position,
146                                 AP4_ByteStream&            input,
147                                 AP4_ByteStream&            output)
148 {
149     unsigned int fragment_index = 0;
150     AP4_Array<FragmentMapEntry> fragment_map;
151 
152     for (AP4_List<AP4_AtomLocator>::Item* item = atoms.FirstItem();
153                                           item;
154                                           item = item->GetNext(), ++fragment_index) {
155         AP4_AtomLocator*   locator     = item->GetData();
156         AP4_Atom*          atom        = locator->m_Atom;
157         AP4_UI64           atom_offset = locator->m_Offset;
158         AP4_UI64           mdat_payload_offset = atom_offset+atom->GetSize()+AP4_ATOM_HEADER_SIZE;
159         AP4_Sample         sample;
160         AP4_DataBuffer     sample_data_in;
161         AP4_DataBuffer     sample_data_out;
162         AP4_Result         result;
163 
164         // if this is not a moof atom, just write it back and continue
165         if (atom->GetType() != AP4_ATOM_TYPE_MOOF) {
166             result = atom->Write(output);
167             if (AP4_FAILED(result)) return result;
168             continue;
169         }
170 
171         // parse the moof
172         AP4_ContainerAtom* moof = AP4_DYNAMIC_CAST(AP4_ContainerAtom, atom);
173         AP4_MovieFragment* fragment = new AP4_MovieFragment(moof);
174 
175         // process all the traf atoms
176         AP4_Array<AP4_Processor::FragmentHandler*> handlers;
177         AP4_Array<AP4_FragmentSampleTable*> sample_tables;
178         for (;AP4_Atom* child = moof->GetChild(AP4_ATOM_TYPE_TRAF, handlers.ItemCount());) {
179             AP4_ContainerAtom* traf = AP4_DYNAMIC_CAST(AP4_ContainerAtom, child);
180             AP4_TfhdAtom* tfhd = AP4_DYNAMIC_CAST(AP4_TfhdAtom, traf->GetChild(AP4_ATOM_TYPE_TFHD));
181 
182             // find the 'trak' for this track
183             AP4_TrakAtom* trak = NULL;
184             for (AP4_List<AP4_Atom>::Item* child_item = moov->GetChildren().FirstItem();
185                                            child_item;
186                                            child_item = child_item->GetNext()) {
187                 AP4_Atom* child_atom = child_item->GetData();
188                 if (child_atom->GetType() == AP4_ATOM_TYPE_TRAK) {
189                     trak = AP4_DYNAMIC_CAST(AP4_TrakAtom, child_atom);
190                     if (trak) {
191                         AP4_TkhdAtom* tkhd = AP4_DYNAMIC_CAST(AP4_TkhdAtom, trak->GetChild(AP4_ATOM_TYPE_TKHD));
192                         if (tkhd && tkhd->GetTrackId() == tfhd->GetTrackId()) {
193                             break;
194                         }
195                     }
196                     trak = NULL;
197                 }
198             }
199 
200             // find the 'trex' for this track
201             AP4_ContainerAtom* mvex = NULL;
202             AP4_TrexAtom*      trex = NULL;
203             mvex = AP4_DYNAMIC_CAST(AP4_ContainerAtom, moov->GetChild(AP4_ATOM_TYPE_MVEX));
204             if (mvex) {
205                 for (AP4_List<AP4_Atom>::Item* child_item = mvex->GetChildren().FirstItem();
206                                                child_item;
207                                                child_item = child_item->GetNext()) {
208                     AP4_Atom* child_atom = child_item->GetData();
209                     if (child_atom->GetType() == AP4_ATOM_TYPE_TREX) {
210                         trex = AP4_DYNAMIC_CAST(AP4_TrexAtom, child_atom);
211                         if (trex && trex->GetTrackId() == tfhd->GetTrackId()) {
212                             break;
213                         }
214                         trex = NULL;
215                     }
216                 }
217             }
218 
219             // create the handler for this traf
220             AP4_Processor::FragmentHandler* handler = CreateFragmentHandler(trak, trex, traf, input, atom_offset);
221             if (handler) {
222                 result = handler->ProcessFragment();
223                 if (AP4_FAILED(result)) return result;
224             }
225             handlers.Append(handler);
226 
227             // create a sample table object so we can read the sample data
228             AP4_FragmentSampleTable* sample_table = NULL;
229             result = fragment->CreateSampleTable(moov, tfhd->GetTrackId(), &input, atom_offset, mdat_payload_offset, 0, sample_table);
230             if (AP4_FAILED(result)) return result;
231             sample_tables.Append(sample_table);
232 
233             // let the handler look at the samples before we process them
234             if (handler) result = handler->PrepareForSamples(sample_table);
235             if (AP4_FAILED(result)) return result;
236         }
237 
238         // write the moof
239         AP4_UI64 moof_out_start = 0;
240         output.Tell(moof_out_start);
241         moof->Write(output);
242 
243         // remember the location of this fragment
244         FragmentMapEntry map_entry = {atom_offset, moof_out_start};
245         fragment_map.Append(map_entry);
246 
247         // write an mdat header
248         AP4_Position mdat_out_start;
249         AP4_UI64 mdat_size = AP4_ATOM_HEADER_SIZE;
250         output.Tell(mdat_out_start);
251         output.WriteUI32(0);
252         output.WriteUI32(AP4_ATOM_TYPE_MDAT);
253 
254         // process all track runs
255         for (unsigned int i=0; i<handlers.ItemCount(); i++) {
256             AP4_Processor::FragmentHandler* handler = handlers[i];
257 
258             // get the track ID
259             AP4_ContainerAtom* traf = AP4_DYNAMIC_CAST(AP4_ContainerAtom, moof->GetChild(AP4_ATOM_TYPE_TRAF, i));
260             if (traf == NULL) continue;
261             AP4_TfhdAtom* tfhd = AP4_DYNAMIC_CAST(AP4_TfhdAtom, traf->GetChild(AP4_ATOM_TYPE_TFHD));
262 
263             // compute the base data offset
264             AP4_UI64 base_data_offset;
265             if (tfhd->GetFlags() & AP4_TFHD_FLAG_BASE_DATA_OFFSET_PRESENT) {
266                 base_data_offset = mdat_out_start+AP4_ATOM_HEADER_SIZE;
267             } else {
268                 base_data_offset = moof_out_start;
269             }
270 
271             // build a list of all trun atoms
272             AP4_Array<AP4_TrunAtom*> truns;
273             for (AP4_List<AP4_Atom>::Item* child_item = traf->GetChildren().FirstItem();
274                                            child_item;
275                                            child_item = child_item->GetNext()) {
276                 AP4_Atom* child_atom = child_item->GetData();
277                 if (child_atom->GetType() == AP4_ATOM_TYPE_TRUN) {
278                     AP4_TrunAtom* trun = AP4_DYNAMIC_CAST(AP4_TrunAtom, child_atom);
279                     truns.Append(trun);
280                 }
281             }
282             AP4_Ordinal   trun_index        = 0;
283             AP4_Ordinal   trun_sample_index = 0;
284             AP4_TrunAtom* trun = truns[0];
285             trun->SetDataOffset((AP4_SI32)((mdat_out_start+mdat_size)-base_data_offset));
286 
287             // write the mdat
288             AP4_UI32 default_sample_size = 0;
289             for (unsigned int j=0; j<sample_tables[i]->GetSampleCount(); j++, trun_sample_index++) {
290                 // advance the trun index if necessary
291                 if (trun_sample_index >= trun->GetEntries().ItemCount()) {
292                     trun = truns[++trun_index];
293                     trun->SetDataOffset((AP4_SI32)((mdat_out_start+mdat_size)-base_data_offset));
294                     trun_sample_index = 0;
295                 }
296 
297                 // get the next sample
298                 result = sample_tables[i]->GetSample(j, sample);
299                 if (AP4_FAILED(result)) return result;
300                 sample.ReadData(sample_data_in);
301 
302                 // process the sample data
303                 if (handler) {
304                     result = handler->ProcessSample(sample_data_in, sample_data_out);
305                     if (AP4_FAILED(result)) return result;
306 
307                     // write the sample data
308                     result = output.Write(sample_data_out.GetData(), sample_data_out.GetDataSize());
309                     if (AP4_FAILED(result)) return result;
310 
311                     // update the mdat size
312                     mdat_size += sample_data_out.GetDataSize();
313 
314                     // update the trun entry
315                     trun->UseEntries()[trun_sample_index].sample_size = sample_data_out.GetDataSize();
316 
317                     // if this entry uses the default sample size, adjust the default accordingly
318                     // (NOTE: there's only one default, so this assumes, of course, that all sample
319                     // sizes change the same way, if they change at all)
320                     if (default_sample_size == 0 && (trun->GetFlags() & AP4_TRUN_FLAG_SAMPLE_SIZE_PRESENT) == 0) {
321                         default_sample_size = sample_data_out.GetDataSize();
322                     }
323                 } else {
324                     // write the sample data (unmodified)
325                     result = output.Write(sample_data_in.GetData(), sample_data_in.GetDataSize());
326                     if (AP4_FAILED(result)) return result;
327 
328                     // update the mdat size
329                     mdat_size += sample_data_in.GetDataSize();
330                 }
331             }
332 
333             if (handler) {
334                 // update the tfhd header
335                 if (tfhd->GetFlags() & AP4_TFHD_FLAG_BASE_DATA_OFFSET_PRESENT) {
336                     tfhd->SetBaseDataOffset(mdat_out_start+AP4_ATOM_HEADER_SIZE);
337                 }
338                 if (tfhd->GetFlags() & AP4_TFHD_FLAG_DEFAULT_SAMPLE_SIZE_PRESENT) {
339                     if (default_sample_size) {
340                         tfhd->SetDefaultSampleSize(default_sample_size);
341                     }
342                 }
343 
344                 // give the handler a chance to update the atoms
345                 handler->FinishFragment();
346             }
347         }
348 
349         // update the mdat header
350         AP4_Position mdat_out_end;
351         output.Tell(mdat_out_end);
352 #if defined(AP4_DEBUG)
353         AP4_ASSERT(mdat_out_end-mdat_out_start == mdat_size);
354 #endif
355         output.Seek(mdat_out_start);
356         output.WriteUI32((AP4_UI32)mdat_size);
357         output.Seek(mdat_out_end);
358 
359         // update the moof if needed
360         output.Seek(moof_out_start);
361         moof->Write(output);
362         output.Seek(mdat_out_end);
363 
364         // update the sidx if we have one
365         if (sidx && fragment_index < sidx->GetReferences().ItemCount()) {
366             if (fragment_index == 0) {
367                 sidx->SetFirstOffset(moof_out_start-(sidx_position+sidx->GetSize()));
368             }
369             AP4_LargeSize fragment_size = mdat_out_end-moof_out_start;
370             AP4_SidxAtom::Reference& sidx_ref = sidx->UseReferences()[fragment_index];
371             sidx_ref.m_ReferencedSize = (AP4_UI32)fragment_size;
372         }
373 
374         // cleanup
375         delete fragment;
376 
377         for (unsigned int i=0; i<handlers.ItemCount(); i++) {
378             delete handlers[i];
379         }
380         for (unsigned int i=0; i<sample_tables.ItemCount(); i++) {
381             delete sample_tables[i];
382         }
383     }
384 
385     // update the mfra if we have one
386     if (mfra) {
387         for (AP4_List<AP4_Atom>::Item* mfra_item = mfra->GetChildren().FirstItem();
388                                        mfra_item;
389                                        mfra_item = mfra_item->GetNext()) {
390             if (mfra_item->GetData()->GetType() != AP4_ATOM_TYPE_TFRA) continue;
391             AP4_TfraAtom* tfra = AP4_DYNAMIC_CAST(AP4_TfraAtom, mfra_item->GetData());
392             if (tfra == NULL) continue;
393             AP4_Array<AP4_TfraAtom::Entry>& entries     = tfra->GetEntries();
394             AP4_Cardinal                    entry_count = entries.ItemCount();
395             for (unsigned int i=0; i<entry_count; i++) {
396                 const FragmentMapEntry* found = FindFragmentMapEntry(fragment_map, entries[i].m_MoofOffset);
397                 if (found) {
398                     entries[i].m_MoofOffset = found->after;
399                 }
400             }
401         }
402     }
403 
404     return AP4_SUCCESS;
405 }
406 
407 /*----------------------------------------------------------------------
408 |   AP4_Processor::CreateFragmentHandler
409 +---------------------------------------------------------------------*/
410 AP4_Processor::FragmentHandler*
CreateFragmentHandler(AP4_TrakAtom *,AP4_TrexAtom *,AP4_ContainerAtom * traf,AP4_ByteStream &,AP4_Position)411 AP4_Processor::CreateFragmentHandler(AP4_TrakAtom*      /* trak */,
412                                      AP4_TrexAtom*      /* trex */,
413                                      AP4_ContainerAtom* traf,
414                                      AP4_ByteStream&    /* moof_data   */,
415                                      AP4_Position       /* moof_offset */)
416 {
417     // find the matching track handler
418     for (unsigned int i=0; i<m_TrackIds.ItemCount(); i++) {
419         AP4_TfhdAtom* tfhd = AP4_DYNAMIC_CAST(AP4_TfhdAtom, traf->GetChild(AP4_ATOM_TYPE_TFHD));
420         if (tfhd && m_TrackIds[i] == tfhd->GetTrackId()) {
421             return new AP4_DefaultFragmentHandler(m_TrackHandlers[i]);
422         }
423     }
424 
425     return NULL;
426 }
427 
428 /*----------------------------------------------------------------------
429 |   AP4_Processor::Process
430 +---------------------------------------------------------------------*/
431 AP4_Result
Process(AP4_ByteStream & input,AP4_ByteStream & output,AP4_ByteStream * fragments,ProgressListener * listener,AP4_AtomFactory & atom_factory)432 AP4_Processor::Process(AP4_ByteStream&   input,
433                        AP4_ByteStream&   output,
434                        AP4_ByteStream*   fragments,
435                        ProgressListener* listener,
436                        AP4_AtomFactory&  atom_factory)
437 {
438     // read all atoms.
439     // keep all atoms except [mdat]
440     // keep a ref to [moov]
441     // put [moof] atoms in a separate list
442     AP4_AtomParent              top_level;
443     AP4_MoovAtom*               moov = NULL;
444     AP4_ContainerAtom*          mfra = NULL;
445     AP4_SidxAtom*               sidx = NULL;
446     AP4_List<AP4_AtomLocator>   frags;
447     AP4_UI64                    stream_offset = 0;
448     bool                        in_fragments = false;
449     unsigned int                sidx_count = 0;
450     for (AP4_Atom* atom = NULL;
451         AP4_SUCCEEDED(atom_factory.CreateAtomFromStream(input, atom));
452         input.Tell(stream_offset)) {
453         if (atom->GetType() == AP4_ATOM_TYPE_MDAT) {
454             delete atom;
455             continue;
456         } else if (atom->GetType() == AP4_ATOM_TYPE_MOOV) {
457             moov = AP4_DYNAMIC_CAST(AP4_MoovAtom, atom);
458             if (fragments) break;
459         } else if (atom->GetType() == AP4_ATOM_TYPE_MFRA) {
460             mfra = AP4_DYNAMIC_CAST(AP4_ContainerAtom, atom);
461             continue;
462         } else if (atom->GetType() == AP4_ATOM_TYPE_SIDX) {
463             // don't keep the index, it is likely to be invalidated, we will recompute it later
464             ++sidx_count;
465             if (sidx == NULL) {
466                 sidx = AP4_DYNAMIC_CAST(AP4_SidxAtom, atom);
467             } else {
468                 delete atom;
469                 continue;
470             }
471         } else if (atom->GetType() == AP4_ATOM_TYPE_SSIX) {
472             // don't keep the index, it is likely to be invalidated
473             delete atom;
474             continue;
475         } else if (!fragments && (in_fragments || atom->GetType() == AP4_ATOM_TYPE_MOOF)) {
476             in_fragments = true;
477             frags.Add(new AP4_AtomLocator(atom, stream_offset));
478             continue;
479         }
480         top_level.AddChild(atom);
481     }
482 
483     // check that we have at most one sidx (we can't deal with multi-sidx streams here
484     if (sidx_count > 1) {
485         top_level.RemoveChild(sidx);
486         delete sidx;
487         sidx = NULL;
488     }
489 
490     // if we have a fragments stream, get the fragment locators from there
491     if (fragments) {
492         stream_offset = 0;
493         for (AP4_Atom* atom = NULL;
494             AP4_SUCCEEDED(atom_factory.CreateAtomFromStream(*fragments, atom));
495             fragments->Tell(stream_offset)) {
496             if (atom->GetType() == AP4_ATOM_TYPE_MDAT) {
497                 delete atom;
498                 continue;
499             }
500             frags.Add(new AP4_AtomLocator(atom, stream_offset));
501         }
502     }
503 
504     // initialize the processor
505     AP4_Result result = Initialize(top_level, input);
506     if (AP4_FAILED(result)) return result;
507 
508     // process the tracks if we have a moov atom
509     AP4_Array<AP4_SampleLocator> locators;
510     AP4_Cardinal                 track_count       = 0;
511     AP4_List<AP4_TrakAtom>*      trak_atoms        = NULL;
512     AP4_LargeSize                mdat_payload_size = 0;
513     AP4_SampleCursor*            cursors           = NULL;
514     if (moov) {
515         // build an array of track sample locators
516         trak_atoms = &moov->GetTrakAtoms();
517         track_count = trak_atoms->ItemCount();
518         cursors = new AP4_SampleCursor[track_count];
519         m_TrackHandlers.SetItemCount(track_count);
520         m_TrackIds.SetItemCount(track_count);
521         for (AP4_Ordinal i=0; i<track_count; i++) {
522             m_TrackHandlers[i] = NULL;
523             m_TrackIds[i] = 0;
524         }
525 
526         unsigned int index = 0;
527         for (AP4_List<AP4_TrakAtom>::Item* item = trak_atoms->FirstItem(); item; item=item->GetNext()) {
528             AP4_TrakAtom* trak = item->GetData();
529 
530             // find the stsd atom
531             AP4_ContainerAtom* stbl = AP4_DYNAMIC_CAST(AP4_ContainerAtom, trak->FindChild("mdia/minf/stbl"));
532             if (stbl == NULL) continue;
533 
534             // see if there's an external data source for this track
535             AP4_ByteStream* trak_data_stream = &input;
536             for (AP4_List<ExternalTrackData>::Item* ditem = m_ExternalTrackData.FirstItem(); ditem; ditem=ditem->GetNext()) {
537                 ExternalTrackData* tdata = ditem->GetData();
538                 if (tdata->m_TrackId == trak->GetId()) {
539                     trak_data_stream = tdata->m_MediaData;
540                     break;
541                 }
542             }
543 
544             // create the track handler
545             m_TrackHandlers[index] = CreateTrackHandler(trak);
546             m_TrackIds[index]      = trak->GetId();
547             cursors[index].m_Locator.m_TrakIndex   = index;
548             cursors[index].m_Locator.m_SampleTable = new AP4_AtomSampleTable(stbl, *trak_data_stream);
549             cursors[index].m_Locator.m_SampleIndex = 0;
550             cursors[index].m_Locator.m_ChunkIndex  = 0;
551             if (cursors[index].m_Locator.m_SampleTable->GetSampleCount()) {
552                 cursors[index].m_Locator.m_SampleTable->GetSample(0, cursors[index].m_Locator.m_Sample);
553             } else {
554                 cursors[index].m_EndReached = true;
555             }
556 
557             index++;
558         }
559 
560         // figure out the layout of the chunks
561         for (;;) {
562             // see which is the next sample to write
563             AP4_UI64 min_offset = (AP4_UI64)(-1);
564             int cursor = -1;
565             for (unsigned int i=0; i<track_count; i++) {
566                 if (!cursors[i].m_EndReached &&
567                     cursors[i].m_Locator.m_SampleTable &&
568                     cursors[i].m_Locator.m_Sample.GetOffset() <= min_offset) {
569                     min_offset = cursors[i].m_Locator.m_Sample.GetOffset();
570                     cursor = i;
571                 }
572             }
573 
574             // stop if all cursors are exhausted
575             if (cursor == -1) break;
576 
577             // append this locator to the layout list
578             AP4_SampleLocator& locator = cursors[cursor].m_Locator;
579             locators.Append(locator);
580 
581             // move the cursor to the next sample
582             locator.m_SampleIndex++;
583             if (locator.m_SampleIndex == locator.m_SampleTable->GetSampleCount()) {
584                 // mark this track as completed
585                 cursors[cursor].m_EndReached = true;
586             } else {
587                 // get the next sample info
588                 locator.m_SampleTable->GetSample(locator.m_SampleIndex, locator.m_Sample);
589                 AP4_Ordinal skip, sdesc;
590                 locator.m_SampleTable->GetChunkForSample(locator.m_SampleIndex,
591                                                          locator.m_ChunkIndex,
592                                                          skip, sdesc);
593             }
594         }
595 
596         // update the stbl atoms and compute the mdat size
597         int current_track = -1;
598         int current_chunk = -1;
599         AP4_Position current_chunk_offset = 0;
600         AP4_Size current_chunk_size = 0;
601         for (AP4_Ordinal i=0; i<locators.ItemCount(); i++) {
602             AP4_SampleLocator& locator = locators[i];
603             if ((int)locator.m_TrakIndex  != current_track ||
604                 (int)locator.m_ChunkIndex != current_chunk) {
605                 // start a new chunk for this track
606                 current_chunk_offset += current_chunk_size;
607                 current_chunk_size = 0;
608                 current_track = locator.m_TrakIndex;
609                 current_chunk = locator.m_ChunkIndex;
610                 locator.m_SampleTable->SetChunkOffset(locator.m_ChunkIndex, current_chunk_offset);
611             }
612             AP4_Size sample_size;
613             TrackHandler* handler = m_TrackHandlers[locator.m_TrakIndex];
614             if (handler) {
615                 sample_size = handler->GetProcessedSampleSize(locator.m_Sample);
616                 locator.m_SampleTable->SetSampleSize(locator.m_SampleIndex, sample_size);
617             } else {
618                 sample_size = locator.m_Sample.GetSize();
619             }
620             current_chunk_size += sample_size;
621             mdat_payload_size  += sample_size;
622         }
623 
624         // process the tracks (ex: sample descriptions processing)
625         for (AP4_Ordinal i=0; i<track_count; i++) {
626             TrackHandler* handler = m_TrackHandlers[i];
627             if (handler) handler->ProcessTrack();
628         }
629     }
630 
631     // finalize the processor
632     Finalize(top_level);
633 
634     if (!fragments) {
635         // calculate the size of all atoms combined
636         AP4_UI64 atoms_size = 0;
637         top_level.GetChildren().Apply(AP4_AtomSizeAdder(atoms_size));
638 
639         // see if we need a 64-bit or 32-bit mdat
640         AP4_Size mdat_header_size = AP4_ATOM_HEADER_SIZE;
641         if (mdat_payload_size+mdat_header_size > 0xFFFFFFFF) {
642             // we need a 64-bit size
643             mdat_header_size += 8;
644         }
645 
646         // adjust the chunk offsets
647         for (AP4_Ordinal i=0; i<track_count; i++) {
648             AP4_TrakAtom* trak;
649             trak_atoms->Get(i, trak);
650             trak->AdjustChunkOffsets(atoms_size+mdat_header_size);
651         }
652 
653         // write all atoms
654         top_level.GetChildren().Apply(AP4_AtomListWriter(output));
655 
656         // write mdat header
657         if (mdat_payload_size) {
658             if (mdat_header_size == AP4_ATOM_HEADER_SIZE) {
659                 // 32-bit size
660                 output.WriteUI32((AP4_UI32)(mdat_header_size+mdat_payload_size));
661                 output.WriteUI32(AP4_ATOM_TYPE_MDAT);
662             } else {
663                 // 64-bit size
664                 output.WriteUI32(1);
665                 output.WriteUI32(AP4_ATOM_TYPE_MDAT);
666                 output.WriteUI64(mdat_header_size+mdat_payload_size);
667             }
668         }
669     }
670 
671     // write the samples
672     if (moov) {
673         if (!fragments) {
674 #if defined(AP4_DEBUG)
675             AP4_Position before;
676             output.Tell(before);
677 #endif
678             AP4_Sample     sample;
679             AP4_DataBuffer data_in;
680             AP4_DataBuffer data_out;
681             for (unsigned int i=0; i<locators.ItemCount(); i++) {
682                 AP4_SampleLocator& locator = locators[i];
683                 locator.m_Sample.ReadData(data_in);
684                 TrackHandler* handler = m_TrackHandlers[locator.m_TrakIndex];
685                 if (handler) {
686                     result = handler->ProcessSample(data_in, data_out);
687                     if (AP4_FAILED(result)) return result;
688                     output.Write(data_out.GetData(), data_out.GetDataSize());
689                 } else {
690                     output.Write(data_in.GetData(), data_in.GetDataSize());
691                 }
692 
693                 // notify the progress listener
694                 if (listener) {
695                     listener->OnProgress(i+1, locators.ItemCount());
696                 }
697             }
698 
699 #if defined(AP4_DEBUG)
700             AP4_Position after;
701             output.Tell(after);
702             AP4_ASSERT(after-before == mdat_payload_size);
703 #endif
704         }
705 
706         // find the position of the sidx atom
707         AP4_Position sidx_position = 0;
708         if (sidx) {
709             for (AP4_List<AP4_Atom>::Item* item = top_level.GetChildren().FirstItem();
710                                            item;
711                                            item = item->GetNext()) {
712                 AP4_Atom* atom = item->GetData();
713                 if (atom->GetType() == AP4_ATOM_TYPE_SIDX) {
714                     break;
715                 }
716                 sidx_position += atom->GetSize();
717             }
718         }
719 
720         // process the fragments, if any
721         result = ProcessFragments(moov, frags, mfra, sidx, sidx_position, fragments?*fragments:input, output);
722         if (AP4_FAILED(result)) return result;
723 
724         // update and re-write the sidx if we have one
725         if (sidx && sidx_position) {
726             AP4_Position where = 0;
727             output.Tell(where);
728             output.Seek(sidx_position);
729             result = sidx->Write(output);
730             if (AP4_FAILED(result)) return result;
731             output.Seek(where);
732         }
733 
734         if (!fragments) {
735             // write the mfra atom at the end if we have one
736             if (mfra) {
737                 mfra->Write(output);
738             }
739         }
740 
741         // cleanup
742         for (AP4_Ordinal i=0; i<track_count; i++) {
743             delete cursors[i].m_Locator.m_SampleTable;
744             delete m_TrackHandlers[i];
745         }
746         m_TrackHandlers.Clear();
747         delete[] cursors;
748     }
749 
750     // cleanup
751     frags.DeleteReferences();
752     delete mfra;
753     if (fragments) {
754         // with a fragments stream, `moov` isn't inclued in `top_level`
755         // so we need to delete it here
756         delete moov;
757     }
758 
759     return AP4_SUCCESS;
760 }
761 
762 /*----------------------------------------------------------------------
763 |   AP4_Processor::Process
764 +---------------------------------------------------------------------*/
765 AP4_Result
Process(AP4_ByteStream & input,AP4_ByteStream & output,ProgressListener * listener,AP4_AtomFactory & atom_factory)766 AP4_Processor::Process(AP4_ByteStream&   input,
767                        AP4_ByteStream&   output,
768                        ProgressListener* listener,
769                        AP4_AtomFactory&  atom_factory)
770 {
771     return Process(input, output, NULL, listener, atom_factory);
772 }
773 
774 /*----------------------------------------------------------------------
775 |   AP4_Processor::Process
776 +---------------------------------------------------------------------*/
777 AP4_Result
Process(AP4_ByteStream & fragments,AP4_ByteStream & output,AP4_ByteStream & init,ProgressListener * listener,AP4_AtomFactory & atom_factory)778 AP4_Processor::Process(AP4_ByteStream&   fragments,
779                        AP4_ByteStream&   output,
780                        AP4_ByteStream&   init,
781                        ProgressListener* listener,
782                        AP4_AtomFactory&  atom_factory)
783 {
784     return Process(init, output, &fragments, listener, atom_factory);
785 }
786 
787 /*----------------------------------------------------------------------
788 |   AP4_Processor:Initialize
789 +---------------------------------------------------------------------*/
790 AP4_Result
Initialize(AP4_AtomParent &,AP4_ByteStream &,ProgressListener *)791 AP4_Processor::Initialize(AP4_AtomParent&   /* top_level */,
792                           AP4_ByteStream&   /* stream    */,
793                           ProgressListener* /* listener  */)
794 {
795     // default implementation: do nothing
796     return AP4_SUCCESS;
797 }
798 
799 /*----------------------------------------------------------------------
800 |   AP4_Processor:Finalize
801 +---------------------------------------------------------------------*/
802 AP4_Result
Finalize(AP4_AtomParent &,ProgressListener *)803 AP4_Processor::Finalize(AP4_AtomParent&   /* top_level */,
804                         ProgressListener* /* listener */ )
805 {
806     // default implementation: do nothing
807     return AP4_SUCCESS;
808 }
809 
810 /*----------------------------------------------------------------------
811 |   AP4_Processor::TrackHandler Dynamic Cast Anchor
812 +---------------------------------------------------------------------*/
813 AP4_DEFINE_DYNAMIC_CAST_ANCHOR_S(AP4_Processor::TrackHandler, TrackHandler)
814