~ubuntu-branches/ubuntu/raring/virtualbox-ose/raring

« back to all changes in this revision

Viewing changes to src/VBox/Devices/Audio/audiosniffer.c

  • Committer: Bazaar Package Importer
  • Author(s): Felix Geyer
  • Date: 2011-01-30 23:27:25 UTC
  • mfrom: (0.3.12 upstream)
  • Revision ID: james.westby@ubuntu.com-20110130232725-2ouajjd2ggdet0zd
Tags: 4.0.2-dfsg-1ubuntu1
* Merge from Debian unstable, remaining changes:
  - Add Apport hook.
    - debian/virtualbox-ose.files/source_virtualbox-ose.py
    - debian/virtualbox-ose.install
  - Drop *-source packages.
* Drop ubuntu-01-fix-build-gcc45.patch, fixed upstream.
* Drop ubuntu-02-as-needed.patch, added to the Debian package.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* $Id: audiosniffer.c $ */
 
1
/* $Id: audiosniffer.c 35402 2011-01-04 14:59:31Z vboxsync $ */
2
2
/** @file
3
3
 * VBox audio device: Audio sniffer device
4
4
 */
5
5
 
6
6
/*
7
 
 * Copyright (C) 2006-2007 Oracle Corporation
 
7
 * Copyright (C) 2006-2010 Oracle Corporation
8
8
 *
9
9
 * This file is part of VirtualBox Open Source Edition (OSE), as
10
10
 * available from http://www.virtualbox.org. This file is free software;
17
17
 
18
18
#define LOG_GROUP LOG_GROUP_DEV_AUDIO
19
19
#define AUDIO_CAP "sniffer"
20
 
#include <VBox/pdm.h>
 
20
#include <VBox/vmm/pdm.h>
21
21
#include <VBox/err.h>
22
22
 
23
23
#include <VBox/log.h>
 
24
#include <iprt/asm.h>
24
25
#include <iprt/assert.h>
25
26
#include <iprt/uuid.h>
26
27
#include <iprt/string.h>
27
28
#include <iprt/alloc.h>
28
29
 
29
 
#include "Builtins.h"
30
 
#include "../../vl_vbox.h"
 
30
#include "VBoxDD.h"
 
31
#include "vl_vbox.h"
31
32
 
32
33
#include "audio.h"
33
34
#include "audio_int.h"
40
41
    /** Whether audio should reach the host driver too. */
41
42
    bool fKeepHostAudio;
42
43
 
 
44
    /** Whether audio input operations should be forwarded to the connector. */
 
45
    bool fInterceptAudioInput;
 
46
 
43
47
    /** Pointer to device instance. */
44
48
    PPDMDEVINS                   pDevIns;
45
49
 
99
103
 
100
104
 
101
105
/*
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
 */
 
113
typedef 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
    /* Temporary buffer for st_sample_t representation of the input audio data. */
 
144
    void *pvSamplesBuffer;
 
145
    uint32_t cbSamplesBufferAllocated;
 
146
 
 
147
    /* Temporary buffer for frequency conversion. */
 
148
    void *pvRateBuffer;
 
149
    uint32_t cbRateBufferAllocated;
 
150
 
 
151
} SnifferInputCtx;
 
152
 
 
153
static void ictxDelete(SnifferInputCtx *pCtx)
 
154
{
 
155
    /* The caller will not use this context anymore. */
 
156
    if (pCtx->rate)
 
157
    {
 
158
        st_rate_stop (pCtx->rate);
 
159
    }
 
160
 
 
161
    RTMemFree(pCtx->pvSamplesBuffer);
 
162
    RTMemFree(pCtx->pvRateBuffer);
 
163
 
 
164
    memset(pCtx, 0, sizeof(*pCtx));
 
165
    RTMemFree(pCtx);
 
166
}
 
167
 
 
168
static void ictxReallocSamplesBuffer(SnifferInputCtx *pCtx, uint32_t cs)
 
