VirtualBox

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

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

Main: doxygen fixes

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