videorenderer.c 16.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * 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
18
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 20 21 22 23 24 25 26 27 28
 */

#include "quartz_private.h"

#include "uuids.h"
#include "vfwmsgs.h"
#include "amvideo.h"
#include "windef.h"
#include "winbase.h"
#include "dshow.h"
29
#include "evcode.h"
30 31
#include "strmif.h"
#include "ddraw.h"
32
#include "dvdmedia.h"
33

34
#include <assert.h>
35 36 37 38
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(quartz);

39
struct video_renderer
40
{
41
    struct strmbase_renderer renderer;
42
    struct video_window window;
43

44 45
    IOverlay IOverlay_iface;

46 47
    LONG VideoWidth;
    LONG VideoHeight;
48
    LONG FullScreenMode;
49 50

    DWORD saved_style;
51 52

    HANDLE run_event;
53
    IMediaSample *current_sample;
54
};
55

56
static inline struct video_renderer *impl_from_video_window(struct video_window *iface)
57
{
58
    return CONTAINING_RECORD(iface, struct video_renderer, window);
59 60
}

61
static inline struct video_renderer *impl_from_strmbase_renderer(struct strmbase_renderer *iface)
62
{
63
    return CONTAINING_RECORD(iface, struct video_renderer, renderer);
64 65
}

66
static inline struct video_renderer *impl_from_IVideoWindow(IVideoWindow *iface)
67
{
68
    return CONTAINING_RECORD(iface, struct video_renderer, window.IVideoWindow_iface);
69 70
}

71 72 73 74 75 76 77 78
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;
}

79
static void VideoRenderer_AutoShowWindow(struct video_renderer *This)
80
{
81 82
    if (This->window.AutoShow)
        ShowWindow(This->window.hwnd, SW_SHOW);
83 84
}

85
static HRESULT WINAPI VideoRenderer_DoRenderSample(struct strmbase_renderer *iface, IMediaSample *pSample)
86
{
87
    struct video_renderer *filter = impl_from_strmbase_renderer(iface);
88
    RECT src = filter->window.src, dst = filter->window.dst;
89 90
    LPBYTE pbSrcStream = NULL;
    HRESULT hr;
91
    HDC dc;
92

93
    TRACE("filter %p, sample %p.\n", filter, pSample);
94

95 96 97
    hr = IMediaSample_GetPointer(pSample, &pbSrcStream);
    if (FAILED(hr))
    {
98
        ERR("Cannot get pointer to sample data (%x)\n", hr);
99
        return hr;
100 101
    }

102
    dc = GetDC(filter->window.hwnd);
103 104 105
    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);
106
    ReleaseDC(filter->window.hwnd, dc);
107

108 109 110 111
    if (filter->renderer.filter.state == State_Paused)
    {
        const HANDLE events[2] = {filter->run_event, filter->renderer.flush_event};

112 113
        filter->current_sample = pSample;

114
        SetEvent(filter->renderer.state_event);
115
        LeaveCriticalSection(&filter->renderer.filter.stream_cs);
116
        WaitForMultipleObjects(2, events, FALSE, INFINITE);
117
        EnterCriticalSection(&filter->renderer.filter.stream_cs);
118 119

        filter->current_sample = NULL;
120 121
    }

122 123 124
    return S_OK;
}

125
static HRESULT WINAPI VideoRenderer_CheckMediaType(struct strmbase_renderer *iface, const AM_MEDIA_TYPE *mt)
126
{
127
    if (!IsEqualGUID(&mt->majortype, &MEDIATYPE_Video))
128 129
        return S_FALSE;

130 131 132 133 134
    if (!IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_RGB32)
            && !IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_RGB24)
            && !IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_RGB565)
            && !IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_RGB8))
        return S_FALSE;
135

136 137 138 139 140
    if (!IsEqualGUID(&mt->formattype, &FORMAT_VideoInfo)
            && !IsEqualGUID(&mt->formattype, &FORMAT_VideoInfo2))
        return S_FALSE;

    return S_OK;
141 142
}

143
static void video_renderer_destroy(struct strmbase_renderer *iface)
144
{
145
    struct video_renderer *filter = impl_from_strmbase_renderer(iface);
146

147
    video_window_cleanup(&filter->window);
148
    CloseHandle(filter->run_event);
149
    strmbase_renderer_cleanup(&filter->renderer);
150
    free(filter);
151 152

    InterlockedDecrement(&object_locks);
153 154
}

155
static HRESULT video_renderer_query_interface(struct strmbase_renderer *iface, REFIID iid, void **out)
156
{
157
    struct video_renderer *filter = impl_from_strmbase_renderer(iface);
158 159

    if (IsEqualGUID(iid, &IID_IBasicVideo))
160
        *out = &filter->window.IBasicVideo_iface;
161
    else if (IsEqualGUID(iid, &IID_IVideoWindow))
162
        *out = &filter->window.IVideoWindow_iface;
163 164 165 166 167 168 169
    else
        return E_NOINTERFACE;

    IUnknown_AddRef((IUnknown *)*out);
    return S_OK;
}

