How to save HBITMAP as JPG using Win32 Imaging API (Windows Mobile 6+)? - c++

I have created procedures to save window screenshot to file. It works for PNG and BMP, but not for JPG (and GIF).
Here is code for capturing HBITMAP:
HBITMAP Signature::getScreenHBITMAP() {
// get screen rectangle
RECT windowRect;
GetWindowRect(getMainWnd(), &windowRect);
// bitmap dimensions
int bitmap_dx = windowRect.right - windowRect.left;
int bitmap_dy = windowRect.bottom - windowRect.top;
// create bitmap info header
BITMAPINFOHEADER infoHeader;
infoHeader.biSize = sizeof(infoHeader);
infoHeader.biWidth = bitmap_dx;
infoHeader.biHeight = bitmap_dy;
infoHeader.biPlanes = 1;
infoHeader.biBitCount = 24;
infoHeader.biCompression = BI_RGB;
infoHeader.biSizeImage = 0;
infoHeader.biXPelsPerMeter = 0;
infoHeader.biYPelsPerMeter = 0;
infoHeader.biClrUsed = 0;
infoHeader.biClrImportant = 0;
// dibsection information
BITMAPINFO info;
info.bmiHeader = infoHeader;
HDC winDC = GetWindowDC(getMainWnd());
HDC memDC = CreateCompatibleDC(winDC);
BYTE* memory = 0;
HBITMAP bitmap = CreateDIBSection(winDC, &info, DIB_RGB_COLORS, (void**)&memory, 0, 0);
SelectObject(memDC, bitmap);
// Copies screen upside down (as it is already upside down) - if need normal layout, change to BitBlt function call
StretchBlt(memDC, 0, 0, bitmap_dx, bitmap_dy, winDC, 0, bitmap_dy, bitmap_dx, bitmap_dy * -1, SRCCOPY);
DeleteDC(memDC);
ReleaseDC(getMainWnd(), winDC);
return bitmap;
}
And here is code for image saving:
HRESULT Imaging_SaveToFile(HBITMAP handle, LPTSTR filename, LPCTSTR format){
HRESULT res;
res = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if ((res == S_OK) || (res == S_FALSE)) {
IImagingFactory* factory=NULL;
if (CoCreateInstance(CLSID_ImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_IImagingFactory, (void**)&factory) == S_OK) {
UINT count;
ImageCodecInfo* imageCodecInfo=NULL;
if (factory->GetInstalledEncoders(&count, &imageCodecInfo) == S_OK) {
// Get the particular encoder to use
LPTSTR formatString;
if (wcscmp(format, L"png") == 0) {
formatString = _T("image/png");
} else if (wcscmp(format, L"jpg") == 0) {
formatString = _T("image/jpeg");
} else if (wcscmp(format, L"gif") == 0) {
formatString = _T("image/gif");
} else if (wcscmp(format, L"bmp") == 0) {
formatString = _T("image/bmp");
} else {
CoUninitialize();
return S_FALSE;
}
CLSID encoderClassId;
if (count == 0) {
CoUninitialize();
return S_FALSE;
}
for(int i=0; i < (int)count; i++) {
if (wcscmp(imageCodecInfo[i].MimeType, formatString) == 0) {
encoderClassId= imageCodecInfo[i].Clsid;
free(imageCodecInfo);
break;
} else {
continue;
}
CoUninitialize();
return S_FALSE;
}
IImageEncoder* imageEncoder=NULL;
if (factory->CreateImageEncoderToFile(&encoderClassId, filename, &imageEncoder) == S_OK) {
IImageSink* imageSink = NULL;
res = imageEncoder->GetEncodeSink(&imageSink);
if (res != S_OK) {
CoUninitialize();
return res;
}
BITMAP bm;
GetObject (handle, sizeof(BITMAP), &bm);
ImageInfo* imageInfo = new ImageInfo();
imageInfo->Width = bm.bmWidth;
imageInfo->Height = bm.bmHeight;
imageInfo->RawDataFormat = IMGFMT_MEMORYBMP; //ImageFormatMemoryBMP;
imageInfo->Flags |= SinkFlagsTopDown | SinkFlagsFullWidth;
// Get pixel format from hBitmap
PixelFormatID pixelFormat;
int numColors = 0;
switch (bm.bmBitsPixel) {
case 1: {
pixelFormat = PixelFormat1bppIndexed;
numColors = 1;
break;
}
case 4: {
pixelFormat = PixelFormat4bppIndexed;
numColors = 16;
break;
}
case 8: {
pixelFormat = PixelFormat8bppIndexed;
numColors = 256;
break;
}
case 24: {
pixelFormat = PixelFormat24bppRGB;
break;
}
default: {
pixelFormat = PixelFormat32bppARGB;
numColors = 3; // according to MSDN 16 and 32 bpp numColors should be 3
break;
}
}
imageInfo->PixelFormat = pixelFormat;
if (pixelFormat == PixelFormat32bppARGB) imageInfo->Flags |= SinkFlagsHasAlpha;
res = imageSink->BeginSink(imageInfo, NULL);
if (res != S_OK) {
CoUninitialize();
return res;
}
ColorPalette* palette = NULL;
if (numColors > 0) {
palette = (ColorPalette*)malloc(sizeof(ColorPalette) + (numColors - 1) * sizeof(ARGB));
palette->Count = numColors;
for (int i=0; i<numColors; i++) {
int rgb = i*64;
int red = rgb & 0x00FF;
int green = (rgb >> 8) & 0x00FF;
int blue = (rgb >> 16) & 0x00FF;
palette->Entries[i] = MAKEARGB(0, red, green, blue);
}
} else {
palette = (ColorPalette*)malloc(sizeof(ColorPalette));
palette->Count = 0;
if (pixelFormat == PixelFormat32bppARGB) palette->Flags = PALFLAG_HASALPHA;
}
res = imageSink->SetPalette(palette);
if (res != S_OK) {
CoUninitialize();
return res;
}
BitmapData* bmData = new BitmapData();
bmData->Height = bm.bmHeight;
bmData->Width = bm.bmWidth;
bmData->Scan0 = bm.bmBits;
bmData->PixelFormat = pixelFormat;
UINT bitsPerLine = imageInfo->Width * bm.bmBitsPixel;
UINT bitAlignment = sizeof(LONG) * 8;
UINT bitStride = bitAlignment * (bitsPerLine / bitAlignment); // The image buffer is always padded to LONG boundaries
if ((bitsPerLine % bitAlignment) != 0) bitStride += bitAlignment; // Add a bit more for the leftover values
bmData->Stride = (bitStride / 8);
RECT rect;
rect.top = 0;
rect.bottom = bm.bmHeight;
rect.left = 0;
rect.right = bm.bmWidth;
res = imageSink->PushPixelData(&rect, bmData, TRUE);
if (res != S_OK) {
CoUninitialize();
return res;
}
res = imageSink->EndSink(S_OK);
if (res != S_OK) {
CoUninitialize();
return res;
}
imageSink->Release();
res = imageEncoder->TerminateEncoder();
if (res != S_OK) {
CoUninitialize();
return res;
}
}
}
}
CoUninitialize();
} else {
return res;
}
return res;
}
I used code from Koders.com as an example and tried to follow MSDN description of image encoding when modified this example.
Found also that others have similar issue, but with no answer:
social.msdn.microsoft.com/Forums/en-US/windowsmobiledev/thread/1c368cc1-cc5b-419e-a7d2-2a39c90ae83d/
groups.google.com/group/microsoft.public.windowsce.embedded.vc/browse_thread/thread/8cd67e16ac29627b/9242e82721c48ace?hl=hu&pli=1
I also found solution which uses GDI+ wrapper:
www.ernzo.com/LibGdiplus.aspx
www.codeproject.com/KB/windows/gdiplusandwinmobile.aspx
But I cannot use this GDI+ lib. Also I do not need whole GDI+. Tried to create similar saving procedure like in this wrapper, but with no success.
EDIT
Here is fixed and working solution (Thanks PhilMY for answer):
HRESULT Imaging_SaveToFile(HBITMAP handle, LPTSTR filename, LPCTSTR format){
HRESULT res;
res = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if ((res == S_OK) || (res == S_FALSE)) {
IImagingFactory* factory=NULL;
if (CoCreateInstance(CLSID_ImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_IImagingFactory, (void**)&factory) == S_OK) {
UINT count;
ImageCodecInfo* imageCodecInfo=NULL;
if (factory->GetInstalledEncoders(&count, &imageCodecInfo) == S_OK) {
// Get the particular encoder to use
LPTSTR formatString;
if (wcscmp(format, L"png") == 0) {
formatString = _T("image/png");
} else if (wcscmp(format, L"jpg") == 0) {
formatString = _T("image/jpeg");
} else if (wcscmp(format, L"gif") == 0) {
formatString = _T("image/gif");
} else if (wcscmp(format, L"bmp") == 0) {
formatString = _T("image/bmp");
} else {
CoUninitialize();
return S_FALSE;
}
CLSID encoderClassId;
if (count == 0) {
CoUninitialize();
return S_FALSE;
}
for(int i=0; i < (int)count; i++) {
if (wcscmp(imageCodecInfo[i].MimeType, formatString) == 0) {
encoderClassId= imageCodecInfo[i].Clsid;
free(imageCodecInfo);
break;
} else {
continue;
}
CoUninitialize();
return S_FALSE;
}
IImageEncoder* imageEncoder=NULL;
if (factory->CreateImageEncoderToFile(&encoderClassId, filename, &imageEncoder) == S_OK) {
IImageSink* imageSink = NULL;
res = imageEncoder->GetEncodeSink(&imageSink);
if (res != S_OK) {
CoUninitialize();
return res;
}
BITMAP bm;
GetObject (handle, sizeof(BITMAP), &bm);
PixelFormatID pixelFormat;
switch (bm.bmBitsPixel) {
case 1: {
pixelFormat = PixelFormat1bppIndexed;
break;
}
case 4: {
pixelFormat = PixelFormat4bppIndexed;
break;
}
case 8: {
pixelFormat = PixelFormat8bppIndexed;
break;
}
case 24: {
pixelFormat = PixelFormat24bppRGB;
break;
}
default: {
pixelFormat = PixelFormat32bppARGB;
break;
}
}
BitmapData* bmData = new BitmapData();
bmData->Height = bm.bmHeight;
bmData->Width = bm.bmWidth;
bmData->Scan0 = bm.bmBits;
bmData->PixelFormat = pixelFormat;
UINT bitsPerLine = bm.bmWidth * bm.bmBitsPixel;
UINT bitAlignment = sizeof(LONG) * 8;
UINT bitStride = bitAlignment * (bitsPerLine / bitAlignment); // The image buffer is always padded to LONG boundaries
if ((bitsPerLine % bitAlignment) != 0) bitStride += bitAlignment; // Add a bit more for the leftover values
bmData->Stride = (bitStride / 8);
IBitmapImage* pBitmap;
factory->CreateBitmapFromBuffer(bmData, &pBitmap);
IImage* pImage;
pBitmap->QueryInterface(IID_IImage, (void**)&pImage);
res = pImage->PushIntoSink(imageSink);
if (res != S_OK) {
CoUninitialize();
return res;
}
pBitmap->Release();
pImage->Release();
imageSink->Release();
imageEncoder->TerminateEncoder();
imageEncoder->Release();
}
}
}
CoUninitialize();
} else {
return res;
}
return res;
}

