diff options
Diffstat (limited to 'src/VBox/Frontends/VBoxHeadless')
12 files changed, 192 insertions, 1854 deletions
diff --git a/src/VBox/Frontends/VBoxHeadless/Framebuffer.cpp b/src/VBox/Frontends/VBoxHeadless/Framebuffer.cpp index 96f2d65f..c30e61f1 100644 --- a/src/VBox/Frontends/VBoxHeadless/Framebuffer.cpp +++ b/src/VBox/Frontends/VBoxHeadless/Framebuffer.cpp @@ -293,3 +293,8 @@ STDMETHODIMP VRDPFramebuffer::ProcessVHWACommand(BYTE *pCommand) { return E_NOTIMPL; } + +STDMETHODIMP VRDPFramebuffer::Notify3DEvent(ULONG uType, BYTE *pReserved) +{ + return E_NOTIMPL; +} diff --git a/src/VBox/Frontends/VBoxHeadless/Framebuffer.h b/src/VBox/Frontends/VBoxHeadless/Framebuffer.h index 525852c3..58384ac1 100644 --- a/src/VBox/Frontends/VBoxHeadless/Framebuffer.h +++ b/src/VBox/Frontends/VBoxHeadless/Framebuffer.h @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2010 Oracle Corporation + * Copyright (C) 2010-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -69,6 +69,7 @@ public: STDMETHOD(ProcessVHWACommand)(BYTE *pCommand); + STDMETHOD(Notify3DEvent)(ULONG uType, BYTE *pReserved); private: /* If the format is Opaque, then internal memory buffer is used. * Otherwise guest VRAM is used directly. diff --git a/src/VBox/Frontends/VBoxHeadless/Makefile.kmk b/src/VBox/Frontends/VBoxHeadless/Makefile.kmk index 1bf2aa20..8cae020a 100644 --- a/src/VBox/Frontends/VBoxHeadless/Makefile.kmk +++ b/src/VBox/Frontends/VBoxHeadless/Makefile.kmk @@ -19,9 +19,6 @@ SUB_DEPTH = ../../../.. include $(KBUILD_PATH)/subheader.kmk include $(PATH_SUB_CURRENT)/testcase/Makefile.kmk -ifdef VBOX_WITH_FFMPEG - include $(PATH_SUB_CURRENT)/VideoCapture/Makefile.kmk -endif # # Targets. @@ -47,7 +44,7 @@ VBoxHeadlessHardened_NAME = VBoxHeadless # VBoxHeadless_TEMPLATE := $(if $(VBOX_WITH_HARDENING),VBOXMAINCLIENTDLL,VBOXMAINCLIENTEXE) VBoxHeadless_DEFS += \ - VBOX_WITH_VIDEO_REC + VBOX_WITH_VIDEO_REC VBoxHeadless_SOURCES = VBoxHeadless.cpp VBoxHeadless_SOURCES += Framebuffer.cpp ifdef VBOX_WITH_GUEST_PROPS diff --git a/src/VBox/Frontends/VBoxHeadless/NullFramebuffer.h b/src/VBox/Frontends/VBoxHeadless/NullFramebuffer.h index 2c082234..346add4a 100644 --- a/src/VBox/Frontends/VBoxHeadless/NullFramebuffer.h +++ b/src/VBox/Frontends/VBoxHeadless/NullFramebuffer.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Oracle Corporation + * Copyright (C) 2010-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -205,6 +205,11 @@ public: return E_NOTIMPL; } + STDMETHOD(Notify3DEvent)(ULONG uType, BYTE *pReserved) + { + return E_NOTIMPL; + } + private: /** Guest framebuffer pixel format */ ULONG mPixelFormat; diff --git a/src/VBox/Frontends/VBoxHeadless/VBoxHeadless.cpp b/src/VBox/Frontends/VBoxHeadless/VBoxHeadless.cpp index 4a6d62f5..bca3b245 100644 --- a/src/VBox/Frontends/VBoxHeadless/VBoxHeadless.cpp +++ b/src/VBox/Frontends/VBoxHeadless/VBoxHeadless.cpp @@ -21,7 +21,7 @@ #include <VBox/com/Guid.h> #include <VBox/com/ErrorInfo.h> #include <VBox/com/errorprint.h> -#include <VBox/com/EventQueue.h> +#include <VBox/com/NativeEventQueue.h> #include <VBox/com/VirtualBox.h> #include <VBox/com/listeners.h> @@ -73,7 +73,7 @@ using namespace com; /* global weak references (for event handlers) */ static IConsole *gConsole = NULL; -static EventQueue *gEventQ = NULL; +static NativeEventQueue *gEventQ = NULL; /* flag whether frontend should terminate */ static volatile bool g_fTerminateFE = false; @@ -164,93 +164,129 @@ public: { case VBoxEventType_OnGuestPropertyChanged: { - ComPtr<IGuestPropertyChangedEvent> gpcev = aEvent; - Assert(gpcev); + ComPtr<IGuestPropertyChangedEvent> pChangedEvent = aEvent; + Assert(pChangedEvent); - Bstr aKey; - gpcev->COMGETTER(Name)(aKey.asOutParam()); + HRESULT hrc; - if (aKey == Bstr("/VirtualBox/GuestInfo/OS/NoLoggedInUsers")) + ComPtr <IMachine> pMachine; + if (gConsole) { - /* Check if this is our machine and the "disconnect on logout feature" is enabled. */ - BOOL fProcessDisconnectOnGuestLogout = FALSE; - ComPtr <IMachine> machine; - HRESULT hrc = S_OK; - - if (gConsole) + hrc = gConsole->COMGETTER(Machine)(pMachine.asOutParam()); + if (SUCCEEDED(hrc) && pMachine) { - hrc = gConsole->COMGETTER(Machine)(machine.asOutParam()); - if (SUCCEEDED(hrc) && machine) - { - Bstr id, machineId; - hrc = machine->COMGETTER(Id)(id.asOutParam()); - gpcev->COMGETTER(MachineId)(machineId.asOutParam()); - if (id == machineId) - { - Bstr value1; - hrc = machine->GetExtraData(Bstr("VRDP/DisconnectOnGuestLogout").raw(), - value1.asOutParam()); - if (SUCCEEDED(hrc) && value1 == "1") - { - fProcessDisconnectOnGuestLogout = TRUE; - } - } - } + Bstr gpMachineId, machineId; + hrc = pMachine->COMGETTER(Id)(gpMachineId.asOutParam()); + AssertComRC(hrc); + hrc = pChangedEvent->COMGETTER(MachineId)(machineId.asOutParam()); + AssertComRC(hrc); + if (gpMachineId != machineId) + hrc = VBOX_E_OBJECT_NOT_FOUND; } + } + else + hrc = VBOX_E_INVALID_VM_STATE; + + if (SUCCEEDED(hrc)) + { + Bstr strKey; + hrc = pChangedEvent->COMGETTER(Name)(strKey.asOutParam()); + AssertComRC(hrc); + + Bstr strValue; + hrc = pChangedEvent->COMGETTER(Value)(strValue.asOutParam()); + AssertComRC(hrc); - if (fProcessDisconnectOnGuestLogout) + Utf8Str utf8Key = strKey; + Utf8Str utf8Value = strValue; + LogRelFlow(("Guest property \"%s\" has been changed to \"%s\"\n", + utf8Key.c_str(), utf8Value.c_str())); + + if (utf8Key.equals("/VirtualBox/GuestInfo/OS/NoLoggedInUsers")) { - bool fDropConnection = false; + LogRelFlow(("Guest indicates that there %s logged in users\n", + utf8Value.equals("true") ? "are no" : "are")); - Bstr value; - gpcev->COMGETTER(Value)(value.asOutParam()); - Utf8Str utf8Value = value; + /* Check if this is our machine and the "disconnect on logout feature" is enabled. */ + BOOL fProcessDisconnectOnGuestLogout = FALSE; - if (!mfNoLoggedInUsers) /* Only if the property really changes. */ + /* Does the machine handle VRDP disconnects? */ + Bstr strDiscon; + hrc = pMachine->GetExtraData(Bstr("VRDP/DisconnectOnGuestLogout").raw(), + strDiscon.asOutParam()); + if (SUCCEEDED(hrc)) { - if ( utf8Value == "true" - /* Guest property got deleted due to reset, - * so it has no value anymore. */ - || utf8Value.isEmpty()) - { - mfNoLoggedInUsers = true; - fDropConnection = true; - } + Utf8Str utf8Discon = strDiscon; + fProcessDisconnectOnGuestLogout = utf8Discon.equals("1") + ? TRUE : FALSE; } - else if (utf8Value == "false") - mfNoLoggedInUsers = false; - /* Guest property got deleted due to reset, - * take the shortcut without touching the mfNoLoggedInUsers - * state. */ - else if (utf8Value.isEmpty()) - fDropConnection = true; - - if (fDropConnection) + + LogRelFlow(("VRDE: hrc=%Rhrc: Host %s disconnecting clients (current host state known: %s)\n", + hrc, fProcessDisconnectOnGuestLogout ? "will handle" : "does not handle", + mfNoLoggedInUsers ? "No users logged in" : "Users logged in")); + + if (fProcessDisconnectOnGuestLogout) { - /* If there is a connection, drop it. */ - ComPtr<IVRDEServerInfo> info; - hrc = gConsole->COMGETTER(VRDEServerInfo)(info.asOutParam()); - if (SUCCEEDED(hrc) && info) + bool fDropConnection = false; + if (!mfNoLoggedInUsers) /* Only if the property really changes. */ + { + if ( utf8Value == "true" + /* Guest property got deleted due to reset, + * so it has no value anymore. */ + || utf8Value.isEmpty()) + { + mfNoLoggedInUsers = true; + fDropConnection = true; + } + } + else if (utf8Value == "false") + mfNoLoggedInUsers = false; + /* Guest property got deleted due to reset, + * take the shortcut without touching the mfNoLoggedInUsers + * state. */ + else if (utf8Value.isEmpty()) + fDropConnection = true; + + LogRelFlow(("VRDE: szNoLoggedInUsers=%s, mfNoLoggedInUsers=%RTbool, fDropConnection=%RTbool\n", + utf8Value.c_str(), mfNoLoggedInUsers, fDropConnection)); + + if (fDropConnection) { - ULONG cClients = 0; - hrc = info->COMGETTER(NumberOfClients)(&cClients); - if (SUCCEEDED(hrc) && cClients > 0) + /* If there is a connection, drop it. */ + ComPtr<IVRDEServerInfo> info; + hrc = gConsole->COMGETTER(VRDEServerInfo)(info.asOutParam()); + if (SUCCEEDED(hrc) && info) { - ComPtr <IVRDEServer> vrdeServer; - hrc = machine->COMGETTER(VRDEServer)(vrdeServer.asOutParam()); - if (SUCCEEDED(hrc) && vrdeServer) + ULONG cClients = 0; + hrc = info->COMGETTER(NumberOfClients)(&cClients); + + LogRelFlow(("VRDE: connected clients=%RU32\n", cClients)); + if (SUCCEEDED(hrc) && cClients > 0) { - LogRel(("VRDE: the guest user has logged out, disconnecting remote clients.\n")); - vrdeServer->COMSETTER(Enabled)(FALSE); - vrdeServer->COMSETTER(Enabled)(TRUE); + ComPtr <IVRDEServer> vrdeServer; + hrc = pMachine->COMGETTER(VRDEServer)(vrdeServer.asOutParam()); + if (SUCCEEDED(hrc) && vrdeServer) + { + LogRel(("VRDE: the guest user has logged out, disconnecting remote clients.\n")); + hrc = vrdeServer->COMSETTER(Enabled)(FALSE); + AssertComRC(hrc); + HRESULT hrc2 = vrdeServer->COMSETTER(Enabled)(TRUE); + if (SUCCEEDED(hrc)) + hrc = hrc2; + } } } } } } + + if (FAILED(hrc)) + LogRelFlow(("VRDE: returned error=%Rhrc\n", hrc)); } + break; } + default: AssertFailed(); } @@ -259,6 +295,7 @@ public: } private: + bool mfNoLoggedInUsers; }; @@ -295,7 +332,7 @@ public: { ComPtr<IMouseCapabilityChangedEvent> mccev = aEvent; - Assert(mccev); + Assert(!mccev.isNull()); BOOL fSupportsAbsolute = false; mccev->COMGETTER(SupportsAbsolute)(&fSupportsAbsolute); @@ -496,7 +533,12 @@ static void parse_environ(unsigned long *pulFrameWidth, unsigned long *pulFrameH unsigned long *pulBitRate, const char **ppszFileName) { const char *pszEnvTemp; - +/** @todo r=bird: This isn't up to scratch. The life time of an RTEnvGet + * return value is only up to the next RTEnv*, *getenv, *putenv, + * setenv call in _any_ process in the system and the it has known and + * documented code page issues. + * + * Use RTEnvGetEx instead! */ if ((pszEnvTemp = RTEnvGet("VBOX_CAPTUREWIDTH")) != 0) { errno = 0; @@ -612,18 +654,18 @@ extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv, char **envp) unsigned fPATM = ~0U; unsigned fCSAM = ~0U; #ifdef VBOX_WITH_VIDEO_REC - unsigned fVIDEOREC = 0; + bool fVideoRec = 0; unsigned long ulFrameWidth = 800; unsigned long ulFrameHeight = 600; - unsigned long ulBitRate = 300000; - char pszMPEGFile[RTPATH_MAX]; + unsigned long ulBitRate = 300000; /** @todo r=bird: The COM type ULONG isn't unsigned long, it's 32-bit unsigned int. */ + char szMpegFile[RTPATH_MAX]; const char *pszFileNameParam = "VBox-%d.vob"; #endif /* VBOX_WITH_VIDEO_REC */ - LogFlow (("VBoxHeadless STARTED.\n")); - RTPrintf (VBOX_PRODUCT " Headless Interface " VBOX_VERSION_STRING "\n" - "(C) 2008-" VBOX_C_YEAR " " VBOX_VENDOR "\n" - "All rights reserved.\n\n"); + LogFlow(("VBoxHeadless STARTED.\n")); + RTPrintf(VBOX_PRODUCT " Headless Interface " VBOX_VERSION_STRING "\n" + "(C) 2008-" VBOX_C_YEAR " " VBOX_VENDOR "\n" + "All rights reserved.\n\n"); #ifdef VBOX_WITH_VIDEO_REC /* Parse the environment */ @@ -754,7 +796,7 @@ extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv, char **envp) break; #ifdef VBOX_WITH_VIDEO_REC case 'c': - fVIDEOREC = true; + fVideoRec = true; break; case 'w': ulFrameWidth = ValueUnion.u32; @@ -818,7 +860,7 @@ extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv, char **envp) LogError("VBoxHeadless: ERROR: Only one format modifier is allowed in the capture file name.", -1); return 1; } - RTStrPrintf(&pszMPEGFile[0], RTPATH_MAX, pszFileNameParam, RTProcSelf()); + RTStrPrintf(&szMpegFile[0], RTPATH_MAX, pszFileNameParam, RTProcSelf()); #endif /* defined VBOX_WITH_VIDEO_REC */ if (!pcszNameOrUUID) @@ -933,7 +975,7 @@ extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv, char **envp) RTLDRMOD hLdrVideoRecFB; PFNREGISTERVIDEORECFB pfnRegisterVideoRecFB; - if (fVIDEOREC) + if (fVideoRec) { HRESULT rcc = S_OK; int rrc = VINF_SUCCESS; @@ -957,7 +999,7 @@ extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv, char **envp) { Log2(("VBoxHeadless: calling pfnRegisterVideoRecFB\n")); rcc = pfnRegisterVideoRecFB(ulFrameWidth, ulFrameHeight, ulBitRate, - pszMPEGFile, &pFramebuffer); + szMpegFile, &pFramebuffer); if (rcc != S_OK) LogError("Failed to initialise video capturing - make sure that the file format\n" "you wish to use is supported on your system\n", rcc); @@ -983,7 +1025,7 @@ extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv, char **envp) for (uScreenId = 0; uScreenId < cMonitors; uScreenId++) { # ifdef VBOX_WITH_VIDEO_REC - if (fVIDEOREC && uScreenId == 0) + if (fVideoRec && uScreenId == 0) { /* Already registered. */ continue; @@ -1067,7 +1109,7 @@ extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv, char **envp) /* initialize global references */ gConsole = console; - gEventQ = com::EventQueue::getMainEventQueue(); + gEventQ = com::NativeEventQueue::getMainEventQueue(); /* VirtualBoxClient events registration. */ { @@ -1251,6 +1293,13 @@ extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv, char **envp) vboxListener = listener; com::SafeArray<VBoxEventType_T> eventTypes; eventTypes.push_back(VBoxEventType_OnGuestPropertyChanged); + + /** + * @todo Set the notification pattern to "/VirtualBox/GuestInfo/OS/ *Logged*" + * to not cause too much load. The current API is broken as + * IMachine::GuestPropertyNotificationPatterns() would change the + * filter for _all_ clients. This is not what we want! + */ CHECK_ERROR(es, RegisterListener(vboxListener, ComSafeArrayAsInParam(eventTypes), true)); } diff --git a/src/VBox/Frontends/VBoxHeadless/VBoxHeadless.h b/src/VBox/Frontends/VBoxHeadless/VBoxHeadless.h index c241f5b5..03a3e576 100644 --- a/src/VBox/Frontends/VBoxHeadless/VBoxHeadless.h +++ b/src/VBox/Frontends/VBoxHeadless/VBoxHeadless.h @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2006-2010 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Frontends/VBoxHeadless/VBoxHeadlessHardened.cpp b/src/VBox/Frontends/VBoxHeadless/VBoxHeadlessHardened.cpp index abb18eea..2d05ed74 100644 --- a/src/VBox/Frontends/VBoxHeadless/VBoxHeadlessHardened.cpp +++ b/src/VBox/Frontends/VBoxHeadless/VBoxHeadlessHardened.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2008 Oracle Corporation + * Copyright (C) 2008-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Frontends/VBoxHeadless/VideoCapture/FFmpegFB.cpp b/src/VBox/Frontends/VBoxHeadless/VideoCapture/FFmpegFB.cpp deleted file mode 100644 index 7b35b3b2..00000000 --- a/src/VBox/Frontends/VBoxHeadless/VideoCapture/FFmpegFB.cpp +++ /dev/null @@ -1,1223 +0,0 @@ -/** @file - * - * Framebuffer implementation that interfaces with FFmpeg - * to create a video of the guest. - */ - -/* - * Copyright (C) 2006-2012 Oracle Corporation - * - * This file is part of VirtualBox Open Source Edition (OSE), as - * available from http://www.virtualbox.org. This file is free software; - * you can redistribute it and/or modify it under the terms of the GNU - * General Public License (GPL) as published by the Free Software - * Foundation, in version 2 as it comes in the "COPYING" file of the - * VirtualBox OSE distribution. VirtualBox OSE is distributed in the - * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. - */ - -#define LOG_GROUP LOG_GROUP_GUI - -#include "FFmpegFB.h" - -#include <iprt/file.h> -#include <iprt/param.h> -#include <iprt/assert.h> -#include <VBox/log.h> -#include <png.h> -#include <iprt/stream.h> - -#define VBOX_SHOW_AVAILABLE_FORMATS - -// external constructor for dynamic loading -///////////////////////////////////////////////////////////////////////////// - -/** - * Callback function to register an ffmpeg framebuffer. - * - * @returns COM status code. - * @param width Framebuffer width. - * @param height Framebuffer height. - * @param bitrate Bitrate of mpeg file to be created. - * @param filename Name of mpeg file to be created - * @retval retVal The new framebuffer - */ -extern "C" DECLEXPORT(HRESULT) VBoxRegisterFFmpegFB(ULONG width, - ULONG height, ULONG bitrate, - com::Bstr filename, - IFramebuffer **retVal) -{ - Log2(("VBoxRegisterFFmpegFB: called\n")); - FFmpegFB *pFramebuffer = new FFmpegFB(width, height, bitrate, filename); - int rc = pFramebuffer->init(); - AssertMsg(rc == S_OK, - ("failed to initialise the FFmpeg framebuffer, rc = %d\n", - rc)); - if (rc == S_OK) - { - *retVal = pFramebuffer; - return S_OK; - } - delete pFramebuffer; - return rc; -} - - - - - -// constructor / destructor -///////////////////////////////////////////////////////////////////////////// - -/** - * Perform parts of initialisation which are guaranteed not to fail - * unless we run out of memory. In this case, we just set the guest - * buffer to 0 so that RequestResize() does not free it the first time - * it is called. - */ -#ifdef VBOX_WITH_VPX -FFmpegFB::FFmpegFB(ULONG width, ULONG height, ULONG bitrate, - com::Bstr filename) : - mfUrlOpen(false), - mBitRate(bitrate), - mPixelFormat(FramebufferPixelFormat_Opaque), - mBitsPerPixel(0), - mFileName(filename), - mBytesPerLine(0), - mFrameWidth(width), mFrameHeight(height), - mYUVFrameSize(width * height * 3 / 2), - mRGBBuffer(0), - mOutOfMemory(false), mToggle(false) -#else -FFmpegFB::FFmpegFB(ULONG width, ULONG height, ULONG bitrate, - com::Bstr filename) : - mpFormatContext(0), mpStream(0), - mfUrlOpen(false), - mBitRate(bitrate), - mPixelFormat(FramebufferPixelFormat_Opaque), - mBitsPerPixel(0), - mFileName(filename), - mBytesPerLine(0), - mFrameWidth(width), mFrameHeight(height), - mYUVFrameSize(width * height * 3 / 2),mRGBBuffer(0), - mOutOfMemory(false), mToggle(false) -#endif -{ - ULONG cPixels = width * height; - -#ifdef VBOX_WITH_VPX - Assert(width % 2 == 0 && height % 2 == 0); - /* For temporary RGB frame we allocate enough memory to deal with - RGB16 to RGB32 */ - mTempRGBBuffer = reinterpret_cast<uint8_t *>(RTMemAlloc(cPixels * 4)); - if (!mTempRGBBuffer) - goto nomem_temp_rgb_buffer; - mYUVBuffer = reinterpret_cast<uint8_t *>(RTMemAlloc(mYUVFrameSize)); - if (!mYUVBuffer) - goto nomem_yuv_buffer; - return; - - /* C-based memory allocation and how to deal with it in C++ :) */ -nomem_yuv_buffer: - Log(("Failed to allocate memory for mYUVBuffer\n")); - RTMemFree(mYUVBuffer); -nomem_temp_rgb_buffer: - Log(("Failed to allocate memory for mTempRGBBuffer\n")); - RTMemFree(mTempRGBBuffer); - mOutOfMemory = true; -#else - LogFlow(("Creating FFmpegFB object %p, width=%lu, height=%lu\n", - this, (unsigned long) width, (unsigned long) height)); - Assert(width % 2 == 0 && height % 2 == 0); - /* For temporary RGB frame we allocate enough memory to deal with - RGB16 to RGB32 */ - mTempRGBBuffer = reinterpret_cast<uint8_t *>(av_malloc(cPixels * 4)); - if (!mTempRGBBuffer) - goto nomem_temp_rgb_buffer; - mYUVBuffer = reinterpret_cast<uint8_t *>(av_malloc(mYUVFrameSize)); - if (!mYUVBuffer) - goto nomem_yuv_buffer; - mFrame = avcodec_alloc_frame(); - if (!mFrame) - goto nomem_mframe; - mOutBuf = reinterpret_cast<uint8_t *>(av_malloc(mYUVFrameSize * 2)); - if (!mOutBuf) - goto nomem_moutbuf; - - return; - - /* C-based memory allocation and how to deal with it in C++ :) */ -nomem_moutbuf: - Log(("Failed to allocate memory for mOutBuf\n")); - av_free(mFrame); -nomem_mframe: - Log(("Failed to allocate memory for mFrame\n")); - av_free(mYUVBuffer); -nomem_yuv_buffer: - Log(("Failed to allocate memory for mYUVBuffer\n")); - av_free(mTempRGBBuffer); -nomem_temp_rgb_buffer: - Log(("Failed to allocate memory for mTempRGBBuffer\n")); - mOutOfMemory = true; -#endif -} - - -/** - * Write the last frame to disk and free allocated memory - */ -FFmpegFB::~FFmpegFB() -{ - LogFlow(("Destroying FFmpegFB object %p\n", this)); -#ifdef VBOX_WITH_VPX - /* Dummy update to make sure we get all the frame (timing). */ - NotifyUpdate(0, 0, 0, 0); - /* Write the last pending frame before exiting */ - int rc = do_rgb_to_yuv_conversion(); - if (rc == S_OK) - VideoRecEncodeAndWrite(pVideoRecContext, mFrameWidth, mFrameHeight, mYUVBuffer); -# if 1 - /* Add another 10 seconds. */ - for (int i = 10*25; i > 0; i--) - VideoRecEncodeAndWrite(pVideoRecContext, mFrameWidth, mFrameHeight, mYUVBuffer); -# endif - VideoRecContextClose(pVideoRecContext); - RTCritSectDelete(&mCritSect); - - /* We have already freed the stream above */ - if (mTempRGBBuffer) - RTMemFree(mTempRGBBuffer); - if (mYUVBuffer) - RTMemFree(mYUVBuffer); - if (mRGBBuffer) - RTMemFree(mRGBBuffer); -#else - if (mpFormatContext != 0) - { - if (mfUrlOpen) - { - /* Dummy update to make sure we get all the frame (timing). */ - NotifyUpdate(0, 0, 0, 0); - /* Write the last pending frame before exiting */ - int rc = do_rgb_to_yuv_conversion(); - if (rc == S_OK) - do_encoding_and_write(); -# if 1 - /* Add another 10 seconds. */ - for (int i = 10*25; i > 0; i--) - do_encoding_and_write(); -# endif - /* write a png file of the last frame */ - write_png(); - avcodec_close(mpStream->codec); - av_write_trailer(mpFormatContext); - /* free the streams */ - for(unsigned i = 0; i < (unsigned)mpFormatContext->nb_streams; i++) { - av_freep(&mpFormatContext->streams[i]->codec); - av_freep(&mpFormatContext->streams[i]); - } -/* Changed sometime between 50.5.0 and 52.7.0 */ -# if LIBAVFORMAT_VERSION_INT >= (52 << 16) - url_fclose(mpFormatContext->pb); -# else /* older version */ - url_fclose(&mpFormatContext->pb); -# endif /* older version */ - } - av_free(mpFormatContext); - } - RTCritSectDelete(&mCritSect); - /* We have already freed the stream above */ - mpStream = 0; - if (mTempRGBBuffer) - av_free(mTempRGBBuffer); - if (mYUVBuffer) - av_free(mYUVBuffer); - if (mFrame) - av_free(mFrame); - if (mOutBuf) - av_free(mOutBuf); - if (mRGBBuffer) - RTMemFree(mRGBBuffer); -#endif -} - -// public methods only for internal purposes -///////////////////////////////////////////////////////////////////////////// - -/** - * Perform any parts of the initialisation which could potentially fail - * for reasons other than "out of memory". - * - * @returns COM status code - * @param width width to be used for MPEG frame framebuffer and initially - * for the guest frame buffer - must be a multiple of two - * @param height height to be used for MPEG frame framebuffer and - * initially for the guest framebuffer - must be a multiple - * of two - * @param depth depth to be used initially for the guest framebuffer - */ -HRESULT FFmpegFB::init() -{ - LogFlow(("Initialising FFmpegFB object %p\n", this)); - if (mOutOfMemory == true) - return E_OUTOFMEMORY; - int rc; - int rcOpenFile; - int rcOpenCodec; - -#ifdef VBOX_WITH_VPX - - rc = VideoRecContextCreate(&pVideoRecContext); - rc = RTCritSectInit(&mCritSect); - AssertReturn(rc == VINF_SUCCESS, E_UNEXPECTED); - - if(rc == VINF_SUCCESS) - rc = VideoRecContextInit(pVideoRecContext, mFileName, mFrameWidth, mFrameHeight); -#else - rc = RTCritSectInit(&mCritSect); - AssertReturn(rc == VINF_SUCCESS, E_UNEXPECTED); - int rcSetupLibrary = setup_library(); - AssertReturn(rcSetupLibrary == S_OK, rcSetupLibrary); - int rcSetupFormat = setup_output_format(); - AssertReturn(rcSetupFormat == S_OK, rcSetupFormat); - rcOpenCodec = open_codec(); - AssertReturn(rcOpenCodec == S_OK, rcOpenCodec); - rcOpenFile = open_output_file(); - AssertReturn(rcOpenFile == S_OK, rcOpenFile); - - /* Fill in the picture data for the AVFrame - not particularly - elegant, but that is the API. */ - avpicture_fill((AVPicture *) mFrame, mYUVBuffer, PIX_FMT_YUV420P, - mFrameWidth, mFrameHeight); -#endif - - /* Set the initial framebuffer size to the mpeg frame dimensions */ - BOOL finished; - RequestResize(0, FramebufferPixelFormat_Opaque, NULL, 0, 0, - mFrameWidth, mFrameHeight, &finished); - /* Start counting time */ - mLastTime = RTTimeMilliTS(); - mLastTime = mLastTime - mLastTime % 40; - return rc; -} - -// IFramebuffer properties -///////////////////////////////////////////////////////////////////////////// - -/** - * Return the address of the frame buffer for the virtual VGA device to - * write to. If COMGETTER(UsesGuestVRAM) returns FLASE (or if this address - * is not the same as the guests VRAM buffer), the device will perform - * translation. - * - * @returns COM status code - * @retval address The address of the buffer - */ -STDMETHODIMP FFmpegFB::COMGETTER(Address) (BYTE **address) -{ - if (!address) - return E_POINTER; - LogFlow(("FFmpeg::COMGETTER(Address): returning address %p\n", mBufferAddress)); - *address = mBufferAddress; - return S_OK; -} - -/** - * Return the width of our frame buffer. - * - * @returns COM status code - * @retval width The width of the frame buffer - */ -STDMETHODIMP FFmpegFB::COMGETTER(Width) (ULONG *width) -{ - if (!width) - return E_POINTER; - LogFlow(("FFmpeg::COMGETTER(Width): returning width %lu\n", - (unsigned long) mGuestWidth)); - *width = mGuestWidth; - return S_OK; -} - -/** - * Return the height of our frame buffer. - * - * @returns COM status code - * @retval height The height of the frame buffer - */ -STDMETHODIMP FFmpegFB::COMGETTER(Height) (ULONG *height) -{ - if (!height) - return E_POINTER; - LogFlow(("FFmpeg::COMGETTER(Height): returning height %lu\n", - (unsigned long) mGuestHeight)); - *height = mGuestHeight; - return S_OK; -} - -/** - * Return the colour depth of our frame buffer. Note that we actually - * store the pixel format, not the colour depth internally, since - * when display sets FramebufferPixelFormat_Opaque, it - * wants to retrieve FramebufferPixelFormat_Opaque and - * nothing else. - * - * @returns COM status code - * @retval bitsPerPixel The colour depth of the frame buffer - */ -STDMETHODIMP FFmpegFB::COMGETTER(BitsPerPixel) (ULONG *bitsPerPixel) -{ - if (!bitsPerPixel) - return E_POINTER; - *bitsPerPixel = mBitsPerPixel; - LogFlow(("FFmpeg::COMGETTER(BitsPerPixel): returning depth %lu\n", - (unsigned long) *bitsPerPixel)); - return S_OK; -} - -/** - * Return the number of bytes per line in our frame buffer. - * - * @returns COM status code - * @retval bytesPerLine The number of bytes per line - */ -STDMETHODIMP FFmpegFB::COMGETTER(BytesPerLine) (ULONG *bytesPerLine) -{ - if (!bytesPerLine) - return E_POINTER; - LogFlow(("FFmpeg::COMGETTER(BytesPerLine): returning line size %lu\n", - (unsigned long) mBytesPerLine)); - *bytesPerLine = mBytesPerLine; - return S_OK; -} - -/** - * Return the pixel layout of our frame buffer. - * - * @returns COM status code - * @retval pixelFormat The pixel layout - */ -STDMETHODIMP FFmpegFB::COMGETTER(PixelFormat) (ULONG *pixelFormat) -{ - if (!pixelFormat) - return E_POINTER; - LogFlow(("FFmpeg::COMGETTER(PixelFormat): returning pixel format: %lu\n", - (unsigned long) mPixelFormat)); - *pixelFormat = mPixelFormat; - return S_OK; -} - -/** - * Return whether we use the guest VRAM directly. - * - * @returns COM status code - * @retval pixelFormat The pixel layout - */ -STDMETHODIMP FFmpegFB::COMGETTER(UsesGuestVRAM) (BOOL *usesGuestVRAM) -{ - if (!usesGuestVRAM) - return E_POINTER; - LogFlow(("FFmpeg::COMGETTER(UsesGuestVRAM): uses guest VRAM? %d\n", - mRGBBuffer == NULL)); - *usesGuestVRAM = (mRGBBuffer == NULL); - return S_OK; -} - -/** - * Return the number of lines of our frame buffer which can not be used - * (e.g. for status lines etc?). - * - * @returns COM status code - * @retval heightReduction The number of unused lines - */ -STDMETHODIMP FFmpegFB::COMGETTER(HeightReduction) (ULONG *heightReduction) -{ - if (!heightReduction) - return E_POINTER; - /* no reduction */ - *heightReduction = 0; - LogFlow(("FFmpeg::COMGETTER(HeightReduction): returning 0\n")); - return S_OK; -} - -/** - * Return a pointer to the alpha-blended overlay used to render status icons - * etc above the framebuffer. - * - * @returns COM status code - * @retval aOverlay The overlay framebuffer - */ -STDMETHODIMP FFmpegFB::COMGETTER(Overlay) (IFramebufferOverlay **aOverlay) -{ - if (!aOverlay) - return E_POINTER; - /* not yet implemented */ - *aOverlay = 0; - LogFlow(("FFmpeg::COMGETTER(Overlay): returning 0\n")); - return S_OK; -} - -/** - * Return id of associated window - * - * @returns COM status code - * @retval winId Associated window id - */ -STDMETHODIMP FFmpegFB::COMGETTER(WinId) (LONG64 *winId) -{ - if (!winId) - return E_POINTER; - *winId = 0; - return S_OK; -} - -// IFramebuffer methods -///////////////////////////////////////////////////////////////////////////// - -STDMETHODIMP FFmpegFB::Lock() -{ - LogFlow(("FFmpeg::Lock: called\n")); - int rc = RTCritSectEnter(&mCritSect); - AssertRC(rc); - if (rc == VINF_SUCCESS) - return S_OK; - return E_UNEXPECTED; -} - -STDMETHODIMP FFmpegFB::Unlock() -{ - LogFlow(("FFmpeg::Unlock: called\n")); - RTCritSectLeave(&mCritSect); - return S_OK; -} - - -/** - * This method is used to notify us that an area of the guest framebuffer - * has been updated. - * - * @returns COM status code - * @param x X co-ordinate of the upper left-hand corner of the - * area which has been updated - * @param y Y co-ordinate of the upper left-hand corner of the - * area which has been updated - * @param w width of the area which has been updated - * @param h height of the area which has been updated - */ -STDMETHODIMP FFmpegFB::NotifyUpdate(ULONG x, ULONG y, ULONG w, ULONG h) -{ - int rc; - int64_t iCurrentTime = RTTimeMilliTS(); - - LogFlow(("FFmpeg::NotifyUpdate called: x=%lu, y=%lu, w=%lu, h=%lu\n", - (unsigned long) x, (unsigned long) y, (unsigned long) w, - (unsigned long) h)); - - /* We always leave at least one frame update pending, which we - process when the time until the next frame has elapsed. */ - if (iCurrentTime - mLastTime >= 40) - { - rc = do_rgb_to_yuv_conversion(); - if (rc != S_OK) - { -#ifdef VBOX_WITH_VPX - VideoRecCopyToIntBuffer(pVideoRecContext, x, y, w, h, mPixelFormat, - mBitsPerPixel, mBytesPerLine, mFrameWidth, - mFrameHeight, mGuestHeight, mGuestWidth, - mBufferAddress, mTempRGBBuffer); -#else - copy_to_intermediate_buffer(x, y, w, h); -#endif - return rc; - } - rc = do_encoding_and_write(); - if (rc != S_OK) - { -#ifdef VBOX_WITH_VPX - VideoRecCopyToIntBuffer(pVideoRecContext, x, y, w, h, mPixelFormat, - mBitsPerPixel, mBytesPerLine, mFrameWidth, - mFrameHeight, mGuestHeight, mGuestWidth, - mBufferAddress, mTempRGBBuffer); -#else - copy_to_intermediate_buffer(x, y, w, h); -#endif - - return rc; - } - mLastTime = mLastTime + 40; - /* Write frames for the time in-between. Not a good way - to handle this. */ - while (iCurrentTime - mLastTime >= 40) - { -/* rc = do_rgb_to_yuv_conversion(); - if (rc != S_OK) - { - copy_to_intermediate_buffer(x, y, w, h); - return rc; - } -*/ rc = do_encoding_and_write(); - if (rc != S_OK) - { -#ifdef VBOX_WITH_VPX - VideoRecCopyToIntBuffer(pVideoRecContext, x, y, w, h, mPixelFormat, - mBitsPerPixel, mBytesPerLine, mFrameWidth, - mFrameHeight, mGuestHeight, mGuestWidth, - mBufferAddress, mTempRGBBuffer); -#else - copy_to_intermediate_buffer(x, y, w, h); -#endif - return rc; - } - mLastTime = mLastTime + 40; - } - } - /* Finally we copy the updated data to the intermediate buffer, - ready for the next update. */ -#ifdef VBOX_WITH_VPX - VideoRecCopyToIntBuffer(pVideoRecContext, x, y, w, h, mPixelFormat, - mBitsPerPixel, mBytesPerLine, mFrameWidth, - mFrameHeight, mGuestHeight, mGuestWidth, - mBufferAddress, mTempRGBBuffer); - -#else - copy_to_intermediate_buffer(x, y, w, h); -#endif - return S_OK; -} - - -/** - * Requests a resize of our "screen". - * - * @returns COM status code - * @param pixelFormat Layout of the guest video RAM (i.e. 16, 24, - * 32 bpp) - * @param vram host context pointer to the guest video RAM, - * in case we can cope with the format - * @param bitsPerPixel color depth of the guest video RAM - * @param bytesPerLine length of a screen line in the guest video RAM - * @param w video mode width in pixels - * @param h video mode height in pixels - * @retval finished set to true if the method is synchronous and - * to false otherwise - * - * This method is called when the guest attempts to resize the virtual - * screen. The pointer to the guest's video RAM is supplied in case - * the framebuffer can handle the pixel format. If it can't, it should - * allocate a memory buffer itself, and the virtual VGA device will copy - * the guest VRAM to that in a format we can handle. The - * COMGETTER(UsesGuestVRAM) method is used to tell the VGA device which method - * we have chosen, and the other COMGETTER methods tell the device about - * the layout of our buffer. We currently handle all VRAM layouts except - * FramebufferPixelFormat_Opaque (which cannot be handled by - * definition). - */ -STDMETHODIMP FFmpegFB::RequestResize(ULONG aScreenId, ULONG pixelFormat, - BYTE *vram, ULONG bitsPerPixel, - ULONG bytesPerLine, - ULONG w, ULONG h, BOOL *finished) -{ - NOREF(aScreenId); - if (!finished) - return E_POINTER; - LogFlow(("FFmpeg::RequestResize called: pixelFormat=%lu, vram=%lu, " - "bpp=%lu bpl=%lu, w=%lu, h=%lu\n", - (unsigned long) pixelFormat, (unsigned long) vram, - (unsigned long) bitsPerPixel, (unsigned long) bytesPerLine, - (unsigned long) w, (unsigned long) h)); - /* For now, we are doing things synchronously */ - *finished = true; - - /* We always reallocate our buffer */ - if (mRGBBuffer) - RTMemFree(mRGBBuffer); - mGuestWidth = w; - mGuestHeight = h; - - bool fallback = false; - - /* See if there are conditions under which we can use the guest's VRAM, - * fallback to our own memory buffer otherwise */ - - if (pixelFormat == FramebufferPixelFormat_FOURCC_RGB) - { - switch (bitsPerPixel) - { -#ifdef VBOX_WITH_VPX - case 32: - mFFMPEGPixelFormat = VPX_IMG_FMT_RGB32; - Log2(("FFmpeg::RequestResize: setting ffmpeg pixel format to VPX_IMG_FMT_RGB32\n")); - break; - case 24: - mFFMPEGPixelFormat = VPX_IMG_FMT_RGB24; - Log2(("FFmpeg::RequestResize: setting ffmpeg pixel format to VPX_IMG_FMT_RGB24\n")); - break; - case 16: - mFFMPEGPixelFormat = VPX_IMG_FMT_RGB565; - Log2(("FFmpeg::RequestResize: setting ffmpeg pixel format to VPX_IMG_FMT_RGB565\n")); - break; -#else - case 32: - mFFMPEGPixelFormat = PIX_FMT_RGBA32; - Log2(("FFmpeg::RequestResize: setting ffmpeg pixel format to PIX_FMT_RGBA32\n")); - break; - case 24: - mFFMPEGPixelFormat = PIX_FMT_RGB24; - Log2(("FFmpeg::RequestResize: setting ffmpeg pixel format to PIX_FMT_RGB24\n")); - break; - case 16: - mFFMPEGPixelFormat = PIX_FMT_RGB565; - Log2(("FFmpeg::RequestResize: setting ffmpeg pixel format to PIX_FMT_RGB565\n")); - break; -#endif - default: - fallback = true; - break; - } - } - else - { - fallback = true; - } - - if (!fallback) - { - mPixelFormat = FramebufferPixelFormat_FOURCC_RGB; - mBufferAddress = reinterpret_cast<uint8_t *>(vram); - mBytesPerLine = bytesPerLine; - mBitsPerPixel = bitsPerPixel; - mRGBBuffer = 0; - Log2(("FFmpeg::RequestResize: setting mBufferAddress to vram and mLineSize to %lu\n", - (unsigned long) mBytesPerLine)); - } - else - { - /* we always fallback to 32bpp RGB */ - mPixelFormat = FramebufferPixelFormat_FOURCC_RGB; -#ifdef VBOX_WITH_VPX - mFFMPEGPixelFormat = VPX_IMG_FMT_RGB32; - Log2(("FFmpeg::RequestResize: setting ffmpeg pixel format to VPX_IMG_FMT_RGB32\n")); -#else - mFFMPEGPixelFormat = PIX_FMT_RGBA32; - Log2(("FFmpeg::RequestResize: setting ffmpeg pixel format to PIX_FMT_RGBA32\n")); -#endif - - mBytesPerLine = w * 4; - mBitsPerPixel = 32; - mRGBBuffer = reinterpret_cast<uint8_t *>(RTMemAlloc(mBytesPerLine * h)); - AssertReturn(mRGBBuffer != 0, E_OUTOFMEMORY); - Log2(("FFmpeg::RequestResize: alloc'ing mBufferAddress and mRGBBuffer to %p and mBytesPerLine to %lu\n", - mBufferAddress, (unsigned long) mBytesPerLine)); - mBufferAddress = mRGBBuffer; - } - - /* Blank out the intermediate frame framebuffer */ - memset(mTempRGBBuffer, 0, mFrameWidth * mFrameHeight * 4); - return S_OK; -} - -/** - * Returns whether we like the given video mode. - * - * @returns COM status code - * @param width video mode width in pixels - * @param height video mode height in pixels - * @param bpp video mode bit depth in bits per pixel - * @param supported pointer to result variable - * - * As far as I know, the only restriction we have on video modes is that - * we have to have an even number of horizontal and vertical pixels. - * I sincerely doubt that anything else will be requested, and if it - * is anyway, we will just silently amputate one line when we write to - * the mpeg file. - */ -STDMETHODIMP FFmpegFB::VideoModeSupported(ULONG width, ULONG height, - ULONG bpp, BOOL *supported) -{ - if (!supported) - return E_POINTER; - *supported = true; - return S_OK; -} - -/** Stubbed */ -STDMETHODIMP FFmpegFB::GetVisibleRegion(BYTE *rectangles, ULONG /* count */, ULONG * /* countCopied */) -{ - if (!rectangles) - return E_POINTER; - *rectangles = 0; - return S_OK; -} - -/** Stubbed */ -STDMETHODIMP FFmpegFB::SetVisibleRegion(BYTE *rectangles, ULONG /* count */) -{ - if (!rectangles) - return E_POINTER; - return S_OK; -} - -STDMETHODIMP FFmpegFB::ProcessVHWACommand(BYTE *pCommand) -{ - return E_NOTIMPL; -} -// Private Methods -////////////////////////////////////////////////////////////////////////// -// -#ifndef VBOX_WITH_VPX -HRESULT FFmpegFB::setup_library() -{ - /* Set up the avcodec library */ - avcodec_init(); - /* Register all codecs in the library. */ - avcodec_register_all(); - /* Register all formats in the format library */ - av_register_all(); - mpFormatContext = av_alloc_format_context(); - AssertReturn(mpFormatContext != 0, E_OUTOFMEMORY); - mpStream = av_new_stream(mpFormatContext, 0); - AssertReturn(mpStream != 0, E_UNEXPECTED); - strncpy(mpFormatContext->filename, com::Utf8Str(mFileName).c_str(), - sizeof(mpFormatContext->filename)); - return S_OK; -} - - -/** - * Determine the correct output format and codec for our MPEG file. - * - * @returns COM status code - * - * @pre The format context (mpFormatContext) should have already been - * allocated. - */ -HRESULT FFmpegFB::setup_output_format() -{ - Assert(mpFormatContext != 0); - AVOutputFormat *pOutFormat = guess_format(0, com::Utf8Str(mFileName).c_str(), - 0); -# ifdef VBOX_SHOW_AVAILABLE_FORMATS - if (!pOutFormat) - { - RTPrintf("Could not guess an output format for that extension.\n" - "Available formats:\n"); - list_formats(); - } -# endif - AssertMsgReturn(pOutFormat != 0, - ("Could not deduce output format from file name\n"), - E_INVALIDARG); - AssertMsgReturn((pOutFormat->flags & AVFMT_RAWPICTURE) == 0, - ("Can't handle output format for file\n"), - E_INVALIDARG); - AssertMsgReturn((pOutFormat->flags & AVFMT_NOFILE) == 0, - ("pOutFormat->flags=%x, pOutFormat->name=%s\n", - pOutFormat->flags, pOutFormat->name), E_UNEXPECTED); - AssertMsgReturn(pOutFormat->video_codec != CODEC_ID_NONE, - ("No video codec available - you have probably selected a non-video file format\n"), E_UNEXPECTED); - mpFormatContext->oformat = pOutFormat; - /* Set format specific parameters - requires the format to be set. */ - int rcSetParam = av_set_parameters(mpFormatContext, 0); - AssertReturn(rcSetParam >= 0, E_UNEXPECTED); -# if 1 /* bird: This works for me on the mac, please review & test elsewhere. */ - /* Fill in any uninitialized parameters like opt_output_file in ffpmeg.c does. - This fixes most of the buffer underflow warnings: - http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2005-June/001699.html */ - if (!mpFormatContext->preload) - mpFormatContext->preload = (int)(0.5 * AV_TIME_BASE); - if (!mpFormatContext->max_delay) - mpFormatContext->max_delay = (int)(0.7 * AV_TIME_BASE); -# endif - return S_OK; -} - - -HRESULT FFmpegFB::list_formats() -{ - AVCodec *codec; - for (codec = first_avcodec; codec != NULL; codec = codec->next) - { - if (codec->type == CODEC_TYPE_VIDEO && codec->encode) - { - AVOutputFormat *ofmt; - for (ofmt = first_oformat; ofmt != NULL; ofmt = ofmt->next) - { - if (ofmt->video_codec == codec->id) - RTPrintf(" %20s: %20s => '%s'\n", codec->name, ofmt->extensions, ofmt->long_name); - } - } - } - return S_OK; -} -#endif - -#ifndef VBOX_WITH_VPX -/** - * Open the FFmpeg codec and set it up (width, etc) for our MPEG file. - * - * @returns COM status code - * - * @pre The format context (mpFormatContext) and the stream (mpStream) - * should have already been allocated. - */ -HRESULT FFmpegFB::open_codec() -{ - Assert(mpFormatContext != 0); - Assert(mpStream != 0); - AVOutputFormat *pOutFormat = mpFormatContext->oformat; - AVCodecContext *pCodecContext = mpStream->codec; - AssertReturn(pCodecContext != 0, E_UNEXPECTED); - AVCodec *pCodec = avcodec_find_encoder(pOutFormat->video_codec); -# ifdef VBOX_SHOW_AVAILABLE_FORMATS - if (!pCodec) - { - RTPrintf("Could not find a suitable codec for the output format on your system\n" - "Available formats:\n"); - list_formats(); - } -# endif - AssertReturn(pCodec != 0, E_UNEXPECTED); - pCodecContext->codec_id = pOutFormat->video_codec; - pCodecContext->codec_type = CODEC_TYPE_VIDEO; - pCodecContext->bit_rate = mBitRate; - pCodecContext->width = mFrameWidth; - pCodecContext->height = mFrameHeight; - pCodecContext->time_base.den = 25; - pCodecContext->time_base.num = 1; - pCodecContext->gop_size = 12; /* at most one intra frame in 12 */ - pCodecContext->max_b_frames = 1; - pCodecContext->pix_fmt = PIX_FMT_YUV420P; - /* taken from the ffmpeg output example */ - // some formats want stream headers to be separate - if (!strcmp(pOutFormat->name, "mp4") - || !strcmp(pOutFormat->name, "mov") - || !strcmp(pOutFormat->name, "3gp")) - pCodecContext->flags |= CODEC_FLAG_GLOBAL_HEADER; - /* end output example section */ - int rcOpenCodec = avcodec_open(pCodecContext, pCodec); - AssertReturn(rcOpenCodec >= 0, E_UNEXPECTED); - return S_OK; -} - - -/** - * Open our MPEG file and write the header. - * - * @returns COM status code - * - * @pre The format context (mpFormatContext) and the stream (mpStream) - * should have already been allocated and set up. - */ -HRESULT FFmpegFB::open_output_file() -{ - char szFileName[RTPATH_MAX]; - Assert(mpFormatContext); - Assert(mpFormatContext->oformat); - strcpy(szFileName, com::Utf8Str(mFileName).c_str()); - int rcUrlFopen = url_fopen(&mpFormatContext->pb, - szFileName, URL_WRONLY); - AssertReturn(rcUrlFopen >= 0, E_UNEXPECTED); - mfUrlOpen = true; - av_write_header(mpFormatContext); - return S_OK; -} -#endif - -/** - * Copy an area from the output buffer used by the virtual VGA (may - * just be the guest's VRAM) to our fixed size intermediate buffer. - * The picture in the intermediate buffer is centred if the guest - * screen dimensions are smaller and amputated if they are larger than - * our frame dimensions. - * - * @param x X co-ordinate of the upper left-hand corner of the - * area which has been updated - * @param y Y co-ordinate of the upper left-hand corner of the - * area which has been updated - * @param w width of the area which has been updated - * @param h height of the area which has been updated - */ -void FFmpegFB::copy_to_intermediate_buffer(ULONG x, ULONG y, ULONG w, ULONG h) -{ - Log2(("FFmpegFB::copy_to_intermediate_buffer: x=%lu, y=%lu, w=%lu, h=%lu\n", - (unsigned long) x, (unsigned long) y, (unsigned long) w, (unsigned long) h)); - /* Perform clipping and calculate the destination co-ordinates */ - ULONG destX, destY, bpp; - LONG xDiff = (LONG(mFrameWidth) - LONG(mGuestWidth)) / 2; - LONG yDiff = (LONG(mFrameHeight) - LONG(mGuestHeight)) / 2; - if (LONG(w) + xDiff + LONG(x) <= 0) /* nothing visible */ - return; - if (LONG(x) < -xDiff) - { - w = LONG(w) + xDiff + x; - x = -xDiff; - destX = 0; - } - else - destX = x + xDiff; - - if (LONG(h) + yDiff + LONG(y) <= 0) /* nothing visible */ - return; - if (LONG(y) < -yDiff) - { - h = LONG(h) + yDiff + LONG(y); - y = -yDiff; - destY = 0; - } - else - destY = y + yDiff; - if (destX > mFrameWidth || destY > mFrameHeight) - return; /* nothing visible */ - if (destX + w > mFrameWidth) - w = mFrameWidth - destX; - if (destY + h > mFrameHeight) - h = mFrameHeight - destY; - /* Calculate bytes per pixel */ - if (mPixelFormat == FramebufferPixelFormat_FOURCC_RGB) - { - switch (mBitsPerPixel) - { - case 32: - case 24: - case 16: - bpp = mBitsPerPixel / 8; - break; - default: - AssertMsgFailed(("Unknown color depth! mBitsPerPixel=%d\n", mBitsPerPixel)); - bpp = 1; - break; - } - } - else - { - AssertMsgFailed(("Unknown pixel format! mPixelFormat=%d\n", mPixelFormat)); - bpp = 1; - } - /* Calculate start offset in source and destination buffers */ - ULONG srcOffs = y * mBytesPerLine + x * bpp; - ULONG destOffs = (destY * mFrameWidth + destX) * bpp; - /* do the copy */ - for (unsigned int i = 0; i < h; i++) - { - /* Overflow check */ - Assert(srcOffs + w * bpp <= mGuestHeight * mBytesPerLine); - Assert(destOffs + w * bpp <= mFrameHeight * mFrameWidth * bpp); - memcpy(mTempRGBBuffer + destOffs, mBufferAddress + srcOffs, - w * bpp); - srcOffs = srcOffs + mBytesPerLine; - destOffs = destOffs + mFrameWidth * bpp; - } -} - - -/** - * Copy the RGB data in the intermediate framebuffer to YUV data in - * the YUV framebuffer. - * - * @returns COM status code - */ -HRESULT FFmpegFB::do_rgb_to_yuv_conversion() -{ - switch (mFFMPEGPixelFormat) - { -#ifdef VBOX_WITH_VPX - case VPX_IMG_FMT_RGB32: - if (!FFmpegWriteYUV420p<FFmpegBGRA32Iter>(mFrameWidth, mFrameHeight, - mYUVBuffer, mTempRGBBuffer)) - return E_UNEXPECTED; - break; - case VPX_IMG_FMT_RGB24: - if (!FFmpegWriteYUV420p<FFmpegBGR24Iter>(mFrameWidth, mFrameHeight, - mYUVBuffer, mTempRGBBuffer)) - return E_UNEXPECTED; - break; - case VPX_IMG_FMT_RGB565: - if (!FFmpegWriteYUV420p<FFmpegBGR565Iter>(mFrameWidth, mFrameHeight, - mYUVBuffer, mTempRGBBuffer)) - return E_UNEXPECTED; - break; -#else - case PIX_FMT_RGBA32: - if (!FFmpegWriteYUV420p<FFmpegBGRA32Iter>(mFrameWidth, mFrameHeight, - mYUVBuffer, mTempRGBBuffer)) - return E_UNEXPECTED; - break; - case PIX_FMT_RGB24: - if (!FFmpegWriteYUV420p<FFmpegBGR24Iter>(mFrameWidth, mFrameHeight, - mYUVBuffer, mTempRGBBuffer)) - return E_UNEXPECTED; - break; - case PIX_FMT_RGB565: - if (!FFmpegWriteYUV420p<FFmpegBGR565Iter>(mFrameWidth, mFrameHeight, - mYUVBuffer, mTempRGBBuffer)) - return E_UNEXPECTED; - break; - -#endif - default: - return E_UNEXPECTED; - } - return S_OK; -} - -/** - * Encode the YUV framebuffer as an MPEG frame and write it to the file. - * - * @returns COM status code - */ -HRESULT FFmpegFB::do_encoding_and_write() -{ - - - /* A hack: ffmpeg mpeg2 only writes a frame if something has - changed. So we flip the low luminance bit of the first - pixel every frame. */ - if (mToggle) - mYUVBuffer[0] |= 1; - else - mYUVBuffer[0] &= 0xfe; - mToggle = !mToggle; - -#ifdef VBOX_WITH_VPX - VideoRecEncodeAndWrite(pVideoRecContext, mFrameWidth, mFrameHeight, mYUVBuffer); -#else - AVCodecContext *pContext = mpStream->codec; - int cSize = avcodec_encode_video(pContext, mOutBuf, mYUVFrameSize * 2, - mFrame); - AssertMsgReturn(cSize >= 0, - ("avcodec_encode_video() failed with rc=%d.\n" - "mFrameWidth=%u, mFrameHeight=%u\n", cSize, - mFrameWidth, mFrameHeight), E_UNEXPECTED); - if (cSize > 0) - { - AVPacket Packet; - av_init_packet(&Packet); - Packet.pts = av_rescale_q(pContext->coded_frame->pts, - pContext->time_base, - mpStream->time_base); - if(pContext->coded_frame->key_frame) - Packet.flags |= PKT_FLAG_KEY; - Packet.stream_index = mpStream->index; - Packet.data = mOutBuf; - Packet.size = cSize; - - /* write the compressed frame in the media file */ - int rcWriteFrame = av_write_frame(mpFormatContext, &Packet); - AssertReturn(rcWriteFrame == 0, E_UNEXPECTED); - } -#endif - return S_OK; -} - -#ifndef VBOX_WITH_VPX -/** - * Capture the current (i.e. the last) frame as a PNG file with the - * same basename as the captured video file. - */ -HRESULT FFmpegFB::write_png() -{ - HRESULT errorCode = E_OUTOFMEMORY; - png_bytep *row_pointers; - char PNGFileName[RTPATH_MAX], oldName[RTPATH_MAX]; - png_structp png_ptr; - png_infop info_ptr; - uint8_t *PNGBuffer; - /* Work out the new file name - for some reason, we can't use - the com::Utf8Str() directly, but have to copy it */ - strcpy(oldName, com::Utf8Str(mFileName).c_str()); - int baseLen = strrchr(oldName, '.') - oldName; - if (baseLen == 0) - baseLen = strlen(oldName); - if (baseLen >= RTPATH_MAX - 5) /* for whatever reason */ - baseLen = RTPATH_MAX - 5; - memcpy(&PNGFileName[0], oldName, baseLen); - PNGFileName[baseLen] = '.'; - PNGFileName[baseLen + 1] = 'p'; - PNGFileName[baseLen + 2] = 'n'; - PNGFileName[baseLen + 3] = 'g'; - PNGFileName[baseLen + 4] = 0; - /* Open output file */ - FILE *fp = fopen(PNGFileName, "wb"); - if (fp == 0) - { - errorCode = E_UNEXPECTED; - goto fopen_failed; - } - /* Create libpng basic structures */ - png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, - 0 /* error function */, 0 /* warning function */); - if (png_ptr == 0) - goto png_create_write_struct_failed; - info_ptr = png_create_info_struct(png_ptr); - if (info_ptr == 0) - { - png_destroy_write_struct(&png_ptr, (png_infopp)NULL); - goto png_create_info_struct_failed; - } - /* Convert image to standard RGB24 to simplify life */ - PNGBuffer = reinterpret_cast<uint8_t *>(av_malloc(mFrameWidth - * mFrameHeight * 4)); - if (PNGBuffer == 0) - goto av_malloc_buffer_failed; - row_pointers = - reinterpret_cast<png_bytep *>(av_malloc(mFrameHeight - * sizeof(png_bytep))); - if (row_pointers == 0) - goto av_malloc_pointers_failed; - switch (mFFMPEGPixelFormat) - { - case PIX_FMT_RGBA32: - if (!FFmpegWriteRGB24<FFmpegBGRA32Iter>(mFrameWidth, mFrameHeight, - PNGBuffer, mTempRGBBuffer)) - goto setjmp_exception; - break; - case PIX_FMT_RGB24: - if (!FFmpegWriteRGB24<FFmpegBGR24Iter>(mFrameWidth, mFrameHeight, - PNGBuffer, mTempRGBBuffer)) - goto setjmp_exception; - break; - case PIX_FMT_RGB565: - if (!FFmpegWriteRGB24<FFmpegBGR565Iter>(mFrameWidth, mFrameHeight, - PNGBuffer, mTempRGBBuffer)) - goto setjmp_exception; - break; - default: - goto setjmp_exception; - } - /* libpng exception handling */ - if (setjmp(png_jmpbuf(png_ptr))) - goto setjmp_exception; - /* pass libpng the file pointer */ - png_init_io(png_ptr, fp); - /* set the image properties */ - png_set_IHDR(png_ptr, info_ptr, mFrameWidth, mFrameHeight, - 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, - PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); - /* set up the information about the bitmap for libpng */ - row_pointers[0] = png_bytep(PNGBuffer); - for (unsigned i = 1; i < mFrameHeight; i++) - row_pointers[i] = row_pointers[i - 1] + mFrameWidth * 3; - png_set_rows(png_ptr, info_ptr, &row_pointers[0]); - /* and write the thing! */ - png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, 0); - /* drop through to cleanup */ - errorCode = S_OK; -setjmp_exception: - av_free(row_pointers); -av_malloc_pointers_failed: - av_free(PNGBuffer); -av_malloc_buffer_failed: - png_destroy_write_struct(&png_ptr, &info_ptr); -png_create_info_struct_failed: -png_create_write_struct_failed: - fclose(fp); -fopen_failed: - if (errorCode != S_OK) - Log(("FFmpegFB::write_png: Failed to write .png image of final frame\n")); - return errorCode; -} -#endif - -#ifdef VBOX_WITH_XPCOM -NS_DECL_CLASSINFO(FFmpegFB) -NS_IMPL_THREADSAFE_ISUPPORTS1_CI(FFmpegFB, IFramebuffer) -#endif diff --git a/src/VBox/Frontends/VBoxHeadless/VideoCapture/FFmpegFB.h b/src/VBox/Frontends/VBoxHeadless/VideoCapture/FFmpegFB.h deleted file mode 100644 index a3f9bdc0..00000000 --- a/src/VBox/Frontends/VBoxHeadless/VideoCapture/FFmpegFB.h +++ /dev/null @@ -1,458 +0,0 @@ -/** @file - * - * VBox Remote Desktop Protocol. - * FFmpeg framebuffer interface. - */ - -/* - * Copyright (C) 2006-2012 Oracle Corporation - * - * This file is part of VirtualBox Open Source Edition (OSE), as - * available from http://www.virtualbox.org. This file is free software; - * you can redistribute it and/or modify it under the terms of the GNU - * General Public License (GPL) as published by the Free Software - * Foundation, in version 2 as it comes in the "COPYING" file of the - * VirtualBox OSE distribution. VirtualBox OSE is distributed in the - * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. - */ - -#ifndef _H_FFMPEGFB -#define _H_FFMPEGFB - -#include <VBox/com/VirtualBox.h> -#include <iprt/uuid.h> - -#include <VBox/com/com.h> -#include <VBox/com/string.h> - -#include <iprt/initterm.h> -#include <iprt/critsect.h> - -#ifdef VBOX_WITH_VPX -#include "EbmlWriter.h" -#include "EncodeAndWrite.h" -#include <stdarg.h> -#include <string.h> -#define VPX_CODEC_DISABLE_COMPAT 1 -#include <vp8cx.h> -#include <vpx_image.h> -#include <vpx_mem.h> -#define interface (vpx_codec_vp8_cx()) -#else -# ifdef DEBUG -# define VBOX_DEBUG_FF DEBUG -# include <avcodec.h> -# include <avformat.h> -# undef DEBUG -# define DEBUG VBOX_DEBUG_FF -# else /* DEBUG not defined */ -# include <avcodec.h> -# include <avformat.h> -# endif /* DEBUG not defined */ -#endif - -#ifdef VBOX_WITH_VPX -PVIDEORECCONTEXT pVideoRecContext; -#endif - -class FFmpegFB : VBOX_SCRIPTABLE_IMPL(IFramebuffer) -{ -public: - FFmpegFB(ULONG width, ULONG height, ULONG bitrate, com::Bstr filename); - virtual ~FFmpegFB(); - -#ifndef VBOX_WITH_XPCOM - STDMETHOD_(ULONG, AddRef)() - { - return ::InterlockedIncrement (&refcnt); - } - STDMETHOD_(ULONG, Release)() - { - long cnt = ::InterlockedDecrement (&refcnt); - if (cnt == 0) - delete this; - return cnt; - } -#endif - VBOX_SCRIPTABLE_DISPATCH_IMPL(IFramebuffer) - - NS_DECL_ISUPPORTS - - // public methods only for internal purposes - HRESULT init (); - - STDMETHOD(COMGETTER(Width))(ULONG *width); - STDMETHOD(COMGETTER(Height))(ULONG *height); - STDMETHOD(Lock)(); - STDMETHOD(Unlock)(); - STDMETHOD(COMGETTER(Address))(BYTE **address); - STDMETHOD(COMGETTER(BitsPerPixel))(ULONG *bitsPerPixel); - STDMETHOD(COMGETTER(BytesPerLine))(ULONG *bytesPerLine); - STDMETHOD(COMGETTER(PixelFormat)) (ULONG *pixelFormat); - STDMETHOD(COMGETTER(UsesGuestVRAM)) (BOOL *usesGuestVRAM); - STDMETHOD(COMGETTER(HeightReduction)) (ULONG *heightReduction); - STDMETHOD(COMGETTER(Overlay)) (IFramebufferOverlay **aOverlay); - STDMETHOD(COMGETTER(WinId)) (LONG64 *winId); - - STDMETHOD(NotifyUpdate)(ULONG x, ULONG y, ULONG w, ULONG h); - STDMETHOD(RequestResize)(ULONG aScreenId, ULONG pixelFormat, BYTE *vram, - ULONG bitsPerPixel, ULONG bytesPerLine, - ULONG w, ULONG h, BOOL *finished); - STDMETHOD(VideoModeSupported)(ULONG width, ULONG height, ULONG bpp, BOOL *supported); - STDMETHOD(GetVisibleRegion)(BYTE *rectangles, ULONG count, ULONG *countCopied); - STDMETHOD(SetVisibleRegion)(BYTE *rectangles, ULONG count); - - STDMETHOD(ProcessVHWACommand)(BYTE *pCommand); -public: -private: -#ifdef VBOX_WITH_VPX - EbmlGlobal ebml; - vpx_codec_ctx_t mVpxCodec; - vpx_codec_enc_cfg_t mVpxConfig; - FILE * mOutputFile; - unsigned long mDuration; - uint32_t mFrameCount; - -#else - /** Pointer to ffmpeg's format information context */ - AVFormatContext *mpFormatContext; - /** ffmpeg context containing information about the stream */ - AVStream *mpStream; - /** Information for ffmpeg describing the current frame */ - AVFrame *mFrame; - - HRESULT setup_library(); - HRESULT setup_output_format(); - HRESULT list_formats(); -#endif - /** true if url_fopen actually succeeded */ - bool mfUrlOpen; - /** Guest framebuffer width */ - ULONG mGuestWidth; - /** Guest framebuffer height */ - ULONG mGuestHeight; - /** Bit rate used for encoding */ - ULONG mBitRate; - /** Guest framebuffer pixel format */ - ULONG mPixelFormat; - /** Guest framebuffer color depth */ - ULONG mBitsPerPixel; - /** Name of the file we will write to */ - com::Bstr mFileName; - /** Guest framebuffer line length */ - ULONG mBytesPerLine; - /** MPEG frame framebuffer width */ - ULONG mFrameWidth; - /** MPEG frame framebuffer height */ - ULONG mFrameHeight; - /** The size of one YUV frame */ - ULONG mYUVFrameSize; - /** If we can't use the video RAM directly, we allocate our own - * buffer */ - uint8_t *mRGBBuffer; - /** The address of the buffer - can be either mRGBBuffer or the - * guests VRAM (HC address) if we can handle that directly */ - uint8_t *mBufferAddress; - /** An intermediary RGB buffer with the same dimensions */ - uint8_t *mTempRGBBuffer; - /** Frame buffer translated into YUV420 for the mpeg codec */ - uint8_t *mYUVBuffer; - /** Temporary buffer into which the codec writes frames to be - * written into the file */ - uint8_t *mOutBuf; - RTCRITSECT mCritSect; - /** File where we store the mpeg stream */ - RTFILE mFile; - /** time at which the last "real" frame was created */ - int64_t mLastTime; - /** ffmpeg pixel format of guest framebuffer */ - int mFFMPEGPixelFormat; - /** Since we are building without exception support, we use this - to signal allocation failure in the constructor */ - bool mOutOfMemory; - /** A hack: ffmpeg mpeg2 only writes a frame if something has - changed. So we flip the low luminance bit of the first - pixel every frame. */ - bool mToggle; - - - HRESULT open_codec(); - HRESULT open_output_file(); - void copy_to_intermediate_buffer(ULONG x, ULONG y, ULONG w, ULONG h); - HRESULT do_rgb_to_yuv_conversion(); - HRESULT do_encoding_and_write(); - HRESULT write_png(); -#ifndef VBOX_WITH_XPCOM - long refcnt; -#endif -}; - -/** - * Iterator class for running through an BGRA32 image buffer and converting - * it to RGB. - */ -class FFmpegBGRA32Iter -{ -private: - enum { PIX_SIZE = 4 }; -public: - FFmpegBGRA32Iter(unsigned aWidth, unsigned aHeight, uint8_t *aBuffer) - { - mPos = 0; - mSize = aWidth * aHeight * PIX_SIZE; - mBuffer = aBuffer; - } - /** - * Convert the next pixel to RGB. - * @returns true on success, false if we have reached the end of the buffer - * @param aRed where to store the red value - * @param aGreen where to store the green value - * @param aBlue where to store the blue value - */ - bool getRGB(unsigned *aRed, unsigned *aGreen, unsigned *aBlue) - { - bool rc = false; - if (mPos + PIX_SIZE <= mSize) - { - *aRed = mBuffer[mPos + 2]; - *aGreen = mBuffer[mPos + 1]; - *aBlue = mBuffer[mPos]; - mPos += PIX_SIZE; - rc = true; - } - return rc; - } - - /** - * Skip forward by a certain number of pixels - * @param aPixels how many pixels to skip - */ - void skip(unsigned aPixels) - { - mPos += PIX_SIZE * aPixels; - } -private: - /** Size of the picture buffer */ - unsigned mSize; - /** Current position in the picture buffer */ - unsigned mPos; - /** Address of the picture buffer */ - uint8_t *mBuffer; -}; - -/** - * Iterator class for running through an BGR24 image buffer and converting - * it to RGB. - */ -class FFmpegBGR24Iter -{ -private: - enum { PIX_SIZE = 3 }; -public: - FFmpegBGR24Iter(unsigned aWidth, unsigned aHeight, uint8_t *aBuffer) - { - mPos = 0; - mSize = aWidth * aHeight * PIX_SIZE; - mBuffer = aBuffer; - } - /** - * Convert the next pixel to RGB. - * @returns true on success, false if we have reached the end of the buffer - * @param aRed where to store the red value - * @param aGreen where to store the green value - * @param aBlue where to store the blue value - */ - bool getRGB(unsigned *aRed, unsigned *aGreen, unsigned *aBlue) - { - bool rc = false; - if (mPos + PIX_SIZE <= mSize) - { - *aRed = mBuffer[mPos + 2]; - *aGreen = mBuffer[mPos + 1]; - *aBlue = mBuffer[mPos]; - mPos += PIX_SIZE; - rc = true; - } - return rc; - } - - /** - * Skip forward by a certain number of pixels - * @param aPixels how many pixels to skip - */ - void skip(unsigned aPixels) - { - mPos += PIX_SIZE * aPixels; - } -private: - /** Size of the picture buffer */ - unsigned mSize; - /** Current position in the picture buffer */ - unsigned mPos; - /** Address of the picture buffer */ - uint8_t *mBuffer; -}; - -/** - * Iterator class for running through an BGR565 image buffer and converting - * it to RGB. - */ -class FFmpegBGR565Iter -{ -private: - enum { PIX_SIZE = 2 }; -public: - FFmpegBGR565Iter(unsigned aWidth, unsigned aHeight, uint8_t *aBuffer) - { - mPos = 0; - mSize = aWidth * aHeight * PIX_SIZE; - mBuffer = aBuffer; - } - /** - * Convert the next pixel to RGB. - * @returns true on success, false if we have reached the end of the buffer - * @param aRed where to store the red value - * @param aGreen where to store the green value - * @param aBlue where to store the blue value - */ - bool getRGB(unsigned *aRed, unsigned *aGreen, unsigned *aBlue) - { - bool rc = false; - if (mPos + PIX_SIZE <= mSize) - { - unsigned uFull = (((unsigned) mBuffer[mPos + 1]) << 8) - | ((unsigned) mBuffer[mPos]); - *aRed = (uFull >> 8) & ~7; - *aGreen = (uFull >> 3) & ~3 & 0xff; - *aBlue = (uFull << 3) & ~7 & 0xff; - mPos += PIX_SIZE; - rc = true; - } - return rc; - } - - /** - * Skip forward by a certain number of pixels - * @param aPixels how many pixels to skip - */ - void skip(unsigned aPixels) - { - mPos += PIX_SIZE * aPixels; - } -private: - /** Size of the picture buffer */ - unsigned mSize; - /** Current position in the picture buffer */ - unsigned mPos; - /** Address of the picture buffer */ - uint8_t *mBuffer; -}; - - -/** - * Convert an image to YUV420p format - * @returns true on success, false on failure - * @param aWidth width of image - * @param aHeight height of image - * @param aDestBuf an allocated memory buffer large enough to hold the - * destination image (i.e. width * height * 12bits) - * @param aSrcBuf the source image as an array of bytes - */ -template <class T> -inline bool FFmpegWriteYUV420p(unsigned aWidth, unsigned aHeight, uint8_t *aDestBuf, - uint8_t *aSrcBuf) -{ - AssertReturn(0 == (aWidth & 1), false); - AssertReturn(0 == (aHeight & 1), false); - bool rc = true; - T iter1(aWidth, aHeight, aSrcBuf); - T iter2 = iter1; - iter2.skip(aWidth); - unsigned cPixels = aWidth * aHeight; - unsigned offY = 0; - unsigned offU = cPixels; - unsigned offV = cPixels + cPixels / 4; - for (unsigned i = 0; (i < aHeight / 2) && rc; ++i) - { - for (unsigned j = 0; (j < aWidth / 2) && rc; ++j) - { - unsigned red, green, blue, u, v; - rc = iter1.getRGB(&red, &green, &blue); - if (rc) - { - aDestBuf[offY] = ((66 * red + 129 * green + 25 * blue + 128) >> 8) + 16; - u = (((-38 * red - 74 * green + 112 * blue + 128) >> 8) + 128) / 4; - v = (((112 * red - 94 * green - 18 * blue + 128) >> 8) + 128) / 4; - rc = iter1.getRGB(&red, &green, &blue); - } - if (rc) - { - aDestBuf[offY + 1] = ((66 * red + 129 * green + 25 * blue + 128) >> 8) + 16; - u += (((-38 * red - 74 * green + 112 * blue + 128) >> 8) + 128) / 4; - v += (((112 * red - 94 * green - 18 * blue + 128) >> 8) + 128) / 4; - rc = iter2.getRGB(&red, &green, &blue); - } - if (rc) - { - aDestBuf[offY + aWidth] = ((66 * red + 129 * green + 25 * blue + 128) >> 8) + 16; - u += (((-38 * red - 74 * green + 112 * blue + 128) >> 8) + 128) / 4; - v += (((112 * red - 94 * green - 18 * blue + 128) >> 8) + 128) / 4; - rc = iter2.getRGB(&red, &green, &blue); - } - if (rc) - { - aDestBuf[offY + aWidth + 1] = ((66 * red + 129 * green + 25 * blue + 128) >> 8) + 16; - u += (((-38 * red - 74 * green + 112 * blue + 128) >> 8) + 128) / 4; - v += (((112 * red - 94 * green - 18 * blue + 128) >> 8) + 128) / 4; - aDestBuf[offU] = u; - aDestBuf[offV] = v; - offY += 2; - ++offU; - ++offV; - } - } - if (rc) - { - iter1.skip(aWidth); - iter2.skip(aWidth); - offY += aWidth; - } - } - return rc; -} - - -/** - * Convert an image to RGB24 format - * @returns true on success, false on failure - * @param aWidth width of image - * @param aHeight height of image - * @param aDestBuf an allocated memory buffer large enough to hold the - * destination image (i.e. width * height * 12bits) - * @param aSrcBuf the source image as an array of bytes - */ -template <class T> -inline bool FFmpegWriteRGB24(unsigned aWidth, unsigned aHeight, uint8_t *aDestBuf, - uint8_t *aSrcBuf) -{ - enum { PIX_SIZE = 3 }; - bool rc = true; - AssertReturn(0 == (aWidth & 1), false); - AssertReturn(0 == (aHeight & 1), false); - T iter(aWidth, aHeight, aSrcBuf); - unsigned cPixels = aWidth * aHeight; - for (unsigned i = 0; (i < cPixels) && rc; ++i) - { - unsigned red, green, blue; - rc = iter.getRGB(&red, &green, &blue); - if (rc) - { - aDestBuf[i * PIX_SIZE] = red; - aDestBuf[i * PIX_SIZE + 1] = green; - aDestBuf[i * PIX_SIZE + 2] = blue; - } - } - return rc; -} - -#endif /* !_H_FFMPEGFB */ diff --git a/src/VBox/Frontends/VBoxHeadless/VideoCapture/Makefile.kmk b/src/VBox/Frontends/VBoxHeadless/VideoCapture/Makefile.kmk deleted file mode 100644 index 88a39325..00000000 --- a/src/VBox/Frontends/VBoxHeadless/VideoCapture/Makefile.kmk +++ /dev/null @@ -1,32 +0,0 @@ -# $Id: Makefile.kmk $ -## @file -# Sub-Makefile for the ffmpeg frame buffer module. -# - -# -# Copyright (C) 2006-2012 Oracle Corporation -# -# This file is part of VirtualBox Open Source Edition (OSE), as -# available from http://www.virtualbox.org. This file is free software; -# you can redistribute it and/or modify it under the terms of the GNU -# General Public License (GPL) as published by the Free Software -# Foundation, in version 2 as it comes in the "COPYING" file of the -# VirtualBox OSE distribution. VirtualBox OSE is distributed in the -# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. -# - -SUB_DEPTH = ../../../../.. -include $(KBUILD_PATH)/subheader.kmk - -DLLS += VBoxVideoRecFB -VBoxVideoRecFB_TEMPLATE = VBOXMAINCLIENTDLL - -VBoxVideoRecFB_SDKS = VBOX_FFMPEG VBOX_LIBPNG VBOX_ZLIB - -VBoxVideoRecFB_SOURCES += \ - VideoRecFB.cpp - -VBoxVideoRecFB_CXXFLAGS.linux += -fPIC - -include $(FILE_KBUILD_SUB_FOOTER) - diff --git a/src/VBox/Frontends/VBoxHeadless/testcase/Makefile.kmk b/src/VBox/Frontends/VBoxHeadless/testcase/Makefile.kmk index c14eb4ae..02e7d092 100644 --- a/src/VBox/Frontends/VBoxHeadless/testcase/Makefile.kmk +++ b/src/VBox/Frontends/VBoxHeadless/testcase/Makefile.kmk @@ -20,7 +20,7 @@ include $(KBUILD_PATH)/subheader.kmk ifdef VBOX_WITH_TESTCASES PROGRAMS += tstHeadless - tstHeadless_TEMPLATE = VBOXMAINCLIENTEXE + tstHeadless_TEMPLATE = VBOXMAINCLIENTTSTEXE tstHeadless_SOURCES = tstHeadless.cpp endif diff --git a/src/VBox/Frontends/VBoxHeadless/testcase/tstHeadless.cpp b/src/VBox/Frontends/VBoxHeadless/testcase/tstHeadless.cpp index 4d94438a..3e40a75b 100644 --- a/src/VBox/Frontends/VBoxHeadless/testcase/tstHeadless.cpp +++ b/src/VBox/Frontends/VBoxHeadless/testcase/tstHeadless.cpp @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2006-2009 Oracle Corporation + * Copyright (C) 2006-2014 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -21,7 +21,6 @@ #include <VBox/com/Guid.h> #include <VBox/com/ErrorInfo.h> #include <VBox/com/errorprint.h> -#include <VBox/com/EventQueue.h> #include <VBox/com/VirtualBox.h> @@ -37,7 +36,7 @@ using namespace com; /** * Entry point. */ -int main (int argc, char **argv) +int main(int argc, char **argv) { // initialize VBox Runtime RTR3InitExe(argc, &argv, 0); @@ -55,18 +54,18 @@ int main (int argc, char **argv) if (!name || !operation) { - RTPrintf ("\nUsage:\n\n" - "%s <machine_name> [on|off|pause|resume]\n\n", - argv [0]); - return -1; + RTPrintf("\nUsage:\n\n" + "%s <machine_name> [on|off|pause|resume]\n\n", + argv [0]); + return 0; } - RTPrintf ("\n"); - RTPrintf ("tstHeadless STARTED.\n"); + RTPrintf("\n"); + RTPrintf("tstHeadless STARTED.\n"); - RTPrintf ("VM name : {%s}\n" - "Operation : %s\n\n", - name, operation); + RTPrintf("VM name : {%s}\n" + "Operation : %s\n\n", + name, operation); HRESULT rc; @@ -82,8 +81,8 @@ int main (int argc, char **argv) ComPtr <IVirtualBox> virtualBox; ComPtr <ISession> session; - RTPrintf ("Creating VirtualBox object...\n"); - rc = virtualBox.createLocalObject (CLSID_VirtualBox); + RTPrintf("Creating VirtualBox object...\n"); + rc = virtualBox.createLocalObject(CLSID_VirtualBox); if (FAILED(rc)) RTPrintf("ERROR: failed to create the VirtualBox object!\n"); else @@ -93,7 +92,7 @@ int main (int argc, char **argv) RTPrintf("ERROR: failed to create a session object!\n"); } - if (FAILED (rc)) + if (FAILED(rc)) { com::ErrorInfo info; if (!info.isFullAvailable() && !info.isBasicAvailable()) @@ -106,11 +105,6 @@ int main (int argc, char **argv) break; } - // create the event queue - // (here it is necessary only to process remaining XPCOM/IPC events - // after the session is closed) - EventQueue eventQ; - ComPtr <IMachine> m; // find ID by name @@ -120,78 +114,78 @@ int main (int argc, char **argv) if (!strcmp(operation, "on")) { ComPtr <IProgress> progress; - RTPrintf ("Opening a new (remote) session...\n"); - CHECK_ERROR_BREAK (m, - LaunchVMProcess(session, Bstr("vrdp").raw(), - NULL, progress.asOutParam())); + RTPrintf("Opening a new (remote) session...\n"); + CHECK_ERROR_BREAK(m, + LaunchVMProcess(session, Bstr("vrdp").raw(), + NULL, progress.asOutParam())); - RTPrintf ("Waiting for the remote session to open...\n"); - CHECK_ERROR_BREAK (progress, WaitForCompletion (-1)); + RTPrintf("Waiting for the remote session to open...\n"); + CHECK_ERROR_BREAK(progress, WaitForCompletion(-1)); BOOL completed; - CHECK_ERROR_BREAK (progress, COMGETTER(Completed) (&completed)); - ASSERT (completed); + CHECK_ERROR_BREAK(progress, COMGETTER(Completed)(&completed)); + ASSERT(completed); LONG resultCode; - CHECK_ERROR_BREAK (progress, COMGETTER(ResultCode) (&resultCode)); - if (FAILED (resultCode)) + CHECK_ERROR_BREAK(progress, COMGETTER(ResultCode)(&resultCode)); + if (FAILED(resultCode)) { ProgressErrorInfo info(progress); com::GluePrintErrorInfo(info); } else { - RTPrintf ("Remote session has been successfully opened.\n"); + RTPrintf("Remote session has been successfully opened.\n"); } } else { - RTPrintf ("Opening an existing session...\n"); + RTPrintf("Opening an existing session...\n"); CHECK_ERROR_BREAK(m, LockMachine(session, LockType_Shared)); ComPtr <IConsole> console; CHECK_ERROR_BREAK(session, COMGETTER(Console)(console.asOutParam())); - if (!strcmp (operation, "off")) + if (!strcmp(operation, "off")) { ComPtr <IProgress> progress; - RTPrintf ("Powering the VM off...\n"); - CHECK_ERROR_BREAK (console, PowerDown(progress.asOutParam())); + RTPrintf("Powering the VM off...\n"); + CHECK_ERROR_BREAK(console, PowerDown(progress.asOutParam())); - RTPrintf ("Waiting for the VM to power down...\n"); - CHECK_ERROR_BREAK (progress, WaitForCompletion (-1)); + RTPrintf("Waiting for the VM to power down...\n"); + CHECK_ERROR_BREAK(progress, WaitForCompletion(-1)); BOOL completed; - CHECK_ERROR_BREAK (progress, COMGETTER(Completed) (&completed)); - ASSERT (completed); + CHECK_ERROR_BREAK(progress, COMGETTER(Completed)(&completed)); + ASSERT(completed); LONG resultCode; - CHECK_ERROR_BREAK (progress, COMGETTER(ResultCode) (&resultCode)); - if (FAILED (resultCode)) + CHECK_ERROR_BREAK(progress, COMGETTER(ResultCode)(&resultCode)); + if (FAILED(resultCode)) { ProgressErrorInfo info(progress); com::GluePrintErrorInfo(info); } else { - RTPrintf ("VM is powered down.\n"); + RTPrintf("VM is powered down.\n"); } } else - if (!strcmp (operation, "pause")) + if (!strcmp(operation, "pause")) { - RTPrintf ("Pausing the VM...\n"); - CHECK_ERROR_BREAK (console, Pause()); + RTPrintf("Pausing the VM...\n"); + CHECK_ERROR_BREAK(console, Pause()); } else - if (!strcmp (operation, "resume")) + if (!strcmp(operation, "resume")) { - RTPrintf ("Resuming the VM...\n"); - CHECK_ERROR_BREAK (console, Resume()); + RTPrintf("Resuming the VM...\n"); + CHECK_ERROR_BREAK(console, Resume()); } else { - RTPrintf ("Invalid operation!\n"); + RTPrintf("Invalid operation!\n"); } } @@ -199,11 +193,11 @@ int main (int argc, char **argv) CHECK_ERROR(session, UnlockMachine()); } while (0); - RTPrintf ("\n"); + RTPrintf("\n"); com::Shutdown(); - RTPrintf ("tstHeadless FINISHED.\n"); + RTPrintf("tstHeadless FINISHED.\n"); return rc; } |
