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