VirtualBox

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

Last change on this file since 78071 was 78071, checked in by vboxsync, 6 years ago

Recording/Main: Implemented RecordingSettings::i_applyDefaults().

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.4 KB
Line 
1/* $Id: RecordingSettingsImpl.cpp 78071 2019-04-10 10:01:04Z vboxsync $ */
2/** @file
3 *
4 * VirtualBox COM class implementation - Machine capture settings.
5 */
6
7/*
8 * Copyright (C) 2018-2019 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19#define LOG_GROUP LOG_GROUP_MAIN_RECORDINGSETTINGS
20#include "LoggingNew.h"
21
22#include "RecordingSettingsImpl.h"
23#include "RecordingScreenSettingsImpl.h"
24#include "MachineImpl.h"
25
26#include <iprt/cpp/utils.h>
27#include <VBox/settings.h>
28
29#include "AutoStateDep.h"
30#include "AutoCaller.h"
31#include "Global.h"
32
33////////////////////////////////////////////////////////////////////////////////
34//
35// RecordSettings private data definition
36//
37////////////////////////////////////////////////////////////////////////////////
38
39struct RecordingSettings::Data
40{
41 Data()
42 : pMachine(NULL)
43 { }
44
45 Machine * const pMachine;
46 const ComObjPtr<RecordingSettings> pPeer;
47 RecordScreenSettingsMap mapScreenObj;
48
49 // use the XML settings structure in the members for simplicity
50 Backupable<settings::RecordingSettings> bd;
51};
52
53DEFINE_EMPTY_CTOR_DTOR(RecordingSettings)
54
55HRESULT RecordingSettings::FinalConstruct()
56{
57 return BaseFinalConstruct();
58}
59
60void RecordingSettings::FinalRelease()
61{
62 uninit();
63 BaseFinalRelease();
64}
65
66/**
67 * Initializes the recording settings object.
68 *
69 * @returns COM result indicator
70 */
71HRESULT RecordingSettings::init(Machine *aParent)
72{
73 LogFlowThisFuncEnter();
74 LogFlowThisFunc(("aParent: %p\n", aParent));
75
76 ComAssertRet(aParent, E_INVALIDARG);
77
78 /* Enclose the state transition NotReady->InInit->Ready */
79 AutoInitSpan autoInitSpan(this);
80 AssertReturn(autoInitSpan.isOk(), E_FAIL);
81
82 m = new Data();
83
84 /* share the parent weakly */
85 unconst(m->pMachine) = aParent;
86
87 m->bd.allocate();
88
89 autoInitSpan.setSucceeded();
90
91 LogFlowThisFuncLeave();
92 return S_OK;
93}
94
95/**
96 * Initializes the capture settings object given another capture settings object
97 * (a kind of copy constructor). This object shares data with
98 * the object passed as an argument.
99 *
100 * @note This object must be destroyed before the original object
101 * it shares data with is destroyed.
102 */
103HRESULT RecordingSettings::init(Machine *aParent, RecordingSettings *aThat)
104{
105 LogFlowThisFuncEnter();
106 LogFlowThisFunc(("aParent: %p, aThat: %p\n", aParent, aThat));
107
108 ComAssertRet(aParent && aThat, E_INVALIDARG);
109
110 /* Enclose the state transition NotReady->InInit->Ready */
111 AutoInitSpan autoInitSpan(this);
112 AssertReturn(autoInitSpan.isOk(), E_FAIL);
113
114 m = new Data();
115
116 unconst(m->pMachine) = aParent;
117 unconst(m->pPeer) = aThat;
118
119 AutoWriteLock thatlock(aThat COMMA_LOCKVAL_SRC_POS);
120
121 m->bd.share(aThat->m->bd);
122 m->mapScreenObj = aThat->m->mapScreenObj;
123
124 autoInitSpan.setSucceeded();
125
126 LogFlowThisFuncLeave();
127 return S_OK;
128}
129
130/**
131 * Initializes the guest object given another guest object
132 * (a kind of copy constructor). This object makes a private copy of data
133 * of the original object passed as an argument.
134 */
135HRESULT RecordingSettings::initCopy(Machine *aParent, RecordingSettings *aThat)
136{
137 LogFlowThisFuncEnter();
138 LogFlowThisFunc(("aParent: %p, aThat: %p\n", aParent, aThat));
139
140 ComAssertRet(aParent && aThat, E_INVALIDARG);
141
142 /* Enclose the state transition NotReady->InInit->Ready */
143 AutoInitSpan autoInitSpan(this);
144 AssertReturn(autoInitSpan.isOk(), E_FAIL);
145
146 m = new Data();
147
148 unconst(m->pMachine) = aParent;
149 // mPeer is left null
150
151 AutoWriteLock thatlock(aThat COMMA_LOCKVAL_SRC_POS);
152
153 m->bd.attachCopy(aThat->m->bd);
154 m->mapScreenObj = aThat->m->mapScreenObj;
155
156 autoInitSpan.setSucceeded();
157
158 LogFlowThisFuncLeave();
159 return S_OK;
160}
161
162/**
163 * Uninitializes the instance and sets the ready flag to FALSE.
164 * Called either from FinalRelease() or by the parent when it gets destroyed.
165 */
166void RecordingSettings::uninit()
167{
168 LogFlowThisFuncEnter();
169
170 /* Enclose the state transition Ready->InUninit->NotReady */
171 AutoUninitSpan autoUninitSpan(this);
172 if (autoUninitSpan.uninitDone())
173 return;
174
175 /* Note: Do *not* call i_reset() here, as the shared recording configuration
176 * otherwise gets destructed when this object goes out of scope or is destroyed. */
177
178 m->bd.free();
179
180 unconst(m->pPeer) = NULL;
181 unconst(m->pMachine) = NULL;
182
183 delete m;
184 m = NULL;
185
186 LogFlowThisFuncLeave();
187}
188
189// IRecordSettings properties
190/////////////////////////////////////////////////////////////////////////////
191
192HRESULT RecordingSettings::getEnabled(BOOL *enabled)
193{
194 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
195
196 *enabled = m->bd->fEnabled;
197
198 return S_OK;
199}
200
201HRESULT RecordingSettings::setEnabled(BOOL enable)
202{
203 LogFlowThisFuncEnter();
204
205 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
206
207 const bool fEnabled = RT_BOOL(enable);
208
209 HRESULT rc = S_OK;
210
211 if (m->bd->fEnabled != fEnabled)
212 {
213 m->bd.backup();
214 m->bd->fEnabled = fEnabled;
215
216 alock.release();
217 rc = m->pMachine->i_onRecordingChange(enable);
218 if (FAILED(rc))
219 {
220 /*
221 * Normally we would do the actual change _after_ i_onCaptureChange() succeeded.
222 * We cannot do this because that function uses RecordSettings::GetEnabled to
223 * determine if it should start or stop capturing. Therefore we need to manually
224 * undo change.
225 */
226 alock.acquire();
227 m->bd->fEnabled = m->bd.backedUpData()->fEnabled;
228 }
229 else
230 {
231 AutoWriteLock mlock(m->pMachine COMMA_LOCKVAL_SRC_POS); // mParent is const, needs no locking
232 m->pMachine->i_setModified(Machine::IsModified_Recording);
233
234 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
235 if (Global::IsOnline(m->pMachine->i_getMachineState()))
236 rc = m->pMachine->i_saveSettings(NULL);
237 }
238 }
239
240 return rc;
241}
242
243HRESULT RecordingSettings::getScreens(std::vector<ComPtr<IRecordingScreenSettings> > &aRecordScreenSettings)
244{
245 LogFlowThisFuncEnter();
246
247 i_syncToMachineDisplays();
248
249 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
250
251 aRecordScreenSettings.clear();
252 aRecordScreenSettings.resize(m->mapScreenObj.size());
253
254 RecordScreenSettingsMap::const_iterator itScreenSettings = m->mapScreenObj.begin();
255 size_t i = 0;
256 while (itScreenSettings != m->mapScreenObj.end())
257 {
258 itScreenSettings->second.queryInterfaceTo(aRecordScreenSettings[i].asOutParam());
259 Assert(aRecordScreenSettings[i].isNotNull());
260 ++i;
261 ++itScreenSettings;
262 }
263
264 Assert(aRecordScreenSettings.size() == m->mapScreenObj.size());
265
266 return S_OK;
267}
268
269HRESULT RecordingSettings::getScreenSettings(ULONG uScreenId, ComPtr<IRecordingScreenSettings> &aRecordScreenSettings)
270{
271 LogFlowThisFuncEnter();
272
273 i_syncToMachineDisplays();
274
275 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
276
277 if (uScreenId + 1 > m->mapScreenObj.size())
278 return setError(E_INVALIDARG, tr("Invalid screen ID specified"));
279
280 RecordScreenSettingsMap::const_iterator itScreenSettings = m->mapScreenObj.find(uScreenId);
281 if (itScreenSettings != m->mapScreenObj.end())
282 {
283 itScreenSettings->second.queryInterfaceTo(aRecordScreenSettings.asOutParam());
284 return S_OK;
285 }
286
287 return VBOX_E_OBJECT_NOT_FOUND;
288}
289
290// IRecordSettings methods
291/////////////////////////////////////////////////////////////////////////////
292
293// public methods only for internal purposes
294/////////////////////////////////////////////////////////////////////////////
295
296/**
297 * Adds a screen settings object to a particular map.
298 *
299 * @returns IPRT status code. VERR_ALREADY_EXISTS if the object in question already exists.
300 * @param screenSettingsMap Map to add screen settings to.
301 * @param uScreenId Screen ID to add settings for.
302 * @param data Recording screen settings to use for that screen.
303 */
304int RecordingSettings::i_createScreenObj(RecordScreenSettingsMap &screenSettingsMap,
305 uint32_t uScreenId, const settings::RecordingScreenSettings &data)
306{
307 LogFlowThisFunc(("Screen %RU32\n", uScreenId));
308
309 if (screenSettingsMap.find(uScreenId) != screenSettingsMap.end())
310 {
311 AssertFailed();
312 return VERR_ALREADY_EXISTS;
313 }
314
315 int vrc = VINF_SUCCESS;
316
317 ComObjPtr<RecordingScreenSettings> recordingScreenSettings;
318 HRESULT rc = recordingScreenSettings.createObject();
319 if (SUCCEEDED(rc))
320 {
321 rc = recordingScreenSettings->init(this, uScreenId, data);
322 if (SUCCEEDED(rc))
323 {
324 try
325 {
326 screenSettingsMap[uScreenId] = recordingScreenSettings;
327 }
328 catch (std::bad_alloc &)
329 {
330 vrc = VERR_NO_MEMORY;
331 }
332 }
333 }
334
335 return vrc;
336}
337
338/**
339 * Removes a screen settings object from a particular map.
340 *
341 * @returns IPRT status code. VERR_NOT_FOUND if specified screen was not found.
342 * @param screenSettingsMap Map to remove screen settings from.
343 * @param uScreenId ID of screen to remove.
344 */
345int RecordingSettings::i_destroyScreenObj(RecordScreenSettingsMap &screenSettingsMap, uint32_t uScreenId)
346{
347 LogFlowThisFunc(("Screen %RU32\n", uScreenId));
348
349 AssertReturn(uScreenId > 0, VERR_INVALID_PARAMETER); /* Removing screen 0 isn't a good idea. */
350
351 RecordScreenSettingsMap::iterator itScreen = screenSettingsMap.find(uScreenId);
352 if (itScreen == screenSettingsMap.end())
353 {
354 AssertFailed();
355 return VERR_NOT_FOUND;
356 }
357
358 /* Make sure to consume the pointer before the one of the
359 * iterator gets released. */
360 ComObjPtr<RecordingScreenSettings> pScreenSettings = itScreen->second;
361
362 screenSettingsMap.erase(itScreen);
363
364 pScreenSettings.setNull();
365
366 return VINF_SUCCESS;
367}
368
369/**
370 * Destroys all screen settings objects of a particular map.
371 *
372 * @returns IPRT status code.
373 * @param screenSettingsMap Map to destroy screen settings objects for.
374 */
375int RecordingSettings::i_destroyAllScreenObj(RecordScreenSettingsMap &screenSettingsMap)
376{
377 LogFlowThisFuncEnter();
378
379 RecordScreenSettingsMap::iterator itScreen = screenSettingsMap.begin();
380 while (itScreen != screenSettingsMap.end())
381 {
382 /* Make sure to consume the pointer before the one of the
383 * iterator gets released. */
384 ComObjPtr<RecordingScreenSettings> pScreenSettings = itScreen->second;
385
386 screenSettingsMap.erase(itScreen);
387
388 pScreenSettings.setNull();
389
390 itScreen = screenSettingsMap.begin();
391 }
392
393 Assert(screenSettingsMap.size() == 0);
394 return VINF_SUCCESS;
395}
396
397/**
398 * Loads settings from the given settings.
399 * May be called once right after this object creation.
400 *
401 * @param data Capture settings to load from.
402 *
403 * @note Locks this object for writing.
404 */
405HRESULT RecordingSettings::i_loadSettings(const settings::RecordingSettings &data)
406{
407 LogFlowThisFuncEnter();
408
409 AutoCaller autoCaller(this);
410 AssertComRCReturnRC(autoCaller.rc());
411
412 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
413
414 HRESULT rc = S_OK;
415
416 i_reset();
417
418 LogFlowThisFunc(("Data has %zu screens\n", data.mapScreens.size()));
419
420 settings::RecordingScreenMap::const_iterator itScreen = data.mapScreens.begin();
421 while (itScreen != data.mapScreens.end())
422 {
423 int vrc = i_createScreenObj(m->mapScreenObj,
424 itScreen->first /* uScreenId */, itScreen->second /* Settings */);
425 if (RT_FAILURE(vrc))
426 {
427 rc = E_OUTOFMEMORY;
428 break;
429 }
430
431 ++itScreen;
432 }
433
434 if (FAILED(rc))
435 return rc;
436
437 ComAssertComRC(rc);
438 Assert(m->mapScreenObj.size() == data.mapScreens.size());
439
440 // simply copy
441 m->bd.assignCopy(&data);
442
443 LogFlowThisFunc(("Returning %Rhrc\n", rc));
444 return rc;
445}
446
447/**
448 * Resets the internal object state by destroying all screen settings objects.
449 */
450void RecordingSettings::i_reset(void)
451{
452 LogFlowThisFuncEnter();
453
454 i_destroyAllScreenObj(m->mapScreenObj);
455 m->bd->mapScreens.clear();
456}
457
458/**
459 * Saves settings to the given settings.
460 *
461 * @param data Where to store the capture settings to.
462 *
463 * @note Locks this object for reading.
464 */
465HRESULT RecordingSettings::i_saveSettings(settings::RecordingSettings &data)
466{
467 LogFlowThisFuncEnter();
468
469 AutoCaller autoCaller(this);
470 AssertComRCReturnRC(autoCaller.rc());
471
472 int rc2 = i_syncToMachineDisplays();
473 AssertRC(rc2);
474
475 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
476
477 data = *m->bd.data();
478
479 settings::RecordingScreenMap::iterator itScreen = data.mapScreens.begin();
480 while (itScreen != data.mapScreens.end())
481 {
482 /* Store relative path of capture file if possible. */
483 m->pMachine->i_copyPathRelativeToMachine(itScreen->second.File.strName /* Source */,
484 itScreen->second.File.strName /* Target */);
485 ++itScreen;
486 }
487
488 LogFlowThisFuncLeave();
489 return S_OK;
490}
491
492void RecordingSettings::i_rollback()
493{
494 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
495 m->bd.rollback();
496}
497
498void RecordingSettings::i_commit()
499{
500 /* sanity */
501 AutoCaller autoCaller(this);
502 AssertComRCReturnVoid(autoCaller.rc());
503
504 /* sanity too */
505 AutoCaller peerCaller(m->pPeer);
506 AssertComRCReturnVoid(peerCaller.rc());
507
508 /* lock both for writing since we modify both (mPeer is "master" so locked
509 * first) */
510 AutoMultiWriteLock2 alock(m->pPeer, this COMMA_LOCKVAL_SRC_POS);
511
512 if (m->bd.isBackedUp())
513 {
514 m->bd.commit();
515 if (m->pPeer)
516 {
517 /* attach new data to the peer and reshare it */
518 AutoWriteLock peerlock(m->pPeer COMMA_LOCKVAL_SRC_POS);
519 m->pPeer->m->bd.attach(m->bd);
520 }
521 }
522}
523
524void RecordingSettings::i_copyFrom(RecordingSettings *aThat)
525{
526 AssertReturnVoid(aThat != NULL);
527
528 /* sanity */
529 AutoCaller autoCaller(this);
530 AssertComRCReturnVoid(autoCaller.rc());
531
532 /* sanity too */
533 AutoCaller thatCaller(aThat);
534 AssertComRCReturnVoid(thatCaller.rc());
535
536 /* peer is not modified, lock it for reading (aThat is "master" so locked
537 * first) */
538 AutoReadLock rl(aThat COMMA_LOCKVAL_SRC_POS);
539 AutoWriteLock wl(this COMMA_LOCKVAL_SRC_POS);
540
541 /* this will back up current data */
542 m->bd.assignCopy(aThat->m->bd);
543}
544
545void RecordingSettings::i_applyDefaults(void)
546{
547 /* sanity */
548 AutoCaller autoCaller(this);
549 AssertComRCReturnVoid(autoCaller.rc());
550
551 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
552
553 /* Initialize default capturing settings here. */
554 m->bd->fEnabled = false;
555
556 /* First, do a reset so that all internal screen settings objects are destroyed. */
557 i_reset();
558 /* Second, sync (again) to configured machine displays to (re-)create screen settings objects. */
559 i_syncToMachineDisplays();
560}
561
562/**
563 * Returns the full path to the default video capture file.
564 */
565int RecordingSettings::i_getDefaultFilename(Utf8Str &strFile, bool fWithFileExtension)
566{
567 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
568
569 strFile = m->pMachine->i_getSettingsFileFull(); // path/to/machinesfolder/vmname/vmname.vbox
570 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
571 if (fWithFileExtension)
572 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
573
574 return VINF_SUCCESS;
575}
576
577/**
578 * Determines whether the recording settings currently can be changed or not.
579 *
580 * @returns \c true if the settings can be changed, \c false if not.
581 */
582bool RecordingSettings::i_canChangeSettings(void)
583{
584 AutoAnyStateDependency adep(m->pMachine);
585 if (FAILED(adep.rc()))
586 return false;
587
588 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
589
590 /* Only allow settings to be changed when recording is disabled when the machine is running. */
591 if ( Global::IsOnline(adep.machineState())
592 && m->bd->fEnabled)
593 {
594 return false;
595 }
596
597 return true;
598}
599
600/**
601 * Gets called when the machine object needs to know that the recording settings
602 * have been changed.
603 */
604void RecordingSettings::i_onSettingsChanged(void)
605{
606 LogFlowThisFuncEnter();
607
608 AutoWriteLock mlock(m->pMachine COMMA_LOCKVAL_SRC_POS);
609 m->pMachine->i_setModified(Machine::IsModified_Recording);
610 mlock.release();
611
612 LogFlowThisFuncLeave();
613}
614
615/**
616 * Synchronizes the screen settings (COM) objects and configuration data
617 * to the number of the machine's configured displays.
618 */
619int RecordingSettings::i_syncToMachineDisplays(void)
620{
621 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
622
623 AssertPtr(m->pMachine);
624 const ULONG cMonitors = m->pMachine->i_getMonitorCount();
625
626 LogFlowThisFunc(("cMonitors=%RU32\n", cMonitors));
627 LogFlowThisFunc(("Data screen count = %zu, COM object count = %zu\n", m->bd->mapScreens.size(), m->mapScreenObj.size()));
628
629 /* If counts match, take a shortcut. */
630 if (cMonitors == m->mapScreenObj.size())
631 return VINF_SUCCESS;
632
633 /* Create all new screen settings objects which are not there yet. */
634 for (ULONG i = 0; i < cMonitors; i++)
635 {
636 if (m->mapScreenObj.find(i) == m->mapScreenObj.end())
637 {
638 settings::RecordingScreenMap::const_iterator itScreen = m->bd->mapScreens.find(i);
639 if (itScreen == m->bd->mapScreens.end())
640 {
641 settings::RecordingScreenSettings defaultScreenSettings; /* Apply default settings. */
642 m->bd->mapScreens[i] = defaultScreenSettings;
643 }
644
645 int vrc2 = i_createScreenObj(m->mapScreenObj, i /* Screen ID */, m->bd->mapScreens[i]);
646 AssertRC(vrc2);
647 }
648 }
649
650 /* Remove all left over screen settings objects which are not needed anymore. */
651 const ULONG cSettings = (ULONG)m->mapScreenObj.size();
652 for (ULONG i = cMonitors; i < cSettings; i++)
653 {
654 m->bd->mapScreens.erase(i);
655 int vrc2 = i_destroyScreenObj(m->mapScreenObj, i /* Screen ID */);
656 AssertRC(vrc2);
657 }
658
659 Assert(m->mapScreenObj.size() == cMonitors);
660 Assert(m->bd->mapScreens.size() == cMonitors);
661
662 LogFlowThisFuncLeave();
663 return VINF_SUCCESS;
664}
665
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