#include "libavcodec/avcodec.h"
#include "libswresample/swresample.h"
#include "libswscale/swscale.h"
enum ColorFormat {
RGB565,
BGR565,
RGBA8888,
BGRA8888
};
class VideoScaleContext {
public:
AVPicture outputPic1;
AVPicture outputPic2;
AVPicture outputPic3;
VideoScaleContext() {
//img_convert_init();
videoScaleContext2 = 0;
outputPic1.data[0] = 0;
outputPic2.data[0] = 0;
outputPic3.data[0] = 0;
}
virtual ~VideoScaleContext() {
free();
}
void free() {
if ( videoScaleContext2 )
sws_freeContext(videoScaleContext2);
videoScaleContext2 = 0;
if ( outputPic1.data[0] )
avpicture_free(&outputPic1);
outputPic1.data[0] = 0;
if ( outputPic2.data[0] )
avpicture_free(&outputPic2);
outputPic2.data[0] = 0;
if ( outputPic3.data[0] )
avpicture_free(&outputPic3);
outputPic3.data[0] = 0;
}
void init() {
scaleContextDepth = -1;
scaleContextInputWidth = -1;
scaleContextInputHeight = -1;
scaleContextPicture1Width = -1;
scaleContextPicture2Width = -1;
scaleContextOutputWidth = -1;
scaleContextOutputHeight = -1;
scaleContextLineStride = -1;
}
bool configure(int w, int h, int outW, int outH, AVFrame *picture, int lineStride, int fmt, ColorFormat outFmt ) {
int colorMode = -1;
switch ( outFmt ) {
case RGB565: colorMode = AV_PIX_FMT_RGB565; break;
case BGR565: colorMode = AV_PIX_FMT_RGB565; break;
case RGBA8888: colorMode = AV_PIX_FMT_RGB32_1; break;
case BGRA8888: colorMode = AV_PIX_FMT_RGB32_1; break;
};
scaleContextFormat = fmt;
scaleContextDepth = colorMode;
if ( scaleContextInputWidth != w || scaleContextInputHeight != h
|| scaleContextOutputWidth != outW || scaleContextOutputHeight != outH ) {
scaleContextInputWidth = w;
scaleContextInputHeight = h;
scaleContextOutputWidth = outW;
scaleContextOutputHeight = outH;
scaleContextLineStride = lineStride;
free();
videoScaleContext2 = sws_getContext(w, h, AV_PIX_FMT_RGB32_1, outW, outH, (AVPixelFormat)colorMode, 0, nullptr, nullptr, nullptr);
if ( !videoScaleContext2 )
return false;
if ( avpicture_alloc(&outputPic1, AV_PIX_FMT_YUV420P, scaleContextOutputWidth, scaleContextOutputHeight) < 0 )
return false;
if ( avpicture_alloc(&outputPic2, (AVPixelFormat)scaleContextDepth, scaleContextOutputWidth, scaleContextOutputHeight) < 0 )
return false;
if ( avpicture_alloc(&outputPic3, AV_PIX_FMT_YUV420P, scaleContextOutputWidth, scaleContextOutputHeight) < 0 )
return false;
}
return true;
}
void convert(uint8_t *output, AVFrame *picture) {
if ( !videoScaleContext2 || !picture || !outputPic1.data[0] || !outputPic2.data[0] )
return;
// XXXXXXXXX This sucks ATM, converts to YUV420P, scales, then converts to output format
// first conversion needed because img_resample assumes YUV420P, doesn't seem to
// behave with packed image formats
img_convert(&outputPic1, AV_PIX_FMT_YUV420P, (AVPicture*)picture, scaleContextFormat, scaleContextInputWidth, scaleContextInputHeight);
img_resample(videoScaleContext2, &outputPic3, &outputPic1);
img_convert(&outputPic2, scaleContextDepth, &outputPic3, AV_PIX_FMT_YUV420P, scaleContextOutputWidth, scaleContextOutputHeight);
sws_scale(videoScaleContext2, picture->buf[0]->data const uint8_t *const srcSlice[],
const int srcStride[], int srcSliceY, int srcSliceH,
uint8_t *const dst[], const int dstStride[]);
//img_resample(videoScaleContext2, &outputPic1, (AVPicture*)picture);
//img_convert(&outputPic2, scaleContextDepth, &outputPic1, scaleContextFormat, scaleContextOutputWidth, scaleContextOutputHeight);
int offset = 0;
for ( int i = 0; i < scaleContextOutputHeight; i++ ) {
memcpy( output, outputPic2.data[0] + offset, outputPic2.linesize[0] );
output += scaleContextLineStride;
offset += outputPic2.linesize[0];
}
}
private:
struct SwsContext *videoScaleContext2;
int scaleContextDepth;
int scaleContextInputWidth;
int scaleContextInputHeight;
int scaleContextPicture1Width;
int scaleContextPicture2Width;
int scaleContextOutputWidth;
int scaleContextOutputHeight;
int scaleContextLineStride;
int scaleContextFormat;
};
#ifdef _WIN32
#include <windows.h>
#include <ddraw.h>
enum display_method { USE_WINDOWS_API, USE_DIRECT_DRAW };
// Generic Global Variables
HWND MainWnd_hWnd;
HINSTANCE g_hInstance;
HDC hdc;
HPALETTE oldhpal;
RECT r;
// DirectDraw specific Variables
LPDIRECTDRAW lpDD = NULL;
LPDIRECTDRAWSURFACE lpDDSPrimary = NULL; // DirectDraw primary surface
LPDIRECTDRAWSURFACE lpDDSOne = NULL; // Offscreen surface #1
DDSURFACEDESC ddsd;
// Standard Windows API specific Variables
HDC hdcMemory;
HBITMAP hbmpMyBitmap, hbmpOld;
// User decided variables
int _method__; // API or DirectDraw
int _do_full_; // Full screen
int _do_flip_; // Page flipping
int _double__; // Double window size
int _on_top__; // Always on top
int _rate____; // Calculate frame rate
// Interface Variables
unsigned char *DoubleBuffer;
// Resolution Variables
int width;
int height;
int bytes_per_pixel;
#define fatal_error(message) _fatal_error(message, __FILE__, __LINE__)
void _fatal_error(char *message, char *file, int line);
// Fatal error handler (use the macro version in header file)
void _fatal_error(char *message, char *file, int line)
{
char error_message[1024];
sprintf(error_message, "%s, in %s at line %d", message, file, line);
puts(error_message);
MessageBox(NULL, error_message, "Fatal Error!", MB_OK);
exit(EXIT_FAILURE);
}
class MSWindowsWindow {
};
class DirectDrawWindow {
};
// Flip/Blt Doublebuffer to screen (updating &doublebuffer if necassery)
void MyShowDoubleBuffer(void)
{
if (_method__ == USE_DIRECT_DRAW) {
if (_do_flip_) {
// Page flipped DirectDraw
if (IDirectDrawSurface_Lock(lpDDSPrimary, NULL, &ddsd, 0, NULL) != DD_OK)
fatal_error("Error Locking a DirectDraw Surface (DDSurfaceLock)");
DoubleBuffer = (unsigned char *)ddsd.lpSurface;
if (IDirectDrawSurface_Unlock(lpDDSPrimary, NULL) != DD_OK)
fatal_error("Error Unlocking a DirectDraw Surface (DDSurfaceUnlock)");
if(IDirectDrawSurface_Flip(lpDDSPrimary,lpDDSOne,0)==DDERR_SURFACELOST) {
IDirectDrawSurface_Restore(lpDDSPrimary);
IDirectDrawSurface_Restore(lpDDSOne);
}
} else {
// Non Page flipped DirectDraw
POINT pt;
HDC hdcx;
ShowCursor(0);
if (_do_full_) {
if(IDirectDrawSurface_BltFast(lpDDSPrimary,0,0,lpDDSOne,&r,DDBLTFAST_NOCOLORKEY)==DDERR_SURFACELOST)
IDirectDrawSurface_Restore(lpDDSPrimary),
IDirectDrawSurface_Restore(lpDDSOne);
} else {
GetDCOrgEx(hdcx = GetDC(MainWnd_hWnd), &pt);
ReleaseDC(MainWnd_hWnd, hdcx);
IDirectDrawSurface_BltFast(lpDDSPrimary,pt.x,pt.y,lpDDSOne,&r,DDBLTFAST_NOCOLORKEY);
}
ShowCursor(1);
}
} else {
// Using Windows API
// BltBlt from memory to screen using standard windows API
SetBitmapBits(hbmpMyBitmap, width*height*bytes_per_pixel, DoubleBuffer);
if (_double__)
StretchBlt(hdc, 0, 0, 2*width, 2*height, hdcMemory, 0, 0, width, height, SRCCOPY);
else
BitBlt(hdc, 0, 0, width, height, hdcMemory, 0, 0, SRCCOPY);
}
}
int done = 0;
// Shut down application
void MyCloseWindow(void)
{
if (done == 0)
{
done = 1;
if (_method__ == USE_DIRECT_DRAW) {
ShowCursor(1);
if(lpDD != NULL) {
if(lpDDSPrimary != NULL)
IDirectDrawSurface_Release(lpDDSPrimary);
if (!_do_flip_)
if(lpDDSOne != NULL)
IDirectDrawSurface_Release(lpDDSOne);
IDirectDrawSurface_Release(lpDD);
}
lpDD = NULL;
lpDDSOne = NULL;
lpDDSPrimary = NULL;
} else {
/* release buffer */
free(DoubleBuffer);
// Release interfaces to BitBlt functionality
SelectObject(hdcMemory, hbmpOld);
DeleteDC(hdcMemory);
}
ReleaseDC(MainWnd_hWnd, hdc);
PostQuitMessage(0);
}
}
// Do the standard windows message loop thing
void MyDoMessageLoop(void)
{
MSG msg;
while(GetMessage(&msg, NULL, 0, 0 ))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
exit(msg.wParam);
}
void ProcessMessages()
{
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, 1 ))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
if ( iMessage == WM_SIZE ) {
width = lParam & 0xFFFF;
height = (lParam >> 16) + 4;
printf("resize: %i x %i (%i %i)\n", width, height, (uint)lParam & 0xFFFF, lParam >> 16);
}
return DefWindowProc(hWnd, iMessage, wParam, lParam);
}
// Setup the application
void MyCreateWindow()
{
DDSCAPS ddscaps;
WNDCLASS wndclass; // Structure used to register Windows class.
HINSTANCE hInstance = 0;//g_hInstance;
wndclass.style = 0;
wndclass.lpfnWndProc = WndProc;//DefWindowProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(hInstance, "3D-MAGIC");
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = "DDraw Renderer Module";
if (!RegisterClass(&wndclass))
fatal_error("Error Registering Window");
if (!(MainWnd_hWnd = CreateWindow("DDraw Renderer Module", "Media Player",
WS_OVERLAPPEDWINDOW | WS_VISIBLE, /* Window style. */
CW_USEDEFAULT, CW_USEDEFAULT, /* Default position. */
// take into account window border, and create a larger
// window if stretching to double the window size.
(_double__) ? 2*width + 10 : width + 10,
(_double__) ? 2*height + 30 : height + 30,
NULL, NULL, hInstance, NULL)))
fatal_error("Error Creating Window");
hdc = GetDC(MainWnd_hWnd);
r.left = 0;
r.top = 0;
r.right = width;
r.bottom = height;
if (_method__ == USE_DIRECT_DRAW)
{
if (DirectDrawCreate(NULL, &lpDD, NULL) != DD_OK)
fatal_error("Error initialising DirectDraw (DDCreate)");
if (_do_full_)
{
if (IDirectDraw_SetCooperativeLevel(lpDD, MainWnd_hWnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWMODEX) != DD_OK)
fatal_error("Error initialising DirectDraw (DDSetCoopLevel)");
if (IDirectDraw_SetDisplayMode(lpDD, width, height, 8*bytes_per_pixel) != DD_OK)
fatal_error("Error initialising DirectDraw (DDSetDisplayMode)");
}
else
{
if (IDirectDraw_SetCooperativeLevel(lpDD, MainWnd_hWnd, /* DDSCL_EXCLUSIVE | */ DDSCL_NORMAL) != DD_OK)
fatal_error("Error initialising DirectDraw (DDSetCoopLevel)");
_do_flip_ = 0;
bytes_per_pixel = GetDeviceCaps(hdc, BITSPIXEL) >> 3;
}
if (_do_flip_)
{
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX;
ddsd.dwBackBufferCount = 1;
if (IDirectDraw_CreateSurface(lpDD, &ddsd, &lpDDSPrimary, NULL) != DD_OK)
fatal_error("Error Creating a Primiary DirectDraw Surface (DDCreateSurface)");
// Get the pointer to the back buffer
ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
if (IDirectDrawSurface_GetAttachedSurface(lpDDSPrimary, &ddscaps, &lpDDSOne) != DD_OK)
fatal_error("Error Creating a Primiary DirectDraw Surface (DDCreateSurface)");
}
else
{
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags=DDSD_CAPS;
ddsd.ddsCaps.dwCaps=DDSCAPS_PRIMARYSURFACE;
if (IDirectDraw_CreateSurface(lpDD,&ddsd,&lpDDSPrimary,NULL) != DD_OK)
fatal_error("Error Creating a Primiary DirectDraw Surface (DDCreateSurface)");
ddsd.dwSize=sizeof(ddsd);
ddsd.dwFlags=DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH;
ddsd.ddsCaps.dwCaps=DDSCAPS_OFFSCREENPLAIN;
ddsd.dwWidth=width;
ddsd.dwHeight=height;
if (IDirectDraw_CreateSurface(lpDD,&ddsd,&lpDDSOne,NULL) != DD_OK)
fatal_error("Error Creating a DirectDraw Off Screen Surface (DDCreateSurface)");
if (lpDDSOne == NULL)
fatal_error("Error Creating a DirectDraw Off Screen Surface (DDCreateSurface)");
}
// Get pointer to buffer surface
if (IDirectDrawSurface_Lock(lpDDSOne, NULL, &ddsd, 0, NULL) != DD_OK)
fatal_error("Error Locking a DirectDraw Surface (DDSurfaceLock)");
DoubleBuffer = (unsigned char *)ddsd.lpSurface;
if (IDirectDrawSurface_Unlock(lpDDSOne, NULL) != DD_OK)
fatal_error("Error Unlocking a DirectDraw Surface (DDSurfaceUnlock)");
if (_do_flip_)
ShowCursor(0);
}
else /* Windows API */
{
bytes_per_pixel = GetDeviceCaps(hdc, BITSPIXEL) >> 3;
DoubleBuffer = (unsigned char *)malloc(width*height*bytes_per_pixel);
if (DoubleBuffer == NULL)
fatal_error("Unable to allocate enough main memory for an offscreen Buffer");
// Initialise interface to BitBlt function
hdcMemory = CreateCompatibleDC(hdc);
hbmpMyBitmap = CreateCompatibleBitmap(hdc, width, height);
hbmpOld = (HBITMAP)SelectObject(hdcMemory, hbmpMyBitmap);
{
HPALETTE hpal;
PALETTEENTRY mypal[64*3+16];
int i;
LOGPALETTE *plgpl;
plgpl = (LOGPALETTE*) LocalAlloc(LPTR,
sizeof(LOGPALETTE) + (16+3*64)*sizeof(PALETTEENTRY));
plgpl->palNumEntries = 64*3+16;
plgpl->palVersion = 0x300;
for (i = 16; i < 64+16; i++)
{
plgpl->palPalEntry[i].peRed = mypal[i].peRed = LOBYTE(i << 2);
plgpl->palPalEntry[i].peGreen = mypal[i].peGreen = 0;
plgpl->palPalEntry[i].peBlue = mypal[i].peBlue = 0;
plgpl->palPalEntry[i].peFlags = mypal[i].peFlags = PC_RESERVED;
plgpl->palPalEntry[i+64].peRed = mypal[i+64].peRed = 0;
plgpl->palPalEntry[i+64].peGreen = mypal[i+64].peGreen = LOBYTE(i << 2);
plgpl->palPalEntry[i+64].peBlue = mypal[i+64].peBlue = 0;
plgpl->palPalEntry[i+64].peFlags = mypal[i+64].peFlags = PC_RESERVED;
plgpl->palPalEntry[i+128].peRed = mypal[i+128].peRed = 0;
plgpl->palPalEntry[i+128].peGreen = mypal[i+128].peGreen = 0;
plgpl->palPalEntry[i+128].peBlue = mypal[i+128].peBlue = LOBYTE(i << 2);
plgpl->palPalEntry[i+128].peFlags = mypal[i+128].peFlags = PC_RESERVED;
}
hpal = CreatePalette(plgpl);
oldhpal = SelectPalette(hdc, hpal, FALSE);
RealizePalette(hdc);
}
}
}
class DirectDrawRenderer : public SimpleModule {
public:
DirectDrawRenderer() {
width = 320 + 32;
height = 240;
_method__ = 0; // API or DirectDraw
_do_full_ = 0; // Full screen
_do_flip_ = 0; // Page flipping
_double__ = 0; // Double window size
_on_top__ = 0; // Always on top
_rate____ = 0; // Calculate frame rate
}
void init() {
MyCreateWindow();
}
void process( const Frame &f ) {
const Frame *frame = &f;
if ( frame && frame->refcount() ) {
//printf("width: %i height: %i\n", width, height);
free(DoubleBuffer);
SelectObject(hdcMemory, hbmpOld);
DeleteDC((HDC)hbmpMyBitmap);
//DeleteDC(hdcMemory);
DoubleBuffer = (unsigned char *)malloc(width*height*bytes_per_pixel);
if (DoubleBuffer == NULL)
fatal_error("Unable to allocate enough main memory for an offscreen Buffer");
// Initialise interface to BitBlt function
hbmpMyBitmap = CreateCompatibleBitmap(hdc, width, height);
hbmpOld = (HBITMAP)SelectObject(hdcMemory, hbmpMyBitmap);
YUVFrame *picture = (YUVFrame *)frame->data();
if (!videoScaleContext.configure(picture->width, picture->height, width, height,
picture->pic, width * 4, picture->fmt, RGBA8888))
return;
videoScaleContext.convert(DoubleBuffer, picture->pic);
MyShowDoubleBuffer();
frame->deref();
}
}
const char *name() { return "YUV Renderer"; }
Format inputFormat() { return "FRAME_ID_YUV_VIDEO_FRAME"; }
Format outputFormat() { return "FRAME_ID_RENDERED_VIDEO"; }
bool isBlocking() { return true; }
private:
VideoScaleContext videoScaleContext;
};
#endif // _WIN32