Changeset 96229 in vbox for trunk/src/VBox/Main
- Timestamp:
- Aug 16, 2022 3:41:39 PM (2 years ago)
- Location:
- trunk/src/VBox/Main
- Files:
-
- 9 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/include/Recording.h
r95645 r96229 38 38 RecordingContext(); 39 39 40 RecordingContext(Console *ptrConsole, const settings::RecordingSettings & settings);40 RecordingContext(Console *ptrConsole, const settings::RecordingSettings &Settings); 41 41 42 42 virtual ~RecordingContext(void); … … 47 47 RecordingStream *GetStream(unsigned uScreen) const; 48 48 size_t GetStreamCount(void) const; 49 #ifdef VBOX_WITH_AUDIO_RECORDING 50 PRECORDINGCODEC GetCodecAudio(void) { return &this->CodecAudio; } 51 #endif 49 52 50 int Create(Console *pConsole, const settings::RecordingSettings & settings);53 int Create(Console *pConsole, const settings::RecordingSettings &Settings); 51 54 void Destroy(void); 52 55 … … 72 75 protected: 73 76 74 int createInternal(Console *ptrConsole, const settings::RecordingSettings & settings);77 int createInternal(Console *ptrConsole, const settings::RecordingSettings &Settings); 75 78 int startInternal(void); 76 79 int stopInternal(void); … … 80 83 RecordingStream *getStreamInternal(unsigned uScreen) const; 81 84 85 int writeCommonData(PRECORDINGCODEC pCodec, const void *pvData, size_t cbData, uint64_t msAbsPTS, uint32_t uFlags); 86 82 87 int lock(void); 83 88 int unlock(void); … … 86 91 87 92 int threadNotify(void); 93 94 protected: 95 96 int audioInit(const settings::RecordingScreenSettings &screenSettings); 97 98 static DECLCALLBACK(int) audioCodecWriteDataCallback(PRECORDINGCODEC pCodec, const void *pvData, size_t cbData, uint64_t msAbsPTS, uint32_t uFlags, void *pvUser); 88 99 89 100 protected: … … 107 118 Console *pConsole; 108 119 /** Used recording configuration. */ 109 settings::RecordingSettings Settings;120 settings::RecordingSettings m_Settings; 110 121 /** The current state. */ 111 122 RECORDINGSTS enmState; … … 116 127 /** Shutdown indicator. */ 117 128 bool fShutdown; 118 /** Worker thread. */129 /** Encoding worker thread. */ 119 130 RTTHREAD Thread; 120 131 /** Vector of current recording streams. … … 125 136 /** Timestamp (in ms) of when recording has been started. */ 126 137 uint64_t tsStartMs; 138 #ifdef VBOX_WITH_AUDIO_RECORDING 139 /** Audio codec to use. 140 * 141 * We multiplex audio data from this recording context to all streams, 142 * to avoid encoding the same audio data for each stream. We ASSUME that 143 * all audio data of a VM will be the same for each stream at a given 144 * point in time. */ 145 RECORDINGCODEC CodecAudio; 146 #endif /* VBOX_WITH_AUDIO_RECORDING */ 127 147 /** Block map of common blocks which need to get multiplexed 128 148 * to all recording streams. This common block maps should help -
trunk/src/VBox/Main/include/RecordingInternals.h
r96178 r96229 122 122 * @param pCodec Codec instance to use. 123 123 * @param pFrame Pointer to frame data to encode. 124 * @param pvDst Where to store the encoded data on success.125 * @param cbDst Size (in bytes) of \a pvDst.126 124 * @param pcEncoded Where to return the number of encoded blocks in \a pvDst on success. Optional. 127 125 * @param pcbEncoded Where to return the number of encoded bytes in \a pvDst on success. Optional. 128 126 */ 129 DECLCALLBACKMEMBER(int, pfnEncode, (PRECORDINGCODEC pCodec, const PRECORDINGFRAME pFrame, void *pvDst, size_t cbDst,size_t *pcEncoded, size_t *pcbEncoded));127 DECLCALLBACKMEMBER(int, pfnEncode, (PRECORDINGCODEC pCodec, const PRECORDINGFRAME pFrame, size_t *pcEncoded, size_t *pcbEncoded)); 130 128 131 129 /** … … 138 136 } RECORDINGCODECOPS, *PRECORDINGCODECOPS; 139 137 138 /** No encoding flags set. */ 139 #define RECORDINGCODEC_ENC_F_NONE UINT32_C(0) 140 /** Data block is a key block. */ 141 #define RECORDINGCODEC_ENC_F_BLOCK_IS_KEY RT_BIT_32(0) 142 /** Data block is invisible. */ 143 #define RECORDINGCODEC_ENC_F_BLOCK_IS_INVISIBLE RT_BIT_32(1) 144 /** Encoding flags valid mask. */ 145 #define RECORDINGCODEC_ENC_F_VALID_MASK 0x1 146 140 147 /** 141 148 * Structure for keeping a codec callback table. … … 143 150 typedef struct RECORDINGCODECCALLBACKS 144 151 { 145 DECLCALLBACKMEMBER(int, pfnWriteData, (PRECORDINGCODEC pCodec, const void *pvData, size_t cbData, void *pvUser)); 152 /** 153 * Callback for notifying that encoded data has been written. 154 * 155 * @returns VBox status code. 156 * @param pCodec Pointer to codec instance which has written the data. 157 * @param pvData Pointer to written data (encoded). 158 * @param cbData Size (in bytes) of \a pvData. 159 * @param msAbsPTS Absolute PTS (in ms) of the written data. 160 * @param uFlags Encoding flags of type RECORDINGCODEC_ENC_F_XXX. 161 * @param pvUser User-supplied pointer. 162 */ 163 DECLCALLBACKMEMBER(int, pfnWriteData, (PRECORDINGCODEC pCodec, const void *pvData, size_t cbData, uint64_t msAbsPTS, uint32_t uFlags, void *pvUser)); 146 164 /** User-supplied data pointer. */ 147 void 165 void *pvUser; 148 166 } RECORDINGCODECCALLBACKS, *PRECORDINGCODECCALLBACKS; 149 167 … … 186 204 * Set to 0 to use a variable bit rate (VBR) (if available, otherwise fall back to CBR). */ 187 205 uint32_t uBitrate; 188 /** Time (in ms) an (encoded) frame takes.206 /** Time (in ms) the encoder expects us to send data to encode. 189 207 * 190 208 * For Opus, valid frame sizes are: … … 196 214 * 40 1920 197 215 * 60 2880 216 * 217 * For Vorbis, valid frame sizes are powers of two from 64 to 8192 bytes. 198 218 */ 199 219 uint32_t msFrame; 200 /** The frame size in bytes (based on msFrame). */220 /** The frame size in bytes (based on \a msFrame). */ 201 221 uint32_t cbFrame; 202 /** The frame size in samples per frame (based on msFrame). */222 /** The frame size in samples per frame (based on \a msFrame). */ 203 223 uint32_t csFrame; 204 224 } RECORDINGCODECPARMS, *PRECORDINGCODECPARMS; … … 258 278 259 279 /** 280 * Structure for keeping a codec's internal state. 281 */ 282 typedef struct RECORDINGCODECSTATE 283 { 284 /** Timestamp (in ms, absolute) of the last frame was encoded. */ 285 uint64_t tsLastWrittenMs; 286 /** Number of encoding errors. */ 287 uint64_t cEncErrors; 288 } RECORDINGCODECSTATE; 289 /** Pointer to an internal encoder state. */ 290 typedef RECORDINGCODECSTATE *PRECORDINGCODECSTATE; 291 292 /** 260 293 * Structure for keeping codec-specific data. 261 294 */ … … 268 301 /** Generic codec parameters. */ 269 302 RECORDINGCODECPARMS Parms; 303 /** Generic codec parameters. */ 304 RECORDINGCODECSTATE State; 270 305 271 306 #ifdef VBOX_WITH_LIBVPX … … 288 323 #endif /* VBOX_WITH_AUDIO_RECORDING */ 289 324 290 /** Timestamp (in ms) of the last frame was encoded. */291 uint64_t uLastTimeStampMs;292 /** Number of encoding errors. */293 uint 64_t cEncErrors;325 /** Internal scratch buffer for en-/decoding steps. */ 326 void *pvScratch; 327 /** Size (in bytes) of \a pvScratch. */ 328 uint32_t cbScratch; 294 329 295 330 #ifdef VBOX_WITH_STATISTICS /** @todo Register these values with STAM. */ … … 300 335 /** Total time (in ms) of already encoded audio data. */ 301 336 uint64_t msEncTotal; 302 } S tats;337 } STAM; 303 338 #endif 304 339 } RECORDINGCODEC, *PRECORDINGCODEC; … … 395 430 : enmType(RECORDINGBLOCKTYPE_UNKNOWN) 396 431 , cRefs(0) 432 , uFlags(RECORDINGCODEC_ENC_F_NONE) 397 433 , pvData(NULL) 398 434 , cbData(0) { } … … 434 470 /** Number of references held of this block. */ 435 471 uint16_t cRefs; 472 /** Block flags of type RECORDINGCODEC_ENC_F_XXX. */ 473 uint64_t uFlags; 436 474 /** The (absolute) timestamp (in ms, PTS) of this block. */ 437 475 uint64_t msTimestamp; … … 449 487 int recordingCodecInit(const PRECORDINGCODEC pCodec, const PRECORDINGCODECCALLBACKS pCallbacks, const settings::RecordingScreenSettings &Settings); 450 488 int recordingCodecDestroy(PRECORDINGCODEC pCodec); 451 int recordingCodecEncode(PRECORDINGCODEC pCodec, const PRECORDINGFRAME pFrame, void *pvDst, size_t cbDst,size_t *pcEncoded, size_t *pcbEncoded);489 int recordingCodecEncode(PRECORDINGCODEC pCodec, const PRECORDINGFRAME pFrame, size_t *pcEncoded, size_t *pcbEncoded); 452 490 int recordingCodecFinalize(PRECORDINGCODEC pCodec); 453 491 #endif /* !MAIN_INCLUDED_RecordingInternals_h */ -
trunk/src/VBox/Main/include/RecordingStream.h
r96179 r96229 126 126 uint16_t GetID(void) const { return this->uScreenID; }; 127 127 #ifdef VBOX_WITH_AUDIO_RECORDING 128 PRECORDINGCODEC GetAudioCodec(void) { return &this->CodecAudio; };128 PRECORDINGCODEC GetAudioCodec(void) { return this->pCodecAudio; }; 129 129 #endif 130 130 PRECORDINGCODEC GetVideoCodec(void) { return &this->CodecVideo; }; … … 132 132 bool IsReady(void) const; 133 133 134 public: 135 136 static DECLCALLBACK(int) codecWriteDataCallback(PRECORDINGCODEC pCodec, const void *pvData, size_t cbData, uint64_t msAbsPTS, uint32_t uFlags, void *pvUser); 137 134 138 protected: 135 139 136 static DECLCALLBACK(int) codecWriteDataCallback(PRECORDINGCODEC pCodec, const void *pvData, size_t cbData, void *pvUser); 137 138 protected: 139 140 int open(const settings::RecordingScreenSettings &Settings); 140 int open(const settings::RecordingScreenSettings &screenSettings); 141 141 int close(void); 142 142 143 int initInternal(RecordingContext *pCtx, uint32_t uScreen, const settings::RecordingScreenSettings & Settings);143 int initInternal(RecordingContext *pCtx, uint32_t uScreen, const settings::RecordingScreenSettings &screenSettings); 144 144 int uninitInternal(void); 145 145 146 int initVideo(const settings::RecordingScreenSettings & Settings);146 int initVideo(const settings::RecordingScreenSettings &screenSettings); 147 147 int unitVideo(void); 148 149 int initAudio(const settings::RecordingScreenSettings &Settings);150 148 151 149 bool isLimitReachedInternal(uint64_t msTimestamp) const; 152 150 int iterateInternal(uint64_t msTimestamp); 151 152 int codecWriteToWebM(PRECORDINGCODEC pCodec, const void *pvData, size_t cbData, uint64_t msAbsPTS, uint32_t uFlags); 153 153 154 154 void lock(void); … … 171 171 172 172 /** Recording context this stream is associated to. */ 173 RecordingContext * pCtx;173 RecordingContext *m_pCtx; 174 174 /** The current state. */ 175 175 RECORDINGSTREAMSTATE enmState; … … 192 192 /** Critical section to serialize access. */ 193 193 RTCRITSECT CritSect; 194 /** Timestamp (in ms) of when recording has been start . */194 /** Timestamp (in ms) of when recording has been started. */ 195 195 uint64_t tsStartMs; 196 197 /** Audio codec instance data to use. */ 198 RECORDINGCODEC CodecAudio; 196 #ifdef VBOX_WITH_AUDIO_RECORDING 197 /** Pointer to audio codec instance data to use. 198 * 199 * We multiplex audio data from the recording context to all streams, 200 * to avoid encoding the same audio data for each stream. We ASSUME that 201 * all audio data of a VM will be the same for each stream at a given 202 * point in time. 203 * 204 * Might be NULL if not being used. */ 205 PRECORDINGCODEC pCodecAudio; 206 #endif /* VBOX_WITH_AUDIO_RECORDING */ 199 207 /** Video codec instance data to use. */ 200 RECORDINGCODEC 208 RECORDINGCODEC CodecVideo; 201 209 /** Screen settings to use. */ 202 settings::RecordingScreenSettings ScreenSettings; 210 settings::RecordingScreenSettings 211 ScreenSettings; 203 212 /** Common set of recording (data) blocks, needed for 204 213 * multiplexing to all recording streams. */ 205 RecordingBlockSet 214 RecordingBlockSet Blocks; 206 215 }; 207 216 -
trunk/src/VBox/Main/include/WebMWriter.h
r96210 r96229 563 563 int AddVideoTrack(PRECORDINGCODEC pCodec, uint16_t uWidth, uint16_t uHeight, uint32_t uFPS, uint8_t *puTrack); 564 564 565 int WriteBlock(uint8_t uTrack, const void *pvData, size_t cbData );565 int WriteBlock(uint8_t uTrack, const void *pvData, size_t cbData, WebMTimecodeAbs tcAbsPTSMs, WebMBlockFlags uFlags); 566 566 567 567 const com::Utf8Str& GetFileName(void); … … 594 594 int writeSimpleBlockQueued(WebMTrack *a_pTrack, WebMSimpleBlock *a_pBlock); 595 595 596 #ifdef VBOX_WITH_LIBVPX597 int writeSimpleBlockVP8(WebMTrack *a_pTrack, const vpx_codec_enc_cfg_t *a_pCfg, const vpx_codec_cx_pkt_t *a_pPkt);598 #endif599 600 int writeSimpleBlockAudio(WebMTrack *pTrack, const void *pvData, size_t cbData, WebMTimecodeAbs tcAbsPTSMs);601 602 596 int processQueue(WebMQueue *pQueue, bool fForce); 603 597 -
trunk/src/VBox/Main/src-client/DrvAudioRec.cpp
r96207 r96229 171 171 typedef struct AVRECSINK 172 172 { 173 /** Pointer to audio codec to use. */173 /** Pointer (weak) to audio codec to use. */ 174 174 PRECORDINGCODEC pCodec; 175 175 /** Container data to use for data processing. */ … … 198 198 /** Size (in bytes) of the temporary buffer holding the input (source) data to encode. */ 199 199 size_t cbSrcBuf; 200 /** Temporary buffer for the encoded output (destination) data. */201 void *pvDstBuf;202 /** Size (in bytes) of the temporary buffer holding the encoded output (destination) data. */203 size_t cbDstBuf;204 200 } AVRECSTREAM, *PAVRECSTREAM; 205 201 … … 355 351 { 356 352 pStreamAV->cbSrcBuf = cbScratchBuf; 357 pStreamAV->pvDstBuf = RTMemAlloc(cbScratchBuf); 358 if (pStreamAV->pvDstBuf) 359 { 360 pStreamAV->cbDstBuf = cbScratchBuf; 361 362 pStreamAV->pSink = pSink; /* Assign sink to stream. */ 363 pStreamAV->uLastPTSMs = 0; 364 365 /* Make sure to let the driver backend know that we need the audio data in 366 * a specific sampling rate the codec is optimized for. */ 353 354 pStreamAV->pSink = pSink; /* Assign sink to stream. */ 355 pStreamAV->uLastPTSMs = 0; 356 357 /* Make sure to let the driver backend know that we need the audio data in 358 * a specific sampling rate the codec is optimized for. */ 367 359 /** @todo r=bird: pCfgAcq->Props isn't initialized at all, except for uHz... */ 368 360 pCfgAcq->Props.uHz = pCodec->Parms.Audio.PCMProps.uHz; 369 361 // pCfgAcq->Props.cShift = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(pCfgAcq->Props.cbSample, pCfgAcq->Props.cChannels); 370 362 371 /* Every Opus frame marks a period for now. Optimize this later. */ 372 pCfgAcq->Backend.cFramesPeriod = PDMAudioPropsMilliToFrames(&pCfgAcq->Props, pCodec->Parms.msFrame); 373 pCfgAcq->Backend.cFramesBufferSize = PDMAudioPropsMilliToFrames(&pCfgAcq->Props, 100 /*ms*/); /** @todo Make this configurable. */ 374 pCfgAcq->Backend.cFramesPreBuffering = pCfgAcq->Backend.cFramesPeriod * 2; 375 } 376 else 377 vrc = VERR_NO_MEMORY; 363 /* Every Opus frame marks a period for now. Optimize this later. */ 364 pCfgAcq->Backend.cFramesPeriod = PDMAudioPropsMilliToFrames(&pCfgAcq->Props, pCodec->Parms.msFrame); 365 pCfgAcq->Backend.cFramesBufferSize = PDMAudioPropsMilliToFrames(&pCfgAcq->Props, 100 /*ms*/); /** @todo Make this configurable. */ 366 pCfgAcq->Backend.cFramesPreBuffering = pCfgAcq->Backend.cFramesPeriod * 2; 378 367 } 379 368 else … … 435 424 pStreamAV->pvSrcBuf = NULL; 436 425 pStreamAV->cbSrcBuf = 0; 437 }438 439 if (pStreamAV->pvDstBuf)440 {441 Assert(pStreamAV->cbDstBuf);442 RTMemFree(pStreamAV->pvDstBuf);443 pStreamAV->pvDstBuf = NULL;444 pStreamAV->cbDstBuf = 0;445 426 } 446 427 … … 629 610 vrc = recordingCodecEncode(pSink->pCodec, 630 611 /* Source */ 631 &Frame, 632 /* Dest */ 633 pStreamAV->pvDstBuf, pStreamAV->cbDstBuf, &cEncoded, &cbEncoded); 612 &Frame, &cEncoded, &cbEncoded); 634 613 if ( RT_SUCCESS(vrc) 635 614 && cEncoded) 636 615 { 637 616 Assert(cbEncoded); 638 639 if (pStreamAV->uLastPTSMs == 0)640 pStreamAV->uLastPTSMs = RTTimeProgramMilliTS(); /* We want the absolute time (in ms) since program start. */641 642 const uint64_t uDurationMs = pSink->pCodec->Parms.msFrame * cEncoded;643 const uint64_t uPTSMs = pStreamAV->uLastPTSMs;644 645 pStreamAV->uLastPTSMs += uDurationMs;646 647 switch (pSink->Con.Parms.enmType)648 {649 case AVRECCONTAINERTYPE_MAIN_CONSOLE:650 {651 HRESULT hrc = pSink->Con.Main.pConsole->i_recordingSendAudio(pStreamAV->pvDstBuf, cbEncoded, uPTSMs);652 Assert(hrc == S_OK);653 RT_NOREF(hrc);654 break;655 }656 657 case AVRECCONTAINERTYPE_WEBM:658 {659 WebMWriter::BlockData_Audio blockData = { pStreamAV->pvDstBuf, cbEncoded, uPTSMs };660 vrc = pSink->Con.WebM.pWebM->WriteBlock(pSink->Con.WebM.uTrack, &blockData, sizeof(blockData));661 AssertRC(vrc);662 break;663 }664 665 default:666 AssertFailedStmt(vrc = VERR_NOT_IMPLEMENTED);667 break;668 }669 617 } 670 618 else if (RT_FAILURE(vrc)) /* Something went wrong -- report all bytes as being processed, to not hold up others. */ … … 737 685 AssertPtrReturnVoid(pSink); 738 686 739 recordingCodecFinalize(pSink->pCodec);740 recordingCodecDestroy(pSink->pCodec);741 687 pSink->pCodec = NULL; 742 688 … … 901 847 return VINF_SUCCESS; 902 848 } 903 904 recordingCodecDestroy(pSink->pCodec);905 pSink->pCodec = NULL;906 849 907 850 LogRel(("Recording: Error creating sink (%Rrc)\n", vrc)); -
trunk/src/VBox/Main/src-client/Recording.cpp
r96140 r96229 61 61 * Recording context constructor. 62 62 * 63 * @note Will throw when unable to create.63 * @note Will throw rc when unable to create. 64 64 */ 65 65 RecordingContext::RecordingContext(void) … … 77 77 * 78 78 * @param ptrConsole Pointer to console object this context is bound to (weak pointer). 79 * @param settings Reference to recording settings to use for creation.80 * 81 * @note Will throw when unable to create.82 */ 83 RecordingContext::RecordingContext(Console *ptrConsole, const settings::RecordingSettings & settings)79 * @param Settings Reference to recording settings to use for creation. 80 * 81 * @note Will throw rc when unable to create. 82 */ 83 RecordingContext::RecordingContext(Console *ptrConsole, const settings::RecordingSettings &Settings) 84 84 : pConsole(NULL) 85 85 , enmState(RECORDINGSTS_UNINITIALIZED) … … 90 90 throw vrc; 91 91 92 vrc = RecordingContext::createInternal(ptrConsole, settings);92 vrc = RecordingContext::createInternal(ptrConsole, Settings); 93 93 if (RT_FAILURE(vrc)) 94 94 throw vrc; … … 170 170 171 171 /** 172 * Writes block data which are common (shared) between all streams. 173 * 174 * To save time spent in EMT or other important threads (such as audio async I/O), 175 * do the required audio multiplexing in the encoding thread. 176 * 177 * The multiplexing is needed to supply all recorded (enabled) screens with the same 178 * data at the same given point in time. 179 * 180 * Currently this only is being used for audio data. 181 */ 182 int RecordingContext::writeCommonData(PRECORDINGCODEC pCodec, const void *pvData, size_t cbData, 183 uint64_t msAbsPTS, uint32_t uFlags) 184 { 185 AssertPtrReturn(pvData, VERR_INVALID_POINTER); 186 AssertReturn(cbData, VERR_INVALID_PARAMETER); 187 188 LogFlowFunc(("pCodec=%p, cbData=%zu, msAbsPTS=%zu, uFlags=%#x\n", 189 pCodec, cbData, msAbsPTS, uFlags)); 190 191 /** @todo Optimize this! Three allocations in here! */ 192 193 RECORDINGBLOCKTYPE const enmType = pCodec->Parms.enmType == RECORDINGCODECTYPE_AUDIO 194 ? RECORDINGBLOCKTYPE_AUDIO : RECORDINGBLOCKTYPE_UNKNOWN; 195 196 AssertReturn(enmType != RECORDINGBLOCKTYPE_UNKNOWN, VERR_NOT_SUPPORTED); 197 198 RecordingBlock *pBlock = new RecordingBlock(); 199 200 switch (enmType) 201 { 202 case RECORDINGBLOCKTYPE_AUDIO: 203 { 204 PRECORDINGAUDIOFRAME pFrame = (PRECORDINGAUDIOFRAME)RTMemAlloc(sizeof(RECORDINGAUDIOFRAME)); 205 AssertPtrReturn(pFrame, VERR_NO_MEMORY); 206 207 pFrame->pvBuf = (uint8_t *)RTMemAlloc(cbData); 208 AssertPtrReturn(pFrame->pvBuf, VERR_NO_MEMORY); 209 pFrame->cbBuf = cbData; 210 211 memcpy(pFrame->pvBuf, pvData, cbData); 212 213 pBlock->enmType = enmType; 214 pBlock->pvData = pFrame; 215 pBlock->cbData = sizeof(RECORDINGAUDIOFRAME) + cbData; 216 pBlock->cRefs = this->cStreamsEnabled; 217 pBlock->msTimestamp = msAbsPTS; 218 pBlock->uFlags = uFlags; 219 220 break; 221 } 222 223 default: 224 AssertFailed(); 225 break; 226 } 227 228 lock(); 229 230 int vrc; 231 232 try 233 { 234 RecordingBlockMap::iterator itBlocks = this->mapBlocksCommon.find(msAbsPTS); 235 if (itBlocks == this->mapBlocksCommon.end()) 236 { 237 RecordingBlocks *pRecordingBlocks = new RecordingBlocks(); 238 pRecordingBlocks->List.push_back(pBlock); 239 240 this->mapBlocksCommon.insert(std::make_pair(msAbsPTS, pRecordingBlocks)); 241 } 242 else 243 itBlocks->second->List.push_back(pBlock); 244 245 vrc = VINF_SUCCESS; 246 } 247 catch (const std::exception &ex) 248 { 249 RT_NOREF(ex); 250 vrc = VERR_NO_MEMORY; 251 } 252 253 unlock(); 254 255 if (RT_SUCCESS(vrc)) 256 vrc = threadNotify(); 257 258 return vrc; 259 } 260 261 #ifdef VBOX_WITH_AUDIO_RECORDING 262 /** 263 * Callback function for taking care of multiplexing the encoded audio data to all connected streams. 264 * 265 * @copydoc RECORDINGCODECCALLBACKS::pfnWriteData 266 */ 267 /* static */ 268 DECLCALLBACK(int) RecordingContext::audioCodecWriteDataCallback(PRECORDINGCODEC pCodec, const void *pvData, size_t cbData, 269 uint64_t msAbsPTS, uint32_t uFlags, void *pvUser) 270 { 271 #if 0 272 RecordingContext *pThis = (RecordingContext *)pvUser; 273 274 int vrc = VINF_SUCCESS; 275 276 RecordingStreams::iterator itStream = pThis->vecStreams.begin(); 277 while (itStream != pThis->vecStreams.end()) 278 { 279 RecordingStream *pStream = (*itStream); 280 281 LogFlowFunc(("pStream=%p\n", pStream)); 282 283 if (pStream->GetConfig().isFeatureEnabled(RecordingFeature_Audio)) /** @todo Optimize this! Use a dedicated stream group for video-only / audio-only streams. */ 284 { 285 vrc = RecordingStream::codecWriteDataCallback(pCodec, pvData, cbData, msAbsPTS, uFlags, pStream); 286 if (RT_FAILURE(vrc)) 287 { 288 LogRel(("Recording: Calling audio write callback for stream #%RU16 failed (%Rrc)\n", pStream->GetID(), vrc)); 289 break; 290 } 291 } 292 293 ++itStream; 294 } 295 296 return vrc; 297 #else 298 RecordingContext *pThis = (RecordingContext *)pvUser; 299 300 return pThis->writeCommonData(pCodec, pvData, cbData, msAbsPTS, uFlags); 301 #endif 302 } 303 304 /** 305 * Initializes the audio codec for a (multiplexing) recording context. 306 * 307 * @returns VBox status code. 308 * @param screenSettings Reference to recording screen settings to use for initialization. 309 */ 310 int RecordingContext::audioInit(const settings::RecordingScreenSettings &screenSettings) 311 { 312 RecordingAudioCodec_T const enmCodec = screenSettings.Audio.enmCodec; 313 314 if (enmCodec == RecordingAudioCodec_None) 315 { 316 LogRel2(("Recording: No audio codec configured, skipping audio init\n")); 317 return VINF_SUCCESS; 318 } 319 320 RECORDINGCODECCALLBACKS Callbacks; 321 Callbacks.pvUser = this; 322 Callbacks.pfnWriteData = RecordingContext::audioCodecWriteDataCallback; 323 324 int vrc = recordingCodecCreateAudio(&this->CodecAudio, enmCodec); 325 if (RT_SUCCESS(vrc)) 326 vrc = recordingCodecInit(&this->CodecAudio, &Callbacks, screenSettings); 327 328 return vrc; 329 } 330 #endif /* VBOX_WITH_AUDIO_RECORDING */ 331 332 /** 172 333 * Creates a recording context. 173 334 * 174 335 * @returns IPRT status code. 175 336 * @param ptrConsole Pointer to console object this context is bound to (weak pointer). 176 * @param settings Reference to recording settings to use for creation.177 */ 178 int RecordingContext::createInternal(Console *ptrConsole, const settings::RecordingSettings & settings)337 * @param Settings Reference to recording settings to use for creation. 338 */ 339 int RecordingContext::createInternal(Console *ptrConsole, const settings::RecordingSettings &Settings) 179 340 { 180 341 int vrc = VINF_SUCCESS; 181 342 343 /* Copy the settings to our context. */ 344 m_Settings = Settings; 345 346 #ifdef VBOX_WITH_AUDIO_RECORDING 347 settings::RecordingScreenSettingsMap::const_iterator itScreen0 = m_Settings.mapScreens.begin(); 348 AssertReturn(itScreen0 != m_Settings.mapScreens.end(), VERR_WRONG_ORDER); 349 350 /* We always use the audio settings from screen 0, as we multiplex the audio data anyway. */ 351 settings::RecordingScreenSettings const &screen0Settings = itScreen0->second; 352 353 vrc = this->audioInit(screen0Settings); 354 if (RT_FAILURE(vrc)) 355 return vrc; 356 #endif 357 182 358 this->pConsole = ptrConsole; 183 359 184 settings::RecordingScreenSettingsMap::const_iterator itScreen = settings.mapScreens.begin();185 while (itScreen != settings.mapScreens.end())360 settings::RecordingScreenSettingsMap::const_iterator itScreen = m_Settings.mapScreens.begin(); 361 while (itScreen != m_Settings.mapScreens.end()) 186 362 { 187 363 RecordingStream *pStream = NULL; … … 192 368 if (itScreen->second.fEnabled) 193 369 this->cStreamsEnabled++; 370 LogFlowFunc(("pStream=%p\n", pStream)); 194 371 } 195 372 catch (std::bad_alloc &) … … 213 390 this->fShutdown = false; 214 391 215 /* Copy the settings to our context. */216 this->Settings = settings;217 218 392 vrc = RTSemEventCreate(&this->WaitEvent); 219 393 AssertRCReturn(vrc, vrc); … … 343 517 const settings::RecordingSettings &RecordingContext::GetConfig(void) const 344 518 { 345 return this-> Settings;519 return this->m_Settings; 346 520 } 347 521 … … 368 542 } 369 543 544 /** 545 * Locks the recording context for serializing access. 546 * 547 * @returns VBox status code. 548 */ 370 549 int RecordingContext::lock(void) 371 550 { … … 375 554 } 376 555 556 /** 557 * Unlocks the recording context for serializing access. 558 * 559 * @returns VBox status code. 560 */ 377 561 int RecordingContext::unlock(void) 378 562 { … … 408 592 * @returns IPRT status code. 409 593 * @param ptrConsole Pointer to console object this context is bound to (weak pointer). 410 * @param settings Reference to recording settings to use for creation.411 */ 412 int RecordingContext::Create(Console *ptrConsole, const settings::RecordingSettings & settings)413 { 414 return createInternal(ptrConsole, settings);594 * @param Settings Reference to recording settings to use for creation. 595 */ 596 int RecordingContext::Create(Console *ptrConsole, const settings::RecordingSettings &Settings) 597 { 598 return createInternal(ptrConsole, Settings); 415 599 } 416 600 … … 605 789 { 606 790 #ifdef VBOX_WITH_AUDIO_RECORDING 607 AssertPtrReturn(pvData, VERR_INVALID_POINTER); 608 AssertReturn(cbData, VERR_INVALID_PARAMETER); 609 610 /* To save time spent in EMT, do the required audio multiplexing in the encoding thread. 611 * 612 * The multiplexing is needed to supply all recorded (enabled) screens with the same 613 * audio data at the same given point in time. 614 */ 615 RecordingBlock *pBlock = new RecordingBlock(); 616 pBlock->enmType = RECORDINGBLOCKTYPE_AUDIO; 617 618 PRECORDINGAUDIOFRAME pFrame = (PRECORDINGAUDIOFRAME)RTMemAlloc(sizeof(RECORDINGAUDIOFRAME)); 619 AssertPtrReturn(pFrame, VERR_NO_MEMORY); 620 621 pFrame->pvBuf = (uint8_t *)RTMemAlloc(cbData); 622 AssertPtrReturn(pFrame->pvBuf, VERR_NO_MEMORY); 623 pFrame->cbBuf = cbData; 624 625 memcpy(pFrame->pvBuf, pvData, cbData); 626 627 pBlock->pvData = pFrame; 628 pBlock->cbData = sizeof(RECORDINGAUDIOFRAME) + cbData; 629 pBlock->cRefs = this->cStreamsEnabled; 630 pBlock->msTimestamp = msTimestamp; 631 632 lock(); 633 634 int vrc; 635 636 try 637 { 638 RecordingBlockMap::iterator itBlocks = this->mapBlocksCommon.find(msTimestamp); 639 if (itBlocks == this->mapBlocksCommon.end()) 640 { 641 RecordingBlocks *pRecordingBlocks = new RecordingBlocks(); 642 pRecordingBlocks->List.push_back(pBlock); 643 644 this->mapBlocksCommon.insert(std::make_pair(msTimestamp, pRecordingBlocks)); 645 } 646 else 647 itBlocks->second->List.push_back(pBlock); 648 649 vrc = VINF_SUCCESS; 650 } 651 catch (const std::exception &ex) 652 { 653 RT_NOREF(ex); 654 vrc = VERR_NO_MEMORY; 655 } 656 657 unlock(); 658 659 if (RT_SUCCESS(vrc)) 660 vrc = threadNotify(); 661 662 return vrc; 791 return writeCommonData(&this->CodecAudio, pvData, cbData, msTimestamp, RECORDINGCODEC_ENC_F_BLOCK_IS_KEY); 663 792 #else 664 793 RT_NOREF(pvData, cbData, msTimestamp); -
trunk/src/VBox/Main/src-client/RecordingCodec.cpp
r96179 r96229 39 39 static DECLCALLBACK(int) recordingCodecVPXInit(PRECORDINGCODEC pCodec) 40 40 { 41 pCodec->cbScratch = _4K; 42 pCodec->pvScratch = RTMemAlloc(pCodec->cbScratch); 43 AssertPtrReturn(pCodec->pvScratch, VERR_NO_MEMORY); 44 41 45 # ifdef VBOX_WITH_LIBVPX_VP9 42 46 vpx_codec_iface_t *pCodecIface = vpx_codec_vp9_cx(); … … 132 136 /** @copydoc RECORDINGCODECOPS::pfnEncode */ 133 137 static DECLCALLBACK(int) recordingCodecVPXEncode(PRECORDINGCODEC pCodec, PRECORDINGFRAME pFrame, 134 void *pvDst, size_t cbDst,size_t *pcEncoded, size_t *pcbEncoded)135 { 136 RT_NOREF(p vDst, cbDst, pcEncoded, pcbEncoded);138 size_t *pcEncoded, size_t *pcbEncoded) 139 { 140 RT_NOREF(pcEncoded, pcbEncoded); 137 141 138 142 AssertPtrReturn(pFrame, VERR_INVALID_POINTER); … … 158 162 if (rcv != VPX_CODEC_OK) 159 163 { 160 if (pCodec-> cEncErrors++ < 64) /** @todo Make this configurable. */164 if (pCodec->State.cEncErrors++ < 64) /** @todo Make this configurable. */ 161 165 LogRel(("Recording: Failed to encode video frame: %s\n", vpx_codec_err_to_string(rcv))); 162 166 return VERR_RECORDING_ENCODING_FAILED; 163 167 } 164 168 165 pCodec-> cEncErrors = 0;169 pCodec->State.cEncErrors = 0; 166 170 167 171 vpx_codec_iter_t iter = NULL; … … 169 173 for (;;) 170 174 { 171 const vpx_codec_cx_pkt_t *pP acket = vpx_codec_get_cx_data(&pVPX->Ctx, &iter);172 if (!pP acket)175 const vpx_codec_cx_pkt_t *pPkt = vpx_codec_get_cx_data(&pVPX->Ctx, &iter); 176 if (!pPkt) 173 177 break; 174 178 175 switch (pP acket->kind)179 switch (pPkt->kind) 176 180 { 177 181 case VPX_CODEC_CX_FRAME_PKT: 178 182 { 179 WebMWriter::BlockData_VP8 blockData = { &pCodec->Video.VPX.Cfg, pPacket }; 180 AssertPtr(pCodec->Callbacks.pfnWriteData); 181 182 vrc = pCodec->Callbacks.pfnWriteData(pCodec, &blockData, sizeof(blockData), pCodec->Callbacks.pvUser); 183 /* Calculate the absolute PTS of this frame (in ms). */ 184 uint64_t tsAbsPTSMs = pPkt->data.frame.pts * 1000 185 * (uint64_t)pCodec->Video.VPX.Cfg.g_timebase.num / pCodec->Video.VPX.Cfg.g_timebase.den; 186 187 pCodec->State.tsLastWrittenMs += pCodec->Parms.msFrame; 188 189 const bool fKeyframe = RT_BOOL(pPkt->data.frame.flags & VPX_FRAME_IS_KEY); 190 191 uint32_t fFlags = RECORDINGCODEC_ENC_F_NONE; 192 if (fKeyframe) 193 fFlags |= RECORDINGCODEC_ENC_F_BLOCK_IS_KEY; 194 if (pPkt->data.frame.flags & VPX_FRAME_IS_INVISIBLE) 195 fFlags |= RECORDINGCODEC_ENC_F_BLOCK_IS_INVISIBLE; 196 197 vrc = pCodec->Callbacks.pfnWriteData(pCodec, pPkt->data.frame.buf, pPkt->data.frame.sz, 198 tsAbsPTSMs, fFlags, pCodec->Callbacks.pvUser); 183 199 break; 184 200 } … … 186 202 default: 187 203 AssertFailed(); 188 LogFunc(("Unexpected video packet type %ld\n", pP acket->kind));204 LogFunc(("Unexpected video packet type %ld\n", pPkt->kind)); 189 205 break; 190 206 } … … 204 220 static DECLCALLBACK(int) recordingCodecOpusInit(PRECORDINGCODEC pCodec) 205 221 { 222 pCodec->cbScratch = _4K; 223 pCodec->pvScratch = RTMemAlloc(pCodec->cbScratch); 224 AssertPtrReturn(pCodec->pvScratch, VERR_NO_MEMORY); 225 206 226 const PPDMAUDIOPCMPROPS pProps = &pCodec->Parms.Audio.PCMProps; 207 227 … … 275 295 /** @copydoc RECORDINGCODECOPS::pfnEncode */ 276 296 static DECLCALLBACK(int) recordingCodecOpusEncode(PRECORDINGCODEC pCodec, 277 const PRECORDINGFRAME pFrame, void *pvDst, size_t cbDst, 278 size_t *pcEncoded, size_t *pcbEncoded) 297 const PRECORDINGFRAME pFrame, size_t *pcEncoded, size_t *pcbEncoded) 279 298 { 280 299 const PPDMAUDIOPCMPROPS pPCMProps = &pCodec->Parms.Audio.PCMProps; … … 284 303 Assert (pFrame->Audio.cbBuf); 285 304 AssertReturn (pFrame->Audio.cbBuf % pPCMProps->cbFrame == 0, VERR_INVALID_PARAMETER); 305 AssertReturn(pCodec->cbScratch >= pFrame->Audio.cbBuf, VERR_INVALID_PARAMETER); 286 306 AssertPtrReturn(pcEncoded, VERR_INVALID_POINTER); 287 307 AssertPtrReturn(pcbEncoded, VERR_INVALID_POINTER); 288 308 289 309 int vrc = VINF_SUCCESS; 310 311 size_t cBlocksEncoded = 0; 312 size_t cBytesEncoded = 0; 290 313 291 314 /* … … 298 321 opus_int32 cbWritten = opus_encode(pCodec->Audio.Opus.pEnc, 299 322 (opus_int16 *)pFrame->Audio.pvBuf, (int)(pFrame->Audio.cbBuf / pPCMProps->cbFrame /* Number of audio frames */), 300 (uint8_t *)p vDst, (opus_int32)cbDst);323 (uint8_t *)pCodec->pvScratch, (opus_int32)pCodec->cbScratch); 301 324 if (cbWritten < 0) 302 325 { … … 305 328 } 306 329 307 /* Get overall frames encoded. */ 308 *pcEncoded = opus_packet_get_nb_frames((uint8_t *)pvDst, cbWritten); 309 *pcbEncoded = cbWritten; 330 if (cbWritten) 331 { 332 vrc = pCodec->Callbacks.pfnWriteData(pCodec, pCodec->pvScratch, (size_t)cbWritten, pCodec->State.tsLastWrittenMs, 333 RECORDINGCODEC_ENC_F_BLOCK_IS_KEY /* Every Opus frame is a key frame */, 334 pCodec->Callbacks.pvUser); 335 if (RT_SUCCESS(vrc)) 336 pCodec->State.tsLastWrittenMs += pCodec->Parms.msFrame; 337 } 338 339 if (RT_SUCCESS(vrc)) 340 { 341 /* Get overall frames encoded. */ 342 cBlocksEncoded = opus_packet_get_nb_frames((uint8_t *)pCodec->pvScratch, cbWritten); 343 cBytesEncoded = cbWritten; 344 345 if (pcEncoded) 346 *pcEncoded = cBlocksEncoded; 347 if (pcbEncoded) 348 *pcbEncoded = cBytesEncoded; 349 } 310 350 311 351 if (RT_FAILURE(vrc)) 312 352 LogRel(("Recording: Encoding Opus data failed, rc=%Rrc\n", vrc)); 313 353 314 Log3Func(("cbSrc=%zu, cbDst=%zu, cEncoded=%zu, cbEncoded=%zu, vrc=%Rrc\n", pFrame->Audio.cbBuf, cbDst,315 RT_SUCCESS(vrc) ? *pcEncoded : 0, RT_SUCCESS(vrc) ? *pcbEncoded : 0, vrc));354 Log3Func(("cbSrc=%zu, cbDst=%zu, cEncoded=%zu, cbEncoded=%zu, vrc=%Rrc\n", 355 pFrame->Audio.cbBuf, pCodec->cbScratch, cBlocksEncoded, cBytesEncoded, vrc)); 316 356 317 357 return vrc; … … 328 368 static DECLCALLBACK(int) recordingCodecVorbisInit(PRECORDINGCODEC pCodec) 329 369 { 370 pCodec->cbScratch = _4K; 371 pCodec->pvScratch = RTMemAlloc(pCodec->cbScratch); 372 AssertPtrReturn(pCodec->pvScratch, VERR_NO_MEMORY); 373 330 374 const PPDMAUDIOPCMPROPS pPCMProps = &pCodec->Parms.Audio.PCMProps; 331 375 … … 395 439 /** @copydoc RECORDINGCODECOPS::pfnEncode */ 396 440 static DECLCALLBACK(int) recordingCodecVorbisEncode(PRECORDINGCODEC pCodec, 397 const PRECORDINGFRAME pFrame, void *pvDst, size_t cbDst, 398 size_t *pcEncoded, size_t *pcbEncoded) 441 const PRECORDINGFRAME pFrame, size_t *pcEncoded, size_t *pcbEncoded) 399 442 { 400 443 const PPDMAUDIOPCMPROPS pPCMProps = &pCodec->Parms.Audio.PCMProps; 401 444 402 Assert (pCodec->Parms.cbFrame); 403 AssertReturn (pFrame->Audio.cbBuf % pCodec->Parms.cbFrame == 0, VERR_INVALID_PARAMETER); 404 Assert (pFrame->Audio.cbBuf); 405 AssertReturn (pFrame->Audio.cbBuf % PDMAudioPropsFrameSize(pPCMProps) == 0, VERR_INVALID_PARAMETER); 445 Assert (pCodec->Parms.cbFrame); 446 AssertReturn(pFrame->Audio.cbBuf % pCodec->Parms.cbFrame == 0, VERR_INVALID_PARAMETER); 447 Assert (pFrame->Audio.cbBuf); 448 AssertReturn(pFrame->Audio.cbBuf % PDMAudioPropsFrameSize(pPCMProps) == 0, VERR_INVALID_PARAMETER); 449 AssertReturn(pCodec->cbScratch >= pFrame->Audio.cbBuf, VERR_INVALID_PARAMETER); 406 450 407 451 int vrc = VINF_SUCCESS; … … 443 487 size_t cBytesEncoded = 0; 444 488 445 uint8_t *puDst = (uint8_t *)p vDst;489 uint8_t *puDst = (uint8_t *)pCodec->pvScratch; 446 490 447 491 while (vorbis_analysis_blockout(&pCodec->Audio.Vorbis.dsp_state, &pCodec->Audio.Vorbis.block_cur) == 1 /* More available? */) … … 474 518 { 475 519 cBytesEncoded += op.bytes; 476 AssertBreakStmt(cBytesEncoded <= cbDst, vrc = VERR_BUFFER_OVERFLOW);520 AssertBreakStmt(cBytesEncoded <= pCodec->cbScratch, vrc = VERR_BUFFER_OVERFLOW); 477 521 cBlocksEncoded++; 478 522 479 if (pCodec->Callbacks.pfnWriteData) 480 { 481 WebMWriter::BlockData_Audio blockData = { op.packet, (size_t)op.bytes, pCodec->uLastTimeStampMs }; 482 pCodec->Callbacks.pfnWriteData(pCodec, &blockData, sizeof(blockData), pCodec->Callbacks.pvUser); 483 } 484 485 pCodec->uLastTimeStampMs += uDurationMs; 523 vrc = pCodec->Callbacks.pfnWriteData(pCodec, op.packet, (size_t)op.bytes, pCodec->State.tsLastWrittenMs, 524 RECORDINGCODEC_ENC_F_BLOCK_IS_KEY /* Every Vorbis frame is a key frame */, 525 pCodec->Callbacks.pvUser); 526 if (RT_SUCCESS(vrc)) 527 pCodec->State.tsLastWrittenMs += uDurationMs; 486 528 } 487 529 … … 513 555 514 556 Log3Func(("cbSrc=%zu, cbDst=%zu, cEncoded=%zu, cbEncoded=%zu, vrc=%Rrc\n", 515 pFrame->Audio.cbBuf, cbDst, cBlocksEncoded, cBytesEncoded, vrc));557 pFrame->Audio.cbBuf, pCodec->cbScratch, cBlocksEncoded, cBytesEncoded, vrc)); 516 558 517 559 return vrc; … … 687 729 #endif 688 730 731 static void recordingCodecReset(PRECORDINGCODEC pCodec) 732 { 733 pCodec->State.tsLastWrittenMs = 0; 734 735 pCodec->State.cEncErrors = 0; 736 #ifdef VBOX_WITH_STATISTICS 737 pCodec->STAM.cEncBlocks = 0; 738 pCodec->STAM.msEncTotal = 0; 739 #endif 740 } 741 742 /** 743 * Common code for codec creation. 744 * 745 * @param pCodec Codec instance to create. 746 */ 747 static void recordingCodecCreateCommon(PRECORDINGCODEC pCodec) 748 { 749 RT_ZERO(pCodec->Ops); 750 RT_ZERO(pCodec->Callbacks); 751 } 752 689 753 /** 690 754 * Creates an audio codec. … … 697 761 { 698 762 int vrc; 763 764 recordingCodecCreateCommon(pCodec); 699 765 700 766 switch (enmAudioCodec) … … 728 794 729 795 default: 796 LogRel(("Recording: Selected codec is not supported!\n")); 730 797 vrc = VERR_RECORDING_CODEC_NOT_SUPPORTED; 731 798 break; … … 751 818 { 752 819 int vrc; 820 821 recordingCodecCreateCommon(pCodec); 753 822 754 823 switch (enmVideoCodec) … … 791 860 int recordingCodecInit(const PRECORDINGCODEC pCodec, const PRECORDINGCODECCALLBACKS pCallbacks, const settings::RecordingScreenSettings &Settings) 792 861 { 862 recordingCodecReset(pCodec); 863 793 864 int vrc; 794 865 if (pCodec->Parms.enmType == RECORDINGCODECTYPE_AUDIO) … … 799 870 AssertFailedStmt(vrc = VERR_NOT_SUPPORTED); 800 871 801 if (RT_SUCCESS(vrc))802 {803 pCodec->cEncErrors = 0;804 #ifdef VBOX_WITH_STATISTICS805 pCodec->Stats.cEncBlocks = 0;806 pCodec->Stats.msEncTotal = 0;807 #endif808 }809 810 872 return vrc; 811 873 } … … 859 921 if (RT_SUCCESS(vrc)) 860 922 { 923 if (pCodec->pvScratch) 924 { 925 Assert(pCodec->cbScratch); 926 RTMemFree(pCodec->pvScratch); 927 pCodec->pvScratch = NULL; 928 pCodec->cbScratch = 0; 929 } 930 861 931 pCodec->Parms.enmType = RECORDINGCODECTYPE_INVALID; 862 932 pCodec->Parms.enmVideoCodec = RecordingVideoCodec_None; … … 872 942 * @param pCodec Codec to use. 873 943 * @param pFrame Pointer to frame data to encode. 874 * @param pvDst Where to store the encoded data on success.875 * @param cbDst Size (in bytes) of \a pvDst.876 944 * @param pcEncoded Where to return the number of encoded blocks in \a pvDst on success. Optional. 877 945 * @param pcbEncoded Where to return the number of encoded bytes in \a pvDst on success. Optional. 878 946 */ 879 947 int recordingCodecEncode(PRECORDINGCODEC pCodec, 880 const PRECORDINGFRAME pFrame, void *pvDst, size_t cbDst, 881 size_t *pcEncoded, size_t *pcbEncoded) 948 const PRECORDINGFRAME pFrame, size_t *pcEncoded, size_t *pcbEncoded) 882 949 { 883 950 AssertPtrReturn(pCodec->Ops.pfnEncode, VERR_NOT_SUPPORTED); 884 951 885 952 size_t cEncoded, cbEncoded; 886 int vrc = pCodec->Ops.pfnEncode(pCodec, pFrame, pvDst, cbDst,&cEncoded, &cbEncoded);953 int vrc = pCodec->Ops.pfnEncode(pCodec, pFrame, &cEncoded, &cbEncoded); 887 954 if (RT_SUCCESS(vrc)) 888 955 { 889 956 #ifdef VBOX_WITH_STATISTICS 890 pCodec->S tats.cEncBlocks += cEncoded;891 pCodec->S tats.msEncTotal += pCodec->Parms.msFrame * cEncoded;957 pCodec->STAM.cEncBlocks += cEncoded; 958 pCodec->STAM.msEncTotal += pCodec->Parms.msFrame * cEncoded; 892 959 #endif 893 960 if (pcEncoded) -
trunk/src/VBox/Main/src-client/RecordingStream.cpp
r96178 r96229 42 42 int vrc2 = initInternal(a_pCtx, uScreen, Settings); 43 43 if (RT_FAILURE(vrc2)) 44 {45 LogRel(("Recording: Initialization failed with %Rrc\n", vrc2));46 44 throw vrc2; 47 }48 45 } 49 46 … … 58 55 * 59 56 * @returns IPRT status code. 60 */ 61 int RecordingStream::open(const settings::RecordingScreenSettings &Settings) 57 * @param screenSettings Recording settings to use. 58 */ 59 int RecordingStream::open(const settings::RecordingScreenSettings &screenSettings) 62 60 { 63 61 /* Sanity. */ 64 Assert( Settings.enmDest != RecordingDestination_None);62 Assert(screenSettings.enmDest != RecordingDestination_None); 65 63 66 64 int vrc; 67 65 68 switch ( Settings.enmDest)66 switch (screenSettings.enmDest) 69 67 { 70 68 case RecordingDestination_File: 71 69 { 72 Assert( Settings.File.strName.isNotEmpty());73 74 char *pszAbsPath = RTPathAbsDup( Settings.File.strName.c_str());70 Assert(screenSettings.File.strName.isNotEmpty()); 71 72 char *pszAbsPath = RTPathAbsDup(screenSettings.File.strName.c_str()); 75 73 AssertPtrReturn(pszAbsPath, VERR_NO_MEMORY); 76 74 … … 87 85 char *pszFile = NULL; 88 86 89 if (this->uScreenID > 0) 90 vrc = RTStrAPrintf(&pszFile, "%s-%u%s", pszAbsPath, this->uScreenID + 1, pszSuff); 91 else 92 vrc = RTStrAPrintf(&pszFile, "%s%s", pszAbsPath, pszSuff); 93 87 vrc = RTStrAPrintf(&pszFile, "%s-%u%s", pszAbsPath, this->uScreenID, pszSuff); 94 88 if (RT_SUCCESS(vrc)) 95 89 { 90 #ifdef DEBUG_andy 91 uint64_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE; 92 #else 96 93 uint64_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_WRITE; 97 94 … … 100 97 * other important file, causing unintentional data loss. */ 101 98 fOpen |= RTFILE_O_CREATE; 102 99 #endif 103 100 RTFILE hFile; 104 101 vrc = RTFileOpen(&hFile, pszFile, fOpen); … … 113 110 RTTimeExplode(&time, &ts); 114 111 115 if (this->uScreenID > 0) 116 vrc = RTStrAPrintf(&pszFile, "%s-%04d-%02u-%02uT%02u-%02u-%02u-%09uZ-%u%s", 117 pszAbsPath, time.i32Year, time.u8Month, time.u8MonthDay, 118 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond, 119 this->uScreenID + 1, pszSuff); 120 else 121 vrc = RTStrAPrintf(&pszFile, "%s-%04d-%02u-%02uT%02u-%02u-%02u-%09uZ%s", 122 pszAbsPath, time.i32Year, time.u8Month, time.u8MonthDay, 123 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond, 124 pszSuff); 125 112 vrc = RTStrAPrintf(&pszFile, "%s-%04d-%02u-%02uT%02u-%02u-%02u-%09uZ-%u%s", 113 pszAbsPath, time.i32Year, time.u8Month, time.u8MonthDay, 114 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond, 115 this->uScreenID, pszSuff); 126 116 if (RT_SUCCESS(vrc)) 127 117 vrc = RTFileOpen(&hFile, pszFile, fOpen); 128 118 } 129 119 120 if (RT_FAILURE(vrc)) 121 break; 122 123 LogRel2(("Recording: Opened file '%s'\n", pszFile)); 124 130 125 try 131 126 { … … 134 129 } 135 130 catch (std::bad_alloc &) 136 {131 { 137 132 vrc = VERR_NO_MEMORY; 138 133 } … … 242 237 vrc = VINF_SUCCESS; 243 238 244 AssertPtr(this-> pCtx);239 AssertPtr(this->m_pCtx); 245 240 246 241 switch (vrc) … … 250 245 this->fEnabled = false; 251 246 252 int vrc2 = this-> pCtx->OnLimitReached(this->uScreenID, VINF_SUCCESS /* rc */);247 int vrc2 = this->m_pCtx->OnLimitReached(this->uScreenID, VINF_SUCCESS /* rc */); 253 248 AssertRC(vrc2); 254 249 break; … … 294 289 * @returns IPRT status code. 295 290 * @param mapBlocksCommon Map of common block to process for this stream. 291 * 292 * @note Runs in recording thread. 296 293 */ 297 294 int RecordingStream::Process(RecordingBlockMap &mapBlocksCommon) … … 328 325 Frame.msTimestamp = msTimestamp; 329 326 330 int vrc2 = recordingCodecEncode(&this->CodecVideo, &Frame, NULL, 0, NULL,NULL);327 int vrc2 = recordingCodecEncode(&this->CodecVideo, &Frame, NULL, NULL); 331 328 AssertRC(vrc2); 332 329 if (RT_SUCCESS(vrc)) … … 346 343 347 344 #ifdef VBOX_WITH_AUDIO_RECORDING 348 AssertPtr(pCtx); 349 350 /* As each (enabled) screen has to get the same audio data, look for common (audio) data which needs to be 351 * written to the screen's assigned recording stream. */ 352 RecordingBlockMap::iterator itCommonBlocks = mapBlocksCommon.begin(); 353 while (itCommonBlocks != mapBlocksCommon.end()) 354 { 355 RecordingBlockList::iterator itBlock = itCommonBlocks->second->List.begin(); 356 while (itBlock != itCommonBlocks->second->List.end()) 357 { 358 RecordingBlock *pBlockCommon = (RecordingBlock *)(*itBlock); 359 switch (pBlockCommon->enmType) 360 { 361 case RECORDINGBLOCKTYPE_AUDIO: 362 { 363 PRECORDINGAUDIOFRAME pAudioFrame = (PRECORDINGAUDIOFRAME)pBlockCommon->pvData; 364 AssertPtr(pAudioFrame); 365 AssertPtr(pAudioFrame->pvBuf); 366 Assert(pAudioFrame->cbBuf); 367 368 WebMWriter::BlockData_Audio blockData = { pAudioFrame->pvBuf, pAudioFrame->cbBuf, 369 pBlockCommon->msTimestamp }; 370 AssertPtr(this->File.pWEBM); 371 int vrc2 = this->File.pWEBM->WriteBlock(this->uTrackAudio, &blockData, sizeof(blockData)); 372 AssertRC(vrc2); 373 if (RT_SUCCESS(vrc)) 374 vrc = vrc2; 375 break; 376 } 377 378 default: 379 AssertFailed(); 380 break; 381 } 382 383 Assert(pBlockCommon->cRefs); 384 pBlockCommon->cRefs--; 385 if (pBlockCommon->cRefs == 0) 386 { 387 itCommonBlocks->second->List.erase(itBlock); 388 delete pBlockCommon; 389 itBlock = itCommonBlocks->second->List.begin(); 345 /* Do we need to multiplex the common audio data to this stream? */ 346 if (this->ScreenSettings.isFeatureEnabled(RecordingFeature_Audio)) 347 { 348 /* As each (enabled) screen has to get the same audio data, look for common (audio) data which needs to be 349 * written to the screen's assigned recording stream. */ 350 RecordingBlockMap::iterator itCommonBlocks = mapBlocksCommon.begin(); 351 while (itCommonBlocks != mapBlocksCommon.end()) 352 { 353 RecordingBlockList::iterator itBlock = itCommonBlocks->second->List.begin(); 354 while (itBlock != itCommonBlocks->second->List.end()) 355 { 356 RecordingBlock *pBlockCommon = (RecordingBlock *)(*itBlock); 357 switch (pBlockCommon->enmType) 358 { 359 case RECORDINGBLOCKTYPE_AUDIO: 360 { 361 PRECORDINGAUDIOFRAME pAudioFrame = (PRECORDINGAUDIOFRAME)pBlockCommon->pvData; 362 AssertPtr(pAudioFrame); 363 AssertPtr(pAudioFrame->pvBuf); 364 Assert(pAudioFrame->cbBuf); 365 366 AssertPtr(this->File.pWEBM); 367 int vrc2 = this->File.pWEBM->WriteBlock(this->uTrackAudio, pAudioFrame->pvBuf, pAudioFrame->cbBuf, pBlockCommon->msTimestamp, pBlockCommon->uFlags); 368 AssertRC(vrc2); 369 if (RT_SUCCESS(vrc)) 370 vrc = vrc2; 371 break; 372 } 373 374 default: 375 AssertFailed(); 376 break; 377 } 378 379 Assert(pBlockCommon->cRefs); 380 pBlockCommon->cRefs--; 381 if (pBlockCommon->cRefs == 0) 382 { 383 itCommonBlocks->second->List.erase(itBlock); 384 delete pBlockCommon; 385 itBlock = itCommonBlocks->second->List.begin(); 386 } 387 else 388 ++itBlock; 389 } 390 391 /* If no entries are left over in the block map, remove it altogether. */ 392 if (itCommonBlocks->second->List.empty()) 393 { 394 delete itCommonBlocks->second; 395 mapBlocksCommon.erase(itCommonBlocks); 396 itCommonBlocks = mapBlocksCommon.begin(); 390 397 } 391 398 else 392 ++itBlock; 393 } 394 395 /* If no entries are left over in the block map, remove it altogether. */ 396 if (itCommonBlocks->second->List.empty()) 397 { 398 delete itCommonBlocks->second; 399 mapBlocksCommon.erase(itCommonBlocks); 400 itCommonBlocks = mapBlocksCommon.begin(); 401 } 402 else 403 ++itCommonBlocks; 404 405 LogFunc(("Common blocks: %zu\n", mapBlocksCommon.size())); 399 ++itCommonBlocks; 400 401 LogFunc(("Common blocks: %zu\n", mapBlocksCommon.size())); 402 } 406 403 } 407 404 #else … … 429 426 * @param uSrcHeight Height (in pixels) of the video frame. 430 427 * @param puSrcData Actual pixel data of the video frame. 431 * @param msTimestamp Timestamp (in ms) as PTS.428 * @param msTimestamp Absolute PTS timestamp (in ms). 432 429 */ 433 430 int RecordingStream::SendVideoFrame(uint32_t x, uint32_t y, uint32_t uPixelFormat, uint32_t uBPP, uint32_t uBytesPerLine, … … 436 433 lock(); 437 434 438 LogFlowFunc((" msTimestamp=%RU64\n", msTimestamp));435 LogFlowFunc(("tsAbsPTSMs=%RU64\n", msTimestamp)); 439 436 440 437 PRECORDINGCODEC pCodec = &this->CodecVideo; … … 451 448 do 452 449 { 453 if (msTimestamp < pCodec-> uLastTimeStampMs + pCodec->Parms.Video.uDelayMs)450 if (msTimestamp < pCodec->State.tsLastWrittenMs + pCodec->Parms.Video.uDelayMs) 454 451 { 455 452 vrc = VINF_RECORDING_THROTTLED; /* Respect maximum frames per second. */ … … 457 454 } 458 455 459 pCodec-> uLastTimeStampMs = msTimestamp;456 pCodec->State.tsLastWrittenMs = msTimestamp; 460 457 461 458 int xDiff = ((int)this->ScreenSettings.Video.ulWidth - (int)uSrcWidth) / 2; … … 657 654 * 658 655 * @returns IPRT status code. 659 * @param a_pCtxPointer to recording context.656 * @param pCtx Pointer to recording context. 660 657 * @param uScreen Screen number to use for this recording stream. 661 658 * @param Settings Recording screen configuration to use for initialization. 662 659 */ 663 int RecordingStream::Init(RecordingContext * a_pCtx, uint32_t uScreen, const settings::RecordingScreenSettings &Settings)664 { 665 return initInternal( a_pCtx, uScreen, Settings);660 int RecordingStream::Init(RecordingContext *pCtx, uint32_t uScreen, const settings::RecordingScreenSettings &Settings) 661 { 662 return initInternal(pCtx, uScreen, Settings); 666 663 } 667 664 … … 670 667 * 671 668 * @returns IPRT status code. 672 * @param a_pCtxPointer to recording context.669 * @param pCtx Pointer to recording context. 673 670 * @param uScreen Screen number to use for this recording stream. 674 * @param Settings Recording screen configuration to use for initialization. 675 */ 676 int RecordingStream::initInternal(RecordingContext *a_pCtx, uint32_t uScreen, const settings::RecordingScreenSettings &Settings) 671 * @param screenSettings Recording screen configuration to use for initialization. 672 */ 673 int RecordingStream::initInternal(RecordingContext *pCtx, uint32_t uScreen, 674 const settings::RecordingScreenSettings &screenSettings) 677 675 { 678 676 AssertReturn(enmState == RECORDINGSTREAMSTATE_UNINITIALIZED, VERR_WRONG_ORDER); 679 677 680 this-> pCtx = a_pCtx;678 this->m_pCtx = pCtx; 681 679 this->uTrackAudio = UINT8_MAX; 682 680 this->uTrackVideo = UINT8_MAX; 683 681 this->tsStartMs = 0; 684 682 this->uScreenID = uScreen; 685 this->ScreenSettings = Settings; 683 #ifdef VBOX_WITH_AUDIO_RECORDING 684 /* We use the codec from the recording context, as this stream only receives multiplexed data (same audio for all streams). */ 685 this->pCodecAudio = m_pCtx->GetCodecAudio(); 686 #endif 687 this->ScreenSettings = screenSettings; 686 688 687 689 settings::RecordingScreenSettings *pSettings = &this->ScreenSettings; … … 704 706 { 705 707 vrc = initVideo(*pSettings); 706 if (RT_FAILURE(vrc))707 return vrc;708 }709 710 if (fAudioEnabled)711 {712 vrc = initAudio(*pSettings);713 708 if (RT_FAILURE(vrc)) 714 709 return vrc; … … 755 750 if (fAudioEnabled) 756 751 { 757 vrc = this->File.pWEBM->AddAudioTrack(&this->CodecAudio, pSettings->Audio.uHz, pSettings->Audio.cChannels, pSettings->Audio.cBits, 752 AssertPtr(this->pCodecAudio); 753 vrc = this->File.pWEBM->AddAudioTrack(this->pCodecAudio, 754 pSettings->Audio.uHz, pSettings->Audio.cChannels, pSettings->Audio.cBits, 758 755 &this->uTrackAudio); 759 756 if (RT_FAILURE(vrc)) … … 803 800 this->fEnabled = true; 804 801 this->tsStartMs = RTTimeProgramMilliTS(); 805 } 806 else807 {808 int vrc2 = uninitInternal(); 809 AssertRC(vrc2);810 return vrc;811 } 812 813 return VINF_SUCCESS;802 803 return VINF_SUCCESS; 804 } 805 806 int vrc2 = uninitInternal(); 807 AssertRC(vrc2); 808 809 LogRel(("Recording: Stream #%RU32 initialization failed with %Rrc\n", uScreen, vrc)); 810 return vrc; 814 811 } 815 812 … … 920 917 return vrc; 921 918 919 #ifdef VBOX_WITH_AUDIO_RECORDING 920 this->pCodecAudio = NULL; 921 #endif 922 922 923 if (this->ScreenSettings.isFeatureEnabled(RecordingFeature_Video)) 923 vrc = recordingCodecDestroy(&this->CodecVideo); 924 { 925 vrc = recordingCodecFinalize(&this->CodecVideo); 926 if (RT_SUCCESS(vrc)) 927 vrc = recordingCodecDestroy(&this->CodecVideo); 928 } 924 929 925 930 if (RT_SUCCESS(vrc)) … … 934 939 } 935 940 941 /** 942 * Writes encoded data to a WebM file instance. 943 * 944 * @returns VBox status code. 945 * @param pCodec Codec which has encoded the data. 946 * @param pvData Encoded data to write. 947 * @param cbData Size (in bytes) of \a pvData. 948 * @param msAbsPTS Absolute PTS (in ms) of written data. 949 * @param uFlags Encoding flags of type RECORDINGCODEC_ENC_F_XXX. 950 */ 951 int RecordingStream::codecWriteToWebM(PRECORDINGCODEC pCodec, const void *pvData, size_t cbData, 952 uint64_t msAbsPTS, uint32_t uFlags) 953 { 954 AssertPtr(this->File.pWEBM); 955 AssertPtr(pvData); 956 Assert (cbData); 957 958 WebMWriter::WebMBlockFlags blockFlags = VBOX_WEBM_BLOCK_FLAG_NONE; 959 if (RT_LIKELY(uFlags != RECORDINGCODEC_ENC_F_NONE)) 960 { 961 /* All set. */ 962 } 963 else 964 { 965 if (uFlags & RECORDINGCODEC_ENC_F_BLOCK_IS_KEY) 966 blockFlags |= VBOX_WEBM_BLOCK_FLAG_KEY_FRAME; 967 if (uFlags & RECORDINGCODEC_ENC_F_BLOCK_IS_INVISIBLE) 968 blockFlags |= VBOX_WEBM_BLOCK_FLAG_INVISIBLE; 969 } 970 971 return this->File.pWEBM->WriteBlock( pCodec->Parms.enmType == RECORDINGCODECTYPE_AUDIO 972 ? this->uTrackAudio : this->uTrackVideo, 973 pvData, cbData, msAbsPTS, blockFlags); 974 } 975 976 /** 977 * Codec callback for writing encoded data to a recording stream. 978 * 979 * @returns VBox status code. 980 * @param pCodec Codec which has encoded the data. 981 * @param pvData Encoded data to write. 982 * @param cbData Size (in bytes) of \a pvData. 983 * @param msAbsPTS Absolute PTS (in ms) of written data. 984 * @param uFlags Encoding flags of type RECORDINGCODEC_ENC_F_XXX. 985 */ 936 986 /* static */ 937 DECLCALLBACK(int) RecordingStream::codecWriteDataCallback(PRECORDINGCODEC pCodec, const void *pvData, size_t cbData, void *pvUser) 938 { 939 RT_NOREF(pCodec); 940 987 DECLCALLBACK(int) RecordingStream::codecWriteDataCallback(PRECORDINGCODEC pCodec, const void *pvData, size_t cbData, 988 uint64_t msAbsPTS, uint32_t uFlags, void *pvUser) 989 { 941 990 RecordingStream *pThis = (RecordingStream *)pvUser; 942 991 AssertPtr(pThis); 943 992 944 AssertPtr(pThis->File.pWEBM); 945 return pThis->File.pWEBM->WriteBlock(pCodec->Parms.enmType == RECORDINGCODECTYPE_AUDIO 946 ? pThis->uTrackAudio : pThis->uTrackVideo, pvData, cbData); 993 /** @todo For now this is hardcoded to always write to a WebM file. Add other stuff later. */ 994 return pThis->codecWriteToWebM(pCodec, pvData, cbData, msAbsPTS, uFlags); 947 995 } 948 996 … … 951 999 * 952 1000 * @returns VBox status code. 953 * @param Settings Settings to use.954 */ 955 int RecordingStream::initVideo(const settings::RecordingScreenSettings & Settings)1001 * @param screenSettings Screen settings to use. 1002 */ 1003 int RecordingStream::initVideo(const settings::RecordingScreenSettings &screenSettings) 956 1004 { 957 1005 /* Sanity. */ 958 AssertReturn( Settings.Video.ulRate, VERR_INVALID_PARAMETER);959 AssertReturn( Settings.Video.ulWidth, VERR_INVALID_PARAMETER);960 AssertReturn( Settings.Video.ulHeight, VERR_INVALID_PARAMETER);961 AssertReturn( Settings.Video.ulFPS, VERR_INVALID_PARAMETER);1006 AssertReturn(screenSettings.Video.ulRate, VERR_INVALID_PARAMETER); 1007 AssertReturn(screenSettings.Video.ulWidth, VERR_INVALID_PARAMETER); 1008 AssertReturn(screenSettings.Video.ulHeight, VERR_INVALID_PARAMETER); 1009 AssertReturn(screenSettings.Video.ulFPS, VERR_INVALID_PARAMETER); 962 1010 963 1011 PRECORDINGCODEC pCodec = &this->CodecVideo; … … 967 1015 Callbacks.pfnWriteData = RecordingStream::codecWriteDataCallback; 968 1016 969 int vrc = recordingCodecCreateVideo(pCodec, Settings.Video.enmCodec);1017 int vrc = recordingCodecCreateVideo(pCodec, screenSettings.Video.enmCodec); 970 1018 if (RT_SUCCESS(vrc)) 971 vrc = recordingCodecInit(pCodec, &Callbacks, Settings);1019 vrc = recordingCodecInit(pCodec, &Callbacks, screenSettings); 972 1020 973 1021 if (RT_FAILURE(vrc)) … … 978 1026 979 1027 /** 980 * Initializes the audio part of a recording stream,981 *982 * @returns VBox status code.983 * @retval VERR_NOT_SUPPORTED if the selected codec is not supported / available.984 * @param Settings Settings to use.985 */986 int RecordingStream::initAudio(const settings::RecordingScreenSettings &Settings)987 {988 if (Settings.Audio.enmCodec == RecordingAudioCodec_None)989 return VINF_SUCCESS;990 991 if (Settings.isFeatureEnabled(RecordingFeature_Audio))992 {993 /* Sanity. */994 AssertReturn(Settings.Audio.uHz, VERR_INVALID_PARAMETER);995 AssertReturn(Settings.Audio.cBits, VERR_INVALID_PARAMETER);996 AssertReturn(Settings.Audio.cChannels, VERR_INVALID_PARAMETER);997 }998 999 PRECORDINGCODEC pCodec = &this->CodecAudio;1000 1001 RECORDINGCODECCALLBACKS Callbacks;1002 Callbacks.pvUser = this;1003 Callbacks.pfnWriteData = RecordingStream::codecWriteDataCallback;1004 1005 int vrc = recordingCodecCreateAudio(pCodec, Settings.Audio.enmCodec);1006 if (RT_SUCCESS(vrc))1007 vrc = recordingCodecInit(pCodec, &Callbacks, Settings);1008 1009 if (RT_FAILURE(vrc))1010 LogRel(("Recording: Initializing audio codec failed with %Rrc\n", vrc));1011 1012 return vrc;1013 }1014 1015 /**1016 1028 * Locks a recording stream. 1017 1029 */ -
trunk/src/VBox/Main/src-client/WebMWriter.cpp
r96175 r96229 566 566 } 567 567 568 #ifdef VBOX_WITH_LIBVPX569 /**570 * Writes VPX (VP8 video) simple data block.571 *572 * @returns IPRT status code.573 * @param a_pTrack Track ID to write data to.574 * @param a_pCfg VPX encoder configuration to use.575 * @param a_pPkt VPX packet video data packet to write.576 */577 int WebMWriter::writeSimpleBlockVP8(WebMTrack *a_pTrack, const vpx_codec_enc_cfg_t *a_pCfg, const vpx_codec_cx_pkt_t *a_pPkt)578 {579 RT_NOREF(a_pTrack);580 581 /* Calculate the absolute PTS of this frame (in ms). */582 WebMTimecodeAbs tcAbsPTSMs = a_pPkt->data.frame.pts * 1000583 * (uint64_t) a_pCfg->g_timebase.num / a_pCfg->g_timebase.den;584 if ( tcAbsPTSMs585 && tcAbsPTSMs <= a_pTrack->tcAbsLastWrittenMs)586 {587 AssertFailed(); /* Should never happen. */588 tcAbsPTSMs = a_pTrack->tcAbsLastWrittenMs + 1;589 }590 591 const bool fKeyframe = RT_BOOL(a_pPkt->data.frame.flags & VPX_FRAME_IS_KEY);592 593 uint8_t fFlags = VBOX_WEBM_BLOCK_FLAG_NONE;594 if (fKeyframe)595 fFlags |= VBOX_WEBM_BLOCK_FLAG_KEY_FRAME;596 if (a_pPkt->data.frame.flags & VPX_FRAME_IS_INVISIBLE)597 fFlags |= VBOX_WEBM_BLOCK_FLAG_INVISIBLE;598 599 return writeSimpleBlockQueued(a_pTrack,600 new WebMSimpleBlock(a_pTrack,601 tcAbsPTSMs, a_pPkt->data.frame.buf, a_pPkt->data.frame.sz, fFlags));602 }603 #endif /* VBOX_WITH_LIBVPX */604 605 /**606 * Writes an Opus (audio) simple data block.607 *608 * @returns IPRT status code.609 * @param pTrack Track ID to write data to.610 * @param pvData Pointer to simple data block to write.611 * @param cbData Size (in bytes) of simple data block to write.612 * @param tcAbsPTSMs Absolute PTS of simple data block.613 *614 * @remarks Audio blocks that have same absolute timecode as video blocks SHOULD be written before the video blocks.615 */616 int WebMWriter::writeSimpleBlockAudio(WebMTrack *pTrack, const void *pvData, size_t cbData, WebMTimecodeAbs tcAbsPTSMs)617 {618 AssertPtrReturn(pTrack, VERR_INVALID_POINTER);619 AssertPtrReturn(pvData, VERR_INVALID_POINTER);620 AssertReturn(cbData, VERR_INVALID_PARAMETER);621 622 /* Every Opus frame is a key frame. */623 const uint8_t fFlags = VBOX_WEBM_BLOCK_FLAG_KEY_FRAME;624 625 return writeSimpleBlockQueued(pTrack,626 new WebMSimpleBlock(pTrack, tcAbsPTSMs, pvData, cbData, fFlags));627 }628 629 568 /** 630 569 * Writes a data block to the specified track. … … 634 573 * @param pvData Pointer to data block to write. 635 574 * @param cbData Size (in bytes) of data block to write. 636 * /637 int WebMWriter::WriteBlock(uint8_t uTrack, const void *pvData, size_t cbData) 638 { 639 RT_NOREF(cbData); /* Only needed for assertions for now. */ 640 575 * @param tcAbsPTSMs Absolute PTS of simple data block. 576 * @param uFlags WebM block flags to use for this block. 577 */ 578 int WebMWriter::WriteBlock(uint8_t uTrack, const void *pvData, size_t cbData, WebMTimecodeAbs tcAbsPTSMs, WebMBlockFlags uFlags) 579 { 641 580 int vrc = RTCritSectEnter(&CurSeg.CritSect); 642 581 AssertRC(vrc); 643 582 583 #ifdef DEBUG /* Only validate in debug builds, to speed things up in release builds. */ 644 584 WebMTracks::iterator itTrack = CurSeg.mapTracks.find(uTrack); 645 585 if (itTrack == CurSeg.mapTracks.end()) … … 648 588 return VERR_NOT_FOUND; 649 589 } 590 #endif 650 591 651 592 WebMTrack *pTrack = itTrack->second; … … 658 599 } 659 600 660 switch (pTrack->enmType) 661 { 662 663 case WebMTrackType_Audio: 664 { 665 if ( m_enmAudioCodec == RecordingAudioCodec_Opus 666 || m_enmAudioCodec == RecordingAudioCodec_OggVorbis) 667 { 668 Assert(cbData == sizeof(WebMWriter::BlockData_Audio)); 669 WebMWriter::BlockData_Audio *pData = (WebMWriter::BlockData_Audio *)pvData; 670 vrc = writeSimpleBlockAudio(pTrack, pData->pvData, pData->cbData, pData->uPTSMs); 671 } 672 /* else nothing to do here; WebM only supports Opus or Ogg Vorbis. */ 673 break; 674 } 675 676 case WebMTrackType_Video: 677 { 678 #ifdef VBOX_WITH_LIBVPX 679 if (m_enmVideoCodec == RecordingVideoCodec_VP8) 680 { 681 Assert(cbData == sizeof(WebMWriter::BlockData_VP8)); 682 WebMWriter::BlockData_VP8 *pData = (WebMWriter::BlockData_VP8 *)pvData; 683 vrc = writeSimpleBlockVP8(pTrack, pData->pCfg, pData->pPkt); 684 } 685 else 686 #endif /* VBOX_WITH_LIBVPX */ 687 vrc = VERR_NOT_SUPPORTED; 688 break; 689 } 690 691 default: 692 vrc = VERR_NOT_SUPPORTED; 693 break; 601 try 602 { 603 vrc = writeSimpleBlockQueued(pTrack, 604 new WebMSimpleBlock(pTrack, 605 tcAbsPTSMs, pvData, cbData, uFlags)); 606 } 607 catch (std::bad_alloc &) 608 { 609 vrc = VERR_NO_MEMORY; 694 610 } 695 611
Note:
See TracChangeset
for help on using the changeset viewer.