Logo Search packages:      
Sourcecode: farsight version File versions  Download package

test-jingle.c

/*
 * test_jingle.c - Farsight tests
 *
 * Farsight Voice+Video library test suite
 *  Copyright 2005,2006 Collabora Ltd.
 *  Copyright 2005,2006 Nokia Corporation
 *   @author: Rob Taylor <rob.taylor@collabora.co.uk>.
 *   @author: Philippe Khalaf <philippe.khalaf@collabora.co.uk>.
 *  Copyright 2006 Vittorio Palmisano
 *   @author: Vittorio Palmisano <vpalmisano@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <glib.h>
#include <unistd.h>
#include <gst/gst.h>
#include <gst/interfaces/xoverlay.h>
#include <farsight/farsight-session.h>
#include <farsight/farsight-stream.h>
#include <farsight/farsight-transport.h>

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#define TYPE_CANDIDATE 0
#define TYPE_CODEC 1
#define TYPE_READY_BYTE 2

#define MODE_DUPLEX 0
#define MODE_AUDIO 1
#define MODE_VIDEO 2

GMainLoop *mainloop = NULL;

guint send_sock = 0;
struct sockaddr_in serv_addr;
FarsightStream *audio_stream = NULL;
FarsightStream *video_stream = NULL;

static void
stream_error (FarsightStream * stream,
    FarsightStreamError error, const gchar * debug)
{
  g_print ("%s: stream=%p error=%s\n", __FUNCTION__, stream, debug);
}

static void
session_error (FarsightSession * stream,
    FarsightSessionError error, const gchar * debug)
{
  g_print ("%s: session=%p error=%s\n", __FUNCTION__, stream, debug);
}


static void
new_active_candidate_pair (FarsightStream * stream, gchar * native_candidate,
    gchar * remote_candidate)
{
  g_debug ("%s: native: %s, remote: %s", __FUNCTION__, native_candidate,
      remote_candidate);
}

static void
codec_changed (FarsightStream * stream, gint codec_id)
{
  g_print ("%s: codec_id=%d, stream=%p\n", __FUNCTION__, codec_id, stream);
}

static void
native_candidates_prepared (FarsightStream * stream)
{
  const GList *transport_candidates, *lp;
  FarsightTransportInfo *info;

  g_message ("%s: stream=%p\n", __FUNCTION__, stream);

  transport_candidates = farsight_stream_get_native_candidate_list (stream);
  for (lp = transport_candidates; lp; lp = g_list_next (lp)) {
    info = (FarsightTransportInfo *) lp->data;
    g_message ("Local transport candidate: %s %d %s %s %s %d pref %f",
        info->candidate_id, info->component,
        (info->proto == FARSIGHT_NETWORK_PROTOCOL_TCP) ? "TCP" : "UDP",
        info->proto_subtype, info->ip, info->port, (double) info->preference);
  }
}

void
show_element (GstElement * e)
{
  GstElementFactory *fac = gst_element_get_factory (e);

  g_print ("element: %s (%s)\n", gst_element_get_name (e),
      gst_element_factory_get_longname (fac));
}

static void
state_changed (FarsightStream * stream,
    FarsightStreamState state, FarsightStreamDirection dir)
{
  switch (state) {
    case FARSIGHT_STREAM_STATE_DISCONNECTED:
      g_message ("%s: %p disconnected\n", __FUNCTION__, stream);
      break;
    case FARSIGHT_STREAM_STATE_CONNECTING:
      g_message ("%s: %p connecting\n", __FUNCTION__, stream);
      break;
    case FARSIGHT_STREAM_STATE_CONNECTED:
      g_message ("%s: %p connected\n", __FUNCTION__, stream);

      farsight_stream_signal_native_candidates_prepared (stream);

      farsight_stream_start (stream);
      break;
  }
}

void
setup_send (gchar * host, gint port)
{
  struct hostent *rx_addr = gethostbyname (host);
  send_sock = socket (AF_INET, SOCK_DGRAM, 0);

  serv_addr.sin_family = AF_INET;
  bcopy (rx_addr->h_addr, &serv_addr.sin_addr, rx_addr->h_length);

  serv_addr.sin_port = htons (port);
}

GIOChannel *
setup_recv (gint port)
{
  int sockfd = socket (AF_INET, SOCK_DGRAM, 0);
  struct sockaddr_in dest_addr;
  g_message (G_STRFUNC);


  bzero (&dest_addr, sizeof (dest_addr));
  dest_addr.sin_family = AF_INET;
  dest_addr.sin_addr.s_addr = INADDR_ANY;
  dest_addr.sin_port = htons (port);

  if (bind (sockfd, (const struct sockaddr *) &dest_addr, sizeof (dest_addr)) == -1)
    {
      g_critical ("%s: bind returned error %s", G_STRFUNC, strerror(errno));
      exit(1);
    }

  g_debug ("%s: sockfd = %d",G_STRFUNC, sockfd);
  return g_io_channel_unix_new (sockfd);
}

static void
send_codecs (const GList *codecs)
{
  const GList *lp;
  FarsightCodec *codec;
  gchar s[300] = { 0 };
  codec = (FarsightCodec *) codecs->data;

  for (lp = codecs; lp; lp = g_list_next (lp)) {
    codec = (FarsightCodec *) lp->data;
    g_message (G_STRFUNC);

    sprintf (s, "%d %d %s %d %u %u ", TYPE_CODEC, codec->media_type,
        codec->encoding_name, codec->id, codec->clock_rate, codec->channels);
    g_message ("Sending codec");
    if (sendto (send_sock, s, strlen (s), 0,
          (struct sockaddr *) &serv_addr, sizeof (struct sockaddr_in)) <0)
      perror ("sendto: ");
  }

  sprintf (s, "%d %d %s %d %u %u ", TYPE_CODEC, codec->media_type, "LAST", 0, 0,
      0);
  g_message ("Sending end of list packet");
  if (sendto (send_sock, s, strlen (s), 0,
        (struct sockaddr *) &serv_addr, sizeof (struct sockaddr_in)) <0)
    {
      g_critical ("%s: sendto returned error %s", G_STRFUNC, strerror(errno));
      exit(1);
    }
}

static void
send_ready_byte ()
{
  gchar s[2];
  sprintf (s, "%d", TYPE_READY_BYTE);
  g_message ("Sending ready byte");
  if (sendto (send_sock, s, strlen (s), 0,
    (struct sockaddr *) &serv_addr, sizeof (struct sockaddr_in)) <0)
    {
      g_critical ("%s: sendto returned error %s", G_STRFUNC, strerror(errno));
      exit(1);
    }
}

static void
do_handshake(gint sockfd)
{
  gint type;
  socklen_t fromlen;
  struct sockaddr_in from_addr;
  gchar buf[1500] = { 0 };

  send_ready_byte();
  /* wait for reply */
  fromlen = sizeof (struct sockaddr_in);

  g_message ("waiting for ready byte");
  if (recvfrom (sockfd, buf, 1500, 0,
      (struct sockaddr *) &from_addr, &fromlen) == -1)
    {
      g_critical ("%s: recvfrom returned error %s", G_STRFUNC, strerror(errno));
      exit(1);
    }
  g_message ("got message!");

  sscanf (buf, "%d", &type);

  if (type == TYPE_READY_BYTE)
  {
    send_ready_byte();
  }

  return;
}

