VirtualBox

Changeset 89329 in vbox for trunk/src/VBox/Devices/Audio


Ignore:
Timestamp:
May 28, 2021 12:26:17 AM (4 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
144678
Message:

Audio: Reworking the capture (recording) code path, part 4: Combine PDMIAUDIOCONNECTOR::pfnStreamCapture and PDMIAUDIOCONNECTOR::pfnStreamRead, remove PDMIAUDIOCONNECTOR::pfnStreamSetVoplume, eliminate mixer buffers in DrvAudio. Added pre-buffering of input streams (delay fetching samples from the backend till we've reached the desired buffer fill there). [build fix + cleanup] bugref:9890

File:
1 edited

Legend:

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

    r89327 r89329  
    511511}
    512512
    513 
     513#ifdef LOG_ENABLED
    514514/**
    515515 * Get capture state name string.
     
    528528    return "BAD";
    529529}
    530 
     530#endif
    531531
    532532/**
     
    38623862
    38633863
    3864 #ifdef OLD_CAPTURE_CODE
    3865 /**
    3866  * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamRead}
    3867  */
    3868 static DECLCALLBACK(int) drvAudioStreamRead(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream,
    3869                                             void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
    3870 {
    3871     PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
    3872     AssertPtr(pThis);
    3873     PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream;
    3874     AssertPtrReturn(pStreamEx, VERR_INVALID_POINTER);
    3875     AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
    3876     AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
    3877     AssertPtrNullReturn(pcbRead, VERR_INVALID_POINTER);
    3878     AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
    3879     AssertReturn(pStreamEx->uMagic      == DRVAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
    3880     AssertMsg(pStreamEx->Core.enmDir == PDMAUDIODIR_IN,
    3881               ("Stream '%s' is not an input stream and therefore cannot be read from (direction is 0x%x)\n",
    3882                pStreamEx->Core.szName, pStreamEx->Core.enmDir));
    3883 
    3884     int rc = RTCritSectEnter(&pStreamEx->Core.CritSect);
    3885     AssertRCReturn(rc, rc);
    3886 
    3887     /*
    3888      * ...
    3889      */
    3890     uint32_t cbReadTotal = 0;
    3891 
    3892     do
    3893     {
    3894         uint32_t cfReadTotal = 0;
    3895 
    3896         const uint32_t cfBuf = AUDIOMIXBUF_B2F(&pStreamEx->Guest.MixBuf, cbBuf);
    3897 
    3898         if (pThis->In.fEnabled) /* Input for this audio driver enabled? See #9822. */
    3899         {
    3900             if (!PDMAudioStrmStatusCanRead(pStreamEx->fStatus))
    3901             {
    3902                 rc = VERR_AUDIO_STREAM_NOT_READY;
    3903                 break;
    3904             }
    3905 
    3906             /*
    3907              * Read from the parent buffer (that is, the guest buffer) which
    3908              * should have the audio data in the format the guest needs.
    3909              */
    3910             uint32_t cfToRead = RT_MIN(cfBuf, AudioMixBufLive(&pStreamEx->Guest.MixBuf));
    3911             while (cfToRead)
    3912             {
    3913                 uint32_t cfRead;
    3914                 rc = AudioMixBufAcquireReadBlock(&pStreamEx->Guest.MixBuf,
    3915                                                  (uint8_t *)pvBuf + AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfReadTotal),
    3916                                                  AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfToRead), &cfRead);
    3917                 if (RT_FAILURE(rc))
    3918                     break;
    3919 
    3920 #ifdef VBOX_WITH_STATISTICS
    3921                 const uint32_t cbRead = AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfRead);
    3922                 STAM_COUNTER_ADD(&pThis->Stats.TotalBytesRead,    cbRead);
    3923                 STAM_COUNTER_ADD(&pStreamEx->In.Stats.TotalFramesRead, cfRead);
    3924                 STAM_COUNTER_INC(&pStreamEx->In.Stats.TotalTimesRead);
    3925 #endif
    3926                 Assert(cfToRead >= cfRead);
    3927                 cfToRead -= cfRead;
    3928 
    3929                 cfReadTotal += cfRead;
    3930 
    3931                 AudioMixBufReleaseReadBlock(&pStreamEx->Guest.MixBuf, cfRead);
    3932             }
    3933 
    3934             if (cfReadTotal)
    3935             {
    3936                 if (pThis->CfgIn.Dbg.fEnabled)
    3937                     AudioHlpFileWrite(pStreamEx->In.Dbg.pFileStreamRead,
    3938                                       pvBuf, AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfReadTotal), 0 /* fFlags */);
    3939 
    3940                 AudioMixBufFinish(&pStreamEx->Guest.MixBuf, cfReadTotal);
    3941             }
    3942         }
    3943 
    3944         /* If we were not able to read as much data as requested, fill up the returned
    3945          * data with silence.
    3946          *
    3947          * This is needed to keep the device emulation DMA transfers up and running at a constant rate. */
    3948         if (cfReadTotal < cfBuf)
    3949         {
    3950             Log3Func(("[%s] Filling in silence (%RU64ms / %RU64ms)\n", pStream->szName,
    3951                       PDMAudioPropsFramesToMilli(&pStreamEx->Guest.Cfg.Props, cfBuf - cfReadTotal),
    3952                       PDMAudioPropsFramesToMilli(&pStreamEx->Guest.Cfg.Props, cfBuf)));
    3953 
    3954             PDMAudioPropsClearBuffer(&pStreamEx->Guest.Cfg.Props,
    3955                                      (uint8_t *)pvBuf + AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfReadTotal),
    3956                                      AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfBuf - cfReadTotal),
    3957                                      cfBuf - cfReadTotal);
    3958 
    3959             cfReadTotal = cfBuf;
    3960         }
    3961 
    3962         cbReadTotal = AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfReadTotal);
    3963 
    3964         pStreamEx->nsLastReadWritten = RTTimeNanoTS();
    3965 
    3966         Log3Func(("[%s] fEnabled=%RTbool, cbReadTotal=%RU32, rc=%Rrc\n", pStream->szName, pThis->In.fEnabled, cbReadTotal, rc));
    3967 
    3968     } while (0);
    3969 
    3970     RTCritSectLeave(&pStreamEx->Core.CritSect);
    3971 
    3972     if (RT_SUCCESS(rc) && pcbRead)
    3973         *pcbRead = cbReadTotal;
    3974     return rc;
    3975 }
    3976 
    3977 
    3978 /**
    3979  * Captures non-interleaved input from a host stream.
    3980  *
    3981  * @returns VBox status code.
    3982  * @param   pThis       Driver instance.
    3983  * @param   pStreamEx   Stream to capture from.
    3984  * @param   pcfCaptured Number of (host) audio frames captured.
    3985  */
    3986 static int drvAudioStreamCaptureNonInterleaved(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx, uint32_t *pcfCaptured)
    3987 {
    3988     Assert(pStreamEx->Core.enmDir == PDMAUDIODIR_IN);
    3989     Assert(pStreamEx->Host.Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED);
    3990 
    3991     /*
    3992      * ...
    3993      */
    3994     uint32_t cbReadable = pThis->pHostDrvAudio->pfnStreamGetReadable(pThis->pHostDrvAudio, pStreamEx->pBackend);
    3995     if (!cbReadable)
    3996         Log2Func(("[%s] No readable data available\n", pStreamEx->Core.szName));
    3997 
    3998     uint32_t cbFree = AudioMixBufFreeBytes(&pStreamEx->Guest.MixBuf); /* Parent */
    3999     if (!cbFree)
    4000         Log2Func(("[%s] Buffer full\n", pStreamEx->Core.szName));
    4001 
    4002     if (cbReadable > cbFree) /* More data readable than we can store at the moment? Limit. */
    4003         cbReadable = cbFree;
    4004 
    4005     /*
    4006      * ...
    4007      */
    4008     int      rc = VINF_SUCCESS;
    4009     uint32_t cfCapturedTotal = 0;
    4010     while (cbReadable)
    4011     {
    4012         uint8_t  abChunk[_4K];
    4013         uint32_t cbCaptured;
    4014         rc = pThis->pHostDrvAudio->pfnStreamCapture(pThis->pHostDrvAudio, pStreamEx->pBackend,
    4015                                                     abChunk, RT_MIN(cbReadable, (uint32_t)sizeof(abChunk)), &cbCaptured);
    4016         if (RT_FAILURE(rc))
    4017         {
    4018             int rc2 = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
    4019             AssertRC(rc2);
    4020             break;
    4021         }
    4022 
    4023         Assert(cbCaptured <= sizeof(abChunk));
    4024         if (cbCaptured > sizeof(abChunk)) /* Paranoia. */
    4025             cbCaptured = (uint32_t)sizeof(abChunk);
    4026 
    4027         if (!cbCaptured) /* Nothing captured? Take a shortcut. */
    4028             break;
    4029 
    4030         /* We use the host side mixing buffer as an intermediate buffer to do some
    4031          * (first) processing (if needed), so always write the incoming data at offset 0. */
    4032         uint32_t cfHstWritten = 0;
    4033         rc = AudioMixBufWriteAt(&pStreamEx->Host.MixBuf, 0 /* offFrames */, abChunk, cbCaptured, &cfHstWritten);
    4034         if (   RT_FAILURE(rc)
    4035             || !cfHstWritten)
    4036         {
    4037             AssertMsgFailed(("[%s] Write failed: cbCaptured=%RU32, cfHstWritten=%RU32, rc=%Rrc\n",
    4038                              pStreamEx->Core.szName, cbCaptured, cfHstWritten, rc));
    4039             break;
    4040         }
    4041 
    4042         if (pThis->CfgIn.Dbg.fEnabled)
    4043             AudioHlpFileWrite(pStreamEx->In.Dbg.pFileCapture, abChunk, cbCaptured, 0 /* fFlags */);
    4044 
    4045         uint32_t cfHstMixed = 0;
    4046         if (cfHstWritten)
    4047         {
    4048             int rc2 = AudioMixBufMixToParentEx(&pStreamEx->Host.MixBuf, 0 /* cSrcOffset */, cfHstWritten /* cSrcFrames */,
    4049                                                &cfHstMixed /* pcSrcMixed */);
    4050             Log3Func(("[%s] cbCaptured=%RU32, cfWritten=%RU32, cfMixed=%RU32, rc=%Rrc\n",
    4051                       pStreamEx->Core.szName, cbCaptured, cfHstWritten, cfHstMixed, rc2));
    4052             AssertRC(rc2);
    4053         }
    4054 
    4055         Assert(cbReadable >= cbCaptured);
    4056         cbReadable      -= cbCaptured;
    4057         cfCapturedTotal += cfHstMixed;
    4058     }
    4059 
    4060     if (RT_SUCCESS(rc))
    4061     {
    4062         if (cfCapturedTotal)
    4063             Log2Func(("[%s] %RU32 frames captured, rc=%Rrc\n", pStreamEx->Core.szName, cfCapturedTotal, rc));
    4064     }
    4065     else
    4066         LogFunc(("[%s] Capturing failed with rc=%Rrc\n", pStreamEx->Core.szName, rc));
    4067 
    4068     if (pcfCaptured)
    4069         *pcfCaptured = cfCapturedTotal;
    4070 
    4071     return rc;
    4072 }
    4073 
    4074 
    4075 /**
    4076  * Captures raw input from a host stream.
    4077  *
    4078  * Raw input means that the backend directly operates on PDMAUDIOFRAME structs without
    4079  * no data layout processing done in between.
    4080  *
    4081  * Needed for e.g. the VRDP audio backend (in Main).
    4082  *
    4083  * @returns VBox status code.
    4084  * @param   pThis       Driver instance.
    4085  * @param   pStreamEx   Stream to capture from.
    4086  * @param   pcfCaptured Number of (host) audio frames captured.
    4087  */
    4088 static int drvAudioStreamCaptureRaw(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx, uint32_t *pcfCaptured)
    4089 {
    4090     Assert(pStreamEx->Core.enmDir == PDMAUDIODIR_IN);
    4091     Assert(pStreamEx->Host.Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_RAW);
    4092     AssertPtr(pThis->pHostDrvAudio->pfnStreamGetReadable);
    4093 
    4094     /*
    4095      * ...
    4096      */
    4097     /* Note: Raw means *audio frames*, not bytes! */
    4098     uint32_t cfReadable = pThis->pHostDrvAudio->pfnStreamGetReadable(pThis->pHostDrvAudio, pStreamEx->pBackend);
    4099     if (!cfReadable)
    4100         Log2Func(("[%s] No readable data available\n", pStreamEx->Core.szName));
    4101 
    4102     const uint32_t cfFree = AudioMixBufFree(&pStreamEx->Guest.MixBuf); /* Parent */
    4103     if (!cfFree)
    4104         Log2Func(("[%s] Buffer full\n", pStreamEx->Core.szName));
    4105 
    4106     if (cfReadable > cfFree) /* More data readable than we can store at the moment? Limit. */
    4107         cfReadable = cfFree;
    4108 
    4109     /*
    4110      * ...
    4111      */
    4112     int      rc              = VINF_SUCCESS;
    4113     uint32_t cfCapturedTotal = 0;
    4114     while (cfReadable)
    4115     {
    4116         PPDMAUDIOFRAME paFrames;
    4117         uint32_t cfWritable;
    4118         rc = AudioMixBufPeekMutable(&pStreamEx->Host.MixBuf, cfReadable, &paFrames, &cfWritable);
    4119         if (   RT_FAILURE(rc)
    4120             || !cfWritable)
    4121             break;
    4122 
    4123         uint32_t cfCaptured;
    4124         rc = pThis->pHostDrvAudio->pfnStreamCapture(pThis->pHostDrvAudio, pStreamEx->pBackend,
    4125                                                     paFrames, cfWritable, &cfCaptured);
    4126         if (RT_FAILURE(rc))
    4127         {
    4128             int rc2 = drvAudioStreamControlInternalBackend(pThis, pStreamEx, PDMAUDIOSTREAMCMD_DISABLE);
    4129             AssertRC(rc2);
    4130             break;
    4131         }
    4132 
    4133         Assert(cfCaptured <= cfWritable);
    4134         if (cfCaptured > cfWritable) /* Paranoia. */
    4135             cfCaptured = cfWritable;
    4136 
    4137         Assert(cfReadable >= cfCaptured);
    4138         cfReadable      -= cfCaptured;
    4139         cfCapturedTotal += cfCaptured;
    4140     }
    4141 
    4142     if (pcfCaptured)
    4143         *pcfCaptured = cfCapturedTotal;
    4144     Log2Func(("[%s] %RU32 frames captured, rc=%Rrc\n", pStreamEx->Core.szName, cfCapturedTotal, rc));
    4145     return rc;
    4146 }
    4147 
    4148 
    4149 /**
    4150  * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamCapture}
    4151  */
    4152 static DECLCALLBACK(int) drvAudioStreamCapture(PPDMIAUDIOCONNECTOR pInterface,
    4153                                                PPDMAUDIOSTREAM pStream, uint32_t *pcFramesCaptured)
    4154 {
    4155     PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector);
    4156     AssertPtr(pThis);
    4157     PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream;
    4158     AssertPtrReturn(pStreamEx, VERR_INVALID_POINTER);
    4159     AssertPtrNull(pcFramesCaptured);
    4160     AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
    4161     AssertReturn(pStreamEx->uMagic      == DRVAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC);
    4162     AssertMsg(pStreamEx->Core.enmDir == PDMAUDIODIR_IN,
    4163               ("Stream '%s' is not an input stream and therefore cannot be captured (direction is 0x%x)\n",
    4164                pStreamEx->Core.szName, pStreamEx->Core.enmDir));
    4165     int rc = RTCritSectEnter(&pStreamEx->Core.CritSect);
    4166     AssertRCReturn(rc, rc);
    4167     RTCritSectRwEnterShared(&pThis->CritSectHotPlug);
    4168 
    4169 #ifdef LOG_ENABLED
    4170     char szStreamSts[DRVAUDIO_STATUS_STR_MAX];
    4171 #endif
    4172     Log3Func(("[%s] fStatus=%s\n", pStreamEx->Core.szName, drvAudioStreamStatusToStr(szStreamSts, pStreamEx->fStatus)));
    4173 
    4174     /*
    4175      * ...
    4176      */
    4177     uint32_t cfCaptured = 0;
    4178     do
    4179     {
    4180         if (!pThis->pHostDrvAudio)
    4181         {
    4182             rc = VERR_PDM_NO_ATTACHED_DRIVER;
    4183             break;
    4184         }
    4185 
    4186         if (   !pThis->In.fEnabled
    4187             || !PDMAudioStrmStatusCanRead(pStreamEx->fStatus)
    4188             || !(pStreamEx->fStatus & PDMAUDIOSTREAM_STS_BACKEND_READY))
    4189         {
    4190             rc = VERR_AUDIO_STREAM_NOT_READY;
    4191             break;
    4192         }
    4193 
    4194         PDMHOSTAUDIOSTREAMSTATE const enmBackendState = drvAudioStreamGetBackendState(pThis, pStreamEx);
    4195         if (enmBackendState == PDMHOSTAUDIOSTREAMSTATE_OKAY)
    4196         {
    4197             /*
    4198              * Do the actual capturing.
    4199              */
    4200             if (RT_LIKELY(pStreamEx->Host.Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED))
    4201                 rc = drvAudioStreamCaptureNonInterleaved(pThis, pStreamEx, &cfCaptured);
    4202             else if (pStreamEx->Host.Cfg.enmLayout == PDMAUDIOSTREAMLAYOUT_RAW)
    4203                 rc = drvAudioStreamCaptureRaw(pThis, pStreamEx, &cfCaptured);
    4204             else
    4205                 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
    4206 
    4207             if (RT_SUCCESS(rc))
    4208             {
    4209                 Log3Func(("[%s] %RU32 frames captured, rc=%Rrc\n", pStreamEx->Core.szName, cfCaptured, rc));
    4210 
    4211                 STAM_COUNTER_ADD(&pThis->Stats.TotalFramesIn,              cfCaptured);
    4212                 STAM_COUNTER_ADD(&pStreamEx->In.Stats.TotalFramesCaptured, cfCaptured);
    4213             }
    4214             else if (RT_UNLIKELY(RT_FAILURE(rc)))
    4215                 LogRel(("Audio: Capturing stream '%s' failed with %Rrc\n", pStreamEx->Core.szName, rc));
    4216         }
    4217         else
    4218             rc = VERR_AUDIO_STREAM_NOT_READY;
    4219     } while (0);
    4220 
    4221     RTCritSectRwLeaveShared(&pThis->CritSectHotPlug);
    4222     RTCritSectLeave(&pStreamEx->Core.CritSect);
    4223 
    4224     if (pcFramesCaptured)
    4225         *pcFramesCaptured = cfCaptured;
    4226 
    4227     if (RT_FAILURE(rc))
    4228         LogFlowFuncLeaveRC(rc);
    4229     return rc;
    4230 }
    4231 #endif
    4232 
    4233 
    42343864/*********************************************************************************************************************************
    42353865*   PDMIHOSTAUDIOPORT interface implementation.                                                                                  *
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