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 / image (jpg/png/bmp/j2k) 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/filters.h>
27 : #include <gpac/constants.h>
28 : #include <gpac/avparse.h>
29 :
30 :
31 : #if defined(WIN32) || defined(_WIN32_WCE)
32 : #include <windows.h>
33 : #else
34 :
35 : #ifdef GPAC_CONFIG_LINUX
36 : #include <arpa/inet.h>
37 : #endif
38 :
39 : typedef struct tagBITMAPFILEHEADER
40 : {
41 : u16 bfType;
42 : u32 bfSize;
43 : u16 bfReserved1;
44 : u16 bfReserved2;
45 : u32 bfOffBits;
46 : } BITMAPFILEHEADER;
47 :
48 : typedef struct tagBITMAPINFOHEADER {
49 : u32 biSize;
50 : s32 biWidth;
51 : s32 biHeight;
52 : u16 biPlanes;
53 : u16 biBitCount;
54 : u32 biCompression;
55 : u32 biSizeImage;
56 : s32 biXPelsPerMeter;
57 : s32 biYPelsPerMeter;
58 : u32 biClrUsed;
59 : u32 biClrImportant;
60 : } BITMAPINFOHEADER;
61 :
62 : #define BI_RGB 0L
63 :
64 : #endif
65 :
66 : typedef struct
67 : {
68 : //options
69 : GF_Fraction fps;
70 :
71 : //only one input pid declared
72 : GF_FilterPid *ipid;
73 : //only one output pid declared
74 : GF_FilterPid *opid;
75 : u32 src_timescale;
76 : Bool is_bmp;
77 : Bool owns_timescale;
78 : u32 codec_id;
79 :
80 : Bool initial_play_done;
81 : Bool is_playing;
82 : } GF_ReframeImgCtx;
83 :
84 :
85 313 : GF_Err img_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
86 : {
87 313 : GF_ReframeImgCtx *ctx = gf_filter_get_udta(filter);
88 : const GF_PropertyValue *p;
89 :
90 313 : if (is_remove) {
91 25 : ctx->ipid = NULL;
92 25 : return GF_OK;
93 : }
94 :
95 288 : if (! gf_filter_pid_check_caps(pid))
96 : return GF_NOT_SUPPORTED;
97 :
98 288 : gf_filter_pid_set_framing_mode(pid, GF_TRUE);
99 288 : ctx->ipid = pid;
100 : //force retest of codecid
101 288 : ctx->codec_id = 0;
102 :
103 288 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_TIMESCALE);
104 288 : if (p) ctx->src_timescale = p->value.uint;
105 :
106 288 : if (ctx->src_timescale && !ctx->opid) {
107 0 : ctx->opid = gf_filter_pid_new(filter);
108 0 : gf_filter_pid_copy_properties(ctx->opid, ctx->ipid);
109 0 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_UNFRAMED, NULL);
110 : }
111 288 : ctx->is_playing = GF_TRUE;
112 288 : return GF_OK;
113 : }
114 :
115 756 : Bool img_process_event(GF_Filter *filter, const GF_FilterEvent *evt)
116 : {
117 : GF_FilterEvent fevt;
118 756 : GF_ReframeImgCtx *ctx = gf_filter_get_udta(filter);
119 756 : if (evt->base.on_pid != ctx->opid) return GF_TRUE;
120 441 : switch (evt->base.type) {
121 317 : case GF_FEVT_PLAY:
122 317 : if (ctx->is_playing) {
123 : return GF_TRUE;
124 : }
125 :
126 315 : ctx->is_playing = GF_TRUE;
127 315 : if (!ctx->initial_play_done) {
128 284 : ctx->initial_play_done = GF_TRUE;
129 284 : return GF_TRUE;
130 : }
131 :
132 31 : GF_FEVT_INIT(fevt, GF_FEVT_SOURCE_SEEK, ctx->ipid);
133 : fevt.seek.start_offset = 0;
134 31 : gf_filter_pid_send_event(ctx->ipid, &fevt);
135 31 : return GF_TRUE;
136 66 : case GF_FEVT_STOP:
137 66 : ctx->is_playing = GF_FALSE;
138 66 : return GF_FALSE;
139 : default:
140 : break;
141 : }
142 : //cancel all events
143 : return GF_TRUE;
144 : }
145 :
146 723 : GF_Err img_process(GF_Filter *filter)
147 : {
148 723 : GF_ReframeImgCtx *ctx = gf_filter_get_udta(filter);
149 : GF_FilterPacket *pck, *dst_pck;
150 : GF_Err e;
151 : u8 *data, *output;
152 723 : u32 size, w=0, h=0, pf=0;
153 : u8 *pix;
154 : u32 i, j, irow, in_stride, out_stride;
155 : GF_BitStream *bs;
156 : BITMAPFILEHEADER fh;
157 : BITMAPINFOHEADER fi;
158 :
159 723 : pck = gf_filter_pid_get_packet(ctx->ipid);
160 723 : if (!pck) {
161 408 : if (gf_filter_pid_is_eos(ctx->ipid)) {
162 318 : if (ctx->opid)
163 318 : gf_filter_pid_set_eos(ctx->opid);
164 318 : ctx->is_playing = GF_FALSE;
165 318 : return GF_EOS;
166 : }
167 : return GF_OK;
168 : }
169 315 : data = (char *) gf_filter_pck_get_data(pck, &size);
170 :
171 315 : if (!ctx->opid || !ctx->codec_id) {
172 : #ifndef GPAC_DISABLE_AV_PARSERS
173 : u32 dsi_size;
174 286 : u8 *dsi=NULL;
175 : #endif
176 : const char *ext, *mime;
177 : const GF_PropertyValue *prop;
178 286 : u32 codecid = 0;
179 :
180 286 : if ((size >= 54) && (data[0] == 'B') && (data[1] == 'M')) {
181 0 : codecid = GF_CODECID_RAW;
182 0 : ctx->is_bmp = GF_TRUE;
183 : }
184 : #ifndef GPAC_DISABLE_AV_PARSERS
185 : else {
186 286 : bs = gf_bs_new(data, size, GF_BITSTREAM_READ);
187 286 : gf_img_parse(bs, &codecid, &w, &h, &dsi, &dsi_size);
188 286 : gf_bs_del(bs);
189 : }
190 : #endif
191 :
192 286 : prop = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_FILE_EXT);
193 286 : ext = (prop && prop->value.string) ? prop->value.string : "";
194 286 : prop = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_MIME);
195 286 : mime = (prop && prop->value.string) ? prop->value.string : "";
196 :
197 286 : if (!codecid) {
198 0 : if (!stricmp(ext, "jpeg") || !stricmp(ext, "jpg") || !strcmp(mime, "image/jpg")) {
199 0 : codecid = GF_CODECID_JPEG;
200 0 : } else if (!stricmp(ext, "png") || !strcmp(mime, "image/png")) {
201 0 : codecid = GF_CODECID_PNG;
202 0 : } else if (!stricmp(ext, "jp2") || !stricmp(ext, "j2k") || !strcmp(mime, "image/jp2")) {
203 0 : codecid = GF_CODECID_J2K;
204 0 : } else if (!stricmp(ext, "pngd")) {
205 0 : codecid = GF_CODECID_PNG;
206 : pf = GF_PIXEL_RGBD;
207 0 : } else if (!stricmp(ext, "pngds")) {
208 0 : codecid = GF_CODECID_PNG;
209 : pf = GF_PIXEL_RGBDS;
210 0 : } else if (!stricmp(ext, "pngs")) {
211 0 : codecid = GF_CODECID_PNG;
212 : pf = GF_PIXEL_RGBS;
213 0 : } else if (!stricmp(ext, "bmp") || !strcmp(mime, "image/png")) {
214 0 : codecid = GF_CODECID_RAW;
215 : }
216 : }
217 286 : if (!codecid) {
218 0 : gf_filter_pid_drop_packet(ctx->ipid);
219 0 : return GF_NOT_SUPPORTED;
220 : }
221 286 : ctx->codec_id = codecid;
222 286 : ctx->opid = gf_filter_pid_new(filter);
223 286 : if (!ctx->opid) {
224 0 : gf_filter_pid_drop_packet(ctx->ipid);
225 0 : return GF_SERVICE_ERROR;
226 : }
227 286 : if (!ctx->fps.num || !ctx->fps.den) {
228 286 : ctx->fps.num = 1000;
229 286 : ctx->fps.den = 1000;
230 : }
231 : //we don't have input reconfig for now
232 286 : gf_filter_pid_copy_properties(ctx->opid, ctx->ipid);
233 286 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STREAM_TYPE, & PROP_UINT(GF_STREAM_VISUAL));
234 286 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CODECID, & PROP_UINT(codecid));
235 286 : if (pf) gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_PIXFMT, & PROP_UINT(pf));
236 286 : if (w) gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_WIDTH, & PROP_UINT(w));
237 286 : if (h) gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_HEIGHT, & PROP_UINT(h));
238 : #ifndef GPAC_DISABLE_AV_PARSERS
239 286 : if (dsi) gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DECODER_CONFIG, & PROP_DATA_NO_COPY(dsi, dsi_size));
240 : #endif
241 286 : if (! gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_TIMESCALE)) {
242 286 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_TIMESCALE, &PROP_UINT(ctx->fps.num) );
243 286 : ctx->owns_timescale = GF_TRUE;
244 : }
245 :
246 286 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_NB_FRAMES, &PROP_UINT(1) );
247 286 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_PLAYBACK_MODE, &PROP_UINT(GF_PLAYBACK_MODE_FASTFORWARD ) );
248 :
249 286 : if (ext || mime)
250 286 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CAN_DATAREF, & PROP_BOOL(GF_TRUE ) );
251 : }
252 315 : if (! ctx->is_bmp) {
253 : e = GF_OK;
254 : u32 start_offset = 0;
255 315 : if (ctx->codec_id==GF_CODECID_J2K) {
256 :
257 4 : if (size<8) {
258 0 : gf_filter_pid_drop_packet(ctx->ipid);
259 0 : return GF_NON_COMPLIANT_BITSTREAM;
260 : }
261 :
262 4 : if ((data[4]=='j') && (data[5]=='P') && (data[6]==' ') && (data[7]==' ')) {
263 4 : bs = gf_bs_new(data, size, GF_BITSTREAM_READ);
264 24 : while (gf_bs_available(bs)) {
265 20 : u32 bsize = gf_bs_read_u32(bs);
266 20 : u32 btype = gf_bs_read_u32(bs);
267 20 : if (btype == GF_4CC('j','p','2','c') ) {
268 4 : start_offset = (u32) gf_bs_get_position(bs) - 8;
269 4 : break;
270 : }
271 16 : gf_bs_skip_bytes(bs, bsize-8);
272 : }
273 4 : gf_bs_del(bs);
274 4 : if (start_offset>=size) {
275 0 : gf_filter_pid_drop_packet(ctx->ipid);
276 0 : return GF_NON_COMPLIANT_BITSTREAM;
277 : }
278 : }
279 : }
280 315 : dst_pck = gf_filter_pck_new_ref(ctx->opid, start_offset, size-start_offset, pck);
281 315 : if (!dst_pck) return GF_OUT_OF_MEM;
282 :
283 315 : gf_filter_pck_merge_properties(pck, dst_pck);
284 315 : if (ctx->owns_timescale) {
285 315 : gf_filter_pck_set_cts(dst_pck, 0);
286 315 : gf_filter_pck_set_sap(dst_pck, GF_FILTER_SAP_1 );
287 315 : gf_filter_pck_set_duration(dst_pck, ctx->fps.den);
288 : }
289 315 : gf_filter_pck_send(dst_pck);
290 315 : gf_filter_pid_drop_packet(ctx->ipid);
291 315 : return e;
292 : }
293 :
294 0 : bs = gf_bs_new(data, size, GF_BITSTREAM_READ);
295 :
296 0 : /*fh.bfType = */ gf_bs_read_u16(bs);
297 0 : /*fh.bfSize = */ gf_bs_read_u32(bs);
298 0 : /*fh.bfReserved1 = */ gf_bs_read_u16(bs);
299 0 : /*fh.bfReserved2 = */ gf_bs_read_u16(bs);
300 0 : fh.bfOffBits = gf_bs_read_u32(bs);
301 : fh.bfOffBits = ntohl(fh.bfOffBits);
302 :
303 0 : gf_bs_read_data(bs, (char *) &fi, 40);
304 0 : gf_bs_del(bs);
305 :
306 0 : if ((fi.biCompression != BI_RGB) || (fi.biPlanes!=1)) return GF_NOT_SUPPORTED;
307 0 : if ((fi.biBitCount!=24) && (fi.biBitCount!=32)) return GF_NOT_SUPPORTED;
308 :
309 0 : w = fi.biWidth;
310 0 : h = fi.biHeight;
311 0 : pf = (fi.biBitCount==24) ? GF_PIXEL_RGB : GF_PIXEL_RGBA;
312 0 : size = (fi.biBitCount==24) ? 3 : 4;
313 0 : size *= w;
314 : out_stride = size;
315 0 : size *= h;
316 :
317 0 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_PIXFMT, & PROP_UINT(pf));
318 0 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_WIDTH, & PROP_UINT(w));
319 0 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_HEIGHT, & PROP_UINT(h));
320 :
321 0 : dst_pck = gf_filter_pck_new_alloc(ctx->opid, size, &output);
322 0 : if (!dst_pck) return GF_OUT_OF_MEM;
323 :
324 0 : gf_filter_pck_merge_properties(pck, dst_pck);
325 0 : if (ctx->owns_timescale) {
326 0 : gf_filter_pck_set_cts(dst_pck, 0);
327 0 : gf_filter_pck_set_sap(dst_pck, GF_FILTER_SAP_1 );
328 0 : gf_filter_pck_set_duration(dst_pck, ctx->fps.den);
329 : }
330 :
331 : in_stride = out_stride;
332 0 : while (in_stride % 4) in_stride++;
333 :
334 0 : if (fi.biBitCount==24) {
335 0 : for (i=0; i<h; i++) {
336 0 : irow = (h-1-i)*out_stride;
337 0 : pix = data + fh.bfOffBits + i*in_stride;
338 0 : for (j=0; j<out_stride; j+=3) {
339 0 : output[j + irow] = pix[2];
340 0 : output[j+1 + irow] = pix[1];
341 0 : output[j+2 + irow] = pix[0];
342 0 : pix += 3;
343 : }
344 : }
345 : } else {
346 0 : for (i=0; i<h; i++) {
347 0 : irow = (h-1-i)*out_stride;
348 0 : pix = data + fh.bfOffBits + i*in_stride;
349 0 : for (j=0; j<out_stride; j+=4) {
350 0 : output[j + irow] = pix[2];
351 0 : output[j+1 + irow] = pix[1];
352 0 : output[j+2 + irow] = pix[0];
353 0 : output[j+3 + irow] = pix[3];
354 0 : pix += 4;
355 : }
356 : }
357 : }
358 0 : e = gf_filter_pck_send(dst_pck);
359 0 : gf_filter_pid_drop_packet(ctx->ipid);
360 0 : return e;
361 : }
362 :
363 : #include <gpac/internal/isomedia_dev.h>
364 :
365 3074 : static const char * img_probe_data(const u8 *data, u32 size, GF_FilterProbeScore *score)
366 : {
367 : /*JPEG*/
368 3074 : if ((data[0]==0xFF) && (data[1]==0xD8) && (data[2]==0xFF)) {
369 170 : *score = GF_FPROBE_SUPPORTED;
370 170 : return "image/jpg";
371 : }
372 : /*PNG*/
373 2904 : if ((data[0]==0x89) && (data[1]==0x50) && (data[2]==0x4E)) {
374 120 : *score = GF_FPROBE_SUPPORTED;
375 120 : return "image/png";
376 : }
377 2784 : GF_BitStream *bs = gf_bs_new(data, size, GF_BITSTREAM_READ);
378 2784 : u32 bsize = gf_bs_read_u32(bs);
379 2784 : u32 btype = gf_bs_read_u32(bs);
380 2784 : if ( (bsize==12) && ( (btype==GF_ISOM_BOX_TYPE_JP ) || (btype==GF_ISOM_BOX_TYPE_JP2H) ) ) {
381 4 : if (btype==GF_ISOM_BOX_TYPE_JP2H) {
382 0 : *score = GF_FPROBE_FORCE;
383 0 : gf_bs_del(bs);
384 0 : return "image/jp2";
385 : }
386 4 : btype = gf_bs_read_u32(bs);
387 4 : if (btype==0x0D0A870A) {
388 4 : *score = GF_FPROBE_FORCE;
389 4 : gf_bs_del(bs);
390 4 : return "image/jp2";
391 : }
392 : }
393 2780 : gf_bs_del(bs);
394 2780 : if ((size >= 54) && (data[0] == 'B') && (data[1] == 'M')) {
395 0 : *score = GF_FPROBE_SUPPORTED;
396 0 : return "image/bmp";
397 : }
398 : return NULL;
399 : }
400 : static const GF_FilterCapability ReframeImgCaps[] =
401 : {
402 : CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
403 : CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_FILE_EXT, "jpg|jpeg|jp2|bmp|png|pngd|pngds|pngs"),
404 : CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_MIME, "image/jpg|image/jp2|image/bmp|image/png|image/x-png+depth|image/x-png+depth+mask|image/x-png+stereo"),
405 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
406 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_PNG),
407 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_JPEG),
408 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_J2K),
409 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_RAW),
410 : // CAP_BOOL(GF_CAPS_OUTPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
411 : };
412 :
413 : #define OFFS(_n) #_n, offsetof(GF_ReframeImgCtx, _n)
414 : static const GF_FilterArgs ReframeImgArgs[] =
415 : {
416 : { OFFS(fps), "import frame rate (0 default to 1 Hz)", GF_PROP_FRACTION, "0/1000", NULL, GF_FS_ARG_HINT_HIDE},
417 : {0}
418 : };
419 :
420 : GF_FilterRegister ReframeImgRegister = {
421 : .name = "rfimg",
422 : GF_FS_SET_DESCRIPTION("JPG/J2K/PNG/BMP reframer")
423 : GF_FS_SET_HELP("This filter parses JPG/J2K/PNG/BMP files/data and outputs corresponding visual PID and frames.")
424 : .private_size = sizeof(GF_ReframeImgCtx),
425 : .args = ReframeImgArgs,
426 : SETCAPS(ReframeImgCaps),
427 : .configure_pid = img_configure_pid,
428 : .probe_data = img_probe_data,
429 : .process = img_process,
430 : .process_event = img_process_event
431 : };
432 :
433 2877 : const GF_FilterRegister *img_reframe_register(GF_FilterSession *session)
434 : {
435 2877 : return &ReframeImgRegister;
436 : }
437 :
438 :
|