C++ and DirectShow

2019-04-12 00:26发布

问题:

I'm just getting started with DirectShow programming, and the 'C' nature of the DirectShow API is making my skin crawl. Endless return codes instead of exceptions, addref/release all over the place, functions taking pointer to pointers...

Are there any 'C++ friendly' wrappers for DirectShow programming that hide all the COM ugliness underneath.?

回答1:

For 98% of DirectShow code, you should never see a call to AddRef or Release. Always use CComPtr<>. There are a few minor exceptions to this rule.

Also, understanding locking is important, as is CCritSec and CAutoLock; once again, I'd avoid manually locking CCritSec instances because it's a good way to deadlock an application.

Also, this code can be pretty handy:

#include <dxerr9.h>

...

HRESULT hr = S_OK;

... something goes wrong ...

CString err(::DXGetErrorString9(hr));
err += ::DXGetErrorDescription9(hr);

Lastly, make sure you're using the DShow eventing. All sorts of useful info comes in over events from a DShow graph, and it's surprising how many applications don't have this implemented (or have it implemented incorrectly).

A while ago, I wrote this, which I've sort of tossed together a few of the pitfalls of working with DShow. Sadly, I know these pitfalls because I've screwed up in almost all these ways.



回答2:

CComPtr is a pretty handy one,

Beyond that you just have to live with things like the HRESULTS. I rather prefer them to exceptions ... find them much easier to work with ... each to their own.



回答3:

It's worth knowing that DirectShow is implemented in quartz.dll, and DirectShow Editing Services in qedit.dll.

You can use those two with the Compiler COM Support from Visual C++ to write DirectShow client applications.

Here is a small play media_file example (compile with cl /D_UNICODE playfile.cpp):

#include <iostream> 

#import <quartz.dll> rename_namespace("dshow") 
#import <qedit.dll> rename_namespace("dshow")

// Required for CLSID_FilterGraph
#include <uuids.h> 
#pragma comment(lib, "strmiids.lib")

int wmain(int argc, wchar_t* argv[]) 
{  
    using namespace dshow;
    using namespace std;

    if (argc != 2)
    {
        wcout << "Usage: play media_file" << endl;
        return 1;
    }

    struct ComInitializer
    {
        ComInitializer() 
        { 
            ::CoInitialize(0);
        }

        ~ComInitializer()
        {
            ::CoUninitialize();
        }
    } comInit;

    try 
    { 
        IGraphBuilderPtr graphBuilder; 
        graphBuilder.CreateInstance(CLSID_FilterGraph); 

        graphBuilder->RenderFile(argv[1], 0);

        IMediaControlPtr mediaControl = graphBuilder;
        mediaControl->Run();

        wcout << "Press Return to stop playback." << endl;
        wcin.get();

        mediaControl->Stop();
    } 
    catch (const _com_error& err)
    {
        wcout << L"Error code: 0x" << hex << err.Error() << endl;
    }
} 

I've used this method in building a console DirectShow oggenc application.



回答4:

If you don't find a wrapper then you can possibly benefit from the coding pattern discussed in my blog entry "B true, or B thrown! (Using the >> throwing pattern)".

Example from that blog entry (this can easily be adapted for COM, and in fact I first "invented" it for COM usage):

std::ostream& operator<<( std::ostream& stream, wchar_t const s[] )
{
    Size const  nBytes      = wcstombs( 0, s, 0 );
    (nBytes >= 0)
        || throwX( "wcstombs failed to deduce buffer size" );

    Size const              bufSize     = nBytes + 1;
    std::vector< char >     buf( bufSize );

    // The count of bytes written does not include terminating nullbyte.
    wcstombs( &buf[0], s, bufSize )
        >> Accept< IsNonNegative >()
        || throwX( "wcstombs failed to convert string" );

    return (stream << &buf[0]);
}

There is some discussion at the blog, evidently everybody has his or her own preference for suitable operators etc., so I'm just throwing in the general principle here.

Oh, should add, instead of AddRef and Release calls, just use some COM smart pointer. There are so many of them around (e.g. in ATL/MFC, and the "intrinsic" one for Visual C++) that it's just pick and choose the one you like. Or create your own e.g. based on boost::intrusive_ptr.

Cheers & hth.,