void
send_candidate (FarsightMediaType type, FarsightTransportInfo *trans)
{
  gchar s[300] = { 0 };
  g_message (G_STRFUNC);
  sprintf (s, "%d %u %s %s %d %s %s ", TYPE_CANDIDATE, type,
      trans->candidate_id, trans->ip, trans->port, trans->username,
      trans->password);
  g_message ("Sending..");
  if (sendto (send_sock, s, strlen (s), 0,
    (struct sockaddr *) &serv_addr, sizeof (struct sockaddr_in)) <0)
    {
      g_critical ("%s: sendto returned error %s", G_STRFUNC, strerror(errno));
      exit(1);
    }
}


static void
new_native_candidate (FarsightStream * stream, gchar * candidate_id)
{
  GList *candidate =
      farsight_stream_get_native_candidate (stream, candidate_id);
  FarsightTransportInfo *trans = candidate->data;
  FarsightMediaType type;

  g_message ("New native candidate: "
      "<id: %s, "
      "component: %d, "
      "ip: %s port: %d "
      "proto: %d, "
      "proto_subtype: %s, "
      "proto_profile: %s, "
      "preference: %f, "
      "type: %d "
      "username: %s password: %s>",
      trans->candidate_id, trans->component,
      trans->ip, trans->port, trans->proto, trans->proto_subtype,
      trans->proto_profile, trans->preference,
      trans->type, trans->username, trans->password);

  g_object_get (G_OBJECT(stream), "media-type", &type, NULL);

  send_candidate (type,trans);

  /*g_message("Native candidates:");
     GList* nc = farsight_stream_get_native_candidate_list(stream);
     GList* p;
     for(p = nc; p; p = g_list_next(p)){
     FarsightTransportInfo* trans = p->data;
     g_print("%s:%d\n", trans->ip, trans->port);
     } */

}

