-
2021-12-09 22:10:54
之前写过两篇ffmpeg录制桌面,读者可以翻看我的博客,但是都存在着问题,尤其当一个进程里面,自己创建几个线程干其他事情时,出错概率大大增加,
ret = avcodec_send_frame(pCodecEncodeCtx_Video, pFrameYUV); if (ret == AVERROR(EAGAIN)) { continue; } ret = avcodec_receive_packet(pCodecEncodeCtx_Video, &packet); if (ret == AVERROR(EAGAIN)) { continue; }
这是个编码的过程,其中avcodec_receive_packet的返回值经常是AVERROR(EAGAIN),而我源头是通过av_read_frame(pFormatCtx_Video, &packet)读取的视频帧,最终的结果导致视频里面的时间比实际录制的时间要短。
这让我很头疼,无意中发现了一个人的博客,自己用gdi抓图,博客地址如下:
https://blog.csdn.net/Donghui_Luo/article/details/88383313但是这篇博客存在着一点瑕疵,该博客设置帧率为15,我自己稍微修改了点代码,修改录制时间为1分钟,则最终发现,生成的mp4文件中的视频时长不止1分钟,大概在1分钟17秒的样子,经过分析,其抓图时,一秒钟不止抓15张,所以导致最终的时间大于1分钟。如果其线程得不到充分运行,比如一分钟才能抓5张,则对应的最终mp4的文件视频时长是20秒。
所以需要自己控制抓图频率,线程性能太好,一秒钟抓的图片数量大于帧率时,需要适当的进行Sleep,如果线程性能太差,比如无论如何,也无法做到1秒钟抓15张,则可以降低编码器的帧率。
为方便演示,本人将编码器的帧率设置为10,核心逻辑修改如下:
DWORD dwBeginTime = ::GetTickCount(); for (;;) { BYTE* frameimage = ccs->CaptureImage(); RGB24_TO_YUV420(frameimage, width, height, outbuffer); //Sframe->pkt_dts = frame->pts = frameNumber * avCodecCtx_Out->time_base.num * avStream->time_base.den / (avCodecCtx_Out->time_base.den * avStream->time_base.num); frame->pkt_dts = frame->pts = av_rescale_q_rnd(frameNumber, avCodecCtx_Out->time_base, avStream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX)); frame->pkt_duration = 0; frame->pkt_pos = -1; ret = avcodec_send_frame(avCodecCtx_Out, frame); if (ret < 0) continue; ret = avcodec_receive_packet(avCodecCtx_Out, packet); if (ret < 0) continue; static DWORD dwInitTime = ::GetTickCount(); if (packet->size > 0) { //av_packet_rescale_ts(packet, avCodecCtx_Out->time_base, avStream->time_base); av_write_frame(avFormCtx_Out, packet); frameNumber++; printf("录入第%d帧....\n", frameNumber); } DWORD dwCurrentTime = ::GetTickCount(); if (dwCurrentTime - dwInitTime > 60000) { break; } int dwPassedMillSeconds = dwCurrentTime - dwBeginTime; int dwDiff = frameNumber * 100 - dwPassedMillSeconds; if (dwDiff > 0) { Sleep(dwDiff); } }
本人在抓图开始前记录下时间dwBeginTime,然后每次抓图后,计算出从开始到现在的抓图时间dwPassedMillSeconds,因为帧率是10,所以理论上,每100毫秒抓取一张,dwDiff用于表示实际的时差。
本人乐于直接将代码贴在博客内,便于本人日后查看。
总共是3个文件,如下图所示:
现在本人依次粘贴它们的代码
CaptureScreen.cpp代码如下://#include "stdafx.h" #include "CaptureScreen.h" CCaptureScreen::CCaptureScreen(void) { m_hdib = NULL; m_hSavedCursor = NULL; hScreenDC = NULL; hMemDC = NULL; hbm = NULL; m_width = 1920; m_height = 1080; FetchCursorHandle(); } // // 释放资源 // CCaptureScreen::~CCaptureScreen(void) { DeleteObject(hbm); if (m_hdib){ free(m_hdib); m_hdib = NULL; } if (hScreenDC){ ::ReleaseDC(NULL, hScreenDC); } if (hMemDC) { DeleteDC(hMemDC); } if (hbm) { DeleteObject(hbm); } } // // 初始化 // int CCaptureScreen::Init(int& src_VideoWidth, int& src_VideoHeight) { hScreenDC = ::GetDC(GetDesktopWindow()); if (hScreenDC == NULL) return 0; int m_nMaxxScreen = GetDeviceCaps(hScreenDC, HORZRES); int m_nMaxyScreen = GetDeviceCaps(hScreenDC, VERTRES); hMemDC = ::CreateCompatibleDC(hScreenDC); if (hMemDC == NULL) return 0; m_width = m_nMaxxScreen; m_height = m_nMaxyScreen; if (!m_hdib){ m_hdib = (PRGBTRIPLE)malloc(m_width * m_height * 3);//24位图像大小 } //位图头信息结构体 pbi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); pbi.bmiHeader.biWidth = m_width; pbi.bmiHeader.biHeight = m_height; pbi.bmiHeader.biPlanes = 1; pbi.bmiHeader.biBitCount = 24; pbi.bmiHeader.biCompression = BI_RGB; src_VideoWidth = m_width; src_VideoHeight = m_height; hbm = CreateCompatibleBitmap(hScreenDC, m_width, m_height); SelectObject(hMemDC, hbm); wLineLen = ((m_width * 24 + 31) & 0xffffffe0) / 8; wColSize = sizeof(RGBQUAD)* ((24 <= 8) ? 1 << 24 : 0); dwSize = (DWORD)(UINT)wLineLen * (DWORD)(UINT)m_height; return 1; } //抓取屏幕数据 BYTE* CCaptureScreen::CaptureImage() { VOID* alpbi = CaptureScreenFrame(0, 0, m_width, m_height); return (BYTE*)(alpbi); } void* CCaptureScreen::CaptureScreenFrame(int left, int top, int width, int height) { if (hbm == NULL || hMemDC == NULL || hScreenDC == NULL) return NULL; BitBlt(hMemDC, 0, 0, width, height, hScreenDC, left, top, SRCCOPY); /*-------------------------捕获鼠标-------------------------------*/ { POINT xPoint; GetCursorPos(&xPoint); HCURSOR hcur = FetchCursorHandle(); xPoint.x -= left; xPoint.y -= top; ICONINFO iconinfo; BOOL ret; ret = GetIconInfo(hcur, &iconinfo); if (ret){ xPoint.x -= iconinfo.xHotspot; xPoint.y -= iconinfo.yHotspot; if (iconinfo.hbmMask) DeleteObject(iconinfo.hbmMask); if (iconinfo.hbmColor) DeleteObject(iconinfo.hbmColor); } /*画鼠标*/ ::DrawIcon(hMemDC, xPoint.x, xPoint.y, hcur); } //动态分配的内存 PRGBTRIPLE hdib = m_hdib; if (!hdib) return hdib; GetDIBits(hMemDC, hbm, 0, m_height, hdib, (LPBITMAPINFO)&pbi, DIB_RGB_COLORS); return hdib; } // // 获取窗体鼠标光标 // HCURSOR CCaptureScreen::FetchCursorHandle() { if (m_hSavedCursor == NULL) { m_hSavedCursor = GetCursor(); } return m_hSavedCursor; }
CaptureScreen.h的代码如下:
#ifndef _CCAPTURE_SCREEN_HH #define _CCAPTURE_SCREEN_HH #include<time.h> #include <d3d9.h> #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <windows.h> #include <tchar.h> #include <winbase.h> #include <winreg.h> #include <Strsafe.h> // // ---抓屏类---- // class CCaptureScreen { public: CCaptureScreen(void); ~CCaptureScreen(void); public: /*-----------定义外部调用函数-----------*/ int Init(int&, int&);//初始化 BYTE* CaptureImage(); //抓取屏幕 private: /*-----------定义内部调用函数-----------*/ void* CaptureScreenFrame(int, int, int, int);//抓屏 HCURSOR FetchCursorHandle(); //获取鼠标光标 private: /*-----------定义私有变量-----------*/ int m_width; int m_height; UINT wLineLen; DWORD dwSize; DWORD wColSize; //设备句柄 HDC hScreenDC; HDC hMemDC; //图像RGB内存缓存 PRGBTRIPLE m_hdib; //位图头信息结构体 BITMAPINFO pbi; HBITMAP hbm; //鼠标光标 HCURSOR m_hSavedCursor; }; #endif //--_CCAPTURE_SCREEN_HH
FfmpegVideoCaptureWithGdi.cpp的代码如下:
// RecordingScreen.cpp : 定义控制台应用程序的入口点。 // //#include "stdafx.h" #include "CaptureScreen.h" extern "C" { #include <libavutil/opt.h> #include <libavutil/mathematics.h> #include <libavutil\time.h> #include <libavformat/avformat.h> #include <libswscale/swscale.h> #include <libswresample/swresample.h> #include <libavutil/imgutils.h> #include <libavcodec/avcodec.h> #include <libavdevice\avdevice.h> #pragma comment(lib, "avcodec.lib") #pragma comment(lib, "avformat.lib") #pragma comment(lib, "avutil.lib") #pragma comment(lib, "avdevice.lib") #pragma comment(lib, "avfilter.lib") #pragma comment(lib, "postproc.lib") #pragma comment(lib, "swresample.lib") #pragma comment(lib, "swscale.lib") } //#include <ipp.h> #include <chrono> //signed int Bgr2YuvI420(const BYTE* srcBgr, int image_width, int image_height, BYTE* dstYuvI420) //{ // IppStatus ipp_status; // // int srcStep = image_width * 3; // int dstYStep = image_width; // int dstCbCrStep = image_width; // IppiSize roiSize = { image_width, image_height }; // // const Ipp8u* pSrc = (Ipp8u*)srcBgr; // // Ipp8u *pDstY = (Ipp8u*)dstYuvI420; // Ipp8u *pDstU = (Ipp8u*)&dstYuvI420[image_width * image_height]; // Ipp8u *pDstV = (Ipp8u*)&dstYuvI420[image_width * image_height * 5 / 4]; // Ipp8u *pDst[3]; // pDst[0] = pDstY; // pDst[1] = pDstU; // pDst[2] = pDstV; // int dstStep[3] = { image_width, image_width / 2, image_width / 2 }; // // ipp_status = ippiBGRToYCbCr420_8u_C3P3R(pSrc, srcStep, pDst, dstStep, roiSize); // // return ipp_status; //} unsigned char clip_value(unsigned char x, unsigned char min_val, unsigned char max_val) { if (x > max_val) { return max_val; } else if (x < min_val) { return min_val; } else { return x; } } //RGB to YUV420 bool RGB24_TO_YUV420(unsigned char *RgbBuf, int w, int h, unsigned char *yuvBuf) { unsigned char*ptrY, *ptrU, *ptrV, *ptrRGB; memset(yuvBuf, 0, w*h * 3 / 2); ptrY = yuvBuf; ptrU = yuvBuf + w * h; ptrV = ptrU + (w*h * 1 / 4); unsigned char y, u, v, r, g, b; for (int j = h - 1; j >= 0; j--) { ptrRGB = RgbBuf + w * j * 3; for (int i = 0; i < w; i++) { b = *(ptrRGB++); g = *(ptrRGB++); r = *(ptrRGB++); y = (unsigned char)((66 * r + 129 * g + 25 * b + 128) >> 8) + 16; u = (unsigned char)((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128; v = (unsigned char)((112 * r - 94 * g - 18 * b + 128) >> 8) + 128; *(ptrY++) = clip_value(y, 0, 255); if (j % 2 == 0 && i % 2 == 0) { *(ptrU++) = clip_value(u, 0, 255); } else { if (i % 2 == 0) { *(ptrV++) = clip_value(v, 0, 255); } } } } return true; } DWORD WINAPI ScreenCapThreadProc(LPVOID lpParam) { CCaptureScreen* ccs = new CCaptureScreen(); int width = 0; int height = 0; ccs->Init(width, height); AVFormatContext* avFormCtx_Out; AVCodecContext* avCodecCtx_Out; AVCodec* avCodec; AVStream* avStream; AVFrame* frame; AVPacket* packet; int frameRate = 10; int ret = 0; const char* filename = "out.mp4"; ret = avformat_alloc_output_context2(&avFormCtx_Out, NULL, NULL, filename); if (ret < 0) { printf("Init avformat object is faild! \n"); return 0; } avCodec = (AVCodec *)avcodec_find_encoder(avFormCtx_Out->oformat->video_codec); if (!avCodec) { printf("Init avCodec object is faild! \n"); return 0; } avCodecCtx_Out = avcodec_alloc_context3(avCodec); if (!avCodecCtx_Out) { printf("Init avCodecCtx_Out object is faild! \n"); return 0; } avStream = avformat_new_stream(avFormCtx_Out, avCodec); if (!avStream) { printf("Init avStream object is faild! \n"); return 0; } avCodecCtx_Out->flags |= AV_CODEC_FLAG_QSCALE; avCodecCtx_Out->bit_rate = 4000000; avCodecCtx_Out->rc_min_rate = 4000000; avCodecCtx_Out->rc_max_rate = 4000000; avCodecCtx_Out->bit_rate_tolerance = 4000000; avCodecCtx_Out->time_base.den = frameRate; avCodecCtx_Out->time_base.num = 1; avCodecCtx_Out->width = width; avCodecCtx_Out->height = height; //pH264Encoder->pCodecCtx->frame_number = 1; avCodecCtx_Out->gop_size = 12; avCodecCtx_Out->max_b_frames = 0; avCodecCtx_Out->thread_count = 4; avCodecCtx_Out->pix_fmt = AV_PIX_FMT_YUV420P; avCodecCtx_Out->codec_id = AV_CODEC_ID_H264; avCodecCtx_Out->codec_type = AVMEDIA_TYPE_VIDEO; av_opt_set(avCodecCtx_Out->priv_data, "b-pyramid", "none", 0); av_opt_set(avCodecCtx_Out->priv_data, "preset", "superfast", 0); av_opt_set(avCodecCtx_Out->priv_data, "tune", "zerolatency", 0); if (avFormCtx_Out->oformat->flags & AVFMT_GLOBALHEADER) avCodecCtx_Out->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; ret = avcodec_open2(avCodecCtx_Out, avCodec, NULL); if (ret < 0) { printf("Open avcodec is faild! \n"); return 0; } avcodec_parameters_from_context(avStream->codecpar, avCodecCtx_Out); if (!(avFormCtx_Out->oformat->flags & AVFMT_NOFILE)) { ret = avio_open(&avFormCtx_Out->pb, filename, AVIO_FLAG_WRITE); if (ret < 0) { printf("Open file is faild! \n"); return 0; } } ret = avformat_write_header(avFormCtx_Out, NULL); if (ret < 0) { printf("write header is faild! \n"); return 0; } frame = av_frame_alloc(); if (!frame) { printf("Init frame is faild! \n"); return 0; } frame->format = AV_PIX_FMT_YUV420P; frame->width = width; frame->height = height; LONG64 frameSize = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, 1920, 1080, 1); BYTE* outbuffer = new BYTE[frameSize]; ret = av_image_fill_arrays(frame->data, frame->linesize, outbuffer, AV_PIX_FMT_YUV420P, 1920, 1080, 1); if (ret < 0) { printf("av_image_fill_arrays is faild! \n"); return 0; } packet = av_packet_alloc(); //av_init_packet(packet); if (!packet) { printf("packet is faild! \n"); return 0; } int frameNumber = 0; int got_packet = 0; DWORD dwBeginTime = ::GetTickCount(); for (;;) { BYTE* frameimage = ccs->CaptureImage(); RGB24_TO_YUV420(frameimage, width, height, outbuffer); //Sframe->pkt_dts = frame->pts = frameNumber * avCodecCtx_Out->time_base.num * avStream->time_base.den / (avCodecCtx_Out->time_base.den * avStream->time_base.num); frame->pkt_dts = frame->pts = av_rescale_q_rnd(frameNumber, avCodecCtx_Out->time_base, avStream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX)); frame->pkt_duration = 0; frame->pkt_pos = -1; ret = avcodec_send_frame(avCodecCtx_Out, frame); if (ret < 0) continue; ret = avcodec_receive_packet(avCodecCtx_Out, packet); if (ret < 0) continue; static DWORD dwInitTime = ::GetTickCount(); if (packet->size > 0) { //av_packet_rescale_ts(packet, avCodecCtx_Out->time_base, avStream->time_base); av_write_frame(avFormCtx_Out, packet); frameNumber++; printf("录入第%d帧....\n", frameNumber); } DWORD dwCurrentTime = ::GetTickCount(); if (dwCurrentTime - dwInitTime > 60000) { break; } int dwPassedMillSeconds = dwCurrentTime - dwBeginTime; int dwDiff = frameNumber * 100 - dwPassedMillSeconds; if (dwDiff > 0) { Sleep(dwDiff); } } av_write_trailer(avFormCtx_Out); avformat_free_context(avFormCtx_Out); avcodec_close(avCodecCtx_Out); avcodec_free_context(&avCodecCtx_Out); av_free(avCodec); av_packet_free(&packet); av_frame_free(&frame); return 0; } int _tmain(int argc, _TCHAR* argv[]) { //av_register_all(); //avformat_network_init(); avdevice_register_all(); HANDLE hThread = CreateThread(NULL, 0, ScreenCapThreadProc, 0, 0, NULL); WaitForSingleObject(hThread, INFINITE); return 0; }
更多相关内容 -
VB实现局域网抓图,抓屏
2021-05-18 09:25:30摘要:VB源码,网络相关,局域网抓图,截屏,抓屏 VB局域网抓图,局域网截屏、抓屏程序,很值得参考吧,可以学习一下屏幕抓图过程中使用的API函数都有哪些,本源码中给出了详细的函数及使用方法。再此注意,输入的计算机... -
Delphi截屏,抓图,抓屏程序.rar
2019-07-10 11:26:37Delphi截屏,抓图,抓屏程序,支持连续抓屏,效果不错,再完善一些,加入鼠标选框截图的功能,我想那就更完善了。PS注:本来想做网络抓屏,但似乎没准备好,就先做了这个本地抓屏的程序。 -
易语言代码抓图工具
2020-08-21 00:19:01易语言代码抓图工具源码系统结构:取代码编辑区和滚动条句柄,取易语言句柄,取窗口类名,取窗口标题,取滚动条最大值,取滚动条位置,置滚动条位置,刷新窗口显示,截取窗口位图,取要拼接 -
抓屏工具,抓图工具,抓图源码
2012-05-27 08:32:02抓屏工具 抓屏源码 捕捉屏幕 抓图工具 抓图源码 -
抓屏服务器
2018-09-11 16:25:47抓屏服务器是一款用于对本地PC机桌面图像进行编码的软件。支持通过网络的方式进行远程登录,取流。功能类似于一台IPC。 -
海康摄像头实时预览抓图录像 硬盘录像机回放抓图模块-易语言
2021-06-14 03:21:15网上找的大多只有实时预览 自己写了一个 功能包括 实时预览 截图 录像 按指定时间、通道号回放硬盘录像机上录像 截图 等 附件包含所需dll 请解压后放入同一目录 -
用Visual C++设计屏幕抓图程序
2020-07-26 05:07:00本实例通过一个钩子DLL实现了屏幕抓图的功能,它有一个缺陷,那就是只能保存整个屏幕图像,不过可以在此基础上,实现屏幕任意区域的抓图功能. -
4.抓屏服务器.rar
2020-03-09 11:45:14海康抓屏服务器,本机安装,实现桌面作为ipc可被添加预览,配合海康4200等软件使用。内含说明文档,免费使用 -
抓图软件(淘宝天猫阿里).zip
2019-11-06 18:57:51抓图软件(淘宝天猫阿里)抓取 淘宝 天猫 阿里巴巴图片的工具 喜欢的自己下载 -
C# 海康实时预览与抓图
2018-12-30 09:35:18海康摄像机实时预览与抓图的Demo,界面只有两个按钮,播放按钮和抓图按钮,代码简单有注释,非常适合海康API入门开发。并且程序顺带封装了海康C++API为.net方法(这个就不止目前下载的分数)。项目基于.net4.0 ,目标... -
海康威视SDK调用抓图、车牌解析-DELPHI版.zip
2020-08-26 16:03:43海康威视SDK调用,登录、预览显示图像、抓图、车牌解析等功能! -DELPHI 源码精品 车牌解析:登录后,直接点击车牌解析即可。 预览显示图像:登陆后,直接点击显示图像即可。 ... ... -
Delphi 全屏抓图、截屏,区域截图代码,支持滚屏
2021-05-16 22:52:42内容索引:Delphi源码,图形处理,Capture.rar Delphi 全屏抓图、常规截屏,区域截图,活动窗口抓图代码,支持滚屏截图功能。截屏完毕,可打印,可保存为指定格式,可显示图片信息,代码可在delphi7中直接编译成功。源... -
大华摄像头抓图录像亲测可用
2019-02-21 09:55:30大华摄像头抓图录像亲测可用 -
linux下python抓屏实现方法
2020-09-15 17:08:40主要介绍了linux下python抓屏实现方法,涉及Python操作屏幕截取的相关技巧,需要的朋友可以参考下 -
抓图-crx插件
2021-04-04 01:28:26抓图是一个神奇的AR工具,它能帮您把手机拍到的物体直观地放置到浏览器上 抓图能将图片中的主体,包括人、物品、场景等,进行智能抠图,并通过云端自动传送至您的浏览器上。需要您使用移动设备及应用配套使用,包括... -
VC WINSOCK技术实现抓图及屏幕传输.rar
2019-07-10 10:33:50VC WINSOCK技术实现抓图及屏幕传输,捕获当前屏幕并保存到imagestream中,拷贝屏幕的指定区域到位图,将接收到的字符串分解为接收端口、色深、品质3个参数,捕获当前屏幕并保存到ImageStream中。这是一个例子程序,... -
python 截图 抓图 源码
2019-01-09 10:48:35python 截图 抓图 源码,支持快捷键截屏,自动保存,源码自由修改 -
抓图软件(抓图英雄)
2018-11-12 12:43:22此分享的是我用了很多年的抓图软件,叫做抓图英雄,软件本身很小,每次工作中文档处理需要截图的时候用这个软件非常方便,可鼠标选取需要截图的区域,可以直接粘贴在office系列软件中,也可以另存为图片格式,兼容xp... -
抓图工具,实用小程序,实用顺手方便
2019-03-06 18:45:02抓图实用小程序,只是分享搬运,自己用过,比较好使 有需要用到的朋友可以下载试试 截图方便,功能足够满足一般的操作 -
红蜻蜓抓图精灵2018
2019-03-06 16:07:30红蜻蜓抓图精灵(RdfSnap) 是一款完全免费的专业级屏幕捕捉软件,能够让您得心应手地捕捉到需要的屏幕截图。捕捉图像方式灵活,主要可以捕捉整个屏幕、活动窗口、选定区域、固定区域、选定控件、选定菜单、选定网页等... -
趣马抓图工具(淘宝天猫京东详情版)V0.8.zip
2020-03-14 15:27:41淘宝天猫批量下载工具,正在联系原作者,本人只是搬运工。以前京东能用 现在京东不能用了! 不懂程序。有懂的可以改改啊! -
C# 屏幕抓图 (图像操作)
2022-05-18 17:10:42C# 屏幕抓图 (图像操作) -
海康威视QT二次开发。布防,撤防,预览,抓图,录像,停止录像,入侵报警
2021-08-25 17:00:33海康威视二次开发。布防,撤防,预览,抓图,录像,停止录像,入侵告警 演示地址: https://blog.csdn.net/weixin_39276851/article/details/110871893 开发环境:Vs2013+QT5.6.2 -
海康威视QT二次开发。布防,撤防,预览,抓图
2021-05-25 11:56:23布防,撤防,预览,抓图等 开发环境:Vs2013+QT5.6.2 摄像头:红外网络摄像头枪机,型号DS-2CD3T10D-13 Email:okjokull@gmail.com GitHub: https://github.com/okjokull/QHikvision/tree/master 演示地址: ... -
FSCapture抓屏软件
2018-06-05 13:09:46FSCapture :一个非常简单也非常方便的截图小软件,可以添加水平印,也可以对图像进行简单处理。很好用的。 -
1- 实时预览&抓图&云台控制 _云台摄像仪_
2021-09-29 16:46:59海康威视云台摄像仪DEMO,控制云台运动 -
VC 抓图程序 屏幕捕捉.rar
2019-07-10 19:38:37VC 抓图程序,把屏幕上的显示的所有内容保存成图片,可以通过本程序以抓图的模式保存,有时候称为“屏幕捕捉”、“截屏”、“抓屏”,总之就是这一类程序,当然键盘上的PrtscSysRq键,也是可以完成抓屏功能的,只是... -
DXGI高效率抓屏纯易语言版源码-易语言
2021-06-11 17:38:00翻译自@啪啪啪么么哒 提供的源码 原帖地址: https://bbs.125.la/forum.php?mod=viewthreadtid=14196772