DirectShow学习之三媒体播放过程分析

DirectShow学习之三媒体播放过程分析

分类: J:DirectShow学习 1583人阅读 评论(2) 收藏 举报

作者:liguisen

Bloghttp://blog.csdn.net/liguisen

 
    上一篇做了一个简单的媒体文件播放器,它到底与其它程序有什么不同呢?很大的一个关键就在于我们使用了 CDXGraph.h和CDXGraph.cpp这两个文件,只有这两个东西不是我们自己的,现在就把它变成我们自己的。
    先看看我们做的程序,要播放媒体,就两个步骤,打开文件,播放。跟踪代码的执行过程,无非就是这样:
通过“打开对话框”得到文件: mSourceFile = dlgOpen.GetPathName();
然后 CreateGraph(),看:
void CMyPlayerDlg::CreateGraph(void)
{
    DestroyGraph();//老套路了,先破坏,确保 mFilterGraph是NULL,先看DestroyGraph()
    mFilterGraph = new CDXGraph();
    if (mFilterGraph->Create())
    {
        mFilterGraph->RenderFile(mSourceFile);
        mFilterGraph->SetDisplayWindow(mVideoWindow.GetSafeHwnd());
        mFilterGraph->SetNotifyWindow(this->GetSafeHwnd());
        mFilterGraph->Pause();
    }
}
看 DestroyGraph():
void CMyPlayerDlg::DestroyGraph(void)
{//这个函数的作用就是:如果“打开”了就“关闭”,否则什么也不干,
//就是运行了这个函数,一定是“关闭”的
    if (mFilterGraph)//CDXGraph *   mFilterGraph,是 CDXGraph指针
    {
        mFilterGraph->Stop();
        mFilterGraph->SetNotifyWindow(NULL);
        delete mFilterGraph;
        mFilterGraph = NULL;
    }
}
CreateGraph函数我们从 new CDXGraph()开始“研究”,就是看其构造函数,什么都没做,就是初始化,往下看mFilterGraph->Create():
bool CDXGraph::Create(void)
{
    if (!mGraph)
    {
        if (SUCCEEDED(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,IID_IGraphBuilder, (void **)&mGraph)))
        {
            //AddToObjectTable();这个是用来做 GraphEdit调试的,暂且去掉
            return QueryInterfaces();
        }
        mGraph = 0;
    }
    return false;
}
第一个不认识的是 mGraph,mGraph是什么?是IGraphBuilder *     mGraph;
    DirectShow是基于模块化的,使用一个 Filter Graph Manager来管理整个数据流的处理过程,参与处理的各个功能模块叫做Filter(国内目前的翻译有滤波器、过滤器、滤镜、筛选器等等乱七八糟的),各个Filter在Filter Graph中按一定的顺序连接成一条流水线协调工作,大致分为3类:Source Filters(负责获取数据)、Transform Filters(负责数据的格式转换,例如数据流的分离/合成、解码/编码)、Rendering Filters(负责数据的去向,显卡、声卡、文件等)。
    DirectShow建立在 COM组件技术基础上,DirectShow与COM紧密相连,它所有的部件和功能都由COM接口来构造和实现,其中几个重要的接口经常需要用到的:IGraphBuilder接口,用来创建Filter Graph Manager;IMediaControl接口,用来控制流媒体在Filter Graph中的流动,例如流媒体的启动和停止;IMediaEvent接口,该接口在Filter Graph发生一些事件时用来创建事件的标志信息并传送给应用程序(参考附2);IVideoWindow: 用于设置多媒体播放窗口的属性。(更多接口请参考附1)
    所以,在 CDXGraph类中有如下一些定义:
    IGraphBuilder *     mGraph; 
    IMediaControl *     mMediaControl;
    IMediaEventEx *     mEvent;
IVideoWindow *      mVideoWindow;
 
因此,程序中 if (!mGraph)判断还没有创建Filter Graph Manager,然后SUCCEEDED(CoCreateInstance(CLSID_FilterGraph,NULL,CLSCTX_INPROC_SERVER,IID_IGraphBuilder, (void **)&mGraph))创建Filter Graph Manager。
 
