|
1 /* GStreamer |
|
2 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> |
|
3 * Copyright (C) <2004> Thomas Vander Stichele <thomas at apestaart dot org> |
|
4 * Copyright (C) 2006 Wim Taymans <wim at fluendo dot com> |
|
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., 59 Temple Place - Suite 330, |
|
19 * Boston, MA 02111-1307, USA. |
|
20 */ |
|
21 |
|
22 /** |
|
23 * SECTION:element-multifdsink |
|
24 * @short_description: Send data to multiple file descriptors |
|
25 * @see_also: tcpserversink |
|
26 * |
|
27 * <refsect2> |
|
28 * <para> |
|
29 * This plugin writes incoming data to a set of file descriptors. The |
|
30 * file descriptors can be added to multifdsink by emitting the "add" signal. |
|
31 * For each descriptor added, the "client-added" signal will be called. |
|
32 * </para> |
|
33 * <para> |
|
34 * As of version 0.10.8, a client can also be added with the "add-full" signal |
|
35 * that allows for more control over what and how much data a client |
|
36 * initially receives. |
|
37 * </para> |
|
38 * <para> |
|
39 * Clients can be removed from multifdsink by emitting the "remove" signal. For |
|
40 * each descriptor removed, the "client-removed" signal will be called. The |
|
41 * "client-removed" signal can also be fired when multifdsink decides that a |
|
42 * client is not active anymore or, depending on the value of the |
|
43 * "recover-policy" property, if the client is reading too slowly. |
|
44 * In all cases, multifdsink will never close a file descriptor itself. |
|
45 * The user of multifdsink is responsible for closing all file descriptors. |
|
46 * This can for example be done in response to the "client-fd-removed" signal. |
|
47 * Note that multifdsink still has a reference to the file descriptor when the |
|
48 * "client-removed" signal is emitted, so that "get-stats" can be performed on |
|
49 * the descriptor; it is therefore not safe to close the file descriptor in |
|
50 * the "client-removed" signal handler, and you should use the |
|
51 * "client-fd-removed" signal to safely close the fd. |
|
52 * </para> |
|
53 * <para> |
|
54 * Multifdsink internally keeps a queue of the incoming buffers and uses a |
|
55 * separate thread to send the buffers to the clients. This ensures that no |
|
56 * client write can block the pipeline and that clients can read with different |
|
57 * speeds. |
|
58 * </para> |
|
59 * <para> |
|
60 * When adding a client to multifdsink, the "sync-method" property will define |
|
61 * which buffer in the queued buffers will be sent first to the client. Clients |
|
62 * can be sent the most recent buffer (which might not be decodable by the |
|
63 * client if it is not a keyframe), the next keyframe received in |
|
64 * multifdsink (which can take some time depending on the keyframe rate), or the |
|
65 * last received keyframe (which will cause a simple burst-on-connect). |
|
66 * Multifdsink will always keep at least one keyframe in its internal buffers |
|
67 * when the sync-mode is set to latest-keyframe. |
|
68 * </para> |
|
69 * <para> |
|
70 * As of version 0.10.8, there are additional values for the sync-method |
|
71 * property to allow finer control over burst-on-connect behaviour. By selecting |
|
72 * the 'burst' method a minimum burst size can be chosen, 'burst-keyframe' |
|
73 * additionally requires that the burst begin with a keyframe, and |
|
74 * 'burst-with-keyframe' attempts to burst beginning with a keyframe, but will |
|
75 * prefer a minimum burst size even if it requires not starting with a keyframe. |
|
76 * </para> |
|
77 * <para> |
|
78 * Multifdsink can be instructed to keep at least a minimum amount of data |
|
79 * expressed in time or byte units in its internal queues with the the |
|
80 * "time-min" and "bytes-min" properties respectively. These properties are |
|
81 * useful if the application adds clients with the "add-full" signal to |
|
82 * make sure that a burst connect can actually be honored. |
|
83 * </para> |
|
84 * <para> |
|
85 * When streaming data, clients are allowed to read at a different rate than |
|
86 * the rate at which multifdsink receives data. If the client is reading too |
|
87 * fast, no data will be send to the client until multifdsink receives more |
|
88 * data. If the client, however, reads too slowly, data for that client will be |
|
89 * queued up in multifdsink. Two properties control the amount of data |
|
90 * (buffers) that is queued in multifdsink: "buffers-max" and |
|
91 * "buffers-soft-max". A client that falls behind by "buffers-max" is removed |
|
92 * from multifdsink forcibly. |
|
93 * </para> |
|
94 * <para> |
|
95 * A client with a lag of at least "buffers-soft-max" enters the recovery |
|
96 * procedure which is controlled with the "recover-policy" property. A recover |
|
97 * policy of NONE will do nothing, RESYNC_LATEST will send the most recently |
|
98 * received buffer as the next buffer for the client, RESYNC_SOFT_LIMIT |
|
99 * positions the client to the soft limit in the buffer queue and |
|
100 * RESYNC_KEYFRAME positions the client at the most recent keyframe in the |
|
101 * buffer queue. |
|
102 * </para> |
|
103 * <para> |
|
104 * multifdsink will by default synchronize on the clock before serving the |
|
105 * buffers to the clients. This behaviour can be disabled by setting the sync |
|
106 * property to FALSE. Multifdsink will by default not do QoS and will never |
|
107 * drop late buffers. |
|
108 * </para> |
|
109 * </refsect2> |
|
110 * |
|
111 * Last reviewed on 2006-09-12 (0.10.10) |
|
112 */ |
|
113 |
|
114 #ifdef HAVE_CONFIG_H |
|
115 #include "config.h" |
|
116 #endif |
|
117 #include <gst/gst-i18n-plugin.h> |
|
118 |
|
119 #include <sys/ioctl.h> |
|
120 #include <unistd.h> |
|
121 #include <fcntl.h> |
|
122 #include <sys/types.h> |
|
123 #include <sys/socket.h> |
|
124 #include <sys/stat.h> |
|
125 |
|
126 #ifdef HAVE_FIONREAD_IN_SYS_FILIO |
|
127 #include <sys/filio.h> |
|
128 #endif |
|
129 |
|
130 #include "gstmultifdsink.h" |
|
131 #include "gsttcp-marshal.h" |
|
132 |
|
133 #define NOT_IMPLEMENTED 0 |
|
134 |
|
135 /* elementfactory information */ |
|
136 static const GstElementDetails gst_multi_fd_sink_details = |
|
137 GST_ELEMENT_DETAILS ("Multi filedescriptor sink", |
|
138 "Sink/Network", |
|
139 "Send data to multiple filedescriptors", |
|
140 "Thomas Vander Stichele <thomas at apestaart dot org>, " |
|
141 "Wim Taymans <wim@fluendo.com>"); |
|
142 |
|
143 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink", |
|
144 GST_PAD_SINK, |
|
145 GST_PAD_ALWAYS, |
|
146 GST_STATIC_CAPS_ANY); |
|
147 |
|
148 GST_DEBUG_CATEGORY_STATIC (multifdsink_debug); |
|
149 #define GST_CAT_DEFAULT (multifdsink_debug) |
|
150 |
|
151 /* MultiFdSink signals and args */ |
|
152 enum |
|
153 { |
|
154 /* methods */ |
|
155 SIGNAL_ADD, |
|
156 SIGNAL_ADD_BURST, |
|
157 SIGNAL_REMOVE, |
|
158 SIGNAL_REMOVE_FLUSH, |
|
159 SIGNAL_CLEAR, |
|
160 SIGNAL_GET_STATS, |
|
161 |
|
162 /* signals */ |
|
163 SIGNAL_CLIENT_ADDED, |
|
164 SIGNAL_CLIENT_REMOVED, |
|
165 SIGNAL_CLIENT_FD_REMOVED, |
|
166 |
|
167 LAST_SIGNAL |
|
168 }; |
|
169 |
|
170 |
|
171 /* this is really arbitrarily chosen */ |
|
172 #define DEFAULT_PROTOCOL GST_TCP_PROTOCOL_NONE |
|
173 #define DEFAULT_MODE 1 |
|
174 #define DEFAULT_BUFFERS_MAX -1 |
|
175 #define DEFAULT_BUFFERS_SOFT_MAX -1 |
|
176 #define DEFAULT_TIME_MIN -1 |
|
177 #define DEFAULT_BYTES_MIN -1 |
|
178 #define DEFAULT_BUFFERS_MIN -1 |
|
179 #define DEFAULT_UNIT_TYPE GST_UNIT_TYPE_BUFFERS |
|
180 #define DEFAULT_UNITS_MAX -1 |
|
181 #define DEFAULT_UNITS_SOFT_MAX -1 |
|
182 #define DEFAULT_RECOVER_POLICY GST_RECOVER_POLICY_NONE |
|
183 #define DEFAULT_TIMEOUT 0 |
|
184 #define DEFAULT_SYNC_METHOD GST_SYNC_METHOD_LATEST |
|
185 |
|
186 #define DEFAULT_BURST_UNIT GST_UNIT_TYPE_UNDEFINED |
|
187 #define DEFAULT_BURST_VALUE 0 |
|
188 |
|
189 enum |
|
190 { |
|
191 PROP_0, |
|
192 PROP_PROTOCOL, |
|
193 PROP_MODE, |
|
194 PROP_BUFFERS_QUEUED, |
|
195 PROP_BYTES_QUEUED, |
|
196 PROP_TIME_QUEUED, |
|
197 |
|
198 PROP_UNIT_TYPE, |
|
199 PROP_UNITS_MAX, |
|
200 PROP_UNITS_SOFT_MAX, |
|
201 |
|
202 PROP_BUFFERS_MAX, |
|
203 PROP_BUFFERS_SOFT_MAX, |
|
204 |
|
205 PROP_TIME_MIN, |
|
206 PROP_BYTES_MIN, |
|
207 PROP_BUFFERS_MIN, |
|
208 |
|
209 PROP_RECOVER_POLICY, |
|
210 PROP_TIMEOUT, |
|
211 PROP_SYNC_METHOD, |
|
212 PROP_BYTES_TO_SERVE, |
|
213 PROP_BYTES_SERVED, |
|
214 |
|
215 PROP_BURST_UNIT, |
|
216 PROP_BURST_VALUE, |
|
217 }; |
|
218 |
|
219 /* For backward compat, we can't really select the poll mode anymore with |
|
220 * GstPoll. */ |
|
221 #define GST_TYPE_FDSET_MODE (gst_fdset_mode_get_type()) |
|
222 static GType |
|
223 gst_fdset_mode_get_type (void) |
|
224 { |
|
225 static GType fdset_mode_type = 0; |
|
226 static const GEnumValue fdset_mode[] = { |
|
227 {0, "Select", "select"}, |
|
228 {1, "Poll", "poll"}, |
|
229 {2, "EPoll", "epoll"}, |
|
230 {0, NULL, NULL}, |
|
231 }; |
|
232 if (!fdset_mode_type) { |
|
233 fdset_mode_type = g_enum_register_static ("GstFDSetMode", fdset_mode); |
|
234 } |
|
235 return fdset_mode_type; |
|
236 } |
|
237 |
|
238 #define GST_TYPE_RECOVER_POLICY (gst_recover_policy_get_type()) |
|
239 static GType |
|
240 gst_recover_policy_get_type (void) |
|
241 { |
|
242 static GType recover_policy_type = 0; |
|
243 static const GEnumValue recover_policy[] = { |
|
244 {GST_RECOVER_POLICY_NONE, |
|
245 "Do not try to recover", "none"}, |
|
246 {GST_RECOVER_POLICY_RESYNC_LATEST, |
|
247 "Resync client to latest buffer", "latest"}, |
|
248 {GST_RECOVER_POLICY_RESYNC_SOFT_LIMIT, |
|
249 "Resync client to soft limit", "soft-limit"}, |
|
250 {GST_RECOVER_POLICY_RESYNC_KEYFRAME, |
|
251 "Resync client to most recent keyframe", "keyframe"}, |
|
252 {0, NULL, NULL}, |
|
253 }; |
|
254 |
|
255 if (!recover_policy_type) { |
|
256 recover_policy_type = |
|
257 g_enum_register_static ("GstRecoverPolicy", recover_policy); |
|
258 } |
|
259 return recover_policy_type; |
|
260 } |
|
261 |
|
262 #define GST_TYPE_SYNC_METHOD (gst_sync_method_get_type()) |
|
263 static GType |
|
264 gst_sync_method_get_type (void) |
|
265 { |
|
266 static GType sync_method_type = 0; |
|
267 static const GEnumValue sync_method[] = { |
|
268 {GST_SYNC_METHOD_LATEST, |
|
269 "Serve starting from the latest buffer", "latest"}, |
|
270 {GST_SYNC_METHOD_NEXT_KEYFRAME, |
|
271 "Serve starting from the next keyframe", "next-keyframe"}, |
|
272 {GST_SYNC_METHOD_LATEST_KEYFRAME, |
|
273 "Serve everything since the latest keyframe (burst)", |
|
274 "latest-keyframe"}, |
|
275 {GST_SYNC_METHOD_BURST, "Serve burst-value data to client", "burst"}, |
|
276 {GST_SYNC_METHOD_BURST_KEYFRAME, |
|
277 "Serve burst-value data starting on a keyframe", |
|
278 "burst-keyframe"}, |
|
279 {GST_SYNC_METHOD_BURST_WITH_KEYFRAME, |
|
280 "Serve burst-value data preferably starting on a keyframe", |
|
281 "burst-with-keyframe"}, |
|
282 {0, NULL, NULL}, |
|
283 }; |
|
284 |
|
285 if (!sync_method_type) { |
|
286 sync_method_type = g_enum_register_static ("GstSyncMethod", sync_method); |
|
287 } |
|
288 return sync_method_type; |
|
289 } |
|
290 |
|
291 #define GST_TYPE_UNIT_TYPE (gst_unit_type_get_type()) |
|
292 static GType |
|
293 gst_unit_type_get_type (void) |
|
294 { |
|
295 static GType unit_type_type = 0; |
|
296 static const GEnumValue unit_type[] = { |
|
297 {GST_UNIT_TYPE_UNDEFINED, "Undefined", "undefined"}, |
|
298 {GST_UNIT_TYPE_BUFFERS, "Buffers", "buffers"}, |
|
299 {GST_UNIT_TYPE_BYTES, "Bytes", "bytes"}, |
|
300 {GST_UNIT_TYPE_TIME, "Time", "time"}, |
|
301 {0, NULL, NULL}, |
|
302 }; |
|
303 |
|
304 if (!unit_type_type) { |
|
305 unit_type_type = g_enum_register_static ("GstTCPUnitType", unit_type); |
|
306 } |
|
307 return unit_type_type; |
|
308 } |
|
309 |
|
310 #define GST_TYPE_CLIENT_STATUS (gst_client_status_get_type()) |
|
311 static GType |
|
312 gst_client_status_get_type (void) |
|
313 { |
|
314 static GType client_status_type = 0; |
|
315 static const GEnumValue client_status[] = { |
|
316 {GST_CLIENT_STATUS_OK, "ok", "ok"}, |
|
317 {GST_CLIENT_STATUS_CLOSED, "Closed", "closed"}, |
|
318 {GST_CLIENT_STATUS_REMOVED, "Removed", "removed"}, |
|
319 {GST_CLIENT_STATUS_SLOW, "Too slow", "slow"}, |
|
320 {GST_CLIENT_STATUS_ERROR, "Error", "error"}, |
|
321 {GST_CLIENT_STATUS_DUPLICATE, "Duplicate", "duplicate"}, |
|
322 {GST_CLIENT_STATUS_FLUSHING, "Flushing", "flushing"}, |
|
323 {0, NULL, NULL}, |
|
324 }; |
|
325 |
|
326 if (!client_status_type) { |
|
327 client_status_type = |
|
328 g_enum_register_static ("GstClientStatus", client_status); |
|
329 } |
|
330 return client_status_type; |
|
331 } |
|
332 |
|
333 static void gst_multi_fd_sink_finalize (GObject * object); |
|
334 |
|
335 static void gst_multi_fd_sink_remove_client_link (GstMultiFdSink * sink, |
|
336 GList * link); |
|
337 |
|
338 static GstFlowReturn gst_multi_fd_sink_render (GstBaseSink * bsink, |
|
339 GstBuffer * buf); |
|
340 static GstStateChangeReturn gst_multi_fd_sink_change_state (GstElement * |
|
341 element, GstStateChange transition); |
|
342 |
|
343 static void gst_multi_fd_sink_set_property (GObject * object, guint prop_id, |
|
344 const GValue * value, GParamSpec * pspec); |
|
345 static void gst_multi_fd_sink_get_property (GObject * object, guint prop_id, |
|
346 GValue * value, GParamSpec * pspec); |
|
347 |
|
348 GST_BOILERPLATE (GstMultiFdSink, gst_multi_fd_sink, GstBaseSink, |
|
349 GST_TYPE_BASE_SINK); |
|
350 |
|
351 static guint gst_multi_fd_sink_signals[LAST_SIGNAL] = { 0 }; |
|
352 |
|
353 static void |
|
354 gst_multi_fd_sink_base_init (gpointer g_class) |
|
355 { |
|
356 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); |
|
357 |
|
358 gst_element_class_add_pad_template (element_class, |
|
359 gst_static_pad_template_get (&sinktemplate)); |
|
360 |
|
361 gst_element_class_set_details (element_class, &gst_multi_fd_sink_details); |
|
362 } |
|
363 |
|
364 static void |
|
365 gst_multi_fd_sink_class_init (GstMultiFdSinkClass * klass) |
|
366 { |
|
367 GObjectClass *gobject_class; |
|
368 GstElementClass *gstelement_class; |
|
369 GstBaseSinkClass *gstbasesink_class; |
|
370 |
|
371 gobject_class = (GObjectClass *) klass; |
|
372 gstelement_class = (GstElementClass *) klass; |
|
373 gstbasesink_class = (GstBaseSinkClass *) klass; |
|
374 |
|
375 gobject_class->set_property = gst_multi_fd_sink_set_property; |
|
376 gobject_class->get_property = gst_multi_fd_sink_get_property; |
|
377 gobject_class->finalize = gst_multi_fd_sink_finalize; |
|
378 |
|
379 g_object_class_install_property (gobject_class, PROP_PROTOCOL, |
|
380 g_param_spec_enum ("protocol", "Protocol", "The protocol to wrap data in", |
|
381 GST_TYPE_TCP_PROTOCOL, DEFAULT_PROTOCOL, G_PARAM_READWRITE)); |
|
382 |
|
383 /** |
|
384 * GstMultiFdSink::mode |
|
385 * |
|
386 * The mode for selecting activity on the fds. |
|
387 * |
|
388 * This property is deprecated since 0.10.18, if will now automatically |
|
389 * select and use the most optimal method. |
|
390 */ |
|
391 g_object_class_install_property (gobject_class, PROP_MODE, |
|
392 g_param_spec_enum ("mode", "Mode", |
|
393 "The mode for selecting activity on the fds (deprecated)", |
|
394 GST_TYPE_FDSET_MODE, DEFAULT_MODE, G_PARAM_READWRITE)); |
|
395 |
|
396 g_object_class_install_property (gobject_class, PROP_BUFFERS_MAX, |
|
397 g_param_spec_int ("buffers-max", "Buffers max", |
|
398 "max number of buffers to queue for a client (-1 = no limit)", -1, |
|
399 G_MAXINT, DEFAULT_BUFFERS_MAX, G_PARAM_READWRITE)); |
|
400 g_object_class_install_property (gobject_class, |
|
401 PROP_BUFFERS_SOFT_MAX, g_param_spec_int ("buffers-soft-max", |
|
402 "Buffers soft max", |
|
403 "Recover client when going over this limit (-1 = no limit)", -1, |
|
404 G_MAXINT, DEFAULT_BUFFERS_SOFT_MAX, G_PARAM_READWRITE)); |
|
405 |
|
406 g_object_class_install_property (gobject_class, PROP_BYTES_MIN, |
|
407 g_param_spec_int ("bytes-min", "Bytes min", |
|
408 "min number of bytes to queue (-1 = as little as possible)", -1, |
|
409 G_MAXINT, DEFAULT_BYTES_MIN, G_PARAM_READWRITE)); |
|
410 g_object_class_install_property (gobject_class, PROP_TIME_MIN, |
|
411 g_param_spec_int64 ("time-min", "Time min", |
|
412 "min number of time to queue (-1 = as little as possible)", -1, |
|
413 G_MAXINT64, DEFAULT_TIME_MIN, G_PARAM_READWRITE)); |
|
414 g_object_class_install_property (gobject_class, PROP_BUFFERS_MIN, |
|
415 g_param_spec_int ("buffers-min", "Buffers min", |
|
416 "min number of buffers to queue (-1 = as few as possible)", -1, |
|
417 G_MAXINT, DEFAULT_BUFFERS_MIN, G_PARAM_READWRITE)); |
|
418 |
|
419 g_object_class_install_property (gobject_class, PROP_UNIT_TYPE, |
|
420 g_param_spec_enum ("unit-type", "Units type", |
|
421 "The unit to measure the max/soft-max/queued properties", |
|
422 GST_TYPE_UNIT_TYPE, DEFAULT_UNIT_TYPE, G_PARAM_READWRITE)); |
|
423 g_object_class_install_property (gobject_class, PROP_UNITS_MAX, |
|
424 g_param_spec_int64 ("units-max", "Units max", |
|
425 "max number of units to queue (-1 = no limit)", -1, G_MAXINT64, |
|
426 DEFAULT_UNITS_MAX, G_PARAM_READWRITE)); |
|
427 g_object_class_install_property (gobject_class, PROP_UNITS_SOFT_MAX, |
|
428 g_param_spec_int64 ("units-soft-max", "Units soft max", |
|
429 "Recover client when going over this limit (-1 = no limit)", -1, |
|
430 G_MAXINT64, DEFAULT_UNITS_SOFT_MAX, G_PARAM_READWRITE)); |
|
431 |
|
432 g_object_class_install_property (gobject_class, PROP_BUFFERS_QUEUED, |
|
433 g_param_spec_uint ("buffers-queued", "Buffers queued", |
|
434 "Number of buffers currently queued", 0, G_MAXUINT, 0, |
|
435 G_PARAM_READABLE)); |
|
436 #if NOT_IMPLEMENTED |
|
437 g_object_class_install_property (gobject_class, PROP_BYTES_QUEUED, |
|
438 g_param_spec_uint ("bytes-queued", "Bytes queued", |
|
439 "Number of bytes currently queued", 0, G_MAXUINT, 0, |
|
440 G_PARAM_READABLE)); |
|
441 g_object_class_install_property (gobject_class, PROP_TIME_QUEUED, |
|
442 g_param_spec_uint64 ("time-queued", "Time queued", |
|
443 "Number of time currently queued", 0, G_MAXUINT64, 0, |
|
444 G_PARAM_READABLE)); |
|
445 #endif |
|
446 |
|
447 g_object_class_install_property (gobject_class, PROP_RECOVER_POLICY, |
|
448 g_param_spec_enum ("recover-policy", "Recover Policy", |
|
449 "How to recover when client reaches the soft max", |
|
450 GST_TYPE_RECOVER_POLICY, DEFAULT_RECOVER_POLICY, G_PARAM_READWRITE)); |
|
451 g_object_class_install_property (gobject_class, PROP_TIMEOUT, |
|
452 g_param_spec_uint64 ("timeout", "Timeout", |
|
453 "Maximum inactivity timeout in nanoseconds for a client (0 = no limit)", |
|
454 0, G_MAXUINT64, DEFAULT_TIMEOUT, G_PARAM_READWRITE)); |
|
455 g_object_class_install_property (gobject_class, PROP_SYNC_METHOD, |
|
456 g_param_spec_enum ("sync-method", "Sync Method", |
|
457 "How to sync new clients to the stream", |
|
458 GST_TYPE_SYNC_METHOD, DEFAULT_SYNC_METHOD, G_PARAM_READWRITE)); |
|
459 g_object_class_install_property (gobject_class, PROP_BYTES_TO_SERVE, |
|
460 g_param_spec_uint64 ("bytes-to-serve", "Bytes to serve", |
|
461 "Number of bytes received to serve to clients", 0, G_MAXUINT64, 0, |
|
462 G_PARAM_READABLE)); |
|
463 g_object_class_install_property (gobject_class, PROP_BYTES_SERVED, |
|
464 g_param_spec_uint64 ("bytes-served", "Bytes served", |
|
465 "Total number of bytes send to all clients", 0, G_MAXUINT64, 0, |
|
466 G_PARAM_READABLE)); |
|
467 |
|
468 g_object_class_install_property (gobject_class, PROP_BURST_UNIT, |
|
469 g_param_spec_enum ("burst-unit", "Burst unit", |
|
470 "The format of the burst units (when sync-method is burst[[-with]-keyframe])", |
|
471 GST_TYPE_UNIT_TYPE, DEFAULT_BURST_UNIT, G_PARAM_READWRITE)); |
|
472 g_object_class_install_property (gobject_class, PROP_BURST_VALUE, |
|
473 g_param_spec_uint64 ("burst-value", "Burst value", |
|
474 "The amount of burst expressed in burst-unit", |
|
475 0, G_MAXUINT64, DEFAULT_BURST_VALUE, G_PARAM_READWRITE)); |
|
476 |
|
477 /** |
|
478 * GstMultiFdSink::add: |
|
479 * @gstmultifdsink: the multifdsink element to emit this signal on |
|
480 * @fd: the file descriptor to add to multifdsink |
|
481 * |
|
482 * Hand the given open file descriptor to multifdsink to write to. |
|
483 */ |
|
484 gst_multi_fd_sink_signals[SIGNAL_ADD] = |
|
485 g_signal_new ("add", G_TYPE_FROM_CLASS (klass), |
|
486 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstMultiFdSinkClass, |
|
487 add), NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, |
|
488 G_TYPE_INT); |
|
489 /** |
|
490 * GstMultiFdSink::add-full: |
|
491 * @gstmultifdsink: the multifdsink element to emit this signal on |
|
492 * @fd: the file descriptor to add to multifdsink |
|
493 * @keyframe: start bursting from a keyframe |
|
494 * @unit_type_min: the unit-type of @value_min |
|
495 * @value_min: the minimum amount of data to burst expressed in |
|
496 * @unit_type_min units. |
|
497 * @unit_type_max: the unit-type of @value_max |
|
498 * @value_max: the maximum amount of data to burst expressed in |
|
499 * @unit_type_max units. |
|
500 * |
|
501 * Hand the given open file descriptor to multifdsink to write to and |
|
502 * specify the burst parameters for the new connection. |
|
503 */ |
|
504 gst_multi_fd_sink_signals[SIGNAL_ADD_BURST] = |
|
505 g_signal_new ("add-full", G_TYPE_FROM_CLASS (klass), |
|
506 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstMultiFdSinkClass, |
|
507 add_full), NULL, NULL, |
|
508 gst_tcp_marshal_VOID__INT_BOOLEAN_INT_UINT64_INT_UINT64, G_TYPE_NONE, 6, |
|
509 G_TYPE_INT, G_TYPE_BOOLEAN, GST_TYPE_UNIT_TYPE, G_TYPE_UINT64, |
|
510 GST_TYPE_UNIT_TYPE, G_TYPE_UINT64); |
|
511 /** |
|
512 * GstMultiFdSink::remove: |
|
513 * @gstmultifdsink: the multifdsink element to emit this signal on |
|
514 * @fd: the file descriptor to remove from multifdsink |
|
515 * |
|
516 * Remove the given open file descriptor from multifdsink. |
|
517 */ |
|
518 gst_multi_fd_sink_signals[SIGNAL_REMOVE] = |
|
519 g_signal_new ("remove", G_TYPE_FROM_CLASS (klass), |
|
520 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstMultiFdSinkClass, |
|
521 remove), NULL, NULL, gst_tcp_marshal_VOID__INT, G_TYPE_NONE, 1, |
|
522 G_TYPE_INT); |
|
523 /** |
|
524 * GstMultiFdSink::remove_flush: |
|
525 * @gstmultifdsink: the multifdsink element to emit this signal on |
|
526 * @fd: the file descriptor to remove from multifdsink |
|
527 * |
|
528 * Remove the given open file descriptor from multifdsink after flushing all |
|
529 * the pending data to the fd. |
|
530 */ |
|
531 gst_multi_fd_sink_signals[SIGNAL_REMOVE_FLUSH] = |
|
532 g_signal_new ("remove-flush", G_TYPE_FROM_CLASS (klass), |
|
533 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstMultiFdSinkClass, |
|
534 remove_flush), NULL, NULL, gst_tcp_marshal_VOID__INT, G_TYPE_NONE, 1, |
|
535 G_TYPE_INT); |
|
536 /** |
|
537 * GstMultiFdSink::clear: |
|
538 * @gstmultifdsink: the multifdsink element to emit this signal on |
|
539 * |
|
540 * Remove all file descriptors from multifdsink. Since multifdsink did not |
|
541 * open fd's itself, it does not explicitly close the fd. The application |
|
542 * should do so by connecting to the client-fd-removed callback. |
|
543 */ |
|
544 gst_multi_fd_sink_signals[SIGNAL_CLEAR] = |
|
545 g_signal_new ("clear", G_TYPE_FROM_CLASS (klass), |
|
546 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstMultiFdSinkClass, |
|
547 clear), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); |
|
548 |
|
549 /** |
|
550 * GstMultiFdSink::get-stats: |
|
551 * @gstmultifdsink: the multifdsink element to emit this signal on |
|
552 * @fd: the file descriptor to get stats of from multifdsink |
|
553 * |
|
554 * Get statistics about @fd. This function returns a GValueArray to ease |
|
555 * automatic wrapping for bindings. |
|
556 * |
|
557 * Returns: a GValueArray with the statistics. The array contains guint64 |
|
558 * values that represent respectively: total number of bytes sent, time |
|
559 * when the client was added, time when the client was |
|
560 * disconnected/removed, time the client is/was active, last activity |
|
561 * time (in epoch seconds), number of buffers dropped. |
|
562 * All times are expressed in nanoseconds (GstClockTime). |
|
563 * The array can be 0-length if the client was not found. |
|
564 */ |
|
565 gst_multi_fd_sink_signals[SIGNAL_GET_STATS] = |
|
566 g_signal_new ("get-stats", G_TYPE_FROM_CLASS (klass), |
|
567 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstMultiFdSinkClass, |
|
568 get_stats), NULL, NULL, gst_tcp_marshal_BOXED__INT, |
|
569 G_TYPE_VALUE_ARRAY, 1, G_TYPE_INT); |
|
570 |
|
571 /** |
|
572 * GstMultiFdSink::client-added: |
|
573 * @gstmultifdsink: the multifdsink element that emitted this signal |
|
574 * @fd: the file descriptor that was added to multifdsink |
|
575 * |
|
576 * The given file descriptor was added to multifdsink. This signal will |
|
577 * be emitted from the streaming thread so application should be prepared |
|
578 * for that. |
|
579 */ |
|
580 gst_multi_fd_sink_signals[SIGNAL_CLIENT_ADDED] = |
|
581 g_signal_new ("client-added", G_TYPE_FROM_CLASS (klass), |
|
582 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstMultiFdSinkClass, client_added), |
|
583 NULL, NULL, gst_tcp_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT); |
|
584 /** |
|
585 * GstMultiFdSink::client-removed: |
|
586 * @gstmultifdsink: the multifdsink element that emitted this signal |
|
587 * @fd: the file descriptor that is to be removed from multifdsink |
|
588 * @status: the reason why the client was removed |
|
589 * |
|
590 * The given file descriptor is about to be removed from multifdsink. This |
|
591 * signal will be emitted from the streaming thread so applications should |
|
592 * be prepared for that. |
|
593 * |
|
594 * @gstmultifdsink still holds a handle to @fd so it is possible to call |
|
595 * the get-stats signal from this callback. For the same reason it is |
|
596 * not safe to close() and reuse @fd in this callback. |
|
597 */ |
|
598 gst_multi_fd_sink_signals[SIGNAL_CLIENT_REMOVED] = |
|
599 g_signal_new ("client-removed", G_TYPE_FROM_CLASS (klass), |
|
600 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstMultiFdSinkClass, |
|
601 client_removed), NULL, NULL, gst_tcp_marshal_VOID__INT_BOXED, |
|
602 G_TYPE_NONE, 2, G_TYPE_INT, GST_TYPE_CLIENT_STATUS); |
|
603 /** |
|
604 * GstMultiFdSink::client-fd-removed: |
|
605 * @gstmultifdsink: the multifdsink element that emitted this signal |
|
606 * @fd: the file descriptor that was removed from multifdsink |
|
607 * |
|
608 * The given file descriptor was removed from multifdsink. This signal will |
|
609 * be emitted from the streaming thread so applications should be prepared |
|
610 * for that. |
|
611 * |
|
612 * In this callback, @gstmultifdsink has removed all the information |
|
613 * associated with @fd and it is therefore not possible to call get-stats |
|
614 * with @fd. It is however safe to close() and reuse @fd in the callback. |
|
615 * |
|
616 * Since: 0.10.7 |
|
617 */ |
|
618 gst_multi_fd_sink_signals[SIGNAL_CLIENT_FD_REMOVED] = |
|
619 g_signal_new ("client-fd-removed", G_TYPE_FROM_CLASS (klass), |
|
620 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstMultiFdSinkClass, |
|
621 client_fd_removed), NULL, NULL, gst_tcp_marshal_VOID__INT, |
|
622 G_TYPE_NONE, 1, G_TYPE_INT); |
|
623 |
|
624 gstelement_class->change_state = |
|
625 GST_DEBUG_FUNCPTR (gst_multi_fd_sink_change_state); |
|
626 |
|
627 gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_multi_fd_sink_render); |
|
628 |
|
629 klass->add = GST_DEBUG_FUNCPTR (gst_multi_fd_sink_add); |
|
630 klass->add_full = GST_DEBUG_FUNCPTR (gst_multi_fd_sink_add_full); |
|
631 klass->remove = GST_DEBUG_FUNCPTR (gst_multi_fd_sink_remove); |
|
632 klass->remove_flush = GST_DEBUG_FUNCPTR (gst_multi_fd_sink_remove_flush); |
|
633 klass->clear = GST_DEBUG_FUNCPTR (gst_multi_fd_sink_clear); |
|
634 klass->get_stats = GST_DEBUG_FUNCPTR (gst_multi_fd_sink_get_stats); |
|
635 |
|
636 GST_DEBUG_CATEGORY_INIT (multifdsink_debug, "multifdsink", 0, "FD sink"); |
|
637 } |
|
638 |
|
639 static void |
|
640 gst_multi_fd_sink_init (GstMultiFdSink * this, GstMultiFdSinkClass * klass) |
|
641 { |
|
642 GST_OBJECT_FLAG_UNSET (this, GST_MULTI_FD_SINK_OPEN); |
|
643 |
|
644 this->protocol = DEFAULT_PROTOCOL; |
|
645 this->mode = DEFAULT_MODE; |
|
646 |
|
647 CLIENTS_LOCK_INIT (this); |
|
648 this->clients = NULL; |
|
649 this->fd_hash = g_hash_table_new (g_int_hash, g_int_equal); |
|
650 |
|
651 this->bufqueue = g_array_new (FALSE, TRUE, sizeof (GstBuffer *)); |
|
652 this->unit_type = DEFAULT_UNIT_TYPE; |
|
653 this->units_max = DEFAULT_UNITS_MAX; |
|
654 this->units_soft_max = DEFAULT_UNITS_SOFT_MAX; |
|
655 this->time_min = DEFAULT_TIME_MIN; |
|
656 this->bytes_min = DEFAULT_BYTES_MIN; |
|
657 this->buffers_min = DEFAULT_BUFFERS_MIN; |
|
658 this->recover_policy = DEFAULT_RECOVER_POLICY; |
|
659 |
|
660 this->timeout = DEFAULT_TIMEOUT; |
|
661 this->def_sync_method = DEFAULT_SYNC_METHOD; |
|
662 this->def_burst_unit = DEFAULT_BURST_UNIT; |
|
663 this->def_burst_value = DEFAULT_BURST_VALUE; |
|
664 |
|
665 this->header_flags = 0; |
|
666 } |
|
667 |
|
668 static void |
|
669 gst_multi_fd_sink_finalize (GObject * object) |
|
670 { |
|
671 GstMultiFdSink *this; |
|
672 |
|
673 this = GST_MULTI_FD_SINK (object); |
|
674 |
|
675 CLIENTS_LOCK_FREE (this); |
|
676 g_hash_table_destroy (this->fd_hash); |
|
677 g_array_free (this->bufqueue, TRUE); |
|
678 |
|
679 G_OBJECT_CLASS (parent_class)->finalize (object); |
|
680 } |
|
681 |
|
682 /* "add-full" signal implementation */ |
|
683 #ifdef __SYMBIAN32__ |
|
684 EXPORT_C |
|
685 #endif |
|
686 |
|
687 void |
|
688 gst_multi_fd_sink_add_full (GstMultiFdSink * sink, int fd, |
|
689 GstSyncMethod sync_method, GstUnitType min_unit, guint64 min_value, |
|
690 GstUnitType max_unit, guint64 max_value) |
|
691 { |
|
692 GstTCPClient *client; |
|
693 GList *clink; |
|
694 GTimeVal now; |
|
695 gint flags, res; |
|
696 struct stat statbuf; |
|
697 |
|
698 GST_DEBUG_OBJECT (sink, "[fd %5d] adding client, sync_method %d, " |
|
699 "min_unit %d, min_value %" G_GUINT64_FORMAT |
|
700 ", max_unit %d, max_value %" G_GUINT64_FORMAT, fd, sync_method, |
|
701 min_unit, min_value, max_unit, max_value); |
|
702 |
|
703 /* do limits check if we can */ |
|
704 if (min_unit == max_unit) { |
|
705 if (max_value != -1 && min_value != -1 && max_value < min_value) |
|
706 goto wrong_limits; |
|
707 } |
|
708 |
|
709 /* create client datastructure */ |
|
710 client = g_new0 (GstTCPClient, 1); |
|
711 client->fd.fd = fd; |
|
712 client->status = GST_CLIENT_STATUS_OK; |
|
713 client->bufpos = -1; |
|
714 client->flushcount = -1; |
|
715 client->bufoffset = 0; |
|
716 client->sending = NULL; |
|
717 client->bytes_sent = 0; |
|
718 client->dropped_buffers = 0; |
|
719 client->avg_queue_size = 0; |
|
720 client->new_connection = TRUE; |
|
721 client->burst_min_unit = min_unit; |
|
722 client->burst_min_value = min_value; |
|
723 client->burst_max_unit = max_unit; |
|
724 client->burst_max_value = max_value; |
|
725 client->sync_method = sync_method; |
|
726 client->currently_removing = FALSE; |
|
727 |
|
728 /* update start time */ |
|
729 g_get_current_time (&now); |
|
730 client->connect_time = GST_TIMEVAL_TO_TIME (now); |
|
731 client->disconnect_time = 0; |
|
732 /* set last activity time to connect time */ |
|
733 client->last_activity_time = client->connect_time; |
|
734 |
|
735 CLIENTS_LOCK (sink); |
|
736 |
|
737 /* check the hash to find a duplicate fd */ |
|
738 clink = g_hash_table_lookup (sink->fd_hash, &client->fd.fd); |
|
739 if (clink != NULL) |
|
740 goto duplicate; |
|
741 |
|
742 /* we can add the fd now */ |
|
743 clink = sink->clients = g_list_prepend (sink->clients, client); |
|
744 g_hash_table_insert (sink->fd_hash, &client->fd.fd, clink); |
|
745 sink->clients_cookie++; |
|
746 |
|
747 /* set the socket to non blocking */ |
|
748 //temporary commented for multifdsink issue |
|
749 //res = fcntl (fd, F_SETFL, O_NONBLOCK); //Arun |
|
750 |
|
751 /* we always read from a client */ |
|
752 gst_poll_add_fd (sink->fdset, &client->fd); |
|
753 |
|
754 /* we don't try to read from write only fds */ |
|
755 flags = fcntl (fd, F_GETFL, 0); |
|
756 if ((flags & O_ACCMODE) != O_WRONLY) { |
|
757 gst_poll_fd_ctl_read (sink->fdset, &client->fd, TRUE); |
|
758 } |
|
759 /* figure out the mode, can't use send() for non sockets */ |
|
760 res = fstat (fd, &statbuf); |
|
761 if (S_ISSOCK (statbuf.st_mode)) { |
|
762 client->is_socket = TRUE; |
|
763 } |
|
764 |
|
765 gst_poll_restart (sink->fdset); |
|
766 |
|
767 CLIENTS_UNLOCK (sink); |
|
768 |
|
769 g_signal_emit (G_OBJECT (sink), |
|
770 gst_multi_fd_sink_signals[SIGNAL_CLIENT_ADDED], 0, fd); |
|
771 |
|
772 return; |
|
773 |
|
774 /* errors */ |
|
775 wrong_limits: |
|
776 { |
|
777 GST_WARNING_OBJECT (sink, |
|
778 "[fd %5d] wrong values min =%" G_GUINT64_FORMAT ", max=%" |
|
779 G_GUINT64_FORMAT ", unit %d specified when adding client", fd, |
|
780 min_value, max_value, min_unit); |
|
781 return; |
|
782 } |
|
783 duplicate: |
|
784 { |
|
785 client->status = GST_CLIENT_STATUS_DUPLICATE; |
|
786 CLIENTS_UNLOCK (sink); |
|
787 GST_WARNING_OBJECT (sink, "[fd %5d] duplicate client found, refusing", fd); |
|
788 g_signal_emit (G_OBJECT (sink), |
|
789 gst_multi_fd_sink_signals[SIGNAL_CLIENT_REMOVED], 0, fd, |
|
790 client->status); |
|
791 g_free (client); |
|
792 return; |
|
793 } |
|
794 } |
|
795 |
|
796 /* "add" signal implemntation */ |
|
797 #ifdef __SYMBIAN32__ |
|
798 EXPORT_C |
|
799 #endif |
|
800 |
|
801 void |
|
802 gst_multi_fd_sink_add (GstMultiFdSink * sink, int fd) |
|
803 { |
|
804 gst_multi_fd_sink_add_full (sink, fd, sink->def_sync_method, |
|
805 sink->def_burst_unit, sink->def_burst_value, sink->def_burst_unit, -1); |
|
806 } |
|
807 |
|
808 /* "remove" signal implementation */ |
|
809 #ifdef __SYMBIAN32__ |
|
810 EXPORT_C |
|
811 #endif |
|
812 |
|
813 void |
|
814 gst_multi_fd_sink_remove (GstMultiFdSink * sink, int fd) |
|
815 { |
|
816 GList *clink; |
|
817 |
|
818 GST_DEBUG_OBJECT (sink, "[fd %5d] removing client", fd); |
|
819 |
|
820 CLIENTS_LOCK (sink); |
|
821 clink = g_hash_table_lookup (sink->fd_hash, &fd); |
|
822 if (clink != NULL) { |
|
823 GstTCPClient *client = (GstTCPClient *) clink->data; |
|
824 |
|
825 if (client->status != GST_CLIENT_STATUS_OK) { |
|
826 GST_INFO_OBJECT (sink, |
|
827 "[fd %5d] Client already disconnecting with status %d", |
|
828 fd, client->status); |
|
829 goto done; |
|
830 } |
|
831 |
|
832 client->status = GST_CLIENT_STATUS_REMOVED; |
|
833 gst_multi_fd_sink_remove_client_link (sink, clink); |
|
834 gst_poll_restart (sink->fdset); |
|
835 } else { |
|
836 GST_WARNING_OBJECT (sink, "[fd %5d] no client with this fd found!", fd); |
|
837 } |
|
838 |
|
839 done: |
|
840 CLIENTS_UNLOCK (sink); |
|
841 } |
|
842 |
|
843 /* "remove-flush" signal implementation */ |
|
844 #ifdef __SYMBIAN32__ |
|
845 EXPORT_C |
|
846 #endif |
|
847 |
|
848 void |
|
849 gst_multi_fd_sink_remove_flush (GstMultiFdSink * sink, int fd) |
|
850 { |
|
851 GList *clink; |
|
852 |
|
853 GST_DEBUG_OBJECT (sink, "[fd %5d] flushing client", fd); |
|
854 |
|
855 CLIENTS_LOCK (sink); |
|
856 clink = g_hash_table_lookup (sink->fd_hash, &fd); |
|
857 if (clink != NULL) { |
|
858 GstTCPClient *client = (GstTCPClient *) clink->data; |
|
859 |
|
860 if (client->status != GST_CLIENT_STATUS_OK) { |
|
861 GST_INFO_OBJECT (sink, |
|
862 "[fd %5d] Client already disconnecting with status %d", |
|
863 fd, client->status); |
|
864 goto done; |
|
865 } |
|
866 |
|
867 /* take the position of the client as the number of buffers left to flush. |
|
868 * If the client was at position -1, we flush 0 buffers, 0 == flush 1 |
|
869 * buffer, etc... */ |
|
870 client->flushcount = client->bufpos + 1; |
|
871 /* mark client as flushing. We can not remove the client right away because |
|
872 * it might have some buffers to flush in the ->sending queue. */ |
|
873 client->status = GST_CLIENT_STATUS_FLUSHING; |
|
874 } else { |
|
875 GST_WARNING_OBJECT (sink, "[fd %5d] no client with this fd found!", fd); |
|
876 } |
|
877 done: |
|
878 CLIENTS_UNLOCK (sink); |
|
879 } |
|
880 |
|
881 /* can be called both through the signal (i.e. from any thread) or when |
|
882 * stopping, after the writing thread has shut down */ |
|
883 #ifdef __SYMBIAN32__ |
|
884 EXPORT_C |
|
885 #endif |
|
886 |
|
887 void |
|
888 gst_multi_fd_sink_clear (GstMultiFdSink * sink) |
|
889 { |
|
890 GList *clients, *next; |
|
891 guint32 cookie; |
|
892 |
|
893 GST_DEBUG_OBJECT (sink, "clearing all clients"); |
|
894 |
|
895 CLIENTS_LOCK (sink); |
|
896 restart: |
|
897 cookie = sink->clients_cookie; |
|
898 for (clients = sink->clients; clients; clients = next) { |
|
899 GstTCPClient *client; |
|
900 |
|
901 if (cookie != sink->clients_cookie) { |
|
902 GST_DEBUG_OBJECT (sink, "cookie changed while removing all clients"); |
|
903 goto restart; |
|
904 } |
|
905 |
|
906 client = (GstTCPClient *) clients->data; |
|
907 next = g_list_next (clients); |
|
908 |
|
909 client->status = GST_CLIENT_STATUS_REMOVED; |
|
910 gst_multi_fd_sink_remove_client_link (sink, clients); |
|
911 } |
|
912 gst_poll_restart (sink->fdset); |
|
913 CLIENTS_UNLOCK (sink); |
|
914 } |
|
915 |
|
916 /* "get-stats" signal implementation |
|
917 * the array returned contains: |
|
918 * |
|
919 * guint64 : bytes_sent |
|
920 * guint64 : connect time (in nanoseconds, since Epoch) |
|
921 * guint64 : disconnect time (in nanoseconds, since Epoch) |
|
922 * guint64 : time the client is/was connected (in nanoseconds) |
|
923 * guint64 : last activity time (in nanoseconds, since Epoch) |
|
924 * guint64 : buffers dropped due to recovery |
|
925 */ |
|
926 #ifdef __SYMBIAN32__ |
|
927 EXPORT_C |
|
928 #endif |
|
929 |
|
930 GValueArray * |
|
931 gst_multi_fd_sink_get_stats (GstMultiFdSink * sink, int fd) |
|
932 { |
|
933 GstTCPClient *client; |
|
934 GValueArray *result = NULL; |
|
935 GList *clink; |
|
936 |
|
937 CLIENTS_LOCK (sink); |
|
938 clink = g_hash_table_lookup (sink->fd_hash, &fd); |
|
939 if (clink == NULL) |
|
940 goto noclient; |
|
941 |
|
942 client = (GstTCPClient *) clink->data; |
|
943 if (client != NULL) { |
|
944 GValue value = { 0 }; |
|
945 guint64 interval; |
|
946 |
|
947 result = g_value_array_new (5); |
|
948 |
|
949 g_value_init (&value, G_TYPE_UINT64); |
|
950 g_value_set_uint64 (&value, client->bytes_sent); |
|
951 result = g_value_array_append (result, &value); |
|
952 g_value_unset (&value); |
|
953 g_value_init (&value, G_TYPE_UINT64); |
|
954 g_value_set_uint64 (&value, client->connect_time); |
|
955 result = g_value_array_append (result, &value); |
|
956 g_value_unset (&value); |
|
957 if (client->disconnect_time == 0) { |
|
958 GTimeVal nowtv; |
|
959 |
|
960 g_get_current_time (&nowtv); |
|
961 |
|
962 interval = GST_TIMEVAL_TO_TIME (nowtv) - client->connect_time; |
|
963 } else { |
|
964 interval = client->disconnect_time - client->connect_time; |
|
965 } |
|
966 g_value_init (&value, G_TYPE_UINT64); |
|
967 g_value_set_uint64 (&value, client->disconnect_time); |
|
968 result = g_value_array_append (result, &value); |
|
969 g_value_unset (&value); |
|
970 g_value_init (&value, G_TYPE_UINT64); |
|
971 g_value_set_uint64 (&value, interval); |
|
972 result = g_value_array_append (result, &value); |
|
973 g_value_unset (&value); |
|
974 g_value_init (&value, G_TYPE_UINT64); |
|
975 g_value_set_uint64 (&value, client->last_activity_time); |
|
976 result = g_value_array_append (result, &value); |
|
977 g_value_unset (&value); |
|
978 g_value_init (&value, G_TYPE_UINT64); |
|
979 g_value_set_uint64 (&value, client->dropped_buffers); |
|
980 result = g_value_array_append (result, &value); |
|
981 } |
|
982 |
|
983 noclient: |
|
984 CLIENTS_UNLOCK (sink); |
|
985 |
|
986 /* python doesn't like a NULL pointer yet */ |
|
987 if (result == NULL) { |
|
988 GST_WARNING_OBJECT (sink, "[fd %5d] no client with this found!", fd); |
|
989 result = g_value_array_new (0); |
|
990 } |
|
991 |
|
992 return result; |
|
993 } |
|
994 |
|
995 /* should be called with the clientslock helt. |
|
996 * Note that we don't close the fd as we didn't open it in the first |
|
997 * place. An application should connect to the client-fd-removed signal and |
|
998 * close the fd itself. |
|
999 */ |
|
1000 static void |
|
1001 gst_multi_fd_sink_remove_client_link (GstMultiFdSink * sink, GList * link) |
|
1002 { |
|
1003 int fd; |
|
1004 GTimeVal now; |
|
1005 GstTCPClient *client = (GstTCPClient *) link->data; |
|
1006 GstMultiFdSinkClass *fclass; |
|
1007 |
|
1008 fclass = GST_MULTI_FD_SINK_GET_CLASS (sink); |
|
1009 |
|
1010 fd = client->fd.fd; |
|
1011 |
|
1012 if (client->currently_removing) { |
|
1013 GST_WARNING_OBJECT (sink, "[fd %5d] client is already being removed", fd); |
|
1014 return; |
|
1015 } else { |
|
1016 client->currently_removing = TRUE; |
|
1017 } |
|
1018 |
|
1019 /* FIXME: if we keep track of ip we can log it here and signal */ |
|
1020 switch (client->status) { |
|
1021 case GST_CLIENT_STATUS_OK: |
|
1022 GST_WARNING_OBJECT (sink, "[fd %5d] removing client %p for no reason", |
|
1023 fd, client); |
|
1024 break; |
|
1025 case GST_CLIENT_STATUS_CLOSED: |
|
1026 GST_DEBUG_OBJECT (sink, "[fd %5d] removing client %p because of close", |
|
1027 fd, client); |
|
1028 break; |
|
1029 case GST_CLIENT_STATUS_REMOVED: |
|
1030 GST_DEBUG_OBJECT (sink, |
|
1031 "[fd %5d] removing client %p because the app removed it", fd, client); |
|
1032 break; |
|
1033 case GST_CLIENT_STATUS_SLOW: |
|
1034 GST_INFO_OBJECT (sink, |
|
1035 "[fd %5d] removing client %p because it was too slow", fd, client); |
|
1036 break; |
|
1037 case GST_CLIENT_STATUS_ERROR: |
|
1038 GST_WARNING_OBJECT (sink, |
|
1039 "[fd %5d] removing client %p because of error", fd, client); |
|
1040 break; |
|
1041 case GST_CLIENT_STATUS_FLUSHING: |
|
1042 default: |
|
1043 GST_WARNING_OBJECT (sink, |
|
1044 "[fd %5d] removing client %p with invalid reason %d", fd, client, |
|
1045 client->status); |
|
1046 break; |
|
1047 } |
|
1048 |
|
1049 gst_poll_remove_fd (sink->fdset, &client->fd); |
|
1050 |
|
1051 g_get_current_time (&now); |
|
1052 client->disconnect_time = GST_TIMEVAL_TO_TIME (now); |
|
1053 |
|
1054 /* free client buffers */ |
|
1055 g_slist_foreach (client->sending, (GFunc) gst_mini_object_unref, NULL); |
|
1056 g_slist_free (client->sending); |
|
1057 client->sending = NULL; |
|
1058 |
|
1059 if (client->caps) |
|
1060 gst_caps_unref (client->caps); |
|
1061 client->caps = NULL; |
|
1062 |
|
1063 /* unlock the mutex before signaling because the signal handler |
|
1064 * might query some properties */ |
|
1065 CLIENTS_UNLOCK (sink); |
|
1066 |
|
1067 g_signal_emit (G_OBJECT (sink), |
|
1068 gst_multi_fd_sink_signals[SIGNAL_CLIENT_REMOVED], 0, fd, client->status); |
|
1069 |
|
1070 /* lock again before we remove the client completely */ |
|
1071 CLIENTS_LOCK (sink); |
|
1072 |
|
1073 /* fd cannot be reused in the above signal callback so we can safely |
|
1074 * remove it from the hashtable here */ |
|
1075 if (!g_hash_table_remove (sink->fd_hash, &client->fd.fd)) { |
|
1076 GST_WARNING_OBJECT (sink, |
|
1077 "[fd %5d] error removing client %p from hash", client->fd.fd, client); |
|
1078 } |
|
1079 /* after releasing the lock above, the link could be invalid, more |
|
1080 * precisely, the next and prev pointers could point to invalid list |
|
1081 * links. One optimisation could be to add a cookie to the linked list |
|
1082 * and take a shortcut when it did not change between unlocking and locking |
|
1083 * our mutex. For now we just walk the list again. */ |
|
1084 sink->clients = g_list_remove (sink->clients, client); |
|
1085 sink->clients_cookie++; |
|
1086 |
|
1087 if (fclass->removed) |
|
1088 fclass->removed (sink, client->fd.fd); |
|
1089 |
|
1090 g_free (client); |
|
1091 CLIENTS_UNLOCK (sink); |
|
1092 |
|
1093 /* and the fd is really gone now */ |
|
1094 g_signal_emit (G_OBJECT (sink), |
|
1095 gst_multi_fd_sink_signals[SIGNAL_CLIENT_FD_REMOVED], 0, fd); |
|
1096 |
|
1097 CLIENTS_LOCK (sink); |
|
1098 } |
|
1099 |
|
1100 /* handle a read on a client fd, |
|
1101 * which either indicates a close or should be ignored |
|
1102 * returns FALSE if some error occured or the client closed. */ |
|
1103 static gboolean |
|
1104 gst_multi_fd_sink_handle_client_read (GstMultiFdSink * sink, |
|
1105 GstTCPClient * client) |
|
1106 { |
|
1107 int avail, fd; |
|
1108 gboolean ret; |
|
1109 |
|
1110 fd = client->fd.fd; |
|
1111 |
|
1112 if (ioctl (fd, FIONREAD, &avail) < 0) |
|
1113 goto ioctl_failed; |
|
1114 |
|
1115 GST_DEBUG_OBJECT (sink, "[fd %5d] select reports client read of %d bytes", |
|
1116 fd, avail); |
|
1117 |
|
1118 ret = TRUE; |
|
1119 |
|
1120 if (avail == 0) { |
|
1121 /* client sent close, so remove it */ |
|
1122 GST_DEBUG_OBJECT (sink, "[fd %5d] client asked for close, removing", fd); |
|
1123 client->status = GST_CLIENT_STATUS_CLOSED; |
|
1124 ret = FALSE; |
|
1125 } else if (avail < 0) { |
|
1126 GST_WARNING_OBJECT (sink, "[fd %5d] avail < 0, removing", fd); |
|
1127 client->status = GST_CLIENT_STATUS_ERROR; |
|
1128 ret = FALSE; |
|
1129 } else { |
|
1130 guint8 dummy[512]; |
|
1131 gint nread; |
|
1132 |
|
1133 /* just Read 'n' Drop, could also just drop the client as it's not supposed |
|
1134 * to write to us except for closing the socket, I guess it's because we |
|
1135 * like to listen to our customers. */ |
|
1136 do { |
|
1137 /* this is the maximum we can read */ |
|
1138 gint to_read = MIN (avail, 512); |
|
1139 |
|
1140 GST_DEBUG_OBJECT (sink, "[fd %5d] client wants us to read %d bytes", |
|
1141 fd, to_read); |
|
1142 |
|
1143 nread = read (fd, dummy, to_read); |
|
1144 if (nread < -1) { |
|
1145 GST_WARNING_OBJECT (sink, "[fd %5d] could not read %d bytes: %s (%d)", |
|
1146 fd, to_read, g_strerror (errno), errno); |
|
1147 client->status = GST_CLIENT_STATUS_ERROR; |
|
1148 ret = FALSE; |
|
1149 break; |
|
1150 } else if (nread == 0) { |
|
1151 GST_WARNING_OBJECT (sink, "[fd %5d] 0 bytes in read, removing", fd); |
|
1152 client->status = GST_CLIENT_STATUS_ERROR; |
|
1153 ret = FALSE; |
|
1154 break; |
|
1155 } |
|
1156 avail -= nread; |
|
1157 } |
|
1158 while (avail > 0); |
|
1159 } |
|
1160 return ret; |
|
1161 |
|
1162 /* ERRORS */ |
|
1163 ioctl_failed: |
|
1164 { |
|
1165 GST_WARNING_OBJECT (sink, "[fd %5d] ioctl failed: %s (%d)", |
|
1166 fd, g_strerror (errno), errno); |
|
1167 client->status = GST_CLIENT_STATUS_ERROR; |
|
1168 return FALSE; |
|
1169 } |
|
1170 } |
|
1171 |
|
1172 /* Queue raw data for this client, creating a new buffer. |
|
1173 * This takes ownership of the data by |
|
1174 * setting it as GST_BUFFER_MALLOCDATA() on the created buffer so |
|
1175 * be sure to pass g_free()-able @data. |
|
1176 */ |
|
1177 static gboolean |
|
1178 gst_multi_fd_sink_client_queue_data (GstMultiFdSink * sink, |
|
1179 GstTCPClient * client, gchar * data, gint len) |
|
1180 { |
|
1181 GstBuffer *buf; |
|
1182 |
|
1183 buf = gst_buffer_new (); |
|
1184 GST_BUFFER_DATA (buf) = (guint8 *) data; |
|
1185 GST_BUFFER_MALLOCDATA (buf) = (guint8 *) data; |
|
1186 GST_BUFFER_SIZE (buf) = len; |
|
1187 |
|
1188 GST_LOG_OBJECT (sink, "[fd %5d] queueing data of length %d", |
|
1189 client->fd.fd, len); |
|
1190 |
|
1191 client->sending = g_slist_append (client->sending, buf); |
|
1192 |
|
1193 return TRUE; |
|
1194 } |
|
1195 |
|
1196 /* GDP-encode given caps and queue them for sending */ |
|
1197 static gboolean |
|
1198 gst_multi_fd_sink_client_queue_caps (GstMultiFdSink * sink, |
|
1199 GstTCPClient * client, const GstCaps * caps) |
|
1200 { |
|
1201 guint8 *header; |
|
1202 guint8 *payload; |
|
1203 guint length; |
|
1204 gchar *string; |
|
1205 |
|
1206 g_return_val_if_fail (caps != NULL, FALSE); |
|
1207 |
|
1208 string = gst_caps_to_string (caps); |
|
1209 GST_DEBUG_OBJECT (sink, "[fd %5d] Queueing caps %s through GDP", |
|
1210 client->fd.fd, string); |
|
1211 g_free (string); |
|
1212 |
|
1213 if (!gst_dp_packet_from_caps (caps, sink->header_flags, &length, &header, |
|
1214 &payload)) { |
|
1215 GST_DEBUG_OBJECT (sink, "Could not create GDP packet from caps"); |
|
1216 return FALSE; |
|
1217 } |
|
1218 gst_multi_fd_sink_client_queue_data (sink, client, (gchar *) header, length); |
|
1219 |
|
1220 length = gst_dp_header_payload_length (header); |
|
1221 gst_multi_fd_sink_client_queue_data (sink, client, (gchar *) payload, length); |
|
1222 |
|
1223 return TRUE; |
|
1224 } |
|
1225 |
|
1226 static gboolean |
|
1227 is_sync_frame (GstMultiFdSink * sink, GstBuffer * buffer) |
|
1228 { |
|
1229 if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT)) { |
|
1230 return FALSE; |
|
1231 } else if (!GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_IN_CAPS)) { |
|
1232 return TRUE; |
|
1233 } |
|
1234 |
|
1235 return FALSE; |
|
1236 } |
|
1237 |
|
1238 /* queue the given buffer for the given client, possibly adding the GDP |
|
1239 * header if GDP is being used */ |
|
1240 static gboolean |
|
1241 gst_multi_fd_sink_client_queue_buffer (GstMultiFdSink * sink, |
|
1242 GstTCPClient * client, GstBuffer * buffer) |
|
1243 { |
|
1244 GstCaps *caps; |
|
1245 |
|
1246 /* TRUE: send them if the new caps have them */ |
|
1247 gboolean send_streamheader = FALSE; |
|
1248 GstStructure *s; |
|
1249 |
|
1250 /* before we queue the buffer, we check if we need to queue streamheader |
|
1251 * buffers (because it's a new client, or because they changed) */ |
|
1252 caps = gst_buffer_get_caps (buffer); /* cleaned up after streamheader */ |
|
1253 if (!client->caps) { |
|
1254 GST_DEBUG_OBJECT (sink, |
|
1255 "[fd %5d] no previous caps for this client, send streamheader", |
|
1256 client->fd.fd); |
|
1257 send_streamheader = TRUE; |
|
1258 client->caps = gst_caps_ref (caps); |
|
1259 } else { |
|
1260 /* there were previous caps recorded, so compare */ |
|
1261 if (!gst_caps_is_equal (caps, client->caps)) { |
|
1262 const GValue *sh1, *sh2; |
|
1263 |
|
1264 /* caps are not equal, but could still have the same streamheader */ |
|
1265 s = gst_caps_get_structure (caps, 0); |
|
1266 if (!gst_structure_has_field (s, "streamheader")) { |
|
1267 /* no new streamheader, so nothing new to send */ |
|
1268 GST_DEBUG_OBJECT (sink, |
|
1269 "[fd %5d] new caps do not have streamheader, not sending", |
|
1270 client->fd.fd); |
|
1271 } else { |
|
1272 /* there is a new streamheader */ |
|
1273 s = gst_caps_get_structure (client->caps, 0); |
|
1274 if (!gst_structure_has_field (s, "streamheader")) { |
|
1275 /* no previous streamheader, so send the new one */ |
|
1276 GST_DEBUG_OBJECT (sink, |
|
1277 "[fd %5d] previous caps did not have streamheader, sending", |
|
1278 client->fd.fd); |
|
1279 send_streamheader = TRUE; |
|
1280 } else { |
|
1281 /* both old and new caps have streamheader set */ |
|
1282 sh1 = gst_structure_get_value (s, "streamheader"); |
|
1283 s = gst_caps_get_structure (caps, 0); |
|
1284 sh2 = gst_structure_get_value (s, "streamheader"); |
|
1285 if (gst_value_compare (sh1, sh2) != GST_VALUE_EQUAL) { |
|
1286 GST_DEBUG_OBJECT (sink, |
|
1287 "[fd %5d] new streamheader different from old, sending", |
|
1288 client->fd.fd); |
|
1289 send_streamheader = TRUE; |
|
1290 } |
|
1291 } |
|
1292 } |
|
1293 } |
|
1294 /* Replace the old caps */ |
|
1295 gst_caps_unref (client->caps); |
|
1296 client->caps = gst_caps_ref (caps); |
|
1297 } |
|
1298 |
|
1299 if (G_UNLIKELY (send_streamheader)) { |
|
1300 const GValue *sh; |
|
1301 GArray *buffers; |
|
1302 int i; |
|
1303 |
|
1304 GST_LOG_OBJECT (sink, |
|
1305 "[fd %5d] sending streamheader from caps %" GST_PTR_FORMAT, |
|
1306 client->fd.fd, caps); |
|
1307 s = gst_caps_get_structure (caps, 0); |
|
1308 if (!gst_structure_has_field (s, "streamheader")) { |
|
1309 GST_DEBUG_OBJECT (sink, |
|
1310 "[fd %5d] no new streamheader, so nothing to send", client->fd.fd); |
|
1311 } else { |
|
1312 GST_LOG_OBJECT (sink, |
|
1313 "[fd %5d] sending streamheader from caps %" GST_PTR_FORMAT, |
|
1314 client->fd.fd, caps); |
|
1315 sh = gst_structure_get_value (s, "streamheader"); |
|
1316 g_assert (G_VALUE_TYPE (sh) == GST_TYPE_ARRAY); |
|
1317 buffers = g_value_peek_pointer (sh); |
|
1318 GST_DEBUG_OBJECT (sink, "%d streamheader buffers", buffers->len); |
|
1319 for (i = 0; i < buffers->len; ++i) { |
|
1320 GValue *bufval; |
|
1321 GstBuffer *buffer; |
|
1322 |
|
1323 bufval = &g_array_index (buffers, GValue, i); |
|
1324 g_assert (G_VALUE_TYPE (bufval) == GST_TYPE_BUFFER); |
|
1325 buffer = g_value_peek_pointer (bufval); |
|
1326 GST_DEBUG_OBJECT (sink, |
|
1327 "[fd %5d] queueing streamheader buffer of length %d", |
|
1328 client->fd.fd, GST_BUFFER_SIZE (buffer)); |
|
1329 gst_buffer_ref (buffer); |
|
1330 |
|
1331 if (sink->protocol == GST_TCP_PROTOCOL_GDP) { |
|
1332 guint8 *header; |
|
1333 guint len; |
|
1334 |
|
1335 if (!gst_dp_header_from_buffer (buffer, sink->header_flags, &len, |
|
1336 &header)) { |
|
1337 GST_DEBUG_OBJECT (sink, |
|
1338 "[fd %5d] could not create header, removing client", |
|
1339 client->fd.fd); |
|
1340 return FALSE; |
|
1341 } |
|
1342 gst_multi_fd_sink_client_queue_data (sink, client, (gchar *) header, |
|
1343 len); |
|
1344 } |
|
1345 |
|
1346 client->sending = g_slist_append (client->sending, buffer); |
|
1347 } |
|
1348 } |
|
1349 } |
|
1350 |
|
1351 gst_caps_unref (caps); |
|
1352 caps = NULL; |
|
1353 /* now we can send the buffer, possibly sending a GDP header first */ |
|
1354 if (sink->protocol == GST_TCP_PROTOCOL_GDP) { |
|
1355 guint8 *header; |
|
1356 guint len; |
|
1357 |
|
1358 if (!gst_dp_header_from_buffer (buffer, sink->header_flags, &len, &header)) { |
|
1359 GST_DEBUG_OBJECT (sink, |
|
1360 "[fd %5d] could not create header, removing client", client->fd.fd); |
|
1361 return FALSE; |
|
1362 } |
|
1363 gst_multi_fd_sink_client_queue_data (sink, client, (gchar *) header, len); |
|
1364 } |
|
1365 |
|
1366 GST_LOG_OBJECT (sink, "[fd %5d] queueing buffer of length %d", |
|
1367 client->fd.fd, GST_BUFFER_SIZE (buffer)); |
|
1368 |
|
1369 gst_buffer_ref (buffer); |
|
1370 client->sending = g_slist_append (client->sending, buffer); |
|
1371 |
|
1372 return TRUE; |
|
1373 } |
|
1374 |
|
1375 /* find the keyframe in the list of buffers starting the |
|
1376 * search from @idx. @direction as -1 will search backwards, |
|
1377 * 1 will search forwards. |
|
1378 * Returns: the index or -1 if there is no keyframe after idx. |
|
1379 */ |
|
1380 static gint |
|
1381 find_syncframe (GstMultiFdSink * sink, gint idx, gint direction) |
|
1382 { |
|
1383 gint i, len, result; |
|
1384 |
|
1385 /* take length of queued buffers */ |
|
1386 len = sink->bufqueue->len; |
|
1387 |
|
1388 /* assume we don't find a keyframe */ |
|
1389 result = -1; |
|
1390 |
|
1391 /* then loop over all buffers to find the first keyframe */ |
|
1392 for (i = idx; i >= 0 && i < len; i += direction) { |
|
1393 GstBuffer *buf; |
|
1394 |
|
1395 buf = g_array_index (sink->bufqueue, GstBuffer *, i); |
|
1396 if (is_sync_frame (sink, buf)) { |
|
1397 GST_LOG_OBJECT (sink, "found keyframe at %d from %d, direction %d", |
|
1398 i, idx, direction); |
|
1399 result = i; |
|
1400 break; |
|
1401 } |
|
1402 } |
|
1403 return result; |
|
1404 } |
|
1405 |
|
1406 #define find_next_syncframe(s,i) find_syncframe(s,i,1) |
|
1407 #define find_prev_syncframe(s,i) find_syncframe(s,i,-1) |
|
1408 |
|
1409 /* Get the number of buffers from the buffer queue needed to satisfy |
|
1410 * the maximum max in the configured units. |
|
1411 * If units are not BUFFERS, and there are insufficient buffers in the |
|
1412 * queue to satify the limit, return len(queue) + 1 */ |
|
1413 static gint |
|
1414 get_buffers_max (GstMultiFdSink * sink, gint64 max) |
|
1415 { |
|
1416 switch (sink->unit_type) { |
|
1417 case GST_UNIT_TYPE_BUFFERS: |
|
1418 return max; |
|
1419 case GST_UNIT_TYPE_TIME: |
|
1420 { |
|
1421 GstBuffer *buf; |
|
1422 int i; |
|
1423 int len; |
|
1424 gint64 diff; |
|
1425 GstClockTime first = GST_CLOCK_TIME_NONE; |
|
1426 |
|
1427 len = sink->bufqueue->len; |
|
1428 |
|
1429 for (i = 0; i < len; i++) { |
|
1430 buf = g_array_index (sink->bufqueue, GstBuffer *, i); |
|
1431 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) { |
|
1432 if (first == -1) |
|
1433 first = GST_BUFFER_TIMESTAMP (buf); |
|
1434 |
|
1435 diff = first - GST_BUFFER_TIMESTAMP (buf); |
|
1436 |
|
1437 if (diff > max) |
|
1438 return i + 1; |
|
1439 } |
|
1440 } |
|
1441 return len + 1; |
|
1442 } |
|
1443 case GST_UNIT_TYPE_BYTES: |
|
1444 { |
|
1445 GstBuffer *buf; |
|
1446 int i; |
|
1447 int len; |
|
1448 gint acc = 0; |
|
1449 |
|
1450 len = sink->bufqueue->len; |
|
1451 |
|
1452 for (i = 0; i < len; i++) { |
|
1453 buf = g_array_index (sink->bufqueue, GstBuffer *, i); |
|
1454 acc += GST_BUFFER_SIZE (buf); |
|
1455 |
|
1456 if (acc > max) |
|
1457 return i + 1; |
|
1458 } |
|
1459 return len + 1; |
|
1460 } |
|
1461 default: |
|
1462 return max; |
|
1463 } |
|
1464 } |
|
1465 |
|
1466 /* find the positions in the buffer queue where *_min and *_max |
|
1467 * is satisfied |
|
1468 */ |
|
1469 /* count the amount of data in the buffers and return the index |
|
1470 * that satifies the given limits. |
|
1471 * |
|
1472 * Returns: index @idx in the buffer queue so that the given limits are |
|
1473 * satisfied. TRUE if all the limits could be satisfied, FALSE if not |
|
1474 * enough data was in the queue. |
|
1475 * |
|
1476 * FIXME, this code might now work if any of the units is in buffers... |
|
1477 */ |
|
1478 static gboolean |
|
1479 find_limits (GstMultiFdSink * sink, |
|
1480 gint * min_idx, gint bytes_min, gint buffers_min, gint64 time_min, |
|
1481 gint * max_idx, gint bytes_max, gint buffers_max, gint64 time_max) |
|
1482 { |
|
1483 GstClockTime first, time; |
|
1484 gint i, len, bytes; |
|
1485 gboolean result, max_hit; |
|
1486 |
|
1487 /* take length of queue */ |
|
1488 len = sink->bufqueue->len; |
|
1489 |
|
1490 /* this must hold */ |
|
1491 g_assert (len > 0); |
|
1492 |
|
1493 GST_LOG_OBJECT (sink, |
|
1494 "bytes_min %d, buffers_min %d, time_min %" GST_TIME_FORMAT |
|
1495 ", bytes_max %d, buffers_max %d, time_max %" GST_TIME_FORMAT, bytes_min, |
|
1496 buffers_min, GST_TIME_ARGS (time_min), bytes_max, buffers_max, |
|
1497 GST_TIME_ARGS (time_max)); |
|
1498 |
|
1499 /* do the trivial buffer limit test */ |
|
1500 if (buffers_min != -1 && len < buffers_min) { |
|
1501 *min_idx = len - 1; |
|
1502 *max_idx = len - 1; |
|
1503 return FALSE; |
|
1504 } |
|
1505 |
|
1506 result = FALSE; |
|
1507 /* else count bytes and time */ |
|
1508 first = -1; |
|
1509 bytes = 0; |
|
1510 /* unset limits */ |
|
1511 *min_idx = -1; |
|
1512 *max_idx = -1; |
|
1513 max_hit = FALSE; |
|
1514 |
|
1515 i = 0; |
|
1516 /* loop through the buffers, when a limit is ok, mark it |
|
1517 * as -1, we have at least one buffer in the queue. */ |
|
1518 do { |
|
1519 GstBuffer *buf; |
|
1520 |
|
1521 /* if we checked all min limits, update result */ |
|
1522 if (bytes_min == -1 && time_min == -1 && *min_idx == -1) { |
|
1523 /* don't go below 0 */ |
|
1524 *min_idx = MAX (i - 1, 0); |
|
1525 } |
|
1526 /* if we reached one max limit break out */ |
|
1527 if (max_hit) { |
|
1528 /* i > 0 when we get here, we subtract one to get the position |
|
1529 * of the previous buffer. */ |
|
1530 *max_idx = i - 1; |
|
1531 /* we have valid complete result if we found a min_idx too */ |
|
1532 result = *min_idx != -1; |
|
1533 break; |
|
1534 } |
|
1535 buf = g_array_index (sink->bufqueue, GstBuffer *, i); |
|
1536 |
|
1537 bytes += GST_BUFFER_SIZE (buf); |
|
1538 |
|
1539 /* take timestamp and save for the base first timestamp */ |
|
1540 if ((time = GST_BUFFER_TIMESTAMP (buf)) != -1) { |
|
1541 GST_LOG_OBJECT (sink, "Ts %" GST_TIME_FORMAT " on buffer", |
|
1542 GST_TIME_ARGS (time)); |
|
1543 if (first == -1) |
|
1544 first = time; |
|
1545 |
|
1546 /* increase max usage if we did not fill enough. Note that |
|
1547 * buffers are sorted from new to old, so the first timestamp is |
|
1548 * bigger than the next one. */ |
|
1549 if (time_min != -1 && first - time >= time_min) |
|
1550 time_min = -1; |
|
1551 if (time_max != -1 && first - time >= time_max) |
|
1552 max_hit = TRUE; |
|
1553 } else { |
|
1554 GST_LOG_OBJECT (sink, "No timestamp on buffer"); |
|
1555 } |
|
1556 /* time is OK or unknown, check and increase if not enough bytes */ |
|
1557 if (bytes_min != -1) { |
|
1558 if (bytes >= bytes_min) |
|
1559 bytes_min = -1; |
|
1560 } |
|
1561 if (bytes_max != -1) { |
|
1562 if (bytes >= bytes_max) { |
|
1563 max_hit = TRUE; |
|
1564 } |
|
1565 } |
|
1566 i++; |
|
1567 } |
|
1568 while (i < len); |
|
1569 |
|
1570 /* if we did not hit the max or min limit, set to buffer size */ |
|
1571 if (*max_idx == -1) |
|
1572 *max_idx = len - 1; |
|
1573 /* make sure min does not exceed max */ |
|
1574 if (*min_idx == -1) |
|
1575 *min_idx = *max_idx; |
|
1576 |
|
1577 return result; |
|
1578 } |
|
1579 |
|
1580 /* parse the unit/value pair and assign it to the result value of the |
|
1581 * right type, leave the other values untouched |
|
1582 * |
|
1583 * Returns: FALSE if the unit is unknown or undefined. TRUE otherwise. |
|
1584 */ |
|
1585 static gboolean |
|
1586 assign_value (GstUnitType unit, guint64 value, gint * bytes, gint * buffers, |
|
1587 GstClockTime * time) |
|
1588 { |
|
1589 gboolean res = TRUE; |
|
1590 |
|
1591 /* set only the limit of the given format to the given value */ |
|
1592 switch (unit) { |
|
1593 case GST_UNIT_TYPE_BUFFERS: |
|
1594 *buffers = (gint) value; |
|
1595 break; |
|
1596 case GST_UNIT_TYPE_TIME: |
|
1597 *time = value; |
|
1598 break; |
|
1599 case GST_UNIT_TYPE_BYTES: |
|
1600 *bytes = (gint) value; |
|
1601 break; |
|
1602 case GST_UNIT_TYPE_UNDEFINED: |
|
1603 default: |
|
1604 res = FALSE; |
|
1605 break; |
|
1606 } |
|
1607 return res; |
|
1608 } |
|
1609 |
|
1610 /* count the index in the buffer queue to satisfy the given unit |
|
1611 * and value pair starting from buffer at index 0. |
|
1612 * |
|
1613 * Returns: TRUE if there was enough data in the queue to satisfy the |
|
1614 * burst values. @idx contains the index in the buffer that contains enough |
|
1615 * data to satisfy the limits or the last buffer in the queue when the |
|
1616 * function returns FALSE. |
|
1617 */ |
|
1618 static gboolean |
|
1619 count_burst_unit (GstMultiFdSink * sink, gint * min_idx, GstUnitType min_unit, |
|
1620 guint64 min_value, gint * max_idx, GstUnitType max_unit, guint64 max_value) |
|
1621 { |
|
1622 gint bytes_min = -1, buffers_min = -1; |
|
1623 gint bytes_max = -1, buffers_max = -1; |
|
1624 GstClockTime time_min = GST_CLOCK_TIME_NONE, time_max = GST_CLOCK_TIME_NONE; |
|
1625 |
|
1626 assign_value (min_unit, min_value, &bytes_min, &buffers_min, &time_min); |
|
1627 assign_value (max_unit, max_value, &bytes_max, &buffers_max, &time_max); |
|
1628 |
|
1629 return find_limits (sink, min_idx, bytes_min, buffers_min, time_min, |
|
1630 max_idx, bytes_max, buffers_max, time_max); |
|
1631 } |
|
1632 |
|
1633 /* decide where in the current buffer queue this new client should start |
|
1634 * receiving buffers from. |
|
1635 * This function is called whenever a client is connected and has not yet |
|
1636 * received a buffer. |
|
1637 * If this returns -1, it means that we haven't found a good point to |
|
1638 * start streaming from yet, and this function should be called again later |
|
1639 * when more buffers have arrived. |
|
1640 */ |
|
1641 static gint |
|
1642 gst_multi_fd_sink_new_client (GstMultiFdSink * sink, GstTCPClient * client) |
|
1643 { |
|
1644 gint result; |
|
1645 |
|
1646 GST_DEBUG_OBJECT (sink, |
|
1647 "[fd %5d] new client, deciding where to start in queue", client->fd.fd); |
|
1648 GST_DEBUG_OBJECT (sink, "queue is currently %d buffers long", |
|
1649 sink->bufqueue->len); |
|
1650 switch (client->sync_method) { |
|
1651 case GST_SYNC_METHOD_LATEST: |
|
1652 /* no syncing, we are happy with whatever the client is going to get */ |
|
1653 result = client->bufpos; |
|
1654 GST_DEBUG_OBJECT (sink, |
|
1655 "[fd %5d] SYNC_METHOD_LATEST, position %d", client->fd.fd, result); |
|
1656 break; |
|
1657 case GST_SYNC_METHOD_NEXT_KEYFRAME: |
|
1658 { |
|
1659 /* if one of the new buffers (between client->bufpos and 0) in the queue |
|
1660 * is a sync point, we can proceed, otherwise we need to keep waiting */ |
|
1661 GST_LOG_OBJECT (sink, |
|
1662 "[fd %5d] new client, bufpos %d, waiting for keyframe", client->fd.fd, |
|
1663 client->bufpos); |
|
1664 |
|
1665 result = find_prev_syncframe (sink, client->bufpos); |
|
1666 if (result != -1) { |
|
1667 GST_DEBUG_OBJECT (sink, |
|
1668 "[fd %5d] SYNC_METHOD_NEXT_KEYFRAME: result %d", |
|
1669 client->fd.fd, result); |
|
1670 break; |
|
1671 } |
|
1672 |
|
1673 /* client is not on a syncbuffer, need to skip these buffers and |
|
1674 * wait some more */ |
|
1675 GST_LOG_OBJECT (sink, |
|
1676 "[fd %5d] new client, skipping buffer(s), no syncpoint found", |
|
1677 client->fd.fd); |
|
1678 client->bufpos = -1; |
|
1679 break; |
|
1680 } |
|
1681 case GST_SYNC_METHOD_LATEST_KEYFRAME: |
|
1682 { |
|
1683 GST_DEBUG_OBJECT (sink, |
|
1684 "[fd %5d] SYNC_METHOD_LATEST_KEYFRAME", client->fd.fd); |
|
1685 |
|
1686 /* for new clients we initially scan the complete buffer queue for |
|
1687 * a sync point when a buffer is added. If we don't find a keyframe, |
|
1688 * we need to wait for the next keyframe and so we change the client's |
|
1689 * sync method to GST_SYNC_METHOD_NEXT_KEYFRAME. |
|
1690 */ |
|
1691 result = find_next_syncframe (sink, 0); |
|
1692 if (result != -1) { |
|
1693 GST_DEBUG_OBJECT (sink, |
|
1694 "[fd %5d] SYNC_METHOD_LATEST_KEYFRAME: result %d", client->fd.fd, |
|
1695 result); |
|
1696 break; |
|
1697 } |
|
1698 |
|
1699 GST_DEBUG_OBJECT (sink, |
|
1700 "[fd %5d] SYNC_METHOD_LATEST_KEYFRAME: no keyframe found, " |
|
1701 "switching to SYNC_METHOD_NEXT_KEYFRAME", client->fd.fd); |
|
1702 /* throw client to the waiting state */ |
|
1703 client->bufpos = -1; |
|
1704 /* and make client sync to next keyframe */ |
|
1705 client->sync_method = GST_SYNC_METHOD_NEXT_KEYFRAME; |
|
1706 break; |
|
1707 } |
|
1708 case GST_SYNC_METHOD_BURST: |
|
1709 { |
|
1710 gboolean ok; |
|
1711 gint max; |
|
1712 |
|
1713 /* move to the position where we satisfy the client's burst |
|
1714 * parameters. If we could not satisfy the parameters because there |
|
1715 * is not enough data, we just send what we have (which is in result). |
|
1716 * We use the max value to limit the search |
|
1717 */ |
|
1718 ok = count_burst_unit (sink, &result, client->burst_min_unit, |
|
1719 client->burst_min_value, &max, client->burst_max_unit, |
|
1720 client->burst_max_value); |
|
1721 GST_DEBUG_OBJECT (sink, |
|
1722 "[fd %5d] SYNC_METHOD_BURST: burst_unit returned %d, result %d", |
|
1723 client->fd.fd, ok, result); |
|
1724 |
|
1725 GST_LOG_OBJECT (sink, "min %d, max %d", result, max); |
|
1726 |
|
1727 /* we hit the max and it is below the min, use that then */ |
|
1728 if (max != -1 && max <= result) { |
|
1729 result = MAX (max - 1, 0); |
|
1730 GST_DEBUG_OBJECT (sink, |
|
1731 "[fd %5d] SYNC_METHOD_BURST: result above max, taken down to %d", |
|
1732 client->fd.fd, result); |
|
1733 } |
|
1734 break; |
|
1735 } |
|
1736 case GST_SYNC_METHOD_BURST_KEYFRAME: |
|
1737 { |
|
1738 gboolean ok; |
|
1739 gint min_idx, max_idx; |
|
1740 gint next_syncframe, prev_syncframe; |
|
1741 |
|
1742 /* BURST_KEYFRAME: |
|
1743 * |
|
1744 * _always_ start sending a keyframe to the client. We first search |
|
1745 * a keyframe between min/max limits. If there is none, we send it the |
|
1746 * last keyframe before min. If there is none, the behaviour is like |
|
1747 * NEXT_KEYFRAME. |
|
1748 */ |
|
1749 /* gather burst limits */ |
|
1750 ok = count_burst_unit (sink, &min_idx, client->burst_min_unit, |
|
1751 client->burst_min_value, &max_idx, client->burst_max_unit, |
|
1752 client->burst_max_value); |
|
1753 |
|
1754 GST_LOG_OBJECT (sink, "min %d, max %d", min_idx, max_idx); |
|
1755 |
|
1756 /* first find a keyframe after min_idx */ |
|
1757 next_syncframe = find_next_syncframe (sink, min_idx); |
|
1758 if (next_syncframe != -1 && next_syncframe < max_idx) { |
|
1759 /* we have a valid keyframe and it's below the max */ |
|
1760 GST_LOG_OBJECT (sink, "found keyframe in min/max limits"); |
|
1761 result = next_syncframe; |
|
1762 break; |
|
1763 } |
|
1764 |
|
1765 /* no valid keyframe, try to find one below min */ |
|
1766 prev_syncframe = find_prev_syncframe (sink, min_idx); |
|
1767 if (prev_syncframe != -1) { |
|
1768 GST_WARNING_OBJECT (sink, |
|
1769 "using keyframe below min in BURST_KEYFRAME sync mode"); |
|
1770 result = prev_syncframe; |
|
1771 break; |
|
1772 } |
|
1773 |
|
1774 /* no prev keyframe or not enough data */ |
|
1775 GST_WARNING_OBJECT (sink, |
|
1776 "no prev keyframe found in BURST_KEYFRAME sync mode, waiting for next"); |
|
1777 |
|
1778 /* throw client to the waiting state */ |
|
1779 client->bufpos = -1; |
|
1780 /* and make client sync to next keyframe */ |
|
1781 client->sync_method = GST_SYNC_METHOD_NEXT_KEYFRAME; |
|
1782 result = -1; |
|
1783 break; |
|
1784 } |
|
1785 case GST_SYNC_METHOD_BURST_WITH_KEYFRAME: |
|
1786 { |
|
1787 gboolean ok; |
|
1788 gint min_idx, max_idx; |
|
1789 gint next_syncframe; |
|
1790 |
|
1791 /* BURST_WITH_KEYFRAME: |
|
1792 * |
|
1793 * try to start sending a keyframe to the client. We first search |
|
1794 * a keyframe between min/max limits. If there is none, we send it the |
|
1795 * amount of data up 'till min. |
|
1796 */ |
|
1797 /* gather enough data to burst */ |
|
1798 ok = count_burst_unit (sink, &min_idx, client->burst_min_unit, |
|
1799 client->burst_min_value, &max_idx, client->burst_max_unit, |
|
1800 client->burst_max_value); |
|
1801 |
|
1802 GST_LOG_OBJECT (sink, "min %d, max %d", min_idx, max_idx); |
|
1803 |
|
1804 /* first find a keyframe after min_idx */ |
|
1805 next_syncframe = find_next_syncframe (sink, min_idx); |
|
1806 if (next_syncframe != -1 && next_syncframe < max_idx) { |
|
1807 /* we have a valid keyframe and it's below the max */ |
|
1808 GST_LOG_OBJECT (sink, "found keyframe in min/max limits"); |
|
1809 result = next_syncframe; |
|
1810 break; |
|
1811 } |
|
1812 |
|
1813 /* no keyframe, send data from min_idx */ |
|
1814 GST_WARNING_OBJECT (sink, "using min in BURST_WITH_KEYFRAME sync mode"); |
|
1815 |
|
1816 /* make sure we don't go over the max limit */ |
|
1817 if (max_idx != -1 && max_idx <= min_idx) { |
|
1818 result = MAX (max_idx - 1, 0); |
|
1819 } else { |
|
1820 result = min_idx; |
|
1821 } |
|
1822 |
|
1823 break; |
|
1824 } |
|
1825 default: |
|
1826 g_warning ("unknown sync method %d", client->sync_method); |
|
1827 result = client->bufpos; |
|
1828 break; |
|
1829 } |
|
1830 return result; |
|
1831 } |
|
1832 |
|
1833 /* Handle a write on a client, |
|
1834 * which indicates a read request from a client. |
|
1835 * |
|
1836 * For each client we maintain a queue of GstBuffers that contain the raw bytes |
|
1837 * we need to send to the client. In the case of the GDP protocol, we create |
|
1838 * buffers out of the header bytes so that we can focus only on sending |
|
1839 * buffers. |
|
1840 * |
|
1841 * We first check to see if we need to send caps (in GDP) and streamheaders. |
|
1842 * If so, we queue them. |
|
1843 * |
|
1844 * Then we run into the main loop that tries to send as many buffers as |
|
1845 * possible. It will first exhaust the client->sending queue and if the queue |
|
1846 * is empty, it will pick a buffer from the global queue. |
|
1847 * |
|
1848 * Sending the buffers from the client->sending queue is basically writing |
|
1849 * the bytes to the socket and maintaining a count of the bytes that were |
|
1850 * sent. When the buffer is completely sent, it is removed from the |
|
1851 * client->sending queue and we try to pick a new buffer for sending. |
|
1852 * |
|
1853 * When the sending returns a partial buffer we stop sending more data as |
|
1854 * the next send operation could block. |
|
1855 * |
|
1856 * This functions returns FALSE if some error occured. |
|
1857 */ |
|
1858 static gboolean |
|
1859 gst_multi_fd_sink_handle_client_write (GstMultiFdSink * sink, |
|
1860 GstTCPClient * client) |
|
1861 { |
|
1862 int fd = client->fd.fd; |
|
1863 gboolean more; |
|
1864 gboolean res; |
|
1865 gboolean flushing; |
|
1866 GstClockTime now; |
|
1867 GTimeVal nowtv; |
|
1868 |
|
1869 g_get_current_time (&nowtv); |
|
1870 now = GST_TIMEVAL_TO_TIME (nowtv); |
|
1871 |
|
1872 flushing = client->status == GST_CLIENT_STATUS_FLUSHING; |
|
1873 |
|
1874 /* when using GDP, first check if we have queued caps yet */ |
|
1875 if (sink->protocol == GST_TCP_PROTOCOL_GDP) { |
|
1876 /* don't need to do anything when the client is flushing */ |
|
1877 if (!client->caps_sent && !flushing) { |
|
1878 GstPad *peer; |
|
1879 GstCaps *caps; |
|
1880 |
|
1881 peer = gst_pad_get_peer (GST_BASE_SINK_PAD (sink)); |
|
1882 if (!peer) { |
|
1883 GST_WARNING_OBJECT (sink, "pad has no peer"); |
|
1884 return FALSE; |
|
1885 } |
|
1886 gst_object_unref (peer); |
|
1887 |
|
1888 caps = gst_pad_get_negotiated_caps (GST_BASE_SINK_PAD (sink)); |
|
1889 if (!caps) { |
|
1890 GST_WARNING_OBJECT (sink, "pad caps not yet negotiated"); |
|
1891 return FALSE; |
|
1892 } |
|
1893 |
|
1894 /* queue caps for sending */ |
|
1895 res = gst_multi_fd_sink_client_queue_caps (sink, client, caps); |
|
1896 |
|
1897 gst_caps_unref (caps); |
|
1898 |
|
1899 if (!res) { |
|
1900 GST_DEBUG_OBJECT (sink, "Failed queueing caps, removing client"); |
|
1901 return FALSE; |
|
1902 } |
|
1903 client->caps_sent = TRUE; |
|
1904 } |
|
1905 } |
|
1906 |
|
1907 more = TRUE; |
|
1908 do { |
|
1909 gint maxsize; |
|
1910 |
|
1911 if (!client->sending) { |
|
1912 /* client is not working on a buffer */ |
|
1913 if (client->bufpos == -1) { |
|
1914 /* client is too fast, remove from write queue until new buffer is |
|
1915 * available */ |
|
1916 gst_poll_fd_ctl_write (sink->fdset, &client->fd, FALSE); |
|
1917 /* if we flushed out all of the client buffers, we can stop */ |
|
1918 if (client->flushcount == 0) |
|
1919 goto flushed; |
|
1920 |
|
1921 return TRUE; |
|
1922 } else { |
|
1923 /* client can pick a buffer from the global queue */ |
|
1924 GstBuffer *buf; |
|
1925 |
|
1926 /* for new connections, we need to find a good spot in the |
|
1927 * bufqueue to start streaming from */ |
|
1928 if (client->new_connection && !flushing) { |
|
1929 gint position = gst_multi_fd_sink_new_client (sink, client); |
|
1930 |
|
1931 if (position >= 0) { |
|
1932 /* we got a valid spot in the queue */ |
|
1933 client->new_connection = FALSE; |
|
1934 client->bufpos = position; |
|
1935 } else { |
|
1936 /* cannot send data to this client yet */ |
|
1937 gst_poll_fd_ctl_write (sink->fdset, &client->fd, FALSE); |
|
1938 return TRUE; |
|
1939 } |
|
1940 } |
|
1941 |
|
1942 /* we flushed all remaining buffers, no need to get a new one */ |
|
1943 if (client->flushcount == 0) |
|
1944 goto flushed; |
|
1945 |
|
1946 /* grab buffer */ |
|
1947 buf = g_array_index (sink->bufqueue, GstBuffer *, client->bufpos); |
|
1948 client->bufpos--; |
|
1949 |
|
1950 /* decrease flushcount */ |
|
1951 if (client->flushcount != -1) |
|
1952 client->flushcount--; |
|
1953 |
|
1954 GST_LOG_OBJECT (sink, "[fd %5d] client %p at position %d", |
|
1955 fd, client, client->bufpos); |
|
1956 |
|
1957 /* queueing a buffer will ref it */ |
|
1958 gst_multi_fd_sink_client_queue_buffer (sink, client, buf); |
|
1959 |
|
1960 /* need to start from the first byte for this new buffer */ |
|
1961 client->bufoffset = 0; |
|
1962 } |
|
1963 } |
|
1964 |
|
1965 /* see if we need to send something */ |
|
1966 if (client->sending) { |
|
1967 ssize_t wrote; |
|
1968 GstBuffer *head; |
|
1969 |
|
1970 /* pick first buffer from list */ |
|
1971 head = GST_BUFFER (client->sending->data); |
|
1972 maxsize = GST_BUFFER_SIZE (head) - client->bufoffset; |
|
1973 |
|
1974 /* try to write the complete buffer */ |
|
1975 #ifdef MSG_NOSIGNAL |
|
1976 #define FLAGS MSG_NOSIGNAL |
|
1977 #else |
|
1978 #define FLAGS 0 |
|
1979 #endif |
|
1980 if (client->is_socket) { |
|
1981 wrote = |
|
1982 send (fd, GST_BUFFER_DATA (head) + client->bufoffset, maxsize, |
|
1983 FLAGS); |
|
1984 } else { |
|
1985 wrote = write (fd, GST_BUFFER_DATA (head) + client->bufoffset, maxsize); |
|
1986 } |
|
1987 |
|
1988 if (wrote < 0) { |
|
1989 /* hmm error.. */ |
|
1990 if (errno == EAGAIN) { |
|
1991 /* nothing serious, resource was unavailable, try again later */ |
|
1992 more = FALSE; |
|
1993 } else if (errno == ECONNRESET) { |
|
1994 goto connection_reset; |
|
1995 } else { |
|
1996 goto write_error; |
|
1997 } |
|
1998 } else { |
|
1999 if (wrote < maxsize) { |
|
2000 /* partial write means that the client cannot read more and we should |
|
2001 * stop sending more */ |
|
2002 GST_LOG_OBJECT (sink, |
|
2003 "partial write on %d of %" G_GSSIZE_FORMAT " bytes", fd, wrote); |
|
2004 client->bufoffset += wrote; |
|
2005 more = FALSE; |
|
2006 } else { |
|
2007 /* complete buffer was written, we can proceed to the next one */ |
|
2008 client->sending = g_slist_remove (client->sending, head); |
|
2009 gst_buffer_unref (head); |
|
2010 /* make sure we start from byte 0 for the next buffer */ |
|
2011 client->bufoffset = 0; |
|
2012 } |
|
2013 /* update stats */ |
|
2014 client->bytes_sent += wrote; |
|
2015 client->last_activity_time = now; |
|
2016 sink->bytes_served += wrote; |
|
2017 } |
|
2018 } |
|
2019 } while (more); |
|
2020 |
|
2021 return TRUE; |
|
2022 |
|
2023 /* ERRORS */ |
|
2024 flushed: |
|
2025 { |
|
2026 GST_DEBUG_OBJECT (sink, "[fd %5d] flushed, removing", fd); |
|
2027 client->status = GST_CLIENT_STATUS_REMOVED; |
|
2028 return FALSE; |
|
2029 } |
|
2030 connection_reset: |
|
2031 { |
|
2032 GST_DEBUG_OBJECT (sink, "[fd %5d] connection reset by peer, removing", fd); |
|
2033 client->status = GST_CLIENT_STATUS_CLOSED; |
|
2034 return FALSE; |
|
2035 } |
|
2036 write_error: |
|
2037 { |
|
2038 GST_WARNING_OBJECT (sink, |
|
2039 "[fd %5d] could not write, removing client: %s (%d)", fd, |
|
2040 g_strerror (errno), errno); |
|
2041 client->status = GST_CLIENT_STATUS_ERROR; |
|
2042 return FALSE; |
|
2043 } |
|
2044 } |
|
2045 |
|
2046 /* calculate the new position for a client after recovery. This function |
|
2047 * does not update the client position but merely returns the required |
|
2048 * position. |
|
2049 */ |
|
2050 static gint |
|
2051 gst_multi_fd_sink_recover_client (GstMultiFdSink * sink, GstTCPClient * client) |
|
2052 { |
|
2053 gint newbufpos; |
|
2054 |
|
2055 GST_WARNING_OBJECT (sink, |
|
2056 "[fd %5d] client %p is lagging at %d, recover using policy %d", |
|
2057 client->fd.fd, client, client->bufpos, sink->recover_policy); |
|
2058 |
|
2059 switch (sink->recover_policy) { |
|
2060 case GST_RECOVER_POLICY_NONE: |
|
2061 /* do nothing, client will catch up or get kicked out when it reaches |
|
2062 * the hard max */ |
|
2063 newbufpos = client->bufpos; |
|
2064 break; |
|
2065 case GST_RECOVER_POLICY_RESYNC_LATEST: |
|
2066 /* move to beginning of queue */ |
|
2067 newbufpos = -1; |
|
2068 break; |
|
2069 case GST_RECOVER_POLICY_RESYNC_SOFT_LIMIT: |
|
2070 /* move to beginning of soft max */ |
|
2071 newbufpos = get_buffers_max (sink, sink->units_soft_max); |
|
2072 break; |
|
2073 case GST_RECOVER_POLICY_RESYNC_KEYFRAME: |
|
2074 /* find keyframe in buffers, we search backwards to find the |
|
2075 * closest keyframe relative to what this client already received. */ |
|
2076 newbufpos = MIN (sink->bufqueue->len - 1, |
|
2077 get_buffers_max (sink, sink->units_soft_max) - 1); |
|
2078 |
|
2079 while (newbufpos >= 0) { |
|
2080 GstBuffer *buf; |
|
2081 |
|
2082 buf = g_array_index (sink->bufqueue, GstBuffer *, newbufpos); |
|
2083 if (is_sync_frame (sink, buf)) { |
|
2084 /* found a buffer that is not a delta unit */ |
|
2085 break; |
|
2086 } |
|
2087 newbufpos--; |
|
2088 } |
|
2089 break; |
|
2090 default: |
|
2091 /* unknown recovery procedure */ |
|
2092 newbufpos = get_buffers_max (sink, sink->units_soft_max); |
|
2093 break; |
|
2094 } |
|
2095 return newbufpos; |
|
2096 } |
|
2097 |
|
2098 /* Queue a buffer on the global queue. |
|
2099 * |
|
2100 * This function adds the buffer to the front of a GArray. It removes the |
|
2101 * tail buffer if the max queue size is exceeded, unreffing the queued buffer. |
|
2102 * Note that unreffing the buffer is not a problem as clients who |
|
2103 * started writing out this buffer will still have a reference to it in the |
|
2104 * client->sending queue. |
|
2105 * |
|
2106 * After adding the buffer, we update all client positions in the queue. If |
|
2107 * a client moves over the soft max, we start the recovery procedure for this |
|
2108 * slow client. If it goes over the hard max, it is put into the slow list |
|
2109 * and removed. |
|
2110 * |
|
2111 * Special care is taken of clients that were waiting for a new buffer (they |
|
2112 * had a position of -1) because they can proceed after adding this new buffer. |
|
2113 * This is done by adding the client back into the write fd_set and signalling |
|
2114 * the select thread that the fd_set changed. |
|
2115 */ |
|
2116 static void |
|
2117 gst_multi_fd_sink_queue_buffer (GstMultiFdSink * sink, GstBuffer * buf) |
|
2118 { |
|
2119 GList *clients, *next; |
|
2120 gint queuelen; |
|
2121 gboolean need_signal = FALSE; |
|
2122 gint max_buffer_usage; |
|
2123 gint i; |
|
2124 GTimeVal nowtv; |
|
2125 GstClockTime now; |
|
2126 gint max_buffers, soft_max_buffers; |
|
2127 guint cookie; |
|
2128 |
|
2129 g_get_current_time (&nowtv); |
|
2130 now = GST_TIMEVAL_TO_TIME (nowtv); |
|
2131 |
|
2132 CLIENTS_LOCK (sink); |
|
2133 /* add buffer to queue */ |
|
2134 g_array_prepend_val (sink->bufqueue, buf); |
|
2135 queuelen = sink->bufqueue->len; |
|
2136 |
|
2137 if (sink->units_max > 0) |
|
2138 max_buffers = get_buffers_max (sink, sink->units_max); |
|
2139 else |
|
2140 max_buffers = -1; |
|
2141 |
|
2142 if (sink->units_soft_max > 0) |
|
2143 soft_max_buffers = get_buffers_max (sink, sink->units_soft_max); |
|
2144 else |
|
2145 soft_max_buffers = -1; |
|
2146 GST_LOG_OBJECT (sink, "Using max %d, softmax %d", max_buffers, |
|
2147 soft_max_buffers); |
|
2148 |
|
2149 /* then loop over the clients and update the positions */ |
|
2150 max_buffer_usage = 0; |
|
2151 |
|
2152 restart: |
|
2153 cookie = sink->clients_cookie; |
|
2154 for (clients = sink->clients; clients; clients = next) { |
|
2155 GstTCPClient *client; |
|
2156 |
|
2157 if (cookie != sink->clients_cookie) { |
|
2158 GST_DEBUG_OBJECT (sink, "Clients cookie outdated, restarting"); |
|
2159 goto restart; |
|
2160 } |
|
2161 |
|
2162 client = (GstTCPClient *) clients->data; |
|
2163 next = g_list_next (clients); |
|
2164 |
|
2165 client->bufpos++; |
|
2166 GST_LOG_OBJECT (sink, "[fd %5d] client %p at position %d", |
|
2167 client->fd.fd, client, client->bufpos); |
|
2168 /* check soft max if needed, recover client */ |
|
2169 if (soft_max_buffers > 0 && client->bufpos >= soft_max_buffers) { |
|
2170 gint newpos; |
|
2171 |
|
2172 newpos = gst_multi_fd_sink_recover_client (sink, client); |
|
2173 if (newpos != client->bufpos) { |
|
2174 client->dropped_buffers += client->bufpos - newpos; |
|
2175 client->bufpos = newpos; |
|
2176 client->discont = TRUE; |
|
2177 GST_INFO_OBJECT (sink, "[fd %5d] client %p position reset to %d", |
|
2178 client->fd.fd, client, client->bufpos); |
|
2179 } else { |
|
2180 GST_INFO_OBJECT (sink, |
|
2181 "[fd %5d] client %p not recovering position", |
|
2182 client->fd.fd, client); |
|
2183 } |
|
2184 } |
|
2185 /* check hard max and timeout, remove client */ |
|
2186 if ((max_buffers > 0 && client->bufpos >= max_buffers) || |
|
2187 (sink->timeout > 0 |
|
2188 && now - client->last_activity_time > sink->timeout)) { |
|
2189 /* remove client */ |
|
2190 GST_WARNING_OBJECT (sink, "[fd %5d] client %p is too slow, removing", |
|
2191 client->fd.fd, client); |
|
2192 /* remove the client, the fd set will be cleared and the select thread |
|
2193 * will be signaled */ |
|
2194 client->status = GST_CLIENT_STATUS_SLOW; |
|
2195 /* set client to invalid position while being removed */ |
|
2196 client->bufpos = -1; |
|
2197 gst_multi_fd_sink_remove_client_link (sink, clients); |
|
2198 need_signal = TRUE; |
|
2199 continue; |
|
2200 } else if (client->bufpos == 0 || client->new_connection) { |
|
2201 /* can send data to this client now. need to signal the select thread that |
|
2202 * the fd_set changed */ |
|
2203 gst_poll_fd_ctl_write (sink->fdset, &client->fd, TRUE); |
|
2204 need_signal = TRUE; |
|
2205 } |
|
2206 /* keep track of maximum buffer usage */ |
|
2207 if (client->bufpos > max_buffer_usage) { |
|
2208 max_buffer_usage = client->bufpos; |
|
2209 } |
|
2210 } |
|
2211 |
|
2212 /* make sure we respect bytes-min, buffers-min and time-min when they are set */ |
|
2213 { |
|
2214 gint usage, max; |
|
2215 |
|
2216 GST_LOG_OBJECT (sink, |
|
2217 "extending queue %d to respect time_min %" GST_TIME_FORMAT |
|
2218 ", bytes_min %d, buffers_min %d", max_buffer_usage, |
|
2219 GST_TIME_ARGS (sink->time_min), sink->bytes_min, sink->buffers_min); |
|
2220 |
|
2221 /* get index where the limits are ok, we don't really care if all limits |
|
2222 * are ok, we just queue as much as we need. We also don't compare against |
|
2223 * the max limits. */ |
|
2224 find_limits (sink, &usage, sink->bytes_min, sink->buffers_min, |
|
2225 sink->time_min, &max, -1, -1, -1); |
|
2226 |
|
2227 max_buffer_usage = MAX (max_buffer_usage, usage + 1); |
|
2228 GST_LOG_OBJECT (sink, "extended queue to %d", max_buffer_usage); |
|
2229 } |
|
2230 |
|
2231 /* now look for sync points and make sure there is at least one |
|
2232 * sync point in the queue. We only do this if the LATEST_KEYFRAME or |
|
2233 * BURST_KEYFRAME mode is selected */ |
|
2234 if (sink->def_sync_method == GST_SYNC_METHOD_LATEST_KEYFRAME || |
|
2235 sink->def_sync_method == GST_SYNC_METHOD_BURST_KEYFRAME) { |
|
2236 /* no point in searching beyond the queue length */ |
|
2237 gint limit = queuelen; |
|
2238 GstBuffer *buf; |
|
2239 |
|
2240 /* no point in searching beyond the soft-max if any. */ |
|
2241 if (soft_max_buffers) { |
|
2242 limit = MIN (limit, soft_max_buffers); |
|
2243 } |
|
2244 GST_LOG_OBJECT (sink, "extending queue to include sync point, now at %d", |
|
2245 max_buffer_usage); |
|
2246 for (i = 0; i < limit; i++) { |
|
2247 buf = g_array_index (sink->bufqueue, GstBuffer *, i); |
|
2248 if (is_sync_frame (sink, buf)) { |
|
2249 /* found a sync frame, now extend the buffer usage to |
|
2250 * include at least this frame. */ |
|
2251 max_buffer_usage = MAX (max_buffer_usage, i); |
|
2252 break; |
|
2253 } |
|
2254 } |
|
2255 GST_LOG_OBJECT (sink, "max buffer usage is now %d", max_buffer_usage); |
|
2256 } |
|
2257 |
|
2258 GST_LOG_OBJECT (sink, "len %d, usage %d", queuelen, max_buffer_usage); |
|
2259 |
|
2260 /* nobody is referencing units after max_buffer_usage so we can |
|
2261 * remove them from the queue. We remove them in reverse order as |
|
2262 * this is the most optimal for GArray. */ |
|
2263 for (i = queuelen - 1; i > max_buffer_usage; i--) { |
|
2264 GstBuffer *old; |
|
2265 |
|
2266 /* queue exceeded max size */ |
|
2267 queuelen--; |
|
2268 old = g_array_index (sink->bufqueue, GstBuffer *, i); |
|
2269 sink->bufqueue = g_array_remove_index (sink->bufqueue, i); |
|
2270 |
|
2271 /* unref tail buffer */ |
|
2272 gst_buffer_unref (old); |
|
2273 } |
|
2274 /* save for stats */ |
|
2275 sink->buffers_queued = max_buffer_usage; |
|
2276 CLIENTS_UNLOCK (sink); |
|
2277 |
|
2278 /* and send a signal to thread if fd_set changed */ |
|
2279 if (need_signal) { |
|
2280 gst_poll_restart (sink->fdset); |
|
2281 } |
|
2282 } |
|
2283 |
|
2284 /* Handle the clients. Basically does a blocking select for one |
|
2285 * of the client fds to become read or writable. We also have a |
|
2286 * filedescriptor to receive commands on that we need to check. |
|
2287 * |
|
2288 * After going out of the select call, we read and write to all |
|
2289 * clients that can do so. Badly behaving clients are put on a |
|
2290 * garbage list and removed. |
|
2291 */ |
|
2292 static void |
|
2293 gst_multi_fd_sink_handle_clients (GstMultiFdSink * sink) |
|
2294 { |
|
2295 int result; |
|
2296 GList *clients, *next; |
|
2297 gboolean try_again; |
|
2298 GstMultiFdSinkClass *fclass; |
|
2299 guint cookie; |
|
2300 |
|
2301 fclass = GST_MULTI_FD_SINK_GET_CLASS (sink); |
|
2302 |
|
2303 do { |
|
2304 try_again = FALSE; |
|
2305 |
|
2306 /* check for: |
|
2307 * - server socket input (ie, new client connections) |
|
2308 * - client socket input (ie, clients saying goodbye) |
|
2309 * - client socket output (ie, client reads) */ |
|
2310 GST_LOG_OBJECT (sink, "waiting on action on fdset"); |
|
2311 result = gst_poll_wait (sink->fdset, GST_CLOCK_TIME_NONE); |
|
2312 |
|
2313 /* < 0 is an error, 0 just means a timeout happened, which is impossible */ |
|
2314 if (result < 0) { |
|
2315 GST_WARNING_OBJECT (sink, "wait failed: %s (%d)", g_strerror (errno), |
|
2316 errno); |
|
2317 if (errno == EBADF) { |
|
2318 /* ok, so one or more of the fds is invalid. We loop over them to find |
|
2319 * the ones that give an error to the F_GETFL fcntl. */ |
|
2320 CLIENTS_LOCK (sink); |
|
2321 restart: |
|
2322 cookie = sink->clients_cookie; |
|
2323 for (clients = sink->clients; clients; clients = next) { |
|
2324 GstTCPClient *client; |
|
2325 int fd; |
|
2326 long flags; |
|
2327 int res; |
|
2328 |
|
2329 if (cookie != sink->clients_cookie) { |
|
2330 GST_DEBUG_OBJECT (sink, "Cookie changed finding bad fd"); |
|
2331 goto restart; |
|
2332 } |
|
2333 |
|
2334 client = (GstTCPClient *) clients->data; |
|
2335 next = g_list_next (clients); |
|
2336 |
|
2337 fd = client->fd.fd; |
|
2338 |
|
2339 res = fcntl (fd, F_GETFL, &flags); |
|
2340 if (res == -1) { |
|
2341 GST_WARNING_OBJECT (sink, "fnctl failed for %d, removing: %s (%d)", |
|
2342 fd, g_strerror (errno), errno); |
|
2343 if (errno == EBADF) { |
|
2344 client->status = GST_CLIENT_STATUS_ERROR; |
|
2345 gst_multi_fd_sink_remove_client_link (sink, clients); |
|
2346 } |
|
2347 } |
|
2348 } |
|
2349 CLIENTS_UNLOCK (sink); |
|
2350 /* after this, go back in the select loop as the read/writefds |
|
2351 * are not valid */ |
|
2352 try_again = TRUE; |
|
2353 } else if (errno == EINTR) { |
|
2354 /* interrupted system call, just redo the wait */ |
|
2355 try_again = TRUE; |
|
2356 } else if (errno == EBUSY) { |
|
2357 /* the call to gst_poll_wait() was flushed */ |
|
2358 return; |
|
2359 } else { |
|
2360 /* this is quite bad... */ |
|
2361 GST_ELEMENT_ERROR (sink, RESOURCE, READ, (NULL), |
|
2362 ("select failed: %s (%d)", g_strerror (errno), errno)); |
|
2363 return; |
|
2364 } |
|
2365 } else { |
|
2366 GST_LOG_OBJECT (sink, "wait done: %d sockets with events", result); |
|
2367 } |
|
2368 } while (try_again); |
|
2369 |
|
2370 /* subclasses can check fdset with this virtual function */ |
|
2371 if (fclass->wait) |
|
2372 fclass->wait (sink, sink->fdset); |
|
2373 |
|
2374 /* Check the clients */ |
|
2375 CLIENTS_LOCK (sink); |
|
2376 |
|
2377 restart2: |
|
2378 cookie = sink->clients_cookie; |
|
2379 for (clients = sink->clients; clients; clients = next) { |
|
2380 GstTCPClient *client; |
|
2381 |
|
2382 if (sink->clients_cookie != cookie) { |
|
2383 GST_DEBUG_OBJECT (sink, "Restarting loop, cookie out of date"); |
|
2384 goto restart2; |
|
2385 } |
|
2386 |
|
2387 client = (GstTCPClient *) clients->data; |
|
2388 next = g_list_next (clients); |
|
2389 |
|
2390 if (client->status != GST_CLIENT_STATUS_FLUSHING |
|
2391 && client->status != GST_CLIENT_STATUS_OK) { |
|
2392 gst_multi_fd_sink_remove_client_link (sink, clients); |
|
2393 continue; |
|
2394 } |
|
2395 |
|
2396 if (gst_poll_fd_has_closed (sink->fdset, &client->fd)) { |
|
2397 client->status = GST_CLIENT_STATUS_CLOSED; |
|
2398 gst_multi_fd_sink_remove_client_link (sink, clients); |
|
2399 continue; |
|
2400 } |
|
2401 if (gst_poll_fd_has_error (sink->fdset, &client->fd)) { |
|
2402 GST_WARNING_OBJECT (sink, "gst_poll_fd_has_error for %d", client->fd.fd); |
|
2403 client->status = GST_CLIENT_STATUS_ERROR; |
|
2404 gst_multi_fd_sink_remove_client_link (sink, clients); |
|
2405 continue; |
|
2406 } |
|
2407 if (gst_poll_fd_can_read (sink->fdset, &client->fd)) { |
|
2408 /* handle client read */ |
|
2409 if (!gst_multi_fd_sink_handle_client_read (sink, client)) { |
|
2410 gst_multi_fd_sink_remove_client_link (sink, clients); |
|
2411 continue; |
|
2412 } |
|
2413 } |
|
2414 if (gst_poll_fd_can_write (sink->fdset, &client->fd)) { |
|
2415 /* handle client write */ |
|
2416 if (!gst_multi_fd_sink_handle_client_write (sink, client)) { |
|
2417 gst_multi_fd_sink_remove_client_link (sink, clients); |
|
2418 continue; |
|
2419 } |
|
2420 } |
|
2421 } |
|
2422 CLIENTS_UNLOCK (sink); |
|
2423 } |
|
2424 |
|
2425 /* we handle the client communication in another thread so that we do not block |
|
2426 * the gstreamer thread while we select() on the client fds */ |
|
2427 static gpointer |
|
2428 gst_multi_fd_sink_thread (GstMultiFdSink * sink) |
|
2429 { |
|
2430 while (sink->running) { |
|
2431 gst_multi_fd_sink_handle_clients (sink); |
|
2432 } |
|
2433 return NULL; |
|
2434 } |
|
2435 |
|
2436 static GstFlowReturn |
|
2437 gst_multi_fd_sink_render (GstBaseSink * bsink, GstBuffer * buf) |
|
2438 { |
|
2439 GstMultiFdSink *sink; |
|
2440 gboolean in_caps; |
|
2441 GstCaps *bufcaps, *padcaps; |
|
2442 |
|
2443 sink = GST_MULTI_FD_SINK (bsink); |
|
2444 |
|
2445 g_return_val_if_fail (GST_OBJECT_FLAG_IS_SET (sink, GST_MULTI_FD_SINK_OPEN), |
|
2446 GST_FLOW_WRONG_STATE); |
|
2447 |
|
2448 /* since we check every buffer for streamheader caps, we need to make |
|
2449 * sure every buffer has caps set */ |
|
2450 bufcaps = gst_buffer_get_caps (buf); |
|
2451 padcaps = GST_PAD_CAPS (GST_BASE_SINK_PAD (bsink)); |
|
2452 |
|
2453 /* make sure we have caps on the pad */ |
|
2454 if (!padcaps && !bufcaps) |
|
2455 goto no_caps; |
|
2456 |
|
2457 /* get IN_CAPS first, code below might mess with the flags */ |
|
2458 in_caps = GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_IN_CAPS); |
|
2459 |
|
2460 /* stamp the buffer with previous caps if no caps set */ |
|
2461 if (!bufcaps) { |
|
2462 if (!gst_buffer_is_metadata_writable (buf)) { |
|
2463 /* metadata is not writable, copy will be made and original buffer |
|
2464 * will be unreffed so we need to ref so that we don't lose the |
|
2465 * buffer in the render method. */ |
|
2466 gst_buffer_ref (buf); |
|
2467 /* the new buffer is ours only, we keep it out of the scope of this |
|
2468 * function */ |
|
2469 buf = gst_buffer_make_metadata_writable (buf); |
|
2470 } else { |
|
2471 /* else the metadata is writable, we ref because we keep the buffer |
|
2472 * out of the scope of this method */ |
|
2473 gst_buffer_ref (buf); |
|
2474 } |
|
2475 /* buffer metadata is writable now, set the caps */ |
|
2476 gst_buffer_set_caps (buf, padcaps); |
|
2477 } else { |
|
2478 gst_caps_unref (bufcaps); |
|
2479 |
|
2480 /* since we keep this buffer out of the scope of this method */ |
|
2481 gst_buffer_ref (buf); |
|
2482 } |
|
2483 |
|
2484 GST_LOG_OBJECT (sink, "received buffer %p, in_caps: %s, offset %" |
|
2485 G_GINT64_FORMAT ", offset_end %" G_GINT64_FORMAT |
|
2486 ", timestamp %" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT, |
|
2487 buf, in_caps ? "yes" : "no", GST_BUFFER_OFFSET (buf), |
|
2488 GST_BUFFER_OFFSET_END (buf), |
|
2489 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), |
|
2490 GST_TIME_ARGS (GST_BUFFER_DURATION (buf))); |
|
2491 |
|
2492 /* if we get IN_CAPS buffers, but the previous buffer was not IN_CAPS, |
|
2493 * it means we're getting new streamheader buffers, and we should clear |
|
2494 * the old ones */ |
|
2495 if (in_caps && sink->previous_buffer_in_caps == FALSE) { |
|
2496 GST_DEBUG_OBJECT (sink, |
|
2497 "receiving new IN_CAPS buffers, clearing old streamheader"); |
|
2498 g_slist_foreach (sink->streamheader, (GFunc) gst_mini_object_unref, NULL); |
|
2499 g_slist_free (sink->streamheader); |
|
2500 sink->streamheader = NULL; |
|
2501 } |
|
2502 |
|
2503 /* save the current in_caps */ |
|
2504 sink->previous_buffer_in_caps = in_caps; |
|
2505 |
|
2506 /* if the incoming buffer is marked as IN CAPS, then we assume for now |
|
2507 * it's a streamheader that needs to be sent to each new client, so we |
|
2508 * put it on our internal list of streamheader buffers. |
|
2509 * FIXME: we could check if the buffer's contents are in fact part of the |
|
2510 * current streamheader. |
|
2511 * |
|
2512 * We don't send the buffer to the client, since streamheaders are sent |
|
2513 * separately when necessary. */ |
|
2514 if (in_caps) { |
|
2515 GST_DEBUG_OBJECT (sink, |
|
2516 "appending IN_CAPS buffer with length %d to streamheader", |
|
2517 GST_BUFFER_SIZE (buf)); |
|
2518 sink->streamheader = g_slist_append (sink->streamheader, buf); |
|
2519 } else { |
|
2520 /* queue the buffer, this is a regular data buffer. */ |
|
2521 gst_multi_fd_sink_queue_buffer (sink, buf); |
|
2522 |
|
2523 sink->bytes_to_serve += GST_BUFFER_SIZE (buf); |
|
2524 } |
|
2525 return GST_FLOW_OK; |
|
2526 |
|
2527 /* ERRORS */ |
|
2528 no_caps: |
|
2529 { |
|
2530 GST_ELEMENT_ERROR (sink, CORE, NEGOTIATION, (NULL), |
|
2531 ("Received first buffer without caps set")); |
|
2532 return GST_FLOW_NOT_NEGOTIATED; |
|
2533 } |
|
2534 } |
|
2535 |
|
2536 static void |
|
2537 gst_multi_fd_sink_set_property (GObject * object, guint prop_id, |
|
2538 const GValue * value, GParamSpec * pspec) |
|
2539 { |
|
2540 GstMultiFdSink *multifdsink; |
|
2541 |
|
2542 multifdsink = GST_MULTI_FD_SINK (object); |
|
2543 |
|
2544 switch (prop_id) { |
|
2545 case PROP_PROTOCOL: |
|
2546 multifdsink->protocol = g_value_get_enum (value); |
|
2547 break; |
|
2548 case PROP_MODE: |
|
2549 multifdsink->mode = g_value_get_enum (value); |
|
2550 break; |
|
2551 case PROP_BUFFERS_MAX: |
|
2552 multifdsink->units_max = g_value_get_int (value); |
|
2553 break; |
|
2554 case PROP_BUFFERS_SOFT_MAX: |
|
2555 multifdsink->units_soft_max = g_value_get_int (value); |
|
2556 break; |
|
2557 case PROP_TIME_MIN: |
|
2558 multifdsink->time_min = g_value_get_int64 (value); |
|
2559 break; |
|
2560 case PROP_BYTES_MIN: |
|
2561 multifdsink->bytes_min = g_value_get_int (value); |
|
2562 break; |
|
2563 case PROP_BUFFERS_MIN: |
|
2564 multifdsink->buffers_min = g_value_get_int (value); |
|
2565 break; |
|
2566 case PROP_UNIT_TYPE: |
|
2567 multifdsink->unit_type = g_value_get_enum (value); |
|
2568 break; |
|
2569 case PROP_UNITS_MAX: |
|
2570 multifdsink->units_max = g_value_get_int64 (value); |
|
2571 break; |
|
2572 case PROP_UNITS_SOFT_MAX: |
|
2573 multifdsink->units_soft_max = g_value_get_int64 (value); |
|
2574 break; |
|
2575 case PROP_RECOVER_POLICY: |
|
2576 multifdsink->recover_policy = g_value_get_enum (value); |
|
2577 break; |
|
2578 case PROP_TIMEOUT: |
|
2579 multifdsink->timeout = g_value_get_uint64 (value); |
|
2580 break; |
|
2581 case PROP_SYNC_METHOD: |
|
2582 multifdsink->def_sync_method = g_value_get_enum (value); |
|
2583 break; |
|
2584 case PROP_BURST_UNIT: |
|
2585 multifdsink->def_burst_unit = g_value_get_enum (value); |
|
2586 break; |
|
2587 case PROP_BURST_VALUE: |
|
2588 multifdsink->def_burst_value = g_value_get_uint64 (value); |
|
2589 break; |
|
2590 |
|
2591 default: |
|
2592 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
|
2593 break; |
|
2594 } |
|
2595 } |
|
2596 |
|
2597 static void |
|
2598 gst_multi_fd_sink_get_property (GObject * object, guint prop_id, GValue * value, |
|
2599 GParamSpec * pspec) |
|
2600 { |
|
2601 GstMultiFdSink *multifdsink; |
|
2602 |
|
2603 multifdsink = GST_MULTI_FD_SINK (object); |
|
2604 |
|
2605 switch (prop_id) { |
|
2606 case PROP_PROTOCOL: |
|
2607 g_value_set_enum (value, multifdsink->protocol); |
|
2608 break; |
|
2609 case PROP_MODE: |
|
2610 g_value_set_enum (value, multifdsink->mode); |
|
2611 break; |
|
2612 case PROP_BUFFERS_MAX: |
|
2613 g_value_set_int (value, multifdsink->units_max); |
|
2614 break; |
|
2615 case PROP_BUFFERS_SOFT_MAX: |
|
2616 g_value_set_int (value, multifdsink->units_soft_max); |
|
2617 break; |
|
2618 case PROP_TIME_MIN: |
|
2619 g_value_set_int64 (value, multifdsink->time_min); |
|
2620 break; |
|
2621 case PROP_BYTES_MIN: |
|
2622 g_value_set_int (value, multifdsink->bytes_min); |
|
2623 break; |
|
2624 case PROP_BUFFERS_MIN: |
|
2625 g_value_set_int (value, multifdsink->buffers_min); |
|
2626 break; |
|
2627 case PROP_BUFFERS_QUEUED: |
|
2628 g_value_set_uint (value, multifdsink->buffers_queued); |
|
2629 break; |
|
2630 case PROP_BYTES_QUEUED: |
|
2631 g_value_set_uint (value, multifdsink->bytes_queued); |
|
2632 break; |
|
2633 case PROP_TIME_QUEUED: |
|
2634 g_value_set_uint64 (value, multifdsink->time_queued); |
|
2635 break; |
|
2636 case PROP_UNIT_TYPE: |
|
2637 g_value_set_enum (value, multifdsink->unit_type); |
|
2638 break; |
|
2639 case PROP_UNITS_MAX: |
|
2640 g_value_set_int64 (value, multifdsink->units_max); |
|
2641 break; |
|
2642 case PROP_UNITS_SOFT_MAX: |
|
2643 g_value_set_int64 (value, multifdsink->units_soft_max); |
|
2644 break; |
|
2645 case PROP_RECOVER_POLICY: |
|
2646 g_value_set_enum (value, multifdsink->recover_policy); |
|
2647 break; |
|
2648 case PROP_TIMEOUT: |
|
2649 g_value_set_uint64 (value, multifdsink->timeout); |
|
2650 break; |
|
2651 case PROP_SYNC_METHOD: |
|
2652 g_value_set_enum (value, multifdsink->def_sync_method); |
|
2653 break; |
|
2654 case PROP_BYTES_TO_SERVE: |
|
2655 g_value_set_uint64 (value, multifdsink->bytes_to_serve); |
|
2656 break; |
|
2657 case PROP_BYTES_SERVED: |
|
2658 g_value_set_uint64 (value, multifdsink->bytes_served); |
|
2659 break; |
|
2660 case PROP_BURST_UNIT: |
|
2661 g_value_set_enum (value, multifdsink->def_burst_unit); |
|
2662 break; |
|
2663 case PROP_BURST_VALUE: |
|
2664 g_value_set_uint64 (value, multifdsink->def_burst_value); |
|
2665 break; |
|
2666 |
|
2667 default: |
|
2668 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
|
2669 break; |
|
2670 } |
|
2671 } |
|
2672 |
|
2673 |
|
2674 /* create a socket for sending to remote machine */ |
|
2675 static gboolean |
|
2676 gst_multi_fd_sink_start (GstBaseSink * bsink) |
|
2677 { |
|
2678 GstMultiFdSinkClass *fclass; |
|
2679 GstMultiFdSink *this; |
|
2680 |
|
2681 if (GST_OBJECT_FLAG_IS_SET (bsink, GST_MULTI_FD_SINK_OPEN)) |
|
2682 return TRUE; |
|
2683 |
|
2684 this = GST_MULTI_FD_SINK (bsink); |
|
2685 fclass = GST_MULTI_FD_SINK_GET_CLASS (this); |
|
2686 |
|
2687 GST_INFO_OBJECT (this, "starting in mode %d", this->mode); |
|
2688 if ((this->fdset = gst_poll_new (TRUE)) == NULL) |
|
2689 goto socket_pair; |
|
2690 |
|
2691 this->streamheader = NULL; |
|
2692 this->bytes_to_serve = 0; |
|
2693 this->bytes_served = 0; |
|
2694 |
|
2695 if (fclass->init) { |
|
2696 fclass->init (this); |
|
2697 } |
|
2698 |
|
2699 this->running = TRUE; |
|
2700 this->thread = g_thread_create ((GThreadFunc) gst_multi_fd_sink_thread, |
|
2701 this, TRUE, NULL); |
|
2702 |
|
2703 GST_OBJECT_FLAG_SET (this, GST_MULTI_FD_SINK_OPEN); |
|
2704 |
|
2705 return TRUE; |
|
2706 |
|
2707 /* ERRORS */ |
|
2708 socket_pair: |
|
2709 { |
|
2710 GST_ELEMENT_ERROR (this, RESOURCE, OPEN_READ_WRITE, (NULL), |
|
2711 GST_ERROR_SYSTEM); |
|
2712 return FALSE; |
|
2713 } |
|
2714 } |
|
2715 |
|
2716 static gboolean |
|
2717 multifdsink_hash_remove (gpointer key, gpointer value, gpointer data) |
|
2718 { |
|
2719 return TRUE; |
|
2720 } |
|
2721 |
|
2722 static gboolean |
|
2723 gst_multi_fd_sink_stop (GstBaseSink * bsink) |
|
2724 { |
|
2725 GstMultiFdSinkClass *fclass; |
|
2726 GstMultiFdSink *this; |
|
2727 GstBuffer *buf; |
|
2728 int i; |
|
2729 |
|
2730 this = GST_MULTI_FD_SINK (bsink); |
|
2731 fclass = GST_MULTI_FD_SINK_GET_CLASS (this); |
|
2732 |
|
2733 if (!GST_OBJECT_FLAG_IS_SET (bsink, GST_MULTI_FD_SINK_OPEN)) |
|
2734 return TRUE; |
|
2735 |
|
2736 this->running = FALSE; |
|
2737 |
|
2738 gst_poll_set_flushing (this->fdset, TRUE); |
|
2739 if (this->thread) { |
|
2740 GST_DEBUG_OBJECT (this, "joining thread"); |
|
2741 g_thread_join (this->thread); |
|
2742 GST_DEBUG_OBJECT (this, "joined thread"); |
|
2743 this->thread = NULL; |
|
2744 } |
|
2745 |
|
2746 /* free the clients */ |
|
2747 gst_multi_fd_sink_clear (this); |
|
2748 |
|
2749 if (this->streamheader) { |
|
2750 g_slist_foreach (this->streamheader, (GFunc) gst_mini_object_unref, NULL); |
|
2751 g_slist_free (this->streamheader); |
|
2752 this->streamheader = NULL; |
|
2753 } |
|
2754 |
|
2755 if (fclass->close) |
|
2756 fclass->close (this); |
|
2757 |
|
2758 if (this->fdset) { |
|
2759 gst_poll_free (this->fdset); |
|
2760 this->fdset = NULL; |
|
2761 } |
|
2762 g_hash_table_foreach_remove (this->fd_hash, multifdsink_hash_remove, this); |
|
2763 |
|
2764 /* remove all queued buffers */ |
|
2765 if (this->bufqueue) { |
|
2766 GST_DEBUG_OBJECT (this, "Emptying bufqueue with %d buffers", |
|
2767 this->bufqueue->len); |
|
2768 for (i = this->bufqueue->len - 1; i >= 0; --i) { |
|
2769 buf = g_array_index (this->bufqueue, GstBuffer *, i); |
|
2770 GST_LOG_OBJECT (this, "Removing buffer %p (%d) with refcount %d", buf, i, |
|
2771 GST_MINI_OBJECT_REFCOUNT (buf)); |
|
2772 gst_buffer_unref (buf); |
|
2773 this->bufqueue = g_array_remove_index (this->bufqueue, i); |
|
2774 } |
|
2775 /* freeing the array is done in _finalize */ |
|
2776 } |
|
2777 GST_OBJECT_FLAG_UNSET (this, GST_MULTI_FD_SINK_OPEN); |
|
2778 |
|
2779 return TRUE; |
|
2780 } |
|
2781 |
|
2782 static GstStateChangeReturn |
|
2783 gst_multi_fd_sink_change_state (GstElement * element, GstStateChange transition) |
|
2784 { |
|
2785 GstMultiFdSink *sink; |
|
2786 GstStateChangeReturn ret; |
|
2787 |
|
2788 sink = GST_MULTI_FD_SINK (element); |
|
2789 |
|
2790 /* we disallow changing the state from the streaming thread */ |
|
2791 if (g_thread_self () == sink->thread) |
|
2792 return GST_STATE_CHANGE_FAILURE; |
|
2793 |
|
2794 |
|
2795 switch (transition) { |
|
2796 case GST_STATE_CHANGE_NULL_TO_READY: |
|
2797 if (!gst_multi_fd_sink_start (GST_BASE_SINK (sink))) |
|
2798 goto start_failed; |
|
2799 break; |
|
2800 case GST_STATE_CHANGE_READY_TO_PAUSED: |
|
2801 break; |
|
2802 case GST_STATE_CHANGE_PAUSED_TO_PLAYING: |
|
2803 break; |
|
2804 default: |
|
2805 break; |
|
2806 } |
|
2807 |
|
2808 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); |
|
2809 |
|
2810 switch (transition) { |
|
2811 case GST_STATE_CHANGE_PLAYING_TO_PAUSED: |
|
2812 break; |
|
2813 case GST_STATE_CHANGE_PAUSED_TO_READY: |
|
2814 break; |
|
2815 case GST_STATE_CHANGE_READY_TO_NULL: |
|
2816 gst_multi_fd_sink_stop (GST_BASE_SINK (sink)); |
|
2817 break; |
|
2818 default: |
|
2819 break; |
|
2820 } |
|
2821 return ret; |
|
2822 |
|
2823 /* ERRORS */ |
|
2824 start_failed: |
|
2825 { |
|
2826 /* error message was posted */ |
|
2827 return GST_STATE_CHANGE_FAILURE; |
|
2828 } |
|
2829 } |