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

          Line data    Source code
       1             : /*
       2             :  *                                      GPAC Multimedia Framework
       3             :  *
       4             :  *                      Authors: Jean Le Feuvre
       5             :  *                      Copyright (c) Telecom ParisTech 2005-2017
       6             :  *                                      All rights reserved
       7             :  *
       8             :  *  This file is part of GPAC / SVG loader 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             : 
      27             : #include <gpac/internal/compositor_dev.h>
      28             : #include <gpac/scene_manager.h>
      29             : #include <gpac/constants.h>
      30             : 
      31             : #if !defined(GPAC_DISABLE_SVG) && !defined(GPAC_DISABLE_ZLIB)
      32             : 
      33             : #include <zlib.h>
      34             : 
      35             : typedef struct
      36             : {
      37             :         //opts
      38             :         u32 sax_dur;
      39             : 
      40             :         //internal
      41             :         GF_FilterPid *in_pid, *out_pid;
      42             :         GF_SceneLoader loader;
      43             :         GF_Scene *scene;
      44             :         u32 codecid;
      45             :         const char *file_name;
      46             :         u32 file_size;
      47             :         u16 base_es_id;
      48             :         u32 file_pos;
      49             :         void *src;
      50             :         Bool is_playing;
      51             : } SVGIn;
      52             : 
      53             : 
      54             : #define SVG_PROGRESSIVE_BUFFER_SIZE             4096
      55             : 
      56             : //static
      57           1 : GF_Err svgin_deflate(SVGIn *svgin, const char *buffer, u32 buffer_len, Bool is_dims)
      58             : {
      59             :         GF_Err e;
      60             :         char svg_data[2049];
      61             :         int err;
      62             :         u32 done = 0;
      63             :         z_stream d_stream;
      64             :         memset(&d_stream, 0, sizeof(z_stream));
      65           1 :         d_stream.next_in  = (Bytef*)buffer;
      66           1 :         d_stream.avail_in = buffer_len;
      67           1 :         d_stream.next_out = (Bytef*)svg_data;
      68           1 :         d_stream.avail_out = 2048;
      69             : 
      70           1 :         if (is_dims==1) {
      71           1 :                 err = inflateInit2(&d_stream, 16+MAX_WBITS);
      72             :         } else {
      73           0 :                 err = inflateInit(&d_stream);
      74             :         }
      75             : 
      76           1 :         if (err == Z_OK) {
      77             :                 e = GF_OK;
      78           1 :                 while (d_stream.total_in < buffer_len) {
      79           1 :                         err = inflate(&d_stream, Z_NO_FLUSH);
      80           1 :                         if (err < Z_OK) {
      81             :                                 e = GF_NON_COMPLIANT_BITSTREAM;
      82             :                                 break;
      83             :                         }
      84           1 :                         svg_data[d_stream.total_out - done] = 0;
      85           1 :                         e = gf_sm_load_string(&svgin->loader, svg_data, GF_FALSE);
      86           1 :                         if (e || (err== Z_STREAM_END)) break;
      87           0 :                         done = (u32) d_stream.total_out;
      88           0 :                         d_stream.avail_out = 2048;
      89           0 :                         d_stream.next_out = (Bytef*)svg_data;
      90             :                 }
      91           1 :                 inflateEnd(&d_stream);
      92           1 :                 return e;
      93             :         }
      94             :         return GF_NON_COMPLIANT_BITSTREAM;
      95             : }
      96             : 
      97         156 : static GF_Err svgin_process(GF_Filter *filter)
      98             : {
      99             :         Bool start, end;
     100             :         const u8 *data;
     101             :         u32 size;
     102             :         GF_Err e = GF_OK;
     103             :         GF_FilterPacket *pck;
     104         156 :         SVGIn *svgin = (SVGIn *) gf_filter_get_udta(filter);
     105             : 
     106             :         //no scene yet attached
     107         156 :         if (!svgin->scene) {
     108           0 :                 if (svgin->is_playing) {
     109           0 :                         gf_filter_pid_set_eos(svgin->out_pid);
     110           0 :                         return GF_EOS;
     111             :                 }
     112             :                 return GF_OK;
     113             :         }
     114             : 
     115             : #ifdef FILTER_FIXME
     116             :         if (stream_time==(u32)-1) {
     117             :                 if (svgin->src) gf_gzclose(svgin->src);
     118             :                 svgin->src = NULL;
     119             :                 gf_sm_load_done(&svgin->loader);
     120             :                 svgin->loader.fileName = NULL;
     121             :                 svgin->file_pos = 0;
     122             :                 gf_sg_reset(svgin->scene->graph);
     123             :                 return GF_OK;
     124             :         }
     125             : #endif
     126             : 
     127         156 :         switch (svgin->codecid) {
     128             :         /*! streaming SVG*/
     129           0 :         case GF_CODECID_SVG:
     130           0 :                 pck = gf_filter_pid_get_packet(svgin->in_pid);
     131           0 :                 if (!pck) {
     132           0 :                         if (gf_filter_pid_is_eos(svgin->in_pid)) {
     133           0 :                                 gf_filter_pid_set_eos(svgin->out_pid);
     134           0 :                                 return GF_EOS;
     135             :                         }
     136             :                         return GF_OK;
     137             :                 }
     138           0 :                 data = gf_filter_pck_get_data(pck, &size);
     139           0 :                 e = gf_sm_load_string(&svgin->loader, data, GF_FALSE);
     140           0 :                 gf_filter_pid_drop_packet(svgin->in_pid);
     141           0 :                 break;
     142             : 
     143             :         /*! streaming SVG + gz*/
     144           0 :         case GF_CODECID_SVG_GZ:
     145           0 :                 pck = gf_filter_pid_get_packet(svgin->in_pid);
     146           0 :                 if (!pck) {
     147           0 :                         if (gf_filter_pid_is_eos(svgin->in_pid)) {
     148           0 :                                 gf_filter_pid_set_eos(svgin->out_pid);
     149           0 :                                 return GF_EOS;
     150             :                         }
     151             :                         return GF_OK;
     152             :                 }
     153           0 :                 data = gf_filter_pck_get_data(pck, &size);
     154           0 :                 e = svgin_deflate(svgin, data, size, GF_FALSE);
     155           0 :                 gf_filter_pid_drop_packet(svgin->in_pid);
     156           0 :                 break;
     157             : 
     158             :         /*! DIMS (dsi = 3GPP DIMS configuration)*/
     159           3 :         case GF_CODECID_DIMS:
     160             :         {
     161             :                 u8 prev, dims_hdr;
     162             :                 u32 nb_bytes;
     163             :                 u64 pos;
     164             :                 char * buf2 ;
     165             :                 GF_BitStream *bs;
     166             : 
     167           3 :                 pck = gf_filter_pid_get_packet(svgin->in_pid);
     168           3 :                 if (!pck) {
     169           2 :                         if (gf_filter_pid_is_eos(svgin->in_pid)) {
     170           1 :                                 gf_filter_pid_set_eos(svgin->out_pid);
     171           1 :                                 return GF_EOS;
     172             :                         }
     173             :                         return GF_OK;
     174             :                 }
     175           1 :                 data = gf_filter_pck_get_data(pck, &size);
     176             : 
     177           1 :                 buf2 = gf_malloc(size+1);
     178           1 :                 bs = gf_bs_new((u8 *)data, size, GF_BITSTREAM_READ);
     179           1 :                 memcpy(buf2, data, size);
     180           1 :                 buf2[size] = 0;
     181             : 
     182           1 :                 gf_filter_pid_drop_packet(svgin->in_pid);
     183             :                 e = GF_OK;
     184           3 :                 while (gf_bs_available(bs)) {
     185           1 :                         pos = gf_bs_get_position(bs);
     186           1 :                         size = gf_bs_read_u16(bs);
     187             :                         nb_bytes = 2;
     188             :                         /*GPAC internal hack*/
     189           1 :                         if (!size) {
     190           0 :                                 size = gf_bs_read_u32(bs);
     191             :                                 nb_bytes = 6;
     192             :                         }
     193             : 
     194           1 :                         dims_hdr = gf_bs_read_u8(bs);
     195           1 :                         prev = buf2[pos + nb_bytes + size];
     196             : 
     197           1 :                         buf2[pos + nb_bytes + size] = 0;
     198           1 :                         if (dims_hdr & GF_DIMS_UNIT_C) {
     199           1 :                                 e = svgin_deflate(svgin, buf2 + pos + nb_bytes + 1, size - 1, GF_TRUE);
     200             :                         } else {
     201           0 :                                 e = gf_sm_load_string(&svgin->loader, buf2 + pos + nb_bytes + 1, GF_FALSE);
     202             :                         }
     203           1 :                         buf2[pos + nb_bytes + size] = prev;
     204           1 :                         gf_bs_skip_bytes(bs, size-1);
     205           1 :                         if (e) break;
     206             : 
     207             :                 }
     208           1 :                 gf_bs_del(bs);
     209           1 :                 gf_free(buf2);
     210             :                 
     211             :                 if (e) goto exit;
     212             :         }
     213             :         break;
     214             : 
     215         153 :         default:
     216         153 :                 if (!svgin->file_name) return GF_SERVICE_ERROR;
     217             : 
     218         153 :                 pck = gf_filter_pid_get_packet(svgin->in_pid);
     219         153 :                 if (!pck) return GF_OK;
     220          97 :                 gf_filter_pck_get_framing(pck, &start, &end);
     221          97 :                 gf_filter_pid_drop_packet( svgin->in_pid);
     222             : 
     223             :                 /*full doc parsing*/
     224          97 :                 if ((svgin->sax_dur == 0) && svgin->file_name) {
     225          97 :                         if (end) {
     226          48 :                                 svgin->loader.fileName = svgin->file_name;
     227          48 :                                 e = gf_sm_load_init(&svgin->loader);
     228          48 :                                 if (!e) e = gf_sm_load_run(&svgin->loader);
     229             :                         }
     230             :                 }
     231             :                 /*chunk parsing*/
     232             :                 else {
     233             :                         u32 entry_time;
     234             :                         char file_buf[SVG_PROGRESSIVE_BUFFER_SIZE+2];
     235             :                         /*initial load*/
     236           0 :                         if (!svgin->src && !svgin->file_pos) {
     237           0 :                                 svgin->src = gf_gzopen(svgin->file_name, "rb");
     238           0 :                                 if (!svgin->src) return GF_URL_ERROR;
     239           0 :                                 svgin->loader.fileName = svgin->file_name;
     240           0 :                                 gf_sm_load_init(&svgin->loader);
     241             :                         }
     242             :                         e = GF_OK;
     243           0 :                         entry_time = gf_sys_clock();
     244             : 
     245             :                         while (1) {
     246             :                                 u32 diff;
     247             :                                 s32 nb_read;
     248           0 :                                 nb_read = gf_gzread(svgin->src, file_buf, SVG_PROGRESSIVE_BUFFER_SIZE);
     249             :                                 /*we may have read nothing but we still need to call parse in case the parser got suspended*/
     250           0 :                                 if (nb_read<=0) {
     251           0 :                                         if ((e==GF_EOS) && gf_gzeof(svgin->src)) {
     252           0 :                                                 gf_set_progress("SVG Parsing", svgin->file_pos, svgin->file_size);
     253           0 :                                                 gf_gzclose(svgin->src);
     254           0 :                                                 svgin->src = NULL;
     255           0 :                                                 gf_sm_load_done(&svgin->loader);
     256             :                                         }
     257           0 :                                         goto exit;
     258             :                                 }
     259             : 
     260           0 :                                 file_buf[nb_read] = file_buf[nb_read+1] = 0;
     261             : 
     262           0 :                                 e = gf_sm_load_string(&svgin->loader, file_buf, GF_FALSE);
     263           0 :                                 svgin->file_pos += nb_read;
     264             : 
     265             : 
     266             : 
     267             :                                 /*handle decompression*/
     268           0 :                                 if (svgin->file_pos > svgin->file_size) svgin->file_size = svgin->file_pos + 1;
     269           0 :                                 if (e) break;
     270             : 
     271           0 :                                 gf_set_progress("SVG Parsing", svgin->file_pos, svgin->file_size);
     272           0 :                                 diff = gf_sys_clock() - entry_time;
     273           0 :                                 if (diff > svgin->sax_dur) {
     274             :                                         break;
     275             :                                 }
     276             :                         }
     277             :                 }
     278             :                 break;
     279             :         }
     280             : 
     281          50 : exit:
     282          49 :         if ((e>=GF_OK) && (svgin->scene->graph_attached!=1) && (gf_sg_get_root_node(svgin->loader.scene_graph)!=NULL) ) {
     283          48 :                 gf_scene_attach_to_compositor(svgin->scene);
     284             :         }
     285             :         /*prepare for next playback*/
     286          98 :         if (e) {
     287          48 :                 gf_sm_load_done(&svgin->loader);
     288          48 :                 svgin->loader.fileName = NULL;
     289          48 :                 gf_filter_pid_set_eos(svgin->out_pid);
     290             :         }
     291             :         return e;
     292             : }
     293             : 
     294          70 : static GF_Err svgin_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
     295             : {
     296          70 :         SVGIn *svgin = (SVGIn *) gf_filter_get_udta(filter);
     297             :         const GF_PropertyValue *prop;
     298             : 
     299          70 :         if (is_remove) {
     300          21 :                 svgin->in_pid = NULL;
     301          21 :                 return GF_OK;
     302             :         }
     303             : 
     304          49 :         if (! gf_filter_pid_check_caps(pid))
     305             :                 return GF_NOT_SUPPORTED;
     306             : 
     307          49 :         prop = gf_filter_pid_get_property(pid, GF_PROP_PID_CODECID);
     308          49 :         if (prop && prop->value.uint) svgin->codecid = prop->value.uint;
     309             : 
     310          49 :         prop = gf_filter_pid_get_property(pid, GF_PROP_PID_STREAM_TYPE);
     311          49 :         if (prop && (prop->value.uint==GF_STREAM_FILE)) {
     312             :                 //we must have a file path or codecid
     313          48 :                 prop = gf_filter_pid_get_property(pid, GF_PROP_PID_FILEPATH);
     314          48 :                 if (prop && prop->value.string) svgin->file_name = prop->value.string;
     315             :         }
     316             : 
     317          49 :         if (!svgin->codecid && !svgin->file_name)
     318             :                 return GF_NOT_SUPPORTED;
     319             : 
     320          49 :         svgin->loader.type = GF_SM_LOAD_SVG;
     321             :         /* decSpecInfo is not null only when reading from an SVG file (local or distant, cached or not) */
     322          49 :         switch (svgin->codecid) {
     323           0 :         case GF_CODECID_SVG:
     324             :         case GF_CODECID_SVG_GZ:
     325           0 :                 svgin->loader.flags |= GF_SM_LOAD_CONTEXT_STREAMING;
     326             :                 /*no decSpecInfo defined for streaming svg yet*/
     327           0 :                 break;
     328           1 :         case GF_CODECID_DIMS:
     329           1 :                 svgin->loader.type = GF_SM_LOAD_DIMS;
     330           1 :                 svgin->loader.flags |= GF_SM_LOAD_CONTEXT_STREAMING;
     331             :                 /*decSpecInfo not yet supported for DIMS svg - we need properties at the scene level to store the
     332             :                 various indications*/
     333           1 :                 break;
     334             :         default:
     335             :                 break;
     336             :         }
     337             : 
     338          49 :         if (!svgin->in_pid) {
     339             :                 GF_FilterEvent fevt;
     340          49 :                 svgin->in_pid = pid;
     341             : 
     342             :                 //we work with full file only, send a play event on source to indicate that
     343          49 :                 GF_FEVT_INIT(fevt, GF_FEVT_PLAY, pid);
     344             :                 fevt.play.start_range = 0;
     345          49 :                 fevt.base.on_pid = svgin->in_pid;
     346          49 :                 fevt.play.full_file_only = GF_TRUE;
     347          49 :                 gf_filter_pid_send_event(svgin->in_pid, &fevt);
     348             : 
     349             :         } else {
     350           0 :                 if (pid != svgin->in_pid) {
     351             :                         return GF_REQUIRES_NEW_INSTANCE;
     352             :                 }
     353             :                 //check for filename change
     354           0 :                 return GF_OK;
     355             :         }
     356             : 
     357             :         //declare a new output PID of type scene, codecid RAW
     358          49 :         svgin->out_pid = gf_filter_pid_new(filter);
     359             : 
     360          49 :         gf_filter_pid_copy_properties(svgin->out_pid, pid);
     361          49 :         gf_filter_pid_set_property(svgin->out_pid, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(GF_STREAM_SCENE) );
     362          49 :         gf_filter_pid_set_property(svgin->out_pid, GF_PROP_PID_CODECID, &PROP_UINT(GF_CODECID_RAW) );
     363          49 :         gf_filter_pid_set_property(svgin->out_pid, GF_PROP_PID_IN_IOD, &PROP_BOOL(GF_TRUE) );
     364          49 :         gf_filter_pid_set_udta(pid, svgin->out_pid);
     365             : 
     366             : 
     367          49 :         if (svgin->file_name) {
     368          48 :                 gf_filter_set_name(filter,  ((svgin->sax_dur==0) && svgin->file_size) ? "SVGLoad" : "SVGLoad:Progressive");
     369           1 :         } else if (svgin->codecid==GF_CODECID_SVG) {
     370           0 :                 gf_filter_set_name(filter,  "SVG:Streaming");
     371           1 :         } else if (svgin->codecid==GF_CODECID_SVG_GZ) {
     372           0 :                 gf_filter_set_name(filter,  "SVG:Streaming:GZ");
     373           1 :         } else if (svgin->codecid==GF_CODECID_DIMS) {
     374           1 :                 gf_filter_set_name(filter,  "SVG:DIMS");
     375             :         }
     376             : 
     377             :         return GF_OK;
     378             : }
     379             : 
     380         294 : static Bool svgin_process_event(GF_Filter *filter, const GF_FilterEvent *com)
     381             : {
     382             :         u32 count, i;
     383         294 :         SVGIn *svgin = gf_filter_get_udta(filter);
     384             :         //check for scene attach
     385         294 :         switch (com->base.type) {
     386          49 :         case GF_FEVT_PLAY:
     387             :                 //cancel play event, we work with full file
     388          49 :                 svgin->is_playing = GF_TRUE;
     389          49 :                 return GF_TRUE;
     390             :         case GF_FEVT_ATTACH_SCENE:
     391             :                 break;
     392          49 :         case GF_FEVT_RESET_SCENE:
     393          49 :                 gf_sm_load_done(&svgin->loader);
     394          49 :                 svgin->scene = NULL;
     395          49 :                 return GF_FALSE;
     396             :         default:
     397             :                 return GF_FALSE;
     398             :         }
     399          49 :         if (!com->attach_scene.on_pid) return GF_TRUE;
     400             : 
     401          49 :         count = gf_filter_get_ipid_count(filter);
     402          49 :         for (i=0; i<count; i++) {
     403          49 :                 GF_FilterPid *ipid = gf_filter_get_ipid(filter, i);
     404          49 :                 GF_FilterPid *opid = gf_filter_pid_get_udta(ipid);
     405             :                 //we found our pid, set it up
     406          49 :                 if (opid == com->attach_scene.on_pid) {
     407          49 :                         if (!svgin->scene) {
     408          49 :                                 GF_ObjectManager *odm = com->attach_scene.object_manager;
     409          49 :                                 svgin->scene = odm->subscene ? odm->subscene : odm->parentscene;
     410             : 
     411          49 :                                 memset(&svgin->loader, 0, sizeof(GF_SceneLoader));
     412          49 :                                 svgin->loader.is = svgin->scene;
     413          49 :                                 svgin->loader.scene_graph = svgin->scene->graph;
     414          49 :                                 svgin->loader.localPath = gf_get_default_cache_directory();
     415             :                                 
     416             :                                 /*Warning: svgin->loader.type may be overridden in attach stream */
     417          49 :                                 svgin->loader.type = GF_SM_LOAD_SVG;
     418          49 :                                 svgin->loader.flags = GF_SM_LOAD_FOR_PLAYBACK;
     419             : 
     420          49 :                                 if (! svgin->file_name)
     421           1 :                                         gf_sm_load_init(&svgin->loader);
     422             : 
     423          49 :                                 if (svgin->scene->root_od->ck && !svgin->scene->root_od->ck->clock_init)
     424          49 :                                         gf_clock_set_time(svgin->scene->root_od->ck, 0);
     425             : 
     426             :                                 //init clocks
     427          49 :                                 gf_odm_check_buffering(svgin->scene->root_od, svgin->in_pid);
     428             :                         }
     429             :                         return GF_TRUE;
     430             :                 }
     431             :         }
     432             : 
     433             :         return GF_FALSE;
     434             : }
     435             : 
     436             : static const GF_FilterCapability SVGInCaps[] =
     437             : {
     438             :         CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
     439             :         CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_FILE_EXT, "svg|svgz|svg.gz"),
     440             :         CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_MIME, "image/svg+xml"),
     441             :         CAP_UINT(GF_CAPS_OUTPUT_STATIC, GF_PROP_PID_STREAM_TYPE, GF_STREAM_SCENE),
     442             :         CAP_UINT(GF_CAPS_OUTPUT_STATIC, GF_PROP_PID_CODECID, GF_CODECID_RAW),
     443             :         {0},
     444             :         CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_SCENE),
     445             :         CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_SVG),
     446             :         CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_SVG_GZ),
     447             :         CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_DIMS),
     448             :         CAP_BOOL(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
     449             : };
     450             : 
     451             : 
     452             : #define OFFS(_n)        #_n, offsetof(SVGIn, _n)
     453             : 
     454             : static const GF_FilterArgs SVGInArgs[] =
     455             : {
     456             :         { OFFS(sax_dur), "loading duration for SAX parsing, 0 disables SAX parsing", GF_PROP_UINT, "0", NULL, GF_FS_ARG_HINT_ADVANCED},
     457             :         {0}
     458             : };
     459             : GF_FilterRegister SVGInRegister = {
     460             :         .name = "svgplay",
     461             :         GF_FS_SET_DESCRIPTION("SVG loader")
     462             :         GF_FS_SET_HELP("This filter parses SVG files directly into the scene graph of the compositor.")
     463             :         .private_size = sizeof(SVGIn),
     464             :         .flags = GF_FS_REG_MAIN_THREAD,
     465             :         .args = SVGInArgs,
     466             :         SETCAPS(SVGInCaps),
     467             :         .process = svgin_process,
     468             :         .configure_pid = svgin_configure_pid,
     469             :         .process_event = svgin_process_event,
     470             : };
     471             : 
     472             : #endif
     473             : 
     474             : 
     475        2877 : const GF_FilterRegister *svgin_register(GF_FilterSession *session)
     476             : {
     477             : #if !defined(GPAC_DISABLE_SVG) && !defined(GPAC_DISABLE_ZLIB)
     478        2877 :         return &SVGInRegister;
     479             : #else
     480             :         return NULL;
     481             : #endif
     482             : }

Generated by: LCOV version 1.13