169
{
 
170
    uint32_t cbBuffer = cs * sizeof(st_sample_t);
 
171
 
 
172
    if (cbBuffer > pCtx->cbSamplesBufferAllocated)
 
173
    {
 
174
        RTMemFree(pCtx->pvSamplesBuffer);
 
175
 
 
176
        pCtx->pvSamplesBuffer = RTMemAlloc(cbBuffer);
 
177
        if (pCtx->pvSamplesBuffer)
 
178
        {
 
179
            pCtx->cbSamplesBufferAllocated = cbBuffer;
 
180
        }
 
181
        else
 
182
        {
 
183
            pCtx->cbSamplesBufferAllocated = 0;
 
184
        }
 
185
    }
 
186
}
 
187
 
 
188
static void ictxReallocRateBuffer(SnifferInputCtx *pCtx, uint32_t cs)
 
189
{
 
190
    uint32_t cbBuffer = cs * sizeof(st_sample_t);
 
191
 
 
192
    if (cbBuffer > pCtx->cbRateBufferAllocated)
 
193
    {
 
194
        RTMemFree(pCtx->pvRateBuffer);
 
195
 
 
196
        pCtx->pvRateBuffer = RTMemAlloc(cbBuffer);
 
197
        if (pCtx->pvRateBuffer)
 
198
        {
 
199
            pCtx->cbRateBufferAllocated = cbBuffer;
 
200
        }
 
201
        else
 
202
        {
 
203
            pCtx->cbRateBufferAllocated = 0;
 
204
        }
 
205
    }
 
206
}
 
207
 
 
208
 
 
209
/*
 
210
 * Filter audio output.
 
211
 */
 
212
 
 
213
/* Whether the filter should intercept audio output. */
 
214
int filter_output_intercepted(void)
 
215
{
 
216
    return 0; /* @todo Not implemented yet.*/
 
217
}
 
218
 
 
219
/* Filter informs that an audio output is starting. */
 
220
int filter_output_begin(void **ppvOutputCtx, struct audio_pcm_info *pinfo, int samples)
 
221
{
 
222
    return VERR_NOT_SUPPORTED; /* @todo Not implemented yet.*/
 
223
}
 
224
 
 
225
/* Filter informs that the audio output has been stopped. */
 
226
void filter_output_end(void *pvOutputCtx)
 
227
{
 
228
    return; /* @todo Not implemented yet.*/
 
229
}
 
230
 
 
231
/*
 
232
 * Filter audio input.
 
233
 */
 
234
 
 
235
/* Whether the filter should intercept audio input. */
 
236
int filter_input_intercepted(void)
 
237
{
 
238
    if (!g_pData || !g_pData->pDrv)
 
239
    {
 
240
        return 0;
 
241
    }
 
242
 
 
243
    return g_pData->fInterceptAudioInput;
 
244
}
 
245
 
 
246
/* Filter informs that an audio input is starting. */
 
247
int filter_input_begin (void **ppvInputCtx, PFNAUDIOINPUTCALLBACK pfnCallback, void *pvCallback, HWVoiceIn *phw, int cSamples)
 
