本文已参与「新人创作礼」活动,一起开启掘金创作之路。

前几天看书了解了语音通话的原理,就很想自己尝试一下,然后就——做出来了,嘻嘻,先看看效果吧!

socket实现局域网语音通话 c++ winapi
socket实现局域网语音通话 c++ winapi

因为通信这边没字符是什么办法上传视频,所以只能录制一个gif大家看一下效果,但是初始化电脑是可以听到声音的。

源码的appear话我已经上传到csdn了,这个是下载链接:download.csdn.nappearet/download/we…

功能介绍: 1.支持录音设备查找以及播放设备查找 2.支持局域网语音通话 3.通话包含语音来电提醒和挂断电话的提示信息,还能实时的获取在线用户的数量以及对应的id,其他功能正在开发,期待大家一起进步。

Summary: 1.socket通信 2.wavein和waveout系列函数的使用 3.各种通信场景的添加以及windows7旗舰版数据包的APP设计

一、socket通信

唔,感觉全部初始化磁盘放在这里面感觉会很长,以后写一篇其他的文章详细介绍这个内APP容吧。

二、waveIn和WaveOut的Win32API

1.音频设备的的信息获取

首先是输入音频设备个数的获取,仅仅通过调用下面的函数就可以了,没有输入参数,输出设备个数获取的函数调用方式一样,名称略有不同。 waveInGetNumDevs(); //获取输入音频设备个数 waveOutGetNumDevs(); //获取输出音频设备个数 然后便是获取具体的设备信息了,具体调用如下面所示:

	//音频输入设备信息的获取
	tstring sDevText = L"";		//这里用的是unicode的宽字符串的tstring对象
	WAVEINCAPS waveCaps;	//用于获取设备信息的结构体
	int res = waveInGetDevCaps(dwID, &waveCaps, sizeof(WAVEINCAPS));
	if (res == MMSYSERR_NOERROR)
	{
		sDevText = waveCaps.szPname;	//此处保存的是设备名称
	}
	//音频输出设备信息的获取,方式和上面类似,只不过函数名称略有不同
	tstring sDevText = L"";
	WAVEOUTCAPS waveCaps;
	int res = waveOutGetDevCaps(dwID, &waveCaps, sizeof(WAVEOUTCAPS));
	if (res == MMSYSERR_NOERROR)
	{
		sDevText = waveCaps.szPname;
	}

2.音频设备的初始化

首先是打开音频设备: waveInOpen函数参数说明字符型变量m_hWaveIn 表示的是音频输入的设备句柄,参数为该句柄的地址appetite iWaveInD通信evID 表示的是音频输入设备的ID,ID默认是从初始化sdk什么意思0开始的,如果是在不知道音频IappreciateD的话,可以将该参数设置为 WAVE_MAPPER ,即默认选择。 m_soundFormat 表示的是打开设备的格式,这个就字符是什么略微复杂一点了,常用的参数有几个吧: m_soundF初始化是什么意思ormat.wFormatTag = WAVE_FORMAT_PCM; //这个是采样数据的格式,其他的咱也不懂,就用默认的 PCM 脉冲采样的格式。 m_soundFormat.nChannels = 1; //通道数 m_soundFormat.nSamplesPerSec = 11025; //采样率,常用的有11.application025 kHz、22.05 kHz和44.1 kHz,其他的不建议设一些不规则的数。 m_soundFormat初始化是什么意思.nAvgBytesPerSec = 11025; //不懂,和字符是什么采样率一般设置为一样的数 m_soundFormat.wBitsPerSample = 8; //表示的是数据位数,8或者16位 m_soundFormat.cbSize = 0; //一般为0 hWnd 这个是用于接收录音通知消息的句柄,填windows11有必要升级吗做主窗口句柄就行,注意一个类型转换。 0L 它的名字是 dwInstance,不太懂,没什么关系其实 CA字符串是什么意思LLBACK_WINDOW 表示的是 dwCallback(也就是hWnd) 是个窗口句柄,指定的是 dwCallback(也初始化游戏启动器失败就是hWnd) 参数是什么东西。给大家看一下原版的英文解释,这个有好多种内容,不想看的可以跳过了,看起来挺晦涩的:

fdwOpen: Flags for opening the device. The following values are defined: CALLBACK_初始化EVENT The dwCallback parameter is an event handle. CALLBACKwindows是什么意思_FUNCTION The dwCalapplelback parameter is a calappstorelback procedure address. CALLBACK_NULL No callback mechanism. This is the default setting. CALLBACK_THREAD The dwCallback parameter is a thread identifier. CALLBACK_WINDOW The dwCallback parwindows许可证即将过期怎么办ameter is a window handle. WAVE_FORMAT_DIRECT If this flag is specified, the ACM driver does not perform conversions on the audio data. WAVE字符间距怎么加宽_FORMAT_Qwindows10激活密钥UERY The function queries the device to determine w通信行程卡hether it supports the given format, but it does not open the device. WAV通信大数据行程卡E_MAPPED The uDeviceID parameter specifies a waveform-audio de通信地址是写什么地址vice to be mapped to by the wave mapper.

返回值为 MMSYSERR_NOERROR 表示失败了,然后这里对返回值做一个判通信人家园断。

	HWAVEIN m_hWaveIn;							//音频输入的句柄
	//打开录音设备,采用窗口方式接收音频消息
	int res = waveInOpen(&m_hWaveIn, iWaveInDevID, &m_soundFormat, (DWORD)hWnd, 0L, CALLBACK_WINDOW);
	if (res != MMSYSERR_NOERROR)
		return false;

输出设备的打开啊方式类似,windows10激活密钥此处也不做过多解释了,大家看一下通信地址是写什么地址就差不多能懂了,相信能认认真真看字符间距在哪里设置这篇博客的应该都是很棒的人。 (注意:此处的通信工程 m_hWaveOut 类型是 HWAVEOUT,和上面的那个不一样,注意区分哦)

	//======================== 播放 ==========================
	res = waveOutOpen(&m_hWaveOut, iWaveOutDevID, &m_soundFormat, (DWORD)hWnd,
		0L, CALLBACK_WINDOW);
	if (res != MMSYSERR_NOERROR)
		return false;

3.输入输出设备缓冲区的准备和添加

老样子了,先从音频输入设备讲起: MAX_BUFFER_SIZE 是自己设置的一个宏定义,给大家一个大概的数量大小参考吧,10240 或者 20480 都可以的,字符串逆序输出这个其实是一个平衡,如果缓冲区过大,那么通话延迟比较高,如果比较少,则字符通话的连续性质量不高,自己看着试试就行 m_pWaveHdrIn.dwBytesRecorded 表示的是在准备这个缓冲区的时候,里面的初始数据占多少字节,填个 0 就行。 m_pWav初始化电脑时出现问题未进行更改eHdrIn.dwFlags 参数有好多内容,有兴趣的可以看看下面的参考内容:

方法

提供缓冲区信息的标志。定义了以下值: WHDR_BEGINLOOP 这个缓冲区是循环中的第一个缓冲区。该标志仅用于输出缓冲区。 WHDR_DONE 由设备驱动程序字符间距在哪里设置设置,表示缓冲区已用完,正在将其返回给应用程序。 WHDR_ENDLOOP 这个缓冲区是循环中的最后一个缓冲区。该标志仅用于输出缓冲区。 WHDR_INQ通信技术UEUE 由窗口设置,字符常量表示缓冲区已排队等待回放。 WHDR_PREPARED 由窗口设置,表示缓冲区已用波形预appearance预热器或波形输出预预热器功能准备好。

waveInPrepapproachareHeader 函数主要是准备音频输入设备的缓冲区(其实翻译一下看名字大概就能猜出来),大概参数介绍: m_hWaveIn 音频输入设备的句柄 m_pWaveHdrIn 缓冲区的地址 sizeof(WAVE字符串逆序输出HDR) 这个参数么,不用多说了哈哈

下一个函数 waveInAddBuffer 也字符间距加宽2磅怎么设置简单,不多说初始化电脑时出现问题未进行更改了,相信大家的实力。

	char m_cBufferIn[MAX_BUFFER_SIZE];	//这个是实际的缓冲区空间
	WAVEHDR m_pWaveHdrIn;				//这是一个结构体,用于函数调用参数的一个内容
	//准备内存块录音
	m_pWaveHdrIn.lpData = m_cBufferIn;
	m_pWaveHdrIn.dwBufferLength = MAX_BUFFER_SIZE; 
	m_pWaveHdrIn.dwBytesRecorded = 0;
	m_pWaveHdrIn.dwFlags = 0;
	res = waveInPrepareHeader(m_hWaveIn, &m_pWaveHdrIn, sizeof(WAVEHDR));
	if (res != MMSYSERR_NOERROR)
		return false;
	//增加内存块
	res = waveInAddBuffer(m_hWaveIn, &m_pWaveHdrIn, sizeof(WAVEHDR));
	if (res != MMSYSERR_NOERROR)
		return false;

