Multimedia Support in Windows CE
Recently, facing up to the fact that Windows CE 5.0 is coming soon on real devices, it is wonderful to understand that PDAs already have their own history. Mobile technologies made unbelievable progress during the last 10 years. The very first Windows CE-powered devices were an adventure in terms of capabilities and programming. The only sign of multimedia features was an opportunity to play WAV files. The latest Windows CE handhelds come with Windows Media Player 10 Mobile, which has many wonderful features and is built on the same platform as its big desktop brother. Now, you may add rich multimedia support to your applications, including playing video and audio files, displaying pictures, and so forth. You will find additional information about available SDKs and other related topics in the “References” section at the end of this article.
This article doesn’t pretend to be a full guide for WMP SDK, far from this. My goal is to introduce WMP common techniques, so that you’ll be able to use them easily in your own code.
Windows Media Player Object Model Overview
WMSDK offers you a lot of different interfaces, but not all of them are supported on Windows Mobile platforms. Below, I’ve put together a short description of what you can use:
Interface | Description |
---|---|
IWMPCore | Root interface of WMP object model. You may retrieve pointers to other interfaces and accesses basic features of the control via this interface. |
IWMPControls | Allows an application to access to Windows Media Player controls; for example, it has Play, Stop, and Pause buttons. |
IWMPError | Provides error information. |
IWMPEvents | Exposes events that originate from the Windows Media Player control to which an embedding program can respond. |
IWMPMedia and IWMPMediaCollection | Manages the properties of media items. |
IWMPNetwork | Sets and retrieves the properties of the network connection used by Windows Media Player. |
IWMPPlayer | Controls the behavior of the Windows Media Player control user interface. |
IWMPPlaylist, IWMPPlaylistArray, and IWMPPlaylistCollection | Playlists manipulation. |
IWMPSettings | Sets or retrieves Windows Media Player settings. |
As you can see, this is really only a small part of desktop definitions, but it is still useful anyway.
Creating a First Application
You will start with a simple ATL application, where you easily can create a control container window. The code snippet below uses a standard ATL technique to display the Windows Media Player control:
LRESULT CWMPHost::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { AtlAxWinInit(); CComPtr<IAxWinHostWindow> spHost; CComPtr<IConnectionPointContainer> spConnectionContainer; CComWMPEventDispatch *pEventListener = NULL; CComPtr<IWMPEvents> spEventListener; HRESULT hr; RECT rcClient; m_dwAdviseCookie = 0; ... // create window GetClientRect(&rcClient); m_wndView.Create(m_hWnd, rcClient, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS); if (NULL == m_wndView.m_hWnd) goto FAILURE; // load OCX in window hr = m_wndView.QueryHost(&spHost); if (FAILMSG(hr)) goto FAILURE; hr = spHost->CreateControl(CComBSTR(_T("WMPlayer.OCX")), m_wndView, 0); if (FAILMSG(hr)) goto FAILURE; hr = m_wndView.QueryControl(&m_spWMPPlayer); if (FAILMSG(hr)) goto FAILURE; // start listening to events hr = CComWMPEventDispatch::CreateInstance(&pEventListener); spEventListener = pEventListener; if (FAILMSG(hr)) goto FAILURE; hr = m_spWMPPlayer->QueryInterface(__uuidof(IConnectionPointContainer), (void**)&spConnectionContainer); if (FAILMSG(hr)) goto FAILURE; // See whether OCX supports the IWMPEvents interface hr = spConnectionContainer->FindConnectionPoint(__uuidof(IWMPEvents), &m_spConnectionPoint); if (FAILMSG(hr)) goto FAILURE; hr = m_spConnectionPoint->Advise(spEventListener, &m_dwAdviseCookie); if (FAILMSG(hr)) goto FAILURE; return 0; FAILURE: ::PostQuitMessage(0); return 0; }
All you need to to is to create the control’s window, obtain an IWMPPlayer interface pointer, and sign up to WMP events. ATL is usually the easier way to perform such tasks than MFC; nevertheless, you may use MFC as well. As a result, your application will be able to play Windows Media files such as WMA and WMV.
The WMP control has a number of methods that allow you to control its behavior. For example, you can start playing a media file as follows:
LRESULT CWMPHost::OnFileOpen(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) { CFileOpenDlg dlgOpen; HRESULT hr; if (dlgOpen.DoModal(m_hWnd) == IDOK) { hr = m_spWMPPlayer->put_URL(dlgOpen.m_bstrName); if (FAILMSG(hr)) return 0; } return 0; }
Mobile samples for Windows Media Player 10 provide a full example the of controls’ usage.
Using WMP OCX from a Web Application
In case you are using a Web browser, hosting WMP is the easiest thing (this is a SDK sample):
<HTML> <HEAD> </HEAD> <BODY> <OBJECT> ID=wmpocx WIDTH=200 HEIGHT=150 CLASSID="clsid:6BF52A52-394A-11d3-B153-00C04F79FAA6" TYPE="application/x-oleobject" VIEWASTEXT> <PARAM name="uimode" value="none"> </OBJECT> <BR> <script for="wmpocx" event="PlayStateChange(NewState)" language="JScript">ClipPlayState(NewState); </script> <script for="wmpocx" event="Error()" language="JScript">StopPlayer(); </script> <p> <a href=# OnClick='PlayClip("storage cardwebappglass.wmv", ImgVideoPlay, true)'> <IMG id="ImgVideoPlay" src="bt_play.gif" border="0"></a> Video <br> <a href=# OnClick='PlayClip("storage cardwebappjeanne.wma", ImgAudioPlay, false)'> <IMG id="ImgAudioPlay" src="bt_play.gif" border="0"></a> Audio <br> Play state sequence <br> <input type="text" id="PlayStateSequence" width=30> <SCRIPT language="JScript"> <!-- var CurrentPlayImage = null; var bVideo = null; var bWasBuffering = false; function StopPlayer() { wmpocx.controls.stop(); wmpocx.close(); if (CurrentPlayImage != null) { CurrentPlayImage.src = "bt_play.gif"; } bWasBufferring = false; } function ClipPlayState(NewState) { PlayStateSequence.value = PlayStateSequence.value + NewState + " "; switch(NewState) { case 1: // stopped if (bWasBuffering) { bWasBufferring = false; if (CurrentPlayImage != null) { CurrentPlayImage.src = "bt_play.gif"; } } break; case 6: // buffering bWasBufferring = true; if (CurrentPlayImage != null) { CurrentPlayImage.src = "bt_load.gif"; } break; case 9: // transitioning case 11: // reconnecting bWasBufferring = false; break; case 3: // playing if (bWasBufferring) { if (CurrentPlayImage != null) { CurrentPlayImage.src = "bt_stop.gif"; } if (bVideo) { wmpocx.fullScreen = true; } } break; default: } } function PlayClip(url, img, video) { if (wmpocx.playState == 3 && bVideo != null && bVideo != video) { return; } bVideo = video; CurrentPlayImage = img; if (wmpocx.playState == 3) { StopPlayer(); } else { PlayStateSequence.value = ""; if (CurrentPlayImage != null) { CurrentPlayImage.src = "bt_load.gif"; } wmpocx.URL = url; } } --> </SCRIPT> </BODY> </HTML>
Obviously, you can decorate this HTML as you want to.
Working with Older Versions of the WMP Control
If you have a handheld device without WMP 10, this is yet not the end of the world. You still have an opportunity to use WMP OCX of version 8 for Pocket PC, which has much fewer amazing features, but can serve your needs as well. For exercise purposes, I’ve created a simple project to illustrate how it works in an MFC environment. The small code snippet below proves that it is pretty similar to the ATL way:
BOOL CWMP8SampleDlg::OnInitDialog() { CDialog::OnInitDialog(); // Set the icon for this dialog. The framework does this // automatically when the application's main window is not // a dialog SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon CenterWindow(GetDesktopWindow()); // center to the hpc screen CRect rect; m_Panel.GetClientRect(&rect); if ( m_PlayerWnd.CreateControl(__uuidof(WMP),L"", WS_VISIBLE|WS_CHILD,rect, &m_Panel,AFX_IDW_PANE_FIRST) ) { LPUNKNOWN lpUnk = m_PlayerWnd.GetControlUnknown(); HRESULT hr = lpUnk->QueryInterface(__uuidof(IWMP),(void**) &m_spWMPPlayer); } else { AfxMessageBox(L"Failed to create WMP control"); ::PostQuitMessage(0); return 0; } if ( m_spWMPPlayer ) { m_WMPEvents.m_pMainDlg = (CWMP8SampleDlg*)this; CComPtr<IConnectionPointContainer> spConnectionContainer; HRESULT hr = m_spWMPPlayer-> QueryInterface( IID_IConnectionPointContainer, (void**)&spConnectionContainer ); if (SUCCEEDED(hr)) { hr = spConnectionContainer-> FindConnectionPoint( __uuidof(_IWMPEvents), &m_spConnectionPoint ); } if (SUCCEEDED(hr)) { hr = m_spConnectionPoint->Advise((IDispatch*)&m_WMPEvents, &m_dwAdviseCookie ); } else { AfxMessageBox(L"Failed to get WMP control events"); ::PostQuitMessage(0); return 0; } if ( FAILED(SetupWMP()) ) { AfxMessageBox(L"Failed to setup WMP control"); ::PostQuitMessage(0); return 0; } } m_spWMPPlayer->Stop(); return TRUE; // return TRUE unless you set the focus to a // control }
Conclusion
With WMP control handy, your application gets additional strength in an interaction with the end user. Such multimedia support may create new added value for any product. Windows CE 5.0 brings you long-awaited support for DirectShow API to render video, but this is a good topic for future articles.
Download
Download the accompanying code’s zip file here
References
Microsoft Windows Media Player 10 SDK
Microsoft Windows Media Player 8.x for Pocket PC
About the Author
Alex Gusev started to play with mainframes at the end of the 1980s, using Pascal and REXX, but soon switched to C/C++ and Java on different platforms. When mobile PDAs seriously rose their heads in the IT market, Alex did it too. Now, he works at an international retail software company as a team leader of the Mobile R department, making programmers’ lives in the mobile jungles a little bit simpler.