糖尿病康复,内容丰富有趣,生活中的好帮手!
糖尿病康复 > 重新压缩一个AVI文件

重新压缩一个AVI文件

时间:2023-03-28 12:31:16

相关推荐

重新压缩一个AVI文件

效果图:

directshow文档中有一节关于重新压缩一个AVI,按照文章的思路:

1、选择一个压缩Filter

2、设置视频压缩属性

3、生成重新压缩graph

4、写文件

给出所有源代码(文档中已经给出了绝大部分代码)

#include "stdafx.h"

#include "dshowexample.h"

#include "RecompressingAVI.h"

// CRecompressingAVI 对话框

IMPLEMENT_DYNAMIC(CRecompressingAVI, CDialog)

CRecompressingAVI::CRecompressingAVI(CWnd* pParent /*=NULL*/)

: CDialog(CRecompressingAVI::IDD, pParent)

{

//pCompress = NULL;

m_pCompressVideo = NULL;

pMux = NULL;

pGraph = NULL;

pSeek = NULL;

pEvent = NULL;

pControl = NULL;

}

CRecompressingAVI::~CRecompressingAVI()

{

}

void CRecompressingAVI::DoDataExchange(CDataExchange* pDX)

{

CDialog::DoDataExchange(pDX);

DDX_Control(pDX, IDC_CODEC_LIST, m_wndCodeList);

}

BEGIN_MESSAGE_MAP(CRecompressingAVI, CDialog)

ON_CBN_SELCHANGE(IDC_CODEC_LIST, &CRecompressingAVI::OnCbnSelchangeCodecList)

ON_WM_TIMER()

ON_MESSAGE(WM_GRAPHNOTIFY_COMPRESS, DoHandleEvent)

END_MESSAGE_MAP()

// CRecompressingAVI 消息处理程序

BOOL CRecompressingAVI::OnInitDialog()

{

CDialog::OnInitDialog();

// TODO: 在此添加额外的初始化

//choose a comperssion filter

ChooseCompressionFilter();

SetVideoCompressorProperty();

BuildRecompressorGraph();

Write2File();

return TRUE; // return TRUE unless you set the focus to a control

// 异常: OCX 属性页应返回 FALSE

}

//choose a comperssion filter

HRESULT CRecompressingAVI::ChooseCompressionFilter()

{

HRESULT hr;

ICreateDevEnum *pSysDevEnum = NULL;

IEnumMoniker *pEnum = NULL;

IMoniker *pMoniker = NULL;

IBaseFilter *pFilter = NULL;

hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void**)&pSysDevEnum);

//列举视频压缩设备

hr = pSysDevEnum->CreateClassEnumerator(CLSID_VideoCompressorCategory, &pEnum, 0);

while (S_OK == pEnum->Next(1, &pMoniker, NULL))

{

IPropertyBag *pPropBag = NULL;

pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag);

VARIANT var;

VariantInit(&var);

hr = pPropBag->Read(L"FriendlyName", &var, 0);

if (SUCCEEDED(hr))

{

m_wndCodeList.AddString(var.bstrVal);

if(wcscmp(L"Microsoft Video 1", var.bstrVal)==0)

{

hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter, (void**)&m_pCompressVideo);

pMoniker->Release();

break;

}

}

VariantClear(&var);

pPropBag->Release();

}

m_wndCodeList.SetCurSel(0);

pSysDevEnum->Release();

pEnum->Release();

TCHAR szCompressName[MAX_PATH];

for(int i=0; i<m_wndCodeList.GetCount(); i++)

{

m_wndCodeList.GetLBText(i, szCompressName);

if(wcscmp(szCompressName, L"Microsoft Video 1")==0)

{

m_wndCodeList.SetCurSel(i);

break;

}

}

if (m_pCompressVideo==NULL)

{

MessageBox(L"没有发现Microsoft Video 1压缩器!",L"提示",MB_ICONASTERISK);

return FALSE;

}

return S_OK;

}

//set video compressor property

HRESULT CRecompressingAVI::SetVideoCompressorProperty()

