Feature: Support recording video in remote desktop control

This commit is contained in:
yuanyuanxiang
2025-11-23 10:57:52 +01:00
parent 1e3aa8ff63
commit 9a640c0a1d
11 changed files with 3614 additions and 150 deletions

View File

@@ -962,6 +962,14 @@ inline std::string ToPekingTimeAsString(const time_t* t)
return buffer;
}
inline std::string ToPekingDateTime(const time_t* t)
{
auto pekingTime = ToPekingTime(t);
char buffer[20];
std::strftime(buffer, sizeof(buffer), "%Y%m%d%H%M%S", &pekingTime);
return buffer;
}
typedef struct Validation {
char From[20]; // <20><>ʼ<EFBFBD><CABC><EFBFBD><EFBFBD>
char To[20]; // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>

2809
common/turbojpeg.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -266,6 +266,7 @@
<ClInclude Include="2015RemoteDlg.h" />
<ClInclude Include="adapter.h" />
<ClInclude Include="AudioDlg.h" />
<ClInclude Include="Bmp2Video.h" />
<ClInclude Include="Buffer.h" />
<ClInclude Include="BuildDlg.h" />
<ClInclude Include="CDrawingBoard.h" />
@@ -339,6 +340,7 @@
<ClCompile Include="2015Remote.cpp" />
<ClCompile Include="2015RemoteDlg.cpp" />
<ClCompile Include="AudioDlg.cpp" />
<ClCompile Include="Bmp2Video.cpp" />
<ClCompile Include="Buffer.cpp" />
<ClCompile Include="BuildDlg.cpp" />
<ClCompile Include="CDrawingBoard.cpp" />

View File

@@ -56,6 +56,7 @@
<ClCompile Include="CWalletDlg.cpp" />
<ClCompile Include="CRcEditDlg.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="Bmp2Video.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\client\Audio.h" />
@@ -125,6 +126,7 @@
<ClInclude Include="CRcEditDlg.h" />
<ClInclude Include="..\..\common\obfs.h" />
<ClInclude Include="..\..\common\file_upload.h" />
<ClInclude Include="Bmp2Video.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="2015Remote.rc" />

View 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;
}

View File

@@ -0,0 +1,55 @@
#pragma once
#include <Vfw.h>
#pragma comment(lib,"Vfw32.lib")
#define ERR_INVALID_PARAM 1
#define ERR_NO_ENCODER 2
#define ERR_INTERNAL 3
#define ERR_NOT_SUPPORT 4
enum FCCHandler {
ENCODER_BMP = BI_RGB,
ENCODER_MJPEG = mmioFOURCC('M', 'J', 'P', 'G'),
// <20><>װx264vfw<66><77><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>: https://sourceforge.net/projects/x264vfw/
ENCODER_H264 = mmioFOURCC('X', '2', '6', '4'),
};
/************************************************************************
* @class CBmpToAvi
* @brief λͼתAVI֡
************************************************************************/
class CBmpToAvi
{
public:
CBmpToAvi();
virtual ~CBmpToAvi();
int Open(LPCTSTR szFile, LPBITMAPINFO lpbmi, int rate = 4, FCCHandler h = ENCODER_BMP);
bool Write(unsigned char* lpBuffer);
void Close();
static std::string GetErrMsg(int result) {
switch (result) {
case ERR_INVALID_PARAM:
return ("<EFBFBD><EFBFBD>Ч<EFBFBD><EFBFBD><EFBFBD><EFBFBD>");
case ERR_NOT_SUPPORT:
return ("<EFBFBD><EFBFBD>֧<EFBFBD>ֵ<EFBFBD>λ<EFBFBD><EFBFBD><EFBFBD>ȣ<EFBFBD><EFBFBD><EFBFBD>Ҫ24λ<EFBFBD><EFBFBD>32λ");
case ERR_NO_ENCODER:
return ("δ<EFBFBD><EFBFBD>װx264<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> \n<EFBFBD><EFBFBD><EFBFBD>ص<EFBFBD>ַ<EFBFBD><EFBFBD>https://sourceforge.net/projects/x264vfw");
case ERR_INTERNAL:
return("<EFBFBD><EFBFBD><EFBFBD><EFBFBD>AVI<EFBFBD>ļ<EFBFBD>ʧ<EFBFBD><EFBFBD>");
default:
return "succeed";
}
}
private:
FCCHandler m_fccHandler;
PAVIFILE m_pfile;
PAVISTREAM m_pavi;
int m_nFrames;
static AVISTREAMINFO m_si; // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ҫ<EFBFBD>Ǿ<EFBFBD>̬<EFBFBD><CCAC>
int m_bitCount = 24;
int m_width = 1920;
int m_height = 1080;
int m_quality = 90;
HIC m_hic = NULL;
};

View File

@@ -12,6 +12,8 @@
#define new DEBUG_NEW
#endif
#define TIMER_ID 132
/////////////////////////////////////////////////////////////////////////////
// CHideScreenSpyDlg dialog
enum {
@@ -33,10 +35,14 @@ enum {
IDM_FPS_20,
IDM_FPS_25,
IDM_FPS_30,
IDM_SAVEAVI_H264 = 996,
};
IMPLEMENT_DYNAMIC(CHideScreenSpyDlg, CDialog)
bool DirectoryExists(const char* path);
std::string GetScreenShotPath(CWnd* parent, const CString& ip, const CString& filter, const CString& suffix);
CHideScreenSpyDlg::CHideScreenSpyDlg(CWnd* pParent, Server* pIOCPServer, ClientContext* pContext)
: DialogBase(CHideScreenSpyDlg::IDD, pParent, pIOCPServer, pContext, IDI_SCREENSYP)
{
@@ -61,11 +67,7 @@ CHideScreenSpyDlg::~CHideScreenSpyDlg()
m_ContextObject->GetServer()->Disconnect(m_ContextObject);
DestroyIcon(m_hIcon);
Sleep(200);
if (!m_aviFile.IsEmpty()) {
KillTimer(132);
m_aviFile = "";
m_aviStream.Close();
}
::ReleaseDC(m_hWnd, m_hFullDC);
DeleteDC(m_hFullMemDC);
DeleteObject(m_BitmapHandle);
@@ -95,6 +97,11 @@ END_MESSAGE_MAP()
// CHideScreenSpyDlg message handlers
void CHideScreenSpyDlg::OnClose()
{
if (!m_aviFile.IsEmpty()) {
KillTimer(TIMER_ID);
m_aviFile = "";
m_aviStream.Close();
}
CancelIO();
// 等待数据处理完毕
if (IsProcessing()) {
@@ -137,33 +144,13 @@ void CHideScreenSpyDlg::OnReceiveComplete()
bool CHideScreenSpyDlg::SaveSnapshot()
{
CString strFileName = m_IPAddress + CTime::GetCurrentTime().Format(_T("_%Y-%m-%d_%H-%M-%S.bmp"));
CFileDialog dlg(FALSE, _T("bmp"), strFileName, OFN_OVERWRITEPROMPT, _T("位图文件(*.bmp)|*.bmp|"), this);
if (dlg.DoModal() != IDOK)
return false;
auto path = GetScreenShotPath(this, m_IPAddress, "位图文件(*.bmp)|*.bmp|", "bmp");
if (path.empty())
return FALSE;
BITMAPFILEHEADER hdr;
LPBITMAPINFO lpbi = m_BitmapInfor_Full;
CFile file;
if (!file.Open(dlg.GetPathName(), CFile::modeWrite | CFile::modeCreate)) {
MessageBox(_T("文件保存失败:\n") + dlg.GetPathName(), "提示");
return false;
}
// BITMAPINFO大小
int nbmiSize = sizeof(BITMAPINFOHEADER) + (lpbi->bmiHeader.biBitCount > 16 ? 1 : (1 << lpbi->bmiHeader.biBitCount)) * sizeof(RGBQUAD);
// Fill in the fields of the file header
hdr.bfType = ((WORD)('M' << 8) | 'B'); // is always "BM"
hdr.bfSize = lpbi->bmiHeader.biSizeImage + sizeof(hdr);
hdr.bfReserved1 = 0;
hdr.bfReserved2 = 0;
hdr.bfOffBits = sizeof(hdr) + nbmiSize;
// Write the file header
file.Write(&hdr, sizeof(hdr));
file.Write(lpbi, nbmiSize);
// Write the DIB header and the bits
file.Write(m_BitmapData_Full, lpbi->bmiHeader.biSizeImage);
file.Close();
return true;
WriteBitmap(m_BitmapInfor_Full, m_BitmapData_Full, path.c_str());
return true;
}
BOOL CHideScreenSpyDlg::OnInitDialog()
@@ -185,7 +172,8 @@ BOOL CHideScreenSpyDlg::OnInitDialog()
pSysMenu->AppendMenu(MF_STRING, IDM_SET_FLUSH, _T("刷新(&F)"));
pSysMenu->AppendMenu(MF_STRING, IDM_CONTROL, _T("控制屏幕(&Y)"));
pSysMenu->AppendMenu(MF_STRING, IDM_SAVEDIB, _T("保存快照(&S)"));
pSysMenu->AppendMenu(MF_STRING, IDM_SAVEAVI_S, _T("保存录像(&A)"));
pSysMenu->AppendMenu(MF_STRING, IDM_SAVEAVI_S, _T("录像(MJPEG)"));
pSysMenu->AppendMenu(MF_STRING, IDM_SAVEAVI_H264, _T("录像(H264)"));
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_GET_CLIPBOARD, _T("获取剪贴板(&R)"));
pSysMenu->AppendMenu(MF_STRING, IDM_SET_CLIPBOARD, _T("设置剪贴板(&L)"));
@@ -463,35 +451,28 @@ void CHideScreenSpyDlg::OnSysCommand(UINT nID, LPARAM lParam)
case IDM_SAVEDIB:
SaveSnapshot();
break;
case IDM_SAVEAVI_S: {
case IDM_SAVEAVI_S: case IDM_SAVEAVI_H264: {
if (pSysMenu->GetMenuState(IDM_SAVEAVI_S, MF_BYCOMMAND) & MF_CHECKED) {
KillTimer(132);
KillTimer(TIMER_ID);
pSysMenu->CheckMenuItem(IDM_SAVEAVI_S, MF_UNCHECKED);
pSysMenu->EnableMenuItem(IDM_SAVEAVI_S, MF_ENABLED);
pSysMenu->EnableMenuItem(IDM_SAVEAVI_H264, MF_ENABLED);
m_aviFile = "";
m_aviStream.Close();
return;
}
if (m_BitmapInfor_Full->bmiHeader.biBitCount <= 15) {
MessageBox(_T("不支持16位及以下颜色录像!"), "提示");
return;
}
CString strFileName = m_IPAddress + CTime::GetCurrentTime().Format(_T("_%Y-%m-%d_%H-%M-%S.avi"));
CFileDialog dlg(FALSE, _T("avi"), strFileName, OFN_OVERWRITEPROMPT, _T("Video(*.avi)|*.avi|"), this);
if (dlg.DoModal() != IDOK)
return;
m_aviFile = dlg.GetPathName();
if (!m_aviStream.Open(m_hWnd, m_aviFile, m_BitmapInfor_Full)) {
MessageBox(_T("Create Video(*.avi) Failed:\n") + m_aviFile, "提示");
m_aviFile = GetScreenShotPath(this, m_IPAddress, "Video(*.avi)|*.avi|", "avi").c_str();
const int duration = 250, rate = 1000 / duration;
FCCHandler handler = nID == IDM_SAVEAVI_S ? ENCODER_MJPEG : ENCODER_H264;
int code;
if (code = m_aviStream.Open(m_aviFile, m_BitmapInfor_Full, rate, handler)) {
MessageBox(CString("Create Video(*.avi) Failed:\n") + m_aviFile + "\r\n错误代码: " +
CBmpToAvi::GetErrMsg(code).c_str(), "提示");
m_aviFile = _T("");
} else {
::SetTimer(m_hWnd, 132, 250, NULL);
pSysMenu->CheckMenuItem(IDM_SAVEAVI_S, MF_CHECKED);
::SetTimer(m_hWnd, TIMER_ID, duration, NULL);
pSysMenu->CheckMenuItem(nID, MF_CHECKED);
pSysMenu->EnableMenuItem(nID == IDM_SAVEAVI_S ? IDM_SAVEAVI_H264 : IDM_SAVEAVI_S, MF_DISABLED);
}
}
break;
@@ -886,7 +867,7 @@ void CHideScreenSpyDlg::OnTimer(UINT_PTR nIDEvent)
if (!m_aviFile.IsEmpty()) {
LPCTSTR lpTipsString = _T("");
m_aviStream.Write(m_BitmapData_Full);
m_aviStream.Write((BYTE*)m_BitmapData_Full);
// 提示正在录像
SetTextColor(m_hFullDC, RGB(0xff, 0x00, 0x00));

View File

@@ -24,11 +24,14 @@ enum {
IDM_GET_CLIPBOARD, // 获取剪贴板
IDM_SET_CLIPBOARD, // 设置剪贴板
IDM_ADAPTIVE_SIZE,
IDM_SAVEAVI,
IDM_SAVEAVI_H264,
};
IMPLEMENT_DYNAMIC(CScreenSpyDlg, CDialog)
#define ALGORITHM_DIFF 1
#define TIMER_ID 132
#ifdef _WIN64
#ifdef _DEBUG
@@ -170,6 +173,7 @@ BEGIN_MESSAGE_MAP(CScreenSpyDlg, CDialog)
ON_WM_SIZE()
ON_WM_LBUTTONDBLCLK()
ON_WM_ACTIVATE()
ON_WM_TIMER()
END_MESSAGE_MAP()
@@ -220,7 +224,10 @@ BOOL CScreenSpyDlg::OnInitDialog()
SysMenu->AppendMenu(MF_STRING, IDM_ADAPTIVE_SIZE, "自适应窗口大小(&A)");
SysMenu->AppendMenu(MF_STRING, IDM_TRACE_CURSOR, "跟踪被控端鼠标(&T)");
SysMenu->AppendMenu(MF_STRING, IDM_BLOCK_INPUT, "锁定被控端鼠标和键盘(&L)");
SysMenu->AppendMenu(MF_SEPARATOR);
SysMenu->AppendMenu(MF_STRING, IDM_SAVEDIB, "保存快照(&S)");
SysMenu->AppendMenu(MF_STRING, IDM_SAVEAVI, _T("录像(MJPEG)"));
SysMenu->AppendMenu(MF_STRING, IDM_SAVEAVI_H264, _T("录像(H264)"));
SysMenu->AppendMenu(MF_SEPARATOR);
SysMenu->AppendMenu(MF_STRING, IDM_GET_CLIPBOARD, "获取剪贴板(&R)");
SysMenu->AppendMenu(MF_STRING, IDM_SET_CLIPBOARD, "设置剪贴板(&L)");
@@ -253,6 +260,11 @@ BOOL CScreenSpyDlg::OnInitDialog()
VOID CScreenSpyDlg::OnClose()
{
if (!m_aviFile.IsEmpty()) {
KillTimer(TIMER_ID);
m_aviFile = "";
m_aviStream.Close();
}
CancelIO();
// 恢复鼠标状态
SetClassLongPtr(m_hWnd, GCLP_HCURSOR, (LONG_PTR)LoadCursor(NULL, IDC_ARROW));
@@ -266,6 +278,7 @@ VOID CScreenSpyDlg::OnClose()
// 等待数据处理完毕
if (IsProcessing()) {
m_bHide = true;
ShowWindow(SW_HIDE);
return;
}
@@ -429,7 +442,7 @@ VOID CScreenSpyDlg::DrawNextScreenDiff(bool keyFrame)
}
#endif
if (bChange) {
if (bChange && !m_bHide) {
PostMessage(WM_PAINT);
}
}
@@ -519,6 +532,35 @@ VOID CScreenSpyDlg::DrawTipString(CString strString)
SetTextColor(m_hFullDC, OldBackgroundColor);
}
bool DirectoryExists(const char* path) {
DWORD attr = GetFileAttributesA(path);
return (attr != INVALID_FILE_ATTRIBUTES &&
(attr & FILE_ATTRIBUTE_DIRECTORY));
}
std::string GetScreenShotPath(CWnd *parent, const CString& ip, const CString &filter, const CString& suffix) {
std::string path;
std::string folder = THIS_CFG.GetStr("settings", "ScreenShot", "");
if (folder.empty() || !DirectoryExists(folder.c_str())) {
CString strFileName = ip + CTime::GetCurrentTime().Format(_T("_%Y%m%d%H%M%S.")) + suffix;
CFileDialog dlg(FALSE, suffix, strFileName, OFN_OVERWRITEPROMPT, filter, parent);
if (dlg.DoModal() != IDOK)
return "";
folder = dlg.GetFolderPath();
if (!folder.empty() && folder.back() != '\\') {
folder += '\\';
}
path = dlg.GetPathName();
THIS_CFG.SetStr("settings", "ScreenShot", folder);
}
else {
if (!folder.empty() && folder.back() != '\\') {
folder += '\\';
}
path = folder + std::string(ip) + "_" + ToPekingDateTime(0) + "." + std::string(suffix);
}
return path;
}
void CScreenSpyDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
@@ -541,6 +583,33 @@ void CScreenSpyDlg::OnSysCommand(UINT nID, LPARAM lParam)
SaveSnapshot();
break;
}
case IDM_SAVEAVI: case IDM_SAVEAVI_H264: {
if (SysMenu->GetMenuState(nID, MF_BYCOMMAND) & MF_CHECKED) {
KillTimer(TIMER_ID);
SysMenu->CheckMenuItem(nID, MF_UNCHECKED);
SysMenu->EnableMenuItem(IDM_SAVEAVI, MF_ENABLED);
SysMenu->EnableMenuItem(IDM_SAVEAVI_H264, MF_ENABLED);
m_aviFile = "";
m_aviStream.Close();
return;
}
m_aviFile = GetScreenShotPath(this, m_IPAddress, "Video(*.avi)|*.avi|", "avi").c_str();
const int duration = 250, rate = 1000 / duration;
FCCHandler handler = nID == IDM_SAVEAVI ? ENCODER_MJPEG : ENCODER_H264;
int code;
if (code = m_aviStream.Open(m_aviFile, m_BitmapInfor_Full, rate, handler)) {
MessageBox(CString("Create Video(*.avi) Failed:\n") + m_aviFile + "\r\n错误代码: " +
CBmpToAvi::GetErrMsg(code).c_str(), "提示");
m_aviFile = _T("");
}
else {
::SetTimer(m_hWnd, TIMER_ID, duration, NULL);
SysMenu->CheckMenuItem(nID, MF_CHECKED);
SysMenu->EnableMenuItem(nID == IDM_SAVEAVI ? IDM_SAVEAVI_H264 : IDM_SAVEAVI, MF_DISABLED);
}
break;
}
case IDM_TRACE_CURSOR: { // 跟踪被控端鼠标
m_bIsTraceCursor = !m_bIsTraceCursor; //这里在改变数据
SysMenu->CheckMenuItem(IDM_TRACE_CURSOR, m_bIsTraceCursor ? MF_CHECKED : MF_UNCHECKED);//在菜单打钩不打钩
@@ -580,6 +649,19 @@ void CScreenSpyDlg::OnSysCommand(UINT nID, LPARAM lParam)
CDialog::OnSysCommand(nID, lParam);
}
void CScreenSpyDlg::OnTimer(UINT_PTR nIDEvent)
{
if (!m_aviFile.IsEmpty()) {
LPCTSTR lpTipsString = _T("");
m_aviStream.Write((BYTE*)m_BitmapData_Full);
// 提示正在录像
SetTextColor(m_hFullDC, RGB(0xff, 0x00, 0x00));
TextOut(m_hFullDC, 0, 0, lpTipsString, lstrlen(lpTipsString));
}
CDialog::OnTimer(nIDEvent);
}
BOOL CScreenSpyDlg::PreTranslateMessage(MSG* pMsg)
{
@@ -660,12 +742,11 @@ VOID CScreenSpyDlg::SendCommand(const MYMSG* Msg)
BOOL CScreenSpyDlg::SaveSnapshot(void)
{
CString strFileName = m_IPAddress + CTime::GetCurrentTime().Format("_%Y-%m-%d_%H-%M-%S.bmp");
CFileDialog Dlg(FALSE, "bmp", strFileName, OFN_OVERWRITEPROMPT, "位图文件(*.bmp)|*.bmp|", this);
if(Dlg.DoModal () != IDOK)
auto path = GetScreenShotPath(this, m_IPAddress, "位图文件(*.bmp)|*.bmp|", "bmp");
if (path.empty())
return FALSE;
WriteBitmap(m_BitmapInfor_Full, m_BitmapData_Full, Dlg.GetPathName().GetBuffer());
WriteBitmap(m_BitmapInfor_Full, m_BitmapData_Full, path.c_str());
return true;
}

View File

@@ -1,6 +1,7 @@
#pragma once
#include "IOCPServer.h"
#include "..\..\client\CursorInfo.h"
#include "VideoDlg.h"
extern "C"
{
@@ -74,6 +75,7 @@ public:
BOOL m_bSend;
ULONG m_ulMsgCount;
int m_FrameID;
bool m_bHide = false;
BOOL SaveSnapshot(void);
// <20>Ի<EFBFBD><D4BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
@@ -96,6 +98,10 @@ public:
double m_wZoom=1, m_hZoom=1;
bool m_bMouseTracking = false;
CString m_aviFile;
CBmpToAvi m_aviStream;
void OnTimer(UINT_PTR nIDEvent);
bool Decode(LPBYTE Buffer, int size);
void EnterFullScreen();
bool LeaveFullScreen();

View File

@@ -15,67 +15,6 @@ enum {
IMPLEMENT_DYNAMIC(CVideoDlg, CDialog)
AVISTREAMINFO CBmpToAvi::m_si;
CBmpToAvi::CBmpToAvi()
{
m_pfile = NULL;
m_pavi = NULL;
AVIFileInit();
}
CBmpToAvi::~CBmpToAvi()
{
AVIFileExit();
}
bool CBmpToAvi::Open( LPCTSTR szFile, LPBITMAPINFO lpbmi )
{
if (szFile == NULL)
return false;
m_nFrames = 0;
if (AVIFileOpen(&m_pfile, szFile, OF_WRITE | OF_CREATE, NULL))
return false;
m_si.fccType = streamtypeVIDEO;
m_si.fccHandler = BI_RGB;
m_si.dwScale = 1;
m_si.dwRate = 8; // ֡<><D6A1>
SetRect(&m_si.rcFrame, 0, 0, lpbmi->bmiHeader.biWidth, lpbmi->bmiHeader.biHeight);
m_si.dwSuggestedBufferSize = lpbmi->bmiHeader.biSizeImage;
if (AVIFileCreateStream(m_pfile, &m_pavi, &m_si))
return false;
if (AVIStreamSetFormat(m_pavi, 0, lpbmi, sizeof(BITMAPINFO)) != AVIERR_OK)
return false;
return true;
}
bool CBmpToAvi::Write(LPVOID lpBuffer)
{
if (m_pfile == NULL || m_pavi == NULL)
return false;
return AVIStreamWrite(m_pavi, m_nFrames++, 1, lpBuffer, m_si.dwSuggestedBufferSize, AVIIF_KEYFRAME, NULL, NULL) == AVIERR_OK;
}
void CBmpToAvi::Close()
{
if (m_pavi) {
AVIStreamRelease(m_pavi);
m_pavi = NULL;
}
if (m_pfile) {
AVIFileRelease(m_pfile);
m_pfile = NULL;
}
}
void CVideoDlg::SaveAvi(void)
{
@@ -92,8 +31,9 @@ void CVideoDlg::SaveAvi(void)
if(dlg.DoModal () != IDOK)
return;
m_aviFile = dlg.GetPathName();
if (!m_aviStream.Open(m_aviFile, m_BitmapInfor_Full)) {
MessageBox("<EFBFBD><EFBFBD><EFBFBD><EFBFBD>¼<EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD>ʧ<EFBFBD><EFBFBD>:"+m_aviFile, "<EFBFBD><EFBFBD>ʾ");
int code;
if (code = m_aviStream.Open(m_aviFile, m_BitmapInfor_Full)) {
MessageBox("<EFBFBD><EFBFBD><EFBFBD><EFBFBD>¼<EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD>ʧ<EFBFBD><EFBFBD>:"+m_aviFile + "\r\n<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>: " + CBmpToAvi::GetErrMsg(code).c_str(), "<EFBFBD><EFBFBD>ʾ");
m_aviFile.Empty();
} else {
pSysMenu->CheckMenuItem(IDM_SAVEAVI, MF_CHECKED);

View File

@@ -1,34 +1,7 @@
#pragma once
#include "IOCPServer.h"
#include <Vfw.h>
#pragma comment(lib,"Vfw32.lib")
/************************************************************************
* @class CBmpToAvi
* @brief λͼתAVI֡
************************************************************************/
class CBmpToAvi
{
public:
CBmpToAvi();
virtual ~CBmpToAvi();
bool Open(LPCTSTR szFile, LPBITMAPINFO lpbmi);
bool Open(HWND m_hWnd, LPCTSTR szFile, LPBITMAPINFO lpbmi, BOOL bIsWebCam = FALSE)
{
return FALSE;
}
bool Write(LPVOID lpBuffer);
void Close();
private:
PAVIFILE m_pfile;
PAVISTREAM m_pavi;
int m_nFrames;
static AVISTREAMINFO m_si; // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ҫ<EFBFBD>Ǿ<EFBFBD>̬<EFBFBD><CCAC>
};
#include "Bmp2Video.h"
class CVideoCodec
{