VirtualBox

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

Last change on this file since 98103 was 98103, checked in by vboxsync, 23 months 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: 25.6 KB
Line 
1/* $Id: Recording.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * Recording context code.
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.
11 */
12
13/*
14 * Copyright (C) 2012-2023 Oracle and/or its affiliates.
15 *
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
33 */
34
35#ifdef LOG_GROUP
36# undef LOG_GROUP
37#endif
38#define LOG_GROUP LOG_GROUP_RECORDING
39#include "LoggingNew.h"
40
41#include <stdexcept>
42#include <vector>
43
44#include <iprt/asm.h>
45#include <iprt/assert.h>
46#include <iprt/critsect.h>
47#include <iprt/path.h>
48#include <iprt/semaphore.h>
49#include <iprt/thread.h>
50#include <iprt/time.h>
51
52#include <VBox/err.h>
53#include <VBox/com/VirtualBox.h>
54
55#include "ConsoleImpl.h"
56#include "Recording.h"
57#include "RecordingInternals.h"
58#include "RecordingStream.h"
59#include "RecordingUtils.h"
60#include "WebMWriter.h"
61
62using namespace com;
63
64#ifdef DEBUG_andy
65/** Enables dumping audio / video data for debugging reasons. */
66//# define VBOX_RECORDING_DUMP
67#endif
68
69
70/**
71 * Recording context constructor.
72 *
73 * @note Will throw rc when unable to create.
74 */
75RecordingContext::RecordingContext(void)
76 : m_pConsole(NULL)
77 , m_enmState(RECORDINGSTS_UNINITIALIZED)
78 , m_cStreamsEnabled(0)
79{
80 int vrc = RTCritSectInit(&m_CritSect);
81 if (RT_FAILURE(vrc))
82 throw vrc;
83}
84
85/**
86 * Recording context constructor.
87 *
88 * @param ptrConsole Pointer to console object this context is bound to (weak pointer).
89 * @param Settings Reference to recording settings to use for creation.
90 *
91 * @note Will throw rc when unable to create.
92 */
93RecordingContext::RecordingContext(Console *ptrConsole, const settings::RecordingSettings &Settings)
94 : m_pConsole(NULL)
95 , m_enmState(RECORDINGSTS_UNINITIALIZED)
96 , m_cStreamsEnabled(0)
97{
98 int vrc = RTCritSectInit(&m_CritSect);
99 if (RT_FAILURE(vrc))
100 throw vrc;
101
102 vrc = RecordingContext::createInternal(ptrConsole, Settings);
103 if (RT_FAILURE(vrc))
104 throw vrc;
105}
106
107RecordingContext::~RecordingContext(void)
108{
109 destroyInternal();
110
111 if (RTCritSectIsInitialized(&m_CritSect))
112 RTCritSectDelete(&m_CritSect);
113}
114
115/**
116 * Worker thread for all streams of a recording context.
117 *
118 * For video frames, this also does the RGB/YUV conversion and encoding.
119 */
120DECLCALLBACK(int) RecordingContext::threadMain(RTTHREAD hThreadSelf, void *pvUser)
121{
122 RecordingContext *pThis = (RecordingContext *)pvUser;
123
124 /* Signal that we're up and rockin'. */
125 RTThreadUserSignal(hThreadSelf);
126
127 LogRel2(("Recording: Thread started\n"));
128
129 for (;;)
130 {
131 int vrc = RTSemEventWait(pThis->m_WaitEvent, RT_INDEFINITE_WAIT);
132 AssertRCBreak(vrc);
133
134 Log2Func(("Processing %zu streams\n", pThis->m_vecStreams.size()));
135
136 /* Process common raw blocks (data which not has been encoded yet). */
137 vrc = pThis->processCommonData(pThis->m_mapBlocksRaw, 100 /* ms timeout */);
138
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. */
142 RecordingStreams::iterator itStream = pThis->m_vecStreams.begin();
143 while (itStream != pThis->m_vecStreams.end())
144 {
145 RecordingStream *pStream = (*itStream);
146
147 /* Hand-in common encoded blocks. */
148 vrc = pStream->Process(pThis->m_mapBlocksEncoded);
149 if (RT_FAILURE(vrc))
150 {
151 LogRel(("Recording: Processing stream #%RU16 failed (%Rrc)\n", pStream->GetID(), vrc));
152 break;
153 }
154
155 ++itStream;
156 }
157
158 if (RT_FAILURE(vrc))
159 LogRel(("Recording: Encoding thread failed (%Rrc)\n", vrc));
160
161 /* Keep going in case of errors. */
162
163 if (ASMAtomicReadBool(&pThis->m_fShutdown))
164 {
165 LogFunc(("Thread is shutting down ...\n"));
166 break;
167 }
168
169 } /* for */
170
171 LogRel2(("Recording: Thread ended\n"));
172 return VINF_SUCCESS;
173}
174
175/**
176 * Notifies a recording context's encoding thread.
177 *
178 * @returns VBox status code.
179 */
180int RecordingContext::threadNotify(void)
181{
182 return RTSemEventSignal(m_WaitEvent);
183}
184
185/**
186 * Worker function for processing common block data.
187 *
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.
192 *
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
221 vrc = recordingCodecEncode(&m_CodecAudio, &Frame, NULL, NULL);
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 *
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.
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.
270 */
271int RecordingContext::writeCommonData(RecordingBlockMap &mapCommon, PRECORDINGCODEC pCodec, const void *pvData, size_t cbData,
272 uint64_t msTimestamp, uint32_t uFlags)
273{
274 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
275 AssertReturn(cbData, VERR_INVALID_PARAMETER);
276
277 LogFlowFunc(("pCodec=%p, cbData=%zu, msTimestamp=%zu, uFlags=%#x\n",
278 pCodec, cbData, msTimestamp, uFlags));
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;
305 pBlock->cRefs = m_cStreamsEnabled;
306 pBlock->msTimestamp = msTimestamp;
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 {
323 RecordingBlockMap::iterator itBlocks = mapCommon.find(msTimestamp);
324 if (itBlocks == mapCommon.end())
325 {
326 RecordingBlocks *pRecordingBlocks = new RecordingBlocks();
327 pRecordingBlocks->List.push_back(pBlock);
328
329 mapCommon.insert(std::make_pair(msTimestamp, pRecordingBlocks));
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/**
352 * Callback function for writing encoded audio data into the common encoded block map.
353 *
354 * This is called by the audio codec when finishing encoding audio data.
355 *
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;
363 return pThis->writeCommonData(pThis->m_mapBlocksEncoded, pCodec, pvData, cbData, msAbsPTS, uFlags);
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
386 int vrc = recordingCodecCreateAudio(&m_CodecAudio, enmCodec);
387 if (RT_SUCCESS(vrc))
388 vrc = recordingCodecInit(&m_CodecAudio, &Callbacks, screenSettings);
389
390 return vrc;
391}
392#endif /* VBOX_WITH_AUDIO_RECORDING */
393
394/**
395 * Creates a recording context.
396 *
397 * @returns VBox status code.
398 * @param ptrConsole Pointer to console object this context is bound to (weak pointer).
399 * @param Settings Reference to recording settings to use for creation.
400 */
401int RecordingContext::createInternal(Console *ptrConsole, const settings::RecordingSettings &Settings)
402{
403 int vrc = VINF_SUCCESS;
404
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
420 m_pConsole = ptrConsole;
421
422 settings::RecordingScreenSettingsMap::const_iterator itScreen = m_Settings.mapScreens.begin();
423 while (itScreen != m_Settings.mapScreens.end())
424 {
425 RecordingStream *pStream = NULL;
426 try
427 {
428 pStream = new RecordingStream(this, itScreen->first /* Screen ID */, itScreen->second);
429 m_vecStreams.push_back(pStream);
430 if (itScreen->second.fEnabled)
431 m_cStreamsEnabled++;
432 LogFlowFunc(("pStream=%p\n", pStream));
433 }
434 catch (std::bad_alloc &)
435 {
436 vrc = VERR_NO_MEMORY;
437 break;
438 }
439 catch (int vrc_thrown) /* Catch rc thrown by constructor. */
440 {
441 vrc = vrc_thrown;
442 break;
443 }
444
445 ++itScreen;
446 }
447
448 if (RT_SUCCESS(vrc))
449 {
450 m_tsStartMs = RTTimeMilliTS();
451 m_enmState = RECORDINGSTS_CREATED;
452 m_fShutdown = false;
453
454 vrc = RTSemEventCreate(&m_WaitEvent);
455 AssertRCReturn(vrc, vrc);
456 }
457
458 if (RT_FAILURE(vrc))
459 destroyInternal();
460
461 return vrc;
462}
463
464/**
465 * Starts a recording context by creating its worker thread.
466 *
467 * @returns VBox status code.
468 */
469int RecordingContext::startInternal(void)
470{
471 if (m_enmState == RECORDINGSTS_STARTED)
472 return VINF_SUCCESS;
473
474 Assert(m_enmState == RECORDINGSTS_CREATED);
475
476 int vrc = RTThreadCreate(&m_Thread, RecordingContext::threadMain, (void *)this, 0,
477 RTTHREADTYPE_MAIN_WORKER, RTTHREADFLAGS_WAITABLE, "Record");
478
479 if (RT_SUCCESS(vrc)) /* Wait for the thread to start. */
480 vrc = RTThreadUserWait(m_Thread, RT_MS_30SEC /* 30s timeout */);
481
482 if (RT_SUCCESS(vrc))
483 {
484 LogRel(("Recording: Started\n"));
485 m_enmState = RECORDINGSTS_STARTED;
486 }
487 else
488 Log(("Recording: Failed to start (%Rrc)\n", vrc));
489
490 return vrc;
491}
492
493/**
494 * Stops a recording context by telling the worker thread to stop and finalizing its operation.
495 *
496 * @returns VBox status code.
497 */
498int RecordingContext::stopInternal(void)
499{
500 if (m_enmState != RECORDINGSTS_STARTED)
501 return VINF_SUCCESS;
502
503 LogThisFunc(("Shutting down thread ...\n"));
504
505 /* Set shutdown indicator. */
506 ASMAtomicWriteBool(&m_fShutdown, true);
507
508 /* Signal the thread and wait for it to shut down. */
509 int vrc = threadNotify();
510 if (RT_SUCCESS(vrc))
511 vrc = RTThreadWait(m_Thread, RT_MS_30SEC /* 30s timeout */, NULL);
512
513 lock();
514
515 if (RT_SUCCESS(vrc))
516 {
517 LogRel(("Recording: Stopped\n"));
518 m_enmState = RECORDINGSTS_CREATED;
519 }
520 else
521 Log(("Recording: Failed to stop (%Rrc)\n", vrc));
522
523 unlock();
524
525 LogFlowThisFunc(("%Rrc\n", vrc));
526 return vrc;
527}
528
529/**
530 * Destroys a recording context, internal version.
531 */
532void RecordingContext::destroyInternal(void)
533{
534 lock();
535
536 if (m_enmState == RECORDINGSTS_UNINITIALIZED)
537 {
538 unlock();
539 return;
540 }
541
542 int vrc = stopInternal();
543 AssertRCReturnVoid(vrc);
544
545 vrc = RTSemEventDestroy(m_WaitEvent);
546 AssertRCReturnVoid(vrc);
547
548 m_WaitEvent = NIL_RTSEMEVENT;
549
550 RecordingStreams::iterator it = m_vecStreams.begin();
551 while (it != m_vecStreams.end())
552 {
553 RecordingStream *pStream = (*it);
554
555 vrc = pStream->Uninit();
556 AssertRC(vrc);
557
558 delete pStream;
559 pStream = NULL;
560
561 m_vecStreams.erase(it);
562 it = m_vecStreams.begin();
563 }
564
565 /* Sanity. */
566 Assert(m_vecStreams.empty());
567 Assert(m_mapBlocksRaw.size() == 0);
568 Assert(m_mapBlocksEncoded.size() == 0);
569
570 m_enmState = RECORDINGSTS_UNINITIALIZED;
571
572 unlock();
573}
574
575/**
576 * Returns a recording context's current settings.
577 *
578 * @returns The recording context's current settings.
579 */
580const settings::RecordingSettings &RecordingContext::GetConfig(void) const
581{
582 return m_Settings;
583}
584
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 */
591RecordingStream *RecordingContext::getStreamInternal(unsigned uScreen) const
592{
593 RecordingStream *pStream;
594
595 try
596 {
597 pStream = m_vecStreams.at(uScreen);
598 }
599 catch (std::out_of_range &)
600 {
601 pStream = NULL;
602 }
603
604 return pStream;
605}
606
607/**
608 * Locks the recording context for serializing access.
609 *
610 * @returns VBox status code.
611 */
612int RecordingContext::lock(void)
613{
614 int vrc = RTCritSectEnter(&m_CritSect);
615 AssertRC(vrc);
616 return vrc;
617}
618
619/**
620 * Unlocks the recording context for serializing access.
621 *
622 * @returns VBox status code.
623 */
624int RecordingContext::unlock(void)
625{
626 int vrc = RTCritSectLeave(&m_CritSect);
627 AssertRC(vrc);
628 return vrc;
629}
630
631/**
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 */
637RecordingStream *RecordingContext::GetStream(unsigned uScreen) const
638{
639 return getStreamInternal(uScreen);
640}
641
642/**
643 * Returns the number of configured recording streams for a recording context.
644 *
645 * @returns Number of configured recording streams.
646 */
647size_t RecordingContext::GetStreamCount(void) const
648{
649 return m_vecStreams.size();
650}
651
652/**
653 * Creates a new recording context.
654 *
655 * @returns VBox status code.
656 * @param ptrConsole Pointer to console object this context is bound to (weak pointer).
657 * @param Settings Reference to recording settings to use for creation.
658 */
659int RecordingContext::Create(Console *ptrConsole, const settings::RecordingSettings &Settings)
660{
661 return createInternal(ptrConsole, Settings);
662}
663
664/**
665 * Destroys a recording context.
666 */
667void RecordingContext::Destroy(void)
668{
669 destroyInternal();
670}
671
672/**
673 * Starts a recording context.
674 *
675 * @returns VBox status code.
676 */
677int RecordingContext::Start(void)
678{
679 return startInternal();
680}
681
682/**
683 * Stops a recording context.
684 */
685int RecordingContext::Stop(void)
686{
687 return stopInternal();
688}
689
690/**
691 * Returns if a specific recoding feature is enabled for at least one of the attached
692 * recording streams or not.
693 *
694 * @returns @c true if at least one recording stream has this feature enabled, or @c false if
695 * no recording stream has this feature enabled.
696 * @param enmFeature Recording feature to check for.
697 */
698bool RecordingContext::IsFeatureEnabled(RecordingFeature_T enmFeature)
699{
700 lock();
701
702 RecordingStreams::const_iterator itStream = m_vecStreams.begin();
703 while (itStream != m_vecStreams.end())
704 {
705 if ((*itStream)->GetConfig().isFeatureEnabled(enmFeature))
706 {
707 unlock();
708 return true;
709 }
710 ++itStream;
711 }
712
713 unlock();
714
715 return false;
716}
717
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 */
723bool RecordingContext::IsReady(void)
724{
725 lock();
726
727 const bool fIsReady = m_enmState >= RECORDINGSTS_CREATED;
728
729 unlock();
730
731 return fIsReady;
732}
733
734/**
735 * Returns if this recording context is ready to accept new recording data for a given screen.
736 *
737 * @returns @c true if the specified screen is ready, @c false if not.
738 * @param uScreen Screen ID.
739 * @param msTimestamp Timestamp (PTS, in ms). Currently not being used.
740 */
741bool RecordingContext::IsReady(uint32_t uScreen, uint64_t msTimestamp)
742{
743 RT_NOREF(msTimestamp);
744
745 lock();
746
747 bool fIsReady = false;
748
749 if (m_enmState != RECORDINGSTS_STARTED)
750 {
751 const RecordingStream *pStream = getStreamInternal(uScreen);
752 if (pStream)
753 fIsReady = pStream->IsReady();
754
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 }
759
760 unlock();
761
762 return fIsReady;
763}
764
765/**
766 * Returns whether a given recording context has been started or not.
767 *
768 * @returns true if active, false if not.
769 */
770bool RecordingContext::IsStarted(void)
771{
772 lock();
773
774 const bool fIsStarted = m_enmState == RECORDINGSTS_STARTED;
775
776 unlock();
777
778 return fIsStarted;
779}
780
781/**
782 * Checks if a specified limit for recording has been reached.
783 *
784 * @returns true if any limit has been reached.
785 */
786bool RecordingContext::IsLimitReached(void)
787{
788 lock();
789
790 LogFlowThisFunc(("cStreamsEnabled=%RU16\n", m_cStreamsEnabled));
791
792 const bool fLimitReached = m_cStreamsEnabled == 0;
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.
803 * @param uScreen Screen ID.
804 * @param msTimestamp Timestamp (PTS, in ms) to check for.
805 */
806bool RecordingContext::IsLimitReached(uint32_t uScreen, uint64_t msTimestamp)
807{
808 lock();
809
810 bool fLimitReached = false;
811
812 const RecordingStream *pStream = getStreamInternal(uScreen);
813 if ( !pStream
814 || pStream->IsLimitReached(msTimestamp))
815 {
816 fLimitReached = true;
817 }
818
819 unlock();
820
821 return fLimitReached;
822}
823
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
837 if (m_enmState == RECORDINGSTS_STARTED)
838 {
839#ifdef VBOX_WITH_AUDIO_RECORDING
840 if ( recordingCodecIsInitialized(&m_CodecAudio)
841 && recordingCodecGetWritable(&m_CodecAudio, msTimestamp) > 0)
842 {
843 fNeedsUpdate = true;
844 }
845#endif /* VBOX_WITH_AUDIO_RECORDING */
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
860DECLCALLBACK(int) RecordingContext::OnLimitReached(uint32_t uScreen, int rc)
861{
862 RT_NOREF(uScreen, rc);
863 LogFlowThisFunc(("Stream %RU32 has reached its limit (%Rrc)\n", uScreen, rc));
864
865 lock();
866
867 Assert(m_cStreamsEnabled);
868 m_cStreamsEnabled--;
869
870 LogFlowThisFunc(("cStreamsEnabled=%RU16\n", m_cStreamsEnabled));
871
872 unlock();
873
874 return VINF_SUCCESS;
875}
876
877/**
878 * Sends an audio frame to the recording thread.
879 *
880 * @returns VBox status code.
881 * @param pvData Audio frame data to send.
882 * @param cbData Size (in bytes) of (encoded) audio frame data.
883 * @param msTimestamp Timestamp (PTS, in ms) of audio playback.
884 */
885int RecordingContext::SendAudioFrame(const void *pvData, size_t cbData, uint64_t msTimestamp)
886{
887#ifdef VBOX_WITH_AUDIO_RECORDING
888 return writeCommonData(m_mapBlocksRaw, &m_CodecAudio,
889 pvData, cbData, msTimestamp, RECORDINGCODEC_ENC_F_BLOCK_IS_KEY);
890#else
891 RT_NOREF(pvData, cbData, msTimestamp);
892 return VERR_NOT_SUPPORTED;
893#endif
894}
895
896/**
897 * Sends a video frame to the recording thread.
898 *
899 * @thread EMT
900 *
901 * @returns VBox status code.
902 * @param uScreen Screen number to send video frame to.
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.
911 * @param msTimestamp Timestamp (PTS, in ms).
912 */
913int RecordingContext::SendVideoFrame(uint32_t uScreen, uint32_t x, uint32_t y,
914 uint32_t uPixelFormat, uint32_t uBPP, uint32_t uBytesPerLine,
915 uint32_t uSrcWidth, uint32_t uSrcHeight, uint8_t *puSrcData,
916 uint64_t msTimestamp)
917{
918 AssertReturn(uSrcWidth, VERR_INVALID_PARAMETER);
919 AssertReturn(uSrcHeight, VERR_INVALID_PARAMETER);
920 AssertReturn(puSrcData, VERR_INVALID_POINTER);
921
922 lock();
923
924 RecordingStream *pStream = getStreamInternal(uScreen);
925 if (!pStream)
926 {
927 unlock();
928
929 AssertFailed();
930 return VERR_NOT_FOUND;
931 }
932
933 int vrc = pStream->SendVideoFrame(x, y, uPixelFormat, uBPP, uBytesPerLine, uSrcWidth, uSrcHeight, puSrcData, msTimestamp);
934
935 unlock();
936
937 if ( RT_SUCCESS(vrc)
938 && vrc != VINF_RECORDING_THROTTLED) /* Only signal the thread if operation was successful. */
939 {
940 threadNotify();
941 }
942
943 return vrc;
944}
945
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette