2 * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3 * 2000 Wim Taymans <wtay@chello.be>
4 * 2002 Kristian Rietveld <kris@gtk.org>
5 * 2002,2003 Colin Walters <walters@gnu.org>
6 * 2001,2010 Bastien Nocera <hadess@hadess.net>
7 * 2010 Sebastian Dröge <sebastian.droege@collabora.co.uk>
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Library General Public
13 * License as published by the Free Software Foundation; either
14 * version 2 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Library General Public License for more details.
21 * You should have received a copy of the GNU Library General Public
22 * License along with this library; if not, write to the
23 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 * Boston, MA 02111-1307, USA.
28 * SECTION:element-rtmpsrc
30 * This plugin reads data from a local or remote location specified
31 * by an URI. This location can be specified using any protocol supported by
32 * the RTMP library, i.e. rtmp, rtmpt, rtmps, rtmpe, rtmfp, rtmpte and rtmpts.
35 * <title>Example launch lines</title>
37 * gst-launch -v rtmpsrc location=rtmp://somehost/someurl ! fakesink
38 * ]| Open an RTMP location and pass its content to fakesink.
46 #include <glib/gi18n-lib.h>
48 #include "gstrtmpsrc.h"
60 GST_DEBUG_CATEGORY_STATIC (rtmpsrc_debug);
61 #define GST_CAT_DEFAULT rtmpsrc_debug
63 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
76 static void gst_rtmp_src_uri_handler_init (gpointer g_iface,
79 static void gst_rtmp_src_set_property (GObject * object, guint prop_id,
80 const GValue * value, GParamSpec * pspec);
81 static void gst_rtmp_src_get_property (GObject * object, guint prop_id,
82 GValue * value, GParamSpec * pspec);
83 static void gst_rtmp_src_finalize (GObject * object);
85 static gboolean gst_rtmp_src_stop (GstBaseSrc * src);
86 static gboolean gst_rtmp_src_start (GstBaseSrc * src);
87 static gboolean gst_rtmp_src_is_seekable (GstBaseSrc * src);
88 static gboolean gst_rtmp_src_prepare_seek_segment (GstBaseSrc * src,
89 GstEvent * event, GstSegment * segment);
90 static gboolean gst_rtmp_src_do_seek (GstBaseSrc * src, GstSegment * segment);
91 static GstFlowReturn gst_rtmp_src_create (GstPushSrc * pushsrc,
93 static gboolean gst_rtmp_src_query (GstBaseSrc * src, GstQuery * query);
96 _do_init (GType gtype)
98 static const GInterfaceInfo urihandler_info = {
99 gst_rtmp_src_uri_handler_init,
104 g_type_add_interface_static (gtype, GST_TYPE_URI_HANDLER, &urihandler_info);
106 GST_DEBUG_CATEGORY_INIT (rtmpsrc_debug, "rtmpsrc", 0, "RTMP Source");
109 GST_BOILERPLATE_FULL (GstRTMPSrc, gst_rtmp_src, GstPushSrc, GST_TYPE_PUSH_SRC,
113 gst_rtmp_src_base_init (gpointer g_class)
115 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
117 gst_element_class_add_static_pad_template (element_class, &srctemplate);
119 gst_element_class_set_details_simple (element_class,
123 "Bastien Nocera <hadess@hadess.net>, "
124 "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
128 gst_rtmp_src_class_init (GstRTMPSrcClass * klass)
130 GObjectClass *gobject_class;
131 GstBaseSrcClass *gstbasesrc_class;
132 GstPushSrcClass *gstpushsrc_class;
134 gobject_class = G_OBJECT_CLASS (klass);
135 gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
136 gstpushsrc_class = GST_PUSH_SRC_CLASS (klass);
138 gobject_class->finalize = gst_rtmp_src_finalize;
139 gobject_class->set_property = gst_rtmp_src_set_property;
140 gobject_class->get_property = gst_rtmp_src_get_property;
143 gst_element_class_install_std_props (GST_ELEMENT_CLASS (klass),
144 "location", PROP_LOCATION, G_PARAM_READWRITE, NULL);
146 gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_rtmp_src_start);
147 gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_rtmp_src_stop);
148 gstbasesrc_class->is_seekable = GST_DEBUG_FUNCPTR (gst_rtmp_src_is_seekable);
149 gstbasesrc_class->prepare_seek_segment =
150 GST_DEBUG_FUNCPTR (gst_rtmp_src_prepare_seek_segment);
151 gstbasesrc_class->do_seek = GST_DEBUG_FUNCPTR (gst_rtmp_src_do_seek);
152 gstpushsrc_class->create = GST_DEBUG_FUNCPTR (gst_rtmp_src_create);
153 gstbasesrc_class->query = GST_DEBUG_FUNCPTR (gst_rtmp_src_query);
157 gst_rtmp_src_init (GstRTMPSrc * rtmpsrc, GstRTMPSrcClass * klass)
162 if (WSAStartup (MAKEWORD (2, 2), &wsa_data) != 0) {
163 GST_ERROR_OBJECT (rtmpsrc, "WSAStartup failed: 0x%08x", WSAGetLastError ());
167 rtmpsrc->cur_offset = 0;
168 rtmpsrc->last_timestamp = 0;
170 gst_base_src_set_format (GST_BASE_SRC (rtmpsrc), GST_FORMAT_TIME);
174 gst_rtmp_src_finalize (GObject * object)
176 GstRTMPSrc *rtmpsrc = GST_RTMP_SRC (object);
178 g_free (rtmpsrc->uri);
185 G_OBJECT_CLASS (parent_class)->finalize (object);
189 * URI interface support.
193 gst_rtmp_src_uri_get_type (void)
199 gst_rtmp_src_uri_get_protocols (void)
201 static gchar *protocols[] =
202 { (char *) "rtmp", (char *) "rtmpt", (char *) "rtmps", (char *) "rtmpe",
203 (char *) "rtmfp", (char *) "rtmpte", (char *) "rtmpts", NULL
209 gst_rtmp_src_uri_get_uri (GstURIHandler * handler)
211 GstRTMPSrc *src = GST_RTMP_SRC (handler);
217 gst_rtmp_src_uri_set_uri (GstURIHandler * handler, const gchar * uri)
219 GstRTMPSrc *src = GST_RTMP_SRC (handler);
221 if (GST_STATE (src) >= GST_STATE_PAUSED)
233 if (!RTMP_ParseURL (uri, &protocol, &host, &port, &playpath, &app) ||
234 !host.av_len || !playpath.av_len) {
235 GST_ERROR_OBJECT (src, "Failed to parse URI %s", uri);
238 src->uri = g_strdup (uri);
241 GST_DEBUG_OBJECT (src, "Changed URI to %s", GST_STR_NULL (uri));
247 gst_rtmp_src_uri_handler_init (gpointer g_iface, gpointer iface_data)
249 GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
251 iface->get_type = gst_rtmp_src_uri_get_type;
252 iface->get_protocols = gst_rtmp_src_uri_get_protocols;
253 iface->get_uri = gst_rtmp_src_uri_get_uri;
254 iface->set_uri = gst_rtmp_src_uri_set_uri;
258 gst_rtmp_src_set_property (GObject * object, guint prop_id,
259 const GValue * value, GParamSpec * pspec)
263 src = GST_RTMP_SRC (object);
267 gst_rtmp_src_uri_set_uri (GST_URI_HANDLER (src),
268 g_value_get_string (value));
272 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
278 gst_rtmp_src_get_property (GObject * object, guint prop_id, GValue * value,
283 src = GST_RTMP_SRC (object);
287 g_value_set_string (value, src->uri);
290 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
296 * Read a new buffer from src->reqoffset, takes care of events
297 * and seeking and such.
300 gst_rtmp_src_create (GstPushSrc * pushsrc, GstBuffer ** buffer)
309 src = GST_RTMP_SRC (pushsrc);
311 g_return_val_if_fail (src->rtmp != NULL, GST_FLOW_ERROR);
313 size = GST_BASE_SRC_CAST (pushsrc)->blocksize;
315 GST_DEBUG ("reading from %" G_GUINT64_FORMAT
316 ", size %u", src->cur_offset, size);
318 buf = gst_buffer_try_new_and_alloc (size);
319 if (G_UNLIKELY (buf == NULL)) {
320 GST_ERROR_OBJECT (src, "Failed to allocate %u bytes", size);
321 return GST_FLOW_ERROR;
325 data = GST_BUFFER_DATA (buf);
329 read = RTMP_Read (src->rtmp, (char *) data, todo);
331 if (G_UNLIKELY (read == 0 && todo == size)) {
333 } else if (G_UNLIKELY (read == 0)) {
334 GST_BUFFER_SIZE (buf) -= todo;
339 if (G_UNLIKELY (read < 0))
348 GST_LOG (" got size %d", read);
352 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
353 src->discont = FALSE;
356 GST_BUFFER_TIMESTAMP (buf) = src->last_timestamp;
357 GST_BUFFER_OFFSET (buf) = src->cur_offset;
359 src->cur_offset += size;
360 if (src->last_timestamp == GST_CLOCK_TIME_NONE)
361 src->last_timestamp = src->rtmp->m_mediaStamp * GST_MSECOND;
363 src->last_timestamp =
364 MAX (src->last_timestamp, src->rtmp->m_mediaStamp * GST_MSECOND);
366 GST_LOG_OBJECT (src, "Created buffer of size %u at %" G_GINT64_FORMAT
367 " with timestamp %" GST_TIME_FORMAT, size, GST_BUFFER_OFFSET (buf),
368 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
371 /* we're done, return the buffer */
378 gst_buffer_unref (buf);
379 GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), ("Failed to read data"));
380 return GST_FLOW_ERROR;
384 gst_buffer_unref (buf);
385 GST_DEBUG_OBJECT (src, "Reading data gave EOS");
386 return GST_FLOW_UNEXPECTED;
391 gst_rtmp_src_query (GstBaseSrc * basesrc, GstQuery * query)
393 gboolean ret = FALSE;
394 GstRTMPSrc *src = GST_RTMP_SRC (basesrc);
396 switch (GST_QUERY_TYPE (query)) {
398 gst_query_set_uri (query, src->uri);
401 case GST_QUERY_POSITION:{
404 gst_query_parse_position (query, &format, NULL);
405 if (format == GST_FORMAT_TIME) {
406 gst_query_set_duration (query, format, src->last_timestamp);
411 case GST_QUERY_DURATION:{
415 gst_query_parse_duration (query, &format, NULL);
416 if (format == GST_FORMAT_TIME && src->rtmp) {
417 duration = RTMP_GetDuration (src->rtmp);
418 if (duration != 0.0) {
419 gst_query_set_duration (query, format, duration * GST_SECOND);
431 ret = GST_BASE_SRC_CLASS (parent_class)->query (basesrc, query);
437 gst_rtmp_src_is_seekable (GstBaseSrc * basesrc)
441 src = GST_RTMP_SRC (basesrc);
443 return src->seekable;
447 gst_rtmp_src_prepare_seek_segment (GstBaseSrc * basesrc, GstEvent * event,
448 GstSegment * segment)
451 GstSeekType cur_type, stop_type;
457 src = GST_RTMP_SRC (basesrc);
459 gst_event_parse_seek (event, &rate, &format, &flags,
460 &cur_type, &cur, &stop_type, &stop);
462 if (!src->seekable) {
463 GST_LOG_OBJECT (src, "Not a seekable stream");
468 GST_LOG_OBJECT (src, "Not connected yet");
472 if (format != GST_FORMAT_TIME) {
473 GST_LOG_OBJECT (src, "Seeking only supported in TIME format");
477 if (stop_type != GST_SEEK_TYPE_NONE) {
478 GST_LOG_OBJECT (src, "Setting a stop position is not supported");
482 gst_segment_init (segment, GST_FORMAT_TIME);
483 gst_segment_set_seek (segment, rate, format, flags, cur_type, cur, stop_type,
490 gst_rtmp_src_do_seek (GstBaseSrc * basesrc, GstSegment * segment)
494 src = GST_RTMP_SRC (basesrc);
496 if (segment->format != GST_FORMAT_TIME) {
497 GST_LOG_OBJECT (src, "Only time based seeks are supported");
501 if (!src->seekable) {
502 GST_LOG_OBJECT (src, "Not a seekable stream");
507 GST_LOG_OBJECT (src, "Not connected yet");
514 if (src->cur_offset == 0 && segment->start == 0)
517 src->last_timestamp = GST_CLOCK_TIME_NONE;
518 if (!RTMP_SendSeek (src->rtmp, segment->start / GST_MSECOND)) {
519 GST_ERROR_OBJECT (src, "Seeking failed");
520 src->seekable = FALSE;
524 GST_DEBUG_OBJECT (src, "Seek to %" GST_TIME_FORMAT " successfull",
525 GST_TIME_ARGS (segment->start));
530 #define STR2AVAL(av,str) G_STMT_START { \
532 av.av_len = strlen(av.av_val); \
535 /* open the file, do stuff necessary to go to PAUSED state */
537 gst_rtmp_src_start (GstBaseSrc * basesrc)
542 src = GST_RTMP_SRC (basesrc);
545 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), ("No filename given"));
550 src->last_timestamp = 0;
551 src->seekable = TRUE;
554 uri_copy = g_strdup (src->uri);
555 src->rtmp = RTMP_Alloc ();
556 RTMP_Init (src->rtmp);
557 if (!RTMP_SetupURL (src->rtmp, uri_copy)) {
558 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL),
559 ("Failed to setup URL '%s'", src->uri));
561 RTMP_Free (src->rtmp);
566 /* open if required */
567 if (!RTMP_IsConnected (src->rtmp)) {
568 if (!RTMP_Connect (src->rtmp, NULL)) {
569 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL),
570 ("Could not connect to RTMP stream \"%s\" for reading", src->uri));
571 RTMP_Free (src->rtmp);
583 gst_rtmp_src_stop (GstBaseSrc * basesrc)
587 src = GST_RTMP_SRC (basesrc);
590 RTMP_Close (src->rtmp);
591 RTMP_Free (src->rtmp);
596 src->last_timestamp = 0;