/* * GStreamer * Copyright (C) 2006 Stefan Kost * Copyright (C) 2016 Enrico Scholz * * 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. */ /** * SECTION:element-bayer2rgbneon * * FIXME:Describe bayer2rgbneon here. * * * Example launch line * |[ * gst-launch -v -m fakesrc ! bayer2rgbneon ! fakesink silent=TRUE * ]| * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include "gstbayer2rgbneon.h" GST_DEBUG_CATEGORY_STATIC(gst_bayer2rgbneon_debug); #define GST_CAT_DEFAULT gst_bayer2rgbneon_debug /* Filter signals and args */ enum { /* FILL ME */ LAST_SIGNAL }; enum { PROP_0, PROP_SILENT, PROP_SHOW_FPS, PROP_NO_ACCEL, PROP_ROUND_2, PROP_ROUND_4, PROP_PT, PROP_REDUCE_BPP, }; static clockid_t const g_clk = CLOCK_PROCESS_CPUTIME_ID; #define CAP_BAYER(_fmt) \ "video/x-bayer, " \ "format = " _fmt ", " \ "bpp = (int){ 8, 10, 12, 16 }, " \ "endian = (string){ le, be, native, foreign }, " \ "width = (int)[ 16, 2147483647 ], " \ "height = " GST_VIDEO_SIZE_RANGE ", " \ "framerate = " GST_VIDEO_FPS_RANGE #define SINK_CAPS \ CAP_BAYER("(string){ bggr, rggb, grbg, gbrg }") ";" \ #define SRC_CAPS \ GST_VIDEO_CAPS_MAKE("{ RGBx, RGBA, xRGB, ARGB, BGRx, BGRA, xBGR, ABGR }") ";" \ /* the capabilities of the inputs and outputs. * * FIXME:describe the real formats here. */ static GstStaticPadTemplate sink_template = { .name_template = "sink", .direction = GST_PAD_SINK, .presence = GST_PAD_ALWAYS, .static_caps = GST_STATIC_CAPS(SINK_CAPS), }; static GstStaticPadTemplate src_template = { .name_template = "src", .direction = GST_PAD_SRC, .presence = GST_PAD_ALWAYS, .static_caps = GST_STATIC_CAPS(SRC_CAPS), }; typedef struct bayer2rgbneon bayer2rgbneon; #define bayer2rgbneonClass struct bayer2rgbneon_class #define gst_bayer2rgbneon_parent_class parent_class G_DEFINE_TYPE(bayer2rgbneon, gst_bayer2rgbneon, GST_TYPE_BASE_TRANSFORM); #undef bayer2rgbneonClass static void gst_bayer2rgbneon_set_property(GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_bayer2rgbneon_get_property(GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); /* GObject vmethod implementations */ /* initialize the new element * initialize instance structure */ static void gst_bayer2rgbneon_init(struct bayer2rgbneon *filter) { clock_gettime(g_clk, &filter->last_fps_tm); filter->silent = false; filter->show_fps = false; filter->info_shown = false; filter->quality = (1ul << QUALITY_ROUND_4); } static bool str_to_bayer(char const *str, unsigned int *format) { if (strcmp(str, "bggr") == 0) *format = BAYER_BGGR; else if (strcmp(str, "grbg") == 0) *format = BAYER_GRBG; else if (strcmp(str, "rggb") == 0) *format = BAYER_RGGB; else if (strcmp(str, "gbrg") == 0) *format = BAYER_GBRG; else goto err; return true; err: GST_ERROR("bad bayer format '%s'", str); return false; } static bool str_to_endian(char const *str, unsigned int *endian) { if (!str || strcmp(str, "native") == 0) { if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) *endian = BAYER_E_LITTLE; else *endian = BAYER_E_BIG; } else if (strcmp(str, "foreign") == 0) { if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) *endian = BAYER_E_BIG; else *endian = BAYER_E_LITTLE; } else if (strcmp(str, "le") == 0) { *endian = BAYER_E_LITTLE; } else if (strcmp(str, "be") == 0) { *endian = BAYER_E_BIG; } else goto err; return true; err: GST_ERROR("bad endian id '%s'", str); return false; } static void gst_bayer2rgbneon_finalize(GObject * object) { struct bayer2rgbneon *filter = GST_BAYER2RGBNEON(object); g_free(filter->bpp_convert_buf); } static gboolean gst_bayer2rgbneon_set_caps(GstBaseTransform *base, GstCaps *incaps, GstCaps *outcaps) { struct bayer2rgbneon *filter = GST_BAYER2RGBNEON(base); GstVideoInfo in_info; GstVideoInfo out_info; GstStructure *in_structure; GstStructure *out_structure; gint tmp; GST_DEBUG_OBJECT (filter, "setting caps: in [%" GST_PTR_FORMAT "] --> out [%" GST_PTR_FORMAT "]", incaps, outcaps); if (!gst_video_info_from_caps(&in_info, incaps)) { GST_ERROR_OBJECT(filter, "invalid input caps: %" GST_PTR_FORMAT, incaps); return FALSE; } if (!gst_video_info_from_caps(&out_info, outcaps) || !out_info.finfo) { GST_ERROR_OBJECT(filter, "invalid output caps: %" GST_PTR_FORMAT, outcaps); return FALSE; } in_structure = gst_caps_get_structure(incaps, 0); out_structure = gst_caps_get_structure(outcaps, 0); if (!str_to_bayer(gst_structure_get_string(in_structure, "format"), &filter->in.format)) return FALSE; if (!str_to_endian(gst_structure_get_string(in_structure, "endian"), &filter->in.endian)) return FALSE; if (!gst_structure_get_int(in_structure, "bpp", &tmp)) tmp = 8; filter->in.bpp = tmp; if (!gst_structure_get_int(out_structure, "bpp", &tmp)) tmp = 32; filter->out.bpp = tmp; switch (out_info.finfo->format) { case GST_VIDEO_FORMAT_RGBx: case GST_VIDEO_FORMAT_RGBA: filter->out.type = RGB_FMT_RGBx; break; case GST_VIDEO_FORMAT_xRGB: case GST_VIDEO_FORMAT_ARGB: filter->out.type = RGB_FMT_xRGB; break; case GST_VIDEO_FORMAT_BGRx: case GST_VIDEO_FORMAT_BGRA: filter->out.type = RGB_FMT_BGRx; break; case GST_VIDEO_FORMAT_xBGR: case GST_VIDEO_FORMAT_ABGR: filter->out.type = RGB_FMT_xBGR; break; default: GST_ERROR_OBJECT(filter, "unsupported output format %d in: %" GST_PTR_FORMAT, out_info.finfo->format, outcaps); return FALSE; } filter->in.width = in_info.width; filter->in.height = in_info.height; filter->in.stride = in_info.width * ((filter->in.bpp + 7)/8); filter->out.width = out_info.width; filter->out.height = out_info.height; filter->out.stride = out_info.stride[0]; if (filter->bpp_convert_buf) g_free(filter->bpp_convert_buf); if (filter->reduce_bpp && filter->in.bpp > 8) { filter->bpp_convert_buf = g_malloc(in_info.width * in_info.height); } return TRUE; } static GstCaps * gst_bayer2rgbneon_transform_caps(GstBaseTransform *base, GstPadDirection direction, GstCaps *caps, GstCaps *filter) { GstStructure *structure; GstCaps *newcaps; GstStructure *newstruct; GST_DEBUG_OBJECT(caps, "%s caps", direction == GST_PAD_SRC ? "src" : "sink"); GST_DEBUG_OBJECT(filter, "filter caps"); structure = gst_caps_get_structure (caps, 0); if (direction == GST_PAD_SRC) newcaps = gst_caps_from_string(SINK_CAPS); else newcaps = gst_caps_from_string(SRC_CAPS); if (filter) { newcaps = gst_caps_intersect(newcaps, filter); GST_DEBUG_OBJECT(newcaps, "filtered"); } newstruct = gst_caps_get_structure(newcaps, 0); gst_structure_set_value(newstruct, "width", gst_structure_get_value(structure, "width")); gst_structure_set_value(newstruct, "height", gst_structure_get_value(structure, "height")); gst_structure_set_value(newstruct, "framerate", gst_structure_get_value(structure, "framerate")); GST_DEBUG_OBJECT(newcaps, "result"); return newcaps; } static void set_quality_flag(struct bayer2rgbneon *filter, enum image_quality flag, GValue const *value) { if (!g_value_get_boolean(value)) filter->quality &= ~(1ul << flag); else filter->quality |= (1ul << flag); } static void get_quality_flag(struct bayer2rgbneon *filter, enum image_quality flag, GValue *value) { g_value_set_boolean(value, !!(filter->quality & (1ul << flag))); } static void gst_bayer2rgbneon_set_property(GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { struct bayer2rgbneon *filter = GST_BAYER2RGBNEON(object); switch (prop_id) { case PROP_SILENT: filter->silent = g_value_get_boolean(value); break; case PROP_SHOW_FPS: filter->show_fps = g_value_get_boolean(value); break; case PROP_REDUCE_BPP: filter->reduce_bpp = g_value_get_boolean(value); break; case PROP_NO_ACCEL: set_quality_flag(filter, QUALITY_NO_ACCEL, value); break; case PROP_ROUND_2: set_quality_flag(filter, QUALITY_ROUND_2, value); break; case PROP_ROUND_4: set_quality_flag(filter, QUALITY_ROUND_4, value); break; case PROP_PT: gst_base_transform_set_passthrough(&filter->element, g_value_get_boolean(value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } static void gst_bayer2rgbneon_get_property(GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { struct bayer2rgbneon *filter = GST_BAYER2RGBNEON(object); switch (prop_id) { case PROP_SILENT: g_value_set_boolean(value, filter->silent); break; case PROP_SHOW_FPS: g_value_set_boolean(value, filter->show_fps); break; case PROP_REDUCE_BPP: g_value_set_boolean(value, filter->reduce_bpp); break; case PROP_NO_ACCEL: get_quality_flag(filter, QUALITY_NO_ACCEL, value); break; case PROP_ROUND_2: get_quality_flag(filter, QUALITY_ROUND_2, value); break; case PROP_ROUND_4: get_quality_flag(filter, QUALITY_ROUND_4, value); break; case PROP_PT: g_value_set_boolean( value, gst_base_transform_is_passthrough(&filter->element)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } static gboolean gst_bayer2rgbneon_get_unit_size(GstBaseTransform *trans, GstCaps *caps, gsize *size) { struct bayer2rgbneon *filter = GST_BAYER2RGBNEON(trans); GstStructure *structure; char const *name; structure = gst_caps_get_structure(caps, 0); name = gst_structure_get_name (structure); if (strcmp(name, "video/x-bayer") == 0) *size = filter->in.stride * filter->in.height; else *size = filter->out.stride * filter->out.height; GST_DEBUG_OBJECT(caps, "unit size -> %zu", (size_t)(*size)); return true; } /* GstBaseTransform vmethod implementations */ static int64_t timeval_delta(struct timespec const *a, struct timespec const *b) { int64_t d; d = a->tv_sec; d -= b->tv_sec; d *= (int64_t)1000000000ull; d += a->tv_nsec; d -= b->tv_nsec; return d; } /* this function does the actual processing */ static GstFlowReturn gst_bayer2rgbneon_transform(GstBaseTransform * base, GstBuffer *inbuf, GstBuffer * outbuf) { struct bayer2rgbneon *filter = GST_BAYER2RGBNEON(base); GstBaseTransform *btrans = GST_BASE_TRANSFORM(filter); GstMapInfo inmap_info; GstMapInfo outmap_info; struct image_conversion_info cvt_info; struct image_in image_in = { .info = { .bpp = filter->in.bpp, .w = filter->in.width, .h = filter->in.height, .stride = filter->in.stride, .endian = filter->in.endian, }, .type = filter->in.format, }; struct image_out image_out = { .info = { .bpp = filter->out.bpp, .w = filter->out.width, .h = filter->out.height, .stride = filter->out.stride, }, .quality = filter->quality, .type = filter->out.type, }; struct timespec start; struct timespec end; if (GST_CLOCK_TIME_IS_VALID(GST_BUFFER_TIMESTAMP(outbuf))) gst_object_sync_values(GST_OBJECT(filter), GST_BUFFER_TIMESTAMP(outbuf)); if (gst_base_transform_is_passthrough (btrans)) return GST_FLOW_OK; if (!gst_buffer_map(inbuf, &inmap_info, GST_MAP_READ)) { GST_ERROR_OBJECT(filter, "failed to map inbuf"); return GST_FLOW_ERROR; } if (!gst_buffer_map(outbuf, &outmap_info, GST_MAP_WRITE)) { GST_ERROR_OBJECT(filter, "failed to map outbuf"); gst_buffer_unmap(inbuf, &inmap_info); return GST_FLOW_ERROR; } image_in.data = inmap_info.data;; image_out.data = outmap_info.data;; clock_gettime(g_clk, &start); if (filter->bpp_convert_buf && bayer2rgb_reduce_bpp(&image_in.info, inmap_info.data, filter->bpp_convert_buf, 8, NULL)) { image_in.data = filter->bpp_convert_buf; } bayer2rgb_convert(&image_in, &image_out, &cvt_info); clock_gettime(g_clk, &end); gst_buffer_unmap(outbuf, &outmap_info); gst_buffer_unmap(inbuf, &inmap_info); /* FIXME: do something interesting here. This simply copies the source * to the destination. */ if (filter->show_fps) { struct timespec now; if (!filter->info_shown) { printf("bayer2rgbneon: fn=%s (fallback due to '%s'), quality=%04lx\n", cvt_info.fn, cvt_info.fallback_reason, filter->quality); filter->info_shown = true; } filter->frame_cnt += 1; filter->conv_tm += timeval_delta(&end, &start); clock_gettime(CLOCK_MONOTONIC_RAW, &now); if (timeval_delta(&now, &filter->last_fps_tm) > 1000000000) { int64_t delta_conv = filter->conv_tm; int64_t delta_frm = timeval_delta(&now, &filter->last_fps_tm); delta_frm /= filter->frame_cnt; delta_conv /= filter->frame_cnt;; if (delta_conv && delta_frm) { printf("\r\33[2KFPS: conv %.1f, stream %.1f", (double)(1000000000.0) / delta_conv, (double)(1000000000.0) / delta_frm); fflush(stdout); } filter->last_fps_tm = now; filter->frame_cnt = 0; filter->conv_tm = 0; } } return GST_FLOW_OK; } /* initialize the bayer2rgbneon's class */ static void gst_bayer2rgbneon_class_init(struct bayer2rgbneon_class *klass) { GObjectClass *gobject_class; GstElementClass *gstelement_class; gobject_class = (GObjectClass *) klass; gstelement_class = (GstElementClass *) klass; gobject_class->set_property = gst_bayer2rgbneon_set_property; gobject_class->get_property = gst_bayer2rgbneon_get_property; gobject_class->finalize = gst_bayer2rgbneon_finalize; g_object_class_install_property( gobject_class, PROP_SILENT, g_param_spec_boolean("silent", "Silent", "Produce verbose output?", FALSE, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE)); g_object_class_install_property( gobject_class, PROP_PT, g_param_spec_boolean("passthrough", "flag", "sets passthrough mode", FALSE, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE)); g_object_class_install_property( gobject_class, PROP_SHOW_FPS, g_param_spec_boolean("show-fps", "flag", "Show fps", FALSE, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE)); g_object_class_install_property( gobject_class, PROP_NO_ACCEL, g_param_spec_boolean("no-accel", "flag", "do not use hw acceleration ", FALSE, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE)); g_object_class_install_property( gobject_class, PROP_ROUND_2, g_param_spec_boolean("round2", "flag", "round a merge result of pixel pairs", FALSE, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE)); g_object_class_install_property( gobject_class, PROP_ROUND_4, g_param_spec_boolean("round4", "flag", "round a merge result of pixel quadruples", FALSE, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE)); g_object_class_install_property( gobject_class, PROP_REDUCE_BPP, g_param_spec_boolean("reduce-bpp", "flag", "reduce bpp to 8bpp", FALSE, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE)); gst_element_class_set_details_simple( gstelement_class, "bayer2rgbneon", "Filter/Converter/Video", "yet another bayer2rgb conversion filter", "Enrico Scholz "); gst_element_class_add_pad_template( gstelement_class, gst_static_pad_template_get(&sink_template)); gst_element_class_add_pad_template( gstelement_class, gst_static_pad_template_get(&src_template)); GST_BASE_TRANSFORM_CLASS(klass)->transform = GST_DEBUG_FUNCPTR(gst_bayer2rgbneon_transform); GST_BASE_TRANSFORM_CLASS(klass)->set_caps = GST_DEBUG_FUNCPTR(gst_bayer2rgbneon_set_caps); GST_BASE_TRANSFORM_CLASS(klass)->transform_caps = GST_DEBUG_FUNCPTR(gst_bayer2rgbneon_transform_caps); GST_BASE_TRANSFORM_CLASS(klass)->get_unit_size = GST_DEBUG_FUNCPTR(gst_bayer2rgbneon_get_unit_size); /* debug category for fltering log messages * * FIXME:exchange the string 'Template bayer2rgbneon' with your description */ GST_DEBUG_CATEGORY_INIT(gst_bayer2rgbneon_debug, "bayer2rgbneon", 0, "bayer2rgbneon"); } /* entry point to initialize the plug-in * initialize the plug-in itself * register the element factories and other features */ static gboolean bayer2rgbneon_init(GstPlugin * bayer2rgbneon) { return gst_element_register(bayer2rgbneon, "bayer2rgbneon", GST_RANK_SECONDARY, GST_TYPE_BAYER2RGBNEON); } /* gstreamer looks for this structure to register bayer2rgbneons * * FIXME:exchange the string 'Template bayer2rgbneon' with you bayer2rgbneon description */ GST_PLUGIN_DEFINE(GST_VERSION_MAJOR, GST_VERSION_MINOR, bayer2rgbneon, "Template bayer2rgbneon", bayer2rgbneon_init, VERSION, "LGPL", "GStreamer", "http://gstreamer.net/")