| /* GStreamer |
| * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> |
| * Library <2002> Ronald Bultje <rbultje@ronald.bitfreak.net> |
| * Copyright (C) 2007 David A. Schleef <ds@schleef.org> |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public |
| * License along with this library; if not, write to the |
| * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| # include "config.h" |
| #endif |
| |
| #include <string.h> |
| #include <stdio.h> |
| |
| #include "video-info.h" |
| #include "video-tile.h" |
| |
| #ifndef GST_DISABLE_GST_DEBUG |
| #define GST_CAT_DEFAULT ensure_debug_category() |
| static GstDebugCategory * |
| ensure_debug_category (void) |
| { |
| static gsize cat_gonce = 0; |
| |
| if (g_once_init_enter (&cat_gonce)) { |
| gsize cat_done; |
| |
| cat_done = (gsize) _gst_debug_category_new ("video-info", 0, |
| "video-info structure"); |
| |
| g_once_init_leave (&cat_gonce, cat_done); |
| } |
| |
| return (GstDebugCategory *) cat_gonce; |
| } |
| #else |
| #define ensure_debug_category() /* NOOP */ |
| #endif /* GST_DISABLE_GST_DEBUG */ |
| |
| /** |
| * gst_video_info_copy: |
| * @info: a #GstVideoInfo |
| * |
| * Copy a GstVideoInfo structure. |
| * |
| * Returns: a new #GstVideoInfo. free with gst_video_info_free. |
| * |
| * Since: 1.6 |
| */ |
| GstVideoInfo * |
| gst_video_info_copy (const GstVideoInfo * info) |
| { |
| return g_slice_dup (GstVideoInfo, info); |
| } |
| |
| /** |
| * gst_video_info_free: |
| * @info: a #GstVideoInfo |
| * |
| * Free a GstVideoInfo structure previously allocated with gst_video_info_new() |
| * or gst_video_info_copy(). |
| * |
| * Since: 1.6 |
| */ |
| void |
| gst_video_info_free (GstVideoInfo * info) |
| { |
| g_slice_free (GstVideoInfo, info); |
| } |
| |
| G_DEFINE_BOXED_TYPE (GstVideoInfo, gst_video_info, |
| (GBoxedCopyFunc) gst_video_info_copy, (GBoxedFreeFunc) gst_video_info_free); |
| |
| /** |
| * gst_video_info_new: |
| * |
| * Allocate a new #GstVideoInfo that is also initialized with |
| * gst_video_info_init(). |
| * |
| * Returns: a new #GstVideoInfo. free with gst_video_info_free(). |
| * |
| * Since: 1.6 |
| */ |
| GstVideoInfo * |
| gst_video_info_new (void) |
| { |
| GstVideoInfo *info; |
| |
| info = g_slice_new (GstVideoInfo); |
| gst_video_info_init (info); |
| |
| return info; |
| } |
| |
| static gboolean fill_planes (GstVideoInfo * info); |
| |
| /** |
| * gst_video_info_init: |
| * @info: a #GstVideoInfo |
| * |
| * Initialize @info with default values. |
| */ |
| void |
| gst_video_info_init (GstVideoInfo * info) |
| { |
| g_return_if_fail (info != NULL); |
| |
| memset (info, 0, sizeof (GstVideoInfo)); |
| |
| info->finfo = gst_video_format_get_info (GST_VIDEO_FORMAT_UNKNOWN); |
| |
| info->views = 1; |
| /* arrange for sensible defaults, e.g. if turned into caps */ |
| info->fps_n = 0; |
| info->fps_d = 1; |
| info->par_n = 1; |
| info->par_d = 1; |
| GST_VIDEO_INFO_MULTIVIEW_MODE (info) = GST_VIDEO_MULTIVIEW_MODE_NONE; |
| GST_VIDEO_INFO_MULTIVIEW_FLAGS (info) = GST_VIDEO_MULTIVIEW_FLAGS_NONE; |
| GST_VIDEO_INFO_FIELD_ORDER (info) = GST_VIDEO_FIELD_ORDER_UNKNOWN; |
| } |
| |
| #define MAKE_COLORIMETRY(r,m,t,p) { \ |
| GST_VIDEO_COLOR_RANGE ##r, GST_VIDEO_COLOR_MATRIX_ ##m, \ |
| GST_VIDEO_TRANSFER_ ##t, GST_VIDEO_COLOR_PRIMARIES_ ##p } |
| |
| #define DEFAULT_YUV_SD 0 |
| #define DEFAULT_YUV_HD 1 |
| #define DEFAULT_RGB 2 |
| #define DEFAULT_GRAY 3 |
| #define DEFAULT_UNKNOWN 4 |
| #define DEFAULT_YUV_UHD 5 |
| |
| static const GstVideoColorimetry default_color[] = { |
| MAKE_COLORIMETRY (_16_235, BT601, BT709, SMPTE170M), |
| MAKE_COLORIMETRY (_16_235, BT709, BT709, BT709), |
| MAKE_COLORIMETRY (_0_255, RGB, SRGB, BT709), |
| MAKE_COLORIMETRY (_0_255, BT601, UNKNOWN, UNKNOWN), |
| MAKE_COLORIMETRY (_UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN), |
| MAKE_COLORIMETRY (_16_235, BT2020, BT2020_12, BT2020), |
| }; |
| |
| static void |
| set_default_colorimetry (GstVideoInfo * info) |
| { |
| const GstVideoFormatInfo *finfo = info->finfo; |
| |
| if (GST_VIDEO_FORMAT_INFO_IS_YUV (finfo)) { |
| if (info->height >= 2160) { |
| info->chroma_site = GST_VIDEO_CHROMA_SITE_H_COSITED; |
| info->colorimetry = default_color[DEFAULT_YUV_UHD]; |
| } else if (info->height > 576) { |
| info->chroma_site = GST_VIDEO_CHROMA_SITE_H_COSITED; |
| info->colorimetry = default_color[DEFAULT_YUV_HD]; |
| } else { |
| info->chroma_site = GST_VIDEO_CHROMA_SITE_NONE; |
| info->colorimetry = default_color[DEFAULT_YUV_SD]; |
| } |
| } else if (GST_VIDEO_FORMAT_INFO_IS_GRAY (finfo)) { |
| info->colorimetry = default_color[DEFAULT_GRAY]; |
| } else if (GST_VIDEO_FORMAT_INFO_IS_RGB (finfo)) { |
| info->colorimetry = default_color[DEFAULT_RGB]; |
| } else { |
| info->colorimetry = default_color[DEFAULT_UNKNOWN]; |
| } |
| } |
| |
| static gboolean |
| validate_colorimetry (GstVideoInfo * info) |
| { |
| const GstVideoFormatInfo *finfo = info->finfo; |
| |
| if (!GST_VIDEO_FORMAT_INFO_IS_RGB (finfo) && |
| info->colorimetry.matrix == GST_VIDEO_COLOR_MATRIX_RGB) |
| return FALSE; |
| |
| if (GST_VIDEO_FORMAT_INFO_IS_YUV (finfo) && |
| info->colorimetry.matrix == GST_VIDEO_COLOR_MATRIX_UNKNOWN) |
| return FALSE; |
| |
| return TRUE; |
| } |
| |
| /** |
| * gst_video_info_set_format: |
| * @info: a #GstVideoInfo |
| * @format: the format |
| * @width: a width |
| * @height: a height |
| * |
| * Set the default info for a video frame of @format and @width and @height. |
| * |
| * Note: This initializes @info first, no values are preserved. This function |
| * does not set the offsets correctly for interlaced vertically |
| * subsampled formats. |
| * |
| * Returns: %FALSE if the returned video info is invalid, e.g. because the |
| * size of a frame can't be represented as a 32 bit integer (Since: 1.12) |
| */ |
| gboolean |
| gst_video_info_set_format (GstVideoInfo * info, GstVideoFormat format, |
| guint width, guint height) |
| { |
| g_return_val_if_fail (info != NULL, FALSE); |
| g_return_val_if_fail (format != GST_VIDEO_FORMAT_UNKNOWN, FALSE); |
| |
| if (width > G_MAXINT || height > G_MAXINT) |
| return FALSE; |
| |
| gst_video_info_init (info); |
| |
| info->finfo = gst_video_format_get_info (format); |
| info->width = width; |
| info->height = height; |
| info->views = 1; |
| |
| set_default_colorimetry (info); |
| |
| return fill_planes (info); |
| } |
| |
| static const gchar *interlace_mode[] = { |
| "progressive", |
| "interleaved", |
| "mixed", |
| "fields" |
| }; |
| |
| /** |
| * gst_video_interlace_mode_to_string: |
| * @mode: a #GstVideoInterlaceMode |
| * |
| * Convert @mode to its string representation. |
| * |
| * Returns: @mode as a string or NULL if @mode in invalid. |
| * |
| * Since: 1.6 |
| */ |
| const gchar * |
| gst_video_interlace_mode_to_string (GstVideoInterlaceMode mode) |
| { |
| if (((guint) mode) >= G_N_ELEMENTS (interlace_mode)) |
| return NULL; |
| |
| return interlace_mode[mode]; |
| } |
| |
| /** |
| * gst_video_interlace_mode_from_string: |
| * @mode: a mode |
| * |
| * Convert @mode to a #GstVideoInterlaceMode |
| * |
| * Returns: the #GstVideoInterlaceMode of @mode or |
| * #GST_VIDEO_INTERLACE_MODE_PROGRESSIVE when @mode is not a valid |
| * string representation for a #GstVideoInterlaceMode. |
| * |
| * Since: 1.6 |
| */ |
| GstVideoInterlaceMode |
| gst_video_interlace_mode_from_string (const gchar * mode) |
| { |
| gint i; |
| for (i = 0; i < G_N_ELEMENTS (interlace_mode); i++) { |
| if (g_str_equal (interlace_mode[i], mode)) |
| return i; |
| } |
| return GST_VIDEO_INTERLACE_MODE_PROGRESSIVE; |
| } |
| |
| static const gchar *field_order[] = { |
| "unknown", |
| "top-field-first", |
| "bottom-field-first" |
| }; |
| |
| /** |
| * gst_video_field_order_to_string: |
| * @order: a #GstVideoFieldOrder |
| * |
| * Convert @order to its string representation. |
| * |
| * Returns: @order as a string or NULL if @order in invalid. |
| * |
| * Since: 1.12 |
| */ |
| const gchar * |
| gst_video_field_order_to_string (GstVideoFieldOrder order) |
| { |
| if (((guint) order) >= G_N_ELEMENTS (field_order)) |
| return NULL; |
| |
| return field_order[order]; |
| } |
| |
| /** |
| * gst_video_field_order_from_string: |
| * @order: a field order |
| * |
| * Convert @order to a #GstVideoFieldOrder |
| * |
| * Returns: the #GstVideoFieldOrder of @order or |
| * #GST_VIDEO_FIELD_ORDER_UNKNOWN when @order is not a valid |
| * string representation for a #GstVideoFieldOrder. |
| * |
| * Since: 1.12 |
| */ |
| GstVideoFieldOrder |
| gst_video_field_order_from_string (const gchar * order) |
| { |
| gint i; |
| for (i = 0; i < G_N_ELEMENTS (field_order); i++) { |
| if (g_str_equal (field_order[i], order)) |
| return i; |
| } |
| return GST_VIDEO_FIELD_ORDER_UNKNOWN; |
| } |
| |
| /** |
| * gst_video_info_from_caps: |
| * @info: a #GstVideoInfo |
| * @caps: a #GstCaps |
| * |
| * Parse @caps and update @info. |
| * |
| * Returns: TRUE if @caps could be parsed |
| */ |
| gboolean |
| gst_video_info_from_caps (GstVideoInfo * info, const GstCaps * caps) |
| { |
| GstStructure *structure; |
| const gchar *s; |
| GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN; |
| gint width = 0, height = 0; |
| gint fps_n, fps_d; |
| gint par_n, par_d; |
| guint multiview_flags; |
| |
| g_return_val_if_fail (info != NULL, FALSE); |
| g_return_val_if_fail (caps != NULL, FALSE); |
| g_return_val_if_fail (gst_caps_is_fixed (caps), FALSE); |
| |
| GST_DEBUG ("parsing caps %" GST_PTR_FORMAT, caps); |
| |
| structure = gst_caps_get_structure (caps, 0); |
| |
| if (gst_structure_has_name (structure, "video/x-raw")) { |
| if (!(s = gst_structure_get_string (structure, "format"))) |
| goto no_format; |
| |
| format = gst_video_format_from_string (s); |
| if (format == GST_VIDEO_FORMAT_UNKNOWN) |
| goto unknown_format; |
| |
| } else if (g_str_has_prefix (gst_structure_get_name (structure), "video/") || |
| g_str_has_prefix (gst_structure_get_name (structure), "image/")) { |
| format = GST_VIDEO_FORMAT_ENCODED; |
| } else { |
| goto wrong_name; |
| } |
| |
| /* width and height are mandatory, except for non-raw-formats */ |
| if (!gst_structure_get_int (structure, "width", &width) && |
| format != GST_VIDEO_FORMAT_ENCODED) |
| goto no_width; |
| if (!gst_structure_get_int (structure, "height", &height) && |
| format != GST_VIDEO_FORMAT_ENCODED) |
| goto no_height; |
| |
| gst_video_info_init (info); |
| |
| info->finfo = gst_video_format_get_info (format); |
| info->width = width; |
| info->height = height; |
| |
| if (gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)) { |
| if (fps_n == 0) { |
| /* variable framerate */ |
| info->flags |= GST_VIDEO_FLAG_VARIABLE_FPS; |
| /* see if we have a max-framerate */ |
| gst_structure_get_fraction (structure, "max-framerate", &fps_n, &fps_d); |
| } |
| info->fps_n = fps_n; |
| info->fps_d = fps_d; |
| } else { |
| /* unspecified is variable framerate */ |
| info->fps_n = 0; |
| info->fps_d = 1; |
| } |
| |
| if (gst_structure_get_fraction (structure, "pixel-aspect-ratio", |
| &par_n, &par_d)) { |
| info->par_n = par_n; |
| info->par_d = par_d; |
| } else { |
| info->par_n = 1; |
| info->par_d = 1; |
| } |
| |
| if ((s = gst_structure_get_string (structure, "interlace-mode"))) |
| info->interlace_mode = gst_video_interlace_mode_from_string (s); |
| else |
| info->interlace_mode = GST_VIDEO_INTERLACE_MODE_PROGRESSIVE; |
| |
| if (info->interlace_mode == GST_VIDEO_INTERLACE_MODE_INTERLEAVED && |
| (s = gst_structure_get_string (structure, "field-order"))) { |
| GST_VIDEO_INFO_FIELD_ORDER (info) = gst_video_field_order_from_string (s); |
| } else { |
| GST_VIDEO_INFO_FIELD_ORDER (info) = GST_VIDEO_FIELD_ORDER_UNKNOWN; |
| } |
| |
| { |
| if ((s = gst_structure_get_string (structure, "multiview-mode"))) |
| GST_VIDEO_INFO_MULTIVIEW_MODE (info) = |
| gst_video_multiview_mode_from_caps_string (s); |
| else |
| GST_VIDEO_INFO_MULTIVIEW_MODE (info) = GST_VIDEO_MULTIVIEW_MODE_NONE; |
| |
| if (gst_structure_get_flagset (structure, "multiview-flags", |
| &multiview_flags, NULL)) |
| GST_VIDEO_INFO_MULTIVIEW_FLAGS (info) = multiview_flags; |
| |
| if (!gst_structure_get_int (structure, "views", &info->views)) |
| info->views = 1; |
| |
| /* At one point, I tried normalising the half-aspect flag here, |
| * but it behaves weird for GstVideoInfo operations other than |
| * directly converting to/from caps - sometimes causing the |
| * PAR to be doubled/halved too many times */ |
| } |
| |
| if ((s = gst_structure_get_string (structure, "chroma-site"))) |
| info->chroma_site = gst_video_chroma_from_string (s); |
| else |
| info->chroma_site = GST_VIDEO_CHROMA_SITE_UNKNOWN; |
| |
| if ((s = gst_structure_get_string (structure, "colorimetry"))) { |
| if (!gst_video_colorimetry_from_string (&info->colorimetry, s)) { |
| GST_WARNING ("unparsable colorimetry, using default"); |
| set_default_colorimetry (info); |
| } else if (!validate_colorimetry (info)) { |
| GST_WARNING ("invalid colorimetry, using default"); |
| set_default_colorimetry (info); |
| } else { |
| /* force RGB matrix for RGB formats */ |
| if (GST_VIDEO_FORMAT_INFO_IS_RGB (info->finfo) && |
| info->colorimetry.matrix != GST_VIDEO_COLOR_MATRIX_RGB) { |
| GST_WARNING ("invalid matrix %d for RGB format, using RGB", |
| info->colorimetry.matrix); |
| info->colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_RGB; |
| } |
| } |
| } else { |
| GST_DEBUG ("no colorimetry, using default"); |
| set_default_colorimetry (info); |
| } |
| |
| if (!fill_planes (info)) |
| return FALSE; |
| |
| return TRUE; |
| |
| /* ERROR */ |
| wrong_name: |
| { |
| GST_ERROR ("wrong name '%s', expected video/ or image/", |
| gst_structure_get_name (structure)); |
| return FALSE; |
| } |
| no_format: |
| { |
| GST_ERROR ("no format given"); |
| return FALSE; |
| } |
| unknown_format: |
| { |
| GST_ERROR ("unknown format '%s' given", s); |
| return FALSE; |
| } |
| no_width: |
| { |
| GST_ERROR ("no width property given"); |
| return FALSE; |
| } |
| no_height: |
| { |
| GST_ERROR ("no height property given"); |
| return FALSE; |
| } |
| } |
| |
| /** |
| * gst_video_info_is_equal: |
| * @info: a #GstVideoInfo |
| * @other: a #GstVideoInfo |
| * |
| * Compares two #GstVideoInfo and returns whether they are equal or not |
| * |
| * Returns: %TRUE if @info and @other are equal, else %FALSE. |
| */ |
| gboolean |
| gst_video_info_is_equal (const GstVideoInfo * info, const GstVideoInfo * other) |
| { |
| gint i; |
| |
| if (GST_VIDEO_INFO_FORMAT (info) != GST_VIDEO_INFO_FORMAT (other)) |
| return FALSE; |
| if (GST_VIDEO_INFO_INTERLACE_MODE (info) != |
| GST_VIDEO_INFO_INTERLACE_MODE (other)) |
| return FALSE; |
| if (GST_VIDEO_INFO_FLAGS (info) != GST_VIDEO_INFO_FLAGS (other)) |
| return FALSE; |
| if (GST_VIDEO_INFO_WIDTH (info) != GST_VIDEO_INFO_WIDTH (other)) |
| return FALSE; |
| if (GST_VIDEO_INFO_HEIGHT (info) != GST_VIDEO_INFO_HEIGHT (other)) |
| return FALSE; |
| if (GST_VIDEO_INFO_SIZE (info) != GST_VIDEO_INFO_SIZE (other)) |
| return FALSE; |
| if (GST_VIDEO_INFO_PAR_N (info) != GST_VIDEO_INFO_PAR_N (other)) |
| return FALSE; |
| if (GST_VIDEO_INFO_PAR_D (info) != GST_VIDEO_INFO_PAR_D (other)) |
| return FALSE; |
| if (GST_VIDEO_INFO_FPS_N (info) != GST_VIDEO_INFO_FPS_N (other)) |
| return FALSE; |
| if (GST_VIDEO_INFO_FPS_D (info) != GST_VIDEO_INFO_FPS_D (other)) |
| return FALSE; |
| if (!gst_video_colorimetry_is_equal (&GST_VIDEO_INFO_COLORIMETRY (info), |
| &GST_VIDEO_INFO_COLORIMETRY (other))) |
| return FALSE; |
| if (GST_VIDEO_INFO_CHROMA_SITE (info) != GST_VIDEO_INFO_CHROMA_SITE (other)) |
| return FALSE; |
| if (GST_VIDEO_INFO_MULTIVIEW_MODE (info) != |
| GST_VIDEO_INFO_MULTIVIEW_MODE (other)) |
| return FALSE; |
| if (GST_VIDEO_INFO_MULTIVIEW_FLAGS (info) != |
| GST_VIDEO_INFO_MULTIVIEW_FLAGS (other)) |
| return FALSE; |
| if (GST_VIDEO_INFO_VIEWS (info) != GST_VIDEO_INFO_VIEWS (other)) |
| return FALSE; |
| |
| for (i = 0; i < info->finfo->n_planes; i++) { |
| if (info->stride[i] != other->stride[i]) |
| return FALSE; |
| if (info->offset[i] != other->offset[i]) |
| return FALSE; |
| } |
| |
| return TRUE; |
| } |
| |
| /** |
| * gst_video_info_to_caps: |
| * @info: a #GstVideoInfo |
| * |
| * Convert the values of @info into a #GstCaps. |
| * |
| * Returns: a new #GstCaps containing the info of @info. |
| */ |
| GstCaps * |
| gst_video_info_to_caps (GstVideoInfo * info) |
| { |
| GstCaps *caps; |
| const gchar *format; |
| gchar *color; |
| gint par_n, par_d; |
| GstVideoColorimetry colorimetry; |
| |
| g_return_val_if_fail (info != NULL, NULL); |
| g_return_val_if_fail (info->finfo != NULL, NULL); |
| g_return_val_if_fail (info->finfo->format != GST_VIDEO_FORMAT_UNKNOWN, NULL); |
| |
| format = gst_video_format_to_string (info->finfo->format); |
| g_return_val_if_fail (format != NULL, NULL); |
| |
| caps = gst_caps_new_simple ("video/x-raw", |
| "format", G_TYPE_STRING, format, |
| "width", G_TYPE_INT, info->width, |
| "height", G_TYPE_INT, info->height, NULL); |
| |
| par_n = info->par_n; |
| par_d = info->par_d; |
| |
| gst_caps_set_simple (caps, "interlace-mode", G_TYPE_STRING, |
| gst_video_interlace_mode_to_string (info->interlace_mode), NULL); |
| |
| if (info->interlace_mode == GST_VIDEO_INTERLACE_MODE_INTERLEAVED && |
| GST_VIDEO_INFO_FIELD_ORDER (info) != GST_VIDEO_FIELD_ORDER_UNKNOWN) { |
| gst_caps_set_simple (caps, "field-order", G_TYPE_STRING, |
| gst_video_field_order_to_string (GST_VIDEO_INFO_FIELD_ORDER (info)), |
| NULL); |
| } |
| |
| if (GST_VIDEO_INFO_MULTIVIEW_MODE (info) != GST_VIDEO_MULTIVIEW_MODE_NONE) { |
| const gchar *caps_str = NULL; |
| |
| /* If the half-aspect flag is set, applying it into the PAR of the |
| * resulting caps now seems safe, and helps with automatic behaviour |
| * in elements that aren't explicitly multiview aware */ |
| if (GST_VIDEO_INFO_MULTIVIEW_FLAGS (info) & |
| GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT) { |
| GST_VIDEO_INFO_MULTIVIEW_FLAGS (info) &= |
| ~GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT; |
| switch (GST_VIDEO_INFO_MULTIVIEW_MODE (info)) { |
| case GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE: |
| case GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE_QUINCUNX: |
| case GST_VIDEO_MULTIVIEW_MODE_COLUMN_INTERLEAVED: |
| case GST_VIDEO_MULTIVIEW_MODE_CHECKERBOARD: |
| par_n *= 2; /* double the width / half the height */ |
| break; |
| case GST_VIDEO_MULTIVIEW_MODE_ROW_INTERLEAVED: |
| case GST_VIDEO_MULTIVIEW_MODE_TOP_BOTTOM: |
| par_d *= 2; /* half the width / double the height */ |
| break; |
| default: |
| break; |
| } |
| } |
| |
| caps_str = |
| gst_video_multiview_mode_to_caps_string (GST_VIDEO_INFO_MULTIVIEW_MODE |
| (info)); |
| if (caps_str != NULL) { |
| gst_caps_set_simple (caps, "multiview-mode", G_TYPE_STRING, |
| caps_str, "multiview-flags", GST_TYPE_VIDEO_MULTIVIEW_FLAGSET, |
| GST_VIDEO_INFO_MULTIVIEW_FLAGS (info), GST_FLAG_SET_MASK_EXACT, NULL); |
| } |
| } |
| |
| gst_caps_set_simple (caps, "pixel-aspect-ratio", |
| GST_TYPE_FRACTION, par_n, par_d, NULL); |
| |
| if (info->chroma_site != GST_VIDEO_CHROMA_SITE_UNKNOWN) |
| gst_caps_set_simple (caps, "chroma-site", G_TYPE_STRING, |
| gst_video_chroma_to_string (info->chroma_site), NULL); |
| |
| /* make sure we set the RGB matrix for RGB formats */ |
| colorimetry = info->colorimetry; |
| if (GST_VIDEO_FORMAT_INFO_IS_RGB (info->finfo) && |
| colorimetry.matrix != GST_VIDEO_COLOR_MATRIX_RGB) { |
| GST_WARNING ("invalid matrix %d for RGB format, using RGB", |
| colorimetry.matrix); |
| colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_RGB; |
| } |
| if ((color = gst_video_colorimetry_to_string (&colorimetry))) { |
| gst_caps_set_simple (caps, "colorimetry", G_TYPE_STRING, color, NULL); |
| g_free (color); |
| } |
| |
| if (info->views > 1) |
| gst_caps_set_simple (caps, "views", G_TYPE_INT, info->views, NULL); |
| |
| if (info->flags & GST_VIDEO_FLAG_VARIABLE_FPS && info->fps_n != 0) { |
| /* variable fps with a max-framerate */ |
| gst_caps_set_simple (caps, "framerate", GST_TYPE_FRACTION, 0, 1, |
| "max-framerate", GST_TYPE_FRACTION, info->fps_n, info->fps_d, NULL); |
| } else { |
| /* no variable fps or no max-framerate */ |
| gst_caps_set_simple (caps, "framerate", GST_TYPE_FRACTION, |
| info->fps_n, info->fps_d, NULL); |
| } |
| |
| return caps; |
| } |
| |
| static gboolean |
| fill_planes (GstVideoInfo * info) |
| { |
| gsize width, height, cr_h; |
| gint bpp = 0, i; |
| |
| width = (gsize) info->width; |
| height = (gsize) info->height; |
| |
| /* Sanity check the resulting frame size for overflows */ |
| for (i = 0; i < GST_VIDEO_INFO_N_COMPONENTS (info); i++) |
| bpp += GST_VIDEO_INFO_COMP_DEPTH (info, i); |
| bpp = GST_ROUND_UP_8 (bpp) / 8; |
| if (bpp > 0 && GST_ROUND_UP_128 ((guint64) width) * ((guint64) height) >= |
| G_MAXUINT / bpp) { |
| GST_ERROR ("Frame size %ux%u would overflow", info->width, info->height); |
| return FALSE; |
| } |
| |
| switch (info->finfo->format) { |
| case GST_VIDEO_FORMAT_YUY2: |
| case GST_VIDEO_FORMAT_YVYU: |
| case GST_VIDEO_FORMAT_UYVY: |
| case GST_VIDEO_FORMAT_VYUY: |
| info->stride[0] = GST_ROUND_UP_4 (width * 2); |
| info->offset[0] = 0; |
| info->size = info->stride[0] * height; |
| break; |
| case GST_VIDEO_FORMAT_AYUV: |
| case GST_VIDEO_FORMAT_RGBx: |
| case GST_VIDEO_FORMAT_RGBA: |
| case GST_VIDEO_FORMAT_BGRx: |
| case GST_VIDEO_FORMAT_BGRA: |
| case GST_VIDEO_FORMAT_xRGB: |
| case GST_VIDEO_FORMAT_ARGB: |
| case GST_VIDEO_FORMAT_xBGR: |
| case GST_VIDEO_FORMAT_ABGR: |
| case GST_VIDEO_FORMAT_r210: |
| info->stride[0] = width * 4; |
| info->offset[0] = 0; |
| info->size = info->stride[0] * height; |
| break; |
| case GST_VIDEO_FORMAT_RGB16: |
| case GST_VIDEO_FORMAT_BGR16: |
| case GST_VIDEO_FORMAT_RGB15: |
| case GST_VIDEO_FORMAT_BGR15: |
| info->stride[0] = GST_ROUND_UP_4 (width * 2); |
| info->offset[0] = 0; |
| info->size = info->stride[0] * height; |
| break; |
| case GST_VIDEO_FORMAT_RGB: |
| case GST_VIDEO_FORMAT_BGR: |
| case GST_VIDEO_FORMAT_v308: |
| case GST_VIDEO_FORMAT_IYU2: |
| info->stride[0] = GST_ROUND_UP_4 (width * 3); |
| info->offset[0] = 0; |
| info->size = info->stride[0] * height; |
| break; |
| case GST_VIDEO_FORMAT_v210: |
| info->stride[0] = ((width + 47) / 48) * 128; |
| info->offset[0] = 0; |
| info->size = info->stride[0] * height; |
| break; |
| case GST_VIDEO_FORMAT_v216: |
| info->stride[0] = GST_ROUND_UP_8 (width * 4); |
| info->offset[0] = 0; |
| info->size = info->stride[0] * height; |
| break; |
| case GST_VIDEO_FORMAT_GRAY8: |
| info->stride[0] = GST_ROUND_UP_4 (width); |
| info->offset[0] = 0; |
| info->size = info->stride[0] * height; |
| break; |
| case GST_VIDEO_FORMAT_GRAY16_BE: |
| case GST_VIDEO_FORMAT_GRAY16_LE: |
| info->stride[0] = GST_ROUND_UP_4 (width * 2); |
| info->offset[0] = 0; |
| info->size = info->stride[0] * height; |
| break; |
| case GST_VIDEO_FORMAT_UYVP: |
| info->stride[0] = GST_ROUND_UP_4 ((width * 2 * 5 + 3) / 4); |
| info->offset[0] = 0; |
| info->size = info->stride[0] * height; |
| break; |
| case GST_VIDEO_FORMAT_RGB8P: |
| info->stride[0] = GST_ROUND_UP_4 (width); |
| info->stride[1] = 4; |
| info->offset[0] = 0; |
| info->offset[1] = info->stride[0] * height; |
| info->size = info->offset[1] + (4 * 256); |
| break; |
| case GST_VIDEO_FORMAT_IYU1: |
| info->stride[0] = GST_ROUND_UP_4 (GST_ROUND_UP_4 (width) + |
| GST_ROUND_UP_4 (width) / 2); |
| info->offset[0] = 0; |
| info->size = info->stride[0] * height; |
| break; |
| case GST_VIDEO_FORMAT_ARGB64: |
| case GST_VIDEO_FORMAT_AYUV64: |
| info->stride[0] = width * 8; |
| info->offset[0] = 0; |
| info->size = info->stride[0] * height; |
| break; |
| case GST_VIDEO_FORMAT_I420: |
| case GST_VIDEO_FORMAT_YV12: /* same as I420, but plane 1+2 swapped */ |
| info->stride[0] = GST_ROUND_UP_4 (width); |
| info->stride[1] = GST_ROUND_UP_4 (GST_ROUND_UP_2 (width) / 2); |
| info->stride[2] = info->stride[1]; |
| info->offset[0] = 0; |
| info->offset[1] = info->stride[0] * GST_ROUND_UP_2 (height); |
| cr_h = GST_ROUND_UP_2 (height) / 2; |
| if (GST_VIDEO_INFO_IS_INTERLACED (info)) |
| cr_h = GST_ROUND_UP_2 (cr_h); |
| info->offset[2] = info->offset[1] + info->stride[1] * cr_h; |
| info->size = info->offset[2] + info->stride[2] * cr_h; |
| break; |
| case GST_VIDEO_FORMAT_Y41B: |
| info->stride[0] = GST_ROUND_UP_4 (width); |
| info->stride[1] = GST_ROUND_UP_16 (width) / 4; |
| info->stride[2] = info->stride[1]; |
| info->offset[0] = 0; |
| info->offset[1] = info->stride[0] * height; |
| info->offset[2] = info->offset[1] + info->stride[1] * height; |
| /* simplification of ROUNDUP4(w)*h + 2*((ROUNDUP16(w)/4)*h */ |
| info->size = (info->stride[0] + (GST_ROUND_UP_16 (width) / 2)) * height; |
| break; |
| case GST_VIDEO_FORMAT_Y42B: |
| info->stride[0] = GST_ROUND_UP_4 (width); |
| info->stride[1] = GST_ROUND_UP_8 (width) / 2; |
| info->stride[2] = info->stride[1]; |
| info->offset[0] = 0; |
| info->offset[1] = info->stride[0] * height; |
| info->offset[2] = info->offset[1] + info->stride[1] * height; |
| /* simplification of ROUNDUP4(w)*h + 2*(ROUNDUP8(w)/2)*h */ |
| info->size = (info->stride[0] + GST_ROUND_UP_8 (width)) * height; |
| break; |
| case GST_VIDEO_FORMAT_Y444: |
| case GST_VIDEO_FORMAT_GBR: |
| info->stride[0] = GST_ROUND_UP_4 (width); |
| info->stride[1] = info->stride[0]; |
| info->stride[2] = info->stride[0]; |
| info->offset[0] = 0; |
| info->offset[1] = info->stride[0] * height; |
| info->offset[2] = info->offset[1] * 2; |
| info->size = info->stride[0] * height * 3; |
| break; |
| case GST_VIDEO_FORMAT_GBRA: |
| info->stride[0] = GST_ROUND_UP_4 (width); |
| info->stride[1] = info->stride[0]; |
| info->stride[2] = info->stride[0]; |
| info->stride[3] = info->stride[0]; |
| info->offset[0] = 0; |
| info->offset[1] = info->stride[0] * height; |
| info->offset[2] = info->offset[1] * 2; |
| info->offset[3] = info->offset[1] * 3; |
| info->size = info->stride[0] * height * 4; |
| break; |
| case GST_VIDEO_FORMAT_NV12: |
| case GST_VIDEO_FORMAT_NV21: |
| info->stride[0] = GST_ROUND_UP_4 (width); |
| info->stride[1] = info->stride[0]; |
| info->offset[0] = 0; |
| info->offset[1] = info->stride[0] * GST_ROUND_UP_2 (height); |
| cr_h = GST_ROUND_UP_2 (height) / 2; |
| if (GST_VIDEO_INFO_IS_INTERLACED (info)) |
| cr_h = GST_ROUND_UP_2 (cr_h); |
| info->size = info->offset[1] + info->stride[0] * cr_h; |
| break; |
| case GST_VIDEO_FORMAT_NV16: |
| case GST_VIDEO_FORMAT_NV61: |
| info->stride[0] = GST_ROUND_UP_4 (width); |
| info->stride[1] = info->stride[0]; |
| info->offset[0] = 0; |
| info->offset[1] = info->stride[0] * height; |
| info->size = info->stride[0] * height * 2; |
| break; |
| case GST_VIDEO_FORMAT_NV24: |
| info->stride[0] = GST_ROUND_UP_4 (width); |
| info->stride[1] = GST_ROUND_UP_4 (width * 2); |
| info->offset[0] = 0; |
| info->offset[1] = info->stride[0] * height; |
| info->size = info->stride[0] * height + info->stride[1] * height; |
| break; |
| case GST_VIDEO_FORMAT_A420: |
| info->stride[0] = GST_ROUND_UP_4 (width); |
| info->stride[1] = GST_ROUND_UP_4 (GST_ROUND_UP_2 (width) / 2); |
| info->stride[2] = info->stride[1]; |
| info->stride[3] = info->stride[0]; |
| info->offset[0] = 0; |
| info->offset[1] = info->stride[0] * GST_ROUND_UP_2 (height); |
| cr_h = GST_ROUND_UP_2 (height) / 2; |
| if (GST_VIDEO_INFO_IS_INTERLACED (info)) |
| cr_h = GST_ROUND_UP_2 (cr_h); |
| info->offset[2] = info->offset[1] + info->stride[1] * cr_h; |
| info->offset[3] = info->offset[2] + info->stride[2] * cr_h; |
| info->size = info->offset[3] + info->stride[0] * GST_ROUND_UP_2 (height); |
| break; |
| case GST_VIDEO_FORMAT_YUV9: |
| case GST_VIDEO_FORMAT_YVU9: |
| info->stride[0] = GST_ROUND_UP_4 (width); |
| info->stride[1] = GST_ROUND_UP_4 (GST_ROUND_UP_4 (width) / 4); |
| info->stride[2] = info->stride[1]; |
| info->offset[0] = 0; |
| info->offset[1] = info->stride[0] * height; |
| cr_h = GST_ROUND_UP_4 (height) / 4; |
| if (GST_VIDEO_INFO_IS_INTERLACED (info)) |
| cr_h = GST_ROUND_UP_2 (cr_h); |
| info->offset[2] = info->offset[1] + info->stride[1] * cr_h; |
| info->size = info->offset[2] + info->stride[2] * cr_h; |
| break; |
| case GST_VIDEO_FORMAT_I420_10LE: |
| case GST_VIDEO_FORMAT_I420_10BE: |
| case GST_VIDEO_FORMAT_I420_12LE: |
| case GST_VIDEO_FORMAT_I420_12BE: |
| info->stride[0] = GST_ROUND_UP_4 (width * 2); |
| info->stride[1] = GST_ROUND_UP_4 (width); |
| info->stride[2] = info->stride[1]; |
| info->offset[0] = 0; |
| info->offset[1] = info->stride[0] * GST_ROUND_UP_2 (height); |
| cr_h = GST_ROUND_UP_2 (height) / 2; |
| if (GST_VIDEO_INFO_IS_INTERLACED (info)) |
| cr_h = GST_ROUND_UP_2 (cr_h); |
| info->offset[2] = info->offset[1] + info->stride[1] * cr_h; |
| info->size = info->offset[2] + info->stride[2] * cr_h; |
| break; |
| case GST_VIDEO_FORMAT_I422_10LE: |
| case GST_VIDEO_FORMAT_I422_10BE: |
| case GST_VIDEO_FORMAT_I422_12LE: |
| case GST_VIDEO_FORMAT_I422_12BE: |
| info->stride[0] = GST_ROUND_UP_4 (width * 2); |
| info->stride[1] = GST_ROUND_UP_4 (width); |
| info->stride[2] = info->stride[1]; |
| info->offset[0] = 0; |
| info->offset[1] = info->stride[0] * GST_ROUND_UP_2 (height); |
| info->offset[2] = info->offset[1] + |
| info->stride[1] * GST_ROUND_UP_2 (height); |
| info->size = info->offset[2] + info->stride[2] * GST_ROUND_UP_2 (height); |
| break; |
| case GST_VIDEO_FORMAT_Y444_10LE: |
| case GST_VIDEO_FORMAT_Y444_10BE: |
| case GST_VIDEO_FORMAT_Y444_12LE: |
| case GST_VIDEO_FORMAT_Y444_12BE: |
| case GST_VIDEO_FORMAT_GBR_10LE: |
| case GST_VIDEO_FORMAT_GBR_10BE: |
| case GST_VIDEO_FORMAT_GBR_12LE: |
| case GST_VIDEO_FORMAT_GBR_12BE: |
| info->stride[0] = GST_ROUND_UP_4 (width * 2); |
| info->stride[1] = info->stride[0]; |
| info->stride[2] = info->stride[0]; |
| info->offset[0] = 0; |
| info->offset[1] = info->stride[0] * height; |
| info->offset[2] = info->offset[1] * 2; |
| info->size = info->stride[0] * height * 3; |
| break; |
| case GST_VIDEO_FORMAT_GBRA_10LE: |
| case GST_VIDEO_FORMAT_GBRA_10BE: |
| case GST_VIDEO_FORMAT_GBRA_12LE: |
| case GST_VIDEO_FORMAT_GBRA_12BE: |
| info->stride[0] = GST_ROUND_UP_4 (width * 2); |
| info->stride[1] = info->stride[0]; |
| info->stride[2] = info->stride[0]; |
| info->stride[3] = info->stride[0]; |
| info->offset[0] = 0; |
| info->offset[1] = info->stride[0] * height; |
| info->offset[2] = info->offset[1] * 2; |
| info->offset[3] = info->offset[1] * 3; |
| info->size = info->stride[0] * height * 4; |
| break; |
| case GST_VIDEO_FORMAT_NV12_64Z32: |
| info->stride[0] = |
| GST_VIDEO_TILE_MAKE_STRIDE (GST_ROUND_UP_128 (width) / 64, |
| GST_ROUND_UP_32 (height) / 32); |
| info->stride[1] = |
| GST_VIDEO_TILE_MAKE_STRIDE (GST_ROUND_UP_128 (width) / 64, |
| GST_ROUND_UP_64 (height) / 64); |
| info->offset[0] = 0; |
| info->offset[1] = GST_ROUND_UP_128 (width) * GST_ROUND_UP_32 (height); |
| info->size = info->offset[1] + |
| GST_ROUND_UP_128 (width) * GST_ROUND_UP_64 (height) / 2; |
| break; |
| case GST_VIDEO_FORMAT_A420_10LE: |
| case GST_VIDEO_FORMAT_A420_10BE: |
| info->stride[0] = GST_ROUND_UP_4 (width * 2); |
| info->stride[1] = GST_ROUND_UP_4 (width); |
| info->stride[2] = info->stride[1]; |
| info->stride[3] = info->stride[0]; |
| info->offset[0] = 0; |
| info->offset[1] = info->stride[0] * GST_ROUND_UP_2 (height); |
| cr_h = GST_ROUND_UP_2 (height) / 2; |
| if (GST_VIDEO_INFO_IS_INTERLACED (info)) |
| cr_h = GST_ROUND_UP_2 (cr_h); |
| info->offset[2] = info->offset[1] + info->stride[1] * cr_h; |
| info->offset[3] = info->offset[2] + info->stride[2] * cr_h; |
| info->size = info->offset[3] + info->stride[0] * GST_ROUND_UP_2 (height); |
| break; |
| case GST_VIDEO_FORMAT_A422_10LE: |
| case GST_VIDEO_FORMAT_A422_10BE: |
| info->stride[0] = GST_ROUND_UP_4 (width * 2); |
| info->stride[1] = GST_ROUND_UP_4 (width); |
| info->stride[2] = info->stride[1]; |
| info->stride[3] = info->stride[0]; |
| info->offset[0] = 0; |
| info->offset[1] = info->stride[0] * GST_ROUND_UP_2 (height); |
| info->offset[2] = info->offset[1] + |
| info->stride[1] * GST_ROUND_UP_2 (height); |
| info->offset[3] = |
| info->offset[2] + info->stride[2] * GST_ROUND_UP_2 (height); |
| info->size = info->offset[3] + info->stride[0] * GST_ROUND_UP_2 (height); |
| break; |
| case GST_VIDEO_FORMAT_A444_10LE: |
| case GST_VIDEO_FORMAT_A444_10BE: |
| info->stride[0] = GST_ROUND_UP_4 (width * 2); |
| info->stride[1] = info->stride[0]; |
| info->stride[2] = info->stride[0]; |
| info->stride[3] = info->stride[0]; |
| info->offset[0] = 0; |
| info->offset[1] = info->stride[0] * height; |
| info->offset[2] = info->offset[1] * 2; |
| info->offset[3] = info->offset[1] * 3; |
| info->size = info->stride[0] * height * 4; |
| break; |
| case GST_VIDEO_FORMAT_P010_10LE: |
| case GST_VIDEO_FORMAT_P010_10BE: |
| info->stride[0] = GST_ROUND_UP_4 (width * 2); |
| info->stride[1] = info->stride[0]; |
| info->offset[0] = 0; |
| info->offset[1] = info->stride[0] * GST_ROUND_UP_2 (height); |
| cr_h = GST_ROUND_UP_2 (height) / 2; |
| info->size = info->offset[1] + info->stride[0] * cr_h; |
| break; |
| case GST_VIDEO_FORMAT_GRAY10_LE32: |
| info->stride[0] = (width + 2) / 3 * 4; |
| info->offset[0] = 0; |
| info->size = info->stride[0] * GST_ROUND_UP_2 (height); |
| break; |
| case GST_VIDEO_FORMAT_NV12_10LE32: |
| info->stride[0] = (width + 2) / 3 * 4; |
| info->stride[1] = info->stride[0]; |
| info->offset[0] = 0; |
| info->offset[1] = info->stride[0] * GST_ROUND_UP_2 (height); |
| cr_h = GST_ROUND_UP_2 (height) / 2; |
| if (GST_VIDEO_INFO_IS_INTERLACED (info)) |
| cr_h = GST_ROUND_UP_2 (cr_h); |
| info->size = info->offset[1] + info->stride[0] * cr_h; |
| break; |
| case GST_VIDEO_FORMAT_NV16_10LE32: |
| info->stride[0] = (width + 2) / 3 * 4; |
| info->stride[1] = info->stride[0]; |
| info->offset[0] = 0; |
| info->offset[1] = info->stride[0] * height; |
| info->size = info->stride[0] * height * 2; |
| break; |
| case GST_VIDEO_FORMAT_NV12_10LE: |
| info->stride[0] = GST_ROUND_UP_4 (width * 5 / 4); |
| info->stride[1] = info->stride[0]; |
| info->offset[0] = 0; |
| info->offset[1] = info->stride[0] * GST_ROUND_UP_2 (height); |
| cr_h = GST_ROUND_UP_2 (height) / 2; |
| info->size = info->offset[1] + info->stride[0] * cr_h; |
| break; |
| |
| case GST_VIDEO_FORMAT_ENCODED: |
| break; |
| case GST_VIDEO_FORMAT_UNKNOWN: |
| GST_ERROR ("invalid format"); |
| g_warning ("invalid format"); |
| return FALSE; |
| break; |
| } |
| return TRUE; |
| } |
| |
| /** |
| * gst_video_info_convert: |
| * @info: a #GstVideoInfo |
| * @src_format: #GstFormat of the @src_value |
| * @src_value: value to convert |
| * @dest_format: #GstFormat of the @dest_value |
| * @dest_value: (out): pointer to destination value |
| * |
| * Converts among various #GstFormat types. This function handles |
| * GST_FORMAT_BYTES, GST_FORMAT_TIME, and GST_FORMAT_DEFAULT. For |
| * raw video, GST_FORMAT_DEFAULT corresponds to video frames. This |
| * function can be used to handle pad queries of the type GST_QUERY_CONVERT. |
| * |
| * Returns: TRUE if the conversion was successful. |
| */ |
| gboolean |
| gst_video_info_convert (GstVideoInfo * info, |
| GstFormat src_format, gint64 src_value, |
| GstFormat dest_format, gint64 * dest_value) |
| { |
| gboolean ret = FALSE; |
| int fps_n, fps_d; |
| gsize size; |
| |
| g_return_val_if_fail (info != NULL, 0); |
| g_return_val_if_fail (info->finfo != NULL, 0); |
| g_return_val_if_fail (info->finfo->format != GST_VIDEO_FORMAT_UNKNOWN, 0); |
| g_return_val_if_fail (info->size > 0, 0); |
| |
| size = info->size; |
| fps_n = info->fps_n; |
| fps_d = info->fps_d; |
| |
| GST_DEBUG ("converting value %" G_GINT64_FORMAT " from %s to %s", |
| src_value, gst_format_get_name (src_format), |
| gst_format_get_name (dest_format)); |
| |
| if (src_format == dest_format) { |
| *dest_value = src_value; |
| ret = TRUE; |
| goto done; |
| } |
| |
| if (src_value == -1) { |
| *dest_value = -1; |
| ret = TRUE; |
| goto done; |
| } |
| |
| /* bytes to frames */ |
| if (src_format == GST_FORMAT_BYTES && dest_format == GST_FORMAT_DEFAULT) { |
| if (size != 0) { |
| *dest_value = gst_util_uint64_scale (src_value, 1, size); |
| } else { |
| GST_ERROR ("blocksize is 0"); |
| *dest_value = 0; |
| } |
| ret = TRUE; |
| goto done; |
| } |
| |
| /* frames to bytes */ |
| if (src_format == GST_FORMAT_DEFAULT && dest_format == GST_FORMAT_BYTES) { |
| *dest_value = gst_util_uint64_scale (src_value, size, 1); |
| ret = TRUE; |
| goto done; |
| } |
| |
| /* time to frames */ |
| if (src_format == GST_FORMAT_TIME && dest_format == GST_FORMAT_DEFAULT) { |
| if (fps_d != 0) { |
| *dest_value = gst_util_uint64_scale (src_value, |
| fps_n, GST_SECOND * fps_d); |
| } else { |
| GST_ERROR ("framerate denominator is 0"); |
| *dest_value = 0; |
| } |
| ret = TRUE; |
| goto done; |
| } |
| |
| /* frames to time */ |
| if (src_format == GST_FORMAT_DEFAULT && dest_format == GST_FORMAT_TIME) { |
| if (fps_n != 0) { |
| *dest_value = gst_util_uint64_scale (src_value, |
| GST_SECOND * fps_d, fps_n); |
| } else { |
| GST_ERROR ("framerate numerator is 0"); |
| *dest_value = 0; |
| } |
| ret = TRUE; |
| goto done; |
| } |
| |
| /* time to bytes */ |
| if (src_format == GST_FORMAT_TIME && dest_format == GST_FORMAT_BYTES) { |
| if (fps_d != 0) { |
| *dest_value = gst_util_uint64_scale (src_value, |
| fps_n * size, GST_SECOND * fps_d); |
| } else { |
| GST_ERROR ("framerate denominator is 0"); |
| *dest_value = 0; |
| } |
| ret = TRUE; |
| goto done; |
| } |
| |
| /* bytes to time */ |
| if (src_format == GST_FORMAT_BYTES && dest_format == GST_FORMAT_TIME) { |
| if (fps_n != 0 && size != 0) { |
| *dest_value = gst_util_uint64_scale (src_value, |
| GST_SECOND * fps_d, fps_n * size); |
| } else { |
| GST_ERROR ("framerate denominator and/or blocksize is 0"); |
| *dest_value = 0; |
| } |
| ret = TRUE; |
| } |
| |
| done: |
| |
| GST_DEBUG ("ret=%d result %" G_GINT64_FORMAT, ret, *dest_value); |
| |
| return ret; |
| } |
| |
| /** |
| * gst_video_info_align: |
| * @info: a #GstVideoInfo |
| * @align: alignment parameters |
| * |
| * Adjust the offset and stride fields in @info so that the padding and |
| * stride alignment in @align is respected. |
| * |
| * Extra padding will be added to the right side when stride alignment padding |
| * is required and @align will be updated with the new padding values. |
| * |
| * Returns: %FALSE if alignment could not be applied, e.g. because the |
| * size of a frame can't be represented as a 32 bit integer (Since: 1.12) |
| */ |
| gboolean |
| gst_video_info_align (GstVideoInfo * info, GstVideoAlignment * align) |
| { |
| const GstVideoFormatInfo *vinfo = info->finfo; |
| gint width, height; |
| gint padded_width, padded_height; |
| gint i, n_planes; |
| gboolean aligned; |
| |
| width = GST_VIDEO_INFO_WIDTH (info); |
| height = GST_VIDEO_INFO_HEIGHT (info); |
| |
| GST_LOG ("padding %u-%ux%u-%u", align->padding_top, |
| align->padding_left, align->padding_right, align->padding_bottom); |
| |
| n_planes = GST_VIDEO_INFO_N_PLANES (info); |
| |
| if (GST_VIDEO_FORMAT_INFO_HAS_PALETTE (vinfo)) |
| n_planes--; |
| |
| /* first make sure the left padding does not cause alignment problems later */ |
| do { |
| GST_LOG ("left padding %u", align->padding_left); |
| aligned = TRUE; |
| for (i = 0; i < n_planes; i++) { |
| gint hedge; |
| |
| /* this is the amout of pixels to add as left padding */ |
| hedge = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (vinfo, i, align->padding_left); |
| hedge *= GST_VIDEO_FORMAT_INFO_PSTRIDE (vinfo, i); |
| |
| GST_LOG ("plane %d, padding %d, alignment %u", i, hedge, |
| align->stride_align[i]); |
| aligned &= (hedge & align->stride_align[i]) == 0; |
| } |
| if (aligned) |
| break; |
| |
| GST_LOG ("unaligned padding, increasing padding"); |
| /* increase padded_width */ |
| align->padding_left += align->padding_left & ~(align->padding_left - 1); |
| } while (!aligned); |
| |
| /* add the padding */ |
| padded_width = width + align->padding_left + align->padding_right; |
| padded_height = height + align->padding_top + align->padding_bottom; |
| |
| do { |
| GST_LOG ("padded dimension %u-%u", padded_width, padded_height); |
| |
| info->width = padded_width; |
| info->height = padded_height; |
| |
| if (!fill_planes (info)) |
| return FALSE; |
| |
| /* check alignment */ |
| aligned = TRUE; |
| for (i = 0; i < n_planes; i++) { |
| GST_LOG ("plane %d, stride %d, alignment %u", i, info->stride[i], |
| align->stride_align[i]); |
| aligned &= (info->stride[i] & align->stride_align[i]) == 0; |
| } |
| if (aligned) |
| break; |
| |
| GST_LOG ("unaligned strides, increasing dimension"); |
| /* increase padded_width */ |
| padded_width += padded_width & ~(padded_width - 1); |
| } while (!aligned); |
| |
| align->padding_right = padded_width - width - align->padding_left; |
| |
| info->width = width; |
| info->height = height; |
| |
| for (i = 0; i < n_planes; i++) { |
| gint vedge, hedge, comp; |
| |
| /* Find the component for this plane, FIXME, we assume the plane number and |
| * component number is the same for now, for scaling the dimensions this is |
| * currently true for all formats but it might not be when adding new |
| * formats. We might need to add a plane subsamling in the format info to |
| * make this more generic or maybe use a plane -> component mapping. */ |
| comp = i; |
| |
| hedge = |
| GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (vinfo, comp, align->padding_left); |
| vedge = |
| GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (vinfo, comp, align->padding_top); |
| |
| GST_DEBUG ("plane %d: comp: %d, hedge %d vedge %d align %d stride %d", i, |
| comp, hedge, vedge, align->stride_align[i], info->stride[i]); |
| |
| info->offset[i] += (vedge * info->stride[i]) + |
| (hedge * GST_VIDEO_FORMAT_INFO_PSTRIDE (vinfo, comp)); |
| } |
| |
| return TRUE; |
| } |