mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-29 05:01:23 +00:00
mms: remove mmssrc plugin
Doubtful that anyone still needs that or there are even any streams left out there. MMS was deprecated in 2003 (in favour of RTSP) and support for it was dropped with Microsoft Media Services 2008. https://en.wikipedia.org/wiki/Microsoft_Media_Server https://sdp.ppona.com/news2008.html Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/issues/821 Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1211>
This commit is contained in:
parent
47445980a9
commit
9a4651e387
7 changed files with 0 additions and 775 deletions
|
@ -45,10 +45,6 @@ dtsdec (for DTS audio decoding)
|
||||||
http://www.videolan.org/libdca.html
|
http://www.videolan.org/libdca.html
|
||||||
musepack (for musepack audio codec/format)
|
musepack (for musepack audio codec/format)
|
||||||
(http://www.musepack.net/)
|
(http://www.musepack.net/)
|
||||||
nas (for the NAS sound server sink)
|
|
||||||
(http://radscan.com/nas.html)
|
|
||||||
libmms (for MMS protocol support)
|
|
||||||
(http://www.sf.net/projects/libmms)
|
|
||||||
libamrnb (for AMR-NB support)
|
libamrnb (for AMR-NB support)
|
||||||
(http://www.penguin.cz/~utx/amr)
|
(http://www.penguin.cz/~utx/amr)
|
||||||
libamrwb (for AMR-WB support)
|
libamrwb (for AMR-WB support)
|
||||||
|
|
|
@ -209604,72 +209604,6 @@
|
||||||
"tracers": {},
|
"tracers": {},
|
||||||
"url": "Unknown package origin"
|
"url": "Unknown package origin"
|
||||||
},
|
},
|
||||||
"mms": {
|
|
||||||
"description": "Microsoft Multi Media Server streaming protocol support",
|
|
||||||
"elements": {
|
|
||||||
"mmssrc": {
|
|
||||||
"author": "Maciej Katafiasz <mathrick@users.sourceforge.net>",
|
|
||||||
"description": "Receive data streamed via MSFT Multi Media Server protocol",
|
|
||||||
"hierarchy": [
|
|
||||||
"GstMMS",
|
|
||||||
"GstPushSrc",
|
|
||||||
"GstBaseSrc",
|
|
||||||
"GstElement",
|
|
||||||
"GstObject",
|
|
||||||
"GInitiallyUnowned",
|
|
||||||
"GObject"
|
|
||||||
],
|
|
||||||
"interfaces": [
|
|
||||||
"GstURIHandler"
|
|
||||||
],
|
|
||||||
"klass": "Source/Network",
|
|
||||||
"long-name": "MMS streaming source",
|
|
||||||
"pad-templates": {
|
|
||||||
"src": {
|
|
||||||
"caps": "video/x-ms-asf:\n",
|
|
||||||
"direction": "src",
|
|
||||||
"presence": "always"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"properties": {
|
|
||||||
"connection-speed": {
|
|
||||||
"blurb": "Network connection speed in kbps (0 = unknown)",
|
|
||||||
"conditionally-available": false,
|
|
||||||
"construct": false,
|
|
||||||
"construct-only": false,
|
|
||||||
"controllable": false,
|
|
||||||
"default": "0",
|
|
||||||
"max": "2147483",
|
|
||||||
"min": "0",
|
|
||||||
"mutable": "null",
|
|
||||||
"readable": true,
|
|
||||||
"type": "guint64",
|
|
||||||
"writable": true
|
|
||||||
},
|
|
||||||
"location": {
|
|
||||||
"blurb": "Host URL to connect to. Accepted are mms://, mmsu://, mmst:// URL types",
|
|
||||||
"conditionally-available": false,
|
|
||||||
"construct": false,
|
|
||||||
"construct-only": false,
|
|
||||||
"controllable": false,
|
|
||||||
"default": "NULL",
|
|
||||||
"mutable": "null",
|
|
||||||
"readable": true,
|
|
||||||
"type": "gchararray",
|
|
||||||
"writable": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"rank": "none"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"filename": "gstmms",
|
|
||||||
"license": "LGPL",
|
|
||||||
"other-types": {},
|
|
||||||
"package": "GStreamer Bad Plug-ins",
|
|
||||||
"source": "gst-plugins-bad",
|
|
||||||
"tracers": {},
|
|
||||||
"url": "Unknown package origin"
|
|
||||||
},
|
|
||||||
"modplug": {
|
"modplug": {
|
||||||
"description": ".MOD audio decoding",
|
"description": ".MOD audio decoding",
|
||||||
"elements": {
|
"elements": {
|
||||||
|
|
|
@ -1,637 +0,0 @@
|
||||||
/*
|
|
||||||
*
|
|
||||||
* GStreamer
|
|
||||||
* Copyright (C) 1999-2001 Erik Walthinsen <omega@cse.ogi.edu>
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Library General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
* Library General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Library General Public
|
|
||||||
* License along with this library; if not, write to the
|
|
||||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
||||||
* Boston, MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
# include <config.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <gst/gst.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "gstmms.h"
|
|
||||||
|
|
||||||
#define DEFAULT_CONNECTION_SPEED 0
|
|
||||||
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
PROP_0,
|
|
||||||
PROP_LOCATION,
|
|
||||||
PROP_CONNECTION_SPEED
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
GST_DEBUG_CATEGORY_STATIC (mmssrc_debug);
|
|
||||||
#define GST_CAT_DEFAULT mmssrc_debug
|
|
||||||
|
|
||||||
static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
|
|
||||||
GST_PAD_SRC,
|
|
||||||
GST_PAD_ALWAYS,
|
|
||||||
GST_STATIC_CAPS ("video/x-ms-asf")
|
|
||||||
);
|
|
||||||
|
|
||||||
static void gst_mms_finalize (GObject * gobject);
|
|
||||||
static void gst_mms_uri_handler_init (gpointer g_iface, gpointer iface_data);
|
|
||||||
|
|
||||||
static void gst_mms_set_property (GObject * object, guint prop_id,
|
|
||||||
const GValue * value, GParamSpec * pspec);
|
|
||||||
static void gst_mms_get_property (GObject * object, guint prop_id,
|
|
||||||
GValue * value, GParamSpec * pspec);
|
|
||||||
|
|
||||||
static gboolean gst_mms_query (GstBaseSrc * src, GstQuery * query);
|
|
||||||
|
|
||||||
static gboolean gst_mms_start (GstBaseSrc * bsrc);
|
|
||||||
static gboolean gst_mms_stop (GstBaseSrc * bsrc);
|
|
||||||
static gboolean gst_mms_is_seekable (GstBaseSrc * src);
|
|
||||||
static gboolean gst_mms_get_size (GstBaseSrc * src, guint64 * size);
|
|
||||||
static gboolean gst_mms_prepare_seek_segment (GstBaseSrc * src,
|
|
||||||
GstEvent * event, GstSegment * segment);
|
|
||||||
static gboolean gst_mms_do_seek (GstBaseSrc * src, GstSegment * segment);
|
|
||||||
|
|
||||||
static GstFlowReturn gst_mms_create (GstPushSrc * psrc, GstBuffer ** buf);
|
|
||||||
|
|
||||||
static gboolean gst_mms_uri_set_uri (GstURIHandler * handler,
|
|
||||||
const gchar * uri, GError ** error);
|
|
||||||
|
|
||||||
#define gst_mms_parent_class parent_class
|
|
||||||
G_DEFINE_TYPE_WITH_CODE (GstMMS, gst_mms, GST_TYPE_PUSH_SRC,
|
|
||||||
G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER, gst_mms_uri_handler_init));
|
|
||||||
GST_ELEMENT_REGISTER_DEFINE (mmssrc, "mmssrc", GST_RANK_NONE, GST_TYPE_MMS);
|
|
||||||
|
|
||||||
/* initialize the plugin's class */
|
|
||||||
static void
|
|
||||||
gst_mms_class_init (GstMMSClass * klass)
|
|
||||||
{
|
|
||||||
GObjectClass *gobject_class = (GObjectClass *) klass;
|
|
||||||
GstElementClass *gstelement_class = (GstElementClass *) klass;
|
|
||||||
GstBaseSrcClass *gstbasesrc_class = (GstBaseSrcClass *) klass;
|
|
||||||
GstPushSrcClass *gstpushsrc_class = (GstPushSrcClass *) klass;
|
|
||||||
|
|
||||||
gobject_class->set_property = gst_mms_set_property;
|
|
||||||
gobject_class->get_property = gst_mms_get_property;
|
|
||||||
gobject_class->finalize = gst_mms_finalize;
|
|
||||||
|
|
||||||
g_object_class_install_property (gobject_class, PROP_LOCATION,
|
|
||||||
g_param_spec_string ("location", "location",
|
|
||||||
"Host URL to connect to. Accepted are mms://, mmsu://, mmst:// URL types",
|
|
||||||
NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
||||||
|
|
||||||
/* Note: connection-speed is intentionaly limited to G_MAXINT as libmms
|
|
||||||
* uses int for it */
|
|
||||||
g_object_class_install_property (gobject_class, PROP_CONNECTION_SPEED,
|
|
||||||
g_param_spec_uint64 ("connection-speed", "Connection Speed",
|
|
||||||
"Network connection speed in kbps (0 = unknown)",
|
|
||||||
0, G_MAXINT / 1000, DEFAULT_CONNECTION_SPEED,
|
|
||||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
||||||
|
|
||||||
gst_element_class_add_static_pad_template (gstelement_class, &src_factory);
|
|
||||||
|
|
||||||
gst_element_class_set_static_metadata (gstelement_class,
|
|
||||||
"MMS streaming source", "Source/Network",
|
|
||||||
"Receive data streamed via MSFT Multi Media Server protocol",
|
|
||||||
"Maciej Katafiasz <mathrick@users.sourceforge.net>");
|
|
||||||
|
|
||||||
GST_DEBUG_CATEGORY_INIT (mmssrc_debug, "mmssrc", 0, "MMS Source Element");
|
|
||||||
|
|
||||||
gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_mms_start);
|
|
||||||
gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_mms_stop);
|
|
||||||
|
|
||||||
gstpushsrc_class->create = GST_DEBUG_FUNCPTR (gst_mms_create);
|
|
||||||
|
|
||||||
gstbasesrc_class->is_seekable = GST_DEBUG_FUNCPTR (gst_mms_is_seekable);
|
|
||||||
gstbasesrc_class->get_size = GST_DEBUG_FUNCPTR (gst_mms_get_size);
|
|
||||||
gstbasesrc_class->prepare_seek_segment =
|
|
||||||
GST_DEBUG_FUNCPTR (gst_mms_prepare_seek_segment);
|
|
||||||
gstbasesrc_class->do_seek = GST_DEBUG_FUNCPTR (gst_mms_do_seek);
|
|
||||||
gstbasesrc_class->query = GST_DEBUG_FUNCPTR (gst_mms_query);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* initialize the new element
|
|
||||||
* instantiate pads and add them to element
|
|
||||||
* set functions
|
|
||||||
* initialize structure
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
gst_mms_init (GstMMS * mmssrc)
|
|
||||||
{
|
|
||||||
mmssrc->uri_name = NULL;
|
|
||||||
mmssrc->current_connection_uri_name = NULL;
|
|
||||||
mmssrc->connection = NULL;
|
|
||||||
mmssrc->connection_speed = DEFAULT_CONNECTION_SPEED;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gst_mms_finalize (GObject * gobject)
|
|
||||||
{
|
|
||||||
GstMMS *mmssrc = GST_MMS (gobject);
|
|
||||||
|
|
||||||
/* We may still have a connection open, as we preserve unused / pristine
|
|
||||||
open connections in stop to reuse them in start. */
|
|
||||||
if (mmssrc->connection) {
|
|
||||||
mmsx_close (mmssrc->connection);
|
|
||||||
mmssrc->connection = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mmssrc->current_connection_uri_name) {
|
|
||||||
g_free (mmssrc->current_connection_uri_name);
|
|
||||||
mmssrc->current_connection_uri_name = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mmssrc->uri_name) {
|
|
||||||
g_free (mmssrc->uri_name);
|
|
||||||
mmssrc->uri_name = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
G_OBJECT_CLASS (parent_class)->finalize (gobject);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* FIXME operating in TIME rather than BYTES could remove this altogether
|
|
||||||
* and be more convenient elsewhere */
|
|
||||||
static gboolean
|
|
||||||
gst_mms_query (GstBaseSrc * src, GstQuery * query)
|
|
||||||
{
|
|
||||||
GstMMS *mmssrc = GST_MMS (src);
|
|
||||||
gboolean res = TRUE;
|
|
||||||
GstFormat format;
|
|
||||||
gint64 value;
|
|
||||||
|
|
||||||
switch (GST_QUERY_TYPE (query)) {
|
|
||||||
case GST_QUERY_POSITION:
|
|
||||||
gst_query_parse_position (query, &format, &value);
|
|
||||||
if (format != GST_FORMAT_BYTES) {
|
|
||||||
res = FALSE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
value = (gint64) mmsx_get_current_pos (mmssrc->connection);
|
|
||||||
gst_query_set_position (query, format, value);
|
|
||||||
break;
|
|
||||||
case GST_QUERY_DURATION:
|
|
||||||
if (!mmsx_get_seekable (mmssrc->connection)) {
|
|
||||||
res = FALSE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
gst_query_parse_duration (query, &format, &value);
|
|
||||||
switch (format) {
|
|
||||||
case GST_FORMAT_BYTES:
|
|
||||||
value = (gint64) mmsx_get_length (mmssrc->connection);
|
|
||||||
gst_query_set_duration (query, format, value);
|
|
||||||
break;
|
|
||||||
case GST_FORMAT_TIME:
|
|
||||||
value = mmsx_get_time_length (mmssrc->connection) * GST_SECOND;
|
|
||||||
gst_query_set_duration (query, format, value);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
res = FALSE;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/* chain to parent */
|
|
||||||
res =
|
|
||||||
GST_BASE_SRC_CLASS (parent_class)->query (GST_BASE_SRC (src), query);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gst_mms_prepare_seek_segment (GstBaseSrc * src, GstEvent * event,
|
|
||||||
GstSegment * segment)
|
|
||||||
{
|
|
||||||
GstSeekType cur_type, stop_type;
|
|
||||||
gint64 cur, stop;
|
|
||||||
GstSeekFlags flags;
|
|
||||||
GstFormat seek_format;
|
|
||||||
gdouble rate;
|
|
||||||
|
|
||||||
gst_event_parse_seek (event, &rate, &seek_format, &flags,
|
|
||||||
&cur_type, &cur, &stop_type, &stop);
|
|
||||||
|
|
||||||
if (seek_format != GST_FORMAT_BYTES && seek_format != GST_FORMAT_TIME) {
|
|
||||||
GST_LOG_OBJECT (src, "Only byte or time seeking is supported");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stop_type != GST_SEEK_TYPE_NONE) {
|
|
||||||
GST_LOG_OBJECT (src, "Stop seeking not supported");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cur_type != GST_SEEK_TYPE_NONE && cur_type != GST_SEEK_TYPE_SET) {
|
|
||||||
GST_LOG_OBJECT (src, "Only absolute seeking is supported");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We would like to convert from GST_FORMAT_TIME to GST_FORMAT_BYTES here
|
|
||||||
when needed, but we cannot as to do that we need to actually do the seek,
|
|
||||||
so we handle this in do_seek instead. */
|
|
||||||
|
|
||||||
/* FIXME implement relative seeking, we could do any needed relevant
|
|
||||||
seeking calculations here (in seek_format metrics), before the re-init
|
|
||||||
of the segment. */
|
|
||||||
|
|
||||||
gst_segment_init (segment, seek_format);
|
|
||||||
gst_segment_do_seek (segment, rate, seek_format, flags, cur_type, cur,
|
|
||||||
stop_type, stop, NULL);
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gst_mms_do_seek (GstBaseSrc * src, GstSegment * segment)
|
|
||||||
{
|
|
||||||
gint64 start;
|
|
||||||
GstMMS *mmssrc = GST_MMS (src);
|
|
||||||
|
|
||||||
if (segment->format == GST_FORMAT_TIME) {
|
|
||||||
if (!mmsx_time_seek (NULL, mmssrc->connection,
|
|
||||||
(double) segment->start / GST_SECOND)) {
|
|
||||||
GST_LOG_OBJECT (mmssrc, "mmsx_time_seek() failed");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
start = mmsx_get_current_pos (mmssrc->connection);
|
|
||||||
GST_INFO_OBJECT (mmssrc,
|
|
||||||
"performed seek to %" GST_TIME_FORMAT ", offset after " "seek: %"
|
|
||||||
G_GINT64_FORMAT, GST_TIME_ARGS (segment->start), start);
|
|
||||||
} else if (segment->format == GST_FORMAT_BYTES) {
|
|
||||||
start = mmsx_seek (NULL, mmssrc->connection, segment->start, SEEK_SET);
|
|
||||||
/* mmsx_seek will close and reopen the connection when seeking with the
|
|
||||||
mmsh protocol, if the reopening fails this is indicated with -1 */
|
|
||||||
if (start == -1) {
|
|
||||||
GST_DEBUG_OBJECT (mmssrc, "connection broken during seek");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
GST_INFO_OBJECT (mmssrc, "performed seek to: %" G_GINT64_FORMAT " bytes, "
|
|
||||||
"result: %" G_GINT64_FORMAT, segment->start, start);
|
|
||||||
} else {
|
|
||||||
GST_DEBUG_OBJECT (mmssrc, "unsupported seek segment format: %s",
|
|
||||||
GST_STR_NULL (gst_format_get_name (segment->format)));
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
gst_segment_init (segment, GST_FORMAT_BYTES);
|
|
||||||
gst_segment_do_seek (segment, segment->rate, GST_FORMAT_BYTES,
|
|
||||||
GST_SEEK_FLAG_NONE, GST_SEEK_TYPE_SET, start, GST_SEEK_TYPE_NONE,
|
|
||||||
segment->stop, NULL);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* get function
|
|
||||||
* this function generates new data when needed
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
static GstFlowReturn
|
|
||||||
gst_mms_create (GstPushSrc * psrc, GstBuffer ** buf)
|
|
||||||
{
|
|
||||||
GstMMS *mmssrc = GST_MMS (psrc);
|
|
||||||
guint8 *data;
|
|
||||||
guint blocksize;
|
|
||||||
gint result;
|
|
||||||
goffset offset;
|
|
||||||
|
|
||||||
*buf = NULL;
|
|
||||||
|
|
||||||
offset = mmsx_get_current_pos (mmssrc->connection);
|
|
||||||
|
|
||||||
/* Check if a seek perhaps has wrecked our connection */
|
|
||||||
if (offset == -1) {
|
|
||||||
GST_ERROR_OBJECT (mmssrc,
|
|
||||||
"connection broken (probably an error during mmsx_seek_time during a convert query) returning FLOW_ERROR");
|
|
||||||
return GST_FLOW_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Choose blocksize best for optimum performance */
|
|
||||||
if (offset == 0)
|
|
||||||
blocksize = mmsx_get_asf_header_len (mmssrc->connection);
|
|
||||||
else
|
|
||||||
blocksize = mmsx_get_asf_packet_len (mmssrc->connection);
|
|
||||||
|
|
||||||
data = g_try_malloc (blocksize);
|
|
||||||
if (!data) {
|
|
||||||
GST_ERROR_OBJECT (mmssrc, "Failed to allocate %u bytes", blocksize);
|
|
||||||
return GST_FLOW_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
GST_LOG_OBJECT (mmssrc, "reading %d bytes", blocksize);
|
|
||||||
result = mmsx_read (NULL, mmssrc->connection, (char *) data, blocksize);
|
|
||||||
/* EOS? */
|
|
||||||
if (result == 0)
|
|
||||||
goto eos;
|
|
||||||
|
|
||||||
*buf = gst_buffer_new_wrapped (data, result);
|
|
||||||
GST_BUFFER_OFFSET (*buf) = offset;
|
|
||||||
|
|
||||||
GST_LOG_OBJECT (mmssrc, "Returning buffer with offset %" G_GOFFSET_FORMAT
|
|
||||||
" and size %u", offset, result);
|
|
||||||
|
|
||||||
return GST_FLOW_OK;
|
|
||||||
|
|
||||||
eos:
|
|
||||||
{
|
|
||||||
GST_DEBUG_OBJECT (mmssrc, "EOS");
|
|
||||||
g_free (data);
|
|
||||||
*buf = NULL;
|
|
||||||
return GST_FLOW_EOS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gst_mms_is_seekable (GstBaseSrc * src)
|
|
||||||
{
|
|
||||||
GstMMS *mmssrc = GST_MMS (src);
|
|
||||||
|
|
||||||
return mmsx_get_seekable (mmssrc->connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gst_mms_get_size (GstBaseSrc * src, guint64 * size)
|
|
||||||
{
|
|
||||||
GstMMS *mmssrc = GST_MMS (src);
|
|
||||||
|
|
||||||
/* non seekable usually means live streams, and get_length() returns,
|
|
||||||
erm, interesting values for live streams */
|
|
||||||
if (!mmsx_get_seekable (mmssrc->connection))
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
*size = mmsx_get_length (mmssrc->connection);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gst_mms_start (GstBaseSrc * bsrc)
|
|
||||||
{
|
|
||||||
GstMMS *mms = GST_MMS (bsrc);
|
|
||||||
guint bandwidth_avail;
|
|
||||||
|
|
||||||
if (!mms->uri_name || *mms->uri_name == '\0')
|
|
||||||
goto no_uri;
|
|
||||||
|
|
||||||
if (mms->connection_speed)
|
|
||||||
bandwidth_avail = mms->connection_speed;
|
|
||||||
else
|
|
||||||
bandwidth_avail = G_MAXINT;
|
|
||||||
|
|
||||||
/* If we already have a connection, and the uri isn't changed, reuse it,
|
|
||||||
as connecting is expensive. */
|
|
||||||
if (mms->connection) {
|
|
||||||
if (!strcmp (mms->uri_name, mms->current_connection_uri_name)) {
|
|
||||||
GST_DEBUG_OBJECT (mms, "Reusing existing connection for %s",
|
|
||||||
mms->uri_name);
|
|
||||||
return TRUE;
|
|
||||||
} else {
|
|
||||||
mmsx_close (mms->connection);
|
|
||||||
g_free (mms->current_connection_uri_name);
|
|
||||||
mms->current_connection_uri_name = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* FIXME: pass some sane arguments here */
|
|
||||||
GST_DEBUG_OBJECT (mms,
|
|
||||||
"Trying mms_connect (%s) with bandwidth constraint of %d bps",
|
|
||||||
mms->uri_name, bandwidth_avail);
|
|
||||||
mms->connection = mmsx_connect (NULL, NULL, mms->uri_name, bandwidth_avail);
|
|
||||||
if (mms->connection) {
|
|
||||||
/* Save the uri name so that it can be checked for connection reusing,
|
|
||||||
see above. */
|
|
||||||
mms->current_connection_uri_name = g_strdup (mms->uri_name);
|
|
||||||
GST_DEBUG_OBJECT (mms, "Connect successful");
|
|
||||||
return TRUE;
|
|
||||||
} else {
|
|
||||||
gchar *url, *location;
|
|
||||||
|
|
||||||
GST_ERROR_OBJECT (mms,
|
|
||||||
"Could not connect to this stream, redirecting to rtsp");
|
|
||||||
location = strstr (mms->uri_name, "://");
|
|
||||||
if (location == NULL || *location == '\0' || *(location + 3) == '\0')
|
|
||||||
goto no_uri;
|
|
||||||
url = g_strdup_printf ("rtsp://%s", location + 3);
|
|
||||||
|
|
||||||
gst_element_post_message (GST_ELEMENT_CAST (mms),
|
|
||||||
gst_message_new_element (GST_OBJECT_CAST (mms),
|
|
||||||
gst_structure_new ("redirect", "new-location", G_TYPE_STRING, url,
|
|
||||||
NULL)));
|
|
||||||
|
|
||||||
/* post an error message as well, so that applications that don't handle
|
|
||||||
* redirect messages get to see a proper error message */
|
|
||||||
GST_ELEMENT_ERROR (mms, RESOURCE, OPEN_READ,
|
|
||||||
("Could not connect to streaming server."),
|
|
||||||
("A redirect message was posted on the bus and should have been "
|
|
||||||
"handled by the application."));
|
|
||||||
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
no_uri:
|
|
||||||
{
|
|
||||||
GST_ELEMENT_ERROR (mms, RESOURCE, OPEN_READ,
|
|
||||||
("No URI to open specified"), (NULL));
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gst_mms_stop (GstBaseSrc * bsrc)
|
|
||||||
{
|
|
||||||
GstMMS *mms = GST_MMS (bsrc);
|
|
||||||
|
|
||||||
if (mms->connection != NULL) {
|
|
||||||
/* Check if the connection is still pristine, that is if no more then
|
|
||||||
just the mmslib cached asf header has been read. If it is still pristine
|
|
||||||
preserve it as we often are re-started with the same URL and connecting
|
|
||||||
is expensive */
|
|
||||||
if (mmsx_get_current_pos (mms->connection) >
|
|
||||||
mmsx_get_asf_header_len (mms->connection)) {
|
|
||||||
mmsx_close (mms->connection);
|
|
||||||
mms->connection = NULL;
|
|
||||||
g_free (mms->current_connection_uri_name);
|
|
||||||
mms->current_connection_uri_name = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gst_mms_set_property (GObject * object, guint prop_id,
|
|
||||||
const GValue * value, GParamSpec * pspec)
|
|
||||||
{
|
|
||||||
GstMMS *mmssrc = GST_MMS (object);
|
|
||||||
|
|
||||||
switch (prop_id) {
|
|
||||||
case PROP_LOCATION:
|
|
||||||
gst_mms_uri_set_uri (GST_URI_HANDLER (mmssrc),
|
|
||||||
g_value_get_string (value), NULL);
|
|
||||||
break;
|
|
||||||
case PROP_CONNECTION_SPEED:
|
|
||||||
GST_OBJECT_LOCK (mmssrc);
|
|
||||||
mmssrc->connection_speed = g_value_get_uint64 (value) * 1000;
|
|
||||||
GST_OBJECT_UNLOCK (mmssrc);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gst_mms_get_property (GObject * object, guint prop_id,
|
|
||||||
GValue * value, GParamSpec * pspec)
|
|
||||||
{
|
|
||||||
GstMMS *mmssrc = GST_MMS (object);
|
|
||||||
|
|
||||||
GST_OBJECT_LOCK (mmssrc);
|
|
||||||
switch (prop_id) {
|
|
||||||
case PROP_LOCATION:
|
|
||||||
if (mmssrc->uri_name)
|
|
||||||
g_value_set_string (value, mmssrc->uri_name);
|
|
||||||
break;
|
|
||||||
case PROP_CONNECTION_SPEED:
|
|
||||||
g_value_set_uint64 (value, mmssrc->connection_speed / 1000);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
GST_OBJECT_UNLOCK (mmssrc);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* entry point to initialize the plug-in
|
|
||||||
* initialize the plug-in itself
|
|
||||||
* register the element factories and pad templates
|
|
||||||
* register the features
|
|
||||||
*/
|
|
||||||
static gboolean
|
|
||||||
plugin_init (GstPlugin * plugin)
|
|
||||||
{
|
|
||||||
return GST_ELEMENT_REGISTER (mmssrc, plugin);
|
|
||||||
}
|
|
||||||
|
|
||||||
static GstURIType
|
|
||||||
gst_mms_uri_get_type (GType type)
|
|
||||||
{
|
|
||||||
return GST_URI_SRC;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const gchar *const *
|
|
||||||
gst_mms_uri_get_protocols (GType type)
|
|
||||||
{
|
|
||||||
static const gchar *protocols[] = { "mms", "mmsh", "mmst", "mmsu", NULL };
|
|
||||||
|
|
||||||
return protocols;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gchar *
|
|
||||||
gst_mms_uri_get_uri (GstURIHandler * handler)
|
|
||||||
{
|
|
||||||
GstMMS *src = GST_MMS (handler);
|
|
||||||
|
|
||||||
/* FIXME: make thread-safe */
|
|
||||||
return g_strdup (src->uri_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
static gchar *
|
|
||||||
gst_mms_src_make_valid_uri (const gchar * uri)
|
|
||||||
{
|
|
||||||
gchar *protocol;
|
|
||||||
const gchar *colon, *tmp;
|
|
||||||
gsize len;
|
|
||||||
|
|
||||||
if (!uri || !gst_uri_is_valid (uri))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
protocol = gst_uri_get_protocol (uri);
|
|
||||||
|
|
||||||
if ((strcmp (protocol, "mms") != 0) && (strcmp (protocol, "mmsh") != 0) &&
|
|
||||||
(strcmp (protocol, "mmst") != 0) && (strcmp (protocol, "mmsu") != 0)) {
|
|
||||||
g_free (protocol);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
g_free (protocol);
|
|
||||||
|
|
||||||
colon = strstr (uri, "://");
|
|
||||||
if (!colon)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
tmp = colon + 3;
|
|
||||||
len = strlen (tmp);
|
|
||||||
if (len == 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
/* libmms segfaults if there's no hostname or
|
|
||||||
* no / after the hostname
|
|
||||||
*/
|
|
||||||
colon = strstr (tmp, "/");
|
|
||||||
if (colon == tmp)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (strstr (tmp, "/") == NULL) {
|
|
||||||
gchar *ret;
|
|
||||||
|
|
||||||
len = strlen (uri);
|
|
||||||
ret = g_malloc0 (len + 2);
|
|
||||||
memcpy (ret, uri, len);
|
|
||||||
ret[len] = '/';
|
|
||||||
return ret;
|
|
||||||
} else {
|
|
||||||
return g_strdup (uri);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gst_mms_uri_set_uri (GstURIHandler * handler, const gchar * uri,
|
|
||||||
GError ** error)
|
|
||||||
{
|
|
||||||
GstMMS *src = GST_MMS (handler);
|
|
||||||
gchar *fixed_uri;
|
|
||||||
|
|
||||||
fixed_uri = gst_mms_src_make_valid_uri (uri);
|
|
||||||
if (!fixed_uri && uri) {
|
|
||||||
g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI,
|
|
||||||
"Invalid MMS URI");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
GST_OBJECT_LOCK (src);
|
|
||||||
g_free (src->uri_name);
|
|
||||||
src->uri_name = fixed_uri;
|
|
||||||
GST_OBJECT_UNLOCK (src);
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gst_mms_uri_handler_init (gpointer g_iface, gpointer iface_data)
|
|
||||||
{
|
|
||||||
GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
|
|
||||||
|
|
||||||
iface->get_type = gst_mms_uri_get_type;
|
|
||||||
iface->get_protocols = gst_mms_uri_get_protocols;
|
|
||||||
iface->get_uri = gst_mms_uri_get_uri;
|
|
||||||
iface->set_uri = gst_mms_uri_set_uri;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* this is the structure that gst-register looks for
|
|
||||||
* so keep the name plugin_desc, or you cannot get your plug-in registered */
|
|
||||||
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
|
|
||||||
GST_VERSION_MINOR,
|
|
||||||
mms,
|
|
||||||
"Microsoft Multi Media Server streaming protocol support",
|
|
||||||
plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
|
|
|
@ -1,51 +0,0 @@
|
||||||
/*
|
|
||||||
* gstmms.h: header file for gst-mms plugin
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __GST_MMS_H__
|
|
||||||
#define __GST_MMS_H__
|
|
||||||
|
|
||||||
#include <gst/gst.h>
|
|
||||||
#include <libmms/mmsx.h>
|
|
||||||
#include <gst/base/gstpushsrc.h>
|
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
|
||||||
|
|
||||||
/* #define's don't like whitespacey bits */
|
|
||||||
#define GST_TYPE_MMS \
|
|
||||||
(gst_mms_get_type())
|
|
||||||
#define GST_MMS(obj) \
|
|
||||||
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_MMS,GstMMS))
|
|
||||||
#define GST_MMS_CLASS(klass) \
|
|
||||||
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_MMS,GstMMSClass))
|
|
||||||
#define GST_IS_MMS(obj) \
|
|
||||||
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_MMS))
|
|
||||||
#define GST_IS_MMS_CLASS(klass) \
|
|
||||||
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_MMS))
|
|
||||||
|
|
||||||
typedef struct _GstMMS GstMMS;
|
|
||||||
typedef struct _GstMMSClass GstMMSClass;
|
|
||||||
|
|
||||||
struct _GstMMS
|
|
||||||
{
|
|
||||||
GstPushSrc parent;
|
|
||||||
|
|
||||||
gchar *uri_name;
|
|
||||||
gchar *current_connection_uri_name;
|
|
||||||
guint64 connection_speed;
|
|
||||||
|
|
||||||
mmsx_t *connection;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct _GstMMSClass
|
|
||||||
{
|
|
||||||
GstPushSrcClass parent_class;
|
|
||||||
};
|
|
||||||
|
|
||||||
GType gst_mms_get_type (void);
|
|
||||||
|
|
||||||
GST_ELEMENT_REGISTER_DECLARE (mmssrc);
|
|
||||||
|
|
||||||
G_END_DECLS
|
|
||||||
|
|
||||||
#endif /* __GST_MMS_H__ */
|
|
|
@ -1,15 +0,0 @@
|
||||||
mms_dep = dependency('libmms', version : '>= 0.4', required : get_option('libmms'))
|
|
||||||
|
|
||||||
if mms_dep.found()
|
|
||||||
gstmms = library('gstmms',
|
|
||||||
'gstmms.c',
|
|
||||||
c_args : gst_plugins_bad_args,
|
|
||||||
link_args : noseh_link_args,
|
|
||||||
include_directories : [configinc],
|
|
||||||
dependencies : [gstbase_dep, mms_dep],
|
|
||||||
install : true,
|
|
||||||
install_dir : plugins_install_dir,
|
|
||||||
)
|
|
||||||
pkgconfig.generate(gstmms, install_dir : plugins_pkgconfig_install_dir)
|
|
||||||
plugins += [gstmms]
|
|
||||||
endif
|
|
|
@ -28,7 +28,6 @@ subdir('kate')
|
||||||
subdir('ladspa')
|
subdir('ladspa')
|
||||||
subdir('ldac')
|
subdir('ldac')
|
||||||
subdir('libde265')
|
subdir('libde265')
|
||||||
subdir('libmms')
|
|
||||||
subdir('lv2')
|
subdir('lv2')
|
||||||
subdir('mdns')
|
subdir('mdns')
|
||||||
subdir('modplug')
|
subdir('modplug')
|
||||||
|
|
|
@ -120,7 +120,6 @@ option('kms', type : 'feature', value : 'auto', description : 'KMS video sink pl
|
||||||
option('ladspa', type : 'feature', value : 'auto', description : 'LADSPA plugin bridge')
|
option('ladspa', type : 'feature', value : 'auto', description : 'LADSPA plugin bridge')
|
||||||
option('ldac', type : 'feature', value : 'auto', description : 'LDAC bluetooth audio codec plugin')
|
option('ldac', type : 'feature', value : 'auto', description : 'LDAC bluetooth audio codec plugin')
|
||||||
option('libde265', type : 'feature', value : 'auto', description : 'HEVC/H.265 video decoder plugin')
|
option('libde265', type : 'feature', value : 'auto', description : 'HEVC/H.265 video decoder plugin')
|
||||||
option('libmms', type : 'feature', value : 'auto', description : 'Microsoft multimedia server network source plugin')
|
|
||||||
option('openaptx', type : 'feature', value : 'auto', description : 'Open Source implementation of Audio Processing Technology codec (aptX) plugin')
|
option('openaptx', type : 'feature', value : 'auto', description : 'Open Source implementation of Audio Processing Technology codec (aptX) plugin')
|
||||||
option('lv2', type : 'feature', value : 'auto', description : 'LV2 audio plugin bridge')
|
option('lv2', type : 'feature', value : 'auto', description : 'LV2 audio plugin bridge')
|
||||||
option('mediafoundation', type : 'feature', value : 'auto', description : 'Microsoft Media Foundation plugin')
|
option('mediafoundation', type : 'feature', value : 'auto', description : 'Microsoft Media Foundation plugin')
|
||||||
|
|
Loading…
Reference in a new issue