File: VoiceSessionDlg.cpp

package info (click to toggle)
openmohaa 0.81.1%2Bdfsg-2
  • links: PTS, VCS
  • area: contrib
  • in suites: trixie
  • size: 29,124 kB
  • sloc: ansic: 270,865; cpp: 250,173; sh: 234; asm: 141; xml: 64; makefile: 7
file content (397 lines) | stat: -rw-r--r-- 12,587 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
// VoiceSessionDlg.cpp : implementation file
//

#include "stdafx.h"
#include "Voice2BuddyMFC.h"
#include "VoiceSessionDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define THINK_TIMER_ID      100+2
#define THINK_TIMER_DELAY   20


/////////////////////////////////////////////////////////////////////////////
// CVoiceSessionDlg dialog
struct VoicePacket
{
	GVFrameStamp  mFrameStamp;
	int           mDataLength;	
	GVByte        mBuffer[1024];  // most likely get < 256 bytes
};
const int gVoicePacketHeaderSize = sizeof(GVFrameStamp)+sizeof(int);


CVoiceSessionDlg::CVoiceSessionDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CVoiceSessionDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CVoiceSessionDlg)
	m_DisplayText = _T("");
	//}}AFX_DATA_INIT
}


void CVoiceSessionDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CVoiceSessionDlg)
	DDX_Control(pDX, IDC_SPEAKING2, m_RemoteSpeaking);
	DDX_Control(pDX, IDC_SPEAKING1, m_LocalSpeaking);
	DDX_Text(pDX, IDC_DISPLAYTEXT, m_DisplayText);
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CVoiceSessionDlg, CDialog)
	//{{AFX_MSG_MAP(CVoiceSessionDlg)
	ON_WM_TIMER()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CVoiceSessionDlg message handlers
void CVoiceSessionDlg::OnTimer(UINT nIDEvent) 
{
	// Win32 timers can be called concurrently by the OS.
	//    (you can get a second timer callback before the first finishes!)
	// CRITICAL_SECTION will not prevent this as Win32 allows concurrent access
	static bool inTimer = false;
	if (inTimer)
		return;
	
	// Prevent windows from entering the timer again
	inTimer = true;

	// Process voice data
	gvThink();

	// Process NN
	NNThink();

	// Process network traffic
	if (m_Socket)
		gt2Think(m_Socket);

	// If we're not connected we don't need to do the rest
	if (!m_Connection || (gt2GetConnectionState(m_Connection) != GT2Connected))
	{
		inTimer = false;
		return;
	}

	// Show a bitmap if the sources are speaking
	static bool wasLocalSpeaking = false;
	static bool wasRemoteSpeaking = false;

	bool isLocalSpeaking  = (GVTrue == gvIsSourceTalking(m_SetupInfo.m_PlaybackDevice, 0));
	bool isRemoteSpeaking = (GVTrue == gvIsSourceTalking(m_SetupInfo.m_PlaybackDevice, m_RemoteAddr));
	
	if (isLocalSpeaking != wasLocalSpeaking)
	{
		wasLocalSpeaking = isLocalSpeaking;
		m_LocalSpeaking.ShowWindow(isLocalSpeaking ? SW_SHOW:SW_HIDE);
	}
	if (isRemoteSpeaking != wasRemoteSpeaking)
	{
		wasRemoteSpeaking = isRemoteSpeaking;
		m_RemoteSpeaking.ShowWindow(isRemoteSpeaking ? SW_SHOW:SW_HIDE);
	}

	// Check for microphone activity
	int aBytesAvailable = gvGetAvailableCaptureBytes(m_SetupInfo.m_CaptureDevice);
	if (aBytesAvailable > 0)
	{
		// Voice packet struct, so we don't have to do a buffer copy
		// We just record the data directly into the packet
		// (the actual packet may be smaller

		VoicePacket aVoicePacket;
		memset(&aVoicePacket, 0, sizeof(aVoicePacket));
		aVoicePacket.mDataLength = sizeof(aVoicePacket.mBuffer);

		GVScalar    aVolume; // this doesn't need to be sent

		// Read in a voice packet
		GVBool gotPacket = gvCapturePacket(m_SetupInfo.m_CaptureDevice, aVoicePacket.mBuffer, &aVoicePacket.mDataLength, &aVoicePacket.mFrameStamp, &aVolume);
		if (gotPacket == GVTrue)
		{
			// Playback for local echo
			//gvPlayPacket(m_SetupInfo.m_PlaybackDevice, aVoicePacket.mBuffer, aVoicePacket.mDataLength, 0, aVoicePacket.mFrameStamp);

			// Contruct a message for the buddy
			int aTotalPacketSize = gVoicePacketHeaderSize + aVoicePacket.mDataLength;
			gt2Send(m_Connection, (GT2Byte*)&aVoicePacket, aTotalPacketSize, GT2False);
		}
	}

	// Allow new timers to be triggered
	inTimer = false;
	
	CDialog::OnTimer(nIDEvent);
}


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Callbacks for GT2
void GT2SocketErrorCallback(GT2Socket theSocket)
{
	CVoiceSessionDlg* aDialog = (CVoiceSessionDlg*)gt2GetSocketData(theSocket);
	aDialog->m_DisplayText = "GT2SocketErrorCallback.  Connection Closed";
	aDialog->UpdateData(FALSE);
}

void GT2ConnectedCallback(GT2Connection theConnection, GT2Result theResult, unsigned char* theMessage, int theLength)
{
	GT2Socket aSocket = gt2GetConnectionSocket(theConnection);
	CVoiceSessionDlg* aDialog = (CVoiceSessionDlg*)gt2GetSocketData(aSocket);

	// Was there an error?
	if (theResult != GT2Success)
	{
		aDialog->MessageBox("GT2ConnectedCallback returned failure!");
		aDialog->PostMessage(WM_CLOSE, 0, 0);
	}
	else
	{
		// Remember our connection
		aDialog->m_Connection = theConnection;
		aDialog->m_DisplayText = "Session established.\r\n(You may begin speaking)";
		aDialog->UpdateData(FALSE);

		// Start the voice devices
		if (aDialog->m_SetupInfo.m_CaptureDevice != NULL)
			gvStartDevice(aDialog->m_SetupInfo.m_CaptureDevice, GV_CAPTURE);
		if (aDialog->m_SetupInfo.m_PlaybackDevice != NULL)
			gvStartDevice(aDialog->m_SetupInfo.m_PlaybackDevice, GV_PLAYBACK);
	}
	GSI_UNUSED(theMessage);
	GSI_UNUSED(theLength);
	GSI_UNUSED(theResult);
}

void GT2ReceivedCallback(GT2Connection theConnection, unsigned char* theBuffer, int theLength, int wasReliable)
{
	// Get the ptr to the dialog
	CVoiceSessionDlg* aDlg = (CVoiceSessionDlg*)gt2GetSocketData(gt2GetConnectionSocket(theConnection));

	// Cast to a voice packet so we can extract data
	VoicePacket* aVoicePacket = (VoicePacket*)theBuffer;

	// Sanity check the packet
	assert(theLength >= gVoicePacketHeaderSize);
	assert(theLength == (gVoicePacketHeaderSize + aVoicePacket->mDataLength));

	// Play the voice data
	GVSource aSource = gt2GetRemoteIP(theConnection);
	gvPlayPacket(aDlg->m_SetupInfo.m_PlaybackDevice, aVoicePacket->mBuffer, aVoicePacket->mDataLength, aSource, aVoicePacket->mFrameStamp, 0);
	OutputDebugString("Playing voice packet\r\n");
	GSI_UNUSED(wasReliable);
	GSI_UNUSED(theLength);
}

GT2Bool GT2UnrecognizedMessageCallback(GT2Socket theSocket, unsigned int theIp, unsigned short thePort, GT2Byte* theData, int theLength)
{
	static unsigned char aNNHeader[NATNEG_MAGIC_LEN] = 
				{ NN_MAGIC_0, NN_MAGIC_1, NN_MAGIC_2,
				NN_MAGIC_3, NN_MAGIC_4, NN_MAGIC_5 };

	// Bail out if it's too short
	if (theLength < NATNEG_MAGIC_LEN)
	{
		OutputDebugString("\tDiscarded message (too small)\r\n");
		return GT2False;
	}

	// Check if it matches the NN Magic bytes
	if (0==memcmp(theData, aNNHeader,NATNEG_MAGIC_LEN))
	{
		// Hand off to the NN SDK
		sockaddr_in anAddr;
		anAddr.sin_family      = AF_INET;
		anAddr.sin_port        = htons(thePort);
		anAddr.sin_addr.s_addr = theIp;
		NNProcessData((char*)theData, theLength, &anAddr);
		OutputDebugString("\tProcessed NN message\r\n");
		return GT2True;
	}

	// Not handled by us
	OutputDebugString("\tDiscarded message (not recognized)\r\n");
	GSI_UNUSED(theSocket);
	return GT2False;
}

void GT2ClosedCallback(GT2Connection theConnection, GT2CloseReason theReason)
{
	GT2Socket aSocket = gt2GetConnectionSocket(theConnection);
	CVoiceSessionDlg* aDialog = (CVoiceSessionDlg*)gt2GetSocketData(aSocket);

	aDialog->m_DisplayText = "Connection closed. (GT2ClosedCallback)";
	aDialog->UpdateData(FALSE);
	GSI_UNUSED(theReason);
}

void GT2ConnectAttemptCallback(GT2Socket theSocket,  GT2Connection theConnection,  unsigned int theIp,  unsigned short thePort,  int theLatency,  GT2Byte * theMessage,  int theLength)
{
	CVoiceSessionDlg* aDialog = (CVoiceSessionDlg*)gt2GetSocketData(theSocket);

	// Set the callbacks so we can receive data
	GT2ConnectionCallbacks aCallbackList;
	aCallbackList.received  = GT2ReceivedCallback;
	aCallbackList.connected = GT2ConnectedCallback;  // we're connected
	aCallbackList.closed    = GT2ClosedCallback;

	// Accept the connection
	aDialog->m_Connection = theConnection;
	gt2Accept(theConnection, &aCallbackList);

	// Store off the remote addr
	aDialog->m_RemoteAddr = gt2GetRemoteIP(theConnection);

	// Start the voice devices
	if (aDialog->m_SetupInfo.m_CaptureDevice != NULL)
		gvStartDevice(aDialog->m_SetupInfo.m_CaptureDevice, GV_CAPTURE);
	if (aDialog->m_SetupInfo.m_PlaybackDevice != NULL)
		gvStartDevice(aDialog->m_SetupInfo.m_PlaybackDevice, GV_PLAYBACK);

	aDialog->m_DisplayText = "Session established.\r\n(You may begin speaking)";
	aDialog->UpdateData(FALSE);
	GSI_UNUSED(theMessage);
	GSI_UNUSED(theLength);
	GSI_UNUSED(theIp);
	GSI_UNUSED(thePort);
	GSI_UNUSED(theLatency);
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Callbacks for the NN SDK
void NNProgressCallback(NegotiateState theState, void* theParam)
{
	CVoiceSessionDlg* aDialog = (CVoiceSessionDlg*)theParam;
	aDialog->m_DisplayText.Format("NNProgressCallback: %d\r\n", theState);
	aDialog->UpdateData(FALSE);
}

void NNCompletedCallback(NegotiateResult theResult, SOCKET theSocket, sockaddr_in* theAddr, void* theParam)
{
	CVoiceSessionDlg* aDialog = (CVoiceSessionDlg*)theParam;
	
	// Check the result, are we connected at the socket layer?
	if (theResult == nr_success)
	{
		// Convert sockaddr_in to a string
		CString aAddrString;
		aAddrString.Format("%s:%d", inet_ntoa(theAddr->sin_addr), ntohs(theAddr->sin_port));
		aDialog->m_DisplayText = "NN Successful";

		// If hosting, listen for the client...
		if (aDialog->m_IsHost)
		{
			gt2Listen(aDialog->m_Socket, GT2ConnectAttemptCallback);
			aDialog->m_DisplayText = "Listening for GT2 connection";
		}
		// ...otherwise connect to the host
		else
		{
			// Store off the remote addr
			aDialog->m_RemoteAddr = theAddr->sin_addr.s_addr;

			// Set our gt2 callbacks, so we can receive messages
			GT2ConnectionCallbacks aCallbackList;
			aCallbackList.connected = GT2ConnectedCallback;  // we're connected
			aCallbackList.received  = GT2ReceivedCallback;   // we've received data
			aCallbackList.closed    = GT2ClosedCallback;

			int aTimeout = 0;
			GT2Result aResult = gt2Connect(aDialog->m_Socket, &aDialog->m_Connection, aAddrString, NULL, 0, aTimeout, &aCallbackList, GT2False);
			if (aResult != GT2Success)
			{
				// Error, bail out
				aDialog->m_DisplayText = "Failed on gt2Connect";
				//aDialog->PostMessage(WM_CLOSE, 0, 0);
			}
			aDialog->m_DisplayText = "Connecting with GT2";
		}
	}
	else
	{
		aDialog->m_DisplayText = "Failed to negotiate a connection.";
	}

	// Draw the new display text
	aDialog->UpdateData(FALSE);
	GSI_UNUSED(theSocket);
}


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// MFC Event handlers
BOOL CVoiceSessionDlg::OnInitDialog() 
{
	CDialog::OnInitDialog();
	
	// Init some variables
	m_Socket     = NULL;
	m_Connection = NULL;

	// Set the gt2Callbacks
	// Create our gt2 socket (we'll need it for NN)
	//   We use NULL for the local addr because we don't care which
	//   port we're bound to.  We could specify one if we wanted by
	//   using ":5000" as the addr, for example
	GT2Result aResult = gt2CreateSocket(&m_Socket, NULL, 0, 0, GT2SocketErrorCallback);
	if (aResult != GT2Success)
	{
		// We're hosed, bail out by closing the dialog
		MessageBox("Failed on gt2CreateSocket");
		PostMessage(WM_CLOSE, 0, 0);
		return TRUE;
	}

	// Set the socket data (the user param) to our dialog
	gt2SetSocketData(m_Socket, this);
	gt2SetUnrecognizedMessageCallback(m_Socket, GT2UnrecognizedMessageCallback);

	// Begin nat negotiation (use the gt2 socket)
	//    m_NNCookie and m_IsHost are set before the dialog is run
	SOCKET aWinSocket = gt2GetSocketSOCKET(m_Socket);
	NNBeginNegotiationWithSocket(aWinSocket, m_NNCookie, m_IsHost, NNProgressCallback, NNCompletedCallback, this);

	m_DisplayText = "Establishing connection...";
	UpdateData(FALSE);

	// Set the think timer
	SetTimer(THINK_TIMER_ID, THINK_TIMER_DELAY, NULL);

	return TRUE;  // return TRUE unless you set the focus to a control
	              // EXCEPTION: OCX Property Pages should return FALSE
}

BOOL CVoiceSessionDlg::DestroyWindow() 
{
	// Clean up NN SDK
	NNFreeNegotiateList();

	// Close the gt2 socket (will close the connection if connected)
	if (m_Socket)
		gt2CloseSocket(m_Socket);

	// Stop the voice devices
	if (m_SetupInfo.m_CaptureDevice != NULL)
		gvStopDevice(m_SetupInfo.m_CaptureDevice, GV_CAPTURE);
	if (m_SetupInfo.m_PlaybackDevice != NULL)
		gvStopDevice(m_SetupInfo.m_PlaybackDevice, GV_PLAYBACK);

	// Kill the timer
	KillTimer(THINK_TIMER_ID);


	return CDialog::DestroyWindow();
}