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 "AmrnbDecoderTest"
19 #define OUTPUT_FILE "/data/local/tmp/amrnbDecode.out"
20
21 #include <utils/Log.h>
22
23 #include <audio_utils/sndfile.h>
24 #include <stdio.h>
25 #include <fstream>
26
27 #include "gsmamr_dec.h"
28
29 #include "AmrnbDecTestEnvironment.h"
30
31 // Constants for AMR-NB
32 constexpr int32_t kInputBufferSize = 64;
33 constexpr int32_t kSamplesPerFrame = L_FRAME;
34 constexpr int32_t kBitsPerSample = 16;
35 constexpr int32_t kSampleRate = 8000;
36 constexpr int32_t kChannels = 1;
37 constexpr int32_t kOutputBufferSize = kSamplesPerFrame * kBitsPerSample / 8;
38 const int32_t kFrameSizes[] = {12, 13, 15, 17, 19, 20, 26, 31, -1, -1, -1, -1, -1, -1, -1, -1};
39
40 constexpr int32_t kNumFrameReset = 150;
41
42 static AmrnbDecTestEnvironment *gEnv = nullptr;
43
44 class AmrnbDecoderTest : public ::testing::TestWithParam<std::tuple<string, string>> {
45 public:
AmrnbDecoderTest()46 AmrnbDecoderTest() : mFpInput(nullptr) {}
47
~AmrnbDecoderTest()48 ~AmrnbDecoderTest() {
49 if (mFpInput) {
50 fclose(mFpInput);
51 mFpInput = nullptr;
52 }
53 }
54
55 FILE *mFpInput;
56 SNDFILE *openOutputFile(SF_INFO *sfInfo);
57 int32_t DecodeFrames(void *amrHandle, SNDFILE *outFileHandle, int32_t frameCount = INT32_MAX);
58 bool compareBinaryFiles(const std::string& refFilePath, const std::string& outFilePath);
59 };
60
openOutputFile(SF_INFO * sfInfo)61 SNDFILE *AmrnbDecoderTest::openOutputFile(SF_INFO *sfInfo) {
62 memset(sfInfo, 0, sizeof(SF_INFO));
63 sfInfo->channels = kChannels;
64 sfInfo->format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
65 sfInfo->samplerate = kSampleRate;
66 SNDFILE *outFileHandle = sf_open(OUTPUT_FILE, SFM_WRITE, sfInfo);
67 return outFileHandle;
68 }
69
DecodeFrames(void * amrHandle,SNDFILE * outFileHandle,int32_t frameCount)70 int32_t AmrnbDecoderTest::DecodeFrames(void *amrHandle, SNDFILE *outFileHandle,
71 int32_t frameCount) {
72 uint8_t inputBuf[kInputBufferSize];
73 int16_t outputBuf[kOutputBufferSize];
74
75 while (frameCount > 0) {
76 uint8_t mode;
77 int32_t bytesRead = fread(&mode, 1, 1, mFpInput);
78 if (bytesRead != 1) break;
79
80 // Find frame type
81 Frame_Type_3GPP frameType = (Frame_Type_3GPP)((mode >> 3) & 0x0f);
82 int32_t frameSize = kFrameSizes[frameType];
83 if (frameSize < 0) {
84 ALOGE("Illegal frame type");
85 return -1;
86 }
87 bytesRead = fread(inputBuf, 1, frameSize, mFpInput);
88 if (bytesRead != frameSize) break;
89
90 int32_t bytesDecoded = AMRDecode(amrHandle, frameType, inputBuf, outputBuf, MIME_IETF);
91 if (bytesDecoded == -1) {
92 ALOGE("Failed to decode the input file");
93 return -1;
94 }
95
96 sf_writef_short(outFileHandle, outputBuf, kSamplesPerFrame);
97 frameCount--;
98 }
99 return 0;
100 }
101
compareBinaryFiles(const std::string & refFilePath,const std::string & outFilePath)102 bool AmrnbDecoderTest::compareBinaryFiles(const std::string &refFilePath,
103 const std::string &outFilePath) {
104 std::ifstream refFile(refFilePath, std::ios::binary | std::ios::ate);
105 std::ifstream outFile(outFilePath, std::ios::binary | std::ios::ate);
106 assert(refFile.is_open() && "Error opening reference file " + refFilePath);
107 assert(outFile.is_open() && "Error opening output file " + outFilePath);
108
109 std::streamsize refFileSize = refFile.tellg();
110 std::streamsize outFileSize = outFile.tellg();
111 if (refFileSize != outFileSize) {
112 ALOGE("Error, File size mismatch: Reference file size = %td bytes,"
113 " but output file size = %td bytes.", refFileSize, outFileSize);
114 return false;
115 }
116
117 refFile.seekg(0, std::ios::beg);
118 outFile.seekg(0, std::ios::beg);
119 constexpr std::streamsize kBufferSize = 16 * 1024;
120 char refBuffer[kBufferSize];
121 char outBuffer[kBufferSize];
122
123 while (refFile && outFile) {
124 refFile.read(refBuffer, kBufferSize);
125 outFile.read(outBuffer, kBufferSize);
126
127 std::streamsize refBytesRead = refFile.gcount();
128 std::streamsize outBytesRead = outFile.gcount();
129
130 if (refBytesRead != outBytesRead || memcmp(refBuffer, outBuffer, refBytesRead) != 0) {
131 ALOGE("Error, File content mismatch.");
132 return false;
133 }
134 }
135 return true;
136 }
137
TEST_F(AmrnbDecoderTest,CreateAmrnbDecoderTest)138 TEST_F(AmrnbDecoderTest, CreateAmrnbDecoderTest) {
139 void *amrHandle;
140 int32_t status = GSMInitDecode(&amrHandle, (Word8 *)"AMRNBDecoder");
141 ASSERT_EQ(status, 0) << "Error creating AMR-NB decoder";
142 GSMDecodeFrameExit(&amrHandle);
143 ASSERT_EQ(amrHandle, nullptr) << "Error deleting AMR-NB decoder";
144 }
145
TEST_P(AmrnbDecoderTest,DecodeTest)146 TEST_P(AmrnbDecoderTest, DecodeTest) {
147 string inputFile = gEnv->getRes() + std::get<0>(GetParam());
148 mFpInput = fopen(inputFile.c_str(), "rb");
149 ASSERT_NE(mFpInput, nullptr) << "Error opening input file " << inputFile;
150
151 // Open the output file.
152 SF_INFO sfInfo;
153 SNDFILE *outFileHandle = openOutputFile(&sfInfo);
154 ASSERT_NE(outFileHandle, nullptr) << "Error opening output file for writing decoded output";
155
156 void *amrHandle;
157 int32_t status = GSMInitDecode(&amrHandle, (Word8 *)"AMRNBDecoder");
158 ASSERT_EQ(status, 0) << "Error creating AMR-NB decoder";
159
160 // Decode
161 int32_t decoderErr = DecodeFrames(amrHandle, outFileHandle);
162 ASSERT_EQ(decoderErr, 0) << "DecodeFrames returned error";
163
164 sf_close(outFileHandle);
165 GSMDecodeFrameExit(&amrHandle);
166 ASSERT_EQ(amrHandle, nullptr) << "Error deleting AMR-NB decoder";
167
168 string refFilePath = gEnv->getRes() + std::get<1>(GetParam());
169 ASSERT_TRUE(compareBinaryFiles(refFilePath, OUTPUT_FILE))
170 << "Error, Binary file comparison failed: Output file " << OUTPUT_FILE
171 << " does not match the reference file " << refFilePath << ".";
172 }
173
TEST_P(AmrnbDecoderTest,ResetDecodeTest)174 TEST_P(AmrnbDecoderTest, ResetDecodeTest) {
175 string inputFile = gEnv->getRes() + std::get<0>(GetParam());
176 mFpInput = fopen(inputFile.c_str(), "rb");
177 ASSERT_NE(mFpInput, nullptr) << "Error opening input file " << inputFile;
178
179 // Open the output file.
180 SF_INFO sfInfo;
181 SNDFILE *outFileHandle = openOutputFile(&sfInfo);
182 ASSERT_NE(outFileHandle, nullptr) << "Error opening output file for writing decoded output";
183
184 void *amrHandle;
185 int32_t status = GSMInitDecode(&amrHandle, (Word8 *)"AMRNBDecoder");
186 ASSERT_EQ(status, 0) << "Error creating AMR-NB decoder";
187
188 // Decode kNumFrameReset first
189 int32_t decoderErr = DecodeFrames(amrHandle, outFileHandle, kNumFrameReset);
190 ASSERT_EQ(decoderErr, 0) << "DecodeFrames returned error";
191
192 status = Speech_Decode_Frame_reset(amrHandle);
193 ASSERT_EQ(status, 0) << "Error resting AMR-NB decoder";
194
195 // Start decoding again
196 decoderErr = DecodeFrames(amrHandle, outFileHandle);
197 ASSERT_EQ(decoderErr, 0) << "DecodeFrames returned error";
198
199 sf_close(outFileHandle);
200 GSMDecodeFrameExit(&amrHandle);
201 ASSERT_EQ(amrHandle, nullptr) << "Error deleting AMR-NB decoder";
202 }
203
204 INSTANTIATE_TEST_SUITE_P(AmrnbDecoderTestAll, AmrnbDecoderTest,
205 ::testing::Values(std::make_tuple(
206 "bbb_8000hz_1ch_8kbps_amrnb_30sec.amrnb",
207 "bbb_8000hz_1ch_8kbps_amrnb_30sec_ref.pcm"),
208 std::make_tuple(
209 "sine_amrnb_1ch_12kbps_8000hz.amrnb",
210 "sine_amrnb_1ch_12kbps_8000hz_ref.pcm"),
211 std::make_tuple(
212 "trim_8000hz_1ch_12kpbs_amrnb_200ms.amrnb",
213 "trim_8000hz_1ch_12kpbs_amrnb_200ms_ref.pcm"),
214 std::make_tuple(
215 "bbb_8kHz_1ch_4.75kbps_amrnb_3sec.amrnb",
216 "bbb_8kHz_1ch_4.75kbps_amrnb_3sec_ref.pcm"),
217 std::make_tuple(
218 "bbb_8kHz_1ch_10kbps_amrnb_1sec.amrnb",
219 "bbb_8kHz_1ch_10kbps_amrnb_1sec_ref.pcm"),
220 std::make_tuple(
221 "bbb_8kHz_1ch_12.2kbps_amrnb_3sec.amrnb",
222 "bbb_8kHz_1ch_12.2kbps_amrnb_3sec_ref.pcm")));
223
main(int argc,char ** argv)224 int main(int argc, char **argv) {
225 gEnv = new AmrnbDecTestEnvironment();
226 ::testing::AddGlobalTestEnvironment(gEnv);
227 ::testing::InitGoogleTest(&argc, argv);
228 int status = gEnv->initFromOptions(argc, argv);
229 if (status == 0) {
230 status = RUN_ALL_TESTS();
231 ALOGV("Test result = %d\n", status);
232 }
233 return status;
234 }
235