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