[Nice] integrating nice to another project

Andrés Suero kaneda.uy at gmail.com
Sun May 20 13:28:40 PDT 2012


Youness,
Thanks for your response, what we have is a socket that bring us the information, then we transfer peer to peer with libnice, and another socket where we need to put the info that we receive from the peer to peer connection.
The issue is that the info that we need to transfer to the other peer is not given all in one call, we need to check is theres info in the socket time to time to see if theres more, and if it is, we need to send it.
Is there a way to add a signal to be called in some period of time in the future, so we can check if we have new info to transmit, or some other way to get this effect?
The other (complicated) way that I thought was to start a thread when we receive the READY state and within this thread call to receive data from the socket and call to sendit peer to peer, but I don't know if this is going to crash all over the place because the call to the libnice to send is going to be from another thread.

Another question, Is there a way to attach a function to a signal with a context? this way I don't need to have a lot of global variables.

Attached is an example of our code.

Thanks,
Andrés Suero.




On 5/19/2012 5:16 PM, Youness Alaoui wrote:
> Hi,
>
> I'm not exactly sure why you're doing this, libnice is meant to create peer to
> peer connections, it's not meant to be used on a server that proxies data
> between two peers.
> Either way, no, the component_state_changed only gets fired when the state of
> the agent changes, so once it goes into READY, it won't get fired again. You
> should start your own proxying system only after the state goes to READY.
>
> Hope that helps,
> Youness
>
> On 05/18/2012 11:08 PM, Federico Kouyoumdjian wrote:
>> We are trying to integrate nice to another project.
>>
>> We have a socket in which we read data when the state of nice is
>> NICE_COMPONENT_STATE_READY. Then we send the data read in the socket to another
>> nice agent with the function nice_agent_send.
>>
>> Our doubt is what happens if no data is available in the sockt to send. If we
>> don't call the function nice_agent_send (because there is no data available to
>> send) can we count that cb_component_state_changed will fire again with the
>> state NICE_COMPONENT_STATE_READY.
>>
>> Thanks for the help.
>> _______________________________________________
>> nice mailing list
>> nice at lists.freedesktop.org
>> http://lists.freedesktop.org/mailman/listinfo/nice
>
>
> _______________________________________________
> nice mailing list
> nice at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/nice
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.freedesktop.org/archives/nice/attachments/20120520/83adc78d/attachment.htm>
-------------- next part --------------
#include <nice/nice.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


#include <fcntl.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#include <iostream>

#include "base64.h"

//stun.pjsip.org
#define STUN_ADDR "208.109.222.137"
#define STUN_PORT 3478

//standard MTU
#define MAX_PKG 1400

#define PORT_NO 34781

#define TIMEOUT 0.5
#define MAX_SDP_RETRIES 200

#define USR_LEN 80
#define PWD_LEN 80


//randevouz server address
#define HOST_NAME "labs.goalbit-solutions.com"


using namespace std;


NiceAgent *agent = NULL;
guint stream_id;
GSList *localCands,*remoteCands;
GMainLoop *loop;
guint myGlobalNiceComponentState;
GSList *rcands;
char agentDistinct;

char *interestedId = "A";
char *myId = "B";

char * r_id, *r_user, *r_pass, *r_candidates; //r_ define los datos del peer remoto


static void err_exit(const char *title)
{
  cout << title << endl;
  
  if (agent != NULL) g_object_unref(agent);
  exit(1);
}


