VirtualBox

Changeset 88448 in vbox


Ignore:
Timestamp:
Apr 9, 2021 7:52:39 PM (4 years ago)
Author:
vboxsync
Message:

DrvHostAudioAlsa: Kicked out the scratch buffer as I could find no reason for having it, other than limiting write sizes. Straightened out the capture code. bugref:9890

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Devices/Audio/DrvHostAudioAlsa.cpp

    r88442 r88448  
    8585    /** Pointer to allocated ALSA PCM configuration to use. */
    8686    snd_pcm_t          *phPCM;
    87     /** Scratch buffer.
    88      * @todo r=bird: WHY THE *BEEEEP* DO WE NEED THIS? Do I have to go search svn
    89      *       history for this (probably just an 'updates' commit)? */
    90     void               *pvBuf;
    91     /** Size (in bytes) of allocated scratch buffer. */
    92     size_t              cbBuf;
    9387    /** Internal stream offset (for debugging). */
    9488    uint64_t            offInternal;
     
    495489 *
    496490 * @returns VBox status code.
    497  * @param   phPCM               ALSA stream handle.
    498  * @param   pFramesAvail        Where to store the available frames.
    499  */
    500 static int alsaStreamGetAvail(snd_pcm_t *phPCM, snd_pcm_sframes_t *pFramesAvail)
    501 {
    502     AssertPtrReturn(phPCM, VERR_INVALID_POINTER);
    503     /* pFramesAvail is optional. */
     491 * @param   phPCM           ALSA stream handle.
     492 * @param   pcFramesAvail   Where to store the available frames.
     493 */
     494static int alsaStreamGetAvail(snd_pcm_t *phPCM, snd_pcm_sframes_t *pcFramesAvail)
     495{
     496    AssertPtr(phPCM);
     497    AssertPtr(pcFramesAvail);
    504498
    505499    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            }
    515520        }
    516521        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;
    529531    return rc;
    530532}
     
    574576 */
    575577static 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);
    584581    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     */
    586593    snd_pcm_sframes_t cAvail;
    587594    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
    589629    {
    590630        LogFunc(("Error getting number of captured frames, rc=%Rrc\n", rc));
     
    592632    }
    593633
    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));
    628637
    629638    /*
    630      * Check how much we can read from the capture device without overflowing
    631      * the mixer buffer.
     639     * Read loop.
    632640     */
    633     size_t cbToRead = RT_MIN((size_t)PDMAUDIOSTREAMCFG_F2B(pCfg, cAvail), uBufSize);
    634 
    635     LogFlowFunc(("cbToRead=%zu, cAvail=%RI32\n", cbToRead, cAvail));
    636 
    637641    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)
    690652        {
    691653            /*
    692              * We should not run into a full mixer buffer or we loose samples and
     654             * We should not run into a full mixer buffer or we lose samples and
    693655             * run into an endless loop if ALSA keeps producing samples ("null"
    694656             * capture device for example).
    695657             */
    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
    701661            cbToRead    -= cbRead;
    702662            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;
    712704    return rc;
    713705}
     
    741733            PCPDMAUDIOPCMPROPS pProps    = &pStreamALSA->pCfg->Props;
    742734            uint32_t           cbToWrite = PDMAudioPropsFramesToBytes(pProps, (uint32_t)cFramesAvail);
    743             cbToWrite = RT_MIN(cbToWrite, (uint32_t)pStreamALSA->cbBuf);
    744735            if (cbToWrite)
    745736            {
    746737                if (cbToWrite > cbBuf)
    747738                    cbToWrite = cbBuf;
    748 
    749                 /* Now we copy the stuff into our scratch buffer for some totally unexplained reason. */
    750                 memcpy(pStreamALSA->pvBuf, pvBuf, cbToWrite);
    751739
    752740                /*
     
    754742                 */
    755743                uint32_t cFramesToWrite = PDMAudioPropsBytesToFrames(pProps, cbToWrite);
    756                 snd_pcm_sframes_t cFramesWritten = snd_pcm_writei(pStreamALSA->phPCM, pStreamALSA->pvBuf, cFramesToWrite);
     744                snd_pcm_sframes_t cFramesWritten = snd_pcm_writei(pStreamALSA->phPCM, pvBuf, cFramesToWrite);
    757745                if (cFramesWritten > 0)
    758746                {
     
    794782                    }
    795783
    796                     cFramesWritten = snd_pcm_writei(pStreamALSA->phPCM, pStreamALSA->pvBuf, cFramesToWrite);
     784                    cFramesWritten = snd_pcm_writei(pStreamALSA->phPCM, pvBuf, cFramesToWrite);
    797785                    if (cFramesWritten > 0)
    798786                    {
     
    827815
    828816/**
    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 /**
    867817 * Creates an ALSA output stream.
    868818 *
     
    904854        pCfgAcq->Backend.cFramesPreBuffering     = obt.threshold;
    905855
    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 
    915856        pStreamALSA->phPCM = phPCM;
    916857    }
     
    963904        pCfgAcq->Backend.cFramesBufferSize = obt.buffer_size;
    964905        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         }
    974906
    975907        pStreamALSA->phPCM = phPCM;
     
    12871219        return VINF_SUCCESS;
    12881220
    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... */
    12951224    if (RT_SUCCESS(rc))
    12961225    {
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette