VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/VideoRecStream.cpp@ 75069

Last change on this file since 75069 was 75069, checked in by vboxsync, 6 years ago

VideoRec/Main: Initialize context in VideoRecStreamInit().

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 22.3 KB
Line 
1/* $Id: VideoRecStream.cpp 75069 2018-10-25 13:21:17Z vboxsync $ */
2/** @file
3 * Video recording stream code.
4 */
5
6/*
7 * Copyright (C) 2012-2018 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#ifdef LOG_GROUP
19# undef LOG_GROUP
20#endif
21#define LOG_GROUP LOG_GROUP_MAIN_DISPLAY
22#include "LoggingNew.h"
23
24#include <stdexcept>
25
26#include <iprt/asm.h>
27#include <iprt/assert.h>
28#include <iprt/critsect.h>
29#include <iprt/file.h>
30#include <iprt/path.h>
31#include <iprt/semaphore.h>
32#include <iprt/thread.h>
33#include <iprt/time.h>
34
35#include <VBox/err.h>
36#include <VBox/com/VirtualBox.h>
37
38#include "VideoRec.h"
39#include "VideoRecStream.h"
40#include "VideoRecUtils.h"
41#include "WebMWriter.h"
42
43
44/**
45 * Locks a recording stream.
46 *
47 * @param pStream Recording stream to lock.
48 */
49void VideoRecStreamLock(PVIDEORECSTREAM pStream)
50{
51 int rc = RTCritSectEnter(&pStream->CritSect);
52 AssertRC(rc);
53}
54
55/**
56 * Unlocks a locked recording stream.
57 *
58 * @param pStream Recording stream to unlock.
59 */
60void VideoRecStreamUnlock(PVIDEORECSTREAM pStream)
61{
62 int rc = RTCritSectLeave(&pStream->CritSect);
63 AssertRC(rc);
64}
65
66/**
67 * Opens a recording stream.
68 *
69 * @returns IPRT status code.
70 * @param pStream Recording stream to open.
71 * @param pCfg Recording configuration to use.
72 */
73int VideoRecStreamOpen(PVIDEORECSTREAM pStream, PVIDEORECCFG pCfg)
74{
75 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
76 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
77
78 Assert(pStream->enmDst == VIDEORECDEST_INVALID);
79
80 int rc;
81
82 switch (pCfg->enmDst)
83 {
84 case VIDEORECDEST_FILE:
85 {
86 Assert(pCfg->File.strName.isNotEmpty());
87
88 char *pszAbsPath = RTPathAbsDup(com::Utf8Str(pCfg->File.strName).c_str());
89 AssertPtrReturn(pszAbsPath, VERR_NO_MEMORY);
90
91 RTPathStripSuffix(pszAbsPath);
92
93 char *pszSuff = RTStrDup(".webm");
94 if (!pszSuff)
95 {
96 RTStrFree(pszAbsPath);
97 rc = VERR_NO_MEMORY;
98 break;
99 }
100
101 char *pszFile = NULL;
102
103 if (pCfg->aScreens.size() > 1)
104 rc = RTStrAPrintf(&pszFile, "%s-%u%s", pszAbsPath, pStream->uScreenID + 1, pszSuff);
105 else
106 rc = RTStrAPrintf(&pszFile, "%s%s", pszAbsPath, pszSuff);
107
108 if (RT_SUCCESS(rc))
109 {
110 uint64_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_WRITE;
111
112 /* Play safe: the file must not exist, overwriting is potentially
113 * hazardous as nothing prevents the user from picking a file name of some
114 * other important file, causing unintentional data loss. */
115 fOpen |= RTFILE_O_CREATE;
116
117 RTFILE hFile;
118 rc = RTFileOpen(&hFile, pszFile, fOpen);
119 if (rc == VERR_ALREADY_EXISTS)
120 {
121 RTStrFree(pszFile);
122 pszFile = NULL;
123
124 RTTIMESPEC ts;
125 RTTimeNow(&ts);
126 RTTIME time;
127 RTTimeExplode(&time, &ts);
128
129 if (pCfg->aScreens.size() > 1)
130 rc = RTStrAPrintf(&pszFile, "%s-%04d-%02u-%02uT%02u-%02u-%02u-%09uZ-%u%s",
131 pszAbsPath, time.i32Year, time.u8Month, time.u8MonthDay,
132 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond,
133 pStream->uScreenID + 1, pszSuff);
134 else
135 rc = RTStrAPrintf(&pszFile, "%s-%04d-%02u-%02uT%02u-%02u-%02u-%09uZ%s",
136 pszAbsPath, time.i32Year, time.u8Month, time.u8MonthDay,
137 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond,
138 pszSuff);
139
140 if (RT_SUCCESS(rc))
141 rc = RTFileOpen(&hFile, pszFile, fOpen);
142 }
143
144 if (RT_SUCCESS(rc))
145 {
146 pStream->enmDst = VIDEORECDEST_FILE;
147 pStream->File.hFile = hFile;
148 pStream->File.pszFile = pszFile; /* Assign allocated string to our stream's config. */
149 }
150 }
151
152 RTStrFree(pszSuff);
153 RTStrFree(pszAbsPath);
154
155 if (RT_FAILURE(rc))
156 {
157 LogRel(("VideoRec: Failed to open file '%s' for screen %RU32, rc=%Rrc\n",
158 pszFile ? pszFile : "<Unnamed>", pStream->uScreenID, rc));
159 RTStrFree(pszFile);
160 }
161
162 break;
163 }
164
165 default:
166 rc = VERR_NOT_IMPLEMENTED;
167 break;
168 }
169
170 LogFlowFuncLeaveRC(rc);
171 return rc;
172}
173
174/**
175 * Processes a recording stream.
176 * This function takes care of the actual encoding and writing of a certain stream.
177 * As this can be very CPU intensive, this function usually is called from a separate thread.
178 *
179 * @returns IPRT status code.
180 * @param pStream Recording stream to process.
181 */
182int VideoRecStreamProcess(PVIDEORECSTREAM pStream)
183{
184 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
185
186 VideoRecStreamLock(pStream);
187
188 if (!pStream->fEnabled)
189 {
190 VideoRecStreamUnlock(pStream);
191 return VINF_SUCCESS;
192 }
193
194 int rc = VINF_SUCCESS;
195
196 const PVIDEORECCONTEXT pCtx = pStream->pCtx;
197 AssertPtr(pCtx);
198
199 VideoRecBlockMap::iterator itStreamBlocks = pStream->Blocks.Map.begin();
200 while (itStreamBlocks != pStream->Blocks.Map.end())
201 {
202 const uint64_t uTimeStampMs = itStreamBlocks->first;
203 VideoRecBlocks *pBlocks = itStreamBlocks->second;
204
205 AssertPtr(pBlocks);
206
207 while (!pBlocks->List.empty())
208 {
209 PVIDEORECBLOCK pBlock = pBlocks->List.front();
210 AssertPtr(pBlock);
211
212#ifdef VBOX_WITH_LIBVPX
213 if (pBlock->enmType == VIDEORECBLOCKTYPE_VIDEO)
214 {
215 PVIDEORECVIDEOFRAME pVideoFrame = (PVIDEORECVIDEOFRAME)pBlock->pvData;
216
217 rc = videoRecRGBToYUV(pVideoFrame->uPixelFormat,
218 /* Destination */
219 pStream->Video.Codec.VPX.pu8YuvBuf, pVideoFrame->uWidth, pVideoFrame->uHeight,
220 /* Source */
221 pVideoFrame->pu8RGBBuf, pStream->Video.uWidth, pStream->Video.uHeight);
222 if (RT_SUCCESS(rc))
223 {
224 rc = VideoRecStreamWriteVideoVPX(pStream, uTimeStampMs, pVideoFrame);
225 }
226 else
227 break;
228 }
229#endif
230 VideoRecBlockFree(pBlock);
231 pBlock = NULL;
232
233 pBlocks->List.pop_front();
234 }
235
236 ++itStreamBlocks;
237 }
238
239#ifdef VBOX_WITH_AUDIO_VIDEOREC
240 /* As each (enabled) screen has to get the same audio data, look for common (audio) data which needs to be
241 * written to the screen's assigned recording stream. */
242 VideoRecBlockMap::iterator itCommonBlocks = pCtx->mapBlocksCommon.begin();
243 while (itCommonBlocks != pCtx->mapBlocksCommon.end())
244 {
245 VideoRecBlockList::iterator itBlock = itCommonBlocks->second->List.begin();
246 while (itBlock != itCommonBlocks->second->List.end())
247 {
248 PVIDEORECBLOCK pBlockCommon = (PVIDEORECBLOCK)(*itBlock);
249 switch (pBlockCommon->enmType)
250 {
251 case VIDEORECBLOCKTYPE_AUDIO:
252 {
253 PVIDEORECAUDIOFRAME pAudioFrame = (PVIDEORECAUDIOFRAME)pBlockCommon->pvData;
254 AssertPtr(pAudioFrame);
255 AssertPtr(pAudioFrame->pvBuf);
256 Assert(pAudioFrame->cbBuf);
257
258 WebMWriter::BlockData_Opus blockData = { pAudioFrame->pvBuf, pAudioFrame->cbBuf,
259 pBlockCommon->uTimeStampMs };
260 AssertPtr(pStream->File.pWEBM);
261 rc = pStream->File.pWEBM->WriteBlock(pStream->uTrackAudio, &blockData, sizeof(blockData));
262 break;
263 }
264
265 default:
266 AssertFailed();
267 break;
268 }
269
270 if (RT_FAILURE(rc))
271 break;
272
273 Assert(pBlockCommon->cRefs);
274 pBlockCommon->cRefs--;
275 if (pBlockCommon->cRefs == 0)
276 {
277 VideoRecBlockFree(pBlockCommon);
278 itCommonBlocks->second->List.erase(itBlock);
279 itBlock = itCommonBlocks->second->List.begin();
280 }
281 else
282 ++itBlock;
283 }
284
285 /* If no entries are left over in the block map, remove it altogether. */
286 if (itCommonBlocks->second->List.empty())
287 {
288 delete itCommonBlocks->second;
289 pCtx->mapBlocksCommon.erase(itCommonBlocks);
290 itCommonBlocks = pCtx->mapBlocksCommon.begin();
291 }
292 else
293 ++itCommonBlocks;
294
295 LogFunc(("Common blocks: %zu\n", pCtx->mapBlocksCommon.size()));
296
297 if (RT_FAILURE(rc))
298 break;
299 }
300#endif
301
302 VideoRecStreamUnlock(pStream);
303
304 return rc;
305}
306
307/**
308 * Initializes a recording stream.
309 *
310 * @returns IPRT status code.
311 * @param pStream Recording stream to initialize.
312 * @param pCtx Recording context to use for initialization.
313 * @param uScreen Screen number to record.
314 */
315int VideoRecStreamInit(PVIDEORECSTREAM pStream, PVIDEORECCONTEXT pCtx, uint32_t uScreen)
316{
317 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
318 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
319
320 PVIDEORECCFG pCfg = &pCtx->Cfg;
321
322#ifdef VBOX_WITH_AUDIO_VIDEOREC
323 if (pCfg->Audio.fEnabled)
324 {
325 /* Sanity. */
326 AssertReturn(pCfg->Audio.uHz, VERR_INVALID_PARAMETER);
327 AssertReturn(pCfg->Audio.cBits, VERR_INVALID_PARAMETER);
328 AssertReturn(pCfg->Audio.cChannels, VERR_INVALID_PARAMETER);
329 }
330#endif
331
332 int rc = VideoRecStreamOpen(pStream, pCfg);
333 if (RT_FAILURE(rc))
334 return rc;
335
336 if (pCfg->Video.fEnabled)
337 rc = VideoRecStreamInitVideo(pStream, pCfg);
338
339 switch (pStream->enmDst)
340 {
341 case VIDEORECDEST_FILE:
342 {
343 rc = pStream->File.pWEBM->OpenEx(pStream->File.pszFile, &pStream->File.hFile,
344#ifdef VBOX_WITH_AUDIO_VIDEOREC
345 pCfg->Audio.fEnabled ? WebMWriter::AudioCodec_Opus : WebMWriter::AudioCodec_None,
346#else
347 WebMWriter::AudioCodec_None,
348#endif
349 pCfg->Video.fEnabled ? WebMWriter::VideoCodec_VP8 : WebMWriter::VideoCodec_None);
350 if (RT_FAILURE(rc))
351 {
352 LogRel(("VideoRec: Failed to create the capture output file '%s' (%Rrc)\n", pStream->File.pszFile, rc));
353 break;
354 }
355
356 const char *pszFile = pStream->File.pszFile;
357
358 if (pCfg->Video.fEnabled)
359 {
360 rc = pStream->File.pWEBM->AddVideoTrack(pCfg->Video.uWidth, pCfg->Video.uHeight, pCfg->Video.uFPS,
361 &pStream->uTrackVideo);
362 if (RT_FAILURE(rc))
363 {
364 LogRel(("VideoRec: Failed to add video track to output file '%s' (%Rrc)\n", pszFile, rc));
365 break;
366 }
367
368 LogRel(("VideoRec: Recording video of screen #%u with %RU32x%RU32 @ %RU32 kbps, %RU32 FPS (track #%RU8)\n",
369 uScreen, pCfg->Video.uWidth, pCfg->Video.uHeight, pCfg->Video.uRate, pCfg->Video.uFPS,
370 pStream->uTrackVideo));
371 }
372
373#ifdef VBOX_WITH_AUDIO_VIDEOREC
374 if (pCfg->Audio.fEnabled)
375 {
376 rc = pStream->File.pWEBM->AddAudioTrack(pCfg->Audio.uHz, pCfg->Audio.cChannels, pCfg->Audio.cBits,
377 &pStream->uTrackAudio);
378 if (RT_FAILURE(rc))
379 {
380 LogRel(("VideoRec: Failed to add audio track to output file '%s' (%Rrc)\n", pszFile, rc));
381 break;
382 }
383
384 LogRel(("VideoRec: Recording audio in %RU16Hz, %RU8 bit, %RU8 %s (track #%RU8)\n",
385 pCfg->Audio.uHz, pCfg->Audio.cBits, pCfg->Audio.cChannels, pCfg->Audio.cChannels ? "channels" : "channel",
386 pStream->uTrackAudio));
387 }
388#endif
389
390 if ( pCfg->Video.fEnabled
391#ifdef VBOX_WITH_AUDIO_VIDEOREC
392 || pCfg->Audio.fEnabled
393#endif
394 )
395 {
396 char szWhat[32] = { 0 };
397 if (pCfg->Video.fEnabled)
398 RTStrCat(szWhat, sizeof(szWhat), "video");
399#ifdef VBOX_WITH_AUDIO_VIDEOREC
400 if (pCfg->Audio.fEnabled)
401 {
402 if (pCfg->Video.fEnabled)
403 RTStrCat(szWhat, sizeof(szWhat), " + ");
404 RTStrCat(szWhat, sizeof(szWhat), "audio");
405 }
406#endif
407 LogRel(("VideoRec: Recording %s to '%s'\n", szWhat, pszFile));
408 }
409
410 break;
411 }
412
413 default:
414 AssertFailed(); /* Should never happen. */
415 rc = VERR_NOT_IMPLEMENTED;
416 break;
417 }
418
419 if (RT_SUCCESS(rc))
420 {
421 pStream->pCtx = pCtx;
422 pStream->fEnabled = true;
423 }
424 else
425 {
426 int rc2 = VideoRecStreamClose(pStream);
427 AssertRC(rc2);
428 return rc;
429 }
430
431 return VINF_SUCCESS;
432}
433
434/**
435 * Closes a recording stream.
436 * Depending on the stream's recording destination, this function closes all associated handles
437 * and finalizes recording.
438 *
439 * @returns IPRT status code.
440 * @param pStream Recording stream to close.
441 *
442 */
443int VideoRecStreamClose(PVIDEORECSTREAM pStream)
444{
445 int rc = VINF_SUCCESS;
446
447 if (pStream->fEnabled)
448 {
449 switch (pStream->enmDst)
450 {
451 case VIDEORECDEST_FILE:
452 {
453 if (pStream->File.pWEBM)
454 rc = pStream->File.pWEBM->Close();
455 break;
456 }
457
458 default:
459 AssertFailed(); /* Should never happen. */
460 break;
461 }
462
463 pStream->Blocks.Clear();
464
465 LogRel(("VideoRec: Recording screen #%u stopped\n", pStream->uScreenID));
466 }
467
468 if (RT_FAILURE(rc))
469 {
470 LogRel(("VideoRec: Error stopping recording screen #%u, rc=%Rrc\n", pStream->uScreenID, rc));
471 return rc;
472 }
473
474 switch (pStream->enmDst)
475 {
476 case VIDEORECDEST_FILE:
477 {
478 AssertPtr(pStream->File.pszFile);
479 if (RTFileIsValid(pStream->File.hFile))
480 {
481 rc = RTFileClose(pStream->File.hFile);
482 if (RT_SUCCESS(rc))
483 {
484 LogRel(("VideoRec: Closed file '%s'\n", pStream->File.pszFile));
485 }
486 else
487 {
488 LogRel(("VideoRec: Error closing file '%s', rc=%Rrc\n", pStream->File.pszFile, rc));
489 break;
490 }
491 }
492
493 RTStrFree(pStream->File.pszFile);
494 pStream->File.pszFile = NULL;
495
496 if (pStream->File.pWEBM)
497 {
498 delete pStream->File.pWEBM;
499 pStream->File.pWEBM = NULL;
500 }
501 break;
502 }
503
504 default:
505 rc = VERR_NOT_IMPLEMENTED;
506 break;
507 }
508
509 if (RT_SUCCESS(rc))
510 {
511 pStream->enmDst = VIDEORECDEST_INVALID;
512 }
513
514 LogFlowFuncLeaveRC(rc);
515 return rc;
516}
517
518/**
519 * Uninitializes a recording stream.
520 *
521 * @returns IPRT status code.
522 * @param pStream Recording stream to uninitialize.
523 */
524int VideoRecStreamUninit(PVIDEORECSTREAM pStream)
525{
526 int rc = VINF_SUCCESS;
527
528 if (pStream->pCtx->Cfg.Video.fEnabled)
529 {
530 int rc2 = VideoRecStreamUnitVideo(pStream);
531 if (RT_SUCCESS(rc))
532 rc = rc2;
533 }
534
535 return rc;
536}
537
538/**
539 * Uninitializes video recording for a certain recording stream.
540 *
541 * @returns IPRT status code.
542 * @param pStream Recording stream to uninitialize video recording for.
543 */
544int VideoRecStreamUnitVideo(PVIDEORECSTREAM pStream)
545{
546#ifdef VBOX_WITH_LIBVPX
547 /* At the moment we only have VPX. */
548 return VideoRecStreamUninitVideoVPX(pStream);
549#else
550 return VERR_NOT_SUPPORTED;
551#endif
552}
553
554#ifdef VBOX_WITH_LIBVPX
555/**
556 * Uninitializes the VPX codec for a certain recording stream.
557 *
558 * @returns IPRT status code.
559 * @param pStream Recording stream to uninitialize VPX codec for.
560 */
561int VideoRecStreamUninitVideoVPX(PVIDEORECSTREAM pStream)
562{
563 vpx_img_free(&pStream->Video.Codec.VPX.RawImage);
564 vpx_codec_err_t rcv = vpx_codec_destroy(&pStream->Video.Codec.VPX.Ctx);
565 Assert(rcv == VPX_CODEC_OK); RT_NOREF(rcv);
566
567 return VINF_SUCCESS;
568}
569#endif
570
571/**
572 * Initializes the video recording for a certain recording stream.
573 *
574 * @returns IPRT status code.
575 * @param pStream Recording stream to initialize video recording for.
576 * @param pCfg Video recording configuration to use for initialization.
577 */
578int VideoRecStreamInitVideo(PVIDEORECSTREAM pStream, PVIDEORECCFG pCfg)
579{
580#ifdef VBOX_WITH_LIBVPX
581 /* At the moment we only have VPX. */
582 return VideoRecStreamInitVideoVPX(pStream, pCfg);
583#else
584 return VERR_NOT_SUPPORTED;
585#endif
586}
587
588#ifdef VBOX_WITH_LIBVPX
589/**
590 * Initializes the VPX codec for a certain recording stream.
591 *
592 * @returns IPRT status code.
593 * @param pStream Recording stream to initialize VPX codec for.
594 * @param pCfg Video recording configuration to use for initialization.
595 */
596int VideoRecStreamInitVideoVPX(PVIDEORECSTREAM pStream, PVIDEORECCFG pCfg)
597{
598 pStream->Video.uWidth = pCfg->Video.uWidth;
599 pStream->Video.uHeight = pCfg->Video.uHeight;
600 pStream->Video.cFailedEncodingFrames = 0;
601
602 PVIDEORECVIDEOCODEC pVC = &pStream->Video.Codec;
603
604 pStream->Video.uDelayMs = RT_MS_1SEC / pCfg->Video.uFPS;
605
606 pVC->enmType = VIDEORECVIDEOCODECTYPE_VP8; /** @todo Make this configurable. */
607
608# ifdef VBOX_WITH_LIBVPX_VP9
609 vpx_codec_iface_t *pCodecIface = vpx_codec_vp9_cx();
610# else /* Default is using VP8. */
611 vpx_codec_iface_t *pCodecIface = vpx_codec_vp8_cx();
612# endif
613
614 vpx_codec_err_t rcv = vpx_codec_enc_config_default(pCodecIface, &pVC->VPX.Cfg, 0 /* Reserved */);
615 if (rcv != VPX_CODEC_OK)
616 {
617 LogRel(("VideoRec: Failed to get default config for VPX encoder: %s\n", vpx_codec_err_to_string(rcv)));
618 return VERR_AVREC_CODEC_INIT_FAILED;
619 }
620
621 /* Target bitrate in kilobits per second. */
622 pVC->VPX.Cfg.rc_target_bitrate = pCfg->Video.uRate;
623 /* Frame width. */
624 pVC->VPX.Cfg.g_w = pCfg->Video.uWidth;
625 /* Frame height. */
626 pVC->VPX.Cfg.g_h = pCfg->Video.uHeight;
627 /* 1ms per frame. */
628 pVC->VPX.Cfg.g_timebase.num = 1;
629 pVC->VPX.Cfg.g_timebase.den = 1000;
630 /* Disable multithreading. */
631 pVC->VPX.Cfg.g_threads = 0;
632
633 /* Initialize codec. */
634 rcv = vpx_codec_enc_init(&pVC->VPX.Ctx, pCodecIface, &pVC->VPX.Cfg, 0 /* Flags */);
635 if (rcv != VPX_CODEC_OK)
636 {
637 LogRel(("VideoRec: Failed to initialize VPX encoder: %s\n", vpx_codec_err_to_string(rcv)));
638 return VERR_AVREC_CODEC_INIT_FAILED;
639 }
640
641 if (!vpx_img_alloc(&pVC->VPX.RawImage, VPX_IMG_FMT_I420, pCfg->Video.uWidth, pCfg->Video.uHeight, 1))
642 {
643 LogRel(("VideoRec: Failed to allocate image %RU32x%RU32\n", pCfg->Video.uWidth, pCfg->Video.uHeight));
644 return VERR_NO_MEMORY;
645 }
646
647 /* Save a pointer to the first raw YUV plane. */
648 pStream->Video.Codec.VPX.pu8YuvBuf = pVC->VPX.RawImage.planes[0];
649
650 return VINF_SUCCESS;
651}
652#endif
653
654#ifdef VBOX_WITH_LIBVPX
655/**
656 * Encodes the source image and write the encoded image to the stream's destination.
657 *
658 * @returns IPRT status code.
659 * @param pStream Stream to encode and submit to.
660 * @param uTimeStampMs Absolute timestamp (PTS) of frame (in ms) to encode.
661 * @param pFrame Frame to encode and submit.
662 */
663int VideoRecStreamWriteVideoVPX(PVIDEORECSTREAM pStream, uint64_t uTimeStampMs, PVIDEORECVIDEOFRAME pFrame)
664{
665 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
666 AssertPtrReturn(pFrame, VERR_INVALID_POINTER);
667
668 int rc;
669
670 AssertPtr(pStream->pCtx);
671 PVIDEORECCFG pCfg = &pStream->pCtx->Cfg;
672 PVIDEORECVIDEOCODEC pVC = &pStream->Video.Codec;
673
674 /* Presentation Time Stamp (PTS). */
675 vpx_codec_pts_t pts = uTimeStampMs;
676 vpx_codec_err_t rcv = vpx_codec_encode(&pVC->VPX.Ctx,
677 &pVC->VPX.RawImage,
678 pts /* Time stamp */,
679 pStream->Video.uDelayMs /* How long to show this frame */,
680 0 /* Flags */,
681 pCfg->Video.Codec.VPX.uEncoderDeadline /* Quality setting */);
682 if (rcv != VPX_CODEC_OK)
683 {
684 if (pStream->Video.cFailedEncodingFrames++ < 64)
685 {
686 LogRel(("VideoRec: Failed to encode video frame: %s\n", vpx_codec_err_to_string(rcv)));
687 return VERR_GENERAL_FAILURE;
688 }
689 }
690
691 pStream->Video.cFailedEncodingFrames = 0;
692
693 vpx_codec_iter_t iter = NULL;
694 rc = VERR_NO_DATA;
695 for (;;)
696 {
697 const vpx_codec_cx_pkt_t *pPacket = vpx_codec_get_cx_data(&pVC->VPX.Ctx, &iter);
698 if (!pPacket)
699 break;
700
701 switch (pPacket->kind)
702 {
703 case VPX_CODEC_CX_FRAME_PKT:
704 {
705 WebMWriter::BlockData_VP8 blockData = { &pVC->VPX.Cfg, pPacket };
706 rc = pStream->File.pWEBM->WriteBlock(pStream->uTrackVideo, &blockData, sizeof(blockData));
707 break;
708 }
709
710 default:
711 AssertFailed();
712 LogFunc(("Unexpected video packet type %ld\n", pPacket->kind));
713 break;
714 }
715 }
716
717 return rc;
718}
719#endif /* VBOX_WITH_LIBVPX */
720
Note: See TracBrowser for help on using the repository browser.

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