简单的用directshow播放一些视频和音频流的程序

这是一个基于对话框的程序。源代码参照了微软的directshow 的例子程序和一本英文介绍如何编写directshow程序的书里面的程序代码。

首先,因为要播放视频流,所以,要在对话框上给视频流的播放留个地方,这样就先弄个picture control控件到对话框上,将其ID设为IDC_VIDEO_WINDOW,并且让它的type是Rectangle,并且让一个类型为CStatic的名为m_VideoWindow的变量和它相关联,这样,就可以通过m_VideoWindow控制IDC_VIDEO_WINDOW了。也就是:
void CplayMediaDlg::DoDataExchange(CDataExchange* pDX)
{
 CDialog::DoDataExchange(pDX);
 DDX_Control(pDX, IDC_VIDEO_WINDOW, m_VideoWindow);
}
这可以通过用classvizard添加代码的方式实现,也可以自己手工添加代码。

由于directshow用到了com技术,所以,即使仅要编写directshow的应用程序,也要对组件对象模型有些了解。

通过自动的方式播放视频流和音频流是比较简单的,下面是一些源代码,并且其中会有些讲解。

// playMediaDlg.h : 头文件
//

#pragma once
#include "afxwin.h"

#include <Dshow.h>
#pragma comment(lib,"Strmiids.lib")


// CplayMediaDlg 对话框
class CplayMediaDlg : public CDialog
{
// 构造
public:
 CplayMediaDlg(CWnd* pParent = NULL); // 标准构造函数

// 对话框数据
 enum { IDD = IDD_PLAYMEDIA_DIALOG };

 protected:
 virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持


// 实现
protected:
 HICON m_hIcon;

 // 生成的消息映射函数
 virtual BOOL OnInitDialog();
 afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
 afx_msg void OnPaint();
 afx_msg HCURSOR OnQueryDragIcon();
 DECLARE_MESSAGE_MAP()
public:
 CStatic m_VideoWindow;
 afx_msg BOOL OnEraseBkgnd(CDC* pDC);
 afx_msg void OnBnClickedOpen();
 // Graph builder interface,这是用于控制对filter graph创建等等的接口,其中有相应的函数
 IGraphBuilder* pGraph;
 // Media control interface,这是用于控制流的run,stop,pause的接口,里面有相应的函数
 IMediaControl* pControl;
  afx_msg void OnBnClickedPlay();
 afx_msg void OnBnClickedPause();
 afx_msg void OnBnClickedStop();
 afx_msg void OnClose();
 // 用于控制显示在什么地方的接口,要把视频流显示出来就要用到这个接口中的函数
 IVideoWindow* m_pVidWin;
 // 记录按下了几次pause按钮
 int m_pausePress;
 afx_msg void OnBnClickedSave();
};

在playMediaDlg.cpp中是具体的函数代码。

// playMediaDlg.cpp---------------------------------------

CplayMediaDlg::CplayMediaDlg(CWnd* pParent /*=NULL*/)
 : CDialog(CplayMediaDlg::IDD, pParent)
 , pGraph(NULL)
 , pControl(NULL)
 , m_pVidWin(NULL)
 , m_pausePress(0)
{
 m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

BOOL CplayMediaDlg::OnInitDialog()
{
 CDialog::OnInitDialog();

 // 将/“关于.../”菜单项添加到系统菜单中。

 // IDM_ABOUTBOX 必须在系统命令范围内。
 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
 ASSERT(IDM_ABOUTBOX < 0xF000);

 CMenu* pSysMenu = GetSystemMenu(FALSE);
 if (pSysMenu != NULL)
 {
  CString strAboutMenu;
  strAboutMenu.LoadString(IDS_ABOUTBOX);
  if (!strAboutMenu.IsEmpty())
  {
   pSysMenu->AppendMenu(MF_SEPARATOR);
   pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
  }
 }

 // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
 //  执行此操作
 SetIcon(m_hIcon, TRUE);   // 设置大图标
 SetIcon(m_hIcon, FALSE);  // 设置小图标

 // TODO: 在此添加额外的初始化代码

 // Since we're embedding video in a child window of a dialog,
    // we must set the WS_CLIPCHILDREN style to prevent the bounding
    // rectangle from drawing over our video frames.
    //
    // Neglecting to set this style can lead to situations when the video
    // is erased and replaced with the default color of the bounding rectangle.
    m_VideoWindow.ModifyStyle(0, WS_CLIPCHILDREN);

 // Initialize the COM library.
    HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
 if (FAILED(hr))
 {
  AfxMessageBox(TEXT("cannot initialize the com"));
 }

    //定义一些接口
 // Create the Filter Graph Manager and query for interfaces.
//用CoCreateInstance也是一种获得接口的方法
 hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
      IID_IGraphBuilder, (void **)&pGraph);
 if(FAILED(hr))
 {
  AfxMessageBox(TEXT("cannot create filter graph manager"));
 }

 // Use IGraphBuilder::QueryInterface (inherited from IUnknown)
 // to get the IMediaControl interface.
 hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);
 if(FAILED(hr))
 {
  AfxMessageBox(TEXT("Can not obtain the media control interface"));
  pGraph->Release();
  pGraph=NULL;
 }


  //for display
 hr = pGraph->QueryInterface(IID_IVideoWindow, (void**)&m_pVidWin);
 if(FAILED(hr))
 {
  AfxMessageBox(TEXT("can not obtain the video window interface"));
  pControl->Release();
  pControl=NULL;
  pGraph->Release();
  pGraph=NULL;
   }

 return TRUE;  // 除非设置了控件的焦点,否则返回 TRUE
}

上面的函数其实也就是在创建对话框的时候获得接口,下面的函数是打开文件(音频或者视频文件)进行播放的函数。

