VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/VideoRec.cpp@ 67914

Last change on this file since 67914 was 67914, checked in by vboxsync, 7 years ago

src-client: Define LOG_GROUP according to interface or similar.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
  • Property svn:mergeinfo set to (toggle deleted branches)
    /branches/VBox-3.0/src/VBox/Frontends/VBoxHeadless/VideoCapture/EncodeAndWrite.cpp58652,​70973
    /branches/VBox-3.2/src/VBox/Frontends/VBoxHeadless/VideoCapture/EncodeAndWrite.cpp66309,​66318
    /branches/VBox-4.0/src/VBox/Frontends/VBoxHeadless/VideoCapture/EncodeAndWrite.cpp70873
    /branches/VBox-4.1/src/VBox/Frontends/VBoxHeadless/VideoCapture/EncodeAndWrite.cpp74233
    /branches/VBox-4.2/src/VBox/Main/src-client/VideoRec.cpp91503-91504,​91506-91508,​91510,​91514-91515,​91521
    /branches/VBox-4.3/src/VBox/Main/src-client/VideoRec.cpp91223
    /branches/VBox-4.3/trunk/src/VBox/Main/src-client/VideoRec.cpp91223
    /branches/dsen/gui/src/VBox/Frontends/VBoxHeadless/VideoCapture/EncodeAndWrite.cpp79076-79078,​79089,​79109-79110,​79112-79113,​79127-79130,​79134,​79141,​79151,​79155,​79157-79159,​79193,​79197
    /branches/dsen/gui2/src/VBox/Frontends/VBoxHeadless/VideoCapture/EncodeAndWrite.cpp79224,​79228,​79233,​79235,​79258,​79262-79263,​79273,​79341,​79345,​79354,​79357,​79387-79388,​79559-79569,​79572-79573,​79578,​79581-79582,​79590-79591,​79598-79599,​79602-79603,​79605-79606,​79632,​79635,​79637,​79644
    /branches/dsen/gui3/src/VBox/Frontends/VBoxHeadless/VideoCapture/EncodeAndWrite.cpp79645-79692
