Changeset 88466 in vbox for trunk/src/VBox
- Timestamp:
- Apr 12, 2021 11:54:26 AM (4 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Devices/Audio/DrvHostAudioPulseAudio.cpp
r88390 r88466 167 167 168 168 169 169 170 /* 170 * To allow running onsystems with PulseAudio < 0.9.11.171 * Glue to make the code work systems with PulseAudio < 0.9.11. 171 172 */ 172 173 #if !defined(PA_CONTEXT_IS_GOOD) && PA_API_VERSION < 12 /* 12 = 0.9.11 where PA_STREAM_IS_GOOD was added */ … … 189 190 190 191 191 /********************************************************************************************************************************* 192 * Prototypes * 193 *********************************************************************************************************************************/ 194 195 static int paEnumerate(PDRVHOSTPULSEAUDIO pThis, PPDMAUDIOBACKENDCFG pCfg, uint32_t fEnum); 196 static int paError(PDRVHOSTPULSEAUDIO pThis, const char *szMsg); 197 #ifdef DEBUG 198 static void paStreamCbUnderflow(pa_stream *pStream, void *pvContext); 199 static void paStreamCbReqWrite(pa_stream *pStream, size_t cbLen, void *pvContext); 200 #endif 201 static void paStreamCbSuccess(pa_stream *pStream, int fSuccess, void *pvContext); 192 193 /** @todo Implement va handling. */ 194 static int drvHostAudioPaError(PDRVHOSTPULSEAUDIO pThis, const char *szMsg) 195 { 196 AssertPtrReturn(pThis, VERR_INVALID_POINTER); 197 AssertPtrReturn(szMsg, VERR_INVALID_POINTER); 198 199 if (pThis->cLogErrors++ < VBOX_PULSEAUDIO_MAX_LOG_REL_ERRORS) 200 { 201 int rc2 = pa_context_errno(pThis->pContext); 202 LogRel2(("PulseAudio: %s: %s\n", szMsg, pa_strerror(rc2))); 203 } 204 205 /** @todo Implement some PulseAudio -> IPRT mapping here. */ 206 return VERR_GENERAL_FAILURE; 207 } 202 208 203 209 … … 206 212 * mainloop might not have been entered yet. 207 213 */ 208 static void paSignalWaiter(PDRVHOSTPULSEAUDIO pThis)214 static void drvHostAudioPaSignalWaiter(PDRVHOSTPULSEAUDIO pThis) 209 215 { 210 216 if (!pThis) … … 216 222 217 223 218 static pa_sample_format_t paAudioPropsToPulse(PPDMAUDIOPCMPROPS pProps) 219 { 220 switch (PDMAudioPropsSampleSize(pProps)) 221 { 222 case 1: 223 if (!pProps->fSigned) 224 return PA_SAMPLE_U8; 225 break; 226 227 case 2: 228 if (pProps->fSigned) 229 return PDMAudioPropsIsLittleEndian(pProps) ? PA_SAMPLE_S16LE : PA_SAMPLE_S16BE; 230 break; 231 232 #ifdef PA_SAMPLE_S32LE 233 case 4: 234 if (pProps->fSigned) 235 return PDMAudioPropsIsLittleEndian(pProps) ? PA_SAMPLE_S32LE : PA_SAMPLE_S32BE; 236 break; 237 #endif 238 } 239 240 AssertMsgFailed(("%RU8%s not supported\n", PDMAudioPropsSampleSize(pProps), pProps->fSigned ? "S" : "U")); 241 return PA_SAMPLE_INVALID; 242 } 243 244 245 static int paPulseToAudioProps(PPDMAUDIOPCMPROPS pProps, pa_sample_format_t pulsefmt, uint8_t cChannels, uint32_t uHz) 246 { 247 AssertReturn(cChannels > 0, VERR_INVALID_PARAMETER); 248 AssertReturn(cChannels < 16, VERR_INVALID_PARAMETER); 249 250 switch (pulsefmt) 251 { 252 case PA_SAMPLE_U8: 253 PDMAudioPropsInit(pProps, 1 /*8-bit*/, false /*signed*/, cChannels, uHz); 254 break; 255 256 case PA_SAMPLE_S16LE: 257 PDMAudioPropsInitEx(pProps, 2 /*16-bit*/, true /*signed*/, cChannels, uHz, true /*fLittleEndian*/, false /*fRaw*/); 258 break; 259 260 case PA_SAMPLE_S16BE: 261 PDMAudioPropsInitEx(pProps, 2 /*16-bit*/, true /*signed*/, cChannels, uHz, false /*fLittleEndian*/, false /*fRaw*/); 262 break; 263 264 #ifdef PA_SAMPLE_S32LE 265 case PA_SAMPLE_S32LE: 266 PDMAudioPropsInitEx(pProps, 4 /*32-bit*/, true /*signed*/, cChannels, uHz, true /*fLittleEndian*/, false /*fRaw*/); 267 break; 268 #endif 269 270 #ifdef PA_SAMPLE_S32BE 271 case PA_SAMPLE_S32BE: 272 PDMAudioPropsInitEx(pProps, 4 /*32-bit*/, true /*signed*/, cChannels, uHz, false /*fLittleEndian*/, false /*fRaw*/); 273 break; 274 #endif 224 225 /** 226 * Pulse audio callback for context status changes, init variant. 227 */ 228 static void drvHostAudioPaCtxCallbackStateChanged(pa_context *pCtx, void *pvUser) 229 { 230 AssertPtrReturnVoid(pCtx); 231 232 PDRVHOSTPULSEAUDIO pThis = (PDRVHOSTPULSEAUDIO)pvUser; 233 AssertPtrReturnVoid(pThis); 234 235 switch (pa_context_get_state(pCtx)) 236 { 237 case PA_CONTEXT_READY: 238 case PA_CONTEXT_TERMINATED: 239 case PA_CONTEXT_FAILED: 240 drvHostAudioPaSignalWaiter(pThis); 241 break; 275 242 276 243 default: 277 AssertLogRelMsgFailed(("PulseAudio: Format (%d) not supported\n", pulsefmt)); 278 return VERR_NOT_SUPPORTED; 279 } 280 281 return VINF_SUCCESS; 244 break; 245 } 246 } 247 248 249 /** 250 * Callback used with pa_stream_cork() in a number of places. 251 */ 252 static void drvHostAudioPaStreamSuccessCallback(pa_stream *pStream, int fSuccess, void *pvUser) 253 { 254 AssertPtrReturnVoid(pStream); 255 PPULSEAUDIOSTREAM pStrm = (PPULSEAUDIOSTREAM)pvUser; 256 AssertPtrReturnVoid(pStrm); 257 258 pStrm->fOpSuccess = fSuccess; 259 260 if (fSuccess) 261 drvHostAudioPaSignalWaiter(pStrm->pDrv); 262 else 263 drvHostAudioPaError(pStrm->pDrv, "Failed to finish stream operation"); 282 264 } 283 265 … … 286 268 * Synchronously wait until an operation completed. 287 269 */ 288 static int paWaitForEx(PDRVHOSTPULSEAUDIO pThis, pa_operation *pOP, RTMSINTERVAL cMsTimeout)270 static int drvHostAudioPaWaitForEx(PDRVHOSTPULSEAUDIO pThis, pa_operation *pOP, RTMSINTERVAL cMsTimeout) 289 271 { 290 272 AssertPtrReturn(pThis, VERR_INVALID_POINTER); … … 323 305 324 306 325 static int paWaitFor(PDRVHOSTPULSEAUDIO pThis, pa_operation *pOP) 326 { 327 return paWaitForEx(pThis, pOP, 10 * 1000 /* 10s timeout */); 328 } 329 330 331 /** 332 * Context status changed, init variant signalling our own event semaphore 333 * so we can do a timed wait. 334 */ 335 static void paContextCbStateChangedInit(pa_context *pCtx, void *pvUser) 336 { 307 static int drvHostAudioPaWaitFor(PDRVHOSTPULSEAUDIO pThis, pa_operation *pOP) 308 { 309 return drvHostAudioPaWaitForEx(pThis, pOP, 10 * RT_MS_1SEC); 310 } 311 312 313 static void drvHostAudioPaEnumSinkCallback(pa_context *pCtx, const pa_sink_info *pInfo, int eol, void *pvUserData) 314 { 315 if (eol > 0) 316 return; 317 318 PPULSEAUDIOENUMCBCTX pCbCtx = (PPULSEAUDIOENUMCBCTX)pvUserData; 319 AssertPtrReturnVoid(pCbCtx); 320 PDRVHOSTPULSEAUDIO pThis = pCbCtx->pDrv; 321 AssertPtrReturnVoid(pThis); 322 if (eol < 0) 323 { 324 pThis->fEnumOpSuccess = false; 325 pa_threaded_mainloop_signal(pCbCtx->pDrv->pMainLoop, 0); 326 return; 327 } 328 337 329 AssertPtrReturnVoid(pCtx); 338 339 PPULSEAUDIOSTATECHGCTX pStateChgCtx = (PPULSEAUDIOSTATECHGCTX)pvUser; 340 pa_context_state_t enmCtxState = pa_context_get_state(pCtx); 341 switch (enmCtxState) 342 { 343 case PA_CONTEXT_READY: 344 case PA_CONTEXT_TERMINATED: 345 case PA_CONTEXT_FAILED: 346 pStateChgCtx->enmCtxState = enmCtxState; 347 RTSemEventSignal(pStateChgCtx->hEvtInit); 348 break; 330 AssertPtrReturnVoid(pInfo); 331 332 LogRel2(("PulseAudio: Using output sink '%s'\n", pInfo->name)); 333 334 /** @todo Store sinks + channel mapping in callback context as soon as we have surround support. */ 335 pCbCtx->cDevOut++; 336 337 pThis->fEnumOpSuccess = true; 338 pa_threaded_mainloop_signal(pCbCtx->pDrv->pMainLoop, 0); 339 } 340 341 342 static void drvHostAudioPaEnumSourceCallback(pa_context *pCtx, const pa_source_info *pInfo, int eol, void *pvUserData) 343 { 344 if (eol > 0) 345 return; 346 347 PPULSEAUDIOENUMCBCTX pCbCtx = (PPULSEAUDIOENUMCBCTX)pvUserData; 348 AssertPtrReturnVoid(pCbCtx); 349 PDRVHOSTPULSEAUDIO pThis = pCbCtx->pDrv; 350 AssertPtrReturnVoid(pThis); 351 if (eol < 0) 352 { 353 pThis->fEnumOpSuccess = false; 354 pa_threaded_mainloop_signal(pCbCtx->pDrv->pMainLoop, 0); 355 return; 356 } 357 358 AssertPtrReturnVoid(pCtx); 359 AssertPtrReturnVoid(pInfo); 360 361 LogRel2(("PulseAudio: Using input source '%s'\n", pInfo->name)); 362 363 /** @todo Store sources + channel mapping in callback context as soon as we have surround support. */ 364 pCbCtx->cDevIn++; 365 366 pThis->fEnumOpSuccess = true; 367 pa_threaded_mainloop_signal(pCbCtx->pDrv->pMainLoop, 0); 368 } 369 370 371 static void drvHostAudioPaEnumServerCallback(pa_context *pCtx, const pa_server_info *pInfo, void *pvUserData) 372 { 373 AssertPtrReturnVoid(pCtx); 374 PPULSEAUDIOENUMCBCTX pCbCtx = (PPULSEAUDIOENUMCBCTX)pvUserData; 375 AssertPtrReturnVoid(pCbCtx); 376 PDRVHOSTPULSEAUDIO pThis = pCbCtx->pDrv; 377 AssertPtrReturnVoid(pThis); 378 379 if (!pInfo) 380 { 381 pThis->fEnumOpSuccess = false; 382 pa_threaded_mainloop_signal(pCbCtx->pDrv->pMainLoop, 0); 383 return; 384 } 385 386 if (pInfo->default_sink_name) 387 { 388 Assert(RTStrIsValidEncoding(pInfo->default_sink_name)); 389 pCbCtx->pszDefaultSink = RTStrDup(pInfo->default_sink_name); 390 } 391 392 if (pInfo->default_sink_name) 393 { 394 Assert(RTStrIsValidEncoding(pInfo->default_source_name)); 395 pCbCtx->pszDefaultSource = RTStrDup(pInfo->default_source_name); 396 } 397 398 pThis->fEnumOpSuccess = true; 399 pa_threaded_mainloop_signal(pThis->pMainLoop, 0); 400 } 401 402 403 static int drvHostAudioPaEnumerate(PDRVHOSTPULSEAUDIO pThis, PPDMAUDIOBACKENDCFG pCfg, uint32_t fEnum) 404 { 405 AssertPtrReturn(pThis, VERR_INVALID_POINTER); 406 AssertPtrReturn(pCfg, VERR_INVALID_POINTER); 407 408 PDMAUDIOBACKENDCFG Cfg; 409 RT_ZERO(Cfg); 410 411 RTStrPrintf2(Cfg.szName, sizeof(Cfg.szName), "PulseAudio"); 412 413 Cfg.cbStreamOut = sizeof(PULSEAUDIOSTREAM); 414 Cfg.cbStreamIn = sizeof(PULSEAUDIOSTREAM); 415 Cfg.cMaxStreamsOut = UINT32_MAX; 416 Cfg.cMaxStreamsIn = UINT32_MAX; 417 418 PULSEAUDIOENUMCBCTX CbCtx; 419 RT_ZERO(CbCtx); 420 421 CbCtx.pDrv = pThis; 422 CbCtx.fFlags = fEnum; 423 424 bool fLog = (fEnum & PULSEAUDIOENUMCBFLAGS_LOG); 425 426 pa_threaded_mainloop_lock(pThis->pMainLoop); 427 428 pThis->fEnumOpSuccess = false; 429 430 LogRel(("PulseAudio: Retrieving server information ...\n")); 431 432 /* Check if server information is available and bail out early if it isn't. */ 433 pa_operation *paOpServerInfo = pa_context_get_server_info(pThis->pContext, drvHostAudioPaEnumServerCallback, &CbCtx); 434 if (!paOpServerInfo) 435 { 436 pa_threaded_mainloop_unlock(pThis->pMainLoop); 437 438 LogRel(("PulseAudio: Server information not available, skipping enumeration\n")); 439 return VINF_SUCCESS; 440 } 441 442 int rc = drvHostAudioPaWaitFor(pThis, paOpServerInfo); 443 if (RT_SUCCESS(rc) && !pThis->fEnumOpSuccess) 444 rc = VERR_AUDIO_BACKEND_INIT_FAILED; /* error code does not matter */ 445 if (RT_SUCCESS(rc)) 446 { 447 if (CbCtx.pszDefaultSink) 448 { 449 if (fLog) 450 LogRel2(("PulseAudio: Default output sink is '%s'\n", CbCtx.pszDefaultSink)); 451 452 pThis->fEnumOpSuccess = false; 453 rc = drvHostAudioPaWaitFor(pThis, pa_context_get_sink_info_by_name(pThis->pContext, CbCtx.pszDefaultSink, 454 drvHostAudioPaEnumSinkCallback, &CbCtx)); 455 if (RT_SUCCESS(rc) && !pThis->fEnumOpSuccess) 456 rc = VERR_AUDIO_BACKEND_INIT_FAILED; /* error code does not matter */ 457 if ( RT_FAILURE(rc) 458 && fLog) 459 { 460 LogRel(("PulseAudio: Error enumerating properties for default output sink '%s'\n", CbCtx.pszDefaultSink)); 461 } 462 } 463 else if (fLog) 464 LogRel2(("PulseAudio: No default output sink found\n")); 465 466 if (RT_SUCCESS(rc)) 467 { 468 if (CbCtx.pszDefaultSource) 469 { 470 if (fLog) 471 LogRel2(("PulseAudio: Default input source is '%s'\n", CbCtx.pszDefaultSource)); 472 473 pThis->fEnumOpSuccess = false; 474 rc = drvHostAudioPaWaitFor(pThis, pa_context_get_source_info_by_name(pThis->pContext, CbCtx.pszDefaultSource, 475 drvHostAudioPaEnumSourceCallback, &CbCtx)); 476 if ( (RT_FAILURE(rc) || !pThis->fEnumOpSuccess) 477 && fLog) 478 { 479 LogRel(("PulseAudio: Error enumerating properties for default input source '%s'\n", CbCtx.pszDefaultSource)); 480 } 481 } 482 else if (fLog) 483 LogRel2(("PulseAudio: No default input source found\n")); 484 } 485 486 if (RT_SUCCESS(rc)) 487 { 488 if (fLog) 489 { 490 LogRel2(("PulseAudio: Found %RU8 host playback device(s)\n", CbCtx.cDevOut)); 491 LogRel2(("PulseAudio: Found %RU8 host capturing device(s)\n", CbCtx.cDevIn)); 492 } 493 494 if (pCfg) 495 memcpy(pCfg, &Cfg, sizeof(PDMAUDIOBACKENDCFG)); 496 } 497 498 if (CbCtx.pszDefaultSink) 499 { 500 RTStrFree(CbCtx.pszDefaultSink); 501 CbCtx.pszDefaultSink = NULL; 502 } 503 504 if (CbCtx.pszDefaultSource) 505 { 506 RTStrFree(CbCtx.pszDefaultSource); 507 CbCtx.pszDefaultSource = NULL; 508 } 509 } 510 else if (fLog) 511 LogRel(("PulseAudio: Error enumerating PulseAudio server properties\n")); 512 513 pa_threaded_mainloop_unlock(pThis->pMainLoop); 514 515 LogFlowFuncLeaveRC(rc); 516 return rc; 517 } 518 519 520 /** 521 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig} 522 */ 523 static DECLCALLBACK(int) drvHostAudioPaHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg) 524 { 525 AssertPtrReturn(pInterface, VERR_INVALID_POINTER); 526 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER); 527 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface); 528 529 return drvHostAudioPaEnumerate(pThis, pBackendCfg, PULSEAUDIOENUMCBFLAGS_LOG /* fEnum */); 530 } 531 532 533 /** 534 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus} 535 */ 536 static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostAudioPaHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir) 537 { 538 RT_NOREF(enmDir); 539 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN); 540 541 return PDMAUDIOBACKENDSTS_RUNNING; 542 } 543 544 545 static pa_sample_format_t drvHostAudioPaPropsToPulse(PPDMAUDIOPCMPROPS pProps) 546 { 547 switch (PDMAudioPropsSampleSize(pProps)) 548 { 549 case 1: 550 if (!pProps->fSigned) 551 return PA_SAMPLE_U8; 552 break; 553 554 case 2: 555 if (pProps->fSigned) 556 return PDMAudioPropsIsLittleEndian(pProps) ? PA_SAMPLE_S16LE : PA_SAMPLE_S16BE; 557 break; 558 559 #ifdef PA_SAMPLE_S32LE 560 case 4: 561 if (pProps->fSigned) 562 return PDMAudioPropsIsLittleEndian(pProps) ? PA_SAMPLE_S32LE : PA_SAMPLE_S32BE; 563 break; 564 #endif 565 } 566 567 AssertMsgFailed(("%RU8%s not supported\n", PDMAudioPropsSampleSize(pProps), pProps->fSigned ? "S" : "U")); 568 return PA_SAMPLE_INVALID; 569 } 570 571 572 static int drvHostAudioPaToAudioProps(PPDMAUDIOPCMPROPS pProps, pa_sample_format_t pulsefmt, uint8_t cChannels, uint32_t uHz) 573 { 574 AssertReturn(cChannels > 0, VERR_INVALID_PARAMETER); 575 AssertReturn(cChannels < 16, VERR_INVALID_PARAMETER); 576 577 switch (pulsefmt) 578 { 579 case PA_SAMPLE_U8: 580 PDMAudioPropsInit(pProps, 1 /*8-bit*/, false /*signed*/, cChannels, uHz); 581 break; 582 583 case PA_SAMPLE_S16LE: 584 PDMAudioPropsInitEx(pProps, 2 /*16-bit*/, true /*signed*/, cChannels, uHz, true /*fLittleEndian*/, false /*fRaw*/); 585 break; 586 587 case PA_SAMPLE_S16BE: 588 PDMAudioPropsInitEx(pProps, 2 /*16-bit*/, true /*signed*/, cChannels, uHz, false /*fLittleEndian*/, false /*fRaw*/); 589 break; 590 591 #ifdef PA_SAMPLE_S32LE 592 case PA_SAMPLE_S32LE: 593 PDMAudioPropsInitEx(pProps, 4 /*32-bit*/, true /*signed*/, cChannels, uHz, true /*fLittleEndian*/, false /*fRaw*/); 594 break; 595 #endif 596 597 #ifdef PA_SAMPLE_S32BE 598 case PA_SAMPLE_S32BE: 599 PDMAudioPropsInitEx(pProps, 4 /*32-bit*/, true /*signed*/, cChannels, uHz, false /*fLittleEndian*/, false /*fRaw*/); 600 break; 601 #endif 349 602 350 603 default: 351 break; 352 } 353 } 354 355 356 /** 357 * Context status changed. 358 */ 359 static void paContextCbStateChanged(pa_context *pCtx, void *pvUser) 360 { 361 AssertPtrReturnVoid(pCtx); 362 363 PDRVHOSTPULSEAUDIO pThis = (PDRVHOSTPULSEAUDIO)pvUser; 364 AssertPtrReturnVoid(pThis); 365 366 switch (pa_context_get_state(pCtx)) 367 { 368 case PA_CONTEXT_READY: 369 case PA_CONTEXT_TERMINATED: 370 case PA_CONTEXT_FAILED: 371 paSignalWaiter(pThis); 372 break; 373 374 default: 375 break; 376 } 377 } 378 379 380 /** 381 * Callback called when our pa_stream_drain operation was completed. 382 */ 383 static void paStreamCbDrain(pa_stream *pStream, int fSuccess, void *pvUser) 384 { 385 AssertPtrReturnVoid(pStream); 386 387 PPULSEAUDIOSTREAM pStreamPA = (PPULSEAUDIOSTREAM)pvUser; 388 AssertPtrReturnVoid(pStreamPA); 389 390 pStreamPA->fOpSuccess = fSuccess; 391 if (fSuccess) 392 { 393 pa_operation_unref(pa_stream_cork(pStream, 1, 394 paStreamCbSuccess, pvUser)); 395 } 396 else 397 paError(pStreamPA->pDrv, "Failed to drain stream"); 398 399 if (pStreamPA->pDrainOp) 400 { 401 pa_operation_unref(pStreamPA->pDrainOp); 402 pStreamPA->pDrainOp = NULL; 403 } 604 AssertLogRelMsgFailed(("PulseAudio: Format (%d) not supported\n", pulsefmt)); 605 return VERR_NOT_SUPPORTED; 606 } 607 608 return VINF_SUCCESS; 404 609 } 405 610 … … 408 613 * Stream status changed. 409 614 */ 410 static void paStreamCbStateChanged(pa_stream *pStream, void *pvUser)615 static void drvHostAudioPaStreamStateChangedCallback(pa_stream *pStream, void *pvUser) 411 616 { 412 617 AssertPtrReturnVoid(pStream); … … 420 625 case PA_STREAM_FAILED: 421 626 case PA_STREAM_TERMINATED: 422 paSignalWaiter(pThis);627 drvHostAudioPaSignalWaiter(pThis); 423 628 break; 424 629 … … 430 635 #ifdef DEBUG 431 636 432 static void paStreamCbReqWrite(pa_stream *pStream, size_t cbLen, void *pvContext)637 static void drvHostAudioPaStreamReqWriteDebugCallback(pa_stream *pStream, size_t cbLen, void *pvContext) 433 638 { 434 639 RT_NOREF(cbLen, pvContext); … … 445 650 446 651 447 static void paStreamCbUnderflow(pa_stream *pStream, void *pvContext)652 static void drvHostAudioPaStreamUnderflowDebugCallback(pa_stream *pStream, void *pvContext) 448 653 { 449 654 PPULSEAUDIOSTREAM pStrm = (PPULSEAUDIOSTREAM)pvContext; … … 489 694 490 695 491 static void paStreamCbOverflow(pa_stream *pStream, void *pvContext)696 static void drvHostAudioPaStreamOverflowDebugCallback(pa_stream *pStream, void *pvContext) 492 697 { 493 698 RT_NOREF(pStream, pvContext); … … 498 703 #endif /* DEBUG */ 499 704 500 static void paStreamCbSuccess(pa_stream *pStream, int fSuccess, void *pvUser) 501 { 502 AssertPtrReturnVoid(pStream); 503 504 PPULSEAUDIOSTREAM pStrm = (PPULSEAUDIOSTREAM)pvUser; 505 AssertPtrReturnVoid(pStrm); 506 507 pStrm->fOpSuccess = fSuccess; 508 509 if (fSuccess) 510 paSignalWaiter(pStrm->pDrv); 511 else 512 paError(pStrm->pDrv, "Failed to finish stream operation"); 513 } 514 515 516 static int paStreamOpen(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM pStreamPA, bool fIn, const char *pszName) 705 706 static int drvHostAudioPaStreamOpen(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM pStreamPA, bool fIn, const char *pszName) 517 707 { 518 708 AssertPtrReturn(pThis, VERR_INVALID_POINTER); … … 550 740 551 741 #ifdef DEBUG 552 pa_stream_set_write_callback (pStream, paStreamCbReqWrite,pStreamPA);553 pa_stream_set_underflow_callback (pStream, paStreamCbUnderflow,pStreamPA);742 pa_stream_set_write_callback( pStream, drvHostAudioPaStreamReqWriteDebugCallback, pStreamPA); 743 pa_stream_set_underflow_callback( pStream, drvHostAudioPaStreamUnderflowDebugCallback, pStreamPA); 554 744 if (!fIn) /* Only for output streams. */ 555 pa_stream_set_overflow_callback(pStream, paStreamCbOverflow,pStreamPA);745 pa_stream_set_overflow_callback(pStream, drvHostAudioPaStreamOverflowDebugCallback, pStreamPA); 556 746 #endif 557 pa_stream_set_state_callback (pStream, paStreamCbStateChanged,pThis);747 pa_stream_set_state_callback( pStream, drvHostAudioPaStreamStateChangedCallback, pThis); 558 748 559 749 uint32_t flags = PA_STREAM_NOFLAGS; … … 645 835 646 836 647 static int paCreateStreamOut(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM pStreamPA,648 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)837 static int drvHostAudioPaCreateStreamOut(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM pStreamPA, 838 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq) 649 839 { 650 840 pStreamPA->pDrainOp = NULL; 651 841 652 pStreamPA->SampleSpec.format = paAudioPropsToPulse(&pCfgReq->Props);842 pStreamPA->SampleSpec.format = drvHostAudioPaPropsToPulse(&pCfgReq->Props); 653 843 pStreamPA->SampleSpec.rate = PDMAudioPropsHz(&pCfgReq->Props); 654 844 pStreamPA->SampleSpec.channels = PDMAudioPropsChannels(&pCfgReq->Props); … … 674 864 675 865 /* Note that the struct BufAttr is updated to the obtained values after this call! */ 676 int rc = paStreamOpen(pThis, pStreamPA, false /* fIn */, szName);866 int rc = drvHostAudioPaStreamOpen(pThis, pStreamPA, false /* fIn */, szName); 677 867 if (RT_FAILURE(rc)) 678 868 return rc; 679 869 680 rc = paPulseToAudioProps(&pCfgAcq->Props, pStreamPA->SampleSpec.format,681 pStreamPA->SampleSpec.channels, pStreamPA->SampleSpec.rate);870 rc = drvHostAudioPaToAudioProps(&pCfgAcq->Props, pStreamPA->SampleSpec.format, 871 pStreamPA->SampleSpec.channels, pStreamPA->SampleSpec.rate); 682 872 if (RT_FAILURE(rc)) 683 873 { … … 699 889 700 890 701 static int paCreateStreamIn(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM pStreamPA,702 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)703 { 704 pStreamPA->SampleSpec.format = paAudioPropsToPulse(&pCfgReq->Props);891 static int drvHostAudioPaCreateStreamIn(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM pStreamPA, 892 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq) 893 { 894 pStreamPA->SampleSpec.format = drvHostAudioPaPropsToPulse(&pCfgReq->Props); 705 895 pStreamPA->SampleSpec.rate = PDMAudioPropsHz(&pCfgReq->Props); 706 896 pStreamPA->SampleSpec.channels = PDMAudioPropsChannels(&pCfgReq->Props); … … 715 905 716 906 /* Note: Other members of BufAttr are ignored for record streams. */ 717 int rc = paStreamOpen(pThis, pStreamPA, true /* fIn */, szName);907 int rc = drvHostAudioPaStreamOpen(pThis, pStreamPA, true /* fIn */, szName); 718 908 if (RT_FAILURE(rc)) 719 909 return rc; 720 910 721 rc = paPulseToAudioProps(&pCfgAcq->Props, pStreamPA->SampleSpec.format,722 pStreamPA->SampleSpec.channels, pStreamPA->SampleSpec.rate);911 rc = drvHostAudioPaToAudioProps(&pCfgAcq->Props, pStreamPA->SampleSpec.format, 912 pStreamPA->SampleSpec.channels, pStreamPA->SampleSpec.rate); 723 913 if (RT_FAILURE(rc)) 724 914 { … … 742 932 743 933 /** 934 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate} 935 */ 936 static DECLCALLBACK(int) drvHostAudioPaHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream, 937 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq) 938 { 939 AssertPtrReturn(pInterface, VERR_INVALID_POINTER); 940 AssertPtrReturn(pStream, VERR_INVALID_POINTER); 941 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER); 942 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER); 943 944 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface); 945 PPULSEAUDIOSTREAM pStreamPA = (PPULSEAUDIOSTREAM)pStream; 946 947 int rc; 948 if (pCfgReq->enmDir == PDMAUDIODIR_IN) 949 rc = drvHostAudioPaCreateStreamIn (pThis, pStreamPA, pCfgReq, pCfgAcq); 950 else if (pCfgReq->enmDir == PDMAUDIODIR_OUT) 951 rc = drvHostAudioPaCreateStreamOut(pThis, pStreamPA, pCfgReq, pCfgAcq); 952 else 953 AssertFailedReturn(VERR_NOT_IMPLEMENTED); 954 955 if (RT_SUCCESS(rc)) 956 { 957 pStreamPA->pCfg = PDMAudioStrmCfgDup(pCfgAcq); 958 if (!pStreamPA->pCfg) 959 rc = VERR_NO_MEMORY; 960 } 961 962 return rc; 963 } 964 965 966 /** 967 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy} 968 */ 969 static DECLCALLBACK(int) drvHostAudioPaHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream) 970 { 971 AssertPtrReturn(pInterface, VERR_INVALID_POINTER); 972 AssertPtrReturn(pStream, VERR_INVALID_POINTER); 973 974 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface); 975 PPULSEAUDIOSTREAM pStreamPA = (PPULSEAUDIOSTREAM)pStream; 976 977 if (pStreamPA->pStream) 978 { 979 pa_threaded_mainloop_lock(pThis->pMainLoop); 980 981 /* Make sure to cancel a pending draining operation, if any. */ 982 if (pStreamPA->pDrainOp) 983 { 984 pa_operation_cancel(pStreamPA->pDrainOp); 985 pStreamPA->pDrainOp = NULL; 986 } 987 988 pa_stream_disconnect(pStreamPA->pStream); 989 pa_stream_unref(pStreamPA->pStream); 990 991 pStreamPA->pStream = NULL; 992 993 pa_threaded_mainloop_unlock(pThis->pMainLoop); 994 } 995 996 if (pStreamPA->pCfg) 997 { 998 PDMAudioStrmCfgFree(pStreamPA->pCfg); 999 pStreamPA->pCfg = NULL; 1000 } 1001 1002 return VINF_SUCCESS; 1003 } 1004 1005 1006 /** 1007 * Pulse audio pa_stream_drain() completion callback. 1008 */ 1009 static void drvHostAudioPaStreamDrainCompletionCallback(pa_stream *pStream, int fSuccess, void *pvUser) 1010 { 1011 AssertPtrReturnVoid(pStream); 1012 PPULSEAUDIOSTREAM pStreamPA = (PPULSEAUDIOSTREAM)pvUser; 1013 AssertPtrReturnVoid(pStreamPA); 1014 1015 pStreamPA->fOpSuccess = fSuccess; 1016 if (fSuccess) 1017 pa_operation_unref(pa_stream_cork(pStream, 1, drvHostAudioPaStreamSuccessCallback, pvUser)); 1018 else 1019 drvHostAudioPaError(pStreamPA->pDrv, "Failed to drain stream"); 1020 1021 if (pStreamPA->pDrainOp) 1022 { 1023 pa_operation_unref(pStreamPA->pDrainOp); 1024 pStreamPA->pDrainOp = NULL; 1025 } 1026 } 1027 1028 1029 static int drvHostAudioPaStreamControlOut(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM pStreamPA, PDMAUDIOSTREAMCMD enmStreamCmd) 1030 { 1031 int rc = VINF_SUCCESS; 1032 1033 switch (enmStreamCmd) 1034 { 1035 case PDMAUDIOSTREAMCMD_ENABLE: 1036 case PDMAUDIOSTREAMCMD_RESUME: 1037 { 1038 pa_threaded_mainloop_lock(pThis->pMainLoop); 1039 1040 if ( pStreamPA->pDrainOp 1041 && pa_operation_get_state(pStreamPA->pDrainOp) != PA_OPERATION_DONE) 1042 { 1043 pa_operation_cancel(pStreamPA->pDrainOp); 1044 pa_operation_unref(pStreamPA->pDrainOp); 1045 1046 pStreamPA->pDrainOp = NULL; 1047 } 1048 else 1049 { 1050 /* Uncork (resume) stream. */ 1051 rc = drvHostAudioPaWaitFor(pThis, pa_stream_cork(pStreamPA->pStream, 0 /* Uncork */, drvHostAudioPaStreamSuccessCallback, pStreamPA)); 1052 } 1053 1054 pa_threaded_mainloop_unlock(pThis->pMainLoop); 1055 break; 1056 } 1057 1058 case PDMAUDIOSTREAMCMD_DISABLE: 1059 case PDMAUDIOSTREAMCMD_PAUSE: 1060 { 1061 /* Pause audio output (the Pause bit of the AC97 x_CR register is set). 1062 * Note that we must return immediately from here! */ 1063 pa_threaded_mainloop_lock(pThis->pMainLoop); 1064 if (!pStreamPA->pDrainOp) 1065 { 1066 rc = drvHostAudioPaWaitFor(pThis, pa_stream_trigger(pStreamPA->pStream, drvHostAudioPaStreamSuccessCallback, pStreamPA)); 1067 if (RT_SUCCESS(rc)) 1068 pStreamPA->pDrainOp = pa_stream_drain(pStreamPA->pStream, drvHostAudioPaStreamDrainCompletionCallback, pStreamPA); 1069 } 1070 pa_threaded_mainloop_unlock(pThis->pMainLoop); 1071 break; 1072 } 1073 1074 default: 1075 rc = VERR_NOT_SUPPORTED; 1076 break; 1077 } 1078 1079 LogFlowFuncLeaveRC(rc); 1080 return rc; 1081 } 1082 1083 1084 static int drvHostAudioPaStreamControlIn(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM pStreamPA, PDMAUDIOSTREAMCMD enmStreamCmd) 1085 { 1086 int rc = VINF_SUCCESS; 1087 1088 LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd)); 1089 1090 switch (enmStreamCmd) 1091 { 1092 case PDMAUDIOSTREAMCMD_ENABLE: 1093 case PDMAUDIOSTREAMCMD_RESUME: 1094 { 1095 pa_threaded_mainloop_lock(pThis->pMainLoop); 1096 rc = drvHostAudioPaWaitFor(pThis, pa_stream_cork(pStreamPA->pStream, 0 /* Play / resume */, drvHostAudioPaStreamSuccessCallback, pStreamPA)); 1097 pa_threaded_mainloop_unlock(pThis->pMainLoop); 1098 break; 1099 } 1100 1101 case PDMAUDIOSTREAMCMD_DISABLE: 1102 case PDMAUDIOSTREAMCMD_PAUSE: 1103 { 1104 pa_threaded_mainloop_lock(pThis->pMainLoop); 1105 if (pStreamPA->pu8PeekBuf) /* Do we need to drop the peek buffer?*/ 1106 { 1107 pa_stream_drop(pStreamPA->pStream); 1108 pStreamPA->pu8PeekBuf = NULL; 1109 } 1110 1111 rc = drvHostAudioPaWaitFor(pThis, pa_stream_cork(pStreamPA->pStream, 1 /* Stop / pause */, drvHostAudioPaStreamSuccessCallback, pStreamPA)); 1112 pa_threaded_mainloop_unlock(pThis->pMainLoop); 1113 break; 1114 } 1115 1116 default: 1117 rc = VERR_NOT_SUPPORTED; 1118 break; 1119 } 1120 1121 return rc; 1122 } 1123 1124 1125 /** 1126 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl} 1127 */ 1128 static DECLCALLBACK(int) drvHostAudioPaHA_StreamControl(PPDMIHOSTAUDIO pInterface, 1129 PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd) 1130 { 1131 AssertPtrReturn(pInterface, VERR_INVALID_POINTER); 1132 AssertPtrReturn(pStream, VERR_INVALID_POINTER); 1133 1134 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface); 1135 PPULSEAUDIOSTREAM pStreamPA = (PPULSEAUDIOSTREAM)pStream; 1136 1137 if (!pStreamPA->pCfg) /* Not (yet) configured? Skip. */ 1138 return VINF_SUCCESS; 1139 1140 int rc; 1141 if (pStreamPA->pCfg->enmDir == PDMAUDIODIR_IN) 1142 rc = drvHostAudioPaStreamControlIn (pThis, pStreamPA, enmStreamCmd); 1143 else if (pStreamPA->pCfg->enmDir == PDMAUDIODIR_OUT) 1144 rc = drvHostAudioPaStreamControlOut(pThis, pStreamPA, enmStreamCmd); 1145 else 1146 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED); 1147 1148 return rc; 1149 } 1150 1151 1152 static uint32_t drvHostAudioPaStreamGetAvailable(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM pStreamPA) 1153 { 1154 pa_threaded_mainloop_lock(pThis->pMainLoop); 1155 1156 uint32_t cbAvail = 0; 1157 1158 if (PA_STREAM_IS_GOOD(pa_stream_get_state(pStreamPA->pStream))) 1159 { 1160 if (pStreamPA->pCfg->enmDir == PDMAUDIODIR_IN) 1161 { 1162 cbAvail = (uint32_t)pa_stream_readable_size(pStreamPA->pStream); 1163 Log3Func(("cbReadable=%RU32\n", cbAvail)); 1164 } 1165 else if (pStreamPA->pCfg->enmDir == PDMAUDIODIR_OUT) 1166 { 1167 size_t cbWritable = pa_stream_writable_size(pStreamPA->pStream); 1168 1169 Log3Func(("cbWritable=%zu, maxLength=%RU32, minReq=%RU32\n", 1170 cbWritable, pStreamPA->BufAttr.maxlength, pStreamPA->BufAttr.minreq)); 1171 1172 /* Don't report more writable than the PA server can handle. */ 1173 if (cbWritable > pStreamPA->BufAttr.maxlength) 1174 cbWritable = pStreamPA->BufAttr.maxlength; 1175 1176 cbAvail = (uint32_t)cbWritable; 1177 } 1178 else 1179 AssertFailed(); 1180 } 1181 1182 pa_threaded_mainloop_unlock(pThis->pMainLoop); 1183 1184 return cbAvail; 1185 } 1186 1187 1188 /** 1189 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable} 1190 */ 1191 static DECLCALLBACK(uint32_t) drvHostAudioPaHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream) 1192 { 1193 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface); 1194 PPULSEAUDIOSTREAM pStreamPA = (PPULSEAUDIOSTREAM)pStream; 1195 1196 return drvHostAudioPaStreamGetAvailable(pThis, pStreamPA); 1197 } 1198 1199 1200 /** 1201 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable} 1202 */ 1203 static DECLCALLBACK(uint32_t) drvHostAudioPaHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream) 1204 { 1205 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface); 1206 PPULSEAUDIOSTREAM pStreamPA = (PPULSEAUDIOSTREAM)pStream; 1207 1208 return drvHostAudioPaStreamGetAvailable(pThis, pStreamPA); 1209 } 1210 1211 1212 /** 1213 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus} 1214 */ 1215 static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvHostAudioPaHA_StreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream) 1216 { 1217 AssertPtrReturn(pInterface, VERR_INVALID_POINTER); 1218 RT_NOREF(pStream); 1219 1220 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface); 1221 1222 PDMAUDIOSTREAMSTS fStrmSts = PDMAUDIOSTREAMSTS_FLAGS_NONE; 1223 1224 /* Check PulseAudio's general status. */ 1225 if ( pThis->pContext 1226 && PA_CONTEXT_IS_GOOD(pa_context_get_state(pThis->pContext))) 1227 fStrmSts = PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED | PDMAUDIOSTREAMSTS_FLAGS_ENABLED; 1228 1229 return fStrmSts; 1230 } 1231 1232 1233 /** 1234 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay} 1235 */ 1236 static DECLCALLBACK(int) drvHostAudioPaHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream, 1237 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten) 1238 { 1239 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface); 1240 AssertPtr(pThis); 1241 PPULSEAUDIOSTREAM pPAStream = (PPULSEAUDIOSTREAM)pStream; 1242 AssertPtrReturn(pPAStream, VERR_INVALID_POINTER); 1243 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); 1244 AssertReturn(cbBuf, VERR_INVALID_PARAMETER); 1245 AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER); 1246 1247 pa_threaded_mainloop_lock(pThis->pMainLoop); 1248 1249 #ifdef LOG_ENABLED 1250 const pa_usec_t tsNowUs = pa_rtclock_now(); 1251 const pa_usec_t tsDeltaPlayedUs = tsNowUs - pPAStream->tsLastReadWrittenUs; 1252 pPAStream->tsLastReadWrittenUs = tsNowUs; 1253 Log3Func(("tsDeltaPlayedMs=%RU64\n", tsDeltaPlayedUs / RT_US_1MS)); 1254 #endif 1255 1256 int rc; 1257 size_t const cbWriteable = pa_stream_writable_size(pPAStream->pStream); 1258 if (cbWriteable != (size_t)-1) 1259 { 1260 size_t cbLeft = RT_MIN(cbWriteable, cbBuf); 1261 Assert(cbLeft > 0 /* At this point we better have *something* to write (DrvAudio checked before calling). */); 1262 if (pa_stream_write(pPAStream->pStream, pvBuf, cbLeft, NULL /*pfnFree*/, 0 /*offset*/, PA_SEEK_RELATIVE) >= 0) 1263 { 1264 *pcbWritten = (uint32_t)cbLeft; 1265 rc = VINF_SUCCESS; 1266 } 1267 else 1268 rc = drvHostAudioPaError(pPAStream->pDrv, "Failed to write to output stream"); 1269 } 1270 else 1271 rc = drvHostAudioPaError(pPAStream->pDrv, "Failed to determine output data size"); 1272 1273 pa_threaded_mainloop_unlock(pThis->pMainLoop); 1274 return rc; 1275 } 1276 1277 1278 /** 744 1279 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture} 745 1280 */ 746 static DECLCALLBACK(int) drvHost PulseAudioHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,1281 static DECLCALLBACK(int) drvHostAudioPaHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream, 747 1282 void *pvBuf, uint32_t uBufSize, uint32_t *puRead) 748 1283 { … … 762 1297 763 1298 if (cbAvail == (size_t)-1) 764 return paError(pStreamPA->pDrv, "Failed to determine input data size");1299 return drvHostAudioPaError(pStreamPA->pDrv, "Failed to determine input data size"); 765 1300 766 1301 /* If the buffer was not dropped last call, add what remains. */ … … 858 1393 859 1394 860 /** 861 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay} 862 */ 863 static DECLCALLBACK(int) drvHostPulseAudioHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream, 864 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten) 865 { 866 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface); 867 AssertPtr(pThis); 868 PPULSEAUDIOSTREAM pPAStream = (PPULSEAUDIOSTREAM)pStream; 869 AssertPtrReturn(pPAStream, VERR_INVALID_POINTER); 870 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); 871 AssertReturn(cbBuf, VERR_INVALID_PARAMETER); 872 AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER); 873 874 pa_threaded_mainloop_lock(pThis->pMainLoop); 875 876 #ifdef LOG_ENABLED 877 const pa_usec_t tsNowUs = pa_rtclock_now(); 878 const pa_usec_t tsDeltaPlayedUs = tsNowUs - pPAStream->tsLastReadWrittenUs; 879 pPAStream->tsLastReadWrittenUs = tsNowUs; 880 Log3Func(("tsDeltaPlayedMs=%RU64\n", tsDeltaPlayedUs / RT_US_1MS)); 881 #endif 882 883 int rc; 884 size_t const cbWriteable = pa_stream_writable_size(pPAStream->pStream); 885 if (cbWriteable != (size_t)-1) 886 { 887 size_t cbLeft = RT_MIN(cbWriteable, cbBuf); 888 Assert(cbLeft > 0 /* At this point we better have *something* to write (DrvAudio checked before calling). */); 889 if (pa_stream_write(pPAStream->pStream, pvBuf, cbLeft, NULL /*pfnFree*/, 0 /*offset*/, PA_SEEK_RELATIVE) >= 0) 890 { 891 *pcbWritten = (uint32_t)cbLeft; 892 rc = VINF_SUCCESS; 893 } 894 else 895 rc = paError(pPAStream->pDrv, "Failed to write to output stream"); 896 } 897 else 898 rc = paError(pPAStream->pDrv, "Failed to determine output data size"); 899 900 pa_threaded_mainloop_unlock(pThis->pMainLoop); 901 return rc; 902 } 903 904 905 /** @todo Implement va handling. */ 906 static int paError(PDRVHOSTPULSEAUDIO pThis, const char *szMsg) 907 { 908 AssertPtrReturn(pThis, VERR_INVALID_POINTER); 909 AssertPtrReturn(szMsg, VERR_INVALID_POINTER); 910 911 if (pThis->cLogErrors++ < VBOX_PULSEAUDIO_MAX_LOG_REL_ERRORS) 912 { 913 int rc2 = pa_context_errno(pThis->pContext); 914 LogRel2(("PulseAudio: %s: %s\n", szMsg, pa_strerror(rc2))); 915 } 916 917 /** @todo Implement some PulseAudio -> IPRT mapping here. */ 918 return VERR_GENERAL_FAILURE; 919 } 920 921 922 static void paEnumSinkCb(pa_context *pCtx, const pa_sink_info *pInfo, int eol, void *pvUserData) 923 { 924 if (eol > 0) 925 return; 926 927 PPULSEAUDIOENUMCBCTX pCbCtx = (PPULSEAUDIOENUMCBCTX)pvUserData; 928 AssertPtrReturnVoid(pCbCtx); 929 PDRVHOSTPULSEAUDIO pThis = pCbCtx->pDrv; 930 AssertPtrReturnVoid(pThis); 931 if (eol < 0) 932 { 933 pThis->fEnumOpSuccess = false; 934 pa_threaded_mainloop_signal(pCbCtx->pDrv->pMainLoop, 0); 935 return; 936 } 937 938 AssertPtrReturnVoid(pCtx); 939 AssertPtrReturnVoid(pInfo); 940 941 LogRel2(("PulseAudio: Using output sink '%s'\n", pInfo->name)); 942 943 /** @todo Store sinks + channel mapping in callback context as soon as we have surround support. */ 944 pCbCtx->cDevOut++; 945 946 pThis->fEnumOpSuccess = true; 947 pa_threaded_mainloop_signal(pCbCtx->pDrv->pMainLoop, 0); 948 } 949 950 951 static void paEnumSourceCb(pa_context *pCtx, const pa_source_info *pInfo, int eol, void *pvUserData) 952 { 953 if (eol > 0) 954 return; 955 956 PPULSEAUDIOENUMCBCTX pCbCtx = (PPULSEAUDIOENUMCBCTX)pvUserData; 957 AssertPtrReturnVoid(pCbCtx); 958 PDRVHOSTPULSEAUDIO pThis = pCbCtx->pDrv; 959 AssertPtrReturnVoid(pThis); 960 if (eol < 0) 961 { 962 pThis->fEnumOpSuccess = false; 963 pa_threaded_mainloop_signal(pCbCtx->pDrv->pMainLoop, 0); 964 return; 965 } 966 967 AssertPtrReturnVoid(pCtx); 968 AssertPtrReturnVoid(pInfo); 969 970 LogRel2(("PulseAudio: Using input source '%s'\n", pInfo->name)); 971 972 /** @todo Store sources + channel mapping in callback context as soon as we have surround support. */ 973 pCbCtx->cDevIn++; 974 975 pThis->fEnumOpSuccess = true; 976 pa_threaded_mainloop_signal(pCbCtx->pDrv->pMainLoop, 0); 977 } 978 979 980 static void paEnumServerCb(pa_context *pCtx, const pa_server_info *pInfo, void *pvUserData) 981 { 982 AssertPtrReturnVoid(pCtx); 983 PPULSEAUDIOENUMCBCTX pCbCtx = (PPULSEAUDIOENUMCBCTX)pvUserData; 984 AssertPtrReturnVoid(pCbCtx); 985 PDRVHOSTPULSEAUDIO pThis = pCbCtx->pDrv; 986 AssertPtrReturnVoid(pThis); 987 988 if (!pInfo) 989 { 990 pThis->fEnumOpSuccess = false; 991 pa_threaded_mainloop_signal(pCbCtx->pDrv->pMainLoop, 0); 992 return; 993 } 994 995 if (pInfo->default_sink_name) 996 { 997 Assert(RTStrIsValidEncoding(pInfo->default_sink_name)); 998 pCbCtx->pszDefaultSink = RTStrDup(pInfo->default_sink_name); 999 } 1000 1001 if (pInfo->default_sink_name) 1002 { 1003 Assert(RTStrIsValidEncoding(pInfo->default_source_name)); 1004 pCbCtx->pszDefaultSource = RTStrDup(pInfo->default_source_name); 1005 } 1006 1007 pThis->fEnumOpSuccess = true; 1008 pa_threaded_mainloop_signal(pThis->pMainLoop, 0); 1009 } 1010 1011 1012 static int paEnumerate(PDRVHOSTPULSEAUDIO pThis, PPDMAUDIOBACKENDCFG pCfg, uint32_t fEnum) 1013 { 1014 AssertPtrReturn(pThis, VERR_INVALID_POINTER); 1015 AssertPtrReturn(pCfg, VERR_INVALID_POINTER); 1016 1017 PDMAUDIOBACKENDCFG Cfg; 1018 RT_ZERO(Cfg); 1019 1020 RTStrPrintf2(Cfg.szName, sizeof(Cfg.szName), "PulseAudio"); 1021 1022 Cfg.cbStreamOut = sizeof(PULSEAUDIOSTREAM); 1023 Cfg.cbStreamIn = sizeof(PULSEAUDIOSTREAM); 1024 Cfg.cMaxStreamsOut = UINT32_MAX; 1025 Cfg.cMaxStreamsIn = UINT32_MAX; 1026 1027 PULSEAUDIOENUMCBCTX CbCtx; 1028 RT_ZERO(CbCtx); 1029 1030 CbCtx.pDrv = pThis; 1031 CbCtx.fFlags = fEnum; 1032 1033 bool fLog = (fEnum & PULSEAUDIOENUMCBFLAGS_LOG); 1034 1035 pa_threaded_mainloop_lock(pThis->pMainLoop); 1036 1037 pThis->fEnumOpSuccess = false; 1038 1039 LogRel(("PulseAudio: Retrieving server information ...\n")); 1040 1041 /* Check if server information is available and bail out early if it isn't. */ 1042 pa_operation *paOpServerInfo = pa_context_get_server_info(pThis->pContext, paEnumServerCb, &CbCtx); 1043 if (!paOpServerInfo) 1044 { 1045 pa_threaded_mainloop_unlock(pThis->pMainLoop); 1046 1047 LogRel(("PulseAudio: Server information not available, skipping enumeration\n")); 1048 return VINF_SUCCESS; 1049 } 1050 1051 int rc = paWaitFor(pThis, paOpServerInfo); 1052 if (RT_SUCCESS(rc) && !pThis->fEnumOpSuccess) 1053 rc = VERR_AUDIO_BACKEND_INIT_FAILED; /* error code does not matter */ 1054 if (RT_SUCCESS(rc)) 1055 { 1056 if (CbCtx.pszDefaultSink) 1057 { 1058 if (fLog) 1059 LogRel2(("PulseAudio: Default output sink is '%s'\n", CbCtx.pszDefaultSink)); 1060 1061 pThis->fEnumOpSuccess = false; 1062 rc = paWaitFor(pThis, pa_context_get_sink_info_by_name(pThis->pContext, CbCtx.pszDefaultSink, 1063 paEnumSinkCb, &CbCtx)); 1064 if (RT_SUCCESS(rc) && !pThis->fEnumOpSuccess) 1065 rc = VERR_AUDIO_BACKEND_INIT_FAILED; /* error code does not matter */ 1066 if ( RT_FAILURE(rc) 1067 && fLog) 1068 { 1069 LogRel(("PulseAudio: Error enumerating properties for default output sink '%s'\n", CbCtx.pszDefaultSink)); 1070 } 1071 } 1072 else if (fLog) 1073 LogRel2(("PulseAudio: No default output sink found\n")); 1074 1075 if (RT_SUCCESS(rc)) 1076 { 1077 if (CbCtx.pszDefaultSource) 1078 { 1079 if (fLog) 1080 LogRel2(("PulseAudio: Default input source is '%s'\n", CbCtx.pszDefaultSource)); 1081 1082 pThis->fEnumOpSuccess = false; 1083 rc = paWaitFor(pThis, pa_context_get_source_info_by_name(pThis->pContext, CbCtx.pszDefaultSource, 1084 paEnumSourceCb, &CbCtx)); 1085 if ( (RT_FAILURE(rc) || !pThis->fEnumOpSuccess) 1086 && fLog) 1087 { 1088 LogRel(("PulseAudio: Error enumerating properties for default input source '%s'\n", CbCtx.pszDefaultSource)); 1089 } 1090 } 1091 else if (fLog) 1092 LogRel2(("PulseAudio: No default input source found\n")); 1093 } 1094 1095 if (RT_SUCCESS(rc)) 1096 { 1097 if (fLog) 1098 { 1099 LogRel2(("PulseAudio: Found %RU8 host playback device(s)\n", CbCtx.cDevOut)); 1100 LogRel2(("PulseAudio: Found %RU8 host capturing device(s)\n", CbCtx.cDevIn)); 1101 } 1102 1103 if (pCfg) 1104 memcpy(pCfg, &Cfg, sizeof(PDMAUDIOBACKENDCFG)); 1105 } 1106 1107 if (CbCtx.pszDefaultSink) 1108 { 1109 RTStrFree(CbCtx.pszDefaultSink); 1110 CbCtx.pszDefaultSink = NULL; 1111 } 1112 1113 if (CbCtx.pszDefaultSource) 1114 { 1115 RTStrFree(CbCtx.pszDefaultSource); 1116 CbCtx.pszDefaultSource = NULL; 1117 } 1118 } 1119 else if (fLog) 1120 LogRel(("PulseAudio: Error enumerating PulseAudio server properties\n")); 1121 1122 pa_threaded_mainloop_unlock(pThis->pMainLoop); 1123 1124 LogFlowFuncLeaveRC(rc); 1125 return rc; 1126 } 1127 1128 1129 static int paDestroyStreamIn(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM pStreamPA) 1130 { 1131 LogFlowFuncEnter(); 1132 1133 if (pStreamPA->pStream) 1134 { 1135 pa_threaded_mainloop_lock(pThis->pMainLoop); 1136 1137 pa_stream_disconnect(pStreamPA->pStream); 1138 pa_stream_unref(pStreamPA->pStream); 1139 1140 pStreamPA->pStream = NULL; 1141 1142 pa_threaded_mainloop_unlock(pThis->pMainLoop); 1143 } 1144 1145 return VINF_SUCCESS; 1146 } 1147 1148 1149 static int paDestroyStreamOut(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM pStreamPA) 1150 { 1151 if (pStreamPA->pStream) 1152 { 1153 pa_threaded_mainloop_lock(pThis->pMainLoop); 1154 1155 /* Make sure to cancel a pending draining operation, if any. */ 1156 if (pStreamPA->pDrainOp) 1157 { 1158 pa_operation_cancel(pStreamPA->pDrainOp); 1159 pStreamPA->pDrainOp = NULL; 1160 } 1161 1162 pa_stream_disconnect(pStreamPA->pStream); 1163 pa_stream_unref(pStreamPA->pStream); 1164 1165 pStreamPA->pStream = NULL; 1166 1167 pa_threaded_mainloop_unlock(pThis->pMainLoop); 1168 } 1169 1170 return VINF_SUCCESS; 1171 } 1172 1173 1174 static int paControlStreamOut(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM pStreamPA, PDMAUDIOSTREAMCMD enmStreamCmd) 1175 { 1176 int rc = VINF_SUCCESS; 1177 1178 switch (enmStreamCmd) 1179 { 1180 case PDMAUDIOSTREAMCMD_ENABLE: 1181 case PDMAUDIOSTREAMCMD_RESUME: 1182 { 1183 pa_threaded_mainloop_lock(pThis->pMainLoop); 1184 1185 if ( pStreamPA->pDrainOp 1186 && pa_operation_get_state(pStreamPA->pDrainOp) != PA_OPERATION_DONE) 1187 { 1188 pa_operation_cancel(pStreamPA->pDrainOp); 1189 pa_operation_unref(pStreamPA->pDrainOp); 1190 1191 pStreamPA->pDrainOp = NULL; 1192 } 1193 else 1194 { 1195 /* Uncork (resume) stream. */ 1196 rc = paWaitFor(pThis, pa_stream_cork(pStreamPA->pStream, 0 /* Uncork */, paStreamCbSuccess, pStreamPA)); 1197 } 1198 1199 pa_threaded_mainloop_unlock(pThis->pMainLoop); 1200 break; 1201 } 1202 1203 case PDMAUDIOSTREAMCMD_DISABLE: 1204 case PDMAUDIOSTREAMCMD_PAUSE: 1205 { 1206 /* Pause audio output (the Pause bit of the AC97 x_CR register is set). 1207 * Note that we must return immediately from here! */ 1208 pa_threaded_mainloop_lock(pThis->pMainLoop); 1209 if (!pStreamPA->pDrainOp) 1210 { 1211 rc = paWaitFor(pThis, pa_stream_trigger(pStreamPA->pStream, paStreamCbSuccess, pStreamPA)); 1212 if (RT_SUCCESS(rc)) 1213 pStreamPA->pDrainOp = pa_stream_drain(pStreamPA->pStream, paStreamCbDrain, pStreamPA); 1214 } 1215 pa_threaded_mainloop_unlock(pThis->pMainLoop); 1216 break; 1217 } 1218 1219 default: 1220 rc = VERR_NOT_SUPPORTED; 1221 break; 1222 } 1223 1224 LogFlowFuncLeaveRC(rc); 1225 return rc; 1226 } 1227 1228 1229 static int paControlStreamIn(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM pStreamPA, PDMAUDIOSTREAMCMD enmStreamCmd) 1230 { 1231 int rc = VINF_SUCCESS; 1232 1233 LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd)); 1234 1235 switch (enmStreamCmd) 1236 { 1237 case PDMAUDIOSTREAMCMD_ENABLE: 1238 case PDMAUDIOSTREAMCMD_RESUME: 1239 { 1240 pa_threaded_mainloop_lock(pThis->pMainLoop); 1241 rc = paWaitFor(pThis, pa_stream_cork(pStreamPA->pStream, 0 /* Play / resume */, paStreamCbSuccess, pStreamPA)); 1242 pa_threaded_mainloop_unlock(pThis->pMainLoop); 1243 break; 1244 } 1245 1246 case PDMAUDIOSTREAMCMD_DISABLE: 1247 case PDMAUDIOSTREAMCMD_PAUSE: 1248 { 1249 pa_threaded_mainloop_lock(pThis->pMainLoop); 1250 if (pStreamPA->pu8PeekBuf) /* Do we need to drop the peek buffer?*/ 1251 { 1252 pa_stream_drop(pStreamPA->pStream); 1253 pStreamPA->pu8PeekBuf = NULL; 1254 } 1255 1256 rc = paWaitFor(pThis, pa_stream_cork(pStreamPA->pStream, 1 /* Stop / pause */, paStreamCbSuccess, pStreamPA)); 1257 pa_threaded_mainloop_unlock(pThis->pMainLoop); 1258 break; 1259 } 1260 1261 default: 1262 rc = VERR_NOT_SUPPORTED; 1263 break; 1264 } 1265 1266 return rc; 1267 } 1268 1269 1270 /** 1271 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig} 1272 */ 1273 static DECLCALLBACK(int) drvHostPulseAudioHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg) 1274 { 1275 AssertPtrReturn(pInterface, VERR_INVALID_POINTER); 1276 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER); 1277 1278 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface); 1279 1280 return paEnumerate(pThis, pBackendCfg, PULSEAUDIOENUMCBFLAGS_LOG /* fEnum */); 1281 } 1282 1283 1284 /** 1285 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus} 1286 */ 1287 static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostPulseAudioHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir) 1288 { 1289 RT_NOREF(enmDir); 1290 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN); 1291 1292 return PDMAUDIOBACKENDSTS_RUNNING; 1293 } 1294 1295 1296 /** 1297 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate} 1298 */ 1299 static DECLCALLBACK(int) drvHostPulseAudioHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream, 1300 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq) 1301 { 1302 AssertPtrReturn(pInterface, VERR_INVALID_POINTER); 1303 AssertPtrReturn(pStream, VERR_INVALID_POINTER); 1304 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER); 1305 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER); 1306 1307 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface); 1308 PPULSEAUDIOSTREAM pStreamPA = (PPULSEAUDIOSTREAM)pStream; 1309 1310 int rc; 1311 if (pCfgReq->enmDir == PDMAUDIODIR_IN) 1312 rc = paCreateStreamIn (pThis, pStreamPA, pCfgReq, pCfgAcq); 1313 else if (pCfgReq->enmDir == PDMAUDIODIR_OUT) 1314 rc = paCreateStreamOut(pThis, pStreamPA, pCfgReq, pCfgAcq); 1315 else 1316 AssertFailedReturn(VERR_NOT_IMPLEMENTED); 1317 1318 if (RT_SUCCESS(rc)) 1319 { 1320 pStreamPA->pCfg = PDMAudioStrmCfgDup(pCfgAcq); 1321 if (!pStreamPA->pCfg) 1322 rc = VERR_NO_MEMORY; 1323 } 1324 1325 return rc; 1326 } 1327 1328 1329 /** 1330 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy} 1331 */ 1332 static DECLCALLBACK(int) drvHostPulseAudioHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream) 1333 { 1334 AssertPtrReturn(pInterface, VERR_INVALID_POINTER); 1335 AssertPtrReturn(pStream, VERR_INVALID_POINTER); 1336 1337 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface); 1338 PPULSEAUDIOSTREAM pStreamPA = (PPULSEAUDIOSTREAM)pStream; 1339 1340 if (!pStreamPA->pCfg) /* Not (yet) configured? Skip. */ 1341 return VINF_SUCCESS; 1342 1343 int rc; 1344 if (pStreamPA->pCfg->enmDir == PDMAUDIODIR_IN) 1345 rc = paDestroyStreamIn (pThis, pStreamPA); 1346 else if (pStreamPA->pCfg->enmDir == PDMAUDIODIR_OUT) 1347 rc = paDestroyStreamOut(pThis, pStreamPA); 1348 else 1349 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED); 1350 1351 if (RT_SUCCESS(rc)) 1352 { 1353 PDMAudioStrmCfgFree(pStreamPA->pCfg); 1354 pStreamPA->pCfg = NULL; 1355 } 1356 1357 return rc; 1358 } 1359 1360 1361 /** 1362 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl} 1363 */ 1364 static DECLCALLBACK(int) drvHostPulseAudioHA_StreamControl(PPDMIHOSTAUDIO pInterface, 1365 PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd) 1366 { 1367 AssertPtrReturn(pInterface, VERR_INVALID_POINTER); 1368 AssertPtrReturn(pStream, VERR_INVALID_POINTER); 1369 1370 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface); 1371 PPULSEAUDIOSTREAM pStreamPA = (PPULSEAUDIOSTREAM)pStream; 1372 1373 if (!pStreamPA->pCfg) /* Not (yet) configured? Skip. */ 1374 return VINF_SUCCESS; 1375 1376 int rc; 1377 if (pStreamPA->pCfg->enmDir == PDMAUDIODIR_IN) 1378 rc = paControlStreamIn (pThis, pStreamPA, enmStreamCmd); 1379 else if (pStreamPA->pCfg->enmDir == PDMAUDIODIR_OUT) 1380 rc = paControlStreamOut(pThis, pStreamPA, enmStreamCmd); 1381 else 1382 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED); 1383 1384 return rc; 1385 } 1386 1387 1388 static uint32_t paStreamGetAvail(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM pStreamPA) 1389 { 1390 pa_threaded_mainloop_lock(pThis->pMainLoop); 1391 1392 uint32_t cbAvail = 0; 1393 1394 if (PA_STREAM_IS_GOOD(pa_stream_get_state(pStreamPA->pStream))) 1395 { 1396 if (pStreamPA->pCfg->enmDir == PDMAUDIODIR_IN) 1397 { 1398 cbAvail = (uint32_t)pa_stream_readable_size(pStreamPA->pStream); 1399 Log3Func(("cbReadable=%RU32\n", cbAvail)); 1400 } 1401 else if (pStreamPA->pCfg->enmDir == PDMAUDIODIR_OUT) 1402 { 1403 size_t cbWritable = pa_stream_writable_size(pStreamPA->pStream); 1404 1405 Log3Func(("cbWritable=%zu, maxLength=%RU32, minReq=%RU32\n", 1406 cbWritable, pStreamPA->BufAttr.maxlength, pStreamPA->BufAttr.minreq)); 1407 1408 /* Don't report more writable than the PA server can handle. */ 1409 if (cbWritable > pStreamPA->BufAttr.maxlength) 1410 cbWritable = pStreamPA->BufAttr.maxlength; 1411 1412 cbAvail = (uint32_t)cbWritable; 1413 } 1414 else 1415 AssertFailed(); 1416 } 1417 1418 pa_threaded_mainloop_unlock(pThis->pMainLoop); 1419 1420 return cbAvail; 1421 } 1422 1423 1424 /** 1425 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable} 1426 */ 1427 static DECLCALLBACK(uint32_t) drvHostPulseAudioHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream) 1428 { 1429 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface); 1430 PPULSEAUDIOSTREAM pStreamPA = (PPULSEAUDIOSTREAM)pStream; 1431 1432 return paStreamGetAvail(pThis, pStreamPA); 1433 } 1434 1435 1436 /** 1437 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable} 1438 */ 1439 static DECLCALLBACK(uint32_t) drvHostPulseAudioHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream) 1440 { 1441 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface); 1442 PPULSEAUDIOSTREAM pStreamPA = (PPULSEAUDIOSTREAM)pStream; 1443 1444 return paStreamGetAvail(pThis, pStreamPA); 1445 } 1446 1447 1448 /** 1449 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus} 1450 */ 1451 static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvHostPulseAudioHA_StreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream) 1452 { 1453 AssertPtrReturn(pInterface, VERR_INVALID_POINTER); 1454 RT_NOREF(pStream); 1455 1456 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface); 1457 1458 PDMAUDIOSTREAMSTS fStrmSts = PDMAUDIOSTREAMSTS_FLAGS_NONE; 1459 1460 /* Check PulseAudio's general status. */ 1461 if ( pThis->pContext 1462 && PA_CONTEXT_IS_GOOD(pa_context_get_state(pThis->pContext))) 1463 fStrmSts = PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED | PDMAUDIOSTREAMSTS_FLAGS_ENABLED; 1464 1465 return fStrmSts; 1466 } 1467 1395 /********************************************************************************************************************************* 1396 * PDMIBASE * 1397 *********************************************************************************************************************************/ 1468 1398 1469 1399 /** 1470 1400 * @interface_method_impl{PDMIBASE,pfnQueryInterface} 1471 1401 */ 1472 static DECLCALLBACK(void *) drvHost PulseAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)1402 static DECLCALLBACK(void *) drvHostAudioPaQueryInterface(PPDMIBASE pInterface, const char *pszIID) 1473 1403 { 1474 1404 AssertPtrReturn(pInterface, NULL); … … 1484 1414 1485 1415 1416 /********************************************************************************************************************************* 1417 * PDMDRVREG * 1418 *********************************************************************************************************************************/ 1419 1486 1420 /** 1487 1421 * @interface_method_impl{PDMDRVREG,pfnPowerOff} 1488 1422 */ 1489 static DECLCALLBACK(void) drvHost PulseAudioPowerOff(PPDMDRVINS pDrvIns)1423 static DECLCALLBACK(void) drvHostAudioPaPowerOff(PPDMDRVINS pDrvIns) 1490 1424 { 1491 1425 PDRVHOSTPULSEAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPULSEAUDIO); … … 1517 1451 * @copydoc FNPDMDRVDESTRUCT 1518 1452 */ 1519 static DECLCALLBACK(void) drvHost PulseAudioDestruct(PPDMDRVINS pDrvIns)1453 static DECLCALLBACK(void) drvHostAudioPaDestruct(PPDMDRVINS pDrvIns) 1520 1454 { 1521 1455 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns); 1522 1456 LogFlowFuncEnter(); 1523 drvHost PulseAudioPowerOff(pDrvIns);1457 drvHostAudioPaPowerOff(pDrvIns); 1524 1458 LogFlowFuncLeave(); 1459 } 1460 1461 1462 /** 1463 * Pulse audio callback for context status changes, init variant. 1464 * 1465 * Signalls our event semaphore so we can do a timed wait from 1466 * drvHostAudioPaConstruct(). 1467 */ 1468 static void drvHostAudioPaCtxCallbackStateChangedInit(pa_context *pCtx, void *pvUser) 1469 { 1470 AssertPtrReturnVoid(pCtx); 1471 PPULSEAUDIOSTATECHGCTX pStateChgCtx = (PPULSEAUDIOSTATECHGCTX)pvUser; 1472 pa_context_state_t enmCtxState = pa_context_get_state(pCtx); 1473 switch (enmCtxState) 1474 { 1475 case PA_CONTEXT_READY: 1476 case PA_CONTEXT_TERMINATED: 1477 case PA_CONTEXT_FAILED: 1478 AssertPtrReturnVoid(pStateChgCtx); 1479 pStateChgCtx->enmCtxState = enmCtxState; 1480 RTSemEventSignal(pStateChgCtx->hEvtInit); 1481 break; 1482 1483 default: 1484 break; 1485 } 1525 1486 } 1526 1487 … … 1531 1492 * @copydoc FNPDMDRVCONSTRUCT 1532 1493 */ 1533 static DECLCALLBACK(int) drvHost PulseAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)1494 static DECLCALLBACK(int) drvHostAudioPaConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags) 1534 1495 { 1535 1496 RT_NOREF(pCfg, fFlags); … … 1545 1506 pThis->pDrvIns = pDrvIns; 1546 1507 /* IBase */ 1547 pDrvIns->IBase.pfnQueryInterface = drvHost PulseAudioQueryInterface;1508 pDrvIns->IBase.pfnQueryInterface = drvHostAudioPaQueryInterface; 1548 1509 /* IHostAudio */ 1549 pThis->IHostAudio.pfnGetConfig = drvHostPulseAudioHA_GetConfig; 1550 pThis->IHostAudio.pfnGetStatus = drvHostPulseAudioHA_GetStatus; 1551 pThis->IHostAudio.pfnStreamCreate = drvHostPulseAudioHA_StreamCreate; 1552 pThis->IHostAudio.pfnStreamDestroy = drvHostPulseAudioHA_StreamDestroy; 1553 pThis->IHostAudio.pfnStreamControl = drvHostPulseAudioHA_StreamControl; 1554 pThis->IHostAudio.pfnStreamGetReadable = drvHostPulseAudioHA_StreamGetReadable; 1555 pThis->IHostAudio.pfnStreamGetWritable = drvHostPulseAudioHA_StreamGetWritable; 1556 pThis->IHostAudio.pfnStreamGetStatus = drvHostPulseAudioHA_StreamGetStatus; 1557 pThis->IHostAudio.pfnStreamPlay = drvHostPulseAudioHA_StreamPlay; 1558 pThis->IHostAudio.pfnStreamCapture = drvHostPulseAudioHA_StreamCapture; 1510 pThis->IHostAudio.pfnGetConfig = drvHostAudioPaHA_GetConfig; 1559 1511 pThis->IHostAudio.pfnGetDevices = NULL; 1512 pThis->IHostAudio.pfnGetStatus = drvHostAudioPaHA_GetStatus; 1513 pThis->IHostAudio.pfnStreamCreate = drvHostAudioPaHA_StreamCreate; 1514 pThis->IHostAudio.pfnStreamDestroy = drvHostAudioPaHA_StreamDestroy; 1515 pThis->IHostAudio.pfnStreamControl = drvHostAudioPaHA_StreamControl; 1516 pThis->IHostAudio.pfnStreamGetReadable = drvHostAudioPaHA_StreamGetReadable; 1517 pThis->IHostAudio.pfnStreamGetWritable = drvHostAudioPaHA_StreamGetWritable; 1560 1518 pThis->IHostAudio.pfnStreamGetPending = NULL; 1519 pThis->IHostAudio.pfnStreamGetStatus = drvHostAudioPaHA_StreamGetStatus; 1520 pThis->IHostAudio.pfnStreamPlay = drvHostAudioPaHA_StreamPlay; 1521 pThis->IHostAudio.pfnStreamCapture = drvHostAudioPaHA_StreamCapture; 1561 1522 1562 1523 /* … … 1614 1575 1615 1576 pa_threaded_mainloop_lock(pThis->pMainLoop); 1616 pa_context_set_state_callback(pThis->pContext, paContextCbStateChangedInit, &pThis->InitStateChgCtx);1577 pa_context_set_state_callback(pThis->pContext, drvHostAudioPaCtxCallbackStateChangedInit, &pThis->InitStateChgCtx); 1617 1578 if (!pa_context_connect(pThis->pContext, NULL /* pszServer */, PA_CONTEXT_NOFLAGS, NULL)) 1618 1579 { … … 1626 1587 /* Install the main state changed callback to know if something happens to our acquired context. */ 1627 1588 pa_threaded_mainloop_lock(pThis->pMainLoop); 1628 pa_context_set_state_callback(pThis->pContext, paContextCbStateChanged, pThis /* pvUserData */);1589 pa_context_set_state_callback(pThis->pContext, drvHostAudioPaCtxCallbackStateChanged, pThis /* pvUserData */); 1629 1590 pa_threaded_mainloop_unlock(pThis->pMainLoop); 1630 1591 } … … 1679 1640 sizeof(DRVHOSTPULSEAUDIO), 1680 1641 /* pfnConstruct */ 1681 drvHost PulseAudioConstruct,1642 drvHostAudioPaConstruct, 1682 1643 /* pfnDestruct */ 1683 drvHost PulseAudioDestruct,1644 drvHostAudioPaDestruct, 1684 1645 /* pfnRelocate */ 1685 1646 NULL, … … 1699 1660 NULL, 1700 1661 /* pfnPowerOff */ 1701 drvHost PulseAudioPowerOff,1662 drvHostAudioPaPowerOff, 1702 1663 /* pfnSoftReset */ 1703 1664 NULL, … … 1706 1667 }; 1707 1668 1708 #if 0 /* unused */1709 static struct audio_option pulse_options[] =1710 {1711 {"DAC_MS", AUD_OPT_INT, &s_pulseCfg.buffer_msecs_out,1712 "DAC period size in milliseconds", NULL, 0},1713 {"ADC_MS", AUD_OPT_INT, &s_pulseCfg.buffer_msecs_in,1714 "ADC period size in milliseconds", NULL, 0}1715 };1716 #endif1717
Note:
See TracChangeset
for help on using the changeset viewer.