VirtualBox

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


Ignore:
Timestamp:
Aug 16, 2022 3:41:39 PM (2 years ago)
Author:
vboxsync
Message:

Recording/Main: Decoupled the WebM writer class from codec dependencies. Various bugfixes. bugref:10275

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

Legend:

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

    r95645 r96229  
    3838    RecordingContext();
    3939
    40     RecordingContext(Console *ptrConsole, const settings::RecordingSettings &settings);
     40    RecordingContext(Console *ptrConsole, const settings::RecordingSettings &Settings);
    4141
    4242    virtual ~RecordingContext(void);
     
    4747    RecordingStream *GetStream(unsigned uScreen) const;
    4848    size_t GetStreamCount(void) const;
     49#ifdef VBOX_WITH_AUDIO_RECORDING
     50    PRECORDINGCODEC GetCodecAudio(void) { return &this->CodecAudio; }
     51#endif
    4952
    50     int Create(Console *pConsole, const settings::RecordingSettings &settings);
     53    int Create(Console *pConsole, const settings::RecordingSettings &Settings);
    5154    void Destroy(void);
    5255
     
    7275protected:
    7376
    74     int createInternal(Console *ptrConsole, const settings::RecordingSettings &settings);
     77    int createInternal(Console *ptrConsole, const settings::RecordingSettings &Settings);
    7578    int startInternal(void);
    7679    int stopInternal(void);
     
    8083    RecordingStream *getStreamInternal(unsigned uScreen) const;
    8184
     85    int writeCommonData(PRECORDINGCODEC pCodec, const void *pvData, size_t cbData, uint64_t msAbsPTS, uint32_t uFlags);
     86
    8287    int lock(void);
    8388    int unlock(void);
     
    8691
    8792    int threadNotify(void);
     93
     94protected:
     95
     96    int audioInit(const settings::RecordingScreenSettings &screenSettings);
     97
     98    static DECLCALLBACK(int) audioCodecWriteDataCallback(PRECORDINGCODEC pCodec, const void *pvData, size_t cbData, uint64_t msAbsPTS, uint32_t uFlags, void *pvUser);
    8899
    89100protected:
     
    107118    Console                     *pConsole;
    108119    /** Used recording configuration. */
    109     settings::RecordingSettings  Settings;
     120    settings::RecordingSettings  m_Settings;
    110121    /** The current state. */
    111122    RECORDINGSTS                 enmState;
     
    116127    /** Shutdown indicator. */
    117128    bool                         fShutdown;
    118     /** Worker thread. */
     129    /** Encoding worker thread. */
    119130    RTTHREAD                     Thread;
    120131    /** Vector of current recording streams.
     
    125136    /** Timestamp (in ms) of when recording has been started. */
    126137    uint64_t                     tsStartMs;
     138#ifdef VBOX_WITH_AUDIO_RECORDING
     139    /** Audio codec to use.
     140     *
     141     *  We multiplex audio data from this recording context to all streams,
     142     *  to avoid encoding the same audio data for each stream. We ASSUME that
     143     *  all audio data of a VM will be the same for each stream at a given
     144     *  point in time. */
     145    RECORDINGCODEC               CodecAudio;
     146#endif /* VBOX_WITH_AUDIO_RECORDING */
    127147    /** Block map of common blocks which need to get multiplexed
    128148     *  to all recording streams. This common block maps should help
  • trunk/src/VBox/Main/include/RecordingInternals.h

    r96178 r96229  
    122122     * @param   pCodec              Codec instance to use.
    123123     * @param   pFrame              Pointer to frame data to encode.
    124      * @param   pvDst               Where to store the encoded data on success.
    125      * @param   cbDst               Size (in bytes) of \a pvDst.
    126124     * @param   pcEncoded           Where to return the number of encoded blocks in \a pvDst on success. Optional.
    127125     * @param   pcbEncoded          Where to return the number of encoded bytes in \a pvDst on success. Optional.
    128126     */
    129     DECLCALLBACKMEMBER(int, pfnEncode,       (PRECORDINGCODEC pCodec, const PRECORDINGFRAME pFrame, void *pvDst, size_t cbDst, size_t *pcEncoded, size_t *pcbEncoded));
     127    DECLCALLBACKMEMBER(int, pfnEncode,       (PRECORDINGCODEC pCodec, const PRECORDINGFRAME pFrame, size_t *pcEncoded, size_t *pcbEncoded));
    130128
    131129    /**
     
    138136} RECORDINGCODECOPS, *PRECORDINGCODECOPS;
    139137
     138/** No encoding flags set. */
     139#define RECORDINGCODEC_ENC_F_NONE               UINT32_C(0)
     140/** Data block is a key block. */
     141#define RECORDINGCODEC_ENC_F_BLOCK_IS_KEY       RT_BIT_32(0)
     142/** Data block is invisible. */
     143#define RECORDINGCODEC_ENC_F_BLOCK_IS_INVISIBLE RT_BIT_32(1)
     144/** Encoding flags valid mask. */
     145#define RECORDINGCODEC_ENC_F_VALID_MASK         0x1
     146
    140147/**
    141148 * Structure for keeping a codec callback table.
     
    143150typedef struct RECORDINGCODECCALLBACKS
    144151{
    145     DECLCALLBACKMEMBER(int, pfnWriteData, (PRECORDINGCODEC pCodec, const void *pvData, size_t cbData, void *pvUser));
     152    /**
     153     * Callback for notifying that encoded data has been written.
     154     *
     155     * @returns VBox status code.
     156     * @param   pCodec          Pointer to codec instance which has written the data.
     157     * @param   pvData          Pointer to written data (encoded).
     158     * @param   cbData          Size (in bytes) of \a pvData.
     159     * @param   msAbsPTS        Absolute PTS (in ms) of the written data.
     160     * @param   uFlags          Encoding flags of type RECORDINGCODEC_ENC_F_XXX.
     161     * @param   pvUser          User-supplied pointer.
     162     */
     163    DECLCALLBACKMEMBER(int, pfnWriteData, (PRECORDINGCODEC pCodec, const void *pvData, size_t cbData, uint64_t msAbsPTS, uint32_t uFlags, void *pvUser));
    146164    /** User-supplied data pointer. */
    147     void                       *pvUser;
     165    void                   *pvUser;
    148166} RECORDINGCODECCALLBACKS, *PRECORDINGCODECCALLBACKS;
    149167
     
    186204     *  Set to 0 to use a variable bit rate (VBR) (if available, otherwise fall back to CBR). */
    187205    uint32_t                    uBitrate;
    188     /** Time (in ms) an (encoded) frame takes.
     206    /** Time (in ms) the encoder expects us to send data to encode.
    189207     *
    190208     *  For Opus, valid frame sizes are:
     
    196214     *  40           1920
    197215     *  60           2880
     216     *
     217     *  For Vorbis, valid frame sizes are powers of two from 64 to 8192 bytes.
    198218     */
    199219    uint32_t                    msFrame;
    200     /** The frame size in bytes (based on msFrame). */
     220    /** The frame size in bytes (based on \a msFrame). */
    201221    uint32_t                    cbFrame;
    202     /** The frame size in samples per frame (based on msFrame). */
     222    /** The frame size in samples per frame (based on \a msFrame). */
    203223    uint32_t                    csFrame;
    204224} RECORDINGCODECPARMS, *PRECORDINGCODECPARMS;
     
    258278
    259279/**
     280 * Structure for keeping a codec's internal state.
     281 */
     282typedef struct RECORDINGCODECSTATE
     283{
     284    /** Timestamp (in ms, absolute) of the last frame was encoded. */
     285    uint64_t            tsLastWrittenMs;
     286    /** Number of encoding errors. */
     287    uint64_t            cEncErrors;
     288} RECORDINGCODECSTATE;
     289/** Pointer to an internal encoder state. */
     290typedef RECORDINGCODECSTATE *PRECORDINGCODECSTATE;
     291
     292/**
    260293 * Structure for keeping codec-specific data.
    261294 */
     
    268301    /** Generic codec parameters. */
    269302    RECORDINGCODECPARMS         Parms;
     303    /** Generic codec parameters. */
     304    RECORDINGCODECSTATE         State;
    270305
    271306#ifdef VBOX_WITH_LIBVPX
     
    288323#endif /* VBOX_WITH_AUDIO_RECORDING */
    289324
    290     /** Timestamp (in ms) of the last frame was encoded. */
    291     uint64_t            uLastTimeStampMs;
    292     /** Number of encoding errors. */
    293     uint64_t            cEncErrors;
     325    /** Internal scratch buffer for en-/decoding steps. */
     326    void               *pvScratch;
     327    /** Size (in bytes) of \a pvScratch. */
     328    uint32_t            cbScratch;
    294329
    295330#ifdef VBOX_WITH_STATISTICS /** @todo Register these values with STAM. */
     
    300335        /** Total time (in ms) of already encoded audio data. */
    301336        uint64_t        msEncTotal;
    302     } Stats;
     337    } STAM;
    303338#endif
    304339} RECORDINGCODEC, *PRECORDINGCODEC;
     
    395430        : enmType(RECORDINGBLOCKTYPE_UNKNOWN)
    396431        , cRefs(0)
     432        , uFlags(RECORDINGCODEC_ENC_F_NONE)
    397433        , pvData(NULL)
    398434        , cbData(0) { }
     
    434470    /** Number of references held of this block. */
    435471    uint16_t           cRefs;
     472    /** Block flags of type RECORDINGCODEC_ENC_F_XXX. */
     473    uint64_t           uFlags;
    436474    /** The (absolute) timestamp (in ms, PTS) of this block. */
    437475    uint64_t           msTimestamp;
     
    449487int recordingCodecInit(const PRECORDINGCODEC pCodec, const PRECORDINGCODECCALLBACKS pCallbacks, const settings::RecordingScreenSettings &Settings);
    450488int recordingCodecDestroy(PRECORDINGCODEC pCodec);
    451 int recordingCodecEncode(PRECORDINGCODEC pCodec, const PRECORDINGFRAME pFrame, void *pvDst, size_t cbDst, size_t *pcEncoded, size_t *pcbEncoded);
     489int recordingCodecEncode(PRECORDINGCODEC pCodec, const PRECORDINGFRAME pFrame, size_t *pcEncoded, size_t *pcbEncoded);
    452490int recordingCodecFinalize(PRECORDINGCODEC pCodec);
    453491#endif /* !MAIN_INCLUDED_RecordingInternals_h */
  • trunk/src/VBox/Main/include/RecordingStream.h

    r96179 r96229  
    126126    uint16_t GetID(void) const { return this->uScreenID; };
    127127#ifdef VBOX_WITH_AUDIO_RECORDING
    128     PRECORDINGCODEC GetAudioCodec(void) { return &this->CodecAudio; };
     128    PRECORDINGCODEC GetAudioCodec(void) { return this->pCodecAudio; };
    129129#endif
    130130    PRECORDINGCODEC GetVideoCodec(void) { return &this->CodecVideo; };
     
    132132    bool IsReady(void) const;
    133133
     134public:
     135
     136    static DECLCALLBACK(int) codecWriteDataCallback(PRECORDINGCODEC pCodec, const void *pvData, size_t cbData, uint64_t msAbsPTS, uint32_t uFlags, void *pvUser);
     137
    134138protected:
    135139
    136     static DECLCALLBACK(int) codecWriteDataCallback(PRECORDINGCODEC pCodec, const void *pvData, size_t cbData, void *pvUser);
    137 
    138 protected:
    139 
    140     int open(const settings::RecordingScreenSettings &Settings);
     140    int open(const settings::RecordingScreenSettings &screenSettings);
    141141    int close(void);
    142142
    143     int initInternal(RecordingContext *pCtx, uint32_t uScreen, const settings::RecordingScreenSettings &Settings);
     143    int initInternal(RecordingContext *pCtx, uint32_t uScreen, const settings::RecordingScreenSettings &screenSettings);
    144144    int uninitInternal(void);
    145145
    146     int initVideo(const settings::RecordingScreenSettings &Settings);
     146    int initVideo(const settings::RecordingScreenSettings &screenSettings);
    147147    int unitVideo(void);
    148 
    149     int initAudio(const settings::RecordingScreenSettings &Settings);
    150148
    151149    bool isLimitReachedInternal(uint64_t msTimestamp) const;
    152150    int iterateInternal(uint64_t msTimestamp);
     151
     152    int codecWriteToWebM(PRECORDINGCODEC pCodec, const void *pvData, size_t cbData, uint64_t msAbsPTS, uint32_t uFlags);
    153153
    154154    void lock(void);
     
    171171
    172172    /** Recording context this stream is associated to. */
    173     RecordingContext       *pCtx;
     173    RecordingContext       *m_pCtx;
    174174    /** The current state. */
    175175    RECORDINGSTREAMSTATE    enmState;
     
    192192    /** Critical section to serialize access. */
    193193    RTCRITSECT          CritSect;
    194     /** Timestamp (in ms) of when recording has been start. */
     194    /** Timestamp (in ms) of when recording has been started. */
    195195    uint64_t            tsStartMs;
    196 
    197     /** Audio codec instance data to use. */
    198     RECORDINGCODEC                    CodecAudio;
     196#ifdef VBOX_WITH_AUDIO_RECORDING
     197    /** Pointer to audio codec instance data to use.
     198     *
     199     *  We multiplex audio data from the recording context to all streams,
     200     *  to avoid encoding the same audio data for each stream. We ASSUME that
     201     *  all audio data of a VM will be the same for each stream at a given
     202     *  point in time.
     203     *
     204     *  Might be NULL if not being used. */
     205    PRECORDINGCODEC     pCodecAudio;
     206#endif /* VBOX_WITH_AUDIO_RECORDING */
    199207    /** Video codec instance data to use. */
    200     RECORDINGCODEC                    CodecVideo;
     208    RECORDINGCODEC      CodecVideo;
    201209    /** Screen settings to use. */
    202     settings::RecordingScreenSettings ScreenSettings;
     210    settings::RecordingScreenSettings
     211                        ScreenSettings;
    203212    /** Common set of recording (data) blocks, needed for
    204213     *  multiplexing to all recording streams. */
    205     RecordingBlockSet                 Blocks;
     214    RecordingBlockSet   Blocks;
    206215};
    207216
  • trunk/src/VBox/Main/include/WebMWriter.h

    r96210 r96229  
    563563    int AddVideoTrack(PRECORDINGCODEC pCodec, uint16_t uWidth, uint16_t uHeight, uint32_t uFPS, uint8_t *puTrack);
    564564
    565     int WriteBlock(uint8_t uTrack, const void *pvData, size_t cbData);
     565    int WriteBlock(uint8_t uTrack, const void *pvData, size_t cbData, WebMTimecodeAbs tcAbsPTSMs, WebMBlockFlags uFlags);
    566566
    567567    const com::Utf8Str& GetFileName(void);
     
    594594    int writeSimpleBlockQueued(WebMTrack *a_pTrack, WebMSimpleBlock *a_pBlock);
    595595
    596 #ifdef VBOX_WITH_LIBVPX
    597     int writeSimpleBlockVP8(WebMTrack *a_pTrack, const vpx_codec_enc_cfg_t *a_pCfg, const vpx_codec_cx_pkt_t *a_pPkt);
    598 #endif
    599 
    600     int writeSimpleBlockAudio(WebMTrack *pTrack, const void *pvData, size_t cbData, WebMTimecodeAbs tcAbsPTSMs);
    601 
    602596    int processQueue(WebMQueue *pQueue, bool fForce);
    603597
  • trunk/src/VBox/Main/src-client/DrvAudioRec.cpp

    r96207 r96229  
    171171typedef struct AVRECSINK
    172172{
    173     /** Pointer to audio codec to use. */
     173    /** Pointer (weak) to audio codec to use. */
    174174    PRECORDINGCODEC      pCodec;
    175175    /** Container data to use for data processing. */
     
    198198    /** Size (in bytes) of the temporary buffer holding the input (source) data to encode. */
    199199    size_t                  cbSrcBuf;
    200     /** Temporary buffer for the encoded output (destination) data. */
    201     void                   *pvDstBuf;
    202     /** Size (in bytes) of the temporary buffer holding the encoded output (destination) data. */
    203     size_t                  cbDstBuf;
    204200} AVRECSTREAM, *PAVRECSTREAM;
    205201
     
    355351        {
    356352            pStreamAV->cbSrcBuf = cbScratchBuf;
    357             pStreamAV->pvDstBuf = RTMemAlloc(cbScratchBuf);
    358             if (pStreamAV->pvDstBuf)
    359             {
    360                 pStreamAV->cbDstBuf = cbScratchBuf;
    361 
    362                 pStreamAV->pSink      = pSink; /* Assign sink to stream. */
    363                 pStreamAV->uLastPTSMs = 0;
    364 
    365                 /* Make sure to let the driver backend know that we need the audio data in
    366                  * a specific sampling rate the codec is optimized for. */
     353
     354            pStreamAV->pSink      = pSink; /* Assign sink to stream. */
     355            pStreamAV->uLastPTSMs = 0;
     356
     357            /* Make sure to let the driver backend know that we need the audio data in
     358             * a specific sampling rate the codec is optimized for. */
    367359/** @todo r=bird: pCfgAcq->Props isn't initialized at all, except for uHz... */
    368                 pCfgAcq->Props.uHz         = pCodec->Parms.Audio.PCMProps.uHz;
     360            pCfgAcq->Props.uHz         = pCodec->Parms.Audio.PCMProps.uHz;
    369361//                pCfgAcq->Props.cShift      = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(pCfgAcq->Props.cbSample, pCfgAcq->Props.cChannels);
    370362
    371                 /* Every Opus frame marks a period for now. Optimize this later. */
    372                 pCfgAcq->Backend.cFramesPeriod       = PDMAudioPropsMilliToFrames(&pCfgAcq->Props, pCodec->Parms.msFrame);
    373                 pCfgAcq->Backend.cFramesBufferSize   = PDMAudioPropsMilliToFrames(&pCfgAcq->Props, 100 /*ms*/); /** @todo Make this configurable. */
    374                 pCfgAcq->Backend.cFramesPreBuffering = pCfgAcq->Backend.cFramesPeriod * 2;
    375             }
    376             else
    377                 vrc = VERR_NO_MEMORY;
     363            /* Every Opus frame marks a period for now. Optimize this later. */
     364            pCfgAcq->Backend.cFramesPeriod       = PDMAudioPropsMilliToFrames(&pCfgAcq->Props, pCodec->Parms.msFrame);
     365            pCfgAcq->Backend.cFramesBufferSize   = PDMAudioPropsMilliToFrames(&pCfgAcq->Props, 100 /*ms*/); /** @todo Make this configurable. */
     366            pCfgAcq->Backend.cFramesPreBuffering = pCfgAcq->Backend.cFramesPeriod * 2;
    378367        }
    379368        else
     
    435424        pStreamAV->pvSrcBuf = NULL;
    436425        pStreamAV->cbSrcBuf = 0;
    437     }
    438 
    439     if (pStreamAV->pvDstBuf)
    440     {
    441         Assert(pStreamAV->cbDstBuf);
    442         RTMemFree(pStreamAV->pvDstBuf);
    443         pStreamAV->pvDstBuf = NULL;
    444         pStreamAV->cbDstBuf = 0;
    445426    }
    446427
     
    629610        vrc = recordingCodecEncode(pSink->pCodec,
    630611                                   /* Source */
    631                                    &Frame,
    632                                    /* Dest */
    633                                    pStreamAV->pvDstBuf, pStreamAV->cbDstBuf, &cEncoded, &cbEncoded);
     612                                   &Frame, &cEncoded, &cbEncoded);
    634613        if (   RT_SUCCESS(vrc)
    635614            && cEncoded)
    636615        {
    637616            Assert(cbEncoded);
    638 
    639             if (pStreamAV->uLastPTSMs == 0)
    640                 pStreamAV->uLastPTSMs = RTTimeProgramMilliTS(); /* We want the absolute time (in ms) since program start. */
    641 
    642             const uint64_t uDurationMs = pSink->pCodec->Parms.msFrame * cEncoded;
    643             const uint64_t uPTSMs      = pStreamAV->uLastPTSMs;
    644 
    645             pStreamAV->uLastPTSMs += uDurationMs;
    646 
    647             switch (pSink->Con.Parms.enmType)
    648             {
    649                 case AVRECCONTAINERTYPE_MAIN_CONSOLE:
    650                 {
    651                     HRESULT hrc = pSink->Con.Main.pConsole->i_recordingSendAudio(pStreamAV->pvDstBuf, cbEncoded, uPTSMs);
    652                     Assert(hrc == S_OK);
    653                     RT_NOREF(hrc);
    654                     break;
    655                 }
    656 
    657                 case AVRECCONTAINERTYPE_WEBM:
    658                 {
    659                     WebMWriter::BlockData_Audio blockData = { pStreamAV->pvDstBuf, cbEncoded, uPTSMs };
    660                     vrc = pSink->Con.WebM.pWebM->WriteBlock(pSink->Con.WebM.uTrack, &blockData, sizeof(blockData));
    661                     AssertRC(vrc);
    662                     break;
    663                 }
    664 
    665                 default:
    666                     AssertFailedStmt(vrc = VERR_NOT_IMPLEMENTED);
    667                     break;
    668             }
    669617        }
    670618        else if (RT_FAILURE(vrc)) /* Something went wrong -- report all bytes as being processed, to not hold up others. */
     
    737685    AssertPtrReturnVoid(pSink);
    738686
    739     recordingCodecFinalize(pSink->pCodec);
    740     recordingCodecDestroy(pSink->pCodec);
    741687    pSink->pCodec = NULL;
    742688
     
    901847        return VINF_SUCCESS;
    902848    }
    903 
    904     recordingCodecDestroy(pSink->pCodec);
    905     pSink->pCodec = NULL;
    906849
    907850    LogRel(("Recording: Error creating sink (%Rrc)\n", vrc));
  • trunk/src/VBox/Main/src-client/Recording.cpp

    r96140 r96229  
    6161 * Recording context constructor.
    6262 *
    63  * @note    Will throw when unable to create.
     63 * @note    Will throw rc when unable to create.
    6464 */
    6565RecordingContext::RecordingContext(void)
     
    7777 *
    7878 * @param   ptrConsole          Pointer to console object this context is bound to (weak pointer).
    79  * @param   settings            Reference to recording settings to use for creation.
    80  *
    81  * @note    Will throw when unable to create.
    82  */
    83 RecordingContext::RecordingContext(Console *ptrConsole, const settings::RecordingSettings &settings)
     79 * @param   Settings            Reference to recording settings to use for creation.
     80 *
     81 * @note    Will throw rc when unable to create.
     82 */
     83RecordingContext::RecordingContext(Console *ptrConsole, const settings::RecordingSettings &Settings)
    8484    : pConsole(NULL)
    8585    , enmState(RECORDINGSTS_UNINITIALIZED)
     
    9090        throw vrc;
    9191
    92     vrc = RecordingContext::createInternal(ptrConsole, settings);
     92    vrc = RecordingContext::createInternal(ptrConsole, Settings);
    9393    if (RT_FAILURE(vrc))
    9494        throw vrc;
     
    170170
    171171/**
     172 * Writes block data which are common (shared) between all streams.
     173 *
     174 * To save time spent in EMT or other important threads (such as audio async I/O),
     175 * do the required audio multiplexing in the encoding thread.
     176 *
     177 * The multiplexing is needed to supply all recorded (enabled) screens with the same
     178 * data at the same given point in time.
     179 *
     180 * Currently this only is being used for audio data.
     181 */
     182int RecordingContext::writeCommonData(PRECORDINGCODEC pCodec, const void *pvData, size_t cbData,
     183                                      uint64_t msAbsPTS, uint32_t uFlags)
     184{
     185    AssertPtrReturn(pvData, VERR_INVALID_POINTER);
     186    AssertReturn(cbData, VERR_INVALID_PARAMETER);
     187
     188    LogFlowFunc(("pCodec=%p, cbData=%zu, msAbsPTS=%zu, uFlags=%#x\n",
     189                 pCodec, cbData, msAbsPTS, uFlags));
     190
     191    /** @todo Optimize this! Three allocations in here! */
     192
     193    RECORDINGBLOCKTYPE const enmType = pCodec->Parms.enmType == RECORDINGCODECTYPE_AUDIO
     194                                     ? RECORDINGBLOCKTYPE_AUDIO : RECORDINGBLOCKTYPE_UNKNOWN;
     195
     196    AssertReturn(enmType != RECORDINGBLOCKTYPE_UNKNOWN, VERR_NOT_SUPPORTED);
     197
     198    RecordingBlock *pBlock = new RecordingBlock();
     199
     200    switch (enmType)
     201    {
     202        case RECORDINGBLOCKTYPE_AUDIO:
     203        {
     204            PRECORDINGAUDIOFRAME pFrame = (PRECORDINGAUDIOFRAME)RTMemAlloc(sizeof(RECORDINGAUDIOFRAME));
     205            AssertPtrReturn(pFrame, VERR_NO_MEMORY);
     206
     207            pFrame->pvBuf = (uint8_t *)RTMemAlloc(cbData);
     208            AssertPtrReturn(pFrame->pvBuf, VERR_NO_MEMORY);
     209            pFrame->cbBuf = cbData;
     210
     211            memcpy(pFrame->pvBuf, pvData, cbData);
     212
     213            pBlock->enmType     = enmType;
     214            pBlock->pvData      = pFrame;
     215            pBlock->cbData      = sizeof(RECORDINGAUDIOFRAME) + cbData;
     216            pBlock->cRefs       = this->cStreamsEnabled;
     217            pBlock->msTimestamp = msAbsPTS;
     218            pBlock->uFlags      = uFlags;
     219
     220            break;
     221        }
     222
     223        default:
     224            AssertFailed();
     225            break;
     226    }
     227
     228    lock();
     229
     230    int vrc;
     231
     232    try
     233    {
     234        RecordingBlockMap::iterator itBlocks = this->mapBlocksCommon.find(msAbsPTS);
     235        if (itBlocks == this->mapBlocksCommon.end())
     236        {
     237            RecordingBlocks *pRecordingBlocks = new RecordingBlocks();
     238            pRecordingBlocks->List.push_back(pBlock);
     239
     240            this->mapBlocksCommon.insert(std::make_pair(msAbsPTS, pRecordingBlocks));
     241        }
     242        else
     243            itBlocks->second->List.push_back(pBlock);
     244
     245        vrc = VINF_SUCCESS;
     246    }
     247    catch (const std::exception &ex)
     248    {
     249        RT_NOREF(ex);
     250        vrc = VERR_NO_MEMORY;
     251    }
     252
     253    unlock();
     254
     255    if (RT_SUCCESS(vrc))
     256        vrc = threadNotify();
     257
     258    return vrc;
     259}
     260
     261#ifdef VBOX_WITH_AUDIO_RECORDING
     262/**
     263 * Callback function for taking care of multiplexing the encoded audio data to all connected streams.
     264 *
     265 * @copydoc RECORDINGCODECCALLBACKS::pfnWriteData
     266 */
     267/* static */
     268DECLCALLBACK(int) RecordingContext::audioCodecWriteDataCallback(PRECORDINGCODEC pCodec, const void *pvData, size_t cbData,
     269                                                                uint64_t msAbsPTS, uint32_t uFlags, void *pvUser)
     270{
     271#if 0
     272    RecordingContext *pThis = (RecordingContext *)pvUser;
     273
     274    int vrc = VINF_SUCCESS;
     275
     276    RecordingStreams::iterator itStream = pThis->vecStreams.begin();
     277    while (itStream != pThis->vecStreams.end())
     278    {
     279        RecordingStream *pStream = (*itStream);
     280
     281        LogFlowFunc(("pStream=%p\n", pStream));
     282
     283        if (pStream->GetConfig().isFeatureEnabled(RecordingFeature_Audio)) /** @todo Optimize this! Use a dedicated stream group for video-only / audio-only streams. */
     284        {
     285            vrc = RecordingStream::codecWriteDataCallback(pCodec, pvData, cbData, msAbsPTS, uFlags, pStream);
     286            if (RT_FAILURE(vrc))
     287            {
     288                LogRel(("Recording: Calling audio write callback for stream #%RU16 failed (%Rrc)\n", pStream->GetID(), vrc));
     289                break;
     290            }
     291        }
     292
     293        ++itStream;
     294    }
     295
     296    return vrc;
     297#else
     298    RecordingContext *pThis = (RecordingContext *)pvUser;
     299
     300    return pThis->writeCommonData(pCodec, pvData, cbData, msAbsPTS, uFlags);
     301#endif
     302}
     303
     304/**
     305 * Initializes the audio codec for a (multiplexing) recording context.
     306 *
     307 * @returns VBox status code.
     308 * @param   screenSettings      Reference to recording screen settings to use for initialization.
     309 */
     310int RecordingContext::audioInit(const settings::RecordingScreenSettings &screenSettings)
     311{
     312    RecordingAudioCodec_T const enmCodec = screenSettings.Audio.enmCodec;
     313
     314    if (enmCodec == RecordingAudioCodec_None)
     315    {
     316        LogRel2(("Recording: No audio codec configured, skipping audio init\n"));
     317        return VINF_SUCCESS;
     318    }
     319
     320    RECORDINGCODECCALLBACKS Callbacks;
     321    Callbacks.pvUser       = this;
     322    Callbacks.pfnWriteData = RecordingContext::audioCodecWriteDataCallback;
     323
     324    int vrc = recordingCodecCreateAudio(&this->CodecAudio, enmCodec);
     325    if (RT_SUCCESS(vrc))
     326        vrc = recordingCodecInit(&this->CodecAudio, &Callbacks, screenSettings);
     327
     328    return vrc;
     329}
     330#endif /* VBOX_WITH_AUDIO_RECORDING */
     331
     332/**
    172333 * Creates a recording context.
    173334 *
    174335 * @returns IPRT status code.
    175336 * @param   ptrConsole          Pointer to console object this context is bound to (weak pointer).
    176  * @param   settings            Reference to recording settings to use for creation.
    177  */
    178 int RecordingContext::createInternal(Console *ptrConsole, const settings::RecordingSettings &settings)
     337 * @param   Settings            Reference to recording settings to use for creation.
     338 */
     339int RecordingContext::createInternal(Console *ptrConsole, const settings::RecordingSettings &Settings)
    179340{
    180341    int vrc = VINF_SUCCESS;
    181342
     343    /* Copy the settings to our context. */
     344    m_Settings = Settings;
     345
     346#ifdef VBOX_WITH_AUDIO_RECORDING
     347    settings::RecordingScreenSettingsMap::const_iterator itScreen0 = m_Settings.mapScreens.begin();
     348    AssertReturn(itScreen0 != m_Settings.mapScreens.end(), VERR_WRONG_ORDER);
     349
     350    /* We always use the audio settings from screen 0, as we multiplex the audio data anyway. */
     351    settings::RecordingScreenSettings const &screen0Settings = itScreen0->second;
     352
     353    vrc = this->audioInit(screen0Settings);
     354    if (RT_FAILURE(vrc))
     355        return vrc;
     356#endif
     357
    182358    this->pConsole = ptrConsole;
    183359
    184     settings::RecordingScreenSettingsMap::const_iterator itScreen = settings.mapScreens.begin();
    185     while (itScreen != settings.mapScreens.end())
     360    settings::RecordingScreenSettingsMap::const_iterator itScreen = m_Settings.mapScreens.begin();
     361    while (itScreen != m_Settings.mapScreens.end())
    186362    {
    187363        RecordingStream *pStream = NULL;
     
    192368            if (itScreen->second.fEnabled)
    193369                this->cStreamsEnabled++;
     370            LogFlowFunc(("pStream=%p\n", pStream));
    194371        }
    195372        catch (std::bad_alloc &)
     
    213390        this->fShutdown = false;
    214391
    215         /* Copy the settings to our context. */
    216         this->Settings  = settings;
    217 
    218392        vrc = RTSemEventCreate(&this->WaitEvent);
    219393        AssertRCReturn(vrc, vrc);
     
    343517const settings::RecordingSettings &RecordingContext::GetConfig(void) const
    344518{
    345     return this->Settings;
     519    return this->m_Settings;
    346520}
    347521
     
    368542}
    369543
     544/**
     545 * Locks the recording context for serializing access.
     546 *
     547 * @returns VBox status code.
     548 */
    370549int RecordingContext::lock(void)
    371550{
     
    375554}
    376555
     556/**
     557 * Unlocks the recording context for serializing access.
     558 *
     559 * @returns VBox status code.
     560 */
    377561int RecordingContext::unlock(void)
    378562{
     
    408592 * @returns IPRT status code.
    409593 * @param   ptrConsole          Pointer to console object this context is bound to (weak pointer).
    410  * @param   settings            Reference to recording settings to use for creation.
    411  */
    412 int RecordingContext::Create(Console *ptrConsole, const settings::RecordingSettings &settings)
    413 {
    414     return createInternal(ptrConsole, settings);
     594 * @param   Settings            Reference to recording settings to use for creation.
     595 */
     596int RecordingContext::Create(Console *ptrConsole, const settings::RecordingSettings &Settings)
     597{
     598    return createInternal(ptrConsole, Settings);
    415599}
    416600
     
    605789{
    606790#ifdef VBOX_WITH_AUDIO_RECORDING
    607     AssertPtrReturn(pvData, VERR_INVALID_POINTER);
    608     AssertReturn(cbData, VERR_INVALID_PARAMETER);
    609 
    610     /* To save time spent in EMT, do the required audio multiplexing in the encoding thread.
    611      *
    612      * The multiplexing is needed to supply all recorded (enabled) screens with the same
    613      * audio data at the same given point in time.
    614      */
    615     RecordingBlock *pBlock = new RecordingBlock();
    616     pBlock->enmType = RECORDINGBLOCKTYPE_AUDIO;
    617 
    618     PRECORDINGAUDIOFRAME pFrame = (PRECORDINGAUDIOFRAME)RTMemAlloc(sizeof(RECORDINGAUDIOFRAME));
    619     AssertPtrReturn(pFrame, VERR_NO_MEMORY);
    620 
    621     pFrame->pvBuf = (uint8_t *)RTMemAlloc(cbData);
    622     AssertPtrReturn(pFrame->pvBuf, VERR_NO_MEMORY);
    623     pFrame->cbBuf = cbData;
    624 
    625     memcpy(pFrame->pvBuf, pvData, cbData);
    626 
    627     pBlock->pvData      = pFrame;
    628     pBlock->cbData      = sizeof(RECORDINGAUDIOFRAME) + cbData;
    629     pBlock->cRefs       = this->cStreamsEnabled;
    630     pBlock->msTimestamp = msTimestamp;
    631 
    632     lock();
    633 
    634     int vrc;
    635 
    636     try
    637     {
    638         RecordingBlockMap::iterator itBlocks = this->mapBlocksCommon.find(msTimestamp);
    639         if (itBlocks == this->mapBlocksCommon.end())
    640         {
    641             RecordingBlocks *pRecordingBlocks = new RecordingBlocks();
    642             pRecordingBlocks->List.push_back(pBlock);
    643 
    644             this->mapBlocksCommon.insert(std::make_pair(msTimestamp, pRecordingBlocks));
    645         }
    646         else
    647             itBlocks->second->List.push_back(pBlock);
    648 
    649         vrc = VINF_SUCCESS;
    650     }
    651     catch (const std::exception &ex)
    652     {
    653         RT_NOREF(ex);
    654         vrc = VERR_NO_MEMORY;
    655     }
    656 
    657     unlock();
    658 
    659     if (RT_SUCCESS(vrc))
    660         vrc = threadNotify();
    661 
    662     return vrc;
     791    return writeCommonData(&this->CodecAudio, pvData, cbData, msTimestamp, RECORDINGCODEC_ENC_F_BLOCK_IS_KEY);
    663792#else
    664793    RT_NOREF(pvData, cbData, msTimestamp);
  • trunk/src/VBox/Main/src-client/RecordingCodec.cpp

    r96179 r96229  
    3939static DECLCALLBACK(int) recordingCodecVPXInit(PRECORDINGCODEC pCodec)
    4040{
     41    pCodec->cbScratch = _4K;
     42    pCodec->pvScratch = RTMemAlloc(pCodec->cbScratch);
     43    AssertPtrReturn(pCodec->pvScratch, VERR_NO_MEMORY);
     44
    4145# ifdef VBOX_WITH_LIBVPX_VP9
    4246    vpx_codec_iface_t *pCodecIface = vpx_codec_vp9_cx();
     
    132136/** @copydoc RECORDINGCODECOPS::pfnEncode */
    133137static DECLCALLBACK(int) recordingCodecVPXEncode(PRECORDINGCODEC pCodec, PRECORDINGFRAME pFrame,
    134                                                  void *pvDst, size_t cbDst, size_t *pcEncoded, size_t *pcbEncoded)
    135 {
    136     RT_NOREF(pvDst, cbDst, pcEncoded, pcbEncoded);
     138                                                 size_t *pcEncoded, size_t *pcbEncoded)
     139{
     140    RT_NOREF(pcEncoded, pcbEncoded);
    137141
    138142    AssertPtrReturn(pFrame, VERR_INVALID_POINTER);
     
    158162    if (rcv != VPX_CODEC_OK)
    159163    {
    160         if (pCodec->cEncErrors++ < 64) /** @todo Make this configurable. */
     164        if (pCodec->State.cEncErrors++ < 64) /** @todo Make this configurable. */
    161165            LogRel(("Recording: Failed to encode video frame: %s\n", vpx_codec_err_to_string(rcv)));
    162166        return VERR_RECORDING_ENCODING_FAILED;
    163167    }
    164168
    165     pCodec->cEncErrors = 0;
     169    pCodec->State.cEncErrors = 0;
    166170
    167171    vpx_codec_iter_t iter = NULL;
     
    169173    for (;;)
    170174    {
    171         const vpx_codec_cx_pkt_t *pPacket = vpx_codec_get_cx_data(&pVPX->Ctx, &iter);
    172         if (!pPacket)
     175        const vpx_codec_cx_pkt_t *pPkt = vpx_codec_get_cx_data(&pVPX->Ctx, &iter);
     176        if (!pPkt)
    173177            break;
    174178
    175         switch (pPacket->kind)
     179        switch (pPkt->kind)
    176180        {
    177181            case VPX_CODEC_CX_FRAME_PKT:
    178182            {
    179                 WebMWriter::BlockData_VP8 blockData = { &pCodec->Video.VPX.Cfg, pPacket };
    180                 AssertPtr(pCodec->Callbacks.pfnWriteData);
    181 
    182                 vrc = pCodec->Callbacks.pfnWriteData(pCodec, &blockData, sizeof(blockData), pCodec->Callbacks.pvUser);
     183                /* Calculate the absolute PTS of this frame (in ms). */
     184                uint64_t tsAbsPTSMs =   pPkt->data.frame.pts * 1000
     185                                      * (uint64_t)pCodec->Video.VPX.Cfg.g_timebase.num / pCodec->Video.VPX.Cfg.g_timebase.den;
     186
     187                pCodec->State.tsLastWrittenMs += pCodec->Parms.msFrame;
     188
     189                const bool fKeyframe = RT_BOOL(pPkt->data.frame.flags & VPX_FRAME_IS_KEY);
     190
     191                uint32_t fFlags = RECORDINGCODEC_ENC_F_NONE;
     192                if (fKeyframe)
     193                    fFlags |= RECORDINGCODEC_ENC_F_BLOCK_IS_KEY;
     194                if (pPkt->data.frame.flags & VPX_FRAME_IS_INVISIBLE)
     195                    fFlags |= RECORDINGCODEC_ENC_F_BLOCK_IS_INVISIBLE;
     196
     197                vrc = pCodec->Callbacks.pfnWriteData(pCodec, pPkt->data.frame.buf, pPkt->data.frame.sz,
     198                                                     tsAbsPTSMs, fFlags, pCodec->Callbacks.pvUser);
    183199                break;
    184200            }
     
    186202            default:
    187203                AssertFailed();
    188                 LogFunc(("Unexpected video packet type %ld\n", pPacket->kind));
     204                LogFunc(("Unexpected video packet type %ld\n", pPkt->kind));
    189205                break;
    190206        }
     
    204220static DECLCALLBACK(int) recordingCodecOpusInit(PRECORDINGCODEC pCodec)
    205221{
     222    pCodec->cbScratch = _4K;
     223    pCodec->pvScratch = RTMemAlloc(pCodec->cbScratch);
     224    AssertPtrReturn(pCodec->pvScratch, VERR_NO_MEMORY);
     225
    206226    const PPDMAUDIOPCMPROPS pProps = &pCodec->Parms.Audio.PCMProps;
    207227
     
    275295/** @copydoc RECORDINGCODECOPS::pfnEncode */
    276296static DECLCALLBACK(int) recordingCodecOpusEncode(PRECORDINGCODEC pCodec,
    277                                                   const PRECORDINGFRAME pFrame, void *pvDst, size_t cbDst,
    278                                                   size_t *pcEncoded, size_t *pcbEncoded)
     297                                                  const PRECORDINGFRAME pFrame, size_t *pcEncoded, size_t *pcbEncoded)
    279298{
    280299    const PPDMAUDIOPCMPROPS pPCMProps = &pCodec->Parms.Audio.PCMProps;
     
    284303    Assert         (pFrame->Audio.cbBuf);
    285304    AssertReturn   (pFrame->Audio.cbBuf % pPCMProps->cbFrame == 0, VERR_INVALID_PARAMETER);
     305    AssertReturn(pCodec->cbScratch >= pFrame->Audio.cbBuf, VERR_INVALID_PARAMETER);
    286306    AssertPtrReturn(pcEncoded,  VERR_INVALID_POINTER);
    287307    AssertPtrReturn(pcbEncoded, VERR_INVALID_POINTER);
    288308
    289309    int vrc = VINF_SUCCESS;
     310
     311    size_t cBlocksEncoded = 0;
     312    size_t cBytesEncoded  = 0;
    290313
    291314    /*
     
    298321    opus_int32 cbWritten = opus_encode(pCodec->Audio.Opus.pEnc,
    299322                                       (opus_int16 *)pFrame->Audio.pvBuf, (int)(pFrame->Audio.cbBuf / pPCMProps->cbFrame /* Number of audio frames */),
    300                                        (uint8_t *)pvDst, (opus_int32)cbDst);
     323                                       (uint8_t *)pCodec->pvScratch, (opus_int32)pCodec->cbScratch);
    301324    if (cbWritten < 0)
    302325    {
     
    305328    }
    306329
    307     /* Get overall frames encoded. */
    308     *pcEncoded  = opus_packet_get_nb_frames((uint8_t *)pvDst, cbWritten);
    309     *pcbEncoded = cbWritten;
     330    if (cbWritten)
     331    {
     332        vrc = pCodec->Callbacks.pfnWriteData(pCodec, pCodec->pvScratch, (size_t)cbWritten, pCodec->State.tsLastWrittenMs,
     333                                             RECORDINGCODEC_ENC_F_BLOCK_IS_KEY /* Every Opus frame is a key frame */,
     334                                             pCodec->Callbacks.pvUser);
     335        if (RT_SUCCESS(vrc))
     336            pCodec->State.tsLastWrittenMs += pCodec->Parms.msFrame;
     337    }
     338
     339    if (RT_SUCCESS(vrc))
     340    {
     341        /* Get overall frames encoded. */
     342        cBlocksEncoded = opus_packet_get_nb_frames((uint8_t *)pCodec->pvScratch, cbWritten);
     343        cBytesEncoded  = cbWritten;
     344
     345        if (pcEncoded)
     346            *pcEncoded  = cBlocksEncoded;
     347        if (pcbEncoded)
     348            *pcbEncoded = cBytesEncoded;
     349    }
    310350
    311351    if (RT_FAILURE(vrc))
    312352        LogRel(("Recording: Encoding Opus data failed, rc=%Rrc\n", vrc));
    313353
    314     Log3Func(("cbSrc=%zu, cbDst=%zu, cEncoded=%zu, cbEncoded=%zu, vrc=%Rrc\n", pFrame->Audio.cbBuf, cbDst,
    315               RT_SUCCESS(vrc) ? *pcEncoded : 0, RT_SUCCESS(vrc) ? *pcbEncoded : 0, vrc));
     354    Log3Func(("cbSrc=%zu, cbDst=%zu, cEncoded=%zu, cbEncoded=%zu, vrc=%Rrc\n",
     355              pFrame->Audio.cbBuf, pCodec->cbScratch, cBlocksEncoded, cBytesEncoded, vrc));
    316356
    317357    return vrc;
     
    328368static DECLCALLBACK(int) recordingCodecVorbisInit(PRECORDINGCODEC pCodec)
    329369{
     370    pCodec->cbScratch = _4K;
     371    pCodec->pvScratch = RTMemAlloc(pCodec->cbScratch);
     372    AssertPtrReturn(pCodec->pvScratch, VERR_NO_MEMORY);
     373
    330374    const PPDMAUDIOPCMPROPS pPCMProps = &pCodec->Parms.Audio.PCMProps;
    331375
     
    395439/** @copydoc RECORDINGCODECOPS::pfnEncode */
    396440static DECLCALLBACK(int) recordingCodecVorbisEncode(PRECORDINGCODEC pCodec,
    397                                                     const PRECORDINGFRAME pFrame, void *pvDst, size_t cbDst,
    398                                                     size_t *pcEncoded, size_t *pcbEncoded)
     441                                                    const PRECORDINGFRAME pFrame, size_t *pcEncoded, size_t *pcbEncoded)
    399442{
    400443    const PPDMAUDIOPCMPROPS pPCMProps = &pCodec->Parms.Audio.PCMProps;
    401444
    402     Assert         (pCodec->Parms.cbFrame);
    403     AssertReturn   (pFrame->Audio.cbBuf % pCodec->Parms.cbFrame == 0, VERR_INVALID_PARAMETER);
    404     Assert         (pFrame->Audio.cbBuf);
    405     AssertReturn   (pFrame->Audio.cbBuf % PDMAudioPropsFrameSize(pPCMProps) == 0, VERR_INVALID_PARAMETER);
     445    Assert      (pCodec->Parms.cbFrame);
     446    AssertReturn(pFrame->Audio.cbBuf % pCodec->Parms.cbFrame == 0, VERR_INVALID_PARAMETER);
     447    Assert      (pFrame->Audio.cbBuf);
     448    AssertReturn(pFrame->Audio.cbBuf % PDMAudioPropsFrameSize(pPCMProps) == 0, VERR_INVALID_PARAMETER);
     449    AssertReturn(pCodec->cbScratch >= pFrame->Audio.cbBuf, VERR_INVALID_PARAMETER);
    406450
    407451    int vrc = VINF_SUCCESS;
     
    443487    size_t cBytesEncoded  = 0;
    444488
    445     uint8_t *puDst = (uint8_t *)pvDst;
     489    uint8_t *puDst = (uint8_t *)pCodec->pvScratch;
    446490
    447491    while (vorbis_analysis_blockout(&pCodec->Audio.Vorbis.dsp_state, &pCodec->Audio.Vorbis.block_cur) == 1 /* More available? */)
     
    474518        {
    475519            cBytesEncoded += op.bytes;
    476             AssertBreakStmt(cBytesEncoded <= cbDst, vrc = VERR_BUFFER_OVERFLOW);
     520            AssertBreakStmt(cBytesEncoded <= pCodec->cbScratch, vrc = VERR_BUFFER_OVERFLOW);
    477521            cBlocksEncoded++;
    478522
    479             if (pCodec->Callbacks.pfnWriteData)
    480             {
    481                 WebMWriter::BlockData_Audio blockData = { op.packet, (size_t)op.bytes, pCodec->uLastTimeStampMs };
    482                 pCodec->Callbacks.pfnWriteData(pCodec, &blockData, sizeof(blockData), pCodec->Callbacks.pvUser);
    483             }
    484 
    485             pCodec->uLastTimeStampMs += uDurationMs;
     523            vrc = pCodec->Callbacks.pfnWriteData(pCodec, op.packet, (size_t)op.bytes, pCodec->State.tsLastWrittenMs,
     524                                                 RECORDINGCODEC_ENC_F_BLOCK_IS_KEY /* Every Vorbis frame is a key frame */,
     525                                                 pCodec->Callbacks.pvUser);
     526            if (RT_SUCCESS(vrc))
     527                pCodec->State.tsLastWrittenMs += uDurationMs;
    486528        }
    487529
     
    513555
    514556    Log3Func(("cbSrc=%zu, cbDst=%zu, cEncoded=%zu, cbEncoded=%zu, vrc=%Rrc\n",
    515               pFrame->Audio.cbBuf, cbDst, cBlocksEncoded, cBytesEncoded, vrc));
     557              pFrame->Audio.cbBuf, pCodec->cbScratch, cBlocksEncoded, cBytesEncoded, vrc));
    516558
    517559    return vrc;
     
    687729#endif
    688730
     731static void recordingCodecReset(PRECORDINGCODEC pCodec)
     732{
     733    pCodec->State.tsLastWrittenMs = 0;
     734
     735    pCodec->State.cEncErrors = 0;
     736#ifdef VBOX_WITH_STATISTICS
     737    pCodec->STAM.cEncBlocks  = 0;
     738    pCodec->STAM.msEncTotal  = 0;
     739#endif
     740}
     741
     742/**
     743 * Common code for codec creation.
     744 *
     745 * @param   pCodec              Codec instance to create.
     746 */
     747static void recordingCodecCreateCommon(PRECORDINGCODEC pCodec)
     748{
     749    RT_ZERO(pCodec->Ops);
     750    RT_ZERO(pCodec->Callbacks);
     751}
     752
    689753/**
    690754 * Creates an audio codec.
     
    697761{
    698762    int vrc;
     763
     764    recordingCodecCreateCommon(pCodec);
    699765
    700766    switch (enmAudioCodec)
     
    728794
    729795        default:
     796            LogRel(("Recording: Selected codec is not supported!\n"));
    730797            vrc = VERR_RECORDING_CODEC_NOT_SUPPORTED;
    731798            break;
     
    751818{
    752819    int vrc;
     820
     821    recordingCodecCreateCommon(pCodec);
    753822
    754823    switch (enmVideoCodec)
     
    791860int recordingCodecInit(const PRECORDINGCODEC pCodec, const PRECORDINGCODECCALLBACKS pCallbacks, const settings::RecordingScreenSettings &Settings)
    792861{
     862    recordingCodecReset(pCodec);
     863
    793864    int vrc;
    794865    if (pCodec->Parms.enmType == RECORDINGCODECTYPE_AUDIO)
     
    799870        AssertFailedStmt(vrc = VERR_NOT_SUPPORTED);
    800871
    801     if (RT_SUCCESS(vrc))
    802     {
    803         pCodec->cEncErrors       = 0;
    804 #ifdef VBOX_WITH_STATISTICS
    805         pCodec->Stats.cEncBlocks = 0;
    806         pCodec->Stats.msEncTotal = 0;
    807 #endif
    808     }
    809 
    810872    return vrc;
    811873}
     
    859921    if (RT_SUCCESS(vrc))
    860922    {
     923        if (pCodec->pvScratch)
     924        {
     925            Assert(pCodec->cbScratch);
     926            RTMemFree(pCodec->pvScratch);
     927            pCodec->pvScratch = NULL;
     928            pCodec->cbScratch = 0;
     929        }
     930
    861931        pCodec->Parms.enmType       = RECORDINGCODECTYPE_INVALID;
    862932        pCodec->Parms.enmVideoCodec = RecordingVideoCodec_None;
     
    872942 * @param   pCodec              Codec to use.
    873943 * @param   pFrame              Pointer to frame data to encode.
    874  * @param   pvDst               Where to store the encoded data on success.
    875  * @param   cbDst               Size (in bytes) of \a pvDst.
    876944 * @param   pcEncoded           Where to return the number of encoded blocks in \a pvDst on success. Optional.
    877945 * @param   pcbEncoded          Where to return the number of encoded bytes in \a pvDst on success. Optional.
    878946 */
    879947int recordingCodecEncode(PRECORDINGCODEC pCodec,
    880                          const PRECORDINGFRAME pFrame, void *pvDst, size_t cbDst,
    881                          size_t *pcEncoded, size_t *pcbEncoded)
     948                         const PRECORDINGFRAME pFrame, size_t *pcEncoded, size_t *pcbEncoded)
    882949{
    883950    AssertPtrReturn(pCodec->Ops.pfnEncode, VERR_NOT_SUPPORTED);
    884951
    885952    size_t cEncoded, cbEncoded;
    886     int vrc = pCodec->Ops.pfnEncode(pCodec, pFrame, pvDst, cbDst, &cEncoded, &cbEncoded);
     953    int vrc = pCodec->Ops.pfnEncode(pCodec, pFrame, &cEncoded, &cbEncoded);
    887954    if (RT_SUCCESS(vrc))
    888955    {
    889956#ifdef VBOX_WITH_STATISTICS
    890         pCodec->Stats.cEncBlocks += cEncoded;
    891         pCodec->Stats.msEncTotal += pCodec->Parms.msFrame * cEncoded;
     957        pCodec->STAM.cEncBlocks += cEncoded;
     958        pCodec->STAM.msEncTotal += pCodec->Parms.msFrame * cEncoded;
    892959#endif
    893960        if (pcEncoded)
  • trunk/src/VBox/Main/src-client/RecordingStream.cpp

    r96178 r96229  
    4242    int vrc2 = initInternal(a_pCtx, uScreen, Settings);
    4343    if (RT_FAILURE(vrc2))
    44     {
    45         LogRel(("Recording: Initialization failed with %Rrc\n", vrc2));
    4644        throw vrc2;
    47     }
    4845}
    4946
     
    5855 *
    5956 * @returns IPRT status code.
    60  */
    61 int RecordingStream::open(const settings::RecordingScreenSettings &Settings)
     57 * @param   screenSettings      Recording settings to use.
     58 */
     59int RecordingStream::open(const settings::RecordingScreenSettings &screenSettings)
    6260{
    6361    /* Sanity. */
    64     Assert(Settings.enmDest != RecordingDestination_None);
     62    Assert(screenSettings.enmDest != RecordingDestination_None);
    6563
    6664    int vrc;
    6765
    68     switch (Settings.enmDest)
     66    switch (screenSettings.enmDest)
    6967    {
    7068        case RecordingDestination_File:
    7169        {
    72             Assert(Settings.File.strName.isNotEmpty());
    73 
    74             char *pszAbsPath = RTPathAbsDup(Settings.File.strName.c_str());
     70            Assert(screenSettings.File.strName.isNotEmpty());
     71
     72            char *pszAbsPath = RTPathAbsDup(screenSettings.File.strName.c_str());
    7573            AssertPtrReturn(pszAbsPath, VERR_NO_MEMORY);
    7674
     
    8785            char *pszFile = NULL;
    8886
    89             if (this->uScreenID > 0)
    90                 vrc = RTStrAPrintf(&pszFile, "%s-%u%s", pszAbsPath, this->uScreenID + 1, pszSuff);
    91             else
    92                 vrc = RTStrAPrintf(&pszFile, "%s%s", pszAbsPath, pszSuff);
    93 
     87            vrc = RTStrAPrintf(&pszFile, "%s-%u%s", pszAbsPath, this->uScreenID, pszSuff);
    9488            if (RT_SUCCESS(vrc))
    9589            {
     90#ifdef DEBUG_andy
     91                uint64_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE;
     92#else
    9693                uint64_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_WRITE;
    9794
     
    10097                 * other important file, causing unintentional data loss. */
    10198                fOpen |= RTFILE_O_CREATE;
    102 
     99#endif
    103100                RTFILE hFile;
    104101                vrc = RTFileOpen(&hFile, pszFile, fOpen);
     
    113110                    RTTimeExplode(&time, &ts);
    114111
    115                     if (this->uScreenID > 0)
    116                         vrc = RTStrAPrintf(&pszFile, "%s-%04d-%02u-%02uT%02u-%02u-%02u-%09uZ-%u%s",
    117                                            pszAbsPath, time.i32Year, time.u8Month, time.u8MonthDay,
    118                                            time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond,
    119                                            this->uScreenID + 1, pszSuff);
    120                     else
    121                         vrc = RTStrAPrintf(&pszFile, "%s-%04d-%02u-%02uT%02u-%02u-%02u-%09uZ%s",
    122                                            pszAbsPath, time.i32Year, time.u8Month, time.u8MonthDay,
    123                                            time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond,
    124                                            pszSuff);
    125 
     112                    vrc = RTStrAPrintf(&pszFile, "%s-%04d-%02u-%02uT%02u-%02u-%02u-%09uZ-%u%s",
     113                                       pszAbsPath, time.i32Year, time.u8Month, time.u8MonthDay,
     114                                       time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond,
     115                                       this->uScreenID, pszSuff);
    126116                    if (RT_SUCCESS(vrc))
    127117                        vrc = RTFileOpen(&hFile, pszFile, fOpen);
    128118                }
    129119
     120                if (RT_FAILURE(vrc))
     121                    break;
     122
     123                LogRel2(("Recording: Opened file '%s'\n", pszFile));
     124
    130125                try
    131126                {
     
    134129                }
    135130                catch (std::bad_alloc &)
    136                {
     131                {
    137132                    vrc = VERR_NO_MEMORY;
    138133                }
     
    242237        vrc = VINF_SUCCESS;
    243238
    244     AssertPtr(this->pCtx);
     239    AssertPtr(this->m_pCtx);
    245240
    246241    switch (vrc)
     
    250245            this->fEnabled = false;
    251246
    252             int vrc2 = this->pCtx->OnLimitReached(this->uScreenID, VINF_SUCCESS /* rc */);
     247            int vrc2 = this->m_pCtx->OnLimitReached(this->uScreenID, VINF_SUCCESS /* rc */);
    253248            AssertRC(vrc2);
    254249            break;
     
    294289 * @returns IPRT status code.
    295290 * @param   mapBlocksCommon     Map of common block to process for this stream.
     291 *
     292 * @note    Runs in recording thread.
    296293 */
    297294int RecordingStream::Process(RecordingBlockMap &mapBlocksCommon)
     
    328325                Frame.msTimestamp = msTimestamp;
    329326
    330                 int vrc2 = recordingCodecEncode(&this->CodecVideo, &Frame, NULL, 0, NULL, NULL);
     327                int vrc2 = recordingCodecEncode(&this->CodecVideo, &Frame, NULL, NULL);
    331328                AssertRC(vrc2);
    332329                if (RT_SUCCESS(vrc))
     
    346343
    347344#ifdef VBOX_WITH_AUDIO_RECORDING
    348     AssertPtr(pCtx);
    349 
    350     /* As each (enabled) screen has to get the same audio data, look for common (audio) data which needs to be
    351      * written to the screen's assigned recording stream. */
    352     RecordingBlockMap::iterator itCommonBlocks = mapBlocksCommon.begin();
    353     while (itCommonBlocks != mapBlocksCommon.end())
    354     {
    355         RecordingBlockList::iterator itBlock = itCommonBlocks->second->List.begin();
    356         while (itBlock != itCommonBlocks->second->List.end())
    357         {
    358             RecordingBlock *pBlockCommon = (RecordingBlock *)(*itBlock);
    359             switch (pBlockCommon->enmType)
    360             {
    361                 case RECORDINGBLOCKTYPE_AUDIO:
    362                 {
    363                     PRECORDINGAUDIOFRAME pAudioFrame = (PRECORDINGAUDIOFRAME)pBlockCommon->pvData;
    364                     AssertPtr(pAudioFrame);
    365                     AssertPtr(pAudioFrame->pvBuf);
    366                     Assert(pAudioFrame->cbBuf);
    367 
    368                     WebMWriter::BlockData_Audio blockData = { pAudioFrame->pvBuf, pAudioFrame->cbBuf,
    369                                                               pBlockCommon->msTimestamp };
    370                     AssertPtr(this->File.pWEBM);
    371                     int vrc2 = this->File.pWEBM->WriteBlock(this->uTrackAudio, &blockData, sizeof(blockData));
    372                     AssertRC(vrc2);
    373                     if (RT_SUCCESS(vrc))
    374                         vrc = vrc2;
    375                     break;
    376                 }
    377 
    378                 default:
    379                     AssertFailed();
    380                     break;
    381             }
    382 
    383             Assert(pBlockCommon->cRefs);
    384             pBlockCommon->cRefs--;
    385             if (pBlockCommon->cRefs == 0)
    386             {
    387                 itCommonBlocks->second->List.erase(itBlock);
    388                 delete pBlockCommon;
    389                 itBlock = itCommonBlocks->second->List.begin();
     345    /* Do we need to multiplex the common audio data to this stream? */
     346    if (this->ScreenSettings.isFeatureEnabled(RecordingFeature_Audio))
     347    {
     348        /* As each (enabled) screen has to get the same audio data, look for common (audio) data which needs to be
     349         * written to the screen's assigned recording stream. */
     350        RecordingBlockMap::iterator itCommonBlocks = mapBlocksCommon.begin();
     351        while (itCommonBlocks != mapBlocksCommon.end())
     352        {
     353            RecordingBlockList::iterator itBlock = itCommonBlocks->second->List.begin();
     354            while (itBlock != itCommonBlocks->second->List.end())
     355            {
     356                RecordingBlock *pBlockCommon = (RecordingBlock *)(*itBlock);
     357                switch (pBlockCommon->enmType)
     358                {
     359                    case RECORDINGBLOCKTYPE_AUDIO:
     360                    {
     361                        PRECORDINGAUDIOFRAME pAudioFrame = (PRECORDINGAUDIOFRAME)pBlockCommon->pvData;
     362                        AssertPtr(pAudioFrame);
     363                        AssertPtr(pAudioFrame->pvBuf);
     364                        Assert(pAudioFrame->cbBuf);
     365
     366                        AssertPtr(this->File.pWEBM);
     367                        int vrc2 = this->File.pWEBM->WriteBlock(this->uTrackAudio, pAudioFrame->pvBuf, pAudioFrame->cbBuf, pBlockCommon->msTimestamp, pBlockCommon->uFlags);
     368                        AssertRC(vrc2);
     369                        if (RT_SUCCESS(vrc))
     370                            vrc = vrc2;
     371                        break;
     372                    }
     373
     374                    default:
     375                        AssertFailed();
     376                        break;
     377                }
     378
     379                Assert(pBlockCommon->cRefs);
     380                pBlockCommon->cRefs--;
     381                if (pBlockCommon->cRefs == 0)
     382                {
     383                    itCommonBlocks->second->List.erase(itBlock);
     384                    delete pBlockCommon;
     385                    itBlock = itCommonBlocks->second->List.begin();
     386                }
     387                else
     388                    ++itBlock;
     389            }
     390
     391            /* If no entries are left over in the block map, remove it altogether. */
     392            if (itCommonBlocks->second->List.empty())
     393            {
     394                delete itCommonBlocks->second;
     395                mapBlocksCommon.erase(itCommonBlocks);
     396                itCommonBlocks = mapBlocksCommon.begin();
    390397            }
    391398            else
    392                 ++itBlock;
    393         }
    394 
    395         /* If no entries are left over in the block map, remove it altogether. */
    396         if (itCommonBlocks->second->List.empty())
    397         {
    398             delete itCommonBlocks->second;
    399             mapBlocksCommon.erase(itCommonBlocks);
    400             itCommonBlocks = mapBlocksCommon.begin();
    401         }
    402         else
    403             ++itCommonBlocks;
    404 
    405         LogFunc(("Common blocks: %zu\n", mapBlocksCommon.size()));
     399                ++itCommonBlocks;
     400
     401            LogFunc(("Common blocks: %zu\n", mapBlocksCommon.size()));
     402        }
    406403    }
    407404#else
     
    429426 * @param   uSrcHeight          Height (in pixels) of the video frame.
    430427 * @param   puSrcData           Actual pixel data of the video frame.
    431  * @param   msTimestamp         Timestamp (in ms) as PTS.
     428 * @param   msTimestamp         Absolute PTS timestamp (in ms).
    432429 */
    433430int RecordingStream::SendVideoFrame(uint32_t x, uint32_t y, uint32_t uPixelFormat, uint32_t uBPP, uint32_t uBytesPerLine,
     
    436433    lock();
    437434
    438     LogFlowFunc(("msTimestamp=%RU64\n", msTimestamp));
     435    LogFlowFunc(("tsAbsPTSMs=%RU64\n", msTimestamp));
    439436
    440437    PRECORDINGCODEC pCodec = &this->CodecVideo;
     
    451448    do
    452449    {
    453         if (msTimestamp < pCodec->uLastTimeStampMs + pCodec->Parms.Video.uDelayMs)
     450        if (msTimestamp < pCodec->State.tsLastWrittenMs + pCodec->Parms.Video.uDelayMs)
    454451        {
    455452            vrc = VINF_RECORDING_THROTTLED; /* Respect maximum frames per second. */
     
    457454        }
    458455
    459         pCodec->uLastTimeStampMs = msTimestamp;
     456        pCodec->State.tsLastWrittenMs = msTimestamp;
    460457
    461458        int xDiff = ((int)this->ScreenSettings.Video.ulWidth - (int)uSrcWidth) / 2;
     
    657654 *
    658655 * @returns IPRT status code.
    659  * @param   a_pCtx              Pointer to recording context.
     656 * @param   pCtx                Pointer to recording context.
    660657 * @param   uScreen             Screen number to use for this recording stream.
    661658 * @param   Settings            Recording screen configuration to use for initialization.
    662659 */
    663 int RecordingStream::Init(RecordingContext *a_pCtx, uint32_t uScreen, const settings::RecordingScreenSettings &Settings)
    664 {
    665     return initInternal(a_pCtx, uScreen, Settings);
     660int RecordingStream::Init(RecordingContext *pCtx, uint32_t uScreen, const settings::RecordingScreenSettings &Settings)
     661{
     662    return initInternal(pCtx, uScreen, Settings);
    666663}
    667664
     
    670667 *
    671668 * @returns IPRT status code.
    672  * @param   a_pCtx              Pointer to recording context.
     669 * @param   pCtx                Pointer to recording context.
    673670 * @param   uScreen             Screen number to use for this recording stream.
    674  * @param   Settings            Recording screen configuration to use for initialization.
    675  */
    676 int RecordingStream::initInternal(RecordingContext *a_pCtx, uint32_t uScreen, const settings::RecordingScreenSettings &Settings)
     671 * @param   screenSettings      Recording screen configuration to use for initialization.
     672 */
     673int RecordingStream::initInternal(RecordingContext *pCtx, uint32_t uScreen,
     674                                  const settings::RecordingScreenSettings &screenSettings)
    677675{
    678676    AssertReturn(enmState == RECORDINGSTREAMSTATE_UNINITIALIZED, VERR_WRONG_ORDER);
    679677
    680     this->pCtx           = a_pCtx;
     678    this->m_pCtx         = pCtx;
    681679    this->uTrackAudio    = UINT8_MAX;
    682680    this->uTrackVideo    = UINT8_MAX;
    683681    this->tsStartMs      = 0;
    684682    this->uScreenID      = uScreen;
    685     this->ScreenSettings = Settings;
     683#ifdef VBOX_WITH_AUDIO_RECORDING
     684    /* We use the codec from the recording context, as this stream only receives multiplexed data (same audio for all streams). */
     685    this->pCodecAudio    = m_pCtx->GetCodecAudio();
     686#endif
     687    this->ScreenSettings = screenSettings;
    686688
    687689    settings::RecordingScreenSettings *pSettings = &this->ScreenSettings;
     
    704706    {
    705707        vrc = initVideo(*pSettings);
    706         if (RT_FAILURE(vrc))
    707             return vrc;
    708     }
    709 
    710     if (fAudioEnabled)
    711     {
    712         vrc = initAudio(*pSettings);
    713708        if (RT_FAILURE(vrc))
    714709            return vrc;
     
    755750            if (fAudioEnabled)
    756751            {
    757                 vrc = this->File.pWEBM->AddAudioTrack(&this->CodecAudio, pSettings->Audio.uHz, pSettings->Audio.cChannels, pSettings->Audio.cBits,
     752                AssertPtr(this->pCodecAudio);
     753                vrc = this->File.pWEBM->AddAudioTrack(this->pCodecAudio,
     754                                                      pSettings->Audio.uHz, pSettings->Audio.cChannels, pSettings->Audio.cBits,
    758755                                                      &this->uTrackAudio);
    759756                if (RT_FAILURE(vrc))
     
    803800        this->fEnabled  = true;
    804801        this->tsStartMs = RTTimeProgramMilliTS();
    805     }
    806     else
    807     {
    808         int vrc2 = uninitInternal();
    809         AssertRC(vrc2);
    810         return vrc;
    811     }
    812 
    813     return VINF_SUCCESS;
     802
     803        return VINF_SUCCESS;
     804    }
     805
     806    int vrc2 = uninitInternal();
     807    AssertRC(vrc2);
     808
     809    LogRel(("Recording: Stream #%RU32 initialization failed with %Rrc\n", uScreen, vrc));
     810    return vrc;
    814811}
    815812
     
    920917        return vrc;
    921918
     919#ifdef VBOX_WITH_AUDIO_RECORDING
     920    this->pCodecAudio = NULL;
     921#endif
     922
    922923    if (this->ScreenSettings.isFeatureEnabled(RecordingFeature_Video))
    923         vrc = recordingCodecDestroy(&this->CodecVideo);
     924    {
     925        vrc = recordingCodecFinalize(&this->CodecVideo);
     926        if (RT_SUCCESS(vrc))
     927            vrc = recordingCodecDestroy(&this->CodecVideo);
     928    }
    924929
    925930    if (RT_SUCCESS(vrc))
     
    934939}
    935940
     941/**
     942 * Writes encoded data to a WebM file instance.
     943 *
     944 * @returns VBox status code.
     945 * @param   pCodec              Codec which has encoded the data.
     946 * @param   pvData              Encoded data to write.
     947 * @param   cbData              Size (in bytes) of \a pvData.
     948 * @param   msAbsPTS            Absolute PTS (in ms) of written data.
     949 * @param   uFlags              Encoding flags of type RECORDINGCODEC_ENC_F_XXX.
     950 */
     951int RecordingStream::codecWriteToWebM(PRECORDINGCODEC pCodec, const void *pvData, size_t cbData,
     952                                      uint64_t msAbsPTS, uint32_t uFlags)
     953{
     954    AssertPtr(this->File.pWEBM);
     955    AssertPtr(pvData);
     956    Assert   (cbData);
     957
     958    WebMWriter::WebMBlockFlags blockFlags = VBOX_WEBM_BLOCK_FLAG_NONE;
     959    if (RT_LIKELY(uFlags != RECORDINGCODEC_ENC_F_NONE))
     960    {
     961        /* All set. */
     962    }
     963    else
     964    {
     965        if (uFlags & RECORDINGCODEC_ENC_F_BLOCK_IS_KEY)
     966            blockFlags |= VBOX_WEBM_BLOCK_FLAG_KEY_FRAME;
     967        if (uFlags & RECORDINGCODEC_ENC_F_BLOCK_IS_INVISIBLE)
     968            blockFlags |= VBOX_WEBM_BLOCK_FLAG_INVISIBLE;
     969    }
     970
     971    return this->File.pWEBM->WriteBlock(  pCodec->Parms.enmType == RECORDINGCODECTYPE_AUDIO
     972                                        ? this->uTrackAudio : this->uTrackVideo,
     973                                        pvData, cbData, msAbsPTS, blockFlags);
     974}
     975
     976/**
     977 * Codec callback for writing encoded data to a recording stream.
     978 *
     979 * @returns VBox status code.
     980 * @param   pCodec              Codec which has encoded the data.
     981 * @param   pvData              Encoded data to write.
     982 * @param   cbData              Size (in bytes) of \a pvData.
     983 * @param   msAbsPTS            Absolute PTS (in ms) of written data.
     984 * @param   uFlags              Encoding flags of type RECORDINGCODEC_ENC_F_XXX.
     985 */
    936986/* static */
    937 DECLCALLBACK(int) RecordingStream::codecWriteDataCallback(PRECORDINGCODEC pCodec, const void *pvData, size_t cbData, void *pvUser)
    938 {
    939     RT_NOREF(pCodec);
    940 
     987DECLCALLBACK(int) RecordingStream::codecWriteDataCallback(PRECORDINGCODEC pCodec, const void *pvData, size_t cbData,
     988                                                          uint64_t msAbsPTS, uint32_t uFlags, void *pvUser)
     989{
    941990    RecordingStream *pThis = (RecordingStream *)pvUser;
    942991    AssertPtr(pThis);
    943992
    944     AssertPtr(pThis->File.pWEBM);
    945     return pThis->File.pWEBM->WriteBlock(pCodec->Parms.enmType == RECORDINGCODECTYPE_AUDIO
    946                                          ? pThis->uTrackAudio : pThis->uTrackVideo, pvData, cbData);
     993    /** @todo For now this is hardcoded to always write to a WebM file. Add other stuff later. */
     994    return pThis->codecWriteToWebM(pCodec, pvData, cbData, msAbsPTS, uFlags);
    947995}
    948996
     
    951999 *
    9521000 * @returns VBox status code.
    953  * @param   Settings            Settings to use.
    954  */
    955 int RecordingStream::initVideo(const settings::RecordingScreenSettings &Settings)
     1001 * @param   screenSettings      Screen settings to use.
     1002 */
     1003int RecordingStream::initVideo(const settings::RecordingScreenSettings &screenSettings)
    9561004{
    9571005    /* Sanity. */
    958     AssertReturn(Settings.Video.ulRate,   VERR_INVALID_PARAMETER);
    959     AssertReturn(Settings.Video.ulWidth,  VERR_INVALID_PARAMETER);
    960     AssertReturn(Settings.Video.ulHeight, VERR_INVALID_PARAMETER);
    961     AssertReturn(Settings.Video.ulFPS,    VERR_INVALID_PARAMETER);
     1006    AssertReturn(screenSettings.Video.ulRate,   VERR_INVALID_PARAMETER);
     1007    AssertReturn(screenSettings.Video.ulWidth,  VERR_INVALID_PARAMETER);
     1008    AssertReturn(screenSettings.Video.ulHeight, VERR_INVALID_PARAMETER);
     1009    AssertReturn(screenSettings.Video.ulFPS,    VERR_INVALID_PARAMETER);
    9621010
    9631011    PRECORDINGCODEC pCodec = &this->CodecVideo;
     
    9671015    Callbacks.pfnWriteData = RecordingStream::codecWriteDataCallback;
    9681016
    969     int vrc = recordingCodecCreateVideo(pCodec, Settings.Video.enmCodec);
     1017    int vrc = recordingCodecCreateVideo(pCodec, screenSettings.Video.enmCodec);
    9701018    if (RT_SUCCESS(vrc))
    971         vrc = recordingCodecInit(pCodec, &Callbacks, Settings);
     1019        vrc = recordingCodecInit(pCodec, &Callbacks, screenSettings);
    9721020
    9731021    if (RT_FAILURE(vrc))
     
    9781026
    9791027/**
    980  * Initializes the audio part of a recording stream,
    981  *
    982  * @returns VBox status code.
    983  * @retval  VERR_NOT_SUPPORTED if the selected codec is not supported / available.
    984  * @param   Settings            Settings to use.
    985  */
    986 int RecordingStream::initAudio(const settings::RecordingScreenSettings &Settings)
    987 {
    988     if (Settings.Audio.enmCodec == RecordingAudioCodec_None)
    989         return VINF_SUCCESS;
    990 
    991     if (Settings.isFeatureEnabled(RecordingFeature_Audio))
    992     {
    993         /* Sanity. */
    994         AssertReturn(Settings.Audio.uHz,       VERR_INVALID_PARAMETER);
    995         AssertReturn(Settings.Audio.cBits,     VERR_INVALID_PARAMETER);
    996         AssertReturn(Settings.Audio.cChannels, VERR_INVALID_PARAMETER);
    997     }
    998 
    999     PRECORDINGCODEC pCodec = &this->CodecAudio;
    1000 
    1001     RECORDINGCODECCALLBACKS Callbacks;
    1002     Callbacks.pvUser       = this;
    1003     Callbacks.pfnWriteData = RecordingStream::codecWriteDataCallback;
    1004 
    1005     int vrc = recordingCodecCreateAudio(pCodec, Settings.Audio.enmCodec);
    1006     if (RT_SUCCESS(vrc))
    1007         vrc = recordingCodecInit(pCodec, &Callbacks, Settings);
    1008 
    1009     if (RT_FAILURE(vrc))
    1010         LogRel(("Recording: Initializing audio codec failed with %Rrc\n", vrc));
    1011 
    1012     return vrc;
    1013 }
    1014 
    1015 /**
    10161028 * Locks a recording stream.
    10171029 */
  • trunk/src/VBox/Main/src-client/WebMWriter.cpp

    r96175 r96229  
    566566}
    567567
    568 #ifdef VBOX_WITH_LIBVPX
    569 /**
    570  * Writes VPX (VP8 video) simple data block.
    571  *
    572  * @returns IPRT status code.
    573  * @param   a_pTrack        Track ID to write data to.
    574  * @param   a_pCfg          VPX encoder configuration to use.
    575  * @param   a_pPkt          VPX packet video data packet to write.
    576  */
    577 int WebMWriter::writeSimpleBlockVP8(WebMTrack *a_pTrack, const vpx_codec_enc_cfg_t *a_pCfg, const vpx_codec_cx_pkt_t *a_pPkt)
    578 {
    579     RT_NOREF(a_pTrack);
    580 
    581     /* Calculate the absolute PTS of this frame (in ms). */
    582     WebMTimecodeAbs tcAbsPTSMs =   a_pPkt->data.frame.pts * 1000
    583                                  * (uint64_t) a_pCfg->g_timebase.num / a_pCfg->g_timebase.den;
    584     if (   tcAbsPTSMs
    585         && tcAbsPTSMs <= a_pTrack->tcAbsLastWrittenMs)
    586     {
    587         AssertFailed(); /* Should never happen. */
    588         tcAbsPTSMs = a_pTrack->tcAbsLastWrittenMs + 1;
    589     }
    590 
    591     const bool fKeyframe = RT_BOOL(a_pPkt->data.frame.flags & VPX_FRAME_IS_KEY);
    592 
    593     uint8_t fFlags = VBOX_WEBM_BLOCK_FLAG_NONE;
    594     if (fKeyframe)
    595         fFlags |= VBOX_WEBM_BLOCK_FLAG_KEY_FRAME;
    596     if (a_pPkt->data.frame.flags & VPX_FRAME_IS_INVISIBLE)
    597         fFlags |= VBOX_WEBM_BLOCK_FLAG_INVISIBLE;
    598 
    599     return writeSimpleBlockQueued(a_pTrack,
    600                                   new WebMSimpleBlock(a_pTrack,
    601                                                       tcAbsPTSMs, a_pPkt->data.frame.buf, a_pPkt->data.frame.sz, fFlags));
    602 }
    603 #endif /* VBOX_WITH_LIBVPX */
    604 
    605 /**
    606  * Writes an Opus (audio) simple data block.
    607  *
    608  * @returns IPRT status code.
    609  * @param   pTrack          Track ID to write data to.
    610  * @param   pvData          Pointer to simple data block to write.
    611  * @param   cbData          Size (in bytes) of simple data block to write.
    612  * @param   tcAbsPTSMs      Absolute PTS of simple data block.
    613  *
    614  * @remarks Audio blocks that have same absolute timecode as video blocks SHOULD be written before the video blocks.
    615  */
    616 int WebMWriter::writeSimpleBlockAudio(WebMTrack *pTrack, const void *pvData, size_t cbData, WebMTimecodeAbs tcAbsPTSMs)
    617 {
    618     AssertPtrReturn(pTrack, VERR_INVALID_POINTER);
    619     AssertPtrReturn(pvData, VERR_INVALID_POINTER);
    620     AssertReturn(cbData,    VERR_INVALID_PARAMETER);
    621 
    622     /* Every Opus frame is a key frame. */
    623     const uint8_t fFlags = VBOX_WEBM_BLOCK_FLAG_KEY_FRAME;
    624 
    625     return writeSimpleBlockQueued(pTrack,
    626                                   new WebMSimpleBlock(pTrack, tcAbsPTSMs, pvData, cbData, fFlags));
    627 }
    628 
    629568/**
    630569 * Writes a data block to the specified track.
     
    634573 * @param   pvData          Pointer to data block to write.
    635574 * @param   cbData          Size (in bytes) of data block to write.
    636  */
    637 int WebMWriter::WriteBlock(uint8_t uTrack, const void *pvData, size_t cbData)
    638 {
    639     RT_NOREF(cbData); /* Only needed for assertions for now. */
    640 
     575 * @param   tcAbsPTSMs      Absolute PTS of simple data block.
     576 * @param   uFlags          WebM block flags to use for this block.
     577 */
     578int WebMWriter::WriteBlock(uint8_t uTrack, const void *pvData, size_t cbData, WebMTimecodeAbs tcAbsPTSMs, WebMBlockFlags uFlags)
     579{
    641580    int vrc = RTCritSectEnter(&CurSeg.CritSect);
    642581    AssertRC(vrc);
    643582
     583#ifdef DEBUG /* Only validate in debug builds, to speed things up in release builds. */
    644584    WebMTracks::iterator itTrack = CurSeg.mapTracks.find(uTrack);
    645585    if (itTrack == CurSeg.mapTracks.end())
     
    648588        return VERR_NOT_FOUND;
    649589    }
     590#endif
    650591
    651592    WebMTrack *pTrack = itTrack->second;
     
    658599    }
    659600
    660     switch (pTrack->enmType)
    661     {
    662 
    663         case WebMTrackType_Audio:
    664         {
    665             if (   m_enmAudioCodec == RecordingAudioCodec_Opus
    666                 || m_enmAudioCodec == RecordingAudioCodec_OggVorbis)
    667             {
    668                 Assert(cbData == sizeof(WebMWriter::BlockData_Audio));
    669                 WebMWriter::BlockData_Audio *pData = (WebMWriter::BlockData_Audio *)pvData;
    670                 vrc = writeSimpleBlockAudio(pTrack, pData->pvData, pData->cbData, pData->uPTSMs);
    671             }
    672             /* else nothing to do here; WebM only supports Opus or Ogg Vorbis. */
    673             break;
    674         }
    675 
    676         case WebMTrackType_Video:
    677         {
    678 #ifdef VBOX_WITH_LIBVPX
    679             if (m_enmVideoCodec == RecordingVideoCodec_VP8)
    680             {
    681                 Assert(cbData == sizeof(WebMWriter::BlockData_VP8));
    682                 WebMWriter::BlockData_VP8 *pData = (WebMWriter::BlockData_VP8 *)pvData;
    683                 vrc = writeSimpleBlockVP8(pTrack, pData->pCfg, pData->pPkt);
    684             }
    685             else
    686 #endif /* VBOX_WITH_LIBVPX */
    687                 vrc = VERR_NOT_SUPPORTED;
    688             break;
    689         }
    690 
    691         default:
    692             vrc = VERR_NOT_SUPPORTED;
    693             break;
     601    try
     602    {
     603        vrc = writeSimpleBlockQueued(pTrack,
     604                                     new WebMSimpleBlock(pTrack,
     605                                                         tcAbsPTSMs, pvData, cbData, uFlags));
     606    }
     607    catch (std::bad_alloc &)
     608    {
     609        vrc = VERR_NO_MEMORY;
    694610    }
    695611
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