TeamTalk 4 .NET DLL  Version 4.5A
Client Programming Guide

The following sections gives a step-by-step tour of how to build an application which uses the TeamTalk 4 .NET DLL to transmit audio and video. This chapter assumes the developer has read the section Client Setup Guide on how to configure Visual Studio to work with the TeamTalk client DLL.

In order for the user application to interact with other clients a TeamTalk server must first be set up. Section Server Setup Guide explains how to do this. The job of the TeamTalk server is to provide user autentication and keep track of users. Once a client is connected and has been authenticated the client is presented with a tree structure where each node is a so-called "channel" which the client can join and from there interact with the other users who are in the same channel.

The following six steps explains how to build a TeamTalk client:

Step 1: Create a TeamTalk Client Instance

A new client instance is created by instantiating the BearWare.TeamTalk4-class. Once the new instance has been created its current state can be retrieved by calling the function TeamTalk4.GetFlags() which will return a bitmask describing the client's current state. Initially after creating the client instance a call to TeamTalk4.GetFlags() will return a bitmask with the value ClientFlag CLIENT_CLOSED since no operations have been performed on the client.

Here a code-snip which shows how to instantiate the BearWare.TeamTalk4 class.

//...
public partial class Form1 : Form
{
BearWare.TeamTalk4 ttclient;
public Form1()
{
InitializeComponent();
//we pass 'false' to the constructor since we're a Forms application
ttclient = new BearWare.TeamTalk4(false);
Debug.Assert(ttclient.Flags == ClientFlag.CLIENT_CLOSED);
}
}
//...

Step 2: Initialize Sound and Video Devices

Before connecting to a server it is a good idea to setup the user's sound and video devices. Sound devices are initialized using the TeamTalk4.InitSoundInputDevice() and TeamTalk4.InitSoundOutputDevice() functions. The video capture device is initialized using TeamTalk4.InitVideoCaptureDevice(). Initializing the video capture device can be quite tricky because there's many properties which needs to be configured. Look at the SDK sample applications to see how it is done.

Once sound and video devices has been initialized the function TeamTalk4.GetFlags() will return a mask containing ClientFlag CLIENT_SNDINPUT_READY, ClientFlag CLIENT_SNDOUTPUT_READY and ClientFlag CLIENT_VIDEO_READY.

Here a code-snip which shows how to extract extract sound and video devices:

//...
SoundDevice[] sndinput, sndoutput;
VideoCaptureDevice[] videodevs;
/* extract sound and video devices and put them in UI */
public void button3ListDevices()
{
/* get sound recording devices */
ttclient.GetSoundInputDevices(out sndinput); //we should check return value...
/* get sound playback devices */
ttclient.GetSoundOutputDevices(out sndoutput); //we should check return value...
foreach(SoundDevice dev in sndinput) {
/* add sound input to display container, say 'recComboBox'... */
}
foreach(SoundDevice dev in sndoutput) {
/* add sound output to display container, let's say 'playComboBox'... */
}
/* get video capture devices. Here we assume there is ONE */
ttclient.GetVideoCaptureDevices(out videodevs); //we should check return value...
Debug.Assert(videodevs.Length == 1);
/* add video capture device to display container, let's say 'vidComboBox'... */
/* here we list the capture formats supported */
foreach(CaptureFormat cap in videodevs[0].captureFormats) {
/* add capture format to display container, let's say 'fmtComboBox' */
}
}
/* when user has chosen sound and video devices they can now be initialized */
public void button4InitDevices()
{
/* init sound recording device */
ttclient.InitSoundInputDevice(sndinput[recComboBox1.SelectedIndex].nDeviceID); //we should check return value...
Debug.Assert( (ttclient.Flags & ClientFlag.CLIENT_SNDINPUT_READY) ==
ClientFlag.CLIENT_SNDINPUT_READY );
/* init sound playback device */
ttclient.InitSoundOutputDevice(sndoutput[playComboBox.SelectedIndex].nDeviceID); //we should check return value...
Debug.Assert( (ttclient.Flags & ClientFlag.CLIENT_SNDOUTPUT_READY) ==
ClientFlag.CLIENT_SNDOUTPUT_READY );
/* Let's choose some Theora video codec settings for use with video capture */
VideoCodec vidcodec;
vidcodec.nCodec = Codec ::THEORA_CODEC;
vidcodec.theora.nQuality = 10;
vidcodec.theora.nTargetBitRate = 16384; //16 KBits
/* init video capture device and capture format. */
ttclient.InitVideoCaptureDevice(videodevs[0].szDeviceName, //we pretended to have ONE
videodevs[0].captureFormats[fmtComboBox.SelectedIndex],
vidcodec); //we should check return value...
Debug.Assert( (ttclient.Flags & ClientFlag.CLIENT_VIDEO_READY) ==
(ClientFlag.CLIENT_VIDEO_READY) );
}
//...

