Changeset 105266 in vbox for trunk/src/VBox/Main/src-client
- Timestamp:
- Jul 11, 2024 7:49:37 AM (5 months ago)
- Location:
- trunk/src/VBox/Main/src-client
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/src-client/ConsoleImpl.cpp
r105095 r105266 6094 6094 * 6095 6095 * @returns VBox status code. 6096 * @retval V ERR_NO_CHANGE if the recording state has not been changed.6096 * @retval VINF_NO_CHANGE if the recording state has not been changed. 6097 6097 * @param fEnable Whether to enable or disable the recording. 6098 6098 * @param pAutoLock Pointer to auto write lock to use for attaching/detaching required driver(s) at runtime. 6099 */ 6100 int Console::i_recordingEnable(BOOL fEnable, util::AutoWriteLock *pAutoLock) 6099 * @param pProgress Progress object to use. Optional and can be NULL. 6100 */ 6101 int Console::i_recordingEnable(BOOL fEnable, util::AutoWriteLock *pAutoLock, ComPtr<IProgress> &pProgress) 6101 6102 { 6102 6103 AssertPtrReturn(pAutoLock, VERR_INVALID_POINTER); 6103 6104 6105 bool const fIsEnabled = mRecording.mCtx.IsStarted(); 6106 6107 if (RT_BOOL(fEnable) == fIsEnabled) /* No change? Bail out. */ 6108 return VINF_NO_CHANGE; 6109 6104 6110 int vrc = VINF_SUCCESS; 6105 6111 6106 6112 Display *pDisplay = i_getDisplay(); 6107 if (pDisplay) 6108 { 6109 bool const fIsEnabled = mRecording.mCtx.IsStarted(); 6110 6111 if (RT_BOOL(fEnable) != fIsEnabled) 6112 { 6113 LogRel(("Recording: %s\n", fEnable ? "Enabling" : "Disabling")); 6114 6115 SafeVMPtrQuiet ptrVM(this); 6116 if (ptrVM.isOk()) 6113 AssertPtrReturn(pDisplay, VERR_INVALID_POINTER); 6114 6115 LogRel(("Recording: %s\n", fEnable ? "Enabling" : "Disabling")); 6116 6117 SafeVMPtrQuiet ptrVM(this); 6118 if (ptrVM.isOk()) 6119 { 6120 if (fEnable) 6121 { 6122 vrc = i_recordingCreate(pProgress); 6123 if (RT_SUCCESS(vrc)) 6117 6124 { 6118 if (fEnable) 6125 # ifdef VBOX_WITH_AUDIO_RECORDING 6126 /* Attach the video recording audio driver if required. */ 6127 if ( mRecording.mCtx.IsFeatureEnabled(RecordingFeature_Audio) 6128 && mRecording.mAudioRec) 6119 6129 { 6120 vrc = i_recordingCreate();6130 vrc = mRecording.mAudioRec->applyConfiguration(mRecording.mCtx.GetConfig()); 6121 6131 if (RT_SUCCESS(vrc)) 6122 { 6123 # ifdef VBOX_WITH_AUDIO_RECORDING 6124 /* Attach the video recording audio driver if required. */ 6125 if ( mRecording.mCtx.IsFeatureEnabled(RecordingFeature_Audio) 6126 && mRecording.mAudioRec) 6127 { 6128 vrc = mRecording.mAudioRec->applyConfiguration(mRecording.mCtx.GetConfig()); 6129 if (RT_SUCCESS(vrc)) 6130 vrc = mRecording.mAudioRec->doAttachDriverViaEmt(ptrVM.rawUVM(), ptrVM.vtable(), pAutoLock); 6131 6132 if (RT_FAILURE(vrc)) 6133 setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Attaching to audio recording driver failed (%Rrc) -- please consult log file for details"), vrc); 6134 } 6132 vrc = mRecording.mAudioRec->doAttachDriverViaEmt(ptrVM.rawUVM(), ptrVM.vtable(), pAutoLock); 6133 6134 if (RT_FAILURE(vrc)) 6135 mRecording.mCtx.SetError(vrc, Utf8StrFmt(tr("Attaching audio recording driver failed (%Rrc)"), vrc)); 6136 } 6135 6137 # endif 6136 if ( RT_SUCCESS(vrc) 6137 && mRecording.mCtx.IsReady()) /* Any video recording (audio and/or video) feature enabled? */ 6138 { 6139 vrc = i_recordingStart(pAutoLock); 6140 if (RT_FAILURE(vrc)) 6141 setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Recording start failed (%Rrc) -- please consult log file for details"), vrc); 6142 } 6143 } 6144 else 6145 setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Recording initialization failed (%Rrc) -- please consult log file for details"), vrc); 6146 6147 if (RT_FAILURE(vrc)) 6148 LogRel(("Recording: Failed to enable with %Rrc\n", vrc)); 6149 } 6150 else 6138 if ( RT_SUCCESS(vrc) 6139 && mRecording.mCtx.IsReady()) /* Any video recording (audio and/or video) feature enabled? */ 6151 6140 { 6152 vrc = i_recordingStop(pAutoLock); 6153 if (RT_SUCCESS(vrc)) 6154 { 6155 # ifdef VBOX_WITH_AUDIO_RECORDING 6156 if (mRecording.mAudioRec) 6157 mRecording.mAudioRec->doDetachDriverViaEmt(ptrVM.rawUVM(), ptrVM.vtable(), pAutoLock); 6158 # endif 6159 i_recordingDestroy(); 6160 } 6161 else 6162 setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Recording stop failed (%Rrc) -- please consult log file for details"), vrc); 6141 vrc = i_recordingStart(pAutoLock); 6163 6142 } 6164 6143 } 6165 else6166 vrc = VERR_VM_INVALID_VM_STATE;6167 6144 6168 6145 if (RT_FAILURE(vrc)) 6169 LogRel(("Recording: %s failed with %Rrc\n", fEnable ? "Enabling" : "Disabling", vrc)); 6170 } 6171 else /* Should not happen. */ 6172 { 6173 vrc = VERR_NO_CHANGE; 6174 setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Recording already %s"), fIsEnabled ? tr("enabled") : tr("disabled")); 6175 } 6176 } 6146 LogRel(("Recording: Failed to enable with %Rrc\n", vrc)); 6147 } 6148 else /* Disable */ 6149 { 6150 vrc = i_recordingStop(pAutoLock); 6151 if (RT_SUCCESS(vrc)) 6152 { 6153 # ifdef VBOX_WITH_AUDIO_RECORDING 6154 if (mRecording.mAudioRec) 6155 { 6156 vrc = mRecording.mAudioRec->doDetachDriverViaEmt(ptrVM.rawUVM(), ptrVM.vtable(), pAutoLock); 6157 if (RT_FAILURE(vrc)) 6158 mRecording.mCtx.SetError(vrc, Utf8StrFmt(tr("Detaching audio recording driver failed (%Rrc) -- please consult log file for details"), vrc)); 6159 } 6160 # endif 6161 i_recordingDestroy(); 6162 } 6163 } 6164 } 6165 else 6166 vrc = VERR_VM_INVALID_VM_STATE; 6167 6168 if (RT_FAILURE(vrc)) 6169 LogRel(("Recording: %s failed with %Rrc\n", fEnable ? "Enabling" : "Disabling", vrc)); 6177 6170 6178 6171 return vrc; … … 6181 6174 6182 6175 /** 6183 * Called by IInternalSessionControl::OnRecording Change().6184 */ 6185 HRESULT Console::i_onRecording Change(BOOL fEnabled)6186 { 6187 AutoCaller autoCaller(this); 6188 AssertComRCReturnRC(autoCaller.hrc());6176 * Called by IInternalSessionControl::OnRecordingStateChange(). 6177 */ 6178 HRESULT Console::i_onRecordingStateChange(BOOL aEnable, ComPtr<IProgress> &aProgress) 6179 { 6180 #ifdef VBOX_WITH_RECORDING 6181 HRESULT hrc = S_OK; 6189 6182 6190 6183 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 6191 6184 6192 HRESULT hrc = S_OK;6193 #ifdef VBOX_WITH_RECORDING 6185 LogRel2(("Recording: State changed (%s)\n", aEnable ? "enabled" : "disabled")); 6186 6194 6187 /* Don't trigger recording changes if the VM isn't running. */ 6195 6188 SafeVMPtrQuiet ptrVM(this); 6196 6189 if (ptrVM.isOk()) 6197 6190 { 6198 LogFlowThisFunc(("fEnabled=%RTbool\n", RT_BOOL(fEnabled))); 6199 6200 int vrc = i_recordingEnable(fEnabled, &alock); 6201 if (RT_SUCCESS(vrc)) 6202 { 6203 alock.release(); 6204 ::FireRecordingChangedEvent(mEventSource); 6205 } 6206 else /* Error set via ErrorInfo within i_recordingEnable() already. */ 6207 hrc = VBOX_E_IPRT_ERROR; 6191 ComPtr<IVirtualBoxErrorInfo> pErrorInfo; 6192 6193 int vrc = i_recordingEnable(aEnable, &alock, aProgress); 6194 if (RT_FAILURE(vrc)) 6195 { 6196 /* If available, get the error information from the progress object and fire it via the event below. */ 6197 if (aProgress.isNotNull()) 6198 { 6199 hrc = aProgress->COMGETTER(ErrorInfo(pErrorInfo.asOutParam())); 6200 AssertComRCReturn(hrc, hrc); 6201 } 6202 } 6203 6204 alock.release(); /* Release lock before firing event. */ 6205 6206 if (vrc != VINF_NO_CHANGE) 6207 ::FireRecordingStateChangedEvent(mEventSource, aEnable, pErrorInfo); 6208 6209 if (RT_FAILURE(vrc)) 6210 hrc = VBOX_E_RECORDING_ERROR; 6211 6208 6212 ptrVM.release(); 6209 6213 } 6214 6215 return hrc; 6210 6216 #else 6211 RT_NOREF(fEnabled); 6217 RT_NOREF(aEnabled, aProgress); 6218 ReturnComNotImplemented(); 6212 6219 #endif /* VBOX_WITH_RECORDING */ 6213 return hrc; 6220 } 6221 6222 /** 6223 * Called by IInternalSessionControl::OnRecordingScreenStateChange(). 6224 */ 6225 HRESULT Console::i_onRecordingScreenStateChange(BOOL aEnable, ULONG aScreen) 6226 { 6227 RT_NOREF(aEnable, aScreen); 6228 ReturnComNotImplemented(); 6214 6229 } 6215 6230 … … 7604 7619 * @returns VBox status code. 7605 7620 */ 7606 int Console::i_recordingCreate( void)7621 int Console::i_recordingCreate(ComPtr<IProgress> &pProgress) 7607 7622 { 7608 7623 settings::RecordingSettings recordingSettings; 7609 7624 int vrc = i_recordingGetSettings(recordingSettings); 7610 7625 if (RT_SUCCESS(vrc)) 7611 { 7612 vrc = mRecording.mCtx.Create(this, recordingSettings); 7613 if (RT_SUCCESS(vrc)) 7614 { 7615 RecordingContext::CALLBACKS Callbacks; 7616 RT_ZERO(Callbacks); 7617 Callbacks.pfnStateChanged = Console::s_recordingOnStateChangedCallback; 7618 7619 mRecording.mCtx.SetCallbacks(&Callbacks, this /* pvUser */); 7620 } 7621 } 7626 vrc = mRecording.mCtx.Create(this, recordingSettings, pProgress); 7627 7628 if (RT_FAILURE(vrc)) 7629 setErrorBoth(VBOX_E_RECORDING_ERROR, vrc, tr("Recording initialization failed (%Rrc) -- please consult log file for details"), vrc); 7622 7630 7623 7631 LogFlowFuncLeaveRC(vrc); … … 7633 7641 } 7634 7642 7635 /** @copydoc RecordingContext::CALLBACKS::pfnStateChanged */7636 DECLCALLBACK(void) Console::s_recordingOnStateChangedCallback(RecordingContext *pCtx,7637 RECORDINGSTS enmSts, uint32_t uScreen, int vrc, void *pvUser)7638 {7639 RT_NOREF(pCtx, vrc);7640 7641 Console *pThis = (Console *)pvUser;7642 AssertPtrReturnVoid(pThis);7643 7644 switch (enmSts)7645 {7646 case RECORDINGSTS_LIMIT_REACHED:7647 {7648 if (uScreen == UINT32_MAX) /* Limit for all screens reached? Disable recording. */7649 pThis->i_onRecordingChange(FALSE /* Disable */);7650 break;7651 }7652 7653 default:7654 break;7655 }7656 }7657 7658 7643 /** 7659 7644 * Starts recording. Does nothing if recording is already started. … … 7667 7652 if (mRecording.mCtx.IsStarted()) 7668 7653 return VINF_SUCCESS; 7669 7670 LogRel(("Recording: Starting ...\n"));7671 7654 7672 7655 int vrc = mRecording.mCtx.Start(); … … 7674 7657 vrc = mDisplay->i_recordingStart(); 7675 7658 7659 if (RT_FAILURE(vrc)) 7660 mRecording.mCtx.SetError(vrc, Utf8StrFmt(tr("Recording start failed (%Rrc)"), vrc).c_str()); 7661 7676 7662 LogFlowFuncLeaveRC(vrc); 7677 7663 return vrc; … … 7688 7674 if (!mRecording.mCtx.IsStarted()) 7689 7675 return VINF_SUCCESS; 7690 7691 LogRel(("Recording: Stopping ...\n"));7692 7676 7693 7677 int vrc = mRecording.mCtx.Stop(); 7694 7678 if (RT_SUCCESS(vrc)) 7695 7679 vrc = mDisplay->i_recordingStop(); 7680 7681 if (RT_FAILURE(vrc)) 7682 setErrorBoth(VBOX_E_RECORDING_ERROR, vrc, tr("Recording stop failed (%Rrc)"), vrc); 7696 7683 7697 7684 LogFlowFuncLeaveRC(vrc); … … 11394 11381 pConsole->i_consoleVRDPServer()->EnableConnections(); 11395 11382 11383 /* Release the lock before a lengthy operation. */ 11384 alock.release(); 11385 11396 11386 #ifdef VBOX_WITH_RECORDING 11397 11387 /* 11398 * Enablerecording if configured.11388 * Start recording if configured. 11399 11389 */ 11390 ComPtr<IRecordingSettings> pRecordingSettings; 11391 hrc = pConsole->mMachine->COMGETTER(RecordingSettings)(pRecordingSettings.asOutParam()); 11392 AssertComRCBreak(hrc, RT_NOTHING); 11393 11400 11394 BOOL fRecordingEnabled = FALSE; 11401 { 11402 ComPtr<IRecordingSettings> ptrRecordingSettings; 11403 hrc = pConsole->mMachine->COMGETTER(RecordingSettings)(ptrRecordingSettings.asOutParam()); 11404 AssertComRCBreak(hrc, RT_NOTHING); 11405 11406 hrc = ptrRecordingSettings->COMGETTER(Enabled)(&fRecordingEnabled); 11407 AssertComRCBreak(hrc, RT_NOTHING); 11408 } 11395 hrc = pRecordingSettings->COMGETTER(Enabled)(&fRecordingEnabled); 11396 AssertComRCBreak(hrc, RT_NOTHING); 11397 11409 11398 if (fRecordingEnabled) 11410 11399 { 11411 vrc = pConsole->i_recordingEnable(fRecordingEnabled, &alock); 11412 if (RT_SUCCESS(vrc)) 11413 ::FireRecordingChangedEvent(pConsole->mEventSource); 11414 else 11400 ComPtr<IProgress> pProgress; 11401 hrc = pRecordingSettings->Start(pProgress.asOutParam()); 11402 if (FAILED(hrc)) 11415 11403 { 11416 LogRel(("Recording: Failed with %Rrc on VM power up\n", vrc)); 11417 vrc = VINF_SUCCESS; /* do not fail with broken recording */ 11404 com::ErrorInfoKeeper eik; 11405 ComPtr<IVirtualBoxErrorInfo> pErrorInfo = eik.takeError(); 11406 Bstr strMsg; 11407 hrc = pErrorInfo->COMGETTER(Text)(strMsg.asOutParam()); 11408 AssertComRCBreak(hrc, RT_NOTHING); 11409 LONG lRc; 11410 hrc = pErrorInfo->COMGETTER(ResultCode)(&lRc); 11411 AssertComRCBreak(hrc, RT_NOTHING); 11412 11413 LogRel(("Recording: Failed to start on VM power up: %ls (%Rrc)\n", 11414 strMsg.raw(), lRc)); 11418 11415 } 11419 11416 } 11420 11417 #endif 11421 11422 /* release the lock before a lengthy operation */11423 alock.release();11424 11425 11418 /* 11426 11419 * Capture USB devices. -
trunk/src/VBox/Main/src-client/Recording.cpp
r105097 r105266 50 50 #include <iprt/time.h> 51 51 52 #include <iprt/cpp/utils.h> 53 52 54 #include <VBox/err.h> 53 55 #include <VBox/com/VirtualBox.h> … … 59 61 #include "RecordingUtils.h" 60 62 #include "WebMWriter.h" 63 #include "VirtualBoxErrorInfoImpl.h" 61 64 62 65 using namespace com; … … 188 191 } 189 192 190 /**191 * Recording context constructor.192 *193 * @param ptrConsole Pointer to console object this context is bound to (weak pointer).194 * @param Settings Reference to recording settings to use for creation.195 *196 * @note Will throw vrc when unable to create.197 */198 RecordingContext::RecordingContext(Console *ptrConsole, const settings::RecordingSettings &Settings)199 : m_pConsole(NULL)200 , m_enmState(RECORDINGSTS_UNINITIALIZED)201 , m_cStreamsEnabled(0)202 {203 int vrc = RTCritSectInit(&m_CritSect);204 if (RT_FAILURE(vrc))205 throw vrc;206 207 vrc = RecordingContext::createInternal(ptrConsole, Settings);208 if (RT_FAILURE(vrc))209 throw vrc;210 }211 212 193 RecordingContext::~RecordingContext(void) 213 194 { … … 219 200 220 201 /** 202 * Returns whether the recording progress object has been canceled or not. 203 * 204 * @returns \c true if canceled, or \c false if not. 205 */ 206 bool RecordingContext::progressIsCanceled(void) const 207 { 208 if (m_pProgress.isNull()) 209 return true; 210 211 BOOL fCanceled; 212 HRESULT const hrc = m_pProgress->COMGETTER(Canceled(&fCanceled)); 213 AssertComRC(hrc); 214 return RT_BOOL(fCanceled); 215 } 216 217 /** 218 * Returns whether the recording progress object has been completed or not. 219 * 220 * @returns \c true if completed, or \c false if not. 221 */ 222 bool RecordingContext::progressIsCompleted(void) const 223 { 224 if (m_pProgress.isNull()) 225 return true; 226 227 BOOL fCompleted; 228 HRESULT const hrc = m_pProgress->COMGETTER(Completed(&fCompleted)); 229 AssertComRC(hrc); 230 return RT_BOOL(fCompleted); 231 } 232 233 /** 234 * Creates a progress object based on the given recording settings. 235 * 236 * @returns VBox status code. 237 * @param Settings Recording settings to use for creation. 238 * @param pProgress Where to return the created progress object on success. 239 */ 240 int RecordingContext::progressCreate(const settings::RecordingSettings &Settings, ComObjPtr<Progress> &pProgress) 241 { 242 /* Determine the number of operations the recording progress has. 243 * We use the maximum time (in s) of each screen as the overall progress indicator. 244 * If one screen is configured to be recorded indefinitely (until manually stopped), 245 * the operation count gets reset to 1. */ 246 ULONG cOperations; 247 settings::RecordingScreenSettingsMap::const_iterator itScreen = Settings.mapScreens.begin(); 248 while (itScreen != Settings.mapScreens.end()) 249 { 250 settings::RecordingScreenSettings const &screenSettings = itScreen->second; 251 if (screenSettings.ulMaxTimeS == 0) 252 { 253 cOperations = 1; /* Screen will be recorded indefinitely, reset operation count and bail out. */ 254 break; 255 } 256 else 257 cOperations = RT_MAX(cOperations, screenSettings.ulMaxTimeS); 258 ++itScreen; 259 } 260 261 HRESULT hrc = pProgress.createObject(); 262 if (SUCCEEDED(hrc)) 263 { 264 hrc = pProgress->init(m_pConsole, Utf8Str("Recording"), 265 TRUE /* aCancelable */, cOperations, cOperations /* ulTotalOperationsWeight */, 266 Utf8Str("Starting"), 1 /* ulFirstOperationWeight */); 267 if (SUCCEEDED(hrc)) 268 pProgress->i_setCancelCallback(RecordingContext::s_progressCancelCallback, this /* pvUser */); 269 } 270 271 return SUCCEEDED(hrc) ? VINF_SUCCESS : VERR_COM_UNEXPECTED; 272 } 273 274 /** 275 * Sets the current progress based on the operation. 276 * 277 * @returns VBox status code. 278 * @param uOp Operation index to set (zero-based). 279 * @param strDesc Description of the operation. 280 */ 281 int RecordingContext::progressSet(uint32_t uOp, const Bstr &strDesc) 282 { 283 if (m_pProgress.isNull()) 284 return VINF_SUCCESS; 285 286 if ( uOp == m_ulCurOp /* No change? */ 287 || uOp + 1 > m_cOps /* Done? */ 288 || m_cOps == 1) /* Indefinitely recording until canceled? Skip. */ 289 return VINF_SUCCESS; 290 291 Assert(uOp > m_ulCurOp); 292 293 ComPtr<IInternalProgressControl> pProgressControl(m_pProgress); 294 AssertReturn(!!pProgressControl, VERR_COM_UNEXPECTED); 295 296 /* hrc ignored */ pProgressControl->SetNextOperation(strDesc.raw(), 1 /* Weight */); 297 /* Might be E_FAIL if already canceled. */ 298 299 m_ulCurOp = uOp; 300 301 return VINF_SUCCESS; 302 } 303 304 /** 305 * Sets the current progress based on a timestamp (PTS). 306 * 307 * @returns VBox status code. 308 * @param msTimestamp Timestamp to use (absolute, PTS). 309 */ 310 int RecordingContext::progressSet(uint64_t msTimestamp) 311 { 312 /* Run until stopped / canceled? */ 313 if (m_cOps == 1) 314 return VINF_SUCCESS; 315 316 ULONG const nextOp = (ULONG)msTimestamp / RT_MS_1SEC; /* Each operation equals 1s (same weight). */ 317 if (nextOp <= m_ulCurOp) /* If next operation still is the current operation, bail out early. */ 318 return VINF_SUCCESS; 319 320 /* Format the recording time as a human-readable time (HH:MM:SS) and set it as current progress operation text. */ 321 char szDesc[32]; 322 szDesc[0] = '\0'; 323 char *psz = szDesc; 324 RTTIMESPEC TimeSpec; 325 RTTIME Time; 326 RTTimeExplode(&Time, RTTimeSpecSetMilli(&TimeSpec, msTimestamp)); 327 psz += RTStrFormatNumber(psz, Time.u8Hour, 10, 2, 0, RTSTR_F_ZEROPAD); 328 *psz++ = ':'; 329 psz += RTStrFormatNumber(psz, Time.u8Minute, 10, 2, 0, RTSTR_F_ZEROPAD); 330 *psz++ = ':'; 331 psz += RTStrFormatNumber(psz, Time.u8Second, 10, 2, 0, RTSTR_F_ZEROPAD); 332 333 /* All operations have the same weight. */ 334 uint8_t const uPercent = (100 * nextOp + m_cOps / 2) / m_cOps; 335 336 LogRel2(("Recording: Progress %s (%RU32 / %RU32) -- %RU8%%\n", szDesc, nextOp, m_cOps, uPercent)); 337 338 psz += RTStrPrintf2(psz, psz - szDesc, " (%RU8%%)", uPercent); 339 340 return progressSet(nextOp, Bstr(szDesc)); 341 } 342 343 /** 344 * Notifies the progress object about completion. 345 * 346 * @returns VBox status code. 347 * @param hrc Completion result to set. 348 * @param pErrorInfo Error info to set in case \a hrc indicates an error. Optional and can be NULL. 349 */ 350 int RecordingContext::progressNotifyComplete(HRESULT hrc /* = S_OK */, IVirtualBoxErrorInfo *pErrorInfo /* = NULL */) 351 { 352 if (m_pProgress.isNull()) 353 return VINF_SUCCESS; 354 355 BOOL fCompleted; 356 HRESULT hrc2 = m_pProgress->COMGETTER(Completed)(&fCompleted); 357 AssertComRC(hrc2); 358 359 if (!fCompleted) 360 { 361 ComPtr<IInternalProgressControl> pProgressControl(m_pProgress); 362 AssertReturn(!!pProgressControl, VERR_COM_UNEXPECTED); 363 364 pProgressControl->NotifyComplete(hrc, pErrorInfo); 365 } 366 367 return VINF_SUCCESS; 368 } 369 370 /** 371 * Reports an error condition to the recording context. 372 * 373 * @returns VBox status code. 374 * @param rc Error code to set. 375 * @param strText Error description to set. 376 */ 377 int RecordingContext::SetError(int rc, const com::Utf8Str &strText) 378 { 379 lock(); 380 381 if ( m_pProgress.isNull() 382 || !m_pConsole) 383 { 384 unlock(); 385 return VINF_SUCCESS; 386 } 387 388 ComObjPtr<VirtualBoxErrorInfo> pErrorInfo; 389 HRESULT hrc = pErrorInfo.createObject(); 390 AssertComRC(hrc); 391 hrc = pErrorInfo->initEx(VBOX_E_RECORDING_ERROR, (LONG)rc, 392 m_pConsole->getStaticClassIID(), m_pConsole->getStaticComponentName(), strText); 393 AssertComRC(hrc); 394 395 unlock(); 396 397 LogRel(("Recording: An error occurred: %s (%Rrc)\n", strText.c_str(), rc)); 398 399 hrc = m_pProgress->NotifyComplete(VBOX_E_RECORDING_ERROR, pErrorInfo); 400 AssertComRC(hrc); 401 402 return VINF_SUCCESS; 403 } 404 405 /** 221 406 * Worker thread for all streams of a recording context. 222 *223 * For video frames, this also does the RGB/YUV conversion and encoding.224 407 */ 225 408 DECLCALLBACK(int) RecordingContext::threadMain(RTTHREAD hThreadSelf, void *pvUser) … … 236 419 int vrcWait = RTSemEventWait(pThis->m_WaitEvent, RT_MS_1SEC); 237 420 421 if (ASMAtomicReadBool(&pThis->m_fShutdown)) 422 { 423 LogRel2(("Recording: Thread is shutting down ...\n")); 424 break; 425 } 426 238 427 Log2Func(("Processing %zu streams (wait = %Rrc)\n", pThis->m_vecStreams.size(), vrcWait)); 239 428 429 uint64_t const msTimestamp = pThis->GetCurrentPTS(); 430 431 /* Set the overall progress. */ 432 int vrc = pThis->progressSet(msTimestamp); 433 AssertRC(vrc); 434 240 435 /* Process common raw blocks (data which not has been encoded yet). */ 241 intvrc = pThis->processCommonData(pThis->m_mapBlocksRaw, 100 /* ms timeout */);436 vrc = pThis->processCommonData(pThis->m_mapBlocksRaw, 100 /* ms timeout */); 242 437 243 438 /** @todo r=andy This is inefficient -- as we already wake up this thread … … 250 445 251 446 /* Hand-in common encoded blocks. */ 252 vrc = pStream->ThreadMain(vrcWait, pThis->m_mapBlocksEncoded);447 vrc = pStream->ThreadMain(vrcWait, msTimestamp, pThis->m_mapBlocksEncoded); 253 448 if (RT_FAILURE(vrc)) 254 449 { … … 264 459 265 460 /* Keep going in case of errors. */ 266 267 if (ASMAtomicReadBool(&pThis->m_fShutdown))268 {269 LogFunc(("Thread is shutting down ...\n"));270 break;271 }272 461 273 462 } /* for */ … … 468 657 */ 469 658 /* static */ 470 DECLCALLBACK(int) RecordingContext:: audioCodecWriteDataCallback(PRECORDINGCODEC pCodec, const void *pvData, size_t cbData,659 DECLCALLBACK(int) RecordingContext::s_audioCodecWriteDataCallback(PRECORDINGCODEC pCodec, const void *pvData, size_t cbData, 471 660 uint64_t msAbsPTS, uint32_t uFlags, void *pvUser) 472 661 { … … 493 682 RECORDINGCODECCALLBACKS Callbacks; 494 683 Callbacks.pvUser = this; 495 Callbacks.pfnWriteData = RecordingContext:: audioCodecWriteDataCallback;684 Callbacks.pfnWriteData = RecordingContext::s_audioCodecWriteDataCallback; 496 685 497 686 int vrc = recordingCodecCreateAudio(&m_CodecAudio, enmCodec); … … 504 693 505 694 /** 695 * Progress canceled callback. 696 * 697 * @param pvUser User-supplied pointer. Points to the RecordingContext instance. 698 */ 699 /* static */ 700 DECLCALLBACK(void) RecordingContext::s_progressCancelCallback(void *pvUser) 701 { 702 RecordingContext *pThis = (RecordingContext *)pvUser; 703 704 LogRel(("Recording: Canceled\n")); 705 706 if (pThis->m_pConsole) 707 { 708 ComPtr<IProgress> pProgressIgnored; 709 pThis->m_pConsole->i_onRecordingStateChange(FALSE /* Disable */, pProgressIgnored); 710 } 711 } 712 713 /** @copydoc RecordingContext::CALLBACKS::pfnStateChanged */ 714 DECLCALLBACK(void) RecordingContext::s_recordingStateChangedCallback(RecordingContext *pCtx, 715 RECORDINGSTS enmSts, uint32_t uScreen, int vrc, void *pvUser) 716 { 717 RT_NOREF(vrc, pvUser); 718 719 Log2Func(("enmSts=%0x, uScreen=%RU32, vrc=%Rrc\n", enmSts, uScreen, vrc)); 720 721 switch (enmSts) 722 { 723 case RECORDINGSTS_LIMIT_REACHED: 724 { 725 if (uScreen == UINT32_MAX) /* Limit for all screens reached? Disable recording. */ 726 { 727 ComPtr<IProgress> pProgressIgnored; 728 pCtx->m_pConsole->i_onRecordingStateChange(FALSE /* Disable */, pProgressIgnored); 729 730 pCtx->lock(); 731 732 /* Make sure to complete the progress object (if not already done so). */ 733 pCtx->progressNotifyComplete(S_OK); 734 735 pCtx->unlock(); 736 } 737 else if (pCtx->m_pConsole) 738 pCtx->m_pConsole->i_onRecordingScreenStateChange(FALSE /* Disable */, uScreen); 739 break; 740 } 741 742 default: 743 break; 744 } 745 } 746 747 /** 506 748 * Creates a recording context. 507 749 * … … 509 751 * @param ptrConsole Pointer to console object this context is bound to (weak pointer). 510 752 * @param Settings Reference to recording settings to use for creation. 511 * /512 int RecordingContext::createInternal(Console *ptrConsole, const settings::RecordingSettings &Settings) 513 { 514 int vrc = VINF_SUCCESS;515 753 * @param pProgress Progress object returned on success. 754 */ 755 int RecordingContext::createInternal(Console *ptrConsole, const settings::RecordingSettings &Settings, 756 ComPtr<IProgress> &pProgress) 757 { 516 758 /* Copy the settings to our context. */ 517 759 m_Settings = Settings; … … 524 766 settings::RecordingScreenSettings const &screen0Settings = itScreen0->second; 525 767 526 vrc = this->audioInit(screen0Settings);768 int vrc = this->audioInit(screen0Settings); 527 769 if (RT_FAILURE(vrc)) 528 770 return vrc; … … 560 802 } 561 803 804 ComObjPtr<Progress> pThisProgress; 805 vrc = progressCreate(m_Settings, pThisProgress); 562 806 if (RT_SUCCESS(vrc)) 563 807 { 564 m_tsStartMs = 0;565 m_enmState = RECORDINGSTS_CREATED;566 m_fShutdown = false;567 568 808 vrc = RTSemEventCreate(&m_WaitEvent); 569 809 AssertRCReturn(vrc, vrc); 570 810 571 RT_ZERO(m_Callbacks); 811 RecordingContext::CALLBACKS Callbacks; 812 RT_ZERO(Callbacks); 813 Callbacks.pfnStateChanged = RecordingContext::s_recordingStateChangedCallback; 814 815 SetCallbacks(&Callbacks, this /* pvUser */); 816 817 reset(); 818 819 unconst(m_pProgress) = pThisProgress; 820 pThisProgress.queryInterfaceTo(pProgress.asOutParam()); 572 821 } 573 822 … … 595 844 596 845 /** 846 * Resets a recording context. 847 */ 848 void RecordingContext::reset(void) 849 { 850 m_tsStartMs = 0; 851 m_enmState = RECORDINGSTS_CREATED; 852 m_fShutdown = false; 853 m_cStreamsEnabled = 0; 854 855 unconst(m_pProgress).setNull(); 856 } 857 858 /** 597 859 * Starts a recording context by creating its worker thread. 598 860 * … … 606 868 Assert(m_enmState == RECORDINGSTS_CREATED); 607 869 870 LogRel2(("Recording: Starting ...\n")); 871 608 872 m_tsStartMs = RTTimeMilliTS(); 873 874 m_ulCurOp = 0; 875 if (m_pProgress.isNotNull()) 876 { 877 HRESULT hrc = m_pProgress->COMGETTER(OperationCount)(&m_cOps); 878 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED); 879 } 609 880 610 881 int vrc = RTThreadCreate(&m_Thread, RecordingContext::threadMain, (void *)this, 0, … … 620 891 } 621 892 else 622 Log (("Recording: Failed to start (%Rrc)\n", vrc));893 LogRel(("Recording: Failed to start (%Rrc)\n", vrc)); 623 894 624 895 return vrc; … … 635 906 return VINF_SUCCESS; 636 907 637 Log ThisFunc(("Shutting down thread...\n"));908 LogRel2(("Recording: Stopping ...\n")); 638 909 639 910 /* Set shutdown indicator. */ … … 649 920 if (RT_SUCCESS(vrc)) 650 921 { 922 if (m_pProgress.isNotNull()) 923 progressNotifyComplete(); 924 651 925 LogRel(("Recording: Stopped\n")); 652 m_tsStartMs = 0; 653 m_enmState = RECORDINGSTS_CREATED;926 927 reset(); 654 928 } 655 929 else 656 Log (("Recording: Failed to stop (%Rrc)\n", vrc));930 LogRel(("Recording: Failed to stop (%Rrc)\n", vrc)); 657 931 658 932 unlock(); … … 705 979 m_enmState = RECORDINGSTS_UNINITIALIZED; 706 980 981 unconst(m_pProgress).setNull(); 982 707 983 unlock(); 708 984 } … … 791 1067 * @param ptrConsole Pointer to console object this context is bound to (weak pointer). 792 1068 * @param Settings Reference to recording settings to use for creation. 793 */ 794 int RecordingContext::Create(Console *ptrConsole, const settings::RecordingSettings &Settings) 795 { 796 return createInternal(ptrConsole, Settings); 1069 * @param pProgress Progress object returned on success. 1070 */ 1071 int RecordingContext::Create(Console *ptrConsole, const settings::RecordingSettings &Settings, ComPtr<IProgress> &pProgress) 1072 { 1073 return createInternal(ptrConsole, Settings, pProgress); 797 1074 } 798 1075 … … 1012 1289 int RecordingContext::onLimitReached(uint32_t uScreen, int vrc) 1013 1290 { 1014 LogFlowThisFunc(("Stream %RU32 has reached its limit (%Rrc)\n", uScreen, vrc)); 1015 1016 lock(); 1017 1018 Assert(m_cStreamsEnabled); 1291 lock(); 1292 1293 LogRel2(("Recording: Active streams: %RU16\n", m_cStreamsEnabled)); 1294 1019 1295 if (m_cStreamsEnabled) 1020 1296 m_cStreamsEnabled--; … … 1022 1298 bool const fAllDisabled = m_cStreamsEnabled == 0; 1023 1299 1024 LogFlowThisFunc(("cStreamsEnabled=%RU16\n", m_cStreamsEnabled)); 1300 if (fAllDisabled) 1301 LogRel(("Recording: All set limits have been reached\n")); 1302 else 1303 LogRel(("Recording: Set limit for screen #%RU32 has been reached\n", uScreen)); 1025 1304 1026 1305 unlock(); /* Leave the lock before invoking callbacks. */ … … 1176 1455 vrc = vrc2; 1177 1456 1457 /* Bail out as soon as possible when the shutdown flag is set. */ 1458 if (ASMAtomicReadBool(&m_fShutdown)) 1459 break; 1460 1178 1461 ++it; 1179 1462 } -
trunk/src/VBox/Main/src-client/RecordingStream.cpp
r105096 r105266 402 402 * Can be used for figuring out if the encoder has to perform some 403 403 * worked based on that result. 404 * @param msTimestamp Timestamp to use for PTS calculation (absolute). 404 405 * @param commonBlocks Common blocks multiplexed to all recording streams. 405 406 * 406 407 * @note Runs in encoding thread. 407 408 */ 408 int RecordingStream::ThreadMain(int rcWait, RecordingBlockMap &commonBlocks) 409 { 410 Log3Func(("uScreenID=%RU16, rcWait=%Rrc\n", m_uScreenID, rcWait)); 411 412 uint64_t const msTimestamp = m_pCtx->GetCurrentPTS(); 409 int RecordingStream::ThreadMain(int rcWait, uint64_t msTimestamp, RecordingBlockMap &commonBlocks) 410 { 411 Log3Func(("uScreenID=%RU16, msTimestamp=%RU64, rcWait=%Rrc\n", m_uScreenID, msTimestamp, rcWait)); 413 412 414 413 /* No new data arrived within time? Feed the encoder with the last frame we built. -
trunk/src/VBox/Main/src-client/SessionImpl.cpp
r98262 r105266 773 773 } 774 774 775 HRESULT Session::onRecordingChange(BOOL aEnable) 776 { 777 LogFlowThisFunc(("\n")); 778 779 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 780 AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE); 781 AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE); 782 #ifndef VBOX_COM_INPROC_API_CLIENT 783 AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE); 784 785 return mConsole->i_onRecordingChange(aEnable); 775 HRESULT Session::onRecordingStateChange(BOOL aEnable, ComPtr<IProgress> &aProgress) 776 { 777 LogFlowThisFunc(("\n")); 778 779 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 780 AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE); 781 AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE); 782 #ifndef VBOX_COM_INPROC_API_CLIENT 783 AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE); 784 785 return mConsole->i_onRecordingStateChange(aEnable, aProgress); 786 #else 787 RT_NOREF(aEnable); 788 return S_OK; 789 #endif 790 } 791 792 HRESULT Session::onRecordingScreenStateChange(BOOL aEnable, ULONG aScreen) 793 { 794 LogFlowThisFunc(("\n")); 795 796 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 797 AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE); 798 AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE); 799 #ifndef VBOX_COM_INPROC_API_CLIENT 800 AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE); 801 802 return mConsole->i_onRecordingScreenStateChange(aEnable, aScreen); 786 803 #else 787 804 RT_NOREF(aEnable);
Note:
See TracChangeset
for help on using the changeset viewer.