当前位置: 首页 > news >正文

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

相关文章:

MFC文件-屏幕录像

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

JAVA的泛型

为什么引入泛型 有两个作用&#xff1a; 适用于多种数据类型执行相同的代码&#xff08;代码复用&#xff09;泛型中的类型在使用时指定&#xff0c;不需要强制类型转换&#xff08;类型安全&#xff0c;编译器会检查类型&#xff09;消除强制类型转换兼容性与类型擦除更灵活…...

【UniApp】Vue2 scss 预编译器默认已由 node-sass 更换为 dart-sass

从 HBuilderX 4.56 &#xff0c;vue2 项目也将默认使用 dart-sass 预编译器。 vue2开发者sass预处理注意&#xff1a; sass的预处理器&#xff0c;早年使用node-sass&#xff0c;也就是vue2最初默认的编译器。 sass官方推出了dart-sass来替代。node-sass已经停维很久了。 另…...

【sylar-webserver】8 HOOK模块

文章目录 知识点HOOK实现方式非侵入式hook侵入式hook ⭐⭐⭐ 覆盖系统调用接口获取被全局符号介入机制覆盖的系统调用接口 具体实现 在写之前模块的时候&#xff0c;我一直在困惑 协程是如何高效工作的&#xff0c;毕竟协程阻塞线程也就阻塞了。 HOOK模块解开了我的困惑。&…...

【今日三题】判断是不是平衡二叉树(递归) / 最大子矩阵(二维前缀和) / 小葱的01串(滑动窗口)

⭐️个人主页&#xff1a;小羊 ⭐️所属专栏&#xff1a;每日两三题 很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~ 目录 判断是不是平衡二叉树(递归)最大子矩阵(二维前缀和)小葱的01串(滑动窗口) 判断是不是平衡二叉树(递归) 判断是不是平衡二叉…...

交易系统的构建与实战法则

Ⅰ 交易哲学:理解市场本质 时间的艺术:鳄鱼法则的启示80%的交易时间应用于观察和等待日均有效交易机会不超过3次(以A股为例)杰西利弗莫尔的棉花合约案例(1907年等待11周)波动率与交易频率的黄金分割比例Ⅱ 形态识别系统:双轨交易模型 A. 趋势引擎 三级趋势验证体系: 均…...

C++高并发内存池ConcurrenMemoPool

一、介绍高并发内存池 本项目的原型是Google的开源项目tcmalloc&#xff0c;即线程缓存的malloc&#xff0c;相较于系统的内存分配函数malloc&#xff0c;free&#xff0c;本项目能达到高效的多线程内存管理 旨在学习其核心框架&#xff0c;借鉴其实现方式来模拟实现出一个我们…...

ubuntu下gcc/g++安装及不同版本切换

1. 查看当前gcc版本 $ gcc --version# 查看当前系统中已安装版本 $ ls /usr/bin/gcc*2. 安装新版本gcc $ sudo apt-get update# 这里以版本12为依据&#xff08;也可以通过源码方式安装&#xff0c;请自行Google&#xff01;&#xff09; $ sudo apt-get install -y gcc-12 g…...

React-在使用map循环数组渲染列表时须指定唯一且稳定值的key

在渲染列表的时候&#xff0c;我们须给组件或者元素分配一个唯一值的key, key是一个特殊的属性&#xff0c;不会最终加在元素上面&#xff0c;也无法通过props.key来获取&#xff0c;仅在react内部使用。react中的key本质是服务于diff算法, 它的默认值是null, 在diff算法过程中…...

(03)Vue的常用指令

文章目录 第3章 Vue的常用指令3.1 v-text与v-html3.2 v-for3.3 v-if与v-show3.4 MVVM双向绑定3.4.1 v-bind3.4.2 v-model 第3章 Vue的常用指令 3.1 v-text与v-html v-text&#xff1a;不会渲染字符串里面的HTML内容v-html&#xff1a;会渲染字符串里面的HTML内容 <body s…...

从代码学习深度学习 - 优化算法 PyTorch 版

