video-info: implement GstVideoInfo as boxed type
[platform/upstream/gstreamer.git] / gst-libs / gst / video / video-info.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  * Library       <2002> Ronald Bultje <rbultje@ronald.bitfreak.net>
4  * Copyright (C) 2007 David A. Schleef <ds@schleef.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
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  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #  include "config.h"
24 #endif
25
26 #include <string.h>
27 #include <stdio.h>
28
29 #include "video-info.h"
30 #include "video-tile.h"
31
32 /**
33  * gst_video_info_copy:
34  * @info: a #GstVideoInfo
35  *
36  * Copy a GstVideoInfo structure.
37  *
38  * Returns: a new #GstVideoInfo. free with gst_video_info_free.
39  *
40  * Since: 1.6
41  */
42 GstVideoInfo *
43 gst_video_info_copy (const GstVideoInfo * info)
44 {
45   return g_slice_dup (GstVideoInfo, info);
46 }
47
48 /**
49  * gst_video_info_free:
50  * @info: a #GstVideoInfo
51  *
52  * Free a GstVideoInfo structure previously allocated with gst_video_info_new()
53  * or gst_video_info_copy().
54  *
55  * Since: 1.6
56  */
57 void
58 gst_video_info_free (GstVideoInfo * info)
59 {
60   g_slice_free (GstVideoInfo, info);
61 }
62
63 G_DEFINE_BOXED_TYPE (GstVideoInfo, gst_video_info,
64     (GBoxedCopyFunc) gst_video_info_copy, (GBoxedFreeFunc) gst_video_info_free);
65
66 /**
67  * gst_video_info_new:
68  *
69  * Allocate a new #GstVideoInfo that is also initialized with
70  * gst_video_info_init().
71  *
72  * Returns: a new #GstVideoInfo. free with gst_video_info_free().
73  *
74  * Since: 1.6
75  */
76 GstVideoInfo *
77 gst_video_info_new (void)
78 {
79   GstVideoInfo *info;
80
81   info = g_slice_new (GstVideoInfo);
82   gst_video_info_init (info);
83
84   return info;
85 }
86
87 static int fill_planes (GstVideoInfo * info);
88
89 /**
90  * gst_video_info_init:
91  * @info: a #GstVideoInfo
92  *
93  * Initialize @info with default values.
94  */
95 void
96 gst_video_info_init (GstVideoInfo * info)
97 {
98   g_return_if_fail (info != NULL);
99
100   memset (info, 0, sizeof (GstVideoInfo));
101
102   info->finfo = gst_video_format_get_info (GST_VIDEO_FORMAT_UNKNOWN);
103
104   info->views = 1;
105   /* arrange for sensible defaults, e.g. if turned into caps */
106   info->fps_n = 0;
107   info->fps_d = 1;
108   info->par_n = 1;
109   info->par_d = 1;
110   GST_VIDEO_INFO_MULTIVIEW_MODE (info) = GST_VIDEO_MULTIVIEW_MODE_NONE;
111 }
112
113 #define MAKE_COLORIMETRY(r,m,t,p) {  \
114   GST_VIDEO_COLOR_RANGE ##r, GST_VIDEO_COLOR_MATRIX_ ##m, \
115   GST_VIDEO_TRANSFER_ ##t, GST_VIDEO_COLOR_PRIMARIES_ ##p }
116
117 #define DEFAULT_YUV_SD  0
118 #define DEFAULT_YUV_HD  1
119 #define DEFAULT_RGB     2
120 #define DEFAULT_GRAY    3
121 #define DEFAULT_UNKNOWN 4
122 #define DEFAULT_YUV_UHD 5
123
124 static const GstVideoColorimetry default_color[] = {
125   MAKE_COLORIMETRY (_16_235, BT601, BT709, SMPTE170M),
126   MAKE_COLORIMETRY (_16_235, BT709, BT709, BT709),
127   MAKE_COLORIMETRY (_0_255, RGB, SRGB, BT709),
128   MAKE_COLORIMETRY (_0_255, BT601, UNKNOWN, UNKNOWN),
129   MAKE_COLORIMETRY (_UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN),
130   MAKE_COLORIMETRY (_16_235, BT2020, BT2020_12, BT2020),
131 };
132
133 static void
134 set_default_colorimetry (GstVideoInfo * info)
135 {
136   const GstVideoFormatInfo *finfo = info->finfo;
137
138   if (GST_VIDEO_FORMAT_INFO_IS_YUV (finfo)) {
139     if (info->height >= 2160) {
140       info->chroma_site = GST_VIDEO_CHROMA_SITE_H_COSITED;
141       info->colorimetry = default_color[DEFAULT_YUV_UHD];
142     } else if (info->height > 576) {
143       info->chroma_site = GST_VIDEO_CHROMA_SITE_H_COSITED;
144       info->colorimetry = default_color[DEFAULT_YUV_HD];
145     } else {
146       info->chroma_site = GST_VIDEO_CHROMA_SITE_NONE;
147       info->colorimetry = default_color[DEFAULT_YUV_SD];
148     }
149   } else if (GST_VIDEO_FORMAT_INFO_IS_GRAY (finfo)) {
150     info->colorimetry = default_color[DEFAULT_GRAY];
151   } else if (GST_VIDEO_FORMAT_INFO_IS_RGB (finfo)) {
152     info->colorimetry = default_color[DEFAULT_RGB];
153   } else {
154     info->colorimetry = default_color[DEFAULT_UNKNOWN];
155   }
156 }
157
158 static gboolean
159 validate_colorimetry (GstVideoInfo * info)
160 {
161   const GstVideoFormatInfo *finfo = info->finfo;
162
163   if (!GST_VIDEO_FORMAT_INFO_IS_RGB (finfo) &&
164       info->colorimetry.matrix == GST_VIDEO_COLOR_MATRIX_RGB)
165     return FALSE;
166
167   if (GST_VIDEO_FORMAT_INFO_IS_YUV (finfo) &&
168       info->colorimetry.matrix == GST_VIDEO_COLOR_MATRIX_UNKNOWN)
169     return FALSE;
170
171   return TRUE;
172 }
173
174 /**
175  * gst_video_info_set_format:
176  * @info: a #GstVideoInfo
177  * @format: the format
178  * @width: a width
179  * @height: a height
180  *
181  * Set the default info for a video frame of @format and @width and @height.
182  *
183  * Note: This initializes @info first, no values are preserved. This function
184  * does not set the offsets correctly for interlaced vertically
185  * subsampled formats.
186  */
187 void
188 gst_video_info_set_format (GstVideoInfo * info, GstVideoFormat format,
189     guint width, guint height)
190 {
191   g_return_if_fail (info != NULL);
192   g_return_if_fail (format != GST_VIDEO_FORMAT_UNKNOWN);
193
194   gst_video_info_init (info);
195
196   info->finfo = gst_video_format_get_info (format);
197   info->width = width;
198   info->height = height;
199   info->views = 1;
200
201   set_default_colorimetry (info);
202
203   fill_planes (info);
204 }
205
206 static const gchar *interlace_mode[] = {
207   "progressive",
208   "interleaved",
209   "mixed",
210   "fields"
211 };
212
213 /**
214  * gst_video_interlace_mode_to_string:
215  * @mode: a #GstVideoInterlaceMode
216  *
217  * Convert @mode to its string representation.
218  *
219  * Returns: @mode as a string or NULL if @mode in invalid.
220  *
221  * Since: 1.6
222  */
223 const gchar *
224 gst_video_interlace_mode_to_string (GstVideoInterlaceMode mode)
225 {
226   if (((guint) mode) >= G_N_ELEMENTS (interlace_mode))
227     return NULL;
228
229   return interlace_mode[mode];
230 }
231
232 /**
233  * gst_video_interlace_mode_from_string:
234  * @mode: a mode
235  *
236  * Convert @mode to a #GstVideoInterlaceMode
237  *
238  * Returns: the #GstVideoInterlaceMode of @mode or
239  *    #GST_VIDEO_INTERLACE_MODE_PROGRESSIVE when @mode is not a valid
240  *    string representation for a #GstVideoInterlaceMode.
241  *
242  * Since: 1.6
243  */
244 GstVideoInterlaceMode
245 gst_video_interlace_mode_from_string (const gchar * mode)
246 {
247   gint i;
248   for (i = 0; i < G_N_ELEMENTS (interlace_mode); i++) {
249     if (g_str_equal (interlace_mode[i], mode))
250       return i;
251   }
252   return GST_VIDEO_INTERLACE_MODE_PROGRESSIVE;
253 }
254
255 /**
256  * gst_video_info_from_caps:
257  * @info: a #GstVideoInfo
258  * @caps: a #GstCaps
259  *
260  * Parse @caps and update @info.
261  *
262  * Returns: TRUE if @caps could be parsed
263  */
264 gboolean
265 gst_video_info_from_caps (GstVideoInfo * info, const GstCaps * caps)
266 {
267   GstStructure *structure;
268   const gchar *s;
269   GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN;
270   gint width = 0, height = 0;
271   gint fps_n, fps_d;
272   gint par_n, par_d;
273
274   g_return_val_if_fail (info != NULL, FALSE);
275   g_return_val_if_fail (caps != NULL, FALSE);
276   g_return_val_if_fail (gst_caps_is_fixed (caps), FALSE);
277
278   GST_DEBUG ("parsing caps %" GST_PTR_FORMAT, caps);
279
280   structure = gst_caps_get_structure (caps, 0);
281
282   if (gst_structure_has_name (structure, "video/x-raw")) {
283     if (!(s = gst_structure_get_string (structure, "format")))
284       goto no_format;
285
286     format = gst_video_format_from_string (s);
287     if (format == GST_VIDEO_FORMAT_UNKNOWN)
288       goto unknown_format;
289
290   } else if (g_str_has_prefix (gst_structure_get_name (structure), "video/") ||
291       g_str_has_prefix (gst_structure_get_name (structure), "image/")) {
292     format = GST_VIDEO_FORMAT_ENCODED;
293   } else {
294     goto wrong_name;
295   }
296
297   /* width and height are mandatory, except for non-raw-formats */
298   if (!gst_structure_get_int (structure, "width", &width) &&
299       format != GST_VIDEO_FORMAT_ENCODED)
300     goto no_width;
301   if (!gst_structure_get_int (structure, "height", &height) &&
302       format != GST_VIDEO_FORMAT_ENCODED)
303     goto no_height;
304
305   gst_video_info_init (info);
306
307   info->finfo = gst_video_format_get_info (format);
308   info->width = width;
309   info->height = height;
310
311   if (gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)) {
312     if (fps_n == 0) {
313       /* variable framerate */
314       info->flags |= GST_VIDEO_FLAG_VARIABLE_FPS;
315       /* see if we have a max-framerate */
316       gst_structure_get_fraction (structure, "max-framerate", &fps_n, &fps_d);
317     }
318     info->fps_n = fps_n;
319     info->fps_d = fps_d;
320   } else {
321     /* unspecified is variable framerate */
322     info->fps_n = 0;
323     info->fps_d = 1;
324   }
325
326   if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
327           &par_n, &par_d)) {
328     info->par_n = par_n;
329     info->par_d = par_d;
330   } else {
331     info->par_n = 1;
332     info->par_d = 1;
333   }
334
335   if ((s = gst_structure_get_string (structure, "interlace-mode")))
336     info->interlace_mode = gst_video_interlace_mode_from_string (s);
337   else
338     info->interlace_mode = GST_VIDEO_INTERLACE_MODE_PROGRESSIVE;
339
340   {
341     if ((s = gst_structure_get_string (structure, "multiview-mode")))
342       GST_VIDEO_INFO_MULTIVIEW_MODE (info) =
343           gst_video_multiview_mode_from_caps_string (s);
344     else
345       GST_VIDEO_INFO_MULTIVIEW_MODE (info) = GST_VIDEO_MULTIVIEW_MODE_NONE;
346
347     gst_structure_get_flagset (structure, "multiview-flags",
348         &GST_VIDEO_INFO_MULTIVIEW_FLAGS (info), NULL);
349
350     if (!gst_structure_get_int (structure, "views", &info->views))
351       info->views = 1;
352
353     /* At one point, I tried normalising the half-aspect flag here,
354      * but it behaves weird for GstVideoInfo operations other than
355      * directly converting to/from caps - sometimes causing the
356      * PAR to be doubled/halved too many times */
357   }
358
359   if ((s = gst_structure_get_string (structure, "chroma-site")))
360     info->chroma_site = gst_video_chroma_from_string (s);
361   else
362     info->chroma_site = GST_VIDEO_CHROMA_SITE_UNKNOWN;
363
364   if ((s = gst_structure_get_string (structure, "colorimetry"))) {
365     if (!gst_video_colorimetry_from_string (&info->colorimetry, s)) {
366       GST_WARNING ("unparsable colorimetry, using default");
367       set_default_colorimetry (info);
368     } else if (!validate_colorimetry (info)) {
369       GST_WARNING ("invalid colorimetry, using default");
370       set_default_colorimetry (info);
371     }
372   } else {
373     GST_DEBUG ("no colorimetry, using default");
374     set_default_colorimetry (info);
375   }
376
377   fill_planes (info);
378
379   return TRUE;
380
381   /* ERROR */
382 wrong_name:
383   {
384     GST_ERROR ("wrong name '%s', expected video/ or image/",
385         gst_structure_get_name (structure));
386     return FALSE;
387   }
388 no_format:
389   {
390     GST_ERROR ("no format given");
391     return FALSE;
392   }
393 unknown_format:
394   {
395     GST_ERROR ("unknown format '%s' given", s);
396     return FALSE;
397   }
398 no_width:
399   {
400     GST_ERROR ("no width property given");
401     return FALSE;
402   }
403 no_height:
404   {
405     GST_ERROR ("no height property given");
406     return FALSE;
407   }
408 }
409
410 /**
411  * gst_video_info_is_equal:
412  * @info: a #GstVideoInfo
413  * @other: a #GstVideoInfo
414  *
415  * Compares two #GstVideoInfo and returns whether they are equal or not
416  *
417  * Returns: %TRUE if @info and @other are equal, else %FALSE.
418  */
419 gboolean
420 gst_video_info_is_equal (const GstVideoInfo * info, const GstVideoInfo * other)
421 {
422   gint i;
423
424   if (GST_VIDEO_INFO_FORMAT (info) != GST_VIDEO_INFO_FORMAT (other))
425     return FALSE;
426   if (GST_VIDEO_INFO_INTERLACE_MODE (info) !=
427       GST_VIDEO_INFO_INTERLACE_MODE (other))
428     return FALSE;
429   if (GST_VIDEO_INFO_FLAGS (info) != GST_VIDEO_INFO_FLAGS (other))
430     return FALSE;
431   if (GST_VIDEO_INFO_WIDTH (info) != GST_VIDEO_INFO_WIDTH (other))
432     return FALSE;
433   if (GST_VIDEO_INFO_HEIGHT (info) != GST_VIDEO_INFO_HEIGHT (other))
434     return FALSE;
435   if (GST_VIDEO_INFO_SIZE (info) != GST_VIDEO_INFO_SIZE (other))
436     return FALSE;
437   if (GST_VIDEO_INFO_PAR_N (info) != GST_VIDEO_INFO_PAR_N (other))
438     return FALSE;
439   if (GST_VIDEO_INFO_PAR_D (info) != GST_VIDEO_INFO_PAR_D (other))
440     return FALSE;
441   if (GST_VIDEO_INFO_FPS_N (info) != GST_VIDEO_INFO_FPS_N (other))
442     return FALSE;
443   if (GST_VIDEO_INFO_FPS_D (info) != GST_VIDEO_INFO_FPS_D (other))
444     return FALSE;
445   if (!gst_video_colorimetry_is_equal (&GST_VIDEO_INFO_COLORIMETRY (info),
446           &GST_VIDEO_INFO_COLORIMETRY (other)))
447     return FALSE;
448   if (GST_VIDEO_INFO_CHROMA_SITE (info) != GST_VIDEO_INFO_CHROMA_SITE (other))
449     return FALSE;
450   if (GST_VIDEO_INFO_MULTIVIEW_MODE (info) !=
451       GST_VIDEO_INFO_MULTIVIEW_MODE (other))
452     return FALSE;
453   if (GST_VIDEO_INFO_MULTIVIEW_FLAGS (info) !=
454       GST_VIDEO_INFO_MULTIVIEW_FLAGS (other))
455     return FALSE;
456   if (GST_VIDEO_INFO_VIEWS (info) != GST_VIDEO_INFO_VIEWS (other))
457     return FALSE;
458
459   for (i = 0; i < info->finfo->n_planes; i++) {
460     if (info->stride[i] != other->stride[i])
461       return FALSE;
462     if (info->offset[i] != other->offset[i])
463       return FALSE;
464   }
465
466   return TRUE;
467 }
468
469 /**
470  * gst_video_info_to_caps:
471  * @info: a #GstVideoInfo
472  *
473  * Convert the values of @info into a #GstCaps.
474  *
475  * Returns: a new #GstCaps containing the info of @info.
476  */
477 GstCaps *
478 gst_video_info_to_caps (GstVideoInfo * info)
479 {
480   GstCaps *caps;
481   const gchar *format;
482   gchar *color;
483   gint par_n, par_d;
484
485   g_return_val_if_fail (info != NULL, NULL);
486   g_return_val_if_fail (info->finfo != NULL, NULL);
487   g_return_val_if_fail (info->finfo->format != GST_VIDEO_FORMAT_UNKNOWN, NULL);
488
489   format = gst_video_format_to_string (info->finfo->format);
490   g_return_val_if_fail (format != NULL, NULL);
491
492   caps = gst_caps_new_simple ("video/x-raw",
493       "format", G_TYPE_STRING, format,
494       "width", G_TYPE_INT, info->width,
495       "height", G_TYPE_INT, info->height, NULL);
496
497   par_n = info->par_n;
498   par_d = info->par_d;
499
500   gst_caps_set_simple (caps, "interlace-mode", G_TYPE_STRING,
501       gst_video_interlace_mode_to_string (info->interlace_mode), NULL);
502
503   if (GST_VIDEO_INFO_MULTIVIEW_MODE (info) != GST_VIDEO_MULTIVIEW_MODE_NONE) {
504     const gchar *caps_str = NULL;
505
506     /* If the half-aspect flag is set, applying it into the PAR of the
507      * resulting caps now seems safe, and helps with automatic behaviour
508      * in elements that aren't explicitly multiview aware */
509     if (GST_VIDEO_INFO_MULTIVIEW_FLAGS (info) &
510         GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT) {
511       GST_VIDEO_INFO_MULTIVIEW_FLAGS (info) &=
512           ~GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT;
513       switch (GST_VIDEO_INFO_MULTIVIEW_MODE (info)) {
514         case GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE:
515         case GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE_QUINCUNX:
516         case GST_VIDEO_MULTIVIEW_MODE_COLUMN_INTERLEAVED:
517         case GST_VIDEO_MULTIVIEW_MODE_CHECKERBOARD:
518           par_n *= 2;           /* double the width / half the height */
519           break;
520         case GST_VIDEO_MULTIVIEW_MODE_ROW_INTERLEAVED:
521         case GST_VIDEO_MULTIVIEW_MODE_TOP_BOTTOM:
522           par_d *= 2;           /* half the width / double the height */
523           break;
524         default:
525           break;
526       }
527     }
528
529     caps_str =
530         gst_video_multiview_mode_to_caps_string (GST_VIDEO_INFO_MULTIVIEW_MODE
531         (info));
532     if (caps_str != NULL) {
533       gst_caps_set_simple (caps, "multiview-mode", G_TYPE_STRING,
534           caps_str, "multiview-flags", GST_TYPE_VIDEO_MULTIVIEW_FLAGSET,
535           GST_VIDEO_INFO_MULTIVIEW_FLAGS (info), GST_FLAG_SET_MASK_EXACT, NULL);
536     }
537   }
538
539   gst_caps_set_simple (caps, "pixel-aspect-ratio",
540       GST_TYPE_FRACTION, par_n, par_d, NULL);
541
542   if (info->chroma_site != GST_VIDEO_CHROMA_SITE_UNKNOWN)
543     gst_caps_set_simple (caps, "chroma-site", G_TYPE_STRING,
544         gst_video_chroma_to_string (info->chroma_site), NULL);
545
546   if ((color = gst_video_colorimetry_to_string (&info->colorimetry))) {
547     gst_caps_set_simple (caps, "colorimetry", G_TYPE_STRING, color, NULL);
548     g_free (color);
549   }
550
551   if (info->views > 1)
552     gst_caps_set_simple (caps, "views", G_TYPE_INT, info->views, NULL);
553
554   if (info->flags & GST_VIDEO_FLAG_VARIABLE_FPS && info->fps_n != 0) {
555     /* variable fps with a max-framerate */
556     gst_caps_set_simple (caps, "framerate", GST_TYPE_FRACTION, 0, 1,
557         "max-framerate", GST_TYPE_FRACTION, info->fps_n, info->fps_d, NULL);
558   } else {
559     /* no variable fps or no max-framerate */
560     gst_caps_set_simple (caps, "framerate", GST_TYPE_FRACTION,
561         info->fps_n, info->fps_d, NULL);
562   }
563
564   return caps;
565 }
566
567 static int
568 fill_planes (GstVideoInfo * info)
569 {
570   gsize width, height, cr_h;
571
572   width = (gsize) info->width;
573   height = (gsize) info->height;
574
575   switch (info->finfo->format) {
576     case GST_VIDEO_FORMAT_YUY2:
577     case GST_VIDEO_FORMAT_YVYU:
578     case GST_VIDEO_FORMAT_UYVY:
579       info->stride[0] = GST_ROUND_UP_4 (width * 2);
580       info->offset[0] = 0;
581       info->size = info->stride[0] * height;
582       break;
583     case GST_VIDEO_FORMAT_AYUV:
584     case GST_VIDEO_FORMAT_RGBx:
585     case GST_VIDEO_FORMAT_RGBA:
586     case GST_VIDEO_FORMAT_BGRx:
587     case GST_VIDEO_FORMAT_BGRA:
588     case GST_VIDEO_FORMAT_xRGB:
589     case GST_VIDEO_FORMAT_ARGB:
590     case GST_VIDEO_FORMAT_xBGR:
591     case GST_VIDEO_FORMAT_ABGR:
592     case GST_VIDEO_FORMAT_r210:
593       info->stride[0] = width * 4;
594       info->offset[0] = 0;
595       info->size = info->stride[0] * height;
596       break;
597     case GST_VIDEO_FORMAT_RGB16:
598     case GST_VIDEO_FORMAT_BGR16:
599     case GST_VIDEO_FORMAT_RGB15:
600     case GST_VIDEO_FORMAT_BGR15:
601       info->stride[0] = GST_ROUND_UP_4 (width * 2);
602       info->offset[0] = 0;
603       info->size = info->stride[0] * height;
604       break;
605     case GST_VIDEO_FORMAT_RGB:
606     case GST_VIDEO_FORMAT_BGR:
607     case GST_VIDEO_FORMAT_v308:
608       info->stride[0] = GST_ROUND_UP_4 (width * 3);
609       info->offset[0] = 0;
610       info->size = info->stride[0] * height;
611       break;
612     case GST_VIDEO_FORMAT_v210:
613       info->stride[0] = ((width + 47) / 48) * 128;
614       info->offset[0] = 0;
615       info->size = info->stride[0] * height;
616       break;
617     case GST_VIDEO_FORMAT_v216:
618       info->stride[0] = GST_ROUND_UP_8 (width * 4);
619       info->offset[0] = 0;
620       info->size = info->stride[0] * height;
621       break;
622     case GST_VIDEO_FORMAT_GRAY8:
623       info->stride[0] = GST_ROUND_UP_4 (width);
624       info->offset[0] = 0;
625       info->size = info->stride[0] * height;
626       break;
627     case GST_VIDEO_FORMAT_GRAY16_BE:
628     case GST_VIDEO_FORMAT_GRAY16_LE:
629       info->stride[0] = GST_ROUND_UP_4 (width * 2);
630       info->offset[0] = 0;
631       info->size = info->stride[0] * height;
632       break;
633     case GST_VIDEO_FORMAT_UYVP:
634       info->stride[0] = GST_ROUND_UP_4 ((width * 2 * 5 + 3) / 4);
635       info->offset[0] = 0;
636       info->size = info->stride[0] * height;
637       break;
638     case GST_VIDEO_FORMAT_RGB8P:
639       info->stride[0] = GST_ROUND_UP_4 (width);
640       info->stride[1] = 4;
641       info->offset[0] = 0;
642       info->offset[1] = info->stride[0] * height;
643       info->size = info->offset[1] + (4 * 256);
644       break;
645     case GST_VIDEO_FORMAT_IYU1:
646       info->stride[0] = GST_ROUND_UP_4 (GST_ROUND_UP_4 (width) +
647           GST_ROUND_UP_4 (width) / 2);
648       info->offset[0] = 0;
649       info->size = info->stride[0] * height;
650       break;
651     case GST_VIDEO_FORMAT_ARGB64:
652     case GST_VIDEO_FORMAT_AYUV64:
653       info->stride[0] = width * 8;
654       info->offset[0] = 0;
655       info->size = info->stride[0] * height;
656       break;
657     case GST_VIDEO_FORMAT_I420:
658     case GST_VIDEO_FORMAT_YV12:        /* same as I420, but plane 1+2 swapped */
659       info->stride[0] = GST_ROUND_UP_4 (width);
660       info->stride[1] = GST_ROUND_UP_4 (GST_ROUND_UP_2 (width) / 2);
661       info->stride[2] = info->stride[1];
662       info->offset[0] = 0;
663       info->offset[1] = info->stride[0] * GST_ROUND_UP_2 (height);
664       cr_h = GST_ROUND_UP_2 (height) / 2;
665       if (GST_VIDEO_INFO_IS_INTERLACED (info))
666         cr_h = GST_ROUND_UP_2 (cr_h);
667       info->offset[2] = info->offset[1] + info->stride[1] * cr_h;
668       info->size = info->offset[2] + info->stride[2] * cr_h;
669       GST_DEBUG ("%d %d %d", GST_VIDEO_INFO_IS_INTERLACED (info),
670           (int) info->offset[2], (int) info->size);
671       break;
672     case GST_VIDEO_FORMAT_Y41B:
673       info->stride[0] = GST_ROUND_UP_4 (width);
674       info->stride[1] = GST_ROUND_UP_16 (width) / 4;
675       info->stride[2] = info->stride[1];
676       info->offset[0] = 0;
677       info->offset[1] = info->stride[0] * height;
678       info->offset[2] = info->offset[1] + info->stride[1] * height;
679       /* simplification of ROUNDUP4(w)*h + 2*((ROUNDUP16(w)/4)*h */
680       info->size = (info->stride[0] + (GST_ROUND_UP_16 (width) / 2)) * height;
681       break;
682     case GST_VIDEO_FORMAT_Y42B:
683       info->stride[0] = GST_ROUND_UP_4 (width);
684       info->stride[1] = GST_ROUND_UP_8 (width) / 2;
685       info->stride[2] = info->stride[1];
686       info->offset[0] = 0;
687       info->offset[1] = info->stride[0] * height;
688       info->offset[2] = info->offset[1] + info->stride[1] * height;
689       /* simplification of ROUNDUP4(w)*h + 2*(ROUNDUP8(w)/2)*h */
690       info->size = (info->stride[0] + GST_ROUND_UP_8 (width)) * height;
691       break;
692     case GST_VIDEO_FORMAT_Y444:
693     case GST_VIDEO_FORMAT_GBR:
694       info->stride[0] = GST_ROUND_UP_4 (width);
695       info->stride[1] = info->stride[0];
696       info->stride[2] = info->stride[0];
697       info->offset[0] = 0;
698       info->offset[1] = info->stride[0] * height;
699       info->offset[2] = info->offset[1] * 2;
700       info->size = info->stride[0] * height * 3;
701       break;
702     case GST_VIDEO_FORMAT_NV12:
703     case GST_VIDEO_FORMAT_NV21:
704       info->stride[0] = GST_ROUND_UP_4 (width);
705       info->stride[1] = info->stride[0];
706       info->offset[0] = 0;
707       info->offset[1] = info->stride[0] * GST_ROUND_UP_2 (height);
708       cr_h = GST_ROUND_UP_2 (height) / 2;
709       if (GST_VIDEO_INFO_IS_INTERLACED (info))
710         cr_h = GST_ROUND_UP_2 (cr_h);
711       info->size = info->offset[1] + info->stride[0] * cr_h;
712       break;
713     case GST_VIDEO_FORMAT_NV16:
714     case GST_VIDEO_FORMAT_NV61:
715       info->stride[0] = GST_ROUND_UP_4 (width);
716       info->stride[1] = info->stride[0];
717       info->offset[0] = 0;
718       info->offset[1] = info->stride[0] * height;
719       info->size = info->stride[0] * height * 2;
720       break;
721     case GST_VIDEO_FORMAT_NV24:
722       info->stride[0] = GST_ROUND_UP_4 (width);
723       info->stride[1] = GST_ROUND_UP_4 (width * 2);
724       info->offset[0] = 0;
725       info->offset[1] = info->stride[0] * height;
726       info->size = info->stride[0] * height + info->stride[1] * height;
727       break;
728     case GST_VIDEO_FORMAT_A420:
729       info->stride[0] = GST_ROUND_UP_4 (width);
730       info->stride[1] = GST_ROUND_UP_4 (GST_ROUND_UP_2 (width) / 2);
731       info->stride[2] = info->stride[1];
732       info->stride[3] = info->stride[0];
733       info->offset[0] = 0;
734       info->offset[1] = info->stride[0] * GST_ROUND_UP_2 (height);
735       cr_h = GST_ROUND_UP_2 (height) / 2;
736       if (GST_VIDEO_INFO_IS_INTERLACED (info))
737         cr_h = GST_ROUND_UP_2 (cr_h);
738       info->offset[2] = info->offset[1] + info->stride[1] * cr_h;
739       info->offset[3] = info->offset[2] + info->stride[2] * cr_h;
740       info->size = info->offset[3] + info->stride[0] * GST_ROUND_UP_2 (height);
741       break;
742     case GST_VIDEO_FORMAT_YUV9:
743     case GST_VIDEO_FORMAT_YVU9:
744       info->stride[0] = GST_ROUND_UP_4 (width);
745       info->stride[1] = GST_ROUND_UP_4 (GST_ROUND_UP_4 (width) / 4);
746       info->stride[2] = info->stride[1];
747       info->offset[0] = 0;
748       info->offset[1] = info->stride[0] * height;
749       cr_h = GST_ROUND_UP_4 (height) / 4;
750       if (GST_VIDEO_INFO_IS_INTERLACED (info))
751         cr_h = GST_ROUND_UP_2 (cr_h);
752       info->offset[2] = info->offset[1] + info->stride[1] * cr_h;
753       info->size = info->offset[2] + info->stride[2] * cr_h;
754       break;
755     case GST_VIDEO_FORMAT_I420_10LE:
756     case GST_VIDEO_FORMAT_I420_10BE:
757       info->stride[0] = GST_ROUND_UP_4 (width * 2);
758       info->stride[1] = GST_ROUND_UP_4 (width);
759       info->stride[2] = info->stride[1];
760       info->offset[0] = 0;
761       info->offset[1] = info->stride[0] * GST_ROUND_UP_2 (height);
762       cr_h = GST_ROUND_UP_2 (height) / 2;
763       if (GST_VIDEO_INFO_IS_INTERLACED (info))
764         cr_h = GST_ROUND_UP_2 (cr_h);
765       info->offset[2] = info->offset[1] + info->stride[1] * cr_h;
766       info->size = info->offset[2] + info->stride[2] * cr_h;
767       break;
768     case GST_VIDEO_FORMAT_I422_10LE:
769     case GST_VIDEO_FORMAT_I422_10BE:
770       info->stride[0] = GST_ROUND_UP_4 (width * 2);
771       info->stride[1] = GST_ROUND_UP_4 (width);
772       info->stride[2] = info->stride[1];
773       info->offset[0] = 0;
774       info->offset[1] = info->stride[0] * GST_ROUND_UP_2 (height);
775       info->offset[2] = info->offset[1] +
776           info->stride[1] * GST_ROUND_UP_2 (height);
777       info->size = info->offset[2] + info->stride[2] * GST_ROUND_UP_2 (height);
778       break;
779     case GST_VIDEO_FORMAT_Y444_10LE:
780     case GST_VIDEO_FORMAT_Y444_10BE:
781     case GST_VIDEO_FORMAT_GBR_10LE:
782     case GST_VIDEO_FORMAT_GBR_10BE:
783       info->stride[0] = GST_ROUND_UP_4 (width * 2);
784       info->stride[1] = info->stride[0];
785       info->stride[2] = info->stride[0];
786       info->offset[0] = 0;
787       info->offset[1] = info->stride[0] * height;
788       info->offset[2] = info->offset[1] * 2;
789       info->size = info->stride[0] * height * 3;
790       break;
791     case GST_VIDEO_FORMAT_NV12_64Z32:
792       info->stride[0] =
793           GST_VIDEO_TILE_MAKE_STRIDE (GST_ROUND_UP_128 (width) / 64,
794           GST_ROUND_UP_32 (height) / 32);
795       info->stride[1] =
796           GST_VIDEO_TILE_MAKE_STRIDE (GST_ROUND_UP_128 (width) / 64,
797           GST_ROUND_UP_64 (height) / 64);
798       info->offset[0] = 0;
799       info->offset[1] = GST_ROUND_UP_128 (width) * GST_ROUND_UP_32 (height);
800       info->size = info->offset[1] +
801           GST_ROUND_UP_128 (width) * GST_ROUND_UP_64 (height) / 2;
802       break;
803     case GST_VIDEO_FORMAT_A420_10LE:
804     case GST_VIDEO_FORMAT_A420_10BE:
805       info->stride[0] = GST_ROUND_UP_4 (width * 2);
806       info->stride[1] = GST_ROUND_UP_4 (width);
807       info->stride[2] = info->stride[1];
808       info->stride[3] = info->stride[0];
809       info->offset[0] = 0;
810       info->offset[1] = info->stride[0] * GST_ROUND_UP_2 (height);
811       cr_h = GST_ROUND_UP_2 (height) / 2;
812       if (GST_VIDEO_INFO_IS_INTERLACED (info))
813         cr_h = GST_ROUND_UP_2 (cr_h);
814       info->offset[2] = info->offset[1] + info->stride[1] * cr_h;
815       info->offset[3] = info->offset[2] + info->stride[2] * cr_h;
816       info->size = info->offset[3] + info->stride[0] * GST_ROUND_UP_2 (height);
817       break;
818     case GST_VIDEO_FORMAT_A422_10LE:
819     case GST_VIDEO_FORMAT_A422_10BE:
820       info->stride[0] = GST_ROUND_UP_4 (width * 2);
821       info->stride[1] = GST_ROUND_UP_4 (width);
822       info->stride[2] = info->stride[1];
823       info->stride[3] = info->stride[0];
824       info->offset[0] = 0;
825       info->offset[1] = info->stride[0] * GST_ROUND_UP_2 (height);
826       info->offset[2] = info->offset[1] +
827           info->stride[1] * GST_ROUND_UP_2 (height);
828       info->offset[3] =
829           info->offset[2] + info->stride[2] * GST_ROUND_UP_2 (height);
830       info->size = info->offset[3] + info->stride[0] * GST_ROUND_UP_2 (height);
831       break;
832     case GST_VIDEO_FORMAT_A444_10LE:
833     case GST_VIDEO_FORMAT_A444_10BE:
834       info->stride[0] = GST_ROUND_UP_4 (width * 2);
835       info->stride[1] = info->stride[0];
836       info->stride[2] = info->stride[0];
837       info->stride[3] = info->stride[0];
838       info->offset[0] = 0;
839       info->offset[1] = info->stride[0] * height;
840       info->offset[2] = info->offset[1] * 2;
841       info->offset[3] = info->offset[1] * 3;
842       info->size = info->stride[0] * height * 4;
843       break;
844
845     case GST_VIDEO_FORMAT_ENCODED:
846       break;
847     case GST_VIDEO_FORMAT_UNKNOWN:
848       GST_ERROR ("invalid format");
849       g_warning ("invalid format");
850       break;
851   }
852   return 0;
853 }
854
855 /**
856  * gst_video_info_convert:
857  * @info: a #GstVideoInfo
858  * @src_format: #GstFormat of the @src_value
859  * @src_value: value to convert
860  * @dest_format: #GstFormat of the @dest_value
861  * @dest_value: pointer to destination value
862  *
863  * Converts among various #GstFormat types.  This function handles
864  * GST_FORMAT_BYTES, GST_FORMAT_TIME, and GST_FORMAT_DEFAULT.  For
865  * raw video, GST_FORMAT_DEFAULT corresponds to video frames.  This
866  * function can be used to handle pad queries of the type GST_QUERY_CONVERT.
867  *
868  * Returns: TRUE if the conversion was successful.
869  */
870 gboolean
871 gst_video_info_convert (GstVideoInfo * info,
872     GstFormat src_format, gint64 src_value,
873     GstFormat dest_format, gint64 * dest_value)
874 {
875   gboolean ret = FALSE;
876   int fps_n, fps_d;
877   gsize size;
878
879   g_return_val_if_fail (info != NULL, 0);
880   g_return_val_if_fail (info->finfo != NULL, 0);
881   g_return_val_if_fail (info->finfo->format != GST_VIDEO_FORMAT_UNKNOWN, 0);
882   g_return_val_if_fail (info->size > 0, 0);
883
884   size = info->size;
885   fps_n = info->fps_n;
886   fps_d = info->fps_d;
887
888   GST_DEBUG ("converting value %" G_GINT64_FORMAT " from %s to %s",
889       src_value, gst_format_get_name (src_format),
890       gst_format_get_name (dest_format));
891
892   if (src_format == dest_format) {
893     *dest_value = src_value;
894     ret = TRUE;
895     goto done;
896   }
897
898   if (src_value == -1) {
899     *dest_value = -1;
900     ret = TRUE;
901     goto done;
902   }
903
904   /* bytes to frames */
905   if (src_format == GST_FORMAT_BYTES && dest_format == GST_FORMAT_DEFAULT) {
906     if (size != 0) {
907       *dest_value = gst_util_uint64_scale (src_value, 1, size);
908     } else {
909       GST_ERROR ("blocksize is 0");
910       *dest_value = 0;
911     }
912     ret = TRUE;
913     goto done;
914   }
915
916   /* frames to bytes */
917   if (src_format == GST_FORMAT_DEFAULT && dest_format == GST_FORMAT_BYTES) {
918     *dest_value = gst_util_uint64_scale (src_value, size, 1);
919     ret = TRUE;
920     goto done;
921   }
922
923   /* time to frames */
924   if (src_format == GST_FORMAT_TIME && dest_format == GST_FORMAT_DEFAULT) {
925     if (fps_d != 0) {
926       *dest_value = gst_util_uint64_scale (src_value,
927           fps_n, GST_SECOND * fps_d);
928     } else {
929       GST_ERROR ("framerate denominator is 0");
930       *dest_value = 0;
931     }
932     ret = TRUE;
933     goto done;
934   }
935
936   /* frames to time */
937   if (src_format == GST_FORMAT_DEFAULT && dest_format == GST_FORMAT_TIME) {
938     if (fps_n != 0) {
939       *dest_value = gst_util_uint64_scale (src_value,
940           GST_SECOND * fps_d, fps_n);
941     } else {
942       GST_ERROR ("framerate numerator is 0");
943       *dest_value = 0;
944     }
945     ret = TRUE;
946     goto done;
947   }
948
949   /* time to bytes */
950   if (src_format == GST_FORMAT_TIME && dest_format == GST_FORMAT_BYTES) {
951     if (fps_d != 0) {
952       *dest_value = gst_util_uint64_scale (src_value,
953           fps_n * size, GST_SECOND * fps_d);
954     } else {
955       GST_ERROR ("framerate denominator is 0");
956       *dest_value = 0;
957     }
958     ret = TRUE;
959     goto done;
960   }
961
962   /* bytes to time */
963   if (src_format == GST_FORMAT_BYTES && dest_format == GST_FORMAT_TIME) {
964     if (fps_n != 0 && size != 0) {
965       *dest_value = gst_util_uint64_scale (src_value,
966           GST_SECOND * fps_d, fps_n * size);
967     } else {
968       GST_ERROR ("framerate denominator and/or blocksize is 0");
969       *dest_value = 0;
970     }
971     ret = TRUE;
972   }
973
974 done:
975
976   GST_DEBUG ("ret=%d result %" G_GINT64_FORMAT, ret, *dest_value);
977
978   return ret;
979 }
980
981 /**
982  * gst_video_info_align:
983  * @info: a #GstVideoInfo
984  * @align: alignment parameters
985  *
986  * Adjust the offset and stride fields in @info so that the padding and
987  * stride alignment in @align is respected.
988  *
989  * Extra padding will be added to the right side when stride alignment padding
990  * is required and @align will be updated with the new padding values.
991  */
992 void
993 gst_video_info_align (GstVideoInfo * info, GstVideoAlignment * align)
994 {
995   const GstVideoFormatInfo *vinfo = info->finfo;
996   gint width, height;
997   gint padded_width, padded_height;
998   gint i, n_planes;
999   gboolean aligned;
1000
1001   width = GST_VIDEO_INFO_WIDTH (info);
1002   height = GST_VIDEO_INFO_HEIGHT (info);
1003
1004   GST_LOG ("padding %u-%ux%u-%u", align->padding_top,
1005       align->padding_left, align->padding_right, align->padding_bottom);
1006
1007   n_planes = GST_VIDEO_INFO_N_PLANES (info);
1008
1009   if (GST_VIDEO_FORMAT_INFO_HAS_PALETTE (vinfo))
1010     n_planes--;
1011
1012   /* first make sure the left padding does not cause alignment problems later */
1013   do {
1014     GST_LOG ("left padding %u", align->padding_left);
1015     aligned = TRUE;
1016     for (i = 0; i < n_planes; i++) {
1017       gint hedge;
1018
1019       /* this is the amout of pixels to add as left padding */
1020       hedge = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (vinfo, i, align->padding_left);
1021       hedge *= GST_VIDEO_FORMAT_INFO_PSTRIDE (vinfo, i);
1022
1023       GST_LOG ("plane %d, padding %d, alignment %u", i, hedge,
1024           align->stride_align[i]);
1025       aligned &= (hedge & align->stride_align[i]) == 0;
1026     }
1027     if (aligned)
1028       break;
1029
1030     GST_LOG ("unaligned padding, increasing padding");
1031     /* increase padded_width */
1032     align->padding_left += align->padding_left & ~(align->padding_left - 1);
1033   } while (!aligned);
1034
1035   /* add the padding */
1036   padded_width = width + align->padding_left + align->padding_right;
1037   padded_height = height + align->padding_top + align->padding_bottom;
1038
1039   do {
1040     GST_LOG ("padded dimension %u-%u", padded_width, padded_height);
1041
1042     info->width = padded_width;
1043     info->height = padded_height;
1044     fill_planes (info);
1045
1046     /* check alignment */
1047     aligned = TRUE;
1048     for (i = 0; i < n_planes; i++) {
1049       GST_LOG ("plane %d, stride %d, alignment %u", i, info->stride[i],
1050           align->stride_align[i]);
1051       aligned &= (info->stride[i] & align->stride_align[i]) == 0;
1052     }
1053     if (aligned)
1054       break;
1055
1056     GST_LOG ("unaligned strides, increasing dimension");
1057     /* increase padded_width */
1058     padded_width += padded_width & ~(padded_width - 1);
1059   } while (!aligned);
1060
1061   align->padding_right = padded_width - width - align->padding_left;
1062
1063   info->width = width;
1064   info->height = height;
1065
1066   for (i = 0; i < n_planes; i++) {
1067     gint vedge, hedge, comp;
1068
1069     /* Find the component for this plane, FIXME, we assume the plane number and
1070      * component number is the same for now, for scaling the dimensions this is
1071      * currently true for all formats but it might not be when adding new
1072      * formats. We might need to add a plane subsamling in the format info to
1073      * make this more generic or maybe use a plane -> component mapping. */
1074     comp = i;
1075
1076     hedge =
1077         GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (vinfo, comp, align->padding_left);
1078     vedge =
1079         GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (vinfo, comp, align->padding_top);
1080
1081     GST_DEBUG ("plane %d: comp: %d, hedge %d vedge %d align %d stride %d", i,
1082         comp, hedge, vedge, align->stride_align[i], info->stride[i]);
1083
1084     info->offset[i] += (vedge * info->stride[i]) +
1085         (hedge * GST_VIDEO_FORMAT_INFO_PSTRIDE (vinfo, comp));
1086   }
1087 }