/*
Asumimos que los parametros que necesitan ser escapados para meterse en el packete ya vienen escapados.
*/
int get_remote_sdp(char * user, char * pass, char * candidates, char * my_id, char * interested_in)
{
  
  int sockfd, portno, n;
  struct sockaddr_in serv_addr;
  struct hostent *server;

  char buffer[MAX_PKG];
  portno = PORT_NO;
  
  sockfd = socket(AF_INET, SOCK_STREAM, 0);
  if (sockfd < 0) 
      err_exit("ERROR opening socket");
  
  server = gethostbyname(HOST_NAME);
  if (server == NULL)
      err_exit("ERROR, no such host");
  
  bzero((char *) &serv_addr, sizeof(serv_addr));
  serv_addr.sin_family = AF_INET;
  bcopy((char *)server->h_addr,(char *)&serv_addr.sin_addr.s_addr,server->h_length);
  serv_addr.sin_port = htons(PORT_NO);
  if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0) 
      err_exit("ERROR connecting");

  
  //es mas uno por el \0 del final del string
  char resp[MAX_PKG+1] = "";
  
  string user_encoded = base64_encode((unsigned char*)user, strlen(user));
  string pwd_encoded = base64_encode((unsigned char*)pass, strlen(pass));  
  
  //Crear respuesta
  strcpy(resp,"a");
  strcat(resp,my_id);
  strcat(resp,"#");
  // begin old SDP
  strcat(resp,user_encoded.c_str());  
  strcat(resp,"%");
  strcat(resp,pwd_encoded.c_str());  
  strcat(resp,"%");
  strcat(resp,candidates);
  // end old SDP
  strcat(resp,"#");
  strcat(resp,interested_in);
  strcat(resp,"@");  
  
  n = write(sockfd,resp,strlen(resp));
  if (n < 0) 
    err_exit("ERROR writing to socket");


  int fin = 0;
  char package[MAX_PKG];
  int copiado;
  
  int ciclos = 0;
  while (!fin) {
    copiado = 0;    
    
    //obtener todo el paquete si se encuentra dentro del buffer, sino seguir haciendo read
    //recordar que el cliente manda solo un paquete por connexion, asi que si termino de recibir el paquete
    //no va a haber mas info despues.

    do{	
      n = read(sockfd, buffer, MAX_PKG);
      if (n < 0) 
	err_exit("ERROR reading from socket");

      if (n > 0){
	memcpy(package + copiado, buffer, n);
	copiado += n;    
      }          
      
    }while(n != 0 && package[copiado-1] != '@');
    
    package[copiado-1]='\0';
    //printf("Recibo del server=\n%s\n",package);
    

    if (package[0]=='o'){
//      printf("Sacar del paquete recibido el SDP\n");

      char *resto = package + 1;
      char * id, *r_user_encoded, *r_pass_encoded; //r_ define los datos del peer remoto
      //printf("resto=%s\n",resto);
      id = strtok(resto,"#");
      //printf("id=%s\n",id);
      r_user_encoded = strtok(NULL,"%");
      //printf("r_user_encoded=%s\n",r_user_encoded);
      std::string r_user_s = base64_decode(r_user_encoded);
      r_pass_encoded = strtok(NULL,"%");
      //printf("r_pass_encoded=%s\n",r_pass_encoded);
      std::string r_pass_s = base64_decode(r_pass_encoded);
      
      r_candidates = strtok(NULL,"#");
      //printf("r_candidates=%s\n",r_candidates);

      r_id = id;
      //r_user = (char*)r_user_s.c_str();
      r_user =(char*) malloc(sizeof(char)*USR_LEN);
      memcpy(r_user,r_user_s.c_str(),r_user_s.length()+1);
      //r_user
      //r_pass = (char*)r_pass_s.c_str();
      r_pass =(char*) malloc(sizeof(char)*PWD_LEN);
      memcpy(r_pass,r_pass_s.c_str(),r_pass_s.length()+1);
      printf("r_user=%s\n",r_user);
      
      fin = 1;
    } else {
	if (package[0] == 'x'){
	  err_exit("ERROR timeout randezvous");
	}
	
	ciclos++;
	//printf("No recibi respuesta satisfactoria\n");
	close(sockfd);
	sleep(TIMEOUT);
	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0) 
	    err_exit("ERROR connecting");
	
	strcpy(resp,"q");
	strcat(resp,my_id);
	strcat(resp,"@");
//	printf("Envio al server=\n%s\n",resp);
	
	n = write(sockfd,resp,strlen(resp));
	if (n < 0){	  	
	  err_exit("ERROR writing to socket");
	}
    }
    if (ciclos > MAX_SDP_RETRIES){
      //puts("ciclos>MAX_SDP_RETRIES\n");
      
      strcpy(resp,"d");      
      strcat(resp,my_id);
      strcat(resp,"@");
      n = write(sockfd,resp,strlen(resp));
      if (n < 0) 
	err_exit("ERROR writing to socket");
  
      close(sockfd);
      return -1;      
      //err_exit("Demasiadas respuestas insatisfactorias");
    }
  }
  close(sockfd);
  return 0;
}


