Jump to PiXCL Home page

Windows Media Encoder 9 SDK Sample Code Bug Fix

According to Microsoft the Windows Media Encode 9 SDK was retired in mid-2010, and replaced by the Expression Library which is intended to be used with dotNET languages such as C#.

The WME SDK makes heavy use of the COM automation libraries, and is notoriously difficult to learn as the various method and property names required are hidden behind obscure COM calls. That is, just looking at application source code it's not immediately possible for a programmer new to the SDK to understand what's really happening.

The downloaded SDK also contains some sample code that is supposed to convert (i.e. encode) one video file format to another, amongst other things. This sample code has been aggravating programmers for years, as it does not work as written, and it's unclear why there is a problem.

If the Windows Media Encoder application (required to be installed to get the SDK) is run, it will quite happily convert an input video file, but getting the same function to work using the SDK is impossible using the sample code provided.

One of the most common frustrating errors in the sample code is the call to

hr = pEncoder->PrepareToEncode(VARIANT_TRUE);         

that crashes for no apparent reason. Depending on the testing process, the fatal error may appear as an invalid handle.

This page describes why there is a problem, and what can be done about it to get working code.

In summary , PrepareToEncode initializes the encoder object and makes it ready for the encoding process to run. If the setup of the encoder object is not correct and complete, the call will crash. The most common reason, located after some days of poking around, is that the encoder does not have a valid video profile present in the ProfileGroup. The relevent method in the automation object (wmenc.exe) is poorly written and does not include any kind of graceful error handling. This makes debugging next to impossible, and did I mention very frustrating...

Hence, when the ProfileGroup causes the crash, it's because the code is looking for audio / video input object handles and is not finding what it wants.

It also turns out that the Windows Media Encoder application looks to different profiles (described below) while the SDK sample code looks to default profiles in the SYSTEM32 directory.

The sample code also wants to create a console window, which is unnecessary and undesirable.

So how is this to be fixed? Here's the updated sample code.