{

HRESULT hr = E_FAIL;

IEnumPins *pEnumPin = NULL;

IPin *pPin = NULL;

IAMVideoCompression *pCompress = NULL;

// 寻找支持 IAMVideoCompression的引脚

m_pCompressVideo->EnumPins(&pEnumPin);

while (S_OK == pEnumPin->Next(1, &pPin, NULL))

{

hr = pPin->QueryInterface(IID_IAMVideoCompression, (void**)&pCompress);

pPin->Release();

if (SUCCEEDED(hr)) // Found the interface.

{

break;

}

}

if (SUCCEEDED(hr))

{

long lCap; // 性能标识

long lKeyFrame, lPFrame; // 真实值

double Quality;

long lKeyFrameDef, lPFrameDef; //默认值

double QualityDef;

//得到默认值和它的性能

hr = pCompress->GetInfo(0, 0, 0, 0, &lKeyFrameDef, &lPFrameDef,&QualityDef, &lCap);

if (SUCCEEDED(hr))

{

// 得到实际的值.

if (lCap & CompressionCaps_CanKeyFrame)

{

hr = pCompress->get_KeyFrameRate(&lKeyFrame);

if (FAILED(hr) || lKeyFrame < 0)

{

lKeyFrame = lKeyFrameDef;

}

}

if (lCap & CompressionCaps_CanBFrame)

{

hr = pCompress->get_PFramesPerKeyFrame(&lPFrame);

if (FAILED(hr) || lPFrame < 0)

{

lPFrame = lPFrameDef;

}

}

if (lCap & CompressionCaps_CanQuality)

{

hr = pCompress->get_Quality(&Quality);

if (FAILED(hr) || Quality < 0)

{

Quality = QualityDef;

}

}

}

}

return S_OK;

}

//build recompressor graph

HRESULT CRecompressingAVI::BuildRecompressorGraph()

{

ICaptureGraphBuilder2 *pBuild = NULL;

HRESULT hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC_SERVER,IID_ICaptureGraphBuilder2, (void **)&pBuild);

WCHAR wszOutputFile[MAX_PATH];

WCHAR wszInputFile[MAX_PATH];

//wcscpy(szPath, L"E://Documents//example.avi");

//wcscpy(szPath1, L"E://Documents//example2.avi");

wcscpy(wszOutputFile, L"I://Documents//example6.avi");

wcscpy(wszInputFile, L"I://Documents//example.avi");

pBuild->SetOutputFileName(

&MEDIASUBTYPE_Avi, //文件类型

wszOutputFile, // 文件名

&pMux, // 得到一个指向multiplexer的指针

NULL); // 得到一个指向File Write的指针

//HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&pGraph);

pBuild->GetFiltergraph(&pGraph);

IBaseFilter *pSrc = NULL;

pGraph->AddSourceFilter(wszInputFile, L"Source Filter", &pSrc);

pGraph->AddFilter(m_pCompressVideo, L"Compressor");

pBuild->RenderStream(

NULL, // 输出引脚类型

NULL, // 媒体类型

pSrc, // 源过滤器

m_pCompressVideo, // 压缩过滤器

pMux);

DWORD dwRegister;

#ifdef _DEBUG

AddToRot(pGraph, &dwRegister);

#endif

return S_OK;

}

//write to file

HRESULT CRecompressingAVI::Write2File()

{

REFERENCE_TIME rtTotal;

HRESULT hr = pMux->QueryInterface(IID_IMediaSeeking, (void**)&pSeek);

hr = pGraph->QueryInterface(IID_IMediaEventEx, (void**)&pEvent);

hr = pGraph->QueryInterface(IID_IMediaControl, (void**)&pControl);

// 设置DirectShow的时间通报

hr = pEvent->SetNotifyWindow((OAHWND)m_hWnd, WM_GRAPHNOTIFY_COMPRESS, 0);

hr = pSeek->GetDuration(&rtTotal);

SendDlgItemMessage(IDC_PROGRESS1, PBM_SETRANGE, 0, MAKELPARAM(0, rtTotal / 10000000));

UINT_PTR res = SetTimer(WM_DURATION, 100, NULL);

pControl->Run();

return S_OK;

}

