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