MFC文件-屏幕录像

下载本文件
本文件将获取屏幕图像数据的所有代码整合到两个文件中(ScreenRecorder.h和ScreenRecorder.cpp),使获取屏幕图像数据变得简单。输出IYUV视频流。还可以获取系统播放的声音,输出PCM音频流。由于使用了MFC类,本文件只适用于MFC程序。

使用方法

1.创建MFC项目。
2.将ScreenRecorder.h和ScreenRecorder.cpp文件复制到你的MFC项目目录下。
3.将ScreenRecorder.h和ScreenRecorder.cpp文件添加到项目。
4.包含ScreenRecorder.h头文件,声明ScreenRecorder类对象。对象应在录屏生命周期内不被析构掉,比如放在对话框头文件对话框类定义中。

	ScreenRecorder SR;

5.声明SR_INIT初始化结构,填写结构参数。提供录制区域矩形,可以是整个屏幕,也可以是屏幕的某个区域。提供录制帧率。提供样本输出函数。创建“停止”和“退出”事件,提供事件句柄。

int VideoSample(BYTE* pB, LONG len)//视频样本输出函数
{return 0;
}int AudioSample(BYTE* pB, LONG len)//音频样本输出函数
{return 0;
}HANDLE hStop = CreateEvent(NULL, TRUE, TRUE, NULL);//创建“停止”事件。手动重置HANDLE hExit = CreateEvent(NULL, TRUE, FALSE, NULL);//创建“退出”事件。手动重置CRect rect;rect.left=0;rect.top=0;rect.right=1920;rect.bottom=1080;SR_INIT SrInit;//SR初始化结构SrInit.rect = rect;//录制矩形SrInit.ShowCursor = TRUE;//TRUE显示光标,FALSE不显示SrInit.nFramePerSec = 30;//视频帧率SrInit.VideoSample = (MYPROC_VideoSample)VideoSample;//视频样本输出函数SrInit.AudioSample = (MYPROC_AudioSample)AudioSample;//音频样本输出函数SrInit.hStop = hStop;//“停止”事件句柄SrInit.hExit = hExit;//“退出”事件句柄

6.运行。调用初始化函数创建录屏线程,如果提供了音频样本输出函数,还将创建录制系统声音线程。设置“停止”无信号后,视频和音频样本输出函数将被反复调用;函数参数1为样本缓冲区指针,参数2为样本的字节大小。

		SR.Init(SrInit);//使用初始化结构作为参数,调用初始化函数ResetEvent(hStop);//设置“停止”无信号

7.暂停。此时,样本输出函数将停止调用,但录屏线程仍然存在。

		SetEvent(hStop);//设置“停止”无信号

8.停止。将退出录屏线程。

		SetEvent(hStop);//设置“停止”无信号SetEvent(hExit);//设置“退出”有信号

代码没有提供样本时间戳,视频一个样本就是一个IYUV视频帧,根据帧数量就可以计算出当前的样本时间:

	int index=0;//帧索引double dur=(double)10000000/(double)30;//1帧的持续时间,单位100纳秒。30为帧率LONGLONG SampleTime=(LONGLONG)(dur*index);//当前的样本时间,单位100纳秒index++;

音频样本为若干音频帧。音频为2声道,16位PCM,采样率48000。一个音频帧占4字节,根据累计的音频帧数量,可以计算出音频样本的当前时间:

	int FrameCount=0;double Adur=(double)10000000/(double)48000;//1帧的持续时间,单位100纳秒LONGLONG ASampleTime=(LONGLONG)(Adur*FrameCount);//当前的样本时间,单位100纳秒FrameCount+=len/4;//len为此次样本的字节大小

ScreenRecorder.h文件的全部代码

