Merge "WriterTest: Add listener test"
diff --git a/media/libstagefright/tests/writer/Android.bp b/media/libstagefright/tests/writer/Android.bp
index 7e169cb..d058ed3 100644
--- a/media/libstagefright/tests/writer/Android.bp
+++ b/media/libstagefright/tests/writer/Android.bp
@@ -28,6 +28,7 @@
         "libcutils",
         "liblog",
         "libutils",
+        "libmedia",
     ],
 
     static_libs: [
diff --git a/media/libstagefright/tests/writer/WriterListener.h b/media/libstagefright/tests/writer/WriterListener.h
new file mode 100644
index 0000000..81f0a7c
--- /dev/null
+++ b/media/libstagefright/tests/writer/WriterListener.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef WRITER_LISTENER_H_
+#define WRITER_LISTENER_H_
+
+#include <mutex>
+
+#include <media/IMediaRecorderClient.h>
+#include <media/mediarecorder.h>
+
+using namespace android;
+using namespace std;
+
+class WriterListener : public BnMediaRecorderClient {
+  public:
+    WriterListener() : mSignaledSize(false), mSignaledDuration(false) {}
+
+    virtual void notify(int32_t msg, int32_t ext1, int32_t ext2) {
+        ALOGV("msg : %d, ext1 : %d, ext2 : %d", msg, ext1, ext2);
+        if (ext1 == MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED) {
+            mSignaledSize = true;
+        } else if (ext1 == MEDIA_RECORDER_INFO_MAX_DURATION_REACHED) {
+            mSignaledDuration = true;
+        }
+    }
+
+    volatile bool mSignaledSize;
+    volatile bool mSignaledDuration;
+};
+
+#endif  // WRITER_LISTENER_H_
diff --git a/media/libstagefright/tests/writer/WriterTest.cpp b/media/libstagefright/tests/writer/WriterTest.cpp
index ff063e3..3fa2aa6 100644
--- a/media/libstagefright/tests/writer/WriterTest.cpp
+++ b/media/libstagefright/tests/writer/WriterTest.cpp
@@ -87,36 +87,37 @@
          "bbb_mpeg4_352x288_512kbps_30fps.info", 352, 288, false},
 };
 
