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#include <arpa/inet.h>
18
19#include <ctype.h>
20#include <pthread.h>
21
22#include <media/stagefright/MPEG4Writer.h>
23#include <media/stagefright/MediaBuffer.h>
24#include <media/stagefright/MetaData.h>
25#include <media/stagefright/MediaDebug.h>
26#include <media/stagefright/MediaDefs.h>
27#include <media/stagefright/MediaSource.h>
28#include <media/stagefright/Utils.h>
29
30namespace android {
31
32class MPEG4Writer::Track {
33public:
34    Track(MPEG4Writer *owner, const sp<MediaSource> &source);
35    ~Track();
36
37    status_t start();
38    void stop();
39    bool reachedEOS();
40
41    int64_t getDurationUs() const;
42    void writeTrackHeader(int32_t trackID);
43
44private:
45    MPEG4Writer *mOwner;
46    sp<MetaData> mMeta;
47    sp<MediaSource> mSource;
48    volatile bool mDone;
49    int64_t mMaxTimeStampUs;
50
51    pthread_t mThread;
52
53    struct SampleInfo {
54        size_t size;
55        off_t offset;
56        int64_t timestamp;
57    };
58    List<SampleInfo> mSampleInfos;
59
60    void *mCodecSpecificData;
61    size_t mCodecSpecificDataSize;
62
63    bool mReachedEOS;
64
65    static void *ThreadWrapper(void *me);
66    void threadEntry();
67
68    Track(const Track &);
69    Track &operator=(const Track &);
70};
71
72MPEG4Writer::MPEG4Writer(const char *filename)
73    : mFile(fopen(filename, "wb")),
74      mOffset(0),
75      mMdatOffset(0) {
76    CHECK(mFile != NULL);
77}
78
79MPEG4Writer::MPEG4Writer(int fd)
80    : mFile(fdopen(fd, "wb")),
81      mOffset(0),
82      mMdatOffset(0) {
83    CHECK(mFile != NULL);
84}
85
86MPEG4Writer::~MPEG4Writer() {
87    stop();
88
89    for (List<Track *>::iterator it = mTracks.begin();
90         it != mTracks.end(); ++it) {
91        delete *it;
92    }
93    mTracks.clear();
94}
95
96status_t MPEG4Writer::addSource(const sp<MediaSource> &source) {
97    Track *track = new Track(this, source);
98    mTracks.push_back(track);
99
100    return OK;
101}
102
103status_t MPEG4Writer::start() {
104    if (mFile == NULL) {
105        return UNKNOWN_ERROR;
106    }
107
108    beginBox("ftyp");
109      writeFourcc("isom");
110      writeInt32(0);
111      writeFourcc("isom");
112    endBox();
113
114    mMdatOffset = mOffset;
115    write("\x00\x00\x00\x01mdat????????", 16);
116
117    for (List<Track *>::iterator it = mTracks.begin();
118         it != mTracks.end(); ++it) {
119        status_t err = (*it)->start();
120
121        if (err != OK) {
122            for (List<Track *>::iterator it2 = mTracks.begin();
123                 it2 != it; ++it2) {
124                (*it2)->stop();
125            }
126
127            return err;
128        }
129    }
130
131    return OK;
132}
133
134void MPEG4Writer::stop() {
135    if (mFile == NULL) {
136        return;
137    }
138
139    int64_t max_duration = 0;
140    for (List<Track *>::iterator it = mTracks.begin();
141         it != mTracks.end(); ++it) {
142        (*it)->stop();
143
144        int64_t duration = (*it)->getDurationUs();
145        if (duration > max_duration) {
146            max_duration = duration;
147        }
148    }
149
150    // Fix up the size of the 'mdat' chunk.
151    fseek(mFile, mMdatOffset + 8, SEEK_SET);
152    int64_t size = mOffset - mMdatOffset;
153    size = hton64(size);
154    fwrite(&size, 1, 8, mFile);
155    fseek(mFile, mOffset, SEEK_SET);
156
157    time_t now = time(NULL);
158
159    beginBox("moov");
160
161      beginBox("mvhd");
162        writeInt32(0);             // version=0, flags=0
163        writeInt32(now);           // creation time
164        writeInt32(now);           // modification time
165        writeInt32(1000);          // timescale
166        writeInt32(max_duration / 1000);
167        writeInt32(0x10000);       // rate
168        writeInt16(0x100);         // volume
169        writeInt16(0);             // reserved
170        writeInt32(0);             // reserved
171        writeInt32(0);             // reserved
172        writeInt32(0x10000);       // matrix
173        writeInt32(0);
174        writeInt32(0);
175        writeInt32(0);
176        writeInt32(0x10000);
177        writeInt32(0);
178        writeInt32(0);
179        writeInt32(0);
180        writeInt32(0x40000000);
181        writeInt32(0);             // predefined
182        writeInt32(0);             // predefined
183        writeInt32(0);             // predefined
184        writeInt32(0);             // predefined
185        writeInt32(0);             // predefined
186        writeInt32(0);             // predefined
187        writeInt32(mTracks.size() + 1);  // nextTrackID
188      endBox();  // mvhd
189
190      int32_t id = 1;
191      for (List<Track *>::iterator it = mTracks.begin();
192           it != mTracks.end(); ++it, ++id) {
193          (*it)->writeTrackHeader(id);
194      }
195    endBox();  // moov
196
197    CHECK(mBoxes.empty());
198
199    fclose(mFile);
200    mFile = NULL;
201}
202
203off_t MPEG4Writer::addSample(MediaBuffer *buffer) {
204    Mutex::Autolock autoLock(mLock);
205
206    off_t old_offset = mOffset;
207
208    fwrite((const uint8_t *)buffer->data() + buffer->range_offset(),
209           1, buffer->range_length(), mFile);
210
211    mOffset += buffer->range_length();
212
213    return old_offset;
214}
215
216off_t MPEG4Writer::addLengthPrefixedSample(MediaBuffer *buffer) {
217    Mutex::Autolock autoLock(mLock);
218
219    off_t old_offset = mOffset;
220
221    size_t length = buffer->range_length();
222    CHECK(length < 65536);
223
224    uint8_t x = length >> 8;
225    fwrite(&x, 1, 1, mFile);
226    x = length & 0xff;
227    fwrite(&x, 1, 1, mFile);
228
229    fwrite((const uint8_t *)buffer->data() + buffer->range_offset(),
230           1, length, mFile);
231
232    mOffset += length + 2;
233
234    return old_offset;
235}
236
237void MPEG4Writer::beginBox(const char *fourcc) {
238    CHECK_EQ(strlen(fourcc), 4);
239
240    mBoxes.push_back(mOffset);
241
242    writeInt32(0);
243    writeFourcc(fourcc);
244}
245
246void MPEG4Writer::endBox() {
247    CHECK(!mBoxes.empty());
248
249    off_t offset = *--mBoxes.end();
250    mBoxes.erase(--mBoxes.end());
251
252    fseek(mFile, offset, SEEK_SET);
253    writeInt32(mOffset - offset);
254    mOffset -= 4;
255    fseek(mFile, mOffset, SEEK_SET);
256}
257
258void MPEG4Writer::writeInt8(int8_t x) {
259    fwrite(&x, 1, 1, mFile);
260    ++mOffset;
261}
262
263void MPEG4Writer::writeInt16(int16_t x) {
264    x = htons(x);
265    fwrite(&x, 1, 2, mFile);
266    mOffset += 2;
267}
268
269void MPEG4Writer::writeInt32(int32_t x) {
270    x = htonl(x);
271    fwrite(&x, 1, 4, mFile);
272    mOffset += 4;
273}
274
275void MPEG4Writer::writeInt64(int64_t x) {
276    x = hton64(x);
277    fwrite(&x, 1, 8, mFile);
278    mOffset += 8;
279}
280
281void MPEG4Writer::writeCString(const char *s) {
282    size_t n = strlen(s);
283
284    fwrite(s, 1, n + 1, mFile);
285    mOffset += n + 1;
286}
287
288void MPEG4Writer::writeFourcc(const char *s) {
289    CHECK_EQ(strlen(s), 4);
290    fwrite(s, 1, 4, mFile);
291    mOffset += 4;
292}
293
294void MPEG4Writer::write(const void *data, size_t size) {
295    fwrite(data, 1, size, mFile);
296    mOffset += size;
297}
298
299bool MPEG4Writer::reachedEOS() {
300    bool allDone = true;
301    for (List<Track *>::iterator it = mTracks.begin();
302         it != mTracks.end(); ++it) {
303        if (!(*it)->reachedEOS()) {
304            allDone = false;
305            break;
306        }
307    }
308
309    return allDone;
310}
311
312////////////////////////////////////////////////////////////////////////////////
313
314MPEG4Writer::Track::Track(
315        MPEG4Writer *owner, const sp<MediaSource> &source)
316    : mOwner(owner),
317      mMeta(source->getFormat()),
318      mSource(source),
319      mDone(false),
320      mMaxTimeStampUs(0),
321      mCodecSpecificData(NULL),
322      mCodecSpecificDataSize(0),
323      mReachedEOS(false) {
324}
325
326MPEG4Writer::Track::~Track() {
327    stop();
328
329    if (mCodecSpecificData != NULL) {
330        free(mCodecSpecificData);
331        mCodecSpecificData = NULL;
332    }
333}
334
335status_t MPEG4Writer::Track::start() {
336    status_t err = mSource->start();
337
338    if (err != OK) {
339        mDone = mReachedEOS = true;
340        return err;
341    }
342
343    pthread_attr_t attr;
344    pthread_attr_init(&attr);
345    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
346
347    mDone = false;
348    mMaxTimeStampUs = 0;
349    mReachedEOS = false;
350
351    pthread_create(&mThread, &attr, ThreadWrapper, this);
352    pthread_attr_destroy(&attr);
353
354    return OK;
355}
356
357void MPEG4Writer::Track::stop() {
358    if (mDone) {
359        return;
360    }
361
362    mDone = true;
363
364    void *dummy;
365    pthread_join(mThread, &dummy);
366
367    mSource->stop();
368}
369
370bool MPEG4Writer::Track::reachedEOS() {
371    return mReachedEOS;
372}
373
374// static
375void *MPEG4Writer::Track::ThreadWrapper(void *me) {
376    Track *track = static_cast<Track *>(me);
377
378    track->threadEntry();
379
380    return NULL;
381}
382
383void MPEG4Writer::Track::threadEntry() {
384    sp<MetaData> meta = mSource->getFormat();
385    const char *mime;
386    meta->findCString(kKeyMIMEType, &mime);
387    bool is_mpeg4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4);
388    bool is_avc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
389    int32_t count = 0;
390
391    MediaBuffer *buffer;
392    while (!mDone && mSource->read(&buffer) == OK) {
393        if (buffer->range_length() == 0) {
394            buffer->release();
395            buffer = NULL;
396
397            continue;
398        }
399
400        ++count;
401
402        if (is_avc && count < 3) {
403            size_t size = buffer->range_length();
404
405            switch (count) {
406                case 1:
407                {
408                    CHECK_EQ(mCodecSpecificData, NULL);
409                    mCodecSpecificData = malloc(size + 8);
410                    uint8_t *header = (uint8_t *)mCodecSpecificData;
411                    header[0] = 1;
412                    header[1] = 0x42;  // profile
413                    header[2] = 0x80;
414                    header[3] = 0x1e;  // level
415                    header[4] = 0xfc | 3;
416                    header[5] = 0xe0 | 1;
417                    header[6] = size >> 8;
418                    header[7] = size & 0xff;
419                    memcpy(&header[8],
420                            (const uint8_t *)buffer->data() + buffer->range_offset(),
421                            size);
422
423                    mCodecSpecificDataSize = size + 8;
424                    break;
425                }
426
427                case 2:
428                {
429                    size_t offset = mCodecSpecificDataSize;
430                    mCodecSpecificDataSize += size + 3;
431                    mCodecSpecificData = realloc(mCodecSpecificData, mCodecSpecificDataSize);
432                    uint8_t *header = (uint8_t *)mCodecSpecificData;
433                    header[offset] = 1;
434                    header[offset + 1] = size >> 8;
435                    header[offset + 2] = size & 0xff;
436                    memcpy(&header[offset + 3],
437                            (const uint8_t *)buffer->data() + buffer->range_offset(),
438                            size);
439                    break;
440                }
441            }
442
443            buffer->release();
444            buffer = NULL;
445
446            continue;
447        }
448
449        if (mCodecSpecificData == NULL && is_mpeg4) {
450            const uint8_t *data =
451                (const uint8_t *)buffer->data() + buffer->range_offset();
452
453            const size_t size = buffer->range_length();
454
455            size_t offset = 0;
456            while (offset + 3 < size) {
457                if (data[offset] == 0x00 && data[offset + 1] == 0x00
458                    && data[offset + 2] == 0x01 && data[offset + 3] == 0xb6) {
459                    break;
460                }
461
462                ++offset;
463            }
464
465            // CHECK(offset + 3 < size);
466            if (offset + 3 >= size) {
467                // XXX assume the entire first chunk of data is the codec specific
468                // data.
469                offset = size;
470            }
471
472            mCodecSpecificDataSize = offset;
473            mCodecSpecificData = malloc(offset);
474            memcpy(mCodecSpecificData, data, offset);
475
476            buffer->set_range(buffer->range_offset() + offset, size - offset);
477        }
478
479        off_t offset = is_avc ? mOwner->addLengthPrefixedSample(buffer)
480                              : mOwner->addSample(buffer);
481
482        SampleInfo info;
483        info.size = is_avc ? buffer->range_length() + 2 : buffer->range_length();
484        info.offset = offset;
485
486        int64_t timestampUs;
487        CHECK(buffer->meta_data()->findInt64(kKeyTime, &timestampUs));
488
489        if (timestampUs > mMaxTimeStampUs) {
490            mMaxTimeStampUs = timestampUs;
491        }
492
493        // Our timestamp is in ms.
494        info.timestamp = (timestampUs + 500) / 1000;
495
496        mSampleInfos.push_back(info);
497
498        buffer->release();
499        buffer = NULL;
500    }
501
502    mReachedEOS = true;
503}
504
505int64_t MPEG4Writer::Track::getDurationUs() const {
506    return mMaxTimeStampUs;
507}
508
509void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) {
510    const char *mime;
511    bool success = mMeta->findCString(kKeyMIMEType, &mime);
512    CHECK(success);
513
514    bool is_audio = !strncasecmp(mime, "audio/", 6);
515
516    time_t now = time(NULL);
517
518    mOwner->beginBox("trak");
519
520      mOwner->beginBox("tkhd");
521        mOwner->writeInt32(0);             // version=0, flags=0
522        mOwner->writeInt32(now);           // creation time
523        mOwner->writeInt32(now);           // modification time
524        mOwner->writeInt32(trackID);
525        mOwner->writeInt32(0);             // reserved
526        mOwner->writeInt32(getDurationUs() / 1000);
527        mOwner->writeInt32(0);             // reserved
528        mOwner->writeInt32(0);             // reserved
529        mOwner->writeInt16(0);             // layer
530        mOwner->writeInt16(0);             // alternate group
531        mOwner->writeInt16(is_audio ? 0x100 : 0);  // volume
532        mOwner->writeInt16(0);             // reserved
533
534        mOwner->writeInt32(0x10000);       // matrix
535        mOwner->writeInt32(0);
536        mOwner->writeInt32(0);
537        mOwner->writeInt32(0);
538        mOwner->writeInt32(0x10000);
539        mOwner->writeInt32(0);
540        mOwner->writeInt32(0);
541        mOwner->writeInt32(0);
542        mOwner->writeInt32(0x40000000);
543
544        if (is_audio) {
545            mOwner->writeInt32(0);
546            mOwner->writeInt32(0);
547        } else {
548            int32_t width, height;
549            bool success = mMeta->findInt32(kKeyWidth, &width);
550            success = success && mMeta->findInt32(kKeyHeight, &height);
551            CHECK(success);
552
553            mOwner->writeInt32(width);
554            mOwner->writeInt32(height);
555        }
556      mOwner->endBox();  // tkhd
557
558      mOwner->beginBox("mdia");
559
560        mOwner->beginBox("mdhd");
561          mOwner->writeInt32(0);             // version=0, flags=0
562          mOwner->writeInt32(now);           // creation time
563          mOwner->writeInt32(now);           // modification time
564          mOwner->writeInt32(1000);          // timescale
565          mOwner->writeInt32(getDurationUs() / 1000);
566          mOwner->writeInt16(0);             // language code XXX
567          mOwner->writeInt16(0);             // predefined
568        mOwner->endBox();
569
570        mOwner->beginBox("hdlr");
571          mOwner->writeInt32(0);             // version=0, flags=0
572          mOwner->writeInt32(0);             // predefined
573          mOwner->writeFourcc(is_audio ? "soun" : "vide");
574          mOwner->writeInt32(0);             // reserved
575          mOwner->writeInt32(0);             // reserved
576          mOwner->writeInt32(0);             // reserved
577          mOwner->writeCString("");          // name
578        mOwner->endBox();
579
580        mOwner->beginBox("minf");
581
582          mOwner->beginBox("dinf");
583            mOwner->beginBox("dref");
584              mOwner->writeInt32(0);  // version=0, flags=0
585              mOwner->writeInt32(1);
586              mOwner->beginBox("url ");
587                mOwner->writeInt32(1);  // version=0, flags=1
588              mOwner->endBox();  // url
589            mOwner->endBox();  // dref
590          mOwner->endBox();  // dinf
591
592          if (is_audio) {
593              mOwner->beginBox("smhd");
594              mOwner->writeInt32(0);           // version=0, flags=0
595              mOwner->writeInt16(0);           // balance
596              mOwner->writeInt16(0);           // reserved
597              mOwner->endBox();
598          } else {
599              mOwner->beginBox("vmhd");
600              mOwner->writeInt32(0x00000001);  // version=0, flags=1
601              mOwner->writeInt16(0);           // graphics mode
602              mOwner->writeInt16(0);           // opcolor
603              mOwner->writeInt16(0);
604              mOwner->writeInt16(0);
605              mOwner->endBox();
606          }
607        mOwner->endBox();  // minf
608
609        mOwner->beginBox("stbl");
610
611          mOwner->beginBox("stsd");
612            mOwner->writeInt32(0);               // version=0, flags=0
613            mOwner->writeInt32(1);               // entry count
614            if (is_audio) {
615                const char *fourcc = NULL;
616                if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime)) {
617                    fourcc = "samr";
618                } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) {
619                    fourcc = "sawb";
620                } else {
621                    LOGE("Unknown mime type '%s'.", mime);
622                    CHECK(!"should not be here, unknown mime type.");
623                }
624
625                mOwner->beginBox(fourcc);          // audio format
626                  mOwner->writeInt32(0);           // reserved
627                  mOwner->writeInt16(0);           // reserved
628                  mOwner->writeInt16(0);           // data ref index
629                  mOwner->writeInt32(0);           // reserved
630                  mOwner->writeInt32(0);           // reserved
631                  mOwner->writeInt16(2);           // channel count
632                  mOwner->writeInt16(16);          // sample size
633                  mOwner->writeInt16(0);           // predefined
634                  mOwner->writeInt16(0);           // reserved
635
636                  int32_t samplerate;
637                  bool success = mMeta->findInt32(kKeySampleRate, &samplerate);
638                  CHECK(success);
639
640                  mOwner->writeInt32(samplerate << 16);
641                mOwner->endBox();
642            } else {
643                if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
644                    mOwner->beginBox("mp4v");
645                } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
646                    mOwner->beginBox("s263");
647                } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
648                    mOwner->beginBox("avc1");
649                } else {
650                    LOGE("Unknown mime type '%s'.", mime);
651                    CHECK(!"should not be here, unknown mime type.");
652                }
653
654                  mOwner->writeInt32(0);           // reserved
655                  mOwner->writeInt16(0);           // reserved
656                  mOwner->writeInt16(0);           // data ref index
657                  mOwner->writeInt16(0);           // predefined
658                  mOwner->writeInt16(0);           // reserved
659                  mOwner->writeInt32(0);           // predefined
660                  mOwner->writeInt32(0);           // predefined
661                  mOwner->writeInt32(0);           // predefined
662
663                  int32_t width, height;
664                  bool success = mMeta->findInt32(kKeyWidth, &width);
665                  success = success && mMeta->findInt32(kKeyHeight, &height);
666                  CHECK(success);
667
668                  mOwner->writeInt16(width);
669                  mOwner->writeInt16(height);
670                  mOwner->writeInt32(0x480000);    // horiz resolution
671                  mOwner->writeInt32(0x480000);    // vert resolution
672                  mOwner->writeInt32(0);           // reserved
673                  mOwner->writeInt16(1);           // frame count
674                  mOwner->write("                                ", 32);
675                  mOwner->writeInt16(0x18);        // depth
676                  mOwner->writeInt16(-1);          // predefined
677
678                  CHECK(23 + mCodecSpecificDataSize < 128);
679
680                  if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
681                      mOwner->beginBox("esds");
682
683                        mOwner->writeInt32(0);           // version=0, flags=0
684
685                        mOwner->writeInt8(0x03);  // ES_DescrTag
686                        mOwner->writeInt8(23 + mCodecSpecificDataSize);
687                        mOwner->writeInt16(0x0000);  // ES_ID
688                        mOwner->writeInt8(0x1f);
689
690                        mOwner->writeInt8(0x04);  // DecoderConfigDescrTag
691                        mOwner->writeInt8(15 + mCodecSpecificDataSize);
692                        mOwner->writeInt8(0x20);  // objectTypeIndication ISO/IEC 14492-2
693                        mOwner->writeInt8(0x11);  // streamType VisualStream
694
695                        static const uint8_t kData[] = {
696                            0x01, 0x77, 0x00,
697                            0x00, 0x03, 0xe8, 0x00,
698                            0x00, 0x03, 0xe8, 0x00
699                        };
700                        mOwner->write(kData, sizeof(kData));
701
702                        mOwner->writeInt8(0x05);  // DecoderSpecificInfoTag
703
704                        mOwner->writeInt8(mCodecSpecificDataSize);
705                        mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
706
707                        static const uint8_t kData2[] = {
708                            0x06,  // SLConfigDescriptorTag
709                            0x01,
710                            0x02
711                        };
712                        mOwner->write(kData2, sizeof(kData2));
713
714                      mOwner->endBox();  // esds
715                  } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
716                      mOwner->beginBox("d263");
717
718                          mOwner->writeInt32(0);  // vendor
719                          mOwner->writeInt8(0);   // decoder version
720                          mOwner->writeInt8(10);  // level: 10
721                          mOwner->writeInt8(0);   // profile: 0
722
723                      mOwner->endBox();  // d263
724                  } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
725                      mOwner->beginBox("avcC");
726                        mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
727                      mOwner->endBox();  // avcC
728                  }
729
730                mOwner->endBox();  // mp4v, s263 or avc1
731            }
732          mOwner->endBox();  // stsd
733
734          mOwner->beginBox("stts");
735            mOwner->writeInt32(0);  // version=0, flags=0
736            mOwner->writeInt32(mSampleInfos.size() - 1);
737
738            List<SampleInfo>::iterator it = mSampleInfos.begin();
739            int64_t last = (*it).timestamp;
740            ++it;
741            while (it != mSampleInfos.end()) {
742                mOwner->writeInt32(1);
743                mOwner->writeInt32((*it).timestamp - last);
744
745                last = (*it).timestamp;
746
747                ++it;
748            }
749          mOwner->endBox();  // stts
750
751          mOwner->beginBox("stsz");
752            mOwner->writeInt32(0);  // version=0, flags=0
753            mOwner->writeInt32(0);  // default sample size
754            mOwner->writeInt32(mSampleInfos.size());
755            for (List<SampleInfo>::iterator it = mSampleInfos.begin();
756                 it != mSampleInfos.end(); ++it) {
757                mOwner->writeInt32((*it).size);
758            }
759          mOwner->endBox();  // stsz
760
761          mOwner->beginBox("stsc");
762            mOwner->writeInt32(0);  // version=0, flags=0
763            mOwner->writeInt32(mSampleInfos.size());
764            int32_t n = 1;
765            for (List<SampleInfo>::iterator it = mSampleInfos.begin();
766                 it != mSampleInfos.end(); ++it, ++n) {
767                mOwner->writeInt32(n);
768                mOwner->writeInt32(1);
769                mOwner->writeInt32(1);
770            }
771          mOwner->endBox();  // stsc
772
773          mOwner->beginBox("co64");
774            mOwner->writeInt32(0);  // version=0, flags=0
775            mOwner->writeInt32(mSampleInfos.size());
776            for (List<SampleInfo>::iterator it = mSampleInfos.begin();
777                 it != mSampleInfos.end(); ++it, ++n) {
778                mOwner->writeInt64((*it).offset);
779            }
780          mOwner->endBox();  // co64
781
782        mOwner->endBox();  // stbl
783      mOwner->endBox();  // mdia
784    mOwner->endBox();  // trak
785}
786
787}  // namespace android
788