外面的 SUCCEEDED是一个宏:
#define SUCCEEDED(Status)     ((HRESULT)(Status) >= 0)
CoCreateInstance是 com的api函数。
 
接着 QueryInterfaces()查询各接口,获取Filter Graph 和IMediaEvent等组件的指针;
bool CDXGraph::QueryInterfaces( void)
{
    if (mGraph)
    {
        HRESULT hr = NOERROR;
        hr |= mGraph->QueryInterface(IID_IMediaControl, (void **)&mMediaControl);
        hr |= mGraph->QueryInterface(IID_IMediaEventEx, (void **)&mEvent);
        hr |= mGraph->QueryInterface(IID_IBasicVideo, (void **)&mBasicVideo);
        hr |= mGraph->QueryInterface(IID_IBasicAudio, (void **)&mBasicAudio);
        hr |= mGraph->QueryInterface(IID_IVideoWindow, (void **)&mVideoWindow);
        hr |= mGraph->QueryInterface(IID_IMediaSeeking, (void **)&mSeeking);
        if (mSeeking)
        {
            mSeeking->SetTimeFormat(&TIME_FORMAT_MEDIA_TIME);
        }
        return SUCCEEDED(hr);
    }
    return false;
}
mFilterGraph->Create()成功创建 Filter Graph Manager之后,就是
mFilterGraph->RenderFile(mSourceFile);
看如何实现:
bool CDXGraph::RenderFile( const char * inFile)
{
    if (mGraph)
    {
        WCHAR    szFilePath[MAX_PATH];
        MultiByteToWideChar(CP_ACP, 0, inFile, -1, szFilePath, MAX_PATH);
        if (SUCCEEDED(mGraph->RenderFile(szFilePath, NULL)))
        {
            return true;
        }
    }
    return false;
}
先对字符串作一个转换,关键在mGraph->RenderFile(szFilePath, NULL),即IGraphBuilder::RenderFile函数,内部实现暂且不管。
mFilterGraph->RenderFile(mSourceFile);后是
mFilterGraph->SetDisplayWindow(mVideoWindow.GetSafeHwnd());即:
bool CDXGraph::SetDisplayWindow(HWND inWindow)
{   
    if (mVideoWindow)
    {
        mVideoWindow->put_Visible(OAFALSE);
        mVideoWindow->put_Owner((OAHWND)inWindow);
 
        RECT windowRect;
        ::GetClientRect(inWindow, &windowRect);
        mVideoWindow->put_Left(0);
        mVideoWindow->put_Top(0);
        mVideoWindow->put_Width(windowRect.right - windowRect.left);
        mVideoWindow->put_Height(windowRect.bottom - windowRect.top);
        mVideoWindow->put_WindowStyle(WS_CHILD|WS_CLIPCHILDREN|WS_CLIPSIBLINGS);
 
        mVideoWindow->put_MessageDrain((OAHWND) inWindow);
        if (inWindow != NULL)
        {
            mVideoWindow->put_Visible(OATRUE);
        }
        else
        {
            mVideoWindow->put_Visible(OAFALSE);
        }
        return true;
    }
    return false;
}
上面这个很好理解,无非就是播放窗口的设置,除了上面设置的,还有全屏啊什么的设置,不一一列出。
再往下就是事件通知处理 mFilterGraph->SetNotifyWindow(this->GetSafeHwnd());,即:
bool CDXGraph::SetNotifyWindow(HWND inWindow)
{
    if (mEvent)
    {
        mEvent->SetNotifyWindow((OAHWND)inWindow, WM_GRAPHNOTIFY, 0);
        return true;
    }
    return false;
}
然后先暂停(代码不再详细列出) mMediaControl->Pause(),当然做这些操作之前应该先获得状态,例如:
bool CDXGraph::IsPaused( void)
{
    if (mGraph && mMediaControl)
    {
        OAFilterState state = State_Stopped;
        if (SUCCEEDED(mMediaControl->GetState(10, &state)))
        {
            return state == State_Paused;
        }
    }
    return false;
}
打开文件就做这么多,至于播放,就是一个: mMediaControl->Run()。
下面不使用 CDXGraph类来创建我们自己的工程,用vc创建MFC对话框工程MyPlayer2,和MyPlayer一样做好各个设置,为对话框头文件增加#include <streams.h>,引进CDXGraph类的过程不要,设置好界面,打开按钮不要,只要一个播放按钮,为播放按钮添加函数,精简的代码(省略了一些细节)如下:
void CMyPlayer2Dlg::OnBnClickedButtonplay()
{
    // TODO: Add your control notification handler code here
    IGraphBuilder *     mGraph; 
    IMediaControl *     mMediaControl;
    IVideoWindow *     mVWindow;
 
    CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&mGraph);
    if (mGraph)
    {
        mGraph->QueryInterface(IID_IMediaControl, (void **)&mMediaControl);
        mGraph->QueryInterface(IID_IVideoWindow, (void **)&mVWindow);
    }
    mGraph->RenderFile(L"g://cctv00.mpg", NULL);
 
    HWND inWindow=mVideoWindow.GetSafeHwnd();
    if (mVWindow)
    {
        mVWindow->put_Visible(OAFALSE);
        mVWindow->put_Owner((OAHWND)inWindow);
 
        RECT windowRect;
        ::GetClientRect(inWindow, &windowRect);
        mVWindow->put_Left(0);
        mVWindow->put_Top(0);
        mVWindow->put_Width(windowRect.right - windowRect.left);
        mVWindow->put_Height(windowRect.bottom - windowRect.top);
        mVWindow->put_WindowStyle(WS_CHILD|WS_CLIPCHILDREN|WS_CLIPSIBLINGS);
 
        mVWindow->put_MessageDrain((OAHWND) inWindow);
        if (inWindow != NULL)
        {
            mVWindow->put_Visible(OATRUE);
        }
        else
        {
            mVWindow->put_Visible(OAFALSE);
        }
 
    }
    mMediaControl->Run();
}
其中大部分代码是窗口的设置,我们甚至连那个窗口也不要,仅仅在对话框中添加一个播放按钮:
void CMyPlayer2Dlg::OnBnClickedButtonplay()
{
    IGraphBuilder *     mGraph; 
    IMediaControl *     mMediaControl;
    CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&mGraph);
    mGraph->QueryInterface(IID_IMediaControl, (void **)&mMediaControl);
    mGraph->RenderFile(L"g://cctv00.mpg", NULL);   
    mMediaControl->Run();
}
上面六行代码告诉了我们播放一个媒体文件的必需步骤:
1,通过 API函数CoCreateInstance()创建一个Filter Graph Manager 实例; 

