MPEG4Writer.cpp revision 18291bc20e55e8f3fd5feb786771a8ed32c19c59
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        int32_t units, scale;
403        bool success =
404            buffer->meta_data()->findInt32(kKeyTimeUnits, &units);
405        CHECK(success);
406        success =
407            buffer->meta_data()->findInt32(kKeyTimeScale, &scale);
408        CHECK(success);
409
410        info.timestamp = (int64_t)units * 1000 / scale;
411
412        mSampleInfos.push_back(info);
413
414        buffer->release();
415        buffer = NULL;
416    }
417
418    mReachedEOS = true;
419}
420
421int64_t MPEG4Writer::Track::getDuration() const {
422    return 10000;  // XXX
423}
424
425void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) {
426    const char *mime;
427    bool success = mMeta->findCString(kKeyMIMEType, &mime);
428    CHECK(success);
429
430    bool is_audio = !strncasecmp(mime, "audio/", 6);
431
432    time_t now = time(NULL);
433
434    mOwner->beginBox("trak");
435
436      mOwner->beginBox("tkhd");
437        mOwner->writeInt32(0);             // version=0, flags=0
438        mOwner->writeInt32(now);           // creation time
439        mOwner->writeInt32(now);           // modification time
440        mOwner->writeInt32(trackID);
441        mOwner->writeInt32(0);             // reserved
442        mOwner->writeInt32(getDuration());
443        mOwner->writeInt32(0);             // reserved
444        mOwner->writeInt32(0);             // reserved
445        mOwner->writeInt16(0);             // layer
446        mOwner->writeInt16(0);             // alternate group
447        mOwner->writeInt16(is_audio ? 0x100 : 0);  // volume
448        mOwner->writeInt16(0);             // reserved
449
450        mOwner->writeInt32(0x10000);       // matrix
451        mOwner->writeInt32(0);
452        mOwner->writeInt32(0);
453        mOwner->writeInt32(0);
454        mOwner->writeInt32(0x10000);
455        mOwner->writeInt32(0);
456        mOwner->writeInt32(0);
457        mOwner->writeInt32(0);
458        mOwner->writeInt32(0x40000000);
459
460        if (is_audio) {
461            mOwner->writeInt32(0);
462            mOwner->writeInt32(0);
463        } else {
464            int32_t width, height;
465            bool success = mMeta->findInt32(kKeyWidth, &width);
466            success = success && mMeta->findInt32(kKeyHeight, &height);
467            CHECK(success);
468
469            mOwner->writeInt32(width);
470            mOwner->writeInt32(height);
471        }
472      mOwner->endBox();  // tkhd
473
474      mOwner->beginBox("mdia");
475
476        mOwner->beginBox("mdhd");
477          mOwner->writeInt32(0);             // version=0, flags=0
478          mOwner->writeInt32(now);           // creation time
479          mOwner->writeInt32(now);           // modification time
480          mOwner->writeInt32(1000);          // timescale
481          mOwner->writeInt32(getDuration());
482          mOwner->writeInt16(0);             // language code XXX
483          mOwner->writeInt16(0);             // predefined
484        mOwner->endBox();
485
486        mOwner->beginBox("hdlr");
487          mOwner->writeInt32(0);             // version=0, flags=0
488          mOwner->writeInt32(0);             // predefined
489          mOwner->writeFourcc(is_audio ? "soun" : "vide");
490          mOwner->writeInt32(0);             // reserved
491          mOwner->writeInt32(0);             // reserved
492          mOwner->writeInt32(0);             // reserved
493          mOwner->writeCString("");          // name
494        mOwner->endBox();
495
496        mOwner->beginBox("minf");
497
498          mOwner->beginBox("dinf");
499            mOwner->beginBox("dref");
500              mOwner->writeInt32(0);  // version=0, flags=0
501              mOwner->writeInt32(1);
502              mOwner->beginBox("url ");
503                mOwner->writeInt32(1);  // version=0, flags=1
504              mOwner->endBox();  // url
505            mOwner->endBox();  // dref
506          mOwner->endBox();  // dinf
507
508          if (is_audio) {
509              mOwner->beginBox("smhd");
510              mOwner->writeInt32(0);           // version=0, flags=0
511              mOwner->writeInt16(0);           // balance
512              mOwner->writeInt16(0);           // reserved
513              mOwner->endBox();
514          } else {
515              mOwner->beginBox("vmhd");
516              mOwner->writeInt32(0x00000001);  // version=0, flags=1
517              mOwner->writeInt16(0);           // graphics mode
518              mOwner->writeInt16(0);           // opcolor
519              mOwner->writeInt16(0);
520              mOwner->writeInt16(0);
521              mOwner->endBox();
522          }
523        mOwner->endBox();  // minf
524
525        mOwner->beginBox("stbl");
526
527          mOwner->beginBox("stsd");
528            mOwner->writeInt32(0);               // version=0, flags=0
529            mOwner->writeInt32(1);               // entry count
530            if (is_audio) {
531                const char *fourcc = NULL;
532                if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime)) {
533                    fourcc = "samr";
534                } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) {
535                    fourcc = "sawb";
536                } else {
537                    LOGE("Unknown mime type '%s'.", mime);
538                    CHECK(!"should not be here, unknown mime type.");
539                }
540
541                mOwner->beginBox(fourcc);          // audio format
542                  mOwner->writeInt32(0);           // reserved
543                  mOwner->writeInt16(0);           // reserved
544                  mOwner->writeInt16(0);           // data ref index
545                  mOwner->writeInt32(0);           // reserved
546                  mOwner->writeInt32(0);           // reserved
547                  mOwner->writeInt16(2);           // channel count
548                  mOwner->writeInt16(16);          // sample size
549                  mOwner->writeInt16(0);           // predefined
550                  mOwner->writeInt16(0);           // reserved
551
552                  int32_t samplerate;
553                  bool success = mMeta->findInt32(kKeySampleRate, &samplerate);
554                  CHECK(success);
555
556                  mOwner->writeInt32(samplerate << 16);
557                mOwner->endBox();
558            } else {
559                if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
560                    mOwner->beginBox("mp4v");
561                } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
562                    mOwner->beginBox("s263");
563                } else {
564                    LOGE("Unknown mime type '%s'.", mime);
565                    CHECK(!"should not be here, unknown mime type.");
566                }
567
568                  mOwner->writeInt32(0);           // reserved
569                  mOwner->writeInt16(0);           // reserved
570                  mOwner->writeInt16(0);           // data ref index
571                  mOwner->writeInt16(0);           // predefined
572                  mOwner->writeInt16(0);           // reserved
573                  mOwner->writeInt32(0);           // predefined
574                  mOwner->writeInt32(0);           // predefined
575                  mOwner->writeInt32(0);           // predefined
576
577                  int32_t width, height;
578                  bool success = mMeta->findInt32(kKeyWidth, &width);
579                  success = success && mMeta->findInt32(kKeyHeight, &height);
580                  CHECK(success);
581
582                  mOwner->writeInt16(width);
583                  mOwner->writeInt16(height);
584                  mOwner->writeInt32(0x480000);    // horiz resolution
585                  mOwner->writeInt32(0x480000);    // vert resolution
586                  mOwner->writeInt32(0);           // reserved
587                  mOwner->writeInt16(1);           // frame count
588                  mOwner->write("                                ", 32);
589                  mOwner->writeInt16(0x18);        // depth
590                  mOwner->writeInt16(-1);          // predefined
591
592                  CHECK(23 + mCodecSpecificDataSize < 128);
593
594                  if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
595                      mOwner->beginBox("esds");
596
597                        mOwner->writeInt32(0);           // version=0, flags=0
598
599                        mOwner->writeInt8(0x03);  // ES_DescrTag
600                        mOwner->writeInt8(23 + mCodecSpecificDataSize);
601                        mOwner->writeInt16(0x0000);  // ES_ID
602                        mOwner->writeInt8(0x1f);
603
604                        mOwner->writeInt8(0x04);  // DecoderConfigDescrTag
605                        mOwner->writeInt8(15 + mCodecSpecificDataSize);
606                        mOwner->writeInt8(0x20);  // objectTypeIndication ISO/IEC 14492-2
607                        mOwner->writeInt8(0x11);  // streamType VisualStream
608
609                        static const uint8_t kData[] = {
610                            0x01, 0x77, 0x00,
611                            0x00, 0x03, 0xe8, 0x00,
612                            0x00, 0x03, 0xe8, 0x00
613                        };
614                        mOwner->write(kData, sizeof(kData));
615
616                        mOwner->writeInt8(0x05);  // DecoderSpecificInfoTag
617
618                        mOwner->writeInt8(mCodecSpecificDataSize);
619                        mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
620
621                        static const uint8_t kData2[] = {
622                            0x06,  // SLConfigDescriptorTag
623                            0x01,
624                            0x02
625                        };
626                        mOwner->write(kData2, sizeof(kData2));
627
628                      mOwner->endBox();  // esds
629                  } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
630                      mOwner->beginBox("d263");
631
632                          mOwner->writeInt32(0);  // vendor
633                          mOwner->writeInt8(0);   // decoder version
634                          mOwner->writeInt8(10);  // level: 10
635                          mOwner->writeInt8(0);   // profile: 0
636
637                      mOwner->endBox();  // d263
638                  }
639                mOwner->endBox();  // mp4v or s263
640            }
641          mOwner->endBox();  // stsd
642
643          mOwner->beginBox("stts");
644            mOwner->writeInt32(0);  // version=0, flags=0
645            mOwner->writeInt32(mSampleInfos.size() - 1);
646
647            List<SampleInfo>::iterator it = mSampleInfos.begin();
648            int64_t last = (*it).timestamp;
649            ++it;
650            while (it != mSampleInfos.end()) {
651                mOwner->writeInt32(1);
652                mOwner->writeInt32((*it).timestamp - last);
653
654                last = (*it).timestamp;
655
656                ++it;
657            }
658          mOwner->endBox();  // stts
659
660          mOwner->beginBox("stsz");
661            mOwner->writeInt32(0);  // version=0, flags=0
662            mOwner->writeInt32(0);  // default sample size
663            mOwner->writeInt32(mSampleInfos.size());
664            for (List<SampleInfo>::iterator it = mSampleInfos.begin();
665                 it != mSampleInfos.end(); ++it) {
666                mOwner->writeInt32((*it).size);
667            }
668          mOwner->endBox();  // stsz
669
670          mOwner->beginBox("stsc");
671            mOwner->writeInt32(0);  // version=0, flags=0
672            mOwner->writeInt32(mSampleInfos.size());
673            int32_t n = 1;
674            for (List<SampleInfo>::iterator it = mSampleInfos.begin();
675                 it != mSampleInfos.end(); ++it, ++n) {
676                mOwner->writeInt32(n);
677                mOwner->writeInt32(1);
678                mOwner->writeInt32(1);
679            }
680          mOwner->endBox();  // stsc
681
682          mOwner->beginBox("co64");
683            mOwner->writeInt32(0);  // version=0, flags=0
684            mOwner->writeInt32(mSampleInfos.size());
685            for (List<SampleInfo>::iterator it = mSampleInfos.begin();
686                 it != mSampleInfos.end(); ++it, ++n) {
687                mOwner->writeInt64((*it).offset);
688            }
689          mOwner->endBox();  // co64
690
691        mOwner->endBox();  // stbl
692      mOwner->endBox();  // mdia
693    mOwner->endBox();  // trak
694}
695
696}  // namespace android
697