VirtualBox

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

Last change on this file since 67254 was 65438, checked in by vboxsync, 8 years ago

VideoRec: Update.

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