// MediaBarPlayer.cpp #include "priv.h" #include "dispex.h" #include "player.h" #include "resource.h" #include "math.h" #include "mediautil.h" #include "mbBehave.h" #define WZ_PLAY L"beginElement" #define WZ_STOP L"endElement" #define WZ_PAUSE L"pauseElement" #define WZ_RESUME L"resumeElement" #define WZ_URL L"src" #define WZ_VOLUME L"volume" #define WZ_MUTE L"mute" #define WZ_REGISTERED_TIME_NAME L"HTMLTIME" #define WZ_BODY L"body" #define WZ_PLAYER L"player" #define WZ_ONMEDIACOMPLETE L"onmediacomplete" #define WZ_MEDIACOMPLETE L"mediacomplete" #define WZ_ONMEDIAERROR L"onmediaerror" #define WZ_MEDIAERROR L"mediaerror" #define WZ_ONTRACKCHANGE L"ontrackchange" #define WZ_TRACKCHANGE L"trackchange" #define WZ_ONEND L"onend" #define WZ_END L"end" #define WZ_PARAM L"Param" /////////////////////// Convenience macros //////////////////// // // used in QI calls, // e.g. IOleSite * pSite; p->QI( IID_TO_PPV(IOleInPlaceSite, &pSite) ) // would cause a C2440 as _src is not really a _type **. // Note: the riid must be the _type prepended by IID_. // #define IID_TO_PPV(_type,_src) IID_##_type, \ reinterpret_cast(static_cast<_type **>(_src)) // Explicit directive to ignore a return value #define IGNORE_RETURN(_call) static_cast((_call)) #define ERROREXIT(hr) if(FAILED(hr)){hr = E_FAIL; goto done;} #define IGNORE_HR(hr) IGNORE_RETURN(hr) #define TIME_INFINITE HUGE_VAL static inline double Clamp(double min, double val, double max) { if (val < min) { val = min; } else if (val > max) { val = max; } return val; } /****************************************************\ FUNCTION: CMediaBarPlayer_CreateInstance DESCRIPTION: This function will create an instance of the MediaBarPlayer COM object. \****************************************************/ HRESULT CMediaBarPlayer_CreateInstance(REFIID riid, void ** ppvObj) { // aggregation checking is handled in class factory CComObject * pMediaBarPlayer = NULL; *ppvObj = NULL; HRESULT hr = CComObject::CreateInstance(&pMediaBarPlayer); if (FAILED(hr)) return hr; hr = pMediaBarPlayer->QueryInterface(riid, ppvObj); if (FAILED(hr)) delete pMediaBarPlayer; return hr; } //+------------------------------------------------------------------------- // Constructor //-------------------------------------------------------------------------- CMediaBarPlayer::CMediaBarPlayer() : _dwDocumentEventConPtCookie(0), _dwCookiePropNotify(0), _pMediaBar(NULL), _hwnd(NULL) { } //+------------------------------------------------------------------------- // Destructor //-------------------------------------------------------------------------- CMediaBarPlayer::~CMediaBarPlayer() { _DestroyHost(); } //+------------------------------------------------------------------------- // Creates the control host //-------------------------------------------------------------------------- STDMETHODIMP CMediaBarPlayer::_CreateHost(HWND hWnd) { HRESULT hr = E_FAIL; if (_spBrowser.p) { hr = E_FAIL; goto done; } if (hWnd && ::IsWindow(hWnd) && _pMediaBar) { // Register the OCHost window class SHDRC shdrc = {sizeof(SHDRC), SHDRCF_OCHOST}; shdrc.cbSize = sizeof (SHDRC); shdrc.dwFlags |= SHDRCF_OCHOST; if (DllRegisterWindowClasses(&shdrc)) { // Create an OCHost window _hwnd = CreateWindow(OCHOST_CLASS, NULL, (WS_CHILD|WS_CLIPCHILDREN|WS_CLIPSIBLINGS) & ~(WS_HSCROLL|WS_VSCROLL), 0, 0, 0, 0, hWnd, NULL, HINST_THISDLL, NULL); if (_hwnd) { OCHINITSTRUCT ocs; ocs.cbSize = SIZEOF(OCHINITSTRUCT); ocs.clsidOC = CLSID_WebBrowser; ocs.punkOwner = SAFECAST(_pMediaBar, IUnknown*); hr = OCHost_InitOC(_hwnd, (LPARAM)&ocs); ERROREXIT(hr) OCHost_QueryInterface(_hwnd, IID_PPV_ARG(IWebBrowser2, &_spBrowser)); OCHost_DoVerb(_hwnd, OLEIVERB_INPLACEACTIVATE, FALSE); } } } if (!_spBrowser.p) { hr = E_FAIL; goto done; } // navigate to the page containing the player { TCHAR szModule[_MAX_PATH]; GetModuleFileName(_Module.GetModuleInstance(), szModule, _MAX_PATH); // generate the url for the HTML resource containing the player CComBSTR sbstrURL(OLESTR("res://")); sbstrURL.Append(szModule); sbstrURL.Append(OLESTR("/")); TCHAR szResID[11]; wnsprintf(szResID, _MAX_PATH, _T("%d"), IDH_PLAYER); sbstrURL.Append(szResID); hr = _Navigate(sbstrURL.m_str); ERROREXIT(hr) } // listen to events hr = _InitEventSink(); ERROREXIT(hr) hr = S_OK; done: if (FAILED(hr)) { _DestroyHost(); } return hr; } STDMETHODIMP CMediaBarPlayer::_Navigate(BSTR bstrUrl) { HRESULT hr = E_FAIL; CComVariant svarEmpty; if (!bstrUrl || !_spBrowser.p) { hr = E_FAIL; goto done; } hr = _spBrowser->Navigate(bstrUrl, &svarEmpty, &svarEmpty, &svarEmpty, &svarEmpty); ERROREXIT(hr) hr = S_OK; done: return hr; } //+------------------------------------------------------------------------- // Destroys the control host //-------------------------------------------------------------------------- STDMETHODIMP CMediaBarPlayer::_DestroyHost() { _AttachPlayerEvents(FALSE); _UnhookPropNotifies(); _DeInitEventSink(); if (_spMediaElem.p) { _spMediaElem.Release(); } if (_spMediaElem2.p) { _spMediaElem2.Release(); } if (_spPlayerHTMLElem2.p) { _spPlayerHTMLElem2.Release(); } if (_spBodyElem.p) { _spBodyElem.Release(); } if (_spBrowser.p) { _spBrowser.Release(); } if (_hwnd && ::IsWindow(_hwnd)) { ::DestroyWindow(_hwnd); _hwnd = NULL; } return S_OK; } STDMETHODIMP CMediaBarPlayer::GetVideoHwnd(HWND * pHwnd) { if (pHwnd) *pHwnd = _hwnd; return S_OK; } static const PWSTR ppszInterestingEvents[] = { WZ_ONMEDIACOMPLETE, WZ_ONMEDIAERROR, WZ_ONEND, WZ_ONTRACKCHANGE }; //+------------------------------------------------------------------------- // Attaches to player events //-------------------------------------------------------------------------- STDMETHODIMP CMediaBarPlayer::_AttachPlayerEvents(BOOL fAttach) { HRESULT hr = E_FAIL; CComPtr spDispEx; CComPtr spElem2; hr = _GetElementDispatch(WZ_PLAYER, &spDispEx); ERROREXIT(hr) hr = spDispEx->QueryInterface(IID_TO_PPV(IHTMLElement2, &spElem2)); ERROREXIT(hr) hr = S_OK; for (DWORD i = 0; i < ARRAYSIZE(ppszInterestingEvents); i++) { if (fAttach) { VARIANT_BOOL bSuccess = FALSE; // Try to attach all events. We don't care if they fail if (FAILED(spElem2->attachEvent(ppszInterestingEvents[i], static_cast(this), &bSuccess))) { hr = S_FALSE; } } else { // Try to detact all events. We don't care if they fail hr = spElem2->detachEvent(ppszInterestingEvents[i], static_cast(this)); } } done: return hr; } //+------------------------------------------------------------------------- // Hooks property change notifications //-------------------------------------------------------------------------- STDMETHODIMP CMediaBarPlayer::_HookPropNotifies() { HRESULT hr = E_FAIL; CComPtr spTimeState; CComPtr spConPtCont; if (!_spMediaElem || _spPropNotifyCP.p || _dwCookiePropNotify) { hr = E_FAIL; goto done; } hr = _spMediaElem->get_currTimeState(&spTimeState); ERROREXIT(hr) hr = spTimeState->QueryInterface(IID_TO_PPV(IConnectionPointContainer, &spConPtCont)); ERROREXIT(hr) hr = spConPtCont->FindConnectionPoint(IID_IPropertyNotifySink, &_spPropNotifyCP); ERROREXIT(hr) hr = _spPropNotifyCP->Advise(static_cast(static_cast(this)), &_dwCookiePropNotify); ERROREXIT(hr) hr = S_OK; done: return hr; } //+------------------------------------------------------------------------- // Unhooks property change notifications //-------------------------------------------------------------------------- STDMETHODIMP CMediaBarPlayer::_UnhookPropNotifies() { if (_spPropNotifyCP.p) { if (_dwCookiePropNotify != 0) { IGNORE_HR(_spPropNotifyCP->Unadvise(_dwCookiePropNotify)); } _spPropNotifyCP.Release(); } _dwCookiePropNotify = 0; return S_OK; } //+------------------------------------------------------------------------- // Invokes a method on the given element //-------------------------------------------------------------------------- STDMETHODIMP CMediaBarPlayer::_InvokeDocument(LPWSTR pstrElem, INVOKETYPE it, LPWSTR pstrName, VARIANT * pvarArg) { HRESULT hr = E_FAIL; DISPPARAMS dispparams = {NULL, NULL, 0, 0}; CComPtr spDispEx; CComBSTR sbstrName; DISPID dispid = 0; DISPID dispidProp = 0; sbstrName.m_str = SysAllocString(pstrName); if (!sbstrName.m_str) { hr = E_OUTOFMEMORY; goto done; } hr = _GetElementDispatch(pstrElem, &spDispEx); ERROREXIT(hr) hr = spDispEx->GetDispID(sbstrName, fdexNameEnsure, &dispid); ERROREXIT(hr) switch (it) { case IT_METHOD: { dispparams.rgvarg = pvarArg; dispparams.cArgs = (pvarArg ? 1 : 0); hr = spDispEx->InvokeEx(dispid, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispparams, NULL, NULL, NULL); ERROREXIT(hr) } break; case IT_PUT: { dispidProp = DISPID_PROPERTYPUT; if (!pvarArg) { hr = E_INVALIDARG; goto done; } dispparams.rgvarg = pvarArg; dispparams.rgdispidNamedArgs = &dispidProp; dispparams.cArgs = 1; dispparams.cNamedArgs = 1; hr = spDispEx->InvokeEx(dispid, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUT, &dispparams, NULL, NULL, NULL); ERROREXIT(hr) } break; case IT_GET: { if (!pvarArg) { hr = E_INVALIDARG; goto done; } hr = spDispEx->InvokeEx(dispid, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &dispparams, pvarArg, NULL, NULL); ERROREXIT(hr) } break; } hr = S_OK; done: return hr; } //+------------------------------------------------------------------------- // Gets the dispatch ptr of the document //-------------------------------------------------------------------------- STDMETHODIMP CMediaBarPlayer::_GetDocumentDispatch(IDispatch ** ppDocDisp) { HRESULT hr = E_FAIL; if (!ppDocDisp) { hr = E_INVALIDARG; goto done; } if (!_spBrowser) { hr = E_FAIL; goto done; } hr = _spBrowser->get_Document(ppDocDisp); ERROREXIT(hr) hr = S_OK; done: return hr; } //+------------------------------------------------------------------------- // Gets the ITIMEBodyElement interface //-------------------------------------------------------------------------- STDMETHODIMP CMediaBarPlayer::_GetBodyElement(ITIMEBodyElement ** ppBodyElem) { HRESULT hr = E_FAIL; CComVariant svarArg; if (!ppBodyElem) { hr = E_INVALIDARG; goto done; } hr = _InvokeDocument(WZ_BODY, IT_GET, WZ_REGISTERED_TIME_NAME, &svarArg); ERROREXIT(hr) hr = ::VariantChangeType(&svarArg, &svarArg, NULL, VT_DISPATCH); ERROREXIT(hr) if (NULL == V_DISPATCH(&svarArg)) { hr = E_FAIL; goto done; } hr = V_DISPATCH(&svarArg)->QueryInterface(IID_TO_PPV(ITIMEBodyElement, ppBodyElem)); ERROREXIT(hr) ASSERT(ppBodyElem); hr = S_OK; done: return hr; } //+------------------------------------------------------------------------- // Gets the ITIMEMediaElement interface //-------------------------------------------------------------------------- STDMETHODIMP CMediaBarPlayer::_GetMediaElement(ITIMEMediaElement ** ppMediaElem) { HRESULT hr = E_FAIL; CComVariant svarArg; if (!ppMediaElem) { hr = E_INVALIDARG; goto done; } hr = _InvokeDocument(WZ_PLAYER, IT_GET, WZ_REGISTERED_TIME_NAME, &svarArg); ERROREXIT(hr) hr = ::VariantChangeType(&svarArg, &svarArg, NULL, VT_DISPATCH); ERROREXIT(hr) if (NULL == V_DISPATCH(&svarArg)) { hr = E_FAIL; goto done; } hr = V_DISPATCH(&svarArg)->QueryInterface(IID_TO_PPV(ITIMEMediaElement, ppMediaElem)); ERROREXIT(hr) ASSERT(ppMediaElem); hr = S_OK; done: return hr; } //+------------------------------------------------------------------------- // Gets the IDispatchEx pointer of the named element //-------------------------------------------------------------------------- STDMETHODIMP CMediaBarPlayer::_GetElementDispatch(LPWSTR pstrElem, IDispatchEx ** ppDispEx) { HRESULT hr = E_FAIL; CComPtr spElemDisp; CComPtr spDocDisp; CComPtr spDoc2; CComPtr spAll; CComVariant svarName; CComVariant svarIndex; if (!ppDispEx || !pstrElem) { hr = E_INVALIDARG; goto done; } V_VT(&svarName) = VT_BSTR; V_BSTR(&svarName) = SysAllocString(pstrElem); if (NULL == V_BSTR(&svarName)) { hr = E_OUTOFMEMORY; goto done; } V_VT(&svarIndex) = VT_I4; V_I4(&svarIndex) = 0; hr = _GetDocumentDispatch(&spDocDisp); ERROREXIT(hr) // WebOC returns S_OK even if doc disp is not available if (!spDocDisp.p) { hr = E_FAIL; goto done; } hr = spDocDisp->QueryInterface(IID_TO_PPV(IHTMLDocument2, &spDoc2)); ERROREXIT(hr) hr = spDoc2->get_all(&spAll); ERROREXIT(hr) hr = spAll->item(svarName, svarIndex, &spElemDisp); ERROREXIT(hr) if (spElemDisp.p) { hr = spElemDisp->QueryInterface(IID_TO_PPV(IDispatchEx, ppDispEx)); ERROREXIT(hr) } else { hr = E_FAIL; goto done; } hr = S_OK; done: return hr; } //+------------------------------------------------------------------------- // Stuff to be done when document is loaded //-------------------------------------------------------------------------- STDMETHODIMP CMediaBarPlayer::_OnDocumentComplete() { HRESULT hr = E_FAIL; // store off a pointer to the media element behavior hr = _GetMediaElement(&_spMediaElem); ERROREXIT(hr) hr = _spMediaElem->QueryInterface(IID_TO_PPV(ITIMEMediaElement2, &_spMediaElem2)); ERROREXIT(hr) // store off a pointer to the player's HTML element { CComPtr spPlayerDisp; hr = _GetElementDispatch(WZ_PLAYER, &spPlayerDisp); ERROREXIT(hr) if (!spPlayerDisp.p) { hr = E_FAIL; goto done; } hr = spPlayerDisp->QueryInterface(IID_TO_PPV(IHTMLElement2, &_spPlayerHTMLElem2)); ERROREXIT(hr) } // store off a pointer to the body element hr = _GetBodyElement(&_spBodyElem); ERROREXIT(hr) // Attach to player events hr = _AttachPlayerEvents(TRUE); ERROREXIT(hr) // Hook Property notifications hr = _HookPropNotifies(); ERROREXIT(hr) // set the type if deferred if (_sbstrType.m_str) { hr = put_type(_sbstrType); _sbstrType.Empty(); ERROREXIT(hr) } // set the url if deferred if (_sbstrUrl.m_str) { hr = put_url(_sbstrUrl); _sbstrUrl.Empty(); ERROREXIT(hr) } hr = S_OK; done: return hr; } //+------------------------------------------------------------------------- // Stuff to be done when Media is ready //-------------------------------------------------------------------------- STDMETHODIMP CMediaBarPlayer::_OnMediaComplete() { // notify the media bar if (_pMediaBar) { _pMediaBar->Notify(MEDIACOMPLETE); } return S_OK; } //+------------------------------------------------------------------------- // Stuff to be done when Media is ready //-------------------------------------------------------------------------- STDMETHODIMP CMediaBarPlayer::_OnTrackChange() { // notify the media bar if (_pMediaBar) { _pMediaBar->Notify(TRACK_CHANGE); } return S_OK; } //+------------------------------------------------------------------------- // Stuff to be done when track is finished //-------------------------------------------------------------------------- STDMETHODIMP CMediaBarPlayer::_OnEnd() { // notify the media bar if (_pMediaBar) { _pMediaBar->Notify(MEDIA_TRACK_FINISHED); } return S_OK; } //+------------------------------------------------------------------------- // notification that there was some error playing the Media stream //-------------------------------------------------------------------------- STDMETHODIMP CMediaBarPlayer::_OnMediaError(int iErrCode) { CComPtr spWMP; if (_spMediaElem && SUCCEEDED(_spMediaElem->get_playerObject(&spWMP))) { VARIANT varError; if (SUCCEEDED(GetProp(spWMP, L"error", &varError))) { CallMethod(varError.pdispVal, L"webHelp"); } VariantClear(&varError); } // notify the media bar if (_pMediaBar) { _pMediaBar->OnMediaError(iErrCode); } return S_OK; } //+------------------------------------------------------------------------- // Handle property change notifications //-------------------------------------------------------------------------- STDMETHODIMP CMediaBarPlayer::OnChanged(DISPID dispid) { // notify the media bar if (_pMediaBar) { _pMediaBar->Notify(dispid); } return S_OK; } //////////////////////////////////////////////////////////////////////////// // // IMediaBarPlayer // //////////////////////////////////////////////////////////////////////////// //+------------------------------------------------------------------------- // Init the player //-------------------------------------------------------------------------- STDMETHODIMP CMediaBarPlayer::Init(HWND hWnd, IMediaBar * pMediaBar) { HRESULT hr = E_FAIL; if (!pMediaBar) { hr = E_INVALIDARG; goto done; } // store a weak ref to prevent circular reference _pMediaBar = pMediaBar; hr = _CreateHost(hWnd); ERROREXIT(hr) hr = S_OK; done: if (FAILED(hr)) { DeInit(); } return hr; } //+------------------------------------------------------------------------- // DeInit the player //-------------------------------------------------------------------------- STDMETHODIMP CMediaBarPlayer::DeInit() { _pMediaBar = NULL; return _DestroyHost(); } //+------------------------------------------------------------------------- // sets the media clip type //-------------------------------------------------------------------------- STDMETHODIMP CMediaBarPlayer::put_type(BSTR bstrType) { HRESULT hr = E_FAIL; CComVariant svarArg; CComVariant svarEmpty; svarArg.vt = VT_NULL; svarEmpty.vt = VT_NULL; if (!IsReady()) { _sbstrType.m_str = ::SysAllocString(bstrType); if (bstrType && !_sbstrType) { hr = E_OUTOFMEMORY; } else { hr = S_OK; } goto done; } if (bstrType) { V_VT(&svarArg) = VT_BSTR; V_BSTR(&svarArg) = SysAllocString(bstrType); if (NULL == V_BSTR(&svarArg)) { hr = E_OUTOFMEMORY; goto done; } } // always stop the player hr = _spMediaElem->endElement(); ERROREXIT(hr) hr = _spMediaElem->put_src(svarEmpty); ERROREXIT(hr) hr = _spMediaElem->put_type(svarArg); ERROREXIT(hr) hr = S_OK; done: return hr; } //+------------------------------------------------------------------------- // sets the media clip url //-------------------------------------------------------------------------- STDMETHODIMP CMediaBarPlayer::put_url(BSTR bstrUrl) { HRESULT hr = E_FAIL; CComVariant svarArg; CComVariant svarEmpty; svarArg.vt = VT_NULL; svarEmpty.vt = VT_NULL; if (!IsReady()) { _sbstrUrl.m_str = ::SysAllocString(bstrUrl); if (bstrUrl && !_sbstrUrl) { hr = E_OUTOFMEMORY; } else { hr = S_OK; } goto done; } if (bstrUrl) { V_VT(&svarArg) = VT_BSTR; V_BSTR(&svarArg) = SysAllocString(bstrUrl); if (NULL == V_BSTR(&svarArg)) { hr = E_OUTOFMEMORY; goto done; } } // always stop the player hr = _spMediaElem->endElement(); ERROREXIT(hr) hr = _spMediaElem->put_src(svarArg); ERROREXIT(hr) // always start the player hr = _spMediaElem->beginElement(); ERROREXIT(hr) hr = S_OK; done: return hr; } //+------------------------------------------------------------------------- // gets the media clip url //-------------------------------------------------------------------------- STDMETHODIMP CMediaBarPlayer::get_url(BSTR * pbstrUrl) { HRESULT hr = E_FAIL; CComVariant svarArg; if (!pbstrUrl || !IsReady()) { hr = E_FAIL; goto done; } *pbstrUrl = NULL; hr = _spMediaElem->get_src(&svarArg); ERROREXIT(hr) hr = svarArg.ChangeType(VT_BSTR); ERROREXIT(hr) if (svarArg.bstrVal) { *pbstrUrl = SysAllocString(svarArg.bstrVal); if (NULL == *pbstrUrl) { hr = E_OUTOFMEMORY; goto done; } } hr = S_OK; done: return hr; } //+------------------------------------------------------------------------- // gets the player attribute //-------------------------------------------------------------------------- STDMETHODIMP CMediaBarPlayer::get_player(BSTR * pbstrPlayer) { HRESULT hr = E_FAIL; CComVariant svarArg; if (!pbstrPlayer || !IsReady()) { hr = E_FAIL; goto done; } *pbstrPlayer = NULL; hr = _spMediaElem->get_player(&svarArg); ERROREXIT(hr) hr = svarArg.ChangeType(VT_BSTR); ERROREXIT(hr) if (svarArg.bstrVal) { *pbstrPlayer = SysAllocString(svarArg.bstrVal); if (NULL == *pbstrPlayer) { hr = E_OUTOFMEMORY; goto done; } } hr = S_OK; done: return hr; } //+------------------------------------------------------------------------- // sets the volume //-------------------------------------------------------------------------- STDMETHODIMP CMediaBarPlayer::put_volume(double dblVolume) { HRESULT hr = E_FAIL; CComVariant svarArg; if (!IsReady()) { hr = E_FAIL; goto done; } V_VT(&svarArg) = VT_R8; V_R8(&svarArg) = dblVolume; hr = _spMediaElem->put_volume(svarArg); ERROREXIT(hr) hr = S_OK; done: return hr; } //+------------------------------------------------------------------------- // gets the volume //-------------------------------------------------------------------------- STDMETHODIMP CMediaBarPlayer::get_volume(double * pdblVolume) { return E_NOTIMPL; } //+------------------------------------------------------------------------- // gets the media element pointer //-------------------------------------------------------------------------- STDMETHODIMP CMediaBarPlayer::get_mediaElement(ITIMEMediaElement ** ppMediaElem) { if (!ppMediaElem || !_spMediaElem) { return E_FAIL; } *ppMediaElem = _spMediaElem; (_spMediaElem.p)->AddRef(); return S_OK; } //+------------------------------------------------------------------------- // sets the mute //-------------------------------------------------------------------------- STDMETHODIMP CMediaBarPlayer::put_mute(BOOL bMute) { HRESULT hr = E_FAIL; CComVariant svarArg; if (!IsReady()) { hr = E_FAIL; goto done; } V_VT(&svarArg) = VT_BOOL; V_BOOL(&svarArg) = bMute ? VARIANT_TRUE : VARIANT_FALSE; hr = _spMediaElem->put_mute(svarArg); ERROREXIT(hr) hr = S_OK; done: return hr; } //+------------------------------------------------------------------------- // gets the mute //-------------------------------------------------------------------------- STDMETHODIMP CMediaBarPlayer::get_mute(BOOL * pbMute) { return E_NOTIMPL; } //+------------------------------------------------------------------------- // plays the media //-------------------------------------------------------------------------- STDMETHODIMP CMediaBarPlayer::Play() { if (!IsReady()) return E_FAIL; return _spMediaElem->beginElement(); } //+------------------------------------------------------------------------- // stops the media //-------------------------------------------------------------------------- STDMETHODIMP CMediaBarPlayer::Stop() { if (!IsReady()) return E_FAIL; return _spMediaElem->endElement(); } //+------------------------------------------------------------------------- // pauses the media //-------------------------------------------------------------------------- STDMETHODIMP CMediaBarPlayer::Pause() { if (!IsReady()) return E_FAIL; return _spMediaElem->pauseElement(); } //+------------------------------------------------------------------------- // resumes the media //-------------------------------------------------------------------------- STDMETHODIMP CMediaBarPlayer::Resume() { if (!IsReady()) return E_FAIL; return _spMediaElem->resumeElement(); } //+------------------------------------------------------------------------- // seeks the media to the given progress //-------------------------------------------------------------------------- STDMETHODIMP CMediaBarPlayer::Seek(double dblProgress) { HRESULT hr = E_FAIL; CComPtr spTimeState; double dblActiveDur = 0.0; double dblSeekTime = 0.0; VARIANT_BOOL vbActive = VARIANT_FALSE; if (!IsReady()) { hr = E_FAIL; goto done; } if (!IsPlayList()) { hr = _spMediaElem->get_currTimeState(&spTimeState); ERROREXIT(hr) hr = spTimeState->get_activeDur(&dblActiveDur); ERROREXIT(hr) hr = spTimeState->get_isActive(&vbActive); ERROREXIT(hr) // ISSUE: workaround for IE6 #20622 // if the clip has ended, reactivate it in the paused state if (VARIANT_FALSE == vbActive) { _spMediaElem->beginElement(); _spMediaElem->pauseElement(); } if (TIME_INFINITE == dblActiveDur) { // we shouldn't be allowed to seek goto done; } else { Clamp(0.0, dblProgress, 1.0); dblSeekTime = dblActiveDur * dblProgress; } // seek the body hr = _spMediaElem->seekActiveTime(dblSeekTime); ERROREXIT(hr) hr = S_OK; } else { CComPtr spPlayList; CComPtr spPlayItem; CComPtr spMediaNative; hr = _spMediaElem->get_playList(&spPlayList); if (SUCCEEDED(hr) && spPlayList) { hr = spPlayList->get_activeTrack(&spPlayItem); if (SUCCEEDED(hr) && spPlayItem) { spPlayItem->get_dur(&dblActiveDur); dblSeekTime = dblActiveDur * dblProgress; hr = _spMediaElem->QueryInterface(IID_TO_PPV(ITIMEMediaNative, &spMediaNative)); if (SUCCEEDED(hr) && spMediaNative) { spMediaNative->seekActiveTrack(dblSeekTime); } } } } done: return hr; } //+------------------------------------------------------------------------- // Resize the video to fit in the given window size, preserving aspect ratio //-------------------------------------------------------------------------- STDMETHODIMP CMediaBarPlayer::Resize(LONG* plHeight, LONG* plWidth, BOOL fClampMaxSizeToNaturalSize) { HRESULT hr = E_FAIL; long lMediaWidth = 0; long lMediaHeight = 0; long lResizeWidth = 0; long lResizeHeight = 0; float flWndAspect = 0.0f; float flMediaAspect = 0.0f; if (!IsReady() || !plHeight || !plWidth || (0 == (*plHeight)) || (0 == (*plWidth))) { goto done; } hr = _spMediaElem->get_mediaWidth(&lMediaWidth); ERROREXIT(hr) hr = _spMediaElem->get_mediaHeight(&lMediaHeight); ERROREXIT(hr) // do resize only if both dimensions are non-zero if (0 != lMediaWidth && 0 != lMediaHeight) { // if natural media size <= window size and max size is clamped to natural media size if ( fClampMaxSizeToNaturalSize && lMediaWidth <= (*plWidth) && lMediaHeight <= (*plHeight)) { // set the media back to it's natural size lResizeHeight = lMediaHeight; lResizeWidth = lMediaWidth; } else { // resize the media to the window size flWndAspect = (float) (*plHeight) / (float) (*plWidth); flMediaAspect = (float) lMediaHeight / (float) lMediaWidth; if (flMediaAspect <= flWndAspect) { // set width to window width and compute the height according to aspect ratio lResizeWidth = (long)(*plWidth); lResizeHeight = (long)(lResizeWidth * flMediaAspect); } else { // set height to window height and compute the width according to aspect ratio lResizeHeight = (long)(*plHeight); lResizeWidth = (long)(lResizeHeight / flMediaAspect); } } // set the resized height and width on the HTML element { CComPtr spStyle; CComPtr spHTMLElem; hr = _spPlayerHTMLElem2->QueryInterface(IID_PPV_ARG(IHTMLElement2, &spHTMLElem)); ERROREXIT(hr) // Using runtimeStyle instead of style. // (Previously, we did the reverse as a work around for IE6 #20625. But now style is broken.) hr = spHTMLElem->get_runtimeStyle(&spStyle); ERROREXIT(hr) hr = spStyle->put_pixelWidth(lResizeWidth); ERROREXIT(hr) hr = spStyle->put_pixelHeight(lResizeHeight); ERROREXIT(hr) } } *plWidth = lResizeWidth ; *plHeight = lResizeHeight; hr = S_OK; done: return hr; } //////////////////////////////////////////////////////////////////////////// // // IDispatch // //////////////////////////////////////////////////////////////////////////// //+------------------------------------------------------------------------- // Name: Invoke // // Abstract: // This switches on the dispid looking for dispid's of events // that it should handle. Note, this is called for all events // fired from the window, only the selected events are handled. //-------------------------------------------------------------------------- STDMETHODIMP CMediaBarPlayer::Invoke( /* [in] */ DISPID dispIdMember, /* [in] */ REFIID /*riid*/, /* [in] */ LCID /*lcid*/, /* [in] */ WORD /*wFlags*/, /* [out][in] */ DISPPARAMS* pDispParams, /* [out] */ VARIANT* pVarResult, /* [out] */ EXCEPINFO* /*pExcepInfo*/, /* [out] */ UINT* puArgErr) { HRESULT hr = E_FAIL; switch (dispIdMember) { case 0: //this is the case for events that have been hooked using attachEvent { CComBSTR sbstrEvent; CComPtr pEventObj; if ((NULL != pDispParams) && (NULL != pDispParams->rgvarg) && (V_VT(&(pDispParams->rgvarg[0])) == VT_DISPATCH)) { hr = THR((pDispParams->rgvarg[0].pdispVal)->QueryInterface(IID_IHTMLEventObj, (void**)&pEventObj)); if (FAILED(hr)) { goto done; } } else { ASSERT(0 && "Unexpected dispparam values passed to CEventMgr::Invoke(dispid = 0)"); hr = E_UNEXPECTED; goto done; } hr = THR(pEventObj->get_type(&sbstrEvent)); if (0 == StrCmpIW(WZ_TRACKCHANGE, sbstrEvent)) { _OnTrackChange(); } if (0 == StrCmpIW(WZ_MEDIACOMPLETE, sbstrEvent)) { _OnMediaComplete(); } else if (0 == StrCmpIW(WZ_MEDIAERROR, sbstrEvent)) { int iErrCode = -1; // Get the param if available CComPtr spEventObj2; CComVariant svarParam; CComBSTR sbstrParam(WZ_PARAM); hr = pEventObj->QueryInterface(IID_IHTMLEventObj2, (void**) &spEventObj2); if (SUCCEEDED(hr) && sbstrParam.m_str) { // get the params hr = spEventObj2->getAttribute(sbstrParam, 0, &svarParam); if (SUCCEEDED(hr)) { // change type to int hr = svarParam.ChangeType(VT_I4); if (SUCCEEDED(hr)) { iErrCode = V_I4(&svarParam); } } } _OnMediaError(iErrCode); } else if (0 == StrCmpIW(WZ_END, sbstrEvent)) { _OnEnd(); } } break; case 259: // DISPID_DOCUMENTCOMPLETE { hr = _OnDocumentComplete(); ERROREXIT(hr) } break; } hr = S_OK; done: return S_OK; } /////////////////////////////////////////////////////////////// // Name: _InitEventSink // // Abstract: // Finds a connection point on the HTMLDocument interface // and passes this as an event handler. /////////////////////////////////////////////////////////////// STDMETHODIMP CMediaBarPlayer::_InitEventSink() { // Get a connection point to the container CComPtr spDocCPC; HRESULT hr = E_FAIL; hr = _spBrowser->QueryInterface(IID_IConnectionPointContainer, (void**)&spDocCPC); ERROREXIT(hr) hr = THR(spDocCPC->FindConnectionPoint(DIID_DWebBrowserEvents2, &_spDocConPt )); ERROREXIT(hr) hr = THR(_spDocConPt->Advise(static_cast(static_cast(this)), &_dwDocumentEventConPtCookie)); ERROREXIT(hr) hr = S_OK; done: if (FAILED(hr)) { _DeInitEventSink(); } return hr; } /////////////////////////////////////////////////////////////// // Name: _DeInitEventSink // /////////////////////////////////////////////////////////////// STDMETHODIMP CMediaBarPlayer::_DeInitEventSink() { //release the document connection points if (_spDocConPt) { if (_dwDocumentEventConPtCookie != 0) { IGNORE_HR(_spDocConPt->Unadvise(_dwDocumentEventConPtCookie)); } _spDocConPt.Release(); } _dwDocumentEventConPtCookie = 0; return S_OK; } double STDMETHODCALLTYPE CMediaBarPlayer::GetTrackProgress() { if (IsReady()) { if (!IsPlayList()) { CComPtr spTimeState; if (SUCCEEDED(_spMediaElem->get_currTimeState(&spTimeState)) && spTimeState) { double dblProgress = 0.0; if (SUCCEEDED(spTimeState->get_progress(&dblProgress))) { return dblProgress; } } } else { CComPtr spMediaNative; double dblProgress, dblActiveDur; CComPtr spPlayList; CComPtr spPlayItem; if (SUCCEEDED(_spMediaElem->get_playList(&spPlayList)) && spPlayList) { if (SUCCEEDED(spPlayList->get_activeTrack(&spPlayItem)) && spPlayItem) { spPlayItem->get_dur(&dblActiveDur); if (SUCCEEDED(_spMediaElem->QueryInterface(IID_TO_PPV(ITIMEMediaNative, &spMediaNative))) && spMediaNative) { spMediaNative->get_activeTrackTime(&dblProgress); return dblProgress / dblActiveDur; } } } } } return 0.0; } double STDMETHODCALLTYPE CMediaBarPlayer::GetTrackTime() { if (IsReady()) { CComPtr spTimeState; if (SUCCEEDED(_spMediaElem->get_currTimeState(&spTimeState)) && spTimeState) { double dblTime = 0.0; if (SUCCEEDED(spTimeState->get_simpleTime(&dblTime))) { return dblTime; } } } return 0.0; } double STDMETHODCALLTYPE CMediaBarPlayer::GetTrackLength() { if (IsReady()) { double dblDur = 0.0; if (SUCCEEDED(_spMediaElem->get_mediaDur(&dblDur))) return dblDur ; } return 0.0; } // Returns a progress between 0 and 100 and whether this is download or buffering progress STDMETHODIMP CMediaBarPlayer::GetBufProgress(double * pdblProg, ProgressType * ppt) { HRESULT hr = E_FAIL; VARIANT_BOOL vb = VARIANT_FALSE; if (!pdblProg || !ppt) { hr = E_INVALIDARG; goto done; } if (!IsReady()) { hr = E_FAIL; goto done; } *pdblProg = 0.0; *ppt = PT_None; hr = _spMediaElem2->get_isStreamed(&vb); if (FAILED(hr)) { goto done; } if (VARIANT_TRUE == vb) { CComVariant svarBufProg; hr = _spMediaElem2->get_bufferingProgress(&svarBufProg); if (SUCCEEDED(hr)) { *ppt = PT_Buffering; if (SUCCEEDED(svarBufProg.ChangeType(VT_R8))) { *pdblProg = V_R8(&svarBufProg); } } } else { CComVariant svarDownloadProg; hr = _spMediaElem2->get_downloadProgress(&svarDownloadProg); if (SUCCEEDED(hr)) { *ppt = PT_Download; if (SUCCEEDED(svarDownloadProg.ChangeType(VT_R8))) { *pdblProg = V_R8(&svarDownloadProg); } } } done: return hr; } VARIANT_BOOL STDMETHODCALLTYPE CMediaBarPlayer::isMuted() { VARIANT_BOOL vbMuted = VARIANT_FALSE; if (IsReady()) { CComPtr spTimeState; if (SUCCEEDED(_spMediaElem->get_currTimeState(&spTimeState)) && spTimeState) { spTimeState->get_isMuted(&vbMuted); } } return vbMuted; } VARIANT_BOOL STDMETHODCALLTYPE CMediaBarPlayer::isPaused() { VARIANT_BOOL vbPaused = VARIANT_FALSE; if (IsReady()) { CComPtr spTimeState; if (SUCCEEDED(_spMediaElem->get_currTimeState(&spTimeState)) && spTimeState) { spTimeState->get_isPaused(&vbPaused); } } return vbPaused; } VARIANT_BOOL STDMETHODCALLTYPE CMediaBarPlayer::isStopped() { VARIANT_BOOL vbActive = VARIANT_FALSE; if (IsReady()) { CComPtr spTimeState; if (SUCCEEDED(_spMediaElem->get_currTimeState(&spTimeState)) && spTimeState) { spTimeState->get_isActive(&vbActive); } } return (vbActive ? VARIANT_FALSE : VARIANT_TRUE); } STDMETHODIMP CMediaBarPlayer::Next() { return _SetTrack(TT_Next);; } STDMETHODIMP CMediaBarPlayer::Prev() { return _SetTrack(TT_Prev); } STDMETHODIMP CMediaBarPlayer::_SetTrack(TrackType tt) { HRESULT hr = E_FAIL; CComPtr spPlayList; if (!IsReady()) { goto done; } hr = _spMediaElem->get_playList(&spPlayList); ERROREXIT(hr) if (NULL != spPlayList.p) { if (TT_Next == tt) { hr = spPlayList->nextTrack(); ERROREXIT(hr) } else if (TT_Prev == tt) { hr = spPlayList->prevTrack(); ERROREXIT(hr) } } hr = S_OK; done: return hr; } LONG_PTR STDMETHODCALLTYPE CMediaBarPlayer::GetPlayListItemIndex() { CComPtr spPlayList; long lIndex = -1; if (IsReady()) { if (SUCCEEDED(_spMediaElem->get_playList(&spPlayList))) { CComPtr spPlayItem; if (spPlayList && SUCCEEDED(spPlayList->get_activeTrack(&spPlayItem))) { if (spPlayItem && SUCCEEDED(spPlayItem->get_index(&lIndex))) return lIndex; } } } return (LONG_PTR)lIndex; } LONG_PTR STDMETHODCALLTYPE CMediaBarPlayer::GetPlayListItemCount() { CComPtr spPlayList; LONG lLength = 0; if (IsReady()) { if (SUCCEEDED(_spMediaElem->get_playList(&spPlayList))) { if (spPlayList && SUCCEEDED(spPlayList->get_length(&lLength))) { return lLength; } } } return lLength; } HRESULT STDMETHODCALLTYPE CMediaBarPlayer::SetActiveTrack( long lIndex) { CComPtr spPlayList; HRESULT hr = S_OK; if (IsReady()) { hr = _spMediaElem->get_playList(&spPlayList); ERROREXIT(hr); if (spPlayList) { VARIANT vIndex; VariantInit(&vIndex); vIndex.vt = VT_I4; vIndex.lVal = lIndex; hr = spPlayList->put_activeTrack(vIndex) ; ERROREXIT(hr); } else { hr = S_FALSE; } } done : return hr ; } BOOL STDMETHODCALLTYPE CMediaBarPlayer::IsPausePossible() { if (IsReady()) { VARIANT_BOOL vbIsPausePossible = VARIANT_FALSE; if (SUCCEEDED(_spMediaElem->get_canPause(&vbIsPausePossible))) { return (vbIsPausePossible == VARIANT_TRUE) ? TRUE : FALSE; } } return FALSE; } BOOL STDMETHODCALLTYPE CMediaBarPlayer::IsSeekPossible() { if (IsReady()) { VARIANT_BOOL vbIsSeekPossible = VARIANT_FALSE; if (SUCCEEDED(_spMediaElem->get_canSeek(&vbIsSeekPossible))) { return (vbIsSeekPossible == VARIANT_TRUE) ? TRUE : FALSE; } } return FALSE; } BOOL STDMETHODCALLTYPE CMediaBarPlayer::IsStreaming() { if (IsReady()) { VARIANT_BOOL vbIsStreaming = VARIANT_FALSE; if (SUCCEEDED(_spMediaElem2->get_isStreamed(&vbIsStreaming))) { return (vbIsStreaming == VARIANT_TRUE) ? TRUE : FALSE; } } return FALSE; } BOOL STDMETHODCALLTYPE CMediaBarPlayer::IsPlayList() { VARIANT_BOOL vbIsPlayList = VARIANT_FALSE; if (IsReady()) { if (SUCCEEDED(_spMediaElem->get_hasPlayList(&vbIsPlayList))) { return (vbIsPlayList == VARIANT_TRUE) ? TRUE : FALSE; } } return FALSE; } BOOL STDMETHODCALLTYPE CMediaBarPlayer::IsSkippable() { BOOL fRet = TRUE; // We need to check if the client has specified CLIENTSKIP="no" and respect that. This is to prevent // the media bar from allowing the user to skip server-side stuff, which is a no-no. There are legal restrictions // related to this. CComDispatchDriverEx spWMP; if (_spMediaElem && SUCCEEDED(_spMediaElem->get_playerObject(&spWMP)) && spWMP) { CComVariant vtControls; HRESULT hr = spWMP.GetPropertyByName(L"controls", &vtControls); if (SUCCEEDED(hr)) { CComDispatchDriverEx pwmpControls; pwmpControls = vtControls; // We're only checking for next (but not back), and assuming that NOSKIP will affect only "next". CComVariant vtNext = "Next"; CComVariant vtEnabled; hr = pwmpControls.GetPropertyByName1(L"isAvailable", &vtNext, &vtEnabled); if (SUCCEEDED(hr) && (V_VT(&vtEnabled) == VT_BOOL)) { fRet = (V_BOOL(&vtEnabled) == VARIANT_TRUE); } } } return fRet; } #ifdef SINKWMP HRESULT CMediaBarPlayer::InitWMPSink() { if (!_spWMP) { if (SUCCEEDED(_spMediaElem->get_playerObject(&_spWMP))) { CComPtr pcpc; HRESULT hr = _spWmp->QueryInterface(IID_TO_PPV(IConnectionPointContainer, &pcpc)); if (SUCCEEDED(hr)) { hr = pcpc->FindConnectionPoint(DIID__WMPOCXEvents, &_spWMPCP); } if (SUCCEEDED(hr)) { hr = _spWMPCP->Advise(GetUnknown(), &_dwWMPCookie); if (FAILED(hr)) { m_pcpMediaEvents.Release(); m_dwMediaEventsCookie = 0; } } } } return S_OK; } #endif HRESULT CMediaBarPlayer::GetProp(IDispatch* pDispatch, OLECHAR* pwzProp, VARIANT* pvarResult, DISPPARAMS* pParams) { DISPID dispid = NULL; HRESULT hr = S_OK; DISPPARAMS params = {NULL, NULL, 0, 0}; if (!pParams) { pParams = ¶ms; } if (!pDispatch) { hr = E_POINTER; goto done; } hr = pDispatch->GetIDsOfNames(IID_NULL, &pwzProp, 1, LOCALE_SYSTEM_DEFAULT, &dispid); if (FAILED(hr)) { goto done; } hr = pDispatch->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, pParams, pvarResult, NULL, NULL); if (FAILED(hr)) { goto done; } done: return hr; } HRESULT CMediaBarPlayer::CallMethod(IDispatch* pDispatch, OLECHAR* pwzMethod, VARIANT* pvarResult, VARIANT* pvarArgument1) { DISPID dispid = NULL; HRESULT hr = S_OK; DISPPARAMS params = {pvarArgument1, NULL, 0, 0}; if (NULL != pvarArgument1) { params.cArgs = 1; } if (!pDispatch) { hr = E_POINTER; goto done; } hr = pDispatch->GetIDsOfNames(IID_NULL, &pwzMethod, 1, LOCALE_SYSTEM_DEFAULT, &dispid); if (FAILED(hr)) { goto done; } hr = pDispatch->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, ¶ms, pvarResult, NULL, NULL); if (FAILED(hr)) { goto done; } done: return hr; }