VirtualBox

Changeset 74904 in vbox


Ignore:
Timestamp:
Oct 18, 2018 8:03:38 AM (6 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
125898
Message:

VideoRec/Main: Implemented separate and common rendering queues per recording stream, reducing the data processing overhead significantly.

File:
1 edited

Legend:

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

    r73505 r74904  
    11/* $Id$ */
    22/** @file
    3  * Video capturing utility routines.
     3 * Video recording (with optional audio recording) code.
     4 *
     5 * This code employs a separate encoding thread per recording context
     6 * to keep time spent in EMT as short as possible. Each configured VM display
     7 * is represented by an own recording stream, which in turn has its own rendering
     8 * queue. Common recording data across all recording streams is kept in a
     9 * separate queue in the recording context to minimize data duplication and
     10 * multiplexing overhead in EMT.
    411 */
    512
    613/*
    7  * Copyright (C) 2012-2017 Oracle Corporation
     14 * Copyright (C) 2012-2018 Oracle Corporation
    815 *
    916 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    4653#endif /* VBOX_WITH_LIBVPX */
    4754
    48 struct VIDEORECVIDEOFRAME;
    49 typedef struct VIDEORECVIDEOFRAME *PVIDEORECVIDEOFRAME;
    50 
    51 static int videoRecEncodeAndWrite(PVIDEORECSTREAM pStream, PVIDEORECVIDEOFRAME pFrame);
    52 static int videoRecRGBToYUV(uint32_t uPixelFormat,
    53                             uint8_t *paDst, uint32_t uDstWidth, uint32_t uDstHeight,
    54                             uint8_t *paSrc, uint32_t uSrcWidth, uint32_t uSrcHeight);
    55 
    56 static int videoRecStreamCloseFile(PVIDEORECSTREAM pStream);
    57 static void videoRecStreamLock(PVIDEORECSTREAM pStream);
    58 static void videoRecStreamUnlock(PVIDEORECSTREAM pStream);
    59 
    6055using namespace com;
    6156
    62 #if 0
    63 /** Enables support for encoding multiple audio / video data frames at once. */
    64 #define VBOX_VIDEOREC_WITH_QUEUE
    65 #endif
    6657#ifdef DEBUG_andy
    6758/** Enables dumping audio / video data for debugging reasons. */
     
    131122    /** Pixel format of this frame. */
    132123    uint32_t            uPixelFormat;
    133     /** Time stamp (in ms). */
    134     uint64_t            uTimeStampMs;
    135124    /** RGB buffer containing the unmodified frame buffer data from Main's display. */
    136125    uint8_t            *pu8RGBBuf;
     
    145134typedef struct VIDEORECAUDIOFRAME
    146135{
    147     uint8_t             abBuf[_64K]; /** @todo Fix! */
     136    /** Pointer to audio data. */
     137    uint8_t            *pvBuf;
     138    /** Size (in bytes) of audio data. */
    148139    size_t              cbBuf;
    149     /** Absolute time stamp (in ms). */
    150     uint64_t            uTimeStampMs;
    151140} VIDEORECAUDIOFRAME, *PVIDEORECAUDIOFRAME;
    152141#endif
    153142
    154143/**
    155  * Strucutre for maintaining a video recording stream.
    156  */
    157 typedef struct VIDEORECSTREAM
     144 * Enumeration for specifying a video recording block type.
     145 */
     146typedef enum VIDEORECBLOCKTYPE
     147{
     148    /** Uknown block type, do not use. */
     149    VIDEORECBLOCKTYPE_UNKNOWN = 0,
     150    /** The block is a video frame. */
     151    VIDEORECBLOCKTYPE_VIDEO,
     152#ifdef VBOX_WITH_AUDIO_VIDEOREC
     153    /** The block is an audio frame. */
     154    VIDEORECBLOCKTYPE_AUDIO
     155#endif
     156} VIDEORECBLOCKTYPE;
     157
     158/**
     159 * Generic structure for keeping a single video recording (data) block.
     160 */
     161typedef struct VIDEORECBLOCK
     162{
     163    /** The block's type. */
     164    VIDEORECBLOCKTYPE  enmType;
     165    /** Number of references held of this block. */
     166    uint16_t           cRefs;
     167    /** The (absolute) time stamp (in ms, PTS) of this block. */
     168    uint64_t           uTimeStampMs;
     169    /** Opaque data block to the actual block data, depending on the block's type. */
     170    void              *pvData;
     171    /** Size (in bytes) of the (opaque) data block. */
     172    size_t             cbData;
     173} VIDEORECBLOCK, *PVIDEORECBLOCK;
     174
     175/** List for keeping video recording (data) blocks. */
     176typedef std::list<PVIDEORECBLOCK> VideoRecBlockList;
     177
     178static void videoRecBlockFree(PVIDEORECBLOCK pBlock);
     179
     180/** Structure for queuing all blocks bound to a single timecode.
     181 *  This can happen if multiple tracks are being involved. */
     182struct VideoRecBlocks
     183{
     184    virtual ~VideoRecBlocks()
     185    {
     186        Clear();
     187    }
     188
     189    /**
     190     * Resets a video recording block list by removing (destroying)
     191     * all current elements.
     192     */
     193    void Clear()
     194    {
     195        while (!List.empty())
     196        {
     197            PVIDEORECBLOCK pBlock = List.front();
     198            videoRecBlockFree(pBlock);
     199            List.pop_front();
     200        }
     201
     202        Assert(List.size() == 0);
     203    }
     204
     205    /** The actual block list for this timecode. */
     206    VideoRecBlockList List;
     207};
     208
     209/** A block map containing all currently queued blocks.
     210 *  The key specifies a unique timecode, whereas the value
     211 *  is a list of blocks which all correlate to the same key (timecode). */
     212typedef std::map<uint64_t, VideoRecBlocks *> VideoRecBlockMap;
     213
     214/**
     215 * Structure for holding a set of video recording (data) blocks.
     216 */
     217struct VideoRecBlockSet
     218{
     219    virtual ~VideoRecBlockSet()
     220    {
     221        Clear();
     222    }
     223
     224    /**
     225     * Resets a video recording block set by removing (destroying)
     226     * all current elements.
     227     */
     228    void Clear()
     229    {
     230        VideoRecBlockMap::iterator it = Map.begin();
     231        while (it != Map.end())
     232        {
     233            it->second->Clear();
     234            delete it->second;
     235            it = Map.erase(it);
     236        }
     237
     238        Assert(Map.size() == 0);
     239    }
     240
     241    /** Timestamp (in ms) when this set was last processed. */
     242    uint64_t         tsLastProcessedMs;
     243    /** All blocks related to this block set. */
     244    VideoRecBlockMap Map;
     245};
     246
     247/**
     248 * Structure for maintaining a video recording stream.
     249 */
     250struct VIDEORECSTREAM
    158251{
    159252    /** Video recording context this stream is associated to. */
     
    200293        /** Pointer to the codec's internal YUV buffer. */
    201294        uint8_t            *pu8YuvBuf;
    202 #ifdef VBOX_VIDEOREC_WITH_QUEUE
    203 # error "Implement me!"
    204 #else
    205         VIDEORECVIDEOFRAME  Frame;
    206         bool                fHasVideoData;
    207 #endif
    208295        /** Number of failed attempts to encode the current video frame in a row. */
    209296        uint16_t            cFailedEncodingFrames;
    210297    } Video;
    211 } VIDEORECSTREAM, *PVIDEORECSTREAM;
     298
     299    /** Common set of video recording (data) blocks, needed for
     300     *  multiplexing to all recording streams. */
     301    VideoRecBlockSet Blocks;
     302};
    212303
    213304/** Vector of video recording streams. */
     
    217308 * Structure for keeping a video recording context.
    218309 */
    219 typedef struct VIDEORECCONTEXT
     310struct VIDEORECCONTEXT
    220311{
    221312    /** Used recording configuration. */
     
    233324    /** Worker thread. */
    234325    RTTHREAD            Thread;
    235     /** Vector of current recording stream contexts. */
     326    /** Vector of current recording streams.
     327     *  Per VM screen (display) one recording stream is being used. */
    236328    VideoRecStreams     vecStreams;
    237329    /** Timestamp (in ms) of when recording has been started. */
    238330    uint64_t            tsStartMs;
    239 #ifdef VBOX_WITH_AUDIO_VIDEOREC
    240     struct
    241     {
    242         bool                fHasAudioData;
    243         VIDEORECAUDIOFRAME  Frame;
    244     } Audio;
    245 #endif
    246 } VIDEORECCONTEXT, *PVIDEORECCONTEXT;
     331    /** Block map of common blocks which need to get multiplexed
     332     *  to all recording streams. This common block maps should help
     333     *  reducing the time spent in EMT and avoid doing the (expensive)
     334     *  multiplexing work in there.
     335     *
     336     *  For now this only affects audio, e.g. all recording streams
     337     *  need to have the same audio data at a specific point in time. */
     338    VideoRecBlockMap    mapBlocksCommon;
     339};
    247340
    248341#ifdef VBOX_VIDEOREC_DUMP
     
    440533};
    441534
     535#ifdef VBOX_WITH_AUDIO_VIDEOREC
     536static void videoRecAudioFrameFree(PVIDEORECAUDIOFRAME pFrame);
     537#endif
     538static int videoRecEncodeAndWrite(PVIDEORECSTREAM pStream, uint64_t uTimeStampMs, PVIDEORECVIDEOFRAME pFrame);
     539static int videoRecRGBToYUV(uint32_t uPixelFormat,
     540                            uint8_t *paDst, uint32_t uDstWidth, uint32_t uDstHeight,
     541                            uint8_t *paSrc, uint32_t uSrcWidth, uint32_t uSrcHeight);
     542static int videoRecStreamCloseFile(PVIDEORECSTREAM pStream);
     543static void videoRecStreamLock(PVIDEORECSTREAM pStream);
     544static void videoRecStreamUnlock(PVIDEORECSTREAM pStream);
     545static void videoRecVideoFrameFree(PVIDEORECVIDEOFRAME pFrame);
     546
    442547/**
    443548 * Convert an image to YUV420p format.
     
    547652}
    548653
     654#ifdef VBOX_WITH_AUDIO_VIDEOREC
     655/**
     656 * Frees a previously allocated video recording audio frame.
     657 *
     658 * @param   pFrame              Audio frame to free. The pointer will be invalid after return.
     659 */
     660static void videoRecAudioFrameFree(PVIDEORECAUDIOFRAME pFrame)
     661{
     662    if (!pFrame)
     663        return;
     664
     665    if (pFrame->pvBuf)
     666    {
     667        Assert(pFrame->cbBuf);
     668        RTMemFree(pFrame->pvBuf);
     669    }
     670    RTMemFree(pFrame);
     671    pFrame = NULL;
     672}
     673#endif
     674
     675/**
     676 * Frees a video recording (data) block.
     677 *
     678 * @returns IPRT status code.
     679 * @param   pBlock              Video recording (data) block to free. The pointer will be invalid after return.
     680 */
     681static void videoRecBlockFree(PVIDEORECBLOCK pBlock)
     682{
     683    if (!pBlock)
     684        return;
     685
     686    switch (pBlock->enmType)
     687    {
     688        case VIDEORECBLOCKTYPE_VIDEO:
     689            videoRecVideoFrameFree((PVIDEORECVIDEOFRAME)pBlock->pvData);
     690            break;
     691
     692#ifdef VBOX_WITH_AUDIO_VIDEOREC
     693        case VIDEORECBLOCKTYPE_AUDIO:
     694            videoRecAudioFrameFree((PVIDEORECAUDIOFRAME)pBlock->pvData);
     695            break;
     696#endif
     697        default:
     698            AssertFailed();
     699            break;
     700    }
     701
     702    RTMemFree(pBlock);
     703    pBlock = NULL;
     704}
     705
    549706/**
    550707 * Worker thread for all streams of a video recording context.
    551708 *
    552  * Does RGB/YUV conversion and encoding.
     709 * For video frames, this also does the RGB/YUV conversion and encoding.
    553710 */
    554711static DECLCALLBACK(int) videoRecThread(RTTHREAD hThreadSelf, void *pvUser)
     
    559716    RTThreadUserSignal(hThreadSelf);
    560717
     718    LogFunc(("Thread started\n"));
     719
    561720    for (;;)
    562721    {
    563722        int rc = RTSemEventWait(pCtx->WaitEvent, RT_INDEFINITE_WAIT);
    564723        AssertRCBreak(rc);
    565 
    566         if (ASMAtomicReadBool(&pCtx->fShutdown))
    567             break;
    568 
    569 #ifdef VBOX_WITH_AUDIO_VIDEOREC
    570         VIDEORECAUDIOFRAME audioFrame;
    571         RT_ZERO(audioFrame);
    572 
    573         int rc2 = RTCritSectEnter(&pCtx->CritSect);
    574         AssertRC(rc2);
    575 
    576         const bool fEncodeAudio = pCtx->Audio.fHasAudioData;
    577         if (fEncodeAudio)
    578         {
    579             /*
    580              * Every recording stream needs to get the same audio data at a certain point in time.
    581              * Do the multiplexing here to not block EMT for too long.
    582              *
    583              * For now just doing a simple copy of the current audio frame should be good enough.
    584              */
    585             memcpy(&audioFrame, &pCtx->Audio.Frame, sizeof(VIDEORECAUDIOFRAME));
    586 
    587             pCtx->Audio.fHasAudioData = false;
    588         }
    589 
    590         rc2 = RTCritSectLeave(&pCtx->CritSect);
    591         AssertRC(rc2);
    592 #endif
    593724
    594725        /** @todo r=andy This is inefficient -- as we already wake up this thread
    595726         *               for every screen from Main, we here go again (on every wake up) through
    596727         *               all screens.  */
    597         for (VideoRecStreams::iterator it = pCtx->vecStreams.begin(); it != pCtx->vecStreams.end(); it++)
    598         {
    599             PVIDEORECSTREAM pStream = (*it);
     728        for (VideoRecStreams::iterator itStream = pCtx->vecStreams.begin(); itStream != pCtx->vecStreams.end(); itStream++)
     729        {
     730            PVIDEORECSTREAM pStream = (*itStream);
    600731
    601732            videoRecStreamLock(pStream);
     
    607738            }
    608739
    609             PVIDEORECVIDEOFRAME pVideoFrame = &pStream->Video.Frame;
    610             const bool fEncodeVideo = pStream->Video.fHasVideoData;
    611 
    612             if (fEncodeVideo)
     740            VideoRecBlockMap::iterator itBlockStream = pStream->Blocks.Map.begin();
     741            while (itBlockStream != pStream->Blocks.Map.end())
    613742            {
    614                 rc = videoRecRGBToYUV(pVideoFrame->uPixelFormat,
    615                                       /* Destination */
    616                                       pStream->Video.pu8YuvBuf, pVideoFrame->uWidth, pVideoFrame->uHeight,
    617                                       /* Source */
    618                                       pVideoFrame->pu8RGBBuf, pStream->Video.uWidth, pStream->Video.uHeight);
    619                 if (RT_SUCCESS(rc))
    620                     rc = videoRecEncodeAndWrite(pStream, pVideoFrame);
    621 
    622                 pStream->Video.fHasVideoData = false;
     743                const uint64_t        uTimeStampMs = itBlockStream->first;
     744                      VideoRecBlocks *pBlocks      = itBlockStream->second;
     745
     746                AssertPtr(pBlocks);
     747
     748                while (!pBlocks->List.empty())
     749                {
     750                    PVIDEORECBLOCK pBlock = pBlocks->List.front();
     751                    AssertPtr(pBlock);
     752
     753                    if (pBlock->enmType == VIDEORECBLOCKTYPE_VIDEO)
     754                    {
     755                        PVIDEORECVIDEOFRAME pVideoFrame  = (PVIDEORECVIDEOFRAME)pBlock->pvData;
     756
     757                        rc = videoRecRGBToYUV(pVideoFrame->uPixelFormat,
     758                                              /* Destination */
     759                                              pStream->Video.pu8YuvBuf, pVideoFrame->uWidth, pVideoFrame->uHeight,
     760                                              /* Source */
     761                                              pVideoFrame->pu8RGBBuf, pStream->Video.uWidth, pStream->Video.uHeight);
     762                        if (RT_SUCCESS(rc))
     763                            rc = videoRecEncodeAndWrite(pStream, uTimeStampMs, pVideoFrame);
     764                    }
     765
     766                    videoRecBlockFree(pBlock);
     767                    pBlock = NULL;
     768
     769                    pBlocks->List.pop_front();
     770                }
     771
     772#ifdef VBOX_WITH_AUDIO_VIDEOREC
     773                /* As each (enabled) screen has to get the same audio data, look for common (audio) data which needs to be
     774                 * written to the screen's assigned recording stream. */
     775                VideoRecBlockMap::iterator itCommon = pCtx->mapBlocksCommon.begin();
     776                while (itCommon != pCtx->mapBlocksCommon.end())
     777                {
     778                    VideoRecBlockList::iterator itBlockCommon = itCommon->second->List.begin();
     779                    while (itBlockCommon != itCommon->second->List.end())
     780                    {
     781                        PVIDEORECBLOCK pBlockCommon = (PVIDEORECBLOCK)(*itBlockCommon);
     782                        switch (pBlockCommon->enmType)
     783                        {
     784                            case VIDEORECBLOCKTYPE_AUDIO:
     785                            {
     786                                PVIDEORECAUDIOFRAME pAudioFrame = (PVIDEORECAUDIOFRAME)pBlockCommon->pvData;
     787                                AssertPtr(pAudioFrame);
     788                                AssertPtr(pAudioFrame->pvBuf);
     789                                Assert(pAudioFrame->cbBuf);
     790
     791                                WebMWriter::BlockData_Opus blockData = { pAudioFrame->pvBuf, pAudioFrame->cbBuf,
     792                                                                         pBlockCommon->uTimeStampMs };
     793                                rc = pStream->File.pWEBM->WriteBlock(pStream->uTrackAudio, &blockData, sizeof(blockData));
     794                                break;
     795                            }
     796
     797                            default:
     798                                AssertFailed();
     799                                break;
     800                        }
     801
     802                        Assert(pBlockCommon->cRefs);
     803                        if (--pBlockCommon->cRefs == 0)
     804                        {
     805                            videoRecBlockFree(pBlockCommon);
     806                            itBlockCommon = itCommon->second->List.erase(itBlockCommon);
     807                        }
     808                        else
     809                            ++itBlockCommon;
     810                    }
     811
     812                    /* If no entries are left over in the block map, remove it altogether. */
     813                    if (itCommon->second->List.empty())
     814                    {
     815                        delete itCommon->second;
     816                        pCtx->mapBlocksCommon.erase(itCommon);
     817                    }
     818
     819                    itCommon = pCtx->mapBlocksCommon.begin();
     820
     821                    LogFunc(("Common blocks: %zu\n", pCtx->mapBlocksCommon.size()));
     822                }
     823#endif
     824                ++itBlockStream;
    623825            }
    624826
    625827            videoRecStreamUnlock(pStream);
    626 
    627             if (RT_FAILURE(rc))
    628             {
    629                 static unsigned s_cErrEncVideo = 0;
    630                 if (s_cErrEncVideo < 32)
    631                 {
    632                     LogRel(("VideoRec: Error %Rrc encoding / writing video frame\n", rc));
    633                     s_cErrEncVideo++;
    634                 }
    635             }
    636 
    637 #ifdef VBOX_WITH_AUDIO_VIDEOREC
    638             /* Each (enabled) screen has to get the same audio data. */
    639             if (fEncodeAudio)
    640             {
    641                 Assert(audioFrame.cbBuf);
    642                 Assert(audioFrame.cbBuf <= _64K); /** @todo Fix. */
    643 
    644                 WebMWriter::BlockData_Opus blockData = { audioFrame.abBuf, audioFrame.cbBuf, audioFrame.uTimeStampMs };
    645                 rc = pStream->File.pWEBM->WriteBlock(pStream->uTrackAudio, &blockData, sizeof(blockData));
    646                 if (RT_FAILURE(rc))
    647                 {
    648                     static unsigned s_cErrEncAudio = 0;
    649                     if (s_cErrEncAudio < 32)
    650                     {
    651                         LogRel(("VideoRec: Error %Rrc encoding audio frame\n", rc));
    652                         s_cErrEncAudio++;
    653                     }
    654                 }
    655             }
    656 #endif
    657828        }
    658829
    659830        /* Keep going in case of errors. */
    660831
     832        if (ASMAtomicReadBool(&pCtx->fShutdown))
     833        {
     834            LogFunc(("Thread is shutting down ...\n"));
     835            break;
     836        }
     837
    661838    } /* for */
    662839
     840    LogFunc(("Thread ended\n"));
    663841    return VINF_SUCCESS;
     842}
     843
     844/**
     845 * Notifies a recording context's encoding thread.
     846 *
     847 * @returns IPRT status code.
     848 * @param   pCtx                Video recording context to notify thread for.
     849 */
     850static int videoRecThreadNotify(PVIDEORECCONTEXT pCtx)
     851{
     852    AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
     853
     854    return RTSemEventSignal(pCtx->WaitEvent);
    664855}
    665856
     
    678869    AssertPtrReturn(ppCtx,        VERR_INVALID_POINTER);
    679870
    680     PVIDEORECCONTEXT pCtx = (PVIDEORECCONTEXT)RTMemAllocZ(sizeof(VIDEORECCONTEXT));
    681     if (!pCtx)
     871    VIDEORECCONTEXT *pCtx = NULL;
     872    try
     873    {
     874        pCtx = new VIDEORECCONTEXT();
     875    }
     876    catch (std::bad_alloc &)
     877    {
    682878        return VERR_NO_MEMORY;
     879    }
    683880
    684881    int rc = RTCritSectInit(&pCtx->CritSect);
    685882    if (RT_FAILURE(rc))
    686883    {
    687         RTMemFree(pCtx);
     884        delete pCtx;
    688885        return rc;
    689886    }
     
    691888    for (uint32_t uScreen = 0; uScreen < cScreens; uScreen++)
    692889    {
    693         PVIDEORECSTREAM pStream = (PVIDEORECSTREAM)RTMemAllocZ(sizeof(VIDEORECSTREAM));
    694         if (!pStream)
     890        VIDEORECSTREAM *pStream = NULL;
     891        try
     892        {
     893            pStream = new VIDEORECSTREAM();
     894        }
     895        catch (std::bad_alloc &)
    695896        {
    696897            rc = VERR_NO_MEMORY;
     
    765966        return VINF_SUCCESS;
    766967
     968    int rc = VINF_SUCCESS;
     969
    767970    if (pCtx->enmState == VIDEORECSTS_INITIALIZED)
    768971    {
     972        LogFunc(("Shutting down thread ...\n"));
     973
    769974        /* Set shutdown indicator. */
    770975        ASMAtomicWriteBool(&pCtx->fShutdown, true);
    771976
    772         /* Signal the thread. */
    773         RTSemEventSignal(pCtx->WaitEvent);
    774 
    775         int rc = RTThreadWait(pCtx->Thread, 10 * 1000 /* 10s timeout */, NULL);
    776         if (RT_FAILURE(rc))
    777             return rc;
    778 
    779         /* Disable the context. */
    780         ASMAtomicWriteBool(&pCtx->fStarted, false);
    781 
    782         rc = RTSemEventDestroy(pCtx->WaitEvent);
     977        /* Signal the thread and wait for it to shut down. */
     978        rc = videoRecThreadNotify(pCtx);
     979        if (RT_SUCCESS(rc))
     980            rc = RTThreadWait(pCtx->Thread, 10 * 1000 /* 10s timeout */, NULL);
     981
     982        if (RT_SUCCESS(rc))
     983        {
     984            /* Disable the context. */
     985            ASMAtomicWriteBool(&pCtx->fStarted, false);
     986
     987            int rc2 = RTSemEventDestroy(pCtx->WaitEvent);
     988            AssertRC(rc2);
     989
     990            pCtx->WaitEvent = NIL_RTSEMEVENT;
     991        }
     992    }
     993
     994    if (RT_FAILURE(rc))
     995    {
    783996        AssertRC(rc);
    784 
    785         pCtx->WaitEvent = NIL_RTSEMEVENT;
    786     }
    787 
    788     int rc = RTCritSectEnter(&pCtx->CritSect);
     997        return rc;
     998    }
     999
     1000    rc = RTCritSectEnter(&pCtx->CritSect);
    7891001    if (RT_SUCCESS(rc))
    7901002    {
     
    8161028                Assert(rcv == VPX_CODEC_OK); RT_NOREF(rcv);
    8171029
    818 #ifdef VBOX_VIDEOREC_WITH_QUEUE
    819 # error "Implement me!"
    820 #else
    821                 PVIDEORECVIDEOFRAME pFrame = &pStream->Video.Frame;
    822 #endif
    823                 if (pFrame->pu8RGBBuf)
    824                 {
    825                     Assert(pFrame->cbRGBBuf);
    826 
    827                     RTMemFree(pFrame->pu8RGBBuf);
    828                     pFrame->pu8RGBBuf = NULL;
    829                 }
    830 
    831                 pFrame->cbRGBBuf = 0;
     1030                pStream->Blocks.Clear();
    8321031
    8331032                LogRel(("VideoRec: Recording screen #%u stopped\n", pStream->uScreenID));
     
    8601059            RTCritSectDelete(&pStream->CritSect);
    8611060
    862             RTMemFree(pStream);
     1061            delete pStream;
    8631062            pStream = NULL;
    8641063        }
    8651064
     1065        /* Sanity. */
    8661066        Assert(pCtx->vecStreams.empty());
     1067        Assert(pCtx->mapBlocksCommon.size() == 0);
    8671068
    8681069        int rc2 = RTCritSectLeave(&pCtx->CritSect);
     
    8711072        RTCritSectDelete(&pCtx->CritSect);
    8721073
    873         RTMemFree(pCtx);
     1074        delete pCtx;
    8741075        pCtx = NULL;
    8751076    }
     
    10561257    AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
    10571258
     1259    PVIDEORECCFG pCfg = &pCtx->Cfg;
     1260
     1261#ifdef VBOX_WITH_AUDIO_VIDEOREC
     1262    if (pCfg->Audio.fEnabled)
     1263    {
     1264        /* Sanity. */
     1265        AssertReturn(pCfg->Audio.uHz,       VERR_INVALID_PARAMETER);
     1266        AssertReturn(pCfg->Audio.cBits,     VERR_INVALID_PARAMETER);
     1267        AssertReturn(pCfg->Audio.cChannels, VERR_INVALID_PARAMETER);
     1268    }
     1269#endif
     1270
    10581271    PVIDEORECSTREAM pStream = videoRecStreamGet(pCtx, uScreen);
    10591272    if (!pStream)
     
    10631276    if (RT_FAILURE(rc))
    10641277        return rc;
    1065 
    1066     PVIDEORECCFG pCfg = &pCtx->Cfg;
    10671278
    10681279    pStream->pCtx = pCtx;
     
    10721283    pStream->Video.uHeight               = pCfg->Video.uHeight;
    10731284    pStream->Video.cFailedEncodingFrames = 0;
    1074 
    1075 #ifndef VBOX_VIDEOREC_WITH_QUEUE
    1076     /* When not using a queue, we only use one frame per stream at once.
    1077      * So do the initialization here. */
    1078     PVIDEORECVIDEOFRAME pFrame = &pStream->Video.Frame;
    1079 
    1080     const size_t cbRGBBuf =   pStream->Video.uWidth
    1081                             * pStream->Video.uHeight
    1082                             * 4 /* 32 BPP maximum */;
    1083     AssertReturn(cbRGBBuf, VERR_INVALID_PARAMETER);
    1084 
    1085     pFrame->pu8RGBBuf = (uint8_t *)RTMemAllocZ(cbRGBBuf);
    1086     AssertReturn(pFrame->pu8RGBBuf, VERR_NO_MEMORY);
    1087     pFrame->cbRGBBuf  = cbRGBBuf;
    1088 #endif
    10891285
    10901286    PVIDEORECVIDEOCODEC pVC = &pStream->Video.Codec;
     
    12671463    }
    12681464
    1269     PVIDEORECVIDEOFRAME pLastFrame = &pStream->Video.Frame;
    1270 
    1271     if (uTimeStampMs < pLastFrame->uTimeStampMs + pStream->Video.uDelayMs)
     1465    if (uTimeStampMs < pStream->Video.uLastTimeStampMs + pStream->Video.uDelayMs)
    12721466        return false;
    12731467
     
    13411535 * @returns IPRT status code.
    13421536 * @param   pStream             Stream to encode and submit to.
     1537 * @param   uTimeStampMs        Absolute timestamp (PTS) of frame (in ms) to encode.
    13431538 * @param   pFrame              Frame to encode and submit.
    13441539 */
    1345 static int videoRecEncodeAndWrite(PVIDEORECSTREAM pStream, PVIDEORECVIDEOFRAME pFrame)
     1540static int videoRecEncodeAndWrite(PVIDEORECSTREAM pStream, uint64_t uTimeStampMs, PVIDEORECVIDEOFRAME pFrame)
    13461541{
    13471542    AssertPtrReturn(pStream, VERR_INVALID_POINTER);
     
    13551550#ifdef VBOX_WITH_LIBVPX
    13561551    /* Presentation Time Stamp (PTS). */
    1357     vpx_codec_pts_t pts = pFrame->uTimeStampMs;
     1552    vpx_codec_pts_t pts = uTimeStampMs;
    13581553    vpx_codec_err_t rcv = vpx_codec_encode(&pVC->VPX.Ctx,
    13591554                                           &pVC->VPX.RawImage,
     
    14451640 * @param   pCtx                Pointer to the video recording context.
    14461641 * @param   pvData              Audio frame data to send.
    1447  * @param   cbData              Size (in bytes) of audio frame data.
     1642 * @param   cbData              Size (in bytes) of (encoded) audio frame data.
    14481643 * @param   uTimeStampMs        Time stamp (in ms) of audio playback.
    14491644 */
     
    14511646{
    14521647#ifdef VBOX_WITH_AUDIO_VIDEOREC
    1453     AssertReturn(cbData <= _64K, VERR_INVALID_PARAMETER);
    1454 
    1455     int rc = RTCritSectEnter(&pCtx->CritSect);
    1456     if (RT_FAILURE(rc))
    1457         return rc;
     1648    AssertPtrReturn(pvData, VERR_INVALID_POINTER);
     1649    AssertReturn(cbData, VERR_INVALID_PARAMETER);
    14581650
    14591651    /* To save time spent in EMT, do the required audio multiplexing in the encoding thread.
     
    14621654     * audio data at the same given point in time.
    14631655     */
    1464     PVIDEORECAUDIOFRAME pFrame = &pCtx->Audio.Frame;
    1465 
    1466     memcpy(pFrame->abBuf, pvData, RT_MIN(_64K /** @todo Fix! */, cbData));
    1467 
    1468     pFrame->cbBuf        = cbData;
    1469     pFrame->uTimeStampMs = uTimeStampMs;
    1470 
    1471     pCtx->Audio.fHasAudioData = true;
    1472 
    1473     rc = RTCritSectLeave(&pCtx->CritSect);
     1656    PVIDEORECBLOCK pBlock = (PVIDEORECBLOCK)RTMemAlloc(sizeof(VIDEORECBLOCK));
     1657    AssertPtrReturn(pBlock, VERR_NO_MEMORY);
     1658    pBlock->enmType = VIDEORECBLOCKTYPE_AUDIO;
     1659
     1660    PVIDEORECAUDIOFRAME pFrame = (PVIDEORECAUDIOFRAME)RTMemAlloc(sizeof(VIDEORECAUDIOFRAME));
     1661    AssertPtrReturn(pFrame, VERR_NO_MEMORY);
     1662
     1663    pFrame->pvBuf = (uint8_t *)RTMemAlloc(cbData);
     1664    AssertPtrReturn(pFrame->pvBuf, VERR_NO_MEMORY);
     1665    pFrame->cbBuf = cbData;
     1666
     1667    memcpy(pFrame->pvBuf, pvData, cbData);
     1668
     1669    pBlock->pvData       = pFrame;
     1670    pBlock->cbData       = sizeof(VIDEORECAUDIOFRAME) + cbData;
     1671    pBlock->cRefs        = (uint16_t)pCtx->vecStreams.size(); /* All streams need the same audio data. */
     1672    pBlock->uTimeStampMs = uTimeStampMs;
     1673
     1674    int rc = RTCritSectEnter(&pCtx->CritSect);
     1675    if (RT_FAILURE(rc))
     1676        return rc;
     1677
     1678    try
     1679    {
     1680        VideoRecBlockMap::iterator itBlocks = pCtx->mapBlocksCommon.find(uTimeStampMs);
     1681        if (itBlocks == pCtx->mapBlocksCommon.end())
     1682        {
     1683            VideoRecBlocks *pVideoRecBlocks = new VideoRecBlocks();
     1684            pVideoRecBlocks->List.push_back(pBlock);
     1685
     1686            pCtx->mapBlocksCommon.insert(std::make_pair(uTimeStampMs, pVideoRecBlocks));
     1687        }
     1688        else
     1689            itBlocks->second->List.push_back(pBlock);
     1690    }
     1691    catch (const std::exception &ex)
     1692    {
     1693        RT_NOREF(ex);
     1694        rc = VERR_NO_MEMORY;
     1695    }
     1696
     1697    int rc2 = RTCritSectLeave(&pCtx->CritSect);
     1698    AssertRC(rc2);
     1699
    14741700    if (RT_SUCCESS(rc))
    1475         rc = RTSemEventSignal(pCtx->WaitEvent);
     1701        rc = videoRecThreadNotify(pCtx);
    14761702
    14771703    return rc;
     
    15111737    AssertReturn(puSrcData,  VERR_INVALID_POINTER);
    15121738
     1739    int rc = RTCritSectEnter(&pCtx->CritSect);
     1740    AssertRC(rc);
     1741
    15131742    PVIDEORECSTREAM pStream = videoRecStreamGet(pCtx, uScreen);
    15141743    if (!pStream)
     1744    {
     1745        rc = RTCritSectLeave(&pCtx->CritSect);
     1746        AssertRC(rc);
     1747
    15151748        return VERR_NOT_FOUND;
     1749    }
    15161750
    15171751    videoRecStreamLock(pStream);
    15181752
    1519     int rc = VINF_SUCCESS;
     1753    PVIDEORECVIDEOFRAME pFrame = NULL;
    15201754
    15211755    do
     
    15841818            h = pStream->Video.uHeight - destY;
    15851819
    1586 #ifdef VBOX_VIDEOREC_WITH_QUEUE
    1587 # error "Implement me!"
    1588 #else
    1589         PVIDEORECVIDEOFRAME pFrame = &pStream->Video.Frame;
    1590 #endif
     1820        pFrame = (PVIDEORECVIDEOFRAME)RTMemAllocZ(sizeof(VIDEORECVIDEOFRAME));
     1821        AssertBreakStmt(pFrame, rc = VERR_NO_MEMORY);
     1822
    15911823        /* Calculate bytes per pixel and set pixel format. */
    15921824        const unsigned uBytesPerPixel = uBPP / 8;
     
    16121844            AssertMsgFailed(("Unknown pixel format (%RU32)\n", uPixelFormat));
    16131845
    1614 #ifndef VBOX_VIDEOREC_WITH_QUEUE
    1615         /* If we don't use a queue then we have to compare the dimensions
    1616          * of the current frame with the previous frame:
    1617          *
    1618          * If it's smaller than before then clear the entire buffer to prevent artifacts
    1619          * from the previous frame. */
    1620         if (   uSrcWidth  < pFrame->uWidth
    1621             || uSrcHeight < pFrame->uHeight)
    1622         {
    1623             /** @todo r=andy Only clear dirty areas. */
     1846        const size_t cbRGBBuf =   pStream->Video.uWidth
     1847                                * pStream->Video.uHeight
     1848                                * uBytesPerPixel;
     1849        AssertBreakStmt(cbRGBBuf, rc = VERR_INVALID_PARAMETER);
     1850
     1851        pFrame->pu8RGBBuf = (uint8_t *)RTMemAlloc(cbRGBBuf);
     1852        AssertBreakStmt(pFrame->pu8RGBBuf, rc = VERR_NO_MEMORY);
     1853        pFrame->cbRGBBuf  = cbRGBBuf;
     1854        pFrame->uWidth    = uSrcWidth;
     1855        pFrame->uHeight   = uSrcHeight;
     1856
     1857        /* If the current video frame is smaller than video resolution we're going to encode,
     1858         * clear the frame beforehand to prevent artifacts. */
     1859        if (   uSrcWidth  < pStream->Video.uWidth
     1860            || uSrcHeight < pStream->Video.uHeight)
     1861        {
    16241862            RT_BZERO(pFrame->pu8RGBBuf, pFrame->cbRGBBuf);
    16251863        }
    1626 #endif
     1864
    16271865        /* Calculate start offset in source and destination buffers. */
    16281866        uint32_t offSrc = y * uBytesPerLine + x * uBytesPerPixel;
     
    16801918            RTFileClose(fh);
    16811919#endif
    1682         pFrame->uTimeStampMs = uTimeStampMs;
    1683         pFrame->uWidth       = uSrcWidth;
    1684         pFrame->uHeight      = uSrcHeight;
    1685 
    1686         pStream->Video.fHasVideoData = true;
    16871920
    16881921    } while (0);
    16891922
     1923    if (rc == VINF_SUCCESS) /* Note: Also could be VINF_TRY_AGAIN. */
     1924    {
     1925        PVIDEORECBLOCK pBlock = (PVIDEORECBLOCK)RTMemAlloc(sizeof(VIDEORECBLOCK));
     1926        if (pBlock)
     1927        {
     1928            AssertPtr(pFrame);
     1929
     1930            pBlock->enmType = VIDEORECBLOCKTYPE_VIDEO;
     1931            pBlock->pvData  = pFrame;
     1932            pBlock->cbData  = sizeof(VIDEORECVIDEOFRAME) + pFrame->cbRGBBuf;
     1933
     1934            try
     1935            {
     1936                VideoRecBlocks *pVideoRecBlocks = new VideoRecBlocks();
     1937                pVideoRecBlocks->List.push_back(pBlock);
     1938
     1939                Assert(pStream->Blocks.Map.find(uTimeStampMs) == pStream->Blocks.Map.end());
     1940                pStream->Blocks.Map.insert(std::make_pair(uTimeStampMs, pVideoRecBlocks));
     1941            }
     1942            catch (const std::exception &ex)
     1943            {
     1944                RT_NOREF(ex);
     1945
     1946                RTMemFree(pBlock);
     1947                rc = VERR_NO_MEMORY;
     1948            }
     1949        }
     1950        else
     1951            rc = VERR_NO_MEMORY;
     1952    }
     1953
     1954    if (RT_FAILURE(rc))
     1955        videoRecVideoFrameFree(pFrame);
     1956
    16901957    videoRecStreamUnlock(pStream);
     1958
     1959    int rc2 = RTCritSectLeave(&pCtx->CritSect);
     1960    AssertRC(rc2);
    16911961
    16921962    if (   RT_SUCCESS(rc)
    16931963        && rc != VINF_TRY_AGAIN) /* Only signal the thread if operation was successful. */
    16941964    {
    1695         int rc2 = RTSemEventSignal(pCtx->WaitEvent);
    1696         AssertRC(rc2);
     1965        videoRecThreadNotify(pCtx);
    16971966    }
    16981967
    16991968    return rc;
    17001969}
     1970
     1971/**
     1972 * Frees a video recording video frame.
     1973 *
     1974 * @returns IPRT status code.
     1975 * @param   pFrame              Pointer to video frame to free. The pointer will be invalid after return.
     1976 */
     1977static void videoRecVideoFrameFree(PVIDEORECVIDEOFRAME pFrame)
     1978{
     1979    if (!pFrame)
     1980        return;
     1981
     1982    if (pFrame->pu8RGBBuf)
     1983    {
     1984        Assert(pFrame->cbRGBBuf);
     1985        RTMemFree(pFrame->pu8RGBBuf);
     1986    }
     1987    RTMemFree(pFrame);
     1988}
     1989
Note: See TracChangeset for help on using the changeset viewer.

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