Skip to content

Commit

Permalink
videometa: add alignment field
Browse files Browse the repository at this point in the history
By adding this field, buffer producers can now explicitly set the exact
geometry of planes, allowing users to easily know the padded size and
height of each plane.

GstVideoMeta is always heap allocated by GStreamer itself so we can
safely extend it.
  • Loading branch information
Guillaume Desmottes committed Nov 2, 2019
1 parent 36ce088 commit 75680e5
Show file tree
Hide file tree
Showing 3 changed files with 272 additions and 0 deletions.
135 changes: 135 additions & 0 deletions gst-libs/gst/video/gstvideometa.c
Expand Up @@ -64,6 +64,7 @@ gst_video_meta_init (GstMeta * meta, gpointer params, GstBuffer * buffer)
emeta->width = emeta->height = emeta->n_planes = 0;
memset (emeta->offset, 0, sizeof (emeta->offset));
memset (emeta->stride, 0, sizeof (emeta->stride));
gst_video_alignment_reset (&emeta->alignment);
emeta->map = NULL;
emeta->unmap = NULL;

Expand Down Expand Up @@ -104,6 +105,7 @@ gst_video_meta_transform (GstBuffer * dest, GstMeta * meta,
for (i = 0; i < dmeta->n_planes; i++) {
dmeta->offset[i] = smeta->offset[i];
dmeta->stride[i] = smeta->stride[i];
dmeta->alignment = smeta->alignment;
}
dmeta->map = smeta->map;
dmeta->unmap = smeta->unmap;
Expand Down Expand Up @@ -392,6 +394,139 @@ gst_video_meta_unmap (GstVideoMeta * meta, guint plane, GstMapInfo * info)
return meta->unmap (meta, plane, info);
}

static gboolean
gst_video_meta_validate_alignment (GstVideoMeta * meta,
gsize plane_size[GST_VIDEO_MAX_PLANES])
{
GstVideoInfo info;
guint i;

gst_video_info_init (&info);
gst_video_info_set_format (&info, meta->format, meta->width, meta->height);

if (!gst_video_info_align_full (&info, &meta->alignment, plane_size)) {
GST_WARNING ("Failed to align meta with its alignment");
return FALSE;
}

for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&info); i++) {
if (GST_VIDEO_INFO_PLANE_STRIDE (&info, i) != meta->stride[i]) {
GST_WARNING
("Stride of plane %d defined in meta (%d) is different from the one computed from the alignment (%d)",
i, meta->stride[i], GST_VIDEO_INFO_PLANE_STRIDE (&info, i));
return FALSE;
}
}

return TRUE;
}

/**
* gst_video_meta_set_alignment:
* @meta: a #GstVideoMeta
* @alignment: a #GstVideoAlignment
*
* Set the alignment of @meta to @alignment. This function checks that
* the paddings defined in @alignment are compatible with the strides
* defined in @meta and will fail to update if they are not.
*
* Returns: %TRUE if @alignment's meta has been updated, %FALSE if not
*
* Since: 1.18
*/
gboolean
gst_video_meta_set_alignment (GstVideoMeta * meta, GstVideoAlignment alignment)
{
GstVideoAlignment old;

g_return_val_if_fail (meta, FALSE);

old = meta->alignment;
meta->alignment = alignment;

if (!gst_video_meta_validate_alignment (meta, NULL)) {
/* Invalid alignment, restore the previous one */
meta->alignment = old;
return FALSE;
}

GST_LOG ("Set alignment on meta: padding %u-%ux%u-%u", alignment.padding_top,
alignment.padding_left, alignment.padding_right,
alignment.padding_bottom);

return TRUE;
}

/**
* gst_video_meta_get_plane_size:
* @meta: a #GstVideoMeta
* @plane_size: (out): array used to store the plane sizes
*
* Compute the size, in bytes, of each video plane described in @meta including
* any padding and alignment constraint defined in @meta->alignment.
*
* Returns: %TRUE if @meta's alignment is valid and @plane_size has been
* updated, %FALSE otherwise
*
* Since: 1.18
*/
gboolean
gst_video_meta_get_plane_size (GstVideoMeta * meta,
gsize plane_size[GST_VIDEO_MAX_PLANES])
{
g_return_val_if_fail (meta, FALSE);
g_return_val_if_fail (plane_size, FALSE);

return gst_video_meta_validate_alignment (meta, plane_size);
}

