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
31namespace android {
32
33SampleIterator::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
44void 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
54status_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
154status_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            if (mStopChunk < mFirstChunk ||
175                (mStopChunk - mFirstChunk) > UINT32_MAX / mSamplesPerChunk ||
176                ((mStopChunk - mFirstChunk) * mSamplesPerChunk >
177                 UINT32_MAX - mFirstChunkSampleIndex)) {
178
179                return ERROR_OUT_OF_RANGE;
180            }
181            mStopChunkSampleIndex =
182                mFirstChunkSampleIndex
183                    + (mStopChunk - mFirstChunk) * mSamplesPerChunk;
184        } else {
185            mStopChunk = 0xffffffff;
186            mStopChunkSampleIndex = 0xffffffff;
187        }
188
189        ++mSampleToChunkIndex;
190    }
191
192    return OK;
193}
194
195status_t SampleIterator::getChunkOffset(uint32_t chunk, off64_t *offset) {
196    *offset = 0;
197
198    if (chunk >= mTable->mNumChunkOffsets) {
199        return ERROR_OUT_OF_RANGE;
200    }
201
202    if (mTable->mChunkOffsetType == SampleTable::kChunkOffsetType32) {
203        uint32_t offset32;
204
205        if (mTable->mDataSource->readAt(
206                    mTable->mChunkOffsetOffset + 8 + 4 * chunk,
207                    &offset32,
208                    sizeof(offset32)) < (ssize_t)sizeof(offset32)) {
209            return ERROR_IO;
210        }
211
212        *offset = ntohl(offset32);
213    } else {
214        CHECK_EQ(mTable->mChunkOffsetType, SampleTable::kChunkOffsetType64);
215
216        uint64_t offset64;
217        if (mTable->mDataSource->readAt(
218                    mTable->mChunkOffsetOffset + 8 + 8 * chunk,
219                    &offset64,
220                    sizeof(offset64)) < (ssize_t)sizeof(offset64)) {
221            return ERROR_IO;
222        }
223
224        *offset = ntoh64(offset64);
225    }
226
227    return OK;
228}
229
230status_t SampleIterator::getSampleSizeDirect(
231        uint32_t sampleIndex, size_t *size) {
232    *size = 0;
233
234    if (sampleIndex >= mTable->mNumSampleSizes) {
235        return ERROR_OUT_OF_RANGE;
236    }
237
238    if (mTable->mDefaultSampleSize > 0) {
239        *size = mTable->mDefaultSampleSize;
240        return OK;
241    }
242
243    switch (mTable->mSampleSizeFieldSize) {
244        case 32:
245        {
246            if (mTable->mDataSource->readAt(
247                        mTable->mSampleSizeOffset + 12 + 4 * sampleIndex,
248                        size, sizeof(*size)) < (ssize_t)sizeof(*size)) {
249                return ERROR_IO;
250            }
251
252            *size = ntohl(*size);
253            break;
254        }
255
256        case 16:
257        {
258            uint16_t x;
259            if (mTable->mDataSource->readAt(
260                        mTable->mSampleSizeOffset + 12 + 2 * sampleIndex,
261                        &x, sizeof(x)) < (ssize_t)sizeof(x)) {
262                return ERROR_IO;
263            }
264
265            *size = ntohs(x);
266            break;
267        }
268
269        case 8:
270        {
271            uint8_t x;
272            if (mTable->mDataSource->readAt(
273                        mTable->mSampleSizeOffset + 12 + sampleIndex,
274                        &x, sizeof(x)) < (ssize_t)sizeof(x)) {
275                return ERROR_IO;
276            }
277
278            *size = x;
279            break;
280        }
281
282        default:
283        {
284            CHECK_EQ(mTable->mSampleSizeFieldSize, 4);
285
286            uint8_t x;
287            if (mTable->mDataSource->readAt(
288                        mTable->mSampleSizeOffset + 12 + sampleIndex / 2,
289                        &x, sizeof(x)) < (ssize_t)sizeof(x)) {
290                return ERROR_IO;
291            }
292
293            *size = (sampleIndex & 1) ? x & 0x0f : x >> 4;
294            break;
295        }
296    }
297
298    return OK;
299}
300
301status_t SampleIterator::findSampleTimeAndDuration(
302        uint32_t sampleIndex, uint32_t *time, uint32_t *duration) {
303    if (sampleIndex >= mTable->mNumSampleSizes) {
304        return ERROR_OUT_OF_RANGE;
305    }
306
307    while (sampleIndex >= mTTSSampleIndex + mTTSCount) {
308        if (mTimeToSampleIndex == mTable->mTimeToSampleCount) {
309            return ERROR_OUT_OF_RANGE;
310        }
311
312        mTTSSampleIndex += mTTSCount;
313        mTTSSampleTime += mTTSCount * mTTSDuration;
314
315        mTTSCount = mTable->mTimeToSample[2 * mTimeToSampleIndex];
316        mTTSDuration = mTable->mTimeToSample[2 * mTimeToSampleIndex + 1];
317
318        ++mTimeToSampleIndex;
319    }
320
321    *time = mTTSSampleTime + mTTSDuration * (sampleIndex - mTTSSampleIndex);
322
323    int32_t offset = mTable->getCompositionTimeOffset(sampleIndex);
324    if ((offset < 0 && *time < (offset == INT32_MIN ?
325            INT32_MAX : uint32_t(-offset))) ||
326            (offset > 0 && *time > UINT32_MAX - offset)) {
327        ALOGE("%u + %d would overflow", *time, offset);
328        return ERROR_OUT_OF_RANGE;
329    }
330    if (offset > 0) {
331        *time += offset;
332    } else {
333        *time -= (offset == INT32_MIN ? INT32_MAX : (-offset));
334    }
335
336    *duration = mTTSDuration;
337
338    return OK;
339}
340
341}  // namespace android
342
343