VirtualBox

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

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

VideoRec: Update.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 22.1 KB
Line 
1/* $Id: DrvAudioVideoRec.cpp 65330 2017-01-16 13:38:36Z 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.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
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
27#include "Logging.h"
28
29#include "../../Devices/Audio/DrvAudio.h"
30#include "../../Devices/Audio/AudioMixBuffer.h"
31#include "EbmlWriter.h"
32
33#include <iprt/mem.h>
34#include <iprt/cdefs.h>
35
36#include <VBox/vmm/pdmaudioifs.h>
37#include <VBox/vmm/pdmdrv.h>
38#include <VBox/vmm/cfgm.h>
39#include <VBox/err.h>
40
41#ifdef VBOX_WITH_LIBOPUS
42# include <opus.h>
43#endif
44
45/*********************************************************************************************************************************
46* Structures and Typedefs *
47*********************************************************************************************************************************/
48
49/**
50 * Enumeration for audio/video recording driver recording mode.
51 */
52typedef enum AVRECMODE
53{
54 /** Unknown / invalid recording mode. */
55 AVRECMODE_UNKNOWN = 0,
56 /** Only record audio.
57 * This mode does not need to talk to the video recording driver,
58 * as this driver then simply creates an own WebM container. */
59 AVRECMODE_AUDIO = 1,
60 /** Records audio and video.
61 * Needs to work together with the video recording driver in
62 * order to get a full-featured WebM container. */
63 AVRECMODE_AUDIO_VIDEO = 2
64} AVRECMODE;
65
66/**
67 * Structure for keeping codec specific data.
68 */
69typedef struct AVRECCODEC
70{
71 union
72 {
73#ifdef VBOX_WITH_LIBOPUS
74 struct
75 {
76 /** Encoder we're going to use. */
77 OpusEncoder *pEnc;
78 } Opus;
79#endif /* VBOX_WITH_LIBOPUS */
80 };
81} AVRECCODEC, *PAVRECCODEC;
82
83/**
84 * Audio video recording output stream.
85 */
86typedef struct AVRECSTREAMOUT
87{
88 /** Note: Always must come first! */
89 PDMAUDIOSTREAM Stream;
90 /** The PCM properties of this stream. */
91 PDMAUDIOPCMPROPS Props;
92 uint64_t old_ticks;
93 uint64_t cSamplesSentPerSec;
94 /** Codec-specific data.
95 * As every stream can be different, one codec per stream is needed. */
96 AVRECCODEC Codec;
97 /** (Audio) frame buffer. */
98 PRTCIRCBUF pCircBuf;
99 uint8_t *pvFrameBuf;
100 size_t cbFrameBufSize;
101 size_t cbFrameBufUsed;
102 size_t offFrameBufRead;
103 size_t offFrameBufWrite;
104} AVRECSTREAMOUT, *PAVRECSTREAMOUT;
105
106/**
107 * Video recording audio driver instance data.
108 */
109typedef struct DRVAUDIOVIDEOREC
110{
111 /** Pointer to audio video recording object. */
112 AudioVideoRec *pAudioVideoRec;
113 /** Pointer to the driver instance structure. */
114 PPDMDRVINS pDrvIns;
115 /** Pointer to host audio interface. */
116 PDMIHOSTAUDIO IHostAudio;
117 /** Pointer to the DrvAudio port interface that is above us. */
118 PPDMIAUDIOCONNECTOR pDrvAudio;
119 /** Recording mode. */
120 AVRECMODE enmMode;
121 /** Pointer to WebM container to write recorded audio data to.
122 * See the AVRECMODE enumeration for more information. */
123 WebMWriter *pEBML;
124 /** Track number for audio data. */
125 uint8_t uTrack;
126} DRVAUDIOVIDEOREC, *PDRVAUDIOVIDEOREC;
127
128/** Makes DRVAUDIOVIDEOREC out of PDMIHOSTAUDIO. */
129#define PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface) \
130 ( (PDRVAUDIOVIDEOREC)((uintptr_t)pInterface - RT_OFFSETOF(DRVAUDIOVIDEOREC, IHostAudio)) )
131
132
133static int avRecCreateStreamOut(PPDMIHOSTAUDIO pInterface,
134 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
135{
136 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
137 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
138 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
139 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
140
141 PAVRECSTREAMOUT pStreamOut = (PAVRECSTREAMOUT)pStream;
142
143 int rc;
144
145#ifdef VBOX_WITH_LIBOPUS
146 RTCircBufCreate(&pStreamOut->pCircBuf, _4K);
147
148 rc = DrvAudioHlpStreamCfgToProps(pCfgReq, &pStreamOut->Props);
149 if (RT_SUCCESS(rc))
150 {
151 size_t cbFrameBuf = sizeof(uint8_t) * _4K; /** @todo Make this configurable */
152
153 pStreamOut->pvFrameBuf = (uint8_t *)RTMemAlloc(cbFrameBuf);
154 if (pStreamOut->pvFrameBuf)
155 {
156 pStreamOut->cbFrameBufSize = cbFrameBuf;
157 pStreamOut->cbFrameBufUsed = 0;
158 pStreamOut->offFrameBufRead = 0;
159 pStreamOut->offFrameBufWrite = 0;
160
161 OpusEncoder *pEnc = NULL;
162
163 int orc;
164 pEnc = opus_encoder_create(48000 /* Hz */, 2 /* Stereo */, OPUS_APPLICATION_AUDIO, &orc);
165 if (orc != OPUS_OK)
166 {
167 LogRel(("VideoRec: Audio codec failed to initialize: %s\n", opus_strerror(orc)));
168 return VERR_AUDIO_BACKEND_INIT_FAILED;
169 }
170
171 AssertPtr(pEnc);
172
173 #if 0
174 orc = opus_encoder_ctl(pEnc, OPUS_SET_BITRATE(DrvAudioHlpCalcBitrate()));
175 if (orc != OPUS_OK)
176 {
177 LogRel(("VideoRec: Audio codec failed to set bitrate: %s\n", opus_strerror(orc)));
178 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
179 }
180 else
181 {
182 #endif
183 pStreamOut->Codec.Opus.pEnc = pEnc;
184
185 if (pCfgAcq)
186 pCfgAcq->cSampleBufferSize = _4K; /** @todo Make this configurable. */
187 // }
188 }
189 }
190#else
191 rc = VERR_NOT_SUPPORTED;
192#endif /* VBOX_WITH_LIBOPUS */
193
194 LogFlowFuncLeaveRC(rc);
195 return rc;
196}
197
198
199static int avRecControlStreamOut(PPDMIHOSTAUDIO pInterface,
200 PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
201{
202 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
203 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
204 RT_NOREF(enmStreamCmd);
205
206 PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface);
207 RT_NOREF(pThis);
208
209 LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
210
211 AudioMixBufReset(&pStream->MixBuf);
212
213 return VINF_SUCCESS;
214}
215
216
217/**
218 * @interface_method_impl{PDMIHOSTAUDIO,pfnInit}
219 */
220static DECLCALLBACK(int) drvAudioVideoRecInit(PPDMIHOSTAUDIO pInterface)
221{
222 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
223
224 LogFlowFuncEnter();
225
226 PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface);
227
228 pThis->enmMode = AVRECMODE_AUDIO; /** @todo Fix mode! */
229
230 int rc;
231
232 try
233 {
234 switch (pThis->enmMode)
235 {
236 case AVRECMODE_AUDIO:
237 {
238 pThis->pEBML = new WebMWriter();
239 rc = pThis->pEBML->Create("/tmp/acap.webm", RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE, /** @todo Fix path! */
240 WebMWriter::AudioCodec_Opus, WebMWriter::VideoCodec_None);
241 if (RT_SUCCESS(rc))
242 rc = pThis->pEBML->AddAudioTrack(44100, 2, 16, &pThis->uTrack);
243 break;
244 }
245
246 case AVRECMODE_AUDIO_VIDEO:
247 {
248 rc = VERR_NOT_SUPPORTED;
249 break;
250 }
251
252 default:
253 rc = VERR_NOT_SUPPORTED;
254 break;
255 }
256 }
257 catch (std::bad_alloc)
258 {
259 rc = VERR_NO_MEMORY;
260 }
261
262 if (RT_FAILURE(rc))
263 {
264 LogRel(("VideoRec: Audio recording driver failed to initialize, rc=%Rrc\n", rc));
265 }
266 else
267 LogRel2(("VideoRec: Audio recording driver initialized\n"));
268
269 return rc;
270}
271
272
273/**
274 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
275 */
276static DECLCALLBACK(int) drvAudioVideoRecStreamCapture(PPDMIHOSTAUDIO pInterface,
277 PPDMAUDIOSTREAM pStream, void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
278{
279 RT_NOREF(pInterface, pStream, pvBuf, cbBuf);
280
281 if (pcbRead)
282 *pcbRead = 0;
283
284 return VINF_SUCCESS;
285}
286
287
288/**
289 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
290 */
291static DECLCALLBACK(int) drvAudioVideoRecStreamPlay(PPDMIHOSTAUDIO pInterface,
292 PPDMAUDIOSTREAM pStream, const void *pvBuf2, uint32_t cbBuf2,
293 uint32_t *pcbWritten)
294{
295 RT_NOREF2(pvBuf2, cbBuf2);
296
297 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
298 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
299 /* pcbWritten is optional. */
300
301 PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface);
302 PAVRECSTREAMOUT pStreamOut = (PAVRECSTREAMOUT)pStream;
303
304 uint32_t cLive = AudioMixBufLive(&pStream->MixBuf);
305
306 uint64_t now = PDMDrvHlpTMGetVirtualTime(pThis->pDrvIns);
307 uint64_t ticks = now - pStreamOut->old_ticks;
308 uint64_t ticks_per_second = PDMDrvHlpTMGetVirtualFreq(pThis->pDrvIns);
309
310 /* Minimize the rounding error: samples = int((ticks * freq) / ticks_per_second + 0.5). */
311 uint32_t cSamplesPlayed = (int)((2 * ticks * pStreamOut->Props.uHz + ticks_per_second) / ticks_per_second / 2);
312
313 /* Don't play more than available. */
314 if (cSamplesPlayed > cLive)
315 cSamplesPlayed = cLive;
316
317 /* Remember when samples were consumed. */
318 pStreamOut->old_ticks = now;
319
320 int cSamplesToSend = cSamplesPlayed;
321
322 LogFlowFunc(("uFreq=%RU32, cChan=%RU8, cBits=%RU8, fSigned=%RTbool, cSamplesToSend=%RU32\n",
323 pStreamOut->Props.uHz, pStreamOut->Props.cChannels,
324 pStreamOut->Props.cBits, pStreamOut->Props.fSigned, cSamplesToSend));
325
326 uint32_t csReadTotal = 0;
327
328 int rc;
329
330 /*
331 * Call the encoder with the data.
332 */
333#ifdef VBOX_WITH_LIBOPUS
334
335 /** @todo For now we ASSUME 25 FPS. */
336 uint16_t cbFrameSize = (/*pStreamOut->Props.uHz*/ 48000 / 25 /* FPS */);
337
338 PRTCIRCBUF pCircBuf = pStreamOut->pCircBuf;
339
340 while (pStreamOut->cbFrameBufUsed < cbFrameSize)
341 {
342 void *pvBuf;
343 size_t cbBuf;
344 RTCircBufAcquireWriteBlock(pCircBuf, RTCircBufFree(pCircBuf), &pvBuf, &cbBuf);
345
346 uint32_t cbRead = 0;
347
348 if (cbBuf)
349 {
350 uint32_t csRead = 0;
351 rc = AudioMixBufReadCirc(&pStream->MixBuf, pvBuf, cbBuf, &csRead);
352 if (RT_SUCCESS(rc))
353 {
354 AudioMixBufFinish(&pStream->MixBuf, csRead);
355
356 cbRead = AUDIOMIXBUF_S2B(&pStream->MixBuf, csRead);
357 csReadTotal += csRead;
358 }
359 }
360
361 RTCircBufReleaseWriteBlock(pCircBuf, cbRead);
362
363 if ( RT_FAILURE(rc)
364 || !cbRead)
365 {
366 break;
367 }
368
369 if (RTCircBufUsed(pCircBuf) >= cbFrameSize)
370 {
371 uint8_t abSrc[_4K];
372 size_t cbSrc = 0;
373
374 while (cbSrc < cbFrameSize)
375 {
376 RTCircBufAcquireReadBlock(pCircBuf, cbFrameSize - cbSrc, &pvBuf, &cbBuf);
377
378 if (cbBuf)
379 {
380 memcpy(&abSrc[cbSrc], pvBuf, cbBuf);
381
382 cbSrc += cbBuf;
383 Assert(cbSrc <= sizeof(abSrc));
384 }
385
386 RTCircBufReleaseReadBlock(pCircBuf, cbBuf);
387
388 if (!cbBuf)
389 break;
390 }
391
392 /*
393 * Opus always encodes PER FRAME, that is, 2.5, 5, 10, 20, 40 or 60 ms of audio data.
394 *
395 * A packet can have up to 120ms worth of audio data.
396 * Anything > 120ms of data will result in a "corrupted package" error message by
397 * by decoding application.
398 */
399 uint8_t abDst[_4K];
400 size_t cbDst = sizeof(abDst);
401
402 /* Call the encoder to encode our input samples. */
403 opus_encoder_ctl(pStreamOut->Codec.Opus.pEnc, OPUS_SET_BITRATE(196000)); /** @todo */
404 opus_int32 cbWritten = opus_encode(pStreamOut->Codec.Opus.pEnc,
405 (opus_int16 *)abSrc, cbSrc, abDst, cbDst);
406 if (cbWritten > 0)
407 {
408 /* Call the WebM writer to actually write the encoded audio data. */
409 WebMWriter::BlockData_Opus blockData = { abDst, cbDst };
410 rc = pThis->pEBML->WriteBlock(pThis->uTrack, &blockData, sizeof(blockData));
411 AssertRC(rc);
412 }
413 else if (cbWritten < 0)
414 {
415 AssertMsgFailed(("Encoding failed: %s\n", opus_strerror(cbWritten)));
416 rc = VERR_INVALID_PARAMETER;
417 }
418
419 if (RT_FAILURE(rc))
420 break;
421 }
422 }
423#else
424 rc = VERR_NOT_SUPPORTED;
425#endif /* VBOX_WITH_LIBOPUS */
426
427 /*
428 * Always report back all samples acquired, regardless of whether the
429 * encoder actually did process those.
430 */
431 if (pcbWritten)
432 *pcbWritten = csReadTotal;
433
434 LogFlowFunc(("csReadTotal=%RU32, rc=%Rrc\n", csReadTotal, rc));
435 return rc;
436}
437
438
439static int avRecDestroyStreamOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
440{
441 PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface);
442 RT_NOREF(pThis);
443 PAVRECSTREAMOUT pStreamOut = (PAVRECSTREAMOUT)pStream;
444
445 if (pStreamOut->pCircBuf)
446 {
447 RTCircBufDestroy(pStreamOut->pCircBuf);
448 pStreamOut->pCircBuf = NULL;
449 }
450
451 if (pStreamOut->pvFrameBuf)
452 {
453 Assert(pStreamOut->cbFrameBufSize);
454 RTMemFree(pStreamOut->pvFrameBuf);
455 pStreamOut->pvFrameBuf = NULL;
456 }
457
458#ifdef VBOX_WITH_LIBOPUS
459 if (pStreamOut->Codec.Opus.pEnc)
460 {
461 opus_encoder_destroy(pStreamOut->Codec.Opus.pEnc);
462 pStreamOut->Codec.Opus.pEnc = NULL;
463 }
464#endif
465
466 return VINF_SUCCESS;
467}
468
469
470/**
471 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
472 */
473static DECLCALLBACK(int) drvAudioVideoRecGetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
474{
475 NOREF(pInterface);
476 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
477
478 pBackendCfg->cbStreamOut = sizeof(AVRECSTREAMOUT);
479 pBackendCfg->cbStreamIn = 0;
480 pBackendCfg->cMaxStreamsIn = 0;
481 pBackendCfg->cMaxStreamsOut = UINT32_MAX;
482
483 return VINF_SUCCESS;
484}
485
486
487/**
488 * @interface_method_impl{PDMIHOSTAUDIO,pfnShutdown}
489 */
490static DECLCALLBACK(void) drvAudioVideoRecShutdown(PPDMIHOSTAUDIO pInterface)
491{
492 LogFlowFuncEnter();
493
494 PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface);
495
496 int rc;
497
498 switch (pThis->enmMode)
499 {
500 case AVRECMODE_AUDIO:
501 {
502 if (pThis->pEBML)
503 {
504 pThis->pEBML->Close();
505
506 delete pThis->pEBML;
507 pThis->pEBML = NULL;
508 }
509 break;
510 }
511
512 case AVRECMODE_AUDIO_VIDEO:
513 {
514 break;
515 }
516
517 default:
518 rc = VERR_NOT_SUPPORTED;
519 break;
520 }
521
522 LogFlowFuncLeaveRC(rc);
523}
524
525
526/**
527 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
528 */
529static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvAudioVideoRecGetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
530{
531 RT_NOREF(enmDir);
532 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
533
534 return PDMAUDIOBACKENDSTS_RUNNING;
535}
536
537
538/**
539 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
540 */
541static DECLCALLBACK(int) drvAudioVideoRecStreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream,
542 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
543{
544 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
545 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
546 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
547 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
548
549 if (pCfgReq->enmDir == PDMAUDIODIR_OUT)
550 return avRecCreateStreamOut(pInterface, pStream, pCfgReq, pCfgAcq);
551
552 return VERR_NOT_SUPPORTED;
553}
554
555
556/**
557 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
558 */
559static DECLCALLBACK(int) drvAudioVideoRecStreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
560{
561 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
562 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
563
564 if (pStream->enmDir == PDMAUDIODIR_OUT)
565 return avRecDestroyStreamOut(pInterface, pStream);
566
567 return VINF_SUCCESS;
568}
569
570
571/**
572 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
573 */
574static DECLCALLBACK(int) drvAudioVideoRecStreamControl(PPDMIHOSTAUDIO pInterface,
575 PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
576{
577 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
578 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
579
580 Assert(pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST);
581
582 if (pStream->enmDir == PDMAUDIODIR_OUT)
583 return avRecControlStreamOut(pInterface, pStream, enmStreamCmd);
584
585 return VINF_SUCCESS;
586}
587
588
589/**
590 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus}
591 */
592static DECLCALLBACK(PDMAUDIOSTRMSTS) drvAudioVideoRecStreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
593{
594 NOREF(pInterface);
595 NOREF(pStream);
596
597 return ( PDMAUDIOSTRMSTS_FLAG_INITIALIZED | PDMAUDIOSTRMSTS_FLAG_ENABLED
598 | PDMAUDIOSTRMSTS_FLAG_DATA_READABLE | PDMAUDIOSTRMSTS_FLAG_DATA_WRITABLE);
599}
600
601
602/**
603 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamIterate}
604 */
605static DECLCALLBACK(int) drvAudioVideoRecStreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
606{
607 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
608 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
609
610 LogFlowFuncEnter();
611
612 /* Nothing to do here for video recording. */
613 return VINF_SUCCESS;
614}
615
616
617/**
618 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
619 */
620static DECLCALLBACK(void *) drvAudioVideoRecQueryInterface(PPDMIBASE pInterface, const char *pszIID)
621{
622 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
623 PDRVAUDIOVIDEOREC pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVIDEOREC);
624
625 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
626 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
627 return NULL;
628}
629
630
631AudioVideoRec::AudioVideoRec(Console *pConsole)
632 : mpDrv(NULL),
633 mParent(pConsole)
634{
635}
636
637
638AudioVideoRec::~AudioVideoRec(void)
639{
640 if (mpDrv)
641 {
642 mpDrv->pAudioVideoRec = NULL;
643 mpDrv = NULL;
644 }
645}
646
647
648/**
649 * Construct a audio video recording driver instance.
650 *
651 * @copydoc FNPDMDRVCONSTRUCT
652 */
653/* static */
654DECLCALLBACK(int) AudioVideoRec::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
655{
656 RT_NOREF(fFlags);
657
658 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
659 PDRVAUDIOVIDEOREC pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVIDEOREC);
660
661 AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
662 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
663
664 LogRel(("Audio: Initializing video recording audio driver\n"));
665 LogFlowFunc(("fFlags=0x%x\n", fFlags));
666
667 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
668 ("Configuration error: Not possible to attach anything to this driver!\n"),
669 VERR_PDM_DRVINS_NO_ATTACH);
670
671 /*
672 * Init the static parts.
673 */
674 pThis->pDrvIns = pDrvIns;
675 /* IBase */
676 pDrvIns->IBase.pfnQueryInterface = drvAudioVideoRecQueryInterface;
677 /* IHostAudio */
678 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvAudioVideoRec);
679
680 /*
681 * Get the AudioVideoRec object pointer.
682 */
683 void *pvUser = NULL;
684 int rc = CFGMR3QueryPtr(pCfg, "Object", &pvUser); /** @todo r=andy Get rid of this hack and use IHostAudio::SetCallback. */
685 AssertMsgRCReturn(rc, ("Confguration error: No/bad \"Object\" value, rc=%Rrc\n", rc), rc);
686
687 pThis->pAudioVideoRec = (AudioVideoRec *)pvUser;
688 pThis->pAudioVideoRec->mpDrv = pThis;
689
690 /*
691 * Get the interface for the above driver (DrvAudio) to make mixer/conversion calls.
692 * Described in CFGM tree.
693 */
694 pThis->pDrvAudio = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIAUDIOCONNECTOR);
695 AssertMsgReturn(pThis->pDrvAudio, ("Configuration error: No upper interface specified!\n"), VERR_PDM_MISSING_INTERFACE_ABOVE);
696
697 return VINF_SUCCESS;
698}
699
700
701/**
702 * @interface_method_impl{PDMDRVREG,pfnDestruct}
703 */
704/* static */
705DECLCALLBACK(void) AudioVideoRec::drvDestruct(PPDMDRVINS pDrvIns)
706{
707 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
708 PDRVAUDIOVIDEOREC pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVIDEOREC);
709 LogFlowFuncEnter();
710
711 /*
712 * If the AudioVideoRec object is still alive, we must clear it's reference to
713 * us since we'll be invalid when we return from this method.
714 */
715 if (pThis->pAudioVideoRec)
716 {
717 pThis->pAudioVideoRec->mpDrv = NULL;
718 pThis->pAudioVideoRec = NULL;
719 }
720}
721
722
723/**
724 * Video recording audio driver registration record.
725 */
726const PDMDRVREG AudioVideoRec::DrvReg =
727{
728 PDM_DRVREG_VERSION,
729 /* szName */
730 "AudioVideoRec",
731 /* szRCMod */
732 "",
733 /* szR0Mod */
734 "",
735 /* pszDescription */
736 "Audio driver for video recording",
737 /* fFlags */
738 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
739 /* fClass. */
740 PDM_DRVREG_CLASS_AUDIO,
741 /* cMaxInstances */
742 ~0U,
743 /* cbInstance */
744 sizeof(DRVAUDIOVIDEOREC),
745 /* pfnConstruct */
746 AudioVideoRec::drvConstruct,
747 /* pfnDestruct */
748 AudioVideoRec::drvDestruct,
749 /* pfnRelocate */
750 NULL,
751 /* pfnIOCtl */
752 NULL,
753 /* pfnPowerOn */
754 NULL,
755 /* pfnReset */
756 NULL,
757 /* pfnSuspend */
758 NULL,
759 /* pfnResume */
760 NULL,
761 /* pfnAttach */
762 NULL,
763 /* pfnDetach */
764 NULL,
765 /* pfnPowerOff */
766 NULL,
767 /* pfnSoftReset */
768 NULL,
769 /* u32EndVersion */
770 PDM_DRVREG_VERSION
771};
772
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