diff --git a/ext/freeimage/gstfreeimage.c b/ext/freeimage/gstfreeimage.c index 53bbd1f..38ac8e1 100644 --- a/ext/freeimage/gstfreeimage.c +++ b/ext/freeimage/gstfreeimage.c @@ -24,18 +24,24 @@ #include #include "gstfreeimagedec.h" +#include "gstfreeimageenc.h" GST_DEBUG_CATEGORY (freeimagedec_debug); +GST_DEBUG_CATEGORY (freeimageenc_debug); static gboolean plugin_init (GstPlugin * plugin) { GST_DEBUG_CATEGORY_INIT (freeimagedec_debug, "freeimagedec", 0, "FreeImage image decoder"); + GST_DEBUG_CATEGORY_INIT (freeimageenc_debug, "freeimageenc", 0, "FreeImage image encoder"); if (!gst_freeimagedec_register_plugins (plugin)) return FALSE; + if (!gst_freeimageenc_register_plugins (plugin)) + return FALSE; + return TRUE; } diff --git a/ext/freeimage/gstfreeimageenc.c b/ext/freeimage/gstfreeimageenc.c new file mode 100644 index 0000000..b54d7f0 --- /dev/null +++ b/ext/freeimage/gstfreeimageenc.c @@ -0,0 +1,398 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ +/** + * SECTION:element-freeimageenc + * + * Encodes image types supported by FreeImage. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "gstfreeimageenc.h" +#include "gstfreeimageutils.h" + +GST_DEBUG_CATEGORY_EXTERN (freeimageenc_debug); +#define GST_CAT_DEFAULT freeimageenc_debug + +typedef struct +{ + FREE_IMAGE_FORMAT fif; +} GstFreeImageEncClassData; + +static void gst_freeimageenc_class_init (GstFreeImageEncClass * klass, + GstFreeImageEncClassData * class_data); +static void gst_freeimageenc_init (GstFreeImageEnc * freeimageenc); + +static gboolean gst_freeimageenc_sink_activate_push (GstPad * sinkpad, + gboolean active); +static gboolean gst_freeimageenc_sink_activate_pull (GstPad * sinkpad, + gboolean active); +static gboolean gst_freeimageenc_sink_activate (GstPad * sinkpad); +static GstFlowReturn gst_freeimageenc_chain (GstPad * pad, GstBuffer * buffer); +static gboolean gst_freeimageenc_sink_event (GstPad * pad, GstEvent * event); +static gboolean gst_freeimageenc_sink_setcaps (GstPad * pad, GstCaps * caps); + +static void gst_freeimageenc_task (GstPad * pad); + +static gboolean gst_freeimageenc_freeimage_init (GstFreeImageEnc * freeimageenc); +static gboolean gst_freeimageenc_freeimage_clear (GstFreeImageEnc * freeimageenc); +static GstFlowReturn gst_freeimageenc_push_dib (GstFreeImageEnc * freeimageenc); + +static GstElementClass *parent_class = NULL; + +void DLL_CALLCONV +gst_freeimageenc_user_error (FREE_IMAGE_FORMAT fif, const char *message) +{ + GST_ERROR ("%s", message); +} + +static int DLL_CALLCONV +gst_freeimageenc_user_seek (fi_handle handle, long offset, int origin) +{ + GstFreeImageEnc *freeimageenc = GST_FREEIMAGEENC (handle); + + switch (origin) { + case SEEK_SET: + freeimageenc->offset = offset; + break; + case SEEK_CUR: + freeimageenc->offset += offset; + break; + case SEEK_END: + freeimageenc->offset = freeimageenc->length + offset; + break; + } + return 0; +} + +static long DLL_CALLCONV +gst_freeimageenc_user_tell (fi_handle handle) +{ + GstFreeImageEnc *freeimageenc = GST_FREEIMAGEENC (handle); + + return freeimageenc->offset; +} + +static void +gst_freeimageenc_class_init (GstFreeImageEncClass * klass, + GstFreeImageEncClassData * class_data) +{ + GstElementClass *gstelement_class; + GstCaps * caps; + GstPadTemplate *templ; + const gchar * mimetype; + const gchar * format; + const gchar * format_description; + const gchar * extensions; + gchar * description; + gchar * longname; + + klass->fif = class_data->fif; + + gstelement_class = (GstElementClass *) klass; + + parent_class = g_type_class_peek_parent (klass); + + mimetype = FreeImage_GetFIFMimeType (klass->fif); + format = FreeImage_GetFormatFromFIF (klass->fif); + format_description = FreeImage_GetFIFDescription (klass->fif); + extensions = FreeImage_GetFIFExtensionList (klass->fif); + + /* add src pad template from FIF mimetype */ + if (mimetype) + caps = gst_caps_new_simple (mimetype, NULL); + else + caps = gst_caps_new_simple ("image/freeimage-unknown", NULL); + templ = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, caps); + gst_element_class_add_pad_template (gstelement_class, templ); + + /* add sink pad template */ + caps = gst_freeimageutils_caps_from_freeimage_format (klass->fif); + templ = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, caps); + gst_element_class_add_pad_template (gstelement_class, templ); + + /* set details */ + longname = g_strdup_printf ("FreeImage %s image encoder", format); + description = g_strdup_printf ("Encode %s (%s) images", + format_description, extensions); + gst_element_class_set_details_simple (gstelement_class, longname, + "Codec/Encoder/Image", + description, + "Joshua M. Doe "); + g_free (longname); + g_free (description); +} + +static void +gst_freeimageenc_init (GstFreeImageEnc * freeimageenc) +{ + GstElementClass * klass = GST_ELEMENT_GET_CLASS (freeimageenc); + + freeimageenc->sinkpad = gst_pad_new_from_template ( + gst_element_class_get_pad_template (klass, "sink"), "sink"); + gst_pad_set_chain_function (freeimageenc->sinkpad, gst_freeimageenc_chain); + gst_pad_set_setcaps_function (freeimageenc->sinkpad, gst_freeimageenc_sink_setcaps); + gst_element_add_pad (GST_ELEMENT (freeimageenc), freeimageenc->sinkpad); + + freeimageenc->srcpad = gst_pad_new_from_template ( + gst_element_class_get_pad_template (klass, "src"), "src"); + gst_pad_use_fixed_caps (freeimageenc->srcpad); + gst_element_add_pad (GST_ELEMENT (freeimageenc), freeimageenc->srcpad); + + freeimageenc->setup = FALSE; + + freeimageenc->in_timestamp = GST_CLOCK_TIME_NONE; + freeimageenc->in_duration = GST_CLOCK_TIME_NONE; + + freeimageenc->fps_n = 0; + freeimageenc->fps_d = 1; + + /* Set user IO functions to FreeImageIO struct */ + freeimageenc->fiio.read_proc = NULL; + freeimageenc->fiio.write_proc = NULL; + freeimageenc->fiio.seek_proc = gst_freeimageenc_user_seek; + freeimageenc->fiio.tell_proc = gst_freeimageenc_user_tell; +} + +static GstFlowReturn +gst_freeimageenc_chain (GstPad * pad, GstBuffer * buffer) + { + GstFreeImageEnc *freeimageenc; + GstFreeImageEncClass *klass; + GstFlowReturn ret = GST_FLOW_OK; + GstBuffer * buffer_out; + FIMEMORY *hmem = NULL; + gint srcPitch, dstPitch; + guint8 * pSrc, * pDst; + gint width, height, bpp; + size_t y; + BYTE * mem_buffer; + DWORD size_in_bytes; + + freeimageenc = GST_FREEIMAGEENC (gst_pad_get_parent (pad)); + klass = GST_FREEIMAGEENC_GET_CLASS (freeimageenc); + + GST_LOG_OBJECT (freeimageenc, "Got buffer, size=%u", GST_BUFFER_SIZE (buffer)); + + /* convert raw buffer to FIBITMAP */ + width = FreeImage_GetWidth (freeimageenc->dib); + height = FreeImage_GetHeight (freeimageenc->dib); + bpp = FreeImage_GetBPP (freeimageenc->dib); + + dstPitch = FreeImage_GetPitch (freeimageenc->dib); + srcPitch = GST_ROUND_UP_4 (width * bpp / 8); + + /* Copy data, invert scanlines and respect FreeImage pitch */ + pDst = FreeImage_GetBits(freeimageenc->dib); + for (y = 0; y < height; ++y) { + pSrc = GST_BUFFER_DATA (buffer) + (height - y - 1) * srcPitch; + memcpy(pDst, pSrc, srcPitch); + pDst += dstPitch; + } + + /* open memory stream */ + hmem = FreeImage_OpenMemory (0, 0); + + /* encode raw image to memory */ + if (!FreeImage_SaveToMemory (klass->fif, freeimageenc->dib, hmem, 0)) { + GST_ERROR ("Failed to encode image"); + FreeImage_CloseMemory (hmem); + gst_buffer_unref (buffer); + return GST_FLOW_ERROR; + } + + if (!FreeImage_AcquireMemory(hmem, &mem_buffer, &size_in_bytes)) { + GST_ERROR ("Failed to acquire encoded image"); + FreeImage_CloseMemory (hmem); + gst_buffer_unref (buffer); + return GST_FLOW_ERROR; + } + + buffer_out = gst_buffer_new_and_alloc (size_in_bytes); + + /* copy compressed image to buffer */ + memcpy (GST_BUFFER_DATA (buffer_out), mem_buffer, size_in_bytes); + + FreeImage_CloseMemory (hmem); + + gst_buffer_copy_metadata (buffer_out, buffer, GST_BUFFER_COPY_TIMESTAMPS); + gst_buffer_unref (buffer); + gst_buffer_set_caps (buffer, GST_PAD_CAPS (freeimageenc->srcpad)); + + if ((ret = gst_pad_push (freeimageenc->srcpad, buffer_out)) != GST_FLOW_OK) + goto done; + + //if (pngenc->snapshot) { + // GstEvent *event; + + // GST_DEBUG_OBJECT (pngenc, "snapshot mode, sending EOS"); + // /* send EOS event, since a frame has been pushed out */ + // event = gst_event_new_eos (); + + // gst_pad_push_event (pngenc->srcpad, event); + // ret = GST_FLOW_UNEXPECTED; + //} + +done: + GST_DEBUG_OBJECT (freeimageenc, "END, ret:%d", ret); + + if (buffer_out != NULL) { + gst_buffer_unref (buffer_out); + buffer_out = NULL; + } + + gst_object_unref (freeimageenc); + return ret; +} + +static gboolean +gst_freeimageenc_sink_setcaps (GstPad * pad, GstCaps * caps) +{ + GstFreeImageEnc *freeimageenc; + FREE_IMAGE_TYPE type; + gint width, height, bpp; + gint red_mask, green_mask, blue_mask; + + freeimageenc = GST_FREEIMAGEENC (gst_pad_get_parent (pad)); + + if (gst_freeimageutils_parse_caps (caps, &type, &width, &height, &bpp, + &red_mask, &green_mask, &blue_mask) == FALSE) { + GST_DEBUG ("Failed to parse caps"); + return FALSE; + } + + if (freeimageenc->dib) { + FreeImage_Unload (freeimageenc->dib); + freeimageenc->dib = NULL; + } + + freeimageenc->dib = FreeImage_AllocateT (type, width, height, bpp, + red_mask, green_mask, blue_mask); + + if (freeimageenc == NULL) { + GST_DEBUG ("Failed to allocate memory for DIB"); + return FALSE; + } + + gst_object_unref (freeimageenc); + return TRUE; +} + +/* Clean up the freeimage structures */ +static gboolean +gst_freeimageenc_freeimage_clear (GstFreeImageEnc * freeimageenc) +{ + GST_LOG ("cleaning up freeimage structures"); + + if (freeimageenc->dib) { + FreeImage_Unload (freeimageenc->dib); + freeimageenc->dib = NULL; + } + + freeimageenc->in_timestamp = GST_CLOCK_TIME_NONE; + freeimageenc->in_duration = GST_CLOCK_TIME_NONE; + + freeimageenc->setup = FALSE; + + return TRUE; +} + +static gboolean +gst_freeimageenc_freeimage_init (GstFreeImageEnc * freeimageenc) +{ + if (freeimageenc->setup) + return TRUE; + + GST_LOG ("init freeimage"); + + freeimageenc->setup = TRUE; + + return TRUE; +} + +gboolean +gst_freeimageenc_register_plugin (GstPlugin * plugin, FREE_IMAGE_FORMAT fif) +{ + GTypeInfo typeinfo = { + sizeof (GstFreeImageEncClass), + NULL, + NULL, + (GClassInitFunc) gst_freeimageenc_class_init, + NULL, + NULL, + sizeof (GstFreeImageEnc), + 0, + (GInstanceInitFunc) gst_freeimageenc_init + }; + GType type; + gchar *type_name, *tmp; + GstFreeImageEncClassData *class_data; + gboolean ret = FALSE; + + const gchar *format = FreeImage_GetFormatFromFIF (fif); + if (format == NULL) { + GST_WARNING ("Specified format not supported by FreeImage"); + return FALSE; + } + + tmp = g_strdup_printf ("fienc_%s", format); + type_name = g_ascii_strdown (tmp, -1); + g_free (tmp); + g_strcanon (type_name, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-+_", '-'); + + GST_LOG ("Trying to use name %s", type_name); + + if (g_type_from_name (type_name)) { + GST_WARNING ("Type '%s' already exists", type_name); + return FALSE; + } + + class_data = g_new0 (GstFreeImageEncClassData, 1); + class_data->fif = fif; + typeinfo.class_data = class_data; + + type = g_type_register_static (GST_TYPE_ELEMENT, type_name, &typeinfo, 0); + ret = gst_element_register (plugin, type_name, GST_RANK_NONE, type); + + g_free (type_name); + return ret; +} + +gboolean +gst_freeimageenc_register_plugins (GstPlugin * plugin) +{ + gint i; + gint nloaded = 0; + + GST_LOG ("FreeImage indicates there are %d formats supported", FreeImage_GetFIFCount()); + + for (i = 0; i < FreeImage_GetFIFCount(); i++) { + if (FreeImage_FIFSupportsWriting ((FREE_IMAGE_FORMAT)i)) { + if (gst_freeimageenc_register_plugin (plugin, (FREE_IMAGE_FORMAT)i) == TRUE) + nloaded += 1; + } + } + + if (nloaded) + return TRUE; + else + return FALSE; +} \ No newline at end of file diff --git a/ext/freeimage/gstfreeimageenc.h b/ext/freeimage/gstfreeimageenc.h new file mode 100644 index 0000000..53dd9eb --- /dev/null +++ b/ext/freeimage/gstfreeimageenc.h @@ -0,0 +1,83 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#ifndef __GST_FREEIMAGEENC_H__ +#define __GST_FREEIMAGEENC_H__ + +#include +#include + +G_BEGIN_DECLS + +#define GST_FREEIMAGEENC(obj) \ + ((GstFreeImageEnc *) obj) +#define GST_FREEIMAGEENC_CLASS(klass) \ + ((GstFreeImageEncClass *) klass) +#define GST_FREEIMAGEENC_GET_CLASS(obj) \ + ((GstFreeImageEncClass *) g_type_class_peek (G_TYPE_FROM_INSTANCE (obj))) + + +typedef struct _GstFreeImageEnc GstFreeImageEnc; +typedef struct _GstFreeImageEncClass GstFreeImageEncClass; + +struct _GstFreeImageEnc +{ + GstElement element; + + GstPad *sinkpad, *srcpad; + + gboolean need_newsegment; + + /* Pull range */ + long offset; + + + guint64 in_timestamp; + guint64 in_duration; + + gboolean framed; + + gint ret; + + FIBITMAP *dib; + + gboolean setup; + + gint fps_n; + gint fps_d; + + gboolean image_ready; + + FreeImageIO fiio; + guint64 length; +}; + +struct _GstFreeImageEncClass +{ + GstElementClass parent_class; + + FREE_IMAGE_FORMAT fif; +}; + +gboolean gst_freeimageenc_register_plugins (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_FREEIMAGEENC_H__ */