Changeset 65410 in vbox for trunk/src/VBox/Main/src-client
- Timestamp:
- Jan 24, 2017 10:06:12 AM (8 years ago)
- svn:sync-xref-src-repo-rev:
- 113018
- Location:
- trunk/src/VBox/Main/src-client
- Files:
-
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/src-client/ConsoleImpl.cpp
r65162 r65410 5394 5394 int vrc = VINF_SUCCESS; 5395 5395 if (SUCCEEDED(rc)) 5396 vrc = mDisplay->i_ VideoCaptureEnableScreens(ComSafeArrayAsInParam(screens));5396 vrc = mDisplay->i_videoCaptureEnableScreens(ComSafeArrayAsInParam(screens)); 5397 5397 if (RT_SUCCESS(vrc)) 5398 5398 { 5399 5399 if (fEnabled) 5400 5400 { 5401 vrc = mDisplay->i_ VideoCaptureStart();5401 vrc = mDisplay->i_videoCaptureStart(); 5402 5402 if (RT_FAILURE(vrc)) 5403 5403 rc = setError(E_FAIL, tr("Unable to start video capturing (%Rrc)"), vrc); 5404 5404 } 5405 5405 else 5406 mDisplay->i_ VideoCaptureStop();5406 mDisplay->i_videoCaptureStop(); 5407 5407 } 5408 5408 else … … 6575 6575 return S_OK; 6576 6576 } 6577 6578 #ifdef VBOX_WITH_AUDIO_VIDEOREC 6579 HRESULT Console::i_audioVideoRecSendAudio(const void *pvData, size_t cbData, uint64_t uTimestampMs) 6580 { 6581 if (mDisplay) 6582 { 6583 int rc2 = mDisplay->i_videoCaptureSendAudio(pvData, cbData, uTimestampMs); 6584 AssertRC(rc2); 6585 } 6586 6587 return S_OK; 6588 } 6589 #endif /* VBOX_WITH_AUDIO_VIDEOREC */ 6577 6590 6578 6591 /** -
trunk/src/VBox/Main/src-client/ConsoleImpl2.cpp
r65172 r65410 2968 2968 InsertConfigString(pCfg, "StreamName", bstr); 2969 2969 InsertConfigInteger(pCfg, "Object", (uintptr_t)mAudioVideoRec); 2970 InsertConfigInteger(pCfg, "ObjectConsole", (uintptr_t)this /* Console */); 2970 2971 #endif /* VBOX_WITH_AUDIO_VIDEOREC */ 2971 2972 -
trunk/src/VBox/Main/src-client/DisplayImpl.cpp
r65401 r65410 131 131 mfHostCursorCapabilities = 0; 132 132 #endif 133 133 134 #ifdef VBOX_WITH_VIDEOREC 134 135 rc = RTCritSectInit(&mVideoCaptureLock); … … 145 146 AssertRC(rc); 146 147 #endif 148 147 149 #ifdef VBOX_WITH_CROGL 148 150 RT_ZERO(mCrOglCallbacks); … … 2263 2265 } 2264 2266 2265 2266 int Display::i_VideoCaptureEnableScreens(ComSafeArrayIn(BOOL, aScreens)) 2267 int Display::i_videoCaptureEnableScreens(ComSafeArrayIn(BOOL, aScreens)) 2267 2268 { 2268 2269 #ifdef VBOX_WITH_VIDEOREC … … 2286 2287 2287 2288 /** 2289 * Sends belonging audio samples to the video capturing code. 2290 * Does nothing if capturing is disabled or if audio support for video capturing is disabled. 2291 * 2292 * @returns IPRT status code. 2293 * @param pvData Audio data. 2294 * @param cbData Size (in bytes) of audio data. 2295 * @param uTimestampMs Timestamp (in ms) of the audio data. 2296 */ 2297 int Display::i_videoCaptureSendAudio(const void *pvData, size_t cbData, uint64_t uTimestampMs) 2298 { 2299 #ifdef VBOX_WITH_AUDIO_VIDEOREC 2300 if (!VideoRecIsEnabled(mpVideoRecCtx)) 2301 return VINF_SUCCESS; 2302 2303 return VideoRecSendAudio(mpVideoRecCtx, pvData, cbData, uTimestampMs); 2304 #else 2305 RT_NOREF(pvData, cbData, uTimestampMs); 2306 return VERR_NOT_SUPPORTED; 2307 #endif 2308 } 2309 2310 /** 2288 2311 * Start video capturing. Does nothing if capturing is already active. 2312 * 2313 * @returns IPRT status code. 2289 2314 */ 2290 int Display::i_ VideoCaptureStart()2315 int Display::i_videoCaptureStart(void) 2291 2316 { 2292 2317 #ifdef VBOX_WITH_VIDEOREC … … 2403 2428 * Stop video capturing. Does nothing if video capturing is not active. 2404 2429 */ 2405 void Display::i_ VideoCaptureStop()2430 void Display::i_videoCaptureStop() 2406 2431 { 2407 2432 #ifdef VBOX_WITH_VIDEOREC … … 3185 3210 if (VideoRecLimitReached(pDisplay->mpVideoRecCtx, uScreenId, u64Now)) 3186 3211 { 3187 pDisplay->i_ VideoCaptureStop();3212 pDisplay->i_videoCaptureStop(); 3188 3213 pDisplay->mParent->i_machine()->COMSETTER(VideoCaptureEnabled)(false); 3189 3214 break; … … 4287 4312 AutoWriteLock displayLock(pThis->pDisplay COMMA_LOCKVAL_SRC_POS); 4288 4313 #ifdef VBOX_WITH_VIDEOREC 4289 pThis->pDisplay->i_ VideoCaptureStop();4314 pThis->pDisplay->i_videoCaptureStop(); 4290 4315 #endif 4291 4316 #ifdef VBOX_WITH_CRHGSMI … … 4405 4430 if (fEnabled) 4406 4431 { 4407 rc = pDisplay->i_ VideoCaptureStart();4432 rc = pDisplay->i_videoCaptureStart(); 4408 4433 fireVideoCaptureChangedEvent(pDisplay->mParent->i_getEventSource()); 4409 4434 } -
trunk/src/VBox/Main/src-client/DrvAudioVideoRec.cpp
r65391 r65410 16 16 */ 17 17 18 /** 19 * This driver is part of Main and is responsible for providing audio 20 * data to Main's video capturing feature. 21 * 22 * The driver itself implements a PDM host audio backend, which in turn 23 * provides the driver with the required audio data and audio events. 24 * 25 * For now there is support for the following destinations (called "sinks"): 26 * 27 * - Direct writing of .webm files to the host. 28 * - Communicating with Main via the Console object to send the encoded audio data to. 29 * The Console object in turn then will route the data to the Display / video capturing interface then. 30 */ 18 31 19 32 /********************************************************************************************************************************* … … 48 61 49 62 /** 50 * Enumeration for audio/video recording driver recording mode. 51 */ 52 typedef 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. 63 * Enumeration for specifying the recording container type. 64 */ 65 typedef enum AVRECCONTAINERTYPE 66 { 67 /** Unknown / invalid container type. */ 68 AVRECCONTAINERTYPE_UNKNOWN = 0, 69 /** Recorded data goes to Main / Console. */ 70 AVRECCONTAINERTYPE_MAIN_CONSOLE = 1, 71 /** Recorded data will be written a .webm file. */ 72 AVRECCONTAINERTYPE_WEBM = 2 73 } AVRECCONTAINERTYPE; 74 75 /** 76 * Structure for keeping generic container parameters. 77 */ 78 typedef struct AVRECCONTAINERPARMS 79 { 80 /** The container's type. */ 81 AVRECCONTAINERTYPE enmType; 82 83 } AVRECCONTAINERPARMS, *PAVRECCONTAINERPARMS; 84 85 /** 86 * Structure for keeping container-specific data. 87 */ 88 typedef struct AVRECCONTAINER 89 { 90 /** Generic container parameters. */ 91 AVRECCONTAINERPARMS Parms; 92 93 union 94 { 95 struct 96 { 97 /** Pointer to Console. */ 98 Console *pConsole; 99 } Main; 100 101 struct 102 { 103 /** Pointer to WebM container to write recorded audio data to. 104 * See the AVRECMODE enumeration for more information. */ 105 WebMWriter *pWebM; 106 /** Assigned track number from WebM container. */ 107 uint8_t uTrack; 108 } WebM; 109 }; 110 } AVRECCONTAINER, *PAVRECCONTAINER; 111 112 /** 113 * Structure for keeping generic codec parameters. 114 */ 115 typedef struct AVRECCODECPARMS 116 { 117 /** The encoding rate to use. */ 118 uint32_t uHz; 119 /** Duration of the frame in samples (per channel). 120 * 121 * For Opus, valid frame size are: 122 * ms Frame size 123 * 2.5 120 124 * 5 240 125 * 10 480 126 * 20 (Default) 960 127 * 40 1920 128 * 60 2880 129 */ 130 /** Number of audio channels to encode. 131 * Currently we only supported stereo (2) channels. */ 132 uint8_t cChannels; 133 /** The codec's bitrate. 0 if not used / cannot be specified. */ 134 uint32_t uBitrate; 135 136 } AVRECCODECPARMS, *PAVRECCODECPARMS; 137 138 /** 139 * Structure for keeping codec-specific data. 68 140 */ 69 141 typedef struct AVRECCODEC 70 142 { 143 /** Generic codec parameters. */ 144 AVRECCODECPARMS Parms; 71 145 union 72 146 { … … 76 150 /** Encoder we're going to use. */ 77 151 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 size84 * 2.5 12085 * 5 24086 * 10 48087 * 20 (Default) 96088 * 40 192089 * 60 288090 */91 152 uint32_t csFrame; 92 153 /** The maximum frame size (in samples) we can handle. */ 93 154 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 # endif100 155 } Opus; 101 156 #endif /* VBOX_WITH_LIBOPUS */ 102 157 }; 158 159 #ifdef VBOX_WITH_STATISTICS /** @todo Make these real STAM values. */ 160 struct 161 { 162 /** Number of frames encoded. */ 163 uint64_t cEncFrames; 164 /** Total time (in ms) of already encoded audio data. */ 165 uint64_t msEncTotal; 166 } STAM; 167 #endif /* VBOX_WITH_STATISTICS */ 168 103 169 } AVRECCODEC, *PAVRECCODEC; 170 171 typedef struct AVRECSINK 172 { 173 /** @todo Add types for container / codec as soon as we implement more stuff. */ 174 175 /** Container data to use for data processing. */ 176 AVRECCONTAINER Con; 177 /** Codec data this stream uses for encoding. */ 178 AVRECCODEC Codec; 179 } AVRECSINK, *PAVRECSINK; 104 180 105 181 /** … … 112 188 /** The PCM properties of this stream. */ 113 189 PDMAUDIOPCMPROPS Props; 114 /** Codec-specific data.115 * As every stream can be different, one codec per stream is needed. */116 AVRECCODEC Codec;117 190 /** (Audio) frame buffer. */ 118 191 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; 192 /** Pointer to sink to use for writing. */ 193 PAVRECSINK pSink; 124 194 } AVRECSTREAMOUT, *PAVRECSTREAMOUT; 125 195 … … 135 205 /** Pointer to host audio interface. */ 136 206 PDMIHOSTAUDIO IHostAudio; 207 /** Pointer to the console object. */ 208 ComObjPtr<Console> pConsole; 137 209 /** Pointer to the DrvAudio port interface that is above us. */ 138 210 PPDMIAUDIOCONNECTOR pDrvAudio; 139 /** Recording mode. */140 AVREC MODE enmMode;211 /** The driver's sink for writing output to. */ 212 AVRECSINK Sink; 141 213 } DRVAUDIOVIDEOREC, *PDRVAUDIOVIDEOREC; 142 214 … … 145 217 ( (PDRVAUDIOVIDEOREC)((uintptr_t)pInterface - RT_OFFSETOF(DRVAUDIOVIDEOREC, IHostAudio)) ) 146 218 147 148 static 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; 219 /** 220 * Initializes a recording sink. 221 * 222 * @returns IPRT status code. 223 * @param pThis Driver instance. 224 * @param pSink Sink to initialize. 225 * @param pConParms Container parameters to set. 226 * @param pCodecParms Codec parameters to set. 227 */ 228 static int avRecSinkInit(PDRVAUDIOVIDEOREC pThis, PAVRECSINK pSink, PAVRECCONTAINERPARMS pConParms, PAVRECCODECPARMS pCodecParms) 229 { 230 uint32_t uHz = pCodecParms->uHz; 166 231 167 232 /* Opus only supports certain input sample rates in an efficient manner. … … 173 238 else uHz = 8000; 174 239 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) 240 OpusEncoder *pEnc = NULL; 241 242 int orc; 243 pEnc = opus_encoder_create(pCodecParms->uHz, pCodecParms->cChannels, OPUS_APPLICATION_AUDIO, &orc); 244 if (orc != OPUS_OK) 245 { 246 LogRel(("VideoRec: Audio codec failed to initialize: %s\n", opus_strerror(orc))); 247 return VERR_AUDIO_BACKEND_INIT_FAILED; 248 } 249 250 AssertPtr(pEnc); 251 252 opus_encoder_ctl(pEnc, OPUS_SET_BITRATE(pCodecParms->uBitrate)); 253 if (orc != OPUS_OK) 254 { 255 LogRel(("VideoRec: Audio codec failed to set bitrate (%RU32): %s\n", pCodecParms->uBitrate, opus_strerror(orc))); 256 return VERR_AUDIO_BACKEND_INIT_FAILED; 257 } 258 259 LogRel(("VideoRec: Recording audio in %RU16Hz, %RU8 channels, %RU32 bpS\n", 260 pCodecParms->uHz, pCodecParms->cChannels, pCodecParms->uBitrate / 1000)); 261 262 int rc; 263 264 try 265 { 266 switch (pConParms->enmType) 267 { 268 case AVRECCONTAINERTYPE_MAIN_CONSOLE: 228 269 { 229 /* Make sure to let the driver backend know that we need the audio data in230 * a specific sampling rate Opus is optimized for. */ 231 pCfgAcq->uHz = pStreamOut->Codec.Opus.uHz;232 pCfgAcq->cSampleBufferSize = _4K; /** @todo Make this configurable. */270 pSink->Con.Main.pConsole = pThis->pConsole; 271 272 rc = VINF_SUCCESS; 273 break; 233 274 } 234 275 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 248 static 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 */ 287 static 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: 276 case AVRECCONTAINERTYPE_WEBM: 306 277 { 307 278 rc = VINF_SUCCESS; … … 309 280 } 310 281 311 case AVRECMODE_AUDIO_VIDEO:312 {313 rc = VERR_NOT_SUPPORTED;314 break;315 }316 317 282 default: 318 283 rc = VERR_NOT_SUPPORTED; … … 325 290 } 326 291 292 if (RT_SUCCESS(rc)) 293 { 294 pSink->Codec.Parms.uHz = uHz; 295 pSink->Codec.Parms.cChannels = pCodecParms->cChannels; 296 pSink->Codec.Parms.uBitrate = pCodecParms->uBitrate; 297 298 pSink->Codec.Opus.pEnc = pEnc; 299 pSink->Codec.Opus.csFrame = uHz / 50; 300 301 #ifdef VBOX_WITH_STATISTICS 302 pSink->Codec.STAM.cEncFrames = 0; 303 pSink->Codec.STAM.msEncTotal = 0; 304 #endif 305 306 /* Calculate the maximum frame size. */ 307 pSink->Codec.Opus.csFrameMax = 48000 /* Maximum sample rate Opus can handle */ 308 * pCodecParms->cChannels; /* Number of channels */ 309 } 310 311 return rc; 312 } 313 314 315 /** 316 * Shuts down (closes) a recording sink, 317 * 318 * @returns IPRT status code. 319 * @param pSink Recording sink to shut down. 320 */ 321 static void avRecSinkShutdown(PAVRECSINK pSink) 322 { 323 AssertPtrReturnVoid(pSink); 324 325 #ifdef VBOX_WITH_LIBOPUS 326 if (pSink->Codec.Opus.pEnc) 327 { 328 opus_encoder_destroy(pSink->Codec.Opus.pEnc); 329 pSink->Codec.Opus.pEnc = NULL; 330 } 331 #endif 332 switch (pSink->Con.Parms.enmType) 333 { 334 case AVRECCONTAINERTYPE_WEBM: 335 { 336 if (pSink->Con.WebM.pWebM) 337 { 338 int rc2 = pSink->Con.WebM.pWebM->Close(); 339 AssertRC(rc2); 340 341 delete pSink->Con.WebM.pWebM; 342 pSink->Con.WebM.pWebM = NULL; 343 } 344 break; 345 } 346 347 case AVRECCONTAINERTYPE_MAIN_CONSOLE: 348 default: 349 break; 350 } 351 } 352 353 354 /** 355 * Creates an audio output stream and associates it with the specified recording sink. 356 * 357 * @returns IPRT status code. 358 * @param pThis Driver instance. 359 * @param pStream Audio output stream to create. 360 * @param pSink Recording sink to associate audio output stream to. 361 * @param pCfgReq Requested configuration by the audio backend. 362 * @param pCfgAcq Acquired configuration by the audio output stream. 363 */ 364 static int avRecCreateStreamOut(PDRVAUDIOVIDEOREC pThis, PPDMAUDIOSTREAM pStream, 365 PAVRECSINK pSink, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq) 366 { 367 AssertPtrReturn(pThis, VERR_INVALID_POINTER); 368 AssertPtrReturn(pStream, VERR_INVALID_POINTER); 369 AssertPtrReturn(pSink, VERR_INVALID_POINTER); 370 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER); 371 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER); 372 373 PAVRECSTREAMOUT pStreamOut = (PAVRECSTREAMOUT)pStream; 374 375 if (pCfgReq->DestSource.Dest != PDMAUDIOPLAYBACKDEST_FRONT) 376 { 377 AssertFailed(); 378 379 if (pCfgAcq) 380 pCfgAcq->cSampleBufferSize = 0; 381 382 LogRel2(("VideoRec: Support for surround audio not implemented yet\n")); 383 return VERR_NOT_SUPPORTED; 384 } 385 386 int rc = DrvAudioHlpStreamCfgToProps(pCfgReq, &pStreamOut->Props); 387 if (RT_FAILURE(rc)) 388 return rc; 389 390 #ifdef VBOX_WITH_LIBOPUS 391 /* If we only record audio, create our own WebM writer instance here. */ 392 if (pSink->Con.Parms.enmType == AVRECCONTAINERTYPE_WEBM) 393 { 394 pSink->Con.WebM.pWebM = new WebMWriter(); 395 rc = pSink->Con.WebM.pWebM->Create("/tmp/acap.webm", RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE, /** @todo Fix path! */ 396 WebMWriter::AudioCodec_Opus, WebMWriter::VideoCodec_None); 397 if (RT_SUCCESS(rc)) 398 rc = pSink->Con.WebM.pWebM->AddAudioTrack(pSink->Codec.Parms.uHz, pStreamOut->Props.cChannels, pStreamOut->Props.cBits, 399 &pSink->Con.WebM.uTrack); 400 } 401 402 if (RT_FAILURE(rc)) 403 return rc; 404 405 rc = RTCircBufCreate(&pStreamOut->pCircBuf, (pSink->Codec.Opus.csFrame * pSink->Codec.Parms.cChannels) * sizeof(uint16_t)); 406 if (RT_SUCCESS(rc)) 407 { 408 pStreamOut->pSink = pSink; /* Assign sink to stream. */ 409 410 if (pCfgAcq) 411 { 412 /* Make sure to let the driver backend know that we need the audio data in 413 * a specific sampling rate Opus is optimized for. */ 414 pCfgAcq->uHz = pSink->Codec.Parms.uHz; 415 pCfgAcq->cSampleBufferSize = _4K; /** @todo Make this configurable. */ 416 } 417 } 418 #else 419 RT_NOREF(pThis, pSink, pStream, pCfgReq, pCfgAcq); 420 rc = VERR_NOT_SUPPORTED; 421 #endif /* VBOX_WITH_LIBOPUS */ 422 423 LogFlowFuncLeaveRC(rc); 424 return rc; 425 } 426 427 428 /** 429 * Destroys (closes) an audio output stream. 430 * 431 * @returns IPRT status code. 432 * @param pThis Driver instance. 433 * @param pStream Audio output stream to destroy. 434 */ 435 static int avRecDestroyStreamOut(PDRVAUDIOVIDEOREC pThis, PPDMAUDIOSTREAM pStream) 436 { 437 RT_NOREF(pThis); 438 PAVRECSTREAMOUT pStreamOut = (PAVRECSTREAMOUT)pStream; 439 440 if (pStreamOut->pCircBuf) 441 { 442 RTCircBufDestroy(pStreamOut->pCircBuf); 443 pStreamOut->pCircBuf = NULL; 444 } 445 446 return VINF_SUCCESS; 447 } 448 449 450 /** 451 * Controls an audio output stream 452 * 453 * @returns IPRT status code. 454 * @param pThis Driver instance. 455 * @param pStream Audio output stream to control. 456 * @param enmStreamCmd Stream command to issue. 457 */ 458 static int avRecControlStreamOut(PDRVAUDIOVIDEOREC pThis, 459 PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd) 460 { 461 AssertPtrReturn(pThis, VERR_INVALID_POINTER); 462 AssertPtrReturn(pStream, VERR_INVALID_POINTER); 463 RT_NOREF(enmStreamCmd); 464 465 RT_NOREF(pThis); 466 467 LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd)); 468 469 switch (enmStreamCmd) 470 { 471 case PDMAUDIOSTREAMCMD_ENABLE: 472 case PDMAUDIOSTREAMCMD_RESUME: 473 break; 474 475 case PDMAUDIOSTREAMCMD_DISABLE: 476 { 477 AudioMixBufReset(&pStream->MixBuf); 478 break; 479 } 480 481 case PDMAUDIOSTREAMCMD_PAUSE: 482 break; 483 484 default: 485 AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd)); 486 break; 487 } 488 489 return VINF_SUCCESS; 490 } 491 492 493 /** 494 * @interface_method_impl{PDMIHOSTAUDIO,pfnInit} 495 */ 496 static DECLCALLBACK(int) drvAudioVideoRecInit(PPDMIHOSTAUDIO pInterface) 497 { 498 AssertPtrReturn(pInterface, VERR_INVALID_POINTER); 499 500 LogFlowFuncEnter(); 501 502 PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface); 503 504 AVRECCONTAINERPARMS ContainerParms; 505 ContainerParms.enmType = AVRECCONTAINERTYPE_MAIN_CONSOLE; /** @todo Make this configurable. */ 506 507 AVRECCODECPARMS CodecParms; 508 CodecParms.uHz = 48000; /** @todo Make this configurable. */ 509 CodecParms.cChannels = 2; /** @todo Make this configurable. */ 510 CodecParms.uBitrate = 196000; /** @todo Make this configurable. */ 511 512 int rc = avRecSinkInit(pThis, &pThis->Sink, &ContainerParms, &CodecParms); 327 513 if (RT_FAILURE(rc)) 328 514 { … … 385 571 */ 386 572 #ifdef VBOX_WITH_LIBOPUS 573 PAVRECSINK pSink = pStreamOut->pSink; 574 AssertPtr(pSink); 387 575 PRTCIRCBUF pCircBuf = pStreamOut->pCircBuf; 576 AssertPtr(pCircBuf); 388 577 389 578 void *pvBuf; … … 430 619 size_t cbSrc; 431 620 432 const uint32_t csFrame = pS treamOut->Codec.Opus.csFrame;621 const uint32_t csFrame = pSink->Codec.Opus.csFrame; 433 622 const uint32_t cbFrame = AUDIOMIXBUF_S2B(&pStream->MixBuf, csFrame); 434 623 … … 455 644 } 456 645 457 # ifdef DEBUG_andy646 # ifdef DEBUG_andy 458 647 RTFILE fh; 459 648 RTFileOpen(&fh, "/tmp/acap.pcm", RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE); 460 649 RTFileWrite(fh, abSrc, cbSrc, NULL); 461 650 RTFileClose(fh); 462 # endif651 # endif 463 652 464 653 Assert(cbSrc == cbFrame); … … 475 664 476 665 /* Call the encoder to encode one frame per iteration. */ 477 opus_int32 cbWritten = opus_encode(pS treamOut->Codec.Opus.pEnc,666 opus_int32 cbWritten = opus_encode(pSink->Codec.Opus.pEnc, 478 667 (opus_int16 *)abSrc, csFrame, abDst, cbDst); 479 668 if (cbWritten > 0) 480 669 { 481 # ifdef DEBUG670 # ifdef VBOX_WITH_STATISTICS 482 671 /* Get overall frames encoded. */ 483 672 uint32_t cEncFrames = opus_packet_get_nb_frames(abDst, cbDst); 484 uint32_t cEncSamplesPerFrame = opus_packet_get_samples_per_frame(abDst, pS treamOut->Codec.Opus.uHz);673 uint32_t cEncSamplesPerFrame = opus_packet_get_samples_per_frame(abDst, pSink->Codec.Parms.uHz); 485 674 uint32_t csEnc = cEncFrames * cEncSamplesPerFrame; 486 675 487 pS treamOut->Codec.Opus.cEncFrames += cEncFrames;488 pS treamOut->Codec.Opus.msEncTotal += 20 /* Default 20 ms */ * cEncFrames;676 pSink->Codec.STAM.cEncFrames += cEncFrames; 677 pSink->Codec.STAM.msEncTotal += 20 /* Default 20 ms */ * cEncFrames; 489 678 490 679 LogFunc(("%RU64ms [%RU64 frames]: cbSrc=%zu, cbDst=%zu, cEncFrames=%RU32, cEncSamplesPerFrame=%RU32, csEnc=%RU32\n", 491 pS treamOut->Codec.Opus.msEncTotal, pStreamOut->Codec.Opus.cEncFrames,680 pSink->Codec.STAM.msEncTotal, pSink->Codec.STAM.cEncFrames, 492 681 cbSrc, cbDst, cEncFrames, cEncSamplesPerFrame, csEnc)); 493 # endif682 # endif 494 683 Assert((uint32_t)cbWritten <= cbDst); 495 684 cbDst = RT_MIN((uint32_t)cbWritten, cbDst); /* Update cbDst to actual bytes encoded (written). */ 496 685 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); 686 switch (pSink->Con.Parms.enmType) 687 { 688 case AVRECCONTAINERTYPE_MAIN_CONSOLE: 689 { 690 HRESULT hr = pSink->Con.Main.pConsole->i_audioVideoRecSendAudio(abDst, cbDst, RTTimeMilliTS() /* Now */); 691 Assert(hr == S_OK); 692 693 break; 694 } 695 696 case AVRECCONTAINERTYPE_WEBM: 697 { 698 WebMWriter::BlockData_Opus blockData = { abDst, cbDst }; 699 rc = pSink->Con.WebM.pWebM->WriteBlock(pSink->Con.WebM.uTrack, &blockData, sizeof(blockData)); 700 AssertRC(rc); 701 702 break; 703 } 704 705 default: 706 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED); 707 break; 708 } 501 709 } 502 710 else if (cbWritten < 0) … … 525 733 526 734 527 static 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_LIBOPUS540 if (pStreamOut->Codec.Opus.pEnc)541 {542 opus_encoder_destroy(pStreamOut->Codec.Opus.pEnc);543 pStreamOut->Codec.Opus.pEnc = NULL;544 }545 #endif546 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 735 /** 566 736 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig} … … 589 759 PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface); 590 760 591 switch (pThis->enmMode) 592 { 593 case AVRECMODE_AUDIO: 594 case AVRECMODE_AUDIO_VIDEO: 595 default: 596 break; 597 } 761 avRecSinkShutdown(&pThis->Sink); 598 762 } 599 763 … … 622 786 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER); 623 787 788 PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface); 789 790 /* For now we only have one sink, namely the driver's one. 791 * Later each stream could have its own one, to e.g. router different stream to different sinks .*/ 792 PAVRECSINK pSink = &pThis->Sink; 793 624 794 if (pCfgReq->enmDir == PDMAUDIODIR_OUT) 625 return avRecCreateStreamOut(p Interface, pStream, pCfgReq, pCfgAcq);795 return avRecCreateStreamOut(pThis, pStream, pSink, pCfgReq, pCfgAcq); 626 796 627 797 return VERR_NOT_SUPPORTED; … … 637 807 AssertPtrReturn(pStream, VERR_INVALID_POINTER); 638 808 809 PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface); 810 639 811 if (pStream->enmDir == PDMAUDIODIR_OUT) 640 return avRecDestroyStreamOut(p Interface, pStream);812 return avRecDestroyStreamOut(pThis, pStream); 641 813 642 814 return VINF_SUCCESS; … … 653 825 AssertPtrReturn(pStream, VERR_INVALID_POINTER); 654 826 827 PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface); 828 655 829 Assert(pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST); 656 830 657 831 if (pStream->enmDir == PDMAUDIODIR_OUT) 658 return avRecControlStreamOut(p Interface, pStream, enmStreamCmd);832 return avRecControlStreamOut(pThis, pStream, enmStreamCmd); 659 833 660 834 return VINF_SUCCESS; … … 705 879 706 880 AudioVideoRec::AudioVideoRec(Console *pConsole) 707 : mpDrv(NULL) ,708 mParent(pConsole)881 : mpDrv(NULL) 882 , mpConsole(pConsole) 709 883 { 710 884 } … … 754 928 755 929 /* 756 * Get the AudioVideoRecobject pointer.930 * Get the Console object pointer. 757 931 */ 758 void *pvUser = NULL; 759 int rc = CFGMR3QueryPtr(pCfg, "Object", &pvUser); /** @todo r=andy Get rid of this hack and use IHostAudio::SetCallback. */ 932 void *pvUser; 933 int rc = CFGMR3QueryPtr(pCfg, "ObjectConsole", &pvUser); /** @todo r=andy Get rid of this hack and use IHostAudio::SetCallback. */ 934 AssertMsgRCReturn(rc, ("Confguration error: No/bad \"ObjectConsole\" value, rc=%Rrc\n", rc), rc); 935 936 /* CFGM tree saves the pointer to Console in the Object node of AudioVideoRec. */ 937 pThis->pConsole = (Console *)pvUser; 938 939 /* 940 * Get the pointer to the audio driver instance. 941 */ 942 rc = CFGMR3QueryPtr(pCfg, "Object", &pvUser); /** @todo r=andy Get rid of this hack and use IHostAudio::SetCallback. */ 760 943 AssertMsgRCReturn(rc, ("Confguration error: No/bad \"Object\" value, rc=%Rrc\n", rc), rc); 761 944 -
trunk/src/VBox/Main/src-client/VideoRec.cpp
r65402 r65410 1041 1041 } 1042 1042 1043 int VideoRecSendAudio(PVIDEORECCONTEXT pCtx, const void *pvData, size_t cbData, uint64_t uTimestampMs) 1044 { 1045 RT_NOREF(pCtx, pvData, cbData, uTimestampMs); 1046 return VINF_SUCCESS; 1047 } 1048 1043 1049 /** 1044 1050 * VideoRec utility function to copy a source image (FrameBuf) to the intermediate -
trunk/src/VBox/Main/src-client/VideoRec.h
r65401 r65410 33 33 34 34 bool VideoRecIsEnabled(PVIDEORECCONTEXT pCtx); 35 int VideoRecSendAudio(PVIDEORECCONTEXT pCtx, const void *pvData, size_t cbData, uint64_t uTimestampMs); 35 36 int VideoRecCopyToIntBuf(PVIDEORECCONTEXT pCtx, uint32_t uScreen, 36 37 uint32_t x, uint32_t y, uint32_t uPixelFormat, uint32_t uBitsPerPixel,
Note:
See TracChangeset
for help on using the changeset viewer.