VirtualBox

Changeset 88459 in vbox for trunk/src/VBox


Ignore:
Timestamp:
Apr 12, 2021 10:07:51 AM (4 years ago)
Author:
vboxsync
Message:

DrvAudioRec: Cleanups. bugref:9890

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/src-client/DrvAudioRec.cpp

    r88395 r88459  
    113113*   Structures and Typedefs                                                                                                      *
    114114*********************************************************************************************************************************/
    115 
    116115/**
    117116 * Enumeration for specifying the recording container type.
     
    219218    };
    220219
    221 #ifdef VBOX_WITH_STATISTICS /** @todo Make these real STAM values. */
     220#ifdef VBOX_WITH_STATISTICS /** @todo Register these values. */
    222221    struct
    223222    {
     
    226225        /** Total time (in ms) of already encoded audio data. */
    227226        uint64_t        msEncTotal;
    228     } STAM;
    229 #endif /* VBOX_WITH_STATISTICS */
    230 
     227    } Stats;
     228#endif
    231229} AVRECCODEC, *PAVRECCODEC;
    232230
     
    249247{
    250248    /** The stream's acquired configuration. */
    251     PPDMAUDIOSTREAMCFG   pCfg;
     249    PDMAUDIOSTREAMCFG    Cfg;
    252250    /** (Audio) frame buffer. */
    253251    PRTCIRCBUF           pCircBuf;
     
    289287} DRVAUDIORECORDING, *PDRVAUDIORECORDING;
    290288
    291 /** Makes DRVAUDIORECORDING out of PDMIHOSTAUDIO. */
    292 #define PDMIHOSTAUDIO_2_DRVAUDIORECORDING(pInterface) /* (clang doesn't think it is a POD, thus _DYN.) */ \
    293     ( (PDRVAUDIORECORDING)((uintptr_t)pInterface - RT_UOFFSETOF_DYN(DRVAUDIORECORDING, IHostAudio)) )
     289
     290AudioVideoRec::AudioVideoRec(Console *pConsole)
     291    : AudioDriver(pConsole)
     292    , mpDrv(NULL)
     293{
     294}
     295
     296
     297AudioVideoRec::~AudioVideoRec(void)
     298{
     299    if (mpDrv)
     300    {
     301        mpDrv->pAudioVideoRec = NULL;
     302        mpDrv = NULL;
     303    }
     304}
     305
     306
     307/**
     308 * Applies a video recording configuration to this driver instance.
     309 *
     310 * @returns VBox status code.
     311 * @param   Settings        Capturing configuration to apply.
     312 */
     313int AudioVideoRec::applyConfiguration(const settings::RecordingSettings &Settings)
     314{
     315    /** @todo Do some validation here. */
     316    mVideoRecCfg = Settings; /* Note: Does have an own copy operator. */
     317    return VINF_SUCCESS;
     318}
     319
     320
     321/**
     322 * @copydoc AudioDriver::configureDriver
     323 */
     324int AudioVideoRec::configureDriver(PCFGMNODE pLunCfg)
     325{
     326    int rc = CFGMR3InsertInteger(pLunCfg, "Object",    (uintptr_t)mpConsole->i_recordingGetAudioDrv());
     327    AssertRCReturn(rc, rc);
     328    rc = CFGMR3InsertInteger(pLunCfg, "ObjectConsole", (uintptr_t)mpConsole);
     329    AssertRCReturn(rc, rc);
     330
     331    /** @todo For now we're using the configuration of the first screen here audio-wise. */
     332    Assert(mVideoRecCfg.mapScreens.size() >= 1);
     333    const settings::RecordingScreenSettings &Screen0Settings = mVideoRecCfg.mapScreens[0];
     334
     335    rc = CFGMR3InsertInteger(pLunCfg, "ContainerType", (uint64_t)Screen0Settings.enmDest);
     336    AssertRCReturn(rc, rc);
     337    if (Screen0Settings.enmDest == RecordingDestination_File)
     338    {
     339        rc = CFGMR3InsertString(pLunCfg, "ContainerFileName", Utf8Str(Screen0Settings.File.strName).c_str());
     340        AssertRCReturn(rc, rc);
     341    }
     342    rc = CFGMR3InsertInteger(pLunCfg, "CodecHz", Screen0Settings.Audio.uHz);
     343    AssertRCReturn(rc, rc);
     344    rc = CFGMR3InsertInteger(pLunCfg, "CodecBits", Screen0Settings.Audio.cBits);
     345    AssertRCReturn(rc, rc);
     346    rc = CFGMR3InsertInteger(pLunCfg, "CodecChannels", Screen0Settings.Audio.cChannels);
     347    AssertRCReturn(rc, rc);
     348    rc = CFGMR3InsertInteger(pLunCfg, "CodecBitrate", 0); /* Let Opus decide for now. */
     349    AssertRCReturn(rc, rc);
     350
     351    return AudioDriver::configureDriver(pLunCfg);
     352}
     353
     354
     355/*********************************************************************************************************************************
     356*   PDMIHOSTAUDIO                                                                                                                *
     357*********************************************************************************************************************************/
     358
     359/**
     360 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
     361 */
     362static DECLCALLBACK(int) drvAudioVideoRecHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
     363{
     364    RT_NOREF(pInterface);
     365    AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
     366
     367    RTStrPrintf2(pBackendCfg->szName, sizeof(pBackendCfg->szName), "VideoRec");
     368
     369    pBackendCfg->cbStreamOut    = sizeof(AVRECSTREAM);
     370    pBackendCfg->cbStreamIn     = 0;
     371    pBackendCfg->cMaxStreamsIn  = 0;
     372    pBackendCfg->cMaxStreamsOut = UINT32_MAX;
     373
     374    return VINF_SUCCESS;
     375}
     376
     377
     378/**
     379 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
     380 */
     381static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvAudioVideoRecHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
     382{
     383    RT_NOREF(pInterface, enmDir);
     384    return PDMAUDIOBACKENDSTS_RUNNING;
     385}
     386
     387
     388/**
     389 * Creates an audio output stream and associates it with the specified recording sink.
     390 *
     391 * @returns VBox status code.
     392 * @param   pThis               Driver instance.
     393 * @param   pStreamAV           Audio output stream to create.
     394 * @param   pSink               Recording sink to associate audio output stream to.
     395 * @param   pCfgReq             Requested configuration by the audio backend.
     396 * @param   pCfgAcq             Acquired configuration by the audio output stream.
     397 */
     398static int avRecCreateStreamOut(PDRVAUDIORECORDING pThis, PAVRECSTREAM pStreamAV,
     399                                PAVRECSINK pSink, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
     400{
     401    AssertPtrReturn(pThis,     VERR_INVALID_POINTER);
     402    AssertPtrReturn(pStreamAV, VERR_INVALID_POINTER);
     403    AssertPtrReturn(pSink,     VERR_INVALID_POINTER);
     404    AssertPtrReturn(pCfgReq,   VERR_INVALID_POINTER);
     405    AssertPtrReturn(pCfgAcq,   VERR_INVALID_POINTER);
     406
     407    if (pCfgReq->u.enmDst != PDMAUDIOPLAYBACKDST_FRONT)
     408    {
     409        LogRel2(("Recording: Support for surround audio not implemented yet\n"));
     410        AssertFailed();
     411        return VERR_NOT_SUPPORTED;
     412    }
     413
     414#ifdef VBOX_WITH_LIBOPUS
     415    int rc = RTCircBufCreate(&pStreamAV->pCircBuf, pSink->Codec.Opus.cbFrame * 2 /* Use "double buffering" */);
     416    if (RT_SUCCESS(rc))
     417    {
     418        size_t cbScratchBuf = pSink->Codec.Opus.cbFrame;
     419        pStreamAV->pvSrcBuf = RTMemAlloc(cbScratchBuf);
     420        if (pStreamAV->pvSrcBuf)
     421        {
     422            pStreamAV->cbSrcBuf = cbScratchBuf;
     423            pStreamAV->pvDstBuf = RTMemAlloc(cbScratchBuf);
     424            if (pStreamAV->pvDstBuf)
     425            {
     426                pStreamAV->cbDstBuf = cbScratchBuf;
     427
     428                pStreamAV->pSink      = pSink; /* Assign sink to stream. */
     429                pStreamAV->uLastPTSMs = 0;
     430
     431                /* Make sure to let the driver backend know that we need the audio data in
     432                 * a specific sampling rate Opus is optimized for. */
     433/** @todo r=bird: pCfgAcq->Props isn't initialized at all, except for uHz... */
     434                pCfgAcq->Props.uHz         = pSink->Codec.Parms.PCMProps.uHz;
     435//                pCfgAcq->Props.cShift      = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(pCfgAcq->Props.cbSample, pCfgAcq->Props.cChannels);
     436
     437                /* Every Opus frame marks a period for now. Optimize this later. */
     438                pCfgAcq->Backend.cFramesPeriod       = PDMAudioPropsMilliToFrames(&pCfgAcq->Props, pSink->Codec.Opus.msFrame);
     439                pCfgAcq->Backend.cFramesBufferSize   = PDMAudioPropsMilliToFrames(&pCfgAcq->Props, 100 /*ms*/); /** @todo Make this configurable. */
     440                pCfgAcq->Backend.cFramesPreBuffering = pCfgAcq->Backend.cFramesPeriod * 2;
     441            }
     442            else
     443                rc = VERR_NO_MEMORY;
     444        }
     445        else
     446            rc = VERR_NO_MEMORY;
     447    }
     448#else
     449    RT_NOREF(pThis, pSink, pStreamAV, pCfgReq, pCfgAcq);
     450    int rc = VERR_NOT_SUPPORTED;
     451#endif /* VBOX_WITH_LIBOPUS */
     452
     453    LogFlowFuncLeaveRC(rc);
     454    return rc;
     455}
     456
     457
     458/**
     459 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
     460 */
     461static DECLCALLBACK(int) drvAudioVideoRecHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
     462                                                         PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
     463{
     464    PDRVAUDIORECORDING pThis     = RT_FROM_CPP_MEMBER(pInterface, DRVAUDIORECORDING, IHostAudio);
     465    PAVRECSTREAM       pStreamAV = (PAVRECSTREAM)pStream;
     466    AssertPtrReturn(pStreamAV, VERR_INVALID_POINTER);
     467    AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
     468    AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
     469
     470    if (pCfgReq->enmDir == PDMAUDIODIR_IN)
     471        return VERR_NOT_SUPPORTED;
     472
     473    /* For now we only have one sink, namely the driver's one.
     474     * Later each stream could have its own one, to e.g. router different stream to different sinks .*/
     475    PAVRECSINK pSink = &pThis->Sink;
     476
     477    int rc = avRecCreateStreamOut(pThis, pStreamAV, pSink, pCfgReq, pCfgAcq);
     478    PDMAudioStrmCfgCopy(&pStreamAV->Cfg, pCfgAcq);
     479
     480    return rc;
     481}
     482
     483
     484/**
     485 * Destroys (closes) an audio output stream.
     486 *
     487 * @returns VBox status code.
     488 * @param   pThis               Driver instance.
     489 * @param   pStreamAV           Audio output stream to destroy.
     490 */
     491static int avRecDestroyStreamOut(PDRVAUDIORECORDING pThis, PAVRECSTREAM pStreamAV)
     492{
     493    RT_NOREF(pThis);
     494
     495    if (pStreamAV->pCircBuf)
     496    {
     497        RTCircBufDestroy(pStreamAV->pCircBuf);
     498        pStreamAV->pCircBuf = NULL;
     499    }
     500
     501    if (pStreamAV->pvSrcBuf)
     502    {
     503        Assert(pStreamAV->cbSrcBuf);
     504        RTMemFree(pStreamAV->pvSrcBuf);
     505        pStreamAV->pvSrcBuf = NULL;
     506        pStreamAV->cbSrcBuf = 0;
     507    }
     508
     509    if (pStreamAV->pvDstBuf)
     510    {
     511        Assert(pStreamAV->cbDstBuf);
     512        RTMemFree(pStreamAV->pvDstBuf);
     513        pStreamAV->pvDstBuf = NULL;
     514        pStreamAV->cbDstBuf = 0;
     515    }
     516
     517    return VINF_SUCCESS;
     518}
     519
     520
     521/**
     522 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
     523 */
     524static DECLCALLBACK(int) drvAudioVideoRecHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
     525{
     526    PDRVAUDIORECORDING pThis     = RT_FROM_CPP_MEMBER(pInterface, DRVAUDIORECORDING, IHostAudio);
     527    PAVRECSTREAM       pStreamAV = (PAVRECSTREAM)pStream;
     528    AssertPtrReturn(pStream, VERR_INVALID_POINTER);
     529
     530    int rc = VINF_SUCCESS;
     531    if (pStreamAV->Cfg.enmDir == PDMAUDIODIR_OUT)
     532        rc = avRecDestroyStreamOut(pThis, pStreamAV);
     533
     534    return rc;
     535}
     536
     537
     538/**
     539 * Controls an audio output stream
     540 *
     541 * @returns VBox status code.
     542 * @param   pThis               Driver instance.
     543 * @param   pStreamAV           Audio output stream to control.
     544 * @param   enmStreamCmd        Stream command to issue.
     545 */
     546static int avRecControlStreamOut(PDRVAUDIORECORDING pThis, PAVRECSTREAM pStreamAV, PDMAUDIOSTREAMCMD enmStreamCmd)
     547{
     548    RT_NOREF(pThis, pStreamAV);
     549
     550    int rc;
     551    switch (enmStreamCmd)
     552    {
     553        case PDMAUDIOSTREAMCMD_ENABLE:
     554        case PDMAUDIOSTREAMCMD_DISABLE:
     555        case PDMAUDIOSTREAMCMD_RESUME:
     556        case PDMAUDIOSTREAMCMD_PAUSE:
     557            rc = VINF_SUCCESS;
     558            break;
     559
     560        default:
     561            rc = VERR_NOT_SUPPORTED;
     562            break;
     563    }
     564
     565    return rc;
     566}
     567
     568
     569/**
     570 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
     571 */
     572static DECLCALLBACK(int) drvAudioVideoRecHA_StreamControl(PPDMIHOSTAUDIO pInterface,
     573                                                          PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
     574{
     575    PDRVAUDIORECORDING pThis     = RT_FROM_CPP_MEMBER(pInterface, DRVAUDIORECORDING, IHostAudio);
     576    PAVRECSTREAM       pStreamAV = (PAVRECSTREAM)pStream;
     577    AssertPtrReturn(pStreamAV, VERR_INVALID_POINTER);
     578
     579    if (pStreamAV->Cfg.enmDir == PDMAUDIODIR_OUT)
     580        return avRecControlStreamOut(pThis, pStreamAV, enmStreamCmd);
     581
     582    return VINF_SUCCESS;
     583}
     584
     585
     586/**
     587 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
     588 */
     589static DECLCALLBACK(uint32_t) drvAudioVideoRecHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
     590{
     591    RT_NOREF(pInterface, pStream);
     592    return 0; /* Video capturing does not provide any input. */
     593}
     594
     595
     596/**
     597 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
     598 */
     599static DECLCALLBACK(uint32_t) drvAudioVideoRecHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
     600{
     601    RT_NOREF(pInterface, pStream);
     602    return UINT32_MAX;
     603}
     604
     605
     606/**
     607 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus}
     608 */
     609static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvAudioVideoRecHA_StreamGetStatus(PPDMIHOSTAUDIO pInterface,
     610                                                                          PPDMAUDIOBACKENDSTREAM pStream)
     611{
     612    RT_NOREF(pInterface, pStream);
     613    return PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED | PDMAUDIOSTREAMSTS_FLAGS_ENABLED;
     614}
     615
     616
     617/**
     618 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
     619 */
     620static DECLCALLBACK(int) drvAudioVideoRecHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
     621                                                       const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
     622{
     623    RT_NOREF(pInterface);
     624    PAVRECSTREAM pStreamAV = (PAVRECSTREAM)pStream;
     625    AssertPtrReturn(pStreamAV, VERR_INVALID_POINTER);
     626    AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
     627    AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
     628    AssertReturn(pcbWritten, VERR_INVALID_PARAMETER);
     629
     630    int rc = VINF_SUCCESS;
     631
     632    uint32_t cbWrittenTotal = 0;
     633
     634    /*
     635     * Call the encoder with the data.
     636     */
     637#ifdef VBOX_WITH_LIBOPUS
     638    PAVRECSINK pSink    = pStreamAV->pSink;
     639    AssertPtr(pSink);
     640    PAVRECCODEC pCodec  = &pSink->Codec;
     641    AssertPtr(pCodec);
     642    PRTCIRCBUF pCircBuf = pStreamAV->pCircBuf;
     643    AssertPtr(pCircBuf);
     644
     645    uint32_t cbToWrite = cbBuf;
     646
     647    /*
     648     * Write as much as we can into our internal ring buffer.
     649     */
     650    while (   cbToWrite
     651           && RTCircBufFree(pCircBuf))
     652    {
     653        void  *pvCircBuf = NULL;
     654        size_t cbCircBuf = 0;
     655        RTCircBufAcquireWriteBlock(pCircBuf, cbToWrite, &pvCircBuf, &cbCircBuf);
     656
     657        if (cbCircBuf)
     658        {
     659            memcpy(pvCircBuf, (uint8_t *)pvBuf + cbWrittenTotal, cbCircBuf),
     660            cbWrittenTotal += (uint32_t)cbCircBuf;
     661            Assert(cbToWrite >= cbCircBuf);
     662            cbToWrite      -= (uint32_t)cbCircBuf;
     663        }
     664
     665        RTCircBufReleaseWriteBlock(pCircBuf, cbCircBuf);
     666        AssertBreak(cbCircBuf);
     667    }
     668
     669    /*
     670     * Process our internal ring buffer and encode the data.
     671     */
     672
     673    /* Only encode data if we have data for the given time period (or more). */
     674    while (RTCircBufUsed(pCircBuf) >= pCodec->Opus.cbFrame)
     675    {
     676        LogFunc(("cbAvail=%zu, csFrame=%RU32, cbFrame=%RU32\n",
     677                 RTCircBufUsed(pCircBuf), pCodec->Opus.csFrame, pCodec->Opus.cbFrame));
     678
     679        uint32_t cbSrc = 0;
     680        while (cbSrc < pCodec->Opus.cbFrame)
     681        {
     682            void  *pvCircBuf = NULL;
     683            size_t cbCircBuf = 0;
     684            RTCircBufAcquireReadBlock(pCircBuf, pCodec->Opus.cbFrame - cbSrc, &pvCircBuf, &cbCircBuf);
     685
     686            if (cbCircBuf)
     687            {
     688                memcpy((uint8_t *)pStreamAV->pvSrcBuf + cbSrc, pvCircBuf, cbCircBuf);
     689
     690                cbSrc += (uint32_t)cbCircBuf;
     691                Assert(cbSrc <= pStreamAV->cbSrcBuf);
     692            }
     693
     694            RTCircBufReleaseReadBlock(pCircBuf, cbCircBuf);
     695            AssertBreak(cbCircBuf);
     696        }
     697
     698        Assert(cbSrc == pCodec->Opus.cbFrame);
     699
     700# ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
     701        RTFILE fh;
     702        RTFileOpen(&fh, VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH "DrvAudioVideoRec.pcm",
     703                   RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
     704        RTFileWrite(fh, pStreamAV->pvSrcBuf, cbSrc, NULL);
     705        RTFileClose(fh);
     706# endif
     707
     708        /*
     709         * Opus always encodes PER "OPUS FRAME", that is, exactly 2.5, 5, 10, 20, 40 or 60 ms of audio data.
     710         *
     711         * A packet can have up to 120ms worth of audio data.
     712         * Anything > 120ms of data will result in a "corrupted package" error message by
     713         * by decoding application.
     714         */
     715
     716        /* Call the encoder to encode one "Opus frame" per iteration. */
     717        opus_int32 cbWritten = opus_encode(pSink->Codec.Opus.pEnc,
     718                                           (opus_int16 *)pStreamAV->pvSrcBuf, pCodec->Opus.csFrame,
     719                                           (uint8_t *)pStreamAV->pvDstBuf, (opus_int32)pStreamAV->cbDstBuf);
     720        if (cbWritten > 0)
     721        {
     722            /* Get overall frames encoded. */
     723            const uint32_t cEncFrames     = opus_packet_get_nb_frames((uint8_t *)pStreamAV->pvDstBuf, cbWritten);
     724
     725# ifdef VBOX_WITH_STATISTICS
     726            pSink->Codec.Stats.cEncFrames += cEncFrames;
     727            pSink->Codec.Stats.msEncTotal += pSink->Codec.Opus.msFrame * cEncFrames;
     728# endif
     729            Assert((uint32_t)cbWritten <= (uint32_t)pStreamAV->cbDstBuf);
     730            const uint32_t cbDst = RT_MIN((uint32_t)cbWritten, (uint32_t)pStreamAV->cbDstBuf);
     731
     732            Assert(cEncFrames == 1);
     733
     734            if (pStreamAV->uLastPTSMs == 0)
     735                pStreamAV->uLastPTSMs = RTTimeProgramMilliTS(); /* We want the absolute time (in ms) since program start. */
     736
     737            const uint64_t uDurationMs = pSink->Codec.Opus.msFrame * cEncFrames;
     738            const uint64_t uPTSMs      = pStreamAV->uLastPTSMs;
     739
     740            pStreamAV->uLastPTSMs += uDurationMs;
     741
     742            switch (pSink->Con.Parms.enmType)
     743            {
     744                case AVRECCONTAINERTYPE_MAIN_CONSOLE:
     745                {
     746                    HRESULT hr = pSink->Con.Main.pConsole->i_recordingSendAudio(pStreamAV->pvDstBuf, cbDst, uPTSMs);
     747                    Assert(hr == S_OK);
     748                    RT_NOREF(hr);
     749                    break;
     750                }
     751
     752                case AVRECCONTAINERTYPE_WEBM:
     753                {
     754                    WebMWriter::BlockData_Opus blockData = { pStreamAV->pvDstBuf, cbDst, uPTSMs };
     755                    rc = pSink->Con.WebM.pWebM->WriteBlock(pSink->Con.WebM.uTrack, &blockData, sizeof(blockData));
     756                    AssertRC(rc);
     757                    break;
     758                }
     759
     760                default:
     761                    AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
     762                    break;
     763            }
     764        }
     765        else if (cbWritten < 0)
     766        {
     767            AssertMsgFailed(("Encoding failed: %s\n", opus_strerror(cbWritten)));
     768            rc = VERR_INVALID_PARAMETER;
     769        }
     770
     771        if (RT_FAILURE(rc))
     772            break;
     773    }
     774
     775    *pcbWritten = cbWrittenTotal;
     776#else
     777    /* Report back all data as being processed. */
     778    *pcbWritten = cbBuf;
     779
     780    rc = VERR_NOT_SUPPORTED;
     781#endif /* VBOX_WITH_LIBOPUS */
     782
     783    LogFlowFunc(("csReadTotal=%RU32, rc=%Rrc\n", cbWrittenTotal, rc));
     784    return rc;
     785}
     786
     787
     788/**
     789 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
     790 */
     791static DECLCALLBACK(int) drvAudioVideoRecHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
     792                                                          void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
     793{
     794    RT_NOREF(pInterface, pStream, pvBuf, cbBuf);
     795    *pcbRead = 0;
     796    return VINF_SUCCESS;
     797}
     798
     799
     800/*********************************************************************************************************************************
     801*   PDMIBASE                                                                                                                     *
     802*********************************************************************************************************************************/
     803
     804/**
     805 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
     806 */
     807static DECLCALLBACK(void *) drvAudioVideoRecQueryInterface(PPDMIBASE pInterface, const char *pszIID)
     808{
     809    PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
     810    PDRVAUDIORECORDING pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIORECORDING);
     811
     812    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
     813    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
     814    return NULL;
     815}
     816
     817
     818/*********************************************************************************************************************************
     819*   PDMDRVREG                                                                                                                    *
     820*********************************************************************************************************************************/
     821
     822/**
     823 * Shuts down (closes) a recording sink,
     824 *
     825 * @returns VBox status code.
     826 * @param   pSink               Recording sink to shut down.
     827 */
     828static void avRecSinkShutdown(PAVRECSINK pSink)
     829{
     830    AssertPtrReturnVoid(pSink);
     831
     832#ifdef VBOX_WITH_LIBOPUS
     833    if (pSink->Codec.Opus.pEnc)
     834    {
     835        opus_encoder_destroy(pSink->Codec.Opus.pEnc);
     836        pSink->Codec.Opus.pEnc = NULL;
     837    }
     838#endif
     839    switch (pSink->Con.Parms.enmType)
     840    {
     841        case AVRECCONTAINERTYPE_WEBM:
     842        {
     843            if (pSink->Con.WebM.pWebM)
     844            {
     845                LogRel2(("Recording: Finished recording audio to file '%s' (%zu bytes)\n",
     846                         pSink->Con.WebM.pWebM->GetFileName().c_str(), pSink->Con.WebM.pWebM->GetFileSize()));
     847
     848                int rc2 = pSink->Con.WebM.pWebM->Close();
     849                AssertRC(rc2);
     850
     851                delete pSink->Con.WebM.pWebM;
     852                pSink->Con.WebM.pWebM = NULL;
     853            }
     854            break;
     855        }
     856
     857        case AVRECCONTAINERTYPE_MAIN_CONSOLE:
     858        default:
     859            break;
     860    }
     861}
     862
     863
     864/**
     865 * @interface_method_impl{PDMDRVREG,pfnPowerOff}
     866 */
     867/*static*/ DECLCALLBACK(void) AudioVideoRec::drvPowerOff(PPDMDRVINS pDrvIns)
     868{
     869    PDRVAUDIORECORDING pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIORECORDING);
     870    LogFlowFuncEnter();
     871    avRecSinkShutdown(&pThis->Sink);
     872}
     873
     874
     875/**
     876 * @interface_method_impl{PDMDRVREG,pfnDestruct}
     877 */
     878/*static*/ DECLCALLBACK(void) AudioVideoRec::drvDestruct(PPDMDRVINS pDrvIns)
     879{
     880    PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
     881    PDRVAUDIORECORDING pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIORECORDING);
     882
     883    LogFlowFuncEnter();
     884
     885    switch (pThis->ContainerParms.enmType)
     886    {
     887        case AVRECCONTAINERTYPE_WEBM:
     888        {
     889            avRecSinkShutdown(&pThis->Sink);
     890            RTStrFree(pThis->ContainerParms.WebM.pszFile);
     891            break;
     892        }
     893
     894        default:
     895            break;
     896    }
     897
     898    /*
     899     * If the AudioVideoRec object is still alive, we must clear it's reference to
     900     * us since we'll be invalid when we return from this method.
     901     */
     902    if (pThis->pAudioVideoRec)
     903    {
     904        pThis->pAudioVideoRec->mpDrv = NULL;
     905        pThis->pAudioVideoRec = NULL;
     906    }
     907
     908    LogFlowFuncLeave();
     909}
     910
    294911
    295912/**
     
    4341051
    4351052#ifdef VBOX_WITH_STATISTICS
    436         pSink->Codec.STAM.cEncFrames = 0;
    437         pSink->Codec.STAM.msEncTotal = 0;
     1053        pSink->Codec.Stats.cEncFrames = 0;
     1054        pSink->Codec.Stats.msEncTotal = 0;
    4381055#endif
    439         pSink->tsStartMs             = RTTimeMilliTS();
     1056        pSink->tsStartMs              = RTTimeMilliTS();
    4401057    }
    4411058    else
     
    4511068
    4521069    return rc;
    453 }
    454 
    455 
    456 /**
    457  * Shuts down (closes) a recording sink,
    458  *
    459  * @returns VBox status code.
    460  * @param   pSink               Recording sink to shut down.
    461  */
    462 static void avRecSinkShutdown(PAVRECSINK pSink)
    463 {
    464     AssertPtrReturnVoid(pSink);
    465 
    466 #ifdef VBOX_WITH_LIBOPUS
    467     if (pSink->Codec.Opus.pEnc)
    468     {
    469         opus_encoder_destroy(pSink->Codec.Opus.pEnc);
    470         pSink->Codec.Opus.pEnc = NULL;
    471     }
    472 #endif
    473     switch (pSink->Con.Parms.enmType)
    474     {
    475         case AVRECCONTAINERTYPE_WEBM:
    476         {
    477             if (pSink->Con.WebM.pWebM)
    478             {
    479                 LogRel2(("Recording: Finished recording audio to file '%s' (%zu bytes)\n",
    480                          pSink->Con.WebM.pWebM->GetFileName().c_str(), pSink->Con.WebM.pWebM->GetFileSize()));
    481 
    482                 int rc2 = pSink->Con.WebM.pWebM->Close();
    483                 AssertRC(rc2);
    484 
    485                 delete pSink->Con.WebM.pWebM;
    486                 pSink->Con.WebM.pWebM = NULL;
    487             }
    488             break;
    489         }
    490 
    491         case AVRECCONTAINERTYPE_MAIN_CONSOLE:
    492         default:
    493             break;
    494     }
    495 }
    496 
    497 
    498 /**
    499  * Creates an audio output stream and associates it with the specified recording sink.
    500  *
    501  * @returns VBox status code.
    502  * @param   pThis               Driver instance.
    503  * @param   pStreamAV           Audio output stream to create.
    504  * @param   pSink               Recording sink to associate audio output stream to.
    505  * @param   pCfgReq             Requested configuration by the audio backend.
    506  * @param   pCfgAcq             Acquired configuration by the audio output stream.
    507  */
    508 static int avRecCreateStreamOut(PDRVAUDIORECORDING pThis, PAVRECSTREAM pStreamAV,
    509                                 PAVRECSINK pSink, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
    510 {
    511     AssertPtrReturn(pThis,     VERR_INVALID_POINTER);
    512     AssertPtrReturn(pStreamAV, VERR_INVALID_POINTER);
    513     AssertPtrReturn(pSink,     VERR_INVALID_POINTER);
    514     AssertPtrReturn(pCfgReq,   VERR_INVALID_POINTER);
    515     AssertPtrReturn(pCfgAcq,   VERR_INVALID_POINTER);
    516 
    517     if (pCfgReq->u.enmDst != PDMAUDIOPLAYBACKDST_FRONT)
    518     {
    519         LogRel2(("Recording: Support for surround audio not implemented yet\n"));
    520         AssertFailed();
    521         return VERR_NOT_SUPPORTED;
    522     }
    523 
    524 #ifdef VBOX_WITH_LIBOPUS
    525     int rc = RTCircBufCreate(&pStreamAV->pCircBuf, pSink->Codec.Opus.cbFrame * 2 /* Use "double buffering" */);
    526     if (RT_SUCCESS(rc))
    527     {
    528         size_t cbScratchBuf = pSink->Codec.Opus.cbFrame;
    529         pStreamAV->pvSrcBuf = RTMemAlloc(cbScratchBuf);
    530         if (pStreamAV->pvSrcBuf)
    531         {
    532             pStreamAV->cbSrcBuf = cbScratchBuf;
    533             pStreamAV->pvDstBuf = RTMemAlloc(cbScratchBuf);
    534             if (pStreamAV->pvDstBuf)
    535             {
    536                 pStreamAV->cbDstBuf = cbScratchBuf;
    537 
    538                 pStreamAV->pSink      = pSink; /* Assign sink to stream. */
    539                 pStreamAV->uLastPTSMs = 0;
    540 
    541                 /* Make sure to let the driver backend know that we need the audio data in
    542                  * a specific sampling rate Opus is optimized for. */
    543 /** @todo r=bird: pCfgAcq->Props isn't initialized at all, except for uHz... */
    544                 pCfgAcq->Props.uHz         = pSink->Codec.Parms.PCMProps.uHz;
    545 //                pCfgAcq->Props.cShift      = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(pCfgAcq->Props.cbSample, pCfgAcq->Props.cChannels);
    546 
    547                 /* Every Opus frame marks a period for now. Optimize this later. */
    548                 pCfgAcq->Backend.cFramesPeriod       = PDMAudioPropsMilliToFrames(&pCfgAcq->Props, pSink->Codec.Opus.msFrame);
    549                 pCfgAcq->Backend.cFramesBufferSize   = PDMAudioPropsMilliToFrames(&pCfgAcq->Props, 100 /*ms*/); /** @todo Make this configurable. */
    550                 pCfgAcq->Backend.cFramesPreBuffering = pCfgAcq->Backend.cFramesPeriod * 2;
    551             }
    552             else
    553                 rc = VERR_NO_MEMORY;
    554         }
    555         else
    556             rc = VERR_NO_MEMORY;
    557     }
    558 #else
    559     RT_NOREF(pThis, pSink, pStreamAV, pCfgReq, pCfgAcq);
    560     int rc = VERR_NOT_SUPPORTED;
    561 #endif /* VBOX_WITH_LIBOPUS */
    562 
    563     LogFlowFuncLeaveRC(rc);
    564     return rc;
    565 }
    566 
    567 
    568 /**
    569  * Destroys (closes) an audio output stream.
    570  *
    571  * @returns VBox status code.
    572  * @param   pThis               Driver instance.
    573  * @param   pStreamAV           Audio output stream to destroy.
    574  */
    575 static int avRecDestroyStreamOut(PDRVAUDIORECORDING pThis, PAVRECSTREAM pStreamAV)
    576 {
    577     RT_NOREF(pThis);
    578 
    579     if (pStreamAV->pCircBuf)
    580     {
    581         RTCircBufDestroy(pStreamAV->pCircBuf);
    582         pStreamAV->pCircBuf = NULL;
    583     }
    584 
    585     if (pStreamAV->pvSrcBuf)
    586     {
    587         Assert(pStreamAV->cbSrcBuf);
    588         RTMemFree(pStreamAV->pvSrcBuf);
    589         pStreamAV->pvSrcBuf = NULL;
    590         pStreamAV->cbSrcBuf = 0;
    591     }
    592 
    593     if (pStreamAV->pvDstBuf)
    594     {
    595         Assert(pStreamAV->cbDstBuf);
    596         RTMemFree(pStreamAV->pvDstBuf);
    597         pStreamAV->pvDstBuf = NULL;
    598         pStreamAV->cbDstBuf = 0;
    599     }
    600 
    601     return VINF_SUCCESS;
    602 }
    603 
    604 
    605 /**
    606  * Controls an audio output stream
    607  *
    608  * @returns VBox status code.
    609  * @param   pThis               Driver instance.
    610  * @param   pStreamAV           Audio output stream to control.
    611  * @param   enmStreamCmd        Stream command to issue.
    612  */
    613 static int avRecControlStreamOut(PDRVAUDIORECORDING pThis, PAVRECSTREAM pStreamAV, PDMAUDIOSTREAMCMD enmStreamCmd)
    614 {
    615     RT_NOREF(pThis, pStreamAV);
    616 
    617     int rc = VINF_SUCCESS;
    618 
    619     switch (enmStreamCmd)
    620     {
    621         case PDMAUDIOSTREAMCMD_ENABLE:
    622         case PDMAUDIOSTREAMCMD_DISABLE:
    623         case PDMAUDIOSTREAMCMD_RESUME:
    624         case PDMAUDIOSTREAMCMD_PAUSE:
    625             break;
    626 
    627         default:
    628             rc = VERR_NOT_SUPPORTED;
    629             break;
    630     }
    631 
    632     return rc;
    633 }
    634 
    635 
    636 /**
    637  * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
    638  */
    639 static DECLCALLBACK(int) drvAudioVideoRecHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
    640                                                           void *pvBuf, uint32_t uBufSize, uint32_t *puRead)
    641 {
    642     RT_NOREF(pInterface, pStream, pvBuf, uBufSize);
    643 
    644     if (puRead)
    645         *puRead = 0;
    646 
    647     return VINF_SUCCESS;
    648 }
    649 
    650 
    651 /**
    652  * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
    653  */
    654 static DECLCALLBACK(int) drvAudioVideoRecHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
    655                                                        const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
    656 {
    657     RT_NOREF(pInterface);
    658     PAVRECSTREAM pStreamAV = (PAVRECSTREAM)pStream;
    659     AssertPtrReturn(pStreamAV, VERR_INVALID_POINTER);
    660     AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
    661     AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
    662     AssertReturn(pcbWritten, VERR_INVALID_PARAMETER);
    663 
    664     int rc = VINF_SUCCESS;
    665 
    666     uint32_t cbWrittenTotal = 0;
    667 
    668     /*
    669      * Call the encoder with the data.
    670      */
    671 #ifdef VBOX_WITH_LIBOPUS
    672     PAVRECSINK pSink    = pStreamAV->pSink;
    673     AssertPtr(pSink);
    674     PAVRECCODEC pCodec  = &pSink->Codec;
    675     AssertPtr(pCodec);
    676     PRTCIRCBUF pCircBuf = pStreamAV->pCircBuf;
    677     AssertPtr(pCircBuf);
    678 
    679     void  *pvCircBuf;
    680     size_t cbCircBuf;
    681 
    682     uint32_t cbToWrite = cbBuf;
    683 
    684     /*
    685      * Fetch as much as we can into our internal ring buffer.
    686      */
    687     while (   cbToWrite
    688            && RTCircBufFree(pCircBuf))
    689     {
    690         RTCircBufAcquireWriteBlock(pCircBuf, cbToWrite, &pvCircBuf, &cbCircBuf);
    691 
    692         if (cbCircBuf)
    693         {
    694             memcpy(pvCircBuf, (uint8_t *)pvBuf + cbWrittenTotal, cbCircBuf),
    695             cbWrittenTotal += (uint32_t)cbCircBuf;
    696             Assert(cbToWrite >= cbCircBuf);
    697             cbToWrite      -= (uint32_t)cbCircBuf;
    698         }
    699 
    700         RTCircBufReleaseWriteBlock(pCircBuf, cbCircBuf);
    701 
    702         if (   RT_FAILURE(rc)
    703             || !cbCircBuf)
    704         {
    705             break;
    706         }
    707     }
    708 
    709     /*
    710      * Process our internal ring buffer and encode the data.
    711      */
    712 
    713     uint32_t cbSrc;
    714 
    715     /* Only encode data if we have data for the given time period (or more). */
    716     while (RTCircBufUsed(pCircBuf) >= pCodec->Opus.cbFrame)
    717     {
    718         LogFunc(("cbAvail=%zu, csFrame=%RU32, cbFrame=%RU32\n",
    719                  RTCircBufUsed(pCircBuf), pCodec->Opus.csFrame, pCodec->Opus.cbFrame));
    720 
    721         cbSrc = 0;
    722 
    723         while (cbSrc < pCodec->Opus.cbFrame)
    724         {
    725             RTCircBufAcquireReadBlock(pCircBuf, pCodec->Opus.cbFrame - cbSrc, &pvCircBuf, &cbCircBuf);
    726 
    727             if (cbCircBuf)
    728             {
    729                 memcpy((uint8_t *)pStreamAV->pvSrcBuf + cbSrc, pvCircBuf, cbCircBuf);
    730 
    731                 cbSrc += (uint32_t)cbCircBuf;
    732                 Assert(cbSrc <= pStreamAV->cbSrcBuf);
    733             }
    734 
    735             RTCircBufReleaseReadBlock(pCircBuf, cbCircBuf);
    736 
    737             if (!cbCircBuf)
    738                 break;
    739         }
    740 
    741 # ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
    742         RTFILE fh;
    743         RTFileOpen(&fh, VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH "DrvAudioVideoRec.pcm",
    744                    RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
    745         RTFileWrite(fh, pStreamAV->pvSrcBuf, cbSrc, NULL);
    746         RTFileClose(fh);
    747 # endif
    748 
    749         Assert(cbSrc == pCodec->Opus.cbFrame);
    750 
    751         /*
    752          * Opus always encodes PER "OPUS FRAME", that is, exactly 2.5, 5, 10, 20, 40 or 60 ms of audio data.
    753          *
    754          * A packet can have up to 120ms worth of audio data.
    755          * Anything > 120ms of data will result in a "corrupted package" error message by
    756          * by decoding application.
    757          */
    758 
    759         /* Call the encoder to encode one "Opus frame" per iteration. */
    760         opus_int32 cbWritten = opus_encode(pSink->Codec.Opus.pEnc,
    761                                            (opus_int16 *)pStreamAV->pvSrcBuf, pCodec->Opus.csFrame,
    762                                            (uint8_t *)pStreamAV->pvDstBuf, (opus_int32)pStreamAV->cbDstBuf);
    763         if (cbWritten > 0)
    764         {
    765             /* Get overall frames encoded. */
    766             const uint32_t cEncFrames     = opus_packet_get_nb_frames((uint8_t *)pStreamAV->pvDstBuf, cbWritten);
    767 
    768 # ifdef VBOX_WITH_STATISTICS
    769             pSink->Codec.STAM.cEncFrames += cEncFrames;
    770             pSink->Codec.STAM.msEncTotal += pSink->Codec.Opus.msFrame * cEncFrames;
    771 # endif
    772             Assert((uint32_t)cbWritten <= (uint32_t)pStreamAV->cbDstBuf);
    773             const uint32_t cbDst = RT_MIN((uint32_t)cbWritten, (uint32_t)pStreamAV->cbDstBuf);
    774 
    775             Assert(cEncFrames == 1);
    776 
    777             if (pStreamAV->uLastPTSMs == 0)
    778                 pStreamAV->uLastPTSMs = RTTimeProgramMilliTS(); /* We want the absolute time (in ms) since program start. */
    779 
    780             const uint64_t uDurationMs = pSink->Codec.Opus.msFrame * cEncFrames;
    781             const uint64_t uPTSMs      = pStreamAV->uLastPTSMs;
    782 
    783             pStreamAV->uLastPTSMs += uDurationMs;
    784 
    785             switch (pSink->Con.Parms.enmType)
    786             {
    787                 case AVRECCONTAINERTYPE_MAIN_CONSOLE:
    788                 {
    789                     HRESULT hr = pSink->Con.Main.pConsole->i_recordingSendAudio(pStreamAV->pvDstBuf, cbDst, uPTSMs);
    790                     Assert(hr == S_OK);
    791                     RT_NOREF(hr);
    792 
    793                     break;
    794                 }
    795 
    796                 case AVRECCONTAINERTYPE_WEBM:
    797                 {
    798                     WebMWriter::BlockData_Opus blockData = { pStreamAV->pvDstBuf, cbDst, uPTSMs };
    799                     rc = pSink->Con.WebM.pWebM->WriteBlock(pSink->Con.WebM.uTrack, &blockData, sizeof(blockData));
    800                     AssertRC(rc);
    801 
    802                     break;
    803                 }
    804 
    805                 default:
    806                     AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
    807                     break;
    808             }
    809         }
    810         else if (cbWritten < 0)
    811         {
    812             AssertMsgFailed(("Encoding failed: %s\n", opus_strerror(cbWritten)));
    813             rc = VERR_INVALID_PARAMETER;
    814         }
    815 
    816         if (RT_FAILURE(rc))
    817             break;
    818     }
    819 
    820     *pcbWritten = cbWrittenTotal;
    821 #else
    822     /* Report back all data as being processed. */
    823     *pcbWritten = cbBuf;
    824 
    825     rc = VERR_NOT_SUPPORTED;
    826 #endif /* VBOX_WITH_LIBOPUS */
    827 
    828     LogFlowFunc(("csReadTotal=%RU32, rc=%Rrc\n", cbWrittenTotal, rc));
    829     return rc;
    830 }
    831 
    832 
    833 /**
    834  * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
    835  */
    836 static DECLCALLBACK(int) drvAudioVideoRecHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
    837 {
    838     RT_NOREF(pInterface);
    839     AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
    840 
    841     RTStrPrintf2(pBackendCfg->szName, sizeof(pBackendCfg->szName), "VideoRec");
    842 
    843     pBackendCfg->cbStreamOut    = sizeof(AVRECSTREAM);
    844     pBackendCfg->cbStreamIn     = 0;
    845     pBackendCfg->cMaxStreamsIn  = 0;
    846     pBackendCfg->cMaxStreamsOut = UINT32_MAX;
    847 
    848     return VINF_SUCCESS;
    849 }
    850 
    851 
    852 /**
    853  * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
    854  */
    855 static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvAudioVideoRecHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
    856 {
    857     RT_NOREF(enmDir);
    858     AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
    859 
    860     return PDMAUDIOBACKENDSTS_RUNNING;
    861 }
    862 
    863 
    864 /**
    865  * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
    866  */
    867 static DECLCALLBACK(int) drvAudioVideoRecHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
    868                                                          PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
    869 {
    870     AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
    871     AssertPtrReturn(pCfgReq,    VERR_INVALID_POINTER);
    872     AssertPtrReturn(pCfgAcq,    VERR_INVALID_POINTER);
    873 
    874     if (pCfgReq->enmDir == PDMAUDIODIR_IN)
    875         return VERR_NOT_SUPPORTED;
    876 
    877     AssertPtrReturn(pStream,    VERR_INVALID_POINTER);
    878 
    879     PDRVAUDIORECORDING pThis     = PDMIHOSTAUDIO_2_DRVAUDIORECORDING(pInterface);
    880     PAVRECSTREAM      pStreamAV = (PAVRECSTREAM)pStream;
    881 
    882     /* For now we only have one sink, namely the driver's one.
    883      * Later each stream could have its own one, to e.g. router different stream to different sinks .*/
    884     PAVRECSINK pSink = &pThis->Sink;
    885 
    886     int rc = avRecCreateStreamOut(pThis, pStreamAV, pSink, pCfgReq, pCfgAcq);
    887     if (RT_SUCCESS(rc))
    888     {
    889         pStreamAV->pCfg = PDMAudioStrmCfgDup(pCfgAcq);
    890         if (!pStreamAV->pCfg)
    891             rc = VERR_NO_MEMORY;
    892     }
    893 
    894     return rc;
    895 }
    896 
    897 
    898 /**
    899  * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
    900  */
    901 static DECLCALLBACK(int) drvAudioVideoRecHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
    902 {
    903     AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
    904     AssertPtrReturn(pStream,    VERR_INVALID_POINTER);
    905 
    906     PDRVAUDIORECORDING pThis     = PDMIHOSTAUDIO_2_DRVAUDIORECORDING(pInterface);
    907     PAVRECSTREAM      pStreamAV = (PAVRECSTREAM)pStream;
    908 
    909     if (!pStreamAV->pCfg) /* Not (yet) configured? Skip. */
    910         return VINF_SUCCESS;
    911 
    912     int rc = VINF_SUCCESS;
    913 
    914     if (pStreamAV->pCfg->enmDir == PDMAUDIODIR_OUT)
    915         rc = avRecDestroyStreamOut(pThis, pStreamAV);
    916 
    917     if (RT_SUCCESS(rc))
    918     {
    919         PDMAudioStrmCfgFree(pStreamAV->pCfg);
    920         pStreamAV->pCfg = NULL;
    921     }
    922 
    923     return rc;
    924 }
    925 
    926 
    927 /**
    928  * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
    929  */
    930 static DECLCALLBACK(int) drvAudioVideoRecHA_StreamControl(PPDMIHOSTAUDIO pInterface,
    931                                                           PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
    932 {
    933     AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
    934     AssertPtrReturn(pStream,    VERR_INVALID_POINTER);
    935 
    936     PDRVAUDIORECORDING pThis     = PDMIHOSTAUDIO_2_DRVAUDIORECORDING(pInterface);
    937     PAVRECSTREAM      pStreamAV = (PAVRECSTREAM)pStream;
    938 
    939     if (!pStreamAV->pCfg) /* Not (yet) configured? Skip. */
    940         return VINF_SUCCESS;
    941 
    942     if (pStreamAV->pCfg->enmDir == PDMAUDIODIR_OUT)
    943         return avRecControlStreamOut(pThis, pStreamAV, enmStreamCmd);
    944 
    945     return VINF_SUCCESS;
    946 }
    947 
    948 
    949 /**
    950  * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
    951  */
    952 static DECLCALLBACK(uint32_t) drvAudioVideoRecHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
    953 {
    954     RT_NOREF(pInterface, pStream);
    955 
    956     return 0; /* Video capturing does not provide any input. */
    957 }
    958 
    959 
    960 /**
    961  * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
    962  */
    963 static DECLCALLBACK(uint32_t) drvAudioVideoRecHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
    964 {
    965     RT_NOREF(pInterface, pStream);
    966 
    967     return UINT32_MAX;
    968 }
    969 
    970 
    971 /**
    972  * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus}
    973  */
    974 static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvAudioVideoRecHA_StreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
    975 {
    976     RT_NOREF(pInterface, pStream);
    977 
    978     return PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED | PDMAUDIOSTREAMSTS_FLAGS_ENABLED;
    979 }
    980 
    981 
    982 /**
    983  * @interface_method_impl{PDMIBASE,pfnQueryInterface}
    984  */
    985 static DECLCALLBACK(void *) drvAudioVideoRecQueryInterface(PPDMIBASE pInterface, const char *pszIID)
    986 {
    987     PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
    988     PDRVAUDIORECORDING pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIORECORDING);
    989 
    990     PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
    991     PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
    992     return NULL;
    993 }
    994 
    995 
    996 AudioVideoRec::AudioVideoRec(Console *pConsole)
    997     : AudioDriver(pConsole)
    998     , mpDrv(NULL)
    999 {
    1000 }
    1001 
    1002 
    1003 AudioVideoRec::~AudioVideoRec(void)
    1004 {
    1005     if (mpDrv)
    1006     {
    1007         mpDrv->pAudioVideoRec = NULL;
    1008         mpDrv = NULL;
    1009     }
    1010 }
    1011 
    1012 
    1013 /**
    1014  * Applies a video recording configuration to this driver instance.
    1015  *
    1016  * @returns VBox status code.
    1017  * @param   Settings        Capturing configuration to apply.
    1018  */
    1019 int AudioVideoRec::applyConfiguration(const settings::RecordingSettings &Settings)
    1020 {
    1021     /** @todo Do some validation here. */
    1022     mVideoRecCfg = Settings; /* Note: Does have an own copy operator. */
    1023     return VINF_SUCCESS;
    1024 }
    1025 
    1026 
    1027 /**
    1028  * @copydoc AudioDriver::configureDriver
    1029  */
    1030 int AudioVideoRec::configureDriver(PCFGMNODE pLunCfg)
    1031 {
    1032     int rc = CFGMR3InsertInteger(pLunCfg, "Object",    (uintptr_t)mpConsole->i_recordingGetAudioDrv());
    1033     AssertRCReturn(rc, rc);
    1034     rc = CFGMR3InsertInteger(pLunCfg, "ObjectConsole", (uintptr_t)mpConsole);
    1035     AssertRCReturn(rc, rc);
    1036 
    1037     /** @todo For now we're using the configuration of the first screen here audio-wise. */
    1038     Assert(mVideoRecCfg.mapScreens.size() >= 1);
    1039     const settings::RecordingScreenSettings &Screen0Settings = mVideoRecCfg.mapScreens[0];
    1040 
    1041     rc = CFGMR3InsertInteger(pLunCfg, "ContainerType", (uint64_t)Screen0Settings.enmDest);
    1042     AssertRCReturn(rc, rc);
    1043     if (Screen0Settings.enmDest == RecordingDestination_File)
    1044     {
    1045         rc = CFGMR3InsertString(pLunCfg, "ContainerFileName", Utf8Str(Screen0Settings.File.strName).c_str());
    1046         AssertRCReturn(rc, rc);
    1047     }
    1048     rc = CFGMR3InsertInteger(pLunCfg, "CodecHz", Screen0Settings.Audio.uHz);
    1049     AssertRCReturn(rc, rc);
    1050     rc = CFGMR3InsertInteger(pLunCfg, "CodecBits", Screen0Settings.Audio.cBits);
    1051     AssertRCReturn(rc, rc);
    1052     rc = CFGMR3InsertInteger(pLunCfg, "CodecChannels", Screen0Settings.Audio.cChannels);
    1053     AssertRCReturn(rc, rc);
    1054     rc = CFGMR3InsertInteger(pLunCfg, "CodecBitrate", 0); /* Let Opus decide for now. */
    1055     AssertRCReturn(rc, rc);
    1056 
    1057     return AudioDriver::configureDriver(pLunCfg);
    1058 }
    1059 
    1060 
    1061 /**
    1062  * @interface_method_impl{PDMDRVREG,pfnPowerOff}
    1063  */
    1064 /*static*/ DECLCALLBACK(void) AudioVideoRec::drvPowerOff(PPDMDRVINS pDrvIns)
    1065 {
    1066     PDRVAUDIORECORDING pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIORECORDING);
    1067     LogFlowFuncEnter();
    1068     avRecSinkShutdown(&pThis->Sink);
    1069 }
    1070 
    1071 
    1072 /**
    1073  * @interface_method_impl{PDMDRVREG,pfnDestruct}
    1074  */
    1075 /*static*/ DECLCALLBACK(void) AudioVideoRec::drvDestruct(PPDMDRVINS pDrvIns)
    1076 {
    1077     PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
    1078     PDRVAUDIORECORDING pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIORECORDING);
    1079 
    1080     LogFlowFuncEnter();
    1081 
    1082     switch (pThis->ContainerParms.enmType)
    1083     {
    1084         case AVRECCONTAINERTYPE_WEBM:
    1085         {
    1086             avRecSinkShutdown(&pThis->Sink);
    1087             RTStrFree(pThis->ContainerParms.WebM.pszFile);
    1088             break;
    1089         }
    1090 
    1091         default:
    1092             break;
    1093     }
    1094 
    1095     /*
    1096      * If the AudioVideoRec object is still alive, we must clear it's reference to
    1097      * us since we'll be invalid when we return from this method.
    1098      */
    1099     if (pThis->pAudioVideoRec)
    1100     {
    1101         pThis->pAudioVideoRec->mpDrv = NULL;
    1102         pThis->pAudioVideoRec = NULL;
    1103     }
    1104 
    1105     LogFlowFuncLeave();
    11061070}
    11071071
     
    11331097    /* IHostAudio */
    11341098    pThis->IHostAudio.pfnGetConfig          = drvAudioVideoRecHA_GetConfig;
     1099    pThis->IHostAudio.pfnGetDevices         = NULL;
    11351100    pThis->IHostAudio.pfnGetStatus          = drvAudioVideoRecHA_GetStatus;
    11361101    pThis->IHostAudio.pfnStreamCreate       = drvAudioVideoRecHA_StreamCreate;
     
    11391104    pThis->IHostAudio.pfnStreamGetReadable  = drvAudioVideoRecHA_StreamGetReadable;
    11401105    pThis->IHostAudio.pfnStreamGetWritable  = drvAudioVideoRecHA_StreamGetWritable;
     1106    pThis->IHostAudio.pfnStreamGetPending   = NULL;
    11411107    pThis->IHostAudio.pfnStreamGetStatus    = drvAudioVideoRecHA_StreamGetStatus;
    11421108    pThis->IHostAudio.pfnStreamPlay         = drvAudioVideoRecHA_StreamPlay;
    11431109    pThis->IHostAudio.pfnStreamCapture      = drvAudioVideoRecHA_StreamCapture;
    1144     pThis->IHostAudio.pfnGetDevices         = NULL;
    1145     pThis->IHostAudio.pfnStreamGetPending   = NULL;
    11461110
    11471111    /*
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