Windows应用-音视频捕获

article/2025/6/13 0:49:07

下载“Windows应用-音视频捕获”项目
本应用可以同时捕获4个视频源和4个音频源,可以监视视频源图像,监听音频源;可以将视频源图像写入MP4文件,将音频源写入MP3或WAV文件;还可以录制系统播放的声音。本应用使用MFC对话框程序创建。下面是应用的界面图像。
在这里插入图片描述

使用方法

启动应用就可以获取捕获设备,也可以按“重新捕获”按钮,再次捕获。上图是,电脑外接1个摄像头和1个话筒获得的界面图像。红色区域中,左侧是视频捕获设备名称,其右是监视按钮,点击可以显示该捕获设备当前视频图像,“SelAu”按钮点击后,会弹出菜单,菜单项是音频捕获设备名称和系统声音,选择菜单项就可以为视频录制指定声音源。点击“录制”按钮,会将该捕获设备的图像和选择的声音,写入MP4文件。
蓝色区域中,左侧是音频捕获设备名称,其右是监听按钮,点击后,电脑可以播放该捕获设备的声音;监听按钮右边的按钮可以选择录制产生的文件的格式(wav或mp3)。点击“录制”按钮,会将该声音源写入音频文件。

应用开发信息

应用使用了4个插件,捕获.dll,写MP4.dll,写wav.dll,写mp3.dll。有关这4个插件的信息可以看前面的文章。捕获.dll使用媒体基础的方法获取捕获设备。虽然DirectShow也可以实现音视频捕获,但由于媒体基础对象可以在win32和MFC程序中创建并使用,所以使用起来更加灵活。
加载捕获DLL,获取两个导出函数的地址。

typedef int(__cdecl *MYPROC_CAP_Init)(CAP_INIT init, CAP_VIDEO_INFO*& VideoInfo, CAP_AUDIO_INFO*& AudioInfo);
typedef int(__cdecl *MYPROC_VOID)();HMODULE hCap = NULL;//捕获dll模块句柄MYPROC_CAP_Init CAP_Init = NULL;MYPROC_VOID CAP_ReleaseDevice = NULL;hCap = LoadLibrary(L"捕获.dll");//加载捕获模块if (hCap){CAP_Init=(MYPROC_CAP_Init)GetProcAddress(hCap, "Init");//获取捕获模块“初始化”函数地址CAP_ReleaseDevice=(MYPROC_VOID)GetProcAddress(hCap, "ReleaseDevice");//获取捕获模块“释放所有捕获设备”函数地址}

定义捕获初始化结构和视频样本,音频样本输出函数。

typedef int(__cdecl *MYPROC_CAP_Sample)(LONGLONG star, LONGLONG end, BYTE* pB, LONG len);int VideoSample0(LONGLONG star, LONGLONG end, BYTE* pB, LONG len)//视频流0样本输出函数
{return 1;
}
....struct CAP_INIT
{MYPROC_CAP_Sample VideoSample0 = NULL;//视频样本输出函数MYPROC_CAP_Sample VideoSample1 = NULL;MYPROC_CAP_Sample VideoSample2 = NULL;MYPROC_CAP_Sample VideoSample3 = NULL;MYPROC_CAP_Sample AudioSample0 = NULL;//音频样本输出函数MYPROC_CAP_Sample AudioSample1 = NULL;MYPROC_CAP_Sample AudioSample2 = NULL;MYPROC_CAP_Sample AudioSample3 = NULL;
};

调用捕获初始化函数,此时将获取捕获设备,创建视频流和音频流。即,视频音频样本输出函数被反复调用,函数的参数提供时间戳,样本缓冲区指针和样本大小。

	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;struct CAP_VIDEO_INFO
{UINT32 Width = 0;//视频宽,单位像素UINT32 Height = 0;//视频高,单位像素double nFrames = 0;//帧率WCHAR *Name = NULL;//视频捕获设备名称
};struct CAP_AUDIO_INFO
{UINT32 nCh;//声道数WCHAR *Name = NULL;//视频捕获设备名称
};CAP_VIDEO_INFO* pVInfo = NULL;//结构数组用于存储视频流,音频流信息CAP_AUDIO_INFO* pAInfo = NULL;pVInfo = new CAP_VIDEO_INFO[4]; pAInfo = new CAP_AUDIO_INFO[4];//结构数组用于存储视频流,音频流信息	WORD w = CAP_Init(CapInit, pVInfo, pAInfo);//获取捕获设备,创建视频流,音频流ACount = LOBYTE(w); VCount = HIBYTE(w);//获取音频流,视频流数量

在程序退出时,释放所有捕获设备。

	CAP_ReleaseDevice();//释放捕获设备

