VirtualBox

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

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

VideoRec: Update.

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