Feature: Support recording video in remote desktop control
This commit is contained in:
607
server/2015Remote/Bmp2Video.cpp
Normal file
607
server/2015Remote/Bmp2Video.cpp
Normal file
@@ -0,0 +1,607 @@
|
||||
#include "stdafx.h"
|
||||
#include "Bmp2Video.h"
|
||||
|
||||
#define USE_JPEG 0
|
||||
|
||||
#if USE_JPEG
|
||||
#include "common/turbojpeg.h"
|
||||
#ifdef _WIN64
|
||||
#ifdef _DEBUG
|
||||
#pragma comment(lib, "jpeg/turbojpeg_64_d.lib")
|
||||
#else
|
||||
#pragma comment(lib, "jpeg/turbojpeg_64_d.lib")
|
||||
#endif
|
||||
|
||||
#else
|
||||
#ifdef _DEBUG
|
||||
#pragma comment(lib, "jpeg/turbojpeg_32_d.lib")
|
||||
#else
|
||||
#pragma comment(lib, "jpeg/turbojpeg_32_d.lib")
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#else
|
||||
#define tjFree free
|
||||
#endif
|
||||
|
||||
|
||||
AVISTREAMINFO CBmpToAvi::m_si = {};
|
||||
|
||||
CBmpToAvi::CBmpToAvi()
|
||||
{
|
||||
m_nFrames = 0;
|
||||
m_pfile = NULL;
|
||||
m_pavi = NULL;
|
||||
m_hic = NULL;
|
||||
AVIFileInit();
|
||||
}
|
||||
|
||||
CBmpToAvi::~CBmpToAvi()
|
||||
{
|
||||
Close();
|
||||
AVIFileExit();
|
||||
}
|
||||
|
||||
int CBmpToAvi::Open(LPCTSTR szFile, LPBITMAPINFO lpbmi, int rate, FCCHandler h)
|
||||
{
|
||||
if (szFile == NULL)
|
||||
return ERR_INVALID_PARAM;
|
||||
|
||||
m_nFrames = 0;
|
||||
if (AVIFileOpen(&m_pfile, szFile, OF_WRITE | OF_CREATE, NULL))
|
||||
return ERR_INTERNAL;
|
||||
|
||||
m_fccHandler = h;
|
||||
m_si.fccType = streamtypeVIDEO;
|
||||
m_si.fccHandler = m_fccHandler;
|
||||
m_si.dwScale = 1;
|
||||
m_si.dwRate = rate;
|
||||
m_width = lpbmi->bmiHeader.biWidth;
|
||||
m_height = lpbmi->bmiHeader.biHeight;
|
||||
SetRect(&m_si.rcFrame, 0, 0, m_width, m_height);
|
||||
|
||||
m_bitCount = lpbmi->bmiHeader.biBitCount;
|
||||
if (m_bitCount != 24 && m_bitCount != 32) {
|
||||
AVIFileRelease(m_pfile);
|
||||
m_pfile = NULL;
|
||||
return ERR_NOT_SUPPORT;
|
||||
}
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȷ<EFBFBD><C8B7>BITMAPINFO<46><4F><EFBFBD><EFBFBD>MJPEG
|
||||
BITMAPINFO bmiFormat = *lpbmi;
|
||||
|
||||
if (m_fccHandler == ENCODER_H264) {
|
||||
// <20><><EFBFBD><EFBFBD>H.264ѹ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
m_hic = ICOpen(ICTYPE_VIDEO, mmioFOURCC('X', '2', '6', '4'), ICMODE_COMPRESS);
|
||||
if (!m_hic) {
|
||||
AVIFileRelease(m_pfile);
|
||||
m_pfile = NULL;
|
||||
return ERR_NO_ENCODER;
|
||||
}
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʽ<EFBFBD><CABD>δѹ<CEB4><D1B9><EFBFBD><EFBFBD>24λBMP<4D><50>
|
||||
BITMAPINFOHEADER inputFormat = { 0 };
|
||||
inputFormat.biSize = sizeof(BITMAPINFOHEADER);
|
||||
inputFormat.biWidth = m_width;
|
||||
inputFormat.biHeight = m_height;
|
||||
inputFormat.biPlanes = 1;
|
||||
inputFormat.biBitCount = 24;
|
||||
inputFormat.biCompression = BI_RGB;
|
||||
inputFormat.biSizeImage = m_width * m_height * 3;
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʽ<EFBFBD><CABD>H.264ѹ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
BITMAPINFOHEADER outputFormat = inputFormat;
|
||||
outputFormat.biCompression = mmioFOURCC('X', '2', '6', '4');
|
||||
|
||||
// <20><>ѯѹ<D1AF><D1B9><EFBFBD><EFBFBD><EFBFBD>ܷ<EFBFBD><DCB7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʽ
|
||||
DWORD result = ICCompressQuery(m_hic, &inputFormat, &outputFormat);
|
||||
if (result != ICERR_OK) {
|
||||
ICClose(m_hic);
|
||||
m_hic = NULL;
|
||||
AVIFileRelease(m_pfile);
|
||||
m_pfile = NULL;
|
||||
Mprintf("ICCompressQuery failed: %d\n", result);
|
||||
return ERR_NO_ENCODER;
|
||||
}
|
||||
|
||||
// <20><>ʼѹ<CABC><D1B9>
|
||||
result = ICCompressBegin(m_hic, &inputFormat, &outputFormat);
|
||||
if (result != ICERR_OK) {
|
||||
ICClose(m_hic);
|
||||
m_hic = NULL;
|
||||
AVIFileRelease(m_pfile);
|
||||
m_pfile = NULL;
|
||||
Mprintf("ICCompressBegin failed: %d\n", result);
|
||||
return ERR_NO_ENCODER;
|
||||
}
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
m_quality = 7500;
|
||||
|
||||
// AVI<56><49><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
bmiFormat.bmiHeader.biCompression = mmioFOURCC('X', '2', '6', '4');
|
||||
bmiFormat.bmiHeader.biBitCount = 24;
|
||||
bmiFormat.bmiHeader.biSizeImage = m_width * m_height * 3;
|
||||
m_si.dwSuggestedBufferSize = bmiFormat.bmiHeader.biSizeImage;
|
||||
}
|
||||
else if (m_fccHandler == ENCODER_MJPEG) {
|
||||
// MJPEG<45><47>Ҫ<EFBFBD><D2AA><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
bmiFormat.bmiHeader.biCompression = mmioFOURCC('M', 'J', 'P', 'G');
|
||||
bmiFormat.bmiHeader.biBitCount = 24; // MJPEG<45><47><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>24λ
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȷ<EFBFBD><C8B7>ͼ<EFBFBD><CDBC><EFBFBD><EFBFBD>С
|
||||
bmiFormat.bmiHeader.biSizeImage = m_width * m_height * 3;
|
||||
m_si.dwSuggestedBufferSize = bmiFormat.bmiHeader.biSizeImage * 2; // Ԥ<><D4A4><EFBFBD>㹻<EFBFBD>ռ<EFBFBD>
|
||||
m_quality = 85; // Ĭ<><C4AC><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
}
|
||||
else {
|
||||
m_si.dwSuggestedBufferSize = lpbmi->bmiHeader.biSizeImage;
|
||||
}
|
||||
|
||||
if (AVIFileCreateStream(m_pfile, &m_pavi, &m_si)) {
|
||||
if (m_hic) {
|
||||
ICCompressEnd(m_hic);
|
||||
ICClose(m_hic);
|
||||
m_hic = NULL;
|
||||
}
|
||||
AVIFileRelease(m_pfile);
|
||||
m_pfile = NULL;
|
||||
return ERR_INTERNAL;
|
||||
}
|
||||
|
||||
if (AVIStreamSetFormat(m_pavi, 0, &bmiFormat, sizeof(BITMAPINFOHEADER))) {
|
||||
if (m_hic) {
|
||||
ICCompressEnd(m_hic);
|
||||
ICClose(m_hic);
|
||||
m_hic = NULL;
|
||||
}
|
||||
AVIStreamRelease(m_pavi);
|
||||
m_pavi = NULL;
|
||||
AVIFileRelease(m_pfile);
|
||||
m_pfile = NULL;
|
||||
return ERR_INTERNAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if USE_JPEG
|
||||
// <20>Ż<EFBFBD><C5BB><EFBFBD>BMP<4D><50>JPEGת<47><D7AA>
|
||||
bool BmpToJpeg(LPVOID lpBuffer, int width, int height, int quality, unsigned char** jpegData, unsigned long* jpegSize) {
|
||||
if (!lpBuffer || !jpegData || !jpegSize) {
|
||||
return false;
|
||||
}
|
||||
tjhandle jpegCompressor = tjInitCompress();
|
||||
if (!jpegCompressor) {
|
||||
Mprintf("TurboJPEG initialization failed: %s\n", tjGetErrorStr());
|
||||
return false;
|
||||
}
|
||||
|
||||
// ȷ<><C8B7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ں<EFBFBD><DABA><EFBFBD><EFBFBD><EFBFBD>Χ<EFBFBD><CEA7>
|
||||
if (quality < 1) quality = 85;
|
||||
if (quality > 100) quality = 100;
|
||||
|
||||
int pitch = width * 3; // BGR24<32><34>ʽ<EFBFBD><CABD>ÿ<EFBFBD><C3BF><EFBFBD>ֽ<EFBFBD><D6BD><EFBFBD>
|
||||
|
||||
// <20><>Ҫ<EFBFBD><D2AA><EFBFBD><EFBFBD>ʼ<EFBFBD><CABC>ΪNULL<4C><4C><EFBFBD><EFBFBD>TurboJPEG<45>Լ<EFBFBD><D4BC><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڴ<EFBFBD>
|
||||
*jpegData = NULL;
|
||||
*jpegSize = 0;
|
||||
|
||||
// ȥ<><C8A5>TJFLAG_NOREALLOC<4F><43>־<EFBFBD><D6BE><EFBFBD><EFBFBD>TurboJPEG<45>Զ<EFBFBD><D4B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڴ<EFBFBD>
|
||||
int tjError = tjCompress2(
|
||||
jpegCompressor,
|
||||
(unsigned char*)lpBuffer,
|
||||
width,
|
||||
pitch, // ÿ<><C3BF><EFBFBD>ֽ<EFBFBD><D6BD><EFBFBD>
|
||||
height,
|
||||
TJPF_BGR, // BGR<47><52>ʽ
|
||||
jpegData, // TurboJPEG<45><47><EFBFBD>Զ<EFBFBD><D4B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڴ<EFBFBD>
|
||||
jpegSize,
|
||||
TJSAMP_422, // 4:2:2ɫ<32><C9AB><EFBFBD>Ӳ<EFBFBD><D3B2><EFBFBD>
|
||||
quality, // ѹ<><D1B9><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
0 // <20><>ʹ<EFBFBD><CAB9><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>־
|
||||
);
|
||||
|
||||
if (tjError != 0) {
|
||||
Mprintf("TurboJPEG compression failed: %s\n", tjGetErrorStr2(jpegCompressor));
|
||||
tjDestroy(jpegCompressor);
|
||||
return false;
|
||||
}
|
||||
|
||||
tjDestroy(jpegCompressor);
|
||||
|
||||
// <20><>֤<EFBFBD><D6A4><EFBFBD><EFBFBD>
|
||||
if (*jpegData == NULL || *jpegSize == 0) {
|
||||
Mprintf("JPEG compression produced no data\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
Mprintf("JPEG compression successful: %lu bytes\n", *jpegSize);
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
#include <windows.h>
|
||||
#include <gdiplus.h>
|
||||
#include <shlwapi.h>
|
||||
#pragma comment(lib, "gdiplus.lib")
|
||||
#pragma comment(lib, "shlwapi.lib")
|
||||
|
||||
using namespace Gdiplus;
|
||||
|
||||
// ==================== <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ====================
|
||||
|
||||
// <20><>ȡ JPEG <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> CLSID
|
||||
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
|
||||
{
|
||||
UINT num = 0;
|
||||
UINT size = 0;
|
||||
|
||||
GetImageEncodersSize(&num, &size);
|
||||
if (size == 0) return -1;
|
||||
|
||||
ImageCodecInfo* pImageCodecInfo = (ImageCodecInfo*)malloc(size);
|
||||
if (pImageCodecInfo == NULL) return -1;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
free(pImageCodecInfo);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// ==================== <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ====================
|
||||
|
||||
bool BmpToJpeg(LPVOID lpBuffer, int width, int height, int quality,
|
||||
unsigned char** jpegData, unsigned long* jpegSize)
|
||||
{
|
||||
if (!lpBuffer || !jpegData || !jpegSize || width <= 0 || height <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// <20><><EFBFBD><EFBFBD> DIB <20><><EFBFBD><EFBFBD><EFBFBD>ֽ<EFBFBD><D6BD><EFBFBD><EFBFBD><EFBFBD>4<EFBFBD>ֽڶ<D6BD><DAB6>룩
|
||||
int rowSize = ((width * 3 + 3) / 4) * 4;
|
||||
|
||||
// <20><><EFBFBD><EFBFBD> Bitmap <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>24λ BGR <20><>ʽ<EFBFBD><CABD>
|
||||
Bitmap* bitmap = new Bitmap(width, height, PixelFormat24bppRGB);
|
||||
if (!bitmap || bitmap->GetLastStatus() != Ok) {
|
||||
if (bitmap) delete bitmap;
|
||||
return false;
|
||||
}
|
||||
|
||||
// <20><><EFBFBD><EFBFBD> Bitmap <20><>д<EFBFBD><D0B4><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
BitmapData bitmapData;
|
||||
Rect rect(0, 0, width, height);
|
||||
Status status = bitmap->LockBits(&rect, ImageLockModeWrite,
|
||||
PixelFormat24bppRGB, &bitmapData);
|
||||
|
||||
if (status != Ok) {
|
||||
delete bitmap;
|
||||
return false;
|
||||
}
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݣ<EFBFBD>ע<EFBFBD>⣺DIB <20>ǵײ<C7B5><D7B2><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ҫ<EFBFBD><D2AA>ת<EFBFBD><D7AA>
|
||||
BYTE* srcData = (BYTE*)lpBuffer;
|
||||
BYTE* dstData = (BYTE*)bitmapData.Scan0;
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
// DIB <20>Ǵӵײ<D3B5><D7B2><EFBFBD>ʼ<EFBFBD>ģ<EFBFBD><C4A3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ҫ<EFBFBD><D2AA>ת
|
||||
BYTE* srcRow = srcData + (height - 1 - y) * rowSize;
|
||||
BYTE* dstRow = dstData + y * bitmapData.Stride;
|
||||
memcpy(dstRow, srcRow, width * 3);
|
||||
}
|
||||
|
||||
bitmap->UnlockBits(&bitmapData);
|
||||
|
||||
// <20><>ȡ JPEG <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
CLSID jpegClsid;
|
||||
if (GetEncoderClsid(L"image/jpeg", &jpegClsid) < 0) {
|
||||
delete bitmap;
|
||||
return false;
|
||||
}
|
||||
|
||||
// <20><><EFBFBD><EFBFBD> JPEG <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
EncoderParameters encoderParams;
|
||||
encoderParams.Count = 1;
|
||||
encoderParams.Parameter[0].Guid = EncoderQuality;
|
||||
encoderParams.Parameter[0].Type = EncoderParameterValueTypeLong;
|
||||
encoderParams.Parameter[0].NumberOfValues = 1;
|
||||
|
||||
ULONG qualityValue = (ULONG)quality;
|
||||
encoderParams.Parameter[0].Value = &qualityValue;
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD>ڴ<EFBFBD><DAB4><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڱ<EFBFBD><DAB1><EFBFBD> JPEG
|
||||
IStream* stream = NULL;
|
||||
HRESULT hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
|
||||
if (FAILED(hr)) {
|
||||
delete bitmap;
|
||||
return false;
|
||||
}
|
||||
|
||||
// <20><><EFBFBD><EFBFBD>Ϊ JPEG
|
||||
status = bitmap->Save(stream, &jpegClsid, &encoderParams);
|
||||
delete bitmap;
|
||||
|
||||
if (status != Ok) {
|
||||
stream->Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
// <20><>ȡ JPEG <20><><EFBFBD><EFBFBD>
|
||||
HGLOBAL hMem = NULL;
|
||||
hr = GetHGlobalFromStream(stream, &hMem);
|
||||
if (FAILED(hr)) {
|
||||
stream->Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
SIZE_T memSize = GlobalSize(hMem);
|
||||
if (memSize == 0) {
|
||||
stream->Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
*jpegSize = (unsigned long)memSize;
|
||||
*jpegData = new unsigned char[*jpegSize];
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
void* pMem = GlobalLock(hMem);
|
||||
if (pMem) {
|
||||
memcpy(*jpegData, pMem, *jpegSize);
|
||||
GlobalUnlock(hMem);
|
||||
}
|
||||
else {
|
||||
delete[] * jpegData;
|
||||
*jpegData = NULL;
|
||||
stream->Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
stream->Release();
|
||||
return true;
|
||||
}
|
||||
|
||||
// ==================== GDI+ <20><>ʼ<EFBFBD><CABC>/<2F><><EFBFBD><EFBFBD> ====================
|
||||
|
||||
class GdiplusManager {
|
||||
private:
|
||||
ULONG_PTR gdiplusToken;
|
||||
bool initialized;
|
||||
|
||||
public:
|
||||
GdiplusManager() : gdiplusToken(0), initialized(false) {
|
||||
GdiplusStartupInput gdiplusStartupInput;
|
||||
if (GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL) == Ok) {
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
~GdiplusManager() {
|
||||
if (initialized) {
|
||||
GdiplusShutdown(gdiplusToken);
|
||||
}
|
||||
}
|
||||
|
||||
bool IsInitialized() const { return initialized; }
|
||||
};
|
||||
|
||||
// ȫ<>ֶ<EFBFBD><D6B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Զ<EFBFBD><D4B6><EFBFBD>ʼ<EFBFBD><CABC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
static GdiplusManager g_gdiplusManager;
|
||||
#endif
|
||||
|
||||
|
||||
// <20><>ȷ<EFBFBD><C8B7>32λת24λת<CEBB><D7AA><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ת<EFBFBD><D7AA>
|
||||
unsigned char* ConvertScreenshot32to24(unsigned char* p32bitBmp, int width, int height)
|
||||
{
|
||||
// <20><><EFBFBD><EFBFBD>BMP<4D><50>ʵ<EFBFBD><CAB5><EFBFBD>д<EFBFBD>С<EFBFBD><D0A1>4<EFBFBD>ֽڶ<D6BD><DAB6>룩
|
||||
int srcRowSize = ((width * 32 + 31) / 32) * 4;
|
||||
int dstRowSize = width * 3; // Ŀ<><C4BF><EFBFBD>ǽ<EFBFBD><C7BD>յ<EFBFBD>24λ
|
||||
|
||||
unsigned char* p24bitBmp = (unsigned char*)malloc(dstRowSize * height);
|
||||
if (!p24bitBmp) return nullptr;
|
||||
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
// BMP<4D>Ǵ<EFBFBD><C7B4>µ<EFBFBD><C2B5>ϴ洢<CFB4><E6B4A2><EFBFBD><EFBFBD>Ҫ<EFBFBD><D2AA>ת
|
||||
unsigned char* src = p32bitBmp + (height - 1 - y) * srcRowSize;
|
||||
unsigned char* dst = p24bitBmp + y * dstRowSize;
|
||||
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
dst[x * 3 + 0] = src[x * 4 + 0]; // B
|
||||
dst[x * 3 + 1] = src[x * 4 + 1]; // G
|
||||
dst[x * 3 + 2] = src[x * 4 + 2]; // R
|
||||
// <20><><EFBFBD><EFBFBD>Alphaͨ<61><CDA8>
|
||||
}
|
||||
}
|
||||
|
||||
return p24bitBmp;
|
||||
}
|
||||
|
||||
// 24λBMP<4D><50><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ת<EFBFBD><D7AA>ȥ<EFBFBD><C8A5><EFBFBD>룩
|
||||
unsigned char* Process24BitBmp(unsigned char* lpBuffer, int width, int height)
|
||||
{
|
||||
// BMP 24λ<34>д<EFBFBD>С<EFBFBD><D0A1>4<EFBFBD>ֽڶ<D6BD><DAB6>룩
|
||||
int srcRowSize = ((width * 24 + 31) / 32) * 4;
|
||||
int dstRowSize = width * 3; // <20><><EFBFBD>ո<EFBFBD>ʽ
|
||||
|
||||
unsigned char* processed = (unsigned char*)malloc(dstRowSize * height);
|
||||
if (!processed) return nullptr;
|
||||
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
// <20><>ת<EFBFBD><D7AA>ȥ<EFBFBD><C8A5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֽ<EFBFBD>
|
||||
unsigned char* src = lpBuffer + (height - 1 - y) * srcRowSize;
|
||||
unsigned char* dst = processed + y * dstRowSize;
|
||||
memcpy(dst, src, dstRowSize);
|
||||
}
|
||||
|
||||
return processed;
|
||||
}
|
||||
|
||||
bool CBmpToAvi::Write(unsigned char* lpBuffer)
|
||||
{
|
||||
if (m_pfile == NULL || m_pavi == NULL || lpBuffer == NULL)
|
||||
return false;
|
||||
|
||||
unsigned char* writeData = nullptr;
|
||||
unsigned long writeSize = 0;
|
||||
bool needFree = false;
|
||||
|
||||
switch (m_fccHandler)
|
||||
{
|
||||
case ENCODER_BMP:
|
||||
writeData = lpBuffer;
|
||||
writeSize = m_si.dwSuggestedBufferSize;
|
||||
break;
|
||||
|
||||
case ENCODER_H264: {
|
||||
unsigned char* processedBuffer = nullptr;
|
||||
|
||||
if (m_bitCount == 32) {
|
||||
processedBuffer = ConvertScreenshot32to24(lpBuffer, m_width, m_height);
|
||||
}
|
||||
else if (m_bitCount == 24) {
|
||||
processedBuffer = Process24BitBmp(lpBuffer, m_width, m_height);
|
||||
}
|
||||
|
||||
if (!processedBuffer) {
|
||||
Mprintf("Failed to process buffer\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȷ<EFBFBD>ĸ<EFBFBD>ʽͷ
|
||||
BITMAPINFOHEADER inputHeader = { 0 };
|
||||
inputHeader.biSize = sizeof(BITMAPINFOHEADER);
|
||||
inputHeader.biWidth = m_width;
|
||||
inputHeader.biHeight = -m_height;
|
||||
inputHeader.biPlanes = 1;
|
||||
inputHeader.biBitCount = 24;
|
||||
inputHeader.biCompression = BI_RGB;
|
||||
inputHeader.biSizeImage = m_width * m_height * 3;
|
||||
|
||||
BITMAPINFOHEADER outputHeader = inputHeader;
|
||||
outputHeader.biCompression = mmioFOURCC('X', '2', '6', '4');
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
DWORD maxCompressedSize = m_width * m_height * 3;
|
||||
unsigned char* compressedData = (unsigned char*)malloc(maxCompressedSize);
|
||||
if (!compressedData) {
|
||||
free(processedBuffer);
|
||||
Mprintf("Failed to allocate compression buffer\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD flags = 0;
|
||||
|
||||
// <20><>ȷ<EFBFBD><C8B7><EFBFBD><EFBFBD>ICCompress
|
||||
DWORD result = ICCompress(
|
||||
m_hic, // ѹ<><D1B9><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
0, // <20><>־<EFBFBD><D6BE>0=<3D>Զ<EFBFBD><D4B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ؼ<EFBFBD>֡<EFBFBD><D6A1>
|
||||
&outputHeader, // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʽͷ
|
||||
compressedData, // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
&inputHeader, // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʽͷ
|
||||
processedBuffer, // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
NULL, // ckid
|
||||
&flags, // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>־
|
||||
m_nFrames, // ֡<><D6A1>
|
||||
0, // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>С<EFBFBD><D0A1>0=<3D>Զ<EFBFBD><D4B6><EFBFBD>
|
||||
m_quality, // <20><><EFBFBD><EFBFBD>
|
||||
NULL, // ǰһ֡<D2BB><D6A1>ʽͷ
|
||||
NULL // ǰһ֡<D2BB><D6A1><EFBFBD><EFBFBD>
|
||||
);
|
||||
|
||||
if (result != ICERR_OK) {
|
||||
free(compressedData);
|
||||
free(processedBuffer);
|
||||
Mprintf("ICCompress failed: %d\n", result);
|
||||
return false;
|
||||
}
|
||||
|
||||
// ʵ<><CAB5>ѹ<EFBFBD><D1B9><EFBFBD><EFBFBD>С<EFBFBD><D0A1>outputHeader.biSizeImage<67><65>
|
||||
writeData = compressedData;
|
||||
writeSize = outputHeader.biSizeImage;
|
||||
needFree = true;
|
||||
|
||||
free(processedBuffer);
|
||||
break;
|
||||
}
|
||||
|
||||
case ENCODER_MJPEG: {
|
||||
unsigned char* processedBuffer = nullptr;
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͬλ<CDAC><CEBB><EFBFBD><EFBFBD>
|
||||
if (m_bitCount == 32) {
|
||||
processedBuffer = ConvertScreenshot32to24(lpBuffer, m_width, m_height);
|
||||
}
|
||||
else if (m_bitCount == 24) {
|
||||
processedBuffer = Process24BitBmp(lpBuffer, m_width, m_height);
|
||||
}
|
||||
|
||||
if (!processedBuffer) {
|
||||
return false;
|
||||
}
|
||||
// ѹ<><D1B9>ΪJPEG
|
||||
if (!BmpToJpeg(processedBuffer, m_width, m_height, m_quality, &writeData, &writeSize)) {
|
||||
free(processedBuffer);
|
||||
Mprintf("Failed to compress JPEG\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
free(processedBuffer);
|
||||
needFree = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
// д<><D0B4>AVI<56><49>
|
||||
LONG bytesWritten = 0;
|
||||
LONG samplesWritten = 0;
|
||||
HRESULT hr = AVIStreamWrite(m_pavi, m_nFrames, 1,
|
||||
writeData, writeSize,
|
||||
AVIIF_KEYFRAME,
|
||||
&samplesWritten, &bytesWritten);
|
||||
|
||||
if (needFree && writeData) {
|
||||
if (m_fccHandler == ENCODER_MJPEG) {
|
||||
tjFree(writeData);
|
||||
}
|
||||
else {
|
||||
free(writeData);
|
||||
}
|
||||
}
|
||||
|
||||
if (hr != AVIERR_OK) {
|
||||
Mprintf("AVIStreamWrite failed: 0x%08X\n", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_nFrames++;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CBmpToAvi::Close()
|
||||
{
|
||||
if (m_hic) {
|
||||
ICCompressEnd(m_hic);
|
||||
ICClose(m_hic);
|
||||
m_hic = NULL;
|
||||
}
|
||||
if (m_pavi) {
|
||||
AVIStreamRelease(m_pavi);
|
||||
m_pavi = NULL;
|
||||
}
|
||||
if (m_pfile) {
|
||||
AVIFileRelease(m_pfile);
|
||||
m_pfile = NULL;
|
||||
}
|
||||
m_nFrames = 0;
|
||||
}
|
||||
Reference in New Issue
Block a user