gst_plugins_base/gst/tcp/gsttcpserversink.c
changeset 0 0e761a78d257
child 8 4a7fac7dd34a
equal deleted inserted replaced
-1:000000000000 0:0e761a78d257
       
     1 /* GStreamer
       
     2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
       
     3  * Copyright (C) <2004> Thomas Vander Stichele <thomas at apestaart dot org>
       
     4  *
       
     5  * This library is free software; you can redistribute it and/or
       
     6  * modify it under the terms of the GNU Library General Public
       
     7  * License as published by the Free Software Foundation; either
       
     8  * version 2 of the License, or (at your option) any later version.
       
     9  *
       
    10  * This library is distributed in the hope that it will be useful,
       
    11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    13  * Library General Public License for more details.
       
    14  *
       
    15  * You should have received a copy of the GNU Library General Public
       
    16  * License along with this library; if not, write to the
       
    17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
       
    18  * Boston, MA 02111-1307, USA.
       
    19  */
       
    20 
       
    21 /**
       
    22  * SECTION:tcpserversink
       
    23  * @short_description: a sink that acts as a TCP server and sends data to
       
    24  *  multiple clients
       
    25  * @see_also: #multifdsink
       
    26  */
       
    27 
       
    28 #ifdef HAVE_CONFIG_H
       
    29 #include "config.h"
       
    30 #endif
       
    31 #ifdef __SYMBIAN32__
       
    32 #include "gst/gst-i18n-plugin.h"
       
    33 #else
       
    34 #include <gst/gst-i18n-plugin.h>
       
    35 #endif
       
    36 #include <string.h>             /* memset */
       
    37 
       
    38 #include <sys/ioctl.h>
       
    39 
       
    40 #ifdef HAVE_FIONREAD_IN_SYS_FILIO
       
    41 #include <sys/filio.h>
       
    42 #endif
       
    43 
       
    44 #include "gsttcp.h"
       
    45 #include "gsttcpserversink.h"
       
    46 #include "gsttcp-marshal.h"
       
    47 
       
    48 #define TCP_BACKLOG             5
       
    49 
       
    50 /* elementfactory information */
       
    51 static const GstElementDetails gst_tcp_server_sink_details =
       
    52 GST_ELEMENT_DETAILS ("TCP server sink",
       
    53     "Sink/Network",
       
    54     "Send data as a server over the network via TCP",
       
    55     "Thomas Vander Stichele <thomas at apestaart dot org>");
       
    56 
       
    57 GST_DEBUG_CATEGORY_STATIC (tcpserversink_debug);
       
    58 #define GST_CAT_DEFAULT (tcpserversink_debug)
       
    59 
       
    60 enum
       
    61 {
       
    62   ARG_0,
       
    63   ARG_HOST,
       
    64   ARG_PORT,
       
    65 };
       
    66 
       
    67 static void gst_tcp_server_sink_finalize (GObject * gobject);
       
    68 
       
    69 static gboolean gst_tcp_server_sink_handle_wait (GstMultiFdSink * sink,
       
    70     GstPoll * set);
       
    71 static gboolean gst_tcp_server_sink_init_send (GstMultiFdSink * this);
       
    72 static gboolean gst_tcp_server_sink_close (GstMultiFdSink * this);
       
    73 static void gst_tcp_server_sink_removed (GstMultiFdSink * sink, int fd);
       
    74 
       
    75 static void gst_tcp_server_sink_set_property (GObject * object, guint prop_id,
       
    76     const GValue * value, GParamSpec * pspec);
       
    77 static void gst_tcp_server_sink_get_property (GObject * object, guint prop_id,
       
    78     GValue * value, GParamSpec * pspec);
       
    79 
       
    80 
       
    81 GST_BOILERPLATE (GstTCPServerSink, gst_tcp_server_sink, GstMultiFdSink,
       
    82     GST_TYPE_MULTI_FD_SINK);
       
    83 
       
    84 
       
    85 static void
       
    86 gst_tcp_server_sink_base_init (gpointer g_class)
       
    87 {
       
    88   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
       
    89 
       
    90   gst_element_class_set_details (element_class, &gst_tcp_server_sink_details);
       
    91 }
       
    92 
       
    93 static void
       
    94 gst_tcp_server_sink_class_init (GstTCPServerSinkClass * klass)
       
    95 {
       
    96   GObjectClass *gobject_class;
       
    97   GstMultiFdSinkClass *gstmultifdsink_class;
       
    98 
       
    99   gobject_class = (GObjectClass *) klass;
       
   100   gstmultifdsink_class = (GstMultiFdSinkClass *) klass;
       
   101 
       
   102   gobject_class->set_property = gst_tcp_server_sink_set_property;
       
   103   gobject_class->get_property = gst_tcp_server_sink_get_property;
       
   104   gobject_class->finalize = gst_tcp_server_sink_finalize;
       
   105 
       
   106   g_object_class_install_property (gobject_class, ARG_HOST,
       
   107       g_param_spec_string ("host", "host", "The host/IP to send the packets to",
       
   108           TCP_DEFAULT_HOST, G_PARAM_READWRITE));
       
   109   g_object_class_install_property (gobject_class, ARG_PORT,
       
   110       g_param_spec_int ("port", "port", "The port to send the packets to",
       
   111           0, TCP_HIGHEST_PORT, TCP_DEFAULT_PORT, G_PARAM_READWRITE));
       
   112 
       
   113   gstmultifdsink_class->init = gst_tcp_server_sink_init_send;
       
   114   gstmultifdsink_class->wait = gst_tcp_server_sink_handle_wait;
       
   115   gstmultifdsink_class->close = gst_tcp_server_sink_close;
       
   116   gstmultifdsink_class->removed = gst_tcp_server_sink_removed;
       
   117 
       
   118   GST_DEBUG_CATEGORY_INIT (tcpserversink_debug, "tcpserversink", 0, "TCP sink");
       
   119 }
       
   120 
       
   121 static void
       
   122 gst_tcp_server_sink_init (GstTCPServerSink * this,
       
   123     GstTCPServerSinkClass * klass)
       
   124 {
       
   125   this->server_port = TCP_DEFAULT_PORT;
       
   126   /* should support as minimum 576 for IPV4 and 1500 for IPV6 */
       
   127   /* this->mtu = 1500; */
       
   128   this->host = g_strdup (TCP_DEFAULT_HOST);
       
   129 
       
   130   this->server_sock.fd = -1;
       
   131 }
       
   132 
       
   133 static void
       
   134 gst_tcp_server_sink_finalize (GObject * gobject)
       
   135 {
       
   136   GstTCPServerSink *this = GST_TCP_SERVER_SINK (gobject);
       
   137 
       
   138   g_free (this->host);
       
   139 
       
   140   G_OBJECT_CLASS (parent_class)->finalize (gobject);
       
   141 }
       
   142 
       
   143 /* handle a read request on the server,
       
   144  * which indicates a new client connection */
       
   145 static gboolean
       
   146 gst_tcp_server_sink_handle_server_read (GstTCPServerSink * sink)
       
   147 {
       
   148   /* new client */
       
   149   int client_sock_fd;
       
   150   struct sockaddr_in client_address;
       
   151   unsigned int client_address_len;
       
   152 
       
   153   /* For some stupid reason, client_address and client_address_len has to be
       
   154    * zeroed */
       
   155   memset (&client_address, 0, sizeof (client_address));
       
   156   client_address_len = 0;
       
   157 
       
   158   client_sock_fd =
       
   159       accept (sink->server_sock.fd, (struct sockaddr *) &client_address,
       
   160       &client_address_len);
       
   161   if (client_sock_fd == -1) {
       
   162     GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE, (NULL),
       
   163         ("Could not accept client on server socket %d: %s (%d)",
       
   164             sink->server_sock.fd, g_strerror (errno), errno));
       
   165     return FALSE;
       
   166   }
       
   167 
       
   168   gst_multi_fd_sink_add (GST_MULTI_FD_SINK (sink), client_sock_fd);
       
   169 
       
   170   GST_DEBUG_OBJECT (sink, "added new client ip %s with fd %d",
       
   171       inet_ntoa (client_address.sin_addr), client_sock_fd);
       
   172 
       
   173   return TRUE;
       
   174 }
       
   175 
       
   176 static void
       
   177 gst_tcp_server_sink_removed (GstMultiFdSink * sink, int fd)
       
   178 {
       
   179 #ifndef GST_DISABLE_GST_DEBUG
       
   180   GstTCPServerSink *this = GST_TCP_SERVER_SINK (sink);
       
   181 #endif
       
   182 
       
   183   GST_LOG_OBJECT (this, "closing fd %d", fd);
       
   184   if (close (fd) < 0) {
       
   185     GST_WARNING_OBJECT (this, "error closing fd %d: %s", fd,
       
   186         g_strerror (errno));
       
   187   }
       
   188 }
       
   189 
       
   190 static gboolean
       
   191 gst_tcp_server_sink_handle_wait (GstMultiFdSink * sink, GstPoll * set)
       
   192 {
       
   193   GstTCPServerSink *this = GST_TCP_SERVER_SINK (sink);
       
   194 
       
   195   if (gst_poll_fd_can_read (set, &this->server_sock)) {
       
   196     /* handle new client connection on server socket */
       
   197     if (!gst_tcp_server_sink_handle_server_read (this)) {
       
   198       GST_ELEMENT_ERROR (this, RESOURCE, READ, (NULL),
       
   199           ("client connection failed: %s", g_strerror (errno)));
       
   200       return FALSE;
       
   201     }
       
   202   }
       
   203   return TRUE;
       
   204 }
       
   205 
       
   206 static void
       
   207 gst_tcp_server_sink_set_property (GObject * object, guint prop_id,
       
   208     const GValue * value, GParamSpec * pspec)
       
   209 {
       
   210   GstTCPServerSink *sink;
       
   211 
       
   212   g_return_if_fail (GST_IS_TCP_SERVER_SINK (object));
       
   213   sink = GST_TCP_SERVER_SINK (object);
       
   214 
       
   215   switch (prop_id) {
       
   216     case ARG_HOST:
       
   217       if (!g_value_get_string (value)) {
       
   218         g_warning ("host property cannot be NULL");
       
   219         break;
       
   220       }
       
   221       g_free (sink->host);
       
   222       sink->host = g_strdup (g_value_get_string (value));
       
   223       break;
       
   224     case ARG_PORT:
       
   225       sink->server_port = g_value_get_int (value);
       
   226       break;
       
   227 
       
   228     default:
       
   229       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       
   230       break;
       
   231   }
       
   232 }
       
   233 
       
   234 static void
       
   235 gst_tcp_server_sink_get_property (GObject * object, guint prop_id,
       
   236     GValue * value, GParamSpec * pspec)
       
   237 {
       
   238   GstTCPServerSink *sink;
       
   239 
       
   240   g_return_if_fail (GST_IS_TCP_SERVER_SINK (object));
       
   241   sink = GST_TCP_SERVER_SINK (object);
       
   242 
       
   243   switch (prop_id) {
       
   244     case ARG_HOST:
       
   245       g_value_set_string (value, sink->host);
       
   246       break;
       
   247     case ARG_PORT:
       
   248       g_value_set_int (value, sink->server_port);
       
   249       break;
       
   250 
       
   251     default:
       
   252       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       
   253       break;
       
   254   }
       
   255 }
       
   256 
       
   257 
       
   258 /* create a socket for sending to remote machine */
       
   259 static gboolean
       
   260 gst_tcp_server_sink_init_send (GstMultiFdSink * parent)
       
   261 {
       
   262   int ret;
       
   263   GstTCPServerSink *this = GST_TCP_SERVER_SINK (parent);
       
   264 
       
   265   /* create sending server socket */
       
   266   if ((this->server_sock.fd = socket (AF_INET, SOCK_STREAM, 0)) == -1) {
       
   267     GST_ELEMENT_ERROR (this, RESOURCE, OPEN_WRITE, (NULL), GST_ERROR_SYSTEM);
       
   268     return FALSE;
       
   269   }
       
   270   GST_DEBUG_OBJECT (this, "opened sending server socket with fd %d",
       
   271       this->server_sock.fd);
       
   272 
       
   273   /* make address reusable */
       
   274   ret = 1;
       
   275   if (setsockopt (this->server_sock.fd, SOL_SOCKET, SO_REUSEADDR,
       
   276           (void *) &ret, sizeof (ret)) < 0) {
       
   277     gst_tcp_socket_close (&this->server_sock);
       
   278     GST_ELEMENT_ERROR (this, RESOURCE, SETTINGS, (NULL),
       
   279         ("Could not setsockopt: %s", g_strerror (errno)));
       
   280     return FALSE;
       
   281   }
       
   282   /* keep connection alive; avoids SIGPIPE during write */
       
   283   ret = 1;
       
   284   if (setsockopt (this->server_sock.fd, SOL_SOCKET, SO_KEEPALIVE,
       
   285           (void *) &ret, sizeof (ret)) < 0) {
       
   286     gst_tcp_socket_close (&this->server_sock);
       
   287     GST_ELEMENT_ERROR (this, RESOURCE, SETTINGS, (NULL),
       
   288         ("Could not setsockopt: %s", g_strerror (errno)));
       
   289     return FALSE;
       
   290   }
       
   291 
       
   292   /* name the socket */
       
   293   memset (&this->server_sin, 0, sizeof (this->server_sin));
       
   294   this->server_sin.sin_family = AF_INET;        /* network socket */
       
   295   this->server_sin.sin_port = htons (this->server_port);        /* on port */
       
   296   this->server_sin.sin_addr.s_addr = htonl (INADDR_ANY);        /* for hosts */
       
   297 
       
   298   /* bind it */
       
   299   GST_DEBUG_OBJECT (this, "binding server socket to address");
       
   300   ret = bind (this->server_sock.fd, (struct sockaddr *) &this->server_sin,
       
   301       sizeof (this->server_sin));
       
   302 
       
   303   if (ret) {
       
   304     gst_tcp_socket_close (&this->server_sock);
       
   305     switch (errno) {
       
   306       default:
       
   307         GST_ELEMENT_ERROR (this, RESOURCE, OPEN_READ, (NULL),
       
   308             ("bind on port %d failed: %s", this->server_port,
       
   309                 g_strerror (errno)));
       
   310         return FALSE;
       
   311         break;
       
   312     }
       
   313   }
       
   314 
       
   315   /* set the server socket to nonblocking */
       
   316   fcntl (this->server_sock.fd, F_SETFL, O_NONBLOCK);
       
   317 
       
   318   GST_DEBUG_OBJECT (this, "listening on server socket %d with queue of %d",
       
   319       this->server_sock.fd, TCP_BACKLOG);
       
   320   if (listen (this->server_sock.fd, TCP_BACKLOG) == -1) {
       
   321     gst_tcp_socket_close (&this->server_sock);
       
   322     GST_ELEMENT_ERROR (this, RESOURCE, OPEN_READ, (NULL),
       
   323         ("Could not listen on server socket: %s", g_strerror (errno)));
       
   324     return FALSE;
       
   325   }
       
   326   GST_DEBUG_OBJECT (this,
       
   327       "listened on server socket %d, returning from connection setup",
       
   328       this->server_sock.fd);
       
   329 
       
   330   gst_poll_add_fd (parent->fdset, &this->server_sock);
       
   331   gst_poll_fd_ctl_read (parent->fdset, &this->server_sock, TRUE);
       
   332 
       
   333   return TRUE;
       
   334 }
       
   335 
       
   336 static gboolean
       
   337 gst_tcp_server_sink_close (GstMultiFdSink * parent)
       
   338 {
       
   339   GstTCPServerSink *this = GST_TCP_SERVER_SINK (parent);
       
   340 
       
   341   if (this->server_sock.fd != -1) {
       
   342     gst_poll_remove_fd (parent->fdset, &this->server_sock);
       
   343 
       
   344     close (this->server_sock.fd);
       
   345     this->server_sock.fd = -1;
       
   346   }
       
   347   return TRUE;
       
   348 }