Windows软件插件-音视频捕获
下载本插件
音视频捕获就是获取电脑外接的话筒,摄像头,或线路输入的音频和视频。
本插件捕获电脑外接的音频和视频。最多可以同时获取4个视频源和4个音频源。插件可以在win32和MFC程序中使用。
使用方法
首先,加载本“捕获”DLL,获得DLL模块句柄。
HMODULE hCap = LoadLibrary(L"捕获.dll");//加载捕获模块
获取所有导出函数的地址。
typedef int(__cdecl *MYPROC_Sample)(LONGLONG star, LONGLONG end, BYTE* pB, LONG len);struct CAP_INIT
{MYPROC_Sample VideoSample0 = NULL;//视频样本输出函数MYPROC_Sample VideoSample1 = NULL;MYPROC_Sample VideoSample2 = NULL;MYPROC_Sample VideoSample3 = NULL;MYPROC_Sample AudioSample0 = NULL;//音频样本输出函数MYPROC_Sample AudioSample1 = NULL;MYPROC_Sample AudioSample2 = NULL;MYPROC_Sample AudioSample3 = NULL;
};
struct CAP_VIDEO_INFO
{UINT32 Width=0;//视频宽,单位像素UINT32 Height=0;//视频高,单位像素double nFrames=0;//帧率WCHAR Name[MAX_PATH];//视频捕获设备名称
};
struct CAP_AUDIO_INFO
{UINT32 nCh;//声道数WCHAR Name[MAX_PATH];//音频捕获设备名称
};typedef int(__cdecl *MYPROC_CAP_Init)(CAP_INIT init, CAP_VIDEO_INFO*& VideoInfo, CAP_AUDIO_INFO*& AudioInfo);
typedef int(__cdecl *MYPROC_VOID)();MYPROC_CAP_Init CAP_Init=NULL;
MYPROC_VOID CAP_ReleaseDevice=NULL;if (hCap){CAP_Init=(MYPROC_CAP_Init)GetProcAddress(hCap, "Init");//“初始化”函数CAP_ReleaseDevice=(MYPROC_VOID)GetProcAddress(hCap, "ReleaseDevice");//“释放所有捕获设备”函数}
定义8个全局函数,以提供给初始化函数;全局函数用于输出视频和音频样本。
int VideoSample0(LONGLONG star, LONGLONG end, BYTE* pB, LONG len)//视频流1样本输出函数
{return 1;
}int VideoSample1(LONGLONG star, LONGLONG end, BYTE* pB, LONG len)//视频流2样本输出函数
{return 1;
}int VideoSample2(LONGLONG star, LONGLONG end, BYTE* pB, LONG len)//视频流3样本输出函数
{return 1;
}int VideoSample3(LONGLONG star, LONGLONG end, BYTE* pB, LONG len)//视频流4样本输出函数
{return 1;
}int AudioSample0(LONGLONG star, LONGLONG end, BYTE* pB, LONG len)//音频流1样本输出函数
{return 1;
}int AudioSample1(LONGLONG star, LONGLONG end, BYTE* pB, LONG len)//音频流2样本输出函数
{return 1;
}int AudioSample2(LONGLONG star, LONGLONG end, BYTE* pB, LONG len)//音频流3样本输出函数
{return 1;
}int AudioSample3(LONGLONG star, LONGLONG end, BYTE* pB, LONG len)//音频流4样本输出函数
{return 1;
}CAP_INIT CapInit;CapInit.VideoSample0 = &VideoSample0;//为视频,音频源指定输出函数CapInit.VideoSample1 = &VideoSample1;CapInit.VideoSample2 = &VideoSample2;CapInit.VideoSample3 = &VideoSample3;CapInit.AudioSample0 = &AudioSample0;CapInit.AudioSample1 = &AudioSample1;CapInit.AudioSample2 = &AudioSample2;CapInit.AudioSample3 = &AudioSample3;
捕获视频和音频。
CAP_Init函数为每一个捕获设备(接口为IMFMediaSource)创建1个源读取器(接口为IMFSourceReader),并指定输出媒体类型;
为每一个捕获设备创建1个线程,在线程中使用源读取器接口读取样本,反复调用CAP_INIT结构提供的样本输出函数输出视频和音频样本。
CAP_VIDEO_INFO* pVInfo = new CAP_VIDEO_INFO[4];//结构数组用于存储视频流,音频流信息CAP_VIDEO_INFO* pAInfo = new CAP_AUDIO_INFO[4];WORD w = CAP_Init(CapInit, pVInfo, pAInfo);//获取捕获设备,创建视频流,音频流VCount = LOBYTE(w); //获取视频流数量ACount = HIBYTE(w);//获取音频流数量
释放所有获取到的捕获设备。
函数将终止所有捕获线程,并释放所有源读取器接口。
CAP_ReleaseDevice();
有关本插件的使用示例,可以看后续文章“Windows应用-音视频捕获”。
“捕获”DLL的全部代码
DLL.h
#pragma once
#include <SDKDDKVer.h>#define WIN32_LEAN_AND_MEAN // 从 Windows 头中排除极少使用的资料
#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // 某些 CString 构造函数将是显式的
#define _AFX_NO_MFC_CONTROLS_IN_DIALOGS // 移除对话框中的 MFC 控件支持#ifndef VC_EXTRALEAN
#define VC_EXTRALEAN // 从 Windows 头中排除极少使用的资料
#endif#include <afx.h>
#include <afxwin.h> // MFC 核心组件和标准组件
#include <afxext.h> // MFC 扩展
#ifndef _AFX_NO_OLE_SUPPORT
#include <afxdtctl.h> // MFC 对 Internet Explorer 4 公共控件的支持
#endif
#ifndef _AFX_NO_AFXCMN_SUPPORT
#include <afxcmn.h> // MFC 对 Windows 公共控件的支持
#endif // _AFX_NO_AFXCMN_SUPPORT// Windows 头文件:
#include <windows.h>#include "mfidl.h"
#include "mfapi.h"
#pragma comment(lib, "mfuuid")
#pragma comment(lib, "mf")
#pragma comment(lib, "mfplat")#include "mfreadwrite.h"
#pragma comment(lib, "mfreadwrite")#ifndef SAFE_RELEASE
#define SAFE_RELEASEtemplate <class T> void SafeRelease(T** ppT)
{if (*ppT){(*ppT)->Release();*ppT = NULL;}
}#endif //SAFE_RELEASEtypedef int(__cdecl *MYPROC_Sample)(LONGLONG star, LONGLONG end, BYTE* pB, LONG len);struct CAP_INIT
{MYPROC_Sample VideoSample0 = NULL;//视频样本输出函数MYPROC_Sample VideoSample1 = NULL;MYPROC_Sample VideoSample2 = NULL;MYPROC_Sample VideoSample3 = NULL;MYPROC_Sample AudioSample0 = NULL;//音频样本输出函数MYPROC_Sample AudioSample1 = NULL;MYPROC_Sample AudioSample2 = NULL;MYPROC_Sample AudioSample3 = NULL;
};struct CAP_VIDEO_INFO
{UINT32 Width=0;//视频宽,单位像素UINT32 Height=0;//视频高,单位像素double nFrames=0;//帧率WCHAR Name[MAX_PATH];//视频捕获设备名称
};struct CAP_AUDIO_INFO
{UINT32 nCh;//声道数WCHAR Name[MAX_PATH];//音频捕获设备名称
};class Capture
{
public:Capture();~Capture();CAP_INIT mInit;CAP_VIDEO_INFO mVideoInfo[4];HANDLE hEXIT = NULL;HANDLE hVThread[4];HANDLE hAThread[4];IMFSourceReader* pVideoReader[4];//视频源读取器指针数组IMFSourceReader* pAudioReader[4];//音频源读取器指针数组void GetDevice(int& mVideo, CAP_VIDEO_INFO* VideoInfo, int& mAudio, CAP_AUDIO_INFO* AudioInfo);//获取前4个音频,视频捕获设备void ReleaseDevice();//释放捕获的设备int Init(CAP_INIT init, CAP_VIDEO_INFO*& VideoInfo, CAP_AUDIO_INFO*& AudioInfo);//初始化捕获设备
};struct PARAM
{Capture* pCapture = NULL;int index;
};
DLL.cpp
#include "DLL.h"Capture::Capture()
{HRESULT hr = MFStartup(MF_VERSION);hEXIT = CreateEvent(NULL, TRUE, FALSE, NULL);//创建“退出”事件。手动重置,初始无信号
}Capture::~Capture()
{CloseHandle(hEXIT);MFShutdown();
}DWORD WINAPI AudioThread(LPVOID lp);
DWORD WINAPI VideoThread(LPVOID lp);void Capture::GetDevice(int& mVideo, CAP_VIDEO_INFO* VideoInfo, int& mAudio, CAP_AUDIO_INFO* AudioInfo)//获取捕获设备
{HRESULT hr;IMFAttributes *pVideoAttributes = NULL;hr = MFCreateAttributes(&pVideoAttributes, 1);hr = pVideoAttributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);UINT32 countV;IMFActivate **ppVideoDevices = NULL;hr = MFEnumDeviceSources(pVideoAttributes, &ppVideoDevices, &countV);//枚举视频捕获设备SafeRelease(&pVideoAttributes);if (countV > 4){for (int i = 4; i < (int)countV; i++)//释放4个设备后面的设备{ppVideoDevices[i]->Release();}countV = 4;}mVideo = countV;for (DWORD i = 0; i < countV; i++)//获取所有视频源信息,为视频源创建读取样本线程{hr = S_OK;WCHAR *szFriendlyName = NULL;UINT32 cchName;hr = ppVideoDevices[i]->GetAllocatedString(MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, &szFriendlyName, &cchName); //获取显示名称wmemset(VideoInfo[i].Name, 0, MAX_PATH);CopyMemory(VideoInfo[i].Name, szFriendlyName, 2 * (wcslen(szFriendlyName) + 1));CoTaskMemFree(szFriendlyName);IMFMediaSource *pSource = NULL;if (SUCCEEDED(hr)){hr = ppVideoDevices[i]->ActivateObject(IID_IMFMediaSource, (void**)&pSource);//激活对象}IMFAttributes* pIMFAttributes = NULL;if (SUCCEEDED(hr)){hr = MFCreateAttributes(&pIMFAttributes, 0);//创建空的属性}if (SUCCEEDED(hr)){hr = pIMFAttributes->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, (UINT32)1);//使用基于硬件的媒体基础转换}if (SUCCEEDED(hr)){hr = pIMFAttributes->SetUINT32(MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING, (UINT32)1);//允许源读取器进行有限的视频处理}hr = MFCreateSourceReaderFromMediaSource(pSource, pIMFAttributes, &pVideoReader[i]);//从媒体源创建源读取器SafeRelease(&pIMFAttributes); SafeRelease(&pSource);IMFMediaType* pVideoMTV = NULL;if (SUCCEEDED(hr)){hr = MFCreateMediaType(&pVideoMTV);//创建空的媒体类型}if (SUCCEEDED(hr)){hr = pVideoMTV->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);//设置主要类型为视频}if (SUCCEEDED(hr)){hr = pVideoMTV->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32);//设置子类型RGB32}if (SUCCEEDED(hr)){hr = pVideoReader[i]->SetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, NULL, pVideoMTV);//设置源读取器视频输出媒体类型}SafeRelease(&pVideoMTV);IMFMediaType* pMT = NULL;if (SUCCEEDED(hr)){hr = pVideoReader[i]->GetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, &pMT);//获取源读取器视频输出媒体类型。此时可以获得完整的输出媒体类型信息}UINT32 Width, Height;if (SUCCEEDED(hr)){hr = MFGetAttributeSize(pMT, MF_MT_FRAME_SIZE, &Width, &Height);//获取视频宽高VideoInfo[i].Width = Width; VideoInfo[i].Height = Height;}UINT32 Numerator, Denominator;if (SUCCEEDED(hr)){hr = MFGetAttributeRatio(pMT, MF_MT_FRAME_RATE, &Numerator, &Denominator);//获取帧率VideoInfo[i].nFrames = (double)Numerator / (double)Denominator;}SafeRelease(&pMT);mVideoInfo[i] = VideoInfo[i];PARAM* pParam = new PARAM();pParam->index = i;pParam->pCapture = this;hVThread[i] = CreateThread(NULL, 0, VideoThread, pParam, 0, NULL);//为视频源创建读取线程}CoTaskMemFree(ppVideoDevices);//释放视频捕获设备接口指针数组使用的内存IMFAttributes *pAudioAttributes = NULL;hr = MFCreateAttributes(&pAudioAttributes, 1);//创建空的属性hr = pAudioAttributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_GUID);UINT32 countA;IMFActivate **ppAudioDevices = NULL;hr = MFEnumDeviceSources(pAudioAttributes, &ppAudioDevices, &countA);//枚举音频捕获设备SafeRelease(&pAudioAttributes);if (countA > 4){for (int i = 4; i < (int)countA; i++)//释放4个设备后面的设备{ppAudioDevices[i]->Release();}countA = 4;}mAudio = countA;for (DWORD i = 0; i < countA; i++){hr = S_OK;WCHAR *szFriendlyName = NULL;UINT32 cchName;hr = ppAudioDevices[i]->GetAllocatedString(MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, &szFriendlyName, &cchName); //获取捕获设备名称wmemset(AudioInfo[i].Name, 0, MAX_PATH);CopyMemory(AudioInfo[i].Name, szFriendlyName, 2*(wcslen(szFriendlyName) + 1));CoTaskMemFree(szFriendlyName);IMFMediaSource *pSource = NULL;if (SUCCEEDED(hr)){hr = ppAudioDevices[i]->ActivateObject(IID_IMFMediaSource, (void**)&pSource);//激活对象}IMFAttributes* pIMFAttributes = NULL;if (SUCCEEDED(hr)){hr = MFCreateAttributes(&pIMFAttributes, 0);}if (SUCCEEDED(hr)){hr = pIMFAttributes->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, (UINT32)1);//使用基于硬件的媒体基础转换}if (SUCCEEDED(hr)){hr = pIMFAttributes->SetUINT32(MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING, (UINT32)1);//允许源读取器进行有限的视频处理}if (SUCCEEDED(hr)){hr = MFCreateSourceReaderFromMediaSource(pSource, pIMFAttributes, &pAudioReader[i]);//从媒体源创建源读取器}SafeRelease(&pIMFAttributes);SafeRelease(&pSource);IMFMediaType* pAudioMTA = NULL;if (SUCCEEDED(hr)){hr = MFCreateMediaType(&pAudioMTA);//创建空的媒体类型}if (SUCCEEDED(hr)){hr = pAudioMTA->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);//设置主要类型音频}if (SUCCEEDED(hr)){hr = pAudioMTA->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM);//设置子类型PCM}if (SUCCEEDED(hr)){hr = pAudioMTA->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, (UINT32)16);//设置样本16位}if (SUCCEEDED(hr)){hr = pAudioMTA->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, (UINT32)48000);//设置样本采样率48000}if (SUCCEEDED(hr)){hr = pAudioReader[i]->SetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, NULL, pAudioMTA);//设置音频输出媒体类型(此时未提供媒体类型全部信息)}SafeRelease(&pAudioMTA);IMFMediaType* pMT = NULL;if (SUCCEEDED(hr)){hr = pAudioReader[i]->GetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, &pMT);//获取音频输出媒体类型,此时可以得到完整的媒体类型信息}UINT32 nch;if (SUCCEEDED(hr)){hr = pMT->GetUINT32(MF_MT_AUDIO_NUM_CHANNELS, &nch);//获取声道数AudioInfo[i].nCh = nch;}SafeRelease(&pMT);PARAM* pParam = new PARAM();pParam->index = i;pParam->pCapture = this;hAThread[i] = CreateThread(NULL, 0, AudioThread, pParam, 0, NULL);//为音频源创建读取线程}CoTaskMemFree(ppAudioDevices);//释放音频捕获设备接口指针数组使用的内存
}DWORD WINAPI VideoThread(LPVOID lp)//视频源读取样本线程共用函数
{PARAM* pParam = (PARAM*)lp;Capture* pCapture = pParam->pCapture;int index = pParam->index;HRESULT hr;IMFSourceReader* pIMFSourceReader = pCapture->pVideoReader[index];double dur = (double)10000000 / pCapture->mVideoInfo[index].nFrames;//计算1帧持续时间IMFSample* pMFSample = NULL; DWORD flags;
Agan:hr = pIMFSourceReader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, NULL, &flags, NULL, &pMFSample);//读取视频样本if (hr == S_OK && pMFSample){LONGLONG Cur, Dur;hr = pMFSample->GetSampleTime(&Cur);//获取显示时间hr = pMFSample->GetSampleDuration(&Dur);//获取持续时间DWORD Lt;hr = pMFSample->GetTotalLength(&Lt);//获取有效长度DWORD count;hr = pMFSample->GetBufferCount(&count);//获取缓冲区数量IMFMediaBuffer* pMFBuffer = NULL;if (count == 1)//如果只有1个缓冲区{hr = pMFSample->GetBufferByIndex(0, &pMFBuffer);}else//如果有多个缓冲区{hr = pMFSample->ConvertToContiguousBuffer(&pMFBuffer);}BYTE* pSB = NULL;hr = pMFBuffer->Lock(&pSB, NULL, NULL);//锁定缓冲区switch (index){case 0://第1个视频源pCapture->mInit.VideoSample0(Cur, Cur + Dur, pSB, (LONG)Lt);//发送样本break;case 1://第2个pCapture->mInit.VideoSample1(Cur, Cur + Dur, pSB, (LONG)Lt);break;case 2:pCapture->mInit.VideoSample2(Cur, Cur + Dur, pSB, (LONG)Lt);break;case 3:pCapture->mInit.VideoSample3(Cur, Cur + Dur, pSB, (LONG)Lt);break;}hr = pMFBuffer->Unlock();//解锁缓冲区SafeRelease(&pMFBuffer); SafeRelease(&pMFSample);//释放接口}DWORD mExit = WaitForSingleObject(pCapture->hEXIT, 0);if (mExit == WAIT_OBJECT_0)//有“退出”信号{delete pParam;return 0;}goto Agan;
}DWORD WINAPI AudioThread(LPVOID lp)//读取音频源样本线程共用函数
{PARAM* pParam = (PARAM*)lp;Capture* pCapture = pParam->pCapture;int index = pParam->index;HRESULT hr;IMFSourceReader* pIMFSourceReader = pCapture->pAudioReader[index];IMFSample* pMFSample = NULL; DWORD flags;
Agan:hr = pIMFSourceReader->ReadSample(MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, NULL, &flags, NULL, &pMFSample);//读取音频样本if (hr == S_OK && pMFSample){LONGLONG Cur, Dur;hr = pMFSample->GetSampleTime(&Cur);//获取显示时间hr = pMFSample->GetSampleDuration(&Dur);//获取持续时间DWORD Lt;hr = pMFSample->GetTotalLength(&Lt);//获取有效长度DWORD count;hr = pMFSample->GetBufferCount(&count);//获取缓冲区数量IMFMediaBuffer* pMFBuffer = NULL;if (count == 1)//如果只有1个缓冲区{hr = pMFSample->GetBufferByIndex(0, &pMFBuffer);}else//如果有多个缓冲区{hr = pMFSample->ConvertToContiguousBuffer(&pMFBuffer);}BYTE* pSB = NULL;hr = pMFBuffer->Lock(&pSB, NULL, NULL);//锁定缓冲区switch (index){case 0://第1个音频源pCapture->mInit.AudioSample0(Cur, Cur + Dur, pSB, (LONG)Lt);//发送样本break;case 1://第2个pCapture->mInit.AudioSample1(Cur, Cur + Dur, pSB, (LONG)Lt);break;case 2:pCapture->mInit.AudioSample2(Cur, Cur + Dur, pSB, (LONG)Lt);break;case 3:pCapture->mInit.AudioSample3(Cur, Cur + Dur, pSB, (LONG)Lt);break;}hr = pMFBuffer->Unlock();//解锁缓冲区SafeRelease(&pMFBuffer); SafeRelease(&pMFSample);//释放接口}DWORD mExit = WaitForSingleObject(pCapture->hEXIT, 0);if (mExit == WAIT_OBJECT_0)//有“退出”信号{delete pParam;return 0;}goto Agan;
}void Capture::ReleaseDevice()
{SetEvent(hEXIT);//发送“退出”信号WaitForMultipleObjects(4, hVThread, TRUE, INFINITE);//等待所有线程退出WaitForMultipleObjects(4, hAThread, TRUE, INFINITE);for (int i = 0; i < 4; i++)//释放所有视频音频源读取器接口{SafeRelease(&pVideoReader[i]); SafeRelease(&pAudioReader[i]);}
}int Capture::Init(CAP_INIT init, CAP_VIDEO_INFO*& VideoInfo, CAP_AUDIO_INFO*& AudioInfo)
{mInit = init;ResetEvent(hEXIT);//设置“退出”无信号int mV, mA;GetDevice(mV,VideoInfo, mA,AudioInfo);//获取前4个音频,视频捕获设备return (int)MAKEWORD((BYTE)mA, (BYTE)mV);
}Capture mCapture;#ifdef __cplusplus // If used by C++ code,
extern "C" { // we need to export the C interface
#endif__declspec(dllexport) int __cdecl Init(CAP_INIT init, CAP_VIDEO_INFO*& VideoInfo, CAP_AUDIO_INFO*& AudioInfo)//最多获取前4个视频音频捕获设备{return mCapture.Init(init, VideoInfo, AudioInfo);}__declspec(dllexport) int __cdecl ReleaseDevice()//释放获取到的捕获设备{mCapture.ReleaseDevice();return 0;}#ifdef __cplusplus
}
#endif
相关文章:
Windows软件插件-音视频捕获
下载本插件 音视频捕获就是获取电脑外接的话筒,摄像头,或线路输入的音频和视频。 本插件捕获电脑外接的音频和视频。最多可以同时获取4个视频源和4个音频源。插件可以在win32和MFC程序中使用。 使用方法 首先,加载本“捕获”DLL,…...
每日算法 - 【Swift 算法】Two Sum 问题:从暴力解法到最优解法的演进
【Swift 算法】Two Sum 问题:从暴力解法到最优解法的演进 本文通过“Two Sum”问题,带你了解如何从最直观的暴力解法,逐步优化到高效的哈希表解法,并对两者进行对比,适合算法入门和面试准备。 💡 问题描述 …...
2025年,如何制作并部署一个完整的个人博客网站
欢迎访问我的个人博客网站:欢迎来到Turnin的个人博客 github开源地址:https://github.com/Re-restart/my_website 前言 2024年年初,从dji实习回来之后,我一直想着拓宽自己的知识边界。在那里我发现虽然大家不用java,…...
深度学习框架---TensorFlow概览
一、TensorFlow 概述 1. 发展历程 1.x 版本:基于静态图(Graph)和会话(Session),需预先定义计算图,调试较复杂。2.x 版本:默认启用动态图(Eager Execution)&…...
鸿蒙OSUniApp制作自定义的下拉菜单组件(鸿蒙系统适配版)#三方框架 #Uniapp
UniApp制作自定义的下拉菜单组件(鸿蒙系统适配版) 前言 在移动应用开发中,下拉菜单是一个常见且实用的交互组件,它能在有限的屏幕空间内展示更多的选项。虽然各种UI框架都提供了下拉菜单组件,但在一些特定场景下&…...
扣子(Coze)案例:工作流生成小红书心理学卡片
大家好!我是 Robin。专注于 AI 技术探索与实践,持续分享 Coze 智能体、Coze 模板,以及 Coze 工作流搭建案例。 工作流智能体作用: 输入需要生成小红书心理学知识卡片的数量,工作流自动批量生成图文。 首先演示一下生…...
深度理解用于多智能体强化学习的单调价值函数分解QMIX算法:基于python从零实现
引言:合作式多智能体强化学习与功劳分配 在合作式多智能体强化学习(MARL)中,多个智能体携手合作,共同达成一个目标,通常会收到一个团队共享的奖励。在这种场景下,一个关键的挑战就是功劳分配&a…...
C语言经典笔试题目分析(持续更新)
1. 描述下面代码中两个static 各自的含义 static void func (void) {static unsigned int i; }static void func(void) 中的 static 作用对象:函数 func。 含义: 限制函数的作用域(链接属性),使其仅在当前源文件&…...
射击游戏demo11
完善地图,加载出装饰品,检测人员与地面的碰撞,检测子弹与岩壁的碰撞,检测手雷与地面的碰撞。 import pygame import sys import os import random import csv # 初始化Pygame pygame.init()# 屏幕宽度 SCREEN_WIDTH 1200 # 屏幕高…...
多智能体Multi-Agent应用实战与原理分析
一:Agent 与传统工具调用的对比 在当今的开发环境中,Agent 的出现极大地简化了工作流程。其底层主要基于提示词、模型和工具。用户只需向 Agent 输入需求,Agent 便会自动分析需求,并利用工具获取最终答案。而传统方式下,若没有 Agent,我们则需要手动编码来执行工具,还要…...
专项智能练习(定义判断)_DA_01
1. 单选题 热传导是介质内无宏观运动时的传热现象,其在固体、液体和气体中均可发生。但严格而言,只有在固体中才是纯粹的热传导,在流体(泛指液体和气体)中又是另外一种情况,流体即使处于静止状态࿰…...
关于NLP自然语言处理的简单总结
参考: 什么是自然语言处理?看这篇文章就够了! - 知乎 (zhihu.com) 所谓自然语言理解,就是研究如何让机器能够理解我们人类的语言并给出一些回应。 自然语言处理(Natural Language Processing,NLP࿰…...
SLAM定位与地图构建
SLAM介绍 SLAM全称Simultaneous Localization And Mapping,中文名称同时定位与地图构建。旨在让移动设备在未知环境中同时完成以下两个任务(定位需要地图,而建图又依赖定位信息,两者互为依赖): 定位&#…...
REST架构风格介绍
一.REST(表述性状态转移) 1.定义 REST(Representational State Transfer)是由 Roy Fielding 在 2000 年提出的一种软件架构风格,用于设计网络应用的通信模式。它基于 HTTP 协议,强调通过统一的接口&#…...
前端流行框架Vue3教程:16. 组件事件配合`v-model`使用
组件事件配合v-model使用 如果是用户输入,我们希望在获取数据的同时发送数据配合v-model 来使用,帮助理解组件间的通信和数据绑定。 🧩 第一步:创建子组件(SearchComponent.vue) 这个组件用于处理用户的搜…...
5月15日day26打卡
函数专题1 知识点回顾: 函数的定义变量作用域:局部变量和全局变量函数的参数类型:位置参数、默认参数、不定参数传递参数的手段:关键词参数传递参数的顺序:同时出现三种参数类型时 作业: 题目1:…...
Java中的深拷贝与浅拷贝
什么是拷贝 在Java中,拷贝是指创建一个对象的副本。拷贝主要分为两种类型:浅拷贝(Shallow Copy)和深拷贝(Deep Copy)。理解这两种拷贝的区别对于编写正确的Java程序非常重要,特别是在处理对象引用时。 浅拷贝(Shallow Copy) 浅拷贝是指创建…...
springboot AOP中,通过解析SpEL 表达式动态获取参数值
切面注解 import com.bn.document.constants.FmDeptCatalogueConstants;import java.lang.annotation.*;Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) public interface FmDeptCatalogueAopAnnotation {/*** 权限类型*/FmDeptCatalogueConstants value();/…...
【论信息系统项目的合同管理】
论信息系统项目的合同管理 论文要求写作要点正文前言一、合同的签订管理二、合同履行管理三、合同变更管理四、合同档案管理五、合同违约索赔管理结语 论文要求 项目合同管理通过对项目合同的全生命周期进行管理,来回避和减轻可识别的项目风险。 请以“论信息系统项…...
redis持久化方式
一、RDB redis database:快照,某一时刻将内存中的数据,写入磁盘中生成1个dump.rdb文件RDB的触发方式: 手动触发:save(阻塞主进程,阻塞其它指令,保证数据一致性)、bgsave…...
free void* 指令
https://stackoverflow.com/questions/2182103/is-it-ok-to-free-void free(ptr) 仅释放指针指向的内存,不会修改指针变量本身的值。调用后,ptr 仍然指向原来的地址(称为 "悬空指针"),但该地址对应的内存已…...
ADS1220高精度ADC(TI)——应用 源码
文章目录 德州仪器ADS1220概述资料引脚&封装布线寄存器配置寄存器0(00h)配置寄存器1(01h)配置寄存器2(02h)配置寄存器3(03h) 连续转换流程驱动源码ads1220.cads1220.h 德州仪器A…...
mysql-Java手写分布式事物提交流程
准备 innodb存储引擎开启支持分布式事务 set global innodb_support_axon分布式的流程 详细流程: XA START ‘a’; 作用:开始一个新的XA事务,并分配一个唯一的事务ID ‘a’。 说明:在这个命令之后,所有后续的SQL操…...
红黑树:数据世界的平衡守护者
在 C 算法的神秘森林里,红黑树是一棵充满智慧的 “魔法树”。它既不像普通二叉搜索树那样容易失衡,也不像 AVL 树对平衡要求那么苛刻。作为 C 算法小白,今天就和大家一起深入探索红黑树的奥秘,看看它是如何成为数据世界的平衡守护…...
哈夫曼树完全解析:从原理到应用
目录 一、核心概念 二、构造全流程解析 三、编码生成机制 四、C语言实现关键代码 五、核心特性深度解读 六、现代应用场景 七、压缩实战演示 一、核心概念 哈夫曼树(最优二叉树)是带权路径长度(WPL)最短的树形结构&#x…...
如何在 Windows 命令提示符中创建多个文件夹和多个文件
如何在 Windows 命令提示符中创建多个文件夹和多个文件 虽然大多数用户习惯使用 Windows 图形界面来创建文件夹,但如果你需要一次性创建多个文件夹或文件,如同在类Unix系统中可以使用mkdir和touch命令一样,windows下也有创建目录和文件的对应…...
【Java】Spring的声明事务在多线程场景中失效问题。
大家都知道Spring的声明式事务在多线程当中会失效,来看一下如下案例。 按照如下方式,b()方法抛出异常,由于父子线程导致事务失效,a()会成功插入,但是b()不会。 因此成功插入一条数据,事务失效。 Component public class UserServ…...
多平台图标设计与管理的终极解决方案
IconWorkshop Pro 是一款由Axialis团队开发的专业图标设计与制作软件,专注于为设计师、开发者及企业用户提供高效且灵活的图标创作解决方案。该软件凭借其强大的功能与跨平台适配性,成为Windows、macOS、iOS、Android等多系统图标设计的首选工具之一。 …...
【搭建Node-RED + MQTT Broker实现AI大模型交互】
搭建Node-RED MQTT Broker实现AI大模型交互 搭建Node-RED MQTT Broker实现AI大模型交互一、系统架构二、环境准备与安装1. 安装Node.js2. 安装Mosquitto MQTT Broker3. 配置Mosquitto4. 安装Node-RED5. 配置Node-RED监听所有网络接口6. 启动Node-RED 三、Node-RED流程配置1. …...
小结: js 在浏览器执行原理
浏览器多进程与多线程 现代浏览器的标签环境隔离主要通过多进程架构和多线程机制实现,以确保安全、性能和稳定性。以下是浏览器实现标签环境隔离的多进程和多线程交互架构的详细解析: ------------------- ------------------- -----------…...
C++核心编程--2 引用
引用就是给变量起别名,操作引用就等于操作原始变量。 2.1 引用基本用法 int var 10; int & r_var var; 2.2 注意事项 声明时必须初始化不允许更改引用指向的原始变量 2.3 引用作为函数参数传递 简化指针修饰函数参数 2.4 引用作为函数返回值 不要返回…...
音频/AI/BLE/WIFI/玩具/商业等方向的论坛网站总结
我爱音频网 我爱音频网 - 我们只谈音频,丰富的TWS真无线蓝牙耳机拆解报告 (52audio.com) 中国人工智能学会 中国人工智能学会 (caai.cn) AIIA人工智能网 https://www.aiiaw.com/ 世界人工智能论坛 世界人工智能论坛 - (amtbbs.org) 36氪 36氪_让一部分人先…...
告别碎片化!MCP 带来 AI Agent 开发生态的革命性突破
引言: 在当今的智能客服系统开发中,开发者常常面临一个棘手的挑战:需要整合用户的 CRM 数据、知识库和实时聊天记录。然而,由于缺乏统一的标准,每个团队都不得不手动实现这些集成。这不仅延长了开发周期,还…...
centos7部署mysql5.7
1.下载mysql的官方yum源 wget http://dev.mysql.com/get/mysql57-community-release-el7-11.noarch.rpm2.安装yum源 yum -y install mysql57-community-release-el7-11.noarch.rpm3.安装秘钥文件 rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-20224.安装mysql5.7…...
linux dbus
Linux D-Bus(Desktop Bus)是一种进程间通信(IPC)机制,主要用于Linux桌面环境和系统服务之间的消息传递。它允许不同的应用程序或系统组件以高效、安全的方式相互通信,是现代Linux桌面(如GNOME、KDE)的核心基础设施之一。 1. D-Bus 的核心概念 消息总线(Message Bus):…...
计量——异方差的检验及其修正
目录 1.异方差的检验 1 BP检验 2white检验 2.异方差的修正 1.异方差的检验 1 BP检验 选择检验方法:BP BP检验的实际步骤(非机器): 1.y对所有x进行回归,得到残差u。计算残差的平方u^2 2.u^2对所有x进行回归&#…...
操作系统学习笔记第3章 内存管理(灰灰题库)
1. 单选题 某页式存储管理系统中,主存为 128KB,分成 32 块,块号为 0、1、2、3、…、31。某作业有 5 块,其页号为 0、1、2、3、4,被分别装入主存的 3、8、4、6、9 块中。有一逻辑地址为 [3, 70](其中方括号中…...
vue3项目中使用CanvasEditor开箱即用(组件的形式,组件封装好了)
canvas-editor-vue 这是canvas-editor项目的vue版本,我封装成组建了,当然了这是个已经把canvas-editor封装好的的Vue项目 项目地址GitHub地址:GitHub - aini-aini/canvas-editor-vue: this is a project than can be used in vue project as Componentthis is a project than…...
人体肢体工作识别-一步几个脚印从头设计数字生命——仙盟创梦IDE
人体肢体识别是借助计算机视觉、传感器等技术,对人体各肢体的位置、动作、姿态等进行检测与分析的技术。其在医疗健康、智能交互、运动训练、安全监控等多个领域具有重要价值, 示例代码 import cv2 import mediapipe as mp import numpy as np import c…...
【重磅】配电网智能软开关和储能联合规划
目录 1 主要内容 目标函数 数据说明 节点系统图 2 部分代码 3 程序结果 4 下载链接 1 主要内容 该程序复现《具有源荷不平衡特性的配电网智能软开关和储能联合规划》部分模型,未考虑聚类分析和分布鲁棒部分,就智能软开关和储能联合规划部分进行了…...
实验6 电子邮件
实验6 电子邮件 1、实验目的 理解电子邮件系统基本结构 理解客户端和服务器端,以及服务器之间的通信 分析理解SMTP,POP3协议 2、实验环境 硬件要求:阿里云云主机ECS 一台。 软件要求:Linux/ Windows 操作系统 3、实验内容…...
NHANES指标推荐:OBS
文章题目:Association between oxidative balance score and all-cause and cancer-specific mortality among cancer survivors DOI:10.3389/fimmu.2025.1541675 中文标题:癌症幸存者氧化平衡评分与全因死亡率和癌症特异性死亡率之间的关联 …...
python-修改图片背景色
在Python中,可以使用图像处理库(如OpenCV或Pillow)来修改图片的背景色。通常,修改背景色的流程包括以下步骤: 1、对图片进行分割,识别前景和背景。 2、对背景区域进行颜色替换。 下面是两种实现方法&#x…...
数据结构与算法--顺序表--单链表
一 顺序表 需要指向存储位置的基地址分配一段连续的内存用length记录实际的元素的个数,也即顺序表的长度,因为顺序表是允许删除和插入元素的不需要定义数组 1.1 普通结构体数组实现版本,实现白色的点以一定的步长移除画面,大量fo…...
MATLAB安装全攻略:常见问题与解决方案
MATLAB安装常见问题与解决方案 一、系统兼容性验证 安装前需确认操作系统满足MATLAB版本要求: Windows 10版本1903及以上(64位)macOS Monterey 12.6及以上Ubuntu 22.04 LTS及以上 验证命令示例: # Linux系统验证 lsb_release…...
constexpr 关键字的意义(入门)
author: hjjdebug date: 2025年 05月 15日 星期四 16:03:33 CST description: constexpr 关键字的意义(入门) constexpr 是c11 引入的一个关键字, 代表了一种属性. 文章目录 1. constexpr 修饰的变量, 在编译期间就可以得到其数值.2. constexpr 修饰的函数, 可以在编译期间被调…...
aptitude 深度教程:从基础到生产实践
目录 一、aptitude 基础:核心概念与环境准备 1.1 aptitude 是什么? 1.2 安装与环境配置 二、aptitude 核心操作:从命令行到交互式界面 2.1 命令行基础操作 2.2 交互式界面(TUI)入门 三、高级功能:依赖管理与版本控制 3.1 依赖冲突解决实战 3.2 版本锁定与降级 3…...
嵌入式开发学习日志(数据结构--双链表)Day21
一、双链表 1.定义 双向链表是在单链表的每个结点中,再设置一个指向其钱去节点的指针域。 2、声明文件 3、创建表头 4、头插 5、 遍历 6、尾插、 7、指定插 8、查找 9、修改 10.、删除 11、逆序 12、销毁链表 13、main.c 三、扩展:工程管理工具&#…...
抢购Python代码示例与技术解析
引言:抢购系统的技术挑战 在当今电子商务高度发达的时代,抢购活动已成为各大电商平台吸引用户的重要手段。然而,高并发、低延迟的抢购场景对系统设计提出了严峻挑战。本文将提供一个完整的Python抢购代码示例,并深入分析其技术实…...
undefined reference to CPUAllocatorSingleton::instance
它发生的原因是你声明了 CPUAllocatorSingleton 类中的 instance 变量,但没有提供它的定义。 这个错误是链接器无法找到 CPUAllocatorSingleton::instance 的定义。它发生的原因是你声明了 CPUAllocatorSingleton 类中的 instance 变量,但没有提供它的定…...