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