VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxHeadless/VideoCapture/FFmpegFB.cpp@ 42341

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

VBoxHeadless: bogus assertion and a few style fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 41.5 KB
Line 
1/** @file
2 *
3 * Framebuffer implementation that interfaces with FFmpeg
4 * to create a video of the guest.
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#define LOG_GROUP LOG_GROUP_GUI
20
21#include "FFmpegFB.h"
22
23#include <iprt/file.h>
24#include <iprt/param.h>
25#include <iprt/assert.h>
26#include <VBox/log.h>
27#include <png.h>
28#include <iprt/stream.h>
29
30#define VBOX_SHOW_AVAILABLE_FORMATS
31
32// external constructor for dynamic loading
33/////////////////////////////////////////////////////////////////////////////
34
35/**
36 * Callback function to register an ffmpeg framebuffer.
37 *
38 * @returns COM status code.
39 * @param width Framebuffer width.
40 * @param height Framebuffer height.
41 * @param bitrate Bitrate of mpeg file to be created.
42 * @param filename Name of mpeg file to be created
43 * @retval retVal The new framebuffer
44 */
45extern "C" DECLEXPORT(HRESULT) VBoxRegisterFFmpegFB(ULONG width,
46 ULONG height, ULONG bitrate,
47 com::Bstr filename,
48 IFramebuffer **retVal)
49{
50 Log2(("VBoxRegisterFFmpegFB: called\n"));
51 FFmpegFB *pFramebuffer = new FFmpegFB(width, height, bitrate, filename);
52 int rc = pFramebuffer->init();
53 AssertMsg(rc == S_OK,
54 ("failed to initialise the FFmpeg framebuffer, rc = %d\n",
55 rc));
56 if (rc == S_OK)
57 {
58 *retVal = pFramebuffer;
59 return S_OK;
60 }
61 delete pFramebuffer;
62 return rc;
63}
64
65
66
67
68
69// constructor / destructor
70/////////////////////////////////////////////////////////////////////////////
71
72/**
73 * Perform parts of initialisation which are guaranteed not to fail
74 * unless we run out of memory. In this case, we just set the guest
75 * buffer to 0 so that RequestResize() does not free it the first time
76 * it is called.
77 */
78#ifdef VBOX_WITH_VPX
79FFmpegFB::FFmpegFB(ULONG width, ULONG height, ULONG bitrate,
80 com::Bstr filename) :
81 mfUrlOpen(false),
82 mBitRate(bitrate),
83 mPixelFormat(FramebufferPixelFormat_Opaque),
84 mBitsPerPixel(0),
85 mFileName(filename),
86 mBytesPerLine(0),
87 mFrameWidth(width), mFrameHeight(height),
88 mYUVFrameSize(width * height * 3 / 2),
89 mRGBBuffer(0),
90 mOutOfMemory(false), mToggle(false)
91#else
92FFmpegFB::FFmpegFB(ULONG width, ULONG height, ULONG bitrate,
93 com::Bstr filename) :
94 mpFormatContext(0), mpStream(0),
95 mfUrlOpen(false),
96 mBitRate(bitrate),
97 mPixelFormat(FramebufferPixelFormat_Opaque),
98 mBitsPerPixel(0),
99 mFileName(filename),
100 mBytesPerLine(0),
101 mFrameWidth(width), mFrameHeight(height),
102 mYUVFrameSize(width * height * 3 / 2),mRGBBuffer(0),
103 mOutOfMemory(false), mToggle(false)
104#endif
105{
106 ULONG cPixels = width * height;
107
108#ifdef VBOX_WITH_VPX
109 Assert(width % 2 == 0 && height % 2 == 0);
110 /* For temporary RGB frame we allocate enough memory to deal with
111 RGB16 to RGB32 */
112 mTempRGBBuffer = reinterpret_cast<uint8_t *>(RTMemAlloc(cPixels * 4));
113 if (!mTempRGBBuffer)
114 goto nomem_temp_rgb_buffer;
115 mYUVBuffer = reinterpret_cast<uint8_t *>(RTMemAlloc(mYUVFrameSize));
116 if (!mYUVBuffer)
117 goto nomem_yuv_buffer;
118 return;
119
120 /* C-based memory allocation and how to deal with it in C++ :) */
121nomem_yuv_buffer:
122 Log(("Failed to allocate memory for mYUVBuffer\n"));
123 RTMemFree(mYUVBuffer);
124nomem_temp_rgb_buffer:
125 Log(("Failed to allocate memory for mTempRGBBuffer\n"));
126 RTMemFree(mTempRGBBuffer);
127 mOutOfMemory = true;
128#else
129 LogFlow(("Creating FFmpegFB object %p, width=%lu, height=%lu\n",
130 this, (unsigned long) width, (unsigned long) height));
131 Assert(width % 2 == 0 && height % 2 == 0);
132 /* For temporary RGB frame we allocate enough memory to deal with
133 RGB16 to RGB32 */
134 mTempRGBBuffer = reinterpret_cast<uint8_t *>(av_malloc(cPixels * 4));
135 if (!mTempRGBBuffer)
136 goto nomem_temp_rgb_buffer;
137 mYUVBuffer = reinterpret_cast<uint8_t *>(av_malloc(mYUVFrameSize));
138 if (!mYUVBuffer)
139 goto nomem_yuv_buffer;
140 mFrame = avcodec_alloc_frame();
141 if (!mFrame)
142 goto nomem_mframe;
143 mOutBuf = reinterpret_cast<uint8_t *>(av_malloc(mYUVFrameSize * 2));
144 if (!mOutBuf)
145 goto nomem_moutbuf;
146
147 return;
148
149 /* C-based memory allocation and how to deal with it in C++ :) */
150nomem_moutbuf:
151 Log(("Failed to allocate memory for mOutBuf\n"));
152 av_free(mFrame);
153nomem_mframe:
154 Log(("Failed to allocate memory for mFrame\n"));
155 av_free(mYUVBuffer);
156nomem_yuv_buffer:
157 Log(("Failed to allocate memory for mYUVBuffer\n"));
158 av_free(mTempRGBBuffer);
159nomem_temp_rgb_buffer:
160 Log(("Failed to allocate memory for mTempRGBBuffer\n"));
161 mOutOfMemory = true;
162#endif
163}
164
165
166/**
167 * Write the last frame to disk and free allocated memory
168 */
169FFmpegFB::~FFmpegFB()
170{
171 LogFlow(("Destroying FFmpegFB object %p\n", this));
172#ifdef VBOX_WITH_VPX
173 /* Dummy update to make sure we get all the frame (timing). */
174 NotifyUpdate(0, 0, 0, 0);
175 /* Write the last pending frame before exiting */
176 int rc = do_rgb_to_yuv_conversion();
177 if (rc == S_OK)
178 do_encoding_and_write();
179# if 1
180 /* Add another 10 seconds. */
181 for (int i = 10*25; i > 0; i--)
182 do_encoding_and_write();
183# endif
184 Ebml_WriteWebMFileFooter(&ebml, 0);
185 if(ebml.stream)
186 fclose(ebml.stream);
187 vpx_codec_destroy(&mVpxCodec);
188 RTCritSectDelete(&mCritSect);
189
190 /* We have already freed the stream above */
191 if (mTempRGBBuffer)
192 RTMemFree(mTempRGBBuffer);
193 if (mYUVBuffer)
194 RTMemFree(mYUVBuffer);
195 if (mRGBBuffer)
196 RTMemFree(mRGBBuffer);
197#else
198 if (mpFormatContext != 0)
199 {
200 if (mfUrlOpen)
201 {
202 /* Dummy update to make sure we get all the frame (timing). */
203 NotifyUpdate(0, 0, 0, 0);
204 /* Write the last pending frame before exiting */
205 int rc = do_rgb_to_yuv_conversion();
206 if (rc == S_OK)
207 do_encoding_and_write();
208# if 1
209 /* Add another 10 seconds. */
210 for (int i = 10*25; i > 0; i--)
211 do_encoding_and_write();
212# endif
213 /* write a png file of the last frame */
214 write_png();
215 avcodec_close(mpStream->codec);
216 av_write_trailer(mpFormatContext);
217 /* free the streams */
218 for(unsigned i = 0; i < (unsigned)mpFormatContext->nb_streams; i++) {
219 av_freep(&mpFormatContext->streams[i]->codec);
220 av_freep(&mpFormatContext->streams[i]);
221 }
222/* Changed sometime between 50.5.0 and 52.7.0 */
223# if LIBAVFORMAT_VERSION_INT >= (52 << 16)
224 url_fclose(mpFormatContext->pb);
225# else /* older version */
226 url_fclose(&mpFormatContext->pb);
227# endif /* older version */
228 }
229 av_free(mpFormatContext);
230 }
231 RTCritSectDelete(&mCritSect);
232 /* We have already freed the stream above */
233 mpStream = 0;
234 if (mTempRGBBuffer)
235 av_free(mTempRGBBuffer);
236 if (mYUVBuffer)
237 av_free(mYUVBuffer);
238 if (mFrame)
239 av_free(mFrame);
240 if (mOutBuf)
241 av_free(mOutBuf);
242 if (mRGBBuffer)
243 RTMemFree(mRGBBuffer);
244#endif
245}
246
247// public methods only for internal purposes
248/////////////////////////////////////////////////////////////////////////////
249
250/**
251 * Perform any parts of the initialisation which could potentially fail
252 * for reasons other than "out of memory".
253 *
254 * @returns COM status code
255 * @param width width to be used for MPEG frame framebuffer and initially
256 * for the guest frame buffer - must be a multiple of two
257 * @param height height to be used for MPEG frame framebuffer and
258 * initially for the guest framebuffer - must be a multiple
259 * of two
260 * @param depth depth to be used initially for the guest framebuffer
261 */
262HRESULT FFmpegFB::init()
263{
264 LogFlow(("Initialising FFmpegFB object %p\n", this));
265 if (mOutOfMemory == true)
266 return E_OUTOFMEMORY;
267 int rc;
268 int rcOpenFile;
269 int rcOpenCodec;
270
271#ifdef VBOX_WITH_VPX
272 mFrameCount = 0;
273 memset(&ebml, 0, sizeof(struct EbmlGlobal));
274 ebml.last_pts_ms = -1;
275 rc = RTCritSectInit(&mCritSect);
276 AssertReturn(rc == VINF_SUCCESS, E_UNEXPECTED);
277 rcOpenFile = open_output_file();
278 AssertReturn(rcOpenFile == S_OK, rcOpenFile);
279 rcOpenCodec = open_codec();
280 AssertReturn(rcOpenCodec == S_OK, rcOpenCodec);
281#else
282 rc = RTCritSectInit(&mCritSect);
283 AssertReturn(rc == VINF_SUCCESS, E_UNEXPECTED);
284 int rcSetupLibrary = setup_library();
285 AssertReturn(rcSetupLibrary == S_OK, rcSetupLibrary);
286 int rcSetupFormat = setup_output_format();
287 AssertReturn(rcSetupFormat == S_OK, rcSetupFormat);
288 rcOpenCodec = open_codec();
289 AssertReturn(rcOpenCodec == S_OK, rcOpenCodec);
290 rcOpenFile = open_output_file();
291 AssertReturn(rcOpenFile == S_OK, rcOpenFile);
292
293 /* Fill in the picture data for the AVFrame - not particularly
294 elegant, but that is the API. */
295 avpicture_fill((AVPicture *) mFrame, mYUVBuffer, PIX_FMT_YUV420P,
296 mFrameWidth, mFrameHeight);
297#endif
298
299 /* Set the initial framebuffer size to the mpeg frame dimensions */
300 BOOL finished;
301 RequestResize(0, FramebufferPixelFormat_Opaque, NULL, 0, 0,
302 mFrameWidth, mFrameHeight, &finished);
303 /* Start counting time */
304 mLastTime = RTTimeMilliTS();
305 mLastTime = mLastTime - mLastTime % 40;
306 return rc;
307}
308
309// IFramebuffer properties
310/////////////////////////////////////////////////////////////////////////////
311
312/**
313 * Return the address of the frame buffer for the virtual VGA device to
314 * write to. If COMGETTER(UsesGuestVRAM) returns FLASE (or if this address
315 * is not the same as the guests VRAM buffer), the device will perform
316 * translation.
317 *
318 * @returns COM status code
319 * @retval address The address of the buffer
320 */
321STDMETHODIMP FFmpegFB::COMGETTER(Address) (BYTE **address)
322{
323 if (!address)
324 return E_POINTER;
325 LogFlow(("FFmpeg::COMGETTER(Address): returning address %p\n", mBufferAddress));
326 *address = mBufferAddress;
327 return S_OK;
328}
329
330/**
331 * Return the width of our frame buffer.
332 *
333 * @returns COM status code
334 * @retval width The width of the frame buffer
335 */
336STDMETHODIMP FFmpegFB::COMGETTER(Width) (ULONG *width)
337{
338 if (!width)
339 return E_POINTER;
340 LogFlow(("FFmpeg::COMGETTER(Width): returning width %lu\n",
341 (unsigned long) mGuestWidth));
342 *width = mGuestWidth;
343 return S_OK;
344}
345
346/**
347 * Return the height of our frame buffer.
348 *
349 * @returns COM status code
350 * @retval height The height of the frame buffer
351 */
352STDMETHODIMP FFmpegFB::COMGETTER(Height) (ULONG *height)
353{
354 if (!height)
355 return E_POINTER;
356 LogFlow(("FFmpeg::COMGETTER(Height): returning height %lu\n",
357 (unsigned long) mGuestHeight));
358 *height = mGuestHeight;
359 return S_OK;
360}
361
362/**
363 * Return the colour depth of our frame buffer. Note that we actually
364 * store the pixel format, not the colour depth internally, since
365 * when display sets FramebufferPixelFormat_Opaque, it
366 * wants to retrieve FramebufferPixelFormat_Opaque and
367 * nothing else.
368 *
369 * @returns COM status code
370 * @retval bitsPerPixel The colour depth of the frame buffer
371 */
372STDMETHODIMP FFmpegFB::COMGETTER(BitsPerPixel) (ULONG *bitsPerPixel)
373{
374 if (!bitsPerPixel)
375 return E_POINTER;
376 *bitsPerPixel = mBitsPerPixel;
377 LogFlow(("FFmpeg::COMGETTER(BitsPerPixel): returning depth %lu\n",
378 (unsigned long) *bitsPerPixel));
379 return S_OK;
380}
381
382/**
383 * Return the number of bytes per line in our frame buffer.
384 *
385 * @returns COM status code
386 * @retval bytesPerLine The number of bytes per line
387 */
388STDMETHODIMP FFmpegFB::COMGETTER(BytesPerLine) (ULONG *bytesPerLine)
389{
390 if (!bytesPerLine)
391 return E_POINTER;
392 LogFlow(("FFmpeg::COMGETTER(BytesPerLine): returning line size %lu\n",
393 (unsigned long) mBytesPerLine));
394 *bytesPerLine = mBytesPerLine;
395 return S_OK;
396}
397
398/**
399 * Return the pixel layout of our frame buffer.
400 *
401 * @returns COM status code
402 * @retval pixelFormat The pixel layout
403 */
404STDMETHODIMP FFmpegFB::COMGETTER(PixelFormat) (ULONG *pixelFormat)
405{
406 if (!pixelFormat)
407 return E_POINTER;
408 LogFlow(("FFmpeg::COMGETTER(PixelFormat): returning pixel format: %lu\n",
409 (unsigned long) mPixelFormat));
410 *pixelFormat = mPixelFormat;
411 return S_OK;
412}
413
414/**
415 * Return whether we use the guest VRAM directly.
416 *
417 * @returns COM status code
418 * @retval pixelFormat The pixel layout
419 */
420STDMETHODIMP FFmpegFB::COMGETTER(UsesGuestVRAM) (BOOL *usesGuestVRAM)
421{
422 if (!usesGuestVRAM)
423 return E_POINTER;
424 LogFlow(("FFmpeg::COMGETTER(UsesGuestVRAM): uses guest VRAM? %d\n",
425 mRGBBuffer == NULL));
426 *usesGuestVRAM = (mRGBBuffer == NULL);
427 return S_OK;
428}
429
430/**
431 * Return the number of lines of our frame buffer which can not be used
432 * (e.g. for status lines etc?).
433 *
434 * @returns COM status code
435 * @retval heightReduction The number of unused lines
436 */
437STDMETHODIMP FFmpegFB::COMGETTER(HeightReduction) (ULONG *heightReduction)
438{
439 if (!heightReduction)
440 return E_POINTER;
441 /* no reduction */
442 *heightReduction = 0;
443 LogFlow(("FFmpeg::COMGETTER(HeightReduction): returning 0\n"));
444 return S_OK;
445}
446
447/**
448 * Return a pointer to the alpha-blended overlay used to render status icons
449 * etc above the framebuffer.
450 *
451 * @returns COM status code
452 * @retval aOverlay The overlay framebuffer
453 */
454STDMETHODIMP FFmpegFB::COMGETTER(Overlay) (IFramebufferOverlay **aOverlay)
455{
456 if (!aOverlay)
457 return E_POINTER;
458 /* not yet implemented */
459 *aOverlay = 0;
460 LogFlow(("FFmpeg::COMGETTER(Overlay): returning 0\n"));
461 return S_OK;
462}
463
464/**
465 * Return id of associated window
466 *
467 * @returns COM status code
468 * @retval winId Associated window id
469 */
470STDMETHODIMP FFmpegFB::COMGETTER(WinId) (LONG64 *winId)
471{
472 if (!winId)
473 return E_POINTER;
474 *winId = 0;
475 return S_OK;
476}
477
478// IFramebuffer methods
479/////////////////////////////////////////////////////////////////////////////
480
481STDMETHODIMP FFmpegFB::Lock()
482{
483 LogFlow(("FFmpeg::Lock: called\n"));
484 int rc = RTCritSectEnter(&mCritSect);
485 AssertRC(rc);
486 if (rc == VINF_SUCCESS)
487 return S_OK;
488 return E_UNEXPECTED;
489}
490
491STDMETHODIMP FFmpegFB::Unlock()
492{
493 LogFlow(("FFmpeg::Unlock: called\n"));
494 RTCritSectLeave(&mCritSect);
495 return S_OK;
496}
497
498
499/**
500 * This method is used to notify us that an area of the guest framebuffer
501 * has been updated.
502 *
503 * @returns COM status code
504 * @param x X co-ordinate of the upper left-hand corner of the
505 * area which has been updated
506 * @param y Y co-ordinate of the upper left-hand corner of the
507 * area which has been updated
508 * @param w width of the area which has been updated
509 * @param h height of the area which has been updated
510 */
511STDMETHODIMP FFmpegFB::NotifyUpdate(ULONG x, ULONG y, ULONG w, ULONG h)
512{
513 int rc;
514 int64_t iCurrentTime = RTTimeMilliTS();
515
516 LogFlow(("FFmpeg::NotifyUpdate called: x=%lu, y=%lu, w=%lu, h=%lu\n",
517 (unsigned long) x, (unsigned long) y, (unsigned long) w,
518 (unsigned long) h));
519
520 /* We always leave at least one frame update pending, which we
521 process when the time until the next frame has elapsed. */
522 if (iCurrentTime - mLastTime >= 40)
523 {
524 rc = do_rgb_to_yuv_conversion();
525 if (rc != S_OK)
526 {
527 copy_to_intermediate_buffer(x, y, w, h);
528 return rc;
529 }
530 rc = do_encoding_and_write();
531 if (rc != S_OK)
532 {
533 copy_to_intermediate_buffer(x, y, w, h);
534 return rc;
535 }
536 mLastTime = mLastTime + 40;
537 /* Write frames for the time in-between. Not a good way
538 to handle this. */
539 while (iCurrentTime - mLastTime >= 40)
540 {
541/* rc = do_rgb_to_yuv_conversion();
542 if (rc != S_OK)
543 {
544 copy_to_intermediate_buffer(x, y, w, h);
545 return rc;
546 }
547*/ rc = do_encoding_and_write();
548 if (rc != S_OK)
549 {
550 copy_to_intermediate_buffer(x, y, w, h);
551 return rc;
552 }
553 mLastTime = mLastTime + 40;
554 }
555 }
556 /* Finally we copy the updated data to the intermediate buffer,
557 ready for the next update. */
558 copy_to_intermediate_buffer(x, y, w, h);
559 return S_OK;
560}
561
562
563/**
564 * Requests a resize of our "screen".
565 *
566 * @returns COM status code
567 * @param pixelFormat Layout of the guest video RAM (i.e. 16, 24,
568 * 32 bpp)
569 * @param vram host context pointer to the guest video RAM,
570 * in case we can cope with the format
571 * @param bitsPerPixel color depth of the guest video RAM
572 * @param bytesPerLine length of a screen line in the guest video RAM
573 * @param w video mode width in pixels
574 * @param h video mode height in pixels
575 * @retval finished set to true if the method is synchronous and
576 * to false otherwise
577 *
578 * This method is called when the guest attempts to resize the virtual
579 * screen. The pointer to the guest's video RAM is supplied in case
580 * the framebuffer can handle the pixel format. If it can't, it should
581 * allocate a memory buffer itself, and the virtual VGA device will copy
582 * the guest VRAM to that in a format we can handle. The
583 * COMGETTER(UsesGuestVRAM) method is used to tell the VGA device which method
584 * we have chosen, and the other COMGETTER methods tell the device about
585 * the layout of our buffer. We currently handle all VRAM layouts except
586 * FramebufferPixelFormat_Opaque (which cannot be handled by
587 * definition).
588 */
589STDMETHODIMP FFmpegFB::RequestResize(ULONG aScreenId, ULONG pixelFormat,
590 BYTE *vram, ULONG bitsPerPixel,
591 ULONG bytesPerLine,
592 ULONG w, ULONG h, BOOL *finished)
593{
594 NOREF(aScreenId);
595 if (!finished)
596 return E_POINTER;
597 LogFlow(("FFmpeg::RequestResize called: pixelFormat=%lu, vram=%lu, "
598 "bpp=%lu bpl=%lu, w=%lu, h=%lu\n",
599 (unsigned long) pixelFormat, (unsigned long) vram,
600 (unsigned long) bitsPerPixel, (unsigned long) bytesPerLine,
601 (unsigned long) w, (unsigned long) h));
602 /* For now, we are doing things synchronously */
603 *finished = true;
604
605 /* We always reallocate our buffer */
606 if (mRGBBuffer)
607 RTMemFree(mRGBBuffer);
608 mGuestWidth = w;
609 mGuestHeight = h;
610
611 bool fallback = false;
612
613 /* See if there are conditions under which we can use the guest's VRAM,
614 * fallback to our own memory buffer otherwise */
615
616 if (pixelFormat == FramebufferPixelFormat_FOURCC_RGB)
617 {
618 switch (bitsPerPixel)
619 {
620#ifdef VBOX_WITH_VPX
621 case 32:
622 mFFMPEGPixelFormat = VPX_IMG_FMT_RGB32;
623 Log2(("FFmpeg::RequestResize: setting ffmpeg pixel format to VPX_IMG_FMT_RGB32\n"));
624 break;
625 case 24:
626 mFFMPEGPixelFormat = VPX_IMG_FMT_RGB24;
627 Log2(("FFmpeg::RequestResize: setting ffmpeg pixel format to VPX_IMG_FMT_RGB24\n"));
628 break;
629 case 16:
630 mFFMPEGPixelFormat = VPX_IMG_FMT_RGB565;
631 Log2(("FFmpeg::RequestResize: setting ffmpeg pixel format to VPX_IMG_FMT_RGB565\n"));
632 break;
633#else
634 case 32:
635 mFFMPEGPixelFormat = PIX_FMT_RGBA32;
636 Log2(("FFmpeg::RequestResize: setting ffmpeg pixel format to PIX_FMT_RGBA32\n"));
637 break;
638 case 24:
639 mFFMPEGPixelFormat = PIX_FMT_RGB24;
640 Log2(("FFmpeg::RequestResize: setting ffmpeg pixel format to PIX_FMT_RGB24\n"));
641 break;
642 case 16:
643 mFFMPEGPixelFormat = PIX_FMT_RGB565;
644 Log2(("FFmpeg::RequestResize: setting ffmpeg pixel format to PIX_FMT_RGB565\n"));
645 break;
646#endif
647 default:
648 fallback = true;
649 break;
650 }
651 }
652 else
653 {
654 fallback = true;
655 }
656
657 if (!fallback)
658 {
659 mPixelFormat = FramebufferPixelFormat_FOURCC_RGB;
660 mBufferAddress = reinterpret_cast<uint8_t *>(vram);
661 mBytesPerLine = bytesPerLine;
662 mBitsPerPixel = bitsPerPixel;
663 mRGBBuffer = 0;
664 Log2(("FFmpeg::RequestResize: setting mBufferAddress to vram and mLineSize to %lu\n",
665 (unsigned long) mBytesPerLine));
666 }
667 else
668 {
669 /* we always fallback to 32bpp RGB */
670 mPixelFormat = FramebufferPixelFormat_FOURCC_RGB;
671#ifdef VBOX_WITH_VPX
672 mFFMPEGPixelFormat = VPX_IMG_FMT_RGB32;
673 Log2(("FFmpeg::RequestResize: setting ffmpeg pixel format to VPX_IMG_FMT_RGB32\n"));
674#else
675 mFFMPEGPixelFormat = PIX_FMT_RGBA32;
676 Log2(("FFmpeg::RequestResize: setting ffmpeg pixel format to PIX_FMT_RGBA32\n"));
677#endif
678
679 mBytesPerLine = w * 4;
680 mBitsPerPixel = 32;
681 mRGBBuffer = reinterpret_cast<uint8_t *>(RTMemAlloc(mBytesPerLine * h));
682 AssertReturn(mRGBBuffer != 0, E_OUTOFMEMORY);
683 Log2(("FFmpeg::RequestResize: alloc'ing mBufferAddress and mRGBBuffer to %p and mBytesPerLine to %lu\n",
684 mBufferAddress, (unsigned long) mBytesPerLine));
685 mBufferAddress = mRGBBuffer;
686 }
687
688 /* Blank out the intermediate frame framebuffer */
689 memset(mTempRGBBuffer, 0, mFrameWidth * mFrameHeight * 4);
690 return S_OK;
691}
692
693/**
694 * Returns whether we like the given video mode.
695 *
696 * @returns COM status code
697 * @param width video mode width in pixels
698 * @param height video mode height in pixels
699 * @param bpp video mode bit depth in bits per pixel
700 * @param supported pointer to result variable
701 *
702 * As far as I know, the only restriction we have on video modes is that
703 * we have to have an even number of horizontal and vertical pixels.
704 * I sincerely doubt that anything else will be requested, and if it
705 * is anyway, we will just silently amputate one line when we write to
706 * the mpeg file.
707 */
708STDMETHODIMP FFmpegFB::VideoModeSupported(ULONG width, ULONG height,
709 ULONG bpp, BOOL *supported)
710{
711 if (!supported)
712 return E_POINTER;
713 *supported = true;
714 return S_OK;
715}
716
717/** Stubbed */
718STDMETHODIMP FFmpegFB::GetVisibleRegion(BYTE *rectangles, ULONG /* count */, ULONG * /* countCopied */)
719{
720 if (!rectangles)
721 return E_POINTER;
722 *rectangles = 0;
723 return S_OK;
724}
725
726/** Stubbed */
727STDMETHODIMP FFmpegFB::SetVisibleRegion(BYTE *rectangles, ULONG /* count */)
728{
729 if (!rectangles)
730 return E_POINTER;
731 return S_OK;
732}
733
734STDMETHODIMP FFmpegFB::ProcessVHWACommand(BYTE *pCommand)
735{
736 return E_NOTIMPL;
737}
738// Private Methods
739//////////////////////////////////////////////////////////////////////////
740//
741#ifndef VBOX_WITH_VPX
742HRESULT FFmpegFB::setup_library()
743{
744 /* Set up the avcodec library */
745 avcodec_init();
746 /* Register all codecs in the library. */
747 avcodec_register_all();
748 /* Register all formats in the format library */
749 av_register_all();
750 mpFormatContext = av_alloc_format_context();
751 AssertReturn(mpFormatContext != 0, E_OUTOFMEMORY);
752 mpStream = av_new_stream(mpFormatContext, 0);
753 AssertReturn(mpStream != 0, E_UNEXPECTED);
754 strncpy(mpFormatContext->filename, com::Utf8Str(mFileName).c_str(),
755 sizeof(mpFormatContext->filename));
756 return S_OK;
757}
758
759
760/**
761 * Determine the correct output format and codec for our MPEG file.
762 *
763 * @returns COM status code
764 *
765 * @pre The format context (mpFormatContext) should have already been
766 * allocated.
767 */
768HRESULT FFmpegFB::setup_output_format()
769{
770 Assert(mpFormatContext != 0);
771 AVOutputFormat *pOutFormat = guess_format(0, com::Utf8Str(mFileName).c_str(),
772 0);
773# ifdef VBOX_SHOW_AVAILABLE_FORMATS
774 if (!pOutFormat)
775 {
776 RTPrintf("Could not guess an output format for that extension.\n"
777 "Available formats:\n");
778 list_formats();
779 }
780# endif
781 AssertMsgReturn(pOutFormat != 0,
782 ("Could not deduce output format from file name\n"),
783 E_INVALIDARG);
784 AssertMsgReturn((pOutFormat->flags & AVFMT_RAWPICTURE) == 0,
785 ("Can't handle output format for file\n"),
786 E_INVALIDARG);
787 AssertMsgReturn((pOutFormat->flags & AVFMT_NOFILE) == 0,
788 ("pOutFormat->flags=%x, pOutFormat->name=%s\n",
789 pOutFormat->flags, pOutFormat->name), E_UNEXPECTED);
790 AssertMsgReturn(pOutFormat->video_codec != CODEC_ID_NONE,
791 ("No video codec available - you have probably selected a non-video file format\n"), E_UNEXPECTED);
792 mpFormatContext->oformat = pOutFormat;
793 /* Set format specific parameters - requires the format to be set. */
794 int rcSetParam = av_set_parameters(mpFormatContext, 0);
795 AssertReturn(rcSetParam >= 0, E_UNEXPECTED);
796# if 1 /* bird: This works for me on the mac, please review & test elsewhere. */
797 /* Fill in any uninitialized parameters like opt_output_file in ffpmeg.c does.
798 This fixes most of the buffer underflow warnings:
799 http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2005-June/001699.html */
800 if (!mpFormatContext->preload)
801 mpFormatContext->preload = (int)(0.5 * AV_TIME_BASE);
802 if (!mpFormatContext->max_delay)
803 mpFormatContext->max_delay = (int)(0.7 * AV_TIME_BASE);
804# endif
805 return S_OK;
806}
807
808
809HRESULT FFmpegFB::list_formats()
810{
811 AVCodec *codec;
812 for (codec = first_avcodec; codec != NULL; codec = codec->next)
813 {
814 if (codec->type == CODEC_TYPE_VIDEO && codec->encode)
815 {
816 AVOutputFormat *ofmt;
817 for (ofmt = first_oformat; ofmt != NULL; ofmt = ofmt->next)
818 {
819 if (ofmt->video_codec == codec->id)
820 RTPrintf(" %20s: %20s => '%s'\n", codec->name, ofmt->extensions, ofmt->long_name);
821 }
822 }
823 }
824 return S_OK;
825}
826#endif
827
828/**
829 * Open the FFmpeg codec and set it up (width, etc) for our MPEG file.
830 *
831 * @returns COM status code
832 *
833 * @pre The format context (mpFormatContext) and the stream (mpStream)
834 * should have already been allocated.
835 */
836HRESULT FFmpegFB::open_codec()
837{
838#ifdef VBOX_WITH_VPX
839 vpx_codec_err_t res;
840 /* Populate encoder configuration */
841 if ((res = vpx_codec_enc_config_default(interface, &mVpxConfig, 0)))
842 {
843 LogFlow(("Failed to configure codec \n"));
844 AssertReturn(res == 0, E_UNEXPECTED);
845 }
846
847 mVpxConfig.rc_target_bitrate = 512;
848 mVpxConfig.g_w = mFrameWidth;
849 mVpxConfig.g_h = mFrameHeight;
850 mVpxConfig.g_timebase.den = 30;
851 mVpxConfig.g_timebase.num = 1;
852 mVpxConfig.g_threads = 8;
853
854 vpx_rational ebmlFPS = mVpxConfig.g_timebase;
855 struct vpx_rational arg_framerate = {30, 1};
856 Ebml_WriteWebMFileHeader(&ebml, &mVpxConfig, &arg_framerate);
857 mDuration = (float)arg_framerate.den / (float)arg_framerate.num * 1000;
858
859 /* Initialize codec */
860 if (vpx_codec_enc_init(&mVpxCodec, interface, &mVpxConfig, 0))
861 {
862 LogFlow(("Failed to initialize encoder"));
863 AssertReturn(res == 0, E_UNEXPECTED);
864 }
865#else
866 Assert(mpFormatContext != 0);
867 Assert(mpStream != 0);
868 AVOutputFormat *pOutFormat = mpFormatContext->oformat;
869 AVCodecContext *pCodecContext = mpStream->codec;
870 AssertReturn(pCodecContext != 0, E_UNEXPECTED);
871 AVCodec *pCodec = avcodec_find_encoder(pOutFormat->video_codec);
872# ifdef VBOX_SHOW_AVAILABLE_FORMATS
873 if (!pCodec)
874 {
875 RTPrintf("Could not find a suitable codec for the output format on your system\n"
876 "Available formats:\n");
877 list_formats();
878 }
879# endif
880 AssertReturn(pCodec != 0, E_UNEXPECTED);
881 pCodecContext->codec_id = pOutFormat->video_codec;
882 pCodecContext->codec_type = CODEC_TYPE_VIDEO;
883 pCodecContext->bit_rate = mBitRate;
884 pCodecContext->width = mFrameWidth;
885 pCodecContext->height = mFrameHeight;
886 pCodecContext->time_base.den = 25;
887 pCodecContext->time_base.num = 1;
888 pCodecContext->gop_size = 12; /* at most one intra frame in 12 */
889 pCodecContext->max_b_frames = 1;
890 pCodecContext->pix_fmt = PIX_FMT_YUV420P;
891 /* taken from the ffmpeg output example */
892 // some formats want stream headers to be separate
893 if (!strcmp(pOutFormat->name, "mp4")
894 || !strcmp(pOutFormat->name, "mov")
895 || !strcmp(pOutFormat->name, "3gp"))
896 pCodecContext->flags |= CODEC_FLAG_GLOBAL_HEADER;
897 /* end output example section */
898 int rcOpenCodec = avcodec_open(pCodecContext, pCodec);
899 AssertReturn(rcOpenCodec >= 0, E_UNEXPECTED);
900#endif
901 return S_OK;
902}
903
904
905/**
906 * Open our MPEG file and write the header.
907 *
908 * @returns COM status code
909 *
910 * @pre The format context (mpFormatContext) and the stream (mpStream)
911 * should have already been allocated and set up.
912 */
913HRESULT FFmpegFB::open_output_file()
914{
915#ifdef VBOX_WITH_VPX
916 char szFileName[RTPATH_MAX];
917 strcpy(szFileName, com::Utf8Str(mFileName).c_str());
918 ebml.stream = fopen(szFileName, "wb");
919 if (!ebml.stream)
920 {
921 LogFlow(("Failed to open the output File \n"));
922 return E_FAIL;
923 }
924 return S_OK;
925#else
926 char szFileName[RTPATH_MAX];
927 Assert(mpFormatContext);
928 Assert(mpFormatContext->oformat);
929 strcpy(szFileName, com::Utf8Str(mFileName).c_str());
930 int rcUrlFopen = url_fopen(&mpFormatContext->pb,
931 szFileName, URL_WRONLY);
932 AssertReturn(rcUrlFopen >= 0, E_UNEXPECTED);
933 mfUrlOpen = true;
934 av_write_header(mpFormatContext);
935#endif
936 return S_OK;
937}
938
939
940/**
941 * Copy an area from the output buffer used by the virtual VGA (may
942 * just be the guest's VRAM) to our fixed size intermediate buffer.
943 * The picture in the intermediate buffer is centred if the guest
944 * screen dimensions are smaller and amputated if they are larger than
945 * our frame dimensions.
946 *
947 * @param x X co-ordinate of the upper left-hand corner of the
948 * area which has been updated
949 * @param y Y co-ordinate of the upper left-hand corner of the
950 * area which has been updated
951 * @param w width of the area which has been updated
952 * @param h height of the area which has been updated
953 */
954void FFmpegFB::copy_to_intermediate_buffer(ULONG x, ULONG y, ULONG w, ULONG h)
955{
956 Log2(("FFmpegFB::copy_to_intermediate_buffer: x=%lu, y=%lu, w=%lu, h=%lu\n",
957 (unsigned long) x, (unsigned long) y, (unsigned long) w, (unsigned long) h));
958 /* Perform clipping and calculate the destination co-ordinates */
959 ULONG destX, destY, bpp;
960 LONG xDiff = (LONG(mFrameWidth) - LONG(mGuestWidth)) / 2;
961 LONG yDiff = (LONG(mFrameHeight) - LONG(mGuestHeight)) / 2;
962 if (LONG(w) + xDiff + LONG(x) <= 0) /* nothing visible */
963 return;
964 if (LONG(x) < -xDiff)
965 {
966 w = LONG(w) + xDiff + x;
967 x = -xDiff;
968 destX = 0;
969 }
970 else
971 destX = x + xDiff;
972 if (LONG(h) + yDiff + LONG(y) <= 0) /* nothing visible */
973 return;
974 if (LONG(y) < -yDiff)
975 {
976 h = LONG(h) + yDiff + LONG(y);
977 y = -yDiff;
978 destY = 0;
979 }
980 else
981 destY = y + yDiff;
982 if (destX > mFrameWidth || destY > mFrameHeight)
983 return; /* nothing visible */
984 if (destX + w > mFrameWidth)
985 w = mFrameWidth - destX;
986 if (destY + h > mFrameHeight)
987 h = mFrameHeight - destY;
988 /* Calculate bytes per pixel */
989 if (mPixelFormat == FramebufferPixelFormat_FOURCC_RGB)
990 {
991 switch (mBitsPerPixel)
992 {
993 case 32:
994 case 24:
995 case 16:
996 bpp = mBitsPerPixel / 8;
997 break;
998 default:
999 AssertMsgFailed(("Unknown color depth! mBitsPerPixel=%d\n", mBitsPerPixel));
1000 bpp = 1;
1001 break;
1002 }
1003 }
1004 else
1005 {
1006 AssertMsgFailed(("Unknown pixel format! mPixelFormat=%d\n", mPixelFormat));
1007 bpp = 1;
1008 }
1009 /* Calculate start offset in source and destination buffers */
1010 ULONG srcOffs = y * mBytesPerLine + x * bpp;
1011 ULONG destOffs = (destY * mFrameWidth + destX) * bpp;
1012 /* do the copy */
1013 for (unsigned int i = 0; i < h; i++)
1014 {
1015 /* Overflow check */
1016 Assert(srcOffs + w * bpp <= mGuestHeight * mBytesPerLine);
1017 Assert(destOffs + w * bpp <= mFrameHeight * mFrameWidth * bpp);
1018 memcpy(mTempRGBBuffer + destOffs, mBufferAddress + srcOffs,
1019 w * bpp);
1020 srcOffs = srcOffs + mBytesPerLine;
1021 destOffs = destOffs + mFrameWidth * bpp;
1022 }
1023}
1024
1025
1026/**
1027 * Copy the RGB data in the intermediate framebuffer to YUV data in
1028 * the YUV framebuffer.
1029 *
1030 * @returns COM status code
1031 */
1032HRESULT FFmpegFB::do_rgb_to_yuv_conversion()
1033{
1034 switch (mFFMPEGPixelFormat)
1035 {
1036#ifdef VBOX_WITH_VPX
1037 case VPX_IMG_FMT_RGB32:
1038 if (!FFmpegWriteYUV420p<FFmpegBGRA32Iter>(mFrameWidth, mFrameHeight,
1039 mYUVBuffer, mTempRGBBuffer))
1040 return E_UNEXPECTED;
1041 break;
1042 case VPX_IMG_FMT_RGB24:
1043 if (!FFmpegWriteYUV420p<FFmpegBGR24Iter>(mFrameWidth, mFrameHeight,
1044 mYUVBuffer, mTempRGBBuffer))
1045 return E_UNEXPECTED;
1046 break;
1047 case VPX_IMG_FMT_RGB565:
1048 if (!FFmpegWriteYUV420p<FFmpegBGR565Iter>(mFrameWidth, mFrameHeight,
1049 mYUVBuffer, mTempRGBBuffer))
1050 return E_UNEXPECTED;
1051 break;
1052#else
1053 case PIX_FMT_RGBA32:
1054 if (!FFmpegWriteYUV420p<FFmpegBGRA32Iter>(mFrameWidth, mFrameHeight,
1055 mYUVBuffer, mTempRGBBuffer))
1056 return E_UNEXPECTED;
1057 break;
1058 case PIX_FMT_RGB24:
1059 if (!FFmpegWriteYUV420p<FFmpegBGR24Iter>(mFrameWidth, mFrameHeight,
1060 mYUVBuffer, mTempRGBBuffer))
1061 return E_UNEXPECTED;
1062 break;
1063 case PIX_FMT_RGB565:
1064 if (!FFmpegWriteYUV420p<FFmpegBGR565Iter>(mFrameWidth, mFrameHeight,
1065 mYUVBuffer, mTempRGBBuffer))
1066 return E_UNEXPECTED;
1067 break;
1068
1069#endif
1070 default:
1071 return E_UNEXPECTED;
1072 }
1073 return S_OK;
1074}
1075
1076/**
1077 * Encode the YUV framebuffer as an MPEG frame and write it to the file.
1078 *
1079 * @returns COM status code
1080 */
1081HRESULT FFmpegFB::do_encoding_and_write()
1082{
1083
1084
1085 /* A hack: ffmpeg mpeg2 only writes a frame if something has
1086 changed. So we flip the low luminance bit of the first
1087 pixel every frame. */
1088 if (mToggle)
1089 mYUVBuffer[0] |= 1;
1090 else
1091 mYUVBuffer[0] &= 0xfe;
1092 mToggle = !mToggle;
1093
1094#ifdef VBOX_WITH_VPX
1095 vpx_image_t VpxRawImage;
1096 vpx_codec_err_t res;
1097 const vpx_codec_cx_pkt_t *pkt;
1098 vpx_codec_iter_t iter = NULL;
1099
1100 if (mFrameWidth < 16 || mFrameWidth%2 || mFrameHeight <16 || mFrameHeight%2)
1101 LogFlow(("Invalid resolution: %ldx%ld", mFrameWidth, mFrameHeight));
1102
1103 if (!vpx_img_alloc(&VpxRawImage, VPX_IMG_FMT_YV12, mFrameWidth, mFrameHeight, 1))
1104 {
1105 LogFlow(("Faile tod allocate image", mFrameWidth, mFrameHeight));
1106 return E_OUTOFMEMORY;
1107 }
1108
1109 if (mYUVBuffer != NULL)
1110 {
1111// BOGUS! AssertReturn(VpxRawImage.w*VpxRawImage.h*3/2 <= sizeof(mYUVFrameSize), E_UNEXPECTED);
1112 memcpy(VpxRawImage.planes[0], (uint8_t *)mYUVBuffer, VpxRawImage.w*VpxRawImage.h*3/2);
1113 }
1114
1115 if ((res = vpx_codec_encode(&mVpxCodec, &VpxRawImage , mFrameCount,
1116 mDuration, 0, VPX_DL_REALTIME)))
1117 {
1118 LogFlow(("Failed to encode: %s\n", vpx_codec_err_to_string(res)));
1119 AssertReturn(res != 0, E_UNEXPECTED);
1120
1121 }
1122 while ((pkt = vpx_codec_get_cx_data(&mVpxCodec, &iter)))
1123 {
1124 switch (pkt->kind)
1125 {
1126 case VPX_CODEC_CX_FRAME_PKT:
1127 Ebml_WriteWebMBlock(&ebml, &mVpxConfig, pkt);
1128 break;
1129 default:
1130 break;
1131 }
1132 }
1133 mFrameCount++;
1134#else
1135 AVCodecContext *pContext = mpStream->codec;
1136 int cSize = avcodec_encode_video(pContext, mOutBuf, mYUVFrameSize * 2,
1137 mFrame);
1138 AssertMsgReturn(cSize >= 0,
1139 ("avcodec_encode_video() failed with rc=%d.\n"
1140 "mFrameWidth=%u, mFrameHeight=%u\n", cSize,
1141 mFrameWidth, mFrameHeight), E_UNEXPECTED);
1142 if (cSize > 0)
1143 {
1144 AVPacket Packet;
1145 av_init_packet(&Packet);
1146 Packet.pts = av_rescale_q(pContext->coded_frame->pts,
1147 pContext->time_base,
1148 mpStream->time_base);
1149 if(pContext->coded_frame->key_frame)
1150 Packet.flags |= PKT_FLAG_KEY;
1151 Packet.stream_index = mpStream->index;
1152 Packet.data = mOutBuf;
1153 Packet.size = cSize;
1154
1155 /* write the compressed frame in the media file */
1156 int rcWriteFrame = av_write_frame(mpFormatContext, &Packet);
1157 AssertReturn(rcWriteFrame == 0, E_UNEXPECTED);
1158 }
1159#endif
1160 return S_OK;
1161}
1162
1163#ifndef VBOX_WITH_VPX
1164/**
1165 * Capture the current (i.e. the last) frame as a PNG file with the
1166 * same basename as the captured video file.
1167 */
1168HRESULT FFmpegFB::write_png()
1169{
1170 HRESULT errorCode = E_OUTOFMEMORY;
1171 png_bytep *row_pointers;
1172 char PNGFileName[RTPATH_MAX], oldName[RTPATH_MAX];
1173 png_structp png_ptr;
1174 png_infop info_ptr;
1175 uint8_t *PNGBuffer;
1176 /* Work out the new file name - for some reason, we can't use
1177 the com::Utf8Str() directly, but have to copy it */
1178 strcpy(oldName, com::Utf8Str(mFileName).c_str());
1179 int baseLen = strrchr(oldName, '.') - oldName;
1180 if (baseLen == 0)
1181 baseLen = strlen(oldName);
1182 if (baseLen >= RTPATH_MAX - 5) /* for whatever reason */
1183 baseLen = RTPATH_MAX - 5;
1184 memcpy(&PNGFileName[0], oldName, baseLen);
1185 PNGFileName[baseLen] = '.';
1186 PNGFileName[baseLen + 1] = 'p';
1187 PNGFileName[baseLen + 2] = 'n';
1188 PNGFileName[baseLen + 3] = 'g';
1189 PNGFileName[baseLen + 4] = 0;
1190 /* Open output file */
1191 FILE *fp = fopen(PNGFileName, "wb");
1192 if (fp == 0)
1193 {
1194 errorCode = E_UNEXPECTED;
1195 goto fopen_failed;
1196 }
1197 /* Create libpng basic structures */
1198 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL,
1199 0 /* error function */, 0 /* warning function */);
1200 if (png_ptr == 0)
1201 goto png_create_write_struct_failed;
1202 info_ptr = png_create_info_struct(png_ptr);
1203 if (info_ptr == 0)
1204 {
1205 png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
1206 goto png_create_info_struct_failed;
1207 }
1208 /* Convert image to standard RGB24 to simplify life */
1209 PNGBuffer = reinterpret_cast<uint8_t *>(av_malloc(mFrameWidth
1210 * mFrameHeight * 4));
1211 if (PNGBuffer == 0)
1212 goto av_malloc_buffer_failed;
1213 row_pointers =
1214 reinterpret_cast<png_bytep *>(av_malloc(mFrameHeight
1215 * sizeof(png_bytep)));
1216 if (row_pointers == 0)
1217 goto av_malloc_pointers_failed;
1218 switch (mFFMPEGPixelFormat)
1219 {
1220 case PIX_FMT_RGBA32:
1221 if (!FFmpegWriteRGB24<FFmpegBGRA32Iter>(mFrameWidth, mFrameHeight,
1222 PNGBuffer, mTempRGBBuffer))
1223 goto setjmp_exception;
1224 break;
1225 case PIX_FMT_RGB24:
1226 if (!FFmpegWriteRGB24<FFmpegBGR24Iter>(mFrameWidth, mFrameHeight,
1227 PNGBuffer, mTempRGBBuffer))
1228 goto setjmp_exception;
1229 break;
1230 case PIX_FMT_RGB565:
1231 if (!FFmpegWriteRGB24<FFmpegBGR565Iter>(mFrameWidth, mFrameHeight,
1232 PNGBuffer, mTempRGBBuffer))
1233 goto setjmp_exception;
1234 break;
1235 default:
1236 goto setjmp_exception;
1237 }
1238 /* libpng exception handling */
1239 if (setjmp(png_jmpbuf(png_ptr)))
1240 goto setjmp_exception;
1241 /* pass libpng the file pointer */
1242 png_init_io(png_ptr, fp);
1243 /* set the image properties */
1244 png_set_IHDR(png_ptr, info_ptr, mFrameWidth, mFrameHeight,
1245 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
1246 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
1247 /* set up the information about the bitmap for libpng */
1248 row_pointers[0] = png_bytep(PNGBuffer);
1249 for (unsigned i = 1; i < mFrameHeight; i++)
1250 row_pointers[i] = row_pointers[i - 1] + mFrameWidth * 3;
1251 png_set_rows(png_ptr, info_ptr, &row_pointers[0]);
1252 /* and write the thing! */
1253 png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, 0);
1254 /* drop through to cleanup */
1255 errorCode = S_OK;
1256setjmp_exception:
1257 av_free(row_pointers);
1258av_malloc_pointers_failed:
1259 av_free(PNGBuffer);
1260av_malloc_buffer_failed:
1261 png_destroy_write_struct(&png_ptr, &info_ptr);
1262png_create_info_struct_failed:
1263png_create_write_struct_failed:
1264 fclose(fp);
1265fopen_failed:
1266 if (errorCode != S_OK)
1267 Log(("FFmpegFB::write_png: Failed to write .png image of final frame\n"));
1268 return errorCode;
1269}
1270#endif
1271
1272#ifdef VBOX_WITH_XPCOM
1273NS_DECL_CLASSINFO(FFmpegFB)
1274NS_IMPL_THREADSAFE_ISUPPORTS1_CI(FFmpegFB, IFramebuffer)
1275#endif
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use