248
{
 
249
    int rc = VINF_SUCCESS;
 
250
 
 
251
    SnifferInputCtx *pCtx = NULL;
 
252
 
 
253
    if (!g_pData || !g_pData->pDrv)
 
254
    {
 
255
        return VERR_NOT_SUPPORTED;
 
256
    }
 
257
 
 
258
    pCtx = (SnifferInputCtx *)RTMemAlloc(sizeof(SnifferInputCtx));
 
259
 
 
260
    if (!pCtx)
 
261
    {
 
262
        return VERR_NO_MEMORY;
 
263
    }
 
264
 
 
265
    pCtx->cRefs = 2; /* Context is used by both the filter and the user. */
 
266
    pCtx->pfnFilterCallback = pfnCallback;
 
267
    pCtx->pvFilterCallback = pvCallback;
 
268
    pCtx->fEndedByFilter = false;
 
269
    pCtx->pvUserCtx = NULL;
 
270
    pCtx->phw = phw;
 
271
    pCtx->cBytesPerFrame = 1;
 
272
    pCtx->iFreq = 0;
 
273
    pCtx->conv = NULL;
 
274
    pCtx->rate = NULL;
 
275
    pCtx->pvSamplesBuffer = NULL;
 
276
    pCtx->cbSamplesBufferAllocated = 0;
 
277
    pCtx->pvRateBuffer = NULL;
 
278
    pCtx->cbRateBufferAllocated = 0;
 
279
 
 
280
    rc = g_pData->pDrv->pfnAudioInputBegin (g_pData->pDrv,
 
281
                                            &pCtx->pvUserCtx,      /* Returned by the pDrv. */
 
282
                                            pCtx,
 
283
                                            cSamples,              /* How many samples in one block is preferred. */
 
284
                                            phw->info.freq,        /* Required frequency. */
 
285
                                            phw->info.nchannels,   /* Number of audio channels. */
 
286
                                            phw->info.bits);       /* A sample size in one channel, samples are signed. */
 
287
 
 
288
    if (RT_SUCCESS(rc))
 
289
    {
 
290
        *ppvInputCtx = pCtx;
 
291
    }
 
292
    else
 
293
    {
 
294
        RTMemFree(pCtx);
 
295
    }
 
296
 
 
297
    Log(("input_begin rc = %Rrc\n", rc));
 
298
 
 
299
    return rc;
 
300
}
 
301
 
 
302
/* Filter informs that the audio input must be stopped. */
 
303
void filter_input_end(void *pvCtx)
 
304
{
 
305
    int32_t c;
 
306
 
 
307
    SnifferInputCtx *pCtx = (SnifferInputCtx *)pvCtx;
 
308
 
 
309
    void *pvUserCtx = pCtx->pvUserCtx;
 
310
 
 
311
    pCtx->fEndedByFilter = true;
 
312
 
 
313
    c = ASMAtomicDecS32(&pCtx->cRefs);
 
314
 
 
315
    if (c == 0)
 
316
    {
 
317
        ictxDelete(pCtx);
 
318
        pCtx = NULL;
 
319
    }
 
320
 
 
321
    if (!g_pData || !g_pData->pDrv)
 
322
    {
 
323
        AssertFailed();
 
324
        return;
 
325
    }
 
326
 
 
327
    g_pData->pDrv->pfnAudioInputEnd (g_pData->pDrv,
 
328
                                     pvUserCtx);
 
329
 
 
330
    Log(("input_end\n"));
 
331
}
 
332
 
 
333
 
 
334
/*
 
335
 * Audio PDM device.
 
336
 */
 
337
static DECLCALLBACK(int) iface_AudioInputIntercept (PPDMIAUDIOSNIFFERPORT pInterface, bool fIntercept)
 
338
{
 
339
    AUDIOSNIFFERSTATE *pThis = RT_FROM_MEMBER(pInterface, AUDIOSNIFFERSTATE, IPort);
 
340
 
 
341
    Assert(g_pData == pThis);
 
342
 
 
343
    pThis->fInterceptAudioInput = fIntercept;
 
344
 
 
345
    return VINF_SUCCESS;
 
346
}
 
347
 
 
348
static DECLCALLBACK(int) iface_AudioInputEventBegin (PPDMIAUDIOSNIFFERPORT pInterface,
 
349
                                                     void *pvContext,
 
350
                                                     int iSampleHz,
 
351
                                                     int cChannels,
 
352
                                                     int cBits,
 
353
                                                     bool fUnsigned)
 
