mike1821 mike1821 - 1 month ago 18
C++ Question

DirectShow camera stream driver

I am a newbie in DirectShow programming and I am trying to create a sample grabber application in order to frequently grab frames (i.e every 100 msec) from a video capture device.

My source code is based in the SampleGrabber application which is available in the Windows SDK samples.

In general the driver works just fine using my PCs webcam. However, I do have some problems in using it with an Asus T100 tablet. In this case I am getting an error "libtbd error data is not tagged properly" just when I am trying to use ConnectFilters function, after setting the resolution of the camera. The resolution I am trying to set to the camera is 1280x720 and I do know that the device can support it.

Below I am attaching part of my code regarding the capturing function:

ICreateDevEnum *pSysDevEnum = 0;
IEnumMoniker *pEnumCams = 0;
IMoniker *pMon = 0;
IBaseFilter *CameraF = 0;

IGraphBuilder *pGraph = NULL;
IMediaControl *pControl = NULL;
IMediaEventEx *pEvent = NULL;
IBaseFilter *pGrabberF = NULL;
ISampleGrabber *pSGrabber = NULL;
IBaseFilter *pSourceF = NULL;
IEnumPins *pEnum = NULL;
IPin *pPin = NULL;
IBaseFilter *pNullF = NULL;

BYTE *pBuffer = NULL;
HRESULT hr;
AM_MEDIA_TYPE mt;
int count = 0;

printf("Statting with capturing\n");
//Device list
hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
IID_ICreateDevEnum, (void **)&pSysDevEnum);
if (FAILED(hr))
return false;

// Obtain a class enumerator for the video compressor category.
hr = pSysDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
&pEnumCams, 0);
if (hr != S_OK)
return false;

// Enumerate the monikers. to desired device (0,1,2,...)
for (int i = 0; i <= cur_cam; ++i)
{
hr = pEnumCams->Next(1, &pMon, NULL);
if (hr != S_OK)
return false;
}
//Get BaseFilter of chosen camera
hr = pMon->BindToObject(0, 0, IID_IBaseFilter, (void**)&CameraF);
if (hr != S_OK)
return false;

//Create the Filter Graph Manager
hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pGraph));
if (FAILED(hr))
return false;

//Query for the IMediaControl and IMediaEventEx interfaces
hr = pGraph->QueryInterface(IID_PPV_ARGS(&pControl));
if (FAILED(hr))
return false;

hr = pGraph->QueryInterface(IID_PPV_ARGS(&pEvent));
if (FAILED(hr))
return false;

// Add web camera to graph as source filter (because first?)
hr = pGraph->AddFilter(CameraF, L"Capture Source");
if (hr != S_OK)
return false;

// Create an instance of the Sample Grabber Filter
hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pGrabberF));
if (FAILED(hr))
return false;

// Add it to the filter graph.
hr = pGraph->AddFilter(pGrabberF, L"Sample Grabber");
if (FAILED(hr))
return false;

hr = pGrabberF->QueryInterface(IID_PPV_ARGS(&pSGrabber));
if (FAILED(hr))
return false;

//Set the Media Type
ZeroMemory(&mt, sizeof(mt));
mt.majortype = MEDIATYPE_Video;
mt.subtype = MEDIASUBTYPE_RGB24 ;

hr = pSGrabber->SetMediaType(&mt);
if (FAILED(hr))
return false;

//Building graph. Connecting Source (CameraF) and Sample Grabber
hr = CameraF->EnumPins(&pEnum);
if (FAILED(hr))
return false;

while (S_OK == pEnum->Next(1, &pPin, NULL))
{
hr = SetResolution(width_, height_, pPin);

if (FAILED(hr))
std::cout << "Failed to set resolution" << std::endl;

hr = ConnectFilters(pGraph, pPin, pGrabberF);

if (SUCCEEDED(hr))
{
std::cout << "Success to Connect Filter Graphs" << std::endl;
break;
}
else{
std::cout << "Failed to Connect Filter Graphs" << std::endl;
}
}

if (FAILED(hr)){
std::cout << "Failed A" << std::endl;
return false;
}


SafeRelease(pPin);

//The following code connects the Sample Grabber to the Null Renderer filter:
hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pNullF));
if (FAILED(hr))
return false;

hr = pGraph->AddFilter(pNullF, L"Null Filter");
if (FAILED(hr))
return false;

//ConnectFilters SamplerGrabber and Null Filter
hr = ConnectFilters(pGraph, pGrabberF, pNullF);
if (FAILED(hr))
return false;

hr = pSGrabber->SetBufferSamples(TRUE);
if (FAILED(hr))
return false;

hr = pSGrabber->SetOneShot(TRUE);//Halt after sample received
if (FAILED(hr))
return false;

hr = pControl->Run();//Run filter graph
if (FAILED(hr))
return false;

printf("Wait for a seconds to let cam sensor adopt to light\n");
Sleep(3000);//Wait 3 seconds
printf("OK\n");

//char filename[50];
printf("Go for the loop");

for (;;){

long evCode;
while (1)
{
hr = pEvent->WaitForCompletion(INFINITE, &evCode);//Wait for frame
if (evCode == EC_COMPLETE || evCode == EC_SYSTEMBASE)
break;
printf("Not complete. event: %d\n", evCode);
pControl->Pause();//pause the Graph
pSGrabber->SetOneShot(TRUE);//Set to halt after first frame
hr = pControl->Run();//resume filter graph
if (FAILED(hr))
return false;
Sleep(1000);//wait for a second
}
printf("out of while loop");
//if (hr != S_OK)
// return false;
Sleep(100);
//there frame got. Graph still running, but Sample Grabber halted

// Find the required buffer size.
long cbBufSize;
hr = pSGrabber->GetCurrentBuffer(&cbBufSize, NULL);
if (FAILED(hr))
return false;

pBuffer = (BYTE*)CoTaskMemAlloc(cbBufSize);
if (!pBuffer)
{
hr = E_OUTOFMEMORY;
return false;
}

hr = pSGrabber->GetCurrentBuffer(&cbBufSize, (long*)pBuffer);
if (FAILED(hr))
return false;


hr = pSGrabber->GetConnectedMediaType(&mt);
if (FAILED(hr))
return false;


// Examine the format block.
if ((mt.formattype == FORMAT_VideoInfo) &&
(mt.cbFormat >= sizeof(VIDEOINFOHEADER)) && (mt.pbFormat != NULL))
{
VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*)mt.pbFormat;
//sprintf_s(filename, sizeof(filename), "photo%d.bmp",++count);
time_t t1 = time(NULL);
SYSTEMTIME st;
GetSystemTime(&st);

printf("Save frame to file: %s - %02ld,%02ld.%03ld seconds\n", FileName, st.wMinute,st.wSecond, st.wMilliseconds );
hr = WriteBitmap(FileName, &pVih->bmiHeader, mt.cbFormat - SIZE_PREHEADER,pBuffer, cbBufSize);
}
else
{
// Invalid format.
hr = VFW_E_INVALIDMEDIATYPE;
printf("Invalid frame format\n");
}

CoTaskMemFree(pBuffer);

if ((GetAsyncKeyState(VK_ESCAPE) & 0x01))
break;
else if ((GetAsyncKeyState(VK_SPACE) & 0x01))
CameraProperties(CameraF);

}

SafeRelease(pEnum);
SafeRelease(pNullF);
SafeRelease(pSourceF);
SafeRelease(pSGrabber);
SafeRelease(pGrabberF);
SafeRelease(pControl);
SafeRelease(pEvent);
SafeRelease(pGraph);


And below is the function which sets the camera resolution:

// Set the grabbing size
// First we iterate through the available media types and
// store the first one that fits the requested size.
// If we have found one, we set it.
// In any case we query the size of the current media type
// to have this information for clients of this class.

HRESULT hr;