#pragma once#include "mmsystem.h"
#pragma comment(lib, "winmm.lib")#include "D3D11.h"
#pragma comment(lib, "D3D11.lib")
#include "DXGI1_2.h"
#pragma comment(lib, "DXGI.lib")#include "mmdeviceapi.h" 
#include "audioclient.h"#ifndef  SAFE_RELEASE
#define SAFE_RELEASEtemplate <class T> void SafeRelease(T** ppT)
{if (*ppT){(*ppT)->Release();*ppT = NULL;}
}#endif //SAFE_RELEASEtypedef int(__cdecl *MYPROC_VideoSample)(BYTE* pB, LONG len);
typedef int(__cdecl *MYPROC_AudioSample)(BYTE* pB, LONG len);struct SR_INIT
{CRect rect;//录制区域矩形BOOL ShowCursor = TRUE;//为TRUE,显示光标UINT nFramePerSec;//每秒帧数MYPROC_VideoSample VideoSample = NULL;//视频样本接收函数指针MYPROC_AudioSample AudioSample = NULL;//音频样本接收函数指针HANDLE hStop = NULL;//“停止”事件句柄HANDLE hExit = NULL;//“退出”事件句柄
};class ScreenRecorder
{
public:SR_INIT mInit;//初始化信息结构ScreenRecorder();~ScreenRecorder();BOOL Init(SR_INIT init);DWORD GetAudioEndpoint();//获取音频端点HRESULT GetScreenData();//获取屏幕图像数据void DrawCursor(BYTE* pB);//绘制光标int ScreenWidth = GetSystemMetrics(SM_CXSCREEN);//获取主显示器的宽度,以像素为单位int ScreenHeight = GetSystemMetrics(SM_CYSCREEN);//获取主显示器的高度,以像素为单位UINT len;ID3D11Device* p3D11Device = NULL;ID3D11DeviceContext* p3D11DeviceContext = NULL;IDXGIOutputDuplication *pDuplication = NULL;BYTE* pPreBuffer = NULL;//不包含光标的图像缓冲区BYTE* pDrawCursorBuffer = NULL;//包含光标的图像缓冲区BYTE* pBuffer1 = NULL;//视频输出缓冲区BYTE* pBuffer2 = NULL;//音频缓冲区HANDLE hVideoThread = NULL;HANDLE hAudioThread = NULL;IAudioClient *pAudioClient = NULL;IAudioCaptureClient *pCaptureClient = NULL;REFERENCE_TIME nDur;//音频包传递默认时间间隔,100纳秒单位LONG BufferSize;//音频样本缓冲区大小,单位字节CBitmap bmp;//用于绘制光标CDC mDC;//内存DC,用于绘制光标int mState = 0;//状态标志。0停止,1运行,2暂停
};

ScreenRecorder.cpp文件的全部代码

