AVIFileOpenW与AVIStreamSetFormat参数误用及屏幕录制AVI写入问题求助
问题分析与修正方案
你的代码存在几个关键问题,直接导致AVI文件写入失败,以下是问题点和修复后的实现:
核心错误点
- AVIFileOpenW参数类型不匹配:该函数需要传入
PAVIFILE*类型变量,你错误传入了PAVISTREAM*,这是完全不同的对象类型。 - 未正确创建视频流:AVI文件需要先打开文件对象,再创建对应的视频流,不能直接操作流对象。
- 帧存储逻辑错误:你把同一个
hBitmap指针重复存入buffer,所有帧最终都是最后一帧的内容,每个帧需要创建独立的HBITMAP来存储。 - AVI格式与写入参数错误:
AVIStreamSetFormat需要传入完整的BITMAPINFO结构,而非HBITMAP;AVIStreamWrite的帧数据大小、参数含义都不符合要求。 - 无帧率控制:当前循环会尽可能快地截图,无法保证60fps的录制帧率。
修复后的完整代码
#include <Windows.h> #include <Vfw.h> #include <iostream> #include <vector> #include <chrono> #include <thread> #pragma comment(lib, "vfw32.lib") const int BUFFER_SIZE = 30 * 60; // 30秒@60fps const int FPS = 60; const int FRAME_DELAY = 1000 / FPS; // 每帧间隔毫秒数 // 释放HBITMAP并清空指针 void SafeDeleteBitmap(HBITMAP& hBmp) { if (hBmp) { DeleteObject(hBmp); hBmp = nullptr; } } int main() { // 获取主显示器尺寸 HMONITOR hMonitor = MonitorFromWindow(NULL, MONITOR_DEFAULTTOPRIMARY); MONITORINFO info{}; info.cbSize = sizeof(MONITORINFO); GetMonitorInfo(hMonitor, &info); int width = info.rcMonitor.right - info.rcMonitor.left; int height = info.rcMonitor.bottom - info.rcMonitor.top; HDC hScreenDC = GetDC(NULL); HDC hMemDC = CreateCompatibleDC(hScreenDC); std::vector<HBITMAP> buffer(BUFFER_SIZE, nullptr); int index = 0; // 录制循环:控制60fps,存储每帧独立的HBITMAP while (true) { auto start = std::chrono::steady_clock::now(); // 为当前帧创建独立的位图 HBITMAP hFrameBmp = CreateCompatibleBitmap(hScreenDC, width, height); HBITMAP hOldBmp = (HBITMAP)SelectObject(hMemDC, hFrameBmp); // 截取屏幕到当前帧位图 BitBlt(hMemDC, 0, 0, width, height, hScreenDC, 0, 0, SRCCOPY); // 恢复MemDC的原有位图,保存当前帧到buffer SelectObject(hMemDC, hOldBmp); SafeDeleteBitmap(buffer[index]); buffer[index] = hFrameBmp; index = (index + 1) % BUFFER_SIZE; // 检查退出按键 if (GetAsyncKeyState(VK_ESCAPE) & 0x8000) { break; } // 控制帧率 auto end = std::chrono::steady_clock::now(); auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count(); if (elapsed < FRAME_DELAY) { std::this_thread::sleep_for(std::chrono::milliseconds(FRAME_DELAY - elapsed)); } } // AVI文件写入流程 PAVIFILE pFile = nullptr; PAVISTREAM pStream = nullptr; AVISTREAMINFO streamInfo{}; AVIFileInit(); // 1. 打开AVI文件 if (AVIFileOpenW(&pFile, L"screen_recording.avi", OF_WRITE | OF_CREATE, NULL) != AVIERR_OK) { std::cerr << "Failed to open AVI file" << std::endl; goto Cleanup; } // 2. 设置视频流信息 streamInfo.fccType = streamtypeVIDEO; streamInfo.fccHandler = mmioFOURCC('D', 'I', 'B', ' '); // 未压缩DIB格式 streamInfo.dwScale = 1; streamInfo.dwRate = FPS; streamInfo.dwSuggestedBufferSize = width * height * 4; // 32位色深的帧大小 SetRect(&streamInfo.rcFrame, 0, 0, width, height); // 3. 创建视频流 if (AVIFileCreateStream(pFile, &pStream, &streamInfo) != AVIERR_OK) { std::cerr << "Failed to create AVI stream" << std::endl; goto Cleanup; } // 4. 设置视频流格式:构造BITMAPINFO BITMAPINFO bmpInfo{}; bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmpInfo.bmiHeader.biWidth = width; bmpInfo.bmiHeader.biHeight = -height; // 负数值表示顶行在前(符合AVI规范) bmpInfo.bmiHeader.biPlanes = 1; bmpInfo.bmiHeader.biBitCount = 32; bmpInfo.bmiHeader.biCompression = BI_RGB; bmpInfo.bmiHeader.biSizeImage = width * height * 4; if (AVIStreamSetFormat(pStream, 0, &bmpInfo, sizeof(BITMAPINFO)) != AVIERR_OK) { std::cerr << "Failed to set stream format" << std::endl; goto Cleanup; } // 5. 写入所有有效帧 int startIndex = index; // 找到buffer中最早的帧 for (int i = 0; i < BUFFER_SIZE; i++) { int frameIdx = (startIndex + i) % BUFFER_SIZE; HBITMAP hFrame = buffer[frameIdx]; if (!hFrame) continue; // 获取帧像素数据 BITMAP bmp{}; GetObject(hFrame, sizeof(BITMAP), &bmp); char* pFrameData = new char[bmp.bmWidthBytes * bmp.bmHeight]; BITMAPINFOHEADER bi{}; bi.biSize = sizeof(BITMAPINFOHEADER); bi.biWidth = bmp.bmWidth; bi.biHeight = -bmp.bmHeight; bi.biPlanes = 1; bi.biBitCount = 32; bi.biCompression = BI_RGB; GetDIBits(hMemDC, hFrame, 0, bmp.bmHeight, pFrameData, (BITMAPINFO*)&bi, DIB_RGB_COLORS); // 写入帧数据 if (AVIStreamWrite(pStream, i, 1, pFrameData, bmp.bmWidthBytes * bmp.bmHeight, AVIIF_KEYFRAME, NULL, NULL) != AVIERR_OK) { std::cerr << "Failed to write frame " << i << std::endl; } delete[] pFrameData; } Cleanup: // 资源释放 if (pStream) AVIStreamClose(pStream); if (pFile) AVIFileClose(pFile); AVIFileExit(); // 释放所有存储的帧 for (auto& hBmp : buffer) { SafeDeleteBitmap(hBmp); } DeleteDC(hMemDC); ReleaseDC(NULL, hScreenDC); return 0; }
关键修复说明
- AVI对象模型修正:严格遵循
AVIFileInit()→AVIFileOpenW()(操作PAVIFILE)→AVIFileCreateStream()(创建PAVISTREAM)→ 写入 → 关闭流 → 关闭文件 →AVIFileExit()的流程。 - 独立帧存储:每帧创建新的
HBITMAP,避免所有帧指向同一个位图对象。 - 帧率控制:用
chrono和thread实现固定60fps的录制间隔。 - 正确的格式设置:使用
BITMAPINFO结构描述视频格式,注意biHeight设为负数以符合AVI的顶行优先规范。 - 帧数据提取:通过
GetDIBits()将HBITMAP转换为DIB格式数据,这是AVIStreamWrite可接受的像素数据格式。 - 安全资源释放:添加
SafeDeleteBitmap工具函数,确保所有HBITMAP和AVI对象被正确释放,避免内存泄漏。
内容的提问来源于stack exchange,提问作者joeymalvinni
相关产品推荐
相关产品推荐

