gst_plugins_good/ext/libpng/gstpngenc.c
changeset 27 d43ce56a1534
parent 23 29ecd5cb86b3
child 31 aec498aab1d3
equal deleted inserted replaced
23:29ecd5cb86b3 27:d43ce56a1534
     1 /* GStreamer
       
     2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
       
     3  *
       
     4  * Filter:
       
     5  * Copyright (C) 2000 Donald A. Graft
       
     6  *
       
     7  * This library is distributed in the hope that it will be useful,
       
     8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
     9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    10  * Library General Public License for more details.
       
    11  *
       
    12  * You should have received a copy of the GNU Library General Public
       
    13  * License along with this library; if not, write to the
       
    14  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
       
    15  * Boston, MA 02111-1307, USA.
       
    16  *
       
    17  */
       
    18 /**
       
    19  * SECTION:element-pngenc
       
    20  *
       
    21  * Encodes png images.
       
    22  */
       
    23 
       
    24 #ifdef HAVE_CONFIG_H
       
    25 #include "config.h"
       
    26 #endif
       
    27 #include <string.h>
       
    28 #include <gst/gst.h>
       
    29 #include "gstpngenc.h"
       
    30 #include <gst/video/video.h>
       
    31 #include <zlib.h>
       
    32 
       
    33 #define MAX_HEIGHT              4096
       
    34 
       
    35 
       
    36 static const GstElementDetails gst_pngenc_details =
       
    37 GST_ELEMENT_DETAILS ("PNG image encoder",
       
    38     "Codec/Encoder/Image",
       
    39     "Encode a video frame to a .png image",
       
    40     "Jeremy SIMON <jsimon13@yahoo.fr>");
       
    41 
       
    42 GST_DEBUG_CATEGORY_STATIC (pngenc_debug);
       
    43 #define GST_CAT_DEFAULT pngenc_debug
       
    44 
       
    45 /* Filter signals and args */
       
    46 enum
       
    47 {
       
    48   /* FILL ME */
       
    49   LAST_SIGNAL
       
    50 };
       
    51 
       
    52 #define DEFAULT_SNAPSHOT                TRUE
       
    53 /* #define DEFAULT_NEWMEDIA             FALSE */
       
    54 #define DEFAULT_COMPRESSION_LEVEL       6
       
    55 
       
    56 enum
       
    57 {
       
    58   ARG_0,
       
    59   ARG_SNAPSHOT,
       
    60 /*   ARG_NEWMEDIA, */
       
    61   ARG_COMPRESSION_LEVEL
       
    62 };
       
    63 
       
    64 static GstStaticPadTemplate pngenc_src_template =
       
    65 GST_STATIC_PAD_TEMPLATE ("src",
       
    66     GST_PAD_SRC,
       
    67     GST_PAD_ALWAYS,
       
    68     GST_STATIC_CAPS ("image/png, "
       
    69         "width = (int) [ 16, 4096 ], "
       
    70         "height = (int) [ 16, 4096 ], " "framerate = (fraction) [ 0.0, MAX ]")
       
    71     );
       
    72 
       
    73 static GstStaticPadTemplate pngenc_sink_template =
       
    74     GST_STATIC_PAD_TEMPLATE ("sink",
       
    75     GST_PAD_SINK,
       
    76     GST_PAD_ALWAYS,
       
    77     GST_STATIC_CAPS (GST_VIDEO_CAPS_RGBA ";" GST_VIDEO_CAPS_RGB)
       
    78     );
       
    79 
       
    80 /* static GstElementClass *parent_class = NULL; */
       
    81 
       
    82 GST_BOILERPLATE (GstPngEnc, gst_pngenc, GstElement, GST_TYPE_ELEMENT);
       
    83 
       
    84 static void gst_pngenc_set_property (GObject * object,
       
    85     guint prop_id, const GValue * value, GParamSpec * pspec);
       
    86 static void gst_pngenc_get_property (GObject * object,
       
    87     guint prop_id, GValue * value, GParamSpec * pspec);
       
    88 
       
    89 static GstFlowReturn gst_pngenc_chain (GstPad * pad, GstBuffer * data);
       
    90 
       
    91 static void
       
    92 user_error_fn (png_structp png_ptr, png_const_charp error_msg)
       
    93 {
       
    94   g_warning ("%s", error_msg);
       
    95 }
       
    96 
       
    97 static void
       
    98 user_warning_fn (png_structp png_ptr, png_const_charp warning_msg)
       
    99 {
       
   100   g_warning ("%s", warning_msg);
       
   101 }
       
   102 
       
   103 static void
       
   104 gst_pngenc_base_init (gpointer g_class)
       
   105 {
       
   106   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
       
   107 
       
   108   gst_element_class_add_pad_template
       
   109       (element_class, gst_static_pad_template_get (&pngenc_sink_template));
       
   110   gst_element_class_add_pad_template
       
   111       (element_class, gst_static_pad_template_get (&pngenc_src_template));
       
   112   gst_element_class_set_details (element_class, &gst_pngenc_details);
       
   113 }
       
   114 
       
   115 static void
       
   116 gst_pngenc_class_init (GstPngEncClass * klass)
       
   117 {
       
   118   GObjectClass *gobject_class;
       
   119 
       
   120   gobject_class = (GObjectClass *) klass;
       
   121 
       
   122   parent_class = g_type_class_peek_parent (klass);
       
   123 
       
   124   gobject_class->get_property = gst_pngenc_get_property;
       
   125   gobject_class->set_property = gst_pngenc_set_property;
       
   126 
       
   127   g_object_class_install_property (gobject_class, ARG_SNAPSHOT,
       
   128       g_param_spec_boolean ("snapshot", "Snapshot",
       
   129           "Send EOS after encoding a frame, useful for snapshots",
       
   130           DEFAULT_SNAPSHOT, (GParamFlags) G_PARAM_READWRITE));
       
   131 
       
   132 /*   g_object_class_install_property (gobject_class, ARG_NEWMEDIA, */
       
   133 /*       g_param_spec_boolean ("newmedia", "newmedia", */
       
   134 /*           "Send new media discontinuity after encoding each frame", */
       
   135 /*           DEFAULT_NEWMEDIA, (GParamFlags) G_PARAM_READWRITE)); */
       
   136 
       
   137   g_object_class_install_property
       
   138       (gobject_class, ARG_COMPRESSION_LEVEL,
       
   139       g_param_spec_uint ("compression-level", "compression-level",
       
   140           "PNG compression level",
       
   141           Z_NO_COMPRESSION, Z_BEST_COMPRESSION,
       
   142           DEFAULT_COMPRESSION_LEVEL, (GParamFlags) G_PARAM_READWRITE));
       
   143 
       
   144   GST_DEBUG_CATEGORY_INIT (pngenc_debug, "pngenc", 0, "PNG image encoder");
       
   145 }
       
   146 
       
   147 
       
   148 static gboolean
       
   149 gst_pngenc_setcaps (GstPad * pad, GstCaps * caps)
       
   150 {
       
   151   GstPngEnc *pngenc;
       
   152   const GValue *fps;
       
   153   GstStructure *structure;
       
   154   GstCaps *pcaps;
       
   155   gboolean ret = TRUE;
       
   156 
       
   157   pngenc = GST_PNGENC (gst_pad_get_parent (pad));
       
   158 
       
   159   structure = gst_caps_get_structure (caps, 0);
       
   160   gst_structure_get_int (structure, "width", &pngenc->width);
       
   161   gst_structure_get_int (structure, "height", &pngenc->height);
       
   162   fps = gst_structure_get_value (structure, "framerate");
       
   163   gst_structure_get_int (structure, "bpp", &pngenc->bpp);
       
   164 
       
   165   if (pngenc->bpp == 32)
       
   166     pngenc->stride = pngenc->width * 4;
       
   167   else
       
   168     pngenc->stride = GST_ROUND_UP_4 (pngenc->width * 3);
       
   169 
       
   170   pcaps = gst_caps_new_simple ("image/png",
       
   171       "width", G_TYPE_INT, pngenc->width,
       
   172       "height", G_TYPE_INT, pngenc->height, NULL);
       
   173   structure = gst_caps_get_structure (pcaps, 0);
       
   174   gst_structure_set_value (structure, "framerate", fps);
       
   175 
       
   176   ret = gst_pad_set_caps (pngenc->srcpad, pcaps);
       
   177 
       
   178   gst_caps_unref (pcaps);
       
   179   gst_object_unref (pngenc);
       
   180 
       
   181   return ret;
       
   182 }
       
   183 
       
   184 static void
       
   185 gst_pngenc_init (GstPngEnc * pngenc, GstPngEncClass * g_class)
       
   186 {
       
   187   /* sinkpad */
       
   188   pngenc->sinkpad = gst_pad_new_from_static_template
       
   189       (&pngenc_sink_template, "sink");
       
   190   gst_pad_set_chain_function (pngenc->sinkpad, gst_pngenc_chain);
       
   191   /*   gst_pad_set_link_function (pngenc->sinkpad, gst_pngenc_sinklink); */
       
   192   /*   gst_pad_set_getcaps_function (pngenc->sinkpad, gst_pngenc_sink_getcaps); */
       
   193   gst_pad_set_setcaps_function (pngenc->sinkpad, gst_pngenc_setcaps);
       
   194   gst_element_add_pad (GST_ELEMENT (pngenc), pngenc->sinkpad);
       
   195 
       
   196   /* srcpad */
       
   197   pngenc->srcpad = gst_pad_new_from_static_template
       
   198       (&pngenc_src_template, "src");
       
   199   /*   pngenc->srcpad = gst_pad_new ("src", GST_PAD_SRC); */
       
   200   /*   gst_pad_set_getcaps_function (pngenc->srcpad, gst_pngenc_src_getcaps); */
       
   201   /*   gst_pad_set_setcaps_function (pngenc->srcpad, gst_pngenc_setcaps); */
       
   202   gst_element_add_pad (GST_ELEMENT (pngenc), pngenc->srcpad);
       
   203 
       
   204   /* init settings */
       
   205   pngenc->png_struct_ptr = NULL;
       
   206   pngenc->png_info_ptr = NULL;
       
   207 
       
   208   pngenc->snapshot = DEFAULT_SNAPSHOT;
       
   209 /*   pngenc->newmedia = FALSE; */
       
   210   pngenc->compression_level = DEFAULT_COMPRESSION_LEVEL;
       
   211 }
       
   212 
       
   213 static void
       
   214 user_flush_data (png_structp png_ptr G_GNUC_UNUSED)
       
   215 {
       
   216 }
       
   217 
       
   218 static void
       
   219 user_write_data (png_structp png_ptr, png_bytep data, png_uint_32 length)
       
   220 {
       
   221   GstPngEnc *pngenc;
       
   222 
       
   223   pngenc = (GstPngEnc *) png_get_io_ptr (png_ptr);
       
   224 
       
   225   if (pngenc->written + length >= GST_BUFFER_SIZE (pngenc->buffer_out)) {
       
   226     GST_ERROR_OBJECT (pngenc, "output buffer bigger than the input buffer!?");
       
   227     /* yuck */
       
   228     longjmp (pngenc->png_struct_ptr->jmpbuf, 1);
       
   229 
       
   230     /* never reached */
       
   231     return;
       
   232   }
       
   233 
       
   234   memcpy (GST_BUFFER_DATA (pngenc->buffer_out) + pngenc->written, data, length);
       
   235   pngenc->written += length;
       
   236 }
       
   237 
       
   238 static GstFlowReturn
       
   239 gst_pngenc_chain (GstPad * pad, GstBuffer * buf)
       
   240 {
       
   241   GstPngEnc *pngenc;
       
   242   gint row_index;
       
   243   png_byte *row_pointers[MAX_HEIGHT];
       
   244   GstFlowReturn ret = GST_FLOW_OK;
       
   245   GstBuffer *encoded_buf = NULL;
       
   246 
       
   247   pngenc = GST_PNGENC (gst_pad_get_parent (pad));
       
   248 
       
   249   GST_DEBUG_OBJECT (pngenc, "BEGINNING");
       
   250 
       
   251   /* initialize png struct stuff */
       
   252   pngenc->png_struct_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING,
       
   253       (png_voidp) NULL, user_error_fn, user_warning_fn);
       
   254   if (pngenc->png_struct_ptr == NULL) {
       
   255     gst_buffer_unref (buf);
       
   256     GST_ELEMENT_ERROR (pngenc, LIBRARY, INIT, (NULL),
       
   257         ("Failed to initialize png structure"));
       
   258     ret = GST_FLOW_ERROR;
       
   259     goto done;
       
   260   }
       
   261 
       
   262   pngenc->png_info_ptr = png_create_info_struct (pngenc->png_struct_ptr);
       
   263   if (!pngenc->png_info_ptr) {
       
   264     gst_buffer_unref (buf);
       
   265     png_destroy_write_struct (&(pngenc->png_struct_ptr), (png_infopp) NULL);
       
   266     GST_ELEMENT_ERROR (pngenc, LIBRARY, INIT, (NULL),
       
   267         ("Failed to initialize the png info structure"));
       
   268     ret = GST_FLOW_ERROR;
       
   269     goto done;
       
   270   }
       
   271 
       
   272   /* non-0 return is from a longjmp inside of libpng */
       
   273   if (setjmp (pngenc->png_struct_ptr->jmpbuf) != 0) {
       
   274     gst_buffer_unref (buf);
       
   275     png_destroy_write_struct (&pngenc->png_struct_ptr, &pngenc->png_info_ptr);
       
   276     GST_ELEMENT_ERROR (pngenc, LIBRARY, FAILED, (NULL),
       
   277         ("returning from longjmp"));
       
   278     ret = GST_FLOW_ERROR;
       
   279     goto done;
       
   280   }
       
   281 
       
   282   png_set_filter (pngenc->png_struct_ptr, 0,
       
   283       PNG_FILTER_NONE | PNG_FILTER_VALUE_NONE);
       
   284   png_set_compression_level (pngenc->png_struct_ptr, pngenc->compression_level);
       
   285 
       
   286   png_set_IHDR (pngenc->png_struct_ptr,
       
   287       pngenc->png_info_ptr,
       
   288       pngenc->width,
       
   289       pngenc->height,
       
   290       8,
       
   291       (pngenc->bpp == 32) ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB,
       
   292       PNG_INTERLACE_NONE,
       
   293       PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
       
   294 
       
   295   png_set_write_fn (pngenc->png_struct_ptr, pngenc,
       
   296       (png_rw_ptr) user_write_data, user_flush_data);
       
   297 
       
   298   for (row_index = 0; row_index < pngenc->height; row_index++) {
       
   299     row_pointers[row_index] = GST_BUFFER_DATA (buf) +
       
   300         (row_index * pngenc->stride);
       
   301   }
       
   302 
       
   303   /* allocate the output buffer */
       
   304   pngenc->buffer_out =
       
   305       gst_buffer_new_and_alloc (pngenc->height * pngenc->stride);
       
   306   pngenc->written = 0;
       
   307 
       
   308   png_write_info (pngenc->png_struct_ptr, pngenc->png_info_ptr);
       
   309   png_write_image (pngenc->png_struct_ptr, row_pointers);
       
   310   png_write_end (pngenc->png_struct_ptr, NULL);
       
   311 
       
   312   encoded_buf = gst_buffer_create_sub (pngenc->buffer_out, 0, pngenc->written);
       
   313 
       
   314   png_destroy_info_struct (pngenc->png_struct_ptr, &pngenc->png_info_ptr);
       
   315   png_destroy_write_struct (&pngenc->png_struct_ptr, (png_infopp) NULL);
       
   316   gst_buffer_copy_metadata (encoded_buf, buf, GST_BUFFER_COPY_TIMESTAMPS);
       
   317   gst_buffer_unref (buf);
       
   318   gst_buffer_set_caps (encoded_buf, GST_PAD_CAPS (pngenc->srcpad));
       
   319 
       
   320   if ((ret = gst_pad_push (pngenc->srcpad, encoded_buf)) != GST_FLOW_OK)
       
   321     goto done;
       
   322 
       
   323   if (pngenc->snapshot) {
       
   324     GstEvent *event;
       
   325 
       
   326     GST_DEBUG_OBJECT (pngenc, "snapshot mode, sending EOS");
       
   327     /* send EOS event, since a frame has been pushed out */
       
   328     event = gst_event_new_eos ();
       
   329 
       
   330     gst_pad_push_event (pngenc->srcpad, event);
       
   331     ret = GST_FLOW_UNEXPECTED;
       
   332   }
       
   333 
       
   334 done:
       
   335   GST_DEBUG_OBJECT (pngenc, "END, ret:%d", ret);
       
   336 
       
   337   if (pngenc->buffer_out != NULL) {
       
   338     gst_buffer_unref (pngenc->buffer_out);
       
   339     pngenc->buffer_out = NULL;
       
   340   }
       
   341 
       
   342   gst_object_unref (pngenc);
       
   343   return ret;
       
   344 }
       
   345 
       
   346 
       
   347 static void
       
   348 gst_pngenc_get_property (GObject * object,
       
   349     guint prop_id, GValue * value, GParamSpec * pspec)
       
   350 {
       
   351   GstPngEnc *pngenc;
       
   352 
       
   353   pngenc = GST_PNGENC (object);
       
   354 
       
   355   switch (prop_id) {
       
   356     case ARG_SNAPSHOT:
       
   357       g_value_set_boolean (value, pngenc->snapshot);
       
   358       break;
       
   359 /*     case ARG_NEWMEDIA: */
       
   360 /*       g_value_set_boolean (value, pngenc->newmedia); */
       
   361 /*       break; */
       
   362     case ARG_COMPRESSION_LEVEL:
       
   363       g_value_set_uint (value, pngenc->compression_level);
       
   364       break;
       
   365     default:
       
   366       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       
   367       break;
       
   368   }
       
   369 }
       
   370 
       
   371 
       
   372 static void
       
   373 gst_pngenc_set_property (GObject * object,
       
   374     guint prop_id, const GValue * value, GParamSpec * pspec)
       
   375 {
       
   376   GstPngEnc *pngenc;
       
   377 
       
   378   pngenc = GST_PNGENC (object);
       
   379 
       
   380   switch (prop_id) {
       
   381     case ARG_SNAPSHOT:
       
   382       pngenc->snapshot = g_value_get_boolean (value);
       
   383       break;
       
   384 /*     case ARG_NEWMEDIA: */
       
   385 /*       pngenc->newmedia = g_value_get_boolean (value); */
       
   386 /*       break; */
       
   387     case ARG_COMPRESSION_LEVEL:
       
   388       pngenc->compression_level = g_value_get_uint (value);
       
   389       break;
       
   390     default:
       
   391       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       
   392       break;
       
   393   }
       
   394 }