2005-01-08  Gergely Nagy  <gergely.nagy@neteyes.hu>

	* ext/libpng/gstpngenc.h: (_GstPngEnc): Added three new members.
	* ext/libpng/gstpngenc.c: (gst_pngenc_finalize): New function.
	(gst_pngenc_class_init): Set gobject->finalize to
	gst_pngenc_finalize.
	(gst_pngenc_sinkpad_link): Use a new GetCaps* object instead of
	clobbering the one received.
	(gst_pngenc_init): Initialise ->input_data and
	->input_data_offset.
	(gst_pngenc_png_init): New function.
	(gst_pngenc_chain): Upon the first received buffer, call
	gst_pngenc_png_init(). On all subsequent buffers, store the data
	in ->input_data. When all data arrived, push out the PNG and
	reset our state.

--- orig/ext/libpng/gstpngenc.c
+++ mod/ext/libpng/gstpngenc.c
@@ -93,6 +93,25 @@
   g_warning ("%s", warning_msg);
 }
 
+static void
+gst_pngenc_finalize (GObject *object)
+{
+  GstPngEnc *pngenc = GST_PNGENC (object);
+
+  if (pngenc->png_struct_ptr != NULL) {
+    png_destroy_write_struct (&pngenc->png_struct_ptr, &pngenc->png_info_ptr);
+  }
+
+  pngenc->png_struct_ptr =  NULL;
+  pngenc->png_info_ptr = NULL;
+
+  g_free (pngenc->input_data);
+  pngenc->input_data = NULL;
+  pngenc->input_data_offset = 0;
+
+  g_free (pngenc->row_pointers);
+  pngenc->row_pointers = NULL;
+}
 
 GType
 gst_pngenc_get_type (void)
@@ -159,6 +178,8 @@
 		       -1, Z_BEST_COMPRESSION, Z_DEFAULT_COMPRESSION,
 		       G_PARAM_READWRITE));
 
+  gobject_class->finalize = gst_pngenc_finalize;
+
   gstelement_class->get_property = gst_pngenc_get_property;
   gstelement_class->set_property = gst_pngenc_set_property;
 }
@@ -170,6 +191,8 @@
   GstPngEnc *pngenc;
   gdouble fps;
   GstStructure *structure;
+  GstPadLinkReturn ret;
+  GstCaps *propcaps;
 
   pngenc = GST_PNGENC (gst_pad_get_parent (pad));
 
@@ -179,12 +202,15 @@
   gst_structure_get_double (structure, "framerate", &fps);
   gst_structure_get_int (structure, "bpp", &pngenc->bpp);
 
-  caps = gst_caps_new_simple ("image/png",
-      "framerate", G_TYPE_DOUBLE, fps,
-      "width", G_TYPE_INT, pngenc->width,
-      "height", G_TYPE_INT, pngenc->height, NULL);
+  propcaps = gst_caps_new_simple ("image/png",
+				  "framerate", G_TYPE_DOUBLE, fps,
+				  "width", G_TYPE_INT, pngenc->width,
+				  "height", G_TYPE_INT, pngenc->height, NULL);
+
+  ret = gst_pad_try_set_caps (pngenc->srcpad, propcaps);
+  gst_caps_free (propcaps);
 
-  return gst_pad_try_set_caps (pngenc->srcpad, caps);
+  return ret;
 }
 
 static void
@@ -206,6 +232,9 @@
   pngenc->snapshot = DEFAULT_SNAPSHOT;
   pngenc->newmedia = FALSE;
   pngenc->compression_level = Z_DEFAULT_COMPRESSION;
+
+  pngenc->input_data = NULL;
+  pngenc->input_data_offset = 0;
 }
 
 static void
@@ -242,65 +271,113 @@
     pngenc->buffer_out = buffer;
 }
 
-static void
-gst_pngenc_chain (GstPad * pad, GstData * _data)
+static gboolean
+gst_pngenc_png_init (GstPngEnc *pngenc)
 {
-  GstBuffer *buf = GST_BUFFER (_data);
-  GstPngEnc *pngenc;
   gint row_index;
-  png_byte *row_pointers[MAX_HEIGHT];
-
-  pngenc = GST_PNGENC (gst_pad_get_parent (pad));
-
-  pngenc->buffer_out = NULL;
-  if (!GST_PAD_IS_USABLE (pngenc->srcpad)) {
-    gst_buffer_unref (buf);
-    return;
-  }
 
-  /* initialize png struct stuff */
   pngenc->png_struct_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING,
-      (png_voidp) NULL, user_error_fn, user_warning_fn);
-  /* FIXME: better error handling */
-  if (pngenc->png_struct_ptr == NULL)
-    g_warning ("Failed to initialize png structure");
+						    (png_voidp) NULL,
+						    user_error_fn,
+						    user_warning_fn);
+  if (pngenc->png_struct_ptr == NULL) {
+    GST_ELEMENT_ERROR (pngenc, LIBRARY, INIT, (NULL),
+		       ("Failed to initialize PNG structure"));
+    return FALSE;
+  }
 
   pngenc->png_info_ptr = png_create_info_struct (pngenc->png_struct_ptr);
