X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/920a7c1510f56dfef5ce65bb15b6e87479beb407..f60b1d829dd8a6d5e47a2adcd3690acb3bd10150:/src/msw/mediactrl.cpp diff --git a/src/msw/mediactrl.cpp b/src/msw/mediactrl.cpp index 84f5bc6888..6d56bfa700 100644 --- a/src/msw/mediactrl.cpp +++ b/src/msw/mediactrl.cpp @@ -38,6 +38,9 @@ //--------------------------------------------------------------------------- #if wxUSE_MEDIACTRL +#include "wx/dcclient.h" +#include "wx/thread.h" + //--------------------------------------------------------------------------- // Externals (somewhere in src/msw/app.cpp) //--------------------------------------------------------------------------- @@ -67,28 +70,28 @@ extern WXDLLIMPEXP_CORE const wxChar *wxCanvasClassName; // Basically, the way directshow works is that you tell it to render // a file, and it builds and connects a bunch of filters together. // -// There are many, many ways to do this. +// There are many, many ways to do this. // // WAYS TO RENDER A FILE (URLS WORK IN DS ALSO) // // 1) Create an instance of IGraphBuilder and call RenderFile on it // 2) Create an instance of IMediaControl and call RenderFile on it -// 3) Create an instance of IAMMultiMediaStream, call +// 3) Create an instance of IAMMultiMediaStream, call // IAMMultiMediaStream::AddStream and pass an IDirectDraw instance for -// the video, and pass an IDirectSound(Buffer?) instance or use the +// the video, and pass an IDirectSound(Buffer?) instance or use the // default sound renderer, then call RenderFile or RenderMoniker // 4) Create a Moniker instance for the file and create and build // all of the filtergraph manually // // Our issue here is that we can't use the default representation of 1 and 2 -// because the IVideoWindow instance hogs a lot of the useful window +// because the IVideoWindow instance hogs a lot of the useful window // messages such as WM_SETCURSOR. // -// Solution #1 was to use #3 by creating a seperate IDirectDraw instance +// Solution #1 was to use #3 by creating a seperate IDirectDraw instance // for our window and blitting to that through a thread... unfortunately // the blitting resizing is very low quality and its quite slow. // -// The current way is to use windowless rendering and have directshow +// The current way is to use windowless rendering and have directshow // do all the DirectDraw-style clipping to our window // // ~~~~~~~~~~~~~~AFTER RENDERING THE FILE~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -96,7 +99,7 @@ extern WXDLLIMPEXP_CORE const wxChar *wxCanvasClassName; // When done rendering the file, we need to get several interfaces from // either a IMediaControl or IGraphBuilder instance - // -// IMediaPosition - we can set the rate with this... we can also get +// IMediaPosition - we can set the rate with this... we can also get // positions and set positions through this with REFTIME (double) instead // of the normal LONGLONG that IAMMultiMediaStream and IMediaControl use // @@ -104,7 +107,7 @@ extern WXDLLIMPEXP_CORE const wxChar *wxCanvasClassName; // // Interfaces that we don't use but might be useful one day - // -// IDirectDrawVideo - you can get this through the IFilter returned +// IDirectDrawVideo - you can get this through the IFilter returned // from L"Video Renderer" filter from FindFilter on the IGraphBuilder. // Through this we can set the IDirectDraw instance DrawShow uses. // @@ -113,7 +116,7 @@ extern WXDLLIMPEXP_CORE const wxChar *wxCanvasClassName; // There are two ways we can do this - // 1) Have a thread compare the current position to the end position // about every 10 milliseconds -// 2) Have IMediaSeekingEx send a message to a windowproc or signal a +// 2) Have IMediaSeekingEx send a message to a windowproc or signal a // windows event // // Note that we can do these both, I.E. if an IMediaSeekingEx interface @@ -141,7 +144,7 @@ extern WXDLLIMPEXP_CORE const wxChar *wxCanvasClassName; #endif //--------------------------------------------------------------------------- // IIDS - used by CoCreateInstance and IUnknown::QueryInterface -// Dumped from amstream.idl, quartz.idl, direct draw and with some +// Dumped from amstream.idl, quartz.idl, direct draw and with some // confirmation from WINE // // Some of these are not used but are kept here for future reference anyway @@ -206,31 +209,31 @@ typedef struct DDCAPS* LPDDCAPS; struct DDSURFACEDESC { - DWORD dwSize; - DWORD dwFlags; - DWORD dwHeight; - DWORD dwWidth; + DWORD dwSize; + DWORD dwFlags; + DWORD dwHeight; + DWORD dwWidth; union { - LONG lPitch; - DWORD dwLinearSize; + LONG lPitch; + DWORD dwLinearSize; }; - DWORD dwBackBufferCount; + DWORD dwBackBufferCount; union { - DWORD dwMipMapCount; - DWORD dwZBufferBitDepth; - DWORD dwRefreshRate; + DWORD dwMipMapCount; + DWORD dwZBufferBitDepth; + DWORD dwRefreshRate; }; - DWORD dwAlphaBitDepth; - DWORD dwReserved; - LPVOID lpSurface; - DDCOLORKEY ddckCKDestOverlay; - DDCOLORKEY ddckCKDestBlt; - DDCOLORKEY ddckCKSrcOverlay; - DDCOLORKEY ddckCKSrcBlt; - DDPIXELFORMAT ddpfPixelFormat; - struct DDSCAPS {DWORD dwCaps;} ddsCaps; + DWORD dwAlphaBitDepth; + DWORD dwReserved; + LPVOID lpSurface; + DDCOLORKEY ddckCKDestOverlay; + DDCOLORKEY ddckCKDestBlt; + DDCOLORKEY ddckCKSrcOverlay; + DDCOLORKEY ddckCKSrcBlt; + DDPIXELFORMAT ddpfPixelFormat; + struct DDSCAPS {DWORD dwCaps;} ddsCaps; }; struct IDirectDrawClipper : public IUnknown @@ -260,7 +263,7 @@ struct IDirectDrawSurface : public IUnknown STDMETHOD(GetClipper)(LPDIRECTDRAWCLIPPER*) PURE; STDMETHOD(GetColorKey)(DWORD, LPDDCOLORKEY) PURE; STDMETHOD(GetDC)(HDC *) PURE; - STDMETHOD(GetFlipStatus)(DWORD) PURE; + STDMETHOD(GetFlipStatus)(DWORD) PURE; STDMETHOD(GetOverlayPosition)(LPLONG, LPLONG ) PURE; STDMETHOD(GetPalette)(LPDIRECTDRAWPALETTE FAR*) PURE; STDMETHOD(GetPixelFormat)(LPDDPIXELFORMAT) PURE; @@ -273,7 +276,7 @@ struct IDirectDrawSurface : public IUnknown STDMETHOD(SetClipper)(LPDIRECTDRAWCLIPPER) PURE; STDMETHOD(SetColorKey)(DWORD, LPDDCOLORKEY) PURE; STDMETHOD(SetOverlayPosition)(LONG, LONG ) PURE; - STDMETHOD(SetPalette)(IUnknown*) PURE; + STDMETHOD(SetPalette)(IUnknown*) PURE; STDMETHOD(Unlock)(LPVOID) PURE; STDMETHOD(UpdateOverlay)(LPRECT, LPDIRECTDRAWSURFACE,LPRECT, DWORD, struct DDOVERLAYFX*) PURE; @@ -304,9 +307,9 @@ struct IDirectDraw : public IUnknown STDMETHOD(SetDisplayMode)(DWORD, DWORD,DWORD, DWORD, DWORD) PURE; STDMETHOD(WaitForVerticalBlank)(DWORD, HANDLE ) PURE; }; - + //--------------------------------------------------------------------------- -// AMMEDIA COM INTERFACES +// AMMEDIA COM INTERFACES //--------------------------------------------------------------------------- struct IMediaStream; struct IMultiMediaStream; @@ -330,10 +333,10 @@ public: struct IMediaStream : public IUnknown { STDMETHOD(GetMultiMediaStream)(IMultiMediaStream **) PURE; - STDMETHOD(GetInformation)(GUID *, int *) PURE; + STDMETHOD(GetInformation)(GUID *, int *) PURE; STDMETHOD(SetSameFormat)(IMediaStream *, DWORD) PURE; STDMETHOD(AllocateSample)(DWORD, IStreamSample **) PURE; - STDMETHOD(CreateSharedSample)(IStreamSample *, DWORD, + STDMETHOD(CreateSharedSample)(IStreamSample *, DWORD, IStreamSample **) PURE; STDMETHOD(SendEndOfStream)(DWORD dwFlags) PURE; }; @@ -353,7 +356,7 @@ struct IDirectDrawMediaStream : public IMediaStream struct IMultiMediaStream : public IUnknown { STDMETHOD(GetInformation)(DWORD *, int *) PURE; - STDMETHOD(GetMediaStream)(REFGUID, IMediaStream **) PURE; + STDMETHOD(GetMediaStream)(REFGUID, IMediaStream **) PURE; STDMETHOD(EnumMediaStreams)(long, IMediaStream **) PURE; STDMETHOD(GetState)(int *pCurrentState) PURE; STDMETHOD(SetState)(int NewState) PURE; @@ -368,7 +371,7 @@ struct IAMMultiMediaStream : public IMultiMediaStream STDMETHOD(Initialize)(int, DWORD, IUnknown *) PURE; STDMETHOD(GetFilterGraph)(IUnknown **) PURE; STDMETHOD(GetFilter)(IUnknown **) PURE; - STDMETHOD(AddMediaStream)(IUnknown *, const GUID*, DWORD, + STDMETHOD(AddMediaStream)(IUnknown *, const GUID*, DWORD, IMediaStream **) PURE; STDMETHOD(OpenFile)(LPCWSTR, DWORD) PURE; STDMETHOD(OpenMoniker)(IBindCtx *, IMoniker *, DWORD) PURE; @@ -446,7 +449,7 @@ struct IVMRWindowlessControl : public IUnknown STDMETHOD(GetNativeVideoSize)(LONG *, LONG *, LONG *, LONG *) PURE; STDMETHOD(GetMinIdealVideoSize)(LONG *, LONG *) PURE; STDMETHOD(GetMaxIdealVideoSize)(LONG *, LONG *) PURE; - STDMETHOD(SetVideoPosition)(const LPRECT,const LPRECT) PURE; + STDMETHOD(SetVideoPosition)(const LPRECT,const LPRECT) PURE; STDMETHOD(GetVideoPosition)(LPRECT, LPRECT) PURE; STDMETHOD(GetAspectRatioMode)(DWORD *) PURE; STDMETHOD(SetAspectRatioMode)(DWORD) PURE; @@ -470,13 +473,13 @@ struct IVMRFilterConfig : public IUnknown STDMETHOD(SetRenderingPrefs)(DWORD) PURE; STDMETHOD(GetRenderingPrefs)(DWORD *) PURE; STDMETHOD(SetRenderingMode)(DWORD) PURE; - STDMETHOD(GetRenderingMode)(DWORD *) PURE; + STDMETHOD(GetRenderingMode)(DWORD *) PURE; }; - + typedef IUnknown IBaseFilter; typedef IUnknown IPin; typedef IUnknown IEnumFilters; -typedef int AM_MEDIA_TYPE; +typedef int AM_MEDIA_TYPE; struct IFilterGraph : public IUnknown { @@ -518,8 +521,6 @@ class WXDLLIMPEXP_MEDIA wxAMMediaEvtHandler : public wxEvtHandler { public: void OnPaint(wxPaintEvent&); - void OnMove(wxMoveEvent&); - void OnSize(wxSizeEvent&); void OnEraseBackground(wxEraseEvent&); }; @@ -564,7 +565,7 @@ public: void Cleanup(); void OnStop(); - bool SetWindowlessMode(IGraphBuilder* pGB, + bool SetWindowlessMode(IGraphBuilder* pGB, IVMRWindowlessControl** ppVMC = NULL); wxControl* m_ctrl; @@ -578,7 +579,8 @@ public: IMediaControl* m_pMC; IMediaEvent* m_pME; IMediaPosition* m_pMS; - + bool m_bVideo; + wxAMMediaThread* m_pThread; wxSize m_bestSize; @@ -586,6 +588,7 @@ public: #ifdef __WXDEBUG__ HMODULE m_hQuartzDll; LPAMGETERRORTEXT m_lpAMGetErrorText; + wxString GetErrorString(HRESULT hrdsv); #endif friend class wxAMMediaThread; @@ -740,7 +743,8 @@ struct TimeRecord { #define wxDL_METHOD_LOAD( lib, name, success ) \ pfn_ ## name = (name ## Type) lib.GetSymbol( wxT(#name), &success ); \ - if (!success) return false; + if (!success) { wxLog::EnableLogging(bWasLoggingEnabled); return false; } + //Class that utilizes Robert Roeblings Dynamic Library Macros class WXDLLIMPEXP_MEDIA wxQuickTimeLibrary @@ -826,10 +830,15 @@ bool wxQuickTimeLibrary::Initialize() { m_ok = false; + bool bWasLoggingEnabled = wxLog::EnableLogging(false); //Turn off the wxDynamicLibrary logging + if(!m_dll.Load(wxT("qtmlClient.dll"))) + { + wxLog::EnableLogging(bWasLoggingEnabled); return false; + } - bool bOk; + bool bOk; //TODO: Get rid of this, use m_ok instead (not a biggie) wxDL_METHOD_LOAD( m_dll, StartMovie, bOk ); wxDL_METHOD_LOAD( m_dll, StopMovie, bOk ); @@ -867,6 +876,7 @@ bool wxQuickTimeLibrary::Initialize() wxDL_METHOD_LOAD( m_dll, GetMovieVolume, bOk ); wxDL_METHOD_LOAD( m_dll, SetMovieVolume, bOk ); + wxLog::EnableLogging(bWasLoggingEnabled); m_ok = true; return true; @@ -938,42 +948,39 @@ IMPLEMENT_DYNAMIC_CLASS(wxAMMediaBackend, wxMediaBackend); // Usual debugging macros //--------------------------------------------------------------------------- #ifdef __WXDEBUG__ -#include "wx/msgdlg.h" #define MAX_ERROR_TEXT_LEN 160 -// -// FIXME : Just use wxASSERT_MSG here instead of msgdlg - but -// stackwalker still crashes win2k, so do msgdlg for now... -// -#define wxAMVERIFY(x) \ -{ \ - HRESULT hrdsv = (x); \ - if ( FAILED(hrdsv) ) \ - { \ - wxChar szError[MAX_ERROR_TEXT_LEN]; \ - if( m_lpAMGetErrorText != NULL && \ - (*m_lpAMGetErrorText)(hrdsv, szError, MAX_ERROR_TEXT_LEN) == 0) \ - { \ - wxMessageBox( wxString::Format(wxT("DirectShow error \"%s\" \n")\ - wxT("(numeric %i)\n")\ - wxT("occured at line %i in ") \ - wxT("mediactrl.cpp"), \ - (int)hrdsv, szError, __LINE__) ); \ - wxASSERT(false);\ - } \ - else \ - { \ - wxMessageBox( wxString::Format(wxT("Unknown error (%i) ") \ - wxT("occurred at") \ - wxT(" line %i in mediactrl.cpp."), \ - (int)hrdsv, __LINE__) ); \ - wxASSERT(false);\ - } \ - } \ +#include "wx/log.h" //wxLogDebug et al. + +//Get the error string for Active Movie +wxString wxAMMediaBackend::GetErrorString(HRESULT hrdsv) +{ + wxChar szError[MAX_ERROR_TEXT_LEN]; + if( m_lpAMGetErrorText != NULL && + (*m_lpAMGetErrorText)(hrdsv, szError, MAX_ERROR_TEXT_LEN) == 0) + { + return wxString::Format(wxT("DirectShow error \"%s\" \n") + wxT("(numeric %i)\n") + wxT("occured at line %i in ") + wxT("mediactrl.cpp"), + szError, (int)hrdsv, __LINE__); + } + else + { + return wxString::Format(wxT("Unknown error (%i) ") + wxT("occurred at") + wxT(" line %i in mediactrl.cpp."), + (int)hrdsv, __LINE__); + } } + +#define wxAMFAIL(x) wxFAIL_MSG(GetErrorString(x)); #define wxVERIFY(x) wxASSERT((x)) +#define wxAMLOG(x) wxLogDebug(GetErrorString(x)) #else #define wxAMVERIFY(x) (x) #define wxVERIFY(x) (x) +#define wxAMLOG(x) +#define wxAMFAIL(x) #endif //--------------------------------------------------------------------------- @@ -986,11 +993,18 @@ IMPLEMENT_DYNAMIC_CLASS(wxAMMediaBackend, wxMediaBackend); // // Sets m_hNotifyWnd to NULL to signify that we haven't loaded anything yet //--------------------------------------------------------------------------- -wxAMMediaBackend::wxAMMediaBackend() : m_state(wxMEDIASTATE_STOPPED), -m_pVMC(NULL) - #ifdef __WXDEBUG__ - , m_hQuartzDll(NULL) - #endif +wxAMMediaBackend::wxAMMediaBackend() + :m_state(wxMEDIASTATE_STOPPED) + ,m_pVMC(NULL) + ,m_pGB(NULL) + ,m_pBA(NULL) + ,m_pMC(NULL) + ,m_pME(NULL) + ,m_pMS(NULL) + ,m_pThread(NULL) +#ifdef __WXDEBUG__ + ,m_hQuartzDll(NULL) +#endif { } @@ -1047,7 +1061,7 @@ bool wxAMMediaBackend::CreateControl(wxControl* ctrl, wxWindow* parent, //Make sure a valid windowless video mixing interface exists IGraphBuilder* pGB; - if( CoCreateInstance(CLSID_FilgraphManager, NULL, + if( ::CoCreateInstance(CLSID_FilgraphManager, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void**)&pGB) != 0 ) return false; @@ -1070,51 +1084,20 @@ bool wxAMMediaBackend::CreateControl(wxControl* ctrl, wxWindow* parent, validator, name) ) return false; - m_ctrl = ctrl; - - // - // Connect Events - // -//TODO: Greg Hazel reports problems with this... but win2k seems fine on mine... -// m_ctrl->Connect(m_ctrl->GetId(), wxEVT_ERASE_BACKGROUND, -// wxEraseEventHandler(wxAMMediaEvtHandler::OnEraseBackground), -// NULL, (wxEvtHandler*) this); - m_ctrl->Connect(m_ctrl->GetId(), wxEVT_PAINT, - wxPaintEventHandler(wxAMMediaEvtHandler::OnPaint), + // My problem with this was only with a previous patch, probably the third rewrite + // fixed it as a side-effect. In fact, the erase background style of drawing not + // only works now, but is much better than paint-based updates (the paint event + // handler flickers if the wxMediaCtrl shares a sizer with another child window, + // or is on a notebook) + // - Greg Hazel + ctrl->Connect(ctrl->GetId(), wxEVT_ERASE_BACKGROUND, + wxEraseEventHandler(wxAMMediaEvtHandler::OnEraseBackground), NULL, (wxEvtHandler*) this); - // - // As noted below, we need to catch the Top Level Window's - // move events because they are not sent to us if the media control - // size remains the same but it actually moves in window coordinates - // - wxWindow* pTheTLW = m_ctrl->GetParent(); - while( pTheTLW->GetParent() ) - pTheTLW = pTheTLW->GetParent(); - - // - // FIXMEHACKFIXMEHACKFIXME - // This is really nasty... basically the deal is not only above - // but the repainting is messed up when the parent is maximized - // too, so we've got to catch all 4 events! - // - m_ctrl->Connect(m_ctrl->GetId(), wxEVT_MOVE, - wxMoveEventHandler(wxAMMediaEvtHandler::OnMove), - NULL, (wxEvtHandler*) this); - m_ctrl->Connect(m_ctrl->GetId(), wxEVT_SIZE, - wxSizeEventHandler(wxAMMediaEvtHandler::OnSize), - NULL, (wxEvtHandler*) this); - - pTheTLW->Connect(pTheTLW->GetId(), wxEVT_MOVE, - wxMoveEventHandler(wxAMMediaEvtHandler::OnMove), - NULL, (wxEvtHandler*) this); - pTheTLW->Connect(pTheTLW->GetId(), wxEVT_SIZE, - wxSizeEventHandler(wxAMMediaEvtHandler::OnSize), - NULL, (wxEvtHandler*) this); - // // done... // + m_ctrl = ctrl; return true; } @@ -1124,19 +1107,23 @@ bool wxAMMediaBackend::CreateControl(wxControl* ctrl, wxWindow* parent, // Adds a Video Mixing Renderer to a Filter Graph and obtains the // windowless control from it //--------------------------------------------------------------------------- -bool wxAMMediaBackend::SetWindowlessMode(IGraphBuilder* pGB, +bool wxAMMediaBackend::SetWindowlessMode(IGraphBuilder* pGB, IVMRWindowlessControl** ppVMC) { + HRESULT hr; + // // Create and add a custom Video Mixing Render to the graph // IBaseFilter* pVMR; - if( CoCreateInstance(CLSID_VideoMixingRenderer, NULL, CLSCTX_INPROC_SERVER, - IID_IBaseFilter, (void**)&pVMR) != 0 ) + if( ::CoCreateInstance(CLSID_VideoMixingRenderer, NULL, + CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&pVMR) != 0 ) return false; - if ( pGB->AddFilter(pVMR, L"Video Mixing Renderer") != 0) + hr = pGB->AddFilter(pVMR, L"Video Mixing Renderer"); + if ( hr != 0) { + wxAMLOG(hr); pVMR->Release(); return false; } @@ -1144,29 +1131,35 @@ bool wxAMMediaBackend::SetWindowlessMode(IGraphBuilder* pGB, // // Set the graph to windowless mode // - IVMRFilterConfig* pConfig; - if( pVMR->QueryInterface(IID_IVMRFilterConfig, (void**)&pConfig) != 0 ) + IVMRFilterConfig* pConfig; + hr = pVMR->QueryInterface(IID_IVMRFilterConfig, (void**)&pConfig); + if( hr != 0 ) { + wxAMLOG(hr); pVMR->Release(); return false; } - if( pConfig->SetRenderingMode(2) != 0) //2 == VMRMode_Windowless + hr = pConfig->SetRenderingMode(2); + if( hr != 0) //2 == VMRMode_Windowless { - pConfig->Release(); + wxAMLOG(hr); + pConfig->Release(); pVMR->Release(); return false; } - pConfig->Release(); + pConfig->Release(); // // Obtain the windowless control // IVMRWindowlessControl* pVMC; - if( pVMR->QueryInterface(IID_IVMRWindowlessControl, (void**)&pVMC) != 0 ) + hr = pVMR->QueryInterface(IID_IVMRWindowlessControl, (void**)&pVMC); + if( hr != 0 ) { - pVMR->Release(); + wxAMLOG(hr); + pVMR->Release(); return false; } @@ -1178,8 +1171,8 @@ bool wxAMMediaBackend::SetWindowlessMode(IGraphBuilder* pGB, else pVMC->Release(); - pVMR->Release(); - return true; + pVMR->Release(); + return true; } //--------------------------------------------------------------------------- @@ -1196,41 +1189,80 @@ bool wxAMMediaBackend::SetWindowlessMode(IGraphBuilder* pGB, //--------------------------------------------------------------------------- bool wxAMMediaBackend::Load(const wxString& fileName) { + HRESULT hr; + //if previously loaded cleanup if(m_pVMC) Cleanup(); //Create interfaces - we already checked for success in CreateControl - CoCreateInstance(CLSID_FilgraphManager, NULL, CLSCTX_INPROC_SERVER, + ::CoCreateInstance(CLSID_FilgraphManager, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void**)&m_pGB); // Set and clip output SetWindowlessMode(m_pGB, &m_pVMC); - if( m_pVMC->SetVideoClippingWindow((HWND)m_ctrl->GetHandle()) != 0 ) + hr = m_pVMC->SetVideoClippingWindow((HWND)m_ctrl->GetHandle()); + + if(hr != 0) { m_bestSize.x = m_bestSize.y = 0; - wxASSERT(false); + wxAMFAIL(hr); + return false; } //load the graph & render if( m_pGB->RenderFile(fileName.wc_str(wxConvLocal), NULL) != 0 ) return false; + // //Get the interfaces, all of them - wxAMVERIFY( m_pGB->QueryInterface(IID_IMediaEvent, (void**)&m_pME) ); - wxAMVERIFY( m_pGB->QueryInterface(IID_IMediaControl, (void**)&m_pMC) ); - wxAMVERIFY( m_pGB->QueryInterface(IID_IMediaPosition, (void**)&m_pMS) ); - wxAMVERIFY( m_pGB->QueryInterface(IID_IBasicAudio, (void**)&m_pBA) ); + // + hr = m_pGB->QueryInterface(IID_IMediaEvent, (void**)&m_pME); + if(FAILED(hr)) + { + wxAMLOG(hr); + return false; + } + + hr = m_pGB->QueryInterface(IID_IMediaControl, (void**)&m_pMC); + if(FAILED(hr)) + { + wxAMLOG(hr); + return false; + } + hr = m_pGB->QueryInterface(IID_IMediaPosition, (void**)&m_pMS); + if(FAILED(hr)) + { + wxAMLOG(hr); + return false; + } + + hr = m_pGB->QueryInterface(IID_IBasicAudio, (void**)&m_pBA); + if(FAILED(hr)) + { + wxAMLOG(hr); + //not critical + } + + // // Get original video size - if( m_pVMC->GetNativeVideoSize((LONG*)&m_bestSize.x, (LONG*)&m_bestSize.y, - NULL, NULL) != 0 ) + // + hr = m_pVMC->GetNativeVideoSize((LONG*)&m_bestSize.x, (LONG*)&m_bestSize.y, + NULL, NULL); + if(hr != 0) { m_bestSize.x = m_bestSize.y = 0; - wxASSERT(false); + wxAMFAIL(hr); + return false; } + if(m_bestSize.x == 0 && m_bestSize.y == 0) + m_bVideo = false; + else + m_bVideo = true; + // // Force the parent window of this control to recalculate // the size of this if sizers are being used @@ -1242,11 +1274,17 @@ bool wxAMMediaBackend::Load(const wxString& fileName) m_ctrl->GetParent()->Update(); m_ctrl->SetSize(m_ctrl->GetSize()); + // + // Create the event thread + // m_pThread = new wxAMMediaThread; m_pThread->pThis = this; m_pThread->Create(); m_pThread->Run(); + // + // done + // return true; } @@ -1262,6 +1300,33 @@ bool wxAMMediaBackend::Load(const wxURI& location) return Load(location.BuildUnescapedURI()); } +//--------------------------------------------------------------------------- +// wxAMMediaBackend::Cleanup +// +// Releases all the directshow interfaces we use +// TODO: Maybe only create one instance of IAMMultiMediaStream and reuse it +// rather than recreating it each time? +//--------------------------------------------------------------------------- +void wxAMMediaBackend::Cleanup() +{ + // RN: This could be a bad ptr if load failed after + // m_pVMC was created + if(m_pThread) + { + m_pThread->Delete(); + m_pThread = NULL; + } + + // Release and zero DirectShow interfaces + SAFE_RELEASE(m_pMC); + SAFE_RELEASE(m_pME); + SAFE_RELEASE(m_pMS); + SAFE_RELEASE(m_pBA); + SAFE_RELEASE(m_pGB); + SAFE_RELEASE(m_pVMC); +} + + //--------------------------------------------------------------------------- // wxAMMediaBackend::Play // @@ -1336,9 +1401,16 @@ bool wxAMMediaBackend::Stop() //--------------------------------------------------------------------------- bool wxAMMediaBackend::SetPosition(wxLongLong where) { - return SUCCEEDED( m_pMS->put_CurrentPosition( + HRESULT hr = m_pMS->put_CurrentPosition( ((LONGLONG)where.GetValue()) / 1000.0 - ) ); + ); + if(FAILED(hr)) + { + wxAMLOG(hr); + return false; + } + + return true; } //--------------------------------------------------------------------------- @@ -1350,7 +1422,12 @@ bool wxAMMediaBackend::SetPosition(wxLongLong where) wxLongLong wxAMMediaBackend::GetPosition() { double outCur; - wxAMVERIFY( m_pMS->get_CurrentPosition(&outCur) ); + HRESULT hr = m_pMS->get_CurrentPosition(&outCur); + if(FAILED(hr)) + { + wxAMLOG(hr); + return 0; + } //h,m,s,milli - outdur is in 1 second (double) outCur *= 1000; @@ -1361,30 +1438,53 @@ wxLongLong wxAMMediaBackend::GetPosition() } //--------------------------------------------------------------------------- -// wxAMMediaBackend::SetVolume +// wxAMMediaBackend::GetVolume // -// Sets the volume through the IBasicAudio interface - +// Gets the volume through the IBasicAudio interface - // value ranges from 0 (MAX volume) to -10000 (minimum volume). // -100 per decibel. //--------------------------------------------------------------------------- -bool wxAMMediaBackend::SetVolume(double dVolume) +double wxAMMediaBackend::GetVolume() { - return SUCCEEDED(m_pBA->put_Volume( (long) ((dVolume-1.0) * 10000.0) ) ); + if(m_pBA) + { + long lVolume; + HRESULT hr = m_pBA->get_Volume(&lVolume); + if(FAILED(hr)) + { + wxAMLOG(hr); + return 0.0; + } + + return (((double)(lVolume + 10000)) / 10000.0); + } + + wxLogDebug(wxT("No directshow audio interface")); + return 0.0; } //--------------------------------------------------------------------------- -// wxAMMediaBackend::GetVolume +// wxAMMediaBackend::SetVolume // -// Gets the volume through the IBasicAudio interface - +// Sets the volume through the IBasicAudio interface - // value ranges from 0 (MAX volume) to -10000 (minimum volume). // -100 per decibel. //--------------------------------------------------------------------------- -double wxAMMediaBackend::GetVolume() +bool wxAMMediaBackend::SetVolume(double dVolume) { - long lVolume; - if ( SUCCEEDED( m_pBA->get_Volume(&lVolume) ) ) - return (((double)(lVolume + 10000)) / 10000.0); - return 0.0; + if(m_pBA) + { + HRESULT hr = m_pBA->put_Volume( (long) ((dVolume-1.0) * 10000.0) ); + if(FAILED(hr)) + { + wxAMLOG(hr); + return false; + } + return true; + } + + wxLogDebug(wxT("No directshow audio interface")); + return false; } //--------------------------------------------------------------------------- @@ -1393,14 +1493,19 @@ double wxAMMediaBackend::GetVolume() // 1) Obtains the duration of the media from IAMMultiMediaStream // 2) Converts that value to our time base, and returns it // -// NB: With VBR MP3 files the default DirectShow MP3 render does not +// NB: With VBR MP3 files the default DirectShow MP3 render does not // read the Xing header correctly, resulting in skewed values for duration // and seeking //--------------------------------------------------------------------------- wxLongLong wxAMMediaBackend::GetDuration() { double outDuration; - wxAMVERIFY( m_pMS->get_Duration(&outDuration) ); + HRESULT hr = m_pMS->get_Duration(&outDuration); + if(FAILED(hr)) + { + wxAMLOG(hr); + return 0; + } //h,m,s,milli - outdur is in 1 second (double) outDuration *= 1000; @@ -1429,7 +1534,12 @@ wxMediaState wxAMMediaBackend::GetState() double wxAMMediaBackend::GetPlaybackRate() { double dRate; - wxAMVERIFY( m_pMS->get_Rate(&dRate) ); + HRESULT hr = m_pMS->get_Rate(&dRate); + if(FAILED(hr)) + { + wxAMLOG(hr); + return 0.0; + } return dRate; } @@ -1441,31 +1551,16 @@ double wxAMMediaBackend::GetPlaybackRate() //--------------------------------------------------------------------------- bool wxAMMediaBackend::SetPlaybackRate(double dRate) { - return m_pMS->put_Rate(dRate) == 0; -} - -//--------------------------------------------------------------------------- -// wxAMMediaBackend::Cleanup -// -// Releases all the directshow interfaces we use -// TODO: Maybe only create one instance of IAMMultiMediaStream and reuse it -// rather than recreating it each time? -//--------------------------------------------------------------------------- -void wxAMMediaBackend::Cleanup() -{ - m_pThread->Delete(); - m_pThread = NULL; + HRESULT hr = m_pMS->put_Rate(dRate); + if(FAILED(hr)) + { + wxAMLOG(hr); + return false; + } - // Release and zero DirectShow interfaces - SAFE_RELEASE(m_pMC); - SAFE_RELEASE(m_pME); - SAFE_RELEASE(m_pMS); - SAFE_RELEASE(m_pBA); - SAFE_RELEASE(m_pGB); - SAFE_RELEASE(m_pVMC); + return true; } - //--------------------------------------------------------------------------- // wxAMMediaBackend::GetVideoSize // @@ -1481,66 +1576,41 @@ wxSize wxAMMediaBackend::GetVideoSize() const // // We take care of this in our redrawing //--------------------------------------------------------------------------- -void wxAMMediaBackend::Move(int WXUNUSED(x), int WXUNUSED(y), +void wxAMMediaBackend::Move(int WXUNUSED(x), int WXUNUSED(y), int w, int h) { - if(m_pVMC) + //don't use deferred positioning on windows + if(m_pVMC && m_bVideo) { RECT srcRect, destRect; - + //portion of video to display in window srcRect.top = 0; srcRect.left = 0; srcRect.bottom = m_bestSize.y; srcRect.right = m_bestSize.x; + //it happens. + if (w < 0) + { + w = 0; + } + if (h < 0) + { + h = 0; + } + //position in window client coordinates to display and stretch to destRect.top = 0; destRect.left = 0; destRect.bottom = h; destRect.right = w; //set the windowless control positions - if( m_pVMC->SetVideoPosition(&srcRect, &destRect) != 0 ) + HRESULT hr = m_pVMC->SetVideoPosition(&srcRect, &destRect); + if(FAILED(hr)) { - wxASSERT_MSG(false, wxT("Could not set video position!")); + wxAMLOG(hr); } - -/* - //oddly enough, it doesn't redraw the frame after moving... - //TODO: Use wxClientDC? - HDC hdc = ::GetDC((HWND)m_ctrl->GetHandle()); - if( m_pVMC->RepaintVideo((HWND)m_ctrl->GetHandle(), - hdc) != 0 ) - { - wxASSERT(false); - } - ::ReleaseDC((HWND)m_ctrl->GetHandle(), hdc); -*/ } } -//--------------------------------------------------------------------------- -// wxAMMediaEvtHandler::OnMove -// -// Oddly enough Move isn't called on MSW when the parent moves -// and the child (us) doesn't, so we have to do it twice I guess :( -//--------------------------------------------------------------------------- -void wxAMMediaEvtHandler::OnMove(wxMoveEvent& evt) -{ - wxAMMediaBackend* pThis = (wxAMMediaBackend*) this; - pThis->Move(pThis->m_ctrl->GetPosition().x, - pThis->m_ctrl->GetPosition().y, - pThis->m_ctrl->GetSize().x, - pThis->m_ctrl->GetSize().y ); - evt.Skip(); -} -void wxAMMediaEvtHandler::OnSize(wxSizeEvent& evt) -{ - wxAMMediaBackend* pThis = (wxAMMediaBackend*) this; - pThis->Move(pThis->m_ctrl->GetPosition().x, - pThis->m_ctrl->GetPosition().y, - pThis->m_ctrl->GetSize().x, - pThis->m_ctrl->GetSize().y ); - evt.Skip(); -} - //--------------------------------------------------------------------------- // wxAMMediaThread::Entry // @@ -1563,13 +1633,17 @@ wxThread::ExitCode wxAMMediaThread::Entry() (LONG_PTR *) &evParam2, 0) == 0 ) { // Cleanup memory that GetEvent allocated - if( pThis->m_pME->FreeEventParams(evCode, evParam1, evParam2) != 0 ) + HRESULT hr = pThis->m_pME->FreeEventParams(evCode, + evParam1, evParam2); + if(hr != 0) { - wxASSERT(false); + //Even though this makes a messagebox this + //is windows where we can do gui stuff in seperate + //threads :) + wxFAIL_MSG(pThis->GetErrorString(hr)); } - // If this is the end of the clip, notify handler - if(1 == evCode) //EC_COMPLETE + else if(1 == evCode) //EC_COMPLETE { pThis->OnStop(); } @@ -1589,22 +1663,22 @@ wxThread::ExitCode wxAMMediaThread::Entry() //--------------------------------------------------------------------------- void wxAMMediaBackend::OnStop() { - //send the event to our child - wxMediaEvent theEvent(wxEVT_MEDIA_STOP, m_ctrl->GetId()); - m_ctrl->ProcessEvent(theEvent); + //send the event to our child + wxMediaEvent theEvent(wxEVT_MEDIA_STOP, m_ctrl->GetId()); + m_ctrl->ProcessEvent(theEvent); - //if the user didn't veto it, stop the stream - if (theEvent.IsAllowed()) - { - //Interestingly enough, DirectShow does not actually stop - //the filters - even when it reaches the end! - wxVERIFY( Stop() ); - - //send the event to our child - wxMediaEvent theEvent(wxEVT_MEDIA_FINISHED, - m_ctrl->GetId()); - m_ctrl->ProcessEvent(theEvent); - } + //if the user didn't veto it, stop the stream + if (theEvent.IsAllowed()) + { + //Interestingly enough, DirectShow does not actually stop + //the filters - even when it reaches the end! + wxVERIFY( Stop() ); + + //send the event to our child + wxMediaEvent theEvent(wxEVT_MEDIA_FINISHED, + m_ctrl->GetId()); + m_ctrl->ProcessEvent(theEvent); + } } //--------------------------------------------------------------------------- @@ -1615,34 +1689,21 @@ void wxAMMediaBackend::OnStop() void wxAMMediaEvtHandler::OnEraseBackground(wxEraseEvent& evt) { wxAMMediaBackend* pThis = (wxAMMediaBackend*) this; - if(pThis->m_pVMC) + if(pThis->m_pVMC && pThis->m_bVideo) { //TODO: Use wxClientDC? HDC hdc = ::GetDC((HWND)pThis->m_ctrl->GetHandle()); - if( pThis->m_pVMC->RepaintVideo((HWND)pThis->m_ctrl->GetHandle(), - hdc) != 0 ) + HRESULT hr = pThis->m_pVMC->RepaintVideo((HWND)pThis->m_ctrl->GetHandle(), + hdc); + if(FAILED(hr)) { - wxASSERT(false); - } + wxFAIL_MSG(pThis->GetErrorString(hr)); + } ::ReleaseDC((HWND)pThis->m_ctrl->GetHandle(), hdc); } else - evt.Skip(); -} - -//--------------------------------------------------------------------------- -// wxAMMediaEvtHandler::OnPaint -// -// Handle redrawing -//--------------------------------------------------------------------------- -void wxAMMediaEvtHandler::OnPaint(wxPaintEvent& WXUNUSED(evt)) -{ - wxAMMediaBackend* pThis = (wxAMMediaBackend*) this; - wxPaintDC dc(pThis->m_ctrl); - if( pThis->m_pVMC->RepaintVideo((HWND)pThis->m_ctrl->GetHandle(), - (HDC)dc.GetHDC()) != 0 ) { - wxASSERT(false); + evt.Skip(); } }