netphonedlg.cpp

来自「《Visual C++视频技术方案宝典》配套光盘」· C++ 代码 · 共 471 行

CPP
471
字号
// NetPhoneDlg.cpp : implementation file
//

#include "stdafx.h"
#include "NetPhone.h"
#include "NetPhoneDlg.h"
#include "mmsystem.h" 

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define BUFFERSIZE 5120            //缓冲区大小
static HWAVEIN      hWaveIn ;      //录音设备句柄
static HWAVEOUT     hWaveOut ;     //播放设备句柄
static PBYTE        pBufferIn[2];  //用于接收和播放的两块缓冲区
static PBYTE        pBufferOut[2]; //用于发送和录音的两块缓冲区
static PWAVEHDR     pWHIn[2];      //用于录音的PWAVEHDR结构数组
static PWAVEHDR     pWHOut[2];     //用于播放的PWAVEHDR结构数组
static WAVEFORMATEX waveform ;     //用于打开音频设备的WAVEFORMATEX结构

int nIn       = 0;  //当前播放缓冲区号
int nOut      = 0;  //当前录音缓冲区号
int nComState = 1;  //显示通话状态信息的变量
CString   LocalIP;  //本机IP地址
CString   RemoteIP; //远端主机IP地址
CServerAS sServer;  //服务器套接字 
CClientAS sClient;  //客户端套接字
CServerAS sListen;  //监听套接字

BOOL ConnectDown   = FALSE; //“连接”按钮是否被按下
BOOL sState        = FALSE; //是否处于服务器端状态
BOOL cState        = FALSE; //是否处于客户端状态

CNetPhoneDlg* pDlg;
extern CNetPhoneApp theApp;
/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
	CAboutDlg();

// Dialog Data
	//{{AFX_DATA(CAboutDlg)
	enum { IDD = IDD_ABOUTBOX };
	//}}AFX_DATA

	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CAboutDlg)
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
	//}}AFX_VIRTUAL

// Implementation
protected:
	//{{AFX_MSG(CAboutDlg)
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
	//{{AFX_DATA_INIT(CAboutDlg)
	//}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CAboutDlg)
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
	//{{AFX_MSG_MAP(CAboutDlg)
		// No message handlers
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CNetPhoneDlg dialog

CNetPhoneDlg::CNetPhoneDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CNetPhoneDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CNetPhoneDlg)
	m_edit = _T("");
	//}}AFX_DATA_INIT
	// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
	m_frAudio = TRUE;
	
}

void CNetPhoneDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CNetPhoneDlg)
	DDX_Text(pDX, IDC_EDIT1, m_edit);
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CNetPhoneDlg, CDialog)
	//{{AFX_MSG_MAP(CNetPhoneDlg)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_BUTCONNECT, OnButconnect)
	ON_BN_CLICKED(IDC_BUTCOMMUNICATE, OnButcommunicate)
	ON_MESSAGE(MM_WOM_OPEN,OnOpen)
	ON_MESSAGE(MM_WIM_DATA,OnData)
	ON_MESSAGE(MM_WIM_CLOSE,OnClose)
	ON_BN_CLICKED(IDC_BUTEXIT, OnButexit)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CNetPhoneDlg message handlers

BOOL CNetPhoneDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// Add "About..." menu item to system menu.

	// IDM_ABOUTBOX must be in the system command range.
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		CString strAboutMenu;
		strAboutMenu.LoadString(IDS_ABOUTBOX);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon
	
	// TODO: Add extra initialization here
	pDlg=(CNetPhoneDlg *)theApp.m_pMainWnd; // 得到对话框句柄
	DWORD nSize = MAX_COMPUTERNAME_LENGTH + 1;
	char Buffer[MAX_COMPUTERNAME_LENGTH + 1];
	GetComputerName(Buffer,&nSize);
	CString str="",name;
	struct hostent * pHost;
	pHost = gethostbyname(Buffer);
    for(int i=0;i<4;i++)
	{
		CString addr;
		if(i > 0)
		{
			str += ".";
		}
		addr.Format("%u",(unsigned int)((unsigned char*)pHost->h_addr_list[0])[i]);
		str += addr;
	}
	LocalIP = str;
	//创建侦听套接字
	sListen.Create(50);
	//绑定侦听套接字到本地IP
	sListen.Bind(50,LocalIP);
	//开始侦听
	sListen.Listen();
	//创建客户端套接字
	sClient.Create(51);
	GetDlgItem(IDC_BUTCOMMUNICATE)->EnableWindow(FALSE);
	return TRUE;  // return TRUE  unless you set the focus to a control
}

void CNetPhoneDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialog::OnSysCommand(nID, lParam);
	}
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CNetPhoneDlg::OnPaint() 
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CNetPhoneDlg::OnQueryDragIcon()
{
	return (HCURSOR) m_hIcon;
}

void CNetPhoneDlg::AcceptConnect()
{
	if(!ConnectDown)
	{
		UINT Port;
		//得到呼叫端IP地址及端口
		sServer.GetPeerName(RemoteIP,Port);
		//设置各个按钮状态
		pDlg->GetDlgItem(IDC_BUTCOMMUNICATE)->EnableWindow(TRUE);
		m_edit = RemoteIP+":呼叫您";
		UpdateData(FALSE);
	}
}

BOOL CNetPhoneDlg::InitAudioDevice()
{
	//初始化waveform
	waveform.wFormatTag      = WAVE_FORMAT_PCM; //采样方式,PCM(脉冲编码调制)
    waveform.nChannels       = 2;               //双声道
    waveform.nSamplesPerSec  = 11025;           //采样率
    waveform.nAvgBytesPerSec = 11025;           //数据率
    waveform.nBlockAlign     = 2;               //最小块单元
    waveform.wBitsPerSample  = 8;               //样本大小为8bit
    waveform.cbSize          = 0;               //附加格式信息
    
	//准备pWHIn和pWHOut
	for(int i=0;i<=1;i++)
	{
        //为缓冲区分配内存
		pBufferIn[i]  = (PBYTE)malloc(BUFFERSIZE);   
        pBufferOut[i] = (PBYTE)malloc(BUFFERSIZE);
		if(!pBufferIn[i] || !pBufferOut[i])
		{
             if(pBufferIn[i])
		         free(pBufferIn[i]);
             if(pBufferOut[i])
		         free(pBufferIn[i]);
	         AfxMessageBox(_T("内存分配失败!"),MB_ICONINFORMATION|MB_OK,NULL);
        }

		pWHIn[i]  = new WAVEHDR;
        pWHOut[i] = new WAVEHDR;

		pWHOut[i]->lpData          = (char *)pBufferIn[i];
        pWHOut[i]->dwBufferLength  = BUFFERSIZE;
        pWHOut[i]->dwBytesRecorded = 0;
        pWHOut[i]->dwUser          = 0;
        pWHOut[i]->dwFlags         = WHDR_BEGINLOOP | WHDR_ENDLOOP;
        pWHOut[i]->dwLoops         = 1;
        pWHOut[i]->lpNext          = NULL;
        pWHOut[i]->reserved        = 0;

	    pWHIn[i]->lpData          = (char *)pBufferOut[i];
    	pWHIn[i]->dwBufferLength  = BUFFERSIZE;
	    pWHIn[i]->dwBytesRecorded = 0;
	    pWHIn[i]->dwUser          = 0;
	    pWHIn[i]->dwFlags         = WHDR_BEGINLOOP | WHDR_ENDLOOP;
	    pWHIn[i]->dwLoops         = 1;
	    pWHIn[i]->lpNext          = NULL;
	    pWHIn[i]->reserved        = 0;
	}
	//打开播放波形音频设备
	MMRESULT result;
	result = waveOutOpen(&hWaveOut,WAVE_MAPPER,&waveform, 
                     (DWORD)pDlg->m_hWnd,0,CALLBACK_WINDOW);
    //打开录制波形音频设备
	if(result == MMSYSERR_NOERROR)
	   result = waveInOpen(&hWaveIn,WAVE_MAPPER,&waveform, 
			          (DWORD)pDlg->m_hWnd,0,CALLBACK_WINDOW);
	//为播放和录音准备
    for(int j=0;j<=1;j++)		
	{
		if(result == MMSYSERR_NOERROR)
			result = waveOutPrepareHeader(hWaveOut,pWHOut[j],sizeof(WAVEHDR));
		if(result == MMSYSERR_NOERROR)
			result = waveInPrepareHeader(hWaveIn,pWHIn[j],sizeof(WAVEHDR));	
	} 
	//设置音量
	if(result == MMSYSERR_NOERROR)
		result = waveOutSetVolume(hWaveOut,65535);
	if(result == MMSYSERR_NOERROR)
    {      
		return TRUE;	
	}
	else
	{
		AfxMessageBox(_T("打开音频设备时发生错误!"),MB_ICONINFORMATION | MB_OK,NULL);
		return FALSE;
	}
}

void CNetPhoneDlg::RecordBegin()
{
	//准备录音缓冲区
	waveInAddBuffer(hWaveIn,pWHIn[nOut],sizeof(WAVEHDR));
	//开始录音
	waveInStart(hWaveIn); 
}

void CNetPhoneDlg::OnOpen()
{
	//接收对方发送过来的音频数据	   
	sServer.Receive((void *)pBufferIn[nIn],BUFFERSIZE);
	//播放接收缓冲区中音频数据
	waveOutWrite(hWaveOut,pWHOut[nIn],sizeof(WAVEHDR));
	nIn = 1 - nIn;
	   
	//显示通话状态
	if (nComState == 1)
	{
		SetDlgItemText(IDC_TEXT,"通话状态:");
		m_edit = "通话中";
	}
	else
	{
		SetDlgItemText(IDC_TEXT,"通话状态:");
		m_edit = "";
	}
	UpdateData(FALSE);
	nComState++;
	if(nComState == 10)
		nComState = 1;
}

void CNetPhoneDlg::OnData()
{
	//发送缓冲区中录制的音频数据
	sClient.Send(pBufferOut[nOut],BUFFERSIZE); 
	//缓冲区循环
	nOut = 1 - nOut;	
	RecordBegin(); //开始下一轮录音
}

void CNetPhoneDlg::OnClose()
{
	waveInUnprepareHeader(hWaveIn,pWHIn[0],sizeof(WAVEHDR));
	waveInUnprepareHeader(hWaveIn,pWHIn[1],sizeof(WAVEHDR));
	waveInClose (hWaveIn); 	 
	hWaveIn=NULL;
}

void CNetPhoneDlg::OnButconnect() 
{
	// TODO: Add your control notification handler code here
	UpdateData(TRUE);
	//连接按钮被按下
	ConnectDown = TRUE;
	cState = TRUE;
	//连接被呼叫主机,端口50
	sClient.Connect(m_edit,50);
	GetDlgItem(IDC_BUTCONNECT)->EnableWindow(FALSE);
}

void CNetPhoneDlg::OnReceive()
{
	//判断电话是否被接听
	if(ConnectDown == TRUE && cState == TRUE && sState == FALSE)
    {    
		//接收信息
		int i;
		i = sServer.Receive(cAck,5);
		cAck[i] = '\0';
		sAck.Format("%s",cAck);
	    if(sAck == "AGREE")
		{	
			sState = TRUE;
			m_edit = "对方已接听电话";
		    ::SendMessage(pDlg->m_hWnd,WM_COMMAND,IDC_BUTCOMMUNICATE,0);			
		}
		else
		{
			sState = FALSE;
		}
		UpdateData(FALSE);
	}
    //如果用户同时处于通话状态,播放接收到的音频数据
	if(cState == TRUE && sState == TRUE)
	{   
		OnOpen();
	}
}

void CNetPhoneDlg::OnButcommunicate() 
{
	// TODO: Add your control notification handler code here
	GetDlgItem(IDC_BUTCOMMUNICATE)->EnableWindow(FALSE);
	GetDlgItem(IDC_BUTCONNECT)->EnableWindow(FALSE);
	//程序运行后,只初始化一次音频设备
	if(m_frAudio==TRUE)
	{
		if(InitAudioDevice())
			m_frAudio=FALSE;
		else
		{
			AfxMessageBox(_T("初始化音频设备失败!"),MB_ICONINFORMATION | MB_OK,NULL);
			return;
		}
	}
	//“通话”按钮按下之前,程序不处于客户端状态
	if(cState==FALSE)
	{   
		//连接呼叫主机,端口50
		sClient.Connect(RemoteIP,50);
		Sleep(100);
		//发送“AGREE”给呼叫主机,表示同意通话
		sClient.Send("AGREE",5);
		m_edit = RemoteIP;
		UpdateData(FALSE);
		cState = TRUE;
		sState = TRUE;
		//开始录音
		RecordBegin();
	}
	else
	{   
		RecordBegin();
	}
}

void CNetPhoneDlg::OnButexit() 
{
	// TODO: Add your control notification handler code here
	//关闭套接字
	sServer.Close();
	sListen.Close();
	sClient.Close(); 
	//释放缓冲区内存
	for(int i=0;i<=1;i++)
	{
		delete pWHIn[i];
		delete pWHOut[i];
		free(pBufferIn[i]);
		free(pBufferOut[i]);
		waveInUnprepareHeader(hWaveIn,pWHIn[i],sizeof(WAVEHDR));
		waveOutUnprepareHeader(hWaveOut,pWHOut[i],sizeof(WAVEHDR));
	}
	//关闭音频设备
	waveInClose(hWaveIn);
	waveOutClose(hWaveOut);
	CDialog::OnCancel();	
}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?