File size: 40.4 KB
Line 
1/* $Id: VideoRec.cpp 67914 2017-07-11 20:46:37Z vboxsync $ */
2/** @file
3 * Video capturing utility routines.
4 */
5
6/*
7 * Copyright (C) 2012-2017 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#define LOG_GROUP LOG_GROUP_MAIN_DISPLAY
19#include "LoggingNew.h"
20
21#include <stdexcept>
22#include <vector>
23
24#include <VBox/log.h>
25#include <iprt/asm.h>
26#include <iprt/assert.h>
27#include <iprt/semaphore.h>
28#include <iprt/thread.h>
29#include <iprt/time.h>
30
31#include <VBox/com/VirtualBox.h>
32#include <VBox/com/com.h>
33#include <VBox/com/string.h>
34
35#include "EbmlWriter.h"
36#include "VideoRec.h"
37
38#ifdef VBOX_WITH_LIBVPX
39# define VPX_CODEC_DISABLE_COMPAT 1
40# include <vpx/vp8cx.h>
41# include <vpx/vpx_image.h>
42
43/** Default VPX codec to use. */
44# define DEFAULTCODEC (vpx_codec_vp8_cx())
45#endif /* VBOX_WITH_LIBVPX */
46
47static int videoRecEncodeAndWrite(PVIDEORECSTREAM pStrm);
48static int videoRecRGBToYUV(PVIDEORECSTREAM pStrm);
49
50using namespace com;
51
52/**
53 * Enumeration for a video recording state.
54 */
55enum VIDEORECSTS
56{
57 /** Not initialized. */
58 VIDEORECSTS_UNINITIALIZED = 0,
59 /** Initialized, idle. */
60 VIDEORECSTS_IDLE = 1,
61 /** Currently busy, delay termination. */
62 VIDEORECSTS_BUSY = 2,
63 /** The usual 32-bit hack. */
64 VIDEORECSTS_32BIT_HACK = 0x7fffffff
65};
66
67/**
68 * Enumeration for supported pixel formats.
69 */
70enum VIDEORECPIXELFMT
71{
72 /** Unknown pixel format. */
73 VIDEORECPIXELFMT_UNKNOWN = 0,
74 /** RGB 24. */
75 VIDEORECPIXELFMT_RGB24 = 1,
76 /** RGB 24. */
77 VIDEORECPIXELFMT_RGB32 = 2,
78 /** RGB 565. */
79 VIDEORECPIXELFMT_RGB565 = 3
80};
81
82/**
83 * Structure for keeping specific video recording codec data.
84 */
85typedef struct VIDEORECCODEC
86{
87 union
88 {
89#ifdef VBOX_WITH_LIBVPX
90 struct
91 {
92 /** VPX codec context. */
93 vpx_codec_ctx_t CodecCtx;
94 /** VPX codec configuration. */
95 vpx_codec_enc_cfg_t Config;
96 /** VPX image context. */
97 vpx_image_t RawImage;
98 } VPX;
99#endif /* VBOX_WITH_LIBVPX */
100 };
101} VIDEORECCODEC, *PVIDEORECCODEC;
102
103/**
104 * Strucutre for maintaining a video recording stream.
105 */
106typedef struct VIDEORECSTREAM
107{
108 /** Container context. */
109 WebMWriter *pEBML;
110#ifdef VBOX_WITH_AUDIO_VIDEOREC
111 /** Track number of audio stream. */
112 uint8_t uTrackAudio;
113#endif
114 /** Track number of video stream. */
115 uint8_t uTrackVideo;
116 /** Codec data. */
117 VIDEORECCODEC Codec;
118 /** Screen ID. */
119 uint16_t uScreen;
120 /** Whether video recording is enabled or not. */
121 bool fEnabled;
122 /** Time stamp (in ms) of the last frame we encoded. */
123 uint64_t uLastTimeStampMs;
124 /** Time stamp (in ms) of the current frame. */
125 uint64_t uCurTimeStampMs;
126
127 /** Whether the RGB buffer is filled or not. */
128 bool fHasVideoData;
129
130 struct
131 {
132 /** Target X resolution (in pixels). */
133 uint32_t uDstWidth;
134 /** Target Y resolution (in pixels). */
135 uint32_t uDstHeight;
136 /** X resolution of the last encoded frame. */
137 uint32_t uSrcLastWidth;
138 /** Y resolution of the last encoded frame. */
139 uint32_t uSrcLastHeight;
140 /** RGB buffer containing the most recent frame of Main's framebuffer. */
141 uint8_t *pu8RgbBuf;
142 /** YUV buffer the encode function fetches the frame from. */
143 uint8_t *pu8YuvBuf;
144 /** Pixel format of the current frame. */
145 uint32_t uPixelFormat;
146 /** Minimal delay (in ms) between two frames. */
147 uint32_t uDelayMs;
148 /** Encoder deadline. */
149 unsigned int uEncoderDeadline;
150 } Video;
151} VIDEORECSTREAM, *PVIDEORECSTREAM;
152
153#ifdef VBOX_WITH_AUDIO_VIDEOREC
154typedef struct VIDEORECAUDIOFRAME
155{
156 uint8_t abBuf[_64K]; /** @todo Fix! */
157 uint32_t cbBuf;
158 /** Time stamp (in ms). */
159 uint64_t uTimeStampMs;
160} VIDEORECAUDIOFRAME, *PVIDEORECAUDIOFRAME;
161#endif
162
163/** Vector of video recording streams. */
164typedef std::vector <PVIDEORECSTREAM> VideoRecStreams;
165
166/**
167 * Structure for keeping a video recording context.
168 */
169typedef struct VIDEORECCONTEXT
170{
171 /** The current state. */
172 uint32_t enmState;
173 /** Semaphore to signal the encoding worker thread. */
174 RTSEMEVENT WaitEvent;
175 /** Whether video recording is enabled or not. */
176 bool fEnabled;
177 /** Shutdown indicator. */
178 bool fShutdown;
179 /** Worker thread. */
180 RTTHREAD Thread;
181 /** Maximal time (in ms) to record. */
182 uint64_t uMaxTimeMs;
183 /** Maximal file size (in MB) to record. */
184 uint32_t uMaxSizeMB;
185 /** Vector of current video recording stream contexts. */
186 VideoRecStreams vecStreams;
187#ifdef VBOX_WITH_AUDIO_VIDEOREC
188 bool fHasAudioData;
189 VIDEORECAUDIOFRAME Audio;
190#endif
191} VIDEORECCONTEXT, *PVIDEORECCONTEXT;
192
193
194/**
195 * Iterator class for running through a BGRA32 image buffer and converting
196 * it to RGB.
197 */
198class ColorConvBGRA32Iter
199{
200private:
201 enum { PIX_SIZE = 4 };
202public:
203 ColorConvBGRA32Iter(unsigned aWidth, unsigned aHeight, uint8_t *aBuf)
204 {
205 LogFlow(("width = %d height=%d aBuf=%lx\n", aWidth, aHeight, aBuf));
206 mPos = 0;
207 mSize = aWidth * aHeight * PIX_SIZE;
208 mBuf = aBuf;
209 }
210 /**
211 * Convert the next pixel to RGB.
212 * @returns true on success, false if we have reached the end of the buffer
213 * @param aRed where to store the red value
214 * @param aGreen where to store the green value
215 * @param aBlue where to store the blue value
216 */
217 bool getRGB(unsigned *aRed, unsigned *aGreen, unsigned *aBlue)
218 {
219 bool rc = false;
220 if (mPos + PIX_SIZE <= mSize)
221 {
222 *aRed = mBuf[mPos + 2];
223 *aGreen = mBuf[mPos + 1];
224 *aBlue = mBuf[mPos ];
225 mPos += PIX_SIZE;
226 rc = true;
227 }
228 return rc;
229 }
230
231 /**
232 * Skip forward by a certain number of pixels
233 * @param aPixels how many pixels to skip
234 */
235 void skip(unsigned aPixels)
236 {
237 mPos += PIX_SIZE * aPixels;
238 }
239private:
240 /** Size of the picture buffer */
241 unsigned mSize;
242 /** Current position in the picture buffer */
243 unsigned mPos;
244 /** Address of the picture buffer */
245 uint8_t *mBuf;
246};
247
248/**
249 * Iterator class for running through an BGR24 image buffer and converting
250 * it to RGB.
251 */
252class ColorConvBGR24Iter
253{
254private:
255 enum { PIX_SIZE = 3 };
256public:
257 ColorConvBGR24Iter(unsigned aWidth, unsigned aHeight, uint8_t *aBuf)
258 {
259 mPos = 0;
260 mSize = aWidth * aHeight * PIX_SIZE;
261 mBuf = aBuf;
262 }
263 /**
264 * Convert the next pixel to RGB.
265 * @returns true on success, false if we have reached the end of the buffer
266 * @param aRed where to store the red value
267 * @param aGreen where to store the green value
268 * @param aBlue where to store the blue value
269 */
270 bool getRGB(unsigned *aRed, unsigned *aGreen, unsigned *aBlue)
271 {
272 bool rc = false;
273 if (mPos + PIX_SIZE <= mSize)
274 {
275 *aRed = mBuf[mPos + 2];
276 *aGreen = mBuf[mPos + 1];
277 *aBlue = mBuf[mPos ];
278 mPos += PIX_SIZE;
279 rc = true;
280 }
281 return rc;
282 }
283
284 /**
285 * Skip forward by a certain number of pixels
286 * @param aPixels how many pixels to skip
287 */
288 void skip(unsigned aPixels)
289 {
290 mPos += PIX_SIZE * aPixels;
291 }
292private:
293 /** Size of the picture buffer */
294 unsigned mSize;
295 /** Current position in the picture buffer */
296 unsigned mPos;
297 /** Address of the picture buffer */
298 uint8_t *mBuf;
299};
300
301/**
302 * Iterator class for running through an BGR565 image buffer and converting
303 * it to RGB.
304 */
305class ColorConvBGR565Iter
306{
307private:
308 enum { PIX_SIZE = 2 };
309public:
310 ColorConvBGR565Iter(unsigned aWidth, unsigned aHeight, uint8_t *aBuf)
311 {
312 mPos = 0;
313 mSize = aWidth * aHeight * PIX_SIZE;
314 mBuf = aBuf;
315 }
316 /**
317 * Convert the next pixel to RGB.
318 * @returns true on success, false if we have reached the end of the buffer
319 * @param aRed where to store the red value
320 * @param aGreen where to store the green value
321 * @param aBlue where to store the blue value
322 */
323 bool getRGB(unsigned *aRed, unsigned *aGreen, unsigned *aBlue)
324 {
325 bool rc = false;
326 if (mPos + PIX_SIZE <= mSize)
327 {
328 unsigned uFull = (((unsigned) mBuf[mPos + 1]) << 8)
329 | ((unsigned) mBuf[mPos]);
330 *aRed = (uFull >> 8) & ~7;
331 *aGreen = (uFull >> 3) & ~3 & 0xff;
332 *aBlue = (uFull << 3) & ~7 & 0xff;
333 mPos += PIX_SIZE;
334 rc = true;
335 }
336 return rc;
337 }
338
339 /**
340 * Skip forward by a certain number of pixels
341 * @param aPixels how many pixels to skip
342 */
343 void skip(unsigned aPixels)
344 {
345 mPos += PIX_SIZE * aPixels;
346 }
347private:
348 /** Size of the picture buffer */
349 unsigned mSize;
350 /** Current position in the picture buffer */
351 unsigned mPos;
352 /** Address of the picture buffer */
353 uint8_t *mBuf;
354};
355
356/**
357 * Convert an image to YUV420p format
358 * @returns true on success, false on failure
359 * @param aWidth width of image
360 * @param aHeight height of image
361 * @param aDestBuf an allocated memory buffer large enough to hold the
362 * destination image (i.e. width * height * 12bits)
363 * @param aSrcBuf the source image as an array of bytes
364 */
365template <class T>
366inline bool colorConvWriteYUV420p(unsigned aWidth, unsigned aHeight, uint8_t *aDestBuf, uint8_t *aSrcBuf)
367{
368 AssertReturn(!(aWidth & 1), false);
369 AssertReturn(!(aHeight & 1), false);
370 bool fRc = true;
371 T iter1(aWidth, aHeight, aSrcBuf);
372 T iter2 = iter1;
373 iter2.skip(aWidth);
374 unsigned cPixels = aWidth * aHeight;
375 unsigned offY = 0;
376 unsigned offU = cPixels;
377 unsigned offV = cPixels + cPixels / 4;
378 unsigned const cyHalf = aHeight / 2;
379 unsigned const cxHalf = aWidth / 2;
380 for (unsigned i = 0; i < cyHalf && fRc; ++i)
381 {
382 for (unsigned j = 0; j < cxHalf; ++j)
383 {
384 unsigned red, green, blue;
385 fRc = iter1.getRGB(&red, &green, &blue);
386 AssertReturn(fRc, false);
387 aDestBuf[offY] = ((66 * red + 129 * green + 25 * blue + 128) >> 8) + 16;
388 unsigned u = (((-38 * red - 74 * green + 112 * blue + 128) >> 8) + 128) / 4;
389 unsigned v = (((112 * red - 94 * green - 18 * blue + 128) >> 8) + 128) / 4;
390
391 fRc = iter1.getRGB(&red, &green, &blue);
392 AssertReturn(fRc, false);
393 aDestBuf[offY + 1] = ((66 * red + 129 * green + 25 * blue + 128) >> 8) + 16;
394 u += (((-38 * red - 74 * green + 112 * blue + 128) >> 8) + 128) / 4;
395 v += (((112 * red - 94 * green - 18 * blue + 128) >> 8) + 128) / 4;
396
397 fRc = iter2.getRGB(&red, &green, &blue);
398 AssertReturn(fRc, false);
399 aDestBuf[offY + aWidth] = ((66 * red + 129 * green + 25 * blue + 128) >> 8) + 16;
400 u += (((-38 * red - 74 * green + 112 * blue + 128) >> 8) + 128) / 4;
401 v += (((112 * red - 94 * green - 18 * blue + 128) >> 8) + 128) / 4;
402
403 fRc = iter2.getRGB(&red, &green, &blue);
404 AssertReturn(fRc, false);
405 aDestBuf[offY + aWidth + 1] = ((66 * red + 129 * green + 25 * blue + 128) >> 8) + 16;
406 u += (((-38 * red - 74 * green + 112 * blue + 128) >> 8) + 128) / 4;
407 v += (((112 * red - 94 * green - 18 * blue + 128) >> 8) + 128) / 4;
408
409 aDestBuf[offU] = u;
410 aDestBuf[offV] = v;
411 offY += 2;
412 ++offU;
413 ++offV;
414 }
415
416 iter1.skip(aWidth);
417 iter2.skip(aWidth);
418 offY += aWidth;
419 }
420
421 return true;
422}
423
424/**
425 * Convert an image to RGB24 format
426 * @returns true on success, false on failure
427 * @param aWidth width of image
428 * @param aHeight height of image
429 * @param aDestBuf an allocated memory buffer large enough to hold the
430 * destination image (i.e. width * height * 12bits)
431 * @param aSrcBuf the source image as an array of bytes
432 */
433template <class T>
434inline bool colorConvWriteRGB24(unsigned aWidth, unsigned aHeight,
435 uint8_t *aDestBuf, uint8_t *aSrcBuf)
436{
437 enum { PIX_SIZE = 3 };
438 bool rc = true;
439 AssertReturn(0 == (aWidth & 1), false);
440 AssertReturn(0 == (aHeight & 1), false);
441 T iter(aWidth, aHeight, aSrcBuf);
442 unsigned cPixels = aWidth * aHeight;
443 for (unsigned i = 0; i < cPixels && rc; ++i)
444 {
445 unsigned red, green, blue;
446 rc = iter.getRGB(&red, &green, &blue);
447 if (rc)
448 {
449 aDestBuf[i * PIX_SIZE ] = red;
450 aDestBuf[i * PIX_SIZE + 1] = green;
451 aDestBuf[i * PIX_SIZE + 2] = blue;
452 }
453 }
454 return rc;
455}
456
457/**
458 * Worker thread for all streams of a video recording context.
459 *
460 * Does RGB/YUV conversion and encoding.
461 */
462static DECLCALLBACK(int) videoRecThread(RTTHREAD hThreadSelf, void *pvUser)
463{
464 PVIDEORECCONTEXT pCtx = (PVIDEORECCONTEXT)pvUser;
465
466 /* Signal that we're up and rockin'. */
467 RTThreadUserSignal(hThreadSelf);
468
469 for (;;)
470 {
471 int rc = RTSemEventWait(pCtx->WaitEvent, RT_INDEFINITE_WAIT);
472 AssertRCBreak(rc);
473
474 if (ASMAtomicReadBool(&pCtx->fShutdown))
475 break;
476
477#ifdef VBOX_WITH_AUDIO_VIDEOREC
478 const bool fHasAudioData = ASMAtomicReadBool(&pCtx->fHasAudioData);
479#endif
480 /** @todo r=andy This is inefficient -- as we already wake up this thread
481 * for every screen from Main, we here go again (on every wake up) through
482 * all screens. */
483 for (VideoRecStreams::iterator it = pCtx->vecStreams.begin(); it != pCtx->vecStreams.end(); it++)
484 {
485 PVIDEORECSTREAM pStream = (*it);
486
487 if (!pStream->fEnabled)
488 continue;
489
490 if (ASMAtomicReadBool(&pStream->fHasVideoData))
491 {
492 rc = videoRecRGBToYUV(pStream);
493
494 ASMAtomicWriteBool(&pStream->fHasVideoData, false);
495
496 if (RT_SUCCESS(rc))
497 rc = videoRecEncodeAndWrite(pStream);
498
499 if (RT_FAILURE(rc))
500 {
501 static unsigned s_cErrEnc = 100;
502 if (s_cErrEnc > 0)
503 {
504 LogRel(("VideoRec: Error %Rrc encoding / writing video frame\n", rc));
505 s_cErrEnc--;
506 }
507 }
508 }
509
510#ifdef VBOX_WITH_AUDIO_VIDEOREC
511 /* Each (enabled) screen has to get the audio data. */
512 if (fHasAudioData)
513 {
514 WebMWriter::BlockData_Opus blockData = { pCtx->Audio.abBuf, pCtx->Audio.cbBuf, pCtx->Audio.uTimeStampMs };
515 rc = pStream->pEBML->WriteBlock(pStream->uTrackAudio, &blockData, sizeof(blockData));
516 }
517#endif
518 } /* for */
519
520#ifdef VBOX_WITH_AUDIO_VIDEOREC
521 if (fHasAudioData)
522 ASMAtomicWriteBool(&pCtx->fHasAudioData, false);
523#endif
524 }
525
526 return VINF_SUCCESS;
527}
528
529/**
530 * Creates a video recording context.
531 *
532 * @returns IPRT status code.
533 * @param cScreens Number of screens to create context for.
534 * @param ppCtx Pointer to created video recording context on success.
535 */
536int VideoRecContextCreate(uint32_t cScreens, PVIDEORECCONTEXT *ppCtx)
537{
538 AssertReturn(cScreens, VERR_INVALID_PARAMETER);
539 AssertPtrReturn(ppCtx, VERR_INVALID_POINTER);
540
541 int rc = VINF_SUCCESS;
542
543 PVIDEORECCONTEXT pCtx = (PVIDEORECCONTEXT)RTMemAllocZ(sizeof(VIDEORECCONTEXT));
544 if (!pCtx)
545 return VERR_NO_MEMORY;
546
547 for (uint32_t uScreen = 0; uScreen < cScreens; uScreen++)
548 {
549 PVIDEORECSTREAM pStream = (PVIDEORECSTREAM)RTMemAllocZ(sizeof(VIDEORECSTREAM));
550 if (!pStream)
551 {
552 rc = VERR_NO_MEMORY;
553 break;
554 }
555
556 try
557 {
558 pStream->uScreen = uScreen;
559
560 pCtx->vecStreams.push_back(pStream);
561
562 pStream->pEBML = new WebMWriter();
563 }
564 catch (std::bad_alloc)
565 {
566 rc = VERR_NO_MEMORY;
567 break;
568 }
569 }
570
571 if (RT_SUCCESS(rc))
572 {
573 pCtx->enmState = VIDEORECSTS_UNINITIALIZED;
574 pCtx->fShutdown = false;
575
576 rc = RTSemEventCreate(&pCtx->WaitEvent);
577 AssertRCReturn(rc, rc);
578
579 rc = RTThreadCreate(&pCtx->Thread, videoRecThread, (void *)pCtx, 0,
580 RTTHREADTYPE_MAIN_WORKER, RTTHREADFLAGS_WAITABLE, "VideoRec");
581
582 if (RT_SUCCESS(rc)) /* Wait for the thread to start. */
583 rc = RTThreadUserWait(pCtx->Thread, 30 * 1000 /* 30s timeout */);
584
585 if (RT_SUCCESS(rc))
586 {
587 pCtx->enmState = VIDEORECSTS_IDLE;
588 pCtx->fEnabled = true;
589
590 if (ppCtx)
591 *ppCtx = pCtx;
592 }
593 }
594
595 if (RT_FAILURE(rc))
596 {
597 /* Roll back allocations on error. */
598 VideoRecStreams::iterator it = pCtx->vecStreams.begin();
599 while (it != pCtx->vecStreams.end())
600 {
601 PVIDEORECSTREAM pStream = (*it);
602
603 if (pStream->pEBML)
604 delete pStream->pEBML;
605
606 it = pCtx->vecStreams.erase(it);
607
608 RTMemFree(pStream);
609 pStream = NULL;
610 }
611
612 Assert(pCtx->vecStreams.empty());
613 }
614
615 return rc;
616}
617
618/**
619 * Destroys a video recording context.
620 *
621 * @param pCtx Video recording context to destroy.
622 */
623int VideoRecContextDestroy(PVIDEORECCONTEXT pCtx)
624{
625 if (!pCtx)
626 return VINF_SUCCESS;
627
628 /* Set shutdown indicator. */
629 ASMAtomicWriteBool(&pCtx->fShutdown, true);
630
631 /* Signal the thread. */
632 RTSemEventSignal(pCtx->WaitEvent);
633
634 int rc = RTThreadWait(pCtx->Thread, 10 * 1000 /* 10s timeout */, NULL);
635 if (RT_FAILURE(rc))
636 return rc;
637
638 rc = RTSemEventDestroy(pCtx->WaitEvent);
639 AssertRC(rc);
640
641 pCtx->WaitEvent = NIL_RTSEMEVENT;
642
643 VideoRecStreams::iterator it = pCtx->vecStreams.begin();
644 while (it != pCtx->vecStreams.end())
645 {
646 PVIDEORECSTREAM pStream = (*it);
647
648 if (pStream->fEnabled)
649 {
650 AssertPtr(pStream->pEBML);
651 pStream->pEBML->Close();
652
653 vpx_img_free(&pStream->Codec.VPX.RawImage);
654 vpx_codec_err_t rcv = vpx_codec_destroy(&pStream->Codec.VPX.CodecCtx);
655 Assert(rcv == VPX_CODEC_OK); RT_NOREF(rcv);
656
657 if (pStream->Video.pu8RgbBuf)
658 {
659 RTMemFree(pStream->Video.pu8RgbBuf);
660 pStream->Video.pu8RgbBuf = NULL;
661 }
662
663 LogRel(("VideoRec: Recording screen #%u stopped\n", pStream->uScreen));
664 }
665
666 if (pStream->pEBML)
667 {
668 delete pStream->pEBML;
669 pStream->pEBML = NULL;
670 }
671
672 it = pCtx->vecStreams.erase(it);
673
674 RTMemFree(pStream);
675 pStream = NULL;
676 }
677
678 Assert(pCtx->vecStreams.empty());
679
680 RTMemFree(pCtx);
681 pCtx = NULL;
682
683 return VINF_SUCCESS;
684}
685
686/**
687 * Retrieves a specific recording stream of a recording context.
688 *
689 * @returns Pointer to recording stream if found, or NULL if not found.
690 * @param pCtx Recording context to look up stream for.
691 * @param uScreen Screen number of recording stream to look up.
692 */
693DECLINLINE(PVIDEORECSTREAM) videoRecStreamGet(PVIDEORECCONTEXT pCtx, uint32_t uScreen)
694{
695 AssertPtrReturn(pCtx, NULL);
696
697 PVIDEORECSTREAM pStream;
698
699 try
700 {
701 pStream = pCtx->vecStreams.at(uScreen);
702 }
703 catch (std::out_of_range)
704 {
705 pStream = NULL;
706 }
707
708 return pStream;
709}
710
711/**
712 * VideoRec utility function to initialize video recording context.
713 *
714 * @returns IPRT status code.
715 * @param pCtx Pointer to video recording context.
716 * @param uScreen Screen number to record.
717 * @param pszFile File to save recording to.
718 * @param uWidth Target video resolution (width).
719 * @param uHeight Target video resolution (height).
720 * @param uRate Target encoding bit rate.
721 * @param uFPS Target FPS (Frame Per Second).
722 * @param uMaxTimeS Maximum time (in s) to record, or 0 for no time limit.
723 * @param uMaxSizeMB Maximum file size (in MB) to record, or 0 for no limit.
724 * @param pszOptions Additional options in "key=value" array format. Optional.
725 */
726int VideoRecStreamInit(PVIDEORECCONTEXT pCtx, uint32_t uScreen, const char *pszFile,
727 uint32_t uWidth, uint32_t uHeight, uint32_t uRate, uint32_t uFPS,
728 uint32_t uMaxTimeS, uint32_t uMaxSizeMB, const char *pszOptions)
729{
730 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
731 AssertPtrReturn(pszFile, VERR_INVALID_POINTER);
732 AssertReturn(uWidth, VERR_INVALID_PARAMETER);
733 AssertReturn(uHeight, VERR_INVALID_PARAMETER);
734 AssertReturn(uRate, VERR_INVALID_PARAMETER);
735 AssertReturn(uFPS, VERR_INVALID_PARAMETER);
736 /* pszOptions is optional. */
737
738 PVIDEORECSTREAM pStream = videoRecStreamGet(pCtx, uScreen);
739 if (!pStream)
740 return VERR_NOT_FOUND;
741
742 pCtx->uMaxTimeMs = (uMaxTimeS > 0 ? RTTimeProgramMilliTS() + uMaxTimeS * 1000 : 0);
743 pCtx->uMaxSizeMB = uMaxSizeMB;
744
745 pStream->Video.uDstWidth = uWidth;
746 pStream->Video.uDstHeight = uHeight;
747 pStream->Video.pu8RgbBuf = (uint8_t *)RTMemAllocZ(uWidth * uHeight * 4);
748 AssertReturn(pStream->Video.pu8RgbBuf, VERR_NO_MEMORY);
749
750 /* Play safe: the file must not exist, overwriting is potentially
751 * hazardous as nothing prevents the user from picking a file name of some
752 * other important file, causing unintentional data loss. */
753
754#ifdef VBOX_WITH_LIBVPX
755 pStream->Video.uEncoderDeadline = VPX_DL_REALTIME;
756
757 vpx_codec_err_t rcv = vpx_codec_enc_config_default(DEFAULTCODEC, &pStream->Codec.VPX.Config, 0);
758 if (rcv != VPX_CODEC_OK)
759 {
760 LogRel(("VideoRec: Failed to get default configuration for VPX codec: %s\n", vpx_codec_err_to_string(rcv)));
761 return VERR_INVALID_PARAMETER;
762 }
763#endif
764
765 com::Utf8Str options(pszOptions);
766 size_t pos = 0;
767
768 /* By default we enable everything (if available). */
769 bool fHasVideoTrack = true;
770#ifdef VBOX_WITH_AUDIO_VIDEOREC
771 bool fHasAudioTrack = true;
772#endif
773
774 com::Utf8Str key, value;
775 while ((pos = options.parseKeyValue(key, value, pos)) != com::Utf8Str::npos)
776 {
777 if (key.compare("vc_quality", Utf8Str::CaseInsensitive) == 0)
778 {
779 if (value.compare("realtime", Utf8Str::CaseInsensitive) == 0)
780 {
781#ifdef VBOX_WITH_LIBVPX
782 pStream->Video.uEncoderDeadline = VPX_DL_REALTIME;
783#endif
784 }
785 else if (value.compare("good", Utf8Str::CaseInsensitive) == 0)
786 {
787 pStream->Video.uEncoderDeadline = 1000000 / uFPS;
788 }
789 else if (value.compare("best", Utf8Str::CaseInsensitive) == 0)
790 {
791#ifdef VBOX_WITH_LIBVPX
792 pStream->Video.uEncoderDeadline = VPX_DL_BEST_QUALITY;
793#endif
794 }
795 else
796 {
797 LogRel(("VideoRec: Setting quality deadline to '%s'\n", value.c_str()));
798 pStream->Video.uEncoderDeadline = value.toUInt32();
799 }
800 }
801 else if (key.compare("vc_enabled", Utf8Str::CaseInsensitive) == 0)
802 {
803#ifdef VBOX_WITH_AUDIO_VIDEOREC
804 if (value.compare("false", Utf8Str::CaseInsensitive) == 0) /* Disable audio. */
805 {
806 fHasVideoTrack = false;
807 LogRel(("VideoRec: Only audio will be recorded\n"));
808 }
809#endif
810 }
811 else if (key.compare("ac_enabled", Utf8Str::CaseInsensitive) == 0)
812 {
813#ifdef VBOX_WITH_AUDIO_VIDEOREC
814 if (value.compare("false", Utf8Str::CaseInsensitive)) /* Disable audio. */
815 {
816 fHasAudioTrack = false;
817 LogRel(("VideoRec: Only video will be recorded\n"));
818 }
819#endif
820 }
821 else
822 LogRel(("VideoRec: Unknown option '%s' (value '%s'), skipping\n", key.c_str(), value.c_str()));
823
824 } /* while */
825
826 uint64_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_WRITE;
827#ifdef DEBUG
828 fOpen |= RTFILE_O_CREATE_REPLACE;
829#else
830 fOpen |= RTFILE_O_CREATE;
831#endif
832
833 int rc = pStream->pEBML->Create(pszFile, fOpen, WebMWriter::AudioCodec_Opus, WebMWriter::VideoCodec_VP8);
834 if (RT_FAILURE(rc))
835 {
836 LogRel(("VideoRec: Failed to create the video capture output file '%s' (%Rrc)\n", pszFile, rc));
837 return rc;
838 }
839
840 pStream->Video.uDelayMs = 1000 / uFPS;
841
842 if (fHasVideoTrack)
843 {
844 rc = pStream->pEBML->AddVideoTrack(uWidth, uHeight, uFPS, &pStream->uTrackVideo);
845 if (RT_FAILURE(rc))
846 {
847 LogRel(("VideoRec: Failed to add video track to output file '%s' (%Rrc)\n", pszFile, rc));
848 return rc;
849 }
850
851 LogRel(("VideoRec: Recording screen #%u with %ux%u @ %u kbps, %u fps to '%s'\n",
852 uScreen, uWidth, uHeight, uRate, uFPS, pszFile));
853 }
854
855#ifdef VBOX_WITH_AUDIO_VIDEOREC
856 if (fHasAudioTrack)
857 {
858 rc = pStream->pEBML->AddAudioTrack(48000, 2, 16, &pStream->uTrackAudio); /** @todo Make this configurable. */
859 if (RT_FAILURE(rc))
860 {
861 LogRel(("VideoRec: Failed to add audio track to output file '%s' (%Rrc)\n", pszFile, rc));
862 return rc;
863 }
864
865 LogRel(("VideoRec: Recording audio enabled\n"));
866 }
867#endif
868
869#ifdef VBOX_WITH_LIBVPX
870 /* Target bitrate in kilobits per second. */
871 pStream->Codec.VPX.Config.rc_target_bitrate = uRate;
872 /* Frame width. */
873 pStream->Codec.VPX.Config.g_w = uWidth;
874 /* Frame height. */
875 pStream->Codec.VPX.Config.g_h = uHeight;
876 /* 1ms per frame. */
877 pStream->Codec.VPX.Config.g_timebase.num = 1;
878 pStream->Codec.VPX.Config.g_timebase.den = 1000;
879 /* Disable multithreading. */
880 pStream->Codec.VPX.Config.g_threads = 0;
881
882 /* Initialize codec. */
883 rcv = vpx_codec_enc_init(&pStream->Codec.VPX.CodecCtx, DEFAULTCODEC, &pStream->Codec.VPX.Config, 0);
884 if (rcv != VPX_CODEC_OK)
885 {
886 LogFlow(("Failed to initialize VP8 encoder %s", vpx_codec_err_to_string(rcv)));
887 return VERR_INVALID_PARAMETER;
888 }
889
890 if (!vpx_img_alloc(&pStream->Codec.VPX.RawImage, VPX_IMG_FMT_I420, uWidth, uHeight, 1))
891 {
892 LogFlow(("Failed to allocate image %dx%d", uWidth, uHeight));
893 return VERR_NO_MEMORY;
894 }
895
896 pStream->Video.pu8YuvBuf = pStream->Codec.VPX.RawImage.planes[0];
897#endif
898 pStream->fEnabled = true;
899
900 return VINF_SUCCESS;
901}
902
903/**
904 * VideoRec utility function to check if recording is enabled.
905 *
906 * @returns true if recording is enabled.
907 * @param pCtx Pointer to video recording context.
908 */
909bool VideoRecIsEnabled(PVIDEORECCONTEXT pCtx)
910{
911 if (!pCtx)
912 return false;
913
914 uint32_t enmState = ASMAtomicReadU32(&pCtx->enmState);
915
916 return ( enmState == VIDEORECSTS_IDLE
917 || enmState == VIDEORECSTS_BUSY);
918}
919
920/**
921 * VideoRec utility function to check if recording engine is ready to accept a new frame
922 * for the given screen.
923 *
924 * @returns true if recording engine is ready.
925 * @param pCtx Pointer to video recording context.
926 * @param uScreen Screen ID.
927 * @param uTimeStampMs Current time stamp (in ms).
928 */
929bool VideoRecIsReady(PVIDEORECCONTEXT pCtx, uint32_t uScreen, uint64_t uTimeStampMs)
930{
931 AssertPtrReturn(pCtx, false);
932
933 uint32_t enmState = ASMAtomicReadU32(&pCtx->enmState);
934 if (enmState != VIDEORECSTS_IDLE)
935 return false;
936
937 PVIDEORECSTREAM pStream = videoRecStreamGet(pCtx, uScreen);
938 if ( !pStream
939 || !pStream->fEnabled)
940 {
941 return false;
942 }
943
944 if (uTimeStampMs < pStream->uLastTimeStampMs + pStream->Video.uDelayMs)
945 return false;
946
947 if ( ASMAtomicReadBool(&pStream->fHasVideoData)
948#ifdef VBOX_WITH_AUDIO_VIDEOREC
949 /* Check if we have audio data left for the current frame. */
950 || ASMAtomicReadBool(&pCtx->fHasAudioData)
951#endif
952 )
953 {
954 return false;
955 }
956
957 return true;
958}
959
960/**
961 * VideoRec utility function to check if a specified limit for recording
962 * has been reached.
963 *
964 * @returns true if any limit has been reached.
965 * @param pCtx Pointer to video recording context.
966 * @param uScreen Screen ID.
967 * @param tsNowMs Current time stamp (in ms).
968 */
969
970bool VideoRecIsLimitReached(PVIDEORECCONTEXT pCtx, uint32_t uScreen, uint64_t tsNowMs)
971{
972 PVIDEORECSTREAM pStream = videoRecStreamGet(pCtx, uScreen);
973 if ( !pStream
974 || !pStream->fEnabled)
975 {
976 return false;
977 }
978
979 if ( pCtx->uMaxTimeMs
980 && tsNowMs >= pCtx->uMaxTimeMs)
981 {
982 return true;
983 }
984
985 if (pCtx->uMaxSizeMB)
986 {
987 uint64_t sizeInMB = pStream->pEBML->GetFileSize() / (1024 * 1024);
988 if(sizeInMB >= pCtx->uMaxSizeMB)
989 return true;
990 }
991 /* Check for available free disk space */
992 if (pStream->pEBML->GetAvailableSpace() < 0x100000)
993 {
994 LogRel(("VideoRec: Not enough free storage space available, stopping video capture\n"));
995 return true;
996 }
997
998 return false;
999}
1000
1001/**
1002 * VideoRec utility function to encode the source image and write the encoded
1003 * image to target file.
1004 *
1005 * @returns IPRT status code.
1006 * @param pStream Stream to encode and write.
1007 */
1008static int videoRecEncodeAndWrite(PVIDEORECSTREAM pStream)
1009{
1010 int rc;
1011
1012#ifdef VBOX_WITH_LIBVPX
1013 /* Presentation Time Stamp (PTS). */
1014 vpx_codec_pts_t pts = pStream->uCurTimeStampMs;
1015 vpx_codec_err_t rcv = vpx_codec_encode(&pStream->Codec.VPX.CodecCtx,
1016 &pStream->Codec.VPX.RawImage,
1017 pts /* Time stamp */,
1018 pStream->Video.uDelayMs /* How long to show this frame */,
1019 0 /* Flags */,
1020 pStream->Video.uEncoderDeadline /* Quality setting */);
1021 if (rcv != VPX_CODEC_OK)
1022 {
1023 LogFunc(("Failed to encode video frame: %s\n", vpx_codec_err_to_string(rcv)));
1024 return VERR_GENERAL_FAILURE;
1025 }
1026
1027 vpx_codec_iter_t iter = NULL;
1028 rc = VERR_NO_DATA;
1029 for (;;)
1030 {
1031 const vpx_codec_cx_pkt_t *pPacket = vpx_codec_get_cx_data(&pStream->Codec.VPX.CodecCtx, &iter);
1032 if (!pPacket)
1033 break;
1034
1035 switch (pPacket->kind)
1036 {
1037 case VPX_CODEC_CX_FRAME_PKT:
1038 {
1039 WebMWriter::BlockData_VP8 blockData = { &pStream->Codec.VPX.Config, pPacket };
1040 rc = pStream->pEBML->WriteBlock(pStream->uTrackVideo, &blockData, sizeof(blockData));
1041 break;
1042 }
1043
1044 default:
1045 AssertFailed();
1046 LogFunc(("Unexpected video packet type %ld\n", pPacket->kind));
1047 break;
1048 }
1049 }
1050#else
1051 RT_NOREF(pStream);
1052 rc = VERR_NOT_SUPPORTED;
1053#endif /* VBOX_WITH_LIBVPX */
1054 return rc;
1055}
1056
1057/**
1058 * VideoRec utility function to convert RGB to YUV.
1059 *
1060 * @returns IPRT status code.
1061 * @param pStream Recording stream to convert RGB to YUV video frame buffer for.
1062 */
1063static int videoRecRGBToYUV(PVIDEORECSTREAM pStream)
1064{
1065 switch (pStream->Video.uPixelFormat)
1066 {
1067 case VIDEORECPIXELFMT_RGB32:
1068 LogFlow(("32 bit\n"));
1069 if (!colorConvWriteYUV420p<ColorConvBGRA32Iter>(pStream->Video.uDstWidth,
1070 pStream->Video.uDstHeight,
1071 pStream->Video.pu8YuvBuf,
1072 pStream->Video.pu8RgbBuf))
1073 return VERR_INVALID_PARAMETER;
1074 break;
1075 case VIDEORECPIXELFMT_RGB24:
1076 LogFlow(("24 bit\n"));
1077 if (!colorConvWriteYUV420p<ColorConvBGR24Iter>(pStream->Video.uDstWidth,
1078 pStream->Video.uDstHeight,
1079 pStream->Video.pu8YuvBuf,
1080 pStream->Video.pu8RgbBuf))
1081 return VERR_INVALID_PARAMETER;
1082 break;
1083 case VIDEORECPIXELFMT_RGB565:
1084 LogFlow(("565 bit\n"));
1085 if (!colorConvWriteYUV420p<ColorConvBGR565Iter>(pStream->Video.uDstWidth,
1086 pStream->Video.uDstHeight,
1087 pStream->Video.pu8YuvBuf,
1088 pStream->Video.pu8RgbBuf))
1089 return VERR_INVALID_PARAMETER;
1090 break;
1091 default:
1092 return VERR_NOT_SUPPORTED;
1093 }
1094 return VINF_SUCCESS;
1095}
1096
1097/**
1098 * Sends an audio frame to the video encoding thread.
1099 *
1100 * @thread EMT
1101 *
1102 * @returns IPRT status code.
1103 * @param pCtx Pointer to the video recording context.
1104 * @param pvData Audio frame data to send.
1105 * @param cbData Size (in bytes) of audio frame data.
1106 * @param uTimeStampMs Time stamp (in ms) of audio playback.
1107 */
1108int VideoRecSendAudioFrame(PVIDEORECCONTEXT pCtx, const void *pvData, size_t cbData, uint64_t uTimeStampMs)
1109{
1110#ifdef VBOX_WITH_AUDIO_VIDEOREC
1111 AssertReturn(cbData <= _64K, VERR_INVALID_PARAMETER);
1112
1113 /* Do not execute during termination and guard against termination. */
1114 if (!ASMAtomicCmpXchgU32(&pCtx->enmState, VIDEORECSTS_BUSY, VIDEORECSTS_IDLE))
1115 return VINF_TRY_AGAIN;
1116
1117 /* To save time spent in EMT, do the required audio multiplexing in the encoding thread.
1118 *
1119 * The multiplexing is needed to supply all recorded (enabled) screens with the same
1120 * audio data at the same given point in time.
1121 */
1122
1123 if (ASMAtomicReadBool(&pCtx->fHasAudioData))
1124 return VERR_TRY_AGAIN; /* Previous frame not yet encoded. */
1125
1126 memcpy(pCtx->Audio.abBuf, pvData, RT_MIN(_64K, cbData));
1127
1128 pCtx->Audio.cbBuf = cbData;
1129 pCtx->Audio.uTimeStampMs = uTimeStampMs;
1130
1131 ASMAtomicWriteBool(&pCtx->fHasAudioData, true);
1132 RTSemEventSignal(pCtx->WaitEvent);
1133#else
1134 RT_NOREF(pCtx, pvData, cbData, uTimeStampMs);
1135#endif
1136 return VINF_SUCCESS;
1137}
1138
1139/**
1140 * VideoRec utility function to copy a source video frame to the intermediate
1141 * RGB buffer. This function is executed only once per time.
1142 *
1143 * @thread EMT
1144 *
1145 * @returns IPRT status code.
1146 * @param pCtx Pointer to the video recording context.
1147 * @param uScreen Screen number.
1148 * @param x Starting x coordinate of the video frame.
1149 * @param y Starting y coordinate of the video frame.
1150 * @param uPixelFormat Pixel format.
1151 * @param uBPP Bits Per Pixel (BPP).
1152 * @param uBytesPerLine Bytes per scanline.
1153 * @param uSrcWidth Width of the video frame.
1154 * @param uSrcHeight Height of the video frame.
1155 * @param puSrcData Pointer to video frame data.
1156 * @param uTimeStampMs Time stamp (in ms).
1157 */
1158int VideoRecSendVideoFrame(PVIDEORECCONTEXT pCtx, uint32_t uScreen, uint32_t x, uint32_t y,
1159 uint32_t uPixelFormat, uint32_t uBPP, uint32_t uBytesPerLine,
1160 uint32_t uSrcWidth, uint32_t uSrcHeight, uint8_t *puSrcData,
1161 uint64_t uTimeStampMs)
1162{
1163 /* Do not execute during termination and guard against termination. */
1164 if (!ASMAtomicCmpXchgU32(&pCtx->enmState, VIDEORECSTS_BUSY, VIDEORECSTS_IDLE))
1165 return VINF_TRY_AGAIN;
1166
1167 int rc = VINF_SUCCESS;
1168 do
1169 {
1170 AssertPtrBreakStmt(pCtx, rc = VERR_INVALID_POINTER);
1171 AssertBreakStmt(uSrcWidth, rc = VERR_INVALID_PARAMETER);
1172 AssertBreakStmt(uSrcHeight, rc = VERR_INVALID_PARAMETER);
1173 AssertPtrBreakStmt(puSrcData, rc = VERR_INVALID_POINTER);
1174
1175 PVIDEORECSTREAM pStream = videoRecStreamGet(pCtx, uScreen);
1176 if (!pStream)
1177 {
1178 rc = VERR_NOT_FOUND;
1179 break;
1180 }
1181
1182 if (!pStream->fEnabled)
1183 {
1184 rc = VINF_TRY_AGAIN; /* Not (yet) enabled. */
1185 break;
1186 }
1187
1188 if (uTimeStampMs < pStream->uLastTimeStampMs + pStream->Video.uDelayMs)
1189 {
1190 rc = VINF_TRY_AGAIN; /* Respect maximum frames per second. */
1191 break;
1192 }
1193
1194 if (ASMAtomicReadBool(&pStream->fHasVideoData))
1195 {
1196 rc = VERR_TRY_AGAIN; /* Previous frame not yet encoded. */
1197 break;
1198 }
1199
1200 pStream->uLastTimeStampMs = uTimeStampMs;
1201
1202 int xDiff = ((int)pStream->Video.uDstWidth - (int)uSrcWidth) / 2;
1203 uint32_t w = uSrcWidth;
1204 if ((int)w + xDiff + (int)x <= 0) /* Nothing visible. */
1205 {
1206 rc = VERR_INVALID_PARAMETER;
1207 break;
1208 }
1209
1210 uint32_t destX;
1211 if ((int)x < -xDiff)
1212 {
1213 w += xDiff + x;
1214 x = -xDiff;
1215 destX = 0;
1216 }
1217 else
1218 destX = x + xDiff;
1219
1220 uint32_t h = uSrcHeight;
1221 int yDiff = ((int)pStream->Video.uDstHeight - (int)uSrcHeight) / 2;
1222 if ((int)h + yDiff + (int)y <= 0) /* Nothing visible. */
1223 {
1224 rc = VERR_INVALID_PARAMETER;
1225 break;
1226 }
1227
1228 uint32_t destY;
1229 if ((int)y < -yDiff)
1230 {
1231 h += yDiff + (int)y;
1232 y = -yDiff;
1233 destY = 0;
1234 }
1235 else
1236 destY = y + yDiff;
1237
1238 if ( destX > pStream->Video.uDstWidth
1239 || destY > pStream->Video.uDstHeight)
1240 {
1241 rc = VERR_INVALID_PARAMETER; /* Nothing visible. */
1242 break;
1243 }
1244
1245 if (destX + w > pStream->Video.uDstWidth)
1246 w = pStream->Video.uDstWidth - destX;
1247
1248 if (destY + h > pStream->Video.uDstHeight)
1249 h = pStream->Video.uDstHeight - destY;
1250
1251 /* Calculate bytes per pixel. */
1252 uint32_t bpp = 1;
1253 if (uPixelFormat == BitmapFormat_BGR)
1254 {
1255 switch (uBPP)
1256 {
1257 case 32:
1258 pStream->Video.uPixelFormat = VIDEORECPIXELFMT_RGB32;
1259 bpp = 4;
1260 break;
1261 case 24:
1262 pStream->Video.uPixelFormat = VIDEORECPIXELFMT_RGB24;
1263 bpp = 3;
1264 break;
1265 case 16:
1266 pStream->Video.uPixelFormat = VIDEORECPIXELFMT_RGB565;
1267 bpp = 2;
1268 break;
1269 default:
1270 AssertMsgFailed(("Unknown color depth! mBitsPerPixel=%d\n", uBPP));
1271 break;
1272 }
1273 }
1274 else
1275 AssertMsgFailed(("Unknown pixel format! mPixelFormat=%d\n", pStream->Video.uPixelFormat));
1276
1277 /* One of the dimensions of the current frame is smaller than before so
1278 * clear the entire buffer to prevent artifacts from the previous frame. */
1279 if ( uSrcWidth < pStream->Video.uSrcLastWidth
1280 || uSrcHeight < pStream->Video.uSrcLastHeight)
1281 memset(pStream->Video.pu8RgbBuf, 0, pStream->Video.uDstWidth * pStream->Video.uDstHeight * 4);
1282
1283 pStream->Video.uSrcLastWidth = uSrcWidth;
1284 pStream->Video.uSrcLastHeight = uSrcHeight;
1285
1286 /* Calculate start offset in source and destination buffers. */
1287 uint32_t offSrc = y * uBytesPerLine + x * bpp;
1288 uint32_t offDst = (destY * pStream->Video.uDstWidth + destX) * bpp;
1289
1290 /* Do the copy. */
1291 for (unsigned int i = 0; i < h; i++)
1292 {
1293 /* Overflow check. */
1294 Assert(offSrc + w * bpp <= uSrcHeight * uBytesPerLine);
1295 Assert(offDst + w * bpp <= pStream->Video.uDstHeight * pStream->Video.uDstWidth * bpp);
1296
1297 memcpy(pStream->Video.pu8RgbBuf + offDst, puSrcData + offSrc, w * bpp);
1298
1299 offSrc += uBytesPerLine;
1300 offDst += pStream->Video.uDstWidth * bpp;
1301 }
1302
1303 pStream->uCurTimeStampMs = uTimeStampMs;
1304
1305 ASMAtomicWriteBool(&pStream->fHasVideoData, true);
1306 RTSemEventSignal(pCtx->WaitEvent);
1307
1308 } while (0);
1309
1310 ASMAtomicCmpXchgU32(&pCtx->enmState, VIDEORECSTS_IDLE, VIDEORECSTS_BUSY);
1311
1312 return rc;
1313}
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