VirtualBox

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

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

Audio/Main: Some (ground) work for audio support for video recording.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.3 KB
Line 
1/* $Id: DrvAudioVideoRec.cpp 65162 2017-01-05 17:26:48Z vboxsync $ */
2/** @file
3 * Video recording audio backend for Main.
4 */
5
6/*
7 * Copyright (C) 2016-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. 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
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DRV_AUDIO
23#include <VBox/log.h>
24#include "DrvAudioVideoRec.h"
25#include "ConsoleImpl.h"
26#include "ConsoleVRDPServer.h"
27
28#include "Logging.h"
29
30#include "../../Devices/Audio/DrvAudio.h"
31#include "../../Devices/Audio/AudioMixBuffer.h"
32
33#include <iprt/mem.h>
34#include <iprt/cdefs.h>
35#include <iprt/circbuf.h>
36
37#include <VBox/vmm/pdmaudioifs.h>
38#include <VBox/vmm/pdmdrv.h>
39#include <VBox/vmm/cfgm.h>
40#include <VBox/err.h>
41
42
43/*********************************************************************************************************************************
44* Structures and Typedefs *
45*********************************************************************************************************************************/
46/**
47 * Video recording audio driver instance data.
48 */
49typedef struct DRVAUDIOVIDEOREC
50{
51 /** Pointer to audio video recording object. */
52 AudioVideoRec *pAudioVideoRec;
53 /** Pointer to the driver instance structure. */
54 PPDMDRVINS pDrvIns;
55 /** Pointer to host audio interface. */
56 PDMIHOSTAUDIO IHostAudio;
57 /** Pointer to the VRDP's console object. */
58 ConsoleVRDPServer *pConsoleVRDPServer;
59 /** Pointer to the DrvAudio port interface that is above us. */
60 PPDMIAUDIOCONNECTOR pDrvAudio;
61} DRVAUDIOVIDEOREC, *PDRVAUDIOVIDEOREC;
62
63typedef struct AVRECSTREAMOUT
64{
65 /** Note: Always must come first! */
66 PDMAUDIOSTREAM Stream;
67 /** The PCM properties of this stream. */
68 PDMAUDIOPCMPROPS Props;
69 uint64_t old_ticks;
70 uint64_t cSamplesSentPerSec;
71} AVRECSTREAMOUT, *PAVRECSTREAMOUT;
72
73/** Makes DRVAUDIOVIDEOREC out of PDMIHOSTAUDIO. */
74#define PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface) \
75 ( (PDRVAUDIOVIDEOREC)((uintptr_t)pInterface - RT_OFFSETOF(DRVAUDIOVIDEOREC, IHostAudio)) )
76
77
78static int avRecCreateStreamOut(PPDMIHOSTAUDIO pInterface,
79 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
80{
81 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
82 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
83 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
84 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
85
86 PAVRECSTREAMOUT pStreamOut = (PAVRECSTREAMOUT)pStream;
87
88 int rc = DrvAudioHlpStreamCfgToProps(pCfgReq, &pStreamOut->Props);
89 if (RT_SUCCESS(rc))
90 {
91 if (pCfgAcq)
92 pCfgAcq->cSampleBufferSize = _4K; /** @todo Make this configurable. */
93 }
94
95 LogFlowFuncLeaveRC(VINF_SUCCESS);
96 return VINF_SUCCESS;
97}
98
99
100static int avRecControlStreamOut(PPDMIHOSTAUDIO pInterface,
101 PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
102{
103 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
104 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
105 RT_NOREF(enmStreamCmd);
106
107 PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface);
108 RT_NOREF(pThis);
109
110 LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
111
112 AudioMixBufReset(&pStream->MixBuf);
113
114 return VINF_SUCCESS;
115}
116
117
118/**
119 * @interface_method_impl{PDMIHOSTAUDIO,pfnInit}
120 */
121static DECLCALLBACK(int) drvAudioVideoRecInit(PPDMIHOSTAUDIO pInterface)
122{
123 RT_NOREF(pInterface);
124 LogFlowFuncEnter();
125
126 return VINF_SUCCESS;
127}
128
129
130/**
131 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
132 */
133static DECLCALLBACK(int) drvAudioVideoRecStreamCapture(PPDMIHOSTAUDIO pInterface,
134 PPDMAUDIOSTREAM pStream, void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
135{
136 RT_NOREF(pInterface, pStream, pvBuf, cbBuf);
137
138 if (pcbRead)
139 *pcbRead = 0;
140
141 return VINF_SUCCESS;
142}
143
144
145/**
146 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
147 */
148static DECLCALLBACK(int) drvAudioVideoRecStreamPlay(PPDMIHOSTAUDIO pInterface,
149 PPDMAUDIOSTREAM pStream, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
150{
151 RT_NOREF2(pvBuf, cbBuf);
152
153 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
154 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
155 /* pcbWritten is optional. */
156
157 PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface);
158 PAVRECSTREAMOUT pStreamOut = (PAVRECSTREAMOUT)pStream;
159
160 uint32_t cLive = AudioMixBufLive(&pStream->MixBuf);
161
162 uint64_t now = PDMDrvHlpTMGetVirtualTime(pThis->pDrvIns);
163 uint64_t ticks = now - pStreamOut->old_ticks;
164 uint64_t ticks_per_second = PDMDrvHlpTMGetVirtualFreq(pThis->pDrvIns);
165
166 /* Minimize the rounding error: samples = int((ticks * freq) / ticks_per_second + 0.5). */
167 uint32_t cSamplesPlayed = (int)((2 * ticks * pStreamOut->Props.uHz + ticks_per_second) / ticks_per_second / 2);
168
169 /* Don't play more than available. */
170 if (cSamplesPlayed > cLive)
171 cSamplesPlayed = cLive;
172
173 /* Remember when samples were consumed. */
174 pStreamOut->old_ticks = now;
175
176 int cSamplesToSend = cSamplesPlayed;
177
178 LogFlowFunc(("uFreq=%RU32, cChan=%RU8, cBits=%RU8, fSigned=%RTbool, cSamplesToSend=%RU32\n",
179 pStreamOut->Props.uHz, pStreamOut->Props.cChannels,
180 pStreamOut->Props.cBits, pStreamOut->Props.fSigned, cSamplesToSend));
181
182 /*
183 * Call the VRDP server with the data.
184 */
185 uint32_t cReadTotal = 0;
186
187 PPDMAUDIOSAMPLE pSamples;
188 uint32_t cRead;
189 int rc = AudioMixBufAcquire(&pStream->MixBuf, cSamplesToSend,
190 &pSamples, &cRead);
191 if ( RT_SUCCESS(rc)
192 && cRead)
193 {
194 cReadTotal = cRead;
195
196 if (rc == VINF_TRY_AGAIN)
197 {
198 rc = AudioMixBufAcquire(&pStream->MixBuf, cSamplesToSend - cRead,
199 &pSamples, &cRead);
200
201 cReadTotal += cRead;
202 }
203 }
204
205 AudioMixBufFinish(&pStream->MixBuf, cSamplesToSend);
206
207 /*
208 * Always report back all samples acquired, regardless of whether the
209 * VRDP server actually did process those.
210 */
211 if (pcbWritten)
212 *pcbWritten = cReadTotal;
213
214 LogFlowFunc(("cReadTotal=%RU32, rc=%Rrc\n", cReadTotal, rc));
215 return rc;
216}
217
218
219static int avRecDestroyStreamIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
220{
221 RT_NOREF(pStream);
222 PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface);
223
224 if (pThis->pConsoleVRDPServer)
225 pThis->pConsoleVRDPServer->SendAudioInputEnd(NULL);
226
227 return VINF_SUCCESS;
228}
229
230
231static int avRecDestroyStreamOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
232{
233 RT_NOREF(pInterface);
234 RT_NOREF(pStream);
235
236 return VINF_SUCCESS;
237}
238
239
240/**
241 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
242 */
243static DECLCALLBACK(int) drvAudioVideoRecGetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
244{
245 NOREF(pInterface);
246 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
247
248 pBackendCfg->cbStreamOut = sizeof(AVRECSTREAMOUT);
249 pBackendCfg->cbStreamIn = 0;
250 pBackendCfg->cMaxStreamsIn = 0;
251 pBackendCfg->cMaxStreamsOut = UINT32_MAX;
252
253 return VINF_SUCCESS;
254}
255
256
257/**
258 * @interface_method_impl{PDMIHOSTAUDIO,pfnShutdown}
259 */
260static DECLCALLBACK(void) drvAudioVideoRecShutdown(PPDMIHOSTAUDIO pInterface)
261{
262 RT_NOREF(pInterface);
263}
264
265
266/**
267 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
268 */
269static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvAudioVideoRecGetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
270{
271 RT_NOREF(enmDir);
272 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
273
274 return PDMAUDIOBACKENDSTS_RUNNING;
275}
276
277
278/**
279 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
280 */
281static DECLCALLBACK(int) drvAudioVideoRecStreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream,
282 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
283{
284 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
285 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
286 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
287 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
288
289 if (pCfgReq->enmDir == PDMAUDIODIR_OUT)
290 return avRecCreateStreamOut(pInterface, pStream, pCfgReq, pCfgAcq);
291
292 return VERR_NOT_SUPPORTED;
293}
294
295
296/**
297 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
298 */
299static DECLCALLBACK(int) drvAudioVideoRecStreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
300{
301 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
302 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
303
304 int rc;
305 if (pStream->enmDir == PDMAUDIODIR_IN)
306 rc = avRecDestroyStreamIn(pInterface, pStream);
307 else
308 rc = avRecDestroyStreamOut(pInterface, pStream);
309
310 return rc;
311}
312
313
314/**
315 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
316 */
317static DECLCALLBACK(int) drvAudioVideoRecStreamControl(PPDMIHOSTAUDIO pInterface,
318 PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
319{
320 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
321 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
322
323 Assert(pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST);
324
325 if (pStream->enmDir == PDMAUDIODIR_OUT)
326 return avRecControlStreamOut(pInterface, pStream, enmStreamCmd);
327
328 return VINF_SUCCESS;
329}
330
331
332/**
333 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus}
334 */
335static DECLCALLBACK(PDMAUDIOSTRMSTS) drvAudioVideoRecStreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
336{
337 NOREF(pInterface);
338 NOREF(pStream);
339
340 return ( PDMAUDIOSTRMSTS_FLAG_INITIALIZED | PDMAUDIOSTRMSTS_FLAG_ENABLED
341 | PDMAUDIOSTRMSTS_FLAG_DATA_READABLE | PDMAUDIOSTRMSTS_FLAG_DATA_WRITABLE);
342}
343
344
345/**
346 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamIterate}
347 */
348static DECLCALLBACK(int) drvAudioVideoRecStreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
349{
350 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
351 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
352
353 LogFlowFuncEnter();
354
355 /* Nothing to do here for video recording. */
356 return VINF_SUCCESS;
357}
358
359
360/**
361 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
362 */
363static DECLCALLBACK(void *) drvAudioVideoRecQueryInterface(PPDMIBASE pInterface, const char *pszIID)
364{
365 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
366 PDRVAUDIOVIDEOREC pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVIDEOREC);
367
368 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
369 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
370 return NULL;
371}
372
373
374AudioVideoRec::AudioVideoRec(Console *pConsole)
375 : mpDrv(NULL),
376 mParent(pConsole)
377{
378}
379
380
381AudioVideoRec::~AudioVideoRec(void)
382{
383 if (mpDrv)
384 {
385 mpDrv->pAudioVideoRec = NULL;
386 mpDrv = NULL;
387 }
388}
389
390
391/**
392 * Construct a audio video recording driver instance.
393 *
394 * @copydoc FNPDMDRVCONSTRUCT
395 */
396/* static */
397DECLCALLBACK(int) AudioVideoRec::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
398{
399 RT_NOREF(fFlags);
400
401 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
402 PDRVAUDIOVIDEOREC pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVIDEOREC);
403
404 AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
405 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
406
407 LogRel(("Audio: Initializing video recording driver\n"));
408 LogFlowFunc(("fFlags=0x%x\n", fFlags));
409
410 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
411 ("Configuration error: Not possible to attach anything to this driver!\n"),
412 VERR_PDM_DRVINS_NO_ATTACH);
413
414 /*
415 * Init the static parts.
416 */
417 pThis->pDrvIns = pDrvIns;
418 /* IBase */
419 pDrvIns->IBase.pfnQueryInterface = drvAudioVideoRecQueryInterface;
420 /* IHostAudio */
421 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvAudioVideoRec);
422
423 /*
424 * Get the ConsoleVRDPServer object pointer.
425 */
426 void *pvUser;
427 int rc = CFGMR3QueryPtr(pCfg, "ObjectVRDPServer", &pvUser); /** @todo r=andy Get rid of this hack and use IHostAudio::SetCallback. */
428 AssertMsgRCReturn(rc, ("Confguration error: No/bad \"ObjectVRDPServer\" value, rc=%Rrc\n", rc), rc);
429
430 /* CFGM tree saves the pointer to ConsoleVRDPServer in the Object node of AudioVideoRec. */
431 pThis->pConsoleVRDPServer = (ConsoleVRDPServer *)pvUser;
432
433 /*
434 * Get the AudioVideoRec object pointer.
435 */
436 pvUser = NULL;
437 rc = CFGMR3QueryPtr(pCfg, "Object", &pvUser); /** @todo r=andy Get rid of this hack and use IHostAudio::SetCallback. */
438 AssertMsgRCReturn(rc, ("Confguration error: No/bad \"Object\" value, rc=%Rrc\n", rc), rc);
439
440 pThis->pAudioVideoRec = (AudioVideoRec *)pvUser;
441 pThis->pAudioVideoRec->mpDrv = pThis;
442
443 /*
444 * Get the interface for the above driver (DrvAudio) to make mixer/conversion calls.
445 * Described in CFGM tree.
446 */
447 pThis->pDrvAudio = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIAUDIOCONNECTOR);
448 AssertMsgReturn(pThis->pDrvAudio, ("Configuration error: No upper interface specified!\n"), VERR_PDM_MISSING_INTERFACE_ABOVE);
449
450 return VINF_SUCCESS;
451}
452
453
454/**
455 * @interface_method_impl{PDMDRVREG,pfnDestruct}
456 */
457/* static */
458DECLCALLBACK(void) AudioVideoRec::drvDestruct(PPDMDRVINS pDrvIns)
459{
460 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
461 PDRVAUDIOVIDEOREC pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVIDEOREC);
462 LogFlowFuncEnter();
463
464 /*
465 * If the AudioVideoRec object is still alive, we must clear it's reference to
466 * us since we'll be invalid when we return from this method.
467 */
468 if (pThis->pAudioVideoRec)
469 {
470 pThis->pAudioVideoRec->mpDrv = NULL;
471 pThis->pAudioVideoRec = NULL;
472 }
473}
474
475
476/**
477 * Video recording audio driver registration record.
478 */
479const PDMDRVREG AudioVideoRec::DrvReg =
480{
481 PDM_DRVREG_VERSION,
482 /* szName */
483 "AudioVideoRec",
484 /* szRCMod */
485 "",
486 /* szR0Mod */
487 "",
488 /* pszDescription */
489 "Audio driver for video recording",
490 /* fFlags */
491 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
492 /* fClass. */
493 PDM_DRVREG_CLASS_AUDIO,
494 /* cMaxInstances */
495 ~0U,
496 /* cbInstance */
497 sizeof(DRVAUDIOVIDEOREC),
498 /* pfnConstruct */
499 AudioVideoRec::drvConstruct,
500 /* pfnDestruct */
501 AudioVideoRec::drvDestruct,
502 /* pfnRelocate */
503 NULL,
504 /* pfnIOCtl */
505 NULL,
506 /* pfnPowerOn */
507 NULL,
508 /* pfnReset */
509 NULL,
510 /* pfnSuspend */
511 NULL,
512 /* pfnResume */
513 NULL,
514 /* pfnAttach */
515 NULL,
516 /* pfnDetach */
517 NULL,
518 /* pfnPowerOff */
519 NULL,
520 /* pfnSoftReset */
521 NULL,
522 /* u32EndVersion */
523 PDM_DRVREG_VERSION
524};
525
Note: See TracBrowser for help on using the repository browser.

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