MPEG4Writer.cpp revision c5f0c714dc4225cd2ec305d5ddd297964a3dd3dc
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_NDEBUG 0
18#define LOG_TAG "MPEG4Writer"
19#include <utils/Log.h>
20
21#include <arpa/inet.h>
22
23#include <ctype.h>
24#include <pthread.h>
25
26#include <media/stagefright/MPEG4Writer.h>
27#include <media/stagefright/MediaBuffer.h>
28#include <media/stagefright/MetaData.h>
29#include <media/stagefright/MediaDebug.h>
30#include <media/stagefright/MediaDefs.h>
31#include <media/stagefright/MediaErrors.h>
32#include <media/stagefright/MediaSource.h>
33#include <media/stagefright/Utils.h>
34#include <media/mediarecorder.h>
35#include <cutils/properties.h>
36
37#include "include/ESDS.h"
38
39namespace android {
40
41class MPEG4Writer::Track {
42public:
43    Track(MPEG4Writer *owner, const sp<MediaSource> &source);
44
45    ~Track();
46
47    status_t start(MetaData *params);
48    void stop();
49    void pause();
50    bool reachedEOS();
51
52    int64_t getDurationUs() const;
53    int64_t getEstimatedTrackSizeBytes() const;
54    void writeTrackHeader(int32_t trackID, bool use32BitOffset = true);
55    void bufferChunk(int64_t timestampUs);
56    bool isAvc() const { return mIsAvc; }
57    bool isAudio() const { return mIsAudio; }
58    bool isMPEG4() const { return mIsMPEG4; }
59    void addChunkOffset(off_t offset) { mChunkOffsets.push_back(offset); }
60
61private:
62    MPEG4Writer *mOwner;
63    sp<MetaData> mMeta;
64    sp<MediaSource> mSource;
65    volatile bool mDone;
66    volatile bool mPaused;
67    volatile bool mResumed;
68    bool mIsAvc;
69    bool mIsAudio;
70    bool mIsMPEG4;
71    int64_t mTrackDurationUs;
72    int64_t mEstimatedTrackSizeBytes;
73    int64_t mMaxWriteTimeUs;
74    int32_t mTimeScale;
75
76    pthread_t mThread;
77
78    // mNumSamples is used to track how many samples in mSampleSizes List.
79    // This is to reduce the cost associated with mSampleSizes.size() call,
80    // since it is O(n). Ideally, the fix should be in List class.
81    size_t              mNumSamples;
82    List<size_t>        mSampleSizes;
83    bool                mSamplesHaveSameSize;
84
85    List<MediaBuffer *> mChunkSamples;
86    List<off_t>         mChunkOffsets;
87
88    struct StscTableEntry {
89
90        StscTableEntry(uint32_t chunk, uint32_t samples, uint32_t id)
91            : firstChunk(chunk),
92              samplesPerChunk(samples),
93              sampleDescriptionId(id) {}
94
95        uint32_t firstChunk;
96        uint32_t samplesPerChunk;
97        uint32_t sampleDescriptionId;
98    };
99    List<StscTableEntry> mStscTableEntries;
100
101    List<int32_t> mStssTableEntries;
102    List<int64_t> mChunkDurations;
103
104    struct SttsTableEntry {
105
106        SttsTableEntry(uint32_t count, uint32_t durationUs)
107            : sampleCount(count), sampleDurationUs(durationUs) {}
108
109        uint32_t sampleCount;
110        uint32_t sampleDurationUs;
111    };
112    List<SttsTableEntry> mSttsTableEntries;
113
114    void *mCodecSpecificData;
115    size_t mCodecSpecificDataSize;
116    bool mGotAllCodecSpecificData;
117    bool mTrackingProgressStatus;
118
119    bool mReachedEOS;
120    int64_t mStartTimestampUs;
121    int64_t mPreviousTrackTimeUs;
122    int64_t mTrackEveryTimeDurationUs;
123
124    static void *ThreadWrapper(void *me);
125    void threadEntry();
126
127    status_t makeAVCCodecSpecificData(
128            const uint8_t *data, size_t size);
129
130    // Track authoring progress status
131    void trackProgressStatus(int64_t timeUs, status_t err = OK);
132    void initTrackingProgressStatus(MetaData *params);
133
134    // Utilities for collecting statistical data
135    void logStatisticalData(bool isAudio);
136    void findMinAvgMaxSampleDurationMs(
137            int32_t *min, int32_t *avg, int32_t *max);
138    void findMinMaxChunkDurations(int64_t *min, int64_t *max);
139
140    void getCodecSpecificDataFromInputFormatIfPossible();
141
142    Track(const Track &);
143    Track &operator=(const Track &);
144};
145
146#define USE_NALLEN_FOUR         1
147
148MPEG4Writer::MPEG4Writer(const char *filename)
149    : mFile(fopen(filename, "wb")),
150      mUse32BitOffset(true),
151      mPaused(false),
152      mStarted(false),
153      mOffset(0),
154      mMdatOffset(0),
155      mEstimatedMoovBoxSize(0),
156      mInterleaveDurationUs(1000000) {
157    CHECK(mFile != NULL);
158}
159
160MPEG4Writer::MPEG4Writer(int fd)
161    : mFile(fdopen(fd, "wb")),
162      mUse32BitOffset(true),
163      mPaused(false),
164      mStarted(false),
165      mOffset(0),
166      mMdatOffset(0),
167      mEstimatedMoovBoxSize(0),
168      mInterleaveDurationUs(1000000) {
169    CHECK(mFile != NULL);
170}
171
172MPEG4Writer::~MPEG4Writer() {
173    stop();
174
175    for (List<Track *>::iterator it = mTracks.begin();
176         it != mTracks.end(); ++it) {
177        delete *it;
178    }
179    mTracks.clear();
180}
181
182status_t MPEG4Writer::addSource(const sp<MediaSource> &source) {
183    Track *track = new Track(this, source);
184    mTracks.push_back(track);
185
186    return OK;
187}
188
189status_t MPEG4Writer::startTracks(MetaData *params) {
190    for (List<Track *>::iterator it = mTracks.begin();
191         it != mTracks.end(); ++it) {
192        status_t err = (*it)->start(params);
193
194        if (err != OK) {
195            for (List<Track *>::iterator it2 = mTracks.begin();
196                 it2 != it; ++it2) {
197                (*it2)->stop();
198            }
199
200            return err;
201        }
202    }
203    return OK;
204}
205
206int64_t MPEG4Writer::estimateMoovBoxSize(int32_t bitRate) {
207    // This implementation is highly experimental/heurisitic.
208    //
209    // Statistical analysis shows that metadata usually accounts
210    // for a small portion of the total file size, usually < 0.6%.
211    // Currently, lets set to 0.4% for now.
212
213    // The default MIN_MOOV_BOX_SIZE is set to 0.4% x 1MB,
214    // where 1MB is the common file size limit for MMS application.
215    // The default MAX _MOOV_BOX_SIZE value is based on about 4
216    // minute video recording with a bit rate about 3 Mbps, because
217    // statistics also show that most of the video captured are going
218    // to be less than 3 minutes.
219
220    // If the estimation is wrong, we will pay the price of wasting
221    // some reserved space. This should not happen so often statistically.
222    static const int32_t factor = mUse32BitOffset? 1: 2;
223    static const int64_t MIN_MOOV_BOX_SIZE = 4 * 1024;  // 4 KB
224    static const int64_t MAX_MOOV_BOX_SIZE = (180 * 3000000 * 6LL / 8000);
225    int64_t size = MIN_MOOV_BOX_SIZE;
226
227    if (mMaxFileSizeLimitBytes != 0) {
228        size = mMaxFileSizeLimitBytes * 4 / 1000;
229    } else if (mMaxFileDurationLimitUs != 0) {
230        if (bitRate <= 0) {
231            // We could not estimate the file size since bitRate is not set.
232            size = MIN_MOOV_BOX_SIZE;
233        } else {
234            size = ((mMaxFileDurationLimitUs * bitRate * 4) / 1000 / 8000000);
235        }
236    }
237    if (size < MIN_MOOV_BOX_SIZE) {
238        size = MIN_MOOV_BOX_SIZE;
239    }
240
241    // Any long duration recording will be probably end up with
242    // non-streamable mp4 file.
243    if (size > MAX_MOOV_BOX_SIZE) {
244        size = MAX_MOOV_BOX_SIZE;
245    }
246
247    LOGI("limits: %lld/%lld bytes/us, bit rate: %d bps and the estimated"
248         " moov size %lld bytes",
249         mMaxFileSizeLimitBytes, mMaxFileDurationLimitUs, bitRate, size);
250    return factor * size;
251}
252
253status_t MPEG4Writer::start(MetaData *param) {
254    if (mFile == NULL) {
255        return UNKNOWN_ERROR;
256    }
257
258    int32_t use64BitOffset;
259    if (param &&
260        param->findInt32(kKey64BitFileOffset, &use64BitOffset) &&
261        use64BitOffset) {
262        mUse32BitOffset = false;
263    }
264
265    // System property can overwrite the file offset bits parameter
266    char value[PROPERTY_VALUE_MAX];
267    if (property_get("media.stagefright.record-64bits", value, NULL)
268        && (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
269        mUse32BitOffset = false;
270    }
271
272    mStartTimestampUs = -1;
273
274    if (mStarted) {
275        if (mPaused) {
276            mPaused = false;
277            return startTracks(param);
278        }
279        return OK;
280    }
281
282    if (!param ||
283        !param->findInt32(kKeyTimeScale, &mTimeScale)) {
284        mTimeScale = 1000;
285    }
286    CHECK(mTimeScale > 0);
287    LOGV("movie time scale: %d", mTimeScale);
288
289    mStreamableFile = true;
290    mWriteMoovBoxToMemory = false;
291    mMoovBoxBuffer = NULL;
292    mMoovBoxBufferOffset = 0;
293
294    beginBox("ftyp");
295      {
296        int32_t fileType;
297        if (param && param->findInt32(kKeyFileType, &fileType) &&
298            fileType != OUTPUT_FORMAT_MPEG_4) {
299            writeFourcc("3gp4");
300        } else {
301            writeFourcc("isom");
302        }
303      }
304      writeInt32(0);
305      writeFourcc("isom");
306      writeFourcc("3gp4");
307    endBox();
308
309    mFreeBoxOffset = mOffset;
310
311    if (mEstimatedMoovBoxSize == 0) {
312        int32_t bitRate = -1;
313        if (param) {
314            param->findInt32(kKeyBitRate, &bitRate);
315        }
316        mEstimatedMoovBoxSize = estimateMoovBoxSize(bitRate);
317    }
318    CHECK(mEstimatedMoovBoxSize >= 8);
319    fseeko(mFile, mFreeBoxOffset, SEEK_SET);
320    writeInt32(mEstimatedMoovBoxSize);
321    write("free", 4);
322
323    mMdatOffset = mFreeBoxOffset + mEstimatedMoovBoxSize;
324    mOffset = mMdatOffset;
325    fseeko(mFile, mMdatOffset, SEEK_SET);
326    if (mUse32BitOffset) {
327        write("????mdat", 8);
328    } else {
329        write("\x00\x00\x00\x01mdat????????", 16);
330    }
331
332    status_t err = startWriterThread();
333    if (err != OK) {
334        return err;
335    }
336
337    err = startTracks(param);
338    if (err != OK) {
339        return err;
340    }
341
342    mStarted = true;
343    return OK;
344}
345
346void MPEG4Writer::pause() {
347    if (mFile == NULL) {
348        return;
349    }
350    mPaused = true;
351    for (List<Track *>::iterator it = mTracks.begin();
352         it != mTracks.end(); ++it) {
353        (*it)->pause();
354    }
355}
356
357void MPEG4Writer::stopWriterThread() {
358    LOGV("stopWriterThread");
359
360    {
361        Mutex::Autolock autolock(mLock);
362
363        mDone = true;
364        mChunkReadyCondition.signal();
365    }
366
367    void *dummy;
368    pthread_join(mThread, &dummy);
369}
370
371void MPEG4Writer::stop() {
372    if (mFile == NULL) {
373        return;
374    }
375
376    int64_t maxDurationUs = 0;
377    for (List<Track *>::iterator it = mTracks.begin();
378         it != mTracks.end(); ++it) {
379        (*it)->stop();
380
381        int64_t durationUs = (*it)->getDurationUs();
382        if (durationUs > maxDurationUs) {
383            maxDurationUs = durationUs;
384        }
385    }
386
387    stopWriterThread();
388
389    // Fix up the size of the 'mdat' chunk.
390    if (mUse32BitOffset) {
391        fseeko(mFile, mMdatOffset, SEEK_SET);
392        int32_t size = htonl(static_cast<int32_t>(mOffset - mMdatOffset));
393        fwrite(&size, 1, 4, mFile);
394    } else {
395        fseeko(mFile, mMdatOffset + 8, SEEK_SET);
396        int64_t size = mOffset - mMdatOffset;
397        size = hton64(size);
398        fwrite(&size, 1, 8, mFile);
399    }
400    fseeko(mFile, mOffset, SEEK_SET);
401
402    time_t now = time(NULL);
403    const off_t moovOffset = mOffset;
404    mWriteMoovBoxToMemory = true;
405    mMoovBoxBuffer = (uint8_t *) malloc(mEstimatedMoovBoxSize);
406    mMoovBoxBufferOffset = 0;
407    CHECK(mMoovBoxBuffer != NULL);
408    int32_t duration = (maxDurationUs * mTimeScale) / 1E6;
409
410    beginBox("moov");
411
412      beginBox("mvhd");
413        writeInt32(0);             // version=0, flags=0
414        writeInt32(now);           // creation time
415        writeInt32(now);           // modification time
416        writeInt32(mTimeScale);    // mvhd timescale
417        writeInt32(duration);
418        writeInt32(0x10000);       // rate: 1.0
419        writeInt16(0x100);         // volume
420        writeInt16(0);             // reserved
421        writeInt32(0);             // reserved
422        writeInt32(0);             // reserved
423        writeInt32(0x10000);       // matrix
424        writeInt32(0);
425        writeInt32(0);
426        writeInt32(0);
427        writeInt32(0x10000);
428        writeInt32(0);
429        writeInt32(0);
430        writeInt32(0);
431        writeInt32(0x40000000);
432        writeInt32(0);             // predefined
433        writeInt32(0);             // predefined
434        writeInt32(0);             // predefined
435        writeInt32(0);             // predefined
436        writeInt32(0);             // predefined
437        writeInt32(0);             // predefined
438        writeInt32(mTracks.size() + 1);  // nextTrackID
439      endBox();  // mvhd
440
441      int32_t id = 1;
442      for (List<Track *>::iterator it = mTracks.begin();
443           it != mTracks.end(); ++it, ++id) {
444          (*it)->writeTrackHeader(id, mUse32BitOffset);
445      }
446    endBox();  // moov
447
448    mWriteMoovBoxToMemory = false;
449    if (mStreamableFile) {
450        CHECK(mMoovBoxBufferOffset + 8 <= mEstimatedMoovBoxSize);
451
452        // Moov box
453        fseeko(mFile, mFreeBoxOffset, SEEK_SET);
454        mOffset = mFreeBoxOffset;
455        write(mMoovBoxBuffer, 1, mMoovBoxBufferOffset, mFile);
456
457        // Free box
458        fseeko(mFile, mOffset, SEEK_SET);
459        writeInt32(mEstimatedMoovBoxSize - mMoovBoxBufferOffset);
460        write("free", 4);
461
462        // Free temp memory
463        free(mMoovBoxBuffer);
464        mMoovBoxBuffer = NULL;
465        mMoovBoxBufferOffset = 0;
466    } else {
467        LOGI("The mp4 file will not be streamable.");
468    }
469
470    CHECK(mBoxes.empty());
471
472    fflush(mFile);
473    fclose(mFile);
474    mFile = NULL;
475    mStarted = false;
476}
477
478status_t MPEG4Writer::setInterleaveDuration(uint32_t durationUs) {
479    mInterleaveDurationUs = durationUs;
480    return OK;
481}
482
483void MPEG4Writer::lock() {
484    mLock.lock();
485}
486
487void MPEG4Writer::unlock() {
488    mLock.unlock();
489}
490
491off_t MPEG4Writer::addSample_l(MediaBuffer *buffer) {
492    off_t old_offset = mOffset;
493
494    fwrite((const uint8_t *)buffer->data() + buffer->range_offset(),
495           1, buffer->range_length(), mFile);
496
497    mOffset += buffer->range_length();
498
499    return old_offset;
500}
501
502static void StripStartcode(MediaBuffer *buffer) {
503    if (buffer->range_length() < 4) {
504        return;
505    }
506
507    const uint8_t *ptr =
508        (const uint8_t *)buffer->data() + buffer->range_offset();
509
510    if (!memcmp(ptr, "\x00\x00\x00\x01", 4)) {
511        buffer->set_range(
512                buffer->range_offset() + 4, buffer->range_length() - 4);
513    }
514}
515
516off_t MPEG4Writer::addLengthPrefixedSample_l(MediaBuffer *buffer) {
517    off_t old_offset = mOffset;
518
519    size_t length = buffer->range_length();
520
521#if USE_NALLEN_FOUR
522    uint8_t x = length >> 24;
523    fwrite(&x, 1, 1, mFile);
524    x = (length >> 16) & 0xff;
525    fwrite(&x, 1, 1, mFile);
526    x = (length >> 8) & 0xff;
527    fwrite(&x, 1, 1, mFile);
528    x = length & 0xff;
529    fwrite(&x, 1, 1, mFile);
530#else
531    CHECK(length < 65536);
532
533    uint8_t x = length >> 8;
534    fwrite(&x, 1, 1, mFile);
535    x = length & 0xff;
536    fwrite(&x, 1, 1, mFile);
537#endif
538
539    fwrite((const uint8_t *)buffer->data() + buffer->range_offset(),
540           1, length, mFile);
541
542#if USE_NALLEN_FOUR
543    mOffset += length + 4;
544#else
545    mOffset += length + 2;
546#endif
547
548    return old_offset;
549}
550
551size_t MPEG4Writer::write(
552        const void *ptr, size_t size, size_t nmemb, FILE *stream) {
553
554    const size_t bytes = size * nmemb;
555    if (mWriteMoovBoxToMemory) {
556        off_t moovBoxSize = 8 + mMoovBoxBufferOffset + bytes;
557        if (moovBoxSize > mEstimatedMoovBoxSize) {
558            for (List<off_t>::iterator it = mBoxes.begin();
559                 it != mBoxes.end(); ++it) {
560                (*it) += mOffset;
561            }
562            fseeko(mFile, mOffset, SEEK_SET);
563            fwrite(mMoovBoxBuffer, 1, mMoovBoxBufferOffset, stream);
564            fwrite(ptr, size, nmemb, stream);
565            mOffset += (bytes + mMoovBoxBufferOffset);
566            free(mMoovBoxBuffer);
567            mMoovBoxBuffer = NULL;
568            mMoovBoxBufferOffset = 0;
569            mWriteMoovBoxToMemory = false;
570            mStreamableFile = false;
571        } else {
572            memcpy(mMoovBoxBuffer + mMoovBoxBufferOffset, ptr, bytes);
573            mMoovBoxBufferOffset += bytes;
574        }
575    } else {
576        fwrite(ptr, size, nmemb, stream);
577        mOffset += bytes;
578    }
579    return bytes;
580}
581
582void MPEG4Writer::beginBox(const char *fourcc) {
583    CHECK_EQ(strlen(fourcc), 4);
584
585    mBoxes.push_back(mWriteMoovBoxToMemory?
586            mMoovBoxBufferOffset: mOffset);
587
588    writeInt32(0);
589    writeFourcc(fourcc);
590}
591
592void MPEG4Writer::endBox() {
593    CHECK(!mBoxes.empty());
594
595    off_t offset = *--mBoxes.end();
596    mBoxes.erase(--mBoxes.end());
597
598    if (mWriteMoovBoxToMemory) {
599       int32_t x = htonl(mMoovBoxBufferOffset - offset);
600       memcpy(mMoovBoxBuffer + offset, &x, 4);
601    } else {
602        fseeko(mFile, offset, SEEK_SET);
603        writeInt32(mOffset - offset);
604        mOffset -= 4;
605        fseeko(mFile, mOffset, SEEK_SET);
606    }
607}
608
609void MPEG4Writer::writeInt8(int8_t x) {
610    write(&x, 1, 1, mFile);
611}
612
613void MPEG4Writer::writeInt16(int16_t x) {
614    x = htons(x);
615    write(&x, 1, 2, mFile);
616}
617
618void MPEG4Writer::writeInt32(int32_t x) {
619    x = htonl(x);
620    write(&x, 1, 4, mFile);
621}
622
623void MPEG4Writer::writeInt64(int64_t x) {
624    x = hton64(x);
625    write(&x, 1, 8, mFile);
626}
627
628void MPEG4Writer::writeCString(const char *s) {
629    size_t n = strlen(s);
630    write(s, 1, n + 1, mFile);
631}
632
633void MPEG4Writer::writeFourcc(const char *s) {
634    CHECK_EQ(strlen(s), 4);
635    write(s, 1, 4, mFile);
636}
637
638void MPEG4Writer::write(const void *data, size_t size) {
639    write(data, 1, size, mFile);
640}
641
642bool MPEG4Writer::exceedsFileSizeLimit() {
643    // No limit
644    if (mMaxFileSizeLimitBytes == 0) {
645        return false;
646    }
647
648    int64_t nTotalBytesEstimate = static_cast<int64_t>(mEstimatedMoovBoxSize);
649    for (List<Track *>::iterator it = mTracks.begin();
650         it != mTracks.end(); ++it) {
651        nTotalBytesEstimate += (*it)->getEstimatedTrackSizeBytes();
652    }
653    return (nTotalBytesEstimate >= mMaxFileSizeLimitBytes);
654}
655
656bool MPEG4Writer::exceedsFileDurationLimit() {
657    // No limit
658    if (mMaxFileDurationLimitUs == 0) {
659        return false;
660    }
661
662    for (List<Track *>::iterator it = mTracks.begin();
663         it != mTracks.end(); ++it) {
664        if ((*it)->getDurationUs() >= mMaxFileDurationLimitUs) {
665            return true;
666        }
667    }
668    return false;
669}
670
671bool MPEG4Writer::reachedEOS() {
672    bool allDone = true;
673    for (List<Track *>::iterator it = mTracks.begin();
674         it != mTracks.end(); ++it) {
675        if (!(*it)->reachedEOS()) {
676            allDone = false;
677            break;
678        }
679    }
680
681    return allDone;
682}
683
684void MPEG4Writer::setStartTimestampUs(int64_t timeUs) {
685    LOGI("setStartTimestampUs: %lld", timeUs);
686    CHECK(timeUs >= 0);
687    Mutex::Autolock autoLock(mLock);
688    if (mStartTimestampUs < 0 || mStartTimestampUs > timeUs) {
689        mStartTimestampUs = timeUs;
690        LOGI("Earliest track starting time: %lld", mStartTimestampUs);
691    }
692}
693
694int64_t MPEG4Writer::getStartTimestampUs() {
695    Mutex::Autolock autoLock(mLock);
696    return mStartTimestampUs;
697}
698
699size_t MPEG4Writer::numTracks() {
700    Mutex::Autolock autolock(mLock);
701    return mTracks.size();
702}
703
704////////////////////////////////////////////////////////////////////////////////
705
706MPEG4Writer::Track::Track(
707        MPEG4Writer *owner, const sp<MediaSource> &source)
708    : mOwner(owner),
709      mMeta(source->getFormat()),
710      mSource(source),
711      mDone(false),
712      mPaused(false),
713      mResumed(false),
714      mTrackDurationUs(0),
715      mEstimatedTrackSizeBytes(0),
716      mSamplesHaveSameSize(true),
717      mCodecSpecificData(NULL),
718      mCodecSpecificDataSize(0),
719      mGotAllCodecSpecificData(false),
720      mReachedEOS(false) {
721    getCodecSpecificDataFromInputFormatIfPossible();
722
723    if (!mMeta->findInt32(kKeyTimeScale, &mTimeScale)) {
724        mTimeScale = 1000;
725    }
726
727    const char *mime;
728    mMeta->findCString(kKeyMIMEType, &mime);
729    mIsAvc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
730    mIsAudio = !strncasecmp(mime, "audio/", 6);
731    mIsMPEG4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) ||
732               !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC);
733
734    CHECK(mTimeScale > 0);
735}
736
737void MPEG4Writer::Track::getCodecSpecificDataFromInputFormatIfPossible() {
738    const char *mime;
739    CHECK(mMeta->findCString(kKeyMIMEType, &mime));
740
741    if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
742        uint32_t type;
743        const void *data;
744        size_t size;
745        if (mMeta->findData(kKeyAVCC, &type, &data, &size)) {
746            mCodecSpecificData = malloc(size);
747            mCodecSpecificDataSize = size;
748            memcpy(mCodecSpecificData, data, size);
749            mGotAllCodecSpecificData = true;
750        }
751    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4)
752            || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
753        uint32_t type;
754        const void *data;
755        size_t size;
756        if (mMeta->findData(kKeyESDS, &type, &data, &size)) {
757            ESDS esds(data, size);
758            if (esds.getCodecSpecificInfo(&data, &size) == OK) {
759                mCodecSpecificData = malloc(size);
760                mCodecSpecificDataSize = size;
761                memcpy(mCodecSpecificData, data, size);
762                mGotAllCodecSpecificData = true;
763            }
764        }
765    }
766}
767
768MPEG4Writer::Track::~Track() {
769    stop();
770
771    if (mCodecSpecificData != NULL) {
772        free(mCodecSpecificData);
773        mCodecSpecificData = NULL;
774    }
775}
776
777void MPEG4Writer::Track::initTrackingProgressStatus(MetaData *params) {
778    LOGV("initTrackingProgressStatus");
779    mPreviousTrackTimeUs = -1;
780    mTrackingProgressStatus = false;
781    mTrackEveryTimeDurationUs = 0;
782    {
783        int64_t timeUs;
784        if (params && params->findInt64(kKeyTrackTimeStatus, &timeUs)) {
785            LOGV("Receive request to track progress status for every %lld us", timeUs);
786            mTrackEveryTimeDurationUs = timeUs;
787            mTrackingProgressStatus = true;
788        }
789    }
790}
791
792// static
793void *MPEG4Writer::ThreadWrapper(void *me) {
794    LOGV("ThreadWrapper: %p", me);
795    MPEG4Writer *writer = static_cast<MPEG4Writer *>(me);
796    writer->threadFunc();
797    return NULL;
798}
799
800void MPEG4Writer::bufferChunk(const Chunk& chunk) {
801    LOGV("bufferChunk: %p", chunk.mTrack);
802    Mutex::Autolock autolock(mLock);
803    CHECK_EQ(mDone, false);
804
805    for (List<ChunkInfo>::iterator it = mChunkInfos.begin();
806         it != mChunkInfos.end(); ++it) {
807
808        if (chunk.mTrack == it->mTrack) {  // Found owner
809            it->mChunks.push_back(chunk);
810            mChunkReadyCondition.signal();
811            return;
812        }
813    }
814
815    CHECK("Received a chunk for a unknown track" == 0);
816}
817
818void MPEG4Writer::writeFirstChunk(ChunkInfo* info) {
819    LOGV("writeFirstChunk: %p", info->mTrack);
820
821    List<Chunk>::iterator chunkIt = info->mChunks.begin();
822    for (List<MediaBuffer *>::iterator it = chunkIt->mSamples.begin();
823         it != chunkIt->mSamples.end(); ++it) {
824
825        off_t offset = info->mTrack->isAvc()
826                            ? addLengthPrefixedSample_l(*it)
827                            : addSample_l(*it);
828        if (it == chunkIt->mSamples.begin()) {
829            info->mTrack->addChunkOffset(offset);
830        }
831    }
832
833    // Done with the current chunk.
834    // Release all the samples in this chunk.
835    while (!chunkIt->mSamples.empty()) {
836        List<MediaBuffer *>::iterator it = chunkIt->mSamples.begin();
837        (*it)->release();
838        (*it) = NULL;
839        chunkIt->mSamples.erase(it);
840    }
841    chunkIt->mSamples.clear();
842    info->mChunks.erase(chunkIt);
843}
844
845void MPEG4Writer::writeChunks() {
846    LOGV("writeChunks");
847    size_t outstandingChunks = 0;
848    while (!mChunkInfos.empty()) {
849        List<ChunkInfo>::iterator it = mChunkInfos.begin();
850        while (!it->mChunks.empty()) {
851            CHECK_EQ(OK, writeOneChunk());
852            ++outstandingChunks;
853        }
854        it->mTrack = NULL;
855        mChunkInfos.erase(it);
856    }
857    mChunkInfos.clear();
858    LOGD("%d chunks are written in the last batch", outstandingChunks);
859}
860
861status_t MPEG4Writer::writeOneChunk() {
862    LOGV("writeOneChunk");
863
864    // Find the smallest timestamp, and write that chunk out
865    // XXX: What if some track is just too slow?
866    int64_t minTimestampUs = 0x7FFFFFFFFFFFFFFFLL;
867    Track *track = NULL;
868    for (List<ChunkInfo>::iterator it = mChunkInfos.begin();
869         it != mChunkInfos.end(); ++it) {
870        if (!it->mChunks.empty()) {
871            List<Chunk>::iterator chunkIt = it->mChunks.begin();
872            if (chunkIt->mTimeStampUs < minTimestampUs) {
873                minTimestampUs = chunkIt->mTimeStampUs;
874                track = it->mTrack;
875            }
876        }
877    }
878
879    if (track == NULL) {
880        LOGV("Nothing to be written after all");
881        return OK;
882    }
883
884    if (mIsFirstChunk) {
885        mIsFirstChunk = false;
886    }
887    for (List<ChunkInfo>::iterator it = mChunkInfos.begin();
888         it != mChunkInfos.end(); ++it) {
889        if (it->mTrack == track) {
890            writeFirstChunk(&(*it));
891        }
892    }
893    return OK;
894}
895
896void MPEG4Writer::threadFunc() {
897    LOGV("threadFunc");
898
899    while (!mDone) {
900        {
901            Mutex::Autolock autolock(mLock);
902            mChunkReadyCondition.wait(mLock);
903            CHECK_EQ(writeOneChunk(), OK);
904        }
905    }
906
907    {
908        // Write ALL samples
909        Mutex::Autolock autolock(mLock);
910        writeChunks();
911    }
912}
913
914status_t MPEG4Writer::startWriterThread() {
915    LOGV("startWriterThread");
916
917    mDone = false;
918    mIsFirstChunk = true;
919    for (List<Track *>::iterator it = mTracks.begin();
920         it != mTracks.end(); ++it) {
921        ChunkInfo info;
922        info.mTrack = *it;
923        mChunkInfos.push_back(info);
924    }
925
926    pthread_attr_t attr;
927    pthread_attr_init(&attr);
928    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
929    pthread_create(&mThread, &attr, ThreadWrapper, this);
930    pthread_attr_destroy(&attr);
931    return OK;
932}
933
934status_t MPEG4Writer::Track::start(MetaData *params) {
935    if (!mDone && mPaused) {
936        mPaused = false;
937        mResumed = true;
938        return OK;
939    }
940
941    int64_t startTimeUs;
942    if (params == NULL || !params->findInt64(kKeyTime, &startTimeUs)) {
943        startTimeUs = 0;
944    }
945
946    initTrackingProgressStatus(params);
947
948    sp<MetaData> meta = new MetaData;
949    meta->setInt64(kKeyTime, startTimeUs);
950    status_t err = mSource->start(meta.get());
951    if (err != OK) {
952        mDone = mReachedEOS = true;
953        return err;
954    }
955
956    pthread_attr_t attr;
957    pthread_attr_init(&attr);
958    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
959
960    mDone = false;
961    mTrackDurationUs = 0;
962    mReachedEOS = false;
963    mEstimatedTrackSizeBytes = 0;
964
965    pthread_create(&mThread, &attr, ThreadWrapper, this);
966    pthread_attr_destroy(&attr);
967
968    return OK;
969}
970
971void MPEG4Writer::Track::pause() {
972    mPaused = true;
973}
974
975void MPEG4Writer::Track::stop() {
976    if (mDone) {
977        return;
978    }
979
980    mDone = true;
981
982    void *dummy;
983    pthread_join(mThread, &dummy);
984
985    mSource->stop();
986}
987
988bool MPEG4Writer::Track::reachedEOS() {
989    return mReachedEOS;
990}
991
992// static
993void *MPEG4Writer::Track::ThreadWrapper(void *me) {
994    Track *track = static_cast<Track *>(me);
995
996    track->threadEntry();
997
998    return NULL;
999}
1000
1001#include <ctype.h>
1002static void hexdump(const void *_data, size_t size) {
1003    const uint8_t *data = (const uint8_t *)_data;
1004    size_t offset = 0;
1005    while (offset < size) {
1006        printf("0x%04x  ", offset);
1007
1008        size_t n = size - offset;
1009        if (n > 16) {
1010            n = 16;
1011        }
1012
1013        for (size_t i = 0; i < 16; ++i) {
1014            if (i == 8) {
1015                printf(" ");
1016            }
1017
1018            if (offset + i < size) {
1019                printf("%02x ", data[offset + i]);
1020            } else {
1021                printf("   ");
1022            }
1023        }
1024
1025        printf(" ");
1026
1027        for (size_t i = 0; i < n; ++i) {
1028            if (isprint(data[offset + i])) {
1029                printf("%c", data[offset + i]);
1030            } else {
1031                printf(".");
1032            }
1033        }
1034
1035        printf("\n");
1036
1037        offset += 16;
1038    }
1039}
1040
1041
1042status_t MPEG4Writer::Track::makeAVCCodecSpecificData(
1043        const uint8_t *data, size_t size) {
1044    // hexdump(data, size);
1045
1046    if (mCodecSpecificData != NULL) {
1047        LOGE("Already have codec specific data");
1048        return ERROR_MALFORMED;
1049    }
1050
1051    if (size < 4 || memcmp("\x00\x00\x00\x01", data, 4)) {
1052        LOGE("Must start with a start code");
1053        return ERROR_MALFORMED;
1054    }
1055
1056    size_t picParamOffset = 4;
1057    while (picParamOffset + 3 < size
1058            && memcmp("\x00\x00\x00\x01", &data[picParamOffset], 4)) {
1059        ++picParamOffset;
1060    }
1061
1062    if (picParamOffset + 3 >= size) {
1063        LOGE("Could not find start-code for pictureParameterSet");
1064        return ERROR_MALFORMED;
1065    }
1066
1067    size_t seqParamSetLength = picParamOffset - 4;
1068    size_t picParamSetLength = size - picParamOffset - 4;
1069
1070    mCodecSpecificDataSize =
1071        6 + 1 + seqParamSetLength + 2 + picParamSetLength + 2;
1072
1073    mCodecSpecificData = malloc(mCodecSpecificDataSize);
1074    uint8_t *header = (uint8_t *)mCodecSpecificData;
1075    header[0] = 1;
1076    header[1] = 0x42;  // profile
1077    header[2] = 0x80;
1078    header[3] = 0x1e;  // level
1079
1080#if USE_NALLEN_FOUR
1081    header[4] = 0xfc | 3;  // length size == 4 bytes
1082#else
1083    header[4] = 0xfc | 1;  // length size == 2 bytes
1084#endif
1085
1086    header[5] = 0xe0 | 1;
1087    header[6] = seqParamSetLength >> 8;
1088    header[7] = seqParamSetLength & 0xff;
1089    memcpy(&header[8], &data[4], seqParamSetLength);
1090    header += 8 + seqParamSetLength;
1091    header[0] = 1;
1092    header[1] = picParamSetLength >> 8;
1093    header[2] = picParamSetLength & 0xff;
1094    memcpy(&header[3], &data[picParamOffset + 4], picParamSetLength);
1095
1096    return OK;
1097}
1098
1099static bool collectStatisticalData() {
1100    char value[PROPERTY_VALUE_MAX];
1101    if (property_get("media.stagefright.record-stats", value, NULL)
1102        && (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
1103        return true;
1104    }
1105    return false;
1106}
1107
1108void MPEG4Writer::Track::threadEntry() {
1109    int32_t count = 0;
1110    const int64_t interleaveDurationUs = mOwner->interleaveDuration();
1111    int64_t chunkTimestampUs = 0;
1112    int32_t nChunks = 0;
1113    int32_t nZeroLengthFrames = 0;
1114    int64_t lastTimestampUs = 0;  // Previous sample time stamp in ms
1115    int64_t lastDurationUs = 0;   // Between the previous two samples in ms
1116    int32_t sampleCount = 1;      // Sample count in the current stts table entry
1117    uint32_t previousSampleSize = 0;  // Size of the previous sample
1118    int64_t previousPausedDurationUs = 0;
1119    int64_t timestampUs;
1120    sp<MetaData> meta_data;
1121    bool collectStats = collectStatisticalData();
1122
1123    mNumSamples = 0;
1124    mMaxWriteTimeUs = 0;
1125    status_t err = OK;
1126    MediaBuffer *buffer;
1127    while (!mDone && (err = mSource->read(&buffer)) == OK) {
1128        if (buffer->range_length() == 0) {
1129            buffer->release();
1130            buffer = NULL;
1131            ++nZeroLengthFrames;
1132            continue;
1133        }
1134
1135        // If the codec specific data has not been received yet, delay pause.
1136        // After the codec specific data is received, discard what we received
1137        // when the track is to be paused.
1138        if (mPaused && !mResumed) {
1139            buffer->release();
1140            buffer = NULL;
1141            continue;
1142        }
1143
1144        ++count;
1145
1146        int32_t isCodecConfig;
1147        if (buffer->meta_data()->findInt32(kKeyIsCodecConfig, &isCodecConfig)
1148                && isCodecConfig) {
1149            CHECK(!mGotAllCodecSpecificData);
1150
1151            if (mIsAvc) {
1152                status_t err = makeAVCCodecSpecificData(
1153                        (const uint8_t *)buffer->data()
1154                            + buffer->range_offset(),
1155                        buffer->range_length());
1156                CHECK_EQ(OK, err);
1157            } else if (mIsMPEG4) {
1158                mCodecSpecificDataSize = buffer->range_length();
1159                mCodecSpecificData = malloc(mCodecSpecificDataSize);
1160                memcpy(mCodecSpecificData,
1161                        (const uint8_t *)buffer->data()
1162                            + buffer->range_offset(),
1163                       buffer->range_length());
1164            }
1165
1166            buffer->release();
1167            buffer = NULL;
1168
1169            mGotAllCodecSpecificData = true;
1170            continue;
1171        } else if (!mGotAllCodecSpecificData &&
1172                count == 1 && mIsMPEG4 && mCodecSpecificData == NULL) {
1173            // The TI mpeg4 encoder does not properly set the
1174            // codec-specific-data flag.
1175
1176            const uint8_t *data =
1177                (const uint8_t *)buffer->data() + buffer->range_offset();
1178
1179            const size_t size = buffer->range_length();
1180
1181            size_t offset = 0;
1182            while (offset + 3 < size) {
1183                if (data[offset] == 0x00 && data[offset + 1] == 0x00
1184                    && data[offset + 2] == 0x01 && data[offset + 3] == 0xb6) {
1185                    break;
1186                }
1187
1188                ++offset;
1189            }
1190
1191            // CHECK(offset + 3 < size);
1192            if (offset + 3 >= size) {
1193                // XXX assume the entire first chunk of data is the codec specific
1194                // data.
1195                offset = size;
1196            }
1197
1198            mCodecSpecificDataSize = offset;
1199            mCodecSpecificData = malloc(offset);
1200            memcpy(mCodecSpecificData, data, offset);
1201
1202            buffer->set_range(buffer->range_offset() + offset, size - offset);
1203
1204            if (size == offset) {
1205                buffer->release();
1206                buffer = NULL;
1207
1208                continue;
1209            }
1210
1211            mGotAllCodecSpecificData = true;
1212        } else if (!mGotAllCodecSpecificData && mIsAvc && count < 3) {
1213            // The TI video encoder does not flag codec specific data
1214            // as such and also splits up SPS and PPS across two buffers.
1215
1216            const uint8_t *data =
1217                (const uint8_t *)buffer->data() + buffer->range_offset();
1218
1219            size_t size = buffer->range_length();
1220
1221            CHECK(count == 2 || mCodecSpecificData == NULL);
1222
1223            size_t offset = mCodecSpecificDataSize;
1224            mCodecSpecificDataSize += size + 4;
1225            mCodecSpecificData =
1226                realloc(mCodecSpecificData, mCodecSpecificDataSize);
1227
1228            memcpy((uint8_t *)mCodecSpecificData + offset,
1229                   "\x00\x00\x00\x01", 4);
1230
1231            memcpy((uint8_t *)mCodecSpecificData + offset + 4, data, size);
1232
1233            buffer->release();
1234            buffer = NULL;
1235
1236            if (count == 2) {
1237                void *tmp = mCodecSpecificData;
1238                size = mCodecSpecificDataSize;
1239                mCodecSpecificData = NULL;
1240                mCodecSpecificDataSize = 0;
1241
1242                status_t err = makeAVCCodecSpecificData(
1243                        (const uint8_t *)tmp, size);
1244                free(tmp);
1245                tmp = NULL;
1246                CHECK_EQ(OK, err);
1247
1248                mGotAllCodecSpecificData = true;
1249            }
1250
1251            continue;
1252        }
1253
1254        if (!mGotAllCodecSpecificData) {
1255            mGotAllCodecSpecificData = true;
1256        }
1257
1258        // Make a deep copy of the MediaBuffer and Metadata and release
1259        // the original as soon as we can
1260        MediaBuffer *copy = new MediaBuffer(buffer->range_length());
1261        memcpy(copy->data(), (uint8_t *)buffer->data() + buffer->range_offset(),
1262                buffer->range_length());
1263        copy->set_range(0, buffer->range_length());
1264        meta_data = new MetaData(*buffer->meta_data().get());
1265        buffer->release();
1266        buffer = NULL;
1267
1268        if (mIsAvc) StripStartcode(copy);
1269
1270        size_t sampleSize;
1271        sampleSize = mIsAvc
1272#if USE_NALLEN_FOUR
1273                ? copy->range_length() + 4
1274#else
1275                ? copy->range_length() + 2
1276#endif
1277                : copy->range_length();
1278
1279        // Max file size or duration handling
1280        mEstimatedTrackSizeBytes += sampleSize;
1281        if (mOwner->exceedsFileSizeLimit()) {
1282            mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED, 0);
1283            break;
1284        }
1285        if (mOwner->exceedsFileDurationLimit()) {
1286            mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_DURATION_REACHED, 0);
1287            break;
1288        }
1289
1290
1291        int32_t isSync = false;
1292        meta_data->findInt32(kKeyIsSyncFrame, &isSync);
1293
1294        CHECK(meta_data->findInt64(kKeyTime, &timestampUs));
1295
1296////////////////////////////////////////////////////////////////////////////////
1297        if (mSampleSizes.empty()) {
1298            mStartTimestampUs = timestampUs;
1299            mOwner->setStartTimestampUs(mStartTimestampUs);
1300        }
1301
1302        if (mResumed) {
1303            previousPausedDurationUs += (timestampUs - mTrackDurationUs - lastDurationUs);
1304            mResumed = false;
1305        }
1306
1307        timestampUs -= previousPausedDurationUs;
1308        LOGV("time stamp: %lld and previous paused duration %lld",
1309                timestampUs, previousPausedDurationUs);
1310        if (timestampUs > mTrackDurationUs) {
1311            mTrackDurationUs = timestampUs;
1312        }
1313
1314        mSampleSizes.push_back(sampleSize);
1315        ++mNumSamples;
1316        if (mNumSamples > 2) {
1317            if (lastDurationUs != timestampUs - lastTimestampUs) {
1318                SttsTableEntry sttsEntry(sampleCount, lastDurationUs);
1319                mSttsTableEntries.push_back(sttsEntry);
1320                sampleCount = 1;
1321            } else {
1322                ++sampleCount;
1323            }
1324        }
1325        if (mSamplesHaveSameSize) {
1326            if (mNumSamples >= 2 && previousSampleSize != sampleSize) {
1327                mSamplesHaveSameSize = false;
1328            }
1329            previousSampleSize = sampleSize;
1330        }
1331        lastDurationUs = timestampUs - lastTimestampUs;
1332        lastTimestampUs = timestampUs;
1333
1334        if (isSync != 0) {
1335            mStssTableEntries.push_back(mNumSamples);
1336        }
1337
1338        if (mTrackingProgressStatus) {
1339            if (mPreviousTrackTimeUs <= 0) {
1340                mPreviousTrackTimeUs = mStartTimestampUs;
1341            }
1342            trackProgressStatus(timestampUs);
1343        }
1344        if (mOwner->numTracks() == 1) {
1345            off_t offset = mIsAvc? mOwner->addLengthPrefixedSample_l(copy)
1346                                 : mOwner->addSample_l(copy);
1347            if (mChunkOffsets.empty()) {
1348                mChunkOffsets.push_back(offset);
1349            }
1350            copy->release();
1351            copy = NULL;
1352            continue;
1353        }
1354
1355        mChunkSamples.push_back(copy);
1356        if (interleaveDurationUs == 0) {
1357            StscTableEntry stscEntry(++nChunks, 1, 1);
1358            mStscTableEntries.push_back(stscEntry);
1359            bufferChunk(timestampUs);
1360        } else {
1361            if (chunkTimestampUs == 0) {
1362                chunkTimestampUs = timestampUs;
1363            } else {
1364                if (timestampUs - chunkTimestampUs > interleaveDurationUs) {
1365                    ++nChunks;
1366                    if (collectStats) {
1367                        mChunkDurations.push_back(timestampUs - chunkTimestampUs);
1368                    }
1369                    if (nChunks == 1 ||  // First chunk
1370                        (--(mStscTableEntries.end()))->samplesPerChunk !=
1371                         mChunkSamples.size()) {
1372                        StscTableEntry stscEntry(nChunks,
1373                                mChunkSamples.size(), 1);
1374                        mStscTableEntries.push_back(stscEntry);
1375                    }
1376                    bufferChunk(timestampUs);
1377                    chunkTimestampUs = timestampUs;
1378                }
1379            }
1380        }
1381
1382    }
1383
1384    if (mSampleSizes.empty()) {
1385        err = UNKNOWN_ERROR;
1386    }
1387    mOwner->trackProgressStatus(this, -1, err);
1388
1389    // Last chunk
1390    if (mOwner->numTracks() == 1) {
1391        StscTableEntry stscEntry(1, mNumSamples, 1);
1392        mStscTableEntries.push_back(stscEntry);
1393    } else if (!mChunkSamples.empty()) {
1394        ++nChunks;
1395        StscTableEntry stscEntry(nChunks, mChunkSamples.size(), 1);
1396        mStscTableEntries.push_back(stscEntry);
1397        bufferChunk(timestampUs);
1398    }
1399
1400    // We don't really know how long the last frame lasts, since
1401    // there is no frame time after it, just repeat the previous
1402    // frame's duration.
1403    if (mNumSamples == 1) {
1404        lastDurationUs = 0;  // A single sample's duration
1405    } else {
1406        ++sampleCount;  // Count for the last sample
1407    }
1408    SttsTableEntry sttsEntry(sampleCount, lastDurationUs);
1409    mSttsTableEntries.push_back(sttsEntry);
1410    mTrackDurationUs += lastDurationUs;
1411    mReachedEOS = true;
1412    LOGI("Received total/0-length (%d/%d) buffers and encoded %d frames. Max write time: %lld us - %s",
1413            count, nZeroLengthFrames, mNumSamples, mMaxWriteTimeUs, mIsAudio? "audio": "video");
1414
1415    logStatisticalData(mIsAudio);
1416}
1417
1418void MPEG4Writer::Track::trackProgressStatus(int64_t timeUs, status_t err) {
1419    LOGV("trackProgressStatus: %lld us", timeUs);
1420    if (mTrackEveryTimeDurationUs > 0 &&
1421        timeUs - mPreviousTrackTimeUs >= mTrackEveryTimeDurationUs) {
1422        LOGV("Fire time tracking progress status at %lld us", timeUs);
1423        mOwner->trackProgressStatus(this, timeUs - mPreviousTrackTimeUs, err);
1424        mPreviousTrackTimeUs = timeUs;
1425    }
1426}
1427
1428void MPEG4Writer::trackProgressStatus(
1429        const MPEG4Writer::Track* track, int64_t timeUs, status_t err) {
1430    Mutex::Autolock lock(mLock);
1431    int32_t nTracks = mTracks.size();
1432    CHECK(nTracks >= 1);
1433    CHECK(nTracks < 64);  // Arbitrary number
1434
1435    int32_t trackNum = 0;
1436#if 0
1437    // In the worst case, we can put the trackNum
1438    // along with MEDIA_RECORDER_INFO_COMPLETION_STATUS
1439    // to report the progress.
1440    for (List<Track *>::iterator it = mTracks.begin();
1441         it != mTracks.end(); ++it, ++trackNum) {
1442        if (track == (*it)) {
1443            break;
1444        }
1445    }
1446#endif
1447    CHECK(trackNum < nTracks);
1448    trackNum <<= 16;
1449
1450    // Error notification
1451    // Do not consider ERROR_END_OF_STREAM an error
1452    if (err != OK && err != ERROR_END_OF_STREAM) {
1453        notify(MEDIA_RECORDER_EVENT_ERROR,
1454               trackNum | MEDIA_RECORDER_ERROR_UNKNOWN,
1455               err);
1456        return;
1457    }
1458
1459    if (timeUs == -1) {
1460        // Send completion notification
1461        notify(MEDIA_RECORDER_EVENT_INFO,
1462               trackNum | MEDIA_RECORDER_INFO_COMPLETION_STATUS,
1463               err);
1464        return;
1465    } else {
1466        // Send progress status
1467        notify(MEDIA_RECORDER_EVENT_INFO,
1468               trackNum | MEDIA_RECORDER_INFO_PROGRESS_TIME_STATUS,
1469               timeUs / 1000);
1470    }
1471}
1472
1473void MPEG4Writer::Track::findMinAvgMaxSampleDurationMs(
1474        int32_t *min, int32_t *avg, int32_t *max) {
1475    CHECK(!mSampleSizes.empty());
1476    int32_t avgSampleDurationMs = mTrackDurationUs / 1000 / mNumSamples;
1477    int32_t minSampleDurationMs = 0x7FFFFFFF;
1478    int32_t maxSampleDurationMs = 0;
1479    for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin();
1480        it != mSttsTableEntries.end(); ++it) {
1481        int32_t sampleDurationMs =
1482            (static_cast<int32_t>(it->sampleDurationUs) + 500) / 1000;
1483        if (sampleDurationMs > maxSampleDurationMs) {
1484            maxSampleDurationMs = sampleDurationMs;
1485        } else if (sampleDurationMs < minSampleDurationMs) {
1486            minSampleDurationMs = sampleDurationMs;
1487        }
1488        LOGI("sample duration: %d ms", sampleDurationMs);
1489    }
1490    CHECK(minSampleDurationMs != 0);
1491    CHECK(avgSampleDurationMs != 0);
1492    CHECK(maxSampleDurationMs != 0);
1493    *min = minSampleDurationMs;
1494    *avg = avgSampleDurationMs;
1495    *max = maxSampleDurationMs;
1496}
1497
1498// Don't count the last duration
1499void MPEG4Writer::Track::findMinMaxChunkDurations(int64_t *min, int64_t *max) {
1500    int64_t duration = mOwner->interleaveDuration();
1501    int64_t minChunkDuration = duration;
1502    int64_t maxChunkDuration = duration;
1503    if (mChunkDurations.size() > 1) {
1504        for (List<int64_t>::iterator it = mChunkDurations.begin();
1505            it != --mChunkDurations.end(); ++it) {
1506            if (minChunkDuration > (*it)) {
1507                minChunkDuration = (*it);
1508            } else if (maxChunkDuration < (*it)) {
1509                maxChunkDuration = (*it);
1510            }
1511        }
1512    }
1513    *min = minChunkDuration;
1514    *max = maxChunkDuration;
1515}
1516
1517void MPEG4Writer::Track::logStatisticalData(bool isAudio) {
1518    if (mTrackDurationUs <= 0 || mSampleSizes.empty()) {
1519        LOGI("nothing is recorded");
1520        return;
1521    }
1522
1523    bool collectStats = collectStatisticalData();
1524
1525    if (collectStats) {
1526        LOGI("%s track - duration %lld us, total %d frames",
1527                isAudio? "audio": "video", mTrackDurationUs,
1528                mNumSamples);
1529        int32_t min, avg, max;
1530        findMinAvgMaxSampleDurationMs(&min, &avg, &max);
1531        LOGI("min/avg/max sample duration (ms): %d/%d/%d", min, avg, max);
1532        if (!isAudio) {
1533            float avgFps = 1000.0 / avg;
1534            float minFps = 1000.0 / max;
1535            float maxFps = 1000.0 / min;
1536            LOGI("min/avg/max frame rate (fps): %.2f/%.2f/%.2f",
1537                minFps, avgFps, maxFps);
1538        }
1539
1540        int64_t totalBytes = 0;
1541        for (List<size_t>::iterator it = mSampleSizes.begin();
1542            it != mSampleSizes.end(); ++it) {
1543            totalBytes += (*it);
1544        }
1545        float bitRate = (totalBytes * 8000000.0) / mTrackDurationUs;
1546        LOGI("avg bit rate (bps): %.2f", bitRate);
1547
1548        int64_t duration = mOwner->interleaveDuration();
1549        if (duration != 0) {  // If interleaving is enabled
1550            int64_t minChunk, maxChunk;
1551            findMinMaxChunkDurations(&minChunk, &maxChunk);
1552            LOGI("min/avg/max chunk duration (ms): %lld/%lld/%lld",
1553                minChunk, duration, maxChunk);
1554        }
1555    }
1556}
1557
1558void MPEG4Writer::Track::bufferChunk(int64_t timestampUs) {
1559    LOGV("bufferChunk");
1560
1561    int64_t startTimeUs = systemTime() / 1000;
1562    Chunk chunk(this, timestampUs, mChunkSamples);
1563    mOwner->bufferChunk(chunk);
1564    mChunkSamples.clear();
1565    int64_t endTimeUs = systemTime() / 1000;
1566    if (mMaxWriteTimeUs < endTimeUs - startTimeUs) {
1567        mMaxWriteTimeUs = endTimeUs - startTimeUs;
1568    }
1569}
1570
1571int64_t MPEG4Writer::Track::getDurationUs() const {
1572    return mTrackDurationUs;
1573}
1574
1575int64_t MPEG4Writer::Track::getEstimatedTrackSizeBytes() const {
1576    return mEstimatedTrackSizeBytes;
1577}
1578
1579void MPEG4Writer::Track::writeTrackHeader(
1580        int32_t trackID, bool use32BitOffset) {
1581    const char *mime;
1582    bool success = mMeta->findCString(kKeyMIMEType, &mime);
1583    CHECK(success);
1584
1585    LOGV("%s track time scale: %d",
1586        mIsAudio? "Audio": "Video", mTimeScale);
1587
1588
1589    time_t now = time(NULL);
1590    int32_t mvhdTimeScale = mOwner->getTimeScale();
1591    int64_t trakDurationUs = getDurationUs();
1592
1593    mOwner->beginBox("trak");
1594
1595      mOwner->beginBox("tkhd");
1596        // Flags = 7 to indicate that the track is enabled, and
1597        // part of the presentation
1598        mOwner->writeInt32(0x07);          // version=0, flags=7
1599        mOwner->writeInt32(now);           // creation time
1600        mOwner->writeInt32(now);           // modification time
1601        mOwner->writeInt32(trackID);
1602        mOwner->writeInt32(0);             // reserved
1603        int32_t tkhdDuration =
1604            (trakDurationUs * mvhdTimeScale + 5E5) / 1E6;
1605        mOwner->writeInt32(tkhdDuration);  // in mvhd timescale
1606        mOwner->writeInt32(0);             // reserved
1607        mOwner->writeInt32(0);             // reserved
1608        mOwner->writeInt16(0);             // layer
1609        mOwner->writeInt16(0);             // alternate group
1610        mOwner->writeInt16(mIsAudio ? 0x100 : 0);  // volume
1611        mOwner->writeInt16(0);             // reserved
1612
1613        mOwner->writeInt32(0x10000);       // matrix
1614        mOwner->writeInt32(0);
1615        mOwner->writeInt32(0);
1616        mOwner->writeInt32(0);
1617        mOwner->writeInt32(0x10000);
1618        mOwner->writeInt32(0);
1619        mOwner->writeInt32(0);
1620        mOwner->writeInt32(0);
1621        mOwner->writeInt32(0x40000000);
1622
1623        if (mIsAudio) {
1624            mOwner->writeInt32(0);
1625            mOwner->writeInt32(0);
1626        } else {
1627            int32_t width, height;
1628            bool success = mMeta->findInt32(kKeyWidth, &width);
1629            success = success && mMeta->findInt32(kKeyHeight, &height);
1630            CHECK(success);
1631
1632            mOwner->writeInt32(width << 16);   // 32-bit fixed-point value
1633            mOwner->writeInt32(height << 16);  // 32-bit fixed-point value
1634        }
1635      mOwner->endBox();  // tkhd
1636
1637      int64_t moovStartTimeUs = mOwner->getStartTimestampUs();
1638      if (mStartTimestampUs != moovStartTimeUs) {
1639        mOwner->beginBox("edts");
1640          mOwner->beginBox("elst");
1641            mOwner->writeInt32(0);           // version=0, flags=0: 32-bit time
1642            mOwner->writeInt32(2);           // never ends with an empty list
1643
1644            // First elst entry: specify the starting time offset
1645            int64_t offsetUs = mStartTimestampUs - moovStartTimeUs;
1646            int32_t seg = (offsetUs * mvhdTimeScale + 5E5) / 1E6;
1647            mOwner->writeInt32(seg);         // in mvhd timecale
1648            mOwner->writeInt32(-1);          // starting time offset
1649            mOwner->writeInt32(1 << 16);     // rate = 1.0
1650
1651            // Second elst entry: specify the track duration
1652            seg = (trakDurationUs * mvhdTimeScale + 5E5) / 1E6;
1653            mOwner->writeInt32(seg);         // in mvhd timescale
1654            mOwner->writeInt32(0);
1655            mOwner->writeInt32(1 << 16);
1656          mOwner->endBox();
1657        mOwner->endBox();
1658      }
1659
1660      mOwner->beginBox("mdia");
1661
1662        mOwner->beginBox("mdhd");
1663          mOwner->writeInt32(0);             // version=0, flags=0
1664          mOwner->writeInt32(now);           // creation time
1665          mOwner->writeInt32(now);           // modification time
1666          mOwner->writeInt32(mTimeScale);    // media timescale
1667          int32_t mdhdDuration = (trakDurationUs * mTimeScale + 5E5) / 1E6;
1668          mOwner->writeInt32(mdhdDuration);  // use media timescale
1669          // Language follows the three letter standard ISO-639-2/T
1670          // 'e', 'n', 'g' for "English", for instance.
1671          // Each character is packed as the difference between its ASCII value and 0x60.
1672          // For "English", these are 00101, 01110, 00111.
1673          // XXX: Where is the padding bit located: 0x15C7?
1674          mOwner->writeInt16(0);             // language code
1675          mOwner->writeInt16(0);             // predefined
1676        mOwner->endBox();
1677
1678        mOwner->beginBox("hdlr");
1679          mOwner->writeInt32(0);             // version=0, flags=0
1680          mOwner->writeInt32(0);             // component type: should be mhlr
1681          mOwner->writeFourcc(mIsAudio ? "soun" : "vide");  // component subtype
1682          mOwner->writeInt32(0);             // reserved
1683          mOwner->writeInt32(0);             // reserved
1684          mOwner->writeInt32(0);             // reserved
1685          // Removing "r" for the name string just makes the string 4 byte aligned
1686          mOwner->writeCString(mIsAudio ? "SoundHandle": "VideoHandle");  // name
1687        mOwner->endBox();
1688
1689        mOwner->beginBox("minf");
1690          if (mIsAudio) {
1691              mOwner->beginBox("smhd");
1692              mOwner->writeInt32(0);           // version=0, flags=0
1693              mOwner->writeInt16(0);           // balance
1694              mOwner->writeInt16(0);           // reserved
1695              mOwner->endBox();
1696          } else {
1697              mOwner->beginBox("vmhd");
1698              mOwner->writeInt32(0x01);        // version=0, flags=1
1699              mOwner->writeInt16(0);           // graphics mode
1700              mOwner->writeInt16(0);           // opcolor
1701              mOwner->writeInt16(0);
1702              mOwner->writeInt16(0);
1703              mOwner->endBox();
1704          }
1705
1706          mOwner->beginBox("dinf");
1707            mOwner->beginBox("dref");
1708              mOwner->writeInt32(0);  // version=0, flags=0
1709              mOwner->writeInt32(1);  // entry count (either url or urn)
1710              // The table index here refers to the sample description index
1711              // in the sample table entries.
1712              mOwner->beginBox("url ");
1713                mOwner->writeInt32(1);  // version=0, flags=1 (self-contained)
1714              mOwner->endBox();  // url
1715            mOwner->endBox();  // dref
1716          mOwner->endBox();  // dinf
1717
1718        mOwner->beginBox("stbl");
1719
1720          mOwner->beginBox("stsd");
1721            mOwner->writeInt32(0);               // version=0, flags=0
1722            mOwner->writeInt32(1);               // entry count
1723            if (mIsAudio) {
1724                const char *fourcc = NULL;
1725                if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime)) {
1726                    fourcc = "samr";
1727                } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) {
1728                    fourcc = "sawb";
1729                } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) {
1730                    fourcc = "mp4a";
1731                } else {
1732                    LOGE("Unknown mime type '%s'.", mime);
1733                    CHECK(!"should not be here, unknown mime type.");
1734                }
1735
1736                mOwner->beginBox(fourcc);          // audio format
1737                  mOwner->writeInt32(0);           // reserved
1738                  mOwner->writeInt16(0);           // reserved
1739                  mOwner->writeInt16(0x1);         // data ref index
1740                  mOwner->writeInt32(0);           // reserved
1741                  mOwner->writeInt32(0);           // reserved
1742                  int32_t nChannels;
1743                  CHECK_EQ(true, mMeta->findInt32(kKeyChannelCount, &nChannels));
1744                  mOwner->writeInt16(nChannels);   // channel count
1745                  mOwner->writeInt16(16);          // sample size
1746                  mOwner->writeInt16(0);           // predefined
1747                  mOwner->writeInt16(0);           // reserved
1748
1749                  int32_t samplerate;
1750                  bool success = mMeta->findInt32(kKeySampleRate, &samplerate);
1751                  CHECK(success);
1752
1753                  mOwner->writeInt32(samplerate << 16);
1754                  if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) {
1755                    mOwner->beginBox("esds");
1756
1757                        mOwner->writeInt32(0);     // version=0, flags=0
1758                        mOwner->writeInt8(0x03);   // ES_DescrTag
1759                        mOwner->writeInt8(23 + mCodecSpecificDataSize);
1760                        mOwner->writeInt16(0x0000);// ES_ID
1761                        mOwner->writeInt8(0x00);
1762
1763                        mOwner->writeInt8(0x04);   // DecoderConfigDescrTag
1764                        mOwner->writeInt8(15 + mCodecSpecificDataSize);
1765                        mOwner->writeInt8(0x40);   // objectTypeIndication ISO/IEC 14492-2
1766                        mOwner->writeInt8(0x15);   // streamType AudioStream
1767
1768                        mOwner->writeInt16(0x03);  // XXX
1769                        mOwner->writeInt8(0x00);   // buffer size 24-bit
1770                        mOwner->writeInt32(96000); // max bit rate
1771                        mOwner->writeInt32(96000); // avg bit rate
1772
1773                        mOwner->writeInt8(0x05);   // DecoderSpecificInfoTag
1774                        mOwner->writeInt8(mCodecSpecificDataSize);
1775                        mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
1776
1777                        static const uint8_t kData2[] = {
1778                            0x06,  // SLConfigDescriptorTag
1779                            0x01,
1780                            0x02
1781                        };
1782                        mOwner->write(kData2, sizeof(kData2));
1783
1784                    mOwner->endBox();  // esds
1785                  } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime) ||
1786                             !strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) {
1787                    // 3gpp2 Spec AMRSampleEntry fields
1788                    mOwner->beginBox("damr");
1789                      mOwner->writeCString("   ");  // vendor: 4 bytes
1790                      mOwner->writeInt8(0);         // decoder version
1791                      mOwner->writeInt16(0x83FF);   // mode set: all enabled
1792                      mOwner->writeInt8(0);         // mode change period
1793                      mOwner->writeInt8(1);         // frames per sample
1794                    mOwner->endBox();
1795                  }
1796                mOwner->endBox();
1797            } else {
1798                if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
1799                    mOwner->beginBox("mp4v");
1800                } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
1801                    mOwner->beginBox("s263");
1802                } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
1803                    mOwner->beginBox("avc1");
1804                } else {
1805                    LOGE("Unknown mime type '%s'.", mime);
1806                    CHECK(!"should not be here, unknown mime type.");
1807                }
1808
1809                  mOwner->writeInt32(0);           // reserved
1810                  mOwner->writeInt16(0);           // reserved
1811                  mOwner->writeInt16(1);           // data ref index
1812                  mOwner->writeInt16(0);           // predefined
1813                  mOwner->writeInt16(0);           // reserved
1814                  mOwner->writeInt32(0);           // predefined
1815                  mOwner->writeInt32(0);           // predefined
1816                  mOwner->writeInt32(0);           // predefined
1817
1818                  int32_t width, height;
1819                  bool success = mMeta->findInt32(kKeyWidth, &width);
1820                  success = success && mMeta->findInt32(kKeyHeight, &height);
1821                  CHECK(success);
1822
1823                  mOwner->writeInt16(width);
1824                  mOwner->writeInt16(height);
1825                  mOwner->writeInt32(0x480000);    // horiz resolution
1826                  mOwner->writeInt32(0x480000);    // vert resolution
1827                  mOwner->writeInt32(0);           // reserved
1828                  mOwner->writeInt16(1);           // frame count
1829                  mOwner->write("                                ", 32);
1830                  mOwner->writeInt16(0x18);        // depth
1831                  mOwner->writeInt16(-1);          // predefined
1832
1833                  CHECK(23 + mCodecSpecificDataSize < 128);
1834
1835                  if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
1836                      mOwner->beginBox("esds");
1837
1838                        mOwner->writeInt32(0);           // version=0, flags=0
1839
1840                        mOwner->writeInt8(0x03);  // ES_DescrTag
1841                        mOwner->writeInt8(23 + mCodecSpecificDataSize);
1842                        mOwner->writeInt16(0x0000);  // ES_ID
1843                        mOwner->writeInt8(0x1f);
1844
1845                        mOwner->writeInt8(0x04);  // DecoderConfigDescrTag
1846                        mOwner->writeInt8(15 + mCodecSpecificDataSize);
1847                        mOwner->writeInt8(0x20);  // objectTypeIndication ISO/IEC 14492-2
1848                        mOwner->writeInt8(0x11);  // streamType VisualStream
1849
1850                        static const uint8_t kData[] = {
1851                            0x01, 0x77, 0x00,
1852                            0x00, 0x03, 0xe8, 0x00,
1853                            0x00, 0x03, 0xe8, 0x00
1854                        };
1855                        mOwner->write(kData, sizeof(kData));
1856
1857                        mOwner->writeInt8(0x05);  // DecoderSpecificInfoTag
1858
1859                        mOwner->writeInt8(mCodecSpecificDataSize);
1860                        mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
1861
1862                        static const uint8_t kData2[] = {
1863                            0x06,  // SLConfigDescriptorTag
1864                            0x01,
1865                            0x02
1866                        };
1867                        mOwner->write(kData2, sizeof(kData2));
1868
1869                      mOwner->endBox();  // esds
1870                  } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
1871                      mOwner->beginBox("d263");
1872
1873                          mOwner->writeInt32(0);  // vendor
1874                          mOwner->writeInt8(0);   // decoder version
1875                          mOwner->writeInt8(10);  // level: 10
1876                          mOwner->writeInt8(0);   // profile: 0
1877
1878                      mOwner->endBox();  // d263
1879                  } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
1880                      mOwner->beginBox("avcC");
1881                        mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
1882                      mOwner->endBox();  // avcC
1883                  }
1884
1885                  mOwner->beginBox("pasp");
1886                    // This is useful if the pixel is not square
1887                    mOwner->writeInt32(1 << 16);  // hspacing
1888                    mOwner->writeInt32(1 << 16);  // vspacing
1889                  mOwner->endBox();  // pasp
1890                mOwner->endBox();  // mp4v, s263 or avc1
1891            }
1892          mOwner->endBox();  // stsd
1893
1894          mOwner->beginBox("stts");
1895            mOwner->writeInt32(0);  // version=0, flags=0
1896            mOwner->writeInt32(mSttsTableEntries.size());
1897            for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin();
1898                 it != mSttsTableEntries.end(); ++it) {
1899                mOwner->writeInt32(it->sampleCount);
1900                int32_t dur = (it->sampleDurationUs * mTimeScale + 5E5) / 1E6;
1901                mOwner->writeInt32(dur);
1902            }
1903          mOwner->endBox();  // stts
1904
1905          if (!mIsAudio) {
1906            mOwner->beginBox("stss");
1907              mOwner->writeInt32(0);  // version=0, flags=0
1908              mOwner->writeInt32(mStssTableEntries.size());  // number of sync frames
1909              for (List<int32_t>::iterator it = mStssTableEntries.begin();
1910                   it != mStssTableEntries.end(); ++it) {
1911                  mOwner->writeInt32(*it);
1912              }
1913            mOwner->endBox();  // stss
1914          }
1915
1916          mOwner->beginBox("stsz");
1917            mOwner->writeInt32(0);  // version=0, flags=0
1918            if (mSamplesHaveSameSize) {
1919                List<size_t>::iterator it = mSampleSizes.begin();
1920                mOwner->writeInt32(*it);  // default sample size
1921            } else {
1922                mOwner->writeInt32(0);
1923            }
1924            mOwner->writeInt32(mNumSamples);
1925            if (!mSamplesHaveSameSize) {
1926                for (List<size_t>::iterator it = mSampleSizes.begin();
1927                     it != mSampleSizes.end(); ++it) {
1928                    mOwner->writeInt32(*it);
1929                }
1930            }
1931          mOwner->endBox();  // stsz
1932
1933          mOwner->beginBox("stsc");
1934            mOwner->writeInt32(0);  // version=0, flags=0
1935            mOwner->writeInt32(mStscTableEntries.size());
1936            for (List<StscTableEntry>::iterator it = mStscTableEntries.begin();
1937                 it != mStscTableEntries.end(); ++it) {
1938                mOwner->writeInt32(it->firstChunk);
1939                mOwner->writeInt32(it->samplesPerChunk);
1940                mOwner->writeInt32(it->sampleDescriptionId);
1941            }
1942          mOwner->endBox();  // stsc
1943          mOwner->beginBox(use32BitOffset? "stco": "co64");
1944            mOwner->writeInt32(0);  // version=0, flags=0
1945            mOwner->writeInt32(mChunkOffsets.size());
1946            for (List<off_t>::iterator it = mChunkOffsets.begin();
1947                 it != mChunkOffsets.end(); ++it) {
1948                if (use32BitOffset) {
1949                    mOwner->writeInt32(static_cast<int32_t>(*it));
1950                } else {
1951                    mOwner->writeInt64((*it));
1952                }
1953            }
1954          mOwner->endBox();  // stco or co64
1955
1956        mOwner->endBox();  // stbl
1957       mOwner->endBox();  // minf
1958      mOwner->endBox();  // mdia
1959    mOwner->endBox();  // trak
1960}
1961
1962}  // namespace android
1963