#include "stdafx.h"
#include "ScreenRecorder.h"ScreenRecorder::ScreenRecorder()
{HRESULT hr = CoInitialize(NULL);//初始化COM库if (hr != S_OK){MessageBox(NULL, L"COM库初始化失败!", L"ScreenRecorder", MB_OK);}D3D_DRIVER_TYPE driver_types[] ={D3D_DRIVER_TYPE_HARDWARE,D3D_DRIVER_TYPE_WARP,D3D_DRIVER_TYPE_REFERENCE,};UINT n_driver_types = ARRAYSIZE(driver_types);D3D_FEATURE_LEVEL feature_levels[] ={D3D_FEATURE_LEVEL_11_0,D3D_FEATURE_LEVEL_10_1,D3D_FEATURE_LEVEL_10_0,D3D_FEATURE_LEVEL_9_1};UINT n_feature_levels = ARRAYSIZE(feature_levels);D3D_FEATURE_LEVEL feature_level;hr = D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, feature_levels, n_feature_levels, D3D11_SDK_VERSION, &p3D11Device, &feature_level, &p3D11DeviceContext);IDXGIDevice* pIDXGIDevice = NULL;if (hr == S_OK){hr = p3D11Device->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&pIDXGIDevice));//获取对应的DXGI设备接口}IDXGIAdapter* pDXGIAdapter = NULL;if (hr == S_OK){hr = pIDXGIDevice->GetParent(__uuidof(IDXGIAdapter), reinterpret_cast<void**>(&pDXGIAdapter));//获取DXGI设备适配器}SafeRelease(&pIDXGIDevice);IDXGIOutput* pDXGIOutput = NULL;if (hr == S_OK){hr = pDXGIAdapter->EnumOutputs(0, &pDXGIOutput); //获取设备输出接口}SafeRelease(&pDXGIAdapter);DXGI_OUTPUT_DESC _output_des;if (hr == S_OK){hr = pDXGIOutput->GetDesc(&_output_des);//获取设备输出描述}IDXGIOutput1* pDXGIOutput1 = NULL;if (hr == S_OK){hr = pDXGIOutput->QueryInterface(__uuidof(pDXGIOutput1), reinterpret_cast<void**>(&pDXGIOutput1));}SafeRelease(&pDXGIOutput);if (hr == S_OK){hr = pDXGIOutput1->DuplicateOutput(p3D11Device, &pDuplication);//根据设备输出接口创建一个duplication接口}SafeRelease(&pDXGIOutput1);if (hr != S_OK)MessageBox(0, L"DXGI初始化失败", L"屏幕录像", MB_OK);len = ScreenWidth * ScreenHeight * 4;pBuffer1 = new BYTE[len]; pPreBuffer = new BYTE[len]; pDrawCursorBuffer = new BYTE[len];bmp.CreateBitmap(ScreenWidth, ScreenHeight, 1, 32, NULL);mDC.CreateCompatibleDC(NULL);mDC.SelectObject(&bmp);
}ScreenRecorder::~ScreenRecorder()
{SafeRelease(&p3D11Device); SafeRelease(&pDuplication); SafeRelease(&p3D11DeviceContext);SafeRelease(&pAudioClient); SafeRelease(&pCaptureClient);delete[] pBuffer1; delete[] pPreBuffer; delete[] pDrawCursorBuffer;if (pBuffer2)delete[] pBuffer2;CoUninitialize();//关闭COM库mDC.DeleteDC();
}DWORD ScreenRecorder::GetAudioEndpoint()//获取音频端点
{SafeRelease(&pAudioClient); SafeRelease(&pCaptureClient);HRESULT hr;IMMDeviceEnumerator *pEnumerator = NULL;hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pEnumerator);//创建设备枚举器IMMDevice *pDevice = NULL;if (hr == S_OK){hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice);//获取默认音频端点设备}SafeRelease(&pEnumerator);if (hr == S_OK){hr = pDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL, (void**)&pAudioClient);//激活默认音频端点设备}SafeRelease(&pDevice);WAVEFORMATEX wfx;wfx.wFormatTag = 1;wfx.nChannels = 2;wfx.nSamplesPerSec = 48000;wfx.nAvgBytesPerSec = 48000 * 4;wfx.nBlockAlign = 4;wfx.wBitsPerSample = 16;wfx.cbSize = 0;if (hr == S_OK){hr = pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_LOOPBACK, (REFERENCE_TIME)10000000, 0, &wfx, NULL);//创建端点缓冲区,可以容纳1秒的音频数据}REFERENCE_TIME r2;if (hr == S_OK){hr = pAudioClient->GetDevicePeriod(&nDur, &r2);//获取单个音频包持续时间,单位100纳秒}UINT32 Size;if (hr == S_OK){hr = pAudioClient->GetBufferSize(&Size);//获取申请的端点缓冲区总大小,单位音频帧}if (hr == S_OK){BufferSize = (LONG)(Size * wfx.nChannels * 2 * nDur / 10000000);//计算样本大小,单位字节if (pBuffer2 == NULL)pBuffer2 = new BYTE[BufferSize];hr = pAudioClient->GetService(__uuidof(IAudioCaptureClient), (void**)&pCaptureClient);//获取音频服务}if (hr != S_OK){SafeRelease(&pAudioClient); SafeRelease(&pCaptureClient);MessageBox(NULL, L"获取音频端点失败!", L"提示", MB_OK);return 0;}return 1;
}void ScreenRecorder::DrawCursor(BYTE* pB)//绘制光标
{CURSORINFO CursorInfo;CursorInfo.cbSize = sizeof(CURSORINFO);if (!GetCursorInfo(&CursorInfo))return;//获取光标的信息if (CursorInfo.flags != CURSOR_SHOWING)return;//如果光标没有显示,不绘制光标bmp.SetBitmapBits(len, pB);mDC.DrawIcon(CursorInfo.ptScreenPos, CursorInfo.hCursor);//在内存DC绘制光标BITMAPINFOHEADER Hdr;Hdr.biSize = sizeof(BITMAPINFOHEADER);Hdr.biWidth = ScreenWidth;Hdr.biHeight = -ScreenHeight;Hdr.biPlanes = 1;Hdr.biBitCount = 32;Hdr.biCompression = BI_RGB;Hdr.biSizeImage = len;Hdr.biXPelsPerMeter = 0;Hdr.biYPelsPerMeter = 0;Hdr.biClrUsed = 0;Hdr.biClrImportant = 0;GetDIBits(mDC.m_hDC, (HBITMAP)bmp, 0, ScreenHeight, pB, (BITMAPINFO*)&Hdr, DIB_RGB_COLORS);//将内存DC位图的位 复制到pB
}HRESULT ScreenRecorder::GetScreenData()//获取屏幕图像数据
{IDXGIResource* pIDXGIResource = NULL;DXGI_OUTDUPL_FRAME_INFO frame_info;HRESULT DuplicationHr = pDuplication->AcquireNextFrame(0, &frame_info, &pIDXGIResource);//获取下一个桌面映像D3D11_TEXTURE2D_DESC frame_desc;DXGI_MAPPED_RECT mapped_rect;IDXGISurface *dxgi_surface = NULL;HRESULT MapHr = S_FALSE;if (DuplicationHr == S_OK){ID3D11Texture2D *_image = NULL;HRESULT Texture2DHr = pIDXGIResource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void **>(&_image));//获取一帧图像纹理SafeRelease(&pIDXGIResource);if (Texture2DHr == S_OK){_image->GetDesc(&frame_desc);frame_desc.MipLevels = 1;frame_desc.ArraySize = 1;frame_desc.SampleDesc.Count = 1;frame_desc.Usage = D3D11_USAGE_STAGING;frame_desc.BindFlags = 0;frame_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;frame_desc.MiscFlags = 0;ID3D11Texture2D *new_image = NULL;HRESULT NewHr = p3D11Device->CreateTexture2D(&frame_desc, NULL, &new_image);//创建新的纹理if (NewHr == S_OK){p3D11DeviceContext->CopyResource(new_image, _image);//拷贝图像HRESULT SurfaceHr = new_image->QueryInterface(__uuidof(IDXGISurface), (void **)(&dxgi_surface));SafeRelease(&new_image);if (SurfaceHr == S_OK){MapHr = dxgi_surface->Map(&mapped_rect, DXGI_MAP_READ);//将图像从GPU映射到内存中if (MapHr == S_OK){CopyMemory(pPreBuffer, mapped_rect.pBits, len);}}}SafeRelease(&_image);}}BYTE* RGB32_data;if (MapHr == S_OK)//如果获取图像和映射成功{RGB32_data = mapped_rect.pBits;//直接在mapped_rect.pBits中绘制光标}else//如果失败{CopyMemory(pDrawCursorBuffer, pPreBuffer, len);//复制上一帧图像到pDrawCursorBufferRGB32_data = pDrawCursorBuffer;//在pDrawCursorBuffer中绘制光标}if (mInit.ShowCursor)//如果要求显示光标{DrawCursor(RGB32_data);//在RGB32_data中绘制光标}int iY = 0;int YSize = mInit.rect.Width() * mInit.rect.Height();int iU = YSize;int iV = YSize + YSize / 4;BYTE Color32[4];for (int y = mInit.rect.top; y < mInit.rect.bottom; y++)//将指定矩形内的RGB32数据转换为IYUV存储在pBuffer1{for (int x = mInit.rect.left; x < mInit.rect.right; x++){CopyMemory(Color32, &RGB32_data[y * ScreenWidth * 4 + x * 4], 4);pBuffer1[iY] = (BYTE)(0.299 * Color32[2] + 0.587 * Color32[1] + 0.114 * Color32[0]);//Yif ((x & 1) && (y & 1)){pBuffer1[iU] = (BYTE)(-0.1687 * Color32[2] - 0.3313 * Color32[1] + 0.5 * Color32[0] + 128);//UpBuffer1[iV] = (BYTE)(0.5 * Color32[2] - 0.4187 * Color32[1] - 0.0813 * Color32[0] + 128);//ViU++; iV++;}iY++;}}if (MapHr == S_OK){dxgi_surface->Unmap();}SafeRelease(&dxgi_surface);if (DuplicationHr == S_OK){pDuplication->ReleaseFrame();//释放桌面映像}mInit.VideoSample(pBuffer1, (LONG)(mInit.rect.Width() * mInit.rect.Height() * 1.5));//调用外部函数,发送视频样本。如果获取桌面图像成功,发送新的图像;失败发送上一帧图像return DuplicationHr;
}DWORD WINAPI ScreenRecorderThread(LPVOID lp)
{ScreenRecorder* pSR = (ScreenRecorder*)lp;LONGLONG index = 0; double Ntime = (double)1000 / (double)pSR->mInit.nFramePerSec;DWORD STAR = timeGetTime();//记录开始时间
Agan:DWORD Cur = timeGetTime() - STAR;//当前时间,单位毫秒DWORD Sur = (DWORD)(Ntime * (double)index);//帧呈现时间,单位毫秒if (Cur < Sur)goto Agan;//如果没有到帧显示时间,等待if (Cur >= (DWORD)(Ntime * (double)(index+1)))//如果当前时间大于下一帧的呈现时间{pSR->mInit.VideoSample(pSR->pBuffer1, (LONG)(pSR->mInit.rect.Width() * pSR->mInit.rect.Height() * 1.5));index++;goto Agan;}DWORD mStop = WaitForSingleObject(pSR->mInit.hStop, 0);if (mStop == WAIT_OBJECT_0)//如果“停止”有信号{pSR->mState = 2;//状态为暂停}else{pSR->mState = 1;//状态为运行HRESULT hr = pSR->GetScreenData();}DWORD mExit = WaitForSingleObject(pSR->mInit.hExit, 0);if (mExit == WAIT_OBJECT_0)//如果“退出”有信号{pSR->mState = 0;//状态为停止return 1;}index++;goto Agan;
}DWORD WINAPI AudioEndpointThread(LPVOID lp)
{ScreenRecorder* pSR = (ScreenRecorder*)lp;HRESULT hr; LONGLONG index = 0; BOOL Run = FALSE, Stop = TRUE; double Ntime = (double)pSR->nDur / (double)10000;DWORD STAR = timeGetTime();//记录开始时间
Agan:DWORD Cur = timeGetTime() - STAR;//当前时间,单位毫秒DWORD Sur = (DWORD)(Ntime * (double)index);//帧呈现时间,单位毫秒if (Cur < Sur)goto Agan;//如果没有到间隔时间(间隔10毫秒),等待DWORD mExit = WaitForSingleObject(pSR->mInit.hExit, 0);if (mExit == WAIT_OBJECT_0){return 1;}DWORD mStop = WaitForSingleObject(pSR->mInit.hStop, 0);if (mStop == WAIT_OBJECT_0)//如果“停止”有信号{if (Stop == FALSE){Stop = TRUE; Run = FALSE;hr = pSR->pAudioClient->Stop();  //停止音频流hr = pSR->pAudioClient->Reset();//刷新所有挂起的数据}}else//如果“停止”无信号{if (Run == FALSE){Run = TRUE; Stop = FALSE;hr = pSR->pAudioClient->Start();  //启动音频流}DWORD flags; UINT32 numFramesAvailable = 0; BYTE *pData = NULL;//音频包缓冲区指针UINT32 packetLength = 0;//音频包大小,单位音频帧hr = pSR->pCaptureClient->GetNextPacketSize(&packetLength);//获取音频包的大小,单位音频帧if (packetLength != 0)//如果获取到音频包,将音频包数据转换为short类型,复制到缓冲区{hr = pSR->pCaptureClient->GetBuffer(&pData, &numFramesAvailable, &flags, NULL, NULL);//获取当前音频包的指针if (hr == S_OK && numFramesAvailable != 0 && pData != NULL){int count = numFramesAvailable * 4 / pSR->BufferSize;//计算需要发送多少个样本for (int i = 0; i < count; i++){pSR->mInit.AudioSample(pData + i*pSR->BufferSize, pSR->BufferSize);}hr = pSR->pCaptureClient->ReleaseBuffer(numFramesAvailable);//释放音频包}}else//如果当前没有音频流,将音频缓冲区全部置0{memset(pSR->pBuffer2, 0, pSR->BufferSize);pSR->mInit.AudioSample(pSR->pBuffer2, pSR->BufferSize);//发送10毫秒的0数据}}index++;goto Agan;
}BOOL ScreenRecorder::Init(SR_INIT init)
{DWORD dwV = WaitForSingleObject(hVideoThread, 0);if (dwV == WAIT_TIMEOUT)return FALSE;//如果线程已存在,返回DWORD dwA = WaitForSingleObject(hAudioThread, 0);if (dwA == WAIT_TIMEOUT)return FALSE;if (init.hExit == NULL || init.hStop == NULL){MessageBox(NULL, L"必须提供“停止”和“退出”事件句柄", L"写MP4", MB_OK); return FALSE;}if (init.VideoSample == NULL){MessageBox(NULL, L"必须提供视频样本输出函数", L"写MP4", MB_OK); return FALSE;}mInit = init;mInit.rect.NormalizeRect();//使其为正常矩形(宽度和高度为正值)if (mInit.rect.left < 0)mInit.rect.left = 0;if (mInit.rect.top < 0)mInit.rect.top = 0;if (mInit.rect.right > ScreenWidth)mInit.rect.right = ScreenWidth;if (mInit.rect.bottom > ScreenHeight)mInit.rect.bottom = ScreenHeight;if (mInit.rect.Width() % 2)//确保录制矩形宽度为偶数{if (mInit.rect.right != ScreenWidth)mInit.rect.right += 1;else if (mInit.rect.left != 0)mInit.rect.left -= 1;}if (mInit.rect.Height() % 2)//确保录制矩形高度为偶数{if (mInit.rect.bottom != ScreenHeight)mInit.rect.bottom += 1;else if (mInit.rect.top != 0)mInit.rect.top -= 1;}ResetEvent(mInit.hExit);//设置“退出”无信号SetEvent(mInit.hStop);//设置“停止”有信号hVideoThread = CreateThread(NULL, 0, ScreenRecorderThread, this, 0, NULL);//创建录屏线程if (mInit.AudioSample)//如果提供了音频样本输出函数,创建录制系统播放的声音线程。不提供该函数,则不创建{GetAudioEndpoint();//获取音频端点hAudioThread = CreateThread(NULL, 0, AudioEndpointThread, this, 0, NULL);//创建录制系统播放的声音线程}return TRUE;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/76480.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

0801ajax_mock-网络ajax请求1-react-仿低代码平台项目

0 vite配置proxy代理 vite.config.ts代码如下图所示&#xff1a; import { defineConfig } from "vite"; import react from "vitejs/plugin-react";// https://vite.dev/config/ export default defineConfig({plugins: [react()],server: {proxy: {&qu…

JVM笔记【一】java和Tomcat类加载机制

JVM笔记一java和Tomcat类加载机制 java和Tomcat类加载机制 Java类加载 * loadClass加载步骤类加载机制类加载器初始化过程双亲委派机制全盘负责委托机制类关系图自定义类加载器打破双亲委派机制 Tomcat类加载器 * 为了解决以上问题&#xff0c;tomcat是如何实现类加载机制的…

IP编址(来自YESLAB新网工的笔记)

上层协议类型 概念&#xff1a;通常指的是位于网络层&#xff08;如 IP 层&#xff09;以上的协议类型&#xff0c;这些协议在数据传输时需要由网络层&#xff08;或更低层&#xff09;协议承载。以 IP 协议为例&#xff0c;IP 报文头部中的 协议字段&#xff08;Protocol Fie…

SpringBoot学习(过滤器Filter。拦截器Interceptor。全局异常捕获处理器GlobalExceptionHandler)(详细使用教程)

目录 一、过滤器Filter。 1.1定义与规范。 1.2工作原理与范围。 1.3使用场景。 1.4 SpringBoot实现过滤器。&#xff08;Filter配置2种方式&#xff09; <1>注解配置(WebFilter、Order、ServletComponentScan)。 创建过滤器类。 启用 Servlet 组件扫描。 <2>配置类…

c++题目_P1443 马的遍历

P1443 马的遍历 # P1443 马的遍历 ## 题目描述 有一个 $n \times m$ 的棋盘&#xff0c;在某个点 $(x, y)$ 上有一个马&#xff0c;要求你计算出马到达棋盘上任意一个点最少要走几步。 ## 输入格式 输入只有一行四个整数&#xff0c;分别为 $n, m, x, y$。 ## 输出格式 …

清华《数据挖掘算法与应用》K-means聚类算法

使用k均值聚类算法对表4.1中的数据进行聚类。代码参考P281。 创建一个名为 testSet.txt 的文本文件&#xff0c;将以下内容复制粘贴进去保存即可&#xff1a; 0 0 1 2 3 1 8 8 9 10 10 7 表4.1 # -*- coding: utf-8 -*- """ Created on Thu Apr 17 16:59:58 …

HarmonyOS-ArkUI V2工具类:AppStorageV2:应用全局UI状态存储

AppStorageV2是一个能够跨界面存储数据,管理数据的类。开发者可以使用AppStorageV2来存储全局UI状态变量数据。它提供的是应用级的全局共享能力,开发者可以通过connect绑定同一个key,进行跨ability数据共享。 概述 AppStorageV2是一个单例,创建时间是应用UI启动时。其目的…

打靶日记 zico2: 1

一、探测靶机IP&#xff08;进行信息收集&#xff09; 主机发现 arp-scan -lnmap -sS -sV -T5 -p- 192.168.10.20 -A二、进行目录枚举 发现dbadmin目录下有个test_db.php 进入后发现是一个登录界面&#xff0c;尝试弱口令&#xff0c;结果是admin&#xff0c;一试就出 得到加…

使用Java基于Geotools的SLD文件编程式创建与磁盘生成实战

前言 在地理信息系统&#xff08;GIS&#xff09;领域&#xff0c;地图的可视化呈现至关重要&#xff0c;而样式定义语言&#xff08;SLD&#xff09;文件为地图元素的样式配置提供了强大的支持。SLD 能够精确地定义地图图层中各类要素&#xff08;如点、线、面、文本等&#x…

kubernetes》》k8s》》Service

Kubernetes 中的 Service 是用于暴露应用服务的核心抽象&#xff0c;为 Pod 提供稳定的访问入口、负载均衡和服务发现机制。Service在Kubernetes中代表了一组Pod的逻辑集合&#xff0c;通过创建一个Service&#xff0c;可以为一组具有相同功能的容器应用提供一个统一的入口地址…

【HDFS】EC重构过程中的校验功能:DecodingValidator

一、动机 DecodingValidator是在HDFS-15759中引入的一个用于校验EC数据重构正确性的组件。 先说下引入DecodingValidator的动机,据很多已知的ISSUE(如HDFS-14768, HDFS-15186, HDFS-15240,这些目前都已经fix了)反馈, EC在重构的时候可能会有各种各样的问题,导致数据错误…

现代c++获取linux系统架构

现代c获取linux系统架构 前言一、使用命令获取系统架构二、使用c代码获取系统架构三、验证四、总结 前言 本文介绍一种使用c获取linux系统架构的方法。 一、使用命令获取系统架构 linux系统中可以使用arch或者uname -m命令来获取当前系统架构&#xff0c;如下图所示 archuna…

didFinishLaunching 与「主线程首次 idle」, 哪个是更优的启动结束时间点 ?

结论先行 在这两个候选时间点里—— application:didFinishLaunchingWithOptions: 执行结束主线程第一次进入 idle&#xff08;RunLoop kCFRunLoopBeforeWaiting&#xff09; 若你只能二选一&#xff0c;以「主线程首次 idle」作为 启动结束 更合理。它比 didFinishLaunchin…

Vue3 + TypeScript中defineEmits 类型定义解析

TypeScript 中 Vue 3 的 defineEmits 函数的类型定义&#xff0c;用于声明组件可以触发的事件。以下是分步解释&#xff1a; 1. 泛型定义 ts <"closeDialog" | "getApplySampleAndItemX"> 作用&#xff1a;定义允许的事件名称集合&#xff0c;即组…

树莓派超全系列教程文档--(34)树莓派配置GPIO

配置GPIO GPIO控制gpio 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 GPIO控制 gpio 通过 gpio 指令&#xff0c;可以在启动时将 GPIO 引脚设置为特定模式和值&#xff0c;而以前需要自定义 dt-blob.bin 文件。每一行都对一组引脚应用相同的设…

AladdinEdu(H卡GPU算力平台)使用教程: 1)注册与开通流程 2)插件使用流程

