VirtualBox

Changeset 65389 in vbox


Ignore:
Timestamp:
Jan 20, 2017 2:03:51 PM (8 years ago)
Author:
vboxsync
Message:

VideoRec: Update.

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

Legend:

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

    r65353 r65389  
    7575        {
    7676            /** Encoder we're going to use. */
    77             OpusEncoder *pEnc;
     77            OpusEncoder    *pEnc;
     78            /** The encoding rate to use. */
     79            uint16_t        uHz;
     80            /** Duration of the frame in samples (per channel).
     81             *  Valid frame size are:
     82             *
     83             *  ms           Frame size
     84             *  2.5          120
     85             *  5            240
     86             *  10           480
     87             *  20 (Default) 960
     88             *  40           1920
     89             *  60           2880
     90             */
     91            uint32_t        csFrame;
     92            /** The maximum frame size (in samples) we can handle. */
     93            uint32_t        csFrameMax;
     94# ifdef DEBUG /** @todo Make these a STAM value? */
     95            /** Number of frames encoded. */
     96            uint64_t        cEncFrames;
     97            /** Total time (in ms) of already encoded audio data. */
     98            uint64_t        msEncTotal;
     99# endif
    78100        } Opus;
    79101#endif /* VBOX_WITH_LIBOPUS */
     
    90112    /** The PCM properties of this stream. */
    91113    PDMAUDIOPCMPROPS     Props;
    92     uint64_t             old_ticks;
    93     uint64_t             cSamplesSentPerSec;
    94114    /** Codec-specific data.
    95115     *  As every stream can be different, one codec per stream is needed. */
     
    136156    PAVRECSTREAMOUT pStreamOut = (PAVRECSTREAMOUT)pStream;
    137157
    138     int rc;
     158    int rc = DrvAudioHlpStreamCfgToProps(pCfgReq, &pStreamOut->Props);
     159    if (RT_FAILURE(rc))
     160        return rc;
    139161
    140162#ifdef VBOX_WITH_LIBOPUS
    141163    PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface);
    142164
     165    uint16_t uHz = pStreamOut->Props.uHz;
     166
     167    /* Opus only supports certain input sample rates in an efficient manner.
     168     * So make sure that we use those by resampling the data to the requested rate. */
     169    if      (uHz > 24000) uHz = 48000;
     170    else if (uHz > 16000) uHz = 24000;
     171    else if (uHz > 12000) uHz = 16000;
     172    else if (uHz > 8000 ) uHz = 12000;
     173    else     uHz = 8000;
     174
     175    LogFunc(("%RU16Hz -> %RU16Hz\n", pStreamOut->Props.uHz, uHz));
     176
     177    pStreamOut->Codec.Opus.uHz        = uHz;
     178    pStreamOut->Codec.Opus.csFrame    = uHz / 50;
     179#ifdef DEBUG
     180    pStreamOut->Codec.Opus.cEncFrames = 0;
     181    pStreamOut->Codec.Opus.msEncTotal = 0;
     182#endif
     183
     184    /* Calculate the maximum frame size. */
     185    pStreamOut->Codec.Opus.csFrameMax = 48000                        /* Maximum sample rate Opus can handle */
     186                                      * pStreamOut->Props.cChannels; /* Number of channels */
     187
     188    /* If we only record audio, create our own WebM writer instance here. */
    143189    if (pThis->enmMode == AVRECMODE_AUDIO)
    144190    {
     
    147193                                       WebMWriter::AudioCodec_Opus, WebMWriter::VideoCodec_None);
    148194        if (RT_SUCCESS(rc))
    149             rc = pStreamOut->pWebM->AddAudioTrack(44100, 2, 16, &pStreamOut->uTrack);
     195            rc = pStreamOut->pWebM->AddAudioTrack(pStreamOut->Codec.Opus.uHz, pStreamOut->Props.cChannels, pStreamOut->Props.cBits,
     196                                                  &pStreamOut->uTrack);
    150197    }
    151198
     
    153200        return rc;
    154201
    155     rc = RTCircBufCreate(&pStreamOut->pCircBuf, _4K);
    156     if (RT_FAILURE(rc))
    157         return rc;
    158 
    159     rc = DrvAudioHlpStreamCfgToProps(pCfgReq, &pStreamOut->Props);
     202    rc = RTCircBufCreate(&pStreamOut->pCircBuf, (pStreamOut->Codec.Opus.csFrame * pStreamOut->Props.cChannels) * sizeof(uint16_t));
    160203    if (RT_SUCCESS(rc))
    161204    {
     
    163206
    164207        int orc;
    165         pEnc = opus_encoder_create(48000 /* Hz */, 2 /* Stereo */, OPUS_APPLICATION_AUDIO, &orc);
     208        pEnc = opus_encoder_create(pStreamOut->Codec.Opus.uHz, pStreamOut->Props.cChannels, OPUS_APPLICATION_AUDIO, &orc);
    166209        if (orc != OPUS_OK)
    167210        {
     
    172215        AssertPtr(pEnc);
    173216
    174 #if 0
    175         orc = opus_encoder_ctl(pEnc, OPUS_SET_BITRATE(DrvAudioHlpCalcBitrate()));
     217        opus_encoder_ctl(pEnc, OPUS_SET_BITRATE(196000)); /** @todo Make this configurable. */
    176218        if (orc != OPUS_OK)
    177219        {
     
    181223        else
    182224        {
    183 #endif
    184         pStreamOut->Codec.Opus.pEnc = pEnc;
    185 
    186         if (pCfgAcq)
    187             pCfgAcq->cSampleBufferSize = _4K; /** @todo Make this configurable. */
     225            pStreamOut->Codec.Opus.pEnc = pEnc;
     226
     227            if (pCfgAcq)
     228            {
     229                /* Make sure to let the driver backend know that we need the audio data in
     230                 * a specific sampling rate Opus is optimized for. */
     231                pCfgAcq->uHz               = pStreamOut->Codec.Opus.uHz;
     232                pCfgAcq->cSampleBufferSize = _4K; /** @todo Make this configurable. */
     233            }
     234
     235            LogRel(("VideoRec: Recording audio in %RU16Hz, %RU8 channels, %RU16 bpS\n",
     236                    uHz, pStreamOut->Props.cChannels, pStreamOut->Props.cBits));
     237        }
    188238    }
    189239#else
     
    315365
    316366    PDRVAUDIOVIDEOREC pThis      = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface);
     367    RT_NOREF(pThis);
    317368    PAVRECSTREAMOUT   pStreamOut = (PAVRECSTREAMOUT)pStream;
    318369
    319     uint32_t cLive = AudioMixBufLive(&pStream->MixBuf);
    320 
    321     uint64_t now = PDMDrvHlpTMGetVirtualTime(pThis->pDrvIns);
    322     uint64_t ticks = now  - pStreamOut->old_ticks;
    323     uint64_t ticks_per_second = PDMDrvHlpTMGetVirtualFreq(pThis->pDrvIns);
    324 
    325     /* Minimize the rounding error: samples = int((ticks * freq) / ticks_per_second + 0.5). */
    326     uint32_t cSamplesPlayed = (int)((2 * ticks * pStreamOut->Props.uHz + ticks_per_second) / ticks_per_second / 2);
    327 
    328     /* Don't play more than available. */
    329     if (cSamplesPlayed > cLive)
    330         cSamplesPlayed = cLive;
    331 
    332     /* Remember when samples were consumed. */
    333     pStreamOut->old_ticks = now;
    334 
    335     int cSamplesToSend = cSamplesPlayed;
    336 
    337     LogFlowFunc(("uFreq=%RU32, cChan=%RU8, cBits=%RU8, fSigned=%RTbool, cSamplesToSend=%RU32\n",
    338                  pStreamOut->Props.uHz,   pStreamOut->Props.cChannels,
    339                  pStreamOut->Props.cBits, pStreamOut->Props.fSigned, cSamplesToSend));
     370    uint32_t csLive = AudioMixBufUsed(&pStream->MixBuf);
     371    if (!csLive)
     372    {
     373        Log3Func(("No live samples, skipping\n"));
     374        if (pcbWritten)
     375            *pcbWritten = 0;
     376        return VINF_SUCCESS;
     377    }
     378
     379    int rc;
    340380
    341381    uint32_t csReadTotal = 0;
    342 
    343     int rc;
    344382
    345383    /*
     
    347385     */
    348386#ifdef VBOX_WITH_LIBOPUS
    349 
    350     uint16_t cbFrameSize = 960; /** @todo 20ms worth of audio data. */
    351 
    352387    PRTCIRCBUF pCircBuf = pStreamOut->pCircBuf;
    353388
    354     while (RTCircBufUsed(pCircBuf) < cbFrameSize)
    355     {
    356         void  *pvBuf;
    357         size_t cbBuf;
     389    void  *pvBuf;
     390    size_t cbBuf;
     391
     392    /*
     393     * Fetch as much as we can into our internal ring buffer.
     394     */
     395    while (RTCircBufFree(pCircBuf))
     396    {
    358397        RTCircBufAcquireWriteBlock(pCircBuf, RTCircBufFree(pCircBuf), &pvBuf, &cbBuf);
    359398
     
    364403            uint32_t csRead = 0;
    365404            rc = AudioMixBufReadCirc(&pStream->MixBuf, pvBuf, cbBuf, &csRead);
    366             if (RT_SUCCESS(rc))
     405            if (   RT_SUCCESS(rc)
     406                && csRead)
    367407            {
    368                 AudioMixBufFinish(&pStream->MixBuf, csRead);
    369 
    370                 cbRead = AUDIOMIXBUF_S2B(&pStream->MixBuf, csRead);
     408                cbRead       = AUDIOMIXBUF_S2B(&pStream->MixBuf, csRead);
    371409                csReadTotal += csRead;
    372410            }
     
    380418            break;
    381419        }
    382 
    383         if (RTCircBufUsed(pCircBuf) >= cbFrameSize)
    384         {
    385             uint8_t abSrc[_4K];
    386             size_t  cbSrc = 0;
    387 
    388             while (cbSrc < cbFrameSize)
     420    }
     421
     422    if (csReadTotal)
     423        AudioMixBufFinish(&pStream->MixBuf, csReadTotal);
     424
     425    /*
     426     * Process our internal ring buffer and encode the data.
     427     */
     428
     429    uint8_t abSrc[_64K]; /** @todo Fix! */
     430    size_t  cbSrc;
     431
     432    const uint32_t csFrame = pStreamOut->Codec.Opus.csFrame;
     433    const uint32_t cbFrame = AUDIOMIXBUF_S2B(&pStream->MixBuf, csFrame);
     434
     435    while (RTCircBufUsed(pCircBuf) >= cbFrame)
     436    {
     437        cbSrc = 0;
     438
     439        while (cbSrc < cbFrame)
     440        {
     441            RTCircBufAcquireReadBlock(pCircBuf, cbFrame - cbSrc, &pvBuf, &cbBuf);
     442
     443            if (cbBuf)
    389444            {
    390                 RTCircBufAcquireReadBlock(pCircBuf, cbFrameSize - cbSrc, &pvBuf, &cbBuf);
    391 
    392                 if (cbBuf)
    393                 {
    394                     memcpy(&abSrc[cbSrc], pvBuf, cbBuf);
    395 
    396                     cbSrc += cbBuf;
    397                     Assert(cbSrc <= sizeof(abSrc));
    398                 }
    399 
    400                 RTCircBufReleaseReadBlock(pCircBuf, cbBuf);
    401 
    402                 if (!cbBuf)
    403                     break;
     445                memcpy(&abSrc[cbSrc], pvBuf, cbBuf);
     446
     447                cbSrc += cbBuf;
     448                Assert(cbSrc <= sizeof(abSrc));
    404449            }
    405450
    406             /*
    407              * Opus always encodes PER FRAME, that is, 2.5, 5, 10, 20, 40 or 60 ms of audio data.
    408              *
    409              * A packet can have up to 120ms worth of audio data.
    410              * Anything > 120ms of data will result in a "corrupted package" error message by
    411              * by decoding application.
    412              */
    413             uint8_t abDst[_4K];
    414             size_t  cbDst = sizeof(abDst);
    415 
    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. */
    418             opus_int32 cbWritten = opus_encode(pStreamOut->Codec.Opus.pEnc,
    419                                                (opus_int16 *)abSrc, cbSrc, abDst, cbDst);
    420             if (cbWritten > 0)
    421             {
    422                 cbDst = cbWritten;
    423                 Assert(cbDst <= sizeof(abDst));
    424 
    425                 /* Call the WebM writer to actually write the encoded audio data. */
    426                 WebMWriter::BlockData_Opus blockData = { abDst, cbDst };
    427                 rc = pStreamOut->pWebM->WriteBlock(pStreamOut->uTrack, &blockData, sizeof(blockData));
    428                 AssertRC(rc);
    429             }
    430             else if (cbWritten < 0)
    431             {
    432                 AssertMsgFailed(("Encoding failed: %s\n", opus_strerror(cbWritten)));
    433                 rc = VERR_INVALID_PARAMETER;
    434             }
    435 
    436             if (RT_FAILURE(rc))
     451            RTCircBufReleaseReadBlock(pCircBuf, cbBuf);
     452
     453            if (!cbBuf)
    437454                break;
    438455        }
     456
     457#ifdef DEBUG_andy
     458        RTFILE fh;
     459        RTFileOpen(&fh, "/tmp/acap.pcm", RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
     460        RTFileWrite(fh, abSrc, cbSrc, NULL);
     461        RTFileClose(fh);
     462#endif
     463
     464        Assert(cbSrc == cbFrame);
     465
     466        /*
     467         * Opus always encodes PER FRAME, that is, exactly 2.5, 5, 10, 20, 40 or 60 ms of audio data.
     468         *
     469         * A packet can have up to 120ms worth of audio data.
     470         * Anything > 120ms of data will result in a "corrupted package" error message by
     471         * by decoding application.
     472         */
     473        uint8_t abDst[_64K]; /** @todo Fix! */
     474        size_t  cbDst = sizeof(abDst);
     475
     476        /* Call the encoder to encode one frame per iteration. */
     477        opus_int32 cbWritten = opus_encode(pStreamOut->Codec.Opus.pEnc,
     478                                           (opus_int16 *)abSrc, csFrame, abDst, cbDst);
     479        if (cbWritten > 0)
     480        {
     481#ifdef DEBUG
     482            /* Get overall frames encoded. */
     483            uint32_t cEncFrames          = opus_packet_get_nb_frames(abDst, cbDst);
     484            uint32_t cEncSamplesPerFrame = opus_packet_get_samples_per_frame(abDst, pStreamOut->Codec.Opus.uHz);
     485            uint32_t csEnc               = cEncFrames * cEncSamplesPerFrame;
     486
     487            pStreamOut->Codec.Opus.cEncFrames += cEncFrames;
     488            pStreamOut->Codec.Opus.msEncTotal += 20 /* Default 20 ms */ * cEncFrames;
     489
     490            LogFunc(("%RU64ms [%RU64 frames]: cbSrc=%zu, cbDst=%zu, cEncFrames=%RU32, cEncSamplesPerFrame=%RU32, csEnc=%RU32\n",
     491                     pStreamOut->Codec.Opus.msEncTotal, pStreamOut->Codec.Opus.cEncFrames,
     492                     cbSrc, cbDst, cEncFrames, cEncSamplesPerFrame, csEnc));
     493#endif
     494            Assert((uint32_t)cbWritten <= cbDst);
     495            cbDst = RT_MIN((uint32_t)cbWritten, cbDst); /* Update cbDst to actual bytes encoded (written). */
     496
     497            /* Call the WebM writer to actually write the encoded audio data. */
     498            WebMWriter::BlockData_Opus blockData = { abDst, cbDst };
     499            rc = pStreamOut->pWebM->WriteBlock(pStreamOut->uTrack, &blockData, sizeof(blockData));
     500            AssertRC(rc);
     501        }
     502        else if (cbWritten < 0)
     503        {
     504            AssertMsgFailed(("Encoding failed: %s\n", opus_strerror(cbWritten)));
     505            rc = VERR_INVALID_PARAMETER;
     506        }
     507
     508        if (RT_FAILURE(rc))
     509            break;
    439510    }
    440511#else
  • trunk/src/VBox/Main/src-client/EbmlWriter.cpp

    r65362 r65389  
    2020#include <map>
    2121#include <stack>
     22
     23#include <math.h> /* For lround.h. */
     24
    2225#include <iprt/asm.h>
    2326#include <iprt/buildconfig.h>
     
    176179     * by this function.
    177180     */
    178     inline Ebml &serializeFloat(EbmlClassId classId, double value)
     181    inline Ebml &serializeFloat(EbmlClassId classId, float value)
    179182    {
    180183        writeClassId(classId);
    181         writeSize(sizeof(double));
    182         writeUnsignedInteger(*reinterpret_cast<uint64_t*>(&value));
     184        Assert(sizeof(uint32_t) == sizeof(float));
     185        writeSize(sizeof(float));
     186
     187        union
     188        {
     189            float   f;
     190            uint8_t u8[4];
     191        } u;
     192
     193        u.f = value;
     194
     195        for (int8_t i = 3; i >= 0; i--) /* Converts values to big endian. */
     196            write(&u.u8[i], 1);
     197
    183198        return *this;
    184199    }
     
    314329            {
    315330                /** Sample rate of input data. */
    316                 uint16_t uHzIn;
    317                 /** Sample rate the codec is using. */
    318                 uint16_t uHzCodec;
    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! */
    322                 size_t   cbFrameSize;
     331                uint16_t uHz;
     332                /** Duration of the frame in samples (per channel).
     333                 *  Valid frame size are:
     334                 *
     335                 *  ms           Frame size
     336                 *  2.5          120
     337                 *  5            240
     338                 *  10           480
     339                 *  20 (Default) 960
     340                 *  40           1920
     341                 *  60           2880
     342                 */
     343                uint16_t csFrame;
    323344            } Audio;
    324345        };
     
    346367            , offCluster(0)
    347368            , fOpen(false)
    348             , tsStartMs(0)
    349             , tsEndMs(0) { }
     369            , tcStart(0)
     370            , tcLast(0)
     371            , cBlocks(0)
     372            , cbData(0) { }
    350373
    351374        /** This cluster's ID. */
     
    356379        /** Whether this cluster element is opened currently. */
    357380        bool          fOpen;
    358         /** Timestamp (in ms) when starting this  cluster. */
    359         uint64_t      tsStartMs;
    360         /** Timestamp (in ms) when this cluster ended. */
    361         uint64_t      tsEndMs;
     381        /** Timecode when starting this cluster. */
     382        uint64_t      tcStart;
     383        /** Timecode when this cluster was last touched. */
     384        uint64_t      tcLast;
     385        /** Number of (simple) blocks in this cluster. */
     386        uint64_t      cBlocks;
     387        /** Size (in bytes) of data already written. */
     388        uint64_t      cbData;
    362389    };
    363390
     
    370397    {
    371398        WebMSegment(void)
    372             : offStart(0)
     399            : tcStart(UINT64_MAX)
     400            , tcEnd(UINT64_MAX)
     401            , offStart(0)
    373402            , offInfo(0)
    374403            , offSeekInfo(0)
    375404            , offTracks(0)
    376             , offCues(0) { }
     405            , offCues(0)
     406        {
     407            /* This is the default for WebM -- all timecodes in the segments are expressed in ms.
     408             * This allows every cluster to have blocks with positive values up to 32.767 seconds. */
     409            uTimecodeScaleFactor = 1000000;
     410            //m_uTimecodeScaleFactor = lround(1000000000 /* ns */ / 48000);
     411
     412            LogFunc(("Default timecode scale is: %RU64ns\n", uTimecodeScaleFactor));
     413        }
     414
     415        /** The timecode scale factor of this segment.
     416         *  By default this is 1000000, which is 1ms per timecode unit. */
     417        uint64_t                        uTimecodeScaleFactor;
     418
     419        /** Timecode when starting this segment. */
     420        uint64_t                        tcStart;
     421        /** Timecode when this segment ended. */
     422        uint64_t                        tcEnd;
    377423
    378424        /** Absolute offset (in bytes) of CurSeg. */
     
    434480    WebMWriter::VideoCodec      m_enmVideoCodec;
    435481
    436     /** This PTS (Presentation Time Stamp) acts as our master clock for synchronizing streams. */
    437     uint64_t                    m_uPts;
    438     /** Timestamp (in ms) of initial PTS. */
    439     int64_t                     m_tsInitialPtsMs;
    440     /** Timestamp (in ms) of last written PTS. */
    441     int64_t                     m_tsLastPtsMs;
    442 
    443482    /** Whether we're currently in the tracks section. */
    444483    bool                        m_fInTracksSection;
     
    448487    /** Maximum value a timecode can have. */
    449488    uint32_t                    m_uTimecodeMax;
    450     /** The timecode scale factor. */
    451     uint64_t                    m_uTimecodeScaleNs;
    452489
    453490    Ebml                        m_Ebml;
     
    460497
    461498    WebMWriter_Impl() :
    462           m_uPts(0)
    463         , m_tsInitialPtsMs(-1)
    464         , m_tsLastPtsMs(-1)
    465         , m_fInTracksSection(false)
     499        m_fInTracksSection(false)
    466500    {
    467501        /* Size (in bytes) of time code to write. We use 2 bytes (16 bit) by default. */
    468502        m_cbTimecode   = 2;
    469503        m_uTimecodeMax = UINT16_MAX;
    470 
    471         /* This is the default for WebM -- all timecodes in the segments are expressed in ms.
    472          * This allows every cluster to have blocks with positive values up to 32.767 seconds. */
    473         m_uTimecodeScaleNs = 1000000;
    474504    }
    475505
     
    479509    }
    480510
     511    /**
     512     * Adds an audio track.
     513     *
     514     * @returns IPRT status code.
     515     * @param   uHz             Input sampling rate.
     516     * @param   cChannels       Number of input audio channels.
     517     * @param   cBits           Number of input bits per channel.
     518     * @param   puTrack         Track number on successful creation. Optional.
     519     */
    481520    int AddAudioTrack(uint16_t uHz, uint8_t cChannels, uint8_t cBits, uint8_t *puTrack)
    482521    {
    483522#ifdef VBOX_WITH_LIBOPUS
     523        int rc;
     524
     525        /*
     526         * Check if the requested codec rate is supported.
     527         *
     528         * Only the following values are supported by an Opus standard build
     529         * -- every other rate only is supported by a custom build.
     530         */
     531        switch (uHz)
     532        {
     533            case 48000:
     534            case 24000:
     535            case 16000:
     536            case 12000:
     537            case  8000:
     538                rc = VINF_SUCCESS;
     539                break;
     540
     541            default:
     542                rc = VERR_NOT_SUPPORTED;
     543                break;
     544        }
     545
     546        if (RT_FAILURE(rc))
     547            return rc;
     548
    484549        m_Ebml.subStart(MkvElem_TrackEntry);
    485550        m_Ebml.serializeUnsignedInteger(MkvElem_TrackNumber, (uint8_t)CurSeg.mapTracks.size());
     
    490555        WebMTrack *pTrack = new WebMTrack(WebMTrackType_Audio, uTrack, RTFileTell(m_Ebml.getFile()));
    491556
    492         /* Clamp the codec rate (Hz) value if it reaches a certain threshold. */
    493         if      (uHz > 24000) pTrack->Audio.uHzCodec = 48000;
    494         else if (uHz > 16000) pTrack->Audio.uHzCodec = 24000;
    495         else if (uHz > 12000) pTrack->Audio.uHzCodec = 16000;
    496         else if (uHz > 8000 ) pTrack->Audio.uHzCodec = 12000;
    497         else                  pTrack->Audio.uHzCodec = 8000;
    498 
    499         pTrack->Audio.uHzIn = uHz;
    500 
    501         /** @todo 960 bytes is 20ms worth of audio data. Make this configurable. */
    502         pTrack->Audio.cbFrameSize = 960 /* Bytes */ / (48000 /* Hz */ / pTrack->Audio.uHzCodec);
    503 
    504         /** @todo Resolve codec type. */
     557        pTrack->Audio.uHz     = uHz;
     558        pTrack->Audio.csFrame = pTrack->Audio.uHz / 50; /** @todo 20 ms of audio data. Make this configurable? */
     559
    505560        OpusPrivData opusPrivData(uHz, cChannels);
    506561
    507         m_Ebml.serializeUnsignedInteger(MkvElem_TrackUID, pTrack->uUUID, 4)
    508               .serializeUnsignedInteger(MkvElem_TrackType, 2 /* Audio */)
    509               .serializeString(MkvElem_CodecID, "A_OPUS")
    510               .serializeData(MkvElem_CodecPrivate, &opusPrivData, sizeof(opusPrivData))
    511               .serializeUnsignedInteger(MkvElem_CodecDelay,  0)
    512               .serializeUnsignedInteger(MkvElem_SeekPreRoll, 80000000) /* 80.000ms */
     562        LogFunc(("Opus @ %RU16Hz (Frame size is %RU16 samples / channel))\n", pTrack->Audio.uHz, pTrack->Audio.csFrame));
     563
     564        m_Ebml.serializeUnsignedInteger(MkvElem_TrackUID,     pTrack->uUUID, 4)
     565              .serializeUnsignedInteger(MkvElem_TrackType,    2 /* Audio */)
     566              .serializeString(MkvElem_CodecID,               "A_OPUS")
     567              .serializeData(MkvElem_CodecPrivate,            &opusPrivData, sizeof(opusPrivData))
     568              .serializeUnsignedInteger(MkvElem_CodecDelay,   0)
     569              .serializeUnsignedInteger(MkvElem_SeekPreRoll,  80000000) /* 80ms in ns. */
    513570              .subStart(MkvElem_Audio)
    514                   .serializeFloat(MkvElem_SamplingFrequency,  (float)pTrack->Audio.uHzCodec)
     571                  .serializeFloat(MkvElem_SamplingFrequency,  (float)uHz)
    515572                  .serializeUnsignedInteger(MkvElem_Channels, cChannels)
    516573                  .serializeUnsignedInteger(MkvElem_BitDepth, cBits)
     
    519576
    520577        CurSeg.mapTracks[uTrack] = pTrack;
     578
     579        Assert(uTrack == 0);
    521580
    522581        if (puTrack)
     
    541600
    542601        /** @todo Resolve codec type. */
    543         m_Ebml.serializeUnsignedInteger(MkvElem_TrackUID, pTrack->uUUID /* UID */, 4)
    544               .serializeUnsignedInteger(MkvElem_TrackType, 1 /* Video */)
    545               .serializeString(MkvElem_CodecID, "V_VP8")
     602        m_Ebml.serializeUnsignedInteger(MkvElem_TrackUID,    pTrack->uUUID /* UID */, 4)
     603              .serializeUnsignedInteger(MkvElem_TrackType,   1 /* Video */)
     604              .serializeString(MkvElem_CodecID,              "V_VP8")
    546605              .subStart(MkvElem_Video)
    547               .serializeUnsignedInteger(MkvElem_PixelWidth, uWidth)
     606              .serializeUnsignedInteger(MkvElem_PixelWidth,  uWidth)
    548607              .serializeUnsignedInteger(MkvElem_PixelHeight, uHeight)
    549               .serializeFloat(MkvElem_FrameRate, dbFPS)
     608              .serializeFloat(MkvElem_FrameRate,             dbFPS)
    550609              .subEnd(MkvElem_Video)
    551610              .subEnd(MkvElem_TrackEntry);
     
    630689
    631690        /* Calculate the PTS of this frame in milliseconds. */
    632         int64_t tsPtsMs = a_pPkt->data.frame.pts * 1000
    633                         * (uint64_t) a_pCfg->g_timebase.num / a_pCfg->g_timebase.den;
    634 
    635         if (tsPtsMs <= m_tsLastPtsMs)
    636             tsPtsMs = m_tsLastPtsMs + 1;
    637 
    638         m_tsLastPtsMs = tsPtsMs;
    639 
    640         if (m_tsInitialPtsMs < 0)
    641             m_tsInitialPtsMs = m_tsLastPtsMs;
     691        uint64_t tcPTS = a_pPkt->data.frame.pts * 1000
     692                         * (uint64_t) a_pCfg->g_timebase.num / a_pCfg->g_timebase.den;
     693
     694        if (tcPTS <= CurSeg.CurCluster.tcLast)
     695            tcPTS = CurSeg.CurCluster.tcLast + 1;
     696
     697        CurSeg.CurCluster.tcLast = tcPTS;
     698
     699        if (CurSeg.CurCluster.tcStart == UINT64_MAX)
     700            CurSeg.CurCluster.tcStart = CurSeg.CurCluster.tcLast;
    642701
    643702        /* Calculate the relative time of this block. */
    644         uint16_t tsBlockMs     = 0;
     703        uint16_t tcBlock       = 0;
    645704        bool     fClusterStart = false;
    646705
    647706        /* Did we reach the maximum our timecode can hold? Use a new cluster then. */
    648         if (tsPtsMs - CurSeg.CurCluster.tsStartMs > m_uTimecodeMax)
     707        if (tcPTS - CurSeg.CurCluster.tcStart > m_uTimecodeMax)
    649708            fClusterStart = true;
    650709        else
    651710        {
    652711            /* Calculate the block's timecode, which is relative to the current cluster's starting timecode. */
    653             tsBlockMs = static_cast<uint16_t>(tsPtsMs - CurSeg.CurCluster.tsStartMs);
     712            tcBlock = static_cast<uint16_t>(tcPTS - CurSeg.CurCluster.tcStart);
    654713        }
    655714
     
    669728            }
    670729
    671             tsBlockMs = 0;
     730            tcBlock = 0;
    672731
    673732            /* Open a new cluster. */
    674733            Cluster.fOpen      = true;
    675             Cluster.tsStartMs    = tsPtsMs;
     734            Cluster.tcStart    = tcPTS;
    676735            Cluster.offCluster = RTFileTell(m_Ebml.getFile());
    677 
    678             LogFunc(("Cluster @ %RU64\n", Cluster.offCluster));
     736            Cluster.cBlocks    = 0;
     737            Cluster.cbData     = 0;
     738
     739            LogFunc(("[C%RU64] Start @ tc=%RU64 off=%RU64\n", a_pTrack->cTotalClusters, Cluster.tcStart, Cluster.offCluster));
    679740
    680741            m_Ebml.subStart(MkvElem_Cluster)
    681                   .serializeUnsignedInteger(MkvElem_Timecode, Cluster.tsStartMs);
     742                  .serializeUnsignedInteger(MkvElem_Timecode, Cluster.tcStart);
    682743
    683744            /* Save a cue point if this is a keyframe. */
    684745            if (fKeyframe)
    685746            {
    686                 WebMCueEntry cue(Cluster.tsStartMs, Cluster.offCluster);
     747                WebMCueEntry cue(Cluster.tcStart, Cluster.offCluster);
    687748                CurSeg.lstCues.push_back(cue);
    688749            }
    689750        }
     751
     752        LogFunc(("tcPTS=%RU64, s=%RU64, e=%RU64\n", tcPTS, CurSeg.CurCluster.tcStart, CurSeg.CurCluster.tcLast));
    690753
    691754        uint8_t fFlags = 0;
     
    695758            fFlags |= 0x08; /* Invisible frame. */
    696759
    697         return writeSimpleBlockInternal(a_pTrack, tsBlockMs, a_pPkt->data.frame.buf, a_pPkt->data.frame.sz, fFlags);
     760        return writeSimpleBlockInternal(a_pTrack, tcBlock, a_pPkt->data.frame.buf, a_pPkt->data.frame.sz, fFlags);
    698761    }
    699762#endif /* VBOX_WITH_LIBVPX */
     
    707770        AssertReturn(cbData, VERR_INVALID_PARAMETER);
    708771
    709         //static uint64_t s_uTimecode = 0;
    710         /* For now this is constant while encoding (CBR), but might change to VBR later. */
    711         //s_uTimecode += a_pTrack->Audio.cbFrameSize * 48000 / a_pTrack->Audio.uHzCodec;
    712 
    713         //static uint64_t s_uPts = 0;
    714 
    715         int64_t tsPtsMs = 1000000000 * a_pTrack->cTotalBlocks / 48000;
    716 
    717         m_tsLastPtsMs = tsPtsMs;
    718 
    719         if (m_tsInitialPtsMs < 0)
    720             m_tsInitialPtsMs = m_tsLastPtsMs;
    721 
    722772        WebMCluster &Cluster = CurSeg.CurCluster;
    723773
    724         /* Calculate the relative time of this block. */
    725         uint16_t tsBlockMs     = 0;
     774        /* Calculate the PTS. */
     775        /* Make sure to round the result. This is very important! */
     776        uint64_t tcPTSRaw = lround((CurSeg.uTimecodeScaleFactor * 1000 * Cluster.cbData) / a_pTrack->Audio.uHz);
     777
     778        uint64_t tcPTS = /*Cluster.tcStart +*/ lround(tcPTSRaw / CurSeg.uTimecodeScaleFactor);
     779
     780        if (Cluster.tcStart == UINT64_MAX)
     781            Cluster.tcStart = tcPTS;
     782
     783        LogFunc(("tcPTSRaw=%RU64, tcPTS=%RU64, cbData=%RU64\n", tcPTSRaw, tcPTS, Cluster.cbData));
     784
     785        Cluster.tcLast = tcPTS;
     786
     787        uint16_t tcBlock;
    726788        bool     fClusterStart = false;
    727789
     
    730792
    731793        /* Did we reach the maximum our timecode can hold? Use a new cluster then. */
    732         if (tsPtsMs - Cluster.tsStartMs > m_uTimecodeMax)
     794        if (tcPTS - Cluster.tcStart > m_uTimecodeMax)
     795        {
     796            tcBlock = 0;
     797
    733798            fClusterStart = true;
     799        }
    734800        else
    735801        {
    736802            /* Calculate the block's timecode, which is relative to the Cluster timecode. */
    737             tsBlockMs = static_cast<uint16_t>(tsPtsMs - Cluster.tsStartMs);
     803            tcBlock = static_cast<uint16_t>(tcPTS - Cluster.tcStart);
    738804        }
    739805
    740806        if (fClusterStart)
    741807        {
    742             a_pTrack->cTotalClusters++;
    743 
    744808            if (Cluster.fOpen) /* Close current cluster first. */
    745809            {
     
    748812            }
    749813
    750             tsBlockMs = 0;
     814            tcBlock = 0;
    751815
    752816            Cluster.fOpen      = true;
    753             Cluster.tsStartMs    = tsPtsMs;
     817            Cluster.tcStart    = tcPTS;
    754818            Cluster.offCluster = RTFileTell(m_Ebml.getFile());
    755 
    756             LogFunc(("Cluster @ %RU64\n", Cluster.offCluster));
     819            Cluster.cBlocks    = 0;
     820            Cluster.cbData     = 0;
     821
     822            LogFunc(("[C%RU64] Start @ tc=%RU64 off=%RU64\n", a_pTrack->cTotalClusters, Cluster.tcStart, Cluster.offCluster));
    757823
    758824            m_Ebml.subStart(MkvElem_Cluster)
    759                   .serializeUnsignedInteger(MkvElem_Timecode, Cluster.tsStartMs);
    760         }
    761 
    762         LogFunc(("Cluster %RU64 - Block %RU64: -> %RU64ms\n",
    763                  a_pTrack->cTotalClusters, a_pTrack->cTotalBlocks, a_pTrack->cTotalBlocks * 20));
    764 
    765         return writeSimpleBlockInternal(a_pTrack, tsBlockMs, pvData, cbData, 0 /* Flags */);
     825                  .serializeUnsignedInteger(MkvElem_Timecode, Cluster.tcStart);
     826
     827            a_pTrack->cTotalClusters++;
     828        }
     829
     830        LogFunc(("tcPTS=%RU64, s=%RU64, e=%RU64\n", tcPTS, CurSeg.CurCluster.tcStart, CurSeg.CurCluster.tcLast));
     831
     832        Cluster.cBlocks += 1;
     833        Cluster.cbData  += cbData;
     834
     835        return writeSimpleBlockInternal(a_pTrack, tcBlock, pvData, cbData, 0x80 /* Flags */);
    766836    }
    767837#endif /* VBOX_WITH_LIBOPUS */
     
    9301000        .subEnd(MkvElem_SeekHead);
    9311001
    932         int64_t iFrameTime = (int64_t)1000 * 1 / 25; //m_Framerate.den / m_Framerate.num; /** @todo Fix this! */
     1002        //int64_t iFrameTime = (int64_t)1000 * 1 / 25; //m_Framerate.den / m_Framerate.num; /** @todo Fix this! */
    9331003        CurSeg.offInfo = RTFileTell(m_Ebml.getFile());
    9341004
     
    9451015        RTStrPrintf(szApp, sizeof(szApp), VBOX_PRODUCT " %sr%u", VBOX_VERSION_STRING, RTBldCfgRevision());
    9461016
     1017        LogFunc(("Duration=%RU64\n", CurSeg.tcEnd - CurSeg.tcStart));
     1018
    9471019        m_Ebml.subStart(MkvElem_Info)
    948               .serializeUnsignedInteger(MkvElem_TimecodeScale, m_uTimecodeScaleNs)
    949               .serializeFloat(MkvElem_Segment_Duration, m_tsLastPtsMs + iFrameTime - m_tsInitialPtsMs)
     1020              .serializeUnsignedInteger(MkvElem_TimecodeScale, CurSeg.uTimecodeScaleFactor)
     1021              .serializeFloat(MkvElem_Segment_Duration, CurSeg.tcEnd /*+ iFrameTime*/ - CurSeg.tcStart)
    9501022              .serializeString(MkvElem_MuxingApp, szMux)
    9511023              .serializeString(MkvElem_WritingApp, szApp)
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