cubeb_pulse.c (52402B)
1 /* 2 * Copyright © 2011 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 "cubeb-internal.h" 9 #include "cubeb/cubeb.h" 10 #include "cubeb_mixer.h" 11 #include "cubeb_strings.h" 12 #include <assert.h> 13 #include <dlfcn.h> 14 #include <pulse/pulseaudio.h> 15 #include <stdio.h> 16 #include <stdlib.h> 17 #include <string.h> 18 19 #ifdef DISABLE_LIBPULSE_DLOPEN 20 #define WRAP(x) x 21 #else 22 #define WRAP(x) (*cubeb_##x) 23 #define LIBPULSE_API_VISIT(X) \ 24 X(pa_channel_map_can_balance) \ 25 X(pa_channel_map_init) \ 26 X(pa_context_connect) \ 27 X(pa_context_disconnect) \ 28 X(pa_context_drain) \ 29 X(pa_context_get_server_info) \ 30 X(pa_context_get_sink_info_by_name) \ 31 X(pa_context_get_sink_info_list) \ 32 X(pa_context_get_sink_input_info) \ 33 X(pa_context_get_source_info_list) \ 34 X(pa_context_get_state) \ 35 X(pa_context_new) \ 36 X(pa_context_rttime_new) \ 37 X(pa_context_set_sink_input_volume) \ 38 X(pa_context_set_state_callback) \ 39 X(pa_context_unref) \ 40 X(pa_cvolume_set) \ 41 X(pa_cvolume_set_balance) \ 42 X(pa_frame_size) \ 43 X(pa_operation_get_state) \ 44 X(pa_operation_unref) \ 45 X(pa_proplist_gets) \ 46 X(pa_rtclock_now) \ 47 X(pa_stream_begin_write) \ 48 X(pa_stream_cancel_write) \ 49 X(pa_stream_connect_playback) \ 50 X(pa_stream_cork) \ 51 X(pa_stream_disconnect) \ 52 X(pa_stream_get_channel_map) \ 53 X(pa_stream_get_index) \ 54 X(pa_stream_get_latency) \ 55 X(pa_stream_get_sample_spec) \ 56 X(pa_stream_get_state) \ 57 X(pa_stream_get_time) \ 58 X(pa_stream_new) \ 59 X(pa_stream_set_state_callback) \ 60 X(pa_stream_set_write_callback) \ 61 X(pa_stream_unref) \ 62 X(pa_stream_update_timing_info) \ 63 X(pa_stream_write) \ 64 X(pa_sw_volume_from_linear) \ 65 X(pa_threaded_mainloop_free) \ 66 X(pa_threaded_mainloop_get_api) \ 67 X(pa_threaded_mainloop_in_thread) \ 68 X(pa_threaded_mainloop_lock) \ 69 X(pa_threaded_mainloop_new) \ 70 X(pa_threaded_mainloop_signal) \ 71 X(pa_threaded_mainloop_start) \ 72 X(pa_threaded_mainloop_stop) \ 73 X(pa_threaded_mainloop_unlock) \ 74 X(pa_threaded_mainloop_wait) \ 75 X(pa_usec_to_bytes) \ 76 X(pa_stream_set_read_callback) \ 77 X(pa_stream_connect_record) \ 78 X(pa_stream_readable_size) \ 79 X(pa_stream_writable_size) \ 80 X(pa_stream_peek) \ 81 X(pa_stream_drop) \ 82 X(pa_stream_get_buffer_attr) \ 83 X(pa_stream_get_device_name) \ 84 X(pa_context_set_subscribe_callback) \ 85 X(pa_context_subscribe) \ 86 X(pa_mainloop_api_once) \ 87 X(pa_get_library_version) \ 88 X(pa_channel_map_init_auto) \ 89 X(pa_stream_set_name) 90 91 #define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x; 92 LIBPULSE_API_VISIT(MAKE_TYPEDEF); 93 #undef MAKE_TYPEDEF 94 #endif 95 96 #if PA_CHECK_VERSION(2, 0, 0) 97 static int has_pulse_v2 = 0; 98 #endif 99 100 static struct cubeb_ops const pulse_ops; 101 102 struct cubeb_default_sink_info { 103 pa_channel_map channel_map; 104 uint32_t sample_spec_rate; 105 pa_sink_flags_t flags; 106 }; 107 108 struct cubeb { 109 struct cubeb_ops const * ops; 110 void * libpulse; 111 pa_threaded_mainloop * mainloop; 112 pa_context * context; 113 struct cubeb_default_sink_info * default_sink_info; 114 char * context_name; 115 int error; 116 cubeb_device_collection_changed_callback output_collection_changed_callback; 117 void * output_collection_changed_user_ptr; 118 cubeb_device_collection_changed_callback input_collection_changed_callback; 119 void * input_collection_changed_user_ptr; 120 cubeb_strings * device_ids; 121 }; 122 123 struct cubeb_stream { 124 /* Note: Must match cubeb_stream layout in cubeb.c. */ 125 cubeb * context; 126 void * user_ptr; 127 /**/ 128 pa_stream * output_stream; 129 pa_stream * input_stream; 130 cubeb_data_callback data_callback; 131 cubeb_state_callback state_callback; 132 pa_time_event * drain_timer; 133 pa_sample_spec output_sample_spec; 134 pa_sample_spec input_sample_spec; 135 int shutdown; 136 float volume; 137 cubeb_state state; 138 }; 139 140 static const float PULSE_NO_GAIN = -1.0; 141 142 enum cork_state { UNCORK = 0, CORK = 1 << 0, NOTIFY = 1 << 1 }; 143 144 static int 145 intern_device_id(cubeb * ctx, char const ** id) 146 { 147 char const * interned; 148 149 assert(ctx); 150 assert(id); 151 152 interned = cubeb_strings_intern(ctx->device_ids, *id); 153 if (!interned) { 154 return CUBEB_ERROR; 155 } 156 157 *id = interned; 158 159 return CUBEB_OK; 160 } 161 162 static void 163 sink_info_callback(pa_context * context, const pa_sink_info * info, int eol, 164 void * u) 165 { 166 (void)context; 167 cubeb * ctx = u; 168 if (!eol) { 169 free(ctx->default_sink_info); 170 ctx->default_sink_info = malloc(sizeof(struct cubeb_default_sink_info)); 171 memcpy(&ctx->default_sink_info->channel_map, &info->channel_map, 172 sizeof(pa_channel_map)); 173 ctx->default_sink_info->sample_spec_rate = info->sample_spec.rate; 174 ctx->default_sink_info->flags = info->flags; 175 } 176 WRAP(pa_threaded_mainloop_signal)(ctx->mainloop, 0); 177 } 178 179 static void 180 server_info_callback(pa_context * context, const pa_server_info * info, 181 void * u) 182 { 183 pa_operation * o; 184 o = WRAP(pa_context_get_sink_info_by_name)(context, info->default_sink_name, 185 sink_info_callback, u); 186 if (o) { 187 WRAP(pa_operation_unref)(o); 188 } 189 } 190 191 static void 192 context_state_callback(pa_context * c, void * u) 193 { 194 cubeb * ctx = u; 195 if (!PA_CONTEXT_IS_GOOD(WRAP(pa_context_get_state)(c))) { 196 ctx->error = 1; 197 } 198 WRAP(pa_threaded_mainloop_signal)(ctx->mainloop, 0); 199 } 200 201 static void 202 context_notify_callback(pa_context * c, void * u) 203 { 204 (void)c; 205 cubeb * ctx = u; 206 WRAP(pa_threaded_mainloop_signal)(ctx->mainloop, 0); 207 } 208 209 static void 210 stream_success_callback(pa_stream * s, int success, void * u) 211 { 212 (void)s; 213 (void)success; 214 cubeb_stream * stm = u; 215 WRAP(pa_threaded_mainloop_signal)(stm->context->mainloop, 0); 216 } 217 218 static void 219 stream_state_change_callback(cubeb_stream * stm, cubeb_state s) 220 { 221 stm->state = s; 222 stm->state_callback(stm, stm->user_ptr, s); 223 } 224 225 static void 226 stream_drain_callback(pa_mainloop_api * a, pa_time_event * e, 227 struct timeval const * tv, void * u) 228 { 229 (void)a; 230 (void)tv; 231 cubeb_stream * stm = u; 232 assert(stm->drain_timer == e); 233 stream_state_change_callback(stm, CUBEB_STATE_DRAINED); 234 /* there's no pa_rttime_free, so use this instead. */ 235 a->time_free(stm->drain_timer); 236 stm->drain_timer = NULL; 237 WRAP(pa_threaded_mainloop_signal)(stm->context->mainloop, 0); 238 } 239 240 static void 241 stream_state_callback(pa_stream * s, void * u) 242 { 243 cubeb_stream * stm = u; 244 if (!PA_STREAM_IS_GOOD(WRAP(pa_stream_get_state)(s))) { 245 stream_state_change_callback(stm, CUBEB_STATE_ERROR); 246 } 247 WRAP(pa_threaded_mainloop_signal)(stm->context->mainloop, 0); 248 } 249 250 static void 251 trigger_user_callback(pa_stream * s, void const * input_data, size_t nbytes, 252 cubeb_stream * stm) 253 { 254 void * buffer; 255 size_t size; 256 int r; 257 long got; 258 size_t towrite, read_offset; 259 size_t frame_size; 260 261 frame_size = WRAP(pa_frame_size)(&stm->output_sample_spec); 262 assert(nbytes % frame_size == 0); 263 264 towrite = nbytes; 265 read_offset = 0; 266 while (towrite) { 267 size = towrite; 268 r = WRAP(pa_stream_begin_write)(s, &buffer, &size); 269 // Note: this has failed running under rr on occassion - needs 270 // investigation. 271 assert(r == 0); 272 assert(size > 0); 273 assert(size % frame_size == 0); 274 275 LOGV("Trigger user callback with output buffer size=%zd, read_offset=%zd", 276 size, read_offset); 277 got = stm->data_callback(stm, stm->user_ptr, 278 (uint8_t const *)input_data + read_offset, buffer, 279 size / frame_size); 280 if (got < 0) { 281 WRAP(pa_stream_cancel_write)(s); 282 stm->shutdown = 1; 283 stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); 284 return; 285 } 286 // If more iterations move offset of read buffer 287 if (input_data) { 288 size_t in_frame_size = WRAP(pa_frame_size)(&stm->input_sample_spec); 289 read_offset += (size / frame_size) * in_frame_size; 290 } 291 292 if (stm->volume != PULSE_NO_GAIN) { 293 uint32_t samples = size * stm->output_sample_spec.channels / frame_size; 294 295 if (stm->output_sample_spec.format == PA_SAMPLE_S16BE || 296 stm->output_sample_spec.format == PA_SAMPLE_S16LE) { 297 short * b = buffer; 298 for (uint32_t i = 0; i < samples; i++) { 299 b[i] *= stm->volume; 300 } 301 } else { 302 float * b = buffer; 303 for (uint32_t i = 0; i < samples; i++) { 304 b[i] *= stm->volume; 305 } 306 } 307 } 308 309 r = WRAP(pa_stream_write)(s, buffer, got * frame_size, NULL, 0, 310 PA_SEEK_RELATIVE); 311 assert(r == 0); 312 313 if ((size_t)got < size / frame_size) { 314 pa_usec_t latency = 0; 315 r = WRAP(pa_stream_get_latency)(s, &latency, NULL); 316 if (r == -PA_ERR_NODATA) { 317 /* this needs a better guess. */ 318 latency = 100 * PA_USEC_PER_MSEC; 319 } 320 assert(r == 0 || r == -PA_ERR_NODATA); 321 /* pa_stream_drain is useless, see PA bug# 866. this is a workaround. */ 322 /* arbitrary safety margin: double the current latency. */ 323 assert(!stm->drain_timer); 324 stm->drain_timer = WRAP(pa_context_rttime_new)( 325 stm->context->context, WRAP(pa_rtclock_now)() + 2 * latency, 326 stream_drain_callback, stm); 327 stm->shutdown = 1; 328 return; 329 } 330 331 towrite -= size; 332 } 333 334 assert(towrite == 0); 335 } 336 337 static int 338 read_from_input(pa_stream * s, void const ** buffer, size_t * size) 339 { 340 size_t readable_size = WRAP(pa_stream_readable_size)(s); 341 if (readable_size > 0) { 342 if (WRAP(pa_stream_peek)(s, buffer, size) < 0) { 343 return -1; 344 } 345 } 346 return readable_size; 347 } 348 349 static void 350 stream_write_callback(pa_stream * s, size_t nbytes, void * u) 351 { 352 LOGV("Output callback to be written buffer size %zd", nbytes); 353 cubeb_stream * stm = u; 354 if (stm->shutdown || stm->state != CUBEB_STATE_STARTED) { 355 return; 356 } 357 358 if (!stm->input_stream) { 359 // Output/playback only operation. 360 // Write directly to output 361 assert(!stm->input_stream && stm->output_stream); 362 trigger_user_callback(s, NULL, nbytes, stm); 363 } 364 } 365 366 static void 367 stream_read_callback(pa_stream * s, size_t nbytes, void * u) 368 { 369 LOGV("Input callback buffer size %zd", nbytes); 370 cubeb_stream * stm = u; 371 if (stm->shutdown) { 372 return; 373 } 374 375 void const * read_data = NULL; 376 size_t read_size; 377 while (read_from_input(s, &read_data, &read_size) > 0) { 378 /* read_data can be NULL in case of a hole. */ 379 if (read_data) { 380 size_t in_frame_size = WRAP(pa_frame_size)(&stm->input_sample_spec); 381 size_t read_frames = read_size / in_frame_size; 382 383 if (stm->output_stream) { 384 // input/capture + output/playback operation 385 size_t out_frame_size = WRAP(pa_frame_size)(&stm->output_sample_spec); 386 size_t write_size = read_frames * out_frame_size; 387 // Offer full duplex data for writing 388 trigger_user_callback(stm->output_stream, read_data, write_size, stm); 389 } else { 390 // input/capture only operation. Call callback directly 391 long got = stm->data_callback(stm, stm->user_ptr, read_data, NULL, 392 read_frames); 393 if (got < 0 || (size_t)got != read_frames) { 394 WRAP(pa_stream_cancel_write)(s); 395 stm->shutdown = 1; 396 if (got < 0) { 397 stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); 398 } 399 break; 400 } 401 } 402 } 403 if (read_size > 0) { 404 WRAP(pa_stream_drop)(s); 405 } 406 407 if (stm->shutdown) { 408 return; 409 } 410 } 411 } 412 413 static int 414 wait_until_context_ready(cubeb * ctx) 415 { 416 for (;;) { 417 pa_context_state_t state = WRAP(pa_context_get_state)(ctx->context); 418 if (!PA_CONTEXT_IS_GOOD(state)) 419 return -1; 420 if (state == PA_CONTEXT_READY) 421 break; 422 WRAP(pa_threaded_mainloop_wait)(ctx->mainloop); 423 } 424 return 0; 425 } 426 427 static int 428 wait_until_io_stream_ready(pa_stream * stream, pa_threaded_mainloop * mainloop) 429 { 430 if (!stream || !mainloop) { 431 return -1; 432 } 433 for (;;) { 434 pa_stream_state_t state = WRAP(pa_stream_get_state)(stream); 435 if (!PA_STREAM_IS_GOOD(state)) 436 return -1; 437 if (state == PA_STREAM_READY) 438 break; 439 WRAP(pa_threaded_mainloop_wait)(mainloop); 440 } 441 return 0; 442 } 443 444 static int 445 wait_until_stream_ready(cubeb_stream * stm) 446 { 447 if (stm->output_stream && 448 wait_until_io_stream_ready(stm->output_stream, stm->context->mainloop) == 449 -1) { 450 return -1; 451 } 452 if (stm->input_stream && 453 wait_until_io_stream_ready(stm->input_stream, stm->context->mainloop) == 454 -1) { 455 return -1; 456 } 457 return 0; 458 } 459 460 static int 461 operation_wait(cubeb * ctx, pa_stream * stream, pa_operation * o) 462 { 463 while (WRAP(pa_operation_get_state)(o) == PA_OPERATION_RUNNING) { 464 WRAP(pa_threaded_mainloop_wait)(ctx->mainloop); 465 if (!PA_CONTEXT_IS_GOOD(WRAP(pa_context_get_state)(ctx->context))) { 466 return -1; 467 } 468 if (stream && !PA_STREAM_IS_GOOD(WRAP(pa_stream_get_state)(stream))) { 469 return -1; 470 } 471 } 472 return 0; 473 } 474 475 static void 476 cork_io_stream(cubeb_stream * stm, pa_stream * io_stream, enum cork_state state) 477 { 478 pa_operation * o; 479 if (!io_stream) { 480 return; 481 } 482 o = WRAP(pa_stream_cork)(io_stream, state & CORK, stream_success_callback, 483 stm); 484 if (o) { 485 operation_wait(stm->context, io_stream, o); 486 WRAP(pa_operation_unref)(o); 487 } 488 } 489 490 static void 491 stream_cork(cubeb_stream * stm, enum cork_state state) 492 { 493 WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop); 494 cork_io_stream(stm, stm->output_stream, state); 495 cork_io_stream(stm, stm->input_stream, state); 496 WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop); 497 498 if (state & NOTIFY) { 499 stream_state_change_callback(stm, state & CORK ? CUBEB_STATE_STOPPED 500 : CUBEB_STATE_STARTED); 501 } 502 } 503 504 static int 505 stream_update_timing_info(cubeb_stream * stm) 506 { 507 int r = -1; 508 pa_operation * o = NULL; 509 if (stm->output_stream) { 510 o = WRAP(pa_stream_update_timing_info)(stm->output_stream, 511 stream_success_callback, stm); 512 if (o) { 513 r = operation_wait(stm->context, stm->output_stream, o); 514 WRAP(pa_operation_unref)(o); 515 } 516 if (r != 0) { 517 return r; 518 } 519 } 520 521 if (stm->input_stream) { 522 o = WRAP(pa_stream_update_timing_info)(stm->input_stream, 523 stream_success_callback, stm); 524 if (o) { 525 r = operation_wait(stm->context, stm->input_stream, o); 526 WRAP(pa_operation_unref)(o); 527 } 528 } 529 530 return r; 531 } 532 533 static pa_channel_position_t 534 cubeb_channel_to_pa_channel(cubeb_channel channel) 535 { 536 switch (channel) { 537 case CHANNEL_FRONT_LEFT: 538 return PA_CHANNEL_POSITION_FRONT_LEFT; 539 case CHANNEL_FRONT_RIGHT: 540 return PA_CHANNEL_POSITION_FRONT_RIGHT; 541 case CHANNEL_FRONT_CENTER: 542 return PA_CHANNEL_POSITION_FRONT_CENTER; 543 case CHANNEL_LOW_FREQUENCY: 544 return PA_CHANNEL_POSITION_LFE; 545 case CHANNEL_BACK_LEFT: 546 return PA_CHANNEL_POSITION_REAR_LEFT; 547 case CHANNEL_BACK_RIGHT: 548 return PA_CHANNEL_POSITION_REAR_RIGHT; 549 case CHANNEL_FRONT_LEFT_OF_CENTER: 550 return PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER; 551 case CHANNEL_FRONT_RIGHT_OF_CENTER: 552 return PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER; 553 case CHANNEL_BACK_CENTER: 554 return PA_CHANNEL_POSITION_REAR_CENTER; 555 case CHANNEL_SIDE_LEFT: 556 return PA_CHANNEL_POSITION_SIDE_LEFT; 557 case CHANNEL_SIDE_RIGHT: 558 return PA_CHANNEL_POSITION_SIDE_RIGHT; 559 case CHANNEL_TOP_CENTER: 560 return PA_CHANNEL_POSITION_TOP_CENTER; 561 case CHANNEL_TOP_FRONT_LEFT: 562 return PA_CHANNEL_POSITION_TOP_FRONT_LEFT; 563 case CHANNEL_TOP_FRONT_CENTER: 564 return PA_CHANNEL_POSITION_TOP_FRONT_CENTER; 565 case CHANNEL_TOP_FRONT_RIGHT: 566 return PA_CHANNEL_POSITION_TOP_FRONT_RIGHT; 567 case CHANNEL_TOP_BACK_LEFT: 568 return PA_CHANNEL_POSITION_TOP_REAR_LEFT; 569 case CHANNEL_TOP_BACK_CENTER: 570 return PA_CHANNEL_POSITION_TOP_REAR_CENTER; 571 case CHANNEL_TOP_BACK_RIGHT: 572 return PA_CHANNEL_POSITION_TOP_REAR_RIGHT; 573 default: 574 return PA_CHANNEL_POSITION_INVALID; 575 } 576 } 577 578 static void 579 layout_to_channel_map(cubeb_channel_layout layout, pa_channel_map * cm) 580 { 581 assert(cm && layout != CUBEB_LAYOUT_UNDEFINED); 582 583 WRAP(pa_channel_map_init)(cm); 584 585 uint32_t channels = 0; 586 cubeb_channel_layout channelMap = layout; 587 for (uint32_t i = 0; channelMap != 0; ++i) { 588 uint32_t channel = (channelMap & 1) << i; 589 if (channel != 0) { 590 cm->map[channels] = cubeb_channel_to_pa_channel(channel); 591 channels++; 592 } 593 channelMap = channelMap >> 1; 594 } 595 unsigned int channels_from_layout = cubeb_channel_layout_nb_channels(layout); 596 assert(channels_from_layout <= UINT8_MAX); 597 cm->channels = (uint8_t)channels_from_layout; 598 599 // Special case single channel center mapping as mono. 600 if (cm->channels == 1 && cm->map[0] == PA_CHANNEL_POSITION_FRONT_CENTER) { 601 cm->map[0] = PA_CHANNEL_POSITION_MONO; 602 } 603 } 604 605 static void 606 pulse_context_destroy(cubeb * ctx); 607 static void 608 pulse_destroy(cubeb * ctx); 609 610 static int 611 pulse_context_init(cubeb * ctx) 612 { 613 int r; 614 615 if (ctx->context) { 616 assert(ctx->error == 1); 617 pulse_context_destroy(ctx); 618 } 619 620 ctx->context = WRAP(pa_context_new)( 621 WRAP(pa_threaded_mainloop_get_api)(ctx->mainloop), ctx->context_name); 622 if (!ctx->context) { 623 return -1; 624 } 625 WRAP(pa_context_set_state_callback) 626 (ctx->context, context_state_callback, ctx); 627 628 WRAP(pa_threaded_mainloop_lock)(ctx->mainloop); 629 r = WRAP(pa_context_connect)(ctx->context, NULL, 0, NULL); 630 631 if (r < 0 || wait_until_context_ready(ctx) != 0) { 632 WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop); 633 pulse_context_destroy(ctx); 634 ctx->context = NULL; 635 return -1; 636 } 637 638 WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop); 639 640 ctx->error = 0; 641 642 return 0; 643 } 644 645 static int 646 pulse_subscribe_notifications(cubeb * context, pa_subscription_mask_t mask); 647 648 /*static*/ int 649 pulse_init(cubeb ** context, char const * context_name) 650 { 651 void * libpulse = NULL; 652 cubeb * ctx; 653 pa_operation * o; 654 655 *context = NULL; 656 657 #ifndef DISABLE_LIBPULSE_DLOPEN 658 libpulse = dlopen("libpulse.so.0", RTLD_LAZY); 659 if (!libpulse) { 660 libpulse = dlopen("libpulse.so", RTLD_LAZY); 661 if (!libpulse) { 662 return CUBEB_ERROR; 663 } 664 } 665 666 #define LOAD(x) \ 667 { \ 668 cubeb_##x = dlsym(libpulse, #x); \ 669 if (!cubeb_##x) { \ 670 dlclose(libpulse); \ 671 return CUBEB_ERROR; \ 672 } \ 673 } 674 675 LIBPULSE_API_VISIT(LOAD); 676 #undef LOAD 677 #endif 678 679 #if PA_CHECK_VERSION(2, 0, 0) 680 const char * version = WRAP(pa_get_library_version)(); 681 has_pulse_v2 = strtol(version, NULL, 10) >= 2; 682 #endif 683 684 ctx = calloc(1, sizeof(*ctx)); 685 assert(ctx); 686 687 ctx->ops = &pulse_ops; 688 ctx->libpulse = libpulse; 689 if (cubeb_strings_init(&ctx->device_ids) != CUBEB_OK) { 690 pulse_destroy(ctx); 691 return CUBEB_ERROR; 692 } 693 694 ctx->mainloop = WRAP(pa_threaded_mainloop_new)(); 695 ctx->default_sink_info = NULL; 696 697 WRAP(pa_threaded_mainloop_start)(ctx->mainloop); 698 699 ctx->context_name = context_name ? strdup(context_name) : NULL; 700 if (pulse_context_init(ctx) != 0) { 701 pulse_destroy(ctx); 702 return CUBEB_ERROR; 703 } 704 705 /* server_info_callback performs a second async query, which is 706 responsible for initializing default_sink_info and signalling the 707 mainloop to end the wait. */ 708 WRAP(pa_threaded_mainloop_lock)(ctx->mainloop); 709 o = WRAP(pa_context_get_server_info)(ctx->context, server_info_callback, ctx); 710 if (o) { 711 operation_wait(ctx, NULL, o); 712 WRAP(pa_operation_unref)(o); 713 } 714 WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop); 715 716 /* Update `default_sink_info` when the default device changes. */ 717 pulse_subscribe_notifications(ctx, PA_SUBSCRIPTION_MASK_SERVER); 718 719 *context = ctx; 720 721 return CUBEB_OK; 722 } 723 724 static char const * 725 pulse_get_backend_id(cubeb * ctx) 726 { 727 (void)ctx; 728 return "pulse"; 729 } 730 731 static int 732 pulse_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) 733 { 734 (void)ctx; 735 assert(ctx && max_channels); 736 737 if (!ctx->default_sink_info) 738 return CUBEB_ERROR; 739 740 *max_channels = ctx->default_sink_info->channel_map.channels; 741 742 return CUBEB_OK; 743 } 744 745 static int 746 pulse_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) 747 { 748 assert(ctx && rate); 749 (void)ctx; 750 751 if (!ctx->default_sink_info) 752 return CUBEB_ERROR; 753 754 *rate = ctx->default_sink_info->sample_spec_rate; 755 756 return CUBEB_OK; 757 } 758 759 static int 760 pulse_get_min_latency(cubeb * ctx, cubeb_stream_params params, 761 uint32_t * latency_frames) 762 { 763 (void)ctx; 764 // According to PulseAudio developers, this is a safe minimum. 765 *latency_frames = 25 * params.rate / 1000; 766 767 return CUBEB_OK; 768 } 769 770 static void 771 pulse_context_destroy(cubeb * ctx) 772 { 773 pa_operation * o; 774 775 WRAP(pa_threaded_mainloop_lock)(ctx->mainloop); 776 o = WRAP(pa_context_drain)(ctx->context, context_notify_callback, ctx); 777 if (o) { 778 operation_wait(ctx, NULL, o); 779 WRAP(pa_operation_unref)(o); 780 } 781 WRAP(pa_context_set_state_callback)(ctx->context, NULL, NULL); 782 WRAP(pa_context_disconnect)(ctx->context); 783 WRAP(pa_context_unref)(ctx->context); 784 WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop); 785 } 786 787 static void 788 pulse_destroy(cubeb * ctx) 789 { 790 assert(!ctx->input_collection_changed_callback && 791 !ctx->input_collection_changed_user_ptr && 792 !ctx->output_collection_changed_callback && 793 !ctx->output_collection_changed_user_ptr); 794 free(ctx->context_name); 795 if (ctx->context) { 796 pulse_context_destroy(ctx); 797 } 798 799 if (ctx->mainloop) { 800 WRAP(pa_threaded_mainloop_stop)(ctx->mainloop); 801 WRAP(pa_threaded_mainloop_free)(ctx->mainloop); 802 } 803 804 if (ctx->device_ids) { 805 cubeb_strings_destroy(ctx->device_ids); 806 } 807 #ifndef DISABLE_LIBPULSE_DLOPEN 808 if (ctx->libpulse) { 809 dlclose(ctx->libpulse); 810 } 811 #endif 812 free(ctx->default_sink_info); 813 free(ctx); 814 } 815 816 static void 817 pulse_stream_destroy(cubeb_stream * stm); 818 819 static pa_sample_format_t 820 to_pulse_format(cubeb_sample_format format) 821 { 822 switch (format) { 823 case CUBEB_SAMPLE_S16LE: 824 return PA_SAMPLE_S16LE; 825 case CUBEB_SAMPLE_S16BE: 826 return PA_SAMPLE_S16BE; 827 case CUBEB_SAMPLE_FLOAT32LE: 828 return PA_SAMPLE_FLOAT32LE; 829 case CUBEB_SAMPLE_FLOAT32BE: 830 return PA_SAMPLE_FLOAT32BE; 831 default: 832 return PA_SAMPLE_INVALID; 833 } 834 } 835 836 static cubeb_channel_layout 837 pulse_default_layout_for_channels(uint32_t ch) 838 { 839 assert(ch > 0 && ch <= 8); 840 switch (ch) { 841 case 1: 842 return CUBEB_LAYOUT_MONO; 843 case 2: 844 return CUBEB_LAYOUT_STEREO; 845 case 3: 846 return CUBEB_LAYOUT_3F; 847 case 4: 848 return CUBEB_LAYOUT_QUAD; 849 case 5: 850 return CUBEB_LAYOUT_3F2; 851 case 6: 852 return CUBEB_LAYOUT_3F_LFE | CHANNEL_SIDE_LEFT | CHANNEL_SIDE_RIGHT; 853 case 7: 854 return CUBEB_LAYOUT_3F3R_LFE; 855 case 8: 856 return CUBEB_LAYOUT_3F4_LFE; 857 } 858 // Never get here! 859 return CUBEB_LAYOUT_UNDEFINED; 860 } 861 862 static int 863 create_pa_stream(cubeb_stream * stm, pa_stream ** pa_stm, 864 cubeb_stream_params * stream_params, char const * stream_name) 865 { 866 assert(stm && stream_params); 867 assert(&stm->input_stream == pa_stm || 868 (&stm->output_stream == pa_stm && 869 (stream_params->layout == CUBEB_LAYOUT_UNDEFINED || 870 (stream_params->layout != CUBEB_LAYOUT_UNDEFINED && 871 cubeb_channel_layout_nb_channels(stream_params->layout) == 872 stream_params->channels)))); 873 if (stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) { 874 return CUBEB_ERROR_NOT_SUPPORTED; 875 } 876 *pa_stm = NULL; 877 pa_sample_spec ss; 878 ss.format = to_pulse_format(stream_params->format); 879 if (ss.format == PA_SAMPLE_INVALID) 880 return CUBEB_ERROR_INVALID_FORMAT; 881 ss.rate = stream_params->rate; 882 if (stream_params->channels > UINT8_MAX) 883 return CUBEB_ERROR_INVALID_FORMAT; 884 ss.channels = (uint8_t)stream_params->channels; 885 886 if (stream_params->layout == CUBEB_LAYOUT_UNDEFINED) { 887 pa_channel_map cm; 888 if (stream_params->channels <= 8 && 889 !WRAP(pa_channel_map_init_auto)(&cm, stream_params->channels, 890 PA_CHANNEL_MAP_DEFAULT)) { 891 LOG("Layout undefined and PulseAudio's default layout has not been " 892 "configured, guess one."); 893 layout_to_channel_map( 894 pulse_default_layout_for_channels(stream_params->channels), &cm); 895 *pa_stm = 896 WRAP(pa_stream_new)(stm->context->context, stream_name, &ss, &cm); 897 } else { 898 LOG("Layout undefined, PulseAudio will use its default."); 899 *pa_stm = 900 WRAP(pa_stream_new)(stm->context->context, stream_name, &ss, NULL); 901 } 902 } else { 903 pa_channel_map cm; 904 layout_to_channel_map(stream_params->layout, &cm); 905 *pa_stm = WRAP(pa_stream_new)(stm->context->context, stream_name, &ss, &cm); 906 } 907 return (*pa_stm == NULL) ? CUBEB_ERROR : CUBEB_OK; 908 } 909 910 static pa_buffer_attr 911 set_buffering_attribute(unsigned int latency_frames, 912 pa_sample_spec * sample_spec) 913 { 914 pa_buffer_attr battr; 915 battr.maxlength = -1; 916 battr.prebuf = -1; 917 battr.tlength = latency_frames * WRAP(pa_frame_size)(sample_spec); 918 battr.minreq = battr.tlength / 4; 919 battr.fragsize = battr.minreq; 920 921 LOG("Requested buffer attributes maxlength %u, tlength %u, prebuf %u, minreq " 922 "%u, fragsize %u", 923 battr.maxlength, battr.tlength, battr.prebuf, battr.minreq, 924 battr.fragsize); 925 926 return battr; 927 } 928 929 static int 930 pulse_stream_init(cubeb * context, cubeb_stream ** stream, 931 char const * stream_name, cubeb_devid input_device, 932 cubeb_stream_params * input_stream_params, 933 cubeb_devid output_device, 934 cubeb_stream_params * output_stream_params, 935 unsigned int latency_frames, 936 cubeb_data_callback data_callback, 937 cubeb_state_callback state_callback, void * user_ptr) 938 { 939 cubeb_stream * stm; 940 pa_buffer_attr battr; 941 int r; 942 943 assert(context); 944 945 // If the connection failed for some reason, try to reconnect 946 if (context->error == 1 && pulse_context_init(context) != 0) { 947 return CUBEB_ERROR; 948 } 949 950 *stream = NULL; 951 952 stm = calloc(1, sizeof(*stm)); 953 assert(stm); 954 955 stm->context = context; 956 stm->data_callback = data_callback; 957 stm->state_callback = state_callback; 958 stm->user_ptr = user_ptr; 959 stm->volume = PULSE_NO_GAIN; 960 stm->state = -1; 961 assert(stm->shutdown == 0); 962 963 WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop); 964 if (output_stream_params) { 965 r = create_pa_stream(stm, &stm->output_stream, output_stream_params, 966 stream_name); 967 if (r != CUBEB_OK) { 968 WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop); 969 pulse_stream_destroy(stm); 970 return r; 971 } 972 973 stm->output_sample_spec = 974 *(WRAP(pa_stream_get_sample_spec)(stm->output_stream)); 975 976 WRAP(pa_stream_set_state_callback) 977 (stm->output_stream, stream_state_callback, stm); 978 WRAP(pa_stream_set_write_callback) 979 (stm->output_stream, stream_write_callback, stm); 980 981 battr = set_buffering_attribute(latency_frames, &stm->output_sample_spec); 982 WRAP(pa_stream_connect_playback) 983 (stm->output_stream, (char const *)output_device, &battr, 984 PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING | 985 PA_STREAM_START_CORKED | PA_STREAM_ADJUST_LATENCY, 986 NULL, NULL); 987 } 988 989 // Set up input stream 990 if (input_stream_params) { 991 r = create_pa_stream(stm, &stm->input_stream, input_stream_params, 992 stream_name); 993 if (r != CUBEB_OK) { 994 WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop); 995 pulse_stream_destroy(stm); 996 return r; 997 } 998 999 stm->input_sample_spec = 1000 *(WRAP(pa_stream_get_sample_spec)(stm->input_stream)); 1001 1002 WRAP(pa_stream_set_state_callback) 1003 (stm->input_stream, stream_state_callback, stm); 1004 WRAP(pa_stream_set_read_callback) 1005 (stm->input_stream, stream_read_callback, stm); 1006 1007 battr = set_buffering_attribute(latency_frames, &stm->input_sample_spec); 1008 WRAP(pa_stream_connect_record) 1009 (stm->input_stream, (char const *)input_device, &battr, 1010 PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING | 1011 PA_STREAM_START_CORKED | PA_STREAM_ADJUST_LATENCY); 1012 } 1013 1014 r = wait_until_stream_ready(stm); 1015 if (r == 0) { 1016 /* force a timing update now, otherwise timing info does not become valid 1017 until some point after initialization has completed. */ 1018 r = stream_update_timing_info(stm); 1019 } 1020 1021 WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop); 1022 1023 if (r != 0) { 1024 pulse_stream_destroy(stm); 1025 return CUBEB_ERROR; 1026 } 1027 1028 if (cubeb_log_get_level()) { 1029 if (output_stream_params) { 1030 const pa_buffer_attr * output_att; 1031 output_att = WRAP(pa_stream_get_buffer_attr)(stm->output_stream); 1032 LOG("Output buffer attributes maxlength %u, tlength %u, prebuf %u, " 1033 "minreq %u, fragsize %u", 1034 output_att->maxlength, output_att->tlength, output_att->prebuf, 1035 output_att->minreq, output_att->fragsize); 1036 } 1037 1038 if (input_stream_params) { 1039 const pa_buffer_attr * input_att; 1040 input_att = WRAP(pa_stream_get_buffer_attr)(stm->input_stream); 1041 LOG("Input buffer attributes maxlength %u, tlength %u, prebuf %u, minreq " 1042 "%u, fragsize %u", 1043 input_att->maxlength, input_att->tlength, input_att->prebuf, 1044 input_att->minreq, input_att->fragsize); 1045 } 1046 } 1047 1048 *stream = stm; 1049 LOG("Cubeb stream (%p) init successful.", *stream); 1050 1051 return CUBEB_OK; 1052 } 1053 1054 static void 1055 pulse_stream_destroy(cubeb_stream * stm) 1056 { 1057 stream_cork(stm, CORK); 1058 1059 WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop); 1060 if (stm->output_stream) { 1061 1062 if (stm->drain_timer) { 1063 /* there's no pa_rttime_free, so use this instead. */ 1064 WRAP(pa_threaded_mainloop_get_api) 1065 (stm->context->mainloop)->time_free(stm->drain_timer); 1066 } 1067 1068 WRAP(pa_stream_set_state_callback)(stm->output_stream, NULL, NULL); 1069 WRAP(pa_stream_set_write_callback)(stm->output_stream, NULL, NULL); 1070 WRAP(pa_stream_disconnect)(stm->output_stream); 1071 WRAP(pa_stream_unref)(stm->output_stream); 1072 } 1073 1074 if (stm->input_stream) { 1075 WRAP(pa_stream_set_state_callback)(stm->input_stream, NULL, NULL); 1076 WRAP(pa_stream_set_read_callback)(stm->input_stream, NULL, NULL); 1077 WRAP(pa_stream_disconnect)(stm->input_stream); 1078 WRAP(pa_stream_unref)(stm->input_stream); 1079 } 1080 WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop); 1081 1082 LOG("Cubeb stream (%p) destroyed successfully.", stm); 1083 free(stm); 1084 } 1085 1086 static void 1087 pulse_defer_event_cb(pa_mainloop_api * a, void * userdata) 1088 { 1089 (void)a; 1090 cubeb_stream * stm = userdata; 1091 if (stm->shutdown) { 1092 return; 1093 } 1094 size_t writable_size = WRAP(pa_stream_writable_size)(stm->output_stream); 1095 trigger_user_callback(stm->output_stream, NULL, writable_size, stm); 1096 } 1097 1098 static int 1099 pulse_stream_start(cubeb_stream * stm) 1100 { 1101 stm->shutdown = 0; 1102 stream_cork(stm, UNCORK | NOTIFY); 1103 1104 if (stm->output_stream && !stm->input_stream) { 1105 /* On output only case need to manually call user cb once in order to make 1106 * things roll. This is done via a defer event in order to execute it 1107 * from PA server thread. */ 1108 WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop); 1109 WRAP(pa_mainloop_api_once) 1110 (WRAP(pa_threaded_mainloop_get_api)(stm->context->mainloop), 1111 pulse_defer_event_cb, stm); 1112 WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop); 1113 } 1114 1115 LOG("Cubeb stream (%p) started successfully.", stm); 1116 return CUBEB_OK; 1117 } 1118 1119 static int 1120 pulse_stream_stop(cubeb_stream * stm) 1121 { 1122 WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop); 1123 stm->shutdown = 1; 1124 // If draining is taking place wait to finish 1125 while (stm->drain_timer) { 1126 WRAP(pa_threaded_mainloop_wait)(stm->context->mainloop); 1127 } 1128 WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop); 1129 1130 stream_cork(stm, CORK | NOTIFY); 1131 LOG("Cubeb stream (%p) stopped successfully.", stm); 1132 return CUBEB_OK; 1133 } 1134 1135 static int 1136 pulse_stream_get_position(cubeb_stream * stm, uint64_t * position) 1137 { 1138 int r, in_thread; 1139 pa_usec_t r_usec; 1140 uint64_t bytes; 1141 1142 if (!stm || !stm->output_stream) { 1143 return CUBEB_ERROR; 1144 } 1145 1146 in_thread = WRAP(pa_threaded_mainloop_in_thread)(stm->context->mainloop); 1147 1148 if (!in_thread) { 1149 WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop); 1150 } 1151 r = WRAP(pa_stream_get_time)(stm->output_stream, &r_usec); 1152 if (!in_thread) { 1153 WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop); 1154 } 1155 1156 if (r != 0) { 1157 return CUBEB_ERROR; 1158 } 1159 1160 bytes = WRAP(pa_usec_to_bytes)(r_usec, &stm->output_sample_spec); 1161 *position = bytes / WRAP(pa_frame_size)(&stm->output_sample_spec); 1162 1163 return CUBEB_OK; 1164 } 1165 1166 static int 1167 pulse_stream_get_latency(cubeb_stream * stm, uint32_t * latency) 1168 { 1169 pa_usec_t r_usec; 1170 int negative, r; 1171 1172 if (!stm || !stm->output_stream) { 1173 return CUBEB_ERROR; 1174 } 1175 1176 r = WRAP(pa_stream_get_latency)(stm->output_stream, &r_usec, &negative); 1177 assert(!negative); 1178 if (r) { 1179 return CUBEB_ERROR; 1180 } 1181 1182 *latency = r_usec * stm->output_sample_spec.rate / PA_USEC_PER_SEC; 1183 return CUBEB_OK; 1184 } 1185 1186 static void 1187 volume_success(pa_context * c, int success, void * userdata) 1188 { 1189 (void)success; 1190 (void)c; 1191 cubeb_stream * stream = userdata; 1192 assert(success); 1193 WRAP(pa_threaded_mainloop_signal)(stream->context->mainloop, 0); 1194 } 1195 1196 static void 1197 rename_success(pa_stream * s, int success, void * userdata) 1198 { 1199 cubeb_stream * stream = userdata; 1200 assert(success); 1201 WRAP(pa_threaded_mainloop_signal)(stream->context->mainloop, 0); 1202 } 1203 1204 static int 1205 pulse_stream_set_volume(cubeb_stream * stm, float volume) 1206 { 1207 uint32_t index; 1208 pa_operation * op; 1209 pa_volume_t vol; 1210 pa_cvolume cvol; 1211 const pa_sample_spec * ss; 1212 cubeb * ctx; 1213 1214 if (!stm->output_stream) { 1215 return CUBEB_ERROR; 1216 } 1217 1218 WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop); 1219 1220 /* if the pulse daemon is configured to use flat volumes, 1221 * apply our own gain instead of changing the input volume on the sink. */ 1222 ctx = stm->context; 1223 if (ctx->default_sink_info && 1224 (ctx->default_sink_info->flags & PA_SINK_FLAT_VOLUME)) { 1225 stm->volume = volume; 1226 } else { 1227 ss = WRAP(pa_stream_get_sample_spec)(stm->output_stream); 1228 1229 vol = WRAP(pa_sw_volume_from_linear)(volume); 1230 WRAP(pa_cvolume_set)(&cvol, ss->channels, vol); 1231 1232 index = WRAP(pa_stream_get_index)(stm->output_stream); 1233 1234 op = WRAP(pa_context_set_sink_input_volume)(ctx->context, index, &cvol, 1235 volume_success, stm); 1236 if (op) { 1237 operation_wait(ctx, stm->output_stream, op); 1238 WRAP(pa_operation_unref)(op); 1239 } 1240 } 1241 1242 WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop); 1243 1244 return CUBEB_OK; 1245 } 1246 1247 static int 1248 pulse_stream_set_name(cubeb_stream * stm, char const * stream_name) 1249 { 1250 if (!stm || !stm->output_stream) { 1251 return CUBEB_ERROR; 1252 } 1253 1254 WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop); 1255 1256 pa_operation * op = WRAP(pa_stream_set_name)(stm->output_stream, stream_name, 1257 rename_success, stm); 1258 1259 if (op) { 1260 operation_wait(stm->context, stm->output_stream, op); 1261 WRAP(pa_operation_unref)(op); 1262 } 1263 1264 WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop); 1265 1266 return CUBEB_OK; 1267 } 1268 1269 typedef struct { 1270 char * default_sink_name; 1271 char * default_source_name; 1272 1273 cubeb_device_info * devinfo; 1274 uint32_t max; 1275 uint32_t count; 1276 cubeb * context; 1277 } pulse_dev_list_data; 1278 1279 static cubeb_device_fmt 1280 pulse_format_to_cubeb_format(pa_sample_format_t format) 1281 { 1282 switch (format) { 1283 case PA_SAMPLE_S16LE: 1284 return CUBEB_DEVICE_FMT_S16LE; 1285 case PA_SAMPLE_S16BE: 1286 return CUBEB_DEVICE_FMT_S16BE; 1287 case PA_SAMPLE_FLOAT32LE: 1288 return CUBEB_DEVICE_FMT_F32LE; 1289 case PA_SAMPLE_FLOAT32BE: 1290 return CUBEB_DEVICE_FMT_F32BE; 1291 default: 1292 return CUBEB_DEVICE_FMT_F32NE; 1293 } 1294 } 1295 1296 static void 1297 pulse_ensure_dev_list_data_list_size(pulse_dev_list_data * list_data) 1298 { 1299 if (list_data->count == list_data->max) { 1300 list_data->max += 8; 1301 list_data->devinfo = 1302 realloc(list_data->devinfo, sizeof(cubeb_device_info) * list_data->max); 1303 } 1304 } 1305 1306 static cubeb_device_state 1307 pulse_get_state_from_sink_port(pa_sink_port_info * info) 1308 { 1309 if (info != NULL) { 1310 #if PA_CHECK_VERSION(2, 0, 0) 1311 if (has_pulse_v2 && info->available == PA_PORT_AVAILABLE_NO) 1312 return CUBEB_DEVICE_STATE_UNPLUGGED; 1313 else /*if (info->available == PA_PORT_AVAILABLE_YES) + UNKNOWN */ 1314 #endif 1315 return CUBEB_DEVICE_STATE_ENABLED; 1316 } 1317 1318 return CUBEB_DEVICE_STATE_ENABLED; 1319 } 1320 1321 static void 1322 pulse_sink_info_cb(pa_context * context, const pa_sink_info * info, int eol, 1323 void * user_data) 1324 { 1325 pulse_dev_list_data * list_data = user_data; 1326 cubeb_device_info * devinfo; 1327 char const * prop = NULL; 1328 char const * device_id = NULL; 1329 1330 (void)context; 1331 1332 if (eol) { 1333 WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0); 1334 return; 1335 } 1336 1337 if (info == NULL) 1338 return; 1339 1340 device_id = info->name; 1341 if (intern_device_id(list_data->context, &device_id) != CUBEB_OK) { 1342 assert(NULL); 1343 return; 1344 } 1345 1346 pulse_ensure_dev_list_data_list_size(list_data); 1347 devinfo = &list_data->devinfo[list_data->count]; 1348 memset(devinfo, 0, sizeof(cubeb_device_info)); 1349 1350 devinfo->device_id = device_id; 1351 devinfo->devid = (cubeb_devid)devinfo->device_id; 1352 devinfo->friendly_name = strdup(info->description); 1353 prop = WRAP(pa_proplist_gets)(info->proplist, "sysfs.path"); 1354 if (prop) 1355 devinfo->group_id = strdup(prop); 1356 prop = WRAP(pa_proplist_gets)(info->proplist, "device.vendor.name"); 1357 if (prop) 1358 devinfo->vendor_name = strdup(prop); 1359 1360 devinfo->type = CUBEB_DEVICE_TYPE_OUTPUT; 1361 devinfo->state = pulse_get_state_from_sink_port(info->active_port); 1362 devinfo->preferred = (strcmp(info->name, list_data->default_sink_name) == 0) 1363 ? CUBEB_DEVICE_PREF_ALL 1364 : CUBEB_DEVICE_PREF_NONE; 1365 1366 devinfo->format = CUBEB_DEVICE_FMT_ALL; 1367 devinfo->default_format = 1368 pulse_format_to_cubeb_format(info->sample_spec.format); 1369 devinfo->max_channels = info->channel_map.channels; 1370 devinfo->min_rate = 1; 1371 devinfo->max_rate = PA_RATE_MAX; 1372 devinfo->default_rate = info->sample_spec.rate; 1373 1374 devinfo->latency_lo = 0; 1375 devinfo->latency_hi = 0; 1376 1377 list_data->count += 1; 1378 } 1379 1380 static cubeb_device_state 1381 pulse_get_state_from_source_port(pa_source_port_info * info) 1382 { 1383 if (info != NULL) { 1384 #if PA_CHECK_VERSION(2, 0, 0) 1385 if (has_pulse_v2 && info->available == PA_PORT_AVAILABLE_NO) 1386 return CUBEB_DEVICE_STATE_UNPLUGGED; 1387 else /*if (info->available == PA_PORT_AVAILABLE_YES) + UNKNOWN */ 1388 #endif 1389 return CUBEB_DEVICE_STATE_ENABLED; 1390 } 1391 1392 return CUBEB_DEVICE_STATE_ENABLED; 1393 } 1394 1395 static void 1396 pulse_source_info_cb(pa_context * context, const pa_source_info * info, int eol, 1397 void * user_data) 1398 { 1399 pulse_dev_list_data * list_data = user_data; 1400 cubeb_device_info * devinfo; 1401 char const * prop = NULL; 1402 char const * device_id = NULL; 1403 1404 (void)context; 1405 1406 if (eol) { 1407 WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0); 1408 return; 1409 } 1410 1411 device_id = info->name; 1412 if (intern_device_id(list_data->context, &device_id) != CUBEB_OK) { 1413 assert(NULL); 1414 return; 1415 } 1416 1417 pulse_ensure_dev_list_data_list_size(list_data); 1418 devinfo = &list_data->devinfo[list_data->count]; 1419 memset(devinfo, 0, sizeof(cubeb_device_info)); 1420 1421 devinfo->device_id = device_id; 1422 devinfo->devid = (cubeb_devid)devinfo->device_id; 1423 devinfo->friendly_name = strdup(info->description); 1424 prop = WRAP(pa_proplist_gets)(info->proplist, "sysfs.path"); 1425 if (prop) 1426 devinfo->group_id = strdup(prop); 1427 prop = WRAP(pa_proplist_gets)(info->proplist, "device.vendor.name"); 1428 if (prop) 1429 devinfo->vendor_name = strdup(prop); 1430 1431 devinfo->type = CUBEB_DEVICE_TYPE_INPUT; 1432 devinfo->state = pulse_get_state_from_source_port(info->active_port); 1433 devinfo->preferred = (strcmp(info->name, list_data->default_source_name) == 0) 1434 ? CUBEB_DEVICE_PREF_ALL 1435 : CUBEB_DEVICE_PREF_NONE; 1436 1437 devinfo->format = CUBEB_DEVICE_FMT_ALL; 1438 devinfo->default_format = 1439 pulse_format_to_cubeb_format(info->sample_spec.format); 1440 devinfo->max_channels = info->channel_map.channels; 1441 devinfo->min_rate = 1; 1442 devinfo->max_rate = PA_RATE_MAX; 1443 devinfo->default_rate = info->sample_spec.rate; 1444 1445 devinfo->latency_lo = 0; 1446 devinfo->latency_hi = 0; 1447 1448 list_data->count += 1; 1449 } 1450 1451 static void 1452 pulse_server_info_cb(pa_context * c, const pa_server_info * i, void * userdata) 1453 { 1454 pulse_dev_list_data * list_data = userdata; 1455 1456 (void)c; 1457 1458 free(list_data->default_sink_name); 1459 free(list_data->default_source_name); 1460 list_data->default_sink_name = 1461 i->default_sink_name ? strdup(i->default_sink_name) : NULL; 1462 list_data->default_source_name = 1463 i->default_source_name ? strdup(i->default_source_name) : NULL; 1464 1465 WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0); 1466 } 1467 1468 static int 1469 pulse_enumerate_devices(cubeb * context, cubeb_device_type type, 1470 cubeb_device_collection * collection) 1471 { 1472 pulse_dev_list_data user_data = {NULL, NULL, NULL, 0, 0, context}; 1473 pa_operation * o; 1474 1475 WRAP(pa_threaded_mainloop_lock)(context->mainloop); 1476 1477 o = WRAP(pa_context_get_server_info)(context->context, pulse_server_info_cb, 1478 &user_data); 1479 if (o) { 1480 operation_wait(context, NULL, o); 1481 WRAP(pa_operation_unref)(o); 1482 } 1483 1484 if (type & CUBEB_DEVICE_TYPE_OUTPUT) { 1485 o = WRAP(pa_context_get_sink_info_list)(context->context, 1486 pulse_sink_info_cb, &user_data); 1487 if (o) { 1488 operation_wait(context, NULL, o); 1489 WRAP(pa_operation_unref)(o); 1490 } 1491 } 1492 1493 if (type & CUBEB_DEVICE_TYPE_INPUT) { 1494 o = WRAP(pa_context_get_source_info_list)(context->context, 1495 pulse_source_info_cb, &user_data); 1496 if (o) { 1497 operation_wait(context, NULL, o); 1498 WRAP(pa_operation_unref)(o); 1499 } 1500 } 1501 1502 WRAP(pa_threaded_mainloop_unlock)(context->mainloop); 1503 1504 collection->device = user_data.devinfo; 1505 collection->count = user_data.count; 1506 1507 free(user_data.default_sink_name); 1508 free(user_data.default_source_name); 1509 return CUBEB_OK; 1510 } 1511 1512 static int 1513 pulse_device_collection_destroy(cubeb * ctx, 1514 cubeb_device_collection * collection) 1515 { 1516 size_t n; 1517 1518 for (n = 0; n < collection->count; n++) { 1519 free((void *)collection->device[n].friendly_name); 1520 free((void *)collection->device[n].vendor_name); 1521 free((void *)collection->device[n].group_id); 1522 } 1523 1524 free(collection->device); 1525 return CUBEB_OK; 1526 } 1527 1528 static int 1529 pulse_stream_get_current_device(cubeb_stream * stm, 1530 cubeb_device ** const device) 1531 { 1532 #if PA_CHECK_VERSION(0, 9, 8) 1533 *device = calloc(1, sizeof(cubeb_device)); 1534 if (*device == NULL) 1535 return CUBEB_ERROR; 1536 1537 if (stm->input_stream) { 1538 const char * name = WRAP(pa_stream_get_device_name)(stm->input_stream); 1539 (*device)->input_name = (name == NULL) ? NULL : strdup(name); 1540 } 1541 1542 if (stm->output_stream) { 1543 const char * name = WRAP(pa_stream_get_device_name)(stm->output_stream); 1544 (*device)->output_name = (name == NULL) ? NULL : strdup(name); 1545 } 1546 1547 return CUBEB_OK; 1548 #else 1549 return CUBEB_ERROR_NOT_SUPPORTED; 1550 #endif 1551 } 1552 1553 static int 1554 pulse_stream_device_destroy(cubeb_stream * stream, cubeb_device * device) 1555 { 1556 (void)stream; 1557 free(device->input_name); 1558 free(device->output_name); 1559 free(device); 1560 return CUBEB_OK; 1561 } 1562 1563 static void 1564 pulse_subscribe_callback(pa_context * ctx, pa_subscription_event_type_t t, 1565 uint32_t index, void * userdata) 1566 { 1567 (void)ctx; 1568 cubeb * context = userdata; 1569 1570 switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) { 1571 case PA_SUBSCRIPTION_EVENT_SERVER: 1572 if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) { 1573 LOG("Server changed %d", index); 1574 WRAP(pa_context_get_server_info) 1575 (context->context, server_info_callback, context); 1576 } 1577 break; 1578 case PA_SUBSCRIPTION_EVENT_SOURCE: 1579 case PA_SUBSCRIPTION_EVENT_SINK: 1580 1581 if (cubeb_log_get_level()) { 1582 if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == 1583 PA_SUBSCRIPTION_EVENT_SOURCE && 1584 (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == 1585 PA_SUBSCRIPTION_EVENT_REMOVE) { 1586 LOG("Removing source index %d", index); 1587 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == 1588 PA_SUBSCRIPTION_EVENT_SOURCE && 1589 (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == 1590 PA_SUBSCRIPTION_EVENT_NEW) { 1591 LOG("Adding source index %d", index); 1592 } 1593 if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == 1594 PA_SUBSCRIPTION_EVENT_SINK && 1595 (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == 1596 PA_SUBSCRIPTION_EVENT_REMOVE) { 1597 LOG("Removing sink index %d", index); 1598 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == 1599 PA_SUBSCRIPTION_EVENT_SINK && 1600 (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == 1601 PA_SUBSCRIPTION_EVENT_NEW) { 1602 LOG("Adding sink index %d", index); 1603 } 1604 } 1605 1606 if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE || 1607 (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { 1608 if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == 1609 PA_SUBSCRIPTION_EVENT_SOURCE) { 1610 context->input_collection_changed_callback( 1611 context, context->input_collection_changed_user_ptr); 1612 } 1613 if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == 1614 PA_SUBSCRIPTION_EVENT_SINK) { 1615 context->output_collection_changed_callback( 1616 context, context->output_collection_changed_user_ptr); 1617 } 1618 } 1619 break; 1620 } 1621 } 1622 1623 static void 1624 subscribe_success(pa_context * c, int success, void * userdata) 1625 { 1626 (void)c; 1627 cubeb * context = userdata; 1628 assert(success); 1629 WRAP(pa_threaded_mainloop_signal)(context->mainloop, 0); 1630 } 1631 1632 static int 1633 pulse_subscribe_notifications(cubeb * context, pa_subscription_mask_t mask) 1634 { 1635 WRAP(pa_threaded_mainloop_lock)(context->mainloop); 1636 1637 WRAP(pa_context_set_subscribe_callback) 1638 (context->context, pulse_subscribe_callback, context); 1639 1640 pa_operation * o; 1641 o = WRAP(pa_context_subscribe)(context->context, mask, subscribe_success, 1642 context); 1643 if (o == NULL) { 1644 WRAP(pa_threaded_mainloop_unlock)(context->mainloop); 1645 LOG("Context subscribe failed"); 1646 return CUBEB_ERROR; 1647 } 1648 operation_wait(context, NULL, o); 1649 WRAP(pa_operation_unref)(o); 1650 1651 WRAP(pa_threaded_mainloop_unlock)(context->mainloop); 1652 1653 return CUBEB_OK; 1654 } 1655 1656 static int 1657 pulse_register_device_collection_changed( 1658 cubeb * context, cubeb_device_type devtype, 1659 cubeb_device_collection_changed_callback collection_changed_callback, 1660 void * user_ptr) 1661 { 1662 if (devtype & CUBEB_DEVICE_TYPE_INPUT) { 1663 context->input_collection_changed_callback = collection_changed_callback; 1664 context->input_collection_changed_user_ptr = user_ptr; 1665 } 1666 if (devtype & CUBEB_DEVICE_TYPE_OUTPUT) { 1667 context->output_collection_changed_callback = collection_changed_callback; 1668 context->output_collection_changed_user_ptr = user_ptr; 1669 } 1670 1671 pa_subscription_mask_t mask = PA_SUBSCRIPTION_MASK_NULL; 1672 if (context->input_collection_changed_callback) { 1673 /* Input added or removed */ 1674 mask |= PA_SUBSCRIPTION_MASK_SOURCE; 1675 } 1676 if (context->output_collection_changed_callback) { 1677 /* Output added or removed */ 1678 mask |= PA_SUBSCRIPTION_MASK_SINK; 1679 } 1680 /* Default device changed, this is always registered in order to update the 1681 * `default_sink_info` when the default device changes. */ 1682 mask |= PA_SUBSCRIPTION_MASK_SERVER; 1683 1684 return pulse_subscribe_notifications(context, mask); 1685 } 1686 1687 static struct cubeb_ops const pulse_ops = { 1688 .init = pulse_init, 1689 .get_backend_id = pulse_get_backend_id, 1690 .get_max_channel_count = pulse_get_max_channel_count, 1691 .get_min_latency = pulse_get_min_latency, 1692 .get_preferred_sample_rate = pulse_get_preferred_sample_rate, 1693 .get_supported_input_processing_params = NULL, 1694 .enumerate_devices = pulse_enumerate_devices, 1695 .device_collection_destroy = pulse_device_collection_destroy, 1696 .destroy = pulse_destroy, 1697 .stream_init = pulse_stream_init, 1698 .stream_destroy = pulse_stream_destroy, 1699 .stream_start = pulse_stream_start, 1700 .stream_stop = pulse_stream_stop, 1701 .stream_get_position = pulse_stream_get_position, 1702 .stream_get_latency = pulse_stream_get_latency, 1703 .stream_get_input_latency = NULL, 1704 .stream_set_volume = pulse_stream_set_volume, 1705 .stream_set_name = pulse_stream_set_name, 1706 .stream_get_current_device = pulse_stream_get_current_device, 1707 .stream_set_input_mute = NULL, 1708 .stream_set_input_processing_params = NULL, 1709 .stream_device_destroy = pulse_stream_device_destroy, 1710 .stream_register_device_changed_callback = NULL, 1711 .register_device_collection_changed = 1712 pulse_register_device_collection_changed};