Tizen 2.0 Release
[framework/multimedia/gst-plugins-bad0.10.git] / ext / resindvd / rsnparsetter.c
1 /*
2  * Copyright (C) 2008 Jan Schmidt <thaytan@noraisin.net>
3  */
4
5 #ifdef HAVE_CONFIG_H
6 #  include <config.h>
7 #endif
8
9 #include <gst/gst.h>
10 #include <gst/video/video.h>
11 #include <string.h>
12
13 #include "rsnparsetter.h"
14 #include "rsnwrappedbuffer.h"
15
16 GST_DEBUG_CATEGORY_STATIC (rsn_parsetter_debug);
17 #define GST_CAT_DEFAULT rsn_parsetter_debug
18
19 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
20     GST_PAD_SINK,
21     GST_PAD_ALWAYS,
22     GST_STATIC_CAPS ("video/x-raw-rgb; video/x-raw-yuv")
23     );
24
25 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
26     GST_PAD_SRC,
27     GST_PAD_ALWAYS,
28     GST_STATIC_CAPS ("video/x-raw-rgb; video/x-raw-yuv")
29     );
30
31 static void rsn_parsetter_register_extra (GType rsn_parsetter_type);
32
33 GST_BOILERPLATE_FULL (RsnParSetter, rsn_parsetter, GstElement,
34     GST_TYPE_ELEMENT, rsn_parsetter_register_extra);
35
36 static void rsn_parsetter_finalize (GObject * object);
37 static GstFlowReturn rsn_parsetter_chain (GstPad * pad, GstBuffer * buf);
38 static gboolean rsn_parsetter_sink_event (GstPad * pad, GstEvent * event);
39 static gboolean rsn_parsetter_sink_setcaps (GstPad * pad, GstCaps * caps);
40 static GstFlowReturn rsn_parsetter_sink_bufferalloc (GstPad * pad,
41     guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf);
42
43 static GstCaps *rsn_parsetter_src_getcaps (GstPad * pad);
44 static GstCaps *rsn_parsetter_convert_caps (RsnParSetter * parset,
45     GstCaps * caps, gboolean widescreen);
46 static gboolean rsn_parsetter_check_caps (RsnParSetter * parset,
47     GstCaps * caps);
48
49 static void
50 rsn_parsetter_register_extra (GType rsn_parsetter_type)
51 {
52   GST_DEBUG_CATEGORY_INIT (rsn_parsetter_debug, "rsnparsetter", 0,
53       "Resin DVD aspect ratio adjuster");
54 }
55
56 static void
57 rsn_parsetter_base_init (gpointer gclass)
58 {
59
60   GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
61
62   gst_element_class_add_static_pad_template (element_class, &src_factory);
63   gst_element_class_add_static_pad_template (element_class, &sink_factory);
64   gst_element_class_set_details_simple (element_class,
65       "Resin Aspect Ratio Setter", "Filter/Video",
66       "Overrides caps on video buffers to force a particular display ratio",
67       "Jan Schmidt <thaytan@noraisin.net>");
68 }
69
70 static void
71 rsn_parsetter_class_init (RsnParSetterClass * klass)
72 {
73   GObjectClass *gobject_class;
74
75   gobject_class = (GObjectClass *) klass;
76
77   gobject_class->finalize = rsn_parsetter_finalize;
78 }
79
80 static void
81 rsn_parsetter_init (RsnParSetter * parset, RsnParSetterClass * gclass)
82 {
83   parset->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
84   gst_pad_set_getcaps_function (parset->sinkpad,
85       GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps));
86   gst_pad_set_chain_function (parset->sinkpad,
87       GST_DEBUG_FUNCPTR (rsn_parsetter_chain));
88   gst_pad_set_event_function (parset->sinkpad,
89       GST_DEBUG_FUNCPTR (rsn_parsetter_sink_event));
90   gst_pad_set_setcaps_function (parset->sinkpad,
91       GST_DEBUG_FUNCPTR (rsn_parsetter_sink_setcaps));
92   gst_pad_set_bufferalloc_function (parset->sinkpad,
93       GST_DEBUG_FUNCPTR (rsn_parsetter_sink_bufferalloc));
94   gst_element_add_pad (GST_ELEMENT (parset), parset->sinkpad);
95
96   parset->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
97   gst_pad_set_getcaps_function (parset->srcpad,
98       GST_DEBUG_FUNCPTR (rsn_parsetter_src_getcaps));
99   gst_element_add_pad (GST_ELEMENT (parset), parset->srcpad);
100
101   parset->caps_lock = g_mutex_new ();
102 }
103
104 static void
105 rsn_parsetter_finalize (GObject * object)
106 {
107   RsnParSetter *parset = RSN_PARSETTER (object);
108
109   gst_caps_replace (&parset->outcaps, NULL);
110   gst_caps_replace (&parset->in_caps_last, NULL);
111   gst_caps_replace (&parset->in_caps_converted, NULL);
112
113   g_mutex_free (parset->caps_lock);
114
115   G_OBJECT_CLASS (parent_class)->finalize (object);
116 }
117
118 static GstFlowReturn
119 rsn_parsetter_chain (GstPad * pad, GstBuffer * buf)
120 {
121   RsnParSetter *parset = RSN_PARSETTER (GST_OBJECT_PARENT (pad));
122
123   /* If this is a buffer we wrapped up earlier, unwrap it now */
124   if (RSN_IS_WRAPPEDBUFFER (buf)) {
125     RsnWrappedBuffer *wrap_buf = RSN_WRAPPEDBUFFER (buf);
126
127     if (wrap_buf->owner == GST_ELEMENT (parset)) {
128       buf = rsn_wrappedbuffer_unwrap_and_unref (wrap_buf);
129       GST_DEBUG_OBJECT (parset, "Unwrapping %p yields buffer %p with caps %"
130           GST_PTR_FORMAT, wrap_buf, buf, GST_BUFFER_CAPS (buf));
131     }
132   }
133
134   if (parset->outcaps != GST_BUFFER_CAPS (buf)) {
135     if (parset->override_outcaps == FALSE &&
136         gst_caps_is_equal (parset->outcaps, GST_BUFFER_CAPS (buf))) {
137       /* Just update our output caps var */
138       gst_caps_replace (&parset->outcaps, GST_BUFFER_CAPS (buf));
139       goto out;
140     }
141
142     /* Replace the caps on the output buffer */
143     buf = gst_buffer_make_metadata_writable (buf);
144     gst_buffer_set_caps (buf, parset->outcaps);
145
146     GST_DEBUG_OBJECT (parset,
147         "Replacing caps on buffer %p with caps %" GST_PTR_FORMAT,
148         buf, parset->outcaps);
149   }
150
151 out:
152   return gst_pad_push (parset->srcpad, buf);
153 }
154
155 static gboolean
156 rsn_parsetter_sink_event (GstPad * pad, GstEvent * event)
157 {
158   RsnParSetter *parset = RSN_PARSETTER (gst_pad_get_parent (pad));
159   const GstStructure *structure = gst_event_get_structure (event);
160
161   if (structure != NULL &&
162       gst_structure_has_name (structure, "application/x-gst-dvd")) {
163     const char *type = gst_structure_get_string (structure, "event");
164     if (type == NULL)
165       goto out;
166
167     if (strcmp (type, "dvd-video-format") == 0) {
168       gboolean is_widescreen;
169
170       gst_structure_get_boolean (structure, "video-widescreen", &is_widescreen);
171
172       GST_DEBUG_OBJECT (parset, "Video is %s",
173           parset->is_widescreen ? "16:9" : "4:3");
174
175       g_mutex_lock (parset->caps_lock);
176       if (parset->is_widescreen != is_widescreen) {
177         /* Force caps check */
178         gst_caps_replace (&parset->in_caps_last, NULL);
179         gst_caps_replace (&parset->in_caps_converted, NULL);
180       }
181       parset->is_widescreen = is_widescreen;
182
183       /* FIXME: Added for testing: */
184       // parset->is_widescreen = FALSE;
185
186       g_mutex_unlock (parset->caps_lock);
187     }
188   }
189
190 out:
191   gst_object_unref (GST_OBJECT (parset));
192   return gst_pad_event_default (pad, event);
193 }
194
195 static GstCaps *
196 rsn_parsetter_src_getcaps (GstPad * pad)
197 {
198   RsnParSetter *parset = RSN_PARSETTER (gst_pad_get_parent (pad));
199   GstCaps *ret;
200   const GstCaps *templ_caps = gst_pad_get_pad_template_caps (pad);
201
202   ret = gst_pad_peer_get_caps (parset->sinkpad);
203   if (ret == NULL)
204     ret = gst_caps_copy (templ_caps);
205   else {
206     GstCaps *temp;
207     temp = gst_caps_intersect (templ_caps, ret);
208     gst_caps_unref (ret);
209     ret = rsn_parsetter_convert_caps (parset, temp, parset->is_widescreen);
210     gst_caps_unref (temp);
211   }
212
213   gst_object_unref (parset);
214   return ret;
215 }
216
217 static gboolean
218 rsn_parsetter_check_caps (RsnParSetter * parset, GstCaps * caps)
219 {
220   GstStructure *s;
221   gint width, height;
222   gint par_n, par_d;
223   guint dar_n, dar_d;
224   gboolean ret = FALSE;
225
226   g_mutex_lock (parset->caps_lock);
227
228   if (caps == parset->in_caps_last ||
229       gst_caps_is_equal (caps, parset->in_caps_last)) {
230     ret = parset->in_caps_was_ok;
231     goto out;
232   }
233
234   /* Calculate the DAR from the incoming caps, and return TRUE if it matches
235    * the required DAR, FALSE if not */
236   s = gst_caps_get_structure (caps, 0);
237   if (s == NULL)
238     goto out;
239
240   if (!gst_structure_get_int (s, "width", &width) ||
241       !gst_structure_get_int (s, "height", &height))
242     goto out;
243
244   if (!gst_structure_get_fraction (s, "pixel-aspect-ratio", &par_n, &par_d))
245     par_n = par_d = 1;
246
247   if (!gst_video_calculate_display_ratio (&dar_n, &dar_d, width, height,
248           par_n, par_d, 1, 1))
249     goto out;
250
251   GST_DEBUG_OBJECT (parset,
252       "Incoming video caps now: w %d h %d PAR %d/%d = DAR %d/%d",
253       width, height, par_n, par_d, dar_n, dar_d);
254
255   if (parset->is_widescreen) {
256     if (dar_n == 16 && dar_d == 9)
257       ret = TRUE;
258   } else {
259     if (dar_n == 4 && dar_d == 3)
260       ret = TRUE;
261   }
262
263   gst_caps_replace (&parset->in_caps_last, caps);
264   gst_caps_replace (&parset->in_caps_converted, NULL);
265   parset->in_caps_was_ok = ret;
266
267 out:
268   g_mutex_unlock (parset->caps_lock);
269   return ret;
270 }
271
272 static GstCaps *
273 rsn_parsetter_convert_caps (RsnParSetter * parset, GstCaps * caps,
274     gboolean widescreen)
275 {
276   /* Duplicate the given caps, with a PAR that provides the desired DAR */
277   GstCaps *outcaps;
278   GstStructure *s;
279   gint width, height;
280   gint par_n, par_d;
281   guint dar_n, dar_d;
282   GValue par = { 0, };
283
284   g_mutex_lock (parset->caps_lock);
285   if (caps == parset->in_caps_last && parset->in_caps_converted) {
286     outcaps = gst_caps_ref (parset->in_caps_converted);
287     goto out;
288   }
289
290   outcaps = gst_caps_copy (caps);
291
292   /* Calculate the DAR from the incoming caps, and return TRUE if it matches
293    * the required DAR, FALSE if not */
294   s = gst_caps_get_structure (outcaps, 0);
295   if (s == NULL)
296     goto out;
297
298   if (!gst_structure_get_int (s, "width", &width) ||
299       !gst_structure_get_int (s, "height", &height))
300     goto out;
301
302   if (widescreen) {
303     dar_n = 16;
304     dar_d = 9;
305   } else {
306     dar_n = 4;
307     dar_d = 3;
308   }
309
310   par_n = dar_n * height;
311   par_d = dar_d * width;
312
313   g_value_init (&par, GST_TYPE_FRACTION);
314   gst_value_set_fraction (&par, par_n, par_d);
315   gst_structure_set_value (s, "pixel-aspect-ratio", &par);
316   g_value_unset (&par);
317
318   gst_caps_replace (&parset->in_caps_converted, outcaps);
319 out:
320   g_mutex_unlock (parset->caps_lock);
321   return outcaps;
322 }
323
324 static gboolean
325 rsn_parsetter_sink_setcaps (GstPad * pad, GstCaps * caps)
326 {
327   /* Check the new incoming caps against our current DAR, and mark
328    * whether the buffers will need adjusting */
329   RsnParSetter *parset = RSN_PARSETTER (gst_pad_get_parent (pad));
330
331   if (rsn_parsetter_check_caps (parset, caps)) {
332     parset->override_outcaps = FALSE;
333     gst_caps_replace (&parset->outcaps, caps);
334   } else {
335     GstCaps *override_caps = rsn_parsetter_convert_caps (parset, caps,
336         parset->is_widescreen);
337     if (parset->outcaps)
338       gst_caps_unref (parset->outcaps);
339     parset->outcaps = override_caps;
340
341     parset->override_outcaps = TRUE;
342   }
343
344   GST_DEBUG_OBJECT (parset, "caps changed: need_override now = %d",
345       parset->override_outcaps);
346
347   gst_object_unref (parset);
348   return TRUE;
349 }
350
351 static GstFlowReturn
352 rsn_parsetter_sink_bufferalloc (GstPad * pad, guint64 offset, guint size,
353     GstCaps * caps, GstBuffer ** buf)
354 {
355   RsnParSetter *parset = RSN_PARSETTER (gst_pad_get_parent (pad));
356   GstFlowReturn ret;
357
358   GST_LOG_OBJECT (parset, "Entering bufferalloc");
359
360   if (rsn_parsetter_check_caps (parset, caps)) {
361     ret = gst_pad_alloc_buffer (parset->srcpad, offset, size, caps, buf);
362     GST_LOG_OBJECT (parset, "Not wrapping buf %p", *buf);
363   } else {
364     /* Allocate and wrap a downstream buffer */
365     GstBuffer *orig_buf;
366     GstBuffer *outbuf;
367     GstCaps *override_caps = rsn_parsetter_convert_caps (parset, caps,
368         parset->is_widescreen);
369
370     ret = gst_pad_alloc_buffer (parset->srcpad, offset, size,
371         override_caps, &orig_buf);
372     gst_caps_unref (override_caps);
373
374     if (ret != GST_FLOW_OK)
375       return ret;
376
377     outbuf = (GstBuffer *) rsn_wrapped_buffer_new (orig_buf);
378     if (!outbuf) {
379       /* FIXME: Throw error */
380       return GST_FLOW_ERROR;
381     }
382
383     rsn_wrapped_buffer_set_owner (RSN_WRAPPEDBUFFER (outbuf),
384         GST_ELEMENT (parset));
385
386     gst_buffer_set_caps (outbuf, caps);
387
388     GST_LOG_OBJECT (parset,
389         "Wrapped ds buf %p with caps %" GST_PTR_FORMAT
390         " into new buf %p with caps %" GST_PTR_FORMAT,
391         orig_buf, GST_BUFFER_CAPS (orig_buf), outbuf, GST_BUFFER_CAPS (outbuf));
392
393     *buf = outbuf;
394   }
395
396   gst_object_unref (GST_OBJECT (parset));
397
398   return ret;
399 }