/**
* gst_video_meta_get_plane_height:
* @meta: a #GstVideoMeta
* @plane_height: (out): array used to store the plane height
*
* Compute the padded height of each plane from @meta (padded size
* divided by stride).
*
* It is not valid to call this function with a meta associated to a
* TILED video format.
*
* Returns: %TRUE if @meta's alignment is valid and @plane_height has been
* updated, %FALSE otherwise
*
* Since: 1.18
*/
gboolean
gst_video_meta_get_plane_height (GstVideoMeta * meta,
guint plane_height[GST_VIDEO_MAX_PLANES])
{
gsize plane_size[GST_VIDEO_MAX_PLANES];
guint i;
GstVideoInfo info;

g_return_val_if_fail (meta, FALSE);
g_return_val_if_fail (plane_height, FALSE);

gst_video_info_init (&info);
gst_video_info_set_format (&info, meta->format, meta->width, meta->height);
g_return_val_if_fail (!GST_VIDEO_FORMAT_INFO_IS_TILED (&info), FALSE);

if (!gst_video_meta_get_plane_size (meta, plane_size))
return FALSE;

for (i = 0; i < meta->n_planes; i++) {
if (!meta->stride[i])
plane_height[i] = 0;
else
plane_height[i] = plane_size[i] / meta->stride[i];
}

for (; i < GST_VIDEO_MAX_PLANES; i++)
plane_height[i] = 0;

return TRUE;
}

static gboolean
gst_video_crop_meta_transform (GstBuffer * dest, GstMeta * meta,
GstBuffer * buffer, GQuark type, gpointer data)
Expand Down
15 changes: 15 additions & 0 deletions gst-libs/gst/video/gstvideometa.h
Expand Up @@ -53,6 +53,10 @@ typedef struct _GstVideoCropMeta GstVideoCropMeta;
* valid, it is used by the default implementation of @map.
* @map: map the memory of a plane
* @unmap: unmap the memory of a plane
* @alignment: the paddings and alignment constraints of the video buffer.
* It is up to the caller of `gst_buffer_add_video_meta_full()` to set it
* using gst_video_meta_set_alignment(), if they did not it defaults
* to no padding and no alignment. Since: 1.18
*
* Extra buffer metadata describing image properties
*/
Expand All @@ -74,6 +78,8 @@ struct _GstVideoMeta {
gboolean (*map) (GstVideoMeta *meta, guint plane, GstMapInfo *info,
gpointer *data, gint * stride, GstMapFlags flags);
gboolean (*unmap) (GstVideoMeta *meta, guint plane, GstMapInfo *info);

GstVideoAlignment alignment;
};

