• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <gtest/gtest.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 
9 extern "C" {
10 #include "cras_audio_area.h"
11 #include "cras_iodev.h"
12 #include "cras_iodev_list.h"
13 #include "cras_loopback_iodev.h"
14 #include "cras_shm.h"
15 #include "cras_types.h"
16 #include "dev_stream.h"
17 #include "utlist.h"
18 }
19 
20 namespace {
21 
22 static const unsigned int kBufferFrames = 16384;
23 static const unsigned int kFrameBytes = 4;
24 static const unsigned int kBufferSize = kBufferFrames * kFrameBytes;
25 
26 static struct timespec time_now;
27 static cras_audio_area* dummy_audio_area;
28 static loopback_hook_data_t loop_hook;
29 static struct cras_iodev* enabled_dev;
30 static unsigned int cras_iodev_list_add_input_called;
31 static unsigned int cras_iodev_list_rm_input_called;
32 static unsigned int cras_iodev_list_set_device_enabled_callback_called;
33 static device_enabled_callback_t device_enabled_callback_cb;
34 static device_disabled_callback_t device_disabled_callback_cb;
35 static void* device_enabled_callback_cb_data;
36 static int cras_iodev_list_register_loopback_called;
37 static int cras_iodev_list_unregister_loopback_called;
38 
39 class LoopBackTestSuite : public testing::Test {
40  protected:
SetUp()41   virtual void SetUp() {
42     dummy_audio_area = (cras_audio_area*)calloc(
43         1, sizeof(*dummy_audio_area) + sizeof(cras_channel_area) * 2);
44     for (unsigned int i = 0; i < kBufferSize; i++) {
45       buf_[i] = rand();
46     }
47     fmt_.frame_rate = 48000;
48     fmt_.num_channels = 2;
49     fmt_.format = SND_PCM_FORMAT_S16_LE;
50 
51     loop_in_ = loopback_iodev_create(LOOPBACK_POST_MIX_PRE_DSP);
52     EXPECT_EQ(1, cras_iodev_list_add_input_called);
53     loop_in_->format = &fmt_;
54 
55     loop_hook = NULL;
56     cras_iodev_list_add_input_called = 0;
57     cras_iodev_list_rm_input_called = 0;
58     cras_iodev_list_set_device_enabled_callback_called = 0;
59     cras_iodev_list_register_loopback_called = 0;
60     cras_iodev_list_unregister_loopback_called = 0;
61   }
62 
TearDown()63   virtual void TearDown() {
64     loopback_iodev_destroy(loop_in_);
65     EXPECT_EQ(1, cras_iodev_list_rm_input_called);
66     EXPECT_EQ(NULL, device_enabled_callback_cb);
67     EXPECT_EQ(NULL, device_disabled_callback_cb);
68     free(dummy_audio_area);
69   }
70 
71   uint8_t buf_[kBufferSize];
72   struct cras_audio_format fmt_;
73   struct cras_iodev* loop_in_;
74 };
75 
TEST_F(LoopBackTestSuite,InstallLoopHook)76 TEST_F(LoopBackTestSuite, InstallLoopHook) {
77   struct cras_iodev iodev;
78   struct timespec tstamp;
79 
80   iodev.direction = CRAS_STREAM_OUTPUT;
81   iodev.format = &fmt_;
82   iodev.streams = NULL;
83   iodev.info.idx = 123;
84   enabled_dev = &iodev;
85 
86   // Open loopback devices.
87   EXPECT_EQ(0, loop_in_->configure_dev(loop_in_));
88   EXPECT_EQ(1, cras_iodev_list_set_device_enabled_callback_called);
89   EXPECT_EQ(1, cras_iodev_list_register_loopback_called);
90 
91   // Signal an output device is enabled.
92   device_enabled_callback_cb(&iodev, device_enabled_callback_cb_data);
93 
94   // Expect that a hook was added to the iodev
95   EXPECT_EQ(2, cras_iodev_list_register_loopback_called);
96   ASSERT_NE(reinterpret_cast<loopback_hook_data_t>(NULL), loop_hook);
97 
98   // Check zero frames queued.
99   EXPECT_EQ(0, loop_in_->frames_queued(loop_in_, &tstamp));
100 
101   device_disabled_callback_cb(&iodev, device_enabled_callback_cb_data);
102   EXPECT_EQ(1, cras_iodev_list_unregister_loopback_called);
103   EXPECT_EQ(3, cras_iodev_list_register_loopback_called);
104 
105   enabled_dev->info.idx = 456;
106   device_enabled_callback_cb(&iodev, device_enabled_callback_cb_data);
107   EXPECT_EQ(4, cras_iodev_list_register_loopback_called);
108 
109   // Close loopback devices.
110   EXPECT_EQ(0, loop_in_->close_dev(loop_in_));
111   EXPECT_EQ(2, cras_iodev_list_unregister_loopback_called);
112   EXPECT_EQ(2, cras_iodev_list_set_device_enabled_callback_called);
113 }
114 
TEST_F(LoopBackTestSuite,SelectDevFromAToB)115 TEST_F(LoopBackTestSuite, SelectDevFromAToB) {
116   struct cras_iodev iodev1, iodev2;
117 
118   iodev1.direction = CRAS_STREAM_OUTPUT;
119   iodev2.direction = CRAS_STREAM_OUTPUT;
120   enabled_dev = &iodev1;
121 
122   enabled_dev->info.idx = 111;
123   EXPECT_EQ(0, loop_in_->configure_dev(loop_in_));
124   EXPECT_EQ(1, cras_iodev_list_set_device_enabled_callback_called);
125   EXPECT_EQ(1, cras_iodev_list_register_loopback_called);
126 
127   /* Not the current sender being disabled, assert unregister not called. */
128   iodev2.info.idx = 222;
129   device_disabled_callback_cb(&iodev2, device_enabled_callback_cb_data);
130   EXPECT_EQ(0, cras_iodev_list_unregister_loopback_called);
131   EXPECT_EQ(1, cras_iodev_list_register_loopback_called);
132 
133   enabled_dev = &iodev2;
134   device_disabled_callback_cb(&iodev1, device_enabled_callback_cb_data);
135   EXPECT_EQ(1, cras_iodev_list_unregister_loopback_called);
136   EXPECT_EQ(2, cras_iodev_list_register_loopback_called);
137 
138   EXPECT_EQ(0, loop_in_->close_dev(loop_in_));
139 }
140 
141 // Test how loopback works if there isn't any output devices open.
TEST_F(LoopBackTestSuite,OpenIdleSystem)142 TEST_F(LoopBackTestSuite, OpenIdleSystem) {
143   cras_audio_area* area;
144   unsigned int nread = 1024;
145   struct timespec tstamp;
146   int rc;
147 
148   // No active output device.
149   enabled_dev = NULL;
150   time_now.tv_sec = 100;
151   time_now.tv_nsec = 0;
152 
153   EXPECT_EQ(0, loop_in_->configure_dev(loop_in_));
154   EXPECT_EQ(1, cras_iodev_list_set_device_enabled_callback_called);
155 
156   // Should be 480 samples after 480/frame rate seconds
157   time_now.tv_nsec += 480 * 1e9 / 48000;
158   EXPECT_EQ(480, loop_in_->frames_queued(loop_in_, &tstamp));
159 
160   // Verify frames from loopback record.
161   loop_in_->get_buffer(loop_in_, &area, &nread);
162   EXPECT_EQ(480, nread);
163   memset(buf_, 0, nread * kFrameBytes);
164   rc = memcmp(area->channels[0].buf, buf_, nread * kFrameBytes);
165   EXPECT_EQ(0, rc);
166   loop_in_->put_buffer(loop_in_, nread);
167 
168   // Check zero frames queued.
169   EXPECT_EQ(0, loop_in_->frames_queued(loop_in_, &tstamp));
170 
171   EXPECT_EQ(0, loop_in_->close_dev(loop_in_));
172 }
173 
TEST_F(LoopBackTestSuite,SimpleLoopback)174 TEST_F(LoopBackTestSuite, SimpleLoopback) {
175   cras_audio_area* area;
176   unsigned int nframes = 1024;
177   unsigned int nread = 1024;
178   int rc;
179   struct cras_iodev iodev;
180   struct dev_stream stream;
181   struct timespec tstamp;
182 
183   iodev.streams = &stream;
184   enabled_dev = &iodev;
185 
186   loop_in_->configure_dev(loop_in_);
187   ASSERT_NE(reinterpret_cast<void*>(NULL), loop_hook);
188 
189   // Loopback callback for the hook.
190   loop_hook(buf_, nframes, &fmt_, loop_in_);
191 
192   // Verify frames from loopback record.
193   loop_in_->get_buffer(loop_in_, &area, &nread);
194   EXPECT_EQ(nframes, nread);
195   rc = memcmp(area->channels[0].buf, buf_, nframes * kFrameBytes);
196   EXPECT_EQ(0, rc);
197   loop_in_->put_buffer(loop_in_, nread);
198 
199   // Check zero frames queued.
200   EXPECT_EQ(0, loop_in_->frames_queued(loop_in_, &tstamp));
201 
202   EXPECT_EQ(0, loop_in_->close_dev(loop_in_));
203 }
204 
205 // TODO(chinyue): Test closing last iodev while streaming loopback data.
206 
207 /* Stubs */
208 extern "C" {
209 
cras_audio_area_config_buf_pointers(struct cras_audio_area * area,const struct cras_audio_format * fmt,uint8_t * base_buffer)210 void cras_audio_area_config_buf_pointers(struct cras_audio_area* area,
211                                          const struct cras_audio_format* fmt,
212                                          uint8_t* base_buffer) {
213   dummy_audio_area->channels[0].buf = base_buffer;
214 }
215 
cras_iodev_free_audio_area(struct cras_iodev * iodev)216 void cras_iodev_free_audio_area(struct cras_iodev* iodev) {}
217 
cras_iodev_free_format(struct cras_iodev * iodev)218 void cras_iodev_free_format(struct cras_iodev* iodev) {}
219 
cras_iodev_init_audio_area(struct cras_iodev * iodev,int num_channels)220 void cras_iodev_init_audio_area(struct cras_iodev* iodev, int num_channels) {
221   iodev->area = dummy_audio_area;
222 }
223 
cras_iodev_add_node(struct cras_iodev * iodev,struct cras_ionode * node)224 void cras_iodev_add_node(struct cras_iodev* iodev, struct cras_ionode* node) {
225   DL_APPEND(iodev->nodes, node);
226 }
227 
cras_iodev_set_active_node(struct cras_iodev * iodev,struct cras_ionode * node)228 void cras_iodev_set_active_node(struct cras_iodev* iodev,
229                                 struct cras_ionode* node) {}
cras_iodev_list_register_loopback(enum CRAS_LOOPBACK_TYPE loopback_type,unsigned int output_dev_idx,loopback_hook_data_t hook_data,loopback_hook_control_t hook_start,unsigned int loopback_dev_idx)230 void cras_iodev_list_register_loopback(enum CRAS_LOOPBACK_TYPE loopback_type,
231                                        unsigned int output_dev_idx,
232                                        loopback_hook_data_t hook_data,
233                                        loopback_hook_control_t hook_start,
234                                        unsigned int loopback_dev_idx) {
235   cras_iodev_list_register_loopback_called++;
236   loop_hook = hook_data;
237 }
238 
cras_iodev_list_unregister_loopback(enum CRAS_LOOPBACK_TYPE loopback_type,unsigned int output_dev_idx,unsigned int loopback_dev_idx)239 void cras_iodev_list_unregister_loopback(enum CRAS_LOOPBACK_TYPE loopback_type,
240                                          unsigned int output_dev_idx,
241                                          unsigned int loopback_dev_idx) {
242   cras_iodev_list_unregister_loopback_called++;
243 }
244 
cras_iodev_list_add_input(struct cras_iodev * input)245 int cras_iodev_list_add_input(struct cras_iodev* input) {
246   cras_iodev_list_add_input_called++;
247   return 0;
248 }
249 
cras_iodev_list_rm_input(struct cras_iodev * input)250 int cras_iodev_list_rm_input(struct cras_iodev* input) {
251   cras_iodev_list_rm_input_called++;
252   return 0;
253 }
254 
cras_iodev_list_set_device_enabled_callback(device_enabled_callback_t enabled_cb,device_disabled_callback_t disabled_cb,void * cb_data)255 int cras_iodev_list_set_device_enabled_callback(
256     device_enabled_callback_t enabled_cb,
257     device_disabled_callback_t disabled_cb,
258     void* cb_data) {
259   cras_iodev_list_set_device_enabled_callback_called++;
260   device_enabled_callback_cb = enabled_cb;
261   device_disabled_callback_cb = disabled_cb;
262   device_enabled_callback_cb_data = cb_data;
263   return 0;
264 }
265 
clock_gettime(clockid_t clk_id,struct timespec * tp)266 int clock_gettime(clockid_t clk_id, struct timespec* tp) {
267   *tp = time_now;
268   return 0;
269 }
270 
cras_iodev_list_get_first_enabled_iodev(enum CRAS_STREAM_DIRECTION direction)271 struct cras_iodev* cras_iodev_list_get_first_enabled_iodev(
272     enum CRAS_STREAM_DIRECTION direction) {
273   return enabled_dev;
274 }
275 
276 }  // extern "C"
277 
278 }  //  namespace
279 
main(int argc,char ** argv)280 int main(int argc, char** argv) {
281   ::testing::InitGoogleTest(&argc, argv);
282   return RUN_ALL_TESTS();
283 }
284