1 /*****************************************************************
2 |
3 |    AP4 - Fragment Based Sample Tables
4 |
5 |    Copyright 2002-2009 Axiomatic Systems, LLC
6 |
7 |
8 |    This atom is part of AP4 (MP4 Audio 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 atom 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 "Ap4FragmentSampleTable.h"
33 #include "Ap4ByteStream.h"
34 #include "Ap4Sample.h"
35 #include "Ap4TrexAtom.h"
36 #include "Ap4TfhdAtom.h"
37 #include "Ap4ContainerAtom.h"
38 #include "Ap4TrunAtom.h"
39 #include "Ap4TfdtAtom.h"
40 #include "Ap4MovieFragment.h"
41 
42 /*----------------------------------------------------------------------
43 |   AP4_FragmentSampleTable::AP4_FragmentSampleTable
44 +---------------------------------------------------------------------*/
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 mdat_payload_size,AP4_UI64 dts_origin)45 AP4_FragmentSampleTable::AP4_FragmentSampleTable(AP4_ContainerAtom* traf,
46   AP4_TrexAtom*      trex,
47   AP4_Cardinal       internal_track_id,
48   AP4_ByteStream*    sample_stream,
49   AP4_Position       moof_offset,
50   AP4_Position       mdat_payload_offset,
51   AP4_UI64           mdat_payload_size,
52   AP4_UI64           dts_origin)
53   : m_Duration(0)
54   , m_InternalTrackId(internal_track_id)
55 {
56     AP4_TfhdAtom* tfhd = AP4_DYNAMIC_CAST(AP4_TfhdAtom, traf->GetChild(AP4_ATOM_TYPE_TFHD));
57     if (tfhd == NULL) return;
58 
59     // count all the samples and reserve space for them
60     unsigned int sample_count = 0;
61     for (AP4_List<AP4_Atom>::Item* item = traf->GetChildren().FirstItem();
62                                    item;
63                                    item = item->GetNext()) {
64         AP4_Atom* atom = item->GetData();
65         if (atom->GetType() == AP4_ATOM_TYPE_TRUN) {
66             AP4_TrunAtom* trun = AP4_DYNAMIC_CAST(AP4_TrunAtom, atom);
67             if (trun) sample_count += trun->GetEntries().ItemCount();
68         }
69     }
70     m_Samples.EnsureCapacity(sample_count);
71 
72     // check if we have a timecode base
73     AP4_TfdtAtom* tfdt = AP4_DYNAMIC_CAST(AP4_TfdtAtom, traf->GetChild(AP4_ATOM_TYPE_TFDT));
74     if (tfdt) {
75         dts_origin = tfdt->GetBaseMediaDecodeTime();
76     }
77 
78     // process all the trun atoms
79     AP4_UI32 trun_flags(0);
80     for (AP4_List<AP4_Atom>::Item* item = traf->GetChildren().FirstItem();
81                                    item;
82                                    item = item->GetNext()) {
83         AP4_Atom* atom = item->GetData();
84         if (atom->GetType() == AP4_ATOM_TYPE_TRUN) {
85             AP4_TrunAtom* trun = AP4_DYNAMIC_CAST(AP4_TrunAtom, atom);
86             if (trun) {
87                 AP4_Result result = AddTrun(trun,
88                                             tfhd,
89                                             trex,
90                                             sample_stream,
91                                             moof_offset,
92                                             mdat_payload_offset,
93                                             dts_origin);
94                 if (AP4_FAILED(result)) return;
95                 trun_flags |= trun->GetFlags();
96             }
97         }
98     }
99     // Hack if we have a single sample and default sample size is wrong (hbo ttml)
100     if (m_Samples.ItemCount() == 1 && (trun_flags & AP4_TRUN_FLAG_SAMPLE_SIZE_PRESENT) == 0)
101       m_Samples[0].SetSize(mdat_payload_size);
102 }
103 
104 /*----------------------------------------------------------------------
105 |   AP4_FragmentSampleTable::~AP4_FragmentSampleTable
106 +---------------------------------------------------------------------*/
~AP4_FragmentSampleTable()107 AP4_FragmentSampleTable::~AP4_FragmentSampleTable()
108 {
109 }
110 
111 /*----------------------------------------------------------------------
112 |   AP4_FragmentSampleTable::AddTrun
113 +---------------------------------------------------------------------*/
114 AP4_Result
AddTrun(AP4_TrunAtom * trun,AP4_TfhdAtom * tfhd,AP4_TrexAtom * trex,AP4_ByteStream * sample_stream,AP4_Position moof_offset,AP4_Position & payload_offset,AP4_UI64 & dts_origin)115 AP4_FragmentSampleTable::AddTrun(AP4_TrunAtom*   trun,
116                                  AP4_TfhdAtom*   tfhd,
117                                  AP4_TrexAtom*   trex,
118                                  AP4_ByteStream* sample_stream,
119                                  AP4_Position    moof_offset,
120                                  AP4_Position&   payload_offset,
121                                  AP4_UI64&       dts_origin)
122 {
123     AP4_Flags tfhd_flags = tfhd->GetFlags();
124     AP4_Flags trun_flags = trun->GetFlags();
125 
126     // update the number of samples
127     unsigned int start = m_Samples.ItemCount();
128     m_Samples.SetItemCount(start + trun->GetEntries().ItemCount());
129 
130     // base data offset
131     AP4_Position data_offset = 0;
132     if (tfhd_flags & AP4_TFHD_FLAG_BASE_DATA_OFFSET_PRESENT) {
133         data_offset = tfhd->GetBaseDataOffset();
134     } else {
135         data_offset = moof_offset;
136     }
137     if (trun_flags & AP4_TRUN_FLAG_DATA_OFFSET_PRESENT) {
138         data_offset += trun->GetDataOffset();
139     }
140     // MS hack
141     if (data_offset < payload_offset) {
142         data_offset = payload_offset;
143     } else {
144         payload_offset = data_offset;
145     }
146 
147     // sample description index
148     AP4_UI32 sample_description_index = 0;
149     if (tfhd_flags & AP4_TFHD_FLAG_SAMPLE_DESCRIPTION_INDEX_PRESENT) {
150         sample_description_index = tfhd->GetSampleDescriptionIndex();
151     } else if (trex) {
152         sample_description_index = trex->GetDefaultSampleDescriptionIndex();
153     }
154 
155     // default sample size
156     AP4_UI32 default_sample_size = 0;
157     if (tfhd_flags & AP4_TFHD_FLAG_DEFAULT_SAMPLE_SIZE_PRESENT) {
158         default_sample_size = tfhd->GetDefaultSampleSize();
159     } else if (trex) {
160         default_sample_size = trex->GetDefaultSampleSize();
161     }
162 
163     // default sample duration
164     AP4_UI32 default_sample_duration = 0;
165     if (tfhd_flags & AP4_TFHD_FLAG_DEFAULT_SAMPLE_DURATION_PRESENT) {
166         default_sample_duration = tfhd->GetDefaultSampleDuration();
167     } else if (trex) {
168         default_sample_duration = trex->GetDefaultSampleDuration();
169     }
170 
171     // default sample flags
172     AP4_UI32 default_sample_flags = 0;
173     if (tfhd_flags & AP4_TFHD_FLAG_DEFAULT_SAMPLE_FLAGS_PRESENT) {
174         default_sample_flags = tfhd->GetDefaultSampleFlags();
175     } else if (trex) {
176         default_sample_flags = trex->GetDefaultSampleFlags();
177     }
178 
179     // parse all trun entries to setup the samples
180     AP4_UI64 dts = dts_origin;
181     m_Duration = 0;
182     for (unsigned int i=0; i<trun->GetEntries().ItemCount(); i++) {
183         const AP4_TrunAtom::Entry& entry  = trun->GetEntries()[i];
184         AP4_Sample&                sample = m_Samples[start+i];
185 
186         // sample size
187         if (trun_flags & AP4_TRUN_FLAG_SAMPLE_SIZE_PRESENT) {
188             sample.SetSize(entry.sample_size);
189         } else {
190             sample.SetSize(default_sample_size);
191         }
192         payload_offset += sample.GetSize(); // update the payload offset
193 
194         // sample duration
195         if (trun_flags & AP4_TRUN_FLAG_SAMPLE_DURATION_PRESENT) {
196             sample.SetDuration(entry.sample_duration);
197         } else {
198             sample.SetDuration(default_sample_duration);
199         }
200 
201         // sample flags
202         AP4_UI32 sample_flags = default_sample_flags;
203         if (i==0 && (trun_flags & AP4_TRUN_FLAG_FIRST_SAMPLE_FLAGS_PRESENT)) {
204             sample_flags = trun->GetFirstSampleFlags();
205         } else if (trun_flags & AP4_TRUN_FLAG_SAMPLE_FLAGS_PRESENT) {
206             sample_flags = entry.sample_flags;
207         }
208         if ((sample_flags & AP4_FRAG_FLAG_SAMPLE_IS_DIFFERENCE) == 0) {
209             sample.SetSync(true);
210         } else {
211             sample.SetSync(false);
212         }
213 
214         // sample description index
215         if (sample_description_index >= 1) {
216             sample.SetDescriptionIndex(sample_description_index-1);
217         }
218 
219         // data stream
220         if (sample_stream) sample.SetDataStream(*sample_stream);
221 
222         // data offset
223         sample.SetOffset(data_offset);
224         data_offset += sample.GetSize();
225 
226         // dts and cts
227         sample.SetDts(dts);
228         if (trun_flags & AP4_TRUN_FLAG_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT) {
229             sample.SetCtsDelta(entry.sample_composition_time_offset);
230         }
231 
232         // update the counters
233         dts        += sample.GetDuration();
234         m_Duration += sample.GetDuration();
235     }
236 
237     // update the dts
238     dts_origin = dts;
239 
240     return AP4_SUCCESS;
241 }
242 
243 /*----------------------------------------------------------------------
244 |   AP4_FragmentSampleTable::GetSample
245 +---------------------------------------------------------------------*/
246 AP4_Result
GetSample(AP4_Ordinal index,AP4_Sample & sample)247 AP4_FragmentSampleTable::GetSample(AP4_Ordinal index,
248                                    AP4_Sample& sample)
249 {
250     if (index >= m_Samples.ItemCount()) return AP4_ERROR_OUT_OF_RANGE;
251 
252     // copy the sample
253     sample = m_Samples[index];
254 
255     return AP4_SUCCESS;
256 }
257 
258 /*----------------------------------------------------------------------
259 |   AP4_FragmentSampleTable::GetSampleCount
260 +---------------------------------------------------------------------*/
261 AP4_Cardinal
GetSampleCount()262 AP4_FragmentSampleTable::GetSampleCount()
263 {
264     return m_Samples.ItemCount();
265 }
266 
267 /*----------------------------------------------------------------------
268 |   AP4_FragmentSampleTable::GetSampleDescription
269 +---------------------------------------------------------------------*/
270 AP4_SampleDescription*
GetSampleDescription(AP4_Ordinal)271 AP4_FragmentSampleTable::GetSampleDescription(AP4_Ordinal /*index*/)
272 {
273     return NULL; // FIXME
274 }
275 
276 /*----------------------------------------------------------------------
277 |   AP4_FragmentSampleTable::GetSampleDescriptionCount
278 +---------------------------------------------------------------------*/
279 AP4_Cardinal
GetSampleDescriptionCount()280 AP4_FragmentSampleTable::GetSampleDescriptionCount()
281 {
282     return 1; // FIXME
283 }
284 
285 /*----------------------------------------------------------------------
286 |   AP4_AtomSampleTable::GetSampleChunkPosition
287 +---------------------------------------------------------------------*/
288 AP4_Result
GetSampleChunkPosition(AP4_Ordinal sample_index,AP4_Ordinal & chunk_index,AP4_Ordinal & position_in_chunk)289 AP4_FragmentSampleTable::GetSampleChunkPosition(AP4_Ordinal  sample_index,
290                                                 AP4_Ordinal& chunk_index,
291                                                 AP4_Ordinal& position_in_chunk)
292 {
293     chunk_index       = 0;
294     position_in_chunk = sample_index;
295 
296     return AP4_SUCCESS;
297 }
298 
299 /*----------------------------------------------------------------------
300 |   AP4_FragmentSampleTable::GetSampleIndexForTimeStamp
301 +---------------------------------------------------------------------*/
302 AP4_Result
GetSampleIndexForTimeStamp(AP4_UI64 ts,AP4_Ordinal & sample_index)303 AP4_FragmentSampleTable::GetSampleIndexForTimeStamp(AP4_UI64     ts,
304                                                     AP4_Ordinal& sample_index)
305 {
306   if (!m_Samples.ItemCount())
307     return AP4_ERROR_NOT_ENOUGH_DATA;
308 
309   sample_index = 0;
310   while (sample_index < m_Samples.ItemCount() && m_Samples[sample_index].GetCts() + m_Samples[sample_index].GetDuration() < ts)
311     ++sample_index;
312 
313   if (sample_index == m_Samples.ItemCount())
314     return AP4_ERROR_NOT_ENOUGH_DATA;
315 
316   return AP4_SUCCESS;
317 }
318 
319 /*----------------------------------------------------------------------
320 |   AP4_FragmentSampleTable::GetNearestSyncSampleIndex
321 +---------------------------------------------------------------------*/
322 AP4_Ordinal
GetNearestSyncSampleIndex(AP4_Ordinal sample_index,bool before)323 AP4_FragmentSampleTable::GetNearestSyncSampleIndex(AP4_Ordinal sample_index, bool before)
324 {
325   if (sample_index >= m_Samples.ItemCount())
326     return sample_index;
327 
328   AP4_Ordinal end(before ? 0 : m_Samples.ItemCount());
329 
330   while (sample_index != end && !m_Samples[sample_index].IsSync())
331     sample_index = sample_index + (before ? -1 : 1);
332 
333   return sample_index;
334 }
335 
336