VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/RecordingUtils.cpp@ 98284

Last change on this file since 98284 was 98103, checked in by vboxsync, 23 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.5 KB
Line 
1/* $Id: RecordingUtils.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * Recording utility code.
4 */
5
6/*
7 * Copyright (C) 2012-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28#include "Recording.h"
29#include "RecordingUtils.h"
30
31#include <iprt/asm.h>
32#include <iprt/assert.h>
33#include <iprt/critsect.h>
34#include <iprt/path.h>
35#include <iprt/semaphore.h>
36#include <iprt/thread.h>
37#include <iprt/time.h>
38
39#ifdef DEBUG
40#include <iprt/file.h>
41#include <iprt/formats/bmp.h>
42#endif
43
44
45/**
46 * Convert an image to YUV420p format.
47 *
48 * @return \c true on success, \c false on failure.
49 * @param aDstBuf The destination image buffer.
50 * @param aDstWidth Width (in pixel) of destination buffer.
51 * @param aDstHeight Height (in pixel) of destination buffer.
52 * @param aSrcBuf The source image buffer.
53 * @param aSrcWidth Width (in pixel) of source buffer.
54 * @param aSrcHeight Height (in pixel) of source buffer.
55 */
56template <class T>
57inline bool recordingUtilsColorConvWriteYUV420p(uint8_t *aDstBuf, unsigned aDstWidth, unsigned aDstHeight,
58 uint8_t *aSrcBuf, unsigned aSrcWidth, unsigned aSrcHeight)
59{
60 RT_NOREF(aDstWidth, aDstHeight);
61
62 AssertReturn(!(aSrcWidth & 1), false);
63 AssertReturn(!(aSrcHeight & 1), false);
64
65 bool fRc = true;
66 T iter1(aSrcWidth, aSrcHeight, aSrcBuf);
67 T iter2 = iter1;
68 iter2.skip(aSrcWidth);
69 unsigned cPixels = aSrcWidth * aSrcHeight;
70 unsigned offY = 0;
71 unsigned offU = cPixels;
72 unsigned offV = cPixels + cPixels / 4;
73 unsigned const cyHalf = aSrcHeight / 2;
74 unsigned const cxHalf = aSrcWidth / 2;
75 for (unsigned i = 0; i < cyHalf && fRc; ++i)
76 {
77 for (unsigned j = 0; j < cxHalf; ++j)
78 {
79 unsigned red, green, blue;
80 fRc = iter1.getRGB(&red, &green, &blue);
81 AssertReturn(fRc, false);
82 aDstBuf[offY] = ((66 * red + 129 * green + 25 * blue + 128) >> 8) + 16;
83 unsigned u = (((-38 * red - 74 * green + 112 * blue + 128) >> 8) + 128) / 4;
84 unsigned v = (((112 * red - 94 * green - 18 * blue + 128) >> 8) + 128) / 4;
85
86 fRc = iter1.getRGB(&red, &green, &blue);
87 AssertReturn(fRc, false);
88 aDstBuf[offY + 1] = ((66 * red + 129 * green + 25 * blue + 128) >> 8) + 16;
89 u += (((-38 * red - 74 * green + 112 * blue + 128) >> 8) + 128) / 4;
90 v += (((112 * red - 94 * green - 18 * blue + 128) >> 8) + 128) / 4;
91
92 fRc = iter2.getRGB(&red, &green, &blue);
93 AssertReturn(fRc, false);
94 aDstBuf[offY + aSrcWidth] = ((66 * red + 129 * green + 25 * blue + 128) >> 8) + 16;
95 u += (((-38 * red - 74 * green + 112 * blue + 128) >> 8) + 128) / 4;
96 v += (((112 * red - 94 * green - 18 * blue + 128) >> 8) + 128) / 4;
97
98 fRc = iter2.getRGB(&red, &green, &blue);
99 AssertReturn(fRc, false);
100 aDstBuf[offY + aSrcWidth + 1] = ((66 * red + 129 * green + 25 * blue + 128) >> 8) + 16;
101 u += (((-38 * red - 74 * green + 112 * blue + 128) >> 8) + 128) / 4;
102 v += (((112 * red - 94 * green - 18 * blue + 128) >> 8) + 128) / 4;
103
104 aDstBuf[offU] = u;
105 aDstBuf[offV] = v;
106 offY += 2;
107 ++offU;
108 ++offV;
109 }
110
111 iter1.skip(aSrcWidth);
112 iter2.skip(aSrcWidth);
113 offY += aSrcWidth;
114 }
115
116 return true;
117}
118
119/**
120 * Convert an image to RGB24 format.
121 *
122 * @returns true on success, false on failure.
123 * @param aWidth Width of image.
124 * @param aHeight Height of image.
125 * @param aDestBuf An allocated memory buffer large enough to hold the
126 * destination image (i.e. width * height * 12bits).
127 * @param aSrcBuf The source image as an array of bytes.
128 */
129template <class T>
130inline bool RecordingUtilsColorConvWriteRGB24(unsigned aWidth, unsigned aHeight,
131 uint8_t *aDestBuf, uint8_t *aSrcBuf)
132{
133 enum { PIX_SIZE = 3 };
134 bool fRc = true;
135 AssertReturn(0 == (aWidth & 1), false);
136 AssertReturn(0 == (aHeight & 1), false);
137 T iter(aWidth, aHeight, aSrcBuf);
138 unsigned cPixels = aWidth * aHeight;
139 for (unsigned i = 0; i < cPixels && fRc; ++i)
140 {
141 unsigned red, green, blue;
142 fRc = iter.getRGB(&red, &green, &blue);
143 if (fRc)
144 {
145 aDestBuf[i * PIX_SIZE ] = red;
146 aDestBuf[i * PIX_SIZE + 1] = green;
147 aDestBuf[i * PIX_SIZE + 2] = blue;
148 }
149 }
150 return fRc;
151}
152
153/**
154 * Converts a RGB to YUV buffer.
155 *
156 * @returns IPRT status code.
157 * @param enmPixelFormat Pixel format to use for conversion.
158 * @param paDst Pointer to destination buffer.
159 * @param uDstWidth Width (X, in pixels) of destination buffer.
160 * @param uDstHeight Height (Y, in pixels) of destination buffer.
161 * @param paSrc Pointer to source buffer.
162 * @param uSrcWidth Width (X, in pixels) of source buffer.
163 * @param uSrcHeight Height (Y, in pixels) of source buffer.
164 */
165int RecordingUtilsRGBToYUV(RECORDINGPIXELFMT enmPixelFormat,
166 uint8_t *paDst, uint32_t uDstWidth, uint32_t uDstHeight,
167 uint8_t *paSrc, uint32_t uSrcWidth, uint32_t uSrcHeight)
168{
169 switch (enmPixelFormat)
170 {
171 case RECORDINGPIXELFMT_RGB32:
172 if (!recordingUtilsColorConvWriteYUV420p<ColorConvBGRA32Iter>(paDst, uDstWidth, uDstHeight,
173 paSrc, uSrcWidth, uSrcHeight))
174 return VERR_INVALID_PARAMETER;
175 break;
176 case RECORDINGPIXELFMT_RGB24:
177 if (!recordingUtilsColorConvWriteYUV420p<ColorConvBGR24Iter>(paDst, uDstWidth, uDstHeight,
178 paSrc, uSrcWidth, uSrcHeight))
179 return VERR_INVALID_PARAMETER;
180 break;
181 case RECORDINGPIXELFMT_RGB565:
182 if (!recordingUtilsColorConvWriteYUV420p<ColorConvBGR565Iter>(paDst, uDstWidth, uDstHeight,
183 paSrc, uSrcWidth, uSrcHeight))
184 return VERR_INVALID_PARAMETER;
185 break;
186 default:
187 AssertFailed();
188 return VERR_NOT_SUPPORTED;
189 }
190 return VINF_SUCCESS;
191}
192
193#ifdef DEBUG
194/**
195 * Dumps a video recording frame to a bitmap (BMP) file, extended version.
196 *
197 * @returns VBox status code.
198 * @param pu8RGBBuf Pointer to actual RGB frame data.
199 * @param cbRGBBuf Size (in bytes) of \a pu8RGBBuf.
200 * @param pszPath Absolute path to dump file to. Must exist.
201 * Specify NULL to use the system's temp directory.
202 * Existing frame files will be overwritten.
203 * @param pszPrefx Naming prefix to use. Optional and can be NULL.
204 * @param uWidth Width (in pixel) to write.
205 * @param uHeight Height (in pixel) to write.
206 * @param uBPP Bits in pixel.
207 */
208int RecordingUtilsDbgDumpFrameEx(const uint8_t *pu8RGBBuf, size_t cbRGBBuf, const char *pszPath, const char *pszPrefx,
209 uint16_t uWidth, uint32_t uHeight, uint8_t uBPP)
210{
211 RT_NOREF(cbRGBBuf);
212
213 const uint8_t uBytesPerPixel = uBPP / 8 /* Bits */;
214 const size_t cbData = uWidth * uHeight * uBytesPerPixel;
215
216 if (!cbData) /* No data to write? Bail out early. */
217 return VINF_SUCCESS;
218
219 BMPFILEHDR fileHdr;
220 RT_ZERO(fileHdr);
221
222 BMPWIN3XINFOHDR coreHdr;
223 RT_ZERO(coreHdr);
224
225 fileHdr.uType = BMP_HDR_MAGIC;
226 fileHdr.cbFileSize = (uint32_t)(sizeof(BMPFILEHDR) + sizeof(BMPWIN3XINFOHDR) + cbData);
227 fileHdr.offBits = (uint32_t)(sizeof(BMPFILEHDR) + sizeof(BMPWIN3XINFOHDR));
228
229 coreHdr.cbSize = sizeof(BMPWIN3XINFOHDR);
230 coreHdr.uWidth = uWidth ;
231 coreHdr.uHeight = uHeight;
232 coreHdr.cPlanes = 1;
233 coreHdr.cBits = uBPP;
234 coreHdr.uXPelsPerMeter = 5000;
235 coreHdr.uYPelsPerMeter = 5000;
236
237 static uint64_t s_iCount = 0;
238
239 char szPath[RTPATH_MAX];
240 if (!pszPath)
241 RTPathTemp(szPath, sizeof(szPath));
242
243 char szFileName[RTPATH_MAX];
244 if (RTStrPrintf2(szFileName, sizeof(szFileName), "%s/RecDump-%04RU64-%s-w%RU16h%RU16.bmp",
245 pszPath ? pszPath : szPath, s_iCount, pszPrefx ? pszPrefx : "Frame", uWidth, uHeight) <= 0)
246 {
247 return VERR_BUFFER_OVERFLOW;
248 }
249
250 s_iCount++;
251
252 RTFILE fh;
253 int vrc = RTFileOpen(&fh, szFileName,
254 RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
255 if (RT_SUCCESS(vrc))
256 {
257 RTFileWrite(fh, &fileHdr, sizeof(fileHdr), NULL);
258 RTFileWrite(fh, &coreHdr, sizeof(coreHdr), NULL);
259
260 /* Bitmaps (DIBs) are stored upside-down (thanks, OS/2), so work from the bottom up. */
261 uint32_t offSrc = (uHeight * uWidth * uBytesPerPixel) - uWidth * uBytesPerPixel;
262
263 /* Do the copy. */
264 for (unsigned int i = 0; i < uHeight; i++)
265 {
266 RTFileWrite(fh, pu8RGBBuf + offSrc, uWidth * uBytesPerPixel, NULL);
267 offSrc -= uWidth * uBytesPerPixel;
268 }
269
270 RTFileClose(fh);
271 }
272
273 return vrc;
274}
275
276/**
277 * Dumps a video recording frame to a bitmap (BMP) file.
278 *
279 * @returns VBox status code.
280 * @param pFrame Video frame to dump.
281 */
282int RecordingUtilsDbgDumpFrame(const PRECORDINGFRAME pFrame)
283{
284 AssertReturn(pFrame->enmType == RECORDINGFRAME_TYPE_VIDEO, VERR_INVALID_PARAMETER);
285 return RecordingUtilsDbgDumpFrameEx(pFrame->Video.pu8RGBBuf, pFrame->Video.cbRGBBuf,
286 NULL /* Use temp directory */, NULL /* pszPrefix */,
287 pFrame->Video.uWidth, pFrame->Video.uHeight, pFrame->Video.uBPP);
288}
289#endif /* DEBUG */
290
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