VirtualBox

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

Last change on this file since 46553 was 46553, checked in by vboxsync, 12 years ago

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