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