SampleTable.cpp revision 20dfd1547445eca8853b7cde0a11aa11d28bf8d2
1/*
2 * Copyright (C) 2009 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 "SampleTable"
18//#define LOG_NDEBUG 0
19#include <utils/Log.h>
20
21#include "include/SampleTable.h"
22#include "include/SampleIterator.h"
23
24#include <arpa/inet.h>
25
26#include <media/stagefright/foundation/ADebug.h>
27#include <media/stagefright/DataSource.h>
28#include <media/stagefright/Utils.h>
29
30/* TODO: remove after being merged into other branches */
31#ifndef UINT32_MAX
32#define UINT32_MAX       (4294967295U)
33#endif
34
35namespace android {
36
37// static
38const uint32_t SampleTable::kChunkOffsetType32 = FOURCC('s', 't', 'c', 'o');
39// static
40const uint32_t SampleTable::kChunkOffsetType64 = FOURCC('c', 'o', '6', '4');
41// static
42const uint32_t SampleTable::kSampleSizeType32 = FOURCC('s', 't', 's', 'z');
43// static
44const uint32_t SampleTable::kSampleSizeTypeCompact = FOURCC('s', 't', 'z', '2');
45
46////////////////////////////////////////////////////////////////////////////////
47
48struct SampleTable::CompositionDeltaLookup {
49    CompositionDeltaLookup();
50
51    void setEntries(
52            const uint32_t *deltaEntries, size_t numDeltaEntries);
53
54    uint32_t getCompositionTimeOffset(uint32_t sampleIndex);
55
56private:
57    Mutex mLock;
58
59    const uint32_t *mDeltaEntries;
60    size_t mNumDeltaEntries;
61
62    size_t mCurrentDeltaEntry;
63    size_t mCurrentEntrySampleIndex;
64
65    DISALLOW_EVIL_CONSTRUCTORS(CompositionDeltaLookup);
66};
67
68SampleTable::CompositionDeltaLookup::CompositionDeltaLookup()
69    : mDeltaEntries(NULL),
70      mNumDeltaEntries(0),
71      mCurrentDeltaEntry(0),
72      mCurrentEntrySampleIndex(0) {
73}
74
75void SampleTable::CompositionDeltaLookup::setEntries(
76        const uint32_t *deltaEntries, size_t numDeltaEntries) {
77    Mutex::Autolock autolock(mLock);
78
79    mDeltaEntries = deltaEntries;
80    mNumDeltaEntries = numDeltaEntries;
81    mCurrentDeltaEntry = 0;
82    mCurrentEntrySampleIndex = 0;
83}
84
85uint32_t SampleTable::CompositionDeltaLookup::getCompositionTimeOffset(
86        uint32_t sampleIndex) {
87    Mutex::Autolock autolock(mLock);
88
89    if (mDeltaEntries == NULL) {
90        return 0;
91    }
92
93    if (sampleIndex < mCurrentEntrySampleIndex) {
94        mCurrentDeltaEntry = 0;
95        mCurrentEntrySampleIndex = 0;
96    }
97
98    while (mCurrentDeltaEntry < mNumDeltaEntries) {
99        uint32_t sampleCount = mDeltaEntries[2 * mCurrentDeltaEntry];
100        if (sampleIndex < mCurrentEntrySampleIndex + sampleCount) {
101            return mDeltaEntries[2 * mCurrentDeltaEntry + 1];
102        }
103
104        mCurrentEntrySampleIndex += sampleCount;
105        ++mCurrentDeltaEntry;
106    }
107
108    return 0;
109}
110
111////////////////////////////////////////////////////////////////////////////////
112
113SampleTable::SampleTable(const sp<DataSource> &source)
114    : mDataSource(source),
115      mChunkOffsetOffset(-1),
116      mChunkOffsetType(0),
117      mNumChunkOffsets(0),
118      mSampleToChunkOffset(-1),
119      mNumSampleToChunkOffsets(0),
120      mSampleSizeOffset(-1),
121      mSampleSizeFieldSize(0),
122      mDefaultSampleSize(0),
123      mNumSampleSizes(0),
124      mTimeToSampleCount(0),
125      mTimeToSample(NULL),
126      mSampleTimeEntries(NULL),
127      mCompositionTimeDeltaEntries(NULL),
128      mNumCompositionTimeDeltaEntries(0),
129      mCompositionDeltaLookup(new CompositionDeltaLookup),
130      mSyncSampleOffset(-1),
131      mNumSyncSamples(0),
132      mSyncSamples(NULL),
133      mLastSyncSampleIndex(0),
134      mSampleToChunkEntries(NULL) {
135    mSampleIterator = new SampleIterator(this);
136}
137
138SampleTable::~SampleTable() {
139    delete[] mSampleToChunkEntries;
140    mSampleToChunkEntries = NULL;
141
142    delete[] mSyncSamples;
143    mSyncSamples = NULL;
144
145    delete mCompositionDeltaLookup;
146    mCompositionDeltaLookup = NULL;
147
148    delete[] mCompositionTimeDeltaEntries;
149    mCompositionTimeDeltaEntries = NULL;
150
151    delete[] mSampleTimeEntries;
152    mSampleTimeEntries = NULL;
153
154    delete[] mTimeToSample;
155    mTimeToSample = NULL;
156
157    delete mSampleIterator;
158    mSampleIterator = NULL;
159}
160
161bool SampleTable::isValid() const {
162    return mChunkOffsetOffset >= 0
163        && mSampleToChunkOffset >= 0
164        && mSampleSizeOffset >= 0
165        && mTimeToSample != NULL;
166}
167
168status_t SampleTable::setChunkOffsetParams(
169        uint32_t type, off64_t data_offset, size_t data_size) {
170    if (mChunkOffsetOffset >= 0) {
171        return ERROR_MALFORMED;
172    }
173
174    CHECK(type == kChunkOffsetType32 || type == kChunkOffsetType64);
175
176    mChunkOffsetOffset = data_offset;
177    mChunkOffsetType = type;
178
179    if (data_size < 8) {
180        return ERROR_MALFORMED;
181    }
182
183    uint8_t header[8];
184    if (mDataSource->readAt(
185                data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
186        return ERROR_IO;
187    }
188
189    if (U32_AT(header) != 0) {
190        // Expected version = 0, flags = 0.
191        return ERROR_MALFORMED;
192    }
193
194    mNumChunkOffsets = U32_AT(&header[4]);
195
196    if (mChunkOffsetType == kChunkOffsetType32) {
197        if (data_size < 8 + mNumChunkOffsets * 4) {
198            return ERROR_MALFORMED;
199        }
200    } else {
201        if (data_size < 8 + mNumChunkOffsets * 8) {
202            return ERROR_MALFORMED;
203        }
204    }
205
206    return OK;
207}
208
209status_t SampleTable::setSampleToChunkParams(
210        off64_t data_offset, size_t data_size) {
211    if (mSampleToChunkOffset >= 0) {
212        return ERROR_MALFORMED;
213    }
214
215    mSampleToChunkOffset = data_offset;
216
217    if (data_size < 8) {
218        return ERROR_MALFORMED;
219    }
220
221    uint8_t header[8];
222    if (mDataSource->readAt(
223                data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
224        return ERROR_IO;
225    }
226
227    if (U32_AT(header) != 0) {
228        // Expected version = 0, flags = 0.
229        return ERROR_MALFORMED;
230    }
231
232    mNumSampleToChunkOffsets = U32_AT(&header[4]);
233
234    if (data_size < 8 + mNumSampleToChunkOffsets * 12) {
235        return ERROR_MALFORMED;
236    }
237
238    if (SIZE_MAX / sizeof(SampleToChunkEntry) <= (size_t)mNumSampleToChunkOffsets)
239        return ERROR_OUT_OF_RANGE;
240
241    mSampleToChunkEntries =
242        new (std::nothrow) SampleToChunkEntry[mNumSampleToChunkOffsets];
243    if (!mSampleToChunkEntries)
244        return ERROR_OUT_OF_RANGE;
245
246    for (uint32_t i = 0; i < mNumSampleToChunkOffsets; ++i) {
247        uint8_t buffer[12];
248        if (mDataSource->readAt(
249                    mSampleToChunkOffset + 8 + i * 12, buffer, sizeof(buffer))
250                != (ssize_t)sizeof(buffer)) {
251            return ERROR_IO;
252        }
253
254        CHECK(U32_AT(buffer) >= 1);  // chunk index is 1 based in the spec.
255
256        // We want the chunk index to be 0-based.
257        mSampleToChunkEntries[i].startChunk = U32_AT(buffer) - 1;
258        mSampleToChunkEntries[i].samplesPerChunk = U32_AT(&buffer[4]);
259        mSampleToChunkEntries[i].chunkDesc = U32_AT(&buffer[8]);
260    }
261
262    return OK;
263}
264
265status_t SampleTable::setSampleSizeParams(
266        uint32_t type, off64_t data_offset, size_t data_size) {
267    if (mSampleSizeOffset >= 0) {
268        return ERROR_MALFORMED;
269    }
270
271    CHECK(type == kSampleSizeType32 || type == kSampleSizeTypeCompact);
272
273    mSampleSizeOffset = data_offset;
274
275    if (data_size < 12) {
276        return ERROR_MALFORMED;
277    }
278
279    uint8_t header[12];
280    if (mDataSource->readAt(
281                data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
282        return ERROR_IO;
283    }
284
285    if (U32_AT(header) != 0) {
286        // Expected version = 0, flags = 0.
287        return ERROR_MALFORMED;
288    }
289
290    mDefaultSampleSize = U32_AT(&header[4]);
291    mNumSampleSizes = U32_AT(&header[8]);
292    if (mNumSampleSizes > (UINT32_MAX - 12) / 16) {
293        ALOGE("b/23247055, mNumSampleSizes(%u)", mNumSampleSizes);
294        return ERROR_MALFORMED;
295    }
296
297    if (type == kSampleSizeType32) {
298        mSampleSizeFieldSize = 32;
299
300        if (mDefaultSampleSize != 0) {
301            return OK;
302        }
303
304        if (data_size < 12 + mNumSampleSizes * 4) {
305            return ERROR_MALFORMED;
306        }
307    } else {
308        if ((mDefaultSampleSize & 0xffffff00) != 0) {
309            // The high 24 bits are reserved and must be 0.
310            return ERROR_MALFORMED;
311        }
312
313        mSampleSizeFieldSize = mDefaultSampleSize & 0xff;
314        mDefaultSampleSize = 0;
315
316        if (mSampleSizeFieldSize != 4 && mSampleSizeFieldSize != 8
317            && mSampleSizeFieldSize != 16) {
318            return ERROR_MALFORMED;
319        }
320
321        if (data_size < 12 + (mNumSampleSizes * mSampleSizeFieldSize + 4) / 8) {
322            return ERROR_MALFORMED;
323        }
324    }
325
326    return OK;
327}
328
329status_t SampleTable::setTimeToSampleParams(
330        off64_t data_offset, size_t data_size) {
331    if (mTimeToSample != NULL || data_size < 8) {
332        return ERROR_MALFORMED;
333    }
334
335    uint8_t header[8];
336    if (mDataSource->readAt(
337                data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
338        return ERROR_IO;
339    }
340
341    if (U32_AT(header) != 0) {
342        // Expected version = 0, flags = 0.
343        return ERROR_MALFORMED;
344    }
345
346    mTimeToSampleCount = U32_AT(&header[4]);
347    uint64_t allocSize = (uint64_t)mTimeToSampleCount * 2 * sizeof(uint32_t);
348    if (allocSize > UINT32_MAX) {
349        return ERROR_OUT_OF_RANGE;
350    }
351    mTimeToSample = new (std::nothrow) uint32_t[mTimeToSampleCount * 2];
352    if (!mTimeToSample)
353        return ERROR_OUT_OF_RANGE;
354
355    size_t size = sizeof(uint32_t) * mTimeToSampleCount * 2;
356    if (mDataSource->readAt(
357                data_offset + 8, mTimeToSample, size) < (ssize_t)size) {
358        return ERROR_IO;
359    }
360
361    for (uint32_t i = 0; i < mTimeToSampleCount * 2; ++i) {
362        mTimeToSample[i] = ntohl(mTimeToSample[i]);
363    }
364
365    return OK;
366}
367
368status_t SampleTable::setCompositionTimeToSampleParams(
369        off64_t data_offset, size_t data_size) {
370    ALOGI("There are reordered frames present.");
371
372    if (mCompositionTimeDeltaEntries != NULL || data_size < 8) {
373        return ERROR_MALFORMED;
374    }
375
376    uint8_t header[8];
377    if (mDataSource->readAt(
378                data_offset, header, sizeof(header))
379            < (ssize_t)sizeof(header)) {
380        return ERROR_IO;
381    }
382
383    if (U32_AT(header) != 0) {
384        // Expected version = 0, flags = 0.
385        return ERROR_MALFORMED;
386    }
387
388    size_t numEntries = U32_AT(&header[4]);
389
390    if (data_size != (numEntries + 1) * 8) {
391        return ERROR_MALFORMED;
392    }
393
394    mNumCompositionTimeDeltaEntries = numEntries;
395    uint64_t allocSize = (uint64_t)numEntries * 2 * sizeof(uint32_t);
396    if (allocSize > UINT32_MAX) {
397        return ERROR_OUT_OF_RANGE;
398    }
399
400    mCompositionTimeDeltaEntries = new (std::nothrow) uint32_t[2 * numEntries];
401    if (!mCompositionTimeDeltaEntries)
402        return ERROR_OUT_OF_RANGE;
403
404    if (mDataSource->readAt(
405                data_offset + 8, mCompositionTimeDeltaEntries, numEntries * 8)
406            < (ssize_t)numEntries * 8) {
407        delete[] mCompositionTimeDeltaEntries;
408        mCompositionTimeDeltaEntries = NULL;
409
410        return ERROR_IO;
411    }
412
413    for (size_t i = 0; i < 2 * numEntries; ++i) {
414        mCompositionTimeDeltaEntries[i] = ntohl(mCompositionTimeDeltaEntries[i]);
415    }
416
417    mCompositionDeltaLookup->setEntries(
418            mCompositionTimeDeltaEntries, mNumCompositionTimeDeltaEntries);
419
420    return OK;
421}
422
423status_t SampleTable::setSyncSampleParams(off64_t data_offset, size_t data_size) {
424    if (mSyncSampleOffset >= 0 || data_size < 8) {
425        return ERROR_MALFORMED;
426    }
427
428    mSyncSampleOffset = data_offset;
429
430    uint8_t header[8];
431    if (mDataSource->readAt(
432                data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
433        return ERROR_IO;
434    }
435
436    if (U32_AT(header) != 0) {
437        // Expected version = 0, flags = 0.
438        return ERROR_MALFORMED;
439    }
440
441    mNumSyncSamples = U32_AT(&header[4]);
442
443    if (mNumSyncSamples < 2) {
444        ALOGV("Table of sync samples is empty or has only a single entry!");
445    }
446
447    uint64_t allocSize = mNumSyncSamples * (uint64_t)sizeof(uint32_t);
448    if (allocSize > SIZE_MAX) {
449        return ERROR_OUT_OF_RANGE;
450    }
451
452    mSyncSamples = new (std::nothrow) uint32_t[mNumSyncSamples];
453    if (!mSyncSamples)
454        return ERROR_OUT_OF_RANGE;
455
456    size_t size = mNumSyncSamples * sizeof(uint32_t);
457    if (mDataSource->readAt(mSyncSampleOffset + 8, mSyncSamples, size)
458            != (ssize_t)size) {
459        return ERROR_IO;
460    }
461
462    for (size_t i = 0; i < mNumSyncSamples; ++i) {
463        mSyncSamples[i] = ntohl(mSyncSamples[i]) - 1;
464    }
465
466    return OK;
467}
468
469uint32_t SampleTable::countChunkOffsets() const {
470    return mNumChunkOffsets;
471}
472
473uint32_t SampleTable::countSamples() const {
474    return mNumSampleSizes;
475}
476
477status_t SampleTable::getMaxSampleSize(size_t *max_size) {
478    Mutex::Autolock autoLock(mLock);
479
480    *max_size = 0;
481
482    for (uint32_t i = 0; i < mNumSampleSizes; ++i) {
483        size_t sample_size;
484        status_t err = getSampleSize_l(i, &sample_size);
485
486        if (err != OK) {
487            return err;
488        }
489
490        if (sample_size > *max_size) {
491            *max_size = sample_size;
492        }
493    }
494
495    return OK;
496}
497
498uint32_t abs_difference(uint32_t time1, uint32_t time2) {
499    return time1 > time2 ? time1 - time2 : time2 - time1;
500}
501
502// static
503int SampleTable::CompareIncreasingTime(const void *_a, const void *_b) {
504    const SampleTimeEntry *a = (const SampleTimeEntry *)_a;
505    const SampleTimeEntry *b = (const SampleTimeEntry *)_b;
506
507    if (a->mCompositionTime < b->mCompositionTime) {
508        return -1;
509    } else if (a->mCompositionTime > b->mCompositionTime) {
510        return 1;
511    }
512
513    return 0;
514}
515
516void SampleTable::buildSampleEntriesTable() {
517    Mutex::Autolock autoLock(mLock);
518
519    if (mSampleTimeEntries != NULL || mNumSampleSizes == 0) {
520        ALOGE("b/23247055, mNumSampleSizes(%u), mSampleTimeEntries(%p)",
521                mNumSampleSizes, mSampleTimeEntries);
522        return;
523    }
524
525    mSampleTimeEntries = new (std::nothrow) SampleTimeEntry[mNumSampleSizes];
526    if (!mSampleTimeEntries)
527        return;
528
529    uint32_t sampleIndex = 0;
530    uint32_t sampleTime = 0;
531
532    for (uint32_t i = 0; i < mTimeToSampleCount; ++i) {
533        uint32_t n = mTimeToSample[2 * i];
534        uint32_t delta = mTimeToSample[2 * i + 1];
535
536        for (uint32_t j = 0; j < n; ++j) {
537            if (sampleIndex < mNumSampleSizes) {
538                // Technically this should always be the case if the file
539                // is well-formed, but you know... there's (gasp) malformed
540                // content out there.
541
542                mSampleTimeEntries[sampleIndex].mSampleIndex = sampleIndex;
543
544                uint32_t compTimeDelta =
545                    mCompositionDeltaLookup->getCompositionTimeOffset(
546                            sampleIndex);
547
548                mSampleTimeEntries[sampleIndex].mCompositionTime =
549                    sampleTime + compTimeDelta;
550            }
551
552            ++sampleIndex;
553            sampleTime += delta;
554        }
555    }
556
557    qsort(mSampleTimeEntries, mNumSampleSizes, sizeof(SampleTimeEntry),
558          CompareIncreasingTime);
559}
560
561status_t SampleTable::findSampleAtTime(
562        uint64_t req_time, uint64_t scale_num, uint64_t scale_den,
563        uint32_t *sample_index, uint32_t flags) {
564    buildSampleEntriesTable();
565
566    if (mSampleTimeEntries == NULL) {
567        return ERROR_OUT_OF_RANGE;
568    }
569
570    uint32_t left = 0;
571    uint32_t right_plus_one = mNumSampleSizes;
572    while (left < right_plus_one) {
573        uint32_t center = left + (right_plus_one - left) / 2;
574        uint64_t centerTime =
575            getSampleTime(center, scale_num, scale_den);
576
577        if (req_time < centerTime) {
578            right_plus_one = center;
579        } else if (req_time > centerTime) {
580            left = center + 1;
581        } else {
582            *sample_index = mSampleTimeEntries[center].mSampleIndex;
583            return OK;
584        }
585    }
586
587    uint32_t closestIndex = left;
588
589    if (closestIndex == mNumSampleSizes) {
590        if (flags == kFlagAfter) {
591            return ERROR_OUT_OF_RANGE;
592        }
593        flags = kFlagBefore;
594    } else if (closestIndex == 0) {
595        if (flags == kFlagBefore) {
596            // normally we should return out of range, but that is
597            // treated as end-of-stream.  instead return first sample
598            //
599            // return ERROR_OUT_OF_RANGE;
600        }
601        flags = kFlagAfter;
602    }
603
604    switch (flags) {
605        case kFlagBefore:
606        {
607            --closestIndex;
608            break;
609        }
610
611        case kFlagAfter:
612        {
613            // nothing to do
614            break;
615        }
616
617        default:
618        {
619            CHECK(flags == kFlagClosest);
620            // pick closest based on timestamp. use abs_difference for safety
621            if (abs_difference(
622                    getSampleTime(closestIndex, scale_num, scale_den), req_time) >
623                abs_difference(
624                    req_time, getSampleTime(closestIndex - 1, scale_num, scale_den))) {
625                --closestIndex;
626            }
627            break;
628        }
629    }
630
631    *sample_index = mSampleTimeEntries[closestIndex].mSampleIndex;
632    return OK;
633}
634
635status_t SampleTable::findSyncSampleNear(
636        uint32_t start_sample_index, uint32_t *sample_index, uint32_t flags) {
637    Mutex::Autolock autoLock(mLock);
638
639    *sample_index = 0;
640
641    if (mSyncSampleOffset < 0) {
642        // All samples are sync-samples.
643        *sample_index = start_sample_index;
644        return OK;
645    }
646
647    if (mNumSyncSamples == 0) {
648        *sample_index = 0;
649        return OK;
650    }
651
652    uint32_t left = 0;
653    uint32_t right_plus_one = mNumSyncSamples;
654    while (left < right_plus_one) {
655        uint32_t center = left + (right_plus_one - left) / 2;
656        uint32_t x = mSyncSamples[center];
657
658        if (start_sample_index < x) {
659            right_plus_one = center;
660        } else if (start_sample_index > x) {
661            left = center + 1;
662        } else {
663            *sample_index = x;
664            return OK;
665        }
666    }
667
668    if (left == mNumSyncSamples) {
669        if (flags == kFlagAfter) {
670            ALOGE("tried to find a sync frame after the last one: %d", left);
671            return ERROR_OUT_OF_RANGE;
672        }
673        flags = kFlagBefore;
674    }
675    else if (left == 0) {
676        if (flags == kFlagBefore) {
677            ALOGE("tried to find a sync frame before the first one: %d", left);
678
679            // normally we should return out of range, but that is
680            // treated as end-of-stream.  instead seek to first sync
681            //
682            // return ERROR_OUT_OF_RANGE;
683        }
684        flags = kFlagAfter;
685    }
686
687    // Now ssi[left - 1] <(=) start_sample_index <= ssi[left]
688    switch (flags) {
689        case kFlagBefore:
690        {
691            --left;
692            break;
693        }
694        case kFlagAfter:
695        {
696            // nothing to do
697            break;
698        }
699        default:
700        {
701            // this route is not used, but implement it nonetheless
702            CHECK(flags == kFlagClosest);
703
704            status_t err = mSampleIterator->seekTo(start_sample_index);
705            if (err != OK) {
706                return err;
707            }
708            uint32_t sample_time = mSampleIterator->getSampleTime();
709
710            err = mSampleIterator->seekTo(mSyncSamples[left]);
711            if (err != OK) {
712                return err;
713            }
714            uint32_t upper_time = mSampleIterator->getSampleTime();
715
716            err = mSampleIterator->seekTo(mSyncSamples[left - 1]);
717            if (err != OK) {
718                return err;
719            }
720            uint32_t lower_time = mSampleIterator->getSampleTime();
721
722            // use abs_difference for safety
723            if (abs_difference(upper_time, sample_time) >
724                abs_difference(sample_time, lower_time)) {
725                --left;
726            }
727            break;
728        }
729    }
730
731    *sample_index = mSyncSamples[left];
732    return OK;
733}
734
735status_t SampleTable::findThumbnailSample(uint32_t *sample_index) {
736    Mutex::Autolock autoLock(mLock);
737
738    if (mSyncSampleOffset < 0) {
739        // All samples are sync-samples.
740        *sample_index = 0;
741        return OK;
742    }
743
744    uint32_t bestSampleIndex = 0;
745    size_t maxSampleSize = 0;
746
747    static const size_t kMaxNumSyncSamplesToScan = 20;
748
749    // Consider the first kMaxNumSyncSamplesToScan sync samples and
750    // pick the one with the largest (compressed) size as the thumbnail.
751
752    size_t numSamplesToScan = mNumSyncSamples;
753    if (numSamplesToScan > kMaxNumSyncSamplesToScan) {
754        numSamplesToScan = kMaxNumSyncSamplesToScan;
755    }
756
757    for (size_t i = 0; i < numSamplesToScan; ++i) {
758        uint32_t x = mSyncSamples[i];
759
760        // Now x is a sample index.
761        size_t sampleSize;
762        status_t err = getSampleSize_l(x, &sampleSize);
763        if (err != OK) {
764            return err;
765        }
766
767        if (i == 0 || sampleSize > maxSampleSize) {
768            bestSampleIndex = x;
769            maxSampleSize = sampleSize;
770        }
771    }
772
773    *sample_index = bestSampleIndex;
774
775    return OK;
776}
777
778status_t SampleTable::getSampleSize_l(
779        uint32_t sampleIndex, size_t *sampleSize) {
780    return mSampleIterator->getSampleSizeDirect(
781            sampleIndex, sampleSize);
782}
783
784status_t SampleTable::getMetaDataForSample(
785        uint32_t sampleIndex,
786        off64_t *offset,
787        size_t *size,
788        uint32_t *compositionTime,
789        bool *isSyncSample,
790        uint32_t *sampleDuration) {
791    Mutex::Autolock autoLock(mLock);
792
793    status_t err;
794    if ((err = mSampleIterator->seekTo(sampleIndex)) != OK) {
795        return err;
796    }
797
798    if (offset) {
799        *offset = mSampleIterator->getSampleOffset();
800    }
801
802    if (size) {
803        *size = mSampleIterator->getSampleSize();
804    }
805
806    if (compositionTime) {
807        *compositionTime = mSampleIterator->getSampleTime();
808    }
809
810    if (isSyncSample) {
811        *isSyncSample = false;
812        if (mSyncSampleOffset < 0) {
813            // Every sample is a sync sample.
814            *isSyncSample = true;
815        } else {
816            size_t i = (mLastSyncSampleIndex < mNumSyncSamples)
817                    && (mSyncSamples[mLastSyncSampleIndex] <= sampleIndex)
818                ? mLastSyncSampleIndex : 0;
819
820            while (i < mNumSyncSamples && mSyncSamples[i] < sampleIndex) {
821                ++i;
822            }
823
824            if (i < mNumSyncSamples && mSyncSamples[i] == sampleIndex) {
825                *isSyncSample = true;
826            }
827
828            mLastSyncSampleIndex = i;
829        }
830    }
831
832    if (sampleDuration) {
833        *sampleDuration = mSampleIterator->getSampleDuration();
834    }
835
836    return OK;
837}
838
839uint32_t SampleTable::getCompositionTimeOffset(uint32_t sampleIndex) {
840    return mCompositionDeltaLookup->getCompositionTimeOffset(sampleIndex);
841}
842
843}  // namespace android
844
845