static gchar * gst_rtmp_src_uri_get_uri (GstURIHandler * handler) { GstRTMPSrc *src = GST_RTMP_SRC (handler); /* FIXME: make thread-safe */ return g_strdup (src->uri); }
static gboolean gst_rtmp_src_is_seekable (GstBaseSrc * basesrc) { GstRTMPSrc *src; src = GST_RTMP_SRC (basesrc); return src->seekable; }
static void gst_rtmp_src_finalize (GObject * object) { GstRTMPSrc *rtmpsrc = GST_RTMP_SRC (object); g_free (rtmpsrc->uri); rtmpsrc->uri = NULL; G_OBJECT_CLASS (parent_class)->finalize (object); }
static gboolean gst_rtmp_src_query (GstBaseSrc * basesrc, GstQuery * query) { gboolean ret = FALSE; GstRTMPSrc *src = GST_RTMP_SRC (basesrc); switch (GST_QUERY_TYPE (query)) { case GST_QUERY_URI: gst_query_set_uri (query, src->uri); ret = TRUE; break; case GST_QUERY_POSITION:{ GstFormat format; gst_query_parse_position (query, &format, NULL); if (format == GST_FORMAT_TIME) { gst_query_set_position (query, format, src->last_timestamp); ret = TRUE; } break; } case GST_QUERY_DURATION:{ GstFormat format; gdouble duration; gst_query_parse_duration (query, &format, NULL); if (format == GST_FORMAT_TIME && src->rtmp) { duration = RTMP_GetDuration (src->rtmp); if (duration != 0.0) { gst_query_set_duration (query, format, duration * GST_SECOND); ret = TRUE; } } break; } case GST_QUERY_SCHEDULING:{ gst_query_set_scheduling (query, GST_SCHEDULING_FLAG_SEQUENTIAL | GST_SCHEDULING_FLAG_BANDWIDTH_LIMITED, 1, -1, 0); gst_query_add_scheduling_mode (query, GST_PAD_MODE_PUSH); ret = TRUE; break; } default: ret = FALSE; break; } if (!ret) ret = GST_BASE_SRC_CLASS (parent_class)->query (basesrc, query); return ret; }
static void gst_rtmp_src_finalize (GObject * object) { GstRTMPSrc *rtmpsrc = GST_RTMP_SRC (object); g_free (rtmpsrc->uri); rtmpsrc->uri = NULL; #ifdef G_OS_WIN32 WSACleanup (); #endif G_OBJECT_CLASS (parent_class)->finalize (object); }
/* open the file, do stuff necessary to go to PAUSED state */ static gboolean gst_rtmp_src_start (GstBaseSrc * basesrc) { GstRTMPSrc *src; src = GST_RTMP_SRC (basesrc); if (!src->uri) { GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), ("No filename given")); return FALSE; } src->cur_offset = 0; src->last_timestamp = 0; src->discont = TRUE; src->rtmp = RTMP_Alloc (); if (!src->rtmp) { GST_ERROR_OBJECT (src, "Could not allocate librtmp's RTMP context"); goto error; } RTMP_Init (src->rtmp); if (!RTMP_SetupURL (src->rtmp, src->uri)) { GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), ("Failed to setup URL '%s'", src->uri)); goto error; } src->seekable = !(src->rtmp->Link.lFlags & RTMP_LF_LIVE); GST_INFO_OBJECT (src, "seekable %d", src->seekable); /* open if required */ if (!RTMP_IsConnected (src->rtmp)) { if (!RTMP_Connect (src->rtmp, NULL)) { GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), ("Could not connect to RTMP stream \"%s\" for reading", src->uri)); goto error; } } return TRUE; error: if (src->rtmp) { RTMP_Free (src->rtmp); src->rtmp = NULL; } return FALSE; }
static gboolean gst_rtmp_src_unlock (GstBaseSrc * basesrc) { GstRTMPSrc *rtmpsrc = GST_RTMP_SRC (basesrc); GST_DEBUG_OBJECT (rtmpsrc, "unlock"); /* This closes the socket, which means that any pending socket calls * error out. */ if (rtmpsrc->rtmp) { RTMP_Close (rtmpsrc->rtmp); } return TRUE; }
static void gst_rtmp_src_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstRTMPSrc *src; src = GST_RTMP_SRC (object); switch (prop_id) { case PROP_LOCATION: g_value_set_string (value, src->uri); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static gboolean gst_rtmp_src_query (GstBaseSrc * basesrc, GstQuery * query) { gboolean ret = FALSE; GstRTMPSrc *src = GST_RTMP_SRC (basesrc); switch (GST_QUERY_TYPE (query)) { case GST_QUERY_URI: gst_query_set_uri (query, src->uri); ret = TRUE; break; case GST_QUERY_POSITION:{ GstFormat format; gst_query_parse_position (query, &format, NULL); if (format == GST_FORMAT_TIME) { gst_query_set_duration (query, format, src->last_timestamp); ret = TRUE; } break; } case GST_QUERY_DURATION:{ GstFormat format; gdouble duration; gst_query_parse_duration (query, &format, NULL); if (format == GST_FORMAT_TIME && src->rtmp) { duration = RTMP_GetDuration (src->rtmp); if (duration != 0.0) { gst_query_set_duration (query, format, duration * GST_SECOND); ret = TRUE; } } break; } default: ret = FALSE; break; } if (!ret) ret = GST_BASE_SRC_CLASS (parent_class)->query (basesrc, query); return ret; }
static gboolean gst_rtmp_src_stop (GstBaseSrc * basesrc) { GstRTMPSrc *src; src = GST_RTMP_SRC (basesrc); if (src->rtmp) { RTMP_Free (src->rtmp); src->rtmp = NULL; } src->cur_offset = 0; src->last_timestamp = 0; src->discont = TRUE; return TRUE; }
static void gst_rtmp_src_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstRTMPSrc *src; src = GST_RTMP_SRC (object); switch (prop_id) { case PROP_LOCATION:{ gst_rtmp_src_uri_set_uri (GST_URI_HANDLER (src), g_value_get_string (value), NULL); break; } default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
/* open the file, do stuff necessary to go to PAUSED state */ static gboolean gst_rtmp_src_start (GstBaseSrc * basesrc) { GstRTMPSrc *src; gchar *uri_copy; src = GST_RTMP_SRC (basesrc); if (!src->uri) { GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), ("No filename given")); return FALSE; } src->cur_offset = 0; src->last_timestamp = 0; src->seekable = TRUE; src->discont = TRUE; uri_copy = g_strdup (src->uri); src->rtmp = RTMP_Alloc (); RTMP_Init (src->rtmp); if (!RTMP_SetupURL (src->rtmp, uri_copy)) { GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), ("Failed to setup URL '%s'", src->uri)); g_free (uri_copy); RTMP_Free (src->rtmp); src->rtmp = NULL; return FALSE; } /* open if required */ if (!RTMP_IsConnected (src->rtmp)) { if (!RTMP_Connect (src->rtmp, NULL)) { GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), ("Could not connect to RTMP stream \"%s\" for reading", src->uri)); RTMP_Free (src->rtmp); src->rtmp = NULL; return FALSE; } } return TRUE; }
static gboolean gst_rtmp_src_prepare_seek_segment (GstBaseSrc * basesrc, GstEvent * event, GstSegment * segment) { GstRTMPSrc *src; GstSeekType cur_type, stop_type; gint64 cur, stop; GstSeekFlags flags; GstFormat format; gdouble rate; src = GST_RTMP_SRC (basesrc); gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur, &stop_type, &stop); if (!src->seekable) { GST_LOG_OBJECT (src, "Not a seekable stream"); return FALSE; } if (!src->rtmp) { GST_LOG_OBJECT (src, "Not connected yet"); return FALSE; } if (format != GST_FORMAT_TIME) { GST_LOG_OBJECT (src, "Seeking only supported in TIME format"); return FALSE; } if (stop_type != GST_SEEK_TYPE_NONE) { GST_LOG_OBJECT (src, "Setting a stop position is not supported"); return FALSE; } gst_segment_init (segment, GST_FORMAT_TIME); gst_segment_do_seek (segment, rate, format, flags, cur_type, cur, stop_type, stop, NULL); return TRUE; }
static gboolean gst_rtmp_src_do_seek (GstBaseSrc * basesrc, GstSegment * segment) { GstRTMPSrc *src; src = GST_RTMP_SRC (basesrc); if (segment->format != GST_FORMAT_TIME) { GST_LOG_OBJECT (src, "Only time based seeks are supported"); return FALSE; } if (!src->rtmp) { GST_LOG_OBJECT (src, "Not connected yet"); return FALSE; } src->discont = TRUE; /* Initial seek */ if (src->cur_offset == 0 && segment->start == 0) return TRUE; if (!src->seekable) { GST_LOG_OBJECT (src, "Not a seekable stream"); return FALSE; } src->last_timestamp = GST_CLOCK_TIME_NONE; if (!RTMP_SendSeek (src->rtmp, segment->start / GST_MSECOND)) { GST_ERROR_OBJECT (src, "Seeking failed"); src->seekable = FALSE; return FALSE; } GST_DEBUG_OBJECT (src, "Seek to %" GST_TIME_FORMAT " successfull", GST_TIME_ARGS (segment->start)); return TRUE; }
static gboolean gst_rtmp_src_uri_set_uri (GstURIHandler * handler, const gchar * uri, GError ** error) { GstRTMPSrc *src = GST_RTMP_SRC (handler); if (GST_STATE (src) >= GST_STATE_PAUSED) { g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_STATE, "Changing the URI on rtmpsrc when it is running is not supported"); return FALSE; } g_free (src->uri); src->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_ERROR_OBJECT (src, "Failed to parse URI %s", uri); g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI, "Could not parse RTMP URI"); /* FIXME: we should not be freeing RTMP internals to avoid leaking */ free (playpath.av_val); return FALSE; } free (playpath.av_val); src->uri = g_strdup (uri); } GST_DEBUG_OBJECT (src, "Changed URI to %s", GST_STR_NULL (uri)); return TRUE; }
/* * Read a new buffer from src->reqoffset, takes care of events * and seeking and such. */ static GstFlowReturn gst_rtmp_src_create (GstPushSrc * pushsrc, GstBuffer ** buffer) { GstRTMPSrc *src; GstBuffer *buf; GstMapInfo map; guint8 *data; guint todo; gsize bsize; int read; int size; src = GST_RTMP_SRC (pushsrc); g_return_val_if_fail (src->rtmp != NULL, GST_FLOW_ERROR); size = GST_BASE_SRC_CAST (pushsrc)->blocksize; GST_DEBUG ("reading from %" G_GUINT64_FORMAT ", size %u", src->cur_offset, size); buf = gst_buffer_new_allocate (NULL, size, NULL); if (G_UNLIKELY (buf == NULL)) { GST_ERROR_OBJECT (src, "Failed to allocate %u bytes", size); return GST_FLOW_ERROR; } bsize = todo = size; gst_buffer_map (buf, &map, GST_MAP_WRITE); data = map.data; read = bsize = 0; while (todo > 0) { read = RTMP_Read (src->rtmp, (char *) data, todo); if (G_UNLIKELY (read == 0 && todo == size)) { goto eos; } else if (G_UNLIKELY (read == 0)) { todo = 0; break; } if (G_UNLIKELY (read < 0)) goto read_failed; if (read < todo) { data += read; todo -= read; bsize += read; } else { bsize += todo; todo = 0; } GST_LOG (" got size %d", read); } gst_buffer_unmap (buf, &map); gst_buffer_resize (buf, 0, bsize); if (src->discont) { GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); src->discont = FALSE; } GST_BUFFER_TIMESTAMP (buf) = src->last_timestamp; GST_BUFFER_OFFSET (buf) = src->cur_offset; src->cur_offset += size; if (src->last_timestamp == GST_CLOCK_TIME_NONE) src->last_timestamp = src->rtmp->m_mediaStamp * GST_MSECOND; else src->last_timestamp = MAX (src->last_timestamp, src->rtmp->m_mediaStamp * GST_MSECOND); GST_LOG_OBJECT (src, "Created buffer of size %u at %" G_GINT64_FORMAT " with timestamp %" GST_TIME_FORMAT, size, GST_BUFFER_OFFSET (buf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf))); /* we're done, return the buffer */ *buffer = buf; return GST_FLOW_OK; read_failed: { gst_buffer_unref (buf); GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), ("Failed to read data")); return GST_FLOW_ERROR; } eos: { gst_buffer_unref (buf); GST_DEBUG_OBJECT (src, "Reading data gave EOS"); return GST_FLOW_EOS; } }