MPEG4Writer.cpp revision 365a963142093a1cd8efdcea76b5f65096a5b115
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
37namespace android {
38
39class MPEG4Writer::Track {
40public:
41    Track(MPEG4Writer *owner, const sp<MediaSource> &source);
42    ~Track();
43
44    status_t start();
45    void stop();
46    bool reachedEOS();
47
48    int64_t getDurationUs() const;
49    int64_t getEstimatedTrackSizeBytes() const;
50    void writeTrackHeader(int32_t trackID);
51
52private:
53    MPEG4Writer *mOwner;
54    sp<MetaData> mMeta;
55    sp<MediaSource> mSource;
56    volatile bool mDone;
57    int64_t mMaxTimeStampUs;
58    int64_t mEstimatedTrackSizeBytes;
59
60    pthread_t mThread;
61
62    struct SampleInfo {
63        size_t size;
64        int64_t timestamp;
65    };
66    List<SampleInfo>    mSampleInfos;
67    bool                mSamplesHaveSameSize;
68
69    List<MediaBuffer *> mChunkSamples;
70    List<off_t>         mChunkOffsets;
71
72    struct StscTableEntry {
73
74        StscTableEntry(uint32_t chunk, uint32_t samples, uint32_t id)
75            : firstChunk(chunk),
76              samplesPerChunk(samples),
77              sampleDescriptionId(id) {}
78
79        uint32_t firstChunk;
80        uint32_t samplesPerChunk;
81        uint32_t sampleDescriptionId;
82    };
83    List<StscTableEntry> mStscTableEntries;
84
85    List<int32_t> mStssTableEntries;
86    List<int64_t> mChunkDurations;
87
88    struct SttsTableEntry {
89
90        SttsTableEntry(uint32_t count, uint32_t duration)
91            : sampleCount(count), sampleDuration(duration) {}
92
93        uint32_t sampleCount;
94        uint32_t sampleDuration;
95    };
96    List<SttsTableEntry> mSttsTableEntries;
97
98    void *mCodecSpecificData;
99    size_t mCodecSpecificDataSize;
100    bool mGotAllCodecSpecificData;
101
102    bool mReachedEOS;
103    int64_t mStartTimestampUs;
104
105    static void *ThreadWrapper(void *me);
106    void threadEntry();
107
108    status_t makeAVCCodecSpecificData(
109            const uint8_t *data, size_t size);
110    void writeOneChunk(bool isAvc);
111    void logStatisticalData(bool isAudio);
112    void findMinMaxFrameRates(float *minFps, float *maxFps);
113    void findMinMaxChunkDurations(int64_t *min, int64_t *max);
114
115    Track(const Track &);
116    Track &operator=(const Track &);
117};
118
119#define USE_NALLEN_FOUR         1
120
121MPEG4Writer::MPEG4Writer(const char *filename)
122    : mFile(fopen(filename, "wb")),
123      mOffset(0),
124      mMdatOffset(0),
125      mEstimatedMoovBoxSize(0),
126      mInterleaveDurationUs(500000) {
127    CHECK(mFile != NULL);
128}
129
130MPEG4Writer::MPEG4Writer(int fd)
131    : mFile(fdopen(fd, "wb")),
132      mOffset(0),
133      mMdatOffset(0),
134      mEstimatedMoovBoxSize(0),
135      mInterleaveDurationUs(500000) {
136    CHECK(mFile != NULL);
137}
138
139MPEG4Writer::~MPEG4Writer() {
140    stop();
141
142    for (List<Track *>::iterator it = mTracks.begin();
143         it != mTracks.end(); ++it) {
144        delete *it;
145    }
146    mTracks.clear();
147}
148
149status_t MPEG4Writer::addSource(const sp<MediaSource> &source) {
150    Track *track = new Track(this, source);
151    mTracks.push_back(track);
152
153    return OK;
154}
155
156status_t MPEG4Writer::start() {
157    if (mFile == NULL) {
158        return UNKNOWN_ERROR;
159    }
160
161    mStartTimestampUs = 0;
162    mStreamableFile = true;
163    mWriteMoovBoxToMemory = false;
164    mMoovBoxBuffer = NULL;
165    mMoovBoxBufferOffset = 0;
166
167    beginBox("ftyp");
168      writeFourcc("isom");
169      writeInt32(0);
170      writeFourcc("isom");
171    endBox();
172
173    mFreeBoxOffset = mOffset;
174
175    if (mEstimatedMoovBoxSize == 0) {
176        // XXX: Estimate the moov box size
177        //      based on max file size or duration limit
178        mEstimatedMoovBoxSize = 0x0F00;
179    }
180    CHECK(mEstimatedMoovBoxSize >= 8);
181    fseeko(mFile, mFreeBoxOffset, SEEK_SET);
182    writeInt32(mEstimatedMoovBoxSize);
183    write("free", 4);
184
185    mMdatOffset = mFreeBoxOffset + mEstimatedMoovBoxSize;
186    mOffset = mMdatOffset;
187    fseeko(mFile, mMdatOffset, SEEK_SET);
188    write("\x00\x00\x00\x01mdat????????", 16);
189    for (List<Track *>::iterator it = mTracks.begin();
190         it != mTracks.end(); ++it) {
191        status_t err = (*it)->start();
192
193        if (err != OK) {
194            for (List<Track *>::iterator it2 = mTracks.begin();
195                 it2 != it; ++it2) {
196                (*it2)->stop();
197            }
198
199            return err;
200        }
201    }
202
203    return OK;
204}
205
206void MPEG4Writer::stop() {
207    if (mFile == NULL) {
208        return;
209    }
210
211    int64_t max_duration = 0;
212    for (List<Track *>::iterator it = mTracks.begin();
213         it != mTracks.end(); ++it) {
214        (*it)->stop();
215
216        int64_t duration = (*it)->getDurationUs();
217        if (duration > max_duration) {
218            max_duration = duration;
219        }
220    }
221
222
223    // Fix up the size of the 'mdat' chunk.
224    fseeko(mFile, mMdatOffset + 8, SEEK_SET);
225    int64_t size = mOffset - mMdatOffset;
226    size = hton64(size);
227    fwrite(&size, 1, 8, mFile);
228    fseeko(mFile, mOffset, SEEK_SET);
229
230    time_t now = time(NULL);
231    const off_t moovOffset = mOffset;
232    mWriteMoovBoxToMemory = true;
233    mMoovBoxBuffer = (uint8_t *) malloc(mEstimatedMoovBoxSize);
234    mMoovBoxBufferOffset = 0;
235    CHECK(mMoovBoxBuffer != NULL);
236
237    beginBox("moov");
238
239      beginBox("mvhd");
240        writeInt32(0);             // version=0, flags=0
241        writeInt32(now);           // creation time
242        writeInt32(now);           // modification time
243        writeInt32(1000);          // timescale
244        writeInt32(max_duration / 1000);
245        writeInt32(0x10000);       // rate
246        writeInt16(0x100);         // volume
247        writeInt16(0);             // reserved
248        writeInt32(0);             // reserved
249        writeInt32(0);             // reserved
250        writeInt32(0x10000);       // matrix
251        writeInt32(0);
252        writeInt32(0);
253        writeInt32(0);
254        writeInt32(0x10000);
255        writeInt32(0);
256        writeInt32(0);
257        writeInt32(0);
258        writeInt32(0x40000000);
259        writeInt32(0);             // predefined
260        writeInt32(0);             // predefined
261        writeInt32(0);             // predefined
262        writeInt32(0);             // predefined
263        writeInt32(0);             // predefined
264        writeInt32(0);             // predefined
265        writeInt32(mTracks.size() + 1);  // nextTrackID
266      endBox();  // mvhd
267
268      int32_t id = 1;
269      for (List<Track *>::iterator it = mTracks.begin();
270           it != mTracks.end(); ++it, ++id) {
271          (*it)->writeTrackHeader(id);
272      }
273    endBox();  // moov
274
275    mWriteMoovBoxToMemory = false;
276    if (mStreamableFile) {
277        CHECK(mMoovBoxBufferOffset + 8 <= mEstimatedMoovBoxSize);
278
279        // Moov box
280        fseeko(mFile, mFreeBoxOffset, SEEK_SET);
281        mOffset = mFreeBoxOffset;
282        write(mMoovBoxBuffer, 1, mMoovBoxBufferOffset, mFile);
283
284        // Free box
285        mFreeBoxOffset = mStreamableFile? mOffset: mFreeBoxOffset;
286        fseeko(mFile, mFreeBoxOffset, SEEK_SET);
287        writeInt32(mEstimatedMoovBoxSize - mMoovBoxBufferOffset);
288        write("free", 4);
289
290        // Free temp memory
291        free(mMoovBoxBuffer);
292        mMoovBoxBuffer = NULL;
293        mMoovBoxBufferOffset = 0;
294    }
295
296    CHECK(mBoxes.empty());
297
298    fflush(mFile);
299    fclose(mFile);
300    mFile = NULL;
301}
302
303status_t MPEG4Writer::setInterleaveDuration(uint32_t durationUs) {
304    mInterleaveDurationUs = durationUs;
305    return OK;
306}
307
308void MPEG4Writer::lock() {
309    mLock.lock();
310}
311
312void MPEG4Writer::unlock() {
313    mLock.unlock();
314}
315
316off_t MPEG4Writer::addSample_l(MediaBuffer *buffer) {
317    off_t old_offset = mOffset;
318
319    fwrite((const uint8_t *)buffer->data() + buffer->range_offset(),
320           1, buffer->range_length(), mFile);
321
322    mOffset += buffer->range_length();
323
324    return old_offset;
325}
326
327static void StripStartcode(MediaBuffer *buffer) {
328    if (buffer->range_length() < 4) {
329        return;
330    }
331
332    const uint8_t *ptr =
333        (const uint8_t *)buffer->data() + buffer->range_offset();
334
335    if (!memcmp(ptr, "\x00\x00\x00\x01", 4)) {
336        buffer->set_range(
337                buffer->range_offset() + 4, buffer->range_length() - 4);
338    }
339}
340
341off_t MPEG4Writer::addLengthPrefixedSample_l(MediaBuffer *buffer) {
342    off_t old_offset = mOffset;
343
344    size_t length = buffer->range_length();
345
346#if USE_NALLEN_FOUR
347    uint8_t x = length >> 24;
348    fwrite(&x, 1, 1, mFile);
349    x = (length >> 16) & 0xff;
350    fwrite(&x, 1, 1, mFile);
351    x = (length >> 8) & 0xff;
352    fwrite(&x, 1, 1, mFile);
353    x = length & 0xff;
354    fwrite(&x, 1, 1, mFile);
355#else
356    CHECK(length < 65536);
357
358    uint8_t x = length >> 8;
359    fwrite(&x, 1, 1, mFile);
360    x = length & 0xff;
361    fwrite(&x, 1, 1, mFile);
362#endif
363
364    fwrite((const uint8_t *)buffer->data() + buffer->range_offset(),
365           1, length, mFile);
366
367#if USE_NALLEN_FOUR
368    mOffset += length + 4;
369#else
370    mOffset += length + 2;
371#endif
372
373    return old_offset;
374}
375
376size_t MPEG4Writer::write(
377        const void *ptr, size_t size, size_t nmemb, FILE *stream) {
378
379    const size_t bytes = size * nmemb;
380    if (mWriteMoovBoxToMemory) {
381        if (8 + mMoovBoxBufferOffset + bytes > mEstimatedMoovBoxSize) {
382            for (List<off_t>::iterator it = mBoxes.begin();
383                 it != mBoxes.end(); ++it) {
384                (*it) += mOffset;
385            }
386            fseeko(mFile, mOffset, SEEK_SET);
387            fwrite(mMoovBoxBuffer, 1, mMoovBoxBufferOffset, stream);
388            fwrite(ptr, size, nmemb, stream);
389            mOffset += (bytes + mMoovBoxBufferOffset);
390            free(mMoovBoxBuffer);
391            mMoovBoxBuffer = NULL;
392            mMoovBoxBufferOffset = 0;
393            mWriteMoovBoxToMemory = false;
394            mStreamableFile = false;
395        } else {
396            memcpy(mMoovBoxBuffer + mMoovBoxBufferOffset, ptr, bytes);
397            mMoovBoxBufferOffset += bytes;
398        }
399    } else {
400        fwrite(ptr, size, nmemb, stream);
401        mOffset += bytes;
402    }
403    return bytes;
404}
405
406void MPEG4Writer::beginBox(const char *fourcc) {
407    CHECK_EQ(strlen(fourcc), 4);
408
409    mBoxes.push_back(mWriteMoovBoxToMemory?
410            mMoovBoxBufferOffset: mOffset);
411
412    writeInt32(0);
413    writeFourcc(fourcc);
414}
415
416void MPEG4Writer::endBox() {
417    CHECK(!mBoxes.empty());
418
419    off_t offset = *--mBoxes.end();
420    mBoxes.erase(--mBoxes.end());
421
422    if (mWriteMoovBoxToMemory) {
423       int32_t x = htonl(mMoovBoxBufferOffset - offset);
424       memcpy(mMoovBoxBuffer + offset, &x, 4);
425    } else {
426        fseeko(mFile, offset, SEEK_SET);
427        writeInt32(mOffset - offset);
428        mOffset -= 4;
429        fseeko(mFile, mOffset, SEEK_SET);
430    }
431}
432
433void MPEG4Writer::writeInt8(int8_t x) {
434    write(&x, 1, 1, mFile);
435}
436
437void MPEG4Writer::writeInt16(int16_t x) {
438    x = htons(x);
439    write(&x, 1, 2, mFile);
440}
441
442void MPEG4Writer::writeInt32(int32_t x) {
443    x = htonl(x);
444    write(&x, 1, 4, mFile);
445}
446
447void MPEG4Writer::writeInt64(int64_t x) {
448    x = hton64(x);
449    write(&x, 1, 8, mFile);
450}
451
452void MPEG4Writer::writeCString(const char *s) {
453    size_t n = strlen(s);
454    write(s, 1, n + 1, mFile);
455}
456
457void MPEG4Writer::writeFourcc(const char *s) {
458    CHECK_EQ(strlen(s), 4);
459    write(s, 1, 4, mFile);
460}
461
462void MPEG4Writer::write(const void *data, size_t size) {
463    write(data, 1, size, mFile);
464}
465
466bool MPEG4Writer::exceedsFileSizeLimit() {
467    // No limit
468    if (mMaxFileSizeLimitBytes == 0) {
469        return false;
470    }
471
472    int64_t nTotalBytesEstimate = static_cast<int64_t>(mEstimatedMoovBoxSize);
473    for (List<Track *>::iterator it = mTracks.begin();
474         it != mTracks.end(); ++it) {
475        nTotalBytesEstimate += (*it)->getEstimatedTrackSizeBytes();
476    }
477    return (nTotalBytesEstimate >= mMaxFileSizeLimitBytes);
478}
479
480bool MPEG4Writer::exceedsFileDurationLimit() {
481    // No limit
482    if (mMaxFileDurationLimitUs == 0) {
483        return false;
484    }
485
486    for (List<Track *>::iterator it = mTracks.begin();
487         it != mTracks.end(); ++it) {
488        if ((*it)->getDurationUs() >= mMaxFileDurationLimitUs) {
489            return true;
490        }
491    }
492    return false;
493}
494
495bool MPEG4Writer::reachedEOS() {
496    bool allDone = true;
497    for (List<Track *>::iterator it = mTracks.begin();
498         it != mTracks.end(); ++it) {
499        if (!(*it)->reachedEOS()) {
500            allDone = false;
501            break;
502        }
503    }
504
505    return allDone;
506}
507
508void MPEG4Writer::setStartTimestamp(int64_t timeUs) {
509    LOGI("setStartTimestamp: %lld", timeUs);
510    Mutex::Autolock autoLock(mLock);
511    if (mStartTimestampUs != 0) {
512        return;  // Sorry, too late
513    }
514    mStartTimestampUs = timeUs;
515}
516
517int64_t MPEG4Writer::getStartTimestamp() {
518    LOGI("getStartTimestamp: %lld", mStartTimestampUs);
519    Mutex::Autolock autoLock(mLock);
520    return mStartTimestampUs;
521}
522
523////////////////////////////////////////////////////////////////////////////////
524
525MPEG4Writer::Track::Track(
526        MPEG4Writer *owner, const sp<MediaSource> &source)
527    : mOwner(owner),
528      mMeta(source->getFormat()),
529      mSource(source),
530      mDone(false),
531      mMaxTimeStampUs(0),
532      mEstimatedTrackSizeBytes(0),
533      mSamplesHaveSameSize(true),
534      mCodecSpecificData(NULL),
535      mCodecSpecificDataSize(0),
536      mGotAllCodecSpecificData(false),
537      mReachedEOS(false) {
538}
539
540MPEG4Writer::Track::~Track() {
541    stop();
542
543    if (mCodecSpecificData != NULL) {
544        free(mCodecSpecificData);
545        mCodecSpecificData = NULL;
546    }
547}
548
549status_t MPEG4Writer::Track::start() {
550    status_t err = mSource->start();
551
552    if (err != OK) {
553        mDone = mReachedEOS = true;
554        return err;
555    }
556
557    pthread_attr_t attr;
558    pthread_attr_init(&attr);
559    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
560
561    mDone = false;
562    mMaxTimeStampUs = 0;
563    mReachedEOS = false;
564    mEstimatedTrackSizeBytes = 0;
565
566    pthread_create(&mThread, &attr, ThreadWrapper, this);
567    pthread_attr_destroy(&attr);
568
569    return OK;
570}
571
572void MPEG4Writer::Track::stop() {
573    if (mDone) {
574        return;
575    }
576
577    mDone = true;
578
579    void *dummy;
580    pthread_join(mThread, &dummy);
581
582    mSource->stop();
583}
584
585bool MPEG4Writer::Track::reachedEOS() {
586    return mReachedEOS;
587}
588
589// static
590void *MPEG4Writer::Track::ThreadWrapper(void *me) {
591    Track *track = static_cast<Track *>(me);
592
593    track->threadEntry();
594
595    return NULL;
596}
597
598#include <ctype.h>
599static void hexdump(const void *_data, size_t size) {
600    const uint8_t *data = (const uint8_t *)_data;
601    size_t offset = 0;
602    while (offset < size) {
603        printf("0x%04x  ", offset);
604
605        size_t n = size - offset;
606        if (n > 16) {
607            n = 16;
608        }
609
610        for (size_t i = 0; i < 16; ++i) {
611            if (i == 8) {
612                printf(" ");
613            }
614
615            if (offset + i < size) {
616                printf("%02x ", data[offset + i]);
617            } else {
618                printf("   ");
619            }
620        }
621
622        printf(" ");
623
624        for (size_t i = 0; i < n; ++i) {
625            if (isprint(data[offset + i])) {
626                printf("%c", data[offset + i]);
627            } else {
628                printf(".");
629            }
630        }
631
632        printf("\n");
633
634        offset += 16;
635    }
636}
637
638
639status_t MPEG4Writer::Track::makeAVCCodecSpecificData(
640        const uint8_t *data, size_t size) {
641    // hexdump(data, size);
642
643    if (mCodecSpecificData != NULL) {
644        LOGE("Already have codec specific data");
645        return ERROR_MALFORMED;
646    }
647
648    if (size < 4 || memcmp("\x00\x00\x00\x01", data, 4)) {
649        LOGE("Must start with a start code");
650        return ERROR_MALFORMED;
651    }
652
653    size_t picParamOffset = 4;
654    while (picParamOffset + 3 < size
655            && memcmp("\x00\x00\x00\x01", &data[picParamOffset], 4)) {
656        ++picParamOffset;
657    }
658
659    if (picParamOffset + 3 >= size) {
660        LOGE("Could not find start-code for pictureParameterSet");
661        return ERROR_MALFORMED;
662    }
663
664    size_t seqParamSetLength = picParamOffset - 4;
665    size_t picParamSetLength = size - picParamOffset - 4;
666
667    mCodecSpecificDataSize =
668        6 + 1 + seqParamSetLength + 2 + picParamSetLength + 2;
669
670    mCodecSpecificData = malloc(mCodecSpecificDataSize);
671    uint8_t *header = (uint8_t *)mCodecSpecificData;
672    header[0] = 1;
673    header[1] = 0x42;  // profile
674    header[2] = 0x80;
675    header[3] = 0x1e;  // level
676
677#if USE_NALLEN_FOUR
678    header[4] = 0xfc | 3;  // length size == 4 bytes
679#else
680    header[4] = 0xfc | 1;  // length size == 2 bytes
681#endif
682
683    header[5] = 0xe0 | 1;
684    header[6] = seqParamSetLength >> 8;
685    header[7] = seqParamSetLength & 0xff;
686    memcpy(&header[8], &data[4], seqParamSetLength);
687    header += 8 + seqParamSetLength;
688    header[0] = 1;
689    header[1] = picParamSetLength >> 8;
690    header[2] = picParamSetLength & 0xff;
691    memcpy(&header[3], &data[picParamOffset + 4], picParamSetLength);
692
693    return OK;
694}
695
696void MPEG4Writer::Track::threadEntry() {
697    sp<MetaData> meta = mSource->getFormat();
698    const char *mime;
699    meta->findCString(kKeyMIMEType, &mime);
700    bool is_mpeg4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) ||
701                    !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC);
702    bool is_avc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
703    bool is_audio = !strncasecmp(mime, "audio/", 6);
704    int32_t count = 0;
705    const int64_t interleaveDurationUs = mOwner->interleaveDuration();
706    int64_t chunkTimestampUs = 0;
707    int32_t nChunks = 0;
708    int32_t nZeroLengthFrames = 0;
709    int64_t lastTimestamp = 0;  // Timestamp of the previous sample
710    int64_t lastDuration = 0;   // Time spacing between the previous two samples
711    int32_t sampleCount = 1;    // Sample count in the current stts table entry
712    uint32_t previousSampleSize = 0;  // Size of the previous sample
713    sp<MetaData> meta_data;
714
715    MediaBuffer *buffer;
716    while (!mDone && mSource->read(&buffer) == OK) {
717        if (buffer->range_length() == 0) {
718            buffer->release();
719            buffer = NULL;
720            ++nZeroLengthFrames;
721            continue;
722        }
723
724        ++count;
725
726        int32_t isCodecConfig;
727        if (buffer->meta_data()->findInt32(kKeyIsCodecConfig, &isCodecConfig)
728                && isCodecConfig) {
729            CHECK(!mGotAllCodecSpecificData);
730
731            if (is_avc) {
732                status_t err = makeAVCCodecSpecificData(
733                        (const uint8_t *)buffer->data()
734                            + buffer->range_offset(),
735                        buffer->range_length());
736                CHECK_EQ(OK, err);
737            } else if (is_mpeg4) {
738                mCodecSpecificDataSize = buffer->range_length();
739                mCodecSpecificData = malloc(mCodecSpecificDataSize);
740                memcpy(mCodecSpecificData,
741                        (const uint8_t *)buffer->data()
742                            + buffer->range_offset(),
743                       buffer->range_length());
744            }
745
746            buffer->release();
747            buffer = NULL;
748
749            mGotAllCodecSpecificData = true;
750            continue;
751        } else if (!mGotAllCodecSpecificData &&
752                count == 1 && is_mpeg4 && mCodecSpecificData == NULL) {
753            // The TI mpeg4 encoder does not properly set the
754            // codec-specific-data flag.
755
756            const uint8_t *data =
757                (const uint8_t *)buffer->data() + buffer->range_offset();
758
759            const size_t size = buffer->range_length();
760
761            size_t offset = 0;
762            while (offset + 3 < size) {
763                if (data[offset] == 0x00 && data[offset + 1] == 0x00
764                    && data[offset + 2] == 0x01 && data[offset + 3] == 0xb6) {
765                    break;
766                }
767
768                ++offset;
769            }
770
771            // CHECK(offset + 3 < size);
772            if (offset + 3 >= size) {
773                // XXX assume the entire first chunk of data is the codec specific
774                // data.
775                offset = size;
776            }
777
778            mCodecSpecificDataSize = offset;
779            mCodecSpecificData = malloc(offset);
780            memcpy(mCodecSpecificData, data, offset);
781
782            buffer->set_range(buffer->range_offset() + offset, size - offset);
783
784            if (size == offset) {
785                buffer->release();
786                buffer = NULL;
787
788                continue;
789            }
790
791            mGotAllCodecSpecificData = true;
792        } else if (!mGotAllCodecSpecificData && is_avc && count < 3) {
793            // The TI video encoder does not flag codec specific data
794            // as such and also splits up SPS and PPS across two buffers.
795
796            const uint8_t *data =
797                (const uint8_t *)buffer->data() + buffer->range_offset();
798
799            size_t size = buffer->range_length();
800
801            CHECK(count == 2 || mCodecSpecificData == NULL);
802
803            size_t offset = mCodecSpecificDataSize;
804            mCodecSpecificDataSize += size + 4;
805            mCodecSpecificData =
806                realloc(mCodecSpecificData, mCodecSpecificDataSize);
807
808            memcpy((uint8_t *)mCodecSpecificData + offset,
809                   "\x00\x00\x00\x01", 4);
810
811            memcpy((uint8_t *)mCodecSpecificData + offset + 4, data, size);
812
813            buffer->release();
814            buffer = NULL;
815
816            if (count == 2) {
817                void *tmp = mCodecSpecificData;
818                size = mCodecSpecificDataSize;
819                mCodecSpecificData = NULL;
820                mCodecSpecificDataSize = 0;
821
822                status_t err = makeAVCCodecSpecificData(
823                        (const uint8_t *)tmp, size);
824                free(tmp);
825                tmp = NULL;
826                CHECK_EQ(OK, err);
827
828                mGotAllCodecSpecificData = true;
829            }
830
831            continue;
832        }
833
834        // Make a deep copy of the MediaBuffer and Metadata and release
835        // the original as soon as we can
836        MediaBuffer *copy = new MediaBuffer(buffer->range_length());
837        memcpy(copy->data(), (uint8_t *)buffer->data() + buffer->range_offset(),
838                buffer->range_length());
839        copy->set_range(0, buffer->range_length());
840        meta_data = new MetaData(*buffer->meta_data().get());
841        buffer->release();
842        buffer = NULL;
843
844        if (is_avc) StripStartcode(copy);
845
846        SampleInfo info;
847        info.size = is_avc
848#if USE_NALLEN_FOUR
849                ? copy->range_length() + 4
850#else
851                ? copy->range_length() + 2
852#endif
853                : copy->range_length();
854
855        // Max file size or duration handling
856        mEstimatedTrackSizeBytes += info.size;
857        if (mOwner->exceedsFileSizeLimit()) {
858            mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED, 0);
859            break;
860        }
861        if (mOwner->exceedsFileDurationLimit()) {
862            mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_DURATION_REACHED, 0);
863            break;
864        }
865
866
867        int32_t isSync = false;
868        meta_data->findInt32(kKeyIsSyncFrame, &isSync);
869
870        int64_t timestampUs;
871        CHECK(meta_data->findInt64(kKeyTime, &timestampUs));
872
873////////////////////////////////////////////////////////////////////////////////
874        if (mSampleInfos.empty()) {
875            mOwner->setStartTimestamp(timestampUs);
876            mStartTimestampUs = (timestampUs - mOwner->getStartTimestamp());
877        }
878
879        if (timestampUs > mMaxTimeStampUs) {
880            mMaxTimeStampUs = timestampUs;
881        }
882
883        // Our timestamp is in ms.
884        info.timestamp = (timestampUs + 500) / 1000;
885        mSampleInfos.push_back(info);
886        if (mSampleInfos.size() > 2) {
887            if (lastDuration != info.timestamp - lastTimestamp) {
888                SttsTableEntry sttsEntry(sampleCount, lastDuration);
889                mSttsTableEntries.push_back(sttsEntry);
890                sampleCount = 1;
891            } else {
892                ++sampleCount;
893            }
894        }
895        if (mSamplesHaveSameSize) {
896            if (mSampleInfos.size() >= 2 && previousSampleSize != info.size) {
897                mSamplesHaveSameSize = false;
898            }
899            previousSampleSize = info.size;
900        }
901        lastDuration = info.timestamp - lastTimestamp;
902        lastTimestamp = info.timestamp;
903
904        if (isSync != 0) {
905            mStssTableEntries.push_back(mSampleInfos.size());
906        }
907
908
909        mChunkSamples.push_back(copy);
910        if (interleaveDurationUs == 0) {
911            StscTableEntry stscEntry(++nChunks, 1, 1);
912            mStscTableEntries.push_back(stscEntry);
913            writeOneChunk(is_avc);
914        } else {
915            if (chunkTimestampUs == 0) {
916                chunkTimestampUs = timestampUs;
917            } else {
918                if (timestampUs - chunkTimestampUs > interleaveDurationUs) {
919                    ++nChunks;
920                    mChunkDurations.push_back(timestampUs - chunkTimestampUs);
921                    if (nChunks == 1 ||  // First chunk
922                        (--(mStscTableEntries.end()))->samplesPerChunk !=
923                         mChunkSamples.size()) {
924                        StscTableEntry stscEntry(nChunks,
925                                mChunkSamples.size(), 1);
926                        mStscTableEntries.push_back(stscEntry);
927                    }
928                    writeOneChunk(is_avc);
929                    chunkTimestampUs = timestampUs;
930                }
931            }
932        }
933
934    }
935
936    if (mSampleInfos.empty()) {
937        mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_STOP_PREMATURELY, 0);
938    }
939
940    // Last chunk
941    if (!mChunkSamples.empty()) {
942        ++nChunks;
943        StscTableEntry stscEntry(nChunks, mChunkSamples.size(), 1);
944        mStscTableEntries.push_back(stscEntry);
945        writeOneChunk(is_avc);
946    }
947
948    // We don't really know how long the last frame lasts, since
949    // there is no frame time after it, just repeat the previous
950    // frame's duration.
951    if (mSampleInfos.size() == 1) {
952        lastDuration = 0;  // A single sample's duration
953    } else {
954        ++sampleCount;  // Count for the last sample
955    }
956    SttsTableEntry sttsEntry(sampleCount, lastDuration);
957    mSttsTableEntries.push_back(sttsEntry);
958    mReachedEOS = true;
959    LOGI("Received total/0-length (%d/%d) buffers and encoded %d frames - %s",
960            count, nZeroLengthFrames, mSampleInfos.size(), is_audio? "audio": "video");
961
962    logStatisticalData(is_audio);
963}
964
965void MPEG4Writer::Track::findMinMaxFrameRates(float *minFps, float *maxFps) {
966    int32_t minSampleDuration = 0x7FFFFFFF;
967    int32_t maxSampleDuration = 0;
968    for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin();
969        it != mSttsTableEntries.end(); ++it) {
970        int32_t sampleDuration = static_cast<int32_t>(it->sampleDuration);
971        if (sampleDuration > maxSampleDuration) {
972            maxSampleDuration = sampleDuration;
973        } else if (sampleDuration < minSampleDuration) {
974            minSampleDuration = sampleDuration;
975        }
976    }
977    CHECK(minSampleDuration != 0 && maxSampleDuration != 0);
978    *minFps = 1000.0 / maxSampleDuration;
979    *maxFps = 1000.0 / minSampleDuration;
980}
981
982// Don't count the last duration
983void MPEG4Writer::Track::findMinMaxChunkDurations(int64_t *min, int64_t *max) {
984    int64_t duration = mOwner->interleaveDuration();
985    int64_t minChunkDuration = duration;
986    int64_t maxChunkDuration = duration;
987    if (mChunkDurations.size() > 1) {
988        for (List<int64_t>::iterator it = mChunkDurations.begin();
989            it != --mChunkDurations.end(); ++it) {
990            if (minChunkDuration > (*it)) {
991                minChunkDuration = (*it);
992            } else if (maxChunkDuration < (*it)) {
993                maxChunkDuration = (*it);
994            }
995        }
996    }
997    *min = minChunkDuration;
998    *max = maxChunkDuration;
999}
1000
1001void MPEG4Writer::Track::logStatisticalData(bool isAudio) {
1002    if (mMaxTimeStampUs <= 0 || mSampleInfos.empty()) {
1003        LOGI("nothing is recorded");
1004        return;
1005    }
1006
1007    bool collectStats = false;
1008    char value[PROPERTY_VALUE_MAX];
1009    if (property_get("media.stagefright.record-stats", value, NULL)
1010        && (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
1011        collectStats = true;
1012    }
1013
1014    if (collectStats) {
1015        if (isAudio) {
1016            LOGI("audio track - duration %lld us", mMaxTimeStampUs);
1017        } else {
1018            float fps = (mSampleInfos.size() * 1000000.0) / mMaxTimeStampUs;
1019            float minFps;
1020            float maxFps;
1021            findMinMaxFrameRates(&minFps, &maxFps);
1022            LOGI("video track - duration %lld us", mMaxTimeStampUs);
1023            LOGI("min/avg/max frame rate (fps): %.2f/%.2f/%.2f",
1024                minFps, fps, maxFps);
1025        }
1026
1027        int64_t totalBytes = 0;
1028        for (List<SampleInfo>::iterator it = mSampleInfos.begin();
1029            it != mSampleInfos.end(); ++it) {
1030            totalBytes += it->size;
1031        }
1032        float bitRate = (totalBytes * 8000000.0) / mMaxTimeStampUs;
1033        LOGI("avg bit rate (bps): %.2f", bitRate);
1034
1035        int64_t duration = mOwner->interleaveDuration();
1036        if (duration != 0) {  // If interleaving is enabled
1037            int64_t minChunk, maxChunk;
1038            findMinMaxChunkDurations(&minChunk, &maxChunk);
1039            LOGI("min/avg/max chunk duration (ms): %lld/%lld/%lld",
1040                minChunk, duration, maxChunk);
1041        }
1042    }
1043}
1044
1045void MPEG4Writer::Track::writeOneChunk(bool isAvc) {
1046    mOwner->lock();
1047    for (List<MediaBuffer *>::iterator it = mChunkSamples.begin();
1048         it != mChunkSamples.end(); ++it) {
1049        off_t offset = isAvc? mOwner->addLengthPrefixedSample_l(*it)
1050                            : mOwner->addSample_l(*it);
1051        if (it == mChunkSamples.begin()) {
1052            mChunkOffsets.push_back(offset);
1053        }
1054    }
1055    mOwner->unlock();
1056    while (!mChunkSamples.empty()) {
1057        List<MediaBuffer *>::iterator it = mChunkSamples.begin();
1058        (*it)->release();
1059        (*it) = NULL;
1060        mChunkSamples.erase(it);
1061    }
1062    mChunkSamples.clear();
1063}
1064
1065int64_t MPEG4Writer::Track::getDurationUs() const {
1066    return mMaxTimeStampUs;
1067}
1068
1069int64_t MPEG4Writer::Track::getEstimatedTrackSizeBytes() const {
1070    return mEstimatedTrackSizeBytes;
1071}
1072
1073void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) {
1074    const char *mime;
1075    bool success = mMeta->findCString(kKeyMIMEType, &mime);
1076    CHECK(success);
1077
1078    bool is_audio = !strncasecmp(mime, "audio/", 6);
1079
1080    time_t now = time(NULL);
1081
1082    mOwner->beginBox("trak");
1083
1084      mOwner->beginBox("tkhd");
1085        mOwner->writeInt32(0);             // version=0, flags=0
1086        mOwner->writeInt32(now);           // creation time
1087        mOwner->writeInt32(now);           // modification time
1088        mOwner->writeInt32(trackID);
1089        mOwner->writeInt32(0);             // reserved
1090        mOwner->writeInt32(getDurationUs() / 1000);
1091        mOwner->writeInt32(0);             // reserved
1092        mOwner->writeInt32(0);             // reserved
1093        mOwner->writeInt16(0);             // layer
1094        mOwner->writeInt16(0);             // alternate group
1095        mOwner->writeInt16(is_audio ? 0x100 : 0);  // volume
1096        mOwner->writeInt16(0);             // reserved
1097
1098        mOwner->writeInt32(0x10000);       // matrix
1099        mOwner->writeInt32(0);
1100        mOwner->writeInt32(0);
1101        mOwner->writeInt32(0);
1102        mOwner->writeInt32(0x10000);
1103        mOwner->writeInt32(0);
1104        mOwner->writeInt32(0);
1105        mOwner->writeInt32(0);
1106        mOwner->writeInt32(0x40000000);
1107
1108        if (is_audio) {
1109            mOwner->writeInt32(0);
1110            mOwner->writeInt32(0);
1111        } else {
1112            int32_t width, height;
1113            bool success = mMeta->findInt32(kKeyWidth, &width);
1114            success = success && mMeta->findInt32(kKeyHeight, &height);
1115            CHECK(success);
1116
1117            mOwner->writeInt32(width << 16);   // 32-bit fixed-point value
1118            mOwner->writeInt32(height << 16);  // 32-bit fixed-point value
1119        }
1120      mOwner->endBox();  // tkhd
1121
1122      if (mStartTimestampUs != 0) {
1123        mOwner->beginBox("edts");
1124          mOwner->writeInt32(0);             // version=0, flags=0
1125          mOwner->beginBox("elst");
1126            mOwner->writeInt32(0);           // version=0, flags=0
1127            mOwner->writeInt32(1);           // a single entry
1128            mOwner->writeInt32(mStartTimestampUs / 1000);  // edit duration
1129            mOwner->writeInt32(-1);          // empty edit box to signal starting time offset
1130            mOwner->writeInt32(1);           // x1 rate
1131          mOwner->endBox();
1132        mOwner->endBox();
1133      }
1134
1135      mOwner->beginBox("mdia");
1136
1137        mOwner->beginBox("mdhd");
1138          mOwner->writeInt32(0);             // version=0, flags=0
1139          mOwner->writeInt32(now);           // creation time
1140          mOwner->writeInt32(now);           // modification time
1141          mOwner->writeInt32(1000);          // timescale
1142          mOwner->writeInt32(getDurationUs() / 1000);
1143          mOwner->writeInt16(0);             // language code XXX
1144          mOwner->writeInt16(0);             // predefined
1145        mOwner->endBox();
1146
1147        mOwner->beginBox("hdlr");
1148          mOwner->writeInt32(0);             // version=0, flags=0
1149          mOwner->writeInt32(0);             // component type: should be mhlr
1150          mOwner->writeFourcc(is_audio ? "soun" : "vide");  // component subtype
1151          mOwner->writeInt32(0);             // reserved
1152          mOwner->writeInt32(0);             // reserved
1153          mOwner->writeInt32(0);             // reserved
1154          mOwner->writeCString(is_audio ? "SoundHandler": "");  // name
1155        mOwner->endBox();
1156
1157        mOwner->beginBox("minf");
1158          if (is_audio) {
1159              mOwner->beginBox("smhd");
1160              mOwner->writeInt32(0);           // version=0, flags=0
1161              mOwner->writeInt16(0);           // balance
1162              mOwner->writeInt16(0);           // reserved
1163              mOwner->endBox();
1164          } else {
1165              mOwner->beginBox("vmhd");
1166              mOwner->writeInt32(0x00000001);  // version=0, flags=1
1167              mOwner->writeInt16(0);           // graphics mode
1168              mOwner->writeInt16(0);           // opcolor
1169              mOwner->writeInt16(0);
1170              mOwner->writeInt16(0);
1171              mOwner->endBox();
1172          }
1173
1174          mOwner->beginBox("dinf");
1175            mOwner->beginBox("dref");
1176              mOwner->writeInt32(0);  // version=0, flags=0
1177              mOwner->writeInt32(1);
1178              mOwner->beginBox("url ");
1179                mOwner->writeInt32(1);  // version=0, flags=1
1180              mOwner->endBox();  // url
1181            mOwner->endBox();  // dref
1182          mOwner->endBox();  // dinf
1183
1184       mOwner->endBox();  // minf
1185
1186        mOwner->beginBox("stbl");
1187
1188          mOwner->beginBox("stsd");
1189            mOwner->writeInt32(0);               // version=0, flags=0
1190            mOwner->writeInt32(1);               // entry count
1191            if (is_audio) {
1192                const char *fourcc = NULL;
1193                if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime)) {
1194                    fourcc = "samr";
1195                } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) {
1196                    fourcc = "sawb";
1197                } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) {
1198                    fourcc = "mp4a";
1199                } else {
1200                    LOGE("Unknown mime type '%s'.", mime);
1201                    CHECK(!"should not be here, unknown mime type.");
1202                }
1203
1204                mOwner->beginBox(fourcc);          // audio format
1205                  mOwner->writeInt32(0);           // reserved
1206                  mOwner->writeInt16(0);           // reserved
1207                  mOwner->writeInt16(0x1);         // data ref index
1208                  mOwner->writeInt32(0);           // reserved
1209                  mOwner->writeInt32(0);           // reserved
1210                  int32_t nChannels;
1211                  CHECK_EQ(true, mMeta->findInt32(kKeyChannelCount, &nChannels));
1212                  mOwner->writeInt16(nChannels);   // channel count
1213                  mOwner->writeInt16(16);          // sample size
1214                  mOwner->writeInt16(0);           // predefined
1215                  mOwner->writeInt16(0);           // reserved
1216
1217                  int32_t samplerate;
1218                  bool success = mMeta->findInt32(kKeySampleRate, &samplerate);
1219                  CHECK(success);
1220
1221                  mOwner->writeInt32(samplerate << 16);
1222                  if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) {
1223                    mOwner->beginBox("esds");
1224
1225                        mOwner->writeInt32(0);     // version=0, flags=0
1226                        mOwner->writeInt8(0x03);   // ES_DescrTag
1227                        mOwner->writeInt8(23 + mCodecSpecificDataSize);
1228                        mOwner->writeInt16(0x0000);// ES_ID
1229                        mOwner->writeInt8(0x00);
1230
1231                        mOwner->writeInt8(0x04);   // DecoderConfigDescrTag
1232                        mOwner->writeInt8(15 + mCodecSpecificDataSize);
1233                        mOwner->writeInt8(0x40);   // objectTypeIndication ISO/IEC 14492-2
1234                        mOwner->writeInt8(0x15);   // streamType AudioStream
1235
1236                        mOwner->writeInt16(0x03);  // XXX
1237                        mOwner->writeInt8(0x00);   // buffer size 24-bit
1238                        mOwner->writeInt32(96000); // max bit rate
1239                        mOwner->writeInt32(96000); // avg bit rate
1240
1241                        mOwner->writeInt8(0x05);   // DecoderSpecificInfoTag
1242                        mOwner->writeInt8(mCodecSpecificDataSize);
1243                        mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
1244
1245                        static const uint8_t kData2[] = {
1246                            0x06,  // SLConfigDescriptorTag
1247                            0x01,
1248                            0x02
1249                        };
1250                        mOwner->write(kData2, sizeof(kData2));
1251
1252                    mOwner->endBox();  // esds
1253                  }
1254                mOwner->endBox();
1255            } else {
1256                if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
1257                    mOwner->beginBox("mp4v");
1258                } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
1259                    mOwner->beginBox("s263");
1260                } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
1261                    mOwner->beginBox("avc1");
1262                } else {
1263                    LOGE("Unknown mime type '%s'.", mime);
1264                    CHECK(!"should not be here, unknown mime type.");
1265                }
1266
1267                  mOwner->writeInt32(0);           // reserved
1268                  mOwner->writeInt16(0);           // reserved
1269                  mOwner->writeInt16(0);           // data ref index
1270                  mOwner->writeInt16(0);           // predefined
1271                  mOwner->writeInt16(0);           // reserved
1272                  mOwner->writeInt32(0);           // predefined
1273                  mOwner->writeInt32(0);           // predefined
1274                  mOwner->writeInt32(0);           // predefined
1275
1276                  int32_t width, height;
1277                  bool success = mMeta->findInt32(kKeyWidth, &width);
1278                  success = success && mMeta->findInt32(kKeyHeight, &height);
1279                  CHECK(success);
1280
1281                  mOwner->writeInt16(width);
1282                  mOwner->writeInt16(height);
1283                  mOwner->writeInt32(0x480000);    // horiz resolution
1284                  mOwner->writeInt32(0x480000);    // vert resolution
1285                  mOwner->writeInt32(0);           // reserved
1286                  mOwner->writeInt16(1);           // frame count
1287                  mOwner->write("                                ", 32);
1288                  mOwner->writeInt16(0x18);        // depth
1289                  mOwner->writeInt16(-1);          // predefined
1290
1291                  CHECK(23 + mCodecSpecificDataSize < 128);
1292
1293                  if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
1294                      mOwner->beginBox("esds");
1295
1296                        mOwner->writeInt32(0);           // version=0, flags=0
1297
1298                        mOwner->writeInt8(0x03);  // ES_DescrTag
1299                        mOwner->writeInt8(23 + mCodecSpecificDataSize);
1300                        mOwner->writeInt16(0x0000);  // ES_ID
1301                        mOwner->writeInt8(0x1f);
1302
1303                        mOwner->writeInt8(0x04);  // DecoderConfigDescrTag
1304                        mOwner->writeInt8(15 + mCodecSpecificDataSize);
1305                        mOwner->writeInt8(0x20);  // objectTypeIndication ISO/IEC 14492-2
1306                        mOwner->writeInt8(0x11);  // streamType VisualStream
1307
1308                        static const uint8_t kData[] = {
1309                            0x01, 0x77, 0x00,
1310                            0x00, 0x03, 0xe8, 0x00,
1311                            0x00, 0x03, 0xe8, 0x00
1312                        };
1313                        mOwner->write(kData, sizeof(kData));
1314
1315                        mOwner->writeInt8(0x05);  // DecoderSpecificInfoTag
1316
1317                        mOwner->writeInt8(mCodecSpecificDataSize);
1318                        mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
1319
1320                        static const uint8_t kData2[] = {
1321                            0x06,  // SLConfigDescriptorTag
1322                            0x01,
1323                            0x02
1324                        };
1325                        mOwner->write(kData2, sizeof(kData2));
1326
1327                      mOwner->endBox();  // esds
1328                  } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
1329                      mOwner->beginBox("d263");
1330
1331                          mOwner->writeInt32(0);  // vendor
1332                          mOwner->writeInt8(0);   // decoder version
1333                          mOwner->writeInt8(10);  // level: 10
1334                          mOwner->writeInt8(0);   // profile: 0
1335
1336                      mOwner->endBox();  // d263
1337                  } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
1338                      mOwner->beginBox("avcC");
1339                        mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
1340                      mOwner->endBox();  // avcC
1341                  }
1342
1343                mOwner->endBox();  // mp4v, s263 or avc1
1344            }
1345          mOwner->endBox();  // stsd
1346
1347          mOwner->beginBox("stts");
1348            mOwner->writeInt32(0);  // version=0, flags=0
1349            mOwner->writeInt32(mSttsTableEntries.size());
1350            for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin();
1351                 it != mSttsTableEntries.end(); ++it) {
1352                mOwner->writeInt32(it->sampleCount);
1353                mOwner->writeInt32(it->sampleDuration);
1354            }
1355          mOwner->endBox();  // stts
1356
1357          if (!is_audio) {
1358            mOwner->beginBox("stss");
1359              mOwner->writeInt32(0);  // version=0, flags=0
1360              mOwner->writeInt32(mStssTableEntries.size());  // number of sync frames
1361              for (List<int32_t>::iterator it = mStssTableEntries.begin();
1362                   it != mStssTableEntries.end(); ++it) {
1363                  mOwner->writeInt32(*it);
1364              }
1365            mOwner->endBox();  // stss
1366          }
1367
1368          mOwner->beginBox("stsz");
1369            mOwner->writeInt32(0);  // version=0, flags=0
1370            if (mSamplesHaveSameSize) {
1371                List<SampleInfo>::iterator it = mSampleInfos.begin();
1372                mOwner->writeInt32(it->size);  // default sample size
1373            } else {
1374                mOwner->writeInt32(0);
1375            }
1376            mOwner->writeInt32(mSampleInfos.size());
1377            if (!mSamplesHaveSameSize) {
1378                for (List<SampleInfo>::iterator it = mSampleInfos.begin();
1379                     it != mSampleInfos.end(); ++it) {
1380                    mOwner->writeInt32((*it).size);
1381                }
1382            }
1383          mOwner->endBox();  // stsz
1384
1385          mOwner->beginBox("stsc");
1386            mOwner->writeInt32(0);  // version=0, flags=0
1387            mOwner->writeInt32(mStscTableEntries.size());
1388            for (List<StscTableEntry>::iterator it = mStscTableEntries.begin();
1389                 it != mStscTableEntries.end(); ++it) {
1390                mOwner->writeInt32(it->firstChunk);
1391                mOwner->writeInt32(it->samplesPerChunk);
1392                mOwner->writeInt32(it->sampleDescriptionId);
1393            }
1394          mOwner->endBox();  // stsc
1395
1396          mOwner->beginBox("co64");
1397            mOwner->writeInt32(0);  // version=0, flags=0
1398            mOwner->writeInt32(mChunkOffsets.size());
1399            for (List<off_t>::iterator it = mChunkOffsets.begin();
1400                 it != mChunkOffsets.end(); ++it) {
1401                mOwner->writeInt64((*it));
1402            }
1403          mOwner->endBox();  // co64
1404
1405        mOwner->endBox();  // stbl
1406      mOwner->endBox();  // mdia
1407    mOwner->endBox();  // trak
1408}
1409
1410}  // namespace android
1411