VirtualBox

Changeset 65256 in vbox for trunk/src/VBox


Ignore:
Timestamp:
Jan 12, 2017 11:11:03 AM (8 years ago)
Author:
vboxsync
Message:

VideoRec: More infrastructure work on making the WebM writer as much as possible independent from the actual codec(s) being used. It also now has the ability to add tracks, making it more flexible in usage (e.g. if an additional audio track is required). Work in progress.

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

Legend:

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

    r65212 r65256  
    3333#include <iprt/mem.h>
    3434#include <iprt/cdefs.h>
    35 #include <iprt/circbuf.h>
    3635
    3736#include <VBox/vmm/pdmaudioifs.h>
     
    9089    uint64_t             old_ticks;
    9190    uint64_t             cSamplesSentPerSec;
    92     /** Codec data.
     91    /** Codec-specific data.
    9392     *  As every stream can be different, one codec per stream is needed. */
    9493    AVRECCODEC           Codec;
     94    /** (Audio) frame buffer. */
     95    uint8_t             *pvFrameBuf;
     96    size_t               cbFrameBufSize;
     97    size_t               cbFrameBufUsed;
     98    size_t               offFrameBufRead;
     99    size_t               offFrameBufWrite;
    95100} AVRECSTREAMOUT, *PAVRECSTREAMOUT;
    96101
     
    133138    if (RT_SUCCESS(rc))
    134139    {
    135         OpusEncoder *pEnc = NULL;
    136 
    137         int orc;
    138         pEnc = opus_encoder_create(48000 /* Hz */, 2 /* Stereo */, OPUS_APPLICATION_AUDIO, &orc);
    139         if (orc != OPUS_OK)
     140        size_t cbFrameBuf = sizeof(uint8_t) * _8K; /** @todo Make this configurable */
     141
     142        pStreamOut->pvFrameBuf = (uint8_t *)RTMemAlloc(cbFrameBuf);
     143        if (pStreamOut->pvFrameBuf)
    140144        {
    141             LogRel(("VideoRec: Audio codec failed to initialize: %s\n", opus_strerror(orc)));
    142             return VERR_AUDIO_BACKEND_INIT_FAILED;
    143         }
    144 
    145         AssertPtr(pEnc);
    146 
    147         orc = opus_encoder_ctl(pEnc, OPUS_SET_BITRATE(pCfgReq->uHz));
    148         if (orc != OPUS_OK)
    149         {
    150             LogRel(("VideoRec: Audio codec failed to set bitrate: %s\n", opus_strerror(orc)));
    151             rc = VERR_AUDIO_BACKEND_INIT_FAILED;
    152         }
    153         else
    154         {
    155             pStreamOut->Codec.Opus.pEnc = pEnc;
    156 
    157             if (pCfgAcq)
    158                 pCfgAcq->cSampleBufferSize = _4K; /** @todo Make this configurable. */
     145            pStreamOut->cbFrameBufSize   = cbFrameBuf;
     146            pStreamOut->cbFrameBufUsed   = 0;
     147            pStreamOut->offFrameBufRead  = 0;
     148            pStreamOut->offFrameBufWrite = 0;
     149
     150            OpusEncoder *pEnc = NULL;
     151
     152            int orc;
     153            pEnc = opus_encoder_create(48000 /* Hz */, 2 /* Stereo */, OPUS_APPLICATION_AUDIO, &orc);
     154            if (orc != OPUS_OK)
     155            {
     156                LogRel(("VideoRec: Audio codec failed to initialize: %s\n", opus_strerror(orc)));
     157                return VERR_AUDIO_BACKEND_INIT_FAILED;
     158            }
     159
     160            AssertPtr(pEnc);
     161
     162    #if 0
     163            orc = opus_encoder_ctl(pEnc, OPUS_SET_BITRATE(DrvAudioHlpCalcBitrate()));
     164            if (orc != OPUS_OK)
     165            {
     166                LogRel(("VideoRec: Audio codec failed to set bitrate: %s\n", opus_strerror(orc)));
     167                rc = VERR_AUDIO_BACKEND_INIT_FAILED;
     168            }
     169            else
     170            {
     171    #endif
     172                pStreamOut->Codec.Opus.pEnc = pEnc;
     173
     174                if (pCfgAcq)
     175                    pCfgAcq->cSampleBufferSize = _4K; /** @todo Make this configurable. */
     176    //      }
    159177        }
    160178    }
     
    205223            {
    206224                pThis->pEBML = new WebMWriter();
    207                 rc = pThis->pEBML->create("/tmp/test.webm", RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE,
    208                                           WebMWriter::Mode_Audio, /** @todo Fix path! */
     225                rc = pThis->pEBML->Create("/tmp/acap.webm", RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE, /** @todo Fix path! */
    209226                                          WebMWriter::AudioCodec_Opus, WebMWriter::VideoCodec_None);
     227                if (RT_SUCCESS(rc))
     228                    rc = pThis->pEBML->AddAudioTrack(44100.0, 44100, 2, 16);
    210229                break;
    211230            }
     
    257276 */
    258277static DECLCALLBACK(int) drvAudioVideoRecStreamPlay(PPDMIHOSTAUDIO pInterface,
    259                                                     PPDMAUDIOSTREAM pStream, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
     278                                                    PPDMAUDIOSTREAM pStream, const void *pvBuf, uint32_t cbBuf,
     279                                                    uint32_t *pcbWritten)
    260280{
    261281    RT_NOREF2(pvBuf, cbBuf);
     
    291311
    292312    /*
    293      * Call the encoder server with the data.
     313     * Call the encoder with the data.
    294314     */
    295     uint32_t cReadTotal = 0;
    296 
    297     uint8_t abDst[_4K];
    298     opus_int32 cbDst = (opus_int32)sizeof(abDst);
    299 
    300     PPDMAUDIOSAMPLE pSamples;
    301     uint32_t cRead;
    302     int rc = AudioMixBufAcquire(&pStream->MixBuf, cSamplesToSend,
    303                                 &pSamples, &cRead);
    304     if (   RT_SUCCESS(rc)
    305         && cRead)
    306     {
    307         cReadTotal = cRead;
    308 
    309         opus_int32 orc = opus_encode(pStreamOut->Codec.Opus.pEnc, (opus_int16 *)pSamples, cRead, abDst, cbDst);
    310         if (orc != OPUS_OK)
    311             LogFunc(("Encoding (1) failed: %s\n", opus_strerror(orc)));
    312 
    313         if (rc == VINF_TRY_AGAIN)
     315
     316    /** @todo For now we ASSUME 25 FPS. */
     317    uint16_t csFrameSize = /*pStreamOut->Props.uHz*/ 48000 / 25;
     318    size_t   cbFrameSize = AUDIOMIXBUF_S2B(&pStream->MixBuf, csFrameSize);
     319
     320    uint32_t csRead = 0;
     321    int rc = AudioMixBufReadCirc(&pStream->MixBuf, (uint8_t *)&pStreamOut->pvFrameBuf[pStreamOut->offFrameBufWrite],
     322                                 pStreamOut->cbFrameBufSize - pStreamOut->offFrameBufWrite, &csRead);
     323    if (RT_SUCCESS(rc))
     324    {
     325        const uint32_t cbRead = AUDIOMIXBUF_S2B(&pStream->MixBuf, csRead);
     326
     327        pStreamOut->offFrameBufWrite =
     328            (pStreamOut->offFrameBufWrite + cbRead) % pStreamOut->cbFrameBufSize;
     329        Assert(pStreamOut->offFrameBufWrite <= pStreamOut->cbFrameBufSize);
     330        pStreamOut->cbFrameBufUsed  += cbRead;
     331        Assert(pStreamOut->cbFrameBufUsed  <= pStreamOut->cbFrameBufSize);
     332
     333        if (pStreamOut->cbFrameBufUsed >= cbFrameSize)
    314334        {
    315             rc = AudioMixBufAcquire(&pStream->MixBuf, cSamplesToSend - cRead,
    316                                     &pSamples, &cRead);
    317 
    318 
    319 
    320             cReadTotal += cRead;
     335            /* Opus always encodes PER FRAME, that is, 2.5, 5, 10, 20, 40 or 60 ms of audio data. */
     336            uint8_t abDst[_4K];
     337            size_t  cbDst = sizeof(abDst);
     338
     339            /* Call the encoder to encode our input samples. */
     340            opus_int32 cbWritten = opus_encode(pStreamOut->Codec.Opus.pEnc,
     341                                               (opus_int16 *)&pStreamOut->pvFrameBuf[pStreamOut->offFrameBufRead],
     342                                               csFrameSize, abDst, cbDst);
     343            if (cbWritten > 0)
     344            {
     345                pStreamOut->offFrameBufRead = (pStreamOut->offFrameBufRead + cbWritten) % pStreamOut->cbFrameBufSize;
     346                Assert(pStreamOut->cbFrameBufUsed >= cbFrameSize);
     347                pStreamOut->cbFrameBufUsed -= cbFrameSize;
     348
     349                /* Call the WebM writer to actually write the encoded audio data. */
     350                WebMWriter::BlockData_Opus blockData = { abDst, cbDst };
     351                rc = pThis->pEBML->WriteBlock(WebMWriter::BlockType_Audio, &blockData, sizeof(blockData));
     352                AssertRC(rc);
     353            }
     354            else if (cbWritten < 0)
     355                AssertMsgFailed(("Encoding failed: %s\n", opus_strerror(cbWritten)));
    321356        }
    322357    }
    323358
    324     AudioMixBufFinish(&pStream->MixBuf, cSamplesToSend);
     359    if (csRead)
     360        AudioMixBufFinish(&pStream->MixBuf, csRead);
    325361
    326362    /*
     
    329365     */
    330366    if (pcbWritten)
    331         *pcbWritten = cReadTotal;
    332 
    333     LogFlowFunc(("cReadTotal=%RU32, rc=%Rrc\n", cReadTotal, rc));
     367        *pcbWritten = csRead;
     368
     369    LogFlowFunc(("csRead=%RU32, rc=%Rrc\n", csRead, rc));
    334370    return rc;
    335371}
     
    341377    RT_NOREF(pThis);
    342378    PAVRECSTREAMOUT   pStreamOut = (PAVRECSTREAMOUT)pStream;
     379
     380    if (pStreamOut->pvFrameBuf)
     381    {
     382        Assert(pStreamOut->cbFrameBufSize);
     383        RTMemFree(pStreamOut->pvFrameBuf);
     384        pStreamOut->pvFrameBuf = NULL;
     385    }
    343386
    344387    if (pStreamOut->Codec.Opus.pEnc)
     
    386429            if (pThis->pEBML)
    387430            {
    388                 rc = pThis->pEBML->writeFooter(0 /* Hash */);
    389                 AssertRC(rc);
    390 
    391                 pThis->pEBML->close();
     431                pThis->pEBML->Close();
    392432
    393433                delete pThis->pEBML;
  • trunk/src/VBox/Main/src-client/EbmlWriter.cpp

    r65212 r65256  
    1919#include <list>
    2020#include <stack>
    21 #include <iprt/string.h>
    22 #include <iprt/file.h>
    2321#include <iprt/asm.h>
    2422#include <iprt/cdefs.h>
    2523#include <iprt/err.h>
     24#include <iprt/file.h>
     25#include <iprt/rand.h>
     26#include <iprt/string.h>
     27
    2628#include <VBox/log.h>
     29
    2730#include "EbmlWriter.h"
    2831#include "EbmlIDs.h"
     32#include "Logging.h"
    2933
    3034
     
    4852public:
    4953
     54    Ebml(void)
     55        : m_File(NIL_RTFILE) { }
     56
     57    virtual ~Ebml(void) { close(); }
     58
     59public:
     60
    5061    /** Creates EBML output file. */
    5162    inline int create(const char *a_pszFilename, uint64_t fOpen)
     
    5566
    5667    /** Returns file size. */
    57     inline uint64_t getFileSize()
     68    inline uint64_t getFileSize(void)
    5869    {
    5970        return RTFileTell(m_File);
     
    6172
    6273    /** Get reference to file descriptor */
    63     inline const RTFILE &getFile()
     74    inline const RTFILE &getFile(void)
    6475    {
    6576        return m_File;
     
    6778
    6879    /** Returns available space on storage. */
    69     inline uint64_t getAvailableSpace()
     80    inline uint64_t getAvailableSpace(void)
    7081    {
    7182        RTFOFF pcbFree;
     
    7586
    7687    /** Closes the file. */
    77     inline void close()
    78     {
     88    inline void close(void)
     89    {
     90        if (!isOpen())
     91            return;
     92
     93        AssertMsg(m_Elements.size() == 0,
     94                  ("%zu elements are not closed yet (next element to close is 0x%x)\n",
     95                   m_Elements.size(), m_Elements.top().classId));
     96
    7997        RTFileClose(m_File);
     98        m_File = NIL_RTFILE;
     99    }
     100
     101    /**
     102     * Returns whether the file is open or not.
     103     *
     104     * @returns True if open, false if not.
     105     */
     106    inline bool isOpen(void)
     107    {
     108        return RTFileIsValid(m_File);
    80109    }
    81110
     
    99128         * to the function. Otherwise it may mean that we have a bug in the code.
    100129         */
    101         if(m_Elements.empty() || m_Elements.top().classId != classId) throw VERR_INTERNAL_ERROR;
     130        AssertMsg(!m_Elements.empty(), ("No elements to close anymore\n"));
     131        AssertMsg(m_Elements.top().classId == classId,
     132                  ("Ending sub element 0x%x is in wrong order (next to close is 0x%x)\n", classId, m_Elements.top().classId));
    102133
    103134        uint64_t uPos = RTFileTell(m_File);
     
    156187
    157188    /** Writes raw data to file. */
    158     inline void write(const void *data, size_t size)
    159     {
    160         int rc = RTFileWrite(m_File, data, size, NULL);
    161         if (!RT_SUCCESS(rc)) throw rc;
     189    inline int write(const void *data, size_t size)
     190    {
     191        return RTFileWrite(m_File, data, size, NULL);
    162192    }
    163193
     
    229259class WebMWriter_Impl
    230260{
    231     struct CueEntry
    232     {
     261    /**
     262     * Structure for keeping a cue entry.
     263     */
     264    struct WebMCueEntry
     265    {
     266        WebMCueEntry(uint32_t t, uint64_t l)
     267            : time(t), loc(l) {}
     268
    233269        uint32_t    time;
    234270        uint64_t    loc;
    235         CueEntry(uint32_t t, uint64_t l) : time(t), loc(l) {}
     271    };
     272
     273    /**
     274     * Track type enumeration.
     275     */
     276    enum WebMTrackType
     277    {
     278        /** Unknown / invalid type. */
     279        WebMTrackType_Invalid     = 0,
     280        /** Only writes audio. */
     281        WebMTrackType_Audio       = 1,
     282        /** Only writes video. */
     283        WebMTrackType_Video       = 2
     284    };
     285
     286    /**
     287     * Structure for keeping a track entry.
     288     */
     289    struct WebMTrack
     290    {
     291        WebMTrack(WebMTrackType a_enmType, uint64_t a_offID)
     292            : enmType(a_enmType)
     293            , offID(a_offID)
     294        {
     295            uID = RTRandU32();
     296        }
     297
     298        /** The type of this track. */
     299        WebMTrackType enmType;
     300        /** The track's UUID. */
     301        uint32_t      uID;
     302        /** Absolute offset in file of track ID.
     303         *  Needed to write the hash sum within the footer. */
     304        uint64_t      offID;
    236305    };
    237306
     
    243312    struct OpusPrivData
    244313    {
    245         uint8_t  au8Head[8]       = { 0x4f, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64 };
    246         uint8_t  u8Version        = 1;
    247         uint8_t  c8Channels;
    248         uint16_t u16PreSkip       = 0;
    249         uint32_t u32SampleRate;
    250         uint16_t u16Gain          = 0;
    251         uint8_t  u8Mapping_family = 0;
     314        uint8_t  au8Head[8]      = { 0x4f, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64 };
     315        uint8_t  u8Version       = 1;
     316        uint8_t  c8Channels      = 0;
     317        uint16_t u16PreSkip      = 0;
     318        uint32_t u32SampleRate   = 0;
     319        uint16_t u16Gain         = 0;
     320        uint8_t  u8MappingFamily = 0;
    252321    };
    253322# pragma pack(pop)
    254323#endif /* VBOX_WITH_AUDIO_VIDEOREC */
    255324
    256     /** Operation mode. */
    257     WebMWriter::Mode       m_enmMode;
    258325    /** Audio codec to use. */
    259     WebMWriter::AudioCodec m_enmAudioCodec;
     326    WebMWriter::AudioCodec      m_enmAudioCodec;
    260327    /** Video codec to use. */
    261     WebMWriter::VideoCodec m_enmVideoCodec;
    262 
    263     bool                   m_fDebug;
    264 
    265     /** Timestamp of initial PTS (Presentation Time Stamp). */
    266     int64_t                m_tsInitialPtsMs;
    267     /** Timestamp of last written PTS (Presentation Time Stamp). */
    268     int64_t                m_tsLastPtsMs;
    269 
    270     vpx_rational_t         m_Framerate;
     328    WebMWriter::VideoCodec      m_enmVideoCodec;
     329
     330    /** This PTS (Presentation Time Stamp) acts as our master clock for synchronizing streams. */
     331    uint64_t                    m_uPts;
     332    /** Timestamp (in ms) of initial PTS. */
     333    int64_t                     m_tsInitialPtsMs;
     334    /** Timestamp (in ms) of last written PTS. */
     335    int64_t                     m_tsLastPtsMs;
    271336
    272337    /** Start offset (in bytes) of current segment. */
    273     uint64_t               m_offSegCurStart;
     338    uint64_t                    m_offSegCurStart;
    274339
    275340    /** Start offset (in bytes) of seeking info segment. */
    276     uint64_t               m_offSegSeekInfoStart;
    277     /** Offset (in bytes) for current seek info element. */
    278     uint64_t               m_offSeekInfo;
     341    uint64_t                    m_offSegSeekInfoStart;
     342    /** Start offset (in bytes) of general info segment. */
     343    uint64_t                    m_offSegInfoStart;
    279344
    280345    /** Start offset (in bytes) of tracks segment. */
    281     uint64_t               m_offSegTracksStart;
     346    uint64_t                    m_offSegTracksStart;
    282347
    283348    /** Absolute position of cue segment. */
    284     uint64_t               m_uCuePos;
     349    uint64_t                    m_offSegCuesStart;
    285350    /** List of cue points. Needed for seeking table. */
    286     std::list<CueEntry>    m_lstCue;
    287 
    288     uint64_t               m_uTrackIdPos;
     351    std::list<WebMCueEntry>     m_lstCue;
     352
     353    /** List of tracks. */
     354    std::list<WebMTrack>        m_lstTracks;
     355    bool                        m_fTracksOpen;
    289356
    290357    /** Timestamp (in ms) when the current cluster has been opened. */
    291     uint32_t               m_tsClusterOpenMs;
     358    uint32_t                    m_tsClusterOpenMs;
    292359    /** Whether we're currently in an opened cluster segment. */
    293     bool                   m_fClusterOpen;
     360    bool                        m_fClusterOpen;
    294361    /** Absolute position (in bytes) of current cluster within file.
    295362     *  Needed for seeking info table. */
    296     uint64_t               m_offSegClusterStart;
    297 
    298     Ebml                   m_Ebml;
     363    uint64_t                    m_offSegClusterStart;
     364
     365    Ebml                        m_Ebml;
    299366
    300367public:
    301368
    302369    WebMWriter_Impl() :
    303           m_enmMode(WebMWriter::Mode_Unknown)
    304         , m_fDebug(false)
     370          m_uPts(0)
    305371        , m_tsInitialPtsMs(-1)
    306372        , m_tsLastPtsMs(-1)
    307         , m_Framerate()
    308373        , m_offSegCurStart(0)
    309374        , m_offSegSeekInfoStart(0)
    310         , m_offSeekInfo(0)
     375        , m_offSegInfoStart(0)
    311376        , m_offSegTracksStart(0)
    312         , m_uCuePos(0)
    313         , m_uTrackIdPos(0)
     377        , m_offSegCuesStart(0)
     378        , m_fTracksOpen(false)
    314379        , m_tsClusterOpenMs(0)
    315380        , m_fClusterOpen(false)
    316381        , m_offSegClusterStart(0) {}
    317382
    318     void writeHeader(const vpx_codec_enc_cfg_t *a_pCfg, const struct vpx_rational *a_pFps)
    319     {
     383    int AddAudioTrack(float fSamplingHz, float fOutputHz, uint8_t cChannels, uint8_t cBitDepth)
     384    {
     385        m_Ebml.subStart(TrackEntry);
     386        m_Ebml.serializeUnsignedInteger(TrackNumber, (uint8_t)m_lstTracks.size());
     387        /** @todo Implement track's "Language" property? Currently this defaults to English ("eng"). */
     388
     389        WebMTrack TrackAudio(WebMTrackType_Audio, RTFileTell(m_Ebml.getFile()));
     390        m_lstTracks.push_back(TrackAudio);
     391
     392        /** @todo Resolve codec type. */
     393        OpusPrivData opusPrivData;
     394
     395        m_Ebml.serializeUnsignedInteger(TrackUID, TrackAudio.uID, 4)
     396              .serializeUnsignedInteger(TrackType, 2 /* Audio */)
     397              .serializeString(CodecID, "A_OPUS")
     398              .serializeData(CodecPrivate, &opusPrivData, sizeof(opusPrivData))
     399              .subStart(Audio)
     400              .serializeFloat(SamplingFrequency, fSamplingHz)
     401              .serializeFloat(OutputSamplingFrequency, fOutputHz)
     402              .serializeUnsignedInteger(Channels, cChannels)
     403              .serializeUnsignedInteger(BitDepth, cBitDepth)
     404              .subEnd(Audio)
     405              .subEnd(TrackEntry);
     406
     407        return VINF_SUCCESS;
     408    }
     409
     410    int AddVideoTrack(uint16_t uWidth, uint16_t uHeight, double dbFPS)
     411    {
     412        m_Ebml.subStart(TrackEntry);
     413        m_Ebml.serializeUnsignedInteger(TrackNumber, (uint8_t)m_lstTracks.size());
     414
     415        WebMTrack TrackVideo(WebMTrackType_Video, RTFileTell(m_Ebml.getFile()));
     416        m_lstTracks.push_back(TrackVideo);
     417
     418        /** @todo Resolve codec type. */
     419
     420        m_Ebml.serializeUnsignedInteger(TrackUID, TrackVideo.uID /* UID */, 4)
     421              .serializeUnsignedInteger(TrackType, 1 /* Video */)
     422              .serializeString(CodecID, "V_VP8")
     423              .subStart(Video)
     424              .serializeUnsignedInteger(PixelWidth, uWidth)
     425              .serializeUnsignedInteger(PixelHeight, uHeight)
     426              .serializeFloat(FrameRate, dbFPS)
     427              .subEnd(Video)
     428              .subEnd(TrackEntry);
     429
     430        return VINF_SUCCESS;
     431    }
     432
     433    int writeHeader(void)
     434    {
     435        LogFunc(("Header @ %RU64\n", RTFileTell(m_Ebml.getFile())));
     436
    320437        m_Ebml.subStart(EBML)
    321438              .serializeUnsignedInteger(EBMLVersion, 1)
     
    330447        m_Ebml.subStart(Segment);
    331448
    332         m_Framerate = *a_pFps;
    333 
    334449        /* Save offset of current segment. */
    335450        m_offSegCurStart = RTFileTell(m_Ebml.getFile());
     
    340455        m_offSegTracksStart = RTFileTell(m_Ebml.getFile());
    341456
     457        /* The tracks segment starts right after this header. */
    342458        m_Ebml.subStart(Tracks);
    343 
    344         /* Write video? */
    345         if (   m_enmMode == WebMWriter::Mode_Video
    346             || m_enmMode == WebMWriter::Mode_AudioVideo)
    347         {
    348             /*
    349              * Video track.
    350              */
    351             m_Ebml.subStart(TrackEntry);
    352             m_Ebml.serializeUnsignedInteger(TrackNumber, 1);
    353 
    354             m_uTrackIdPos = RTFileTell(m_Ebml.getFile());
    355 
    356             m_Ebml.serializeUnsignedInteger(TrackUID, 0 /* UID */, 4)
    357                   .serializeUnsignedInteger(TrackType, 1 /* Video */)
    358                   .serializeString(CodecID, "V_VP8")
    359                   .subStart(Video)
    360                   .serializeUnsignedInteger(PixelWidth, a_pCfg->g_w)
    361                   .serializeUnsignedInteger(PixelHeight, a_pCfg->g_h)
    362                   .serializeFloat(FrameRate, (double) a_pFps->num / a_pFps->den)
    363                   .subEnd(Video)
    364                   .subEnd(TrackEntry);
    365         }
    366 
    367 #ifdef VBOX_WITH_AUDIO_VIDEOREC
    368         if (   m_enmMode == WebMWriter::Mode_Audio
    369             || m_enmMode == WebMWriter::Mode_AudioVideo)
    370         {
    371             /*
    372              * Audio track.
    373              */
    374             m_Ebml.subStart(TrackEntry);
    375             m_Ebml.serializeUnsignedInteger(TrackNumber, 2);
    376             /** @todo Implement track's "Language" property? Currently this defaults to English ("eng"). */
    377 
    378             OpusPrivData opusPrivData;
    379 
    380             m_Ebml.serializeUnsignedInteger(TrackUID, 1 /* UID */, 4)
    381                   .serializeUnsignedInteger(TrackType, 2 /* Audio */)
    382                   .serializeString(CodecID, "A_OPUS")
    383                   .serializeData(CodecPrivate, &opusPrivData, sizeof(opusPrivData))
    384                   .subStart(Audio)
    385                   .serializeFloat(SamplingFrequency, 44100.0)
    386                   .serializeFloat(OutputSamplingFrequency, 44100.0)
    387                   .serializeUnsignedInteger(Channels, 2)
    388                   .serializeUnsignedInteger(BitDepth, 16)
    389                   .subEnd(Audio)
    390                   .subEnd(TrackEntry);
    391         }
    392 #endif
    393 
    394         m_Ebml.subEnd(Tracks);
    395     }
    396 
    397     void writeBlock(const vpx_codec_enc_cfg_t *a_pCfg, const vpx_codec_cx_pkt_t *a_pPkt)
     459        m_fTracksOpen = true;
     460
     461        return VINF_SUCCESS;
     462    }
     463
     464    int writeSimpleBlockInternal(uint8_t uTrackID, uint16_t uTimecode, const void *pvData, size_t cbData, uint8_t fFlags)
     465    {
     466        LogFunc(("SimpleBlock @ %RU64 (T%RU8, TS=%RU16, %zu bytes)\n", RTFileTell(m_Ebml.getFile()), uTrackID, uTimecode, cbData));
     467
     468        /* Write a "Simple Block". */
     469        m_Ebml.writeClassId(SimpleBlock);
     470        /* Block size. */
     471        m_Ebml.writeUnsignedInteger(0x10000000u | (1      /* Track number. */
     472                                    + 2      /* Timecode .*/
     473                                    + 1      /* Flags. */
     474                                    + cbData /* Actual frame data. */),  4);
     475        /* Track number. */
     476        m_Ebml.writeSize(uTrackID);
     477        /* Timecode (relative to cluster opening timecode). */
     478        m_Ebml.writeUnsignedInteger(uTimecode, 2);
     479        /* Flags. */
     480        m_Ebml.writeUnsignedInteger(fFlags, 1);
     481        /* Frame data. */
     482        m_Ebml.write(pvData, cbData);
     483
     484        return VINF_SUCCESS;
     485    }
     486
     487    int writeBlockVP8(const vpx_codec_enc_cfg_t *a_pCfg, const vpx_codec_cx_pkt_t *a_pPkt)
    398488    {
    399489        /* Calculate the PTS of this frame in milliseconds. */
    400         int64_t iPtsMs = a_pPkt->data.frame.pts * 1000
    401                        * (uint64_t) a_pCfg->g_timebase.num / a_pCfg->g_timebase.den;
    402 
    403         if (iPtsMs <= m_tsLastPtsMs)
    404             iPtsMs = m_tsLastPtsMs + 1;
    405 
    406         m_tsLastPtsMs = iPtsMs;
     490        int64_t tsPtsMs = a_pPkt->data.frame.pts * 1000
     491                        * (uint64_t) a_pCfg->g_timebase.num / a_pCfg->g_timebase.den;
     492
     493        if (tsPtsMs <= m_tsLastPtsMs)
     494            tsPtsMs = m_tsLastPtsMs + 1;
     495
     496        m_tsLastPtsMs = tsPtsMs;
    407497
    408498        if (m_tsInitialPtsMs < 0)
    409           m_tsInitialPtsMs = m_tsLastPtsMs;
     499            m_tsInitialPtsMs = m_tsLastPtsMs;
    410500
    411501        /* Calculate the relative time of this block. */
    412         uint16_t uBlockTimecode = 0;
    413         bool     fClusterStart  = false;
    414 
    415         if (iPtsMs - m_tsClusterOpenMs > 65536)
     502        uint16_t tsBlockMs    = 0;
     503        bool     fClusterStart = false;
     504
     505        if (tsPtsMs - m_tsClusterOpenMs > 65536)
    416506            fClusterStart = true;
    417507        else
    418             uBlockTimecode = static_cast<uint16_t>(iPtsMs - m_tsClusterOpenMs);
    419 
    420         bool fKeyframe = RT_BOOL(a_pPkt->data.frame.flags & VPX_FRAME_IS_KEY);
     508        {
     509            /* Calculate the block's timecode, which is relative to the Cluster timecode. */
     510            tsBlockMs = static_cast<uint16_t>(tsPtsMs - m_tsClusterOpenMs);
     511        }
     512
     513        const bool fKeyframe = RT_BOOL(a_pPkt->data.frame.flags & VPX_FRAME_IS_KEY);
    421514
    422515        if (   fClusterStart
    423516            || fKeyframe)
    424517        {
    425             if (m_fClusterOpen)
     518            if (m_fClusterOpen) /* Close current cluster first. */
    426519                m_Ebml.subEnd(Cluster);
    427520
     521            tsBlockMs = 0;
     522
    428523            /* Open a new cluster. */
    429             uBlockTimecode = 0;
    430             m_fClusterOpen = true;
    431             m_tsClusterOpenMs = (uint32_t)iPtsMs;
     524            m_fClusterOpen       = true;
     525            m_tsClusterOpenMs    = (uint32_t)tsPtsMs;
    432526            m_offSegClusterStart = RTFileTell(m_Ebml.getFile());
     527
     528            LogFunc(("Cluster @ %RU64\n", m_offSegClusterStart));
     529
    433530            m_Ebml.subStart(Cluster)
    434531                  .serializeUnsignedInteger(Timecode, m_tsClusterOpenMs);
     
    437534            if (fKeyframe)
    438535            {
    439                 CueEntry cue(m_tsClusterOpenMs, m_offSegClusterStart);
     536                WebMCueEntry cue(m_tsClusterOpenMs, m_offSegClusterStart);
    440537                m_lstCue.push_back(cue);
    441538            }
    442539        }
    443540
    444         /* Write a "Simple Block". */
    445         m_Ebml.writeClassId(SimpleBlock);
    446         m_Ebml.writeUnsignedInteger(0x10000000u | (4 + a_pPkt->data.frame.sz), 4);
    447         m_Ebml.writeSize(1);
    448         m_Ebml.writeUnsignedInteger(uBlockTimecode, 2);
    449         m_Ebml.writeUnsignedInteger((fKeyframe ? 0x80 : 0) | (a_pPkt->data.frame.flags & VPX_FRAME_IS_INVISIBLE ? 0x08 : 0), 1);
    450         m_Ebml.write(a_pPkt->data.frame.buf, a_pPkt->data.frame.sz);
    451     }
    452 
    453     void writeFooter(uint32_t a_u64Hash)
    454     {
     541        uint8_t fFlags = 0;
     542        if (fKeyframe)
     543            fFlags |= 0x80; /* Key frame. */
     544        if (a_pPkt->data.frame.flags & VPX_FRAME_IS_INVISIBLE)
     545            fFlags |= 0x08; /* Invisible frame. */
     546
     547        return writeSimpleBlockInternal(0 /** @todo FIX! */, tsBlockMs, a_pPkt->data.frame.buf, a_pPkt->data.frame.sz, fFlags);
     548    }
     549
     550#ifdef VBOX_WITH_AUDIO_VIDEOREC
     551    /* Audio blocks that have same absolute timecode as video blocks SHOULD be written before the video blocks. */
     552    int writeBlockOpus(const void *pvData, size_t cbData)
     553    {
     554static uint16_t s_uTimecode = 0;
     555
     556        return writeSimpleBlockInternal(1 /** @todo FIX! */, s_uTimecode++, pvData, cbData, 0 /* Flags */);
     557    }
     558#endif
     559
     560    int WriteBlock(WebMWriter::BlockType blockType, const void *pvData, size_t cbData)
     561    {
     562        int rc;
     563
     564        if (m_fTracksOpen)
     565        {
     566            m_Ebml.subEnd(Tracks);
     567            m_fTracksOpen = false;
     568        }
     569
     570        switch (blockType)
     571        {
     572#ifdef VBOX_WITH_AUDIO_VIDEOREC
     573            case WebMWriter::BlockType_Audio:
     574            {
     575                if (m_enmAudioCodec == WebMWriter::AudioCodec_Opus)
     576                {
     577                    Assert(cbData == sizeof(WebMWriter::BlockData_Opus));
     578                    WebMWriter::BlockData_Opus *pData = (WebMWriter::BlockData_Opus *)pvData;
     579                    rc = writeBlockOpus(pData->pvData, pData->cbData);
     580                }
     581                else
     582                    rc = VERR_NOT_SUPPORTED;
     583                break;
     584            }
     585#endif
     586            case WebMWriter::BlockType_Video:
     587            {
     588                if (m_enmVideoCodec == WebMWriter::VideoCodec_VP8)
     589                {
     590                    Assert(cbData == sizeof(WebMWriter::BlockData_VP8));
     591                    WebMWriter::BlockData_VP8 *pData = (WebMWriter::BlockData_VP8 *)pvData;
     592                    rc = writeBlockVP8(pData->pCfg, pData->pPkt);
     593                }
     594                else
     595                    rc = VERR_NOT_SUPPORTED;
     596                break;
     597            }
     598
     599            default:
     600                rc = VERR_NOT_SUPPORTED;
     601                break;
     602        }
     603
     604        return rc;
     605    }
     606
     607    int writeFooter(void)
     608    {
     609        if (m_fTracksOpen)
     610        {
     611            m_Ebml.subEnd(Tracks);
     612            m_fTracksOpen = false;
     613        }
     614
    455615        if (m_fClusterOpen)
     616        {
    456617            m_Ebml.subEnd(Cluster);
    457 
    458         m_uCuePos = RTFileTell(m_Ebml.getFile());
     618            m_fClusterOpen = false;
     619        }
     620
     621        /*
     622         * Write Cues segment.
     623         */
     624        LogFunc(("Cues @ %RU64\n", RTFileTell(m_Ebml.getFile())));
     625
     626        m_offSegCuesStart = RTFileTell(m_Ebml.getFile());
     627
    459628        m_Ebml.subStart(Cues);
    460         for (std::list<CueEntry>::iterator it = m_lstCue.begin(); it != m_lstCue.end(); ++it)
     629
     630        for (std::list<WebMCueEntry>::iterator it = m_lstCue.begin(); it != m_lstCue.end(); ++it)
    461631        {
    462           m_Ebml.subStart(CuePoint)
    463                 .serializeUnsignedInteger(CueTime, it->time)
    464                 .subStart(CueTrackPositions)
    465                 .serializeUnsignedInteger(CueTrack, 1)
    466                 .serializeUnsignedInteger(CueClusterPosition, it->loc - m_offSegCurStart, 8)
    467                 .subEnd(CueTrackPositions)
    468                 .subEnd(CuePoint);
     632            m_Ebml.subStart(CuePoint)
     633                  .serializeUnsignedInteger(CueTime, it->time)
     634                  .subStart(CueTrackPositions)
     635                  .serializeUnsignedInteger(CueTrack, 1)
     636                  .serializeUnsignedInteger(CueClusterPosition, it->loc - m_offSegCurStart, 8)
     637                  .subEnd(CueTrackPositions)
     638                  .subEnd(CuePoint);
    469639        }
    470640
     
    472642              .subEnd(Segment);
    473643
     644        /*
     645         * Update SeekHead / Info segment.
     646         */
     647
    474648        writeSeekInfo();
    475649
    476         int rc = RTFileSeek(m_Ebml.getFile(), m_uTrackIdPos, RTFILE_SEEK_BEGIN, NULL);
    477         if (!RT_SUCCESS(rc)) throw rc;
    478 
    479         m_Ebml.serializeUnsignedInteger(TrackUID, (m_fDebug ? 0xDEADBEEF : a_u64Hash), 4);
    480 
    481         rc = RTFileSeek(m_Ebml.getFile(), 0, RTFILE_SEEK_END, NULL);
    482         if (!RT_SUCCESS(rc)) throw rc;
    483     }
    484 
     650        return RTFileSeek(m_Ebml.getFile(), 0, RTFILE_SEEK_END, NULL);
     651    }
     652
     653    friend class Ebml;
    485654    friend class WebMWriter;
    486655
     
    489658    void writeSeekInfo(void)
    490659    {
    491         uint64_t uPos = RTFileTell(m_Ebml.getFile());
    492660        if (m_offSegSeekInfoStart)
    493661            RTFileSeek(m_Ebml.getFile(), m_offSegSeekInfoStart, RTFILE_SEEK_BEGIN, NULL);
    494662        else
    495             m_offSegSeekInfoStart = uPos;
     663            m_offSegSeekInfoStart = RTFileTell(m_Ebml.getFile());
     664
     665        LogFunc(("SeekHead @ %RU64\n", m_offSegSeekInfoStart));
    496666
    497667        m_Ebml.subStart(SeekHead)
     
    504674              .subStart(Seek)
    505675              .serializeUnsignedInteger(SeekID, Cues)
    506               .serializeUnsignedInteger(SeekPosition, m_uCuePos - m_offSegCurStart, 8)
     676              .serializeUnsignedInteger(SeekPosition, m_offSegCuesStart - m_offSegCurStart, 8)
    507677              .subEnd(Seek)
    508678
    509679              .subStart(Seek)
    510680              .serializeUnsignedInteger(SeekID, Info)
    511               .serializeUnsignedInteger(SeekPosition, m_offSeekInfo - m_offSegCurStart, 8)
     681              .serializeUnsignedInteger(SeekPosition, m_offSegInfoStart - m_offSegCurStart, 8)
    512682              .subEnd(Seek)
    513683
    514684        .subEnd(SeekHead);
    515685
    516         int64_t iFrameTime = (int64_t)1000 * m_Framerate.den / m_Framerate.num;
    517         m_offSeekInfo = RTFileTell(m_Ebml.getFile());
     686        int64_t iFrameTime = (int64_t)1000 * 1 / 25; //m_Framerate.den / m_Framerate.num; /** @todo Fix this! */
     687        m_offSegInfoStart = RTFileTell(m_Ebml.getFile());
     688
     689        LogFunc(("Info @ %RU64\n", m_offSegInfoStart));
    518690
    519691        char szVersion[64];
    520         RTStrPrintf(szVersion, sizeof(szVersion), "vpxenc%s",
    521                     m_fDebug ? "" : vpx_codec_version_str());
     692        RTStrPrintf(szVersion, sizeof(szVersion), "vpxenc%s", vpx_codec_version_str()); /** @todo Make this configurable? */
    522693
    523694        m_Ebml.subStart(Info)
     
    535706{
    536707    if (m_pImpl)
     708    {
     709        Close();
    537710        delete m_pImpl;
     711    }
    538712}
    539713
    540 int WebMWriter::create(const char *a_pszFilename, uint64_t a_fOpen, WebMWriter::Mode a_enmMode,
     714int WebMWriter::Create(const char *a_pszFilename, uint64_t a_fOpen,
    541715                       WebMWriter::AudioCodec a_enmAudioCodec, WebMWriter::VideoCodec a_enmVideoCodec)
    542716{
    543     m_pImpl->m_enmMode       = a_enmMode;
    544     m_pImpl->m_enmAudioCodec = a_enmAudioCodec;
    545     m_pImpl->m_enmVideoCodec = a_enmVideoCodec;
    546 
    547     return m_pImpl->m_Ebml.create(a_pszFilename, a_fOpen);
    548 }
    549 
    550 void WebMWriter::close(void)
    551 {
    552     m_pImpl->m_Ebml.close();
    553 }
    554 
    555 int WebMWriter::writeHeader(const vpx_codec_enc_cfg_t *a_pCfg, const vpx_rational *a_pFps)
    556 {
    557717    try
    558718    {
    559         m_pImpl->writeHeader(a_pCfg, a_pFps);
     719        m_pImpl->m_enmAudioCodec = a_enmAudioCodec;
     720        m_pImpl->m_enmVideoCodec = a_enmVideoCodec;
     721
     722        LogFunc(("Creating '%s'\n", a_pszFilename));
     723
     724        int rc = m_pImpl->m_Ebml.create(a_pszFilename, a_fOpen);
     725        if (RT_SUCCESS(rc))
     726            rc = m_pImpl->writeHeader();
    560727    }
    561728    catch(int rc)
     
    566733}
    567734
    568 int WebMWriter::writeBlock(const vpx_codec_enc_cfg_t *a_pCfg, const vpx_codec_cx_pkt_t *a_pPkt)
    569 {
     735int WebMWriter::Close(void)
     736{
     737    if (!m_pImpl->m_Ebml.isOpen())
     738        return VINF_SUCCESS;
     739
     740    int rc = m_pImpl->writeFooter();
     741
     742    /* Close the file in any case. */
     743    m_pImpl->m_Ebml.close();
     744
     745    return rc;
     746}
     747
     748int WebMWriter::AddAudioTrack(float fSamplingHz, float fOutputHz, uint8_t cChannels, uint8_t cBitDepth)
     749{
     750    return m_pImpl->AddAudioTrack(fSamplingHz, fOutputHz, cChannels, cBitDepth);
     751}
     752
     753int WebMWriter::AddVideoTrack(uint16_t uWidth, uint16_t uHeight, double dbFPS)
     754{
     755    return m_pImpl->AddVideoTrack(uWidth, uHeight, dbFPS);
     756}
     757
     758int WebMWriter::WriteBlock(WebMWriter::BlockType blockType, const void *pvData, size_t cbData)
     759{
     760    int rc;
     761
    570762    try
    571763    {
    572         m_pImpl->writeBlock(a_pCfg, a_pPkt);
    573     }
    574     catch(int rc)
    575     {
    576         return rc;
    577     }
    578     return VINF_SUCCESS;
     764        rc = m_pImpl->WriteBlock(blockType, pvData, cbData);
     765    }
     766    catch(int rc2)
     767    {
     768        rc = rc2;
     769    }
     770    return rc;
    579771}
    580772
    581 int WebMWriter::writeFooter(uint32_t a_u64Hash)
    582 {
    583     try
    584     {
    585         m_pImpl->writeFooter(a_u64Hash);
    586     }
    587     catch(int rc)
    588     {
    589         return rc;
    590     }
    591     return VINF_SUCCESS;
    592 }
    593 
    594 uint64_t WebMWriter::getFileSize()
     773uint64_t WebMWriter::GetFileSize(void)
    595774{
    596775    return m_pImpl->m_Ebml.getFileSize();
    597776}
    598777
    599 uint64_t WebMWriter::getAvailableSpace()
     778uint64_t WebMWriter::GetAvailableSpace(void)
    600779{
    601780    return m_pImpl->m_Ebml.getAvailableSpace();
  • trunk/src/VBox/Main/src-client/EbmlWriter.h

    r65212 r65256  
    5959    };
    6060
    61     struct BlockData
     61    /**
     62     * Block data for VP8-encoded video data.
     63     */
     64    struct BlockData_VP8
    6265    {
    63         void  *pvData;
    64         size_t cbData;
     66        const vpx_codec_enc_cfg_t *pCfg;
     67        const vpx_codec_cx_pkt_t  *pPkt;
    6568    };
    6669
    6770    /**
    68      * Operation mode -- this specifies what to write.
     71     * Block data for Opus-encoded audio data.
    6972     */
    70     enum Mode
     73    struct BlockData_Opus
    7174    {
    72         /** Unknown / invalid mode. */
    73         Mode_Unknown     = 0,
    74         /** Only writes audio. */
    75         Mode_Audio       = 1,
    76         /** Only writes video. */
    77         Mode_Video       = 2,
    78         /** Writes audio and video. */
    79         Mode_AudioVideo  = 3
     75        /** Pointer to encoded Opus audio data. */
     76        const void *pvData;
     77        /** Size (in bytes) of encoded Opus audio data. */
     78        size_t      cbData;
     79    };
     80
     81    /**
     82     * Block type to write.
     83     */
     84    enum BlockType
     85    {
     86        BlockType_Invalid = 0,
     87        BlockType_Audio   = 1,
     88        BlockType_Video   = 2,
     89        BlockType_Raw     = 3
    8090    };
    8191
     
    90100     * @param   a_pszFilename   Name of the file to create.
    91101     * @param   a_fOpen         File open mode of type RTFILE_O_.
    92      * @param   a_enmMode       Operation mode.
    93102     * @param   a_enmAudioCodec Audio codec to use.
    94103     * @param   a_enmVideoCodec Video codec to use.
    95104     *
    96105     * @returns VBox status code. */
    97     int create(const char *a_pszFilename, uint64_t a_fOpen, WebMWriter::Mode a_enmMode,
     106    int Create(const char *a_pszFilename, uint64_t a_fOpen,
    98107               WebMWriter::AudioCodec a_enmAudioCodec, WebMWriter::VideoCodec a_enmVideoCodec);
    99108
    100109    /** Closes output file. */
    101     void close();
     110    int Close(void);
    102111
    103     /**
    104      * Writes WebM header to file.
    105      * Should be called before any writeBlock call.
    106      *
    107      * @param a_pCfg Pointer to VPX Codec configuration structure.
    108      * @param a_pFps Framerate information (frames per second).
    109      *
    110      * @returns VBox status code.
    111      */
    112     int writeHeader(const vpx_codec_enc_cfg_t *a_pCfg, const vpx_rational *a_pFps);
     112    int AddAudioTrack(float fSamplingHz, float fOutputHz, uint8_t cChannels, uint8_t cBitDepth);
     113
     114    int AddVideoTrack(uint16_t uWidth, uint16_t uHeight, double dbFPS);
    113115
    114116    /**
    115117     * Writes a block of compressed data.
    116118     *
    117      * @param a_pCfg Pointer to VPX Codec configuration structure.
    118      * @param a_pPkt VPX data packet.
     119     * @param blockType         Block type to write.
     120     * @param pvData            Pointer to block data to write.
     121     * @param cbData            Size (in bytes) of block data to write.
    119122     *
    120123     * @returns VBox status code.
    121124     */
    122     int writeBlock(const vpx_codec_enc_cfg_t *a_pCfg, const vpx_codec_cx_pkt_t *a_pPkt);
    123 
    124     /**
    125      * Writes WebM footer.
    126      * No other write functions should be called after this one.
    127      *
    128      * @param a_u64Hash Hash value for the data written.
    129      *
    130      * @returns VBox status code.
    131      */
    132     int writeFooter(uint32_t a_u64Hash);
     125    int WriteBlock(WebMWriter::BlockType blockType, const void *pvData, size_t cbData);
    133126
    134127    /**
     
    137130     * @returns File size in bytes.
    138131     */
    139     uint64_t getFileSize(void);
     132    uint64_t GetFileSize(void);
    140133
    141134    /**
     
    144137     * @returns Available storage free space.
    145138     */
    146     uint64_t getAvailableSpace(void);
     139    uint64_t GetAvailableSpace(void);
    147140
    148141private:
  • trunk/src/VBox/Main/src-client/VideoRec.cpp

    r65224 r65256  
    578578        {
    579579            AssertPtr(pStream->pEBML);
    580             int rc = pStream->pEBML->writeFooter(0);
    581             AssertRC(rc);
    582 
    583             pStream->pEBML->close();
     580            pStream->pEBML->Close();
    584581
    585582            vpx_img_free(&pStream->Codec.VPX.RawImage);
     
    659656    size_t pos = 0;
    660657
    661     /* By default we enable both, video and audio recording (if available). */
     658    /* By default we enable everything (if available). */
     659    bool fHasVideoTrack = true;
    662660#ifdef VBOX_WITH_AUDIO_VIDEOREC
    663     WebMWriter::Mode enmMode = WebMWriter::Mode_AudioVideo;
    664 #else
    665     WebMWriter::Mode enmMode = WebMWriter::Mode_Video;
     661    bool fHasAudioTrack = true;
    666662#endif
    667663
    668     do {
    669 
    670         com::Utf8Str key, value;
    671         pos = options.parseKeyValue(key, value, pos);
    672 
     664    com::Utf8Str key, value;
     665    while ((pos = options.parseKeyValue(key, value, pos)) != com::Utf8Str::npos)
     666    {
    673667        if (key.compare("vc_quality", Utf8Str::CaseInsensitive) == 0)
    674668        {
     
    687681            else
    688682            {
    689                 LogRel(("VideoRec: Settings quality deadline to '%s'\n", value.c_str()));
     683                LogRel(("VideoRec: Setting quality deadline to '%s'\n", value.c_str()));
    690684                pStream->uEncoderDeadline = value.toUInt32();
    691685            }
    692686        }
    693         if (key.compare("vc_enabled", Utf8Str::CaseInsensitive) == 0)
     687        else if (key.compare("vc_enabled", Utf8Str::CaseInsensitive) == 0)
    694688        {
    695689#ifdef VBOX_WITH_AUDIO_VIDEOREC
    696690            if (value.compare("false", Utf8Str::CaseInsensitive) == 0) /* Disable audio. */
    697691            {
    698                 enmMode = WebMWriter::Mode_Audio;
     692                fHasVideoTrack = false;
    699693                LogRel(("VideoRec: Only audio will be recorded\n"));
    700694            }
    701695#endif
    702696        }
    703         if (key.compare("ac_enabled", Utf8Str::CaseInsensitive) == 0)
     697        else if (key.compare("ac_enabled", Utf8Str::CaseInsensitive) == 0)
    704698        {
    705699#ifdef VBOX_WITH_AUDIO_VIDEOREC
    706700            if (value.compare("false", Utf8Str::CaseInsensitive)) /* Disable audio. */
    707701            {
    708                 enmMode = WebMWriter::Mode_Video;
     702                fHasAudioTrack = false;
    709703                LogRel(("VideoRec: Only video will be recorded\n"));
    710704            }
     
    714708            LogRel(("VideoRec: Unknown option '%s' (value '%s'), skipping\n", key.c_str(), value.c_str()));
    715709
    716     } while(pos != com::Utf8Str::npos);
     710    } /* while */
    717711
    718712    uint64_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_WRITE;
     
    723717#endif
    724718
    725     int rc = pStream->pEBML->create(pszFile, fOpen, enmMode,
    726                                     WebMWriter::AudioCodec_Opus, WebMWriter::VideoCodec_VP8);
     719    int rc = pStream->pEBML->Create(pszFile, fOpen, WebMWriter::AudioCodec_Opus, WebMWriter::VideoCodec_VP8);
    727720    if (RT_FAILURE(rc))
    728721    {
     
    745738    pStream->uDelay = 1000 / uFps;
    746739
    747     struct vpx_rational arg_framerate = { (int)uFps, 1 };
    748     rc = pStream->pEBML->writeHeader(&pStream->Codec.VPX.Config, &arg_framerate);
    749     AssertRCReturn(rc, rc);
     740    if (fHasVideoTrack)
     741    {
     742        rc = pStream->pEBML->AddVideoTrack(pStream->Codec.VPX.Config.g_w, /* Width */
     743                                           pStream->Codec.VPX.Config.g_h, /* Height */
     744                                           uFps);
     745        if (RT_FAILURE(rc))
     746        {
     747            LogRel(("VideoRec: Failed to add video track to output file '%s' (%Rrc)\n", pszFile, rc));
     748            return rc;
     749        }
     750    }
    750751
    751752    /* Initialize codec */
     
    762763        return VERR_NO_MEMORY;
    763764    }
     765
    764766    pStream->pu8YuvBuf = pStream->Codec.VPX.RawImage.planes[0];
    765767
    766768    pCtx->fEnabled = true;
    767769    pStream->fEnabled = true;
     770
    768771    return VINF_SUCCESS;
    769772}
     
    832835    if (pCtx->uMaxFileSize > 0)
    833836    {
    834         uint64_t sizeInMB = pStream->pEBML->getFileSize() / (1024 * 1024);
     837        uint64_t sizeInMB = pStream->pEBML->GetFileSize() / (1024 * 1024);
    835838        if(sizeInMB >= pCtx->uMaxFileSize)
    836839            return true;
    837840    }
    838841    /* Check for available free disk space */
    839     if (pStream->pEBML->getAvailableSpace() < 0x100000)
     842    if (pStream->pEBML->GetAvailableSpace() < 0x100000)
    840843    {
    841844        LogRel(("VideoRec: Not enough free storage space available, stopping video capture\n"));
     
    873876    for (;;)
    874877    {
    875         const vpx_codec_cx_pkt_t *pkt = vpx_codec_get_cx_data(&pStream->Codec.VPX.CodecCtx, &iter);
    876         if (!pkt)
    877             break;
    878         switch (pkt->kind)
     878        const vpx_codec_cx_pkt_t *pPacket = vpx_codec_get_cx_data(&pStream->Codec.VPX.CodecCtx, &iter);
     879        if (!pPacket)
     880            break;
     881
     882        switch (pPacket->kind)
    879883        {
    880884            case VPX_CODEC_CX_FRAME_PKT:
    881                 rc = pStream->pEBML->writeBlock(&pStream->Codec.VPX.Config, pkt);
     885            {
     886                WebMWriter::BlockData_VP8 blockData { &pStream->Codec.VPX.Config, pPacket };
     887                rc = pStream->pEBML->WriteBlock(WebMWriter::BlockType_Video, &blockData, sizeof(blockData));
    882888                break;
     889            }
     890
    883891            default:
    884                 LogFlow(("Unexpected CODEC Packet.\n"));
     892                LogFlow(("Unexpected CODEC packet kind %ld\n", pPacket->kind));
    885893                break;
    886894        }
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