VirtualBox

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

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