I am trying to use the enhanced video renderer to take a snapshot, image of the current rendered stream. I am able to display a video feed either from a file or capture card. When I run
if (pDisplay)
{
BITMAPINFOHEADER lpHeader = { 0 };
BYTE* lpCurrImage = NULL;
DWORD bitmapSize = 0;
LONGLONG timestamp = 0;
lpHeader.biSize = sizeof(BITMAPINFOHEADER);
if (SUCCEEDED(hr = pDisplay->GetCurrentImage(&lpHeader, &lpCurrImage, &bitmapSize, ×tamp)))
{
the output image data is 0, see image below:
For some reason, when I use VMR7 or 9, the image works fine and has data (rendering the same video) however EVR does not get an image. The bitmap that outputs is 14kb (which is the header information).
For more info, here is the code that I use to render the EVR:
HRESULT InitializeWindowlessEVR(IGraphBuilder* pGraph, IBaseFilter** ppEVR)
{
IBaseFilter* pEvr = NULL;
CComPtr<IMFGetService> pServices;
CComPtr<IMFQualityAdvise2> pAdvise;
if (!ppEVR)
return E_POINTER;
*ppEVR = NULL;
// Create the VMR and add it to the filter graph.
HRESULT hr = CoCreateInstance(CLSID_EnhancedVideoRenderer, NULL,
CLSCTX_INPROC, IID_IBaseFilter, (void**)&pEvr);
if (SUCCEEDED(hr))
{
hr = pGraph->AddFilter(pEvr, L"Enhanced Video Renderer");
if (SUCCEEDED(hr))
{
// Update the window settings
CHECK_HR(pEvr->QueryInterface(IID_IMFGetService, (void**)&pServices));
CHECK_HR(pServices->GetService(MR_VIDEO_RENDER_SERVICE, IID_IMFVideoDisplayControl, (void**)&pVideoDisplay));
CHECK_HR(pVideoDisplay->SetVideoWindow(ghApp));
CHECK_HR(pVideoDisplay->SetAspectRatioMode(MFVideoARMode_PreservePicture));
}
pEvr->AddRef();
*ppEVR = pEvr;
pEvr->Release();
pEvr = NULL;
}
done:
return hr;
}
Please help me figure out why I am unable to get an image for EVR, I do not want to go back and use VMR9. I can post more code if it helps. Thanks in advance!
Answer:
BOOL CaptureImage(LPCTSTR szFile)
{
if (pVideoDisplay)
{
BYTE* lpCurrImage = NULL;
BITMAPINFOHEADER lpHeader = { 0 };
DWORD bitmapSize = 0;
LONGLONG timestamp = 0;
lpHeader.biSize = sizeof(BITMAPINFOHEADER);
if (SUCCEEDED(pVideoDisplay->GetCurrentImage(&lpHeader, &lpCurrImage, &bitmapSize, ×tamp)))
{
// Create a new file to store the bitmap data
HANDLE hFile = CreateFile(szFile, GENERIC_WRITE, FILE_SHARE_READ, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if (hFile == INVALID_HANDLE_VALUE)
return FALSE;
DWORD dwWritten;
BITMAPFILEHEADER fileHeader;
fileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
fileHeader.bfReserved1 = 0;
fileHeader.bfReserved2 = 0;
fileHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + lpHeader.biSizeImage;
fileHeader.bfType = 0x4d42;
// Write the bitmap header and bitmap bits to the file
WriteFile(hFile, (LPCVOID)&fileHeader, sizeof(BITMAPFILEHEADER), &dwWritten, 0);
WriteFile(hFile, (LPCVOID)&lpHeader, sizeof(BITMAPINFOHEADER), &dwWritten, 0);
WriteFile(hFile, (LPCVOID)lpCurrImage, bitmapSize, &dwWritten, 0);
CloseHandle(hFile);
CoTaskMemFree(lpCurrImage);
return TRUE;
}
else
{
return FALSE;
}
}
return FALSE;
}
Related
I using GetCursorInfo to capture cursor but while saving the cursor as icon getting some black rectangle over on icon.
For windows default cursors are fine but few custom cursor I facing this issue http://www.cursors-4u.com/
Placed one example cursor icon in link https://www.google.com/search?q=cursor+icon&rlz=1C1CHBD_enIN789IN789&source=lnms&tbm=isch&sa=X&ved=0ahUKEwix9aj_gq_iAhWCXCsKHcusD0oQ_AUIDigB&biw=1366&bih=657#imgrc=rOxlbRoBfnKs8M:
HRESULT SaveIcon(HICON hIcon, const char* path)
{
// Create the IPicture intrface
PICTDESC desc = { sizeof(PICTDESC) };
desc.picType = PICTYPE_ICON;
desc.icon.hicon = hIcon;
IPicture* pPicture = 0;
HRESULT hr = OleCreatePictureIndirect(&desc, IID_IPicture, FALSE, (void**)&pPicture);
if (FAILED(hr)) return hr;
// Create a stream and save the image
IStream* pStream = 0;
CreateStreamOnHGlobal(0, TRUE, &pStream);
LONG cbSize = 0;
hr = pPicture->SaveAsFile(pStream, TRUE, &cbSize);
// Write the stream content to the file
if (!FAILED(hr))
{
HGLOBAL hBuf = 0;
GetHGlobalFromStream(pStream, &hBuf);
void* buffer = GlobalLock(hBuf);
HANDLE hFile = CreateFileA(path, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0);
if (!hFile)
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
else
{
DWORD written = 0;
WriteFile(hFile, buffer, cbSize, &written, 0);
CloseHandle(hFile);
}
GlobalUnlock(buffer);
}
// Cleanup
pStream->Release();
pPicture->Release();
return hr;
}
//Capture cursor.
CURSORINFO getHCursor()
{
CURSORINFO cursorInfo;
cursorInfo.cbSize = sizeof(CURSORINFO);
if (GetCursorInfo(&cursorInfo) == 0)
{
MessageBox(NULL, _T("Exception : GetCursorInfo creation failed"),_T("message"),MB_OK|MB_SYSTEMMODAL);
cursorInfo.hCursor = NULL;
return cursorInfo;
}
return cursorInfo;
}
//Main Call
int _tmain(int argc, _TCHAR* argv[])
{
while (true)
{
CURSORINFO CursorInfo = getHCursor();
if (CursorInfo.hCursor == NULL)
{
::Sleep(MinSleep);
continue;
}
SaveIcon((HICON)CursorInfo.hCursor, "C:\\Users\\Desktop\\myicon.ico");
Sleep(MaxSleep);
}
return 0;
}
My agenda is to capture cursor and save cursor in to icon(.ico) file or load in to buffer.
Is any other way I can write cursor data in to icon file or buffer ?
The ICONINFO struct contains two members, hbmMask and hbmColor, that contain the mask and color bitmaps, respectively, for the cursor (see the MSDN page for ICONINFO for the official documentation).
When you call GetIconInfo() for the default cursor, the ICONINFO struct contains both valid mask and color bitmaps.
There is probably a better way to render the cursor that the
BitBlt() - BitBlt() - MakeTransparent() combination of method
calls.
Refer to the #Tarsier approach, although it's C #, but the idea is the same.
Link: Capturing the Mouse cursor image
Before to made this post I searched and trying 4 days all things,like https://www.codeproject.com/kb/gdi-plus/memimage.aspx, but I am new in programming.
This code (not mine), made a print screen, save to a file and then are sent to a ftp server. I want to do this without save the files to HDD, from memory to FTP.
// The print-screen and save to file (jpg) code:
wstring wtmp;
tmp = sDate + pictureName + ".jpeg";
wtmp = ToStringW(tmp);
screenName = wtmp.c_str();
ftpScreenName = wtmp.c_str();
fileToRemove = tmp.c_str();
using namespace Gdiplus;
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
{
HDC scrdc, memdc;
HBITMAP membit;
scrdc = ::GetDC(0);
int Height = GetSystemMetrics(SM_CYSCREEN);
int Width = GetSystemMetrics(SM_CXSCREEN);
memdc = CreateCompatibleDC(scrdc);
membit = CreateCompatibleBitmap(scrdc, Width, Height);
HBITMAP hOldBitmap = (HBITMAP)SelectObject(memdc, membit);
BitBlt(memdc, 0, 0, Width, Height, scrdc, 0, 0, SRCCOPY);
Gdiplus::Bitmap bitmap(membit, NULL);
EncoderParameters encoderParameters;
ULONG quality;
CLSID clsid;
GetEncoderClsid(L"image/jpeg", &clsid);
encoderParameters.Count = 1;
encoderParameters.Parameter[0].Guid = EncoderQuality;
encoderParameters.Parameter[0].Type = EncoderParameterValueTypeLong;
encoderParameters.Parameter[0].NumberOfValues = 1;
// Save the image as a JPEG with quality level 80.
quality = 80;
encoderParameters.Parameter[0].Value = &quality;
int result;
result = bitmap.Save(screenName, &clsid); //saving the file to HDD
//send to ftp with given info
saveFTP(server, user, password, ftpScreenName, screenName);
}
GdiplusShutdown(gdiplusToken);
// And here the FTP client part:
bool saveFTP(LPCWSTR l_server, LPCWSTR l_user, LPCWSTR l_pass, LPCWSTR l_ftpfile, LPCWSTR l_screen)
{
HINTERNET internet = InternetOpen(L"tester", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, INTERNET_FLAG_ASYNC);
HINTERNET inter = InternetConnect(internet, l_server, INTERNET_DEFAULT_FTP_PORT, l_user, l_pass, INTERNET_SERVICE_FTP, 0, 0);
bool result;
result = FtpPutFile(inter, l_screen, l_ftpfile, FTP_TRANSFER_TYPE_BINARY, 0);
InternetCloseHandle(inter);
InternetCloseHandle(internet);
return result;
}
Use FtpOpenFile() and InternetWriteFile() instead of FtpPutFile(). This is explicitly stated in the FtpPutFile() documentation:
FtpPutFile is a high-level routine that handles all the bookkeeping and overhead associated with reading a file locally and storing it on an FTP server. An application that needs to send file data only, or that requires close control over the file transfer, should use the FtpOpenFile and InternetWriteFile functions.
As well as in WinInet's FTP Sessions documentation
To upload or place files on an FTP server, the application can use either FtpPutFile or FtpOpenFile (along with InternetWriteFile). FtpPutFile can be used if the file already exists locally, while FtpOpenFile and InternetWriteFile can be used if data needs to be written to a file on the FTP server.
Gdiplus::Bitmap can save to either an HDD file or an IStream. You can use
either CreateStreamOnHGlobal() or SHCreateMemStream() to create a memory stream, then save the Bitmap to the stream, and finally upload the content of the stream using FtpOpenFile() and InternetWriteFile().
For example (error handling omitted for brevity, don't omit it in your real code!):
tmp = sDate + pictureName + ".jpeg";
wstring wtmp = ToStringW(tmp);
IStream *strm = SHCreateMemStream(NULL, 0);
using namespace Gdiplus;
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
CLSID clsid;
GetEncoderClsid(L"image/jpeg", &clsid);
EncoderParameters encoderParameters;
encoderParameters.Count = 1;
encoderParameters.Parameter[0].Guid = EncoderQuality;
encoderParameters.Parameter[0].Type = EncoderParameterValueTypeLong;
encoderParameters.Parameter[0].NumberOfValues = 1;
// Save the image as a JPEG with quality level 80.
ULONG quality = 80;
encoderParameters.Parameter[0].Value = &quality;
HDC scrdc = ::GetDC(0);
int Height = GetSystemMetrics(SM_CYSCREEN);
int Width = GetSystemMetrics(SM_CXSCREEN);
HDC memdc = CreateCompatibleDC(scrdc);
HBITMAP membit = CreateCompatibleBitmap(scrdc, Width, Height);
HBITMAP hOldBitmap = (HBITMAP) SelectObject(memdc, membit);
BitBlt(memdc, 0, 0, Width, Height, scrdc, 0, 0, SRCCOPY);
{
Gdiplus::Bitmap bitmap(membit, NULL);
bitmap.Save(strm, &clsid); //saving the file to HDD
}
SelectObject(memdc, hOldBitmap);
DeleteObject(membit);
DeleteDC(memdc);
::ReleaseDC(0, scrdc);
GdiplusShutdown(gdiplusToken);
LARGE_INTEGER li;
li.QuadPart = 0;
strm->Seek(li, STREAM_SEEK_SET, NULL);
//send to ftp with given info
saveFTP(server, user, password, wtmp.c_str(), strm);
strm->Release();
bool saveFTP(LPCWSTR l_server, LPCWSTR l_user, LPCWSTR l_pass, LPCWSTR l_ftpfile, IStream *l_screen)
{
HINTERNET internet = InternetOpen(L"tester", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, INTERNET_FLAG_ASYNC);
HINTERNET conn = InternetConnect(internet, l_server, INTERNET_DEFAULT_FTP_PORT, l_user, l_pass, INTERNET_SERVICE_FTP, 0, 0);
HINTERNET file = FtpOpenFile(conn, l_ftpfile, GENERIC_WRITE, FTP_TRANSFER_TYPE_BINARY, 0);
BYTE buffer[1024], *pbuf;
ULONG ulRead;
DWORD dwWritten;
HRESULT hr;
bool success = true;
do
{
hr = l_screen->Read(buffer, sizeof(buffer), &ulRead);
if (FAILED(hr))
{
success = false;
break;
}
pbuf = buffer;
while (ulRead != 0)
{
if (!InternetWriteFile(file, pbuf, ulRead, &dwWritten))
{
success = false;
break;
}
pbuf += dwWritten;
ulRead -= dwWritten;
}
}
while (hr == S_OK);
InternetCloseHandle(file);
InternetCloseHandle(conn);
InternetCloseHandle(internet);
return success;
}
Scenario:
Load a TIFF image and extract the frames of tiff image and save it locally.
Combine the extracted frames to the output TIFF image.
When i try to combine the frames, the size of the output tiff image is increasing drastically. For example if my input size if 40 MB , the output is increased to 300 MB.
Below is the code which explains the scenario,
void readTiff()
{
HRESULT hr;
IWICBitmapFrameDecode *frameDecode = NULL;
IWICFormatConverter *formatConverter = NULL;
IWICBitmapEncoder *encoder = NULL;
IWICStream *pOutStream = NULL;
IWICBitmapFrameEncode *frameEncode = NULL;
IWICImagingFactory* m_pWICFactory;
hr = CoCreateInstance(
CLSID_WICImagingFactory,
NULL,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&m_pWICFactory)
);
IWICBitmapDecoder *pIDecoder = NULL;
hr = m_pWICFactory->CreateDecoderFromFilename(
L"D:\\test28\\Multitiff_files\\300dpiTIFF40MB_WATER.tif", // Image to be decoded
NULL, // Do not prefer a particular vendor
GENERIC_READ, // Desired read access to the file
WICDecodeMetadataCacheOnDemand, // Cache metadata when needed
&pIDecoder // Pointer to the decoder
);
UINT frameCount = 0;
pIDecoder->GetFrameCount(&frameCount);
for (int i = 0; i < frameCount; i++)
{
wchar_t temp[200];
int j = i;
swprintf_s(temp, 200, L"D:\\test28\\Multitiff_files\\out\\filename_png%d.jpeg", i);
if (SUCCEEDED(hr))
hr = m_pWICFactory->CreateStream(&pOutStream);
if (SUCCEEDED(hr))
hr = pOutStream->InitializeFromFilename(temp, GENERIC_WRITE);
if (SUCCEEDED(hr))
hr = m_pWICFactory->CreateEncoder(GUID_ContainerFormatJpeg, NULL, &encoder);
if (SUCCEEDED(hr))
hr = encoder->Initialize(pOutStream, WICBitmapEncoderNoCache);
hr = pIDecoder->GetFrame(i, &frameDecode);
if (SUCCEEDED(hr))
hr = m_pWICFactory->CreateFormatConverter(&formatConverter);
hr = formatConverter->Initialize(
frameDecode, // Source frame to convert
GUID_WICPixelFormat8bppIndexed, // The desired pixel format
WICBitmapDitherTypeNone, // The desired dither pattern
NULL, // The desired palette
0.f, // The desired alpha threshold
WICBitmapPaletteTypeMedianCut // Palette translation type
);
IPropertyBag2 *pPropertybag = NULL;
//Create a new frame..
hr = encoder->CreateNewFrame(&frameEncode, &pPropertybag);
//PROPBAG2 option = { 0 };
//option.pstrName = L"ImageQuality";
//VARIANT varValue;
//VariantInit(&varValue);
//varValue.vt = VT_R4;
//varValue.fltVal = 0.01f;
//hr = pPropertybag->Write(
// 1, // number of properties being set
// &option,
// &varValue);
WICPixelFormatGUID pixelFormat;
hr = frameEncode->Initialize(pPropertybag);
//pixelFormat = GUID_WICPixelFormat8bppIndexed;
frameDecode->GetPixelFormat(&pixelFormat);
hr = frameEncode->SetPixelFormat(&pixelFormat);
hr = frameEncode->WriteSource(formatConverter, NULL);
frameEncode->Commit();
encoder->Commit();
if (formatConverter)
formatConverter->Release();
if (frameDecode)
frameDecode->Release();
if (frameEncode)
frameEncode->Release();
if (pOutStream)
pOutStream->Release();
if (encoder)
encoder->Release();
}
if (m_pWICFactory)
m_pWICFactory->Release();
pIDecoder->Release();
}
void combineFile(int count)
{
HRESULT hr;
IWICImagingFactory *pFactory = NULL;
IWICStream *pInStream = NULL;
IWICBitmapDecoder *decoder = NULL;
IWICBitmapFrameDecode *frameDecode = NULL;
IWICFormatConverter *formatConverter = NULL;
IWICBitmapEncoder *encoder = NULL;
IWICStream *pOutStream = NULL;
IWICBitmapFrameEncode *frameEncode = NULL;
hr = CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_IWICImagingFactory, (LPVOID*)&pFactory);
if (!SUCCEEDED(hr)) {
hr = CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_IWICImagingFactory, (LPVOID*)&pFactory);
}
if (SUCCEEDED(hr))
hr = pFactory->CreateStream(&pOutStream);
if (SUCCEEDED(hr))
hr = pOutStream->InitializeFromFilename(L"D:\\test28\\Multitiff_files\\out\\out.tiff", GENERIC_WRITE);
if (SUCCEEDED(hr))
hr = pFactory->CreateEncoder(GUID_ContainerFormatWmp, NULL, &encoder);
if (SUCCEEDED(hr))
hr = encoder->Initialize(pOutStream, WICBitmapEncoderNoCache);
for (int i = 0; i < count; i++)
{
wchar_t temp[200];
swprintf_s(temp, 200, L"D:\\test28\\Multitiff_files\\out\\filename_png%d.jpeg", i);
if (SUCCEEDED(hr))
hr = pFactory->CreateStream(&pInStream);
if (SUCCEEDED(hr))
hr = pInStream->InitializeFromFilename(temp, GENERIC_READ);
if (SUCCEEDED(hr))
hr = pFactory->CreateDecoderFromStream(pInStream, NULL, WICDecodeMetadataCacheOnLoad, &decoder);
if (SUCCEEDED(hr))
hr = pFactory->CreateFormatConverter(&formatConverter);
hr = decoder->GetFrame(0, &frameDecode);
//hr = formatConverter->Initialize(
// frameDecode, // Source frame to convert
// GUID_WICPixelFormat8bppIndexed, // The desired pixel format
// WICBitmapDitherTypeNone, // The desired dither pattern
// NULL, // The desired palette
// 0.f, // The desired alpha threshold
// WICBitmapPaletteTypeMedianCut // Palette translation type
// );
WICPixelFormatGUID pixelFormat;
frameDecode->GetPixelFormat(&pixelFormat);
IPropertyBag2 *pPropertybag = NULL;
//Create a new frame..
hr = encoder->CreateNewFrame(&frameEncode, &pPropertybag);
PROPBAG2 option = { 0 };
option.pstrName = L"TiffCompressionMethod";
VARIANT varValue;
VariantInit(&varValue);
varValue.vt = VT_UI1;
varValue.bVal = WICTiffCompressionOption::WICTiffCompressionZIP;
hr = pPropertybag->Write(1, &option, &varValue);
hr = frameEncode->Initialize(pPropertybag);
hr = frameEncode->SetPixelFormat(&pixelFormat);
hr = frameEncode->WriteSource(formatConverter, NULL);
hr = frameEncode->Commit();
if (pInStream)
pInStream->Release();
if (decoder)
decoder->Release();
if (formatConverter)
formatConverter->Release();
if (frameEncode)
frameEncode->Release();
//if (frameDecode)
//frameDecode->Release();
}
encoder->Commit();
if (pFactory)
pFactory->Release();
if (encoder)
encoder->Release();
if (pOutStream)
pOutStream->Release();
}
One easy way to debug this kind of issue is to use ImageMagick which is installed on most Linux distros and is available for OSX and Windows. First use the identify utility within the suite with its verbose option to find out everything about your before and after images like this:
# Find out all we know about first image and put in file "1.txt"
identify -verbose image1.tif > 1.txt
# Find out all we know about second image and put in file "2.txt"
identify -verbose image2.tif > 2.txt
Now use your favourite file comparison tool to see the differences:
opendiff 1.txt 2.txt
I am trying to modify the existing AmCap application, available through Microsoft's SDK Direct Show samples, in order to get an image of the captured stream when I press the space button. Below is the point in which I am handling the space keydown.
case WM_KEYDOWN:
if((GetAsyncKeyState(VK_ESCAPE) & 0x01) && gcap.fCapturing)
{
StopCapture();
if(gcap.fWantPreview)
{
BuildPreviewGraph();
StartPreview();
}
}
else if ((GetAsyncKeyState(VK_SPACE) & 0x01))
{
IMediaControl *pMC = NULL;
HRESULT hr = gcap.pFg->QueryInterface(IID_IMediaControl, (void **)&pMC);
if (SUCCEEDED(hr)){
hr=pMC->Pause();
if (SUCCEEDED(hr)){
CaptureImage(TEXT("C:\\output.bmp"));
pMC->Run();
pMC->Release();
}else
ErrMsg(TEXT("Failed to pause stream! hr=0x%x"), hr);
}
else
ErrMsg(TEXT("Cannot grab image"));
}
break;
Below is the contents of the CaptureImage function.
HRESULT hr;
SmartPtr<IBasicVideo> pWC;
// If we got here, gcap.pVW is not NULL
ASSERT(gcap.pVW != NULL);
hr = gcap.pVW->QueryInterface(IID_IBasicVideo, (void**)&pWC);
if (pWC)
{
long bufSize;
long *lpCurrImage = NULL;
pWC->GetCurrentImage(&bufSize, NULL);
lpCurrImage = (long *)malloc(bufSize);
//
// Read the current video frame into a byte buffer. The information
// will be returned in a packed Windows DIB and will be allocated
// by the VMR.
hr = pWC->GetCurrentImage(&bufSize, lpCurrImage);
if (SUCCEEDED(hr))
{
HANDLE fh;
BITMAPFILEHEADER bmphdr;
BITMAPINFOHEADER bmpinfo;
DWORD nWritten;
memset(&bmphdr, 0, sizeof(bmphdr));
memset(&bmpinfo, 0, sizeof(bmpinfo));
bmphdr.bfType = ('M' << 8) | 'B';
bmphdr.bfSize = sizeof(bmphdr) + sizeof(bmpinfo) + bufSize;
bmphdr.bfOffBits = sizeof(bmphdr) + sizeof(bmpinfo);
bmpinfo.biSize = sizeof(bmpinfo);
bmpinfo.biWidth = 640;
bmpinfo.biHeight = 480;
bmpinfo.biPlanes = 1;
bmpinfo.biBitCount = 32;
fh = CreateFile(TEXT("C:\\Users\\mike\\Desktop\\output.bmp"),
GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
WriteFile(fh, &bmphdr, sizeof(bmphdr), &nWritten, NULL);
WriteFile(fh, &bmpinfo, sizeof(bmpinfo), &nWritten, NULL);
WriteFile(fh, lpCurrImage, bufSize, &nWritten, NULL);
CloseHandle(fh);
ErrMsg(TEXT("Captured current image to %s"), szFile);
return TRUE;
}
else
{
ErrMsg(TEXT("Failed to capture image! hr=0x%x"), hr);
return FALSE;
}
}
return FALSE;
The problem is that when I am trying to run the app, I receive an HRESULT (0x8000ffff) error when the GetCurrentImage function is being called.
On the other hand in case I execute the app through the VS debugger the code works just fine.
I tried to add a Sleep just after the stream pMC->Pause(), assuming that the problem was timing issue but that did not work.
Any ideas would be extremely helpful!
Thank you in advance.
I'm using Media Foundation IMFSourceReaderCallback implementation for grabbing video frames from the camera, and then OpenCV imshow to present the frames in a loop.
However I get the frames vertically flipped...
Is this a bug? Should I set some attribute to avoid this?
Here is my code:
Initialization:
IMFAttributes* pDeviceAttrs, *pReaderAttrs;
hr = MFCreateAttributes(&pDeviceAttrs, 1);
if (FAILED(hr)) goto Exit;
hr = pDeviceAttrs->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
if (FAILED(hr)) goto Exit;
//...
// Correct source provider is activated through ActivateObject
//
hr = MFCreateAttributes(&pReaderAttrs, 2);
if (FAILED(hr)) goto Exit;
pReaderAttrs->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK,(IUnknown*)this);
pReaderAttrs->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, TRUE);
hr = MFCreateSourceReaderFromMediaSource(pMediaSource, pReaderAttrs, &m_pReader);
if (FAILED(hr)) goto Exit;
// Correct profile is set
OnReadSample implementation:
HRESULT hr = S_OK;
LONG defaultStride = 0;
LONG stride = 0;
BYTE *pBuffer = NULL;
EnterCriticalSection(&m_critSec);
if (NULL != pSample)
{
IMFMediaBuffer* pMediaBuffer;
DWORD dataSize = 0;
// In case of a single buffer, no copy would happen
hr = pSample->ConvertToContiguousBuffer(&pMediaBuffer);
if (FAILED(hr)) goto Cleanup;
pMediaBuffer->GetCurrentLength(&dataSize);
hr = pMediaBuffer->Lock(&pBuffer, &dataSize, &dataSize);
if (FAILED(hr)) goto Cleanup;
// todo: use a backbuffer to avoid sync issues
if (NULL == m_pLatestFrame) m_pLatestFrame = (BYTE*)malloc(dataSize);
memcpy(m_pLatestFrame, pBuffer, dataSize);
++m_frameNumber;
pMediaBuffer->Unlock();
pMediaBuffer->Release();
}
Cleanup:
LeaveCriticalSection(&m_critSec);
// Async ReadFrame for the next buffer:
hr = m_pReader->ReadSample(
(DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM,
0,
NULL, // actual
NULL, // flags
NULL, // timestamp
NULL // sample
);
return hr;
Conversion to cv::image:
void SourceReaderImpl::GetLatestFrame(BYTE** ppLatestFrame)
{
EnterCriticalSection(&m_critSec);
*ppLatestFrame = m_pLatestFrame;
LeaveCriticalSection(&m_critSec);
}
void* CameraWrapperImpl::getLatestFrame()
{
BYTE* pLatestFrame = NULL;
m_pMfReader->GetLatestFrame(&pLatestFrame);
return pLatestFrame;
}
void Player::Present()
{
//...
color = cv::Mat(colorSize,
CV_8UC3,
static_cast<unsigned char*>(m_pColorCameraImpl->getLatestFrame()));
cv::imshow(color);
}
Any idea?
Thanks in advance!
A bitmap is stored with the last scan line first, so the image will appear upside down. The easiest solution is to call cv::flip
void Player::Present()
{
//...
color = cv::Mat(colorSize,
CV_8UC3,
static_cast<unsigned char*>(m_pColorCameraImpl->getLatestFrame()));
cv::Mat corrected;
flip(color, corrected, 0);
imshow(corrected);
}