3 * This file is part of the Nice GLib ICE library.
5 * (C) 2006, 2007 Collabora Ltd.
6 * Contact: Dafydd Harries
7 * (C) 2006, 2007 Nokia Corporation. All rights reserved.
8 * Contact: Kai Vehmanen
10 * The contents of this file are subject to the Mozilla Public License Version
11 * 1.1 (the "License"); you may not use this file except in compliance with
12 * the License. You may obtain a copy of the License at
13 * http://www.mozilla.org/MPL/
15 * Software distributed under the License is distributed on an "AS IS" basis,
16 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
17 * for the specific language governing rights and limitations under the
20 * The Original Code is the Nice GLib ICE library.
22 * The Initial Developers of the Original Code are Collabora Ltd and Nokia
23 * Corporation. All Rights Reserved.
26 * Dafydd Harries, Collabora Ltd.
28 * Alternatively, the contents of this file may be used under the terms of the
29 * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
30 * case the provisions of LGPL are applicable instead of those above. If you
31 * wish to allow use of your version of this file only under the terms of the
32 * LGPL and not to allow others to use your version of this file under the
33 * MPL, indicate your decision by deleting the provisions above and replace
34 * them with the notice and other provisions required by the LGPL. If you do
35 * not delete the provisions above, a recipient may use your version of this
36 * file under either the MPL or the LGPL.
42 #include "gstnicesink.h"
45 GST_DEBUG_CATEGORY_STATIC (nicesink_debug);
46 #define GST_CAT_DEFAULT nicesink_debug
49 gst_nice_sink_render (
50 GstBaseSink *basesink,
52 #if GST_CHECK_VERSION (1,0,0)
54 gst_nice_sink_render_list (
55 GstBaseSink *basesink,
56 GstBufferList *buffer_list);
60 gst_nice_sink_unlock (GstBaseSink *basesink);
63 gst_nice_sink_unlock_stop (GstBaseSink *basesink);
66 _reliable_transport_writable (
73 gst_nice_sink_set_property (
80 gst_nice_sink_get_property (
87 gst_nice_sink_dispose (GObject *object);
88 #if GST_CHECK_VERSION (1,0,0)
90 gst_nice_sink_finalize (GObject *object);
93 static GstStateChangeReturn
94 gst_nice_sink_change_state (
96 GstStateChange transition);
98 static GstStaticPadTemplate gst_nice_sink_sink_template =
99 GST_STATIC_PAD_TEMPLATE (
103 GST_STATIC_CAPS_ANY);
105 G_DEFINE_TYPE (GstNiceSink, gst_nice_sink, GST_TYPE_BASE_SINK);
115 gst_nice_sink_class_init (GstNiceSinkClass *klass)
117 GstBaseSinkClass *gstbasesink_class;
118 GstElementClass *gstelement_class;
119 GObjectClass *gobject_class;
121 GST_DEBUG_CATEGORY_INIT (nicesink_debug, "nicesink",
124 gstbasesink_class = (GstBaseSinkClass *) klass;
125 gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_nice_sink_render);
126 #if GST_CHECK_VERSION (1,0,0)
127 gstbasesink_class->render_list = GST_DEBUG_FUNCPTR (gst_nice_sink_render_list);
129 gstbasesink_class->unlock = GST_DEBUG_FUNCPTR (gst_nice_sink_unlock);
130 gstbasesink_class->unlock_stop = GST_DEBUG_FUNCPTR (gst_nice_sink_unlock_stop);
132 gobject_class = (GObjectClass *) klass;
133 gobject_class->set_property = gst_nice_sink_set_property;
134 gobject_class->get_property = gst_nice_sink_get_property;
135 gobject_class->dispose = gst_nice_sink_dispose;
136 #if GST_CHECK_VERSION (1,0,0)
137 gobject_class->finalize = gst_nice_sink_finalize;
140 gstelement_class = (GstElementClass *) klass;
141 gstelement_class->change_state = gst_nice_sink_change_state;
143 gst_element_class_add_pad_template (gstelement_class,
144 gst_static_pad_template_get (&gst_nice_sink_sink_template));
145 #if GST_CHECK_VERSION (1,0,0)
146 gst_element_class_set_metadata (gstelement_class,
148 gst_element_class_set_details_simple (gstelement_class,
152 "Interactive UDP connectivity establishment",
153 "Dafydd Harries <dafydd.harries@collabora.co.uk>");
156 g_object_class_install_property (gobject_class, PROP_AGENT,
157 g_param_spec_object (
160 "The NiceAgent this source is bound to",
164 g_object_class_install_property (gobject_class, PROP_STREAM,
168 "The ID of the stream to read from",
174 g_object_class_install_property (gobject_class, PROP_COMPONENT,
178 "The ID of the component to read from",
186 gst_nice_sink_init (GstNiceSink *sink)
188 #if GST_CHECK_VERSION (1,0,0)
192 g_cond_init (&sink->writable_cond);
194 #if GST_CHECK_VERSION (1,0,0)
195 /* pre-allocate OutputVector, MapInfo and OutputMessage arrays
196 * for use in the render and render_list functions */
197 max_mem = gst_buffer_get_max_memory ();
199 sink->n_vecs = max_mem;
200 sink->vecs = g_new (GOutputVector, sink->n_vecs);
202 sink->n_maps = max_mem;
203 sink->maps = g_new (GstMapInfo, sink->n_maps);
205 sink->n_messages = 1;
206 sink->messages = g_new (NiceOutputMessage, sink->n_messages);
209 #if GST_CHECK_VERSION (1,12,0)
210 gst_base_sink_set_drop_out_of_segment (GST_BASE_SINK (sink), FALSE);
215 _reliable_transport_writable (NiceAgent *agent, guint stream_id,
216 guint component_id, GstNiceSink *sink)
218 GST_OBJECT_LOCK (sink);
219 if (stream_id == sink->stream_id && component_id == sink->component_id) {
220 g_cond_broadcast (&sink->writable_cond);
222 GST_OBJECT_UNLOCK (sink);
225 #if GST_CHECK_VERSION (1,0,0)
227 fill_vectors (GOutputVector * vecs, GstMapInfo * maps, guint n, GstBuffer * buf)
233 g_assert (gst_buffer_n_memory (buf) == n);
235 for (i = 0; i < n; ++i) {
236 mem = gst_buffer_peek_memory (buf, i);
237 if (gst_memory_map (mem, &maps[i], GST_MAP_READ)) {
238 vecs[i].buffer = maps[i].data;
239 vecs[i].size = maps[i].size;
241 GST_WARNING ("Failed to map memory %p for reading", mem);
245 size += vecs[i].size;
251 /* Buffer list code written by
252 * Tim-Philipp Müller <tim@centricular.com>
254 * gst-plugins-good/gst/udp/gstmultiudpsink.c
257 gst_nice_sink_render_buffers (GstNiceSink * sink, GstBuffer ** buffers,
258 guint num_buffers, guint8 * mem_nums, guint total_mem_num)
260 NiceOutputMessage *msgs;
262 GstMapInfo *map_infos;
266 GstFlowReturn flow_ret = GST_FLOW_OK;
268 GST_LOG_OBJECT (sink, "%u buffers, %u memories -> to be sent",
269 num_buffers, total_mem_num);
271 if (sink->n_vecs < total_mem_num) {
272 sink->n_vecs = GST_ROUND_UP_16 (total_mem_num);
274 sink->vecs = g_new (GOutputVector, sink->n_vecs);
278 if (sink->n_maps < total_mem_num) {
279 sink->n_maps = GST_ROUND_UP_16 (total_mem_num);
281 sink->maps = g_new (GstMapInfo, sink->n_maps);
283 map_infos = sink->maps;
285 if (sink->n_messages < num_buffers) {
286 sink->n_messages = GST_ROUND_UP_16 (num_buffers);
287 g_free (sink->messages);
288 sink->messages = g_new (NiceOutputMessage, sink->n_messages);
290 msgs = sink->messages;
292 for (i = 0, mem = 0; i < num_buffers; ++i) {
293 fill_vectors (&vecs[mem], &map_infos[mem], mem_nums[i], buffers[i]);
294 msgs[i].buffers = &vecs[mem];
295 msgs[i].n_buffers = mem_nums[i];
299 GST_OBJECT_LOCK (sink);
301 ret = nice_agent_send_messages_nonblocking(sink->agent, sink->stream_id,
302 sink->component_id, msgs + written, num_buffers - written, NULL, NULL);
307 if (sink->reliable && written < num_buffers)
308 g_cond_wait (&sink->writable_cond, GST_OBJECT_GET_LOCK (sink));
310 if (sink->flushing) {
311 flow_ret = GST_FLOW_FLUSHING;
314 } while (sink->reliable && written < num_buffers);
315 GST_OBJECT_UNLOCK (sink);
317 for (i = 0; i < mem; ++i)
318 gst_memory_unmap (map_infos[i].memory, &map_infos[i]);
325 gst_nice_sink_render (GstBaseSink *basesink, GstBuffer *buffer)
327 GstNiceSink *nicesink = GST_NICE_SINK (basesink);
328 GstFlowReturn flow_ret = GST_FLOW_OK;
329 #if GST_CHECK_VERSION (1,0,0)
332 n_mem = gst_buffer_n_memory (buffer);
335 flow_ret = gst_nice_sink_render_buffers (nicesink, &buffer, 1, &n_mem,
345 data = (gchar *) GST_BUFFER_DATA (buffer);
346 size = GST_BUFFER_SIZE (buffer);
348 GST_OBJECT_LOCK (nicesink);
350 ret = nice_agent_send (nicesink->agent, nicesink->stream_id,
351 nicesink->component_id, size - written, data + written);
355 if (nicesink->reliable && written < size)
356 g_cond_wait (&nicesink->writable_cond, GST_OBJECT_GET_LOCK (nicesink));
357 if (nicesink->flushing) {
358 flow_ret = GST_FLOW_WRONG_STATE;
361 } while (nicesink->reliable && written < size);
362 GST_OBJECT_UNLOCK (nicesink);
368 #if GST_CHECK_VERSION (1,0,0)
370 gst_nice_sink_render_list (GstBaseSink *basesink, GstBufferList *buffer_list)
372 GstNiceSink *nicesink = GST_NICE_SINK (basesink);
374 GstFlowReturn flow_ret = GST_FLOW_OK;
377 guint i, num_buffers;
379 num_buffers = gst_buffer_list_length (buffer_list);
380 if (num_buffers == 0)
383 buffers = g_newa (GstBuffer *, num_buffers);
384 mem_nums = g_newa (guint8, num_buffers);
385 for (i = 0, total_mems = 0; i < num_buffers; ++i) {
386 buffers[i] = gst_buffer_list_get (buffer_list, i);
387 mem_nums[i] = gst_buffer_n_memory (buffers[i]);
388 total_mems += mem_nums[i];
391 flow_ret = gst_nice_sink_render_buffers (nicesink, buffers, num_buffers,
392 mem_nums, total_mems);
398 GST_LOG_OBJECT (nicesink, "empty buffer");
406 static gboolean gst_nice_sink_unlock (GstBaseSink *basesink)
408 GstNiceSink *nicesink = GST_NICE_SINK (basesink);
410 GST_OBJECT_LOCK (nicesink);
411 nicesink->flushing = TRUE;
412 g_cond_broadcast (&nicesink->writable_cond);
413 GST_OBJECT_UNLOCK (nicesink);
418 static gboolean gst_nice_sink_unlock_stop (GstBaseSink *basesink)
420 GstNiceSink *nicesink = GST_NICE_SINK (basesink);
422 GST_OBJECT_LOCK (nicesink);
423 nicesink->flushing = FALSE;
424 GST_OBJECT_UNLOCK (nicesink);
430 gst_nice_sink_dispose (GObject *object)
432 GstNiceSink *sink = GST_NICE_SINK (object);
434 if (sink->agent && sink->writable_id)
435 g_signal_handler_disconnect (sink->agent, sink->writable_id);
436 sink->writable_id = 0;
437 g_clear_object (&sink->agent);
439 g_cond_clear (&sink->writable_cond);
441 G_OBJECT_CLASS (gst_nice_sink_parent_class)->dispose (object);
444 #if GST_CHECK_VERSION (1,0,0)
446 gst_nice_sink_finalize (GObject *object)
448 GstNiceSink *sink = GST_NICE_SINK (object);
456 g_free (sink->messages);
457 sink->messages = NULL;
458 sink->n_messages = 0;
460 G_OBJECT_CLASS (gst_nice_sink_parent_class)->finalize (object);
465 gst_nice_sink_set_property (
471 GstNiceSink *sink = GST_NICE_SINK (object);
477 GST_ERROR_OBJECT (object,
478 "Changing the agent on a nice sink not allowed");
480 sink->agent = g_value_dup_object (value);
481 g_object_get (sink->agent, "reliable", &sink->reliable, NULL);
483 sink->writable_id = g_signal_connect (sink->agent,
484 "reliable-transport-writable",
485 (GCallback) _reliable_transport_writable, sink);
490 GST_OBJECT_LOCK (sink);
491 sink->stream_id = g_value_get_uint (value);
492 GST_OBJECT_UNLOCK (sink);
497 guint new_component_id = g_value_get_uint (value);
498 GST_OBJECT_LOCK (sink);
499 if (sink->component_id != new_component_id) {
500 sink->component_id = new_component_id;
501 g_cond_broadcast (&sink->writable_cond);
503 GST_OBJECT_UNLOCK (sink);
508 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
514 gst_nice_sink_get_property (
520 GstNiceSink *sink = GST_NICE_SINK (object);
525 g_value_set_object (value, sink->agent);
529 GST_OBJECT_LOCK (sink);
530 g_value_set_uint (value, sink->stream_id);
531 GST_OBJECT_UNLOCK (sink);
535 GST_OBJECT_LOCK (sink);
536 g_value_set_uint (value, sink->component_id);
537 GST_OBJECT_UNLOCK (sink);
541 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
546 static GstStateChangeReturn
547 gst_nice_sink_change_state (GstElement * element, GstStateChange transition)
550 GstStateChangeReturn ret;
552 sink = GST_NICE_SINK (element);
554 switch (transition) {
555 case GST_STATE_CHANGE_NULL_TO_READY:
556 if (sink->agent == NULL)
558 GST_ERROR_OBJECT (element,
559 "Trying to start Nice sink without an agent set");
560 return GST_STATE_CHANGE_FAILURE;
562 else if (sink->stream_id == 0)
564 GST_ERROR_OBJECT (element,
565 "Trying to start Nice sink without a stream set");
566 return GST_STATE_CHANGE_FAILURE;
568 else if (sink->component_id == 0)
570 GST_ERROR_OBJECT (element,
571 "Trying to start Nice sink without a component set");
572 return GST_STATE_CHANGE_FAILURE;
575 case GST_STATE_CHANGE_READY_TO_PAUSED:
576 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
577 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
578 case GST_STATE_CHANGE_PAUSED_TO_READY:
579 case GST_STATE_CHANGE_READY_TO_NULL:
584 ret = GST_ELEMENT_CLASS (gst_nice_sink_parent_class)->change_state (element,