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);
211 _reliable_transport_writable (NiceAgent *agent, guint stream_id,
212 guint component_id, GstNiceSink *sink)
214 GST_OBJECT_LOCK (sink);
215 if (stream_id == sink->stream_id && component_id == sink->component_id) {
216 g_cond_broadcast (&sink->writable_cond);
218 GST_OBJECT_UNLOCK (sink);
221 #if GST_CHECK_VERSION (1,0,0)
223 fill_vectors (GOutputVector * vecs, GstMapInfo * maps, guint n, GstBuffer * buf)
229 g_assert_cmpuint (gst_buffer_n_memory (buf), ==, n);
231 for (i = 0; i < n; ++i) {
232 mem = gst_buffer_peek_memory (buf, i);
233 if (gst_memory_map (mem, &maps[i], GST_MAP_READ)) {
234 vecs[i].buffer = maps[i].data;
235 vecs[i].size = maps[i].size;
237 GST_WARNING ("Failed to map memory %p for reading", mem);
241 size += vecs[i].size;
247 /* Buffer list code written by
248 * Tim-Philipp Müller <tim@centricular.com>
250 * gst-plugins-good/gst/udp/gstmultiudpsink.c
253 gst_nice_sink_render_buffers (GstNiceSink * sink, GstBuffer ** buffers,
254 guint num_buffers, guint8 * mem_nums, guint total_mem_num)
256 NiceOutputMessage *msgs;
258 GstMapInfo *map_infos;
262 GstFlowReturn flow_ret = GST_FLOW_OK;
264 GST_LOG_OBJECT (sink, "%u buffers, %u memories -> to be sent",
265 num_buffers, total_mem_num);
267 if (sink->n_vecs < total_mem_num) {
268 sink->n_vecs = GST_ROUND_UP_16 (total_mem_num);
270 sink->vecs = g_new (GOutputVector, sink->n_vecs);
274 if (sink->n_maps < total_mem_num) {
275 sink->n_maps = GST_ROUND_UP_16 (total_mem_num);
277 sink->maps = g_new (GstMapInfo, sink->n_maps);
279 map_infos = sink->maps;
281 if (sink->n_messages < num_buffers) {
282 sink->n_messages = GST_ROUND_UP_16 (num_buffers);
283 g_free (sink->messages);
284 sink->messages = g_new (NiceOutputMessage, sink->n_messages);
286 msgs = sink->messages;
288 for (i = 0, mem = 0; i < num_buffers; ++i) {
289 fill_vectors (&vecs[mem], &map_infos[mem], mem_nums[i], buffers[i]);
290 msgs[i].buffers = &vecs[mem];
291 msgs[i].n_buffers = mem_nums[i];
295 GST_OBJECT_LOCK (sink);
297 ret = nice_agent_send_messages_nonblocking(sink->agent, sink->stream_id,
298 sink->component_id, msgs + written, num_buffers - written, NULL, NULL);
303 if (sink->reliable && written < num_buffers)
304 g_cond_wait (&sink->writable_cond, GST_OBJECT_GET_LOCK (sink));
306 if (sink->flushing) {
307 flow_ret = GST_FLOW_FLUSHING;
310 } while (sink->reliable && written < num_buffers);
311 GST_OBJECT_UNLOCK (sink);
313 for (i = 0; i < mem; ++i)
314 gst_memory_unmap (map_infos[i].memory, &map_infos[i]);
321 gst_nice_sink_render (GstBaseSink *basesink, GstBuffer *buffer)
323 GstNiceSink *nicesink = GST_NICE_SINK (basesink);
324 GstFlowReturn flow_ret = GST_FLOW_OK;
325 #if GST_CHECK_VERSION (1,0,0)
328 n_mem = gst_buffer_n_memory (buffer);
331 flow_ret = gst_nice_sink_render_buffers (nicesink, &buffer, 1, &n_mem,
341 data = (gchar *) GST_BUFFER_DATA (buffer);
342 size = GST_BUFFER_SIZE (buffer);
344 GST_OBJECT_LOCK (nicesink);
346 ret = nice_agent_send (nicesink->agent, nicesink->stream_id,
347 nicesink->component_id, size - written, data + written);
351 if (nicesink->reliable && written < size)
352 g_cond_wait (&nicesink->writable_cond, GST_OBJECT_GET_LOCK (nicesink));
353 if (nicesink->flushing) {
354 flow_ret = GST_FLOW_WRONG_STATE;
357 } while (nicesink->reliable && written < size);
358 GST_OBJECT_UNLOCK (nicesink);
364 #if GST_CHECK_VERSION (1,0,0)
366 gst_nice_sink_render_list (GstBaseSink *basesink, GstBufferList *buffer_list)
368 GstNiceSink *nicesink = GST_NICE_SINK (basesink);
370 GstFlowReturn flow_ret = GST_FLOW_OK;
373 guint i, num_buffers;
375 num_buffers = gst_buffer_list_length (buffer_list);
376 if (num_buffers == 0)
379 buffers = g_newa (GstBuffer *, num_buffers);
380 mem_nums = g_newa (guint8, num_buffers);
381 for (i = 0, total_mems = 0; i < num_buffers; ++i) {
382 buffers[i] = gst_buffer_list_get (buffer_list, i);
383 mem_nums[i] = gst_buffer_n_memory (buffers[i]);
384 total_mems += mem_nums[i];
387 flow_ret = gst_nice_sink_render_buffers (nicesink, buffers, num_buffers,
388 mem_nums, total_mems);
394 GST_LOG_OBJECT (nicesink, "empty buffer");
402 static gboolean gst_nice_sink_unlock (GstBaseSink *basesink)
404 GstNiceSink *nicesink = GST_NICE_SINK (basesink);
406 GST_OBJECT_LOCK (nicesink);
407 nicesink->flushing = TRUE;
408 g_cond_broadcast (&nicesink->writable_cond);
409 GST_OBJECT_UNLOCK (nicesink);
414 static gboolean gst_nice_sink_unlock_stop (GstBaseSink *basesink)
416 GstNiceSink *nicesink = GST_NICE_SINK (basesink);
418 GST_OBJECT_LOCK (nicesink);
419 nicesink->flushing = FALSE;
420 GST_OBJECT_UNLOCK (nicesink);
426 gst_nice_sink_dispose (GObject *object)
428 GstNiceSink *sink = GST_NICE_SINK (object);
430 if (sink->agent && sink->writable_id)
431 g_signal_handler_disconnect (sink->agent, sink->writable_id);
432 sink->writable_id = 0;
433 g_clear_object (&sink->agent);
435 g_cond_clear (&sink->writable_cond);
437 G_OBJECT_CLASS (gst_nice_sink_parent_class)->dispose (object);
440 #if GST_CHECK_VERSION (1,0,0)
442 gst_nice_sink_finalize (GObject *object)
444 GstNiceSink *sink = GST_NICE_SINK (object);
452 g_free (sink->messages);
453 sink->messages = NULL;
454 sink->n_messages = 0;
456 G_OBJECT_CLASS (gst_nice_sink_parent_class)->finalize (object);
461 gst_nice_sink_set_property (
467 GstNiceSink *sink = GST_NICE_SINK (object);
473 GST_ERROR_OBJECT (object,
474 "Changing the agent on a nice sink not allowed");
476 sink->agent = g_value_dup_object (value);
477 g_object_get (sink->agent, "reliable", &sink->reliable, NULL);
479 sink->writable_id = g_signal_connect (sink->agent,
480 "reliable-transport-writable",
481 (GCallback) _reliable_transport_writable, sink);
486 GST_OBJECT_LOCK (sink);
487 sink->stream_id = g_value_get_uint (value);
488 GST_OBJECT_UNLOCK (sink);
493 guint new_component_id = g_value_get_uint (value);
494 GST_OBJECT_LOCK (sink);
495 if (sink->component_id != new_component_id) {
496 sink->component_id = new_component_id;
497 g_cond_broadcast (&sink->writable_cond);
499 GST_OBJECT_UNLOCK (sink);
504 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
510 gst_nice_sink_get_property (
516 GstNiceSink *sink = GST_NICE_SINK (object);
521 g_value_set_object (value, sink->agent);
525 GST_OBJECT_LOCK (sink);
526 g_value_set_uint (value, sink->stream_id);
527 GST_OBJECT_UNLOCK (sink);
531 GST_OBJECT_LOCK (sink);
532 g_value_set_uint (value, sink->component_id);
533 GST_OBJECT_UNLOCK (sink);
537 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
542 static GstStateChangeReturn
543 gst_nice_sink_change_state (GstElement * element, GstStateChange transition)
546 GstStateChangeReturn ret;
548 sink = GST_NICE_SINK (element);
550 switch (transition) {
551 case GST_STATE_CHANGE_NULL_TO_READY:
552 if (sink->agent == NULL)
554 GST_ERROR_OBJECT (element,
555 "Trying to start Nice sink without an agent set");
556 return GST_STATE_CHANGE_FAILURE;
558 else if (sink->stream_id == 0)
560 GST_ERROR_OBJECT (element,
561 "Trying to start Nice sink without a stream set");
562 return GST_STATE_CHANGE_FAILURE;
564 else if (sink->component_id == 0)
566 GST_ERROR_OBJECT (element,
567 "Trying to start Nice sink without a component set");
568 return GST_STATE_CHANGE_FAILURE;
571 case GST_STATE_CHANGE_READY_TO_PAUSED:
572 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
573 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
574 case GST_STATE_CHANGE_PAUSED_TO_READY:
575 case GST_STATE_CHANGE_READY_TO_NULL:
580 ret = GST_ELEMENT_CLASS (gst_nice_sink_parent_class)->change_state (element,