加载写MP4 DLL,获取所有导出函数地址。

typedef int(__cdecl *MYPROC_MP4_Init)(int index, MW_INIT init);
typedef int(__cdecl *MYPROC_Sample)(BYTE* pB, LONG len);
typedef int(__cdecl *MYPROC_ISample)(int index, BYTE* pB, LONG len);
typedef int(__cdecl *MYPROC_IVSample)(int index, LONGLONG star, LONGLONG end, BYTE* pB, LONG len);
typedef int(__cdecl *MYPROC_IVOID)(int index);
typedef int(__cdecl *H264Set)(int index, VARIANT var);
typedef int(__cdecl *H264Get)(int index, VARIANT& var);HMODULE hMp4 = NULL;MYPROC_VOID MP4_Create = NULL;MYPROC_MP4_Init MP4_Init = NULL;MYPROC_IVSample MP4_VSample = NULL;MYPROC_ISample MP4_ASample = NULL;MYPROC_IVOID MP4_Exit = NULL;MYPROC_IVOID MP4_GetState = NULL;MYPROC_IVOID MP4_Stop = NULL;MYPROC_IVOID MP4_Run = NULL;MYPROC_VOID MP4_DestroyAll = NULL;H264Set H264_SetAdaptiveMode = NULL;//自适应地更改帧速率设置函数H264Set H264_SetRateMode = NULL;//速率控制模式设置函数H264Set H264_SetQuality = NULL;//质量级别设置函数H264Set H264_SetQualityVsSpeed = NULL;//质量/速度权衡设置函数H264Set H264_SetMeanBitRate = NULL;//平均比特率设置函数H264Get H264_GetMeanBitRate = NULL;hMp4 = LoadLibrary(L"写MP4.dll");//加载写MP4模块if (hMp4){MP4_Create = (MYPROC_VOID)GetProcAddress(hMp4, "Create");//创建MP4_Init=(MYPROC_MP4_Init)GetProcAddress(hMp4, "Init");//初始化MP4_VSample=(MYPROC_IVSample)GetProcAddress(hMp4, "WriteVideoSample");//写视频样本MP4_ASample = (MYPROC_ISample)GetProcAddress(hMp4, "WriteAudioSample");//写音频样本MP4_GetState = (MYPROC_IVOID)GetProcAddress(hMp4, "GetState");//获取状态MP4_Run=(MYPROC_IVOID)GetProcAddress(hMp4, "Run");//运行MP4_Stop=(MYPROC_IVOID)GetProcAddress(hMp4, "Stop");//停止MP4_Exit=(MYPROC_IVOID)GetProcAddress(hMp4, "Exit");//退出MP4_DestroyAll = (MYPROC_VOID)GetProcAddress(hMp4, "DestroyAll");H264_SetAdaptiveMode = (H264Set)GetProcAddress(hMp4, "H264_SetAdaptiveMode");//自适应地更改帧速率H264_SetRateMode = (H264Set)GetProcAddress(hMp4, "H264_SetRateControlMode");//速率控制模式H264_SetQuality = (H264Set)GetProcAddress(hMp4, "H264_SetQuality");//质量级别H264_SetQualityVsSpeed = (H264Set)GetProcAddress(hMp4, "H264_SetQualityVsSpeed");//质量/速度权衡H264_SetMeanBitRate = (H264Set)GetProcAddress(hMp4, "H264_SetMeanBitRate");//平均比特率H264_GetMeanBitRate = (H264Get)GetProcAddress(hMp4, "H264_GetMeanBitRate");MP4_Create(); MP4_Create(); MP4_Create(); MP4_Create();//创建4个写MP4对象}

下面以录制1个视频源举例。
开始录制视频源0:

struct MW_INIT
{WCHAR* Path;//输出文件路径UINT VideoWidth;//视频图像宽度,单位像素UINT VideoHeight;//视频图像高度,单位像素double nFramePerSec;//视频每秒帧数BOOL Fixed;//TRUE,样本持续时间是固定值;FALSE,非固定值UINT BIT_RATE;//视频传输率。为0使用默认传输率UINT AudioSamplesPerSec;//音频采样率
};
int mvState0=0//录制状态。在“C音视频捕获Dlg”类中定义MW_INIT MwInit;MwInit.Path = L"D:\\1.mp4";MwInit.VideoWidth = 1280;MwInit.VideoHeight = 720;MwInit.nFramePerSec = 30;MwInit.BIT_RATE = 0;MwInit.Fixed = FALSE;MwInit.AudioSamplesPerSec = 48000;MP4_Init(0, MwInit);MP4_Run(0);mvState0 = 1;//录制状态

暂停录制视频源0:

	if (mvState0 == 1){mvState0 = 2;MP4_Stop(0);}