-class WriterTest : public ::testing::TestWithParam<pair<string, int32_t>> {
+class WriterTest {
   public:
     WriterTest() : mWriter(nullptr), mFileMeta(nullptr), mCurrentTrack(nullptr) {}
 
     ~WriterTest() {
-        if (mWriter) {
-            mWriter.clear();
-            mWriter = nullptr;
-        }
         if (mFileMeta) {
             mFileMeta.clear();
             mFileMeta = nullptr;
         }
         if (mCurrentTrack) {
+            mCurrentTrack->stop();
             mCurrentTrack.clear();
             mCurrentTrack = nullptr;
         }
+        if (mWriter) {
+            mWriter.clear();
+            mWriter = nullptr;
+        }
+        mBufferInfo.clear();
+        if (mInputStream.is_open()) mInputStream.close();
     }
 
-    virtual void SetUp() override {
+    void setupWriterType(string writerFormat) {
         mNumCsds = 0;
         mInputFrameId = 0;
         mWriterName = unknown_comp;
         mDisableTest = false;
-
         static const std::map<std::string, standardWriters> mapWriter = {
                 {"ogg", OGG},     {"aac", AAC},      {"aac_adts", AAC_ADTS}, {"webm", WEBM},
                 {"mpeg4", MPEG4}, {"amrnb", AMR_NB}, {"amrwb", AMR_WB},      {"mpeg2Ts", MPEG2TS}};
         // Find the component type
-        string writerFormat = GetParam().first;
         if (mapWriter.find(writerFormat) != mapWriter.end()) {
             mWriterName = mapWriter.at(writerFormat);
         }
@@ -126,11 +127,6 @@
         }
     }
 
-    virtual void TearDown() override {
-        mBufferInfo.clear();
-        if (mInputStream.is_open()) mInputStream.close();
-    }
-
     void getInputBufferInfo(string inputFileName, string inputInfo);
 
     int32_t createWriter(int32_t fd);
@@ -161,6 +157,12 @@
     vector<BufferInfo> mBufferInfo;
 };
 
+class WriteFunctionalityTest : public WriterTest,
+                               public ::testing::TestWithParam<pair<string, int32_t>> {
+  public:
+    virtual void SetUp() override { setupWriterType(GetParam().first); }
+};
+
 void WriterTest::getInputBufferInfo(string inputFileName, string inputInfo) {
     std::ifstream eleInfo;
     eleInfo.open(inputInfo.c_str());
@@ -270,7 +272,7 @@
     return;
 }
 
-TEST_P(WriterTest, CreateWriterTest) {
+TEST_P(WriteFunctionalityTest, CreateWriterTest) {
     if (mDisableTest) return;
     ALOGV("Tests the creation of writers");
 
@@ -284,7 +286,7 @@
             << "Failed to create writer for output format:" << GetParam().first;
 }
 
-TEST_P(WriterTest, WriterTest) {
+TEST_P(WriteFunctionalityTest, WriterTest) {
     if (mDisableTest) return;
     ALOGV("Checks if for a given input, a valid muxed file has been created or not");
 
@@ -321,7 +323,7 @@
     close(fd);
 }
 
-TEST_P(WriterTest, PauseWriterTest) {
+TEST_P(WriteFunctionalityTest, PauseWriterTest) {
     if (mDisableTest) return;
     ALOGV("Validates the pause() api of writers");
 
@@ -378,7 +380,7 @@
     close(fd);
 }
 
-TEST_P(WriterTest, MultiStartStopPauseTest) {
+TEST_P(WriteFunctionalityTest, MultiStartStopPauseTest) {
     // TODO: (b/144821804)
     // Enable the test for MPE2TS writer
     if (mDisableTest || mWriterName == standardWriters::MPEG2TS) return;
@@ -451,9 +453,112 @@
     close(fd);
 }
 
+class ListenerTest : public WriterTest,
+                     public ::testing::TestWithParam<
+                             tuple<string /* writerFormat*/, int32_t /* inputFileIdx*/,
+                                   float /* FileSizeLimit*/, float /* FileDurationLimit*/>> {
+  public:
+    virtual void SetUp() override {
+        tuple<string, int32_t, float, float> params = GetParam();
+        setupWriterType(get<0>(params));
+    }
+};
+
+TEST_P(ListenerTest, SetMaxFileLimitsTest) {
+    if (mDisableTest) return;
+    ALOGV("Validates writer when max file limits are set");
+
+    tuple<string, int32_t, float, float> params = GetParam();
+    string writerFormat = get<0>(params);
+    string outputFile = OUTPUT_FILE_NAME;
+    int32_t fd =
+            open(outputFile.c_str(), O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
+    ASSERT_GE(fd, 0) << "Failed to open output file to dump writer's data";
+
+    int32_t status = createWriter(fd);
+    ASSERT_EQ((status_t)OK, status) << "Failed to create writer for output format:" << writerFormat;
+
+    string inputFile = gEnv->getRes();
+    string inputInfo = gEnv->getRes();
+    configFormat param;
+    bool isAudio;
+    int32_t inputFileIdx = get<1>(params);
+    getFileDetails(inputFile, inputInfo, param, isAudio, inputFileIdx);
+    ASSERT_NE(inputFile.compare(gEnv->getRes()), 0) << "No input file specified";
+
+    ASSERT_NO_FATAL_FAILURE(getInputBufferInfo(inputFile, inputInfo));
+    status = addWriterSource(isAudio, param);
+    ASSERT_EQ((status_t)OK, status) << "Failed to add source for " << writerFormat << "Writer";
+
+    // Read file properties
+    struct stat buf;
+    status = stat(inputFile.c_str(), &buf);
+    ASSERT_EQ(0, status);
+
+    float fileSizeLimit = get<2>(params);
+    float fileDurationLimit = get<3>(params);
+    int64_t maxFileSize = 0;
+    int64_t maxFileDuration = 0;
+
+    size_t inputFileSize = buf.st_size;
+    int64_t lastFrameTimeStampUs = mBufferInfo[mBufferInfo.size() - 1].timeUs;
+    if (fileSizeLimit > 0) {
+        maxFileSize = (int64_t)(fileSizeLimit * inputFileSize);
+        mWriter->setMaxFileSize(maxFileSize);
+    }
+    if (fileDurationLimit > 0) {
+        maxFileDuration = (int64_t)(fileDurationLimit * lastFrameTimeStampUs);
+        mWriter->setMaxFileDuration(maxFileDuration);
+    }
+
+    sp<WriterListener> listener = new WriterListener();
+    ASSERT_NE(listener, nullptr) << "unable to allocate listener";
+
+    mWriter->setListener(listener);
+    status = mWriter->start(mFileMeta.get());
+
+    ASSERT_EQ((status_t)OK, status);
+    status = sendBuffersToWriter(mInputStream, mBufferInfo, mInputFrameId, mCurrentTrack, 0,
+                                 mBufferInfo.size(), false, listener);
+    ASSERT_EQ((status_t)OK, status) << writerFormat << " writer failed";
+    ASSERT_TRUE(mWriter->reachedEOS()) << "EOS not signalled.";
+
+    mCurrentTrack->stop();
+    status = mWriter->stop();
+    ASSERT_EQ((status_t)OK, status) << "Failed to stop the writer";
+    close(fd);
+
+    if (maxFileSize <= 0) {
+        ASSERT_FALSE(listener->mSignaledSize);
+    } else if (maxFileDuration <= 0) {
+        ASSERT_FALSE(listener->mSignaledDuration);
+    } else if (maxFileSize > 0 && maxFileDuration <= 0) {
+        ASSERT_TRUE(listener->mSignaledSize);
+    } else if (maxFileDuration > 0 && maxFileSize <= 0) {
+        ASSERT_TRUE(listener->mSignaledDuration);
+    } else {
+        ASSERT_TRUE(listener->mSignaledSize || listener->mSignaledDuration);
+    }
+
+    if (maxFileSize > 0) {
+        struct stat buf;
+        status = stat(outputFile.c_str(), &buf);
+        ASSERT_EQ(0, status);
+        ASSERT_LE(buf.st_size, maxFileSize);
+    }
+}
+
+// TODO: (b/150923387)
+// Add WEBM input
+INSTANTIATE_TEST_SUITE_P(
+        ListenerTestAll, ListenerTest,
+        ::testing::Values(make_tuple("ogg", 0, 0.7, 0.3), make_tuple("aac", 1, 0.6, 0.7),
+                          make_tuple("mpeg4", 1, 0.4, 0.3), make_tuple("amrnb", 3, 0.2, 0.6),
+                          make_tuple("amrwb", 4, 0.5, 0.5), make_tuple("mpeg2Ts", 1, 0.2, 1)));
+
 // TODO: (b/144476164)
 // Add AAC_ADTS, FLAC, AV1 input
-INSTANTIATE_TEST_SUITE_P(WriterTestAll, WriterTest,
+INSTANTIATE_TEST_SUITE_P(WriterTestAll, WriteFunctionalityTest,
                          ::testing::Values(make_pair("ogg", 0), make_pair("webm", 0),
                                            make_pair("aac", 1), make_pair("mpeg4", 1),
                                            make_pair("amrnb", 3), make_pair("amrwb", 4),
diff --git a/media/libstagefright/tests/writer/WriterUtility.cpp b/media/libstagefright/tests/writer/WriterUtility.cpp
index f24ccb6..a3043fe 100644
--- a/media/libstagefright/tests/writer/WriterUtility.cpp
+++ b/media/libstagefright/tests/writer/WriterUtility.cpp
@@ -24,9 +24,16 @@
 
 int32_t sendBuffersToWriter(ifstream &inputStream, vector<BufferInfo> &bufferInfo,
                             int32_t &inputFrameId, sp<MediaAdapter> &currentTrack, int32_t offset,
-                            int32_t range, bool isPaused) {
+                            int32_t range, bool isPaused, sp<WriterListener> listener) {
     while (1) {
         if (inputFrameId >= (int)bufferInfo.size() || inputFrameId >= (offset + range)) break;
+        if (listener != nullptr) {
+            if (listener->mSignaledDuration || listener->mSignaledSize) {
+                ALOGV("Max File limit reached. No more buffers will be sent to the writer");
+                break;
+            }
+        }
+
         int32_t size = bufferInfo[inputFrameId].size;
         char *data = (char *)malloc(size);
         if (!data) {
diff --git a/media/libstagefright/tests/writer/WriterUtility.h b/media/libstagefright/tests/writer/WriterUtility.h
index cdd6246..5e19973 100644
--- a/media/libstagefright/tests/writer/WriterUtility.h
+++ b/media/libstagefright/tests/writer/WriterUtility.h
@@ -27,8 +27,7 @@
 
 #include <media/stagefright/MediaAdapter.h>
 
-using namespace android;
-using namespace std;
+#include "WriterListener.h"
 
 #define CODEC_CONFIG_FLAG 32
 
@@ -43,7 +42,8 @@
 
 int32_t sendBuffersToWriter(ifstream &inputStream, vector<BufferInfo> &bufferInfo,
                             int32_t &inputFrameId, sp<MediaAdapter> &currentTrack, int32_t offset,
-                            int32_t range, bool isPaused = false);
+                            int32_t range, bool isPaused = false,
+                            sp<WriterListener> listener = nullptr);
 
 int32_t writeHeaderBuffers(ifstream &inputStream, vector<BufferInfo> &bufferInfo,
                            int32_t &inputFrameId, sp<AMessage> &format, int32_t numCsds);