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