文章目录 前言一、小批量梯度下降(Mini-batch Gradient Descent)1.1 公式1.2 PyTorch 实现二、动量法(Momentum)2.1 公式2.2 PyTorch 实现三、AdaGrad 算法3.1 公式3.2 PyTorch 实现四、RMSProp 算法4.1 公式4.2 PyTorch 实现五、Adadelta 算法5.1 公式5.2 PyTorch 实现六、…...

JAVA设计模式——(1)适配器模式

JAVA设计模式——&#xff08;1&#xff09;适配器模式 目的理解实现优势 目的 将一个类的接口变换成客户端所期待的另一种接口&#xff0c;从而使原本因接口不匹配而无法一起工作的两个类能够在一起工作。 理解 可以想象成一个国标的插头&#xff0c;结果插座是德标的&…...

深入Docker核心技术:从Namespace到容器逃逸防御

深入Docker核心技术&#xff1a;从Namespace到容器逃逸防御 引言&#xff1a;容器技术的本质突破 Docker作为容器技术的代表&#xff0c;其革命性不仅在于轻量级虚拟化&#xff0c;更在于重新定义了应用交付的标准范式。本文将穿透表象&#xff0c;深入剖析Docker的核心技术实…...

面向对象设计中的类的分类:实体类、控制类和边界类

目录 前言1. 实体类&#xff08;Entity Class&#xff09;1.1 定义和作用1.2 实体类的特点1.3 实体类的示例 2. 控制类&#xff08;Control Class&#xff09;2.1 定义和作用2.2 控制类的特点2.3 控制类的示例 3. 边界类&#xff08;Boundary Class&#xff09;3.1 定义和作用3…...

【MySQL】004.MySQL数据类型

文章目录 1. 数据类型分类2. 数值类型2.1 tinyint类型2.2 bit类型2.3 小数类型2.3.1 float2.3.2 decimal 2.4 字符串类型2.4.1 char2.4.2 varchar2.4.3 char和varchar比较 2.5 日期和时间类型2.6 enum和set2.7 enum和set类型查找 1. 数据类型分类 2. 数值类型 2.1 tinyint类型 …...

使用docker在manjaro linux系统上运行windows和ubuntu

因为最近项目必须要使用指定版本的solidworks和maxwell&#xff08;都只能在win系统上使用&#xff09;, 且目前的ubuntu容器是没有桌面的&#xff0c;导致我运行不了一些带图形的ros2功能。无奈之下&#xff0c;决定使用docker-compose写一下配置文件&#xff0c;彻底解决问题…...

Flask应用部署通用指南

IIS 部署 Python Flask 应用通用指南 目录 概述环境准备应用准备wfastcgi 配置IIS 网站配置权限配置静态文件处理安全配置性能优化常见问题与解决方案生产环境最佳实践 概述 将 Flask 应用部署到 Windows IIS 服务器上需要使用 WSGI 适配器&#xff08;如 wfastcgi&#xf…...

数据驱动增长:大数据与营销自动化的结合之道

数据驱动增长&#xff1a;大数据与营销自动化的结合之道 在这个信息爆炸的时代&#xff0c;企业如果还靠拍脑袋做营销决策&#xff0c;那基本等同于闭着眼睛开车&#xff0c;撞上南墙只是时间问题。大数据和营销自动化的结合&#xff0c;让营销从传统的经验主义走向科学决策&a…...

[Java微服务组件]注册中心P3-Nacos中的设计模式1-观察者模式

在P1-简单注册中心实现和P2-Nacos解析中&#xff0c;我们分别实现了简单的注册中心并总结了Nacos的一些设计。 本篇继续看Nacos源码&#xff0c;了解一下Nacos中的设计模式。 目录 Nacos 观察者模式 Observer Pattern观察者模式总结 Nacos 观察者模式 Observer Pattern 模式定…...

Java—— 常见API介绍 第二期

Runtime 说明&#xff1a; Runtime表示当前虚拟机的运行环境 获取Runtime对象的方法是静态的&#xff0c;可以用类名调用 不能用new关键字创建Runtime对象&#xff0c;只能调用获取Runtime对象的方法获取对象 其他的方法不是静态的&#xff0c;不能直接用类名调用&#xff0c;…...

意志力的源头——AMCC(前部中扣带皮层)