BOOL SaveAVItoWMV (WCHAR *pAviFileName)
{
// Declare variables.
HRESULT hr;
IWMEncoder* pEncoder= NULL;
IWMEncSourceGroupCollection* pSrcGrpColl = NULL;
IWMEncSourceGroup* pSrcGrp = NULL;
IWMEncSource* pSrc = NULL;
IWMEncSource* pSrcAud = NULL;
IWMEncVideoSource* pSrcVid = NULL;
IWMEncProfileCollection* pProColl = NULL;
IWMEncProfile* pPro = NULL;
IWMEncProfile2* pPro2 = NULL;
IWMEncFile* pFile = NULL;
IWMEncAttributes* pAttr = NULL;
IWMEncDisplayInfo* pDispInfo = NULL;
CComBSTR bstrName = NULL;
long lCount = 0;
int i;
// Initialize the COM library and retrieve a pointer to an IWMEncoder interface.
// Don't need this if COM is already initialized.
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
if ( SUCCEEDED( hr ) )
{
hr = CoCreateInstance(CLSID_WMEncoder,
NULL,
CLSCTX_INPROC_SERVER,
IID_IWMEncoder,
(void**) &pEncoder);
if (REGDB_E_CLASSNOTREG == hr)
{
MessageBox(ghwndApp,L".WMV video file CLSID_WMEncoder class not registered.",L"Unexpected Error",MB_OK);
return FALSE;
}
}
 
// Retrieve the source group collection.
if ( SUCCEEDED( hr ) )
{
hr = pEncoder->get_SourceGroupCollection(&pSrcGrpColl);
}
 
long srcCount = -1;
pSrcGrpColl->get_Count(&srcCount);
// Unnecessary, srcCount is usually 0
if (0 < srcCount)
{
for (i = 0; i < srcCount; i++)
{
pSrcGrpColl->Remove(CComVariant(i));
}
}
 
// Add a source group to the collection.
if ( SUCCEEDED( hr ) )
{ // Arbitrary name, so we'll use LancerProject.
hr = pSrcGrpColl->Add(CComBSTR(L"LancerProject"), &pSrcGrp);
}
 
hr = CoCreateInstance(CLSID_WMEncProfile2,NULL,CLSCTX_INPROC_SERVER,
IID_IWMEncProfile2,(void**)&pPro2);
// Get the Program Files directory, make the path.
hr = pPro2->LoadFromFile(CComBSTR("C:\\Program Files\\Windows Media Components\\Encoder\\Settings\\d0_cbr_high.prx"));
hr = pPro2->get_Name(&bstrName);
 
//
// If we make the next two AddSource calls without the
// AutoSetFileSource as well,  put_Profile below fails.
// Here, we are simply adding one audio and one video source object,
// but they are  not initialized with input devices.
if ( SUCCEEDED( hr ) )
{
hr = pSrcGrp->AddSource(WMENC_AUDIO, &pSrcAud);
}
if ( SUCCEEDED( hr ) )
{
hr = pSrcGrp->AddSource(WMENC_VIDEO, &pSrc);
}
 
// Instead, try AutoSetFileSource. IN DEBUG mode this displays
// 5 invalid handle messages in debug, which can be ignored.
// In Release mode COM handles the errors and eventually it returns S_OK.
hr = pSrcGrp->AutoSetFileSource(CComBSTR(pAviFileName));
 
// Retrieve an IWMEncVideoSource pointer. Ignore any errors in DEBUG mode,
// it eventually returns S_OK.
// Errors are handled internally by COM in Release mode?
if ( SUCCEEDED( hr ) )
{
hr = pSrc->QueryInterface(IID_IWMEncVideoSource, (void**)&pSrcVid);
}
 
// Add a video and audio source to the source group.
// This is the input  .AVI file in both cases.
if ( ((SUCCEEDED(hr)) && (NULL != pSrcAud)) )
{
hr = pSrcAud->SetInput(CComBSTR(pAviFileName));
}
if ( SUCCEEDED( hr ) )
{
hr = pSrcVid->SetInput(CComBSTR(pAviFileName));
}
 
// There has to be both sources and inputs to those
// sources or this  call fails as shown.
hr = pSrcGrp->put_Profile(CComVariant(pPro2));
if (0xc00d1b5f == hr)
MessageBox(ghwndApp,L"The media content defined in the profile does not match the media content defined in the source group.",L"Debug",MB_OK);
 
// Specify the cropping margins. This is optional.
/*
if ( SUCCEEDED( hr ) )
{
hr = pSrcVid->put_CroppingBottomMargin(5);
}
if ( SUCCEEDED( hr ) )
{
hr = pSrcVid->put_CroppingTopMargin(5);
}
if ( SUCCEEDED( hr ) )
{
hr = pSrcVid->put_CroppingLeftMargin(3);
}
if ( SUCCEEDED( hr ) )
{
hr = pSrcVid->put_CroppingRightMargin(3);
}
*/
// TODO: Fill in the description object members.
// May add some optional setup dialog
 
if ( SUCCEEDED( hr ) )
{
hr = pEncoder->get_DisplayInfo(&pDispInfo);
}
if ( SUCCEEDED( hr ) )
{
hr = pDispInfo->put_Author(CComBSTR(L"LancerProject.ca"));
}
if ( SUCCEEDED( hr ) )
{
hr = pDispInfo->put_Copyright(CComBSTR(L" Copyright 2011"));
}
if ( SUCCEEDED( hr ) )
{
hr = pDispInfo->put_Description(CComBSTR(L"Endoscope video sequence"));
}
if ( SUCCEEDED( hr ) )
{
hr = pDispInfo->put_Rating(CComBSTR(L"Rating"));
}
if ( SUCCEEDED( hr ) )
{
hr = pDispInfo->put_Title(CComBSTR(L"Endoscope Video"));
}
 
// Add some relevent name-value pair attributes to the collection.
// These are abitrary, and  here are websitelurl and app name/video file
if ( SUCCEEDED( hr ) )
{
hr = pEncoder->get_Attributes(&pAttr);
}
if ( SUCCEEDED( hr ) )
{
hr = pAttr->Add(CComBSTR(L"WebSite:"),
    CComVariant(L"www.lancerproject.ca"));
hr = pAttr->Add(CComBSTR(L"Endoscope"),
    CComVariant(L"Saved WMV File"));
}
 
// Specify (ie create) a file object in which to save encoded content.
if ( SUCCEEDED( hr ) )
{
hr = pEncoder->get_File(&pFile);
}
// All we do is change the file extension at present
if ( SUCCEEDED( hr ) )
{
// Copy the name and change the extension
int length = wcslen(pAviFileName);
WCHAR *pWmvFileName = new WCHAR[length + 1];
wcsncpy_s(pWmvFileName,length + 1,pAviFileName,length - 4);
wcscat_s(pWmvFileName,length + 1,L".wmv");
hr = pFile->put_LocalFileName(CComBSTR(pWmvFileName));
}
// Choose a profile from the collection. This is required to
// succeed when PrepareToEncode is called, below.
// Here is also the problem. get_ProfileCollection goes to the
// system directory and accesses WMSysPr9.prx (seems to be the
// WME default, and out of date codec file)
// while we REALLY want C:\Program Files\Windows Media
// Components\Encoder\Profiles
// which is where WMENC.EXE gets the codecs that do work. Calling
// set_ProfileDirectory and Refresh does NOT load the correct profile set.
// Calling  SetCurrentDirectory does not help either.
// So we need a pPro2
 
 
/*
BSTR profileDirectory;
if ( SUCCEEDED( hr ) )
{
hr = pEncoder->get_ProfileCollection(&pProColl);
}
hr = pProColl->get_ProfileDirectory(&profileDirectory);
hr = pProColl->Refresh();
 
if ( SUCCEEDED( hr ) )
{
hr = pProColl->get_Count(&lCount);
}
for (i = 0; i < lCount; i++)
{
if ( SUCCEEDED( hr ) )
{
hr = pProColl->Item(i, &pPro);
}
if ( SUCCEEDED( hr ) )
{
hr = pPro->get_Name(&bstrName);
}
if (0 == _wcsicmp(bstrName,CComBSTR("DVD quality video (VBR)")))
{
// Set the profile in the source group.
if ( SUCCEEDED( hr ) )
{
hr = pSrcGrp->put_Profile(CComVariant(pPro));
}
break;
}
hr = S_FALSE;
}
*/
 
 
// Start the encoding process.
if ( SUCCEEDED( hr ) )
{
BSTR name;
pEncoder->get_Name(&name);
pEncoder->put_Name(CComBSTR("LancerProject"));
VARIANT_BOOL autoArchive;
pEncoder->get_EnableAutoArchive(&autoArchive);
VARIANT_BOOL remoteAdmin;
pEncoder->get_RemoteAdmin(&remoteAdmin);
long errorState;
pEncoder->get_ErrorState(&errorState);
 
hr = pEncoder->PrepareToEncode(VARIANT_TRUE);
 
if (0xC00D1B67 == hr)< /FONT > < /FONT >
MessageBox(ghwndApp,L"No profile found in the source group",L"Debug",MB_OK);
 
}
if ( SUCCEEDED( hr ) )
{
// Have encoding stop automatically.
pEncoder->put_AutoStop(VARIANT_TRUE);
hr = pEncoder->Start();
// see pEncoder->RunState
// Stop the encoding process.
 
if ( SUCCEEDED( hr ) )
{
// Wait till encoding is complete
MSG msg;
WMENC_ENCODER_STATE runState;
pEncoder->get_RunState(&runState);
while(WMENC_ENCODER_RUNNING == runState)
{
// In order for the events to be triggered correctly,
// the windows message queue needs to be processed.
while(PeekMessage(&msg,NULL,NULL,NULL,PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// Test the run state.
pEncoder->get_RunState(&runState);
}
hr = pEncoder->Stop();
}
}
// Release pointers.
if ( NULL != pSrcGrpColl )
{
pSrcGrpColl->Release();
pSrcGrpColl = NULL;
}
if ( NULL != pSrcGrp )
{
pSrcGrp->Release();
pSrcGrp = NULL;
}
if ( NULL != pProColl )
{
pProColl->Release();
pProColl = NULL;
}
if ( NULL != pPro )
{
pPro->Release();
pPro = NULL;
}
if ( NULL != pFile )
{
pFile->Release();
pFile = NULL;
}
if ( NULL != pSrcAud )
{
pSrcAud->Release();
pSrcAud = NULL;
}
if ( NULL != pSrcVid )
{
pSrcVid->Release();
pSrcVid = NULL;
}
if ( NULL != pSrc )
{
pSrc->Release();
pSrc = NULL;
}
if ( NULL != pAttr )
{
pAttr->Release();
pAttr = NULL;
}
if ( NULL != pDispInfo )
{
pDispInfo->Release();
pDispInfo = NULL;
}
if ( NULL != pEncoder )
{
pEncoder->Release();
pEncoder = NULL;
}
if ( SUCCEEDED( hr ) )
return TRUE;
return FALSE;
}

I hope you found this bug fix helpful. Please have a look around rest of the website.

Home

USB Touch Devices

USB Video Endoscopes


 Copyright © 2005-2012 PiXCL Automation Technologies Inc, Canada. All Rights Reserved.