Transcoder: Preserve source track duration.

Preserve the source track's duration by sending an
EOS sample with the appropriate PTS to the muxer.
Modified MediaSampleWriterTests to verify the extra
EOS sample.

Bug: 159732935
Test: MediaTranscoder and MediaSampleWriter unit tests.
Change-Id: I6792eff8932db126e8734e5d3d6fe8aee2ffb4a1
diff --git a/media/libmediatranscoding/transcoder/MediaSampleWriter.cpp b/media/libmediatranscoding/transcoder/MediaSampleWriter.cpp
index 0fdb0b7..91dbf78 100644
--- a/media/libmediatranscoding/transcoder/MediaSampleWriter.cpp
+++ b/media/libmediatranscoding/transcoder/MediaSampleWriter.cpp
@@ -122,7 +122,12 @@
         return false;
     }
 
-    mTracks.emplace_back(sampleQueue, static_cast<size_t>(trackIndex));
+    int64_t durationUs;
+    if (!AMediaFormat_getInt64(trackFormat.get(), AMEDIAFORMAT_KEY_DURATION, &durationUs)) {
+        durationUs = 0;
+    }
+
+    mTracks.emplace_back(sampleQueue, static_cast<size_t>(trackIndex), durationUs);
     return true;
 }
 
@@ -200,10 +205,23 @@
                 } else if (sample->info.flags & SAMPLE_FLAG_END_OF_STREAM) {
                     // Track reached end of stream.
                     track.mReachedEos = true;
-                    break;
+
+                    // Preserve source track duration by setting the appropriate timestamp on the
+                    // empty End-Of-Stream sample.
+                    if (track.mDurationUs > 0 && track.mFirstSampleTimeSet) {
+                        sample->info.presentationTimeUs =
+                                track.mDurationUs + track.mFirstSampleTimeUs;
+                    }
+                } else {
+                    samplesLeft = true;
                 }
 
-                samplesLeft = true;
+                // Record the first sample's timestamp in order to translate duration to EOS time
+                // for tracks that does not start at 0.
+                if (!track.mFirstSampleTimeSet) {
+                    track.mFirstSampleTimeUs = sample->info.presentationTimeUs;
+                    track.mFirstSampleTimeSet = true;
+                }
 
                 bufferInfo.offset = sample->dataOffset;
                 bufferInfo.size = sample->info.size;
@@ -217,7 +235,7 @@
                     return status;
                 }
 
-            } while (sample->info.presentationTimeUs < segmentEndTimeUs);
+            } while (sample->info.presentationTimeUs < segmentEndTimeUs && !track.mReachedEos);
         }
 
         segmentEndTimeUs += mTrackSegmentLengthUs;
diff --git a/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp b/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp
index 4df3296..7b11ebb 100644
--- a/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp
+++ b/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp
@@ -372,6 +372,14 @@
         AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_ROTATION, rotation);
     }
 
+    // Transfer track duration.
+    // Preserve the source track duration by sending it to MediaSampleWriter.
+    int64_t durationUs;
+    if (AMediaFormat_getInt64(mSourceFormat.get(), AMEDIAFORMAT_KEY_DURATION, &durationUs) &&
+        durationUs > 0) {
+        AMediaFormat_setInt64(formatCopy, AMEDIAFORMAT_KEY_DURATION, durationUs);
+    }
+
     // TODO: transfer other fields as required.
 
     mActualOutputFormat = std::shared_ptr<AMediaFormat>(formatCopy, &AMediaFormat_delete);
diff --git a/media/libmediatranscoding/transcoder/include/media/MediaSampleWriter.h b/media/libmediatranscoding/transcoder/include/media/MediaSampleWriter.h
index da83167..d971f3e 100644
--- a/media/libmediatranscoding/transcoder/include/media/MediaSampleWriter.h
+++ b/media/libmediatranscoding/transcoder/include/media/MediaSampleWriter.h
@@ -144,11 +144,20 @@
     media_status_t runWriterLoop();
 
     struct TrackRecord {
-        TrackRecord(const std::shared_ptr<MediaSampleQueue>& sampleQueue, size_t trackIndex)
-              : mSampleQueue(sampleQueue), mTrackIndex(trackIndex), mReachedEos(false) {}
+        TrackRecord(const std::shared_ptr<MediaSampleQueue>& sampleQueue, size_t trackIndex,
+                    int64_t durationUs)
+              : mSampleQueue(sampleQueue),
+                mTrackIndex(trackIndex),
+                mDurationUs(durationUs),
+                mFirstSampleTimeUs(0),
+                mFirstSampleTimeSet(false),
+                mReachedEos(false) {}
 
         std::shared_ptr<MediaSampleQueue> mSampleQueue;
         const size_t mTrackIndex;
+        int64_t mDurationUs;
+        int64_t mFirstSampleTimeUs;
+        bool mFirstSampleTimeSet;
         bool mReachedEos;
     };
 
diff --git a/media/libmediatranscoding/transcoder/tests/MediaSampleWriterTests.cpp b/media/libmediatranscoding/transcoder/tests/MediaSampleWriterTests.cpp
index 98813e7..e3cb192 100644
--- a/media/libmediatranscoding/transcoder/tests/MediaSampleWriterTests.cpp
+++ b/media/libmediatranscoding/transcoder/tests/MediaSampleWriterTests.cpp
@@ -374,6 +374,16 @@
         EXPECT_EQ(event.info.flags, sample->info.flags);
     }
 
+    // Verify EOS samples.
+    for (int trackIndex = 0; trackIndex < kNumTracks; ++trackIndex) {
+        auto trackFormat = mediaSource.mTrackFormats[trackIndex % mediaSource.mTrackCount];
+        int64_t duration = 0;
+        AMediaFormat_getInt64(trackFormat.get(), AMEDIAFORMAT_KEY_DURATION, &duration);
+
+        const AMediaCodecBufferInfo info = {0, 0, duration, AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM};
+        EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::WriteSample(trackIndex, nullptr, &info));
+    }
+
     EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::Stop());
     EXPECT_TRUE(writer.stop());
 }
diff --git a/media/libmediatranscoding/transcoder/tests/MediaTranscoderTests.cpp b/media/libmediatranscoding/transcoder/tests/MediaTranscoderTests.cpp
index 67f1ca1..7cb435b 100644
--- a/media/libmediatranscoding/transcoder/tests/MediaTranscoderTests.cpp
+++ b/media/libmediatranscoding/transcoder/tests/MediaTranscoderTests.cpp
@@ -203,6 +203,7 @@
 
         std::shared_ptr<MediaSampleReader> sampleReader =
                 MediaSampleReaderNDK::createFromFd(dstFd, 0, fileSize);
+        ASSERT_NE(sampleReader, nullptr);
 
         std::shared_ptr<AMediaFormat> videoFormat;
         const size_t trackCount = sampleReader->getTrackCount();
@@ -211,6 +212,7 @@
             if (trackFormat != nullptr) {
                 const char* mime = nullptr;
                 AMediaFormat_getString(trackFormat, AMEDIAFORMAT_KEY_MIME, &mime);
+
                 if (strncmp(mime, "video/", 6) == 0) {
                     LOG(INFO) << "Track # " << trackIndex << ": "
                               << AMediaFormat_toString(trackFormat);