/* * CLSID_FilterGraph event handling. * * Copyright (C) Hidenori TAKESHIMA * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include "windef.h" #include "winbase.h" #include "wingdi.h" #include "winuser.h" #include "winerror.h" #include "strmif.h" #include "control.h" #include "evcode.h" #include "uuids.h" #include "vfwmsgs.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(quartz); #include "quartz_private.h" #include "fgraph.h" #define EVENTQUEUE_BLOCKSIZE 2 #define EVENTQUEUE_MAX 1024 struct FilterGraph_MEDIAEVENT { long lEventCode; LONG_PTR lParam1; LONG_PTR lParam2; }; static HRESULT FGEVENT_KeepEvent( BOOL bKeep, long lEventCode, LONG_PTR lParam1, LONG_PTR lParam2 ) { switch ( lEventCode ) { /*case EC_COMPLETE:*/ case EC_USERABORT: break; case EC_ERRORABORT: break; case EC_TIME: break; /*case EC_REPAINT:*/ case EC_STREAM_ERROR_STOPPED: break; case EC_STREAM_ERROR_STILLPLAYING: break; case EC_ERROR_STILLPLAYING: break; case EC_PALETTE_CHANGED: break; case EC_VIDEO_SIZE_CHANGED: break; case EC_QUALITY_CHANGE: break; /*case EC_SHUTTING_DOWN:*/ case EC_CLOCK_CHANGED: break; case EC_PAUSED: break; case EC_OPENING_FILE: break; case EC_BUFFERING_DATA: break; case EC_FULLSCREEN_LOST: if ( bKeep ) { if ( ((IBaseFilter*)lParam2) != NULL ) IBaseFilter_AddRef( (IBaseFilter*)lParam2 ); } else { if ( ((IBaseFilter*)lParam2) != NULL ) IBaseFilter_Release( (IBaseFilter*)lParam2 ); } break; /*case EC_ACTIVATE:*/ /*case EC_NEED_RESTART:*/ /*case EC_WINDOW_DESTROYED:*/ /*case EC_DISPLAY_CHANGED:*/ /*case EC_STARVATION:*/ /*case EC_OLE_EVENT:*/ /*case EC_NOTIFY_WINDOW:*/ /*case EC_STREAM_CONTROL_STOPPED:*/ /*case EC_STREAM_CONTROL_STARTED:*/ /*case EC_END_OF_SEGMENT:*/ /*case EC_SEGMENT_STARTED:*/ case EC_LENGTH_CHANGED: break; case EC_DEVICE_LOST: if ( bKeep ) { if ( ((IUnknown*)lParam1) != NULL ) IUnknown_AddRef( (IUnknown*)lParam1 ); } else { if ( ((IUnknown*)lParam1) != NULL ) IUnknown_Release( (IUnknown*)lParam1 ); } break; case EC_STEP_COMPLETE: break; case EC_SKIP_FRAMES: break; /*case EC_TIMECODE_AVAILABLE:*/ /*case EC_EXTDEVICE_MODE_CHANGE:*/ case EC_GRAPH_CHANGED: break; case EC_CLOCK_UNSET: break; default: if ( lEventCode < EC_USER ) { FIXME( "unknown system event %08lx\n", lEventCode ); return E_INVALIDARG; } TRACE( "user event %08lx\n", lEventCode ); break; } return NOERROR; } /*************************************************************************** * * CLSID_FilterGraph::IMediaEvent[Ex] * */ static HRESULT WINAPI IMediaEventEx_fnQueryInterface(IMediaEventEx* iface,REFIID riid,void** ppobj) { CFilterGraph_THIS(iface,mediaevent); TRACE("(%p)->()\n",This); return IUnknown_QueryInterface(This->unk.punkControl,riid,ppobj); } static ULONG WINAPI IMediaEventEx_fnAddRef(IMediaEventEx* iface) { CFilterGraph_THIS(iface,mediaevent); TRACE("(%p)->()\n",This); return IUnknown_AddRef(This->unk.punkControl); } static ULONG WINAPI IMediaEventEx_fnRelease(IMediaEventEx* iface) { CFilterGraph_THIS(iface,mediaevent); TRACE("(%p)->()\n",This); return IUnknown_Release(This->unk.punkControl); } static HRESULT WINAPI IMediaEventEx_fnGetTypeInfoCount(IMediaEventEx* iface,UINT* pcTypeInfo) { CFilterGraph_THIS(iface,mediaevent); FIXME("(%p)->()\n",This); return E_NOTIMPL; } static HRESULT WINAPI IMediaEventEx_fnGetTypeInfo(IMediaEventEx* iface,UINT iTypeInfo, LCID lcid, ITypeInfo** ppobj) { CFilterGraph_THIS(iface,mediaevent); FIXME("(%p)->()\n",This); return E_NOTIMPL; } static HRESULT WINAPI IMediaEventEx_fnGetIDsOfNames(IMediaEventEx* iface,REFIID riid, LPOLESTR* ppwszName, UINT cNames, LCID lcid, DISPID* pDispId) { CFilterGraph_THIS(iface,mediaevent); FIXME("(%p)->()\n",This); return E_NOTIMPL; } static HRESULT WINAPI IMediaEventEx_fnInvoke(IMediaEventEx* iface,DISPID DispId, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarRes, EXCEPINFO* pExcepInfo, UINT* puArgErr) { CFilterGraph_THIS(iface,mediaevent); FIXME("(%p)->()\n",This); return E_NOTIMPL; } static HRESULT WINAPI IMediaEventEx_fnGetEventHandle(IMediaEventEx* iface,OAEVENT* hEvent) { CFilterGraph_THIS(iface,mediaevent); TRACE("(%p)->()\n",This); *hEvent = (OAEVENT)This->m_hMediaEvent; return NOERROR; } static HRESULT WINAPI IMediaEventEx_fnGetEvent(IMediaEventEx* iface,long* plEventCode,LONG_PTR* plParam1,LONG_PTR* plParam2,long lTimeOut) { CFilterGraph_THIS(iface,mediaevent); ULONG cQueued; DWORD dw; DWORD dwStart; HRESULT hr; FilterGraph_MEDIAEVENT* pEvent; TRACE("(%p)->(%p,%p,%p,%ld)\n",This,plEventCode, plParam1,plParam2,lTimeOut); if ( plEventCode == NULL || plParam1 == NULL || plParam2 == NULL ) return E_POINTER; while ( 1 ) { dwStart = GetTickCount(); dw = WaitForSingleObject( This->m_hMediaEvent, lTimeOut ); if ( dw == WAIT_TIMEOUT ) return VFW_E_TIMEOUT; if ( dw != WAIT_OBJECT_0 ) return E_FAIL; EnterCriticalSection( &This->m_csMediaEvents ); hr = S_FALSE; if ( This->m_cbMediaEventsMax > 0 ) { cQueued = (This->m_cbMediaEventsMax + This->m_cbMediaEventsPut - This->m_cbMediaEventsGet) % This->m_cbMediaEventsMax; if ( cQueued > 0 ) { pEvent = &This->m_pMediaEvents[This->m_cbMediaEventsGet]; *plEventCode = pEvent->lEventCode; *plParam1 = pEvent->lParam1; *plParam2 = pEvent->lParam2; This->m_cbMediaEventsGet = (This->m_cbMediaEventsGet + 1) % This->m_cbMediaEventsMax; hr = NOERROR; if ( This->m_cbMediaEventsPut == This->m_cbMediaEventsGet ) ResetEvent( This->m_hMediaEvent ); } } LeaveCriticalSection( &This->m_csMediaEvents ); if ( hr != S_FALSE ) return hr; if ( lTimeOut != INFINITE ) { lTimeOut -= GetTickCount() - dwStart; if ( lTimeOut < 0 ) return VFW_E_TIMEOUT; } } } static HRESULT WINAPI IMediaEventEx_fnWaitForCompletion(IMediaEventEx* iface,long lTimeOut,long* plEventCode) { CFilterGraph_THIS(iface,mediaevent); HRESULT hr; long lEventCode; LONG_PTR lParam1; LONG_PTR lParam2; DWORD dwTimePrev; DWORD dwTimeCur; TRACE("(%p)->(%ld,%p)\n",This,lTimeOut,plEventCode); if ( plEventCode == NULL ) return E_POINTER; *plEventCode = 0; dwTimePrev = GetTickCount(); while ( 1 ) { hr = IMediaEventEx_GetEvent( CFilterGraph_IMediaEventEx(This), &lEventCode,&lParam1,&lParam2,lTimeOut); if ( hr == VFW_E_TIMEOUT ) hr = E_ABORT; if ( hr != NOERROR ) return hr; IMediaEventEx_FreeEventParams( CFilterGraph_IMediaEventEx(This), lEventCode,lParam1,lParam2); if ( lEventCode == EC_COMPLETE || lEventCode == EC_ERRORABORT || lEventCode == EC_USERABORT ) { *plEventCode = lEventCode; return NOERROR; } if ( lTimeOut != INFINITE ) { dwTimeCur = GetTickCount(); lTimeOut -= dwTimeCur - dwTimePrev; dwTimePrev = dwTimeCur; if ( lTimeOut < 0 ) return E_ABORT; } } } static HRESULT WINAPI IMediaEventEx_fnCancelDefaultHandling(IMediaEventEx* iface,long lEventCode) { CFilterGraph_THIS(iface,mediaevent); FIXME("(%p)->() stub!\n",This); return E_NOTIMPL; } static HRESULT WINAPI IMediaEventEx_fnRestoreDefaultHandling(IMediaEventEx* iface,long lEventCode) { CFilterGraph_THIS(iface,mediaevent); FIXME("(%p)->() stub!\n",This); return E_NOTIMPL; } static HRESULT WINAPI IMediaEventEx_fnFreeEventParams(IMediaEventEx* iface,long lEventCode,LONG_PTR lParam1,LONG_PTR lParam2) { CFilterGraph_THIS(iface,mediaevent); TRACE("(%p)->(%08lx,%08x,%08x)\n",This,lEventCode,lParam1,lParam2); return FGEVENT_KeepEvent( FALSE, lEventCode, lParam1, lParam2 ); } static HRESULT WINAPI IMediaEventEx_fnSetNotifyWindow(IMediaEventEx* iface,OAHWND hwnd,long message,LONG_PTR lParam) { CFilterGraph_THIS(iface,mediaevent); TRACE("(%p)->(%08x,%08lx,%08x)\n",This,hwnd,message,lParam); EnterCriticalSection( &This->m_csMediaEvents ); This->m_hwndEventNotify = (HWND)hwnd; This->m_lEventNotifyMsg = message; This->m_lEventNotifyParam = lParam; LeaveCriticalSection( &This->m_csMediaEvents ); return NOERROR; } static HRESULT WINAPI IMediaEventEx_fnSetNotifyFlags(IMediaEventEx* iface,long lNotifyFlags) { CFilterGraph_THIS(iface,mediaevent); TRACE("(%p)->(%ld)\n",This,lNotifyFlags); if ( lNotifyFlags != 0 && lNotifyFlags != 1 ) return E_INVALIDARG; EnterCriticalSection( &This->m_csMediaEvents ); This->m_lEventNotifyFlags = lNotifyFlags; LeaveCriticalSection( &This->m_csMediaEvents ); return NOERROR; } static HRESULT WINAPI IMediaEventEx_fnGetNotifyFlags(IMediaEventEx* iface,long* plNotifyFlags) { CFilterGraph_THIS(iface,mediaevent); TRACE("(%p)->(%p)\n",This,plNotifyFlags); if ( plNotifyFlags == NULL ) return E_POINTER; EnterCriticalSection( &This->m_csMediaEvents ); *plNotifyFlags = This->m_lEventNotifyFlags; LeaveCriticalSection( &This->m_csMediaEvents ); return NOERROR; } static ICOM_VTABLE(IMediaEventEx) imediaevent = { ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE /* IUnknown fields */ IMediaEventEx_fnQueryInterface, IMediaEventEx_fnAddRef, IMediaEventEx_fnRelease, /* IDispatch fields */ IMediaEventEx_fnGetTypeInfoCount, IMediaEventEx_fnGetTypeInfo, IMediaEventEx_fnGetIDsOfNames, IMediaEventEx_fnInvoke, /* IMediaEvent fields */ IMediaEventEx_fnGetEventHandle, IMediaEventEx_fnGetEvent, IMediaEventEx_fnWaitForCompletion, IMediaEventEx_fnCancelDefaultHandling, IMediaEventEx_fnRestoreDefaultHandling, IMediaEventEx_fnFreeEventParams, /* IMediaEventEx fields */ IMediaEventEx_fnSetNotifyWindow, IMediaEventEx_fnSetNotifyFlags, IMediaEventEx_fnGetNotifyFlags, }; HRESULT CFilterGraph_InitIMediaEventEx( CFilterGraph* pfg ) { TRACE("(%p)\n",pfg); ICOM_VTBL(&pfg->mediaevent) = &imediaevent; pfg->m_hMediaEvent = CreateEventA( NULL, TRUE, FALSE, NULL ); if ( pfg->m_hMediaEvent == (HANDLE)NULL ) return E_OUTOFMEMORY; InitializeCriticalSection( &pfg->m_csMediaEvents ); pfg->m_pMediaEvents = NULL; pfg->m_cbMediaEventsPut = 0; pfg->m_cbMediaEventsGet = 0; pfg->m_cbMediaEventsMax = 0; pfg->m_hwndEventNotify = (HWND)NULL; pfg->m_lEventNotifyMsg = 0; pfg->m_lEventNotifyParam = 0; pfg->m_lEventNotifyFlags = 0; return NOERROR; } void CFilterGraph_UninitIMediaEventEx( CFilterGraph* pfg ) { HRESULT hr; long lEventCode; LONG_PTR lParam1; LONG_PTR lParam2; TRACE("(%p)\n",pfg); while ( 1 ) { hr = IMediaEventEx_GetEvent( CFilterGraph_IMediaEventEx(pfg), &lEventCode,&lParam1,&lParam2,0); if ( hr != NOERROR ) break; IMediaEventEx_FreeEventParams( CFilterGraph_IMediaEventEx(pfg), lEventCode,lParam1,lParam2); } if ( pfg->m_pMediaEvents != NULL ) { QUARTZ_FreeMem( pfg->m_pMediaEvents ); pfg->m_pMediaEvents = NULL; } DeleteCriticalSection( &pfg->m_csMediaEvents ); CloseHandle( pfg->m_hMediaEvent ); } /*************************************************************************** * * CLSID_FilterGraph::IMediaEventSink * */ static HRESULT WINAPI IMediaEventSink_fnQueryInterface(IMediaEventSink* iface,REFIID riid,void** ppobj) { CFilterGraph_THIS(iface,mediaeventsink); TRACE("(%p)->()\n",This); return IUnknown_QueryInterface(This->unk.punkControl,riid,ppobj); } static ULONG WINAPI IMediaEventSink_fnAddRef(IMediaEventSink* iface) { CFilterGraph_THIS(iface,mediaeventsink); TRACE("(%p)->()\n",This); return IUnknown_AddRef(This->unk.punkControl); } static ULONG WINAPI IMediaEventSink_fnRelease(IMediaEventSink* iface) { CFilterGraph_THIS(iface,mediaeventsink); TRACE("(%p)->()\n",This); return IUnknown_Release(This->unk.punkControl); } static HRESULT WINAPI IMediaEventSink_fnNotify(IMediaEventSink* iface,long lEventCode,LONG_PTR lParam1,LONG_PTR lParam2) { CFilterGraph_THIS(iface,mediaeventsink); HRESULT hr = NOERROR; ULONG cQueued; ULONG cTemp; FilterGraph_MEDIAEVENT* pEvent; TRACE("(%p)->(%08lx,%08x,%08x) stub!\n",This,lEventCode,lParam1,lParam2); EnterCriticalSection( &This->m_csMediaEvents ); /* allocate a new entry. */ if ( This->m_cbMediaEventsMax == 0 ) cQueued = 0; else cQueued = (This->m_cbMediaEventsMax + This->m_cbMediaEventsPut - This->m_cbMediaEventsGet) % This->m_cbMediaEventsMax; if ( (cQueued + 1) >= This->m_cbMediaEventsMax ) { if ( This->m_cbMediaEventsMax >= EVENTQUEUE_MAX ) { hr = E_FAIL; goto end; } pEvent = (FilterGraph_MEDIAEVENT*) QUARTZ_AllocMem( sizeof(FilterGraph_MEDIAEVENT) * (This->m_cbMediaEventsMax+EVENTQUEUE_BLOCKSIZE) ); if ( pEvent == NULL ) { hr = E_OUTOFMEMORY; goto end; } if ( cQueued > 0 ) { if ( (This->m_cbMediaEventsGet + cQueued) >= This->m_cbMediaEventsMax ) { cTemp = This->m_cbMediaEventsMax - This->m_cbMediaEventsGet; memcpy( pEvent, &This->m_pMediaEvents[This->m_cbMediaEventsGet], sizeof(FilterGraph_MEDIAEVENT) * cTemp ); memcpy( pEvent + cTemp, &This->m_pMediaEvents[0], sizeof(FilterGraph_MEDIAEVENT) * (cQueued - cTemp) ); } else { memcpy( pEvent, &This->m_pMediaEvents[This->m_cbMediaEventsGet], sizeof(FilterGraph_MEDIAEVENT) * cQueued ); } QUARTZ_FreeMem( This->m_pMediaEvents ); } This->m_pMediaEvents = pEvent; This->m_cbMediaEventsMax += EVENTQUEUE_BLOCKSIZE; This->m_cbMediaEventsPut = cQueued; This->m_cbMediaEventsGet = 0; } /* duplicate params if necessary. */ hr = FGEVENT_KeepEvent( TRUE, lEventCode, lParam1, lParam2 ); if ( FAILED(hr) ) goto end; /* add to the queue. */ pEvent = &This->m_pMediaEvents[This->m_cbMediaEventsPut]; pEvent->lEventCode = lEventCode; pEvent->lParam1 = lParam1; pEvent->lParam2 = lParam2; This->m_cbMediaEventsPut = (This->m_cbMediaEventsPut + 1) % This->m_cbMediaEventsMax; SetEvent( This->m_hMediaEvent ); if ( This->m_hwndEventNotify != (HWND)NULL && This->m_lEventNotifyFlags == 0 ) { PostMessageA( This->m_hwndEventNotify, This->m_lEventNotifyMsg, (WPARAM)0, (LPARAM)This->m_lEventNotifyParam ); } hr = NOERROR; end: LeaveCriticalSection( &This->m_csMediaEvents ); return hr; } static ICOM_VTABLE(IMediaEventSink) imediaeventsink = { ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE /* IUnknown fields */ IMediaEventSink_fnQueryInterface, IMediaEventSink_fnAddRef, IMediaEventSink_fnRelease, /* IMediaEventSink fields */ IMediaEventSink_fnNotify, }; HRESULT CFilterGraph_InitIMediaEventSink( CFilterGraph* pfg ) { TRACE("(%p)\n",pfg); ICOM_VTBL(&pfg->mediaeventsink) = &imediaeventsink; return NOERROR; } void CFilterGraph_UninitIMediaEventSink( CFilterGraph* pfg ) { TRACE("(%p)\n",pfg); }