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