你的位置:首页 > ASP.net教程

[ASP.net教程]如何给Skype通话录音录像


Skype是免费的语音通话软件,不但可以点对点用电脑进行免费的语音通话,而且只需花费低廉的费用就可以直接呼叫固定电话或手机,Skype以优秀的通话质量而赢得了全世界不少用户的亲睐,我就是Skype的忠实用户,下图就是我的Skype截图:

我常常使用Skype和台湾同胞还有国外的朋友进行联系,有时因为业务需要需要将语音通话录音并保留下来,在我有这个想法的那个时候(2006年)Skype官方并没有提供录音功能,咱们是做程序的嘛,没有的功能可以自己来添加啊,这也是为什么我酷爱编程的原因。

应广大网友的要求,现将该程序的编程思路和源代码贡献出来与大家共勉,希望能给对音频编程有兴趣的朋友提供一点点帮助,那我就心满意足了。

刚开始编写这个程序的时候,我试着用常规的录音方式对声卡进行录音,既然是通话录音,我们希望能将自己的声音和对方的声音同时纪录下来。首先,我们要将对方的声音录下来,那就只能选取“立体声混音”通道进行录音,但此时“麦克风”通道的声音将被丢弃,也就是说在Skype里对方将听不到我说话了;其次,如果我们还要将我自己的声音录下来,就得开启“麦克风”通道录音,但是在Skype通话过程中,“麦克风”通道已经被Skype占用了,我们的程序无法再次进行录音,看来常规的录音方式行不通。

于是,我想到了Windows音频的底层处理机制,任何语音软件的音频数据处理到最后都离不开 Windows 的底层音频 Win32 API 函数,查一下MSDN 库就能得知,这些函数都在 MultiMed.chm 帮助文件中:

Waveform FunctionsThe following functions are used with waveform audio.auxGetDevCapsauxGetNumDevsauxGetVolumeauxOutMessageauxSetVolumePlaySoundsndPlaySoundwaveInAddBufferwaveInClosewaveInGetDevCapswaveInGetErrorTextwaveInGetIDwaveInGetNumDevswaveInGetPositionwaveInMessagewaveInOpenwaveInPrepareHeaderwaveInProcwaveInResetwaveInStartwaveInStopwaveInUnprepareHeaderwaveOutBreakLoopwaveOutClosewaveOutGetDevCapswaveOutGetErrorTextwaveOutGetIDwaveOutGetNumDevswaveOutGetPitchwaveOutGetPlaybackRatewaveOutGetPositionwaveOutGetVolumewaveOutMessagewaveOutOpenwaveOutPausewaveOutPrepareHeaderwaveOutProcwaveOutResetwaveOutRestartwaveOutSetPitchwaveOutSetPlaybackRatewaveOutSetVolumewaveOutUnprepareHeaderwaveOutWrite

 

有了这些函数,我就想到了一个办法,那就是用系统钩子改变这些函数的原地址,在Skype调用这些Win32 API函数之前先进入我的程序,我将Skype的音频数据“偷偷地”拷贝一份传递给我自己的应用程序,再还给Skype,这样就可以神不知鬼不觉地将通话中的语音数据取出来,再加上自己的mp3压缩保存到硬盘文件即可。

以上便是整个Skype录音的全部思路,现在开始介绍代码。

由于我们的程序需要嵌入到Skype程序中,所以我们只能使用dll的形式来编写这个程序,我现在需要写一个修改Win32 API函数地址的类,在这里我直接引用了《Windows 核心编程》随书代码中的 CAPIHook 类,我提供的源代码里就有这个类,这个类可以修改Win32 API函数的地址,当我们修改好API函数地址以后,Skype调用前面所说的6个函数时系统会自动调用我们的函数,请看代码:

//// 定义函数变量//typedef MMRESULT (WINAPI *PFN_waveInOpen) ( LPHWAVEIN phwi,           	          UINT uDeviceID, 					LPWAVEFORMATEX pwfx, 					DWORD dwCallback, 					DWORD dwCallbackInstance, 					DWORD fdwOpen );typedef MMRESULT (WINAPI *PFN_waveInClose) ( HWAVEIN hwi );typedef MMRESULT (WINAPI *PFN_waveOutOpen) ( LPHWAVEOUT phwo, 					UINT uDeviceID, 					LPWAVEFORMATEX pwfx, 					DWORD dwCallback, 					DWORD dwCallbackInstance, 					DWORD fdwOpen );typedef MMRESULT (WINAPI *PFN_waveOutClose) ( HWAVEOUT hwo );typedef MMRESULT (WINAPI *PFN_waveInPrepareHeader) ( HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh );typedef MMRESULT (WINAPI *PFN_waveOutWrite) ( HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh );//// 修改Win32 API函数地址//CAPIHook g_waveInOpen("winmm.dll", "waveInOpen", (PROC) Hook_waveInOpen, TRUE);CAPIHook g_waveInClose("winmm.dll", "waveInClose", (PROC) Hook_waveInClose, TRUE);CAPIHook g_waveOutOpen("winmm.dll", "waveOutOpen", (PROC) Hook_waveOutOpen, TRUE);CAPIHook g_waveOutClose("winmm.dll", "waveOutClose", (PROC) Hook_waveOutClose, TRUE);CAPIHook g_waveInPrepareHeader("winmm.dll", "waveInPrepareHeader", (PROC) Hook_waveInPrepareHeader, TRUE);CAPIHook g_waveOutWrite("winmm.dll", "waveOutWrite", (PROC) Hook_waveOutWrite, TRUE);

 

至此,Skype通话过程中音频输入和输出的数据(即对方讲话和我自己讲话的声音)已经全部“偷取”到了,接下来只要压缩成mp3格式即可,mp3压缩代码网上很多,随便下载一个来用就可以了,我用的是“hw_mp3_enc.dll library”,效果一般,但用做电话录音足亦。

一个有趣的功能:我们录音后的mp3文件播放时,我可以让对方的声音在左声道,我自己的声音在右声道,好像两个人面对面在对话一样。其实做起来并不难,从上面的代码我们知道,其实输入和输出的音频数据是独立获取的,我们在合并到mp3文件时,将输入的数据存为左声道,输出的数据存为右声道即可。

既然叫“Skype答录机”,除了有录音功能外,还应该有自动应答功能,要实现这个功能有两个办法:

a) 当来电震铃超过规定的次数时自动提机,将录音通道切换到“立体声混音”,然后播放之前准备好的一个语音文件(如:您好,我现在不在电脑旁,有事请留言),本软件使用的就是这种方式;

b) 当来电震铃超过规定的次数时自动提机,然后播放之前准备好的一个语音文件(如:您好,我现在不在电脑旁,有事请留言)数据直接传递至上面的.dll文件相关函数中,然后 waveInPrepareHeader 函数中将系统从麦克风中录制的声音替换掉,这种方式比较难控制,但可以发现很多奇怪的效果,比如通话变声等。

需要注意的地方:该程序是通过钩子方式截取Skype的音频数据,所以程序的执行效率要求很高,对于慢速处理的操作(如:压缩mp3数据、数据存盘等)最好是放到其他线程中处理,否则会影响Skype通话质量,造成通话断断续续的感觉,录音数据也可能会丢失。