1 /*
2 *
3 * GStreamer
4 * Copyright (C) 1999-2001 Erik Walthinsen <omega@cse.ogi.edu>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22 #ifdef HAVE_CONFIG_H
23 # include <config.h>
24 #endif
25
26 #include <gst/gst.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include "gstmms.h"
30
31 #define DEFAULT_CONNECTION_SPEED 0
32
33 enum
34 {
35 PROP_0,
36 PROP_LOCATION,
37 PROP_CONNECTION_SPEED
38 };
39
40
41 GST_DEBUG_CATEGORY_STATIC (mmssrc_debug);
42 #define GST_CAT_DEFAULT mmssrc_debug
43
44 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
45 GST_PAD_SRC,
46 GST_PAD_ALWAYS,
47 GST_STATIC_CAPS ("video/x-ms-asf")
48 );
49
50 static void gst_mms_finalize (GObject * gobject);
51 static void gst_mms_uri_handler_init (gpointer g_iface, gpointer iface_data);
52
53 static void gst_mms_set_property (GObject * object, guint prop_id,
54 const GValue * value, GParamSpec * pspec);
55 static void gst_mms_get_property (GObject * object, guint prop_id,
56 GValue * value, GParamSpec * pspec);
57
58 static gboolean gst_mms_query (GstBaseSrc * src, GstQuery * query);
59
60 static gboolean gst_mms_start (GstBaseSrc * bsrc);
61 static gboolean gst_mms_stop (GstBaseSrc * bsrc);
62 static gboolean gst_mms_is_seekable (GstBaseSrc * src);
63 static gboolean gst_mms_get_size (GstBaseSrc * src, guint64 * size);
64 static gboolean gst_mms_prepare_seek_segment (GstBaseSrc * src,
65 GstEvent * event, GstSegment * segment);
66 static gboolean gst_mms_do_seek (GstBaseSrc * src, GstSegment * segment);
67
68 static GstFlowReturn gst_mms_create (GstPushSrc * psrc, GstBuffer ** buf);
69
70 static gboolean gst_mms_uri_set_uri (GstURIHandler * handler,
71 const gchar * uri, GError ** error);
72
73 #define gst_mms_parent_class parent_class
74 G_DEFINE_TYPE_WITH_CODE (GstMMS, gst_mms, GST_TYPE_PUSH_SRC,
75 G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER, gst_mms_uri_handler_init));
76
77 /* initialize the plugin's class */
78 static void
gst_mms_class_init(GstMMSClass * klass)79 gst_mms_class_init (GstMMSClass * klass)
80 {
81 GObjectClass *gobject_class = (GObjectClass *) klass;
82 GstElementClass *gstelement_class = (GstElementClass *) klass;
83 GstBaseSrcClass *gstbasesrc_class = (GstBaseSrcClass *) klass;
84 GstPushSrcClass *gstpushsrc_class = (GstPushSrcClass *) klass;
85
86 gobject_class->set_property = gst_mms_set_property;
87 gobject_class->get_property = gst_mms_get_property;
88 gobject_class->finalize = gst_mms_finalize;
89
90 g_object_class_install_property (gobject_class, PROP_LOCATION,
91 g_param_spec_string ("location", "location",
92 "Host URL to connect to. Accepted are mms://, mmsu://, mmst:// URL types",
93 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
94
95 /* Note: connection-speed is intentionaly limited to G_MAXINT as libmms
96 * uses int for it */
97 g_object_class_install_property (gobject_class, PROP_CONNECTION_SPEED,
98 g_param_spec_uint64 ("connection-speed", "Connection Speed",
99 "Network connection speed in kbps (0 = unknown)",
100 0, G_MAXINT / 1000, DEFAULT_CONNECTION_SPEED,
101 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
102
103 gst_element_class_add_static_pad_template (gstelement_class, &src_factory);
104
105 gst_element_class_set_static_metadata (gstelement_class,
106 "MMS streaming source", "Source/Network",
107 "Receive data streamed via MSFT Multi Media Server protocol",
108 "Maciej Katafiasz <mathrick@users.sourceforge.net>");
109
110 GST_DEBUG_CATEGORY_INIT (mmssrc_debug, "mmssrc", 0, "MMS Source Element");
111
112 gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_mms_start);
113 gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_mms_stop);
114
115 gstpushsrc_class->create = GST_DEBUG_FUNCPTR (gst_mms_create);
116
117 gstbasesrc_class->is_seekable = GST_DEBUG_FUNCPTR (gst_mms_is_seekable);
118 gstbasesrc_class->get_size = GST_DEBUG_FUNCPTR (gst_mms_get_size);
119 gstbasesrc_class->prepare_seek_segment =
120 GST_DEBUG_FUNCPTR (gst_mms_prepare_seek_segment);
121 gstbasesrc_class->do_seek = GST_DEBUG_FUNCPTR (gst_mms_do_seek);
122 gstbasesrc_class->query = GST_DEBUG_FUNCPTR (gst_mms_query);
123 }
124
125 /* initialize the new element
126 * instantiate pads and add them to element
127 * set functions
128 * initialize structure
129 */
130 static void
gst_mms_init(GstMMS * mmssrc)131 gst_mms_init (GstMMS * mmssrc)
132 {
133 mmssrc->uri_name = NULL;
134 mmssrc->current_connection_uri_name = NULL;
135 mmssrc->connection = NULL;
136 mmssrc->connection_speed = DEFAULT_CONNECTION_SPEED;
137 }
138
139 static void
gst_mms_finalize(GObject * gobject)140 gst_mms_finalize (GObject * gobject)
141 {
142 GstMMS *mmssrc = GST_MMS (gobject);
143
144 /* We may still have a connection open, as we preserve unused / pristine
145 open connections in stop to reuse them in start. */
146 if (mmssrc->connection) {
147 mmsx_close (mmssrc->connection);
148 mmssrc->connection = NULL;
149 }
150
151 if (mmssrc->current_connection_uri_name) {
152 g_free (mmssrc->current_connection_uri_name);
153 mmssrc->current_connection_uri_name = NULL;
154 }
155
156 if (mmssrc->uri_name) {
157 g_free (mmssrc->uri_name);
158 mmssrc->uri_name = NULL;
159 }
160
161 G_OBJECT_CLASS (parent_class)->finalize (gobject);
162 }
163
164 /* FIXME operating in TIME rather than BYTES could remove this altogether
165 * and be more convenient elsewhere */
166 static gboolean
gst_mms_query(GstBaseSrc * src,GstQuery * query)167 gst_mms_query (GstBaseSrc * src, GstQuery * query)
168 {
169 GstMMS *mmssrc = GST_MMS (src);
170 gboolean res = TRUE;
171 GstFormat format;
172 gint64 value;
173
174 switch (GST_QUERY_TYPE (query)) {
175 case GST_QUERY_POSITION:
176 gst_query_parse_position (query, &format, &value);
177 if (format != GST_FORMAT_BYTES) {
178 res = FALSE;
179 break;
180 }
181 value = (gint64) mmsx_get_current_pos (mmssrc->connection);
182 gst_query_set_position (query, format, value);
183 break;
184 case GST_QUERY_DURATION:
185 if (!mmsx_get_seekable (mmssrc->connection)) {
186 res = FALSE;
187 break;
188 }
189 gst_query_parse_duration (query, &format, &value);
190 switch (format) {
191 case GST_FORMAT_BYTES:
192 value = (gint64) mmsx_get_length (mmssrc->connection);
193 gst_query_set_duration (query, format, value);
194 break;
195 case GST_FORMAT_TIME:
196 value = mmsx_get_time_length (mmssrc->connection) * GST_SECOND;
197 gst_query_set_duration (query, format, value);
198 break;
199 default:
200 res = FALSE;
201 }
202 break;
203 default:
204 /* chain to parent */
205 res =
206 GST_BASE_SRC_CLASS (parent_class)->query (GST_BASE_SRC (src), query);
207 break;
208 }
209
210 return res;
211 }
212
213
214 static gboolean
gst_mms_prepare_seek_segment(GstBaseSrc * src,GstEvent * event,GstSegment * segment)215 gst_mms_prepare_seek_segment (GstBaseSrc * src, GstEvent * event,
216 GstSegment * segment)
217 {
218 GstSeekType cur_type, stop_type;
219 gint64 cur, stop;
220 GstSeekFlags flags;
221 GstFormat seek_format;
222 gdouble rate;
223
224 gst_event_parse_seek (event, &rate, &seek_format, &flags,
225 &cur_type, &cur, &stop_type, &stop);
226
227 if (seek_format != GST_FORMAT_BYTES && seek_format != GST_FORMAT_TIME) {
228 GST_LOG_OBJECT (src, "Only byte or time seeking is supported");
229 return FALSE;
230 }
231
232 if (stop_type != GST_SEEK_TYPE_NONE) {
233 GST_LOG_OBJECT (src, "Stop seeking not supported");
234 return FALSE;
235 }
236
237 if (cur_type != GST_SEEK_TYPE_NONE && cur_type != GST_SEEK_TYPE_SET) {
238 GST_LOG_OBJECT (src, "Only absolute seeking is supported");
239 return FALSE;
240 }
241
242 /* We would like to convert from GST_FORMAT_TIME to GST_FORMAT_BYTES here
243 when needed, but we cannot as to do that we need to actually do the seek,
244 so we handle this in do_seek instead. */
245
246 /* FIXME implement relative seeking, we could do any needed relevant
247 seeking calculations here (in seek_format metrics), before the re-init
248 of the segment. */
249
250 gst_segment_init (segment, seek_format);
251 gst_segment_do_seek (segment, rate, seek_format, flags, cur_type, cur,
252 stop_type, stop, NULL);
253
254 return TRUE;
255 }
256
257 static gboolean
gst_mms_do_seek(GstBaseSrc * src,GstSegment * segment)258 gst_mms_do_seek (GstBaseSrc * src, GstSegment * segment)
259 {
260 gint64 start;
261 GstMMS *mmssrc = GST_MMS (src);
262
263 if (segment->format == GST_FORMAT_TIME) {
264 if (!mmsx_time_seek (NULL, mmssrc->connection,
265 (double) segment->start / GST_SECOND)) {
266 GST_LOG_OBJECT (mmssrc, "mmsx_time_seek() failed");
267 return FALSE;
268 }
269 start = mmsx_get_current_pos (mmssrc->connection);
270 GST_INFO_OBJECT (mmssrc, "sought to %" GST_TIME_FORMAT ", offset after "
271 "seek: %" G_GINT64_FORMAT, GST_TIME_ARGS (segment->start), start);
272 } else if (segment->format == GST_FORMAT_BYTES) {
273 start = mmsx_seek (NULL, mmssrc->connection, segment->start, SEEK_SET);
274 /* mmsx_seek will close and reopen the connection when seeking with the
275 mmsh protocol, if the reopening fails this is indicated with -1 */
276 if (start == -1) {
277 GST_DEBUG_OBJECT (mmssrc, "connection broken during seek");
278 return FALSE;
279 }
280 GST_INFO_OBJECT (mmssrc, "sought to: %" G_GINT64_FORMAT " bytes, "
281 "result: %" G_GINT64_FORMAT, segment->start, start);
282 } else {
283 GST_DEBUG_OBJECT (mmssrc, "unsupported seek segment format: %s",
284 GST_STR_NULL (gst_format_get_name (segment->format)));
285 return FALSE;
286 }
287 gst_segment_init (segment, GST_FORMAT_BYTES);
288 gst_segment_do_seek (segment, segment->rate, GST_FORMAT_BYTES,
289 GST_SEEK_FLAG_NONE, GST_SEEK_TYPE_SET, start, GST_SEEK_TYPE_NONE,
290 segment->stop, NULL);
291 return TRUE;
292 }
293
294
295 /* get function
296 * this function generates new data when needed
297 */
298
299
300 static GstFlowReturn
gst_mms_create(GstPushSrc * psrc,GstBuffer ** buf)301 gst_mms_create (GstPushSrc * psrc, GstBuffer ** buf)
302 {
303 GstMMS *mmssrc = GST_MMS (psrc);
304 guint8 *data;
305 guint blocksize;
306 gint result;
307 goffset offset;
308
309 *buf = NULL;
310
311 offset = mmsx_get_current_pos (mmssrc->connection);
312
313 /* Check if a seek perhaps has wrecked our connection */
314 if (offset == -1) {
315 GST_ERROR_OBJECT (mmssrc,
316 "connection broken (probably an error during mmsx_seek_time during a convert query) returning FLOW_ERROR");
317 return GST_FLOW_ERROR;
318 }
319
320 /* Choose blocksize best for optimum performance */
321 if (offset == 0)
322 blocksize = mmsx_get_asf_header_len (mmssrc->connection);
323 else
324 blocksize = mmsx_get_asf_packet_len (mmssrc->connection);
325
326 data = g_try_malloc (blocksize);
327 if (!data) {
328 GST_ERROR_OBJECT (mmssrc, "Failed to allocate %u bytes", blocksize);
329 return GST_FLOW_ERROR;
330 }
331
332 GST_LOG_OBJECT (mmssrc, "reading %d bytes", blocksize);
333 result = mmsx_read (NULL, mmssrc->connection, (char *) data, blocksize);
334 /* EOS? */
335 if (result == 0)
336 goto eos;
337
338 *buf = gst_buffer_new_wrapped (data, result);
339 GST_BUFFER_OFFSET (*buf) = offset;
340
341 GST_LOG_OBJECT (mmssrc, "Returning buffer with offset %" G_GOFFSET_FORMAT
342 " and size %u", offset, result);
343
344 return GST_FLOW_OK;
345
346 eos:
347 {
348 GST_DEBUG_OBJECT (mmssrc, "EOS");
349 g_free (data);
350 *buf = NULL;
351 return GST_FLOW_EOS;
352 }
353 }
354
355 static gboolean
gst_mms_is_seekable(GstBaseSrc * src)356 gst_mms_is_seekable (GstBaseSrc * src)
357 {
358 GstMMS *mmssrc = GST_MMS (src);
359
360 return mmsx_get_seekable (mmssrc->connection);
361 }
362
363 static gboolean
gst_mms_get_size(GstBaseSrc * src,guint64 * size)364 gst_mms_get_size (GstBaseSrc * src, guint64 * size)
365 {
366 GstMMS *mmssrc = GST_MMS (src);
367
368 /* non seekable usually means live streams, and get_length() returns,
369 erm, interesting values for live streams */
370 if (!mmsx_get_seekable (mmssrc->connection))
371 return FALSE;
372
373 *size = mmsx_get_length (mmssrc->connection);
374 return TRUE;
375 }
376
377 static gboolean
gst_mms_start(GstBaseSrc * bsrc)378 gst_mms_start (GstBaseSrc * bsrc)
379 {
380 GstMMS *mms = GST_MMS (bsrc);
381 guint bandwidth_avail;
382
383 if (!mms->uri_name || *mms->uri_name == '\0')
384 goto no_uri;
385
386 if (mms->connection_speed)
387 bandwidth_avail = mms->connection_speed;
388 else
389 bandwidth_avail = G_MAXINT;
390
391 /* If we already have a connection, and the uri isn't changed, reuse it,
392 as connecting is expensive. */
393 if (mms->connection) {
394 if (!strcmp (mms->uri_name, mms->current_connection_uri_name)) {
395 GST_DEBUG_OBJECT (mms, "Reusing existing connection for %s",
396 mms->uri_name);
397 return TRUE;
398 } else {
399 mmsx_close (mms->connection);
400 g_free (mms->current_connection_uri_name);
401 mms->current_connection_uri_name = NULL;
402 }
403 }
404
405 /* FIXME: pass some sane arguments here */
406 GST_DEBUG_OBJECT (mms,
407 "Trying mms_connect (%s) with bandwidth constraint of %d bps",
408 mms->uri_name, bandwidth_avail);
409 mms->connection = mmsx_connect (NULL, NULL, mms->uri_name, bandwidth_avail);
410 if (mms->connection) {
411 /* Save the uri name so that it can be checked for connection reusing,
412 see above. */
413 mms->current_connection_uri_name = g_strdup (mms->uri_name);
414 GST_DEBUG_OBJECT (mms, "Connect successful");
415 return TRUE;
416 } else {
417 gchar *url, *location;
418
419 GST_ERROR_OBJECT (mms,
420 "Could not connect to this stream, redirecting to rtsp");
421 location = strstr (mms->uri_name, "://");
422 if (location == NULL || *location == '\0' || *(location + 3) == '\0')
423 goto no_uri;
424 url = g_strdup_printf ("rtsp://%s", location + 3);
425
426 gst_element_post_message (GST_ELEMENT_CAST (mms),
427 gst_message_new_element (GST_OBJECT_CAST (mms),
428 gst_structure_new ("redirect", "new-location", G_TYPE_STRING, url,
429 NULL)));
430
431 /* post an error message as well, so that applications that don't handle
432 * redirect messages get to see a proper error message */
433 GST_ELEMENT_ERROR (mms, RESOURCE, OPEN_READ,
434 ("Could not connect to streaming server."),
435 ("A redirect message was posted on the bus and should have been "
436 "handled by the application."));
437
438 return FALSE;
439 }
440
441 no_uri:
442 {
443 GST_ELEMENT_ERROR (mms, RESOURCE, OPEN_READ,
444 ("No URI to open specified"), (NULL));
445 return FALSE;
446 }
447 }
448
449 static gboolean
gst_mms_stop(GstBaseSrc * bsrc)450 gst_mms_stop (GstBaseSrc * bsrc)
451 {
452 GstMMS *mms = GST_MMS (bsrc);
453
454 if (mms->connection != NULL) {
455 /* Check if the connection is still pristine, that is if no more then
456 just the mmslib cached asf header has been read. If it is still pristine
457 preserve it as we often are re-started with the same URL and connecting
458 is expensive */
459 if (mmsx_get_current_pos (mms->connection) >
460 mmsx_get_asf_header_len (mms->connection)) {
461 mmsx_close (mms->connection);
462 mms->connection = NULL;
463 g_free (mms->current_connection_uri_name);
464 mms->current_connection_uri_name = NULL;
465 }
466 }
467 return TRUE;
468 }
469
470 static void
gst_mms_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)471 gst_mms_set_property (GObject * object, guint prop_id,
472 const GValue * value, GParamSpec * pspec)
473 {
474 GstMMS *mmssrc = GST_MMS (object);
475
476 switch (prop_id) {
477 case PROP_LOCATION:
478 gst_mms_uri_set_uri (GST_URI_HANDLER (mmssrc),
479 g_value_get_string (value), NULL);
480 break;
481 case PROP_CONNECTION_SPEED:
482 GST_OBJECT_LOCK (mmssrc);
483 mmssrc->connection_speed = g_value_get_uint64 (value) * 1000;
484 GST_OBJECT_UNLOCK (mmssrc);
485 break;
486 default:
487 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
488 break;
489 }
490 }
491
492 static void
gst_mms_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)493 gst_mms_get_property (GObject * object, guint prop_id,
494 GValue * value, GParamSpec * pspec)
495 {
496 GstMMS *mmssrc = GST_MMS (object);
497
498 GST_OBJECT_LOCK (mmssrc);
499 switch (prop_id) {
500 case PROP_LOCATION:
501 if (mmssrc->uri_name)
502 g_value_set_string (value, mmssrc->uri_name);
503 break;
504 case PROP_CONNECTION_SPEED:
505 g_value_set_uint64 (value, mmssrc->connection_speed / 1000);
506 break;
507 default:
508 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
509 break;
510 }
511 GST_OBJECT_UNLOCK (mmssrc);
512 }
513
514 /* entry point to initialize the plug-in
515 * initialize the plug-in itself
516 * register the element factories and pad templates
517 * register the features
518 */
519 static gboolean
plugin_init(GstPlugin * plugin)520 plugin_init (GstPlugin * plugin)
521 {
522 return gst_element_register (plugin, "mmssrc", GST_RANK_NONE, GST_TYPE_MMS);
523 }
524
525 static GstURIType
gst_mms_uri_get_type(GType type)526 gst_mms_uri_get_type (GType type)
527 {
528 return GST_URI_SRC;
529 }
530
531 static const gchar *const *
gst_mms_uri_get_protocols(GType type)532 gst_mms_uri_get_protocols (GType type)
533 {
534 static const gchar *protocols[] = { "mms", "mmsh", "mmst", "mmsu", NULL };
535
536 return protocols;
537 }
538
539 static gchar *
gst_mms_uri_get_uri(GstURIHandler * handler)540 gst_mms_uri_get_uri (GstURIHandler * handler)
541 {
542 GstMMS *src = GST_MMS (handler);
543
544 /* FIXME: make thread-safe */
545 return g_strdup (src->uri_name);
546 }
547
548 static gchar *
gst_mms_src_make_valid_uri(const gchar * uri)549 gst_mms_src_make_valid_uri (const gchar * uri)
550 {
551 gchar *protocol;
552 const gchar *colon, *tmp;
553 gsize len;
554
555 if (!uri || !gst_uri_is_valid (uri))
556 return NULL;
557
558 protocol = gst_uri_get_protocol (uri);
559
560 if ((strcmp (protocol, "mms") != 0) && (strcmp (protocol, "mmsh") != 0) &&
561 (strcmp (protocol, "mmst") != 0) && (strcmp (protocol, "mmsu") != 0)) {
562 g_free (protocol);
563 return FALSE;
564 }
565 g_free (protocol);
566
567 colon = strstr (uri, "://");
568 if (!colon)
569 return NULL;
570
571 tmp = colon + 3;
572 len = strlen (tmp);
573 if (len == 0)
574 return NULL;
575
576 /* libmms segfaults if there's no hostname or
577 * no / after the hostname
578 */
579 colon = strstr (tmp, "/");
580 if (colon == tmp)
581 return NULL;
582
583 if (strstr (tmp, "/") == NULL) {
584 gchar *ret;
585
586 len = strlen (uri);
587 ret = g_malloc0 (len + 2);
588 memcpy (ret, uri, len);
589 ret[len] = '/';
590 return ret;
591 } else {
592 return g_strdup (uri);
593 }
594 }
595
596 static gboolean
gst_mms_uri_set_uri(GstURIHandler * handler,const gchar * uri,GError ** error)597 gst_mms_uri_set_uri (GstURIHandler * handler, const gchar * uri,
598 GError ** error)
599 {
600 GstMMS *src = GST_MMS (handler);
601 gchar *fixed_uri;
602
603 fixed_uri = gst_mms_src_make_valid_uri (uri);
604 if (!fixed_uri && uri) {
605 g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI,
606 "Invalid MMS URI");
607 return FALSE;
608 }
609
610 GST_OBJECT_LOCK (src);
611 g_free (src->uri_name);
612 src->uri_name = fixed_uri;
613 GST_OBJECT_UNLOCK (src);
614
615 return TRUE;
616 }
617
618 static void
gst_mms_uri_handler_init(gpointer g_iface,gpointer iface_data)619 gst_mms_uri_handler_init (gpointer g_iface, gpointer iface_data)
620 {
621 GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
622
623 iface->get_type = gst_mms_uri_get_type;
624 iface->get_protocols = gst_mms_uri_get_protocols;
625 iface->get_uri = gst_mms_uri_get_uri;
626 iface->set_uri = gst_mms_uri_set_uri;
627 }
628
629
630 /* this is the structure that gst-register looks for
631 * so keep the name plugin_desc, or you cannot get your plug-in registered */
632 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
633 GST_VERSION_MINOR,
634 mms,
635 "Microsoft Multi Media Server streaming protocol support",
636 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
637