Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2018-2021
6 : * All rights reserved
7 : *
8 : * This file is part of GPAC / pipe input 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 : #ifdef WIN32
32 :
33 : #include <windows.h>
34 :
35 : #else
36 :
37 : #include <fcntl.h>
38 : #include <unistd.h>
39 :
40 : #ifdef GPAC_CONFIG_LINUX
41 : #include <sys/types.h>
42 : #include <sys/stat.h>
43 : #endif
44 :
45 : #ifndef __BEOS__
46 : #include <errno.h>
47 : #endif
48 :
49 : #endif
50 :
51 :
52 : typedef struct
53 : {
54 : //options
55 : char *src;
56 : char *ext;
57 : char *mime;
58 : u32 block_size;
59 : Bool blk, ka, mkp;
60 :
61 : //only one output pid declared
62 : GF_FilterPid *pid;
63 :
64 : #ifdef WIN32
65 : HANDLE pipe;
66 : HANDLE event;
67 : OVERLAPPED overlap;
68 : #else
69 : int fd;
70 : #endif
71 : u64 bytes_read;
72 :
73 : Bool is_end, pck_out, is_first, owns_pipe;
74 : Bool do_reconfigure;
75 : char *buffer;
76 : Bool is_stdin;
77 : } GF_PipeInCtx;
78 :
79 : static Bool pipein_process_event(GF_Filter *filter, const GF_FilterEvent *evt);
80 :
81 : #ifdef WIN32
82 : #include <io.h>
83 : #include <fcntl.h>
84 : #endif //WIN32
85 :
86 10 : static GF_Err pipein_initialize(GF_Filter *filter)
87 : {
88 : GF_Err e = GF_OK;
89 10 : GF_PipeInCtx *ctx = (GF_PipeInCtx *) gf_filter_get_udta(filter);
90 : char *frag_par = NULL;
91 : char *cgi_par = NULL;
92 : char *src;
93 :
94 10 : if (!ctx->src) return GF_BAD_PARAM;
95 :
96 : #ifdef WIN32
97 : ctx->pipe = INVALID_HANDLE_VALUE;
98 : #else
99 10 : ctx->fd = -1;
100 : #endif
101 :
102 10 : if (!strcmp(ctx->src, "-") || !strcmp(ctx->src, "stdin")) {
103 2 : ctx->is_stdin = GF_TRUE;
104 2 : ctx->mkp = GF_FALSE;
105 : #ifdef WIN32
106 : _setmode(_fileno(stdin), _O_BINARY);
107 : #endif
108 : }
109 8 : else if (strnicmp(ctx->src, "pipe:/", 6) && strstr(ctx->src, "://")) {
110 0 : gf_filter_setup_failure(filter, GF_NOT_SUPPORTED);
111 0 : return GF_NOT_SUPPORTED;
112 : }
113 :
114 10 : if (ctx->mkp) ctx->blk = GF_TRUE;
115 :
116 : //strip any fragment identifer
117 10 : frag_par = strchr(ctx->src, '#');
118 10 : if (frag_par) frag_par[0] = 0;
119 10 : cgi_par = strchr(ctx->src, '?');
120 10 : if (cgi_par) cgi_par[0] = 0;
121 :
122 10 : src = (char *) ctx->src;
123 10 : if (!strnicmp(ctx->src, "pipe://", 7)) src += 7;
124 2 : else if (!strnicmp(ctx->src, "pipe:", 5)) src += 5;
125 :
126 10 : if (ctx->is_stdin) {
127 : e = GF_OK;
128 : goto setup_done;
129 : }
130 :
131 : #ifdef WIN32
132 : char szNamedPipe[GF_MAX_PATH];
133 : if (!strncmp(src, "\\\\", 2)) {
134 : strcpy(szNamedPipe, src);
135 : }
136 : else {
137 : strcpy(szNamedPipe, "\\\\.\\pipe\\gpac\\");
138 : strcat(szNamedPipe, src);
139 : }
140 : if (strchr(szNamedPipe, '/')) {
141 : u32 i, len = (u32)strlen(szNamedPipe);
142 : for (i = 0; i < len; i++) {
143 : if (szNamedPipe[i] == '/')
144 : szNamedPipe[i] = '\\';
145 : }
146 : }
147 :
148 : if (WaitNamedPipeA(szNamedPipe, 1) == FALSE) {
149 : if (!ctx->mkp) {
150 : GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[PipeIn] Failed to open %s: %d\n", szNamedPipe, GetLastError()));
151 : e = GF_URL_ERROR;
152 : goto err_exit;
153 : }
154 : DWORD pflags = PIPE_ACCESS_INBOUND;
155 : DWORD flags = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE;
156 : if (ctx->blk) flags |= PIPE_WAIT;
157 : else {
158 : flags |= PIPE_NOWAIT;
159 : pflags |= FILE_FLAG_OVERLAPPED;
160 : if (!ctx->event) ctx->event = CreateEvent(NULL, TRUE, FALSE, NULL);
161 : if (!ctx->event) {
162 : e = GF_IO_ERR;
163 : goto err_exit;
164 : }
165 : ctx->overlap.hEvent = ctx->event;
166 : }
167 : ctx->pipe = CreateNamedPipe(szNamedPipe, pflags, flags, 10, ctx->block_size, ctx->block_size, 0, NULL);
168 :
169 : if (ctx->pipe == INVALID_HANDLE_VALUE) {
170 : GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[PipeIn] Failed to create named pipe %s: %d\n", szNamedPipe, GetLastError()));
171 : e = GF_URL_ERROR;
172 : goto err_exit;
173 : }
174 : if (ctx->blk) {
175 : GF_LOG(GF_LOG_WARNING, GF_LOG_MMIO, ("[PipeIn] Waiting for client connection for %s, blocking\n", szNamedPipe));
176 : }
177 : if (!ConnectNamedPipe(ctx->pipe, ctx->blk ? NULL : &ctx->overlap) && (GetLastError() != ERROR_PIPE_CONNECTED) && (GetLastError() != ERROR_PIPE_LISTENING)) {
178 : GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[PipeOut] Failed to connect named pipe %s: %d\n", szNamedPipe, GetLastError()));
179 : e = GF_IO_ERR;
180 : CloseHandle(ctx->pipe);
181 : ctx->pipe = INVALID_HANDLE_VALUE;
182 : }
183 : else {
184 : ctx->owns_pipe = GF_TRUE;
185 : }
186 : }
187 : else {
188 : ctx->pipe = CreateFile(szNamedPipe, GENERIC_READ, ctx->blk ? PIPE_WAIT : PIPE_NOWAIT, NULL, OPEN_EXISTING, 0, NULL);
189 : if (ctx->pipe == INVALID_HANDLE_VALUE) {
190 : GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[PipeIn] Failed to open %s: %d\n", szNamedPipe, GetLastError()));
191 : e = GF_URL_ERROR;
192 : }
193 : }
194 :
195 : err_exit:
196 :
197 : #else
198 8 : if (!gf_file_exists(src) && ctx->mkp) {
199 :
200 : #ifdef GPAC_CONFIG_DARWIN
201 : mknod(src,S_IFIFO | 0666, 0);
202 : #else
203 1 : mkfifo(src, 0666);
204 : #endif
205 1 : ctx->owns_pipe = GF_TRUE;
206 : }
207 :
208 16 : ctx->fd = open(src, ctx->blk ? O_RDONLY : O_RDONLY|O_NONBLOCK );
209 :
210 8 : if (ctx->fd < 0) {
211 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[PipeIn] Failed to open %s: %s\n", src, gf_errno_str(errno)));
212 : e = GF_URL_ERROR;
213 : }
214 : #endif
215 :
216 8 : setup_done:
217 :
218 : if (e) {
219 0 : if (frag_par) frag_par[0] = '#';
220 0 : if (cgi_par) cgi_par[0] = '?';
221 :
222 0 : gf_filter_setup_failure(filter, GF_URL_ERROR);
223 0 : ctx->owns_pipe = GF_FALSE;
224 0 : return GF_URL_ERROR;
225 : }
226 10 : GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[PipeIn] opening %s\n", src));
227 :
228 10 : ctx->is_end = GF_FALSE;
229 :
230 10 : if (frag_par) frag_par[0] = '#';
231 10 : if (cgi_par) cgi_par[0] = '?';
232 :
233 10 : ctx->is_first = GF_TRUE;
234 10 : if (!ctx->buffer)
235 10 : ctx->buffer = gf_malloc(ctx->block_size +1);
236 :
237 10 : gf_filter_post_process_task(filter);
238 :
239 : #ifdef GPAC_ENABLE_COVERAGE
240 10 : if (gf_sys_is_cov_mode()) {
241 10 : pipein_process_event(NULL, NULL);
242 : }
243 : #endif
244 : return GF_OK;
245 : }
246 :
247 :
248 10 : static void pipein_finalize(GF_Filter *filter)
249 : {
250 10 : GF_PipeInCtx *ctx = (GF_PipeInCtx *) gf_filter_get_udta(filter);
251 :
252 10 : if (!ctx->is_stdin) {
253 : #ifdef WIN32
254 : if (ctx->pipe != INVALID_HANDLE_VALUE) CloseHandle(ctx->pipe);
255 : #else
256 8 : if (ctx->fd>=0) close(ctx->fd);
257 : #endif
258 8 : if (ctx->owns_pipe)
259 1 : gf_file_delete(ctx->src);
260 : }
261 10 : if (ctx->buffer) gf_free(ctx->buffer);
262 :
263 10 : }
264 :
265 2939 : static GF_FilterProbeScore pipein_probe_url(const char *url, const char *mime_type)
266 : {
267 2939 : if (!strnicmp(url, "pipe://", 7)) return GF_FPROBE_SUPPORTED;
268 2931 : else if (!strnicmp(url, "pipe:", 5)) return GF_FPROBE_SUPPORTED;
269 2931 : else if (!strcmp(url, "-") || !strcmp(url, "stdin")) return GF_FPROBE_SUPPORTED;
270 :
271 2929 : return GF_FPROBE_NOT_SUPPORTED;
272 : }
273 :
274 10 : static Bool pipein_process_event(GF_Filter *filter, const GF_FilterEvent *evt)
275 : {
276 : GF_PipeInCtx *ctx;
277 10 : if (!filter || !evt) return GF_TRUE;
278 :
279 0 : ctx = (GF_PipeInCtx *) gf_filter_get_udta(filter);
280 0 : if (evt->base.on_pid && (evt->base.on_pid != ctx->pid))
281 : return GF_TRUE;
282 :
283 0 : switch (evt->base.type) {
284 : case GF_FEVT_PLAY:
285 : return GF_TRUE;
286 0 : case GF_FEVT_STOP:
287 : //stop sending data
288 0 : ctx->is_end = GF_TRUE;
289 0 : gf_filter_pid_set_eos(ctx->pid);
290 0 : return GF_TRUE;
291 0 : case GF_FEVT_SOURCE_SEEK:
292 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_MMIO, ("[PipeIn] Seek request not possible on pipes, ignoring\n"));
293 : return GF_TRUE;
294 0 : case GF_FEVT_SOURCE_SWITCH:
295 : assert(ctx->is_end);
296 0 : if (evt->seek.source_switch) {
297 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_MMIO, ("[PipeIn] source switch request not possible on pipes, ignoring\n"));
298 : }
299 0 : pipein_initialize(filter);
300 0 : gf_filter_post_process_task(filter);
301 0 : break;
302 : default:
303 : break;
304 : }
305 : return GF_TRUE;
306 : }
307 :
308 :
309 277 : static void pipein_pck_destructor(GF_Filter *filter, GF_FilterPid *pid, GF_FilterPacket *pck)
310 : {
311 277 : GF_PipeInCtx *ctx = (GF_PipeInCtx *) gf_filter_get_udta(filter);
312 277 : ctx->pck_out = GF_FALSE;
313 : //ready to process again
314 277 : gf_filter_post_process_task(filter);
315 277 : }
316 :
317 3977 : static GF_Err pipein_process(GF_Filter *filter)
318 : {
319 : GF_Err e;
320 : u32 to_read;
321 : s32 nb_read;
322 : GF_FilterPacket *pck;
323 3977 : GF_PipeInCtx *ctx = (GF_PipeInCtx *) gf_filter_get_udta(filter);
324 :
325 3977 : if (ctx->is_end)
326 : return GF_EOS;
327 :
328 : //until packet is released we return EOS (no processing), and ask for processing again upon release
329 3977 : if (ctx->pck_out)
330 : return GF_EOS;
331 :
332 3967 : if (ctx->pid && gf_filter_pid_would_block(ctx->pid)) {
333 : assert(0);
334 : return GF_OK;
335 : }
336 :
337 3967 : to_read = ctx->block_size;
338 :
339 3967 : if (ctx->is_stdin) {
340 : nb_read = 0;
341 22 : if (feof(stdin)) {
342 2 : if (!ctx->ka) {
343 2 : gf_filter_pid_set_eos(ctx->pid);
344 2 : return GF_EOS;
345 : }
346 : } else {
347 40 : nb_read = (s32) fread(ctx->buffer, 1, to_read, stdin);
348 20 : if (nb_read<0) {
349 0 : if (!ctx->ka) {
350 0 : gf_filter_pid_set_eos(ctx->pid);
351 0 : return GF_EOS;
352 : }
353 : }
354 : }
355 : } else {
356 :
357 3945 : errno = 0;
358 : #ifdef WIN32
359 : nb_read = -1;
360 : if (!ctx->blk && ctx->mkp) {
361 : DWORD res = WaitForMultipleObjects(1, &ctx->event, FALSE, 1);
362 : ResetEvent(ctx->event);
363 : if (res == WAIT_FAILED) {
364 : GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[PipeIn] WaitForMultipleObjects failed!\n"));
365 : return GF_IO_ERR;
366 : }
367 : if (GetOverlappedResult(ctx->pipe, &ctx->overlap, &res, FALSE) == 0) {
368 : if (GetLastError() == ERROR_IO_INCOMPLETE) {
369 : }
370 : else {
371 : CloseHandle(ctx->pipe);
372 : ctx->pipe = INVALID_HANDLE_VALUE;
373 : if (!ctx->ka) {
374 : GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[PipeIn] end of stream detected\n"));
375 : gf_filter_pid_set_eos(ctx->pid);
376 : return GF_EOS;
377 : }
378 : GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[PipeIn] Pipe closed by remote side, reopening!\n"));
379 : return pipein_initialize(filter);
380 : }
381 : }
382 : }
383 : if (! ReadFile(ctx->pipe, ctx->buffer, to_read, (LPDWORD) &nb_read, ctx->blk ? NULL : &ctx->overlap) ) {
384 : s32 error = GetLastError();
385 : if (error == ERROR_PIPE_LISTENING) return GF_OK;
386 : else if ((error == ERROR_IO_PENDING) || (error== ERROR_MORE_DATA)) {
387 : //non blocking pipe with writers active
388 : }
389 : else if (nb_read<0) {
390 : GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[PipeIn] Failed to read, error %d\n", error));
391 : return GF_IO_ERR;
392 : }
393 : else if (!ctx->ka) {
394 : GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[PipeIn] end of stream detected\n"));
395 : gf_filter_pid_set_eos(ctx->pid);
396 : CloseHandle(ctx->pipe);
397 : ctx->pipe = INVALID_HANDLE_VALUE;
398 : ctx->is_end = GF_TRUE;
399 : return GF_EOS;
400 : }
401 : else if (error == ERROR_BROKEN_PIPE) {
402 : GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[PipeIn] Pipe closed by remote side, reopening!\n"));
403 : CloseHandle(ctx->pipe);
404 : ctx->pipe = INVALID_HANDLE_VALUE;
405 : return pipein_initialize(filter);
406 : }
407 : return GF_OK;
408 : }
409 : #else
410 7890 : nb_read = (s32) read(ctx->fd, ctx->buffer, to_read);
411 3945 : if (nb_read <= 0) {
412 3688 : s32 res = errno;
413 3688 : if (res == EAGAIN) {
414 : //non blocking pipe with writers active
415 8 : } else if (nb_read<0) {
416 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[PipeIn] Failed to read, error %s\n", gf_errno_str(res) ));
417 : return GF_IO_ERR;
418 8 : } else if (!ctx->ka) {
419 8 : GF_LOG(GF_LOG_WARNING, GF_LOG_MMIO, ("[PipeIn] end of stream detected\n"));
420 8 : if (ctx->pid) gf_filter_pid_set_eos(ctx->pid);
421 8 : close(ctx->fd);
422 8 : ctx->fd=-1;
423 8 : ctx->is_end = GF_TRUE;
424 8 : return GF_EOS;
425 : }
426 : return GF_OK;
427 : }
428 : #endif
429 : }
430 :
431 277 : if (!nb_read)
432 : return GF_OK;
433 :
434 277 : ctx->buffer[nb_read] = 0;
435 277 : if (!ctx->pid || ctx->do_reconfigure) {
436 10 : GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[PipeIn] configuring stream %d probe bytes\n", nb_read));
437 10 : ctx->do_reconfigure = GF_FALSE;
438 10 : e = gf_filter_pid_raw_new(filter, ctx->src, NULL, ctx->mime, ctx->ext, ctx->buffer, nb_read, GF_TRUE, &ctx->pid);
439 10 : if (e) {
440 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_MMIO, ("[PipeIn] failed to configure stream: %s\n", gf_error_to_string(e) ));
441 : return e;
442 : }
443 10 : gf_filter_pid_set_property(ctx->pid, GF_PROP_PID_FILE_CACHED, &PROP_BOOL(GF_FALSE) );
444 10 : gf_filter_pid_set_property(ctx->pid, GF_PROP_PID_PLAYBACK_MODE, &PROP_UINT(GF_PLAYBACK_MODE_NONE) );
445 : }
446 277 : pck = gf_filter_pck_new_shared(ctx->pid, ctx->buffer, nb_read, pipein_pck_destructor);
447 277 : if (!pck) return GF_OUT_OF_MEM;
448 :
449 277 : GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[PipeIn] sending %d bytes\n", nb_read));
450 277 : gf_filter_pck_set_framing(pck, ctx->is_first, ctx->is_end);
451 277 : gf_filter_pck_set_sap(pck, GF_FILTER_SAP_1);
452 :
453 277 : ctx->is_first = GF_FALSE;
454 277 : ctx->pck_out = GF_TRUE;
455 277 : gf_filter_pck_send(pck);
456 277 : ctx->bytes_read += nb_read;
457 :
458 277 : if (ctx->is_end) {
459 0 : gf_filter_pid_set_eos(ctx->pid);
460 0 : return GF_EOS;
461 : }
462 277 : return ctx->pck_out ? GF_EOS : GF_OK;
463 : }
464 :
465 :
466 :
467 : #define OFFS(_n) #_n, offsetof(GF_PipeInCtx, _n)
468 :
469 : static const GF_FilterArgs PipeInArgs[] =
470 : {
471 : { OFFS(src), "name of source pipe", GF_PROP_NAME, NULL, NULL, 0},
472 : { OFFS(block_size), "buffer size used to read pipe", GF_PROP_UINT, "5000", NULL, GF_FS_ARG_HINT_ADVANCED},
473 : { OFFS(ext), "indicate file extension of pipe data", GF_PROP_STRING, NULL, NULL, 0},
474 : { OFFS(mime), "indicate mime type of pipe data", GF_PROP_STRING, NULL, NULL, 0},
475 : { OFFS(blk), "open pipe in block mode - see filter help", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
476 : { OFFS(ka), "keep-alive pipe when end of input is detected - see filter help", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
477 : { OFFS(mkp), "create pipe if not found - see filter help", GF_PROP_BOOL, "false", NULL, 0},
478 : {0}
479 : };
480 :
481 : static const GF_FilterCapability PipeInCaps[] =
482 : {
483 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
484 : };
485 :
486 : GF_FilterRegister PipeInRegister = {
487 : .name = "pin",
488 : GF_FS_SET_DESCRIPTION("pipe input")
489 : GF_FS_SET_HELP( "This filter handles generic input pipes (mono-directional) in blocking or non blocking mode.\n"
490 : "Warning: Input pipes cannot seek.\n"
491 : "Data format of the pipe may be specified using extension (either in file name or through [-ext]()) or MIME type through [-mime]().\n"
492 : "Note: Unless disabled at session level (see [-no-probe](CORE) ), file extensions are usually ignored and format probing is done on the first data block.\n"
493 : "\n"
494 : "# stdin pipe\n"
495 : "The filter can handle reading from stdin, by using `-` or `stdin` as input file name.\n"
496 : "EX gpac -i - vout\n"
497 : "EX gpac -i stdin vout\n"
498 : "\n"
499 : "# Named pipes\n"
500 : "The filter can handle reading from named pipes. The associated protocol scheme is `pipe://` when loaded as a generic input (eg, `-i pipe://URL` where URL is a relative or absolute pipe name).\n"
501 : "On Windows hosts, the default pipe prefix is `\\\\.\\pipe\\gpac\\` if no prefix is set.\n"
502 : "EX dst=mypipe resolves in \\\\.\\pipe\\gpac\\mypipe\n"
503 : "EX dst=\\\\.\\pipe\\myapp\\mypipe resolves in \\\\.\\pipe\\myapp\\mypipe\n"
504 : "Any destination name starting with `\\\\` is used as is, with `\\` translated in `/`.\n"
505 : "\n"
506 : "Input pipes are created by default in non-blocking mode.\n"
507 : "\n"
508 : "The filter can create the pipe if not found using [-mkp](). On windows hosts, this will create a pipe server.\n"
509 : "On non windows hosts, the created pipe will delete the pipe file upon filter destruction.\n"
510 : "\n"
511 : "Input pipes can be setup to run forever using [-ka](). In this case, any potential pipe close on the writing side and end of stream will only be triggered upon session close.\n"
512 : "This can be useful to pipe raw streams from different process into gpac:\n"
513 : "Receiver side: `gpac -i pipe://mypipe:ext=.264:mkp:ka`\n"
514 : "Sender side: `cat raw1.264 > mypipe && gpac -i raw2.264 -o pipe://mypipe:ext=.264`"
515 : "\n"
516 : "The pipe input can be created in blocking mode or non-blocking mode. If the filter creates the pipe, blocking mode is always enabled.\n"
517 : "")
518 : .private_size = sizeof(GF_PipeInCtx),
519 : .args = PipeInArgs,
520 : .flags = GF_FS_REG_BLOCKING,
521 : SETCAPS(PipeInCaps),
522 : .initialize = pipein_initialize,
523 : .finalize = pipein_finalize,
524 : .process = pipein_process,
525 : .process_event = pipein_process_event,
526 : .probe_url = pipein_probe_url
527 : };
528 :
529 :
530 2877 : const GF_FilterRegister *pipein_register(GF_FilterSession *session)
531 : {
532 2877 : return &PipeInRegister;
533 : }
|