Step 3: Connect to a TeamTalk Server

By calling TeamTalk4.Connect() will make the client connect to the server running on the IP-address and port numbers specified as parameters to the function. After this call the TeamTalk4.GetFlags() function will have the ClientFlag CLIENT_CONNECTING bit set.

If the TeamTalk4.Connect() call fails the TeamTalk4.OnConnectFailed() event will be posted by the client instance and the ClientFlag CLIENT_CONNECTING bit will be cleared.

If the TeamTalk4.Connect() is successful the TeamTalk4.OnConnectSuccess() event will be posted by the client instance and the ClientFlag CLIENT_CONNECTED bit will be set and ClientFlag CLIENT_CONNECTING will be cleared.

Here is a code-snip which shows how to connect to a server:

/* in the constructor we should add event handlers to these two
events for connecting */
public Form1()
{
//..
ttclient.OnConnectSuccess += new TeamTalk4.Connection(form1_OnConnectSuccess);
ttclient.OnConnectFailed += new TeamTalk4.Connection(form1_OnConnectFailed);
//..
}
/* let's say the user presses 'button1' to connect to a server and invokes this */
public void button1ConnectToServer()
{
Debug.Assert( (ttclient.Flags &
(ClientFlag.CLIENT_CONNECTING | ClientFlag.CLIENT_CONNECTED) ) ==
ClientFlag.CLIENT_CLOSED /* basically 0 */ );
if( ttclient.Connect("10.10.66.2", 10333, 10333, 0, 0) == false)
MessageBox.Show("Unable to connect!");
else {
//we wait for 'form1_OnConnectSuccess' or 'form1_OnConnectFailed'
//to be called...
}
}
/* invoked by the client instance if connection is successful */
public void form1_OnConnectSuccess()
{
MessageBox.Show("We're connected!");
Debug.Assert( (ttclient.Flags & ClientFlag.CLIENT_CONNECTED) ==
ClientFlag.CLIENT_CONNECTED);
}
/* invoked by the client instance if the connection is unsuccessful */
public void form1_OnConnectFailed()
{
MessageBox.Show("Unable to connect!");
ttclient.Disconnect(); //reset the client, so we can call .Connect() again
}

Step 4: Log on to a TeamTalk Server

Once connected the user application can call TeamTalk4.DoLogin() to log on to the server. All functions with the prefix Do* are client to server commands (see Client/Server Commands). The TeamTalk4.DoLogin() requires that the user application provides a server password, account username and account password. A server may allow anonymous users (see UserRight USERRIGHT_GUEST_LOGIN) to log on in which case the account username and password can be left blank.

If the server rejects the login the TeamTalk4.OnCmdError() event is posted along with an error code (see ClientError).

If the server accepts the login information the TeamTalk4.OnCmdMyselfLoggedIn() event is posted and the the client instance will have the ClientFlag CLIENT_AUTHORIZED set.

For every channel on the server a TeamTalk4.OnCmdChannelNew() event will be posted and a TeamTalk4.OnCmdUserLoggedIn() will be posted for every user on the server. The TeamTalk4.OnCmdUserJoinedChannel() will also be posted for every user who is in a channel.

Here's a code-snip which shows how to log on to a server:

/* in the constructor we should add an event handler for login success
and login failure */
public Form1()
{
//..
ttclient.OnCmdError += new TeamTalk4.CommandError(form1_OnCmdError);
ttclient.OnCmdMyselfLoggedIn +=
new TeamTalk4.MyselfLoggedIn(form1_OnCmdMyselfLoggedIn);
//..
}
void button2Login()
{
Debug.Assert( (ttclient.Flags & ClientFlag.CLIENT_CONNECTED) ==
ClientFlag.CLIENT_CONNECTED);
if(ttclient.DoLogin("John Doe", "srv_passwd123", "admin", "admin_passwd321")<0)
MessageBox.Show("Failed to issue client/server command");
else {
//wait for 'ttclient_OnCmdMyselfLoggedIn' or
//'ttclient_OnCmdError' to be called
}
}
/* invoked by client instance if login comamnd is ok */
void ttclient_OnCmdMyselfLoggedIn(int nMyUserID)
{
MessageBox.Show("Log in successful");
Debug.Assert((ttclient.Flags & ClientFlag.CLIENT_AUTHORIZED) ==
ClientFlag.CLIENT_AUTHORIZED);
Debug.Assert(ttclient.UserID>0); //we have a user ID now
}
/* invoked by client instance if login command fails */
void ttclient_OnCmdError(ClientError nErrorNo, int nCmdID)
{
switch (nErrorNo)
{
case ClientError.CMDERR_INCORRECT_SERVER_PASSWORD :
MessageBox.Show("Invalid server password"); break;
case ClientError.CMDERR_INVALID_ACCOUNT :
MessageBox.Show("Invalid user account"); break;
//...
}
}

