VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/DrvAudioVideoRec.cpp@ 53508

Last change on this file since 53508 was 53442, checked in by vboxsync, 10 years ago

PDM Audio: Branch -> trunk.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.9 KB
Line 
1/* $Id: DrvAudioVideoRec.cpp 53442 2014-12-04 13:49:43Z vboxsync $ */
2/** @file
3 * Video recording audio backend for Main.
4 */
5
6/*
7 * Copyright (C) 2014 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17#include "DrvAudioVideoRec.h"
18#include "ConsoleImpl.h"
19#include "ConsoleVRDPServer.h"
20
21#include "Logging.h"
22
23#include <iprt/mem.h>
24#include <iprt/cdefs.h>
25#include <iprt/circbuf.h>
26
27#include <VBox/vmm/pdmaudioifs.h>
28#include <VBox/vmm/pdmdrv.h>
29#include <VBox/RemoteDesktop/VRDE.h>
30#include <VBox/vmm/cfgm.h>
31#include <VBox/err.h>
32
33#ifdef LOG_GROUP
34 #undef LOG_GROUP
35#endif
36#define LOG_GROUP LOG_GROUP_DEV_AUDIO
37#include <VBox/log.h>
38
39/* Initialization status indicator used for the recreation of the AudioUnits. */
40#define CA_STATUS_UNINIT UINT32_C(0) /* The device is uninitialized */
41#define CA_STATUS_IN_INIT UINT32_C(1) /* The device is currently initializing */
42#define CA_STATUS_INIT UINT32_C(2) /* The device is initialized */
43#define CA_STATUS_IN_UNINIT UINT32_C(3) /* The device is currently uninitializing */
44
45//@todo move t_sample as a PDM interface
46//typedef struct { int mute; uint32_t r; uint32_t l; } volume_t;
47
48#define INT_MAX 0x7fffffff
49volume_t videorec_nominal_volume = {
50 0,
51 INT_MAX,
52 INT_MAX
53};
54
55/* The desired buffer length in milliseconds. Will be the target total stream
56 * latency on newer version of pulse. Apparent latency can be less (or more.)
57 * In case its need to be used. Currently its not used.
58 */
59#if 0
60static struct
61{
62 int buffer_msecs_out;
63 int buffer_msecs_in;
64} confAudioVideoRec
65=
66{
67 INIT_FIELD (.buffer_msecs_out = ) 100,
68 INIT_FIELD (.buffer_msecs_in = ) 100,
69};
70#endif
71
72/**
73 * Audio video recording driver instance data.
74 *
75 * @extends PDMIAUDIOSNIFFERCONNECTOR
76 */
77typedef struct DRVAUDIOVIDEOREC
78{
79 /** Pointer to audio video recording object. */
80 AudioVideoRec *pAudioVideoRec;
81 PPDMDRVINS pDrvIns;
82 /** Pointer to the driver instance structure. */
83 PDMIHOSTAUDIO IHostAudioR3;
84 ConsoleVRDPServer *pConsoleVRDPServer;
85 /** Pointer to the DrvAudio port interface that is above it. */
86 PPDMIAUDIOCONNECTOR pUpPort;
87} DRVAUDIOVIDEOREC, *PDRVAUDIOVIDEOREC;
88typedef struct PDMAUDIOHSTSTRMOUT PDMAUDIOHSTSTRMOUT;
89typedef PDMAUDIOHSTSTRMOUT *PPDMAUDIOHSTSTRMOUT;
90
91typedef struct VIDEORECAUDIOIN
92{
93 /* Audio and audio details for recording */
94 PDMAUDIOHSTSTRMIN pHostVoiceIn;
95 void * pvUserCtx;
96 /* Number of bytes per frame (bitsPerSample * channels) of the actual input format. */
97 uint32_t cBytesPerFrame;
98 /* Frequency of the actual audio format. */
99 uint32_t uFrequency;
100 /* If the actual format frequence differs from the requested format, this is not NULL. */
101 void *rate;
102 /* Temporary buffer for st_sample_t representation of the input audio data. */
103 void *pvSamplesBuffer;
104 /* buffer for bytes of samples (not rate converted) */
105 uint32_t cbSamplesBufferAllocated;
106 /* Temporary buffer for frequency conversion. */
107 void *pvRateBuffer;
108 /* buffer for bytes rate converted samples */
109 uint32_t cbRateBufferAllocated;
110 /* A ring buffer for transferring data to the playback thread */
111 PRTCIRCBUF pRecordedVoiceBuf;
112 t_sample * convAudioDevFmtToStSampl;
113 uint32_t fIsInit;
114 uint32_t status;
115} VIDEORECAUDIOIN, *PVIDEORECAUDIOIN;
116
117typedef struct VIDEORECAUDIOOUT
118{
119 PDMAUDIOHSTSTRMOUT pHostVoiceOut;
120 uint64_t old_ticks;
121 uint64_t cSamplesSentPerSec;
122} VIDEORECAUDIOOUT, *PVIDEORECAUDIOOUT;
123
124static DECLCALLBACK(int) drvAudioVideoRecInit(PPDMIHOSTAUDIO pInterface)
125{
126 LogFlowFuncEnter();
127
128 return VINF_SUCCESS;
129}
130
131/** @todo Replace this with drvAudioHlpPcmPropsFromCfg(). */
132static int drvAudioVideoRecPcmInitInfo(PDMPCMPROPS * pProps, PPDMAUDIOSTREAMCFG as)
133{
134 int rc = VINF_SUCCESS;
135
136 uint8_t cBits = 8, cShift = 0;
137 bool fSigned = false;
138
139 switch (as->enmFormat)
140 {
141 case AUD_FMT_S8:
142 fSigned = 1;
143 case AUD_FMT_U8:
144 break;
145
146 case AUD_FMT_S16:
147 fSigned = 1;
148 case AUD_FMT_U16:
149 cBits = 16;
150 cShift = 1;
151 break;
152
153 case AUD_FMT_S32:
154 fSigned = 1;
155 case AUD_FMT_U32:
156 cBits = 32;
157 cShift = 2;
158 break;
159
160 default:
161 rc = VERR_NOT_SUPPORTED;
162 break;
163 }
164
165 pProps->uHz = as->uHz;
166 pProps->cBits = cBits;
167 pProps->fSigned = fSigned;
168 pProps->cChannels = as->cChannels;
169 pProps->cShift = (as->cChannels == 2) + cShift;
170 pProps->uAlign = (1 << pProps->cShift) - 1;
171 pProps->cbPerSec = pProps->uHz << pProps->cShift;
172 pProps->fSwapEndian = (as->enmEndianness != PDMAUDIOHOSTENDIANESS);
173
174 return rc;
175}
176
177/*
178 * Hard voice (playback)
179 */
180static int audio_pcm_hw_find_min_out (PPDMAUDIOHSTSTRMOUT hw, int *nb_livep)
181{
182 PPDMAUDIOGSTSTRMOUT sw;
183 PPDMAUDIOGSTSTRMOUT pIter;
184 int m = INT_MAX;
185 int nb_live = 0;
186
187 RTListForEach(&hw->lstGstStrmOut, pIter, PDMAUDIOGSTSTRMOUT, Node)
188 {
189 sw = pIter;
190 if (sw->State.fActive || !sw->State.fEmpty)
191 {
192 m = RT_MIN (m, sw->cTotalSamplesWritten);
193 nb_live += 1;
194 }
195 }
196
197 *nb_livep = nb_live;
198 return m;
199}
200
201static int audio_pcm_hw_get_live_out2 (PPDMAUDIOHSTSTRMOUT hw, int *nb_live)
202{
203 int smin;
204
205 smin = audio_pcm_hw_find_min_out (hw, nb_live);
206
207 if (!*nb_live) {
208 return 0;
209 }
210 else
211 {
212 int live = smin;
213
214 if (live < 0 || live > hw->cSamples)
215 {
216 LogFlowFunc(("Error: live=%d hw->samples=%d\n", live, hw->cSamples));
217 return 0;
218 }
219 return live;
220 }
221}
222
223
224static int audio_pcm_hw_get_live_out (PPDMAUDIOHSTSTRMOUT hw)
225{
226 int nb_live;
227 int live;
228
229 live = audio_pcm_hw_get_live_out2 (hw, &nb_live);
230 if (live < 0 || live > hw->cSamples)
231 {
232 LogFlowFunc(("Error: live=%d hw->samples=%d\n", live, hw->cSamples));
233 return 0;
234 }
235 return live;
236}
237
238/*
239 * Hard voice (capture)
240 */
241static int audio_pcm_hw_find_min_in (PPDMAUDIOHSTSTRMIN hw)
242{
243 PPDMAUDIOGSTSTRMIN pIter;
244 int m = hw->cTotalSamplesCaptured;
245
246 RTListForEach(&hw->lstGstStreamsIn, pIter, PDMAUDIOGSTSTRMIN, Node)
247 {
248 if (pIter->State.fActive)
249 {
250 m = RT_MIN (m, pIter->cTotalHostSamplesRead);
251 }
252 }
253 return m;
254}
255
256int audio_pcm_hw_get_live_in (PPDMAUDIOHSTSTRMIN hw)
257{
258 int live = hw->cTotalSamplesCaptured - audio_pcm_hw_find_min_in (hw);
259 if (live < 0 || live > hw->cSamples)
260 {
261 LogFlowFunc(("Error: live=%d hw->samples=%d\n", live, hw->cSamples));
262 return 0;
263 }
264 return live;
265}
266
267static inline void *advance (void *p, int incr)
268{
269 uint8_t *d = (uint8_t*)p;
270 return (d + incr);
271}
272
273static int vrdeReallocSampleBuf(PVIDEORECAUDIOIN pVRDEVoice, uint32_t cSamples)
274{
275 uint32_t cbBuffer = cSamples * sizeof(PDMAUDIOSAMPLE);
276 if (cbBuffer > pVRDEVoice->cbSamplesBufferAllocated)
277 {
278 /** @todo r=andy Why not using RTMemReAlloc? */
279 if (pVRDEVoice->pvSamplesBuffer)
280 {
281 RTMemFree(pVRDEVoice->pvSamplesBuffer);
282 pVRDEVoice->pvSamplesBuffer = NULL;
283 }
284 pVRDEVoice->pvSamplesBuffer = RTMemAlloc(cbBuffer);
285 if (pVRDEVoice->pvSamplesBuffer)
286 pVRDEVoice->cbSamplesBufferAllocated = cbBuffer;
287 else
288 pVRDEVoice->cbSamplesBufferAllocated = 0;
289 }
290
291 return VINF_SUCCESS;
292}
293
294static int vrdeReallocRateAdjSampleBuf(PVIDEORECAUDIOIN pVRDEVoice, uint32_t cSamples)
295{
296 uint32_t cbBuffer = cSamples * sizeof(PDMAUDIOSAMPLE);
297 if (cbBuffer > pVRDEVoice->cbRateBufferAllocated)
298 {
299 RTMemFree(pVRDEVoice->pvRateBuffer);
300 pVRDEVoice->pvRateBuffer = RTMemAlloc(cbBuffer);
301 if (pVRDEVoice->pvRateBuffer)
302 pVRDEVoice->cbRateBufferAllocated = cbBuffer;
303 else
304 pVRDEVoice->cbRateBufferAllocated = 0;
305 }
306
307 return VINF_SUCCESS;
308}
309
310/*******************************************************************************
311 *
312 * AudioVideoRec input section
313 *
314 ******************************************************************************/
315
316/*
317 * Callback to feed audio input buffer. Samples format is be the same as
318 * in the voice. The caller prepares st_sample_t.
319 *
320 * @param cbSamples Size of pvSamples array in bytes.
321 * @param pvSamples Points to an array of samples.
322 *
323 * @return IPRT status code.
324 */
325static int vrdeRecordingCallback(PVIDEORECAUDIOIN pVRDEVoice, uint32_t cbSamples, const void *pvSamples)
326{
327 int rc = VINF_SUCCESS;
328 size_t csWritten = 0;
329
330 Assert((cbSamples % sizeof(PDMAUDIOSAMPLE)) == 0);
331
332 if (!pVRDEVoice->fIsInit)
333 return VINF_SUCCESS;
334
335 /* If nothing is pending return immediately. */
336 if (cbSamples == 0)
337 return VINF_SUCCESS;
338
339 /* How much space is free in the ring buffer? */
340 size_t csAvail = RTCircBufFree(pVRDEVoice->pRecordedVoiceBuf) / sizeof(PDMAUDIOSAMPLE); /* bytes -> samples */
341
342 /* How much space is used in the audio buffer. Use the smaller size of the too. */
343 csAvail = RT_MIN(csAvail, cbSamples / sizeof(PDMAUDIOSAMPLE));
344
345 /* Iterate as long as data is available. */
346 while (csWritten < csAvail)
347 {
348 /* How much is left? */
349 size_t csToWrite = csAvail - csWritten;
350 size_t cbToWrite = csToWrite * sizeof(PDMAUDIOSAMPLE);
351
352 /* Try to acquire the necessary space from the ring buffer. */
353 void *pcDst;
354 RTCircBufAcquireWriteBlock(pVRDEVoice->pRecordedVoiceBuf, cbToWrite, &pcDst, &cbToWrite);
355
356 /* How much do we get? */
357 csToWrite = cbToWrite / sizeof(PDMAUDIOSAMPLE);
358
359 /* Copy the data from the audio buffer to the ring buffer in PVRDEVoice. */
360 if (csToWrite)
361 {
362 memcpy(pcDst, (uint8_t *)pvSamples + (csWritten * sizeof(PDMAUDIOSAMPLE)), cbToWrite);
363 csWritten += csToWrite;
364 }
365
366 /* Release the ring buffer, so the main thread could start reading this data. */
367 RTCircBufReleaseWriteBlock(pVRDEVoice->pRecordedVoiceBuf, cbToWrite);
368
369 if (RT_UNLIKELY(csToWrite == 0))
370 break;
371 }
372
373 LogFlowFunc(("Finished writing buffer with %RU32 samples (%RU32 bytes)\n",
374 csWritten, csWritten * sizeof(PDMAUDIOSAMPLE)));
375
376 return rc;
377}
378
379static DECLCALLBACK(int) drvAudioVideoRecInitOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHostVoiceOut, PPDMAUDIOSTREAMCFG pCfg)
380{
381 LogFlowFuncEnter();
382 PDRVAUDIOVIDEOREC pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVIDEOREC, IHostAudioR3);
383
384 PVIDEORECAUDIOOUT pVRDEVoiceOut = (PVIDEORECAUDIOOUT)pHostVoiceOut;
385 pHostVoiceOut->cSamples = _4K; /* 4096 samples * 4 = 16K bytes total. */
386
387 return drvAudioVideoRecPcmInitInfo(&pVRDEVoiceOut->pHostVoiceOut.Props, pCfg);
388}
389
390static DECLCALLBACK(int) drvAudioVideoRecInitIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHostVoiceIn, PPDMAUDIOSTREAMCFG pCfg)
391{
392 LogFlowFuncEnter();
393 PDRVAUDIOVIDEOREC pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVIDEOREC, IHostAudioR3);
394
395 PVIDEORECAUDIOIN pVRDEVoice = (PVIDEORECAUDIOIN)pHostVoiceIn;
396 pHostVoiceIn->cSamples = _4K; /* 4096 samples * 4 = 16K bytes total. */
397
398 return drvAudioVideoRecPcmInitInfo(&pVRDEVoice->pHostVoiceIn.Props, pCfg);
399}
400
401static DECLCALLBACK(int) drvAudioVideoRecCaptureIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHostVoiceIn,
402 uint32_t *pcSamplesCaptured)
403{
404 /** @todo Take care of the size of the buffer allocated to pHostVoiceIn. */
405 PVIDEORECAUDIOIN pVRDEVoice = (PVIDEORECAUDIOIN)pHostVoiceIn;
406
407 /* use this from DrvHostCoreAudio.c */
408 if (ASMAtomicReadU32(&pVRDEVoice->status) != CA_STATUS_INIT)
409 {
410 LogFlowFunc(("VRDE voice not initialized\n"));
411
412 *pcSamplesCaptured = 0;
413 return VERR_GENERAL_FAILURE; /** @todo Fudge! */
414 }
415
416 /* how much space is used in the ring buffer in pRecordedVocieBuf with pAudioVideoRec . Bytes-> samples*/
417 size_t cSamplesRingBuffer = RTCircBufUsed(pVRDEVoice->pRecordedVoiceBuf) / sizeof(PDMAUDIOSAMPLE);
418
419 /* How much space is available in the mix buffer. Use the smaller size of the too. */
420 cSamplesRingBuffer = RT_MIN(cSamplesRingBuffer, (uint32_t)(pVRDEVoice->pHostVoiceIn.cSamples -
421 audio_pcm_hw_get_live_in (&pVRDEVoice->pHostVoiceIn)));
422
423 LogFlowFunc(("Start reading buffer with %d samples (%d bytes)\n", cSamplesRingBuffer,
424 cSamplesRingBuffer * sizeof(PDMAUDIOSAMPLE)));
425
426 /* Iterate as long as data is available */
427 size_t cSamplesRead = 0;
428 while (cSamplesRead < cSamplesRingBuffer)
429 {
430 /* How much is left? Split request at the end of our samples buffer. */
431 size_t cSamplesToRead = RT_MIN(cSamplesRingBuffer - cSamplesRead,
432 (uint32_t)(pVRDEVoice->pHostVoiceIn.cSamples - pVRDEVoice->pHostVoiceIn.offSamplesWritten));
433 size_t cbToRead = cSamplesToRead * sizeof(PDMAUDIOSAMPLE);
434 LogFlowFunc(("Try reading %zu samples (%zu bytes)\n", cSamplesToRead, cbToRead));
435
436 /* Try to acquire the necessary block from the ring buffer. Remeber in fltRecrodCallback we
437 * we are filling this buffer with the audio data available from VRDP. Here we are reading it
438 */
439 /*todo do I need to introduce a thread to fill the buffer in fltRecordcallback. So that
440 * filling is in separate thread and the reading of that buffer is in separate thread
441 */
442 void *pvSrc;
443 RTCircBufAcquireReadBlock(pVRDEVoice->pRecordedVoiceBuf, cbToRead, &pvSrc, &cbToRead);
444
445 /* How much to we get? */
446 cSamplesToRead = cbToRead / sizeof(PDMAUDIOSAMPLE);
447 LogFlowFunc(("AuderVRDE: There are %d samples (%d bytes) available\n", cSamplesToRead, cbToRead));
448
449 /* Break if nothing is used anymore. */
450 if (cSamplesToRead)
451 {
452 /* Copy the data from our ring buffer to the mix buffer. */
453 PPDMAUDIOSAMPLE psDst = pVRDEVoice->pHostVoiceIn.paSamples + pVRDEVoice->pHostVoiceIn.offSamplesWritten;
454 memcpy(psDst, pvSrc, cbToRead);
455 }
456
457 /* Release the read buffer, so it could be used for new data. */
458 RTCircBufReleaseReadBlock(pVRDEVoice->pRecordedVoiceBuf, cbToRead);
459
460 if (!cSamplesToRead)
461 break;
462
463 pVRDEVoice->pHostVoiceIn.offSamplesWritten = (pVRDEVoice->pHostVoiceIn.offSamplesWritten + cSamplesToRead)
464 % pVRDEVoice->pHostVoiceIn.cSamples;
465
466 /* How much have we reads so far. */
467 cSamplesRead += cSamplesToRead;
468 }
469
470 LogFlowFunc(("Finished reading buffer with %zu samples (%zu bytes)\n",
471 cSamplesRead, cSamplesRead * sizeof(PDMAUDIOSAMPLE)));
472
473 *pcSamplesCaptured = cSamplesRead;
474 return VINF_SUCCESS;
475}
476
477static DECLCALLBACK(int) drvAudioVideoRecPlayOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHostVoiceOut,
478 uint32_t *pcSamplesPlayed)
479{
480 PDRVAUDIOVIDEOREC pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVIDEOREC, IHostAudioR3);
481 PVIDEORECAUDIOOUT pVRDEVoiceOut = (PVIDEORECAUDIOOUT)pHostVoiceOut;
482
483 /*
484 * Just call the VRDP server with the data.
485 */
486 int live = audio_pcm_hw_get_live_out(pHostVoiceOut);
487 uint64_t now = PDMDrvHlpTMGetVirtualTime(pDrv->pDrvIns);
488 uint64_t ticks = now - pVRDEVoiceOut->old_ticks;
489 uint64_t ticks_per_second = PDMDrvHlpTMGetVirtualFreq(pDrv->pDrvIns);
490
491 int cSamplesPlayed = (int)((2 * ticks * pHostVoiceOut->Props.uHz + ticks_per_second) / ticks_per_second / 2);
492 if (cSamplesPlayed < 0)
493 cSamplesPlayed = live;
494
495 pHostVoiceOut->Props.cBits = 128; /** @todo Make this configurable (or at least a define)? */
496 VRDEAUDIOFORMAT format = VRDE_AUDIO_FMT_MAKE(pHostVoiceOut->Props.uHz,
497 pHostVoiceOut->Props.cChannels,
498 pHostVoiceOut->Props.cBits, /* bits per sample */
499 !pHostVoiceOut->Props.fSigned);
500
501 LogFlowFunc(("freq=%d, chan=%d, cBits = %d, fsigned = %d, cSamples=%d format=%d\n",
502 pHostVoiceOut->Props.uHz, pHostVoiceOut->Props.cChannels,
503 pHostVoiceOut->Props.cBits, pHostVoiceOut->Props.fSigned,
504 pHostVoiceOut->cSamples, format));
505
506 pVRDEVoiceOut->old_ticks = now;
507 int cSamplesToSend = RT_MIN(live, cSamplesPlayed);
508
509 if (pHostVoiceOut->cOffSamplesRead + cSamplesToSend > pHostVoiceOut->cSamples)
510 {
511 /* send the samples till the end of pHostStereoSampleBuf */
512 pDrv->pConsoleVRDPServer->SendAudioSamples(&pHostVoiceOut->paSamples[pHostVoiceOut->cOffSamplesRead],
513 (pHostVoiceOut->cSamples - pHostVoiceOut->cOffSamplesRead), format);
514 /*pHostStereoSampleBuff already has the samples which exceeded its space. They have overwriten the old
515 * played sampled starting from offset 0. So based on the number of samples that we had to play,
516 * read the number of samples from offset 0 .
517 */
518 pDrv->pConsoleVRDPServer->SendAudioSamples(&pHostVoiceOut->paSamples[0],
519 (cSamplesToSend - (pHostVoiceOut->cSamples -
520 pHostVoiceOut->cOffSamplesRead)),
521 format);
522 }
523 else
524 {
525 pDrv->pConsoleVRDPServer->SendAudioSamples(&pHostVoiceOut->paSamples[pHostVoiceOut->cOffSamplesRead],
526 cSamplesToSend, format);
527 }
528
529 pHostVoiceOut->cOffSamplesRead = (pHostVoiceOut->cOffSamplesRead + cSamplesToSend) % pHostVoiceOut->cSamples;
530
531 *pcSamplesPlayed = cSamplesToSend;
532 return VINF_SUCCESS;
533}
534
535static DECLCALLBACK(int) drvAudioVideoRecFiniIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN hw)
536{
537 PDRVAUDIOVIDEOREC pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVIDEOREC, IHostAudioR3);
538 LogFlowFuncEnter();
539 pDrv->pConsoleVRDPServer->SendAudioInputEnd(NULL);
540
541 return VINF_SUCCESS;
542}
543
544static DECLCALLBACK(int) drvAudioVideoRecFiniOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHostVoiceOut)
545{
546 PDRVAUDIOVIDEOREC pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVIDEOREC, IHostAudioR3);
547 LogFlowFuncEnter();
548
549 return VINF_SUCCESS;
550}
551
552static DECLCALLBACK(int) drvAudioVideoRecControlOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT hw,
553 PDMAUDIOSTREAMCMD enmStreamCmd)
554{
555 PDRVAUDIOVIDEOREC pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVIDEOREC, IHostAudioR3);
556 LogFlowFuncEnter();
557
558 return VINF_SUCCESS;
559}
560
561static DECLCALLBACK(int) drvAudioVideoRecControlIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHostVoiceIn,
562 PDMAUDIOSTREAMCMD enmStreamCmd)
563{
564 PDRVAUDIOVIDEOREC pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVIDEOREC, IHostAudioR3);
565
566 /* Initialize VRDEVoice and return to VRDP server which returns this struct back to us
567 * in the form void * pvContext
568 */
569 PVIDEORECAUDIOIN pVRDEVoice = (PVIDEORECAUDIOIN)pHostVoiceIn;
570 LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
571
572 /* initialize only if not already done */
573 if (enmStreamCmd == PDMAUDIOSTREAMCMD_ENABLE)
574 {
575 //@todo if (!pVRDEVoice->fIsInit)
576 // RTCircBufReset(pVRDEVoice->pRecordedVoiceBuf);
577 pVRDEVoice->fIsInit = 1;
578 pVRDEVoice->pHostVoiceIn = *pHostVoiceIn;
579 pVRDEVoice->cBytesPerFrame = 1;
580 pVRDEVoice->uFrequency = 0;
581 pVRDEVoice->rate = NULL;
582 pVRDEVoice->cbSamplesBufferAllocated = 0;
583 pVRDEVoice->pvRateBuffer = NULL;
584 pVRDEVoice->cbRateBufferAllocated = 0;
585
586 pVRDEVoice->pHostVoiceIn.cSamples = 2048;
587 /* Initialize the hardware info section with the audio settings */
588
589 ASMAtomicWriteU32(&pVRDEVoice->status, CA_STATUS_IN_INIT);
590
591 /* Create the internal ring buffer. */
592 RTCircBufCreate(&pVRDEVoice->pRecordedVoiceBuf,
593 pVRDEVoice->pHostVoiceIn.cSamples * sizeof(PDMAUDIOSAMPLE));
594
595 if (!RT_VALID_PTR(pVRDEVoice->pRecordedVoiceBuf))
596 {
597 LogRel(("Failed to create internal ring buffer\n"));
598 return VERR_NO_MEMORY;
599 }
600
601 ASMAtomicWriteU32(&pVRDEVoice->status, CA_STATUS_INIT);
602 return pDrv->pConsoleVRDPServer->SendAudioInputBegin(NULL, pVRDEVoice, pHostVoiceIn->cSamples,
603 pHostVoiceIn->Props.uHz,
604 pHostVoiceIn->Props.cChannels, pHostVoiceIn->Props.cBits);
605 }
606 else if (enmStreamCmd == PDMAUDIOSTREAMCMD_DISABLE)
607 {
608 pVRDEVoice->fIsInit = 0;
609 ASMAtomicWriteU32(&pVRDEVoice->status, CA_STATUS_IN_UNINIT);
610 RTCircBufDestroy(pVRDEVoice->pRecordedVoiceBuf);
611 pVRDEVoice->pRecordedVoiceBuf = NULL;
612 ASMAtomicWriteU32(&pVRDEVoice->status, CA_STATUS_UNINIT);
613 pDrv->pConsoleVRDPServer->SendAudioInputEnd(NULL);
614 }
615
616 return VINF_SUCCESS;
617}
618
619static DECLCALLBACK(int) drvAudioVideoRecGetConf(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pAudioConf)
620{
621 LogFlowFunc(("pAudioConf=%p\n", pAudioConf));
622
623 pAudioConf->cbStreamOut = sizeof(VIDEORECAUDIOOUT);
624 pAudioConf->cbStreamIn = sizeof(VIDEORECAUDIOIN);
625 pAudioConf->cMaxHstStrmsOut = 1;
626 pAudioConf->cMaxHstStrmsIn = 1;
627
628 return VINF_SUCCESS;
629}
630
631/**
632 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
633 */
634static DECLCALLBACK(void *) drvAudioVideoRecQueryInterface(PPDMIBASE pInterface, const char *pszIID)
635{
636 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
637 PDRVAUDIOVIDEOREC pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVIDEOREC);
638 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
639 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudioR3);
640 return NULL;
641}
642
643AudioVideoRec::AudioVideoRec(Console *pConsole)
644 : mpDrv(NULL),
645 mParent(pConsole)
646{
647}
648
649AudioVideoRec::~AudioVideoRec(void)
650{
651 if (mpDrv)
652 {
653 mpDrv->pAudioVideoRec = NULL;
654 mpDrv = NULL;
655 }
656}
657
658int AudioVideoRec::handleVideoRecSvrCmdAudioInputIntercept(bool fIntercept)
659{
660 LogFlowThisFunc(("fIntercept=%RTbool\n", fIntercept));
661 return VINF_SUCCESS;
662}
663
664int AudioVideoRec::handleVideoRecSvrCmdAudioInputEventBegin(void *pvContext, int iSampleHz, int cChannels,
665 int cBits, bool fUnsigned)
666{
667 int bitIdx;
668 PVIDEORECAUDIOIN pVRDEVoice = (PVIDEORECAUDIOIN)pvContext;
669 LogFlowFunc(("handleVRDPCmdInputEventBegin\n"));
670 /* Prepare a format convertion for the actually used format. */
671 pVRDEVoice->cBytesPerFrame = ((cBits + 7) / 8) * cChannels;
672 if (cBits == 16)
673 {
674 bitIdx = 1;
675 }
676 else if (cBits == 32)
677 {
678 bitIdx = 2;
679 }
680 else
681 {
682 bitIdx = 0;
683 }
684
685 //PPDMIAUDIOCONNECTOR pPort = server->mConsole->getAudioVideoRec()->getDrvAudioPort();
686 /* Call DrvAudio interface to get the t_sample type conversion function */
687 /*mpDrv->pUpPort->pfnConvDevFmtToStSample(mpDrv->pUpPort,
688 (cChannels == 2) ? 1 : 0,
689 !fUnsigned, 0, bitIdx,
690 pVRDEVoice->convAudioDevFmtToStSampl);*/
691 if (pVRDEVoice->convAudioDevFmtToStSampl)
692 {
693 LogFlowFunc(("Failed to get the conversion function \n"));
694 }
695 LogFlowFunc(("Required freq as requested by VRDP Server = %d\n", iSampleHz));
696 //if (iSampleHz && iSampleHz != pVRDEVoice->pHostVoiceIn.Props.uFrequency)
697 {
698 /* @todo if the above condition is false then pVRDEVoice->uFrequency will remain 0 */
699 /*mpDrv->pUpPort->pfnPrepareAudioConversion(mpDrv->pUpPort, iSampleHz,
700 pVRDEVoice->pHostVoiceIn.Props.uFrequency,
701 &pVRDEVoice->rate);*/
702 pVRDEVoice->uFrequency = iSampleHz;
703 LogFlowFunc(("pVRDEVoice assigned requested freq =%d\n", pVRDEVoice->uFrequency));
704 }
705 return VINF_SUCCESS;
706}
707
708/*
709 * pvContext: pointer to VRDP voice returned by the VRDP server. The is same pointer that we initialized in
710 * drvAudioVideoRecDisableEnableIn VOICE_ENABLE case.
711 */
712int AudioVideoRec::handleVideoRecSvrCmdAudioInputEventData(void *pvContext, const void *pvData, uint32_t cbData)
713{
714 PVIDEORECAUDIOIN pVRDEVoice = (PVIDEORECAUDIOIN)pvContext;
715 PPDMAUDIOSAMPLE pHostStereoSampleBuf; /* target sample buffer */
716 PPDMAUDIOSAMPLE pConvertedSampleBuf; /* samples adjusted for rate */
717 uint32_t cSamples = cbData / pVRDEVoice->cBytesPerFrame; /* Count of samples */
718 void * pTmpSampleBuf = NULL;
719 uint32_t cConvertedSamples; /* samples adjusted for rate */
720 uint32_t cbSamples; /* count of bytes occupied by samples */
721 int rc = VINF_SUCCESS;
722
723 LogFlowFunc(("handleVRDPCmdInputEventData cbData = %d, bytesperfram=%d\n",
724 cbData, pVRDEVoice->cBytesPerFrame));
725
726 vrdeReallocSampleBuf(pVRDEVoice, cSamples);
727 pHostStereoSampleBuf = (PPDMAUDIOSAMPLE)pVRDEVoice->pvSamplesBuffer;
728 pVRDEVoice->convAudioDevFmtToStSampl(pHostStereoSampleBuf, pvData, cSamples, &videorec_nominal_volume);
729
730 /* count of rate adjusted samples */
731 pVRDEVoice->uFrequency = 22100; /* @todo handle this. How pVRDEVoice will get proper value */
732 cConvertedSamples = (cSamples * pVRDEVoice->pHostVoiceIn.Props.uHz) / pVRDEVoice->uFrequency;
733 vrdeReallocRateAdjSampleBuf(pVRDEVoice, cConvertedSamples);
734
735 pConvertedSampleBuf = (PPDMAUDIOSAMPLE)pVRDEVoice->pvRateBuffer;
736
737 if (pConvertedSampleBuf)
738 {
739 uint32_t cSampleSrc = cSamples;
740 uint32_t cSampleDst = cConvertedSamples;
741 /*mpDrv->pUpPort->pfnDoRateConversion(mpDrv->pUpPort, pVRDEVoice->rate, pHostStereoSampleBuf,
742 pConvertedSampleBuf, &cSampleSrc, &cConvertedSamples);*/
743 pTmpSampleBuf = pConvertedSampleBuf;
744 cbSamples = cConvertedSamples * sizeof(PDMAUDIOSAMPLE);
745 }
746
747
748 if (cbSamples)
749 rc = vrdeRecordingCallback(pVRDEVoice, cbSamples, pTmpSampleBuf);
750
751 return rc;
752}
753
754/*
755 * pvContext: pointer to VRDP voice returned by the VRDP server. The is same pointer that we initialized in
756 * drvAudioVideoRecDisableEnableIn VOICE_ENABLE case.
757 */
758int AudioVideoRec::handleVideoRecSvrCmdAudioInputEventEnd(void *pvContext)
759{
760 LogFlowFuncEnter();
761
762 PVIDEORECAUDIOIN pVRDEVoice = (PVIDEORECAUDIOIN)pvContext;
763 AssertPtrReturn(pVRDEVoice, VERR_INVALID_POINTER);
764
765 /* The caller will not use this context anymore. */
766 if (pVRDEVoice->rate)
767 {
768 //mpDrv->pUpPort->pfnEndAudioConversion(mpDrv->pUpPort, pVRDEVoice->rate);
769 }
770
771 if (pVRDEVoice->pvSamplesBuffer)
772 {
773 RTMemFree(pVRDEVoice->pvSamplesBuffer);
774 pVRDEVoice->pvSamplesBuffer = NULL;
775 }
776
777 if (pVRDEVoice->pvRateBuffer)
778 {
779 RTMemFree(pVRDEVoice->pvRateBuffer);
780 pVRDEVoice->pvRateBuffer = NULL;
781 }
782
783 return VINF_SUCCESS;
784}
785
786/**
787 * Construct a VRDE audio driver instance.
788 *
789 * @copydoc FNPDMDRVCONSTRUCT
790 */
791/* static */
792DECLCALLBACK(int) AudioVideoRec::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
793{
794 PDRVAUDIOVIDEOREC pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVIDEOREC);
795 LogRel(("Audio: Initializing VRDE driver\n"));
796 LogFlowFunc(("fFlags=0x%x\n", fFlags));
797
798 /* we save the address of AudioVideoRec in Object node in CFGM tree and address of VRDP server in
799 * ObjectVRDPServer node. So presence of both is necessary.
800 */
801 //if (!CFGMR3AreValuesValid(pCfg, "Object\0") || !CFGMR3AreValuesValid(pCfg, "ObjectVRDPServer\0"))
802 // return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
803 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
804 ("Configuration error: Not possible to attach anything to this driver!\n"),
805 VERR_PDM_DRVINS_NO_ATTACH);
806
807 /*
808 * Init the static parts.
809 */
810 pThis->pDrvIns = pDrvIns;
811 /* IBase */
812 pDrvIns->IBase.pfnQueryInterface = drvAudioVideoRecQueryInterface;
813 pThis->IHostAudioR3.pfnInitIn = drvAudioVideoRecInitIn;
814 pThis->IHostAudioR3.pfnInitOut = drvAudioVideoRecInitOut;
815 pThis->IHostAudioR3.pfnControlOut = drvAudioVideoRecControlOut;
816 pThis->IHostAudioR3.pfnControlIn = drvAudioVideoRecControlIn;
817 pThis->IHostAudioR3.pfnFiniIn = drvAudioVideoRecFiniIn;
818 pThis->IHostAudioR3.pfnFiniOut = drvAudioVideoRecFiniOut;
819 pThis->IHostAudioR3.pfnCaptureIn = drvAudioVideoRecCaptureIn;
820 pThis->IHostAudioR3.pfnPlayOut = drvAudioVideoRecPlayOut;
821 pThis->IHostAudioR3.pfnGetConf = drvAudioVideoRecGetConf;
822 pThis->IHostAudioR3.pfnInit = drvAudioVideoRecInit;
823
824 /* Get VRDPServer pointer. */
825 void *pvUser;
826 int rc = CFGMR3QueryPtr(pCfg, "ObjectVRDPServer", &pvUser);
827 if (RT_FAILURE(rc))
828 {
829 AssertMsgFailed(("Confguration error: No/bad \"ObjectVRDPServer\" value, rc=%Rrc\n", rc));
830 return rc;
831 }
832
833 /* CFGM tree saves the pointer to ConsoleVRDPServer in the Object node of AudioVideoRec. */
834 pThis->pConsoleVRDPServer = (ConsoleVRDPServer *)pvUser;
835
836 pvUser = NULL;
837 rc = CFGMR3QueryPtr(pCfg, "Object", &pvUser);
838 if (RT_FAILURE(rc))
839 {
840 AssertMsgFailed(("Confguration error: No/bad \"Object\" value, rc=%Rrc\n", rc));
841 return rc;
842 }
843
844 pThis->pAudioVideoRec = (AudioVideoRec *)pvUser;
845 pThis->pAudioVideoRec->mpDrv = pThis;
846
847 /*
848 * Get the interface for the above driver (DrvAudio) to make mixer/conversion calls.
849 * Described in CFGM tree.
850 */
851 pThis->pUpPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIAUDIOCONNECTOR);
852 if (!pThis->pUpPort)
853 {
854 AssertMsgFailed(("Configuration error: No upper interface specified!\n"));
855 return VERR_PDM_MISSING_INTERFACE_ABOVE;
856 }
857
858 return VINF_SUCCESS;
859}
860
861/* static */
862DECLCALLBACK(void) AudioVideoRec::drvDestruct(PPDMDRVINS pDrvIns)
863{
864 LogFlowFuncEnter();
865}
866
867/**
868 * VRDE audio driver registration record.
869 */
870const PDMDRVREG AudioVideoRec::DrvReg =
871{
872 PDM_DRVREG_VERSION,
873 /* szName */
874 "AudioVideoRec",
875 /* szRCMod */
876 "",
877 /* szR0Mod */
878 "",
879 /* pszDescription */
880 "Audio driver for video recording",
881 /* fFlags */
882 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
883 /* fClass. */
884 PDM_DRVREG_CLASS_AUDIO,
885 /* cMaxInstances */
886 ~0U,
887 /* cbInstance */
888 sizeof(DRVAUDIOVIDEOREC),
889 /* pfnConstruct */
890 AudioVideoRec::drvConstruct,
891 /* pfnDestruct */
892 AudioVideoRec::drvDestruct,
893 /* pfnRelocate */
894 NULL,
895 /* pfnIOCtl */
896 NULL,
897 /* pfnPowerOn */
898 NULL,
899 /* pfnReset */
900 NULL,
901 /* pfnSuspend */
902 NULL,
903 /* pfnResume */
904 NULL,
905 /* pfnAttach */
906 NULL,
907 /* pfnDetach */
908 NULL,
909 /* pfnPowerOff */
910 NULL,
911 /* pfnSoftReset */
912 NULL,
913 /* u32EndVersion */
914 PDM_DRVREG_VERSION
915};
916
Note: See TracBrowser for help on using the repository browser.

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