Andreas Huber | c57b679 | 2010-01-19 10:39:21 -0800 | [diff] [blame] | 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 | |
James Dong | f1d5aa1 | 2012-02-06 23:46:37 -0800 | [diff] [blame] | 25 | #include <media/stagefright/foundation/ADebug.h> |
Andreas Huber | c57b679 | 2010-01-19 10:39:21 -0800 | [diff] [blame] | 26 | #include <media/stagefright/DataSource.h> |
Andreas Huber | c57b679 | 2010-01-19 10:39:21 -0800 | [diff] [blame] | 27 | #include <media/stagefright/Utils.h> |
| 28 | |
| 29 | #include "include/SampleTable.h" |
| 30 | |
| 31 | namespace android { |
| 32 | |
| 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 | |
| 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 | |
| 54 | status_t SampleIterator::seekTo(uint32_t sampleIndex) { |
Steve Block | 3856b09 | 2011-10-20 11:56:00 +0100 | [diff] [blame] | 55 | ALOGV("seekTo(%d)", sampleIndex); |
Andreas Huber | c57b679 | 2010-01-19 10:39:21 -0800 | [diff] [blame] | 56 | |
Andreas Huber | 213addf | 2010-01-25 10:41:35 -0800 | [diff] [blame] | 57 | if (sampleIndex >= mTable->mNumSampleSizes) { |
| 58 | return ERROR_END_OF_STREAM; |
| 59 | } |
| 60 | |
Andreas Huber | c57b679 | 2010-01-19 10:39:21 -0800 | [diff] [blame] | 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) { |
Steve Block | 29357bc | 2012-01-06 19:20:56 +0000 | [diff] [blame] | 80 | ALOGE("findChunkRange failed"); |
Andreas Huber | c57b679 | 2010-01-19 10:39:21 -0800 | [diff] [blame] | 81 | return err; |
| 82 | } |
| 83 | } |
| 84 | |
| 85 | CHECK(sampleIndex < mStopChunkSampleIndex); |
| 86 | |
| 87 | uint32_t chunk = |
| 88 | (sampleIndex - mFirstChunkSampleIndex) / mSamplesPerChunk |
| 89 | + mFirstChunk; |
| 90 | |
| 91 | if (!mInitialized || chunk != mCurrentChunkIndex) { |
| 92 | mCurrentChunkIndex = chunk; |
| 93 | |
| 94 | status_t err; |
| 95 | if ((err = getChunkOffset(chunk, &mCurrentChunkOffset)) != OK) { |
Steve Block | 29357bc | 2012-01-06 19:20:56 +0000 | [diff] [blame] | 96 | ALOGE("getChunkOffset return error"); |
Andreas Huber | c57b679 | 2010-01-19 10:39:21 -0800 | [diff] [blame] | 97 | return err; |
| 98 | } |
| 99 | |
| 100 | mCurrentChunkSampleSizes.clear(); |
| 101 | |
| 102 | uint32_t firstChunkSampleIndex = |
| 103 | mFirstChunkSampleIndex |
| 104 | + mSamplesPerChunk * (mCurrentChunkIndex - mFirstChunk); |
| 105 | |
| 106 | for (uint32_t i = 0; i < mSamplesPerChunk; ++i) { |
| 107 | size_t sampleSize; |
| 108 | if ((err = getSampleSizeDirect( |
| 109 | firstChunkSampleIndex + i, &sampleSize)) != OK) { |
Steve Block | 29357bc | 2012-01-06 19:20:56 +0000 | [diff] [blame] | 110 | ALOGE("getSampleSizeDirect return error"); |
Andreas Huber | c57b679 | 2010-01-19 10:39:21 -0800 | [diff] [blame] | 111 | return err; |
| 112 | } |
| 113 | |
| 114 | mCurrentChunkSampleSizes.push(sampleSize); |
| 115 | } |
| 116 | } |
| 117 | |
| 118 | uint32_t chunkRelativeSampleIndex = |
| 119 | (sampleIndex - mFirstChunkSampleIndex) % mSamplesPerChunk; |
| 120 | |
| 121 | mCurrentSampleOffset = mCurrentChunkOffset; |
| 122 | for (uint32_t i = 0; i < chunkRelativeSampleIndex; ++i) { |
| 123 | mCurrentSampleOffset += mCurrentChunkSampleSizes[i]; |
| 124 | } |
| 125 | |
| 126 | mCurrentSampleSize = mCurrentChunkSampleSizes[chunkRelativeSampleIndex]; |
| 127 | if (sampleIndex < mTTSSampleIndex) { |
| 128 | mTimeToSampleIndex = 0; |
| 129 | mTTSSampleIndex = 0; |
| 130 | mTTSSampleTime = 0; |
| 131 | mTTSCount = 0; |
| 132 | mTTSDuration = 0; |
| 133 | } |
| 134 | |
| 135 | status_t err; |
| 136 | if ((err = findSampleTime(sampleIndex, &mCurrentSampleTime)) != OK) { |
Steve Block | 29357bc | 2012-01-06 19:20:56 +0000 | [diff] [blame] | 137 | ALOGE("findSampleTime return error"); |
Andreas Huber | c57b679 | 2010-01-19 10:39:21 -0800 | [diff] [blame] | 138 | return err; |
| 139 | } |
| 140 | |
| 141 | mCurrentSampleIndex = sampleIndex; |
| 142 | |
| 143 | mInitialized = true; |
| 144 | |
| 145 | return OK; |
| 146 | } |
| 147 | |
| 148 | status_t SampleIterator::findChunkRange(uint32_t sampleIndex) { |
| 149 | CHECK(sampleIndex >= mFirstChunkSampleIndex); |
| 150 | |
| 151 | while (sampleIndex >= mStopChunkSampleIndex) { |
| 152 | if (mSampleToChunkIndex == mTable->mNumSampleToChunkOffsets) { |
| 153 | return ERROR_OUT_OF_RANGE; |
| 154 | } |
| 155 | |
| 156 | mFirstChunkSampleIndex = mStopChunkSampleIndex; |
| 157 | |
| 158 | const SampleTable::SampleToChunkEntry *entry = |
| 159 | &mTable->mSampleToChunkEntries[mSampleToChunkIndex]; |
| 160 | |
| 161 | mFirstChunk = entry->startChunk; |
| 162 | mSamplesPerChunk = entry->samplesPerChunk; |
| 163 | mChunkDesc = entry->chunkDesc; |
| 164 | |
| 165 | if (mSampleToChunkIndex + 1 < mTable->mNumSampleToChunkOffsets) { |
| 166 | mStopChunk = entry[1].startChunk; |
| 167 | |
| 168 | mStopChunkSampleIndex = |
| 169 | mFirstChunkSampleIndex |
| 170 | + (mStopChunk - mFirstChunk) * mSamplesPerChunk; |
| 171 | } else { |
| 172 | mStopChunk = 0xffffffff; |
| 173 | mStopChunkSampleIndex = 0xffffffff; |
| 174 | } |
| 175 | |
| 176 | ++mSampleToChunkIndex; |
| 177 | } |
| 178 | |
| 179 | return OK; |
| 180 | } |
| 181 | |
James Dong | c7fc37a | 2010-11-16 14:04:54 -0800 | [diff] [blame] | 182 | status_t SampleIterator::getChunkOffset(uint32_t chunk, off64_t *offset) { |
Andreas Huber | c57b679 | 2010-01-19 10:39:21 -0800 | [diff] [blame] | 183 | *offset = 0; |
| 184 | |
| 185 | if (chunk >= mTable->mNumChunkOffsets) { |
| 186 | return ERROR_OUT_OF_RANGE; |
| 187 | } |
| 188 | |
| 189 | if (mTable->mChunkOffsetType == SampleTable::kChunkOffsetType32) { |
| 190 | uint32_t offset32; |
| 191 | |
| 192 | if (mTable->mDataSource->readAt( |
| 193 | mTable->mChunkOffsetOffset + 8 + 4 * chunk, |
| 194 | &offset32, |
| 195 | sizeof(offset32)) < (ssize_t)sizeof(offset32)) { |
| 196 | return ERROR_IO; |
| 197 | } |
| 198 | |
| 199 | *offset = ntohl(offset32); |
| 200 | } else { |
| 201 | CHECK_EQ(mTable->mChunkOffsetType, SampleTable::kChunkOffsetType64); |
| 202 | |
| 203 | uint64_t offset64; |
| 204 | if (mTable->mDataSource->readAt( |
| 205 | mTable->mChunkOffsetOffset + 8 + 8 * chunk, |
| 206 | &offset64, |
| 207 | sizeof(offset64)) < (ssize_t)sizeof(offset64)) { |
| 208 | return ERROR_IO; |
| 209 | } |
| 210 | |
| 211 | *offset = ntoh64(offset64); |
| 212 | } |
| 213 | |
| 214 | return OK; |
| 215 | } |
| 216 | |
| 217 | status_t SampleIterator::getSampleSizeDirect( |
| 218 | uint32_t sampleIndex, size_t *size) { |
| 219 | *size = 0; |
| 220 | |
| 221 | if (sampleIndex >= mTable->mNumSampleSizes) { |
| 222 | return ERROR_OUT_OF_RANGE; |
| 223 | } |
| 224 | |
| 225 | if (mTable->mDefaultSampleSize > 0) { |
| 226 | *size = mTable->mDefaultSampleSize; |
| 227 | return OK; |
| 228 | } |
| 229 | |
| 230 | switch (mTable->mSampleSizeFieldSize) { |
| 231 | case 32: |
| 232 | { |
| 233 | if (mTable->mDataSource->readAt( |
| 234 | mTable->mSampleSizeOffset + 12 + 4 * sampleIndex, |
| 235 | size, sizeof(*size)) < (ssize_t)sizeof(*size)) { |
| 236 | return ERROR_IO; |
| 237 | } |
| 238 | |
| 239 | *size = ntohl(*size); |
| 240 | break; |
| 241 | } |
| 242 | |
| 243 | case 16: |
| 244 | { |
| 245 | uint16_t x; |
| 246 | if (mTable->mDataSource->readAt( |
| 247 | mTable->mSampleSizeOffset + 12 + 2 * sampleIndex, |
| 248 | &x, sizeof(x)) < (ssize_t)sizeof(x)) { |
| 249 | return ERROR_IO; |
| 250 | } |
| 251 | |
| 252 | *size = ntohs(x); |
| 253 | break; |
| 254 | } |
| 255 | |
| 256 | case 8: |
| 257 | { |
| 258 | uint8_t x; |
| 259 | if (mTable->mDataSource->readAt( |
| 260 | mTable->mSampleSizeOffset + 12 + sampleIndex, |
| 261 | &x, sizeof(x)) < (ssize_t)sizeof(x)) { |
| 262 | return ERROR_IO; |
| 263 | } |
| 264 | |
| 265 | *size = x; |
| 266 | break; |
| 267 | } |
| 268 | |
| 269 | default: |
| 270 | { |
| 271 | CHECK_EQ(mTable->mSampleSizeFieldSize, 4); |
| 272 | |
| 273 | uint8_t x; |
| 274 | if (mTable->mDataSource->readAt( |
| 275 | mTable->mSampleSizeOffset + 12 + sampleIndex / 2, |
| 276 | &x, sizeof(x)) < (ssize_t)sizeof(x)) { |
| 277 | return ERROR_IO; |
| 278 | } |
| 279 | |
| 280 | *size = (sampleIndex & 1) ? x & 0x0f : x >> 4; |
| 281 | break; |
| 282 | } |
| 283 | } |
| 284 | |
| 285 | return OK; |
| 286 | } |
| 287 | |
| 288 | status_t SampleIterator::findSampleTime( |
| 289 | uint32_t sampleIndex, uint32_t *time) { |
| 290 | if (sampleIndex >= mTable->mNumSampleSizes) { |
| 291 | return ERROR_OUT_OF_RANGE; |
| 292 | } |
| 293 | |
| 294 | while (sampleIndex >= mTTSSampleIndex + mTTSCount) { |
| 295 | if (mTimeToSampleIndex == mTable->mTimeToSampleCount) { |
| 296 | return ERROR_OUT_OF_RANGE; |
| 297 | } |
| 298 | |
| 299 | mTTSSampleIndex += mTTSCount; |
| 300 | mTTSSampleTime += mTTSCount * mTTSDuration; |
| 301 | |
| 302 | mTTSCount = mTable->mTimeToSample[2 * mTimeToSampleIndex]; |
| 303 | mTTSDuration = mTable->mTimeToSample[2 * mTimeToSampleIndex + 1]; |
| 304 | |
| 305 | ++mTimeToSampleIndex; |
| 306 | } |
| 307 | |
| 308 | *time = mTTSSampleTime + mTTSDuration * (sampleIndex - mTTSSampleIndex); |
| 309 | |
Andreas Huber | 4931bb5 | 2011-02-03 13:18:16 -0800 | [diff] [blame] | 310 | *time += mTable->getCompositionTimeOffset(sampleIndex); |
| 311 | |
Andreas Huber | c57b679 | 2010-01-19 10:39:21 -0800 | [diff] [blame] | 312 | return OK; |
| 313 | } |
| 314 | |
| 315 | } // namespace android |
| 316 | |