AMCC&#xff08;前部中扣带皮层&#xff09;在面对痛苦需要坚持的事情时会被激活。它的存在能够使人类个体在面临困难的事、本能感到不愿意的麻烦事情时&#xff0c;能够自愿地去做这些事——这些事必须是局部痛苦或宏观的痛苦&#xff0c;即微小的痛苦micro-sucks。 AMCC更多…...

ProfiNet转DeviceNet边缘计算网关多品牌集成实践:污水处理厂设备网络融合全流程解析

一、行业背景 随着环保政策趋严&#xff0c;污水处理行业对自动化、数据实时性和设备兼容性需求激增。传统污水处理厂普遍存在设备协议异构&#xff08;如DeviceNet、ProfiNet混用&#xff09;、数据孤岛严重的问题&#xff0c;现需通过捷米特DeviceNet转ProfiNet协议转换网关…...

CCLinkIE转EtherCAT边缘计算网关构建智能产线:跨协议设备动态组网与数据优化传输

一、行业背景 随着新能源汽车市场爆发式增长&#xff0c;汽车制造企业对产线效率、设备协同性及柔性生产能力的要求显著提升。传统产线多采用CC-LinkIEFieldBasic&#xff08;CCLINKIEFB&#xff09;协议的三菱PLC控制系统&#xff0c;而新一代伺服驱动设备普遍采用EtherCAT协…...

C 语言中的高级数据结构与内存管理

一、引言 C 语言作为一种广泛应用的系统级编程语言&#xff0c;以其高效性和灵活性著称。在 C 语言编程中&#xff0c;高级数据结构和内存管理是两个至关重要的方面。高级数据结构能够帮助我们更高效地组织和处理数据&#xff0c;而合理的内存管理则是保证程序性能和稳定性的关…...

Django 实现服务器主动给客户端发送消息的几种常见方式及其区别

Django Channels 原理 &#xff1a;Django Channels 是 Django 的一个扩展&#xff0c;它通过使用 WebSockets 等协议来处理长连接&#xff0c;使服务器能够与客户端建立持久连接&#xff0c;从而实现双向通信。一旦连接建立&#xff0c;服务器可以随时主动向客户端发送消息。…...

BR_频谱20dB 带宽(RF/TRM/CA/BV-05-C [TX Output Spectrum – 20 dB Bandwidth])

目录 一、规范要求 1、协议章节 2、测试目的 二、测试方法 1、样机初值条件&#xff1a; 2、测试步骤: 方法一&#xff1a;频谱仪 方法二&#xff1a;综测仪CMW500 3、预期结果 一、规范要求 1、协议章节 4.5.5 RF/TRM/CA/BV-05-C [TX Output Spectrum – 20 dB Ba…...

rabbitmq 面试题

一、基础概念 1. 什么是 RabbitMQ&#xff1f; - 基于 AMQP 协议的开源消息中间件&#xff0c;用于实现系统间的异步通信和解耦&#xff0c;支持多种消息模式&#xff08;如发布/订阅、路由、主题等&#xff09;。 1. 你了解那个rabbitmq&#xff0c; rabbitmq 的 虚拟机是…...

论文阅读:2025 arxiv AI Alignment: A Comprehensive Survey

总目录 大模型安全相关研究&#xff1a;https://blog.csdn.net/WhiffeYF/article/details/142132328 AI Alignment: A Comprehensive Survey https://arxiv.org/pdf/2310.19852 https://alignmentsurvey.com/ https://www.doubao.com/chat/3367091682540290 速览 研究动机…...

spring事务事务传播

POROPAGATION_REQUIRED(必须) 解释&#xff1a; 存在事务时 当前方法已在事务中运行&#xff0c;直接加入该事务 无事务 则自动开启一个新事物&#xff0c;并在方法执行结束后提交或者回滚 举例 java Transactional public void transfer() {accountService.reduceBalance…...

JMeter介绍

文章目录 1. JMeter简介2. JMeter 下载3. JMeter修改编码集4. 启动并运行JMeter 1. JMeter简介 JMeter 是 Apache 组织使用 Java 开发的一款测试工具&#xff1a; 1、可以用于对服务器、网络或对象模拟巨大的负载 2、通过创建带有断言的脚本来验证程序是否能返回期望的结果 优…...

