【问题标题】:How to get width and height of directshow webcam video stream如何获取directshow网络摄像头视频流的宽度和高度
【发布时间】:2010-06-10 02:31:48
【问题描述】:

我找到了一些代码,可以让我从我的网络摄像头访问原始像素数据。但是,我需要知道图像的宽度、高度、像素格式,最好是数据步幅(间距、内存填充或任何你想称呼它的东西),如果它不是宽度 * 每像素字节数

#include <windows.h>
#include <dshow.h>

#pragma comment(lib,"Strmiids.lib")

#define DsHook(a,b,c) if (!c##_) { INT_PTR* p=b+*(INT_PTR**)a;   VirtualProtect(&c##_,4,PAGE_EXECUTE_READWRITE,&no);\
                                          *(INT_PTR*)&c##_=*p;   VirtualProtect(p,    4,PAGE_EXECUTE_READWRITE,&no);   *p=(INT_PTR)c; }


// Here you get image video data in buf / len. Process it before calling Receive_ because renderer dealocates it.
HRESULT ( __stdcall * Receive_ ) ( void* inst, IMediaSample *smp ) ; 
HRESULT   __stdcall   Receive    ( void* inst, IMediaSample *smp ) {     
    BYTE*     buf;    smp->GetPointer(&buf); DWORD len = smp->GetActualDataLength();
    //AM_MEDIA_TYPE* info;
    //smp->GetMediaType(&info);
    HRESULT   ret  =  Receive_   ( inst, smp );   
    return    ret; 
}

int WINAPI WinMain(HINSTANCE inst,HINSTANCE prev,LPSTR cmd,int show){
    HRESULT hr = CoInitialize(0); MSG msg={0}; DWORD no;

    IGraphBuilder*  graph= 0;  hr = CoCreateInstance( CLSID_FilterGraph, 0, CLSCTX_INPROC,IID_IGraphBuilder, (void **)&graph );
    IMediaControl*  ctrl = 0;  hr = graph->QueryInterface( IID_IMediaControl, (void **)&ctrl );

    ICreateDevEnum* devs = 0;  hr = CoCreateInstance (CLSID_SystemDeviceEnum, 0, CLSCTX_INPROC, IID_ICreateDevEnum, (void **) &devs);
    IEnumMoniker*   cams = 0;  hr = devs?devs->CreateClassEnumerator (CLSID_VideoInputDeviceCategory, &cams, 0):0;  
    IMoniker*       mon  = 0;  hr = cams->Next (1,&mon,0);  // get first found capture device (webcam?)    
    IBaseFilter*    cam  = 0;  hr = mon->BindToObject(0,0,IID_IBaseFilter, (void**)&cam);
                               hr = graph->AddFilter(cam, L"Capture Source"); // add web cam to graph as source
    IEnumPins*      pins = 0;  hr = cam?cam->EnumPins(&pins):0;   // we need output pin to autogenerate rest of the graph
    IPin*           pin  = 0;  hr = pins?pins->Next(1,&pin, 0):0; // via graph->Render
                               hr = graph->Render(pin); // graph builder now builds whole filter chain including MJPG decompression on some webcams
    IEnumFilters*   fil  = 0;  hr = graph->EnumFilters(&fil); // from all newly added filters
    IBaseFilter*    rnd  = 0;  hr = fil->Next(1,&rnd,0); // we find last one (renderer)
                               hr = rnd->EnumPins(&pins);  // because data we are intersted in are pumped to renderers input pin 
                               hr = pins->Next(1,&pin, 0); // via Receive member of IMemInputPin interface
    IMemInputPin*   mem  = 0;  hr = pin->QueryInterface(IID_IMemInputPin,(void**)&mem);

    DsHook(mem,6,Receive); // so we redirect it to our own proc to grab image data

    hr = ctrl->Run();   

    while ( GetMessage(   &msg, 0, 0, 0 ) ) {  
        TranslateMessage( &msg );   
        DispatchMessage(  &msg ); 
    }
};

如果你能告诉我如何让这个东西不渲染窗口但仍然让我访问图像数据,那么奖励积分。

【问题讨论】:

    标签: c++ directshow webcam


    【解决方案1】:

    真的很丑。请不要那样做。而是插入一个像样本采集器这样的直通过滤器(正如我在同一主题上回复您的其他帖子一样)。将采样采集器连接到 null 渲染器可以让您以一种干净、安全的方式获取数据,而无需渲染图像。

    要获得跨度,您需要通过 ISampleGrabber 或 IPin::ConnectionMediaType 获取媒体类型。格式块将是 VIDEOINFOHEADER 或 VIDEOINFOHEADER2(检查格式 GUID)。 bitmapinfo 标头 biWidth 和 biHeight 定义了位图尺寸(因此也定义了步幅)。如果 RECT 不为空,则定义位图中的相关图像区域。

    触摸这篇文章后,我现在要洗手了。

    【讨论】:

    • 哈哈哈。我认为代码太短而不能很好。我会看看你的其他帖子。
    【解决方案2】:

    我为你感到难过。创建接口时,可能没有最好的程序员。

    // Here you get image video data in buf / len. Process it before calling Receive_ because renderer dealocates it.
    
    BITMAPINFOHEADER bmpInfo; // current bitmap header info
    int stride;
    
    HRESULT ( __stdcall * Receive_ ) ( void* inst, IMediaSample *smp ) ; 
    HRESULT   __stdcall   Receive    ( void* inst, IMediaSample *smp )
    {     
        BYTE*     buf;    smp->GetPointer(&buf); DWORD len = smp->GetActualDataLength();
        HRESULT   ret  =  Receive_   ( inst, smp );   
    
        AM_MEDIA_TYPE* info;
        HRESULT hr = smp->GetMediaType(&info);
        if ( hr != S_OK )
        { //TODO: error } 
        else
        {
            if ( type->formattype == FORMAT_VideoInfo )
            {
                const VIDEOINFOHEADER * vi = reinterpret_cast<VIDEOINFOHEADER*>( type->pbFormat );
                const BITMAPINFOHEADER & bmiHeader = vi->bmiHeader;
                //! now the bmiHeader.biWidth contains the data stride
                stride = bmiHeader.biWidth;
    
                bmpInfo = bmiHeader;
                int width = ( vi->rcTarget.right - vi->rcTarget.left );
                //! replace the data stride be the actual width
                if ( width != 0 )
                    bmpInfo.biWidth = width;
    
            }
            else
            { // unsupported format }
        }
        DeleteMediaType( info );
    
        return    ret; 
    }
    

    【讨论】:

      【解决方案3】:

      下面介绍如何添加抑制渲染窗口的 Null Renderer。创建 IGraphBuilder* 后直接添加

      //create null renderer and add null renderer to graph
      IBaseFilter *m_pNULLRenderer;  hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void **)&m_pNULLRenderer);
                                     hr = graph->AddFilter(m_pNULLRenderer, L"Null Renderer");
      

      dshook hack 是我所知道的唯一优雅的 directshow 代码。

      根据我的经验,DirectShow API 是一场令人费解的噩梦,即使是最简单的操作也需要数百行代码,并且需要调整整个编程范例才能访问您的网络摄像头。因此,如果此代码为您完成工作,就像它为我所做的那样,请使用它并享受更少的代码行来维护。

      【讨论】:

      • 它与问题有什么关系?
      • 他问怎么去掉渲染窗口,就是这样。
      猜你喜欢
      • 2013-07-15
      • 1970-01-01
      • 2016-10-23
      • 2011-02-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-14
      • 2023-03-04
      相关资源
      最近更新 更多