static void
add_remote_codec (FarsightStream *stream, gint pt, gchar *encoding_name,
    FarsightMediaType media_type, guint clock_rate, guint channels)
{
  static GList *remote_codecs_audio = NULL;
  static GList *remote_codecs_video = NULL;
  GList *lp = NULL;
  FarsightCodec *codec;

  if (g_ascii_strcasecmp (encoding_name, "LAST") == 0)
  {
    if (media_type == FARSIGHT_MEDIA_TYPE_AUDIO)
    {
      farsight_stream_set_remote_codecs (stream, remote_codecs_audio);
    }
    else if (media_type == FARSIGHT_MEDIA_TYPE_VIDEO)
    {
      farsight_stream_set_remote_codecs (stream, remote_codecs_video);
    }
    return;
  }

  codec = g_new0 (FarsightCodec, 1);

  codec->id = pt;
  codec->encoding_name = strdup (encoding_name);
  codec->media_type = media_type;
  codec->clock_rate = clock_rate;
  codec->channels = channels;

  if (media_type == FARSIGHT_MEDIA_TYPE_AUDIO)
  {
    remote_codecs_audio = g_list_append (remote_codecs_audio, codec);
  }
  else if (media_type == FARSIGHT_MEDIA_TYPE_VIDEO)
  {
    remote_codecs_video = g_list_append (remote_codecs_video, codec);
  }

  for (lp = remote_codecs_audio; lp; lp = g_list_next (lp)) {
    codec = (FarsightCodec *) lp->data;
    g_message ("added audio codec: %d: %s/%d found", codec->id, codec->encoding_name,
        codec->clock_rate);
  }
  for (lp = remote_codecs_video; lp; lp = g_list_next (lp)) {
    codec = (FarsightCodec *) lp->data;
    g_message ("added video codec: %d: %s/%d found", codec->id, codec->encoding_name,
        codec->clock_rate);
  }
}


static void
add_remote_candidate (FarsightStream * stream, gchar * id, gchar * ip,
    gint port, gchar * username, gchar * password)
{
  FarsightTransportInfo *trans;
  GList *candidate_glist = NULL;

  //g_print(">>> Adding remote candidate: %s\n", id);
  g_return_if_fail (stream != NULL);

  /* let's create our candidate from ip:port given on command line */
  trans = g_new0 (FarsightTransportInfo, 1);

  trans->candidate_id = g_strdup (id);
  trans->component = 1;
  trans->ip = g_strdup (ip);
  trans->port = port;
  trans->proto = FARSIGHT_NETWORK_PROTOCOL_UDP;
  trans->proto_subtype = "RTP";
  trans->proto_profile = "AVP";
  trans->preference = 1.0;
  trans->type = FARSIGHT_CANDIDATE_TYPE_LOCAL;
  trans->username = g_strdup (username);
  trans->password = g_strdup (password);

  candidate_glist = g_list_append (candidate_glist, trans);

  farsight_stream_add_remote_candidate (stream, candidate_glist);
}