GST_VIDEO_API
Expand Down Expand Up @@ -105,6 +111,15 @@ gboolean gst_video_meta_map (GstVideoMeta *meta, guint plane, GstMa
GST_VIDEO_API
gboolean gst_video_meta_unmap (GstVideoMeta *meta, guint plane, GstMapInfo *info);

GST_VIDEO_API
gboolean gst_video_meta_set_alignment (GstVideoMeta * meta, GstVideoAlignment alignment);

GST_VIDEO_API
gboolean gst_video_meta_get_plane_size (GstVideoMeta * meta, gsize plane_size[GST_VIDEO_MAX_PLANES]);

GST_VIDEO_API
gboolean gst_video_meta_get_plane_height (GstVideoMeta * meta, guint plane_height[GST_VIDEO_MAX_PLANES]);

/**
* GstVideoCropMeta:
* @meta: parent #GstMeta
Expand Down
122 changes: 122 additions & 0 deletions tests/check/libs/video.c
Expand Up @@ -3626,6 +3626,127 @@ GST_START_TEST (test_video_info_align)

GST_END_TEST;

GST_START_TEST (test_video_meta_align)
{
GstBuffer *buf;
GstVideoInfo info;
GstVideoMeta *meta;
gsize plane_size[GST_VIDEO_MAX_PLANES];
guint plane_height[GST_VIDEO_MAX_PLANES];
GstVideoAlignment alig;

buf = gst_buffer_new ();

/* NV12 no alignment */
gst_video_info_init (&info);
gst_video_info_set_format (&info, GST_VIDEO_FORMAT_NV12, 1920, 1080);

meta = gst_buffer_add_video_meta_full (buf, GST_VIDEO_FRAME_FLAG_NONE,
GST_VIDEO_INFO_FORMAT (&info), GST_VIDEO_INFO_WIDTH (&info),
GST_VIDEO_INFO_HEIGHT (&info), GST_VIDEO_INFO_N_PLANES (&info),
info.offset, info.stride);

g_assert_cmpuint (meta->alignment.padding_top, ==, 0);
g_assert_cmpuint (meta->alignment.padding_bottom, ==, 0);
g_assert_cmpuint (meta->alignment.padding_left, ==, 0);
g_assert_cmpuint (meta->alignment.padding_right, ==, 0);

g_assert (gst_video_meta_get_plane_size (meta, plane_size));
g_assert_cmpuint (plane_size[0], ==, 1920 * 1080);
g_assert_cmpuint (plane_size[1], ==, 1920 * 1080 * 0.5);
g_assert_cmpuint (plane_size[2], ==, 0);
g_assert_cmpuint (plane_size[3], ==, 0);

g_assert (gst_video_meta_get_plane_height (meta, plane_height));
g_assert_cmpuint (plane_height[0], ==, 1080);
g_assert_cmpuint (plane_height[1], ==, 540);
g_assert_cmpuint (plane_height[2], ==, 0);
g_assert_cmpuint (plane_height[3], ==, 0);

/* horizontal alignment */
gst_video_info_init (&info);
gst_video_info_set_format (&info, GST_VIDEO_FORMAT_NV12, 1920, 1080);

gst_video_alignment_reset (&alig);
alig.padding_left = 2;
alig.padding_right = 6;

g_assert (gst_video_info_align (&info, &alig));

meta = gst_buffer_add_video_meta_full (buf, GST_VIDEO_FRAME_FLAG_NONE,
GST_VIDEO_INFO_FORMAT (&info), GST_VIDEO_INFO_WIDTH (&info),
GST_VIDEO_INFO_HEIGHT (&info), GST_VIDEO_INFO_N_PLANES (&info),
info.offset, info.stride);
g_assert (gst_video_meta_set_alignment (meta, alig));

g_assert_cmpuint (meta->alignment.padding_top, ==, 0);
g_assert_cmpuint (meta->alignment.padding_bottom, ==, 0);
g_assert_cmpuint (meta->alignment.padding_left, ==, 2);
g_assert_cmpuint (meta->alignment.padding_right, ==, 6);

g_assert (gst_video_meta_get_plane_size (meta, plane_size));
g_assert_cmpuint (plane_size[0], ==, 1928 * 1080);
g_assert_cmpuint (plane_size[1], ==, 1928 * 1080 * 0.5);
g_assert_cmpuint (plane_size[2], ==, 0);
g_assert_cmpuint (plane_size[3], ==, 0);

g_assert (gst_video_meta_get_plane_height (meta, plane_height));
g_assert_cmpuint (plane_height[0], ==, 1080);
g_assert_cmpuint (plane_height[1], ==, 540);
g_assert_cmpuint (plane_height[2], ==, 0);
g_assert_cmpuint (plane_height[3], ==, 0);

/* vertical alignment */
gst_video_info_init (&info);
gst_video_info_set_format (&info, GST_VIDEO_FORMAT_NV12, 1920, 1080);

gst_video_alignment_reset (&alig);
alig.padding_top = 2;
alig.padding_bottom = 6;

g_assert (gst_video_info_align (&info, &alig));

meta = gst_buffer_add_video_meta_full (buf, GST_VIDEO_FRAME_FLAG_NONE,
GST_VIDEO_INFO_FORMAT (&info), GST_VIDEO_INFO_WIDTH (&info),
GST_VIDEO_INFO_HEIGHT (&info), GST_VIDEO_INFO_N_PLANES (&info),
info.offset, info.stride);
g_assert (gst_video_meta_set_alignment (meta, alig));

g_assert_cmpuint (meta->alignment.padding_top, ==, 2);
g_assert_cmpuint (meta->alignment.padding_bottom, ==, 6);
g_assert_cmpuint (meta->alignment.padding_left, ==, 0);
g_assert_cmpuint (meta->alignment.padding_right, ==, 0);

g_assert (gst_video_meta_get_plane_size (meta, plane_size));
g_assert_cmpuint (plane_size[0], ==, 1920 * 1088);
g_assert_cmpuint (plane_size[1], ==, 1920 * 1088 * 0.5);
g_assert_cmpuint (plane_size[2], ==, 0);
g_assert_cmpuint (plane_size[3], ==, 0);

g_assert (gst_video_meta_get_plane_height (meta, plane_height));
g_assert_cmpuint (plane_height[0], ==, 1088);
g_assert_cmpuint (plane_height[1], ==, 544);
g_assert_cmpuint (plane_height[2], ==, 0);
g_assert_cmpuint (plane_height[3], ==, 0);

/* incompatible alignment */
gst_video_info_init (&info);
gst_video_info_set_format (&info, GST_VIDEO_FORMAT_NV12, 1920, 1080);

gst_video_alignment_reset (&alig);
alig.padding_right = 2;

meta = gst_buffer_add_video_meta_full (buf, GST_VIDEO_FRAME_FLAG_NONE,
GST_VIDEO_INFO_FORMAT (&info), GST_VIDEO_INFO_WIDTH (&info),
GST_VIDEO_INFO_HEIGHT (&info), GST_VIDEO_INFO_N_PLANES (&info),
info.offset, info.stride);
g_assert (!gst_video_meta_set_alignment (meta, alig));

gst_buffer_unref (buf);
}

GST_END_TEST;

static Suite *
video_suite (void)
{
Expand Down Expand Up @@ -3675,6 +3796,7 @@ video_suite (void)
tcase_add_test (tc_chain, test_video_color_from_to_iso);
tcase_add_test (tc_chain, test_video_format_info_plane_to_components);
tcase_add_test (tc_chain, test_video_info_align);
tcase_add_test (tc_chain, test_video_meta_align);

return s;
}
Expand Down

0 comments on commit 75680e5

Please sign in to comment.