VirtualBox

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

Last change on this file since 73768 was 73543, checked in by vboxsync, 6 years ago

Build fix.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 37.2 KB
RevLine 
[53442]1/* $Id: DrvAudioVideoRec.cpp 73543 2018-08-07 11:51:52Z vboxsync $ */
2/** @file
3 * Video recording audio backend for Main.
4 */
5
6/*
[65162]7 * Copyright (C) 2016-2017 Oracle Corporation
[53442]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 */
[65162]17
[68715]18/* This code makes use of the Opus codec (libopus):
19 *
20 * Copyright 2001-2011 Xiph.Org, Skype Limited, Octasic,
21 * Jean-Marc Valin, Timothy B. Terriberry,
22 * CSIRO, Gregory Maxwell, Mark Borgerding,
23 * Erik de Castro Lopo
24 *
25 * Redistribution and use in source and binary forms, with or without
26 * modification, are permitted provided that the following conditions
27 * are met:
28 *
29 * - Redistributions of source code must retain the above copyright
30 * notice, this list of conditions and the following disclaimer.
31 *
32 * - Redistributions in binary form must reproduce the above copyright
33 * notice, this list of conditions and the following disclaimer in the
34 * documentation and/or other materials provided with the distribution.
35 *
36 * - Neither the name of Internet Society, IETF or IETF Trust, nor the
37 * names of specific contributors, may be used to endorse or promote
38 * products derived from this software without specific prior written
39 * permission.
40 *
41 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
42 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
43 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
44 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
45 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
46 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
47 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
48 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
49 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
50 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
51 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
52 *
53 * Opus is subject to the royalty-free patent licenses which are
54 * specified at:
55 *
56 * Xiph.Org Foundation:
57 * https://datatracker.ietf.org/ipr/1524/
58 *
59 * Microsoft Corporation:
60 * https://datatracker.ietf.org/ipr/1914/
61 *
62 * Broadcom Corporation:
63 * https://datatracker.ietf.org/ipr/1526/
64 *
65 */
66
[65410]67/**
68 * This driver is part of Main and is responsible for providing audio
69 * data to Main's video capturing feature.
70 *
71 * The driver itself implements a PDM host audio backend, which in turn
72 * provides the driver with the required audio data and audio events.
73 *
74 * For now there is support for the following destinations (called "sinks"):
75 *
76 * - Direct writing of .webm files to the host.
77 * - Communicating with Main via the Console object to send the encoded audio data to.
78 * The Console object in turn then will route the data to the Display / video capturing interface then.
79 */
[65162]80
[69240]81
[65162]82/*********************************************************************************************************************************
83* Header Files *
84*********************************************************************************************************************************/
[65694]85#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
[67914]86#include "LoggingNew.h"
87
[53442]88#include "DrvAudioVideoRec.h"
89#include "ConsoleImpl.h"
90
[65162]91#include "../../Devices/Audio/DrvAudio.h"
[69683]92#include "WebMWriter.h"
[65162]93
[53442]94#include <iprt/mem.h>
95#include <iprt/cdefs.h>
96
97#include <VBox/vmm/pdmaudioifs.h>
98#include <VBox/vmm/pdmdrv.h>
99#include <VBox/vmm/cfgm.h>
100#include <VBox/err.h>
101
[65263]102#ifdef VBOX_WITH_LIBOPUS
103# include <opus.h>
104#endif
[53442]105
[68323]106
[65162]107/*********************************************************************************************************************************
[68323]108* Defines *
109*********************************************************************************************************************************/
110
111#define AVREC_OPUS_HZ_MAX 48000 /** Maximum sample rate (in Hz) Opus can handle. */
112
113
114/*********************************************************************************************************************************
[65162]115* Structures and Typedefs *
116*********************************************************************************************************************************/
[65197]117
[53442]118/**
[65410]119 * Enumeration for specifying the recording container type.
[65197]120 */
[65410]121typedef enum AVRECCONTAINERTYPE
[65197]122{
[65410]123 /** Unknown / invalid container type. */
124 AVRECCONTAINERTYPE_UNKNOWN = 0,
125 /** Recorded data goes to Main / Console. */
126 AVRECCONTAINERTYPE_MAIN_CONSOLE = 1,
[68321]127 /** Recorded data will be written to a .webm file. */
[65410]128 AVRECCONTAINERTYPE_WEBM = 2
129} AVRECCONTAINERTYPE;
[65197]130
131/**
[65410]132 * Structure for keeping generic container parameters.
[65197]133 */
[65410]134typedef struct AVRECCONTAINERPARMS
135{
136 /** The container's type. */
137 AVRECCONTAINERTYPE enmType;
138
139} AVRECCONTAINERPARMS, *PAVRECCONTAINERPARMS;
140
141/**
142 * Structure for keeping container-specific data.
143 */
144typedef struct AVRECCONTAINER
145{
146 /** Generic container parameters. */
147 AVRECCONTAINERPARMS Parms;
148
149 union
150 {
151 struct
152 {
153 /** Pointer to Console. */
154 Console *pConsole;
155 } Main;
156
157 struct
158 {
159 /** Pointer to WebM container to write recorded audio data to.
160 * See the AVRECMODE enumeration for more information. */
161 WebMWriter *pWebM;
162 /** Assigned track number from WebM container. */
163 uint8_t uTrack;
164 } WebM;
165 };
166} AVRECCONTAINER, *PAVRECCONTAINER;
167
168/**
169 * Structure for keeping generic codec parameters.
170 */
171typedef struct AVRECCODECPARMS
172{
173 /** The encoding rate to use. */
174 uint32_t uHz;
175 /** Number of audio channels to encode.
176 * Currently we only supported stereo (2) channels. */
177 uint8_t cChannels;
[68338]178 /** Bits per sample. */
179 uint8_t cBits;
[65410]180 /** The codec's bitrate. 0 if not used / cannot be specified. */
181 uint32_t uBitrate;
182
183} AVRECCODECPARMS, *PAVRECCODECPARMS;
184
185/**
186 * Structure for keeping codec-specific data.
187 */
[65197]188typedef struct AVRECCODEC
189{
[65410]190 /** Generic codec parameters. */
191 AVRECCODECPARMS Parms;
[65197]192 union
193 {
[65263]194#ifdef VBOX_WITH_LIBOPUS
[65197]195 struct
196 {
197 /** Encoder we're going to use. */
[65389]198 OpusEncoder *pEnc;
[68338]199 /** Time (in ms) an (encoded) frame takes.
200 *
[68452]201 * For Opus, valid frame sizes are:
[68338]202 * ms Frame size
203 * 2.5 120
204 * 5 240
205 * 10 480
206 * 20 (Default) 960
207 * 40 1920
208 * 60 2880
209 */
210 uint32_t msFrame;
[65197]211 } Opus;
[65263]212#endif /* VBOX_WITH_LIBOPUS */
[65197]213 };
[65410]214
215#ifdef VBOX_WITH_STATISTICS /** @todo Make these real STAM values. */
216 struct
217 {
218 /** Number of frames encoded. */
219 uint64_t cEncFrames;
220 /** Total time (in ms) of already encoded audio data. */
221 uint64_t msEncTotal;
222 } STAM;
223#endif /* VBOX_WITH_STATISTICS */
224
[65197]225} AVRECCODEC, *PAVRECCODEC;
226
[65410]227typedef struct AVRECSINK
228{
229 /** @todo Add types for container / codec as soon as we implement more stuff. */
230
231 /** Container data to use for data processing. */
232 AVRECCONTAINER Con;
[68338]233 /** Codec data this sink uses for encoding. */
[65410]234 AVRECCODEC Codec;
[69188]235 /** Timestamp (in ms) of when the sink was created. */
236 uint64_t tsStartMs;
[65410]237} AVRECSINK, *PAVRECSINK;
238
[65197]239/**
[65624]240 * Audio video recording (output) stream.
[65197]241 */
[65624]242typedef struct AVRECSTREAM
[65197]243{
[65624]244 /** The stream's acquired configuration. */
245 PPDMAUDIOSTREAMCFG pCfg;
[65256]246 /** (Audio) frame buffer. */
[65330]247 PRTCIRCBUF pCircBuf;
[65410]248 /** Pointer to sink to use for writing. */
249 PAVRECSINK pSink;
[69188]250 /** Last encoded PTS (in ms). */
251 uint64_t uLastPTSMs;
[65624]252} AVRECSTREAM, *PAVRECSTREAM;
[65197]253
254/**
[65162]255 * Video recording audio driver instance data.
[53442]256 */
257typedef struct DRVAUDIOVIDEOREC
258{
259 /** Pointer to audio video recording object. */
[65162]260 AudioVideoRec *pAudioVideoRec;
[53442]261 /** Pointer to the driver instance structure. */
[65162]262 PPDMDRVINS pDrvIns;
263 /** Pointer to host audio interface. */
264 PDMIHOSTAUDIO IHostAudio;
[65410]265 /** Pointer to the console object. */
[73104]266 ComPtr<Console> pConsole;
[65162]267 /** Pointer to the DrvAudio port interface that is above us. */
268 PPDMIAUDIOCONNECTOR pDrvAudio;
[65410]269 /** The driver's sink for writing output to. */
270 AVRECSINK Sink;
[53442]271} DRVAUDIOVIDEOREC, *PDRVAUDIOVIDEOREC;
272
[65162]273/** Makes DRVAUDIOVIDEOREC out of PDMIHOSTAUDIO. */
[73104]274#define PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface) /* (clang doesn't think it is a POD, thus _DYN.) */ \
275 ( (PDRVAUDIOVIDEOREC)((uintptr_t)pInterface - RT_UOFFSETOF_DYN(DRVAUDIOVIDEOREC, IHostAudio)) )
[53442]276
[65410]277/**
278 * Initializes a recording sink.
279 *
280 * @returns IPRT status code.
281 * @param pThis Driver instance.
282 * @param pSink Sink to initialize.
283 * @param pConParms Container parameters to set.
284 * @param pCodecParms Codec parameters to set.
285 */
286static int avRecSinkInit(PDRVAUDIOVIDEOREC pThis, PAVRECSINK pSink, PAVRECCONTAINERPARMS pConParms, PAVRECCODECPARMS pCodecParms)
[53442]287{
[68338]288 uint32_t uHz = pCodecParms->uHz;
[68452]289 uint8_t cBits = pCodecParms->cBits;
[68338]290 uint8_t cChannels = pCodecParms->cChannels;
291 uint32_t uBitrate = pCodecParms->uBitrate;
[53442]292
[65389]293 /* Opus only supports certain input sample rates in an efficient manner.
294 * So make sure that we use those by resampling the data to the requested rate. */
[68323]295 if (uHz > 24000) uHz = AVREC_OPUS_HZ_MAX;
[65389]296 else if (uHz > 16000) uHz = 24000;
297 else if (uHz > 12000) uHz = 16000;
298 else if (uHz > 8000 ) uHz = 12000;
299 else uHz = 8000;
300
[68338]301 if (cChannels > 2)
302 {
303 LogRel(("VideoRec: More than 2 (stereo) channels are not supported at the moment\n"));
304 cChannels = 2;
305 }
[65389]306
[68338]307 LogRel2(("VideoRec: Recording audio in %RU16Hz, %RU8 channels, %RU32 bitrate\n", uHz, cChannels, uBitrate));
308
[65410]309 int orc;
[68338]310 OpusEncoder *pEnc = opus_encoder_create(uHz, cChannels, OPUS_APPLICATION_AUDIO, &orc);
[65410]311 if (orc != OPUS_OK)
312 {
313 LogRel(("VideoRec: Audio codec failed to initialize: %s\n", opus_strerror(orc)));
314 return VERR_AUDIO_BACKEND_INIT_FAILED;
315 }
316
317 AssertPtr(pEnc);
318
[68338]319 opus_encoder_ctl(pEnc, OPUS_SET_BITRATE(uBitrate));
[65410]320 if (orc != OPUS_OK)
321 {
[68338]322 opus_encoder_destroy(pEnc);
323 pEnc = NULL;
324
325 LogRel(("VideoRec: Audio codec failed to set bitrate (%RU32): %s\n", uBitrate, opus_strerror(orc)));
[65410]326 return VERR_AUDIO_BACKEND_INIT_FAILED;
327 }
328
[68736]329 int rc = VINF_SUCCESS;
[65410]330
331 try
332 {
333 switch (pConParms->enmType)
334 {
335 case AVRECCONTAINERTYPE_MAIN_CONSOLE:
336 {
[68797]337 if (pThis->pConsole)
338 {
339 pSink->Con.Main.pConsole = pThis->pConsole;
340 }
341 else
342 rc = VERR_NOT_SUPPORTED;
[65410]343 break;
344 }
345
346 case AVRECCONTAINERTYPE_WEBM:
347 {
[68732]348#ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
[68338]349 /* If we only record audio, create our own WebM writer instance here. */
350 if (!pSink->Con.WebM.pWebM) /* Do we already have our WebM writer instance? */
351 {
352 char szFile[RTPATH_MAX];
353 if (RTStrPrintf(szFile, sizeof(szFile), "%s%s",
[68736]354 VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH, "DrvAudioVideoRec.webm"))
[68338]355 {
356 /** @todo Add sink name / number to file name. */
357
358 pSink->Con.WebM.pWebM = new WebMWriter();
359 rc = pSink->Con.WebM.pWebM->Create(szFile,
[68720]360 /** @todo Add option to add some suffix if file exists instead of overwriting? */
[68338]361 RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_NONE,
362 WebMWriter::AudioCodec_Opus, WebMWriter::VideoCodec_None);
363 if (RT_SUCCESS(rc))
364 {
365 rc = pSink->Con.WebM.pWebM->AddAudioTrack(uHz, cChannels, cBits,
366 &pSink->Con.WebM.uTrack);
[68720]367 if (RT_SUCCESS(rc))
368 {
369 LogRel(("VideoRec: Recording audio to file '%s'\n", szFile));
370 }
371 else
[68338]372 LogRel(("VideoRec: Error creating audio track for file '%s' (%Rrc)\n", szFile, rc));
373 }
374 else
375 LogRel(("VideoRec: Error creating audio file '%s' (%Rrc)\n", szFile, rc));
376 }
377 else
378 {
379 AssertFailed(); /* Should never happen. */
380 LogRel(("VideoRec: Error creating audio file path\n"));
381 }
382 }
[68732]383#else
384 rc = VERR_NOT_SUPPORTED;
385#endif /* VBOX_AUDIO_DEBUG_DUMP_PCM_DATA */
[65410]386 break;
387 }
388
389 default:
390 rc = VERR_NOT_SUPPORTED;
391 break;
392 }
393 }
[73505]394 catch (std::bad_alloc &)
[65410]395 {
[68737]396#ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
[65410]397 rc = VERR_NO_MEMORY;
[68737]398#endif
[65410]399 }
400
401 if (RT_SUCCESS(rc))
402 {
[65416]403 pSink->Con.Parms.enmType = pConParms->enmType;
404
[65410]405 pSink->Codec.Parms.uHz = uHz;
[68338]406 pSink->Codec.Parms.cChannels = cChannels;
407 pSink->Codec.Parms.cBits = cBits;
408 pSink->Codec.Parms.uBitrate = uBitrate;
[65410]409
410 pSink->Codec.Opus.pEnc = pEnc;
[68338]411 pSink->Codec.Opus.msFrame = 20; /** @todo 20 ms of audio data. Make this configurable? */
[65410]412
413#ifdef VBOX_WITH_STATISTICS
414 pSink->Codec.STAM.cEncFrames = 0;
415 pSink->Codec.STAM.msEncTotal = 0;
[65389]416#endif
[69188]417
418 pSink->tsStartMs = RTTimeMilliTS();
[68338]419 }
420 else
421 {
422 if (pEnc)
423 {
424 opus_encoder_destroy(pEnc);
425 pEnc = NULL;
426 }
[65389]427
[68338]428 LogRel(("VideoRec: Error creating sink (%Rrc)\n", rc));
[65410]429 }
[65389]430
[65410]431 return rc;
432}
433
434
435/**
436 * Shuts down (closes) a recording sink,
437 *
438 * @returns IPRT status code.
439 * @param pSink Recording sink to shut down.
440 */
441static void avRecSinkShutdown(PAVRECSINK pSink)
442{
443 AssertPtrReturnVoid(pSink);
444
445#ifdef VBOX_WITH_LIBOPUS
446 if (pSink->Codec.Opus.pEnc)
447 {
448 opus_encoder_destroy(pSink->Codec.Opus.pEnc);
449 pSink->Codec.Opus.pEnc = NULL;
450 }
451#endif
452 switch (pSink->Con.Parms.enmType)
453 {
454 case AVRECCONTAINERTYPE_WEBM:
455 {
456 if (pSink->Con.WebM.pWebM)
457 {
[68338]458 LogRel2(("VideoRec: Finished recording audio to file '%s' (%zu bytes)\n",
459 pSink->Con.WebM.pWebM->GetFileName().c_str(), pSink->Con.WebM.pWebM->GetFileSize()));
460
[65410]461 int rc2 = pSink->Con.WebM.pWebM->Close();
462 AssertRC(rc2);
463
464 delete pSink->Con.WebM.pWebM;
465 pSink->Con.WebM.pWebM = NULL;
466 }
467 break;
468 }
469
470 case AVRECCONTAINERTYPE_MAIN_CONSOLE:
471 default:
472 break;
473 }
474}
475
476
477/**
478 * Creates an audio output stream and associates it with the specified recording sink.
479 *
480 * @returns IPRT status code.
481 * @param pThis Driver instance.
[65624]482 * @param pStreamAV Audio output stream to create.
[65410]483 * @param pSink Recording sink to associate audio output stream to.
484 * @param pCfgReq Requested configuration by the audio backend.
485 * @param pCfgAcq Acquired configuration by the audio output stream.
486 */
[65624]487static int avRecCreateStreamOut(PDRVAUDIOVIDEOREC pThis, PAVRECSTREAM pStreamAV,
[65410]488 PAVRECSINK pSink, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
489{
[65624]490 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
491 AssertPtrReturn(pStreamAV, VERR_INVALID_POINTER);
492 AssertPtrReturn(pSink, VERR_INVALID_POINTER);
493 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
494 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
[65410]495
496 if (pCfgReq->DestSource.Dest != PDMAUDIOPLAYBACKDEST_FRONT)
497 {
498 AssertFailed();
499
500 LogRel2(("VideoRec: Support for surround audio not implemented yet\n"));
501 return VERR_NOT_SUPPORTED;
502 }
503
[65624]504 int rc = VINF_SUCCESS;
[65410]505
506#ifdef VBOX_WITH_LIBOPUS
[68338]507 const unsigned cFrames = 2; /** @todo Use the PreRoll param for that? */
[65353]508
[68338]509 const uint32_t csFrame = pSink->Codec.Parms.uHz / (1000 /* s in ms */ / pSink->Codec.Opus.msFrame);
510 const uint32_t cbFrame = csFrame * pSink->Codec.Parms.cChannels * (pSink->Codec.Parms.cBits / 8 /* Bytes */);
[65353]511
[68338]512 rc = RTCircBufCreate(&pStreamAV->pCircBuf, cbFrame * cFrames);
[65162]513 if (RT_SUCCESS(rc))
[53442]514 {
[69188]515 pStreamAV->pSink = pSink; /* Assign sink to stream. */
516 pStreamAV->uLastPTSMs = 0;
[65197]517
[65410]518 if (pCfgAcq)
[65197]519 {
[65410]520 /* Make sure to let the driver backend know that we need the audio data in
521 * a specific sampling rate Opus is optimized for. */
[65624]522 pCfgAcq->Props.uHz = pSink->Codec.Parms.uHz;
[73529]523 pCfgAcq->Props.cShift = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(pCfgAcq->Props.cBytes, pCfgAcq->Props.cChannels);
[73370]524
525 /* Every Opus frame marks a period for now. Optimize this later. */
[73408]526 pCfgAcq->Backend.cfPeriod = DrvAudioHlpMilliToFrames(pSink->Codec.Opus.msFrame, &pCfgAcq->Props); /** @todo Make this configurable. */
[65353]527 }
[53442]528 }
[65263]529#else
[65624]530 RT_NOREF(pThis, pSink, pStreamAV, pCfgReq, pCfgAcq);
[65263]531 rc = VERR_NOT_SUPPORTED;
532#endif /* VBOX_WITH_LIBOPUS */
[53442]533
[65263]534 LogFlowFuncLeaveRC(rc);
535 return rc;
[53442]536}
537
[65162]538
[65410]539/**
540 * Destroys (closes) an audio output stream.
541 *
542 * @returns IPRT status code.
543 * @param pThis Driver instance.
[65624]544 * @param pStreamAV Audio output stream to destroy.
[65410]545 */
[65624]546static int avRecDestroyStreamOut(PDRVAUDIOVIDEOREC pThis, PAVRECSTREAM pStreamAV)
[65410]547{
548 RT_NOREF(pThis);
549
[65624]550 if (pStreamAV->pCircBuf)
[65410]551 {
[65624]552 RTCircBufDestroy(pStreamAV->pCircBuf);
553 pStreamAV->pCircBuf = NULL;
[65410]554 }
555
556 return VINF_SUCCESS;
557}
558
559
560/**
561 * Controls an audio output stream
562 *
563 * @returns IPRT status code.
564 * @param pThis Driver instance.
[65624]565 * @param pStreamAV Audio output stream to control.
[65410]566 * @param enmStreamCmd Stream command to issue.
567 */
568static int avRecControlStreamOut(PDRVAUDIOVIDEOREC pThis,
[65624]569 PAVRECSTREAM pStreamAV, PDMAUDIOSTREAMCMD enmStreamCmd)
[53442]570{
[65624]571 RT_NOREF(pThis, pStreamAV);
[53442]572
[73541]573 int rc = VINF_SUCCESS;
[73540]574
[65353]575 switch (enmStreamCmd)
576 {
577 case PDMAUDIOSTREAMCMD_ENABLE:
[65565]578 case PDMAUDIOSTREAMCMD_DISABLE:
[65353]579 case PDMAUDIOSTREAMCMD_RESUME:
580 case PDMAUDIOSTREAMCMD_PAUSE:
581 break;
582
583 default:
[73540]584 rc = VERR_NOT_SUPPORTED;
[65353]585 break;
586 }
587
[73540]588 return rc;
[53442]589}
590
591
[65162]592/**
593 * @interface_method_impl{PDMIHOSTAUDIO,pfnInit}
594 */
595static DECLCALLBACK(int) drvAudioVideoRecInit(PPDMIHOSTAUDIO pInterface)
[53442]596{
[65197]597 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
598
[65162]599 LogFlowFuncEnter();
[53442]600
[65197]601 PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface);
602
[65410]603 AVRECCONTAINERPARMS ContainerParms;
[68452]604 ContainerParms.enmType = AVRECCONTAINERTYPE_MAIN_CONSOLE; /** @todo Make this configurable. */
[65197]605
[65410]606 AVRECCODECPARMS CodecParms;
[68323]607 CodecParms.uHz = AVREC_OPUS_HZ_MAX; /** @todo Make this configurable. */
608 CodecParms.cChannels = 2; /** @todo Make this configurable. */
[68452]609 CodecParms.cBits = 16; /** @todo Make this configurable. */
[68323]610 CodecParms.uBitrate = 196000; /** @todo Make this configurable. */
[65197]611
[65410]612 int rc = avRecSinkInit(pThis, &pThis->Sink, &ContainerParms, &CodecParms);
[65197]613 if (RT_FAILURE(rc))
614 {
615 LogRel(("VideoRec: Audio recording driver failed to initialize, rc=%Rrc\n", rc));
616 }
617 else
618 LogRel2(("VideoRec: Audio recording driver initialized\n"));
619
620 return rc;
[53442]621}
622
[65162]623
624/**
625 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
[53442]626 */
[65624]627static DECLCALLBACK(int) drvAudioVideoRecStreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
[68085]628 void *pvBuf, uint32_t cxBuf, uint32_t *pcxRead)
[53442]629{
[68085]630 RT_NOREF(pInterface, pStream, pvBuf, cxBuf);
[53442]631
[68085]632 if (pcxRead)
633 *pcxRead = 0;
[53442]634
[65162]635 return VINF_SUCCESS;
[53442]636}
637
638
[65162]639/**
640 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
641 */
[65624]642static DECLCALLBACK(int) drvAudioVideoRecStreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
[68085]643 const void *pvBuf, uint32_t cxBuf, uint32_t *pcxWritten)
[53442]644{
[65162]645 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
646 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
[65565]647 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
[68085]648 AssertReturn(cxBuf, VERR_INVALID_PARAMETER);
649 /* pcxWritten is optional. */
[53442]650
[65624]651 PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface);
[65389]652 RT_NOREF(pThis);
[65624]653 PAVRECSTREAM pStreamAV = (PAVRECSTREAM)pStream;
[53442]654
[65565]655 int rc = VINF_SUCCESS;
[65429]656
[65565]657 uint32_t cbWrittenTotal = 0;
[53442]658
[65162]659 /*
[65256]660 * Call the encoder with the data.
[65162]661 */
[65263]662#ifdef VBOX_WITH_LIBOPUS
[65624]663 PAVRECSINK pSink = pStreamAV->pSink;
[65410]664 AssertPtr(pSink);
[65624]665 PRTCIRCBUF pCircBuf = pStreamAV->pCircBuf;
[65410]666 AssertPtr(pCircBuf);
[53442]667
[65429]668 void *pvCircBuf;
669 size_t cbCircBuf;
[65197]670
[68085]671 uint32_t cbToWrite = cxBuf;
[65565]672
[65389]673 /*
674 * Fetch as much as we can into our internal ring buffer.
675 */
[65565]676 while ( cbToWrite
677 && RTCircBufFree(pCircBuf))
[53442]678 {
[65565]679 RTCircBufAcquireWriteBlock(pCircBuf, cbToWrite, &pvCircBuf, &cbCircBuf);
[53442]680
[65429]681 if (cbCircBuf)
[65162]682 {
[65565]683 memcpy(pvCircBuf, (uint8_t *)pvBuf + cbWrittenTotal, cbCircBuf),
[68720]684 cbWrittenTotal += (uint32_t)cbCircBuf;
[65699]685 Assert(cbToWrite >= cbCircBuf);
[68720]686 cbToWrite -= (uint32_t)cbCircBuf;
[65330]687 }
688
[65565]689 RTCircBufReleaseWriteBlock(pCircBuf, cbCircBuf);
[65330]690
691 if ( RT_FAILURE(rc)
[65565]692 || !cbCircBuf)
[65330]693 {
694 break;
695 }
[65389]696 }
[65330]697
[65389]698 /*
699 * Process our internal ring buffer and encode the data.
700 */
[65330]701
[65389]702 uint8_t abSrc[_64K]; /** @todo Fix! */
703 size_t cbSrc;
[65330]704
[68338]705 const uint32_t csFrame = pSink->Codec.Parms.uHz / (1000 /* s in ms */ / pSink->Codec.Opus.msFrame);
706 const uint32_t cbFrame = csFrame * pSink->Codec.Parms.cChannels * (pSink->Codec.Parms.cBits / 8 /* Bytes */);
[65330]707
[68452]708 /* Only encode data if we have data for the given time period (or more). */
[65389]709 while (RTCircBufUsed(pCircBuf) >= cbFrame)
710 {
711 cbSrc = 0;
[65330]712
[65389]713 while (cbSrc < cbFrame)
714 {
[65429]715 RTCircBufAcquireReadBlock(pCircBuf, cbFrame - cbSrc, &pvCircBuf, &cbCircBuf);
[65330]716
[65429]717 if (cbCircBuf)
[65256]718 {
[65429]719 memcpy(&abSrc[cbSrc], pvCircBuf, cbCircBuf);
[65353]720
[65429]721 cbSrc += cbCircBuf;
[65389]722 Assert(cbSrc <= sizeof(abSrc));
[65256]723 }
[65330]724
[65429]725 RTCircBufReleaseReadBlock(pCircBuf, cbCircBuf);
[65389]726
[65429]727 if (!cbCircBuf)
[65330]728 break;
[53442]729 }
[65389]730
[68320]731# ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
[65389]732 RTFILE fh;
[68320]733 RTFileOpen(&fh, VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH "DrvAudioVideoRec.pcm",
734 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
[65389]735 RTFileWrite(fh, abSrc, cbSrc, NULL);
736 RTFileClose(fh);
[65410]737# endif
[65389]738
739 Assert(cbSrc == cbFrame);
740
741 /*
742 * Opus always encodes PER FRAME, that is, exactly 2.5, 5, 10, 20, 40 or 60 ms of audio data.
743 *
744 * A packet can have up to 120ms worth of audio data.
745 * Anything > 120ms of data will result in a "corrupted package" error message by
746 * by decoding application.
747 */
748 uint8_t abDst[_64K]; /** @todo Fix! */
749 size_t cbDst = sizeof(abDst);
750
751 /* Call the encoder to encode one frame per iteration. */
[65410]752 opus_int32 cbWritten = opus_encode(pSink->Codec.Opus.pEnc,
[68720]753 (opus_int16 *)abSrc, csFrame, abDst, (opus_int32)cbDst);
[65389]754 if (cbWritten > 0)
755 {
756 /* Get overall frames encoded. */
[68452]757 const uint32_t cEncFrames = opus_packet_get_nb_frames(abDst, cbWritten);
[65389]758
[70038]759# ifdef VBOX_WITH_STATISTICS
[65410]760 pSink->Codec.STAM.cEncFrames += cEncFrames;
[68452]761 pSink->Codec.STAM.msEncTotal += pSink->Codec.Opus.msFrame * cEncFrames;
[65389]762
[68452]763 LogFunc(("%RU64ms [%RU64 frames]: cbSrc=%zu, cbDst=%zu, cEncFrames=%RU32\n",
764 pSink->Codec.STAM.msEncTotal, pSink->Codec.STAM.cEncFrames, cbSrc, cbDst, cEncFrames));
[65410]765# endif
[65389]766 Assert((uint32_t)cbWritten <= cbDst);
767 cbDst = RT_MIN((uint32_t)cbWritten, cbDst); /* Update cbDst to actual bytes encoded (written). */
768
[68850]769 Assert(cEncFrames == 1); /* At the moment we encode exactly *one* frame per frame. */
770
[69188]771 if (pStreamAV->uLastPTSMs == 0)
772 pStreamAV->uLastPTSMs = RTTimeMilliTS() - pSink->tsStartMs;
773
[68850]774 const uint64_t uDurationMs = pSink->Codec.Opus.msFrame * cEncFrames;
[69188]775 const uint64_t uPTSMs = pStreamAV->uLastPTSMs + uDurationMs;
[68850]776
[69188]777 pStreamAV->uLastPTSMs += uDurationMs;
778
[65410]779 switch (pSink->Con.Parms.enmType)
780 {
781 case AVRECCONTAINERTYPE_MAIN_CONSOLE:
782 {
[69188]783 HRESULT hr = pSink->Con.Main.pConsole->i_audioVideoRecSendAudio(abDst, cbDst, uPTSMs);
[65410]784 Assert(hr == S_OK);
[66266]785 RT_NOREF(hr);
[65410]786
787 break;
788 }
789
790 case AVRECCONTAINERTYPE_WEBM:
791 {
[69188]792 WebMWriter::BlockData_Opus blockData = { abDst, cbDst, uPTSMs };
[65410]793 rc = pSink->Con.WebM.pWebM->WriteBlock(pSink->Con.WebM.uTrack, &blockData, sizeof(blockData));
794 AssertRC(rc);
795
796 break;
797 }
798
799 default:
800 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
801 break;
802 }
[65389]803 }
804 else if (cbWritten < 0)
805 {
806 AssertMsgFailed(("Encoding failed: %s\n", opus_strerror(cbWritten)));
807 rc = VERR_INVALID_PARAMETER;
808 }
809
810 if (RT_FAILURE(rc))
811 break;
[65162]812 }
[68772]813
814 if (pcxWritten)
815 *pcxWritten = cbWrittenTotal;
[65263]816#else
[68772]817 /* Report back all data as being processed. */
818 if (pcxWritten)
819 *pcxWritten = cxBuf;
820
[65263]821 rc = VERR_NOT_SUPPORTED;
822#endif /* VBOX_WITH_LIBOPUS */
[53442]823
[65565]824 LogFlowFunc(("csReadTotal=%RU32, rc=%Rrc\n", cbWrittenTotal, rc));
[53442]825 return rc;
826}
827
[65162]828
829/**
830 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
831 */
832static DECLCALLBACK(int) drvAudioVideoRecGetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
[53442]833{
[65624]834 RT_NOREF(pInterface);
[65162]835 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
[53442]836
[65624]837 pBackendCfg->cbStreamOut = sizeof(AVRECSTREAM);
[65162]838 pBackendCfg->cbStreamIn = 0;
839 pBackendCfg->cMaxStreamsIn = 0;
840 pBackendCfg->cMaxStreamsOut = UINT32_MAX;
[53442]841
[65162]842 return VINF_SUCCESS;
843}
[53442]844
845
[65162]846/**
847 * @interface_method_impl{PDMIHOSTAUDIO,pfnShutdown}
848 */
849static DECLCALLBACK(void) drvAudioVideoRecShutdown(PPDMIHOSTAUDIO pInterface)
850{
[65197]851 LogFlowFuncEnter();
852
853 PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface);
854
[65410]855 avRecSinkShutdown(&pThis->Sink);
[65162]856}
[53442]857
858
[65162]859/**
860 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
861 */
862static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvAudioVideoRecGetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
863{
864 RT_NOREF(enmDir);
865 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
[53442]866
[65162]867 return PDMAUDIOBACKENDSTS_RUNNING;
868}
[53442]869
870
[65162]871/**
872 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
873 */
[65624]874static DECLCALLBACK(int) drvAudioVideoRecStreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
[65162]875 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
876{
877 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
878 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
879 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
[53442]880
[65624]881 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
882 return VERR_NOT_SUPPORTED;
[65410]883
[65624]884 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
885
886 PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface);
887 PAVRECSTREAM pStreamAV = (PAVRECSTREAM)pStream;
888
[65410]889 /* For now we only have one sink, namely the driver's one.
890 * Later each stream could have its own one, to e.g. router different stream to different sinks .*/
891 PAVRECSINK pSink = &pThis->Sink;
892
[65624]893 int rc = avRecCreateStreamOut(pThis, pStreamAV, pSink, pCfgReq, pCfgAcq);
894 if (RT_SUCCESS(rc))
895 {
896 pStreamAV->pCfg = DrvAudioHlpStreamCfgDup(pCfgAcq);
897 if (!pStreamAV->pCfg)
898 rc = VERR_NO_MEMORY;
899 }
[53442]900
[65624]901 return rc;
[65162]902}
[53442]903
904
[65162]905/**
906 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
907 */
[65624]908static DECLCALLBACK(int) drvAudioVideoRecStreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
[53442]909{
[65162]910 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
911 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
[53442]912
[65624]913 PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface);
914 PAVRECSTREAM pStreamAV = (PAVRECSTREAM)pStream;
[65410]915
[65624]916 if (!pStreamAV->pCfg) /* Not (yet) configured? Skip. */
917 return VINF_SUCCESS;
[53442]918
[65624]919 int rc = VINF_SUCCESS;
920
921 if (pStreamAV->pCfg->enmDir == PDMAUDIODIR_OUT)
922 rc = avRecDestroyStreamOut(pThis, pStreamAV);
923
924 if (RT_SUCCESS(rc))
925 {
926 DrvAudioHlpStreamCfgFree(pStreamAV->pCfg);
927 pStreamAV->pCfg = NULL;
928 }
929
930 return rc;
[53442]931}
932
[65162]933
934/**
935 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
936 */
937static DECLCALLBACK(int) drvAudioVideoRecStreamControl(PPDMIHOSTAUDIO pInterface,
[65624]938 PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
[53442]939{
[65162]940 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
941 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
[53442]942
[65624]943 PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface);
944 PAVRECSTREAM pStreamAV = (PAVRECSTREAM)pStream;
[65410]945
[65624]946 if (!pStreamAV->pCfg) /* Not (yet) configured? Skip. */
947 return VINF_SUCCESS;
[53442]948
[65624]949 if (pStreamAV->pCfg->enmDir == PDMAUDIODIR_OUT)
950 return avRecControlStreamOut(pThis, pStreamAV, enmStreamCmd);
[53442]951
952 return VINF_SUCCESS;
953}
954
[65162]955
956/**
[65694]957 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
958 */
959static DECLCALLBACK(uint32_t) drvAudioVideoRecStreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
960{
961 RT_NOREF(pInterface, pStream);
962
963 return 0; /* Video capturing does not provide any input. */
964}
965
966
967/**
968 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
969 */
970static DECLCALLBACK(uint32_t) drvAudioVideoRecStreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
971{
972 RT_NOREF(pInterface, pStream);
973
974 return UINT32_MAX;
975}
976
977
978/**
[65162]979 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus}
980 */
[68272]981static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvAudioVideoRecStreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
[53442]982{
[65694]983 RT_NOREF(pInterface, pStream);
[53442]984
[68272]985 return (PDMAUDIOSTREAMSTS_FLAG_INITIALIZED | PDMAUDIOSTREAMSTS_FLAG_ENABLED);
[53442]986}
987
988
[65162]989/**
990 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamIterate}
991 */
[65624]992static DECLCALLBACK(int) drvAudioVideoRecStreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
[53442]993{
[65162]994 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
995 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
[53442]996
[65162]997 LogFlowFuncEnter();
[53442]998
[65162]999 /* Nothing to do here for video recording. */
[53442]1000 return VINF_SUCCESS;
1001}
1002
[54491]1003
[53442]1004/**
1005 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1006 */
1007static DECLCALLBACK(void *) drvAudioVideoRecQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1008{
1009 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
[65162]1010 PDRVAUDIOVIDEOREC pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVIDEOREC);
1011
[53442]1012 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
[54368]1013 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
[53442]1014 return NULL;
1015}
1016
[65162]1017
[53442]1018AudioVideoRec::AudioVideoRec(Console *pConsole)
[70563]1019 : AudioDriver(pConsole)
1020 , mpDrv(NULL)
[53442]1021{
1022}
1023
[65162]1024
[53442]1025AudioVideoRec::~AudioVideoRec(void)
1026{
1027 if (mpDrv)
1028 {
1029 mpDrv->pAudioVideoRec = NULL;
1030 mpDrv = NULL;
1031 }
1032}
1033
1034
[70644]1035/**
1036 * @copydoc AudioDriver::configureDriver
1037 */
1038int AudioVideoRec::configureDriver(PCFGMNODE pLunCfg)
[70563]1039{
1040 CFGMR3InsertInteger(pLunCfg, "Object", (uintptr_t)mpConsole->i_getAudioVideoRec());
1041 CFGMR3InsertInteger(pLunCfg, "ObjectConsole", (uintptr_t)mpConsole);
[70644]1042
1043 return VINF_SUCCESS;
[70563]1044}
1045
1046
[53442]1047/**
[65162]1048 * Construct a audio video recording driver instance.
[53442]1049 *
1050 * @copydoc FNPDMDRVCONSTRUCT
1051 */
1052/* static */
1053DECLCALLBACK(int) AudioVideoRec::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1054{
[65162]1055 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
[53442]1056 PDRVAUDIOVIDEOREC pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVIDEOREC);
[68882]1057 RT_NOREF(fFlags);
[65162]1058
[65197]1059 LogRel(("Audio: Initializing video recording audio driver\n"));
[53442]1060 LogFlowFunc(("fFlags=0x%x\n", fFlags));
1061
1062 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
1063 ("Configuration error: Not possible to attach anything to this driver!\n"),
1064 VERR_PDM_DRVINS_NO_ATTACH);
1065
1066 /*
1067 * Init the static parts.
1068 */
[54230]1069 pThis->pDrvIns = pDrvIns;
[53442]1070 /* IBase */
[54230]1071 pDrvIns->IBase.pfnQueryInterface = drvAudioVideoRecQueryInterface;
[54368]1072 /* IHostAudio */
1073 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvAudioVideoRec);
[53442]1074
[65162]1075 /*
[65410]1076 * Get the Console object pointer.
[65162]1077 */
[65410]1078 void *pvUser;
1079 int rc = CFGMR3QueryPtr(pCfg, "ObjectConsole", &pvUser); /** @todo r=andy Get rid of this hack and use IHostAudio::SetCallback. */
[68797]1080 AssertRCReturn(rc, rc);
[65410]1081
1082 /* CFGM tree saves the pointer to Console in the Object node of AudioVideoRec. */
1083 pThis->pConsole = (Console *)pvUser;
[68797]1084 AssertReturn(!pThis->pConsole.isNull(), VERR_INVALID_POINTER);
[65410]1085
1086 /*
1087 * Get the pointer to the audio driver instance.
1088 */
1089 rc = CFGMR3QueryPtr(pCfg, "Object", &pvUser); /** @todo r=andy Get rid of this hack and use IHostAudio::SetCallback. */
[68797]1090 AssertRCReturn(rc, rc);
[53442]1091
1092 pThis->pAudioVideoRec = (AudioVideoRec *)pvUser;
[68797]1093 AssertPtrReturn(pThis->pAudioVideoRec, VERR_INVALID_POINTER);
1094
[53442]1095 pThis->pAudioVideoRec->mpDrv = pThis;
1096
1097 /*
1098 * Get the interface for the above driver (DrvAudio) to make mixer/conversion calls.
1099 * Described in CFGM tree.
1100 */
[65162]1101 pThis->pDrvAudio = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIAUDIOCONNECTOR);
1102 AssertMsgReturn(pThis->pDrvAudio, ("Configuration error: No upper interface specified!\n"), VERR_PDM_MISSING_INTERFACE_ABOVE);
[53442]1103
[68725]1104#ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
1105 RTFileDelete(VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH "DrvAudioVideoRec.webm");
1106 RTFileDelete(VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH "DrvAudioVideoRec.pcm");
1107#endif
1108
[53442]1109 return VINF_SUCCESS;
1110}
1111
[65162]1112
1113/**
[68895]1114 * @interface_method_impl{PDMDRVREG,pfnDestruct}
1115 */
1116/* static */
1117DECLCALLBACK(void) AudioVideoRec::drvDestruct(PPDMDRVINS pDrvIns)
1118{
1119 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1120 PDRVAUDIOVIDEOREC pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVIDEOREC);
1121 LogFlowFuncEnter();
1122
1123 /*
1124 * If the AudioVideoRec object is still alive, we must clear it's reference to
1125 * us since we'll be invalid when we return from this method.
1126 */
1127 if (pThis->pAudioVideoRec)
1128 {
1129 pThis->pAudioVideoRec->mpDrv = NULL;
1130 pThis->pAudioVideoRec = NULL;
1131 }
1132}
1133
1134
1135/**
1136 * @interface_method_impl{PDMDRVREG,pfnAttach}
1137 */
1138/* static */
1139DECLCALLBACK(int) AudioVideoRec::drvAttach(PPDMDRVINS pDrvIns, uint32_t fFlags)
1140{
1141 RT_NOREF(pDrvIns, fFlags);
1142
1143 LogFlowFuncEnter();
1144
1145 return VINF_SUCCESS;
1146}
1147
1148/**
1149 * @interface_method_impl{PDMDRVREG,pfnDetach}
1150 */
1151/* static */
1152DECLCALLBACK(void) AudioVideoRec::drvDetach(PPDMDRVINS pDrvIns, uint32_t fFlags)
1153{
1154 RT_NOREF(pDrvIns, fFlags);
1155
1156 LogFlowFuncEnter();
1157}
1158
1159/**
[65162]1160 * Video recording audio driver registration record.
[53442]1161 */
1162const PDMDRVREG AudioVideoRec::DrvReg =
1163{
1164 PDM_DRVREG_VERSION,
1165 /* szName */
1166 "AudioVideoRec",
1167 /* szRCMod */
1168 "",
1169 /* szR0Mod */
1170 "",
1171 /* pszDescription */
1172 "Audio driver for video recording",
1173 /* fFlags */
1174 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1175 /* fClass. */
1176 PDM_DRVREG_CLASS_AUDIO,
1177 /* cMaxInstances */
1178 ~0U,
1179 /* cbInstance */
1180 sizeof(DRVAUDIOVIDEOREC),
1181 /* pfnConstruct */
1182 AudioVideoRec::drvConstruct,
1183 /* pfnDestruct */
1184 AudioVideoRec::drvDestruct,
1185 /* pfnRelocate */
1186 NULL,
1187 /* pfnIOCtl */
1188 NULL,
1189 /* pfnPowerOn */
1190 NULL,
1191 /* pfnReset */
1192 NULL,
1193 /* pfnSuspend */
1194 NULL,
1195 /* pfnResume */
1196 NULL,
1197 /* pfnAttach */
[68895]1198 AudioVideoRec::drvAttach,
[53442]1199 /* pfnDetach */
[68895]1200 AudioVideoRec::drvDetach,
[53442]1201 /* pfnPowerOff */
1202 NULL,
1203 /* pfnSoftReset */
1204 NULL,
1205 /* u32EndVersion */
1206 PDM_DRVREG_VERSION
1207};
1208
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use