LCOV - code coverage report
Current view: top level - filters - reframe_img.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 119 199 59.8 %
Date: 2021-04-29 23:48:07 Functions: 5 5 100.0 %

          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             : 

Generated by: LCOV version 1.13