Elasticsearch:使用 ES|QL 进行搜索和过滤

本教程展示了 ES|QL 语法的示例。请参考 Query DSL 版本&#xff0c;以获得等效的 Query DSL 语法示例。 这是一个使用 ES|QL 进行全文搜索和语义搜索基础知识的实践介绍。 有关 ES|QL 中所有搜索功能的概述&#xff0c;请参考《使用 ES|QL 进行搜索》。 在这个场景中&#x…...

面向新一代扩展现实(XR)应用的物联网框架

中文标题&#xff1a; 面向新一代扩展现实&#xff08;XR&#xff09;应用的物联网框架 英文标题&#xff1a; Towards an IoT Framework for the New Generation of XR Applications 作者信息 Joo A. Dias&#xff0c;UNIDCOM - IADE&#xff0c;欧洲大学&#xff0c;里斯本&…...

Docker Overlay 网络的核心工作(以跨节点容器通信为例)

Docker 的 overlay 网络是一种基于 VXLAN&#xff08;Virtual Extensible LAN&#xff09;的多主机网络模式&#xff0c;专为 Docker Swarm 集群设计&#xff0c;用于实现跨节点的容器通信。它通过虚拟二层网络&#xff0c;允许容器在不同主机上像在同一局域网内一样通信。Dock…...

开发基于python的商品推荐系统,前端框架和后端框架的选择比较

开发一个基于Python的商品推荐系统时&#xff0c;前端和后端框架的选择需要综合考虑项目需求、开发效率、团队熟悉度以及系统的可扩展性等因素。 以下是一些推荐的框架和建议&#xff1a; 后端框架 Flask 优点&#xff1a; 轻量级&#xff1a;Flask的核心非常简洁&#xff0c;…...

CSRF 请求伪造Referer 同源置空配合 XSSToken 值校验复用删除

#CSRF- 无检测防护 - 检测 & 生成 & 利用(那数据包怎么整 找相似源码自己搭建整&#xff09; 检测&#xff1a;黑盒手工利用测试&#xff0c;白盒看代码检验&#xff08;有无 token &#xff0c;来源检验等&#xff09; 生成&#xff1a; BurpSuite->Engagement t…...

Datawhale AI春训营】AI + 新能源(发电功率预测)Task1

赛题链接 官网 新能源发电功率预测赛题进阶方案 下面是ai给的一些建议 新能源发电功率预测赛题进阶方案 一、时序特性深度挖掘 1. 多尺度周期特征 # 分钟级周期编码 train[15min_index] (train[hour]*4 train[minute]//15)# 周周期特征 train[weekday] pd.to_datetime…...

@EnableAsync+@Async源码学习笔记之二

从本文开始&#xff0c;就正式进入源码追踪阶段了&#xff0c;上一篇的最后我们提到了 EnableAsync 注解上的 Import(AsyncConfigurationSelector.class)了&#xff0c;本文就来看下它&#xff0c;源码如下&#xff1a; package org.springframework.scheduling.annotation;im…...

C++ STL 环形队列模拟实现

C STL 环形队列模拟实现 下面是一个使用C STL实现的环形队列&#xff08;Circular Queue&#xff09;的完整示例&#xff1a; #include <iostream> #include <vector> #include <stdexcept>template <typename T> class CircularQueue { private:std…...

每天五分钟深度学习PyTorch:0填充函数在搭建神经网络中的应用

本文重点 在深度学习中,神经网络的搭建涉及对输入数据、权重矩阵以及中间计算结果的处理。masked_fill 是 PyTorch 等深度学习框架中常用的张量操作函数,它通过布尔掩码(mask)对张量中的指定元素进行填充。当将矩阵元素填充为 0 时,masked_fill 在神经网络中发挥着重要作…...

pycharm中怎么解决系统cuda版本高于pytorch可以支持的版本的问题?

在PyCharm中安装与系统CUDA版本不一致的PyTorch是可行的。以下是解决方案的步骤&#xff1a; 1. 确认系统驱动兼容性 检查NVIDIA驱动支持的CUDA版本&#xff1a;运行 nvidia-smi&#xff0c;右上角显示的CUDA版本是驱动支持的最高版本。只要该版本不低于PyTorch所需的CUDA版本…...