FarsightSession *setup_rtp_session ();
FarsightStream *setup_rtp_stream (FarsightSession * session, FarsightMediaType type);

static gboolean
receive_loop (GIOChannel *ch, GIOCondition cond, gpointer data)
{
  gint type;
  gint sockfd = g_io_channel_unix_get_fd (ch);

  gchar id[100], ip[100], username[100], password[100] = { 0 };
  gint port = 0;

  gchar encoding_name[100] = { 0 };
  gint pt, media_type;
  guint clock_rate, channels;

  gchar buf[1500] = { 0 };
  struct sockaddr_in from_addr;
  socklen_t fromlen;
  fromlen = sizeof (struct sockaddr_in);
  g_message ("waiting for msg");
  if (recvfrom (sockfd, buf, 1500, 0,
      (struct sockaddr *) &from_addr, &fromlen) == -1)
    {
      g_critical ("%s: recvfrom returned error %s", G_STRFUNC, strerror(errno));
      exit(1);
    }
  g_message ("got message!");

  sscanf (buf, "%d", &type);

  g_message ("got type %d", type);

  switch(type)
  {
    case TYPE_CANDIDATE:
      sscanf (buf+1, "%d %s %s %d %s %s", &media_type, id, ip, &port,
          username, password);

      g_message ("Recieved %d %s %s %d %s %s", media_type, id, ip, port,
          username, password);

      switch((FarsightMediaType)media_type)
      {
        case FARSIGHT_MEDIA_TYPE_AUDIO:
          add_remote_candidate (audio_stream, id, ip, port, username,
              password);
          break;
        case FARSIGHT_MEDIA_TYPE_VIDEO:
          add_remote_candidate (video_stream, id, ip, port, username,
              password);
          break;
      }
      break;
    case TYPE_CODEC:
      sscanf (buf+1, "%d %s %d %u %u", &media_type, encoding_name, &pt,
          &clock_rate, &channels);

      g_message ("Received %d %s %d %u %u", media_type, encoding_name, pt,
          clock_rate, channels);

      switch((FarsightMediaType)media_type)
      {
        case FARSIGHT_MEDIA_TYPE_AUDIO:
          add_remote_codec (audio_stream, pt, encoding_name, media_type,
              clock_rate, channels);
          break;
        case FARSIGHT_MEDIA_TYPE_VIDEO:
          add_remote_codec (video_stream, pt, encoding_name, media_type,
              clock_rate, channels);
          break;
      }
      break;
    case TYPE_READY_BYTE:
      g_message ("got ready byte");
      break;
  }
  return TRUE;
}

static GstBusSyncReply
set_xoverlay (GstBus * bus, GstMessage * message, gulong *xid)
{
  g_message (" %s: called", G_STRFUNC);
  /* ignore anything but 'prepare-xwindow-id' element messages */
  if (GST_MESSAGE_TYPE (message) != GST_MESSAGE_ELEMENT)
    return GST_BUS_PASS;

  if (!gst_structure_has_name (message->structure, "prepare-xwindow-id"))
    return GST_BUS_PASS;

  g_message (" %s: setting x overlay window id", G_STRFUNC);

  gst_x_overlay_set_xwindow_id (GST_X_OVERLAY (GST_MESSAGE_SRC (message)),
      *xid);

  return GST_BUS_DROP;
}