HRESULT CRecompressingAVI::AddToRot(IUnknown *pUnkGraph, DWORD *pdwRegister)

{

IMoniker * pMoniker;

IRunningObjectTable *pROT;

if (FAILED(GetRunningObjectTable(0, &pROT))) {

return E_FAIL;

}

WCHAR wsz[256];

wsprintfW(wsz, L"FilterGraph %08x pid %08x", (DWORD_PTR)pUnkGraph, GetCurrentProcessId());

HRESULT hr = CreateItemMoniker(L"!", wsz, &pMoniker);

if (SUCCEEDED(hr)) {

hr = pROT->Register(ROTFLAGS_REGISTRATIONKEEPSALIVE, pUnkGraph, pMoniker, pdwRegister);

pMoniker->Release();

}

pROT->Release();

return hr;

}

LRESULT CRecompressingAVI::DoHandleEvent(WPARAM wParam, LPARAM lParam)

{

HRESULT hr = E_FAIL;

long evCode, param1, param2;

bool bComplete = false;

if (!pEvent) return hr;

//得到所有的事件

while (SUCCEEDED( hr = pEvent->GetEvent(&evCode, &param1, &param2, 0)))

{

pEvent->FreeEventParams(evCode, param1, param2);

switch(evCode)

{

case EC_USERABORT:

case EC_ERRORABORT:

case EC_COMPLETE:

bComplete = true;

break;

}

}

if (bComplete)

{

pControl->Stop(); // Important! You must stop the graph!

//关掉事件通报.

pEvent->SetNotifyWindow(NULL, 0, 0);

pEvent->Release();

pEvent = NULL;

SendDlgItemMessage(IDC_PROGRESS1, PBM_SETPOS, 100, 0);

KillTimer(WM_DURATION);

}

return S_OK;

}

void CRecompressingAVI::OnCbnSelchangeCodecList()

{

// TODO: 在此添加控件通知处理程序代码

}

void CRecompressingAVI::OnTimer(UINT_PTR nIDEvent)

{

REFERENCE_TIME rtNow;

HRESULT hr = pSeek->GetCurrentPosition(&rtNow);

if (SUCCEEDED(hr))

{

SendDlgItemMessage(IDC_PROGRESS1, PBM_SETPOS, rtNow/10000000, 0);

}

CDialog::OnTimer(nIDEvent);

}

LRESULT CRecompressingAVI::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)

{

switch (message)

{

/* ... */

case WM_GRAPHNOTIFY:

DoHandleEvent(wParam, lParam);

break;

/* ... */

}

return CDialog::WindowProc(message, wParam, lParam);

}

本文大部分是转载,内容做了一些修订,因为自己翻译也和原文差不多,所以干脆直接引用了。

原作者不知

这里我们讲解怎样用DirectShow来压缩一个AVI文件。我们重点放到视频压缩,同样的方法可以应用到音频压缩。

我们分以下几步来讲解:

1、 选择一个压缩过滤器

有许多种方法可以压缩视频或者音频,比如:

a、 本地DirectShow过滤器

b、 视频压缩管理编码器(VCM)

c、 音频压缩管理编码器(ACM)

d、 DirectX媒体对象(DMOs)

在DirectShow中,VCM被AVI Compressor过滤器封装了。同样的,ACM编码器也被ACM Wrapper过滤器封装了。DMOs被DMO Wrapper过滤器封装。系统设备枚举器提供了一个统一的方法来枚举和创建这些压缩器,我们不用考虑底层的操作。

系统设备枚举器,查看。。。。简单来说,所有Directshow filter通过类别归类,每个类型由GUID标识。对于视频压缩器,GUID类型是CLSID_VideoCompressorCategory,音频是CLSID_AudioCompressorCategory。为了枚举特定类型,系统设备枚举器建立一个枚举器对象支持IEnumMoniker接口。APP使用这个接口返回设备monikers,每个设备moniker表现一个directshow filter的实例。使用这个moniker建立filter,或者得到这个设备的友好名而不用建立filter。

为了枚举在用户系统上视频或音频compressor:

1、 CoCreateInstance建立系统设备枚举器,它有一个类ID:CLSID_SystemDeviceEnum