170
static HRESULT video_renderer_pin_query_interface(struct strmbase_renderer *iface, REFIID iid, void **out)
171
{
172
    struct video_renderer *filter = impl_from_strmbase_renderer(iface);
173 174 175 176 177 178 179 180 181 182

    if (IsEqualGUID(iid, &IID_IOverlay))
        *out = &filter->IOverlay_iface;
    else
        return E_NOINTERFACE;

    IUnknown_AddRef((IUnknown *)*out);
    return S_OK;
}

183 184
static void video_renderer_start_stream(struct strmbase_renderer *iface)
{
185
    struct video_renderer *filter = impl_from_strmbase_renderer(iface);
186 187 188 189

    SetEvent(filter->run_event);
}

190
static void video_renderer_stop_stream(struct strmbase_renderer *iface)
191
{
192
    struct video_renderer *This = impl_from_strmbase_renderer(iface);
193

194
    TRACE("(%p)->()\n", This);
195

196
    if (This->window.AutoShow)
197
        /* Black it out */
198
        RedrawWindow(This->window.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
199 200

    ResetEvent(This->run_event);
201 202
}

203
static void video_renderer_init_stream(struct strmbase_renderer *iface)
204
{
205
    struct video_renderer *filter = impl_from_strmbase_renderer(iface);
206

207
    VideoRenderer_AutoShowWindow(filter);
208 209
}

210 211 212 213
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);
214
    HWND window = filter->window.hwnd;
215
    RECT rect;
216 217 218

    filter->VideoWidth = bitmap_header->biWidth;
    filter->VideoHeight = abs(bitmap_header->biHeight);
219
    SetRect(&rect, 0, 0, filter->VideoWidth, filter->VideoHeight);
220
    filter->window.src = rect;
221 222 223 224 225

    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);
226
    GetClientRect(window, &filter->window.dst);
227 228 229 230

    return S_OK;
}

231
static RECT video_renderer_get_default_rect(struct video_window *iface)
232
{
233
    struct video_renderer *This = impl_from_video_window(iface);
234
    static RECT defRect;
235

236
    SetRect(&defRect, 0, 0, This->VideoWidth, This->VideoHeight);
237 238

    return defRect;
239 240
}

241
static const struct strmbase_renderer_ops renderer_ops =
242 243 244
{
    .pfnCheckMediaType = VideoRenderer_CheckMediaType,
    .pfnDoRenderSample = VideoRenderer_DoRenderSample,
245
    .renderer_init_stream = video_renderer_init_stream,
246
    .renderer_start_stream = video_renderer_start_stream,
247
    .renderer_stop_stream = video_renderer_stop_stream,
248 249
    .renderer_destroy = video_renderer_destroy,
    .renderer_query_interface = video_renderer_query_interface,
250
    .renderer_pin_query_interface = video_renderer_pin_query_interface,
251
    .renderer_connect = video_renderer_connect,
252 253
};

254
static HRESULT video_renderer_get_current_image(struct video_window *iface, LONG *size, LONG *image)
255
{
256
    struct video_renderer *filter = impl_from_video_window(iface);
257 258 259
    const BITMAPINFOHEADER *bih;
    size_t image_size;
    BYTE *sample_data;
260

261
    EnterCriticalSection(&filter->renderer.filter.stream_cs);
262

263
    bih = get_bitmap_header(&filter->renderer.sink.pin.mt);
264
    image_size = bih->biWidth * bih->biHeight * bih->biBitCount / 8;
265

266
    if (!image)
267
    {
268
        LeaveCriticalSection(&filter->renderer.filter.stream_cs);
269 270
        *size = sizeof(BITMAPINFOHEADER) + image_size;
        return S_OK;
271
    }
272 273

    if (filter->renderer.filter.state != State_Paused)
274
    {
275
        LeaveCriticalSection(&filter->renderer.filter.stream_cs);
276
        return VFW_E_NOT_PAUSED;
277 278
    }

279
    if (!filter->current_sample)
280
    {
281
        LeaveCriticalSection(&filter->renderer.filter.stream_cs);
282
        return E_UNEXPECTED;
283 284
    }

285
    if (*size < sizeof(BITMAPINFOHEADER) + image_size)
286
    {
287
        LeaveCriticalSection(&filter->renderer.filter.stream_cs);
288
        return E_OUTOFMEMORY;
289 290
    }

291
    memcpy(image, bih, sizeof(BITMAPINFOHEADER));
292
    IMediaSample_GetPointer(filter->current_sample, &sample_data);
293
    memcpy((char *)image + sizeof(BITMAPINFOHEADER), sample_data, image_size);
294

295
    LeaveCriticalSection(&filter->renderer.filter.stream_cs);
296 297 298
    return S_OK;
}

299 300 301 302
static const struct video_window_ops window_ops =
{
    .get_default_rect = video_renderer_get_default_rect,
    .get_current_image = video_renderer_get_current_image,
303 304
};

