VirtualBox

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

Last change on this file since 47469 was 46667, checked in by vboxsync, 11 years ago

Main/idl: clarify IMachine.videoCaptureFile to be an absolute path, and many naming convention cleanups for method/attribute names starting with an uppercase letter or containing an acronym
Main/xml/Settings.cpp: clean up default video capture file handling and related path conversions, version handling of the new functionality
Main/Machine: handle default value for video capture file better, store relative path in settings if possible, cleanups
Main/src-client/VideoRec.cpp: do not overwrite a file ever
Main/Display: generate a unique name if there is a collision, matching cleanups for name changes
Frontends/VirtualBox: matching name changes
Frontends/VBoxManage: matching name changes, fixing the machine readable output

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

© 2023 Oracle
ContactPrivacy policyTerms of Use