VirtualBox

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

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

VideoRec: cScreens not not needed anymore; covered by the vector now, renaming.

  • 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: 32.3 KB
Line 
1/* $Id: VideoRec.cpp 65175 2017-01-06 10:04:49Z vboxsync $ */
2/** @file
3 * Encodes the screen content in VPX format.
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.215389.xyz. 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 <vector>
21
22#include <VBox/log.h>
23#include <iprt/asm.h>
24#include <iprt/assert.h>
25#include <iprt/semaphore.h>
26#include <iprt/thread.h>
27#include <iprt/time.h>
28
29#include <VBox/com/VirtualBox.h>
30#include <VBox/com/com.h>
31#include <VBox/com/string.h>
32
33#include "EbmlWriter.h"
34#include "VideoRec.h"
35
36#define VPX_CODEC_DISABLE_COMPAT 1
37#include <vpx/vp8cx.h>
38#include <vpx/vpx_image.h>
39
40/** Default VPX codec to use. */
41#define DEFAULTCODEC (vpx_codec_vp8_cx())
42
43static int videoRecEncodeAndWrite(PVIDEORECSTREAM pStrm);
44static int videoRecRGBToYUV(PVIDEORECSTREAM pStrm);
45
46/**
47 * Enumeration for a video recording state.
48 */
49enum
50{
51 /** Not initialized. */
52 VIDREC_UNINITIALIZED = 0,
53 /** Initialized, idle. */
54 VIDREC_IDLE = 1,
55 /** Currently in VideoRecCopyToIntBuf(), delay termination. */
56 VIDREC_COPYING = 2,
57 /** Signal that we are terminating. */
58 VIDREC_TERMINATING = 3
59};
60
61/* Must be always accessible and therefore cannot be part of VIDEORECCONTEXT */
62static uint32_t g_enmState = VIDREC_UNINITIALIZED;
63
64/**
65 * Strucutre for maintaining a video recording stream.
66 */
67typedef struct VIDEORECSTREAM
68{
69 /** Container context. */
70 WebMWriter *pEBML;
71 /** VPX codec context. */
72 vpx_codec_ctx_t VpxCodec;
73 /** VPX configuration. */
74 vpx_codec_enc_cfg_t VpxConfig;
75 /** Target X resolution (in pixels). */
76 uint32_t uTargetWidth;
77 /** Target Y resolution (in pixels). */
78 uint32_t uTargetHeight;
79 /** X resolution of the last encoded frame. */
80 uint32_t uLastSourceWidth;
81 /** Y resolution of the last encoded frame. */
82 uint32_t uLastSourceHeight;
83 /** Current frame number. */
84 uint64_t cFrame;
85 /** RGB buffer containing the most recent frame of the framebuffer. */
86 uint8_t *pu8RgbBuf;
87 /** YUV buffer the encode function fetches the frame from. */
88 uint8_t *pu8YuvBuf;
89 /** VPX image context. */
90 vpx_image_t VpxRawImage;
91 /** Whether video recording is enabled or not. */
92 bool fEnabled;
93 /** Whether the RGB buffer is filled or not. */
94 bool fRgbFilled;
95 /** Pixel format of the current frame. */
96 uint32_t u32PixelFormat;
97 /** Minimal delay between two frames. */
98 uint32_t uDelay;
99 /** Time stamp of the last frame we encoded. */
100 uint64_t u64LastTimeStamp;
101 /** Time stamp of the current frame. */
102 uint64_t u64TimeStamp;
103 /** Encoder deadline. */
104 unsigned int uEncoderDeadline;
105} VIDEORECSTREAM, *PVIDEORECSTREAM;
106
107/** Vector of video recording streams. */
108typedef std::vector <PVIDEORECSTREAM> VideoRecStreams;
109
110/**
111 * Structure for keeping a video recording context.
112 */
113typedef struct VIDEORECCONTEXT
114{
115 /** Semaphore to signal the encoding worker thread. */
116 RTSEMEVENT WaitEvent;
117 /** Semaphore required during termination. */
118 RTSEMEVENT TermEvent;
119 /** Whether video recording is enabled or not. */
120 bool fEnabled;
121 /** Worker thread. */
122 RTTHREAD Thread;
123 /** Maximal time stamp. */
124 uint64_t u64MaxTimeStamp;
125 /** Maximal file size in MB. */
126 uint32_t uMaxFileSize;
127 /** Vector of current video recording stream contexts. */
128 VideoRecStreams vecStreams;
129} VIDEORECCONTEXT, *PVIDEORECCONTEXT;
130
131
132/**
133 * Iterator class for running through a BGRA32 image buffer and converting
134 * it to RGB.
135 */
136class ColorConvBGRA32Iter
137{
138private:
139 enum { PIX_SIZE = 4 };
140public:
141 ColorConvBGRA32Iter(unsigned aWidth, unsigned aHeight, uint8_t *aBuf)
142 {
143 LogFlow(("width = %d height=%d aBuf=%lx\n", aWidth, aHeight, aBuf));
144 mPos = 0;
145 mSize = aWidth * aHeight * PIX_SIZE;
146 mBuf = aBuf;
147 }
148 /**
149 * Convert the next pixel to RGB.
150 * @returns true on success, false if we have reached the end of the buffer
151 * @param aRed where to store the red value
152 * @param aGreen where to store the green value
153 * @param aBlue where to store the blue value
154 */
155 bool getRGB(unsigned *aRed, unsigned *aGreen, unsigned *aBlue)
156 {
157 bool rc = false;
158 if (mPos + PIX_SIZE <= mSize)
159 {
160 *aRed = mBuf[mPos + 2];
161 *aGreen = mBuf[mPos + 1];
162 *aBlue = mBuf[mPos ];
163 mPos += PIX_SIZE;
164 rc = true;
165 }
166 return rc;
167 }
168
169 /**
170 * Skip forward by a certain number of pixels
171 * @param aPixels how many pixels to skip
172 */
173 void skip(unsigned aPixels)
174 {
175 mPos += PIX_SIZE * aPixels;
176 }
177private:
178 /** Size of the picture buffer */
179 unsigned mSize;
180 /** Current position in the picture buffer */
181 unsigned mPos;
182 /** Address of the picture buffer */
183 uint8_t *mBuf;
184};
185
186/**
187 * Iterator class for running through an BGR24 image buffer and converting
188 * it to RGB.
189 */
190class ColorConvBGR24Iter
191{
192private:
193 enum { PIX_SIZE = 3 };
194public:
195 ColorConvBGR24Iter(unsigned aWidth, unsigned aHeight, uint8_t *aBuf)
196 {
197 mPos = 0;
198 mSize = aWidth * aHeight * PIX_SIZE;
199 mBuf = aBuf;
200 }
201 /**
202 * Convert the next pixel to RGB.
203 * @returns true on success, false if we have reached the end of the buffer
204 * @param aRed where to store the red value
205 * @param aGreen where to store the green value
206 * @param aBlue where to store the blue value
207 */
208 bool getRGB(unsigned *aRed, unsigned *aGreen, unsigned *aBlue)
209 {
210 bool rc = false;
211 if (mPos + PIX_SIZE <= mSize)
212 {
213 *aRed = mBuf[mPos + 2];
214 *aGreen = mBuf[mPos + 1];
215 *aBlue = mBuf[mPos ];
216 mPos += PIX_SIZE;
217 rc = true;
218 }
219 return rc;
220 }
221
222 /**
223 * Skip forward by a certain number of pixels
224 * @param aPixels how many pixels to skip
225 */
226 void skip(unsigned aPixels)
227 {
228 mPos += PIX_SIZE * aPixels;
229 }
230private:
231 /** Size of the picture buffer */
232 unsigned mSize;
233 /** Current position in the picture buffer */
234 unsigned mPos;
235 /** Address of the picture buffer */
236 uint8_t *mBuf;
237};
238
239/**
240 * Iterator class for running through an BGR565 image buffer and converting
241 * it to RGB.
242 */
243class ColorConvBGR565Iter
244{
245private:
246 enum { PIX_SIZE = 2 };
247public:
248 ColorConvBGR565Iter(unsigned aWidth, unsigned aHeight, uint8_t *aBuf)
249 {
250 mPos = 0;
251 mSize = aWidth * aHeight * PIX_SIZE;
252 mBuf = aBuf;
253 }
254 /**
255 * Convert the next pixel to RGB.
256 * @returns true on success, false if we have reached the end of the buffer
257 * @param aRed where to store the red value
258 * @param aGreen where to store the green value
259 * @param aBlue where to store the blue value
260 */
261 bool getRGB(unsigned *aRed, unsigned *aGreen, unsigned *aBlue)
262 {
263 bool rc = false;
264 if (mPos + PIX_SIZE <= mSize)
265 {
266 unsigned uFull = (((unsigned) mBuf[mPos + 1]) << 8)
267 | ((unsigned) mBuf[mPos]);
268 *aRed = (uFull >> 8) & ~7;
269 *aGreen = (uFull >> 3) & ~3 & 0xff;
270 *aBlue = (uFull << 3) & ~7 & 0xff;
271 mPos += PIX_SIZE;
272 rc = true;
273 }
274 return rc;
275 }
276
277 /**
278 * Skip forward by a certain number of pixels
279 * @param aPixels how many pixels to skip
280 */
281 void skip(unsigned aPixels)
282 {
283 mPos += PIX_SIZE * aPixels;
284 }
285private:
286 /** Size of the picture buffer */
287 unsigned mSize;
288 /** Current position in the picture buffer */
289 unsigned mPos;
290 /** Address of the picture buffer */
291 uint8_t *mBuf;
292};
293
294/**
295 * Convert an image to YUV420p format
296 * @returns true on success, false on failure
297 * @param aWidth width of image
298 * @param aHeight height of image
299 * @param aDestBuf an allocated memory buffer large enough to hold the
300 * destination image (i.e. width * height * 12bits)
301 * @param aSrcBuf the source image as an array of bytes
302 */
303template <class T>
304inline bool colorConvWriteYUV420p(unsigned aWidth, unsigned aHeight, uint8_t *aDestBuf, uint8_t *aSrcBuf)
305{
306 AssertReturn(!(aWidth & 1), false);
307 AssertReturn(!(aHeight & 1), false);
308 bool fRc = true;
309 T iter1(aWidth, aHeight, aSrcBuf);
310 T iter2 = iter1;
311 iter2.skip(aWidth);
312 unsigned cPixels = aWidth * aHeight;
313 unsigned offY = 0;
314 unsigned offU = cPixels;
315 unsigned offV = cPixels + cPixels / 4;
316 unsigned const cyHalf = aHeight / 2;
317 unsigned const cxHalf = aWidth / 2;
318 for (unsigned i = 0; i < cyHalf && fRc; ++i)
319 {
320 for (unsigned j = 0; j < cxHalf; ++j)
321 {
322 unsigned red, green, blue;
323 fRc = iter1.getRGB(&red, &green, &blue);
324 AssertReturn(fRc, false);
325 aDestBuf[offY] = ((66 * red + 129 * green + 25 * blue + 128) >> 8) + 16;
326 unsigned u = (((-38 * red - 74 * green + 112 * blue + 128) >> 8) + 128) / 4;
327 unsigned v = (((112 * red - 94 * green - 18 * blue + 128) >> 8) + 128) / 4;
328
329 fRc = iter1.getRGB(&red, &green, &blue);
330 AssertReturn(fRc, false);
331 aDestBuf[offY + 1] = ((66 * red + 129 * green + 25 * blue + 128) >> 8) + 16;
332 u += (((-38 * red - 74 * green + 112 * blue + 128) >> 8) + 128) / 4;
333 v += (((112 * red - 94 * green - 18 * blue + 128) >> 8) + 128) / 4;
334
335 fRc = iter2.getRGB(&red, &green, &blue);
336 AssertReturn(fRc, false);
337 aDestBuf[offY + aWidth] = ((66 * red + 129 * green + 25 * blue + 128) >> 8) + 16;
338 u += (((-38 * red - 74 * green + 112 * blue + 128) >> 8) + 128) / 4;
339 v += (((112 * red - 94 * green - 18 * blue + 128) >> 8) + 128) / 4;
340
341 fRc = iter2.getRGB(&red, &green, &blue);
342 AssertReturn(fRc, false);
343 aDestBuf[offY + aWidth + 1] = ((66 * red + 129 * green + 25 * blue + 128) >> 8) + 16;
344 u += (((-38 * red - 74 * green + 112 * blue + 128) >> 8) + 128) / 4;
345 v += (((112 * red - 94 * green - 18 * blue + 128) >> 8) + 128) / 4;
346
347 aDestBuf[offU] = u;
348 aDestBuf[offV] = v;
349 offY += 2;
350 ++offU;
351 ++offV;
352 }
353
354 iter1.skip(aWidth);
355 iter2.skip(aWidth);
356 offY += aWidth;
357 }
358
359 return true;
360}
361
362/**
363 * Convert an image to RGB24 format
364 * @returns true on success, false on failure
365 * @param aWidth width of image
366 * @param aHeight height of image
367 * @param aDestBuf an allocated memory buffer large enough to hold the
368 * destination image (i.e. width * height * 12bits)
369 * @param aSrcBuf the source image as an array of bytes
370 */
371template <class T>
372inline bool colorConvWriteRGB24(unsigned aWidth, unsigned aHeight,
373 uint8_t *aDestBuf, uint8_t *aSrcBuf)
374{
375 enum { PIX_SIZE = 3 };
376 bool rc = true;
377 AssertReturn(0 == (aWidth & 1), false);
378 AssertReturn(0 == (aHeight & 1), false);
379 T iter(aWidth, aHeight, aSrcBuf);
380 unsigned cPixels = aWidth * aHeight;
381 for (unsigned i = 0; i < cPixels && rc; ++i)
382 {
383 unsigned red, green, blue;
384 rc = iter.getRGB(&red, &green, &blue);
385 if (rc)
386 {
387 aDestBuf[i * PIX_SIZE ] = red;
388 aDestBuf[i * PIX_SIZE + 1] = green;
389 aDestBuf[i * PIX_SIZE + 2] = blue;
390 }
391 }
392 return rc;
393}
394
395/**
396 * Worker thread for all streams of a video recording context.
397 *
398 * Does RGB/YUV conversion and encoding.
399 */
400static DECLCALLBACK(int) videoRecThread(RTTHREAD hThreadSelf, void *pvUser)
401{
402 RT_NOREF(hThreadSelf);
403 PVIDEORECCONTEXT pCtx = (PVIDEORECCONTEXT)pvUser;
404 for (;;)
405 {
406 int rc = RTSemEventWait(pCtx->WaitEvent, RT_INDEFINITE_WAIT);
407 AssertRCBreak(rc);
408
409 if (ASMAtomicReadU32(&g_enmState) == VIDREC_TERMINATING)
410 break;
411
412 for (VideoRecStreams::iterator it = pCtx->vecStreams.begin(); it != pCtx->vecStreams.end(); it++)
413 {
414 PVIDEORECSTREAM pStream = (*it);
415
416 if ( pStream->fEnabled
417 && ASMAtomicReadBool(&pStream->fRgbFilled))
418 {
419 rc = videoRecRGBToYUV(pStream);
420
421 ASMAtomicWriteBool(&pStream->fRgbFilled, false);
422
423 if (RT_SUCCESS(rc))
424 rc = videoRecEncodeAndWrite(pStream);
425
426 if (RT_FAILURE(rc))
427 {
428 static unsigned cErrors = 100;
429 if (cErrors > 0)
430 {
431 LogRel(("Error %Rrc encoding / writing video frame\n", rc));
432 cErrors--;
433 }
434 }
435 }
436 }
437 }
438
439 return VINF_SUCCESS;
440}
441
442/**
443 * Creates a video recording context.
444 *
445 * @returns IPRT status code.
446 * @param cScreens Number of screens to create context for.
447 * @param ppCtx Pointer to created video recording context on success.
448 */
449int VideoRecContextCreate(uint32_t cScreens, PVIDEORECCONTEXT *ppCtx)
450{
451 AssertReturn(cScreens, VERR_INVALID_PARAMETER);
452 AssertPtrReturn(ppCtx, VERR_INVALID_POINTER);
453
454 Assert(ASMAtomicReadU32(&g_enmState) == VIDREC_UNINITIALIZED);
455
456 int rc = VINF_SUCCESS;
457
458 PVIDEORECCONTEXT pCtx = (PVIDEORECCONTEXT)RTMemAllocZ(sizeof(VIDEORECCONTEXT));
459 if (!pCtx)
460 return VERR_NO_MEMORY;
461
462 for (uint32_t uScreen = 0; uScreen < cScreens; uScreen++)
463 {
464 PVIDEORECSTREAM pStream = (PVIDEORECSTREAM)RTMemAllocZ(sizeof(VIDEORECSTREAM));
465 if (!pStream)
466 {
467 rc = VERR_NO_MEMORY;
468 break;
469 }
470
471 try
472 {
473 pCtx->vecStreams.push_back(pStream);
474
475 pStream->pEBML = new WebMWriter();
476 }
477 catch (std::bad_alloc)
478 {
479 rc = VERR_NO_MEMORY;
480 break;
481 }
482 }
483
484 if (RT_SUCCESS(rc))
485 {
486 rc = RTSemEventCreate(&pCtx->WaitEvent);
487 AssertRCReturn(rc, rc);
488
489 rc = RTSemEventCreate(&pCtx->TermEvent);
490 AssertRCReturn(rc, rc);
491
492 rc = RTThreadCreate(&pCtx->Thread, videoRecThread, (void*)pCtx, 0,
493 RTTHREADTYPE_MAIN_WORKER, RTTHREADFLAGS_WAITABLE, "VideoRec");
494 AssertRCReturn(rc, rc);
495
496 ASMAtomicWriteU32(&g_enmState, VIDREC_IDLE);
497
498 if (ppCtx)
499 *ppCtx = pCtx;
500 }
501 else
502 {
503 /* Roll back allocations on error. */
504 VideoRecStreams::iterator it = pCtx->vecStreams.begin();
505 while (it != pCtx->vecStreams.end())
506 {
507 PVIDEORECSTREAM pStream = (*it);
508
509 if (pStream->pEBML)
510 delete pStream->pEBML;
511
512 RTMemFree(pStream);
513 pStream = NULL;
514
515 it = pCtx->vecStreams.erase(it);
516 }
517
518 Assert(pCtx->vecStreams.empty());
519 }
520
521 return rc;
522}
523
524/**
525 * Destroys a video recording context.
526 *
527 * @param pCtx Video recording context to destroy.
528 */
529void VideoRecContextDestroy(PVIDEORECCONTEXT pCtx)
530{
531 if (!pCtx)
532 return;
533
534 uint32_t enmState = VIDREC_IDLE;
535
536 for (;;) /** @todo r=andy Remove busy waiting! */
537 {
538 if (ASMAtomicCmpXchgExU32(&g_enmState, VIDREC_TERMINATING, enmState, &enmState))
539 break;
540 if (enmState == VIDREC_UNINITIALIZED)
541 return;
542 }
543
544 if (enmState == VIDREC_COPYING)
545 {
546 int rc = RTSemEventWait(pCtx->TermEvent, RT_INDEFINITE_WAIT);
547 AssertRC(rc);
548 }
549
550 RTSemEventSignal(pCtx->WaitEvent);
551 RTThreadWait(pCtx->Thread, 10 * 1000, NULL);
552 RTSemEventDestroy(pCtx->WaitEvent);
553 RTSemEventDestroy(pCtx->TermEvent);
554
555 for (VideoRecStreams::iterator it = pCtx->vecStreams.begin(); it != pCtx->vecStreams.end(); it++)
556 {
557 PVIDEORECSTREAM pStream = (*it);
558
559 if (pStream->fEnabled)
560 {
561 AssertPtr(pStream->pEBML);
562 int rc = pStream->pEBML->writeFooter(0);
563 AssertRC(rc);
564
565 pStream->pEBML->close();
566
567 vpx_img_free(&pStream->VpxRawImage);
568 vpx_codec_err_t rcv = vpx_codec_destroy(&pStream->VpxCodec);
569 Assert(rcv == VPX_CODEC_OK); RT_NOREF(rcv);
570
571 if (pStream->pu8RgbBuf)
572 {
573 RTMemFree(pStream->pu8RgbBuf);
574 pStream->pu8RgbBuf = NULL;
575 }
576 }
577
578 delete pStream->pEBML;
579 }
580
581 RTMemFree(pCtx);
582
583 ASMAtomicWriteU32(&g_enmState, VIDREC_UNINITIALIZED);
584}
585
586/**
587 * VideoRec utility function to initialize video recording context.
588 *
589 * @returns IPRT status code.
590 * @param pCtx Pointer to video recording context to initialize Framebuffer width.
591 * @param uScreen Screen number.
592 * @param pszFile File to save the recorded data
593 * @param uWidth Width of the target image in the video recoriding file (movie)
594 * @param uHeight Height of the target image in video recording file.
595 * @param uRate Rate.
596 * @param uFps FPS.
597 * @param uMaxTime
598 * @param uMaxFileSize
599 * @param pszOptions
600 */
601int VideoRecStreamInit(PVIDEORECCONTEXT pCtx, uint32_t uScreen, const char *pszFile,
602 uint32_t uWidth, uint32_t uHeight, uint32_t uRate, uint32_t uFps,
603 uint32_t uMaxTime, uint32_t uMaxFileSize, const char *pszOptions)
604{
605 AssertPtrReturn(pCtx, VERR_INVALID_PARAMETER);
606 AssertReturn(uScreen < pCtx->vecStreams.size(), VERR_INVALID_PARAMETER);
607
608 pCtx->u64MaxTimeStamp = (uMaxTime > 0 ? RTTimeProgramMilliTS() + uMaxTime * 1000 : 0);
609 pCtx->uMaxFileSize = uMaxFileSize;
610
611 PVIDEORECSTREAM pStream = pCtx->vecStreams.at(uScreen);
612
613 pStream->uTargetWidth = uWidth;
614 pStream->uTargetHeight = uHeight;
615 pStream->pu8RgbBuf = (uint8_t *)RTMemAllocZ(uWidth * uHeight * 4);
616 AssertReturn(pStream->pu8RgbBuf, VERR_NO_MEMORY);
617 pStream->uEncoderDeadline = VPX_DL_REALTIME;
618
619 /* Play safe: the file must not exist, overwriting is potentially
620 * hazardous as nothing prevents the user from picking a file name of some
621 * other important file, causing unintentional data loss. */
622
623 int rc = pStream->pEBML->create(pszFile);
624 if (RT_FAILURE(rc))
625 {
626 LogRel(("Failed to create the video capture output file \"%s\" (%Rrc)\n", pszFile, rc));
627 return rc;
628 }
629
630 vpx_codec_err_t rcv = vpx_codec_enc_config_default(DEFAULTCODEC, &pStream->VpxConfig, 0);
631 if (rcv != VPX_CODEC_OK)
632 {
633 LogFlow(("Failed to configure codec: %s\n", vpx_codec_err_to_string(rcv)));
634 return VERR_INVALID_PARAMETER;
635 }
636
637 com::Utf8Str options(pszOptions);
638 size_t pos = 0;
639
640 do {
641
642 com::Utf8Str key, value;
643 pos = options.parseKeyValue(key, value, pos);
644
645 if (key == "quality")
646 {
647 if (value == "realtime")
648 {
649 pStream->uEncoderDeadline = VPX_DL_REALTIME;
650 }
651 else if (value == "good")
652 {
653 pStream->uEncoderDeadline = 1000000 / uFps;
654 }
655 else if (value == "best")
656 {
657 pStream->uEncoderDeadline = VPX_DL_BEST_QUALITY;
658 }
659 else
660 {
661 LogRel(("Settings quality deadline to = %s\n", value.c_str()));
662 pStream->uEncoderDeadline = value.toUInt32();
663 }
664 }
665 else LogRel(("Getting unknown option: %s=%s\n", key.c_str(), value.c_str()));
666
667 } while(pos != com::Utf8Str::npos);
668
669 /* target bitrate in kilobits per second */
670 pStream->VpxConfig.rc_target_bitrate = uRate;
671 /* frame width */
672 pStream->VpxConfig.g_w = uWidth;
673 /* frame height */
674 pStream->VpxConfig.g_h = uHeight;
675 /* 1ms per frame */
676 pStream->VpxConfig.g_timebase.num = 1;
677 pStream->VpxConfig.g_timebase.den = 1000;
678 /* disable multithreading */
679 pStream->VpxConfig.g_threads = 0;
680
681 pStream->uDelay = 1000 / uFps;
682
683 struct vpx_rational arg_framerate = { (int)uFps, 1 };
684 rc = pStream->pEBML->writeHeader(&pStream->VpxConfig, &arg_framerate);
685 AssertRCReturn(rc, rc);
686
687 /* Initialize codec */
688 rcv = vpx_codec_enc_init(&pStream->VpxCodec, DEFAULTCODEC, &pStream->VpxConfig, 0);
689 if (rcv != VPX_CODEC_OK)
690 {
691 LogFlow(("Failed to initialize VP8 encoder %s", vpx_codec_err_to_string(rcv)));
692 return VERR_INVALID_PARAMETER;
693 }
694
695 if (!vpx_img_alloc(&pStream->VpxRawImage, VPX_IMG_FMT_I420, uWidth, uHeight, 1))
696 {
697 LogFlow(("Failed to allocate image %dx%d", uWidth, uHeight));
698 return VERR_NO_MEMORY;
699 }
700 pStream->pu8YuvBuf = pStream->VpxRawImage.planes[0];
701
702 pCtx->fEnabled = true;
703 pStream->fEnabled = true;
704 return VINF_SUCCESS;
705}
706
707/**
708 * VideoRec utility function to check if recording is enabled.
709 *
710 * @returns true if recording is enabled
711 * @param pCtx Pointer to video recording context.
712 */
713bool VideoRecIsEnabled(PVIDEORECCONTEXT pCtx)
714{
715 RT_NOREF(pCtx);
716 uint32_t enmState = ASMAtomicReadU32(&g_enmState);
717 return ( enmState == VIDREC_IDLE
718 || enmState == VIDREC_COPYING);
719}
720
721/**
722 * VideoRec utility function to check if recording engine is ready to accept a new frame
723 * for the given screen.
724 *
725 * @returns true if recording engine is ready
726 * @param pCtx Pointer to video recording context.
727 * @param uScreen Screen ID.
728 * @param u64TimeStamp Current time stamp.
729 */
730bool VideoRecIsReady(PVIDEORECCONTEXT pCtx, uint32_t uScreen, uint64_t u64TimeStamp)
731{
732 uint32_t enmState = ASMAtomicReadU32(&g_enmState);
733 if (enmState != VIDREC_IDLE)
734 return false;
735
736 PVIDEORECSTREAM pStream = pCtx->vecStreams.at(uScreen);
737 if (!pStream->fEnabled)
738 return false;
739
740 if (u64TimeStamp < pStream->u64LastTimeStamp + pStream->uDelay)
741 return false;
742
743 if (ASMAtomicReadBool(&pStream->fRgbFilled))
744 return false;
745
746 return true;
747}
748
749/**
750 * VideoRec utility function to check if the file size has reached
751 * specified limits (if any).
752 *
753 * @returns true if any limit has been reached.
754 * @param pCtx Pointer to video recording context.
755 * @param uScreen Screen ID.
756 * @param u64TimeStamp Current time stamp.
757 */
758
759bool VideoRecIsFull(PVIDEORECCONTEXT pCtx, uint32_t uScreen, uint64_t u64TimeStamp)
760{
761 PVIDEORECSTREAM pStream = pCtx->vecStreams.at(uScreen);
762 if(!pStream->fEnabled)
763 return false;
764
765 if(pCtx->u64MaxTimeStamp > 0 && u64TimeStamp >= pCtx->u64MaxTimeStamp)
766 return true;
767
768 if (pCtx->uMaxFileSize > 0)
769 {
770 uint64_t sizeInMB = pStream->pEBML->getFileSize() / (1024 * 1024);
771 if(sizeInMB >= pCtx->uMaxFileSize)
772 return true;
773 }
774 /* Check for available free disk space */
775 if (pStream->pEBML->getAvailableSpace() < 0x100000)
776 {
777 LogRel(("Storage has not enough free space available, stopping video capture\n"));
778 return true;
779 }
780
781 return false;
782}
783
784/**
785 * VideoRec utility function to encode the source image and write the encoded
786 * image to target file.
787 *
788 * @returns IPRT status code.
789 * @param pStream Stream to encode and write.
790 */
791static int videoRecEncodeAndWrite(PVIDEORECSTREAM pStream)
792{
793 /* presentation time stamp */
794 vpx_codec_pts_t pts = pStream->u64TimeStamp;
795 vpx_codec_err_t rcv = vpx_codec_encode(&pStream->VpxCodec,
796 &pStream->VpxRawImage,
797 pts /* time stamp */,
798 pStream->uDelay /* how long to show this frame */,
799 0 /* flags */,
800 pStream->uEncoderDeadline /* quality setting */);
801 if (rcv != VPX_CODEC_OK)
802 {
803 LogFlow(("Failed to encode:%s\n", vpx_codec_err_to_string(rcv)));
804 return VERR_GENERAL_FAILURE;
805 }
806
807 vpx_codec_iter_t iter = NULL;
808 int rc = VERR_NO_DATA;
809 for (;;)
810 {
811 const vpx_codec_cx_pkt_t *pkt = vpx_codec_get_cx_data(&pStream->VpxCodec, &iter);
812 if (!pkt)
813 break;
814 switch (pkt->kind)
815 {
816 case VPX_CODEC_CX_FRAME_PKT:
817 rc = pStream->pEBML->writeBlock(&pStream->VpxConfig, pkt);
818 break;
819 default:
820 LogFlow(("Unexpected CODEC Packet.\n"));
821 break;
822 }
823 }
824
825 pStream->cFrame++;
826 return rc;
827}
828
829/**
830 * VideoRec utility function to convert RGB to YUV.
831 *
832 * @returns IPRT status code.
833 * @param pStrm Strm.
834 */
835static int videoRecRGBToYUV(PVIDEORECSTREAM pStrm)
836{
837 switch (pStrm->u32PixelFormat)
838 {
839 case VPX_IMG_FMT_RGB32:
840 LogFlow(("32 bit\n"));
841 if (!colorConvWriteYUV420p<ColorConvBGRA32Iter>(pStrm->uTargetWidth,
842 pStrm->uTargetHeight,
843 pStrm->pu8YuvBuf,
844 pStrm->pu8RgbBuf))
845 return VERR_GENERAL_FAILURE;
846 break;
847 case VPX_IMG_FMT_RGB24:
848 LogFlow(("24 bit\n"));
849 if (!colorConvWriteYUV420p<ColorConvBGR24Iter>(pStrm->uTargetWidth,
850 pStrm->uTargetHeight,
851 pStrm->pu8YuvBuf,
852 pStrm->pu8RgbBuf))
853 return VERR_GENERAL_FAILURE;
854 break;
855 case VPX_IMG_FMT_RGB565:
856 LogFlow(("565 bit\n"));
857 if (!colorConvWriteYUV420p<ColorConvBGR565Iter>(pStrm->uTargetWidth,
858 pStrm->uTargetHeight,
859 pStrm->pu8YuvBuf,
860 pStrm->pu8RgbBuf))
861 return VERR_GENERAL_FAILURE;
862 break;
863 default:
864 return VERR_GENERAL_FAILURE;
865 }
866 return VINF_SUCCESS;
867}
868
869/**
870 * VideoRec utility function to copy a source image (FrameBuf) to the intermediate
871 * RGB buffer. This function is executed only once per time.
872 *
873 * @thread EMT
874 *
875 * @returns IPRT status code.
876 * @param pCtx Pointer to the video recording context.
877 * @param uScreen Screen number.
878 * @param x Starting x coordinate of the source buffer (Framebuffer).
879 * @param y Starting y coordinate of the source buffer (Framebuffer).
880 * @param uPixelFormat Pixel Format.
881 * @param uBitsPerPixel Bits Per Pixel
882 * @param uBytesPerLine Bytes per source scanlineName.
883 * @param uSourceWidth Width of the source image (framebuffer).
884 * @param uSourceHeight Height of the source image (framebuffer).
885 * @param pu8BufAddr Pointer to source image(framebuffer).
886 * @param u64TimeStamp Time stamp (milliseconds).
887 */
888int VideoRecCopyToIntBuf(PVIDEORECCONTEXT pCtx, uint32_t uScreen, uint32_t x, uint32_t y,
889 uint32_t uPixelFormat, uint32_t uBitsPerPixel, uint32_t uBytesPerLine,
890 uint32_t uSourceWidth, uint32_t uSourceHeight, uint8_t *pu8BufAddr,
891 uint64_t u64TimeStamp)
892{
893 /* Do not execute during termination and guard against termination */
894 if (!ASMAtomicCmpXchgU32(&g_enmState, VIDREC_COPYING, VIDREC_IDLE))
895 return VINF_TRY_AGAIN;
896
897 int rc = VINF_SUCCESS;
898 do
899 {
900 AssertPtrBreakStmt(pu8BufAddr, rc = VERR_INVALID_PARAMETER);
901 AssertBreakStmt(uSourceWidth, rc = VERR_INVALID_PARAMETER);
902 AssertBreakStmt(uSourceHeight, rc = VERR_INVALID_PARAMETER);
903 AssertBreakStmt(uScreen < pCtx->vecStreams.size(), rc = VERR_INVALID_PARAMETER);
904
905 PVIDEORECSTREAM pStream = pCtx->vecStreams.at(uScreen);
906 if (!pStream->fEnabled)
907 {
908 rc = VINF_TRY_AGAIN; /* not (yet) enabled */
909 break;
910 }
911 if (u64TimeStamp < pStream->u64LastTimeStamp + pStream->uDelay)
912 {
913 rc = VINF_TRY_AGAIN; /* respect maximum frames per second */
914 break;
915 }
916 if (ASMAtomicReadBool(&pStream->fRgbFilled))
917 {
918 rc = VERR_TRY_AGAIN; /* previous frame not yet encoded */
919 break;
920 }
921
922 pStream->u64LastTimeStamp = u64TimeStamp;
923
924 int xDiff = ((int)pStream->uTargetWidth - (int)uSourceWidth) / 2;
925 uint32_t w = uSourceWidth;
926 if ((int)w + xDiff + (int)x <= 0) /* nothing visible */
927 {
928 rc = VERR_INVALID_PARAMETER;
929 break;
930 }
931
932 uint32_t destX;
933 if ((int)x < -xDiff)
934 {
935 w += xDiff + x;
936 x = -xDiff;
937 destX = 0;
938 }
939 else
940 destX = x + xDiff;
941
942 uint32_t h = uSourceHeight;
943 int yDiff = ((int)pStream->uTargetHeight - (int)uSourceHeight) / 2;
944 if ((int)h + yDiff + (int)y <= 0) /* nothing visible */
945 {
946 rc = VERR_INVALID_PARAMETER;
947 break;
948 }
949
950 uint32_t destY;
951 if ((int)y < -yDiff)
952 {
953 h += yDiff + (int)y;
954 y = -yDiff;
955 destY = 0;
956 }
957 else
958 destY = y + yDiff;
959
960 if ( destX > pStream->uTargetWidth
961 || destY > pStream->uTargetHeight)
962 {
963 rc = VERR_INVALID_PARAMETER; /* nothing visible */
964 break;
965 }
966
967 if (destX + w > pStream->uTargetWidth)
968 w = pStream->uTargetWidth - destX;
969
970 if (destY + h > pStream->uTargetHeight)
971 h = pStream->uTargetHeight - destY;
972
973 /* Calculate bytes per pixel */
974 uint32_t bpp = 1;
975 if (uPixelFormat == BitmapFormat_BGR)
976 {
977 switch (uBitsPerPixel)
978 {
979 case 32:
980 pStream->u32PixelFormat = VPX_IMG_FMT_RGB32;
981 bpp = 4;
982 break;
983 case 24:
984 pStream->u32PixelFormat = VPX_IMG_FMT_RGB24;
985 bpp = 3;
986 break;
987 case 16:
988 pStream->u32PixelFormat = VPX_IMG_FMT_RGB565;
989 bpp = 2;
990 break;
991 default:
992 AssertMsgFailed(("Unknown color depth! mBitsPerPixel=%d\n", uBitsPerPixel));
993 break;
994 }
995 }
996 else
997 AssertMsgFailed(("Unknown pixel format! mPixelFormat=%d\n", uPixelFormat));
998
999 /* One of the dimensions of the current frame is smaller than before so
1000 * clear the entire buffer to prevent artifacts from the previous frame */
1001 if ( uSourceWidth < pStream->uLastSourceWidth
1002 || uSourceHeight < pStream->uLastSourceHeight)
1003 memset(pStream->pu8RgbBuf, 0, pStream->uTargetWidth * pStream->uTargetHeight * 4);
1004
1005 pStream->uLastSourceWidth = uSourceWidth;
1006 pStream->uLastSourceHeight = uSourceHeight;
1007
1008 /* Calculate start offset in source and destination buffers */
1009 uint32_t offSrc = y * uBytesPerLine + x * bpp;
1010 uint32_t offDst = (destY * pStream->uTargetWidth + destX) * bpp;
1011 /* do the copy */
1012 for (unsigned int i = 0; i < h; i++)
1013 {
1014 /* Overflow check */
1015 Assert(offSrc + w * bpp <= uSourceHeight * uBytesPerLine);
1016 Assert(offDst + w * bpp <= pStream->uTargetHeight * pStream->uTargetWidth * bpp);
1017 memcpy(pStream->pu8RgbBuf + offDst, pu8BufAddr + offSrc, w * bpp);
1018 offSrc += uBytesPerLine;
1019 offDst += pStream->uTargetWidth * bpp;
1020 }
1021
1022 pStream->u64TimeStamp = u64TimeStamp;
1023
1024 ASMAtomicWriteBool(&pStream->fRgbFilled, true);
1025 RTSemEventSignal(pCtx->WaitEvent);
1026 } while (0);
1027
1028 if (!ASMAtomicCmpXchgU32(&g_enmState, VIDREC_IDLE, VIDREC_COPYING))
1029 {
1030 rc = RTSemEventSignal(pCtx->TermEvent);
1031 AssertRC(rc);
1032 }
1033
1034 return rc;
1035}
Note: See TracBrowser for help on using the repository browser.

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