这篇文章是对以前写过的《显示压缩前的和压缩后的视频》的补充和完善
《显示压缩前的和压缩后的视频》原文如下:
要想显示压缩前的和压缩后的视频流要用到IWMEncDataView 接口。要使用IWMEncDataViewCollection::Add方法增加一个IWMEncDataView 接口。
显示编码前的窗口
代码如下://一些基本设定省略。
IWMEncDataView* pPreview;
IWMEncDataViewCollection* pPreviewColl;
hr = CoCreateInstance( CLSID_WMEncPreview,
NULL,
CLSCTX_INPROC_SERVER,
IID_IWMEncDataView,
(void**)&pPreview);
// Retrieve the preview collection.
hr = pVidSrc->get_PreviewCollection( &pPreviewColl );
long lCookie = -1;//设定为-1,则编码则自动给你加一个唯一的cookie。
hr = pPreviewColl->Add( pPreview, &lCookie);
HWND hWnd;//一个窗口句柄,如果没有设定,则会自动调用一个系统的窗口。
hr = pPreview->SetViewSetting((DWORD) lCookie, sizeof(hWnd),
(BYTE*)&hWnd);
hr = pEncoder->Start();
// Start viewing the stream in a pop-up window.
hr = pPreview->Start(lCookie); //开始编码后,显示窗口。
显示编码后的窗口
IWMEncDataView* pPostview;
IWMEncDataViewCollection* pPostviewColl;
hr = CoCreateInstance( CLSID_WMEncPreview,
NULL,
CLSCTX_INPROC_SERVER,
IID_IWMEncDataView,
(void**)&pPostview);
// Retrieve the preview collection.
hr = pVidSrc->get_PostviewCollection( &pPostviewColl );
long lCookie = -1;//设定为-1,则编码则自动给你加一个唯一的cookie。
hr = pPostviewColl->Add( pPostview, &lCookie);
HWND hWnd;//一个窗口句柄,如果没有设定,则会自动调用一个系统的窗口。
hr = pPostview->SetViewSetting((DWORD) lCookie, sizeof(hWnd),
(BYTE*)&hWnd);
hr = pEncoder->Start();
// Start viewing the stream in a pop-up window.
hr = pPostview->Start(lCookie); //开始编码后,显示窗口。
上文只是说明了显示窗口的设置,没有文件输入和格式选择等操作,因此每次都要改源码来控制打开文件、存储路径、编码格式,改进了的程序运行界面如下:
其中的两个文件路径分别用Cstring m_path, m_path1
浏览按钮的实现分别如下:
CFileDialog file(true);//true为打开一个文件,false为保存一个文件
file.DoModal(); //响应点击事件
m_path=file.GetPathName(); //得到文件的完整路径
this->UpdateData(false); //更新数据
GetDlgItem(IDC_SCREEN) //使屏幕捕获和视频设备的按钮无效,因为已经选择了文件编码。这样会使程序更健壮一些,否则无意中点了这两个按钮会出现意外的错误。
->EnableWindow(FALSE);
GetDlgItem(IDC_DEVICE)
->EnableWindow(FALSE);
CFileDialog file1(false,".wmv",NULL,OFN_HIDEREADONLY, //保存一个文件,而且以.wmv为扩展名
"WMV Files (*.wmv)|*.wmv|");
file1.DoModal();
if(m_path1=file1.GetPathName())
GetDlgItem(IDC_COMBO)
->EnableWindow(TRUE);
this->UpdateData(false);
其中得到编码格式是通过在窗口初始化的时候加入的,通过CPreviewsDlg::Choice();调用自定义的函数,Choice()函数的具体实现如下:
void CPreviewsDlg::Choice()
{
HRESULT hr;
IWMEncoder* pEncoder;
IWMEncProfileCollection* pProColl;
IWMEncProfile* pPro;
long lCount;
int i;
// Initialize the COM library and retrieve a pointer
// to an IWMEncoder interface.
hr = CoInitialize(NULL);
hr = CoCreateInstance(CLSID_WMEncoder,
NULL,
CLSCTX_INPROC_SERVER,
IID_IWMEncoder,
(void**) &pEncoder);
// Retrieve a pointer to an IWMEncProfileCollection interface.
hr = pEncoder->get_ProfileCollection(&pProColl);
// Loop through the profile collection and retrieve a specific
// profile.
CString str;
CComBSTR bstrName(L"");
hr = pProColl->get_Count(&lCount);
for (i=0; i<lCount; i++)
{
hr = pProColl->Item(i, &pPro);
hr = pPro->get_Name(&bstrName);
str=bstrName.Copy();
m_choice.InsertString(i,str);
}
pEncoder->Release();
}
并使用CcomboBox类的OnSelchangeCombo()来得到选择编码的格式
void CPreviewsDlg::OnSelchangeCombo()
{
j=m_choice.GetCurSel(); //把当前的选择赋值给j
if(j!=100) //j的初值为100,如果不等于100说明已经选择了格式
GetDlgItem(IDC_START) //使开始编码按钮有效
->EnableWindow(TRUE);
}
关于屏幕和设备的区别是通过全局变量h来标志的,h=1是屏幕捕获, h=2是从设备得到视频。程序如下:
void CPreviewsDlg::OnScreen()
{
h=1;
GetDlgItem(IDC_DEVICE)
->EnableWindow(FALSE);
GetDlgItem(IDC_BROWER)
->EnableWindow(FALSE);
}
void CPreviewsDlg::OnDevice()
{
h=2;
GetDlgItem(IDC_SCREEN)
->EnableWindow(FALSE);
GetDlgItem(IDC_BROWER)
->EnableWindow(FALSE);
}
当配置好后,就可以开始编码了,程序如下:
void CPreviewsDlg::OnStart()
{
hwnd=::GetDlgItem(this->GetSafeHwnd(),IDC_VIEW); //得到预览编码前窗口的句柄
phwnd=::GetDlgItem(this->GetSafeHwnd(),IDC_POSTVIEW); //得到预览编码后窗口的句柄
// g_MyThread->SetParentWnd(hwnd); //可以去掉
g_MyThread->PostThreadMessage(WM_START,0,0 ); //给线程发送消息,开始编码
GetDlgItem(IDC_WATCH)
->EnableWindow(TRUE);
GetDlgItem(IDC_STOP)
->EnableWindow(TRUE);
GetDlgItem(IDC_START)
->EnableWindow(FALSE);
}
线程中的开始函数如下:
void CMyThread::OnStart( WPARAM wParam, LPARAM lParam)
{
IWMEncSourceGroupCollection* pSrcGrpColl;
IWMEncSourceGroup* pSrcGrp;
IWMEncSource* pAudSrc1;
IWMEncSource* pSrc1;
IWMEncVideoSource* pVidSrc1;
// IWMEncDataView* pPreview;
IWMEncDataViewCollection* pPostviewColl;
IWMEncDataViewCollection* pPreviewColl;
IWMEncProfileCollection* pProColl;
IWMEncSourcePluginInfoManager* pSrcPlugMgr;
IWMEncPluginInfo* pPlugInfo;
IWMEncProfile* pPro;
IWMEncDisplayInfo* pDispInfo;
IWMEncAttributes* pAttr;
IWMEncBroadcast* pBrdcast;
IWMEncFile* pFile;
WMENC_ENCODER_STATE enumEncoderState;
CComBSTR bstrResource(L"");
long lCount;
short i;
hr = CoInitialize(NULL);
hr = CoCreateInstance(CLSID_WMEncoder, //初始化com库
NULL,
CLSCTX_INPROC_SERVER,
IID_IWMEncoder,
(void**) &pEncoder);
// Retrieve a pointer to an IWMEncSourceGroupCollection
// interface.
hr = pEncoder->get_SourceGroupCollection(&pSrcGrpColl);
// Add a source group to the collection. named "SG_1"
hr = pSrcGrpColl->Add(L"SG_1", &pSrcGrp);
// Add a video and audio source to the source group.
hr = pSrcGrp->AddSource(WMENC_VIDEO, &pSrc1);
hr = pSrcGrp->AddSource(WMENC_AUDIO, &pAudSrc1);
// Retrieve a pointer to an IWMEncVideoSource interface.
hr = pSrc1->QueryInterface(IID_IWMEncVideoSource,
(void**)&pVidSrc1);
if(h==0) //h=0说明是编码文件
{
CComBSTR bstrInFile("");
bstrInFile.Append(pThread->m_path); //CString和CComBSTR的转化
hr = pAudSrc1->SetInput(bstrInFile);
hr = pVidSrc1->SetInput(bstrInFile);
}
else
{
hr = pEncoder->get_SourcePluginInfoManager(&pSrcPlugMgr);
hr = pSrcPlugMgr->Item(0, &pPlugInfo);
hr = pPlugInfo->Item(1, &bstrResource); //设置声音的输入
CComBSTR Aud(L"DEVICE://");
Aud.Append(bstrResource);
hr=pAudSrc1->SetInput(Aud);
if(h==1)
hr=pVidSrc1->SetInput(L"ScreenCap://Screen Capture"); //如果h=1,视频来源为屏幕
else
{
hr = pSrcPlugMgr->Item(1, &pPlugInfo);
hr = pPlugInfo->Item(1, &bstrResource);
CComBSTR Vid(L"DEVICE://"); //得到视频设备的输入
Vid.Append(bstrResource);
hr=pVidSrc1->SetInput(Vid);
}
}
// Choose a profile from the collection.
CComBSTR bstrName = NULL;
CComVariant varProfile;
varProfile.vt = VT_DISPATCH;
hr = pEncoder->get_ProfileCollection(&pProColl);
hr = pProColl->get_Count(&lCount);
for (i=0; i<lCount; i++)
{
hr = pProColl->Item(i, &pPro);
hr = pPro->get_Name(&bstrName);
if(i==j) //选择编码格式
{
// Set the profile in the source group.
varProfile.pdispVal = pPro;
hr = pSrcGrp->put_Profile(varProfile);
break;
}
}
// Retrieve a pointer to an IWMEncDisplayInfo interface.
hr = pEncoder->get_DisplayInfo(&pDispInfo);
// Describe the encoded content.
CComBSTR bstrAuthor("Let5fly");
CComBSTR bstrCopyright("Copyright 2000. All rights reserved.");
CComBSTR bstrDescription("Description of the encoded content.");
CComBSTR bstrRating("Content rating.");
CComBSTR bstrTitle("Test the Encoder");
hr = pDispInfo->put_Author(bstrAuthor); //描述信息
hr = pDispInfo->put_Copyright(bstrCopyright);
hr = pDispInfo->put_Description(bstrDescription);
hr = pDispInfo->put_Rating(bstrRating);
hr = pDispInfo->put_Title(bstrTitle);
// Retrieve a pointer to an IWMEncAttributes interface.
hr = pEncoder->get_Attributes(&pAttr);
// Add an attribute to the attributes collection.
CComBSTR bstrAttrName(L"DateCreated: ");
CComVariant varValue;
varValue.vt = VT_BSTR;
varValue.bstrVal = L"02/15/2000";
hr = pAttr->Add(bstrAttrName,varValue);
// Retrieve a pointer to an IWMEncBroadcast interface.
hr = pEncoder->get_Broadcast(&pBrdcast);
// Specify a port number and a protocol.
hr = pBrdcast->put_PortNumber(WMENC_PROTOCOL_HTTP, 527); //设定广播端口为527
// Retrieve a pointer to an IWMEncFile interface.
hr = pEncoder->get_File(&pFile);
// Specify a file in which to save encoded content.
CComBSTR bstrSaveFile("");
bstrSaveFile.Append(pThread->m_path1); //存储编码后的文件
hr = pFile->put_LocalFileName(bstrSaveFile);
// Retrieve a pointer to a preview object.
hr = CoCreateInstance( CLSID_WMEncPreview,
NULL,
CLSCTX_INPROC_SERVER,
IID_IWMEncDataView,
(void**)&pPreview);
hr = CoCreateInstance( CLSID_WMEncPreview, //分别初始化预览窗口
NULL,
CLSCTX_INPROC_SERVER,
IID_IWMEncDataView,
(void**)&pPostview);
// Retrieve the preview collection.
hr = pSrc1->get_PreviewCollection( &pPreviewColl );
hr = pSrc1->get_PostviewCollection(&pPostviewColl);
// Add the postiew object to the data view collection. If you set the
// cookie to -1, the encoder engine automatically generates a unique
// cookie.
hr = pPreviewColl->Add( pPreview, &lCookie);
hr = pPostviewColl->Add( pPostview, &lCookiePostview);
// Continue configuring the encoder engine.
hr = pEncoder->PrepareToEncode(VARIANT_TRUE);
hr = pPreview->SetViewSetting((DWORD) lCookie,
sizeof(hwnd),
(BYTE*)&hwnd);
hr = pPostview->SetViewSetting((DWORD)lCookiePostview, //设置视频输入
sizeof(phwnd),
(BYTE*)&phwnd);
hr = pEncoder->Start();
AfxMessageBox("Encoder have start!");
// hr = pPreview->Start(lCookie);
// Wait until the engoder engine stops before exiting the application.
// You can do this by using the _IWMEncoderEvents object to create an
// event sink.
hr = pEncoder->get_RunState(&enumEncoderState); //得到运行状态
}
其中
HRESULT hr;
IWMEncoder* pEncoder;
IWMEncDataView* pPreview;
IWMEncDataView* pPostview;
long lCookie = -1;
long lCookiePostview =2;
均定义成了全局变量,因为在其他的函数中也要用到。
当要观看视频时就向线程中的watch()函数发送消息
void CPreviewsDlg::OnWatch()
{
// w=2;
g_MyThread->PostThreadMessage(WM_WATCH,0,0);
}
watch()函数通过全局变量w的值判断是显示编码前的还是编码后的视频,程序如下:
void CMyThread::Watch( WPARAM wParam, LPARAM lParam)
{
if(w==1)
hr = pPreview->Start(lCookie);
else
hr = pPostview->Start(lCookiePostview);
AfxMessageBox("Have previwe!");
}
最后要说明的一点是:线程在窗口初始的时候就创建了,并通过Gethwnd()得到窗口句柄。(因为要在CmyThread类中使用它的变量和函数)
g_MyThread = (CMyThread*)AfxBeginThread( RUNTIME_CLASS(CMyThread),0,0);
g_MyThread->Gethwnd(this);
Gethwnd()的实现如下:
CPreviewsDlg* pThread;
bool Gethwnd(CPreviewsDlg* pMain)
{
pThread=pMain;
return 1;
}
关于编码视频的预览程序介绍完了,可能说的有点乱,如果想要程序请和我联系 let5fly@msn.com