VirtualBox

Changeset 105266 in vbox for trunk/src/VBox/Main/src-client


Ignore:
Timestamp:
Jul 11, 2024 7:49:37 AM (5 months ago)
Author:
vboxsync
Message:

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

Location:
trunk/src/VBox/Main/src-client
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/src-client/ConsoleImpl.cpp

    r105095 r105266  
    60946094 *
    60956095 * @returns VBox status code.
    6096  * @retval  VERR_NO_CHANGE if the recording state has not been changed.
     6096 * @retval  VINF_NO_CHANGE if the recording state has not been changed.
    60976097 * @param   fEnable             Whether to enable or disable the recording.
    60986098 * @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 */
     6101int Console::i_recordingEnable(BOOL fEnable, util::AutoWriteLock *pAutoLock, ComPtr<IProgress> &pProgress)
    61016102{
    61026103    AssertPtrReturn(pAutoLock, VERR_INVALID_POINTER);
    61036104
     6105    bool const fIsEnabled = mRecording.mCtx.IsStarted();
     6106
     6107    if (RT_BOOL(fEnable) == fIsEnabled) /* No change? Bail out. */
     6108        return VINF_NO_CHANGE;
     6109
    61046110    int vrc = VINF_SUCCESS;
    61056111
    61066112    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))
    61176124            {
    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)
    61196129                {
    6120                     vrc = i_recordingCreate();
     6130                    vrc = mRecording.mAudioRec->applyConfiguration(mRecording.mCtx.GetConfig());
    61216131                    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                }
    61356137# 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? */
    61516140                {
    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);
    61636142                }
    61646143            }
    6165             else
    6166                 vrc = VERR_VM_INVALID_VM_STATE;
    61676144
    61686145            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));
    61776170
    61786171    return vrc;
     
    61816174
    61826175/**
    6183  * Called by IInternalSessionControl::OnRecordingChange().
    6184  */
    6185 HRESULT Console::i_onRecordingChange(BOOL fEnabled)
    6186 {
    6187     AutoCaller autoCaller(this);
    6188     AssertComRCReturnRC(autoCaller.hrc());
     6176 * Called by IInternalSessionControl::OnRecordingStateChange().
     6177 */
     6178HRESULT Console::i_onRecordingStateChange(BOOL aEnable, ComPtr<IProgress> &aProgress)
     6179{
     6180#ifdef VBOX_WITH_RECORDING
     6181    HRESULT hrc = S_OK;
    61896182
    61906183    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    61916184
    6192     HRESULT hrc = S_OK;
    6193 #ifdef VBOX_WITH_RECORDING
     6185    LogRel2(("Recording: State changed (%s)\n", aEnable ? "enabled" : "disabled"));
     6186
    61946187    /* Don't trigger recording changes if the VM isn't running. */
    61956188    SafeVMPtrQuiet ptrVM(this);
    61966189    if (ptrVM.isOk())
    61976190    {
    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
    62086212        ptrVM.release();
    62096213    }
     6214
     6215    return hrc;
    62106216#else
    6211     RT_NOREF(fEnabled);
     6217    RT_NOREF(aEnabled, aProgress);
     6218    ReturnComNotImplemented();
    62126219#endif /* VBOX_WITH_RECORDING */
    6213     return hrc;
     6220}
     6221
     6222/**
     6223 * Called by IInternalSessionControl::OnRecordingScreenStateChange().
     6224 */
     6225HRESULT Console::i_onRecordingScreenStateChange(BOOL aEnable, ULONG aScreen)
     6226{
     6227    RT_NOREF(aEnable, aScreen);
     6228    ReturnComNotImplemented();
    62146229}
    62156230
     
    76047619 * @returns VBox status code.
    76057620 */
    7606 int Console::i_recordingCreate(void)
     7621int Console::i_recordingCreate(ComPtr<IProgress> &pProgress)
    76077622{
    76087623    settings::RecordingSettings recordingSettings;
    76097624    int vrc = i_recordingGetSettings(recordingSettings);
    76107625    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);
    76227630
    76237631    LogFlowFuncLeaveRC(vrc);
     
    76337641}
    76347642
    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 
    76587643/**
    76597644 * Starts recording. Does nothing if recording is already started.
     
    76677652    if (mRecording.mCtx.IsStarted())
    76687653        return VINF_SUCCESS;
    7669 
    7670     LogRel(("Recording: Starting ...\n"));
    76717654
    76727655    int vrc = mRecording.mCtx.Start();
     
    76747657        vrc = mDisplay->i_recordingStart();
    76757658
     7659    if (RT_FAILURE(vrc))
     7660        mRecording.mCtx.SetError(vrc, Utf8StrFmt(tr("Recording start failed (%Rrc)"), vrc).c_str());
     7661
    76767662    LogFlowFuncLeaveRC(vrc);
    76777663    return vrc;
     
    76887674    if (!mRecording.mCtx.IsStarted())
    76897675        return VINF_SUCCESS;
    7690 
    7691     LogRel(("Recording: Stopping ...\n"));
    76927676
    76937677    int vrc = mRecording.mCtx.Stop();
    76947678    if (RT_SUCCESS(vrc))
    76957679        vrc = mDisplay->i_recordingStop();
     7680
     7681    if (RT_FAILURE(vrc))
     7682        setErrorBoth(VBOX_E_RECORDING_ERROR, vrc, tr("Recording stop failed (%Rrc)"), vrc);
    76967683
    76977684    LogFlowFuncLeaveRC(vrc);
     
    1139411381                pConsole->i_consoleVRDPServer()->EnableConnections();
    1139511382
     11383                /* Release the lock before a lengthy operation. */
     11384                alock.release();
     11385
    1139611386#ifdef VBOX_WITH_RECORDING
    1139711387                /*
    11398                  * Enable recording if configured.
     11388                 * Start recording if configured.
    1139911389                 */
     11390                ComPtr<IRecordingSettings> pRecordingSettings;
     11391                hrc = pConsole->mMachine->COMGETTER(RecordingSettings)(pRecordingSettings.asOutParam());
     11392                AssertComRCBreak(hrc, RT_NOTHING);
     11393
    1140011394                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
    1140911398                if (fRecordingEnabled)
    1141011399                {
    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))
    1141511403                    {
    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));
    1141811415                    }
    1141911416                }
    1142011417#endif
    11421 
    11422                 /* release the lock before a lengthy operation */
    11423                 alock.release();
    11424 
    1142511418                /*
    1142611419                 * Capture USB devices.
  • trunk/src/VBox/Main/src-client/Recording.cpp

    r105097 r105266  
    5050#include <iprt/time.h>
    5151
     52#include <iprt/cpp/utils.h>
     53
    5254#include <VBox/err.h>
    5355#include <VBox/com/VirtualBox.h>
     
    5961#include "RecordingUtils.h"
    6062#include "WebMWriter.h"
     63#include "VirtualBoxErrorInfoImpl.h"
    6164
    6265using namespace com;
     
    188191}
    189192
    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 
    212193RecordingContext::~RecordingContext(void)
    213194{
     
    219200
    220201/**
     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 */
     206bool 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 */
     222bool 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 */
     240int 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 */
     281int 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 */
     310int 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 */
     350int 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 */
     377int 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/**
    221406 * Worker thread for all streams of a recording context.
    222  *
    223  * For video frames, this also does the RGB/YUV conversion and encoding.
    224407 */
    225408DECLCALLBACK(int) RecordingContext::threadMain(RTTHREAD hThreadSelf, void *pvUser)
     
    236419        int vrcWait = RTSemEventWait(pThis->m_WaitEvent, RT_MS_1SEC);
    237420
     421        if (ASMAtomicReadBool(&pThis->m_fShutdown))
     422        {
     423            LogRel2(("Recording: Thread is shutting down ...\n"));
     424            break;
     425        }
     426
    238427        Log2Func(("Processing %zu streams (wait = %Rrc)\n", pThis->m_vecStreams.size(), vrcWait));
    239428
     429        uint64_t const msTimestamp = pThis->GetCurrentPTS();
     430
     431        /* Set the overall progress. */
     432        int vrc = pThis->progressSet(msTimestamp);
     433        AssertRC(vrc);
     434
    240435        /* Process common raw blocks (data which not has been encoded yet). */
    241         int vrc = pThis->processCommonData(pThis->m_mapBlocksRaw, 100 /* ms timeout */);
     436        vrc = pThis->processCommonData(pThis->m_mapBlocksRaw, 100 /* ms timeout */);
    242437
    243438        /** @todo r=andy This is inefficient -- as we already wake up this thread
     
    250445
    251446            /* Hand-in common encoded blocks. */
    252             vrc = pStream->ThreadMain(vrcWait, pThis->m_mapBlocksEncoded);
     447            vrc = pStream->ThreadMain(vrcWait, msTimestamp, pThis->m_mapBlocksEncoded);
    253448            if (RT_FAILURE(vrc))
    254449            {
     
    264459
    265460        /* Keep going in case of errors. */
    266 
    267         if (ASMAtomicReadBool(&pThis->m_fShutdown))
    268         {
    269             LogFunc(("Thread is shutting down ...\n"));
    270             break;
    271         }
    272461
    273462    } /* for */
     
    468657 */
    469658/* static */
    470 DECLCALLBACK(int) RecordingContext::audioCodecWriteDataCallback(PRECORDINGCODEC pCodec, const void *pvData, size_t cbData,
     659DECLCALLBACK(int) RecordingContext::s_audioCodecWriteDataCallback(PRECORDINGCODEC pCodec, const void *pvData, size_t cbData,
    471660                                                                uint64_t msAbsPTS, uint32_t uFlags, void *pvUser)
    472661{
     
    493682    RECORDINGCODECCALLBACKS Callbacks;
    494683    Callbacks.pvUser       = this;
    495     Callbacks.pfnWriteData = RecordingContext::audioCodecWriteDataCallback;
     684    Callbacks.pfnWriteData = RecordingContext::s_audioCodecWriteDataCallback;
    496685
    497686    int vrc = recordingCodecCreateAudio(&m_CodecAudio, enmCodec);
     
    504693
    505694/**
     695 * Progress canceled callback.
     696 *
     697 * @param   pvUser              User-supplied pointer. Points to the RecordingContext instance.
     698 */
     699/* static */
     700DECLCALLBACK(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 */
     714DECLCALLBACK(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/**
    506748 * Creates a recording context.
    507749 *
     
    509751 * @param   ptrConsole          Pointer to console object this context is bound to (weak pointer).
    510752 * @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 */
     755int RecordingContext::createInternal(Console *ptrConsole, const settings::RecordingSettings &Settings,
     756                                     ComPtr<IProgress> &pProgress)
     757{
    516758    /* Copy the settings to our context. */
    517759    m_Settings = Settings;
     
    524766    settings::RecordingScreenSettings const &screen0Settings = itScreen0->second;
    525767
    526     vrc = this->audioInit(screen0Settings);
     768    int vrc = this->audioInit(screen0Settings);
    527769    if (RT_FAILURE(vrc))
    528770        return vrc;
     
    560802    }
    561803
     804    ComObjPtr<Progress> pThisProgress;
     805    vrc = progressCreate(m_Settings, pThisProgress);
    562806    if (RT_SUCCESS(vrc))
    563807    {
    564         m_tsStartMs = 0;
    565         m_enmState  = RECORDINGSTS_CREATED;
    566         m_fShutdown = false;
    567 
    568808        vrc = RTSemEventCreate(&m_WaitEvent);
    569809        AssertRCReturn(vrc, vrc);
    570810
    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());
    572821    }
    573822
     
    595844
    596845/**
     846 * Resets a recording context.
     847 */
     848void 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/**
    597859 * Starts a recording context by creating its worker thread.
    598860 *
     
    606868    Assert(m_enmState == RECORDINGSTS_CREATED);
    607869
     870    LogRel2(("Recording: Starting ...\n"));
     871
    608872    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    }
    609880
    610881    int vrc = RTThreadCreate(&m_Thread, RecordingContext::threadMain, (void *)this, 0,
     
    620891    }
    621892    else
    622         Log(("Recording: Failed to start (%Rrc)\n", vrc));
     893        LogRel(("Recording: Failed to start (%Rrc)\n", vrc));
    623894
    624895    return vrc;
     
    635906        return VINF_SUCCESS;
    636907
    637     LogThisFunc(("Shutting down thread ...\n"));
     908    LogRel2(("Recording: Stopping ...\n"));
    638909
    639910    /* Set shutdown indicator. */
     
    649920    if (RT_SUCCESS(vrc))
    650921    {
     922        if (m_pProgress.isNotNull())
     923            progressNotifyComplete();
     924
    651925        LogRel(("Recording: Stopped\n"));
    652         m_tsStartMs = 0;
    653         m_enmState  = RECORDINGSTS_CREATED;
     926
     927        reset();
    654928    }
    655929    else
    656         Log(("Recording: Failed to stop (%Rrc)\n", vrc));
     930        LogRel(("Recording: Failed to stop (%Rrc)\n", vrc));
    657931
    658932    unlock();
     
    705979    m_enmState = RECORDINGSTS_UNINITIALIZED;
    706980
     981    unconst(m_pProgress).setNull();
     982
    707983    unlock();
    708984}
     
    7911067 * @param   ptrConsole          Pointer to console object this context is bound to (weak pointer).
    7921068 * @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 */
     1071int RecordingContext::Create(Console *ptrConsole, const settings::RecordingSettings &Settings, ComPtr<IProgress> &pProgress)
     1072{
     1073    return createInternal(ptrConsole, Settings, pProgress);
    7971074}
    7981075
     
    10121289int RecordingContext::onLimitReached(uint32_t uScreen, int vrc)
    10131290{
    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
    10191295    if (m_cStreamsEnabled)
    10201296        m_cStreamsEnabled--;
     
    10221298    bool const fAllDisabled = m_cStreamsEnabled == 0;
    10231299
    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));
    10251304
    10261305    unlock(); /* Leave the lock before invoking callbacks. */
     
    11761455            vrc = vrc2;
    11771456
     1457        /* Bail out as soon as possible when the shutdown flag is set. */
     1458        if (ASMAtomicReadBool(&m_fShutdown))
     1459            break;
     1460
    11781461        ++it;
    11791462    }
  • trunk/src/VBox/Main/src-client/RecordingStream.cpp

    r105096 r105266  
    402402 *                              Can be used for figuring out if the encoder has to perform some
    403403 *                              worked based on that result.
     404 * @param   msTimestamp         Timestamp to use for PTS calculation (absolute).
    404405 * @param   commonBlocks        Common blocks multiplexed to all recording streams.
    405406 *
    406407 * @note    Runs in encoding thread.
    407408 */
    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();
     409int RecordingStream::ThreadMain(int rcWait, uint64_t msTimestamp, RecordingBlockMap &commonBlocks)
     410{
     411    Log3Func(("uScreenID=%RU16, msTimestamp=%RU64, rcWait=%Rrc\n", m_uScreenID, msTimestamp, rcWait));
    413412
    414413    /* 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  
    773773}
    774774
    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);
     775HRESULT 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
     792HRESULT 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);
    786803#else
    787804    RT_NOREF(aEnable);
Note: See TracChangeset for help on using the changeset viewer.

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