若干种窗口画面的捕获方法

在直播项目中 需要捕获某个窗口的画面并共享 总结了如下几种场景中窗口的捕获方法

 

1、dc拷贝(BitBlt、PrintWindow)

这是最基本的方法 直接拿到窗口dc 然后从dc中拷贝窗口画面 可优先使用PrintWindow 因为该API可以捕获窗口超出桌面范围的区域。需要注意的是,PrintWindow会触发目标窗口执行WM_PAINT,并且执行完毕后才会返回,所以可能造成目标窗口频繁刷新,或调用PrintWindow会耗时,所以不推荐使用这个API。

关键词:GetDC   BitBlt  GetDIBits

 

2、具有WS_EX_LAYERED属性的窗口

比如酷狗歌词窗口、BigFoot界面(魔兽世界大脚)

这种窗口第一种方法是拿不到画面的(拿到的画面是黑色)

只能通过“dll进程注入+API函数hook”的方法 在hook了的API(UpdateLayeredWindow)的回调函数中

通过参数获取到窗口画面

 

3、使用OpenGL或D3D渲染的窗口

不少游戏(CS/QQ桌球/英雄联盟)和播放器的界面渲染 就是使用OpenGL或者D3D进行渲染的

与方法2一样 也只能通过“dll进程注入+API函数hook”的方法进行画面捕获

其中OpenGL的hook比较简单 直接hook渲染函数wglSwapBuffers并在回调中解析数据就行了

但是D3D 因为版本太多 针对不同的游戏 需要hook对应的D3D版本:D3D9 D3D10 D3D11 D3D12

针对不同版本的D3D hook对应的渲染函数 实现起来工作量相对较多

禁用Aero

不过针对WIN7及以下版本的系统 如果非要捕获这类窗口 也可以采用禁用Aero的方式(具体请参考函数DwmEnableComposition)

OBS就实现了这种方法(在obs的高级设置中会有是否禁用Aero的选项)  但是这种方法会有两个弊端:

a、调用API禁用或恢复玻璃效果时 整个桌面会黑一下 影响用户体验

b、如果有其他窗口遮挡 捕获的画面会有问题:会将遮挡窗口的画面也捕获进去

屏幕dc

不过个人觉得 采用禁用Aero的方法 还不如直接用屏幕dc的方式实现 

与方法1相似 不过创建dc的时候 不是创建游戏窗口的dc 而是创建一个屏幕dc 之后截取游戏在屏幕对应区域的dc数据 即可捕获到游戏画面

不过这种方法与禁用Aero一样 如果有别的窗口遮挡游戏画面 捕获的画面就不对 所以必须要求待捕获的窗口在最前端

 

4、硬件加速的窗口

测试项目时发现 WIN10中chrome界面一直无法捕获 (拿到的画面是黑色)调查发现其并没有使用openGL或者d3d渲染界面(进程中没有加载这些dll)

打开设置界面后 发现有一个硬件加速的选项 取消后 再按照方法1进行捕获 画面就正常了

(这类窗口具体无法捕获的原因 我也暂时还不清楚)

 

---------------------------------------------------------------------------------------------------------
 

方法2和3中提到了“dll进程注入+API函数hook”

其中dll注入的方法有两种:创建远端线程(CreateRemoteThread)、采用钩子注入(SetWindowsHookEx)

API函数hook的方法有两种:修改函数跳转地址、修改虚函数表 

 

 

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
捕获SDL2窗口画面并录制视频,你可以结合SDL2和FFmpeg库来实现。首先,你需要使用SDL2来创建窗口和渲染器,并在每一帧绘制完窗口内容后,将渲染器的像素数据复制到FFmpeg的AVFrame中。然后,你可以使用FFmpeg来编码并写入视频帧。 以下是一个简单的示例代码,展示了如何使用SDL2和FFmpeg来捕获窗口画面并录制视频: ```cpp extern "C" { #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libavutil/imgutils.h> } #include <SDL2/SDL.h> int main() { av_register_all(); // 初始化SDL2 SDL_Init(SDL_INIT_VIDEO); SDL_Window* window = SDL_CreateWindow("Capture Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, 0); SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); SDL_Texture* texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 640, 480); // 创建FFmpeg相关的结构体 AVFormatContext* formatContext; avformat_alloc_output_context2(&formatContext, nullptr, nullptr, "output.mp4"); AVOutputFormat* outputFormat = formatContext->oformat; AVCodec* codec = avcodec_find_encoder(AV_CODEC_ID_H264); AVStream* stream = avformat_new_stream(formatContext, codec); AVCodecContext* codecContext = stream->codec; codecContext->codec_id = AV_CODEC_ID_H264; codecContext->codec_type = AVMEDIA_TYPE_VIDEO; codecContext->pix_fmt = AV_PIX_FMT_YUV420P; codecContext->width = 640; codecContext->height = 480; codecContext->time_base = AVRational{1, 25}; codecContext->bit_rate = 400000; avio_open(&formatContext->pb, "output.mp4", AVIO_FLAG_WRITE); avformat_write_header(formatContext, nullptr); AVFrame* frame = av_frame_alloc(); frame->format = codecContext->pix_fmt; frame->width = codecContext->width; frame->height = codecContext->height; av_frame_get_buffer(frame, 32); // 开始录制视频,逐帧写入 for (int i = 0; i < 250; ++i) { av_frame_make_writable(frame); // 渲染窗口内容到纹理 SDL_RenderClear(renderer); // 这里是你绘制窗口内容的代码 // ... SDL_UpdateTexture(texture, nullptr, nullptr, 0); SDL_RenderCopy(renderer, texture, nullptr, nullptr); SDL_RenderPresent(renderer); // 将纹理像素数据复制到AVFrame中 uint8_t* pixels = nullptr; int pitch = 0; SDL_LockTexture(texture, nullptr, reinterpret_cast<void**>(&pixels), &pitch); for (int y = 0; y < codecContext->height; ++y) { memcpy(frame->data[0] + y * frame->linesize[0], pixels + y * pitch, codecContext->width * 4); } SDL_UnlockTexture(texture); // 编码并写入视频帧 AVPacket packet; av_init_packet(&packet); packet.data = nullptr; packet.size = 0; avcodec_send_frame(codecContext, frame); avcodec_receive_packet(codecContext, &packet); av_interleaved_write_frame(formatContext, &packet); av_packet_unref(&packet); } av_write_trailer(formatContext); av_frame_free(&frame); avio_close(formatContext->pb); avformat_free_context(formatContext); // 释放SDL2资源 SDL_DestroyTexture(texture); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); return 0; } ``` 在上述示例代码中,我们使用SDL2创建了一个窗口和渲染器,并使用一个纹理来绘制窗口内容。然后,我们将纹理的像素数据复制到AVFrame中,并使用FFmpeg进行编码并写入视频帧。 请注意,上述代码只是一个简单的示例,你可能需要根据你的实际需求进行适当修改。另外,为了成功编译运行此代码,你需要安装SDL2和FFmpeg库,并将它们链接到你的项目中。 希望这个示例能对你有所帮助!如果你有任何疑问,请随时提出。祝你成功开发SDL2录制窗口画面的功能!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值