Reputation: 1
I am trying to retrieve a thumbnail from a video using c++. I have been using the code from Microsoft, the code, grabs a frame and then displays it on the screen without saving it in the filesystem. I am trying to save the BYTE* to a suitable image format but failing at it.
My issue is that when I try to save the IMFSample to the filesystem I am getting a weird issue. This the expected output and this is the output I am getting when saving the file.
This is the code I am using to save the IMFsample to bitmap using c++, I got this from here
`
void test(IMFSample* pSample, FormatInfo format)
{
HANDLE file;
BITMAPFILEHEADER fileHeader;
BITMAPINFOHEADER fileInfo;
DWORD write = 0;
file = CreateFile(LR"(C:\Users\soham.mittal\Desktop\Thumbnails\vids\sample.bmp)", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); //Sets up the new bmp to be written to
fileHeader.bfType = 19778; //Sets our type to BM or bmp
fileHeader.bfSize = sizeof(fileHeader.bfOffBits) + sizeof(RGBTRIPLE); //Sets the size equal to the size of the header struct
fileHeader.bfReserved1 = 0; //sets the reserves to 0
fileHeader.bfReserved2 = 0;
fileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); //Sets offbits equal to the size of file and info header
fileInfo.biSize = sizeof(BITMAPINFOHEADER);
fileInfo.biWidth = format.imageWidthPels;
fileInfo.biHeight = format.imageHeightPels;
fileInfo.biPlanes = 1;
fileInfo.biBitCount = 24;
fileInfo.biCompression = BI_RGB;
fileInfo.biSizeImage = format.imageWidthPels * format.imageHeightPels * (24 / 8);
fileInfo.biXPelsPerMeter = 2400;
fileInfo.biYPelsPerMeter = 2400;
fileInfo.biClrImportant = 0;
fileInfo.biClrUsed = 0;
WriteFile(file, &fileHeader, sizeof(fileHeader), &write, NULL);
WriteFile(file, &fileInfo, sizeof(fileInfo), &write, NULL);
IMFMediaBuffer* mediaBuffer = NULL;
BYTE* pData = NULL;
pSample->ConvertToContiguousBuffer(&mediaBuffer);
auto hr = mediaBuffer->Lock(&pData, NULL, NULL);
WriteFile(file, pData, fileInfo.biSizeImage, &write, NULL);
CloseHandle(file);
mediaBuffer->Unlock();
}
`
I have tried reading documentation about it and googling this issue but to no avail. I am still stuck with this distorted image. Please help me identify any bugs in the code or point me in the right direction. Thank you :)
Upvotes: 0
Views: 202
Reputation: 1
The output images are no longer available; however, this bitmap output code has several issues that need fixing:
The calculation of the file size, fileHeader.bfSize = sizeof(fileHeader.bfOffBits) + sizeof(RGBTRIPLE);
, is completely wrong. It should be fileHeader.bfSize = fileHeader.bfOffBits + fileInfo.biSizeImage
for a 24-bpp image, and is best calculated after you've initialized fileInfo
. If using other bit-depths, account for the palette entries using sizeof(RGBQUAD)
; only use sizeof(RGBTRIPLE)
if you're using BITMAPCOREHEADER
.
The calculation of fileInfo.biSizeImage
has to take DWORD padding of scan lines into account. A nice shortcut for doing this is to calculate the number of bits in a scanline, add 31, shift right 5 bits, then shift left 2 bits: fileInfo.biSizeImage = ((((abs(fileInfo.biWidth)*fileInfo.biBitCount)+31)>>5)<<2) * abs(fileInfo.biHeight);
(the abs()
is there because the height is set negative for a top-down bitmap, which I suspect is what you want).
Now that I've mentioned it, use negative height to create a top-down bitmap fileInfo.biHeight = 0 - (LONG)(format.imageHeightPels)
.
If setting the physical dimensions, it is best to set them to 96 ppi unless there's a good reason for doing otherwise bih.biXPelsPerMeter = (((96*10000)+127)/254);
Unless you are sure that the source buffer is padded the same way your destination bitmap is padded (DWORD aligned scanlines), you want to copy the data scanline by scanline. So the code WriteFile(file, pData, fileInfo.biSizeImage, &write, NULL);
might be wrong, and you'll need to replace that with a loop that accounts for the pitch of each buffer.
/*
NB: not the most efficient way of doing this, but it illustrates
the point in an easy-to-follow way.
This code assumes you have some padding bytes at the end of the
source buffer; if not, you might want to use a scanline buffer.
Alternatively, you can also use SetFilePointer() to move the
distance between the two pitch values, but that is inefficient.
*/
pitch_dst = (((abs(fileInfo.biWidth)*fileInfo.biBitCount)+31)>>5)<<2;
pitch_src = fileInfo.biWidth*3; //for example
for (y = 0; y < fileInfo.biHeight; y++)
{
i = WriteFile(file, pData + (y*pitch_src), pitch_dst, &write, NULL);
if((!i) || (write != pitch_dst))
{
goto error_writing_data;
}
}
Upvotes: 0