-  if (!pngenc->png_info_ptr) {
-    png_destroy_read_struct (&(pngenc->png_struct_ptr), (png_infopp) NULL,
-        (png_infopp) NULL);
+  if (pngenc->png_info_ptr == NULL) {
+    GST_ELEMENT_ERROR (pngenc, LIBRARY, INIT, (NULL),
+		       ("Failed to initialize PNG info pointer"));
+    return FALSE;
   }
 
   /* non-0 return is from a longjmp inside of libpng */
   if (setjmp (pngenc->png_struct_ptr->jmpbuf) != 0) {
-    GST_DEBUG ("returning from longjmp");
-    png_destroy_write_struct (&pngenc->png_struct_ptr, &pngenc->png_info_ptr);
-    return;
+    GST_ELEMENT_ERROR (pngenc, LIBRARY, FAILED, (NULL),
+		       ("returning from longjmp"));
+    return FALSE;
   }
 
   png_set_filter (pngenc->png_struct_ptr, 0,
-      PNG_FILTER_NONE | PNG_FILTER_VALUE_NONE);
+		  PNG_FILTER_NONE | PNG_FILTER_VALUE_NONE);
   png_set_compression_level (pngenc->png_struct_ptr,
 			     pngenc->compression_level);
 
   png_set_IHDR (pngenc->png_struct_ptr,
-      pngenc->png_info_ptr,
-      pngenc->width,
-      pngenc->height,
-      8,
-      (pngenc->bpp == 32) ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB,
-      PNG_INTERLACE_NONE,
-      PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+		pngenc->png_info_ptr,
+		pngenc->width,
+		pngenc->height,
+		8,
+		(pngenc->bpp == 32) ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB,
+		PNG_INTERLACE_NONE,
+		PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
 
   png_set_write_fn (pngenc->png_struct_ptr, pngenc,
-      (png_rw_ptr) user_write_data, user_flush_data);
+		    (png_rw_ptr) user_write_data, user_flush_data);
 
+  pngenc->input_data = (gpointer) g_malloc (pngenc->height *
+					    pngenc->png_info_ptr->rowbytes);
+  pngenc->row_pointers = (png_bytep *) g_malloc (sizeof (png_bytep) *
+						 pngenc->height);
   for (row_index = 0; row_index < pngenc->height; row_index++)
-    row_pointers[row_index] = GST_BUFFER_DATA (buf) +
-        (pngenc->width * row_index * pngenc->bpp / 8);
+    pngenc->row_pointers[row_index] = pngenc->input_data +
+      (row_index * pngenc->png_info_ptr->rowbytes);
+
+  pngenc->input_data_offset = 0;
 
   png_write_info (pngenc->png_struct_ptr, pngenc->png_info_ptr);
-  png_write_image (pngenc->png_struct_ptr, row_pointers);
+
+  return TRUE;
+}
+
+static void
+gst_pngenc_chain (GstPad * pad, GstData * _data)
+{
+  GstBuffer *buf = GST_BUFFER (_data);
+  GstPngEnc *pngenc;
+
+  pngenc = GST_PNGENC (gst_pad_get_parent (pad));
+
+  if (!GST_PAD_IS_USABLE (pngenc->srcpad)) {
+    gst_buffer_unref (buf);
+    return;
+  }
+
+  if (pngenc->input_data == NULL) {
+    if (gst_pngenc_png_init (pngenc) == FALSE) {
+      gst_buffer_unref (buf);
+      return;
+    }
+  }
+
+  if (pngenc->input_data_offset + GST_BUFFER_SIZE (buf) >
+      pngenc->height * pngenc->png_info_ptr->rowbytes) {
+    gst_buffer_unref (buf);
+    GST_ELEMENT_ERROR (pngenc, STREAM, FORMAT, (NULL),
+		       ("Too much data on the sink pad - format error."));
+    return;
+  }
+
+  memcpy (pngenc->input_data + pngenc->input_data_offset,
+	  GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
+  pngenc->input_data_offset += GST_BUFFER_SIZE (buf);
+
+  if (pngenc->input_data_offset !=
+      pngenc->height * pngenc->png_info_ptr->rowbytes) {
+    gst_buffer_unref (buf);
+    return;
+  }
+
+/* non-0 return is from a longjmp inside of libpng */
+  if (setjmp (pngenc->png_struct_ptr->jmpbuf) != 0) {
+    GST_ELEMENT_ERROR (pngenc, LIBRARY, FAILED, (NULL),
+		       ("returning from longjmp"));
+    return FALSE;
+  }
+
+  png_write_image (pngenc->png_struct_ptr, pngenc->row_pointers);
   png_write_end (pngenc->png_struct_ptr, NULL);
 
   user_flush_data (pngenc->png_struct_ptr);
@@ -309,6 +386,8 @@
   png_destroy_write_struct (&pngenc->png_struct_ptr, (png_infopp) NULL);
   gst_buffer_stamp (pngenc->buffer_out, buf);
   gst_buffer_unref (buf);
+  g_free (pngenc->input_data);
+  pngenc->input_data = NULL;
 
   gst_pad_push (pngenc->srcpad, GST_DATA (pngenc->buffer_out));
 


--- orig/ext/libpng/gstpngenc.h
+++ mod/ext/libpng/gstpngenc.h
@@ -44,9 +44,12 @@
 
   GstPad *sinkpad, *srcpad;
   GstBuffer *buffer_out;
+  gpointer input_data;
+  guint input_data_offset;
 
   png_structp png_struct_ptr;
   png_infop png_info_ptr;
+  png_bytep *row_pointers;
 
   gint width;
   gint height;
