base_video_dec: Forward GstQuery upstream
[gstreamer-omap:gst-openmax.git] / omx / gstomx_base_videodec.c
1 /*
2  * Copyright (C) 2007-2009 Nokia Corporation.
3  *
4  * Author: Felipe Contreras <felipe.contreras@nokia.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation
9  * version 2.1 of the License.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #include "gstomx_base_videodec.h"
23 #include "gstomx.h"
24
25 #include <gst/video/video.h>
26
27 #ifdef USE_OMXTICORE
28 #  include <OMX_TI_Index.h>
29 #endif
30
31 #include <string.h> /* for memset */
32
33 GSTOMX_BOILERPLATE (GstOmxBaseVideoDec, gst_omx_base_videodec, GstOmxBaseFilter, GST_OMX_BASE_FILTER_TYPE);
34
35 /* OMX component not handling other color formats properly.. use this workaround
36  * until component is fixed or we rebase to get config file support..
37  */
38 #define VIDDEC_COLOR_WORKAROUND
39 #ifdef VIDDEC_COLOR_WORKAROUND
40 #  undef GSTOMX_ALL_FORMATS
41 #  define GSTOMX_ALL_FORMATS  "{NV12}"
42 #endif
43
44 static GstStaticPadTemplate src_template =
45         GST_STATIC_PAD_TEMPLATE ("src",
46                 GST_PAD_SRC,
47                 GST_PAD_ALWAYS,
48                 GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV_STRIDED (
49                         GSTOMX_ALL_FORMATS, "[ 0, max ]"))
50         );
51
52 static GstFlowReturn push_buffer (GstOmxBaseFilter *self, GstBuffer *buf);
53
54 static void
55 type_base_init (gpointer g_class)
56 {
57     GstElementClass *element_class;
58
59     element_class = GST_ELEMENT_CLASS (g_class);
60
61     gst_element_class_add_pad_template (element_class,
62         gst_static_pad_template_get (&src_template));
63 }
64
65 static void
66 type_class_init (gpointer g_class,
67                  gpointer class_data)
68 {
69     GST_OMX_BASE_FILTER_CLASS (g_class)->push_buffer = push_buffer;
70 }
71
72 static GstFlowReturn
73 push_buffer (GstOmxBaseFilter *omx_base, GstBuffer *buf)
74 {
75     GstOmxBaseVideoDec *self = GST_OMX_BASE_VIDEODEC (omx_base);
76     guint n_offset = omx_base->out_port->n_offset;
77     if (n_offset)
78     {
79         gst_pad_push_event (omx_base->srcpad,
80                 gst_event_new_crop (n_offset / self->rowstride, /* top */
81                         n_offset % self->rowstride, /* left */
82                         -1, -1)); /* width/height: can be invalid for now */
83     }
84     return parent_class->push_buffer (omx_base, buf);
85 }
86
87 static void
88 settings_changed_cb (GOmxCore *core)
89 {
90     GstOmxBaseFilter *omx_base;
91     GstOmxBaseVideoDec *self;
92     GstCaps *new_caps;
93
94     omx_base = core->object;
95     self = GST_OMX_BASE_VIDEODEC (omx_base);
96
97     GST_DEBUG_OBJECT (omx_base, "settings changed");
98
99     new_caps = gst_caps_intersect (gst_pad_get_caps (omx_base->srcpad),
100            gst_pad_peer_get_caps (omx_base->srcpad));
101
102     if (!gst_caps_is_fixed (new_caps))
103     {
104         gst_caps_do_simplify (new_caps);
105         GST_INFO_OBJECT (omx_base, "pre-fixated caps: %" GST_PTR_FORMAT, new_caps);
106         gst_pad_fixate_caps (omx_base->srcpad, new_caps);
107     }
108
109     GST_INFO_OBJECT (omx_base, "caps are: %" GST_PTR_FORMAT, new_caps);
110     GST_INFO_OBJECT (omx_base, "old caps are: %" GST_PTR_FORMAT, GST_PAD_CAPS (omx_base->srcpad));
111
112     gst_pad_set_caps (omx_base->srcpad, new_caps);
113 }
114
115 static gboolean
116 sink_setcaps (GstPad *pad,
117               GstCaps *caps)
118 {
119     GstStructure *structure;
120     GstOmxBaseVideoDec *self;
121     GstOmxBaseFilter *omx_base;
122     GOmxCore *gomx;
123
124     gint width = 0;
125     gint height = 0;
126
127     self = GST_OMX_BASE_VIDEODEC (GST_PAD_PARENT (pad));
128     omx_base = GST_OMX_BASE_FILTER (self);
129
130     gomx = (GOmxCore *) omx_base->gomx;
131
132     GST_INFO_OBJECT (self, "setcaps (sink): %" GST_PTR_FORMAT, caps);
133
134     g_return_val_if_fail (caps, FALSE);
135     g_return_val_if_fail (gst_caps_is_fixed (caps), FALSE);
136
137     structure = gst_caps_get_structure (caps, 0);
138
139     g_return_val_if_fail (structure, FALSE);
140
141     if (!(gst_structure_get_int (structure, "width", &width) &&
142             gst_structure_get_int (structure, "height", &height)))
143     {
144         GST_WARNING_OBJECT (self, "width and/or height not set in caps: %dx%d",
145                 width, height);
146         return FALSE;
147     }
148
149     {
150         const GValue *framerate = NULL;
151         framerate = gst_structure_get_value (structure, "framerate");
152         if (framerate)
153         {
154             self->framerate_num = gst_value_get_fraction_numerator (framerate);
155             self->framerate_denom = gst_value_get_fraction_denominator (framerate);
156
157             omx_base->duration = gst_util_uint64_scale_int(GST_SECOND,
158                     gst_value_get_fraction_denominator (framerate),
159                     gst_value_get_fraction_numerator (framerate));
160             GST_DEBUG_OBJECT (self, "Nominal frame duration =%"GST_TIME_FORMAT,
161                                 GST_TIME_ARGS (omx_base->duration));
162         }
163     }
164
165     {
166         const GValue *codec_data;
167         GstBuffer *buffer;
168
169         codec_data = gst_structure_get_value (structure, "codec_data");
170         if (codec_data)
171         {
172             buffer = gst_value_get_buffer (codec_data);
173             omx_base->codec_data = buffer;
174             gst_buffer_ref (buffer);
175         }
176     }
177
178     /* Input port configuration. */
179     {
180         OMX_PARAM_PORTDEFINITIONTYPE param;
181
182         G_OMX_PORT_GET_DEFINITION (omx_base->in_port, &param);
183
184         param.format.video.nFrameWidth = width;
185         param.format.video.nFrameHeight = height;
186         param.nBufferSize = width * height;
187
188         G_OMX_PORT_SET_DEFINITION (omx_base->in_port, &param);
189         GST_DEBUG_OBJECT (self, "G_OMX_PORT_SET_DEFINITION");
190     }
191
192     self->inport_configured = TRUE;
193
194     if (self->sink_setcaps)
195         self->sink_setcaps (pad, caps);
196
197     return gst_pad_set_caps (pad, caps);
198 }
199
200 static GstCaps *
201 src_getcaps (GstPad *pad)
202 {
203     GstCaps *caps;
204     GstOmxBaseVideoDec *self   = GST_OMX_BASE_VIDEODEC (GST_PAD_PARENT (pad));
205     GstOmxBaseFilter *omx_base = GST_OMX_BASE_FILTER (self);
206
207     if (omx_base->gomx->omx_state > OMX_StateLoaded)
208     {
209         /* currently, we cannot change caps once out of loaded..  later this
210          * could possibly be supported by enabling/disabling the port..
211          */
212         GST_DEBUG_OBJECT (self, "cannot getcaps in %d state", omx_base->gomx->omx_state);
213         caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
214         goto done;
215     }
216
217     if (self->inport_configured)
218     {
219         /* if we already have src-caps, we want to take the already configured
220          * width/height/etc.  But we can still support any option of rowstride,
221          * so we still don't want to return fixed caps
222          */
223         OMX_PARAM_PORTDEFINITIONTYPE param;
224         int i;
225
226         G_OMX_PORT_GET_DEFINITION (omx_base->out_port, &param);
227
228         caps = gst_caps_new_empty ();
229
230         for (i=0; i<2; i++)
231         {
232             GstStructure *struc = gst_structure_new (
233                     (i == 0 ? "video/x-raw-yuv-strided" : "video/x-raw-yuv"),
234                     "width",  G_TYPE_INT, param.format.video.nFrameWidth,
235                     "height", G_TYPE_INT, param.format.video.nFrameHeight,
236 #ifdef VIDDEC_COLOR_WORKAROUND
237                     "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('N', 'V', '1', '2'),
238 #endif
239                     NULL);
240
241             if(i)
242             {
243                 /* if buffer sharing is used, we let the upstream that allocates
244                  * the buffer dictate stride, otherwise we let the OMX component
245                  * decide on the stride
246                  */
247                 if (omx_base->out_port->share_buffer)
248                 {
249                     gst_structure_set (struc,
250                             "rowstride", GST_TYPE_INT_RANGE, 1, G_MAXINT,
251                             NULL);
252                 }
253                 else
254                 {
255                     gst_structure_set (struc,
256                             "rowstride", G_TYPE_INT, param.format.video.nStride,
257                             NULL);
258                 }
259             }
260
261             if (self->framerate_denom)
262             {
263                 gst_structure_set (struc,
264                         "framerate", GST_TYPE_FRACTION, self->framerate_num, self->framerate_denom,
265                         NULL);
266             }
267
268             gst_caps_append_structure (caps, struc);
269         }
270     }
271     else
272     {
273         /* we don't have valid width/height/etc yet, so just use the template.. */
274         caps = gst_static_pad_template_get_caps (&src_template);
275         GST_DEBUG_OBJECT (self, "caps=%"GST_PTR_FORMAT, caps);
276     }
277
278 #ifndef VIDDEC_COLOR_WORKAROUND
279     caps = g_omx_port_set_video_formats (omx_base->out_port, caps);
280 #endif
281
282 done:
283     GST_DEBUG_OBJECT (self, "caps=%"GST_PTR_FORMAT, caps);
284
285     return caps;
286 }
287
288 static gboolean
289 src_setcaps (GstPad *pad, GstCaps *caps)
290 {
291     GstOmxBaseVideoDec *self;
292     GstOmxBaseFilter *omx_base;
293
294     GstVideoFormat format;
295     gint width, height, rowstride;
296
297     self = GST_OMX_BASE_VIDEODEC (GST_PAD_PARENT (pad));
298     omx_base = GST_OMX_BASE_FILTER (self);
299
300     GST_INFO_OBJECT (omx_base, "setcaps (src): %" GST_PTR_FORMAT, caps);
301
302     g_return_val_if_fail (caps, FALSE);
303     g_return_val_if_fail (gst_caps_is_fixed (caps), FALSE);
304
305     if (gst_video_format_parse_caps_strided (caps,
306             &format, &width, &height, &rowstride))
307     {
308         /* Output port configuration: */
309         OMX_PARAM_PORTDEFINITIONTYPE param;
310
311         G_OMX_PORT_GET_DEFINITION (omx_base->out_port, &param);
312
313         if (!rowstride)
314             rowstride = gst_video_format_get_component_width (format, 0, width);
315
316         param.format.video.eColorFormat = g_omx_fourcc_to_colorformat (
317                 gst_video_format_to_fourcc (format));
318         param.format.video.nFrameWidth  = width;
319         param.format.video.nFrameHeight = height;
320         param.format.video.nStride      = self->rowstride = rowstride;
321
322         G_OMX_PORT_SET_DEFINITION (omx_base->out_port, &param);
323         GST_INFO_OBJECT (omx_base,"G_OMX_PORT_SET_DEFINITION");
324     }
325
326     return TRUE;
327 }
328
329 static gboolean
330 src_query (GstPad *pad, GstQuery *query)
331 {
332     GstOmxBaseVideoDec *self   = GST_OMX_BASE_VIDEODEC (GST_PAD_PARENT (pad));
333     GstOmxBaseFilter *omx_base = GST_OMX_BASE_FILTER (self);
334     gboolean ret = FALSE;
335
336     GST_DEBUG_OBJECT (self, "begin");
337
338     if (GST_QUERY_TYPE (query) == GST_QUERY_BUFFERS) {
339         const GstCaps *caps;
340         OMX_ERRORTYPE err;
341         OMX_PARAM_PORTDEFINITIONTYPE param;
342
343         _G_OMX_INIT_PARAM (&param);
344
345         gst_query_parse_buffers_caps (query, &caps);
346
347         /* ensure the caps we are querying are the current ones, otherwise
348          * results are meaningless..
349          *
350          * @todo should we save and restore current caps??
351          */
352 #if 0
353         src_setcaps (pad, (GstCaps *)caps);
354 #endif
355
356         param.nPortIndex = omx_base->out_port->port_index;
357         err = OMX_GetParameter (omx_base->gomx->omx_handle,
358                 OMX_IndexParamPortDefinition, &param);
359         g_assert (err == OMX_ErrorNone);
360
361         param.nBufferCountActual = param.nBufferCountMin;
362         err = OMX_SetParameter (omx_base->gomx->omx_handle,
363                 OMX_IndexParamPortDefinition, &param);
364         g_assert (err == OMX_ErrorNone);
365
366         GST_DEBUG_OBJECT (self, "min buffers: %d", param.nBufferCountMin);
367
368         gst_query_set_buffers_count (query, param.nBufferCountMin);
369
370 #ifdef USE_OMXTICORE
371         {
372             OMX_CONFIG_RECTTYPE rect;
373             _G_OMX_INIT_PARAM (&rect);
374
375             rect.nPortIndex = omx_base->out_port->port_index;
376             err = OMX_GetParameter (omx_base->gomx->omx_handle,
377                     OMX_TI_IndexParam2DBufferAllocDimension, &rect);
378             if (err == OMX_ErrorNone) {
379                 GST_DEBUG_OBJECT (self, "min dimensions: %dx%d",
380                         rect.nWidth, rect.nHeight);
381                 gst_query_set_buffers_dimensions (query,
382                         rect.nWidth, rect.nHeight);
383             }
384         }
385 #endif
386
387         ret = TRUE;
388     } else
389       ret = gst_pad_peer_query (omx_base->sinkpad, query);
390
391     GST_DEBUG_OBJECT (self, "end -> %d", ret);
392
393     return ret;
394 }
395
396 static void
397 omx_setup (GstOmxBaseFilter *omx_base)
398 {
399     GstOmxBaseVideoDec *self;
400     GOmxCore *gomx;
401
402     self = GST_OMX_BASE_VIDEODEC (omx_base);
403     gomx = (GOmxCore *) omx_base->gomx;
404
405     GST_INFO_OBJECT (omx_base, "begin");
406
407     {
408         OMX_PARAM_PORTDEFINITIONTYPE param;
409
410         /* Input port configuration. */
411         G_OMX_PORT_GET_DEFINITION (omx_base->in_port, &param);
412
413         param.format.video.eCompressionFormat = self->compression_format;
414
415         G_OMX_PORT_SET_DEFINITION (omx_base->in_port, &param);
416         GST_DEBUG_OBJECT (self, "G_OMX_PORT_SET_DEFINITION 1!!!");
417     }
418
419     GST_INFO_OBJECT (omx_base, "end");
420 }
421
422 static void
423 type_instance_init (GTypeInstance *instance,
424                     gpointer g_class)
425 {
426     GstOmxBaseFilter *omx_base;
427
428     omx_base = GST_OMX_BASE_FILTER (instance);
429
430     omx_base->omx_setup = omx_setup;
431
432     omx_base->gomx->settings_changed_cb = settings_changed_cb;
433
434     omx_base->in_port->omx_allocate = TRUE;
435     omx_base->out_port->omx_allocate = FALSE;
436     omx_base->in_port->share_buffer = FALSE;
437     omx_base->out_port->share_buffer = TRUE;
438
439     gst_pad_set_setcaps_function (omx_base->sinkpad,
440             GST_DEBUG_FUNCPTR (sink_setcaps));
441
442     gst_pad_set_getcaps_function (omx_base->srcpad,
443             GST_DEBUG_FUNCPTR (src_getcaps));
444     gst_pad_set_setcaps_function (omx_base->srcpad,
445             GST_DEBUG_FUNCPTR (src_setcaps));
446     gst_pad_set_query_function (omx_base->srcpad,
447             GST_DEBUG_FUNCPTR (src_query));
448 }
449