Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2000-2021
6 : * All rights reserved
7 : *
8 : * This file is part of GPAC / H263 reframer 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 : #include <gpac/avparse.h>
27 : #include <gpac/constants.h>
28 : #include <gpac/filters.h>
29 :
30 : typedef struct
31 : {
32 : u64 pos;
33 : Double duration;
34 : } H263Idx;
35 :
36 : typedef struct
37 : {
38 : //filter args
39 : GF_Fraction fps;
40 : Double index;
41 :
42 : //only one input pid declared
43 : GF_FilterPid *ipid;
44 : //only one output pid declared
45 : GF_FilterPid *opid;
46 :
47 : GF_BitStream *bs;
48 : u64 cts;
49 : u32 width, height;
50 : GF_Fraction64 duration;
51 : Double start_range;
52 : Bool in_seek;
53 : u32 timescale;
54 :
55 : u32 resume_from;
56 :
57 :
58 : u32 bytes_in_header;
59 : char hdr_store[8];
60 :
61 : Bool is_playing;
62 : Bool is_file, file_loaded;
63 : Bool initial_play_done;
64 :
65 : GF_FilterPacket *src_pck;
66 :
67 : H263Idx *indexes;
68 : u32 index_alloc_size, index_size;
69 : u32 bitrate;
70 : } GF_H263DmxCtx;
71 :
72 :
73 7 : GF_Err h263dmx_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
74 : {
75 : const GF_PropertyValue *p;
76 7 : GF_H263DmxCtx *ctx = gf_filter_get_udta(filter);
77 :
78 7 : if (is_remove) {
79 0 : ctx->ipid = NULL;
80 0 : if (ctx->opid) {
81 0 : gf_filter_pid_remove(ctx->opid);
82 0 : ctx->opid = NULL;
83 : }
84 : return GF_OK;
85 : }
86 7 : if (! gf_filter_pid_check_caps(pid))
87 : return GF_NOT_SUPPORTED;
88 :
89 7 : ctx->ipid = pid;
90 7 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_TIMESCALE);
91 7 : if (p) ctx->timescale = p->value.uint;
92 :
93 7 : if (ctx->timescale && !ctx->opid) {
94 0 : ctx->opid = gf_filter_pid_new(filter);
95 0 : gf_filter_pid_copy_properties(ctx->opid, ctx->ipid);
96 0 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_UNFRAMED, NULL);
97 : }
98 : return GF_OK;
99 : }
100 :
101 : #define H263_CACHE_SIZE 4096
102 628 : static u32 h263dmx_next_start_code_bs(GF_BitStream *bs)
103 : {
104 : u32 v, bpos;
105 : unsigned char h263_cache[H263_CACHE_SIZE];
106 : u64 end, cache_start, load_size;
107 628 : u64 start = gf_bs_get_position(bs);
108 :
109 : /*skip 16b header*/
110 628 : gf_bs_read_u16(bs);
111 : bpos = 0;
112 : load_size = 0;
113 : cache_start = 0;
114 : end = 0;
115 : v = 0xffffffff;
116 374633 : while (!end) {
117 : /*refill cache*/
118 373380 : if (bpos == (u32) load_size) {
119 635 : if (!gf_bs_available(bs)) break;
120 632 : load_size = gf_bs_available(bs);
121 632 : if (load_size>H263_CACHE_SIZE) load_size=H263_CACHE_SIZE;
122 : bpos = 0;
123 632 : cache_start = gf_bs_get_position(bs);
124 632 : gf_bs_read_data(bs, (char *) h263_cache, (u32) load_size);
125 : }
126 373377 : v = (v<<8) | h263_cache[bpos];
127 373377 : bpos++;
128 373377 : if ((v >> (32-22)) == 0x20) end = cache_start+bpos-4;
129 : }
130 628 : gf_bs_seek(bs, start);
131 628 : if (!end) end = gf_bs_get_size(bs);
132 628 : return (u32) (end-start);
133 : }
134 494 : static void h263dmx_check_dur(GF_Filter *filter, GF_H263DmxCtx *ctx)
135 : {
136 :
137 : FILE *stream;
138 : GF_BitStream *bs;
139 : u64 duration, cur_dur, rate;
140 : const GF_PropertyValue *p;
141 494 : if (!ctx->opid || ctx->timescale || ctx->file_loaded) return;
142 :
143 7 : if (ctx->index<=0) {
144 4 : ctx->file_loaded = GF_TRUE;
145 : return;
146 : }
147 :
148 3 : p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_FILEPATH);
149 3 : if (!p || !p->value.string || !strncmp(p->value.string, "gmem://", 7)) {
150 0 : ctx->is_file = GF_FALSE;
151 0 : ctx->file_loaded = GF_TRUE;
152 : return;
153 : }
154 3 : ctx->is_file = GF_TRUE;
155 :
156 3 : stream = gf_fopen(p->value.string, "rb");
157 3 : if (!stream) return;
158 :
159 3 : ctx->index_size = 0;
160 :
161 3 : bs = gf_bs_from_file(stream, GF_BITSTREAM_READ);
162 : duration = 0;
163 : cur_dur = 0;
164 631 : while (gf_bs_available(bs)) {
165 : u8 type;
166 628 : u64 pos = gf_bs_get_position(bs);
167 628 : u64 next_pos = pos + h263dmx_next_start_code_bs(bs);
168 628 : gf_bs_read_u32(bs);
169 628 : type = gf_bs_read_u8(bs);
170 :
171 628 : if (type & 0x02) type = 0;
172 : else
173 : type = 1;
174 :
175 :
176 628 : duration += ctx->fps.den;
177 628 : cur_dur += ctx->fps.den;
178 : //only index at I-frame start
179 628 : if (pos && type && (cur_dur > ctx->index * ctx->fps.num) ) {
180 7 : if (!ctx->index_alloc_size) ctx->index_alloc_size = 10;
181 6 : else if (ctx->index_alloc_size == ctx->index_size) ctx->index_alloc_size *= 2;
182 7 : ctx->indexes = gf_realloc(ctx->indexes, sizeof(H263Idx)*ctx->index_alloc_size);
183 7 : ctx->indexes[ctx->index_size].pos = pos;
184 7 : ctx->indexes[ctx->index_size].duration = (Double) duration;
185 7 : ctx->indexes[ctx->index_size].duration /= ctx->fps.num;
186 7 : ctx->index_size ++;
187 : cur_dur = 0;
188 : }
189 :
190 628 : gf_bs_seek(bs, next_pos);
191 : }
192 3 : rate = gf_bs_get_position(bs);
193 3 : gf_bs_del(bs);
194 3 : gf_fclose(stream);
195 :
196 3 : if (!ctx->duration.num || (ctx->duration.num * ctx->fps.num != duration * ctx->duration.den)) {
197 3 : ctx->duration.num = (s32) duration;
198 3 : ctx->duration.den = ctx->fps.num;
199 :
200 3 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DURATION, & PROP_FRAC64(ctx->duration));
201 :
202 :
203 3 : if (duration && !gf_sys_is_test_mode() ) {
204 0 : rate *= 8 * ctx->duration.den;
205 0 : rate /= ctx->duration.num;
206 0 : ctx->bitrate = (u32) rate;
207 : }
208 : }
209 :
210 3 : p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_FILE_CACHED);
211 3 : if (p && p->value.boolean) ctx->file_loaded = GF_TRUE;
212 3 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CAN_DATAREF, & PROP_BOOL(GF_TRUE ) );
213 : }
214 :
215 1125 : static void h263dmx_check_pid(GF_Filter *filter, GF_H263DmxCtx *ctx, u32 width, u32 height)
216 : {
217 :
218 1125 : if (!ctx->opid) {
219 7 : ctx->opid = gf_filter_pid_new(filter);
220 7 : h263dmx_check_dur(filter, ctx);
221 : }
222 1125 : if ((ctx->width == width) && (ctx->height == height)) return;
223 :
224 : //copy properties at init or reconfig
225 7 : gf_filter_pid_copy_properties(ctx->opid, ctx->ipid);
226 7 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_UNFRAMED, NULL);
227 7 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STREAM_TYPE, & PROP_UINT(GF_STREAM_VISUAL));
228 7 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CODECID, & PROP_UINT(GF_CODECID_H263));
229 7 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_TIMESCALE, & PROP_UINT(ctx->fps.num));
230 7 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_FPS, & PROP_FRAC(ctx->fps));
231 :
232 7 : if (ctx->duration.num)
233 3 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DURATION, & PROP_FRAC64(ctx->duration));
234 :
235 7 : ctx->width = width;
236 7 : ctx->height = height;
237 7 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_WIDTH, & PROP_UINT( width));
238 7 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_HEIGHT, & PROP_UINT( height));
239 :
240 7 : if (ctx->bitrate) {
241 0 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_BITRATE, & PROP_UINT(ctx->bitrate));
242 : }
243 :
244 7 : if (ctx->is_file && ctx->index) {
245 3 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_PLAYBACK_MODE, & PROP_UINT(GF_PLAYBACK_MODE_FASTFORWARD) );
246 : }
247 : }
248 :
249 1129 : static Bool h263dmx_process_event(GF_Filter *filter, const GF_FilterEvent *evt)
250 : {
251 : u32 i;
252 : u64 file_pos = 0;
253 : GF_FilterEvent fevt;
254 1129 : GF_H263DmxCtx *ctx = gf_filter_get_udta(filter);
255 :
256 1129 : switch (evt->base.type) {
257 7 : case GF_FEVT_PLAY:
258 7 : if (!ctx->is_playing) {
259 7 : ctx->is_playing = GF_TRUE;
260 7 : ctx->cts = 0;
261 7 : ctx->bytes_in_header = 0;
262 : }
263 7 : if (! ctx->is_file) {
264 : return GF_FALSE;
265 : }
266 3 : ctx->start_range = evt->play.start_range;
267 3 : ctx->in_seek = GF_TRUE;
268 :
269 3 : if (ctx->start_range) {
270 0 : for (i=1; i<ctx->index_size; i++) {
271 0 : if (ctx->indexes[i].duration>ctx->start_range) {
272 0 : ctx->cts = (u64) (ctx->indexes[i-1].duration * ctx->fps.num);
273 0 : file_pos = ctx->indexes[i-1].pos;
274 0 : break;
275 : }
276 : }
277 : }
278 3 : if (!ctx->initial_play_done) {
279 3 : ctx->initial_play_done = GF_TRUE;
280 : //seek will not change the current source state, don't send a seek
281 3 : if (!file_pos)
282 : return GF_TRUE;
283 : }
284 : //post a seek
285 0 : GF_FEVT_INIT(fevt, GF_FEVT_SOURCE_SEEK, ctx->ipid);
286 0 : fevt.seek.start_offset = file_pos;
287 0 : gf_filter_pid_send_event(ctx->ipid, &fevt);
288 :
289 : //cancel event
290 0 : return GF_TRUE;
291 :
292 3 : case GF_FEVT_STOP:
293 : //don't cancel event
294 3 : ctx->is_playing = GF_FALSE;
295 3 : return GF_FALSE;
296 :
297 : case GF_FEVT_SET_SPEED:
298 : //cancel event
299 : return GF_TRUE;
300 : default:
301 : break;
302 : }
303 : //by default don't cancel event - to rework once we have downloading in place
304 1119 : return GF_FALSE;
305 : }
306 :
307 : static GFINLINE void h263dmx_update_cts(GF_H263DmxCtx *ctx)
308 : {
309 : assert(ctx->fps.num);
310 : assert(ctx->fps.den);
311 :
312 1100 : if (ctx->timescale) {
313 0 : u64 inc = ctx->fps.den;
314 0 : inc *= ctx->timescale;
315 0 : inc /= ctx->fps.num;
316 0 : ctx->cts += inc;
317 : } else {
318 1100 : ctx->cts += ctx->fps.den;
319 : }
320 : }
321 :
322 :
323 : static s32 h263dmx_next_start_code(u8 *data, u32 size)
324 : {
325 : u32 v, bpos;
326 : s64 end;
327 : s64 start = 0;
328 :
329 : /*skip 16b header*/
330 : start+=2;
331 : bpos = 0;
332 : end = 0;
333 : v = 0xffffffff;
334 10321085 : while (!end) {
335 10318307 : if (bpos == size) return -1;
336 10315608 : v = (v<<8) | data[bpos];
337 10315608 : bpos++;
338 10315608 : if ((v >> (32-22)) == 0x20) end = start + bpos - 4;
339 : }
340 :
341 2778 : return (s32) (end-start);
342 : }
343 :
344 : static void h263_get_pic_size(GF_BitStream *bs, u32 fmt, u32 *w, u32 *h)
345 : {
346 : switch (fmt) {
347 : case 1:
348 : *w = 128;
349 : *h = 96;
350 : break;
351 : case 2:
352 : *w = 176;
353 : *h = 144;
354 : break;
355 : case 3:
356 : *w = 352;
357 : *h = 288;
358 : break;
359 : case 4:
360 : *w = 704;
361 : *h = 576;
362 : break;
363 : case 5:
364 : *w = 1409;
365 : *h = 1152 ;
366 : break;
367 : default:
368 : *w = *h = 0;
369 : break;
370 : }
371 : }
372 :
373 1126 : GF_Err h263dmx_process(GF_Filter *filter)
374 : {
375 1126 : GF_H263DmxCtx *ctx = gf_filter_get_udta(filter);
376 : GF_FilterPacket *pck, *dst_pck;
377 : u64 byte_offset;
378 : char *data;
379 : u8 *start;
380 : Bool first_frame_found = GF_FALSE;
381 : u32 pck_size;
382 : s32 remain;
383 :
384 : //always reparse duration
385 1126 : if (!ctx->duration.num)
386 487 : h263dmx_check_dur(filter, ctx);
387 :
388 1126 : pck = gf_filter_pid_get_packet(ctx->ipid);
389 1126 : if (!pck) {
390 7 : if (gf_filter_pid_is_eos(ctx->ipid)) {
391 4 : if (ctx->opid)
392 4 : gf_filter_pid_set_eos(ctx->opid);
393 4 : if (ctx->src_pck) gf_filter_pck_unref(ctx->src_pck);
394 4 : ctx->src_pck = NULL;
395 4 : return GF_EOS;
396 : }
397 : return GF_OK;
398 : }
399 :
400 1119 : data = (char *) gf_filter_pck_get_data(pck, &pck_size);
401 1119 : byte_offset = gf_filter_pck_get_byte_offset(pck);
402 :
403 : start = data;
404 1119 : remain = pck_size;
405 :
406 :
407 1119 : if (ctx->bytes_in_header) {
408 : #if 0
409 : if (ctx->bytes_in_header + remain < 7) {
410 : memcpy(ctx->header + ctx->bytes_in_header, start, remain);
411 : ctx->bytes_in_header += remain;
412 : gf_filter_pid_drop_packet(ctx->ipid);
413 : return GF_OK;
414 : }
415 : alread_sync = 7 - ctx->bytes_in_header;
416 : memcpy(ctx->header + ctx->bytes_in_header, start, alread_sync);
417 : start += alread_sync;
418 : remain -= alread_sync;
419 : ctx->bytes_in_header = 0;
420 : alread_sync = GF_TRUE;
421 : #endif
422 :
423 : }
424 : //input pid sets some timescale - we flushed pending data , update cts
425 1114 : else if (ctx->timescale) {
426 0 : u64 cts = gf_filter_pck_get_cts(pck);
427 0 : if (cts != GF_FILTER_NO_TS)
428 0 : ctx->cts = cts;
429 0 : if (ctx->src_pck) gf_filter_pck_unref(ctx->src_pck);
430 0 : ctx->src_pck = pck;
431 0 : gf_filter_pck_ref_props(&ctx->src_pck);
432 : }
433 :
434 1119 : if (ctx->resume_from) {
435 974 : if (gf_filter_pid_would_block(ctx->opid))
436 : return GF_OK;
437 974 : start += ctx->resume_from;
438 974 : remain -= ctx->resume_from;
439 974 : ctx->resume_from = 0;
440 : }
441 :
442 1126 : while (remain) {
443 : u32 size=0;
444 : Bool full_frame;
445 : u8 *pck_data;
446 : s32 current, next;
447 : u32 fmt, w, h;
448 :
449 : //not enough bytes
450 1126 : if (remain<5) {
451 0 : memcpy(ctx->hdr_store, start, remain);
452 0 : ctx->bytes_in_header = remain;
453 127 : break;
454 : }
455 :
456 1126 : if (ctx->bytes_in_header) {
457 5 : if (first_frame_found) {
458 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_MMIO, ("[H263Dmx] corrupted frame!\n"));
459 : }
460 :
461 5 : memcpy(ctx->hdr_store + ctx->bytes_in_header, start, 8 - ctx->bytes_in_header);
462 : current = h263dmx_next_start_code(ctx->hdr_store, 8);
463 :
464 : //no start code in stored buffer
465 5 : if (current<0) {
466 4 : dst_pck = gf_filter_pck_new_alloc(ctx->opid, ctx->bytes_in_header, &pck_data);
467 996 : if (!dst_pck) return GF_OUT_OF_MEM;
468 :
469 4 : if (ctx->src_pck) gf_filter_pck_merge_properties(ctx->src_pck, dst_pck);
470 :
471 4 : memcpy(pck_data, ctx->hdr_store, ctx->bytes_in_header);
472 4 : gf_filter_pck_set_framing(dst_pck, GF_FALSE, GF_FALSE);
473 4 : gf_filter_pck_set_cts(dst_pck, ctx->cts);
474 4 : gf_filter_pck_set_duration(dst_pck, ctx->fps.den);
475 4 : if (ctx->in_seek) gf_filter_pck_set_seek_flag(dst_pck, GF_TRUE);
476 :
477 4 : if (byte_offset != GF_FILTER_NO_BO) {
478 4 : gf_filter_pck_set_byte_offset(dst_pck, byte_offset - ctx->bytes_in_header);
479 : }
480 4 : gf_filter_pck_send(dst_pck);
481 4 : ctx->bytes_in_header = 0;
482 :
483 4 : current = h263dmx_next_start_code(start, remain);
484 : }
485 : } else {
486 : //locate next start code
487 1121 : current = h263dmx_next_start_code(start, remain);
488 : }
489 :
490 :
491 1126 : if (current<0) {
492 : //not enough bytes to process start code !!
493 : break;
494 : }
495 :
496 1125 : if (current>0) {
497 119 : if (!ctx->opid) {
498 0 : if (ctx->bytes_in_header) {
499 0 : ctx->bytes_in_header -= current;
500 : } else {
501 0 : start += current;
502 0 : remain -= current;
503 : }
504 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_MMIO, ("[H263Dmx] garbage before first frame!\n"));
505 0 : continue;
506 : }
507 119 : if (first_frame_found) {
508 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_MMIO, ("[H263Dmx] corrupted frame!\n"));
509 : }
510 : //flush remaining
511 119 : dst_pck = gf_filter_pck_new_alloc(ctx->opid, current, &pck_data);
512 119 : if (!dst_pck) return GF_OUT_OF_MEM;
513 :
514 119 : if (ctx->src_pck) gf_filter_pck_merge_properties(ctx->src_pck, dst_pck);
515 :
516 119 : if (ctx->bytes_in_header) {
517 1 : if (byte_offset != GF_FILTER_NO_BO) {
518 1 : gf_filter_pck_set_byte_offset(dst_pck, byte_offset - ctx->bytes_in_header);
519 : }
520 1 : ctx->bytes_in_header -= current;
521 1 : memcpy(pck_data, ctx->hdr_store, current);
522 : } else {
523 118 : if (byte_offset != GF_FILTER_NO_BO) {
524 118 : gf_filter_pck_set_byte_offset(dst_pck, byte_offset);
525 : }
526 118 : memcpy(pck_data, start, current);
527 118 : start += current;
528 118 : remain -= current;
529 : }
530 119 : gf_filter_pck_set_framing(dst_pck, GF_FALSE, GF_TRUE);
531 119 : gf_filter_pck_set_cts(dst_pck, ctx->cts);
532 119 : gf_filter_pck_set_duration(dst_pck, ctx->fps.den);
533 119 : if (ctx->in_seek) gf_filter_pck_set_seek_flag(dst_pck, GF_TRUE);
534 119 : gf_filter_pck_send(dst_pck);
535 :
536 : h263dmx_update_cts(ctx);
537 : }
538 :
539 1125 : if (ctx->bytes_in_header) {
540 1 : gf_bs_reassign_buffer(ctx->bs, ctx->hdr_store+current, 8-current);
541 1124 : } else if (!ctx->bs) {
542 7 : ctx->bs = gf_bs_new(start, remain, GF_BITSTREAM_READ);
543 : } else {
544 1117 : gf_bs_reassign_buffer(ctx->bs, start, remain);
545 : }
546 : /*parse header*/
547 1125 : gf_bs_read_int(ctx->bs, 22);
548 1125 : gf_bs_read_int(ctx->bs, 8);
549 : /*spare+0+split_screen_indicator+document_camera_indicator+freeze_picture_release*/
550 1125 : gf_bs_read_int(ctx->bs, 5);
551 :
552 1125 : fmt = gf_bs_read_int(ctx->bs, 3);
553 : h263_get_pic_size(ctx->bs, fmt, &w, &h);
554 :
555 1125 : h263dmx_check_pid(filter, ctx, w, h);
556 :
557 1125 : if (!ctx->is_playing) {
558 18 : ctx->resume_from = (u32) ( (char *)start - (char *)data );
559 18 : return GF_OK;
560 : }
561 :
562 1107 : if (ctx->in_seek) {
563 3 : u64 nb_frames_at_seek = (u64) (ctx->start_range * ctx->fps.num);
564 3 : if (ctx->cts + ctx->fps.den >= nb_frames_at_seek) {
565 : //u32 samples_to_discard = (ctx->cts + ctx->dts_inc) - nb_samples_at_seek;
566 3 : ctx->in_seek = GF_FALSE;
567 : }
568 : }
569 :
570 : //good to go
571 1107 : next = h263dmx_next_start_code(start+1, remain-1);
572 :
573 1107 : if (next>0) {
574 981 : size = next+1 + ctx->bytes_in_header;
575 : full_frame = GF_TRUE;
576 : } else {
577 126 : u8 b3 = start[remain-3];
578 126 : u8 b2 = start[remain-2];
579 126 : u8 b1 = start[remain-1];
580 : //we may have a startcode here !
581 126 : if (!b1 || !b2 || !b3) {
582 5 : memcpy(ctx->hdr_store, start+remain-3, 3);
583 5 : remain -= 3;
584 5 : ctx->bytes_in_header = 3;
585 : }
586 126 : size = remain;
587 : full_frame = GF_FALSE;
588 : }
589 :
590 1107 : dst_pck = gf_filter_pck_new_alloc(ctx->opid, size, &pck_data);
591 1107 : if (!dst_pck) return GF_OUT_OF_MEM;
592 :
593 1107 : if (ctx->src_pck) gf_filter_pck_merge_properties(ctx->src_pck, dst_pck);
594 1107 : if (ctx->bytes_in_header && current) {
595 1 : memcpy(pck_data, ctx->hdr_store+current, ctx->bytes_in_header);
596 1 : size -= ctx->bytes_in_header;
597 1 : ctx->bytes_in_header = 0;
598 1 : if (byte_offset != GF_FILTER_NO_BO) {
599 1 : gf_filter_pck_set_byte_offset(dst_pck, byte_offset + ctx->bytes_in_header);
600 : }
601 1 : memcpy(pck_data, start, size);
602 : } else {
603 1106 : memcpy(pck_data, start, size);
604 1106 : if (byte_offset != GF_FILTER_NO_BO) {
605 1106 : gf_filter_pck_set_byte_offset(dst_pck, byte_offset + start - (u8 *) data);
606 : }
607 : }
608 :
609 1107 : gf_filter_pck_set_framing(dst_pck, GF_TRUE, full_frame);
610 1107 : gf_filter_pck_set_cts(dst_pck, ctx->cts);
611 1107 : gf_filter_pck_set_sap(dst_pck, (start[4]&0x02) ? GF_FILTER_SAP_NONE : GF_FILTER_SAP_1);
612 1107 : gf_filter_pck_set_duration(dst_pck, ctx->fps.den);
613 1107 : if (ctx->in_seek) gf_filter_pck_set_seek_flag(dst_pck, GF_TRUE);
614 1107 : gf_filter_pck_send(dst_pck);
615 :
616 : first_frame_found = GF_TRUE;
617 1107 : start += size;
618 1107 : remain -= size;
619 1107 : if (!full_frame) break;
620 : h263dmx_update_cts(ctx);
621 :
622 :
623 : //don't demux too much of input, abort when we would block. This avoid dispatching
624 : //a huge number of frames in a single call
625 981 : if (gf_filter_pid_would_block(ctx->opid)) {
626 974 : ctx->resume_from = (u32) ( (char *)start - (char *)data);
627 974 : return GF_OK;
628 : }
629 : }
630 127 : gf_filter_pid_drop_packet(ctx->ipid);
631 :
632 127 : return GF_OK;
633 : }
634 :
635 7 : static void h263dmx_finalize(GF_Filter *filter)
636 : {
637 7 : GF_H263DmxCtx *ctx = gf_filter_get_udta(filter);
638 7 : if (ctx->bs) gf_bs_del(ctx->bs);
639 7 : if (ctx->indexes) gf_free(ctx->indexes);
640 7 : }
641 :
642 3072 : static const char * h263dmx_probe_data(const u8 *data, u32 size, GF_FilterProbeScore *score)
643 : {
644 : u32 nb_frames=0;
645 : u32 max_nb_frames=0;
646 : u32 prev_fmt=0;
647 : u32 nb_fail=0;
648 : s32 current = h263dmx_next_start_code((u8*)data, size);
649 3212 : while (size && (current>=0) && (current< (s32) size)) {
650 : u32 fmt=0;
651 672 : data += current;
652 672 : size -= current;
653 :
654 672 : GF_BitStream *bs = gf_bs_new((u8 *)data, size, GF_BITSTREAM_READ);
655 :
656 : /*parse header*/
657 672 : gf_bs_read_int(bs, 22);
658 672 : gf_bs_read_int(bs, 8);
659 672 : gf_bs_read_int(bs, 5);
660 :
661 672 : fmt = gf_bs_read_int(bs, 3);
662 672 : gf_bs_del(bs);
663 :
664 672 : if (fmt>=1 && (fmt<=5)) {
665 168 : if (!prev_fmt || (prev_fmt==fmt)) {
666 156 : nb_frames++;
667 : } else {
668 12 : if (nb_frames>max_nb_frames) {
669 : max_nb_frames = nb_frames;
670 : }
671 : }
672 : prev_fmt=fmt;
673 : } else {
674 : nb_frames=0;
675 : nb_fail++;
676 : break;
677 : }
678 168 : current = h263dmx_next_start_code((u8*)data+1, size-1);
679 168 : if (current<=0) break;
680 140 : current++;
681 140 : if ((s32) size < current) break;
682 : }
683 3072 : if (nb_fail) return NULL;
684 2568 : if (nb_frames>max_nb_frames) {
685 : max_nb_frames = nb_frames;
686 : }
687 :
688 2568 : if (max_nb_frames>1) {
689 : // *score = max_nb_frames>4 ? GF_FPROBE_SUPPORTED : GF_FPROBE_MAYBE_SUPPORTED;
690 : //above fails the following test:
691 : //gpac -mem-track -mem-track-stack -blacklist=nvdec -i results/temp/hevc-split-merge/high_832x0.hvc @ vout
692 : //returning a score GF_FPROBE_SUPPORTED conflicting with naludmx_probe_data which also returns GF_FPROBE_SUPPORTED
693 : //TODO Change the following code line in order that only naludmx_probe_data is GF_FPROBE_SUPPORTED:
694 : //Tips: "nb_frames of naludmx is larger than the one of h263" may be considered.
695 10 : *score = GF_FPROBE_MAYBE_SUPPORTED;
696 10 : return "video/h263";
697 : }
698 : return NULL;
699 : }
700 :
701 : static const GF_FilterCapability H263DmxCaps[] =
702 : {
703 : CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
704 : CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_FILE_EXT, "263|h263|s263"),
705 : CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_MIME, "video/h263"),
706 : CAP_UINT(GF_CAPS_OUTPUT_STATIC, GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
707 : CAP_UINT(GF_CAPS_OUTPUT_STATIC, GF_PROP_PID_CODECID, GF_CODECID_S263),
708 : CAP_UINT(GF_CAPS_OUTPUT_STATIC, GF_PROP_PID_CODECID, GF_CODECID_H263),
709 : CAP_BOOL(GF_CAPS_OUTPUT_STATIC_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
710 : {0},
711 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
712 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_S263),
713 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_H263),
714 : CAP_BOOL(GF_CAPS_INPUT,GF_PROP_PID_UNFRAMED, GF_TRUE),
715 : };
716 :
717 : #define OFFS(_n) #_n, offsetof(GF_H263DmxCtx, _n)
718 : static const GF_FilterArgs H263DmxArgs[] =
719 : {
720 : { OFFS(fps), "import frame rate", GF_PROP_FRACTION, "15000/1000", NULL, 0},
721 : { OFFS(index), "indexing window length", GF_PROP_DOUBLE, "1.0", NULL, 0},
722 : {0}
723 : };
724 :
725 :
726 : GF_FilterRegister H263DmxRegister = {
727 : .name = "rfh263",
728 : GF_FS_SET_DESCRIPTION("H263 reframer")
729 : GF_FS_SET_HELP("This filter parses H263 files/data and outputs corresponding visual PID and frames.")
730 : .private_size = sizeof(GF_H263DmxCtx),
731 : .args = H263DmxArgs,
732 : .finalize = h263dmx_finalize,
733 : SETCAPS(H263DmxCaps),
734 : .configure_pid = h263dmx_configure_pid,
735 : .process = h263dmx_process,
736 : .probe_data = h263dmx_probe_data,
737 : .process_event = h263dmx_process_event
738 : };
739 :
740 :
741 2877 : const GF_FilterRegister *h263dmx_register(GF_FilterSession *session)
742 : {
743 2877 : return &H263DmxRegister;
744 : }
|