VirtualBox

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

Last change on this file since 55043 was 54491, checked in by vboxsync, 10 years ago

PDM/Audio: Fixed crashes on termination.

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