Merge "Convert renderscript from using ACC to LLVM for its compiler."
diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h
index 27d0f50..6064fc4 100644
--- a/include/media/stagefright/MPEG4Writer.h
+++ b/include/media/stagefright/MPEG4Writer.h
@@ -61,6 +61,12 @@
     FILE *mFile;
     off_t mOffset;
     off_t mMdatOffset;
+    uint8_t *mMoovBoxBuffer;
+    off_t mMoovBoxBufferOffset;
+    bool  mWriteMoovBoxToMemory;
+    off_t mFreeBoxOffset;
+    bool mStreamableFile;
+    off_t mEstimatedMoovBoxSize;
     uint32_t mInterleaveDurationUs;
     Mutex mLock;
 
@@ -75,6 +81,8 @@
     off_t addSample_l(MediaBuffer *buffer);
     off_t addLengthPrefixedSample_l(MediaBuffer *buffer);
 
+    inline size_t write(const void *ptr, size_t size, size_t nmemb, FILE* stream);
+
     MPEG4Writer(const MPEG4Writer &);
     MPEG4Writer &operator=(const MPEG4Writer &);
 };
diff --git a/include/ui/android_native_buffer.h b/include/ui/android_native_buffer.h
index 9c92af8..402843e 100644
--- a/include/ui/android_native_buffer.h
+++ b/include/ui/android_native_buffer.h
@@ -33,6 +33,15 @@
         common.version = sizeof(android_native_buffer_t);
         memset(common.reserved, 0, sizeof(common.reserved));
     }
+
+    // Implement the methods that sp<android_native_buffer_t> expects so that it
+    // can be used to automatically refcount android_native_buffer_t's.
+    void incStrong(const void* id) const {
+        common.incRef(const_cast<android_native_base_t*>(&common));
+    }
+    void decStrong(const void* id) const {
+        common.decRef(const_cast<android_native_base_t*>(&common));
+    }
 #endif
 
     struct android_native_base_t common;
diff --git a/include/ui/egl/android_natives.h b/include/ui/egl/android_natives.h
index 471c3c7..49bfa2b 100644
--- a/include/ui/egl/android_natives.h
+++ b/include/ui/egl/android_natives.h
@@ -98,6 +98,15 @@
         common.version = sizeof(android_native_window_t);
         memset(common.reserved, 0, sizeof(common.reserved));
     }
+
+    // Implement the methods that sp<android_native_window_t> expects so that it
+    // can be used to automatically refcount android_native_window_t's.
+    void incStrong(const void* id) const {
+        common.incRef(const_cast<android_native_base_t*>(&common));
+    }
+    void decStrong(const void* id) const {
+        common.decRef(const_cast<android_native_base_t*>(&common));
+    }
 #endif
     
     struct android_native_base_t common;