2、 用filter 类别GUID调用ICreateDevEnum::CreateClassEnumerator。返回IEnuMoniker接口指针

3、 使用IEnumMoniker::Next枚举设备moniker。返回一个IMoniker接口,表现这个moniker

为了得到这个moniker的友好名:

1、 IMoniker::BindToStorage,返回IPropertyBag接口指针

2、 使用IPropertyBag::Read读出FriendlyName属性

APP显示Compressor列表,选择一个

枚举设备方法请参照前面的讲述。这里我们只给出代码:

void OnInitDialog(HWND hDlg)

{

HRESULT hr;

ICreateDevEnum *pSysDevEnum = NULL;

IEnumMoniker *pEnum = NULL;

IMoniker *pMoniker = NULL;

hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL,

CLSCTX_INPROC_SERVER, IID_ICreateDevEnum,

(void**)&pSysDevEnum);

hr = pSysDevEnum->CreateClassEnumerator(

CLSID_VideoCompressorCategory, &pEnum, 0);

while (S_OK == pEnum->Next(1, &pMoniker, NULL))

{

IPropertyBag *pPropBag = NULL;

pMoniker->BindToStorage(0, 0, IID_IPropertyBag,

(void **)&pPropBag);

VARIANT var;

VariantInit(&var);

hr = pPropBag->Read(L"FriendlyName", &var, 0);

if (SUCCEEDED(hr))

{

LRESULT iSel = AddString(GetDlgItem(hDlg,

IDC_CODEC_LIST), var.bstrVal);

}

VariantClear(&var);

pPropBag->Release();

pMoniker->Release();

}

SendDlgItemMessage(hDlg, IDC_CODEC_LIST,

LB_SETCURSEL, 0, 0);

pSysDevEnum->Release();

pEnum->Release();

}

创建一个过滤器的实例,调用IMoniker::BindToObject方法。方法会返回一个IBaseFilter接口指针。就象下面那样:

IBaseFilter *pFilter = NULL;

hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter,

(void**)&pFilter);

if (SUCCEEDED(hr))

{

// 可以使用过滤器了

// 记着释放IBaseFilter接口指针

}

对于VCM编解码器,每个moniker表现一个特定的codec,所有codec由AVI压缩filter包装。BindToObject建立一个filter的实例,初始化这个codec。由此,不能通过CoCreateInstance直接在AVI压缩filter。必须通过系统设备枚举嚣。

2、 设置视频压缩属性

视频压缩过滤器可以在它的输出引脚支持IAMVideoCompression接口。使用这个接口可以设置压缩的属性,比如关键桢率,每关键帧P帧的数目,压缩质量等待。

首先,调用IBaseFilter::EnumPins方法找到过滤器的输出引脚,然后为接口查询引脚。一些过滤器不是所有的接口都支持,也有的不支持某个压缩属性。为了决定支持的属性能力,我们调用IAMVideoCompression::GetInfo来确定。这个方法返回一些信息:

a、 一个设置性能的标识

b、 一个描述字符串和版本字符串

c、 默认的关键桢速率,P帧速率,质量等参数

它按照下面的语法调用:

hr = pCompress->GetInfo(pszVersion, &cbVersion, pszDesc, &cbDesc, lKeyFrame, &lPFrame, &dblQuality, &lCap);

pszVersion和pszDesc参数是接收版本和描述字符串的宽字符缓冲区。cbVersion和cbDesc参数接收被请求的缓冲区大小。IkeyFrame,lPFrame和dblQuality参数获得默认的桢速率,p桢率和质量。质量是用从0.0到1.0的浮点数来表示的。Icap参数获得一个被或的能力标识,这个被定义为CompressionCaps枚举类型。

这些参数的任何一个都可以设置为NULL,。例如如果为了获得版本和描述字符串,第一次调用方法的时候,第一个和第三个参数为NULL。使用返回值cbVersion和cbDesc来分配缓冲区,然后再次调用该方法:

int cbVersion, cbDesc; // Size in bytes, not characters!

hr = pCompress->GetInfo(0, &cbVersion, 0, &cbDesc, 0, 0, 0, 0);

if (SUCCEEDED(hr))

