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