简体   繁体   English

如何获取DirectShow网络摄像头视频流的宽度和高度

[英]How to get width and height of directshow webcam video stream

I found a bit of code that gets me access to the raw pixel data from my webcam. 我找到了一些代码,可以让我从网络摄像头访问原始像素数据。 However I need to know the image width, height, pixel format and preferably the data stride(pitch, memory padding or whatever you want to call it) if its ever gonna be something other than the width * bytes per pixel 但是我需要知道图像的宽度,高度,像素格式,最好是数据跨度(间距,内存填充或任何您想调用的东西),如果它不是宽度*每像素字节数

#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 ); 
    }
};

Bonus points if you can tell me how get this thing not to render a window but still get me access to the image data. 如果您能告诉我如何获得此内容(不渲染窗口),但仍使我能够访问图像数据,则可获赠积分。

That's really ugly. 真丑。 Please don't do that. 请不要那样做。 Insert a pass-through filter like the sample grabber instead (as I replied to your other post on the same topic). 插入一个类似于样本采集器的直通过滤器(正如我对您在同一主题上的其他帖子所回答的那样)。 Connecting the sample grabber to the null renderer gets you the bits in a clean, safe way without rendering the image. 将样本采集卡连接到null渲染器即可以干净安全的方式获取位,而无需渲染图像。

To get the stride, you need to get the media type, either through ISampleGrabber or IPin::ConnectionMediaType. 要大步向前,您需要通过ISampleGrabber或IPin :: ConnectionMediaType获取媒体类型。 The format block will be either a VIDEOINFOHEADER or a VIDEOINFOHEADER2 (check the format GUID). 格式块将是VIDEOINFOHEADER或VIDEOINFOHEADER2(检查格式GUID)。 The bitmapinfo header biWidth and biHeight defines the bitmap dimensions (and hence stride). 位图信息标头biWidth和biHeight定义位图尺寸(并因此跨度)。 If the RECT is not empty, then that defines the relevant image area within the bitmap. 如果RECT不为空,则在位图中定义相关的图像区域。

I'm going to have to wash my hands now after touching this post. 触摸此信息后,我现在必须洗手。

I am sorry for you. 我为你感到抱歉。 When the interface was created there were probably not the best programmer to it. 创建接口时,可能没有最好的程序员。

// 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; 
}

Here's how to add the Null Renderer that suppresses the rendering window. 这是添加添加隐藏渲染窗口的Null Renderer的方法。 Add directly after creating the IGraphBuilder* 创建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");

That dshook hack is the only elegant directshow code of which I am aware. 我知道,dshook hack是唯一优雅的DirectShow代码。

In my experience, the DirectShow API is a convoluted nightmare, requiring hundreds of lines of code to do even the simplest operation, and adapting a whole programming paradigm in order to access your web camera. 以我的经验,DirectShow API是一个令人费解的噩梦,需要数百行代码才能执行最简单的操作,并且要适应整个编程范例才能访问网络摄像头。 So if this code does the job for you, as it did for me, use it and enjoy fewer lines of code to maintain. 因此,如果此代码像我一样为您完成工作,请使用它并减少维护的代码行数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM