LCOV - code coverage report
Current view: top level - filters - enc_png.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 128 171 74.9 %
Date: 2021-04-29 23:48:07 Functions: 9 9 100.0 %

          Line data    Source code
       1             : /*
       2             :  *                      GPAC - Multimedia Framework C SDK
       3             :  *
       4             :  *                      Authors: Jean Le Feuvre
       5             :  *                      Copyright (c) Telecom ParisTech 2018-2021
       6             :  *                                      All rights reserved
       7             :  *
       8             :  *  This file is part of GPAC / libpng encoder 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             : #ifdef GPAC_HAS_PNG
      31             : 
      32             : #include <png.h>
      33             : 
      34             : typedef struct
      35             : {
      36             :         //opts
      37             :         u32 dctmode;
      38             :         u32 quality;
      39             : 
      40             :         GF_FilterPid *ipid, *opid;
      41             :         u32 width, height, pixel_format, stride, stride_uv, nb_planes, uv_height;
      42             :         u32 nb_alloc_rows;
      43             : 
      44             :         u32 max_size, pos, alloc_size;
      45             :         u32 png_type;
      46             :         png_bytep *row_pointers;
      47             : 
      48             :         GF_FilterPacket *dst_pck;
      49             :         u8 *output;
      50             : 
      51             : } GF_PNGEncCtx;
      52             : 
      53          17 : static GF_Err pngenc_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
      54             : {
      55             :         const GF_PropertyValue *prop;
      56          17 :         GF_PNGEncCtx *ctx = (GF_PNGEncCtx *) gf_filter_get_udta(filter);
      57             : 
      58             :         //disconnect of src pid (not yet supported)
      59          17 :         if (is_remove) {
      60             :                 //one in one out, this is simple
      61           0 :                 if (ctx->opid) {
      62           0 :                         gf_filter_pid_remove(ctx->opid);
      63           0 :                         ctx->opid = NULL;
      64             :                 }
      65           0 :                 ctx->ipid = NULL;
      66           0 :                 return GF_OK;
      67             :         }
      68          17 :         if (! gf_filter_pid_check_caps(pid))
      69             :                 return GF_NOT_SUPPORTED;
      70             : 
      71          17 :         ctx->ipid = pid;
      72          17 :         if (!ctx->opid) {
      73           9 :                 ctx->opid = gf_filter_pid_new(filter);
      74             :         }
      75             :         //copy properties at init or reconfig
      76          17 :         gf_filter_pid_copy_properties(ctx->opid, ctx->ipid);
      77          17 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CODECID, & PROP_UINT( GF_CODECID_PNG ));
      78          17 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STRIDE, NULL);
      79          17 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STRIDE_UV, NULL);
      80          17 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DECODER_CONFIG, NULL);
      81          17 :         gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DECODER_CONFIG_ENHANCEMENT, NULL);
      82             : 
      83          17 :         gf_filter_set_name(filter, "encpng:"PNG_LIBPNG_VER_STRING );
      84             :         //not yeat ready
      85          17 :         prop = gf_filter_pid_get_property(pid, GF_PROP_PID_WIDTH);
      86          17 :         if (!prop) return GF_OK;
      87          17 :         ctx->width = prop->value.uint;
      88             : 
      89          17 :         prop = gf_filter_pid_get_property(pid, GF_PROP_PID_HEIGHT);
      90          17 :         if (!prop) return GF_OK;
      91          17 :         ctx->height = prop->value.uint;
      92             : 
      93          17 :         prop = gf_filter_pid_get_property(pid, GF_PROP_PID_PIXFMT);
      94          17 :         if (!prop) return GF_OK;
      95          15 :         ctx->pixel_format = prop->value.uint;
      96             : 
      97          15 :         prop = gf_filter_pid_get_property(pid, GF_PROP_PID_STRIDE);
      98          15 :         if (prop) ctx->stride = prop->value.uint;
      99             : 
     100          15 :         prop = gf_filter_pid_get_property(pid, GF_PROP_PID_STRIDE_UV);
     101          15 :         if (prop) ctx->stride_uv = prop->value.uint;
     102             : 
     103          15 :         gf_pixel_get_size_info(ctx->pixel_format, ctx->width, ctx->height, NULL, &ctx->stride, &ctx->stride_uv, &ctx->nb_planes, &ctx->uv_height);
     104             : 
     105          15 :         switch (ctx->pixel_format) {
     106           0 :         case GF_PIXEL_GREYSCALE:
     107           0 :                 ctx->png_type = PNG_COLOR_TYPE_GRAY;
     108           0 :                 break;
     109           0 :         case GF_PIXEL_GREYALPHA:
     110           0 :                 ctx->png_type = PNG_COLOR_TYPE_GRAY_ALPHA;
     111           0 :                 break;
     112           3 :         case GF_PIXEL_RGB:
     113             :         case GF_PIXEL_BGR:
     114             :         case GF_PIXEL_RGBX:
     115             :         case GF_PIXEL_XRGB:
     116             :         case GF_PIXEL_BGRX:
     117             :         case GF_PIXEL_XBGR:
     118           3 :                 ctx->png_type = PNG_COLOR_TYPE_RGB;
     119           3 :                 break;
     120          12 :         case GF_PIXEL_RGBA:
     121          12 :                 ctx->png_type = PNG_COLOR_TYPE_RGB_ALPHA;
     122          12 :                 break;
     123           0 :         default:
     124           0 :                 gf_filter_pid_negociate_property(pid, GF_PROP_PID_PIXFMT, &PROP_UINT(GF_PIXEL_RGB));
     125           0 :                 break;
     126             :         }
     127          15 :         if (ctx->height > ctx->nb_alloc_rows) {
     128           9 :                 ctx->nb_alloc_rows = ctx->height;
     129           9 :                 ctx->row_pointers = gf_realloc(ctx->row_pointers, sizeof(png_bytep) * ctx->height);
     130             :         }
     131             :         return GF_OK;
     132             : }
     133             : 
     134           9 : static void pngenc_finalize(GF_Filter *filter)
     135             : {
     136           9 :         GF_PNGEncCtx *ctx = (GF_PNGEncCtx *) gf_filter_get_udta(filter);
     137           9 :         if (ctx->row_pointers) gf_free(ctx->row_pointers);
     138           9 : }
     139             : 
     140             : #define PNG_BLOCK_SIZE  4096
     141             : 
     142         294 : static void pngenc_write(png_structp png, png_bytep data, png_size_t size)
     143             : {
     144         294 :         GF_PNGEncCtx *ctx = (GF_PNGEncCtx *)png_get_io_ptr(png);
     145         294 :         if (!ctx->dst_pck) {
     146           9 :                 while (ctx->alloc_size<size) ctx->alloc_size+=PNG_BLOCK_SIZE;
     147           9 :                 ctx->dst_pck = gf_filter_pck_new_alloc(ctx->opid, ctx->alloc_size, &ctx->output);
     148           9 :                 if (!ctx->dst_pck) return;
     149         285 :         } else if (ctx->pos + size > ctx->alloc_size) {
     150             :                 u8 *new_data;
     151             :                 u32 new_size;
     152             :                 u32 old_size = ctx->alloc_size;
     153         196 :                 while (ctx->pos + size > ctx->alloc_size)
     154         129 :                         ctx->alloc_size += PNG_BLOCK_SIZE;
     155             :                 
     156          67 :                 if (gf_filter_pck_expand(ctx->dst_pck, ctx->alloc_size - old_size, &ctx->output, &new_data, &new_size) != GF_OK) {
     157           0 :                         return;
     158             :                 }
     159             :         }
     160             : 
     161         294 :         memcpy(ctx->output + ctx->pos, data, sizeof(char)*size);
     162         294 :         ctx->pos += (u32) size;
     163             : }
     164             : 
     165           9 : void pngenc_flush(png_structp png)
     166             : {
     167           9 :         if (!png) {
     168           9 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[PNGEnc] coverage test\n"));
     169             :         }
     170           9 : }
     171             : 
     172           9 : static void pngenc_error(png_structp cbk, png_const_charp msg)
     173             : {
     174           9 :         if (msg) {
     175           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[PNGEnc] Error %s", msg));
     176             :         } else {
     177           9 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[PNGEnc] coverage test\n"));
     178             :         }
     179           9 : }
     180           9 : static void pngenc_warn(png_structp cbk, png_const_charp msg)
     181             : {
     182           9 :         if (msg) {
     183           0 :                 GF_LOG(GF_LOG_WARNING, GF_LOG_CODEC, ("[PNGEnc] Warning %s", msg));
     184             :         } else {
     185           9 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[PNGEnc] coverage test\n"));
     186             :         }
     187           9 : }
     188             : 
     189          18 : static GF_Err pngenc_process(GF_Filter *filter)
     190             : {
     191             :         GF_FilterPacket *pck;
     192          18 :         GF_PNGEncCtx *ctx = (GF_PNGEncCtx *) gf_filter_get_udta(filter);
     193             :         png_color_8 sig_bit;
     194             :         u32 k;
     195          18 :         GF_Err e = GF_OK;
     196             :         png_structp png_ptr;
     197             :         png_infop info_ptr;
     198             :         char *in_data;
     199             :         u32 size, stride;
     200             : 
     201          18 :         pck = gf_filter_pid_get_packet(ctx->ipid);
     202          18 :         if (!pck) {
     203           9 :                 if (gf_filter_pid_is_eos(ctx->ipid)) {
     204           9 :                         gf_filter_pid_set_eos(ctx->opid);
     205             :                         return GF_EOS;
     206             :                 }
     207             :                 return GF_OK;
     208             :         }
     209           9 :         stride = ctx->stride;
     210           9 :         in_data = (char *) gf_filter_pck_get_data(pck, &size);
     211           9 :         if (!in_data) {
     212           2 :                 GF_FilterFrameInterface *frame_ifce = gf_filter_pck_get_frame_interface(pck);
     213           2 :                 if (!frame_ifce || !frame_ifce->get_plane) {
     214           0 :                         gf_filter_pid_drop_packet(ctx->ipid);
     215             :                         return GF_NOT_SUPPORTED;
     216             :                 }
     217           2 :                 e = frame_ifce->get_plane(frame_ifce, 0, (const u8 **) &in_data, &stride);
     218           2 :                 if (e) {
     219           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[PNGEnc] Failed to fetch first plane in hardware frame\n"));
     220           0 :                         gf_filter_pid_drop_packet(ctx->ipid);
     221             :                         return GF_NOT_SUPPORTED;
     222             :                 }
     223             :         }
     224             : 
     225           9 :         png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, ctx, pngenc_error, pngenc_warn);
     226             : 
     227           9 :         if (png_ptr == NULL) {
     228           0 :                 gf_filter_pid_drop_packet(ctx->ipid);
     229             :                 return GF_IO_ERR;
     230             :         }
     231             : 
     232             :         /* Allocate/initialize the image information data.  REQUIRED */
     233           9 :         info_ptr = png_create_info_struct(png_ptr);
     234           9 :         if (info_ptr == NULL) {
     235           0 :                 png_destroy_write_struct(&png_ptr, NULL);
     236           0 :                 gf_filter_pid_drop_packet(ctx->ipid);
     237             :                 return GF_IO_ERR;
     238             :         }
     239             : 
     240             :         /* Set error handling.  REQUIRED if you aren't supplying your own
     241             :         * error handling functions in the png_create_write_struct() call.
     242             :         */
     243           9 :         if (setjmp(png_jmpbuf(png_ptr))) {
     244             :                 e = GF_NON_COMPLIANT_BITSTREAM;
     245             :                 goto exit;
     246             :         }
     247             : 
     248           9 :         ctx->output = NULL;
     249           9 :         ctx->pos = 0;
     250           9 :         if (ctx->max_size) {
     251           0 :                 ctx->dst_pck = gf_filter_pck_new_alloc(ctx->opid, ctx->max_size, &ctx->output);
     252           0 :                 if (!ctx->dst_pck) {
     253             :                         e = GF_OUT_OF_MEM;
     254             :                         goto exit;
     255             :                 }
     256           0 :                 ctx->alloc_size = ctx->max_size;
     257             :         }
     258           9 :         png_set_write_fn(png_ptr, ctx, pngenc_write, pngenc_flush);
     259             : 
     260           9 :         png_set_IHDR(png_ptr, info_ptr, ctx->width, ctx->height, 8, ctx->png_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
     261             : 
     262             :         memset(&sig_bit, 0, sizeof(sig_bit));
     263           9 :         switch (ctx->png_type) {
     264           0 :         case PNG_COLOR_TYPE_GRAY:
     265           0 :                 sig_bit.gray = 8;
     266           0 :                 break;
     267           0 :         case PNG_COLOR_TYPE_GRAY_ALPHA:
     268           0 :                 sig_bit.gray = 8;
     269           0 :                 sig_bit.alpha = 8;
     270           0 :                 break;
     271           6 :         case PNG_COLOR_TYPE_RGB_ALPHA:
     272           6 :                 sig_bit.alpha = 8;
     273           9 :         case PNG_COLOR_TYPE_RGB:
     274           9 :                 sig_bit.red = 8;
     275           9 :                 sig_bit.green = 8;
     276           9 :                 sig_bit.blue = 8;
     277           9 :                 break;
     278             :         default:
     279             :                 break;
     280             :         }
     281           9 :         png_set_sBIT(png_ptr, info_ptr, &sig_bit);
     282             : 
     283             :         //todo add support for tags
     284             : #if 0
     285             :         {
     286             :         png_text text_ptr[3];
     287             :         /* Optionally write comments into the image */
     288             :         text_ptr[0].key = "Title";
     289             :         text_ptr[0].text = "Mona Lisa";
     290             :         text_ptr[0].compression = PNG_TEXT_COMPRESSION_NONE;
     291             :         text_ptr[1].key = "Author";
     292             :         text_ptr[1].text = "Leonardo DaVinci";
     293             :         text_ptr[1].compression = PNG_TEXT_COMPRESSION_NONE;
     294             :         text_ptr[2].key = "Description";
     295             :         text_ptr[2].text = "<long text>";
     296             :         text_ptr[2].compression = PNG_TEXT_COMPRESSION_zTXt;
     297             :         png_set_text(png_ptr, info_ptr, text_ptr, 3);
     298             :         }
     299             : #endif
     300             : 
     301           9 :         png_write_info(png_ptr, info_ptr);
     302             : 
     303             :         /* Shift the pixels up to a legal bit depth and fill in
     304             :         * as appropriate to correctly scale the image.
     305             :         */
     306           9 :         png_set_shift(png_ptr, &sig_bit);
     307             : 
     308             :         /* pack pixels into bytes */
     309           9 :         png_set_packing(png_ptr);
     310             : 
     311           9 :         switch (ctx->pixel_format) {
     312           0 :         case GF_PIXEL_ARGB:
     313           0 :                 png_set_bgr(png_ptr);
     314             :                 break;
     315           0 :         case GF_PIXEL_RGBX:
     316           0 :                 png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
     317           0 :                 png_set_bgr(png_ptr);
     318             :                 break;
     319           0 :         case GF_PIXEL_BGRX:
     320           0 :                 png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
     321             :                 break;
     322           0 :         case GF_PIXEL_BGR:
     323           0 :                 png_set_bgr(png_ptr);
     324             :                 break;
     325             :         }
     326        3328 :         for (k=0; k<ctx->height; k++) {
     327        3328 :                 ctx->row_pointers[k] = (png_bytep) in_data + k*stride;
     328             :         }
     329             : 
     330           9 :         png_write_image(png_ptr, ctx->row_pointers);
     331           9 :         png_write_end(png_ptr, info_ptr);
     332             : 
     333           9 : exit:
     334             :         /* clean up after the write, and free any memory allocated */
     335           9 :         png_destroy_write_struct(&png_ptr, &info_ptr);
     336           9 :         if (ctx->dst_pck) {
     337           9 :                 if (!e) {
     338           9 :                         gf_filter_pck_truncate(ctx->dst_pck, ctx->pos);
     339           9 :                         gf_filter_pck_merge_properties(pck, ctx->dst_pck);
     340           9 :                         gf_filter_pck_send(ctx->dst_pck);
     341             :                 } else {
     342           0 :                         gf_filter_pck_discard(ctx->dst_pck);
     343             :                 }
     344             :         }
     345           9 :         if (ctx->max_size<ctx->pos)
     346           9 :                 ctx->max_size = ctx->pos;
     347             : 
     348           9 :         ctx->dst_pck = NULL;
     349           9 :         ctx->output = NULL;
     350           9 :         ctx->pos = ctx->alloc_size = 0;
     351           9 :         gf_filter_pid_drop_packet(ctx->ipid);
     352             :         return GF_OK;
     353             : }
     354             : 
     355           9 : static GF_Err pngenc_initialize(GF_Filter *filter)
     356             : {
     357             : #ifdef GPAC_ENABLE_COVERAGE
     358           9 :         if (gf_sys_is_cov_mode()) {
     359           9 :                 pngenc_flush(NULL);
     360           9 :                 pngenc_error(NULL, NULL);
     361           9 :                 pngenc_warn(NULL, NULL);
     362             :         }
     363             : #endif
     364           9 :         return GF_OK;
     365             : }
     366             : 
     367             : static const GF_FilterCapability PNGEncCaps[] =
     368             : {
     369             :         CAP_UINT(GF_CAPS_INPUT_OUTPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
     370             :         CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_CODECID, GF_CODECID_RAW),
     371             :         CAP_UINT(GF_CAPS_OUTPUT,GF_PROP_PID_CODECID, GF_CODECID_PNG)
     372             : };
     373             : 
     374             : GF_FilterRegister PNGEncRegister = {
     375             :         .name = "pngenc",
     376             :         GF_FS_SET_DESCRIPTION("PNG encoder")
     377             :         GF_FS_SET_HELP("This filter encodes a single uncompressed video PID to PNG using libpng.")
     378             :         .private_size = sizeof(GF_PNGEncCtx),
     379             :         .initialize = pngenc_initialize,
     380             :         .finalize = pngenc_finalize,
     381             :         SETCAPS(PNGEncCaps),
     382             :         .configure_pid = pngenc_configure_pid,
     383             :         .process = pngenc_process,
     384             : };
     385             : 
     386             : #endif
     387             : 
     388        2877 : const GF_FilterRegister *pngenc_register(GF_FilterSession *session)
     389             : {
     390             : #ifdef GPAC_HAS_PNG
     391        2877 :         return &PNGEncRegister;
     392             : #else
     393             :         return NULL;
     394             : #endif
     395             : }

Generated by: LCOV version 1.13