Directshow学习笔记六-----重新压缩一个AVI文件(个人学习总结,仅供参考)

用DirectShow来压缩一个AVI文件

一、 选择一个压缩过滤器
有许多种方法可以压缩视频或者音频,比如:
a、 本地DirectShow过滤器
b、 视频压缩管理编码器(VCM)
c、 音频压缩管理编码器(ACM)
d、 DirectX媒体对象(DMOs)

系统设备枚举器提供了一个统一的方法来枚举和创建这些压缩器,我们不用考虑底层的操作。

代码:

//获取编解码器列表

// 初始化COM  
HRESULT hr = CoInitialize(NULL);
ICreateDevEnum *pSysDevEnum = NULL;
//使用CoCreateInstance函数创建系统枚举器组件对象,并获得ICreateDevEnum接口;
hr = CoCreateInstance(CLSID_SystemDeviceEnum,NULL,CLSCTX_INPROC_SERVER,
IID_ICreateDevEnum,(void**)&pSysDevEnum);
if(FAILED(hr))
{
return;
}
//使用接口方法ICreateDevEnum::CreateClassEnumerator为指定的Filter注册类型目录
//创建一个枚举器,并获得IEnumMoniker接口;
IEnumMoniker *pEnumCat = NULL;
hr = pSysDevEnum->CreateClassEnumerator(CLSID_VideoCompressorCategory,
&pEnumCat,0);
if(hr == S_OK)
{
//枚举名称
IMoniker *pMoniker = NULL;
ULONG cFetched;
while(pEnumCat->Next(1,&pMoniker,&cFetched) == S_OK)
{
if(pMoniker)
{
WCHAR * wzDisplayName = NULL;
IPropertyBag *pPropBag;
IBaseFilter *pFilter = NULL;


//获取当前设备的显示名字
//hr = pMoniker->GetDisplayName(NULL,NULL,&wzDisplayName);
//调用IMoniker::BindToStorage之后,可以访问设备标识的属性集,
//比如得到Display Name,Friendly Name等;
hr = pMoniker->BindToStorage(0,0,IID_IPropertyBag,
(void **)&pPropBag);
if(SUCCEEDED(hr))
{
//获得Filter的FriendlyName
VARIANT varName;
VariantInit(&varName);
hr = pPropBag->Read(L"FriendlyName",&varName,NULL);


if(SUCCEEDED(hr))
{
//调用IMoniker::BindToOject可以将设备标识生成一个DirectShow Filter,
//随后调用IFilterGraph::AddFilter,并将之加入到FilterGraph中就可以参与工作了
//生成一个filter绑定到设备上。
hr = pMoniker->BindToObject(0,0,IID_IBaseFilter,(void**)&pFilter);
}
if(SUCCEEDED(hr))
{
m_combodecode.AddString(CString(varName.bstrVal));
}
//释放使用过的接口
if(pFilter)
{
pFilter->Release();
pFilter = NULL;
}
}
pPropBag->Release();
}
pMoniker->Release();
}
pEnumCat->Release();
}
pSysDevEnum->Release();


二、 设置视频压缩属性

视频压缩过滤器可以在它的输出引脚支持IAMVideoCompression接口。使用这个接口可以设置压缩的属性,比如桢率,压缩质量等待。
首先,调用IBaseFilter::EnumPins方法找到过滤器的输出引脚,然后为接口查询引脚。一些过滤器不是所有的接口都支持,也有的不支持某个压缩属性。为了决定支持的属性能力,我们调用IAMVideoCompression::GetInfo来确定。这个方法返回一些信息:
a、 一个设置性能的标识
b、 一个描述字符串和版本字符串
c、 默认的桢速率,质量等参数
暂时没研究,只是

//调用IAMVideoCompression::put_KeyFrameRate来设置桢速率。

hret = pAMCompress->put_KeyFrameRate(8);

三、建立压缩图形

AVI_Splitter过滤器从文件的源过滤器(File Source(Async))拉数据,然后分解到视频和音频流。视频解压缩过滤器解码被压缩的视频,然后重新被视频压缩器重新压缩。
被压缩的视频进入到AVI Mux过滤器。音频流在这个例子中没有被压缩,因此它直接从AVI Splitter传输到AVI Mux。AVI Mux进行隔行扫描,然后使用File Write过滤器将数据输出到磁盘上。注意,就算原始文件里面没有音频流,AVI Mux过滤器也是必须的。最简单的方法创建这种过滤图形就是使用Capture Graph Builder,这是DirectShow里面为了建立捕获图形或者别的定制的过滤图形的一个部件。
注意:DirectShow里面包含了两个Capture Graph Builder版本。它们提供了不同的接口和类的标识。早期的版本类标识是CLSID_CaptureGraphBuild,接口是IcaptureGraphBuilder。它兼容存在的应用程序。新版本的类标识是CLSID_CaptureGraphBuilder2新的接口名称是IcaptureGraphBuilder2。新的接口比老的接口有更多的灵活性。
创建Capture Graph Builder我们还是使用CoCreateInstance:
 ICaptureGraphBuilder2 *pBuild = NULL;
 hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, 
                         NULL, CLSCTX_INPROC_SERVER,
     IID_ICaptureGraphBuilder2, (void **)&pBuild);
然后我们使用Capture Graph Builder来建立过滤图形:
a、 建立部分渲染的过滤图形,它包含AVI Mux 过滤器和File Writer过滤器。
b、 添加源过滤器和压缩过滤器。
c、 连接源过滤器到MUX 过滤器。
下面逐步的解释每一个细节:

建立渲染段
为了建立过滤图形的渲染段,调用IcaptureGraphBuilder2::SetOutputFileName方法。它返回一个MUX的过滤器和File Write的指针。MUX是下面建立过滤图形所需要的,但是这个例子不需要File Write,因此,它的参数为NULL。
 IBaseFilter *pMux = NULL;
 pBuild->SetOutputFileName(
          &MEDIASUBTYPE_Avi, //文件类型
         wszOutputFile,     // 文件名
         &pMux,             // 得到一个指向multiplexer的指针
        NULL);             // 得到一个指向File Write的指针

当该方法返回,MUX过滤器有一个很明显的参考计数,所以以后一定要确保释放它。MUX过滤器提供了两个接口来控制AVI格式:
IconfigInterleaving接口:设置交错模式
IconfigAviMux接口:设置主流和AVI兼容性的索引
添加源过滤器和压缩过滤器
下一步我们要在过滤图形中添加源过滤器和压缩过滤器。当你调用SetOutputFileName的时候,Capture Graph Builder会自动的创建一个过滤图形管理器的实例。你可以调用IcaptureGraphBuilder::GetFiltergraph方法来获得刚才创建的过滤图形管理器的指针。
 IGraphBuilder *pGraph = NULL;
 pBuild->GetFiltergraph(&pGraph);
现在我们该调用IgraphBuilder::AddSourceFilter方法来添加异步文件源过滤器,然后调用IfilterGraph::AddFilter方法来添加视频压缩过滤器:
 IBaseFilter *pSrc = NULL;
 pGraph->AddSourceFilter(wszInputFile, L"Source Filter", &pSrc);
 pGraph->AddFilter(pVComp, L"Compressor");
到了这一步我们的状态就象下图那样,源过滤器和压缩过滤器没有和别的任何过滤器连接。
 
连接源到Mux
最后一步就是通过视频压缩过滤器连接源过滤器到AVI Mux过滤器。我们使用IcaptureGraphBuilder2::RenderStream方法来连接源过滤器的输出引脚到指定的过滤器。
前两个参数指定了用那个源过滤器的引脚来连接,通过指明引脚的分类和媒体类型来实现。异步文件源过滤器只有一个输出引脚,所以这些参数要设置成NULL。后三个参数指定了源过滤器,压缩过滤器,和Mux过滤器。
下面的代码演示了通过视频压缩过滤器来渲染视频流:
pBuild->RenderStream(
        NULL,       // 输出引脚类型
        NULL,       // 媒体类型
        pSrc,       // 源过滤器
        pVComp,     // 压缩过滤器
        pMux);      
假定源文件包含了音频流,AVI Splitter过滤器会在输出引脚输出音频流。为了连接这个管脚我们需要再次调用RenderStream:
 pBuild->RenderStream(NULL, NULL, pSrc, NULL, pMux);
这里我们没有指定压缩过滤器。而且源过滤器的输出引脚已经连接了,因此RenderStream方法会搜索一个未连接的输出引脚到Splitter过滤器。它可以直接连接引脚到MUX过滤器。但是如果源文件没有音频流,那么第二次调用会失败。

代码如下:

//a、 建立部分渲染的过滤图形,它包含AVI Mux 过滤器和File Writer过滤器。
//创建一个过滤器的实例,调用IMoniker::BindToObject方法。方法会返回一个IBaseFilter接口指针
pBuilder->SetOutputFileName( &MEDIASUBTYPE_Avi, //文件类型
dstFile.AllocSysString(), //文件名
&pMux, // 得到一个指向multiplexer的指针
NULL); // 得到一个指向File Write的指针
//b、 添加源过滤器和压缩过滤器。
//调用IgraphBuilder::AddSourceFilter方法来添加异步文件源过滤器
pGraph->AddSourceFilter(srcFile.AllocSysString(), L"Source Filter", &pSrc);


if(decodeName.IsEmpty())
{
MessageBox("请选择编解码器!","提示");
return;
}


pCompress = CreateDecodeDevice(CLSID_VideoCompressorCategory,decodeName);


if (pCompress==NULL)
{
MessageBox("没有发现该压缩器!","提示",MB_ICONASTERISK);
return;
}


//b、 添加源过滤器和压缩过滤器。
//调用IfilterGraph::AddFilter方法来添加视频压缩过滤器
pGraph->AddFilter(pCompress,L"Compressor");


IPin* pCompressIn,* pCompressOut;
// 寻找支持 IAMVideoCompression的引脚
pCompressIn  =  FindPin(pCompress,PINDIR_INPUT) ;
pCompressOut =  FindPin(pCompress,PINDIR_OUTPUT);

IAMVideoCompression * pAMCompress ;
pCompressOut->QueryInterface(IID_IAMVideoCompression,(void**)&pAMCompress);
HRESULT hret;
//调用IAMVideoCompression::get_KeyFrameRate方法来得到关键桢的速率,
//调用IAMVideoCompression::put_KeyFrameRate来设置桢速率。
hret = pAMCompress->put_KeyFrameRate(8);


//c、 连接源过滤器到MUX 过滤器
//使用IcaptureGraphBuilder2::RenderStream方法来连接源过滤器的输出引脚到指定的过滤器。
pBuilder->RenderStream(NULL,NULL,pSrc,pCompress,pMux);
HRESULT hr = pMux->QueryInterface(IID_IMediaSeeking, (void**)&pSeek);


    pGraph->QueryInterface(IID_IMediaEventEx, (void **)&pEvent);
hret =  pEvent->SetNotifyWindow((OAHWND)m_hWnd,CM_NOTIFY,0);


pMediaControl->Run();

暂时就了解这么多,希望高手指点。

亟待解决的问题是,如何将预览视频压缩后保存,边预览边保存。

源码地址:http://download.csdn.net/detail/afu1972715000/8356333




你可能感兴趣的:(多媒体/流媒体开发)