VirtualBox

Ignore:
Timestamp:
Jun 4, 2021 10:27:55 PM (4 years ago)
Author:
vboxsync
Message:

DrvHostAudioOss: Implemented drvHstAudOssHA_StreamGetReadable (was returning UINT32_MAX previously), switched input to blocking mode (wasn't able to capture anything on Linux otherwise). bugref:9890

File:
1 edited

Legend:

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

    r89510 r89516  
    2020*   Header Files                                                                                                                 *
    2121*********************************************************************************************************************************/
     22#define LOG_ENABLED 1
    2223#include <errno.h>
    2324#include <fcntl.h>
     
    298299                          RTErrConvertFromErrno(errno));
    299300
    300 
    301     /*
    302      * Set obsolete non-blocking call for input streams.
    303      */
    304     if (fInput)
    305     {
    306 #if !(defined(VBOX) && defined(RT_OS_SOLARIS)) /* Obsolete on Solaris (using O_NONBLOCK is sufficient). */
    307         AssertLogRelMsgReturn(ioctl(hFile, SNDCTL_DSP_NONBLOCK, NULL) >= 0,
    308                               ("OSS: Failed to set non-blocking mode: %s (%d)\n", strerror(errno), errno),
    309                               RTErrConvertFromErrno(errno));
    310 #endif
    311     }
    312 
    313301    /*
    314302     * Set fragment size and count.
     
    367355    int rc;
    368356    if (pCfgReq->enmDir == PDMAUDIODIR_IN)
    369         pStreamOSS->hFile = open(g_szPathInputDev, O_RDONLY | O_NONBLOCK);
     357        pStreamOSS->hFile = open(g_szPathInputDev, O_RDONLY);
    370358    else
    371359        pStreamOSS->hFile = open(g_szPathOutputDev, O_WRONLY);
     
    463451    RT_NOREF(pInterface);
    464452    PDRVHSTAUDOSSSTREAM pStreamOSS = (PDRVHSTAUDOSSSTREAM)pStream;
    465 
    466     /** @todo this might be a little optimisitic...   */
    467     pStreamOSS->fDraining = false;
    468 
    469453    int rc;
    470     if (pStreamOSS->Cfg.enmDir == PDMAUDIODIR_IN)
    471         rc = VINF_SUCCESS; /** @todo apparently nothing to do here? */
     454
     455    /*
     456     * This is most probably untested...
     457     */
     458    if (pStreamOSS->fDraining)
     459    {
     460        LogFlowFunc(("Still draining...\n"));
     461        rc = RTThreadWait(pStreamOSS->hThreadDrain, 0 /*ms*/, NULL);
     462        if (RT_FAILURE(rc))
     463        {
     464            LogFlowFunc(("Resetting...\n"));
     465            ioctl(pStreamOSS->hFile, SNDCTL_DSP_RESET, NULL);
     466            rc = RTThreadWait(pStreamOSS->hThreadDrain, 0 /*ms*/, NULL);
     467            if (RT_FAILURE(rc))
     468            {
     469                LogFlowFunc(("Poking...\n"));
     470                RTThreadPoke(pStreamOSS->hThreadDrain);
     471                rc = RTThreadWait(pStreamOSS->hThreadDrain, 1 /*ms*/, NULL);
     472            }
     473        }
     474        if (RT_SUCCESS(rc))
     475        {
     476            LogFlowFunc(("Done draining.\n"));
     477            pStreamOSS->hThreadDrain = NIL_RTTHREAD;
     478        }
     479        else
     480            LogFlowFunc(("No, still draining...\n"));
     481        pStreamOSS->fDraining = false;
     482    }
     483
     484    /*
     485     * Enable the stream.
     486     */
     487    int fMask = pStreamOSS->Cfg.enmDir == PDMAUDIODIR_IN ? PCM_ENABLE_INPUT : PCM_ENABLE_OUTPUT;
     488    if (ioctl(pStreamOSS->hFile, SNDCTL_DSP_SETTRIGGER, &fMask) >= 0)
     489        rc = VINF_SUCCESS;
    472490    else
    473491    {
    474         int fMask = PCM_ENABLE_OUTPUT;
    475         if (ioctl(pStreamOSS->hFile, SNDCTL_DSP_SETTRIGGER, &fMask) >= 0)
    476             rc = VINF_SUCCESS;
    477         else
    478         {
    479             LogRel(("OSS: Failed to enable output stream: %s (%d)\n", strerror(errno), errno));
    480             rc = RTErrConvertFromErrno(errno);
    481         }
     492        LogRel(("OSS: Failed to enable output stream: %s (%d)\n", strerror(errno), errno));
     493        rc = RTErrConvertFromErrno(errno);
    482494    }
    483495
     
    494506    RT_NOREF(pInterface);
    495507    PDRVHSTAUDOSSSTREAM pStreamOSS = (PDRVHSTAUDOSSSTREAM)pStream;
    496 
     508    LogFlowFunc(("Stream '%s'\n", pStreamOSS->Cfg.szName));
    497509    int rc;
    498     if (pStreamOSS->Cfg.enmDir == PDMAUDIODIR_IN)
    499         rc = VINF_SUCCESS; /** @todo apparently nothing to do here? */
     510
     511    /*
     512     * If we're still draining, try kick the thread before we try disable the stream.
     513     */
     514    if (pStreamOSS->fDraining)
     515    {
     516        LogFlowFunc(("Trying to cancel draining...\n"));
     517        if (pStreamOSS->hThreadDrain != NIL_RTTHREAD)
     518        {
     519            RTThreadPoke(pStreamOSS->hThreadDrain);
     520            rc = RTThreadWait(pStreamOSS->hThreadDrain, 1 /*ms*/, NULL);
     521            if (RT_SUCCESS(rc) || rc == VERR_INVALID_HANDLE)
     522                pStreamOSS->fDraining = false;
     523            else
     524                LogFunc(("Failed to cancel draining (%Rrc)\n", rc));
     525        }
     526        else
     527        {
     528            LogFlowFunc(("Thread handle is NIL, so we can't be draining\n"));
     529            pStreamOSS->fDraining = false;
     530        }
     531    }
     532
     533    /*
     534     * The Official documentation says this isn't the right way to stop
     535     * playback.  It may work in some implementations but fail in all others...
     536     * Suggest SNDCTL_DSP_RESET / SNDCTL_DSP_HALT.
     537     *
     538     * So, let's do both and see how that works out...
     539     */
     540    rc = VINF_SUCCESS;
     541    int fMask = 0;
     542    if (ioctl(pStreamOSS->hFile, SNDCTL_DSP_SETTRIGGER, &fMask) >= 0)
     543        LogFlowFunc(("SNDCTL_DSP_SETTRIGGER succeeded\n"));
    500544    else
    501545    {
    502         /*
    503          * If we're still draining, try kick the thread before we try disable the stream.
    504          */
    505         if (pStreamOSS->fDraining)
    506         {
    507             LogFlowFunc(("Trying to cancel draining...\n"));
    508             if (pStreamOSS->hThreadDrain != NIL_RTTHREAD)
    509             {
    510                 RTThreadPoke(pStreamOSS->hThreadDrain);
    511                 rc = RTThreadWait(pStreamOSS->hThreadDrain, 1 /*ms*/, NULL);
    512                 if (RT_SUCCESS(rc) || rc == VERR_INVALID_HANDLE)
    513                     pStreamOSS->fDraining = false;
    514                 else
    515                     LogFunc(("Failed to cancel draining (%Rrc)\n", rc));
    516             }
    517             else
    518             {
    519                 LogFlowFunc(("Thread handle is NIL, so we can't be draining\n"));
    520                 pStreamOSS->fDraining = false;
    521             }
    522         }
    523 
    524         /** @todo Official documentation says this isn't the right way to stop playback.
    525          *        It may work in some implementations but fail in all others...  Suggest
    526          *        using SNDCTL_DSP_RESET / SNDCTL_DSP_HALT. */
    527         int fMask = 0;
    528         if (ioctl(pStreamOSS->hFile, SNDCTL_DSP_SETTRIGGER, &fMask) >= 0)
    529             rc = VINF_SUCCESS;
    530         else
    531         {
    532             LogRel(("OSS: Failed to enable output stream: %s (%d)\n", strerror(errno), errno));
    533             rc = RTErrConvertFromErrno(errno);
    534         }
    535     }
     546        LogRel(("OSS: Failed to clear triggers for stream '%s': %s (%d)\n", pStreamOSS->Cfg.szName, strerror(errno), errno));
     547        rc = RTErrConvertFromErrno(errno);
     548    }
     549
     550    if (ioctl(pStreamOSS->hFile, SNDCTL_DSP_RESET, NULL) >= 0)
     551        LogFlowFunc(("SNDCTL_DSP_RESET succeeded\n"));
     552    else
     553    {
     554        LogRel(("OSS: Failed to reset stream '%s': %s (%d)\n", pStreamOSS->Cfg.szName, strerror(errno), errno));
     555        rc = RTErrConvertFromErrno(errno);
     556    }
     557
    536558    LogFlowFunc(("returns %Rrc for '%s'\n", rc, pStreamOSS->Cfg.szName));
    537559    return rc;
     
    571593    LogFunc(("F_GETFL -> %#x\n", fOrgFlags));
    572594    Assert(fOrgFlags != -1);
    573     if (fOrgFlags != -1)
     595    if (fOrgFlags != -1 && (fOrgFlags & O_NONBLOCK))
    574596    {
    575597        rc = fcntl(pStreamOSS->hFile, F_SETFL, fOrgFlags & ~O_NONBLOCK);
     
    782804static DECLCALLBACK(uint32_t) drvHstAudOssHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
    783805{
    784     RT_NOREF(pInterface, pStream);
    785     Log4Func(("returns UINT32_MAX\n"));
    786     return UINT32_MAX;
     806    RT_NOREF(pInterface);
     807    PDRVHSTAUDOSSSTREAM pStreamOSS = (PDRVHSTAUDOSSSTREAM)pStream;
     808    AssertPtr(pStreamOSS);
     809
     810    /*
     811     * Use SNDCTL_DSP_GETISPACE to see how much we can read.
     812     *
     813     * Note! We use bytes rather than the fragments * fragsize, as these are
     814     *       documented as obsolete.  (Playback code should do the same.)
     815     */
     816    audio_buf_info BufInfo = { 0, 0, 0, 0 };
     817    int rc2 = ioctl(pStreamOSS->hFile, SNDCTL_DSP_GETISPACE, &BufInfo);
     818    AssertMsgReturn(rc2 >= 0, ("SNDCTL_DSP_GETISPACE failed: %s (%d)\n", strerror(errno), errno), 0);
     819
     820    uint32_t        cbRet;
     821    uint32_t const  cbBuf = pStreamOSS->OssCfg.cbFragment * pStreamOSS->OssCfg.cFragments;
     822    if (BufInfo.bytes >= 0 && (unsigned)BufInfo.bytes <= cbBuf)
     823        cbRet = BufInfo.bytes;
     824    else
     825    {
     826        AssertMsgFailed(("Invalid available size: %d\n", BufInfo.bytes));
     827        AssertMsgReturn(BufInfo.fragments >= 0, ("fragments: %d\n", BufInfo.fragments), 0);
     828        AssertMsgReturn(BufInfo.fragsize >= 0, ("fragsize: %d\n", BufInfo.fragsize), 0);
     829        cbRet = (uint32_t)(BufInfo.fragments * BufInfo.fragsize);
     830        AssertMsgStmt(cbRet <= cbBuf, ("fragsize*fragments: %d, cbBuf=%#x\n", cbRet, cbBuf), 0);
     831    }
     832
     833    /*
     834     * HACK ALERT! Tweak to force recording to start.  Pretend there are bytes
     835     *             available if we haven't read anything yet.  This will cause
     836     *             the following StreamCapture call to block and make sure the
     837     *             stream is really recording.
     838     */
     839    if (BufInfo.bytes > 0 || pStreamOSS->offInternal != 0)
     840    { /* likely */ }
     841    else
     842        cbRet = PDMAudioPropsFramesToBytes(&pStreamOSS->Cfg.Props, 1);
     843
     844    Log4Func(("returns %#x (%u) [cbBuf=%#x)\n", cbRet, cbRet, cbBuf));
     845    return cbRet;
    787846}
    788847
     
    805864    {
    806865        ssize_t cbRead = read(pStreamOSS->hFile, &pbDst[offWrite], cbToRead);
    807         if (cbRead)
     866        if (cbRead > 0)
    808867        {
    809868            LogFlowFunc(("cbRead=%zi, offWrite=%zu cbToRead=%zu\n", cbRead, offWrite, cbToRead));
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