Mpeg4Writer/MediaMuxer:allow empty tracks

1) Don't count tracks with no samples as malformed for MediaMuxer, but
keep the old behavior as it is for MediaRecorder.
2) When there are no samples to be written in a track, skip that one, but
compose mpeg4 file with all other tracks in it.
3) Allow notifications in MediaMuxer during stop() process.

Bug: 144108285
Bug: 146423844
Bug: 148754639

Test: 1) Unit tested by adding a video and an audio track, once leaving
      audio track with no samples and once leaving video with no
      samples.  Mpeg4 file with expected track was created both the times
      and played well.
      2) android.mediav2.cts.MuxerUnitTest$TestApi#testSimpleStartStopMuxer
      3) android.media.cts.MediaMuxerTest
      4) android.media.cts.MediaRecorderTest

Change-Id: If76a1f3b60d09836d53bce6f6e759e6a751f5538
Merged-In: If76a1f3b60d09836d53bce6f6e759e6a751f5538
(cherry picked from commit afc9f27233fa7dfb8c03e0fc83f612e7a19f27f2)
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index c10e6e0..cf7f423 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -2009,6 +2009,9 @@
             (*meta)->setInt32(kKeyRotation, mRotationDegrees);
         }
     }
+    if (mOutputFormat == OUTPUT_FORMAT_MPEG_4 || mOutputFormat == OUTPUT_FORMAT_THREE_GPP) {
+        (*meta)->setInt32(kKeyEmptyTrackMalFormed, true);
+    }
 }
 
 status_t StagefrightRecorder::pause() {
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 86fe141..cb6798e 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -1131,8 +1131,8 @@
         err = writerErr;
     }
 
-    // Do not write out movie header on error.
-    if (err != OK) {
+    // Do not write out movie header on error except malformed track.
+    if (err != OK && err != ERROR_MALFORMED) {
         release();
         return err;
     }
@@ -2687,7 +2687,7 @@
 status_t MPEG4Writer::Track::stop(bool stopSource) {
     ALOGD("%s track stopping. %s source", getTrackType(), stopSource ? "Stop" : "Not Stop");
     if (!mStarted) {
-        ALOGE("Stop() called but track is not started");
+        ALOGE("Stop() called but track is not started or stopped");
         return ERROR_END_OF_STREAM;
     }
 
@@ -2711,6 +2711,7 @@
     err = static_cast<status_t>(reinterpret_cast<uintptr_t>(dummy));
     WARN_UNLESS(err == 0, "%s track stopped. Status :%d. %s source", getTrackType(), err,
                 stopSource ? "Stop" : "Not Stop");
+    mStarted = false;
     return err;
 }
 
@@ -3586,56 +3587,60 @@
         }
     }
     if (isTrackMalFormed()) {
+        mIsMalformed = true;
         dumpTimeStamps();
         err = ERROR_MALFORMED;
     }
 
     mOwner->trackProgressStatus(mTrackId, -1, err);
 
-    if (mIsHeic) {
-        if (!mChunkSamples.empty()) {
-            bufferChunk(0);
-            ++nChunks;
-        }
-    } else {
-        // Last chunk
-        if (!hasMultipleTracks) {
-            addOneStscTableEntry(1, mStszTableEntries->count());
-        } else if (!mChunkSamples.empty()) {
-            addOneStscTableEntry(++nChunks, mChunkSamples.size());
-            bufferChunk(timestampUs);
-        }
+    // Add final entries only for non-empty tracks.
+    if (mStszTableEntries->count() > 0) {
+        if (mIsHeic) {
+            if (!mChunkSamples.empty()) {
+                bufferChunk(0);
+                ++nChunks;
+            }
+        } else {
+            // Last chunk
+            if (!hasMultipleTracks) {
+                addOneStscTableEntry(1, mStszTableEntries->count());
+            } else if (!mChunkSamples.empty()) {
+                addOneStscTableEntry(++nChunks, mChunkSamples.size());
+                bufferChunk(timestampUs);
+            }
 
-        // We don't really know how long the last frame lasts, since
-        // there is no frame time after it, just repeat the previous
-        // frame's duration.
-        if (mStszTableEntries->count() == 1) {
-            if (lastSampleDurationUs >= 0) {
-                addOneSttsTableEntry(sampleCount, lastSampleDurationTicks);
+            // We don't really know how long the last frame lasts, since
+            // there is no frame time after it, just repeat the previous
+            // frame's duration.
+            if (mStszTableEntries->count() == 1) {
+                if (lastSampleDurationUs >= 0) {
+                    addOneSttsTableEntry(sampleCount, lastSampleDurationTicks);
+                } else {
+                    lastDurationUs = 0;  // A single sample's duration
+                    lastDurationTicks = 0;
+                    addOneSttsTableEntry(sampleCount, lastDurationTicks);
+                }
+            } else if (lastSampleDurationUs >= 0) {
+                addOneSttsTableEntry(sampleCount, lastDurationTicks);
+                addOneSttsTableEntry(1, lastSampleDurationTicks);
             } else {
-                lastDurationUs = 0;  // A single sample's duration
-                lastDurationTicks = 0;
+                ++sampleCount;  // Count for the last sample
                 addOneSttsTableEntry(sampleCount, lastDurationTicks);
             }
-        } else if (lastSampleDurationUs >= 0) {
-            addOneSttsTableEntry(sampleCount, lastDurationTicks);
-            addOneSttsTableEntry(1, lastSampleDurationTicks);
-        } else {
-            ++sampleCount;  // Count for the last sample
-            addOneSttsTableEntry(sampleCount, lastDurationTicks);
-        }
 
-        // The last ctts box entry may not have been written yet, and this
-        // is to make sure that we write out the last ctts box entry.
-        if (currCttsOffsetTimeTicks == lastCttsOffsetTimeTicks) {
-            if (cttsSampleCount > 0) {
-                addOneCttsTableEntry(cttsSampleCount, lastCttsOffsetTimeTicks);
+            // The last ctts box entry may not have been written yet, and this
+            // is to make sure that we write out the last ctts box entry.
+            if (currCttsOffsetTimeTicks == lastCttsOffsetTimeTicks) {
+                if (cttsSampleCount > 0) {
+                    addOneCttsTableEntry(cttsSampleCount, lastCttsOffsetTimeTicks);
+                }
             }
-        }
-        if (lastSampleDurationUs >= 0) {
-            mTrackDurationUs += lastSampleDurationUs;
-        } else {
-            mTrackDurationUs += lastDurationUs;
+            if (lastSampleDurationUs >= 0) {
+                mTrackDurationUs += lastSampleDurationUs;
+            } else {
+                mTrackDurationUs += lastDurationUs;
+            }
         }
     }
     mReachedEOS = true;
@@ -3659,14 +3664,24 @@
         return true;
     }
 
-    if (!mIsHeic && mStszTableEntries->count() == 0) {  // no samples written
-        ALOGE("The number of recorded samples is 0");
-        return true;
-    }
-
-    if (mIsVideo && mStssTableEntries->count() == 0) {  // no sync frames for video
-        ALOGE("There are no sync frames for video track");
-        return true;
+    int32_t emptyTrackMalformed = false;
+    if (mOwner->mStartMeta &&
+        mOwner->mStartMeta->findInt32(kKeyEmptyTrackMalFormed, &emptyTrackMalformed) &&
+        emptyTrackMalformed) {
+        if (!mIsHeic && mStszTableEntries->count() == 0) {  // no samples written
+            ALOGE("The number of recorded samples is 0");
+            return true;
+        }
+        if (mIsVideo && mStssTableEntries->count() == 0) {  // no sync frames for video
+            ALOGE("There are no sync frames for video track");
+            return true;
+        }
+    } else {
+        // No sync frames for video.
+        if (mIsVideo && mStszTableEntries->count() > 0 && mStssTableEntries->count() == 0) {
+            ALOGE("There are no sync frames for video track");
+            return true;
+        }
     }
 
     if (OK != checkCodecSpecificData()) {         // no codec specific data
@@ -3915,25 +3930,28 @@
 
 void MPEG4Writer::Track::writeStblBox() {
     mOwner->beginBox("stbl");
-    mOwner->beginBox("stsd");
-    mOwner->writeInt32(0);               // version=0, flags=0
-    mOwner->writeInt32(1);               // entry count
-    if (mIsAudio) {
-        writeAudioFourCCBox();
-    } else if (mIsVideo) {
-        writeVideoFourCCBox();
-    } else {
-        writeMetadataFourCCBox();
+    // Add subboxes only for non-empty tracks.
+    if (mStszTableEntries->count() > 0) {
+        mOwner->beginBox("stsd");
+        mOwner->writeInt32(0);               // version=0, flags=0
+        mOwner->writeInt32(1);               // entry count
+        if (mIsAudio) {
+            writeAudioFourCCBox();
+        } else if (mIsVideo) {
+            writeVideoFourCCBox();
+        } else {
+            writeMetadataFourCCBox();
+        }
+        mOwner->endBox();  // stsd
+        writeSttsBox();
+        if (mIsVideo) {
+            writeCttsBox();
+            writeStssBox();
+        }
+        writeStszBox();
+        writeStscBox();
+        writeCo64Box();
     }
-    mOwner->endBox();  // stsd
-    writeSttsBox();
-    if (mIsVideo) {
-        writeCttsBox();
-        writeStssBox();
-    }
-    writeStszBox();
-    writeStscBox();
-    writeCo64Box();
     mOwner->endBox();  // stbl
 }
 
diff --git a/media/libstagefright/MediaMuxer.cpp b/media/libstagefright/MediaMuxer.cpp
index dbfc06b..1cb45ac 100644
--- a/media/libstagefright/MediaMuxer.cpp
+++ b/media/libstagefright/MediaMuxer.cpp
@@ -157,7 +157,6 @@
 
 status_t MediaMuxer::stop() {
     Mutex::Autolock autoLock(mMuxerLock);
-
     if (mState == STARTED || mState == ERROR) {
         mState = STOPPED;
         for (size_t i = 0; i < mTrackList.size(); i++) {
@@ -165,7 +164,10 @@
                 return INVALID_OPERATION;
             }
         }
+        // Unlock this mutex to allow notify to be called during stop process.
+        mMuxerLock.unlock();
         status_t err = mWriter->stop();
+        mMuxerLock.lock();
         if (err != OK || mError != OK) {
             ALOGE("stop err: %d, mError:%d", err, mError);
         }
diff --git a/media/libstagefright/include/media/stagefright/MetaDataBase.h b/media/libstagefright/include/media/stagefright/MetaDataBase.h
index 1820668..3b701f6 100644
--- a/media/libstagefright/include/media/stagefright/MetaDataBase.h
+++ b/media/libstagefright/include/media/stagefright/MetaDataBase.h
@@ -238,6 +238,9 @@
     kKeyOpaqueCSD2       = 'csd2',
 
     kKeyHapticChannelCount = 'hapC',
+
+    // Treat empty track as malformed for MediaRecorder.
+    kKeyEmptyTrackMalFormed = 'nemt', // bool (int32_t)
 };
 
 enum {