458 lines
12 KiB
C
458 lines
12 KiB
C
/*
|
|
* This file is part of the Nice GLib ICE library.
|
|
*
|
|
* (C) 2006, 2007 Collabora Ltd.
|
|
* Contact: Dafydd Harries
|
|
* (C) 2006, 2007 Nokia Corporation. All rights reserved.
|
|
* Contact: Kai Vehmanen
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is the Nice GLib ICE library.
|
|
*
|
|
* The Initial Developers of the Original Code are Collabora Ltd and Nokia
|
|
* Corporation. All Rights Reserved.
|
|
*
|
|
* Contributors:
|
|
* Dafydd Harries, Collabora Ltd.
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of the
|
|
* the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
|
|
* case the provisions of LGPL are applicable instead of those above. If you
|
|
* wish to allow use of your version of this file only under the terms of the
|
|
* LGPL and not to allow others to use your version of this file under the
|
|
* MPL, indicate your decision by deleting the provisions above and replace
|
|
* them with the notice and other provisions required by the LGPL. If you do
|
|
* not delete the provisions above, a recipient may use your version of this
|
|
* file under either the MPL or the LGPL.
|
|
*/
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include <string.h>
|
|
|
|
#include "gstnicesrc.h"
|
|
|
|
GST_DEBUG_CATEGORY_STATIC (nicesrc_debug);
|
|
#define GST_CAT_DEFAULT nicesrc_debug
|
|
|
|
|
|
#define BUFFER_SIZE (65536)
|
|
|
|
static GstFlowReturn
|
|
gst_nice_src_create (
|
|
GstPushSrc *basesrc,
|
|
GstBuffer **buffer);
|
|
|
|
static gboolean
|
|
gst_nice_src_unlock (
|
|
GstBaseSrc *basesrc);
|
|
|
|
static gboolean
|
|
gst_nice_src_unlock_stop (
|
|
GstBaseSrc *basesrc);
|
|
|
|
static void
|
|
gst_nice_src_set_property (
|
|
GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec);
|
|
|
|
static void
|
|
gst_nice_src_get_property (
|
|
GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec);
|
|
|
|
|
|
static void
|
|
gst_nice_src_dispose (GObject *object);
|
|
|
|
static GstStateChangeReturn
|
|
gst_nice_src_change_state (
|
|
GstElement * element,
|
|
GstStateChange transition);
|
|
|
|
static GstStaticPadTemplate gst_nice_src_src_template =
|
|
GST_STATIC_PAD_TEMPLATE (
|
|
"src",
|
|
GST_PAD_SRC,
|
|
GST_PAD_ALWAYS,
|
|
GST_STATIC_CAPS_ANY);
|
|
|
|
G_DEFINE_TYPE (GstNiceSrc, gst_nice_src, GST_TYPE_PUSH_SRC);
|
|
|
|
enum
|
|
{
|
|
PROP_AGENT = 1,
|
|
PROP_STREAM,
|
|
PROP_COMPONENT
|
|
};
|
|
|
|
|
|
static void
|
|
gst_nice_src_class_init (GstNiceSrcClass *klass)
|
|
{
|
|
GstPushSrcClass *gstpushsrc_class;
|
|
GstBaseSrcClass *gstbasesrc_class;
|
|
GstElementClass *gstelement_class;
|
|
GObjectClass *gobject_class;
|
|
|
|
GST_DEBUG_CATEGORY_INIT (nicesrc_debug, "nicesrc",
|
|
0, "libnice source");
|
|
|
|
gstpushsrc_class = (GstPushSrcClass *) klass;
|
|
gstpushsrc_class->create = GST_DEBUG_FUNCPTR (gst_nice_src_create);
|
|
|
|
gstbasesrc_class = (GstBaseSrcClass *) klass;
|
|
gstbasesrc_class->unlock = GST_DEBUG_FUNCPTR (gst_nice_src_unlock);
|
|
gstbasesrc_class->unlock_stop = GST_DEBUG_FUNCPTR (gst_nice_src_unlock_stop);
|
|
|
|
gobject_class = (GObjectClass *) klass;
|
|
gobject_class->set_property = gst_nice_src_set_property;
|
|
gobject_class->get_property = gst_nice_src_get_property;
|
|
gobject_class->dispose = gst_nice_src_dispose;
|
|
|
|
gstelement_class = (GstElementClass *) klass;
|
|
gstelement_class->change_state = gst_nice_src_change_state;
|
|
|
|
gst_element_class_add_pad_template (gstelement_class,
|
|
gst_static_pad_template_get (&gst_nice_src_src_template));
|
|
gst_element_class_set_metadata (gstelement_class,
|
|
"ICE source",
|
|
"Source",
|
|
"Interactive UDP connectivity establishment",
|
|
"Dafydd Harries <dafydd.harries@collabora.co.uk>");
|
|
|
|
g_object_class_install_property (gobject_class, PROP_AGENT,
|
|
g_param_spec_object (
|
|
"agent",
|
|
"Agent",
|
|
"The NiceAgent this source is bound to",
|
|
NICE_TYPE_AGENT,
|
|
G_PARAM_READWRITE));
|
|
|
|
g_object_class_install_property (gobject_class, PROP_STREAM,
|
|
g_param_spec_uint (
|
|
"stream",
|
|
"Stream ID",
|
|
"The ID of the stream to read from",
|
|
0,
|
|
G_MAXUINT,
|
|
0,
|
|
G_PARAM_READWRITE));
|
|
|
|
g_object_class_install_property (gobject_class, PROP_COMPONENT,
|
|
g_param_spec_uint (
|
|
"component",
|
|
"Component ID",
|
|
"The ID of the component to read from",
|
|
0,
|
|
G_MAXUINT,
|
|
0,
|
|
G_PARAM_READWRITE));
|
|
}
|
|
|
|
static void
|
|
gst_nice_src_init (GstNiceSrc *src)
|
|
{
|
|
gst_base_src_set_live (GST_BASE_SRC (src), TRUE);
|
|
gst_base_src_set_format (GST_BASE_SRC (src), GST_FORMAT_TIME);
|
|
gst_base_src_set_do_timestamp (GST_BASE_SRC (src), TRUE);
|
|
src->agent = NULL;
|
|
src->stream_id = 0;
|
|
src->component_id = 0;
|
|
src->mainctx = g_main_context_new ();
|
|
src->mainloop = g_main_loop_new (src->mainctx, FALSE);
|
|
src->unlocked = FALSE;
|
|
src->idle_source = NULL;
|
|
src->outbufs = g_queue_new ();
|
|
}
|
|
|
|
static void
|
|
gst_nice_src_read_callback (NiceAgent *agent,
|
|
guint stream_id,
|
|
guint component_id,
|
|
guint len,
|
|
gchar *buf,
|
|
gpointer data)
|
|
{
|
|
GstBaseSrc *basesrc = GST_BASE_SRC (data);
|
|
GstNiceSrc *nicesrc = GST_NICE_SRC (basesrc);
|
|
GstBuffer *buffer = NULL;
|
|
|
|
GST_LOG_OBJECT (agent, "Got buffer, getting out of the main loop");
|
|
|
|
buffer = gst_buffer_new_allocate (NULL, len, NULL);
|
|
gst_buffer_fill (buffer, 0, buf, len);
|
|
GST_OBJECT_LOCK (nicesrc);
|
|
g_queue_push_tail (nicesrc->outbufs, buffer);
|
|
g_main_loop_quit (nicesrc->mainloop);
|
|
GST_OBJECT_UNLOCK (nicesrc);
|
|
}
|
|
|
|
static gboolean
|
|
gst_nice_src_unlock_idler (gpointer data)
|
|
{
|
|
GstNiceSrc *nicesrc = GST_NICE_SRC (data);
|
|
|
|
GST_OBJECT_LOCK (nicesrc);
|
|
if (nicesrc->unlocked)
|
|
g_main_loop_quit (nicesrc->mainloop);
|
|
|
|
if (nicesrc->idle_source) {
|
|
g_source_destroy (nicesrc->idle_source);
|
|
g_source_unref (nicesrc->idle_source);
|
|
nicesrc->idle_source = NULL;
|
|
}
|
|
GST_OBJECT_UNLOCK (nicesrc);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_nice_src_unlock (GstBaseSrc *src)
|
|
{
|
|
GstNiceSrc *nicesrc = GST_NICE_SRC (src);
|
|
|
|
GST_OBJECT_LOCK (src);
|
|
nicesrc->unlocked = TRUE;
|
|
|
|
g_main_loop_quit (nicesrc->mainloop);
|
|
|
|
if (!nicesrc->idle_source) {
|
|
nicesrc->idle_source = g_idle_source_new ();
|
|
g_source_set_priority (nicesrc->idle_source, G_PRIORITY_HIGH);
|
|
g_source_set_callback (nicesrc->idle_source, gst_nice_src_unlock_idler, src, NULL);
|
|
g_source_attach (nicesrc->idle_source, g_main_loop_get_context (nicesrc->mainloop));
|
|
}
|
|
GST_OBJECT_UNLOCK (src);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_nice_src_unlock_stop (GstBaseSrc *src)
|
|
{
|
|
GstNiceSrc *nicesrc = GST_NICE_SRC (src);
|
|
|
|
GST_OBJECT_LOCK (src);
|
|
nicesrc->unlocked = FALSE;
|
|
if (nicesrc->idle_source) {
|
|
g_source_destroy (nicesrc->idle_source);
|
|
g_source_unref(nicesrc->idle_source);
|
|
}
|
|
nicesrc->idle_source = NULL;
|
|
GST_OBJECT_UNLOCK (src);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static GstFlowReturn
|
|
gst_nice_src_create (
|
|
GstPushSrc *basesrc,
|
|
GstBuffer **buffer)
|
|
{
|
|
GstNiceSrc *nicesrc = GST_NICE_SRC (basesrc);
|
|
|
|
GST_LOG_OBJECT (nicesrc, "create called");
|
|
|
|
GST_OBJECT_LOCK (basesrc);
|
|
if (nicesrc->unlocked) {
|
|
GST_OBJECT_UNLOCK (basesrc);
|
|
return GST_FLOW_FLUSHING;
|
|
}
|
|
if (g_queue_is_empty (nicesrc->outbufs)) {
|
|
GST_OBJECT_UNLOCK (basesrc);
|
|
g_main_loop_run (nicesrc->mainloop);
|
|
GST_OBJECT_LOCK (basesrc);
|
|
}
|
|
|
|
*buffer = g_queue_pop_head (nicesrc->outbufs);
|
|
GST_OBJECT_UNLOCK (basesrc);
|
|
|
|
if (*buffer != NULL) {
|
|
GST_LOG_OBJECT (nicesrc, "Got buffer, pushing");
|
|
return GST_FLOW_OK;
|
|
} else {
|
|
GST_LOG_OBJECT (nicesrc, "Got interrupting, returning wrong-state");
|
|
return GST_FLOW_FLUSHING;
|
|
}
|
|
|
|
}
|
|
|
|
static void
|
|
gst_nice_src_dispose (GObject *object)
|
|
{
|
|
GstNiceSrc *src = GST_NICE_SRC (object);
|
|
|
|
if (src->agent)
|
|
g_object_unref (src->agent);
|
|
src->agent = NULL;
|
|
|
|
if (src->mainloop)
|
|
g_main_loop_unref (src->mainloop);
|
|
src->mainloop = NULL;
|
|
|
|
if (src->mainctx)
|
|
g_main_context_unref (src->mainctx);
|
|
src->mainctx = NULL;
|
|
|
|
if (src->outbufs) {
|
|
g_queue_free_full (src->outbufs, (GDestroyNotify) gst_buffer_unref);
|
|
}
|
|
src->outbufs = NULL;
|
|
|
|
if (src->idle_source) {
|
|
g_source_destroy (src->idle_source);
|
|
g_source_unref(src->idle_source);
|
|
}
|
|
src->idle_source = NULL;
|
|
|
|
G_OBJECT_CLASS (gst_nice_src_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
gst_nice_src_set_property (
|
|
GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GstNiceSrc *src = GST_NICE_SRC (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_AGENT:
|
|
if (src->agent)
|
|
GST_ERROR_OBJECT (object,
|
|
"Changing the agent on a nice src not allowed");
|
|
else
|
|
src->agent = g_value_dup_object (value);
|
|
break;
|
|
|
|
case PROP_STREAM:
|
|
src->stream_id = g_value_get_uint (value);
|
|
break;
|
|
|
|
case PROP_COMPONENT:
|
|
src->component_id = g_value_get_uint (value);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_nice_src_get_property (
|
|
GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GstNiceSrc *src = GST_NICE_SRC (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_AGENT:
|
|
g_value_set_object (value, src->agent);
|
|
break;
|
|
|
|
case PROP_STREAM:
|
|
g_value_set_uint (value, src->stream_id);
|
|
break;
|
|
|
|
case PROP_COMPONENT:
|
|
g_value_set_uint (value, src->component_id);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static GstStateChangeReturn
|
|
gst_nice_src_change_state (GstElement * element, GstStateChange transition)
|
|
{
|
|
GstNiceSrc *src;
|
|
GstStateChangeReturn ret;
|
|
|
|
src = GST_NICE_SRC (element);
|
|
|
|
switch (transition) {
|
|
case GST_STATE_CHANGE_NULL_TO_READY:
|
|
if (src->agent == NULL)
|
|
{
|
|
GST_ERROR_OBJECT (element,
|
|
"Trying to start Nice source without an agent set");
|
|
return GST_STATE_CHANGE_FAILURE;
|
|
}
|
|
else if (src->stream_id == 0)
|
|
{
|
|
GST_ERROR_OBJECT (element,
|
|
"Trying to start Nice source without a stream set");
|
|
return GST_STATE_CHANGE_FAILURE;
|
|
}
|
|
else if (src->component_id == 0)
|
|
{
|
|
GST_ERROR_OBJECT (element,
|
|
"Trying to start Nice source without a component set");
|
|
return GST_STATE_CHANGE_FAILURE;
|
|
}
|
|
break;
|
|
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
|
nice_agent_attach_recv (src->agent, src->stream_id, src->component_id,
|
|
src->mainctx, NULL, NULL);
|
|
GST_OBJECT_LOCK (src);
|
|
g_list_free_full (src->outbufs->head, (GDestroyNotify) gst_buffer_unref);
|
|
g_queue_init (src->outbufs);
|
|
GST_OBJECT_UNLOCK (src);
|
|
break;
|
|
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
|
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
|
|
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
|
|
case GST_STATE_CHANGE_READY_TO_NULL:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
ret = GST_ELEMENT_CLASS (gst_nice_src_parent_class)->change_state (element,
|
|
transition);
|
|
|
|
switch (transition) {
|
|
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
|
nice_agent_attach_recv (src->agent, src->stream_id, src->component_id,
|
|
src->mainctx, gst_nice_src_read_callback, (gpointer) src);
|
|
break;
|
|
case GST_STATE_CHANGE_NULL_TO_READY:
|
|
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
|
|
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
|
|
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
|
case GST_STATE_CHANGE_READY_TO_NULL:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
gboolean
|
|
gst_element_register_nicesrc (GstPlugin * plugin)
|
|
{
|
|
return gst_element_register (plugin, "nicesrc", GST_RANK_NONE,
|
|
GST_TYPE_NICE_SRC);
|
|
}
|