{

WCHAR *pszVersion = new WCHAR[cbVersion/2];

WCHAR *pszDesc = new WCHAR[cbDesc/2];

hr = pCompress->GetInfo(pszVersion, 0, pszDesc, 0, 0, 0, 0, 0);

}

iCap参数提供了对IAMVideoCompression方法的过滤器的支持能力。例如,如果iCap包含了CompressionCaps_CanKeyFrame标志,你就可以调用IAMVideoCompression::get_KeyFrameRate方法来得到关键桢的速率,调用IAMVideoCompression::put_KeyFrameRate来设置桢速率。如果iCap的值没有包含这些参数,那么就只能使用默认的值了。

if (lCap & CompressionCaps_CanKeyFrame)

{

hr = pCompress->get_KeyFrameRate(&lKeyFrame);

if (FAILED(hr) || lKeyFrame < 0)

{

lKeyFrame = lDefaultKeyFrame; // 来自 GetInfo.

}

}

下面的代码试着在输出引脚寻找IAMVideoCompression接口。如果成功,它将会返回默认的和实际的压缩属性值。

HRESULT hr = E_FAIL;

IEnumPins *pEnum = NULL;

IPin *pPin = NULL;

IAMVideoCompression *pCompress = NULL;

// 寻找支持 IAMVideoCompression的引脚

pFilter->EnumPins(&pEnum);

while (S_OK == pEnum->Next(1, &pPin, NULL))

{

hr = pPin->QueryInterface(IID_IAMVideoCompression, (void**)&pCompress);

pPin->Release();

if (SUCCEEDED(hr)) // Found the interface.

{

break;

}

}

if (SUCCEEDED(hr))

{

long lCap; // 性能标识

long lKeyFrame, lPFrame; // 真实值

double m_Quality;

long lKeyFrameDef, lPFrameDef; //默认值

double QualityDef;

//得到默认值和它的性能

hr = pCompress->GetInfo(0, 0, 0, 0, &KeyFrameDef, &lPFrameDef,

&QualityDef, &lCap);

if (SUCCEEDED(hr))

{

// 得到实际的值.

if (lCap & CompressionCaps_CanKeyFrame)

{

hr = pCompress->get_KeyFrameRate(&lKeyFrame);

if (FAILED(hr) || lKeyFrame < 0)

lKeyFrame = lKeyFrameDef;

}

if (lCap & CompressionCaps_CanBFrame)

{

hr = pCompress->get_PFramesPerKeyFrame(&lPFrame);

if (FAILED(hr) || lPFrame < 0)

lPFrame = lPFrameDef;

}

if (lCap & CompressionCaps_CanQuality)

{

hr = pCompress->get_Quality(&Quality);

if (FAILED(hr) || Quality < 0)

Quality = QualityDef;

}

}

}

注意:如果你使用IcaptureGraphBuilder2接口来创建你的过滤图形,你可以调用IcaptureGraphBuilder2::FindInterface方法来获得IAMVideoCompression接口。

3、 建立压缩图形

下图是一个典型的AVI文件的过滤图形:

AVI_Splitter过滤器从文件的源过滤器(File Source(Async))拉数据,然后分解到视频和音频流。视频解压缩过滤器解码被压缩的视频,然后重新被视频压缩器重新压缩。decompressors选择依赖于source filter,通过通知连接自动处理。APP必须选择compressors,由列表中选择。

被压缩的视频进入到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过滤器有一个很明显的参考计数,所以以后一定要确保释放它。

graph图形

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方法来连接源过滤器的输出引脚到指定的sin过滤器。选择的,通过一个Compression filter。

前两个参数指定了用那个源过滤器的引脚来连接,通过指明引脚的分类和媒体类型来实现。异步文件源过滤器只有一个输出引脚,所以这些参数要设置成NULL。后三个参数指定了源过滤器,压缩过滤器,和Mux过滤器。

下面的代码演示了通过视频压缩过滤器来渲染视频流:

pBuild->RenderStream(

NULL, // 输出引脚类型

NULL, // 媒体类型

pSrc, // 源过滤器

pVComp, // 压缩过滤器

pMux);

