static gboolean gst_rtmp_sink_uri_set_uri (GstURIHandler * handler, const gchar * uri) { GstRTMPSink *sink = GST_RTMP_SINK (handler); if (GST_STATE (sink) >= GST_STATE_PAUSED) return FALSE; g_free (sink->uri); sink->uri = NULL; if (uri != NULL) { int protocol; AVal host; unsigned int port; AVal playpath, app; if (!RTMP_ParseURL (uri, &protocol, &host, &port, &playpath, &app) || !host.av_len || !playpath.av_len) { GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE, ("Failed to parse URI %s", uri), (NULL)); return FALSE; } sink->uri = g_strdup (uri); } GST_DEBUG_OBJECT (sink, "Changed URI to %s", GST_STR_NULL (uri)); return TRUE; }
static gboolean gst_rtmp_sink_start (GstBaseSink * basesink) { GstRTMPSink *sink = GST_RTMP_SINK (basesink); if (!sink->uri) { GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE, ("Please set URI for RTMP output"), ("No URI set before starting")); return FALSE; } sink->rtmp_uri = g_strdup (sink->uri); sink->rtmp = RTMP_Alloc (); RTMP_Init (sink->rtmp); if (!RTMP_SetupURL (sink->rtmp, sink->rtmp_uri)) { GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE, (NULL), ("Failed to setup URL '%s'", sink->uri)); RTMP_Free (sink->rtmp); sink->rtmp = NULL; g_free (sink->rtmp_uri); sink->rtmp_uri = NULL; return FALSE; } GST_DEBUG_OBJECT (sink, "Created RTMP object"); /* Mark this as an output connection */ RTMP_EnableWrite (sink->rtmp); sink->first = TRUE; return TRUE; }
static const gchar * gst_rtmp_sink_uri_get_uri (GstURIHandler * handler) { GstRTMPSink *sink = GST_RTMP_SINK (handler); return sink->uri; }
static gchar * gst_rtmp_sink_uri_get_uri (GstURIHandler * handler) { GstRTMPSink *sink = GST_RTMP_SINK (handler); /* FIXME: make thread-safe */ return g_strdup (sink->uri); }
static GstFlowReturn gst_rtmp_sink_render (GstBaseSink * bsink, GstBuffer * buf) { GstRTMPSink *sink = GST_RTMP_SINK (bsink); GstBuffer *reffed_buf = NULL; if (sink->first) { /* open the connection */ if (!RTMP_IsConnected (sink->rtmp)) { if (!RTMP_Connect (sink->rtmp, NULL) || !RTMP_ConnectStream (sink->rtmp, 0)) { GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE, (NULL), ("Could not connect to RTMP stream \"%s\" for writing", sink->uri)); RTMP_Free (sink->rtmp); sink->rtmp = NULL; g_free (sink->rtmp_uri); sink->rtmp_uri = NULL; return GST_FLOW_ERROR; } GST_DEBUG_OBJECT (sink, "Opened connection to %s", sink->rtmp_uri); } /* FIXME: Parse the first buffer and see if it contains a header plus a packet instead * of just assuming it's only the header */ GST_LOG_OBJECT (sink, "Caching first buffer of size %d for concatenation", GST_BUFFER_SIZE (buf)); gst_buffer_replace (&sink->cache, buf); sink->first = FALSE; return GST_FLOW_OK; } if (sink->cache) { GST_LOG_OBJECT (sink, "Joining 2nd buffer of size %d to cached buf", GST_BUFFER_SIZE (buf)); gst_buffer_ref (buf); reffed_buf = buf = gst_buffer_join (sink->cache, buf); sink->cache = NULL; } GST_LOG_OBJECT (sink, "Sending %d bytes to RTMP server", GST_BUFFER_SIZE (buf)); if (!RTMP_Write (sink->rtmp, (char *) GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf))) { GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, (NULL), ("Failed to write data")); if (reffed_buf) gst_buffer_unref (reffed_buf); return GST_FLOW_ERROR; } if (reffed_buf) gst_buffer_unref (reffed_buf); return GST_FLOW_OK; }
static void gst_rtmp_sink_finalize (GObject * object) { GstRTMPSink *sink = GST_RTMP_SINK (object); #ifdef G_OS_WIN32 WSACleanup (); #endif g_free (sink->uri); G_OBJECT_CLASS (parent_class)->finalize (object); }
static void gst_rtmp_sink_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstRTMPSink *sink = GST_RTMP_SINK (object); switch (prop_id) { case PROP_LOCATION: g_value_set_string (value, sink->uri); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static gboolean gst_rtmp_sink_event (GstBaseSink * sink, GstEvent * event) { GstRTMPSink *rtmpsink = GST_RTMP_SINK (sink); switch (event->type) { case GST_EVENT_FLUSH_STOP: rtmpsink->have_write_error = FALSE; break; default: break; } return GST_BASE_SINK_CLASS (parent_class)->event (sink, event); }
static void gst_rtmp_sink_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstRTMPSink *sink = GST_RTMP_SINK (object); switch (prop_id) { case PROP_LOCATION: gst_rtmp_sink_uri_set_uri (GST_URI_HANDLER (sink), g_value_get_string (value), NULL); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static gboolean gst_rtmp_sink_uri_set_uri (GstURIHandler * handler, const gchar * uri, GError ** error) { GstRTMPSink *sink = GST_RTMP_SINK (handler); gboolean ret = TRUE; if (GST_STATE (sink) >= GST_STATE_PAUSED) { g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_STATE, "Changing the URI on rtmpsink when it is running is not supported"); return FALSE; } g_free (sink->uri); sink->uri = NULL; if (uri != NULL) { int protocol; AVal host; unsigned int port; AVal playpath, app; if (!RTMP_ParseURL (uri, &protocol, &host, &port, &playpath, &app) || !host.av_len) { GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE, ("Failed to parse URI %s", uri), (NULL)); g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI, "Could not parse RTMP URI"); ret = FALSE; } else { sink->uri = g_strdup (uri); } if (playpath.av_val) free (playpath.av_val); } if (ret) { sink->have_write_error = FALSE; GST_DEBUG_OBJECT (sink, "Changed URI to %s", GST_STR_NULL (uri)); } return ret; }
static gboolean gst_rtmp_sink_stop (GstBaseSink * basesink) { GstRTMPSink *sink = GST_RTMP_SINK (basesink); gst_buffer_replace (&sink->cache, NULL); if (sink->rtmp) { RTMP_Close (sink->rtmp); RTMP_Free (sink->rtmp); sink->rtmp = NULL; } if (sink->rtmp_uri) { g_free (sink->rtmp_uri); sink->rtmp_uri = NULL; } return TRUE; }
static GstFlowReturn gst_rtmp_sink_render (GstBaseSink * bsink, GstBuffer * buf) { GstRTMPSink *sink = GST_RTMP_SINK (bsink); GstBuffer *reffed_buf = NULL; if (sink->first) { /* FIXME: Parse the first buffer and see if it contains a header plus a packet instead * of just assuming it's only the header */ GST_LOG_OBJECT (sink, "Caching first buffer of size %d for concatenation", GST_BUFFER_SIZE (buf)); gst_buffer_replace (&sink->cache, buf); sink->first = FALSE; return GST_FLOW_OK; } if (sink->cache) { GST_LOG_OBJECT (sink, "Joining 2nd buffer of size %d to cached buf", GST_BUFFER_SIZE (buf)); gst_buffer_ref (buf); reffed_buf = buf = gst_buffer_join (sink->cache, buf); sink->cache = NULL; } GST_LOG_OBJECT (sink, "Sending %d bytes to RTMP server", GST_BUFFER_SIZE (buf)); if (!RTMP_Write (sink->rtmp, (char *) GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf))) { GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, (NULL), ("Failed to write data")); if (reffed_buf) gst_buffer_unref (reffed_buf); return GST_FLOW_ERROR; } if (reffed_buf) gst_buffer_unref (reffed_buf); return GST_FLOW_OK; }
static GstFlowReturn gst_rtmp_sink_render (GstBaseSink * bsink, GstBuffer * buf) { GstRTMPSink *sink = GST_RTMP_SINK (bsink); GstBuffer *reffed_buf = NULL; GstMapInfo map; if (sink->first) { /* open the connection */ if (!RTMP_IsConnected (sink->rtmp)) { if (!RTMP_Connect (sink->rtmp, NULL) || !RTMP_ConnectStream (sink->rtmp, 0)) { GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE, (NULL), ("Could not connect to RTMP stream \"%s\" for writing", sink->uri)); RTMP_Free (sink->rtmp); sink->rtmp = NULL; g_free (sink->rtmp_uri); sink->rtmp_uri = NULL; return GST_FLOW_ERROR; } GST_DEBUG_OBJECT (sink, "Opened connection to %s", sink->rtmp_uri); } /* FIXME: Parse the first buffer and see if it contains a header plus a packet instead * of just assuming it's only the header */ GST_LOG_OBJECT (sink, "Caching first buffer of size %" G_GSIZE_FORMAT " for concatenation", gst_buffer_get_size (buf)); gst_buffer_replace (&sink->cache, buf); sink->first = FALSE; return GST_FLOW_OK; } if (sink->cache) { GST_LOG_OBJECT (sink, "Joining 2nd buffer of size %" G_GSIZE_FORMAT " to cached buf", gst_buffer_get_size (buf)); gst_buffer_ref (buf); reffed_buf = buf = gst_buffer_append (sink->cache, buf); sink->cache = NULL; } GST_LOG_OBJECT (sink, "Sending %" G_GSIZE_FORMAT " bytes to RTMP server", gst_buffer_get_size (buf)); gst_buffer_map (buf, &map, GST_MAP_READ); if (RTMP_Write (sink->rtmp, (char *) map.data, map.size) <= 0) goto write_failed; gst_buffer_unmap (buf, &map); if (reffed_buf) gst_buffer_unref (reffed_buf); return GST_FLOW_OK; /* ERRORS */ write_failed: { GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, (NULL), ("Failed to write data")); gst_buffer_unmap (buf, &map); if (reffed_buf) gst_buffer_unref (reffed_buf); return GST_FLOW_ERROR; } }