mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-28 20:51:13 +00:00
49deb0c05d
Original commit message from CVS: * configure.ac: * ext/alsa/gstalsamixerelement.c: (gst_alsa_mixer_element_class_init): * ext/alsa/gstalsasink.c: (gst_alsasink_class_init): * ext/alsa/gstalsasrc.c: (gst_alsasrc_class_init): * ext/cdparanoia/gstcdparanoiasrc.c: (gst_cd_paranoia_src_class_init): * ext/gio/gstgiosink.c: (gst_gio_sink_class_init): * ext/gio/gstgiosrc.c: (gst_gio_src_class_init): * ext/gio/gstgiostreamsink.c: (gst_gio_stream_sink_class_init): * ext/gio/gstgiostreamsrc.c: (gst_gio_stream_src_class_init): * ext/gnomevfs/gstgnomevfssink.c: (gst_gnome_vfs_sink_class_init): * ext/gnomevfs/gstgnomevfssrc.c: (gst_gnome_vfs_src_class_init): * ext/ogg/gstoggmux.c: (gst_ogg_mux_class_init): * ext/pango/gsttextoverlay.c: (gst_text_overlay_class_init): * ext/pango/gsttextrender.c: (gst_text_render_class_init): * ext/theora/theoradec.c: (gst_theora_dec_class_init): * ext/theora/theoraenc.c: (gst_theora_enc_class_init): * ext/theora/theoraparse.c: (gst_theora_parse_class_init): * ext/vorbis/vorbisenc.c: (gst_vorbis_enc_class_init): * gst-libs/gst/audio/gstaudiofiltertemplate.c: (gst_audio_filter_template_class_init): * gst-libs/gst/audio/gstbaseaudiosink.c: (gst_base_audio_sink_class_init): * gst-libs/gst/audio/gstbaseaudiosrc.c: (gst_base_audio_src_class_init): * gst-libs/gst/cdda/gstcddabasesrc.c: (gst_cdda_base_src_class_init): * gst-libs/gst/interfaces/mixertrack.c: (gst_mixer_track_class_init): * gst-libs/gst/rtp/gstbasertpdepayload.c: (gst_base_rtp_depayload_class_init): * gst-libs/gst/rtp/gstbasertppayload.c: (gst_basertppayload_class_init): * gst/audioconvert/gstaudioconvert.c: (gst_audio_convert_class_init): * gst/audiorate/gstaudiorate.c: (gst_audio_rate_class_init): * gst/audioresample/gstaudioresample.c: (gst_audioresample_class_init): * gst/audiotestsrc/gstaudiotestsrc.c: (gst_audio_test_src_class_init): * gst/gdp/gstgdppay.c: (gst_gdp_pay_class_init): * gst/playback/gstdecodebin2.c: (gst_decode_bin_class_init): * gst/playback/gstplaybasebin.c: (gst_play_base_bin_class_init), (preroll_unlinked): * gst/playback/gstplaybin.c: (gst_play_bin_class_init): * gst/playback/gstplaybin2.c: (gst_play_bin_class_init): * gst/playback/gstplaysink.c: (gst_play_sink_class_init): * gst/playback/gstqueue2.c: (gst_queue_class_init): * gst/playback/gststreaminfo.c: (gst_stream_info_class_init): * gst/playback/gststreamselector.c: (gst_selector_pad_class_init), (gst_stream_selector_class_init): * gst/playback/gsturidecodebin.c: (gst_uri_decode_bin_class_init): * gst/subparse/gstsubparse.c: (gst_sub_parse_class_init): * gst/tcp/gstmultifdsink.c: (gst_multi_fd_sink_class_init): * gst/tcp/gsttcpclientsink.c: (gst_tcp_client_sink_class_init): * gst/tcp/gsttcpclientsrc.c: (gst_tcp_client_src_class_init): * gst/tcp/gsttcpserversink.c: (gst_tcp_server_sink_class_init): * gst/tcp/gsttcpserversrc.c: (gst_tcp_server_src_class_init): * gst/videorate/gstvideorate.c: (gst_video_rate_class_init): * gst/videoscale/gstvideoscale.c: (gst_video_scale_class_init): * gst/videotestsrc/gstvideotestsrc.c: (gst_video_test_src_class_init): * gst/volume/gstvolume.c: (gst_volume_class_init): * sys/v4l/gstv4lelement.c: (gst_v4lelement_class_init): * sys/v4l/gstv4lmjpegsink.c: (gst_v4lmjpegsink_class_init): * sys/v4l/gstv4lmjpegsrc.c: (gst_v4lmjpegsrc_class_init): * sys/v4l/gstv4lsrc.c: (gst_v4lsrc_class_init): * sys/ximage/ximagesink.c: (gst_ximagesink_class_init): * sys/xvimage/xvimagesink.c: (gst_xvimagesink_class_init): Use G_PARAM_STATIC_STRINGS everywhere for GParamSpecs that use static strings (i.e. all). This gives us less memory usage, fewer allocations and thus less memory defragmentation. Depend on core CVS for this. Fixes bug #523806.
449 lines
12 KiB
C
449 lines
12 KiB
C
/* GStreamer
|
|
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
|
|
* Copyright (C) <2004> Thomas Vander Stichele <thomas at apestaart dot org>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
#include <gst/gst-i18n-plugin.h>
|
|
#include <gst/dataprotocol/dataprotocol.h>
|
|
#include "gsttcp.h"
|
|
#include "gsttcpclientsink.h"
|
|
#include <string.h> /* memset */
|
|
|
|
/* elementfactory information */
|
|
static const GstElementDetails gst_tcp_client_sink_details =
|
|
GST_ELEMENT_DETAILS ("TCP client sink",
|
|
"Sink/Network",
|
|
"Send data as a client over the network via TCP",
|
|
"Thomas Vander Stichele <thomas at apestaart dot org>");
|
|
|
|
/* TCPClientSink signals and args */
|
|
enum
|
|
{
|
|
FRAME_ENCODED,
|
|
/* FILL ME */
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
GST_DEBUG_CATEGORY_STATIC (tcpclientsink_debug);
|
|
#define GST_CAT_DEFAULT (tcpclientsink_debug)
|
|
|
|
enum
|
|
{
|
|
ARG_0,
|
|
ARG_HOST,
|
|
ARG_PORT,
|
|
ARG_PROTOCOL
|
|
/* FILL ME */
|
|
};
|
|
|
|
static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
|
|
GST_PAD_SINK,
|
|
GST_PAD_ALWAYS,
|
|
GST_STATIC_CAPS_ANY);
|
|
|
|
static void gst_tcp_client_sink_base_init (gpointer g_class);
|
|
static void gst_tcp_client_sink_class_init (GstTCPClientSink * klass);
|
|
static void gst_tcp_client_sink_init (GstTCPClientSink * tcpclientsink);
|
|
static void gst_tcp_client_sink_finalize (GObject * gobject);
|
|
|
|
static gboolean gst_tcp_client_sink_setcaps (GstBaseSink * bsink,
|
|
GstCaps * caps);
|
|
static GstFlowReturn gst_tcp_client_sink_render (GstBaseSink * bsink,
|
|
GstBuffer * buf);
|
|
static GstStateChangeReturn gst_tcp_client_sink_change_state (GstElement *
|
|
element, GstStateChange transition);
|
|
|
|
static void gst_tcp_client_sink_set_property (GObject * object, guint prop_id,
|
|
const GValue * value, GParamSpec * pspec);
|
|
static void gst_tcp_client_sink_get_property (GObject * object, guint prop_id,
|
|
GValue * value, GParamSpec * pspec);
|
|
|
|
|
|
static GstElementClass *parent_class = NULL;
|
|
|
|
/*static guint gst_tcp_client_sink_signals[LAST_SIGNAL] = { 0 }; */
|
|
|
|
GType
|
|
gst_tcp_client_sink_get_type (void)
|
|
{
|
|
static GType tcpclientsink_type = 0;
|
|
|
|
|
|
if (!tcpclientsink_type) {
|
|
static const GTypeInfo tcpclientsink_info = {
|
|
sizeof (GstTCPClientSinkClass),
|
|
gst_tcp_client_sink_base_init,
|
|
NULL,
|
|
(GClassInitFunc) gst_tcp_client_sink_class_init,
|
|
NULL,
|
|
NULL,
|
|
sizeof (GstTCPClientSink),
|
|
0,
|
|
(GInstanceInitFunc) gst_tcp_client_sink_init,
|
|
NULL
|
|
};
|
|
|
|
tcpclientsink_type =
|
|
g_type_register_static (GST_TYPE_BASE_SINK, "GstTCPClientSink",
|
|
&tcpclientsink_info, 0);
|
|
}
|
|
return tcpclientsink_type;
|
|
}
|
|
|
|
static void
|
|
gst_tcp_client_sink_base_init (gpointer g_class)
|
|
{
|
|
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
|
|
|
|
gst_element_class_add_pad_template (element_class,
|
|
gst_static_pad_template_get (&sinktemplate));
|
|
|
|
gst_element_class_set_details (element_class, &gst_tcp_client_sink_details);
|
|
}
|
|
|
|
static void
|
|
gst_tcp_client_sink_class_init (GstTCPClientSink * klass)
|
|
{
|
|
GObjectClass *gobject_class;
|
|
GstElementClass *gstelement_class;
|
|
GstBaseSinkClass *gstbasesink_class;
|
|
|
|
gobject_class = (GObjectClass *) klass;
|
|
gstelement_class = (GstElementClass *) klass;
|
|
gstbasesink_class = (GstBaseSinkClass *) klass;
|
|
|
|
parent_class = g_type_class_peek_parent (klass);
|
|
|
|
gobject_class->set_property = gst_tcp_client_sink_set_property;
|
|
gobject_class->get_property = gst_tcp_client_sink_get_property;
|
|
gobject_class->finalize = gst_tcp_client_sink_finalize;
|
|
|
|
g_object_class_install_property (gobject_class, ARG_HOST,
|
|
g_param_spec_string ("host", "Host", "The host/IP to send the packets to",
|
|
TCP_DEFAULT_HOST, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
g_object_class_install_property (gobject_class, ARG_PORT,
|
|
g_param_spec_int ("port", "Port", "The port to send the packets to",
|
|
0, TCP_HIGHEST_PORT, TCP_DEFAULT_PORT,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
g_object_class_install_property (gobject_class, ARG_PROTOCOL,
|
|
g_param_spec_enum ("protocol", "Protocol", "The protocol to wrap data in",
|
|
GST_TYPE_TCP_PROTOCOL, GST_TCP_PROTOCOL_NONE,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
gstelement_class->change_state = gst_tcp_client_sink_change_state;
|
|
|
|
gstbasesink_class->set_caps = gst_tcp_client_sink_setcaps;
|
|
gstbasesink_class->render = gst_tcp_client_sink_render;
|
|
|
|
GST_DEBUG_CATEGORY_INIT (tcpclientsink_debug, "tcpclientsink", 0, "TCP sink");
|
|
}
|
|
|
|
static void
|
|
gst_tcp_client_sink_init (GstTCPClientSink * this)
|
|
{
|
|
this->host = g_strdup (TCP_DEFAULT_HOST);
|
|
this->port = TCP_DEFAULT_PORT;
|
|
|
|
this->sock_fd.fd = -1;
|
|
this->protocol = GST_TCP_PROTOCOL_NONE;
|
|
GST_OBJECT_FLAG_UNSET (this, GST_TCP_CLIENT_SINK_OPEN);
|
|
}
|
|
|
|
static void
|
|
gst_tcp_client_sink_finalize (GObject * gobject)
|
|
{
|
|
GstTCPClientSink *this = GST_TCP_CLIENT_SINK (gobject);
|
|
|
|
g_free (this->host);
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (gobject);
|
|
}
|
|
|
|
static gboolean
|
|
gst_tcp_client_sink_setcaps (GstBaseSink * bsink, GstCaps * caps)
|
|
{
|
|
GstTCPClientSink *sink;
|
|
|
|
sink = GST_TCP_CLIENT_SINK (bsink);
|
|
|
|
/* write the buffer header if we have one */
|
|
switch (sink->protocol) {
|
|
case GST_TCP_PROTOCOL_NONE:
|
|
break;
|
|
|
|
case GST_TCP_PROTOCOL_GDP:
|
|
/* if we haven't send caps yet, send them first */
|
|
if (!sink->caps_sent) {
|
|
const GstCaps *caps;
|
|
gchar *string;
|
|
|
|
caps = GST_PAD_CAPS (GST_PAD_PEER (GST_BASE_SINK_PAD (bsink)));
|
|
string = gst_caps_to_string (caps);
|
|
GST_DEBUG_OBJECT (sink, "Sending caps %s through GDP", string);
|
|
g_free (string);
|
|
|
|
if (!gst_tcp_gdp_write_caps (GST_ELEMENT (sink), sink->sock_fd.fd,
|
|
caps, TRUE, sink->host, sink->port))
|
|
goto gdp_write_error;
|
|
|
|
sink->caps_sent = TRUE;
|
|
}
|
|
break;
|
|
default:
|
|
g_warning ("Unhandled protocol type");
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
/* ERRORS */
|
|
gdp_write_error:
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static GstFlowReturn
|
|
gst_tcp_client_sink_render (GstBaseSink * bsink, GstBuffer * buf)
|
|
{
|
|
size_t wrote = 0;
|
|
GstTCPClientSink *sink;
|
|
gint size;
|
|
|
|
sink = GST_TCP_CLIENT_SINK (bsink);
|
|
|
|
g_return_val_if_fail (GST_OBJECT_FLAG_IS_SET (sink, GST_TCP_CLIENT_SINK_OPEN),
|
|
GST_FLOW_WRONG_STATE);
|
|
|
|
size = GST_BUFFER_SIZE (buf);
|
|
|
|
GST_LOG_OBJECT (sink, "writing %d bytes for buffer data", size);
|
|
|
|
/* write the buffer header if we have one */
|
|
switch (sink->protocol) {
|
|
case GST_TCP_PROTOCOL_NONE:
|
|
break;
|
|
case GST_TCP_PROTOCOL_GDP:
|
|
GST_LOG_OBJECT (sink, "Sending buffer header through GDP");
|
|
if (!gst_tcp_gdp_write_buffer (GST_ELEMENT (sink), sink->sock_fd.fd, buf,
|
|
TRUE, sink->host, sink->port))
|
|
goto gdp_write_error;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* write buffer data */
|
|
wrote = gst_tcp_socket_write (sink->sock_fd.fd, GST_BUFFER_DATA (buf), size);
|
|
|
|
if (wrote < size)
|
|
goto write_error;
|
|
|
|
sink->data_written += wrote;
|
|
|
|
return GST_FLOW_OK;
|
|
|
|
/* ERRORS */
|
|
gdp_write_error:
|
|
{
|
|
return FALSE;
|
|
}
|
|
write_error:
|
|
{
|
|
GST_ELEMENT_ERROR (sink, RESOURCE, WRITE,
|
|
(_("Error while sending data to \"%s:%d\"."), sink->host, sink->port),
|
|
("Only %" G_GSIZE_FORMAT " of %u bytes written: %s",
|
|
wrote, GST_BUFFER_SIZE (buf), g_strerror (errno)));
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_tcp_client_sink_set_property (GObject * object, guint prop_id,
|
|
const GValue * value, GParamSpec * pspec)
|
|
{
|
|
GstTCPClientSink *tcpclientsink;
|
|
|
|
g_return_if_fail (GST_IS_TCP_CLIENT_SINK (object));
|
|
tcpclientsink = GST_TCP_CLIENT_SINK (object);
|
|
|
|
switch (prop_id) {
|
|
case ARG_HOST:
|
|
if (!g_value_get_string (value)) {
|
|
g_warning ("host property cannot be NULL");
|
|
break;
|
|
}
|
|
g_free (tcpclientsink->host);
|
|
tcpclientsink->host = g_strdup (g_value_get_string (value));
|
|
break;
|
|
case ARG_PORT:
|
|
tcpclientsink->port = g_value_get_int (value);
|
|
break;
|
|
case ARG_PROTOCOL:
|
|
tcpclientsink->protocol = g_value_get_enum (value);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_tcp_client_sink_get_property (GObject * object, guint prop_id,
|
|
GValue * value, GParamSpec * pspec)
|
|
{
|
|
GstTCPClientSink *tcpclientsink;
|
|
|
|
g_return_if_fail (GST_IS_TCP_CLIENT_SINK (object));
|
|
tcpclientsink = GST_TCP_CLIENT_SINK (object);
|
|
|
|
switch (prop_id) {
|
|
case ARG_HOST:
|
|
g_value_set_string (value, tcpclientsink->host);
|
|
break;
|
|
case ARG_PORT:
|
|
g_value_set_int (value, tcpclientsink->port);
|
|
break;
|
|
case ARG_PROTOCOL:
|
|
g_value_set_enum (value, tcpclientsink->protocol);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/* create a socket for sending to remote machine */
|
|
static gboolean
|
|
gst_tcp_client_sink_start (GstTCPClientSink * this)
|
|
{
|
|
int ret;
|
|
gchar *ip;
|
|
|
|
if (GST_OBJECT_FLAG_IS_SET (this, GST_TCP_CLIENT_SINK_OPEN))
|
|
return TRUE;
|
|
|
|
/* reset caps_sent flag */
|
|
this->caps_sent = FALSE;
|
|
|
|
/* create sending client socket */
|
|
GST_DEBUG_OBJECT (this, "opening sending client socket to %s:%d", this->host,
|
|
this->port);
|
|
if ((this->sock_fd.fd = socket (AF_INET, SOCK_STREAM, 0)) == -1) {
|
|
GST_ELEMENT_ERROR (this, RESOURCE, OPEN_WRITE, (NULL), GST_ERROR_SYSTEM);
|
|
return FALSE;
|
|
}
|
|
GST_DEBUG_OBJECT (this, "opened sending client socket with fd %d",
|
|
this->sock_fd.fd);
|
|
|
|
/* look up name if we need to */
|
|
ip = gst_tcp_host_to_ip (GST_ELEMENT (this), this->host);
|
|
if (!ip) {
|
|
gst_tcp_socket_close (&this->sock_fd);
|
|
return FALSE;
|
|
}
|
|
GST_DEBUG_OBJECT (this, "IP address for host %s is %s", this->host, ip);
|
|
|
|
/* connect to server */
|
|
memset (&this->server_sin, 0, sizeof (this->server_sin));
|
|
this->server_sin.sin_family = AF_INET; /* network socket */
|
|
this->server_sin.sin_port = htons (this->port); /* on port */
|
|
this->server_sin.sin_addr.s_addr = inet_addr (ip); /* on host ip */
|
|
g_free (ip);
|
|
|
|
GST_DEBUG_OBJECT (this, "connecting to server");
|
|
ret = connect (this->sock_fd.fd, (struct sockaddr *) &this->server_sin,
|
|
sizeof (this->server_sin));
|
|
|
|
if (ret) {
|
|
gst_tcp_socket_close (&this->sock_fd);
|
|
switch (errno) {
|
|
case ECONNREFUSED:
|
|
GST_ELEMENT_ERROR (this, RESOURCE, OPEN_WRITE,
|
|
(_("Connection to %s:%d refused."), this->host, this->port),
|
|
(NULL));
|
|
return FALSE;
|
|
break;
|
|
default:
|
|
GST_ELEMENT_ERROR (this, RESOURCE, OPEN_READ, (NULL),
|
|
("connect to %s:%d failed: %s", this->host, this->port,
|
|
g_strerror (errno)));
|
|
return FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
GST_OBJECT_FLAG_SET (this, GST_TCP_CLIENT_SINK_OPEN);
|
|
|
|
this->data_written = 0;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_tcp_client_sink_stop (GstTCPClientSink * this)
|
|
{
|
|
if (!GST_OBJECT_FLAG_IS_SET (this, GST_TCP_CLIENT_SINK_OPEN))
|
|
return TRUE;
|
|
|
|
gst_tcp_socket_close (&this->sock_fd);
|
|
|
|
GST_OBJECT_FLAG_UNSET (this, GST_TCP_CLIENT_SINK_OPEN);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static GstStateChangeReturn
|
|
gst_tcp_client_sink_change_state (GstElement * element,
|
|
GstStateChange transition)
|
|
{
|
|
GstTCPClientSink *sink;
|
|
GstStateChangeReturn res;
|
|
|
|
sink = GST_TCP_CLIENT_SINK (element);
|
|
|
|
switch (transition) {
|
|
case GST_STATE_CHANGE_NULL_TO_READY:
|
|
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
|
if (!gst_tcp_client_sink_start (GST_TCP_CLIENT_SINK (element)))
|
|
goto start_failure;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
res = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
|
|
|
|
switch (transition) {
|
|
case GST_STATE_CHANGE_READY_TO_NULL:
|
|
gst_tcp_client_sink_stop (GST_TCP_CLIENT_SINK (element));
|
|
default:
|
|
break;
|
|
}
|
|
return res;
|
|
|
|
start_failure:
|
|
{
|
|
return GST_STATE_CHANGE_FAILURE;
|
|
}
|
|
}
|