VirtualBox

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

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

VideoRec/Main: More renaming.

  • 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 75066 2018-10-25 12:58:49Z 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_FAILURE(rc))
420 {
421 int rc2 = VideoRecStreamClose(pStream);
422 AssertRC(rc2);
423 return rc;
424 }
425
426 pStream->fEnabled = true;
427
428 return VINF_SUCCESS;
429}
430
431/**
432 * Closes a recording stream.
433 * Depending on the stream's recording destination, this function closes all associated handles
434 * and finalizes recording.
435 *
436 * @returns IPRT status code.
437 * @param pStream Recording stream to close.
438 *
439 */
440int VideoRecStreamClose(PVIDEORECSTREAM pStream)
441{
442 int rc = VINF_SUCCESS;
443
444 if (pStream->fEnabled)
445 {
446 switch (pStream->enmDst)
447 {
448 case VIDEORECDEST_FILE:
449 {
450 if (pStream->File.pWEBM)
451 rc = pStream->File.pWEBM->Close();
452 break;
453 }
454
455 default:
456 AssertFailed(); /* Should never happen. */
457 break;
458 }
459
460 pStream->Blocks.Clear();
461
462 LogRel(("VideoRec: Recording screen #%u stopped\n", pStream->uScreenID));
463 }
464
465 if (RT_FAILURE(rc))
466 {
467 LogRel(("VideoRec: Error stopping recording screen #%u, rc=%Rrc\n", pStream->uScreenID, rc));
468 return rc;
469 }
470
471 switch (pStream->enmDst)
472 {
473 case VIDEORECDEST_FILE:
474 {
475 AssertPtr(pStream->File.pszFile);
476 if (RTFileIsValid(pStream->File.hFile))
477 {
478 rc = RTFileClose(pStream->File.hFile);
479 if (RT_SUCCESS(rc))
480 {
481 LogRel(("VideoRec: Closed file '%s'\n", pStream->File.pszFile));
482 }
483 else
484 {
485 LogRel(("VideoRec: Error closing file '%s', rc=%Rrc\n", pStream->File.pszFile, rc));
486 break;
487 }
488 }
489
490 RTStrFree(pStream->File.pszFile);
491 pStream->File.pszFile = NULL;
492
493 if (pStream->File.pWEBM)
494 {
495 delete pStream->File.pWEBM;
496 pStream->File.pWEBM = NULL;
497 }
498 break;
499 }
500
501 default:
502 rc = VERR_NOT_IMPLEMENTED;
503 break;
504 }
505
506 if (RT_SUCCESS(rc))
507 {
508 pStream->enmDst = VIDEORECDEST_INVALID;
509 }
510
511 LogFlowFuncLeaveRC(rc);
512 return rc;
513}
514
515/**
516 * Uninitializes a recording stream.
517 *
518 * @returns IPRT status code.
519 * @param pStream Recording stream to uninitialize.
520 */
521int VideoRecStreamUninit(PVIDEORECSTREAM pStream)
522{
523 int rc = VINF_SUCCESS;
524
525 if (pStream->pCtx->Cfg.Video.fEnabled)
526 {
527 int rc2 = VideoRecStreamUnitVideo(pStream);
528 if (RT_SUCCESS(rc))
529 rc = rc2;
530 }
531
532 return rc;
533}
534
535/**
536 * Uninitializes video recording for a certain recording stream.
537 *
538 * @returns IPRT status code.
539 * @param pStream Recording stream to uninitialize video recording for.
540 */
541int VideoRecStreamUnitVideo(PVIDEORECSTREAM pStream)
542{
543#ifdef VBOX_WITH_LIBVPX
544 /* At the moment we only have VPX. */
545 return VideoRecStreamUninitVideoVPX(pStream);
546#else
547 return VERR_NOT_SUPPORTED;
548#endif
549}
550
551#ifdef VBOX_WITH_LIBVPX
552/**
553 * Uninitializes the VPX codec for a certain recording stream.
554 *
555 * @returns IPRT status code.
556 * @param pStream Recording stream to uninitialize VPX codec for.
557 */
558int VideoRecStreamUninitVideoVPX(PVIDEORECSTREAM pStream)
559{
560 vpx_img_free(&pStream->Video.Codec.VPX.RawImage);
561 vpx_codec_err_t rcv = vpx_codec_destroy(&pStream->Video.Codec.VPX.Ctx);
562 Assert(rcv == VPX_CODEC_OK); RT_NOREF(rcv);
563
564 return VINF_SUCCESS;
565}
566#endif
567
568/**
569 * Initializes the video recording for a certain recording stream.
570 *
571 * @returns IPRT status code.
572 * @param pStream Recording stream to initialize video recording for.
573 * @param pCfg Video recording configuration to use for initialization.
574 */
575int VideoRecStreamInitVideo(PVIDEORECSTREAM pStream, PVIDEORECCFG pCfg)
576{
577#ifdef VBOX_WITH_LIBVPX
578 /* At the moment we only have VPX. */
579 return VideoRecStreamInitVideoVPX(pStream, pCfg);
580#else
581 return VERR_NOT_SUPPORTED;
582#endif
583}
584
585#ifdef VBOX_WITH_LIBVPX
586/**
587 * Initializes the VPX codec for a certain recording stream.
588 *
589 * @returns IPRT status code.
590 * @param pStream Recording stream to initialize VPX codec for.
591 * @param pCfg Video recording configuration to use for initialization.
592 */
593int VideoRecStreamInitVideoVPX(PVIDEORECSTREAM pStream, PVIDEORECCFG pCfg)
594{
595 pStream->Video.uWidth = pCfg->Video.uWidth;
596 pStream->Video.uHeight = pCfg->Video.uHeight;
597 pStream->Video.cFailedEncodingFrames = 0;
598
599 PVIDEORECVIDEOCODEC pVC = &pStream->Video.Codec;
600
601 pStream->Video.uDelayMs = RT_MS_1SEC / pCfg->Video.uFPS;
602
603 pVC->enmType = VIDEORECVIDEOCODECTYPE_VP8; /** @todo Make this configurable. */
604
605# ifdef VBOX_WITH_LIBVPX_VP9
606 vpx_codec_iface_t *pCodecIface = vpx_codec_vp9_cx();
607# else /* Default is using VP8. */
608 vpx_codec_iface_t *pCodecIface = vpx_codec_vp8_cx();
609# endif
610
611 vpx_codec_err_t rcv = vpx_codec_enc_config_default(pCodecIface, &pVC->VPX.Cfg, 0 /* Reserved */);
612 if (rcv != VPX_CODEC_OK)
613 {
614 LogRel(("VideoRec: Failed to get default config for VPX encoder: %s\n", vpx_codec_err_to_string(rcv)));
615 return VERR_AVREC_CODEC_INIT_FAILED;
616 }
617
618 /* Target bitrate in kilobits per second. */
619 pVC->VPX.Cfg.rc_target_bitrate = pCfg->Video.uRate;
620 /* Frame width. */
621 pVC->VPX.Cfg.g_w = pCfg->Video.uWidth;
622 /* Frame height. */
623 pVC->VPX.Cfg.g_h = pCfg->Video.uHeight;
624 /* 1ms per frame. */
625 pVC->VPX.Cfg.g_timebase.num = 1;
626 pVC->VPX.Cfg.g_timebase.den = 1000;
627 /* Disable multithreading. */
628 pVC->VPX.Cfg.g_threads = 0;
629
630 /* Initialize codec. */
631 rcv = vpx_codec_enc_init(&pVC->VPX.Ctx, pCodecIface, &pVC->VPX.Cfg, 0 /* Flags */);
632 if (rcv != VPX_CODEC_OK)
633 {
634 LogRel(("VideoRec: Failed to initialize VPX encoder: %s\n", vpx_codec_err_to_string(rcv)));
635 return VERR_AVREC_CODEC_INIT_FAILED;
636 }
637
638 if (!vpx_img_alloc(&pVC->VPX.RawImage, VPX_IMG_FMT_I420, pCfg->Video.uWidth, pCfg->Video.uHeight, 1))
639 {
640 LogRel(("VideoRec: Failed to allocate image %RU32x%RU32\n", pCfg->Video.uWidth, pCfg->Video.uHeight));
641 return VERR_NO_MEMORY;
642 }
643
644 /* Save a pointer to the first raw YUV plane. */
645 pStream->Video.Codec.VPX.pu8YuvBuf = pVC->VPX.RawImage.planes[0];
646
647 return VINF_SUCCESS;
648}
649#endif
650
651#ifdef VBOX_WITH_LIBVPX
652/**
653 * Encodes the source image and write the encoded image to the stream's destination.
654 *
655 * @returns IPRT status code.
656 * @param pStream Stream to encode and submit to.
657 * @param uTimeStampMs Absolute timestamp (PTS) of frame (in ms) to encode.
658 * @param pFrame Frame to encode and submit.
659 */
660int VideoRecStreamWriteVideoVPX(PVIDEORECSTREAM pStream, uint64_t uTimeStampMs, PVIDEORECVIDEOFRAME pFrame)
661{
662 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
663 AssertPtrReturn(pFrame, VERR_INVALID_POINTER);
664
665 int rc;
666
667 AssertPtr(pStream->pCtx);
668 PVIDEORECCFG pCfg = &pStream->pCtx->Cfg;
669 PVIDEORECVIDEOCODEC pVC = &pStream->Video.Codec;
670
671 /* Presentation Time Stamp (PTS). */
672 vpx_codec_pts_t pts = uTimeStampMs;
673 vpx_codec_err_t rcv = vpx_codec_encode(&pVC->VPX.Ctx,
674 &pVC->VPX.RawImage,
675 pts /* Time stamp */,
676 pStream->Video.uDelayMs /* How long to show this frame */,
677 0 /* Flags */,
678 pCfg->Video.Codec.VPX.uEncoderDeadline /* Quality setting */);
679 if (rcv != VPX_CODEC_OK)
680 {
681 if (pStream->Video.cFailedEncodingFrames++ < 64)
682 {
683 LogRel(("VideoRec: Failed to encode video frame: %s\n", vpx_codec_err_to_string(rcv)));
684 return VERR_GENERAL_FAILURE;
685 }
686 }
687
688 pStream->Video.cFailedEncodingFrames = 0;
689
690 vpx_codec_iter_t iter = NULL;
691 rc = VERR_NO_DATA;
692 for (;;)
693 {
694 const vpx_codec_cx_pkt_t *pPacket = vpx_codec_get_cx_data(&pVC->VPX.Ctx, &iter);
695 if (!pPacket)
696 break;
697
698 switch (pPacket->kind)
699 {
700 case VPX_CODEC_CX_FRAME_PKT:
701 {
702 WebMWriter::BlockData_VP8 blockData = { &pVC->VPX.Cfg, pPacket };
703 rc = pStream->File.pWEBM->WriteBlock(pStream->uTrackVideo, &blockData, sizeof(blockData));
704 break;
705 }
706
707 default:
708 AssertFailed();
709 LogFunc(("Unexpected video packet type %ld\n", pPacket->kind));
710 break;
711 }
712 }
713
714 return rc;
715}
716#endif /* VBOX_WITH_LIBVPX */
717
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