2,通过调用QueryInterface ( )函数来获取组件的指针; 

3,对Filter Graph进行控制和对事件作出响应。
 
附 1:
转载:
DirectShow之接口实战篇(一)作者: pejaq(子墨书屋)
http://hi.baidu.com/pejaq/blog/item/98537931af869b19eac4af00.html
现今自己编程做一个多媒体播放工具是一件很令人开心愉悦的事情,但如果使用 MediaPlay控件开发则会受到很多限制,自己的很多好的创意想法都无法或者很难实现,如果利用微软的DirectX接口开发则可以充分的将作者的独特想法付诸于实现,何乐而不为呢!!不过关于DirectShow接口的开发说明文档实在是少之又少,仅有的一些不是英文的就是一些关于理论方面的,真正关于接口实战编程而且是用Delphi开发工具实现的更是凤毛麟角,使很多人都望而却步。在这里,我把我应用Directshow开发的心得以及我搜集到一些资料重新整理编辑出来公布,希望对所有由此兴趣的同仁有所帮助,就算达到了我的目的。废话少说,进入正文。
       既然是接口实战篇,就先把一些常用的接口列出来,让大家有一些基本的认识,都是用来做什么的,什么时候我们会需要用到此接口。
IFilterGraph
过滤通道接口
IFilterGraph2
增强的 IFilterGraph
IGraphBuilder
最为重用的 COM接口,用于手动或者自动构造过滤通道Filter Graph Manager
IMediaControl
用来控制流媒体,例如流的启动和停止暂停等 ,播放控制接口
IMediaEvent
播放事件接口 ,该接口在Filter Graph发生一些事件时用来创建事件的标志信息并传送给应用程序
IMediaEventEx
扩展播放事件接口
IMediaPosition
播放的位置和速度控制接口 (控制播放位置只能为设置时间控制方式)
IMediaSeeking
另一个播放的位置和播放速度控制接口 ,在位置选择方面功能较强.设置播放格式,多种控制播放方式.常用的有:(1)TIME_FORMAT_MEDIA_TIME单位100纳秒。(2)TIME_FORMAT_FRAME按帧播放
IBasicAudio
声音控制接口
IBasicVideo
图像控制接口 (波特率,宽度,长度等信息)
IVideoWindow
显示窗口控制接口 (有关播放窗口的一切控制,包括caption显示,窗口位置控制等)
ISampleGrabber
捕获图象接口 (可用于抓图控制)
IVideoFrameStep
控制单帧播放的接口
好了,熟悉了应用 DirectShow应用开发常用的接口后,我们就通过一个实例媒体播放器来熟悉掌握这些接口,实例的代码虽然简单,但五脏俱全,功能强大,同时也了解一下应用DirectShow开发一般常用的步骤。
 