停止录制视频源0:

	mvState0 = 0;MP4_Stop(0);MP4_Exit(0);

在视频流0样本输出函数中,写视频样本:

//以下变量在“C音视频捕获Dlg”类中定义int mvState0, mvState1, mvState2, mvState3;//视频录制状态。0停止,1运行,2暂停LONGLONG mTime0, mTime1, mTime2, mTime3;BOOL First0, First1, First2, First3;LONGLONG mStar0, mStar1, mStar2, mStar3;LONGLONG mPause0, mPause1, mPause2, mPause3;int VideoSample0(LONGLONG star, LONGLONG end, BYTE* pB, LONG len)//视频流0样本输出函数
{C音视频捕获Dlg* pDlg = (C音视频捕获Dlg*)theApp.m_pMainWnd;if (pDlg->mvState0 == 0)//如果停止{pDlg->First0 = TRUE;pDlg->mTime0 = 0; pDlg->mPause0 = 0;}if (pDlg->mvState0 == 1)//如果运行{if (pDlg->First0){pDlg->First0 = FALSE;pDlg->mStar0 = star;}pDlg->MP4_VSample(0, pDlg->mPause0 + star - pDlg->mStar0, pDlg->mPause0 + end - pDlg->mStar0, pB, len);pDlg->mTime0 = pDlg->mPause0 + end - pDlg->mStar0;}if (pDlg->mvState0 == 2)//如果暂停{pDlg->First0 = TRUE;pDlg->mPause0 = pDlg->mTime0;}return 1;
}

在音频流0样本输出函数中,写音频样本:

int AudioSample0(LONGLONG star, LONGLONG end, BYTE* pB, LONG len)//音频流0样本输出函数
{C音视频捕获Dlg* pDlg = (C音视频捕获Dlg*)theApp.m_pMainWnd;if (pDlg->mvState0 == 1 &&pDlg->mAudioSel0 == 0)//如果图像0需要获取此音频{if (pDlg->MP4_GetState(0))//如果写MP4已运行{pDlg->MP4_ASample(0, pB, len);}}return 1;
}

如果需要全部代码,可以下载“Windows应用-音视频捕获”项目。


http://www.hkcw.cn/article/NwQhLFfDkt.shtml

相关文章

Linux账号和权限管理

1 Linux用户账号和组账号 Linux系统是一个多用户多任务的分时操作系统,任何一个要使用系统资源的用户,都必须首先向系统管理员申请一个账号,然后以这个账号的身份进入系统。用户的账号一方面可以帮助系统管理员对使用系统的用户进行跟踪&…

Linux——初步认识Shell、深刻理解Linux权限

文章目录 文章引入初步认识Shell深入理解Linux下的权限用户的切换使用sudo进行短暂提权权限的深入理解权限的本质权限的意义如何控制权限Linux下的权限属性的分类Linux下对于角色的分类文件的权限查询权限的具体表现普通文件的权限表现chmod指令、chown指令和chgrp指令chmod——…

Redis底层数据结构之深入理解跳表(1)

在上一篇文章中我们详细的介绍了一下Redis中跳表的结构以及为什么Redis要引入跳表而不是平衡树或红黑树。这篇文章我们就来详细梳理一下跳表的增加、搜索和删除步骤。 SkipList的初始化 跳表初始化时,将每一层链表的头尾节点创建出来并使用集合将头尾节点进行存储&…

嵌入式硬件篇---龙芯2k1000串口

针对串口错误 “device reports readiness to read but returned no data (Device disconnected or multiple access on port?)” 的排查和解决方法 硬件方面 检查连接 确认串口设备(如串口线、连接的模块等)与龙芯设备之间的物理连接是否牢固&#xf…

Ubuntu安装Docker命令清单(以20.04为例)

在你虚拟机上完成Ubuntu的下载后打开终端!!! Ubuntu安装Docker终极命令清单(以20.04为例) # 1. 卸载旧版本(全新系统可跳过) sudo apt-get remove docker docker-engine docker.io containerd …

数据结构:递归:自然数之和

目录 递归解法 🔹第一步:定义本质问题 🔹第二步:分解问题结构 🔹第三步:定义初始条件 🔹第四步:递归思想的自然生成 循环解法 🔹第 1 步:定义问题最小…

Pandas 技术解析:从数据结构到应用场景的深度探索

序 我最早用Python做大数据项目时,接触最早的就是Pandas了。觉得对于IT技术人员而言,它是可以属于多场景的存在,因为它的本身就是数据驱动的技术生态中,对于软件工程师而言,它是快速构建数据处理管道的基石&#xff1…

CRM管理软件的数据可视化功能使用技巧:让数据驱动决策

