VirtualBox

Changeset 68798 in vbox for trunk/src/VBox/Main


Ignore:
Timestamp:
Sep 20, 2017 10:27:16 AM (7 years ago)
Author:
vboxsync
Message:

VideoRec: Overhauled configuration handling to also support audio configuration changes.

Location:
trunk/src/VBox/Main
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/include/ConsoleImpl.h

    r68485 r68798  
    155155    HRESULT i_updateMachineState(MachineState_T aMachineState);
    156156    HRESULT i_getNominalState(MachineState_T &aNominalState);
     157    Utf8Str i_getAudioAdapterDeviceName(IAudioAdapter *aAudioAdapter);
    157158
    158159    // events from IInternalSessionControl
  • trunk/src/VBox/Main/include/DisplayImpl.h

    r68534 r68798  
    55
    66/*
    7  * Copyright (C) 2006-2016 Oracle Corporation
     7 * Copyright (C) 2006-2017 Oracle Corporation
    88 *
    99 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    3232#endif
    3333
     34#ifdef VBOX_WITH_VIDEOREC
     35# include "../src-client/VideoRec.h"
     36struct VIDEORECCONTEXT;
     37#endif
     38
    3439#include "DisplaySourceBitmapWrap.h"
    3540
    3641
    3742class Console;
    38 struct VIDEORECCONTEXT;
    3943
    4044typedef struct _DISPLAYFBINFO
     
    204208    void VideoAccelFlushVMMDev(void);
    205209
    206     int  i_videoCaptureEnableScreens(ComSafeArrayIn(BOOL, aScreens));
     210#ifdef VBOX_WITH_VIDEOREC
     211    VIDEORECFEATURES i_videoCaptureGetEnabled(void);
     212    bool i_videoCaptureStarted(void);
     213    int  i_videoCaptureInvalidate(void);
    207214    int  i_videoCaptureSendAudio(const void *pvData, size_t cbData, uint64_t uTimestampMs);
    208     int  i_videoCaptureStart();
    209     void i_videoCaptureStop();
    210 #ifdef VBOX_WITH_VIDEOREC
    211     void videoCaptureScreenChanged(unsigned uScreenId);
     215    int  i_videoCaptureStart(void);
     216    void i_videoCaptureStop(void);
     217    void i_videoCaptureScreenChanged(unsigned uScreenId);
    212218#endif
    213219
     
    509515
    510516#ifdef VBOX_WITH_VIDEOREC
     517    VIDEORECCFG          mVideoRecCfg;
    511518    VIDEORECCONTEXT     *mpVideoRecCtx;
    512519    bool                 maVideoRecEnabled[SchemaDefs::MaxGuestMonitors];
  • trunk/src/VBox/Main/src-client/ConsoleImpl.cpp

    r68485 r68798  
    8282#include "AutoCaller.h"
    8383#include "ThreadTask.h"
     84
     85#ifdef VBOX_WITH_VIDEOREC
     86# include "VideoRec.h"
     87#endif
    8488
    8589#include <VBox/com/array.h>
     
    49654969}
    49664970
     4971Utf8Str Console::i_getAudioAdapterDeviceName(IAudioAdapter *aAudioAdapter)
     4972{
     4973    Utf8Str strDevice;
     4974
     4975    AudioControllerType_T audioController;
     4976    HRESULT hrc = aAudioAdapter->COMGETTER(AudioController)(&audioController);
     4977    AssertComRC(hrc);
     4978    if (SUCCEEDED(hrc))
     4979    {
     4980        switch (audioController)
     4981        {
     4982            case AudioControllerType_AC97: strDevice = "ichac97"; break;
     4983            case AudioControllerType_SB16: strDevice = "sb16";    break;
     4984            case AudioControllerType_HDA:  strDevice = "hda";     break;
     4985            default:                                              break; /* None. */
     4986        }
     4987    }
     4988
     4989    return strDevice;
     4990}
     4991
    49674992/**
    49684993 * Called by IInternalSessionControl::OnAudioAdapterChange().
     
    49925017            if (SUCCEEDED(hrc))
    49935018            {
    4994                 AudioControllerType_T audioController;
    4995                 hrc = aAudioAdapter->COMGETTER(AudioController)(&audioController);
    4996                 AssertComRC(hrc);
    4997                 if (SUCCEEDED(hrc))
     5019                int rc = VINF_SUCCESS;
     5020
     5021                for (ULONG ulLUN = 0; ulLUN < 16 /** @todo Use a define */; ulLUN++)
    49985022                {
    4999                     Utf8Str strDevice;
    5000 
    5001                     switch (audioController)
     5023                    PPDMIBASE pBase;
     5024                    int rc2 = PDMR3QueryDriverOnLun(ptrVM.rawUVM(),
     5025                                                    i_getAudioAdapterDeviceName(aAudioAdapter).c_str(), 0 /* iInstance */,
     5026                                                    ulLUN, "AUDIO", &pBase);
     5027                    if (RT_FAILURE(rc2))
     5028                        continue;
     5029
     5030                    if (pBase)
    50025031                    {
    5003                         case AudioControllerType_AC97: strDevice = "ichac97"; break;
    5004                         case AudioControllerType_SB16: strDevice = "sb16";    break;
    5005                         case AudioControllerType_HDA:  strDevice = "hda";     break;
    5006                         default:
    5007                             AssertFailed(); break; /* Should never happen. */
    5008                     }
    5009 
    5010                     int rc = VINF_SUCCESS;
    5011 
    5012                     for (ULONG ulLUN = 0; ulLUN < 16 /** @todo Use a define */; ulLUN++)
    5013                     {
    5014                         PPDMIBASE pBase;
    5015                         int rc2 = PDMR3QueryDriverOnLun(ptrVM.rawUVM(),
    5016                                                         strDevice.c_str(), 0 /* iInstance */, ulLUN, "AUDIO", &pBase);
    5017                         if (RT_FAILURE(rc2))
    5018                             continue;
    5019 
    5020                         Log(("%s: LUN#%RU32\n", strDevice.c_str(), ulLUN));
    5021 
    5022                         if (pBase)
     5032                        PPDMIAUDIOCONNECTOR pAudioCon =
     5033                             (PPDMIAUDIOCONNECTOR)pBase->pfnQueryInterface(pBase, PDMIAUDIOCONNECTOR_IID);
     5034
     5035                        if (   pAudioCon
     5036                            && pAudioCon->pfnEnable)
    50235037                        {
    5024                             PPDMIAUDIOCONNECTOR pAudioCon =
    5025                                  (PPDMIAUDIOCONNECTOR)pBase->pfnQueryInterface(pBase, PDMIAUDIOCONNECTOR_IID);
    5026 
    5027                             if (   pAudioCon
    5028                                 && pAudioCon->pfnEnable)
    5029                             {
    5030                                 int rcIn = pAudioCon->pfnEnable(pAudioCon, PDMAUDIODIR_IN, RT_BOOL(fEnabledIn));
    5031                                 if (RT_FAILURE(rcIn))
    5032                                     LogRel(("Audio: Failed to %s input of LUN#%RU32, rc=%Rrc\n",
    5033                                             fEnabledIn ? "enable" : "disable", ulLUN, rcIn));
    5034 
    5035                                 if (RT_SUCCESS(rc))
    5036                                     rc = rcIn;
    5037 
    5038                                 int rcOut = pAudioCon->pfnEnable(pAudioCon, PDMAUDIODIR_OUT, RT_BOOL(fEnabledOut));
    5039                                 if (RT_FAILURE(rcOut))
    5040                                     LogRel(("Audio: Failed to %s output of LUN#%RU32, rc=%Rrc\n",
    5041                                             fEnabledIn ? "enable" : "disable", ulLUN, rcOut));
    5042 
    5043                                 if (RT_SUCCESS(rc))
    5044                                     rc = rcOut;
    5045                             }
     5038                            int rcIn = pAudioCon->pfnEnable(pAudioCon, PDMAUDIODIR_IN, RT_BOOL(fEnabledIn));
     5039                            if (RT_FAILURE(rcIn))
     5040                                LogRel(("Audio: Failed to %s input of LUN#%RU32, rc=%Rrc\n",
     5041                                        fEnabledIn ? "enable" : "disable", ulLUN, rcIn));
     5042
     5043                            if (RT_SUCCESS(rc))
     5044                                rc = rcIn;
     5045
     5046                            int rcOut = pAudioCon->pfnEnable(pAudioCon, PDMAUDIODIR_OUT, RT_BOOL(fEnabledOut));
     5047                            if (RT_FAILURE(rcOut))
     5048                                LogRel(("Audio: Failed to %s output of LUN#%RU32, rc=%Rrc\n",
     5049                                        fEnabledIn ? "enable" : "disable", ulLUN, rcOut));
     5050
     5051                            if (RT_SUCCESS(rc))
     5052                                rc = rcOut;
    50465053                        }
    50475054                    }
    5048 
    5049                     if (RT_SUCCESS(rc))
    5050                         LogRel(("Audio: Status has changed (input is %s, output is %s)\n",
    5051                                 fEnabledIn  ? "enabled" : "disabled", fEnabledOut ? "enabled" : "disabled"));
    50525055                }
     5056
     5057                if (RT_SUCCESS(rc))
     5058                    LogRel(("Audio: Status has changed (input is %s, output is %s)\n",
     5059                            fEnabledIn  ? "enabled" : "disabled", fEnabledOut ? "enabled" : "disabled"));
    50535060            }
    50545061        }
     
    54865493    HRESULT rc = S_OK;
    54875494
    5488     /* don't trigger video capture changes if the VM isn't running */
     5495#ifdef VBOX_WITH_VIDEOREC
     5496    /* Don't trigger video capture changes if the VM isn't running. */
    54895497    SafeVMPtrQuiet ptrVM(this);
    54905498    if (ptrVM.isOk())
    54915499    {
    5492         BOOL fEnabled;
    5493         rc = mMachine->COMGETTER(VideoCaptureEnabled)(&fEnabled);
    5494         SafeArray<BOOL> screens;
    5495         if (SUCCEEDED(rc))
    5496             rc = mMachine->COMGETTER(VideoCaptureScreens)(ComSafeArrayAsOutParam(screens));
    54975500        if (mDisplay)
    54985501        {
    5499             int vrc = VINF_SUCCESS;
    5500             if (SUCCEEDED(rc))
    5501                 vrc = mDisplay->i_videoCaptureEnableScreens(ComSafeArrayAsInParam(screens));
     5502            int vrc = mDisplay->i_videoCaptureInvalidate();
    55025503            if (RT_SUCCESS(vrc))
    55035504            {
    5504                 if (fEnabled)
     5505                VIDEORECFEATURES fFeatures = mDisplay->i_videoCaptureGetEnabled();
     5506
     5507# ifdef VBOX_WITH_AUDIO_VIDEOREC
     5508                ComPtr<IAudioAdapter> audioAdapter;
     5509                rc = mMachine->COMGETTER(AudioAdapter)(audioAdapter.asOutParam());
     5510                AssertComRC(rc);
     5511
     5512                Utf8Str strAudioDev = i_getAudioAdapterDeviceName(audioAdapter);
     5513                if (!strAudioDev.isEmpty()) /* Any audio device enabled? */
     5514                {
     5515                    for (ULONG ulLUN = 0; ulLUN < 16 /** @todo Use a define */; ulLUN++)
     5516                    {
     5517                        PPDMIBASE pBase;
     5518                        int rc2 = PDMR3QueryDriverOnLun(ptrVM.rawUVM(), strAudioDev.c_str(),
     5519                                                        0 /* iInstance */, ulLUN, "AUDIO", &pBase);
     5520                        if (RT_FAILURE(rc2))
     5521                            continue;
     5522
     5523                        if (pBase)
     5524                        {
     5525                            PPDMIAUDIOCONNECTOR pAudioCon =
     5526                                 (PPDMIAUDIOCONNECTOR)pBase->pfnQueryInterface(pBase, PDMIAUDIOCONNECTOR_IID);
     5527
     5528                            if (   pAudioCon
     5529                                && pAudioCon->pfnEnable)
     5530                            {
     5531                                rc2 = pAudioCon->pfnEnable(pAudioCon, PDMAUDIODIR_OUT, fFeatures & VIDEORECFEATURE_AUDIO);
     5532                                if (RT_FAILURE(rc2))
     5533                                    LogRel(("VideoRec: Failed to %s audio recording, rc=%Rrc\n",
     5534                                            fFeatures & VIDEORECFEATURE_AUDIO ? "enable" : "disable", ulLUN, rc2));
     5535                            }
     5536
     5537                            break; /* Driver found, no need to continue. */
     5538                        }
     5539                    }
     5540                }
     5541# endif /* VBOX_WITH_AUDIO_VIDEOREC */
     5542
     5543                if (!mDisplay->i_videoCaptureStarted())
    55055544                {
    55065545                    vrc = mDisplay->i_videoCaptureStart();
     
    55145553                rc = setError(E_FAIL, tr("Unable to set screens for capturing (%Rrc)"), vrc);
    55155554        }
     5555
    55165556        ptrVM.release();
    55175557    }
     5558#endif /* VBOX_WITH_VIDEOREC */
    55185559
    55195560    /* notify console callbacks on success */
  • trunk/src/VBox/Main/src-client/DisplayImpl.cpp

    r68534 r68798  
    5656# include <iprt/path.h>
    5757# include "VideoRec.h"
     58
     59# ifdef VBOX_WITH_LIBVPX
     60#  include <vpx/vpx_encoder.h>
     61# endif
    5862#endif
    5963
     
    10491053
    10501054#ifdef VBOX_WITH_VIDEOREC
    1051     videoCaptureScreenChanged(uScreenId);
     1055    i_videoCaptureScreenChanged(uScreenId);
    10521056#endif
    10531057
     
    23862390}
    23872391
    2388 int Display::i_videoCaptureEnableScreens(ComSafeArrayIn(BOOL, aScreens))
    2389 {
    23902392#ifdef VBOX_WITH_VIDEOREC
    2391     com::SafeArray<BOOL> Screens(ComSafeArrayInArg(aScreens));
    2392     for (unsigned i = 0; i < Screens.size(); i++)
    2393     {
    2394         bool fChanged = maVideoRecEnabled[i] != RT_BOOL(Screens[i]);
    2395 
    2396         maVideoRecEnabled[i] = RT_BOOL(Screens[i]);
     2393VIDEORECFEATURES Display::i_videoCaptureGetEnabled(void)
     2394{
     2395    return VideoRecGetEnabled(&mVideoRecCfg);
     2396}
     2397
     2398bool Display::i_videoCaptureStarted(void)
     2399{
     2400    return VideoRecIsActive(mpVideoRecCtx);
     2401}
     2402
     2403int Display::i_videoCaptureInvalidate(void)
     2404{
     2405    AssertPtr(mParent);
     2406    ComPtr<IMachine> pMachine = mParent->i_machine();
     2407    Assert(pMachine.isNotNull());
     2408
     2409    mVideoRecCfg.fEnabled = true;
     2410    mVideoRecCfg.enmDst   = VIDEORECDEST_FILE; /** @todo Make this configurable once we have more variations. */
     2411
     2412    /*
     2413     * Get parameters from API.
     2414     */
     2415    SafeArray<BOOL> aScreens;
     2416    HRESULT rc = pMachine->COMGETTER(VideoCaptureScreens)(ComSafeArrayAsOutParam(aScreens));
     2417    AssertComRCReturn(rc, VERR_COM_UNEXPECTED);
     2418    aScreens.cloneTo(mVideoRecCfg.aScreens);
     2419
     2420    rc = pMachine->COMGETTER(VideoCaptureWidth)(&mVideoRecCfg.Video.uWidth);
     2421    AssertComRCReturn(rc, VERR_COM_UNEXPECTED);
     2422    rc = pMachine->COMGETTER(VideoCaptureHeight)(&mVideoRecCfg.Video.uHeight);
     2423    AssertComRCReturn(rc, VERR_COM_UNEXPECTED);
     2424    rc = pMachine->COMGETTER(VideoCaptureRate)(&mVideoRecCfg.Video.uRate);
     2425    AssertComRCReturn(rc, VERR_COM_UNEXPECTED);
     2426    rc = pMachine->COMGETTER(VideoCaptureFPS)(&mVideoRecCfg.Video.uFPS);
     2427    AssertComRCReturn(rc, VERR_COM_UNEXPECTED);
     2428    rc = pMachine->COMGETTER(VideoCaptureFile)(&mVideoRecCfg.File.strFile);
     2429    AssertComRCReturn(rc, VERR_COM_UNEXPECTED);
     2430    rc = pMachine->COMGETTER(VideoCaptureMaxFileSize)(&mVideoRecCfg.File.uMaxSizeMB);
     2431    AssertComRCReturn(rc, VERR_COM_UNEXPECTED);
     2432    rc = pMachine->COMGETTER(VideoCaptureMaxTime)(&mVideoRecCfg.uMaxTimeS);
     2433    AssertComRCReturn(rc, VERR_COM_UNEXPECTED);
     2434    BSTR bstrOptions;
     2435    rc = pMachine->COMGETTER(VideoCaptureOptions)(&bstrOptions);
     2436    AssertComRCReturn(rc, VERR_COM_UNEXPECTED);
     2437
     2438    /*
     2439     * Parse options string (from API).
     2440     */
     2441    com::Utf8Str strOptions(bstrOptions);
     2442    size_t pos = 0;
     2443
     2444    /* By default we only enable video recording for now.
     2445     * Audio support is considered as being experimental. There be dragons! */
     2446    mVideoRecCfg.Video.fEnabled  = true;
     2447#ifdef VBOX_WITH_AUDIO_VIDEOREC
     2448    mVideoRecCfg.Audio.fEnabled  = false;
     2449    /* By default we use 48kHz, 16-bit, stereo for the audio track. */
     2450    mVideoRecCfg.Audio.uHz       = 48000;
     2451    mVideoRecCfg.Audio.cBits     = 16;
     2452    mVideoRecCfg.Audio.cChannels = 2;
     2453#endif
     2454
     2455    if (!mVideoRecCfg.Video.uFPS) /* Prevent division by zero. */
     2456        mVideoRecCfg.Video.uFPS = 30;
     2457
     2458    /*
     2459     * Parse options string.
     2460     */
     2461    com::Utf8Str key, value;
     2462    while ((pos = strOptions.parseKeyValue(key, value, pos)) != com::Utf8Str::npos)
     2463    {
     2464        if (key.compare("vc_quality", Utf8Str::CaseInsensitive) == 0)
     2465        {
     2466#ifdef VBOX_WITH_LIBVPX
     2467            if (value.compare("realtime", Utf8Str::CaseInsensitive) == 0)
     2468                mVideoRecCfg.Video.Codec.VPX.uEncoderDeadline = VPX_DL_REALTIME;
     2469            else if (value.compare("good", Utf8Str::CaseInsensitive) == 0)
     2470                mVideoRecCfg.Video.Codec.VPX.uEncoderDeadline = 1000000 / mVideoRecCfg.Video.uFPS;
     2471            else if (value.compare("best", Utf8Str::CaseInsensitive) == 0)
     2472                mVideoRecCfg.Video.Codec.VPX.uEncoderDeadline = VPX_DL_BEST_QUALITY;
     2473            else
     2474            {
     2475                LogRel(("VideoRec: Setting quality deadline to '%s'\n", value.c_str()));
     2476                mVideoRecCfg.Video.Codec.VPX.uEncoderDeadline = value.toUInt32();
     2477#endif
     2478            }
     2479        }
     2480        else if (key.compare("vc_enabled", Utf8Str::CaseInsensitive) == 0)
     2481        {
     2482            if (value.compare("false", Utf8Str::CaseInsensitive) == 0)
     2483            {
     2484                mVideoRecCfg.Video.fEnabled = false;
     2485                LogRel(("VideoRec: Only audio will be recorded\n"));
     2486            }
     2487        }
     2488        else if (key.compare("ac_enabled", Utf8Str::CaseInsensitive) == 0)
     2489        {
     2490#ifdef VBOX_WITH_AUDIO_VIDEOREC
     2491            if (value.compare("false", Utf8Str::CaseInsensitive))
     2492            {
     2493                mVideoRecCfg.Audio.fEnabled = false;
     2494                LogRel(("VideoRec: Only video will be recorded\n"));
     2495            }
     2496#endif
     2497        }
     2498        else if (key.compare("ac_profile", Utf8Str::CaseInsensitive) == 0)
     2499        {
     2500#ifdef VBOX_WITH_AUDIO_VIDEOREC
     2501            if (value.compare("low", Utf8Str::CaseInsensitive) == 0)
     2502            {
     2503                mVideoRecCfg.Audio.uHz       = 8000;
     2504                mVideoRecCfg.Audio.cBits     = 16;
     2505                mVideoRecCfg.Audio.cChannels = 1;
     2506            }
     2507            else if (value.startsWith("med" /* "med[ium]" */, Utf8Str::CaseInsensitive) == 0)
     2508            {
     2509                mVideoRecCfg.Audio.uHz       = 22050;
     2510                mVideoRecCfg.Audio.cBits     = 16;
     2511                mVideoRecCfg.Audio.cChannels = 2;
     2512            }
     2513            else if (value.compare("high", Utf8Str::CaseInsensitive) == 0)
     2514            {
     2515                /* Stay with the default set above. */
     2516            }
     2517#endif
     2518        }
     2519        else
     2520            LogRel(("VideoRec: Unknown option '%s' (value '%s'), skipping\n", key.c_str(), value.c_str()));
     2521
     2522    } /* while */
     2523
     2524    /*
     2525     * Invalidate screens.
     2526     */
     2527    for (unsigned i = 0; i < mVideoRecCfg.aScreens.size(); i++)
     2528    {
     2529        bool fChanged = maVideoRecEnabled[i] != RT_BOOL(mVideoRecCfg.aScreens[i]);
     2530
     2531        maVideoRecEnabled[i] = RT_BOOL(mVideoRecCfg.aScreens[i]);
    23972532
    23982533        if (fChanged && i < mcMonitors)
    2399             videoCaptureScreenChanged(i);
    2400 
    2401     }
    2402     return VINF_SUCCESS;
    2403 #else
    2404     ComSafeArrayNoRef(aScreens);
    2405     return VERR_NOT_SUPPORTED;
    2406 #endif
     2534            i_videoCaptureScreenChanged(i);
     2535
     2536    }
     2537
     2538    return S_OK;
    24072539}
    24082540
     
    24182550int Display::i_videoCaptureSendAudio(const void *pvData, size_t cbData, uint64_t uTimestampMs)
    24192551{
    2420 #ifdef VBOX_WITH_AUDIO_VIDEOREC
    2421     if (!VideoRecIsEnabled(mpVideoRecCtx))
    2422         return VINF_SUCCESS;
    2423 
    2424     return VideoRecSendAudioFrame(mpVideoRecCtx, pvData, cbData, uTimestampMs);
    2425 #else
    2426     RT_NOREF(pvData, cbData, uTimestampMs);
    2427     return VERR_NOT_SUPPORTED;
    2428 #endif
     2552    if (   VideoRecIsActive(mpVideoRecCtx)
     2553        && VideoRecGetEnabled(&mVideoRecCfg) & VIDEORECFEATURE_AUDIO)
     2554    {
     2555        return VideoRecSendAudioFrame(mpVideoRecCtx, pvData, cbData, uTimestampMs);
     2556    }
     2557
     2558    return VINF_SUCCESS;
    24292559}
    24302560
     
    24322562 * Start video capturing. Does nothing if capturing is already active.
    24332563 *
     2564 * @param   pVideoRecCfg        Video recording configuration to use.
    24342565 * @returns IPRT status code.
    24352566 */
    24362567int Display::i_videoCaptureStart(void)
    24372568{
    2438 #ifdef VBOX_WITH_VIDEOREC
    2439     if (VideoRecIsEnabled(mpVideoRecCtx))
     2569    if (VideoRecIsActive(mpVideoRecCtx))
    24402570        return VINF_SUCCESS;
    24412571
    2442     int rc = VideoRecContextCreate(mcMonitors, &mpVideoRecCtx);
     2572    int rc = VideoRecContextCreate(mcMonitors, &mVideoRecCfg, &mpVideoRecCtx);
    24432573    if (RT_FAILURE(rc))
    24442574    {
     
    24462576        return rc;
    24472577    }
    2448     ComPtr<IMachine> pMachine = mParent->i_machine();
    2449     com::SafeArray<BOOL> screens;
    2450     HRESULT hrc = pMachine->COMGETTER(VideoCaptureScreens)(ComSafeArrayAsOutParam(screens));
    2451     AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
    2452     for (unsigned i = 0; i < RT_ELEMENTS(maVideoRecEnabled); i++)
    2453         maVideoRecEnabled[i] = i < screens.size() && screens[i];
    2454     ULONG ulWidth;
    2455     hrc = pMachine->COMGETTER(VideoCaptureWidth)(&ulWidth);
    2456     AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
    2457     ULONG ulHeight;
    2458     hrc = pMachine->COMGETTER(VideoCaptureHeight)(&ulHeight);
    2459     AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
    2460     ULONG ulRate;
    2461     hrc = pMachine->COMGETTER(VideoCaptureRate)(&ulRate);
    2462     AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
    2463     ULONG ulFPS;
    2464     hrc = pMachine->COMGETTER(VideoCaptureFPS)(&ulFPS);
    2465     AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
    2466     BSTR strFile;
    2467     hrc = pMachine->COMGETTER(VideoCaptureFile)(&strFile);
    2468     AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
    2469     ULONG ulMaxTime;
    2470     hrc = pMachine->COMGETTER(VideoCaptureMaxTime)(&ulMaxTime);
    2471     AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
    2472     ULONG ulMaxSize;
    2473     hrc = pMachine->COMGETTER(VideoCaptureMaxFileSize)(&ulMaxSize);
    2474     AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
    2475     BSTR strOptions;
    2476     hrc = pMachine->COMGETTER(VideoCaptureOptions)(&strOptions);
    2477     AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
    2478 
    2479     RTTIMESPEC ts;
    2480     RTTimeNow(&ts);
    2481     RTTIME time;
    2482     RTTimeExplode(&time, &ts);
     2578
    24832579    for (unsigned uScreen = 0; uScreen < mcMonitors; uScreen++)
    24842580    {
    2485         char *pszAbsPath = RTPathAbsDup(com::Utf8Str(strFile).c_str());
    2486         char *pszSuff = RTPathSuffix(pszAbsPath);
    2487         if (pszSuff)
    2488             pszSuff = RTStrDup(pszSuff);
    2489         RTPathStripSuffix(pszAbsPath);
    2490         if (!pszAbsPath)
    2491             rc = VERR_INVALID_PARAMETER;
    2492         if (!pszSuff)
    2493             pszSuff = RTStrDup(".webm");
    2494         char *pszName = NULL;
     2581        int rc2 = VideoRecStreamInit(mpVideoRecCtx, uScreen);
     2582        if (RT_SUCCESS(rc2))
     2583        {
     2584            i_videoCaptureScreenChanged(uScreen);
     2585        }
     2586        else
     2587            LogRel(("Display::VideoCaptureStart: Failed to initialize video recording context #%u (%Rrc)\n", uScreen, rc2));
     2588
    24952589        if (RT_SUCCESS(rc))
    2496         {
    2497             if (mcMonitors > 1)
    2498                 rc = RTStrAPrintf(&pszName, "%s-%u%s", pszAbsPath, uScreen+1, pszSuff);
    2499             else
    2500                 rc = RTStrAPrintf(&pszName, "%s%s", pszAbsPath, pszSuff);
    2501         }
    2502         if (RT_SUCCESS(rc))
    2503         {
    2504             rc = VideoRecStreamInit(mpVideoRecCtx, uScreen,
    2505                                     pszName, ulWidth, ulHeight,
    2506                                     ulRate, ulFPS, ulMaxTime,
    2507                                     ulMaxSize, com::Utf8Str(strOptions).c_str());
    2508             if (rc == VERR_ALREADY_EXISTS)
    2509             {
    2510                 RTStrFree(pszName);
    2511                 pszName = NULL;
    2512 
    2513                 if (mcMonitors > 1)
    2514                     rc = RTStrAPrintf(&pszName, "%s-%04d-%02u-%02uT%02u-%02u-%02u-%09uZ-%u%s",
    2515                                       pszAbsPath, time.i32Year, time.u8Month, time.u8MonthDay,
    2516                                       time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond,
    2517                                       uScreen+1, pszSuff);
    2518                 else
    2519                     rc = RTStrAPrintf(&pszName, "%s-%04d-%02u-%02uT%02u-%02u-%02u-%09uZ%s",
    2520                                       pszAbsPath, time.i32Year, time.u8Month, time.u8MonthDay,
    2521                                       time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond,
    2522                                       pszSuff);
    2523                 if (RT_SUCCESS(rc))
    2524                     rc = VideoRecStreamInit(mpVideoRecCtx, uScreen,
    2525                                             pszName, ulWidth, ulHeight, ulRate,
    2526                                             ulFPS, ulMaxTime,
    2527                                             ulMaxSize, com::Utf8Str(strOptions).c_str());
    2528             }
    2529         }
    2530 
    2531         if (RT_SUCCESS(rc))
    2532         {
    2533             videoCaptureScreenChanged(uScreen);
    2534         }
    2535         else
    2536             LogRel(("Display::VideoCaptureStart: Failed to initialize video recording context #%u, (%Rrc)\n", uScreen, rc));
    2537 
    2538         RTStrFree(pszName);
    2539         RTStrFree(pszSuff);
    2540         RTStrFree(pszAbsPath);
     2590            rc = rc2;
    25412591    }
    25422592    return rc;
    2543 #else /* VBOX_WITH_VIDEOREC */
    2544     return VERR_NOT_SUPPORTED;
    2545 #endif
    25462593}
    25472594
     
    25492596 * Stop video capturing. Does nothing if video capturing is not active.
    25502597 */
    2551 void Display::i_videoCaptureStop()
    2552 {
    2553 #ifdef VBOX_WITH_VIDEOREC
    2554     if (!VideoRecIsEnabled(mpVideoRecCtx))
     2598void Display::i_videoCaptureStop(void)
     2599{
     2600    if (!VideoRecIsActive(mpVideoRecCtx))
    25552601        return;
    25562602
     
    25602606    unsigned uScreenId;
    25612607    for (uScreenId = 0; uScreenId < mcMonitors; ++uScreenId)
    2562         videoCaptureScreenChanged(uScreenId);
    2563 #endif
    2564 }
    2565 
    2566 #ifdef VBOX_WITH_VIDEOREC
    2567 void Display::videoCaptureScreenChanged(unsigned uScreenId)
    2568 {
    2569     if (   !VideoRecIsEnabled(mpVideoRecCtx)
     2608        i_videoCaptureScreenChanged(uScreenId);
     2609}
     2610
     2611void Display::i_videoCaptureScreenChanged(unsigned uScreenId)
     2612{
     2613    if (   !VideoRecIsActive(mpVideoRecCtx)
    25702614        || !maVideoRecEnabled[uScreenId])
    25712615    {
     
    32893333
    32903334#ifdef VBOX_WITH_VIDEOREC
    3291     if (VideoRecIsEnabled(pDisplay->mpVideoRecCtx))
     3335    if (   VideoRecIsActive(pDisplay->mpVideoRecCtx)
     3336        && VideoRecGetEnabled(&pDisplay->mVideoRecCfg) & VIDEORECFEATURE_VIDEO)
    32923337    {
    32933338        do {
     
    44854530    if (fEnabled)
    44864531    {
    4487         rc = pDisplay->i_videoCaptureStart();
    4488         fireVideoCaptureChangedEvent(pDisplay->mParent->i_getEventSource());
     4532        rc = pDisplay->i_videoCaptureInvalidate();
     4533        if (RT_SUCCESS(rc))
     4534        {
     4535            rc = pDisplay->i_videoCaptureStart();
     4536            fireVideoCaptureChangedEvent(pDisplay->mParent->i_getEventSource());
     4537        }
    44894538    }
    44904539#endif
  • trunk/src/VBox/Main/src-client/VideoRec.cpp

    r68776 r68798  
    2222#include <vector>
    2323
    24 #include <VBox/log.h>
    2524#include <iprt/asm.h>
    2625#include <iprt/assert.h>
    2726#include <iprt/critsect.h>
     27#include <iprt/path.h>
    2828#include <iprt/semaphore.h>
    2929#include <iprt/thread.h>
    3030#include <iprt/time.h>
    3131
     32#include <VBox/log.h>
    3233#include <VBox/com/VirtualBox.h>
    3334#include <VBox/com/com.h>
     
    5455                            uint8_t *paSrc, uint32_t uSrcWidth, uint32_t uSrcHeight);
    5556
    56 int videoRecStreamLock(PVIDEORECSTREAM pStream);
    57 int videoRecStreamUnlock(PVIDEORECSTREAM pStream);
     57static int videoRecStreamCloseFile(PVIDEORECSTREAM pStream);
     58static void videoRecStreamLock(PVIDEORECSTREAM pStream);
     59static void videoRecStreamUnlock(PVIDEORECSTREAM pStream);
    5860
    5961using namespace com;
     
    114116            /** VPX image context. */
    115117            vpx_image_t         RawImage;
    116             /** Encoder deadline. */
    117             unsigned int        uEncoderDeadline;
    118118        } VPX;
    119119#endif /* VBOX_WITH_LIBVPX */
     
    158158typedef struct VIDEORECSTREAM
    159159{
    160     /** Container context. */
    161     WebMWriter         *pEBML;
     160    /** Video recording context this stream is associated to. */
     161    PVIDEORECCONTEXT            pCtx;
     162    /** Destination where to write the stream to. */
     163    VIDEORECDEST                enmDst;
     164    union
     165    {
     166        struct
     167        {
     168            /** File handle to use for writing. */
     169            RTFILE              hFile;
     170            /** File name being used for this stream. */
     171            char               *pszFile;
     172            /** Pointer to WebM writer instance being used. */
     173            WebMWriter         *pWEBM;
     174        } File;
     175    };
    162176#ifdef VBOX_WITH_AUDIO_VIDEOREC
    163177    /** Track number of audio stream. */
     
    204218typedef struct VIDEORECCONTEXT
    205219{
     220    /** Used recording configuration. */
     221    VIDEORECCFG         Cfg;
    206222    /** The current state. */
    207223    uint32_t            enmState;
     
    210226    /** Semaphore to signal the encoding worker thread. */
    211227    RTSEMEVENT          WaitEvent;
    212     /** Whether recording is enabled or not. */
     228    /** Whether this conext is enabled or not. */
    213229    bool                fEnabled;
    214230    /** Shutdown indicator. */
     
    216232    /** Worker thread. */
    217233    RTTHREAD            Thread;
    218     /** Maximal time (in ms) to record. */
    219     uint64_t            uMaxTimeMs;
    220     /** Maximal file size (in MB) to record. */
    221     uint32_t            uMaxSizeMB;
    222234    /** Vector of current recording stream contexts. */
    223235    VideoRecStreams     vecStreams;
     236    /** Timestamp (in ms) of when recording has been started. */
     237    uint64_t            tsStartMs;
    224238#ifdef VBOX_WITH_AUDIO_VIDEOREC
    225239    struct
     
    628642
    629643                WebMWriter::BlockData_Opus blockData = { audioFrame.abBuf, audioFrame.cbBuf, audioFrame.uTimeStampMs };
    630                 rc = pStream->pEBML->WriteBlock(pStream->uTrackAudio, &blockData, sizeof(blockData));
     644                rc = pStream->File.pWEBM->WriteBlock(pStream->uTrackAudio, &blockData, sizeof(blockData));
    631645                if (RT_FAILURE(rc))
    632646                {
     
    653667 *
    654668 * @returns IPRT status code.
    655  * @param   cScreens         Number of screens to create context for.
    656  * @param   ppCtx            Pointer to created video recording context on success.
    657  */
    658 int VideoRecContextCreate(uint32_t cScreens, PVIDEORECCONTEXT *ppCtx)
    659 {
    660     AssertReturn(cScreens, VERR_INVALID_PARAMETER);
    661     AssertPtrReturn(ppCtx, VERR_INVALID_POINTER);
     669 * @param   cScreens            Number of screens to create context for.
     670 * @param   pVideoRecCfg        Pointer to video recording configuration to use.
     671 * @param   ppCtx               Pointer to created video recording context on success.
     672 */
     673int VideoRecContextCreate(uint32_t cScreens, PVIDEORECCFG pVideoRecCfg, PVIDEORECCONTEXT *ppCtx)
     674{
     675    AssertReturn(cScreens,        VERR_INVALID_PARAMETER);
     676    AssertPtrReturn(pVideoRecCfg, VERR_INVALID_POINTER);
     677    AssertPtrReturn(ppCtx,        VERR_INVALID_POINTER);
    662678
    663679    PVIDEORECCONTEXT pCtx = (PVIDEORECCONTEXT)RTMemAllocZ(sizeof(VIDEORECCONTEXT));
     
    667683    int rc = RTCritSectInit(&pCtx->CritSect);
    668684    if (RT_FAILURE(rc))
     685    {
     686        RTMemFree(pCtx);
    669687        return rc;
     688    }
    670689
    671690    for (uint32_t uScreen = 0; uScreen < cScreens; uScreen++)
     
    688707            pCtx->vecStreams.push_back(pStream);
    689708
    690             pStream->pEBML = new WebMWriter();
     709            pStream->File.pWEBM = new WebMWriter();
    691710        }
    692711        catch (std::bad_alloc)
     
    699718    if (RT_SUCCESS(rc))
    700719    {
     720        pCtx->tsStartMs = RTTimeMilliTS();
    701721        pCtx->enmState  = VIDEORECSTS_UNINITIALIZED;
    702722        pCtx->fShutdown = false;
    703723
     724        /* Copy the configuration to our context. */
     725        pCtx->Cfg       = *pVideoRecCfg;
     726
    704727        rc = RTSemEventCreate(&pCtx->WaitEvent);
    705728        AssertRCReturn(rc, rc);
     
    723746    if (RT_FAILURE(rc))
    724747    {
    725         /* Roll back allocations on error. */
     748        int rc2 = VideoRecContextDestroy(pCtx);
     749        AssertRC(rc2);
     750    }
     751
     752    return rc;
     753}
     754
     755/**
     756 * Destroys a video recording context.
     757 *
     758 * @param pCtx                  Video recording context to destroy.
     759 */
     760int VideoRecContextDestroy(PVIDEORECCONTEXT pCtx)
     761{
     762    if (!pCtx)
     763        return VINF_SUCCESS;
     764
     765    /* First, disable the context. */
     766    ASMAtomicWriteBool(&pCtx->fEnabled, false);
     767
     768    if (pCtx->enmState == VIDEORECSTS_INITIALIZED)
     769    {
     770        /* Set shutdown indicator. */
     771        ASMAtomicWriteBool(&pCtx->fShutdown, true);
     772
     773        /* Signal the thread. */
     774        RTSemEventSignal(pCtx->WaitEvent);
     775
     776        int rc = RTThreadWait(pCtx->Thread, 10 * 1000 /* 10s timeout */, NULL);
     777        if (RT_FAILURE(rc))
     778            return rc;
     779
     780        rc = RTSemEventDestroy(pCtx->WaitEvent);
     781        AssertRC(rc);
     782
     783        pCtx->WaitEvent = NIL_RTSEMEVENT;
     784    }
     785
     786    int rc = RTCritSectEnter(&pCtx->CritSect);
     787    if (RT_SUCCESS(rc))
     788    {
    726789        VideoRecStreams::iterator it = pCtx->vecStreams.begin();
    727790        while (it != pCtx->vecStreams.end())
     
    729792            PVIDEORECSTREAM pStream = (*it);
    730793
    731             if (pStream->pEBML)
    732                 delete pStream->pEBML;
    733 
    734             it = pCtx->vecStreams.erase(it);
    735 
    736             RTMemFree(pStream);
    737             pStream = NULL;
    738         }
    739 
    740         Assert(pCtx->vecStreams.empty());
    741     }
    742 
    743     return rc;
    744 }
    745 
    746 /**
    747  * Destroys a video recording context.
    748  *
    749  * @param pCtx                  Video recording context to destroy.
    750  */
    751 int VideoRecContextDestroy(PVIDEORECCONTEXT pCtx)
    752 {
    753     if (!pCtx)
    754         return VINF_SUCCESS;
    755 
    756     /* Set shutdown indicator. */
    757     ASMAtomicWriteBool(&pCtx->fShutdown, true);
    758 
    759     /* Signal the thread. */
    760     RTSemEventSignal(pCtx->WaitEvent);
    761 
    762     int rc = RTThreadWait(pCtx->Thread, 10 * 1000 /* 10s timeout */, NULL);
    763     if (RT_FAILURE(rc))
    764         return rc;
    765 
    766     rc = RTSemEventDestroy(pCtx->WaitEvent);
    767     AssertRC(rc);
    768 
    769     pCtx->WaitEvent = NIL_RTSEMEVENT;
    770 
    771     VideoRecStreams::iterator it = pCtx->vecStreams.begin();
    772     while (it != pCtx->vecStreams.end())
    773     {
    774         PVIDEORECSTREAM pStream = (*it);
    775 
    776         if (pStream->fEnabled)
    777         {
    778             AssertPtr(pStream->pEBML);
    779             pStream->pEBML->Close();
    780 
    781             vpx_img_free(&pStream->Video.Codec.VPX.RawImage);
    782             vpx_codec_err_t rcv = vpx_codec_destroy(&pStream->Video.Codec.VPX.Ctx);
    783             Assert(rcv == VPX_CODEC_OK); RT_NOREF(rcv);
     794            videoRecStreamLock(pStream);
     795
     796            if (pStream->fEnabled)
     797            {
     798                switch (pStream->enmDst)
     799                {
     800                    case VIDEORECDEST_FILE:
     801                    {
     802                        if (pStream->File.pWEBM)
     803                            pStream->File.pWEBM->Close();
     804                        break;
     805                    }
     806
     807                    default:
     808                        AssertFailed(); /* Should never happen. */
     809                        break;
     810                }
     811
     812                vpx_img_free(&pStream->Video.Codec.VPX.RawImage);
     813                vpx_codec_err_t rcv = vpx_codec_destroy(&pStream->Video.Codec.VPX.Ctx);
     814                Assert(rcv == VPX_CODEC_OK); RT_NOREF(rcv);
    784815
    785816#ifdef VBOX_VIDEOREC_WITH_QUEUE
    786817# error "Implement me!"
    787818#else
    788             PVIDEORECVIDEOFRAME pFrame = &pStream->Video.Frame;
    789 #endif
    790             if (pFrame->pu8RGBBuf)
     819                PVIDEORECVIDEOFRAME pFrame = &pStream->Video.Frame;
     820#endif
     821                if (pFrame->pu8RGBBuf)
     822                {
     823                    Assert(pFrame->cbRGBBuf);
     824
     825                    RTMemFree(pFrame->pu8RGBBuf);
     826                    pFrame->pu8RGBBuf = NULL;
     827                }
     828
     829                pFrame->cbRGBBuf = 0;
     830
     831                LogRel(("VideoRec: Recording screen #%u stopped\n", pStream->uScreenID));
     832            }
     833
     834            switch (pStream->enmDst)
    791835            {
    792                 Assert(pFrame->cbRGBBuf);
    793 
    794                 RTMemFree(pFrame->pu8RGBBuf);
    795                 pFrame->pu8RGBBuf = NULL;
     836                case VIDEORECDEST_FILE:
     837                {
     838                    int rc2 = videoRecStreamCloseFile(pStream);
     839                    AssertRC(rc2);
     840
     841                    if (pStream->File.pWEBM)
     842                    {
     843                        delete pStream->File.pWEBM;
     844                        pStream->File.pWEBM = NULL;
     845                    }
     846                    break;
     847                }
     848
     849                default:
     850                    AssertFailed(); /* Should never happen. */
     851                    break;
    796852            }
    797853
    798             pFrame->cbRGBBuf = 0;
    799 
    800             LogRel(("VideoRec: Recording screen #%u stopped\n", pStream->uScreenID));
    801         }
    802 
    803         if (pStream->pEBML)
    804         {
    805             delete pStream->pEBML;
    806             pStream->pEBML = NULL;
    807         }
    808 
    809         it = pCtx->vecStreams.erase(it);
    810 
    811         RTCritSectDelete(&pStream->CritSect);
    812 
    813         RTMemFree(pStream);
    814         pStream = NULL;
    815     }
    816 
    817     Assert(pCtx->vecStreams.empty());
    818 
    819     RTCritSectDelete(&pCtx->CritSect);
    820 
    821     RTMemFree(pCtx);
    822     pCtx = NULL;
    823 
    824     return VINF_SUCCESS;
     854            it = pCtx->vecStreams.erase(it);
     855
     856            videoRecStreamUnlock(pStream);
     857
     858            RTCritSectDelete(&pStream->CritSect);
     859
     860            RTMemFree(pStream);
     861            pStream = NULL;
     862        }
     863
     864        Assert(pCtx->vecStreams.empty());
     865
     866        int rc2 = RTCritSectLeave(&pCtx->CritSect);
     867        AssertRC(rc2);
     868
     869        RTCritSectDelete(&pCtx->CritSect);
     870
     871        RTMemFree(pCtx);
     872        pCtx = NULL;
     873    }
     874
     875    return rc;
    825876}
    826877
     
    853904 * Locks a recording stream.
    854905 *
    855  * @returns IPRT status code.
    856906 * @param   pStream             Recording stream to lock.
    857907 */
    858 int videoRecStreamLock(PVIDEORECSTREAM pStream)
     908static void videoRecStreamLock(PVIDEORECSTREAM pStream)
    859909{
    860910    int rc = RTCritSectEnter(&pStream->CritSect);
    861911    AssertRC(rc);
    862 
    863     return rc;
    864912}
    865913
     
    867915 * Unlocks a locked recording stream.
    868916 *
    869  * @returns IPRT status code.
    870917 * @param   pStream             Recording stream to unlock.
    871918 */
    872 int videoRecStreamUnlock(PVIDEORECSTREAM pStream)
     919static void videoRecStreamUnlock(PVIDEORECSTREAM pStream)
    873920{
    874921    int rc = RTCritSectLeave(&pStream->CritSect);
    875922    AssertRC(rc);
     923}
     924
     925/**
     926 * Opens a file for a given recording stream to capture to.
     927 *
     928 * @returns IPRT status code.
     929 * @param   pStream             Recording stream to open file for.
     930 * @param   pCfg                Recording configuration to use.
     931 */
     932int videoRecStreamOpenFile(PVIDEORECSTREAM pStream, PVIDEORECCFG pCfg)
     933{
     934    AssertPtrReturn(pStream, VERR_INVALID_POINTER);
     935    AssertPtrReturn(pCfg,    VERR_INVALID_POINTER);
     936
     937    Assert(pStream->enmDst == VIDEORECDEST_INVALID);
     938    Assert(pCfg->enmDst    == VIDEORECDEST_FILE);
     939
     940    RTTIMESPEC ts;
     941    RTTimeNow(&ts);
     942
     943    RTTIME time;
     944    RTTimeExplode(&time, &ts);
     945
     946    char *pszAbsPath = RTPathAbsDup(com::Utf8Str(pCfg->File.strFile).c_str());
     947    AssertPtrReturn(pszAbsPath, VERR_NO_MEMORY);
     948
     949    char *pszSuff    = RTPathSuffix(pszAbsPath);
     950    if (pszSuff)
     951        pszSuff = RTStrDup(pszSuff);
     952
     953    RTPathStripSuffix(pszAbsPath);
     954    AssertPtrReturn(pszAbsPath, VERR_INVALID_PARAMETER);
     955
     956    if (!pszSuff)
     957        pszSuff = RTStrDup(".webm");
     958
     959    char *pszFile = NULL;
     960
     961    int rc;
     962    if (pStream->uScreenID > 1)
     963        rc = RTStrAPrintf(&pszFile, "%s-%u%s", pszAbsPath, pStream->uScreenID + 1, pszSuff);
     964    else
     965        rc = RTStrAPrintf(&pszFile, "%s%s", pszAbsPath, pszSuff);
     966
     967    if (RT_SUCCESS(rc))
     968    {
     969        uint64_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_WRITE;
     970#ifdef DEBUG
     971        fOpen |= RTFILE_O_CREATE_REPLACE;
     972#else
     973        /* Play safe: the file must not exist, overwriting is potentially
     974         * hazardous as nothing prevents the user from picking a file name of some
     975         * other important file, causing unintentional data loss. */
     976        fOpen |= RTFILE_O_CREATE;
     977#endif
     978        RTFILE hFile;
     979        rc = RTFileOpen(&hFile, pszFile, fOpen);
     980        if (RT_SUCCESS(rc))
     981        {
     982            pStream->enmDst       = VIDEORECDEST_FILE;
     983            pStream->File.hFile   = hFile;
     984            pStream->File.pszFile = pszFile; /* Assign allocated string to our stream's config. */
     985        }
     986        else
     987            RTStrFree(pszFile);
     988    }
     989
     990    RTStrFree(pszSuff);
     991    RTStrFree(pszAbsPath);
     992
     993    if (RT_FAILURE(rc))
     994        LogRel(("VideoRec: Failed to open file for screen %RU32, rc=%Rrc\n", pStream->uScreenID, rc));
    876995
    877996    return rc;
     997}
     998
     999/**
     1000 * Closes a recording stream's file again.
     1001 *
     1002 * @returns IPRT status code.
     1003 * @param   pStream             Recording stream to close file for.
     1004 */
     1005static int videoRecStreamCloseFile(PVIDEORECSTREAM pStream)
     1006{
     1007    Assert(pStream->enmDst == VIDEORECDEST_FILE);
     1008
     1009    pStream->enmDst = VIDEORECDEST_INVALID;
     1010
     1011    AssertPtr(pStream->File.pszFile);
     1012
     1013    if (RTFileIsValid(pStream->File.hFile))
     1014    {
     1015        RTFileClose(pStream->File.hFile);
     1016        LogRel(("VideoRec: Closed file '%s'\n", pStream->File.pszFile));
     1017    }
     1018
     1019    RTStrFree(pStream->File.pszFile);
     1020    pStream->File.pszFile = NULL;
     1021
     1022    return VINF_SUCCESS;
    8781023}
    8791024
     
    8841029 * @param   pCtx                Pointer to video recording context.
    8851030 * @param   uScreen             Screen number to record.
    886  * @param   pszFile             File to save recording to.
    887  * @param   uWidth              Target video resolution (width).
    888  * @param   uHeight             Target video resolution (height).
    889  * @param   uRate               Target encoding bit rate.
    890  * @param   uFPS                Target FPS (Frame Per Second).
    891  * @param   uMaxTimeS           Maximum time (in s) to record, or 0 for no time limit.
    892  * @param   uMaxSizeMB          Maximum file size (in MB) to record, or 0 for no limit.
    893  * @param   pszOptions          Additional options in "key=value" array format. Optional.
    894  */
    895 int VideoRecStreamInit(PVIDEORECCONTEXT pCtx, uint32_t uScreen, const char *pszFile,
    896                        uint32_t uWidth, uint32_t uHeight, uint32_t uRate, uint32_t uFPS,
    897                        uint32_t uMaxTimeS, uint32_t uMaxSizeMB, const char *pszOptions)
    898 {
    899     AssertPtrReturn(pCtx,    VERR_INVALID_POINTER);
    900     AssertPtrReturn(pszFile, VERR_INVALID_POINTER);
    901     AssertReturn(uWidth,     VERR_INVALID_PARAMETER);
    902     AssertReturn(uHeight,    VERR_INVALID_PARAMETER);
    903     AssertReturn(uRate,      VERR_INVALID_PARAMETER);
    904     AssertReturn(uFPS,       VERR_INVALID_PARAMETER);
    905     /* pszOptions is optional. */
     1031 */
     1032int VideoRecStreamInit(PVIDEORECCONTEXT pCtx, uint32_t uScreen)
     1033{
     1034    AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
    9061035
    9071036    PVIDEORECSTREAM pStream = videoRecStreamGet(pCtx, uScreen);
     
    9091038        return VERR_NOT_FOUND;
    9101039
    911     pCtx->uMaxTimeMs = (uMaxTimeS > 0 ? RTTimeProgramMilliTS() + uMaxTimeS * 1000 : 0);
    912     pCtx->uMaxSizeMB = uMaxSizeMB;
    913 
    914     pStream->Video.uWidth  = uWidth;
    915     pStream->Video.uHeight = uHeight;
     1040    int rc = videoRecStreamOpenFile(pStream, &pCtx->Cfg);
     1041    if (RT_FAILURE(rc))
     1042        return rc;
     1043
     1044    PVIDEORECCFG pCfg = &pCtx->Cfg;
     1045
     1046    pStream->pCtx          = pCtx;
     1047    /** @todo Make the following parameters configurable on a per-stream basis? */
     1048    pStream->Video.uWidth  = pCfg->Video.uWidth;
     1049    pStream->Video.uHeight = pCfg->Video.uHeight;
    9161050
    9171051#ifndef VBOX_VIDEOREC_WITH_QUEUE
     
    9331067
    9341068#ifdef VBOX_WITH_LIBVPX
    935     pVC->VPX.uEncoderDeadline = VPX_DL_REALTIME;
    936 
    9371069    vpx_codec_err_t rcv = vpx_codec_enc_config_default(DEFAULTCODEC, &pVC->VPX.Cfg, 0);
    9381070    if (rcv != VPX_CODEC_OK)
     
    9431075#endif
    9441076
    945     com::Utf8Str options(pszOptions);
    946     size_t pos = 0;
    947 
    948     /* By default we only enable video recording for now.
    949      * Audio support is considered as being experimental. There be dragons! */
    950     bool     fHasVideoTrack = true;
     1077    pStream->Video.uDelayMs = 1000 / pCfg->Video.uFPS;
     1078
     1079    switch (pStream->enmDst)
     1080    {
     1081        case VIDEORECDEST_FILE:
     1082        {
     1083            rc = pStream->File.pWEBM->CreateEx(pStream->File.pszFile, &pStream->File.hFile,
    9511084#ifdef VBOX_WITH_AUDIO_VIDEOREC
    952     bool     fHasAudioTrack = false;
    953     /* By default we use 48kHz, 16-bit, stereo for the audio track. */
    954     uint16_t uAudioHz       = 48000;
    955     uint8_t  uAudioBits     = 16;
    956     uint8_t  uAudioChannels = 2;
    957 #endif
    958 
    959     com::Utf8Str key, value;
    960     while ((pos = options.parseKeyValue(key, value, pos)) != com::Utf8Str::npos)
    961     {
    962         if (key.compare("vc_quality", Utf8Str::CaseInsensitive) == 0)
    963         {
    964             if (value.compare("realtime", Utf8Str::CaseInsensitive) == 0)
     1085                                               pCfg->Audio.fEnabled ? WebMWriter::AudioCodec_Opus : WebMWriter::AudioCodec_None,
     1086#else
     1087                                               WebMWriter::AudioCodec_None,
     1088#endif
     1089                                               pCfg->Video.fEnabled ? WebMWriter::VideoCodec_VP8 : WebMWriter::VideoCodec_None);
     1090            if (RT_FAILURE(rc))
    9651091            {
    966 #ifdef VBOX_WITH_LIBVPX
    967                 pVC->VPX.uEncoderDeadline = VPX_DL_REALTIME;
    968 #endif
     1092                LogRel(("VideoRec: Failed to create the capture output file '%s' (%Rrc)\n", pStream->File.pszFile, rc));
     1093                break;
    9691094            }
    970             else if (value.compare("good", Utf8Str::CaseInsensitive) == 0)
     1095
     1096            const char *pszFile = pStream->File.pszFile;
     1097
     1098            if (pCfg->Video.fEnabled)
    9711099            {
    972                 pVC->VPX.uEncoderDeadline = 1000000 / uFPS;
     1100                rc = pStream->File.pWEBM->AddVideoTrack(pCfg->Video.uWidth, pCfg->Video.uHeight, pCfg->Video.uFPS,
     1101                                                        &pStream->uTrackVideo);
     1102                if (RT_FAILURE(rc))
     1103                {
     1104                    LogRel(("VideoRec: Failed to add video track to output file '%s' (%Rrc)\n", pszFile, rc));
     1105                    break;
     1106                }
     1107
     1108                LogRel(("VideoRec: Recording screen #%u with %RU32x%RU32 @ %RU32 kbps, %u FPS\n",
     1109                        uScreen, pCfg->Video.uWidth, pCfg->Video.uHeight, pCfg->Video.uRate, pCfg->Video.uFPS));
    9731110            }
    974             else if (value.compare("best", Utf8Str::CaseInsensitive) == 0)
     1111
     1112#ifdef VBOX_WITH_AUDIO_VIDEOREC
     1113            if (pCfg->Audio.fEnabled)
    9751114            {
    976 #ifdef VBOX_WITH_LIBVPX
    977                 pVC->VPX.uEncoderDeadline = VPX_DL_BEST_QUALITY;
    978 #endif
     1115                rc = pStream->File.pWEBM->AddAudioTrack(pCfg->Audio.uHz, pCfg->Audio.cChannels, pCfg->Audio.cBits,
     1116                                                        &pStream->uTrackAudio);
     1117                if (RT_FAILURE(rc))
     1118                {
     1119                    LogRel(("VideoRec: Failed to add audio track to output file '%s' (%Rrc)\n", pszFile, rc));
     1120                    break;
     1121                }
     1122
     1123                LogRel(("VideoRec: Recording audio in %RU16Hz, %RU8 bit, %RU8 %s\n",
     1124                        pCfg->Audio.uHz, pCfg->Audio.cBits, pCfg->Audio.cChannels, pCfg->Audio.cChannels ? "channel" : "channels"));
    9791125            }
    980             else
     1126#endif
     1127
     1128            if (   pCfg->Video.fEnabled
     1129#ifdef VBOX_WITH_AUDIO_VIDEOREC
     1130                || pCfg->Audio.fEnabled
     1131#endif
     1132               )
    9811133            {
    982                 LogRel(("VideoRec: Setting quality deadline to '%s'\n", value.c_str()));
    983                 pVC->VPX.uEncoderDeadline = value.toUInt32();
     1134                char szWhat[32] = { 0 };
     1135                if (pCfg->Video.fEnabled)
     1136                    RTStrCat(szWhat, sizeof(szWhat), "video");
     1137#ifdef VBOX_WITH_AUDIO_VIDEOREC
     1138                if (pCfg->Audio.fEnabled)
     1139                {
     1140                    if (pCfg->Video.fEnabled)
     1141                        RTStrCat(szWhat, sizeof(szWhat), " + ");
     1142                    RTStrCat(szWhat, sizeof(szWhat), "audio");
     1143                }
     1144#endif
     1145                LogRel(("VideoRec: Recording %s to '%s'\n", szWhat, pszFile));
    9841146            }
    985         }
    986         else if (key.compare("vc_enabled", Utf8Str::CaseInsensitive) == 0)
    987         {
    988 #ifdef VBOX_WITH_AUDIO_VIDEOREC
    989             if (value.compare("false", Utf8Str::CaseInsensitive) == 0)
    990             {
    991                 fHasVideoTrack = false;
    992                 LogRel(("VideoRec: Only audio will be recorded\n"));
    993             }
    994 #endif
    995         }
    996         else if (key.compare("ac_enabled", Utf8Str::CaseInsensitive) == 0)
    997         {
    998 #ifdef VBOX_WITH_AUDIO_VIDEOREC
    999             if (value.compare("false", Utf8Str::CaseInsensitive))
    1000             {
    1001                 fHasAudioTrack = false;
    1002                 LogRel(("VideoRec: Only video will be recorded\n"));
    1003             }
    1004 #endif
    1005         }
    1006         else if (key.compare("ac_profile", Utf8Str::CaseInsensitive) == 0)
    1007         {
    1008 #ifdef VBOX_WITH_AUDIO_VIDEOREC
    1009             if (value.compare("low", Utf8Str::CaseInsensitive) == 0)
    1010             {
    1011                 uAudioHz       = 8000;
    1012                 uAudioBits     = 16;
    1013                 uAudioChannels = 1;
    1014             }
    1015             else if (value.startsWith("med" /* "med[ium]" */, Utf8Str::CaseInsensitive) == 0)
    1016             {
    1017                 uAudioHz       = 22050;
    1018                 uAudioBits     = 16;
    1019                 uAudioChannels = 2;
    1020             }
    1021             else if (value.compare("high", Utf8Str::CaseInsensitive) == 0)
    1022             {
    1023                 /* Stay with the default set above. */
    1024             }
    1025 #endif
    1026         }
    1027         else
    1028             LogRel(("VideoRec: Unknown option '%s' (value '%s'), skipping\n", key.c_str(), value.c_str()));
    1029 
    1030     } /* while */
    1031 
    1032     uint64_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_WRITE;
    1033 #ifdef DEBUG
    1034     fOpen |= RTFILE_O_CREATE_REPLACE;
    1035 #else
    1036     /* Play safe: the file must not exist, overwriting is potentially
    1037      * hazardous as nothing prevents the user from picking a file name of some
    1038      * other important file, causing unintentional data loss. */
    1039     fOpen |= RTFILE_O_CREATE;
    1040 #endif
    1041 
    1042     int rc = pStream->pEBML->Create(pszFile, fOpen,
    1043 #ifdef VBOX_WITH_AUDIO_VIDEOREC
    1044                                     fHasAudioTrack ? WebMWriter::AudioCodec_Opus : WebMWriter::AudioCodec_None,
    1045 #else
    1046                                     WebMWriter::AudioCodec_None,
    1047 #endif
    1048                                     fHasVideoTrack ? WebMWriter::VideoCodec_VP8 : WebMWriter::VideoCodec_None);
     1147
     1148            break;
     1149        }
     1150
     1151        default:
     1152            AssertFailed(); /* Should never happen. */
     1153            rc = VERR_NOT_IMPLEMENTED;
     1154            break;
     1155    }
     1156
    10491157    if (RT_FAILURE(rc))
    1050     {
    1051         LogRel(("VideoRec: Failed to create the video capture output file '%s' (%Rrc)\n", pszFile, rc));
    10521158        return rc;
    1053     }
    1054 
    1055     pStream->Video.uDelayMs = 1000 / uFPS;
    1056 
    1057     if (fHasVideoTrack)
    1058     {
    1059         rc = pStream->pEBML->AddVideoTrack(uWidth, uHeight, uFPS, &pStream->uTrackVideo);
    1060         if (RT_FAILURE(rc))
    1061         {
    1062             LogRel(("VideoRec: Failed to add video track to output file '%s' (%Rrc)\n", pszFile, rc));
    1063             return rc;
    1064         }
    1065 
    1066         LogRel(("VideoRec: Recording screen #%u with %ux%u @ %u kbps, %u FPS\n",
    1067                 uScreen, uWidth, uHeight, uRate, uFPS));
    1068     }
    1069 
    1070 #ifdef VBOX_WITH_AUDIO_VIDEOREC
    1071     if (fHasAudioTrack)
    1072     {
    1073         rc = pStream->pEBML->AddAudioTrack(uAudioHz, uAudioChannels, uAudioBits, &pStream->uTrackAudio);
    1074         if (RT_FAILURE(rc))
    1075         {
    1076             LogRel(("VideoRec: Failed to add audio track to output file '%s' (%Rrc)\n", pszFile, rc));
    1077             return rc;
    1078         }
    1079 
    1080         LogRel(("VideoRec: Recording audio in %RU16Hz, %RU8 bit, %RU8 %s\n",
    1081                 uAudioHz, uAudioBits, uAudioChannels, uAudioChannels == 1 ? "channel" : "channels"));
    1082     }
    1083 #endif
    1084 
    1085     if (   fHasVideoTrack
    1086 #ifdef VBOX_WITH_AUDIO_VIDEOREC
    1087         || fHasAudioTrack
    1088 #endif
    1089        )
    1090     {
    1091         char szWhat[32] = { 0 };
    1092         if (fHasVideoTrack)
    1093             RTStrCat(szWhat, sizeof(szWhat), "video");
    1094 #ifdef VBOX_WITH_AUDIO_VIDEOREC
    1095         if (fHasAudioTrack)
    1096         {
    1097             if (fHasVideoTrack)
    1098                 RTStrCat(szWhat, sizeof(szWhat), " + ");
    1099             RTStrCat(szWhat, sizeof(szWhat), "audio");
    1100         }
    1101 #endif
    1102         LogRel(("Recording %s to '%s'\n", szWhat, pszFile));
    1103     }
    11041159
    11051160#ifdef VBOX_WITH_LIBVPX
    11061161    /* Target bitrate in kilobits per second. */
    1107     pVC->VPX.Cfg.rc_target_bitrate = uRate;
     1162    pVC->VPX.Cfg.rc_target_bitrate = pCfg->Video.uRate;
    11081163    /* Frame width. */
    1109     pVC->VPX.Cfg.g_w = uWidth;
     1164    pVC->VPX.Cfg.g_w = pCfg->Video.uWidth;
    11101165    /* Frame height. */
    1111     pVC->VPX.Cfg.g_h = uHeight;
     1166    pVC->VPX.Cfg.g_h = pCfg->Video.uHeight;
    11121167    /* 1ms per frame. */
    11131168    pVC->VPX.Cfg.g_timebase.num = 1;
     
    11201175    if (rcv != VPX_CODEC_OK)
    11211176    {
    1122         LogFlow(("Failed to initialize VP8 encoder %s", vpx_codec_err_to_string(rcv)));
     1177        LogFlow(("Failed to initialize VP8 encoder: %s\n", vpx_codec_err_to_string(rcv)));
    11231178        return VERR_INVALID_PARAMETER;
    11241179    }
    11251180
    1126     if (!vpx_img_alloc(&pVC->VPX.RawImage, VPX_IMG_FMT_I420, uWidth, uHeight, 1))
    1127     {
    1128         LogFlow(("Failed to allocate image %dx%d", uWidth, uHeight));
     1181    if (!vpx_img_alloc(&pVC->VPX.RawImage, VPX_IMG_FMT_I420, pCfg->Video.uWidth, pCfg->Video.uHeight, 1))
     1182    {
     1183        LogFlow(("Failed to allocate image %RU32x%RU32\n", pCfg->Video.uWidth, pCfg->Video.uHeight));
    11291184        return VERR_NO_MEMORY;
    11301185    }
     
    11391194
    11401195/**
    1141  * VideoRec utility function to check if recording is enabled.
    1142  *
    1143  * @returns true if recording is enabled.
    1144  * @param   pCtx                Pointer to video recording context.
    1145  */
    1146 bool VideoRecIsEnabled(PVIDEORECCONTEXT pCtx)
    1147 {
    1148     if (!pCtx)
    1149         return false;
    1150 
    1151     return ASMAtomicReadBool(&pCtx->fEnabled);
    1152 }
    1153 
    1154 /**
    1155  * VideoRec utility function to check if recording engine is ready to accept a new frame
    1156  * for the given screen.
     1196 * Returns which recording features currently are enabled for a given configuration.
     1197 *
     1198 * @returns Enabled video recording features.
     1199 * @param   pCtx                Pointer to recording configuration.
     1200 */
     1201VIDEORECFEATURES VideoRecGetEnabled(PVIDEORECCFG pCfg)
     1202{
     1203    if (   !pCfg
     1204        || !pCfg->fEnabled)
     1205    {
     1206        return VIDEORECFEATURE_NONE;
     1207    }
     1208
     1209    VIDEORECFEATURES fFeatures = VIDEORECFEATURE_NONE;
     1210
     1211    if (pCfg->Video.fEnabled)
     1212        fFeatures |= VIDEORECFEATURE_VIDEO;
     1213
     1214    if (pCfg->Audio.fEnabled)
     1215        fFeatures |= VIDEORECFEATURE_AUDIO;
     1216
     1217    return fFeatures;
     1218}
     1219
     1220/**
     1221 * Checks if recording engine is ready to accept a new frame for the given screen.
    11571222 *
    11581223 * @returns true if recording engine is ready.
     
    11841249
    11851250/**
    1186  * VideoRec utility function to check if a specified limit for recording
    1187  * has been reached.
     1251 * Returns whether video recording for a given recording context is active or not.
     1252 *
     1253 * @returns true if active, false if not.
     1254 * @param   pCtx                 Pointer to video recording context.
     1255 */
     1256bool VideoRecIsActive(PVIDEORECCONTEXT pCtx)
     1257{
     1258    if (!pCtx)
     1259        return false;
     1260
     1261    return ASMAtomicReadBool(&pCtx->fEnabled);
     1262}
     1263
     1264/**
     1265 * Checks if a specified limit for recording has been reached.
    11881266 *
    11891267 * @returns true if any limit has been reached.
     
    11921270 * @param   tsNowMs             Current time stamp (in ms).
    11931271 */
    1194 
    11951272bool VideoRecIsLimitReached(PVIDEORECCONTEXT pCtx, uint32_t uScreen, uint64_t tsNowMs)
    11961273{
     
    12021279    }
    12031280
    1204     if (   pCtx->uMaxTimeMs
    1205         && tsNowMs >= pCtx->uMaxTimeMs)
     1281    const PVIDEORECCFG pCfg = &pCtx->Cfg;
     1282
     1283    if (   pCfg->uMaxTimeS
     1284        && tsNowMs >= pCtx->tsStartMs + (pCfg->uMaxTimeS * 1000))
    12061285    {
    12071286        return true;
    12081287    }
    12091288
    1210     if (pCtx->uMaxSizeMB)
    1211     {
    1212         uint64_t sizeInMB = pStream->pEBML->GetFileSize() / (1024 * 1024);
    1213         if(sizeInMB >= pCtx->uMaxSizeMB)
     1289    if (pCfg->enmDst == VIDEORECDEST_FILE)
     1290    {
     1291
     1292        if (pCfg->File.uMaxSizeMB)
     1293        {
     1294            uint64_t sizeInMB = pStream->File.pWEBM->GetFileSize() / (1024 * 1024);
     1295            if(sizeInMB >= pCfg->File.uMaxSizeMB)
     1296                return true;
     1297        }
     1298
     1299        /* Check for available free disk space */
     1300        if (   pStream->File.pWEBM
     1301            && pStream->File.pWEBM->GetAvailableSpace() < 0x100000) /**@todo r=andy WTF? Fix this. */
     1302        {
     1303            LogRel(("VideoRec: Not enough free storage space available, stopping video capture\n"));
    12141304            return true;
    1215     }
    1216 
    1217     /* Check for available free disk space */
    1218     if (   pStream->pEBML
    1219         && pStream->pEBML->GetAvailableSpace() < 0x100000) /**@todo r=andy WTF? Fix this. */
    1220     {
    1221         LogRel(("VideoRec: Not enough free storage space available, stopping video capture\n"));
    1222         return true;
     1305        }
    12231306    }
    12241307
     
    12271310
    12281311/**
    1229  * VideoRec utility function to encode the source image and write the encoded
    1230  * image to target file.
     1312 * Encodes the source image and write the encoded image to the stream's destination.
    12311313 *
    12321314 * @returns IPRT status code.
     
    12361318static int videoRecEncodeAndWrite(PVIDEORECSTREAM pStream, PVIDEORECVIDEOFRAME pFrame)
    12371319{
     1320    AssertPtrReturn(pStream, VERR_INVALID_POINTER);
     1321    AssertPtrReturn(pFrame,  VERR_INVALID_POINTER);
     1322
    12381323    int rc;
    12391324
    1240     PVIDEORECVIDEOCODEC pVC = &pStream->Video.Codec;
    1241 
     1325    AssertPtr(pStream->pCtx);
     1326    PVIDEORECCFG        pCfg = &pStream->pCtx->Cfg;
     1327    PVIDEORECVIDEOCODEC pVC  = &pStream->Video.Codec;
    12421328#ifdef VBOX_WITH_LIBVPX
    12431329    /* Presentation Time Stamp (PTS). */
     
    12451331    vpx_codec_err_t rcv = vpx_codec_encode(&pVC->VPX.Ctx,
    12461332                                           &pVC->VPX.RawImage,
    1247                                            pts                       /* Time stamp */,
    1248                                            pStream->Video.uDelayMs   /* How long to show this frame */,
    1249                                            0                         /* Flags */,
    1250                                            pVC->VPX.uEncoderDeadline /* Quality setting */);
     1333                                           pts                                    /* Time stamp */,
     1334                                           pStream->Video.uDelayMs                /* How long to show this frame */,
     1335                                           0                                      /* Flags */,
     1336                                           pCfg->Video.Codec.VPX.uEncoderDeadline /* Quality setting */);
    12511337    if (rcv != VPX_CODEC_OK)
    12521338    {
     
    12681354            {
    12691355                WebMWriter::BlockData_VP8 blockData = { &pVC->VPX.Cfg, pPacket };
    1270                 rc = pStream->pEBML->WriteBlock(pStream->uTrackVideo, &blockData, sizeof(blockData));
     1356                rc = pStream->File.pWEBM->WriteBlock(pStream->uTrackVideo, &blockData, sizeof(blockData));
    12711357                break;
    12721358            }
     
    12861372
    12871373/**
    1288  * VideoRec utility function to convert RGB to YUV.
     1374 * Converts a RGB to YUV buffer.
    12891375 *
    12901376 * @returns IPRT status code.
     
    13651451
    13661452/**
    1367  * VideoRec utility function to copy a source video frame to the intermediate
    1368  * RGB buffer. This function is executed only once per time.
     1453 * Copies a source video frame to the intermediate RGB buffer.
     1454 * This function is executed only once per time.
    13691455 *
    13701456 * @thread  EMT
     
    13971483        return VERR_NOT_FOUND;
    13981484
    1399     int rc = RTCritSectEnter(&pStream->CritSect);
    1400     if (RT_FAILURE(rc))
    1401         return rc;
     1485    videoRecStreamLock(pStream);
     1486
     1487    int rc = VINF_SUCCESS;
    14021488
    14031489    do
     
    15701656    } while (0);
    15711657
    1572     int rc2 = RTCritSectLeave(&pStream->CritSect);
    1573     if (RT_SUCCESS(rc2))
    1574     {
    1575         if (   RT_SUCCESS(rc)
    1576             && rc != VINF_TRY_AGAIN) /* Only signal the thread if operation was successful. */
    1577         {
    1578             rc2 = RTSemEventSignal(pCtx->WaitEvent);
    1579         }
    1580     }
    1581 
    1582     if (RT_SUCCESS(rc))
    1583         rc = rc2;
     1658    videoRecStreamUnlock(pStream);
     1659
     1660    if (   RT_SUCCESS(rc)
     1661        && rc != VINF_TRY_AGAIN) /* Only signal the thread if operation was successful. */
     1662    {
     1663        int rc2 = RTSemEventSignal(pCtx->WaitEvent);
     1664        AssertRC(rc2);
     1665    }
    15841666
    15851667    return rc;
  • trunk/src/VBox/Main/src-client/VideoRec.h

    r65435 r68798  
    1919#define ____H_VIDEOREC
    2020
     21#include <VBox/com/array.h>
     22
    2123struct VIDEORECCONTEXT;
    2224typedef struct VIDEORECCONTEXT *PVIDEORECCONTEXT;
     
    2527typedef struct VIDEORECSTREAM *PVIDEORECSTREAM;
    2628
    27 int VideoRecContextCreate(uint32_t cScreens, PVIDEORECCONTEXT *ppCtx);
     29/**
     30 * Enumeration for video recording destinations.
     31 */
     32typedef enum VIDEORECDEST
     33{
     34    /** Invalid destination, do not use. */
     35    VIDEORECDEST_INVALID = 0,
     36    /** Write to a file. */
     37    VIDEORECDEST_FILE    = 1
     38} VIDEORECDEST;
     39
     40/**
     41 * Enumeration for definining a video / audio
     42 * profile setting.
     43 */
     44typedef enum VIDEORECPROFILE
     45{
     46    VIDEORECPROFILE_NONE   = 0,
     47    VIDEORECPROFILE_LOW,
     48    VIDEORECPROFILE_MEDIUM,
     49    VIDEORECPROFILE_HIGH,
     50    VIDEORECPROFILE_BEST,
     51    VIDEORECPROFILE_REALTIME
     52} VIDEORECPROFILE;
     53
     54/**
     55 * Structure for keeping a video recording configuration.
     56 */
     57typedef struct VIDEORECCFG
     58{
     59    /** Whether recording is enabled or not (as a whole). */
     60    bool                    fEnabled;
     61    /** */
     62    com::SafeArray<BOOL>    aScreens;
     63    /** Destination where to write the stream to. */
     64    VIDEORECDEST            enmDst;
     65    union
     66    {
     67        /**
     68         * Structure for keeping recording parameters if
     69         * destination is a file.
     70         */
     71        struct
     72        {
     73            BSTR            strFile;
     74            /** Maximum file size (in MB) to record. */
     75            uint32_t        uMaxSizeMB;
     76        } File;
     77    };
     78
     79#ifdef VBOX_WITH_AUDIO_VIDEOREC
     80    /**
     81     * Structure for keeping the audio recording parameters.
     82     */
     83    struct
     84    {
     85        bool                fEnabled;
     86        uint16_t            uHz;
     87        uint8_t             cBits;
     88        uint8_t             cChannels;
     89        VIDEORECPROFILE     enmProfile;
     90    } Audio;
     91#endif
     92
     93    /**
     94     * Structure for keeping the video recording parameters.
     95     */
     96    struct
     97    {
     98        bool                fEnabled;
     99        uint32_t            uWidth;
     100        uint32_t            uHeight;
     101        uint32_t            uRate;
     102        uint32_t            uFPS;
     103
     104#ifdef VBOX_WITH_LIBVPX
     105        union
     106        {
     107            struct
     108            {
     109                /** Encoder deadline. */
     110                unsigned int uEncoderDeadline;
     111            } VPX;
     112        } Codec;
     113#endif
     114
     115    } Video;
     116    /** Maximum time (in s) to record.
     117     *  Specify 0 to disable this check. */
     118    uint32_t                uMaxTimeS;
     119
     120    VIDEORECCFG& operator=(const VIDEORECCFG &that)
     121    {
     122        fEnabled = that.fEnabled;
     123        that.aScreens.cloneTo(aScreens);
     124        enmDst   = that.enmDst;
     125
     126        File.strFile    = that.File.strFile;
     127        File.uMaxSizeMB = that.File.uMaxSizeMB;
     128#ifdef VBOX_WITH_AUDIO_VIDEOREC
     129        Audio           = that.Audio;
     130#endif
     131        Video           = that.Video;
     132        uMaxTimeS       = that.uMaxTimeS;
     133        return *this;
     134    }
     135
     136} VIDEORECCFG, *PVIDEORECCFG;
     137
     138/** Stores video recording features. */
     139typedef uint32_t VIDEORECFEATURES;
     140
     141/** Video recording is disabled completely. */
     142#define VIDEORECFEATURE_NONE        0
     143/** Capturing video is enabled. */
     144#define VIDEORECFEATURE_VIDEO       RT_BIT(0)
     145/** Capturing audio is enabled. */
     146#define VIDEORECFEATURE_AUDIO       RT_BIT(1)
     147
     148int VideoRecContextCreate(uint32_t cScreens, PVIDEORECCFG pVideoRecCfg, PVIDEORECCONTEXT *ppCtx);
    28149int VideoRecContextDestroy(PVIDEORECCONTEXT pCtx);
    29150
    30 int  VideoRecStreamInit(PVIDEORECCONTEXT pCtx, uint32_t uScreen, const char *pszFile,
    31                         uint32_t uWidth, uint32_t uHeight, uint32_t uRate, uint32_t uFPS,
    32                         uint32_t uMaxTimeS, uint32_t uMaxFileSizeMB, const char *pszOptions);
     151int VideoRecStreamInit(PVIDEORECCONTEXT pCtx, uint32_t uScreen);
     152int VideoRecStreamUninit(PVIDEORECCONTEXT pCtx, uint32_t uScreen);
    33153
    34 bool VideoRecIsEnabled(PVIDEORECCONTEXT pCtx);
    35 int  VideoRecSendAudioFrame(PVIDEORECCONTEXT pCtx, const void *pvData, size_t cbData, uint64_t uTimestampMs);
    36 int  VideoRecSendVideoFrame(PVIDEORECCONTEXT pCtx, uint32_t uScreen,
     154VIDEORECFEATURES VideoRecGetEnabled(PVIDEORECCFG pCfg);
     155
     156int VideoRecSendAudioFrame(PVIDEORECCONTEXT pCtx, const void *pvData, size_t cbData, uint64_t uTimestampMs);
     157int VideoRecSendVideoFrame(PVIDEORECCONTEXT pCtx, uint32_t uScreen,
    37158                            uint32_t x, uint32_t y, uint32_t uPixelFormat, uint32_t uBPP,
    38159                            uint32_t uBytesPerLine, uint32_t uSrcWidth, uint32_t uSrcHeight,
    39160                            uint8_t *puSrcData, uint64_t uTimeStampMs);
    40161bool VideoRecIsReady(PVIDEORECCONTEXT pCtx, uint32_t uScreen, uint64_t uTimeStampMs);
     162bool VideoRecIsActive(PVIDEORECCONTEXT pCtx);
    41163bool VideoRecIsLimitReached(PVIDEORECCONTEXT pCtx, uint32_t uScreen, uint64_t tsNowMs);
    42164
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