附 2:
EC_ACTIVATE 视频窗口被激活或者转为非激活状态
EC_BUFFERING_DATA 过滤图形包含缓冲数据
EC_CLOCK_CHANGED 参考时钟被改变
EC_CLOCK_UNSET 时钟提供者被断开
EC_COMPLETE 所有数据被渲染完毕
EC_DEVICE_LOST 一个即插即用设备被移除或者变为有效.
EC_DISPLAY_CHANGED 显示模式被改变
EC_END_OF_SEGMENT 到达段的末尾.
EC_ERROR_STILLPLAYING 一个异步命令失败
EC_ERRORABORT 一个操作被放弃
EC_EXTDEVICE_MODE_CHANGE 不支持
EC_FULLSCREEN_LOST 一个视频渲染窗口被切换出全屏模式.
EC_GRAPH_CHANGED 过滤器图被改变
EC_LENGTH_CHANGED 源的长度被改变.
EC_NEED_RESTART 过滤器请求过滤图重新开始.
EC_NOTIFY_WINDOW 通报一个视频渲染窗口的过滤器
EC_OLE_EVENT 过滤器传递一个字符串给应用程序。.
EC_OPENING_FILE 过滤图打开一个文件,或者已经完成了打开文件操作
EC_PALETTE_CHANGED 视频调色板被改变.
EC_PAUSED 一个暂停请求被处理.
EC_QUALITY_CHANGE 过滤图为了质量控制丢桢
EC_REPAINT 一个视频渲染器要求重绘.
EC_SEGMENT_STARTED 一个新段开始
EC_SHUTTING_DOWN 过滤器图被关闭
EC_SNDDEV_IN_ERROR 一个音频设备的输入引脚错误.
EC_SNDDEV_OUT_ERROR 一个音频设备的输出引脚错误.
EC_STARVATION 过滤器没有得到足够的数据.
EC_STATE_CHANGE 过滤器图状态改变
EC_STEP_COMPLETE 一个过滤器执行了单桢渐进
EC_STREAM_CONTROL_STARTED 流控制开始命令产生效果.
EC_STREAM_CONTROL_STOPPED 一个流控制的停止命令产生效果
EC_STREAM_ERROR_STILLPLAYING 在流中产生了一个错误,但流还是在运行中.
EC_STREAM_ERROR_STOPPED 一个流因错误而停止
EC_TIMECODE_AVAILABLE 不支持
EC_USERABORT 用户中断回放.
EC_VIDEO_SIZE_CHANGED 本地视频尺寸改变.
EC_WINDOW_DESTROYED 视频渲染器被销毁,或者从过滤器图中移除.
 
 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值