int
main (int argc, char **argv)
{
  char *sinktype;
  GIOChannel *recv_chan;
  gint mode;
  FarsightSession *session;

  if (argc != 4 && argc != 5 && argc != 6)
  {
    g_print("usage: %s remoteip remoteport localport [mode] [xid]\n", argv[0]);
    return -1;
  }

  if (argc >= 5)
  {
    mode = atoi(argv[4]);
  }
  else
  {
    mode = MODE_DUPLEX;
  }


  g_debug ("argc: %d, xid=%s", argc, argv[5]);

  g_type_init ();
  gst_init (&argc, &argv);

  mainloop = g_main_loop_new (NULL, FALSE);

  setup_send (argv[1], atoi (argv[2]));
  g_message ("Sending to %s %d listening on port %d", argv[1], atoi (argv[2]), atoi
      (argv[3]));

  recv_chan = setup_recv (atoi(argv[3]));

  /* this will wait until both sides are loaded before sending anything */
  do_handshake(g_io_channel_unix_get_fd (recv_chan));

  g_io_add_watch (recv_chan, G_IO_IN | G_IO_PRI, receive_loop, NULL);

  session = setup_rtp_session ();

  if (mode == MODE_AUDIO || mode == MODE_DUPLEX)
  {
    audio_stream = setup_rtp_stream (session, FARSIGHT_MEDIA_TYPE_AUDIO);
  }
  if (mode == MODE_VIDEO || mode == MODE_DUPLEX)
  {
    video_stream = setup_rtp_stream (session, FARSIGHT_MEDIA_TYPE_VIDEO);
  }

  /* it's better to set the active codec before the remote_codecs to avoid a
   * send pipeline change from the start */
  if (audio_stream)
  {
    GstElement *alsasrc, *alsasink;

    farsight_stream_set_active_codec (audio_stream, 8);

    alsasrc = gst_element_factory_make ("audiotestsrc", "src");
    alsasink = gst_element_factory_make ("alsasink", "alsasink");

    g_object_set (G_OBJECT (alsasink), "sync", FALSE, NULL);
    g_object_set (G_OBJECT (alsasink), "latency-time", G_GINT64_CONSTANT (20000),
        NULL);
    g_object_set (G_OBJECT (alsasink), "buffer-time", G_GINT64_CONSTANT (80000),
        NULL);

    g_object_set (G_OBJECT (alsasrc), "blocksize", 320, NULL);
    g_object_set (G_OBJECT (alsasrc), "latency-time", G_GINT64_CONSTANT (20000),
        NULL);
    g_object_set (G_OBJECT (alsasrc), "is-live", TRUE, NULL);

    farsight_stream_set_sink (audio_stream, alsasink);
    farsight_stream_set_source (audio_stream, alsasrc);
  }

  if (video_stream)
  {
    GstElement *videosrc, *videosink, *videoscale, *colorspace, *ximagesink,
               *glimagesink;
    GstPad *pad;
    GstCaps *src_filter;

    videosrc = gst_element_factory_make("videotestsrc", NULL);
    g_object_set (G_OBJECT (videosrc), "is-live", TRUE, NULL);

    sinktype = getenv ("FS_TEST_VIDEO_SINK");

    if (sinktype && 0==strcmp(sinktype,"xvimagesink"))
    {
      videosink = gst_element_factory_make("xvimagesink", NULL);
      g_object_set (G_OBJECT (videosink), "sync", FALSE, NULL);
    }
    else if (sinktype && 0==strcmp(sinktype, "glimagesink"))
    {
      glimagesink = gst_element_factory_make("glimagesink", NULL);
      g_object_set (G_OBJECT (glimagesink), "sync", FALSE, NULL);

      videoscale = gst_element_factory_make("videoscale", NULL);
      colorspace = gst_element_factory_make("ffmpegcolorspace", NULL);

      videosink = gst_pipeline_new ("videosinkbin");

      gst_bin_add_many (GST_BIN (videosink), colorspace, videoscale,
          glimagesink, NULL);
      gst_element_link_many (colorspace, videoscale, glimagesink, NULL);

      pad = gst_element_get_pad (colorspace, "sink");
      gst_element_add_pad (videosink, gst_ghost_pad_new ("sink", pad));
      gst_object_unref (GST_OBJECT (pad));
    }
    else
    {
      ximagesink = gst_element_factory_make("ximagesink", NULL);
      g_object_set (G_OBJECT (ximagesink), "sync", FALSE, NULL);

      videoscale = gst_element_factory_make("videoscale", NULL);
      colorspace = gst_element_factory_make("ffmpegcolorspace", NULL);

      videosink = gst_pipeline_new ("videosinkbin");

      gst_bin_add_many (GST_BIN (videosink), colorspace, videoscale, ximagesink,
          NULL);
      gst_element_link_many (colorspace, videoscale, ximagesink, NULL);

      pad = gst_element_get_pad (colorspace, "sink");
      gst_element_add_pad (videosink, gst_ghost_pad_new ("sink", pad));
      gst_object_unref (GST_OBJECT (pad));
    }

    src_filter = gst_caps_new_simple ("video/x-raw-yuv", "width",
        G_TYPE_INT, 352, "height", G_TYPE_INT, 288, "framerate",
        GST_TYPE_FRACTION, 15,1, NULL);
    farsight_stream_set_source_filter (video_stream, src_filter);
    gst_caps_unref (src_filter);
    farsight_stream_set_source(video_stream, videosrc);
    farsight_stream_set_sink(video_stream, videosink);

    /* let's connect the the prepare-xwindow-id bus message sent by xvimagesink */
    if (argc == 6)
    {
      GstBus *bus;
      GstElement *pipeline = farsight_stream_get_pipeline (video_stream);
      gulong xid = atol (argv[5]);

      g_debug ("setting up xoverlay bus callback");
      bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
      gst_bus_set_sync_handler (bus, (GstBusSyncHandler) set_xoverlay, &xid);
    }
  }
  g_main_loop_run (mainloop);
  return 0;
}