@@ -291,6 +300,15 @@
 template <typename NATIVE_TYPE, typename TYPE, typename REF>
 class EGLNativeBase : public NATIVE_TYPE, public REF
 {
+public:
+    // Disambiguate between the incStrong in REF and NATIVE_TYPE
+    void incStrong(const void* id) const {
+        REF::incStrong(id);
+    }
+    void decStrong(const void* id) const {
+        REF::decStrong(id);
+    }
+
 protected:
     typedef EGLNativeBase<NATIVE_TYPE, TYPE, REF> BASE;
     EGLNativeBase() : NATIVE_TYPE(), REF() {
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 57db7e4..12e8fe6 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -32,6 +32,7 @@
 #include <media/stagefright/OMXCodec.h>
 #include <camera/ICamera.h>
 #include <camera/Camera.h>
+#include <camera/CameraParameters.h>
 #include <surfaceflinger/ISurface.h>
 #include <utils/Errors.h>
 #include <sys/types.h>
@@ -88,6 +89,12 @@
 }
 
 status_t StagefrightRecorder::setVideoSize(int width, int height) {
+    if (width <= 0 || height <= 0) {
+        LOGE("Invalid video size: %dx%d", width, height);
+        return BAD_VALUE;
+    }
+
+    // Additional check on the dimension will be performed later
     mVideoWidth = width;
     mVideoHeight = height;
 
@@ -95,6 +102,12 @@
 }
 
 status_t StagefrightRecorder::setVideoFrameRate(int frames_per_second) {
+    if (frames_per_second <= 0 || frames_per_second > 30) {
+        LOGE("Invalid video frame rate: %d", frames_per_second);
+        return BAD_VALUE;
+    }
+
+    // Additional check on the frame rate will be performed later
     mFrameRate = frames_per_second;
 
     return OK;
@@ -152,9 +165,9 @@
 
 // Attempt to parse an int64 literal optionally surrounded by whitespace,
 // returns true on success, false otherwise.
-static bool safe_strtoi64(const char *s, int32_t *val) {
+static bool safe_strtoi64(const char *s, int64_t *val) {
     char *end;
-    *val = static_cast<int32_t>(strtoll(s, &end, 10));
+    *val = strtoll(s, &end, 10);
 
     if (end == s || errno == ERANGE) {
         return false;
@@ -171,6 +184,18 @@
     return *end == '\0';
 }
 
+// Return true if the value is in [0, 0x007FFFFFFF]
+static bool safe_strtoi32(const char *s, int32_t *val) {
+    int64_t temp;
+    if (safe_strtoi64(s, &temp)) {
+        if (temp >= 0 && temp <= 0x007FFFFFFF) {
+            *val = static_cast<int32_t>(temp);
+            return true;
+        }
+    }
+    return false;
+}
+
 // Trim both leading and trailing whitespace from the given string.
 static void TrimString(String8 *s) {
     size_t num_bytes = s->bytes();
@@ -191,85 +216,129 @@
 
 status_t StagefrightRecorder::setParamAudioSamplingRate(int32_t sampleRate) {
     LOGV("setParamAudioSamplingRate: %d", sampleRate);
+    if (sampleRate <= 0) {
+        LOGE("Invalid audio sampling rate: %d", sampleRate);
+        return BAD_VALUE;
+    }
+
+    // Additional check on the sample rate will be performed later.
     mSampleRate = sampleRate;
     return OK;
 }
 
 status_t StagefrightRecorder::setParamAudioNumberOfChannels(int32_t channels) {
     LOGV("setParamAudioNumberOfChannels: %d", channels);
+    if (channels <= 0 || channels >= 3) {
+        LOGE("Invalid number of audio channels: %d", channels);
+    }
+
+    // Additional check on the number of channels will be performed later.
     mAudioChannels = channels;
     return OK;
 }
 
 status_t StagefrightRecorder::setParamAudioEncodingBitRate(int32_t bitRate) {
     LOGV("setParamAudioEncodingBitRate: %d", bitRate);
+    if (bitRate <= 0) {
+        LOGE("Invalid audio encoding bit rate: %d", bitRate);
+        return BAD_VALUE;
+    }
+
+    // The target bit rate may not be exactly the same as the requested.
+    // It depends on many factors, such as rate control, and the bit rate
+    // range that a specific encoder supports. The mismatch between the
+    // the target and requested bit rate will NOT be treated as an error.
     mAudioBitRate = bitRate;
     return OK;
 }
 
 status_t StagefrightRecorder::setParamVideoEncodingBitRate(int32_t bitRate) {
     LOGV("setParamVideoEncodingBitRate: %d", bitRate);
+    if (bitRate <= 0) {
+        LOGE("Invalid video encoding bit rate: %d", bitRate);
+        return BAD_VALUE;
+    }
+
+    // The target bit rate may not be exactly the same as the requested.
+    // It depends on many factors, such as rate control, and the bit rate
+    // range that a specific encoder supports. The mismatch between the
+    // the target and requested bit rate will NOT be treated as an error.
     mVideoBitRate = bitRate;
     return OK;
 }
 
-status_t StagefrightRecorder::setParamMaxDurationOrFileSize(int32_t limit,
+status_t StagefrightRecorder::setParamMaxDurationOrFileSize(int64_t limit,
         bool limit_is_duration) {
     LOGV("setParamMaxDurationOrFileSize: limit (%d) for %s",
             limit, limit_is_duration?"duration":"size");
+    if (limit_is_duration) {  // limit is in ms
+        if (limit <= 1000) {  // XXX: 1 second
+            LOGE("Max file duration is too short: %lld us", limit);
+        }
+        mMaxFileDurationUs = limit * 1000LL;
+    } else {
+        if (limit <= 1024) {  // XXX: 1 kB
+            LOGE("Max file size is too small: %lld bytes", limit);
+        }
+        mMaxFileSizeBytes = limit;
+    }
     return OK;
 }
 
 status_t StagefrightRecorder::setParamInterleaveDuration(int32_t durationUs) {
     LOGV("setParamInterleaveDuration: %d", durationUs);
+    if (durationUs <= 20000) {  // XXX: 20 ms
+        LOGE("Audio/video interleave duration is too small: %d us", durationUs);
+        return BAD_VALUE;
+    }
     mInterleaveDurationUs = durationUs;
     return OK;
 }
+
 status_t StagefrightRecorder::setParameter(
         const String8 &key, const String8 &value) {
     LOGV("setParameter: key (%s) => value (%s)", key.string(), value.string());
     if (key == "max-duration") {
-        int32_t max_duration_ms;
+        int64_t max_duration_ms;
         if (safe_strtoi64(value.string(), &max_duration_ms)) {
             return setParamMaxDurationOrFileSize(
                     max_duration_ms, true /* limit_is_duration */);
         }
     } else if (key == "max-filesize") {
-        int32_t max_filesize_bytes;
+        int64_t max_filesize_bytes;
         if (safe_strtoi64(value.string(), &max_filesize_bytes)) {
             return setParamMaxDurationOrFileSize(
                     max_filesize_bytes, false /* limit is filesize */);
         }
     } else if (key == "audio-param-sampling-rate") {
         int32_t sampling_rate;
-        if (safe_strtoi64(value.string(), &sampling_rate)) {
+        if (safe_strtoi32(value.string(), &sampling_rate)) {
             return setParamAudioSamplingRate(sampling_rate);
         }
     } else if (key == "audio-param-number-of-channels") {
         int32_t number_of_channels;
-        if (safe_strtoi64(value.string(), &number_of_channels)) {
+        if (safe_strtoi32(value.string(), &number_of_channels)) {
             return setParamAudioNumberOfChannels(number_of_channels);
         }
     } else if (key == "audio-param-encoding-bitrate") {
         int32_t audio_bitrate;
-        if (safe_strtoi64(value.string(), &audio_bitrate)) {
+        if (safe_strtoi32(value.string(), &audio_bitrate)) {
             return setParamAudioEncodingBitRate(audio_bitrate);
         }
     } else if (key == "video-param-encoding-bitrate") {
         int32_t video_bitrate;
-        if (safe_strtoi64(value.string(), &video_bitrate)) {
+        if (safe_strtoi32(value.string(), &video_bitrate)) {
             return setParamVideoEncodingBitRate(video_bitrate);
         }
     } else if (key == "param-interleave-duration-us") {
         int32_t durationUs;
-        if (safe_strtoi64(value.string(), &durationUs)) {
+        if (safe_strtoi32(value.string(), &durationUs)) {
             return setParamInterleaveDuration(durationUs);
         }
     } else {
         LOGE("setParameter: failed to find key %s", key.string());
-        return BAD_VALUE;
     }
-    return OK;
+    return BAD_VALUE;
 }
 
 status_t StagefrightRecorder::setParameters(const String8 &params) {
@@ -332,6 +401,10 @@
         case OUTPUT_FORMAT_AMR_WB:
             return startAMRRecording();
 
+        case OUTPUT_FORMAT_AAC_ADIF:
+        case OUTPUT_FORMAT_AAC_ADTS:
+            return startAACRecording();
+
         default:
             return UNKNOWN_ERROR;
     }
@@ -377,6 +450,7 @@
     encMeta->setInt32(kKeyMaxInputSize, maxInputSize);
     encMeta->setInt32(kKeyChannelCount, mAudioChannels);
     encMeta->setInt32(kKeySampleRate, mSampleRate);
+    encMeta->setInt32(kKeyBitRate, mAudioBitRate);
 
     OMXClient client;
     CHECK_EQ(client.connect(), OK);
@@ -388,18 +462,55 @@
     return audioEncoder;
 }
 
+status_t StagefrightRecorder::startAACRecording() {
+    CHECK(mOutputFormat == OUTPUT_FORMAT_AAC_ADIF ||
+          mOutputFormat == OUTPUT_FORMAT_AAC_ADTS);
+
+    CHECK(mAudioEncoder == AUDIO_ENCODER_AAC);
+    CHECK(mAudioSource != AUDIO_SOURCE_LIST_END);
+    CHECK(mOutputFd >= 0);
+
+    CHECK(0 == "AACWriter is not implemented yet");
+
+    return OK;
+}
+
 status_t StagefrightRecorder::startAMRRecording() {
-    if (mAudioSource == AUDIO_SOURCE_LIST_END
-        || mVideoSource != VIDEO_SOURCE_LIST_END) {
+    CHECK(mOutputFormat == OUTPUT_FORMAT_AMR_NB ||
+          mOutputFormat == OUTPUT_FORMAT_AMR_WB);
+
+    if (mOutputFormat == OUTPUT_FORMAT_AMR_NB) {
+        if (mAudioEncoder != AUDIO_ENCODER_DEFAULT &&
+            mAudioEncoder != AUDIO_ENCODER_AMR_NB) {
+            LOGE("Invalid encoder %d used for AMRNB recording",
+                    mAudioEncoder);
+            return UNKNOWN_ERROR;
+        }
+        if (mSampleRate != 8000) {
+            LOGE("Invalid sampling rate %d used for AMRNB recording",
+                    mSampleRate);
+            return UNKNOWN_ERROR;
+        }
+    } else {  // mOutputFormat must be OUTPUT_FORMAT_AMR_WB
+        if (mAudioEncoder != AUDIO_ENCODER_AMR_WB) {
+            LOGE("Invlaid encoder %d used for AMRWB recording",
+                    mAudioEncoder);
+            return UNKNOWN_ERROR;
+        }
+        if (mSampleRate != 16000) {
+            LOGE("Invalid sample rate %d used for AMRWB recording",
+                    mSampleRate);
+            return UNKNOWN_ERROR;
+        }
+    }
+    if (mAudioChannels != 1) {
+        LOGE("Invalid number of audio channels %d used for amr recording",
+                mAudioChannels);
         return UNKNOWN_ERROR;
     }
 
-    if (mOutputFormat == OUTPUT_FORMAT_AMR_NB
-            && mAudioEncoder != AUDIO_ENCODER_DEFAULT
-            && mAudioEncoder != AUDIO_ENCODER_AMR_NB) {
-        return UNKNOWN_ERROR;
-    } else if (mOutputFormat == OUTPUT_FORMAT_AMR_WB
-            && mAudioEncoder != AUDIO_ENCODER_AMR_WB) {
+    if (mAudioSource >= AUDIO_SOURCE_LIST_END) {
+        LOGE("Invalid audio source: %d", mAudioSource);
         return UNKNOWN_ERROR;
     }
 
@@ -444,6 +555,36 @@
             || mVideoSource == VIDEO_SOURCE_CAMERA) {
         CHECK(mCamera != NULL);
 
+        if (mCamera == 0) {
+            mCamera = Camera::connect(0);
+        }
+        CHECK(mCamera != NULL);
+
+        // Set the actual video recording frame size
+        CameraParameters params(mCamera->getParameters());
+        params.setPreviewSize(mVideoWidth, mVideoHeight);
+        params.setPreviewFrameRate(mFrameRate);
+        String8 s = params.flatten();
+        CHECK_EQ(OK, mCamera->setParameters(s));
+        CameraParameters newCameraParams(mCamera->getParameters());
+
+        // Check on video frame size
+        int frameWidth = 0, frameHeight = 0;
+        newCameraParams.getPreviewSize(&frameWidth, &frameHeight);
+        if (frameWidth  < 0 || frameWidth  != mVideoWidth ||
+            frameHeight < 0 || frameHeight != mVideoHeight) {
+            LOGE("Failed to set the video frame size to %dx%d",
+                    mVideoWidth, mVideoHeight);
+            return UNKNOWN_ERROR;
+        }
+
+        // Check on video frame rate
+        int frameRate = newCameraParams.getPreviewFrameRate();
+        if (frameRate < 0 || (frameRate - mFrameRate) != 0) {
+            LOGE("Failed to set frame rate to %d", mFrameRate);
+            return UNKNOWN_ERROR;
+        }
+
         sp<CameraSource> cameraSource =
             CameraSource::CreateFromCamera(mCamera);
 
@@ -452,6 +593,9 @@
         cameraSource->setPreviewSurface(mPreviewSurface);
 
         sp<MetaData> enc_meta = new MetaData;
+        enc_meta->setInt32(kKeyBitRate, mVideoBitRate);
+        enc_meta->setInt32(kKeySampleRate, mFrameRate);  // XXX: kKeySampleRate?
+
         switch (mVideoEncoder) {
             case VIDEO_ENCODER_H263:
                 enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263);
@@ -491,7 +635,12 @@
         mWriter->addSource(encoder);
     }
 
-    ((MPEG4Writer *)mWriter.get())->setInterleaveDuration(mInterleaveDurationUs);
+    {
+        // MPEGWriter specific handling
+        MPEG4Writer *writer = ((MPEG4Writer *) mWriter.get());  // mWriter is an MPEGWriter
+        writer->setInterleaveDuration(mInterleaveDurationUs);
+    }
+
     mWriter->start();
     return OK;
 }
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index 3b99e91..b7d554b 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -75,6 +75,8 @@
     int32_t mAudioChannels;
     int32_t mSampleRate;
     int32_t mInterleaveDurationUs;
+    int64_t mMaxFileSizeBytes;
+    int64_t mMaxFileDurationUs;
 
     String8 mParams;
     int mOutputFd;
@@ -82,6 +84,7 @@
 
     status_t startMPEG4Recording();
     status_t startAMRRecording();
+    status_t startAACRecording();
     sp<MediaSource> createAudioSource();
     status_t setParameter(const String8 &key, const String8 &value);
     status_t setParamVideoEncodingBitRate(int32_t bitRate);
@@ -89,7 +92,7 @@
     status_t setParamAudioNumberOfChannels(int32_t channles);
     status_t setParamAudioSamplingRate(int32_t sampleRate);
     status_t setParamInterleaveDuration(int32_t durationUs);
-    status_t setParamMaxDurationOrFileSize(int32_t limit, bool limit_is_duration);
+    status_t setParamMaxDurationOrFileSize(int64_t limit, bool limit_is_duration);
 
     StagefrightRecorder(const StagefrightRecorder &);
     StagefrightRecorder &operator=(const StagefrightRecorder &);
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index fd792ee..19cccf7 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -113,6 +113,7 @@
     : mFile(fopen(filename, "wb")),
       mOffset(0),
       mMdatOffset(0),
+      mEstimatedMoovBoxSize(0),
       mInterleaveDurationUs(500000) {
     CHECK(mFile != NULL);
 }
@@ -121,6 +122,7 @@
     : mFile(fdopen(fd, "wb")),
       mOffset(0),
       mMdatOffset(0),
+      mEstimatedMoovBoxSize(0),
       mInterleaveDurationUs(500000) {
     CHECK(mFile != NULL);
 }
@@ -147,15 +149,33 @@
         return UNKNOWN_ERROR;
     }
 
+    mStreamableFile = true;
+    mWriteMoovBoxToMemory = false;
+    mMoovBoxBuffer = NULL;
+    mMoovBoxBufferOffset = 0;
+
     beginBox("ftyp");
       writeFourcc("isom");
       writeInt32(0);
       writeFourcc("isom");
     endBox();
 
-    mMdatOffset = mOffset;
-    write("\x00\x00\x00\x01mdat????????", 16);
+    mFreeBoxOffset = mOffset;
 
+    if (mEstimatedMoovBoxSize == 0) {
+        // XXX: Estimate the moov box size
+        //      based on max file size or duration limit
+        mEstimatedMoovBoxSize = 0x0F00;
+    }
+    CHECK(mEstimatedMoovBoxSize >= 8);
+    fseeko(mFile, mFreeBoxOffset, SEEK_SET);
+    writeInt32(mEstimatedMoovBoxSize);
+    write("free", 4);
+
+    mMdatOffset = mFreeBoxOffset + mEstimatedMoovBoxSize;
+    mOffset = mMdatOffset;
+    fseeko(mFile, mMdatOffset, SEEK_SET);
+    write("\x00\x00\x00\x01mdat????????", 16);
     for (List<Track *>::iterator it = mTracks.begin();
          it != mTracks.end(); ++it) {
         status_t err = (*it)->start();
@@ -189,14 +209,20 @@
         }
     }
 
+
     // Fix up the size of the 'mdat' chunk.
-    fseek(mFile, mMdatOffset + 8, SEEK_SET);
+    fseeko(mFile, mMdatOffset + 8, SEEK_SET);
     int64_t size = mOffset - mMdatOffset;
     size = hton64(size);
     fwrite(&size, 1, 8, mFile);
-    fseek(mFile, mOffset, SEEK_SET);
+    fseeko(mFile, mOffset, SEEK_SET);
 
     time_t now = time(NULL);
+    const off_t moovOffset = mOffset;
+    mWriteMoovBoxToMemory = true;
+    mMoovBoxBuffer = (uint8_t *) malloc(mEstimatedMoovBoxSize);
+    mMoovBoxBufferOffset = 0;
+    CHECK(mMoovBoxBuffer != NULL);
 
     beginBox("moov");
 
@@ -236,8 +262,30 @@
       }
     endBox();  // moov
 
+    mWriteMoovBoxToMemory = false;
+    if (mStreamableFile) {
+        CHECK(mMoovBoxBufferOffset + 8 <= mEstimatedMoovBoxSize);
+
+        // Moov box
+        fseeko(mFile, mFreeBoxOffset, SEEK_SET);
+        mOffset = mFreeBoxOffset;
+        write(mMoovBoxBuffer, 1, mMoovBoxBufferOffset, mFile);
+
+        // Free box
+        mFreeBoxOffset = mStreamableFile? mOffset: mFreeBoxOffset;
+        fseeko(mFile, mFreeBoxOffset, SEEK_SET);
+        writeInt32(mEstimatedMoovBoxSize - mMoovBoxBufferOffset);
+        write("free", 4);
+
+        // Free temp memory
+        free(mMoovBoxBuffer);
+        mMoovBoxBuffer = NULL;
+        mMoovBoxBufferOffset = 0;
+    }
+
     CHECK(mBoxes.empty());
 
+    fflush(mFile);
     fclose(mFile);
     mFile = NULL;
 }
@@ -317,10 +365,41 @@
     return old_offset;
 }
 
+size_t MPEG4Writer::write(
+        const void *ptr, size_t size, size_t nmemb, FILE *stream) {
+
+    const size_t bytes = size * nmemb;
+    if (mWriteMoovBoxToMemory) {
+        if (8 + mMoovBoxBufferOffset + bytes > mEstimatedMoovBoxSize) {
+            for (List<off_t>::iterator it = mBoxes.begin();
+                 it != mBoxes.end(); ++it) {
+                (*it) += mOffset;
+            }
+            fseeko(mFile, mOffset, SEEK_SET);
+            fwrite(mMoovBoxBuffer, 1, mMoovBoxBufferOffset, stream);
+            fwrite(ptr, size, nmemb, stream);
+            mOffset += (bytes + mMoovBoxBufferOffset);
+            free(mMoovBoxBuffer);
+            mMoovBoxBuffer = NULL;
+            mMoovBoxBufferOffset = 0;
+            mWriteMoovBoxToMemory = false;
+            mStreamableFile = false;
+        } else {
+            memcpy(mMoovBoxBuffer + mMoovBoxBufferOffset, ptr, bytes);
+            mMoovBoxBufferOffset += bytes;
+        }
+    } else {
+        fwrite(ptr, size, nmemb, stream);
+        mOffset += bytes;
+    }
+    return bytes;
+}
+
 void MPEG4Writer::beginBox(const char *fourcc) {
     CHECK_EQ(strlen(fourcc), 4);
 
-    mBoxes.push_back(mOffset);
+    mBoxes.push_back(mWriteMoovBoxToMemory?
+            mMoovBoxBufferOffset: mOffset);
 
     writeInt32(0);
     writeFourcc(fourcc);
@@ -332,51 +411,48 @@
     off_t offset = *--mBoxes.end();
     mBoxes.erase(--mBoxes.end());
 
-    fseek(mFile, offset, SEEK_SET);
-    writeInt32(mOffset - offset);
-    mOffset -= 4;
-    fseek(mFile, mOffset, SEEK_SET);
+    if (mWriteMoovBoxToMemory) {
+       int32_t x = htonl(mMoovBoxBufferOffset - offset);
+       memcpy(mMoovBoxBuffer + offset, &x, 4);
+    } else {
+        fseeko(mFile, offset, SEEK_SET);
+        writeInt32(mOffset - offset);
+        mOffset -= 4;
+        fseeko(mFile, mOffset, SEEK_SET);
+    }
 }
 
 void MPEG4Writer::writeInt8(int8_t x) {
-    fwrite(&x, 1, 1, mFile);
-    ++mOffset;
+    write(&x, 1, 1, mFile);
 }
 
 void MPEG4Writer::writeInt16(int16_t x) {
     x = htons(x);
-    fwrite(&x, 1, 2, mFile);
-    mOffset += 2;
+    write(&x, 1, 2, mFile);
 }
 
 void MPEG4Writer::writeInt32(int32_t x) {
     x = htonl(x);
-    fwrite(&x, 1, 4, mFile);
-    mOffset += 4;
+    write(&x, 1, 4, mFile);
 }
 
 void MPEG4Writer::writeInt64(int64_t x) {
     x = hton64(x);
-    fwrite(&x, 1, 8, mFile);
-    mOffset += 8;
+    write(&x, 1, 8, mFile);
 }
 
 void MPEG4Writer::writeCString(const char *s) {
     size_t n = strlen(s);
-
-    fwrite(s, 1, n + 1, mFile);
-    mOffset += n + 1;
+    write(s, 1, n + 1, mFile);
 }
 
 void MPEG4Writer::writeFourcc(const char *s) {
     CHECK_EQ(strlen(s), 4);
-    fwrite(s, 1, 4, mFile);
-    mOffset += 4;
+    write(s, 1, 4, mFile);
 }
 
 void MPEG4Writer::write(const void *data, size_t size) {
-    fwrite(data, 1, size, mFile);
-    mOffset += size;
+    write(data, 1, size, mFile);
 }
 
 bool MPEG4Writer::reachedEOS() {