VirtualBox

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

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

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.1 KB
Line 
1/* $Id: RecordingSettingsImpl.cpp 76553 2019-01-01 01:45:53Z 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 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 audio adapter 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 *that)
104{
105 LogFlowThisFuncEnter();
106 LogFlowThisFunc(("aParent: %p, that: %p\n", aParent, that));
107
108 ComAssertRet(aParent && that, 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 m->pPeer = that;
118
119 AutoWriteLock thatlock(that COMMA_LOCKVAL_SRC_POS);
120
121 m->bd.share(that->m->bd);
122 m->mapScreenObj = that->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 *that)
136{
137 LogFlowThisFuncEnter();
138 LogFlowThisFunc(("aParent: %p, that: %p\n", aParent, that));
139
140 ComAssertRet(aParent && that, 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(that COMMA_LOCKVAL_SRC_POS);
152
153 m->bd.attachCopy(that->m->bd);
154 m->mapScreenObj = that->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 if (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 return VINF_SUCCESS;
394}
395
396/**
397 * Loads settings from the given settings.
398 * May be called once right after this object creation.
399 *
400 * @param data Capture settings to load from.
401 *
402 * @note Locks this object for writing.
403 */
404HRESULT RecordingSettings::i_loadSettings(const settings::RecordingSettings &data)
405{
406 LogFlowThisFuncEnter();
407
408 AutoCaller autoCaller(this);
409 AssertComRCReturnRC(autoCaller.rc());
410
411 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
412
413 HRESULT rc = S_OK;
414
415 i_reset();
416
417 LogFlowThisFunc(("Data has %zu screens\n", data.mapScreens.size()));
418
419 settings::RecordingScreenMap::const_iterator itScreen = data.mapScreens.begin();
420 while (itScreen != data.mapScreens.end())
421 {
422 int vrc = i_createScreenObj(m->mapScreenObj,
423 itScreen->first /* uScreenId */, itScreen->second /* Settings */);
424 if (RT_FAILURE(vrc))
425 {
426 rc = E_OUTOFMEMORY;
427 break;
428 }
429
430 ++itScreen;
431 }
432
433 if (FAILED(rc))
434 return rc;
435
436 ComAssertComRC(rc);
437 Assert(m->mapScreenObj.size() == data.mapScreens.size());
438
439 // simply copy
440 m->bd.assignCopy(&data);
441
442 LogFlowThisFunc(("Returning %Rhrc\n", rc));
443 return rc;
444}
445
446/**
447 * Resets the internal object state by destroying all screen settings objects.
448 */
449void RecordingSettings::i_reset(void)
450{
451 LogFlowThisFuncEnter();
452
453 i_destroyAllScreenObj(m->mapScreenObj);
454 m->bd->mapScreens.clear();
455}
456
457/**
458 * Saves settings to the given settings.
459 *
460 * @param data Where to store the capture settings to.
461 *
462 * @note Locks this object for reading.
463 */
464HRESULT RecordingSettings::i_saveSettings(settings::RecordingSettings &data)
465{
466 LogFlowThisFuncEnter();
467
468 AutoCaller autoCaller(this);
469 AssertComRCReturnRC(autoCaller.rc());
470
471 int rc2 = i_syncToMachineDisplays();
472 AssertRC(rc2);
473
474 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
475
476 data = *m->bd.data();
477
478 settings::RecordingScreenMap::iterator itScreen = data.mapScreens.begin();
479 while (itScreen != data.mapScreens.end())
480 {
481 /* Store relative path of capture file if possible. */
482 m->pMachine->i_copyPathRelativeToMachine(itScreen->second.File.strName /* Source */,
483 itScreen->second.File.strName /* Target */);
484 ++itScreen;
485 }
486
487 LogFlowThisFuncLeave();
488 return S_OK;
489}
490
491void RecordingSettings::i_rollback()
492{
493 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
494 m->bd.rollback();
495}
496
497void RecordingSettings::i_commit()
498{
499 /* sanity */
500 AutoCaller autoCaller(this);
501 AssertComRCReturnVoid(autoCaller.rc());
502
503 /* sanity too */
504 AutoCaller peerCaller(m->pPeer);
505 AssertComRCReturnVoid(peerCaller.rc());
506
507 /* lock both for writing since we modify both (mPeer is "master" so locked
508 * first) */
509 AutoMultiWriteLock2 alock(m->pPeer, this COMMA_LOCKVAL_SRC_POS);
510
511 if (m->bd.isBackedUp())
512 {
513 m->bd.commit();
514 if (m->pPeer)
515 {
516 /* attach new data to the peer and reshare it */
517 AutoWriteLock peerlock(m->pPeer COMMA_LOCKVAL_SRC_POS);
518 m->pPeer->m->bd.attach(m->bd);
519 }
520 }
521}
522
523void RecordingSettings::i_copyFrom(RecordingSettings *aThat)
524{
525 AssertReturnVoid(aThat != NULL);
526
527 /* sanity */
528 AutoCaller autoCaller(this);
529 AssertComRCReturnVoid(autoCaller.rc());
530
531 /* sanity too */
532 AutoCaller thatCaller(aThat);
533 AssertComRCReturnVoid(thatCaller.rc());
534
535 /* peer is not modified, lock it for reading (aThat is "master" so locked
536 * first) */
537 AutoReadLock rl(aThat COMMA_LOCKVAL_SRC_POS);
538 AutoWriteLock wl(this COMMA_LOCKVAL_SRC_POS);
539
540 /* this will back up current data */
541 m->bd.assignCopy(aThat->m->bd);
542}
543
544void RecordingSettings::i_applyDefaults(void)
545{
546 /* sanity */
547 AutoCaller autoCaller(this);
548 AssertComRCReturnVoid(autoCaller.rc());
549
550 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
551
552 /* Initialize default capturing settings here. */
553}
554
555/**
556 * Returns the full path to the default video capture file.
557 */
558int RecordingSettings::i_getDefaultFilename(Utf8Str &strFile, bool fWithFileExtension)
559{
560 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
561
562 strFile = m->pMachine->i_getSettingsFileFull(); // path/to/machinesfolder/vmname/vmname.vbox
563 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
564 if (fWithFileExtension)
565 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
566
567 return VINF_SUCCESS;
568}
569
570/**
571 * Determines whether the recording settings currently can be changed or not.
572 *
573 * @returns \c true if the settings can be changed, \c false if not.
574 */
575bool RecordingSettings::i_canChangeSettings(void)
576{
577 AutoAnyStateDependency adep(m->pMachine);
578 if (FAILED(adep.rc()))
579 return false;
580
581 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
582
583 /* Only allow settings to be changed when recording is disabled when the machine is running. */
584 if ( Global::IsOnline(adep.machineState())
585 && m->bd->fEnabled)
586 {
587 return false;
588 }
589
590 return true;
591}
592
593/**
594 * Gets called when the machine object needs to know that the recording settings
595 * have been changed.
596 */
597void RecordingSettings::i_onSettingsChanged(void)
598{
599 LogFlowThisFuncEnter();
600
601 AutoWriteLock mlock(m->pMachine COMMA_LOCKVAL_SRC_POS);
602 m->pMachine->i_setModified(Machine::IsModified_Recording);
603 mlock.release();
604
605 LogFlowThisFuncLeave();
606}
607
608/**
609 * Synchronizes the screen settings (COM) objects and configuration data
610 * to the number of the machine's configured displays.
611 */
612int RecordingSettings::i_syncToMachineDisplays(void)
613{
614 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
615
616 AssertPtr(m->pMachine);
617 const ULONG cMonitors = m->pMachine->i_getMonitorCount();
618
619 LogFlowThisFunc(("cMonitors=%RU32\n", cMonitors));
620 LogFlowThisFunc(("Data screen count = %zu, COM object count = %zu\n", m->bd->mapScreens.size(), m->mapScreenObj.size()));
621
622 /* If counts match, take a shortcut. */
623 if (cMonitors == m->mapScreenObj.size())
624 return VINF_SUCCESS;
625
626 /* Create all new screen settings objects which are not there yet. */
627 for (ULONG i = 0; i < cMonitors; i++)
628 {
629 if (m->mapScreenObj.find(i) == m->mapScreenObj.end())
630 {
631 settings::RecordingScreenMap::const_iterator itScreen = m->bd->mapScreens.find(i);
632 if (itScreen == m->bd->mapScreens.end())
633 {
634 settings::RecordingScreenSettings defaultScreenSettings; /* Apply default settings. */
635 m->bd->mapScreens[i] = defaultScreenSettings;
636 }
637
638 int vrc2 = i_createScreenObj(m->mapScreenObj, i /* Screen ID */, m->bd->mapScreens[i]);
639 AssertRC(vrc2);
640 }
641 }
642
643 /* Remove all left over screen settings objects which are not needed anymore. */
644 const ULONG cSettings = (ULONG)m->mapScreenObj.size();
645 for (ULONG i = cMonitors; i < cSettings; i++)
646 {
647 m->bd->mapScreens.erase(i);
648 int vrc2 = i_destroyScreenObj(m->mapScreenObj, i /* Screen ID */);
649 AssertRC(vrc2);
650 }
651
652 Assert(m->mapScreenObj.size() == cMonitors);
653 Assert(m->bd->mapScreens.size() == cMonitors);
654
655 LogFlowThisFuncLeave();
656 return VINF_SUCCESS;
657}
658
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