Newer
Older
Import / research / pipeline / Modules / DirectDrawRenderer.cpp
#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