VirtualBox

Changeset 65410 in vbox for trunk/src/VBox/Main/src-client


Ignore:
Timestamp:
Jan 24, 2017 10:06:12 AM (8 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
113018
Message:

VideoRec: Interface work, added container / codec configuration stuff.

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

Legend:

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

    r65162 r65410  
    53945394            int vrc = VINF_SUCCESS;
    53955395            if (SUCCEEDED(rc))
    5396                 vrc = mDisplay->i_VideoCaptureEnableScreens(ComSafeArrayAsInParam(screens));
     5396                vrc = mDisplay->i_videoCaptureEnableScreens(ComSafeArrayAsInParam(screens));
    53975397            if (RT_SUCCESS(vrc))
    53985398            {
    53995399                if (fEnabled)
    54005400                {
    5401                     vrc = mDisplay->i_VideoCaptureStart();
     5401                    vrc = mDisplay->i_videoCaptureStart();
    54025402                    if (RT_FAILURE(vrc))
    54035403                        rc = setError(E_FAIL, tr("Unable to start video capturing (%Rrc)"), vrc);
    54045404                }
    54055405                else
    5406                     mDisplay->i_VideoCaptureStop();
     5406                    mDisplay->i_videoCaptureStop();
    54075407            }
    54085408            else
     
    65756575    return S_OK;
    65766576}
     6577
     6578#ifdef VBOX_WITH_AUDIO_VIDEOREC
     6579HRESULT Console::i_audioVideoRecSendAudio(const void *pvData, size_t cbData, uint64_t uTimestampMs)
     6580{
     6581    if (mDisplay)
     6582    {
     6583        int rc2 = mDisplay->i_videoCaptureSendAudio(pvData, cbData, uTimestampMs);
     6584        AssertRC(rc2);
     6585    }
     6586
     6587    return S_OK;
     6588}
     6589#endif /* VBOX_WITH_AUDIO_VIDEOREC */
    65776590
    65786591/**
  • trunk/src/VBox/Main/src-client/ConsoleImpl2.cpp

    r65172 r65410  
    29682968            InsertConfigString(pCfg, "StreamName", bstr);
    29692969            InsertConfigInteger(pCfg, "Object", (uintptr_t)mAudioVideoRec);
     2970            InsertConfigInteger(pCfg, "ObjectConsole", (uintptr_t)this /* Console */);
    29702971#endif /* VBOX_WITH_AUDIO_VIDEOREC */
    29712972
  • trunk/src/VBox/Main/src-client/DisplayImpl.cpp

    r65401 r65410  
    131131    mfHostCursorCapabilities = 0;
    132132#endif
     133
    133134#ifdef VBOX_WITH_VIDEOREC
    134135    rc = RTCritSectInit(&mVideoCaptureLock);
     
    145146    AssertRC(rc);
    146147#endif
     148
    147149#ifdef VBOX_WITH_CROGL
    148150    RT_ZERO(mCrOglCallbacks);
     
    22632265}
    22642266
    2265 
    2266 int Display::i_VideoCaptureEnableScreens(ComSafeArrayIn(BOOL, aScreens))
     2267int Display::i_videoCaptureEnableScreens(ComSafeArrayIn(BOOL, aScreens))
    22672268{
    22682269#ifdef VBOX_WITH_VIDEOREC
     
    22862287
    22872288/**
     2289 * Sends belonging audio samples to the video capturing code.
     2290 * Does nothing if capturing is disabled or if audio support for video capturing is disabled.
     2291 *
     2292 * @returns IPRT status code.
     2293 * @param   pvData              Audio data.
     2294 * @param   cbData              Size (in bytes) of audio data.
     2295 * @param   uTimestampMs        Timestamp (in ms) of the audio data.
     2296 */
     2297int Display::i_videoCaptureSendAudio(const void *pvData, size_t cbData, uint64_t uTimestampMs)
     2298{
     2299#ifdef VBOX_WITH_AUDIO_VIDEOREC
     2300    if (!VideoRecIsEnabled(mpVideoRecCtx))
     2301        return VINF_SUCCESS;
     2302
     2303    return VideoRecSendAudio(mpVideoRecCtx, pvData, cbData, uTimestampMs);
     2304#else
     2305    RT_NOREF(pvData, cbData, uTimestampMs);
     2306    return VERR_NOT_SUPPORTED;
     2307#endif
     2308}
     2309
     2310/**
    22882311 * Start video capturing. Does nothing if capturing is already active.
     2312 *
     2313 * @returns IPRT status code.
    22892314 */
    2290 int Display::i_VideoCaptureStart()
     2315int Display::i_videoCaptureStart(void)
    22912316{
    22922317#ifdef VBOX_WITH_VIDEOREC
     
    24032428 * Stop video capturing. Does nothing if video capturing is not active.
    24042429 */
    2405 void Display::i_VideoCaptureStop()
     2430void Display::i_videoCaptureStop()
    24062431{
    24072432#ifdef VBOX_WITH_VIDEOREC
     
    31853210                if (VideoRecLimitReached(pDisplay->mpVideoRecCtx, uScreenId, u64Now))
    31863211                {
    3187                     pDisplay->i_VideoCaptureStop();
     3212                    pDisplay->i_videoCaptureStop();
    31883213                    pDisplay->mParent->i_machine()->COMSETTER(VideoCaptureEnabled)(false);
    31893214                    break;
     
    42874312        AutoWriteLock displayLock(pThis->pDisplay COMMA_LOCKVAL_SRC_POS);
    42884313#ifdef VBOX_WITH_VIDEOREC
    4289         pThis->pDisplay->i_VideoCaptureStop();
     4314        pThis->pDisplay->i_videoCaptureStop();
    42904315#endif
    42914316#ifdef VBOX_WITH_CRHGSMI
     
    44054430    if (fEnabled)
    44064431    {
    4407         rc = pDisplay->i_VideoCaptureStart();
     4432        rc = pDisplay->i_videoCaptureStart();
    44084433        fireVideoCaptureChangedEvent(pDisplay->mParent->i_getEventSource());
    44094434    }
  • trunk/src/VBox/Main/src-client/DrvAudioVideoRec.cpp

    r65391 r65410  
    1616 */
    1717
     18/**
     19 * This driver is part of Main and is responsible for providing audio
     20 * data to Main's video capturing feature.
     21 *
     22 * The driver itself implements a PDM host audio backend, which in turn
     23 * provides the driver with the required audio data and audio events.
     24 *
     25 * For now there is support for the following destinations (called "sinks"):
     26 *
     27 * - Direct writing of .webm files to the host.
     28 * - Communicating with Main via the Console object to send the encoded audio data to.
     29 *   The Console object in turn then will route the data to the Display / video capturing interface then.
     30 */
    1831
    1932/*********************************************************************************************************************************
     
    4861
    4962/**
    50  * Enumeration for audio/video recording driver recording mode.
    51  */
    52 typedef enum AVRECMODE
    53 {
    54     /** Unknown / invalid recording mode. */
    55     AVRECMODE_UNKNOWN     = 0,
    56     /** Only record audio.
    57      *  This mode does not need to talk to the video recording driver,
    58      *  as this driver then simply creates an own WebM container. */
    59     AVRECMODE_AUDIO       = 1,
    60     /** Records audio and video.
    61      *  Needs to work together with the video recording driver in
    62      *  order to get a full-featured WebM container. */
    63     AVRECMODE_AUDIO_VIDEO = 2
    64 } AVRECMODE;
    65 
    66 /**
    67  * Structure for keeping codec specific data.
     63 * Enumeration for specifying the recording container type.
     64 */
     65typedef enum AVRECCONTAINERTYPE
     66{
     67    /** Unknown / invalid container type. */
     68    AVRECCONTAINERTYPE_UNKNOWN      = 0,
     69    /** Recorded data goes to Main / Console. */
     70    AVRECCONTAINERTYPE_MAIN_CONSOLE = 1,
     71    /** Recorded data will be written a .webm file. */
     72    AVRECCONTAINERTYPE_WEBM         = 2
     73} AVRECCONTAINERTYPE;
     74
     75/**
     76 * Structure for keeping generic container parameters.
     77 */
     78typedef struct AVRECCONTAINERPARMS
     79{
     80    /** The container's type. */
     81    AVRECCONTAINERTYPE      enmType;
     82
     83} AVRECCONTAINERPARMS, *PAVRECCONTAINERPARMS;
     84
     85/**
     86 * Structure for keeping container-specific data.
     87 */
     88typedef struct AVRECCONTAINER
     89{
     90    /** Generic container parameters. */
     91    AVRECCONTAINERPARMS     Parms;
     92
     93    union
     94    {
     95        struct
     96        {
     97            /** Pointer to Console. */
     98            Console        *pConsole;
     99        } Main;
     100
     101        struct
     102        {
     103            /** Pointer to WebM container to write recorded audio data to.
     104             *  See the AVRECMODE enumeration for more information. */
     105            WebMWriter     *pWebM;
     106            /** Assigned track number from WebM container. */
     107            uint8_t         uTrack;
     108        } WebM;
     109    };
     110} AVRECCONTAINER, *PAVRECCONTAINER;
     111
     112/**
     113 * Structure for keeping generic codec parameters.
     114 */
     115typedef struct AVRECCODECPARMS
     116{
     117    /** The encoding rate to use. */
     118    uint32_t                uHz;
     119    /** Duration of the frame in samples (per channel).
     120     *
     121     *  For Opus, valid frame size are:
     122     *  ms           Frame size
     123     *  2.5          120
     124     *  5            240
     125     *  10           480
     126     *  20 (Default) 960
     127     *  40           1920
     128     *  60           2880
     129     */
     130    /** Number of audio channels to encode.
     131     *  Currently we only supported stereo (2) channels. */
     132    uint8_t                 cChannels;
     133    /** The codec's bitrate. 0 if not used / cannot be specified. */
     134    uint32_t                uBitrate;
     135
     136} AVRECCODECPARMS, *PAVRECCODECPARMS;
     137
     138/**
     139 * Structure for keeping codec-specific data.
    68140 */
    69141typedef struct AVRECCODEC
    70142{
     143    /** Generic codec parameters. */
     144    AVRECCODECPARMS         Parms;
    71145    union
    72146    {
     
    76150            /** Encoder we're going to use. */
    77151            OpusEncoder    *pEnc;
    78             /** The encoding rate to use. */
    79             uint32_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              */
    91152            uint32_t        csFrame;
    92153            /** The maximum frame size (in samples) we can handle. */
    93154            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
    100155        } Opus;
    101156#endif /* VBOX_WITH_LIBOPUS */
    102157    };
     158
     159#ifdef VBOX_WITH_STATISTICS /** @todo Make these real STAM values. */
     160    struct
     161    {
     162        /** Number of frames encoded. */
     163        uint64_t        cEncFrames;
     164        /** Total time (in ms) of already encoded audio data. */
     165        uint64_t        msEncTotal;
     166    } STAM;
     167#endif /* VBOX_WITH_STATISTICS */
     168
    103169} AVRECCODEC, *PAVRECCODEC;
     170
     171typedef struct AVRECSINK
     172{
     173    /** @todo Add types for container / codec as soon as we implement more stuff. */
     174
     175    /** Container data to use for data processing. */
     176    AVRECCONTAINER       Con;
     177    /** Codec data this stream uses for encoding. */
     178    AVRECCODEC           Codec;
     179} AVRECSINK, *PAVRECSINK;
    104180
    105181/**
     
    112188    /** The PCM properties of this stream. */
    113189    PDMAUDIOPCMPROPS     Props;
    114     /** Codec-specific data.
    115      *  As every stream can be different, one codec per stream is needed. */
    116     AVRECCODEC           Codec;
    117190    /** (Audio) frame buffer. */
    118191    PRTCIRCBUF           pCircBuf;
    119     /** Pointer to WebM container to write recorded audio data to.
    120      *  See the AVRECMODE enumeration for more information. */
    121     WebMWriter          *pWebM;
    122     /** Assigned track number from WebM container. */
    123     uint8_t              uTrack;
     192    /** Pointer to sink to use for writing. */
     193    PAVRECSINK           pSink;
    124194} AVRECSTREAMOUT, *PAVRECSTREAMOUT;
    125195
     
    135205    /** Pointer to host audio interface. */
    136206    PDMIHOSTAUDIO        IHostAudio;
     207    /** Pointer to the console object. */
     208    ComObjPtr<Console>   pConsole;
    137209    /** Pointer to the DrvAudio port interface that is above us. */
    138210    PPDMIAUDIOCONNECTOR  pDrvAudio;
    139     /** Recording mode. */
    140     AVRECMODE            enmMode;
     211    /** The driver's sink for writing output to. */
     212    AVRECSINK            Sink;
    141213} DRVAUDIOVIDEOREC, *PDRVAUDIOVIDEOREC;
    142214
     
    145217    ( (PDRVAUDIOVIDEOREC)((uintptr_t)pInterface - RT_OFFSETOF(DRVAUDIOVIDEOREC, IHostAudio)) )
    146218
    147 
    148 static int avRecCreateStreamOut(PPDMIHOSTAUDIO pInterface,
    149                                 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
    150 {
    151     AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
    152     AssertPtrReturn(pStream,    VERR_INVALID_POINTER);
    153     AssertPtrReturn(pCfgReq,    VERR_INVALID_POINTER);
    154     AssertPtrReturn(pCfgAcq,    VERR_INVALID_POINTER);
    155 
    156     PAVRECSTREAMOUT pStreamOut = (PAVRECSTREAMOUT)pStream;
    157 
    158     int rc = DrvAudioHlpStreamCfgToProps(pCfgReq, &pStreamOut->Props);
    159     if (RT_FAILURE(rc))
    160         return rc;
    161 
    162 #ifdef VBOX_WITH_LIBOPUS
    163     PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface);
    164 
    165     uint32_t uHz = pStreamOut->Props.uHz;
     219/**
     220 * Initializes a recording sink.
     221 *
     222 * @returns IPRT status code.
     223 * @param   pThis               Driver instance.
     224 * @param   pSink               Sink to initialize.
     225 * @param   pConParms           Container parameters to set.
     226 * @param   pCodecParms         Codec parameters to set.
     227 */
     228static int avRecSinkInit(PDRVAUDIOVIDEOREC pThis, PAVRECSINK pSink, PAVRECCONTAINERPARMS pConParms, PAVRECCODECPARMS pCodecParms)
     229{
     230    uint32_t uHz = pCodecParms->uHz;
    166231
    167232    /* Opus only supports certain input sample rates in an efficient manner.
     
    173238    else     uHz = 8000;
    174239
    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. */
    189     if (pThis->enmMode == AVRECMODE_AUDIO)
    190     {
    191         pStreamOut->pWebM = new WebMWriter();
    192         rc = pStreamOut->pWebM->Create("/tmp/acap.webm", RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE, /** @todo Fix path! */
    193                                        WebMWriter::AudioCodec_Opus, WebMWriter::VideoCodec_None);
    194         if (RT_SUCCESS(rc))
    195             rc = pStreamOut->pWebM->AddAudioTrack(pStreamOut->Codec.Opus.uHz, pStreamOut->Props.cChannels, pStreamOut->Props.cBits,
    196                                                   &pStreamOut->uTrack);
    197     }
    198 
    199     if (RT_FAILURE(rc))
    200         return rc;
    201 
    202     rc = RTCircBufCreate(&pStreamOut->pCircBuf, (pStreamOut->Codec.Opus.csFrame * pStreamOut->Props.cChannels) * sizeof(uint16_t));
    203     if (RT_SUCCESS(rc))
    204     {
    205         OpusEncoder *pEnc = NULL;
    206 
    207         int orc;
    208         pEnc = opus_encoder_create(pStreamOut->Codec.Opus.uHz, pStreamOut->Props.cChannels, OPUS_APPLICATION_AUDIO, &orc);
    209         if (orc != OPUS_OK)
    210         {
    211             LogRel(("VideoRec: Audio codec failed to initialize: %s\n", opus_strerror(orc)));
    212             return VERR_AUDIO_BACKEND_INIT_FAILED;
    213         }
    214 
    215         AssertPtr(pEnc);
    216 
    217         opus_encoder_ctl(pEnc, OPUS_SET_BITRATE(196000)); /** @todo Make this configurable. */
    218         if (orc != OPUS_OK)
    219         {
    220             LogRel(("VideoRec: Audio codec failed to set bitrate: %s\n", opus_strerror(orc)));
    221             rc = VERR_AUDIO_BACKEND_INIT_FAILED;
    222         }
    223         else
    224         {
    225             pStreamOut->Codec.Opus.pEnc = pEnc;
    226 
    227             if (pCfgAcq)
     240    OpusEncoder *pEnc = NULL;
     241
     242    int orc;
     243    pEnc = opus_encoder_create(pCodecParms->uHz, pCodecParms->cChannels, OPUS_APPLICATION_AUDIO, &orc);
     244    if (orc != OPUS_OK)
     245    {
     246        LogRel(("VideoRec: Audio codec failed to initialize: %s\n", opus_strerror(orc)));
     247        return VERR_AUDIO_BACKEND_INIT_FAILED;
     248    }
     249
     250    AssertPtr(pEnc);
     251
     252    opus_encoder_ctl(pEnc, OPUS_SET_BITRATE(pCodecParms->uBitrate));
     253    if (orc != OPUS_OK)
     254    {
     255        LogRel(("VideoRec: Audio codec failed to set bitrate (%RU32): %s\n", pCodecParms->uBitrate, opus_strerror(orc)));
     256        return VERR_AUDIO_BACKEND_INIT_FAILED;
     257    }
     258
     259    LogRel(("VideoRec: Recording audio in %RU16Hz, %RU8 channels, %RU32 bpS\n",
     260            pCodecParms->uHz, pCodecParms->cChannels, pCodecParms->uBitrate / 1000));
     261
     262    int rc;
     263
     264    try
     265    {
     266        switch (pConParms->enmType)
     267        {
     268            case AVRECCONTAINERTYPE_MAIN_CONSOLE:
    228269            {
    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. */
     270                pSink->Con.Main.pConsole = pThis->pConsole;
     271
     272                rc = VINF_SUCCESS;
     273                break;
    233274            }
    234275
    235             LogRel(("VideoRec: Recording audio in %RU16Hz, %RU8 channels, %RU16 bpS\n",
    236                     uHz, pStreamOut->Props.cChannels, pStreamOut->Props.cBits));
    237         }
    238     }
    239 #else
    240     rc = VERR_NOT_SUPPORTED;
    241 #endif /* VBOX_WITH_LIBOPUS */
    242 
    243     LogFlowFuncLeaveRC(rc);
    244     return rc;
    245 }
    246 
    247 
    248 static int avRecControlStreamOut(PPDMIHOSTAUDIO pInterface,
    249                                  PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
    250 {
    251     AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
    252     AssertPtrReturn(pStream,    VERR_INVALID_POINTER);
    253     RT_NOREF(enmStreamCmd);
    254 
    255     PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface);
    256     RT_NOREF(pThis);
    257 
    258     LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
    259 
    260     switch (enmStreamCmd)
    261     {
    262         case PDMAUDIOSTREAMCMD_ENABLE:
    263         case PDMAUDIOSTREAMCMD_RESUME:
    264             break;
    265 
    266         case PDMAUDIOSTREAMCMD_DISABLE:
    267         {
    268             AudioMixBufReset(&pStream->MixBuf);
    269             break;
    270         }
    271 
    272         case PDMAUDIOSTREAMCMD_PAUSE:
    273             break;
    274 
    275         default:
    276             AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));
    277             break;
    278     }
    279 
    280     return VINF_SUCCESS;
    281 }
    282 
    283 
    284 /**
    285  * @interface_method_impl{PDMIHOSTAUDIO,pfnInit}
    286  */
    287 static DECLCALLBACK(int) drvAudioVideoRecInit(PPDMIHOSTAUDIO pInterface)
    288 {
    289     AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
    290 
    291     LogFlowFuncEnter();
    292 
    293     PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface);
    294 
    295     pThis->enmMode = AVRECMODE_AUDIO; /** @todo Fix mode! */
    296 
    297     int rc;
    298 
    299     try
    300     {
    301         switch (pThis->enmMode)
    302         {
    303             /* In audio-only mode we're creating our own WebM writer instance,
    304              * as we don't have to synchronize with any external source, such as video recording data.*/
    305             case AVRECMODE_AUDIO:
     276            case AVRECCONTAINERTYPE_WEBM:
    306277            {
    307278                rc = VINF_SUCCESS;
     
    309280            }
    310281
    311             case AVRECMODE_AUDIO_VIDEO:
    312             {
    313                 rc = VERR_NOT_SUPPORTED;
    314                 break;
    315             }
    316 
    317282            default:
    318283                rc = VERR_NOT_SUPPORTED;
     
    325290    }
    326291
     292    if (RT_SUCCESS(rc))
     293    {
     294        pSink->Codec.Parms.uHz       = uHz;
     295        pSink->Codec.Parms.cChannels = pCodecParms->cChannels;
     296        pSink->Codec.Parms.uBitrate  = pCodecParms->uBitrate;
     297
     298        pSink->Codec.Opus.pEnc       = pEnc;
     299        pSink->Codec.Opus.csFrame    = uHz / 50;
     300
     301#ifdef VBOX_WITH_STATISTICS
     302        pSink->Codec.STAM.cEncFrames = 0;
     303        pSink->Codec.STAM.msEncTotal = 0;
     304#endif
     305
     306        /* Calculate the maximum frame size. */
     307        pSink->Codec.Opus.csFrameMax = 48000                   /* Maximum sample rate Opus can handle */
     308                                     * pCodecParms->cChannels; /* Number of channels */
     309    }
     310
     311    return rc;
     312}
     313
     314
     315/**
     316 * Shuts down (closes) a recording sink,
     317 *
     318 * @returns IPRT status code.
     319 * @param   pSink               Recording sink to shut down.
     320 */
     321static void avRecSinkShutdown(PAVRECSINK pSink)
     322{
     323    AssertPtrReturnVoid(pSink);
     324
     325#ifdef VBOX_WITH_LIBOPUS
     326    if (pSink->Codec.Opus.pEnc)
     327    {
     328        opus_encoder_destroy(pSink->Codec.Opus.pEnc);
     329        pSink->Codec.Opus.pEnc = NULL;
     330    }
     331#endif
     332    switch (pSink->Con.Parms.enmType)
     333    {
     334        case AVRECCONTAINERTYPE_WEBM:
     335        {
     336            if (pSink->Con.WebM.pWebM)
     337            {
     338                int rc2 = pSink->Con.WebM.pWebM->Close();
     339                AssertRC(rc2);
     340
     341                delete pSink->Con.WebM.pWebM;
     342                pSink->Con.WebM.pWebM = NULL;
     343            }
     344            break;
     345        }
     346
     347        case AVRECCONTAINERTYPE_MAIN_CONSOLE:
     348        default:
     349            break;
     350    }
     351}
     352
     353
     354/**
     355 * Creates an audio output stream and associates it with the specified recording sink.
     356 *
     357 * @returns IPRT status code.
     358 * @param   pThis               Driver instance.
     359 * @param   pStream             Audio output stream to create.
     360 * @param   pSink               Recording sink to associate audio output stream to.
     361 * @param   pCfgReq             Requested configuration by the audio backend.
     362 * @param   pCfgAcq             Acquired configuration by the audio output stream.
     363 */
     364static int avRecCreateStreamOut(PDRVAUDIOVIDEOREC pThis, PPDMAUDIOSTREAM pStream,
     365                                PAVRECSINK pSink, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
     366{
     367    AssertPtrReturn(pThis,   VERR_INVALID_POINTER);
     368    AssertPtrReturn(pStream, VERR_INVALID_POINTER);
     369    AssertPtrReturn(pSink,   VERR_INVALID_POINTER);
     370    AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
     371    AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
     372
     373    PAVRECSTREAMOUT pStreamOut = (PAVRECSTREAMOUT)pStream;
     374
     375    if (pCfgReq->DestSource.Dest != PDMAUDIOPLAYBACKDEST_FRONT)
     376    {
     377        AssertFailed();
     378
     379        if (pCfgAcq)
     380            pCfgAcq->cSampleBufferSize = 0;
     381
     382        LogRel2(("VideoRec: Support for surround audio not implemented yet\n"));
     383        return VERR_NOT_SUPPORTED;
     384    }
     385
     386    int rc = DrvAudioHlpStreamCfgToProps(pCfgReq, &pStreamOut->Props);
     387    if (RT_FAILURE(rc))
     388        return rc;
     389
     390#ifdef VBOX_WITH_LIBOPUS
     391    /* If we only record audio, create our own WebM writer instance here. */
     392    if (pSink->Con.Parms.enmType == AVRECCONTAINERTYPE_WEBM)
     393    {
     394        pSink->Con.WebM.pWebM = new WebMWriter();
     395        rc = pSink->Con.WebM.pWebM->Create("/tmp/acap.webm", RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE, /** @todo Fix path! */
     396                                           WebMWriter::AudioCodec_Opus, WebMWriter::VideoCodec_None);
     397        if (RT_SUCCESS(rc))
     398            rc = pSink->Con.WebM.pWebM->AddAudioTrack(pSink->Codec.Parms.uHz, pStreamOut->Props.cChannels, pStreamOut->Props.cBits,
     399                                                      &pSink->Con.WebM.uTrack);
     400    }
     401
     402    if (RT_FAILURE(rc))
     403        return rc;
     404
     405    rc = RTCircBufCreate(&pStreamOut->pCircBuf, (pSink->Codec.Opus.csFrame * pSink->Codec.Parms.cChannels) * sizeof(uint16_t));
     406    if (RT_SUCCESS(rc))
     407    {
     408        pStreamOut->pSink = pSink; /* Assign sink to stream. */
     409
     410        if (pCfgAcq)
     411        {
     412            /* Make sure to let the driver backend know that we need the audio data in
     413             * a specific sampling rate Opus is optimized for. */
     414            pCfgAcq->uHz               = pSink->Codec.Parms.uHz;
     415            pCfgAcq->cSampleBufferSize = _4K; /** @todo Make this configurable. */
     416        }
     417    }
     418#else
     419    RT_NOREF(pThis, pSink, pStream, pCfgReq, pCfgAcq);
     420    rc = VERR_NOT_SUPPORTED;
     421#endif /* VBOX_WITH_LIBOPUS */
     422
     423    LogFlowFuncLeaveRC(rc);
     424    return rc;
     425}
     426
     427
     428/**
     429 * Destroys (closes) an audio output stream.
     430 *
     431 * @returns IPRT status code.
     432 * @param   pThis               Driver instance.
     433 * @param   pStream             Audio output stream to destroy.
     434 */
     435static int avRecDestroyStreamOut(PDRVAUDIOVIDEOREC pThis, PPDMAUDIOSTREAM pStream)
     436{
     437    RT_NOREF(pThis);
     438    PAVRECSTREAMOUT pStreamOut = (PAVRECSTREAMOUT)pStream;
     439
     440    if (pStreamOut->pCircBuf)
     441    {
     442        RTCircBufDestroy(pStreamOut->pCircBuf);
     443        pStreamOut->pCircBuf = NULL;
     444    }
     445
     446    return VINF_SUCCESS;
     447}
     448
     449
     450/**
     451 * Controls an audio output stream
     452 *
     453 * @returns IPRT status code.
     454 * @param   pThis               Driver instance.
     455 * @param   pStream             Audio output stream to control.
     456 * @param   enmStreamCmd        Stream command to issue.
     457 */
     458static int avRecControlStreamOut(PDRVAUDIOVIDEOREC pThis,
     459                                 PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
     460{
     461    AssertPtrReturn(pThis,   VERR_INVALID_POINTER);
     462    AssertPtrReturn(pStream, VERR_INVALID_POINTER);
     463    RT_NOREF(enmStreamCmd);
     464
     465    RT_NOREF(pThis);
     466
     467    LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
     468
     469    switch (enmStreamCmd)
     470    {
     471        case PDMAUDIOSTREAMCMD_ENABLE:
     472        case PDMAUDIOSTREAMCMD_RESUME:
     473            break;
     474
     475        case PDMAUDIOSTREAMCMD_DISABLE:
     476        {
     477            AudioMixBufReset(&pStream->MixBuf);
     478            break;
     479        }
     480
     481        case PDMAUDIOSTREAMCMD_PAUSE:
     482            break;
     483
     484        default:
     485            AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));
     486            break;
     487    }
     488
     489    return VINF_SUCCESS;
     490}
     491
     492
     493/**
     494 * @interface_method_impl{PDMIHOSTAUDIO,pfnInit}
     495 */
     496static DECLCALLBACK(int) drvAudioVideoRecInit(PPDMIHOSTAUDIO pInterface)
     497{
     498    AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
     499
     500    LogFlowFuncEnter();
     501
     502    PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface);
     503
     504    AVRECCONTAINERPARMS ContainerParms;
     505    ContainerParms.enmType = AVRECCONTAINERTYPE_MAIN_CONSOLE; /** @todo Make this configurable. */
     506
     507    AVRECCODECPARMS CodecParms;
     508    CodecParms.uHz       = 48000;  /** @todo Make this configurable. */
     509    CodecParms.cChannels = 2;      /** @todo Make this configurable. */
     510    CodecParms.uBitrate  = 196000; /** @todo Make this configurable. */
     511
     512    int rc = avRecSinkInit(pThis, &pThis->Sink, &ContainerParms, &CodecParms);
    327513    if (RT_FAILURE(rc))
    328514    {
     
    385571     */
    386572#ifdef VBOX_WITH_LIBOPUS
     573    PAVRECSINK pSink    = pStreamOut->pSink;
     574    AssertPtr(pSink);
    387575    PRTCIRCBUF pCircBuf = pStreamOut->pCircBuf;
     576    AssertPtr(pCircBuf);
    388577
    389578    void  *pvBuf;
     
    430619    size_t  cbSrc;
    431620
    432     const uint32_t csFrame = pStreamOut->Codec.Opus.csFrame;
     621    const uint32_t csFrame = pSink->Codec.Opus.csFrame;
    433622    const uint32_t cbFrame = AUDIOMIXBUF_S2B(&pStream->MixBuf, csFrame);
    434623
     
    455644        }
    456645
    457 #ifdef DEBUG_andy
     646# ifdef DEBUG_andy
    458647        RTFILE fh;
    459648        RTFileOpen(&fh, "/tmp/acap.pcm", RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
    460649        RTFileWrite(fh, abSrc, cbSrc, NULL);
    461650        RTFileClose(fh);
    462 #endif
     651# endif
    463652
    464653        Assert(cbSrc == cbFrame);
     
    475664
    476665        /* Call the encoder to encode one frame per iteration. */
    477         opus_int32 cbWritten = opus_encode(pStreamOut->Codec.Opus.pEnc,
     666        opus_int32 cbWritten = opus_encode(pSink->Codec.Opus.pEnc,
    478667                                           (opus_int16 *)abSrc, csFrame, abDst, cbDst);
    479668        if (cbWritten > 0)
    480669        {
    481 #ifdef DEBUG
     670# ifdef VBOX_WITH_STATISTICS
    482671            /* Get overall frames encoded. */
    483672            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);
     673            uint32_t cEncSamplesPerFrame = opus_packet_get_samples_per_frame(abDst, pSink->Codec.Parms.uHz);
    485674            uint32_t csEnc               = cEncFrames * cEncSamplesPerFrame;
    486675
    487             pStreamOut->Codec.Opus.cEncFrames += cEncFrames;
    488             pStreamOut->Codec.Opus.msEncTotal += 20 /* Default 20 ms */ * cEncFrames;
     676            pSink->Codec.STAM.cEncFrames += cEncFrames;
     677            pSink->Codec.STAM.msEncTotal += 20 /* Default 20 ms */ * cEncFrames;
    489678
    490679            LogFunc(("%RU64ms [%RU64 frames]: cbSrc=%zu, cbDst=%zu, cEncFrames=%RU32, cEncSamplesPerFrame=%RU32, csEnc=%RU32\n",
    491                      pStreamOut->Codec.Opus.msEncTotal, pStreamOut->Codec.Opus.cEncFrames,
     680                     pSink->Codec.STAM.msEncTotal, pSink->Codec.STAM.cEncFrames,
    492681                     cbSrc, cbDst, cEncFrames, cEncSamplesPerFrame, csEnc));
    493 #endif
     682# endif
    494683            Assert((uint32_t)cbWritten <= cbDst);
    495684            cbDst = RT_MIN((uint32_t)cbWritten, cbDst); /* Update cbDst to actual bytes encoded (written). */
    496685
    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);
     686            switch (pSink->Con.Parms.enmType)
     687            {
     688                case AVRECCONTAINERTYPE_MAIN_CONSOLE:
     689                {
     690                    HRESULT hr = pSink->Con.Main.pConsole->i_audioVideoRecSendAudio(abDst, cbDst, RTTimeMilliTS() /* Now */);
     691                    Assert(hr == S_OK);
     692
     693                    break;
     694                }
     695
     696                case AVRECCONTAINERTYPE_WEBM:
     697                {
     698                    WebMWriter::BlockData_Opus blockData = { abDst, cbDst };
     699                    rc = pSink->Con.WebM.pWebM->WriteBlock(pSink->Con.WebM.uTrack, &blockData, sizeof(blockData));
     700                    AssertRC(rc);
     701
     702                    break;
     703                }
     704
     705                default:
     706                    AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
     707                    break;
     708            }
    501709        }
    502710        else if (cbWritten < 0)
     
    525733
    526734
    527 static int avRecDestroyStreamOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
    528 {
    529     PDRVAUDIOVIDEOREC pThis      = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface);
    530     RT_NOREF(pThis);
    531     PAVRECSTREAMOUT   pStreamOut = (PAVRECSTREAMOUT)pStream;
    532 
    533     if (pStreamOut->pCircBuf)
    534     {
    535         RTCircBufDestroy(pStreamOut->pCircBuf);
    536         pStreamOut->pCircBuf = NULL;
    537     }
    538 
    539 #ifdef VBOX_WITH_LIBOPUS
    540     if (pStreamOut->Codec.Opus.pEnc)
    541     {
    542         opus_encoder_destroy(pStreamOut->Codec.Opus.pEnc);
    543         pStreamOut->Codec.Opus.pEnc = NULL;
    544     }
    545 #endif
    546 
    547     switch (pThis->enmMode)
    548     {
    549         case AVRECMODE_AUDIO:
    550         {
    551             if (pStreamOut->pWebM)
    552                 pStreamOut->pWebM->Close();
    553             break;
    554         }
    555 
    556         case AVRECMODE_AUDIO_VIDEO:
    557         default:
    558             break;
    559     }
    560 
    561     return VINF_SUCCESS;
    562 }
    563 
    564 
    565735/**
    566736 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
     
    589759    PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface);
    590760
    591     switch (pThis->enmMode)
    592     {
    593         case AVRECMODE_AUDIO:
    594         case AVRECMODE_AUDIO_VIDEO:
    595         default:
    596             break;
    597     }
     761    avRecSinkShutdown(&pThis->Sink);
    598762}
    599763
     
    622786    AssertPtrReturn(pCfgAcq,    VERR_INVALID_POINTER);
    623787
     788    PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface);
     789
     790    /* For now we only have one sink, namely the driver's one.
     791     * Later each stream could have its own one, to e.g. router different stream to different sinks .*/
     792    PAVRECSINK pSink = &pThis->Sink;
     793
    624794    if (pCfgReq->enmDir == PDMAUDIODIR_OUT)
    625         return avRecCreateStreamOut(pInterface, pStream, pCfgReq, pCfgAcq);
     795        return avRecCreateStreamOut(pThis, pStream, pSink, pCfgReq, pCfgAcq);
    626796
    627797    return VERR_NOT_SUPPORTED;
     
    637807    AssertPtrReturn(pStream,    VERR_INVALID_POINTER);
    638808
     809    PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface);
     810
    639811    if (pStream->enmDir == PDMAUDIODIR_OUT)
    640         return avRecDestroyStreamOut(pInterface, pStream);
     812        return avRecDestroyStreamOut(pThis, pStream);
    641813
    642814    return VINF_SUCCESS;
     
    653825    AssertPtrReturn(pStream,    VERR_INVALID_POINTER);
    654826
     827    PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface);
     828
    655829    Assert(pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST);
    656830
    657831    if (pStream->enmDir == PDMAUDIODIR_OUT)
    658         return avRecControlStreamOut(pInterface,  pStream, enmStreamCmd);
     832        return avRecControlStreamOut(pThis,  pStream, enmStreamCmd);
    659833
    660834    return VINF_SUCCESS;
     
    705879
    706880AudioVideoRec::AudioVideoRec(Console *pConsole)
    707     : mpDrv(NULL),
    708       mParent(pConsole)
     881    : mpDrv(NULL)
     882    , mpConsole(pConsole)
    709883{
    710884}
     
    754928
    755929    /*
    756      * Get the AudioVideoRec object pointer.
     930     * Get the Console object pointer.
    757931     */
    758     void *pvUser = NULL;
    759     int rc = CFGMR3QueryPtr(pCfg, "Object", &pvUser); /** @todo r=andy Get rid of this hack and use IHostAudio::SetCallback. */
     932    void *pvUser;
     933    int rc = CFGMR3QueryPtr(pCfg, "ObjectConsole", &pvUser); /** @todo r=andy Get rid of this hack and use IHostAudio::SetCallback. */
     934    AssertMsgRCReturn(rc, ("Confguration error: No/bad \"ObjectConsole\" value, rc=%Rrc\n", rc), rc);
     935
     936    /* CFGM tree saves the pointer to Console in the Object node of AudioVideoRec. */
     937    pThis->pConsole = (Console *)pvUser;
     938
     939    /*
     940     * Get the pointer to the audio driver instance.
     941     */
     942    rc = CFGMR3QueryPtr(pCfg, "Object", &pvUser); /** @todo r=andy Get rid of this hack and use IHostAudio::SetCallback. */
    760943    AssertMsgRCReturn(rc, ("Confguration error: No/bad \"Object\" value, rc=%Rrc\n", rc), rc);
    761944
  • trunk/src/VBox/Main/src-client/VideoRec.cpp

    r65402 r65410  
    10411041}
    10421042
     1043int VideoRecSendAudio(PVIDEORECCONTEXT pCtx, const void *pvData, size_t cbData, uint64_t uTimestampMs)
     1044{
     1045    RT_NOREF(pCtx, pvData, cbData, uTimestampMs);
     1046    return VINF_SUCCESS;
     1047}
     1048
    10431049/**
    10441050 * VideoRec utility function to copy a source image (FrameBuf) to the intermediate
  • trunk/src/VBox/Main/src-client/VideoRec.h

    r65401 r65410  
    3333
    3434bool VideoRecIsEnabled(PVIDEORECCONTEXT pCtx);
     35int  VideoRecSendAudio(PVIDEORECCONTEXT pCtx, const void *pvData, size_t cbData, uint64_t uTimestampMs);
    3536int  VideoRecCopyToIntBuf(PVIDEORECCONTEXT pCtx, uint32_t uScreen,
    3637                          uint32_t x, uint32_t y, uint32_t uPixelFormat, uint32_t uBitsPerPixel,
Note: See TracChangeset for help on using the changeset viewer.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette