VirtualBox

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

Last change on this file was 98278, checked in by vboxsync, 16 months ago

Main/src-client: Some more rc -> hrc/vrc stuff found by grep. A build fix. bugref:10223

  • 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: 25.6 KB
RevLine 
[45841]1/* $Id: Recording.cpp 98278 2023-01-24 11:55:00Z 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/*
[98103]14 * Copyright (C) 2012-2023 Oracle and/or its affiliates.
[45841]15 *
[96407]16 * This file is part of VirtualBox base platform packages, as
17 * available from https://www.virtualbox.org.
18 *
19 * This program is free software; you can redistribute it and/or
20 * modify it under the terms of the GNU General Public License
21 * as published by the Free Software Foundation, in version 3 of the
22 * License.
23 *
24 * This program is distributed in the hope that it will be useful, but
25 * WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 * General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, see <https://www.gnu.org/licenses>.
31 *
32 * SPDX-License-Identifier: GPL-3.0-only
[45841]33 */
34
[69683]35#ifdef LOG_GROUP
36# undef LOG_GROUP
37#endif
[96140]38#define LOG_GROUP LOG_GROUP_RECORDING
[67914]39#include "LoggingNew.h"
[65173]40
[65401]41#include <stdexcept>
[65173]42#include <vector>
43
[45878]44#include <iprt/asm.h>
[45841]45#include <iprt/assert.h>
[68453]46#include <iprt/critsect.h>
[68798]47#include <iprt/path.h>
[45878]48#include <iprt/semaphore.h>
49#include <iprt/thread.h>
[52791]50#include <iprt/time.h>
[45841]51
[71164]52#include <VBox/err.h>
[45841]53#include <VBox/com/VirtualBox.h>
54
[75251]55#include "ConsoleImpl.h"
[75344]56#include "Recording.h"
57#include "RecordingInternals.h"
58#include "RecordingStream.h"
59#include "RecordingUtils.h"
[75251]60#include "WebMWriter.h"
[45841]61
[65221]62using namespace com;
63
[68453]64#ifdef DEBUG_andy
65/** Enables dumping audio / video data for debugging reasons. */
[75354]66//# define VBOX_RECORDING_DUMP
[68453]67#endif
68
[45841]69
[95645]70/**
71 * Recording context constructor.
72 *
[98278]73 * @note Will throw vrc when unable to create.
[95645]74 */
75RecordingContext::RecordingContext(void)
[96324]76 : m_pConsole(NULL)
77 , m_enmState(RECORDINGSTS_UNINITIALIZED)
78 , m_cStreamsEnabled(0)
[75251]79{
[96324]80 int vrc = RTCritSectInit(&m_CritSect);
[94950]81 if (RT_FAILURE(vrc))
82 throw vrc;
[75251]83}
84
[95645]85/**
86 * Recording context constructor.
87 *
88 * @param ptrConsole Pointer to console object this context is bound to (weak pointer).
[96229]89 * @param Settings Reference to recording settings to use for creation.
[95645]90 *
[98278]91 * @note Will throw vrc when unable to create.
[95645]92 */
[96229]93RecordingContext::RecordingContext(Console *ptrConsole, const settings::RecordingSettings &Settings)
[96324]94 : m_pConsole(NULL)
95 , m_enmState(RECORDINGSTS_UNINITIALIZED)
96 , m_cStreamsEnabled(0)
[95645]97{
[96324]98 int vrc = RTCritSectInit(&m_CritSect);
[95645]99 if (RT_FAILURE(vrc))
100 throw vrc;
101
[96229]102 vrc = RecordingContext::createInternal(ptrConsole, Settings);
[95645]103 if (RT_FAILURE(vrc))
104 throw vrc;
105}
106
[75354]107RecordingContext::~RecordingContext(void)
[75251]108{
109 destroyInternal();
[95645]110
[96324]111 if (RTCritSectIsInitialized(&m_CritSect))
112 RTCritSectDelete(&m_CritSect);
[75251]113}
114
[45841]115/**
[75354]116 * Worker thread for all streams of a recording context.
[45841]117 *
[74904]118 * For video frames, this also does the RGB/YUV conversion and encoding.
[45878]119 */
[75354]120DECLCALLBACK(int) RecordingContext::threadMain(RTTHREAD hThreadSelf, void *pvUser)
[45878]121{
[75354]122 RecordingContext *pThis = (RecordingContext *)pvUser;
[65401]123
[65435]124 /* Signal that we're up and rockin'. */
125 RTThreadUserSignal(hThreadSelf);
126
[96260]127 LogRel2(("Recording: Thread started\n"));
[74904]128
[45878]129 for (;;)
130 {
[96324]131 int vrc = RTSemEventWait(pThis->m_WaitEvent, RT_INDEFINITE_WAIT);
[94950]132 AssertRCBreak(vrc);
[45878]133
[96324]134 Log2Func(("Processing %zu streams\n", pThis->m_vecStreams.size()));
[75040]135
[96260]136 /* Process common raw blocks (data which not has been encoded yet). */
[96324]137 vrc = pThis->processCommonData(pThis->m_mapBlocksRaw, 100 /* ms timeout */);
[96260]138
[65429]139 /** @todo r=andy This is inefficient -- as we already wake up this thread
140 * for every screen from Main, we here go again (on every wake up) through
141 * all screens. */
[96324]142 RecordingStreams::iterator itStream = pThis->m_vecStreams.begin();
143 while (itStream != pThis->m_vecStreams.end())
[45878]144 {
[75354]145 RecordingStream *pStream = (*itStream);
[65173]146
[96260]147 /* Hand-in common encoded blocks. */
[96324]148 vrc = pStream->Process(pThis->m_mapBlocksEncoded);
[94950]149 if (RT_FAILURE(vrc))
[75441]150 {
[94950]151 LogRel(("Recording: Processing stream #%RU16 failed (%Rrc)\n", pStream->GetID(), vrc));
[75040]152 break;
[75441]153 }
[68453]154
[75040]155 ++itStream;
156 }
[65429]157
[94950]158 if (RT_FAILURE(vrc))
159 LogRel(("Recording: Encoding thread failed (%Rrc)\n", vrc));
[65173]160
[68720]161 /* Keep going in case of errors. */
162
[96324]163 if (ASMAtomicReadBool(&pThis->m_fShutdown))
[74904]164 {
165 LogFunc(("Thread is shutting down ...\n"));
166 break;
167 }
168
[68720]169 } /* for */
170
[96260]171 LogRel2(("Recording: Thread ended\n"));
[45878]172 return VINF_SUCCESS;
173}
174
175/**
[74904]176 * Notifies a recording context's encoding thread.
177 *
[96230]178 * @returns VBox status code.
[74904]179 */
[75354]180int RecordingContext::threadNotify(void)
[74904]181{
[96324]182 return RTSemEventSignal(m_WaitEvent);
[74904]183}
184
185/**
[96260]186 * Worker function for processing common block data.
[96229]187 *
[96260]188 * @returns VBox status code.
189 * @param mapCommon Common block map to handle.
190 * @param msTimeout Timeout to use for maximum time spending to process data.
191 * Use RT_INDEFINITE_WAIT for processing all data.
[96229]192 *
[96260]193 * @note Runs in recording thread.
194 */
195int RecordingContext::processCommonData(RecordingBlockMap &mapCommon, RTMSINTERVAL msTimeout)
196{
197 Log2Func(("Processing %zu common blocks (%RU32ms timeout)\n", mapCommon.size(), msTimeout));
198
199 int vrc = VINF_SUCCESS;
200
201 uint64_t const msStart = RTTimeMilliTS();
202 RecordingBlockMap::iterator itCommonBlocks = mapCommon.begin();
203 while (itCommonBlocks != mapCommon.end())
204 {
205 RecordingBlockList::iterator itBlock = itCommonBlocks->second->List.begin();
206 while (itBlock != itCommonBlocks->second->List.end())
207 {
208 RecordingBlock *pBlockCommon = (RecordingBlock *)(*itBlock);
209 switch (pBlockCommon->enmType)
210 {
211#ifdef VBOX_WITH_AUDIO_RECORDING
212 case RECORDINGBLOCKTYPE_AUDIO:
213 {
214 PRECORDINGAUDIOFRAME pAudioFrame = (PRECORDINGAUDIOFRAME)pBlockCommon->pvData;
215
216 RECORDINGFRAME Frame;
217 Frame.msTimestamp = pBlockCommon->msTimestamp;
218 Frame.Audio.pvBuf = pAudioFrame->pvBuf;
219 Frame.Audio.cbBuf = pAudioFrame->cbBuf;
220
[96324]221 vrc = recordingCodecEncode(&m_CodecAudio, &Frame, NULL, NULL);
[96260]222 break;
223 }
224#endif /* VBOX_WITH_AUDIO_RECORDING */
225 default:
226 /* Skip unknown stuff. */
227 break;
228 }
229
230 itCommonBlocks->second->List.erase(itBlock);
231 delete pBlockCommon;
232 itBlock = itCommonBlocks->second->List.begin();
233
234 if (RT_FAILURE(vrc) || RTTimeMilliTS() > msStart + msTimeout)
235 break;
236 }
237
238 /* If no entries are left over in the block map, remove it altogether. */
239 if (itCommonBlocks->second->List.empty())
240 {
241 delete itCommonBlocks->second;
242 mapCommon.erase(itCommonBlocks);
243 itCommonBlocks = mapCommon.begin();
244 }
245 else
246 ++itCommonBlocks;
247
248 if (RT_FAILURE(vrc))
249 break;
250 }
251
252 return vrc;
253}
254
255/**
256 * Writes common block data (i.e. shared / the same) in all streams.
257 *
[96229]258 * The multiplexing is needed to supply all recorded (enabled) screens with the same
259 * data at the same given point in time.
260 *
261 * Currently this only is being used for audio data.
[96260]262 *
263 * @returns VBox status code.
264 * @param mapCommon Common block map to write data to.
265 * @param pCodec Pointer to codec instance which has written the data.
266 * @param pvData Pointer to written data (encoded).
267 * @param cbData Size (in bytes) of \a pvData.
268 * @param msTimestamp Absolute PTS (in ms) of the written data.
269 * @param uFlags Encoding flags of type RECORDINGCODEC_ENC_F_XXX.
[96229]270 */
[96260]271int RecordingContext::writeCommonData(RecordingBlockMap &mapCommon, PRECORDINGCODEC pCodec, const void *pvData, size_t cbData,
272 uint64_t msTimestamp, uint32_t uFlags)
[96229]273{
274 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
275 AssertReturn(cbData, VERR_INVALID_PARAMETER);
276
[96260]277 LogFlowFunc(("pCodec=%p, cbData=%zu, msTimestamp=%zu, uFlags=%#x\n",
278 pCodec, cbData, msTimestamp, uFlags));
[96229]279
280 /** @todo Optimize this! Three allocations in here! */
281
282 RECORDINGBLOCKTYPE const enmType = pCodec->Parms.enmType == RECORDINGCODECTYPE_AUDIO
283 ? RECORDINGBLOCKTYPE_AUDIO : RECORDINGBLOCKTYPE_UNKNOWN;
284
285 AssertReturn(enmType != RECORDINGBLOCKTYPE_UNKNOWN, VERR_NOT_SUPPORTED);
286
287 RecordingBlock *pBlock = new RecordingBlock();
288
289 switch (enmType)
290 {
291 case RECORDINGBLOCKTYPE_AUDIO:
292 {
293 PRECORDINGAUDIOFRAME pFrame = (PRECORDINGAUDIOFRAME)RTMemAlloc(sizeof(RECORDINGAUDIOFRAME));
294 AssertPtrReturn(pFrame, VERR_NO_MEMORY);
295
296 pFrame->pvBuf = (uint8_t *)RTMemAlloc(cbData);
297 AssertPtrReturn(pFrame->pvBuf, VERR_NO_MEMORY);
298 pFrame->cbBuf = cbData;
299
300 memcpy(pFrame->pvBuf, pvData, cbData);
301
302 pBlock->enmType = enmType;
303 pBlock->pvData = pFrame;
304 pBlock->cbData = sizeof(RECORDINGAUDIOFRAME) + cbData;
[96324]305 pBlock->cRefs = m_cStreamsEnabled;
[96260]306 pBlock->msTimestamp = msTimestamp;
[96229]307 pBlock->uFlags = uFlags;
308
309 break;
310 }
311
312 default:
313 AssertFailed();
314 break;
315 }
316
317 lock();
318
319 int vrc;
320
321 try
322 {
[96260]323 RecordingBlockMap::iterator itBlocks = mapCommon.find(msTimestamp);
324 if (itBlocks == mapCommon.end())
[96229]325 {
326 RecordingBlocks *pRecordingBlocks = new RecordingBlocks();
327 pRecordingBlocks->List.push_back(pBlock);
328
[96260]329 mapCommon.insert(std::make_pair(msTimestamp, pRecordingBlocks));
[96229]330 }
331 else
332 itBlocks->second->List.push_back(pBlock);
333
334 vrc = VINF_SUCCESS;
335 }
336 catch (const std::exception &ex)
337 {
338 RT_NOREF(ex);
339 vrc = VERR_NO_MEMORY;
340 }
341
342 unlock();
343
344 if (RT_SUCCESS(vrc))
345 vrc = threadNotify();
346
347 return vrc;
348}
349
350#ifdef VBOX_WITH_AUDIO_RECORDING
351/**
[96260]352 * Callback function for writing encoded audio data into the common encoded block map.
[96229]353 *
[96260]354 * This is called by the audio codec when finishing encoding audio data.
355 *
[96229]356 * @copydoc RECORDINGCODECCALLBACKS::pfnWriteData
357 */
358/* static */
359DECLCALLBACK(int) RecordingContext::audioCodecWriteDataCallback(PRECORDINGCODEC pCodec, const void *pvData, size_t cbData,
360 uint64_t msAbsPTS, uint32_t uFlags, void *pvUser)
361{
362 RecordingContext *pThis = (RecordingContext *)pvUser;
[96324]363 return pThis->writeCommonData(pThis->m_mapBlocksEncoded, pCodec, pvData, cbData, msAbsPTS, uFlags);
[96229]364}
365
366/**
367 * Initializes the audio codec for a (multiplexing) recording context.
368 *
369 * @returns VBox status code.
370 * @param screenSettings Reference to recording screen settings to use for initialization.
371 */
372int RecordingContext::audioInit(const settings::RecordingScreenSettings &screenSettings)
373{
374 RecordingAudioCodec_T const enmCodec = screenSettings.Audio.enmCodec;
375
376 if (enmCodec == RecordingAudioCodec_None)
377 {
378 LogRel2(("Recording: No audio codec configured, skipping audio init\n"));
379 return VINF_SUCCESS;
380 }
381
382 RECORDINGCODECCALLBACKS Callbacks;
383 Callbacks.pvUser = this;
384 Callbacks.pfnWriteData = RecordingContext::audioCodecWriteDataCallback;
385
[96324]386 int vrc = recordingCodecCreateAudio(&m_CodecAudio, enmCodec);
[96229]387 if (RT_SUCCESS(vrc))
[96324]388 vrc = recordingCodecInit(&m_CodecAudio, &Callbacks, screenSettings);
[96229]389
390 return vrc;
391}
392#endif /* VBOX_WITH_AUDIO_RECORDING */
393
394/**
[75355]395 * Creates a recording context.
[45950]396 *
[96230]397 * @returns VBox status code.
[95645]398 * @param ptrConsole Pointer to console object this context is bound to (weak pointer).
[96229]399 * @param Settings Reference to recording settings to use for creation.
[45950]400 */
[96229]401int RecordingContext::createInternal(Console *ptrConsole, const settings::RecordingSettings &Settings)
[45950]402{
[95645]403 int vrc = VINF_SUCCESS;
[68453]404
[96229]405 /* Copy the settings to our context. */
406 m_Settings = Settings;
407
408#ifdef VBOX_WITH_AUDIO_RECORDING
409 settings::RecordingScreenSettingsMap::const_iterator itScreen0 = m_Settings.mapScreens.begin();
410 AssertReturn(itScreen0 != m_Settings.mapScreens.end(), VERR_WRONG_ORDER);
411
412 /* We always use the audio settings from screen 0, as we multiplex the audio data anyway. */
413 settings::RecordingScreenSettings const &screen0Settings = itScreen0->second;
414
415 vrc = this->audioInit(screen0Settings);
416 if (RT_FAILURE(vrc))
417 return vrc;
418#endif
419
[96324]420 m_pConsole = ptrConsole;
[95645]421
[96229]422 settings::RecordingScreenSettingsMap::const_iterator itScreen = m_Settings.mapScreens.begin();
423 while (itScreen != m_Settings.mapScreens.end())
[52312]424 {
[75354]425 RecordingStream *pStream = NULL;
[74904]426 try
[65173]427 {
[75354]428 pStream = new RecordingStream(this, itScreen->first /* Screen ID */, itScreen->second);
[96324]429 m_vecStreams.push_back(pStream);
[75441]430 if (itScreen->second.fEnabled)
[96324]431 m_cStreamsEnabled++;
[96229]432 LogFlowFunc(("pStream=%p\n", pStream));
[74904]433 }
434 catch (std::bad_alloc &)
435 {
[94950]436 vrc = VERR_NO_MEMORY;
[65173]437 break;
438 }
[98278]439 catch (int vrc_thrown) /* Catch vrc thrown by constructor. */
[96137]440 {
441 vrc = vrc_thrown;
442 break;
443 }
[75313]444
445 ++itScreen;
[52312]446 }
[45956]447
[94950]448 if (RT_SUCCESS(vrc))
[65173]449 {
[96324]450 m_tsStartMs = RTTimeMilliTS();
451 m_enmState = RECORDINGSTS_CREATED;
452 m_fShutdown = false;
[65435]453
[96324]454 vrc = RTSemEventCreate(&m_WaitEvent);
[94950]455 AssertRCReturn(vrc, vrc);
[65173]456 }
[65435]457
[94950]458 if (RT_FAILURE(vrc))
[76893]459 destroyInternal();
[65173]460
[94950]461 return vrc;
[45950]462}
463
[75392]464/**
465 * Starts a recording context by creating its worker thread.
466 *
[96230]467 * @returns VBox status code.
[75392]468 */
[75354]469int RecordingContext::startInternal(void)
[75307]470{
[96324]471 if (m_enmState == RECORDINGSTS_STARTED)
[75307]472 return VINF_SUCCESS;
473
[96324]474 Assert(m_enmState == RECORDINGSTS_CREATED);
[75307]475
[96324]476 int vrc = RTThreadCreate(&m_Thread, RecordingContext::threadMain, (void *)this, 0,
[94950]477 RTTHREADTYPE_MAIN_WORKER, RTTHREADFLAGS_WAITABLE, "Record");
[75307]478
[94950]479 if (RT_SUCCESS(vrc)) /* Wait for the thread to start. */
[96324]480 vrc = RTThreadUserWait(m_Thread, RT_MS_30SEC /* 30s timeout */);
[75307]481
[94950]482 if (RT_SUCCESS(vrc))
[75307]483 {
[75391]484 LogRel(("Recording: Started\n"));
[96324]485 m_enmState = RECORDINGSTS_STARTED;
[75307]486 }
[75391]487 else
[94950]488 Log(("Recording: Failed to start (%Rrc)\n", vrc));
[75307]489
[94950]490 return vrc;
[75307]491}
492
[75392]493/**
494 * Stops a recording context by telling the worker thread to stop and finalizing its operation.
495 *
[96230]496 * @returns VBox status code.
[75392]497 */
[75354]498int RecordingContext::stopInternal(void)
[75307]499{
[96324]500 if (m_enmState != RECORDINGSTS_STARTED)
[75307]501 return VINF_SUCCESS;
502
[75313]503 LogThisFunc(("Shutting down thread ...\n"));
[75307]504
505 /* Set shutdown indicator. */
[96324]506 ASMAtomicWriteBool(&m_fShutdown, true);
[75307]507
508 /* Signal the thread and wait for it to shut down. */
[94950]509 int vrc = threadNotify();
510 if (RT_SUCCESS(vrc))
[96324]511 vrc = RTThreadWait(m_Thread, RT_MS_30SEC /* 30s timeout */, NULL);
[75307]512
[76893]513 lock();
514
[94950]515 if (RT_SUCCESS(vrc))
[75313]516 {
[75391]517 LogRel(("Recording: Stopped\n"));
[96324]518 m_enmState = RECORDINGSTS_CREATED;
[75313]519 }
[75391]520 else
[94950]521 Log(("Recording: Failed to stop (%Rrc)\n", vrc));
[75313]522
[76893]523 unlock();
524
[94950]525 LogFlowThisFunc(("%Rrc\n", vrc));
526 return vrc;
[75307]527}
528
[45950]529/**
[75392]530 * Destroys a recording context, internal version.
[65173]531 */
[76893]532void RecordingContext::destroyInternal(void)
[65173]533{
[95645]534 lock();
535
[96324]536 if (m_enmState == RECORDINGSTS_UNINITIALIZED)
[95645]537 {
538 unlock();
[76893]539 return;
[95645]540 }
[76893]541
[94950]542 int vrc = stopInternal();
543 AssertRCReturnVoid(vrc);
[74904]544
[96324]545 vrc = RTSemEventDestroy(m_WaitEvent);
[94950]546 AssertRCReturnVoid(vrc);
[65173]547
[96324]548 m_WaitEvent = NIL_RTSEMEVENT;
[72014]549
[96324]550 RecordingStreams::iterator it = m_vecStreams.begin();
551 while (it != m_vecStreams.end())
[65173]552 {
[76893]553 RecordingStream *pStream = (*it);
[65173]554
[94950]555 vrc = pStream->Uninit();
556 AssertRC(vrc);
[68798]557
[76893]558 delete pStream;
559 pStream = NULL;
[68798]560
[96324]561 m_vecStreams.erase(it);
562 it = m_vecStreams.begin();
[76893]563 }
[68453]564
[76893]565 /* Sanity. */
[96324]566 Assert(m_vecStreams.empty());
567 Assert(m_mapBlocksRaw.size() == 0);
568 Assert(m_mapBlocksEncoded.size() == 0);
[65177]569
[96324]570 m_enmState = RECORDINGSTS_UNINITIALIZED;
[95645]571
[76893]572 unlock();
[65173]573}
574
[75392]575/**
576 * Returns a recording context's current settings.
577 *
578 * @returns The recording context's current settings.
579 */
[75361]580const settings::RecordingSettings &RecordingContext::GetConfig(void) const
[45841]581{
[96324]582 return m_Settings;
[75251]583}
[65438]584
[75392]585/**
586 * Returns the recording stream for a specific screen.
587 *
588 * @returns Recording stream for a specific screen, or NULL if not found.
589 * @param uScreen Screen ID to retrieve recording stream for.
590 */
[75354]591RecordingStream *RecordingContext::getStreamInternal(unsigned uScreen) const
[75251]592{
[75354]593 RecordingStream *pStream;
[68798]594
[75251]595 try
596 {
[96324]597 pStream = m_vecStreams.at(uScreen);
[75251]598 }
599 catch (std::out_of_range &)
600 {
601 pStream = NULL;
602 }
[68798]603
[75251]604 return pStream;
[45841]605}
606
[96229]607/**
608 * Locks the recording context for serializing access.
609 *
610 * @returns VBox status code.
611 */
[75488]612int RecordingContext::lock(void)
613{
[96324]614 int vrc = RTCritSectEnter(&m_CritSect);
[94950]615 AssertRC(vrc);
616 return vrc;
[75488]617}
618
[96229]619/**
620 * Unlocks the recording context for serializing access.
621 *
622 * @returns VBox status code.
623 */
[75488]624int RecordingContext::unlock(void)
625{
[96324]626 int vrc = RTCritSectLeave(&m_CritSect);
[94950]627 AssertRC(vrc);
628 return vrc;
[75488]629}
630
[45841]631/**
[75066]632 * Retrieves a specific recording stream of a recording context.
633 *
634 * @returns Pointer to recording stream if found, or NULL if not found.
635 * @param uScreen Screen number of recording stream to look up.
636 */
[75354]637RecordingStream *RecordingContext::GetStream(unsigned uScreen) const
[75066]638{
[75251]639 return getStreamInternal(uScreen);
640}
[75066]641
[75392]642/**
643 * Returns the number of configured recording streams for a recording context.
644 *
645 * @returns Number of configured recording streams.
646 */
[75354]647size_t RecordingContext::GetStreamCount(void) const
[75251]648{
[96324]649 return m_vecStreams.size();
[75251]650}
[75066]651
[75392]652/**
653 * Creates a new recording context.
654 *
[96230]655 * @returns VBox status code.
[95645]656 * @param ptrConsole Pointer to console object this context is bound to (weak pointer).
[96229]657 * @param Settings Reference to recording settings to use for creation.
[75392]658 */
[96229]659int RecordingContext::Create(Console *ptrConsole, const settings::RecordingSettings &Settings)
[75251]660{
[96229]661 return createInternal(ptrConsole, Settings);
[75251]662}
663
[75392]664/**
665 * Destroys a recording context.
666 */
[76893]667void RecordingContext::Destroy(void)
[75251]668{
[76893]669 destroyInternal();
[75251]670}
671
[75392]672/**
673 * Starts a recording context.
674 *
[96230]675 * @returns VBox status code.
[75392]676 */
[75354]677int RecordingContext::Start(void)
[75307]678{
679 return startInternal();
680}
681
[75392]682/**
683 * Stops a recording context.
684 */
[75354]685int RecordingContext::Stop(void)
[75307]686{
687 return stopInternal();
688}
689
[75392]690/**
691 * Returns if a specific recoding feature is enabled for at least one of the attached
692 * recording streams or not.
693 *
[96323]694 * @returns @c true if at least one recording stream has this feature enabled, or @c false if
[75392]695 * no recording stream has this feature enabled.
696 * @param enmFeature Recording feature to check for.
697 */
[75488]698bool RecordingContext::IsFeatureEnabled(RecordingFeature_T enmFeature)
[75251]699{
[75488]700 lock();
701
[96324]702 RecordingStreams::const_iterator itStream = m_vecStreams.begin();
703 while (itStream != m_vecStreams.end())
[75066]704 {
[75251]705 if ((*itStream)->GetConfig().isFeatureEnabled(enmFeature))
[75488]706 {
707 unlock();
[75251]708 return true;
[75488]709 }
[75251]710 ++itStream;
[75066]711 }
712
[75488]713 unlock();
714
[75251]715 return false;
[75066]716}
717
[75313]718/**
719 * Returns if this recording context is ready to start recording.
720 *
721 * @returns @c true if recording context is ready, @c false if not.
722 */
[76893]723bool RecordingContext::IsReady(void)
[75251]724{
[76893]725 lock();
726
[96324]727 const bool fIsReady = m_enmState >= RECORDINGSTS_CREATED;
[76893]728
729 unlock();
730
731 return fIsReady;
[75251]732}
733
[75066]734/**
[75313]735 * Returns if this recording context is ready to accept new recording data for a given screen.
[50314]736 *
[75313]737 * @returns @c true if the specified screen is ready, @c false if not.
[65173]738 * @param uScreen Screen ID.
[96284]739 * @param msTimestamp Timestamp (PTS, in ms). Currently not being used.
[50314]740 */
[75499]741bool RecordingContext::IsReady(uint32_t uScreen, uint64_t msTimestamp)
[50314]742{
[75499]743 RT_NOREF(msTimestamp);
[65438]744
[75488]745 lock();
[50314]746
[74907]747 bool fIsReady = false;
748
[96324]749 if (m_enmState != RECORDINGSTS_STARTED)
[75488]750 {
[96283]751 const RecordingStream *pStream = getStreamInternal(uScreen);
[75488]752 if (pStream)
753 fIsReady = pStream->IsReady();
[50314]754
[75488]755 /* Note: Do not check for other constraints like the video FPS rate here,
756 * as this check then also would affect other (non-FPS related) stuff
757 * like audio data. */
758 }
[50314]759
[75488]760 unlock();
761
[74907]762 return fIsReady;
[50314]763}
764
765/**
[72014]766 * Returns whether a given recording context has been started or not.
[52312]767 *
[68798]768 * @returns true if active, false if not.
769 */
[75488]770bool RecordingContext::IsStarted(void)
[68798]771{
[75488]772 lock();
773
[96324]774 const bool fIsStarted = m_enmState == RECORDINGSTS_STARTED;
[75488]775
776 unlock();
777
778 return fIsStarted;
[68798]779}
780
781/**
782 * Checks if a specified limit for recording has been reached.
783 *
[65173]784 * @returns true if any limit has been reached.
[75488]785 */
786bool RecordingContext::IsLimitReached(void)
787{
788 lock();
789
[96324]790 LogFlowThisFunc(("cStreamsEnabled=%RU16\n", m_cStreamsEnabled));
[75488]791
[96324]792 const bool fLimitReached = m_cStreamsEnabled == 0;
[75488]793
794 unlock();
795
796 return fLimitReached;
797}
798
799/**
800 * Checks if a specified limit for recording has been reached.
801 *
802 * @returns true if any limit has been reached.
[65173]803 * @param uScreen Screen ID.
[96284]804 * @param msTimestamp Timestamp (PTS, in ms) to check for.
[52312]805 */
[75499]806bool RecordingContext::IsLimitReached(uint32_t uScreen, uint64_t msTimestamp)
[52312]807{
[75488]808 lock();
809
810 bool fLimitReached = false;
811
812 const RecordingStream *pStream = getStreamInternal(uScreen);
[65401]813 if ( !pStream
[75499]814 || pStream->IsLimitReached(msTimestamp))
[65401]815 {
[75488]816 fLimitReached = true;
[65401]817 }
[52312]818
[75488]819 unlock();
820
821 return fLimitReached;
[52312]822}
823
[96322]824/**
825 * Returns if a specific screen needs to be fed with an update or not.
826 *
827 * @returns @c true if an update is needed, @c false if not.
828 * @param uScreen Screen ID to retrieve update stats for.
829 * @param msTimestamp Timestamp (PTS, in ms).
830 */
831bool RecordingContext::NeedsUpdate( uint32_t uScreen, uint64_t msTimestamp)
832{
833 lock();
834
835 bool fNeedsUpdate = false;
836
[96324]837 if (m_enmState == RECORDINGSTS_STARTED)
[96322]838 {
[97404]839#ifdef VBOX_WITH_AUDIO_RECORDING
[96324]840 if ( recordingCodecIsInitialized(&m_CodecAudio)
841 && recordingCodecGetWritable(&m_CodecAudio, msTimestamp) > 0)
[96322]842 {
843 fNeedsUpdate = true;
844 }
[97404]845#endif /* VBOX_WITH_AUDIO_RECORDING */
[96322]846
847 if (!fNeedsUpdate)
848 {
849 const RecordingStream *pStream = getStreamInternal(uScreen);
850 if (pStream)
851 fNeedsUpdate = pStream->NeedsUpdate(msTimestamp);
852 }
853 }
854
855 unlock();
856
857 return fNeedsUpdate;
858}
859
[98278]860DECLCALLBACK(int) RecordingContext::OnLimitReached(uint32_t uScreen, int vrc)
[75488]861{
[98278]862 RT_NOREF(uScreen, vrc);
863 LogFlowThisFunc(("Stream %RU32 has reached its limit (%Rrc)\n", uScreen, vrc));
[75488]864
865 lock();
866
[96324]867 Assert(m_cStreamsEnabled);
868 m_cStreamsEnabled--;
[75488]869
[96324]870 LogFlowThisFunc(("cStreamsEnabled=%RU16\n", m_cStreamsEnabled));
[75488]871
872 unlock();
873
874 return VINF_SUCCESS;
875}
876
[52312]877/**
[96284]878 * Sends an audio frame to the recording thread.
[65418]879 *
[96230]880 * @returns VBox status code.
[65418]881 * @param pvData Audio frame data to send.
[74904]882 * @param cbData Size (in bytes) of (encoded) audio frame data.
[96284]883 * @param msTimestamp Timestamp (PTS, in ms) of audio playback.
[65418]884 */
[75499]885int RecordingContext::SendAudioFrame(const void *pvData, size_t cbData, uint64_t msTimestamp)
[65410]886{
[75346]887#ifdef VBOX_WITH_AUDIO_RECORDING
[96324]888 return writeCommonData(m_mapBlocksRaw, &m_CodecAudio,
[96260]889 pvData, cbData, msTimestamp, RECORDINGCODEC_ENC_F_BLOCK_IS_KEY);
[65435]890#else
[75527]891 RT_NOREF(pvData, cbData, msTimestamp);
[96260]892 return VERR_NOT_SUPPORTED;
[65435]893#endif
[65410]894}
895
[45841]896/**
[96284]897 * Sends a video frame to the recording thread.
[45841]898 *
[46549]899 * @thread EMT
900 *
[96230]901 * @returns VBox status code.
[75251]902 * @param uScreen Screen number to send video frame to.
[65415]903 * @param x Starting x coordinate of the video frame.
904 * @param y Starting y coordinate of the video frame.
905 * @param uPixelFormat Pixel format.
906 * @param uBPP Bits Per Pixel (BPP).
907 * @param uBytesPerLine Bytes per scanline.
908 * @param uSrcWidth Width of the video frame.
909 * @param uSrcHeight Height of the video frame.
910 * @param puSrcData Pointer to video frame data.
[96284]911 * @param msTimestamp Timestamp (PTS, in ms).
[45841]912 */
[75354]913int RecordingContext::SendVideoFrame(uint32_t uScreen, uint32_t x, uint32_t y,
[75392]914 uint32_t uPixelFormat, uint32_t uBPP, uint32_t uBytesPerLine,
915 uint32_t uSrcWidth, uint32_t uSrcHeight, uint8_t *puSrcData,
[75499]916 uint64_t msTimestamp)
[45841]917{
[68453]918 AssertReturn(uSrcWidth, VERR_INVALID_PARAMETER);
919 AssertReturn(uSrcHeight, VERR_INVALID_PARAMETER);
920 AssertReturn(puSrcData, VERR_INVALID_POINTER);
[45841]921
[76893]922 lock();
[74904]923
[96283]924 RecordingStream *pStream = getStreamInternal(uScreen);
[68453]925 if (!pStream)
[74904]926 {
[76893]927 unlock();
[74904]928
[75488]929 AssertFailed();
[68453]930 return VERR_NOT_FOUND;
[74904]931 }
[68453]932
[94950]933 int vrc = pStream->SendVideoFrame(x, y, uPixelFormat, uBPP, uBytesPerLine, uSrcWidth, uSrcHeight, puSrcData, msTimestamp);
[68453]934
[76893]935 unlock();
[74904]936
[94950]937 if ( RT_SUCCESS(vrc)
938 && vrc != VINF_RECORDING_THROTTLED) /* Only signal the thread if operation was successful. */
[68453]939 {
[75251]940 threadNotify();
[68453]941 }
[45878]942
[94950]943 return vrc;
[45841]944}
[74904]945
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use