IAMStreamConfig *pConfig;
IEnumMediaTypes *pMedia;
AM_MEDIA_TYPE *pmt = NULL, *pfnt = NULL;

hr = pPin->EnumMediaTypes(&pMedia);
if (SUCCEEDED(hr))
{

while (pMedia->Next(1, &pmt, 0) == S_OK)
{
if (pmt->formattype == FORMAT_VideoInfo)
{
VIDEOINFOHEADER *vih = (VIDEOINFOHEADER *)pmt->pbFormat;
// printf("Size %i %i\n", vih->bmiHeader.biWidth, vih->bmiHeader.biHeight );
if (vih->bmiHeader.biWidth == width_ && vih->bmiHeader.biHeight == height_)
{
pfnt = pmt;
printf("found mediatype with %i %i\n", vih->bmiHeader.biWidth, vih->bmiHeader.biHeight );
break;
}
DeleteMediaType(pmt);
}
}
pMedia->Release();
}
hr = pPin->QueryInterface(IID_IAMStreamConfig, (void **)&pConfig);
if (SUCCEEDED(hr))
{
if (pfnt != NULL)
{
hr = pConfig->SetFormat(pfnt);
DeleteMediaType(pfnt);
}
hr = pConfig->GetFormat(&pfnt);
if (SUCCEEDED(hr))
{

m_nWidth = ((VIDEOINFOHEADER *)pfnt->pbFormat)->bmiHeader.biWidth;
m_nHeight = ((VIDEOINFOHEADER *)pfnt->pbFormat)->bmiHeader.biHeight;

DeleteMediaType(pfnt);
}
}


Did anyone face this kind of problem before? Is there any mistakes in the capturing filters setup?

Any ideas would be extremely helpful.

UPDATE 1



I am adding a screenshot of the application execution output. Output

UPDATE 2



I tried to set the resolution using OpenCV 3.1, but I had the exact same problem. The tablet is using the Intel(R) Imaging Signal Processor 2400 driver for the camera and this is the where the problem is located. The strange think is that using AmCap I am able to both set the camera resolution and handle the camera device without any problems. Can please someone help? Does anyone knows what interface AmCap is using for communicating with the camera?

Answer

I managed to resolve this issue. It seems that the reported libdbd error was not related to the resolution change.

The problem was that I was trying to set resolution by passing an unsupported video format in the SetFormat() function. In detail the new working SetResolution() function became:

// Set the grabbing size
// First we iterate through the available media types and 
// store the first one that fits the requested size.
// If we have found one, we set it.
// In any case we query the size of the current media type
// to have this information for clients of this class.

HRESULT hr;

IAMStreamConfig *pConfig=NULL;
//IEnumMediaTypes *pMedia;
BYTE *pSCC = NULL;
AM_MEDIA_TYPE *pmt = NULL, *pfnt = NULL, *mfnt=NULL;

//hr = pPin->EnumMediaTypes(&pMedia);
int iCount, iSize;

hr = pPin->QueryInterface(IID_IAMStreamConfig, (void **)&pConfig);

hr = pConfig->GetNumberOfCapabilities(&iCount, &iSize);
pSCC = new BYTE[iSize];
int i = 0;
if (SUCCEEDED(hr))
{
    while (i<=iCount)
    {
        pConfig->GetStreamCaps(i++, &pfnt, pSCC);

        VIDEOINFOHEADER *vih = (VIDEOINFOHEADER *)pfnt->pbFormat;
        printf("Size %i  %i\n", vih->bmiHeader.biWidth, vih->bmiHeader.biHeight );
        if (vih->bmiHeader.biWidth == width_ && vih->bmiHeader.biHeight == height_)
        {
            //pfnt = mfnt;
            printf("Found mediatype with %i %i - %x\n", vih->bmiHeader.biWidth, vih->bmiHeader.biHeight, pfnt->formattype );
            hr = pConfig->SetFormat(pfnt);
            //break;
        }

    }
}

//DeleteMediaType(pfnt);
delete[] pSCC;
pConfig->Release();
return hr;