I have some similar code that worked for JPEGs on WinCE 6.0.
The main differences are:
I check ImageCodecInfo::FormatID against ImageFormatJPEG to match an encoder
I call SetEncoderParameters to set ENCODER_QUALITY
I copy the source bitmap into an IBitmapImage then use IImage::PushIntoSink

Related

C++ Win32Api GUI, I Am trying to display received screenshots on server from client. I am trying to create a Remote Desktop Viewer

I am receiving those screenshots in WM_PAINT, and then save it to memory. The screenshots have around 8-10kB size. And I am trying to display those screenshots in a 15 FPS, but the problem is that its displaying sometimes glitched screenshots like this:
[the gray square with dots inside it, that should not be there, its covering the half of the screenshot :c ]
Image displayed like this, is like every second or third screenshot. I have no idea how to fix this im stuck a couple days at this.
Here is the server code (window procedure) where i am trying to display these screenshots
LRESULT CALLBACK ClientWndProc(HWND Chwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
std::vector<char> buffer(50000);
switch (uMsg)
{
case WM_CREATE:
{
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
SetTimer(Chwnd, SCREEN_TIMER, 1000/15, NULL);
break;
}
case WM_TIMER:
{
if (wParam == SCREEN_TIMER)
{
SOCKET selectedSocket = clientSockets[itemIndex];
auto it = std::find(clientSockets.begin(), clientSockets.end(), selectedSocket);
send(selectedSocket, "capscr", strlen("capscr"), 0);
InvalidateRect(Chwnd, NULL, TRUE);
}
break;
}
case WM_PAINT:
{
SOCKET selectedSocket = clientSockets[itemIndex];
auto it = std::find(clientSockets.begin(), clientSockets.end(), selectedSocket);
if (it != clientSockets.end())
{
int bytesReceived = 0;
int totalBytesReceived = 0;
int expectedBytes = buffer.size();
Sleep(1000/15);
do
{
bytesReceived = recv(selectedSocket, buffer.data() + totalBytesReceived, buffer.size() - totalBytesReceived, 0);
totalBytesReceived += bytesReceived;
} while (totalBytesReceived < expectedBytes && bytesReceived > 0);
//MessageBoxA(NULL, buffer.data(), "s", MB_OK);
HGLOBAL hGlobal = GlobalAlloc(GHND, expectedBytes);
void* pData = GlobalLock(hGlobal);
memcpy(pData, buffer.data(), buffer.size());
GlobalUnlock(hGlobal);
IStream* pStream = NULL;
CreateStreamOnHGlobal(hGlobal, TRUE, &pStream);
Gdiplus::Bitmap bitmap(pStream);
Gdiplus::Status status = bitmap.GetLastStatus();
PAINTSTRUCT ps;
HDC hdc = BeginPaint(Chwnd, &ps);
int imgWidth = bitmap.GetWidth();
int imgHeight = bitmap.GetHeight();
Gdiplus::Graphics graphics(hdc);
RECT clientRect;
GetClientRect(Chwnd, &clientRect);
graphics.DrawImage(&bitmap, 0, 0, imgWidth, imgHeight);
EndPaint(Chwnd, &ps);
GlobalFree(hGlobal);
}
break;
}
case WM_ERASEBKGND:
return TRUE;
case WM_CLOSE:
{
DestroyWindow(hwndClient);
break;
}
case WM_DESTROY:
DestroyWindow(hwndClient);
return 0;
default:
return DefWindowProc(Chwnd, uMsg, wParam, lParam);
}
return 0;
}
client code sending screenshots:
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
UINT num = 0; // number of image encoders
UINT size = 0; // size of the image encoder array in bytes
ImageCodecInfo* pImageCodecInfo = NULL;
GetImageEncodersSize(&num, &size);
if (size == 0)
return -1; // Failure
pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
if (pImageCodecInfo == NULL)
return -1; // Failure
GetImageEncoders(num, size, pImageCodecInfo);
for (UINT j = 0; j < num; ++j)
{
if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0)
{
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j; // Success
}
}
free(pImageCodecInfo);
return 0;
}
void shutdownGdiPlus()
{
Gdiplus::GdiplusShutdown(gdiPlusToken);
gdiPlusToken = NULL;
}
bool initGdiPlusIfNeeded()
{
// If already initialized then return true
if (gdiPlusToken != NULL)
return true;
static Gdiplus::GdiplusStartupInput gdiPlusStartupInput;
return (success = GdiplusStartup(&gdiPlusToken, &gdiPlusStartupInput, NULL)) == Gdiplus::Status::Ok;
}
std::pair<ULONGLONG, char*> getScreenShotAsByteArray()
{
if (!initGdiPlusIfNeeded())
return {};
IStream* iStream;
HRESULT res = CreateStreamOnHGlobal(NULL, true, &iStream);
const HDC srcDC = ::GetDC(NULL);
const int screenHeight = GetSystemMetrics(SM_CYSCREEN);
const int screenWidth = GetSystemMetrics(SM_CXSCREEN);
const HDC memDC = CreateCompatibleDC(srcDC);
const HBITMAP membit = CreateCompatibleBitmap(srcDC, screenWidth, screenHeight);
HBITMAP hOldBitmap = (HBITMAP)SelectObject(memDC, membit);
BitBlt(memDC, 0, 0, screenWidth, screenHeight, srcDC, 0, 0, SRCCOPY);
// Create a bitmap to store the previous screenshot
HBITMAP prevBitmap = CreateCompatibleBitmap(srcDC, screenWidth, screenHeight);
HDC prevDC = CreateCompatibleDC(srcDC);
SelectObject(prevDC, prevBitmap);
// Get the size of the bitmaps in bytes
BITMAP bmp;
GetObject(prevBitmap, sizeof(BITMAP), &bmp);
int prevBmpSize = bmp.bmWidth * bmp.bmHeight * (bmp.bmBitsPixel / 8);
GetObject(membit, sizeof(BITMAP), &bmp);
int currBmpSize = bmp.bmWidth * bmp.bmHeight * (bmp.bmBitsPixel / 8);
// Allocate memory for the bitmap data
char* prevBmpData = new char[prevBmpSize];
char* currBmpData = new char[currBmpSize];
// Get the raw pixel data of the bitmaps
GetBitmapBits(prevBitmap, prevBmpSize, prevBmpData);
GetBitmapBits(membit, currBmpSize, currBmpData);
// Compare the bitmap data
bool isDifferent = memcmp(prevBmpData, currBmpData, currBmpSize) != 0;
// Free the allocated memory
delete[] prevBmpData;
delete[] currBmpData;
// Check if the current screenshot is different from the previous screenshot
if (isDifferent)
{
// Screenshot is different, take a new screenshot
int newScreenWidth = 700;
int newScreenHeight = 500;
Gdiplus::Bitmap fullScreenBitmap(membit, NULL);
Gdiplus::Bitmap newBitmap(newScreenWidth, newScreenHeight);
Gdiplus::Graphics graphics(&newBitmap);
graphics.DrawImage(&fullScreenBitmap, 0, 0, newScreenWidth, newScreenHeight);
CLSID clsid;
GetEncoderClsid(L"image/jpeg", &clsid);
ULONG quality = 0;
EncoderParameters encoderParams;
encoderParams.Count = 1;
encoderParams.Parameter[0].Guid = EncoderQuality;
encoderParams.Parameter[0].Type = EncoderParameterValueTypeLong;
encoderParams.Parameter[0].NumberOfValues = 1;
encoderParams.Parameter[0].Value = &quality;
newBitmap.Save(iStream, &clsid, &encoderParams);
ULARGE_INTEGER pos{ 0, 0 };
const LARGE_INTEGER pos2{ 0, 0 };
iStream->Seek(pos2, STREAM_SEEK_SET, &pos);
ULONG bytesWritten = 0;
STATSTG statstg;
iStream->Stat(&statstg, STATFLAG_DEFAULT);
const ULONGLONG bufferLen = statstg.cbSize.QuadPart;
char* imageBuffer = new char[bufferLen];
iStream->Read(imageBuffer, bufferLen, &bytesWritten);
iStream->Release();
DeleteObject(memDC);
DeleteObject(membit);
::ReleaseDC(NULL, srcDC);
std::pair<ULONGLONG, char*> result(bufferLen, imageBuffer);
return result;
}
else
{
return {};
}
}
void sendScreens() {
if (clientsocket == INVALID_SOCKET) {
// handle error, the socket is not valid or not connected
return;
}
std::pair<ULONGLONG, char*> image = getScreenShotAsByteArray();
ULONGLONG bufferLen = image.first;
char* imageBuffer = image.second;
int bytesSent = send(clientsocket, imageBuffer, bufferLen, 0);
if(bytesSent < 0)
{
return;
}
std::cout << "Sent Bytes: " << bytesSent << "\n";
// wait for the desired interval before sending the next screenshot
std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();
std::chrono::high_resolution_clock::time_point end = start + std::chrono::milliseconds(FPS);
std::this_thread::sleep_until(end);
}

