VirtualBox

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


Ignore:
Timestamp:
Jan 17, 2017 4:58:01 PM (8 years ago)
Author:
vboxsync
Message:

VideoRec: Update.

Location:
trunk/src/VBox/Main/src-client
Files:
1 added
1 deleted
2 edited

Legend:

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

    r65330 r65353  
    9797    /** (Audio) frame buffer. */
    9898    PRTCIRCBUF           pCircBuf;
    99     uint8_t             *pvFrameBuf;
    100     size_t               cbFrameBufSize;
    101     size_t               cbFrameBufUsed;
    102     size_t               offFrameBufRead;
    103     size_t               offFrameBufWrite;
     99    /** Pointer to WebM container to write recorded audio data to.
     100     *  See the AVRECMODE enumeration for more information. */
     101    WebMWriter          *pWebM;
     102    /** Assigned track number from WebM container. */
     103    uint8_t              uTrack;
    104104} AVRECSTREAMOUT, *PAVRECSTREAMOUT;
    105105
     
    119119    /** Recording mode. */
    120120    AVRECMODE            enmMode;
    121     /** Pointer to WebM container to write recorded audio data to.
    122      *  See the AVRECMODE enumeration for more information. */
    123     WebMWriter          *pEBML;
    124     /** Track number for audio data. */
    125     uint8_t              uTrack;
    126121} DRVAUDIOVIDEOREC, *PDRVAUDIOVIDEOREC;
    127122
     
    144139
    145140#ifdef VBOX_WITH_LIBOPUS
    146     RTCircBufCreate(&pStreamOut->pCircBuf, _4K);
     141    PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface);
     142
     143    if (pThis->enmMode == AVRECMODE_AUDIO)
     144    {
     145        pStreamOut->pWebM = new WebMWriter();
     146        rc = pStreamOut->pWebM->Create("/tmp/acap.webm", RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE, /** @todo Fix path! */
     147                                       WebMWriter::AudioCodec_Opus, WebMWriter::VideoCodec_None);
     148        if (RT_SUCCESS(rc))
     149            rc = pStreamOut->pWebM->AddAudioTrack(44100, 2, 16, &pStreamOut->uTrack);
     150    }
     151
     152    if (RT_FAILURE(rc))
     153        return rc;
     154
     155    rc = RTCircBufCreate(&pStreamOut->pCircBuf, _4K);
     156    if (RT_FAILURE(rc))
     157        return rc;
    147158
    148159    rc = DrvAudioHlpStreamCfgToProps(pCfgReq, &pStreamOut->Props);
    149160    if (RT_SUCCESS(rc))
    150161    {
    151         size_t cbFrameBuf = sizeof(uint8_t) * _4K; /** @todo Make this configurable */
    152 
    153         pStreamOut->pvFrameBuf = (uint8_t *)RTMemAlloc(cbFrameBuf);
    154         if (pStreamOut->pvFrameBuf)
    155         {
    156             pStreamOut->cbFrameBufSize   = cbFrameBuf;
    157             pStreamOut->cbFrameBufUsed   = 0;
    158             pStreamOut->offFrameBufRead  = 0;
    159             pStreamOut->offFrameBufWrite = 0;
    160 
    161             OpusEncoder *pEnc = NULL;
    162 
    163             int orc;
    164             pEnc = opus_encoder_create(48000 /* Hz */, 2 /* Stereo */, OPUS_APPLICATION_AUDIO, &orc);
    165             if (orc != OPUS_OK)
    166             {
    167                 LogRel(("VideoRec: Audio codec failed to initialize: %s\n", opus_strerror(orc)));
    168                 return VERR_AUDIO_BACKEND_INIT_FAILED;
    169             }
    170 
    171             AssertPtr(pEnc);
    172 
    173     #if 0
    174             orc = opus_encoder_ctl(pEnc, OPUS_SET_BITRATE(DrvAudioHlpCalcBitrate()));
    175             if (orc != OPUS_OK)
    176             {
    177                 LogRel(("VideoRec: Audio codec failed to set bitrate: %s\n", opus_strerror(orc)));
    178                 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
    179             }
    180             else
    181             {
    182     #endif
    183                 pStreamOut->Codec.Opus.pEnc = pEnc;
    184 
    185                 if (pCfgAcq)
    186                     pCfgAcq->cSampleBufferSize = _4K; /** @todo Make this configurable. */
    187     //      }
     162        OpusEncoder *pEnc = NULL;
     163
     164        int orc;
     165        pEnc = opus_encoder_create(48000 /* Hz */, 2 /* Stereo */, OPUS_APPLICATION_AUDIO, &orc);
     166        if (orc != OPUS_OK)
     167        {
     168            LogRel(("VideoRec: Audio codec failed to initialize: %s\n", opus_strerror(orc)));
     169            return VERR_AUDIO_BACKEND_INIT_FAILED;
    188170        }
     171
     172        AssertPtr(pEnc);
     173
     174#if 0
     175        orc = opus_encoder_ctl(pEnc, OPUS_SET_BITRATE(DrvAudioHlpCalcBitrate()));
     176        if (orc != OPUS_OK)
     177        {
     178            LogRel(("VideoRec: Audio codec failed to set bitrate: %s\n", opus_strerror(orc)));
     179            rc = VERR_AUDIO_BACKEND_INIT_FAILED;
     180        }
     181        else
     182        {
     183#endif
     184        pStreamOut->Codec.Opus.pEnc = pEnc;
     185
     186        if (pCfgAcq)
     187            pCfgAcq->cSampleBufferSize = _4K; /** @todo Make this configurable. */
    189188    }
    190189#else
     
    209208    LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
    210209
    211     AudioMixBufReset(&pStream->MixBuf);
     210    switch (enmStreamCmd)
     211    {
     212        case PDMAUDIOSTREAMCMD_ENABLE:
     213        case PDMAUDIOSTREAMCMD_RESUME:
     214            break;
     215
     216        case PDMAUDIOSTREAMCMD_DISABLE:
     217        {
     218            AudioMixBufReset(&pStream->MixBuf);
     219            break;
     220        }
     221
     222        case PDMAUDIOSTREAMCMD_PAUSE:
     223            break;
     224
     225        default:
     226            AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));
     227            break;
     228    }
    212229
    213230    return VINF_SUCCESS;
     
    234251        switch (pThis->enmMode)
    235252        {
     253            /* In audio-only mode we're creating our own WebM writer instance,
     254             * as we don't have to synchronize with any external source, such as video recording data.*/
    236255            case AVRECMODE_AUDIO:
    237256            {
    238                 pThis->pEBML = new WebMWriter();
    239                 rc = pThis->pEBML->Create("/tmp/acap.webm", RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE, /** @todo Fix path! */
    240                                           WebMWriter::AudioCodec_Opus, WebMWriter::VideoCodec_None);
    241                 if (RT_SUCCESS(rc))
    242                     rc = pThis->pEBML->AddAudioTrack(44100, 2, 16, &pThis->uTrack);
     257                rc = VINF_SUCCESS;
    243258                break;
    244259            }
     
    333348#ifdef VBOX_WITH_LIBOPUS
    334349
    335     /** @todo For now we ASSUME 25 FPS. */
    336     uint16_t cbFrameSize = (/*pStreamOut->Props.uHz*/ 48000 / 25 /* FPS */);
     350    uint16_t cbFrameSize = 960; /** @todo 20ms worth of audio data. */
    337351
    338352    PRTCIRCBUF pCircBuf = pStreamOut->pCircBuf;
    339353
    340     while (pStreamOut->cbFrameBufUsed < cbFrameSize)
     354    while (RTCircBufUsed(pCircBuf) < cbFrameSize)
    341355    {
    342356        void  *pvBuf;
     
    400414            size_t  cbDst = sizeof(abDst);
    401415
    402             /* Call the encoder to encode our input samples. */
    403             opus_encoder_ctl(pStreamOut->Codec.Opus.pEnc, OPUS_SET_BITRATE(196000)); /** @todo */
     416            /* Call the encoder to encode one frame per iteration. */
     417            opus_encoder_ctl(pStreamOut->Codec.Opus.pEnc, OPUS_SET_BITRATE(196000)); /** @todo Only needed for VBR encoding. */
    404418            opus_int32 cbWritten = opus_encode(pStreamOut->Codec.Opus.pEnc,
    405419                                               (opus_int16 *)abSrc, cbSrc, abDst, cbDst);
    406420            if (cbWritten > 0)
    407421            {
     422                cbDst = cbWritten;
     423                Assert(cbDst <= sizeof(abDst));
     424
    408425                /* Call the WebM writer to actually write the encoded audio data. */
    409426                WebMWriter::BlockData_Opus blockData = { abDst, cbDst };
    410                 rc = pThis->pEBML->WriteBlock(pThis->uTrack, &blockData, sizeof(blockData));
     427                rc = pStreamOut->pWebM->WriteBlock(pStreamOut->uTrack, &blockData, sizeof(blockData));
    411428                AssertRC(rc);
    412429            }
     
    449466    }
    450467
    451     if (pStreamOut->pvFrameBuf)
    452     {
    453         Assert(pStreamOut->cbFrameBufSize);
    454         RTMemFree(pStreamOut->pvFrameBuf);
    455         pStreamOut->pvFrameBuf = NULL;
    456     }
    457 
    458468#ifdef VBOX_WITH_LIBOPUS
    459469    if (pStreamOut->Codec.Opus.pEnc)
     
    463473    }
    464474#endif
     475
     476    switch (pThis->enmMode)
     477    {
     478        case AVRECMODE_AUDIO:
     479        {
     480            if (pStreamOut->pWebM)
     481                pStreamOut->pWebM->Close();
     482            break;
     483        }
     484
     485        case AVRECMODE_AUDIO_VIDEO:
     486        default:
     487            break;
     488    }
    465489
    466490    return VINF_SUCCESS;
     
    494518    PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface);
    495519
    496     int rc;
    497 
    498520    switch (pThis->enmMode)
    499521    {
    500522        case AVRECMODE_AUDIO:
    501         {
    502             if (pThis->pEBML)
    503             {
    504                 pThis->pEBML->Close();
    505 
    506                 delete pThis->pEBML;
    507                 pThis->pEBML = NULL;
    508             }
     523        case AVRECMODE_AUDIO_VIDEO:
     524        default:
    509525            break;
    510         }
    511 
    512         case AVRECMODE_AUDIO_VIDEO:
    513         {
    514             break;
    515         }
    516 
    517         default:
    518             rc = VERR_NOT_SUPPORTED;
    519             break;
    520     }
    521 
    522     LogFlowFuncLeaveRC(rc);
     526    }
    523527}
    524528
  • trunk/src/VBox/Main/src-client/EbmlWriter.cpp

    r65340 r65353  
    3232
    3333#include "EbmlWriter.h"
    34 #include "EbmlIDs.h"
     34#include "EbmlMkvIDs.h"
    3535#include "Logging.h"
    3636
     
    143143        RTFileSeek(m_File, m_Elements.top().offset, RTFILE_SEEK_BEGIN, NULL);
    144144
    145         /* make sure that size will be serialized as uint64 */
     145        /* Make sure that size will be serialized as uint64_t. */
    146146        writeUnsignedInteger(uSize | UINT64_C(0x0100000000000000));
    147147        RTFileSeek(m_File, uPos, RTFILE_SEEK_BEGIN, NULL);
     
    292292
    293293    /**
    294      * Structure for keeping a track entry.
     294     * Structure for keeping a WebM track entry.
    295295     */
    296296    struct WebMTrack
     
    299299            : enmType(a_enmType)
    300300            , uTrack(a_uTrack)
    301             , offID(a_offID)
     301            , offUUID(a_offID)
     302            , cTotalClusters(0)
     303            , cTotalBlocks(0)
    302304        {
    303305            uUUID = RTRandU32();
     
    315317                /** Sample rate the codec is using. */
    316318                uint16_t uHzCodec;
    317                 /** Frame size (in bytes), based on the codec sample rate. */
     319                /** Frame size (in bytes), based on the codec sample rate.
     320                 *  Note: For now this does *not* change dynamically, in other words,
     321                 *        we do CBR (and not VBR) here! */
    318322                size_t   cbFrameSize;
    319323            } Audio;
     
    324328         *  Needed in case this track gets mux'ed with tracks from other files. Not really unique though. */
    325329        uint32_t      uUUID;
    326         /** Absolute offset in file of track ID.
     330        /** Absolute offset in file of track UUID.
    327331         *  Needed to write the hash sum within the footer. */
    328         uint64_t      offID;
     332        uint64_t      offUUID;
     333        /** Total number of clusters. */
     334        uint64_t      cTotalClusters;
     335        /** Total number of blocks. */
     336        uint64_t      cTotalBlocks;
    329337    };
     338
     339    /**
     340     * Structure for keeping a WebM cluster entry.
     341     */
     342    struct WebMCluster
     343    {
     344        WebMCluster(void)
     345            : uID(0)
     346            , offCluster(0)
     347            , fOpen(false)
     348            , tsStart(0)
     349            , tsEnd(0) { }
     350
     351        uint64_t      uID;
     352        /** Absolute offset (in bytes) of current cluster.
     353         *  Needed for seeking info table. */
     354        uint64_t      offCluster;
     355        bool          fOpen;
     356        uint64_t      tsStart;
     357        uint64_t      tsEnd;
     358    };
     359
     360    /**
     361     * Structure for keeping a WebM segment entry.
     362     *
     363     * Current we're only using one segment.
     364     */
     365    struct WebMSegment
     366    {
     367        WebMSegment(void)
     368            : offStart(0)
     369            , offInfo(0)
     370            , offSeekInfo(0)
     371            , offTracks(0)
     372            , offCues(0) { }
     373
     374        /** Absolute offset (in bytes) of CurSeg. */
     375        uint64_t                        offStart;
     376        /** Absolute offset (in bytes) of general info. */
     377        uint64_t                        offInfo;
     378        /** Absolute offset (in bytes) of seeking info. */
     379        uint64_t                        offSeekInfo;
     380        /** Absolute offset (in bytes) of tracks. */
     381        uint64_t                        offTracks;
     382        /** Absolute offset (in bytes) of cues table. */
     383        uint64_t                        offCues;
     384        /** List of cue points. Needed for seeking table. */
     385        std::list<WebMCueEntry>         lstCues;
     386
     387        /** Map of tracks.
     388         *  The key marks the track number (*not* the UUID!). */
     389        std::map <uint8_t, WebMTrack *> mapTracks;
     390
     391        /** Current cluster which is being handled.
     392         *
     393         *  Note that we don't need (and shouldn't need, as this can be a *lot* of data!) a
     394         *  list of all clusters. */
     395        WebMCluster                     CurCluster;
     396
     397    } CurSeg;
    330398
    331399#ifdef VBOX_WITH_LIBOPUS
     
    369437    int64_t                     m_tsLastPtsMs;
    370438
    371     /** Start offset (in bytes) of current segment. */
    372     uint64_t                    m_offSegCurStart;
    373 
    374     /** Start offset (in bytes) of seeking info segment. */
    375     uint64_t                    m_offSegSeekInfoStart;
    376     /** Start offset (in bytes) of general info segment. */
    377     uint64_t                    m_offSegInfoStart;
    378 
    379     /** Start offset (in bytes) of tracks segment. */
    380     uint64_t                    m_offSegTracksStart;
    381 
    382     /** Absolute position of cue segment. */
    383     uint64_t                    m_offSegCuesStart;
    384     /** List of cue points. Needed for seeking table. */
    385     std::list<WebMCueEntry>     m_lstCue;
    386 
    387     /** Map of tracks.
    388      *  The key marks the track number (*not* the UID!). */
    389     std::map <uint8_t, WebMTrack *> m_mapTracks;
    390     /** Whether we're currently in an opened tracks segment. */
    391     bool                        m_fTracksOpen;
    392 
    393     /** Timestamp (in ms) when the current cluster has been opened. */
    394     uint32_t                    m_tsClusterOpenMs;
    395     /** Whether we're currently in an opened cluster segment. */
    396     bool                        m_fClusterOpen;
    397     /** Absolute position (in bytes) of current cluster within file.
    398      *  Needed for seeking info table. */
    399     uint64_t                    m_offSegClusterStart;
     439    /** Whether we're currently in the tracks section. */
     440    bool                        m_fInTracksSection;
     441
     442    /** Size of timecodes in bytes. */
     443    size_t                      m_cbTimecode;
     444    /** Maximum value a timecode can have. */
     445    uint32_t                    m_uTimecodeMax;
     446    /** The timecode scale factor. */
     447    uint64_t                    m_uTimecodeScaleNs;
    400448
    401449    Ebml                        m_Ebml;
     
    411459        , m_tsInitialPtsMs(-1)
    412460        , m_tsLastPtsMs(-1)
    413         , m_offSegCurStart(0)
    414         , m_offSegSeekInfoStart(0)
    415         , m_offSegInfoStart(0)
    416         , m_offSegTracksStart(0)
    417         , m_offSegCuesStart(0)
    418         , m_fTracksOpen(false)
    419         , m_tsClusterOpenMs(0)
    420         , m_fClusterOpen(false)
    421         , m_offSegClusterStart(0) {}
     461        , m_fInTracksSection(false)
     462    {
     463        /* Size (in bytes) of time code to write. We use 2 bytes (16 bit) by default. */
     464        m_cbTimecode   = 2;
     465        m_uTimecodeMax = UINT16_MAX;
     466
     467        /* This is the default for WebM -- all timecodes in the segments are expressed in ms.
     468         * This allows every cluster to have blocks with positive values up to 32.767 seconds. */
     469        m_uTimecodeScaleNs = 1000000;
     470    }
    422471
    423472    virtual ~WebMWriter_Impl()
     
    429478    {
    430479#ifdef VBOX_WITH_LIBOPUS
    431         m_Ebml.subStart(TrackEntry);
    432         m_Ebml.serializeUnsignedInteger(TrackNumber, (uint8_t)m_mapTracks.size());
     480        m_Ebml.subStart(MkvElem_TrackEntry);
     481        m_Ebml.serializeUnsignedInteger(MkvElem_TrackNumber, (uint8_t)CurSeg.mapTracks.size());
    433482        /** @todo Implement track's "Language" property? Currently this defaults to English ("eng"). */
    434483
    435         uint8_t uTrack = (uint8_t)m_mapTracks.size();
     484        uint8_t uTrack = (uint8_t)CurSeg.mapTracks.size();
    436485
    437486        WebMTrack *pTrack = new WebMTrack(WebMTrackType_Audio, uTrack, RTFileTell(m_Ebml.getFile()));
    438487
    439         /* Clamp the codec rate value if it reaches a certain threshold. */
     488        /* Clamp the codec rate (Hz) value if it reaches a certain threshold. */
    440489        if      (uHz > 24000) pTrack->Audio.uHzCodec = 48000;
    441490        else if (uHz > 16000) pTrack->Audio.uHzCodec = 24000;
     
    446495        pTrack->Audio.uHzIn = uHz;
    447496
    448         /** @todo 1920 bytes is 40ms worth of audio data. Make this configurable. */
    449         pTrack->Audio.cbFrameSize =  1920 / (48000 / pTrack->Audio.uHzCodec);
     497        /** @todo 960 bytes is 20ms worth of audio data. Make this configurable. */
     498        pTrack->Audio.cbFrameSize = 960 /* Bytes */ / (48000 /* Hz */ / pTrack->Audio.uHzCodec);
    450499
    451500        /** @todo Resolve codec type. */
    452501        OpusPrivData opusPrivData(uHz, cChannels);
    453502
    454         m_Ebml.serializeUnsignedInteger(TrackUID, pTrack->uUUID, 4)
    455               .serializeUnsignedInteger(TrackType, 2 /* Audio */)
    456               .serializeString(CodecID, "A_OPUS")
    457               .serializeData(CodecPrivate, &opusPrivData, sizeof(opusPrivData))
    458               .serializeUnsignedInteger(CodecDelay, 0)
    459               .serializeUnsignedInteger(SeekPreRoll, 80000000)
    460               .subStart(Audio)
    461                   .serializeFloat(SamplingFrequency,  (float)pTrack->Audio.uHzCodec)
    462                   .serializeUnsignedInteger(Channels, cChannels)
    463                   .serializeUnsignedInteger(BitDepth, cBits)
    464               .subEnd(Audio)
    465               .subEnd(TrackEntry);
    466 
    467         m_mapTracks[uTrack] = pTrack;
     503        m_Ebml.serializeUnsignedInteger(MkvElem_TrackUID, pTrack->uUUID, 4)
     504              .serializeUnsignedInteger(MkvElem_TrackType, 2 /* Audio */)
     505              .serializeString(MkvElem_CodecID, "A_OPUS")
     506              .serializeData(MkvElem_CodecPrivate, &opusPrivData, sizeof(opusPrivData))
     507              .serializeUnsignedInteger(MkvElem_CodecDelay, 0)
     508              .serializeUnsignedInteger(MkvElem_SeekPreRoll, 80000000) /* 80.000ms */
     509              .subStart(MkvElem_Audio)
     510                  .serializeFloat(MkvElem_SamplingFrequency,  (float)pTrack->Audio.uHzCodec)
     511                  .serializeUnsignedInteger(MkvElem_Channels, cChannels)
     512                  .serializeUnsignedInteger(MkvElem_BitDepth, cBits)
     513              .subEnd(MkvElem_Audio)
     514              .subEnd(MkvElem_TrackEntry);
     515
     516        CurSeg.mapTracks[uTrack] = pTrack;
    468517
    469518        if (puTrack)
     
    479528    int AddVideoTrack(uint16_t uWidth, uint16_t uHeight, double dbFPS, uint8_t *puTrack)
    480529    {
    481         m_Ebml.subStart(TrackEntry);
    482         m_Ebml.serializeUnsignedInteger(TrackNumber, (uint8_t)m_mapTracks.size());
    483 
    484         uint8_t uTrack = (uint8_t)m_mapTracks.size();
     530        m_Ebml.subStart(MkvElem_TrackEntry);
     531        m_Ebml.serializeUnsignedInteger(MkvElem_TrackNumber, (uint8_t)CurSeg.mapTracks.size());
     532
     533        uint8_t uTrack = (uint8_t)CurSeg.mapTracks.size();
    485534
    486535        WebMTrack *pTrack = new WebMTrack(WebMTrackType_Video, uTrack, RTFileTell(m_Ebml.getFile()));
    487536
    488537        /** @todo Resolve codec type. */
    489         m_Ebml.serializeUnsignedInteger(TrackUID, pTrack->uUUID /* UID */, 4)
    490               .serializeUnsignedInteger(TrackType, 1 /* Video */)
    491               .serializeString(CodecID, "V_VP8")
    492               .subStart(Video)
    493               .serializeUnsignedInteger(PixelWidth, uWidth)
    494               .serializeUnsignedInteger(PixelHeight, uHeight)
    495               .serializeFloat(FrameRate, dbFPS)
    496               .subEnd(Video)
    497               .subEnd(TrackEntry);
    498 
    499         m_mapTracks[uTrack] = pTrack;
     538        m_Ebml.serializeUnsignedInteger(MkvElem_TrackUID, pTrack->uUUID /* UID */, 4)
     539              .serializeUnsignedInteger(MkvElem_TrackType, 1 /* Video */)
     540              .serializeString(MkvElem_CodecID, "V_VP8")
     541              .subStart(MkvElem_Video)
     542              .serializeUnsignedInteger(MkvElem_PixelWidth, uWidth)
     543              .serializeUnsignedInteger(MkvElem_PixelHeight, uHeight)
     544              .serializeFloat(MkvElem_FrameRate, dbFPS)
     545              .subEnd(MkvElem_Video)
     546              .subEnd(MkvElem_TrackEntry);
     547
     548        CurSeg.mapTracks[uTrack] = pTrack;
    500549
    501550        if (puTrack)
     
    509558        LogFunc(("Header @ %RU64\n", RTFileTell(m_Ebml.getFile())));
    510559
    511         m_Ebml.subStart(EBML)
    512               .serializeUnsignedInteger(EBMLVersion, 1)
    513               .serializeUnsignedInteger(EBMLReadVersion, 1)
    514               .serializeUnsignedInteger(EBMLMaxIDLength, 4)
    515               .serializeUnsignedInteger(EBMLMaxSizeLength, 8)
    516               .serializeString(DocType, "webm")
    517               .serializeUnsignedInteger(DocTypeVersion, 2)
    518               .serializeUnsignedInteger(DocTypeReadVersion, 2)
    519               .subEnd(EBML);
    520 
    521         m_Ebml.subStart(Segment);
     560        m_Ebml.subStart(MkvElem_EBML)
     561              .serializeUnsignedInteger(MkvElem_EBMLVersion, 1)
     562              .serializeUnsignedInteger(MkvElem_EBMLReadVersion, 1)
     563              .serializeUnsignedInteger(MkvElem_EBMLMaxIDLength, 4)
     564              .serializeUnsignedInteger(MkvElem_EBMLMaxSizeLength, 8)
     565              .serializeString(MkvElem_DocType, "webm")
     566              .serializeUnsignedInteger(MkvElem_DocTypeVersion, 2)
     567              .serializeUnsignedInteger(MkvElem_DocTypeReadVersion, 2)
     568              .subEnd(MkvElem_EBML);
     569
     570        m_Ebml.subStart(MkvElem_Segment);
    522571
    523572        /* Save offset of current segment. */
    524         m_offSegCurStart = RTFileTell(m_Ebml.getFile());
    525 
    526         writeSeekInfo();
     573        CurSeg.offStart = RTFileTell(m_Ebml.getFile());
     574
     575        writeSegSeekInfo();
    527576
    528577        /* Save offset of upcoming tracks segment. */
    529         m_offSegTracksStart = RTFileTell(m_Ebml.getFile());
     578        CurSeg.offTracks = RTFileTell(m_Ebml.getFile());
    530579
    531580        /* The tracks segment starts right after this header. */
    532         m_Ebml.subStart(Tracks);
    533         m_fTracksOpen = true;
     581        m_Ebml.subStart(MkvElem_Tracks);
     582        m_fInTracksSection = true;
    534583
    535584        return VINF_SUCCESS;
    536585    }
    537586
    538     int writeSimpleBlockInternal(uint8_t uTrackID, uint16_t uTimecode, const void *pvData, size_t cbData, uint8_t fFlags)
    539     {
    540         LogFunc(("SimpleBlock @ %RU64 (T%RU8, TS=%RU16, %zu bytes)\n", RTFileTell(m_Ebml.getFile()), uTrackID, uTimecode, cbData));
     587    int writeSimpleBlockInternal(WebMTrack *a_pTrack, uint64_t a_uTimecode,
     588                                 const void *a_pvData, size_t a_cbData, uint8_t a_fFlags)
     589    {
     590        LogFunc(("SimpleBlock @ %RU64 (T%RU8, TS=%RU64, %zu bytes)\n",
     591                 RTFileTell(m_Ebml.getFile()), a_pTrack->uTrack, a_uTimecode, a_cbData));
     592
     593        /** @todo Mask out non-valid timecode bits, e.g. the upper 48 bits for a (default) 16-bit timecode. */
     594        Assert(a_uTimecode <= m_uTimecodeMax);
    541595
    542596        /* Write a "Simple Block". */
    543         m_Ebml.writeClassId(SimpleBlock);
     597        m_Ebml.writeClassId(MkvElem_SimpleBlock);
    544598        /* Block size. */
    545         m_Ebml.writeUnsignedInteger(0x10000000u | (1      /* Track number. */
    546                                     + 2      /* Timecode .*/
    547                                     + 1      /* Flags. */
    548                                     + cbData /* Actual frame data. */),  4);
     599        m_Ebml.writeUnsignedInteger(0x10000000u | (  1            /* Track number size. */
     600                                                   + m_cbTimecode /* Timecode size .*/
     601                                                   + 1            /* Flags size. */
     602                                                   + a_cbData     /* Actual frame data size. */),  4);
    549603        /* Track number. */
    550         m_Ebml.writeSize(uTrackID);
     604        m_Ebml.writeSize(a_pTrack->uTrack);
    551605        /* Timecode (relative to cluster opening timecode). */
    552         m_Ebml.writeUnsignedInteger(uTimecode, 2);
     606        m_Ebml.writeUnsignedInteger(a_uTimecode, m_cbTimecode);
    553607        /* Flags. */
    554         m_Ebml.writeUnsignedInteger(fFlags, 1);
     608        m_Ebml.writeUnsignedInteger(a_fFlags, 1);
    555609        /* Frame data. */
    556         m_Ebml.write(pvData, cbData);
     610        m_Ebml.write(a_pvData, a_cbData);
     611
     612        a_pTrack->cTotalBlocks++;
    557613
    558614        return VINF_SUCCESS;
    559615    }
    560616
    561     int writeBlockVP8(const WebMTrack *a_pTrack, const vpx_codec_enc_cfg_t *a_pCfg, const vpx_codec_cx_pkt_t *a_pPkt)
     617    int writeBlockVP8(WebMTrack *a_pTrack, const vpx_codec_enc_cfg_t *a_pCfg, const vpx_codec_cx_pkt_t *a_pPkt)
    562618    {
    563619        RT_NOREF(a_pTrack);
     
    579635        bool     fClusterStart = false;
    580636
    581         if (tsPtsMs - m_tsClusterOpenMs > 65536)
     637        /* Did we reach the maximum our timecode can hold? Use a new cluster then. */
     638        if (tsPtsMs - CurSeg.CurCluster.tsStart > m_uTimecodeMax)
    582639            fClusterStart = true;
    583640        else
    584641        {
    585             /* Calculate the block's timecode, which is relative to the Cluster timecode. */
    586             tsBlockMs = static_cast<uint16_t>(tsPtsMs - m_tsClusterOpenMs);
     642            /* Calculate the block's timecode, which is relative to the current cluster's starting timecode. */
     643            tsBlockMs = static_cast<uint16_t>(tsPtsMs - CurSeg.CurCluster.tsStart);
    587644        }
    588645
     
    592649            || fKeyframe)
    593650        {
    594             if (m_fClusterOpen) /* Close current cluster first. */
    595                 m_Ebml.subEnd(Cluster);
     651            WebMCluster &Cluster = CurSeg.CurCluster;
     652
     653            a_pTrack->cTotalClusters++;
     654
     655            if (Cluster.fOpen) /* Close current cluster first. */
     656            {
     657                m_Ebml.subEnd(MkvElem_Cluster);
     658                Cluster.fOpen = false;
     659            }
    596660
    597661            tsBlockMs = 0;
    598662
    599663            /* Open a new cluster. */
    600             m_fClusterOpen       = true;
    601             m_tsClusterOpenMs    = (uint32_t)tsPtsMs;
    602             m_offSegClusterStart = RTFileTell(m_Ebml.getFile());
    603 
    604             LogFunc(("Cluster @ %RU64\n", m_offSegClusterStart));
    605 
    606             m_Ebml.subStart(Cluster)
    607                   .serializeUnsignedInteger(Timecode, m_tsClusterOpenMs);
     664            Cluster.fOpen      = true;
     665            Cluster.tsStart    = tsPtsMs;
     666            Cluster.offCluster = RTFileTell(m_Ebml.getFile());
     667
     668            LogFunc(("Cluster @ %RU64\n", Cluster.offCluster));
     669
     670            m_Ebml.subStart(MkvElem_Cluster)
     671                  .serializeUnsignedInteger(MkvElem_Timecode, Cluster.tsStart);
    608672
    609673            /* Save a cue point if this is a keyframe. */
    610674            if (fKeyframe)
    611675            {
    612                 WebMCueEntry cue(m_tsClusterOpenMs, m_offSegClusterStart);
    613                 m_lstCue.push_back(cue);
     676                WebMCueEntry cue(Cluster.tsStart, Cluster.offCluster);
     677                CurSeg.lstCues.push_back(cue);
    614678            }
    615679        }
     
    621685            fFlags |= 0x08; /* Invisible frame. */
    622686
    623         return writeSimpleBlockInternal(a_pTrack->uTrack, tsBlockMs, a_pPkt->data.frame.buf, a_pPkt->data.frame.sz, fFlags);
     687        return writeSimpleBlockInternal(a_pTrack, tsBlockMs, a_pPkt->data.frame.buf, a_pPkt->data.frame.sz, fFlags);
    624688    }
    625689
    626690#ifdef VBOX_WITH_LIBOPUS
    627691    /* Audio blocks that have same absolute timecode as video blocks SHOULD be written before the video blocks. */
    628     int writeBlockOpus(const WebMTrack *a_pTrack, const void *pvData, size_t cbData)
     692    int writeBlockOpus(WebMTrack *a_pTrack, const void *pvData, size_t cbData)
    629693    {
    630694        AssertPtrReturn(a_pTrack, VERR_INVALID_POINTER);
    631         AssertReturn(a_pTrack->Audio.cbFrameSize == cbData, VERR_INVALID_PARAMETER);
    632 
    633 static uint16_t s_uTimecode = 0;
    634 
    635         if (!m_fClusterOpen)
    636         {
    637             m_Ebml.subStart(Cluster)
    638                   .serializeUnsignedInteger(Timecode, 0);
    639             m_fClusterOpen = true;
     695        AssertPtrReturn(pvData, VERR_INVALID_POINTER);
     696        AssertReturn(cbData, VERR_INVALID_PARAMETER);
     697
     698        //static uint64_t s_uTimecode = 0;
     699        /* For now this is constant while encoding (CBR), but might change to VBR later. */
     700        //s_uTimecode += a_pTrack->Audio.cbFrameSize * 48000 / a_pTrack->Audio.uHzCodec;
     701
     702        //static uint64_t s_uPts = 0;
     703
     704        int64_t tsPtsMs = 1000000000 * a_pTrack->cTotalBlocks / 48000;
     705
     706        m_tsLastPtsMs = tsPtsMs;
     707
     708        if (m_tsInitialPtsMs < 0)
     709            m_tsInitialPtsMs = m_tsLastPtsMs;
     710
     711        WebMCluster &Cluster = CurSeg.CurCluster;
     712
     713        /* Calculate the relative time of this block. */
     714        uint16_t tsBlockMs     = 0;
     715        bool     fClusterStart = false;
     716
     717        if (a_pTrack->cTotalBlocks == 0)
     718            fClusterStart = true;
     719
     720        /* Did we reach the maximum our timecode can hold? Use a new cluster then. */
     721        if (tsPtsMs - Cluster.tsStart > m_uTimecodeMax)
     722            fClusterStart = true;
     723        else
     724        {
     725            /* Calculate the block's timecode, which is relative to the Cluster timecode. */
     726            tsBlockMs = static_cast<uint16_t>(tsPtsMs - Cluster.tsStart);
    640727        }
    641728
    642         uint64_t ts = (s_uTimecode * 1000 * 1000 * 1000) / 48000;
    643 
    644         LogFunc(("ts=%RU64 (timecode %RU16)\n", ts, s_uTimecode));
    645 
    646         s_uTimecode += a_pTrack->Audio.cbFrameSize;
    647 
    648         return writeSimpleBlockInternal(a_pTrack->uTrack, ts, pvData, cbData, 0 /* Flags */);
     729        if (fClusterStart)
     730        {
     731            a_pTrack->cTotalClusters++;
     732
     733            if (Cluster.fOpen) /* Close current cluster first. */
     734            {
     735                m_Ebml.subEnd(MkvElem_Cluster);
     736                Cluster.fOpen = false;
     737            }
     738
     739            tsBlockMs = 0;
     740
     741            Cluster.fOpen      = true;
     742            Cluster.tsStart    = tsPtsMs;
     743            Cluster.offCluster = RTFileTell(m_Ebml.getFile());
     744
     745            LogFunc(("Cluster @ %RU64\n", Cluster.offCluster));
     746
     747            m_Ebml.subStart(MkvElem_Cluster)
     748                  .serializeUnsignedInteger(MkvElem_Timecode, Cluster.tsStart);
     749        }
     750
     751        LogFunc(("Cluster %RU64 - Block %RU64: -> %RU64ms\n",
     752                 a_pTrack->cTotalClusters, a_pTrack->cTotalBlocks, a_pTrack->cTotalBlocks * 20));
     753
     754        return writeSimpleBlockInternal(a_pTrack, tsBlockMs, pvData, cbData, 0 /* Flags */);
    649755    }
    650756#endif
     
    654760        RT_NOREF(cbData); /* Only needed for assertions for now. */
    655761
    656         WebMTracks::const_iterator itTrack = m_mapTracks.find(uTrack);
    657         if (itTrack == m_mapTracks.end())
     762        WebMTracks::iterator itTrack = CurSeg.mapTracks.find(uTrack);
     763        if (itTrack == CurSeg.mapTracks.end())
    658764            return VERR_NOT_FOUND;
    659765
    660         const WebMTrack *pTrack = itTrack->second;
     766        WebMTrack *pTrack = itTrack->second;
    661767        AssertPtr(pTrack);
    662768
    663769        int rc;
    664770
    665         if (m_fTracksOpen)
    666         {
    667             m_Ebml.subEnd(Tracks);
    668             m_fTracksOpen = false;
     771        if (m_fInTracksSection)
     772        {
     773            m_Ebml.subEnd(MkvElem_Tracks);
     774            m_fInTracksSection = false;
    669775        }
    670776
     
    710816    int writeFooter(void)
    711817    {
    712         if (m_fTracksOpen)
    713         {
    714             m_Ebml.subEnd(Tracks);
    715             m_fTracksOpen = false;
     818        if (m_fInTracksSection)
     819        {
     820            m_Ebml.subEnd(MkvElem_Tracks);
     821            m_fInTracksSection = false;
    716822        }
    717823
    718         if (m_fClusterOpen)
    719         {
    720             m_Ebml.subEnd(Cluster);
    721             m_fClusterOpen = false;
     824        if (CurSeg.CurCluster.fOpen)
     825        {
     826            m_Ebml.subEnd(MkvElem_Cluster);
     827            CurSeg.CurCluster.fOpen = false;
    722828        }
    723829
    724830        /*
    725          * Write Cues segment.
     831         * Write Cues element.
    726832         */
    727833        LogFunc(("Cues @ %RU64\n", RTFileTell(m_Ebml.getFile())));
    728834
    729         m_offSegCuesStart = RTFileTell(m_Ebml.getFile());
    730 
    731         m_Ebml.subStart(Cues);
    732 
    733         for (std::list<WebMCueEntry>::iterator it = m_lstCue.begin(); it != m_lstCue.end(); ++it)
    734         {
    735             m_Ebml.subStart(CuePoint)
    736                   .serializeUnsignedInteger(CueTime, it->time)
    737                   .subStart(CueTrackPositions)
    738                   .serializeUnsignedInteger(CueTrack, 1)
    739                   .serializeUnsignedInteger(CueClusterPosition, it->loc - m_offSegCurStart, 8)
    740                   .subEnd(CueTrackPositions)
    741                   .subEnd(CuePoint);
     835        CurSeg.offCues = RTFileTell(m_Ebml.getFile());
     836
     837        m_Ebml.subStart(MkvElem_Cues);
     838
     839        std::list<WebMCueEntry>::iterator itCuePoint = CurSeg.lstCues.begin();
     840        while (itCuePoint != CurSeg.lstCues.end())
     841        {
     842            m_Ebml.subStart(MkvElem_CuePoint)
     843                  .serializeUnsignedInteger(MkvElem_CueTime, itCuePoint->time)
     844                  .subStart(MkvElem_CueTrackPositions)
     845                  .serializeUnsignedInteger(MkvElem_CueTrack, 1)
     846                  .serializeUnsignedInteger(MkvElem_CueClusterPosition, itCuePoint->loc - CurSeg.offStart, 8)
     847                  .subEnd(MkvElem_CueTrackPositions)
     848                  .subEnd(MkvElem_CuePoint);
     849
     850            itCuePoint++;
    742851        }
    743852
    744         m_Ebml.subEnd(Cues)
    745               .subEnd(Segment);
     853        m_Ebml.subEnd(MkvElem_Cues)
     854              .subEnd(MkvElem_Segment);
    746855
    747856        /*
    748          * Update SeekHead / Info segment.
     857         * Re-Update SeekHead / Info elements.
    749858         */
    750859
    751         writeSeekInfo();
     860        writeSegSeekInfo();
    752861
    753862        return RTFileSeek(m_Ebml.getFile(), 0, RTFILE_SEEK_END, NULL);
     
    756865    int close(void)
    757866    {
    758         WebMTracks::iterator itTrack = m_mapTracks.begin();
    759         for (; itTrack != m_mapTracks.end(); ++itTrack)
     867        WebMTracks::iterator itTrack = CurSeg.mapTracks.begin();
     868        for (; itTrack != CurSeg.mapTracks.end(); ++itTrack)
    760869        {
    761870            delete itTrack->second;
    762             m_mapTracks.erase(itTrack);
     871            CurSeg.mapTracks.erase(itTrack);
    763872        }
    764873
    765         Assert(m_mapTracks.size() == 0);
     874        Assert(CurSeg.mapTracks.size() == 0);
    766875
    767876        m_Ebml.close();
     
    775884private:
    776885
    777     void writeSeekInfo(void)
    778     {
    779         if (m_offSegSeekInfoStart)
    780             RTFileSeek(m_Ebml.getFile(), m_offSegSeekInfoStart, RTFILE_SEEK_BEGIN, NULL);
     886    /**
     887     * Writes the segment's seek information and cue points.
     888     *
     889     * @returns IPRT status code.
     890     */
     891    void writeSegSeekInfo(void)
     892    {
     893        if (CurSeg.offSeekInfo)
     894            RTFileSeek(m_Ebml.getFile(), CurSeg.offSeekInfo, RTFILE_SEEK_BEGIN, NULL);
    781895        else
    782             m_offSegSeekInfoStart = RTFileTell(m_Ebml.getFile());
    783 
    784         LogFunc(("SeekHead @ %RU64\n", m_offSegSeekInfoStart));
    785 
    786         m_Ebml.subStart(SeekHead)
    787 
    788               .subStart(Seek)
    789               .serializeUnsignedInteger(SeekID, Tracks)
    790               .serializeUnsignedInteger(SeekPosition, m_offSegTracksStart - m_offSegCurStart, 8)
    791               .subEnd(Seek)
    792 
    793               .subStart(Seek)
    794               .serializeUnsignedInteger(SeekID, Cues)
    795               .serializeUnsignedInteger(SeekPosition, m_offSegCuesStart - m_offSegCurStart, 8)
    796               .subEnd(Seek)
    797 
    798               .subStart(Seek)
    799               .serializeUnsignedInteger(SeekID, Info)
    800               .serializeUnsignedInteger(SeekPosition, m_offSegInfoStart - m_offSegCurStart, 8)
    801               .subEnd(Seek)
    802 
    803         .subEnd(SeekHead);
     896            CurSeg.offSeekInfo = RTFileTell(m_Ebml.getFile());
     897
     898        LogFunc(("SeekHead @ %RU64\n", CurSeg.offSeekInfo));
     899
     900        m_Ebml.subStart(MkvElem_SeekHead)
     901
     902              .subStart(MkvElem_Seek)
     903              .serializeUnsignedInteger(MkvElem_SeekID, MkvElem_Tracks)
     904              .serializeUnsignedInteger(MkvElem_SeekPosition, CurSeg.offTracks - CurSeg.offStart, 8)
     905              .subEnd(MkvElem_Seek)
     906
     907              .subStart(MkvElem_Seek)
     908              .serializeUnsignedInteger(MkvElem_SeekID, MkvElem_Cues)
     909              .serializeUnsignedInteger(MkvElem_SeekPosition, CurSeg.offCues - CurSeg.offStart, 8)
     910              .subEnd(MkvElem_Seek)
     911
     912              .subStart(MkvElem_Seek)
     913              .serializeUnsignedInteger(MkvElem_SeekID, MkvElem_Info)
     914              .serializeUnsignedInteger(MkvElem_SeekPosition, CurSeg.offInfo - CurSeg.offStart, 8)
     915              .subEnd(MkvElem_Seek)
     916
     917        .subEnd(MkvElem_SeekHead);
    804918
    805919        int64_t iFrameTime = (int64_t)1000 * 1 / 25; //m_Framerate.den / m_Framerate.num; /** @todo Fix this! */
    806         m_offSegInfoStart = RTFileTell(m_Ebml.getFile());
    807 
    808         LogFunc(("Info @ %RU64\n", m_offSegInfoStart));
     920        CurSeg.offInfo = RTFileTell(m_Ebml.getFile());
     921
     922        LogFunc(("Info @ %RU64\n", CurSeg.offInfo));
    809923
    810924        char szMux[64];
     
    814928        RTStrPrintf(szApp, sizeof(szApp), VBOX_PRODUCT " %sr%u", VBOX_VERSION_STRING, RTBldCfgRevision());
    815929
    816         m_Ebml.subStart(Info)
    817               .serializeUnsignedInteger(TimecodeScale, 1000000)
    818               .serializeFloat(Segment_Duration, m_tsLastPtsMs + iFrameTime - m_tsInitialPtsMs)
    819               .serializeString(MuxingApp, szMux)
    820               .serializeString(WritingApp, szApp)
    821               .subEnd(Info);
     930        m_Ebml.subStart(MkvElem_Info)
     931              .serializeUnsignedInteger(MkvElem_TimecodeScale, m_uTimecodeScaleNs)
     932              .serializeFloat(MkvElem_Segment_Duration, m_tsLastPtsMs + iFrameTime - m_tsInitialPtsMs)
     933              .serializeString(MkvElem_MuxingApp, szMux)
     934              .serializeString(MkvElem_WritingApp, szApp)
     935              .subEnd(MkvElem_Info);
    822936    }
    823937};
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