1 /*
2 * Copyright © 2013 Mozilla Foundation
3 *
4 * This program is made available under an ISC-style license. See the
5 * accompanying file LICENSE for details.
6 */
7 #undef NDEBUG
8 #include <assert.h>
9 #include <stddef.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include "cubeb/cubeb.h"
13 #include "cubeb-internal.h"
14
15 #define NELEMS(x) ((int) (sizeof(x) / sizeof(x[0])))
16
17 struct cubeb {
18 struct cubeb_ops * ops;
19 };
20
21 struct cubeb_stream {
22 /*
23 * Note: All implementations of cubeb_stream must keep the following
24 * layout.
25 */
26 struct cubeb * context;
27 void * user_ptr;
28 };
29
30 #if defined(USE_PULSE)
31 int pulse_init(cubeb ** context, char const * context_name);
32 #endif
33 #if defined(USE_PULSE_RUST)
34 int pulse_rust_init(cubeb ** contet, char const * context_name);
35 #endif
36 #if defined(USE_JACK)
37 int jack_init (cubeb ** context, char const * context_name);
38 #endif
39 #if defined(USE_ALSA)
40 int alsa_init(cubeb ** context, char const * context_name);
41 #endif
42 #if defined(USE_AUDIOUNIT)
43 int audiounit_init(cubeb ** context, char const * context_name);
44 #endif
45 #if defined(USE_AUDIOUNIT_RUST)
46 int audiounit_rust_init(cubeb ** contet, char const * context_name);
47 #endif
48 #if defined(USE_WINMM)
49 int winmm_init(cubeb ** context, char const * context_name);
50 #endif
51 #if defined(USE_WASAPI)
52 int wasapi_init(cubeb ** context, char const * context_name);
53 #endif
54 #if defined(USE_SNDIO)
55 int sndio_init(cubeb ** context, char const * context_name);
56 #endif
57 #if defined(USE_SUN)
58 int sun_init(cubeb ** context, char const * context_name);
59 #endif
60 #if defined(USE_OPENSL)
61 int opensl_init(cubeb ** context, char const * context_name);
62 #endif
63 #if defined(USE_OSS)
64 int oss_init(cubeb ** context, char const * context_name);
65 #endif
66 #if defined(USE_AAUDIO)
67 int aaudio_init(cubeb ** context, char const * context_name);
68 #endif
69 #if defined(USE_AUDIOTRACK)
70 int audiotrack_init(cubeb ** context, char const * context_name);
71 #endif
72 #if defined(USE_KAI)
73 int kai_init(cubeb ** context, char const * context_name);
74 #endif
75
76 static int
validate_stream_params(cubeb_stream_params * input_stream_params,cubeb_stream_params * output_stream_params)77 validate_stream_params(cubeb_stream_params * input_stream_params,
78 cubeb_stream_params * output_stream_params)
79 {
80 XASSERT(input_stream_params || output_stream_params);
81 if (output_stream_params) {
82 if (output_stream_params->rate < 1000 || output_stream_params->rate > 192000 ||
83 output_stream_params->channels < 1 || output_stream_params->channels > UINT8_MAX) {
84 return CUBEB_ERROR_INVALID_FORMAT;
85 }
86 }
87 if (input_stream_params) {
88 if (input_stream_params->rate < 1000 || input_stream_params->rate > 192000 ||
89 input_stream_params->channels < 1 || input_stream_params->channels > UINT8_MAX) {
90 return CUBEB_ERROR_INVALID_FORMAT;
91 }
92 }
93 // Rate and sample format must be the same for input and output, if using a
94 // duplex stream
95 if (input_stream_params && output_stream_params) {
96 if (input_stream_params->rate != output_stream_params->rate ||
97 input_stream_params->format != output_stream_params->format) {
98 return CUBEB_ERROR_INVALID_FORMAT;
99 }
100 }
101
102 cubeb_stream_params * params = input_stream_params ?
103 input_stream_params : output_stream_params;
104
105 switch (params->format) {
106 case CUBEB_SAMPLE_S16LE:
107 case CUBEB_SAMPLE_S16BE:
108 case CUBEB_SAMPLE_FLOAT32LE:
109 case CUBEB_SAMPLE_FLOAT32BE:
110 return CUBEB_OK;
111 }
112
113 return CUBEB_ERROR_INVALID_FORMAT;
114 }
115
116 static int
validate_latency(int latency)117 validate_latency(int latency)
118 {
119 if (latency < 1 || latency > 96000) {
120 return CUBEB_ERROR_INVALID_PARAMETER;
121 }
122 return CUBEB_OK;
123 }
124
125 int
cubeb_init(cubeb ** context,char const * context_name,char const * backend_name)126 cubeb_init(cubeb ** context, char const * context_name, char const * backend_name)
127 {
128 int (* init_oneshot)(cubeb **, char const *) = NULL;
129
130 if (backend_name != NULL) {
131 if (!strcmp(backend_name, "pulse")) {
132 #if defined(USE_PULSE)
133 init_oneshot = pulse_init;
134 #endif
135 } else if (!strcmp(backend_name, "pulse-rust")) {
136 #if defined(USE_PULSE_RUST)
137 init_oneshot = pulse_rust_init;
138 #endif
139 } else if (!strcmp(backend_name, "jack")) {
140 #if defined(USE_JACK)
141 init_oneshot = jack_init;
142 #endif
143 } else if (!strcmp(backend_name, "alsa")) {
144 #if defined(USE_ALSA)
145 init_oneshot = alsa_init;
146 #endif
147 } else if (!strcmp(backend_name, "audiounit")) {
148 #if defined(USE_AUDIOUNIT)
149 init_oneshot = audiounit_init;
150 #endif
151 } else if (!strcmp(backend_name, "audiounit-rust")) {
152 #if defined(USE_AUDIOUNIT_RUST)
153 init_oneshot = audiounit_rust_init;
154 #endif
155 } else if (!strcmp(backend_name, "wasapi")) {
156 #if defined(USE_WASAPI)
157 init_oneshot = wasapi_init;
158 #endif
159 } else if (!strcmp(backend_name, "winmm")) {
160 #if defined(USE_WINMM)
161 init_oneshot = winmm_init;
162 #endif
163 } else if (!strcmp(backend_name, "sndio")) {
164 #if defined(USE_SNDIO)
165 init_oneshot = sndio_init;
166 #endif
167 } else if (!strcmp(backend_name, "sun")) {
168 #if defined(USE_SUN)
169 init_oneshot = sun_init;
170 #endif
171 } else if (!strcmp(backend_name, "opensl")) {
172 #if defined(USE_OPENSL)
173 init_oneshot = opensl_init;
174 #endif
175 } else if (!strcmp(backend_name, "oss")) {
176 #if defined(USE_OSS)
177 init_oneshot = oss_init;
178 #endif
179 } else if (!strcmp(backend_name, "aaudio")) {
180 #if defined(USE_AAUDIO)
181 init_oneshot = aaudio_init;
182 #endif
183 } else if (!strcmp(backend_name, "audiotrack")) {
184 #if defined(USE_AUDIOTRACK)
185 init_oneshot = audiotrack_init;
186 #endif
187 } else if (!strcmp(backend_name, "kai")) {
188 #if defined(USE_KAI)
189 init_oneshot = kai_init;
190 #endif
191 } else {
192 /* Already set */
193 }
194 }
195
196 int (* default_init[])(cubeb **, char const *) = {
197 /*
198 * init_oneshot must be at the top to allow user
199 * to override all other choices
200 */
201 init_oneshot,
202 #if defined(USE_PULSE_RUST)
203 pulse_rust_init,
204 #endif
205 #if defined(USE_PULSE)
206 pulse_init,
207 #endif
208 #if defined(USE_JACK)
209 jack_init,
210 #endif
211 #if defined(USE_SNDIO)
212 sndio_init,
213 #endif
214 #if defined(USE_ALSA)
215 alsa_init,
216 #endif
217 #if defined (USE_OSS)
218 oss_init,
219 #endif
220 #if defined(USE_AUDIOUNIT_RUST)
221 audiounit_rust_init,
222 #endif
223 #if defined(USE_AUDIOUNIT)
224 audiounit_init,
225 #endif
226 #if defined(USE_WASAPI)
227 wasapi_init,
228 #endif
229 #if defined(USE_WINMM)
230 winmm_init,
231 #endif
232 #if defined(USE_SUN)
233 sun_init,
234 #endif
235 #if defined(USE_OPENSL)
236 opensl_init,
237 #endif
238 // TODO: should probably be preferred over OpenSLES when available.
239 // Initialization will fail on old android devices.
240 #if defined(USE_AAUDIO)
241 aaudio_init,
242 #endif
243 #if defined(USE_AUDIOTRACK)
244 audiotrack_init,
245 #endif
246 #if defined(USE_KAI)
247 kai_init,
248 #endif
249 };
250 int i;
251
252 if (!context) {
253 return CUBEB_ERROR_INVALID_PARAMETER;
254 }
255
256 #define OK(fn) assert((* context)->ops->fn)
257 for (i = 0; i < NELEMS(default_init); ++i) {
258 if (default_init[i] && default_init[i](context, context_name) == CUBEB_OK) {
259 /* Assert that the minimal API is implemented. */
260 OK(get_backend_id);
261 OK(destroy);
262 OK(stream_init);
263 OK(stream_destroy);
264 OK(stream_start);
265 OK(stream_stop);
266 OK(stream_get_position);
267 return CUBEB_OK;
268 }
269 }
270 return CUBEB_ERROR;
271 }
272
273 char const *
cubeb_get_backend_id(cubeb * context)274 cubeb_get_backend_id(cubeb * context)
275 {
276 if (!context) {
277 return NULL;
278 }
279
280 return context->ops->get_backend_id(context);
281 }
282
283 int
cubeb_get_max_channel_count(cubeb * context,uint32_t * max_channels)284 cubeb_get_max_channel_count(cubeb * context, uint32_t * max_channels)
285 {
286 if (!context || !max_channels) {
287 return CUBEB_ERROR_INVALID_PARAMETER;
288 }
289
290 if (!context->ops->get_max_channel_count) {
291 return CUBEB_ERROR_NOT_SUPPORTED;
292 }
293
294 return context->ops->get_max_channel_count(context, max_channels);
295 }
296
297 int
cubeb_get_min_latency(cubeb * context,cubeb_stream_params * params,uint32_t * latency_ms)298 cubeb_get_min_latency(cubeb * context, cubeb_stream_params * params, uint32_t * latency_ms)
299 {
300 if (!context || !params || !latency_ms) {
301 return CUBEB_ERROR_INVALID_PARAMETER;
302 }
303
304 if (!context->ops->get_min_latency) {
305 return CUBEB_ERROR_NOT_SUPPORTED;
306 }
307
308 return context->ops->get_min_latency(context, *params, latency_ms);
309 }
310
311 int
cubeb_get_preferred_sample_rate(cubeb * context,uint32_t * rate)312 cubeb_get_preferred_sample_rate(cubeb * context, uint32_t * rate)
313 {
314 if (!context || !rate) {
315 return CUBEB_ERROR_INVALID_PARAMETER;
316 }
317
318 if (!context->ops->get_preferred_sample_rate) {
319 return CUBEB_ERROR_NOT_SUPPORTED;
320 }
321
322 return context->ops->get_preferred_sample_rate(context, rate);
323 }
324
325 void
cubeb_destroy(cubeb * context)326 cubeb_destroy(cubeb * context)
327 {
328 if (!context) {
329 return;
330 }
331
332 context->ops->destroy(context);
333 }
334
335 int
cubeb_stream_init(cubeb * context,cubeb_stream ** stream,char const * stream_name,cubeb_devid input_device,cubeb_stream_params * input_stream_params,cubeb_devid output_device,cubeb_stream_params * output_stream_params,unsigned int latency,cubeb_data_callback data_callback,cubeb_state_callback state_callback,void * user_ptr)336 cubeb_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
337 cubeb_devid input_device,
338 cubeb_stream_params * input_stream_params,
339 cubeb_devid output_device,
340 cubeb_stream_params * output_stream_params,
341 unsigned int latency,
342 cubeb_data_callback data_callback,
343 cubeb_state_callback state_callback,
344 void * user_ptr)
345 {
346 int r;
347
348 if (!context || !stream || !data_callback || !state_callback) {
349 return CUBEB_ERROR_INVALID_PARAMETER;
350 }
351
352 if ((r = validate_stream_params(input_stream_params, output_stream_params)) != CUBEB_OK ||
353 (r = validate_latency(latency)) != CUBEB_OK) {
354 return r;
355 }
356
357 r = context->ops->stream_init(context, stream, stream_name,
358 input_device,
359 input_stream_params,
360 output_device,
361 output_stream_params,
362 latency,
363 data_callback,
364 state_callback,
365 user_ptr);
366
367 if (r == CUBEB_ERROR_INVALID_FORMAT) {
368 LOG("Invalid format, %p %p %d %d",
369 output_stream_params, input_stream_params,
370 output_stream_params && output_stream_params->format,
371 input_stream_params && input_stream_params->format);
372 }
373
374 return r;
375 }
376
377 void
cubeb_stream_destroy(cubeb_stream * stream)378 cubeb_stream_destroy(cubeb_stream * stream)
379 {
380 if (!stream) {
381 return;
382 }
383
384 stream->context->ops->stream_destroy(stream);
385 }
386
387 int
cubeb_stream_start(cubeb_stream * stream)388 cubeb_stream_start(cubeb_stream * stream)
389 {
390 if (!stream) {
391 return CUBEB_ERROR_INVALID_PARAMETER;
392 }
393
394 return stream->context->ops->stream_start(stream);
395 }
396
397 int
cubeb_stream_stop(cubeb_stream * stream)398 cubeb_stream_stop(cubeb_stream * stream)
399 {
400 if (!stream) {
401 return CUBEB_ERROR_INVALID_PARAMETER;
402 }
403
404 return stream->context->ops->stream_stop(stream);
405 }
406
407 int
cubeb_stream_reset_default_device(cubeb_stream * stream)408 cubeb_stream_reset_default_device(cubeb_stream * stream)
409 {
410 if (!stream) {
411 return CUBEB_ERROR_INVALID_PARAMETER;
412 }
413
414 if (!stream->context->ops->stream_reset_default_device) {
415 return CUBEB_ERROR_NOT_SUPPORTED;
416 }
417
418 return stream->context->ops->stream_reset_default_device(stream);
419 }
420
421 int
cubeb_stream_get_position(cubeb_stream * stream,uint64_t * position)422 cubeb_stream_get_position(cubeb_stream * stream, uint64_t * position)
423 {
424 if (!stream || !position) {
425 return CUBEB_ERROR_INVALID_PARAMETER;
426 }
427
428 return stream->context->ops->stream_get_position(stream, position);
429 }
430
431 int
cubeb_stream_get_latency(cubeb_stream * stream,uint32_t * latency)432 cubeb_stream_get_latency(cubeb_stream * stream, uint32_t * latency)
433 {
434 if (!stream || !latency) {
435 return CUBEB_ERROR_INVALID_PARAMETER;
436 }
437
438 if (!stream->context->ops->stream_get_latency) {
439 return CUBEB_ERROR_NOT_SUPPORTED;
440 }
441
442 return stream->context->ops->stream_get_latency(stream, latency);
443 }
444
445 int
cubeb_stream_get_input_latency(cubeb_stream * stream,uint32_t * latency)446 cubeb_stream_get_input_latency(cubeb_stream * stream, uint32_t * latency)
447 {
448 if (!stream || !latency) {
449 return CUBEB_ERROR_INVALID_PARAMETER;
450 }
451
452 if (!stream->context->ops->stream_get_input_latency) {
453 return CUBEB_ERROR_NOT_SUPPORTED;
454 }
455
456 return stream->context->ops->stream_get_input_latency(stream, latency);
457 }
458
459 int
cubeb_stream_set_volume(cubeb_stream * stream,float volume)460 cubeb_stream_set_volume(cubeb_stream * stream, float volume)
461 {
462 if (!stream || volume > 1.0 || volume < 0.0) {
463 return CUBEB_ERROR_INVALID_PARAMETER;
464 }
465
466 if (!stream->context->ops->stream_set_volume) {
467 return CUBEB_ERROR_NOT_SUPPORTED;
468 }
469
470 return stream->context->ops->stream_set_volume(stream, volume);
471 }
472
473 int
cubeb_stream_set_name(cubeb_stream * stream,char const * stream_name)474 cubeb_stream_set_name(cubeb_stream * stream, char const * stream_name)
475 {
476 if (!stream || !stream_name) {
477 return CUBEB_ERROR_INVALID_PARAMETER;
478 }
479
480 if (!stream->context->ops->stream_set_name) {
481 return CUBEB_ERROR_NOT_SUPPORTED;
482 }
483
484 return stream->context->ops->stream_set_name(stream, stream_name);
485 }
486
cubeb_stream_get_current_device(cubeb_stream * stream,cubeb_device ** const device)487 int cubeb_stream_get_current_device(cubeb_stream * stream,
488 cubeb_device ** const device)
489 {
490 if (!stream || !device) {
491 return CUBEB_ERROR_INVALID_PARAMETER;
492 }
493
494 if (!stream->context->ops->stream_get_current_device) {
495 return CUBEB_ERROR_NOT_SUPPORTED;
496 }
497
498 return stream->context->ops->stream_get_current_device(stream, device);
499 }
500
cubeb_stream_device_destroy(cubeb_stream * stream,cubeb_device * device)501 int cubeb_stream_device_destroy(cubeb_stream * stream,
502 cubeb_device * device)
503 {
504 if (!stream || !device) {
505 return CUBEB_ERROR_INVALID_PARAMETER;
506 }
507
508 if (!stream->context->ops->stream_device_destroy) {
509 return CUBEB_ERROR_NOT_SUPPORTED;
510 }
511
512 return stream->context->ops->stream_device_destroy(stream, device);
513 }
514
cubeb_stream_register_device_changed_callback(cubeb_stream * stream,cubeb_device_changed_callback device_changed_callback)515 int cubeb_stream_register_device_changed_callback(cubeb_stream * stream,
516 cubeb_device_changed_callback device_changed_callback)
517 {
518 if (!stream) {
519 return CUBEB_ERROR_INVALID_PARAMETER;
520 }
521
522 if (!stream->context->ops->stream_register_device_changed_callback) {
523 return CUBEB_ERROR_NOT_SUPPORTED;
524 }
525
526 return stream->context->ops->stream_register_device_changed_callback(stream, device_changed_callback);
527 }
528
cubeb_stream_user_ptr(cubeb_stream * stream)529 void * cubeb_stream_user_ptr(cubeb_stream * stream)
530 {
531 if (!stream) {
532 return NULL;
533 }
534
535 return stream->user_ptr;
536 }
537
538 static
log_device(cubeb_device_info * device_info)539 void log_device(cubeb_device_info * device_info)
540 {
541 char devfmts[128] = "";
542 const char * devtype, * devstate, * devdeffmt;
543
544 switch (device_info->type) {
545 case CUBEB_DEVICE_TYPE_INPUT:
546 devtype = "input";
547 break;
548 case CUBEB_DEVICE_TYPE_OUTPUT:
549 devtype = "output";
550 break;
551 case CUBEB_DEVICE_TYPE_UNKNOWN:
552 default:
553 devtype = "unknown?";
554 break;
555 };
556
557 switch (device_info->state) {
558 case CUBEB_DEVICE_STATE_DISABLED:
559 devstate = "disabled";
560 break;
561 case CUBEB_DEVICE_STATE_UNPLUGGED:
562 devstate = "unplugged";
563 break;
564 case CUBEB_DEVICE_STATE_ENABLED:
565 devstate = "enabled";
566 break;
567 default:
568 devstate = "unknown?";
569 break;
570 };
571
572 switch (device_info->default_format) {
573 case CUBEB_DEVICE_FMT_S16LE:
574 devdeffmt = "S16LE";
575 break;
576 case CUBEB_DEVICE_FMT_S16BE:
577 devdeffmt = "S16BE";
578 break;
579 case CUBEB_DEVICE_FMT_F32LE:
580 devdeffmt = "F32LE";
581 break;
582 case CUBEB_DEVICE_FMT_F32BE:
583 devdeffmt = "F32BE";
584 break;
585 default:
586 devdeffmt = "unknown?";
587 break;
588 };
589
590 if (device_info->format & CUBEB_DEVICE_FMT_S16LE) {
591 strcat(devfmts, " S16LE");
592 }
593 if (device_info->format & CUBEB_DEVICE_FMT_S16BE) {
594 strcat(devfmts, " S16BE");
595 }
596 if (device_info->format & CUBEB_DEVICE_FMT_F32LE) {
597 strcat(devfmts, " F32LE");
598 }
599 if (device_info->format & CUBEB_DEVICE_FMT_F32BE) {
600 strcat(devfmts, " F32BE");
601 }
602
603 LOG("DeviceID: \"%s\"%s\n"
604 "\tName:\t\"%s\"\n"
605 "\tGroup:\t\"%s\"\n"
606 "\tVendor:\t\"%s\"\n"
607 "\tType:\t%s\n"
608 "\tState:\t%s\n"
609 "\tMaximum channels:\t%u\n"
610 "\tFormat:\t%s (0x%x) (default: %s)\n"
611 "\tRate:\t[%u, %u] (default: %u)\n"
612 "\tLatency: lo %u frames, hi %u frames",
613 device_info->device_id, device_info->preferred ? " (PREFERRED)" : "",
614 device_info->friendly_name,
615 device_info->group_id,
616 device_info->vendor_name,
617 devtype,
618 devstate,
619 device_info->max_channels,
620 (devfmts[0] == '\0') ? devfmts : devfmts + 1, (unsigned int)device_info->format, devdeffmt,
621 device_info->min_rate, device_info->max_rate, device_info->default_rate,
622 device_info->latency_lo, device_info->latency_hi);
623 }
624
cubeb_enumerate_devices(cubeb * context,cubeb_device_type devtype,cubeb_device_collection * collection)625 int cubeb_enumerate_devices(cubeb * context,
626 cubeb_device_type devtype,
627 cubeb_device_collection * collection)
628 {
629 int rv;
630 if ((devtype & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) == 0)
631 return CUBEB_ERROR_INVALID_PARAMETER;
632 if (collection == NULL)
633 return CUBEB_ERROR_INVALID_PARAMETER;
634 if (!context->ops->enumerate_devices)
635 return CUBEB_ERROR_NOT_SUPPORTED;
636
637 rv = context->ops->enumerate_devices(context, devtype, collection);
638
639 if (g_cubeb_log_callback) {
640 for (size_t i = 0; i < collection->count; i++) {
641 log_device(&collection->device[i]);
642 }
643 }
644
645 return rv;
646 }
647
cubeb_device_collection_destroy(cubeb * context,cubeb_device_collection * collection)648 int cubeb_device_collection_destroy(cubeb * context,
649 cubeb_device_collection * collection)
650 {
651 int r;
652
653 if (context == NULL || collection == NULL)
654 return CUBEB_ERROR_INVALID_PARAMETER;
655
656 if (!context->ops->device_collection_destroy)
657 return CUBEB_ERROR_NOT_SUPPORTED;
658
659 if (!collection->device)
660 return CUBEB_OK;
661
662 r = context->ops->device_collection_destroy(context, collection);
663 if (r == CUBEB_OK) {
664 collection->device = NULL;
665 collection->count = 0;
666 }
667
668 return r;
669 }
670
cubeb_register_device_collection_changed(cubeb * context,cubeb_device_type devtype,cubeb_device_collection_changed_callback callback,void * user_ptr)671 int cubeb_register_device_collection_changed(cubeb * context,
672 cubeb_device_type devtype,
673 cubeb_device_collection_changed_callback callback,
674 void * user_ptr)
675 {
676 if (context == NULL || (devtype & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) == 0)
677 return CUBEB_ERROR_INVALID_PARAMETER;
678
679 if (!context->ops->register_device_collection_changed) {
680 return CUBEB_ERROR_NOT_SUPPORTED;
681 }
682
683 return context->ops->register_device_collection_changed(context, devtype, callback, user_ptr);
684 }
685
cubeb_set_log_callback(cubeb_log_level log_level,cubeb_log_callback log_callback)686 int cubeb_set_log_callback(cubeb_log_level log_level,
687 cubeb_log_callback log_callback)
688 {
689 if (log_level < CUBEB_LOG_DISABLED || log_level > CUBEB_LOG_VERBOSE) {
690 return CUBEB_ERROR_INVALID_FORMAT;
691 }
692
693 if (!log_callback && log_level != CUBEB_LOG_DISABLED) {
694 return CUBEB_ERROR_INVALID_PARAMETER;
695 }
696
697 if (g_cubeb_log_callback && log_callback) {
698 return CUBEB_ERROR_NOT_SUPPORTED;
699 }
700
701 g_cubeb_log_callback = log_callback;
702 g_cubeb_log_level = log_level;
703
704 // Logging a message here allows to initialize the asynchronous logger from a
705 // thread that is not the audio rendering thread, and especially to not
706 // initialize it the first time we find a verbose log, which is often in the
707 // audio rendering callback, that runs from the audio rendering thread, and
708 // that is high priority, and that we don't want to block.
709 if (log_level >= CUBEB_LOG_VERBOSE) {
710 ALOGV("Starting cubeb log");
711 }
712
713 return CUBEB_OK;
714 }
715
716