Search
SailfishOS Open Build Service
>
Projects
>
home:sledge
:
branches:nemo:devel:hw:ti:omap4:common
>
gst-plugins-base
> 0021-stride-more-flexible-stride-color-conversion.patch
Log In
Username
Password
Cancel
Overview
Repositories
Revisions
Requests
Users
Advanced
Attributes
Meta
File 0021-stride-more-flexible-stride-color-conversion.patch of Package gst-plugins-base
From ecf5f5b36d1d95920462a6fb2258280fbb2bef49 Mon Sep 17 00:00:00 2001 From: Rob Clark <rob@ti.com> Date: Mon, 13 Sep 2010 19:10:36 -0500 Subject: [PATCH 21/38] stride: more flexible stride/color conversion Refactor stride transform element to address a number of limitations: 1) support converting buffers from one rowstride to another, in addition to just handling conversion from strided <-> unstrided. 2) refactor convert code to make it easier to add new formats 3) refactor caps handling code to build template caps based upon color formats listed in convert (stride_conversions table). 4) refactor caps parsing/building to correctly handle RGB formats 5) add support for crop.. currently we optimize by just only copying the uncropped part of the frame, but this is the first step to true handling of cropping, so that we can crop out padding for the benefit of sink elements that don't understand crop or stride. (The convert code handles it fine.. the caps parsing/building in gststridetransform.c would need to handle caps re-negotiation when the crop changes for this to be complete.) --- gst/stride/armv7.s | 8 +- gst/stride/convert.c | 400 ++++++++++++++++++++------------------- gst/stride/gststridetransform.c | 375 +++++++++++++++++++++++++------------ gst/stride/gststridetransform.h | 25 +++- 4 files changed, 490 insertions(+), 318 deletions(-) diff --git a/gst/stride/armv7.s b/gst/stride/armv7.s index 2697a14..5f4200d 100644 --- a/gst/stride/armv7.s +++ b/gst/stride/armv7.s @@ -28,7 +28,7 @@ .global stride_copy_zip2 .type stride_copy_zip2, %function @void -@stride_copy_zip2 (guchar *new_buf, guchar *orig_buf1, guchar *orig_buf2, gint sz) +@stride_copy_zip2 (guchar * out, guchar * in1, guchar * in2, gint sz) @{ @@@@ note: r0-r3, q0-3, and q8-q15 do not need to be preserved stride_copy_zip2: @@ -74,8 +74,8 @@ stride_copy_zip2_3: .global stride_copy_zip3a .type stride_copy_zip3a, %function @void -@stride_copy_zip3a (guchar *new_buf, -@ guchar *orig_buf1, guchar *orig_buf2, guchar *orig_buf3, gint sz) +@stride_copy_zip3a (guchar * out, +@ guchar * in1, guchar * in2, guchar * in3, gint sz) @{ @@@@ note: r0-r3, q0-3, and q8-q15 do not need to be preserved stride_copy_zip3a: @@ -136,7 +136,7 @@ stride_copy_zip3a_3: .global stride_copy .type stride_copy, %function @void -@stride_copy (guchar *new_buf, guchar *orig_buf, gint sz) +@stride_copy (guchar *out, guchar *in, gint sz) @{ @@@@ note: r0-r3, q0-3, and q8-q15 do not need to be preserved stride_copy: diff --git a/gst/stride/convert.c b/gst/stride/convert.c index 17f9e2a..5d392ac 100644 --- a/gst/stride/convert.c +++ b/gst/stride/convert.c @@ -55,32 +55,31 @@ void stride_copy_zip3a (guchar * new_buf, guchar * orig_buf1, void stride_copy (guchar * new_buf, guchar * orig_buf, gint sz); WEAK void -stride_copy_zip2 (guchar * new_buf, guchar * orig_buf1, guchar * orig_buf2, - gint sz) +stride_copy_zip2 (guchar * out, guchar * in1, guchar * in2, gint sz) { while (sz--) { - *new_buf++ = *orig_buf1++; - *new_buf++ = *orig_buf2++; + *out++ = *in1++; + *out++ = *in2++; } } WEAK void -stride_copy_zip3a (guchar * new_buf, - guchar * orig_buf1, guchar * orig_buf2, guchar * orig_buf3, gint sz) +stride_copy_zip3a (guchar * out, + guchar * in1, guchar * in2, guchar * in3, gint sz) { while (sz > 1) { - *new_buf++ = *orig_buf1++; - *new_buf++ = *orig_buf2++; - *new_buf++ = *orig_buf1++; - *new_buf++ = *orig_buf3++; + *out++ = *in1++; + *out++ = *in2++; + *out++ = *in1++; + *out++ = *in3++; sz -= 2; } } WEAK void -stride_copy (guchar * new_buf, guchar * orig_buf, gint sz) +stride_copy (guchar * out, guchar * in, gint sz) { - memcpy (new_buf, orig_buf, sz); + memcpy (out, in, sz); } @@ -88,31 +87,19 @@ stride_copy (guchar * new_buf, guchar * orig_buf, gint sz) * move to strided buffer, interleaving two planes of identical dimensions */ static void -stridemove_zip2 (guchar * new_buf, guchar * orig_buf1, guchar * orig_buf2, - gint new_width, gint orig_width, gint height) +stridemove_zip2 (guchar * out, guchar * in1, guchar * in2, + gint out_bpl, gint in_bpl, gint width, gint height) { int row; GST_DEBUG - ("new_buf=%p, orig_buf1=%p, orig_buf2=%p, new_width=%d, orig_width=%d, height=%d", - new_buf, orig_buf1, orig_buf2, new_width, orig_width, height); - - /* if increasing the stride, work from bottom-up to avoid overwriting data - * that has not been moved yet.. otherwise, work in the opposite order, - * for the same reason. - */ - if (new_width > orig_width) { - for (row = height - 1; row >= 0; row--) { - stride_copy_zip2 (new_buf + (new_width * row), - orig_buf1 + (orig_width * row), - orig_buf2 + (orig_width * row), orig_width); - } - } else { - for (row = 0; row < height; row++) { - stride_copy_zip2 (new_buf + (new_width * row), - orig_buf1 + (orig_width * row), - orig_buf2 + (orig_width * row), new_width); - } + ("out=%p, in1=%p, in2=%p, out_bpl=%d, in_bpl=%d, width=%d, height=%d", + out, in1, in2, out_bpl, in_bpl, width, height); + + for (row = 0; row < height; row++) { + stride_copy_zip2 (out + (out_bpl * row), + in1 + (in_bpl * row), + in2 + (in_bpl * row), width); } } @@ -121,26 +108,28 @@ stridemove_zip2 (guchar * new_buf, guchar * orig_buf1, guchar * orig_buf2, * (orig_buf1) has 2x as many samples.. Ie. ABACABAC.. */ static void -stridemove_zip3a (guchar * new_buf, - guchar * orig_buf1, guchar * orig_buf2, guchar * orig_buf3, - guint new_width, gint orig_width, gint height) +stridemove_zip3a (guchar * out, + guchar * in1, guchar * in2, guchar * in3, + guint out_bpl, gint in_bpl, gint width, gint height) { - gint copy_width = (new_width < orig_width) ? new_width : orig_width; + GST_DEBUG + ("out=%p, in1=%p, in2=%p, in3=%p, out_bpl=%d, in_bpl=%d, width=%d, height=%d", + out, in1, in2, in3, out_bpl, in_bpl, width, height); while (height > 0) { /* even row */ - stride_copy_zip3a (new_buf, orig_buf1, orig_buf2, orig_buf3, copy_width); - new_buf += new_width; - orig_buf1 += orig_width; + stride_copy_zip3a (out, in1, in2, in3, width); + out += out_bpl; + in1 += in_bpl; /* odd row, recycles same U & V */ - stride_copy_zip3a (new_buf, orig_buf1, orig_buf2, orig_buf3, copy_width); - new_buf += new_width; - orig_buf1 += orig_width; + stride_copy_zip3a (out, in1, in2, in3, width); + out += out_bpl; + in1 += in_bpl; - orig_buf2 += orig_width / 2; - orig_buf3 += orig_width / 2; + in2 += in_bpl / 2; + in3 += in_bpl / 2; height -= 2; } @@ -154,28 +143,18 @@ stridemove_zip3a (guchar * new_buf, * enough. */ static void -stridemove (guchar * new_buf, guchar * orig_buf, gint new_width, - gint orig_width, gint height) +stridemove (guchar * out, guchar * in, gint out_bpl, gint in_bpl, + gint width, gint height) { int row; - GST_DEBUG ("new_buf=%p, orig_buf=%p, new_width=%d, orig_width=%d, height=%d", - new_buf, orig_buf, new_width, orig_width, height); - - /* if increasing the stride, work from bottom-up to avoid overwriting data - * that has not been moved yet.. otherwise, work in the opposite order, - * for the same reason. - */ - if (new_width > orig_width) { - for (row = height - 1; row >= 0; row--) { - stride_copy (new_buf + (new_width * row), orig_buf + (orig_width * row), - orig_width); - } - } else { - for (row = 0; row < height; row++) { - stride_copy (new_buf + (new_width * row), orig_buf + (orig_width * row), - new_width); - } + GST_DEBUG ("out=%p, in=%p, out_bpl=%d, in_bpl=%d, width=%d, height=%d", + out, in, out_bpl, in_bpl, width, height); + + for (row = 0; row < height; row++) { + stride_copy (out, in, width); + out += out_bpl; + in += in_bpl; } } @@ -183,195 +162,232 @@ stridemove (guchar * new_buf, guchar * orig_buf, gint new_width, * Conversion Functions: */ -/** convert 4:2:0 semiplanar to same 4:2:0 semiplanar */ -static GstFlowReturn -unstridify_420sp_420sp (GstStrideTransform * self, guchar * unstrided, - guchar * strided) +/** + * helper to calculate offsets/sizes that are re-used for each frame (until + * caps or crop changes) + * @isx: input sub-sampling in x direction + * @osx: output sub-sampling in x direction + * @isy: input sub-sampling in y direction + * @isx: input sub-sampling in y direction + */ +static inline gboolean refresh_cache(GstStrideTransform * self, + gint nplanes, gint bpp, gint * isx, gint * osx, gint * isy, gint * osy) { - gint width = self->width; - gint height = self->height; - gint stride = self->in_rowstride; + gint in_off, out_off; + int i; - g_return_val_if_fail (stride >= width, GST_FLOW_ERROR); + if (((self->crop_top + self->crop_height) > self->height) || + ((self->crop_left + self->crop_width) > self->width)) { + GST_ERROR_OBJECT (self, "invalid crop parameter"); + return GST_FLOW_ERROR; + } - stridemove (unstrided, strided, width, stride, - (GST_ROUND_UP_2 (height) * 3) / 2); + in_off = out_off = 0; - return GST_FLOW_OK; -} + for (i = 0; i < nplanes; i++) { + Cache * cache = &self->cache[i]; -static GstFlowReturn -stridify_420sp_420sp (GstStrideTransform * self, guchar * strided, - guchar * unstrided) -{ - gint width = self->width; - gint height = self->height; - gint stride = self->out_rowstride; + cache->in_bpl = self->in_rowstride ? + self->in_rowstride : bpp * self->width; - g_return_val_if_fail (stride >= width, GST_FLOW_ERROR); + cache->out_bpl = self->out_rowstride ? + self->out_rowstride : bpp * self->width; - g_return_val_if_fail (stride >= width, GST_FLOW_ERROR); - stridemove (strided, unstrided, stride, width, - (GST_ROUND_UP_2 (height) * 3) / 2); + if ((cache->in_bpl < (self->width * bpp)) || + (cache->out_bpl < (self->width * bpp))) { + GST_ERROR_OBJECT (self, "invalid stride parameter"); + return GST_FLOW_ERROR; + } - return GST_FLOW_OK; -} + cache->width = self->crop_width ? + self->crop_width : self->width; -/** convert 4:2:0 planar to same 4:2:0 planar */ -static GstFlowReturn -unstridify_420p_420p (GstStrideTransform * self, guchar * unstrided, - guchar * strided) -{ - gint width = self->width; - gint height = self->height; - gint stride = self->in_rowstride; + cache->height = self->crop_height ? + self->crop_height : self->height; - g_return_val_if_fail (stride >= width, GST_FLOW_ERROR); + if ((cache->width > self->width) || + (cache->height > self->height)) { + GST_ERROR_OBJECT (self, "invalid crop width/height parameter"); + return GST_FLOW_ERROR; + } - stridemove (unstrided, strided, width, stride, height); /* move Y */ - stridemove (unstrided + (height * width), strided + (height * stride), width / 2, stride, height); /* move V/U */ - /* XXX odd widths/heights/strides: */ - stridemove (unstrided + (int) (height * width * 1.5), strided + (int) (height * stride * 1.5), width / 2, stride, height); /* move U/V */ + /* note: everything above here is same for each plane, so in theory we + * could only calculate on first plane, and copy on subsequent planes + */ + + /* adjust for sub-sampling and bytes per pixel (bpp): */ + cache->in_bpl /= *isx; + cache->out_bpl /= *osx; + cache->width *= bpp; + cache->width /= *isx; + cache->height /= *isy; + + /* calculate offset to beginning of data to copy/transform: */ + cache->in_off = in_off; + cache->in_off += (bpp * self->crop_left / *isx) + + (cache->in_bpl * self->crop_top / *isy); + + cache->out_off = out_off; + cache->out_off += (bpp * self->crop_left / *osx) + + (cache->out_bpl * self->crop_top / *osy); + + in_off += (self->height / *isy) * cache->in_bpl; + out_off += (self->height / *osy) * cache->out_bpl; + + osx++; + isx++; + osy++; + isy++; + } return GST_FLOW_OK; } -static GstFlowReturn -stridify_420p_420p (GstStrideTransform * self, guchar * strided, - guchar * unstrided) +/** perform simple convert between buffers of same format */ +static inline GstFlowReturn convert_n_n (GstStrideTransform *self, + guchar * out, guchar * in, gint nplanes) { - gint width = self->width; - gint height = self->height; - gint stride = self->out_rowstride; - - g_return_val_if_fail (stride >= width, GST_FLOW_ERROR); + int i; - /* XXX odd widths/heights/strides: */ - stridemove (strided + (int) (height * stride * 1.5), unstrided + (int) (height * width * 1.5), stride, width / 2, height); /* move U/V */ - stridemove (strided + (height * stride), unstrided + (height * width), stride, width / 2, height); /* move V/U */ - stridemove (strided, unstrided, stride, width, height); /* move Y */ + for (i = 0; i < nplanes; i++) { + stridemove (out + self->cache[i].out_off, in + self->cache[i].in_off, + self->cache[i].out_bpl, self->cache[i].in_bpl, + self->cache[i].width, self->cache[i].height); + } return GST_FLOW_OK; } -/** convert 4:2:2 packed to same 4:2:2 packed */ +/** convert 4:2:0 semiplanar to same 4:2:0 semiplanar */ static GstFlowReturn -unstridify_422i_422i (GstStrideTransform * self, guchar * unstrided, - guchar * strided) +convert_420sp_420sp (GstStrideTransform * self, + guchar * out, guchar * in) { - gint width = self->width; - gint height = self->height; - gint stride = self->in_rowstride; - - g_return_val_if_fail (stride >= (width * 2), GST_FLOW_ERROR); - - stridemove (unstrided, strided, width * 2, stride, height); + if (G_UNLIKELY (self->needs_refresh)) { + gint sx[] = {1, 1}; + gint sy[] = {1, 2}; + if (refresh_cache (self, 2, 1, sx, sx, sy, sy)) + return GST_FLOW_ERROR; + self->needs_refresh = FALSE; + } - return GST_FLOW_OK; + return convert_n_n (self, out, in, 2); } +/** convert 4:2:0 planar to same 4:2:0 planar */ static GstFlowReturn -stridify_422i_422i (GstStrideTransform * self, guchar * strided, - guchar * unstrided) +convert_420p_420p (GstStrideTransform * self, + guchar * out, guchar * in) { - gint width = self->width; - gint height = self->height; - gint stride = self->out_rowstride; - - g_return_val_if_fail (stride >= (width * 2), GST_FLOW_ERROR); - - stridemove (strided, unstrided, stride, width * 2, height); + if (G_UNLIKELY (self->needs_refresh)) { + gint sx[] = {1, 2, 2}; + gint sy[] = {1, 2, 2}; + if (refresh_cache (self, 3, 1, sx, sx, sy, sy)) + return GST_FLOW_ERROR; + self->needs_refresh = FALSE; + } - return GST_FLOW_OK; + return convert_n_n (self, out, in, 3); } -/** convert I420 unstrided to NV12 strided */ +/** convert 4:2:2 packed to same 4:2:2 packed */ + static GstFlowReturn -stridify_i420_nv12 (GstStrideTransform * self, guchar * strided, - guchar * unstrided) +convert_422i_422i (GstStrideTransform * self, + guchar * out, guchar * in) { - gint width = self->width; - gint height = self->height; - gint stride = self->out_rowstride; - - g_return_val_if_fail (stride >= width, GST_FLOW_ERROR); - - /* XXX widths/heights/strides that are not multiple of four??: */ - stridemove_zip2 (strided + (height * stride), unstrided + (height * width), /* U */ - unstrided + (int) (height * width * 1.25), /* V */ - stride, width / 2, height / 2); - stridemove (strided, unstrided, stride, width, height); /* Y */ + if (G_UNLIKELY (self->needs_refresh)) { + gint sx[] = {1}; + gint sy[] = {1}; + if (refresh_cache (self, 1, 2, sx, sx, sy, sy)) + return GST_FLOW_ERROR; + self->needs_refresh = FALSE; + } - return GST_FLOW_OK; + return convert_n_n (self, out, in, 1); } -/** convert I420 unstrided to YUY2 strided */ +/** convert I420 unstrided to NV12 strided */ static GstFlowReturn -stridify_i420_yuy2 (GstStrideTransform * self, guchar * strided, - guchar * unstrided) +convert_i420_nv12 (GstStrideTransform * self, + guchar * out, guchar * in) { - gint width = self->width; - gint height = self->height; - gint stride = self->out_rowstride; + GstFlowReturn ret; + + if (G_UNLIKELY (self->needs_refresh)) { + gint isx[] = {1, 2, 2}; + gint osx[] = {1, 1, 1}; + gint sy[] = {1, 2, 2}; + if (refresh_cache (self, 3, 1, isx, osx, sy, sy)) + return GST_FLOW_ERROR; + self->needs_refresh = FALSE; + } - g_return_val_if_fail (stride >= width, GST_FLOW_ERROR); + ret = convert_n_n (self, out, in, 1); + if (ret != GST_FLOW_OK) + return ret; - /* XXX widths/heights/strides that are not multiple of four??: */ - stridemove_zip3a (strided, unstrided, /* Y */ - unstrided + (height * width), /* U */ - unstrided + (int) (height * width * 1.25), /* V */ - stride, width, height); + stridemove_zip2 (out + self->cache[1].out_off, + in + self->cache[1].in_off, /* U */ + in + self->cache[2].in_off, /* V */ + self->cache[2].out_bpl, + self->cache[1].in_bpl, + self->cache[1].width, + self->cache[1].height); return GST_FLOW_OK; } -/** convert RGB565 to RGB565 strided **/ +/** convert I420 unstrided to YUY2 strided */ static GstFlowReturn -stridify_rgb565_rgb565 (GstStrideTransform * self, guchar * strided, - guchar * unstrided) +convert_i420_yuy2 (GstStrideTransform * self, + guchar * out, guchar * in) { - gint width = self->width; - gint height = self->height; - gint stride = self->out_rowstride; - - g_return_val_if_fail (stride >= (width * 2), GST_FLOW_ERROR); + if (G_UNLIKELY (self->needs_refresh)) { + gint sx[] = {1, 2, 2}; + gint sy[] = {1, 2, 2}; + if (refresh_cache (self, 3, 1, sx, sx, sy, sy)) + return GST_FLOW_ERROR; + self->needs_refresh = FALSE; + } - stridemove (strided, unstrided, stride, width * 2, height); + stridemove_zip3a (out, + in + self->cache[0].in_off, /* Y */ + in + self->cache[1].in_off, /* U */ + in + self->cache[2].in_off, /* V */ + self->cache[0].out_bpl, + self->cache[0].in_bpl, + self->cache[0].width, + self->cache[0].height); return GST_FLOW_OK; } -/** convert RGB565 strided to RGB565 **/ +/** convert 16bpp rgb formats */ static GstFlowReturn -unstridify_rgb565_rgb565 (GstStrideTransform * self, guchar * strided, - guchar * unstrided) +convert_rgb16_rgb16 (GstStrideTransform * self, + guchar * out, guchar * in) { - gint width = self->width; - gint height = self->height; - gint stride = self->in_rowstride; - - g_return_val_if_fail (stride >= (width * 2), GST_FLOW_ERROR); - - stridemove (unstrided, strided, width * 2, stride, height); - return GST_FLOW_OK; + /* format is same 2-bytes per pixel */ + return convert_422i_422i (self, out, in); } -#define CONVERT(tofmt, fromfmt, stridify, unstridify) \ +#define CONVERT(tofmt, fromfmt, convert) \ { \ { GST_VIDEO_FORMAT_##tofmt, GST_VIDEO_FORMAT_##fromfmt }, \ - stridify, unstridify \ + convert \ } /* last entry has GST_VIDEO_FORMAT_UNKNOWN for in/out formats */ const Conversion stride_conversions[] = { - CONVERT (NV12, NV12, stridify_420sp_420sp, unstridify_420sp_420sp), - CONVERT (I420, I420, stridify_420p_420p, unstridify_420p_420p), - CONVERT (YV12, YV12, stridify_420p_420p, unstridify_420p_420p), - CONVERT (YUY2, YUY2, stridify_422i_422i, unstridify_422i_422i), - CONVERT (UYVY, UYVY, stridify_422i_422i, unstridify_422i_422i), - CONVERT (I420, NV12, stridify_i420_nv12, NULL), - CONVERT (I420, YUY2, stridify_i420_yuy2, NULL), - CONVERT (RGB16, RGB16, stridify_rgb565_rgb565, unstridify_rgb565_rgb565), + CONVERT (NV12, NV12, convert_420sp_420sp), + CONVERT (I420, I420, convert_420p_420p), + CONVERT (YV12, YV12, convert_420p_420p), + CONVERT (YUY2, YUY2, convert_422i_422i), + CONVERT (UYVY, UYVY, convert_422i_422i), + CONVERT (I420, NV12, convert_i420_nv12), + CONVERT (I420, YUY2, convert_i420_yuy2), + CONVERT (RGB16, RGB16, convert_rgb16_rgb16), /* add new entries before here */ {{GST_VIDEO_FORMAT_UNKNOWN}} }; diff --git a/gst/stride/gststridetransform.c b/gst/stride/gststridetransform.c index 4469e7f..7874ed4 100644 --- a/gst/stride/gststridetransform.c +++ b/gst/stride/gststridetransform.c @@ -57,27 +57,6 @@ /* last entry has GST_VIDEO_FORMAT_UNKNOWN for in/out formats */ extern const Conversion stride_conversions[]; -/* TODO: add rgb formats too! */ -#define YUV_SUPPORTED_CAPS \ - GST_VIDEO_CAPS_YUV_STRIDED ("{I420, YV12, YUY2, UYVY, NV12 }", "[ 0, max ]") - -#define RGB_SUPPORTED_CAPS \ - GST_VIDEO_CAPS_RGB_16_STRIDED ("[ 0, max ]") - - -static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS (YUV_SUPPORTED_CAPS ";" RGB_SUPPORTED_CAPS) - ); - -static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS (YUV_SUPPORTED_CAPS ";" RGB_SUPPORTED_CAPS) - ); - - GST_DEBUG_CATEGORY (stridetransform_debug); #define GST_CAT_DEFAULT stridetransform_debug @@ -85,6 +64,8 @@ GST_DEBUG_CATEGORY (stridetransform_debug); static void gst_stride_transform_dispose (GObject * obj); /* GstBaseTransform functions */ +static gboolean gst_stride_transform_event (GstBaseTransform * trans, + GstEvent * event); static gboolean gst_stride_transform_get_unit_size (GstBaseTransform * base, GstCaps * caps, guint * size); static gboolean gst_stride_transform_transform_size (GstBaseTransform * base, @@ -96,6 +77,7 @@ static gboolean gst_stride_transform_set_caps (GstBaseTransform * base, GstCaps * incaps, GstCaps * outcaps); static GstFlowReturn gst_stride_transform_transform (GstBaseTransform * base, GstBuffer * inbuf, GstBuffer * outbuf); +static GstCaps * get_all_templ_caps (GstPadDirection direction); GST_BOILERPLATE (GstStrideTransform, gst_stride_transform, GstVideoFilter, GST_TYPE_VIDEO_FILTER); @@ -115,9 +97,11 @@ gst_stride_transform_base_init (gpointer g_class) "Rob Clark <rob@ti.com>,"); gst_element_class_add_pad_template (gstelement_class, - gst_static_pad_template_get (&sink_template)); + gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + get_all_templ_caps (GST_PAD_SINK))); gst_element_class_add_pad_template (gstelement_class, - gst_static_pad_template_get (&src_template)); + gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, + get_all_templ_caps (GST_PAD_SRC))); } static void @@ -128,6 +112,8 @@ gst_stride_transform_class_init (GstStrideTransformClass * klass) gobject_class->dispose = gst_stride_transform_dispose; + basetransform_class->event = + GST_DEBUG_FUNCPTR (gst_stride_transform_event); basetransform_class->get_unit_size = GST_DEBUG_FUNCPTR (gst_stride_transform_get_unit_size); basetransform_class->transform_size = @@ -160,6 +146,35 @@ gst_stride_transform_dispose (GObject * object) G_OBJECT_CLASS (parent_class)->dispose (object); } +static gboolean +gst_stride_transform_event (GstBaseTransform * trans, GstEvent * event) +{ + GstStrideTransform *self = GST_STRIDE_TRANSFORM (trans); + + GST_DEBUG_OBJECT (self, "event %" GST_PTR_FORMAT, event); + + switch (GST_EVENT_TYPE (event)) { + /* if we get a crop, we don't change output size (yet, although it + * would be nice to be able to figure out if the sink supported + * cropping and if it does not perform the crop ourselves.. which + * would involve adjusting output caps appropriately). For now + * we just treat it as an optimization and avoid copying the data + * that will be later cropped out by the sink. + */ + case GST_EVENT_CROP: + gst_event_parse_crop (event, &self->crop_top, &self->crop_left, + &self->crop_width, &self->crop_height); + self->needs_refresh = TRUE; + GST_DEBUG_OBJECT (self, "cropping at %d,%d %dx%d", self->crop_top, + self->crop_left, self->crop_width, self->crop_height); + default: + break; + } + + /* forward all events */ + return TRUE; +} + /** * figure out the required buffer size based on @caps */ @@ -212,95 +227,205 @@ gst_stride_transform_transform_size (GstBaseTransform * base, return TRUE; } +static inline GstCaps * +get_templ_caps (GstVideoFormat fmt, gboolean strided) +{ + return gst_video_format_new_caps_simple (fmt, + strided ? -1 : 0, + "width", GST_TYPE_INT_RANGE, 1, G_MAXINT, + "height", GST_TYPE_INT_RANGE, 1, G_MAXINT, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, + NULL); +} + /** - * helper to check possible @fourcc conversions to the list @formats + * Utility to get all possible template caps for given direction */ -static void -add_all_fourcc_conversions (GValue * formats, guint32 fourcc, - GstPadDirection direction) +static GstCaps * +get_all_templ_caps (GstPadDirection direction) { + int i; gint to_format = (direction == GST_PAD_SINK) ? 1 : 0; - gint from_format = (direction == GST_PAD_SRC) ? 1 : 0; - GValue fourccval = { 0 }; - gint i; - GstVideoFormat format = gst_video_format_from_fourcc (fourcc); - - g_value_init (&fourccval, GST_TYPE_FOURCC); + GstCaps *templ = gst_caps_new_empty (); - for (i = 0; stride_conversions[i].format[0] != GST_VIDEO_FORMAT_UNKNOWN; i++) { - if (stride_conversions[i].format[from_format] == format) { - guint result_fourcc = - gst_video_format_to_fourcc (stride_conversions[i].format[to_format]); - gst_value_set_fourcc (&fourccval, result_fourcc); - gst_value_list_append_value (formats, &fourccval); - } + for (i = 0; stride_conversions[i].format[0]; i++) { + const Conversion *c = &stride_conversions[i]; + gst_caps_append (templ, get_templ_caps (c->format[to_format], TRUE)); + gst_caps_append (templ, get_templ_caps (c->format[to_format], FALSE)); } + + gst_caps_do_simplify (templ); + + GST_DEBUG ("template %s caps: %"GST_PTR_FORMAT, + (direction == GST_PAD_SINK) ? "sink" : "src", templ); + + return templ; } -/** - * helper to add all fields, other than rowstride to @caps, copied from @s. - */ -static void -add_all_fields (GstCaps * caps, const gchar * name, GstStructure * s, - gboolean rowstride, GstPadDirection direction) +static inline gboolean +is_filtered_field (const gchar *name) { - gint idx; - GstStructure *new_s = gst_structure_new (name, NULL); + static const gchar * filtered_fields[] = { + "rowstride", "format", "bpp", "depth", "endianness", + "red_mask", "green_mask", "blue_mask" + }; + gint i; + for (i = 0; i < G_N_ELEMENTS (filtered_fields); i++) + if (!strcmp (filtered_fields[i], name)) + return TRUE; + return FALSE; +} - if (rowstride) { - gst_structure_set (new_s, "rowstride", GST_TYPE_INT_RANGE, 1, G_MAXINT, - NULL); - } +static inline GstCaps * +get_caps (GstVideoFormat fmt, gboolean strided, GstStructure *s) +{ + gint idx; + GstCaps *ret = + gst_video_format_new_caps_simple (fmt, strided ? -1 : 0, NULL); idx = gst_structure_n_fields (s) - 1; while (idx >= 0) { const gchar *name = gst_structure_nth_field_name (s, idx); - const GValue *val = gst_structure_get_value (s, name); idx--; - /* for format field, check the stride_conversions table to see what - * we can support: + /* filter out certain format specific fields.. copy everything else + * from the original struct */ - if (!strcmp ("format", name)) { - GValue formats = { 0 }; - - g_value_init (&formats, GST_TYPE_LIST); + if (!is_filtered_field (name)) { + const GValue *val = gst_structure_get_value (s, name); + gst_caps_set_value (ret, name, val); + } + } - if (GST_VALUE_HOLDS_FOURCC (val)) { - add_all_fourcc_conversions (&formats, - gst_value_get_fourcc (val), direction); - } else if (GST_VALUE_HOLDS_LIST (val)) { - gint i; - for (i = 0; i < gst_value_list_get_size (val); i++) { - const GValue *list_val = gst_value_list_get_value (val, i); - if (GST_VALUE_HOLDS_FOURCC (list_val)) { - add_all_fourcc_conversions (&formats, - gst_value_get_fourcc (list_val), direction); - } else { - GST_WARNING ("malformed caps!!"); - break; - } - } - } else { - GST_WARNING ("malformed caps!!"); - } + return ret; +} - gst_structure_set_value (new_s, "format", &formats); +/** + * Utility to get all possible caps that can be converted to/from (depending + * on 'direction') the specified 'fmt'. The rest of the fields are populated + * from 's' + */ +static GstCaps * +get_all_caps (GstPadDirection direction, GstVideoFormat fmt, GstStructure *s) +{ + GstCaps *ret = gst_caps_new_empty (); + gint to_format = (direction == GST_PAD_SINK) ? 1 : 0; + gint from_format = (direction == GST_PAD_SRC) ? 1 : 0; + gint i; - continue; + for (i = 0; stride_conversions[i].format[0]; i++) { + const Conversion *c = &stride_conversions[i]; + if (c->format[from_format] == fmt) { + gst_caps_append (ret, get_caps (c->format[to_format], TRUE, s)); + gst_caps_append (ret, get_caps (c->format[to_format], FALSE, s)); } + } + + return ret; +} - /* copy over all other non-rowstride fields: */ - if (strcmp ("rowstride", name)) { - gst_structure_set_value (new_s, name, val); +/** convert GValue holding fourcc to GstVideoFormat (for YUV) */ +static inline GstVideoFormat +fmt_from_val (const GValue *val) +{ + return gst_video_format_from_fourcc (gst_value_get_fourcc (val)); +} + +/** convert structure to GstVideoFormat (for RGB) */ +static inline GstVideoFormat +fmt_from_struct (const GstStructure *s) +{ + /* hmm.. this is not supporting any case where ranges/lists are used + * for any of the rgb related fields in the caps. But I'm not quite + * sure a sane way to handle that.. rgb caps suck + */ + gint depth, bpp, endianness; + gint red_mask, green_mask, blue_mask, alpha_mask; + gboolean have_alpha, ok = TRUE; + + ok &= gst_structure_get_int (s, "depth", &depth); + ok &= gst_structure_get_int (s, "bpp", &bpp); + ok &= gst_structure_get_int (s, "endianness", &endianness); + ok &= gst_structure_get_int (s, "red_mask", &red_mask); + ok &= gst_structure_get_int (s, "green_mask", &green_mask); + ok &= gst_structure_get_int (s, "blue_mask", &blue_mask); + have_alpha = gst_structure_get_int (s, "alpha_mask", &alpha_mask); + + if (!ok) + return GST_VIDEO_FORMAT_UNKNOWN; + + if (depth == 24 && bpp == 32 && endianness == G_BIG_ENDIAN) { + if (red_mask == 0xff000000 && green_mask == 0x00ff0000 && + blue_mask == 0x0000ff00) { + return GST_VIDEO_FORMAT_RGBx; + } + if (red_mask == 0x0000ff00 && green_mask == 0x00ff0000 && + blue_mask == 0xff000000) { + return GST_VIDEO_FORMAT_BGRx; + } + if (red_mask == 0x00ff0000 && green_mask == 0x0000ff00 && + blue_mask == 0x000000ff) { + return GST_VIDEO_FORMAT_xRGB; + } + if (red_mask == 0x000000ff && green_mask == 0x0000ff00 && + blue_mask == 0x00ff0000) { + return GST_VIDEO_FORMAT_xBGR; + } + } else if (depth == 32 && bpp == 32 && endianness == G_BIG_ENDIAN && + have_alpha) { + if (red_mask == 0xff000000 && green_mask == 0x00ff0000 && + blue_mask == 0x0000ff00 && alpha_mask == 0x000000ff) { + return GST_VIDEO_FORMAT_RGBA; + } + if (red_mask == 0x0000ff00 && green_mask == 0x00ff0000 && + blue_mask == 0xff000000 && alpha_mask == 0x000000ff) { + return GST_VIDEO_FORMAT_BGRA; + } + if (red_mask == 0x00ff0000 && green_mask == 0x0000ff00 && + blue_mask == 0x000000ff && alpha_mask == 0xff000000) { + return GST_VIDEO_FORMAT_ARGB; + } + if (red_mask == 0x000000ff && green_mask == 0x0000ff00 && + blue_mask == 0x00ff0000 && alpha_mask == 0xff000000) { + return GST_VIDEO_FORMAT_ABGR; + } + } else if (depth == 24 && bpp == 24 && endianness == G_BIG_ENDIAN) { + if (red_mask == 0xff0000 && green_mask == 0x00ff00 && + blue_mask == 0x0000ff) { + return GST_VIDEO_FORMAT_RGB; + } + if (red_mask == 0x0000ff && green_mask == 0x00ff00 && + blue_mask == 0xff0000) { + return GST_VIDEO_FORMAT_BGR; + } + } else if ((depth == 15 || depth == 16) && bpp == 16 && + endianness == G_BYTE_ORDER) { + if (red_mask == GST_VIDEO_COMP1_MASK_16_INT + && green_mask == GST_VIDEO_COMP2_MASK_16_INT + && blue_mask == GST_VIDEO_COMP3_MASK_16_INT) { + return GST_VIDEO_FORMAT_RGB16; + } + if (red_mask == GST_VIDEO_COMP3_MASK_16_INT + && green_mask == GST_VIDEO_COMP2_MASK_16_INT + && blue_mask == GST_VIDEO_COMP1_MASK_16_INT) { + return GST_VIDEO_FORMAT_BGR16; + } + if (red_mask == GST_VIDEO_COMP1_MASK_15_INT + && green_mask == GST_VIDEO_COMP2_MASK_15_INT + && blue_mask == GST_VIDEO_COMP3_MASK_15_INT) { + return GST_VIDEO_FORMAT_RGB15; + } + if (red_mask == GST_VIDEO_COMP3_MASK_15_INT + && green_mask == GST_VIDEO_COMP2_MASK_15_INT + && blue_mask == GST_VIDEO_COMP1_MASK_15_INT) { + return GST_VIDEO_FORMAT_BGR15; } } - gst_caps_merge_structure (caps, new_s); + return GST_VIDEO_FORMAT_UNKNOWN; } - /** * we can transform @caps to strided or non-strided caps with otherwise * identical parameters @@ -310,31 +435,50 @@ gst_stride_transform_transform_caps (GstBaseTransform * base, GstPadDirection direction, GstCaps * caps) { GstStrideTransform *self = GST_STRIDE_TRANSFORM (base); - GstCaps *ret; - GstStructure *s; - - g_return_val_if_fail (GST_CAPS_IS_SIMPLE (caps), NULL); - - GST_DEBUG_OBJECT (self, "direction=%d, caps=%p", direction, caps); - LOG_CAPS (self, caps); - - ret = gst_caps_new_empty (); - s = gst_caps_get_structure (caps, 0); - - if (gst_structure_has_name (s, "video/x-raw-yuv") || - gst_structure_has_name (s, "video/x-raw-yuv-strided")) { - - add_all_fields (ret, "video/x-raw-yuv", s, FALSE, direction); - add_all_fields (ret, "video/x-raw-yuv-strided", s, TRUE, direction); - - } else if (gst_structure_has_name (s, "video/x-raw-rgb") || - gst_structure_has_name (s, "video/x-raw-rgb-strided")) { - - add_all_fields (ret, "video/x-raw-rgb", s, FALSE, direction); - add_all_fields (ret, "video/x-raw-rgb-strided", s, TRUE, direction); + GstCaps *ret = gst_caps_new_empty (); + int i; + + for (i = 0; i < gst_caps_get_size (caps); i++) { + GstStructure *s = gst_caps_get_structure (caps, i); + const char *name = gst_structure_get_name (s); + + /* this is a bit ugly.. ideally it would be easier to parse caps + * a bit more generically without having to care so much about + * difference between RGB and YUV.. but YUV can be specified as + * a list of format params, whereas RGB is a combination of many + * fields.. + */ + if (g_str_has_prefix (name, "video/x-raw-yuv")) { + const GValue *val = gst_structure_get_value (s, "format"); + if (GST_VALUE_HOLDS_FOURCC (val)) { + gst_caps_append (ret, + get_all_caps (direction, fmt_from_val (val), s)); + } else if (GST_VALUE_HOLDS_LIST (val)) { + gint j; + for (j = 0; j < gst_value_list_get_size (val); j++) { + const GValue *list_val = gst_value_list_get_value (val, j); + if (GST_VALUE_HOLDS_FOURCC (list_val)) { + gst_caps_append (ret, + get_all_caps (direction, fmt_from_val (list_val), s)); + } else { + GST_WARNING_OBJECT (self, + "malformed format in caps: %"GST_PTR_FORMAT, s); + break; + } + } + } else { + GST_WARNING_OBJECT (self, "malformed yuv caps: %"GST_PTR_FORMAT, s); + } + } else if (g_str_has_prefix (name, "video/x-raw-rgb")) { + gst_caps_append (ret, get_all_caps (direction, fmt_from_struct (s), s)); + } else { + GST_WARNING_OBJECT (self, "ignoring: %"GST_PTR_FORMAT, s); + } } + gst_caps_do_simplify (ret); + LOG_CAPS (self, ret); return ret; @@ -369,6 +513,7 @@ gst_stride_transform_set_caps (GstBaseTransform * base, (stride_conversions[i].format[1] == out_format)) { GST_DEBUG_OBJECT (self, "found stride_conversion: %d", i); self->conversion = &stride_conversions[i]; + self->needs_refresh = TRUE; break; } } @@ -378,10 +523,6 @@ gst_stride_transform_set_caps (GstBaseTransform * base, i, self->conversion, self->in_rowstride, self->out_rowstride); g_return_val_if_fail (self->conversion, FALSE); - g_return_val_if_fail (self->conversion->unstridify - || !self->in_rowstride, FALSE); - g_return_val_if_fail (self->conversion->stridify - || !self->out_rowstride, FALSE); g_return_val_if_fail (self->width == width, FALSE); g_return_val_if_fail (self->height == height, FALSE); @@ -399,20 +540,14 @@ gst_stride_transform_transform (GstBaseTransform * base, GST_DEBUG_OBJECT (self, "inbuf=%p (size=%d), outbuf=%p (size=%d)", inbuf, GST_BUFFER_SIZE (inbuf), outbuf, GST_BUFFER_SIZE (outbuf)); - if (self->in_rowstride && self->out_rowstride) { - GST_DEBUG_OBJECT (self, "not implemented"); // TODO - return GST_FLOW_ERROR; - } else if (self->in_rowstride) { - return self->conversion->unstridify (self, - GST_BUFFER_DATA (outbuf), GST_BUFFER_DATA (inbuf)); - } else if (self->out_rowstride) { - return self->conversion->stridify (self, + if (self->conversion) { + return self->conversion->convert (self, GST_BUFFER_DATA (outbuf), GST_BUFFER_DATA (inbuf)); } GST_DEBUG_OBJECT (self, - "this shouldn't happen! in_rowstride=%d, out_rowstride=%d", - self->in_rowstride, self->out_rowstride); + "this shouldn't happen! in_rowstride=%d, out_rowstride=%d, conversion=%p", + self->in_rowstride, self->out_rowstride, self->conversion); return GST_FLOW_ERROR; } diff --git a/gst/stride/gststridetransform.h b/gst/stride/gststridetransform.h index bce2526..34733cd 100644 --- a/gst/stride/gststridetransform.h +++ b/gst/stride/gststridetransform.h @@ -52,11 +52,18 @@ typedef struct { GstVideoFormat format[2]; /* in_format, out_format */ - GstFlowReturn (*stridify) (GstStrideTransform *self, guchar *strided, guchar *unstrided); - GstFlowReturn (*unstridify) (GstStrideTransform *self, guchar *unstrided, guchar *strided); + GstFlowReturn (*convert) (GstStrideTransform *self, guchar *out, guchar *in); } Conversion; +typedef struct { + gint in_bpl; /* bytes per line in input */ + gint out_bpl; /* bytes per line in output */ + gint in_off; + gint out_off; + gint width; + gint height; +} Cache; /** * GstStrideTransform: @@ -67,9 +74,23 @@ struct _GstStrideTransform { GstVideoFilter videofilter; /*< private >*/ + + /* values set from caps: */ gint width, height; gint in_rowstride; gint out_rowstride; + + /* values set from set from crop event: */ + gint crop_width, crop_height, crop_top, crop_left; + + /* cached values used for each conversion, indexed by plane in case of + * multi-planar formats. These won't have zero values meaning not-used + * (as long as !needs_refresh), but will be set to whatever byte width/ + * offset is appropriate for the format. + */ + Cache cache[3]; + gboolean needs_refresh; + const Conversion *conversion; /* for caching the tranform_size() results.. */ -- 1.7.5.4