VirtualBox

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

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

VideoRec: Revamped stream context allocation / structure handling by using a vector now.

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