audiosniffer.c revision c58f1213e628a545081c70e26c6b67a841cff880
/* $Id$ */
/** @file
* VBox audio device: Audio sniffer device
*/
/*
* Copyright (C) 2006-2011 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
#define LOG_GROUP LOG_GROUP_DEV_AUDIO
#define AUDIO_CAP "sniffer"
#include "VBoxDD.h"
#include "vl_vbox.h"
#include "audio.h"
#include "audio_int.h"
typedef struct _AUDIOSNIFFERSTATE
{
/** If the device is enabled. */
bool fEnabled;
/** Whether audio should reach the host driver too. */
bool fKeepHostAudio;
/** Whether audio input operations should be forwarded to the connector. */
bool fInterceptAudioInput;
/** Pointer to device instance. */
/** Audio Sniffer port base interface. */
/** Audio Sniffer port interface. */
/** Pointer to base interface of the driver. */
/** Audio Sniffer connector interface */
/*
* Public sniffer callbacks to be called from audio driver.
*/
/* *** Subject to change ***
* Process audio output. The function is called when an audio output
* driver is about to play audio samples.
*
* It is expected that there is only one audio data flow,
* i.e. one voice.
*
* @param hw Audio samples information.
* @param pvSamples Pointer to audio samples.
* @param cSamples Number of audio samples in the buffer.
* @returns 'true' if audio also to be played back by the output driver.
* 'false' if audio should not be played.
*/
{
int samplesPerSec;
int nChannels;
int bitsPerSample;
bool fUnsigned;
{
return true;
}
return g_pData->fKeepHostAudio;
}
/*
* Filter interface.
*/
/* Internal audio input context, which makes sure that:
* - the filter audio input callback is not called after the filter has issued filter_input_end;
* - maintains internal information and state of the audio stream.
*/
typedef struct SnifferInputCtx
{
/* Whether the context is still in use by the filter or I'll check. */
/* The filter callback for incoming audio data. */
void *pvFilterCallback;
/* Whether the stream has been ended by the filter. */
bool fEndedByFilter;
/* Context pointer returned by pfnAudioInputBegin. */
void *pvUserCtx;
/* Audio format used for recording. */
/* Number of bytes per frame (bitsPerSample * channels) of the actual input format. */
/* Frequency of the actual audio format. */
int iFreq;
/* Convertion from the actual input format to st_sample_t. */
/* If the actual format frequence differs from the requested format, this is not NULL. */
void *rate;
/* Temporary buffer for st_sample_t representation of the input audio data. */
void *pvSamplesBuffer;
/* Temporary buffer for frequency conversion. */
void *pvRateBuffer;
{
/* The caller will not use this context anymore. */
{
}
}
{
{
if (pCtx->pvSamplesBuffer)
{
}
else
{
pCtx->cbSamplesBufferAllocated = 0;
}
}
}
{
{
if (pCtx->pvRateBuffer)
{
}
else
{
pCtx->cbRateBufferAllocated = 0;
}
}
}
/*
* Filter audio output.
*/
/* Whether the filter should intercept audio output. */
int filter_output_intercepted(void)
{
return 0; /* @todo Not implemented yet.*/
}
/* Filter informs that an audio output is starting. */
{
return VERR_NOT_SUPPORTED; /* @todo Not implemented yet.*/
}
/* Filter informs that the audio output has been stopped. */
void filter_output_end(void *pvOutputCtx)
{
return; /* @todo Not implemented yet.*/
}
/*
* Filter audio input.
*/
/* Whether the filter should intercept audio input. */
int filter_input_intercepted(void)
{
{
return 0;
}
return g_pData->fInterceptAudioInput;
}
/* Filter informs that an audio input is starting. */
int filter_input_begin (void **ppvInputCtx, PFNAUDIOINPUTCALLBACK pfnCallback, void *pvCallback, HWVoiceIn *phw, int cSamples)
{
int rc = VINF_SUCCESS;
{
return VERR_NOT_SUPPORTED;
}
if (!pCtx)
{
return VERR_NO_MEMORY;
}
pCtx->fEndedByFilter = false;
pCtx->cbSamplesBufferAllocated = 0;
pCtx->cbRateBufferAllocated = 0;
pCtx,
cSamples, /* How many samples in one block is preferred. */
if (RT_SUCCESS(rc))
{
*ppvInputCtx = pCtx;
}
else
{
}
return rc;
}
/* Filter informs that the audio input must be stopped. */
void filter_input_end(void *pvCtx)
{
int32_t c;
pCtx->fEndedByFilter = true;
if (c == 0)
{
}
{
AssertFailed();
return;
}
Log(("input_end\n"));
}
/*
* Audio PDM device.
*/
static DECLCALLBACK(int) iface_AudioInputIntercept (PPDMIAUDIOSNIFFERPORT pInterface, bool fIntercept)
{
return VINF_SUCCESS;
}
void *pvContext,
int iSampleHz,
int cChannels,
int cBits,
bool fUnsigned)
{
int bitIdx;
int rc = VINF_SUCCESS;
Log(("FilterAudio: AudioInputEventBegin: %dHz,%dch,%dbits,%d ended %d\n",
/* Prepare a format convertion for the actually used format. */
if (cBits == 16)
{
bitIdx = 1;
}
else if (cBits == 32)
{
bitIdx = 2;
}
else
{
bitIdx = 0;
}
[!fUnsigned] /* sign */
[0] /* big endian */
[bitIdx]; /* bits */
{
}
return rc;
}
void *pvContext,
const void *pvData,
{
int rc = VINF_SUCCESS;
Log(("FilterAudio: AudioInputEventData: pvData %p. cbData %d, ended %d\n", pvData, cbData, pCtx->fEndedByFilter));
if ( !pCtx->fEndedByFilter
{
/* Convert PCM samples to st_sample_t.
* And then apply rate conversion if necessary.
*/
/* Optimization: allocate 'ps' buffer once per context and reallocate if cbData changes.
* Usually size of packets is constant.
*/
if (ps)
{
{
if (psConverted)
{
int csDst = csConverted;
}
else
{
rc = VERR_NO_MEMORY;
}
}
else
{
}
if (cbSamples)
{
}
}
else
{
rc = VERR_NO_MEMORY;
}
}
return rc;
}
void *pvContext)
{
int32_t c;
if (c == 0)
{
}
}
static DECLCALLBACK(int) iface_Setup (PPDMIAUDIOSNIFFERPORT pInterface, bool fEnable, bool fKeepHostAudio)
{
return VINF_SUCCESS;
}
/**
* @interface_method_impl{PDMIBASE,pfnQueryInterface}
*/
{
return NULL;
}
/**
* Destruct a device instance.
*
* Most VM resources are freed by the VM. This callback is provided so that any non-VM
* resources can be freed correctly.
*
* @returns VBox status.
* @param pDevIns The device instance data.
*/
{
/* Zero the global pointer. */
return VINF_SUCCESS;
}
/**
* @interface_method_impl{PDMDEVREG,pfnConstruct}
*/
static DECLCALLBACK(int) audioSnifferR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfgHandle)
{
int rc = VINF_SUCCESS;
/*
* Validate configuration.
*/
{
}
/*
* Initialize data.
*/
pThis->fKeepHostAudio = true;
if (RT_FAILURE(rc))
N_("Configuration error: Failed to get the \"YieldOnLSRRead\" value"));
/*
* Interfaces
*/
/* Base */
/* Audio Sniffer port */
/*
* Get the corresponding connector interface
*/
if (RT_SUCCESS(rc))
{
AssertMsgStmt(pThis->pDrv, ("LUN #0 doesn't have a Audio Sniffer connector interface rc=%Rrc\n", rc),
}
else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
{
Log(("%s/%d: warning: no driver attached to LUN #0.\n", pDevIns->pReg->szName, pDevIns->iInstance));
rc = VINF_SUCCESS;
}
else
{
}
if (RT_SUCCESS (rc))
{
/* Save PDM device instance data for future reference. */
/* Save the pointer to created instance in the global variable, so other
* functions could reach it.
*/
}
return rc;
}
/**
* The Audio Sniffer device registration structure.
*/
const PDMDEVREG g_DeviceAudioSniffer =
{
/* u32Version */
/* szName */
"AudioSniffer",
/* szRCMod */
"",
/* szR0Mod */
"",
/* pszDescription */
"Audio Sniffer device. Redirects audio data to sniffer driver.",
/* fFlags */
/* fClass */
/* cMaxInstances */
1,
/* cbInstance */
sizeof(AUDIOSNIFFERSTATE),
/* pfnConstruct */
/* pfnDestruct */
/* pfnRelocate */
NULL,
/* pfnIOCtl */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
NULL,
/* pfnSuspend */
NULL,
/* pfnResume */
NULL,
/* pfnAttach */
NULL,
/* pfnDetach */
NULL,
/* pfnQueryInterface */
NULL,
/* pfnInitComplete */
NULL,
/* pfnPowerOff */
NULL,
/* pfnSoftReset */
NULL,
};