Microsoft DTV-DVD Video Decoder at High Resolutions

It is really great news that Windows 7 offers quite some goodies for MPEG-4 support right out of the box, but some things appear to be made not so accurate.

The component responsible for video decoding is Microsoft DTV-DVD Video Decoder (also known as Microsoft MPEG-2 Video Decoder), available with DirectShow interface. It offers video decoding capabilities for video resolutions up to 1920×1088 (Full HD), including DXVA version 2 mode. The decoder is a “preferred decoder” and is given a first chance to be taken when it comes for Intelligent Connect to render a video stream.

Technology went ahead though, stepped behind 1920×1088 in particular. Microsoft DTV-DVD Video Decoder DirectShow filter still remains the first take in H.264 video decoding, and what however is taking place – the decoder accepts the format (media type) which is clearly cannot decode, neither according to specification, nor in real life. As soon as pin connection succeeds, DirectShow is not trying to look for another decoder, which can truly decode the feed. Result: failure to decode video.

The problem takes place in a quite surprising way. Given 5 megapixel video (2560×1592, H.264, Baseline profile, from RTSP-enabled IP camera), the decoder advertises 720×480 NV12 output media type. While consuming input data, the decoder outputs nothing and keeps video window black.

It does not issue any filter graph events, no. The filter is persistent and works hard in assumption that a new piece of knowledge will come from Mars and provide new capabilities to start decoding video, or filter’s input will give up and provide a new SPS to lower resolution down to something less complex.

Well then, so is this advanced technology yet to be discovered by science? It appears that no. CoreAVC Video Decoder, applied to the same H.264 video decodes it well and nice:

Update 2011-09-26: According to Microsoft classification, this is a bug on a released product. That is, if anyone is experiencing severe issues as a result of this behavior, one might need to contact Microsoft Support directly in search of a solution.

Actually, as resolutions over Full HD (1920×1080) are not so much common at the moment, the issue side effects are not yet likely. Still moving on with time, I think the issues are likely to come up, without being severe though.

A tricky EVR bug was caught up: input pin may falsely report disconnected state

Crime

An application which builds a DirectShow graph unexpectedly started failing with VFW_E_NOT_CONNECTED (0x80040209) error code.

Scene

The problem takes place during DirectShow graph building, yet in stopped state. Specific call which appeared to be giving out the error in first place appears to be EVR input pin’s IPin::ConnectionMediaType, and the problem is also specific to Enhanced video Renderer (Windows 7, but not necessarily only this version).

Investigation

The problem does not appear to be persistent. On the contrary, it is taking place for just a few milliseconds after pin connection. After the problem is gone, it does not seem to ever come up again unless the filter graph is built again from the beginning.

EVR pin connection is always reporting success, so the following error code stating VFW_E_NOT_CONNECTED “The operation cannot be performed because the pins are not connected.” goes against documented behavior, and is thus a bug.

Depending on time between pin connection and media type polling, the call can reach EVR:

  • before it starts showing the problem – stage A
  • at the time the call fails – stage B
  • after the failure time interval, when the call is successful from then on – stage C

Thus, the problem is limited to specific use cases:

  • the application should care about media type on EVR input
  • unexpected failure takes place when the call reaches in stage B
  • also found: the clipping window for the EVR has to belong to a non-primary monitor

If an application keep polling for media type in a loop, the result may be about the following:

UINT nStageA = 0, nStageB = 0, nStageC = 0;
// [...]
for(; ; )
{
    AM_MEDIA_TYPE MediaType;
    ZeroMemory(&MediaType, sizeof MediaType);
    const HRESULT nConnectionMediaTypeResult = pInputPin->ConnectionMediaType(&MediaType);
    if(SUCCEEDED(nConnectionMediaTypeResult))
    {
        if(nStageB)
        {
            nStageC++;
            break;
        } else
            nStageA++;
    } else
    {
        ATLASSERT(nConnectionMediaTypeResult == VFW_E_NOT_CONNECTED);
        nStageB++;
    }
    CoTaskMemFree(MediaType.pbFormat);
}
// [...]
CString sMessage;
sMessage.Format(_T("Bingo!\r\n\r\n") _T("nStageA %d, nStageB %d - 0x%08x, nStageC %d\n"), nStageA, nStageB, nResult, nStageC);
AtlMessageBox(m_hWnd, (LPCTSTR) sMessage, _T("Result"), MB_ICONERROR);

Workaround

An obvious straightforward workaround is to follow EVR connection with a wait for Stage B to pass, or timeout – whichever takes place first.

Also, vote for the bug on Microsoft Connect.

More Details

Video renderer filter are notorious for re-agreeing media type and being fretful as for memory allocators and media types (for a good reason though!). So it makes sense to suggest that the problem takes place when the filter is doing something related, such as it starts background activity immediately after connection in order to discover upstream peer capabilities.

In order to possibly get details on this, it is possible to raise an exception as soon as Stage B is detected and take a look at thread states using a debugger. Indeed, on of the background threads is engaged in EVR reconnection activity:

Yes it does the reconnection, but nevertheless it is expected to do the things undercover and transparently, it still allows a failure on the outer API.

     evr.dll!GetSourceRectFromMediaType() + 0x37 bytes    
     evr.dll!CEVRInputPin::CheckMediaType() + 0x81 bytes    
     evr.dll!CBasePin::ReceiveConnection() + 0x61 bytes    
     evr.dll!CEVRInputPin::ReceiveConnection() + 0x1fc2d bytes    
     quartz.dll!CBasePin::AttemptConnection() - 0x21 bytes    
     quartz.dll!CBasePin::TryMediaTypes() + 0x60 bytes    
     quartz.dll!CBasePin::AgreeMediaType() + 0x54 bytes    
     quartz.dll!CBasePin::Connect() + 0x46 bytes    
     quartz.dll!CFilterGraph::ConnectDirectInternal() + 0x83 bytes    
     quartz.dll!CFilterGraph::ConnectRecursively() + 0x2c bytes    
     quartz.dll!CFilterGraph::ConnectInternal() + 0xde bytes    
     quartz.dll!CFilterGraph::Connect() + 0x17 bytes    
     quartz.dll!CFGControl::WorkerDisplayChanged() + 0xf1 bytes    
     quartz.dll!CFGControl::CGraphWindow::OnReceiveMessage() + 0x2e2a bytes    
>    quartz.dll!WndProc() + 0x3e bytes    
     user32.dll!_InternalCallWinProc@20() + 0x23 bytes    
     user32.dll!_UserCallWinProcCheckWow@32() + 0xb7 bytes    
     user32.dll!_DispatchMessageWorker@8() + 0xed bytes    
     user32.dll!_DispatchMessageW@4() + 0xf bytes    
     quartz.dll!ObjectThread() + 0x65 bytes

A test Visual C++ .NET 2010 application is available from SVN. The code requires a media file, and refers to 352×288 I420.avi, which is included into ZIP file attached to MS Connect Feedback.

If your application is looking for network adapters, it may be blind to see

A really long and annoying troubleshooting of a problem finally ended with a bug found in GetAdaptersInfo/GetAdaptersAddresses API.

It may unexpectedly fail under the following conditions:

  • 32-bit application
  • 64-bit operating system or /3GB feature enabled on 32-bit operating system
  • hosting process is linked with /LARGEADDRESSAWARE flag or has otherwise set it in binary header
  • over 2GB of RAM (in terms of Private Bytes/Virtual Size performance counters) consumed by the hosting process

The application might unexpectedly start getting ERROR_NO_DATA (232) or ERROR_NOACCESS (998) error codes instead of list of adapters. Supposedly, the responsible Microsoft component iplhlpapi.dll or one of the underlying components/APIs are incompatible with such environments (such as for example, treating 32-bit pointer with the most significant bit set as invalid or compares such pointers as signed values).

A test Visual C++ .NET 2010 application is available from SVN. More bug details on Microsoft Connect.

NOTE 1: You need to run “GetAdaptersAddressesTest /regserver” once to register the app before running it for the test.

NOTE 2: The problem does not seem to take place with 32-bit operating systems with /3GB tuning enabled.

Build Incrementer Add-In for Visual Studio: Latest Visual Studio Versions

If you share concept (as I do) that every build should have a unique file version stamp in it, for a simple purpose – at least – to distinguish between different version of the same binary, then a helpful tool of automatic incrementing fourth number in FILEVERSION’s file version is something you cannot live without. After going through several fixes and updates, it is finally here available for download.

The last issue was in particular that projects that are in solution’s folder are not found by the add-in with Visual Studio 2008. Why? OnBuildProjConfigBegin event provides you with a unique project name string in Project argument, but it appears that it is only good enough as a quick lookup argument with Visual Studio 2010.