354
{
 
355
    int bitIdx;
 
356
 
 
357
    AUDIOSNIFFERSTATE *pThis = RT_FROM_MEMBER(pInterface, AUDIOSNIFFERSTATE, IPort);
 
358
 
 
359
    int rc = VINF_SUCCESS;
 
360
 
 
361
    SnifferInputCtx *pCtx = (SnifferInputCtx *)pvContext;
 
362
 
 
363
    Log(("FilterAudio: AudioInputEventBegin: %dHz,%dch,%dbits,%d ended %d\n",
 
364
         iSampleHz, cChannels, cBits, fUnsigned, pCtx->fEndedByFilter));
 
365
 
 
366
    Assert(g_pData == pThis);
 
367
 
 
368
    /* Prepare a format convertion for the actually used format. */
 
369
    pCtx->cBytesPerFrame = ((cBits + 7) / 8) * cChannels;
 
370
 
 
371
    if (cBits == 16)
 
372
    {
 
373
        bitIdx = 1;
 
374
    }
 
375
    else if (cBits == 32)
 
376
    {
 
377
        bitIdx = 2;
 
378
    }
 
379
    else
 
380
    {
 
381
        bitIdx = 0;
 
382
    }
 
383
 
 
384
    pCtx->conv = mixeng_conv[(cChannels == 2)? 1: 0] /* stereo */
 
385
                            [!fUnsigned]             /* sign */
 
386
                            [0]                      /* big endian */
 
387
                            [bitIdx];                /* bits */
 
388
 
 
389
    if (iSampleHz && iSampleHz != pCtx->phw->info.freq)
 
390
    {
 
391
        pCtx->rate = st_rate_start (iSampleHz, pCtx->phw->info.freq);
 
392
        pCtx->iFreq = iSampleHz;
 
393
    }
 
394
 
 
395
    return rc;
 
396
}
 
397
 
 
398
static DECLCALLBACK(int) iface_AudioInputEventData (PPDMIAUDIOSNIFFERPORT pInterface,
 
399
                                                    void *pvContext,
 
400
                                                    const void *pvData,
 
401
                                                    uint32_t cbData)
 
402
{
 
403
    AUDIOSNIFFERSTATE *pThis = RT_FROM_MEMBER(pInterface, AUDIOSNIFFERSTATE, IPort);
 
404
 
 
405
    int rc = VINF_SUCCESS;
 
406
 
 
407
    SnifferInputCtx *pCtx = (SnifferInputCtx *)pvContext;
 
408
 
 
409
    Log(("FilterAudio: AudioInputEventData: pvData %p. cbData %d, ended %d\n", pvData, cbData, pCtx->fEndedByFilter));
 
410
 
 
411
    Assert(g_pData == pThis);
 
412
 
 
413
    if (   !pCtx->fEndedByFilter
 
414
        && pCtx->conv)
 
415
    {
 
416
        /* Convert PCM samples to st_sample_t.
 
417
         * And then apply rate conversion if necessary.
 
418
         */
 
419
 
 
420
        /* Optimization: allocate 'ps' buffer once per context and reallocate if cbData changes.
 
421
         * Usually size of packets is constant.
 
422
         */
 
423
        st_sample_t *ps;
 
424
        uint32_t cs = cbData / pCtx->cBytesPerFrame; /* How many samples. */
 
425
 
 
426
        ictxReallocSamplesBuffer(pCtx, cs);
 
427
 
 
428
        ps = (st_sample_t *)pCtx->pvSamplesBuffer;
 
429
        if (ps)
 
430
        {
 
431
            void *pvSamples = NULL;
 
432
            uint32_t cbSamples = 0;
 
433
 
 
434
            Assert(pCtx->cbSamplesBufferAllocated >= cs * sizeof(st_sample_t));
 
435
 
 
436
            pCtx->conv(ps, pvData, cs, &nominal_volume);
 
437
 
 
438
            if (pCtx->rate)
 
439
            {
 
440
                st_sample_t *psConverted;
 
441
                uint32_t csConverted = (cs * pCtx->phw->info.freq) / pCtx->iFreq;
 
442
 
 
443
                ictxReallocRateBuffer(pCtx, csConverted);
 
444
 
 
445
                psConverted = (st_sample_t *)pCtx->pvRateBuffer;
 
446
                if (psConverted)
 
447
                {
 
448
                    int csSrc = cs;
 
449
                    int csDst = csConverted;
 
450
 
 
451
                    Assert(pCtx->cbRateBufferAllocated >= csConverted * sizeof(st_sample_t));
 
452
 
 
453
                    st_rate_flow (pCtx->rate,
 
454
                                  ps, psConverted,
 
455
                                  &csSrc, &csDst);
 
456
 
 
457
                    pvSamples = psConverted;
 
458
                    cbSamples = csDst * sizeof(st_sample_t); /* Use csDst as it may be != csConverted */
 
459
                }
 
460
                else
 
461
                {
 
462
                    rc = VERR_NO_MEMORY;
 
463
                }
 
464
            }
 
465
            else
 
466
            {
 
467
                pvSamples = ps;
 
468
                cbSamples = cs * sizeof(st_sample_t);
 
469
            }
 
470
 
 
471
            if (cbSamples)
 
472
            {
 
473
                rc = pCtx->pfnFilterCallback(pCtx->pvFilterCallback, cbSamples, pvSamples);
 
474
            }
 
475
        }
 
476
        else
 
477
        {
 
478
            rc = VERR_NO_MEMORY;
 
479
        }
 
480
    }
 
481
 
 
482
    return rc;
 
483
}
 
