xref: /aosp_15_r20/frameworks/av/media/module/codecs/amrnb/enc/test/AmrnbEncoderTest.cpp (revision ec779b8e0859a360c3d303172224686826e6e0e1)
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 //#define LOG_NDEBUG 0
18 #define LOG_TAG "AmrnbEncoderTest"
19 
20 #include <utils/Log.h>
21 
22 #include <audio_utils/sndfile.h>
23 #include <stdio.h>
24 #include <fstream>
25 
26 #include "gsmamr_enc.h"
27 
28 #include "AmrnbEncTestEnvironment.h"
29 
30 #define OUTPUT_FILE "/data/local/tmp/amrnbEncode.out"
31 
32 constexpr int32_t kInputBufferSize = L_FRAME * 2;  // 160 samples * 16-bit per sample.
33 constexpr int32_t kOutputBufferSize = 1024;
34 constexpr int32_t kNumFrameReset = 200;
35 constexpr int32_t kMaxCount = 10;
36 struct AmrNbEncState {
37     void *encCtx;
38     void *pidSyncCtx;
39 };
40 
41 static AmrnbEncTestEnvironment *gEnv = nullptr;
42 
43 class AmrnbEncoderTest : public ::testing::TestWithParam<tuple<string, int32_t, string>> {
44   public:
AmrnbEncoderTest()45     AmrnbEncoderTest() : mAmrEncHandle(nullptr) {}
46 
~AmrnbEncoderTest()47     ~AmrnbEncoderTest() {
48         if (mAmrEncHandle) {
49             free(mAmrEncHandle);
50             mAmrEncHandle = nullptr;
51         }
52     }
53 
54     AmrNbEncState *mAmrEncHandle;
55     int32_t EncodeFrames(int32_t mode, FILE *fpInput, FILE *mFpOutput,
56                          int32_t frameCount = INT32_MAX);
57     bool compareBinaryFiles(const string& refFilePath, const string& outFilePath);
58 };
59 
EncodeFrames(int32_t mode,FILE * fpInput,FILE * mFpOutput,int32_t frameCount)60 int32_t AmrnbEncoderTest::EncodeFrames(int32_t mode, FILE *fpInput, FILE *mFpOutput,
61                                        int32_t frameCount) {
62     int32_t frameNum = 0;
63     uint16_t inputBuf[kInputBufferSize];
64     uint8_t outputBuf[kOutputBufferSize];
65     while (frameNum < frameCount) {
66         int32_t bytesRead = fread(inputBuf, 1, kInputBufferSize, fpInput);
67         if (bytesRead != kInputBufferSize && !feof(fpInput)) {
68             ALOGE("Unable to read data from input file");
69             return -1;
70         } else if (feof(fpInput) && bytesRead == 0) {
71             break;
72         }
73         Frame_Type_3GPP frame_type = (Frame_Type_3GPP)mode;
74         int32_t bytesGenerated =
75                 AMREncode(mAmrEncHandle->encCtx, mAmrEncHandle->pidSyncCtx, (Mode)mode,
76                           (Word16 *)inputBuf, outputBuf, &frame_type, AMR_TX_WMF);
77         frameNum++;
78         if (bytesGenerated < 0) {
79             ALOGE("Error in encoging the file: Invalid output format");
80             return -1;
81         }
82 
83         // Convert from WMF to RFC 3267 format.
84         if (bytesGenerated > 0) {
85             outputBuf[0] = ((outputBuf[0] << 3) | 4) & 0x7c;
86         }
87         fwrite(outputBuf, 1, bytesGenerated, mFpOutput);
88     }
89     return 0;
90 }
91 
compareBinaryFiles(const std::string & refFilePath,const std::string & outFilePath)92 bool AmrnbEncoderTest::compareBinaryFiles(const std::string &refFilePath,
93                                           const std::string &outFilePath) {
94     std::ifstream refFile(refFilePath, std::ios::binary | std::ios::ate);
95     std::ifstream outFile(outFilePath, std::ios::binary | std::ios::ate);
96     assert(refFile.is_open() && "Error opening reference file " + refFilePath);
97     assert(outFile.is_open() && "Error opening output file " + outFilePath);
98 
99     std::streamsize refFileSize = refFile.tellg();
100     std::streamsize outFileSize = outFile.tellg();
101     if (refFileSize != outFileSize) {
102         ALOGE("Error, File size mismatch: Reference file size = %td bytes,"
103               " but output file size = %td bytes.", refFileSize, outFileSize);
104         return false;
105     }
106 
107     refFile.seekg(0, std::ios::beg);
108     outFile.seekg(0, std::ios::beg);
109     constexpr std::streamsize kBufferSize = 16 * 1024;
110     char refBuffer[kBufferSize];
111     char outBuffer[kBufferSize];
112 
113     while (refFile && outFile) {
114         refFile.read(refBuffer, kBufferSize);
115         outFile.read(outBuffer, kBufferSize);
116 
117         std::streamsize refBytesRead = refFile.gcount();
118         std::streamsize outBytesRead = outFile.gcount();
119 
120         if (refBytesRead != outBytesRead || memcmp(refBuffer, outBuffer, refBytesRead) != 0) {
121             ALOGE("Error, File content mismatch.");
122             return false;
123         }
124     }
125     return true;
126 }
127 
TEST_F(AmrnbEncoderTest,CreateAmrnbEncoderTest)128 TEST_F(AmrnbEncoderTest, CreateAmrnbEncoderTest) {
129     mAmrEncHandle = (AmrNbEncState *)malloc(sizeof(AmrNbEncState));
130     ASSERT_NE(mAmrEncHandle, nullptr) << "Error in allocating memory to Codec handle";
131     for (int count = 0; count < kMaxCount; count++) {
132         int32_t status = AMREncodeInit(&mAmrEncHandle->encCtx, &mAmrEncHandle->pidSyncCtx, 0);
133         ASSERT_EQ(status, 0) << "Error creating AMR-NB encoder";
134         ALOGV("Successfully created encoder");
135     }
136     if (mAmrEncHandle) {
137         AMREncodeExit(&mAmrEncHandle->encCtx, &mAmrEncHandle->pidSyncCtx);
138         ASSERT_EQ(mAmrEncHandle->encCtx, nullptr) << "Error deleting AMR-NB encoder";
139         ASSERT_EQ(mAmrEncHandle->pidSyncCtx, nullptr) << "Error deleting AMR-NB encoder";
140         free(mAmrEncHandle);
141         mAmrEncHandle = nullptr;
142         ALOGV("Successfully deleted encoder");
143     }
144 }
145 
TEST_P(AmrnbEncoderTest,EncodeTest)146 TEST_P(AmrnbEncoderTest, EncodeTest) {
147     mAmrEncHandle = (AmrNbEncState *)malloc(sizeof(AmrNbEncState));
148     ASSERT_NE(mAmrEncHandle, nullptr) << "Error in allocating memory to Codec handle";
149     int32_t status = AMREncodeInit(&mAmrEncHandle->encCtx, &mAmrEncHandle->pidSyncCtx, 0);
150     ASSERT_EQ(status, 0) << "Error creating AMR-NB encoder";
151 
152     string inputFile = gEnv->getRes() + std::get<0>(GetParam());
153     FILE *fpInput = fopen(inputFile.c_str(), "rb");
154     ASSERT_NE(fpInput, nullptr) << "Error opening input file " << inputFile;
155 
156     FILE *fpOutput = fopen(OUTPUT_FILE, "wb");
157     ASSERT_NE(fpOutput, nullptr) << "Error opening output file " << OUTPUT_FILE;
158 
159     // Write file header.
160     fwrite("#!AMR\n", 1, 6, fpOutput);
161 
162     int32_t mode = std::get<1>(GetParam());
163     int32_t encodeErr = EncodeFrames(mode, fpInput, fpOutput);
164     ASSERT_EQ(encodeErr, 0) << "EncodeFrames returned error for Codec mode: " << mode;
165 
166     fclose(fpOutput);
167     fclose(fpInput);
168 
169     AMREncodeExit(&mAmrEncHandle->encCtx, &mAmrEncHandle->pidSyncCtx);
170     ASSERT_EQ(mAmrEncHandle->encCtx, nullptr) << "Error deleting AMR-NB encoder";
171     ASSERT_EQ(mAmrEncHandle->pidSyncCtx, nullptr) << "Error deleting AMR-NB encoder";
172     free(mAmrEncHandle);
173     mAmrEncHandle = nullptr;
174     ALOGV("Successfully deleted encoder");
175 
176     string refFilePath = gEnv->getRes() + std::get<2>(GetParam());
177     ASSERT_TRUE(compareBinaryFiles(refFilePath, OUTPUT_FILE))
178        << "Error, Binary file comparison failed: Output file " << OUTPUT_FILE
179        << " does not match the reference file " << refFilePath << ".";
180 }
181 
TEST_P(AmrnbEncoderTest,ResetEncoderTest)182 TEST_P(AmrnbEncoderTest, ResetEncoderTest) {
183     mAmrEncHandle = (AmrNbEncState *)malloc(sizeof(AmrNbEncState));
184     ASSERT_NE(mAmrEncHandle, nullptr) << "Error in allocating memory to Codec handle";
185     int32_t status = AMREncodeInit(&mAmrEncHandle->encCtx, &mAmrEncHandle->pidSyncCtx, 0);
186     ASSERT_EQ(status, 0) << "Error creating AMR-NB encoder";
187 
188     string inputFile = gEnv->getRes() + std::get<0>(GetParam());
189     FILE *fpInput = fopen(inputFile.c_str(), "rb");
190     ASSERT_NE(fpInput, nullptr) << "Error opening input file " << inputFile;
191 
192     FILE *fpOutput = fopen(OUTPUT_FILE, "wb");
193     ASSERT_NE(fpOutput, nullptr) << "Error opening output file " << OUTPUT_FILE;
194 
195     // Write file header.
196     fwrite("#!AMR\n", 1, 6, fpOutput);
197 
198     int32_t mode = std::get<1>(GetParam());
199     // Encode kNumFrameReset first
200     int32_t encodeErr = EncodeFrames(mode, fpInput, fpOutput, kNumFrameReset);
201     ASSERT_EQ(encodeErr, 0) << "EncodeFrames returned error for Codec mode: " << mode;
202 
203     status = AMREncodeReset(mAmrEncHandle->encCtx, mAmrEncHandle->pidSyncCtx);
204     ASSERT_EQ(status, 0) << "Error resting AMR-NB encoder";
205 
206     // Start encoding again
207     encodeErr = EncodeFrames(mode, fpInput, fpOutput);
208     ASSERT_EQ(encodeErr, 0) << "EncodeFrames returned error for Codec mode: " << mode;
209 
210     fclose(fpOutput);
211     fclose(fpInput);
212 
213     AMREncodeExit(&mAmrEncHandle->encCtx, &mAmrEncHandle->pidSyncCtx);
214     ASSERT_EQ(mAmrEncHandle->encCtx, nullptr) << "Error deleting AMR-NB encoder";
215     ASSERT_EQ(mAmrEncHandle->pidSyncCtx, nullptr) << "Error deleting AMR-NB encoder";
216     free(mAmrEncHandle);
217     mAmrEncHandle = nullptr;
218     ALOGV("Successfully deleted encoder");
219 }
220 
221 // TODO: Add more test vectors
222 INSTANTIATE_TEST_SUITE_P(AmrnbEncoderTestAll, AmrnbEncoderTest,
223     ::testing::Values(
224         make_tuple("bbb_raw_1ch_8khz_s16le.raw", MR475, "bbb_raw_1ch_8khz_s16le_MR475_ref.amrnb"),
225         make_tuple("bbb_raw_1ch_8khz_s16le.raw", MR515, "bbb_raw_1ch_8khz_s16le_MR515_ref.amrnb"),
226         make_tuple("bbb_raw_1ch_8khz_s16le.raw", MR59, "bbb_raw_1ch_8khz_s16le_MR59_ref.amrnb"),
227         make_tuple("bbb_raw_1ch_8khz_s16le.raw", MR67, "bbb_raw_1ch_8khz_s16le_MR67_ref.amrnb"),
228         make_tuple("bbb_raw_1ch_8khz_s16le.raw", MR74, "bbb_raw_1ch_8khz_s16le_MR74_ref.amrnb"),
229         make_tuple("bbb_raw_1ch_8khz_s16le.raw", MR795, "bbb_raw_1ch_8khz_s16le_MR795_ref.amrnb"),
230         make_tuple("bbb_raw_1ch_8khz_s16le.raw", MR102, "bbb_raw_1ch_8khz_s16le_MR102_ref.amrnb"),
231         make_tuple("bbb_raw_1ch_8khz_s16le.raw", MR122, "bbb_raw_1ch_8khz_s16le_MR122_ref.amrnb"),
232         make_tuple("sinesweepraw.raw", MR475, "sinesweepraw_MR475_ref.amrnb"),
233         make_tuple("sinesweepraw.raw", MR515, "sinesweepraw_MR515_ref.amrnb"),
234         make_tuple("sinesweepraw.raw", MR59, "sinesweepraw_MR59_ref.amrnb"),
235         make_tuple("sinesweepraw.raw", MR67, "sinesweepraw_MR67_ref.amrnb"),
236         make_tuple("sinesweepraw.raw", MR74, "sinesweepraw_MR74_ref.amrnb"),
237         make_tuple("sinesweepraw.raw", MR795, "sinesweepraw_MR795_ref.amrnb"),
238         make_tuple("sinesweepraw.raw", MR102, "sinesweepraw_MR102_ref.amrnb"),
239         make_tuple("sinesweepraw.raw", MR122, "sinesweepraw_MR122_ref.amrnb")));
240 
main(int argc,char ** argv)241 int main(int argc, char **argv) {
242     gEnv = new AmrnbEncTestEnvironment();
243     ::testing::AddGlobalTestEnvironment(gEnv);
244     ::testing::InitGoogleTest(&argc, argv);
245     int status = gEnv->initFromOptions(argc, argv);
246     if (status == 0) {
247         status = RUN_ALL_TESTS();
248         ALOGV("Test result = %d\n", status);
249     }
250     return status;
251 }
252