Merge "MediaTesting: Add amr-nb Encoder Test"
diff --git a/media/libstagefright/codecs/amrnb/enc/test/AmrnbEncTestEnvironment.h b/media/libstagefright/codecs/amrnb/enc/test/AmrnbEncTestEnvironment.h
new file mode 100644
index 0000000..5a2fcd1
--- /dev/null
+++ b/media/libstagefright/codecs/amrnb/enc/test/AmrnbEncTestEnvironment.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 __AMRNBENC_TEST_ENVIRONMENT_H__
+#define __AMRNBENC_TEST_ENVIRONMENT_H__
+
+#include <gtest/gtest.h>
+
+#include <getopt.h>
+
+using namespace std;
+
+class AmrnbEncTestEnvironment : public ::testing::Environment {
+  public:
+    AmrnbEncTestEnvironment() : res("/data/local/tmp/") {}
+
+    // 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 AmrnbEncTestEnvironment::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  // __AMRNBENC_TEST_ENVIRONMENT_H__
diff --git a/media/libstagefright/codecs/amrnb/enc/test/AmrnbEncoderTest.cpp b/media/libstagefright/codecs/amrnb/enc/test/AmrnbEncoderTest.cpp
new file mode 100644
index 0000000..fb72998
--- /dev/null
+++ b/media/libstagefright/codecs/amrnb/enc/test/AmrnbEncoderTest.cpp
@@ -0,0 +1,207 @@
+/*
+ * 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 "AmrnbEncoderTest"
+
+#include <utils/Log.h>
+
+#include <audio_utils/sndfile.h>
+#include <stdio.h>
+
+#include "gsmamr_enc.h"
+
+#include "AmrnbEncTestEnvironment.h"
+
+#define OUTPUT_FILE "/data/local/tmp/amrnbEncode.out"
+
+constexpr int32_t kInputBufferSize = L_FRAME * 2;  // 160 samples * 16-bit per sample.
+constexpr int32_t kOutputBufferSize = 1024;
+constexpr int32_t kNumFrameReset = 200;
+constexpr int32_t kMaxCount = 10;
+struct AmrNbEncState {
+    void *encCtx;
+    void *pidSyncCtx;
+};
+
+static AmrnbEncTestEnvironment *gEnv = nullptr;
+
+class AmrnbEncoderTest : public ::testing::TestWithParam<pair<string, int32_t>> {
+  public:
+    AmrnbEncoderTest() : mAmrEncHandle(nullptr) {}
+
+    ~AmrnbEncoderTest() {
+        if (mAmrEncHandle) {
+            free(mAmrEncHandle);
+            mAmrEncHandle = nullptr;
+        }
+    }
+
+    AmrNbEncState *mAmrEncHandle;
+    int32_t EncodeFrames(int32_t mode, FILE *fpInput, FILE *mFpOutput,
+                         int32_t frameCount = INT32_MAX);
+};
+
+int32_t AmrnbEncoderTest::EncodeFrames(int32_t mode, FILE *fpInput, FILE *mFpOutput,
+                                       int32_t frameCount) {
+    int32_t frameNum = 0;
+    uint16_t inputBuf[kInputBufferSize];
+    uint8_t outputBuf[kOutputBufferSize];
+    while (frameNum < frameCount) {
+        int32_t bytesRead = fread(inputBuf, 1, kInputBufferSize, fpInput);
+        if (bytesRead != kInputBufferSize && !feof(fpInput)) {
+            ALOGE("Unable to read data from input file");
+            return -1;
+        } else if (feof(fpInput) && bytesRead == 0) {
+            break;
+        }
+        Frame_Type_3GPP frame_type = (Frame_Type_3GPP)mode;
+        int32_t bytesGenerated =
+                AMREncode(mAmrEncHandle->encCtx, mAmrEncHandle->pidSyncCtx, (Mode)mode,
+                          (Word16 *)inputBuf, outputBuf, &frame_type, AMR_TX_WMF);
+        frameNum++;
+        if (bytesGenerated < 0) {
+            ALOGE("Error in encoging the file: Invalid output format");
+            return -1;
+        }
+
+        // Convert from WMF to RFC 3267 format.
+        if (bytesGenerated > 0) {
+            outputBuf[0] = ((outputBuf[0] << 3) | 4) & 0x7c;
+        }
+        fwrite(outputBuf, 1, bytesGenerated, mFpOutput);
+    }
+    return 0;
+}
+
+TEST_F(AmrnbEncoderTest, CreateAmrnbEncoderTest) {
+    mAmrEncHandle = (AmrNbEncState *)malloc(sizeof(AmrNbEncState));
+    ASSERT_NE(mAmrEncHandle, nullptr) << "Error in allocating memory to Codec handle";
+    for (int count = 0; count < kMaxCount; count++) {
+        int32_t status = AMREncodeInit(&mAmrEncHandle->encCtx, &mAmrEncHandle->pidSyncCtx, 0);
+        ASSERT_EQ(status, 0) << "Error creating AMR-NB encoder";
+        ALOGV("Successfully created encoder");
+    }
+    if (mAmrEncHandle) {
+        AMREncodeExit(&mAmrEncHandle->encCtx, &mAmrEncHandle->pidSyncCtx);
+        ASSERT_EQ(mAmrEncHandle->encCtx, nullptr) << "Error deleting AMR-NB encoder";
+        ASSERT_EQ(mAmrEncHandle->pidSyncCtx, nullptr) << "Error deleting AMR-NB encoder";
+        free(mAmrEncHandle);
+        mAmrEncHandle = nullptr;
+        ALOGV("Successfully deleted encoder");
+    }
+}
+
+TEST_P(AmrnbEncoderTest, EncodeTest) {
+    mAmrEncHandle = (AmrNbEncState *)malloc(sizeof(AmrNbEncState));
+    ASSERT_NE(mAmrEncHandle, nullptr) << "Error in allocating memory to Codec handle";
+    int32_t status = AMREncodeInit(&mAmrEncHandle->encCtx, &mAmrEncHandle->pidSyncCtx, 0);
+    ASSERT_EQ(status, 0) << "Error creating AMR-NB encoder";
+
+    string inputFile = gEnv->getRes() + GetParam().first;
+    FILE *fpInput = fopen(inputFile.c_str(), "rb");
+    ASSERT_NE(fpInput, nullptr) << "Error opening input file " << inputFile;
+
+    FILE *fpOutput = fopen(OUTPUT_FILE, "wb");
+    ASSERT_NE(fpOutput, nullptr) << "Error opening output file " << OUTPUT_FILE;
+
+    // Write file header.
+    fwrite("#!AMR\n", 1, 6, fpOutput);
+
+    int32_t mode = GetParam().second;
+    int32_t encodeErr = EncodeFrames(mode, fpInput, fpOutput);
+    ASSERT_EQ(encodeErr, 0) << "EncodeFrames returned error for Codec mode: " << mode;
+
+    fclose(fpOutput);
+    fclose(fpInput);
+
+    AMREncodeExit(&mAmrEncHandle->encCtx, &mAmrEncHandle->pidSyncCtx);
+    ASSERT_EQ(mAmrEncHandle->encCtx, nullptr) << "Error deleting AMR-NB encoder";
+    ASSERT_EQ(mAmrEncHandle->pidSyncCtx, nullptr) << "Error deleting AMR-NB encoder";
+    free(mAmrEncHandle);
+    mAmrEncHandle = nullptr;
+    ALOGV("Successfully deleted encoder");
+}
+
+TEST_P(AmrnbEncoderTest, ResetEncoderTest) {
+    mAmrEncHandle = (AmrNbEncState *)malloc(sizeof(AmrNbEncState));
+    ASSERT_NE(mAmrEncHandle, nullptr) << "Error in allocating memory to Codec handle";
+    int32_t status = AMREncodeInit(&mAmrEncHandle->encCtx, &mAmrEncHandle->pidSyncCtx, 0);
+    ASSERT_EQ(status, 0) << "Error creating AMR-NB encoder";
+
+    string inputFile = gEnv->getRes() + GetParam().first;
+    FILE *fpInput = fopen(inputFile.c_str(), "rb");
+    ASSERT_NE(fpInput, nullptr) << "Error opening input file " << inputFile;
+
+    FILE *fpOutput = fopen(OUTPUT_FILE, "wb");
+    ASSERT_NE(fpOutput, nullptr) << "Error opening output file " << OUTPUT_FILE;
+
+    // Write file header.
+    fwrite("#!AMR\n", 1, 6, fpOutput);
+
+    int32_t mode = GetParam().second;
+    // Encode kNumFrameReset first
+    int32_t encodeErr = EncodeFrames(mode, fpInput, fpOutput, kNumFrameReset);
+    ASSERT_EQ(encodeErr, 0) << "EncodeFrames returned error for Codec mode: " << mode;
+
+    status = AMREncodeReset(mAmrEncHandle->encCtx, mAmrEncHandle->pidSyncCtx);
+    ASSERT_EQ(status, 0) << "Error resting AMR-NB encoder";
+
+    // Start encoding again
+    encodeErr = EncodeFrames(mode, fpInput, fpOutput);
+    ASSERT_EQ(encodeErr, 0) << "EncodeFrames returned error for Codec mode: " << mode;
+
+    fclose(fpOutput);
+    fclose(fpInput);
+
+    AMREncodeExit(&mAmrEncHandle->encCtx, &mAmrEncHandle->pidSyncCtx);
+    ASSERT_EQ(mAmrEncHandle->encCtx, nullptr) << "Error deleting AMR-NB encoder";
+    ASSERT_EQ(mAmrEncHandle->pidSyncCtx, nullptr) << "Error deleting AMR-NB encoder";
+    free(mAmrEncHandle);
+    mAmrEncHandle = nullptr;
+    ALOGV("Successfully deleted encoder");
+}
+
+// TODO: Add more test vectors
+INSTANTIATE_TEST_SUITE_P(AmrnbEncoderTestAll, AmrnbEncoderTest,
+                         ::testing::Values(make_pair("bbb_raw_1ch_8khz_s16le.raw", MR475),
+                                           make_pair("bbb_raw_1ch_8khz_s16le.raw", MR515),
+                                           make_pair("bbb_raw_1ch_8khz_s16le.raw", MR59),
+                                           make_pair("bbb_raw_1ch_8khz_s16le.raw", MR67),
+                                           make_pair("bbb_raw_1ch_8khz_s16le.raw", MR74),
+                                           make_pair("bbb_raw_1ch_8khz_s16le.raw", MR795),
+                                           make_pair("bbb_raw_1ch_8khz_s16le.raw", MR102),
+                                           make_pair("bbb_raw_1ch_8khz_s16le.raw", MR122),
+                                           make_pair("sinesweepraw.raw", MR475),
+                                           make_pair("sinesweepraw.raw", MR515),
+                                           make_pair("sinesweepraw.raw", MR59),
+                                           make_pair("sinesweepraw.raw", MR67),
+                                           make_pair("sinesweepraw.raw", MR74),
+                                           make_pair("sinesweepraw.raw", MR795),
+                                           make_pair("sinesweepraw.raw", MR102),
+                                           make_pair("sinesweepraw.raw", MR122)));
+
+int main(int argc, char **argv) {
+    gEnv = new AmrnbEncTestEnvironment();
+    ::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/codecs/amrnb/enc/test/Android.bp b/media/libstagefright/codecs/amrnb/enc/test/Android.bp
new file mode 100644
index 0000000..e8982fe
--- /dev/null
+++ b/media/libstagefright/codecs/amrnb/enc/test/Android.bp
@@ -0,0 +1,48 @@
+/*
+ * 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: "AmrnbEncoderTest",
+    gtest: true,
+
+    srcs: [
+        "AmrnbEncoderTest.cpp",
+    ],
+
+    static_libs: [
+        "libstagefright_amrnb_common",
+        "libstagefright_amrnbenc",
+        "libaudioutils",
+        "libsndfile",
+    ],
+
+    shared_libs: [
+        "liblog",
+    ],
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+    ],
+
+    sanitize: {
+        cfi: true,
+        misc_undefined: [
+            "unsigned-integer-overflow",
+            "signed-integer-overflow",
+        ],
+    },
+}
diff --git a/media/libstagefright/codecs/amrnb/enc/test/README.md b/media/libstagefright/codecs/amrnb/enc/test/README.md
new file mode 100644
index 0000000..4ddaa57
--- /dev/null
+++ b/media/libstagefright/codecs/amrnb/enc/test/README.md
@@ -0,0 +1,34 @@
+## Media Testing ##
+---
+#### AMR-NB Encoder :
+The Amr-Nb Encoder Test Suite validates the amrnb encoder available in libstagefright.
+
+Run the following steps to build the test suite:
+```
+m AmrnbEncoderTest
+```
+
+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/AmrnbEncoderTest/AmrnbEncoderTest /data/local/tmp/
+```
+
+To test 32-bit binary push binaries from nativetest.
+```
+adb push ${OUT}/data/nativetest/AmrnbEncoderTest/AmrnbEncoderTest /data/local/tmp/
+```
+
+The resource file for the tests is taken from [here](https://drive.google.com/drive/folders/13cM4tAaVFrmr-zGFqaAzFBbKs75pnm9b). Push these files into device for testing.
+Download amr-nb_encoder folder and push all the files in this folder to /data/local/tmp/ on the device.
+```
+adb push amr-nb_encoder/. /data/local/tmp/
+```
+
+usage: AmrnbEncoderTest -P \<path_to_folder\>
+```
+adb shell /data/local/tmp/AmrnbEncoderTest -P /data/local/tmp/
+```