1 /*
2 * GPAC - Multimedia Framework C SDK
3 *
4 * Authors: Jean Le Feuvre
5 * Copyright (c) Telecom ParisTech 2019
6 * All rights reserved
7 *
8 * This file is part of GPAC / generic socket output filter
9 *
10 * GPAC is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2, or (at your option)
13 * any later version.
14 *
15 * GPAC is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; see the file COPYING. If not, write to
22 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23 *
24 */
25
26
27 #include <gpac/filters.h>
28 #include <gpac/constants.h>
29 #include <gpac/network.h>
30
31 typedef struct
32 {
33 GF_Socket *socket;
34 Bool is_tuned;
35 char address[GF_MAX_IP_NAME_LEN];
36 Bool pck_pending;
37 } GF_SockOutClient;
38
39 typedef struct
40 {
41 //options
42 Double start, speed;
43 char *dst, *mime, *ext, *ifce;
44 Bool listen;
45 u32 maxc, port, sockbuf, ka, kp, rate;
46 GF_Fraction pckr, pckd;
47
48 GF_Socket *socket;
49 //only one output pid
50 GF_FilterPid *pid;
51
52 GF_List *clients;
53
54 Bool pid_started;
55 Bool had_clients;
56 Bool pck_pending;
57
58 GF_FilterCapability in_caps[2];
59 char szExt[10];
60 char szFileName[GF_MAX_PATH];
61
62 u32 nb_pck_processed;
63 u64 start_time;
64 u64 nb_bytes_sent;
65
66 GF_FilterPacket *rev_pck;
67 u32 next_pckd_idx, next_pckr_idx;
68 u32 nb_pckd_wnd, nb_pckr_wnd;
69 } GF_SockOutCtx;
70
71
sockout_configure_pid(GF_Filter * filter,GF_FilterPid * pid,Bool is_remove)72 static GF_Err sockout_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
73 {
74 const GF_PropertyValue *p;
75 GF_SockOutCtx *ctx = (GF_SockOutCtx *) gf_filter_get_udta(filter);
76 if (is_remove) {
77 ctx->pid = NULL;
78 gf_sk_del(ctx->socket);
79 ctx->socket = NULL;
80 return GF_OK;
81 }
82 gf_filter_pid_check_caps(pid);
83
84 if (!ctx->pid && (!ctx->listen || gf_list_count(ctx->clients) ) ) {
85 GF_FilterEvent evt;
86 gf_filter_pid_init_play_event(pid, &evt, ctx->start, ctx->speed, "SockOut");
87 gf_filter_pid_send_event(pid, &evt);
88 ctx->pid_started = GF_TRUE;
89 }
90 ctx->pid = pid;
91
92 p = gf_filter_pid_get_property(pid, GF_PROP_PID_DISABLE_PROGRESSIVE);
93 if (p && p->value.uint) {
94 GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[SockOut] Block patching is not supported by socket output\n"));
95 return GF_NOT_SUPPORTED;
96 }
97 return GF_OK;
98 }
99
sockout_initialize(GF_Filter * filter)100 static GF_Err sockout_initialize(GF_Filter *filter)
101 {
102 GF_Err e;
103 char *str, *url;
104 u16 port;
105 char *ext;
106 u32 sock_type = 0;
107 GF_SockOutCtx *ctx = (GF_SockOutCtx *) gf_filter_get_udta(filter);
108
109 if (!ctx || !ctx->dst) return GF_OK;
110 e = GF_NOT_SUPPORTED;
111 if (!strncmp(ctx->dst, "tcp://", 6)) e = GF_OK;
112 else if (!strncmp(ctx->dst, "udp://", 6)) e = GF_OK;
113 else if (!strncmp(ctx->dst, "tcpu://", 7)) e = GF_OK;
114 else if (!strncmp(ctx->dst, "udpu://", 7)) e = GF_OK;
115
116
117 if (e) {
118 gf_filter_setup_failure(filter, GF_NOT_SUPPORTED);
119 return GF_NOT_SUPPORTED;
120 }
121 if (ctx->ext) ext = ctx->ext;
122 else {
123 ext = gf_file_ext_start(ctx->dst);
124 if (ext) ext++;
125 }
126
127 if (!ext && !ctx->mime) {
128 GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[SockOut] No extension provided nor mime type for output file %s, cannot infer format\n", ctx->dst));
129 return GF_NOT_SUPPORTED;
130 }
131
132 if (ctx->listen) {
133 ctx->clients = gf_list_new();
134 if (!ctx->clients) return GF_OUT_OF_MEM;
135 }
136
137 //static cap, streamtype = file
138 ctx->in_caps[0].code = GF_PROP_PID_STREAM_TYPE;
139 ctx->in_caps[0].val = PROP_UINT(GF_STREAM_FILE);
140 ctx->in_caps[0].flags = GF_CAPS_INPUT_STATIC;
141
142 if (ctx->mime) {
143 ctx->in_caps[1].code = GF_PROP_PID_MIME;
144 ctx->in_caps[1].val = PROP_NAME( ctx->mime );
145 ctx->in_caps[1].flags = GF_CAPS_INPUT;
146 } else {
147 strncpy(ctx->szExt, ext, 9);
148 ctx->szExt[9] = 0;
149 strlwr(ctx->szExt);
150 ctx->in_caps[1].code = GF_PROP_PID_FILE_EXT;
151 ctx->in_caps[1].val = PROP_NAME( ctx->szExt );
152 ctx->in_caps[1].flags = GF_CAPS_INPUT;
153 }
154 gf_filter_override_caps(filter, ctx->in_caps, 2);
155
156 /*create our ourput socket*/
157
158 if (!strnicmp(ctx->dst, "udp://", 6)) {
159 sock_type = GF_SOCK_TYPE_UDP;
160 ctx->listen = GF_FALSE;
161 } else if (!strnicmp(ctx->dst, "tcp://", 6)) {
162 sock_type = GF_SOCK_TYPE_TCP;
163 #ifdef GPAC_HAS_SOCK_UN
164 } else if (!strnicmp(ctx->dst, "udpu://", 7)) {
165 sock_type = GF_SOCK_TYPE_UDP_UN;
166 ctx->listen = GF_FALSE;
167 } else if (!strnicmp(ctx->dst, "tcpu://", 7)) {
168 sock_type = GF_SOCK_TYPE_TCP_UN;
169 #endif
170 } else {
171 return GF_NOT_SUPPORTED;
172 }
173
174 //skip ://
175 url = strchr(ctx->dst, ':');
176 url += 3;
177
178 ctx->socket = gf_sk_new(sock_type);
179 if (! ctx->socket ) {
180 GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[SockOut] Failed to open socket for %s\n", ctx->dst));
181 return GF_IO_ERR;
182 }
183
184 /*setup port and src*/
185 port = ctx->port;
186 str = strrchr(url, ':');
187 /*take care of IPv6 address*/
188 if (str && strchr(str, ']')) str = strchr(url, ':');
189 if (str) {
190 port = atoi(str+1);
191 str[0] = 0;
192 }
193
194 if (gf_sk_is_multicast_address(url)) {
195 e = gf_sk_setup_multicast(ctx->socket, url, port, 0, 0, ctx->ifce);
196 ctx->listen = GF_FALSE;
197 } else if ((sock_type == GF_SOCK_TYPE_UDP)
198 #ifdef GPAC_HAS_SOCK_UN
199 || (sock_type == GF_SOCK_TYPE_UDP_UN)
200 #endif
201 ) {
202 e = gf_sk_bind(ctx->socket, ctx->ifce, port, url, port, GF_SOCK_REUSE_PORT | GF_SOCK_FAKE_BIND);
203 ctx->listen = GF_FALSE;
204 } else if (ctx->listen) {
205 e = gf_sk_bind(ctx->socket, NULL, port, url, 0, GF_SOCK_REUSE_PORT);
206 if (!e) e = gf_sk_listen(ctx->socket, ctx->maxc);
207 if (!e) {
208 gf_filter_post_process_task(filter);
209 gf_sk_server_mode(ctx->socket, GF_TRUE);
210 }
211 } else {
212 e = gf_sk_connect(ctx->socket, url, port, ctx->ifce);
213 }
214
215 if (str) str[0] = ':';
216
217 if (e) {
218 gf_sk_del(ctx->socket);
219 ctx->socket = NULL;
220 return e;
221 }
222
223 gf_sk_set_buffer_size(ctx->socket, 0, ctx->sockbuf);
224
225 return GF_OK;
226 }
227
sockout_finalize(GF_Filter * filter)228 static void sockout_finalize(GF_Filter *filter)
229 {
230 GF_SockOutCtx *ctx = (GF_SockOutCtx *) gf_filter_get_udta(filter);
231 if (ctx->clients) {
232 while (gf_list_count(ctx->clients)) {
233 GF_SockOutClient *sc = gf_list_pop_back(ctx->clients);
234 if (sc->socket) gf_sk_del(sc->socket);
235 gf_free(sc);
236 }
237 gf_list_del(ctx->clients);
238 }
239
240 if (ctx->socket) gf_sk_del(ctx->socket);
241 }
242
sockout_send_packet(GF_SockOutCtx * ctx,GF_FilterPacket * pck,GF_Socket * dst_sock)243 static GF_Err sockout_send_packet(GF_SockOutCtx *ctx, GF_FilterPacket *pck, GF_Socket *dst_sock)
244 {
245 GF_Err e;
246 const GF_PropertyValue *p;
247 u32 w, h, stride, stride_uv, pf;
248 u32 nb_planes, uv_height;
249 const char *pck_data;
250 u32 pck_size;
251 GF_FilterFrameInterface *hwf=NULL;
252 if (!dst_sock) return GF_OK;
253
254 pck_data = gf_filter_pck_get_data(pck, &pck_size);
255 if (pck_data) {
256 e = gf_sk_send(dst_sock, pck_data, pck_size);
257 if (e) {
258 GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[SockOut] Write error: %s\n", gf_error_to_string(e) ));
259 }
260 ctx->nb_bytes_sent += pck_size;
261 return e;
262 }
263 hwf = gf_filter_pck_get_frame_interface(pck);
264 if (!hwf) {
265 GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[SockOut] output file handle is not opened, discarding %d bytes\n", pck_size));
266 return GF_OK;
267 }
268
269 p = gf_filter_pid_get_property(ctx->pid, GF_PROP_PID_WIDTH);
270 w = p ? p->value.uint : 0;
271 p = gf_filter_pid_get_property(ctx->pid, GF_PROP_PID_HEIGHT);
272 h = p ? p->value.uint : 0;
273 p = gf_filter_pid_get_property(ctx->pid, GF_PROP_PID_PIXFMT);
274 pf = p ? p->value.uint : 0;
275
276 //get stride/stride_uv with no padding
277 stride = stride_uv = 0;
278 if (gf_pixel_get_size_info(pf, w, h, NULL, &stride, &stride_uv, &nb_planes, &uv_height) == GF_TRUE) {
279 u32 i;
280 for (i=0; i<nb_planes; i++) {
281 u32 j, write_h, lsize;
282 const u8 *out_ptr;
283 u32 out_stride = i ? stride_uv : stride;
284 e = hwf->get_plane(hwf, i, &out_ptr, &out_stride);
285 if (e) {
286 GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[SockOut] Failed to fetch plane data from hardware frame, cannot write\n"));
287 break;
288 }
289 if (i) {
290 write_h = uv_height;
291 lsize = stride_uv;
292 } else {
293 write_h = h;
294 lsize = stride;
295 }
296 for (j=0; j<write_h; j++) {
297 e = gf_sk_send(dst_sock, out_ptr, lsize);
298 if (e) {
299 GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[SockOut] Write error: %s\n", gf_error_to_string(e) ));
300 }
301 out_ptr += out_stride;
302 ctx->nb_bytes_sent += lsize;
303 }
304 }
305 }
306 return GF_OK;
307 }
308
309
sockout_process(GF_Filter * filter)310 static GF_Err sockout_process(GF_Filter *filter)
311 {
312 GF_Err e;
313 Bool is_pck_ref = GF_FALSE;
314
315 GF_FilterPacket *pck;
316 GF_SockOutCtx *ctx = (GF_SockOutCtx *) gf_filter_get_udta(filter);
317
318 if (!ctx->socket)
319 return GF_EOS;
320
321 if (ctx->rate) {
322 if (!ctx->start_time) ctx->start_time = gf_sys_clock_high_res();
323 else {
324 u64 now = gf_sys_clock_high_res() - ctx->start_time;
325 if (ctx->nb_bytes_sent*8*1000000 > ctx->rate * now) {
326 u64 diff = ctx->nb_bytes_sent*8*1000000 / ctx->rate - now;
327 gf_filter_ask_rt_reschedule(filter, (u32) MAX(diff, 1000) );
328 return GF_OK;
329 } else {
330 fprintf(stderr, "[SockOut] Sending at "LLU" kbps \r", ctx->nb_bytes_sent*8*1000/now);
331 }
332 }
333 }
334
335 if (ctx->listen) {
336 GF_Socket *new_conn=NULL;
337 e = gf_sk_accept(ctx->socket, &new_conn);
338 if ((e==GF_OK) && new_conn) {
339 GF_SockOutClient *sc;
340 GF_SAFEALLOC(sc, GF_SockOutClient);
341 if (!sc) return GF_OUT_OF_MEM;
342
343 sc->socket = new_conn;
344 strcpy(sc->address, "unknown");
345 gf_sk_get_remote_address(new_conn, sc->address);
346
347 GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[SockOut] Accepting new connection from %s\n", sc->address));
348 gf_list_add(ctx->clients, sc);
349 ctx->had_clients = GF_TRUE;
350 if (!ctx->pid_started && ctx->pid) {
351 GF_FilterEvent evt;
352 gf_filter_pid_init_play_event(ctx->pid, &evt, ctx->start, ctx->speed, "SockOut");
353 gf_filter_pid_send_event(ctx->pid, &evt);
354 ctx->pid_started = GF_TRUE;
355 }
356 sc->pck_pending = ctx->pck_pending;
357 if (!ctx->nb_pck_processed)
358 sc->is_tuned = GF_TRUE;
359 }
360 }
361 if (!ctx->pid) {
362 if (ctx->listen) gf_filter_post_process_task(filter);
363 return GF_OK;
364 }
365
366 pck = gf_filter_pid_get_packet(ctx->pid);
367 if (!pck) {
368 if (gf_filter_pid_is_eos(ctx->pid)) {
369 if (ctx->rev_pck) {
370 is_pck_ref = GF_TRUE;
371 pck = ctx->rev_pck;
372 } else {
373 if (!ctx->listen) {
374 gf_sk_del(ctx->socket);
375 ctx->socket = NULL;
376 return GF_EOS;
377 }
378 if (!ctx->ka)
379 return GF_EOS;
380 //keep alive, ask for real-time reschedule of 100 ms - we should use socket groups and selects !
381 gf_filter_ask_rt_reschedule(filter, 100000);
382 }
383 }
384 if (!pck)
385 return GF_OK;
386 }
387
388 if (ctx->pckd.den && !ctx->rev_pck) {
389 u32 pck_idx = ctx->pckd.num;
390 u32 nb_pck = ctx->nb_pckd_wnd * ctx->pckd.den;
391
392 if (!pck_idx) {
393 if (!ctx->next_pckd_idx) ctx->next_pckd_idx = gf_rand() % ctx->pckd.den;
394 pck_idx = ctx->next_pckd_idx;
395 }
396 nb_pck += pck_idx;
397
398 if (nb_pck == 1+ctx->nb_pck_processed) {
399 ctx->nb_pck_processed++;
400 GF_LOG(GF_LOG_WARNING, GF_LOG_NETWORK, ("[SockOut] Droping packet %d per user request\r", ctx->nb_pck_processed));
401 gf_filter_pid_drop_packet(ctx->pid);
402 ctx->next_pckd_idx = 0;
403 ctx->nb_pckd_wnd ++;
404 return GF_OK;
405 }
406 }
407 if (ctx->pckr.den && !ctx->rev_pck) {
408 u32 pck_idx = ctx->pckr.num;
409 u32 nb_pck = ctx->nb_pckr_wnd * ctx->pckr.den;
410
411 if (!pck_idx) {
412 if (!ctx->next_pckr_idx) ctx->next_pckr_idx = gf_rand() % ctx->pckr.den;
413 pck_idx = ctx->next_pckr_idx;
414 }
415 nb_pck += pck_idx;
416
417 if (nb_pck == 1+ctx->nb_pck_processed) {
418 ctx->rev_pck = pck;
419 gf_filter_pck_ref(&ctx->rev_pck);
420 GF_LOG(GF_LOG_WARNING, GF_LOG_NETWORK, ("[SockOut] Reverting packet %d per user request\r", ctx->nb_pck_processed));
421 ctx->nb_pck_processed++;
422 gf_filter_pid_drop_packet(ctx->pid);
423 pck = gf_filter_pid_get_packet(ctx->pid);
424 if (!pck) return GF_OK;
425 }
426 }
427
428 if (ctx->listen) {
429 Bool had_pck_pending = ctx->pck_pending;
430 u32 i, nb_clients = gf_list_count(ctx->clients);
431 u8 dep_flags = gf_filter_pck_get_dependency_flags(pck);
432 if ((dep_flags & 0x3) == 1) {
433 }
434 ctx->pck_pending = GF_FALSE;
435
436 if (!nb_clients) {
437 //client disconnected, drop packet if needed
438 if (ctx->had_clients && !ctx->kp) {
439 if (is_pck_ref) {
440 gf_filter_pck_unref(pck);
441 ctx->rev_pck = NULL;
442 } else {
443 gf_filter_pid_drop_packet(ctx->pid);
444 }
445 }
446 return GF_OK;
447 }
448 for (i=0; i<nb_clients; i++) {
449 GF_SockOutClient *sc = gf_list_get(ctx->clients, i);
450 if (!sc->is_tuned) {
451
452 }
453 if (had_pck_pending && !sc->pck_pending) {
454 continue;
455 }
456 sc->pck_pending = GF_FALSE;
457
458 e = sockout_send_packet(ctx, pck, sc->socket);
459 if (e == GF_BUFFER_TOO_SMALL) {
460 sc->pck_pending = GF_TRUE;
461 ctx->pck_pending = GF_TRUE;
462 }
463 }
464 if (ctx->pck_pending) return GF_OK;
465
466 } else {
467 e = sockout_send_packet(ctx, pck, ctx->socket);
468 if (e == GF_BUFFER_TOO_SMALL) return GF_OK;
469 }
470
471 ctx->nb_pck_processed++;
472
473 if (is_pck_ref) {
474 gf_filter_pck_unref(pck);
475 ctx->rev_pck = NULL;
476 } else {
477 gf_filter_pid_drop_packet(ctx->pid);
478 if (ctx->rev_pck) {
479 e = sockout_send_packet(ctx, ctx->rev_pck, ctx->socket);
480 if (e == GF_BUFFER_TOO_SMALL) return GF_OK;
481 gf_filter_pck_unref(ctx->rev_pck);
482 ctx->rev_pck = NULL;
483 ctx->next_pckr_idx=0;
484 ctx->nb_pckr_wnd++;
485 }
486 }
487 return GF_OK;
488 }
489
sockout_probe_url(const char * url,const char * mime)490 static GF_FilterProbeScore sockout_probe_url(const char *url, const char *mime)
491 {
492 if (!strnicmp(url, "tcp://", 6)) return GF_FPROBE_SUPPORTED;
493 if (!strnicmp(url, "udp://", 6)) return GF_FPROBE_SUPPORTED;
494 #ifdef GPAC_HAS_SOCK_UN
495 if (!strnicmp(url, "tcpu://", 7)) return GF_FPROBE_SUPPORTED;
496 if (!strnicmp(url, "udpu://", 7)) return GF_FPROBE_SUPPORTED;
497 #endif
498 return GF_FPROBE_NOT_SUPPORTED;
499 }
500
501
502 #define OFFS(_n) #_n, offsetof(GF_SockOutCtx, _n)
503
504 static const GF_FilterArgs SockOutArgs[] =
505 {
506 { OFFS(dst), "location of destination file", GF_PROP_NAME, NULL, NULL, 0},
507 { OFFS(sockbuf), "block size used to read file", GF_PROP_UINT, "65536", NULL, GF_FS_ARG_HINT_ADVANCED},
508 { OFFS(port), "default port if not specified", GF_PROP_UINT, "1234", NULL, 0},
509 { OFFS(ifce), "default multicast interface", GF_PROP_NAME, NULL, NULL, GF_FS_ARG_HINT_ADVANCED},
510 { OFFS(ext), "file extension of pipe data - see filter help", GF_PROP_STRING, NULL, NULL, 0},
511 { OFFS(mime), "mime type of pipe data - see filter help", GF_PROP_STRING, NULL, NULL, 0},
512 { OFFS(listen), "indicate the output socket works in server mode", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
513 { OFFS(maxc), "max number of concurrent connections", GF_PROP_UINT, "+I", NULL, GF_FS_ARG_HINT_ADVANCED},
514 { OFFS(ka), "keep socket alive if no more connections", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
515 { OFFS(kp), "keep packets in queue if no more clients", GF_PROP_BOOL, "true", NULL, GF_FS_ARG_HINT_ADVANCED},
516 { OFFS(start), "set playback start offset. Negative value means percent of media dur with -1 <=> dur", GF_PROP_DOUBLE, "0.0", NULL, 0},
517 { OFFS(speed), "set playback speed. If speed is negative and start is 0, start is set to -1", GF_PROP_DOUBLE, "1.0", NULL, 0},
518 { OFFS(rate), "set send rate in bps, disabled by default (as fast as possible)", GF_PROP_UINT, "0", NULL, GF_FS_ARG_HINT_ADVANCED},
519 { OFFS(pckr), "reverse packet every N - see filter help", GF_PROP_FRACTION, "0/0", NULL, GF_FS_ARG_HINT_EXPERT},
520 { OFFS(pckd), "drop packet every N - see filter help", GF_PROP_FRACTION, "0/0", NULL, GF_FS_ARG_HINT_EXPERT},
521 {0}
522 };
523
524 static const GF_FilterCapability SockOutCaps[] =
525 {
526 CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
527 CAP_STRING(GF_CAPS_INPUT,GF_PROP_PID_FILE_EXT, "*"),
528 CAP_STRING(GF_CAPS_INPUT,GF_PROP_PID_MIME, "*"),
529 };
530
531
532 GF_FilterRegister SockOutRegister = {
533 .name = "sockout",
534 GF_FS_SET_DESCRIPTION("UDP/TCP output")
535 #ifndef GPAC_DISABLE_DOC
536 .help = "This filter handles generic output sockets (mono-directional) in blocking mode only.\n"
537 "The filter can work in server mode, waiting for source connections, or or in client mode, directly connecting.\n"
538 "In server mode, the filter can be instructed to keep running at the end of the stream.\n"
539 "In server mode, the default behaviour is to keep input packets when no more clients are connected; "
540 "this can be adjusted though the [-kp]() option, however there is no realtime regulation of how fast packets are droped.\n"
541 "If your sources are not real time, consider adding a real-time scheduler in the chain (cf reframer filter), or set the send [-rate]() option.\n"
542 "\n"
543 "- UDP sockets are used for destinations URLs formatted as `udp://NAME`\n"
544 "- TCP sockets are used for destinations URLs formatted as `tcp://NAME`\n"
545 #ifdef GPAC_HAS_SOCK_UN
546 "- UDP unix domain sockets are used for destinations URLs formatted as `udpu://NAME`\n"
547 "- TCP unix domain sockets are used for destinations URLs formatted as `tcpu://NAME`\n"
548 #else
549 "Your platform does not supports unix domain sockets"
550 #endif
551 "\n"
552 "The socket output can be configured to drop or revert packet order for test purposes.\n"
553 "For both mode, a window size in packets is specified as the drop/revert fraction denominator, and the index of the packet to drop/revert is given as the numerator/\n"
554 "If the numerator is 0, a packet is randomly chosen in that window.\n"
555 "EX :pckd=4/10\n"\
556 "This drops every 4th packet of each 10 packet window.\n"
557 "EX :pckr=0/100\n"\
558 "This reverts the send order of one random packet in each 100 packet window.\n"
559 "\n",
560 #endif //GPAC_DISABLE_DOC
561 .private_size = sizeof(GF_SockOutCtx),
562 .args = SockOutArgs,
563 SETCAPS(SockOutCaps),
564 .probe_url = sockout_probe_url,
565 .initialize = sockout_initialize,
566 .finalize = sockout_finalize,
567 .configure_pid = sockout_configure_pid,
568 .process = sockout_process
569 };
570
571
sockout_register(GF_FilterSession * session)572 const GF_FilterRegister *sockout_register(GF_FilterSession *session)
573 {
574 return &SockOutRegister;
575 }
576
577