xref: /aosp_15_r20/external/webrtc/modules/audio_coding/codecs/opus/opus_fec_test.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1 /*
2  *  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #include <memory>
12 
13 #include "modules/audio_coding/codecs/opus/opus_interface.h"
14 #include "test/gtest.h"
15 #include "test/testsupport/file_utils.h"
16 
17 using std::get;
18 using std::string;
19 using std::tuple;
20 using ::testing::TestWithParam;
21 
22 namespace webrtc {
23 
24 // Define coding parameter as <channels, bit_rate, filename, extension>.
25 typedef tuple<size_t, int, string, string> coding_param;
26 typedef struct mode mode;
27 
28 struct mode {
29   bool fec;
30   uint8_t target_packet_loss_rate;
31 };
32 
33 const int kOpusBlockDurationMs = 20;
34 const int kOpusSamplingKhz = 48;
35 
36 class OpusFecTest : public TestWithParam<coding_param> {
37  protected:
38   OpusFecTest();
39 
40   void SetUp() override;
41   void TearDown() override;
42 
43   virtual void EncodeABlock();
44 
45   virtual void DecodeABlock(bool lost_previous, bool lost_current);
46 
47   int block_duration_ms_;
48   int sampling_khz_;
49   size_t block_length_sample_;
50 
51   size_t channels_;
52   int bit_rate_;
53 
54   size_t data_pointer_;
55   size_t loop_length_samples_;
56   size_t max_bytes_;
57   size_t encoded_bytes_;
58 
59   WebRtcOpusEncInst* opus_encoder_;
60   WebRtcOpusDecInst* opus_decoder_;
61 
62   string in_filename_;
63 
64   std::unique_ptr<int16_t[]> in_data_;
65   std::unique_ptr<int16_t[]> out_data_;
66   std::unique_ptr<uint8_t[]> bit_stream_;
67 };
68 
SetUp()69 void OpusFecTest::SetUp() {
70   channels_ = get<0>(GetParam());
71   bit_rate_ = get<1>(GetParam());
72   printf("Coding %zu channel signal at %d bps.\n", channels_, bit_rate_);
73 
74   in_filename_ = test::ResourcePath(get<2>(GetParam()), get<3>(GetParam()));
75 
76   FILE* fp = fopen(in_filename_.c_str(), "rb");
77   ASSERT_FALSE(fp == NULL);
78 
79   // Obtain file size.
80   fseek(fp, 0, SEEK_END);
81   loop_length_samples_ = ftell(fp) / sizeof(int16_t);
82   rewind(fp);
83 
84   // Allocate memory to contain the whole file.
85   in_data_.reset(
86       new int16_t[loop_length_samples_ + block_length_sample_ * channels_]);
87 
88   // Copy the file into the buffer.
89   ASSERT_EQ(fread(&in_data_[0], sizeof(int16_t), loop_length_samples_, fp),
90             loop_length_samples_);
91   fclose(fp);
92 
93   // The audio will be used in a looped manner. To ease the acquisition of an
94   // audio frame that crosses the end of the excerpt, we add an extra block
95   // length of samples to the end of the array, starting over again from the
96   // beginning of the array. Audio frames cross the end of the excerpt always
97   // appear as a continuum of memory.
98   memcpy(&in_data_[loop_length_samples_], &in_data_[0],
99          block_length_sample_ * channels_ * sizeof(int16_t));
100 
101   // Maximum number of bytes in output bitstream.
102   max_bytes_ = block_length_sample_ * channels_ * sizeof(int16_t);
103 
104   out_data_.reset(new int16_t[2 * block_length_sample_ * channels_]);
105   bit_stream_.reset(new uint8_t[max_bytes_]);
106 
107   // If channels_ == 1, use Opus VOIP mode, otherwise, audio mode.
108   int app = channels_ == 1 ? 0 : 1;
109 
110   // Create encoder memory.
111   EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_encoder_, channels_, app, 48000));
112   EXPECT_EQ(0, WebRtcOpus_DecoderCreate(&opus_decoder_, channels_, 48000));
113   // Set bitrate.
114   EXPECT_EQ(0, WebRtcOpus_SetBitRate(opus_encoder_, bit_rate_));
115 }
116 
TearDown()117 void OpusFecTest::TearDown() {
118   // Free memory.
119   EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_encoder_));
120   EXPECT_EQ(0, WebRtcOpus_DecoderFree(opus_decoder_));
121 }
122 
OpusFecTest()123 OpusFecTest::OpusFecTest()
124     : block_duration_ms_(kOpusBlockDurationMs),
125       sampling_khz_(kOpusSamplingKhz),
126       block_length_sample_(
127           static_cast<size_t>(block_duration_ms_ * sampling_khz_)),
128       data_pointer_(0),
129       max_bytes_(0),
130       encoded_bytes_(0),
131       opus_encoder_(NULL),
132       opus_decoder_(NULL) {}
133 
EncodeABlock()134 void OpusFecTest::EncodeABlock() {
135   int value =
136       WebRtcOpus_Encode(opus_encoder_, &in_data_[data_pointer_],
137                         block_length_sample_, max_bytes_, &bit_stream_[0]);
138   EXPECT_GT(value, 0);
139 
140   encoded_bytes_ = static_cast<size_t>(value);
141 }
142 
DecodeABlock(bool lost_previous,bool lost_current)143 void OpusFecTest::DecodeABlock(bool lost_previous, bool lost_current) {
144   int16_t audio_type;
145   int value_1 = 0, value_2 = 0;
146 
147   if (lost_previous) {
148     // Decode previous frame.
149     if (!lost_current &&
150         WebRtcOpus_PacketHasFec(&bit_stream_[0], encoded_bytes_) == 1) {
151       value_1 =
152           WebRtcOpus_DecodeFec(opus_decoder_, &bit_stream_[0], encoded_bytes_,
153                                &out_data_[0], &audio_type);
154     } else {
155       // Call decoder PLC.
156       while (value_1 < static_cast<int>(block_length_sample_)) {
157         int ret = WebRtcOpus_Decode(opus_decoder_, NULL, 0, &out_data_[value_1],
158                                     &audio_type);
159         EXPECT_EQ(ret, sampling_khz_ * 10);  // Should return 10 ms of samples.
160         value_1 += ret;
161       }
162     }
163     EXPECT_EQ(static_cast<int>(block_length_sample_), value_1);
164   }
165 
166   if (!lost_current) {
167     // Decode current frame.
168     value_2 = WebRtcOpus_Decode(opus_decoder_, &bit_stream_[0], encoded_bytes_,
169                                 &out_data_[value_1 * channels_], &audio_type);
170     EXPECT_EQ(static_cast<int>(block_length_sample_), value_2);
171   }
172 }
173 
TEST_P(OpusFecTest,RandomPacketLossTest)174 TEST_P(OpusFecTest, RandomPacketLossTest) {
175   const int kDurationMs = 200000;
176   int time_now_ms, fec_frames;
177   int actual_packet_loss_rate;
178   bool lost_current, lost_previous;
179   mode mode_set[3] = {{true, 0}, {false, 0}, {true, 50}};
180 
181   lost_current = false;
182   for (int i = 0; i < 3; i++) {
183     if (mode_set[i].fec) {
184       EXPECT_EQ(0, WebRtcOpus_EnableFec(opus_encoder_));
185       EXPECT_EQ(0, WebRtcOpus_SetPacketLossRate(
186                        opus_encoder_, mode_set[i].target_packet_loss_rate));
187       printf("FEC is ON, target at packet loss rate %d percent.\n",
188              mode_set[i].target_packet_loss_rate);
189     } else {
190       EXPECT_EQ(0, WebRtcOpus_DisableFec(opus_encoder_));
191       printf("FEC is OFF.\n");
192     }
193     // In this test, we let the target packet loss rate match the actual rate.
194     actual_packet_loss_rate = mode_set[i].target_packet_loss_rate;
195     // Run every mode a certain time.
196     time_now_ms = 0;
197     fec_frames = 0;
198     while (time_now_ms < kDurationMs) {
199       // Encode & decode.
200       EncodeABlock();
201 
202       // Check if payload has FEC.
203       int fec = WebRtcOpus_PacketHasFec(&bit_stream_[0], encoded_bytes_);
204 
205       // If FEC is disabled or the target packet loss rate is set to 0, there
206       // should be no FEC in the bit stream.
207       if (!mode_set[i].fec || mode_set[i].target_packet_loss_rate == 0) {
208         EXPECT_EQ(fec, 0);
209       } else if (fec == 1) {
210         fec_frames++;
211       }
212 
213       lost_previous = lost_current;
214       lost_current = rand() < actual_packet_loss_rate * (RAND_MAX / 100);
215       DecodeABlock(lost_previous, lost_current);
216 
217       time_now_ms += block_duration_ms_;
218 
219       // `data_pointer_` is incremented and wrapped across
220       // `loop_length_samples_`.
221       data_pointer_ = (data_pointer_ + block_length_sample_ * channels_) %
222                       loop_length_samples_;
223     }
224     if (mode_set[i].fec) {
225       printf("%.2f percent frames has FEC.\n",
226              static_cast<float>(fec_frames) * block_duration_ms_ / 2000);
227     }
228   }
229 }
230 
231 const coding_param param_set[] = {
232     std::make_tuple(1,
233                     64000,
234                     string("audio_coding/testfile32kHz"),
235                     string("pcm")),
236     std::make_tuple(1,
237                     32000,
238                     string("audio_coding/testfile32kHz"),
239                     string("pcm")),
240     std::make_tuple(2,
241                     64000,
242                     string("audio_coding/teststereo32kHz"),
243                     string("pcm"))};
244 
245 // 64 kbps, stereo
246 INSTANTIATE_TEST_SUITE_P(AllTest, OpusFecTest, ::testing::ValuesIn(param_set));
247 
248 }  // namespace webrtc
249