VirtualBox

Changeset 34906 in vbox for trunk/src/VBox/Devices


Ignore:
Timestamp:
Dec 9, 2010 4:29:49 PM (14 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
68723
Message:

Initial audio filter implementation, which is used for audio input via remote desktop server.

Location:
trunk/src/VBox/Devices
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Devices/Audio/audio.c

    r33400 r34906  
    15511551
    15521552    if (s->drv_opaque) {
     1553        /* Filter must be installed before initializing voices. */
     1554        drv = filteraudio_install(drv, s->drv_opaque);
    15531555        audio_init_nb_voices_out (s, drv);
    15541556        audio_init_nb_voices_in (s, drv);
  • trunk/src/VBox/Devices/Audio/audio_int.h

    r12977 r34906  
    314314                                    unsigned cSamples);
    315315
     316/*
     317 * Filter interface.
     318 */
     319typedef DECLCALLBACK(int) FNAUDIOINPUTCALLBACK(void* pvCtx, uint32_t cbSamples, const void *pvSamples);
     320typedef FNAUDIOINPUTCALLBACK *PFNAUDIOINPUTCALLBACK;
     321
     322int filter_output_intercepted(void);
     323int filter_output_begin(void **ppvOutputCtx, struct audio_pcm_info *pinfo, int samples);
     324void filter_output_end(void *pvOutputCtx);
     325
     326int filter_input_intercepted(void);
     327int filter_input_begin(void **ppvInputCtx, PFNAUDIOINPUTCALLBACK pfnCallback, void *pvCallback, HWVoiceIn *phw, int samples);
     328void filter_input_end(void *pvInputCtx);
     329
     330struct audio_driver *filteraudio_install(struct audio_driver *pDrv, void *pDrvOpaque);
     331
    316332#endif /* audio_int.h */
  • trunk/src/VBox/Devices/Audio/audiosniffer.c

    r32339 r34906  
    55
    66/*
    7  * Copyright (C) 2006-2007 Oracle Corporation
     7 * Copyright (C) 2006-2010 Oracle Corporation
    88 *
    99 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    2222
    2323#include <VBox/log.h>
     24#include <iprt/asm.h>
    2425#include <iprt/assert.h>
    2526#include <iprt/uuid.h>
     
    4041    /** Whether audio should reach the host driver too. */
    4142    bool fKeepHostAudio;
     43
     44    /** Whether audio input operations should be forwarded to the connector. */
     45    bool fInterceptAudioInput;
    4246
    4347    /** Pointer to device instance. */
     
    100104
    101105/*
    102  * Audio Sniffer PDM device.
    103  */
     106 * Filter interface.
     107 */
     108
     109/* Internal audio input context, which makes sure that:
     110 *   - the filter audio input callback is not called after the filter has issued filter_input_end;
     111 *   - maintains internal information and state of the audio stream.
     112 */
     113typedef struct SnifferInputCtx
     114{
     115    /* Whether the context is still in use by the filter or I'll check. */
     116    int32_t volatile cRefs;
     117
     118    /* The filter callback for incoming audio data. */
     119    PFNAUDIOINPUTCALLBACK pfnFilterCallback;
     120    void *pvFilterCallback;
     121
     122    /* Whether the stream has been ended by the filter. */
     123    bool fEndedByFilter;
     124
     125    /* Context pointer returned by pfnAudioInputBegin. */
     126    void *pvUserCtx;
     127
     128    /* Audio format used for recording. */
     129    HWVoiceIn *phw;
     130
     131    /* Number of bytes per frame (bitsPerSample * channels) of the actual input format. */
     132    uint32_t cBytesPerFrame;
     133
     134    /* Frequency of the actual audio format. */
     135    int iFreq;
     136
     137    /* Convertion from the actual input format to st_sample_t. */
     138    t_sample *conv;
     139
     140    /* If the actual format frequence differs from the requested format, this is not NULL. */
     141    void *rate;
     142
     143} SnifferInputCtx;
     144
     145/*
     146 * Filter audio output.
     147 */
     148
     149/* Whether the filter should intercept audio output. */
     150int filter_output_intercepted(void)
     151{
     152    return 0; /* @todo Not implemented yet.*/
     153}
     154
     155/* Filter informs that an audio output is starting. */
     156int filter_output_begin(void **ppvOutputCtx, struct audio_pcm_info *pinfo, int samples)
     157{
     158    return VERR_NOT_SUPPORTED; /* @todo Not implemented yet.*/
     159}
     160
     161/* Filter informs that the audio output has been stopped. */
     162void filter_output_end(void *pvOutputCtx)
     163{
     164    return; /* @todo Not implemented yet.*/
     165}
     166
     167/*
     168 * Filter audio input.
     169 */
     170
     171/* Whether the filter should intercept audio input. */
     172int filter_input_intercepted(void)
     173{
     174    if (!g_pData || !g_pData->pDrv)
     175    {
     176        return 0;
     177    }
     178
     179    return g_pData->fInterceptAudioInput;
     180}
     181
     182/* Filter informs that an audio input is starting. */
     183int filter_input_begin (void **ppvInputCtx, PFNAUDIOINPUTCALLBACK pfnCallback, void *pvCallback, HWVoiceIn *phw, int cSamples)
     184{
     185    int rc = VINF_SUCCESS;
     186
     187    SnifferInputCtx *pCtx = NULL;
     188
     189    if (!g_pData || !g_pData->pDrv)
     190    {
     191        return VERR_NOT_SUPPORTED;
     192    }
     193
     194    pCtx = (SnifferInputCtx *)RTMemAlloc(sizeof(SnifferInputCtx));
     195
     196    if (!pCtx)
     197    {
     198        return VERR_NO_MEMORY;
     199    }
     200
     201    pCtx->cRefs = 2; /* Context is used by both the filter and the user. */
     202    pCtx->pfnFilterCallback = pfnCallback;
     203    pCtx->pvFilterCallback = pvCallback;
     204    pCtx->fEndedByFilter = false;
     205    pCtx->pvUserCtx = NULL;
     206    pCtx->phw = phw;
     207    pCtx->cBytesPerFrame = 1;
     208    pCtx->iFreq = 0;
     209    pCtx->conv = NULL;
     210    pCtx->rate = NULL;
     211
     212    rc = g_pData->pDrv->pfnAudioInputBegin (g_pData->pDrv,
     213                                            &pCtx->pvUserCtx,      /* Returned by the pDrv. */
     214                                            pCtx,
     215                                            cSamples,              /* How many samples in one block is preferred. */
     216                                            phw->info.freq,        /* Required frequency. */
     217                                            phw->info.nchannels,   /* Number of audio channels. */
     218                                            phw->info.bits);       /* A sample size in one channel, samples are signed. */
     219
     220    if (RT_SUCCESS(rc))
     221    {
     222        *ppvInputCtx = pCtx;
     223    }
     224    else
     225    {
     226        RTMemFree(pCtx);
     227    }
     228
     229    Log(("input_begin rc = %Rrc\n", rc));
     230
     231    return rc;
     232}
     233
     234/* Filter informs that the audio input must be stopped. */
     235void filter_input_end(void *pvCtx)
     236{
     237    int32_t c;
     238
     239    SnifferInputCtx *pCtx = (SnifferInputCtx *)pvCtx;
     240
     241    void *pvUserCtx = pCtx->pvUserCtx;
     242
     243    pCtx->fEndedByFilter = true;
     244
     245    c = ASMAtomicDecU32(&pCtx->cRefs);
     246
     247    if (c == 0)
     248    {
     249        /* The caller will not use this context anymore. */
     250        if (pCtx->rate)
     251        {
     252            st_rate_stop (pCtx->rate);
     253        }
     254        RTMemFree(pCtx);
     255        pCtx = NULL;
     256    }
     257
     258    if (!g_pData || !g_pData->pDrv)
     259    {
     260        AssertFailed();
     261        return;
     262    }
     263
     264    g_pData->pDrv->pfnAudioInputEnd (g_pData->pDrv,
     265                                     pvUserCtx);
     266
     267    Log(("input_end\n"));
     268}
     269
     270
     271/*
     272 * Audio PDM device.
     273 */
     274static DECLCALLBACK(int) iface_AudioInputIntercept (PPDMIAUDIOSNIFFERPORT pInterface, bool fIntercept)
     275{
     276    AUDIOSNIFFERSTATE *pThis = RT_FROM_MEMBER(pInterface, AUDIOSNIFFERSTATE, IPort);
     277
     278    Assert(g_pData == pThis);
     279
     280    pThis->fInterceptAudioInput = fIntercept;
     281
     282    return VINF_SUCCESS;
     283}
     284
     285static DECLCALLBACK(int) iface_AudioInputEventBegin (PPDMIAUDIOSNIFFERPORT pInterface,
     286                                                     void *pvContext,
     287                                                     int iSampleHz,
     288                                                     int cChannels,
     289                                                     int cBits,
     290                                                     bool fUnsigned)
     291{
     292    int bitIdx;
     293
     294    AUDIOSNIFFERSTATE *pThis = RT_FROM_MEMBER(pInterface, AUDIOSNIFFERSTATE, IPort);
     295
     296    int rc = VINF_SUCCESS;
     297
     298    SnifferInputCtx *pCtx = (SnifferInputCtx *)pvContext;
     299
     300    Log(("FilterAudio: AudioInputEventBegin: %dHz,%dch,%dbits,%d ended %d\n",
     301         iSampleHz, cChannels, cBits, fUnsigned, pCtx->fEndedByFilter));
     302
     303    Assert(g_pData == pThis);
     304
     305    /* Prepare a format convertion for the actually used format. */
     306    pCtx->cBytesPerFrame = ((cBits + 7) / 8) * cChannels;
     307
     308    if (cBits == 16)
     309    {
     310        bitIdx = 1;
     311    }
     312    else if (cBits == 32)
     313    {
     314        bitIdx = 2;
     315    }
     316    else
     317    {
     318        bitIdx = 0;
     319    }
     320
     321    pCtx->conv = mixeng_conv[(cChannels == 2)? 1: 0] /* stereo */
     322                            [!fUnsigned]             /* sign */
     323                            [0]                      /* big endian */
     324                            [bitIdx];                /* bits */
     325
     326    if (iSampleHz && iSampleHz != pCtx->phw->info.freq)
     327    {
     328        pCtx->rate = st_rate_start (iSampleHz, pCtx->phw->info.freq);
     329        pCtx->iFreq = iSampleHz;
     330    }
     331
     332    return rc;
     333}
     334
     335static DECLCALLBACK(int) iface_AudioInputEventData (PPDMIAUDIOSNIFFERPORT pInterface,
     336                                                    void *pvContext,
     337                                                    const void *pvData,
     338                                                    uint32_t cbData)
     339{
     340    AUDIOSNIFFERSTATE *pThis = RT_FROM_MEMBER(pInterface, AUDIOSNIFFERSTATE, IPort);
     341
     342    int rc = VINF_SUCCESS;
     343
     344    SnifferInputCtx *pCtx = (SnifferInputCtx *)pvContext;
     345
     346    Log(("FilterAudio: AudioInputEventData: pvData %p. cbData %d, ended %d\n", pvData, cbData, pCtx->fEndedByFilter));
     347
     348    Assert(g_pData == pThis);
     349
     350    if (   !pCtx->fEndedByFilter
     351        && pCtx->conv)
     352    {
     353        /* Convert PCM samples to st_sample_t.
     354         * And then apply rate convertion if necessary.
     355         */
     356        /* @todo Optimization: allocate ps buffer once per context and reallocate if cbData changes. */
     357        uint32_t cs = cbData / pCtx->cBytesPerFrame;
     358        st_sample_t *ps = (st_sample_t *)RTMemAlloc(cs * sizeof(st_sample_t));
     359        if (ps)
     360        {
     361            void *pvSamplesRateDst = NULL;
     362
     363            void *pvSamples = NULL;
     364            uint32_t cbSamples = 0;
     365
     366            pCtx->conv(ps, pvData, cs, &nominal_volume);
     367
     368            if (pCtx->rate)
     369            {
     370                uint32_t csConverted = (cs * pCtx->phw->info.freq) / pCtx->iFreq;
     371                pvSamplesRateDst = RTMemAlloc(csConverted * sizeof(st_sample_t));
     372
     373                if (pvSamplesRateDst)
     374                {
     375                    int csSrc = cs;
     376                    int csDst = csConverted;
     377
     378                    st_rate_flow (pCtx->rate,
     379                                  ps, (st_sample_t *)pvSamplesRateDst,
     380                                  &csSrc, &csDst);
     381
     382                    pvSamples = pvSamplesRateDst;
     383                    cbSamples = csDst * sizeof(st_sample_t);
     384                }
     385                else
     386                {
     387                    rc = VERR_NO_MEMORY;
     388                }
     389            }
     390            else
     391            {
     392                pvSamples = ps;
     393                cbSamples = cs * sizeof(st_sample_t);
     394            }
     395
     396            if (cbSamples)
     397            {
     398                rc = pCtx->pfnFilterCallback(pCtx->pvFilterCallback, cbSamples, pvSamples);
     399            }
     400
     401            RTMemFree(pvSamplesRateDst);
     402            RTMemFree(ps);
     403        }
     404        else
     405        {
     406            rc = VERR_NO_MEMORY;
     407        }
     408    }
     409
     410    return rc;
     411}
     412
     413static DECLCALLBACK(void) iface_AudioInputEventEnd (PPDMIAUDIOSNIFFERPORT pInterface,
     414                                                    void *pvContext)
     415{
     416    int32_t c;
     417
     418    AUDIOSNIFFERSTATE *pThis = RT_FROM_MEMBER(pInterface, AUDIOSNIFFERSTATE, IPort);
     419
     420    SnifferInputCtx *pCtx = (SnifferInputCtx *)pvContext;
     421
     422    Log(("FilterAudio: AudioInputEventEnd: ended %d\n", pCtx->fEndedByFilter));
     423
     424    Assert(g_pData == pThis);
     425
     426    c = ASMAtomicDecU32(&pCtx->cRefs);
     427
     428    if (c == 0)
     429    {
     430        /* The caller will not use this context anymore. */
     431        if (pCtx->rate)
     432        {
     433            st_rate_stop (pCtx->rate);
     434        }
     435        RTMemFree(pCtx);
     436    }
     437}
     438
    104439
    105440static DECLCALLBACK(int) iface_Setup (PPDMIAUDIOSNIFFERPORT pInterface, bool fEnable, bool fKeepHostAudio)
     
    159494     * Validate configuration.
    160495     */
    161     if (!CFGMR3AreValuesValid(pCfgHandle, "\0"))
     496    if (!CFGMR3AreValuesValid(pCfgHandle, "InterceptAudioInput\0"))
    162497    {
    163498        return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
     
    170505    pThis->fKeepHostAudio = true;
    171506    pThis->pDrv = NULL;
     507    rc = CFGMR3QueryBoolDef(pCfgHandle, "InterceptAudioInput", &pThis->fInterceptAudioInput, false);
     508    if (RT_FAILURE(rc))
     509        return PDMDEV_SET_ERROR(pDevIns, rc,
     510                                N_("Configuration error: Failed to get the \"YieldOnLSRRead\" value"));
    172511
    173512    /*
     
    179518    /* Audio Sniffer port */
    180519    pThis->IPort.pfnSetup = iface_Setup;
     520    pThis->IPort.pfnAudioInputIntercept = iface_AudioInputIntercept;
     521    pThis->IPort.pfnAudioInputEventBegin = iface_AudioInputEventBegin;
     522    pThis->IPort.pfnAudioInputEventData = iface_AudioInputEventData;
     523    pThis->IPort.pfnAudioInputEventEnd = iface_AudioInputEventEnd;
    181524
    182525    /*
  • trunk/src/VBox/Devices/Makefile.kmk

    r34876 r34906  
    794794        Audio/mixeng.c \
    795795        Audio/noaudio.c \
     796        Audio/filteraudio.c \
    796797        Input/DrvKeyboardQueue.cpp \
    797798        Input/DrvMouseQueue.cpp \
Note: See TracChangeset for help on using the changeset viewer.

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