Merge "MediaTesting: Add Writer Test"
diff --git a/media/libstagefright/tests/writer/Android.bp b/media/libstagefright/tests/writer/Android.bp
new file mode 100644
index 0000000..7e169cb
--- /dev/null
+++ b/media/libstagefright/tests/writer/Android.bp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+cc_test {
+    name: "writerTest",
+    gtest: true,
+
+    srcs: [
+        "WriterUtility.cpp",
+        "WriterTest.cpp",
+    ],
+
+    shared_libs: [
+        "libbinder",
+        "libcutils",
+        "liblog",
+        "libutils",
+    ],
+
+    static_libs: [
+        "libstagefright_webm",
+        "libdatasource",
+        "libstagefright",
+        "libstagefright_foundation",
+        "libstagefright_esds",
+        "libogg",
+    ],
+
+    include_dirs: [
+        "frameworks/av/media/libstagefright",
+    ],
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+    ],
+
+    sanitize: {
+        cfi: true,
+        misc_undefined: [
+            "unsigned-integer-overflow",
+            "signed-integer-overflow",
+        ],
+    },
+}
diff --git a/media/libstagefright/tests/writer/README.md b/media/libstagefright/tests/writer/README.md
new file mode 100644
index 0000000..52db6f0
--- /dev/null
+++ b/media/libstagefright/tests/writer/README.md
@@ -0,0 +1,30 @@
+## Media Testing ##
+---
+#### Writer :
+The Writer Test Suite validates the writers available in libstagefright.
+
+Run the following steps to build the test suite:
+```
+mmm frameworks/av/media/libstagefright/tests/writer/
+```
+
+The 32-bit binaries will be created in the following path : ${OUT}/data/nativetest/
+The 64-bit binaries will be created in the following path : ${OUT}/data/nativetest64/
+
+To test 64-bit binary push binaries from nativetest64.
+
+adb push ${OUT}/data/nativetest64/writerTest/writerTest /data/local/tmp/
+
+To test 32-bit binary push binaries from nativetest.
+
+adb push ${OUT}/data/nativetest/writerTest/writerTest /data/local/tmp/
+
+The resource file for the tests is taken from Codec2 VTS resource folder. Push these files into device for testing.
+```
+adb push  $ANDROID_BUILD_TOP/frameworks/av/media/codec2/hidl/1.0/vts/functional/res /sdcard/
+```
+
+usage: writerTest -P \<path_to_res_folder\>
+```
+adb shell /data/local/tmp/writerTest -P /sdcard/res/
+```
diff --git a/media/libstagefright/tests/writer/WriterTest.cpp b/media/libstagefright/tests/writer/WriterTest.cpp
new file mode 100644
index 0000000..7f19e23
--- /dev/null
+++ b/media/libstagefright/tests/writer/WriterTest.cpp
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "WriterTest"
+#include <utils/Log.h>
+
+#include <fstream>
+#include <iostream>
+
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
+
+#include <media/mediarecorder.h>
+
+#include <media/stagefright/AACWriter.h>
+#include <media/stagefright/AMRWriter.h>
+#include <media/stagefright/OggWriter.h>
+#include <media/stagefright/MPEG4Writer.h>
+#include <media/stagefright/MPEG2TSWriter.h>
+#include <webm/WebmWriter.h>
+
+#include "WriterTestEnvironment.h"
+#include "WriterUtility.h"
+
+#define OUTPUT_FILE_NAME "/data/local/tmp/writer.out"
+
+static WriterTestEnvironment *gEnv = nullptr;
+
+struct configFormat {
+    char mime[128];
+    int32_t width;
+    int32_t height;
+    int32_t sampleRate;
+    int32_t channelCount;
+};
+
+// LookUpTable of clips and metadata for component testing
+static const struct InputData {
+    const char *mime;
+    string inputFile;
+    string info;
+    int32_t firstParam;
+    int32_t secondParam;
+    bool isAudio;
+} kInputData[] = {
+        {MEDIA_MIMETYPE_AUDIO_OPUS, "bbb_opus_stereo_128kbps_48000hz.opus",
+         "bbb_opus_stereo_128kbps_48000hz.info", 48000, 2, true},
+        {MEDIA_MIMETYPE_AUDIO_AAC, "bbb_aac_stereo_128kbps_48000hz.aac",
+         "bbb_aac_stereo_128kbps_48000hz.info", 48000, 2, true},
+        {MEDIA_MIMETYPE_AUDIO_AAC_ADTS, "Mps_2_c2_fr1_Sc1_Dc2_0x03_raw.adts",
+         "Mps_2_c2_fr1_Sc1_Dc2_0x03_raw.info", 48000, 2, true},
+        {MEDIA_MIMETYPE_AUDIO_AMR_NB, "sine_amrnb_1ch_12kbps_8000hz.amrnb",
+         "sine_amrnb_1ch_12kbps_8000hz.info", 8000, 1, true},
+        {MEDIA_MIMETYPE_AUDIO_AMR_WB, "bbb_amrwb_1ch_14kbps_16000hz.amrwb",
+         "bbb_amrwb_1ch_14kbps_16000hz.info", 16000, 1, true},
+        {MEDIA_MIMETYPE_AUDIO_VORBIS, "bbb_vorbis_stereo_128kbps_48000hz.vorbis",
+         "bbb_vorbis_stereo_128kbps_48000hz.info", 48000, 2, true},
+        {MEDIA_MIMETYPE_AUDIO_FLAC, "bbb_flac_stereo_680kbps_48000hz.flac",
+         "bbb_flac_stereo_680kbps_48000hz.info", 48000, 2, true},
+        {MEDIA_MIMETYPE_VIDEO_VP9, "bbb_vp9_176x144_285kbps_60fps.vp9",
+         "bbb_vp9_176x144_285kbps_60fps.info", 176, 144, false},
+        {MEDIA_MIMETYPE_VIDEO_VP8, "bbb_vp8_176x144_240kbps_60fps.vp8",
+         "bbb_vp8_176x144_240kbps_60fps.info", 176, 144, false},
+        {MEDIA_MIMETYPE_VIDEO_AVC, "bbb_avc_176x144_300kbps_60fps.h264",
+         "bbb_avc_176x144_300kbps_60fps.info", 176, 144, false},
+        {MEDIA_MIMETYPE_VIDEO_HEVC, "bbb_hevc_176x144_176kbps_60fps.hevc",
+         "bbb_hevc_176x144_176kbps_60fps.info", 176, 144, false},
+        {MEDIA_MIMETYPE_VIDEO_AV1, "bbb_av1_176_144.av1", "bbb_av1_176_144.info", 176, 144, false},
+        {MEDIA_MIMETYPE_VIDEO_H263, "bbb_h263_352x288_300kbps_12fps.h263",
+         "bbb_h263_352x288_300kbps_12fps.info", 352, 288, false},
+        {MEDIA_MIMETYPE_VIDEO_MPEG4, "bbb_mpeg4_352x288_512kbps_30fps.m4v",
+         "bbb_mpeg4_352x288_512kbps_30fps.info", 352, 288, false},
+};
+
+class WriterTest : public ::testing::TestWithParam<pair<string, int32_t>> {
+  public:
+    virtual void SetUp() override {
+        mNumCsds = 0;
+        mInputFrameId = 0;
+        mWriterName = unknown_comp;
+        mDisableTest = false;
+
+        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[writerFormat];
+        }
+        if (mWriterName == standardWriters::unknown_comp) {
+            cout << "[   WARN   ] Test Skipped. No specific writer mentioned\n";
+            mDisableTest = true;
+        }
+    }
+
+    virtual void TearDown() override {
+        mWriter.clear();
+        mFileMeta.clear();
+        mBufferInfo.clear();
+        if (mInputStream) mInputStream.close();
+    }
+
+    void getInputBufferInfo(string inputFileName, string inputInfo);
+
+    int32_t createWriter(int32_t fd);
+
+    int32_t addWriterSource(bool isAudio, configFormat params);
+
+    enum standardWriters {
+        OGG,
+        AAC,
+        AAC_ADTS,
+        WEBM,
+        MPEG4,
+        AMR_NB,
+        AMR_WB,
+        MPEG2TS,
+        unknown_comp,
+    };
+
+    standardWriters mWriterName;
+    sp<MediaWriter> mWriter;
+    sp<MetaData> mFileMeta;
+    sp<MediaAdapter> mCurrentTrack;
+
+    bool mDisableTest;
+    int32_t mNumCsds;
+    int32_t mInputFrameId;
+    ifstream mInputStream;
+    vector<BufferInfo> mBufferInfo;
+};
+
+void WriterTest::getInputBufferInfo(string inputFileName, string inputInfo) {
+    std::ifstream eleInfo;
+    eleInfo.open(inputInfo.c_str());
+    CHECK_EQ(eleInfo.is_open(), true);
+    int32_t bytesCount = 0;
+    uint32_t flags = 0;
+    int64_t timestamp = 0;
+    while (1) {
+        if (!(eleInfo >> bytesCount)) break;
+        eleInfo >> flags;
+        eleInfo >> timestamp;
+        mBufferInfo.push_back({bytesCount, flags, timestamp});
+        if (flags == CODEC_CONFIG_FLAG) mNumCsds++;
+    }
+    eleInfo.close();
+    mInputStream.open(inputFileName.c_str(), std::ifstream::binary);
+    CHECK_EQ(mInputStream.is_open(), true);
+}
+
+int32_t WriterTest::createWriter(int32_t fd) {
+    mFileMeta = new MetaData;
+    switch (mWriterName) {
+        case OGG:
+            mWriter = new OggWriter(fd);
+            mFileMeta->setInt32(kKeyFileType, output_format::OUTPUT_FORMAT_OGG);
+            break;
+        case AAC:
+            mWriter = new AACWriter(fd);
+            mFileMeta->setInt32(kKeyFileType, output_format::OUTPUT_FORMAT_AAC_ADIF);
+            break;
+        case AAC_ADTS:
+            mWriter = new AACWriter(fd);
+            mFileMeta->setInt32(kKeyFileType, output_format::OUTPUT_FORMAT_AAC_ADTS);
+            break;
+        case WEBM:
+            mWriter = new WebmWriter(fd);
+            mFileMeta->setInt32(kKeyFileType, output_format::OUTPUT_FORMAT_WEBM);
+            break;
+        case MPEG4:
+            mWriter = new MPEG4Writer(fd);
+            mFileMeta->setInt32(kKeyFileType, output_format::OUTPUT_FORMAT_MPEG_4);
+            break;
+        case AMR_NB:
+            mWriter = new AMRWriter(fd);
+            mFileMeta->setInt32(kKeyFileType, output_format::OUTPUT_FORMAT_AMR_NB);
+            break;
+        case AMR_WB:
+            mWriter = new AMRWriter(fd);
+            mFileMeta->setInt32(kKeyFileType, output_format::OUTPUT_FORMAT_AMR_WB);
+            break;
+        case MPEG2TS:
+            mWriter = new MPEG2TSWriter(fd);
+            mFileMeta->setInt32(kKeyFileType, output_format::OUTPUT_FORMAT_MPEG2TS);
+            break;
+        default:
+            return -1;
+    }
+    if (mWriter == nullptr) return -1;
+    mFileMeta->setInt32(kKeyRealTimeRecording, false);
+    return 0;
+}
+
+int32_t WriterTest::addWriterSource(bool isAudio, configFormat params) {
+    if (mInputFrameId) return -1;
+    sp<AMessage> format = new AMessage;
+    if (mInputStream.is_open()) {
+        format->setString("mime", params.mime);
+        if (isAudio) {
+            format->setInt32("channel-count", params.channelCount);
+            format->setInt32("sample-rate", params.sampleRate);
+        } else {
+            format->setInt32("width", params.width);
+            format->setInt32("height", params.height);
+        }
+
+        int32_t status =
+                writeHeaderBuffers(mInputStream, mBufferInfo, mInputFrameId, format, mNumCsds);
+        if (status != 0) return -1;
+    }
+    sp<MetaData> trackMeta = new MetaData;
+    convertMessageToMetaData(format, trackMeta);
+    mCurrentTrack = new MediaAdapter(trackMeta);
+    status_t result = mWriter->addSource(mCurrentTrack);
+    return result;
+}
+
+void getFileDetails(string &inputFilePath, string &info, configFormat &params, bool &isAudio,
+                    int32_t streamIndex = 0) {
+    if (streamIndex > sizeof(kInputData) / sizeof(kInputData[0])) {
+        return;
+    }
+    inputFilePath += kInputData[streamIndex].inputFile;
+    info += kInputData[streamIndex].info;
+    strcpy(params.mime, kInputData[streamIndex].mime);
+    isAudio = kInputData[streamIndex].isAudio;
+    if (isAudio) {
+        params.sampleRate = kInputData[streamIndex].firstParam;
+        params.channelCount = kInputData[streamIndex].secondParam;
+    } else {
+        params.width = kInputData[streamIndex].firstParam;
+        params.height = kInputData[streamIndex].secondParam;
+    }
+    return;
+}
+
+TEST_P(WriterTest, CreateWriterTest) {
+    if (mDisableTest) return;
+    ALOGV("Tests the creation of writers");
+
+    string outputFile = OUTPUT_FILE_NAME;
+    int32_t fd =
+            open(outputFile.c_str(), O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
+    if (fd < 0) return;
+
+    // Creating writer within a test scope. Destructor should be called when the test ends
+    int32_t status = createWriter(fd);
+    if (status) {
+        cout << "Failed to create writer for output format:" << GetParam().first << "\n";
+        ASSERT_TRUE(false);
+    }
+}
+
+TEST_P(WriterTest, WriterTest) {
+    if (mDisableTest) return;
+    ALOGV("Checks if for a given input, a valid muxed file has been created or not");
+
+    string writerFormat = GetParam().first;
+    string outputFile = OUTPUT_FILE_NAME;
+    int32_t fd =
+            open(outputFile.c_str(), O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
+    if (fd < 0) return;
+    int32_t status = createWriter(fd);
+    if (status) {
+        cout << "Failed to create writer for output format:" << writerFormat << "\n";
+        ASSERT_TRUE(false);
+    }
+    string inputFile = gEnv->getRes();
+    string inputInfo = gEnv->getRes();
+    configFormat param;
+    bool isAudio;
+    int32_t inputFileIdx = GetParam().second;
+    getFileDetails(inputFile, inputInfo, param, isAudio, inputFileIdx);
+    if (!inputFile.compare(gEnv->getRes())) {
+        ALOGV("No input file specified");
+        return;
+    }
+    getInputBufferInfo(inputFile, inputInfo);
+    status = addWriterSource(isAudio, param);
+    if (status) {
+        cout << "Failed to add source for " << writerFormat << "Writer \n";
+        ASSERT_TRUE(false);
+    }
+    CHECK_EQ((status_t)OK, mWriter->start(mFileMeta.get()));
+    status = sendBuffersToWriter(mInputStream, mBufferInfo, mInputFrameId, mCurrentTrack, 0,
+                                 mBufferInfo.size());
+    mCurrentTrack->stop();
+    if (status) {
+        cout << writerFormat << " writer failed \n";
+        mWriter->stop();
+        ASSERT_TRUE(false);
+    }
+    CHECK_EQ((status_t)OK, mWriter->stop());
+    close(fd);
+}
+
+TEST_P(WriterTest, PauseWriterTest) {
+    if (mDisableTest) return;
+    ALOGV("Validates the pause() api of writers");
+
+    string writerFormat = GetParam().first;
+    string outputFile = OUTPUT_FILE_NAME;
+    int32_t fd =
+            open(outputFile.c_str(), O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
+    if (fd < 0) return;
+    int32_t status = createWriter(fd);
+    if (status) {
+        cout << "Failed to create writer for output format:" << writerFormat << "\n";
+        ASSERT_TRUE(false);
+    }
+    string inputFile = gEnv->getRes();
+    string inputInfo = gEnv->getRes();
+    configFormat param;
+    bool isAudio;
+    int32_t inputFileIdx = GetParam().second;
+    getFileDetails(inputFile, inputInfo, param, isAudio, inputFileIdx);
+    if (!inputFile.compare(gEnv->getRes())) {
+        ALOGV("No input file specified");
+        return;
+    }
+    getInputBufferInfo(inputFile, inputInfo);
+    status = addWriterSource(isAudio, param);
+    if (status) {
+        cout << "Failed to add source for " << writerFormat << "Writer \n";
+        ASSERT_TRUE(false);
+    }
+    CHECK_EQ((status_t)OK, mWriter->start(mFileMeta.get()));
+    status = sendBuffersToWriter(mInputStream, mBufferInfo, mInputFrameId, mCurrentTrack, 0,
+                                 mBufferInfo.size() / 4);
+    if (status) {
+        cout << writerFormat << " writer failed \n";
+        mCurrentTrack->stop();
+        mWriter->stop();
+        ASSERT_TRUE(false);
+    }
+
+    bool isPaused = false;
+    if ((mWriterName != standardWriters::MPEG2TS) && (mWriterName != standardWriters::MPEG4)) {
+        CHECK_EQ((status_t)OK, mWriter->pause());
+        isPaused = true;
+    }
+    // In the pause state, writers shouldn't write anything. Testing the writers for the same
+    int32_t numFramesPaused = mBufferInfo.size() / 4;
+    status |= sendBuffersToWriter(mInputStream, mBufferInfo, mInputFrameId, mCurrentTrack,
+                                  mInputFrameId, numFramesPaused, isPaused);
+    if (isPaused) {
+        CHECK_EQ((status_t)OK, mWriter->start(mFileMeta.get()));
+    }
+    status |= sendBuffersToWriter(mInputStream, mBufferInfo, mInputFrameId, mCurrentTrack,
+                                  mInputFrameId, mBufferInfo.size());
+    mCurrentTrack->stop();
+    if (status) {
+        cout << writerFormat << " writer failed \n";
+        mWriter->stop();
+        ASSERT_TRUE(false);
+    }
+    CHECK_EQ((status_t)OK, mWriter->stop());
+    close(fd);
+}
+
+// TODO: (b/144476164)
+// Add AAC_ADTS, FLAC, AV1 input
+INSTANTIATE_TEST_SUITE_P(WriterTestAll, WriterTest,
+                         ::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),
+                                           make_pair("webm", 5), make_pair("webm", 7),
+                                           make_pair("webm", 8), make_pair("mpeg4", 9),
+                                           make_pair("mpeg4", 10), make_pair("mpeg4", 12),
+                                           make_pair("mpeg4", 13), make_pair("mpeg2Ts", 1),
+                                           make_pair("mpeg2Ts", 9)));
+
+int main(int argc, char **argv) {
+    gEnv = new WriterTestEnvironment();
+    ::testing::AddGlobalTestEnvironment(gEnv);
+    ::testing::InitGoogleTest(&argc, argv);
+    int status = gEnv->initFromOptions(argc, argv);
+    if (status == 0) {
+        status = RUN_ALL_TESTS();
+        ALOGV("Test result = %d\n", status);
+    }
+    return status;
+}
diff --git a/media/libstagefright/tests/writer/WriterTestEnvironment.h b/media/libstagefright/tests/writer/WriterTestEnvironment.h
new file mode 100644
index 0000000..34c2baa
--- /dev/null
+++ b/media/libstagefright/tests/writer/WriterTestEnvironment.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 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_TEST_ENVIRONMENT_H__
+#define __WRITER_TEST_ENVIRONMENT_H__
+
+#include <gtest/gtest.h>
+
+#include <getopt.h>
+
+using namespace std;
+
+class WriterTestEnvironment : public ::testing::Environment {
+  public:
+    WriterTestEnvironment() : res("/sdcard/media/") {}
+
+    // Parses the command line arguments
+    int initFromOptions(int argc, char **argv);
+
+    void setRes(const char *_res) { res = _res; }
+
+    const string getRes() const { return res; }
+
+  private:
+    string res;
+};
+
+int WriterTestEnvironment::initFromOptions(int argc, char **argv) {
+    static struct option options[] = {{"res", required_argument, 0, 'P'}, {0, 0, 0, 0}};
+
+    while (true) {
+        int index = 0;
+        int c = getopt_long(argc, argv, "P:", options, &index);
+        if (c == -1) {
+            break;
+        }
+
+        switch (c) {
+            case 'P':
+                setRes(optarg);
+                break;
+            default:
+                break;
+        }
+    }
+
+    if (optind < argc) {
+        fprintf(stderr,
+                "unrecognized option: %s\n\n"
+                "usage: %s <gtest options> <test options>\n\n"
+                "test options are:\n\n"
+                "-P, --path: Resource files directory location\n",
+                argv[optind ?: 1], argv[0]);
+        return 2;
+    }
+    return 0;
+}
+
+#endif  // __WRITER_TEST_ENVIRONMENT_H__
diff --git a/media/libstagefright/tests/writer/WriterUtility.cpp b/media/libstagefright/tests/writer/WriterUtility.cpp
new file mode 100644
index 0000000..2ba90a0
--- /dev/null
+++ b/media/libstagefright/tests/writer/WriterUtility.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "WriterUtility"
+#include <utils/Log.h>
+
+#include <media/stagefright/MediaBuffer.h>
+
+#include "WriterUtility.h"
+
+int32_t sendBuffersToWriter(ifstream &inputStream, vector<BufferInfo> &bufferInfo,
+                            int32_t &inputFrameId, sp<MediaAdapter> &currentTrack, int32_t offset,
+                            int32_t range, bool isPaused) {
+    while (1) {
+        if (inputFrameId == (int)bufferInfo.size() || inputFrameId >= (offset + range)) break;
+        int32_t size = bufferInfo[inputFrameId].size;
+        char *data = (char *)malloc(size);
+        if (!data) {
+            ALOGE("Insufficient memeory to read input");
+            return -1;
+        }
+
+        inputStream.read(data, size);
+        CHECK_EQ(inputStream.gcount(), size);
+
+        sp<ABuffer> buffer = new ABuffer((void *)data, size);
+        if (buffer.get() == nullptr) {
+            ALOGE("sendBuffersToWriter() got a nullptr buffer.");
+            return -1;
+        }
+        MediaBuffer *mediaBuffer = new MediaBuffer(buffer);
+
+        // Released in MediaAdapter::signalBufferReturned().
+        mediaBuffer->add_ref();
+        mediaBuffer->set_range(buffer->offset(), buffer->size());
+
+        MetaDataBase &sampleMetaData = mediaBuffer->meta_data();
+        sampleMetaData.setInt64(kKeyTime, bufferInfo[inputFrameId].timeUs);
+        // Just set the kKeyDecodingTime as the presentation time for now.
+        sampleMetaData.setInt64(kKeyDecodingTime, bufferInfo[inputFrameId].timeUs);
+
+        if (bufferInfo[inputFrameId].flags == 1) {
+            sampleMetaData.setInt32(kKeyIsSyncFrame, true);
+        }
+
+        // This pushBuffer will wait until the mediaBuffer is consumed.
+        int status = currentTrack->pushBuffer(mediaBuffer);
+        free(data);
+        inputFrameId++;
+
+        if (OK != status) {
+            if (!isPaused) return status;
+            else {
+                ALOGD("Writer is in paused state. Input buffers won't get consumed");
+                return 0;
+            }
+        }
+    }
+    return 0;
+}
+
+int32_t writeHeaderBuffers(ifstream &inputStream, vector<BufferInfo> &bufferInfo,
+                           int32_t &inputFrameId, sp<AMessage> &format, int32_t numCsds) {
+    char csdName[kMaxCSDStrlen];
+    for (int csdId = 0; csdId < numCsds; csdId++) {
+        int32_t flags = bufferInfo[inputFrameId].flags;
+        if (flags == CODEC_CONFIG_FLAG) {
+            int32_t size = bufferInfo[inputFrameId].size;
+            char *data = (char *)malloc(size);
+            if (!data) {
+                ALOGE("Insufficient memeory to read input");
+                return -1;
+            }
+            inputStream.read(data, size);
+            CHECK_EQ(inputStream.gcount(), size);
+
+            sp<ABuffer> csdBuffer = ABuffer::CreateAsCopy((void *)data, size);
+            if (csdBuffer.get() == nullptr || csdBuffer->base() == nullptr) {
+                return -1;
+            }
+            snprintf(csdName, sizeof(csdName), "csd-%d", csdId);
+            format->setBuffer(csdName, csdBuffer);
+            inputFrameId++;
+            free(data);
+        }
+    }
+    return 0;
+}
diff --git a/media/libstagefright/tests/writer/WriterUtility.h b/media/libstagefright/tests/writer/WriterUtility.h
new file mode 100644
index 0000000..d402798
--- /dev/null
+++ b/media/libstagefright/tests/writer/WriterUtility.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2019 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_UTILITY_H_
+#define WRITER_UTILITY_H_
+
+#include <fstream>
+#include <iostream>
+#include <vector>
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+#include <media/stagefright/MediaAdapter.h>
+
+using namespace android;
+using namespace std;
+
+#define CODEC_CONFIG_FLAG 32
+
+constexpr uint32_t kMaxCSDStrlen = 16;
+
+struct BufferInfo {
+    int32_t size;
+    uint32_t flags;
+    int64_t timeUs;
+};
+
+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 writeHeaderBuffers(ifstream &inputStream, vector<BufferInfo> &bufferInfo,
+                           int32_t &inputFrameId, sp<AMessage> &format, int32_t numCsds);
+
+#endif  // WRITER_UTILITY_H_