| /* |
| * Copyright (C) 2008 Jan Schmidt <thaytan@noraisin.net> |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| # include <config.h> |
| #endif |
| |
| #include <gst/gst.h> |
| #include <gst/glib-compat-private.h> |
| #include <gst/video/video.h> |
| #include <string.h> |
| |
| #include "rsnparsetter.h" |
| |
| GST_DEBUG_CATEGORY_STATIC (rsn_parsetter_debug); |
| #define GST_CAT_DEFAULT rsn_parsetter_debug |
| |
| static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", |
| GST_PAD_SINK, |
| GST_PAD_ALWAYS, |
| GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (GST_VIDEO_FORMATS_ALL)) |
| ); |
| |
| static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", |
| GST_PAD_SRC, |
| GST_PAD_ALWAYS, |
| GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (GST_VIDEO_FORMATS_ALL)) |
| ); |
| |
| #define rsn_parsetter_parent_class parent_class |
| G_DEFINE_TYPE_WITH_CODE (RsnParSetter, rsn_parsetter, GST_TYPE_ELEMENT, |
| GST_DEBUG_CATEGORY_INIT (rsn_parsetter_debug, "rsnparsetter", 0, |
| "Resin DVD aspect ratio adjuster")); |
| |
| static void rsn_parsetter_finalize (GObject * object); |
| static GstFlowReturn rsn_parsetter_chain (GstPad * pad, |
| RsnParSetter * parset, GstBuffer * buf); |
| static gboolean rsn_parsetter_sink_event (GstPad * pad, |
| RsnParSetter * parset, GstEvent * event); |
| |
| static gboolean rsn_parsetter_src_query (GstPad * pad, RsnParSetter * parset, |
| GstQuery * query); |
| static GstCaps *rsn_parsetter_convert_caps (RsnParSetter * parset, |
| GstCaps * caps, gboolean widescreen); |
| static gboolean rsn_parsetter_check_caps (RsnParSetter * parset, |
| GstCaps * caps); |
| static void rsn_parsetter_update_caps (RsnParSetter * parset, GstCaps * caps); |
| |
| static void |
| rsn_parsetter_class_init (RsnParSetterClass * klass) |
| { |
| GObjectClass *gobject_class = G_OBJECT_CLASS (klass); |
| GstElementClass *element_class = GST_ELEMENT_CLASS (klass); |
| |
| gobject_class->finalize = rsn_parsetter_finalize; |
| |
| gst_element_class_add_pad_template (element_class, |
| gst_static_pad_template_get (&src_factory)); |
| gst_element_class_add_pad_template (element_class, |
| gst_static_pad_template_get (&sink_factory)); |
| |
| gst_element_class_set_static_metadata (element_class, |
| "Resin Aspect Ratio Setter", "Filter/Video", |
| "Overrides caps on video buffers to force a particular display ratio", |
| "Jan Schmidt <thaytan@noraisin.net>"); |
| } |
| |
| static void |
| rsn_parsetter_init (RsnParSetter * parset) |
| { |
| parset->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink"); |
| gst_pad_set_chain_function (parset->sinkpad, |
| (GstPadChainFunction) GST_DEBUG_FUNCPTR (rsn_parsetter_chain)); |
| gst_pad_set_event_function (parset->sinkpad, |
| (GstPadEventFunction) GST_DEBUG_FUNCPTR (rsn_parsetter_sink_event)); |
| GST_PAD_SET_PROXY_CAPS (parset->sinkpad); |
| GST_PAD_SET_PROXY_ALLOCATION (parset->sinkpad); |
| gst_element_add_pad (GST_ELEMENT (parset), parset->sinkpad); |
| |
| parset->srcpad = gst_pad_new_from_static_template (&src_factory, "src"); |
| gst_pad_set_query_function (parset->srcpad, |
| (GstPadQueryFunction) GST_DEBUG_FUNCPTR (rsn_parsetter_src_query)); |
| GST_PAD_SET_PROXY_CAPS (parset->srcpad); |
| gst_element_add_pad (GST_ELEMENT (parset), parset->srcpad); |
| } |
| |
| static void |
| rsn_parsetter_finalize (GObject * object) |
| { |
| RsnParSetter *parset = RSN_PARSETTER (object); |
| |
| gst_caps_replace (&parset->outcaps, NULL); |
| gst_caps_replace (&parset->in_caps_last, NULL); |
| gst_caps_replace (&parset->in_caps_converted, NULL); |
| |
| G_OBJECT_CLASS (parent_class)->finalize (object); |
| } |
| |
| static GstFlowReturn |
| rsn_parsetter_chain (GstPad * pad, RsnParSetter * parset, GstBuffer * buf) |
| { |
| return gst_pad_push (parset->srcpad, buf); |
| } |
| |
| static gboolean |
| rsn_parsetter_sink_event (GstPad * pad, RsnParSetter * parset, GstEvent * event) |
| { |
| switch (GST_EVENT_TYPE (event)) { |
| case GST_EVENT_CUSTOM_DOWNSTREAM:{ |
| const GstStructure *structure = gst_event_get_structure (event); |
| |
| if (structure != NULL && |
| gst_structure_has_name (structure, "application/x-gst-dvd")) { |
| const char *type = gst_structure_get_string (structure, "event"); |
| GstEvent *caps_event = NULL; |
| |
| if (type == NULL) |
| goto out; |
| |
| if (strcmp (type, "dvd-video-format") == 0) { |
| gboolean is_widescreen; |
| |
| gst_structure_get_boolean (structure, "video-widescreen", |
| &is_widescreen); |
| |
| GST_DEBUG_OBJECT (parset, "Video is %s", |
| parset->is_widescreen ? "16:9" : "4:3"); |
| |
| if (parset->in_caps_last && parset->is_widescreen != is_widescreen) { |
| /* Force caps check */ |
| gst_caps_replace (&parset->in_caps_converted, NULL); |
| rsn_parsetter_update_caps (parset, parset->in_caps_last); |
| if (parset->override_outcaps) |
| caps_event = gst_event_new_caps (parset->outcaps); |
| } |
| parset->is_widescreen = is_widescreen; |
| |
| /* FIXME: Added for testing: */ |
| // parset->is_widescreen = FALSE; |
| |
| if (caps_event) |
| gst_pad_push_event (parset->srcpad, caps_event); |
| } |
| } |
| break; |
| } |
| case GST_EVENT_CAPS: |
| { |
| GstCaps *caps = NULL; |
| gst_event_parse_caps (event, &caps); |
| rsn_parsetter_update_caps (parset, caps); |
| if (parset->override_outcaps) { |
| gst_event_unref (event); |
| GST_DEBUG_OBJECT (parset, |
| "Handling caps event. Overriding upstream caps" |
| " with %" GST_PTR_FORMAT, parset->outcaps); |
| event = gst_event_new_caps (parset->outcaps); |
| } else { |
| GST_DEBUG_OBJECT (parset, |
| "Handling caps event. Upstream caps %" GST_PTR_FORMAT |
| " acceptable", caps); |
| } |
| break; |
| } |
| default: |
| break; |
| } |
| |
| out: |
| return gst_pad_event_default (pad, (GstObject *) (parset), event); |
| } |
| |
| static gboolean |
| rsn_parsetter_src_query (GstPad * pad, RsnParSetter * parset, GstQuery * query) |
| { |
| GstCaps *caps = NULL; |
| |
| if (!gst_pad_peer_query (parset->sinkpad, query)) |
| return FALSE; |
| |
| if (GST_QUERY_TYPE (query) != GST_QUERY_CAPS) |
| return TRUE; |
| |
| gst_query_parse_caps_result (query, &caps); |
| |
| GST_DEBUG_OBJECT (parset, "Handling caps query. Upstream caps %" |
| GST_PTR_FORMAT, caps); |
| |
| if (caps == NULL) { |
| GstCaps *templ_caps = gst_pad_get_pad_template_caps (pad); |
| gst_query_set_caps_result (query, templ_caps); |
| gst_caps_unref (templ_caps); |
| } else { |
| caps = rsn_parsetter_convert_caps (parset, caps, parset->is_widescreen); |
| gst_query_set_caps_result (query, caps); |
| gst_caps_unref (caps); |
| } |
| |
| return TRUE; |
| } |
| |
| /* Check if the DAR of the passed matches the required DAR */ |
| static gboolean |
| rsn_parsetter_check_caps (RsnParSetter * parset, GstCaps * caps) |
| { |
| GstStructure *s; |
| gint width, height; |
| gint par_n, par_d; |
| guint dar_n, dar_d; |
| gboolean ret = FALSE; |
| |
| if (parset->in_caps_last && |
| (caps == parset->in_caps_last || |
| gst_caps_is_equal (caps, parset->in_caps_last))) { |
| ret = parset->in_caps_was_ok; |
| goto out; |
| } |
| |
| /* Calculate the DAR from the incoming caps, and return TRUE if it matches |
| * the required DAR, FALSE if not */ |
| s = gst_caps_get_structure (caps, 0); |
| if (s == NULL) |
| goto out; |
| |
| if (!gst_structure_get_int (s, "width", &width) || |
| !gst_structure_get_int (s, "height", &height)) |
| goto out; |
| |
| if (!gst_structure_get_fraction (s, "pixel-aspect-ratio", &par_n, &par_d)) |
| par_n = par_d = 1; |
| |
| if (!gst_video_calculate_display_ratio (&dar_n, &dar_d, width, height, |
| par_n, par_d, 1, 1)) |
| goto out; |
| |
| GST_DEBUG_OBJECT (parset, |
| "Incoming video caps now: w %d h %d PAR %d/%d = DAR %d/%d", |
| width, height, par_n, par_d, dar_n, dar_d); |
| |
| if (parset->is_widescreen) { |
| if (dar_n == 16 && dar_d == 9) |
| ret = TRUE; |
| } else { |
| if (dar_n == 4 && dar_d == 3) |
| ret = TRUE; |
| } |
| |
| gst_caps_replace (&parset->in_caps_last, caps); |
| gst_caps_replace (&parset->in_caps_converted, NULL); |
| parset->in_caps_was_ok = ret; |
| |
| out: |
| return ret; |
| } |
| |
| static GstCaps * |
| rsn_parsetter_convert_caps (RsnParSetter * parset, GstCaps * caps, |
| gboolean widescreen) |
| { |
| /* Duplicate the given caps, with a PAR that provides the desired DAR */ |
| GstCaps *outcaps; |
| GstStructure *s; |
| gint width, height; |
| gint par_n, par_d; |
| guint dar_n, dar_d; |
| GValue par = { 0, }; |
| |
| if (caps == parset->in_caps_last && parset->in_caps_converted) { |
| outcaps = gst_caps_ref (parset->in_caps_converted); |
| goto out; |
| } |
| |
| outcaps = gst_caps_copy (caps); |
| |
| /* Calculate the DAR from the incoming caps, and return TRUE if it matches |
| * the required DAR, FALSE if not */ |
| s = gst_caps_get_structure (outcaps, 0); |
| if (s == NULL) |
| goto out; |
| |
| if (!gst_structure_get_int (s, "width", &width) || |
| !gst_structure_get_int (s, "height", &height)) |
| goto out; |
| |
| if (widescreen) { |
| dar_n = 16; |
| dar_d = 9; |
| } else { |
| dar_n = 4; |
| dar_d = 3; |
| } |
| |
| par_n = dar_n * height; |
| par_d = dar_d * width; |
| |
| g_value_init (&par, GST_TYPE_FRACTION); |
| gst_value_set_fraction (&par, par_n, par_d); |
| gst_structure_set_value (s, "pixel-aspect-ratio", &par); |
| g_value_unset (&par); |
| |
| gst_caps_replace (&parset->in_caps_converted, outcaps); |
| out: |
| return outcaps; |
| } |
| |
| static void |
| rsn_parsetter_update_caps (RsnParSetter * parset, GstCaps * caps) |
| { |
| /* Check the new incoming caps against our current DAR, and mark |
| * whether the caps need adjusting */ |
| if (rsn_parsetter_check_caps (parset, caps)) { |
| parset->override_outcaps = FALSE; |
| gst_caps_replace (&parset->outcaps, caps); |
| } else { |
| GstCaps *override_caps = rsn_parsetter_convert_caps (parset, caps, |
| parset->is_widescreen); |
| if (parset->outcaps) |
| gst_caps_unref (parset->outcaps); |
| parset->outcaps = override_caps; |
| |
| parset->override_outcaps = TRUE; |
| } |
| |
| GST_DEBUG_OBJECT (parset, "caps changed: need_override now = %d", |
| parset->override_outcaps); |
| } |