假定源文件包含了音频流,AVI Splitter过滤器会在输出引脚输出音频流。为了连接这个管脚我们需要再次调用RenderStream:

pBuild->RenderStream(NULL, NULL, pSrc, NULL, pMux);

这里我们没有指定压缩过滤器。而且源过滤器的输出引脚已经连接了,因此RenderStream方法会搜索一个未连接的输出引脚到Splitter过滤器。它可以直接连接引脚到MUX过滤器。但是如果源文件没有音频流,那么第二次调用会失败。

这里,两个RenderStream是重要的,因为第二个没有指定compressor,它将从AVI splitter中连接任何未连接的pin,具体的指定必须首先调用。

此时,文件类型也可为MPEG文件,filter grpah manager可能不同。

4、 写文件

如果想写文件正常的进行,你必须调用ImediaControl::Run方法来运行过滤图形。等到播放完成后调用ImediaControl::Stop。否则文件编写不正确。

如果要显示文件的写的进度,你可以使用ImediaSeeking来查询Mux过滤器。调用ImediaSeeking::GetDuration方法来获得文件的持续时间。定时的使用ImediaSeeking::GetCurrentPostition方法来获得当前的位置,但必须要是过滤图形运行中。当前位置由percentage完成除以持续时间。

注意,一般情况下我们用ImediaSeeking接口查询过滤图形管理器来定位,如果正在写文件的话,这将是一个特例。这里我们要查询Mux过滤器就可以了。查询过滤图形定位要求回放的时候才可以,而不是写文件的时候。

看了下面的代码读者就会更加清楚了。

IMediaSeeking *pSeek = NULL;

IMediaEventEx *pEvent = NULL;

IMediaControl *pControl = NULL;

REFERENCE_TIME rtTotal;

hr = pMux->QueryInterface(IID_IMediaSeeking, (void**)&pSeek);

hr = pGraph->QueryInterface(IID_IMediaEventEx, (void**)&pEvent);

hr = pGraph->QueryInterface(IID_IMediaControl, (void**)&pControl);

// 设置DirectShow的时间通报

hr = pEvent->SetNotifyWindow((OAHWND)hwnd, WM_GRAPHNOTIFY, 0);

hr = GetDuration(&rtTotal);

SendDlgItemMessage(hwnd, IDC_PROGRESS1, PBM_SETRANGE, 0,

MAKELPARAM(0, rtTotal / 10000000));

// 开始时间.

UINT_PTR res = SetTimer(hwnd, nIDEvent, 100, NULL);

// 运行过滤图形.

pControl->Run();

当应用程序收到定时器事件,它就更新当前位置:

void OnTimer(HWND hDlg, IMediaSeeking *pSeek)

{

REFERENCE_TIME rtNow;

HRESULT hr = pSeek->GetCurrentPosition(&rtNow);

if (SUCCEEDED(hr))

{

SendDlgItemMessage(hDlg, IDC_PROGRESS1, PBM_SETPOS, rtNow/10000000, 0);

}

}

当应用程序收到DirectShow结束事件,它就可以停止图形,就象下面那样:

LRESULT CALLBACK WndProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)

{

switch (msg)

{

/* ... */

case WM_GRAPHNOTIFY:

DoHandleEvent();

break;

/* ... */

}

}

void DoHandleEvent()

{

long evCode, param1, param2;

bool bComplete = false;

if (!pEvent) return;

//得到所有的事件

while (SUCCEEDED(pEvent->GetEvent(&evCode, &param1, &param2, 0))

{

pEvent->FreeEventParams(evCode, param1, param2);

switch(evCode)

{

case EC_USERABORT:

case EC_ERRORABORT:

case EC_COMPLETE:

bComplete = true;

break;

}

}

if (bComplete)

{

pControl->Stop(); // Important! You must stop the graph!

//关掉事件通报.

pEvent->SetNotifyWindow(NULL, 0, 0);

pEvent->Release();

pEvent = NULL;

SendDlgItemMessage(IDC_PROGRESS1, PBM_SETPOS, 0, 0);

KillTimer(hwnd, nIDEvent);

}

}

如果觉得《重新压缩一个AVI文件》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。