VirtualBox

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

Last change on this file since 63360 was 62117, checked in by vboxsync, 8 years ago

Audio: Bugfixes, logging.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.6 KB
Line 
1/* $Id: DrvAudioVideoRec.cpp 62117 2016-07-07 16:16:01Z vboxsync $ */
2/** @file
3 * Video recording audio backend for Main.
4 */
5
6/*
7 * Copyright (C) 2014-2016 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 IHostAudio;
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 PDMAUDIOFMT_S8:
142 fSigned = 1;
143 case PDMAUDIOFMT_U8:
144 break;
145
146 case PDMAUDIOFMT_S16:
147 fSigned = 1;
148 case PDMAUDIOFMT_U16:
149 cBits = 16;
150 cShift = 1;
151 break;
152
153 case PDMAUDIOFMT_S32:
154 fSigned = 1;
155 case PDMAUDIOFMT_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->cbBitrate = (pProps->cBits * pProps->uHz * pProps->cChannels) / 8;
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, IHostAudio);
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, IHostAudio);
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, IHostAudio);
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, IHostAudio);
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, IHostAudio);
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, IHostAudio);
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, IHostAudio);
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 pCfg)
620{
621 NOREF(pInterface);
622 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
623
624 pCfg->cbStreamIn = sizeof(VIDEORECAUDIOIN);
625 pCfg->cbStreamOut = sizeof(VIDEORECAUDIOOUT);
626 pCfg->cMaxStreamsIn = UINT32_MAx;
627 pCfg->cMaxStreamsOut = UINT32_MAX;
628 pCfg->cSources = 1;
629 pCfg->cSinks = 1;
630
631 return VINF_SUCCESS;
632}
633
634static DECLCALLBACK(void) drvAudioVideoRecShutdown(PPDMIHOSTAUDIO pInterface)
635{
636 NOREF(pInterface);
637}
638
639/**
640 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
641 */
642static DECLCALLBACK(void *) drvAudioVideoRecQueryInterface(PPDMIBASE pInterface, const char *pszIID)
643{
644 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
645 PDRVAUDIOVIDEOREC pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVIDEOREC);
646 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
647 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
648 return NULL;
649}
650
651AudioVideoRec::AudioVideoRec(Console *pConsole)
652 : mpDrv(NULL),
653 mParent(pConsole)
654{
655}
656
657AudioVideoRec::~AudioVideoRec(void)
658{
659 if (mpDrv)
660 {
661 mpDrv->pAudioVideoRec = NULL;
662 mpDrv = NULL;
663 }
664}
665
666int AudioVideoRec::handleVideoRecSvrCmdAudioInputIntercept(bool fIntercept)
667{
668 LogFlowThisFunc(("fIntercept=%RTbool\n", fIntercept));
669 return VINF_SUCCESS;
670}
671
672int AudioVideoRec::handleVideoRecSvrCmdAudioInputEventBegin(void *pvContext, int iSampleHz, int cChannels,
673 int cBits, bool fUnsigned)
674{
675 int bitIdx;
676 PVIDEORECAUDIOIN pVRDEVoice = (PVIDEORECAUDIOIN)pvContext;
677 LogFlowFunc(("handleVRDPCmdInputEventBegin\n"));
678 /* Prepare a format convertion for the actually used format. */
679 pVRDEVoice->cBytesPerFrame = ((cBits + 7) / 8) * cChannels;
680 if (cBits == 16)
681 {
682 bitIdx = 1;
683 }
684 else if (cBits == 32)
685 {
686 bitIdx = 2;
687 }
688 else
689 {
690 bitIdx = 0;
691 }
692
693 //PPDMIAUDIOCONNECTOR pPort = server->mConsole->getAudioVideoRec()->getDrvAudioPort();
694 /* Call DrvAudio interface to get the t_sample type conversion function */
695 /*mpDrv->pUpPort->pfnConvDevFmtToStSample(mpDrv->pUpPort,
696 (cChannels == 2) ? 1 : 0,
697 !fUnsigned, 0, bitIdx,
698 pVRDEVoice->convAudioDevFmtToStSampl);*/
699 if (pVRDEVoice->convAudioDevFmtToStSampl)
700 {
701 LogFlowFunc(("Failed to get the conversion function \n"));
702 }
703 LogFlowFunc(("Required freq as requested by VRDP Server = %d\n", iSampleHz));
704 //if (iSampleHz && iSampleHz != pVRDEVoice->pHostVoiceIn.Props.uFrequency)
705 {
706 /* @todo if the above condition is false then pVRDEVoice->uFrequency will remain 0 */
707 /*mpDrv->pUpPort->pfnPrepareAudioConversion(mpDrv->pUpPort, iSampleHz,
708 pVRDEVoice->pHostVoiceIn.Props.uFrequency,
709 &pVRDEVoice->rate);*/
710 pVRDEVoice->uFrequency = iSampleHz;
711 LogFlowFunc(("pVRDEVoice assigned requested freq =%d\n", pVRDEVoice->uFrequency));
712 }
713 return VINF_SUCCESS;
714}
715
716/*
717 * pvContext: pointer to VRDP voice returned by the VRDP server. The is same pointer that we initialized in
718 * drvAudioVideoRecDisableEnableIn VOICE_ENABLE case.
719 */
720int AudioVideoRec::handleVideoRecSvrCmdAudioInputEventData(void *pvContext, const void *pvData, uint32_t cbData)
721{
722 PVIDEORECAUDIOIN pVRDEVoice = (PVIDEORECAUDIOIN)pvContext;
723 PPDMAUDIOSAMPLE pHostStereoSampleBuf; /* target sample buffer */
724 PPDMAUDIOSAMPLE pConvertedSampleBuf; /* samples adjusted for rate */
725 uint32_t cSamples = cbData / pVRDEVoice->cBytesPerFrame; /* Count of samples */
726 void * pTmpSampleBuf = NULL;
727 uint32_t cConvertedSamples; /* samples adjusted for rate */
728 uint32_t cbSamples = 0; /* count of bytes occupied by samples */
729 int rc = VINF_SUCCESS;
730
731 LogFlowFunc(("handleVRDPCmdInputEventData cbData = %d, bytesperfram=%d\n",
732 cbData, pVRDEVoice->cBytesPerFrame));
733
734 vrdeReallocSampleBuf(pVRDEVoice, cSamples);
735 pHostStereoSampleBuf = (PPDMAUDIOSAMPLE)pVRDEVoice->pvSamplesBuffer;
736 pVRDEVoice->convAudioDevFmtToStSampl(pHostStereoSampleBuf, pvData, cSamples, &videorec_nominal_volume);
737
738 /* count of rate adjusted samples */
739 pVRDEVoice->uFrequency = 22100; /* @todo handle this. How pVRDEVoice will get proper value */
740 cConvertedSamples = (cSamples * pVRDEVoice->pHostVoiceIn.Props.uHz) / pVRDEVoice->uFrequency;
741 vrdeReallocRateAdjSampleBuf(pVRDEVoice, cConvertedSamples);
742
743 pConvertedSampleBuf = (PPDMAUDIOSAMPLE)pVRDEVoice->pvRateBuffer;
744 if (pConvertedSampleBuf)
745 {
746 uint32_t cSampleSrc = cSamples;
747 uint32_t cSampleDst = cConvertedSamples;
748 /*mpDrv->pUpPort->pfnDoRateConversion(mpDrv->pUpPort, pVRDEVoice->rate, pHostStereoSampleBuf,
749 pConvertedSampleBuf, &cSampleSrc, &cConvertedSamples);*/
750 pTmpSampleBuf = pConvertedSampleBuf;
751 cbSamples = cConvertedSamples * sizeof(PDMAUDIOSAMPLE);
752 }
753
754 if (cbSamples)
755 rc = vrdeRecordingCallback(pVRDEVoice, cbSamples, pTmpSampleBuf);
756
757 return rc;
758}
759
760/*
761 * pvContext: pointer to VRDP voice returned by the VRDP server. The is same pointer that we initialized in
762 * drvAudioVideoRecDisableEnableIn VOICE_ENABLE case.
763 */
764int AudioVideoRec::handleVideoRecSvrCmdAudioInputEventEnd(void *pvContext)
765{
766 LogFlowFuncEnter();
767
768 PVIDEORECAUDIOIN pVRDEVoice = (PVIDEORECAUDIOIN)pvContext;
769 AssertPtrReturn(pVRDEVoice, VERR_INVALID_POINTER);
770
771 /* The caller will not use this context anymore. */
772 if (pVRDEVoice->rate)
773 {
774 //mpDrv->pUpPort->pfnEndAudioConversion(mpDrv->pUpPort, pVRDEVoice->rate);
775 }
776
777 if (pVRDEVoice->pvSamplesBuffer)
778 {
779 RTMemFree(pVRDEVoice->pvSamplesBuffer);
780 pVRDEVoice->pvSamplesBuffer = NULL;
781 }
782
783 if (pVRDEVoice->pvRateBuffer)
784 {
785 RTMemFree(pVRDEVoice->pvRateBuffer);
786 pVRDEVoice->pvRateBuffer = NULL;
787 }
788
789 return VINF_SUCCESS;
790}
791
792/**
793 * Construct a VRDE audio driver instance.
794 *
795 * @copydoc FNPDMDRVCONSTRUCT
796 */
797/* static */
798DECLCALLBACK(int) AudioVideoRec::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
799{
800 PDRVAUDIOVIDEOREC pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVIDEOREC);
801 LogRel(("Audio: Initializing VRDE driver\n"));
802 LogFlowFunc(("fFlags=0x%x\n", fFlags));
803
804 /* we save the address of AudioVideoRec in Object node in CFGM tree and address of VRDP server in
805 * ObjectVRDPServer node. So presence of both is necessary.
806 */
807 //if (!CFGMR3AreValuesValid(pCfg, "Object\0") || !CFGMR3AreValuesValid(pCfg, "ObjectVRDPServer\0"))
808 // return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
809 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
810 ("Configuration error: Not possible to attach anything to this driver!\n"),
811 VERR_PDM_DRVINS_NO_ATTACH);
812
813 /*
814 * Init the static parts.
815 */
816 pThis->pDrvIns = pDrvIns;
817 /* IBase */
818 pDrvIns->IBase.pfnQueryInterface = drvAudioVideoRecQueryInterface;
819 /* IHostAudio */
820 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvAudioVideoRec);
821
822 /* Get VRDPServer pointer. */
823 void *pvUser;
824 int rc = CFGMR3QueryPtr(pCfg, "ObjectVRDPServer", &pvUser);
825 if (RT_FAILURE(rc))
826 {
827 AssertMsgFailed(("Confguration error: No/bad \"ObjectVRDPServer\" value, rc=%Rrc\n", rc));
828 return rc;
829 }
830
831 /* CFGM tree saves the pointer to ConsoleVRDPServer in the Object node of AudioVideoRec. */
832 pThis->pConsoleVRDPServer = (ConsoleVRDPServer *)pvUser;
833
834 pvUser = NULL;
835 rc = CFGMR3QueryPtr(pCfg, "Object", &pvUser);
836 if (RT_FAILURE(rc))
837 {
838 AssertMsgFailed(("Confguration error: No/bad \"Object\" value, rc=%Rrc\n", rc));
839 return rc;
840 }
841
842 pThis->pAudioVideoRec = (AudioVideoRec *)pvUser;
843 pThis->pAudioVideoRec->mpDrv = pThis;
844
845 /*
846 * Get the interface for the above driver (DrvAudio) to make mixer/conversion calls.
847 * Described in CFGM tree.
848 */
849 pThis->pUpPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIAUDIOCONNECTOR);
850 if (!pThis->pUpPort)
851 {
852 AssertMsgFailed(("Configuration error: No upper interface specified!\n"));
853 return VERR_PDM_MISSING_INTERFACE_ABOVE;
854 }
855
856 return VINF_SUCCESS;
857}
858
859/* static */
860DECLCALLBACK(void) AudioVideoRec::drvDestruct(PPDMDRVINS pDrvIns)
861{
862 LogFlowFuncEnter();
863}
864
865/**
866 * VRDE audio driver registration record.
867 */
868const PDMDRVREG AudioVideoRec::DrvReg =
869{
870 PDM_DRVREG_VERSION,
871 /* szName */
872 "AudioVideoRec",
873 /* szRCMod */
874 "",
875 /* szR0Mod */
876 "",
877 /* pszDescription */
878 "Audio driver for video recording",
879 /* fFlags */
880 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
881 /* fClass. */
882 PDM_DRVREG_CLASS_AUDIO,
883 /* cMaxInstances */
884 ~0U,
885 /* cbInstance */
886 sizeof(DRVAUDIOVIDEOREC),
887 /* pfnConstruct */
888 AudioVideoRec::drvConstruct,
889 /* pfnDestruct */
890 AudioVideoRec::drvDestruct,
891 /* pfnRelocate */
892 NULL,
893 /* pfnIOCtl */
894 NULL,
895 /* pfnPowerOn */
896 NULL,
897 /* pfnReset */
898 NULL,
899 /* pfnSuspend */
900 NULL,
901 /* pfnResume */
902 NULL,
903 /* pfnAttach */
904 NULL,
905 /* pfnDetach */
906 NULL,
907 /* pfnPowerOff */
908 NULL,
909 /* pfnSoftReset */
910 NULL,
911 /* u32EndVersion */
912 PDM_DRVREG_VERSION
913};
914
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