/* $Id$ */
/** @file
* Video recording audio backend for Main.
*/
/*
* Copyright (C) 2014-2015 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.
*/
#include "DrvAudioVideoRec.h"
#include "ConsoleImpl.h"
#include "ConsoleVRDPServer.h"
#include "Logging.h"
#ifdef LOG_GROUP
#endif
/* Initialization status indicator used for the recreation of the AudioUnits. */
//@todo move t_sample as a PDM interface
//typedef struct { int mute; uint32_t r; uint32_t l; } volume_t;
0,
};
/* The desired buffer length in milliseconds. Will be the target total stream
* latency on newer version of pulse. Apparent latency can be less (or more.)
* In case its need to be used. Currently its not used.
*/
#if 0
static struct
{
int buffer_msecs_out;
int buffer_msecs_in;
=
{
};
#endif
/**
* Audio video recording driver instance data.
*
* @extends PDMIAUDIOSNIFFERCONNECTOR
*/
typedef struct DRVAUDIOVIDEOREC
{
/** Pointer to audio video recording object. */
/** Pointer to the driver instance structure. */
/** Pointer to the DrvAudio port interface that is above it. */
typedef struct VIDEORECAUDIOIN
{
/* Audio and audio details for recording */
void * pvUserCtx;
/* Number of bytes per frame (bitsPerSample * channels) of the actual input format. */
/* Frequency of the actual audio format. */
/* 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;
/* buffer for bytes of samples (not rate converted) */
/* Temporary buffer for frequency conversion. */
void *pvRateBuffer;
/* buffer for bytes rate converted samples */
/* A ring buffer for transferring data to the playback thread */
typedef struct VIDEORECAUDIOOUT
{
{
return VINF_SUCCESS;
}
/** @todo Replace this with drvAudioHlpPcmPropsFromCfg(). */
{
bool fSigned = false;
{
case AUD_FMT_S8:
fSigned = 1;
case AUD_FMT_U8:
break;
case AUD_FMT_S16:
fSigned = 1;
case AUD_FMT_U16:
cBits = 16;
cShift = 1;
break;
case AUD_FMT_S32:
fSigned = 1;
case AUD_FMT_U32:
cBits = 32;
cShift = 2;
break;
default:
break;
}
return rc;
}
/*
* Hard voice (playback)
*/
{
int m = INT_MAX;
int nb_live = 0;
{
{
nb_live += 1;
}
}
return m;
}
{
int smin;
if (!*nb_live) {
return 0;
}
else
{
{
return 0;
}
return live;
}
}
{
int nb_live;
int live;
{
return 0;
}
return live;
}
/*
* Hard voice (capture)
*/
{
int m = hw->cTotalSamplesCaptured;
{
{
}
}
return m;
}
{
{
return 0;
}
return live;
}
{
return (d + incr);
}
{
{
/** @todo r=andy Why not using RTMemReAlloc? */
if (pVRDEVoice->pvSamplesBuffer)
{
}
if (pVRDEVoice->pvSamplesBuffer)
else
}
return VINF_SUCCESS;
}
{
{
if (pVRDEVoice->pvRateBuffer)
else
}
return VINF_SUCCESS;
}
/*******************************************************************************
*
* AudioVideoRec input section
*
******************************************************************************/
/*
* Callback to feed audio input buffer. Samples format is be the same as
* in the voice. The caller prepares st_sample_t.
*
* @param cbSamples Size of pvSamples array in bytes.
* @param pvSamples Points to an array of samples.
*
* @return IPRT status code.
*/
static int vrdeRecordingCallback(PVIDEORECAUDIOIN pVRDEVoice, uint32_t cbSamples, const void *pvSamples)
{
if (!pVRDEVoice->fIsInit)
return VINF_SUCCESS;
/* If nothing is pending return immediately. */
if (cbSamples == 0)
return VINF_SUCCESS;
/* How much space is free in the ring buffer? */
size_t csAvail = RTCircBufFree(pVRDEVoice->pRecordedVoiceBuf) / sizeof(PDMAUDIOSAMPLE); /* bytes -> samples */
/* How much space is used in the audio buffer. Use the smaller size of the too. */
/* Iterate as long as data is available. */
{
/* How much is left? */
/* Try to acquire the necessary space from the ring buffer. */
void *pcDst;
/* How much do we get? */
/* Copy the data from the audio buffer to the ring buffer in PVRDEVoice. */
if (csToWrite)
{
}
/* Release the ring buffer, so the main thread could start reading this data. */
if (RT_UNLIKELY(csToWrite == 0))
break;
}
LogFlowFunc(("Finished writing buffer with %RU32 samples (%RU32 bytes)\n",
return rc;
}
static DECLCALLBACK(int) drvAudioVideoRecInitOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHostVoiceOut, PPDMAUDIOSTREAMCFG pCfg)
{
}
static DECLCALLBACK(int) drvAudioVideoRecInitIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHostVoiceIn, PPDMAUDIOSTREAMCFG pCfg)
{
}
static DECLCALLBACK(int) drvAudioVideoRecCaptureIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHostVoiceIn,
{
/** @todo Take care of the size of the buffer allocated to pHostVoiceIn. */
/* use this from DrvHostCoreAudio.c */
{
LogFlowFunc(("VRDE voice not initialized\n"));
*pcSamplesCaptured = 0;
return VERR_GENERAL_FAILURE; /** @todo Fudge! */
}
/* how much space is used in the ring buffer in pRecordedVocieBuf with pAudioVideoRec . Bytes-> samples*/
/* How much space is available in the mix buffer. Use the smaller size of the too. */
cSamplesRingBuffer * sizeof(PDMAUDIOSAMPLE)));
/* Iterate as long as data is available */
while (cSamplesRead < cSamplesRingBuffer)
{
/* How much is left? Split request at the end of our samples buffer. */
/* Try to acquire the necessary block from the ring buffer. Remeber in fltRecrodCallback we
* we are filling this buffer with the audio data available from VRDP. Here we are reading it
*/
/*todo do I need to introduce a thread to fill the buffer in fltRecordcallback. So that
* filling is in separate thread and the reading of that buffer is in separate thread
*/
void *pvSrc;
/* How much to we get? */
/* Break if nothing is used anymore. */
if (cSamplesToRead)
{
/* Copy the data from our ring buffer to the mix buffer. */
PPDMAUDIOSAMPLE psDst = pVRDEVoice->pHostVoiceIn.paSamples + pVRDEVoice->pHostVoiceIn.offSamplesWritten;
}
/* Release the read buffer, so it could be used for new data. */
if (!cSamplesToRead)
break;
pVRDEVoice->pHostVoiceIn.offSamplesWritten = (pVRDEVoice->pHostVoiceIn.offSamplesWritten + cSamplesToRead)
/* How much have we reads so far. */
}
LogFlowFunc(("Finished reading buffer with %zu samples (%zu bytes)\n",
return VINF_SUCCESS;
}
static DECLCALLBACK(int) drvAudioVideoRecPlayOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHostVoiceOut,
{
/*
* Just call the VRDP server with the data.
*/
int cSamplesPlayed = (int)((2 * ticks * pHostVoiceOut->Props.uHz + ticks_per_second) / ticks_per_second / 2);
if (cSamplesPlayed < 0)
LogFlowFunc(("freq=%d, chan=%d, cBits = %d, fsigned = %d, cSamples=%d format=%d\n",
{
/* send the samples till the end of pHostStereoSampleBuf */
pDrv->pConsoleVRDPServer->SendAudioSamples(&pHostVoiceOut->paSamples[pHostVoiceOut->cOffSamplesRead],
/*pHostStereoSampleBuff already has the samples which exceeded its space. They have overwriten the old
* played sampled starting from offset 0. So based on the number of samples that we had to play,
* read the number of samples from offset 0 .
*/
format);
}
else
{
pDrv->pConsoleVRDPServer->SendAudioSamples(&pHostVoiceOut->paSamples[pHostVoiceOut->cOffSamplesRead],
}
pHostVoiceOut->cOffSamplesRead = (pHostVoiceOut->cOffSamplesRead + cSamplesToSend) % pHostVoiceOut->cSamples;
return VINF_SUCCESS;
}
{
return VINF_SUCCESS;
}
static DECLCALLBACK(int) drvAudioVideoRecFiniOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHostVoiceOut)
{
return VINF_SUCCESS;
}
static DECLCALLBACK(int) drvAudioVideoRecControlOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT hw,
{
return VINF_SUCCESS;
}
static DECLCALLBACK(int) drvAudioVideoRecControlIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHostVoiceIn,
{
/* Initialize VRDEVoice and return to VRDP server which returns this struct back to us
* in the form void * pvContext
*/
/* initialize only if not already done */
if (enmStreamCmd == PDMAUDIOSTREAMCMD_ENABLE)
{
//@todo if (!pVRDEVoice->fIsInit)
// RTCircBufReset(pVRDEVoice->pRecordedVoiceBuf);
pVRDEVoice->uFrequency = 0;
/* Initialize the hardware info section with the audio settings */
/* Create the internal ring buffer. */
{
LogRel(("Failed to create internal ring buffer\n"));
return VERR_NO_MEMORY;
}
}
else if (enmStreamCmd == PDMAUDIOSTREAMCMD_DISABLE)
{
pVRDEVoice->fIsInit = 0;
}
return VINF_SUCCESS;
}
static DECLCALLBACK(int) drvAudioVideoRecGetConf(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pAudioConf)
{
return VINF_SUCCESS;
}
{
}
/**
* @interface_method_impl{PDMIBASE,pfnQueryInterface}
*/
static DECLCALLBACK(void *) drvAudioVideoRecQueryInterface(PPDMIBASE pInterface, const char *pszIID)
{
return NULL;
}
{
}
AudioVideoRec::~AudioVideoRec(void)
{
if (mpDrv)
{
}
}
{
return VINF_SUCCESS;
}
int AudioVideoRec::handleVideoRecSvrCmdAudioInputEventBegin(void *pvContext, int iSampleHz, int cChannels,
{
int bitIdx;
LogFlowFunc(("handleVRDPCmdInputEventBegin\n"));
/* Prepare a format convertion for the actually used format. */
if (cBits == 16)
{
bitIdx = 1;
}
else if (cBits == 32)
{
bitIdx = 2;
}
else
{
bitIdx = 0;
}
//PPDMIAUDIOCONNECTOR pPort = server->mConsole->getAudioVideoRec()->getDrvAudioPort();
/* Call DrvAudio interface to get the t_sample type conversion function */
/*mpDrv->pUpPort->pfnConvDevFmtToStSample(mpDrv->pUpPort,
(cChannels == 2) ? 1 : 0,
!fUnsigned, 0, bitIdx,
pVRDEVoice->convAudioDevFmtToStSampl);*/
{
LogFlowFunc(("Failed to get the conversion function \n"));
}
//if (iSampleHz && iSampleHz != pVRDEVoice->pHostVoiceIn.Props.uFrequency)
{
/* @todo if the above condition is false then pVRDEVoice->uFrequency will remain 0 */
/*mpDrv->pUpPort->pfnPrepareAudioConversion(mpDrv->pUpPort, iSampleHz,
pVRDEVoice->pHostVoiceIn.Props.uFrequency,
&pVRDEVoice->rate);*/
}
return VINF_SUCCESS;
}
/*
* pvContext: pointer to VRDP voice returned by the VRDP server. The is same pointer that we initialized in
* drvAudioVideoRecDisableEnableIn VOICE_ENABLE case.
*/
int AudioVideoRec::handleVideoRecSvrCmdAudioInputEventData(void *pvContext, const void *pvData, uint32_t cbData)
{
LogFlowFunc(("handleVRDPCmdInputEventData cbData = %d, bytesperfram=%d\n",
pVRDEVoice->convAudioDevFmtToStSampl(pHostStereoSampleBuf, pvData, cSamples, &videorec_nominal_volume);
/* count of rate adjusted samples */
if (pConvertedSampleBuf)
{
/*mpDrv->pUpPort->pfnDoRateConversion(mpDrv->pUpPort, pVRDEVoice->rate, pHostStereoSampleBuf,
pConvertedSampleBuf, &cSampleSrc, &cConvertedSamples);*/
}
if (cbSamples)
return rc;
}
/*
* pvContext: pointer to VRDP voice returned by the VRDP server. The is same pointer that we initialized in
* drvAudioVideoRecDisableEnableIn VOICE_ENABLE case.
*/
{
/* The caller will not use this context anymore. */
if (pVRDEVoice->rate)
{
//mpDrv->pUpPort->pfnEndAudioConversion(mpDrv->pUpPort, pVRDEVoice->rate);
}
if (pVRDEVoice->pvSamplesBuffer)
{
}
if (pVRDEVoice->pvRateBuffer)
{
}
return VINF_SUCCESS;
}
/**
* Construct a VRDE audio driver instance.
*
* @copydoc FNPDMDRVCONSTRUCT
*/
/* static */
{
LogRel(("Audio: Initializing VRDE driver\n"));
/* we save the address of AudioVideoRec in Object node in CFGM tree and address of VRDP server in
* ObjectVRDPServer node. So presence of both is necessary.
*/
//if (!CFGMR3AreValuesValid(pCfg, "Object\0") || !CFGMR3AreValuesValid(pCfg, "ObjectVRDPServer\0"))
// return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
("Configuration error: Not possible to attach anything to this driver!\n"),
/*
* Init the static parts.
*/
/* IBase */
/* IHostAudio */
/* Get VRDPServer pointer. */
void *pvUser;
if (RT_FAILURE(rc))
{
return rc;
}
/* CFGM tree saves the pointer to ConsoleVRDPServer in the Object node of AudioVideoRec. */
if (RT_FAILURE(rc))
{
return rc;
}
/*
* Get the interface for the above driver (DrvAudio) to make mixer/conversion calls.
* Described in CFGM tree.
*/
{
AssertMsgFailed(("Configuration error: No upper interface specified!\n"));
return VERR_PDM_MISSING_INTERFACE_ABOVE;
}
return VINF_SUCCESS;
}
/* static */
{
}
/**
* VRDE audio driver registration record.
*/
{
/* szName */
"AudioVideoRec",
/* szRCMod */
"",
/* szR0Mod */
"",
/* pszDescription */
"Audio driver for video recording",
/* fFlags */
/* fClass. */
/* cMaxInstances */
~0U,
/* cbInstance */
sizeof(DRVAUDIOVIDEOREC),
/* pfnConstruct */
/* pfnDestruct */
/* pfnRelocate */
NULL,
/* pfnIOCtl */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
NULL,
/* pfnSuspend */
NULL,
/* pfnResume */
NULL,
/* pfnAttach */
NULL,
/* pfnDetach */
NULL,
/* pfnPowerOff */
NULL,
/* pfnSoftReset */
NULL,
/* u32EndVersion */
};