VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/Recording.cpp@ 86506

Last change on this file since 86506 was 82968, checked in by vboxsync, 4 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
  • Property svn:mergeinfo set to (toggle deleted branches)
    /branches/VBox-3.0/src/VBox/Frontends/VBoxHeadless/VideoCapture/EncodeAndWrite.cpp58652,​70973
    /branches/VBox-3.2/src/VBox/Frontends/VBoxHeadless/VideoCapture/EncodeAndWrite.cpp66309,​66318
    /branches/VBox-4.0/src/VBox/Frontends/VBoxHeadless/VideoCapture/EncodeAndWrite.cpp70873
    /branches/VBox-4.1/src/VBox/Frontends/VBoxHeadless/VideoCapture/EncodeAndWrite.cpp74233
    /branches/VBox-4.2/src/VBox/Main/src-client/VideoRec.cpp91503-91504,​91506-91508,​91510,​91514-91515,​91521
    /branches/VBox-4.3/src/VBox/Main/src-client/VideoRec.cpp91223
    /branches/VBox-4.3/trunk/src/VBox/Main/src-client/VideoRec.cpp91223
    /branches/dsen/gui/src/VBox/Frontends/VBoxHeadless/VideoCapture/EncodeAndWrite.cpp79076-79078,​79089,​79109-79110,​79112-79113,​79127-79130,​79134,​79141,​79151,​79155,​79157-79159,​79193,​79197
    /branches/dsen/gui2/src/VBox/Frontends/VBoxHeadless/VideoCapture/EncodeAndWrite.cpp79224,​79228,​79233,​79235,​79258,​79262-79263,​79273,​79341,​79345,​79354,​79357,​79387-79388,​79559-79569,​79572-79573,​79578,​79581-79582,​79590-79591,​79598-79599,​79602-79603,​79605-79606,​79632,​79635,​79637,​79644
    /branches/dsen/gui3/src/VBox/Frontends/VBoxHeadless/VideoCapture/EncodeAndWrite.cpp79645-79692
