omxvideodec: support interlace-mode=interleaved input
[platform/upstream/gstreamer.git] / omx / gstomxvideo.c
1 /*
2  * Copyright (C) 2011, Hewlett-Packard Development Company, L.P.
3  *   Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>, Collabora Ltd.
4  * Copyright (C) 2013, Collabora Ltd.
5  *   Author: Sebastian Dröge <sebastian.droege@collabora.co.uk> *
6  * Copyright 2014 Advanced Micro Devices, Inc.
7  *   Author: Christian König <christian.koenig@amd.com>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation
12  * version 2.1 of the License.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
22  *
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #include "gstomxvideo.h"
30
31 #include <math.h>
32
33 GST_DEBUG_CATEGORY (gst_omx_video_debug_category);
34 #define GST_CAT_DEFAULT gst_omx_video_debug_category
35
36 /* Keep synced with GST_OMX_VIDEO_DEC_SUPPORTED_FORMATS */
37 GstVideoFormat
38 gst_omx_video_get_format_from_omx (OMX_COLOR_FORMATTYPE omx_colorformat)
39 {
40   GstVideoFormat format;
41
42   switch (omx_colorformat) {
43     case OMX_COLOR_FormatL8:
44       format = GST_VIDEO_FORMAT_GRAY8;
45       break;
46     case OMX_COLOR_FormatYUV420Planar:
47     case OMX_COLOR_FormatYUV420PackedPlanar:
48       format = GST_VIDEO_FORMAT_I420;
49       break;
50     case OMX_COLOR_FormatYUV420SemiPlanar:
51     case OMX_COLOR_FormatYUV420PackedSemiPlanar:
52       format = GST_VIDEO_FORMAT_NV12;
53       break;
54     case OMX_COLOR_FormatYUV422SemiPlanar:
55       format = GST_VIDEO_FORMAT_NV16;
56       break;
57     case OMX_COLOR_FormatYCbYCr:
58       format = GST_VIDEO_FORMAT_YUY2;
59       break;
60     case OMX_COLOR_FormatYCrYCb:
61       format = GST_VIDEO_FORMAT_YVYU;
62       break;
63     case OMX_COLOR_FormatCbYCrY:
64       format = GST_VIDEO_FORMAT_UYVY;
65       break;
66     case OMX_COLOR_Format32bitARGB8888:
67       /* There is a mismatch in omxil specification 4.2.1 between
68        * OMX_COLOR_Format32bitARGB8888 and its description
69        * Follow the description */
70       format = GST_VIDEO_FORMAT_ABGR;
71       break;
72     case OMX_COLOR_Format32bitBGRA8888:
73       /* Same issue as OMX_COLOR_Format32bitARGB8888 */
74       format = GST_VIDEO_FORMAT_ARGB;
75       break;
76     case OMX_COLOR_Format16bitRGB565:
77       format = GST_VIDEO_FORMAT_RGB16;
78       break;
79     case OMX_COLOR_Format16bitBGR565:
80       format = GST_VIDEO_FORMAT_BGR16;
81       break;
82     case OMX_COLOR_Format24bitBGR888:
83       format = GST_VIDEO_FORMAT_BGR;
84       break;
85 #ifdef USE_OMX_TARGET_ZYNQ_USCALE_PLUS
86       /* Formats defined in extensions have their own enum so disable to -Wswitch warning */
87 #pragma GCC diagnostic push
88 #pragma GCC diagnostic ignored "-Wswitch"
89     case OMX_ALG_COLOR_FormatYUV420SemiPlanar10bitPacked:
90       format = GST_VIDEO_FORMAT_NV12_10LE32;
91       break;
92     case OMX_ALG_COLOR_FormatYUV422SemiPlanar10bitPacked:
93       format = GST_VIDEO_FORMAT_NV16_10LE32;
94       break;
95 #pragma GCC diagnostic pop
96 #endif
97     default:
98       format = GST_VIDEO_FORMAT_UNKNOWN;
99       break;
100   }
101
102   return format;
103 }
104
105 GList *
106 gst_omx_video_get_supported_colorformats (GstOMXPort * port,
107     GstVideoCodecState * state)
108 {
109   GstOMXComponent *comp = port->comp;
110   OMX_VIDEO_PARAM_PORTFORMATTYPE param;
111   OMX_ERRORTYPE err;
112   GList *negotiation_map = NULL;
113   gint old_index;
114   GstOMXVideoNegotiationMap *m;
115   GstVideoFormat f;
116
117   GST_OMX_INIT_STRUCT (&param);
118   param.nPortIndex = port->index;
119   param.nIndex = 0;
120   param.xFramerate =
121       state ? gst_omx_video_calculate_framerate_q16 (&state->info) : 0;
122
123   old_index = -1;
124   do {
125     err =
126         gst_omx_component_get_parameter (comp,
127         OMX_IndexParamVideoPortFormat, &param);
128
129     /* FIXME: Workaround for Bellagio that simply always
130      * returns the same value regardless of nIndex and
131      * never returns OMX_ErrorNoMore
132      */
133     if (old_index == param.nIndex)
134       break;
135
136     if (err == OMX_ErrorNone || err == OMX_ErrorNoMore) {
137       f = gst_omx_video_get_format_from_omx (param.eColorFormat);
138
139       if (f != GST_VIDEO_FORMAT_UNKNOWN) {
140         m = g_slice_new (GstOMXVideoNegotiationMap);
141         m->format = f;
142         m->type = param.eColorFormat;
143         negotiation_map = g_list_append (negotiation_map, m);
144         GST_DEBUG_OBJECT (comp->parent,
145             "Component port %d supports %s (%d) at index %u", port->index,
146             gst_video_format_to_string (f), param.eColorFormat,
147             (guint) param.nIndex);
148       } else {
149         GST_DEBUG_OBJECT (comp->parent,
150             "Component port %d supports unsupported color format %d at index %u",
151             port->index, param.eColorFormat, (guint) param.nIndex);
152       }
153     }
154     old_index = param.nIndex++;
155   } while (err == OMX_ErrorNone);
156
157   return negotiation_map;
158 }
159
160 GstCaps *
161 gst_omx_video_get_caps_for_map (GList * map)
162 {
163   GstCaps *caps = gst_caps_new_empty ();
164   GList *l;
165
166   for (l = map; l; l = l->next) {
167     GstOMXVideoNegotiationMap *entry = l->data;
168
169     gst_caps_append_structure (caps,
170         gst_structure_new ("video/x-raw",
171             "format", G_TYPE_STRING,
172             gst_video_format_to_string (entry->format), NULL));
173   }
174   return caps;
175 }
176
177 void
178 gst_omx_video_negotiation_map_free (GstOMXVideoNegotiationMap * m)
179 {
180   g_slice_free (GstOMXVideoNegotiationMap, m);
181 }
182
183 GstVideoCodecFrame *
184 gst_omx_video_find_nearest_frame (GstElement * element, GstOMXBuffer * buf,
185     GList * frames)
186 {
187   GstVideoCodecFrame *best = NULL;
188   GstClockTimeDiff best_diff = G_MAXINT64;
189   GstClockTime timestamp;
190   GList *l;
191
192   timestamp =
193       gst_util_uint64_scale (GST_OMX_GET_TICKS (buf->omx_buf->nTimeStamp),
194       GST_SECOND, OMX_TICKS_PER_SECOND);
195
196   GST_LOG_OBJECT (element, "look for ts %" GST_TIME_FORMAT,
197       GST_TIME_ARGS (timestamp));
198
199   for (l = frames; l; l = l->next) {
200     GstVideoCodecFrame *tmp = l->data;
201     GstClockTimeDiff diff = ABS (GST_CLOCK_DIFF (timestamp, tmp->pts));
202
203     GST_LOG_OBJECT (element,
204         "  frame %u diff %" G_GINT64_FORMAT " ts %" GST_TIME_FORMAT,
205         tmp->system_frame_number, diff, GST_TIME_ARGS (tmp->pts));
206
207     if (diff < best_diff) {
208       best = tmp;
209       best_diff = diff;
210
211       if (diff == 0)
212         break;
213     }
214   }
215
216   if (best) {
217     gst_video_codec_frame_ref (best);
218
219     /* OMX timestamps are in microseconds while gst ones are in nanoseconds.
220      * So if the difference between them is higher than 1 microsecond we likely
221      * picked the wrong frame. */
222     if (best_diff >= GST_USECOND)
223       GST_WARNING_OBJECT (element,
224           "Difference between ts (%" GST_TIME_FORMAT ") and frame %u (%"
225           GST_TIME_FORMAT ") seems too high (%" GST_TIME_FORMAT ")",
226           GST_TIME_ARGS (timestamp), best->system_frame_number,
227           GST_TIME_ARGS (best->pts), GST_TIME_ARGS (best_diff));
228   } else
229     GST_WARNING_OBJECT (element, "No best frame has been found");
230
231   g_list_foreach (frames, (GFunc) gst_video_codec_frame_unref, NULL);
232   g_list_free (frames);
233
234   return best;
235 }
236
237 OMX_U32
238 gst_omx_video_calculate_framerate_q16 (GstVideoInfo * info)
239 {
240   g_assert (info);
241
242   if (!info->fps_d)
243     return 0;
244
245   /* OMX API expects frame rate to actually be the field rate, so twice
246    * the frame rate in interlace mode. */
247   return gst_util_uint64_scale_int (1 << 16, GST_VIDEO_INFO_FIELD_RATE_N (info),
248       info->fps_d);
249 }
250
251 gboolean
252 gst_omx_video_is_equal_framerate_q16 (OMX_U32 q16_a, OMX_U32 q16_b)
253 {
254   /* If one of them is 0 use the classic comparison. The value 0 has a special
255      meaning and is used to indicate the frame rate is unknown, variable, or
256      is not needed. */
257   if (!q16_a || !q16_b)
258     return q16_a == q16_b;
259
260   /* If the 'percentage change' is less than 1% then consider it equal to avoid
261    * an unnecessary re-negotiation. */
262   return fabs (((gdouble) q16_a) - ((gdouble) q16_b)) / (gdouble) q16_b < 0.01;
263 }
264
265 gboolean
266 gst_omx_video_get_port_padding (GstOMXPort * port, GstVideoInfo * info_orig,
267     GstVideoAlignment * align)
268 {
269   guint nstride;
270   guint nslice_height;
271   GstVideoInfo info;
272   gsize plane_size[GST_VIDEO_MAX_PLANES];
273
274   gst_video_alignment_reset (align);
275
276   /* Create a copy of @info_orig without any offset/stride as we need a
277    * 'standard' version to compute the paddings. */
278   gst_video_info_init (&info);
279   gst_video_info_set_interlaced_format (&info,
280       GST_VIDEO_INFO_FORMAT (info_orig),
281       GST_VIDEO_INFO_INTERLACE_MODE (info_orig),
282       GST_VIDEO_INFO_WIDTH (info_orig), GST_VIDEO_INFO_HEIGHT (info_orig));
283
284   /* Retrieve the plane sizes */
285   if (!gst_video_info_align_full (&info, align, plane_size)) {
286     GST_WARNING_OBJECT (port->comp->parent, "Failed to retrieve plane sizes");
287     return FALSE;
288   }
289
290   nstride = port->port_def.format.video.nStride;
291   nslice_height = port->port_def.format.video.nSliceHeight;
292
293   if (nstride > GST_VIDEO_INFO_PLANE_STRIDE (&info, 0)) {
294     align->padding_right = nstride - GST_VIDEO_INFO_PLANE_STRIDE (&info, 0);
295
296     if (GST_VIDEO_FORMAT_INFO_IS_COMPLEX (info.finfo)) {
297       /* Stride is in bytes while padding is in pixels so we need to do manual
298        * conversions for complex formats. */
299       switch (GST_VIDEO_INFO_FORMAT (&info)) {
300         case GST_VIDEO_FORMAT_NV12_10LE32:
301         case GST_VIDEO_FORMAT_NV16_10LE32:
302           /* Need ((width + 2) / 3) 32-bits words to store one row,
303            * see unpack_NV12_10LE32 in -base.
304            *
305            * So let's say:
306            * - W = the width, in pixels
307            * - S = the stride, in bytes
308            * - P = the padding, in bytes
309            * - Δ = the padding, in pixels
310            *
311            * we then have:
312            * S = ((W+2)/3) * 4
313            * S+P = ((W+2+Δ)/3) * 4
314            *
315            * By solving this system we get:
316            * Δ = (3/4) * P
317            */
318           align->padding_right *= 0.75;
319           break;
320         default:
321           GST_FIXME_OBJECT (port->comp->parent,
322               "Stride conversion is not supported for format %s",
323               GST_VIDEO_INFO_NAME (&info));
324           return FALSE;
325       }
326     }
327
328     GST_LOG_OBJECT (port->comp->parent,
329         "OMX stride (%d) is higher than standard (%d) for port %u; right padding: %d",
330         nstride, GST_VIDEO_INFO_PLANE_STRIDE (&info, 0), port->index,
331         align->padding_right);
332   }
333
334   if (nslice_height > GST_VIDEO_INFO_PLANE_HEIGHT (&info, 0, plane_size)) {
335     align->padding_bottom =
336         nslice_height - GST_VIDEO_INFO_PLANE_HEIGHT (&info, 0, plane_size);
337
338     if (GST_VIDEO_INFO_INTERLACE_MODE (&info) ==
339         GST_VIDEO_INTERLACE_MODE_ALTERNATE) {
340       /* GstVideoAlignment defines the alignment for the full frame while
341        * OMX gives us the slice height for a single field, so we have to
342        * double the vertical padding. */
343       GST_DEBUG_OBJECT (port->comp->parent,
344           "Double bottom padding because of alternate stream");
345       align->padding_bottom *= 2;
346     }
347
348     GST_LOG_OBJECT (port->comp->parent,
349         "OMX slice height (%d) is higher than standard (%" G_GSIZE_FORMAT
350         ") for port %u; vertical padding: %d", nslice_height,
351         GST_VIDEO_INFO_PLANE_HEIGHT (&info, 0, plane_size), port->index,
352         align->padding_bottom);
353   }
354
355   return TRUE;
356 }