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