Reduce blocking time in file write

bug - 3418787

Change-Id: I4723662bf46ed07271be8468f84ae5d93cb793fa
diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h
index f7618e9..5c5229d 100644
--- a/include/media/stagefright/MPEG4Writer.h
+++ b/include/media/stagefright/MPEG4Writer.h
@@ -98,6 +98,8 @@
         List<MediaBuffer *> mSamples;       // Sample data
 
         // Convenient constructor
+        Chunk(): mTrack(NULL), mTimeStampUs(0) {}
+
         Chunk(Track *track, int64_t timeUs, List<MediaBuffer *> samples)
             : mTrack(track), mTimeStampUs(timeUs), mSamples(samples) {
         }
@@ -124,13 +126,14 @@
     void bufferChunk(const Chunk& chunk);
 
     // Write all buffered chunks from all tracks
-    void writeChunks();
+    void writeAllChunks();
 
-    // Write a chunk if there is one
-    status_t writeOneChunk();
+    // Retrieve the proper chunk to write if there is one
+    // Return true if a chunk is found; otherwise, return false.
+    bool findChunkToWrite(Chunk *chunk);
 
-    // Write the first chunk from the given ChunkInfo.
-    void writeFirstChunk(ChunkInfo* info);
+    // Actually write the given chunk to the file.
+    void writeChunkToFile(Chunk* chunk);
 
     // Adjust other track media clock (presumably wall clock)
     // based on audio track media clock with the drift time.
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index d1a497f8..b11789d 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -1122,41 +1122,41 @@
     CHECK("Received a chunk for a unknown track" == 0);
 }
 
-void MPEG4Writer::writeFirstChunk(ChunkInfo* info) {
-    LOGV("writeFirstChunk: %p", info->mTrack);
+void MPEG4Writer::writeChunkToFile(Chunk* chunk) {
+    LOGV("writeChunkToFile: %lld from %s track",
+        chunk.mTimestampUs, chunk.mTrack->isAudio()? "audio": "video");
 
-    List<Chunk>::iterator chunkIt = info->mChunks.begin();
-    for (List<MediaBuffer *>::iterator it = chunkIt->mSamples.begin();
-         it != chunkIt->mSamples.end(); ++it) {
+    int32_t isFirstSample = true;
+    while (!chunk->mSamples.empty()) {
+        List<MediaBuffer *>::iterator it = chunk->mSamples.begin();
 
-        off64_t offset = info->mTrack->isAvc()
-                            ? addLengthPrefixedSample_l(*it)
-                            : addSample_l(*it);
-        if (it == chunkIt->mSamples.begin()) {
-            info->mTrack->addChunkOffset(offset);
+        off64_t offset = chunk->mTrack->isAvc()
+                                ? addLengthPrefixedSample_l(*it)
+                                : addSample_l(*it);
+
+        if (isFirstSample) {
+            chunk->mTrack->addChunkOffset(offset);
+            isFirstSample = false;
         }
-    }
 
-    // Done with the current chunk.
-    // Release all the samples in this chunk.
-    while (!chunkIt->mSamples.empty()) {
-        List<MediaBuffer *>::iterator it = chunkIt->mSamples.begin();
         (*it)->release();
         (*it) = NULL;
-        chunkIt->mSamples.erase(it);
+        chunk->mSamples.erase(it);
     }
-    chunkIt->mSamples.clear();
-    info->mChunks.erase(chunkIt);
+    chunk->mSamples.clear();
 }
 
-void MPEG4Writer::writeChunks() {
-    LOGV("writeChunks");
+void MPEG4Writer::writeAllChunks() {
+    LOGV("writeAllChunks");
     size_t outstandingChunks = 0;
     while (!mChunkInfos.empty()) {
         List<ChunkInfo>::iterator it = mChunkInfos.begin();
         while (!it->mChunks.empty()) {
-            CHECK_EQ(OK, writeOneChunk());
-            ++outstandingChunks;
+            Chunk chunk;
+            if (findChunkToWrite(&chunk)) {
+                writeChunkToFile(&chunk);
+                ++outstandingChunks;
+            }
         }
         it->mTrack = NULL;
         mChunkInfos.erase(it);
@@ -1165,8 +1165,8 @@
     LOGD("%d chunks are written in the last batch", outstandingChunks);
 }
 
-status_t MPEG4Writer::writeOneChunk() {
-    LOGV("writeOneChunk");
+bool MPEG4Writer::findChunkToWrite(Chunk *chunk) {
+    LOGV("findChunkToWrite");
 
     // Find the smallest timestamp, and write that chunk out
     // XXX: What if some track is just too slow?
@@ -1185,38 +1185,50 @@
 
     if (track == NULL) {
         LOGV("Nothing to be written after all");
-        return OK;
+        return false;
     }
 
     if (mIsFirstChunk) {
         mIsFirstChunk = false;
     }
+
     for (List<ChunkInfo>::iterator it = mChunkInfos.begin();
          it != mChunkInfos.end(); ++it) {
         if (it->mTrack == track) {
-            writeFirstChunk(&(*it));
+            *chunk = *(it->mChunks.begin());
+            it->mChunks.erase(it->mChunks.begin());
+            CHECK_EQ(chunk->mTrack, track);
+            return true;
         }
     }
-    return OK;
+
+    return false;
 }
 
 void MPEG4Writer::threadFunc() {
     LOGV("threadFunc");
 
     prctl(PR_SET_NAME, (unsigned long)"MPEG4Writer", 0, 0, 0);
+
+    Mutex::Autolock autoLock(mLock);
     while (!mDone) {
-        {
-            Mutex::Autolock autolock(mLock);
+        Chunk chunk;
+        bool chunkFound = false;
+
+        while (!mDone && !(chunkFound = findChunkToWrite(&chunk))) {
             mChunkReadyCondition.wait(mLock);
-            CHECK_EQ(writeOneChunk(), OK);
+        }
+
+        // Actual write without holding the lock in order to
+        // reduce the blocking time for media track threads.
+        if (chunkFound) {
+            mLock.unlock();
+            writeChunkToFile(&chunk);
+            mLock.lock();
         }
     }
 
-    {
-        // Write ALL samples
-        Mutex::Autolock autolock(mLock);
-        writeChunks();
-    }
+    writeAllChunks();
 }
 
 status_t MPEG4Writer::startWriterThread() {