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.pswp.cn/web/76480.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/76480.shtml
英文地址,請注明出處:http://en.pswp.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; 即時…