305 306 307
static HRESULT WINAPI VideoWindow_get_FullScreenMode(IVideoWindow *iface,
                                                     LONG *FullScreenMode)
{
308
    struct video_renderer *This = impl_from_IVideoWindow(iface);
309

310 311 312 313 314 315
    TRACE("(%p/%p)->(%p): %d\n", This, iface, FullScreenMode, This->FullScreenMode);

    if (!FullScreenMode)
        return E_POINTER;

    *FullScreenMode = This->FullScreenMode;
316 317 318 319

    return S_OK;
}

320
static HRESULT WINAPI VideoWindow_put_FullScreenMode(IVideoWindow *iface, LONG fullscreen)
321
{
322
    struct video_renderer *filter = impl_from_IVideoWindow(iface);
323
    HWND window = filter->window.hwnd;
324

325
    FIXME("filter %p, fullscreen %d.\n", filter, fullscreen);
326

327 328
    if (fullscreen)
    {
329 330 331 332 333
        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,
334
                GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), SWP_SHOWWINDOW);
335
        GetWindowRect(window, &filter->window.dst);
336 337 338
    }
    else
    {
339 340 341
        ShowWindow(window, SW_HIDE);
        SetParent(window, filter->window.hwndOwner);
        SetWindowLongW(window, GWL_STYLE, filter->saved_style);
342 343 344
        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);
345
    }
346
    filter->FullScreenMode = fullscreen;
347

348 349 350
    return S_OK;
}

351
static const IVideoWindowVtbl IVideoWindow_VTable =
352
{
353 354 355
    BaseControlWindowImpl_QueryInterface,
    BaseControlWindowImpl_AddRef,
    BaseControlWindowImpl_Release,
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387
    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,
388 389
    VideoWindow_get_FullScreenMode,
    VideoWindow_put_FullScreenMode,
390 391 392 393 394 395 396 397 398
    BaseControlWindowImpl_SetWindowForeground,
    BaseControlWindowImpl_NotifyOwnerMessage,
    BaseControlWindowImpl_SetWindowPosition,
    BaseControlWindowImpl_GetWindowPosition,
    BaseControlWindowImpl_GetMinIdealImageSize,
    BaseControlWindowImpl_GetMaxIdealImageSize,
    BaseControlWindowImpl_GetRestorePosition,
    BaseControlWindowImpl_HideCursor,
    BaseControlWindowImpl_IsCursorHidden
399
};
400

401
static inline struct video_renderer *impl_from_IOverlay(IOverlay *iface)
402
{
403
    return CONTAINING_RECORD(iface, struct video_renderer, IOverlay_iface);
404 405 406 407
}

static HRESULT WINAPI overlay_QueryInterface(IOverlay *iface, REFIID iid, void **out)
{
408
    struct video_renderer *filter = impl_from_IOverlay(iface);
409 410 411 412 413
    return IPin_QueryInterface(&filter->renderer.sink.pin.IPin_iface, iid, out);
}

static ULONG WINAPI overlay_AddRef(IOverlay *iface)
{
414
    struct video_renderer *filter = impl_from_IOverlay(iface);
415 416 417 418 419
    return IPin_AddRef(&filter->renderer.sink.pin.IPin_iface);
}

static ULONG WINAPI overlay_Release(IOverlay *iface)
{
420
    struct video_renderer *filter = impl_from_IOverlay(iface);
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455
    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)
{
456
    struct video_renderer *filter = impl_from_IOverlay(iface);
457 458 459

    TRACE("filter %p, window %p.\n", filter, window);

460
    *window = filter->window.hwnd;
461
    return S_OK;
462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504
}

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,
};

505
HRESULT video_renderer_create(IUnknown *outer, IUnknown **out)
506
{
507
    struct video_renderer *object;
508 509
    HRESULT hr;

510 511
    if (!(object = calloc(1, sizeof(*object))))
        return E_OUTOFMEMORY;
512

513 514
    strmbase_renderer_init(&object->renderer, outer, &CLSID_VideoRenderer, L"In", &renderer_ops);
    object->IOverlay_iface.lpVtbl = &overlay_vtbl;
515

516
    video_window_init(&object->window, &IVideoWindow_VTable,
517
            &object->renderer.filter, &object->renderer.sink.pin, &window_ops);
518

519
    if (FAILED(hr = video_window_create_window(&object->window)))
520
    {
521
        video_window_cleanup(&object->window);
522 523 524 525
        strmbase_renderer_cleanup(&object->renderer);
        free(object);
        return hr;
    }
526

527
    object->run_event = CreateEventW(NULL, TRUE, FALSE, NULL);
528

529 530
    TRACE("Created video renderer %p.\n", object);
    *out = &object->renderer.filter.IUnknown_inner;
531 532 533
    return S_OK;
}

534
HRESULT video_renderer_default_create(IUnknown *outer, IUnknown **out)
535
{
536 537 538 539 540
    HRESULT hr;

    if (SUCCEEDED(hr = vmr7_create(outer, out)))
        return hr;

541
    return video_renderer_create(outer, out);
542
}