VideoRec.cpp revision 3851c4a4d158e0776c6774fc21ed986667f34be3
/* $Id$ */
/** @file
* Encodes the screen content in VPX format.
*/
/*
* Copyright (C) 2012-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* 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_MAIN
#include <iprt/semaphore.h>
#include "EbmlWriter.h"
#include "VideoRec.h"
#define VPX_CODEC_DISABLE_COMPAT 1
#include <vpx/vpx_image.h>
/** Default VPX codec to use */
#define DEFAULTCODEC (vpx_codec_vp8_cx())
/* state to synchronized between threads */
enum
{
VIDREC_UNINITIALIZED = 0,
/* initialized, idle */
VIDREC_IDLE = 1,
/* currently in VideoRecCopyToIntBuf(), delay termination */
VIDREC_COPYING = 2,
/* signal that we are terminating */
};
/* Must be always accessible and therefore cannot be part of VIDEORECCONTEXT */
typedef struct VIDEORECSTREAM
{
/* container context */
/* VPX codec context */
/* VPX configuration */
/* X resolution */
/* Y resolution */
/* X resolution of the last encoded picture */
/* Y resolution of the last encoded picture */
/* current frame number */
/* RGB buffer containing the most recent frame of the framebuffer */
/* YUV buffer the encode function fetches the frame from */
/* VPX image context */
/* true if video recording is enabled */
bool fEnabled;
/* true if the RGB buffer is filled */
bool fRgbFilled;
/* pixel format of the current frame */
/* minimal delay between two frames */
/* time stamp of the last frame we encoded */
/* time stamp of the current frame */
/* encoder deadline */
unsigned int uEncoderDeadline;
typedef struct VIDEORECCONTEXT
{
/* semaphore to signal the encoding worker thread */
/* semaphore required during termination */
/* true if video recording is enabled */
bool fEnabled;
/* worker thread */
/* number of stream contexts */
/* maximal time stamp */
/* maximal file size in MB */
/* video recording stream contexts */
/**
* Iterator class for running through a BGRA32 image buffer and converting
* it to RGB.
*/
class ColorConvBGRA32Iter
{
private:
enum { PIX_SIZE = 4 };
public:
{
mPos = 0;
}
/**
* 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 rc = false;
{
rc = true;
}
return rc;
}
/**
* Skip forward by a certain number of pixels
* @param aPixels how many pixels to skip
*/
{
}
private:
/** Size of the picture buffer */
unsigned mSize;
/** Current position in the picture buffer */
unsigned mPos;
/** Address of the picture buffer */
};
/**
* Iterator class for running through an BGR24 image buffer and converting
* it to RGB.
*/
class ColorConvBGR24Iter
{
private:
enum { PIX_SIZE = 3 };
public:
{
mPos = 0;
}
/**
* 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 rc = false;
{
rc = true;
}
return rc;
}
/**
* Skip forward by a certain number of pixels
* @param aPixels how many pixels to skip
*/
{
}
private:
/** Size of the picture buffer */
unsigned mSize;
/** Current position in the picture buffer */
unsigned mPos;
/** Address of the picture buffer */
};
/**
* Iterator class for running through an BGR565 image buffer and converting
* it to RGB.
*/
class ColorConvBGR565Iter
{
private:
enum { PIX_SIZE = 2 };
public:
{
mPos = 0;
}
/**
* 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 rc = false;
{
rc = true;
}
return rc;
}
/**
* Skip forward by a certain number of pixels
* @param aPixels how many pixels to skip
*/
{
}
private:
/** Size of the picture buffer */
unsigned mSize;
/** Current position in the picture buffer */
unsigned mPos;
/** Address of the picture buffer */
};
/**
* 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>
{
bool rc = true;
unsigned offY = 0;
{
{
if (rc)
{
}
if (rc)
{
}
if (rc)
{
}
if (rc)
{
offY += 2;
++offU;
++offV;
}
}
if (rc)
{
}
}
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>
{
enum { PIX_SIZE = 3 };
bool rc = true;
{
if (rc)
{
}
}
return rc;
}
/**
* Worker thread for all streams.
*
*/
{
for (;;)
{
break;
{
{
if (RT_SUCCESS(rc))
if (RT_FAILURE(rc))
{
static unsigned cErrors = 100;
if (cErrors > 0)
{
cErrors--;
}
}
}
}
}
return VINF_SUCCESS;
}
/**
* VideoRec utility function to create video recording context.
*
* @returns IPRT status code.
* @param ppCtx Video recording context
* @param cScreens Number of screens.
*/
{
PVIDEORECCONTEXT pCtx = (PVIDEORECCONTEXT)RTMemAllocZ(RT_OFFSETOF(VIDEORECCONTEXT, Strm[cScreens]));
{
/* Since we allocate without using standard c++ new mechanism
* it is required to call placement new for correct initialization
* of some members */
}
return VINF_SUCCESS;
}
/**
* VideoRec utility function to initialize video recording context.
*
* @returns IPRT status code.
* @param pCtx Pointer to video recording context to initialize Framebuffer width.
* @param uScreeen Screen number.
* @param strFile File to save the recorded data
* @param uTargetWidth Width of the target image in the video recoriding file (movie)
* @param uTargetHeight Height of the target image in video recording file.
*/
{
/* Play safe: the file must not exist, overwriting is potentially
* hazardous as nothing prevents the user from picking a file name of some
* other important file, causing unintentional data loss. */
if (RT_FAILURE(rc))
{
return rc;
}
if (rcv != VPX_CODEC_OK)
{
return VERR_INVALID_PARAMETER;
}
do {
if (key == "quality")
{
if (value == "realtime")
{
}
else if (value == "good")
{
}
else if (value == "best")
{
}
else
{
}
}
/* target bitrate in kilobits per second */
/* frame width */
/* frame height */
/* 1ms per frame */
/* disable multithreading */
/* Initialize codec */
if (rcv != VPX_CODEC_OK)
{
return VERR_INVALID_PARAMETER;
}
{
return VERR_NO_MEMORY;
}
return VINF_SUCCESS;
}
/**
* VideoRec utility function to close the video recording context.
*
* @param pCtx Pointer to video recording context.
*/
{
if (!pCtx)
return;
for (;;)
{
break;
if (enmState == VIDREC_UNINITIALIZED)
return;
}
if (enmState == VIDREC_COPYING)
{
}
{
{
}
/* explicit deinitilization of Ebml object since it was create using placement new */
}
}
/**
* VideoRec utility function to check if recording is enabled.
*
* @returns true if recording is enabled
* @param pCtx Pointer to video recording context.
*/
{
return ( enmState == VIDREC_IDLE
|| enmState == VIDREC_COPYING);
}
/**
* VideoRec utility function to check if recording engine is ready to accept a new frame
* for the given screen.
*
* @returns true if recording engine is ready
* @param pCtx Pointer to video recording context.
* @param uScreen screen id.
* @param u64TimeStamp current time stamp
*/
{
if (enmState != VIDREC_IDLE)
return false;
return false;
return false;
return false;
return true;
}
/**
* VideoRec utility function to check if the file size has reached
* specified limits (if any).
*
* @returns true if any limit has been reached
* @param pCtx Pointer to video recording context
* @param uScreen screen id
* @param u64TimeStamp current time stamp
*/
{
return false;
return true;
if (pCtx->uMaxFileSize > 0)
{
return true;
}
/* Check for available free disk space */
{
LogRel(("Storage has not enough free space available, stopping video capture\n"));
return true;
}
return false;
}
/**
* VideoRec utility function to encode the source image and write the encoded
* image to target file.
*
* @returns IPRT status code.
* @param pCtx Pointer to video recording context.
* @param uSourceWidth Width of the source image.
* @param uSourceHeight Height of the source image.
*/
{
/* presentation time stamp */
&pStrm->VpxRawImage,
pts /* time stamp */,
0 /* flags */,
if (rcv != VPX_CODEC_OK)
{
return VERR_GENERAL_FAILURE;
}
int rc = VERR_NO_DATA;
for (;;)
{
if (!pkt)
break;
{
case VPX_CODEC_CX_FRAME_PKT:
break;
default:
LogFlow(("Unexpected CODEC Packet.\n"));
break;
}
}
return rc;
}
/**
* VideoRec utility function to convert RGB to YUV.
*
* @returns IPRT status code.
* @param pCtx Pointer to video recording context.
*/
{
switch (pStrm->u32PixelFormat)
{
case VPX_IMG_FMT_RGB32:
LogFlow(("32 bit\n"));
return VERR_GENERAL_FAILURE;
break;
case VPX_IMG_FMT_RGB24:
LogFlow(("24 bit\n"));
return VERR_GENERAL_FAILURE;
break;
case VPX_IMG_FMT_RGB565:
LogFlow(("565 bit\n"));
return VERR_GENERAL_FAILURE;
break;
default:
return VERR_GENERAL_FAILURE;
}
return VINF_SUCCESS;
}
/**
* VideoRec utility function to copy a source image (FrameBuf) to the intermediate
* RGB buffer. This function is executed only once per time.
*
* @thread EMT
*
* @returns IPRT status code.
* @param pCtx Pointer to the video recording context.
* @param uScreen Screen number.
* @param x Starting x coordinate of the source buffer (Framebuffer).
* @param y Starting y coordinate of the source buffer (Framebuffer).
* @param uPixelFormat Pixel Format.
* @param uBitsPerPixel Bits Per Pixel
* @param uBytesPerLine Bytes per source scanlineName.
* @param uSourceWidth Width of the source image (framebuffer).
* @param uSourceHeight Height of the source image (framebuffer).
* @param pu8BufAddr Pointer to source image(framebuffer).
* @param u64TimeStamp Time stamp (milliseconds).
*/
{
/* Do not execute during termination and guard against termination */
return VINF_TRY_AGAIN;
int rc = VINF_SUCCESS;
do
{
{
break;
}
{
break;
}
{
break;
}
uint32_t w = uSourceWidth;
if ((int)w + xDiff + (int)x <= 0) /* nothing visible */
{
break;
}
if ((int)x < -xDiff)
{
w += xDiff + x;
x = -xDiff;
destX = 0;
}
else
uint32_t h = uSourceHeight;
if ((int)h + yDiff + (int)y <= 0) /* nothing visible */
{
break;
}
if ((int)y < -yDiff)
{
h += yDiff + (int)y;
y = -yDiff;
destY = 0;
}
else
{
break;
}
/* Calculate bytes per pixel */
if (uPixelFormat == BitmapFormat_BGR)
{
switch (uBitsPerPixel)
{
case 32:
bpp = 4;
break;
case 24:
bpp = 3;
break;
case 16:
bpp = 2;
break;
default:
break;
}
}
else
/* One of the dimensions of the current frame is smaller than before so
* clear the entire buffer to prevent artifacts from the previous frame */
/* Calculate start offset in source and destination buffers */
/* do the copy */
for (unsigned int i = 0; i < h; i++)
{
/* Overflow check */
offSrc += uBytesPerLine;
}
} while (0);
{
}
return rc;
}