We help IT Professionals succeed at work.

We've partnered with Certified Experts, Carl Webster and Richard Faulkner, to bring you two Citrix podcasts. Learn about 2020 trends and get answers to your biggest Citrix questions!Listen Now

x

How do I copy the video stream from a buffer of webcam in C++ under Windows?

Medium Priority
966 Views
Last Modified: 2013-12-14
I work on a C++ project similar to SplitCam - http://www.splitcamera.com/ and I couldn't find out any way to create DirectShow Source Filter without input pins, that distributes video stream from a webcam to its output pin. I can neither find any API to initiate connection with a webcam from my filter, nor copy the video stream from a buffer of webcam to some temporary destination such as shared memory to use IPC. Please help.
Comment
Watch Question

Author

Commented:
Yes, DirectShow can easily capture frames, but I need to access pixels of each frame to store them into shared memory.

Currently, I generate random frame each time it is called:

// This is where we insert the DIB bits into the video stream.
// FillBuffer is called once for every sample in the stream.
HRESULT MyOutputPin::FillBuffer(IMediaSample *pSample) {
      log_message("MyOutputPin::FillBuffer()");
      CheckPointer(pSample, E_POINTER);
    BYTE *pData;
    long lDataLen;

      pSample->GetPointer(&pData);
      lDataLen = pSample->GetSize();

      ZeroMemory(pData, lDataLen);
      {
          CAutoLock cAutoLockShared(&m_cSharedState);
            long i;
            for (i = 0; i < lDataLen; i++) {
                  if (i & 1) {
                        pData[i] = rand() % 255;
                  } else {
                        pData[i] = rand() % 255;
                  }
            }
    }

    pSample->SetSyncPoint(TRUE);
    return S_OK; // S_FALSE End of stream, S_OK = filled
}

It will look similarly to http://www.eggheadcafe.com/software/aspnet/31543459/source-filters-fillbuffe.aspx when you tell me how to get BYTE *pData from each frame grabbed from a webcam.
Commented:
You can use SampleGrabber to access pixels of each frame.

See the code below... I stripped all error checking for brevity... i may have striped something else by mistake... but it may give you some directions.

The 'mDShowSampleGrabberCB' object will be called every frame, as soon as a new frame is available.




//Callback class
class SampleGrabberCallback : public ISampleGrabberCB
{
public:
    SampleGrabberCallback(void)
    {
        referenceCount = 0;
    }
 
    /// Mandatory overrides
    STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject)
    {
        *ppvObject = static_cast<ISampleGrabberCB*>(this);
        return S_OK;
    }
 
    STDMETHODIMP_(ULONG) AddRef(void)  { return ++referenceCount; }
    STDMETHODIMP_(ULONG) Release(void)
    {
        if(referenceCount == 1)
        {
            // clean Up
        }
        return --referenceCount;
    }
 
    // We receive here a pointer to the original buffer (we need to make a copy)
    STDMETHODIMP SampleCB(double Time, IMediaSample* pSample)
    {
        unsigned char* ptrBuffer;
 
        HRESULT hr = pSample->GetPointer(&ptrBuffer);
 
        if(hr == S_OK)
        {
            // do something with the buffer here...
            // something like:
            //
            // int bufferLength = pSample->GetActualDataLength();
            // memcpy(mybuffer,ptrBuffer,bufferLength);
        }
 
        return S_OK;
    }
 
    // We won't use this... it returns a copy so it has more overhead
    STDMETHODIMP BufferCB(double Time, BYTE* pBuffer, long BufferLen){
       return E_NOTIMPL;
    }
 
    unsigned int referenceCount;
 
};  // SampleGrabberCallback
 
 
 
///
///
/// then, somewhere in your code
///
///
 
(...)
// Create the sample grabber filter and add it to the graph
IBaseFilter* mDShowFilterGrabber;
hr = CoCreateInstance(CLSID_SampleGrabber,NULL,
                      CLSCTX_INPROC_SERVER,IID_IBaseFilter,
                      (void**)&mDShowFilterGrabber);
 
hr = pGraph->AddFilter(mDShowFilterGrabber,L"Sample Grabber");
 
// Create the sample grabber
ISampleGrabber* mDShowSampleGrabber;
hr = mDShowFilterGrabber->QueryInterface(IID_ISampleGrabber,
                                        (void**)&mDShowSampleGrabber);
mDShowSampleGrabber->SetOneShot(FALSE);
mDShowSampleGrabber->SetBufferSamples(FALSE);
 
// Sets the sample grabber media type
AM_MEDIA_TYPE mt;
ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
mt.majortype = MEDIATYPE_Video;
mt.subtype = MEDIASUBTYPE_RGB24;
hr = mDShowSampleGrabber->SetMediaType(&mt);
 
// Create the callback
SampleGrabberCallback* mDShowSampleGrabberCB;
mDShowSampleGrabberCB = new SampleGrabberCallback();
 
//Tell the grabber to use our callback function
//0 is for SampleCB and 1 for BufferCB
hr = mDShowSampleGrabber->SetCallback(mDShowSampleGrabberCB,0);

Open in new window

Not the solution you were looking for? Getting a personalized solution is easy.

Ask the Experts

Author

Commented:
That's exactly it! Thank you so much vhpgomes.
Access more of Experts Exchange with a free account
Thanks for using Experts Exchange.

Create a free account to continue.

Limited access with a free account allows you to:

  • View three pieces of content (articles, solutions, posts, and videos)
  • Ask the experts questions (counted toward content limit)
  • Customize your dashboard and profile

*This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

OR

Please enter a first name

Please enter a last name

8+ characters (letters, numbers, and a symbol)

By clicking, you agree to the Terms of Use and Privacy Policy.