• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
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>
8  *
9  * rtmpsrc.c:
10  *
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.
15  *
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.
20  *
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., 51 Franklin St, Fifth Floor,
24  * Boston, MA 02110-1301, USA.
25  */
26 
27 /**
28  * SECTION:element-rtmpsrc
29  * @title: rtmpsrc
30  *
31  * This plugin reads data from a local or remote location specified
32  * by an URI. This location can be specified using any protocol supported by
33  * the RTMP library, i.e. rtmp, rtmpt, rtmps, rtmpe, rtmfp, rtmpte and rtmpts.
34  * The URL/location can contain extra connection or session parameters
35  * for librtmp, such as 'flashver=version'. See the librtmp documentation
36  * for more detail. Of particular interest can be setting `live=1` to certain
37  * RTMP streams that don't seem to be playing otherwise.
38 
39  *
40  * ## Example launch lines
41  * |[
42  * gst-launch-1.0 -v rtmpsrc location=rtmp://somehost/someurl ! fakesink
43  * ]| Open an RTMP location and pass its content to fakesink.
44  *
45  * |[
46  * gst-launch-1.0 rtmpsrc location="rtmp://somehost/someurl live=1" ! fakesink
47  * ]| Open an RTMP location and pass its content to fakesink while passing the
48  * live=1 flag to librtmp
49  *
50  */
51 
52 #ifdef HAVE_CONFIG_H
53 #include "config.h"
54 #endif
55 
56 #include <gst/gst-i18n-plugin.h>
57 
58 #include "gstrtmpelements.h"
59 #include "gstrtmpsrc.h"
60 
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <string.h>
64 
65 #include <gst/gst.h>
66 
67 #ifdef G_OS_WIN32
68 #include <winsock2.h>
69 #endif
70 
71 GST_DEBUG_CATEGORY_STATIC (rtmpsrc_debug);
72 #define GST_CAT_DEFAULT rtmpsrc_debug
73 
74 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
75     GST_PAD_SRC,
76     GST_PAD_ALWAYS,
77     GST_STATIC_CAPS_ANY);
78 
79 enum
80 {
81   PROP_0,
82   PROP_LOCATION,
83   PROP_TIMEOUT
84 #if 0
85       PROP_SWF_URL,
86   PROP_PAGE_URL
87 #endif
88 };
89 
90 #define DEFAULT_LOCATION NULL
91 #define DEFAULT_TIMEOUT 120
92 
93 static void gst_rtmp_src_uri_handler_init (gpointer g_iface,
94     gpointer iface_data);
95 
96 static void gst_rtmp_src_set_property (GObject * object, guint prop_id,
97     const GValue * value, GParamSpec * pspec);
98 static void gst_rtmp_src_get_property (GObject * object, guint prop_id,
99     GValue * value, GParamSpec * pspec);
100 static void gst_rtmp_src_finalize (GObject * object);
101 
102 static gboolean gst_rtmp_src_connect (GstRTMPSrc * src);
103 static gboolean gst_rtmp_src_unlock (GstBaseSrc * src);
104 static gboolean gst_rtmp_src_stop (GstBaseSrc * src);
105 static gboolean gst_rtmp_src_start (GstBaseSrc * src);
106 static gboolean gst_rtmp_src_is_seekable (GstBaseSrc * src);
107 static gboolean gst_rtmp_src_prepare_seek_segment (GstBaseSrc * src,
108     GstEvent * event, GstSegment * segment);
109 static gboolean gst_rtmp_src_do_seek (GstBaseSrc * src, GstSegment * segment);
110 static GstFlowReturn gst_rtmp_src_create (GstPushSrc * pushsrc,
111     GstBuffer ** buffer);
112 static gboolean gst_rtmp_src_query (GstBaseSrc * src, GstQuery * query);
113 
114 #define gst_rtmp_src_parent_class parent_class
115 G_DEFINE_TYPE_WITH_CODE (GstRTMPSrc, gst_rtmp_src, GST_TYPE_PUSH_SRC,
116     G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER,
117         gst_rtmp_src_uri_handler_init));
118 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (rtmpsrc, "rtmpsrc", GST_RANK_PRIMARY,
119     GST_TYPE_RTMP_SRC, rtmp_element_init (plugin));
120 
121 static void
gst_rtmp_src_class_init(GstRTMPSrcClass * klass)122 gst_rtmp_src_class_init (GstRTMPSrcClass * klass)
123 {
124   GObjectClass *gobject_class;
125   GstElementClass *gstelement_class;
126   GstBaseSrcClass *gstbasesrc_class;
127   GstPushSrcClass *gstpushsrc_class;
128 
129   gobject_class = G_OBJECT_CLASS (klass);
130   gstelement_class = GST_ELEMENT_CLASS (klass);
131   gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
132   gstpushsrc_class = GST_PUSH_SRC_CLASS (klass);
133 
134   gobject_class->finalize = gst_rtmp_src_finalize;
135   gobject_class->set_property = gst_rtmp_src_set_property;
136   gobject_class->get_property = gst_rtmp_src_get_property;
137 
138   /* properties */
139   g_object_class_install_property (gobject_class, PROP_LOCATION,
140       g_param_spec_string ("location", "RTMP Location",
141           "Location of the RTMP url to read",
142           DEFAULT_LOCATION, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
143 
144   g_object_class_install_property (gobject_class, PROP_TIMEOUT,
145       g_param_spec_int ("timeout", "RTMP Timeout",
146           "Time without receiving any data from the server to wait before to timeout the session",
147           0, G_MAXINT,
148           DEFAULT_TIMEOUT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
149 
150   gst_element_class_add_static_pad_template (gstelement_class, &srctemplate);
151 
152   gst_element_class_set_static_metadata (gstelement_class,
153       "RTMP Source",
154       "Source/File",
155       "Read RTMP streams",
156       "Bastien Nocera <hadess@hadess.net>, "
157       "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
158 
159   gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_rtmp_src_start);
160   gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_rtmp_src_stop);
161   gstbasesrc_class->unlock = GST_DEBUG_FUNCPTR (gst_rtmp_src_unlock);
162   gstbasesrc_class->is_seekable = GST_DEBUG_FUNCPTR (gst_rtmp_src_is_seekable);
163   gstbasesrc_class->prepare_seek_segment =
164       GST_DEBUG_FUNCPTR (gst_rtmp_src_prepare_seek_segment);
165   gstbasesrc_class->do_seek = GST_DEBUG_FUNCPTR (gst_rtmp_src_do_seek);
166   gstpushsrc_class->create = GST_DEBUG_FUNCPTR (gst_rtmp_src_create);
167   gstbasesrc_class->query = GST_DEBUG_FUNCPTR (gst_rtmp_src_query);
168 
169   GST_DEBUG_CATEGORY_INIT (rtmpsrc_debug, "rtmpsrc", 0, "RTMP Source");
170 }
171 
172 static void
gst_rtmp_src_init(GstRTMPSrc * rtmpsrc)173 gst_rtmp_src_init (GstRTMPSrc * rtmpsrc)
174 {
175 #ifdef G_OS_WIN32
176   WSADATA wsa_data;
177 
178   if (WSAStartup (MAKEWORD (2, 2), &wsa_data) != 0) {
179     GST_ERROR_OBJECT (rtmpsrc, "WSAStartup failed: 0x%08x", WSAGetLastError ());
180   }
181 #endif
182 
183   rtmpsrc->cur_offset = 0;
184   rtmpsrc->last_timestamp = 0;
185   rtmpsrc->timeout = DEFAULT_TIMEOUT;
186 
187   gst_base_src_set_format (GST_BASE_SRC (rtmpsrc), GST_FORMAT_TIME);
188 }
189 
190 static void
gst_rtmp_src_finalize(GObject * object)191 gst_rtmp_src_finalize (GObject * object)
192 {
193   GstRTMPSrc *rtmpsrc = GST_RTMP_SRC (object);
194 
195   g_free (rtmpsrc->uri);
196   rtmpsrc->uri = NULL;
197 
198 #ifdef G_OS_WIN32
199   WSACleanup ();
200 #endif
201 
202   G_OBJECT_CLASS (parent_class)->finalize (object);
203 }
204 
205 /*
206  * URI interface support.
207  */
208 
209 static GstURIType
gst_rtmp_src_uri_get_type(GType type)210 gst_rtmp_src_uri_get_type (GType type)
211 {
212   return GST_URI_SRC;
213 }
214 
215 static const gchar *const *
gst_rtmp_src_uri_get_protocols(GType type)216 gst_rtmp_src_uri_get_protocols (GType type)
217 {
218   static const gchar *protocols[] =
219       { "rtmp", "rtmpt", "rtmps", "rtmpe", "rtmfp", "rtmpte", "rtmpts", NULL };
220 
221   return protocols;
222 }
223 
224 static gchar *
gst_rtmp_src_uri_get_uri(GstURIHandler * handler)225 gst_rtmp_src_uri_get_uri (GstURIHandler * handler)
226 {
227   GstRTMPSrc *src = GST_RTMP_SRC (handler);
228 
229   /* FIXME: make thread-safe */
230   return g_strdup (src->uri);
231 }
232 
233 static gboolean
gst_rtmp_src_uri_set_uri(GstURIHandler * handler,const gchar * uri,GError ** error)234 gst_rtmp_src_uri_set_uri (GstURIHandler * handler, const gchar * uri,
235     GError ** error)
236 {
237   GstRTMPSrc *src = GST_RTMP_SRC (handler);
238 
239   if (GST_STATE (src) >= GST_STATE_PAUSED) {
240     g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_STATE,
241         "Changing the URI on rtmpsrc when it is running is not supported");
242     return FALSE;
243   }
244 
245   g_free (src->uri);
246   src->uri = NULL;
247 
248   if (uri != NULL) {
249     int protocol;
250     AVal host;
251     unsigned int port;
252     AVal playpath, app;
253 
254     if (!RTMP_ParseURL (uri, &protocol, &host, &port, &playpath, &app) ||
255         !host.av_len || !playpath.av_len) {
256       GST_ERROR_OBJECT (src, "Failed to parse URI %s", uri);
257       g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI,
258           "Could not parse RTMP URI");
259       /* FIXME: we should not be freeing RTMP internals to avoid leaking */
260       free (playpath.av_val);
261       return FALSE;
262     }
263     free (playpath.av_val);
264     src->uri = g_strdup (uri);
265   }
266 
267   GST_DEBUG_OBJECT (src, "Changed URI to %s", GST_STR_NULL (uri));
268 
269   return TRUE;
270 }
271 
272 static void
gst_rtmp_src_uri_handler_init(gpointer g_iface,gpointer iface_data)273 gst_rtmp_src_uri_handler_init (gpointer g_iface, gpointer iface_data)
274 {
275   GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
276 
277   iface->get_type = gst_rtmp_src_uri_get_type;
278   iface->get_protocols = gst_rtmp_src_uri_get_protocols;
279   iface->get_uri = gst_rtmp_src_uri_get_uri;
280   iface->set_uri = gst_rtmp_src_uri_set_uri;
281 }
282 
283 static void
gst_rtmp_src_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)284 gst_rtmp_src_set_property (GObject * object, guint prop_id,
285     const GValue * value, GParamSpec * pspec)
286 {
287   GstRTMPSrc *src;
288 
289   src = GST_RTMP_SRC (object);
290 
291   switch (prop_id) {
292     case PROP_LOCATION:{
293       gst_rtmp_src_uri_set_uri (GST_URI_HANDLER (src),
294           g_value_get_string (value), NULL);
295       break;
296     }
297     case PROP_TIMEOUT:{
298       src->timeout = g_value_get_int (value);
299       break;
300     }
301     default:
302       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
303       break;
304   }
305 }
306 
307 static void
gst_rtmp_src_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)308 gst_rtmp_src_get_property (GObject * object, guint prop_id, GValue * value,
309     GParamSpec * pspec)
310 {
311   GstRTMPSrc *src;
312 
313   src = GST_RTMP_SRC (object);
314 
315   switch (prop_id) {
316     case PROP_LOCATION:
317       g_value_set_string (value, src->uri);
318       break;
319     case PROP_TIMEOUT:
320       g_value_set_int (value, src->timeout);
321       break;
322     default:
323       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
324       break;
325   }
326 }
327 
328 /*
329  * Read a new buffer from src->reqoffset, takes care of events
330  * and seeking and such.
331  */
332 static GstFlowReturn
gst_rtmp_src_create(GstPushSrc * pushsrc,GstBuffer ** buffer)333 gst_rtmp_src_create (GstPushSrc * pushsrc, GstBuffer ** buffer)
334 {
335   GstRTMPSrc *src;
336   GstBuffer *buf;
337   GstMapInfo map;
338   guint8 *data;
339   guint todo;
340   gsize bsize;
341   int size;
342 
343   src = GST_RTMP_SRC (pushsrc);
344 
345   g_return_val_if_fail (src->rtmp != NULL, GST_FLOW_ERROR);
346 
347   if (!RTMP_IsConnected (src->rtmp)) {
348     GST_DEBUG_OBJECT (src, "reconnecting");
349     if (!gst_rtmp_src_connect (src))
350       return GST_FLOW_ERROR;
351   }
352 
353   size = GST_BASE_SRC_CAST (pushsrc)->blocksize;
354 
355   GST_DEBUG ("reading from %" G_GUINT64_FORMAT
356       ", size %u", src->cur_offset, size);
357 
358   buf = gst_buffer_new_allocate (NULL, size, NULL);
359   if (G_UNLIKELY (buf == NULL)) {
360     GST_ERROR_OBJECT (src, "Failed to allocate %u bytes", size);
361     return GST_FLOW_ERROR;
362   }
363 
364   todo = size;
365   gst_buffer_map (buf, &map, GST_MAP_WRITE);
366   data = map.data;
367   bsize = 0;
368 
369   while (todo > 0) {
370     int read = RTMP_Read (src->rtmp, (char *) data, todo);
371 
372     if (G_UNLIKELY (read == 0 && todo == size))
373       goto eos;
374 
375     if (G_UNLIKELY (read == 0))
376       break;
377 
378     if (G_UNLIKELY (read < 0))
379       goto read_failed;
380 
381     if (read < todo) {
382       data += read;
383       todo -= read;
384       bsize += read;
385     } else {
386       bsize += todo;
387       todo = 0;
388     }
389     GST_LOG ("  got size %d", read);
390   }
391   gst_buffer_unmap (buf, &map);
392   gst_buffer_resize (buf, 0, bsize);
393 
394   if (src->discont) {
395     GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
396     src->discont = FALSE;
397   }
398 
399   GST_BUFFER_TIMESTAMP (buf) = src->last_timestamp;
400   GST_BUFFER_OFFSET (buf) = src->cur_offset;
401 
402   src->cur_offset += size;
403   if (src->last_timestamp == GST_CLOCK_TIME_NONE)
404     src->last_timestamp = src->rtmp->m_mediaStamp * GST_MSECOND;
405   else
406     src->last_timestamp =
407         MAX (src->last_timestamp, src->rtmp->m_mediaStamp * GST_MSECOND);
408 
409   GST_LOG_OBJECT (src, "Created buffer of size %u at %" G_GINT64_FORMAT
410       " with timestamp %" GST_TIME_FORMAT, size, GST_BUFFER_OFFSET (buf),
411       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
412 
413 
414   /* we're done, return the buffer */
415   *buffer = buf;
416 
417   return GST_FLOW_OK;
418 
419 read_failed:
420   {
421     gst_buffer_unmap (buf, &map);
422     gst_buffer_unref (buf);
423     GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), ("Failed to read data"));
424     return GST_FLOW_ERROR;
425   }
426 eos:
427   {
428     gst_buffer_unmap (buf, &map);
429     gst_buffer_unref (buf);
430     if (src->cur_offset == 0) {
431       GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL),
432           ("Failed to read any data from stream, check your URL"));
433       return GST_FLOW_ERROR;
434     } else {
435       GST_DEBUG_OBJECT (src, "Reading data gave EOS");
436       return GST_FLOW_EOS;
437     }
438   }
439 }
440 
441 static gboolean
gst_rtmp_src_query(GstBaseSrc * basesrc,GstQuery * query)442 gst_rtmp_src_query (GstBaseSrc * basesrc, GstQuery * query)
443 {
444   gboolean ret = FALSE;
445   GstRTMPSrc *src = GST_RTMP_SRC (basesrc);
446 
447   switch (GST_QUERY_TYPE (query)) {
448     case GST_QUERY_URI:
449       gst_query_set_uri (query, src->uri);
450       ret = TRUE;
451       break;
452     case GST_QUERY_POSITION:{
453       GstFormat format;
454 
455       gst_query_parse_position (query, &format, NULL);
456       if (format == GST_FORMAT_TIME) {
457         gst_query_set_position (query, format, src->last_timestamp);
458         ret = TRUE;
459       }
460       break;
461     }
462     case GST_QUERY_DURATION:{
463       GstFormat format;
464       gdouble duration;
465 
466       gst_query_parse_duration (query, &format, NULL);
467       if (format == GST_FORMAT_TIME && src->rtmp) {
468         duration = RTMP_GetDuration (src->rtmp);
469         if (duration != 0.0) {
470           gst_query_set_duration (query, format, duration * GST_SECOND);
471           ret = TRUE;
472         }
473       }
474       break;
475     }
476     case GST_QUERY_SCHEDULING:{
477       gst_query_set_scheduling (query,
478           GST_SCHEDULING_FLAG_SEQUENTIAL |
479           GST_SCHEDULING_FLAG_BANDWIDTH_LIMITED, 1, -1, 0);
480       gst_query_add_scheduling_mode (query, GST_PAD_MODE_PUSH);
481 
482       ret = TRUE;
483       break;
484     }
485     default:
486       ret = FALSE;
487       break;
488   }
489 
490   if (!ret)
491     ret = GST_BASE_SRC_CLASS (parent_class)->query (basesrc, query);
492 
493   return ret;
494 }
495 
496 static gboolean
gst_rtmp_src_is_seekable(GstBaseSrc * basesrc)497 gst_rtmp_src_is_seekable (GstBaseSrc * basesrc)
498 {
499   GstRTMPSrc *src;
500 
501   src = GST_RTMP_SRC (basesrc);
502 
503   return src->seekable;
504 }
505 
506 static gboolean
gst_rtmp_src_prepare_seek_segment(GstBaseSrc * basesrc,GstEvent * event,GstSegment * segment)507 gst_rtmp_src_prepare_seek_segment (GstBaseSrc * basesrc, GstEvent * event,
508     GstSegment * segment)
509 {
510   GstRTMPSrc *src;
511   GstSeekType cur_type, stop_type;
512   gint64 cur, stop;
513   GstSeekFlags flags;
514   GstFormat format;
515   gdouble rate;
516 
517   src = GST_RTMP_SRC (basesrc);
518 
519   gst_event_parse_seek (event, &rate, &format, &flags,
520       &cur_type, &cur, &stop_type, &stop);
521 
522   if (!src->seekable) {
523     GST_LOG_OBJECT (src, "Not a seekable stream");
524     return FALSE;
525   }
526 
527   if (!src->rtmp) {
528     GST_LOG_OBJECT (src, "Not connected yet");
529     return FALSE;
530   }
531 
532   if (format != GST_FORMAT_TIME) {
533     GST_LOG_OBJECT (src, "Seeking only supported in TIME format");
534     return FALSE;
535   }
536 
537   if (stop_type != GST_SEEK_TYPE_NONE) {
538     GST_LOG_OBJECT (src, "Setting a stop position is not supported");
539     return FALSE;
540   }
541 
542   gst_segment_init (segment, GST_FORMAT_TIME);
543   gst_segment_do_seek (segment, rate, format, flags, cur_type, cur, stop_type,
544       stop, NULL);
545 
546   return TRUE;
547 }
548 
549 static gboolean
gst_rtmp_src_do_seek(GstBaseSrc * basesrc,GstSegment * segment)550 gst_rtmp_src_do_seek (GstBaseSrc * basesrc, GstSegment * segment)
551 {
552   GstRTMPSrc *src;
553 
554   src = GST_RTMP_SRC (basesrc);
555 
556   if (segment->format != GST_FORMAT_TIME) {
557     GST_LOG_OBJECT (src, "Only time based seeks are supported");
558     return FALSE;
559   }
560 
561   if (!src->rtmp) {
562     GST_LOG_OBJECT (src, "Not connected yet");
563     return FALSE;
564   }
565 
566   /* Initial seek */
567   if (src->cur_offset == 0 && segment->start == 0)
568     goto success;
569 
570   if (!src->seekable) {
571     GST_LOG_OBJECT (src, "Not a seekable stream");
572     return FALSE;
573   }
574 
575   /* If we have just disconnected in unlock(), we need to re-connect
576    * and also let librtmp read some data before sending a seek,
577    * otherwise it will stall. Calling create() does both. */
578   if (!RTMP_IsConnected (src->rtmp)) {
579     GstBuffer *buffer = NULL;
580     gst_rtmp_src_create (GST_PUSH_SRC (basesrc), &buffer);
581     gst_buffer_replace (&buffer, NULL);
582   }
583 
584   src->last_timestamp = GST_CLOCK_TIME_NONE;
585   if (!RTMP_SendSeek (src->rtmp, segment->start / GST_MSECOND)) {
586     GST_ERROR_OBJECT (src, "Seeking failed");
587     src->seekable = FALSE;
588     return FALSE;
589   }
590 
591 success:
592   /* This is set here so that the call to create() above doesn't clear it */
593   src->discont = TRUE;
594 
595   GST_DEBUG_OBJECT (src, "Seek to %" GST_TIME_FORMAT " successful",
596       GST_TIME_ARGS (segment->start));
597 
598   return TRUE;
599 }
600 
601 static gboolean
gst_rtmp_src_connect(GstRTMPSrc * src)602 gst_rtmp_src_connect (GstRTMPSrc * src)
603 {
604   RTMP_Init (src->rtmp);
605   src->rtmp->Link.timeout = src->timeout;
606   if (!RTMP_SetupURL (src->rtmp, src->uri)) {
607     GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL),
608         ("Failed to setup URL '%s'", src->uri));
609     return FALSE;
610   }
611   src->seekable = !(src->rtmp->Link.lFlags & RTMP_LF_LIVE);
612   GST_INFO_OBJECT (src, "seekable %d", src->seekable);
613 
614   /* open if required */
615   if (!RTMP_IsConnected (src->rtmp)) {
616     if (!RTMP_Connect (src->rtmp, NULL)) {
617       GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL),
618           ("Could not connect to RTMP stream \"%s\" for reading", src->uri));
619       return FALSE;
620     }
621   }
622 
623   return TRUE;
624 }
625 
626 /* open the file, do stuff necessary to go to PAUSED state */
627 static gboolean
gst_rtmp_src_start(GstBaseSrc * basesrc)628 gst_rtmp_src_start (GstBaseSrc * basesrc)
629 {
630   GstRTMPSrc *src;
631 
632   src = GST_RTMP_SRC (basesrc);
633 
634   if (!src->uri) {
635     GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), ("No filename given"));
636     return FALSE;
637   }
638 
639   src->cur_offset = 0;
640   src->last_timestamp = 0;
641   src->discont = TRUE;
642 
643   src->rtmp = RTMP_Alloc ();
644   if (!src->rtmp) {
645     GST_ERROR_OBJECT (src, "Could not allocate librtmp's RTMP context");
646     goto error;
647   }
648 
649   if (!gst_rtmp_src_connect (src))
650     goto error;
651 
652   return TRUE;
653 
654 error:
655   if (src->rtmp) {
656     RTMP_Free (src->rtmp);
657     src->rtmp = NULL;
658   }
659   return FALSE;
660 }
661 
662 static gboolean
gst_rtmp_src_unlock(GstBaseSrc * basesrc)663 gst_rtmp_src_unlock (GstBaseSrc * basesrc)
664 {
665   GstRTMPSrc *rtmpsrc = GST_RTMP_SRC (basesrc);
666 
667   GST_DEBUG_OBJECT (rtmpsrc, "unlock");
668 
669   /* This closes the socket, which means that any pending socket calls
670    * error out. */
671   if (rtmpsrc->rtmp) {
672     RTMP_Close (rtmpsrc->rtmp);
673   }
674 
675   return TRUE;
676 }
677 
678 
679 static gboolean
gst_rtmp_src_stop(GstBaseSrc * basesrc)680 gst_rtmp_src_stop (GstBaseSrc * basesrc)
681 {
682   GstRTMPSrc *src;
683 
684   src = GST_RTMP_SRC (basesrc);
685 
686   if (src->rtmp) {
687     RTMP_Free (src->rtmp);
688     src->rtmp = NULL;
689   }
690 
691   src->cur_offset = 0;
692   src->last_timestamp = 0;
693   src->discont = TRUE;
694 
695   return TRUE;
696 }
697