音频输出设备的缓冲区准备和添加类似,参考下通信工程专业面代码:

	//准备内存块播放
	m_pWaveHdrout.lpData = m_cBufferout;
	m_pWaveHdrout.dwBufferLength = MAX_BUFFER_SIZE;
	m_pWaveHdrout.dwBytesRecorded = 0;
	m_pWaveHdrout.dwFlags = 0;
	res = waveOutPrepareHeader(m_hWaveOut, &m_pWaveHdrout, sizeof(WAVEHDR));
	if (res != MMSYSERR_NOERROR)
		return false;
	//指定数据块到音频播放缓冲区
	res = waveOutWrite(m_hWaveOut, &m_pWaveHdrout, sizeof(WAVEHDR));
	if (res != MMSYSERR_NOERROR)
		return false;

4.播放和录音的开始和终止

先写这几个比较简单的操作: 开始录音:wavwindows7怎么重装系统eInStart(m_hWaveIn); 停止录音:waveInStop(m_hWaveIn); 停止播放:waveOutReset(m_hWaveOut); 然后播放录音的操作略微复杂一点,需要把数据放到播放缓冲区,缓冲区的内容会自动播放appearancem_cBufferout 播放缓冲区的首地址 pData 要播放的声音数据流首地址 dwDataLenappstore 声音数据流的长度

memcpy(m_cBufferout, pData, dwDataLen); (函数不唯一啊,这个windows有好多,例如CopyMemory也可以实现这个功能,这里用的是 memcpy 函数)

5.录音通知消息的获取和处理

开始录音后,缓冲区不断地增加捕获到的音频数据,当音频数据接受满了之后,就会向前文说的那个approve窗口句柄的窗口发送通知消息 MM_WIM_DATA字符串逆序输出收到这个消息之后程序就要对这些数据进行处理,处理完毕后appointment最最重要一件事是清空缓冲区,windows并不会自己清理缓windows更新有必要吗冲区内容。 清理缓冲区用到的函数是 waveInUnprepareHeader 这个参数其实差windows系统不多,

waveInPrepareHea通信der函数清理由WaveInPreparappearanceeHead通信工程专业eappreciater函数执行的准备。该函数必须在设备驱动程序填充缓冲区并将其返回给应用字符间距加宽2磅程序后调用。在释放缓冲区之前,您必须调用此函数。

这个是官windows10方的解释,感觉这个挺详细的,apple放在这里大家看看。清空缓冲区之后,重新准备缓冲区,和上面的操appetite作一样。

	int res = waveInUnprepareHeader(m_hWaveIn, &m_pWaveHdrIn, sizeof(WAVEHDR));
	if (res != MMSYSERR_NOERROR)
		return false;
	//准备内存块录音
	m_pWaveHdrIn.lpData = m_cBufferIn;
	m_pWaveHdrIn.dwBufferLength = MAX_BUFFER_SIZE;
	m_pWaveHdrIn.dwFlags = 0;
	res = waveInPrepareHeader(m_hWaveIn, &m_pWaveHdrIn, sizeof(WAVEHDR));
	if (res != MMSYSERR_NOERROR)
		return false;
	//增加内存块
	res = waveInAddBuffer(m_hWaveIn, &m_pWaveHdrIn, sizeof(WAVEHDR));
	if (res != MMSYSERR_NOERROR)
		return false;

清空输出缓冲区的函数(程序结束,记得清空缓冲初始化sdk什么意思区内容):

	int res = waveOutUnprepareHeader(m_hWaveOut, &m_pWaveHdrout[0], sizeof(WAVEHDR));

6.关闭音频输入和输出设备

调用两个特别简单的函数实现最终的收尾工作,哦耶!

	if (m_hWaveIn)
	{
		waveInClose(m_hWaveIn);
		m_hWaveIn = NULL;
	}
	if (m_hWaveOut)
	{
		waveOutClose(m_hWaveOut);
		m_hWaveOut = NULL;
	}

三、通信数据包的设计以及客户端服务器逻辑

这个怎么说呢,感觉就要从实际出发了,这里简单的说一下思路吧。 功能分析: 1.客户端登陆ID分配以及其他客户端的广播 可以APP用静态变量++来为客户端赋值ID,以此保证每个用户ID不重复,然后广播就遍历所有的客户端。包括登陆包,反馈包,广播包。 2.windows怎么激活拨打电话提示 这个就是拨打电话请求包和拨打电话的回复包两个是吧。 3.声音数据的传输 必须指定谁的语音信息发到哪个客户端,所以语音包必须包含windows是什么意思发送用户的ID和接收用户的ID。 4.挂断通知 需要挂断包对吧。用户挂断情况可能通信技术是主动挂断,或者是程序异常通信地址关闭,所以挂断包可以添加一点挂断信息等等。

其他的自己看着添加就行了哈哈哈哈,谢谢大家的阅读!