用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

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

  1. 怎样用DirectShow来压缩一个AVI文件

    原文出处:http://littleflute.blog.hexun.com/1043233_d.html 这里我们讲解怎样用DirectShow来压缩一个AVI文件.我们重点放到视频压缩,同样的方法 ...

  2. 重新压缩一个AVI文件

    效果图: directshow文档中有一节关于重新压缩一个AVI,按照文章的思路: 1.选择一个压缩Filter 2.设置视频压缩属性 3.生成重新压缩graph 4.写文件 给出所有源代码(文档中已 ...

  3. matlab GUI学习笔记1 如何生成一个GUI文件,及如何修改保存后的.fig文件

    最近需要做一个项目的小软件,学习如何用matlab GUI生成一个小软件,带对话框等等.所以这几天对matlab GUI的分步学习进行记录.我的matlab版本是R2017a. 首先是如何在matla ...

  4. 吴恩达《机器学习》学习笔记六——过拟合与正则化

    吴恩达<机器学习>学习笔记六--过拟合与正则化 一. 过拟合问题 1.线性回归过拟合问题 2.逻辑回归过拟合问题 3.过拟合的解决 二. 正则化后的代价函数 1.正则化思想 2.实际使用的 ...

  5. ROS学习笔记六:理解ROS服务和参数

    ROS学习笔记六:理解ROS服务和参数 主要介绍ROS服务和参数,同时使用命令行工具rosservice和rosparam. ROS service service是节点之间互相通信的另一种方式,se ...

  6. opencv 手选roi区域_【opencv学习笔记六】图像的ROI区域选择与复制

    图像的数据量还是比较大的,对整张图片进行处理会影响我们的处理效率,因此常常只对图像中我们需要的部分进行处理,也就是感兴趣区域ROI.今天我们来看一下如何设置图像的感兴趣区域ROI.以及对ROI区域图像 ...

  7. JS学习笔记六:js中的DOM操作

    1. JS学习笔记六:js中的DOM操作 文章目录 1. JS学习笔记六:js中的DOM操作 1.1. 获取Dom节点 1.2. 元素属性的操作方式 1.3. DOM节点的创建.插入和删除 1.4. ...

  8. Python学习笔记六——画小猪佩奇

    目录 Python学习笔记六--画小猪佩奇 画布 画笔 属性设置 操纵命令 运动命令 画笔控制命令 全局控制命令 其他命令 Python学习笔记六--画小猪佩奇 使用Python的turtle库可以绘 ...

  9. Polyworks脚本开发学习笔记(六)-比较运算、数学运算、逻辑运算及流程控制

    Polyworks脚本开发学习笔记(六)-比较运算.数学运算.逻辑运算及流程控制 前言 比较运算.逻辑运算及流程控制是编程的基本语法,Polyworks的语法规则与VB/C#/Python等并没有很大 ...

最新文章

  1. 概率论-4.4 特征函数(待补充)
  2. 【机器学习】传统目标检测算法之DPM
  3. 2008服务器系统功能,Windows Server 2008 DNS服务器新增功能
  4. SQL Server2008R2查询数据库的物理路径
  5. Android 自动扫描歌曲,Android扫描本地音乐文件开发案例分享
  6. windows下的workon env
  7. 安装 Visual Studio Async CTP
  8. Android游戏开发LoneBall小游戏
  9. 看看故障诊断文献中的故障设置方法-中文论文篇
  10. YX133数显模组 PD数显充电数据线,TYPE-C功率显示充电线方案 介绍
  11. lol登入显示目录服务器失败,英雄联盟(LOL)登录游戏提示“连接DIR服务器失败”?...
  12. Javascript(五十四)class定义构造函数
  13. 春季养生知识多 吃萝卜可预防上火
  14. 俗语“手握金鱼骨,富贵不用愁”,是啥意思?金鱼骨怎么形成的?
  15. AWS强烈反击Elastic,欲打造自己的Elasticsearch开源产品OpenSearch
  16. 在vmware虚拟机中的ubuntu出现s001.vmdk的操作失败(磁盘空间不足)并影响ubuntu的启动问题
  17. 计算机的边界值分析法,黑盒测试:边界值分析法及测试用例设计.doc
  18. 使用mdadm创建raid mdadm命令详解
  19. mysql中IGNORE使用
  20. 投稿warning: Font T8 is of type Type 3 问题定位和解决。

热门文章

  1. 小艺读书(python)
  2. Small Office Home Office模式——线上团队开发
  3. 百度地图多个兴趣点(经纬度、坐标点)需要同时显示在地图上,如何设置自动缩放等级
  4. spring 启动之全过程 源码解析
  5. mac air 安装linux系统下载,Macbook Air安装linux重获新生
  6. [02-26]网吧母盘网上精华+个人总结=超详细|『 网吧技术软件区 』 - 迈思工作室技术论坛
  7. 计算机ata考试教案,Atasnackbar教案.doc
  8. python 九宫重排_Python蓝桥杯练习 九宫重排
  9. 计算机AZ,az connectedmachine
  10. 认罪吧,你的代码已经出卖了你!