现在的位置: 首页 > 综合 > 正文

Windows Mobile多媒体开发总结之Media Player Plugins

2013年08月04日 ⁄ 综合 ⁄ 共 12774字 ⁄ 字号 评论关闭
文章目录

Windows Mobile多媒体开发总结之Media Player Plugins

 

随着3G网络的普及,像多媒体和流媒体这样的技术需求会越来越大,比如视频通话。但是国内在这方面的高级人才不多,给我们这些做技术的指明了一个方向。:)

在Windows Mobile下媒体播放器开发有两种选择,一种是扩展Windows Media Player(下文简称WMP),一种是完全自己开发播放器(使用DirectShow,或者使用开源库,比如解码MP3的libmad库)。开发播放器是个大骨头,要好好去啃啃,可惜现在没这样的项目需求。

这次要总结的仅仅是Windows Mobile 6.0/6.1(下文简称WM6.0/6.1)下的WMP扩展,WM6.0/6.1使用的是WMP10版本,见下图。
CEZoom1 CEZoom4
微软还未上市的WM6.5使用的WMP也是10版本,见下图。从图中可以看到菜单风格改了,其它基本没有变化。不知道在WMP扩展开发方面有没有变化。
CEZoom2 CEZoom3

言归正传,从网上下载WMP 10 SDK(只有桌面系统的SDK,没有Mobile版本的,而且文档只有英文的):
http://msdn.microsoft.com/en-us/windowsmedia/bb190309.aspx

先总结下WMP在桌面操作系统的扩展开发。Windows Media Player Mobile是其一个子集。

Windows Media Player Skins

微软在Mobile上是完全支持皮肤的。如果你要想在Mobile上设计一个漂亮个性的Windows Media Player,需要注意设计好各部分(如按钮可用和不可用时)的图片以及skin definition file编写。下图展示的即时HTC一款Windows Mobile机子上的Windows Media Player。关于更多内容请见SDK,SDK上有专门的篇幅讲解的,标题为:Windows Media Player for Windows Mobile Skins。
HTC-Diamond-Windows-Media-Player-Skin-0

Windows Media Player Plug-ins

在Mobile上仅仅支持User Interface Plug-ins(以下红色标识的)。

★DSP Plug-ins

-> Provides an architecture that enables the user to install and activate plug-in programs that add digital signal processing (DSP) functionality. DSP plug-ins are Microsoft DirectX Media Objects (DMOs) that connect to the Player by using COM interfaces. A typical DSP plug-in might be an audio equalizer or a video tint control.(用于数据信号处理,比如均衡器,如下图)
dsp-dfx

怎样让WMP知道你的存在?方法是调用IWMPMediaPluginRegistrar::WMPRegisterPlayerPlugin方法向WMP注册。

怎样与WMP进行数据通信?WMP通过提供一个分配好的input buffer向插件提供音频和视频数据,插件向output buffer(也是有WMP分配好的)中返回数据。
WMP通过调用被插件具体实现了的方法来管理自己与插件之间的数据传递,流程如下:

1.Windows Media Player calls IMediaObject::ProcessInput, passing a pointer to an IMediaBuffer object to the DSP plug-in.

2.The DSP plug-in keeps a reference count on the input buffer object. The DSP plug-in returns an appropriate success or failure HRESULT.

3.Windows Media Player calls IMediaObject::ProcessOutput, passing a pointer to an array of DMO_OUTPUT_DATA_BUFFER structures (which contain output buffers) to the DSP plug-in.

4.The DSP plug-in processes the data in the input buffer and then copies the data to the appropriate output buffer. The DSP plug-in releases the reference count on the input buffer object when all the data in the buffer has been processed. The DSP plug-in then returns an appropriate success or failure HRESULT.

5.Windows Media Player renders the content in the output buffer.
This process repeats continuously while the plug-in is enabled and Windows Media Player has content to render.

DoProcessOutput(在这个函数中对感兴趣的音频数据进行处理) is called each time Windows Media Player successfully calls IMediaObject::ProcessOutput. It is the function that performs the digital signal processing tasks that produce the audible result that the DSP plug-in is intended to produce.
比如当前插件没有被激活时,应该像这样实现DoProcessOutPut方法:
// Test whether the plug-in is disabled by the user.
if (!m_bEnabled)
{
    // Just copy the data without changing it.
    memcpy(pbOutputData, m_pbInputData, *cbBytesProcessed);

    return S_OK;
}

相关接口和描述
IMediaObject    Manages data exchange with Windows Media Player and performs digital signal processing tasks. Detailed documentation is included with the DirectX SDK.
IWMPMediaPluginRegistrar    Manages plug-in registration.
IWMPPlugin    Manages the connection to Windows Media Player.
IWMPPluginEnable    Stores whether the plug-in is currently enabled by Windows Media Player.
IWMPServices    Retrieves information from the Player about the current stream time and stream state.

相关枚举类型及描述
WMPPlugin_Caps    Used with IWMPPlugin::GetCaps to indicate whether the plug-in can convert between formats.
WMPServices_StreamState    Indicates the whether the stream is currently stopped, paused, or playing.
----------------------------------------------------------------------------------------------

★Custom Visualizations

-> Windows Media Player provides your code with snapshots of audio frequency and waveform data at timed intervals measured in fractions of a second. The graphical output from your visualization is a Microsoft Windows device context. This is a standard Windows drawing surface that you can draw upon every time an audio snapshot is provided. (用于根据音频信息输出相应的视觉效果,如下图)
060227_eq1

相关接口及描述
IWMPEffects    Interface An interface to custom visualizations.
IWMPEffects2    Interface An interface that extends IWMPEffects, allowing greater control over visualization behavior.

相关结构体和枚举类型
PlayerState    Provides some basic states of Windows Media Player.
TimedLevel    Holds data returned from the spectrum filter.
----------------------------------------------------------------------------------------------

User Interface Plug-ins

-> Provides a variety of control panels that allow the user to modify various aspects of the player such as the video and graphic equalizer settings. Skins are one way to provide additional functionality, but they require the developer to recreate the entire user interface (UI). As an alternative, Windows Media Player allows the creation of custom UI plug-ins that display in the full mode of the player. This functionality is provided through a programming interface that follows standard Microsoft Component Object Model (COM) guidelines. (它可以实现部分UI定制,弥补了皮肤必须全部定制UI的不足。插件必须以COM服务器的形式开发出来,所以你不光要实现IWMPPluginUI接口,还要实现作为COM服务器所需要的接口,如DllGetClassObject, DllCanUnloadNow, DllRegisterServer等,使用ATL会更方便。)

  • Display Area Plug-ins
  • Settings Area Plug-ins
  • Metadata Area Plug-ins
  • Separate Window Plug-ins
  • Background Plug-ins(Windows Mobile下仅仅支持这种,还不清楚6.5以上版本是否会支持更多)

    相关接口及描述
    WMPNotifyPluginAddRemove    An independent function used to notify Windows Media Player that a plug-in has been installed or uninstalled.
    IWMPPluginUI下的方法: 
        Create    Called by Windows Media Player to instantiate the plug-in user interface. 
        Destroy    Called by Windows Media Player to shut down the plug-in user interface.
        DisplayPropertyPage    Called by Windows Media Player to request that the plug-in display its property page.
        GetProperty    Called by Windows Media Player to retrieve name/value property pairs from the plug-in. 
        SetCore    Called by Windows Media Player to provide plug-in access to the core Windows Media Player APIs. (这是个关键方法,你想要通过这个方法获得一个WMP接口指针,然后你可以通过这个指针进一步获得像IWMPControls、IWMPSettings这样的接口指针,这些接口指针提供了控制WMP的方法。 )
        SetProperty    Called by Windows Media Player to set name/value property pairs for the plug-in. 
        TranslateAccelerator    Called as part of the Windows Media Player message loop to allow the plug-in to intercept and respond to keyboard events.

    注册表键位置(每次WMP启动的时候会遍历这个位置,WMP通过这个位置继续找到你的DLL文件以便加载) 
    HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/MediaPlayer/UIPlugins/{ClassId}
    ----------------------------------------------------------------------------------------------

    ★Rendering Plug-ins

    -> Microsoft Windows Media Player provides an architecture that enables you to develop plug-ins that decode (if necessary) and render custom data contained in a Windows Media format stream.

    相关接口及描述
    IWMPMediaPluginRegistrar    Manages plug-in registration.
    IWMPNodeRealEstate    Obtains a rendering area from Windows Media Player.
    IWMPNodeRealEstateHost    Requests state changes from Windows Media Player.
    IWMPNodeWindowed    Stores a handle to the parent window used for rendering.
    IWMPNodeWindowedHost        Sends Windows messages from the plug-in to Windows Media Player.
    IWMPNodeWindowless    Stores a device context handle used by Windows Media Player when rendering in windowless mode.
    IWMPNodeWindowlessHost    Provides methods to direct Windows Media Player to update the rendering area in windowless mode.
    IWMPPlugin    Manages the connection to Windows Media Player.
    IWMPPluginEnable    Stores whether the plug-in is currently enabled by the user.
    IWMPServices    Retrieves information from Windows Media Player about the current stream time and stream state.
    IWMPWindowMessageSink    Receives messages when in windowless mode.

    相关枚举类型及描述
    WMPPlugin_Caps    Used with IWMPPlugin::GetCaps to indicate whether the plug-in can convert between formats.
    WMPServices_StreamState    Indicates the whether the stream is currently stopped, paused, or playing.

     

    作者: 王克伟
    出处: http://wangkewei.cnblogs.com/
    版权声明: 本文的版权归作者与博客园共有。转载时须注明本文的详细链接,否则作者将保留追究其法律责任的权利。
  •  

     

    Windows Mobile多媒体开发总结之Media Player Plugins(续)

     

    在文章“Windows Mobile多媒体开发总结之Media Player Plugins ”中总结了在WM(Windows Mobile)中扩展WMP(Windows Media Player)的几种方法。发布之后有很多朋友询问具体做法,所以我乘机也总结下相关知识,刚好也补一下我这方面的差缺。

    需求:在WM开发中如果不是单独开发自己的播放器或者使用第三方播放器,你就只能使用WMP,但是你可能需要在别的应用程序或者驱动中控制WMP,或者需要获得WMP的播放状态,那么怎样做呢?你可能会想到向WMP对应的按键发送消息,或者模拟键盘消息。实际上这些都不是好的解决方案。解决方法就是使用User Interface Background Plug-ins,这在上一篇文章中提到过了。

    涉及到的知识:User Interface Background Plug-ins需要实现的接口和操作WMP的方法,COM进程内服务器的编写(在Windows下扩展微软本身软件,比如扩展IE,基本都以COM的形式),Today Plug-ins的编写等。(当然你使用ATL会更方便,就不需要自己这样一步步的实现COM了。这里只是为了深入的了解下COM内部原理。)

    【第一步】在Windows Mobile中开发WMP相关头文件在AKU中的位置如下图(比如AKU 6.15):
    未命名 
    在你的项目中需要使用wmp.h和wmpplug.h头文件。

    【第二步】实现作为COM进程内服务器需要的方法,这也是需要在DLL中导出的方法:
    DllGetClassObject
    DllCanUnloadNow
    DllRegisterServer
    先简单了解下COM服务器在创建过程中的位置,为了偷懒,图就直接引用VC知识库的,组件DLL即是所要编写的COM服务器端,客户端就是WMP:

    d
    关于更多COM的原理知识和使用可以参考《COM本质论》、《深入解析ATL》(我对ATL/WTL还是知之甚少,这本书已经放我枕头边很久了,一直在学习Windows操作系统而耽误了学习ATL)或VC知识库等。

    对上面的图再解释一下,当客户端调用CoCreateInstance方法(这是WMP去做的,我们不管)时,CoCreateInstance实际上完成了下列三步:
    CoGetClassObject(rclsid, dwClsContext, NULL, IID_IClassFactory, (void **)&pCF); //此方法调用我们要实现的DllGetClassObject方法,获得工厂对象的指针
    pCF->CreateInstance(pUnkOuter, riid, ppvObject);//CMediaPlayerPluginClassFactory::CreateInstance方法此时被调用
    pCF->Release();

    看一下我们要实现的这3个方法代码是怎样的
    STDAPI DllGetClassObject(REFCLSID clsid, REFIID riid, VOID ** ppv)
    {
        HRESULT                             hr;
        CMediaPlayerPluginClassFactory *    pcf;

        *ppv = NULL;

        if ((pcf = new CMediaPlayerPluginClassFactory()) == NULL)
            return E_OUTOFMEMORY;

        if (FAILED(hr = pcf->QueryInterface(riid, ppv)))//把插件工厂对象的指针传递给WMP
        {
            delete pcf;
            return hr;
        }

        return S_OK;
    }

    STDAPI DllCanUnloadNow()
    {
        if (g_pServer->CanUnload())
            return S_OK;
        else
            return S_FALSE;
    }

    STDAPI DllRegisterServer()//当你安装插件时系统调用这个方法,在这里你把你的COM服务器注册到注册表的CLSID键上。
    {
        TCHAR   szModulePath[MAX_PATH];

        // Get our module path.
        if (!GetModuleFileName(GetModuleHandle(s_szModuleName), szModulePath, MAX_PATH))
            return E_FAIL;

        szModulePath[MAX_PATH - 1] = _T('/0');

        // register this COM object. 
        //
        // IMPORTANT:
        // be sure to update this GUID with another if you create another plug-in.  Each plug-in must have a unique GUID.
        return DllRegisterServerImplementation(_T("{009B9B8A-5080-4d09-8F74-9BD96A3558D4}"), s_szRegDescription, szModulePath, _T("Free"));
    }

    来看看CMediaPlayerPluginClassFactory相关的实现,这个类实现IClassFactory接口,我们看下它最关键的一个方法是怎样实现的:

    HRESULT CMediaPlayerPluginClassFactory::CreateInstance(IUnknown * pUnkOuter, REFIID riid, VOID ** ppvObject)
    {
        HRESULT                 hr;
        CMediaPlayerPlugin *    pPlugin; 

        *ppvObject = NULL; 

        if (pUnkOuter != NULL)
            return CLASS_E_NOAGGREGATION;

        // 创建插件对象
        if ((pPlugin = new CMediaPlayerPlugin()) == NULL)
            return E_OUTOFMEMORY; 

        pPlugin->InternalAddRef();
        hr = pPlugin->FinalConstruct();//在此注册和创建插件窗口,实际上这个窗口是不可见的,仅仅是实现一个消息循环,用于其它应用程序向它发送消息。
        pPlugin->InternalRelease();

        if (FAILED(hr))
        {
            delete pPlugin;
            return hr;
        } 

        if (FAILED(hr = pPlugin->QueryInterface(riid, ppvObject)))//传递插件对象的指针
        {
            delete pPlugin;
            return hr;
        }

        return S_OK;
    }

    另外还有像CServer类的实现,具体见附件的项目。

    【第三步】实现IWMPPluginUI等接口
    class CMediaPlayerPlugin:
        public IWMPPluginUI

    在上一篇文章中我列出了这个接口下的方法:

        STDMETHODIMP Create(HWND hwndParent, HWND * phwndWindow);
        STDMETHODIMP Destroy();
        STDMETHODIMP DisplayPropertyPage(HWND hwndParent);
        STDMETHODIMP GetProperty(LPCWSTR wszName, VARIANT * pvarProperty);
        STDMETHODIMP SetCore(IWMPCore * pCore);
        STDMETHODIMP SetProperty(LPCWSTR wszName, const VARIANT * pvarProperty);
        STDMETHODIMP TranslateAccelerator(MSG * pMsg);

    对于这些方法,实现你需要的,不感兴趣的仅仅让它为空即可,比如:
    HRESULT CMediaPlayerPlugin::Create(HWND hwndParent, HWND * phwndWindow)
    {
        // As per the IWMPPluginUI documentation, this method will never be called
        // because we're a background plugin (our registry Capabilities flags include
        // PLUGIN_TYPE_BACKGROUND = 0x00000001).

        return E_NOTIMPL;
    }

    SetCore是个关键方法,因为我们需要通过它获得操作WMP的接口指针,这里我们把获得的指针存放在m_pCore中:
    HRESULT CMediaPlayerPlugin::SetCore(IWMPCore * pCore)
    {
        // This method is called shortly after this instance is created, and allows
        // us to hook up to Windows Media Player. Save the interface pointer.
        //
        // When WMP is shutting down, it calls SetCore(NULL) so we have to handle that
        // as well.
        if (m_pCore != NULL)
        {
            m_pCore->Release();
        }

        m_pCore = pCore;

        if (m_pCore != NULL)
        {
            m_pCore->AddRef();
        }

        return S_OK;
    }

    然后我们可以像这样进一步通过m_pCore获得其它接口指针,类似这里的get_contronls方法的还有get_settings等,更详细的可以查看附件的项目。
    VOID CMediaPlayerPlugin::ControlPlayer(UINT uMsg)
    {
        IWMPControls *  pControls;

        // Get the 'IWMPControls' interface.
        if (SUCCEEDED(m_pCore->get_controls(&pControls)))
        {
            switch (uMsg)
            {
                // for the toggle message
                case WM_WMPTOGGLE:
                    WMPPlayState wmpps;

                    // get the current state
                    if (SUCCEEDED(m_pCore->get_playState(&wmpps)))
                    {
                      // if it playing a track, pause it
                      if (wmpps == wmppsPlaying)
                      {
                        pControls->pause();
                      }
                      // otherwise try to play the track
                      else
                      {
                        pControls->play();
                      }

                    }
                    break;

                case WM_WMPPREVIOUS:
                    pControls->previous();
                    break;

                case WM_WMPNEXT:
                    pControls->next();
                    break;

                default:
                    break;
            }

            pControls->Release();
        }
    }

    文章上个星期就写的差不多了,一直拖到现在才发布,实在抱歉。下一篇文章我总结下Today Plugin的开发。随便介绍下通过Today Plugin控制WMP。希望对你有所帮助,我的QQ是3423 6777 6,如果你需要什么帮助,或者跟我讨论的话。

    项目的环境是:
    Win32/Windows Mobile 6 Professional(CHS)/Visual Studio 2008(CHS) 
    /Files/wangkewei/WMPlayerPlugin.rar

    作者: 王克伟
    出处: http://wangkewei.cnblogs.com/
    版权声明: 本文的版权归作者与博客园共有。转载时须注明本文的详细链接,否则作者将保留追究其法律责任的权利。

    抱歉!评论已关闭.