您的位置:首页 > 其它

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

2015-01-13 08:34 405 查看
用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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: