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();
}
|