一、注册与开通流程 首先进入AladdinEdu官网&#xff1a;AladdinEdu-同学们用得起的H卡算力平台-高效做AI就上Aladdin 完成注册&#xff0c;并进行学生认证&#xff1a;学生认证账户&#xff0c;认证期间享受教育优惠价。 登录官网进入控制台 二、插件使用流程 VScode中…

精益数据分析(6/126):深入理解精益分析的核心要点

精益数据分析&#xff08;6/126&#xff09;&#xff1a;深入理解精益分析的核心要点 在创业和数据驱动的时代浪潮中&#xff0c;我们都在不断探索如何更好地利用数据推动业务发展。我希望通过和大家分享对《精益数据分析》的学习心得&#xff0c;一起在这个充满挑战和机遇的领…

2.深入剖析 Rust+Axum 类型安全路由系统

摘要 详细解读 RustAxum 路由系统的关键设计原理&#xff0c;涵盖基于 Rust 类型系统的路由匹配机制、动态路径参数与正则表达式验证以及嵌套路由与模块化组织等多种特性。 一、引言 在现代 Web 开发中&#xff0c;路由系统是构建 Web 应用的核心组件之一&#xff0c;它负责…

运筹学之模拟退火

目录 一、历史二、精髓思想三、案例与代码实现 一、历史 问&#xff1a;谁在什么时候提出模拟退火&#xff1f;答&#xff1a;模拟退火算法&#xff08;Simulated Annealing&#xff0c;SA&#xff09;是由斯图尔特柯尔斯基&#xff08;Scott Kirkpatrick&#xff09; 等人在 …

android测试依赖

Android 项目中常用的测试相关库 1. androidx.arch.core:core-testing:2.2.0 作用&#xff1a; 提供与 Android Architecture Components&#xff08;如 LiveData、ViewModel&#xff09;相关的测试工具。主要用于测试基于 LiveData 的异步操作。 常见功能&#xff1a; 即时…