484
 
 
485
static DECLCALLBACK(void) iface_AudioInputEventEnd (PPDMIAUDIOSNIFFERPORT pInterface,
 
486
                                                    void *pvContext)
 
487
{
 
488
    int32_t c;
 
489
 
 
490
    AUDIOSNIFFERSTATE *pThis = RT_FROM_MEMBER(pInterface, AUDIOSNIFFERSTATE, IPort);
 
491
 
 
492
    SnifferInputCtx *pCtx = (SnifferInputCtx *)pvContext;
 
493
 
 
494
    Log(("FilterAudio: AudioInputEventEnd: ended %d\n", pCtx->fEndedByFilter));
 
495
 
 
496
    Assert(g_pData == pThis);
 
497
 
 
498
    c = ASMAtomicDecS32(&pCtx->cRefs);
 
499
 
 
500
    if (c == 0)
 
501
    {
 
502
        ictxDelete(pCtx);
 
503
        pCtx = NULL;
 
504
    }
 
505
}
104
506
 
105
507
static DECLCALLBACK(int) iface_Setup (PPDMIAUDIOSNIFFERPORT pInterface, bool fEnable, bool fKeepHostAudio)
106
508
{
158
560
    /*
159
561
     * Validate configuration.
160
562
     */
161
 
    if (!CFGMR3AreValuesValid(pCfgHandle, "\0"))
 
563
    if (!CFGMR3AreValuesValid(pCfgHandle, "InterceptAudioInput\0"))
162
564
    {
163
565
        return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
164
566
    }
169
571
    pThis->fEnabled = false;
170
572
    pThis->fKeepHostAudio = true;
171
573
    pThis->pDrv = NULL;
 
574
    rc = CFGMR3QueryBoolDef(pCfgHandle, "InterceptAudioInput", &pThis->fInterceptAudioInput, false);
 
575
    if (RT_FAILURE(rc))
 
576
        return PDMDEV_SET_ERROR(pDevIns, rc,
 
577
                                N_("Configuration error: Failed to get the \"YieldOnLSRRead\" value"));
172
578
 
173
579
    /*
174
580
     * Interfaces
178
584
 
179
585
    /* Audio Sniffer port */
180
586
    pThis->IPort.pfnSetup = iface_Setup;
 
587
    pThis->IPort.pfnAudioInputIntercept = iface_AudioInputIntercept;
 
588
    pThis->IPort.pfnAudioInputEventBegin = iface_AudioInputEventBegin;
 
589
    pThis->IPort.pfnAudioInputEventData = iface_AudioInputEventData;
 
590
    pThis->IPort.pfnAudioInputEventEnd = iface_AudioInputEventEnd;
181
591
 
182
592
    /*
183
593
     * Get the corresponding connector interface