void print_candidate_info(NiceCandidate *cand) {
      gchar str_ip[256];

      switch (cand->type){
        case NICE_CANDIDATE_TYPE_HOST:
          puts ("  NICE_CANDIDATE_TYPE_HOST");
          break;
        case NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE:
          puts ("  NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE");
          break;
        case NICE_CANDIDATE_TYPE_PEER_REFLEXIVE:
          puts ("  NICE_CANDIDATE_TYPE_PEER_REFLEXIVE");
          break;
        case NICE_CANDIDATE_TYPE_RELAYED:
          puts ("  NICE_CANDIDATE_TYPE_RELAYED");
          break;
        default:
          puts ("  UNKNOWN CANDIDATE!");
          break;
        }
      switch (cand->transport){
        case NICE_CANDIDATE_TRANSPORT_UDP:
          puts ("  NICE_CANDIDATE_TRANSPORT_UDP");
          break;
        default:
          puts ("UNKNOWN TRANSPORT");
          break;
        }

      nice_address_to_string (&(cand->addr), str_ip);
      printf ("  ADDRESS: %s\n", str_ip);
      nice_address_to_string (&(cand->base_addr), str_ip);
      printf ("  BASE ADDRESS: %s\n", str_ip);
      printf ("  PRIORITY: %d\n", cand->priority);
}

GSList * readCandsChar(char *candidates){

  int readNumCands;
  GSList *i = NULL;
  
  char*len;
  char*tmp;
  printf("CANDIDATES=%s\n",candidates);
  len = strtok(candidates,"$");
  printf("len=%s\n",len);
  readNumCands = atoi (len);
  
  printf("READ %d CANDIDATES\n",readNumCands);

  
  
  int n;
  for (n = 0; n < readNumCands; n++){
    tmp = strtok(NULL,"$");
    printf("candidato recibido=%s\n",tmp);
    std::string cand_s = base64_decode(tmp);
    const char* cand_c = cand_s.c_str();
        
    NiceCandidate *cand=(NiceCandidate *)malloc(sizeof(*cand));
    memcpy(cand,cand_c,sizeof(*cand));
    i = g_slist_append( i,cand);
  }
  
  return i;
}

char* writeCandsToChar(GSList *cands){

  GSList *i;
  std::string res;

  char buffer[10];
   
  int len = g_slist_length(cands);
  //itoa(len, buffer, 10);
  sprintf (buffer, "%d",len);


  res.append(buffer);
  res.append("$");
  
  for (i = cands; i; i = i->next){
    NiceCandidate *cand = (NiceCandidate *) i->data;
    std::string tmp = base64_encode((unsigned char *)cand , sizeof(*cand));
    printf("candidato enviado=%s\n",tmp.c_str());
    res.append(tmp);
    res.append("$");
  }
  
  return (char*)res.c_str();
}

void cb_nice_recv (NiceAgent * agent, guint stream_id, guint component_id, guint len, gchar * buf, gpointer user_data) {
  //aca se reciben los datos
  printf ("cb_nice_recv\n");
  printf ("RECEIVED STRING: %s\n",buf);
}

