VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxHeadless/VideoCapture/FFmpegFB.h@ 43629

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

Frontends/VideoCapture:Implementation of encoding and support logic in a seperate module. VBoxheadless using new encoding module to record video. Under development - Formatting and thorough testing needs to be done.

Code will not be enabled till VBOX_WITH_VPX is enabled in LocalConfig.kmk.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.8 KB
Line 
1/** @file
2 *
3 * VBox Remote Desktop Protocol.
4 * FFmpeg framebuffer interface.
5 */
6
7/*
8 * Copyright (C) 2006-2012 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19#ifndef _H_FFMPEGFB
20#define _H_FFMPEGFB
21
22#include <VBox/com/VirtualBox.h>
23#include <iprt/uuid.h>
24
25#include <VBox/com/com.h>
26#include <VBox/com/string.h>
27
28#include <iprt/initterm.h>
29#include <iprt/critsect.h>
30
31#ifdef VBOX_WITH_VPX
32#include "EbmlWriter.h"
33#include "EncodeAndWrite.h"
34#include <stdarg.h>
35#include <string.h>
36#define VPX_CODEC_DISABLE_COMPAT 1
37#include <vp8cx.h>
38#include <vpx_image.h>
39#include <vpx_mem.h>
40#define interface (vpx_codec_vp8_cx())
41#else
42# ifdef DEBUG
43# define VBOX_DEBUG_FF DEBUG
44# include <avcodec.h>
45# include <avformat.h>
46# undef DEBUG
47# define DEBUG VBOX_DEBUG_FF
48# else /* DEBUG not defined */
49# include <avcodec.h>
50# include <avformat.h>
51# endif /* DEBUG not defined */
52#endif
53
54#ifdef VBOX_WITH_VPX
55PVIDEORECCONTEXT pVideoRecContext;
56#endif
57
58class FFmpegFB : VBOX_SCRIPTABLE_IMPL(IFramebuffer)
59{
60public:
61 FFmpegFB(ULONG width, ULONG height, ULONG bitrate, com::Bstr filename);
62 virtual ~FFmpegFB();
63
64#ifndef VBOX_WITH_XPCOM
65 STDMETHOD_(ULONG, AddRef)()
66 {
67 return ::InterlockedIncrement (&refcnt);
68 }
69 STDMETHOD_(ULONG, Release)()
70 {
71 long cnt = ::InterlockedDecrement (&refcnt);
72 if (cnt == 0)
73 delete this;
74 return cnt;
75 }
76#endif
77 VBOX_SCRIPTABLE_DISPATCH_IMPL(IFramebuffer)
78
79 NS_DECL_ISUPPORTS
80
81 // public methods only for internal purposes
82 HRESULT init ();
83
84 STDMETHOD(COMGETTER(Width))(ULONG *width);
85 STDMETHOD(COMGETTER(Height))(ULONG *height);
86 STDMETHOD(Lock)();
87 STDMETHOD(Unlock)();
88 STDMETHOD(COMGETTER(Address))(BYTE **address);
89 STDMETHOD(COMGETTER(BitsPerPixel))(ULONG *bitsPerPixel);
90 STDMETHOD(COMGETTER(BytesPerLine))(ULONG *bytesPerLine);
91 STDMETHOD(COMGETTER(PixelFormat)) (ULONG *pixelFormat);
92 STDMETHOD(COMGETTER(UsesGuestVRAM)) (BOOL *usesGuestVRAM);
93 STDMETHOD(COMGETTER(HeightReduction)) (ULONG *heightReduction);
94 STDMETHOD(COMGETTER(Overlay)) (IFramebufferOverlay **aOverlay);
95 STDMETHOD(COMGETTER(WinId)) (LONG64 *winId);
96
97 STDMETHOD(NotifyUpdate)(ULONG x, ULONG y, ULONG w, ULONG h);
98 STDMETHOD(RequestResize)(ULONG aScreenId, ULONG pixelFormat, BYTE *vram,
99 ULONG bitsPerPixel, ULONG bytesPerLine,
100 ULONG w, ULONG h, BOOL *finished);
101 STDMETHOD(VideoModeSupported)(ULONG width, ULONG height, ULONG bpp, BOOL *supported);
102 STDMETHOD(GetVisibleRegion)(BYTE *rectangles, ULONG count, ULONG *countCopied);
103 STDMETHOD(SetVisibleRegion)(BYTE *rectangles, ULONG count);
104
105 STDMETHOD(ProcessVHWACommand)(BYTE *pCommand);
106public:
107private:
108#ifdef VBOX_WITH_VPX
109 EbmlGlobal ebml;
110 vpx_codec_ctx_t mVpxCodec;
111 vpx_codec_enc_cfg_t mVpxConfig;
112 FILE * mOutputFile;
113 unsigned long mDuration;
114 uint32_t mFrameCount;
115
116#else
117 /** Pointer to ffmpeg's format information context */
118 AVFormatContext *mpFormatContext;
119 /** ffmpeg context containing information about the stream */
120 AVStream *mpStream;
121 /** Information for ffmpeg describing the current frame */
122 AVFrame *mFrame;
123
124 HRESULT setup_library();
125 HRESULT setup_output_format();
126 HRESULT list_formats();
127#endif
128 /** true if url_fopen actually succeeded */
129 bool mfUrlOpen;
130 /** Guest framebuffer width */
131 ULONG mGuestWidth;
132 /** Guest framebuffer height */
133 ULONG mGuestHeight;
134 /** Bit rate used for encoding */
135 ULONG mBitRate;
136 /** Guest framebuffer pixel format */
137 ULONG mPixelFormat;
138 /** Guest framebuffer color depth */
139 ULONG mBitsPerPixel;
140 /** Name of the file we will write to */
141 com::Bstr mFileName;
142 /** Guest framebuffer line length */
143 ULONG mBytesPerLine;
144 /** MPEG frame framebuffer width */
145 ULONG mFrameWidth;
146 /** MPEG frame framebuffer height */
147 ULONG mFrameHeight;
148 /** The size of one YUV frame */
149 ULONG mYUVFrameSize;
150 /** If we can't use the video RAM directly, we allocate our own
151 * buffer */
152 uint8_t *mRGBBuffer;
153 /** The address of the buffer - can be either mRGBBuffer or the
154 * guests VRAM (HC address) if we can handle that directly */
155 uint8_t *mBufferAddress;
156 /** An intermediary RGB buffer with the same dimensions */
157 uint8_t *mTempRGBBuffer;
158 /** Frame buffer translated into YUV420 for the mpeg codec */
159 uint8_t *mYUVBuffer;
160 /** Temporary buffer into which the codec writes frames to be
161 * written into the file */
162 uint8_t *mOutBuf;
163 RTCRITSECT mCritSect;
164 /** File where we store the mpeg stream */
165 RTFILE mFile;
166 /** time at which the last "real" frame was created */
167 int64_t mLastTime;
168 /** ffmpeg pixel format of guest framebuffer */
169 int mFFMPEGPixelFormat;
170 /** Since we are building without exception support, we use this
171 to signal allocation failure in the constructor */
172 bool mOutOfMemory;
173 /** A hack: ffmpeg mpeg2 only writes a frame if something has
174 changed. So we flip the low luminance bit of the first
175 pixel every frame. */
176 bool mToggle;
177
178
179 HRESULT open_codec();
180 HRESULT open_output_file();
181 void copy_to_intermediate_buffer(ULONG x, ULONG y, ULONG w, ULONG h);
182 HRESULT do_rgb_to_yuv_conversion();
183 HRESULT do_encoding_and_write();
184 HRESULT write_png();
185#ifndef VBOX_WITH_XPCOM
186 long refcnt;
187#endif
188};
189
190/**
191 * Iterator class for running through an BGRA32 image buffer and converting
192 * it to RGB.
193 */
194class FFmpegBGRA32Iter
195{
196private:
197 enum { PIX_SIZE = 4 };
198public:
199 FFmpegBGRA32Iter(unsigned aWidth, unsigned aHeight, uint8_t *aBuffer)
200 {
201 mPos = 0;
202 mSize = aWidth * aHeight * PIX_SIZE;
203 mBuffer = aBuffer;
204 }
205 /**
206 * Convert the next pixel to RGB.
207 * @returns true on success, false if we have reached the end of the buffer
208 * @param aRed where to store the red value
209 * @param aGreen where to store the green value
210 * @param aBlue where to store the blue value
211 */
212 bool getRGB(unsigned *aRed, unsigned *aGreen, unsigned *aBlue)
213 {
214 bool rc = false;
215 if (mPos + PIX_SIZE <= mSize)
216 {
217 *aRed = mBuffer[mPos + 2];
218 *aGreen = mBuffer[mPos + 1];
219 *aBlue = mBuffer[mPos];
220 mPos += PIX_SIZE;
221 rc = true;
222 }
223 return rc;
224 }
225
226 /**
227 * Skip forward by a certain number of pixels
228 * @param aPixels how many pixels to skip
229 */
230 void skip(unsigned aPixels)
231 {
232 mPos += PIX_SIZE * aPixels;
233 }
234private:
235 /** Size of the picture buffer */
236 unsigned mSize;
237 /** Current position in the picture buffer */
238 unsigned mPos;
239 /** Address of the picture buffer */
240 uint8_t *mBuffer;
241};
242
243/**
244 * Iterator class for running through an BGR24 image buffer and converting
245 * it to RGB.
246 */
247class FFmpegBGR24Iter
248{
249private:
250 enum { PIX_SIZE = 3 };
251public:
252 FFmpegBGR24Iter(unsigned aWidth, unsigned aHeight, uint8_t *aBuffer)
253 {
254 mPos = 0;
255 mSize = aWidth * aHeight * PIX_SIZE;
256 mBuffer = aBuffer;
257 }
258 /**
259 * Convert the next pixel to RGB.
260 * @returns true on success, false if we have reached the end of the buffer
261 * @param aRed where to store the red value
262 * @param aGreen where to store the green value
263 * @param aBlue where to store the blue value
264 */
265 bool getRGB(unsigned *aRed, unsigned *aGreen, unsigned *aBlue)
266 {
267 bool rc = false;
268 if (mPos + PIX_SIZE <= mSize)
269 {
270 *aRed = mBuffer[mPos + 2];
271 *aGreen = mBuffer[mPos + 1];
272 *aBlue = mBuffer[mPos];
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 *mBuffer;
294};
295
296/**
297 * Iterator class for running through an BGR565 image buffer and converting
298 * it to RGB.
299 */
300class FFmpegBGR565Iter
301{
302private:
303 enum { PIX_SIZE = 2 };
304public:
305 FFmpegBGR565Iter(unsigned aWidth, unsigned aHeight, uint8_t *aBuffer)
306 {
307 mPos = 0;
308 mSize = aWidth * aHeight * PIX_SIZE;
309 mBuffer = aBuffer;
310 }
311 /**
312 * Convert the next pixel to RGB.
313 * @returns true on success, false if we have reached the end of the buffer
314 * @param aRed where to store the red value
315 * @param aGreen where to store the green value
316 * @param aBlue where to store the blue value
317 */
318 bool getRGB(unsigned *aRed, unsigned *aGreen, unsigned *aBlue)
319 {
320 bool rc = false;
321 if (mPos + PIX_SIZE <= mSize)
322 {
323 unsigned uFull = (((unsigned) mBuffer[mPos + 1]) << 8)
324 | ((unsigned) mBuffer[mPos]);
325 *aRed = (uFull >> 8) & ~7;
326 *aGreen = (uFull >> 3) & ~3 & 0xff;
327 *aBlue = (uFull << 3) & ~7 & 0xff;
328 mPos += PIX_SIZE;
329 rc = true;
330 }
331 return rc;
332 }
333
334 /**
335 * Skip forward by a certain number of pixels
336 * @param aPixels how many pixels to skip
337 */
338 void skip(unsigned aPixels)
339 {
340 mPos += PIX_SIZE * aPixels;
341 }
342private:
343 /** Size of the picture buffer */
344 unsigned mSize;
345 /** Current position in the picture buffer */
346 unsigned mPos;
347 /** Address of the picture buffer */
348 uint8_t *mBuffer;
349};
350
351
352/**
353 * Convert an image to YUV420p format
354 * @returns true on success, false on failure
355 * @param aWidth width of image
356 * @param aHeight height of image
357 * @param aDestBuf an allocated memory buffer large enough to hold the
358 * destination image (i.e. width * height * 12bits)
359 * @param aSrcBuf the source image as an array of bytes
360 */
361template <class T>
362inline bool FFmpegWriteYUV420p(unsigned aWidth, unsigned aHeight, uint8_t *aDestBuf,
363 uint8_t *aSrcBuf)
364{
365 AssertReturn(0 == (aWidth & 1), false);
366 AssertReturn(0 == (aHeight & 1), false);
367 bool rc = true;
368 T iter1(aWidth, aHeight, aSrcBuf);
369 T iter2 = iter1;
370 iter2.skip(aWidth);
371 unsigned cPixels = aWidth * aHeight;
372 unsigned offY = 0;
373 unsigned offU = cPixels;
374 unsigned offV = cPixels + cPixels / 4;
375 for (unsigned i = 0; (i < aHeight / 2) && rc; ++i)
376 {
377 for (unsigned j = 0; (j < aWidth / 2) && rc; ++j)
378 {
379 unsigned red, green, blue, u, v;
380 rc = iter1.getRGB(&red, &green, &blue);
381 if (rc)
382 {
383 aDestBuf[offY] = ((66 * red + 129 * green + 25 * blue + 128) >> 8) + 16;
384 u = (((-38 * red - 74 * green + 112 * blue + 128) >> 8) + 128) / 4;
385 v = (((112 * red - 94 * green - 18 * blue + 128) >> 8) + 128) / 4;
386 rc = iter1.getRGB(&red, &green, &blue);
387 }
388 if (rc)
389 {
390 aDestBuf[offY + 1] = ((66 * red + 129 * green + 25 * blue + 128) >> 8) + 16;
391 u += (((-38 * red - 74 * green + 112 * blue + 128) >> 8) + 128) / 4;
392 v += (((112 * red - 94 * green - 18 * blue + 128) >> 8) + 128) / 4;
393 rc = iter2.getRGB(&red, &green, &blue);
394 }
395 if (rc)
396 {
397 aDestBuf[offY + aWidth] = ((66 * red + 129 * green + 25 * blue + 128) >> 8) + 16;
398 u += (((-38 * red - 74 * green + 112 * blue + 128) >> 8) + 128) / 4;
399 v += (((112 * red - 94 * green - 18 * blue + 128) >> 8) + 128) / 4;
400 rc = iter2.getRGB(&red, &green, &blue);
401 }
402 if (rc)
403 {
404 aDestBuf[offY + aWidth + 1] = ((66 * red + 129 * green + 25 * blue + 128) >> 8) + 16;
405 u += (((-38 * red - 74 * green + 112 * blue + 128) >> 8) + 128) / 4;
406 v += (((112 * red - 94 * green - 18 * blue + 128) >> 8) + 128) / 4;
407 aDestBuf[offU] = u;
408 aDestBuf[offV] = v;
409 offY += 2;
410 ++offU;
411 ++offV;
412 }
413 }
414 if (rc)
415 {
416 iter1.skip(aWidth);
417 iter2.skip(aWidth);
418 offY += aWidth;
419 }
420 }
421 return rc;
422}
423
424
425/**
426 * Convert an image to RGB24 format
427 * @returns true on success, false on failure
428 * @param aWidth width of image
429 * @param aHeight height of image
430 * @param aDestBuf an allocated memory buffer large enough to hold the
431 * destination image (i.e. width * height * 12bits)
432 * @param aSrcBuf the source image as an array of bytes
433 */
434template <class T>
435inline bool FFmpegWriteRGB24(unsigned aWidth, unsigned aHeight, uint8_t *aDestBuf,
436 uint8_t *aSrcBuf)
437{
438 enum { PIX_SIZE = 3 };
439 bool rc = true;
440 AssertReturn(0 == (aWidth & 1), false);
441 AssertReturn(0 == (aHeight & 1), false);
442 T iter(aWidth, aHeight, aSrcBuf);
443 unsigned cPixels = aWidth * aHeight;
444 for (unsigned i = 0; (i < cPixels) && rc; ++i)
445 {
446 unsigned red, green, blue;
447 rc = iter.getRGB(&red, &green, &blue);
448 if (rc)
449 {
450 aDestBuf[i * PIX_SIZE] = red;
451 aDestBuf[i * PIX_SIZE + 1] = green;
452 aDestBuf[i * PIX_SIZE + 2] = blue;
453 }
454 }
455 return rc;
456}
457
458#endif /* !_H_FFMPEGFB */
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