mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-26 19:51:11 +00:00
videoparse: add properties to set framesize, strides and planes offsets
To make parser work with image having non-standard strides, plane offsets or with padding between images. For now, since element doesn't check for videometa, we can't directly push buffers when these properties are set so it convert the frame in the pre_push_buffer method to remove any custom padding. https://bugzilla.gnome.org/show_bug.cgi?id=760270
This commit is contained in:
parent
04d59ffd4d
commit
a90f35e14b
2 changed files with 317 additions and 1 deletions
|
@ -41,6 +41,8 @@ static void gst_video_parse_pre_push_buffer (GstRawParse * rp,
|
|||
GstBuffer * buffer);
|
||||
|
||||
static void gst_video_parse_update_info (GstVideoParse * vp);
|
||||
static gboolean gst_video_parse_deserialize_int_array (const gchar * str,
|
||||
gint * dest, guint n_values);
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (gst_video_parse_debug);
|
||||
#define GST_CAT_DEFAULT gst_video_parse_debug
|
||||
|
@ -54,7 +56,10 @@ enum
|
|||
PROP_PAR,
|
||||
PROP_FRAMERATE,
|
||||
PROP_INTERLACED,
|
||||
PROP_TOP_FIELD_FIRST
|
||||
PROP_TOP_FIELD_FIRST,
|
||||
PROP_STRIDES,
|
||||
PROP_OFFSETS,
|
||||
PROP_FRAMESIZE
|
||||
};
|
||||
|
||||
#define gst_video_parse_parent_class parent_class
|
||||
|
@ -100,6 +105,18 @@ gst_video_parse_class_init (GstVideoParseClass * klass)
|
|||
g_param_spec_boolean ("top-field-first", "Top field first",
|
||||
"True if top field is earlier than bottom field", TRUE,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (gobject_class, PROP_STRIDES,
|
||||
g_param_spec_string ("strides", "Strides",
|
||||
"Stride of each planes in bytes using string format: 's0,s1,s2,s3'",
|
||||
NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (gobject_class, PROP_OFFSETS,
|
||||
g_param_spec_string ("offsets", "Offsets",
|
||||
"Offset of each planes in bytes using string format: 'o0,o1,o2,o3'",
|
||||
NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (gobject_class, PROP_FRAMESIZE,
|
||||
g_param_spec_uint ("framesize", "Framesize",
|
||||
"Size of an image in raw stream (0: default)", 0, G_MAXUINT, 0,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
gst_element_class_set_static_metadata (gstelement_class, "Video Parse",
|
||||
"Filter/Video",
|
||||
|
@ -163,6 +180,29 @@ gst_video_parse_set_property (GObject * object, guint prop_id,
|
|||
case PROP_TOP_FIELD_FIRST:
|
||||
vp->top_field_first = g_value_get_boolean (value);
|
||||
break;
|
||||
case PROP_STRIDES:
|
||||
if (gst_video_parse_deserialize_int_array (g_value_get_string (value),
|
||||
vp->stride, GST_VIDEO_MAX_PLANES)) {
|
||||
vp->stride_set = TRUE;
|
||||
} else {
|
||||
GST_WARNING_OBJECT (vp, "failed to deserialize given strides");
|
||||
vp->stride_set = FALSE;
|
||||
}
|
||||
|
||||
break;
|
||||
case PROP_OFFSETS:
|
||||
if (gst_video_parse_deserialize_int_array (g_value_get_string (value),
|
||||
vp->offset, GST_VIDEO_MAX_PLANES)) {
|
||||
vp->offset_set = TRUE;
|
||||
} else {
|
||||
GST_WARNING_OBJECT (vp, "failed to deserialized given offsets");
|
||||
vp->offset_set = FALSE;
|
||||
}
|
||||
|
||||
break;
|
||||
case PROP_FRAMESIZE:
|
||||
vp->framesize = g_value_get_uint (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
|
@ -203,18 +243,195 @@ gst_video_parse_get_property (GObject * object, guint prop_id, GValue * value,
|
|||
case PROP_TOP_FIELD_FIRST:
|
||||
g_value_set_boolean (value, vp->top_field_first);
|
||||
break;
|
||||
case PROP_STRIDES:
|
||||
{
|
||||
gchar *tmp;
|
||||
|
||||
tmp = g_strdup_printf ("%d,%d,%d,%d", vp->info.stride[0],
|
||||
vp->info.stride[1], vp->info.stride[2], vp->info.stride[3]);
|
||||
g_value_set_string (value, tmp);
|
||||
g_free (tmp);
|
||||
break;
|
||||
}
|
||||
case PROP_OFFSETS:
|
||||
{
|
||||
gchar *tmp;
|
||||
|
||||
tmp = g_strdup_printf ("%" G_GSIZE_FORMAT ",%" G_GSIZE_FORMAT
|
||||
",%" G_GSIZE_FORMAT ",%" G_GSIZE_FORMAT, vp->info.offset[0],
|
||||
vp->info.offset[1], vp->info.offset[2], vp->info.offset[3]);
|
||||
g_value_set_string (value, tmp);
|
||||
g_free (tmp);
|
||||
break;
|
||||
}
|
||||
case PROP_FRAMESIZE:
|
||||
g_value_set_uint (value, vp->info.size);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_video_parse_deserialize_int_array (const gchar * str, gint * dest,
|
||||
guint n_values)
|
||||
{
|
||||
gchar **strv;
|
||||
guint length;
|
||||
guint i;
|
||||
|
||||
strv = g_strsplit (str, ",", n_values);
|
||||
if (strv == NULL)
|
||||
return FALSE;
|
||||
|
||||
length = g_strv_length (strv);
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
gint64 val;
|
||||
|
||||
val = g_ascii_strtoll (strv[i], NULL, 10);
|
||||
if (val < G_MININT || val > G_MAXINT) {
|
||||
g_strfreev (strv);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
dest[i] = val;
|
||||
}
|
||||
|
||||
/* fill remaining values with 0 */
|
||||
for (i = length; i < n_values; i++)
|
||||
dest[i] = 0;
|
||||
|
||||
g_strfreev (strv);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static inline gsize
|
||||
gst_video_parse_get_plane_size (GstVideoInfo * info, guint plane)
|
||||
{
|
||||
gsize size = 0;
|
||||
|
||||
if (GST_VIDEO_FORMAT_INFO_IS_TILED (info->finfo)) {
|
||||
gint tile_width, tile_height, x_tiles, y_tiles;
|
||||
|
||||
tile_width = 1 << GST_VIDEO_FORMAT_INFO_TILE_WS (info->finfo);
|
||||
tile_height = 1 << GST_VIDEO_FORMAT_INFO_TILE_HS (info->finfo);
|
||||
x_tiles = GST_VIDEO_TILE_X_TILES (info->stride[plane]);
|
||||
y_tiles = GST_VIDEO_TILE_Y_TILES (info->stride[plane]);
|
||||
|
||||
/* plane size is the size of one tile multiplied by the number of tiles */
|
||||
size = tile_width * tile_height * x_tiles * y_tiles;
|
||||
} else {
|
||||
size = info->stride[plane] *
|
||||
GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info->finfo, plane, info->height);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
gst_video_parse_update_stride (GstVideoParse * vp)
|
||||
{
|
||||
GstVideoInfo *info = &vp->info;
|
||||
guint i;
|
||||
|
||||
/* 1. check that provided strides are greater than the default ones */
|
||||
for (i = 0; i < GST_VIDEO_INFO_N_PLANES (info); i++) {
|
||||
if (GST_VIDEO_FORMAT_INFO_IS_TILED (info->finfo)) {
|
||||
/* for tiled format, make sure there is more tiles than default */
|
||||
gint default_x_tiles, default_y_tiles, x_tiles, y_tiles;
|
||||
|
||||
x_tiles = GST_VIDEO_TILE_X_TILES (vp->stride[i]);
|
||||
y_tiles = GST_VIDEO_TILE_Y_TILES (vp->stride[i]);
|
||||
default_x_tiles = GST_VIDEO_TILE_X_TILES (info->stride[i]);
|
||||
default_y_tiles = GST_VIDEO_TILE_Y_TILES (info->stride[i]);
|
||||
|
||||
if (x_tiles < default_x_tiles) {
|
||||
GST_WARNING_OBJECT (vp,
|
||||
"x_tiles for plane %u is too small: got %d, min %d", i, x_tiles,
|
||||
default_x_tiles);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (y_tiles < default_y_tiles) {
|
||||
GST_WARNING_OBJECT (vp,
|
||||
"y_tiles for plane %u is too small: got %d, min %d", i, y_tiles,
|
||||
default_y_tiles);
|
||||
return FALSE;
|
||||
}
|
||||
} else {
|
||||
if (vp->stride[i] < info->stride[i]) {
|
||||
GST_WARNING_OBJECT (vp,
|
||||
"stride for plane %u is too small: got %d, min %d", i,
|
||||
vp->stride[i], info->stride[i]);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 2. update stride and plane offsets */
|
||||
for (i = 0; i < GST_VIDEO_INFO_N_PLANES (info); i++) {
|
||||
if (vp->stride[i] != info->stride[i]) {
|
||||
info->stride[i] = vp->stride[i];
|
||||
|
||||
if (i > 0) {
|
||||
/* update offset to reflect stride change for plane > 0 */
|
||||
info->offset[i] = info->offset[i - 1] +
|
||||
gst_video_parse_get_plane_size (info, i - 1);
|
||||
}
|
||||
|
||||
vp->need_videometa = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_video_parse_update_offset (GstVideoParse * vp)
|
||||
{
|
||||
GstVideoInfo *info = &vp->info;
|
||||
guint i;
|
||||
|
||||
/* 1. check that provided offsets are greaters than the default ones and are
|
||||
* consistent with plane size */
|
||||
for (i = 0; i < GST_VIDEO_INFO_N_PLANES (info); i++) {
|
||||
gsize min_offset = info->offset[i];
|
||||
|
||||
if (i > 0)
|
||||
min_offset = MAX (min_offset,
|
||||
vp->offset[i - 1] + gst_video_parse_get_plane_size (info, i - 1));
|
||||
|
||||
if (vp->offset[i] < min_offset) {
|
||||
GST_WARNING_OBJECT (vp,
|
||||
"offset for plane %u is too small: got %d, min %" G_GSIZE_FORMAT, i,
|
||||
vp->offset[i], min_offset);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* 2. update offsets */
|
||||
for (i = 0; i < GST_VIDEO_INFO_N_PLANES (info); i++) {
|
||||
if (vp->offset[i] != info->offset[i]) {
|
||||
info->offset[i] = vp->offset[i];
|
||||
vp->need_videometa = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_video_parse_update_info (GstVideoParse * vp)
|
||||
{
|
||||
GstVideoInfo *info = &vp->info;
|
||||
gint fps_n, fps_d;
|
||||
gint framesize;
|
||||
guint i;
|
||||
gboolean update_size;
|
||||
|
||||
gst_raw_parse_get_fps (GST_RAW_PARSE (vp), &fps_n, &fps_d);
|
||||
|
||||
|
@ -228,6 +445,52 @@ gst_video_parse_update_info (GstVideoParse * vp)
|
|||
GST_VIDEO_INTERLACE_MODE_INTERLEAVED :
|
||||
GST_VIDEO_INTERLACE_MODE_PROGRESSIVE;
|
||||
|
||||
vp->need_videometa = FALSE;
|
||||
|
||||
if (vp->stride_set) {
|
||||
if (gst_video_parse_update_stride (vp))
|
||||
update_size = TRUE;
|
||||
else
|
||||
GST_WARNING_OBJECT (vp, "invalid strides set, use default ones");
|
||||
}
|
||||
|
||||
if (vp->offset_set) {
|
||||
if (gst_video_parse_update_offset (vp))
|
||||
update_size = TRUE;
|
||||
else
|
||||
GST_WARNING_OBJECT (vp, "invalid offsets set, use default ones");
|
||||
}
|
||||
|
||||
if (update_size) {
|
||||
framesize = 0;
|
||||
|
||||
for (i = 0; i < GST_VIDEO_INFO_N_PLANES (info); i++)
|
||||
framesize += info->offset[i];
|
||||
|
||||
framesize += gst_video_parse_get_plane_size (info, i - 1);
|
||||
|
||||
info->size = framesize;
|
||||
}
|
||||
|
||||
if (vp->framesize) {
|
||||
/* user requires a specific framesize, just make sure it's bigger than
|
||||
* the current one */
|
||||
|
||||
if (vp->framesize > vp->info.size)
|
||||
vp->info.size = vp->framesize;
|
||||
else
|
||||
GST_WARNING_OBJECT (vp, "invalid framesize set: got %u, min: %"
|
||||
G_GSIZE_FORMAT, vp->framesize, vp->info.size);
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (vp, "video info: %ux%u, format %s, size %" G_GSIZE_FORMAT
|
||||
", stride {%d,%d,%d,%d}, offset {%" G_GSIZE_FORMAT ",%" G_GSIZE_FORMAT
|
||||
",%" G_GSIZE_FORMAT ",%" G_GSIZE_FORMAT "}",
|
||||
GST_VIDEO_INFO_WIDTH (info), GST_VIDEO_INFO_HEIGHT (info),
|
||||
GST_VIDEO_INFO_NAME (info), GST_VIDEO_INFO_SIZE (info),
|
||||
info->stride[0], info->stride[1], info->stride[2], info->stride[3],
|
||||
info->offset[0], info->offset[1], info->offset[2], info->offset[3]);
|
||||
|
||||
/* update base class framesize */
|
||||
framesize = GST_VIDEO_INFO_SIZE (info);
|
||||
gst_raw_parse_set_framesize (GST_RAW_PARSE (vp), framesize);
|
||||
|
@ -241,11 +504,58 @@ gst_video_parse_get_caps (GstRawParse * rp)
|
|||
return gst_video_info_to_caps (&vp->info);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_video_parse_copy_frame (GstVideoParse * vp, GstBuffer * dest,
|
||||
GstVideoInfo * dest_info, GstBuffer * src, GstVideoInfo * src_info)
|
||||
{
|
||||
GstVideoFrame src_frame;
|
||||
GstVideoFrame dest_frame;
|
||||
gboolean ret;
|
||||
|
||||
if (!gst_video_frame_map (&src_frame, src_info, src, GST_MAP_READ)) {
|
||||
GST_ERROR_OBJECT (vp, "failed to map src frame");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!gst_video_frame_map (&dest_frame, dest_info, dest, GST_MAP_WRITE)) {
|
||||
GST_ERROR_OBJECT (vp, "failed to map dest frame");
|
||||
gst_video_frame_unmap (&src_frame);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ret = gst_video_frame_copy (&dest_frame, &src_frame);
|
||||
|
||||
gst_video_frame_unmap (&src_frame);
|
||||
gst_video_frame_unmap (&dest_frame);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_video_parse_pre_push_buffer (GstRawParse * rp, GstBuffer * buffer)
|
||||
{
|
||||
GstVideoParse *vp = GST_VIDEO_PARSE (rp);
|
||||
|
||||
/* for now, we don't add video meta to buffer, so if we have non standard
|
||||
* stride and offset, just copy frame to new buffer */
|
||||
if (vp->need_videometa) {
|
||||
GstVideoInfo info;
|
||||
GstBuffer *outbuf;
|
||||
|
||||
gst_video_info_init (&info);
|
||||
gst_video_info_set_format (&info, vp->format, vp->width, vp->height);
|
||||
|
||||
GST_DEBUG_OBJECT (vp, "copying frame to remove padding");
|
||||
|
||||
outbuf = gst_buffer_new_allocate (NULL, GST_VIDEO_INFO_SIZE (&info), NULL);
|
||||
|
||||
if (!gst_video_parse_copy_frame (vp, outbuf, &info, buffer, &vp->info))
|
||||
GST_WARNING_OBJECT (vp, "failed to copy frame");
|
||||
|
||||
gst_buffer_replace_all_memory (buffer, gst_buffer_get_all_memory (outbuf));
|
||||
gst_buffer_unref (outbuf);
|
||||
}
|
||||
|
||||
if (vp->interlaced) {
|
||||
if (vp->top_field_first) {
|
||||
GST_BUFFER_FLAG_SET (buffer, GST_VIDEO_BUFFER_FLAG_TFF);
|
||||
|
|
|
@ -49,6 +49,9 @@ struct _GstVideoParse
|
|||
GstRawParse parent;
|
||||
|
||||
GstVideoInfo info;
|
||||
gboolean need_videometa;
|
||||
gboolean stride_set;
|
||||
gboolean offset_set;
|
||||
|
||||
/* properties */
|
||||
GstVideoFormat format;
|
||||
|
@ -57,6 +60,9 @@ struct _GstVideoParse
|
|||
gint par_n, par_d;
|
||||
gboolean interlaced;
|
||||
gboolean top_field_first;
|
||||
gint stride[GST_VIDEO_MAX_PLANES];
|
||||
gint offset[GST_VIDEO_MAX_PLANES];
|
||||
guint framesize;
|
||||
};
|
||||
|
||||
struct _GstVideoParseClass
|
||||
|
|
Loading…
Reference in a new issue