VirtualBox

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

Last change on this file since 75066 was 75066, checked in by vboxsync, 7 years ago

VideoRec/Main: More renaming.

  • 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: 23.7 KB
Line 
1/* $Id: VideoRec.cpp 75066 2018-10-25 12:58:49Z vboxsync $ */
2/** @file
3 * Video recording (with optional audio recording) code.
4 *
5 * This code employs a separate encoding thread per recording context
6 * to keep time spent in EMT as short as possible. Each configured VM display
7 * is represented by an own recording stream, which in turn has its own rendering
8 * queue. Common recording data across all recording streams is kept in a
9 * separate queue in the recording context to minimize data duplication and
10 * multiplexing overhead in EMT.
11 */
12
13/*
14 * Copyright (C) 2012-2018 Oracle Corporation
15 *
16 * This file is part of VirtualBox Open Source Edition (OSE), as
17 * available from http://www.virtualbox.org. This file is free software;
18 * you can redistribute it and/or modify it under the terms of the GNU
19 * General Public License (GPL) as published by the Free Software
20 * Foundation, in version 2 as it comes in the "COPYING" file of the
21 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
22 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
23 */
24
25#ifdef LOG_GROUP
26# undef LOG_GROUP
27#endif
28#define LOG_GROUP LOG_GROUP_MAIN_DISPLAY
29#include "LoggingNew.h"
30
31#include <stdexcept>
32#include <vector>
33
34#include <iprt/asm.h>
35#include <iprt/assert.h>
36#include <iprt/critsect.h>
37#include <iprt/path.h>
38#include <iprt/semaphore.h>
39#include <iprt/thread.h>
40#include <iprt/time.h>
41
42#include <VBox/err.h>
43#include <VBox/com/VirtualBox.h>
44
45#include "WebMWriter.h"
46#include "VideoRec.h"
47#include "VideoRecInternals.h"
48#include "VideoRecStream.h"
49#include "VideoRecUtils.h"
50
51using namespace com;
52
53#ifdef DEBUG_andy
54/** Enables dumping audio / video data for debugging reasons. */
55//# define VBOX_VIDEOREC_DUMP
56#endif
57
58/**
59 * Enumeration for a video recording state.
60 */
61enum VIDEORECSTS
62{
63 /** Not initialized. */
64 VIDEORECSTS_UNINITIALIZED = 0,
65 /** Initialized. */
66 VIDEORECSTS_INITIALIZED = 1,
67 /** The usual 32-bit hack. */
68 VIDEORECSTS_32BIT_HACK = 0x7fffffff
69};
70
71
72#ifdef VBOX_VIDEOREC_DUMP
73#pragma pack(push)
74#pragma pack(1)
75typedef struct
76{
77 uint16_t u16Magic;
78 uint32_t u32Size;
79 uint16_t u16Reserved1;
80 uint16_t u16Reserved2;
81 uint32_t u32OffBits;
82} VIDEORECBMPHDR, *PVIDEORECBMPHDR;
83AssertCompileSize(VIDEORECBMPHDR, 14);
84
85typedef struct
86{
87 uint32_t u32Size;
88 uint32_t u32Width;
89 uint32_t u32Height;
90 uint16_t u16Planes;
91 uint16_t u16BitCount;
92 uint32_t u32Compression;
93 uint32_t u32SizeImage;
94 uint32_t u32XPelsPerMeter;
95 uint32_t u32YPelsPerMeter;
96 uint32_t u32ClrUsed;
97 uint32_t u32ClrImportant;
98} VIDEORECBMPDIBHDR, *PVIDEORECBMPDIBHDR;
99AssertCompileSize(VIDEORECBMPDIBHDR, 40);
100
101#pragma pack(pop)
102#endif /* VBOX_VIDEOREC_DUMP */
103
104
105/**
106 * Worker thread for all streams of a video recording context.
107 *
108 * For video frames, this also does the RGB/YUV conversion and encoding.
109 */
110static DECLCALLBACK(int) videoRecThread(RTTHREAD hThreadSelf, void *pvUser)
111{
112 PVIDEORECCONTEXT pCtx = (PVIDEORECCONTEXT)pvUser;
113
114 /* Signal that we're up and rockin'. */
115 RTThreadUserSignal(hThreadSelf);
116
117 LogFunc(("Thread started\n"));
118
119 for (;;)
120 {
121 int rc = RTSemEventWait(pCtx->WaitEvent, RT_INDEFINITE_WAIT);
122 AssertRCBreak(rc);
123
124 Log2Func(("Processing %zu streams\n", pCtx->vecStreams.size()));
125
126 /** @todo r=andy This is inefficient -- as we already wake up this thread
127 * for every screen from Main, we here go again (on every wake up) through
128 * all screens. */
129 VideoRecStreams::iterator itStream = pCtx->vecStreams.begin();
130 while (itStream != pCtx->vecStreams.end())
131 {
132 PVIDEORECSTREAM pStream = (*itStream);
133
134 rc = VideoRecStreamProcess(pStream);
135 if (RT_FAILURE(rc))
136 break;
137
138 ++itStream;
139 }
140
141 if (RT_FAILURE(rc))
142 LogRel(("VideoRec: Encoding thread failed with rc=%Rrc\n", rc));
143
144 /* Keep going in case of errors. */
145
146 if (ASMAtomicReadBool(&pCtx->fShutdown))
147 {
148 LogFunc(("Thread is shutting down ...\n"));
149 break;
150 }
151
152 } /* for */
153
154 LogFunc(("Thread ended\n"));
155 return VINF_SUCCESS;
156}
157
158/**
159 * Notifies a recording context's encoding thread.
160 *
161 * @returns IPRT status code.
162 * @param pCtx Video recording context to notify thread for.
163 */
164static int videoRecThreadNotify(PVIDEORECCONTEXT pCtx)
165{
166 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
167
168 return RTSemEventSignal(pCtx->WaitEvent);
169}
170
171/**
172 * Creates a video recording context.
173 *
174 * @returns IPRT status code.
175 * @param cScreens Number of screens to create context for.
176 * @param pVideoRecCfg Pointer to video recording configuration to use.
177 * @param ppCtx Pointer to created video recording context on success.
178 */
179int VideoRecContextCreate(uint32_t cScreens, PVIDEORECCFG pVideoRecCfg, PVIDEORECCONTEXT *ppCtx)
180{
181 AssertReturn(cScreens, VERR_INVALID_PARAMETER);
182 AssertPtrReturn(pVideoRecCfg, VERR_INVALID_POINTER);
183 AssertPtrReturn(ppCtx, VERR_INVALID_POINTER);
184
185 VIDEORECCONTEXT *pCtx = NULL;
186 try
187 {
188 pCtx = new VIDEORECCONTEXT();
189 }
190 catch (std::bad_alloc &)
191 {
192 return VERR_NO_MEMORY;
193 }
194
195 int rc = RTCritSectInit(&pCtx->CritSect);
196 if (RT_FAILURE(rc))
197 {
198 delete pCtx;
199 return rc;
200 }
201
202 for (uint32_t uScreen = 0; uScreen < cScreens; uScreen++)
203 {
204 VIDEORECSTREAM *pStream = NULL;
205 try
206 {
207 pStream = new VIDEORECSTREAM();
208 }
209 catch (std::bad_alloc &)
210 {
211 rc = VERR_NO_MEMORY;
212 break;
213 }
214
215 rc = RTCritSectInit(&pStream->CritSect);
216 if (RT_FAILURE(rc))
217 break;
218
219 try
220 {
221 pStream->uScreenID = uScreen;
222
223 pCtx->vecStreams.push_back(pStream);
224
225 pStream->File.pWEBM = new WebMWriter();
226 }
227 catch (std::bad_alloc &)
228 {
229 rc = VERR_NO_MEMORY;
230 break;
231 }
232 }
233
234 if (RT_SUCCESS(rc))
235 {
236 pCtx->tsStartMs = RTTimeMilliTS();
237 pCtx->enmState = VIDEORECSTS_UNINITIALIZED;
238 pCtx->fStarted = false;
239 pCtx->fShutdown = false;
240
241 /* Copy the configuration to our context. */
242 pCtx->Cfg = *pVideoRecCfg;
243
244 rc = RTSemEventCreate(&pCtx->WaitEvent);
245 AssertRCReturn(rc, rc);
246
247 rc = RTThreadCreate(&pCtx->Thread, videoRecThread, (void *)pCtx, 0,
248 RTTHREADTYPE_MAIN_WORKER, RTTHREADFLAGS_WAITABLE, "VideoRec");
249
250 if (RT_SUCCESS(rc)) /* Wait for the thread to start. */
251 rc = RTThreadUserWait(pCtx->Thread, 30 * RT_MS_1SEC /* 30s timeout */);
252
253 if (RT_SUCCESS(rc))
254 {
255 pCtx->enmState = VIDEORECSTS_INITIALIZED;
256 pCtx->fStarted = true;
257
258 if (ppCtx)
259 *ppCtx = pCtx;
260 }
261 }
262
263 if (RT_FAILURE(rc))
264 {
265 int rc2 = VideoRecContextDestroy(pCtx);
266 AssertRC(rc2);
267 }
268
269 return rc;
270}
271
272/**
273 * Destroys a video recording context.
274 *
275 * @param pCtx Video recording context to destroy.
276 */
277int VideoRecContextDestroy(PVIDEORECCONTEXT pCtx)
278{
279 if (!pCtx)
280 return VINF_SUCCESS;
281
282 int rc = VINF_SUCCESS;
283
284 if (pCtx->enmState == VIDEORECSTS_INITIALIZED)
285 {
286 LogFunc(("Shutting down thread ...\n"));
287
288 /* Set shutdown indicator. */
289 ASMAtomicWriteBool(&pCtx->fShutdown, true);
290
291 /* Signal the thread and wait for it to shut down. */
292 rc = videoRecThreadNotify(pCtx);
293 if (RT_SUCCESS(rc))
294 rc = RTThreadWait(pCtx->Thread, 30 * 1000 /* 10s timeout */, NULL);
295
296 if (RT_SUCCESS(rc))
297 {
298 /* Disable the context. */
299 ASMAtomicWriteBool(&pCtx->fStarted, false);
300
301 int rc2 = RTSemEventDestroy(pCtx->WaitEvent);
302 AssertRC(rc2);
303
304 pCtx->WaitEvent = NIL_RTSEMEVENT;
305 }
306 }
307
308 if (RT_FAILURE(rc))
309 {
310 AssertRC(rc);
311 return rc;
312 }
313
314 rc = RTCritSectEnter(&pCtx->CritSect);
315 if (RT_SUCCESS(rc))
316 {
317 VideoRecStreams::iterator it = pCtx->vecStreams.begin();
318 while (it != pCtx->vecStreams.end())
319 {
320 PVIDEORECSTREAM pStream = (*it);
321
322 VideoRecStreamLock(pStream);
323
324 int rc2 = VideoRecStreamClose(pStream);
325 if (RT_SUCCESS(rc))
326 rc = rc2;
327
328 rc2 = VideoRecStreamUninit(pStream);
329 if (RT_SUCCESS(rc))
330 rc = rc2;
331
332 pCtx->vecStreams.erase(it);
333 it = pCtx->vecStreams.begin();
334
335 VideoRecStreamUnlock(pStream);
336
337 RTCritSectDelete(&pStream->CritSect);
338
339 delete pStream;
340 pStream = NULL;
341 }
342
343 /* Sanity. */
344 Assert(pCtx->vecStreams.empty());
345 Assert(pCtx->mapBlocksCommon.size() == 0);
346
347 int rc2 = RTCritSectLeave(&pCtx->CritSect);
348 AssertRC(rc2);
349
350 RTCritSectDelete(&pCtx->CritSect);
351
352 delete pCtx;
353 pCtx = NULL;
354 }
355
356 return rc;
357}
358
359/**
360 * Returns which recording features currently are enabled for a given configuration.
361 *
362 * @returns Enabled video recording features.
363 * @param pCfg Pointer to recording configuration.
364 */
365VIDEORECFEATURES VideoRecGetFeatures(PVIDEORECCFG pCfg)
366{
367 if (!pCfg)
368 return VIDEORECFEATURE_NONE;
369
370 VIDEORECFEATURES fFeatures = VIDEORECFEATURE_NONE;
371
372 if (pCfg->Video.fEnabled)
373 fFeatures |= VIDEORECFEATURE_VIDEO;
374
375#ifdef VBOX_WITH_AUDIO_VIDEOREC
376 if (pCfg->Audio.fEnabled)
377 fFeatures |= VIDEORECFEATURE_AUDIO;
378#endif
379
380 return fFeatures;
381}
382
383/**
384 * Retrieves a specific recording stream of a recording context.
385 *
386 * @returns Pointer to recording stream if found, or NULL if not found.
387 * @param pCtx Recording context to look up stream for.
388 * @param uScreen Screen number of recording stream to look up.
389 */
390PVIDEORECSTREAM videoRecGetStream(PVIDEORECCONTEXT pCtx, uint32_t uScreen)
391{
392 AssertPtrReturn(pCtx, NULL);
393
394 PVIDEORECSTREAM pStream;
395
396 try
397 {
398 pStream = pCtx->vecStreams.at(uScreen);
399 }
400 catch (std::out_of_range &)
401 {
402 pStream = NULL;
403 }
404
405 return pStream;
406}
407
408/**
409 * Checks if recording engine is ready to accept new recording data for a given screen.
410 *
411 * @returns true if recording engine is ready, false if not.
412 * @param pCtx Pointer to video recording context.
413 * @param uScreen Screen ID.
414 * @param uTimeStampMs Current time stamp (in ms). Currently not being used.
415 */
416bool VideoRecIsReady(PVIDEORECCONTEXT pCtx, uint32_t uScreen, uint64_t uTimeStampMs)
417{
418 AssertPtrReturn(pCtx, false);
419 RT_NOREF(uTimeStampMs);
420
421 if (ASMAtomicReadU32(&pCtx->enmState) != VIDEORECSTS_INITIALIZED)
422 return false;
423
424 bool fIsReady = false;
425
426 PVIDEORECSTREAM pStream = videoRecGetStream(pCtx, uScreen);
427 if (pStream)
428 {
429 VideoRecStreamLock(pStream);
430 fIsReady = pStream->fEnabled;
431 VideoRecStreamUnlock(pStream);
432 }
433
434 /* Note: Do not check for other constraints like the video FPS rate here,
435 * as this check then also would affect other (non-FPS related) stuff
436 * like audio data. */
437
438 return fIsReady;
439}
440
441/**
442 * Returns whether a given recording context has been started or not.
443 *
444 * @returns true if active, false if not.
445 * @param pCtx Pointer to video recording context.
446 */
447bool VideoRecIsStarted(PVIDEORECCONTEXT pCtx)
448{
449 if (!pCtx)
450 return false;
451
452 return ASMAtomicReadBool(&pCtx->fStarted);
453}
454
455/**
456 * Checks if a specified limit for recording has been reached.
457 *
458 * @returns true if any limit has been reached.
459 * @param pCtx Pointer to video recording context.
460 * @param uScreen Screen ID.
461 * @param tsNowMs Current time stamp (in ms).
462 */
463bool VideoRecIsLimitReached(PVIDEORECCONTEXT pCtx, uint32_t uScreen, uint64_t tsNowMs)
464{
465 PVIDEORECSTREAM pStream = videoRecGetStream(pCtx, uScreen);
466 if ( !pStream
467 || !pStream->fEnabled)
468 {
469 return false;
470 }
471
472 const PVIDEORECCFG pCfg = &pCtx->Cfg;
473
474 if ( pCfg->uMaxTimeS
475 && tsNowMs >= pCtx->tsStartMs + (pCfg->uMaxTimeS * RT_MS_1SEC))
476 {
477 return true;
478 }
479
480 if (pCfg->enmDst == VIDEORECDEST_FILE)
481 {
482
483 if (pCfg->File.uMaxSizeMB)
484 {
485 uint64_t sizeInMB = pStream->File.pWEBM->GetFileSize() / _1M;
486 if(sizeInMB >= pCfg->File.uMaxSizeMB)
487 return true;
488 }
489
490 /* Check for available free disk space */
491 if ( pStream->File.pWEBM
492 && pStream->File.pWEBM->GetAvailableSpace() < 0x100000) /** @todo r=andy WTF? Fix this. */
493 {
494 LogRel(("VideoRec: Not enough free storage space available, stopping video capture\n"));
495 return true;
496 }
497 }
498
499 return false;
500}
501
502/**
503 * Sends an audio frame to the video encoding thread.
504 *
505 * @thread EMT
506 *
507 * @returns IPRT status code.
508 * @param pCtx Pointer to the video recording context.
509 * @param pvData Audio frame data to send.
510 * @param cbData Size (in bytes) of (encoded) audio frame data.
511 * @param uTimeStampMs Time stamp (in ms) of audio playback.
512 */
513int VideoRecSendAudioFrame(PVIDEORECCONTEXT pCtx, const void *pvData, size_t cbData, uint64_t uTimeStampMs)
514{
515#ifdef VBOX_WITH_AUDIO_VIDEOREC
516 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
517 AssertReturn(cbData, VERR_INVALID_PARAMETER);
518
519 /* To save time spent in EMT, do the required audio multiplexing in the encoding thread.
520 *
521 * The multiplexing is needed to supply all recorded (enabled) screens with the same
522 * audio data at the same given point in time.
523 */
524 PVIDEORECBLOCK pBlock = (PVIDEORECBLOCK)RTMemAlloc(sizeof(VIDEORECBLOCK));
525 AssertPtrReturn(pBlock, VERR_NO_MEMORY);
526 pBlock->enmType = VIDEORECBLOCKTYPE_AUDIO;
527
528 PVIDEORECAUDIOFRAME pFrame = (PVIDEORECAUDIOFRAME)RTMemAlloc(sizeof(VIDEORECAUDIOFRAME));
529 AssertPtrReturn(pFrame, VERR_NO_MEMORY);
530
531 pFrame->pvBuf = (uint8_t *)RTMemAlloc(cbData);
532 AssertPtrReturn(pFrame->pvBuf, VERR_NO_MEMORY);
533 pFrame->cbBuf = cbData;
534
535 memcpy(pFrame->pvBuf, pvData, cbData);
536
537 pBlock->pvData = pFrame;
538 pBlock->cbData = sizeof(VIDEORECAUDIOFRAME) + cbData;
539 pBlock->cRefs = (uint16_t)pCtx->vecStreams.size(); /* All streams need the same audio data. */
540 pBlock->uTimeStampMs = uTimeStampMs;
541
542 int rc = RTCritSectEnter(&pCtx->CritSect);
543 if (RT_FAILURE(rc))
544 return rc;
545
546 try
547 {
548 VideoRecBlockMap::iterator itBlocks = pCtx->mapBlocksCommon.find(uTimeStampMs);
549 if (itBlocks == pCtx->mapBlocksCommon.end())
550 {
551 VideoRecBlocks *pVideoRecBlocks = new VideoRecBlocks();
552 pVideoRecBlocks->List.push_back(pBlock);
553
554 pCtx->mapBlocksCommon.insert(std::make_pair(uTimeStampMs, pVideoRecBlocks));
555 }
556 else
557 itBlocks->second->List.push_back(pBlock);
558 }
559 catch (const std::exception &ex)
560 {
561 RT_NOREF(ex);
562 rc = VERR_NO_MEMORY;
563 }
564
565 int rc2 = RTCritSectLeave(&pCtx->CritSect);
566 AssertRC(rc2);
567
568 if (RT_SUCCESS(rc))
569 rc = videoRecThreadNotify(pCtx);
570
571 return rc;
572#else
573 RT_NOREF(pCtx, pvData, cbData, uTimeStampMs);
574 return VINF_SUCCESS;
575#endif
576}
577
578/**
579 * Copies a source video frame to the intermediate RGB buffer.
580 * This function is executed only once per time.
581 *
582 * @thread EMT
583 *
584 * @returns IPRT status code.
585 * @param pCtx Pointer to the video recording context.
586 * @param uScreen Screen number.
587 * @param x Starting x coordinate of the video frame.
588 * @param y Starting y coordinate of the video frame.
589 * @param uPixelFormat Pixel format.
590 * @param uBPP Bits Per Pixel (BPP).
591 * @param uBytesPerLine Bytes per scanline.
592 * @param uSrcWidth Width of the video frame.
593 * @param uSrcHeight Height of the video frame.
594 * @param puSrcData Pointer to video frame data.
595 * @param uTimeStampMs Time stamp (in ms).
596 */
597int VideoRecSendVideoFrame(PVIDEORECCONTEXT pCtx, uint32_t uScreen, uint32_t x, uint32_t y,
598 uint32_t uPixelFormat, uint32_t uBPP, uint32_t uBytesPerLine,
599 uint32_t uSrcWidth, uint32_t uSrcHeight, uint8_t *puSrcData,
600 uint64_t uTimeStampMs)
601{
602 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
603 AssertReturn(uSrcWidth, VERR_INVALID_PARAMETER);
604 AssertReturn(uSrcHeight, VERR_INVALID_PARAMETER);
605 AssertReturn(puSrcData, VERR_INVALID_POINTER);
606
607 int rc = RTCritSectEnter(&pCtx->CritSect);
608 AssertRC(rc);
609
610 PVIDEORECSTREAM pStream = videoRecGetStream(pCtx, uScreen);
611 if (!pStream)
612 {
613 rc = RTCritSectLeave(&pCtx->CritSect);
614 AssertRC(rc);
615
616 return VERR_NOT_FOUND;
617 }
618
619 VideoRecStreamLock(pStream);
620
621 PVIDEORECVIDEOFRAME pFrame = NULL;
622
623 do
624 {
625 if (!pStream->fEnabled)
626 {
627 rc = VINF_TRY_AGAIN; /* Not (yet) enabled. */
628 break;
629 }
630
631 if (uTimeStampMs < pStream->Video.uLastTimeStampMs + pStream->Video.uDelayMs)
632 {
633 rc = VINF_TRY_AGAIN; /* Respect maximum frames per second. */
634 break;
635 }
636
637 pStream->Video.uLastTimeStampMs = uTimeStampMs;
638
639 int xDiff = ((int)pStream->Video.uWidth - (int)uSrcWidth) / 2;
640 uint32_t w = uSrcWidth;
641 if ((int)w + xDiff + (int)x <= 0) /* Nothing visible. */
642 {
643 rc = VERR_INVALID_PARAMETER;
644 break;
645 }
646
647 uint32_t destX;
648 if ((int)x < -xDiff)
649 {
650 w += xDiff + x;
651 x = -xDiff;
652 destX = 0;
653 }
654 else
655 destX = x + xDiff;
656
657 uint32_t h = uSrcHeight;
658 int yDiff = ((int)pStream->Video.uHeight - (int)uSrcHeight) / 2;
659 if ((int)h + yDiff + (int)y <= 0) /* Nothing visible. */
660 {
661 rc = VERR_INVALID_PARAMETER;
662 break;
663 }
664
665 uint32_t destY;
666 if ((int)y < -yDiff)
667 {
668 h += yDiff + (int)y;
669 y = -yDiff;
670 destY = 0;
671 }
672 else
673 destY = y + yDiff;
674
675 if ( destX > pStream->Video.uWidth
676 || destY > pStream->Video.uHeight)
677 {
678 rc = VERR_INVALID_PARAMETER; /* Nothing visible. */
679 break;
680 }
681
682 if (destX + w > pStream->Video.uWidth)
683 w = pStream->Video.uWidth - destX;
684
685 if (destY + h > pStream->Video.uHeight)
686 h = pStream->Video.uHeight - destY;
687
688 pFrame = (PVIDEORECVIDEOFRAME)RTMemAllocZ(sizeof(VIDEORECVIDEOFRAME));
689 AssertBreakStmt(pFrame, rc = VERR_NO_MEMORY);
690
691 /* Calculate bytes per pixel and set pixel format. */
692 const unsigned uBytesPerPixel = uBPP / 8;
693 if (uPixelFormat == BitmapFormat_BGR)
694 {
695 switch (uBPP)
696 {
697 case 32:
698 pFrame->uPixelFormat = VIDEORECPIXELFMT_RGB32;
699 break;
700 case 24:
701 pFrame->uPixelFormat = VIDEORECPIXELFMT_RGB24;
702 break;
703 case 16:
704 pFrame->uPixelFormat = VIDEORECPIXELFMT_RGB565;
705 break;
706 default:
707 AssertMsgFailed(("Unknown color depth (%RU32)\n", uBPP));
708 break;
709 }
710 }
711 else
712 AssertMsgFailed(("Unknown pixel format (%RU32)\n", uPixelFormat));
713
714 const size_t cbRGBBuf = pStream->Video.uWidth
715 * pStream->Video.uHeight
716 * uBytesPerPixel;
717 AssertBreakStmt(cbRGBBuf, rc = VERR_INVALID_PARAMETER);
718
719 pFrame->pu8RGBBuf = (uint8_t *)RTMemAlloc(cbRGBBuf);
720 AssertBreakStmt(pFrame->pu8RGBBuf, rc = VERR_NO_MEMORY);
721 pFrame->cbRGBBuf = cbRGBBuf;
722 pFrame->uWidth = uSrcWidth;
723 pFrame->uHeight = uSrcHeight;
724
725 /* If the current video frame is smaller than video resolution we're going to encode,
726 * clear the frame beforehand to prevent artifacts. */
727 if ( uSrcWidth < pStream->Video.uWidth
728 || uSrcHeight < pStream->Video.uHeight)
729 {
730 RT_BZERO(pFrame->pu8RGBBuf, pFrame->cbRGBBuf);
731 }
732
733 /* Calculate start offset in source and destination buffers. */
734 uint32_t offSrc = y * uBytesPerLine + x * uBytesPerPixel;
735 uint32_t offDst = (destY * pStream->Video.uWidth + destX) * uBytesPerPixel;
736
737#ifdef VBOX_VIDEOREC_DUMP
738 VIDEORECBMPHDR bmpHdr;
739 RT_ZERO(bmpHdr);
740
741 VIDEORECBMPDIBHDR bmpDIBHdr;
742 RT_ZERO(bmpDIBHdr);
743
744 bmpHdr.u16Magic = 0x4d42; /* Magic */
745 bmpHdr.u32Size = (uint32_t)(sizeof(VIDEORECBMPHDR) + sizeof(VIDEORECBMPDIBHDR) + (w * h * uBytesPerPixel));
746 bmpHdr.u32OffBits = (uint32_t)(sizeof(VIDEORECBMPHDR) + sizeof(VIDEORECBMPDIBHDR));
747
748 bmpDIBHdr.u32Size = sizeof(VIDEORECBMPDIBHDR);
749 bmpDIBHdr.u32Width = w;
750 bmpDIBHdr.u32Height = h;
751 bmpDIBHdr.u16Planes = 1;
752 bmpDIBHdr.u16BitCount = uBPP;
753 bmpDIBHdr.u32XPelsPerMeter = 5000;
754 bmpDIBHdr.u32YPelsPerMeter = 5000;
755
756 char szFileName[RTPATH_MAX];
757 RTStrPrintf2(szFileName, sizeof(szFileName), "/tmp/VideoRecFrame-%RU32.bmp", uScreen);
758
759 RTFILE fh;
760 int rc2 = RTFileOpen(&fh, szFileName,
761 RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
762 if (RT_SUCCESS(rc2))
763 {
764 RTFileWrite(fh, &bmpHdr, sizeof(bmpHdr), NULL);
765 RTFileWrite(fh, &bmpDIBHdr, sizeof(bmpDIBHdr), NULL);
766 }
767#endif
768 Assert(pFrame->cbRGBBuf >= w * h * uBytesPerPixel);
769
770 /* Do the copy. */
771 for (unsigned int i = 0; i < h; i++)
772 {
773 /* Overflow check. */
774 Assert(offSrc + w * uBytesPerPixel <= uSrcHeight * uBytesPerLine);
775 Assert(offDst + w * uBytesPerPixel <= pStream->Video.uHeight * pStream->Video.uWidth * uBytesPerPixel);
776
777 memcpy(pFrame->pu8RGBBuf + offDst, puSrcData + offSrc, w * uBytesPerPixel);
778
779#ifdef VBOX_VIDEOREC_DUMP
780 if (RT_SUCCESS(rc2))
781 RTFileWrite(fh, pFrame->pu8RGBBuf + offDst, w * uBytesPerPixel, NULL);
782#endif
783 offSrc += uBytesPerLine;
784 offDst += pStream->Video.uWidth * uBytesPerPixel;
785 }
786
787#ifdef VBOX_VIDEOREC_DUMP
788 if (RT_SUCCESS(rc2))
789 RTFileClose(fh);
790#endif
791
792 } while (0);
793
794 if (rc == VINF_SUCCESS) /* Note: Also could be VINF_TRY_AGAIN. */
795 {
796 PVIDEORECBLOCK pBlock = (PVIDEORECBLOCK)RTMemAlloc(sizeof(VIDEORECBLOCK));
797 if (pBlock)
798 {
799 AssertPtr(pFrame);
800
801 pBlock->enmType = VIDEORECBLOCKTYPE_VIDEO;
802 pBlock->pvData = pFrame;
803 pBlock->cbData = sizeof(VIDEORECVIDEOFRAME) + pFrame->cbRGBBuf;
804
805 try
806 {
807 VideoRecBlocks *pVideoRecBlocks = new VideoRecBlocks();
808 pVideoRecBlocks->List.push_back(pBlock);
809
810 Assert(pStream->Blocks.Map.find(uTimeStampMs) == pStream->Blocks.Map.end());
811 pStream->Blocks.Map.insert(std::make_pair(uTimeStampMs, pVideoRecBlocks));
812 }
813 catch (const std::exception &ex)
814 {
815 RT_NOREF(ex);
816
817 RTMemFree(pBlock);
818 rc = VERR_NO_MEMORY;
819 }
820 }
821 else
822 rc = VERR_NO_MEMORY;
823 }
824
825 if (RT_FAILURE(rc))
826 VideoRecVideoFrameFree(pFrame);
827
828 VideoRecStreamUnlock(pStream);
829
830 int rc2 = RTCritSectLeave(&pCtx->CritSect);
831 AssertRC(rc2);
832
833 if ( RT_SUCCESS(rc)
834 && rc != VINF_TRY_AGAIN) /* Only signal the thread if operation was successful. */
835 {
836 videoRecThreadNotify(pCtx);
837 }
838
839 return rc;
840}
841
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