VirtualBox

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

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

VideoRec: Made uHz 32-bit.

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

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