gst-plugin-linescan/ext/freeimage/gstfreeimageenc.c

398 lines
11 KiB
C

/* GStreamer
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
*
* 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 <stdlib.h>
#include <string.h>
#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 <oss@nvl.army.mil>");
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;
}