Step 5: Join a channel on the TeamTalk Server

Now that the client is connected and authorized it is possible to join a channel on the server. This is done by either calling the function TeamTalk4.DoJoinChannel() to create a new channel or by calling TeamTalk4.DoJoinChannelByID() to join an existing channel.

If using the TeamTalk4.DoJoinChannelByID() command to join a channel the ID of the channel must be retrieved and also the password needed to join the channel. The ID of a channel is posted in the TeamTalk4.OnCmdChannelNew() event and the password must be known by the user.

If the call to TeamTalk4.DoJoinChannelByID() is successful the event TeamTalk4.OnCmdMyselfJoinedChannel() is posted and if the server rejected the command to join the TeamTalk4.OnCmdError() event is posted.

Here is a code-snip for joining a channel:

/* in the constructor we should add an event handler for
joining a channel and join failure */
public Form1()
{
//..
ttclient.OnCmdError += new TeamTalk4.CommandError(form1_OnCmdError);
ttclient.OnCmdMyselfJoinedChannel +=
new TeamTalk4.MyselfJoinedChannel(form1_OnCmdMyselfJoinedChannel);
//..
}
/* button invoked by user to join a channel... */
public void button7JoinChannel()
{
if(ttclient.DoJoinChannelByID((int)treeView.SelectedItem.Tag,
"chan_passwd123")<0)
MessageBox.Show("Failed to issue commnand to join channel");
else {
//wait for either 'form1_OnCmdMyselfJoinedChannel' or
//'ttclient_OnCmdError' to be invoked
}
}
/* invoked by client instance if join command succeeds */
void form1_OnCmdMyselfJoinedChannel(int nChannelID)
{
string chanpath;
if(ttclient.GetChannelPath(nChannelID, out chanpath))
{
MessageBox.Show("Joined channel: " + chanpath);
Debug.Assert(ttclient.ChannelID>0); //we're in a channel now...
}
}
/* invoked by client instance if join command fails */
void ttclient_OnCmdError(ClientError nErrorNo, int nCmdID)
{
switch (nErrorNo)
{
case ClientError.CMDERR_INCORRECT_CHANNEL_PASSWORD :
MessageBox.Show("Invalid channel password"); break;
case ClientError.CMDERR_CHANNEL_NOT_FOUND :
MessageBox.Show("Channel doesn't exist"); break;
//...
}
}

Step 6: Transmit data to users in a Channel

Having joined a channel now enables the client instance to start transmitting audio and video to the other users in the channel by calling TeamTalk4.EnableTransmission().

When the other users in the channel starts receiving audio they will receive the TeamTalk4.OnUserTalking() event. If video is also being transmitted the event TeamTalk4.OnUserVideoFrame() will be posted for every video frame which is received.

Here's a code-snip on how to transmit and display data:

/* in the constructor we should add an event handler seeing talking user and video */
public Form1()
{
//..
ttclient.OnUserTalking += new TeamTalk4.UserTalking(form1_OnUserTalking);
ttclient.OnUserVideoFrame += new TeamTalk4.UserVideoFrame(form1_OnUserVideoFrame);
//..
}
/* user wants to transmit audio */
public void button8TransmitAudio()
{
Debug.Assert(ttclient.ChannelID>0); //must be in channel or no one will receive
ttclient.EnableTransmission(TransmitType.TRANSMIT_AUDIO);
}
/* user wants to transmit audio */
public void button9TransmitVideo()
{
Debug.Assert(ttclient.ChannelID>0); //must be in channel or no one will receive
ttclient.EnableTransmission(TransmitType.TRANSMIT_VIDEO);
}
/* invoked when a user is talking */
void form1_OnUserTalking(int nUserID, bool bTalking)
{
User user;
if(!ttclient.GetUser(nUserID, out user)) return;
if(bTalking)
Debug.WriteLine(user.szNickName + " is talking...");
else
Debug.WriteLine(user.szNickName + " stopped talking...");
}
/* Hold a Bitmap for each user. We don't want to make a new
bitmap every time. It will take too much memory. */
Dictionary<int, Bitmap> bmps = new Dictionary<int, Bitmap>();
/* invoked when a new video frame can be displayed */
void form1_OnUserVideoFrame(int nUserID, int nFrameQueueSize)
{
Bitmap user_bmp = null;
bmps.TryGetValue(nUserID, out user_bmp);
/* if 'user_bmp' in GetUserVideoFrame(..) is null or if the
bitmap's dimensions are not correct, a new bitmap will
be allocated and the 'user_bmp' reference will be changed
after the call to GetUserVideoFrame(..). */
if(ttclient.GetUserVideoFrame(nUserID, out user_bmp)) {
//display the bitmap somewhere...
}
/* store bitmap in container so it can be reused */
bmps[nUserID] = user_bmp;
}