wine-wine/dlls/quartz/videorenderer.c

547 lines
17 KiB
C

/*
* Video Renderer (Fullscreen and Windowed using Direct Draw)
*
* Copyright 2004 Christian Costa
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "quartz_private.h"
#include "uuids.h"
#include "vfwmsgs.h"
#include "amvideo.h"
#include "windef.h"
#include "winbase.h"
#include "dshow.h"
#include "evcode.h"
#include "strmif.h"
#include "ddraw.h"
#include "dvdmedia.h"
#include <assert.h>
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(quartz);
struct video_renderer
{
struct strmbase_renderer renderer;
struct video_window window;
IOverlay IOverlay_iface;
LONG VideoWidth;
LONG VideoHeight;
LONG FullScreenMode;
DWORD saved_style;
HANDLE run_event;
IMediaSample *current_sample;
};
static inline struct video_renderer *impl_from_video_window(struct video_window *iface)
{
return CONTAINING_RECORD(iface, struct video_renderer, window);
}
static inline struct video_renderer *impl_from_strmbase_renderer(struct strmbase_renderer *iface)
{
return CONTAINING_RECORD(iface, struct video_renderer, renderer);
}
static inline struct video_renderer *impl_from_IVideoWindow(IVideoWindow *iface)
{
return CONTAINING_RECORD(iface, struct video_renderer, window.IVideoWindow_iface);
}
static const BITMAPINFOHEADER *get_bitmap_header(const AM_MEDIA_TYPE *mt)
{
if (IsEqualGUID(&mt->formattype, &FORMAT_VideoInfo))
return &((VIDEOINFOHEADER *)mt->pbFormat)->bmiHeader;
else
return &((VIDEOINFOHEADER2 *)mt->pbFormat)->bmiHeader;
}
static void VideoRenderer_AutoShowWindow(struct video_renderer *This)
{
if (This->window.AutoShow)
ShowWindow(This->window.hwnd, SW_SHOW);
}
static HRESULT WINAPI VideoRenderer_ShouldDrawSampleNow(struct strmbase_renderer *filter,
IMediaSample *pSample, REFERENCE_TIME *start, REFERENCE_TIME *end)
{
/* Preroll means the sample isn't shown, this is used for key frames and things like that */
if (IMediaSample_IsPreroll(pSample) == S_OK)
return E_FAIL;
return S_FALSE;
}
static HRESULT WINAPI VideoRenderer_DoRenderSample(struct strmbase_renderer *iface, IMediaSample *pSample)
{
struct video_renderer *filter = impl_from_strmbase_renderer(iface);
RECT src = filter->window.src, dst = filter->window.dst;
LPBYTE pbSrcStream = NULL;
HRESULT hr;
HDC dc;
TRACE("filter %p, sample %p.\n", filter, pSample);
hr = IMediaSample_GetPointer(pSample, &pbSrcStream);
if (FAILED(hr))
{
ERR("Cannot get pointer to sample data (%x)\n", hr);
return hr;
}
dc = GetDC(filter->window.hwnd);
StretchDIBits(dc, dst.left, dst.top, dst.right - dst.left, dst.bottom - dst.top,
src.left, src.top, src.right - src.left, src.bottom - src.top, pbSrcStream,
(BITMAPINFO *)get_bitmap_header(&filter->renderer.sink.pin.mt), DIB_RGB_COLORS, SRCCOPY);
ReleaseDC(filter->window.hwnd, dc);
if (filter->renderer.filter.state == State_Paused)
{
const HANDLE events[2] = {filter->run_event, filter->renderer.flush_event};
filter->current_sample = pSample;
LeaveCriticalSection(&filter->renderer.csRenderLock);
WaitForMultipleObjects(2, events, FALSE, INFINITE);
EnterCriticalSection(&filter->renderer.csRenderLock);
filter->current_sample = NULL;
}
return S_OK;
}
static HRESULT WINAPI VideoRenderer_CheckMediaType(struct strmbase_renderer *iface, const AM_MEDIA_TYPE *mt)
{
if (!IsEqualGUID(&mt->majortype, &MEDIATYPE_Video))
return S_FALSE;
if (!IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_RGB32)
&& !IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_RGB24)
&& !IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_RGB565)
&& !IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_RGB8))
return S_FALSE;
if (!IsEqualGUID(&mt->formattype, &FORMAT_VideoInfo)
&& !IsEqualGUID(&mt->formattype, &FORMAT_VideoInfo2))
return S_FALSE;
return S_OK;
}
static void video_renderer_destroy(struct strmbase_renderer *iface)
{
struct video_renderer *filter = impl_from_strmbase_renderer(iface);
video_window_cleanup(&filter->window);
CloseHandle(filter->run_event);
strmbase_renderer_cleanup(&filter->renderer);
free(filter);
InterlockedDecrement(&object_locks);
}
static HRESULT video_renderer_query_interface(struct strmbase_renderer *iface, REFIID iid, void **out)
{
struct video_renderer *filter = impl_from_strmbase_renderer(iface);
if (IsEqualGUID(iid, &IID_IBasicVideo))
*out = &filter->window.IBasicVideo_iface;
else if (IsEqualGUID(iid, &IID_IVideoWindow))
*out = &filter->window.IVideoWindow_iface;
else
return E_NOINTERFACE;
IUnknown_AddRef((IUnknown *)*out);
return S_OK;
}
static HRESULT video_renderer_pin_query_interface(struct strmbase_renderer *iface, REFIID iid, void **out)
{
struct video_renderer *filter = impl_from_strmbase_renderer(iface);
if (IsEqualGUID(iid, &IID_IOverlay))
*out = &filter->IOverlay_iface;
else
return E_NOINTERFACE;
IUnknown_AddRef((IUnknown *)*out);
return S_OK;
}
static void video_renderer_start_stream(struct strmbase_renderer *iface)
{
struct video_renderer *filter = impl_from_strmbase_renderer(iface);
SetEvent(filter->run_event);
}
static void video_renderer_stop_stream(struct strmbase_renderer *iface)
{
struct video_renderer *This = impl_from_strmbase_renderer(iface);
TRACE("(%p)->()\n", This);
if (This->window.AutoShow)
/* Black it out */
RedrawWindow(This->window.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
ResetEvent(This->run_event);
}
static void video_renderer_init_stream(struct strmbase_renderer *iface)
{
struct video_renderer *filter = impl_from_strmbase_renderer(iface);
VideoRenderer_AutoShowWindow(filter);
}
static HRESULT video_renderer_connect(struct strmbase_renderer *iface, const AM_MEDIA_TYPE *mt)
{
struct video_renderer *filter = impl_from_strmbase_renderer(iface);
const BITMAPINFOHEADER *bitmap_header = get_bitmap_header(mt);
HWND window = filter->window.hwnd;
RECT rect;
filter->VideoWidth = bitmap_header->biWidth;
filter->VideoHeight = abs(bitmap_header->biHeight);
SetRect(&rect, 0, 0, filter->VideoWidth, filter->VideoHeight);
filter->window.src = filter->window.dst = rect;
AdjustWindowRectEx(&rect, GetWindowLongW(window, GWL_STYLE), FALSE,
GetWindowLongW(window, GWL_EXSTYLE));
SetWindowPos(window, NULL, 0, 0, rect.right - rect.left, rect.bottom - rect.top,
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
return S_OK;
}
static RECT video_renderer_get_default_rect(struct video_window *iface)
{
struct video_renderer *This = impl_from_video_window(iface);
static RECT defRect;
SetRect(&defRect, 0, 0, This->VideoWidth, This->VideoHeight);
return defRect;
}
static const struct strmbase_renderer_ops renderer_ops =
{
.pfnCheckMediaType = VideoRenderer_CheckMediaType,
.pfnDoRenderSample = VideoRenderer_DoRenderSample,
.renderer_init_stream = video_renderer_init_stream,
.renderer_start_stream = video_renderer_start_stream,
.renderer_stop_stream = video_renderer_stop_stream,
.pfnShouldDrawSampleNow = VideoRenderer_ShouldDrawSampleNow,
.renderer_destroy = video_renderer_destroy,
.renderer_query_interface = video_renderer_query_interface,
.renderer_pin_query_interface = video_renderer_pin_query_interface,
.renderer_connect = video_renderer_connect,
};
static HRESULT video_renderer_get_current_image(struct video_window *iface, LONG *size, LONG *image)
{
struct video_renderer *filter = impl_from_video_window(iface);
const BITMAPINFOHEADER *bih;
size_t image_size;
BYTE *sample_data;
EnterCriticalSection(&filter->renderer.csRenderLock);
bih = get_bitmap_header(&filter->renderer.sink.pin.mt);
image_size = bih->biWidth * bih->biHeight * bih->biBitCount / 8;
if (!image)
{
LeaveCriticalSection(&filter->renderer.csRenderLock);
*size = sizeof(BITMAPINFOHEADER) + image_size;
return S_OK;
}
if (filter->renderer.filter.state != State_Paused)
{
LeaveCriticalSection(&filter->renderer.csRenderLock);
return VFW_E_NOT_PAUSED;
}
if (!filter->current_sample)
{
LeaveCriticalSection(&filter->renderer.csRenderLock);
return E_UNEXPECTED;
}
if (*size < sizeof(BITMAPINFOHEADER) + image_size)
{
LeaveCriticalSection(&filter->renderer.csRenderLock);
return E_OUTOFMEMORY;
}
memcpy(image, bih, sizeof(BITMAPINFOHEADER));
IMediaSample_GetPointer(filter->current_sample, &sample_data);
memcpy((char *)image + sizeof(BITMAPINFOHEADER), sample_data, image_size);
LeaveCriticalSection(&filter->renderer.csRenderLock);
return S_OK;
}
static const struct video_window_ops window_ops =
{
.get_default_rect = video_renderer_get_default_rect,
.get_current_image = video_renderer_get_current_image,
};
static HRESULT WINAPI VideoWindow_get_FullScreenMode(IVideoWindow *iface,
LONG *FullScreenMode)
{
struct video_renderer *This = impl_from_IVideoWindow(iface);
TRACE("(%p/%p)->(%p): %d\n", This, iface, FullScreenMode, This->FullScreenMode);
if (!FullScreenMode)
return E_POINTER;
*FullScreenMode = This->FullScreenMode;
return S_OK;
}
static HRESULT WINAPI VideoWindow_put_FullScreenMode(IVideoWindow *iface, LONG fullscreen)
{
struct video_renderer *filter = impl_from_IVideoWindow(iface);
HWND window = filter->window.hwnd;
FIXME("filter %p, fullscreen %d.\n", filter, fullscreen);
if (fullscreen)
{
filter->saved_style = GetWindowLongW(window, GWL_STYLE);
ShowWindow(window, SW_HIDE);
SetParent(window, NULL);
SetWindowLongW(window, GWL_STYLE, WS_POPUP);
SetWindowPos(window, HWND_TOP, 0, 0,
GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), SWP_SHOWWINDOW);
GetWindowRect(window, &filter->window.dst);
}
else
{
ShowWindow(window, SW_HIDE);
SetParent(window, filter->window.hwndOwner);
SetWindowLongW(window, GWL_STYLE, filter->saved_style);
GetClientRect(window, &filter->window.dst);
SetWindowPos(window, 0, filter->window.dst.left, filter->window.dst.top,
filter->window.dst.right, filter->window.dst.bottom, SWP_NOZORDER | SWP_SHOWWINDOW);
}
filter->FullScreenMode = fullscreen;
return S_OK;
}
static const IVideoWindowVtbl IVideoWindow_VTable =
{
BaseControlWindowImpl_QueryInterface,
BaseControlWindowImpl_AddRef,
BaseControlWindowImpl_Release,
BaseControlWindowImpl_GetTypeInfoCount,
BaseControlWindowImpl_GetTypeInfo,
BaseControlWindowImpl_GetIDsOfNames,
BaseControlWindowImpl_Invoke,
BaseControlWindowImpl_put_Caption,
BaseControlWindowImpl_get_Caption,
BaseControlWindowImpl_put_WindowStyle,
BaseControlWindowImpl_get_WindowStyle,
BaseControlWindowImpl_put_WindowStyleEx,
BaseControlWindowImpl_get_WindowStyleEx,
BaseControlWindowImpl_put_AutoShow,
BaseControlWindowImpl_get_AutoShow,
BaseControlWindowImpl_put_WindowState,
BaseControlWindowImpl_get_WindowState,
BaseControlWindowImpl_put_BackgroundPalette,
BaseControlWindowImpl_get_BackgroundPalette,
BaseControlWindowImpl_put_Visible,
BaseControlWindowImpl_get_Visible,
BaseControlWindowImpl_put_Left,
BaseControlWindowImpl_get_Left,
BaseControlWindowImpl_put_Width,
BaseControlWindowImpl_get_Width,
BaseControlWindowImpl_put_Top,
BaseControlWindowImpl_get_Top,
BaseControlWindowImpl_put_Height,
BaseControlWindowImpl_get_Height,
BaseControlWindowImpl_put_Owner,
BaseControlWindowImpl_get_Owner,
BaseControlWindowImpl_put_MessageDrain,
BaseControlWindowImpl_get_MessageDrain,
BaseControlWindowImpl_get_BorderColor,
BaseControlWindowImpl_put_BorderColor,
VideoWindow_get_FullScreenMode,
VideoWindow_put_FullScreenMode,
BaseControlWindowImpl_SetWindowForeground,
BaseControlWindowImpl_NotifyOwnerMessage,
BaseControlWindowImpl_SetWindowPosition,
BaseControlWindowImpl_GetWindowPosition,
BaseControlWindowImpl_GetMinIdealImageSize,
BaseControlWindowImpl_GetMaxIdealImageSize,
BaseControlWindowImpl_GetRestorePosition,
BaseControlWindowImpl_HideCursor,
BaseControlWindowImpl_IsCursorHidden
};
static inline struct video_renderer *impl_from_IOverlay(IOverlay *iface)
{
return CONTAINING_RECORD(iface, struct video_renderer, IOverlay_iface);
}
static HRESULT WINAPI overlay_QueryInterface(IOverlay *iface, REFIID iid, void **out)
{
struct video_renderer *filter = impl_from_IOverlay(iface);
return IPin_QueryInterface(&filter->renderer.sink.pin.IPin_iface, iid, out);
}
static ULONG WINAPI overlay_AddRef(IOverlay *iface)
{
struct video_renderer *filter = impl_from_IOverlay(iface);
return IPin_AddRef(&filter->renderer.sink.pin.IPin_iface);
}
static ULONG WINAPI overlay_Release(IOverlay *iface)
{
struct video_renderer *filter = impl_from_IOverlay(iface);
return IPin_Release(&filter->renderer.sink.pin.IPin_iface);
}
static HRESULT WINAPI overlay_GetPalette(IOverlay *iface, DWORD *count, PALETTEENTRY **palette)
{
FIXME("iface %p, count %p, palette %p, stub!\n", iface, count, palette);
return E_NOTIMPL;
}
static HRESULT WINAPI overlay_SetPalette(IOverlay *iface, DWORD count, PALETTEENTRY *palette)
{
FIXME("iface %p, count %u, palette %p, stub!\n", iface, count, palette);
return E_NOTIMPL;
}
static HRESULT WINAPI overlay_GetDefaultColorKey(IOverlay *iface, COLORKEY *key)
{
FIXME("iface %p, key %p, stub!\n", iface, key);
return E_NOTIMPL;
}
static HRESULT WINAPI overlay_GetColorKey(IOverlay *iface, COLORKEY *key)
{
FIXME("iface %p, key %p, stub!\n", iface, key);
return E_NOTIMPL;
}
static HRESULT WINAPI overlay_SetColorKey(IOverlay *iface, COLORKEY *key)
{
FIXME("iface %p, key %p, stub!\n", iface, key);
return E_NOTIMPL;
}
static HRESULT WINAPI overlay_GetWindowHandle(IOverlay *iface, HWND *window)
{
struct video_renderer *filter = impl_from_IOverlay(iface);
TRACE("filter %p, window %p.\n", filter, window);
*window = filter->window.hwnd;
return S_OK;
}
static HRESULT WINAPI overlay_GetClipList(IOverlay *iface, RECT *source, RECT *dest, RGNDATA **region)
{
FIXME("iface %p, source %p, dest %p, region %p, stub!\n", iface, source, dest, region);
return E_NOTIMPL;
}
static HRESULT WINAPI overlay_GetVideoPosition(IOverlay *iface, RECT *source, RECT *dest)
{
FIXME("iface %p, source %p, dest %p, stub!\n", iface, source, dest);
return E_NOTIMPL;
}
static HRESULT WINAPI overlay_Advise(IOverlay *iface, IOverlayNotify *sink, DWORD flags)
{
FIXME("iface %p, sink %p, flags %#x, stub!\n", iface, sink, flags);
return E_NOTIMPL;
}
static HRESULT WINAPI overlay_Unadvise(IOverlay *iface)
{
FIXME("iface %p, stub!\n", iface);
return E_NOTIMPL;
}
static const IOverlayVtbl overlay_vtbl =
{
overlay_QueryInterface,
overlay_AddRef,
overlay_Release,
overlay_GetPalette,
overlay_SetPalette,
overlay_GetDefaultColorKey,
overlay_GetColorKey,
overlay_SetColorKey,
overlay_GetWindowHandle,
overlay_GetClipList,
overlay_GetVideoPosition,
overlay_Advise,
overlay_Unadvise,
};
HRESULT video_renderer_create(IUnknown *outer, IUnknown **out)
{
struct video_renderer *object;
HRESULT hr;
if (!(object = calloc(1, sizeof(*object))))
return E_OUTOFMEMORY;
strmbase_renderer_init(&object->renderer, outer, &CLSID_VideoRenderer, L"In", &renderer_ops);
object->IOverlay_iface.lpVtbl = &overlay_vtbl;
video_window_init(&object->window, &IVideoWindow_VTable,
&object->renderer.filter, &object->renderer.sink.pin, &window_ops);
if (FAILED(hr = video_window_create_window(&object->window)))
{
video_window_cleanup(&object->window);
strmbase_renderer_cleanup(&object->renderer);
free(object);
return hr;
}
object->run_event = CreateEventW(NULL, TRUE, FALSE, NULL);
TRACE("Created video renderer %p.\n", object);
*out = &object->renderer.filter.IUnknown_inner;
return S_OK;
}
HRESULT video_renderer_default_create(IUnknown *outer, IUnknown **out)
{
/* TODO: Attempt to use the VMR-7 renderer instead when possible */
return video_renderer_create(outer, out);
}