How to decode a picture converted to base64 using CryptStringToBinary?

My doubt is about how to use the value returned by the API to reconstruct the image.
Also, does the way I'm creating the Bitmap preserve the picture transparency?
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
LPCWSTR base64 = L"";
DWORD dwSkip;
DWORD dwFlags;
DWORD dwDataLen;
CryptStringToBinary(
base64,
_tcslen(base64),
CRYPT_STRING_BASE64,
NULL,
&dwDataLen,
&dwSkip,
&dwFlags);
DWORD imageSize = dwDataLen;
HGLOBAL hMem = ::GlobalAlloc(GMEM_MOVEABLE, imageSize);
LPVOID pImage = ::GlobalLock(hMem);
memcpy(pImage, ???? , imageSize);
IStream* pStream = NULL;
::CreateStreamOnHGlobal(hMem, FALSE, &pStream);
Gdiplus::Image image(pStream);
image.GetWidth();
int wd = image.GetWidth();
int hgt = image.GetHeight();
auto format = image.GetPixelFormat();
Bitmap* bmp = new Bitmap(wd, hgt, format);
auto gg = std::unique_ptr<Graphics>(Graphics::FromImage(bmp));
gg->Clear(Color::Transparent);
gg->DrawImage(&image, 0, 0, wd, hgt);
HICON hIcon;
bmp->GetHICON(&hIcon);
pStream->Release();
GlobalUnlock(hMem);
GlobalFree(hMem);
wc.hIcon = hIcon;
My doubt is about how to use the value returned by the API to reconstruct the image.
You are calling CryptStringToBinary() only 1 time, to calculate the size of the decoded bytes. You are even allocating memory to receive the decoded bytes. But, you are not actually decoding the base64 string to produce the bytes. You need to call CryptStringToBinary() a second time for that, eg:
LPCWSTR base64 = L"...";
DWORD dwStrLen = static_cast<DWORD>(wcslen(base64)); // or: 0
DWORD dwDataLen = 0;
if (!CryptStringToBinaryW(
base64,
dwStrLen,
CRYPT_STRING_BASE64,
NULL,
&dwDataLen,
NULL,
NULL))
{
// error handling...
}
HGLOBAL hMem = ::GlobalAlloc(GMEM_MOVEABLE, dwDataLen);
if (!hMem)
{
// error handling...
}
LPVOID pImage = ::GlobalLock(hMem);
if (!CryptStringToBinaryW(
base64,
dwStrLen,
CRYPT_STRING_BASE64,
(BYTE*) pImage,
&dwDataLen,
NULL,
NULL))
{
// error handling...
}
::GlobalUnlock(hMem);
// use hMem as needed...
::GlobalFree(hMem);
we can use CreateIconFromResourceEx instead gdi++ . example of convert base64 to HICON (assumed that wholea icon file is converted to base64)
struct ICONDIRENTRY
{
BYTE bWidth; // Width of the image
BYTE bHeight; // Height of the image (times 2)
BYTE bColorCount; // Number of colors in image (0 if >=8bpp)
BYTE bReserved; // Reserved
WORD wPlanes; // Color Planes
WORD wBitCount; // Bits per pixel
DWORD dwBytesInRes; // how many bytes in this resource?
DWORD dwImageOffset; // where in the file is this image
};
struct ICONDIR
{
WORD NotInFile; // not in file
WORD Reserved; // Reserved
WORD Type; // resource type (IMAGE_ICON for icons)
WORD Count; // how many images?
ICONDIRENTRY Entries[]; // the entries for each image
};
HICON LoadIcon(_In_ ICONDIR* pid, _In_ ULONG cb, _In_ ULONG cxDesired, _In_ ULONG cyDesired)
{
if (cb < sizeof(ICONDIR) || pid->Type != IMAGE_ICON)
{
return 0;
}
if (ULONG Count = pid->Count)
{
if (cb < sizeof(ICONDIR) + Count * sizeof(ICONDIRENTRY))
{
return FALSE;
}
ICONDIRENTRY* Entry = pid->Entries;
do
{
if (Entry->bWidth == cxDesired && Entry->bHeight == cyDesired)
{
if (Entry->wPlanes != 1)
{
return 0;
}
switch (Entry->bColorCount)
{
case 0:
case 1:
case 4:
case 8:
case 16:
case 24:
case 32:
break;
default:
return 0;
}
DWORD dwImageOffset = Entry->dwImageOffset + FIELD_OFFSET(ICONDIR, Reserved);
if (cb < dwImageOffset)
{
return 0;
}
DWORD dwBytesInRes = Entry->dwBytesInRes;
if (cb - dwImageOffset < dwBytesInRes)
{
return 0;
}
return CreateIconFromResourceEx((PBYTE)pid + dwImageOffset, dwBytesInRes, TRUE, 0x00030000, cxDesired, cyDesired, 0);
}
} while (Entry++, --Count);
}
return 0;
}
HICON LoadIcon(_In_ PSTR psz, _In_ ULONG cch, _In_ int cxDesired, _In_ int cyDesired)
{
ULONG cb = 0;
PUCHAR pb = 0;
PUCHAR buf = 0;
HICON hi = 0;
while (CryptStringToBinaryA(psz, cch, CRYPT_STRING_BASE64, pb, &cb, 0, 0))
{
if (pb)
{
hi = LoadIcon((ICONDIR*)buf, cb + FIELD_OFFSET(ICONDIR, Reserved), cxDesired, cyDesired);
break;
}
if (cb < sizeof(ICONDIR))
{
break;
}
if (!(buf = new UCHAR[cb + FIELD_OFFSET(ICONDIR, Reserved)]))
{
break;
}
pb = buf + FIELD_OFFSET(ICONDIR, Reserved);
}
if (buf) delete [] buf;
return hi;
}
for convert icon file to base 64 can use next code:
BOOL IsValidIcon(_In_ ICONDIR* pid, _In_ ULONG cb)
{
if (cb < sizeof(ICONDIR) || pid->Type != IMAGE_ICON)
{
return FALSE;
}
if (ULONG Count = pid->Count)
{
if (cb < sizeof(ICONDIR) + Count * sizeof(ICONDIRENTRY))
{
return FALSE;
}
ICONDIRENTRY* Entry = pid->Entries;
do
{
if (Entry->wPlanes != 1)
{
return FALSE;
}
switch (Entry->bColorCount)
{
case 0:
case 1:
case 4:
case 8:
case 16:
case 24:
case 32:
break;
default:
return FALSE;
}
DWORD dwImageOffset = Entry->dwImageOffset + FIELD_OFFSET(ICONDIR, Reserved);
if (cb < dwImageOffset)
{
return FALSE;
}
DWORD dwBytesInRes = Entry->dwBytesInRes;
if (cb - dwImageOffset < dwBytesInRes)
{
return FALSE;
}
} while (Entry++, --Count);
}
return FALSE;
}
BOOL mi(_In_ PCWSTR lpIconName, _Out_ PSTR* ppsz, _Out_ ULONG* pcch)
{
HANDLE hFile = CreateFileW(lpIconName, FILE_GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
if (hFile == INVALID_HANDLE_VALUE)
{
return FALSE;
}
BOOL f = FALSE;
FILE_STANDARD_INFO fsi;
if (GetFileInformationByHandleEx(hFile, FileStandardInfo, &fsi, sizeof(fsi)))
{
if ((ULONG64)fsi.EndOfFile.QuadPart - 1 < 0x100000)
{
if (PBYTE buf = new UCHAR[fsi.EndOfFile.LowPart + FIELD_OFFSET(ICONDIR, Reserved)])
{
PBYTE pb = buf + FIELD_OFFSET(ICONDIR, Reserved);
if (ReadFile(hFile, pb, fsi.EndOfFile.LowPart, &fsi.EndOfFile.LowPart, 0) &&
IsValidIcon((ICONDIR*)buf, fsi.EndOfFile.LowPart) + FIELD_OFFSET(ICONDIR, Reserved))
{
ULONG cch = 0;
PSTR psz = 0;
while (CryptBinaryToStringA(pb, fsi.EndOfFile.LowPart, CRYPT_STRING_BASE64|CRYPT_STRING_NOCRLF, psz, &cch))
{
if (psz)
{
*pcch = cch, *ppsz = psz, psz = 0, f = TRUE;
break;
}
if (!(psz = new CHAR[cch]))
{
break;
}
}
if (psz)
{
delete [] psz;
}
}
delete [] buf;
}
}
}
CloseHandle(hFile);
return f;
}
for test icon:
ULONG cch;
PSTR psz;
if (mi(L"...ico", &psz, &cch))
{
HICON hi = LoadIcon(psz, cch, 48, 48);
delete [] psz;
if (hi)
{
TASKDIALOGCONFIG TaskConfig = {
sizeof(TaskConfig), 0, 0, TDF_USE_HICON_MAIN, TDCBF_CLOSE_BUTTON, 0, {hi}
};
TaskDialogIndirect(&TaskConfig, 0, 0, 0);
DestroyIcon(hi);
}
}

Some files send on a HLS Server using Http Put request are partially uploaded

I have to upload audio chunks continuously on a HLS Server as a part of my project. I am able to upload chunks sucessfully using HTTP Put method with wininet API in C++ using following code.
bool CHTTP::HttpPut(char *szFile,int fileType)
{
bool bErrorFlag = false;
if(m_hInternet == NULL)
{
int retStatus = OpenHTTPSession();
if(retStatus < 1)
{
return true;
}
}
char szPostURL[256];
INTERNET_BUFFERS BufferIn = {0};
DWORD dwBytesRead;
DWORD dwBytesWritten;
BYTE pBuffer[350000];
BOOL bRead, bRet;
static int flag = 1;
BufferIn.dwStructSize = sizeof( INTERNET_BUFFERS );
char szLocalFilePath[256];
if(fileType == AUDIO_CHUNK)
sprintf(szLocalFilePath,"%s/%s",m_strFilePath,szFile);
else
strcpy(szLocalFilePath,szFile);
int iFileSize = 0;
if(fileType == AUDIO_CHUNK)
{
strcpy(szPostURL,m_strPostPath);
strcat(szPostURL,"/");
strcat(szPostURL,szFile);
}
else if(fileType == M3U8)
strcpy(szPostURL,m_szM3U8FileToPost);
else if(fileType == AUTO_M3U8)
strcpy(szPostURL,m_szM3U8AutoPost);
DWORD dwFlags =
INTERNET_FLAG_KEEP_CONNECTION |
INTERNET_FLAG_NO_COOKIES |
INTERNET_FLAG_NO_CACHE_WRITE |
INTERNET_FLAG_NO_UI |
INTERNET_FLAG_RELOAD |INTERNET_FLAG_SECURE;
m_hRequest = HttpOpenRequest(m_hHttpSession, (const char*)"PUT",szPostURL, "HTTP/1.1",NULL , (const char**)"*/*\0",dwFlags, 1);
if(m_hRequest==NULL)
{
bErrorFlag = true;
CloseHTTPSession();
return bErrorFlag;
}
else
{
bErrorFlag = false;
}
int num_of_try = 0;
while(num_of_try < 3)
{
char logDump[1000];
num_of_try++;
HANDLE hFile = CreateFile (szLocalFilePath, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
bErrorFlag = true;
break;
}
else if(!m_bLogFlagCreateErr)
{
m_bLogFlagCreateErr = true;
sprintf(logDump,"CreateFile success %s",szFile);
WriteLog(mLogFile,logDump);
bErrorFlag = false;
}
BufferIn.dwBufferTotal = GetFileSize (hFile, NULL);
iFileSize = BufferIn.dwBufferTotal;
if(!HttpSendRequestEx( m_hRequest, &BufferIn, NULL, HSR_INITIATE, 0))
{
bErrorFlag = true;
m_bLogFlagSend = false;
sprintf(logDump,"Error on HttpSendRequestEx %lu %s",GetLastError(),szFile);
WriteLog(mLogFile,logDump);
break;
}
else
{
bErrorFlag = false;
sprintf(logDump,"HttpSendRequest success %s",szFile);
WriteLog(mLogFile,logDump);
}
DWORD sum = 0;
int size = 0;
do
{
bRead = ReadFile (hFile, pBuffer,iFileSize,
&dwBytesRead, NULL);
if(dwBytesRead != iFileSize)
{
sprintf(logDump,"dwBytesRead %d iFileSize %d %s",dwBytesRead,iFileSize,szFile);
WriteLog(mLogFile,logDump);
}
if(dwBytesRead > 0)
{
bRet=InternetWriteFile( m_hRequest, pBuffer, dwBytesRead,
&dwBytesWritten);
while(dwBytesRead < dwBytesWritten && bRet)
{
sprintf(logDump,"dwBytesRead %d dwBytesWritten %d %s",dwBytesRead,dwBytesWritten,szFile);
WriteLog(mLogFile,logDump);
bRet=InternetWriteFile( m_hRequest, pBuffer+dwBytesWritten, dwBytesRead - dwBytesWritten ,&dwBytesWritten);
}
if(!bRet)
{
int error = GetLastError();
sprintf(logDump,"InternetWriteFile %lu %s",error,szFile);
WriteLog(mLogFile,logDump);
bErrorFlag = true;
break;
}
else
{
sprintf(logDump,"InternetWriteFile buffer success %s",szFile);
WriteLog(mLogFile,logDump);
bErrorFlag = false;
}
}
}
while (dwBytesRead == iFileSize);
CloseHandle (hFile);
if(!HttpEndRequest(m_hRequest, NULL, 0, 0))
{
int error = GetLastError();
if(error != 12032)
{
sprintf(logDump,"HttpEndRequest %lu %s",error,szFile);
WriteLog(mLogFile,logDump)
bErrorFlag = true;
break;
}
else
{
bErrorFlag = true;
continue;
}
}
else
{
sprintf(logDump,"HttpEndRequest success %s",szFile);
WriteLog(mLogFile,logDump);
DWORD dwCode, dwCodeSize;
dwCodeSize = sizeof(DWORD);
if(!HttpQueryInfo(m_hRequest, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &dwCode, &dwCodeSize, NULL))
{
int error = GetLastError();
char tmp[256];
sprintf(tmp,"HttpQueryfails %d %s",error, szFile);
WriteLog(mLogFile,tmp);
}
else
{
if(dwCode != 201 && dwCode != 204)
{
char tmp[256];
sprintf(tmp,"dwcode %d is not 201 or 204 %s",dwCode,szFile);
WriteLog(mLogFile,tmp);
bErrorFlag = true;
break;
}
}
bErrorFlag = false;
break;
}
}
CloseHTTPSession();
return bErrorFlag;
}
Most of the times chunks uploaded are of full size. Randomly chunks uploaded on server are not of full size as shown in following image.
sample image
In such case I am not getting any error messages, even dwBytesWritten returned by InternetWriteFile function is also correct. I am unable to understand what should I do to solve it. Any help in this regard is appreciated.

Allow selection of video format from media foundation capture device

I have taken the windows CaptureManager code ( https://learn.microsoft.com/en-us/windows/win32/medfound/simplecapture-sample ) as a starting point and am trying to allow selection of the available video formats from the capture device. If I select the first item (default format) the preview works fine, but if I select one of the other formats I get either very fuzzy video or no preview. I can both preview and capture from the default index.
I can enumerate the available video formats. I am restricting my selections to only the FORMAT_VideoInfo2 types which should be the Media Foundation formats. I store these into a class and record the actual Index number as part of this class so I can use it to select the desired format later.
TWCameraFormats EnumerateCaptureFormats(IMFMediaSource * pSource)
{
IMFPresentationDescriptor * pPD = NULL;
IMFStreamDescriptor * pSD = NULL;
IMFMediaTypeHandler * pHandler = NULL;
IMFMediaType * pType = NULL;
GUID guidSubType = GUID_NULL;
GUID guidMediaType = GUID_NULL;
DWORD cTypes = 0;
BOOL fSelected;
UINT32 count = 0;
UINT32 value1;
UINT32 value2;
TWCameraFormats CameraFormats;
DWORD ItemIndex = 0;
CameraFormats.set_length(0);
HRESULT hr = pSource->CreatePresentationDescriptor(&pPD);
if (FAILED(hr))
{
goto done;
}
hr = pPD->GetStreamDescriptorByIndex(0, &fSelected, &pSD);
if (FAILED(hr))
{
goto done;
}
hr = pSD->GetMediaTypeHandler(&pHandler);
if (FAILED(hr))
{
goto done;
}
hr = pHandler->GetMediaTypeCount(&cTypes);
if (FAILED(hr))
{
goto done;
}
for (DWORD typeIndex = 0; typeIndex < cTypes; typeIndex++)
{
hr = pHandler->GetMediaTypeByIndex(typeIndex, &pType);
if (FAILED(hr))
{
goto done;
}
HRESULT hr = pType->GetCount(&count);
if (FAILED(hr))
{
goto done;
}
if (count == 0)
{
goto done;
}
hr = pType->GetGUID(MF_MT_AM_FORMAT_TYPE, &guidMediaType);
if(FAILED(hr))
{
break;
}
if (guidMediaType == FORMAT_VideoInfo2)
{
TWCameraFormat * CurrentFormat;
CameraFormats.set_length(ItemIndex + 1);
CurrentFormat = &CameraFormats[ItemIndex++];
CurrentFormat->Index = typeIndex;
hr = GetFrameSize(pType, &value1, &value2);
if (FAILED(hr))
{
break;
}
CurrentFormat->Width = value1;
CurrentFormat->Height = value2;
hr = GetFrameRate(pType, &value1, &value2);
if (FAILED(hr))
{
break;
}
if (value2 > 0)
{
CurrentFormat->AvgTimePerFrame = 10000000 / (value1 / value2);
}
hr = pType->GetGUID(MF_MT_SUBTYPE, &guidSubType);
if(FAILED(hr))
{
break;
}
CurrentFormat->BitsPerPixel = 0;
if (guidSubType == MFVideoFormat_YUY2)
{
CurrentFormat->Compression[0] = 'Y';
CurrentFormat->Compression[1] = 'U';
CurrentFormat->Compression[2] = 'Y';
CurrentFormat->Compression[3] = '2';
CurrentFormat->BitsPerPixel = 24;
}
else if (guidSubType == MFVideoFormat_H264)
{
CurrentFormat->Compression[0] = 'H';
CurrentFormat->Compression[1] = '2';
CurrentFormat->Compression[2] = '6';
CurrentFormat->Compression[3] = '4';
CurrentFormat->BitsPerPixel = 32;
}
else if (guidSubType == MFVideoFormat_H265)
{
CurrentFormat->Compression[0] = 'H';
CurrentFormat->Compression[1] = '2';
CurrentFormat->Compression[2] = '6';
CurrentFormat->Compression[3] = '5';
CurrentFormat->BitsPerPixel = 32;
}
else if (guidSubType == MFVideoFormat_MJPG)
{
CurrentFormat->Compression[0] = 'M';
CurrentFormat->Compression[1] = 'J';
CurrentFormat->Compression[2] = 'P';
CurrentFormat->Compression[3] = 'G';
CurrentFormat->BitsPerPixel = 32;
}
else if (guidSubType == MFVideoFormat_NV12)
{
CurrentFormat->Compression[0] = 'N';
CurrentFormat->Compression[1] = 'V';
CurrentFormat->Compression[2] = '1';
CurrentFormat->Compression[3] = '2';
CurrentFormat->BitsPerPixel = 12;
}
else
{
CurrentFormat->Compression[3] = (guidSubType.Data1 >> 24) & 0xFF;
CurrentFormat->Compression[2] = (guidSubType.Data1 >> 16) & 0xFF;
CurrentFormat->Compression[1] = (guidSubType.Data1 >> 8) & 0xFF;
CurrentFormat->Compression[0] = (guidSubType.Data1 >> 0) & 0xFF;
}
}
SafeRelease(&pType);
}
done:
if (FAILED(hr))
{
CameraFormats.set_length(0);
}
SafeRelease(&pPD);
SafeRelease(&pSD);
SafeRelease(&pHandler);
SafeRelease(&pType);
return CameraFormats;
}
In the capture manager I pass the Index from the format enumeration and use this to select the desired video format.
HRESULT CaptureManager::SetVideoFormat(int FormatIndex)
{
IMFPresentationDescriptor * pPD = NULL;
IMFStreamDescriptor * pSD = NULL;
IMFMediaTypeHandler * pHandler = NULL;
IMFMediaType * pType = NULL;
IMFCaptureSource * pCaptureSource = NULL;
IMFMediaSource * pSource = NULL;
HRESULT hr = m_pEngine->GetSource(&pCaptureSource);
if (FAILED(hr))
{
goto done;
}
hr = pCaptureSource->GetCaptureDeviceSource(MF_CAPTURE_ENGINE_DEVICE_TYPE_VIDEO, &pSource);
if (FAILED(hr))
{
goto done;
}
hr = pSource->CreatePresentationDescriptor(&pPD);
if (FAILED(hr))
{
goto done;
}
BOOL fSelected;
hr = pPD->GetStreamDescriptorByIndex(0, &fSelected, &pSD);
if (FAILED(hr))
{
goto done;
}
hr = pSD->GetMediaTypeHandler(&pHandler);
if (FAILED(hr))
{
goto done;
}
hr = pHandler->GetMediaTypeByIndex(FormatIndex, &pType);
if (FAILED(hr))
{
goto done;
}
hr = pHandler->SetCurrentMediaType(pType);
done:
SafeRelease(&pPD);
SafeRelease(&pSD);
SafeRelease(&pHandler);
SafeRelease(&pType);
return hr;
}
And then I activate the preview from the capture manager.
HRESULT CaptureManager::StartPreview()
{
if (m_pEngine == NULL)
{
return MF_E_NOT_INITIALIZED;
}
if (m_bPreviewing == true)
{
return S_OK;
}
IMFCaptureSink *pSink = NULL;
IMFMediaType *pMediaType = NULL;
IMFMediaType *pMediaType2 = NULL;
IMFCaptureSource *pSource = NULL;
HRESULT hr = S_OK;
// Get a pointer to the preview sink.
if (m_pPreview == NULL)
{
hr = m_pEngine->GetSink(MF_CAPTURE_ENGINE_SINK_TYPE_PREVIEW, &pSink);
if (FAILED(hr))
{
goto done;
}
hr = pSink->QueryInterface(IID_PPV_ARGS(&m_pPreview));
if (FAILED(hr))
{
goto done;
}
hr = m_pPreview->SetRenderHandle(m_hwndPreview);
if (FAILED(hr))
{
goto done;
}
hr = m_pEngine->GetSource(&pSource);
if (FAILED(hr))
{
goto done;
}
// Configure the video format for the preview sink.
hr = pSource->GetCurrentDeviceMediaType((DWORD)MF_CAPTURE_ENGINE_PREFERRED_SOURCE_STREAM_FOR_VIDEO_PREVIEW , &pMediaType);
if (FAILED(hr))
{
goto done;
}
hr = CloneVideoMediaType(pMediaType, MFVideoFormat_RGB32, &pMediaType2);
if (FAILED(hr))
{
goto done;
}
hr = pMediaType2->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
if (FAILED(hr))
{
goto done;
}
// Connect the video stream to the preview sink.
DWORD dwSinkStreamIndex;
hr = m_pPreview->AddStream((DWORD)MF_CAPTURE_ENGINE_PREFERRED_SOURCE_STREAM_FOR_VIDEO_PREVIEW, pMediaType2, NULL, &dwSinkStreamIndex);
if (FAILED(hr))
{
goto done;
}
}
hr = m_pEngine->StartPreview();
if (!m_fPowerRequestSet && m_hpwrRequest != INVALID_HANDLE_VALUE)
{
// NOTE: By calling this, on SOC systems (AOAC enabled), we're asking the system to not go
// into sleep/connected standby while we're streaming. However, since we don't want to block
// the device from ever entering connected standby/sleep, we're going to latch ourselves to
// the monitor on/off notification (RegisterPowerSettingNotification(GUID_MONITOR_POWER_ON)).
// On SOC systems, this notification will fire when the user decides to put the device in
// connected standby mode--we can trap this, turn off our media streams and clear this
// power set request to allow the device to go into the lower power state.
m_fPowerRequestSet = (TRUE == PowerSetRequest(m_hpwrRequest, PowerRequestExecutionRequired));
}
done:
SafeRelease(&pSink);
SafeRelease(&pMediaType);
SafeRelease(&pMediaType2);
SafeRelease(&pSource);
return hr;
}
All of the functions return valid codes but my preview will only work reliably for the first item in the index.
I create a new CaptureManager whenever I connect for a new format, so nothing should be retained from previous session.

COM/ActiveX and Downloading url

I am have an ActiveX that asks a server for a URL and downloads / reads the data and ask for a new buffer of data until completion. The data gets read correctly, but if I watch the task manager the memory and the handles for IE go up and never come down. Process explorer shows that the handles are to the cache files.
int AXGetUrl(char* url,
void* buffer,
int buffer_size,
void* pUnk)
{
char buffer2[256];
int rc;
HRESULT rc2;
ULONG buffer_len;
ULONG buffer_len2;
IStream* pStream;
char newurl[1024];
ULONG64 RequestBytes;
CURLCallBack UCB;
//Initialize
rc = 0;
rc2 = 0;
memset(newurl,
0x00,
sizeof(newurl));
buffer_len = 0;
pStream = NULL;
RequestBytes = 0;
strcpy(newurl,
url);
rc = URLOpenBlockingStream((IUnknown *)pUnk,
url,
&pStream,
0,
&UCB);
while (rc == 0)
{
if (pStream != NULL)
{
rc2 = pStream->Read(buffer,
buffer_size,
&buffer_len);
RequestBytes = RequestBytes + buffer_len;
pStream->Release();
pStream = NULL;
buildnewurl(newurl,
RequestBytes);
rc = URLOpenBlockingStream((IUnknown *)pUnk,
newurl,
&pStream,
0,
&UCB);
}
else
{
rc = 1;
}
}
return (rc);
}
UPDATE:
#include "stdafx.h"
#include "Test.h"
Test::Test()
{
}
Test::~Test()
{
}
HRESULT Test::FinalConstruct()
{
void* pUnk = NULL;
void* buffer = NULL;
int buffer_size = 131072;
ULONG buffer_len = 0;
HRESULT rc = 0;
HRESULT rc2 = 0;
IStream* pStream = NULL;
int counter = 0;
char url[1024];
rc = QueryInterface(IID_IUnknown, &pUnk);
buffer = malloc(buffer_size);
if (buffer != NULL)
{
//Get URL
sprintf(url,
"http://search.yahoo.com/search?p=%d",
counter);
rc = URLOpenBlockingStream((IUnknown *)pUnk,
url,
&pStream,
0,
NULL);
while (rc == 0)
{
if (pStream != NULL)
{
rc2 = pStream->Read(buffer,
buffer_size,
&buffer_len);
pStream->Release();
pStream = NULL;
counter++;
if (counter == 15)
{
break;
}
sprintf(url,
"http://search.yahoo.com/search?p=%d",
counter);
rc = URLOpenBlockingStream((IUnknown *)pUnk,
url,
&pStream,
0,
NULL);
}
else
{
rc = 1;
}
}
free(buffer);
}
return (S_OK);
}
void Test::FinalRelease()
{
return;
}
You are making a call to 'URLOpenBlockingStream' to retrieve a stream object but the stream it returns is never deleted. Assuming that IStream is a COM object you need to call Release() once you are done with pStream...
rc = URLOpenBlockingStream((IUnknown *)pUnk,
url,
&pStream,
0,
&UCB);
while (rc == 0)
{
// ... your code here...
}
if(pStream != NULL)
{
pStream->Release();
}