VirtualBox

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

最後變更 在這個檔案從96324是 96324,由 vboxsync 提交於 3 年 前

Recording/Main: Renaming (use m_ prefixes for class variables).

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

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