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