1 /*
2 * GPAC - Multimedia Framework C SDK
3 *
4 * Authors: Jean Le Feuvre
5 * Copyright (c) Telecom ParisTech 2017-2020
6 * All rights reserved
7 *
8 * This file is part of GPAC / generic FILE 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
30 enum{
31 FILE_RAND_NONE=0,
32 FILE_RAND_ANY,
33 FILE_RAND_SC_ANY,
34 FILE_RAND_SC_AVC,
35 FILE_RAND_SC_HEVC,
36 FILE_RAND_SC_AV1
37 };
38
39 typedef struct
40 {
41 //options
42 char *src;
43 char *ext, *mime;
44 u32 block_size;
45 GF_Fraction64 range;
46
47 //only one output pid declared
48 GF_FilterPid *pid;
49
50 FILE *file;
51 u64 file_size;
52 u64 file_pos, end_pos;
53 Bool is_end, pck_out;
54 Bool is_null;
55 Bool full_file_only;
56 Bool do_reconfigure;
57 char *block;
58 u32 is_random;
59 Bool cached_set;
60 Bool no_failure;
61 } GF_FileInCtx;
62
63
filein_initialize(GF_Filter * filter)64 static GF_Err filein_initialize(GF_Filter *filter)
65 {
66 GF_FileInCtx *ctx = (GF_FileInCtx *) gf_filter_get_udta(filter);
67 FILE *old_file = NULL;
68 char *ext_start = NULL;
69 char *frag_par = NULL;
70 char *cgi_par = NULL;
71 char *src, *path;
72 const char *prev_url=NULL;
73
74 if (!ctx || !ctx->src) return GF_BAD_PARAM;
75
76 if (!strcmp(ctx->src, "null")) {
77 ctx->pid = gf_filter_pid_new(filter);
78 gf_filter_pid_set_property(ctx->pid, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(GF_STREAM_FILE));
79 gf_filter_pid_set_eos(ctx->pid);
80 ctx->is_end = GF_TRUE;
81 return GF_OK;
82 }
83 if (!strcmp(ctx->src, "rand") || !strcmp(ctx->src, "randsc")) {
84 gf_rand_init(GF_FALSE);
85 ctx->is_random = FILE_RAND_ANY;
86 if (!strcmp(ctx->src, "randsc")) {
87 ctx->is_random = FILE_RAND_SC_ANY;
88 if (ctx->mime) {
89 if (!strcmp(ctx->mime, "video/avc")) ctx->is_random = FILE_RAND_SC_AVC;
90 if (!strcmp(ctx->mime, "video/hevc")) ctx->is_random = FILE_RAND_SC_HEVC;
91 if (!strcmp(ctx->mime, "video/av1")) ctx->is_random = FILE_RAND_SC_AV1;
92 }
93 }
94
95 if (!ctx->block_size) ctx->block_size = 5000;
96 while (ctx->block_size % 4) ctx->block_size++;
97 ctx->block = gf_malloc(ctx->block_size +1);
98 return GF_OK;
99 }
100
101
102
103 if (strnicmp(ctx->src, "file:/", 6) && strnicmp(ctx->src, "gfio:/", 6) && strstr(ctx->src, "://")) {
104 gf_filter_setup_failure(filter, GF_NOT_SUPPORTED);
105 return GF_NOT_SUPPORTED;
106 }
107 path = strstr(ctx->src, "://");
108 if (path) path += 3;
109 if (path && strstr(path, "://")) {
110 ctx->is_end = GF_TRUE;
111 return gf_filter_pid_raw_new(filter, path, path, NULL, NULL, NULL, 0, GF_TRUE, &ctx->pid);
112 }
113
114 //local file
115
116 //strip any fragment identifer
117 ext_start = gf_file_ext_start(ctx->src);
118 frag_par = strchr(ext_start ? ext_start : ctx->src, '#');
119 if (frag_par) frag_par[0] = 0;
120 cgi_par = strchr(ctx->src, '?');
121 if (cgi_par) cgi_par[0] = 0;
122
123 src = (char *) ctx->src;
124 if (!strnicmp(ctx->src, "file://", 7)) src += 7;
125 else if (!strnicmp(ctx->src, "file:", 5)) src += 5;
126
127 //for gfio, do not close file until we open the new one
128 if (ctx->do_reconfigure) {
129 old_file = ctx->file;
130 ctx->file = NULL;
131 if (gf_fileio_check(old_file))
132 prev_url = gf_fileio_url((GF_FileIO *)old_file);
133 }
134
135 if (!ctx->file) {
136 ctx->file = gf_fopen_ex(src, prev_url, "rb");
137 }
138
139 if (old_file) {
140 gf_fclose(old_file);
141 }
142
143 if (!ctx->file) {
144 GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[FileIn] Failed to open %s\n", src));
145
146 if (frag_par) frag_par[0] = '#';
147 if (cgi_par) cgi_par[0] = '?';
148
149 if (ctx->no_failure) {
150 gf_filter_notification_failure(filter, GF_URL_ERROR, GF_FALSE);
151 ctx->is_end = GF_TRUE;
152 return GF_OK;
153 }
154
155 gf_filter_setup_failure(filter, GF_URL_ERROR);
156 #ifdef GPAC_ENABLE_COVERAGE
157 if (gf_sys_is_cov_mode() && !strcmp(src, "blob"))
158 return GF_OK;
159 #endif
160 return GF_URL_ERROR;
161 }
162 GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[FileIn] opening %s\n", src));
163 ctx->file_size = gf_fsize(ctx->file);
164
165 ctx->cached_set = GF_FALSE;
166 ctx->full_file_only = GF_FALSE;
167
168 if (ctx->do_reconfigure && gf_fileio_check(ctx->file)) {
169 GF_FileIO *gfio = (GF_FileIO *)ctx->file;
170 gf_free(ctx->src);
171 ctx->src = gf_strdup(gf_fileio_url(gfio));
172 }
173
174
175 ctx->file_pos = ctx->range.num;
176 if (ctx->range.den) {
177 ctx->end_pos = ctx->range.den;
178 if (ctx->end_pos>ctx->file_size) {
179 ctx->range.den = ctx->end_pos = ctx->file_size;
180 }
181 }
182 gf_fseek(ctx->file, ctx->file_pos, SEEK_SET);
183 ctx->is_end = GF_FALSE;
184
185 if (frag_par) frag_par[0] = '#';
186 if (cgi_par) cgi_par[0] = '?';
187
188 if (!ctx->block) {
189 if (!ctx->block_size) {
190 if (ctx->file_size>500000000) ctx->block_size = 1000000;
191 else ctx->block_size = 5000;
192 }
193 ctx->block = gf_malloc(ctx->block_size +1);
194 }
195 return GF_OK;
196 }
197
filein_finalize(GF_Filter * filter)198 static void filein_finalize(GF_Filter *filter)
199 {
200 GF_FileInCtx *ctx = (GF_FileInCtx *) gf_filter_get_udta(filter);
201
202 if (ctx->file) gf_fclose(ctx->file);
203 if (ctx->block) gf_free(ctx->block);
204 }
205
filein_probe_url(const char * url,const char * mime_type)206 static GF_FilterProbeScore filein_probe_url(const char *url, const char *mime_type)
207 {
208 char *ext_start = NULL;
209 char *frag_par = NULL;
210 char *cgi_par = NULL;
211 char *src = (char *) url;
212 Bool res;
213
214 if (!strcmp(url, "-") || !strcmp(url, "stdin")) return GF_FPROBE_NOT_SUPPORTED;
215
216 if (!strnicmp(url, "file://", 7)) src += 7;
217 else if (!strnicmp(url, "file:", 5)) src += 5;
218
219 if (!strcmp(url, "null")) return GF_FPROBE_SUPPORTED;
220 if (!strcmp(url, "rand")) return GF_FPROBE_SUPPORTED;
221 if (!strcmp(url, "randsc")) return GF_FPROBE_SUPPORTED;
222 if (!strncmp(url, "gfio://", 7)) {
223 GF_FileIO *gfio = gf_fileio_from_url(url);
224 if (gfio && gf_fileio_read_mode(gfio))
225 return GF_FPROBE_SUPPORTED;
226 return GF_FPROBE_NOT_SUPPORTED;
227 }
228
229 //strip any fragment identifer
230 ext_start = gf_file_ext_start(url);
231 frag_par = strchr(ext_start ? ext_start : url, '#');
232 if (frag_par) frag_par[0] = 0;
233 cgi_par = strchr(url, '?');
234 if (cgi_par) cgi_par[0] = 0;
235
236 res = gf_file_exists(src);
237
238 if (frag_par) frag_par[0] = '#';
239 if (cgi_par) cgi_par[0] = '?';
240
241 return res ? GF_FPROBE_SUPPORTED : GF_FPROBE_NOT_SUPPORTED;
242 }
243
filein_process_event(GF_Filter * filter,const GF_FilterEvent * evt)244 static Bool filein_process_event(GF_Filter *filter, const GF_FilterEvent *evt)
245 {
246 GF_FileInCtx *ctx = (GF_FileInCtx *) gf_filter_get_udta(filter);
247
248 if (evt->base.on_pid && (evt->base.on_pid != ctx->pid))
249 return GF_FALSE;
250
251 switch (evt->base.type) {
252 case GF_FEVT_PLAY:
253 case GF_FEVT_PLAY_HINT:
254 ctx->full_file_only = evt->play.full_file_only;
255 return GF_TRUE;
256 case GF_FEVT_STOP:
257 //stop sending data
258 ctx->is_end = GF_TRUE;
259 gf_filter_pid_set_eos(ctx->pid);
260 return GF_TRUE;
261 case GF_FEVT_SOURCE_SEEK:
262 if (ctx->is_random)
263 return GF_TRUE;
264 if (ctx->file_size && (evt->seek.start_offset >= ctx->file_size)) {
265 GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[FileIn] Seek request outside of file %s range ("LLU" vs size "LLU")\n", ctx->src, evt->seek.start_offset, ctx->file_size));
266 return GF_TRUE;
267 }
268
269 GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[FileIn] Asked to seek source to range "LLU"-"LLU"\n", evt->seek.start_offset, evt->seek.end_offset));
270 ctx->is_end = GF_FALSE;
271
272 if (gf_fileio_check(ctx->file)) {
273 ctx->cached_set = GF_FALSE;
274 }
275
276 if (ctx->file) {
277 int res = gf_fseek(ctx->file, evt->seek.start_offset, SEEK_SET);
278 if (res) {
279 GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[FileIn] Seek on file failed: %d\n", res));
280 return GF_TRUE;
281 }
282 }
283
284 ctx->file_pos = evt->seek.start_offset;
285 ctx->end_pos = evt->seek.end_offset;
286 if (ctx->end_pos>ctx->file_size) ctx->end_pos = ctx->file_size;
287 ctx->range.num = evt->seek.start_offset;
288 ctx->range.den = ctx->end_pos;
289 if (evt->seek.hint_block_size > ctx->block_size) {
290 ctx->block_size = evt->seek.hint_block_size;
291 ctx->block = gf_realloc(ctx->block, ctx->block_size+1);
292 }
293 return GF_TRUE;
294 case GF_FEVT_SOURCE_SWITCH:
295 if (ctx->is_random)
296 return GF_TRUE;
297 GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[FileIn] Asked to switch source to %s (range "LLU"-"LLU")\n", evt->seek.source_switch ? evt->seek.source_switch : "self", evt->seek.start_offset, evt->seek.end_offset));
298 assert(ctx->is_end);
299 ctx->range.num = evt->seek.start_offset;
300 ctx->range.den = evt->seek.end_offset;
301 if (evt->seek.source_switch) {
302 if (strcmp(evt->seek.source_switch, ctx->src)) {
303 gf_free(ctx->src);
304 ctx->src = gf_strdup(evt->seek.source_switch);
305 }
306 ctx->do_reconfigure = GF_TRUE;
307 }
308 //don't send a setup failure on source switch (this would destroy ourselves which we don't want in DASH)
309 ctx->no_failure = GF_TRUE;
310 filein_initialize(filter);
311 gf_filter_post_process_task(filter);
312 break;
313 default:
314 break;
315 }
316 return GF_FALSE;
317 }
318
319
filein_pck_destructor(GF_Filter * filter,GF_FilterPid * pid,GF_FilterPacket * pck)320 static void filein_pck_destructor(GF_Filter *filter, GF_FilterPid *pid, GF_FilterPacket *pck)
321 {
322 GF_FileInCtx *ctx = (GF_FileInCtx *) gf_filter_get_udta(filter);
323 ctx->pck_out = GF_FALSE;
324 //ready to process again
325 gf_filter_post_process_task(filter);
326 }
327
filein_process(GF_Filter * filter)328 static GF_Err filein_process(GF_Filter *filter)
329 {
330 GF_Err e;
331 u32 nb_read, to_read;
332 u64 lto_read;
333 GF_FilterPacket *pck;
334 GF_FileInCtx *ctx = (GF_FileInCtx *) gf_filter_get_udta(filter);
335
336 if (ctx->is_end)
337 return GF_EOS;
338
339 //until packet is released we return EOS (no processing), and ask for processing again upon release
340 if (ctx->pck_out)
341 return GF_EOS;
342 if (ctx->pid && gf_filter_pid_would_block(ctx->pid)) {
343 assert(0);
344 return GF_OK;
345 }
346
347 if (ctx->full_file_only && ctx->pid && !ctx->do_reconfigure && ctx->cached_set) {
348 ctx->is_end = GF_TRUE;
349 pck = gf_filter_pck_new_shared(ctx->pid, ctx->block, 0, filein_pck_destructor);
350 gf_filter_pck_set_framing(pck, ctx->file_pos ? GF_FALSE : GF_TRUE, ctx->is_end);
351 gf_filter_pck_set_sap(pck, GF_FILTER_SAP_1);
352 ctx->pck_out = GF_TRUE;
353 gf_filter_pck_send(pck);
354 gf_filter_pid_set_eos(ctx->pid);
355
356 if (ctx->file_size && gf_filter_reporting_enabled(filter)) {
357 char szStatus[1024], *szSrc;
358 szSrc = gf_file_basename(ctx->src);
359
360 sprintf(szStatus, "%s: EOS (dispatch canceled after "LLD" b, file size "LLD" b)", szSrc, (s64) ctx->file_pos, (s64) ctx->file_size);
361 gf_filter_update_status(filter, 10000, szStatus);
362 }
363 return GF_OK;
364 }
365 if (ctx->is_random) {
366 u32 i;
367 if (ctx->is_random>=FILE_RAND_SC_ANY) {
368 for (i=0; i<ctx->block_size; i+= 4) {
369 u32 val = gf_rand();
370
371 if (i+4>=ctx->block_size) {
372 * ((u32 *) (ctx->block + i)) = val;
373 continue;
374 }
375 if (val % 100) {
376 * ((u32 *) (ctx->block + i)) = val;
377 continue;
378 }
379 if (ctx->is_random==FILE_RAND_SC_AVC) {
380 u32 rand_high = val>>24;
381 * ((u32 *) (ctx->block + i)) = 0x00000001;
382 i += 4;
383 val &= 0x00FFFFFF;
384 rand_high = rand_high%31;
385 rand_high <<= 24;
386 val |= rand_high;
387 * ((u32 *) (ctx->block + i)) = val;
388 } else if (ctx->is_random==FILE_RAND_SC_HEVC) {
389 u32 rand_high = val>>16;
390 * ((u32 *) (ctx->block + i)) = 0x00000001;
391 i += 4;
392 val &= 0x0000FFFF;
393 rand_high = rand_high % 63;
394 rand_high <<= 8;
395 rand_high |= 1; //end of layerid (=0) and temporal sublayer (=1)
396 rand_high <<= 16;
397 val |= rand_high;
398 * ((u32 *) (ctx->block + i)) = val;
399 }
400 else {
401 val &= 0x000001FF;
402 * ((u32 *) (ctx->block + i)) = val;
403 }
404 }
405 } else {
406 for (i=0; i<ctx->block_size; i+= 4) {
407 * ((u32 *) (ctx->block + i)) = gf_rand();
408 }
409 }
410 ctx->block[ctx->block_size]=0;
411
412 if (!ctx->pid) {
413 e = gf_filter_pid_raw_new(filter, ctx->src, ctx->src, ctx->mime, ctx->ext, ctx->block, ctx->block_size, GF_TRUE, &ctx->pid);
414 if (e) return e;
415 }
416 pck = gf_filter_pck_new_shared(ctx->pid, ctx->block, ctx->block_size, filein_pck_destructor);
417 if (!pck)
418 return GF_OK;
419
420 gf_filter_pck_set_framing(pck, ctx->file_pos ? GF_FALSE : GF_TRUE, GF_FALSE);
421 gf_filter_pck_set_sap(pck, GF_FILTER_SAP_1);
422 ctx->file_pos += ctx->block_size;
423
424 ctx->pck_out = GF_TRUE;
425 gf_filter_pck_send(pck);
426 return GF_OK;
427 }
428
429 //compute size to read as u64 (large file)
430 if (ctx->end_pos > ctx->file_pos)
431 lto_read = ctx->end_pos - ctx->file_pos;
432 else if (ctx->file_size)
433 lto_read = ctx->file_size - ctx->file_pos;
434 else
435 lto_read = ctx->block_size;
436
437 //and clamp based on blocksize as u32
438 if (lto_read > (u64) ctx->block_size)
439 to_read = (u64) ctx->block_size;
440 else
441 to_read = (u32) lto_read;
442
443 nb_read = (u32) gf_fread(ctx->block, to_read, ctx->file);
444 if (!nb_read)
445 ctx->file_size = ctx->file_pos;
446
447 ctx->block[nb_read] = 0;
448 if (!ctx->pid || ctx->do_reconfigure) {
449 ctx->do_reconfigure = GF_FALSE;
450 e = gf_filter_pid_raw_new(filter, ctx->src, ctx->src, ctx->mime, ctx->ext, ctx->block, nb_read, GF_TRUE, &ctx->pid);
451 if (e) return e;
452
453 if (!gf_fileio_check(ctx->file)) {
454 gf_filter_pid_set_property(ctx->pid, GF_PROP_PID_FILE_CACHED, &PROP_BOOL(GF_TRUE) );
455
456 gf_filter_pid_set_property(ctx->pid, GF_PROP_PID_DOWN_SIZE, ctx->file_size ? &PROP_LONGUINT(ctx->file_size) : NULL);
457
458 ctx->cached_set = GF_TRUE;
459 }
460
461 if (ctx->range.num || ctx->range.den)
462 gf_filter_pid_set_property(ctx->pid, GF_PROP_PID_FILE_RANGE, &PROP_FRAC64(ctx->range) );
463 }
464
465 //GFIO wrapper, gets stats and update
466 if (!ctx->cached_set) {
467 u64 bdone, btotal;
468 Bool fcached;
469 u32 bytes_per_sec;
470 if (gf_fileio_get_stats((GF_FileIO *)ctx->file, &bdone, &btotal, &fcached, &bytes_per_sec)) {
471 if (fcached) {
472 gf_filter_pid_set_property(ctx->pid, GF_PROP_PID_FILE_CACHED, &PROP_BOOL(fcached) );
473 ctx->cached_set = GF_TRUE;
474 }
475
476 gf_filter_pid_set_info(ctx->pid, GF_PROP_PID_DOWN_SIZE, &PROP_LONGUINT(btotal) );
477 gf_filter_pid_set_info(ctx->pid, GF_PROP_PID_DOWN_BYTES, &PROP_LONGUINT(bdone) );
478 gf_filter_pid_set_info(ctx->pid, GF_PROP_PID_DOWN_RATE, &PROP_UINT(bytes_per_sec*8) );
479 }
480 }
481
482 pck = gf_filter_pck_new_shared(ctx->pid, ctx->block, nb_read, filein_pck_destructor);
483 if (!pck)
484 return GF_OK;
485
486 gf_filter_pck_set_byte_offset(pck, ctx->file_pos);
487
488 if (ctx->file_size && (ctx->file_pos + nb_read == ctx->file_size)) {
489 ctx->is_end = GF_TRUE;
490 gf_filter_pid_set_info(ctx->pid, GF_PROP_PID_DOWN_BYTES, &PROP_LONGUINT(ctx->file_size) );
491 } else {
492 if (nb_read < to_read) {
493 Bool is_eof;
494 GF_LOG(GF_LOG_WARNING, GF_LOG_MMIO, ("[FileIn] Asked to read %d but got only %d\n", to_read, nb_read));
495
496 is_eof = gf_feof(ctx->file);
497
498 if (is_eof) {
499 gf_filter_pid_set_info(ctx->pid, GF_PROP_PID_DOWN_BYTES, &PROP_LONGUINT(ctx->file_size) );
500 ctx->is_end = GF_TRUE;
501 GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[FileIn] IO error EOF found after reading %d bytes but file %s size is %d\n", ctx->file_pos+nb_read, ctx->src, ctx->file_size));
502 }
503 } else {
504 gf_filter_pid_set_info(ctx->pid, GF_PROP_PID_DOWN_BYTES, &PROP_LONGUINT(ctx->file_pos) );
505 }
506 }
507 gf_filter_pck_set_framing(pck, ctx->file_pos ? GF_FALSE : GF_TRUE, ctx->is_end);
508 gf_filter_pck_set_sap(pck, GF_FILTER_SAP_1);
509 ctx->file_pos += nb_read;
510
511 ctx->pck_out = GF_TRUE;
512 gf_filter_pck_send(pck);
513
514 if (ctx->file_size && gf_filter_reporting_enabled(filter)) {
515 char szStatus[1024], *szSrc;
516 szSrc = gf_file_basename(ctx->src);
517
518 sprintf(szStatus, "%s: % 16"LLD_SUF" /% 16"LLD_SUF" (%02.02f)", szSrc, (s64) ctx->file_pos, (s64) ctx->file_size, ((Double)ctx->file_pos*100.0)/ctx->file_size);
519 gf_filter_update_status(filter, (u32) (ctx->file_pos*10000/ctx->file_size), szStatus);
520 }
521
522 if (ctx->is_end) {
523 gf_filter_pid_set_eos(ctx->pid);
524 return GF_EOS;
525 }
526 return ctx->pck_out ? GF_EOS : GF_OK;
527 }
528
529
530
531 #define OFFS(_n) #_n, offsetof(GF_FileInCtx, _n)
532
533 static const GF_FilterArgs FileInArgs[] =
534 {
535 { OFFS(src), "location of source content", GF_PROP_NAME, NULL, NULL, 0},
536 { OFFS(block_size), "block size used to read file. 0 means 5000 if file less than 500m, 1M otherwise", GF_PROP_UINT, "0", NULL, GF_FS_ARG_HINT_ADVANCED},
537 { OFFS(range), "byte range", GF_PROP_FRACTION64, "0-0", NULL, 0},
538 { OFFS(ext), "override file extension", GF_PROP_NAME, NULL, NULL, 0},
539 { OFFS(mime), "set file mime type", GF_PROP_NAME, NULL, NULL, 0},
540 {0}
541 };
542
543 static const GF_FilterCapability FileInCaps[] =
544 {
545 CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
546 };
547
548 GF_FilterRegister FileInRegister = {
549 .name = "fin",
550 GF_FS_SET_DESCRIPTION("File input")
551 GF_FS_SET_HELP("This filter dispatch raw blocks from input file into a filter chain.\n"
552 "Block size can be adjusted using [-block_size]().\n"
553 "Content format can be forced through [-mime]() and file extension can be changed through [-ext]().\n"
554 "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"
555 "The special file name `null` is used for creating a file with no data, needed by some filters such as [dasher](dasher).\n"
556 "The special file name `rand` is used to generate random data.\n"
557 "The special file name `randsc` is used to generate random data with fake startcodes (0x000001).\n"
558 "\n"
559 "The filter handles both files and GF_FileIO objects as input URL.\n"
560 )
561 .private_size = sizeof(GF_FileInCtx),
562 .args = FileInArgs,
563 .initialize = filein_initialize,
564 SETCAPS(FileInCaps),
565 .finalize = filein_finalize,
566 .process = filein_process,
567 .process_event = filein_process_event,
568 .probe_url = filein_probe_url
569 };
570
571
filein_register(GF_FilterSession * session)572 const GF_FilterRegister *filein_register(GF_FilterSession *session)
573 {
574 return &FileInRegister;
575 }
576
577