VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/RecordingSettingsImpl.cpp@ 105266

Last change on this file since 105266 was 105266, checked in by vboxsync, 6 months ago

Recording: Implemented support for a dedicated progress object, which is exposed to API clients. This can be used for better tracking the recording progress as well as for error reporting. The RecordingSettings API also now has a dedicated start() method to start recording, as well as support for attaching to an already ongoing recording by retrieving the progress object at a later time. Adapted FE/Qt (draft, see @todos), FE/VBoxManage and the Validation Kit testdriver to the new APIs. VBoxManage also can attach to an ongoing recording now. The recording progress object also will have multiple operations to get the recording progress for convenience. bugref:10718

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 27.8 KB
Line 
1/* $Id: RecordingSettingsImpl.cpp 105266 2024-07-11 07:49:37Z vboxsync $ */
2/** @file
3 *
4 * VirtualBox COM class implementation - Machine capture settings.
5 */
6
7/*
8 * Copyright (C) 2018-2023 Oracle and/or its affiliates.
9 *
10 * This file is part of VirtualBox base platform packages, as
11 * available from https://www.virtualbox.org.
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation, in version 3 of the
16 * License.
17 *
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, see <https://www.gnu.org/licenses>.
25 *
26 * SPDX-License-Identifier: GPL-3.0-only
27 */
28
29#define LOG_GROUP LOG_GROUP_MAIN_RECORDINGSETTINGS
30#include "LoggingNew.h"
31
32#include "RecordingSettingsImpl.h"
33#include "RecordingScreenSettingsImpl.h"
34#include "MachineImpl.h"
35
36#include <iprt/cpp/utils.h>
37#include <VBox/settings.h>
38
39#include "AutoStateDep.h"
40#include "AutoCaller.h"
41#include "Global.h"
42
43////////////////////////////////////////////////////////////////////////////////
44//
45// RecordSettings private data definition
46//
47////////////////////////////////////////////////////////////////////////////////
48
49struct RecordingSettings::Data
50{
51 Data()
52 : pMachine(NULL)
53 { }
54
55 Machine * const pMachine;
56 const ComObjPtr<RecordingSettings> pPeer;
57 RecordingScreenSettingsObjMap mapScreenObj;
58 /** The recording progress object.
59 * There only is one recording progress per VM, shared between multiple recording settings (if any). */
60 ComPtr<IProgress> mProgress;
61
62 // use the XML settings structure in the members for simplicity
63 Backupable<settings::RecordingCommonSettings> bd;
64};
65
66DEFINE_EMPTY_CTOR_DTOR(RecordingSettings)
67
68HRESULT RecordingSettings::FinalConstruct()
69{
70 return BaseFinalConstruct();
71}
72
73void RecordingSettings::FinalRelease()
74{
75 uninit();
76 BaseFinalRelease();
77}
78
79/**
80 * Initializes the recording settings object.
81 *
82 * @returns COM result indicator
83 */
84HRESULT RecordingSettings::init(Machine *aParent)
85{
86 LogFlowThisFuncEnter();
87 LogFlowThisFunc(("aParent: %p\n", aParent));
88
89 ComAssertRet(aParent, E_INVALIDARG);
90
91 /* Enclose the state transition NotReady->InInit->Ready */
92 AutoInitSpan autoInitSpan(this);
93 AssertReturn(autoInitSpan.isOk(), E_FAIL);
94
95 m = new Data();
96
97 /* share the parent weakly */
98 unconst(m->pMachine) = aParent;
99
100 m->bd.allocate();
101
102 i_applyDefaults();
103
104 /* Note: The progress object gets created in i_start(). */
105
106 autoInitSpan.setSucceeded();
107
108 LogFlowThisFuncLeave();
109 return S_OK;
110}
111
112/**
113 * Initializes the capture settings object given another capture settings object
114 * (a kind of copy constructor). This object shares data with
115 * the object passed as an argument.
116 *
117 * @note This object must be destroyed before the original object
118 * it shares data with is destroyed.
119 *
120 * @note Locks @a aThat object for reading.
121 */
122HRESULT RecordingSettings::init(Machine *aParent, RecordingSettings *aThat)
123{
124 LogFlowThisFuncEnter();
125 LogFlowThisFunc(("aParent: %p, aThat: %p\n", aParent, aThat));
126
127 ComAssertRet(aParent && aThat, E_INVALIDARG);
128
129 /* Enclose the state transition NotReady->InInit->Ready */
130 AutoInitSpan autoInitSpan(this);
131 AssertReturn(autoInitSpan.isOk(), E_FAIL);
132
133 m = new Data();
134
135 unconst(m->pMachine) = aParent;
136 unconst(m->pPeer) = aThat;
137
138 AutoCaller thatCaller(aThat);
139 AssertComRCReturnRC(thatCaller.hrc());
140
141 AutoReadLock thatlock(aThat COMMA_LOCKVAL_SRC_POS);
142
143 m->bd.share(aThat->m->bd);
144
145 /* Make sure to add a reference when sharing the screen objects with aThat. */
146 for (RecordingScreenSettingsObjMap::const_iterator itScreenThat = aThat->m->mapScreenObj.begin();
147 itScreenThat != aThat->m->mapScreenObj.end();
148 ++itScreenThat)
149 itScreenThat->second->i_reference();
150
151 m->mapScreenObj = aThat->m->mapScreenObj;
152 m->mProgress = aThat->m->mProgress;
153
154 autoInitSpan.setSucceeded();
155
156 LogFlowThisFuncLeave();
157 return S_OK;
158}
159
160/**
161 * Initializes the guest object given another guest object
162 * (a kind of copy constructor). This object makes a private copy of data
163 * of the original object passed as an argument.
164 *
165 * @note Locks @a aThat object for reading.
166 */
167HRESULT RecordingSettings::initCopy(Machine *aParent, RecordingSettings *aThat)
168{
169 LogFlowThisFuncEnter();
170 LogFlowThisFunc(("aParent: %p, aThat: %p\n", aParent, aThat));
171
172 ComAssertRet(aParent && aThat, E_INVALIDARG);
173
174 /* Enclose the state transition NotReady->InInit->Ready */
175 AutoInitSpan autoInitSpan(this);
176 AssertReturn(autoInitSpan.isOk(), E_FAIL);
177
178 m = new Data();
179
180 unconst(m->pMachine) = aParent;
181 // mPeer is left null
182
183 AutoReadLock thatlock(aThat COMMA_LOCKVAL_SRC_POS);
184 m->bd.attachCopy(aThat->m->bd);
185
186 HRESULT hrc = S_OK;
187
188 for (RecordingScreenSettingsObjMap::const_iterator itScreenThat = aThat->m->mapScreenObj.begin();
189 itScreenThat != aThat->m->mapScreenObj.end();
190 ++itScreenThat)
191 {
192 ComObjPtr<RecordingScreenSettings> pSettings;
193 pSettings.createObject();
194 hrc = pSettings->initCopy(this, itScreenThat->second);
195 if (FAILED(hrc)) return hrc;
196
197 try
198 {
199 m->mapScreenObj[itScreenThat->first] = pSettings;
200 }
201 catch (...)
202 {
203 hrc = E_OUTOFMEMORY;
204 }
205 }
206
207 m->mProgress = aThat->m->mProgress;
208
209 if (SUCCEEDED(hrc))
210 autoInitSpan.setSucceeded();
211
212 LogFlowThisFuncLeave();
213 return hrc;
214}
215
216/**
217 * Uninitializes the instance and sets the ready flag to FALSE.
218 * Called either from FinalRelease() or by the parent when it gets destroyed.
219 */
220void RecordingSettings::uninit()
221{
222 LogFlowThisFuncEnter();
223
224 /* Enclose the state transition Ready->InUninit->NotReady */
225 AutoUninitSpan autoUninitSpan(this);
226 if (autoUninitSpan.uninitDone())
227 return;
228
229 i_reset();
230
231 m->bd.free();
232
233 unconst(m->pPeer) = NULL;
234 unconst(m->pMachine) = NULL;
235
236 delete m;
237 m = NULL;
238
239 LogFlowThisFuncLeave();
240}
241
242// IRecordSettings properties
243/////////////////////////////////////////////////////////////////////////////
244
245HRESULT RecordingSettings::getEnabled(BOOL *enabled)
246{
247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
248
249 *enabled = m->bd->fEnabled;
250
251 return S_OK;
252}
253
254HRESULT RecordingSettings::setEnabled(BOOL enable)
255{
256 /* the machine needs to be mutable */
257 AutoMutableOrSavedOrRunningStateDependency adep(m->pMachine);
258 if (FAILED(adep.hrc())) return adep.hrc();
259
260 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
261
262 const bool fEnabled = RT_BOOL(enable);
263
264 HRESULT hrc = S_OK;
265
266 if (m->bd->fEnabled != fEnabled)
267 {
268 m->bd.backup();
269 m->bd->fEnabled = fEnabled;
270
271 alock.release();
272 }
273
274 return hrc;
275}
276
277HRESULT RecordingSettings::getScreens(std::vector<ComPtr<IRecordingScreenSettings> > &aRecordScreenSettings)
278{
279 LogFlowThisFuncEnter();
280
281 AssertPtr(m->pMachine);
282 ComPtr<IGraphicsAdapter> pGraphicsAdapter;
283 m->pMachine->COMGETTER(GraphicsAdapter)(pGraphicsAdapter.asOutParam());
284 ULONG cMonitors = 0;
285 if (!pGraphicsAdapter.isNull())
286 pGraphicsAdapter->COMGETTER(MonitorCount)(&cMonitors);
287
288 i_syncToMachineDisplays(cMonitors);
289
290 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
291
292 HRESULT hrc = S_OK;
293
294 try
295 {
296 aRecordScreenSettings.clear();
297 aRecordScreenSettings.resize(m->mapScreenObj.size());
298 }
299 catch (...)
300 {
301 hrc = E_OUTOFMEMORY;
302 }
303
304 if (FAILED(hrc))
305 return hrc;
306
307 RecordingScreenSettingsObjMap::const_iterator itScreenObj = m->mapScreenObj.begin();
308 size_t i = 0;
309 while (itScreenObj != m->mapScreenObj.end())
310 {
311 itScreenObj->second.queryInterfaceTo(aRecordScreenSettings[i].asOutParam());
312 AssertBreakStmt(aRecordScreenSettings[i].isNotNull(), hrc = E_POINTER);
313 ++i;
314 ++itScreenObj;
315 }
316
317 Assert(aRecordScreenSettings.size() == m->mapScreenObj.size());
318
319 return hrc;
320}
321
322HRESULT RecordingSettings::getProgress(ComPtr<IProgress> &aProgress)
323{
324#ifndef VBOX_WITH_RECORDING
325 ReturnComNotImplemented();
326#else
327 /* the machine needs to be mutable */
328 AutoMutableOrSavedOrRunningStateDependency adep(m->pMachine);
329 if (FAILED(adep.hrc())) return adep.hrc();
330
331 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
332
333 if (m->mProgress.isNull())
334 return setError(E_FAIL, tr("Recording not started"));
335
336 return m->mProgress.queryInterfaceTo(aProgress.asOutParam());
337#endif
338}
339
340HRESULT RecordingSettings::getScreenSettings(ULONG uScreenId, ComPtr<IRecordingScreenSettings> &aRecordScreenSettings)
341{
342 LogFlowThisFuncEnter();
343
344 AssertPtr(m->pMachine);
345 ComPtr<IGraphicsAdapter> pGraphicsAdapter;
346 m->pMachine->COMGETTER(GraphicsAdapter)(pGraphicsAdapter.asOutParam());
347 ULONG cMonitors = 0;
348 if (!pGraphicsAdapter.isNull())
349 pGraphicsAdapter->COMGETTER(MonitorCount)(&cMonitors);
350
351 i_syncToMachineDisplays(cMonitors);
352
353 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
354
355 if (uScreenId + 1 > m->mapScreenObj.size())
356 return setError(E_INVALIDARG, tr("Invalid screen ID specified"));
357
358 RecordingScreenSettingsObjMap::const_iterator itScreen = m->mapScreenObj.find(uScreenId);
359 if (itScreen != m->mapScreenObj.end())
360 {
361 itScreen->second.queryInterfaceTo(aRecordScreenSettings.asOutParam());
362 return S_OK;
363 }
364
365 return VBOX_E_OBJECT_NOT_FOUND;
366}
367
368HRESULT RecordingSettings::start(ComPtr<IProgress> &aProgress)
369{
370#ifndef VBOX_WITH_RECORDING
371 ReturnComNotImplemented();
372#else
373 /* the machine needs to be mutable */
374 AutoMutableOrSavedOrRunningStateDependency adep(m->pMachine);
375 if (FAILED(adep.hrc())) return adep.hrc();
376
377 /* Recording not explicitly enabled before? Do so now. */
378 if (!m->bd->fEnabled)
379 {
380 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
381
382 m->bd.backup();
383 m->bd->fEnabled = true;
384
385 alock.release();
386
387 /* Note: m->bd->fEnabled is transient here, i.e. we don't save the settings,
388 as this would otherwise start the recording on VM startup the next time. */
389 }
390
391 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
392
393 if (m->mProgress.isNotNull())
394 {
395 BOOL fCompleted = FALSE;
396 HRESULT const hrc = m->mProgress->COMGETTER(Completed)(&fCompleted);
397 ComAssertComRCRetRC(hrc);
398 if (!fCompleted)
399 return setError(E_FAIL, tr("Recording already started"));
400 }
401
402 m->mProgress.setNull(); /* Make sure to release a dangling object from a former run. */
403
404 alock.release();
405
406 int const vrc = i_start();
407 if (RT_FAILURE(vrc))
408 {
409 /* Make the progress' error info available to the caller on failure. */
410 ComObjPtr<IVirtualBoxErrorInfo> pErrorInfo;
411 m->mProgress->COMGETTER(ErrorInfo)(pErrorInfo.asOutParam());
412 return setError(pErrorInfo);
413 }
414
415 return m->mProgress.queryInterfaceTo(aProgress.asOutParam());
416#endif
417}
418
419// IRecordSettings methods
420/////////////////////////////////////////////////////////////////////////////
421
422// public methods only for internal purposes
423/////////////////////////////////////////////////////////////////////////////
424
425/**
426 * Adds a screen settings object to a particular map.
427 *
428 * @returns IPRT status code. VERR_ALREADY_EXISTS if the object in question already exists.
429 * @param screenSettingsMap Map to add screen settings to.
430 * @param idScreen Screen ID to add settings for.
431 * @param data Recording screen settings to use for that screen.
432 */
433int RecordingSettings::i_createScreenObj(RecordingScreenSettingsObjMap &screenSettingsMap,
434 uint32_t idScreen, const settings::RecordingScreenSettings &data)
435{
436 AssertReturn(screenSettingsMap.find(idScreen) == screenSettingsMap.end(), VERR_ALREADY_EXISTS);
437
438 int vrc = VINF_SUCCESS;
439
440 ComObjPtr<RecordingScreenSettings> recordingScreenSettings;
441 HRESULT hrc = recordingScreenSettings.createObject();
442 if (SUCCEEDED(hrc))
443 {
444 hrc = recordingScreenSettings->init(this, idScreen, data);
445 if (SUCCEEDED(hrc))
446 {
447 try
448 {
449 screenSettingsMap[idScreen] = recordingScreenSettings;
450 }
451 catch (std::bad_alloc &)
452 {
453 vrc = VERR_NO_MEMORY;
454 }
455 }
456 }
457
458 LogThisFunc(("%p: Screen %RU32 -> %Rrc\n", recordingScreenSettings.m_p, idScreen, vrc));
459 return vrc;
460}
461
462/**
463 * Removes a screen settings object from a particular map.
464 *
465 * If the internal reference count hits 0, the screen settings object will be destroyed.
466 * This means that this screen settings object is not being used anymore by other recording settings (as shared data).
467 *
468 * @returns IPRT status code.
469 * @retval VERR_NOT_FOUND if specified screen was not found.
470 * @param screenSettingsMap Map to remove screen settings from.
471 * @param idScreen ID of screen to remove.
472 */
473int RecordingSettings::i_destroyScreenObj(RecordingScreenSettingsObjMap &screenSettingsMap, uint32_t idScreen)
474{
475 AssertReturn(screenSettingsMap.find(idScreen) != screenSettingsMap.end(), VERR_NOT_FOUND);
476
477 RecordingScreenSettingsObjMap::iterator itScreen = screenSettingsMap.find(idScreen);
478
479 /* Make sure to consume the pointer before the one of the
480 * iterator gets released. */
481 ComObjPtr<RecordingScreenSettings> pScreenSettings = itScreen->second;
482
483 screenSettingsMap.erase(itScreen);
484
485 LogThisFunc(("%p: Screen %RU32, cRefs=%RI32\n", pScreenSettings.m_p, idScreen, pScreenSettings->i_getReferences()));
486
487 pScreenSettings->i_release();
488
489 /* Only destroy the object if nobody else keeps a reference to it anymore. */
490 if (pScreenSettings->i_getReferences() == 0)
491 {
492 LogThisFunc(("%p: Screen %RU32 -> Null\n", pScreenSettings.m_p, idScreen));
493 pScreenSettings.setNull();
494 }
495
496 return VINF_SUCCESS;
497}
498
499/**
500 * Destroys all screen settings objects of a particular map.
501 *
502 * @returns IPRT status code.
503 * @param screenSettingsMap Map to destroy screen settings objects for.
504 */
505int RecordingSettings::i_destroyAllScreenObj(RecordingScreenSettingsObjMap &screenSettingsMap)
506{
507 LogFlowThisFuncEnter();
508
509 int vrc = VINF_SUCCESS;
510
511 RecordingScreenSettingsObjMap::iterator itScreen = screenSettingsMap.begin();
512 while (itScreen != screenSettingsMap.end())
513 {
514 vrc = i_destroyScreenObj(screenSettingsMap, itScreen->first);
515 if (RT_FAILURE(vrc))
516 break;
517
518 itScreen = screenSettingsMap.begin();
519 }
520
521 Assert(screenSettingsMap.size() == 0);
522 return vrc;
523}
524
525/**
526 * Loads settings from the given settings.
527 * May be called once right after this object creation.
528 *
529 * @param data Capture settings to load from.
530 *
531 * @note Locks this object for writing.
532 */
533HRESULT RecordingSettings::i_loadSettings(const settings::RecordingSettings &data)
534{
535 LogFlowThisFuncEnter();
536
537 AutoCaller autoCaller(this);
538 AssertComRCReturnRC(autoCaller.hrc());
539
540 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
541
542 HRESULT hrc = S_OK;
543
544 LogFlowThisFunc(("Data has %zu screens\n", data.mapScreens.size()));
545
546 settings::RecordingScreenSettingsMap::const_iterator itScreenData = data.mapScreens.begin();
547 while (itScreenData != data.mapScreens.end())
548 {
549 RecordingScreenSettingsObjMap::iterator itScreen = m->mapScreenObj.find(itScreenData->first);
550 if (itScreen != m->mapScreenObj.end())
551 {
552 hrc = itScreen->second->i_loadSettings(itScreenData->second);
553 if (FAILED(hrc))
554 break;
555 }
556 else
557 {
558 int vrc = i_createScreenObj(m->mapScreenObj,
559 itScreenData->first /* uScreenId */, itScreenData->second /* Settings */);
560 if (RT_FAILURE(vrc))
561 {
562 hrc = E_OUTOFMEMORY; /* Most likely. */
563 break;
564 }
565 }
566
567 ++itScreenData;
568 }
569
570 if (SUCCEEDED(hrc))
571 {
572 ComAssertComRCRet(hrc, hrc);
573 AssertReturn(m->mapScreenObj.size() == data.mapScreens.size(), E_UNEXPECTED);
574
575 // simply copy
576 m->bd.assignCopy(&data.common);
577 }
578
579 LogFlowThisFunc(("Returning %Rhrc\n", hrc));
580 return hrc;
581}
582
583/**
584 * Resets the internal object state by destroying all screen settings objects.
585 */
586void RecordingSettings::i_reset(void)
587{
588 LogFlowThisFuncEnter();
589
590 i_stop();
591
592 /* Make sure to destroy screen objects attached to this object.
593 * Note: This also decrements the refcount of a screens object, in case it's shared among other recording settings. */
594 i_destroyAllScreenObj(m->mapScreenObj);
595}
596
597/**
598 * Saves settings to the given settings.
599 *
600 * @param data Where to store the capture settings to.
601 *
602 * @note Locks this object for reading.
603 */
604HRESULT RecordingSettings::i_saveSettings(settings::RecordingSettings &data)
605{
606 LogFlowThisFuncEnter();
607
608 AutoCaller autoCaller(this);
609 AssertComRCReturnRC(autoCaller.hrc());
610
611 AssertPtr(m->pMachine);
612 ComPtr<IGraphicsAdapter> pGraphicsAdapter;
613 m->pMachine->COMGETTER(GraphicsAdapter)(pGraphicsAdapter.asOutParam());
614 ULONG cMonitors = 0;
615 if (!pGraphicsAdapter.isNull())
616 pGraphicsAdapter->COMGETTER(MonitorCount)(&cMonitors);
617
618 int vrc2 = i_syncToMachineDisplays(cMonitors);
619 AssertRC(vrc2);
620
621 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
622
623 data.common = *m->bd.data();
624
625 HRESULT hrc = S_OK;
626
627 for (RecordingScreenSettingsObjMap::const_iterator itScreen = m->mapScreenObj.begin();
628 itScreen != m->mapScreenObj.end();
629 ++itScreen)
630 {
631 hrc = itScreen->second->i_saveSettings(data.mapScreens[itScreen->first /* Screen ID */]);
632 if (FAILED(hrc))
633 break;
634 }
635
636 LogFlowThisFuncLeave();
637 return hrc;
638}
639
640void RecordingSettings::i_rollback(void)
641{
642 /* sanity */
643 AutoCaller autoCaller(this);
644 AssertComRCReturnVoid(autoCaller.hrc());
645
646 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
647
648 m->bd.rollback();
649
650 for (RecordingScreenSettingsObjMap::const_iterator itScreen = m->mapScreenObj.begin();
651 itScreen != m->mapScreenObj.end();
652 ++itScreen)
653 {
654 itScreen->second->i_rollback();
655 }
656}
657
658void RecordingSettings::i_commit(void)
659{
660 /* sanity */
661 AutoCaller autoCaller(this);
662 AssertComRCReturnVoid(autoCaller.hrc());
663
664 /* sanity too */
665 AutoCaller peerCaller(m->pPeer);
666 AssertComRCReturnVoid(peerCaller.hrc());
667
668 /* lock both for writing since we modify both (mPeer is "master" so locked
669 * first) */
670 AutoMultiWriteLock2 alock(m->pPeer, this COMMA_LOCKVAL_SRC_POS);
671
672 if (m->bd.isBackedUp())
673 {
674 m->bd.commit();
675 if (m->pPeer)
676 {
677 /* attach new data to the peer and reshare it */
678 m->pPeer->m->bd.attach(m->bd);
679 }
680
681 for (RecordingScreenSettingsObjMap::const_iterator itScreenObj = m->mapScreenObj.begin();
682 itScreenObj != m->mapScreenObj.end();
683 ++itScreenObj)
684 {
685 itScreenObj->second->i_commit();
686 if (m->pPeer)
687 m->pPeer->i_commit();
688 }
689 }
690}
691
692HRESULT RecordingSettings::i_copyFrom(RecordingSettings *aThat)
693{
694 AssertPtrReturn(aThat, E_INVALIDARG);
695
696 /* sanity */
697 AutoCaller autoCaller(this);
698 AssertComRCReturn(autoCaller.hrc(), VBOX_E_INVALID_OBJECT_STATE);
699
700 /* sanity too */
701 AutoCaller thatCaller(aThat);
702 AssertComRCReturn(thatCaller.hrc(), VBOX_E_INVALID_OBJECT_STATE);
703
704 /* peer is not modified, lock it for reading (aThat is "master" so locked
705 * first) */
706 AutoReadLock rl(aThat COMMA_LOCKVAL_SRC_POS);
707 AutoWriteLock wl(this COMMA_LOCKVAL_SRC_POS);
708
709 /* this will back up current data */
710 m->bd.assignCopy(aThat->m->bd);
711
712 HRESULT hrc = S_OK;
713
714 for (RecordingScreenSettingsObjMap::const_iterator itScreenThat = aThat->m->mapScreenObj.begin();
715 itScreenThat != aThat->m->mapScreenObj.end();
716 ++itScreenThat)
717 {
718 RecordingScreenSettingsObjMap::iterator itScreen = m->mapScreenObj.find(itScreenThat->first);
719 if (itScreen != m->mapScreenObj.end())
720 {
721 itScreen->second->i_copyFrom(itScreenThat->second);
722 }
723 else
724 {
725 int vrc = i_createScreenObj(m->mapScreenObj,
726 itScreenThat->first /* uScreenId */, itScreenThat->second->i_getData() /* Settings */);
727 if (RT_FAILURE(vrc))
728 {
729 hrc = E_OUTOFMEMORY; /* Most likely. */
730 break;
731 }
732 }
733 }
734
735 return hrc;
736}
737
738void RecordingSettings::i_applyDefaults(void)
739{
740 /* sanity */
741 AutoCaller autoCaller(this);
742 AssertComRCReturnVoid(autoCaller.hrc());
743
744 AssertPtr(m->pMachine);
745 ComPtr<IGraphicsAdapter> pGraphicsAdapter;
746 m->pMachine->COMGETTER(GraphicsAdapter)(pGraphicsAdapter.asOutParam());
747 ULONG cMonitors = 0;
748 if (!pGraphicsAdapter.isNull())
749 pGraphicsAdapter->COMGETTER(MonitorCount)(&cMonitors);
750
751 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
752
753 /* Initialize default capturing settings here. */
754 m->bd->fEnabled = false;
755
756 /* First, do a reset so that all internal screen settings objects are destroyed. */
757 i_reset();
758 /* Second, sync (again) to configured machine displays to (re-)create screen settings objects. */
759 i_syncToMachineDisplays(cMonitors);
760}
761
762/**
763 * Returns the full path to the default recording file.
764 *
765 * @returns VBox status code.
766 * @param strFile Where to return the final file name on success.
767 * @param idScreen Screen ID the file is associated to.
768 * @param fWithFileExtension Whether to include the default file extension ('.webm') or not.
769 */
770int RecordingSettings::i_getDefaultFilename(Utf8Str &strFile, uint32_t idScreen, bool fWithFileExtension)
771{
772 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
773
774 strFile = m->pMachine->i_getSettingsFileFull(); // path/to/machinesfolder/vmname/vmname.vbox
775 strFile.stripSuffix();
776 strFile.append(Utf8StrFmt("-screen%RU32", idScreen));
777 if (fWithFileExtension)
778 strFile.append(".webm");
779
780 return VINF_SUCCESS;
781}
782
783/**
784 * Gets a standardized file name from a given template file name.
785 *
786 * @returns VBox status code.
787 * @param strFile Where to return the final file name on success.
788 * @param idScreen Screen ID the file is associated to.
789 * @param strTemplate Template file name to use.
790 * A default file name will be used when empty.
791 */
792int RecordingSettings::i_getFilename(Utf8Str &strFile, uint32_t idScreen, const Utf8Str &strTemplate)
793{
794 strFile = strTemplate;
795
796 if (strFile.isEmpty())
797 return i_getDefaultFilename(strFile, idScreen, true /* fWithFileExtension */);
798
799 /* We force adding a .webm suffix to (hopefully) not let the user overwrite other important stuff. */
800 strFile.stripSuffix();
801
802 Utf8Str strDotExt = ".webm";
803
804 /* We also force adding the screen id suffix, at least for the moment, as FE/Qt only offers settings a single file name
805 * for *all* enabled screens. */
806 char szSuffScreen[] = "-screen";
807 Utf8Str strSuff = Utf8StrFmt("%s%RU32", szSuffScreen, idScreen);
808 if (!strFile.endsWith(strSuff, Utf8Str::CaseInsensitive))
809 {
810 /** @todo The following line checks whether there already is a screen suffix, as FE/Qt currently always works with
811 * screen 0 as the file name. Remove the following if block when FE/Qt supports this properly. */
812 Utf8Str strSuffScreen0 = Utf8StrFmt("%s%RU32", szSuffScreen, 0);
813 if (strFile.endsWith(strSuffScreen0, Utf8Str::CaseInsensitive))
814 strFile.truncate(strFile.length() - strSuffScreen0.length());
815
816 strFile += strSuff; /* Add the suffix with the correct screen ID. */
817 }
818
819 strFile += strDotExt;
820
821 LogRel2(("Recording: File name '%s' -> '%s'\n", strTemplate.c_str(), strFile.c_str()));
822
823 return VINF_SUCCESS;
824}
825
826/**
827 * Determines whether the recording settings currently can be changed or not.
828 *
829 * @returns \c true if the settings can be changed, \c false if not.
830 */
831bool RecordingSettings::i_canChangeSettings(void)
832{
833 AutoAnyStateDependency adep(m->pMachine);
834 if (FAILED(adep.hrc()))
835 return false;
836
837 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
838
839 /* Only allow settings to be changed when recording is disabled when the machine is running. */
840 if ( Global::IsOnline(adep.machineState())
841 && m->bd->fEnabled)
842 {
843 return false;
844 }
845
846 return true;
847}
848
849/**
850 * Gets called when the machine object needs to know that the recording settings
851 * have been changed.
852 */
853void RecordingSettings::i_onSettingsChanged(void)
854{
855 LogFlowThisFuncEnter();
856
857 AutoWriteLock mlock(m->pMachine COMMA_LOCKVAL_SRC_POS);
858 m->pMachine->i_setModified(Machine::IsModified_Recording);
859 mlock.release();
860
861 LogFlowThisFuncLeave();
862}
863
864/**
865 * Starts recording.
866 *
867 * @returns VBox status code.
868 */
869int RecordingSettings::i_start(void)
870{
871 AssertReturn(m->mProgress.isNull(), VERR_WRONG_ORDER);
872
873 HRESULT hrc = m->pMachine->i_onRecordingStateChange(TRUE /* Enable recording */, m->mProgress.asOutParam());
874 if (FAILED(hrc))
875 return VERR_RECORDING_INIT_FAILED;
876
877 return VINF_SUCCESS;
878}
879
880/**
881 * Stops recording. Does nothing if recording already has been stopped.
882 *
883 * @returns VBox status code.
884 */
885int RecordingSettings::i_stop(void)
886{
887 if (m->mProgress.isNull()) /* Not started? */
888 return VINF_SUCCESS;
889
890 /* Note: Returned progress object is just a dummy / not needed for disabling recording. */
891 HRESULT hrc = m->pMachine->i_onRecordingStateChange(FALSE /* Disable recording */, m->mProgress.asOutParam());
892 if (SUCCEEDED(hrc))
893 m->mProgress.setNull();
894
895 if (FAILED(hrc))
896 return VERR_COM_UNEXPECTED;
897
898 return VINF_SUCCESS;
899}
900
901/**
902 * Synchronizes the screen settings (COM) objects and configuration data
903 * to the number of the machine's configured displays.
904 *
905 * Note: This function ASSUMES that we always have configured VM displays
906 * as a consequtive sequence with no holes in between.
907 */
908int RecordingSettings::i_syncToMachineDisplays(uint32_t cDisplays)
909{
910 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
911
912 LogThisFunc(("%p: cDisplays=%RU32 vs. %zu\n", this, cDisplays, m->mapScreenObj.size()));
913
914 /* If counts match, take a shortcut. */
915 if (cDisplays == m->mapScreenObj.size())
916 return VINF_SUCCESS;
917
918 /* Create all new screen settings objects which are not there yet. */
919 for (ULONG i = 0; i < cDisplays; i++)
920 {
921 if (m->mapScreenObj.find(i) == m->mapScreenObj.end())
922 {
923 settings::RecordingScreenSettings defaultScreenSettings(i /* Screen ID */); /* Apply default settings. */
924
925 int vrc2 = i_createScreenObj(m->mapScreenObj, i /* Screen ID */, defaultScreenSettings);
926 AssertRC(vrc2);
927 }
928 }
929
930 /* Remove all left over screen settings objects which are not needed anymore. */
931 for (ULONG i = cDisplays; i < (ULONG)m->mapScreenObj.size(); i++)
932 {
933 int vrc2 = i_destroyScreenObj(m->mapScreenObj, i /* Screen ID */);
934 AssertRC(vrc2);
935 }
936
937 Assert(m->mapScreenObj.size() == cDisplays);
938
939 LogFlowThisFuncLeave();
940 return VINF_SUCCESS;
941}
942
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