FarsightSession *
setup_rtp_session ()
{
  FarsightSession *session;

  session = farsight_session_factory_make ("rtp");

  if (!session) {
    g_error ("RTP plugin not found");
    exit (1);
  }
  g_print ("protocol details:\n name: %s\n description: %s\n author: %s\n",
      farsight_plugin_get_name (session->plugin),
      farsight_plugin_get_description (session->plugin),
      farsight_plugin_get_author (session->plugin));
  g_signal_connect (G_OBJECT (session), "error",
      G_CALLBACK (session_error), NULL);


  return session;
}

FarsightStream *
setup_rtp_stream (FarsightSession * session, FarsightMediaType type)
{
  FarsightStream *stream;
  const GList *possible_codecs, *lp;
  FarsightCodec *codec;

  stream = farsight_session_create_stream (session,
      type, FARSIGHT_STREAM_DIRECTION_BOTH);

  g_object_set (G_OBJECT (stream), "transmitter", "libjingle", NULL);

  g_signal_connect (G_OBJECT (stream), "error",
      G_CALLBACK (stream_error), NULL);
  g_signal_connect (G_OBJECT (stream), "new-active-candidate-pair",
      G_CALLBACK (new_active_candidate_pair), NULL);
  g_signal_connect (G_OBJECT (stream), "codec-changed",
      G_CALLBACK (codec_changed), NULL);
  g_signal_connect (G_OBJECT (stream), "native-candidates-prepared",
      G_CALLBACK (native_candidates_prepared), NULL);
  g_signal_connect (G_OBJECT (stream), "state-changed",
      G_CALLBACK (state_changed), NULL);
  g_signal_connect (G_OBJECT (stream), "new-native-candidate",
      G_CALLBACK (new_native_candidate), NULL);

  possible_codecs = farsight_stream_get_local_codecs (stream);

  for (lp = possible_codecs; lp; lp = g_list_next (lp)) {
    codec = (FarsightCodec *) lp->data;
    g_message ("codec: %d: %s/%d found", codec->id, codec->encoding_name,
        codec->clock_rate);
  }

  send_codecs (possible_codecs);

  farsight_stream_prepare_transports (stream);

  return stream;
}

Generated by  Doxygen 1.6.0   Back to index