Output streamable MP4 file during MP4 file recording

When the reserved moov box space is not big enough,
fall back to non-streamable MP4 file.

Change-Id: I93382d037d657a3f3fe2af31e4ea26e1898b4d95
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/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() {