[DirectShow]Wmv To Wav

本文介绍使用DirectShow库中的WMAsfReader、WMAudioDecodeDMO和WavDest等组件,通过编程方式实现WMV文件到WAV文件的转换过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

说实话接触到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、付费专栏及课程。

余额充值