You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
455 lines
16 KiB
455 lines
16 KiB
/* compile using: gcc d3d9_player.c -o d3d9_player.exe -I <path/liblc> -llibvlc -ld3d9 */
|
|
|
|
#include <windows.h>
|
|
#include <windowsx.h>
|
|
#define COBJMACROS
|
|
#include <d3d9.h>
|
|
|
|
#include <vlc/vlc.h>
|
|
|
|
#define SCREEN_WIDTH 900
|
|
#define SCREEN_HEIGHT 900
|
|
#define BORDER_LEFT ( 20)
|
|
#define BORDER_RIGHT (700 + BORDER_LEFT)
|
|
#define BORDER_TOP ( 10)
|
|
#define BORDER_BOTTOM (700 + BORDER_TOP)
|
|
|
|
struct render_context
|
|
{
|
|
HWND hWnd;
|
|
|
|
libvlc_media_player_t *p_mp;
|
|
|
|
IDirect3D9Ex *d3d;
|
|
IDirect3DDevice9 *d3ddev; /* the host app device */
|
|
IDirect3DDevice9 *libvlc_d3d; /* the device where VLC can do its rendering */
|
|
|
|
/* surface we'll use to display on our backbuffer */
|
|
IDirect3DTexture9 *renderTexture;
|
|
HANDLE sharedHandled;
|
|
/* surface that VLC will render to, which is a shared version of renderTexture
|
|
* on libvlc_d3d */
|
|
IDirect3DTexture9 *sharedRenderTexture;
|
|
IDirect3DSurface9 *sharedRenderSurface;
|
|
|
|
/* our swapchain backbuffer */
|
|
IDirect3DSurface9 *backBuffer;
|
|
|
|
IDirect3DVertexBuffer9 *rectangleFVFVertexBuf;
|
|
|
|
CRITICAL_SECTION sizeLock; // the ReportSize callback cannot be called during/after the CleanupDevice_cb is called
|
|
unsigned width, height;
|
|
libvlc_video_output_resize_cb ReportSize;
|
|
void *ReportOpaque;
|
|
};
|
|
|
|
struct CUSTOMVERTEX {FLOAT X, Y, Z, RHW; DWORD COLOR;
|
|
FLOAT tu, tv; /* texture relative coordinates */};
|
|
#define CUSTOMFVF (D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1)
|
|
|
|
/**
|
|
* Callback called when it's time to display the video, in sync with the audio.
|
|
*
|
|
* This is called outside of the UI thread (in the VLC rendering thread).
|
|
*/
|
|
static void Swap(struct render_context *ctx)
|
|
{
|
|
/* finished drawing to our swap surface, now render that surface to the backbuffer */
|
|
IDirect3DDevice9_SetRenderTarget(ctx->d3ddev, 0, ctx->backBuffer);
|
|
|
|
/* clear the backbuffer to orange */
|
|
IDirect3DDevice9_Clear(ctx->d3ddev, 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(255, 120, 0), 1.0f, 0);
|
|
|
|
IDirect3DDevice9_BeginScene(ctx->d3ddev);
|
|
IDirect3DDevice9_SetTexture(ctx->d3ddev, 0, (IDirect3DBaseTexture9*)ctx->renderTexture);
|
|
|
|
IDirect3DDevice9_SetStreamSource(ctx->d3ddev, 0, ctx->rectangleFVFVertexBuf, 0, sizeof(struct CUSTOMVERTEX));
|
|
IDirect3DDevice9_SetFVF(ctx->d3ddev, CUSTOMFVF);
|
|
IDirect3DDevice9_DrawPrimitive(ctx->d3ddev, D3DPT_TRIANGLEFAN, 0, 2);
|
|
IDirect3DDevice9_EndScene(ctx->d3ddev);
|
|
|
|
IDirect3DDevice9_Present(ctx->d3ddev, NULL, NULL, ctx->hWnd, NULL);
|
|
}
|
|
|
|
/**
|
|
* Callback called to tell the size of the surface that should be available
|
|
* to VLC to draw into.
|
|
*
|
|
* This is called outside of the UI thread (not the VLC rendering thread).
|
|
*/
|
|
static bool Resize(struct render_context *ctx, unsigned width, unsigned height,
|
|
IDirect3DDevice9 *vlc_device,
|
|
libvlc_video_output_cfg_t *out)
|
|
{
|
|
HRESULT hr;
|
|
D3DDISPLAYMODE d3ddm;
|
|
|
|
hr = IDirect3D9Ex_GetAdapterDisplayMode(ctx->d3d, 0, &d3ddm);
|
|
|
|
/* create the output surface VLC will write to */
|
|
if (ctx->renderTexture)
|
|
{
|
|
IDirect3DTexture9_Release(ctx->renderTexture);
|
|
ctx->renderTexture = NULL;
|
|
ctx->sharedHandled = NULL;
|
|
}
|
|
if (ctx->sharedRenderTexture)
|
|
{
|
|
IDirect3DTexture9_Release(ctx->sharedRenderTexture);
|
|
ctx->sharedRenderTexture = NULL;
|
|
}
|
|
if (ctx->sharedRenderSurface)
|
|
{
|
|
IDirect3DSurface9_Release(ctx->sharedRenderSurface);
|
|
ctx->sharedRenderSurface = NULL;
|
|
}
|
|
/* the device to use may have changed */
|
|
if (ctx->libvlc_d3d)
|
|
{
|
|
IDirect3DDevice9_Release(ctx->libvlc_d3d);
|
|
}
|
|
ctx->libvlc_d3d = vlc_device;
|
|
IDirect3DDevice9_AddRef(ctx->libvlc_d3d);
|
|
|
|
/* texture we can use on our device */
|
|
hr = IDirect3DDevice9_CreateTexture(ctx->d3ddev, width, height, 1, D3DUSAGE_RENDERTARGET,
|
|
d3ddm.Format,
|
|
D3DPOOL_DEFAULT,
|
|
&ctx->renderTexture,
|
|
&ctx->sharedHandled);
|
|
if (FAILED(hr))
|
|
return false;
|
|
|
|
/* texture/surface that is set as the render target for libvlc on its device */
|
|
hr = IDirect3DDevice9_CreateTexture(ctx->libvlc_d3d, width, height, 1, D3DUSAGE_RENDERTARGET,
|
|
d3ddm.Format,
|
|
D3DPOOL_DEFAULT,
|
|
&ctx->sharedRenderTexture,
|
|
&ctx->sharedHandled);
|
|
if (FAILED(hr))
|
|
return false;
|
|
|
|
hr = IDirect3DTexture9_GetSurfaceLevel(ctx->sharedRenderTexture, 0, &ctx->sharedRenderSurface);
|
|
if (FAILED(hr))
|
|
return false;
|
|
|
|
hr = IDirect3DDevice9_SetRenderTarget(ctx->libvlc_d3d, 0, ctx->sharedRenderSurface);
|
|
if (FAILED(hr)) return false;
|
|
|
|
out->d3d9_format = d3ddm.Format;
|
|
out->full_range = true;
|
|
out->colorspace = libvlc_video_colorspace_BT709;
|
|
out->primaries = libvlc_video_primaries_BT709;
|
|
out->transfer = libvlc_video_transfer_func_SRGB;
|
|
out->orientation = libvlc_video_orient_top_left;
|
|
|
|
return true;
|
|
}
|
|
|
|
static void init_direct3d(struct render_context *ctx, HWND hWnd)
|
|
{
|
|
ctx->hWnd = hWnd;
|
|
HRESULT hr = Direct3DCreate9Ex(D3D_SDK_VERSION, &ctx->d3d);
|
|
|
|
D3DPRESENT_PARAMETERS d3dpp = { 0 };
|
|
d3dpp.Windowed = TRUE;
|
|
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
|
|
d3dpp.hDeviceWindow = hWnd;
|
|
|
|
IDirect3D9Ex_CreateDevice(ctx->d3d, D3DADAPTER_DEFAULT,
|
|
D3DDEVTYPE_HAL,
|
|
NULL,
|
|
D3DCREATE_MULTITHREADED| D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE,
|
|
&d3dpp,
|
|
&ctx->d3ddev);
|
|
|
|
IDirect3DDevice9_GetRenderTarget(ctx->d3ddev, 0, &ctx->backBuffer);
|
|
|
|
struct CUSTOMVERTEX rectangleVertices[] =
|
|
{
|
|
{ BORDER_LEFT, BORDER_TOP, 0.0f, 1.0f, D3DCOLOR_ARGB(255, 255, 255, 255), 0.0f, 0.0f },
|
|
{ BORDER_RIGHT, BORDER_TOP, 0.0f, 1.0f, D3DCOLOR_ARGB(255, 255, 255, 255), 1.0f, 0.0f },
|
|
{ BORDER_RIGHT, BORDER_BOTTOM, 0.0f, 1.0f, D3DCOLOR_ARGB(255, 255, 255, 255), 1.0f, 1.0f },
|
|
{ BORDER_LEFT, BORDER_BOTTOM, 0.0f, 1.0f, D3DCOLOR_ARGB(255, 255, 255, 255), 0.0f, 1.0f },
|
|
};
|
|
IDirect3DDevice9Ex_CreateVertexBuffer(ctx->d3ddev, sizeof(rectangleVertices),
|
|
D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY,
|
|
CUSTOMFVF,
|
|
D3DPOOL_DEFAULT,
|
|
&ctx->rectangleFVFVertexBuf,
|
|
NULL);
|
|
|
|
LPVOID pVoid;
|
|
IDirect3DVertexBuffer9_Lock(ctx->rectangleFVFVertexBuf, 0, 0, (void**)&pVoid, 0);
|
|
memcpy(pVoid, rectangleVertices, sizeof(rectangleVertices));
|
|
IDirect3DVertexBuffer9_Unlock(ctx->rectangleFVFVertexBuf);
|
|
}
|
|
|
|
static void release_direct3d(struct render_context *ctx)
|
|
{
|
|
if (ctx->backBuffer)
|
|
IDirect3DSurface9_Release(ctx->backBuffer);
|
|
if (ctx->renderTexture)
|
|
IDirect3DTexture9_Release(ctx->renderTexture);
|
|
if (ctx->sharedRenderSurface)
|
|
IDirect3DSurface9_Release(ctx->sharedRenderSurface);
|
|
if (ctx->sharedRenderTexture)
|
|
IDirect3DTexture9_Release(ctx->sharedRenderTexture);
|
|
if (ctx->rectangleFVFVertexBuf)
|
|
IDirect3DVertexBuffer9_Release(ctx->rectangleFVFVertexBuf);
|
|
if (ctx->libvlc_d3d)
|
|
IDirect3DDevice9_Release(ctx->libvlc_d3d);
|
|
IDirect3DDevice9_Release(ctx->d3ddev);
|
|
IDirect3D9_Release(ctx->d3d);
|
|
}
|
|
|
|
static bool SetupDevice_cb( void **opaque, const libvlc_video_setup_device_cfg_t *cfg, libvlc_video_setup_device_info_t *out )
|
|
{
|
|
struct render_context *ctx = *opaque;
|
|
out->d3d9.device = ctx->d3d;
|
|
out->d3d9.adapter = D3DADAPTER_DEFAULT;
|
|
return true;
|
|
}
|
|
|
|
static void CleanupDevice_cb( void *opaque )
|
|
{
|
|
/* here we can release all things Direct3D9 for good (if playing only one file) */
|
|
struct render_context *ctx = opaque;
|
|
if (ctx->libvlc_d3d)
|
|
{
|
|
IDirect3DDevice9_Release(ctx->libvlc_d3d);
|
|
ctx->libvlc_d3d = NULL;
|
|
}
|
|
}
|
|
|
|
static void SetReport_cb( void *opaque,
|
|
libvlc_video_output_resize_cb report_size_change,
|
|
libvlc_video_output_mouse_move_cb report_mouse_move,
|
|
libvlc_video_output_mouse_press_cb report_mouse_press,
|
|
libvlc_video_output_mouse_release_cb report_mouse_release,
|
|
void *report_opaque )
|
|
{
|
|
struct render_context *ctx = opaque;
|
|
EnterCriticalSection(&ctx->sizeLock);
|
|
ctx->ReportSize = report_size_change;
|
|
ctx->ReportOpaque = report_opaque;
|
|
|
|
if (ctx->ReportSize != NULL)
|
|
{
|
|
/* report our initial size */
|
|
ctx->ReportSize(ctx->ReportOpaque, ctx->width, ctx->height);
|
|
}
|
|
LeaveCriticalSection(&ctx->sizeLock);
|
|
}
|
|
|
|
static bool UpdateOutput_cb( void *opaque, const libvlc_video_render_cfg_t *cfg, libvlc_video_output_cfg_t *out )
|
|
{
|
|
struct render_context *ctx = opaque;
|
|
return Resize(ctx, cfg->width, cfg->height, (IDirect3DDevice9*)cfg->device, out);
|
|
}
|
|
|
|
static void Swap_cb( void* opaque )
|
|
{
|
|
struct render_context *ctx = opaque;
|
|
Swap( ctx );
|
|
}
|
|
|
|
/**
|
|
* Callback called just before VLC starts/finishes drawing the video.
|
|
*
|
|
* Set the surface VLC will render to (could be the backbuffer if nothing else
|
|
* needs to be displayed). And then call BeginScene().
|
|
*
|
|
* This is called outside of the UI thread (in the VLC rendering thread).
|
|
*/
|
|
static bool StartRendering_cb( void *opaque, bool enter )
|
|
{
|
|
struct render_context *ctx = opaque;
|
|
if ( enter )
|
|
{
|
|
/* we already set the RenderTarget on the IDirect3DDevice9 */
|
|
return true;
|
|
}
|
|
|
|
/* VLC has finished preparing drawning on our surface, we need do the drawing now
|
|
so the surface is finished rendering when Swap() is called to do our own
|
|
rendering */
|
|
IDirect3DDevice9_Present(ctx->libvlc_d3d, NULL, NULL, NULL, NULL);
|
|
return true;
|
|
}
|
|
|
|
static const char *AspectRatio = NULL;
|
|
|
|
static LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
if( message == WM_CREATE )
|
|
{
|
|
/* Store p_mp for future use */
|
|
CREATESTRUCT *c = (CREATESTRUCT *)lParam;
|
|
SetWindowLongPtr( hWnd, GWLP_USERDATA, (LONG_PTR)c->lpCreateParams );
|
|
return 0;
|
|
}
|
|
|
|
LONG_PTR p_user_data = GetWindowLongPtr( hWnd, GWLP_USERDATA );
|
|
if( p_user_data == 0 )
|
|
return DefWindowProc(hWnd, message, wParam, lParam);
|
|
struct render_context *ctx = (struct render_context *)p_user_data;
|
|
|
|
switch(message)
|
|
{
|
|
case WM_SIZE:
|
|
{
|
|
/* tell libvlc that our size has changed */
|
|
ctx->width = (BORDER_RIGHT - BORDER_LEFT) * LOWORD(lParam) / SCREEN_WIDTH; /* remove the orange part ! */
|
|
ctx->height = (BORDER_BOTTOM - BORDER_TOP) * HIWORD(lParam) / SCREEN_HEIGHT;
|
|
EnterCriticalSection(&ctx->sizeLock);
|
|
if (ctx->ReportSize != NULL)
|
|
ctx->ReportSize(ctx->ReportOpaque, ctx->width, ctx->height);
|
|
LeaveCriticalSection(&ctx->sizeLock);
|
|
}
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
{
|
|
PostQuitMessage(0);
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case WM_KEYDOWN:
|
|
case WM_SYSKEYDOWN:
|
|
{
|
|
int key = tolower( MapVirtualKey( (UINT)wParam, 2 ) );
|
|
if (key == 'a')
|
|
{
|
|
if (AspectRatio == NULL)
|
|
AspectRatio = "16:10";
|
|
else if (strcmp(AspectRatio,"16:10")==0)
|
|
AspectRatio = "16:9";
|
|
else if (strcmp(AspectRatio,"16:9")==0)
|
|
AspectRatio = "4:3";
|
|
else if (strcmp(AspectRatio,"4:3")==0)
|
|
AspectRatio = "185:100";
|
|
else if (strcmp(AspectRatio,"185:100")==0)
|
|
AspectRatio = "221:100";
|
|
else if (strcmp(AspectRatio,"221:100")==0)
|
|
AspectRatio = "235:100";
|
|
else if (strcmp(AspectRatio,"235:100")==0)
|
|
AspectRatio = "239:100";
|
|
else if (strcmp(AspectRatio,"239:100")==0)
|
|
AspectRatio = "5:3";
|
|
else if (strcmp(AspectRatio,"5:3")==0)
|
|
AspectRatio = "5:4";
|
|
else if (strcmp(AspectRatio,"5:4")==0)
|
|
AspectRatio = "1:1";
|
|
else if (strcmp(AspectRatio,"1:1")==0)
|
|
AspectRatio = "fill";
|
|
else if (strcmp(AspectRatio,"fill")==0)
|
|
AspectRatio = NULL;
|
|
libvlc_video_set_aspect_ratio( ctx->p_mp, AspectRatio );
|
|
}
|
|
break;
|
|
}
|
|
default: break;
|
|
}
|
|
|
|
return DefWindowProc (hWnd, message, wParam, lParam);
|
|
}
|
|
|
|
int WINAPI WinMain(HINSTANCE hInstance,
|
|
HINSTANCE hPrevInstance,
|
|
LPSTR lpCmdLine,
|
|
int nCmdShow)
|
|
{
|
|
HWND hWnd;
|
|
WNDCLASSEX wc;
|
|
struct render_context Context = { 0 };
|
|
char *file_path;
|
|
libvlc_instance_t *p_libvlc;
|
|
libvlc_media_t *p_media;
|
|
(void)hPrevInstance;
|
|
|
|
/* remove "" around the given path */
|
|
if (lpCmdLine[0] == '"')
|
|
{
|
|
file_path = _strdup( lpCmdLine+1 );
|
|
if (file_path[strlen(file_path)-1] == '"')
|
|
file_path[strlen(file_path)-1] = '\0';
|
|
}
|
|
else
|
|
file_path = _strdup( lpCmdLine );
|
|
|
|
p_libvlc = libvlc_new( 0, NULL );
|
|
p_media = libvlc_media_new_path( file_path );
|
|
free( file_path );
|
|
Context.p_mp = libvlc_media_player_new_from_media( p_libvlc, p_media );
|
|
|
|
InitializeCriticalSection(&Context.sizeLock);
|
|
|
|
ZeroMemory(&wc, sizeof(WNDCLASSEX));
|
|
|
|
wc.cbSize = sizeof(WNDCLASSEX);
|
|
wc.style = CS_HREDRAW | CS_VREDRAW;
|
|
wc.lpfnWndProc = WindowProc;
|
|
wc.hInstance = hInstance;
|
|
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
|
|
wc.lpszClassName = TEXT("WindowClass");
|
|
|
|
RegisterClassEx(&wc);
|
|
|
|
RECT wr = {0, 0, SCREEN_WIDTH, SCREEN_HEIGHT};
|
|
AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE);
|
|
|
|
hWnd = CreateWindowEx(0,
|
|
TEXT("WindowClass"),
|
|
TEXT("libvlc Demo app"),
|
|
WS_OVERLAPPEDWINDOW,
|
|
CW_USEDEFAULT, CW_USEDEFAULT,
|
|
wr.right - wr.left,
|
|
wr.bottom - wr.top,
|
|
NULL,
|
|
NULL,
|
|
hInstance,
|
|
&Context);
|
|
|
|
ShowWindow(hWnd, nCmdShow);
|
|
|
|
init_direct3d(&Context, hWnd);
|
|
|
|
// DON'T use with callbacks libvlc_media_player_set_hwnd(p_mp, hWnd);
|
|
|
|
/* Tell VLC to render into our D3D9 environment */
|
|
libvlc_video_set_output_callbacks( Context.p_mp, libvlc_video_engine_d3d9,
|
|
SetupDevice_cb, CleanupDevice_cb, SetReport_cb,
|
|
UpdateOutput_cb, Swap_cb, StartRendering_cb,
|
|
NULL, NULL, NULL,
|
|
&Context );
|
|
|
|
libvlc_media_player_play( Context.p_mp );
|
|
|
|
MSG msg;
|
|
|
|
while(TRUE)
|
|
{
|
|
while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
|
|
{
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
|
|
if(msg.message == WM_QUIT)
|
|
break;
|
|
}
|
|
|
|
libvlc_media_player_stop_async( Context.p_mp );
|
|
|
|
libvlc_media_player_release( Context.p_mp );
|
|
libvlc_media_release( p_media );
|
|
libvlc_release( p_libvlc );
|
|
|
|
DeleteCriticalSection(&Context.sizeLock);
|
|
release_direct3d(&Context);
|
|
|
|
return (int)msg.wParam;
|
|
}
|
|
|