[DirectShow]Wmv To Wav

说实话接触到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用到的各种指针资源,文件已经转换完毕


评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值