File size: 17.1 KB
RevLine 
[45841]1/* $Id: Recording.cpp 82968 2020-02-04 10:35:17Z vboxsync $ */
2/** @file
[75392]3 * Recording context code.
[74904]4 *
5 * This code employs a separate encoding thread per recording context
6 * to keep time spent in EMT as short as possible. Each configured VM display
7 * is represented by an own recording stream, which in turn has its own rendering
8 * queue. Common recording data across all recording streams is kept in a
9 * separate queue in the recording context to minimize data duplication and
10 * multiplexing overhead in EMT.
[45841]11 */
12
13/*
[82968]14 * Copyright (C) 2012-2020 Oracle Corporation
[45841]15 *
16 * This file is part of VirtualBox Open Source Edition (OSE), as
17 * available from http://www.virtualbox.org. This file is free software;
18 * you can redistribute it and/or modify it under the terms of the GNU
19 * General Public License (GPL) as published by the Free Software
20 * Foundation, in version 2 as it comes in the "COPYING" file of the
21 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
22 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
23 */
24
[69683]25#ifdef LOG_GROUP
26# undef LOG_GROUP
27#endif
[67914]28#define LOG_GROUP LOG_GROUP_MAIN_DISPLAY
29#include "LoggingNew.h"
[65173]30
[65401]31#include <stdexcept>
[65173]32#include <vector>
33
[45878]34#include <iprt/asm.h>
[45841]35#include <iprt/assert.h>
[68453]36#include <iprt/critsect.h>
[68798]37#include <iprt/path.h>
[45878]38#include <iprt/semaphore.h>
39#include <iprt/thread.h>
[52791]40#include <iprt/time.h>
[45841]41
[71164]42#include <VBox/err.h>
[45841]43#include <VBox/com/VirtualBox.h>
44
[75251]45#include "ConsoleImpl.h"
[75344]46#include "Recording.h"
47#include "RecordingInternals.h"
48#include "RecordingStream.h"
49#include "RecordingUtils.h"
[75251]50#include "WebMWriter.h"
[45841]51
[65221]52using namespace com;
53
[68453]54#ifdef DEBUG_andy
55/** Enables dumping audio / video data for debugging reasons. */
[75354]56//# define VBOX_RECORDING_DUMP
[68453]57#endif
58
[45841]59
[75361]60RecordingContext::RecordingContext(Console *a_pConsole, const settings::RecordingSettings &a_Settings)
[75251]61 : pConsole(a_pConsole)
[75354]62 , enmState(RECORDINGSTS_UNINITIALIZED)
[75441]63 , cStreamsEnabled(0)
[75251]64{
[75354]65 int rc = RecordingContext::createInternal(a_Settings);
[75251]66 if (RT_FAILURE(rc))
67 throw rc;
68}
69
[75354]70RecordingContext::~RecordingContext(void)
[75251]71{
72 destroyInternal();
73}
74
[45841]75/**
[75354]76 * Worker thread for all streams of a recording context.
[45841]77 *
[74904]78 * For video frames, this also does the RGB/YUV conversion and encoding.
[45878]79 */
[75354]80DECLCALLBACK(int) RecordingContext::threadMain(RTTHREAD hThreadSelf, void *pvUser)
[45878]81{
[75354]82 RecordingContext *pThis = (RecordingContext *)pvUser;
[65401]83
[65435]84 /* Signal that we're up and rockin'. */
85 RTThreadUserSignal(hThreadSelf);
86
[74904]87 LogFunc(("Thread started\n"));
88
[45878]89 for (;;)
90 {
[75251]91 int rc = RTSemEventWait(pThis->WaitEvent, RT_INDEFINITE_WAIT);
[45878]92 AssertRCBreak(rc);
93
[75251]94 Log2Func(("Processing %zu streams\n", pThis->vecStreams.size()));
[75040]95
[65429]96 /** @todo r=andy This is inefficient -- as we already wake up this thread
97 * for every screen from Main, we here go again (on every wake up) through
98 * all screens. */
[75354]99 RecordingStreams::iterator itStream = pThis->vecStreams.begin();
[75251]100 while (itStream != pThis->vecStreams.end())
[45878]101 {
[75354]102 RecordingStream *pStream = (*itStream);
[65173]103
[75251]104 rc = pStream->Process(pThis->mapBlocksCommon);
[75040]105 if (RT_FAILURE(rc))
[75441]106 {
107 LogRel(("Recording: Processing stream #%RU16 failed (%Rrc)\n", pStream->GetID(), rc));
[75040]108 break;
[75441]109 }
[68453]110
[75040]111 ++itStream;
112 }
[65429]113
[75040]114 if (RT_FAILURE(rc))
[75391]115 LogRel(("Recording: Encoding thread failed (%Rrc)\n", rc));
[65173]116
[68720]117 /* Keep going in case of errors. */
118
[75251]119 if (ASMAtomicReadBool(&pThis->fShutdown))
[74904]120 {
121 LogFunc(("Thread is shutting down ...\n"));
122 break;
123 }
124
[68720]125 } /* for */
126
[74904]127 LogFunc(("Thread ended\n"));
[45878]128 return VINF_SUCCESS;
129}
130
131/**
[74904]132 * Notifies a recording context's encoding thread.
133 *
134 * @returns IPRT status code.
135 */
[75354]136int RecordingContext::threadNotify(void)
[74904]137{
[75251]138 return RTSemEventSignal(this->WaitEvent);
[74904]139}
140
141/**
[75355]142 * Creates a recording context.
[45950]143 *
144 * @returns IPRT status code.
[75392]145 * @param a_Settings Recording settings to use for context creation.
[45950]146 */
[75361]147int RecordingContext::createInternal(const settings::RecordingSettings &a_Settings)
[45950]148{
[75251]149 int rc = RTCritSectInit(&this->CritSect);
[68453]150 if (RT_FAILURE(rc))
151 return rc;
152
[75361]153 settings::RecordingScreenMap::const_iterator itScreen = a_Settings.mapScreens.begin();
[75251]154 while (itScreen != a_Settings.mapScreens.end())
[52312]155 {
[75354]156 RecordingStream *pStream = NULL;
[74904]157 try
[65173]158 {
[75354]159 pStream = new RecordingStream(this, itScreen->first /* Screen ID */, itScreen->second);
[75251]160 this->vecStreams.push_back(pStream);
[75441]161 if (itScreen->second.fEnabled)
162 this->cStreamsEnabled++;
[74904]163 }
164 catch (std::bad_alloc &)
165 {
[65173]166 rc = VERR_NO_MEMORY;
167 break;
168 }
[75313]169
170 ++itScreen;
[52312]171 }
[45956]172
[65173]173 if (RT_SUCCESS(rc))
174 {
[75251]175 this->tsStartMs = RTTimeMilliTS();
[75354]176 this->enmState = RECORDINGSTS_CREATED;
[75251]177 this->fShutdown = false;
[65435]178
[75251]179 /* Copy the settings to our context. */
180 this->Settings = a_Settings;
[68798]181
[75251]182 rc = RTSemEventCreate(&this->WaitEvent);
[65173]183 AssertRCReturn(rc, rc);
184 }
[65435]185
186 if (RT_FAILURE(rc))
[76893]187 destroyInternal();
[65173]188
189 return rc;
[45950]190}
191
[75392]192/**
193 * Starts a recording context by creating its worker thread.
194 *
195 * @returns IPRT status code.
196 */
[75354]197int RecordingContext::startInternal(void)
[75307]198{
[75354]199 if (this->enmState == RECORDINGSTS_STARTED)
[75307]200 return VINF_SUCCESS;
201
[75354]202 Assert(this->enmState == RECORDINGSTS_CREATED);
[75307]203
[75354]204 int rc = RTThreadCreate(&this->Thread, RecordingContext::threadMain, (void *)this, 0,
[75313]205 RTTHREADTYPE_MAIN_WORKER, RTTHREADFLAGS_WAITABLE, "Record");
[75307]206
207 if (RT_SUCCESS(rc)) /* Wait for the thread to start. */
208 rc = RTThreadUserWait(this->Thread, 30 * RT_MS_1SEC /* 30s timeout */);
209
210 if (RT_SUCCESS(rc))
211 {
[75391]212 LogRel(("Recording: Started\n"));
[75354]213 this->enmState = RECORDINGSTS_STARTED;
[75307]214 }
[75391]215 else
216 Log(("Recording: Failed to start (%Rrc)\n", rc));
[75307]217
218 return rc;
219}
220
[75392]221/**
222 * Stops a recording context by telling the worker thread to stop and finalizing its operation.
223 *
224 * @returns IPRT status code.
225 */
[75354]226int RecordingContext::stopInternal(void)
[75307]227{
[75354]228 if (this->enmState != RECORDINGSTS_STARTED)
[75307]229 return VINF_SUCCESS;
230
[75313]231 LogThisFunc(("Shutting down thread ...\n"));
[75307]232
233 /* Set shutdown indicator. */
234 ASMAtomicWriteBool(&this->fShutdown, true);
235
236 /* Signal the thread and wait for it to shut down. */
237 int rc = threadNotify();
238 if (RT_SUCCESS(rc))
239 rc = RTThreadWait(this->Thread, 30 * 1000 /* 10s timeout */, NULL);
240
[76893]241 lock();
242
[75313]243 if (RT_SUCCESS(rc))
244 {
[75391]245 LogRel(("Recording: Stopped\n"));
[75354]246 this->enmState = RECORDINGSTS_CREATED;
[75313]247 }
[75391]248 else
249 Log(("Recording: Failed to stop (%Rrc)\n", rc));
[75313]250
[76893]251 unlock();
252
[75313]253 LogFlowThisFunc(("%Rrc\n", rc));
[75307]254 return rc;
255}
256
[45950]257/**
[75392]258 * Destroys a recording context, internal version.
[65173]259 */
[76893]260void RecordingContext::destroyInternal(void)
[65173]261{
[76893]262 if (this->enmState == RECORDINGSTS_UNINITIALIZED)
263 return;
264
[75313]265 int rc = stopInternal();
[76893]266 AssertRCReturnVoid(rc);
[74904]267
[76893]268 lock();
269
[75313]270 rc = RTSemEventDestroy(this->WaitEvent);
[76893]271 AssertRCReturnVoid(rc);
[65173]272
[75313]273 this->WaitEvent = NIL_RTSEMEVENT;
[72014]274
[76893]275 RecordingStreams::iterator it = this->vecStreams.begin();
276 while (it != this->vecStreams.end())
[65173]277 {
[76893]278 RecordingStream *pStream = (*it);
[65173]279
[76893]280 rc = pStream->Uninit();
281 AssertRC(rc);
[68798]282
[76893]283 delete pStream;
284 pStream = NULL;
[68798]285
[76893]286 this->vecStreams.erase(it);
287 it = this->vecStreams.begin();
288 }
[68453]289
[76893]290 /* Sanity. */
291 Assert(this->vecStreams.empty());
292 Assert(this->mapBlocksCommon.size() == 0);
[65177]293
[76893]294 unlock();
[65177]295
[76893]296 if (RTCritSectIsInitialized(&this->CritSect))
297 {
298 Assert(RTCritSectGetWaiters(&this->CritSect) == -1);
[75251]299 RTCritSectDelete(&this->CritSect);
[65173]300 }
301
[76893]302 this->enmState = RECORDINGSTS_UNINITIALIZED;
[65173]303}
304
[75392]305/**
306 * Returns a recording context's current settings.
307 *
308 * @returns The recording context's current settings.
309 */
[75361]310const settings::RecordingSettings &RecordingContext::GetConfig(void) const
[45841]311{
[75251]312 return this->Settings;
313}
[65438]314
[75392]315/**
316 * Returns the recording stream for a specific screen.
317 *
318 * @returns Recording stream for a specific screen, or NULL if not found.
319 * @param uScreen Screen ID to retrieve recording stream for.
320 */
[75354]321RecordingStream *RecordingContext::getStreamInternal(unsigned uScreen) const
[75251]322{
[75354]323 RecordingStream *pStream;
[68798]324
[75251]325 try
326 {
327 pStream = this->vecStreams.at(uScreen);
328 }
329 catch (std::out_of_range &)
330 {
331 pStream = NULL;
332 }
[68798]333
[75251]334 return pStream;
[45841]335}
336
[75488]337int RecordingContext::lock(void)
338{
339 int rc = RTCritSectEnter(&this->CritSect);
340 AssertRC(rc);
341 return rc;
342}
343
344int RecordingContext::unlock(void)
345{
346 int rc = RTCritSectLeave(&this->CritSect);
347 AssertRC(rc);
348 return rc;
349}
350
[45841]351/**
[75066]352 * Retrieves a specific recording stream of a recording context.
353 *
354 * @returns Pointer to recording stream if found, or NULL if not found.
355 * @param uScreen Screen number of recording stream to look up.
356 */
[75354]357RecordingStream *RecordingContext::GetStream(unsigned uScreen) const
[75066]358{
[75251]359 return getStreamInternal(uScreen);
360}
[75066]361
[75392]362/**
363 * Returns the number of configured recording streams for a recording context.
364 *
365 * @returns Number of configured recording streams.
366 */
[75354]367size_t RecordingContext::GetStreamCount(void) const
[75251]368{
369 return this->vecStreams.size();
370}
[75066]371
[75392]372/**
373 * Creates a new recording context.
374 *
375 * @returns IPRT status code.
376 * @param a_Settings Recording settings to use for creation.
377 *
378 */
[75361]379int RecordingContext::Create(const settings::RecordingSettings &a_Settings)
[75251]380{
381 return createInternal(a_Settings);
382}
383
[75392]384/**
385 * Destroys a recording context.
386 */
[76893]387void RecordingContext::Destroy(void)
[75251]388{
[76893]389 destroyInternal();
[75251]390}
391
[75392]392/**
393 * Starts a recording context.
394 *
395 * @returns IPRT status code.
396 */
[75354]397int RecordingContext::Start(void)
[75307]398{
399 return startInternal();
400}
401
[75392]402/**
403 * Stops a recording context.
404 */
[75354]405int RecordingContext::Stop(void)
[75307]406{
407 return stopInternal();
408}
409
[75392]410/**
411 * Returns if a specific recoding feature is enabled for at least one of the attached
412 * recording streams or not.
413 *
414 * @returns \c true if at least one recording stream has this feature enabled, or \c false if
415 * no recording stream has this feature enabled.
416 * @param enmFeature Recording feature to check for.
417 */
[75488]418bool RecordingContext::IsFeatureEnabled(RecordingFeature_T enmFeature)
[75251]419{
[75488]420 lock();
421
[75354]422 RecordingStreams::const_iterator itStream = this->vecStreams.begin();
[75251]423 while (itStream != this->vecStreams.end())
[75066]424 {
[75251]425 if ((*itStream)->GetConfig().isFeatureEnabled(enmFeature))
[75488]426 {
427 unlock();
[75251]428 return true;
[75488]429 }
[75251]430 ++itStream;
[75066]431 }
432
[75488]433 unlock();
434
[75251]435 return false;
[75066]436}
437
[75313]438/**
439 * Returns if this recording context is ready to start recording.
440 *
441 * @returns @c true if recording context is ready, @c false if not.
442 */
[76893]443bool RecordingContext::IsReady(void)
[75251]444{
[76893]445 lock();
446
447 const bool fIsReady = this->enmState >= RECORDINGSTS_CREATED;
448
449 unlock();
450
451 return fIsReady;
[75251]452}
453
[75066]454/**
[75313]455 * Returns if this recording context is ready to accept new recording data for a given screen.
[50314]456 *
[75313]457 * @returns @c true if the specified screen is ready, @c false if not.
[65173]458 * @param uScreen Screen ID.
[75499]459 * @param msTimestamp Current timestamp (in ms). Currently not being used.
[50314]460 */
[75499]461bool RecordingContext::IsReady(uint32_t uScreen, uint64_t msTimestamp)
[50314]462{
[75499]463 RT_NOREF(msTimestamp);
[65438]464
[75488]465 lock();
[50314]466
[74907]467 bool fIsReady = false;
468
[75488]469 if (this->enmState != RECORDINGSTS_STARTED)
470 {
471 const RecordingStream *pStream = GetStream(uScreen);
472 if (pStream)
473 fIsReady = pStream->IsReady();
[50314]474
[75488]475 /* Note: Do not check for other constraints like the video FPS rate here,
476 * as this check then also would affect other (non-FPS related) stuff
477 * like audio data. */
478 }
[50314]479
[75488]480 unlock();
481
[74907]482 return fIsReady;
[50314]483}
484
485/**
[72014]486 * Returns whether a given recording context has been started or not.
[52312]487 *
[68798]488 * @returns true if active, false if not.
489 */
[75488]490bool RecordingContext::IsStarted(void)
[68798]491{
[75488]492 lock();
493
494 const bool fIsStarted = this->enmState == RECORDINGSTS_STARTED;
495
496 unlock();
497
498 return fIsStarted;
[68798]499}
500
501/**
502 * Checks if a specified limit for recording has been reached.
503 *
[65173]504 * @returns true if any limit has been reached.
[75488]505 */
506bool RecordingContext::IsLimitReached(void)
507{
508 lock();
509
510 LogFlowThisFunc(("cStreamsEnabled=%RU16\n", this->cStreamsEnabled));
511
512 const bool fLimitReached = this->cStreamsEnabled == 0;
513
514 unlock();
515
516 return fLimitReached;
517}
518
519/**
520 * Checks if a specified limit for recording has been reached.
521 *
522 * @returns true if any limit has been reached.
[65173]523 * @param uScreen Screen ID.
[75499]524 * @param msTimestamp Timestamp (in ms) to check for.
[52312]525 */
[75499]526bool RecordingContext::IsLimitReached(uint32_t uScreen, uint64_t msTimestamp)
[52312]527{
[75488]528 lock();
529
530 bool fLimitReached = false;
531
532 const RecordingStream *pStream = getStreamInternal(uScreen);
[65401]533 if ( !pStream
[75499]534 || pStream->IsLimitReached(msTimestamp))
[65401]535 {
[75488]536 fLimitReached = true;
[65401]537 }
[52312]538
[75488]539 unlock();
540
541 return fLimitReached;
[52312]542}
543
[75488]544DECLCALLBACK(int) RecordingContext::OnLimitReached(uint32_t uScreen, int rc)
545{
[75499]546 RT_NOREF(uScreen, rc);
[75488]547 LogFlowThisFunc(("Stream %RU32 has reached its limit (%Rrc)\n", uScreen, rc));
548
549 lock();
550
551 Assert(this->cStreamsEnabled);
552 this->cStreamsEnabled--;
553
554 LogFlowThisFunc(("cStreamsEnabled=%RU16\n", cStreamsEnabled));
555
556 unlock();
557
558 return VINF_SUCCESS;
559}
560
[52312]561/**
[65418]562 * Sends an audio frame to the video encoding thread.
563 *
[65429]564 * @thread EMT
565 *
[65418]566 * @returns IPRT status code.
567 * @param pvData Audio frame data to send.
[74904]568 * @param cbData Size (in bytes) of (encoded) audio frame data.
[75499]569 * @param msTimestamp Timestamp (in ms) of audio playback.
[65418]570 */
[75499]571int RecordingContext::SendAudioFrame(const void *pvData, size_t cbData, uint64_t msTimestamp)
[65410]572{
[75346]573#ifdef VBOX_WITH_AUDIO_RECORDING
[74904]574 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
575 AssertReturn(cbData, VERR_INVALID_PARAMETER);
[65429]576
577 /* To save time spent in EMT, do the required audio multiplexing in the encoding thread.
578 *
579 * The multiplexing is needed to supply all recorded (enabled) screens with the same
580 * audio data at the same given point in time.
581 */
[75441]582 RecordingBlock *pBlock = new RecordingBlock();
[75354]583 pBlock->enmType = RECORDINGBLOCKTYPE_AUDIO;
[65429]584
[75354]585 PRECORDINGAUDIOFRAME pFrame = (PRECORDINGAUDIOFRAME)RTMemAlloc(sizeof(RECORDINGAUDIOFRAME));
[74904]586 AssertPtrReturn(pFrame, VERR_NO_MEMORY);
[65429]587
[74904]588 pFrame->pvBuf = (uint8_t *)RTMemAlloc(cbData);
589 AssertPtrReturn(pFrame->pvBuf, VERR_NO_MEMORY);
590 pFrame->cbBuf = cbData;
[65438]591
[74904]592 memcpy(pFrame->pvBuf, pvData, cbData);
[65435]593
[75499]594 pBlock->pvData = pFrame;
595 pBlock->cbData = sizeof(RECORDINGAUDIOFRAME) + cbData;
596 pBlock->cRefs = this->cStreamsEnabled;
597 pBlock->msTimestamp = msTimestamp;
[74904]598
[76893]599 lock();
[74904]600
[76893]601 int rc;
602
[74904]603 try
604 {
[75499]605 RecordingBlockMap::iterator itBlocks = this->mapBlocksCommon.find(msTimestamp);
[75251]606 if (itBlocks == this->mapBlocksCommon.end())
[74904]607 {
[75354]608 RecordingBlocks *pRecordingBlocks = new RecordingBlocks();
609 pRecordingBlocks->List.push_back(pBlock);
[74904]610
[75499]611 this->mapBlocksCommon.insert(std::make_pair(msTimestamp, pRecordingBlocks));
[74904]612 }
613 else
614 itBlocks->second->List.push_back(pBlock);
[76893]615
616 rc = VINF_SUCCESS;
[74904]617 }
618 catch (const std::exception &ex)
619 {
620 RT_NOREF(ex);
621 rc = VERR_NO_MEMORY;
622 }
623
[76893]624 unlock();
[74904]625
[68453]626 if (RT_SUCCESS(rc))
[75251]627 rc = threadNotify();
[68453]628
629 return rc;
[65435]630#else
[75527]631 RT_NOREF(pvData, cbData, msTimestamp);
[68453]632 return VINF_SUCCESS;
[65435]633#endif
[65410]634}
635
[45841]636/**
[68798]637 * Copies a source video frame to the intermediate RGB buffer.
638 * This function is executed only once per time.
[45841]639 *
[46549]640 * @thread EMT
641 *
[45841]642 * @returns IPRT status code.
[75251]643 * @param uScreen Screen number to send video frame to.
[65415]644 * @param x Starting x coordinate of the video frame.
645 * @param y Starting y coordinate of the video frame.
646 * @param uPixelFormat Pixel format.
647 * @param uBPP Bits Per Pixel (BPP).
648 * @param uBytesPerLine Bytes per scanline.
649 * @param uSrcWidth Width of the video frame.
650 * @param uSrcHeight Height of the video frame.
651 * @param puSrcData Pointer to video frame data.
[75499]652 * @param msTimestamp Timestamp (in ms).
[45841]653 */
[75354]654int RecordingContext::SendVideoFrame(uint32_t uScreen, uint32_t x, uint32_t y,
[75392]655 uint32_t uPixelFormat, uint32_t uBPP, uint32_t uBytesPerLine,
656 uint32_t uSrcWidth, uint32_t uSrcHeight, uint8_t *puSrcData,
[75499]657 uint64_t msTimestamp)
[45841]658{
[68453]659 AssertReturn(uSrcWidth, VERR_INVALID_PARAMETER);
660 AssertReturn(uSrcHeight, VERR_INVALID_PARAMETER);
661 AssertReturn(puSrcData, VERR_INVALID_POINTER);
[45841]662
[76893]663 lock();
[74904]664
[75354]665 RecordingStream *pStream = GetStream(uScreen);
[68453]666 if (!pStream)
[74904]667 {
[76893]668 unlock();
[74904]669
[75488]670 AssertFailed();
[68453]671 return VERR_NOT_FOUND;
[74904]672 }
[68453]673
[76893]674 int rc = pStream->SendVideoFrame(x, y, uPixelFormat, uBPP, uBytesPerLine, uSrcWidth, uSrcHeight, puSrcData, msTimestamp);
[68453]675
[76893]676 unlock();
[74904]677
[68798]678 if ( RT_SUCCESS(rc)
[75488]679 && rc != VINF_RECORDING_THROTTLED) /* Only signal the thread if operation was successful. */
[68453]680 {
[75251]681 threadNotify();
[68453]682 }
[45878]683
[46549]684 return rc;
[45841]685}
[74904]686
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use