Changeset 88448 in vbox
- Timestamp:
- Apr 9, 2021 7:52:39 PM (4 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Devices/Audio/DrvHostAudioAlsa.cpp
r88442 r88448 85 85 /** Pointer to allocated ALSA PCM configuration to use. */ 86 86 snd_pcm_t *phPCM; 87 /** Scratch buffer.88 * @todo r=bird: WHY THE *BEEEEP* DO WE NEED THIS? Do I have to go search svn89 * history for this (probably just an 'updates' commit)? */90 void *pvBuf;91 /** Size (in bytes) of allocated scratch buffer. */92 size_t cbBuf;93 87 /** Internal stream offset (for debugging). */ 94 88 uint64_t offInternal; … … 495 489 * 496 490 * @returns VBox status code. 497 * @param phPCM 498 * @param p FramesAvailWhere to store the available frames.499 */ 500 static int alsaStreamGetAvail(snd_pcm_t *phPCM, snd_pcm_sframes_t *p FramesAvail)501 { 502 AssertPtr Return(phPCM, VERR_INVALID_POINTER);503 /* pFramesAvail is optional. */491 * @param phPCM ALSA stream handle. 492 * @param pcFramesAvail Where to store the available frames. 493 */ 494 static int alsaStreamGetAvail(snd_pcm_t *phPCM, snd_pcm_sframes_t *pcFramesAvail) 495 { 496 AssertPtr(phPCM); 497 AssertPtr(pcFramesAvail); 504 498 505 499 int rc; 506 507 snd_pcm_sframes_t framesAvail = snd_pcm_avail_update(phPCM); 508 if (framesAvail < 0) 509 { 510 if (framesAvail == -EPIPE) 511 { 512 rc = alsaStreamRecover(phPCM); 513 if (RT_SUCCESS(rc)) 514 framesAvail = snd_pcm_avail_update(phPCM); 500 snd_pcm_sframes_t cFramesAvail = snd_pcm_avail_update(phPCM); 501 if (cFramesAvail > 0) 502 { 503 LogFunc(("cFramesAvail=%ld\n", cFramesAvail)); 504 *pcFramesAvail = cFramesAvail; 505 return VINF_SUCCESS; 506 } 507 508 if (cFramesAvail == -EPIPE) 509 { 510 rc = alsaStreamRecover(phPCM); 511 if (RT_SUCCESS(rc)) 512 { 513 cFramesAvail = snd_pcm_avail_update(phPCM); 514 if (cFramesAvail >= 0) 515 { 516 LogFunc(("cFramesAvail=%ld\n", cFramesAvail)); 517 *pcFramesAvail = cFramesAvail; 518 return VINF_SUCCESS; 519 } 515 520 } 516 521 else 517 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */ 518 } 519 else 520 rc = VINF_SUCCESS; 521 522 if (RT_SUCCESS(rc)) 523 { 524 if (pFramesAvail) 525 *pFramesAvail = framesAvail; 526 } 527 528 LogFunc(("cFrames=%ld, rc=%Rrc\n", framesAvail, rc)); 522 { 523 *pcFramesAvail = 0; 524 return rc; 525 } 526 } 527 528 rc = RTErrConvertFromErrno(-cFramesAvail); 529 LogFunc(("failed - cFramesAvail=%ld rc=%Rrc\n", cFramesAvail, rc)); 530 *pcFramesAvail = 0; 529 531 return rc; 530 532 } … … 574 576 */ 575 577 static DECLCALLBACK(int) drvHostAlsaAudioHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream, 576 void *pvBuf, uint32_t uBufSize, uint32_t *puRead) 577 { 578 AssertPtrReturn(pInterface, VERR_INVALID_POINTER); 579 AssertPtrReturn(pStream, VERR_INVALID_POINTER); 580 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); 581 AssertReturn(uBufSize, VERR_INVALID_PARAMETER); 582 /* pcbRead is optional. */ 583 578 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead) 579 { 580 RT_NOREF_PV(pInterface); 584 581 PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream; 585 582 AssertPtrReturn(pStreamALSA, VERR_INVALID_POINTER); 583 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); 584 AssertReturn(cbBuf, VERR_INVALID_PARAMETER); 585 AssertPtrReturn(pcbRead, VERR_INVALID_POINTER); 586 PPDMAUDIOSTREAMCFG pCfg = pStreamALSA->pCfg; 587 AssertPtr(pCfg); 588 589 /* 590 * Figure out how much we can read without trouble (we're doing 591 * non-blocking reads, but whatever). 592 */ 586 593 snd_pcm_sframes_t cAvail; 587 594 int rc = alsaStreamGetAvail(pStreamALSA->phPCM, &cAvail); 588 if (RT_FAILURE(rc)) 595 if (RT_SUCCESS(rc)) 596 { 597 if (!cAvail) /* No data yet? */ 598 { 599 snd_pcm_state_t enmState = snd_pcm_state(pStreamALSA->phPCM); 600 switch (enmState) 601 { 602 case SND_PCM_STATE_PREPARED: 603 /** @todo r=bird: explain the logic here... */ 604 cAvail = PDMAudioPropsBytesToFrames(&pCfg->Props, cbBuf); 605 break; 606 607 case SND_PCM_STATE_SUSPENDED: 608 rc = alsaStreamResume(pStreamALSA->phPCM); 609 if (RT_SUCCESS(rc)) 610 { 611 LogFlowFunc(("Resumed suspended input stream.\n")); 612 break; 613 } 614 LogFunc(("Failed resuming suspended input stream: %Rrc\n", rc)); 615 return rc; 616 617 default: 618 LogFlow(("No frames available: state=%s (%d)\n", snd_pcm_state_name(enmState), enmState)); 619 break; 620 } 621 if (!cAvail) 622 { 623 *pcbRead = 0; 624 return VINF_SUCCESS; 625 } 626 } 627 } 628 else 589 629 { 590 630 LogFunc(("Error getting number of captured frames, rc=%Rrc\n", rc)); … … 592 632 } 593 633 594 PPDMAUDIOSTREAMCFG pCfg = pStreamALSA->pCfg; 595 AssertPtr(pCfg); 596 597 if (!cAvail) /* No data yet? */ 598 { 599 snd_pcm_state_t state = snd_pcm_state(pStreamALSA->phPCM); 600 switch (state) 601 { 602 case SND_PCM_STATE_PREPARED: 603 cAvail = PDMAUDIOSTREAMCFG_B2F(pCfg, uBufSize); 604 break; 605 606 case SND_PCM_STATE_SUSPENDED: 607 { 608 rc = alsaStreamResume(pStreamALSA->phPCM); 609 if (RT_FAILURE(rc)) 610 break; 611 612 LogFlow(("Resuming suspended input stream\n")); 613 break; 614 } 615 616 default: 617 LogFlow(("No frames available, state=%d\n", state)); 618 break; 619 } 620 621 if (!cAvail) 622 { 623 if (puRead) 624 *puRead = 0; 625 return VINF_SUCCESS; 626 } 627 } 634 size_t cbToRead = PDMAudioPropsFramesToBytes(&pCfg->Props, cAvail); 635 cbToRead = RT_MIN(cbToRead, cbBuf); 636 LogFlowFunc(("cbToRead=%zu, cAvail=%RI32\n", cbToRead, cAvail)); 628 637 629 638 /* 630 * Check how much we can read from the capture device without overflowing 631 * the mixer buffer. 639 * Read loop. 632 640 */ 633 size_t cbToRead = RT_MIN((size_t)PDMAUDIOSTREAMCFG_F2B(pCfg, cAvail), uBufSize);634 635 LogFlowFunc(("cbToRead=%zu, cAvail=%RI32\n", cbToRead, cAvail));636 637 641 uint32_t cbReadTotal = 0; 638 639 snd_pcm_uframes_t cToRead; 640 snd_pcm_sframes_t cRead; 641 642 while ( cbToRead 643 && RT_SUCCESS(rc)) 644 { 645 cToRead = RT_MIN(PDMAUDIOSTREAMCFG_B2F(pCfg, cbToRead), 646 PDMAUDIOSTREAMCFG_B2F(pCfg, pStreamALSA->cbBuf)); 647 AssertBreakStmt(cToRead, rc = VERR_NO_DATA); 648 cRead = snd_pcm_readi(pStreamALSA->phPCM, pStreamALSA->pvBuf, cToRead); 649 if (cRead <= 0) 650 { 651 switch (cRead) 652 { 653 case 0: 654 { 655 LogFunc(("No input frames available\n")); 656 rc = VERR_ACCESS_DENIED; 657 break; 658 } 659 660 case -EAGAIN: 661 { 662 /* 663 * Don't set error here because EAGAIN means there are no further frames 664 * available at the moment, try later. As we might have read some frames 665 * already these need to be processed instead. 666 */ 667 cbToRead = 0; 668 break; 669 } 670 671 case -EPIPE: 672 { 673 rc = alsaStreamRecover(pStreamALSA->phPCM); 674 if (RT_FAILURE(rc)) 675 break; 676 677 LogFlowFunc(("Recovered from capturing\n")); 678 continue; 679 } 680 681 default: 682 { 683 LogFunc(("Failed to read input frames: %s\n", snd_strerror(cRead))); 684 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */ 685 break; 686 } 687 } 688 } 689 else 642 while (cbToRead > 0) 643 { 644 /* 645 * Do the reading. 646 */ 647 snd_pcm_uframes_t const cFramesToRead = PDMAudioPropsBytesToFrames(&pCfg->Props, cbToRead); 648 AssertBreakStmt(cFramesToRead > 0, rc = VERR_NO_DATA); 649 650 snd_pcm_sframes_t cFramesRead = snd_pcm_readi(pStreamALSA->phPCM, pvBuf, cFramesToRead); 651 if (cFramesRead > 0) 690 652 { 691 653 /* 692 * We should not run into a full mixer buffer or we lo ose samples and654 * We should not run into a full mixer buffer or we lose samples and 693 655 * run into an endless loop if ALSA keeps producing samples ("null" 694 656 * capture device for example). 695 657 */ 696 uint32_t cbRead = PDMAUDIOSTREAMCFG_F2B(pCfg, cRead); 697 698 memcpy(pvBuf, pStreamALSA->pvBuf, cbRead); 699 700 Assert(cbToRead >= cbRead); 658 uint32_t const cbRead = PDMAudioPropsFramesToBytes(&pCfg->Props, cFramesRead); 659 Assert(cbRead <= cbToRead); 660 701 661 cbToRead -= cbRead; 702 662 cbReadTotal += cbRead; 703 } 704 } 705 706 if (RT_SUCCESS(rc)) 707 { 708 if (puRead) 709 *puRead = cbReadTotal; 710 } 711 663 pvBuf = (uint8_t *)pvBuf + cbRead; 664 } 665 else 666 { 667 /* 668 * Try recover from overrun and re-try. 669 * Other conditions/errors we cannot and will just quit the loop. 670 */ 671 if (cFramesRead == -EPIPE) 672 { 673 rc = alsaStreamRecover(pStreamALSA->phPCM); 674 if (RT_SUCCESS(rc)) 675 { 676 LogFlowFunc(("Successfully recovered from overrun\n")); 677 continue; 678 } 679 LogFunc(("Failed to recover from overrun: %Rrc\n", rc)); 680 } 681 else if (cFramesRead == -EAGAIN) 682 LogFunc(("No input frames available (EAGAIN)\n")); 683 else if (cFramesRead == 0) 684 LogFunc(("No input frames available (0)\n")); 685 else 686 { 687 rc = RTErrConvertFromErrno(-(int)cFramesRead); 688 LogFunc(("Failed to read input frames: %s (%ld, %Rrc)\n", snd_strerror(cFramesRead), cFramesRead, rc)); 689 } 690 691 /* If we've read anything, suppress the error. */ 692 if (RT_FAILURE(rc) && cbReadTotal > 0) 693 { 694 LogFunc(("Suppressing %Rrc because %#x bytes has been read already\n", rc, cbReadTotal)); 695 rc = VINF_SUCCESS; 696 } 697 break; 698 } 699 } 700 701 LogFlowFunc(("returns %Rrc and %#x (%d) bytes (%u bytes left)\n", rc, cbReadTotal, cbReadTotal, cbToRead)); 702 pStreamALSA->offInternal += cbReadTotal; 703 *pcbRead = cbReadTotal; 712 704 return rc; 713 705 } … … 741 733 PCPDMAUDIOPCMPROPS pProps = &pStreamALSA->pCfg->Props; 742 734 uint32_t cbToWrite = PDMAudioPropsFramesToBytes(pProps, (uint32_t)cFramesAvail); 743 cbToWrite = RT_MIN(cbToWrite, (uint32_t)pStreamALSA->cbBuf);744 735 if (cbToWrite) 745 736 { 746 737 if (cbToWrite > cbBuf) 747 738 cbToWrite = cbBuf; 748 749 /* Now we copy the stuff into our scratch buffer for some totally unexplained reason. */750 memcpy(pStreamALSA->pvBuf, pvBuf, cbToWrite);751 739 752 740 /* … … 754 742 */ 755 743 uint32_t cFramesToWrite = PDMAudioPropsBytesToFrames(pProps, cbToWrite); 756 snd_pcm_sframes_t cFramesWritten = snd_pcm_writei(pStreamALSA->phPCM, p StreamALSA->pvBuf, cFramesToWrite);744 snd_pcm_sframes_t cFramesWritten = snd_pcm_writei(pStreamALSA->phPCM, pvBuf, cFramesToWrite); 757 745 if (cFramesWritten > 0) 758 746 { … … 794 782 } 795 783 796 cFramesWritten = snd_pcm_writei(pStreamALSA->phPCM, p StreamALSA->pvBuf, cFramesToWrite);784 cFramesWritten = snd_pcm_writei(pStreamALSA->phPCM, pvBuf, cFramesToWrite); 797 785 if (cFramesWritten > 0) 798 786 { … … 827 815 828 816 /** 829 * Destroys an ALSA input stream.830 *831 * @returns VBox status code.832 * @param pStreamALSA ALSA input stream to destroy.833 */834 static int alsaDestroyStreamIn(PALSAAUDIOSTREAM pStreamALSA)835 {836 alsaStreamClose(&pStreamALSA->phPCM);837 838 if (pStreamALSA->pvBuf)839 {840 RTMemFree(pStreamALSA->pvBuf);841 pStreamALSA->pvBuf = NULL;842 }843 844 return VINF_SUCCESS;845 }846 847 /**848 * Destroys an ALSA output stream.849 *850 * @returns VBox status code.851 * @param pStreamALSA ALSA output stream to destroy.852 */853 static int alsaDestroyStreamOut(PALSAAUDIOSTREAM pStreamALSA)854 {855 alsaStreamClose(&pStreamALSA->phPCM);856 857 if (pStreamALSA->pvBuf)858 {859 RTMemFree(pStreamALSA->pvBuf);860 pStreamALSA->pvBuf = NULL;861 }862 863 return VINF_SUCCESS;864 }865 866 /**867 817 * Creates an ALSA output stream. 868 818 * … … 904 854 pCfgAcq->Backend.cFramesPreBuffering = obt.threshold; 905 855 906 pStreamALSA->cbBuf = pCfgAcq->Backend.cFramesBufferSize * PDMAudioPropsBytesPerFrame(&pCfgAcq->Props);907 pStreamALSA->pvBuf = RTMemAllocZ(pStreamALSA->cbBuf);908 if (!pStreamALSA->pvBuf)909 {910 LogRel(("ALSA: Not enough memory for output DAC buffer (%zu frames)\n", pCfgAcq->Backend.cFramesBufferSize));911 rc = VERR_NO_MEMORY;912 break;913 }914 915 856 pStreamALSA->phPCM = phPCM; 916 857 } … … 963 904 pCfgAcq->Backend.cFramesBufferSize = obt.buffer_size; 964 905 pCfgAcq->Backend.cFramesPreBuffering = 0; /* No pre-buffering. */ 965 966 pStreamALSA->cbBuf = pCfgAcq->Backend.cFramesBufferSize * PDMAudioPropsBytesPerFrame(&pCfgAcq->Props);967 pStreamALSA->pvBuf = RTMemAlloc(pStreamALSA->cbBuf);968 if (!pStreamALSA->pvBuf)969 {970 LogRel(("ALSA: Not enough memory for input ADC buffer (%zu frames)\n", pCfgAcq->Backend.cFramesBufferSize));971 rc = VERR_NO_MEMORY;972 break;973 }974 906 975 907 pStreamALSA->phPCM = phPCM; … … 1287 1219 return VINF_SUCCESS; 1288 1220 1289 int rc; 1290 if (pStreamALSA->pCfg->enmDir == PDMAUDIODIR_IN) 1291 rc = alsaDestroyStreamIn(pStreamALSA); 1292 else 1293 rc = alsaDestroyStreamOut(pStreamALSA); 1294 1221 int rc = alsaStreamClose(&pStreamALSA->phPCM); 1222 /** @todo r=bird: It's not like we can do much with a bad status... Check 1223 * what the caller does... */ 1295 1224 if (RT_SUCCESS(rc)) 1296 1225 {
Note:
See TracChangeset
for help on using the changeset viewer.