// EnvDTE::_dispBuildEvents
    STDMETHOD(OnBuildBegin)(_EnvDTE_vsBuildScope Scope, _EnvDTE_vsBuildAction Action) throw()
    STDMETHOD(OnBuildDone)(_EnvDTE_vsBuildScope Scope, _EnvDTE_vsBuildAction Action) throw()
    STDMETHOD(OnBuildProjConfigBegin)(BSTR Project, BSTR ProjectConfig, BSTR Platform, BSTR SolutionConfig) throw()
    {
        _Z4(atlTraceCOM, 4, _T("Project \"%s\", ProjectConfig \"%s\", Platform \"%s\", SolutionConfig \"%s\"\n"), CString(Project), CString(ProjectConfig), CString(Platform), CString(SolutionConfig));
        _ATLTRY
        {
            // NOTE: const CString& cast forces compiler to process statement as variable definition rather than function forward declaration
            CProjectConfiguration ProjectConfiguration((const CString&) CString(Project), CString(ProjectConfig), CString(Platform), CString(SolutionConfig));
            CRoCriticalSectionLock DataLock(m_DataCriticalSection);
            // NOTE: Check the project on the first run only (to skip multiple increments in batch build mode)
            if(!Project || m_VersionMap.Lookup(ProjectConfiguration))
                return S_FALSE;
            _Z3(atlTraceGeneral, 3, _T("Checking project \"%s\"...\n"), CString(Project));
            // Checking the project is of C++ kind
            CComPtr<EnvDTE::Project> pProject = GetProject(CComVariant(Project));
            _A(pProject);
            ...

When the project is in a folder, Projects::Item can just fail if you are looking up for the element interface. In which case, you have to walk the collection taking into account SubProjects and additionally look there yourself. Visual Studio 2010 is one step smarter and gives the thing to you right from the start.

Eventually, the add-in is here. It’s job is to go to .RC file and increment file version each time you build the binary. It reports the action into build output window:

To install the add-in:

Or, alternatively, use installation file VisualStudioBuildIncrementerAddInSetup.msi (version 1.0.4, 379K) to have it done for you in a user-friendly way. Partial source code, a Visual Studio 2010 projectis also there in repository.

ATL ActiveX control hosting memory leak, _freea assertion failure and more

If you were adding and removing ActiveX controls programmatically with ATL, you might have noticed a nasty memory leak after final release of the control. Control added, removed and a leaky block is left om heap.

Tracking simple memory leaks is not a rocket science with Microsoft C++ runtime, by adding _CRTDBG_MAP_ALLOC define, as described in Memory Leak Detection Enabling on MSDN:

#if defined(_DEBUG)
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#endif // defined(_DEBUG)

Having that done, the problem was isolated to an extra instance of CAxHostWindow, which was excessively instantiated and never freed. Luckily, having this done you already have sufficient amount of keywords on hands to locate relevant articles on Microsoft knowledge base:

Basically, what is taking place is the behavior by design. That is, AtlAxCreateControlEx API is leaky by design, one gotta just be aware.

CComPtr<IUnknown> pContainerUnkonwn, pControlUnknown;
static LPCTSTR g_pszControlClass = _T("{8856F961-340A-11D0-A96B-00C04FD705A2}");
// NOTE: This results in two instances of CAxWindowHost being created, one of which appears to be leaked (see below), 
//       see http://support.microsoft.com/kb/218442
// 
// Detected memory leaks!
// Dumping objects ->
// {81} normal block at 0x00128928, 244 bytes long.
// Data: <x .       .     > 78 09 2E 01 01 00 00 00 0C 10 2E 01 00 00 00 00 
// Object dump complete.
ATLENSURE_SUCCEEDED(AtlAxCreateControlEx(CT2COLE(g_pszControlClass), m_hWnd, NULL, &pContainerUnkonwn, &pControlUnknown));

Continue reading →

Microsoft.Jet.OLEDB.4.0 ProgID is not available (Windows 7 but probably not only)

It was a sort of ridiculous problem: an attempt to instantiate a Microsoft.Jet.OLEDB.4.0 object failed with error. Still some applications are still running without problems connecting to Jet databases, how comes?

There has been a number of posts on Internet, but none of the top ones appeared to be relevant.

The problem is reproduced extremely simple:

int _tmain(int argc, _TCHAR* argv[])
{
    ATLVERIFY(SUCCEEDED(CoInitialize(NULL)));
    {
        //CoLoadLibrary(L"C:\\Program Files (x86)\\Common Files\\System\\Ole DB\\oledb32.dll", TRUE);
        CComPtr<IDBInitialize> pDbInitialize;
        const HRESULT nResult = pDbInitialize.CoCreateInstance(L"Microsoft.Jet.OLEDB.4.0", NULL, CLSCTX_INPROC_SERVER);
        _tprintf(_T("nResult 0x%08x\n"), nResult);
    }
    CoUninitialize();
    return 0;
}

Oops, the code gives error REGDB_E_CLASSNOTREG 0x80040154 “Class not registered”. It looked like system was unable to locate one of the internally used DLLs – oledb32.dll, and if we help by uncommenting the line commented in the code snippet above, the error changes to ERROR_MOD_NOT_FOUND 0x8007007e “The specified module could not be found”.

The problem appears to be that one of the system components, which is involved, “Microsoft OLE DB Data Conversion Library” is registered with the system using a REG_EXPAND_SZ value, to be located using path “%CommonProgramFiles(x86)%\System\Ole DB\oledb32.dll”. It is obvious that CommonProgramFiles(x86) is placeholder to be expanded, but does the expansion succeed?

Continue reading →

Microsoft MVP Award in DirectShow/Media Foundation

As an appreciation of outstanding contributions in DShow/Media Foundation technical communities during the past year I was presented with the 2009 Microsoft MVP Award, which I am very much pleased to receive.

Microsoft Most Valuable Professional Award
// Microsoft Most Valuable Professional Award

In an intention to accompany the post with useful DirectShow related information, here is the link list of most important DirectShow resources on the Internet:

Continue reading →