void CplayMediaDlg::OnBnClickedOpen()
{
 // TODO: 在此添加控件通知处理程序代码
 //建立filter graph

 HRESULT hr;
 CFileDialog dlg(TRUE);
 if (dlg.DoModal()==IDOK)
 {

  // To build the filter graph, only one call is required.
  // We make the RenderFile call to the Filter Graph Manager
  // to which we pass the name of the media file.
  //下面的几行转换很重要,其实也就是把ASCII码转换成Unicode
  WCHAR wFileName[MAX_PATH];
        MultiByteToWideChar(CP_ACP, 0, dlg.GetPathName(), -1, wFileName,
                        MAX_PATH);

//这里其实就是让directshow自动根据要播放的文件的格式,生成filter graph
hr=pGraph->RenderFile((LPCWSTR)wFileName,NULL);
  if(FAILED(hr))
  {
   AfxMessageBox(TEXT("can not open the file"));
  }

  //用于设置显示的位置的代码,这里就是进行显示的设置
  HRESULT hr = m_pVidWin->put_Owner((OAHWND) m_VideoWindow.GetSafeHwnd());
  if (SUCCEEDED(hr))
  {
   // The video window must have the WS_CHILD style
   hr = m_pVidWin->put_WindowStyle(WS_CHILD);
   // Read coordinates of video container window
   RECT rc;
   m_VideoWindow.GetClientRect(&rc);
   long width =  rc.right - rc.left;
   long height = rc.bottom - rc.top;
   // Ignore the video's original size and stretch to fit bounding rectangle
   hr = m_pVidWin->SetWindowPosition(rc.left, rc.top, width, height);
   m_pVidWin->put_Visible(OATRUE);
  }

 }

}


下面的三个函数是为一个按钮控件添加的消息响应函数

void CplayMediaDlg::OnBnClickedPlay()
{
 // TODO: 在此添加控件通知处理程序代码
    
    //run the graph
   HRESULT hr=pControl->Run();

 }

void CplayMediaDlg::OnBnClickedPause()
{
 // TODO: 在此添加控件通知处理程序代码
 HRESULT hr=pControl->Pause();
 if(FAILED(hr))
 {
  AfxMessageBox(TEXT("can not pause the filter graph"));
 }
}

void CplayMediaDlg::OnBnClickedStop()
{
 // TODO: 在此添加控件通知处理程序代码
 HRESULT hr=pControl->Stop();
 if(FAILED(hr))
 {
  AfxMessageBox(TEXT("can not stop the filter graph"));
 }

 }

当对话框关闭时,要进行些收尾的工作

void CplayMediaDlg::OnClose()
{
 // TODO: 在此添加消息处理程序代码和/或调用默认值

 //// Now release everything and clean up.
    pControl->Release();
     pGraph->Release();
  m_pVidWin->Release();
     CoUninitialize();


 CDialog::OnClose();
}

下面的这个函数从一本英文书上修改的,用途是把当前的filter graph保存起来,这样,对于调试会比较的方便的,到时候用graph edit看就可以了

void CplayMediaDlg::OnBnClickedSave()
{
 // TODO: 在此添加控件通知处理程序代码
 HRESULT hr;
 CFileDialog dlg(TRUE);

 if (dlg.DoModal()==IDOK)
 {
  WCHAR wFileName[MAX_PATH];
        MultiByteToWideChar(CP_ACP, 0, dlg.GetPathName(), -1, wFileName, MAX_PATH);

  IStorage* pStorage=NULL;

   // First, create a document file that will hold the GRF file
  hr = ::StgCreateDocfile(
   wFileName,
   STGM_CREATE|STGM_TRANSACTED|STGM_READWRITE|STGM_SHARE_EXCLUSIVE,
   0, &pStorage);
  if (FAILED(hr))
  {
   AfxMessageBox(TEXT("Can not create a document"));
   return;
  }

        // Next, create a stream to store.
  WCHAR wszStreamName[] = L"ActiveMovieGraph";
  IStream *pStream;
  hr = pStorage->CreateStream(
         wszStreamName,
         STGM_WRITE|STGM_CREATE|STGM_SHARE_EXCLUSIVE,
         0, 0, &pStream);
  if(FAILED(hr))
  {
   AfxMessageBox(TEXT("Can not create a stream"));
   pStorage->Release();
   return;
  }

  // The IpersistStream::Save method converts a stream
        // into a persistent object.
  IPersistStream *pPersist = NULL;
  pGraph->QueryInterface(IID_IPersistStream,
       reinterpret_cast<void**>(&pPersist));
  hr = pPersist->Save(pStream, TRUE);
  pStream->Release();
  pPersist->Release();

  if(SUCCEEDED(hr))
  {
   hr = pStorage->Commit(STGC_DEFAULT);
   if (FAILED(hr))
   {
    AfxMessageBox(TEXT("can not store it"));é
   }
  }
  pStorage->Release();

 }

}

这样,一个简单的基于directshow的流媒体播放程序,就算完成了。

感觉上对于编写directshow的应用程序,各个程序不同的地方在于filter graph不一样的,不一样的filter graph所针对的流媒体的类型也是不一样的,别的如同显示视频流的代码都是一样的。而这里,由于是让directshow自动生成filter graph,所以,只用了一行代码hr=pGraph->RenderFile((LPCWSTR)wFileName,NULL);很多时候,需要自己组建filter graph,这就会复杂些。并且,这里当打开另一个文件的时候,应该把由原来文件生成的filter graph删除掉的,但是,这里没有作。并且,还有考虑不太周到的地方。将来改进。

你可能感兴趣的:(manager,filter,video,null,Graph,interface)