1 /*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "SampleIterator"
18 //#define LOG_NDEBUG 0
19 #include <utils/Log.h>
20
21 #include "include/SampleIterator.h"
22
23 #include <arpa/inet.h>
24
25 #include <media/stagefright/foundation/ADebug.h>
26 #include <media/stagefright/DataSource.h>
27 #include <media/stagefright/Utils.h>
28
29 #include "include/SampleTable.h"
30
31 namespace android {
32
SampleIterator(SampleTable * table)33 SampleIterator::SampleIterator(SampleTable *table)
34 : mTable(table),
35 mInitialized(false),
36 mTimeToSampleIndex(0),
37 mTTSSampleIndex(0),
38 mTTSSampleTime(0),
39 mTTSCount(0),
40 mTTSDuration(0) {
41 reset();
42 }
43
reset()44 void SampleIterator::reset() {
45 mSampleToChunkIndex = 0;
46 mFirstChunk = 0;
47 mFirstChunkSampleIndex = 0;
48 mStopChunk = 0;
49 mStopChunkSampleIndex = 0;
50 mSamplesPerChunk = 0;
51 mChunkDesc = 0;
52 }
53
seekTo(uint32_t sampleIndex)54 status_t SampleIterator::seekTo(uint32_t sampleIndex) {
55 ALOGV("seekTo(%d)", sampleIndex);
56
57 if (sampleIndex >= mTable->mNumSampleSizes) {
58 return ERROR_END_OF_STREAM;
59 }
60
61 if (mTable->mSampleToChunkOffset < 0
62 || mTable->mChunkOffsetOffset < 0
63 || mTable->mSampleSizeOffset < 0
64 || mTable->mTimeToSampleCount == 0) {
65
66 return ERROR_MALFORMED;
67 }
68
69 if (mInitialized && mCurrentSampleIndex == sampleIndex) {
70 return OK;
71 }
72
73 if (!mInitialized || sampleIndex < mFirstChunkSampleIndex) {
74 reset();
75 }
76
77 if (sampleIndex >= mStopChunkSampleIndex) {
78 status_t err;
79 if ((err = findChunkRange(sampleIndex)) != OK) {
80 ALOGE("findChunkRange failed");
81 return err;
82 }
83 }
84
85 CHECK(sampleIndex < mStopChunkSampleIndex);
86
87 if (mSamplesPerChunk == 0) {
88 ALOGE("b/22802344");
89 return ERROR_MALFORMED;
90 }
91
92 uint32_t chunk =
93 (sampleIndex - mFirstChunkSampleIndex) / mSamplesPerChunk
94 + mFirstChunk;
95
96 if (!mInitialized || chunk != mCurrentChunkIndex) {
97 mCurrentChunkIndex = chunk;
98
99 status_t err;
100 if ((err = getChunkOffset(chunk, &mCurrentChunkOffset)) != OK) {
101 ALOGE("getChunkOffset return error");
102 return err;
103 }
104
105 mCurrentChunkSampleSizes.clear();
106
107 uint32_t firstChunkSampleIndex =
108 mFirstChunkSampleIndex
109 + mSamplesPerChunk * (mCurrentChunkIndex - mFirstChunk);
110
111 for (uint32_t i = 0; i < mSamplesPerChunk; ++i) {
112 size_t sampleSize;
113 if ((err = getSampleSizeDirect(
114 firstChunkSampleIndex + i, &sampleSize)) != OK) {
115 ALOGE("getSampleSizeDirect return error");
116 return err;
117 }
118
119 mCurrentChunkSampleSizes.push(sampleSize);
120 }
121 }
122
123 uint32_t chunkRelativeSampleIndex =
124 (sampleIndex - mFirstChunkSampleIndex) % mSamplesPerChunk;
125
126 mCurrentSampleOffset = mCurrentChunkOffset;
127 for (uint32_t i = 0; i < chunkRelativeSampleIndex; ++i) {
128 mCurrentSampleOffset += mCurrentChunkSampleSizes[i];
129 }
130
131 mCurrentSampleSize = mCurrentChunkSampleSizes[chunkRelativeSampleIndex];
132 if (sampleIndex < mTTSSampleIndex) {
133 mTimeToSampleIndex = 0;
134 mTTSSampleIndex = 0;
135 mTTSSampleTime = 0;
136 mTTSCount = 0;
137 mTTSDuration = 0;
138 }
139
140 status_t err;
141 if ((err = findSampleTimeAndDuration(
142 sampleIndex, &mCurrentSampleTime, &mCurrentSampleDuration)) != OK) {
143 ALOGE("findSampleTime return error");
144 return err;
145 }
146
147 mCurrentSampleIndex = sampleIndex;
148
149 mInitialized = true;
150
151 return OK;
152 }
153
findChunkRange(uint32_t sampleIndex)154 status_t SampleIterator::findChunkRange(uint32_t sampleIndex) {
155 CHECK(sampleIndex >= mFirstChunkSampleIndex);
156
157 while (sampleIndex >= mStopChunkSampleIndex) {
158 if (mSampleToChunkIndex == mTable->mNumSampleToChunkOffsets) {
159 return ERROR_OUT_OF_RANGE;
160 }
161
162 mFirstChunkSampleIndex = mStopChunkSampleIndex;
163
164 const SampleTable::SampleToChunkEntry *entry =
165 &mTable->mSampleToChunkEntries[mSampleToChunkIndex];
166
167 mFirstChunk = entry->startChunk;
168 mSamplesPerChunk = entry->samplesPerChunk;
169 mChunkDesc = entry->chunkDesc;
170
171 if (mSampleToChunkIndex + 1 < mTable->mNumSampleToChunkOffsets) {
172 mStopChunk = entry[1].startChunk;
173
174 mStopChunkSampleIndex =
175 mFirstChunkSampleIndex
176 + (mStopChunk - mFirstChunk) * mSamplesPerChunk;
177 } else {
178 mStopChunk = 0xffffffff;
179 mStopChunkSampleIndex = 0xffffffff;
180 }
181
182 ++mSampleToChunkIndex;
183 }
184
185 return OK;
186 }
187
getChunkOffset(uint32_t chunk,off64_t * offset)188 status_t SampleIterator::getChunkOffset(uint32_t chunk, off64_t *offset) {
189 *offset = 0;
190
191 if (chunk >= mTable->mNumChunkOffsets) {
192 return ERROR_OUT_OF_RANGE;
193 }
194
195 if (mTable->mChunkOffsetType == SampleTable::kChunkOffsetType32) {
196 uint32_t offset32;
197
198 if (mTable->mDataSource->readAt(
199 mTable->mChunkOffsetOffset + 8 + 4 * chunk,
200 &offset32,
201 sizeof(offset32)) < (ssize_t)sizeof(offset32)) {
202 return ERROR_IO;
203 }
204
205 *offset = ntohl(offset32);
206 } else {
207 CHECK_EQ(mTable->mChunkOffsetType, SampleTable::kChunkOffsetType64);
208
209 uint64_t offset64;
210 if (mTable->mDataSource->readAt(
211 mTable->mChunkOffsetOffset + 8 + 8 * chunk,
212 &offset64,
213 sizeof(offset64)) < (ssize_t)sizeof(offset64)) {
214 return ERROR_IO;
215 }
216
217 *offset = ntoh64(offset64);
218 }
219
220 return OK;
221 }
222
getSampleSizeDirect(uint32_t sampleIndex,size_t * size)223 status_t SampleIterator::getSampleSizeDirect(
224 uint32_t sampleIndex, size_t *size) {
225 *size = 0;
226
227 if (sampleIndex >= mTable->mNumSampleSizes) {
228 return ERROR_OUT_OF_RANGE;
229 }
230
231 if (mTable->mDefaultSampleSize > 0) {
232 *size = mTable->mDefaultSampleSize;
233 return OK;
234 }
235
236 switch (mTable->mSampleSizeFieldSize) {
237 case 32:
238 {
239 if (mTable->mDataSource->readAt(
240 mTable->mSampleSizeOffset + 12 + 4 * sampleIndex,
241 size, sizeof(*size)) < (ssize_t)sizeof(*size)) {
242 return ERROR_IO;
243 }
244
245 *size = ntohl(*size);
246 break;
247 }
248
249 case 16:
250 {
251 uint16_t x;
252 if (mTable->mDataSource->readAt(
253 mTable->mSampleSizeOffset + 12 + 2 * sampleIndex,
254 &x, sizeof(x)) < (ssize_t)sizeof(x)) {
255 return ERROR_IO;
256 }
257
258 *size = ntohs(x);
259 break;
260 }
261
262 case 8:
263 {
264 uint8_t x;
265 if (mTable->mDataSource->readAt(
266 mTable->mSampleSizeOffset + 12 + sampleIndex,
267 &x, sizeof(x)) < (ssize_t)sizeof(x)) {
268 return ERROR_IO;
269 }
270
271 *size = x;
272 break;
273 }
274
275 default:
276 {
277 CHECK_EQ(mTable->mSampleSizeFieldSize, 4);
278
279 uint8_t x;
280 if (mTable->mDataSource->readAt(
281 mTable->mSampleSizeOffset + 12 + sampleIndex / 2,
282 &x, sizeof(x)) < (ssize_t)sizeof(x)) {
283 return ERROR_IO;
284 }
285
286 *size = (sampleIndex & 1) ? x & 0x0f : x >> 4;
287 break;
288 }
289 }
290
291 return OK;
292 }
293
findSampleTimeAndDuration(uint32_t sampleIndex,uint32_t * time,uint32_t * duration)294 status_t SampleIterator::findSampleTimeAndDuration(
295 uint32_t sampleIndex, uint32_t *time, uint32_t *duration) {
296 if (sampleIndex >= mTable->mNumSampleSizes) {
297 return ERROR_OUT_OF_RANGE;
298 }
299
300 while (sampleIndex >= mTTSSampleIndex + mTTSCount) {
301 if (mTimeToSampleIndex == mTable->mTimeToSampleCount) {
302 return ERROR_OUT_OF_RANGE;
303 }
304
305 mTTSSampleIndex += mTTSCount;
306 mTTSSampleTime += mTTSCount * mTTSDuration;
307
308 mTTSCount = mTable->mTimeToSample[2 * mTimeToSampleIndex];
309 mTTSDuration = mTable->mTimeToSample[2 * mTimeToSampleIndex + 1];
310
311 ++mTimeToSampleIndex;
312 }
313
314 *time = mTTSSampleTime + mTTSDuration * (sampleIndex - mTTSSampleIndex);
315
316 *time += mTable->getCompositionTimeOffset(sampleIndex);
317
318 *duration = mTTSDuration;
319
320 return OK;
321 }
322
323 } // namespace android
324
325