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