在当今数据驱动的商业环境中,CRM管理系统的数据可视化功能已成为企业优化客户管理、提升销售效率的核心工具。据企销客研究显示,具备优秀可视化能力的CRM系统,用户决策效率可提升47%。本文将深入解析如何通过数据可视化功能最大化CRM管理软件…

数据结构:递归的种类(Types of Recursion)

目录 尾递归(Tail Recursion) 什么是 Loop(循环)? 复杂度分析 头递归(Head Recursion) 树形递归(Tree Recursion) 线性递归(Linear Recursion)…

Redis底层数据结构之字典(Dict)

Dict基本结构 Dict我们可以想象成目录,要翻看什么内容,直接通过目录能找到页数,翻过去看。如果没有目录,我们需要一页一页往后翻,这样时间复杂度就与遍历的O(n)一样了,而用了Dict我们就可以在O(1)的时间复杂…

高通SoC阵列服务器

高通SoC阵列服务器是基于高通系统级芯片(SoC)构建的高密度计算解决方案,核心特点为低功耗、高算力集成与模块化设计,主要应用于边缘计算和云服务场景。以下是其技术特性和应用方向的综合分析: 一、核心技术特性 架构…

Linux系统下Google浏览器无法使用中文输入的临时解决方案

文章目录 前言方案描述Edge如何兼容 前言 这个AlamaLinux的ibus-libpinyin确实是让人琢磨不透,就只是无法在Chrome浏览器中、Edge浏览器中使用,而在VSCode、Xfce4-Terminal中使用良好。尝试了很多办法都没有效果,最后在Reddit里面找到了相应…

【AI论文】空间多模态大型语言模型(Spatial-MLLM):增强基于视觉的空间智能中多模态大型语言模型(MLLM)的能力

摘要:多模态大语言模型(MLLM)的最新进展显著提高了2D视觉任务的性能。 然而,提高他们的空间智能仍然是一个挑战。 现有的3D MLLM总是依赖于额外的3D或2.5D数据来加入空间感知,限制了它们在只有2D输入(如图像…

黑马Java面试笔记之 微服务篇(业务)

一. 限流 你们项目中有没有做过限流?怎么做的? 为什么要限流呢? 一是并发的确大(突发流量) 二是防止用户恶意刷接口 限流的实现方式: Tomcat:可以设置最大连接数 可以通过maxThreads设置最大Tomcat连接数,实现限流,但是适用于单体架构 Nginx:漏桶算法网关,令牌桶算法自定…

AWS App Mesh实战:构建可观测、安全的微服务通信解决方案

摘要:本文详解如何利用AWS App Mesh统一管理微服务间通信,实现精细化流量控制、端到端可观测性与安全通信,提升云原生应用稳定性。 一、什么是AWS App Mesh? AWS App Mesh 是一种服务网格(Service Mesh)解…

《云原生安全攻防》-- K8s网络策略:通过NetworkPolicy实现微隔离

默认情况下,K8s集群的网络是没有任何限制的,所有的Pod之间都可以相互访问。这就意味着,一旦攻击者入侵了某个Pod,就能够访问到集群中任意Pod,存在比较大的安全风险。 在本节课程中,我们将详细介绍如何通过N…

吃透 Golang 基础:数据结构之 Map

文章目录 Map概述初始化删除访问不存在的 key 返回 value 的零值遍历 mapmap 自身的零值map 索引时返回的第二个参数使用 map 实现 set Map Hash Map 是无序的 key/value 对集合,其中所有的 key 都是不同的。通过给定的 key 可以在常数时间复杂度内完成检索、更新或…

手机邮箱APP操作

收发电子邮件方式 邮箱可以在网络段登录,也可以在手机端登录。 大学网络服务 收发电子邮件有三种方式: 1、Web方式: 1)登录“网络服务”(https://its.pku.edu.cn),点页面顶端“邮箱”。 2&…

Spring AI之RAG入门

目录 1. 什么是RAG 2. RAG典型应用场景 3. RAG核心流程 3.1. 检索阶段 3.2. 生成阶段 4. 使用Spring AI实现RAG 4.1. 创建项目 4.2. 配置application.yml 4.3. 安装ElasticSearch和Kibana 4.3.1. 安装并启动ElasticSearch 4.3.2. 验证ElasticSearch是否启动成功 …

Spring AI Alibaba + Nacos 动态 MCP Server 代理方案

作者:刘宏宇,Spring AI Alibaba Contributor 文章概览 Spring AI Alibaba MCP 可基于 Nacos 提供的 MCP server registry 信息,建立一个中间代理层 Java 应用,将 Nacos 中注册的服务信息转换成 MCP 协议的服务器信息&#xff0c…