VirtualBox

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


Ignore:
Timestamp:
Nov 5, 2018 5:55:29 PM (6 years ago)
Author:
vboxsync
Message:

Capturing: Separated capturing settings into new interfaces ICaptureSettings and ICaptureScreenSettings to unload stuff from IMachine; a lot of internal interface / code cleanups. Also see #9286. Work in progress.

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

Legend:

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

    r75167 r75251  
    402402    , m_pVMMDev(NULL)
    403403    , mAudioVRDE(NULL)
    404 #ifdef VBOX_WITH_AUDIO_VIDEOREC
    405     , mAudioVideoRec(NULL)
    406 #endif
    407404    , mNvram(NULL)
    408405#ifdef VBOX_WITH_USB_CARDREADER
     
    590587#endif
    591588#ifdef VBOX_WITH_AUDIO_VIDEOREC
    592         unconst(mAudioVideoRec) = new AudioVideoRec(this);
    593         AssertReturn(mAudioVideoRec, E_FAIL);
     589        unconst(Capture.mAudioVideoRec) = new AudioVideoRec(this);
     590        AssertReturn(Capture.mAudioVideoRec, E_FAIL);
    594591#endif
    595592        FirmwareType_T enmFirmwareType;
     
    738735
    739736#ifdef VBOX_WITH_AUDIO_VIDEOREC
    740     if (mAudioVideoRec)
    741     {
    742         delete mAudioVideoRec;
    743         unconst(mAudioVideoRec) = NULL;
     737    if (Capture.mAudioVideoRec)
     738    {
     739        delete Capture.mAudioVideoRec;
     740        unconst(Capture.mAudioVideoRec) = NULL;
    744741    }
    745742#endif
     
    56195616    if (pDisplay)
    56205617    {
    5621         if (RT_BOOL(fEnable) != pDisplay->i_videoRecStarted())
     5618        if (RT_BOOL(fEnable) != Capture.mpVideoRecCtx->IsStarted())
    56225619        {
    56235620            LogRel(("VideoRec: %s\n", fEnable ? "Enabling" : "Disabling"));
     
    56295626# ifdef VBOX_WITH_AUDIO_VIDEOREC
    56305627                /* Attach the video recording audio driver if required. */
    5631                 if (   pDisplay->i_videoRecGetFeatures() & VIDEORECFEATURE_AUDIO
    5632                     && mAudioVideoRec)
     5628                if (   Capture.mpVideoRecCtx->IsFeatureEnabled(CaptureFeature_Audio)
     5629                    && Capture.mAudioVideoRec)
    56335630                {
    5634                     vrc = mAudioVideoRec->applyConfiguration(pDisplay->i_videoRecGetConfig());
     5631                    vrc = Capture.mAudioVideoRec->applyConfiguration(Capture.mpVideoRecCtx->GetConfig());
    56355632                    if (RT_SUCCESS(vrc))
    5636                         vrc = mAudioVideoRec->doAttachDriverViaEmt(mpUVM, pAutoLock);
     5633                        vrc = Capture.mAudioVideoRec->doAttachDriverViaEmt(mpUVM, pAutoLock);
    56375634                }
    56385635# endif
    56395636                if (   RT_SUCCESS(vrc)
    5640                     && pDisplay->i_videoRecGetFeatures()) /* Any video recording (audio and/or video) feature enabled? */
     5637                    && Capture.mpVideoRecCtx->IsReady()) /* Any video recording (audio and/or video) feature enabled? */
    56415638                {
    5642                     vrc = pDisplay->i_videoRecStart();
     5639                    vrc = i_videoRecStart();
    56435640                }
    56445641            }
    56455642            else
    56465643            {
    5647                 mDisplay->i_videoRecStop();
     5644                i_videoRecStop();
    56485645# ifdef VBOX_WITH_AUDIO_VIDEOREC
    5649                 mAudioVideoRec->doDetachDriverViaEmt(mpUVM, pAutoLock);
     5646                Capture.mAudioVideoRec->doDetachDriverViaEmt(mpUVM, pAutoLock);
    56505647# endif
    56515648            }
     
    56625659#endif /* VBOX_WITH_VIDEOREC */
    56635660
    5664 HRESULT Console::i_onVideoCaptureChange()
     5661HRESULT Console::i_onCaptureChange()
    56655662{
    56665663    AutoCaller autoCaller(this);
     
    56755672    if (ptrVM.isOk())
    56765673    {
     5674        ComPtr<ICaptureSettings> CaptureSettings;
     5675        rc = mMachine->COMGETTER(CaptureSettings)(CaptureSettings.asOutParam());
     5676        AssertComRCReturnRC(rc);
     5677
    56775678        BOOL fEnabled;
    5678         rc = mMachine->COMGETTER(VideoCaptureEnabled)(&fEnabled);
     5679        rc = CaptureSettings->COMGETTER(Enabled)(&fEnabled);
    56795680        AssertComRCReturnRC(rc);
    56805681
     
    56835684        {
    56845685            alock.release();
    5685             fireVideoCaptureChangedEvent(mEventSource);
     5686            fireCaptureChangedEvent(mEventSource);
    56865687        }
    56875688
     
    68676868 * @param   uTimestampMs        Timestamp (in ms) of audio data.
    68686869 */
    6869 HRESULT Console::i_audioVideoRecSendAudio(const void *pvData, size_t cbData, uint64_t uTimestampMs)
    6870 {
    6871     if (mDisplay)
    6872     {
    6873         int rc2 = mDisplay->i_videoRecSendAudio(pvData, cbData, uTimestampMs);
    6874         AssertRC(rc2);
     6870HRESULT Console::i_videoRecSendAudio(const void *pvData, size_t cbData, uint64_t uTimestampMs)
     6871{
     6872    if (!Capture.mpVideoRecCtx)
     6873        return S_OK;
     6874
     6875    if (   Capture.mpVideoRecCtx->IsStarted()
     6876        && Capture.mpVideoRecCtx->IsFeatureEnabled(CaptureFeature_Audio))
     6877    {
     6878        return Capture.mpVideoRecCtx->SendAudioFrame(pvData, cbData, uTimestampMs);
    68756879    }
    68766880
     
    68786882}
    68796883#endif /* VBOX_WITH_AUDIO_VIDEOREC */
     6884
     6885#ifdef VBOX_WITH_VIDEOREC
     6886int Console::i_videoRecLoad(settings::CaptureSettings &Settings)
     6887{
     6888    Assert(mMachine.isNotNull());
     6889
     6890    ComPtr<ICaptureSettings> pCaptureSettings;
     6891    HRESULT hrc = mMachine->COMGETTER(CaptureSettings)(pCaptureSettings.asOutParam());
     6892    AssertComRCReturn(hrc, VERR_INVALID_PARAMETER);
     6893
     6894    SafeIfaceArray<ICaptureScreenSettings> paCaptureScreens;
     6895    hrc = pCaptureSettings->GetScreens(ComSafeArrayAsOutParam(paCaptureScreens));
     6896    AssertComRCReturn(hrc, VERR_INVALID_PARAMETER);
     6897
     6898    Settings.mapScreens.clear();
     6899
     6900    for (size_t i = 0; i < paCaptureScreens.size(); ++i)
     6901    {
     6902        settings::CaptureScreenSettings CaptureScreenSettings;
     6903        ComPtr<ICaptureScreenSettings> pCaptureScreenSettings = paCaptureScreens[i];
     6904
     6905        hrc = pCaptureScreenSettings->COMGETTER(MaxTime)((ULONG *)&CaptureScreenSettings.ulMaxTimeS);
     6906        AssertComRCReturn(hrc, VERR_INVALID_PARAMETER);
     6907        hrc = pCaptureScreenSettings->COMGETTER(MaxFileSize)((ULONG *)&CaptureScreenSettings.File.ulMaxSizeMB);
     6908        AssertComRCReturn(hrc, VERR_INVALID_PARAMETER);
     6909        Bstr bstrTemp;
     6910        hrc = pCaptureScreenSettings->COMGETTER(FileName)(bstrTemp.asOutParam());
     6911        AssertComRCReturn(hrc, VERR_INVALID_PARAMETER);
     6912        CaptureScreenSettings.File.strName = bstrTemp;
     6913        hrc = pCaptureScreenSettings->COMGETTER(Options)(bstrTemp.asOutParam());
     6914        AssertComRCReturn(hrc, VERR_INVALID_PARAMETER);
     6915        CaptureScreenSettings.strOptions = bstrTemp;
     6916        hrc = pCaptureScreenSettings->COMGETTER(VideoWidth)((ULONG *)&CaptureScreenSettings.Video.ulWidth);
     6917        AssertComRCReturn(hrc, VERR_INVALID_PARAMETER);
     6918        hrc = pCaptureScreenSettings->COMGETTER(VideoHeight)((ULONG *)&CaptureScreenSettings.Video.ulHeight);
     6919        AssertComRCReturn(hrc, VERR_INVALID_PARAMETER);
     6920        hrc = pCaptureScreenSettings->COMGETTER(VideoRate)((ULONG *)&CaptureScreenSettings.Video.ulRate);
     6921        AssertComRCReturn(hrc, VERR_INVALID_PARAMETER);
     6922        hrc = pCaptureScreenSettings->COMGETTER(VideoFPS)((ULONG *)&CaptureScreenSettings.Video.ulFPS);
     6923        AssertComRCReturn(hrc, VERR_INVALID_PARAMETER);
     6924
     6925        Settings.mapScreens[i] = CaptureScreenSettings;
     6926    }
     6927
     6928    return VINF_SUCCESS;
     6929}
     6930
     6931/**
     6932 * Starts capturing. Does nothing if capturing is already active.
     6933 *
     6934 * @returns IPRT status code.
     6935 */
     6936int Console::i_videoRecStart(void)
     6937{
     6938    if (Capture.mpVideoRecCtx && Capture.mpVideoRecCtx->IsStarted())
     6939        return VINF_SUCCESS;
     6940
     6941    LogRel(("VideoRec: Starting ...\n"));
     6942
     6943    try
     6944    {
     6945        Capture.mpVideoRecCtx = new CaptureContext(this);
     6946    }
     6947    catch (std::bad_alloc &)
     6948    {
     6949        return VERR_NO_MEMORY;
     6950    }
     6951    catch (int &rc)
     6952    {
     6953        return rc;
     6954    }
     6955
     6956    settings::CaptureSettings Settings;
     6957    int rc = i_videoRecLoad(Settings);
     6958    if (RT_SUCCESS(rc))
     6959    {
     6960        rc = Capture.mpVideoRecCtx->Create(Settings);
     6961        if (RT_SUCCESS(rc))
     6962        {
     6963            for (unsigned uScreen = 0; uScreen < Capture.mpVideoRecCtx->GetStreamCount(); uScreen++)
     6964                mDisplay->i_videoRecScreenChanged(uScreen);
     6965        }
     6966    }
     6967
     6968    if (RT_FAILURE(rc))
     6969        LogRel(("VideoRec: Failed to start video recording (%Rrc)\n", rc));
     6970
     6971    return rc;
     6972}
     6973
     6974/**
     6975 * Stops capturing. Does nothing if capturing is not active.
     6976 */
     6977int Console::i_videoRecStop(void)
     6978{
     6979    if (Capture.mpVideoRecCtx && Capture.mpVideoRecCtx->IsStarted())
     6980        return VINF_SUCCESS;
     6981
     6982    LogRel(("VideoRec: Stopping ...\n"));
     6983
     6984    const size_t cStreams = Capture.mpVideoRecCtx->GetStreamCount();
     6985
     6986    for (unsigned uScreen = 0; uScreen < cStreams; ++uScreen)
     6987        mDisplay->i_videoRecScreenChanged(uScreen);
     6988
     6989    delete Capture.mpVideoRecCtx;
     6990    Capture.mpVideoRecCtx = NULL;
     6991
     6992    ComPtr<ICaptureSettings> pCaptureSettings;
     6993    HRESULT hrc = mMachine->COMGETTER(CaptureSettings)(pCaptureSettings.asOutParam());
     6994    ComAssertComRC(hrc);
     6995    hrc = pCaptureSettings->COMSETTER(Enabled)(false);
     6996    ComAssertComRC(hrc);
     6997
     6998    LogRel(("VideoRec: Stopped\n"));
     6999
     7000    return VINF_SUCCESS;
     7001}
     7002#endif /* VBOX_WITH_VIDEOREC */
    68807003
    68817004/**
     
    996610089
    996710090#ifdef VBOX_WITH_VIDEOREC
    9968         BOOL fVideoRecEnabled = FALSE;
    9969         rc = pConsole->mMachine->COMGETTER(VideoCaptureEnabled)(&fVideoRecEnabled);
     10091        ComPtr<ICaptureSettings> CaptureSettings;
     10092        rc = pConsole->mMachine->COMGETTER(CaptureSettings)(CaptureSettings.asOutParam());
    997010093        AssertComRCReturnVoid(rc);
    997110094
    9972         if (fVideoRecEnabled)
    9973         {
    9974             int vrc2 = pConsole->i_videoCaptureEnable(fVideoRecEnabled, &alock);
     10095        BOOL fCaptureEnabled;
     10096        rc = CaptureSettings->COMGETTER(Enabled)(&fCaptureEnabled);
     10097        AssertComRCReturnVoid(rc);
     10098
     10099        if (fCaptureEnabled)
     10100        {
     10101            int vrc2 = pConsole->i_videoCaptureEnable(fCaptureEnabled, &alock);
    997510102            if (RT_SUCCESS(vrc2))
    997610103            {
    9977                 fireVideoCaptureChangedEvent(pConsole->mEventSource);
     10104                fireCaptureChangedEvent(pConsole->mEventSource);
    997810105            }
    997910106            else
  • trunk/src/VBox/Main/src-client/ConsoleImpl2.cpp

    r74474 r75251  
    29982998                InsertConfigString(pLunL0, "Driver", "AUDIO");
    29992999            AudioDriverCfg DrvCfgVideoRec(strAudioDevice, 0 /* Instance */, uAudioLUN, "AudioVideoRec");
    3000             rc = mAudioVideoRec->InitializeConfig(&DrvCfgVideoRec);
     3000            rc = Capture.mAudioVideoRec->InitializeConfig(&DrvCfgVideoRec);
    30013001            if (RT_SUCCESS(rc))
    30023002                uAudioLUN++;
  • trunk/src/VBox/Main/src-client/DisplayImpl.cpp

    r75067 r75251  
    150150    AssertRC(rc);
    151151
    152     mpVideoRecCtx = NULL;
    153152    for (unsigned i = 0; i < RT_ELEMENTS(maVideoRecEnabled); i++)
    154153        maVideoRecEnabled[i] = true;
     
    24082407#ifdef VBOX_WITH_VIDEOREC
    24092408/**
    2410  * Returns the currently enabled video capturing features.
     2409 * Invalidates the capturing configuration.
    24112410 *
    2412  * @returns Enables video capturing features.
     2411 * @returns IPRT status code.
    24132412 */
    2414 VIDEORECFEATURES Display::i_videoRecGetFeatures(void)
    2415 {
    2416     return VideoRecGetFeatures(&mVideoRecCfg);
    2417 }
    2418 
    2419 /**
    2420  * Returns whether video capturing is currently is active or not.
    2421  *
    2422  * @returns True if video capturing is active, false if not.
    2423  */
    2424 bool Display::i_videoRecStarted(void)
    2425 {
    2426     return VideoRecIsStarted(mpVideoRecCtx);
    2427 }
    2428 
    2429 /**
    2430  * Invalidates the video recording configuration.
    2431  */
    2432 void Display::i_videoRecInvalidate(void)
    2433 {
    2434     AssertPtr(mParent);
    2435     ComPtr<IMachine> pMachine = mParent->i_machine();
    2436     Assert(pMachine.isNotNull());
    2437 
    2438     mVideoRecCfg.enmDst = VIDEORECDEST_FILE; /** @todo Make this configurable once we have more variations. */
    2439 
    2440     /*
    2441      * Cache parameters from API.
    2442      */
    2443     com::SafeArray<BOOL> aScreens;
    2444     HRESULT hrc = pMachine->COMGETTER(VideoCaptureScreens)(ComSafeArrayAsOutParam(aScreens));
    2445     AssertComRCReturnVoid(hrc);
    2446 
    2447     mVideoRecCfg.aScreens.resize(aScreens.size());
    2448     for (size_t i = 0; i < aScreens.size(); ++i)
    2449         mVideoRecCfg.aScreens[i] = aScreens[i];
    2450 
    2451     hrc = pMachine->COMGETTER(VideoCaptureWidth)((ULONG *)&mVideoRecCfg.Video.uWidth);
    2452     AssertComRCReturnVoid(hrc);
    2453     hrc = pMachine->COMGETTER(VideoCaptureHeight)((ULONG *)&mVideoRecCfg.Video.uHeight);
    2454     AssertComRCReturnVoid(hrc);
    2455     hrc = pMachine->COMGETTER(VideoCaptureRate)((ULONG *)&mVideoRecCfg.Video.uRate);
    2456     AssertComRCReturnVoid(hrc);
    2457     hrc = pMachine->COMGETTER(VideoCaptureFPS)((ULONG *)&mVideoRecCfg.Video.uFPS);
    2458     AssertComRCReturnVoid(hrc);
    2459     hrc = pMachine->COMGETTER(VideoCaptureFile)(mVideoRecCfg.File.strName.asOutParam());
    2460     AssertComRCReturnVoid(hrc);
    2461     hrc = pMachine->COMGETTER(VideoCaptureMaxFileSize)((ULONG *)&mVideoRecCfg.File.uMaxSizeMB);
    2462     AssertComRCReturnVoid(hrc);
    2463     hrc = pMachine->COMGETTER(VideoCaptureMaxTime)((ULONG *)&mVideoRecCfg.uMaxTimeS);
    2464     AssertComRCReturnVoid(hrc);
    2465     BSTR bstrOptions;
    2466     hrc = pMachine->COMGETTER(VideoCaptureOptions)(&bstrOptions);
    2467     AssertComRCReturnVoid(hrc);
    2468 
    2469     /*
    2470      * Set sensible defaults.
    2471      */
    2472     mVideoRecCfg.Video.fEnabled = true; /* Enabled by default. */
    2473 
    2474     if (!mVideoRecCfg.Video.uFPS) /* Prevent division by zero. */
    2475         mVideoRecCfg.Video.uFPS = 15;
    2476 
    2477 #ifdef VBOX_WITH_LIBVPX
    2478     mVideoRecCfg.Video.Codec.VPX.uEncoderDeadline = 1000000 / mVideoRecCfg.Video.uFPS;
    2479 #endif
    2480 
    2481 #ifdef VBOX_WITH_AUDIO_VIDEOREC
    2482     mVideoRecCfg.Audio.fEnabled  = false; /* Disabled by default, unless set otherwise below. */
    2483     /* By default we use 22,5kHz, 16-bit, stereo for the audio track. */
    2484     mVideoRecCfg.Audio.uHz       = 22050;
    2485     mVideoRecCfg.Audio.cBits     = 16;
    2486     mVideoRecCfg.Audio.cChannels = 2;
    2487 #endif
    2488 
    2489     /*
    2490      * Parse options string.
    2491      */
    2492     com::Utf8Str strOptions(bstrOptions);
    2493     size_t pos = 0;
    2494     com::Utf8Str key, value;
    2495     while ((pos = strOptions.parseKeyValue(key, value, pos)) != com::Utf8Str::npos)
    2496     {
    2497         if (key.compare("vc_quality", Utf8Str::CaseInsensitive) == 0)
    2498         {
    2499 #ifdef VBOX_WITH_LIBVPX
    2500             if (value.compare("realtime", Utf8Str::CaseInsensitive) == 0)
    2501                 mVideoRecCfg.Video.Codec.VPX.uEncoderDeadline = VPX_DL_REALTIME;
    2502             else if (value.compare("good", Utf8Str::CaseInsensitive) == 0)
    2503                 mVideoRecCfg.Video.Codec.VPX.uEncoderDeadline = 1000000 / mVideoRecCfg.Video.uFPS;
    2504             else if (value.compare("best", Utf8Str::CaseInsensitive) == 0)
    2505                 mVideoRecCfg.Video.Codec.VPX.uEncoderDeadline = VPX_DL_BEST_QUALITY;
    2506             else
    2507             {
    2508                 LogRel(("VideoRec: Setting quality deadline to '%s'\n", value.c_str()));
    2509                 mVideoRecCfg.Video.Codec.VPX.uEncoderDeadline = value.toUInt32();
    2510 #endif
    2511             }
    2512         }
    2513         else if (key.compare("vc_enabled", Utf8Str::CaseInsensitive) == 0)
    2514         {
    2515             if (value.compare("false", Utf8Str::CaseInsensitive) == 0)
    2516             {
    2517                 mVideoRecCfg.Video.fEnabled = false;
    2518 #ifdef VBOX_WITH_AUDIO_VIDEOREC
    2519                 LogRel(("VideoRec: Only audio will be recorded\n"));
    2520 #endif
    2521             }
    2522         }
    2523         else if (key.compare("ac_enabled", Utf8Str::CaseInsensitive) == 0)
    2524         {
    2525 #ifdef VBOX_WITH_AUDIO_VIDEOREC
    2526             if (value.compare("true", Utf8Str::CaseInsensitive) == 0)
    2527             {
    2528                 mVideoRecCfg.Audio.fEnabled = true;
    2529 
    2530             }
    2531             else
    2532                 LogRel(("VideoRec: Only video will be recorded\n"));
    2533 #endif
    2534         }
    2535         else if (key.compare("ac_profile", Utf8Str::CaseInsensitive) == 0)
    2536         {
    2537 #ifdef VBOX_WITH_AUDIO_VIDEOREC
    2538             if (value.compare("low", Utf8Str::CaseInsensitive) == 0)
    2539             {
    2540                 mVideoRecCfg.Audio.uHz       = 8000;
    2541                 mVideoRecCfg.Audio.cBits     = 16;
    2542                 mVideoRecCfg.Audio.cChannels = 1;
    2543             }
    2544             else if (value.startsWith("med" /* "med[ium]" */, Utf8Str::CaseInsensitive) == 0)
    2545             {
    2546                 /* Stay with the default set above. */
    2547             }
    2548             else if (value.compare("high", Utf8Str::CaseInsensitive) == 0)
    2549             {
    2550                 mVideoRecCfg.Audio.uHz       = 48000;
    2551                 mVideoRecCfg.Audio.cBits     = 16;
    2552                 mVideoRecCfg.Audio.cChannels = 2;
    2553             }
    2554 #endif
    2555         }
    2556         else
    2557             LogRel(("VideoRec: Unknown option '%s' (value '%s'), skipping\n", key.c_str(), value.c_str()));
    2558 
    2559     } /* while */
     2413int Display::i_videoRecInvalidate(void)
     2414{
     2415    CaptureContext *pCtx = mParent->i_videoRecGetContext();
     2416    if (!pCtx || !pCtx->IsStarted())
     2417        return VINF_SUCCESS;
    25602418
    25612419    /*
    25622420     * Invalidate screens.
    25632421     */
    2564     for (unsigned i = 0; i < mVideoRecCfg.aScreens.size(); i++)
    2565     {
    2566         bool fChanged = maVideoRecEnabled[i] != RT_BOOL(mVideoRecCfg.aScreens[i]);
    2567 
    2568         maVideoRecEnabled[i] = RT_BOOL(mVideoRecCfg.aScreens[i]);
    2569 
    2570         if (fChanged && i < mcMonitors)
    2571             i_videoRecScreenChanged(i);
    2572 
    2573     }
    2574 }
    2575 
    2576 /**
    2577  * Sends belonging audio samples to the video capturing code.
    2578  * Does nothing if capturing is disabled or if audio support for video capturing is disabled.
    2579  *
    2580  * @returns IPRT status code.
    2581  * @param   pvData              Audio data.
    2582  * @param   cbData              Size (in bytes) of audio data.
    2583  * @param   uTimestampMs        Timestamp (in ms) of the audio data.
    2584  */
    2585 int Display::i_videoRecSendAudio(const void *pvData, size_t cbData, uint64_t uTimestampMs)
    2586 {
    2587     if (   VideoRecIsStarted(mpVideoRecCtx)
    2588         && VideoRecGetFeatures(&mVideoRecCfg) & VIDEORECFEATURE_AUDIO)
    2589     {
    2590         return VideoRecSendAudioFrame(mpVideoRecCtx, pvData, cbData, uTimestampMs);
     2422    for (unsigned uScreen = 0; uScreen < mcMonitors; uScreen++)
     2423    {
     2424        CaptureStream *pCaptureStream = pCtx->GetStream(uScreen);
     2425
     2426        const bool fStreamEnabled = pCaptureStream->IsReady();
     2427              bool fChanged       = maVideoRecEnabled[uScreen] != fStreamEnabled;
     2428
     2429        maVideoRecEnabled[uScreen] = fStreamEnabled;
     2430
     2431        if (fChanged && uScreen < mcMonitors)
     2432            i_videoRecScreenChanged(uScreen);
    25912433    }
    25922434
     
    25942436}
    25952437
    2596 /**
    2597  * Start video capturing. Does nothing if capturing is already active.
    2598  *
    2599  * @param   pVideoRecCfg        Video recording configuration to use.
    2600  * @returns IPRT status code.
    2601  */
    2602 int Display::i_videoRecStart(void)
    2603 {
    2604     if (VideoRecIsStarted(mpVideoRecCtx))
    2605         return VINF_SUCCESS;
    2606 
    2607     LogRel(("VideoRec: Starting ...\n"));
    2608 
    2609     int rc = VideoRecContextCreate(mcMonitors, &mVideoRecCfg, &mpVideoRecCtx);
    2610     if (RT_SUCCESS(rc))
    2611     {
    2612         for (unsigned uScreen = 0; uScreen < mcMonitors; uScreen++)
    2613         {
    2614             int rc2 = VideoRecStreamInit(VideoRecGetStream(mpVideoRecCtx, uScreen), mpVideoRecCtx, uScreen);
    2615             if (RT_SUCCESS(rc2))
    2616             {
    2617                 i_videoRecScreenChanged(uScreen);
    2618             }
    2619             else
    2620                 LogRel(("VideoRec: Failed to initialize video recording context #%u (%Rrc)\n", uScreen, rc2));
    2621 
    2622             if (RT_SUCCESS(rc))
    2623                 rc = rc2;
    2624         }
    2625     }
    2626 
    2627     if (RT_FAILURE(rc))
    2628         LogRel(("VideoRec: Failed to start video recording (%Rrc)\n", rc));
    2629 
    2630     return rc;
    2631 }
    2632 
    2633 /**
    2634  * Stops video capturing. Does nothing if video capturing is not active.
    2635  */
    2636 void Display::i_videoRecStop(void)
    2637 {
    2638     if (!VideoRecIsStarted(mpVideoRecCtx))
    2639         return;
    2640 
    2641     LogRel(("VideoRec: Stopping ...\n"));
    2642 
    2643     VideoRecContextDestroy(mpVideoRecCtx);
    2644     mpVideoRecCtx = NULL;
    2645 
    2646     unsigned uScreenId;
    2647     for (uScreenId = 0; uScreenId < mcMonitors; ++uScreenId)
    2648         i_videoRecScreenChanged(uScreenId);
    2649 }
    2650 
    26512438void Display::i_videoRecScreenChanged(unsigned uScreenId)
    26522439{
    2653     if (   !VideoRecIsStarted(mpVideoRecCtx)
    2654         || !maVideoRecEnabled[uScreenId])
     2440    CaptureContext *pCtx = mParent->i_videoRecGetContext();
     2441
     2442    if (   RT_LIKELY(!maVideoRecEnabled[uScreenId])
     2443        || !pCtx || !pCtx->IsStarted())
    26552444    {
    26562445        /* Skip recording this screen. */
     
    35993388
    36003389#ifdef VBOX_WITH_VIDEOREC
    3601     if (   VideoRecIsStarted(pDisplay->mpVideoRecCtx)
    3602         && VideoRecGetFeatures(&pDisplay->mVideoRecCfg) & VIDEORECFEATURE_VIDEO)
     3390    AssertPtr(pDisplay->mParent);
     3391    CaptureContext *pCtx = pDisplay->mParent->i_videoRecGetContext();
     3392
     3393    if (   pCtx
     3394        && pCtx->IsStarted()
     3395        && pCtx->IsFeatureEnabled(CaptureFeature_Video))
    36033396    {
    36043397        do {
     
    36383431# endif /* VBOX_WITH_HGCM && VBOX_WITH_CROGL */
    36393432
    3640             uint64_t u64Now = RTTimeProgramMilliTS();
     3433            uint64_t tsNowMs = RTTimeProgramMilliTS();
    36413434            for (uScreenId = 0; uScreenId < pDisplay->mcMonitors; uScreenId++)
    36423435            {
     
    36443437                    continue;
    36453438
    3646                 if (VideoRecIsLimitReached(pDisplay->mpVideoRecCtx, uScreenId, u64Now))
     3439                if (pCtx->IsLimitReached(uScreenId, tsNowMs))
    36473440                {
    3648                     pDisplay->i_videoRecStop();
    3649                     pDisplay->mParent->i_machine()->COMSETTER(VideoCaptureEnabled)(false);
     3441                    pDisplay->mParent->i_videoRecStop();
    36503442                    break;
    36513443                }
     
    36773469                                                                    &bitmapFormat);
    36783470                        if (SUCCEEDED(hr) && pbAddress)
    3679                             rc = VideoRecSendVideoFrame(pDisplay->mpVideoRecCtx, uScreenId, 0, 0,
    3680                                                         BitmapFormat_BGR,
    3681                                                         ulBitsPerPixel, ulBytesPerLine, ulWidth, ulHeight,
    3682                                                         pbAddress, u64Now);
     3471                            rc = pCtx->SendVideoFrame(uScreenId, 0, 0, BitmapFormat_BGR,
     3472                                                      ulBitsPerPixel, ulBytesPerLine, ulWidth, ulHeight,
     3473                                                      pbAddress, tsNowMs);
    36833474                        else
    36843475                            rc = VERR_NOT_SUPPORTED;
     
    40393830}
    40403831
    4041 bool  Display::i_handleCrVRecScreenshotBegin(uint32_t uScreen, uint64_t u64Timestamp)
    4042 {
    4043     /** @todo r=bird: u64Timestamp - using the 'u64' prefix add nothing.
    4044      *        However, using one of the prefixes indicating the timestamp unit
    4045      *        would be very valuable!  */
     3832bool  Display::i_handleCrVRecScreenshotBegin(uint32_t uScreen, uint64_t uTimestampMs)
     3833{
    40463834# ifdef VBOX_WITH_VIDEOREC
    4047     return VideoRecIsReady(mpVideoRecCtx, uScreen, u64Timestamp);
     3835    CaptureContext *pCtx = mParent->i_videoRecGetContext();
     3836    return (      pCtx
     3837               && pCtx->IsReady(uScreen, uTimestampMs));
    40483838# else
    4049     RT_NOREF(uScreen, u64Timestamp);
     3839    RT_NOREF(uScreen, uTimestampMs);
    40503840    return false;
    40513841# endif
    40523842}
    40533843
    4054 void  Display::i_handleCrVRecScreenshotEnd(uint32_t uScreen, uint64_t u64Timestamp)
    4055 {
    4056     RT_NOREF(uScreen, u64Timestamp);
     3844void  Display::i_handleCrVRecScreenshotEnd(uint32_t uScreen, uint64_t uTimestampMs)
     3845{
     3846    RT_NOREF(uScreen, uTimestampMs);
    40573847}
    40583848
     
    40613851                                               uint32_t uBitsPerPixel, uint32_t uBytesPerLine,
    40623852                                               uint32_t uGuestWidth, uint32_t uGuestHeight,
    4063                                                uint8_t *pu8BufferAddress, uint64_t u64Timestamp)
     3853                                               uint8_t *pu8BufferAddress, uint64_t uTimestampMs)
    40643854{
    40653855    Assert(mfCrOglVideoRecState == CRVREC_STATE_SUBMITTED);
    40663856# ifdef VBOX_WITH_VIDEOREC
    4067     if (   VideoRecIsStarted(mpVideoRecCtx)
    4068         && VideoRecGetFeatures(&mVideoRecCfg) & VIDEORECFEATURE_VIDEO)
    4069     {
    4070         int rc2 = VideoRecSendVideoFrame(mpVideoRecCtx, uScreen, x, y,
    4071                                          uPixelFormat,
    4072                                          uBitsPerPixel, uBytesPerLine,
    4073                                          uGuestWidth, uGuestHeight,
    4074                                          pu8BufferAddress, u64Timestamp);
     3857    CaptureContext *pCtx = mParent->i_videoRecGetContext();
     3858
     3859    if (   pCtx
     3860        && pCtx->IsStarted()
     3861        && pCtx->IsFeatureEnabled(CaptureFeature_Video))
     3862    {
     3863        int rc2 = pCtx->SendVideoFrame(uScreen, x, y,
     3864                                       uPixelFormat,
     3865                                       uBitsPerPixel, uBytesPerLine,
     3866                                       uGuestWidth, uGuestHeight,
     3867                                       pu8BufferAddress, uTimestampMs);
    40753868        RT_NOREF(rc2);
    40763869        Assert(rc2 == VINF_SUCCESS /* || rc == VERR_TRY_AGAIN || rc == VINF_TRY_AGAIN*/);
     
    40783871# else
    40793872    RT_NOREF(uScreen, x, y, uPixelFormat, \
    4080              uBitsPerPixel, uBytesPerLine, uGuestWidth, uGuestHeight, pu8BufferAddress, u64Timestamp);
     3873             uBitsPerPixel, uBytesPerLine, uGuestWidth, uGuestHeight, pu8BufferAddress, uTimestampMs);
    40813874# endif /* VBOX_WITH_VIDEOREC */
    40823875}
     
    47064499        AutoWriteLock displayLock(pThis->pDisplay COMMA_LOCKVAL_SRC_POS);
    47074500#ifdef VBOX_WITH_VIDEOREC
    4708         pThis->pDisplay->i_videoRecStop();
     4501        pThis->pDisplay->mParent->i_videoRecStop();
    47094502#endif
    47104503#ifdef VBOX_WITH_CRHGSMI
  • trunk/src/VBox/Main/src-client/DrvAudioVideoRec.cpp

    r75083 r75251  
    828828                case AVRECCONTAINERTYPE_MAIN_CONSOLE:
    829829                {
    830                     HRESULT hr = pSink->Con.Main.pConsole->i_audioVideoRecSendAudio(pStreamAV->pvDstBuf, cbDst, uPTSMs);
     830                    HRESULT hr = pSink->Con.Main.pConsole->i_videoRecSendAudio(pStreamAV->pvDstBuf, cbDst, uPTSMs);
    831831                    Assert(hr == S_OK);
    832832                    RT_NOREF(hr);
     
    10861086 *
    10871087 * @returns IPRT status code.
    1088  * @param   pVideoRecCfg        Pointer to video recording configuration to apply.
    1089  */
    1090 int AudioVideoRec::applyConfiguration(const PVIDEORECCFG pVideoRecCfg)
     1088 * @param   Settings        Capturing configuration to apply.
     1089 */
     1090int AudioVideoRec::applyConfiguration(const settings::CaptureSettings &a_Settings)
    10911091{
    10921092    /** @todo Do some validation here. */
    1093     mVideoRecCfg = *pVideoRecCfg; /* Note: Does have an own copy operator. */
     1093    mVideoRecCfg = a_Settings; /* Note: Does have an own copy operator. */
    10941094    return VINF_SUCCESS;
    10951095}
     
    11011101int AudioVideoRec::configureDriver(PCFGMNODE pLunCfg)
    11021102{
    1103     int rc = CFGMR3InsertInteger(pLunCfg, "Object",    (uintptr_t)mpConsole->i_getAudioVideoRec());
     1103    int rc = CFGMR3InsertInteger(pLunCfg, "Object",    (uintptr_t)mpConsole->i_videoRecGetAudioDrv());
    11041104    AssertRCReturn(rc, rc);
    11051105    rc = CFGMR3InsertInteger(pLunCfg, "ObjectConsole", (uintptr_t)mpConsole);
    11061106    AssertRCReturn(rc, rc);
    11071107
    1108     rc = CFGMR3InsertInteger(pLunCfg, "ContainerType", (uint64_t)mVideoRecCfg.enmDst);
     1108    /** @todo For now we're using the configuration of the first screen here audio-wise. */
     1109    Assert(mVideoRecCfg.mapScreens.size() >= 1);
     1110    const settings::CaptureScreenSettings &Screen0Settings = mVideoRecCfg.mapScreens[0];
     1111
     1112    rc = CFGMR3InsertInteger(pLunCfg, "ContainerType", (uint64_t)Screen0Settings.enmDest);
    11091113    AssertRCReturn(rc, rc);
    1110     if (mVideoRecCfg.enmDst == VIDEORECDEST_FILE)
    1111     {
    1112         rc = CFGMR3InsertString(pLunCfg, "ContainerFileName", Utf8Str(mVideoRecCfg.File.strName).c_str());
     1114    if (Screen0Settings.enmDest == CaptureDestination_File)
     1115    {
     1116        rc = CFGMR3InsertString(pLunCfg, "ContainerFileName", Utf8Str(Screen0Settings.File.strName).c_str());
    11131117        AssertRCReturn(rc, rc);
    11141118    }
    1115     rc = CFGMR3InsertInteger(pLunCfg, "CodecHz", mVideoRecCfg.Audio.uHz);
     1119    rc = CFGMR3InsertInteger(pLunCfg, "CodecHz", Screen0Settings.Audio.uHz);
    11161120    AssertRCReturn(rc, rc);
    1117     rc = CFGMR3InsertInteger(pLunCfg, "CodecBits", mVideoRecCfg.Audio.cBits);
     1121    rc = CFGMR3InsertInteger(pLunCfg, "CodecBits", Screen0Settings.Audio.cBits);
    11181122    AssertRCReturn(rc, rc);
    1119     rc = CFGMR3InsertInteger(pLunCfg, "CodecChannels", mVideoRecCfg.Audio.cChannels);
     1123    rc = CFGMR3InsertInteger(pLunCfg, "CodecChannels", Screen0Settings.Audio.cChannels);
    11201124    AssertRCReturn(rc, rc);
    11211125    rc = CFGMR3InsertInteger(pLunCfg, "CodecBitrate", 0); /* Let Opus decide for now. */
  • trunk/src/VBox/Main/src-client/SessionImpl.cpp

    r68986 r75251  
    725725}
    726726
    727 HRESULT Session::onVideoCaptureChange()
    728 {
    729     LogFlowThisFunc(("\n"));
    730 
    731     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    732     AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
    733     AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
    734 #ifndef VBOX_COM_INPROC_API_CLIENT
    735     AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
    736 
    737     return mConsole->i_onVideoCaptureChange();
     727HRESULT Session::onCaptureChange()
     728{
     729    LogFlowThisFunc(("\n"));
     730
     731    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     732    AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE);
     733    AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE);
     734#ifndef VBOX_COM_INPROC_API_CLIENT
     735    AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
     736
     737    return mConsole->i_onCaptureChange();
    738738#else
    739739    return S_OK;
  • trunk/src/VBox/Main/src-client/VideoRec.cpp

    r75068 r75251  
    4343#include <VBox/com/VirtualBox.h>
    4444
    45 #include "WebMWriter.h"
     45#include "ConsoleImpl.h"
    4646#include "VideoRec.h"
    4747#include "VideoRecInternals.h"
    4848#include "VideoRecStream.h"
    4949#include "VideoRecUtils.h"
     50#include "WebMWriter.h"
    5051
    5152using namespace com;
     
    103104
    104105
     106CaptureContext::CaptureContext(Console *a_pConsole)
     107    : pConsole(a_pConsole) { }
     108
     109CaptureContext::CaptureContext(Console *a_pConsole, const settings::CaptureSettings &a_Settings)
     110    : pConsole(a_pConsole)
     111{
     112    int rc = CaptureContext::createInternal(a_Settings);
     113    if (RT_FAILURE(rc))
     114        throw rc;
     115}
     116
     117CaptureContext::~CaptureContext(void)
     118{
     119    destroyInternal();
     120}
     121
    105122/**
    106123 * Worker thread for all streams of a video recording context.
     
    108125 * For video frames, this also does the RGB/YUV conversion and encoding.
    109126 */
    110 static DECLCALLBACK(int) videoRecThread(RTTHREAD hThreadSelf, void *pvUser)
    111 {
    112     PVIDEORECCONTEXT pCtx = (PVIDEORECCONTEXT)pvUser;
     127DECLCALLBACK(int) CaptureContext::threadMain(RTTHREAD hThreadSelf, void *pvUser)
     128{
     129    CaptureContext *pThis = (CaptureContext *)pvUser;
    113130
    114131    /* Signal that we're up and rockin'. */
     
    119136    for (;;)
    120137    {
    121         int rc = RTSemEventWait(pCtx->WaitEvent, RT_INDEFINITE_WAIT);
     138        int rc = RTSemEventWait(pThis->WaitEvent, RT_INDEFINITE_WAIT);
    122139        AssertRCBreak(rc);
    123140
    124         Log2Func(("Processing %zu streams\n", pCtx->vecStreams.size()));
     141        Log2Func(("Processing %zu streams\n", pThis->vecStreams.size()));
    125142
    126143        /** @todo r=andy This is inefficient -- as we already wake up this thread
    127144         *               for every screen from Main, we here go again (on every wake up) through
    128145         *               all screens.  */
    129         VideoRecStreams::iterator itStream = pCtx->vecStreams.begin();
    130         while (itStream != pCtx->vecStreams.end())
    131         {
    132             PVIDEORECSTREAM pStream = (*itStream);
    133 
    134             rc = VideoRecStreamProcess(pStream);
     146        VideoRecStreams::iterator itStream = pThis->vecStreams.begin();
     147        while (itStream != pThis->vecStreams.end())
     148        {
     149            CaptureStream *pStream = (*itStream);
     150
     151            rc = pStream->Process(pThis->mapBlocksCommon);
    135152            if (RT_FAILURE(rc))
    136153                break;
     
    144161        /* Keep going in case of errors. */
    145162
    146         if (ASMAtomicReadBool(&pCtx->fShutdown))
     163        if (ASMAtomicReadBool(&pThis->fShutdown))
    147164        {
    148165            LogFunc(("Thread is shutting down ...\n"));
     
    160177 *
    161178 * @returns IPRT status code.
    162  * @param   pCtx                Video recording context to notify thread for.
    163  */
    164 static int videoRecThreadNotify(PVIDEORECCONTEXT pCtx)
    165 {
    166     AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
    167 
    168     return RTSemEventSignal(pCtx->WaitEvent);
     179 */
     180int CaptureContext::threadNotify(void)
     181{
     182    return RTSemEventSignal(this->WaitEvent);
    169183}
    170184
     
    175189 * @param   cScreens            Number of screens to create context for.
    176190 * @param   pVideoRecCfg        Pointer to video recording configuration to use.
    177  * @param   ppCtx               Pointer to created video recording context on success.
    178  */
    179 int VideoRecContextCreate(uint32_t cScreens, PVIDEORECCFG pVideoRecCfg, PVIDEORECCONTEXT *ppCtx)
    180 {
    181     AssertReturn(cScreens,        VERR_INVALID_PARAMETER);
    182     AssertPtrReturn(pVideoRecCfg, VERR_INVALID_POINTER);
    183     AssertPtrReturn(ppCtx,        VERR_INVALID_POINTER);
    184 
    185     VIDEORECCONTEXT *pCtx = NULL;
    186     try
    187     {
    188         pCtx = new VIDEORECCONTEXT();
    189     }
    190     catch (std::bad_alloc &)
    191     {
    192         return VERR_NO_MEMORY;
    193     }
    194 
    195     int rc = RTCritSectInit(&pCtx->CritSect);
     191 */
     192int CaptureContext::createInternal(const settings::CaptureSettings &a_Settings)
     193{
     194    int rc = RTCritSectInit(&this->CritSect);
    196195    if (RT_FAILURE(rc))
    197     {
    198         delete pCtx;
    199196        return rc;
    200     }
    201 
    202     for (uint32_t uScreen = 0; uScreen < cScreens; uScreen++)
    203     {
    204         VIDEORECSTREAM *pStream = NULL;
     197
     198    settings::CaptureScreenMap::const_iterator itScreen = a_Settings.mapScreens.begin();
     199    while (itScreen != a_Settings.mapScreens.end())
     200    {
     201        CaptureStream *pStream = NULL;
    205202        try
    206203        {
    207             pStream = new VIDEORECSTREAM();
     204            pStream = new CaptureStream(itScreen->first /* Screen ID */, itScreen->second);
     205            this->vecStreams.push_back(pStream);
    208206        }
    209207        catch (std::bad_alloc &)
     
    212210            break;
    213211        }
    214 
    215         rc = RTCritSectInit(&pStream->CritSect);
    216         if (RT_FAILURE(rc))
    217             break;
    218 
    219         try
    220         {
    221             pStream->uScreenID = uScreen;
    222 
    223             pCtx->vecStreams.push_back(pStream);
    224 
    225             pStream->File.pWEBM = new WebMWriter();
    226         }
    227         catch (std::bad_alloc &)
    228         {
    229             rc = VERR_NO_MEMORY;
    230             break;
    231         }
    232212    }
    233213
    234214    if (RT_SUCCESS(rc))
    235215    {
    236         pCtx->tsStartMs = RTTimeMilliTS();
    237         pCtx->enmState  = VIDEORECSTS_UNINITIALIZED;
    238         pCtx->fStarted  = false;
    239         pCtx->fShutdown = false;
    240 
    241         /* Copy the configuration to our context. */
    242         pCtx->Cfg       = *pVideoRecCfg;
    243 
    244         rc = RTSemEventCreate(&pCtx->WaitEvent);
     216        this->tsStartMs = RTTimeMilliTS();
     217        this->enmState  = VIDEORECSTS_UNINITIALIZED;
     218        this->fStarted  = false;
     219        this->fShutdown = false;
     220
     221        /* Copy the settings to our context. */
     222        this->Settings  = a_Settings;
     223
     224        rc = RTSemEventCreate(&this->WaitEvent);
    245225        AssertRCReturn(rc, rc);
    246226
    247         rc = RTThreadCreate(&pCtx->Thread, videoRecThread, (void *)pCtx, 0,
     227        rc = RTThreadCreate(&this->Thread, CaptureContext::threadMain, (void *)this, 0,
    248228                            RTTHREADTYPE_MAIN_WORKER, RTTHREADFLAGS_WAITABLE, "VideoRec");
    249229
    250230        if (RT_SUCCESS(rc)) /* Wait for the thread to start. */
    251             rc = RTThreadUserWait(pCtx->Thread, 30 * RT_MS_1SEC /* 30s timeout */);
     231            rc = RTThreadUserWait(this->Thread, 30 * RT_MS_1SEC /* 30s timeout */);
    252232
    253233        if (RT_SUCCESS(rc))
    254234        {
    255             pCtx->enmState = VIDEORECSTS_INITIALIZED;
    256             pCtx->fStarted = true;
    257 
    258             if (ppCtx)
    259                 *ppCtx = pCtx;
     235            this->enmState = VIDEORECSTS_INITIALIZED;
     236            this->fStarted = true;
    260237        }
    261238    }
     
    263240    if (RT_FAILURE(rc))
    264241    {
    265         int rc2 = VideoRecContextDestroy(pCtx);
     242        int rc2 = destroyInternal();
    266243        AssertRC(rc2);
    267244    }
     
    272249/**
    273250 * Destroys a video recording context.
    274  *
    275  * @param pCtx                  Video recording context to destroy.
    276  */
    277 int VideoRecContextDestroy(PVIDEORECCONTEXT pCtx)
    278 {
    279     if (!pCtx)
    280         return VINF_SUCCESS;
    281 
     251 */
     252int CaptureContext::destroyInternal(void)
     253{
    282254    int rc = VINF_SUCCESS;
    283255
    284     if (pCtx->enmState == VIDEORECSTS_INITIALIZED)
     256    if (this->enmState == VIDEORECSTS_INITIALIZED)
    285257    {
    286258        LogFunc(("Shutting down thread ...\n"));
    287259
    288260        /* Set shutdown indicator. */
    289         ASMAtomicWriteBool(&pCtx->fShutdown, true);
     261        ASMAtomicWriteBool(&this->fShutdown, true);
    290262
    291263        /* Signal the thread and wait for it to shut down. */
    292         rc = videoRecThreadNotify(pCtx);
     264        rc = threadNotify();
    293265        if (RT_SUCCESS(rc))
    294             rc = RTThreadWait(pCtx->Thread, 30 * 1000 /* 10s timeout */, NULL);
     266            rc = RTThreadWait(this->Thread, 30 * 1000 /* 10s timeout */, NULL);
    295267
    296268        if (RT_SUCCESS(rc))
    297269        {
    298270            /* Disable the context. */
    299             ASMAtomicWriteBool(&pCtx->fStarted, false);
    300 
    301             int rc2 = RTSemEventDestroy(pCtx->WaitEvent);
     271            ASMAtomicWriteBool(&this->fStarted, false);
     272
     273            int rc2 = RTSemEventDestroy(this->WaitEvent);
    302274            AssertRC(rc2);
    303275
    304             pCtx->WaitEvent = NIL_RTSEMEVENT;
     276            this->WaitEvent = NIL_RTSEMEVENT;
    305277        }
    306278    }
     
    312284    }
    313285
    314     rc = RTCritSectEnter(&pCtx->CritSect);
     286    rc = RTCritSectEnter(&this->CritSect);
    315287    if (RT_SUCCESS(rc))
    316288    {
    317         VideoRecStreams::iterator it = pCtx->vecStreams.begin();
    318         while (it != pCtx->vecStreams.end())
    319         {
    320             PVIDEORECSTREAM pStream = (*it);
    321 
    322             VideoRecStreamLock(pStream);
    323 
    324             int rc2 = VideoRecStreamClose(pStream);
     289        VideoRecStreams::iterator it = this->vecStreams.begin();
     290        while (it != this->vecStreams.end())
     291        {
     292            CaptureStream *pStream = (*it);
     293
     294            int rc2 = pStream->Uninit();
    325295            if (RT_SUCCESS(rc))
    326296                rc = rc2;
    327297
    328             rc2 = VideoRecStreamUninit(pStream);
    329             if (RT_SUCCESS(rc))
    330                 rc = rc2;
    331 
    332             pCtx->vecStreams.erase(it);
    333             it = pCtx->vecStreams.begin();
    334 
    335             VideoRecStreamUnlock(pStream);
    336 
    337             RTCritSectDelete(&pStream->CritSect);
    338 
    339298            delete pStream;
    340299            pStream = NULL;
     300
     301            this->vecStreams.erase(it);
     302            it = this->vecStreams.begin();
     303
     304            delete pStream;
     305            pStream = NULL;
    341306        }
    342307
    343308        /* Sanity. */
    344         Assert(pCtx->vecStreams.empty());
    345         Assert(pCtx->mapBlocksCommon.size() == 0);
    346 
    347         int rc2 = RTCritSectLeave(&pCtx->CritSect);
     309        Assert(this->vecStreams.empty());
     310        Assert(this->mapBlocksCommon.size() == 0);
     311
     312        int rc2 = RTCritSectLeave(&this->CritSect);
    348313        AssertRC(rc2);
    349314
    350         RTCritSectDelete(&pCtx->CritSect);
    351 
    352         delete pCtx;
    353         pCtx = NULL;
     315        RTCritSectDelete(&this->CritSect);
    354316    }
    355317
     
    357319}
    358320
    359 /**
    360  * Returns which recording features currently are enabled for a given configuration.
    361  *
    362  * @returns Enabled video recording features.
    363  * @param   pCfg                Pointer to recording configuration.
    364  */
    365 VIDEORECFEATURES VideoRecGetFeatures(PVIDEORECCFG pCfg)
    366 {
    367     if (!pCfg)
    368         return VIDEORECFEATURE_NONE;
    369 
    370     VIDEORECFEATURES fFeatures = VIDEORECFEATURE_NONE;
    371 
    372     if (pCfg->Video.fEnabled)
    373         fFeatures |= VIDEORECFEATURE_VIDEO;
    374 
    375 #ifdef VBOX_WITH_AUDIO_VIDEOREC
    376     if (pCfg->Audio.fEnabled)
    377         fFeatures |= VIDEORECFEATURE_AUDIO;
    378 #endif
    379 
    380     return fFeatures;
     321const settings::CaptureSettings &CaptureContext::GetConfig(void) const
     322{
     323    return this->Settings;
     324}
     325
     326CaptureStream *CaptureContext::getStreamInternal(unsigned uScreen) const
     327{
     328    CaptureStream *pStream;
     329
     330    try
     331    {
     332        pStream = this->vecStreams.at(uScreen);
     333    }
     334    catch (std::out_of_range &)
     335    {
     336        pStream = NULL;
     337    }
     338
     339    return pStream;
    381340}
    382341
     
    385344 *
    386345 * @returns Pointer to recording stream if found, or NULL if not found.
    387  * @param   pCtx                Recording context to look up stream for.
    388346 * @param   uScreen             Screen number of recording stream to look up.
    389347 */
    390 PVIDEORECSTREAM VideoRecGetStream(PVIDEORECCONTEXT pCtx, uint32_t uScreen)
    391 {
    392     AssertPtrReturn(pCtx, NULL);
    393 
    394     PVIDEORECSTREAM pStream;
    395 
    396     try
    397     {
    398         pStream = pCtx->vecStreams.at(uScreen);
    399     }
    400     catch (std::out_of_range &)
    401     {
    402         pStream = NULL;
    403     }
    404 
    405     return pStream;
     348CaptureStream *CaptureContext::GetStream(unsigned uScreen) const
     349{
     350    return getStreamInternal(uScreen);
     351}
     352
     353size_t CaptureContext::GetStreamCount(void) const
     354{
     355    return this->vecStreams.size();
     356}
     357
     358int CaptureContext::Create(const settings::CaptureSettings &a_Settings)
     359{
     360    return createInternal(a_Settings);
     361}
     362
     363int CaptureContext::Destroy(void)
     364{
     365    return destroyInternal();
     366}
     367
     368bool CaptureContext::IsFeatureEnabled(CaptureFeature_T enmFeature) const
     369{
     370    VideoRecStreams::const_iterator itStream = this->vecStreams.begin();
     371    while (itStream != this->vecStreams.end())
     372    {
     373        if ((*itStream)->GetConfig().isFeatureEnabled(enmFeature))
     374            return true;
     375        ++itStream;
     376    }
     377
     378    return false;
     379}
     380
     381bool CaptureContext::IsReady(void) const
     382{
     383    return this->fStarted;
    406384}
    407385
     
    410388 *
    411389 * @returns true if recording engine is ready, false if not.
    412  * @param   pCtx                Pointer to video recording context.
    413390 * @param   uScreen             Screen ID.
    414391 * @param   uTimeStampMs        Current time stamp (in ms). Currently not being used.
    415392 */
    416 bool VideoRecIsReady(PVIDEORECCONTEXT pCtx, uint32_t uScreen, uint64_t uTimeStampMs)
    417 {
    418     AssertPtrReturn(pCtx, false);
     393bool CaptureContext::IsReady(uint32_t uScreen, uint64_t uTimeStampMs) const
     394{
    419395    RT_NOREF(uTimeStampMs);
    420396
    421     if (ASMAtomicReadU32(&pCtx->enmState) != VIDEORECSTS_INITIALIZED)
     397    if (this->enmState != VIDEORECSTS_INITIALIZED)
    422398        return false;
    423399
    424400    bool fIsReady = false;
    425401
    426     PVIDEORECSTREAM pStream = VideoRecGetStream(pCtx, uScreen);
     402    const CaptureStream *pStream = GetStream(uScreen);
    427403    if (pStream)
    428     {
    429         VideoRecStreamLock(pStream);
    430         fIsReady = pStream->fEnabled;
    431         VideoRecStreamUnlock(pStream);
    432     }
     404        fIsReady = pStream->IsReady();
    433405
    434406    /* Note: Do not check for other constraints like the video FPS rate here,
     
    443415 *
    444416 * @returns true if active, false if not.
    445  * @param   pCtx                 Pointer to video recording context.
    446  */
    447 bool VideoRecIsStarted(PVIDEORECCONTEXT pCtx)
    448 {
    449     if (!pCtx)
    450         return false;
    451 
    452     return ASMAtomicReadBool(&pCtx->fStarted);
     417 */
     418bool CaptureContext::IsStarted(void) const
     419{
     420    return this->fStarted;
    453421}
    454422
     
    457425 *
    458426 * @returns true if any limit has been reached.
    459  * @param   pCtx                Pointer to video recording context.
    460427 * @param   uScreen             Screen ID.
    461428 * @param   tsNowMs             Current time stamp (in ms).
    462429 */
    463 bool VideoRecIsLimitReached(PVIDEORECCONTEXT pCtx, uint32_t uScreen, uint64_t tsNowMs)
    464 {
    465     PVIDEORECSTREAM pStream = VideoRecGetStream(pCtx, uScreen);
     430bool CaptureContext::IsLimitReached(uint32_t uScreen, uint64_t tsNowMs) const
     431{
     432    const CaptureStream *pStream = GetStream(uScreen);
    466433    if (   !pStream
    467         || !pStream->fEnabled)
    468     {
    469         return false;
    470     }
    471 
    472     const PVIDEORECCFG pCfg = &pCtx->Cfg;
    473 
    474     if (   pCfg->uMaxTimeS
    475         && tsNowMs >= pCtx->tsStartMs + (pCfg->uMaxTimeS * RT_MS_1SEC))
     434        || pStream->IsLimitReached(tsNowMs))
    476435    {
    477436        return true;
    478437    }
    479438
    480     if (pCfg->enmDst == VIDEORECDEST_FILE)
    481     {
    482 
    483         if (pCfg->File.uMaxSizeMB)
    484         {
    485             uint64_t sizeInMB = pStream->File.pWEBM->GetFileSize() / _1M;
    486             if(sizeInMB >= pCfg->File.uMaxSizeMB)
    487                 return true;
    488         }
    489 
    490         /* Check for available free disk space */
    491         if (   pStream->File.pWEBM
    492             && pStream->File.pWEBM->GetAvailableSpace() < 0x100000) /** @todo r=andy WTF? Fix this. */
    493         {
    494             LogRel(("VideoRec: Not enough free storage space available, stopping video capture\n"));
    495             return true;
    496         }
    497     }
    498 
    499439    return false;
    500440}
     
    506446 *
    507447 * @returns IPRT status code.
    508  * @param   pCtx                Pointer to the video recording context.
    509448 * @param   pvData              Audio frame data to send.
    510449 * @param   cbData              Size (in bytes) of (encoded) audio frame data.
    511450 * @param   uTimeStampMs        Time stamp (in ms) of audio playback.
    512451 */
    513 int VideoRecSendAudioFrame(PVIDEORECCONTEXT pCtx, const void *pvData, size_t cbData, uint64_t uTimeStampMs)
     452int CaptureContext::SendAudioFrame(const void *pvData, size_t cbData, uint64_t uTimeStampMs)
    514453{
    515454#ifdef VBOX_WITH_AUDIO_VIDEOREC
     
    537476    pBlock->pvData       = pFrame;
    538477    pBlock->cbData       = sizeof(VIDEORECAUDIOFRAME) + cbData;
    539     pBlock->cRefs        = (uint16_t)pCtx->vecStreams.size(); /* All streams need the same audio data. */
     478    pBlock->cRefs        = (uint16_t)this->vecStreams.size(); /* All streams need the same audio data. */
    540479    pBlock->uTimeStampMs = uTimeStampMs;
    541480
    542     int rc = RTCritSectEnter(&pCtx->CritSect);
     481    int rc = RTCritSectEnter(&this->CritSect);
    543482    if (RT_FAILURE(rc))
    544483        return rc;
     
    546485    try
    547486    {
    548         VideoRecBlockMap::iterator itBlocks = pCtx->mapBlocksCommon.find(uTimeStampMs);
    549         if (itBlocks == pCtx->mapBlocksCommon.end())
    550         {
    551             VideoRecBlocks *pVideoRecBlocks = new VideoRecBlocks();
     487        VideoRecBlockMap::iterator itBlocks = this->mapBlocksCommon.find(uTimeStampMs);
     488        if (itBlocks == this->mapBlocksCommon.end())
     489        {
     490            CaptureBlocks *pVideoRecBlocks = new CaptureBlocks();
    552491            pVideoRecBlocks->List.push_back(pBlock);
    553492
    554             pCtx->mapBlocksCommon.insert(std::make_pair(uTimeStampMs, pVideoRecBlocks));
     493            this->mapBlocksCommon.insert(std::make_pair(uTimeStampMs, pVideoRecBlocks));
    555494        }
    556495        else
     
    563502    }
    564503
    565     int rc2 = RTCritSectLeave(&pCtx->CritSect);
     504    int rc2 = RTCritSectLeave(&this->CritSect);
    566505    AssertRC(rc2);
    567506
    568507    if (RT_SUCCESS(rc))
    569         rc = videoRecThreadNotify(pCtx);
     508        rc = threadNotify();
    570509
    571510    return rc;
     
    583522 *
    584523 * @returns IPRT status code.
    585  * @param   pCtx               Pointer to the video recording context.
    586  * @param   uScreen            Screen number.
     524 * @param   uScreen            Screen number to send video frame to.
    587525 * @param   x                  Starting x coordinate of the video frame.
    588526 * @param   y                  Starting y coordinate of the video frame.
     
    595533 * @param   uTimeStampMs       Time stamp (in ms).
    596534 */
    597 int VideoRecSendVideoFrame(PVIDEORECCONTEXT pCtx, uint32_t uScreen, uint32_t x, uint32_t y,
    598                            uint32_t uPixelFormat, uint32_t uBPP, uint32_t uBytesPerLine,
    599                            uint32_t uSrcWidth, uint32_t uSrcHeight, uint8_t *puSrcData,
    600                            uint64_t uTimeStampMs)
    601 {
    602     AssertPtrReturn(pCtx,    VERR_INVALID_POINTER);
     535int CaptureContext::SendVideoFrame(uint32_t uScreen, uint32_t x, uint32_t y,
     536                                   uint32_t uPixelFormat, uint32_t uBPP, uint32_t uBytesPerLine,
     537                                   uint32_t uSrcWidth, uint32_t uSrcHeight, uint8_t *puSrcData,
     538                                   uint64_t uTimeStampMs)
     539{
    603540    AssertReturn(uSrcWidth,  VERR_INVALID_PARAMETER);
    604541    AssertReturn(uSrcHeight, VERR_INVALID_PARAMETER);
    605542    AssertReturn(puSrcData,  VERR_INVALID_POINTER);
    606543
    607     int rc = RTCritSectEnter(&pCtx->CritSect);
     544    int rc = RTCritSectEnter(&this->CritSect);
    608545    AssertRC(rc);
    609546
    610     PVIDEORECSTREAM pStream = VideoRecGetStream(pCtx, uScreen);
     547    CaptureStream *pStream = GetStream(uScreen);
    611548    if (!pStream)
    612549    {
    613         rc = RTCritSectLeave(&pCtx->CritSect);
     550        rc = RTCritSectLeave(&this->CritSect);
    614551        AssertRC(rc);
    615552
     
    617554    }
    618555
    619     VideoRecStreamLock(pStream);
    620 
    621     PVIDEORECVIDEOFRAME pFrame = NULL;
    622 
    623     do
    624     {
    625         if (!pStream->fEnabled)
    626         {
    627             rc = VINF_TRY_AGAIN; /* Not (yet) enabled. */
    628             break;
    629         }
    630 
    631         if (uTimeStampMs < pStream->Video.uLastTimeStampMs + pStream->Video.uDelayMs)
    632         {
    633             rc = VINF_TRY_AGAIN; /* Respect maximum frames per second. */
    634             break;
    635         }
    636 
    637         pStream->Video.uLastTimeStampMs = uTimeStampMs;
    638 
    639         int xDiff = ((int)pStream->Video.uWidth - (int)uSrcWidth) / 2;
    640         uint32_t w = uSrcWidth;
    641         if ((int)w + xDiff + (int)x <= 0)  /* Nothing visible. */
    642         {
    643             rc = VERR_INVALID_PARAMETER;
    644             break;
    645         }
    646 
    647         uint32_t destX;
    648         if ((int)x < -xDiff)
    649         {
    650             w += xDiff + x;
    651             x = -xDiff;
    652             destX = 0;
    653         }
    654         else
    655             destX = x + xDiff;
    656 
    657         uint32_t h = uSrcHeight;
    658         int yDiff = ((int)pStream->Video.uHeight - (int)uSrcHeight) / 2;
    659         if ((int)h + yDiff + (int)y <= 0)  /* Nothing visible. */
    660         {
    661             rc = VERR_INVALID_PARAMETER;
    662             break;
    663         }
    664 
    665         uint32_t destY;
    666         if ((int)y < -yDiff)
    667         {
    668             h += yDiff + (int)y;
    669             y = -yDiff;
    670             destY = 0;
    671         }
    672         else
    673             destY = y + yDiff;
    674 
    675         if (   destX > pStream->Video.uWidth
    676             || destY > pStream->Video.uHeight)
    677         {
    678             rc = VERR_INVALID_PARAMETER;  /* Nothing visible. */
    679             break;
    680         }
    681 
    682         if (destX + w > pStream->Video.uWidth)
    683             w = pStream->Video.uWidth - destX;
    684 
    685         if (destY + h > pStream->Video.uHeight)
    686             h = pStream->Video.uHeight - destY;
    687 
    688         pFrame = (PVIDEORECVIDEOFRAME)RTMemAllocZ(sizeof(VIDEORECVIDEOFRAME));
    689         AssertBreakStmt(pFrame, rc = VERR_NO_MEMORY);
    690 
    691         /* Calculate bytes per pixel and set pixel format. */
    692         const unsigned uBytesPerPixel = uBPP / 8;
    693         if (uPixelFormat == BitmapFormat_BGR)
    694         {
    695             switch (uBPP)
    696             {
    697                 case 32:
    698                     pFrame->uPixelFormat = VIDEORECPIXELFMT_RGB32;
    699                     break;
    700                 case 24:
    701                     pFrame->uPixelFormat = VIDEORECPIXELFMT_RGB24;
    702                     break;
    703                 case 16:
    704                     pFrame->uPixelFormat = VIDEORECPIXELFMT_RGB565;
    705                     break;
    706                 default:
    707                     AssertMsgFailed(("Unknown color depth (%RU32)\n", uBPP));
    708                     break;
    709             }
    710         }
    711         else
    712             AssertMsgFailed(("Unknown pixel format (%RU32)\n", uPixelFormat));
    713 
    714         const size_t cbRGBBuf =   pStream->Video.uWidth
    715                                 * pStream->Video.uHeight
    716                                 * uBytesPerPixel;
    717         AssertBreakStmt(cbRGBBuf, rc = VERR_INVALID_PARAMETER);
    718 
    719         pFrame->pu8RGBBuf = (uint8_t *)RTMemAlloc(cbRGBBuf);
    720         AssertBreakStmt(pFrame->pu8RGBBuf, rc = VERR_NO_MEMORY);
    721         pFrame->cbRGBBuf  = cbRGBBuf;
    722         pFrame->uWidth    = uSrcWidth;
    723         pFrame->uHeight   = uSrcHeight;
    724 
    725         /* If the current video frame is smaller than video resolution we're going to encode,
    726          * clear the frame beforehand to prevent artifacts. */
    727         if (   uSrcWidth  < pStream->Video.uWidth
    728             || uSrcHeight < pStream->Video.uHeight)
    729         {
    730             RT_BZERO(pFrame->pu8RGBBuf, pFrame->cbRGBBuf);
    731         }
    732 
    733         /* Calculate start offset in source and destination buffers. */
    734         uint32_t offSrc = y * uBytesPerLine + x * uBytesPerPixel;
    735         uint32_t offDst = (destY * pStream->Video.uWidth + destX) * uBytesPerPixel;
    736 
    737 #ifdef VBOX_VIDEOREC_DUMP
    738         VIDEORECBMPHDR bmpHdr;
    739         RT_ZERO(bmpHdr);
    740 
    741         VIDEORECBMPDIBHDR bmpDIBHdr;
    742         RT_ZERO(bmpDIBHdr);
    743 
    744         bmpHdr.u16Magic   = 0x4d42; /* Magic */
    745         bmpHdr.u32Size    = (uint32_t)(sizeof(VIDEORECBMPHDR) + sizeof(VIDEORECBMPDIBHDR) + (w * h * uBytesPerPixel));
    746         bmpHdr.u32OffBits = (uint32_t)(sizeof(VIDEORECBMPHDR) + sizeof(VIDEORECBMPDIBHDR));
    747 
    748         bmpDIBHdr.u32Size          = sizeof(VIDEORECBMPDIBHDR);
    749         bmpDIBHdr.u32Width         = w;
    750         bmpDIBHdr.u32Height        = h;
    751         bmpDIBHdr.u16Planes        = 1;
    752         bmpDIBHdr.u16BitCount      = uBPP;
    753         bmpDIBHdr.u32XPelsPerMeter = 5000;
    754         bmpDIBHdr.u32YPelsPerMeter = 5000;
    755 
    756         char szFileName[RTPATH_MAX];
    757         RTStrPrintf2(szFileName, sizeof(szFileName), "/tmp/VideoRecFrame-%RU32.bmp", uScreen);
    758 
    759         RTFILE fh;
    760         int rc2 = RTFileOpen(&fh, szFileName,
    761                              RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
    762         if (RT_SUCCESS(rc2))
    763         {
    764             RTFileWrite(fh, &bmpHdr,    sizeof(bmpHdr),    NULL);
    765             RTFileWrite(fh, &bmpDIBHdr, sizeof(bmpDIBHdr), NULL);
    766         }
    767 #endif
    768         Assert(pFrame->cbRGBBuf >= w * h * uBytesPerPixel);
    769 
    770         /* Do the copy. */
    771         for (unsigned int i = 0; i < h; i++)
    772         {
    773             /* Overflow check. */
    774             Assert(offSrc + w * uBytesPerPixel <= uSrcHeight * uBytesPerLine);
    775             Assert(offDst + w * uBytesPerPixel <= pStream->Video.uHeight * pStream->Video.uWidth * uBytesPerPixel);
    776 
    777             memcpy(pFrame->pu8RGBBuf + offDst, puSrcData + offSrc, w * uBytesPerPixel);
    778 
    779 #ifdef VBOX_VIDEOREC_DUMP
    780             if (RT_SUCCESS(rc2))
    781                 RTFileWrite(fh, pFrame->pu8RGBBuf + offDst, w * uBytesPerPixel, NULL);
    782 #endif
    783             offSrc += uBytesPerLine;
    784             offDst += pStream->Video.uWidth * uBytesPerPixel;
    785         }
    786 
    787 #ifdef VBOX_VIDEOREC_DUMP
    788         if (RT_SUCCESS(rc2))
    789             RTFileClose(fh);
    790 #endif
    791 
    792     } while (0);
    793 
    794     if (rc == VINF_SUCCESS) /* Note: Also could be VINF_TRY_AGAIN. */
    795     {
    796         PVIDEORECBLOCK pBlock = (PVIDEORECBLOCK)RTMemAlloc(sizeof(VIDEORECBLOCK));
    797         if (pBlock)
    798         {
    799             AssertPtr(pFrame);
    800 
    801             pBlock->enmType = VIDEORECBLOCKTYPE_VIDEO;
    802             pBlock->pvData  = pFrame;
    803             pBlock->cbData  = sizeof(VIDEORECVIDEOFRAME) + pFrame->cbRGBBuf;
    804 
    805             try
    806             {
    807                 VideoRecBlocks *pVideoRecBlocks = new VideoRecBlocks();
    808                 pVideoRecBlocks->List.push_back(pBlock);
    809 
    810                 Assert(pStream->Blocks.Map.find(uTimeStampMs) == pStream->Blocks.Map.end());
    811                 pStream->Blocks.Map.insert(std::make_pair(uTimeStampMs, pVideoRecBlocks));
    812             }
    813             catch (const std::exception &ex)
    814             {
    815                 RT_NOREF(ex);
    816 
    817                 RTMemFree(pBlock);
    818                 rc = VERR_NO_MEMORY;
    819             }
    820         }
    821         else
    822             rc = VERR_NO_MEMORY;
    823     }
    824 
    825     if (RT_FAILURE(rc))
    826         VideoRecVideoFrameFree(pFrame);
    827 
    828     VideoRecStreamUnlock(pStream);
    829 
    830     int rc2 = RTCritSectLeave(&pCtx->CritSect);
     556    rc = pStream->SendVideoFrame(x, y, uPixelFormat, uBPP, uBytesPerLine, uSrcWidth, uSrcHeight, puSrcData, uTimeStampMs);
     557
     558    int rc2 = RTCritSectLeave(&this->CritSect);
    831559    AssertRC(rc2);
    832560
     
    834562        && rc != VINF_TRY_AGAIN) /* Only signal the thread if operation was successful. */
    835563    {
    836         videoRecThreadNotify(pCtx);
     564        threadNotify();
    837565    }
    838566
  • trunk/src/VBox/Main/src-client/VideoRecStream.cpp

    r75069 r75251  
    4242
    4343
    44 /**
    45  * Locks a recording stream.
    46  *
    47  * @param   pStream             Recording stream to lock.
    48  */
    49 void VideoRecStreamLock(PVIDEORECSTREAM pStream)
    50 {
    51     int rc = RTCritSectEnter(&pStream->CritSect);
    52     AssertRC(rc);
    53 }
    54 
    55 /**
    56  * Unlocks a locked recording stream.
    57  *
    58  * @param   pStream             Recording stream to unlock.
    59  */
    60 void VideoRecStreamUnlock(PVIDEORECSTREAM pStream)
    61 {
    62     int rc = RTCritSectLeave(&pStream->CritSect);
    63     AssertRC(rc);
     44CaptureStream::CaptureStream(void)
     45    : tsStartMs(0)
     46{
     47}
     48
     49CaptureStream::CaptureStream(uint32_t a_uScreen, const settings::CaptureScreenSettings &a_Settings)
     50    : tsStartMs(0)
     51{
     52    int rc2 = initInternal(a_uScreen, a_Settings);
     53    if (RT_FAILURE(rc2))
     54        throw rc2;
     55}
     56
     57CaptureStream::~CaptureStream(void)
     58{
     59    int rc2 = uninitInternal();
     60    AssertRC(rc2);
    6461}
    6562
     
    6865 *
    6966 * @returns IPRT status code.
    70  * @param   pStream             Recording stream to open.
    71  * @param   pCfg                Recording configuration to use.
    72  */
    73 int VideoRecStreamOpen(PVIDEORECSTREAM pStream, PVIDEORECCFG pCfg)
    74 {
    75     AssertPtrReturn(pStream, VERR_INVALID_POINTER);
    76     AssertPtrReturn(pCfg,    VERR_INVALID_POINTER);
    77 
    78     Assert(pStream->enmDst == VIDEORECDEST_INVALID);
     67 */
     68int CaptureStream::open(void)
     69{
     70    Assert(Settings.enmDest == CaptureDestination_None);
    7971
    8072    int rc;
    8173
    82     switch (pCfg->enmDst)
    83     {
    84         case VIDEORECDEST_FILE:
    85         {
    86             Assert(pCfg->File.strName.isNotEmpty());
    87 
    88             char *pszAbsPath = RTPathAbsDup(com::Utf8Str(pCfg->File.strName).c_str());
     74    switch (Settings.enmDest)
     75    {
     76        case CaptureDestination_File:
     77        {
     78            Assert(Settings.File.strName.isNotEmpty());
     79
     80            char *pszAbsPath = RTPathAbsDup(Settings.File.strName.c_str());
    8981            AssertPtrReturn(pszAbsPath, VERR_NO_MEMORY);
    9082
     
    10193            char *pszFile = NULL;
    10294
    103             if (pCfg->aScreens.size() > 1)
    104                 rc = RTStrAPrintf(&pszFile, "%s-%u%s", pszAbsPath, pStream->uScreenID + 1, pszSuff);
     95            if (this->uScreenID > 0)
     96                rc = RTStrAPrintf(&pszFile, "%s-%u%s", pszAbsPath, this->uScreenID + 1, pszSuff);
    10597            else
    10698                rc = RTStrAPrintf(&pszFile, "%s%s", pszAbsPath, pszSuff);
     
    127119                    RTTimeExplode(&time, &ts);
    128120
    129                     if (pCfg->aScreens.size() > 1)
     121                    if (this->uScreenID > 0)
    130122                        rc = RTStrAPrintf(&pszFile, "%s-%04d-%02u-%02uT%02u-%02u-%02u-%09uZ-%u%s",
    131123                                          pszAbsPath, time.i32Year, time.u8Month, time.u8MonthDay,
    132124                                          time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond,
    133                                           pStream->uScreenID + 1, pszSuff);
     125                                          this->uScreenID + 1, pszSuff);
    134126                    else
    135127                        rc = RTStrAPrintf(&pszFile, "%s-%04d-%02u-%02uT%02u-%02u-%02u-%09uZ%s",
     
    144136                if (RT_SUCCESS(rc))
    145137                {
    146                     pStream->enmDst       = VIDEORECDEST_FILE;
    147                     pStream->File.hFile   = hFile;
    148                     pStream->File.pszFile = pszFile; /* Assign allocated string to our stream's config. */
     138                    this->File.hFile   = hFile;
     139                    this->File.strName = pszFile;
    149140                }
    150141            }
     
    156147            {
    157148                LogRel(("VideoRec: Failed to open file '%s' for screen %RU32, rc=%Rrc\n",
    158                         pszFile ? pszFile : "<Unnamed>", pStream->uScreenID, rc));
    159                 RTStrFree(pszFile);
    160             }
    161 
     149                        pszFile ? pszFile : "<Unnamed>", this->uScreenID, rc));
     150            }
     151
     152            RTStrFree(pszFile);
    162153            break;
    163154        }
     
    170161    LogFlowFuncLeaveRC(rc);
    171162    return rc;
     163}
     164
     165int CaptureStream::parseOptionsString(const com::Utf8Str &strOptions)
     166{
     167    size_t pos = 0;
     168    com::Utf8Str key, value;
     169    while ((pos = strOptions.parseKeyValue(key, value, pos)) != com::Utf8Str::npos)
     170    {
     171        if (key.compare("vc_quality", Utf8Str::CaseInsensitive) == 0)
     172        {
     173#ifdef VBOX_WITH_LIBVPX
     174            Assert(this->Settings.Video.ulFPS);
     175            if (value.compare("realtime", Utf8Str::CaseInsensitive) == 0)
     176                this->Video.Codec.VPX.uEncoderDeadline = VPX_DL_REALTIME;
     177            else if (value.compare("good", Utf8Str::CaseInsensitive) == 0)
     178                this->Video.Codec.VPX.uEncoderDeadline = 1000000 / this->Settings.Video.ulFPS;
     179            else if (value.compare("best", Utf8Str::CaseInsensitive) == 0)
     180                this->Video.Codec.VPX.uEncoderDeadline = VPX_DL_BEST_QUALITY;
     181            else
     182            {
     183                LogRel(("VideoRec: Setting encoder deadline to '%s'\n", value.c_str()));
     184                this->Video.Codec.VPX.uEncoderDeadline = value.toUInt32();
     185#endif
     186            }
     187        }
     188        else if (key.compare("vc_enabled", Utf8Str::CaseInsensitive) == 0)
     189        {
     190            if (value.compare("false", Utf8Str::CaseInsensitive) == 0)
     191            {
     192                this->Settings.featureMap[CaptureFeature_Video] = false;
     193#ifdef VBOX_WITH_AUDIO_VIDEOREC
     194                LogRel(("VideoRec: Only audio will be recorded\n"));
     195#endif
     196            }
     197        }
     198        else if (key.compare("ac_enabled", Utf8Str::CaseInsensitive) == 0)
     199        {
     200#ifdef VBOX_WITH_AUDIO_VIDEOREC
     201            if (value.compare("true", Utf8Str::CaseInsensitive) == 0)
     202            {
     203                this->Settings.featureMap[CaptureFeature_Audio] = true;
     204            }
     205            else
     206                LogRel(("VideoRec: Only video will be recorded\n"));
     207#endif
     208        }
     209        else if (key.compare("ac_profile", Utf8Str::CaseInsensitive) == 0)
     210        {
     211#ifdef VBOX_WITH_AUDIO_VIDEOREC
     212            if (value.compare("low", Utf8Str::CaseInsensitive) == 0)
     213            {
     214                this->Settings.Audio.uHz       = 8000;
     215                this->Settings.Audio.cBits     = 16;
     216                this->Settings.Audio.cChannels = 1;
     217            }
     218            else if (value.startsWith("med" /* "med[ium]" */, Utf8Str::CaseInsensitive) == 0)
     219            {
     220                /* Stay with the default set above. */
     221            }
     222            else if (value.compare("high", Utf8Str::CaseInsensitive) == 0)
     223            {
     224                this->Settings.Audio.uHz       = 48000;
     225                this->Settings.Audio.cBits     = 16;
     226                this->Settings.Audio.cChannels = 2;
     227            }
     228#endif
     229        }
     230        else
     231            LogRel(("VideoRec: Unknown option '%s' (value '%s'), skipping\n", key.c_str(), value.c_str()));
     232
     233    } /* while */
     234
     235    return VINF_SUCCESS;
     236}
     237
     238const settings::CaptureScreenSettings &CaptureStream::GetConfig(void) const
     239{
     240    return this->Settings;
     241}
     242
     243/**
     244 * Checks if a specified limit for recording has been reached.
     245 *
     246 * @returns true if any limit has been reached.
     247 * @param   tsNowMs             Current time stamp (in ms).
     248 */
     249bool CaptureStream::IsLimitReached(uint64_t tsNowMs) const
     250{
     251    if (!IsReady())
     252        return true;
     253
     254    if (   Settings.ulMaxTimeS
     255        && tsNowMs >= this->tsStartMs + (Settings.ulMaxTimeS * RT_MS_1SEC))
     256    {
     257        return true;
     258    }
     259
     260    if (Settings.enmDest == CaptureDestination_File)
     261    {
     262
     263        if (Settings.File.ulMaxSizeMB)
     264        {
     265            uint64_t sizeInMB = this->File.pWEBM->GetFileSize() / _1M;
     266            if(sizeInMB >= Settings.File.ulMaxSizeMB)
     267                return true;
     268        }
     269
     270        /* Check for available free disk space */
     271        if (   this->File.pWEBM
     272            && this->File.pWEBM->GetAvailableSpace() < 0x100000) /** @todo r=andy WTF? Fix this. */
     273        {
     274            LogRel(("VideoRec: Not enough free storage space available, stopping video capture\n"));
     275            return true;
     276        }
     277    }
     278
     279    return false;
     280}
     281
     282bool CaptureStream::IsReady(void) const
     283{
     284    return this->fEnabled;
    172285}
    173286
     
    178291 *
    179292 * @returns IPRT status code.
    180  * @param   pStream             Recording stream to process.
    181  */
    182 int VideoRecStreamProcess(PVIDEORECSTREAM pStream)
    183 {
    184     AssertPtrReturn(pStream, VERR_INVALID_POINTER);
    185 
    186     VideoRecStreamLock(pStream);
    187 
    188     if (!pStream->fEnabled)
    189     {
    190         VideoRecStreamUnlock(pStream);
     293 * @param   mapBlocksCommon     Map of common block to process for this stream.
     294 */
     295int CaptureStream::Process(VideoRecBlockMap &mapBlocksCommon)
     296{
     297    lock();
     298
     299    if (!Settings.fEnabled)
     300    {
     301        unlock();
    191302        return VINF_SUCCESS;
    192303    }
     
    194305    int rc = VINF_SUCCESS;
    195306
    196     const PVIDEORECCONTEXT pCtx = pStream->pCtx;
    197     AssertPtr(pCtx);
    198 
    199     VideoRecBlockMap::iterator itStreamBlocks = pStream->Blocks.Map.begin();
    200     while (itStreamBlocks != pStream->Blocks.Map.end())
     307    VideoRecBlockMap::iterator itStreamBlocks = Blocks.Map.begin();
     308    while (itStreamBlocks != Blocks.Map.end())
    201309    {
    202310        const uint64_t        uTimeStampMs = itStreamBlocks->first;
    203               VideoRecBlocks *pBlocks      = itStreamBlocks->second;
     311              CaptureBlocks *pBlocks       = itStreamBlocks->second;
    204312
    205313        AssertPtr(pBlocks);
     
    217325                rc = videoRecRGBToYUV(pVideoFrame->uPixelFormat,
    218326                                      /* Destination */
    219                                       pStream->Video.Codec.VPX.pu8YuvBuf, pVideoFrame->uWidth, pVideoFrame->uHeight,
     327                                      this->Video.Codec.VPX.pu8YuvBuf, pVideoFrame->uWidth, pVideoFrame->uHeight,
    220328                                      /* Source */
    221                                       pVideoFrame->pu8RGBBuf, pStream->Video.uWidth, pStream->Video.uHeight);
     329                                      pVideoFrame->pu8RGBBuf, this->Settings.Video.ulWidth, this->Settings.Video.ulHeight);
    222330                if (RT_SUCCESS(rc))
    223331                {
    224                     rc = VideoRecStreamWriteVideoVPX(pStream, uTimeStampMs, pVideoFrame);
     332                    rc = writeVideoVPX(uTimeStampMs, pVideoFrame);
    225333                }
    226334                else
     
    238346
    239347#ifdef VBOX_WITH_AUDIO_VIDEOREC
     348    AssertPtr(pCtx);
     349
    240350    /* As each (enabled) screen has to get the same audio data, look for common (audio) data which needs to be
    241351     * written to the screen's assigned recording stream. */
    242     VideoRecBlockMap::iterator itCommonBlocks = pCtx->mapBlocksCommon.begin();
    243     while (itCommonBlocks != pCtx->mapBlocksCommon.end())
     352    VideoRecBlockMap::iterator itCommonBlocks = mapBlocksCommon.begin();
     353    while (itCommonBlocks != mapBlocksCommon.end())
    244354    {
    245355        VideoRecBlockList::iterator itBlock = itCommonBlocks->second->List.begin();
     
    258368                    WebMWriter::BlockData_Opus blockData = { pAudioFrame->pvBuf, pAudioFrame->cbBuf,
    259369                                                             pBlockCommon->uTimeStampMs };
    260                     AssertPtr(pStream->File.pWEBM);
    261                     rc = pStream->File.pWEBM->WriteBlock(pStream->uTrackAudio, &blockData, sizeof(blockData));
     370                    AssertPtr(this->File.pWEBM);
     371                    rc = this->File.pWEBM->WriteBlock(this->uTrackAudio, &blockData, sizeof(blockData));
    262372                    break;
    263373                }
     
    287397        {
    288398            delete itCommonBlocks->second;
    289             pCtx->mapBlocksCommon.erase(itCommonBlocks);
    290             itCommonBlocks = pCtx->mapBlocksCommon.begin();
     399            mapBlocksCommon.erase(itCommonBlocks);
     400            itCommonBlocks = mapBlocksCommon.begin();
    291401        }
    292402        else
    293403            ++itCommonBlocks;
    294404
    295         LogFunc(("Common blocks: %zu\n", pCtx->mapBlocksCommon.size()));
     405        LogFunc(("Common blocks: %zu\n", mapBlocksCommon.size()));
    296406
    297407        if (RT_FAILURE(rc))
     
    300410#endif
    301411
    302     VideoRecStreamUnlock(pStream);
     412    unlock();
    303413
    304414    return rc;
    305415}
    306416
     417int CaptureStream::SendVideoFrame(uint32_t x, uint32_t y, uint32_t uPixelFormat, uint32_t uBPP, uint32_t uBytesPerLine,
     418                                  uint32_t uSrcWidth, uint32_t uSrcHeight, uint8_t *puSrcData, uint64_t uTimeStampMs)
     419{
     420    lock();
     421
     422    PVIDEORECVIDEOFRAME pFrame = NULL;
     423
     424    int rc = VINF_SUCCESS;
     425
     426    do
     427    {
     428        if (!this->fEnabled)
     429        {
     430            rc = VINF_TRY_AGAIN; /* Not (yet) enabled. */
     431            break;
     432        }
     433
     434        if (uTimeStampMs < this->Video.uLastTimeStampMs + this->Video.uDelayMs)
     435        {
     436            rc = VINF_TRY_AGAIN; /* Respect maximum frames per second. */
     437            break;
     438        }
     439
     440        this->Video.uLastTimeStampMs = uTimeStampMs;
     441
     442        int xDiff = ((int)this->Settings.Video.ulWidth - (int)uSrcWidth) / 2;
     443        uint32_t w = uSrcWidth;
     444        if ((int)w + xDiff + (int)x <= 0)  /* Nothing visible. */
     445        {
     446            rc = VERR_INVALID_PARAMETER;
     447            break;
     448        }
     449
     450        uint32_t destX;
     451        if ((int)x < -xDiff)
     452        {
     453            w += xDiff + x;
     454            x = -xDiff;
     455            destX = 0;
     456        }
     457        else
     458            destX = x + xDiff;
     459
     460        uint32_t h = uSrcHeight;
     461        int yDiff = ((int)this->Settings.Video.ulHeight - (int)uSrcHeight) / 2;
     462        if ((int)h + yDiff + (int)y <= 0)  /* Nothing visible. */
     463        {
     464            rc = VERR_INVALID_PARAMETER;
     465            break;
     466        }
     467
     468        uint32_t destY;
     469        if ((int)y < -yDiff)
     470        {
     471            h += yDiff + (int)y;
     472            y = -yDiff;
     473            destY = 0;
     474        }
     475        else
     476            destY = y + yDiff;
     477
     478        if (   destX > this->Settings.Video.ulWidth
     479            || destY > this->Settings.Video.ulHeight)
     480        {
     481            rc = VERR_INVALID_PARAMETER;  /* Nothing visible. */
     482            break;
     483        }
     484
     485        if (destX + w > this->Settings.Video.ulWidth)
     486            w = this->Settings.Video.ulWidth - destX;
     487
     488        if (destY + h > this->Settings.Video.ulHeight)
     489            h = this->Settings.Video.ulHeight - destY;
     490
     491        pFrame = (PVIDEORECVIDEOFRAME)RTMemAllocZ(sizeof(VIDEORECVIDEOFRAME));
     492        AssertBreakStmt(pFrame, rc = VERR_NO_MEMORY);
     493
     494        /* Calculate bytes per pixel and set pixel format. */
     495        const unsigned uBytesPerPixel = uBPP / 8;
     496        if (uPixelFormat == BitmapFormat_BGR)
     497        {
     498            switch (uBPP)
     499            {
     500                case 32:
     501                    pFrame->uPixelFormat = VIDEORECPIXELFMT_RGB32;
     502                    break;
     503                case 24:
     504                    pFrame->uPixelFormat = VIDEORECPIXELFMT_RGB24;
     505                    break;
     506                case 16:
     507                    pFrame->uPixelFormat = VIDEORECPIXELFMT_RGB565;
     508                    break;
     509                default:
     510                    AssertMsgFailedBreakStmt(("Unknown color depth (%RU32)\n", uBPP), rc = VERR_NOT_SUPPORTED);
     511                    break;
     512            }
     513        }
     514        else
     515            AssertMsgFailedBreakStmt(("Unknown pixel format (%RU32)\n", uPixelFormat), rc = VERR_NOT_SUPPORTED);
     516
     517        const size_t cbRGBBuf =   this->Settings.Video.ulWidth
     518                                * this->Settings.Video.ulHeight
     519                                * uBytesPerPixel;
     520        AssertBreakStmt(cbRGBBuf, rc = VERR_INVALID_PARAMETER);
     521
     522        pFrame->pu8RGBBuf = (uint8_t *)RTMemAlloc(cbRGBBuf);
     523        AssertBreakStmt(pFrame->pu8RGBBuf, rc = VERR_NO_MEMORY);
     524        pFrame->cbRGBBuf  = cbRGBBuf;
     525        pFrame->uWidth    = uSrcWidth;
     526        pFrame->uHeight   = uSrcHeight;
     527
     528        /* If the current video frame is smaller than video resolution we're going to encode,
     529         * clear the frame beforehand to prevent artifacts. */
     530        if (   uSrcWidth  < this->Settings.Video.ulWidth
     531            || uSrcHeight < this->Settings.Video.ulHeight)
     532        {
     533            RT_BZERO(pFrame->pu8RGBBuf, pFrame->cbRGBBuf);
     534        }
     535
     536        /* Calculate start offset in source and destination buffers. */
     537        uint32_t offSrc = y * uBytesPerLine + x * uBytesPerPixel;
     538        uint32_t offDst = (destY * this->Settings.Video.ulWidth + destX) * uBytesPerPixel;
     539
     540#ifdef VBOX_VIDEOREC_DUMP
     541        VIDEORECBMPHDR bmpHdr;
     542        RT_ZERO(bmpHdr);
     543
     544        VIDEORECBMPDIBHDR bmpDIBHdr;
     545        RT_ZERO(bmpDIBHdr);
     546
     547        bmpHdr.u16Magic   = 0x4d42; /* Magic */
     548        bmpHdr.u32Size    = (uint32_t)(sizeof(VIDEORECBMPHDR) + sizeof(VIDEORECBMPDIBHDR) + (w * h * uBytesPerPixel));
     549        bmpHdr.u32OffBits = (uint32_t)(sizeof(VIDEORECBMPHDR) + sizeof(VIDEORECBMPDIBHDR));
     550
     551        bmpDIBHdr.u32Size          = sizeof(VIDEORECBMPDIBHDR);
     552        bmpDIBHdr.u32Width         = w;
     553        bmpDIBHdr.u32Height        = h;
     554        bmpDIBHdr.u16Planes        = 1;
     555        bmpDIBHdr.u16BitCount      = uBPP;
     556        bmpDIBHdr.u32XPelsPerMeter = 5000;
     557        bmpDIBHdr.u32YPelsPerMeter = 5000;
     558
     559        char szFileName[RTPATH_MAX];
     560        RTStrPrintf2(szFileName, sizeof(szFileName), "/tmp/VideoRecFrame-%RU32.bmp", this->uScreenID);
     561
     562        RTFILE fh;
     563        int rc2 = RTFileOpen(&fh, szFileName,
     564                             RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
     565        if (RT_SUCCESS(rc2))
     566        {
     567            RTFileWrite(fh, &bmpHdr,    sizeof(bmpHdr),    NULL);
     568            RTFileWrite(fh, &bmpDIBHdr, sizeof(bmpDIBHdr), NULL);
     569        }
     570#endif
     571        Assert(pFrame->cbRGBBuf >= w * h * uBytesPerPixel);
     572
     573        /* Do the copy. */
     574        for (unsigned int i = 0; i < h; i++)
     575        {
     576            /* Overflow check. */
     577            Assert(offSrc + w * uBytesPerPixel <= uSrcHeight * uBytesPerLine);
     578            Assert(offDst + w * uBytesPerPixel <= this->Settings.Video.ulHeight * this->Settings.Video.ulWidth * uBytesPerPixel);
     579
     580            memcpy(pFrame->pu8RGBBuf + offDst, puSrcData + offSrc, w * uBytesPerPixel);
     581
     582#ifdef VBOX_VIDEOREC_DUMP
     583            if (RT_SUCCESS(rc2))
     584                RTFileWrite(fh, pFrame->pu8RGBBuf + offDst, w * uBytesPerPixel, NULL);
     585#endif
     586            offSrc += uBytesPerLine;
     587            offDst += this->Settings.Video.ulWidth * uBytesPerPixel;
     588        }
     589
     590#ifdef VBOX_VIDEOREC_DUMP
     591        if (RT_SUCCESS(rc2))
     592            RTFileClose(fh);
     593#endif
     594
     595    } while (0);
     596
     597    if (rc == VINF_SUCCESS) /* Note: Also could be VINF_TRY_AGAIN. */
     598    {
     599        PVIDEORECBLOCK pBlock = (PVIDEORECBLOCK)RTMemAlloc(sizeof(VIDEORECBLOCK));
     600        if (pBlock)
     601        {
     602            AssertPtr(pFrame);
     603
     604            pBlock->enmType = VIDEORECBLOCKTYPE_VIDEO;
     605            pBlock->pvData  = pFrame;
     606            pBlock->cbData  = sizeof(VIDEORECVIDEOFRAME) + pFrame->cbRGBBuf;
     607
     608            try
     609            {
     610                CaptureBlocks *pVideoRecBlocks = new CaptureBlocks();
     611                pVideoRecBlocks->List.push_back(pBlock);
     612
     613                Assert(this->Blocks.Map.find(uTimeStampMs) == this->Blocks.Map.end());
     614                this->Blocks.Map.insert(std::make_pair(uTimeStampMs, pVideoRecBlocks));
     615            }
     616            catch (const std::exception &ex)
     617            {
     618                RT_NOREF(ex);
     619
     620                RTMemFree(pBlock);
     621                rc = VERR_NO_MEMORY;
     622            }
     623        }
     624        else
     625            rc = VERR_NO_MEMORY;
     626    }
     627
     628    if (RT_FAILURE(rc))
     629        VideoRecVideoFrameFree(pFrame);
     630
     631    lock();
     632
     633    return rc;
     634}
     635
     636int CaptureStream::Init(uint32_t a_uScreen, const settings::CaptureScreenSettings &a_Settings)
     637{
     638    return initInternal(a_uScreen, a_Settings);
     639}
     640
    307641/**
    308642 * Initializes a recording stream.
    309643 *
    310644 * @returns IPRT status code.
    311  * @param   pStream             Recording stream to initialize.
    312  * @param   pCtx                Recording context to use for initialization.
    313  * @param   uScreen             Screen number to record.
    314  */
    315 int VideoRecStreamInit(PVIDEORECSTREAM pStream, PVIDEORECCONTEXT pCtx, uint32_t uScreen)
    316 {
    317     AssertPtrReturn(pStream, VERR_INVALID_POINTER);
    318     AssertPtrReturn(pCtx,    VERR_INVALID_POINTER);
    319 
    320     PVIDEORECCFG pCfg = &pCtx->Cfg;
    321 
    322 #ifdef VBOX_WITH_AUDIO_VIDEOREC
    323     if (pCfg->Audio.fEnabled)
    324     {
    325         /* Sanity. */
    326         AssertReturn(pCfg->Audio.uHz,       VERR_INVALID_PARAMETER);
    327         AssertReturn(pCfg->Audio.cBits,     VERR_INVALID_PARAMETER);
    328         AssertReturn(pCfg->Audio.cChannels, VERR_INVALID_PARAMETER);
    329     }
    330 #endif
    331 
    332     int rc = VideoRecStreamOpen(pStream, pCfg);
     645 * @param   uScreen             Screen number to use for this recording stream.
     646 * @param   Cfg                 Recording screen configuration to use for initialization.
     647 */
     648int CaptureStream::initInternal(uint32_t a_uScreen, const settings::CaptureScreenSettings &a_Settings)
     649{
     650    int rc = parseOptionsString(a_Settings.strOptions);
    333651    if (RT_FAILURE(rc))
    334652        return rc;
    335653
    336     if (pCfg->Video.fEnabled)
    337         rc = VideoRecStreamInitVideo(pStream, pCfg);
    338 
    339     switch (pStream->enmDst)
    340     {
    341         case VIDEORECDEST_FILE:
    342         {
    343             rc = pStream->File.pWEBM->OpenEx(pStream->File.pszFile, &pStream->File.hFile,
     654    rc = RTCritSectInit(&this->CritSect);
     655    if (RT_FAILURE(rc))
     656        return rc;
     657
     658    rc = open();
     659    if (RT_FAILURE(rc))
     660        return rc;
     661
     662    const bool fVideoEnabled = a_Settings.isFeatureEnabled(CaptureFeature_Video);
     663    const bool fAudioEnabled = a_Settings.isFeatureEnabled(CaptureFeature_Audio);
     664
     665    if (fVideoEnabled)
     666        rc = initVideo();
     667
     668    if (fAudioEnabled)
     669        rc = initAudio();
     670
     671    switch (this->Settings.enmDest)
     672    {
     673        case CaptureDestination_File:
     674        {
     675            const char *pszFile = this->Settings.File.strName.c_str();
     676
     677            rc = File.pWEBM->OpenEx(pszFile, &this->File.hFile,
    344678#ifdef VBOX_WITH_AUDIO_VIDEOREC
    345                                              pCfg->Audio.fEnabled ? WebMWriter::AudioCodec_Opus : WebMWriter::AudioCodec_None,
     679                                      a_Settings.isFeatureEnabled(CaptureFeature_Audio)
     680                                    ? WebMWriter::AudioCodec_Opus : WebMWriter::AudioCodec_None,
    346681#else
    347                                              WebMWriter::AudioCodec_None,
    348 #endif
    349                                              pCfg->Video.fEnabled ? WebMWriter::VideoCodec_VP8 : WebMWriter::VideoCodec_None);
     682                                      WebMWriter::AudioCodec_None,
     683#endif
     684                                      a_Settings.isFeatureEnabled(CaptureFeature_Video)
     685                                    ? WebMWriter::VideoCodec_VP8 : WebMWriter::VideoCodec_None);
    350686            if (RT_FAILURE(rc))
    351687            {
    352                 LogRel(("VideoRec: Failed to create the capture output file '%s' (%Rrc)\n", pStream->File.pszFile, rc));
     688                LogRel(("VideoRec: Failed to create the capture output file '%s' (%Rrc)\n", pszFile, rc));
    353689                break;
    354690            }
    355691
    356             const char *pszFile = pStream->File.pszFile;
    357 
    358             if (pCfg->Video.fEnabled)
    359             {
    360                 rc = pStream->File.pWEBM->AddVideoTrack(pCfg->Video.uWidth, pCfg->Video.uHeight, pCfg->Video.uFPS,
    361                                                         &pStream->uTrackVideo);
     692            if (fVideoEnabled)
     693            {
     694                rc = this->File.pWEBM->AddVideoTrack(a_Settings.Video.ulWidth, a_Settings.Video.ulHeight,
     695                                                     a_Settings.Video.ulFPS, &this->uTrackVideo);
    362696                if (RT_FAILURE(rc))
    363697                {
     
    367701
    368702                LogRel(("VideoRec: Recording video of screen #%u with %RU32x%RU32 @ %RU32 kbps, %RU32 FPS (track #%RU8)\n",
    369                         uScreen, pCfg->Video.uWidth, pCfg->Video.uHeight, pCfg->Video.uRate, pCfg->Video.uFPS,
    370                         pStream->uTrackVideo));
     703                        this->uScreenID, a_Settings.Video.ulWidth, a_Settings.Video.ulHeight, a_Settings.Video.ulRate,
     704                        a_Settings.Video.ulFPS, this->uTrackVideo));
    371705            }
    372706
    373707#ifdef VBOX_WITH_AUDIO_VIDEOREC
    374             if (pCfg->Audio.fEnabled)
    375             {
    376                 rc = pStream->File.pWEBM->AddAudioTrack(pCfg->Audio.uHz, pCfg->Audio.cChannels, pCfg->Audio.cBits,
    377                                                         &pStream->uTrackAudio);
     708            if (fAudioEnabled)
     709            {
     710                rc = this->File.pWEBM->AddAudioTrack(a_Settings.Audio.uHz, a_Settings.Audio.cChannels, a_Settings.Audio.cBits,
     711                                                     &this->uTrackAudio);
    378712                if (RT_FAILURE(rc))
    379713                {
     
    383717
    384718                LogRel(("VideoRec: Recording audio in %RU16Hz, %RU8 bit, %RU8 %s (track #%RU8)\n",
    385                         pCfg->Audio.uHz, pCfg->Audio.cBits, pCfg->Audio.cChannels, pCfg->Audio.cChannels ? "channels" : "channel",
    386                         pStream->uTrackAudio));
    387             }
    388 #endif
    389 
    390             if (   pCfg->Video.fEnabled
     719                        a_Settings.Audio.uHz, a_Settings.Audio.cBits, a_Settings.Audio.cChannels,
     720                        a_Settings.Audio.cChannels ? "channels" : "channel", this->uTrackAudio));
     721            }
     722#endif
     723
     724            if (   fVideoEnabled
    391725#ifdef VBOX_WITH_AUDIO_VIDEOREC
    392                 || pCfg->Audio.fEnabled
     726                || fAudioEnabled
    393727#endif
    394728               )
    395729            {
    396730                char szWhat[32] = { 0 };
    397                 if (pCfg->Video.fEnabled)
     731                if (fVideoEnabled)
    398732                    RTStrCat(szWhat, sizeof(szWhat), "video");
    399733#ifdef VBOX_WITH_AUDIO_VIDEOREC
    400                 if (pCfg->Audio.fEnabled)
     734                if (fAudioEnabled)
    401735                {
    402                     if (pCfg->Video.fEnabled)
     736                    if (fVideoEnabled)
    403737                        RTStrCat(szWhat, sizeof(szWhat), " + ");
    404738                    RTStrCat(szWhat, sizeof(szWhat), "audio");
     
    419753    if (RT_SUCCESS(rc))
    420754    {
    421         pStream->pCtx     = pCtx;
    422         pStream->fEnabled = true;
     755        this->pCtx      = pCtx;
     756        this->fEnabled  = true;
     757        this->uScreenID = a_uScreen;
     758        this->tsStartMs = RTTimeMilliTS();
     759        this->Settings  = a_Settings;
    423760    }
    424761    else
    425762    {
    426         int rc2 = VideoRecStreamClose(pStream);
     763        int rc2 = uninitInternal();
    427764        AssertRC(rc2);
    428765        return rc;
     
    438775 *
    439776 * @returns IPRT status code.
    440  * @param   pStream             Recording stream to close.
    441  *
    442  */
    443 int VideoRecStreamClose(PVIDEORECSTREAM pStream)
     777 */
     778int CaptureStream::close(void)
    444779{
    445780    int rc = VINF_SUCCESS;
    446781
    447     if (pStream->fEnabled)
    448     {
    449         switch (pStream->enmDst)
    450         {
    451             case VIDEORECDEST_FILE:
    452             {
    453                 if (pStream->File.pWEBM)
    454                     rc = pStream->File.pWEBM->Close();
     782    if (this->fEnabled)
     783    {
     784        switch (this->Settings.enmDest)
     785        {
     786            case CaptureDestination_File:
     787            {
     788                if (this->File.pWEBM)
     789                    rc = this->File.pWEBM->Close();
    455790                break;
    456791            }
     
    461796        }
    462797
    463         pStream->Blocks.Clear();
    464 
    465         LogRel(("VideoRec: Recording screen #%u stopped\n", pStream->uScreenID));
     798        this->Blocks.Clear();
     799
     800        LogRel(("VideoRec: Recording screen #%u stopped\n", this->uScreenID));
    466801    }
    467802
    468803    if (RT_FAILURE(rc))
    469804    {
    470         LogRel(("VideoRec: Error stopping recording screen #%u, rc=%Rrc\n", pStream->uScreenID, rc));
     805        LogRel(("VideoRec: Error stopping recording screen #%u, rc=%Rrc\n", this->uScreenID, rc));
    471806        return rc;
    472807    }
    473808
    474     switch (pStream->enmDst)
    475     {
    476         case VIDEORECDEST_FILE:
    477         {
    478             AssertPtr(pStream->File.pszFile);
    479             if (RTFileIsValid(pStream->File.hFile))
    480             {
    481                 rc = RTFileClose(pStream->File.hFile);
     809    switch (this->Settings.enmDest)
     810    {
     811        case CaptureDestination_File:
     812        {
     813            if (RTFileIsValid(this->File.hFile))
     814            {
     815                rc = RTFileClose(this->File.hFile);
    482816                if (RT_SUCCESS(rc))
    483817                {
    484                     LogRel(("VideoRec: Closed file '%s'\n", pStream->File.pszFile));
     818                    LogRel(("VideoRec: Closed file '%s'\n", this->Settings.File.strName.c_str()));
    485819                }
    486820                else
    487821                {
    488                     LogRel(("VideoRec: Error closing file '%s', rc=%Rrc\n", pStream->File.pszFile, rc));
     822                    LogRel(("VideoRec: Error closing file '%s', rc=%Rrc\n", this->Settings.File.strName.c_str(), rc));
    489823                    break;
    490824                }
    491825            }
    492826
    493             RTStrFree(pStream->File.pszFile);
    494             pStream->File.pszFile = NULL;
    495 
    496             if (pStream->File.pWEBM)
    497             {
    498                 delete pStream->File.pWEBM;
    499                 pStream->File.pWEBM = NULL;
     827            if (this->File.pWEBM)
     828            {
     829                delete this->File.pWEBM;
     830                this->File.pWEBM = NULL;
    500831            }
    501832            break;
     
    507838    }
    508839
    509     if (RT_SUCCESS(rc))
    510     {
    511         pStream->enmDst = VIDEORECDEST_INVALID;
    512     }
    513 
    514840    LogFlowFuncLeaveRC(rc);
    515841    return rc;
     
    520846 *
    521847 * @returns IPRT status code.
    522  * @param   pStream             Recording stream to uninitialize.
    523  */
    524 int VideoRecStreamUninit(PVIDEORECSTREAM pStream)
    525 {
    526     int rc = VINF_SUCCESS;
    527 
    528     if (pStream->pCtx->Cfg.Video.fEnabled)
    529     {
    530         int rc2 = VideoRecStreamUnitVideo(pStream);
     848 */
     849int CaptureStream::Uninit(void)
     850{
     851    return uninitInternal();
     852}
     853
     854int CaptureStream::uninitInternal(void)
     855{
     856    int rc = close();
     857    if (RT_FAILURE(rc))
     858        return rc;
     859
     860    if (this->Settings.isFeatureEnabled(CaptureFeature_Video))
     861    {
     862        int rc2 = unitVideo();
    531863        if (RT_SUCCESS(rc))
    532864            rc = rc2;
    533865    }
    534866
     867    RTCritSectDelete(&this->CritSect);
     868
     869    this->fEnabled = false;
     870
    535871    return rc;
    536872}
     
    540876 *
    541877 * @returns IPRT status code.
    542  * @param   pStream             Recording stream to uninitialize video recording for.
    543  */
    544 int VideoRecStreamUnitVideo(PVIDEORECSTREAM pStream)
     878 */
     879int CaptureStream::unitVideo(void)
    545880{
    546881#ifdef VBOX_WITH_LIBVPX
    547882    /* At the moment we only have VPX. */
    548     return VideoRecStreamUninitVideoVPX(pStream);
     883    return uninitVideoVPX();
    549884#else
    550885    return VERR_NOT_SUPPORTED;
     
    557892 *
    558893 * @returns IPRT status code.
    559  * @param   pStream             Recording stream to uninitialize VPX codec for.
    560  */
    561 int VideoRecStreamUninitVideoVPX(PVIDEORECSTREAM pStream)
    562 {
    563     vpx_img_free(&pStream->Video.Codec.VPX.RawImage);
    564     vpx_codec_err_t rcv = vpx_codec_destroy(&pStream->Video.Codec.VPX.Ctx);
     894 */
     895int CaptureStream::uninitVideoVPX(void)
     896{
     897    vpx_img_free(&this->Video.Codec.VPX.RawImage);
     898    vpx_codec_err_t rcv = vpx_codec_destroy(&this->Video.Codec.VPX.Ctx);
    565899    Assert(rcv == VPX_CODEC_OK); RT_NOREF(rcv);
    566900
     
    573907 *
    574908 * @returns IPRT status code.
    575  * @param   pStream             Recording stream to initialize video recording for.
    576  * @param   pCfg                Video recording configuration to use for initialization.
    577  */
    578 int VideoRecStreamInitVideo(PVIDEORECSTREAM pStream, PVIDEORECCFG pCfg)
    579 {
     909 */
     910int CaptureStream::initVideo(void)
     911{
     912    /* Sanity. */
     913    AssertReturn(this->Settings.Video.ulRate,   VERR_INVALID_PARAMETER);
     914    AssertReturn(this->Settings.Video.ulWidth,  VERR_INVALID_PARAMETER);
     915    AssertReturn(this->Settings.Video.ulHeight, VERR_INVALID_PARAMETER);
     916    AssertReturn(this->Settings.Video.ulFPS,    VERR_INVALID_PARAMETER);
     917
     918    this->Video.cFailedEncodingFrames = 0;
     919    this->Video.uDelayMs = RT_MS_1SEC / this->Settings.Video.ulFPS;
     920
    580921#ifdef VBOX_WITH_LIBVPX
    581922    /* At the moment we only have VPX. */
    582     return VideoRecStreamInitVideoVPX(pStream, pCfg);
     923    return initVideoVPX();
    583924#else
    584     return VERR_NOT_SUPPORTED;
     925    return VINF_SUCCESS;
    585926#endif
    586927}
     
    591932 *
    592933 * @returns IPRT status code.
    593  * @param   pStream             Recording stream to initialize VPX codec for.
    594  * @param   pCfg                Video recording configuration to use for initialization.
    595  */
    596 int VideoRecStreamInitVideoVPX(PVIDEORECSTREAM pStream, PVIDEORECCFG pCfg)
    597 {
    598     pStream->Video.uWidth                = pCfg->Video.uWidth;
    599     pStream->Video.uHeight               = pCfg->Video.uHeight;
    600     pStream->Video.cFailedEncodingFrames = 0;
    601 
    602     PVIDEORECVIDEOCODEC pVC = &pStream->Video.Codec;
    603 
    604     pStream->Video.uDelayMs = RT_MS_1SEC / pCfg->Video.uFPS;
    605 
    606     pVC->enmType = VIDEORECVIDEOCODECTYPE_VP8; /** @todo Make this configurable. */
    607 
     934 */
     935int CaptureStream::initVideoVPX(void)
     936{
    608937# ifdef VBOX_WITH_LIBVPX_VP9
    609938    vpx_codec_iface_t *pCodecIface = vpx_codec_vp9_cx();
     
    612941# endif
    613942
    614     vpx_codec_err_t rcv = vpx_codec_enc_config_default(pCodecIface, &pVC->VPX.Cfg, 0 /* Reserved */);
     943    PVIDEORECVIDEOCODEC pCodec = &this->Video.Codec;
     944
     945    vpx_codec_err_t rcv = vpx_codec_enc_config_default(pCodecIface, &pCodec->VPX.Cfg, 0 /* Reserved */);
    615946    if (rcv != VPX_CODEC_OK)
    616947    {
     
    620951
    621952    /* Target bitrate in kilobits per second. */
    622     pVC->VPX.Cfg.rc_target_bitrate = pCfg->Video.uRate;
     953    pCodec->VPX.Cfg.rc_target_bitrate = this->Settings.Video.ulRate;
    623954    /* Frame width. */
    624     pVC->VPX.Cfg.g_w = pCfg->Video.uWidth;
     955    pCodec->VPX.Cfg.g_w = this->Settings.Video.ulWidth;
    625956    /* Frame height. */
    626     pVC->VPX.Cfg.g_h = pCfg->Video.uHeight;
     957    pCodec->VPX.Cfg.g_h = this->Settings.Video.ulHeight;
    627958    /* 1ms per frame. */
    628     pVC->VPX.Cfg.g_timebase.num = 1;
    629     pVC->VPX.Cfg.g_timebase.den = 1000;
     959    pCodec->VPX.Cfg.g_timebase.num = 1;
     960    pCodec->VPX.Cfg.g_timebase.den = 1000;
    630961    /* Disable multithreading. */
    631     pVC->VPX.Cfg.g_threads = 0;
     962    pCodec->VPX.Cfg.g_threads = 0;
    632963
    633964    /* Initialize codec. */
    634     rcv = vpx_codec_enc_init(&pVC->VPX.Ctx, pCodecIface, &pVC->VPX.Cfg, 0 /* Flags */);
     965    rcv = vpx_codec_enc_init(&pCodec->VPX.Ctx, pCodecIface, &pCodec->VPX.Cfg, 0 /* Flags */);
    635966    if (rcv != VPX_CODEC_OK)
    636967    {
     
    639970    }
    640971
    641     if (!vpx_img_alloc(&pVC->VPX.RawImage, VPX_IMG_FMT_I420, pCfg->Video.uWidth, pCfg->Video.uHeight, 1))
    642     {
    643         LogRel(("VideoRec: Failed to allocate image %RU32x%RU32\n", pCfg->Video.uWidth, pCfg->Video.uHeight));
     972    if (!vpx_img_alloc(&pCodec->VPX.RawImage, VPX_IMG_FMT_I420,
     973                       this->Settings.Video.ulWidth, this->Settings.Video.ulHeight, 1))
     974    {
     975        LogRel(("VideoRec: Failed to allocate image %RU32x%RU32\n",
     976                this->Settings.Video.ulWidth, this->Settings.Video.ulHeight));
    644977        return VERR_NO_MEMORY;
    645978    }
    646979
    647980    /* Save a pointer to the first raw YUV plane. */
    648     pStream->Video.Codec.VPX.pu8YuvBuf = pVC->VPX.RawImage.planes[0];
     981    pCodec->VPX.pu8YuvBuf = pCodec->VPX.RawImage.planes[0];
    649982
    650983    return VINF_SUCCESS;
     
    652985#endif
    653986
     987int CaptureStream::initAudio(void)
     988{
     989#ifdef VBOX_WITH_AUDIO_VIDEOREC
     990    if (this->Settings.isFeatureEnabled(CaptureFeature_Audio))
     991    {
     992        /* Sanity. */
     993        AssertReturn(this->Settings.Audio.uHz,       VERR_INVALID_PARAMETER);
     994        AssertReturn(this->Settings.Audio.cBits,     VERR_INVALID_PARAMETER);
     995        AssertReturn(this->Settings.Audio.cChannels, VERR_INVALID_PARAMETER);
     996    }
     997#endif
     998
     999    return VINF_SUCCESS;
     1000}
     1001
    6541002#ifdef VBOX_WITH_LIBVPX
    6551003/**
     
    6571005 *
    6581006 * @returns IPRT status code.
    659  * @param   pStream             Stream to encode and submit to.
    6601007 * @param   uTimeStampMs        Absolute timestamp (PTS) of frame (in ms) to encode.
    6611008 * @param   pFrame              Frame to encode and submit.
    6621009 */
    663 int VideoRecStreamWriteVideoVPX(PVIDEORECSTREAM pStream, uint64_t uTimeStampMs, PVIDEORECVIDEOFRAME pFrame)
    664 {
    665     AssertPtrReturn(pStream, VERR_INVALID_POINTER);
    666     AssertPtrReturn(pFrame,  VERR_INVALID_POINTER);
     1010int CaptureStream::writeVideoVPX(uint64_t uTimeStampMs, PVIDEORECVIDEOFRAME pFrame)
     1011{
     1012    AssertPtrReturn(pFrame, VERR_INVALID_POINTER);
    6671013
    6681014    int rc;
    6691015
    670     AssertPtr(pStream->pCtx);
    671     PVIDEORECCFG        pCfg = &pStream->pCtx->Cfg;
    672     PVIDEORECVIDEOCODEC pVC  = &pStream->Video.Codec;
     1016    PVIDEORECVIDEOCODEC pCodec = &this->Video.Codec;
    6731017
    6741018    /* Presentation Time Stamp (PTS). */
    6751019    vpx_codec_pts_t pts = uTimeStampMs;
    676     vpx_codec_err_t rcv = vpx_codec_encode(&pVC->VPX.Ctx,
    677                                            &pVC->VPX.RawImage,
    678                                            pts                                    /* Time stamp */,
    679                                            pStream->Video.uDelayMs                /* How long to show this frame */,
    680                                            0                                      /* Flags */,
    681                                            pCfg->Video.Codec.VPX.uEncoderDeadline /* Quality setting */);
     1020    vpx_codec_err_t rcv = vpx_codec_encode(&pCodec->VPX.Ctx,
     1021                                           &pCodec->VPX.RawImage,
     1022                                           pts                          /* Time stamp */,
     1023                                           this->Video.uDelayMs         /* How long to show this frame */,
     1024                                           0                            /* Flags */,
     1025                                           pCodec->VPX.uEncoderDeadline /* Quality setting */);
    6821026    if (rcv != VPX_CODEC_OK)
    6831027    {
    684         if (pStream->Video.cFailedEncodingFrames++ < 64)
     1028        if (this->Video.cFailedEncodingFrames++ < 64) /** @todo Make this configurable. */
    6851029        {
    6861030            LogRel(("VideoRec: Failed to encode video frame: %s\n", vpx_codec_err_to_string(rcv)));
     
    6891033    }
    6901034
    691     pStream->Video.cFailedEncodingFrames = 0;
     1035    this->Video.cFailedEncodingFrames = 0;
    6921036
    6931037    vpx_codec_iter_t iter = NULL;
     
    6951039    for (;;)
    6961040    {
    697         const vpx_codec_cx_pkt_t *pPacket = vpx_codec_get_cx_data(&pVC->VPX.Ctx, &iter);
     1041        const vpx_codec_cx_pkt_t *pPacket = vpx_codec_get_cx_data(&pCodec->VPX.Ctx, &iter);
    6981042        if (!pPacket)
    6991043            break;
     
    7031047            case VPX_CODEC_CX_FRAME_PKT:
    7041048            {
    705                 WebMWriter::BlockData_VP8 blockData = { &pVC->VPX.Cfg, pPacket };
    706                 rc = pStream->File.pWEBM->WriteBlock(pStream->uTrackVideo, &blockData, sizeof(blockData));
     1049                WebMWriter::BlockData_VP8 blockData = { &pCodec->VPX.Cfg, pPacket };
     1050                rc = this->File.pWEBM->WriteBlock(this->uTrackVideo, &blockData, sizeof(blockData));
    7071051                break;
    7081052            }
     
    7191063#endif /* VBOX_WITH_LIBVPX */
    7201064
     1065/**
     1066 * Locks a recording stream.
     1067 *
     1068 * @param   pStream             Recording stream to lock.
     1069 */
     1070void CaptureStream::lock(void)
     1071{
     1072    int rc = RTCritSectEnter(&CritSect);
     1073    AssertRC(rc);
     1074}
     1075
     1076/**
     1077 * Unlocks a locked recording stream.
     1078 *
     1079 * @param   pStream             Recording stream to unlock.
     1080 */
     1081void CaptureStream::unlock(void)
     1082{
     1083    int rc = RTCritSectLeave(&CritSect);
     1084    AssertRC(rc);
     1085}
     1086
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