说实话接触到direct show已经有半年了,说来惭愧,一直想学总因为工作中和其他的事情耽误,现在只懂一丁点
说到Wmv To Wav,用到几个Filter
1. WM Asf Reader 2. WMAudio Decode DMO 3. WavDest 4. File Writer
一般系统装完dx或dx sdk,常规的filter都有了,但是WavDest需要手动编译或在网上下(csdn 资源里搜wav dest)
通过graph edit可以轻松将wmv格式的文件转成wav,这里不多说,重点是介绍如何通过编程的方式来实现格式转换
既然要用编程的方式来完成转码操作,那么就需要创建这4个filter
上面序号中1、4可以直接通过 CLSID来创建
3. WavDest有点麻烦(这是我不知道有UuidFromString这个函数的想法),实际应用中创建WavDest Filter类似如下代码所示:
先生成一个CLSID
GUID CLSID_WavDest;
UuidFromString((unsigned char*)"3C78B8E2-6C4D-11D1-ADE2-0000F8754B99", &CLSID_WavDest);
这样就有了一个代表WavDest的CLSID,通过这个CLSID就可以创建Filter接口实例:
hr = AddFilterByCLSID(pGraphBuilder, CLSID_WavDest, L"WAV Dest", &pWavDest);
这里用到了一个AddFilterByCLSID的方法也是在网上搜到了,忘了在哪搜的了,谢谢写此函数的人和转载的人,函数源代码如下:
HRESULT AddFilterByCLSID(IGraphBuilder *pGraph, const GUID &clsid, LPCWSTR wszName, IBaseFilter **ppFilter)
{
if ( !pGraph || ! ppFilter )
{
return E_POINTER;
}
*ppFilter = 0;
IBaseFilter *pFilter = 0;
HRESULT hr =CoCreateInstance(clsid, 0, CLSCTX_INPROC_SERVER, IID_IBaseFilter,\
reinterpret_cast<void **>(&pFilter));
if ( SUCCEEDED(hr))
{
hr = pGraph->AddFilter(pFilter, wszName);
if ( SUCCEEDED(hr) )
{
*ppFilter = pFilter;
}
else
{
pFilter->Release();
}
}
return hr;
}
到这里WavDest的创建已经完成,那么就剩一个WMAudio Decode DMO的创建
看GraphEdit中WMAudio Decode DMO的ID描述和其他非绿色突出显示的filter差别在于两个{}表示的ID中间没有\
可能许多人都会犯一个错误就是使用AddFilterByCLSID来创建WMAudio Decode DMO,事实证明是错的,我就是这许多人中的一个
也证明没好好看看direct show开发相关书籍就想先搞点小东西出来将会有不少弯路要走
继续google,创建WMAudio Decode DMO代码如下:
hr = CoCreateInstance(CLSID_DMOWrapperFilter, NULL,
CLSCTX_INPROC, IID_IBaseFilter, (void **)&pFilter);
if (SUCCEEDED(hr))
{
IDMOWrapperFilter *pWrap;
hr = pFilter->QueryInterface(IID_IDMOWrapperFilter, (void **)&pWrap);
if (SUCCEEDED(hr)) { // Initialize the filter.
hr = pWrap->Init(CLSID_DMO_WMDec, CLSID_DMO_WMDec_cat);
pWrap->Release();
}
if (SUCCEEDED(hr)) { // Add the filter to the graph.
hr = pGraphBuilder->AddFilter(pFilter, L"WMAudio Decode DMO");
if ( FAILED(hr))
{
AfxMessageBox("load wmauido decode dmo failed");
return;
}
}
pFilter->Release();
}
上面的CLSID_DMO_WMDec和CLSID_DMO_WMDec_cat都是通过UuidFromString创建的,用到的ID正是在GraphEdit中看到的两个ID(前者是DMOID,后者是DMO_category_ID)
这样所有需要用到的filter已经创建完毕,那么接下来就是该想如何让这些filter连接到一起并工作
用到另外一个函数,同上面AddFilterByCLSID一样的出处,代码如下:
HRESULT ConnectFilters(IGraphBuilder *pGraph,IPin *pOut,IBaseFilter *pDest)
{
if((pGraph==NULL)||(pOut==NULL)||(pDest==NULL))
{
return E_POINTER;
}
#ifdef debug
PIN_DIRECTION PinDir;
pOut->QueryDirection(&PinDir);
_ASSERTE(PinDir==PINDIR_OUTPUT);
#endif
IPin *pIn=0;
HRESULT hr=GetUnconnectedPin(pDest,PINDIR_INPUT,&pIn);
if(FAILED(hr))
{
return hr;
}
hr=pGraph->Connect(pOut,pIn);
pIn->Release();
return hr;
}
HRESULT ConnectFilters(IGraphBuilder *pGraph,IBaseFilter *pSrc,IBaseFilter *pDest)
{
if((pGraph==NULL)||(pSrc==NULL)||(pDest==NULL))
{
return E_POINTER;
}
IPin *pOut=0;
HRESULT hr=GetUnconnectedPin(pSrc,PINDIR_OUTPUT,&pOut);
if(FAILED(hr))
{
return hr;
}
hr=ConnectFilters(pGraph,pOut,pDest);
pOut->Release();
return hr;
}
我也不想这么传代码,只是忘了在哪找到的这些函数,否则直接上地址了,何必这么费劲呢
首先连接wm asf reader 和wmaudio decode dmo
hr = ConnectFilters(pGraphBuilder, pAsfReader, pFilter);
if (FAILED(hr))
{
TCHAR szErr[MAX_ERROR_TEXT_LEN];
DWORD res = AMGetErrorText(hr, szErr, MAX_ERROR_TEXT_LEN);
if (res == 0)
{
wsprintf(szErr, "Unknown Error: 0x%2x", hr);
}
::MessageBox(0, szErr, TEXT("Error!"), MB_OK | MB_ICONERROR);
}
然后连接wmaudio decode dmo和wav dest:
hr = ConnectFilters(pGraphBuilder, pFilter, pWavDest);
if (FAILED(hr))
{
TCHAR szErr[MAX_ERROR_TEXT_LEN];
DWORD res = AMGetErrorText(hr, szErr, MAX_ERROR_TEXT_LEN);
if (res == 0)
{
wsprintf(szErr, "Unknown Error: 0x%2x", hr);
}
::MessageBox(0, szErr, TEXT("Error!"), MB_OK | MB_ICONERROR);
}
然后连接wav dest和file writer
hr = ConnectFilters(pGraphBuilder, pWavDest, pFileWriter);
if (FAILED(hr))
{
TCHAR szErr[MAX_ERROR_TEXT_LEN];
DWORD res = AMGetErrorText(hr, szErr, MAX_ERROR_TEXT_LEN);
if (res == 0)
{
wsprintf(szErr, "Unknown Error: 0x%2x", hr);
}
::MessageBox(0, szErr, TEXT("Error!"), MB_OK | MB_ICONERROR);
}
连接工作完成,
流程是完成了,但是我们还没有指定要从哪个文件读出wmv,然后转换到哪个wav文件
为wm asf reader 指定源
hr=pAsfReader->QueryInterface(IID_IFileSourceFilter, (void **) &pFileSource);
BSTR pSource = m_szSrcFileName.AllocSysString();
hr = pFileSource->Load(pSource, NULL);
下面指定file writer的输出文件源
hr = pFileWriter->QueryInterface(IID_IFileSinkFilter, (void**)&pSeekFile);
pSeekFile->SetFileName(L"D:\\AudTest.wav", NULL);
pSeekFile->SetFileName的第二个参数可以设置为一个AM_MEDIA_TYPE的指针,这块应用上我还没弄好,一设置为AM_MEDIA_TYPE的指针就提示我未找到可用于建立连接的介质筛选器,就用NULL来使用默认的行为,这个问题就是CheckMediaType检查不过去,接下来抽点时间再研究
现在一切准备就绪,获取一个IMediaControl和IMediaEvent接口实例
hr = pGraphBuilder->QueryInterface(IID_IMediaControl,(void**)&pCtl);
hr = pCtl->Run();
hr = pGraphBuilder->QueryInterface(IID_IMediaEventEx,(void **)&pEvent);
pCtl->Run()是让GraphBuilder跑起来,也就是开始转换格式
再通过IMediaEvent的WaitForCompletion来检测执行状态
while (EvCode != EC_COMPLETE)
{
hr = pEvent->WaitForCompletion(1000, &EvCode);
TRACE("EvCode: %d\r\n", EvCode);
if ( FAILED(hr))
{
break;
}
}
忘了说一下:
上面IMediaEvent检测到EC_COMPLETE后会跳出循环,要执行一次pCtl->Stop()
否则wav头信息不会写入到目的文件中
最后Release用到的各种指针资源,文件已经转换完毕