VirtualBox

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


Ignore:
Timestamp:
Jun 24, 2024 5:43:00 PM (7 months ago)
Author:
vboxsync
Message:

Video Recording: Big revamp to improve overall performance. We now don't rely on the periodic display refresh callback anymore to render the entire framebuffer but now rely on delta updates ("dirty rectangles"). Also, we now only encode new frames when an area has changed. This also needed cursor position + change change notifications, as we render the cursor on the host side if mouse integration is enabled (requires 7.1 Guest Additions as of now). Optimized the BGRA32->YUV IV420 color space conversion as well as the overall amount of pixel data shuffled forth and back. Added a new testcase for the cropping/centering code. bugref:10650

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

Legend:

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

    r104799 r105006  
    76667666}
    76677667
     7668/**
     7669 * Sends a cursor shape change to the recording context.
     7670 *
     7671 * @returns VBox status code.
     7672 * @param   fVisible            Whether the mouse cursor actually is visible or not.
     7673 * @param   fAlpha              Whether the pixel data contains alpha channel information or not.
     7674 * @param   xHot                X hot position (in pixel) of the new cursor.
     7675 * @param   yHot                Y hot position (in pixel) of the new cursor.
     7676 * @param   uWidth              Width (in pixel) of the new cursor.
     7677 * @param   uHeight             Height (in pixel) of the new cursor.
     7678 * @param   pu8Shape            Pixel data of the new cursor.
     7679 * @param   cbShape             Size of \a pu8Shape (in bytes).
     7680 */
     7681int Console::i_recordingCursorShapeChange(bool fVisible, bool fAlpha, uint32_t xHot, uint32_t yHot, uint32_t uWidth, uint32_t uHeight, const uint8_t *pu8Shape, uint32_t cbShape)
     7682{
     7683    if (!mRecording.mCtx.IsStarted())
     7684        return VINF_SUCCESS;
     7685
     7686    return mRecording.mCtx.SendCursorShapeChange(fVisible, fAlpha, xHot, yHot, uWidth, uHeight, pu8Shape, cbShape,
     7687                                                 mRecording.mCtx.GetCurrentPTS());
     7688}
    76687689#endif /* VBOX_WITH_RECORDING */
    76697690
     
    77957816
    77967817    if (!mMouse.isNull())
    7797        mMouse->updateMousePointerShape(fVisible, fAlpha, xHot, yHot, width, height, pu8Shape, cbShape);
     7818       mMouse->i_updatePointerShape(fVisible, fAlpha, xHot, yHot, width, height, pu8Shape, cbShape);
    77987819
    77997820    com::SafeArray<BYTE> shape(cbShape);
     
    78017822        memcpy(shape.raw(), pu8Shape, cbShape);
    78027823    ::FireMousePointerShapeChangedEvent(mEventSource, fVisible, fAlpha, xHot, yHot, width, height, ComSafeArrayAsInParam(shape));
     7824
     7825#ifdef VBOX_WITH_RECORDING
     7826    i_recordingCursorShapeChange(fVisible, fAlpha, xHot, yHot, width, height, pu8Shape, cbShape);
     7827#endif
    78037828
    78047829#if 0
  • trunk/src/VBox/Main/src-client/DisplayImpl.cpp

    r104801 r105006  
    3434#include "ConsoleVRDPServer.h"
    3535#include "GuestImpl.h"
     36#include "MouseImpl.h"
    3637#include "VMMDev.h"
    3738
     
    6162# include <iprt/path.h>
    6263# include "Recording.h"
     64# include "RecordingUtils.h"
    6365
    6466# include <VBox/vmm/pdmapi.h>
     
    590592        maFramebuffers[uScreenId].updateImage.cbLine = 0;
    591593        maFramebuffers[uScreenId].pFramebuffer.setNull();
    592 #ifdef VBOX_WITH_RECORDING
    593         maFramebuffers[uScreenId].Recording.pSourceBitmap.setNull();
    594 #endif
    595594    }
    596595
     
    797796    const bool fDisabled = pFBInfo->fDisabled;
    798797
     798#ifdef VBOX_WITH_RECORDING
     799    /* Recording needs to be called before releasing the display's lock below. */
     800    i_recordingScreenChanged(uScreenId, pFBInfo);
     801#endif
     802
    799803    alock.release();
    800804
    801805    if (!pFramebuffer.isNull())
    802806    {
    803         HRESULT hr = pFramebuffer->NotifyChange(uScreenId, 0, 0, w, h); /** @todo origin */
    804         LogFunc(("NotifyChange hr %08X\n", hr));
    805         NOREF(hr);
     807        HRESULT hrc = pFramebuffer->NotifyChange(uScreenId, 0, 0, w, h); /** @todo origin */
     808        LogFunc(("NotifyChange hr %08X\n", hrc));
     809        RT_NOREF(hrc);
    806810    }
    807811
     
    828832        i_handleSetVisibleRegion(mcRectVisibleRegion, mpRectVisibleRegion);
    829833
    830 #ifdef VBOX_WITH_RECORDING
    831     i_recordingScreenChanged(uScreenId);
    832 #endif
    833 
    834834    LogRelFlowFunc(("[%d]: default format %d\n", uScreenId, pFBInfo->fDefaultFormat));
    835835
     
    909909            {
    910910                pFramebuffer->NotifyUpdate(x, y, w, h);
     911
     912#ifdef VBOX_WITH_RECORDING
     913                RECORDINGVIDEOFRAME Frame =
     914                {
     915                    (uint16_t)w, (uint16_t)h,
     916                    (uint8_t )pFBInfo->u16BitsPerPixel, RECORDINGPIXELFMT_BRGA32, pFBInfo->u32LineSize,
     917                    pFBInfo->pu8FramebufferVRAM + (y * pFBInfo->u32LineSize + x * (pFBInfo->u16BitsPerPixel / 8)),
     918                    pFBInfo->w * pFBInfo->u32LineSize,
     919                    (uint16_t)x, (uint16_t)y
     920                };
     921                i_recordingScreenUpdate(uScreenId, &Frame);
     922#endif
    911923            }
    912924        }
     
    943955                            pFBInfo->updateImage.pu8Address = pAddress;
    944956                            pFBInfo->updateImage.cbLine = ulBytesPerLine;
     957#ifdef VBOX_WITH_RECORDING
     958                            RECORDINGVIDEOFRAME Frame =
     959                            {
     960                                (uint16_t)ulWidth, (uint16_t)ulHeight,
     961                                (uint8_t )ulBitsPerPixel, RECORDINGPIXELFMT_BRGA32, ulBytesPerLine,
     962                                pAddress, ulHeight * ulBytesPerLine,
     963                                0, 0
     964                            };
     965
     966                            i_recordingScreenUpdate(uScreenId, &Frame);
     967#endif
    945968                        }
    946969
     
    972995                    i_checkCoordBounds(&x, &y, &w, &h, ulWidth, ulHeight);
    973996
    974                     if (w != 0 && h != 0)
     997                    if (   w != 0
     998                        && h != 0)
    975999                    {
    976                         const size_t cbData = w * h * 4;
     1000                        unsigned const uBytesPerPixel = ulBitsPerPixel / 8;
     1001
     1002                        const size_t cbData = w * h * uBytesPerPixel;
    9771003                        com::SafeArray<BYTE> image(cbData);
    9781004
    9791005                        uint8_t *pu8Dst = image.raw();
    980                         const uint8_t *pu8Src = pbAddress + ulBytesPerLine * y + x * 4;
    981 
    982                         int i;
    983                         for (i = y; i < y + h; ++i)
     1006                        const uint8_t *pu8Src = pbAddress + ulBytesPerLine * y + x * uBytesPerPixel;
     1007
     1008                        for (int i = y; i < y + h; ++i)
    9841009                        {
    985                             memcpy(pu8Dst, pu8Src, w * 4);
    986                             pu8Dst += w * 4;
     1010                            memcpy(pu8Dst, pu8Src, w * uBytesPerPixel);
     1011                            pu8Dst += w * uBytesPerPixel;
    9871012                            pu8Src += ulBytesPerLine;
    9881013                        }
    9891014
    9901015                        pFramebuffer->NotifyUpdateImage(x, y, w, h, ComSafeArrayAsInParam(image));
     1016
     1017#ifdef VBOX_WITH_RECORDING
     1018                        RECORDINGVIDEOFRAME Frame =
     1019                        {
     1020                            (uint16_t)w, (uint16_t)h,
     1021                            (uint8_t)ulBitsPerPixel, RECORDINGPIXELFMT_BRGA32, ulBytesPerLine,
     1022                            pu8Dst, h * ulBytesPerLine,
     1023                            (uint16_t)x, (uint16_t)y
     1024                        };
     1025
     1026                        i_recordingScreenUpdate(uScreenId, &Frame);
     1027#endif
    9911028                    }
    9921029                }
     
    11281165/** Updates the device's view of the host cursor handling capabilities.
    11291166 *  Calls into mpDrv->pUpPort. */
    1130 void Display::i_UpdateDeviceCursorCapabilities(void)
     1167void Display::i_updateDeviceCursorCapabilities(void)
    11311168{
    11321169    bool fRenderCursor = true;
     
    11721209    alock.release();  /* Release before calling up for lock order reasons. */
    11731210    mfHostCursorCapabilities = fHostCursorCapabilities;
    1174     i_UpdateDeviceCursorCapabilities();
     1211    i_updateDeviceCursorCapabilities();
    11751212    return S_OK;
    11761213}
     
    14191456
    14201457    i_VideoAccelVRDP(fConnect, c);
    1421     i_UpdateDeviceCursorCapabilities();
     1458    i_updateDeviceCursorCapabilities();
    14221459}
    14231460
     
    21502187 * @returns VBox status code.
    21512188 * @param   fForce              Whether to force invalidation or not. Default is @c false.
     2189 *
     2190 * @note    Takes the display's read lock.
    21522191 */
    21532192int Display::i_recordingInvalidate(bool fForce /* = false */)
     
    21562195    if (!pCtx)
    21572196        return VINF_SUCCESS;
     2197
     2198    LogFlowFuncEnter();
     2199
     2200    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    21582201
    21592202    /*
     
    21692212        maRecordingEnabled[uScreen] = fStreamEnabled;
    21702213
    2171         if (fChanged && uScreen < mcMonitors)
    2172             i_recordingScreenChanged(uScreen);
     2214        if (   fStreamEnabled
     2215            && fChanged)
     2216        {
     2217            DISPLAYFBINFO const *pFBInfo = &maFramebuffers[uScreen];
     2218            /* ignore rc */ i_recordingScreenChanged(uScreen, pFBInfo);
     2219        }
    21732220    }
    21742221
     
    21792226 * Called when the recording state of a screen got changed.
    21802227 *
     2228 * @returns VBox status code.
    21812229 * @param   uScreenId           ID of screen for which the recording state got changed.
     2230 * @param   pFBInfo             Frame buffer information to use.
    21822231 */
    2183 void Display::i_recordingScreenChanged(unsigned uScreenId)
    2184 {
     2232int Display::i_recordingScreenChanged(unsigned uScreenId, const DISPLAYFBINFO *pFBInfo)
     2233{
     2234    AssertReturn(uScreenId < mcMonitors, VERR_INVALID_PARAMETER);
     2235
    21852236    RecordingContext *pCtx = mParent->i_recordingGetContext();
    21862237
    2187     i_UpdateDeviceCursorCapabilities();
     2238    i_updateDeviceCursorCapabilities();
    21882239    if (   RT_LIKELY(!maRecordingEnabled[uScreenId])
    21892240        || !pCtx || !pCtx->IsStarted())
    21902241    {
    21912242        /* Skip recording this screen. */
    2192         return;
    2193     }
    2194 
    2195     /* Get a new source bitmap which will be used by video recording code. */
    2196     ComPtr<IDisplaySourceBitmap> pSourceBitmap;
    2197     QuerySourceBitmap(uScreenId, pSourceBitmap.asOutParam());
    2198 
    2199     int vrc2 = RTCritSectEnter(&mVideoRecLock);
    2200     if (RT_SUCCESS(vrc2))
    2201     {
    2202         maFramebuffers[uScreenId].Recording.pSourceBitmap = pSourceBitmap;
    2203 
    2204         vrc2 = RTCritSectLeave(&mVideoRecLock);
    2205         AssertRC(vrc2);
    2206     }
     2243        return VINF_SUCCESS;
     2244    }
     2245
     2246    LogFlowFuncEnter();
     2247
     2248    RECORDINGSURFACEINFO ScreenInfo;
     2249    ScreenInfo.uWidth      = pFBInfo->w;
     2250    ScreenInfo.uHeight     = pFBInfo->h;
     2251    ScreenInfo.uBPP        = (uint8_t)pFBInfo->u16BitsPerPixel;
     2252    ScreenInfo.enmPixelFmt = RECORDINGPIXELFMT_BRGA32; /** @todo Does this apply everywhere? */
     2253
     2254    uint64_t const tsNowMs = pCtx->GetCurrentPTS();
     2255
     2256    int vrc = pCtx->SendScreenChange(uScreenId, &ScreenInfo, tsNowMs);
     2257    if (RT_SUCCESS(vrc))
     2258    {
     2259        /** @todo BUGBUG For whatever reason pFBInfo contains a wrong pFBInfo->u32LineSize
     2260         *        when shutting down a VM. So (re-)calculate the line size based on the framebuffer's
     2261         *        BPP + width parameters.
     2262         *
     2263         *        So fend off any requests here which look fishy before sending a full screen update. */
     2264        if (   !pFBInfo->u16BitsPerPixel
     2265            ||  pFBInfo->u16BitsPerPixel % 2 != 0
     2266            || !pFBInfo->w
     2267            || !pFBInfo->h
     2268            ||  pFBInfo->u32LineSize != pFBInfo->w * (pFBInfo->u16BitsPerPixel / 8))
     2269        {
     2270            vrc = VERR_INVALID_PARAMETER;
     2271        }
     2272        else
     2273        {
     2274            /* Make sure that we get the latest mouse pointer shape required for recording. */
     2275            MousePointerData pointerData;
     2276            mParent->i_getMouse()->i_getPointerShape(pointerData);
     2277            mParent->i_recordingCursorShapeChange(pointerData.fVisible, pointerData.fAlpha,
     2278                                                  pointerData.hotX, pointerData.hotY,
     2279                                                  pointerData.width, pointerData.height,
     2280                                                  pointerData.pu8Shape, pointerData.cbShape);
     2281            /* Send the full screen update. */
     2282            RECORDINGVIDEOFRAME Frame =
     2283            {
     2284                (uint16_t)pFBInfo->w, (uint16_t)pFBInfo->h,
     2285                (uint8_t)pFBInfo->u16BitsPerPixel, RECORDINGPIXELFMT_BRGA32, pFBInfo->u32LineSize, //pFBInfo->w * (pFBInfo->u16BitsPerPixel / 8),
     2286                pFBInfo->pu8FramebufferVRAM, pFBInfo->h * pFBInfo->u32LineSize, 0, 0
     2287            };
     2288
     2289            vrc = i_recordingScreenUpdate(uScreenId, &Frame);
     2290        }
     2291    }
     2292
     2293    LogFlowFuncLeaveRC(vrc);
     2294    return vrc;
     2295}
     2296
     2297/**
     2298 * Called when a part of a screen got updated.
     2299 *
     2300 * @returns VBox status code.
     2301 * @param   uScreenId           ID of screen which got updated.
     2302 * @param   pFrame              Video frama to send.
     2303 */
     2304int Display::i_recordingScreenUpdate(unsigned uScreenId, PRECORDINGVIDEOFRAME pFrame)
     2305{
     2306    if (!maRecordingEnabled[uScreenId])
     2307        return VINF_NO_CHANGE;
     2308
     2309    AssertPtr(mParent);
     2310    RecordingContext *pCtx = mParent->i_recordingGetContext();
     2311    if (!pCtx)
     2312        return VINF_SUCCESS;
     2313
     2314    /* If the recording context has reached the configured recording
     2315     * limit, disable recording. */
     2316    if (pCtx->IsLimitReached())
     2317    {
     2318        mParent->i_onRecordingChange(FALSE /* Disable */);
     2319        return VINF_SUCCESS;
     2320    }
     2321
     2322    uint64_t const tsNowMs = pCtx->GetCurrentPTS();
     2323
     2324    int vrc = VINF_NO_CHANGE;
     2325
     2326    if (   pCtx->IsStarted()
     2327        && pCtx->IsFeatureEnabled(RecordingFeature_Video))
     2328    {
     2329        STAM_PROFILE_START(&Stats.Monitor[uScreenId].Recording.profileRecording, a);
     2330
     2331        vrc = pCtx->SendVideoFrame(uScreenId, pFrame, tsNowMs);
     2332
     2333        STAM_PROFILE_STOP(&Stats.Monitor[uScreenId].Recording.profileRecording, a);
     2334    }
     2335
     2336    return vrc;
     2337}
     2338
     2339/**
     2340 * Called when the mouse cursor position has changed within the guest.
     2341 *
     2342 * @returns VBox status code.
     2343 * @param   uScreenId           ID of screen.
     2344 * @param   fFlags              Position flags. Not used yet.
     2345 * @param   x                   X position of the mouse cursor (within guest).
     2346 * @param   y                   Y position of the mouse cursor (within guest).
     2347 *
     2348 * @note    Requires Guest Additions installed + mouse integration enabled.
     2349 */
     2350int Display::i_recordingCursorPositionChange(unsigned uScreenId, uint32_t fFlags, int32_t x, int32_t y)
     2351{
     2352    RT_NOREF(fFlags);
     2353
     2354#if 0 /** @todo For whatever reason we always report SVGA_ID_INVALID here. Needs investigation. */
     2355    if (   uScreenId > mcMonitors /* Might be SVGA_ID_INVALID. */
     2356        || !maRecordingEnabled[uScreenId])
     2357        return VINF_SUCCESS;
     2358#endif
     2359
     2360    AssertPtr(mParent);
     2361    RecordingContext *pCtx = mParent->i_recordingGetContext();
     2362    if (!pCtx)
     2363        return VINF_SUCCESS;
     2364
     2365    /* If the recording context has reached the configured recording
     2366     * limit, disable recording. */
     2367    if (pCtx->IsLimitReached())
     2368    {
     2369        mParent->i_onRecordingChange(FALSE /* Disable */);
     2370        return VINF_SUCCESS;
     2371    }
     2372
     2373    if (   pCtx->IsStarted()
     2374        && pCtx->IsFeatureEnabled(RecordingFeature_Video))
     2375    {
     2376        return pCtx->SendCursorPositionChange(uScreenId, x, y, pCtx->GetCurrentPTS());
     2377    }
     2378
     2379    return VINF_SUCCESS;
    22072380}
    22082381#endif /* VBOX_WITH_RECORDING */
     
    30243197        }
    30253198    }
    3026 
    3027 #ifdef VBOX_WITH_RECORDING
    3028     AssertPtr(pDisplay->mParent);
    3029     RecordingContext *pCtx = pDisplay->mParent->i_recordingGetContext();
    3030 
    3031     if (   pCtx
    3032         && pCtx->IsStarted()
    3033         && pCtx->IsFeatureEnabled(RecordingFeature_Video))
    3034     {
    3035         do
    3036         {
    3037             /* If the recording context has reached the configured recording
    3038              * limit, disable recording. */
    3039             if (pCtx->IsLimitReached())
    3040             {
    3041                 pDisplay->mParent->i_onRecordingChange(FALSE /* Disable */);
    3042                 break;
    3043             }
    3044 
    3045             STAM_PROFILE_START(&pDisplay->Stats.Recording.profileRecording, b);
    3046 
    3047             uint64_t tsNowMs = RTTimeProgramMilliTS();
    3048             for (uScreenId = 0; uScreenId < pDisplay->mcMonitors; uScreenId++)
    3049             {
    3050                 if (!pDisplay->maRecordingEnabled[uScreenId])
    3051                     continue;
    3052 
    3053                 if (!pCtx->NeedsUpdate(uScreenId, tsNowMs))
    3054                     continue;
    3055 
    3056                 STAM_PROFILE_START(&pDisplay->Stats.Monitor[uScreenId].Recording.profileRecording, c);
    3057 
    3058                 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[uScreenId];
    3059                 if (!pFBInfo->fDisabled)
    3060                 {
    3061                     ComPtr<IDisplaySourceBitmap> pSourceBitmap;
    3062                     int vrc2 = RTCritSectEnter(&pDisplay->mVideoRecLock);
    3063                     if (RT_SUCCESS(vrc2))
    3064                     {
    3065                         pSourceBitmap = pFBInfo->Recording.pSourceBitmap;
    3066                         RTCritSectLeave(&pDisplay->mVideoRecLock);
    3067                     }
    3068 
    3069                     if (!pSourceBitmap.isNull())
    3070                     {
    3071                         BYTE *pbAddress = NULL;
    3072                         ULONG ulWidth = 0;
    3073                         ULONG ulHeight = 0;
    3074                         ULONG ulBitsPerPixel = 0;
    3075                         ULONG ulBytesPerLine = 0;
    3076                         BitmapFormat_T bitmapFormat = BitmapFormat_Opaque;
    3077                         HRESULT hrc = pSourceBitmap->QueryBitmapInfo(&pbAddress,
    3078                                                                      &ulWidth,
    3079                                                                      &ulHeight,
    3080                                                                      &ulBitsPerPixel,
    3081                                                                      &ulBytesPerLine,
    3082                                                                      &bitmapFormat);
    3083                         if (SUCCEEDED(hrc) && pbAddress)
    3084                             vrc = pCtx->SendVideoFrame(uScreenId, 0, 0, BitmapFormat_BGR,
    3085                                                        ulBitsPerPixel, ulBytesPerLine, ulWidth, ulHeight,
    3086                                                        pbAddress, tsNowMs);
    3087                         else
    3088                             vrc = VERR_NOT_SUPPORTED;
    3089 
    3090                         pSourceBitmap.setNull();
    3091                     }
    3092                     else
    3093                         vrc = VERR_NOT_SUPPORTED;
    3094 
    3095                     if (vrc == VINF_TRY_AGAIN)
    3096                         break;
    3097                 }
    3098 
    3099                 STAM_PROFILE_STOP(&pDisplay->Stats.Monitor[uScreenId].Recording.profileRecording, c);
    3100             }
    3101 
    3102             STAM_PROFILE_STOP(&pDisplay->Stats.Recording.profileRecording, b);
    3103 
    3104         } while (0);
    3105     }
    3106 #endif /* VBOX_WITH_RECORDING */
    31073199
    31083200    STAM_PROFILE_STOP(&pDisplay->Stats.profileDisplayRefreshCallback, a);
     
    36933785    }
    36943786    ::FireCursorPositionChangedEvent(pThis->mParent->i_getEventSource(), RT_BOOL(fFlags & VBVA_CURSOR_VALID_DATA), x, y);
     3787
     3788# ifdef VBOX_WITH_RECORDING
     3789    pThis->i_recordingCursorPositionChange(aScreenId, fFlags, x, y);
     3790# endif
    36953791}
    36963792
  • trunk/src/VBox/Main/src-client/DrvAudioRec.cpp

    r104635 r105006  
    318318            /* Make sure to let the driver backend know that we need the audio data in
    319319             * a specific sampling rate the codec is optimized for. */
    320             pCfgAcq->Props = pCodec->Parms.Audio.PCMProps;
     320            pCfgAcq->Props = pCodec->Parms.u.Audio.PCMProps;
    321321
    322322            /* Every codec frame marks a period for now. Optimize this later. */
     
    491491                                                       const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
    492492{
    493     RT_NOREF(pInterface);
    494     PAVRECSTREAM pStreamAV = (PAVRECSTREAM)pStream;
     493    PDRVAUDIORECORDING pThis = RT_FROM_CPP_MEMBER(pInterface, DRVAUDIORECORDING, IHostAudio);
     494    PAVRECSTREAM pStreamAV   = (PAVRECSTREAM)pStream;
    495495    AssertPtrReturn(pStreamAV, VERR_INVALID_POINTER);
    496496    if (cbBuf)
     
    561561            if (cbSrc == cbFrame) /* Only send full codec frames. */
    562562            {
    563                 vrc = pRecStream->SendAudioFrame(pStreamAV->pvSrcBuf, cbSrc, RTTimeProgramMilliTS());
     563                AssertPtr(pThis->pRecCtx);
     564                vrc = pRecStream->SendAudioFrame(pStreamAV->pvSrcBuf, cbSrc, pThis->pRecCtx->GetCurrentPTS());
    564565                if (RT_FAILURE(vrc))
    565566                    break;
  • trunk/src/VBox/Main/src-client/MouseImpl.cpp

    r99739 r105006  
    323323}
    324324
    325 void Mouse::updateMousePointerShape(bool fVisible, bool fAlpha,
    326                                     uint32_t hotX, uint32_t hotY,
    327                                     uint32_t width, uint32_t height,
    328                                     const uint8_t *pu8Shape, uint32_t cbShape)
     325/**
     326 * Updates the pointer shape data.
     327 *
     328 * @returns VBox status code.
     329 * @param   fVisible            Whether the mouse cursor actually is visible or not.
     330 * @param   fAlpha              Whether the pixel data contains an alpha mask or not.
     331 * @param   uHotX               X hot position (in pixel) of the new cursor.
     332 * @param   uHotY               Y hot position (in pixel) of the new cursor.
     333 * @param   uWidth              Width (in pixel) of the new cursor.
     334 * @param   uHeight             Height (in pixel) of the new cursor.
     335 * @param   pu8Shape            Pixel data of the new cursor.
     336 * @param   cbShape             Size of \a pu8Shape (in bytes).
     337 *
     338 * @note    Takes the write lock.
     339 */
     340int Mouse::i_updatePointerShape(bool fVisible, bool fAlpha,
     341                                uint32_t uHotX, uint32_t uHotY,
     342                                uint32_t uWidth, uint32_t uHeight,
     343                                const uint8_t *pu8Shape, uint32_t cbShape)
    329344{
    330345    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    331346
    332     RTMemFree(mPointerData.pu8Shape);
    333     mPointerData.pu8Shape = NULL;
    334     mPointerData.cbShape = 0;
    335 
    336     mPointerData.fVisible = fVisible;
    337     mPointerData.fAlpha   = fAlpha;
    338     mPointerData.hotX     = hotX;
    339     mPointerData.hotY     = hotY;
    340     mPointerData.width    = width;
    341     mPointerData.height   = height;
    342     if (cbShape)
    343     {
    344         mPointerData.pu8Shape = (uint8_t *)RTMemDup(pu8Shape, cbShape);
    345         if (mPointerData.pu8Shape)
    346         {
    347             mPointerData.cbShape = cbShape;
    348         }
    349     }
    350 
    351     mPointerShape.setNull();
     347    mPointerData.Destroy(); /* Destroy old data first. */
     348
     349    int vrc = mPointerData.Init(fVisible, fAlpha, uHotX, uHotY, uWidth, uHeight, pu8Shape, cbShape);
     350    if (RT_SUCCESS(vrc))
     351    {
     352        mPointerShape.setNull();
     353    }
     354
     355    return vrc;
    352356}
    353357
     
    11241128{
    11251129    return RT_BOOL(mfVMMDevGuestCaps & VMMDEV_MOUSE_GUEST_NEEDS_HOST_CURSOR);
     1130}
     1131
     1132
     1133/**
     1134 * Returns the current mouse pointer data.
     1135 *
     1136 * @returns VBox status code.
     1137 * @param   aData               Where to return the current mouse pointer data.
     1138 */
     1139int Mouse::i_getPointerShape(MousePointerData &aData)
     1140{
     1141    AutoReadLock aLock(this COMMA_LOCKVAL_SRC_POS);
     1142
     1143    return aData.Init(mPointerData);
    11261144}
    11271145
  • trunk/src/VBox/Main/src-client/Recording.cpp

    r98278 r105006  
    6868
    6969
     70
     71RecordingCursorState::RecordingCursorState()
     72    : m_fFlags(VBOX_RECORDING_CURSOR_F_NONE)
     73{
     74    m_Shape.Pos.x = UINT16_MAX;
     75    m_Shape.Pos.y = UINT16_MAX;
     76
     77    RT_ZERO(m_Shape);
     78}
     79
     80RecordingCursorState::~RecordingCursorState()
     81{
     82    Destroy();
     83}
     84
     85/**
     86 * Destroys a cursor state.
     87 */
     88void RecordingCursorState::Destroy(void)
     89{
     90    RecordingVideoFrameDestroy(&m_Shape);
     91}
     92
     93/**
     94 * Creates or updates the cursor shape.
     95 *
     96 * @returns VBox status code.
     97 * @param   fAlpha              Whether the pixel data contains alpha channel information or not.
     98 * @param   uWidth              Width (in pixel) of new cursor shape.
     99 * @param   uHeight             Height (in pixel) of new cursor shape.
     100 * @param   pu8Shape            Pixel data of new cursor shape.
     101 * @param   cbShape             Bytes of \a pu8Shape.
     102 */
     103int RecordingCursorState::CreateOrUpdate(bool fAlpha, uint32_t uWidth, uint32_t uHeight, const uint8_t *pu8Shape, size_t cbShape)
     104{
     105    int vrc;
     106
     107    uint32_t fFlags = RECORDINGVIDEOFRAME_F_VISIBLE;
     108
     109    const uint8_t uBPP = 32; /* Seems to be fixed. */
     110
     111    uint32_t offShape;
     112    if (fAlpha)
     113    {
     114        /* Calculate the offset to the actual pixel data. */
     115        offShape = (uWidth + 7) / 8 * uHeight; /* size of the AND mask */
     116        offShape = (offShape + 3) & ~3;
     117        AssertReturn(offShape <= cbShape, VERR_INVALID_PARAMETER);
     118        fFlags |= RECORDINGVIDEOFRAME_F_BLIT_ALPHA;
     119    }
     120    else
     121        offShape = 0;
     122
     123    /* Cursor shape size has become bigger? Reallocate. */
     124    if (cbShape > m_Shape.cbBuf)
     125    {
     126        RecordingVideoFrameDestroy(&m_Shape);
     127        vrc = RecordingVideoFrameInit(&m_Shape, fFlags, uWidth, uHeight, 0 /* posX */, 0 /* posY */,
     128                                      uBPP, RECORDINGPIXELFMT_BRGA32);
     129    }
     130    else /* Otherwise just zero out first. */
     131    {
     132        RecordingVideoFrameClear(&m_Shape);
     133        vrc = VINF_SUCCESS;
     134    }
     135
     136    if (RT_SUCCESS(vrc))
     137        vrc = RecordingVideoFrameBlitRaw(&m_Shape, 0, 0, &pu8Shape[offShape], cbShape - offShape, 0, 0, uWidth, uHeight, uWidth * 4 /* BPP */, uBPP,
     138                                         m_Shape.Info.enmPixelFmt);
     139#ifdef DEBUG_andy_disabled
     140    RecordingUtilsDbgDumpVideoFrameEx(&m_Shape, "/tmp/recording", "cursor-update");
     141#endif
     142
     143    return vrc;
     144}
     145
     146/**
     147 * Moves (sets) the cursor to a new position.
     148 *
     149 * @returns VBox status code.
     150 * @retval  VERR_NO_CHANGE if the cursor wasn't moved (set).
     151 * @param   iX                  New X position to set.
     152 * @param   iY                  New Y position to set.
     153 */
     154int RecordingCursorState::Move(int32_t iX, int32_t iY)
     155{
     156    /* No relative coordinates here. */
     157    if (   iX < 0
     158        || iY < 0)
     159        return VERR_NO_CHANGE;
     160
     161    if (   m_Shape.Pos.x == (uint32_t)iX
     162        && m_Shape.Pos.y == (uint32_t)iY)
     163        return VERR_NO_CHANGE;
     164
     165    m_Shape.Pos.x = (uint16_t)iX;
     166    m_Shape.Pos.y = (uint16_t)iY;
     167
     168    return VINF_SUCCESS;
     169}
     170
     171
     172//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     173
     174
    70175/**
    71176 * Recording context constructor.
     
    129234    for (;;)
    130235    {
    131         int vrc = RTSemEventWait(pThis->m_WaitEvent, RT_INDEFINITE_WAIT);
    132         AssertRCBreak(vrc);
    133 
    134         Log2Func(("Processing %zu streams\n", pThis->m_vecStreams.size()));
     236        int vrcWait = RTSemEventWait(pThis->m_WaitEvent, RT_MS_1SEC);
     237
     238        Log2Func(("Processing %zu streams (wait = %Rrc)\n", pThis->m_vecStreams.size(), vrcWait));
    135239
    136240        /* Process common raw blocks (data which not has been encoded yet). */
    137         vrc = pThis->processCommonData(pThis->m_mapBlocksRaw, 100 /* ms timeout */);
     241        int vrc = pThis->processCommonData(pThis->m_mapBlocksRaw, 100 /* ms timeout */);
    138242
    139243        /** @todo r=andy This is inefficient -- as we already wake up this thread
     
    146250
    147251            /* Hand-in common encoded blocks. */
    148             vrc = pStream->Process(pThis->m_mapBlocksEncoded);
     252            vrc = pStream->ThreadMain(vrcWait, pThis->m_mapBlocksEncoded);
    149253            if (RT_FAILURE(vrc))
    150254            {
     
    207311        {
    208312            RecordingBlock *pBlockCommon = (RecordingBlock *)(*itBlock);
    209             switch (pBlockCommon->enmType)
     313            PRECORDINGFRAME pFrame = (PRECORDINGFRAME)pBlockCommon->pvData;
     314            AssertPtr(pFrame);
     315            switch (pFrame->enmType)
    210316            {
    211317#ifdef VBOX_WITH_AUDIO_RECORDING
    212                 case RECORDINGBLOCKTYPE_AUDIO:
     318                case RECORDINGFRAME_TYPE_AUDIO:
    213319                {
    214                     PRECORDINGAUDIOFRAME pAudioFrame = (PRECORDINGAUDIOFRAME)pBlockCommon->pvData;
    215 
    216                     RECORDINGFRAME Frame;
    217                     Frame.msTimestamp = pBlockCommon->msTimestamp;
    218                     Frame.Audio.pvBuf = pAudioFrame->pvBuf;
    219                     Frame.Audio.cbBuf = pAudioFrame->cbBuf;
    220 
    221                     vrc = recordingCodecEncode(&m_CodecAudio, &Frame, NULL, NULL);
     320                    vrc = recordingCodecEncodeFrame(&m_CodecAudio, pFrame, pFrame->msTimestamp, NULL /* pvUser */);
    222321                    break;
    223322                }
    224323#endif /* VBOX_WITH_AUDIO_RECORDING */
    225                 default:
    226                     /* Skip unknown stuff. */
    227                     break;
     324
     325            default:
     326                /* Skip unknown stuff. */
     327                break;
    228328            }
    229329
     
    278378                 pCodec, cbData, msTimestamp, uFlags));
    279379
    280     /** @todo Optimize this! Three allocations in here! */
    281 
    282     RECORDINGBLOCKTYPE const enmType = pCodec->Parms.enmType == RECORDINGCODECTYPE_AUDIO
    283                                      ? RECORDINGBLOCKTYPE_AUDIO : RECORDINGBLOCKTYPE_UNKNOWN;
    284 
    285     AssertReturn(enmType != RECORDINGBLOCKTYPE_UNKNOWN, VERR_NOT_SUPPORTED);
    286 
    287     RecordingBlock *pBlock = new RecordingBlock();
     380    RECORDINGFRAME_TYPE const enmType = pCodec->Parms.enmType == RECORDINGCODECTYPE_AUDIO
     381                                      ? RECORDINGFRAME_TYPE_AUDIO : RECORDINGFRAME_TYPE_INVALID;
     382
     383    AssertReturn(enmType != RECORDINGFRAME_TYPE_INVALID, VERR_NOT_SUPPORTED);
     384
     385    PRECORDINGFRAME pFrame = NULL;
    288386
    289387    switch (enmType)
    290388    {
    291         case RECORDINGBLOCKTYPE_AUDIO:
    292         {
    293             PRECORDINGAUDIOFRAME pFrame = (PRECORDINGAUDIOFRAME)RTMemAlloc(sizeof(RECORDINGAUDIOFRAME));
     389        case RECORDINGFRAME_TYPE_AUDIO:
     390        {
     391            pFrame = (PRECORDINGFRAME)RTMemAlloc(sizeof(RECORDINGFRAME));
    294392            AssertPtrReturn(pFrame, VERR_NO_MEMORY);
    295 
    296             pFrame->pvBuf = (uint8_t *)RTMemAlloc(cbData);
    297             AssertPtrReturn(pFrame->pvBuf, VERR_NO_MEMORY);
    298             pFrame->cbBuf = cbData;
    299 
    300             memcpy(pFrame->pvBuf, pvData, cbData);
    301 
    302             pBlock->enmType     = enmType;
    303             pBlock->pvData      = pFrame;
    304             pBlock->cbData      = sizeof(RECORDINGAUDIOFRAME) + cbData;
    305             pBlock->cRefs       = m_cStreamsEnabled;
    306             pBlock->msTimestamp = msTimestamp;
    307             pBlock->uFlags      = uFlags;
    308 
     393            pFrame->enmType     = RECORDINGFRAME_TYPE_AUDIO;
     394            pFrame->msTimestamp = msTimestamp;
     395
     396            PRECORDINGAUDIOFRAME pAudioFrame = &pFrame->u.Audio;
     397            pAudioFrame->pvBuf = (uint8_t *)RTMemDup(pvData, cbData);
     398            AssertPtrReturn(pAudioFrame->pvBuf, VERR_NO_MEMORY);
     399            pAudioFrame->cbBuf = cbData;
    309400            break;
    310401        }
     
    319410    int vrc;
    320411
     412    RecordingBlock *pBlock = NULL;
    321413    try
    322414    {
     415        pBlock = new RecordingBlock();
     416
     417        pBlock->pvData      = pFrame;
     418        pBlock->cbData      = sizeof(RECORDINGFRAME);
     419        pBlock->cRefs       = m_cStreamsEnabled;
     420        pBlock->msTimestamp = msTimestamp;
     421        pBlock->uFlags      = uFlags;
     422
    323423        RecordingBlockMap::iterator itBlocks = mapCommon.find(msTimestamp);
    324424        if (itBlocks == mapCommon.end())
     
    334434        vrc = VINF_SUCCESS;
    335435    }
    336     catch (const std::exception &ex)
    337     {
    338         RT_NOREF(ex);
     436    catch (const std::exception &)
     437    {
    339438        vrc = VERR_NO_MEMORY;
    340439    }
     
    343442
    344443    if (RT_SUCCESS(vrc))
     444    {
    345445        vrc = threadNotify();
     446    }
     447    else
     448    {
     449        if (pBlock)
     450            delete pBlock;
     451        RecordingFrameFree(pFrame);
     452    }
    346453
    347454    return vrc;
     
    448555    if (RT_SUCCESS(vrc))
    449556    {
    450         m_tsStartMs = RTTimeMilliTS();
     557        m_tsStartMs = 0;
    451558        m_enmState  = RECORDINGSTS_CREATED;
    452559        m_fShutdown = false;
     
    474581    Assert(m_enmState == RECORDINGSTS_CREATED);
    475582
     583    m_tsStartMs = RTTimeMilliTS();
     584
    476585    int vrc = RTThreadCreate(&m_Thread, RecordingContext::threadMain, (void *)this, 0,
    477586                             RTTHREADTYPE_MAIN_WORKER, RTTHREADFLAGS_WAITABLE, "Record");
     
    483592    {
    484593        LogRel(("Recording: Started\n"));
    485         m_enmState = RECORDINGSTS_STARTED;
     594        m_enmState  = RECORDINGSTS_STARTED;
    486595    }
    487596    else
     
    516625    {
    517626        LogRel(("Recording: Stopped\n"));
    518         m_enmState = RECORDINGSTS_CREATED;
     627        m_tsStartMs = 0;
     628        m_enmState  = RECORDINGSTS_CREATED;
    519629    }
    520630    else
     
    689799
    690800/**
     801 * Returns the current PTS (presentation time stamp) for a recording context.
     802 *
     803 * @returns Current PTS.
     804 */
     805uint64_t RecordingContext::GetCurrentPTS(void) const
     806{
     807    return RTTimeMilliTS() - m_tsStartMs;
     808}
     809
     810/**
    691811 * Returns if a specific recoding feature is enabled for at least one of the attached
    692812 * recording streams or not.
     
    829949 * @param   msTimestamp         Timestamp (PTS, in ms).
    830950 */
    831 bool RecordingContext::NeedsUpdate( uint32_t uScreen, uint64_t msTimestamp)
     951bool RecordingContext::NeedsUpdate(uint32_t uScreen, uint64_t msTimestamp)
    832952{
    833953    lock();
     
    9001020 *
    9011021 * @returns VBox status code.
    902  * @param   uScreen            Screen number to send video frame to.
    903  * @param   x                  Starting x coordinate of the video frame.
    904  * @param   y                  Starting y coordinate of the video frame.
    905  * @param   uPixelFormat       Pixel format.
    906  * @param   uBPP               Bits Per Pixel (BPP).
    907  * @param   uBytesPerLine      Bytes per scanline.
    908  * @param   uSrcWidth          Width of the video frame.
    909  * @param   uSrcHeight         Height of the video frame.
    910  * @param   puSrcData          Pointer to video frame data.
    911  * @param   msTimestamp        Timestamp (PTS, in ms).
    912  */
    913 int RecordingContext::SendVideoFrame(uint32_t uScreen, uint32_t x, uint32_t y,
    914                                      uint32_t uPixelFormat, uint32_t uBPP, uint32_t uBytesPerLine,
    915                                      uint32_t uSrcWidth, uint32_t uSrcHeight, uint8_t *puSrcData,
    916                                      uint64_t msTimestamp)
    917 {
    918     AssertReturn(uSrcWidth,  VERR_INVALID_PARAMETER);
    919     AssertReturn(uSrcHeight, VERR_INVALID_PARAMETER);
    920     AssertReturn(puSrcData,  VERR_INVALID_POINTER);
     1022 * @param   uScreen             Screen number to send video frame to.
     1023 * @param   pFrame              Video frame to send.
     1024 * @param   msTimestamp         Timestamp (PTS, in ms).
     1025 */
     1026int RecordingContext::SendVideoFrame(uint32_t uScreen, PRECORDINGVIDEOFRAME pFrame, uint64_t msTimestamp)
     1027{
     1028    AssertPtrReturn(pFrame, VERR_INVALID_POINTER);
     1029
     1030    LogFlowFunc(("uScreen=%RU32, offX=%RU32, offY=%RU32, w=%RU32, h=%RU32, msTimestamp=%RU64\n",
     1031                 uScreen, pFrame->Pos.x, pFrame->Pos.y, pFrame->Info.uWidth, pFrame->Info.uHeight, msTimestamp));
     1032
     1033    if (!pFrame->pau8Buf) /* Empty / invalid frame, skip. */
     1034        return VINF_SUCCESS;
     1035
     1036    /* Sanity. */
     1037    AssertReturn(pFrame->Info.uWidth  * pFrame->Info.uHeight * (pFrame->Info.uBPP / 8) <= pFrame->cbBuf, VERR_INVALID_PARAMETER);
     1038    AssertReturn(pFrame->Info.uHeight * pFrame->Info.uBytesPerLine <= pFrame->cbBuf, VERR_INVALID_PARAMETER);
    9211039
    9221040    lock();
     
    9311049    }
    9321050
    933     int vrc = pStream->SendVideoFrame(x, y, uPixelFormat, uBPP, uBytesPerLine, uSrcWidth, uSrcHeight, puSrcData, msTimestamp);
     1051    int vrc = pStream->SendVideoFrame(pFrame, msTimestamp);
    9341052
    9351053    unlock();
     
    9441062}
    9451063
     1064/**
     1065 * Sends a cursor position change to the recording context.
     1066 *
     1067 * @returns VBox status code.
     1068 * @param   uScreen            Screen number.
     1069 * @param   x                  X location within the guest.
     1070 * @param   y                  Y location within the guest.
     1071 * @param   msTimestamp        Timestamp (PTS, in ms).
     1072 */
     1073int RecordingContext::SendCursorPositionChange(uint32_t uScreen, int32_t x, int32_t y, uint64_t msTimestamp)
     1074{
     1075    LogFlowFunc(("uScreen=%RU32, x=%RU32, y=%RU32\n", uScreen, x, y));
     1076
     1077    /* If no cursor shape is set yet, skip any cursor position changes. */
     1078    if (!m_Cursor.m_Shape.pau8Buf)
     1079        return VINF_SUCCESS;
     1080
     1081    if (uScreen == 0xFFFFFFFF /* SVGA_ID_INVALID */)
     1082        uScreen = 0;
     1083
     1084    int vrc = m_Cursor.Move(x, y);
     1085    if (RT_SUCCESS(vrc))
     1086    {
     1087        lock();
     1088
     1089        RecordingStream *pStream = getStreamInternal(uScreen);
     1090        if (!pStream)
     1091        {
     1092            unlock();
     1093
     1094            AssertFailed();
     1095            return VERR_NOT_FOUND;
     1096        }
     1097
     1098        vrc = pStream->SendCursorPos(0 /* idCursor */, &m_Cursor.m_Shape.Pos, msTimestamp);
     1099
     1100        unlock();
     1101
     1102        if (   RT_SUCCESS(vrc)
     1103            && vrc != VINF_RECORDING_THROTTLED) /* Only signal the thread if operation was successful. */
     1104        {
     1105            threadNotify();
     1106        }
     1107    }
     1108
     1109    return vrc;
     1110}
     1111
     1112/**
     1113 * Sends a cursor shape change to the recording context.
     1114 *
     1115 * @returns VBox status code.
     1116 * @param   fVisible            Whether the mouse cursor actually is visible or not.
     1117 * @param   fAlpha              Whether the pixel data contains alpha channel information or not.
     1118 * @param   xHot                X hot position (in pixel) of the new cursor.
     1119 * @param   yHot                Y hot position (in pixel) of the new cursor.
     1120 * @param   uWidth              Width (in pixel) of the new cursor.
     1121 * @param   uHeight             Height (in pixel) of the new cursor.
     1122 * @param   pu8Shape            Pixel data of the new cursor. Must be 32 BPP RGBA for now.
     1123 * @param   cbShape             Size of \a pu8Shape (in bytes).
     1124 * @param   msTimestamp         Timestamp (PTS, in ms).
     1125 */
     1126int RecordingContext::SendCursorShapeChange(bool fVisible, bool fAlpha, uint32_t xHot, uint32_t yHot,
     1127                                            uint32_t uWidth, uint32_t uHeight, const uint8_t *pu8Shape, size_t cbShape,
     1128                                            uint64_t msTimestamp)
     1129{
     1130    RT_NOREF(fAlpha, xHot, yHot);
     1131
     1132    LogFlowFunc(("fVisible=%RTbool, fAlpha=%RTbool, uWidth=%RU32, uHeight=%RU32\n", fVisible, fAlpha, uWidth, uHeight));
     1133
     1134    if (   !pu8Shape /* Might be NULL on saved state load. */
     1135        || !fVisible)
     1136        return VINF_SUCCESS;
     1137
     1138    AssertReturn(cbShape, VERR_INVALID_PARAMETER);
     1139
     1140    lock();
     1141
     1142    int vrc = m_Cursor.CreateOrUpdate(fAlpha, uWidth, uHeight, pu8Shape, cbShape);
     1143
     1144    RecordingStreams::iterator it = m_vecStreams.begin();
     1145    while (it != m_vecStreams.end())
     1146    {
     1147        RecordingStream *pStream = (*it);
     1148
     1149        int vrc2 = pStream->SendCursorShape(0 /* idCursor */, &m_Cursor.m_Shape, msTimestamp);
     1150        if (RT_SUCCESS(vrc))
     1151            vrc = vrc2;
     1152
     1153        ++it;
     1154    }
     1155
     1156    unlock();
     1157
     1158    if (RT_SUCCESS(vrc))
     1159        threadNotify();
     1160
     1161    return vrc;
     1162}
     1163
     1164/**
     1165 * Sends a screen change to a recording stream.
     1166 *
     1167 * @returns VBox status code.
     1168 * @param   uScreen             Screen number.
     1169 * @param   pInfo               Recording screen info to use.
     1170 * @param   msTimestamp         Timestamp (PTS, in ms).
     1171 */
     1172int RecordingContext::SendScreenChange(uint32_t uScreen, PRECORDINGSURFACEINFO pInfo, uint64_t msTimestamp)
     1173{
     1174    lock();
     1175
     1176    RecordingStream *pStream = getStreamInternal(uScreen);
     1177    if (!pStream)
     1178    {
     1179        unlock();
     1180
     1181        AssertFailed();
     1182        return VERR_NOT_FOUND;
     1183    }
     1184
     1185    int const vrc = pStream->SendScreenChange(pInfo, msTimestamp);
     1186
     1187    unlock();
     1188
     1189    return vrc;
     1190}
     1191
  • trunk/src/VBox/Main/src-client/RecordingCodec.cpp

    r104635 r105006  
    6666#include <VBox/vmm/pdmaudioinline.h>
    6767
     68#include "Recording.h"
    6869#include "RecordingInternals.h"
    6970#include "RecordingUtils.h"
     
    7273#include <math.h>
    7374
     75#include <iprt/formats/bmp.h>
     76
     77
     78/*********************************************************************************************************************************
     79*   Prototypes                                                                                                                   *
     80*********************************************************************************************************************************/
     81static int recordingCodecVPXEncodeWorker(PRECORDINGCODEC pCodec, vpx_image_t *pImage, uint64_t msTimestamp);
     82
     83
     84/*********************************************************************************************************************************
     85*   Generic inline functions                                                                                                     *
     86*********************************************************************************************************************************/
     87
     88DECLINLINE(void) recordingCodecLock(PRECORDINGCODEC pCodec)
     89{
     90    int vrc2 = RTCritSectEnter(&pCodec->CritSect);
     91    AssertRC(vrc2);
     92}
     93
     94DECLINLINE(void) recordingCodecUnlock(PRECORDINGCODEC pCodec)
     95{
     96    int vrc2 = RTCritSectLeave(&pCodec->CritSect);
     97    AssertRC(vrc2);
     98}
     99
    74100
    75101/*********************************************************************************************************************************
     
    78104
    79105#ifdef VBOX_WITH_LIBVPX
     106/** Prototypes. */
     107static DECLCALLBACK(int) recordingCodecVPXScreenChange(PRECORDINGCODEC pCodec, PRECORDINGSURFACEINFO pInfo);
     108
     109/**
     110 * Clears (zeros) the VPX planes.
     111 */
     112DECLINLINE(void) recordingCodecVPXClearPlanes(PRECORDINGCODEC pCodec)
     113{
     114    size_t const cbYPlane  = pCodec->Parms.u.Video.uWidth * pCodec->Parms.u.Video.uHeight;
     115    memset(pCodec->Video.VPX.RawImage.planes[VPX_PLANE_Y], 0, cbYPlane);
     116    size_t const cbUVPlane = (pCodec->Parms.u.Video.uWidth / 2) * (pCodec->Parms.u.Video.uHeight / 2);
     117    memset(pCodec->Video.VPX.RawImage.planes[VPX_PLANE_U], 128, cbUVPlane);
     118    memset(pCodec->Video.VPX.RawImage.planes[VPX_PLANE_V], 128, cbUVPlane);
     119}
     120
    80121/** @copydoc RECORDINGCODECOPS::pfnInit */
    81122static DECLCALLBACK(int) recordingCodecVPXInit(PRECORDINGCODEC pCodec)
    82123{
    83     pCodec->cbScratch = _4K;
    84     pCodec->pvScratch = RTMemAlloc(pCodec->cbScratch);
    85     AssertPtrReturn(pCodec->pvScratch, VERR_NO_MEMORY);
    86 
    87     pCodec->Parms.csFrame  = 0;
    88     pCodec->Parms.cbFrame  = pCodec->Parms.Video.uWidth * pCodec->Parms.Video.uHeight * 4 /* 32-bit */;
    89     pCodec->Parms.msFrame  = 1; /* 1ms per frame. */
     124    const unsigned uBPP = 32;
     125
     126    pCodec->Parms.csFrame = 0;
     127    pCodec->Parms.cbFrame = pCodec->Parms.u.Video.uWidth * pCodec->Parms.u.Video.uHeight * (uBPP / 8);
     128    pCodec->Parms.msFrame = 1; /* 1ms per frame. */
    90129
    91130# ifdef VBOX_WITH_LIBVPX_VP9
     
    106145    pVPX->Cfg.rc_target_bitrate = pCodec->Parms.uBitrate;
    107146    /* Frame width. */
    108     pVPX->Cfg.g_w = pCodec->Parms.Video.uWidth;
     147    pVPX->Cfg.g_w = pCodec->Parms.u.Video.uWidth;
    109148    /* Frame height. */
    110     pVPX->Cfg.g_h = pCodec->Parms.Video.uHeight;
     149    pVPX->Cfg.g_h = pCodec->Parms.u.Video.uHeight;
    111150    /* ms per frame. */
    112151    pVPX->Cfg.g_timebase.num = pCodec->Parms.msFrame;
     
    124163
    125164    if (!vpx_img_alloc(&pVPX->RawImage, VPX_IMG_FMT_I420,
    126                        pCodec->Parms.Video.uWidth, pCodec->Parms.Video.uHeight, 1))
    127     {
    128         LogRel(("Recording: Failed to allocate image %RU32x%RU32\n", pCodec->Parms.Video.uWidth, pCodec->Parms.Video.uHeight));
     165                       pCodec->Parms.u.Video.uWidth, pCodec->Parms.u.Video.uHeight, 1))
     166    {
     167        LogRel(("Recording: Failed to allocate image %RU32x%RU32\n", pCodec->Parms.u.Video.uWidth, pCodec->Parms.u.Video.uHeight));
    129168        return VERR_RECORDING_CODEC_INIT_FAILED;
    130169    }
    131170
    132     /* Save a pointer to the first raw YUV plane. */
    133     pVPX->pu8YuvBuf = pVPX->RawImage.planes[0];
    134 
    135     return VINF_SUCCESS;
     171    /* Save a pointer to the Y (Luminance) plane. */
     172    pVPX->pu8YuvBuf = pVPX->RawImage.planes[VPX_PLANE_Y];
     173
     174    /* Initialize front + back buffers. */
     175    RT_ZERO(pCodec->Video.VPX.Front);
     176    RT_ZERO(pCodec->Video.VPX.Back);
     177
     178    pCodec->Video.VPX.pCursorShape = NULL;
     179
     180    RECORDINGSURFACEINFO ScreenInfo;
     181    ScreenInfo.uWidth      = pCodec->Parms.u.Video.uWidth;
     182    ScreenInfo.uHeight     = pCodec->Parms.u.Video.uHeight;
     183    ScreenInfo.uBPP        = uBPP;
     184    ScreenInfo.enmPixelFmt = RECORDINGPIXELFMT_BRGA32;
     185
     186    RT_ZERO(pCodec->Video.VPX.PosCursorOld);
     187
     188    int vrc = recordingCodecVPXScreenChange(pCodec, &ScreenInfo);
     189    if (RT_FAILURE(vrc))
     190        LogRel(("Recording: Failed to initialize codec: %Rrc\n", vrc));
     191
     192    return vrc;
    136193}
    137194
     
    147204    Assert(rcv == VPX_CODEC_OK); RT_NOREF(rcv);
    148205
     206    RecordingVideoFrameDestroy(&pCodec->Video.VPX.Front);
     207    RecordingVideoFrameDestroy(&pCodec->Video.VPX.Back);
     208
     209    RecordingVideoFrameFree(pCodec->Video.VPX.pCursorShape);
     210    pCodec->Video.VPX.pCursorShape = NULL;
     211
    149212    return VINF_SUCCESS;
     213}
     214
     215/** @copydoc RECORDINGCODECOPS::pfnFinalize */
     216static DECLCALLBACK(int) recordingCodecVPXFinalize(PRECORDINGCODEC pCodec)
     217{
     218    recordingCodecLock(pCodec);
     219
     220    int vrc = recordingCodecVPXEncodeWorker(pCodec, NULL /* pImage */, pCodec->State.tsLastWrittenMs + 1);
     221
     222    recordingCodecUnlock(pCodec);
     223
     224    return vrc;
    150225}
    151226
     
    165240            else if (value.compare("good", com::Utf8Str::CaseInsensitive) == 0)
    166241            {
    167                 AssertStmt(pCodec->Parms.Video.uFPS, pCodec->Parms.Video.uFPS = 25);
    168                 pVPX->uEncoderDeadline = 1000000 / pCodec->Parms.Video.uFPS;
     242                AssertStmt(pCodec->Parms.u.Video.uFPS, pCodec->Parms.u.Video.uFPS = 25);
     243                pVPX->uEncoderDeadline = 1000000 / pCodec->Parms.u.Video.uFPS;
    169244            }
    170245            else if (value.compare("best", com::Utf8Str::CaseInsensitive) == 0)
     
    180255}
    181256
    182 /** @copydoc RECORDINGCODECOPS::pfnEncode */
    183 static DECLCALLBACK(int) recordingCodecVPXEncode(PRECORDINGCODEC pCodec, PRECORDINGFRAME pFrame,
    184                                                  size_t *pcEncoded, size_t *pcbEncoded)
    185 {
    186     RT_NOREF(pcEncoded, pcbEncoded);
    187 
    188     AssertPtrReturn(pFrame, VERR_INVALID_POINTER);
    189 
    190     PRECORDINGVIDEOFRAME pVideoFrame = pFrame->VideoPtr;
    191 
    192     int vrc = RecordingUtilsRGBToYUV(pVideoFrame->enmPixelFmt,
    193                                      /* Destination */
    194                                      pCodec->Video.VPX.pu8YuvBuf, pVideoFrame->uWidth, pVideoFrame->uHeight,
    195                                      /* Source */
    196                                      pVideoFrame->pu8RGBBuf, pCodec->Parms.Video.uWidth, pCodec->Parms.Video.uHeight);
    197     AssertRCReturn(vrc, vrc);
     257/**
     258 * Worker for encoding the last composed image.
     259 *
     260 * @returns VBox status code.
     261 * @param   pCodec              Pointer to codec instance.
     262 * @param   pImage              VPX image to encode.
     263 *                              Set to NULL to signal the encoder that it has to finish up stuff when ending encoding.
     264 * @param   msTimestamp         Timestamp (PTS) to use for encoding.
     265 *
     266 * @note    Caller must take encoder lock.
     267 */
     268static int recordingCodecVPXEncodeWorker(PRECORDINGCODEC pCodec, vpx_image_t *pImage, uint64_t msTimestamp)
     269{
     270    int vrc;
    198271
    199272    PRECORDINGCODECVPX pVPX = &pCodec->Video.VPX;
    200273
    201274    /* Presentation TimeStamp (PTS). */
    202     vpx_codec_pts_t pts = pFrame->msTimestamp;
    203     vpx_codec_err_t rcv = vpx_codec_encode(&pVPX->Ctx,
    204                                            &pVPX->RawImage,
    205                                            pts                          /* Timestamp */,
    206                                            pCodec->Parms.Video.uDelayMs /* How long to show this frame */,
    207                                            0                            /* Flags */,
    208                                            pVPX->uEncoderDeadline       /* Quality setting */);
     275    vpx_codec_pts_t const pts = msTimestamp;
     276    vpx_codec_err_t const rcv = vpx_codec_encode(&pVPX->Ctx,
     277                                                 pImage,
     278                                                 pts                            /* Timestamp */,
     279                                                 pCodec->Parms.u.Video.uDelayMs /* How long to show this frame */,
     280                                                 0                              /* Flags */,
     281                                                 pVPX->uEncoderDeadline         /* Quality setting */);
    209282    if (rcv != VPX_CODEC_OK)
    210283    {
     
    221294    {
    222295        const vpx_codec_cx_pkt_t *pPkt = vpx_codec_get_cx_data(&pVPX->Ctx, &iter);
    223         if (!pPkt)
     296        if (!pPkt) /* End of list */
    224297            break;
    225298
     
    240313                    fFlags |= RECORDINGCODEC_ENC_F_BLOCK_IS_INVISIBLE;
    241314
     315                Log3Func(("msTimestamp=%RU64, fFlags=%#x\n", msTimestamp, fFlags));
     316
    242317                vrc = pCodec->Callbacks.pfnWriteData(pCodec, pPkt->data.frame.buf, pPkt->data.frame.sz,
    243318                                                     tsAbsPTSMs, fFlags, pCodec->Callbacks.pvUser);
     
    254329    return vrc;
    255330}
     331
     332/** @copydoc RECORDINGCODECOPS::pfnEncode */
     333static DECLCALLBACK(int) recordingCodecVPXEncode(PRECORDINGCODEC pCodec, PRECORDINGFRAME pFrame,
     334                                                 uint64_t msTimestamp, void *pvUser)
     335{
     336    recordingCodecLock(pCodec);
     337
     338    int vrc;
     339
     340    /* If no frame is given, encode the last composed frame again with the given timestamp. */
     341    if (pFrame == NULL)
     342    {
     343        vrc = recordingCodecVPXEncodeWorker(pCodec, &pCodec->Video.VPX.RawImage, msTimestamp);
     344        recordingCodecUnlock(pCodec);
     345        return vrc;
     346    }
     347
     348    RecordingContext *pCtx = (RecordingContext *)pvUser;
     349    AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
     350
     351    Assert(pFrame->msTimestamp == msTimestamp);
     352
     353    /* Note: We get BGRA 32 input here. */
     354    PRECORDINGVIDEOFRAME pFront = &pCodec->Video.VPX.Front;
     355    PRECORDINGVIDEOFRAME pBack  = &pCodec->Video.VPX.Back;
     356
     357    int32_t sx = 0; /* X origin within the source frame. */
     358    int32_t sy = 0; /* Y origin within the source frame. */
     359    int32_t sw = 0; /* Width of the source frame (starting at X origin). */
     360    int32_t sh = 0; /* Height of the source frame (starting at Y origin). */
     361    int32_t dx = 0; /* X destination of the source frame within the destination frame. */
     362    int32_t dy = 0; /* Y destination of the source frame within the destination frame. */
     363
     364    /*
     365     * Note!
     366     *
     367     * We don't implement any rendering graph or some such here, as we only have two things to render here, namely:
     368     *
     369     * - the actual framebuffer updates
     370     * - if available (through mouse integration via Guest Additions): the guest's mouse cursor via a (software) overlay
     371     *
     372     * So composing is done the folowing way:
     373     *
     374     *  - always store the plain framebuffer updates in our back buffer first
     375     *  - copy the framebuffer updates to our front buffer
     376     *  - restore the area of the old mouse cursor position by copying frame buffer area data from back -> front buffer
     377     *  - apply the mouse cursor updates to our front buffer
     378     */
     379
     380    switch (pFrame->enmType)
     381    {
     382        case RECORDINGFRAME_TYPE_VIDEO:
     383        {
     384            PRECORDINGVIDEOFRAME pSrc = &pFrame->u.Video;
     385
     386            vrc = RecordingVideoFrameBlitFrame(pFront, pSrc->Pos.x, pSrc->Pos.y,
     387                                               pSrc, 0 /* uSrcX */, 0 /* uSrcY */, pSrc->Info.uWidth, pSrc->Info.uHeight);
     388#if 0
     389            RecordingUtilsDbgDumpVideoFrameEx(pFront, "/tmp/recording", "encode-front");
     390            RecordingUtilsDbgDumpVideoFrameEx(pSrc, "/tmp/recording", "encode-src");
     391#endif
     392            vrc = RecordingVideoFrameBlitFrame(pBack, pSrc->Pos.x, pSrc->Pos.y,
     393                                               pSrc,  0 /* uSrcX */, 0 /* uSrcY */, pSrc->Info.uWidth, pSrc->Info.uHeight);
     394            pFront = pBack;
     395
     396            sw = pSrc->Info.uWidth;
     397            sh = pSrc->Info.uHeight;
     398            sx = pSrc->Pos.x;
     399            sy = pSrc->Pos.y;
     400
     401            Log3Func(("RECORDINGFRAME_TYPE_VIDEO: sx=%d, sy=%d, sw=%d, sh=%d\n", sx, sy, sw, sh));
     402
     403            dx = pSrc->Pos.x;
     404            dy = pSrc->Pos.y;
     405            break;
     406        }
     407
     408        case RECORDINGFRAME_TYPE_CURSOR_SHAPE:
     409        {
     410            pCodec->Video.VPX.pCursorShape = RecordingVideoFrameDup(&pFrame->u.CursorShape);
     411            AssertPtr(pCodec->Video.VPX.pCursorShape);
     412
     413            RT_FALL_THROUGH(); /* Re-render cursor with new shape below. */
     414        }
     415
     416        case RECORDINGFRAME_TYPE_CURSOR_POS:
     417        {
     418            const PRECORDINGVIDEOFRAME pCursor = pCodec->Video.VPX.pCursorShape;
     419            if (!pCursor) /* No cursor shape set yet. */
     420                break;
     421
     422            PRECORDINGPOS pPosOld = &pCodec->Video.VPX.PosCursorOld;
     423            PRECORDINGPOS pPosNew = pFrame->enmType == RECORDINGFRAME_TYPE_CURSOR_POS
     424                                  ? &pFrame->u.Cursor.Pos
     425                                  : pPosOld;
     426
     427            Log3Func(("RECORDINGFRAME_TYPE_CURSOR_POS: x=%d, y=%d, oldx=%d, oldy=%d, w=%d, h=%d\n",
     428                      pPosNew->x, pPosNew->y,
     429                      pPosOld->x, pPosOld->y, pCursor->Info.uWidth, pCursor->Info.uHeight));
     430
     431            /* Calculate the merged area between the old and the new (current) cursor position
     432             * so that we update everything to not create any ghosting. */
     433            sx = RT_MIN(pPosNew->x, pPosOld->x);
     434            sy = RT_MIN(pPosNew->y, pPosOld->y);
     435            sw = (  pPosNew->x > pPosOld->x
     436                  ? pPosNew->x - pPosOld->x
     437                  : pPosOld->x - pPosNew->x) + pCursor->Info.uWidth;
     438            sh = (  pPosNew->y > pPosOld->y
     439                  ? pPosNew->y - pPosOld->y
     440                  : pPosOld->y - pPosNew->y) + pCursor->Info.uHeight;
     441
     442            /* Limit the width / height to blit to the front buffer's size. */
     443            if (sx + sw >= (int32_t)pFront->Info.uWidth)
     444                sw = pFront->Info.uWidth - sx;
     445            if (sy + sh >= (int32_t)pFront->Info.uHeight)
     446                sh = pFront->Info.uHeight - sy;
     447
     448            /* Save current cursor position for next iteration. */
     449            *pPosOld = *pPosNew;
     450
     451            dx = sx;
     452            dy = sy;
     453
     454            Log3Func(("RECORDINGFRAME_TYPE_CURSOR_POS: sx=%d, sy=%d, sw=%d, sh=%d\n", sx, sy, sw, sh));
     455
     456            /* Nothing to encode? Bail out. */
     457            if (             sw <= 0
     458                ||           sh <= 0
     459                || (uint32_t)sx >  pBack->Info.uWidth
     460                || (uint32_t)sy >  pBack->Info.uHeight)
     461                break;
     462
     463            /* Restore background of front buffer first. */
     464            vrc = RecordingVideoFrameBlitFrame(pFront, dx, dy,
     465                                               pBack,  sx, sy, sw, sh);
     466
     467            /* Blit mouse cursor to front buffer. */
     468            if (RT_SUCCESS(vrc))
     469                vrc = RecordingVideoFrameBlitRawAlpha(pFront, pPosNew->x, pPosNew->y,
     470                                                      pCursor->pau8Buf, pCursor->cbBuf,
     471                                                      0 /* uSrcX */, 0 /* uSrcY */, pCursor->Info.uWidth, pCursor->Info.uHeight,
     472                                                      pCursor->Info.uBytesPerLine, pCursor->Info.uBPP, pCursor->Info.enmPixelFmt);
     473#if 0
     474            RecordingUtilsDbgDumpVideoFrameEx(pFront, "/tmp/recording", "cursor-alpha-front");
     475#endif
     476            break;
     477        }
     478
     479        default:
     480            AssertFailed();
     481            break;
     482
     483    }
     484
     485    /* Nothing to encode? Bail out. */
     486    if (   sw == 0
     487        || sh == 0)
     488    {
     489        recordingCodecUnlock(pCodec);
     490        return VINF_SUCCESS;
     491    }
     492
     493    Log3Func(("Encoding video parameters: %RU16x%RU16 (%RU8 FPS), originX=%RI32, originY=%RI32\n",
     494              pCodec->Parms.u.Video.uWidth, pCodec->Parms.u.Video.uHeight, pCodec->Parms.u.Video.uFPS,
     495              pCodec->Parms.u.Video.Scaling.u.Crop.m_iOriginX, pCodec->Parms.u.Video.Scaling.u.Crop.m_iOriginY));
     496
     497    int32_t sx_b = sx; /* X origin within the source frame. */
     498    int32_t sy_b = sy; /* Y origin within the source frame. */
     499    int32_t sw_b = sw; /* Width of the source frame (starting at X origin). */
     500    int32_t sh_b = sh; /* Height of the source frame (starting at Y origin). */
     501    int32_t dx_b = dx; /* X destination of the source frame within the destination frame. */
     502    int32_t dy_b = dy; /* Y destination of the source frame within the destination frame. */
     503
     504    RT_NOREF(sx_b);
     505    RT_NOREF(sy_b);
     506    RT_NOREF(sw_b);
     507    RT_NOREF(sh_b);
     508    RT_NOREF(dx_b);
     509    RT_NOREF(dy_b);
     510
     511    vrc = RecordingUtilsCoordsCropCenter(&pCodec->Parms, &sx, &sy, &sw, &sh, &dx, &dy);
     512    if (vrc == VINF_SUCCESS) /* vrc might be VWRN_RECORDING_ENCODING_SKIPPED to skip encoding. */
     513    {
     514        Log3Func(("Encoding source %RI32,%RI32 (%RI32x%RI32) to %RI32,%RI32 (%zu bytes)\n",
     515                  sx, sy, sw, sh, dx, dy, sw * sh * (pFront->Info.uBPP / 8)));
     516#ifdef DEBUG
     517        AssertReturn(sw      <= (int32_t)pFront->Info.uWidth,  VERR_INVALID_PARAMETER);
     518        AssertReturn(sh      <= (int32_t)pFront->Info.uHeight, VERR_INVALID_PARAMETER);
     519        AssertReturn(sx + sw <= (int32_t)pFront->Info.uWidth , VERR_INVALID_PARAMETER);
     520        AssertReturn(sy + sh <= (int32_t)pFront->Info.uHeight, VERR_INVALID_PARAMETER);
     521#endif
     522
     523#if 0
     524        RecordingUtilsDbgDumpImageData(&pFront->pau8Buf[(sy * pFront->Info.uBytesPerLine) + (sx * (pFront->Info.uBPP / 8))], pFront->cbBuf,
     525                                       "/tmp/recording", "cropped", sw, sh, pFront->Info.uBytesPerLine, pFront->Info.uBPP);
     526#endif
     527        /* Blit (and convert from BGRA 32) the changed parts of the front buffer to the YUV 420 surface of the codec. */
     528        RecordingUtilsConvBGRA32ToYUVI420Ex(/* Destination */
     529                                            pCodec->Video.VPX.pu8YuvBuf, dx, dy, pCodec->Parms.u.Video.uWidth, pCodec->Parms.u.Video.uHeight,
     530                                            /* Source */
     531                                            pFront->pau8Buf, sx, sy, sw, sh, pFront->Info.uBytesPerLine, pFront->Info.uBPP);
     532
     533        vrc = recordingCodecVPXEncodeWorker(pCodec, &pCodec->Video.VPX.RawImage, msTimestamp);
     534    }
     535
     536    recordingCodecUnlock(pCodec);
     537
     538    return vrc;
     539}
     540
     541/** @copydoc RECORDINGCODECOPS::pfnScreenChange */
     542static DECLCALLBACK(int) recordingCodecVPXScreenChange(PRECORDINGCODEC pCodec, PRECORDINGSURFACEINFO pInfo)
     543{
     544    LogRel2(("Recording: Codec got screen change notification (%RU16x%RU16, %RU8 BPP)\n",
     545             pInfo->uWidth, pInfo->uHeight, pInfo->uBPP));
     546
     547    /* Fend-off bogus reports. */
     548    if (   !pInfo->uWidth
     549        || !pInfo->uHeight)
     550        return VERR_INVALID_PARAMETER;
     551
     552     /** @todo BUGBUG Not sure why we sometimes get 0 BPP for a display change from Main.
     553      *               For now we ASSUME 32 BPP. */
     554    if (!pInfo->uBPP)
     555        pInfo->uBPP = 32;
     556
     557    /* The VPX encoder only understands even frame sizes. */
     558    if (   (pInfo->uWidth  % 2) != 0
     559        || (pInfo->uHeight % 2) != 0)
     560        return VERR_INVALID_PARAMETER;
     561
     562    PRECORDINGCODECVPX pVPX = &pCodec->Video.VPX;
     563
     564    recordingCodecLock(pCodec);
     565
     566    /* Tear down old stuff. */
     567    RecordingVideoFrameDestroy(&pVPX->Front);
     568    RecordingVideoFrameDestroy(&pVPX->Back);
     569
     570    /* Initialize front + back buffers. */
     571    int vrc = RecordingVideoFrameInit(&pVPX->Front, RECORDINGVIDEOFRAME_F_VISIBLE,
     572                                      pInfo->uWidth, pInfo->uHeight, 0, 0,
     573                                      pInfo->uBPP, pInfo->enmPixelFmt);
     574    if (RT_SUCCESS(vrc))
     575        vrc = RecordingVideoFrameInit(&pVPX->Back, RECORDINGVIDEOFRAME_F_VISIBLE,
     576                                      pInfo->uWidth, pInfo->uHeight, 0, 0,
     577                                      pInfo->uBPP, pInfo->enmPixelFmt);
     578    if (RT_SUCCESS(vrc))
     579    {
     580        RecordingVideoFrameClear(&pVPX->Front);
     581        RecordingVideoFrameClear(&pVPX->Back);
     582
     583        recordingCodecVPXClearPlanes(pCodec);
     584
     585        /* Calculate the X/Y origins for cropping / centering.
     586         * This is needed as the codec's video output size not necessarily matches the VM's frame buffer size. */
     587        pCodec->Parms.u.Video.Scaling.u.Crop.m_iOriginX = int32_t(pCodec->Parms.u.Video.uWidth  - pInfo->uWidth) / 2;
     588        pCodec->Parms.u.Video.Scaling.u.Crop.m_iOriginY = int32_t(pCodec->Parms.u.Video.uHeight - pInfo->uHeight) / 2;
     589    }
     590
     591    recordingCodecUnlock(pCodec);
     592
     593    if (RT_FAILURE(vrc))
     594        LogRel(("Recording: Codec error handling screen change notification: %Rrc\n", vrc));
     595
     596    return vrc;
     597
     598}
    256599#endif /* VBOX_WITH_LIBVPX */
    257600
     
    269612    AssertPtrReturn(pCodec->pvScratch, VERR_NO_MEMORY);
    270613
    271     const PPDMAUDIOPCMPROPS pPCMProps = &pCodec->Parms.Audio.PCMProps;
     614    const PPDMAUDIOPCMPROPS pPCMProps = &pCodec->Parms.u.Audio.PCMProps;
    272615
    273616    /** @todo BUGBUG When left out this call, vorbis_block_init() does not find oggpack_writeinit and all goes belly up ... */
     
    336679/** @copydoc RECORDINGCODECOPS::pfnEncode */
    337680static DECLCALLBACK(int) recordingCodecVorbisEncode(PRECORDINGCODEC pCodec,
    338                                                     const PRECORDINGFRAME pFrame, size_t *pcEncoded, size_t *pcbEncoded)
    339 {
    340     const PPDMAUDIOPCMPROPS pPCMProps = &pCodec->Parms.Audio.PCMProps;
     681                                                    const PRECORDINGFRAME pFrame, uint64_t msTimestamp, void *pvUser)
     682{
     683    RT_NOREF(msTimestamp, pvUser);
     684
     685    const PPDMAUDIOPCMPROPS pPCMProps = &pCodec->Parms.u.Audio.PCMProps;
    341686
    342687    Assert      (pCodec->Parms.cbFrame);
    343     AssertReturn(pFrame->Audio.cbBuf % pCodec->Parms.cbFrame == 0, VERR_INVALID_PARAMETER);
    344     Assert      (pFrame->Audio.cbBuf);
    345     AssertReturn(pFrame->Audio.cbBuf % PDMAudioPropsFrameSize(pPCMProps) == 0, VERR_INVALID_PARAMETER);
    346     AssertReturn(pCodec->cbScratch >= pFrame->Audio.cbBuf, VERR_INVALID_PARAMETER);
     688    AssertReturn(pFrame->u.Audio.cbBuf % pCodec->Parms.cbFrame == 0, VERR_INVALID_PARAMETER);
     689    Assert      (pFrame->u.Audio.cbBuf);
     690    AssertReturn(pFrame->u.Audio.cbBuf % PDMAudioPropsFrameSize(pPCMProps) == 0, VERR_INVALID_PARAMETER);
     691    AssertReturn(pCodec->cbScratch >= pFrame->u.Audio.cbBuf, VERR_INVALID_PARAMETER);
    347692
    348693    int vrc = VINF_SUCCESS;
    349694
    350695    int const cbFrame = PDMAudioPropsFrameSize(pPCMProps);
    351     int const cFrames = (int)(pFrame->Audio.cbBuf / cbFrame);
     696    int const cFrames = (int)(pFrame->u.Audio.cbBuf / cbFrame);
    352697
    353698    /* Write non-interleaved frames. */
    354699    float  **buffer = vorbis_analysis_buffer(&pCodec->Audio.Vorbis.dsp_state, cFrames);
    355     int16_t *puSrc  = (int16_t *)pFrame->Audio.pvBuf; RT_NOREF(puSrc);
     700    int16_t *puSrc  = (int16_t *)pFrame->u.Audio.pvBuf; RT_NOREF(puSrc);
    356701
    357702    /* Convert samples into floating point. */
     
    375720        return VERR_RECORDING_ENCODING_FAILED;
    376721    }
    377 
    378     if (pcEncoded)
    379         *pcEncoded = 0;
    380     if (pcbEncoded)
    381         *pcbEncoded = 0;
    382722
    383723    size_t cBlocksEncoded = 0;
     
    439779    }
    440780
    441     if (pcbEncoded)
    442         *pcbEncoded = 0;
    443     if (pcEncoded)
    444         *pcEncoded  = 0;
    445 
    446781    if (RT_FAILURE(vrc))
    447782        LogRel(("Recording: Encoding Vorbis audio data failed, vrc=%Rrc\n", vrc));
    448783
    449784    Log3Func(("cbSrc=%zu, cbDst=%zu, cEncoded=%zu, cbEncoded=%zu, vrc=%Rrc\n",
    450               pFrame->Audio.cbBuf, pCodec->cbScratch, cBlocksEncoded, cBytesEncoded, vrc));
    451 
    452     return vrc;
    453 }
    454 
     785              pFrame->u.Audio.cbBuf, pCodec->cbScratch, cBlocksEncoded, cBytesEncoded, vrc));
     786
     787    return vrc;
     788}
     789
     790/** @copydoc RECORDINGCODECOPS::pfnFinalize */
    455791static DECLCALLBACK(int) recordingCodecVorbisFinalize(PRECORDINGCODEC pCodec)
    456792{
     
    488824    LogRel(("Recording: Initializing audio codec '%s'\n", strCodec.c_str()));
    489825
    490     const PPDMAUDIOPCMPROPS pPCMProps = &pCodec->Parms.Audio.PCMProps;
     826    const PPDMAUDIOPCMPROPS pPCMProps = &pCodec->Parms.u.Audio.PCMProps;
    491827
    492828    PDMAudioPropsInit(pPCMProps,
     
    546882    LogRel(("Recording: Initializing video codec '%s'\n", strTemp.c_str()));
    547883
    548     pCodec->Parms.uBitrate       = Settings.Video.ulRate;
    549     pCodec->Parms.Video.uFPS     = Settings.Video.ulFPS;
    550     pCodec->Parms.Video.uWidth   = Settings.Video.ulWidth;
    551     pCodec->Parms.Video.uHeight  = Settings.Video.ulHeight;
    552     pCodec->Parms.Video.uDelayMs = RT_MS_1SEC / pCodec->Parms.Video.uFPS;
     884    pCodec->Parms.uBitrate         = Settings.Video.ulRate;
     885    pCodec->Parms.u.Video.uFPS     = Settings.Video.ulFPS;
     886    pCodec->Parms.u.Video.uWidth   = Settings.Video.ulWidth;
     887    pCodec->Parms.u.Video.uHeight  = Settings.Video.ulHeight;
     888    pCodec->Parms.u.Video.uDelayMs = RT_MS_1SEC / pCodec->Parms.u.Video.uFPS;
    553889
    554890    if (pCallbacks)
     
    556892
    557893    AssertReturn(pCodec->Parms.uBitrate, VERR_INVALID_PARAMETER);        /* Bitrate must be set. */
    558     AssertStmt(pCodec->Parms.Video.uFPS, pCodec->Parms.Video.uFPS = 25); /* Prevent division by zero. */
    559 
    560     AssertReturn(pCodec->Parms.Video.uHeight, VERR_INVALID_PARAMETER);
    561     AssertReturn(pCodec->Parms.Video.uWidth, VERR_INVALID_PARAMETER);
    562     AssertReturn(pCodec->Parms.Video.uDelayMs, VERR_INVALID_PARAMETER);
     894    AssertStmt(pCodec->Parms.u.Video.uFPS, pCodec->Parms.u.Video.uFPS = 25); /* Prevent division by zero. */
     895
     896    AssertReturn(pCodec->Parms.u.Video.uHeight, VERR_INVALID_PARAMETER);
     897    AssertReturn(pCodec->Parms.u.Video.uWidth, VERR_INVALID_PARAMETER);
     898    AssertReturn(pCodec->Parms.u.Video.uDelayMs, VERR_INVALID_PARAMETER);
    563899
    564900    int vrc = VINF_SUCCESS;
     
    602938            if (value.compare("low", com::Utf8Str::CaseInsensitive) == 0)
    603939            {
    604                 PDMAudioPropsInit(&pCodec->Parms.Audio.PCMProps, 16, true /* fSigned */, 1 /* Channels */, 8000 /* Hz */);
     940                PDMAudioPropsInit(&pCodec->Parms.u.Audio.PCMProps, 16, true /* fSigned */, 1 /* Channels */, 8000 /* Hz */);
    605941            }
    606942            else if (value.startsWith("med" /* "med[ium]" */, com::Utf8Str::CaseInsensitive) == 0)
     
    610946            else if (value.compare("high", com::Utf8Str::CaseInsensitive) == 0)
    611947            {
    612                 PDMAudioPropsInit(&pCodec->Parms.Audio.PCMProps, 16, true /* fSigned */, 2 /* Channels */, 48000 /* Hz */);
     948                PDMAudioPropsInit(&pCodec->Parms.u.Audio.PCMProps, 16, true /* fSigned */, 2 /* Channels */, 48000 /* Hz */);
    613949            }
    614950        }
     
    625961{
    626962    pCodec->State.tsLastWrittenMs = 0;
    627 
    628963    pCodec->State.cEncErrors = 0;
    629 #ifdef VBOX_WITH_STATISTICS
    630     pCodec->STAM.cEncBlocks  = 0;
    631     pCodec->STAM.msEncTotal  = 0;
    632 #endif
    633964}
    634965
     
    7081039            pCodec->Ops.pfnInit         = recordingCodecVPXInit;
    7091040            pCodec->Ops.pfnDestroy      = recordingCodecVPXDestroy;
     1041            pCodec->Ops.pfnFinalize     = recordingCodecVPXFinalize;
    7101042            pCodec->Ops.pfnParseOptions = recordingCodecVPXParseOptions;
    7111043            pCodec->Ops.pfnEncode       = recordingCodecVPXEncode;
     1044            pCodec->Ops.pfnScreenChange = recordingCodecVPXScreenChange;
    7121045
    7131046            vrc = VINF_SUCCESS;
     
    7401073int recordingCodecInit(const PRECORDINGCODEC pCodec, const PRECORDINGCODECCALLBACKS pCallbacks, const settings::RecordingScreenSettings &Settings)
    7411074{
     1075    int vrc = RTCritSectInit(&pCodec->CritSect);
     1076    AssertRCReturn(vrc, vrc);
     1077
     1078    pCodec->cbScratch = 0;
     1079    pCodec->pvScratch = NULL;
     1080
    7421081    recordingCodecReset(pCodec);
    7431082
    744     int vrc;
    7451083    if (pCodec->Parms.enmType == RECORDINGCODECTYPE_AUDIO)
    7461084        vrc = recordingCodecInitAudio(pCodec, pCallbacks, Settings);
     
    8111149        pCodec->Parms.enmType       = RECORDINGCODECTYPE_INVALID;
    8121150        pCodec->Parms.enmVideoCodec = RecordingVideoCodec_None;
    813     }
    814 
    815     return vrc;
    816 }
    817 
    818 /**
    819  * Feeds the codec encoder with data to encode.
     1151
     1152        int vrc2 = RTCritSectDelete(&pCodec->CritSect);
     1153        AssertRC(vrc2);
     1154    }
     1155
     1156    return vrc;
     1157}
     1158
     1159/**
     1160 * Feeds the codec encoder with frame data to encode.
    8201161 *
    8211162 * @returns VBox status code.
    8221163 * @param   pCodec              Codec to use.
    8231164 * @param   pFrame              Pointer to frame data to encode.
    824  * @param   pcEncoded           Where to return the number of encoded blocks in \a pvDst on success. Optional.
    825  * @param   pcbEncoded          Where to return the number of encoded bytes in \a pvDst on success. Optional.
    826  */
    827 int recordingCodecEncode(PRECORDINGCODEC pCodec,
    828                          const PRECORDINGFRAME pFrame, size_t *pcEncoded, size_t *pcbEncoded)
     1165 * @param   msTimestamp         Timestamp (PTS) to use for encoding.
     1166 * @param   pvUser              User data pointer. Optional and can be NULL.
     1167 */
     1168int recordingCodecEncodeFrame(PRECORDINGCODEC pCodec, const PRECORDINGFRAME pFrame, uint64_t msTimestamp, void *pvUser)
    8291169{
    8301170    AssertPtrReturn(pCodec->Ops.pfnEncode, VERR_NOT_SUPPORTED);
    8311171
    832     size_t cEncoded, cbEncoded;
    833     int vrc = pCodec->Ops.pfnEncode(pCodec, pFrame, &cEncoded, &cbEncoded);
     1172    int vrc = pCodec->Ops.pfnEncode(pCodec, pFrame, msTimestamp, pvUser);
    8341173    if (RT_SUCCESS(vrc))
    835     {
    8361174        pCodec->State.tsLastWrittenMs = pFrame->msTimestamp;
    8371175
    838 #ifdef VBOX_WITH_STATISTICS
    839         pCodec->STAM.cEncBlocks += cEncoded;
    840         pCodec->STAM.msEncTotal += pCodec->Parms.msFrame * cEncoded;
    841 #endif
    842         if (pcEncoded)
    843             *pcEncoded = cEncoded;
    844         if (pcbEncoded)
    845             *pcbEncoded = cbEncoded;
    846     }
    847 
    848     return vrc;
     1176    return vrc;
     1177}
     1178
     1179/**
     1180 * Feeds the codec encoder with the current composed image.
     1181 *
     1182 * @returns VBox status code.
     1183 * @param   pCodec              Codec to use.
     1184 * @param   msTimestamp         Timestamp (PTS) to use for encoding.
     1185 */
     1186int recordingCodecEncodeCurrent(PRECORDINGCODEC pCodec, uint64_t msTimestamp)
     1187{
     1188    int vrc = pCodec->Ops.pfnEncode(pCodec, NULL /* pFrame */, msTimestamp, NULL /* pvUser */);
     1189    if (RT_SUCCESS(vrc))
     1190        pCodec->State.tsLastWrittenMs = msTimestamp;
     1191
     1192    return vrc;
     1193}
     1194
     1195/**
     1196 * Lets the codec know that a screen change has happened.
     1197 *
     1198 * @returns VBox status code.
     1199 * @param   pCodec              Codec to use.
     1200 * @param   pInfo               Screen info to send.
     1201 */
     1202int recordingCodecScreenChange(PRECORDINGCODEC pCodec, PRECORDINGSURFACEINFO pInfo)
     1203{
     1204    if (!pCodec->Ops.pfnScreenChange)
     1205        return VINF_SUCCESS;
     1206
     1207    return pCodec->Ops.pfnScreenChange(pCodec, pInfo);
    8491208}
    8501209
     
    8851244{
    8861245    Log3Func(("%RU64 -- tsLastWrittenMs=%RU64 + uDelayMs=%RU32\n",
    887               msTimestamp, pCodec->State.tsLastWrittenMs,pCodec->Parms.Video.uDelayMs));
    888 
    889     if (msTimestamp < pCodec->State.tsLastWrittenMs + pCodec->Parms.Video.uDelayMs)
     1246              msTimestamp, pCodec->State.tsLastWrittenMs,pCodec->Parms.u.Video.uDelayMs));
     1247
     1248    if (msTimestamp < pCodec->State.tsLastWrittenMs + pCodec->Parms.u.Video.uDelayMs)
    8901249        return 0; /* Too early for writing (respect set FPS). */
    8911250
     
    8941253    return pCodec->Parms.cbFrame;
    8951254}
     1255
  • trunk/src/VBox/Main/src-client/RecordingInternals.cpp

    r99739 r105006  
    2727
    2828#include "RecordingInternals.h"
     29#include "RecordingUtils.h"
    2930
    3031#include <iprt/assert.h>
    3132#include <iprt/mem.h>
    3233
    33 #ifdef VBOX_WITH_AUDIO_RECORDING
    34 
    35 /**
    36  * Initializes a recording frame.
    37  *
     34#ifdef DEBUG
     35# include <math.h>
     36# include <iprt/file.h>
     37# include <iprt/formats/bmp.h>
     38#endif
     39
     40#include "../src-client/Remotery.h"
     41
     42
     43/*********************************************************************************************************************************
     44*   Prototypes                                                                                                                   *
     45*********************************************************************************************************************************/
     46DECLINLINE(int) recordingVideoFrameInit(PRECORDINGVIDEOFRAME pFrame, uint32_t fFlags, uint32_t uWidth, uint32_t uHeight, uint32_t uPosX, uint32_t uPosY,
     47                                        uint8_t uBPP, RECORDINGPIXELFMT enmFmt);
     48
     49
     50/**
     51 * Allocates an empty video frame, inline version.
     52 *
     53 * @returns Allocated video frame on success, or NULL on failure.
     54 */
     55DECLINLINE(PRECORDINGVIDEOFRAME) recordingVideoFrameAlloc(void)
     56{
     57    return (PRECORDINGVIDEOFRAME)RTMemAlloc(sizeof(RECORDINGVIDEOFRAME));
     58}
     59
     60/**
     61 * Allocates an empty video frame.
     62 *
     63 * @returns Allocated video frame on success, or NULL on failure.
     64 */
     65PRECORDINGVIDEOFRAME RecordingVideoFrameAlloc(void)
     66{
     67    PRECORDINGVIDEOFRAME pFrame = recordingVideoFrameAlloc();
     68    AssertPtrReturn(pFrame, NULL);
     69    RT_BZERO(pFrame, sizeof(RECORDINGVIDEOFRAME));
     70    return pFrame;
     71}
     72
     73/**
     74 * Returns an allocated video frame from given image data.
     75 *
     76 * @returns Allocated video frame on success, or NULL on failure.
     77 * @param   pvData              Pointer to image data to use.
     78 * @param   x                   X location hint (in pixel) to use for allocated frame.
     79 *                              This is *not* the offset within \a pvData!
     80 * @param   y                   X location hint (in pixel) to use for allocated frame.
     81 *                              This is *not* the offset within \a pvData!
     82 * @param   w                   Width (in pixel) of \a pvData image data.
     83 * @param   h                   Height (in pixel) of \a pvData image data.
     84 * @param   uBPP                Bits per pixel) of \a pvData image data.
     85 * @param   enmFmt              Pixel format of \a pvData image data.
     86 */
     87PRECORDINGVIDEOFRAME RecordingVideoFrameAllocEx(const void *pvData, uint32_t x, uint32_t y, uint32_t w, uint32_t h,
     88                                                uint8_t uBPP, RECORDINGPIXELFMT enmFmt)
     89{
     90    PRECORDINGVIDEOFRAME pFrame = recordingVideoFrameAlloc();
     91    AssertPtrReturn(pFrame, NULL);
     92    int rc = recordingVideoFrameInit(pFrame, RECORDINGVIDEOFRAME_F_VISIBLE, w, h, x, y, uBPP, enmFmt);
     93    AssertRCReturn(rc, NULL);
     94    memcpy(pFrame->pau8Buf, pvData, pFrame->cbBuf);
     95
     96    return VINF_SUCCESS;
     97}
     98
     99/**
     100 * Frees a recording video frame.
     101 *
     102 * @param   pFrame              Pointer to video frame to free. The pointer will be invalid after return.
     103 */
     104void RecordingVideoFrameFree(PRECORDINGVIDEOFRAME pFrame)
     105{
     106    if (!pFrame)
     107        return;
     108
     109    RecordingVideoFrameDestroy(pFrame);
     110
     111    RTMemFree(pFrame);
     112}
     113
     114/**
     115 * Initializes a recording frame, inline version.
     116 *
     117 * @returns VBox status code.
    38118 * @param   pFrame              Pointer to video frame to initialize.
    39  * @param   w                   Width (in pixel) of video frame.
    40  * @param   h                   Height (in pixel) of video frame.
     119 * @param   fFlags              Flags of type RECORDINGVIDEOFRAME_F_XXX.
     120 * @param   uWidth              Width (in pixel) of video frame.
     121 * @param   uHeight             Height (in pixel) of video frame.
     122 * @param   uPosX               X positioning hint.
     123 * @param   uPosY               Y positioning hint.
    41124 * @param   uBPP                Bits per pixel (BPP).
    42  * @param   enmPixelFmt         Pixel format to use.
    43  */
    44 int RecordingVideoFrameInit(PRECORDINGVIDEOFRAME pFrame, int w, int h, uint8_t uBPP, RECORDINGPIXELFMT enmPixelFmt)
    45 {
     125 * @param   enmFmt              Pixel format to use.
     126 */
     127DECLINLINE(int) recordingVideoFrameInit(PRECORDINGVIDEOFRAME pFrame, uint32_t fFlags, uint32_t uWidth, uint32_t uHeight,
     128                                        uint32_t uPosX, uint32_t uPosY, uint8_t uBPP, RECORDINGPIXELFMT enmFmt)
     129{
     130    AssertPtrReturn(pFrame, VERR_INVALID_POINTER);
     131    AssertReturn(uWidth, VERR_INVALID_PARAMETER);
     132    AssertReturn(uHeight, VERR_INVALID_PARAMETER);
     133    AssertReturn(uBPP && uBPP % 8 == 0, VERR_INVALID_PARAMETER);
     134
    46135    /* Calculate bytes per pixel and set pixel format. */
    47136    const unsigned uBytesPerPixel = uBPP / 8;
    48     const   size_t cbRGBBuf       = w * h * uBytesPerPixel;
     137
     138    /* Calculate bytes per pixel and set pixel format. */
     139    const size_t cbRGBBuf = uWidth * uHeight * uBytesPerPixel;
    49140    AssertReturn(cbRGBBuf, VERR_INVALID_PARAMETER);
    50141
    51     pFrame->pu8RGBBuf = (uint8_t *)RTMemAlloc(cbRGBBuf);
    52     AssertPtrReturn(pFrame->pu8RGBBuf, VERR_NO_MEMORY);
    53     pFrame->cbRGBBuf    = cbRGBBuf;
    54 
    55     pFrame->uX            = 0;
    56     pFrame->uY            = 0;
    57     pFrame->uWidth        = w;
    58     pFrame->uHeight       = h;
    59     pFrame->enmPixelFmt   = enmPixelFmt;
    60     pFrame->uBPP          = uBPP;
    61     pFrame->uBytesPerLine = w * uBytesPerPixel;
     142    pFrame->pau8Buf = (uint8_t *)RTMemAlloc(cbRGBBuf);
     143    AssertPtrReturn(pFrame->pau8Buf, VERR_NO_MEMORY);
     144    pFrame->cbBuf  = cbRGBBuf;
     145
     146    pFrame->fFlags             = fFlags;
     147    pFrame->Info.uWidth        = uWidth;
     148    pFrame->Info.uHeight       = uHeight;
     149    pFrame->Info.uBPP          = uBPP;
     150    pFrame->Info.enmPixelFmt   = enmFmt;
     151    pFrame->Info.uBytesPerLine = uWidth * uBytesPerPixel;
     152    pFrame->Pos.x              = uPosX;
     153    pFrame->Pos.y              = uPosY;
    62154
    63155    return VINF_SUCCESS;
     
    65157
    66158/**
     159 * Initializes a recording frame.
     160 *
     161 * @param   pFrame              Pointer to video frame to initialize.
     162 * @param   fFlags              Flags of type RECORDINGVIDEOFRAME_F_XXX.
     163 * @param   uWidth              Width (in pixel) of video frame.
     164 * @param   uHeight             Height (in pixel) of video frame.
     165 * @param   uPosX               X positioning hint.
     166 * @param   uPosY               Y positioning hint.
     167 * @param   uBPP                Bits per pixel (BPP).
     168 * @param   enmFmt              Pixel format to use.
     169 */
     170int RecordingVideoFrameInit(PRECORDINGVIDEOFRAME pFrame, uint32_t fFlags, uint32_t uWidth, uint32_t uHeight, uint32_t uPosX, uint32_t uPosY,
     171                            uint8_t uBPP, RECORDINGPIXELFMT enmFmt)
     172{
     173    return recordingVideoFrameInit(pFrame, fFlags, uWidth, uHeight, uPosX, uPosY, uBPP, enmFmt);
     174}
     175
     176/**
     177 * Destroys a recording video frame.
     178 *
     179 * @param   pFrame              Pointer to video frame to destroy.
     180 */
     181void RecordingVideoFrameDestroy(PRECORDINGVIDEOFRAME pFrame)
     182{
     183    if (!pFrame)
     184        return;
     185
     186    if (pFrame->pau8Buf)
     187    {
     188        Assert(pFrame->cbBuf);
     189        RTMemFree(pFrame->pau8Buf);
     190        pFrame->pau8Buf = NULL;
     191        pFrame->cbBuf  = 0;
     192    }
     193}
     194
     195/**
     196 * Duplicates a video frame.
     197 *
     198 * @returns Pointer to duplicated frame on success, or NULL on failure.
     199 * @param   pFrame              Video frame to duplicate.
     200 */
     201PRECORDINGVIDEOFRAME RecordingVideoFrameDup(PRECORDINGVIDEOFRAME pFrame)
     202{
     203    PRECORDINGVIDEOFRAME pFrameDup = (PRECORDINGVIDEOFRAME)RTMemDup(pFrame, sizeof(RECORDINGVIDEOFRAME));
     204    AssertPtrReturn(pFrameDup, NULL);
     205    pFrameDup->pau8Buf = (uint8_t *)RTMemDup(pFrame->pau8Buf, pFrame->cbBuf);
     206    AssertPtrReturnStmt(pFrameDup, RTMemFree(pFrameDup), NULL);
     207
     208    return pFrameDup;
     209}
     210
     211/**
     212 * Clears the content of a video recording frame, inlined version.
     213 *
     214 * @param   pFrame              Video recording frame to clear content for.
     215 */
     216DECLINLINE(void) recordingVideoFrameClear(PRECORDINGVIDEOFRAME pFrame)
     217{
     218    RT_BZERO(pFrame->pau8Buf, pFrame->cbBuf);
     219}
     220
     221/**
     222 * Clears the content of a video recording frame.
     223 *
     224 * @param   pFrame              Video recording frame to clear content for.
     225 */
     226void RecordingVideoFrameClear(PRECORDINGVIDEOFRAME pFrame)
     227{
     228    recordingVideoFrameClear(pFrame);
     229}
     230
     231/**
     232 * Simple blitting function for raw image data, inlined version.
     233 *
     234 * @returns VBox status code.
     235 * @param   pFrame              Destination frame.
     236 * @param   uDstX               X destination (in pixel) within destination frame.
     237 * @param   uDstY               Y destination (in pixel) within destination frame.
     238 * @param   uDstBytesPerLine    Bytes per line in destination buffer.
     239 * @param   uDstBPP             BPP of destination buffer.
     240 * @param   enmDstFmt           Pixel format of source data. Must match \a pFrame.
     241 * @param   pu8Src              Source data to blit. Must be in the same pixel format as \a pFrame.
     242 * @param   cbSrc               Size (in bytes) of \a pu8Src.
     243 * @param   uSrcX               X start (in pixel) within source data.
     244 * @param   uSrcY               Y start (in pixel) within source data.
     245 * @param   uSrcWidth           Width (in pixel) to blit from source data.
     246 * @param   uSrcHeight          Height (in pixel) to blit from data.
     247 * @param   uSrcBytesPerLine    Bytes per line in source data.
     248 * @param   uSrcBPP             BPP of source data. Must match \a pFrame.
     249 * @param   enmSrcFmt           Pixel format of source data. Must match \a pFrame.
     250 */
     251DECLINLINE(int) recordingVideoFrameBlitRaw(uint8_t *pu8Dst, size_t cbDst, uint32_t uDstX, uint32_t uDstY,
     252                                           uint32_t uDstBytesPerLine, uint8_t uDstBPP, RECORDINGPIXELFMT enmDstFmt,
     253                                           const uint8_t *pu8Src, size_t cbSrc, uint32_t uSrcX, uint32_t uSrcY, uint32_t uSrcWidth, uint32_t uSrcHeight,
     254                                           uint32_t uSrcBytesPerLine, uint8_t uSrcBPP, RECORDINGPIXELFMT enmSrcFmt)
     255{
     256    RT_NOREF(enmDstFmt, enmSrcFmt);
     257
     258    uint8_t const uDstBytesPerPixel = uDstBPP / 8;
     259    uint8_t const uSrcBytesPerPixel = uSrcBPP / 8;
     260
     261    size_t offSrc = RT_MIN(uSrcY * uSrcBytesPerLine + uSrcX * uSrcBytesPerPixel, cbSrc);
     262    size_t offDst = RT_MIN(uDstY * uDstBytesPerLine + uDstX * uDstBytesPerPixel, cbDst);
     263
     264    for (uint32_t y = 0; y < uSrcHeight; y++)
     265    {
     266        size_t const cbToCopy = RT_MIN(cbDst - offDst,
     267                                       RT_MIN(uSrcWidth * uSrcBytesPerPixel, cbSrc - offSrc));
     268        if (!cbToCopy)
     269            break;
     270        memcpy(pu8Dst + offDst, (const uint8_t *)pu8Src + offSrc, cbToCopy);
     271        offDst = RT_MIN(offDst + uDstBytesPerLine, cbDst);
     272        Assert(offDst <= cbDst);
     273        offSrc = RT_MIN(offSrc + uSrcBytesPerLine, cbSrc);
     274        Assert(offSrc <= cbSrc);
     275    }
     276
     277    return VINF_SUCCESS;
     278}
     279
     280/**
     281 * Simple blitting function for raw image data with alpha channel, inlined version.
     282 *
     283 * @returns VBox status code.
     284 * @param   pFrame              Destination frame.
     285 * @param   uDstX               X destination (in pixel) within destination frame.
     286 * @param   uDstY               Y destination (in pixel) within destination frame.
     287 * @param   pu8Src              Source data to blit. Must be in the same pixel format as \a pFrame.
     288 * @param   cbSrc               Size (in bytes) of \a pu8Src.
     289 * @param   uSrcX               X start (in pixel) within source data.
     290 * @param   uSrcY               Y start (in pixel) within source data.
     291 * @param   uSrcWidth           Width (in pixel) to blit from source data.
     292 * @param   uSrcHeight          Height (in pixel) to blit from data.
     293 * @param   uSrcBytesPerLine    Bytes per line in source data.
     294 * @param   uSrcBPP             BPP of source data. Must match \a pFrame.
     295 * @param   enmFmt              Pixel format of source data. Must match \a pFrame.
     296 */
     297DECLINLINE(int) recordingVideoFrameBlitRawAlpha(PRECORDINGVIDEOFRAME pFrame, uint32_t uDstX, uint32_t uDstY,
     298                                                const uint8_t *pu8Src, size_t cbSrc, uint32_t uSrcX, uint32_t uSrcY, uint32_t uSrcWidth, uint32_t uSrcHeight,
     299                                                uint32_t uSrcBytesPerLine, uint8_t uSrcBPP, RECORDINGPIXELFMT enmFmt)
     300{
     301    AssertReturn(pFrame->Info.enmPixelFmt == enmFmt, VERR_NOT_SUPPORTED);
     302    AssertReturn(pFrame->Info.uBPP == uSrcBPP, VERR_NOT_SUPPORTED);
     303
     304    RT_NOREF(uDstX, uDstY, cbSrc, uSrcX, uSrcY, uSrcBytesPerLine);
     305    uint8_t const uDstBytesPerPixel = pFrame->Info.uBPP / 8;
     306    uint8_t const uSrcBytesPerPixel = uSrcBPP / 8;
     307
     308    for (uint32_t y = 0; y < uSrcHeight; y++)
     309    {
     310        size_t offSrc = RT_MIN((uSrcY + y) * uSrcBytesPerLine + uSrcX * uSrcBytesPerPixel, cbSrc);
     311        size_t offDst = RT_MIN((uDstY + y) * pFrame->Info.uBytesPerLine + uDstX * uDstBytesPerPixel, pFrame->cbBuf);
     312
     313        for (uint32_t x = 0; x < uSrcWidth; x++)
     314        {
     315            /* BGRA */
     316            int const idx_b = 0;
     317            int const idx_g = 1;
     318            int const idx_r = 2;
     319            int const idx_a = 3;
     320
     321            unsigned int const alpha = pu8Src[offSrc + idx_a] + 1;
     322            unsigned int const inv_alpha = 256 - pu8Src[offSrc + idx_a];
     323            if (pu8Src[offSrc + idx_a])
     324            {
     325                pFrame->pau8Buf[offDst + idx_r] = (unsigned char)((alpha * pu8Src[offSrc + idx_r] + inv_alpha * pFrame->pau8Buf[offDst + idx_r]) >> 8);
     326                pFrame->pau8Buf[offDst + idx_g] = (unsigned char)((alpha * pu8Src[offSrc + idx_g] + inv_alpha * pFrame->pau8Buf[offDst + idx_g]) >> 8);
     327                pFrame->pau8Buf[offDst + idx_b] = (unsigned char)((alpha * pu8Src[offSrc + idx_b] + inv_alpha * pFrame->pau8Buf[offDst + idx_b]) >> 8);
     328                pFrame->pau8Buf[offDst + idx_a] = 0xff;
     329            }
     330
     331            offSrc = RT_MIN(offSrc + uSrcBytesPerPixel, cbSrc);
     332            if (offSrc >= cbSrc)
     333                break;
     334            offDst = RT_MIN(offDst + uDstBytesPerPixel, pFrame->cbBuf);
     335            if (offDst >= pFrame->cbBuf)
     336                break;
     337        }
     338    }
     339
     340#if 0
     341    RecordingUtilsDbgDumpImageData(pu8Src, cbSrc, "/tmp", "cursor-src", uSrcWidth, uSrcHeight, uSrcBytesPerLine, 32);
     342    RecordingUtilsDbgDumpVideoFrameEx(pFrame, "/tmp", "cursor-dst");
     343#endif
     344
     345    return VINF_SUCCESS;
     346}
     347
     348/**
     349 * Simple blitting function for raw image data.
     350 *
     351 * @returns VBox status code.
     352 * @param   pDstFrame           Destination frame.
     353 * @param   uDstX               X destination (in pixel) within destination frame.
     354 * @param   uDstY               Y destination (in pixel) within destination frame.
     355 * @param   pu8Src              Source data to blit. Must be in the same pixel format as \a pFrame.
     356 * @param   cbSrc               Size (in bytes) of \a pu8Src.
     357 * @param   uSrcX               X start (in pixel) within source data.
     358 * @param   uSrcY               Y start (in pixel) within source data.
     359 * @param   uSrcWidth           Width (in pixel) to blit from source data.
     360 * @param   uSrcHeight          Height (in pixel) to blit from data.
     361 * @param   uSrcBytesPerLine    Bytes per line in source data.
     362 * @param   uSrcBPP             BPP of source data. Must match \a pFrame.
     363 * @param   enmFmt              Pixel format of source data. Must match \a pFrame.
     364 */
     365int RecordingVideoFrameBlitRaw(PRECORDINGVIDEOFRAME pDstFrame, uint32_t uDstX, uint32_t uDstY,
     366                               const uint8_t *pu8Src, size_t cbSrc, uint32_t uSrcX, uint32_t uSrcY, uint32_t uSrcWidth, uint32_t uSrcHeight,
     367                               uint32_t uSrcBytesPerLine, uint8_t uSrcBPP, RECORDINGPIXELFMT enmFmt)
     368{
     369    return recordingVideoFrameBlitRaw(/* Destination */
     370                                      pDstFrame->pau8Buf, pDstFrame->cbBuf, uDstX, uDstY,
     371                                      pDstFrame->Info.uBytesPerLine, pDstFrame->Info.uBPP, pDstFrame->Info.enmPixelFmt,
     372                                      /* Source */
     373                                      pu8Src, cbSrc, uSrcX, uSrcY, uSrcWidth, uSrcHeight, uSrcBytesPerLine, uSrcBPP, enmFmt);
     374}
     375
     376/**
     377 * Simple blitting function for raw image data with alpha channel.
     378 *
     379 * @returns VBox status code.
     380 * @param   pFrame              Destination frame.
     381 * @param   uDstX               X destination (in pixel) within destination frame.
     382 * @param   uDstY               Y destination (in pixel) within destination frame.
     383 * @param   pu8Src              Source data to blit. Must be in the same pixel format as \a pFrame.
     384 * @param   cbSrc               Size (in bytes) of \a pu8Src.
     385 * @param   uSrcX               X start (in pixel) within source data.
     386 * @param   uSrcY               Y start (in pixel) within source data.
     387 * @param   uSrcWidth           Width (in pixel) to blit from source data.
     388 * @param   uSrcHeight          Height (in pixel) to blit from data.
     389 * @param   uSrcBytesPerLine    Bytes per line in source data.
     390 * @param   uSrcBPP             BPP of source data. Must match \a pFrame.
     391 * @param   enmFmt              Pixel format of source data. Must match \a pFrame.
     392 */
     393int RecordingVideoFrameBlitRawAlpha(PRECORDINGVIDEOFRAME pFrame, uint32_t uDstX, uint32_t uDstY,
     394                                    const uint8_t *pu8Src, size_t cbSrc, uint32_t uSrcX, uint32_t uSrcY, uint32_t uSrcWidth, uint32_t uSrcHeight,
     395                                    uint32_t uSrcBytesPerLine, uint8_t uSrcBPP, RECORDINGPIXELFMT enmFmt)
     396{
     397    return recordingVideoFrameBlitRawAlpha(pFrame, uDstX, uDstY,
     398                                           pu8Src, cbSrc, uSrcX, uSrcY, uSrcWidth, uSrcHeight, uSrcBytesPerLine, uSrcBPP, enmFmt);
     399}
     400
     401/**
     402 * Simple blitting function for video frames.
     403 *
     404 * @returns VBox status code.
     405 * @param   pDstFrame           Destination frame.
     406 * @param   uDstX               X destination (in pixel) within destination frame.
     407 * @param   uDstY               Y destination (in pixel) within destination frame.
     408 * @param   pSrcFrame           Source frame.
     409 * @param   uSrcX               X start (in pixel) within source frame.
     410 * @param   uSrcY               Y start (in pixel) within source frame.
     411 * @param   uSrcWidth           Width (in pixel) to blit from source frame.
     412 * @param   uSrcHeight          Height (in pixel) to blit from frame.
     413 *
     414 * @note    Does NOT check for limits, so use with care!
     415 */
     416int RecordingVideoFrameBlitFrame(PRECORDINGVIDEOFRAME pDstFrame, uint32_t uDstX, uint32_t uDstY,
     417                                 PRECORDINGVIDEOFRAME pSrcFrame, uint32_t uSrcX, uint32_t uSrcY, uint32_t uSrcWidth, uint32_t uSrcHeight)
     418{
     419    return recordingVideoFrameBlitRaw(/* Dest */
     420                                      pDstFrame->pau8Buf, pDstFrame->cbBuf, uDstX, uDstY,
     421                                      pDstFrame->Info.uBytesPerLine, pDstFrame->Info.uBPP, pDstFrame->Info.enmPixelFmt,
     422                                      /* Source */
     423                                      pSrcFrame->pau8Buf, pSrcFrame->cbBuf, uSrcX, uSrcY, uSrcWidth, uSrcHeight,
     424                                      pSrcFrame->Info.uBytesPerLine, pSrcFrame->Info.uBPP, pSrcFrame->Info.enmPixelFmt);
     425}
     426
     427#ifdef VBOX_WITH_AUDIO_RECORDING
     428/**
    67429 * Destroys a recording audio frame.
    68430 *
    69431 * @param   pFrame              Pointer to audio frame to destroy.
    70432 */
    71 static void recordingAudioFrameDestroy(PRECORDINGAUDIOFRAME pFrame)
    72 {
     433DECLINLINE(void) recordingAudioFrameDestroy(PRECORDINGAUDIOFRAME pFrame)
     434{
     435    if (!pFrame)
     436        return;
     437
    73438    if (pFrame->pvBuf)
    74439    {
    75440        Assert(pFrame->cbBuf);
    76441        RTMemFree(pFrame->pvBuf);
     442        pFrame->pvBuf = NULL;
    77443        pFrame->cbBuf = 0;
    78444    }
     
    94460    pFrame = NULL;
    95461}
    96 
    97462#endif /* VBOX_WITH_AUDIO_RECORDING */
    98463
    99464/**
    100  * Destroys a recording video frame.
    101  *
    102  * @param   pFrame              Pointer to video frame to destroy.
    103  */
    104 void RecordingVideoFrameDestroy(PRECORDINGVIDEOFRAME pFrame)
    105 {
    106     if (pFrame->pu8RGBBuf)
    107     {
    108         Assert(pFrame->cbRGBBuf);
    109         RTMemFree(pFrame->pu8RGBBuf);
    110         pFrame->cbRGBBuf = 0;
    111     }
    112 }
    113 
    114 /**
    115  * Frees a recording video frame.
    116  *
    117  * @param   pFrame              Pointer to video frame to free. The pointer will be invalid after return.
    118  */
    119 void RecordingVideoFrameFree(PRECORDINGVIDEOFRAME pFrame)
    120 {
    121     if (!pFrame)
    122         return;
    123 
    124     RecordingVideoFrameDestroy(pFrame);
    125 
    126     RTMemFree(pFrame);
    127 }
    128 
    129 /**
    130465 * Frees a recording frame.
    131466 *
    132  * @param   pFrame              Pointer to recording frame to free. The pointer will be invalid after return.
     467 * @param   pFrame              Pointer to recording frame to free.
     468 *                              The pointer will be invalid after return.
    133469 */
    134470void RecordingFrameFree(PRECORDINGFRAME pFrame)
     
    141477#ifdef VBOX_WITH_AUDIO_RECORDING
    142478        case RECORDINGFRAME_TYPE_AUDIO:
    143             recordingAudioFrameDestroy(&pFrame->Audio);
     479            recordingAudioFrameDestroy(&pFrame->u.Audio);
    144480            break;
    145481#endif
    146482        case RECORDINGFRAME_TYPE_VIDEO:
    147             RecordingVideoFrameDestroy(&pFrame->Video);
     483            RecordingVideoFrameDestroy(&pFrame->u.Video);
    148484            break;
    149485
     486        case RECORDINGFRAME_TYPE_CURSOR_SHAPE:
     487            RecordingVideoFrameDestroy(&pFrame->u.CursorShape);
     488            break;
     489
    150490        default:
    151             AssertFailed();
     491            /* Nothing to do here. */
    152492            break;
    153493    }
  • trunk/src/VBox/Main/src-client/RecordingStream.cpp

    r104752 r105006  
    259259/**
    260260 * Processes a recording stream.
     261 *
    261262 * This function takes care of the actual encoding and writing of a certain stream.
    262263 * As this can be very CPU intensive, this function usually is called from a separate thread.
    263264 *
    264265 * @returns VBox status code.
    265  * @param   mapBlocksCommon     Map of common block to process for this stream.
     266 * @param   streamBlocks        Block set of stream to process.
     267 * @param   commonBlocks        Block set of common blocks to process for this stream.
    266268 *
    267269 * @note    Runs in recording thread.
    268270 */
    269 int RecordingStream::Process(RecordingBlockMap &mapBlocksCommon)
     271int RecordingStream::process(const RecordingBlockSet &streamBlocks, RecordingBlockMap &commonBlocks)
    270272{
    271273    LogFlowFuncEnter();
     
    281283    int vrc = VINF_SUCCESS;
    282284
    283     RecordingBlockMap::iterator itStreamBlocks = m_Blocks.Map.begin();
    284     while (itStreamBlocks != m_Blocks.Map.end())
    285     {
    286         uint64_t const   msTimestamp = itStreamBlocks->first;
    287         RecordingBlocks *pBlocks     = itStreamBlocks->second;
     285    RecordingBlockMap::const_iterator itStreamBlock = streamBlocks.Map.begin();
     286    while (itStreamBlock != streamBlocks.Map.end())
     287    {
     288        uint64_t const   msTimestamp = itStreamBlock->first; RT_NOREF(msTimestamp);
     289        RecordingBlocks *pBlocks     = itStreamBlock->second;
    288290
    289291        AssertPtr(pBlocks);
    290292
    291         while (!pBlocks->List.empty())
     293        RecordingBlockList::const_iterator itBlockInList = pBlocks->List.cbegin();
     294        while (itBlockInList != pBlocks->List.cend())
    292295        {
    293             RecordingBlock *pBlock = pBlocks->List.front();
    294             AssertPtr(pBlock);
    295 
    296             switch (pBlock->enmType)
    297             {
    298                 case RECORDINGBLOCKTYPE_VIDEO:
    299                 {
    300                     RECORDINGFRAME Frame;
    301                     Frame.VideoPtr    = (PRECORDINGVIDEOFRAME)pBlock->pvData;
    302                     Frame.msTimestamp = msTimestamp;
    303 
    304                     int vrc2 = recordingCodecEncode(&m_CodecVideo, &Frame, NULL, NULL);
     296            PRECORDINGFRAME pFrame = (PRECORDINGFRAME)(*itBlockInList)->pvData;
     297            AssertPtr(pFrame);
     298            Assert(pFrame->msTimestamp == msTimestamp);
     299
     300            switch (pFrame->enmType)
     301            {
     302                case RECORDINGFRAME_TYPE_VIDEO:
     303                    RT_FALL_THROUGH();
     304                case RECORDINGFRAME_TYPE_CURSOR_SHAPE:
     305                    RT_FALL_THROUGH();
     306                case RECORDINGFRAME_TYPE_CURSOR_POS:
     307                {
     308                    int vrc2 = recordingCodecEncodeFrame(&m_CodecVideo, pFrame, pFrame->msTimestamp, m_pCtx /* pvUser */);
    305309                    AssertRC(vrc2);
    306310                    if (RT_SUCCESS(vrc))
    307311                        vrc = vrc2;
    308 
    309312                    break;
    310313                }
    311314
     315                case RECORDINGFRAME_TYPE_SCREEN_CHANGE:
     316                {
     317                    /* ignore rc */ recordingCodecScreenChange(&m_CodecVideo, &pFrame->u.ScreenInfo);
     318                    break;
     319                }
     320
    312321                default:
    313                     /* Note: Audio data already is encoded. */
    314322                    break;
    315323            }
    316324
    317             pBlocks->List.pop_front();
    318             delete pBlock;
     325            ++itBlockInList;
    319326        }
    320327
    321         Assert(pBlocks->List.empty());
    322         delete pBlocks;
    323 
    324         m_Blocks.Map.erase(itStreamBlocks);
    325         itStreamBlocks = m_Blocks.Map.begin();
     328        ++itStreamBlock;
    326329    }
    327330
     
    332335        /* As each (enabled) screen has to get the same audio data, look for common (audio) data which needs to be
    333336         * written to the screen's assigned recording stream. */
    334         RecordingBlockMap::iterator itCommonBlocks = mapBlocksCommon.begin();
    335         while (itCommonBlocks != mapBlocksCommon.end())
     337        RecordingBlockMap::const_iterator itBlockMap = commonBlocks.begin();
     338        while (itBlockMap != commonBlocks.end())
    336339        {
    337             RecordingBlockList::iterator itBlock = itCommonBlocks->second->List.begin();
    338             while (itBlock != itCommonBlocks->second->List.end())
    339             {
    340                 RecordingBlock *pBlockCommon = (RecordingBlock *)(*itBlock);
    341                 switch (pBlockCommon->enmType)
    342                 {
    343                     case RECORDINGBLOCKTYPE_AUDIO:
    344                     {
    345                         PRECORDINGAUDIOFRAME pAudioFrame = (PRECORDINGAUDIOFRAME)pBlockCommon->pvData;
    346                         AssertPtr(pAudioFrame);
    347                         AssertPtr(pAudioFrame->pvBuf);
    348                         Assert(pAudioFrame->cbBuf);
    349 
    350                         AssertPtr(this->File.m_pWEBM);
    351                         int vrc2 = this->File.m_pWEBM->WriteBlock(m_uTrackAudio, pAudioFrame->pvBuf, pAudioFrame->cbBuf, pBlockCommon->msTimestamp, pBlockCommon->uFlags);
    352                         AssertRC(vrc2);
    353                         if (RT_SUCCESS(vrc))
    354                             vrc = vrc2;
    355                         break;
    356                     }
    357 
    358                     default:
    359                         AssertFailed();
    360                         break;
    361                 }
    362 
    363                 Assert(pBlockCommon->cRefs);
    364                 pBlockCommon->cRefs--;
    365                 if (pBlockCommon->cRefs == 0)
    366                 {
    367                     itCommonBlocks->second->List.erase(itBlock);
    368                     delete pBlockCommon;
    369                     itBlock = itCommonBlocks->second->List.begin();
     340            RecordingBlockList &blockList = itBlockMap->second->List;
     341
     342            RecordingBlockList::iterator itBlockList = blockList.begin();
     343            while (itBlockList != blockList.end())
     344            {
     345                RecordingBlock *pBlock = (RecordingBlock *)(*itBlockList);
     346
     347                PRECORDINGFRAME      pFrame      = (PRECORDINGFRAME)pBlock->pvData;
     348                Assert(pFrame->enmType == RECORDINGFRAME_TYPE_AUDIO);
     349                PRECORDINGAUDIOFRAME pAudioFrame = &pFrame->u.Audio;
     350
     351                int vrc2 = this->File.m_pWEBM->WriteBlock(m_uTrackAudio, pAudioFrame->pvBuf, pAudioFrame->cbBuf, pBlock->msTimestamp, pBlock->uFlags);
     352                if (RT_SUCCESS(vrc))
     353                    vrc = vrc2;
     354
     355                Log3Func(("RECORDINGFRAME_TYPE_AUDIO: %zu bytes -> %Rrc\n", pAudioFrame->cbBuf, vrc2));
     356
     357                Assert(pBlock->cRefs);
     358                pBlock->cRefs--;
     359                if (pBlock->cRefs == 0)
     360                {
     361                    blockList.erase(itBlockList);
     362                    delete pBlock;
     363                    itBlockList = blockList.begin();
    370364                }
    371365                else
    372                     ++itBlock;
    373             }
    374 
    375             /* If no entries are left over in the block map, remove it altogether. */
    376             if (itCommonBlocks->second->List.empty())
    377             {
    378                 delete itCommonBlocks->second;
    379                 mapBlocksCommon.erase(itCommonBlocks);
    380                 itCommonBlocks = mapBlocksCommon.begin();
     366                    ++itBlockList;
     367            }
     368
     369            /* If no entries are left over in the block list, remove it altogether. */
     370            if (blockList.empty())
     371            {
     372                delete itBlockMap->second;
     373                commonBlocks.erase(itBlockMap);
     374                itBlockMap = commonBlocks.begin();
    381375            }
    382376            else
    383                 ++itCommonBlocks;
    384 
    385             LogFunc(("Common blocks: %zu\n", mapBlocksCommon.size()));
     377                ++itBlockMap;
    386378        }
    387379    }
    388380#else
    389     RT_NOREF(mapBlocksCommon);
     381    RT_NOREF(commonBlocks);
    390382#endif /* VBOX_WITH_AUDIO_RECORDING */
    391383
     
    393385
    394386    LogFlowFuncLeaveRC(vrc);
     387    return vrc;
     388}
     389
     390/**
     391 * The stream's main routine called from the encoding thread.
     392 *
     393 * @returns VBox status code.
     394 * @param   rcWait              Result of the encoding thread's wait operation.
     395 *                              Can be used for figuring out if the encoder has to perform some
     396 *                              worked based on that result.
     397 * @param   commonBlocks        Common blocks multiplexed to all recording streams.
     398 *
     399 * @note    Runs in encoding thread.
     400 */
     401int RecordingStream::ThreadMain(int rcWait, RecordingBlockMap &commonBlocks)
     402{
     403    Log3Func(("rcWait=%Rrc\n", rcWait));
     404
     405    /* No new data arrived within time? Feed the encoder with the last frame we built.
     406     *
     407     * This is necessary in order to render a video which has a consistent time line,
     408     * as we only encode data when something has changed ("dirty areas"). */
     409    if (   rcWait == VERR_TIMEOUT
     410        && m_ScreenSettings.isFeatureEnabled(RecordingFeature_Video))
     411    {
     412        return recordingCodecEncodeCurrent(&m_CodecVideo, m_pCtx->GetCurrentPTS());
     413    }
     414
     415    int vrc = process(m_Blocks, commonBlocks);
     416
     417    /*
     418     * Housekeeping.
     419     *
     420     * Here we delete all processed stream blocks of this stream.
     421     * The common blocks will be deleted by the recording context (which owns those).
     422     */
     423    lock();
     424
     425    RecordingBlockMap::iterator itStreamBlocks = m_Blocks.Map.begin();
     426    while (itStreamBlocks != m_Blocks.Map.end())
     427    {
     428        RecordingBlocks *pBlocks = itStreamBlocks->second;
     429        AssertPtr(pBlocks);
     430        pBlocks->Clear();
     431        Assert(pBlocks->List.empty());
     432        delete pBlocks;
     433
     434        m_Blocks.Map.erase(itStreamBlocks);
     435        itStreamBlocks = m_Blocks.Map.begin();
     436    }
     437    Assert(m_Blocks.Map.empty());
     438
     439    unlock();
     440
     441    return vrc;
     442}
     443
     444/**
     445 * Adds a recording frame to be fed to the encoder.
     446 *
     447 * @returns VBox status code.
     448 * @param   pFrame              Recording frame to add.
     449 *                              Ownership of the frame will be transferred to the encoder on success then.
     450 *                              Must be free'd by the caller on failure.
     451 * @param   msTimestamp         Timestamp (PTS, in ms).
     452 *
     453 * @note    Caller needs to take the stream's lock.
     454 */
     455int RecordingStream::addFrame(PRECORDINGFRAME pFrame, uint64_t msTimestamp)
     456{
     457    int vrc;
     458
     459    Assert(pFrame->msTimestamp == msTimestamp); /* Sanity. */
     460
     461    try
     462    {
     463        RecordingBlock *pBlock = new RecordingBlock();
     464
     465        pBlock->pvData = pFrame;
     466        pBlock->cbData = sizeof(RECORDINGFRAME);
     467
     468        try
     469        {
     470            RecordingBlocks *pRecordingBlocks;
     471            RecordingBlockMap::const_iterator it = m_Blocks.Map.find(msTimestamp);
     472            if (it == m_Blocks.Map.end())
     473            {
     474                pRecordingBlocks = new RecordingBlocks();
     475                pRecordingBlocks->List.push_back(pBlock);
     476                m_Blocks.Map.insert(std::make_pair(msTimestamp, pRecordingBlocks));
     477            }
     478            else
     479            {
     480                pRecordingBlocks = it->second;
     481                pRecordingBlocks->List.push_back(pBlock);
     482            }
     483
     484            vrc = VINF_SUCCESS;
     485        }
     486        catch (const std::exception &)
     487        {
     488            delete pBlock;
     489            vrc = VERR_NO_MEMORY;
     490        }
     491    }
     492    catch (const std::exception &)
     493    {
     494        vrc = VERR_NO_MEMORY;
     495    }
     496
    395497    return vrc;
    396498}
     
    407509{
    408510    AssertPtrReturn(m_pCtx, VERR_WRONG_ORDER);
    409     AssertReturn(NeedsUpdate(msTimestamp), VINF_RECORDING_THROTTLED); /* We ASSUME that the caller checked that first. */
    410 
    411     Log3Func(("cbData=%zu, msTimestamp=%RU64\n", cbData, msTimestamp));
    412511
    413512    /* As audio data is common across all streams, re-route this to the recording context, where
     
    417516
    418517/**
     518 * Sends a cursor position change to the recording stream.
     519 *
     520 * @returns VBox status code.
     521 * @param   idCursor            Cursor ID. Currently unused and always set to 0.
     522 * @param   pPos                Cursor information to send.
     523 * @param   msTimestamp         Timestamp (PTS, in ms).
     524 */
     525int RecordingStream::SendCursorPos(uint8_t idCursor, PRECORDINGPOS pPos, uint64_t msTimestamp)
     526{
     527    RT_NOREF(idCursor);
     528    AssertPtrReturn(pPos, VERR_INVALID_POINTER);
     529
     530    lock();
     531
     532    int vrc = iterateInternal(msTimestamp);
     533    if (vrc != VINF_SUCCESS) /* Can return VINF_RECORDING_LIMIT_REACHED. */
     534    {
     535        unlock();
     536        return vrc;
     537    }
     538
     539    PRECORDINGFRAME pFrame = (PRECORDINGFRAME)RTMemAlloc(sizeof(RECORDINGFRAME));
     540    AssertPtrReturn(pFrame, VERR_NO_MEMORY);
     541    pFrame->enmType     = RECORDINGFRAME_TYPE_CURSOR_POS;
     542    pFrame->msTimestamp = msTimestamp;
     543
     544    pFrame->u.Cursor.Pos = *pPos;
     545
     546    vrc = addFrame(pFrame, msTimestamp);
     547
     548    unlock();
     549
     550    return vrc;
     551}
     552
     553/**
     554 * Sends a cursor shape change to the recording stream.
     555 *
     556 * @returns VBox status code.
     557 * @param   idCursor            Cursor ID. Currently unused and always set to 0.
     558 * @param   pShape              Cursor shape to send.
     559 * @param   msTimestamp         Timestamp (PTS, in ms).
     560 *
     561 * @note    Keep it as simple as possible, as this function might run on EMT.
     562 * @thread  EMT
     563 */
     564int RecordingStream::SendCursorShape(uint8_t idCursor, PRECORDINGVIDEOFRAME pShape, uint64_t msTimestamp)
     565{
     566    RT_NOREF(idCursor);
     567    AssertPtrReturn(pShape, VERR_INVALID_POINTER);
     568    AssertPtrReturn(m_pCtx, VERR_WRONG_ORDER);
     569
     570    lock();
     571
     572    int vrc = iterateInternal(msTimestamp);
     573    if (vrc != VINF_SUCCESS) /* Can return VINF_RECORDING_LIMIT_REACHED. */
     574    {
     575        unlock();
     576        return vrc;
     577    }
     578
     579    PRECORDINGFRAME pFrame = (PRECORDINGFRAME)RTMemAlloc(sizeof(RECORDINGFRAME));
     580    AssertPtrReturn(pFrame, VERR_NO_MEMORY);
     581
     582    pFrame->u.Video = *pShape;
     583    /* Make a deep copy of the pixel data. */
     584    pFrame->u.Video.pau8Buf = (uint8_t *)RTMemDup(pShape->pau8Buf, pShape->cbBuf);
     585    AssertPtrReturnStmt(pFrame->u.Video.pau8Buf, RTMemFree(pFrame), VERR_NO_MEMORY);
     586    pFrame->u.Video.cbBuf   = pShape->cbBuf;
     587
     588    pFrame->enmType     = RECORDINGFRAME_TYPE_CURSOR_SHAPE;
     589    pFrame->msTimestamp = msTimestamp;
     590
     591    vrc = addFrame(pFrame, msTimestamp);
     592
     593    if (RT_FAILURE(vrc))
     594    {
     595        RecordingVideoFrameDestroy(&pFrame->u.Video);
     596        RecordingFrameFree(pFrame);
     597    }
     598
     599    unlock();
     600
     601    LogFlowFuncLeaveRC(vrc);
     602    return vrc;
     603}
     604
     605/**
    419606 * Sends a raw (e.g. not yet encoded) video frame to the recording stream.
    420607 *
     
    422609 * @retval  VINF_RECORDING_LIMIT_REACHED if the stream's recording limit has been reached.
    423610 * @retval  VINF_RECORDING_THROTTLED if the frame is too early for the current FPS setting.
    424  * @param   x                   Upper left (X) coordinate where the video frame starts.
    425  * @param   y                   Upper left (Y) coordinate where the video frame starts.
    426  * @param   uPixelFormat        Pixel format of the video frame.
    427  * @param   uBPP                Bits per pixel (BPP) of the video frame.
    428  * @param   uBytesPerLine       Bytes per line  of the video frame.
    429  * @param   uSrcWidth           Width (in pixels) of the video frame.
    430  * @param   uSrcHeight          Height (in pixels) of the video frame.
    431  * @param   puSrcData           Actual pixel data of the video frame.
     611 * @param   pVideoFrame         Video frame to send.
    432612 * @param   msTimestamp         Timestamp (PTS, in ms).
    433  */
    434 int RecordingStream::SendVideoFrame(uint32_t x, uint32_t y, uint32_t uPixelFormat, uint32_t uBPP, uint32_t uBytesPerLine,
    435                                     uint32_t uSrcWidth, uint32_t uSrcHeight, uint8_t *puSrcData, uint64_t msTimestamp)
    436 {
     613 *
     614 * @note    Keep it as simple as possible, as this function might run on EMT.
     615 * @thread  EMT
     616 */
     617int RecordingStream::SendVideoFrame(PRECORDINGVIDEOFRAME pVideoFrame, uint64_t msTimestamp)
     618{
     619    AssertPtrReturn(pVideoFrame, VERR_INVALID_POINTER);
    437620    AssertPtrReturn(m_pCtx, VERR_WRONG_ORDER);
    438621
    439     if (RT_UNLIKELY(!NeedsUpdate(msTimestamp)))
    440         return VINF_RECORDING_THROTTLED;
    441 
    442622    lock();
    443 
    444     Log3Func(("[%RU32 %RU32 %RU32 %RU32] msTimestamp=%RU64\n", x , y, uSrcWidth, uSrcHeight, msTimestamp));
    445 
    446     PRECORDINGVIDEOFRAME pFrame = NULL;
    447623
    448624    int vrc = iterateInternal(msTimestamp);
     
    453629    }
    454630
    455     do
    456     {
    457         int xDiff = ((int)m_ScreenSettings.Video.ulWidth - (int)uSrcWidth) / 2;
    458         uint32_t w = uSrcWidth;
    459         if ((int)w + xDiff + (int)x <= 0)  /* Nothing visible. */
    460         {
    461             vrc = VERR_INVALID_PARAMETER;
    462             break;
    463         }
    464 
    465         uint32_t destX;
    466         if ((int)x < -xDiff)
    467         {
    468             w += xDiff + x;
    469             x = -xDiff;
    470             destX = 0;
    471         }
    472         else
    473             destX = x + xDiff;
    474 
    475         uint32_t h = uSrcHeight;
    476         int yDiff = ((int)m_ScreenSettings.Video.ulHeight - (int)uSrcHeight) / 2;
    477         if ((int)h + yDiff + (int)y <= 0)  /* Nothing visible. */
    478         {
    479             vrc = VERR_INVALID_PARAMETER;
    480             break;
    481         }
    482 
    483         uint32_t destY;
    484         if ((int)y < -yDiff)
    485         {
    486             h += yDiff + (int)y;
    487             y = -yDiff;
    488             destY = 0;
    489         }
    490         else
    491             destY = y + yDiff;
    492 
    493         if (   destX > m_ScreenSettings.Video.ulWidth
    494             || destY > m_ScreenSettings.Video.ulHeight)
    495         {
    496             vrc = VERR_INVALID_PARAMETER;  /* Nothing visible. */
    497             break;
    498         }
    499 
    500         if (destX + w > m_ScreenSettings.Video.ulWidth)
    501             w = m_ScreenSettings.Video.ulWidth - destX;
    502 
    503         if (destY + h > m_ScreenSettings.Video.ulHeight)
    504             h = m_ScreenSettings.Video.ulHeight - destY;
    505 
    506         pFrame = (PRECORDINGVIDEOFRAME)RTMemAllocZ(sizeof(RECORDINGVIDEOFRAME));
    507         AssertBreakStmt(pFrame, vrc = VERR_NO_MEMORY);
    508 
    509         /* Calculate bytes per pixel and set pixel format. */
    510         const unsigned uBytesPerPixel = uBPP / 8;
    511         if (uPixelFormat == BitmapFormat_BGR)
    512         {
    513             switch (uBPP)
    514             {
    515                 case 32:
    516                     pFrame->enmPixelFmt = RECORDINGPIXELFMT_RGB32;
    517                     break;
    518                 case 24:
    519                     pFrame->enmPixelFmt = RECORDINGPIXELFMT_RGB24;
    520                     break;
    521                 case 16:
    522                     pFrame->enmPixelFmt = RECORDINGPIXELFMT_RGB565;
    523                     break;
    524                 default:
    525                     AssertMsgFailedBreakStmt(("Unknown color depth (%RU32)\n", uBPP), vrc = VERR_NOT_SUPPORTED);
    526                     break;
    527             }
    528         }
    529         else
    530             AssertMsgFailedBreakStmt(("Unknown pixel format (%RU32)\n", uPixelFormat), vrc = VERR_NOT_SUPPORTED);
    531 
    532         const size_t cbRGBBuf =   m_ScreenSettings.Video.ulWidth
    533                                 * m_ScreenSettings.Video.ulHeight
    534                                 * uBytesPerPixel;
    535         AssertBreakStmt(cbRGBBuf, vrc = VERR_INVALID_PARAMETER);
    536 
    537         pFrame->pu8RGBBuf = (uint8_t *)RTMemAlloc(cbRGBBuf);
    538         AssertBreakStmt(pFrame->pu8RGBBuf, vrc = VERR_NO_MEMORY);
    539         pFrame->cbRGBBuf  = cbRGBBuf;
    540         pFrame->uWidth    = uSrcWidth;
    541         pFrame->uHeight   = uSrcHeight;
    542 
    543         /* If the current video frame is smaller than video resolution we're going to encode,
    544          * clear the frame beforehand to prevent artifacts. */
    545         if (   uSrcWidth  < m_ScreenSettings.Video.ulWidth
    546             || uSrcHeight < m_ScreenSettings.Video.ulHeight)
    547         {
    548             RT_BZERO(pFrame->pu8RGBBuf, pFrame->cbRGBBuf);
    549         }
    550 
    551         /* Calculate start offset in source and destination buffers. */
    552         uint32_t offSrc = y * uBytesPerLine + x * uBytesPerPixel;
    553         uint32_t offDst = (destY * m_ScreenSettings.Video.ulWidth + destX) * uBytesPerPixel;
    554 
    555 #ifdef VBOX_RECORDING_DUMP
    556         BMPFILEHDR fileHdr;
    557         RT_ZERO(fileHdr);
    558 
    559         BMPWIN3XINFOHDR coreHdr;
    560         RT_ZERO(coreHdr);
    561 
    562         fileHdr.uType       = BMP_HDR_MAGIC;
    563         fileHdr.cbFileSize = (uint32_t)(sizeof(BMPFILEHDR) + sizeof(BMPWIN3XINFOHDR) + (w * h * uBytesPerPixel));
    564         fileHdr.offBits    = (uint32_t)(sizeof(BMPFILEHDR) + sizeof(BMPWIN3XINFOHDR));
    565 
    566         coreHdr.cbSize         = sizeof(BMPWIN3XINFOHDR);
    567         coreHdr.uWidth         = w;
    568         coreHdr.uHeight        = h;
    569         coreHdr.cPlanes        = 1;
    570         coreHdr.cBits          = uBPP;
    571         coreHdr.uXPelsPerMeter = 5000;
    572         coreHdr.uYPelsPerMeter = 5000;
    573 
    574         char szFileName[RTPATH_MAX];
    575         RTStrPrintf2(szFileName, sizeof(szFileName), "/tmp/VideoRecFrame-%RU32.bmp", m_uScreenID);
    576 
    577         RTFILE fh;
    578         int vrc2 = RTFileOpen(&fh, szFileName,
    579                               RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
    580         if (RT_SUCCESS(vrc2))
    581         {
    582             RTFileWrite(fh, &fileHdr,    sizeof(fileHdr),    NULL);
    583             RTFileWrite(fh, &coreHdr, sizeof(coreHdr), NULL);
    584         }
    585 #endif
    586         Assert(pFrame->cbRGBBuf >= w * h * uBytesPerPixel);
    587 
    588         /* Do the copy. */
    589         for (unsigned int i = 0; i < h; i++)
    590         {
    591             /* Overflow check. */
    592             Assert(offSrc + w * uBytesPerPixel <= uSrcHeight * uBytesPerLine);
    593             Assert(offDst + w * uBytesPerPixel <= m_ScreenSettings.Video.ulHeight * m_ScreenSettings.Video.ulWidth * uBytesPerPixel);
    594 
    595             memcpy(pFrame->pu8RGBBuf + offDst, puSrcData + offSrc, w * uBytesPerPixel);
    596 
    597 #ifdef VBOX_RECORDING_DUMP
    598             if (RT_SUCCESS(rc2))
    599                 RTFileWrite(fh, pFrame->pu8RGBBuf + offDst, w * uBytesPerPixel, NULL);
    600 #endif
    601             offSrc += uBytesPerLine;
    602             offDst += m_ScreenSettings.Video.ulWidth * uBytesPerPixel;
    603         }
    604 
    605 #ifdef VBOX_RECORDING_DUMP
    606         if (RT_SUCCESS(vrc2))
    607             RTFileClose(fh);
    608 #endif
    609 
    610     } while (0);
    611 
    612     if (vrc == VINF_SUCCESS) /* Note: Also could be VINF_TRY_AGAIN. */
    613     {
    614         RecordingBlock *pBlock = new RecordingBlock();
    615         if (pBlock)
    616         {
    617             AssertPtr(pFrame);
    618 
    619             pBlock->enmType = RECORDINGBLOCKTYPE_VIDEO;
    620             pBlock->pvData  = pFrame;
    621             pBlock->cbData  = sizeof(RECORDINGVIDEOFRAME) + pFrame->cbRGBBuf;
    622 
    623             try
    624             {
    625                 RecordingBlocks *pRecordingBlocks = new RecordingBlocks();
    626                 pRecordingBlocks->List.push_back(pBlock);
    627 
    628                 Assert(m_Blocks.Map.find(msTimestamp) == m_Blocks.Map.end());
    629                 m_Blocks.Map.insert(std::make_pair(msTimestamp, pRecordingBlocks));
    630             }
    631             catch (const std::exception &ex)
    632             {
    633                 RT_NOREF(ex);
    634 
    635                 delete pBlock;
    636                 vrc = VERR_NO_MEMORY;
    637             }
    638         }
    639         else
    640             vrc = VERR_NO_MEMORY;
    641     }
     631    PRECORDINGFRAME pFrame = (PRECORDINGFRAME)RTMemAlloc(sizeof(RECORDINGFRAME));
     632    AssertPtrReturn(pFrame, VERR_NO_MEMORY);
     633
     634    pFrame->u.Video = *pVideoFrame;
     635    /* Make a deep copy of the pixel data. */
     636    pFrame->u.Video.pau8Buf = (uint8_t *)RTMemDup(pVideoFrame->pau8Buf, pVideoFrame->cbBuf);
     637    AssertPtrReturnStmt(pFrame->u.Video.pau8Buf, RTMemFree(pFrame), VERR_NO_MEMORY);
     638    pFrame->u.Video.cbBuf   = pVideoFrame->cbBuf;
     639
     640    pFrame->enmType     = RECORDINGFRAME_TYPE_VIDEO;
     641    pFrame->msTimestamp = msTimestamp;
     642
     643    vrc = addFrame(pFrame, msTimestamp);
    642644
    643645    if (RT_FAILURE(vrc))
    644         RecordingVideoFrameFree(pFrame);
     646    {
     647        RecordingVideoFrameDestroy(&pFrame->u.Video);
     648        RecordingFrameFree(pFrame);
     649    }
     650
     651    unlock();
     652
     653    LogFlowFuncLeaveRC(vrc);
     654    return vrc;
     655}
     656
     657/**
     658 * Sends a screen size change to a recording stream.
     659 *
     660 * @returns VBox status code.
     661 * @param   pInfo               Recording screen info to use.
     662 * @param   msTimestamp         Timestamp (PTS, in ms).
     663 * @param   fForce              Set to \c true to force a change, otherwise to \c false.
     664 */
     665int RecordingStream::SendScreenChange(PRECORDINGSURFACEINFO pInfo, uint64_t msTimestamp, bool fForce /* = false */)
     666{
     667    AssertPtrReturn(pInfo, VERR_INVALID_POINTER);
     668
     669    if (   !pInfo->uWidth
     670        || !pInfo->uHeight)
     671        return VINF_SUCCESS;
     672
     673    RT_NOREF(fForce);
     674
     675    LogRel(("Recording: Size of screen #%RU32 changed to %RU32x%RU32 (%RU8 BPP)\n",
     676            m_uScreenID, pInfo->uWidth, pInfo->uHeight, pInfo->uBPP));
     677
     678    lock();
     679
     680    PRECORDINGFRAME pFrame = (PRECORDINGFRAME)RTMemAlloc(sizeof(RECORDINGFRAME));
     681    AssertPtrReturn(pFrame, VERR_NO_MEMORY);
     682    pFrame->enmType      = RECORDINGFRAME_TYPE_SCREEN_CHANGE;
     683    pFrame->msTimestamp  = msTimestamp;
     684
     685    pFrame->u.ScreenInfo = *pInfo;
     686
     687    int vrc = addFrame(pFrame, msTimestamp);
    645688
    646689    unlock();
     
    795838        m_enmState  = RECORDINGSTREAMSTATE_INITIALIZED;
    796839        m_fEnabled  = true;
    797         m_tsStartMs = RTTimeProgramMilliTS();
     840        m_tsStartMs = RTTimeMilliTS();
    798841
    799842        return VINF_SUCCESS;
     
    817860{
    818861    int vrc = VINF_SUCCESS;
     862
     863    /* ignore rc */ recordingCodecFinalize(&m_CodecVideo);
    819864
    820865    switch (m_ScreenSettings.enmDest)
     
    918963
    919964    if (m_ScreenSettings.isFeatureEnabled(RecordingFeature_Video))
    920     {
    921         vrc = recordingCodecFinalize(&m_CodecVideo);
    922         if (RT_SUCCESS(vrc))
    923             vrc = recordingCodecDestroy(&m_CodecVideo);
    924     }
     965        vrc = recordingCodecDestroy(&m_CodecVideo);
    925966
    926967    if (RT_SUCCESS(vrc))
     
    953994
    954995    WebMWriter::WebMBlockFlags blockFlags = VBOX_WEBM_BLOCK_FLAG_NONE;
    955     if (RT_LIKELY(uFlags != RECORDINGCODEC_ENC_F_NONE))
     996    if (RT_LIKELY(uFlags == RECORDINGCODEC_ENC_F_NONE))
    956997    {
    957998        /* All set. */
     
    9661007
    9671008    return this->File.m_pWEBM->WriteBlock(  pCodec->Parms.enmType == RECORDINGCODECTYPE_AUDIO
    968                                         ? m_uTrackAudio : m_uTrackVideo,
    969                                         pvData, cbData, msAbsPTS, blockFlags);
     1009                                          ? m_uTrackAudio : m_uTrackVideo,
     1010                                          pvData, cbData, msAbsPTS, blockFlags);
    9701011}
    9711012
     
    10121053    Callbacks.pfnWriteData = RecordingStream::codecWriteDataCallback;
    10131054
    1014     int vrc = recordingCodecCreateVideo(pCodec, screenSettings.Video.enmCodec);
     1055    RECORDINGSURFACEINFO ScreenInfo;
     1056    ScreenInfo.uWidth  = screenSettings.Video.ulWidth;
     1057    ScreenInfo.uHeight = screenSettings.Video.ulHeight;
     1058    ScreenInfo.uBPP    = 32; /* We always start with 32 bit. */
     1059
     1060    int vrc = SendScreenChange(&ScreenInfo, true /* fForce */);
    10151061    if (RT_SUCCESS(vrc))
    1016         vrc = recordingCodecInit(pCodec, &Callbacks, screenSettings);
     1062    {
     1063        vrc = recordingCodecCreateVideo(pCodec, screenSettings.Video.enmCodec);
     1064        if (RT_SUCCESS(vrc))
     1065            vrc = recordingCodecInit(pCodec, &Callbacks, screenSettings);
     1066    }
    10171067
    10181068    if (RT_FAILURE(vrc))
  • trunk/src/VBox/Main/src-client/RecordingUtils.cpp

    r98103 r105006  
    4242#endif
    4343
     44#include <VBox/log.h>
     45
    4446
    4547/**
     
    6062    RT_NOREF(aDstWidth, aDstHeight);
    6163
    62     AssertReturn(!(aSrcWidth & 1),  false);
    63     AssertReturn(!(aSrcHeight & 1), false);
    64 
    65     bool fRc = true;
    66     T iter1(aSrcWidth, aSrcHeight, aSrcBuf);
    67     T iter2 = iter1;
    68     iter2.skip(aSrcWidth);
    69     unsigned cPixels = aSrcWidth * aSrcHeight;
    70     unsigned offY = 0;
    71     unsigned offU = cPixels;
    72     unsigned offV = cPixels + cPixels / 4;
    73     unsigned const cyHalf = aSrcHeight / 2;
    74     unsigned const cxHalf = aSrcWidth  / 2;
    75     for (unsigned i = 0; i < cyHalf && fRc; ++i)
    76     {
    77         for (unsigned j = 0; j < cxHalf; ++j)
    78         {
    79             unsigned red, green, blue;
    80             fRc = iter1.getRGB(&red, &green, &blue);
    81             AssertReturn(fRc, false);
    82             aDstBuf[offY] = ((66 * red + 129 * green + 25 * blue + 128) >> 8) + 16;
    83             unsigned u = (((-38 * red - 74 * green + 112 * blue + 128) >> 8) + 128) / 4;
    84             unsigned v = (((112 * red - 94 * green -  18 * blue + 128) >> 8) + 128) / 4;
    85 
    86             fRc = iter1.getRGB(&red, &green, &blue);
    87             AssertReturn(fRc, false);
    88             aDstBuf[offY + 1] = ((66 * red + 129 * green + 25 * blue + 128) >> 8) + 16;
    89             u += (((-38 * red - 74 * green + 112 * blue + 128) >> 8) + 128) / 4;
    90             v += (((112 * red - 94 * green -  18 * blue + 128) >> 8) + 128) / 4;
    91 
    92             fRc = iter2.getRGB(&red, &green, &blue);
    93             AssertReturn(fRc, false);
    94             aDstBuf[offY + aSrcWidth] = ((66 * red + 129 * green + 25 * blue + 128) >> 8) + 16;
    95             u += (((-38 * red - 74 * green + 112 * blue + 128) >> 8) + 128) / 4;
    96             v += (((112 * red - 94 * green -  18 * blue + 128) >> 8) + 128) / 4;
    97 
    98             fRc = iter2.getRGB(&red, &green, &blue);
    99             AssertReturn(fRc, false);
    100             aDstBuf[offY + aSrcWidth + 1] = ((66 * red + 129 * green + 25 * blue + 128) >> 8) + 16;
    101             u += (((-38 * red - 74 * green + 112 * blue + 128) >> 8) + 128) / 4;
    102             v += (((112 * red - 94 * green -  18 * blue + 128) >> 8) + 128) / 4;
    103 
    104             aDstBuf[offU] = u;
    105             aDstBuf[offV] = v;
    106             offY += 2;
    107             ++offU;
    108             ++offV;
    109         }
    110 
    111         iter1.skip(aSrcWidth);
    112         iter2.skip(aSrcWidth);
    113         offY += aSrcWidth;
     64    size_t image_size = aSrcWidth * aSrcHeight;
     65    size_t upos = image_size;
     66    size_t vpos = upos + upos / 4;
     67    size_t i = 0;
     68
     69#define CALC_Y(r, g, b) \
     70    ((66 * r + 129 * g + 25 * b) >> 8) + 16
     71#define CALC_U(r, g, b) \
     72    ((-38 * r + -74 * g + 112 * b) >> 8) + 128
     73#define CALC_V(r, g, b) \
     74    ((112 * r + -94 * g + -18 * b) >> 8) + 128
     75
     76    for (size_t line = 0; line < aSrcHeight; ++line)
     77    {
     78        if ((line % 2) == 0)
     79        {
     80            for (size_t x = 0; x < aSrcWidth; x += 2)
     81            {
     82                uint8_t b = aSrcBuf[4 * i];
     83                uint8_t g = aSrcBuf[4 * i + 1];
     84                uint8_t r = aSrcBuf[4 * i + 2];
     85
     86                aDstBuf[i++] = CALC_Y(r, g, b);
     87
     88                aDstBuf[upos++] = CALC_U(r, g, b);
     89                aDstBuf[vpos++] = CALC_V(r, g, b);
     90
     91                b = aSrcBuf[4 * i];
     92                g = aSrcBuf[4 * i + 1];
     93                r = aSrcBuf[4 * i + 2];
     94
     95                aDstBuf[i++] = CALC_Y(r, g, b);
     96            }
     97        }
     98        else
     99        {
     100            for (size_t x = 0; x < aSrcWidth; x++)
     101            {
     102                uint8_t b = aSrcBuf[4 * i];
     103                uint8_t g = aSrcBuf[4 * i + 1];
     104                uint8_t r = aSrcBuf[4 * i + 2];
     105
     106                aDstBuf[i++] = CALC_Y(r, g, b);
     107            }
     108        }
    114109    }
    115110
     
    152147
    153148/**
    154  * Converts a RGB to YUV buffer.
    155  *
    156  * @returns IPRT status code.
    157  * @param   enmPixelFormat      Pixel format to use for conversion.
     149 * Converts an entire RGB BGRA32 buffer to a YUV I420 buffer.
     150 *
    158151 * @param   paDst               Pointer to destination buffer.
    159  * @param   uDstWidth           Width (X, in pixels) of destination buffer.
    160  * @param   uDstHeight          Height (Y, in pixels) of destination buffer.
     152 * @param   uDstWidth           Width (X, in pixel) of destination buffer.
     153 * @param   uDstHeight          Height (Y, in pixel) of destination buffer.
    161154 * @param   paSrc               Pointer to source buffer.
    162  * @param   uSrcWidth           Width (X, in pixels) of source buffer.
    163  * @param   uSrcHeight          Height (Y, in pixels) of source buffer.
    164  */
    165 int RecordingUtilsRGBToYUV(RECORDINGPIXELFMT enmPixelFormat,
    166                            uint8_t *paDst, uint32_t uDstWidth, uint32_t uDstHeight,
    167                            uint8_t *paSrc, uint32_t uSrcWidth, uint32_t uSrcHeight)
    168 {
    169     switch (enmPixelFormat)
    170     {
    171         case RECORDINGPIXELFMT_RGB32:
    172             if (!recordingUtilsColorConvWriteYUV420p<ColorConvBGRA32Iter>(paDst, uDstWidth, uDstHeight,
    173                                                                           paSrc, uSrcWidth, uSrcHeight))
    174                 return VERR_INVALID_PARAMETER;
    175             break;
    176         case RECORDINGPIXELFMT_RGB24:
    177             if (!recordingUtilsColorConvWriteYUV420p<ColorConvBGR24Iter>(paDst, uDstWidth, uDstHeight,
    178                                                                          paSrc, uSrcWidth, uSrcHeight))
    179                 return VERR_INVALID_PARAMETER;
    180             break;
    181         case RECORDINGPIXELFMT_RGB565:
    182             if (!recordingUtilsColorConvWriteYUV420p<ColorConvBGR565Iter>(paDst, uDstWidth, uDstHeight,
    183                                                                           paSrc, uSrcWidth, uSrcHeight))
    184                 return VERR_INVALID_PARAMETER;
    185             break;
    186         default:
    187             AssertFailed();
    188             return VERR_NOT_SUPPORTED;
    189     }
    190     return VINF_SUCCESS;
     155 * @param   uSrcWidth           Width (X, in pixel) of source buffer.
     156 * @param   uSrcHeight          Height (Y, in pixel) of source buffer.
     157 */
     158void RecordingUtilsConvBGRA32ToYUVI420(uint8_t *paDst, uint32_t uDstWidth, uint32_t uDstHeight,
     159                                       uint8_t *paSrc, uint32_t uSrcWidth, uint32_t uSrcHeight)
     160{
     161    RT_NOREF(uDstWidth, uDstHeight);
     162
     163    size_t const image_size = uSrcWidth * uSrcHeight;
     164    size_t upos = image_size;
     165    size_t vpos = upos + upos / 4;
     166    size_t i = 0;
     167
     168#define CALC_Y(r, g, b) \
     169    ((66 * r + 129 * g + 25 * b) >> 8) + 16
     170#define CALC_U(r, g, b) \
     171    ((-38 * r + -74 * g + 112 * b) >> 8) + 128
     172#define CALC_V(r, g, b) \
     173    ((112 * r + -94 * g + -18 * b) >> 8) + 128
     174
     175    for (size_t y = 0; y < uSrcHeight; y++)
     176    {
     177        if ((y % 2) == 0)
     178        {
     179            for (size_t x = 0; x < uSrcWidth; x += 2)
     180            {
     181                uint8_t b = paSrc[4 * i];
     182                uint8_t g = paSrc[4 * i + 1];
     183                uint8_t r = paSrc[4 * i + 2];
     184
     185                paDst[i++] = CALC_Y(r, g, b);
     186
     187                paDst[upos++] = CALC_U(r, g, b);
     188                paDst[vpos++] = CALC_V(r, g, b);
     189
     190                b = paSrc[4 * i];
     191                g = paSrc[4 * i + 1];
     192                r = paSrc[4 * i + 2];
     193
     194                paDst[i++] = CALC_Y(r, g, b);
     195            }
     196        }
     197        else
     198        {
     199            for (size_t x = 0; x < uSrcWidth; x++)
     200            {
     201                uint8_t const b = paSrc[4 * i];
     202                uint8_t const g = paSrc[4 * i + 1];
     203                uint8_t const r = paSrc[4 * i + 2];
     204
     205                paDst[i++] = CALC_Y(r, g, b);
     206            }
     207        }
     208    }
     209
     210#undef CALC_Y
     211#undef CALC_U
     212#undef CALC_V
     213}
     214
     215/**
     216 * Converts a part of a RGB BGRA32 buffer to a YUV I420 buffer.
     217 *
     218 * @param   paDst               Pointer to destination buffer.
     219 * @param   uDstX               X destination position (in pixel).
     220 * @param   uDstY               Y destination position (in pixel).
     221 * @param   uDstWidth           Width (X, in pixel) of destination buffer.
     222 * @param   uDstHeight          Height (Y, in pixel) of destination buffer.
     223 * @param   paSrc               Pointer to source buffer.
     224 * @param   uSrcX               X source position (in pixel).
     225 * @param   uSrcY               Y source position (in pixel).
     226 * @param   uSrcWidth           Width (X, in pixel) of source buffer.
     227 * @param   uSrcHeight          Height (Y, in pixel) of source buffer.
     228 * @param   uSrcStride          Stride (in bytes) of source buffer.
     229 * @param   uSrcBPP             Bits per pixel of source buffer.
     230 */
     231void RecordingUtilsConvBGRA32ToYUVI420Ex(uint8_t *paDst, uint32_t uDstX, uint32_t uDstY, uint32_t uDstWidth, uint32_t uDstHeight,
     232                                         uint8_t *paSrc, uint32_t uSrcX, uint32_t uSrcY, uint32_t uSrcWidth, uint32_t uSrcHeight,
     233                                         uint32_t uSrcStride, uint8_t uSrcBPP)
     234{
     235    Assert(uDstX < uDstWidth);
     236    Assert(uDstX + uSrcWidth <= uDstWidth);
     237    Assert(uDstY < uDstHeight);
     238    Assert(uDstY + uSrcHeight <= uDstHeight);
     239    Assert(uSrcBPP % 8 == 0);
     240
     241    RT_NOREF(uSrcHeight, uDstHeight);
     242
     243#define CALC_Y(r, g, b) \
     244    (66 * r + 129 * g + 25 * b) >> 8
     245#define CALC_U(r, g, b) \
     246    ((-38 * r + -74 * g + 112 * b) >> 8) + 128
     247#define CALC_V(r, g, b) \
     248    ((112 * r + -94 * g + -18 * b) >> 8) + 128
     249
     250    uint32_t uDstXCur = uDstX;
     251
     252    const unsigned uSrcBytesPerPixel = uSrcBPP / 8;
     253
     254    size_t offSrc = (uSrcY * uSrcStride) + (uSrcX * uSrcBytesPerPixel);
     255
     256    for (size_t y = 0; y < uSrcHeight; y++)
     257    {
     258        for (size_t x = 0; x < uSrcWidth; x++)
     259        {
     260            size_t const offBGR = offSrc + (x * uSrcBytesPerPixel);
     261
     262            uint8_t const b = paSrc[offBGR];
     263            uint8_t const g = paSrc[offBGR + 1];
     264            uint8_t const r = paSrc[offBGR + 2];
     265
     266            size_t const offY  = uDstY * uDstWidth + uDstXCur;
     267            size_t const offUV = (uDstY / 2) * (uDstWidth / 2) + (uDstXCur / 2) + uDstWidth * uDstHeight;
     268
     269            paDst[offY]                               = CALC_Y(r, g, b);
     270            paDst[offUV]                              = CALC_U(r, g, b);
     271            paDst[offUV + uDstWidth * uDstHeight / 4] = CALC_V(r, g, b);
     272
     273            uDstXCur++;
     274        }
     275
     276        offSrc += uSrcStride;
     277
     278        uDstXCur = uDstX;
     279        uDstY++;
     280    }
     281
     282#undef CALC_Y
     283#undef CALC_U
     284#undef CALC_V
     285}
     286
     287/**
     288 * Crops (centers) or centers coordinates of a given source.
     289 *
     290 * @returns VBox status code.
     291 * @retval  VWRN_RECORDING_ENCODING_SKIPPED if the source is not visible.
     292 * @param   pCodecParms         Video codec parameters to use for cropping / centering.
     293 * @param   sx                  Input / output: X location (in pixel) of the source.
     294 * @param   sy                  Input / output: Y location (in pixel) of the source.
     295 * @param   sw                  Input / output: Width (in pixel) of the source.
     296 * @param   sh                  Input / output: Height (in pixel) of the source.
     297 * @param   dx                  Input / output: X location (in pixel) of the destination.
     298 * @param   dy                  Input / output: Y location (in pixel) of the destination.
     299 *
     300 * @note    Used when no other scaling algorithm is being selected / available. See testcase.
     301 */
     302int RecordingUtilsCoordsCropCenter(PRECORDINGCODECPARMS pCodecParms,
     303                                   int32_t *sx, int32_t *sy, int32_t *sw, int32_t *sh, int32_t *dx, int32_t *dy)
     304{
     305    int vrc = VINF_SUCCESS;
     306
     307    Log3Func(("Original: sx=%RI32 sy=%RI32 sw=%RI32 sh=%RI32 dx=%RI32 dy=%RI32\n",
     308              *sx, *sy, *sw, *sh, *dx, *dy));
     309
     310    /*
     311     * Do centered cropping or centering.
     312     */
     313
     314    int32_t const uOriginX = (int32_t)pCodecParms->u.Video.Scaling.u.Crop.m_iOriginX;
     315    int32_t const uOriginY = (int32_t)pCodecParms->u.Video.Scaling.u.Crop.m_iOriginY;
     316
     317    /* Apply cropping / centering values. */
     318    *dx += uOriginX;
     319    *dy += uOriginY;
     320
     321    if (*dx < 0)
     322    {
     323        *dx  = 0;
     324        *sx += RT_ABS(uOriginX);
     325        *sw -= RT_ABS(uOriginX);
     326    }
     327
     328    if (*dy < 0)
     329    {
     330        *dy  = 0;
     331        *sy += RT_ABS(uOriginY);
     332        *sh -= RT_ABS(uOriginY);
     333    }
     334
     335    Log3Func(("Crop0: sx=%RI32 sy=%RI32 sw=%RI32 sh=%RI32 dx=%RI32 dy=%RI32\n",
     336              *sx, *sy, *sw, *sh, *dx, *dy));
     337
     338    if (*sw > (int32_t)pCodecParms->u.Video.uWidth)
     339        *sw = (int32_t)pCodecParms->u.Video.uWidth;
     340
     341    if (*sh > (int32_t)pCodecParms->u.Video.uHeight)
     342        *sh = pCodecParms->u.Video.uHeight;
     343
     344    if (*dx + *sw >= (int32_t)pCodecParms->u.Video.uWidth)
     345        *sw = pCodecParms->u.Video.uWidth - *dx;
     346
     347    if (*dy + *sh >= (int32_t)pCodecParms->u.Video.uHeight)
     348        *sh = pCodecParms->u.Video.uHeight - *dy;
     349
     350    if (   *dx + *sw < 1
     351        || *dy + *sh < 1
     352        || *dx > (int32_t)pCodecParms->u.Video.uWidth
     353        || *dy > (int32_t)pCodecParms->u.Video.uHeight
     354        || *sw < 1
     355        || *sh < 1)
     356    {
     357        vrc = VWRN_RECORDING_ENCODING_SKIPPED; /* Not visible, skip encoding. */
     358    }
     359
     360    Log3Func(("Crop1: sx=%RI32 sy=%RI32 sw=%RI32 sh=%RI32 dx=%RI32 dy=%RI32 -> vrc=%Rrc\n",
     361              *sx, *sy, *sw, *sh, *dx, *dy, vrc));
     362
     363    return vrc;
    191364}
    192365
    193366#ifdef DEBUG
    194367/**
    195  * Dumps a video recording frame to a bitmap (BMP) file, extended version.
     368 * Dumps image data to a bitmap (BMP) file.
    196369 *
    197370 * @returns VBox status code.
    198  * @param   pu8RGBBuf           Pointer to actual RGB frame data.
     371 * @param   pu8RGBBuf           Pointer to actual RGB image data.
    199372 * @param   cbRGBBuf            Size (in bytes) of \a pu8RGBBuf.
    200373 * @param   pszPath             Absolute path to dump file to. Must exist.
    201  *                              Specify NULL to use the system's temp directory.
    202  *                              Existing frame files will be overwritten.
    203  * @param   pszPrefx            Naming prefix to use. Optional and can be NULL.
     374 *                              Specify NULL to use the system's temp directory as output directory.
     375 *                              Existing files will be overwritten.
     376 * @param   pszWhat             Hint of what to dump. Optional and can be NULL.
    204377 * @param   uWidth              Width (in pixel) to write.
    205378 * @param   uHeight             Height (in pixel) to write.
     379 * @param   uBytsPerLine        Bytes per line.
    206380 * @param   uBPP                Bits in pixel.
    207381 */
    208 int RecordingUtilsDbgDumpFrameEx(const uint8_t *pu8RGBBuf, size_t cbRGBBuf, const char *pszPath, const char *pszPrefx,
    209                                  uint16_t uWidth, uint32_t uHeight, uint8_t uBPP)
     382int RecordingUtilsDbgDumpImageData(const uint8_t *pu8RGBBuf, size_t cbRGBBuf, const char *pszPath, const char *pszWhat,
     383                                   uint32_t uWidth, uint32_t uHeight, uint32_t uBytesPerLine, uint8_t uBPP)
    210384{
    211385    RT_NOREF(cbRGBBuf);
     
    228402
    229403    coreHdr.cbSize         = sizeof(BMPWIN3XINFOHDR);
    230     coreHdr.uWidth         = uWidth ;
     404    coreHdr.uWidth         = uWidth;
    231405    coreHdr.uHeight        = uHeight;
    232406    coreHdr.cPlanes        = 1;
     
    237411    static uint64_t s_iCount = 0;
    238412
     413    /* Hardcoded path for now. */
    239414    char szPath[RTPATH_MAX];
    240415    if (!pszPath)
    241         RTPathTemp(szPath, sizeof(szPath));
     416    {
     417        int rc2 = RTPathTemp(szPath, sizeof(szPath));
     418        if (RT_FAILURE(rc2))
     419            return rc2;
     420    }
    242421
    243422    char szFileName[RTPATH_MAX];
    244423    if (RTStrPrintf2(szFileName, sizeof(szFileName), "%s/RecDump-%04RU64-%s-w%RU16h%RU16.bmp",
    245                      pszPath ? pszPath : szPath, s_iCount, pszPrefx ? pszPrefx : "Frame", uWidth, uHeight) <= 0)
    246     {
     424                     pszPath ? pszPath : szPath, s_iCount, pszWhat ? pszWhat : "Frame", uWidth, uHeight) <= 0)
    247425        return VERR_BUFFER_OVERFLOW;
    248     }
    249426
    250427    s_iCount++;
     
    259436
    260437        /* Bitmaps (DIBs) are stored upside-down (thanks, OS/2), so work from the bottom up. */
    261         uint32_t offSrc = (uHeight * uWidth * uBytesPerPixel) - uWidth * uBytesPerPixel;
     438        uint32_t offSrc = cbRGBBuf - uBytesPerLine;
    262439
    263440        /* Do the copy. */
    264441        for (unsigned int i = 0; i < uHeight; i++)
    265442        {
    266             RTFileWrite(fh, pu8RGBBuf + offSrc, uWidth * uBytesPerPixel, NULL);
    267             offSrc -= uWidth * uBytesPerPixel;
     443            RTFileWrite(fh, pu8RGBBuf + offSrc, uBytesPerLine, NULL);
     444            offSrc -= uBytesPerLine;
    268445        }
    269446
     
    275452
    276453/**
    277  * Dumps a video recording frame to a bitmap (BMP) file.
     454 * Dumps a video recording frame to a bitmap (BMP) file, extended version.
    278455 *
    279456 * @returns VBox status code.
    280457 * @param   pFrame              Video frame to dump.
    281  */
    282 int RecordingUtilsDbgDumpFrame(const PRECORDINGFRAME pFrame)
    283 {
    284     AssertReturn(pFrame->enmType == RECORDINGFRAME_TYPE_VIDEO, VERR_INVALID_PARAMETER);
    285     return RecordingUtilsDbgDumpFrameEx(pFrame->Video.pu8RGBBuf, pFrame->Video.cbRGBBuf,
    286                                         NULL /*  Use temp directory */, NULL /* pszPrefix */,
    287                                         pFrame->Video.uWidth, pFrame->Video.uHeight, pFrame->Video.uBPP);
     458 * @param   pszPath             Output directory.
     459 * @param   pszWhat             Hint of what to dump. Optional and can be NULL.
     460 */
     461int RecordingUtilsDbgDumpVideoFrameEx(const PRECORDINGVIDEOFRAME pFrame, const char *pszPath, const char *pszWhat)
     462{
     463    return RecordingUtilsDbgDumpImageData(pFrame->pau8Buf, pFrame->cbBuf,
     464                                          pszPath, pszWhat,
     465                                          pFrame->Info.uWidth, pFrame->Info.uHeight, pFrame->Info.uBytesPerLine, pFrame->Info.uBPP);
     466}
     467
     468/**
     469 * Dumps a video recording frame to a bitmap (BMP) file.
     470 *
     471 * @returns VBox status code.
     472 * @param   pFrame              Video frame to dump.
     473 * @param   pszWhat             Hint of what to dump. Optional and can be NULL.
     474 */
     475int RecordingUtilsDbgDumpVideoFrame(const PRECORDINGVIDEOFRAME pFrame, const char *pszWhat)
     476{
     477    return RecordingUtilsDbgDumpVideoFrameEx(pFrame, NULL /* Use temp directory */, pszWhat);
    288478}
    289479#endif /* DEBUG */
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