• 用VS2005建立DirectShow Filter_菜菜_新浪博客


    http://blog.sina.com.cn/s/blog_5f8ac4b70100n96m.html

    用VS2005建立DirectShow Filter

    (2010-11-10 09:03:49)
    标签:

    杂谈

    分类: DirectShow

    If you’re in need of writing a DirectShow filter and you’ve never done it before you might be interested in reading the following article. It explains how to configure all the necessary stuff in Visual C++ to make a very simple DirectShow filter.

     

    Prerequisites

    Let’s sum up what must be done before you can start dealing with DirectShow filters. First you really need to have a working Visual Studio with all updates and service packs installed. I believe that most people use at least VS2003 or newer version so I won’t be dealing with VS6.0 anymore. All examples and pictures are taken from a VS 2005. You should also be familiar with how C++ works and with the concept of libraries and linking. If you’re not, you don’t need to read this article anymore.

    I strongly recommend to have the latest Platform SDK (also known as Windows SDK). You should also use specific SDK for your OS = not to use Vista SDK with Windows XP etc. You can visit www.microsoft.com to download e.g. Windows SDK for Vista.

    Since DirectShow has been separated from the DirectX SDK it is not really necessary to have DX SDK installed but if you’re also concerned with 3D graphics and stuff you might also consider installing it.

    Microsoft has developed a set of base classes to support the development of DS filters that are now a part of Window SDK and are located within the SDK in the {SDK}\Windows\v6.0\Samples\Multimedia\DirectShow\BaseClasses directory. Usually the library is configured without MFC support. If you’re familiar with MFC and would like to take advantage of the MFC library you might considering making your own builds of the baseclasses. To get more info on this issue try reading this article. If you are developing with VS2005 you should be aware that it requires a new runtime package that is not currently present on all systems and if you are linking MFC as shared library your applications won’t work unless the user installs the SP1 Redistributable Package. To avoid this you should simply use MFC as static libraries. I would also recommend building all 4 types of baseclasses library :

    • MFC Shared, Debug as strmbasdu.lib
    • MFC Shared, Release as strmbaseu.lib
    • MFC Static, Debug as strmbasdus.lib
    • MFC Static, Release as strmbaseus.lib

    Suffix *u means unicode builds, *s means MFC static, *d means debug. The baseclasses library from the Windows SDK contains an entry point named "DllEntryPoint". I’ve created a function named "FilterDllMain" that also initializes MFC properly. If you see these names appear don’t get confused - they both actually mean the same.

    Then you need to add the include and lib path to Visual Studio so you won’t have to specify full path to headers and .lib files for each filter. You can do this by opening Tools -> Options. And then add the folder where baseclasses sources are kept just as on the following image. I’m using my own build of baseclasses that is named "libMonoDShow".

    用VS2005建立DirectShow <wbr>Filter

    And in the same manner add also path to the "Library files".

    New Project

    Start a new project by clicking File -> New -> Project… and select Win32 / Win32 Project. Select some folder to put the filter in and also enter some name. In this tutorial we’ll be writing a simple audio processing filter so I’ve named the project "audio_volume".

    用VS2005建立DirectShow <wbr>Filter

    In the following page of the wizard select DLL as application type and make it an empty project.

    用VS2005建立DirectShow <wbr>Filter

    Writing The Filter

    Now we should have an empty project created so it’s time to add some stuff. It is always a good thing to have a header file with all the filter-specific stuff like CLSID, interface IIDs and interface definitions or configuration structures. So let’s add one such file.

    用VS2005建立DirectShow <wbr>Filter

    用VS2005建立DirectShow <wbr>Filter

    Use the Tools -> Create GUID ("C:\Program Files\Microsoft Visual Studio\Common\Tools\GUIDGEN.EXE")tool to generate a class ID for the new filter. I prefer using the third type - the one with "static const struct GUID ….". Use some proper name such as "CLSID_AudioVolume" for the newly generated GUID. Once this is done the file should look like this :

    // {212A25F7-7000-4f74-9E4C-6FFB3F595EE4}
    static const GUID CLSID_AudioVolume =
    { 0×212a25f7, 0×7000, 0×4f74, { 0×9e, 0×4c, 0×6f, 0xfb, 0×3f, 0×59, 0×5e, 0xe4 } };

    Now it’s time to create the header file for the filter itself so add a new .H file and save it as "audio_filter.h". Insert the following code into the "audio_filter.h" file :

    #pragma once

    class AudioVolume : public CTransformFilter
    {
    private:
        CMediaType        mtIn;
    public:
        // constructor & destructor
        AudioVolume(LPUNKNOWN pUnk, HRESULT *phr);
        virtual ~AudioVolume();
        static CUnknown *WINAPI CreateInstance(LPUNKNOWN pUnk, HRESULT *phr);

       // CTransformFilter overriden
        virtual HRESULT CheckInputType(const CMediaType *mtIn);
        virtual HRESULT CheckTransform(const CMediaType *mtIn, const CMediaType *mtOut);
        virtual HRESULT DecideBufferSize(IMemAllocator *pAlloc,
                                                          ALLOCATOR_PROPERTIES *pProp); 
        virtual HRESULT GetMediaType(int iPosition, CMediaType *pmt);
        virtual HRESULT SetMediaType(PIN_DIRECTION direction, const CMediaType *pmt);
        virtual HRESULT Transform(IMediaSample *pIn, IMediaSample *pOut);
    };

    This should be quite enough for the tutorial. All methods will be explained later. Now add a CPP file where the filter implementation should be. Save it as "audio_filter.cpp" and write the following code :

    AudioVolume::AudioVolume(LPUNKNOWN pUnk, HRESULT *phr) :
        CTransformFilter(_T("Audio Volume"), pUnk, CLSID_AudioVolume)
    {
        if (phr) *phr = NOERROR;
    }

    AudioVolume::~AudioVolume()
    {
    }

    CUnknown *WINAPI AudioVolume::CreateInstance(LPUNKNOWN pUnk, HRESULT *phr)
    {
        AudioVolume *vol = new AudioVolume(pUnk, phr);
        if (!vol) {
            if (phr) *phr = E_OUTOFMEMORY;
        }
        return vol;
    }

    These were just plain constructor, destructor and a static method to provide instances of our audio filter. Now we need to deal with CTransformFilter virtual methods.

    When the upstream filter connects to our audio processing filter it must first negotiate the proper media type. For the purpose of this tutorial we only accept 16-bit raw PCM audio data so the AudioVolume::CheckMediaType might look like this :

    HRESULT AudioVolume::CheckInputType(const CMediaType *mtIn)
    {
        // we only want raw audio
        if (mtIn->majortype != MEDIATYPE_Audio) return E_FAIL;
        if (mtIn->subtype != MEDIASUBTYPE_PCM) return E_FAIL;
        if (mtIn->formattype != FORMAT_WaveFormatEx) return E_FAIL;

       // and we only want 16-bit
        WAVEFORMATEX    *wfx = (WAVEFORMATEX*)mtIn->pbFormat;
        if (wfx->wBitsPerSample != 16) return E_FAIL;

        return NOERROR;
    }

    Our filter does not encode/decode or scramble the data so the output should also be PCM audio.

    HRESULT AudioVolume::CheckTransform(const CMediaType *mtIn, const CMediaType *mtOut)
    {
        HRESULT hr = CheckInputType(mtIn);
        if (FAILED(hr)) return hr;

       // must also be PCM audio
        if (mtOut->majortype != MEDIATYPE_Audio) return E_FAIL;
        if (mtOut->subtype != MEDIASUBTYPE_PCM) return E_FAIL;

        return NOERROR;
    }

    Our filter should offer exactly the same media type for the output so we keep a copy of the input type.

    HRESULT AudioVolume::SetMediaType(PIN_DIRECTION direction, const CMediaType *pmt)
    {
        // ask the baseclass if it’s okay to go
        HRESULT    hr = CTransformFilter::SetMediaType(direction, pmt);
        if (FAILED(hr)) return hr;

        // keep a local copy of the type
        if (direction == PINDIR_INPUT) {
            mtIn = *pmt;
        }

        return NOERROR;
    }

    HRESULT AudioVolume::GetMediaType(int iPosition, CMediaType *pmt)
    {
        if (iPosition < 0) return E_INVALIDARG;
        if (iPosition > 0) return VFW_S_NO_MORE_ITEMS;

        // the input pin must be connected first
        if (m_pInput->IsConnected() == FALSE) return VFW_S_NO_MORE_ITEMS;

       // we offer only one type - the same as input
        *pmt = mtIn;
        return NOERROR;
    }

    We need to make clear how large buffers should be used when delivering data downstream. This might need more sophisticated solution but for this tutorial this can be solved easily :

    HRESULT AudioVolume::DecideBufferSize(IMemAllocator *pAlloc,
            ALLOCATOR_PROPERTIES *pProp)
    {
        WAVEFORMATEX    *wfx = (WAVEFORMATEX*)mtIn.pbFormat;

       // this might be put too simly but
        // we should be able to deliver max 1 second
        // of raw audio
        pProp->cbBuffer        = wfx->nAvgBytesPerSec;

        // when working with audio always try to have
        // some spare buffer free
        pProp->cBuffers        = 3;

        ALLOCATOR_PROPERTIES    act;
        HRESULT hr = pAlloc->SetProperties(pProp, &act);
        if (FAILED(hr)) return hr;

        if (act.cbBuffer < pProp->cbBuffer) return E_FAIL;
        return NOERROR;
    }

    And the final phase - audio processing itself. We only multiply each PCM sample by the factor of 0.5. That means we’re amplify the signal by -6.0205 dB.

    HRESULT AudioVolume::Transform(IMediaSample *pIn, IMediaSample *pOut)
    {
        BYTE    *bufin, *bufout;
        long    sizein;

        // get the input and output buffers
        pIn->GetPointer(&bufin);
        pOut->GetPointer(&bufout);

        // and get the data size
        sizein = pIn->GetActualDataLength();

       // since we’re dealing with 16-bit PCM
        // it might be convenient to use "short"
        short    *src = (short*)bufin;
        short    *dst = (short*)bufout;
        int        samples = sizein / sizeof(short);

       // now deal with the samples and lower the volume
        for (int i=0; i<samples; i++) {
            dst[i] = src[i] * 0.5;
        }

       // and set the data size
        pOut->SetActualDataLength(samples * sizeof(short));
        return NOERROR;
    }

    That’s for the functionality. Now we need to describe the filter so the GraphBuilder can locate and insert the filter if necessary.

    Registry Information

    It is a good habit to create a special file for the purpose of registry information and DLL entry points. I use to follow the "*_reg.cpp" pattern so in this tutorial I’d create an "audio_reg.cpp" file and insert the following structures. I strongly recommend to use standard merit values and to use MERIT_UNLIKELY or MERIT_NORMAL unless really necessary. You can really screw many things by entering stupid values here.

    // Media Types
    const AMOVIESETUP_MEDIATYPE sudPinTypes[] =  
    {
        { &MEDIATYPE_Audio, &MEDIASUBTYPE_PCM }
    };

    // Pins
    const AMOVIESETUP_PIN psudPins[] =
    {
        { L"Input", FALSE, FALSE, FALSE, FALSE, &CLSID_NULL, NULL, 1, &sudPinTypes[0] },
        { L"Output", FALSE, TRUE, FALSE, FALSE, &CLSID_NULL, NULL, 1, &sudPinTypes[0] }
    };  

    // Filters
    const AMOVIESETUP_FILTER sudAudioVolume =
    {
        &CLSID_AudioVolume, L"Audio Volume", MERIT_UNLIKELY, 2, psudPins
    };                    

    // Templates
    CFactoryTemplate g_Templates[]=
    {
        { L"Audio Volume", &CLSID_AudioVolume, AudioVolume::CreateInstance, NULL, &sudAudioVolume }
    };

    int g_cTemplates = sizeof(g_Templates)/sizeof(g_Templates[0]);

    Since DirectShow filters are COM objects they should expose the functions to register and unregister the library. If you would ever need to perform some special tasks (e.g. registering file extensions) this is the place.

    STDAPI DllRegisterServer()
    {
        return AMovieDllRegisterServer2(TRUE);
    }

    STDAPI DllUnregisterServer()
    {
        return AMovieDllRegisterServer2(FALSE);
    }

     

    Headers

    Now create a file named "headers.h" and put the following inside :

    #include <afxwin.h>
    #include <afxtempl.h>
    #include <atlbase.h>
    #include <streams.h>
    #include <initguid.h>
    #include <mmsystem.h>
    #include <mmreg.h>
    #include "audio_types.h"
    #include "audio_filter.h"

    and include the header.h file in all *.cpp files.

    External Symbols

    Now we’re done with programming but we still need to specify a few things. First - each DLL library (whether named .dll or .ax) should expose some functions. COM libraries should expose at least 4 functions :

    • DllRegisterServer
    • DllUnregisterServer
    • DllGetClassObject
    • DllCanUnloadNow

    the first 2 have been declared in the previous paragraph and the last 2 are implemented by the baseclasses library. But we need to make it clear to the linker that we really want to expose them so we should add a new item to the project - "Module-Definition File (.def)" and call it "audio_volume.def". Edit it’s content so it would look like this :

    LIBRARY         audiovolume.ax
    EXPORTS
        DllGetClassObject        PRIVATE
        DllCanUnloadNow         PRIVATE
        DllRegisterServer         PRIVATE
        DllUnregisterServer      PRIVATE

     

    Project Options

    And the last few touches are done in the project options dialog.

    In the Configuration Properties -> General page select the proper MFC usage type.

    用VS2005建立DirectShow <wbr>Filter

    In the Linker -> General page enter the desired output file name.

    用VS2005建立DirectShow <wbr>Filter

    In the Linker -> Input page add the baseclasses libraries and also "strmiids.lib" and "winmm.lib" and make sure the module definition is valid.

    用VS2005建立DirectShow <wbr>Filter

    And finally in the Linker -> Advanced page enter the proper entry point. If you are using MFC-enabled baseclasses as I was, you might be using the same function as I am - "FilterDllMain". If you’re using the original baseclasses library you might enter DllEntryPoint@12 instead.

    用VS2005建立DirectShow <wbr>Filter

    Now you should be able to build the filter :)

    Testing

    You can test the filter very easily using either GraphStudio or GraphEdit tools. I’ve constructed a simple graph like the following. Try it run with and without the "Audio Volume" filter and you should hear the effect.

    用VS2005建立DirectShow <wbr>Filter

    Conclusion

    Congratulations for reading this far :) Even if all this might look scary it’s a simple routine that can be "clicked" within 2 minutes once you know what to do. I hope you’ll find this information useful.

  • 相关阅读:
    Valgrind使用转载 Sanny.Liu
    Caffe模型读取 Sanny.Liu
    JNI动态库生成、编译、查看相关简易资料 Sanny.Liu
    GDB调试,转载一位大牛的东西 Sanny.Liu
    Android处理图片工具(转载) Sanny.Liu
    添加可点击的imagebottom,有个点击动画效果 Sanny.Liu
    去OpenCVManager,大部分为转载,仅当自己学习使用 Sanny.Liu
    转载: vim使用技巧 Sanny.Liu
    结构体数组初始化三种方法,转载 Sanny.Liu
    AsyncTask机制学习 Sanny.Liu
  • 原文地址:https://www.cnblogs.com/carl2380/p/1944332.html
Copyright © 2020-2023  润新知