[53442] | 1 | /* $Id: DrvAudioRec.cpp 103420 2024-02-19 09:08:34Z vboxsync $ */
|
---|
| 2 | /** @file
|
---|
| 3 | * Video recording audio backend for Main.
|
---|
[88030] | 4 | *
|
---|
| 5 | * This driver is part of Main and is responsible for providing audio
|
---|
| 6 | * data to Main's video capturing feature.
|
---|
| 7 | *
|
---|
| 8 | * The driver itself implements a PDM host audio backend, which in turn
|
---|
| 9 | * provides the driver with the required audio data and audio events.
|
---|
| 10 | *
|
---|
| 11 | * For now there is support for the following destinations (called "sinks"):
|
---|
| 12 | *
|
---|
| 13 | * - Direct writing of .webm files to the host.
|
---|
| 14 | * - Communicating with Main via the Console object to send the encoded audio data to.
|
---|
| 15 | * The Console object in turn then will route the data to the Display / video capturing interface then.
|
---|
[53442] | 16 | */
|
---|
| 17 |
|
---|
| 18 | /*
|
---|
[98103] | 19 | * Copyright (C) 2016-2023 Oracle and/or its affiliates.
|
---|
[53442] | 20 | *
|
---|
[96407] | 21 | * This file is part of VirtualBox base platform packages, as
|
---|
| 22 | * available from https://www.virtualbox.org.
|
---|
| 23 | *
|
---|
| 24 | * This program is free software; you can redistribute it and/or
|
---|
| 25 | * modify it under the terms of the GNU General Public License
|
---|
| 26 | * as published by the Free Software Foundation, in version 3 of the
|
---|
| 27 | * License.
|
---|
| 28 | *
|
---|
| 29 | * This program is distributed in the hope that it will be useful, but
|
---|
| 30 | * WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
| 31 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
| 32 | * General Public License for more details.
|
---|
| 33 | *
|
---|
| 34 | * You should have received a copy of the GNU General Public License
|
---|
| 35 | * along with this program; if not, see <https://www.gnu.org/licenses>.
|
---|
| 36 | *
|
---|
| 37 | * SPDX-License-Identifier: GPL-3.0-only
|
---|
[53442] | 38 | */
|
---|
[65162] | 39 |
|
---|
[68715] | 40 |
|
---|
[65162] | 41 | /*********************************************************************************************************************************
|
---|
| 42 | * Header Files *
|
---|
| 43 | *********************************************************************************************************************************/
|
---|
[96140] | 44 | #define LOG_GROUP LOG_GROUP_RECORDING
|
---|
[67914] | 45 | #include "LoggingNew.h"
|
---|
| 46 |
|
---|
[75344] | 47 | #include "DrvAudioRec.h"
|
---|
[53442] | 48 | #include "ConsoleImpl.h"
|
---|
| 49 |
|
---|
[69683] | 50 | #include "WebMWriter.h"
|
---|
[65162] | 51 |
|
---|
[53442] | 52 | #include <iprt/mem.h>
|
---|
| 53 | #include <iprt/cdefs.h>
|
---|
| 54 |
|
---|
[96175] | 55 | #include "VBox/com/VirtualBox.h"
|
---|
[88028] | 56 | #include <VBox/vmm/cfgm.h>
|
---|
| 57 | #include <VBox/vmm/pdmdrv.h>
|
---|
[53442] | 58 | #include <VBox/vmm/pdmaudioifs.h>
|
---|
[88028] | 59 | #include <VBox/vmm/pdmaudioinline.h>
|
---|
[93444] | 60 | #include <VBox/vmm/vmmr3vtable.h>
|
---|
[53442] | 61 | #include <VBox/err.h>
|
---|
[96175] | 62 | #include "VBox/settings.h"
|
---|
[53442] | 63 |
|
---|
| 64 |
|
---|
[65162] | 65 | /*********************************************************************************************************************************
|
---|
| 66 | * Structures and Typedefs *
|
---|
| 67 | *********************************************************************************************************************************/
|
---|
[53442] | 68 | /**
|
---|
[65410] | 69 | * Enumeration for specifying the recording container type.
|
---|
[65197] | 70 | */
|
---|
[65410] | 71 | typedef enum AVRECCONTAINERTYPE
|
---|
[65197] | 72 | {
|
---|
[65410] | 73 | /** Unknown / invalid container type. */
|
---|
| 74 | AVRECCONTAINERTYPE_UNKNOWN = 0,
|
---|
| 75 | /** Recorded data goes to Main / Console. */
|
---|
| 76 | AVRECCONTAINERTYPE_MAIN_CONSOLE = 1,
|
---|
[68321] | 77 | /** Recorded data will be written to a .webm file. */
|
---|
[65410] | 78 | AVRECCONTAINERTYPE_WEBM = 2
|
---|
| 79 | } AVRECCONTAINERTYPE;
|
---|
[65197] | 80 |
|
---|
| 81 | /**
|
---|
[65410] | 82 | * Structure for keeping generic container parameters.
|
---|
[65197] | 83 | */
|
---|
[65410] | 84 | typedef struct AVRECCONTAINERPARMS
|
---|
| 85 | {
|
---|
[96175] | 86 | /** Stream index (hint). */
|
---|
| 87 | uint32_t idxStream;
|
---|
[65410] | 88 | /** The container's type. */
|
---|
| 89 | AVRECCONTAINERTYPE enmType;
|
---|
[74955] | 90 | union
|
---|
| 91 | {
|
---|
| 92 | /** WebM file specifics. */
|
---|
| 93 | struct
|
---|
| 94 | {
|
---|
| 95 | /** Allocated file name to write .webm file to. Must be free'd. */
|
---|
| 96 | char *pszFile;
|
---|
| 97 | } WebM;
|
---|
| 98 | };
|
---|
[65410] | 99 |
|
---|
| 100 | } AVRECCONTAINERPARMS, *PAVRECCONTAINERPARMS;
|
---|
| 101 |
|
---|
| 102 | /**
|
---|
| 103 | * Structure for keeping container-specific data.
|
---|
| 104 | */
|
---|
| 105 | typedef struct AVRECCONTAINER
|
---|
| 106 | {
|
---|
| 107 | /** Generic container parameters. */
|
---|
| 108 | AVRECCONTAINERPARMS Parms;
|
---|
| 109 |
|
---|
| 110 | union
|
---|
| 111 | {
|
---|
| 112 | struct
|
---|
| 113 | {
|
---|
| 114 | /** Pointer to Console. */
|
---|
| 115 | Console *pConsole;
|
---|
| 116 | } Main;
|
---|
| 117 |
|
---|
| 118 | struct
|
---|
| 119 | {
|
---|
| 120 | /** Pointer to WebM container to write recorded audio data to.
|
---|
| 121 | * See the AVRECMODE enumeration for more information. */
|
---|
| 122 | WebMWriter *pWebM;
|
---|
| 123 | /** Assigned track number from WebM container. */
|
---|
| 124 | uint8_t uTrack;
|
---|
| 125 | } WebM;
|
---|
| 126 | };
|
---|
| 127 | } AVRECCONTAINER, *PAVRECCONTAINER;
|
---|
| 128 |
|
---|
| 129 | /**
|
---|
[96175] | 130 | * Audio video recording sink.
|
---|
[65410] | 131 | */
|
---|
| 132 | typedef struct AVRECSINK
|
---|
| 133 | {
|
---|
[96260] | 134 | /** Pointer (weak) to recording stream to bind to. */
|
---|
| 135 | RecordingStream *pRecStream;
|
---|
[65410] | 136 | /** Container data to use for data processing. */
|
---|
| 137 | AVRECCONTAINER Con;
|
---|
[69188] | 138 | /** Timestamp (in ms) of when the sink was created. */
|
---|
| 139 | uint64_t tsStartMs;
|
---|
[65410] | 140 | } AVRECSINK, *PAVRECSINK;
|
---|
| 141 |
|
---|
[65197] | 142 | /**
|
---|
[65624] | 143 | * Audio video recording (output) stream.
|
---|
[65197] | 144 | */
|
---|
[65624] | 145 | typedef struct AVRECSTREAM
|
---|
[65197] | 146 | {
|
---|
[88718] | 147 | /** Common part. */
|
---|
| 148 | PDMAUDIOBACKENDSTREAM Core;
|
---|
[65624] | 149 | /** The stream's acquired configuration. */
|
---|
[88718] | 150 | PDMAUDIOSTREAMCFG Cfg;
|
---|
[65256] | 151 | /** (Audio) frame buffer. */
|
---|
[88718] | 152 | PRTCIRCBUF pCircBuf;
|
---|
[65410] | 153 | /** Pointer to sink to use for writing. */
|
---|
[88718] | 154 | PAVRECSINK pSink;
|
---|
[69188] | 155 | /** Last encoded PTS (in ms). */
|
---|
[88718] | 156 | uint64_t uLastPTSMs;
|
---|
[75082] | 157 | /** Temporary buffer for the input (source) data to encode. */
|
---|
[88718] | 158 | void *pvSrcBuf;
|
---|
[75082] | 159 | /** Size (in bytes) of the temporary buffer holding the input (source) data to encode. */
|
---|
[88718] | 160 | size_t cbSrcBuf;
|
---|
[65624] | 161 | } AVRECSTREAM, *PAVRECSTREAM;
|
---|
[65197] | 162 |
|
---|
| 163 | /**
|
---|
[65162] | 164 | * Video recording audio driver instance data.
|
---|
[53442] | 165 | */
|
---|
[75354] | 166 | typedef struct DRVAUDIORECORDING
|
---|
[53442] | 167 | {
|
---|
| 168 | /** Pointer to audio video recording object. */
|
---|
[65162] | 169 | AudioVideoRec *pAudioVideoRec;
|
---|
[53442] | 170 | /** Pointer to the driver instance structure. */
|
---|
[65162] | 171 | PPDMDRVINS pDrvIns;
|
---|
| 172 | /** Pointer to host audio interface. */
|
---|
| 173 | PDMIHOSTAUDIO IHostAudio;
|
---|
[65410] | 174 | /** Pointer to the console object. */
|
---|
[73104] | 175 | ComPtr<Console> pConsole;
|
---|
[65162] | 176 | /** Pointer to the DrvAudio port interface that is above us. */
|
---|
[74955] | 177 | AVRECCONTAINERPARMS ContainerParms;
|
---|
[96175] | 178 | /** Weak pointer to recording context to use. */
|
---|
| 179 | RecordingContext *pRecCtx;
|
---|
[65410] | 180 | /** The driver's sink for writing output to. */
|
---|
| 181 | AVRECSINK Sink;
|
---|
[75354] | 182 | } DRVAUDIORECORDING, *PDRVAUDIORECORDING;
|
---|
[53442] | 183 |
|
---|
| 184 |
|
---|
[88459] | 185 | AudioVideoRec::AudioVideoRec(Console *pConsole)
|
---|
| 186 | : AudioDriver(pConsole)
|
---|
| 187 | , mpDrv(NULL)
|
---|
[53442] | 188 | {
|
---|
[88459] | 189 | }
|
---|
[53442] | 190 |
|
---|
[65389] | 191 |
|
---|
[88459] | 192 | AudioVideoRec::~AudioVideoRec(void)
|
---|
| 193 | {
|
---|
| 194 | if (mpDrv)
|
---|
[68338] | 195 | {
|
---|
[88459] | 196 | mpDrv->pAudioVideoRec = NULL;
|
---|
| 197 | mpDrv = NULL;
|
---|
[68338] | 198 | }
|
---|
[88459] | 199 | }
|
---|
[65389] | 200 |
|
---|
[65410] | 201 |
|
---|
[88459] | 202 | /**
|
---|
[96175] | 203 | * Applies recording settings to this driver instance.
|
---|
[88459] | 204 | *
|
---|
| 205 | * @returns VBox status code.
|
---|
[96175] | 206 | * @param Settings Recording settings to apply.
|
---|
[88459] | 207 | */
|
---|
| 208 | int AudioVideoRec::applyConfiguration(const settings::RecordingSettings &Settings)
|
---|
| 209 | {
|
---|
| 210 | /** @todo Do some validation here. */
|
---|
[96175] | 211 | mSettings = Settings; /* Note: Does have an own copy operator. */
|
---|
[88459] | 212 | return VINF_SUCCESS;
|
---|
| 213 | }
|
---|
[65410] | 214 |
|
---|
[68338] | 215 |
|
---|
[93444] | 216 | int AudioVideoRec::configureDriver(PCFGMNODE pLunCfg, PCVMMR3VTABLE pVMM)
|
---|
[88459] | 217 | {
|
---|
[96175] | 218 | /** @todo For now we're using the configuration of the first screen (screen 0) here audio-wise. */
|
---|
| 219 | unsigned const idxScreen = 0;
|
---|
[74848] | 220 |
|
---|
[96175] | 221 | AssertReturn(mSettings.mapScreens.size() >= 1, VERR_INVALID_PARAMETER);
|
---|
| 222 | const settings::RecordingScreenSettings &screenSettings = mSettings.mapScreens[idxScreen];
|
---|
| 223 |
|
---|
| 224 | int vrc = pVMM->pfnCFGMR3InsertInteger(pLunCfg, "ContainerType", (uint64_t)screenSettings.enmDest);
|
---|
[94957] | 225 | AssertRCReturn(vrc, vrc);
|
---|
[96175] | 226 | if (screenSettings.enmDest == RecordingDestination_File)
|
---|
[74848] | 227 | {
|
---|
[96175] | 228 | vrc = pVMM->pfnCFGMR3InsertString(pLunCfg, "ContainerFileName", Utf8Str(screenSettings.File.strName).c_str());
|
---|
[94957] | 229 | AssertRCReturn(vrc, vrc);
|
---|
[74848] | 230 | }
|
---|
[96175] | 231 |
|
---|
| 232 | vrc = pVMM->pfnCFGMR3InsertInteger(pLunCfg, "StreamIndex", (uint32_t)idxScreen);
|
---|
[94957] | 233 | AssertRCReturn(vrc, vrc);
|
---|
[74848] | 234 |
|
---|
[93444] | 235 | return AudioDriver::configureDriver(pLunCfg, pVMM);
|
---|
[88459] | 236 | }
|
---|
[65410] | 237 |
|
---|
| 238 |
|
---|
[88459] | 239 | /*********************************************************************************************************************************
|
---|
| 240 | * PDMIHOSTAUDIO *
|
---|
| 241 | *********************************************************************************************************************************/
|
---|
[74955] | 242 |
|
---|
[88459] | 243 | /**
|
---|
| 244 | * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
|
---|
| 245 | */
|
---|
| 246 | static DECLCALLBACK(int) drvAudioVideoRecHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
|
---|
| 247 | {
|
---|
| 248 | RT_NOREF(pInterface);
|
---|
| 249 | AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
|
---|
[65410] | 250 |
|
---|
[88534] | 251 | /*
|
---|
| 252 | * Fill in the config structure.
|
---|
| 253 | */
|
---|
| 254 | RTStrCopy(pBackendCfg->szName, sizeof(pBackendCfg->szName), "VideoRec");
|
---|
| 255 | pBackendCfg->cbStream = sizeof(AVRECSTREAM);
|
---|
| 256 | pBackendCfg->fFlags = 0;
|
---|
[88459] | 257 | pBackendCfg->cMaxStreamsIn = 0;
|
---|
| 258 | pBackendCfg->cMaxStreamsOut = UINT32_MAX;
|
---|
[65416] | 259 |
|
---|
[88459] | 260 | return VINF_SUCCESS;
|
---|
[65410] | 261 | }
|
---|
| 262 |
|
---|
| 263 |
|
---|
| 264 | /**
|
---|
[88459] | 265 | * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
|
---|
[65410] | 266 | */
|
---|
[88459] | 267 | static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvAudioVideoRecHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
|
---|
[65410] | 268 | {
|
---|
[88459] | 269 | RT_NOREF(pInterface, enmDir);
|
---|
| 270 | return PDMAUDIOBACKENDSTS_RUNNING;
|
---|
[65410] | 271 | }
|
---|
| 272 |
|
---|
| 273 |
|
---|
| 274 | /**
|
---|
| 275 | * Creates an audio output stream and associates it with the specified recording sink.
|
---|
| 276 | *
|
---|
[88300] | 277 | * @returns VBox status code.
|
---|
[65410] | 278 | * @param pThis Driver instance.
|
---|
[65624] | 279 | * @param pStreamAV Audio output stream to create.
|
---|
[65410] | 280 | * @param pSink Recording sink to associate audio output stream to.
|
---|
| 281 | * @param pCfgReq Requested configuration by the audio backend.
|
---|
| 282 | * @param pCfgAcq Acquired configuration by the audio output stream.
|
---|
| 283 | */
|
---|
[75354] | 284 | static int avRecCreateStreamOut(PDRVAUDIORECORDING pThis, PAVRECSTREAM pStreamAV,
|
---|
[96260] | 285 | PAVRECSINK pSink, PCPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
|
---|
[65410] | 286 | {
|
---|
[65624] | 287 | AssertPtrReturn(pThis, VERR_INVALID_POINTER);
|
---|
| 288 | AssertPtrReturn(pStreamAV, VERR_INVALID_POINTER);
|
---|
| 289 | AssertPtrReturn(pSink, VERR_INVALID_POINTER);
|
---|
| 290 | AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
|
---|
| 291 | AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
|
---|
[65410] | 292 |
|
---|
[89218] | 293 | if (pCfgReq->enmPath != PDMAUDIOPATH_OUT_FRONT)
|
---|
[65410] | 294 | {
|
---|
[96175] | 295 | LogRel(("Recording: Support for surround audio not implemented yet\n"));
|
---|
[65410] | 296 | AssertFailed();
|
---|
| 297 | return VERR_NOT_SUPPORTED;
|
---|
| 298 | }
|
---|
| 299 |
|
---|
[96260] | 300 | PRECORDINGCODEC pCodec = pSink->pRecStream->GetAudioCodec();
|
---|
| 301 |
|
---|
[96175] | 302 | /* Stuff which has to be set by now. */
|
---|
| 303 | Assert(pCodec->Parms.cbFrame);
|
---|
| 304 | Assert(pCodec->Parms.msFrame);
|
---|
| 305 |
|
---|
| 306 | int vrc = RTCircBufCreate(&pStreamAV->pCircBuf, pCodec->Parms.cbFrame * 2 /* Use "double buffering" */);
|
---|
[94957] | 307 | if (RT_SUCCESS(vrc))
|
---|
[53442] | 308 | {
|
---|
[96175] | 309 | size_t cbScratchBuf = pCodec->Parms.cbFrame;
|
---|
[75082] | 310 | pStreamAV->pvSrcBuf = RTMemAlloc(cbScratchBuf);
|
---|
| 311 | if (pStreamAV->pvSrcBuf)
|
---|
[65197] | 312 | {
|
---|
[75082] | 313 | pStreamAV->cbSrcBuf = cbScratchBuf;
|
---|
[73370] | 314 |
|
---|
[96229] | 315 | pStreamAV->pSink = pSink; /* Assign sink to stream. */
|
---|
| 316 | pStreamAV->uLastPTSMs = 0;
|
---|
[75082] | 317 |
|
---|
[96229] | 318 | /* Make sure to let the driver backend know that we need the audio data in
|
---|
| 319 | * a specific sampling rate the codec is optimized for. */
|
---|
[96260] | 320 | pCfgAcq->Props = pCodec->Parms.Audio.PCMProps;
|
---|
[75082] | 321 |
|
---|
[96260] | 322 | /* Every codec frame marks a period for now. Optimize this later. */
|
---|
[96229] | 323 | pCfgAcq->Backend.cFramesPeriod = PDMAudioPropsMilliToFrames(&pCfgAcq->Props, pCodec->Parms.msFrame);
|
---|
[96260] | 324 | pCfgAcq->Backend.cFramesBufferSize = pCfgAcq->Backend.cFramesPeriod * 2;
|
---|
| 325 | pCfgAcq->Backend.cFramesPreBuffering = pCfgAcq->Backend.cFramesPeriod;
|
---|
[65353] | 326 | }
|
---|
[75082] | 327 | else
|
---|
[94957] | 328 | vrc = VERR_NO_MEMORY;
|
---|
[53442] | 329 | }
|
---|
| 330 |
|
---|
[94957] | 331 | LogFlowFuncLeaveRC(vrc);
|
---|
| 332 | return vrc;
|
---|
[53442] | 333 | }
|
---|
| 334 |
|
---|
[65162] | 335 |
|
---|
[65410] | 336 | /**
|
---|
[88459] | 337 | * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
|
---|
| 338 | */
|
---|
| 339 | static DECLCALLBACK(int) drvAudioVideoRecHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
|
---|
[89487] | 340 | PCPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
|
---|
[88459] | 341 | {
|
---|
| 342 | PDRVAUDIORECORDING pThis = RT_FROM_CPP_MEMBER(pInterface, DRVAUDIORECORDING, IHostAudio);
|
---|
| 343 | PAVRECSTREAM pStreamAV = (PAVRECSTREAM)pStream;
|
---|
| 344 | AssertPtrReturn(pStreamAV, VERR_INVALID_POINTER);
|
---|
| 345 | AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
|
---|
| 346 | AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
|
---|
| 347 |
|
---|
| 348 | if (pCfgReq->enmDir == PDMAUDIODIR_IN)
|
---|
| 349 | return VERR_NOT_SUPPORTED;
|
---|
| 350 |
|
---|
| 351 | /* For now we only have one sink, namely the driver's one.
|
---|
| 352 | * Later each stream could have its own one, to e.g. router different stream to different sinks .*/
|
---|
| 353 | PAVRECSINK pSink = &pThis->Sink;
|
---|
| 354 |
|
---|
[96260] | 355 | int vrc = avRecCreateStreamOut(pThis, pStreamAV, pSink, pCfgReq, pCfgAcq);
|
---|
[88459] | 356 | PDMAudioStrmCfgCopy(&pStreamAV->Cfg, pCfgAcq);
|
---|
| 357 |
|
---|
[94957] | 358 | return vrc;
|
---|
[88459] | 359 | }
|
---|
| 360 |
|
---|
| 361 |
|
---|
| 362 | /**
|
---|
[65410] | 363 | * Destroys (closes) an audio output stream.
|
---|
| 364 | *
|
---|
[88300] | 365 | * @returns VBox status code.
|
---|
[65410] | 366 | * @param pThis Driver instance.
|
---|
[65624] | 367 | * @param pStreamAV Audio output stream to destroy.
|
---|
[65410] | 368 | */
|
---|
[75354] | 369 | static int avRecDestroyStreamOut(PDRVAUDIORECORDING pThis, PAVRECSTREAM pStreamAV)
|
---|
[65410] | 370 | {
|
---|
| 371 | RT_NOREF(pThis);
|
---|
| 372 |
|
---|
[65624] | 373 | if (pStreamAV->pCircBuf)
|
---|
[65410] | 374 | {
|
---|
[65624] | 375 | RTCircBufDestroy(pStreamAV->pCircBuf);
|
---|
| 376 | pStreamAV->pCircBuf = NULL;
|
---|
[65410] | 377 | }
|
---|
| 378 |
|
---|
[75082] | 379 | if (pStreamAV->pvSrcBuf)
|
---|
| 380 | {
|
---|
| 381 | Assert(pStreamAV->cbSrcBuf);
|
---|
| 382 | RTMemFree(pStreamAV->pvSrcBuf);
|
---|
[82252] | 383 | pStreamAV->pvSrcBuf = NULL;
|
---|
[75082] | 384 | pStreamAV->cbSrcBuf = 0;
|
---|
| 385 | }
|
---|
| 386 |
|
---|
[65410] | 387 | return VINF_SUCCESS;
|
---|
| 388 | }
|
---|
| 389 |
|
---|
| 390 |
|
---|
| 391 | /**
|
---|
[88459] | 392 | * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
|
---|
| 393 | */
|
---|
[89213] | 394 | static DECLCALLBACK(int) drvAudioVideoRecHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
|
---|
| 395 | bool fImmediate)
|
---|
[88459] | 396 | {
|
---|
| 397 | PDRVAUDIORECORDING pThis = RT_FROM_CPP_MEMBER(pInterface, DRVAUDIORECORDING, IHostAudio);
|
---|
| 398 | PAVRECSTREAM pStreamAV = (PAVRECSTREAM)pStream;
|
---|
| 399 | AssertPtrReturn(pStream, VERR_INVALID_POINTER);
|
---|
[89213] | 400 | RT_NOREF(fImmediate);
|
---|
[88459] | 401 |
|
---|
[94957] | 402 | int vrc = VINF_SUCCESS;
|
---|
[88459] | 403 | if (pStreamAV->Cfg.enmDir == PDMAUDIODIR_OUT)
|
---|
[94957] | 404 | vrc = avRecDestroyStreamOut(pThis, pStreamAV);
|
---|
[88459] | 405 |
|
---|
[94957] | 406 | return vrc;
|
---|
[88459] | 407 | }
|
---|
| 408 |
|
---|
| 409 |
|
---|
| 410 | /**
|
---|
[89510] | 411 | * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamEnable}
|
---|
[65410] | 412 | */
|
---|
[89510] | 413 | static DECLCALLBACK(int) drvAudioVideoRecHA_StreamEnable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
|
---|
[53442] | 414 | {
|
---|
[89510] | 415 | RT_NOREF(pInterface, pStream);
|
---|
| 416 | return VINF_SUCCESS;
|
---|
| 417 | }
|
---|
[53442] | 418 |
|
---|
[65353] | 419 |
|
---|
[89510] | 420 | /**
|
---|
| 421 | * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDisable}
|
---|
| 422 | */
|
---|
| 423 | static DECLCALLBACK(int) drvAudioVideoRecHA_StreamDisable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
|
---|
| 424 | {
|
---|
| 425 | RT_NOREF(pInterface, pStream);
|
---|
| 426 | return VINF_SUCCESS;
|
---|
| 427 | }
|
---|
[65353] | 428 |
|
---|
[89510] | 429 |
|
---|
| 430 | /**
|
---|
| 431 | * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPause}
|
---|
| 432 | */
|
---|
| 433 | static DECLCALLBACK(int) drvAudioVideoRecHA_StreamPause(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
|
---|
| 434 | {
|
---|
| 435 | RT_NOREF(pInterface, pStream);
|
---|
| 436 | return VINF_SUCCESS;
|
---|
[53442] | 437 | }
|
---|
| 438 |
|
---|
| 439 |
|
---|
[65162] | 440 | /**
|
---|
[89510] | 441 | * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamResume}
|
---|
[53442] | 442 | */
|
---|
[89510] | 443 | static DECLCALLBACK(int) drvAudioVideoRecHA_StreamResume(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
|
---|
[53442] | 444 | {
|
---|
[89510] | 445 | RT_NOREF(pInterface, pStream);
|
---|
| 446 | return VINF_SUCCESS;
|
---|
| 447 | }
|
---|
[53442] | 448 |
|
---|
| 449 |
|
---|
[89510] | 450 | /**
|
---|
| 451 | * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDrain}
|
---|
| 452 | */
|
---|
| 453 | static DECLCALLBACK(int) drvAudioVideoRecHA_StreamDrain(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
|
---|
| 454 | {
|
---|
| 455 | RT_NOREF(pInterface, pStream);
|
---|
[65162] | 456 | return VINF_SUCCESS;
|
---|
[53442] | 457 | }
|
---|
| 458 |
|
---|
| 459 |
|
---|
[65162] | 460 | /**
|
---|
[89504] | 461 | * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetState}
|
---|
[88459] | 462 | */
|
---|
[89504] | 463 | static DECLCALLBACK(PDMHOSTAUDIOSTREAMSTATE) drvAudioVideoRecHA_StreamGetState(PPDMIHOSTAUDIO pInterface,
|
---|
| 464 | PPDMAUDIOBACKENDSTREAM pStream)
|
---|
[88459] | 465 | {
|
---|
[89504] | 466 | RT_NOREF(pInterface);
|
---|
| 467 | AssertPtrReturn(pStream, PDMHOSTAUDIOSTREAMSTATE_INVALID);
|
---|
| 468 | return PDMHOSTAUDIOSTREAMSTATE_OKAY;
|
---|
[88459] | 469 | }
|
---|
| 470 |
|
---|
| 471 |
|
---|
| 472 | /**
|
---|
| 473 | * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
|
---|
| 474 | */
|
---|
| 475 | static DECLCALLBACK(uint32_t) drvAudioVideoRecHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
|
---|
| 476 | {
|
---|
[96260] | 477 | RT_NOREF(pInterface);
|
---|
| 478 | PAVRECSTREAM pStreamAV = (PAVRECSTREAM)pStream;
|
---|
| 479 |
|
---|
| 480 | RecordingStream *pRecStream = pStreamAV->pSink->pRecStream;
|
---|
| 481 | PRECORDINGCODEC pCodec = pRecStream->GetAudioCodec();
|
---|
| 482 |
|
---|
| 483 | return pCodec->Parms.cbFrame;
|
---|
[88459] | 484 | }
|
---|
| 485 |
|
---|
| 486 |
|
---|
| 487 | /**
|
---|
[65162] | 488 | * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
|
---|
| 489 | */
|
---|
[82255] | 490 | static DECLCALLBACK(int) drvAudioVideoRecHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
|
---|
[88394] | 491 | const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
|
---|
[53442] | 492 | {
|
---|
[88395] | 493 | RT_NOREF(pInterface);
|
---|
[88394] | 494 | PAVRECSTREAM pStreamAV = (PAVRECSTREAM)pStream;
|
---|
| 495 | AssertPtrReturn(pStreamAV, VERR_INVALID_POINTER);
|
---|
[88991] | 496 | if (cbBuf)
|
---|
| 497 | AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
|
---|
[88394] | 498 | AssertReturn(pcbWritten, VERR_INVALID_PARAMETER);
|
---|
[53442] | 499 |
|
---|
[94957] | 500 | int vrc = VINF_SUCCESS;
|
---|
[65429] | 501 |
|
---|
[65565] | 502 | uint32_t cbWrittenTotal = 0;
|
---|
[53442] | 503 |
|
---|
[96175] | 504 | PRTCIRCBUF pCircBuf = pStreamAV->pCircBuf;
|
---|
[65410] | 505 | AssertPtr(pCircBuf);
|
---|
[103420] | 506 | uint32_t const cbFree = (uint32_t)RTCircBufFree(pCircBuf);
|
---|
| 507 | uint32_t cbToWrite = RT_MIN(cbBuf, cbFree);
|
---|
[96260] | 508 | AssertReturn(cbToWrite, VERR_BUFFER_OVERFLOW);
|
---|
[65565] | 509 |
|
---|
[65389] | 510 | /*
|
---|
[88459] | 511 | * Write as much as we can into our internal ring buffer.
|
---|
[65389] | 512 | */
|
---|
[96260] | 513 | while (cbToWrite)
|
---|
[53442] | 514 | {
|
---|
[88459] | 515 | void *pvCircBuf = NULL;
|
---|
| 516 | size_t cbCircBuf = 0;
|
---|
[65565] | 517 | RTCircBufAcquireWriteBlock(pCircBuf, cbToWrite, &pvCircBuf, &cbCircBuf);
|
---|
[53442] | 518 |
|
---|
[96260] | 519 | Log3Func(("cbToWrite=%RU32, cbCircBuf=%zu\n", cbToWrite, cbCircBuf));
|
---|
[65330] | 520 |
|
---|
[96260] | 521 | memcpy(pvCircBuf, (uint8_t *)pvBuf + cbWrittenTotal, cbCircBuf),
|
---|
| 522 | cbWrittenTotal += (uint32_t)cbCircBuf;
|
---|
| 523 | Assert(cbWrittenTotal <= cbBuf);
|
---|
| 524 | Assert(cbToWrite >= cbCircBuf);
|
---|
| 525 | cbToWrite -= (uint32_t)cbCircBuf;
|
---|
| 526 |
|
---|
[65565] | 527 | RTCircBufReleaseWriteBlock(pCircBuf, cbCircBuf);
|
---|
[65389] | 528 | }
|
---|
[65330] | 529 |
|
---|
[96260] | 530 | RecordingStream *pRecStream = pStreamAV->pSink->pRecStream;
|
---|
| 531 | PRECORDINGCODEC pCodec = pRecStream->GetAudioCodec();
|
---|
| 532 |
|
---|
[65389] | 533 | /*
|
---|
[96260] | 534 | * Process our internal ring buffer and send the obtained audio data to our encoding thread.
|
---|
[65389] | 535 | */
|
---|
[96262] | 536 | cbToWrite = (uint32_t)RTCircBufUsed(pCircBuf);
|
---|
[65330] | 537 |
|
---|
[96260] | 538 | /** @todo Can we encode more than a frame at a time? Optimize this! */
|
---|
| 539 | uint32_t const cbFrame = pCodec->Parms.cbFrame;
|
---|
| 540 |
|
---|
| 541 | /* Only encode data if we have data for at least one full codec frame. */
|
---|
| 542 | while (cbToWrite >= cbFrame)
|
---|
[65389] | 543 | {
|
---|
[88459] | 544 | uint32_t cbSrc = 0;
|
---|
[96260] | 545 | do
|
---|
[65389] | 546 | {
|
---|
[88459] | 547 | void *pvCircBuf = NULL;
|
---|
| 548 | size_t cbCircBuf = 0;
|
---|
[96260] | 549 | RTCircBufAcquireReadBlock(pCircBuf, cbFrame - cbSrc, &pvCircBuf, &cbCircBuf);
|
---|
[65330] | 550 |
|
---|
[96260] | 551 | Log3Func(("cbSrc=%RU32, cbCircBuf=%zu\n", cbSrc, cbCircBuf));
|
---|
[65353] | 552 |
|
---|
[96260] | 553 | memcpy((uint8_t *)pStreamAV->pvSrcBuf + cbSrc, pvCircBuf, cbCircBuf);
|
---|
[65330] | 554 |
|
---|
[96260] | 555 | cbSrc += (uint32_t)cbCircBuf;
|
---|
| 556 | Assert(cbSrc <= pStreamAV->cbSrcBuf);
|
---|
| 557 | Assert(cbSrc <= cbFrame);
|
---|
| 558 |
|
---|
[65429] | 559 | RTCircBufReleaseReadBlock(pCircBuf, cbCircBuf);
|
---|
[65389] | 560 |
|
---|
[96260] | 561 | if (cbSrc == cbFrame) /* Only send full codec frames. */
|
---|
| 562 | {
|
---|
[96284] | 563 | vrc = pRecStream->SendAudioFrame(pStreamAV->pvSrcBuf, cbSrc, RTTimeProgramMilliTS());
|
---|
[96260] | 564 | if (RT_FAILURE(vrc))
|
---|
| 565 | break;
|
---|
| 566 | }
|
---|
[88459] | 567 |
|
---|
[96260] | 568 | } while (cbSrc < cbFrame);
|
---|
[65389] | 569 |
|
---|
[96260] | 570 | Assert(cbToWrite >= cbFrame);
|
---|
| 571 | cbToWrite -= cbFrame;
|
---|
[65389] | 572 |
|
---|
[94957] | 573 | if (RT_FAILURE(vrc))
|
---|
[65389] | 574 | break;
|
---|
[68772] | 575 |
|
---|
[96175] | 576 | } /* while */
|
---|
| 577 |
|
---|
[88394] | 578 | *pcbWritten = cbWrittenTotal;
|
---|
[68772] | 579 |
|
---|
[96260] | 580 | LogFlowFunc(("cbBuf=%RU32, cbWrittenTotal=%RU32, vrc=%Rrc\n", cbBuf, cbWrittenTotal, vrc));
|
---|
[96175] | 581 | return VINF_SUCCESS; /* Don't propagate encoding errors to the caller. */
|
---|
[53442] | 582 | }
|
---|
| 583 |
|
---|
[65162] | 584 |
|
---|
| 585 | /**
|
---|
[89504] | 586 | * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
|
---|
| 587 | */
|
---|
| 588 | static DECLCALLBACK(uint32_t) drvAudioVideoRecHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
|
---|
| 589 | {
|
---|
| 590 | RT_NOREF(pInterface, pStream);
|
---|
| 591 | return 0; /* Video capturing does not provide any input. */
|
---|
| 592 | }
|
---|
| 593 |
|
---|
| 594 |
|
---|
| 595 | /**
|
---|
[88459] | 596 | * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
|
---|
[65162] | 597 | */
|
---|
[88459] | 598 | static DECLCALLBACK(int) drvAudioVideoRecHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
|
---|
| 599 | void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
|
---|
[53442] | 600 | {
|
---|
[88459] | 601 | RT_NOREF(pInterface, pStream, pvBuf, cbBuf);
|
---|
| 602 | *pcbRead = 0;
|
---|
[65162] | 603 | return VINF_SUCCESS;
|
---|
| 604 | }
|
---|
[53442] | 605 |
|
---|
| 606 |
|
---|
[88459] | 607 | /*********************************************************************************************************************************
|
---|
| 608 | * PDMIBASE *
|
---|
| 609 | *********************************************************************************************************************************/
|
---|
[53442] | 610 |
|
---|
[65162] | 611 | /**
|
---|
[53442] | 612 | * @interface_method_impl{PDMIBASE,pfnQueryInterface}
|
---|
| 613 | */
|
---|
| 614 | static DECLCALLBACK(void *) drvAudioVideoRecQueryInterface(PPDMIBASE pInterface, const char *pszIID)
|
---|
| 615 | {
|
---|
| 616 | PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
|
---|
[75354] | 617 | PDRVAUDIORECORDING pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIORECORDING);
|
---|
[65162] | 618 |
|
---|
[53442] | 619 | PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
|
---|
[54368] | 620 | PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
|
---|
[53442] | 621 | return NULL;
|
---|
| 622 | }
|
---|
| 623 |
|
---|
[65162] | 624 |
|
---|
[88459] | 625 | /*********************************************************************************************************************************
|
---|
| 626 | * PDMDRVREG *
|
---|
| 627 | *********************************************************************************************************************************/
|
---|
[53442] | 628 |
|
---|
[70644] | 629 | /**
|
---|
[88459] | 630 | * Shuts down (closes) a recording sink,
|
---|
[74955] | 631 | *
|
---|
[88459] | 632 | * @param pSink Recording sink to shut down.
|
---|
[74955] | 633 | */
|
---|
[88459] | 634 | static void avRecSinkShutdown(PAVRECSINK pSink)
|
---|
[74955] | 635 | {
|
---|
[88459] | 636 | AssertPtrReturnVoid(pSink);
|
---|
[74955] | 637 |
|
---|
[96260] | 638 | pSink->pRecStream = NULL;
|
---|
[96175] | 639 |
|
---|
[88459] | 640 | switch (pSink->Con.Parms.enmType)
|
---|
| 641 | {
|
---|
| 642 | case AVRECCONTAINERTYPE_WEBM:
|
---|
| 643 | {
|
---|
| 644 | if (pSink->Con.WebM.pWebM)
|
---|
| 645 | {
|
---|
| 646 | LogRel2(("Recording: Finished recording audio to file '%s' (%zu bytes)\n",
|
---|
| 647 | pSink->Con.WebM.pWebM->GetFileName().c_str(), pSink->Con.WebM.pWebM->GetFileSize()));
|
---|
[74955] | 648 |
|
---|
[94957] | 649 | int vrc2 = pSink->Con.WebM.pWebM->Close();
|
---|
| 650 | AssertRC(vrc2);
|
---|
[70644] | 651 |
|
---|
[88459] | 652 | delete pSink->Con.WebM.pWebM;
|
---|
| 653 | pSink->Con.WebM.pWebM = NULL;
|
---|
| 654 | }
|
---|
| 655 | break;
|
---|
| 656 | }
|
---|
[75251] | 657 |
|
---|
[88459] | 658 | case AVRECCONTAINERTYPE_MAIN_CONSOLE:
|
---|
[96175] | 659 | RT_FALL_THROUGH();
|
---|
[88459] | 660 | default:
|
---|
| 661 | break;
|
---|
[74955] | 662 | }
|
---|
[70563] | 663 | }
|
---|
| 664 |
|
---|
| 665 |
|
---|
[53442] | 666 | /**
|
---|
[88382] | 667 | * @interface_method_impl{PDMDRVREG,pfnPowerOff}
|
---|
| 668 | */
|
---|
| 669 | /*static*/ DECLCALLBACK(void) AudioVideoRec::drvPowerOff(PPDMDRVINS pDrvIns)
|
---|
| 670 | {
|
---|
| 671 | PDRVAUDIORECORDING pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIORECORDING);
|
---|
| 672 | LogFlowFuncEnter();
|
---|
| 673 | avRecSinkShutdown(&pThis->Sink);
|
---|
| 674 | }
|
---|
| 675 |
|
---|
| 676 |
|
---|
| 677 | /**
|
---|
| 678 | * @interface_method_impl{PDMDRVREG,pfnDestruct}
|
---|
| 679 | */
|
---|
| 680 | /*static*/ DECLCALLBACK(void) AudioVideoRec::drvDestruct(PPDMDRVINS pDrvIns)
|
---|
| 681 | {
|
---|
| 682 | PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
|
---|
| 683 | PDRVAUDIORECORDING pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIORECORDING);
|
---|
| 684 |
|
---|
| 685 | LogFlowFuncEnter();
|
---|
| 686 |
|
---|
| 687 | switch (pThis->ContainerParms.enmType)
|
---|
| 688 | {
|
---|
| 689 | case AVRECCONTAINERTYPE_WEBM:
|
---|
| 690 | {
|
---|
| 691 | avRecSinkShutdown(&pThis->Sink);
|
---|
| 692 | RTStrFree(pThis->ContainerParms.WebM.pszFile);
|
---|
| 693 | break;
|
---|
| 694 | }
|
---|
| 695 |
|
---|
| 696 | default:
|
---|
| 697 | break;
|
---|
| 698 | }
|
---|
| 699 |
|
---|
| 700 | /*
|
---|
| 701 | * If the AudioVideoRec object is still alive, we must clear it's reference to
|
---|
| 702 | * us since we'll be invalid when we return from this method.
|
---|
| 703 | */
|
---|
| 704 | if (pThis->pAudioVideoRec)
|
---|
| 705 | {
|
---|
| 706 | pThis->pAudioVideoRec->mpDrv = NULL;
|
---|
| 707 | pThis->pAudioVideoRec = NULL;
|
---|
| 708 | }
|
---|
| 709 |
|
---|
| 710 | LogFlowFuncLeave();
|
---|
| 711 | }
|
---|
| 712 |
|
---|
| 713 |
|
---|
| 714 | /**
|
---|
[88459] | 715 | * Initializes a recording sink.
|
---|
| 716 | *
|
---|
| 717 | * @returns VBox status code.
|
---|
| 718 | * @param pThis Driver instance.
|
---|
| 719 | * @param pSink Sink to initialize.
|
---|
| 720 | * @param pConParms Container parameters to set.
|
---|
[96260] | 721 | * @param pStream Recording stream to asssign sink to.
|
---|
[88459] | 722 | */
|
---|
[96260] | 723 | static int avRecSinkInit(PDRVAUDIORECORDING pThis, PAVRECSINK pSink, PAVRECCONTAINERPARMS pConParms, RecordingStream *pStream)
|
---|
[88459] | 724 | {
|
---|
[96260] | 725 | pSink->pRecStream = pStream;
|
---|
[88459] | 726 |
|
---|
[96207] | 727 | int vrc = VINF_SUCCESS;
|
---|
[88459] | 728 |
|
---|
[96175] | 729 | /*
|
---|
| 730 | * Container setup.
|
---|
| 731 | */
|
---|
[88459] | 732 | try
|
---|
| 733 | {
|
---|
| 734 | switch (pConParms->enmType)
|
---|
| 735 | {
|
---|
| 736 | case AVRECCONTAINERTYPE_MAIN_CONSOLE:
|
---|
| 737 | {
|
---|
| 738 | if (pThis->pConsole)
|
---|
| 739 | {
|
---|
| 740 | pSink->Con.Main.pConsole = pThis->pConsole;
|
---|
| 741 | }
|
---|
| 742 | else
|
---|
[94957] | 743 | vrc = VERR_NOT_SUPPORTED;
|
---|
[88459] | 744 | break;
|
---|
| 745 | }
|
---|
| 746 |
|
---|
| 747 | case AVRECCONTAINERTYPE_WEBM:
|
---|
| 748 | {
|
---|
[96260] | 749 | #if 0
|
---|
[88459] | 750 | /* If we only record audio, create our own WebM writer instance here. */
|
---|
| 751 | if (!pSink->Con.WebM.pWebM) /* Do we already have our WebM writer instance? */
|
---|
| 752 | {
|
---|
| 753 | /** @todo Add sink name / number to file name. */
|
---|
| 754 | const char *pszFile = pSink->Con.Parms.WebM.pszFile;
|
---|
| 755 | AssertPtr(pszFile);
|
---|
| 756 |
|
---|
| 757 | pSink->Con.WebM.pWebM = new WebMWriter();
|
---|
[94957] | 758 | vrc = pSink->Con.WebM.pWebM->Open(pszFile,
|
---|
| 759 | /** @todo Add option to add some suffix if file exists instead of overwriting? */
|
---|
| 760 | RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_NONE,
|
---|
[96175] | 761 | pSink->pCodec->Parms.enmAudioCodec, RecordingVideoCodec_None);
|
---|
[94957] | 762 | if (RT_SUCCESS(vrc))
|
---|
[88459] | 763 | {
|
---|
[96175] | 764 | const PPDMAUDIOPCMPROPS pPCMProps = &pCodec->Parms.Audio.PCMProps;
|
---|
| 765 |
|
---|
| 766 | vrc = pSink->Con.WebM.pWebM->AddAudioTrack(pSink->pCodec,
|
---|
| 767 | PDMAudioPropsHz(pPCMProps), PDMAudioPropsChannels(pPCMProps),
|
---|
| 768 | PDMAudioPropsSampleBits(pPCMProps), &pSink->Con.WebM.uTrack);
|
---|
[94957] | 769 | if (RT_SUCCESS(vrc))
|
---|
[88459] | 770 | {
|
---|
| 771 | LogRel(("Recording: Recording audio to audio file '%s'\n", pszFile));
|
---|
| 772 | }
|
---|
| 773 | else
|
---|
[94957] | 774 | LogRel(("Recording: Error creating audio track for audio file '%s' (%Rrc)\n", pszFile, vrc));
|
---|
[88459] | 775 | }
|
---|
| 776 | else
|
---|
[94957] | 777 | LogRel(("Recording: Error creating audio file '%s' (%Rrc)\n", pszFile, vrc));
|
---|
[88459] | 778 | }
|
---|
| 779 | break;
|
---|
[96260] | 780 | #endif
|
---|
[88459] | 781 | }
|
---|
| 782 |
|
---|
| 783 | default:
|
---|
[94957] | 784 | vrc = VERR_NOT_SUPPORTED;
|
---|
[88459] | 785 | break;
|
---|
| 786 | }
|
---|
| 787 | }
|
---|
| 788 | catch (std::bad_alloc &)
|
---|
| 789 | {
|
---|
[94957] | 790 | vrc = VERR_NO_MEMORY;
|
---|
[88459] | 791 | }
|
---|
| 792 |
|
---|
[94957] | 793 | if (RT_SUCCESS(vrc))
|
---|
[88459] | 794 | {
|
---|
[96175] | 795 | pSink->Con.Parms.enmType = pConParms->enmType;
|
---|
| 796 | pSink->tsStartMs = RTTimeMilliTS();
|
---|
[88459] | 797 |
|
---|
[96175] | 798 | return VINF_SUCCESS;
|
---|
[88459] | 799 | }
|
---|
| 800 |
|
---|
[96175] | 801 | LogRel(("Recording: Error creating sink (%Rrc)\n", vrc));
|
---|
[94957] | 802 | return vrc;
|
---|
[88459] | 803 | }
|
---|
| 804 |
|
---|
| 805 |
|
---|
| 806 | /**
|
---|
[65162] | 807 | * Construct a audio video recording driver instance.
|
---|
[53442] | 808 | *
|
---|
| 809 | * @copydoc FNPDMDRVCONSTRUCT
|
---|
| 810 | */
|
---|
[88382] | 811 | /*static*/ DECLCALLBACK(int) AudioVideoRec::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
|
---|
[53442] | 812 | {
|
---|
[65162] | 813 | PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
|
---|
[75354] | 814 | PDRVAUDIORECORDING pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIORECORDING);
|
---|
[68882] | 815 | RT_NOREF(fFlags);
|
---|
[65162] | 816 |
|
---|
[65197] | 817 | LogRel(("Audio: Initializing video recording audio driver\n"));
|
---|
[53442] | 818 | LogFlowFunc(("fFlags=0x%x\n", fFlags));
|
---|
| 819 |
|
---|
| 820 | AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
|
---|
| 821 | ("Configuration error: Not possible to attach anything to this driver!\n"),
|
---|
| 822 | VERR_PDM_DRVINS_NO_ATTACH);
|
---|
| 823 |
|
---|
| 824 | /*
|
---|
| 825 | * Init the static parts.
|
---|
| 826 | */
|
---|
[54230] | 827 | pThis->pDrvIns = pDrvIns;
|
---|
[53442] | 828 | /* IBase */
|
---|
[54230] | 829 | pDrvIns->IBase.pfnQueryInterface = drvAudioVideoRecQueryInterface;
|
---|
[54368] | 830 | /* IHostAudio */
|
---|
[88761] | 831 | pThis->IHostAudio.pfnGetConfig = drvAudioVideoRecHA_GetConfig;
|
---|
| 832 | pThis->IHostAudio.pfnGetDevices = NULL;
|
---|
[89258] | 833 | pThis->IHostAudio.pfnSetDevice = NULL;
|
---|
[88761] | 834 | pThis->IHostAudio.pfnGetStatus = drvAudioVideoRecHA_GetStatus;
|
---|
[88819] | 835 | pThis->IHostAudio.pfnDoOnWorkerThread = NULL;
|
---|
| 836 | pThis->IHostAudio.pfnStreamConfigHint = NULL;
|
---|
[88761] | 837 | pThis->IHostAudio.pfnStreamCreate = drvAudioVideoRecHA_StreamCreate;
|
---|
[88819] | 838 | pThis->IHostAudio.pfnStreamInitAsync = NULL;
|
---|
[88761] | 839 | pThis->IHostAudio.pfnStreamDestroy = drvAudioVideoRecHA_StreamDestroy;
|
---|
| 840 | pThis->IHostAudio.pfnStreamNotifyDeviceChanged = NULL;
|
---|
[89510] | 841 | pThis->IHostAudio.pfnStreamEnable = drvAudioVideoRecHA_StreamEnable;
|
---|
| 842 | pThis->IHostAudio.pfnStreamDisable = drvAudioVideoRecHA_StreamDisable;
|
---|
| 843 | pThis->IHostAudio.pfnStreamPause = drvAudioVideoRecHA_StreamPause;
|
---|
| 844 | pThis->IHostAudio.pfnStreamResume = drvAudioVideoRecHA_StreamResume;
|
---|
| 845 | pThis->IHostAudio.pfnStreamDrain = drvAudioVideoRecHA_StreamDrain;
|
---|
[89504] | 846 | pThis->IHostAudio.pfnStreamGetState = drvAudioVideoRecHA_StreamGetState;
|
---|
| 847 | pThis->IHostAudio.pfnStreamGetPending = NULL;
|
---|
[88761] | 848 | pThis->IHostAudio.pfnStreamGetWritable = drvAudioVideoRecHA_StreamGetWritable;
|
---|
| 849 | pThis->IHostAudio.pfnStreamPlay = drvAudioVideoRecHA_StreamPlay;
|
---|
[89504] | 850 | pThis->IHostAudio.pfnStreamGetReadable = drvAudioVideoRecHA_StreamGetReadable;
|
---|
[88761] | 851 | pThis->IHostAudio.pfnStreamCapture = drvAudioVideoRecHA_StreamCapture;
|
---|
[53442] | 852 |
|
---|
[65162] | 853 | /*
|
---|
[93444] | 854 | * Read configuration.
|
---|
| 855 | */
|
---|
| 856 | PCPDMDRVHLPR3 const pHlp = pDrvIns->pHlpR3;
|
---|
| 857 | /** @todo validate it. */
|
---|
| 858 |
|
---|
| 859 | /*
|
---|
[65410] | 860 | * Get the Console object pointer.
|
---|
[65162] | 861 | */
|
---|
[94326] | 862 | com::Guid ConsoleUuid(COM_IIDOF(IConsole));
|
---|
| 863 | IConsole *pIConsole = (IConsole *)PDMDrvHlpQueryGenericUserObject(pDrvIns, ConsoleUuid.raw());
|
---|
| 864 | AssertLogRelReturn(pIConsole, VERR_INTERNAL_ERROR_3);
|
---|
| 865 | Console *pConsole = static_cast<Console *>(pIConsole);
|
---|
| 866 | AssertLogRelReturn(pConsole, VERR_INTERNAL_ERROR_3);
|
---|
[65410] | 867 |
|
---|
[94326] | 868 | pThis->pConsole = pConsole;
|
---|
[68797] | 869 | AssertReturn(!pThis->pConsole.isNull(), VERR_INVALID_POINTER);
|
---|
[94326] | 870 | pThis->pAudioVideoRec = pConsole->i_recordingGetAudioDrv();
|
---|
| 871 | AssertPtrReturn(pThis->pAudioVideoRec, VERR_INVALID_POINTER);
|
---|
[65410] | 872 |
|
---|
[94326] | 873 | pThis->pAudioVideoRec->mpDrv = pThis;
|
---|
[53442] | 874 |
|
---|
[74955] | 875 | /*
|
---|
[96175] | 876 | * Get the recording container parameters from the audio driver instance.
|
---|
[74955] | 877 | */
|
---|
| 878 | RT_ZERO(pThis->ContainerParms);
|
---|
[96175] | 879 | PAVRECCONTAINERPARMS pConParams = &pThis->ContainerParms;
|
---|
[74955] | 880 |
|
---|
[96175] | 881 | int vrc = pHlp->pfnCFGMQueryU32(pCfg, "StreamIndex", (uint32_t *)&pConParams->idxStream);
|
---|
[94957] | 882 | AssertRCReturn(vrc, vrc);
|
---|
[74955] | 883 |
|
---|
[96175] | 884 | vrc = pHlp->pfnCFGMQueryU32(pCfg, "ContainerType", (uint32_t *)&pConParams->enmType);
|
---|
| 885 | AssertRCReturn(vrc, vrc);
|
---|
| 886 |
|
---|
[74955] | 887 | switch (pConParams->enmType)
|
---|
| 888 | {
|
---|
| 889 | case AVRECCONTAINERTYPE_WEBM:
|
---|
[94957] | 890 | vrc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "ContainerFileName", &pConParams->WebM.pszFile);
|
---|
| 891 | AssertRCReturn(vrc, vrc);
|
---|
[74955] | 892 | break;
|
---|
| 893 |
|
---|
| 894 | default:
|
---|
| 895 | break;
|
---|
| 896 | }
|
---|
| 897 |
|
---|
[96175] | 898 | /*
|
---|
| 899 | * Obtain the recording context.
|
---|
| 900 | */
|
---|
[96260] | 901 | pThis->pRecCtx = pConsole->i_recordingGetContext();
|
---|
[96175] | 902 | AssertPtrReturn(pThis->pRecCtx, VERR_INVALID_POINTER);
|
---|
[88269] | 903 |
|
---|
[53442] | 904 | /*
|
---|
[96175] | 905 | * Get the codec configuration.
|
---|
[53442] | 906 | */
|
---|
[96175] | 907 | RecordingStream *pStream = pThis->pRecCtx->GetStream(pConParams->idxStream);
|
---|
| 908 | AssertPtrReturn(pStream, VERR_INVALID_POINTER);
|
---|
[53442] | 909 |
|
---|
[88381] | 910 | /*
|
---|
| 911 | * Init the recording sink.
|
---|
| 912 | */
|
---|
[96260] | 913 | vrc = avRecSinkInit(pThis, &pThis->Sink, &pThis->ContainerParms, pStream);
|
---|
[94957] | 914 | if (RT_SUCCESS(vrc))
|
---|
[88381] | 915 | LogRel2(("Recording: Audio recording driver initialized\n"));
|
---|
| 916 | else
|
---|
[94957] | 917 | LogRel(("Recording: Audio recording driver initialization failed: %Rrc\n", vrc));
|
---|
[88381] | 918 |
|
---|
[94957] | 919 | return vrc;
|
---|
[53442] | 920 | }
|
---|
| 921 |
|
---|
[65162] | 922 |
|
---|
| 923 | /**
|
---|
| 924 | * Video recording audio driver registration record.
|
---|
[53442] | 925 | */
|
---|
| 926 | const PDMDRVREG AudioVideoRec::DrvReg =
|
---|
| 927 | {
|
---|
| 928 | PDM_DRVREG_VERSION,
|
---|
| 929 | /* szName */
|
---|
| 930 | "AudioVideoRec",
|
---|
| 931 | /* szRCMod */
|
---|
| 932 | "",
|
---|
| 933 | /* szR0Mod */
|
---|
| 934 | "",
|
---|
| 935 | /* pszDescription */
|
---|
| 936 | "Audio driver for video recording",
|
---|
| 937 | /* fFlags */
|
---|
| 938 | PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
|
---|
| 939 | /* fClass. */
|
---|
| 940 | PDM_DRVREG_CLASS_AUDIO,
|
---|
| 941 | /* cMaxInstances */
|
---|
| 942 | ~0U,
|
---|
| 943 | /* cbInstance */
|
---|
[75354] | 944 | sizeof(DRVAUDIORECORDING),
|
---|
[53442] | 945 | /* pfnConstruct */
|
---|
| 946 | AudioVideoRec::drvConstruct,
|
---|
| 947 | /* pfnDestruct */
|
---|
| 948 | AudioVideoRec::drvDestruct,
|
---|
| 949 | /* pfnRelocate */
|
---|
| 950 | NULL,
|
---|
| 951 | /* pfnIOCtl */
|
---|
| 952 | NULL,
|
---|
| 953 | /* pfnPowerOn */
|
---|
| 954 | NULL,
|
---|
| 955 | /* pfnReset */
|
---|
| 956 | NULL,
|
---|
| 957 | /* pfnSuspend */
|
---|
| 958 | NULL,
|
---|
| 959 | /* pfnResume */
|
---|
| 960 | NULL,
|
---|
| 961 | /* pfnAttach */
|
---|
[88382] | 962 | NULL,
|
---|
[53442] | 963 | /* pfnDetach */
|
---|
[88382] | 964 | NULL,
|
---|
[53442] | 965 | /* pfnPowerOff */
|
---|
[88382] | 966 | AudioVideoRec::drvPowerOff,
|
---|
[53442] | 967 | /* pfnSoftReset */
|
---|
| 968 | NULL,
|
---|
| 969 | /* u32EndVersion */
|
---|
| 970 | PDM_DRVREG_VERSION
|
---|
| 971 | };
|
---|