Changeset 65197 in vbox for trunk/src/VBox/Main
- Timestamp:
- Jan 9, 2017 11:40:46 AM (8 years ago)
- Location:
- trunk/src/VBox/Main
- Files:
-
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/include/DrvAudioVideoRec.h
r65162 r65197 1 1 /* $Id$ */ 2 2 /** @file 3 * VirtualBox driver interface to video recordingbackend.3 * VirtualBox driver interface video recording audio backend. 4 4 */ 5 5 6 6 /* 7 * Copyright (C) 2014-201 6Oracle Corporation7 * Copyright (C) 2014-2017 Oracle Corporation 8 8 * 9 9 * This file is part of VirtualBox Open Source Edition (OSE), as -
trunk/src/VBox/Main/src-client/DrvAudioVideoRec.cpp
r65170 r65197 24 24 #include "DrvAudioVideoRec.h" 25 25 #include "ConsoleImpl.h" 26 #include "ConsoleVRDPServer.h"27 26 28 27 #include "Logging.h" … … 30 29 #include "../../Devices/Audio/DrvAudio.h" 31 30 #include "../../Devices/Audio/AudioMixBuffer.h" 31 #include "EbmlWriter.h" 32 32 33 33 #include <iprt/mem.h> … … 40 40 #include <VBox/err.h> 41 41 42 #include <opus.h> 42 43 43 44 /********************************************************************************************************************************* 44 45 * Structures and Typedefs * 45 46 *********************************************************************************************************************************/ 47 48 /** 49 * Enumeration for audio/video recording driver recording mode. 50 */ 51 typedef enum AVRECMODE 52 { 53 /** Unknown / invalid recording mode. */ 54 AVRECMODE_UNKNOWN = 0, 55 /** Only record audio. 56 * This mode does not need to talk to the video recording driver, 57 * as this driver then simply creates an own WebM container. */ 58 AVRECMODE_AUDIO = 1, 59 /** Records audio and video. 60 * Needs to work together with the video recording driver in 61 * order to get a full-featured WebM container. */ 62 AVRECMODE_AUDIO_VIDEO = 2 63 } AVRECMODE; 64 65 /** 66 * Structure for keeping codec specific data. 67 */ 68 typedef struct AVRECCODEC 69 { 70 union 71 { 72 struct 73 { 74 /** Encoder we're going to use. */ 75 OpusEncoder *pEnc; 76 } Opus; 77 }; 78 } AVRECCODEC, *PAVRECCODEC; 79 80 /** 81 * Audio video recording output stream. 82 */ 83 typedef struct AVRECSTREAMOUT 84 { 85 /** Note: Always must come first! */ 86 PDMAUDIOSTREAM Stream; 87 /** The PCM properties of this stream. */ 88 PDMAUDIOPCMPROPS Props; 89 uint64_t old_ticks; 90 uint64_t cSamplesSentPerSec; 91 /** Codec data. 92 * As every stream can be different, one codec per stream is needed. */ 93 AVRECCODEC Codec; 94 } AVRECSTREAMOUT, *PAVRECSTREAMOUT; 95 46 96 /** 47 97 * Video recording audio driver instance data. … … 57 107 /** Pointer to the DrvAudio port interface that is above us. */ 58 108 PPDMIAUDIOCONNECTOR pDrvAudio; 109 /** Recording mode. */ 110 AVRECMODE enmMode; 111 /** Pointer to WebM container to write recorded audio data to. 112 * See the AVRECMODE enumeration for more information. */ 113 WebMWriter *pEBML; 59 114 } DRVAUDIOVIDEOREC, *PDRVAUDIOVIDEOREC; 60 61 typedef struct AVRECSTREAMOUT62 {63 /** Note: Always must come first! */64 PDMAUDIOSTREAM Stream;65 /** The PCM properties of this stream. */66 PDMAUDIOPCMPROPS Props;67 uint64_t old_ticks;68 uint64_t cSamplesSentPerSec;69 } AVRECSTREAMOUT, *PAVRECSTREAMOUT;70 115 71 116 /** Makes DRVAUDIOVIDEOREC out of PDMIHOSTAUDIO. */ … … 87 132 if (RT_SUCCESS(rc)) 88 133 { 89 if (pCfgAcq) 90 pCfgAcq->cSampleBufferSize = _4K; /** @todo Make this configurable. */ 134 OpusEncoder *pEnc = NULL; 135 136 int orc; 137 pEnc = opus_encoder_create(48000 /* Hz */, 2 /* Stereo */, OPUS_APPLICATION_AUDIO, &orc); 138 if (orc != OPUS_OK) 139 { 140 LogRel(("VideoRec: Audio codec failed to initialize: %s\n", opus_strerror(orc))); 141 return VERR_AUDIO_BACKEND_INIT_FAILED; 142 } 143 144 AssertPtr(pEnc); 145 146 orc = opus_encoder_ctl(pEnc, OPUS_SET_BITRATE(pCfgReq->uHz)); 147 if (orc != OPUS_OK) 148 { 149 LogRel(("VideoRec: Audio codec failed to set bitrate: %s\n", opus_strerror(orc))); 150 rc = VERR_AUDIO_BACKEND_INIT_FAILED; 151 } 152 else 153 { 154 pStreamOut->Codec.Opus.pEnc = pEnc; 155 156 if (pCfgAcq) 157 pCfgAcq->cSampleBufferSize = _4K; /** @todo Make this configurable. */ 158 } 91 159 } 92 160 … … 119 187 static DECLCALLBACK(int) drvAudioVideoRecInit(PPDMIHOSTAUDIO pInterface) 120 188 { 121 RT_NOREF(pInterface); 189 AssertPtrReturn(pInterface, VERR_INVALID_POINTER); 190 122 191 LogFlowFuncEnter(); 123 192 124 return VINF_SUCCESS; 193 PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface); 194 195 pThis->enmMode = AVRECMODE_AUDIO; 196 197 int rc; 198 199 try 200 { 201 switch (pThis->enmMode) 202 { 203 case AVRECMODE_AUDIO: 204 { 205 pThis->pEBML = new WebMWriter(); 206 pThis->pEBML->create("/tmp/test.webm", WebMWriter::Mode_Audio); /** @todo FIX! */ 207 break; 208 } 209 210 case AVRECMODE_AUDIO_VIDEO: 211 { 212 break; 213 } 214 215 default: 216 rc = VERR_NOT_SUPPORTED; 217 break; 218 } 219 } 220 catch (std::bad_alloc) 221 { 222 rc = VERR_NO_MEMORY; 223 } 224 225 if (RT_FAILURE(rc)) 226 { 227 LogRel(("VideoRec: Audio recording driver failed to initialize, rc=%Rrc\n", rc)); 228 } 229 else 230 LogRel2(("VideoRec: Audio recording driver initialized\n")); 231 232 return rc; 125 233 } 126 234 … … 179 287 180 288 /* 181 * Call the VRDPserver with the data.289 * Call the encoder server with the data. 182 290 */ 183 291 uint32_t cReadTotal = 0; 292 293 uint8_t pvDst[_4K]; 294 opus_int32 cbDst = _4K; 184 295 185 296 PPDMAUDIOSAMPLE pSamples; … … 192 303 cReadTotal = cRead; 193 304 305 opus_int32 orc = opus_encode(pStreamOut->Codec.Opus.pEnc, (opus_int16 *)pSamples, cRead, pvDst, cbDst); 306 if (orc != OPUS_OK) 307 LogFunc(("Encoding (1) failed: %s\n", opus_strerror(orc))); 308 194 309 if (rc == VINF_TRY_AGAIN) 195 310 { … … 197 312 &pSamples, &cRead); 198 313 314 315 199 316 cReadTotal += cRead; 200 317 } … … 205 322 /* 206 323 * Always report back all samples acquired, regardless of whether the 207 * VRDP server actually did process those.324 * encoder actually did process those. 208 325 */ 209 326 if (pcbWritten) … … 215 332 216 333 217 static int avRecDestroyStreamIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)218 {219 RT_NOREF(pInterface, pStream);220 221 return VINF_SUCCESS;222 }223 224 225 334 static int avRecDestroyStreamOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream) 226 335 { 227 RT_NOREF(pInterface); 228 RT_NOREF(pStream); 336 PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface); 337 RT_NOREF(pThis); 338 PAVRECSTREAMOUT pStreamOut = (PAVRECSTREAMOUT)pStream; 339 340 if (pStreamOut->Codec.Opus.pEnc) 341 { 342 opus_encoder_destroy(pStreamOut->Codec.Opus.pEnc); 343 pStreamOut->Codec.Opus.pEnc = NULL; 344 } 229 345 230 346 return VINF_SUCCESS; … … 254 370 static DECLCALLBACK(void) drvAudioVideoRecShutdown(PPDMIHOSTAUDIO pInterface) 255 371 { 256 RT_NOREF(pInterface); 372 LogFlowFuncEnter(); 373 374 PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface); 375 376 int rc; 377 378 switch (pThis->enmMode) 379 { 380 case AVRECMODE_AUDIO: 381 { 382 if (pThis->pEBML) 383 { 384 rc = pThis->pEBML->writeFooter(0 /* Hash */); 385 AssertRC(rc); 386 387 pThis->pEBML->close(); 388 389 delete pThis->pEBML; 390 pThis->pEBML = NULL; 391 } 392 break; 393 } 394 395 case AVRECMODE_AUDIO_VIDEO: 396 { 397 break; 398 } 399 400 default: 401 rc = VERR_NOT_SUPPORTED; 402 break; 403 } 404 405 LogFlowFuncLeaveRC(rc); 257 406 } 258 407 … … 296 445 AssertPtrReturn(pStream, VERR_INVALID_POINTER); 297 446 298 int rc; 299 if (pStream->enmDir == PDMAUDIODIR_IN) 300 rc = avRecDestroyStreamIn(pInterface, pStream); 301 else 302 rc = avRecDestroyStreamOut(pInterface, pStream); 303 304 return rc; 447 if (pStream->enmDir == PDMAUDIODIR_OUT) 448 return avRecDestroyStreamOut(pInterface, pStream); 449 450 return VINF_SUCCESS; 305 451 } 306 452 … … 399 545 AssertPtrReturn(pCfg, VERR_INVALID_POINTER); 400 546 401 LogRel(("Audio: Initializing video recording driver\n"));547 LogRel(("Audio: Initializing video recording audio driver\n")); 402 548 LogFlowFunc(("fFlags=0x%x\n", fFlags)); 403 549 -
trunk/src/VBox/Main/src-client/EbmlWriter.cpp
r63160 r65197 5 5 6 6 /* 7 * Copyright (C) 2013-201 6Oracle Corporation7 * Copyright (C) 2013-2017 Oracle Corporation 8 8 * 9 9 * This file is part of VirtualBox Open Source Edition (OSE), as … … 220 220 class WebMWriter_Impl 221 221 { 222 223 222 struct CueEntry 224 223 { … … 228 227 }; 229 228 230 bool m_bDebug; 229 /** Operation mode. */ 230 WebMWriter::Mode m_enmMode; 231 232 bool m_fDebug; 231 233 int64_t m_iLastPtsMs; 232 234 int64_t m_iInitialPtsMs; … … 249 251 std::list<CueEntry> m_CueList; 250 252 251 Ebml m_Ebml;253 Ebml m_Ebml; 252 254 253 255 public: 254 256 255 257 WebMWriter_Impl() : 256 m_bDebug(false), 258 m_enmMode(WebMWriter::Mode_Unknown), 259 m_fDebug(false), 257 260 m_iLastPtsMs(-1), 258 261 m_iInitialPtsMs(-1), … … 269 272 m_bClusterOpen(false) {} 270 273 271 void writeHeader(const vpx_codec_enc_cfg_t *a_pCfg, 272 const struct vpx_rational *a_pFps) 274 void writeHeader(const vpx_codec_enc_cfg_t *a_pCfg, const struct vpx_rational *a_pFps) 273 275 { 274 276 m_Ebml.subStart(EBML) … … 291 293 m_uTrackPos = RTFileTell(m_Ebml.getFile()); 292 294 293 m_Ebml.subStart(Tracks) 294 .subStart(TrackEntry) 295 .serializeUnsignedInteger(TrackNumber, 1); 296 297 m_uTrackIdPos = RTFileTell(m_Ebml.getFile()); 298 299 m_Ebml.serializeUnsignedInteger(TrackUID, 0, 4) 300 .serializeUnsignedInteger(TrackType, 1) 301 .serializeString(CodecID, "V_VP8") 302 .subStart(Video) 303 .serializeUnsignedInteger(PixelWidth, a_pCfg->g_w) 304 .serializeUnsignedInteger(PixelHeight, a_pCfg->g_h) 305 .serializeFloat(FrameRate, (double) a_pFps->num / a_pFps->den) 306 .subEnd(Video) 307 .subEnd(TrackEntry) 308 .subEnd(Tracks); 309 } 310 311 void writeBlock(const vpx_codec_enc_cfg_t *a_pCfg, 312 const vpx_codec_cx_pkt_t *a_pPkt) 295 m_Ebml.subStart(Tracks); 296 297 /* Write video? */ 298 if ( m_enmMode == WebMWriter::Mode_Video 299 || m_enmMode == WebMWriter::Mode_AudioVideo) 300 { 301 /* 302 * Video track. 303 */ 304 m_Ebml.subStart(TrackEntry); 305 m_Ebml.serializeUnsignedInteger(TrackNumber, 1); 306 307 m_uTrackIdPos = RTFileTell(m_Ebml.getFile()); 308 309 m_Ebml.serializeUnsignedInteger(TrackUID, 0 /* UID */, 4) 310 .serializeUnsignedInteger(TrackType, 1 /* Video */) 311 .serializeString(CodecID, "V_VP8") 312 .subStart(Video) 313 .serializeUnsignedInteger(PixelWidth, a_pCfg->g_w) 314 .serializeUnsignedInteger(PixelHeight, a_pCfg->g_h) 315 .serializeFloat(FrameRate, (double) a_pFps->num / a_pFps->den) 316 .subEnd(Video) 317 .subEnd(TrackEntry); 318 } 319 320 #ifdef VBOX_WITH_AUDIO_VIDEOREC 321 if ( m_enmMode == WebMWriter::Mode_Audio 322 || m_enmMode == WebMWriter::Mode_AudioVideo) 323 { 324 /* 325 * Audio track. 326 */ 327 m_Ebml.subStart(TrackEntry); 328 m_Ebml.serializeUnsignedInteger(TrackNumber, 2); 329 /** @todo Implement track's "Language" property? Currently this defaults to English ("eng"). */ 330 331 m_Ebml.serializeUnsignedInteger(TrackUID, 1 /* UID */, 4) 332 .serializeUnsignedInteger(TrackType, 2 /* Audio */) 333 .serializeString(CodecID, "A_OPUS") 334 .subStart(Audio) 335 .serializeFloat(SamplingFrequency, 44100.0) 336 .serializeFloat(OutputSamplingFrequency, 44100.0) 337 .serializeUnsignedInteger(Channels, 2) 338 .serializeUnsignedInteger(BitDepth, 16) 339 .subEnd(Audio) 340 .subEnd(TrackEntry); 341 } 342 #endif 343 344 m_Ebml.subEnd(Tracks); 345 } 346 347 void writeBlock(const vpx_codec_enc_cfg_t *a_pCfg, const vpx_codec_cx_pkt_t *a_pPkt) 313 348 { 314 349 uint16_t uBlockTimecode = 0; … … 316 351 bool bStartCluster = false; 317 352 318 /* Calculate the PTS of this frame in milliseconds */353 /* Calculate the PTS of this frame in milliseconds. */ 319 354 iPtsMs = a_pPkt->data.frame.pts * 1000 320 355 * (uint64_t) a_pCfg->g_timebase.num / a_pCfg->g_timebase.den; … … 326 361 m_iInitialPtsMs = m_iLastPtsMs; 327 362 328 /* Calculate the relative time of this block */363 /* Calculate the relative time of this block. */ 329 364 if (iPtsMs - m_uClusterTimecode > 65536) 330 365 bStartCluster = 1; … … 338 373 m_Ebml.subEnd(Cluster); 339 374 340 /* Open a new cluster */375 /* Open a new cluster. */ 341 376 uBlockTimecode = 0; 342 377 m_bClusterOpen = true; … … 354 389 } 355 390 356 /* Write a Simple Block*/391 /* Write a "Simple Block". */ 357 392 m_Ebml.writeClassId(SimpleBlock); 358 393 m_Ebml.writeUnsignedInteger(0x10000000u | (4 + a_pPkt->data.frame.sz), 4); … … 389 424 if (!RT_SUCCESS(rc)) throw rc; 390 425 391 m_Ebml.serializeUnsignedInteger(TrackUID, (m_ bDebug ? 0xDEADBEEF : a_u64Hash), 4);426 m_Ebml.serializeUnsignedInteger(TrackUID, (m_fDebug ? 0xDEADBEEF : a_u64Hash), 4); 392 427 393 428 rc = RTFileSeek(m_Ebml.getFile(), 0, RTFILE_SEEK_END, NULL); … … 431 466 char szVersion[64]; 432 467 RTStrPrintf(szVersion, sizeof(szVersion), "vpxenc%s", 433 m_ bDebug ? "" : vpx_codec_version_str());468 m_fDebug ? "" : vpx_codec_version_str()); 434 469 435 470 m_Ebml.subStart(Info) … … 449 484 } 450 485 451 int WebMWriter::create(const char *a_pszFilename) 452 { 486 int WebMWriter::create(const char *a_pszFilename, WebMWriter::Mode a_enmMode) 487 { 488 m_Impl->m_enmMode = a_enmMode; 489 453 490 return m_Impl->m_Ebml.create(a_pszFilename); 454 491 } -
trunk/src/VBox/Main/src-client/EbmlWriter.h
r63160 r65197 5 5 6 6 /* 7 * Copyright (C) 2013-201 6Oracle Corporation7 * Copyright (C) 2013-2017 Oracle Corporation 8 8 * 9 9 * This file is part of VirtualBox Open Source Edition (OSE), as … … 32 32 class WebMWriter 33 33 { 34 34 35 public: 36 37 /** 38 * Operation mode -- this specifies what to write. 39 */ 40 enum Mode 41 { 42 /** Unknown / invalid mode. */ 43 Mode_Unknown = 0, 44 /** Only writes audio. */ 45 Mode_Audio = 1, 46 /** Only Writes video. */ 47 Mode_Video = 2, 48 /** Writes audio and video. */ 49 Mode_AudioVideo = 3 50 }; 51 52 public: 53 35 54 WebMWriter(); 36 55 virtual ~WebMWriter(); 37 56 38 /** Creates output file 57 /** 58 * Creates output file. 39 59 * 40 60 * @param a_pszFilename Name of the file to create. 61 * @param a_enmMode Operation mode. 41 62 * 42 63 * @returns VBox status code. */ 43 int create(const char *a_pszFilename );64 int create(const char *a_pszFilename, WebMWriter::Mode a_enmMode); 44 65 45 66 /* Closes output file. */ 46 67 void close(); 47 68 48 /** Writes WebM header to file.49 * 69 /** 70 * Writes WebM header to file. 50 71 * Should be called before any writeBlock call. 51 72 * … … 57 78 int writeHeader(const vpx_codec_enc_cfg_t *a_pCfg, const vpx_rational *a_pFps); 58 79 59 /** Writes a block of compressed data 80 /** 81 * Writes a block of compressed data. 60 82 * 61 83 * @param a_pCfg Pointer to VPX Codec configuration structure. … … 66 88 int writeBlock(const vpx_codec_enc_cfg_t *a_pCfg, const vpx_codec_cx_pkt_t *a_pPkt); 67 89 68 /** Writes WebM footer.69 * 90 /** 91 * Writes WebM footer. 70 92 * No other write functions should be called after this one. 71 93 * … … 76 98 int writeFooter(uint32_t a_u64Hash); 77 99 78 /** Gets current output file size. 100 /** 101 * Gets current output file size. 79 102 * 80 103 * @returns File size in bytes. 81 104 */ 82 uint64_t getFileSize( );105 uint64_t getFileSize(void); 83 106 84 /** Gets current free storage space85 * available for the file.107 /** 108 * Gets current free storage space available for the file. 86 109 * 87 110 * @returns Available storage free space. 88 111 */ 89 uint64_t getAvailableSpace( );112 uint64_t getAvailableSpace(void); 90 113 91 114 private: 115 92 116 /** WebMWriter implementation. 93 * To isolate some include files*/117 * To isolate some include files. */ 94 118 WebMWriter_Impl *m_Impl; 95 119 … … 97 121 }; 98 122 99 #endif 123 #endif /* ____EBMLWRITER */ -
trunk/src/VBox/Main/src-client/VideoRec.cpp
r65177 r65197 647 647 * other important file, causing unintentional data loss. */ 648 648 649 int rc = pStream->pEBML->create(pszFile );649 int rc = pStream->pEBML->create(pszFile, WebMWriter::Mode_AudioVideo); /** @todo Make mode configurable. */ 650 650 if (RT_FAILURE(rc)) 651 651 {
Note:
See TracChangeset
for help on using the changeset viewer.