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