void cb_candidate_gathering_done (void) {
  printf ("ENTERING cb_candidate_gathering_done\n");
  localCands = nice_agent_get_local_candidates (agent, stream_id, 1);

  gchar *frag, *pwd;
  nice_agent_get_local_credentials (agent, stream_id, &frag, &pwd);
  printf("LOCAL USER: %s\nLOCAL PWD: %s\n",frag,pwd); 

  
  //mandarle al randezvous un paquete que tenga usr, pwd y el binario (capaz que conviene base64).
   
  char *cands =  writeCandsToChar(localCands);

  if(0 > get_remote_sdp(frag, pwd, cands, myId, interestedId)) err_exit("ERROR: Intercambio de datos con el randevouz fallo.");
   
  //recibe el binario, usr y pwd desde el randezvous
  remoteCands = readCandsChar(r_candidates);
  
  // tiago: print local candidates
  GSList *i;
  int n = 0;
  for (i = remoteCands; i; i = i->next) {
    NiceCandidate *cand = (NiceCandidate *) i->data;
    printf("CANDIDADE NUMBER %d:\n",n);
    print_candidate_info(cand);
    n++;
  }
   
  g_assert(remoteCands);


  printf("REMOTE USR: %s, REMOTE PWD: %s\n",r_user,r_pass);
  g_assert(nice_agent_set_remote_credentials (agent, stream_id, r_user, r_pass));
  //need to change the credentials with the remote agent
   
  puts("SETTING REMOTE CANDIDATES");
  g_assert(remoteCands);
  nice_agent_set_remote_candidates(agent, stream_id, 1, remoteCands);
  puts("REMOTE CANDIDATES CORRECTLY ADDED!\n");

}


void cb_component_state_changed(NiceAgent *agent, guint stream_id, guint component_id, guint state, gpointer data)
{
	puts("cb_component_state_changed");
	char *NiceComponentStateString[] = {
		"NICE_COMPONENT_STATE_DISCONNECTED",
		"NICE_COMPONENT_STATE_GATHERING",
		"NICE_COMPONENT_STATE_CONNECTING",
		"NICE_COMPONENT_STATE_CONNECTED",
		"NICE_COMPONENT_STATE_READY",
		"NICE_COMPONENT_STATE_FAILED",
		"NICE_COMPONENT_STATE_LAST" };
	
	myGlobalNiceComponentState = state; //imposta var globale

	puts("CB_COMPONENT STATE CHANGED");
	printf("%d : %s\n", state, NiceComponentStateString[state]);

	switch(state)
	{
		case NICE_COMPONENT_STATE_READY:
			//aca es donde se mandan los datos
			nice_agent_send(agent, stream_id, NICE_COMPONENT_TYPE_RTP, strlen("Ola"),"Ola");
			break;
		case NICE_COMPONENT_STATE_FAILED: //fechar loop
			if(g_main_loop_is_running (loop)) {
				puts("Stopping the loop. :(\n");
				g_main_loop_quit(loop);
			}
			else { g_error ("Alert: No loop! :("); }
			break;
	}
	return;
}


void cb_new_selected_pair (void)
{
  printf ("cb_new_selected_pair\n");
}


int main (int argc, char *argv[])
{
  
  g_type_init ();
  g_thread_init (NULL);

  loop = g_main_loop_new (NULL, FALSE);

  nice_debug_enable (TRUE);

  // Create a nice agent
  //Es reliable porque es el que implementa el TCP sobre UDP
  agent = nice_agent_new_reliable (g_main_loop_get_context (loop), NICE_COMPATIBILITY_RFC5245);

  g_object_set (G_OBJECT (agent), "stun-server", STUN_ADDR, "stun-server-port", STUN_PORT, NULL);

  // Connect the signals
  g_signal_connect (G_OBJECT (agent), "candidate-gathering-done", G_CALLBACK (cb_candidate_gathering_done), GUINT_TO_POINTER(1));
  g_signal_connect (G_OBJECT (agent), "component-state-changed", G_CALLBACK (cb_component_state_changed), GUINT_TO_POINTER(1));
  g_signal_connect (G_OBJECT (agent), "new-selected-pair", G_CALLBACK (cb_new_selected_pair), GUINT_TO_POINTER(1));

  // Create a new stream with one component and start gathering candidates
  stream_id = nice_agent_add_stream (agent, 1);
  

  
  // Attach to the component to receive the data

  nice_agent_attach_recv (agent, stream_id, 1, g_main_loop_get_context (loop), cb_nice_recv, NULL);

  printf ("START nice_agent_gather_candidates");
  nice_agent_gather_candidates (agent, stream_id);

  //esto es el loop en el que queda corriendo, mientras atiende los eventos
  g_main_loop_run (loop);

  // Destroy the object
  g_object_unref (agent);

  return 0;
}



More information about the nice mailing list