VirtualBox

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


Ignore:
Timestamp:
Jan 9, 2017 11:40:46 AM (8 years ago)
Author:
vboxsync
Message:

VideoRec: Update.

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

Legend:

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

    r65162 r65197  
    11/* $Id$ */
    22/** @file
    3  * VirtualBox driver interface to video recording backend.
     3 * VirtualBox driver interface video recording audio backend.
    44 */
    55
    66/*
    7  * Copyright (C) 2014-2016 Oracle Corporation
     7 * Copyright (C) 2014-2017 Oracle Corporation
    88 *
    99 * This file is part of VirtualBox Open Source Edition (OSE), as
  • trunk/src/VBox/Main/src-client/DrvAudioVideoRec.cpp

    r65170 r65197  
    2424#include "DrvAudioVideoRec.h"
    2525#include "ConsoleImpl.h"
    26 #include "ConsoleVRDPServer.h"
    2726
    2827#include "Logging.h"
     
    3029#include "../../Devices/Audio/DrvAudio.h"
    3130#include "../../Devices/Audio/AudioMixBuffer.h"
     31#include "EbmlWriter.h"
    3232
    3333#include <iprt/mem.h>
     
    4040#include <VBox/err.h>
    4141
     42#include <opus.h>
    4243
    4344/*********************************************************************************************************************************
    4445*   Structures and Typedefs                                                                                                      *
    4546*********************************************************************************************************************************/
     47
     48/**
     49 * Enumeration for audio/video recording driver recording mode.
     50 */
     51typedef enum AVRECMODE
     52{
     53    /** Unknown / invalid recording mode. */
     54    AVRECMODE_UNKNOWN     = 0,
     55    /** Only record audio.
     56     *  This mode does not need to talk to the video recording driver,
     57     *  as this driver then simply creates an own WebM container. */
     58    AVRECMODE_AUDIO       = 1,
     59    /** Records audio and video.
     60     *  Needs to work together with the video recording driver in
     61     *  order to get a full-featured WebM container. */
     62    AVRECMODE_AUDIO_VIDEO = 2
     63} AVRECMODE;
     64
     65/**
     66 * Structure for keeping codec specific data.
     67 */
     68typedef struct AVRECCODEC
     69{
     70    union
     71    {
     72        struct
     73        {
     74            /** Encoder we're going to use. */
     75            OpusEncoder *pEnc;
     76        } Opus;
     77    };
     78} AVRECCODEC, *PAVRECCODEC;
     79
     80/**
     81 * Audio video recording output stream.
     82 */
     83typedef struct AVRECSTREAMOUT
     84{
     85    /** Note: Always must come first! */
     86    PDMAUDIOSTREAM       Stream;
     87    /** The PCM properties of this stream. */
     88    PDMAUDIOPCMPROPS     Props;
     89    uint64_t             old_ticks;
     90    uint64_t             cSamplesSentPerSec;
     91    /** Codec data.
     92     *  As every stream can be different, one codec per stream is needed. */
     93    AVRECCODEC           Codec;
     94} AVRECSTREAMOUT, *PAVRECSTREAMOUT;
     95
    4696/**
    4797 * Video recording audio driver instance data.
     
    57107    /** Pointer to the DrvAudio port interface that is above us. */
    58108    PPDMIAUDIOCONNECTOR  pDrvAudio;
     109    /** Recording mode. */
     110    AVRECMODE            enmMode;
     111    /** Pointer to WebM container to write recorded audio data to.
     112     *  See the AVRECMODE enumeration for more information. */
     113    WebMWriter          *pEBML;
    59114} DRVAUDIOVIDEOREC, *PDRVAUDIOVIDEOREC;
    60 
    61 typedef struct AVRECSTREAMOUT
    62 {
    63     /** Note: Always must come first! */
    64     PDMAUDIOSTREAM       Stream;
    65     /** The PCM properties of this stream. */
    66     PDMAUDIOPCMPROPS     Props;
    67     uint64_t             old_ticks;
    68     uint64_t             cSamplesSentPerSec;
    69 } AVRECSTREAMOUT, *PAVRECSTREAMOUT;
    70115
    71116/** Makes DRVAUDIOVIDEOREC out of PDMIHOSTAUDIO. */
     
    87132    if (RT_SUCCESS(rc))
    88133    {
    89         if (pCfgAcq)
    90             pCfgAcq->cSampleBufferSize = _4K; /** @todo Make this configurable. */
     134        OpusEncoder *pEnc = NULL;
     135
     136        int orc;
     137        pEnc = opus_encoder_create(48000 /* Hz */, 2 /* Stereo */, OPUS_APPLICATION_AUDIO, &orc);
     138        if (orc != OPUS_OK)
     139        {
     140            LogRel(("VideoRec: Audio codec failed to initialize: %s\n", opus_strerror(orc)));
     141            return VERR_AUDIO_BACKEND_INIT_FAILED;
     142        }
     143
     144        AssertPtr(pEnc);
     145
     146        orc = opus_encoder_ctl(pEnc, OPUS_SET_BITRATE(pCfgReq->uHz));
     147        if (orc != OPUS_OK)
     148        {
     149            LogRel(("VideoRec: Audio codec failed to set bitrate: %s\n", opus_strerror(orc)));
     150            rc = VERR_AUDIO_BACKEND_INIT_FAILED;
     151        }
     152        else
     153        {
     154            pStreamOut->Codec.Opus.pEnc = pEnc;
     155
     156            if (pCfgAcq)
     157                pCfgAcq->cSampleBufferSize = _4K; /** @todo Make this configurable. */
     158        }
    91159    }
    92160
     
    119187static DECLCALLBACK(int) drvAudioVideoRecInit(PPDMIHOSTAUDIO pInterface)
    120188{
    121     RT_NOREF(pInterface);
     189    AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
     190
    122191    LogFlowFuncEnter();
    123192
    124     return VINF_SUCCESS;
     193    PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface);
     194
     195    pThis->enmMode = AVRECMODE_AUDIO;
     196
     197    int rc;
     198
     199    try
     200    {
     201        switch (pThis->enmMode)
     202        {
     203            case AVRECMODE_AUDIO:
     204            {
     205                pThis->pEBML = new WebMWriter();
     206                pThis->pEBML->create("/tmp/test.webm", WebMWriter::Mode_Audio); /** @todo FIX! */
     207                break;
     208            }
     209
     210            case AVRECMODE_AUDIO_VIDEO:
     211            {
     212                break;
     213            }
     214
     215            default:
     216                rc = VERR_NOT_SUPPORTED;
     217                break;
     218        }
     219    }
     220    catch (std::bad_alloc)
     221    {
     222        rc = VERR_NO_MEMORY;
     223    }
     224
     225    if (RT_FAILURE(rc))
     226    {
     227        LogRel(("VideoRec: Audio recording driver failed to initialize, rc=%Rrc\n", rc));
     228    }
     229    else
     230        LogRel2(("VideoRec: Audio recording driver initialized\n"));
     231
     232    return rc;
    125233}
    126234
     
    179287
    180288    /*
    181      * Call the VRDP server with the data.
     289     * Call the encoder server with the data.
    182290     */
    183291    uint32_t cReadTotal = 0;
     292
     293    uint8_t pvDst[_4K];
     294    opus_int32 cbDst = _4K;
    184295
    185296    PPDMAUDIOSAMPLE pSamples;
     
    192303        cReadTotal = cRead;
    193304
     305        opus_int32 orc = opus_encode(pStreamOut->Codec.Opus.pEnc, (opus_int16 *)pSamples, cRead, pvDst, cbDst);
     306        if (orc != OPUS_OK)
     307            LogFunc(("Encoding (1) failed: %s\n", opus_strerror(orc)));
     308
    194309        if (rc == VINF_TRY_AGAIN)
    195310        {
     
    197312                                    &pSamples, &cRead);
    198313
     314
     315
    199316            cReadTotal += cRead;
    200317        }
     
    205322    /*
    206323     * Always report back all samples acquired, regardless of whether the
    207      * VRDP server actually did process those.
     324     * encoder actually did process those.
    208325     */
    209326    if (pcbWritten)
     
    215332
    216333
    217 static int avRecDestroyStreamIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
    218 {
    219     RT_NOREF(pInterface, pStream);
    220 
    221     return VINF_SUCCESS;
    222 }
    223 
    224 
    225334static int avRecDestroyStreamOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
    226335{
    227     RT_NOREF(pInterface);
    228     RT_NOREF(pStream);
     336    PDRVAUDIOVIDEOREC pThis      = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface);
     337    RT_NOREF(pThis);
     338    PAVRECSTREAMOUT   pStreamOut = (PAVRECSTREAMOUT)pStream;
     339
     340    if (pStreamOut->Codec.Opus.pEnc)
     341    {
     342        opus_encoder_destroy(pStreamOut->Codec.Opus.pEnc);
     343        pStreamOut->Codec.Opus.pEnc = NULL;
     344    }
    229345
    230346    return VINF_SUCCESS;
     
    254370static DECLCALLBACK(void) drvAudioVideoRecShutdown(PPDMIHOSTAUDIO pInterface)
    255371{
    256     RT_NOREF(pInterface);
     372    LogFlowFuncEnter();
     373
     374    PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface);
     375
     376    int rc;
     377
     378    switch (pThis->enmMode)
     379    {
     380        case AVRECMODE_AUDIO:
     381        {
     382            if (pThis->pEBML)
     383            {
     384                rc = pThis->pEBML->writeFooter(0 /* Hash */);
     385                AssertRC(rc);
     386
     387                pThis->pEBML->close();
     388
     389                delete pThis->pEBML;
     390                pThis->pEBML = NULL;
     391            }
     392            break;
     393        }
     394
     395        case AVRECMODE_AUDIO_VIDEO:
     396        {
     397            break;
     398        }
     399
     400        default:
     401            rc = VERR_NOT_SUPPORTED;
     402            break;
     403    }
     404
     405    LogFlowFuncLeaveRC(rc);
    257406}
    258407
     
    296445    AssertPtrReturn(pStream,    VERR_INVALID_POINTER);
    297446
    298     int rc;
    299     if (pStream->enmDir == PDMAUDIODIR_IN)
    300         rc = avRecDestroyStreamIn(pInterface,  pStream);
    301     else
    302         rc = avRecDestroyStreamOut(pInterface, pStream);
    303 
    304     return rc;
     447    if (pStream->enmDir == PDMAUDIODIR_OUT)
     448        return avRecDestroyStreamOut(pInterface, pStream);
     449
     450    return VINF_SUCCESS;
    305451}
    306452
     
    399545    AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
    400546
    401     LogRel(("Audio: Initializing video recording driver\n"));
     547    LogRel(("Audio: Initializing video recording audio driver\n"));
    402548    LogFlowFunc(("fFlags=0x%x\n", fFlags));
    403549
  • trunk/src/VBox/Main/src-client/EbmlWriter.cpp

    r63160 r65197  
    55
    66/*
    7  * Copyright (C) 2013-2016 Oracle Corporation
     7 * Copyright (C) 2013-2017 Oracle Corporation
    88 *
    99 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    220220class WebMWriter_Impl
    221221{
    222 
    223222    struct CueEntry
    224223    {
     
    228227    };
    229228
    230     bool            m_bDebug;
     229    /** Operation mode. */
     230    WebMWriter::Mode m_enmMode;
     231
     232    bool            m_fDebug;
    231233    int64_t         m_iLastPtsMs;
    232234    int64_t         m_iInitialPtsMs;
     
    249251    std::list<CueEntry> m_CueList;
    250252
    251     Ebml m_Ebml;
     253    Ebml                m_Ebml;
    252254
    253255public:
    254256
    255257    WebMWriter_Impl() :
    256         m_bDebug(false),
     258        m_enmMode(WebMWriter::Mode_Unknown),
     259        m_fDebug(false),
    257260        m_iLastPtsMs(-1),
    258261        m_iInitialPtsMs(-1),
     
    269272        m_bClusterOpen(false) {}
    270273
    271     void writeHeader(const vpx_codec_enc_cfg_t *a_pCfg,
    272                      const struct vpx_rational *a_pFps)
     274    void writeHeader(const vpx_codec_enc_cfg_t *a_pCfg, const struct vpx_rational *a_pFps)
    273275    {
    274276        m_Ebml.subStart(EBML)
     
    291293        m_uTrackPos = RTFileTell(m_Ebml.getFile());
    292294
    293         m_Ebml.subStart(Tracks)
    294               .subStart(TrackEntry)
    295               .serializeUnsignedInteger(TrackNumber, 1);
    296 
    297         m_uTrackIdPos = RTFileTell(m_Ebml.getFile());
    298 
    299         m_Ebml.serializeUnsignedInteger(TrackUID, 0, 4)
    300               .serializeUnsignedInteger(TrackType, 1)
    301               .serializeString(CodecID, "V_VP8")
    302               .subStart(Video)
    303               .serializeUnsignedInteger(PixelWidth, a_pCfg->g_w)
    304               .serializeUnsignedInteger(PixelHeight, a_pCfg->g_h)
    305               .serializeFloat(FrameRate, (double) a_pFps->num / a_pFps->den)
    306               .subEnd(Video)
    307               .subEnd(TrackEntry)
    308               .subEnd(Tracks);
    309     }
    310 
    311     void writeBlock(const vpx_codec_enc_cfg_t *a_pCfg,
    312                                 const vpx_codec_cx_pkt_t *a_pPkt)
     295        m_Ebml.subStart(Tracks);
     296
     297        /* Write video? */
     298        if (   m_enmMode == WebMWriter::Mode_Video
     299            || m_enmMode == WebMWriter::Mode_AudioVideo)
     300        {
     301            /*
     302             * Video track.
     303             */
     304            m_Ebml.subStart(TrackEntry);
     305            m_Ebml.serializeUnsignedInteger(TrackNumber, 1);
     306
     307            m_uTrackIdPos = RTFileTell(m_Ebml.getFile());
     308
     309            m_Ebml.serializeUnsignedInteger(TrackUID, 0 /* UID */, 4)
     310                  .serializeUnsignedInteger(TrackType, 1 /* Video */)
     311                  .serializeString(CodecID, "V_VP8")
     312                  .subStart(Video)
     313                  .serializeUnsignedInteger(PixelWidth, a_pCfg->g_w)
     314                  .serializeUnsignedInteger(PixelHeight, a_pCfg->g_h)
     315                  .serializeFloat(FrameRate, (double) a_pFps->num / a_pFps->den)
     316                  .subEnd(Video)
     317                  .subEnd(TrackEntry);
     318        }
     319
     320#ifdef VBOX_WITH_AUDIO_VIDEOREC
     321        if (   m_enmMode == WebMWriter::Mode_Audio
     322            || m_enmMode == WebMWriter::Mode_AudioVideo)
     323        {
     324            /*
     325             * Audio track.
     326             */
     327            m_Ebml.subStart(TrackEntry);
     328            m_Ebml.serializeUnsignedInteger(TrackNumber, 2);
     329            /** @todo Implement track's "Language" property? Currently this defaults to English ("eng"). */
     330
     331            m_Ebml.serializeUnsignedInteger(TrackUID, 1 /* UID */, 4)
     332                  .serializeUnsignedInteger(TrackType, 2 /* Audio */)
     333                  .serializeString(CodecID, "A_OPUS")
     334                  .subStart(Audio)
     335                  .serializeFloat(SamplingFrequency, 44100.0)
     336                  .serializeFloat(OutputSamplingFrequency, 44100.0)
     337                  .serializeUnsignedInteger(Channels, 2)
     338                  .serializeUnsignedInteger(BitDepth, 16)
     339                  .subEnd(Audio)
     340                  .subEnd(TrackEntry);
     341        }
     342#endif
     343
     344        m_Ebml.subEnd(Tracks);
     345    }
     346
     347    void writeBlock(const vpx_codec_enc_cfg_t *a_pCfg, const vpx_codec_cx_pkt_t *a_pPkt)
    313348    {
    314349        uint16_t uBlockTimecode = 0;
     
    316351        bool     bStartCluster = false;
    317352
    318         /* Calculate the PTS of this frame in milliseconds */
     353        /* Calculate the PTS of this frame in milliseconds. */
    319354        iPtsMs = a_pPkt->data.frame.pts * 1000
    320355                 * (uint64_t) a_pCfg->g_timebase.num / a_pCfg->g_timebase.den;
     
    326361          m_iInitialPtsMs = m_iLastPtsMs;
    327362
    328         /* Calculate the relative time of this block */
     363        /* Calculate the relative time of this block. */
    329364        if (iPtsMs - m_uClusterTimecode > 65536)
    330365            bStartCluster = 1;
     
    338373                m_Ebml.subEnd(Cluster);
    339374
    340             /* Open a new cluster */
     375            /* Open a new cluster. */
    341376            uBlockTimecode = 0;
    342377            m_bClusterOpen = true;
     
    354389        }
    355390
    356         /* Write a Simple Block */
     391        /* Write a "Simple Block". */
    357392        m_Ebml.writeClassId(SimpleBlock);
    358393        m_Ebml.writeUnsignedInteger(0x10000000u | (4 + a_pPkt->data.frame.sz), 4);
     
    389424        if (!RT_SUCCESS(rc)) throw rc;
    390425
    391         m_Ebml.serializeUnsignedInteger(TrackUID, (m_bDebug ? 0xDEADBEEF : a_u64Hash), 4);
     426        m_Ebml.serializeUnsignedInteger(TrackUID, (m_fDebug ? 0xDEADBEEF : a_u64Hash), 4);
    392427
    393428        rc = RTFileSeek(m_Ebml.getFile(), 0, RTFILE_SEEK_END, NULL);
     
    431466        char szVersion[64];
    432467        RTStrPrintf(szVersion, sizeof(szVersion), "vpxenc%s",
    433                     m_bDebug ? "" : vpx_codec_version_str());
     468                    m_fDebug ? "" : vpx_codec_version_str());
    434469
    435470        m_Ebml.subStart(Info)
     
    449484}
    450485
    451 int WebMWriter::create(const char *a_pszFilename)
    452 {
     486int WebMWriter::create(const char *a_pszFilename, WebMWriter::Mode a_enmMode)
     487{
     488    m_Impl->m_enmMode = a_enmMode;
     489
    453490    return m_Impl->m_Ebml.create(a_pszFilename);
    454491}
  • trunk/src/VBox/Main/src-client/EbmlWriter.h

    r63160 r65197  
    55
    66/*
    7  * Copyright (C) 2013-2016 Oracle Corporation
     7 * Copyright (C) 2013-2017 Oracle Corporation
    88 *
    99 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    3232class WebMWriter
    3333{
     34
    3435public:
     36
     37    /**
     38     * Operation mode -- this specifies what to write.
     39     */
     40    enum Mode
     41    {
     42        /** Unknown / invalid mode. */
     43        Mode_Unknown     = 0,
     44        /** Only writes audio. */
     45        Mode_Audio       = 1,
     46        /** Only Writes video. */
     47        Mode_Video       = 2,
     48        /** Writes audio and video. */
     49        Mode_AudioVideo  = 3
     50    };
     51
     52public:
     53
    3554    WebMWriter();
    3655    virtual ~WebMWriter();
    3756
    38     /** Creates output file
     57    /**
     58     * Creates output file.
    3959     *
    4060     * @param   a_pszFilename   Name of the file to create.
     61     * @param   a_enmMode       Operation mode.
    4162     *
    4263     * @returns VBox status code. */
    43     int create(const char *a_pszFilename);
     64    int create(const char *a_pszFilename, WebMWriter::Mode a_enmMode);
    4465
    4566    /* Closes output file. */
    4667    void close();
    4768
    48     /** Writes WebM header to file.
    49      *
     69    /**
     70     * Writes WebM header to file.
    5071     * Should be called before any writeBlock call.
    5172     *
     
    5778    int writeHeader(const vpx_codec_enc_cfg_t *a_pCfg, const vpx_rational *a_pFps);
    5879
    59     /** Writes a block of compressed data
     80    /**
     81     * Writes a block of compressed data.
    6082     *
    6183     * @param a_pCfg Pointer to VPX Codec configuration structure.
     
    6688    int writeBlock(const vpx_codec_enc_cfg_t *a_pCfg, const vpx_codec_cx_pkt_t *a_pPkt);
    6789
    68     /** Writes WebM footer.
    69      *
     90    /**
     91     * Writes WebM footer.
    7092     * No other write functions should be called after this one.
    7193     *
     
    7698    int writeFooter(uint32_t a_u64Hash);
    7799
    78     /** Gets current output file size.
     100    /**
     101     * Gets current output file size.
    79102     *
    80103     * @returns File size in bytes.
    81104     */
    82     uint64_t getFileSize();
     105    uint64_t getFileSize(void);
    83106
    84     /** Gets current free storage space
    85      * available for the file.
     107    /**
     108     * Gets current free storage space available for the file.
    86109     *
    87110     * @returns Available storage free space.
    88111     */
    89     uint64_t getAvailableSpace();
     112    uint64_t getAvailableSpace(void);
    90113
    91114private:
     115
    92116    /** WebMWriter implementation.
    93      * To isolate some include files */
     117     *  To isolate some include files. */
    94118    WebMWriter_Impl *m_Impl;
    95119
     
    97121};
    98122
    99 #endif
     123#endif /* ____EBMLWRITER */
  • trunk/src/VBox/Main/src-client/VideoRec.cpp

    r65177 r65197  
    647647     * other important file, causing unintentional data loss. */
    648648
    649     int rc = pStream->pEBML->create(pszFile);
     649    int rc = pStream->pEBML->create(pszFile, WebMWriter::Mode_AudioVideo); /** @todo Make mode configurable. */
    650650    if (RT_FAILURE(rc))
    651651    {
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