【概率论】条件期望

在高等概率论中&#xff0c;给定一个概率空间 ( Ω , F , P ) (\Omega, \mathcal{F}, P) (Ω,F,P) 和其子 σ \sigma σ-代数 G ⊆ F \mathcal{G} \subseteq \mathcal{F} G⊆F&#xff0c;随机变量 X X X 关于 G \mathcal{G} G 的 条件期望 E [ X ∣ G ] E[X|\mathcal{G}…...

【java实现+4种变体完整例子】排序算法中【计数排序】的详细解析,包含基础实现、常见变体的完整代码示例,以及各变体的对比表格

以下是计数排序的详细解析&#xff0c;包含基础实现、常见变体的完整代码示例&#xff0c;以及各变体的对比表格&#xff1a; 一、计数排序基础实现 原理 通过统计每个元素的出现次数&#xff0c;按顺序累加得到每个元素的最终位置&#xff0c;并填充到结果数组中。 代码示…...

Qt C++ 解析和处理 XML 文件示例

使用 Qt C 解析和处理 XML 文件 以下是使用 Qt C 实现 XML 文件处理的几种方法&#xff0c;包括解析、创建和修改 XML 文件。 1. 使用 QXmlStreamReader (推荐方式) #include <QFile> #include <QXmlStreamReader> #include <QDebug>void parseXmlWithStr…...

在服务器上部署MinIO Server

MinIO的优势 高性能&#xff1a;MinIO号称是目前速度最快的对象存储服务器&#xff0c;据称在标准硬件上&#xff0c;对象存储的读/写速度最高可以高达183 GB/s和171 GB/s&#xff0c;可惜我的磁盘跟不上 兼容性&#xff1a;MinIO基于Amazon S3协议&#xff0c;并提供了与S3兼…...

第二十七讲:AI+农学导论

关键词:人工智能、农业、作物识别、遥感、机器学习、案例实战 目录 📌 一、为什么农业需要人工智能? 📈 二、AI在农学中的典型应用场景 🧪 三、实战案例:AI识别作物类型(以随机森林为例) ✅ 数据集:iris(模拟作物种类识别) 📦 所需包: 🚀 数据准备: …...

医院科研科AI智能科研支撑平台系统设计架构方案探析

一、系统设计概述 1.1 系统定位 本系统是基于MCP(Model Context Protocol,模型上下文协议)协议构建的智能科研支撑平台,旨在为医院科研科室提供全流程AI辅助能力,覆盖课题立项、数据采集、分析建模到成果转化的完整科研生命周期。系统通过MCP协议实现与医院信息系统的深…...

Python基础总结(七)之条件语句

文章目录 条件语句if一、Python中的真假二、条件语句格式2.1 if语句格式2.2 if-else语句2.3 if-elif-else语句 三、if语句嵌套 条件语句if 条件语句其实就是if语句&#xff0c;在讲解if语句之前需要知道Python中对于真假的判断。 一、Python中的真假 在Python中非0的都为真&…...

Day10【基于encoder- decoder架构实现新闻文本摘要的提取】

实现新闻文本摘要的提取 1. 概述与背景2.参数配置3.数据准备4.数据加载5.主程序6.预测评估7.生成效果8.总结 1. 概述与背景 新闻摘要生成是自然语言处理&#xff08;NLP&#xff09;中的一个重要任务&#xff0c;其目标是自动从长篇的新闻文章中提取出简洁、准确的摘要。近年来…...

深度解析算法之二分查找(2)

17.二分查找 题目链接 给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组 nums 和一个目标值 target &#xff0c;写一个函数搜索 nums 中的 target&#xff0c;如果目标值存在返回下标&#xff0c;否则返回 -1。 示例 1: 输入: nums [-1,0,3,5,9,12], target…...

前端工程化之自动化测试

自动化测试 自动化测试为什么需要测试&#xff1f;什么时候需要考虑测试测试类型前端测试框架单元测试Jest 重点掌握项目示例package.jsonsrc/utils/math.tssrc/utils/math.test.ts进行测试jest.config.js覆盖率直观看覆盖率coverage/lcov-report/index.html src/main.test.tst…...