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