From bf82cc10c516e299302dedbdc4b890549af127c3 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Fri, 27 Jan 2012 13:52:30 +0000 Subject: [PATCH 01/12] v4l: include the glib compatiblity header for the deprecated mutex API --- sys/v4l/gstv4lxoverlay.c | 1 + sys/v4l/v4l_calls.c | 1 + sys/v4l/v4lsrc_calls.c | 1 + 3 files changed, 3 insertions(+) diff --git a/sys/v4l/gstv4lxoverlay.c b/sys/v4l/gstv4lxoverlay.c index 5867cafb0a..c9a1c93bb6 100644 --- a/sys/v4l/gstv4lxoverlay.c +++ b/sys/v4l/gstv4lxoverlay.c @@ -32,6 +32,7 @@ #include #include +#include #include "gstv4lxoverlay.h" #include "gstv4lelement.h" #include "v4l_calls.h" diff --git a/sys/v4l/v4l_calls.c b/sys/v4l/v4l_calls.c index b2b7573363..3f8e16b117 100644 --- a/sys/v4l/v4l_calls.c +++ b/sys/v4l/v4l_calls.c @@ -36,6 +36,7 @@ #include #include #include +#include #include "v4l_calls.h" #include "gstv4ltuner.h" diff --git a/sys/v4l/v4lsrc_calls.c b/sys/v4l/v4lsrc_calls.c index 31bd6dee6e..5428deafc9 100644 --- a/sys/v4l/v4lsrc_calls.c +++ b/sys/v4l/v4lsrc_calls.c @@ -34,6 +34,7 @@ #include #include "v4lsrc_calls.h" #include +#include /* number of buffers to be queued *at least* before syncing */ #define MIN_BUFFERS_QUEUED 2 From 1251fb3fc0bd60eb4a38f47403e06c293c51fb16 Mon Sep 17 00:00:00 2001 From: George Kiagiadakis Date: Tue, 10 Jan 2012 21:02:48 +0200 Subject: [PATCH 02/12] video: Use host endianness when generating caps for 16-bit rgb formats This is necessary in order to match what the caps strings in video.h contain for 16-bit rgb formats and also to match how gst_video_format_parse_caps expects them. https://bugzilla.gnome.org/show_bug.cgi?id=667681 --- gst-libs/gst/video/video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst-libs/gst/video/video.c b/gst-libs/gst/video/video.c index f22ff4c12f..f68de1ce4e 100644 --- a/gst-libs/gst/video/video.c +++ b/gst-libs/gst/video/video.c @@ -673,7 +673,7 @@ gst_video_format_new_caps_raw (GstVideoFormat format) if (bpp != 8) { gst_caps_set_simple (caps, - "endianness", G_TYPE_INT, G_BIG_ENDIAN, + "endianness", G_TYPE_INT, bpp == 16 ? G_BYTE_ORDER : G_BIG_ENDIAN, "red_mask", G_TYPE_INT, red_mask, "green_mask", G_TYPE_INT, green_mask, "blue_mask", G_TYPE_INT, blue_mask, NULL); From 6f6f079dd93e113ad8f2e41ef56d462acbf3fea1 Mon Sep 17 00:00:00 2001 From: George Kiagiadakis Date: Tue, 10 Jan 2012 21:17:58 +0200 Subject: [PATCH 03/12] tests: test 16-bit rgb formats in test_parse_caps_rgb https://bugzilla.gnome.org/show_bug.cgi?id=667681 --- tests/check/libs/video.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/check/libs/video.c b/tests/check/libs/video.c index fd3178dcfc..ea32bd4f27 100644 --- a/tests/check/libs/video.c +++ b/tests/check/libs/video.c @@ -560,7 +560,13 @@ GST_START_TEST (test_parse_caps_rgb) GST_VIDEO_CAPS_RGBA, GST_VIDEO_FORMAT_RGBA}, { GST_VIDEO_CAPS_ARGB, GST_VIDEO_FORMAT_ARGB}, { GST_VIDEO_CAPS_BGRA, GST_VIDEO_FORMAT_BGRA}, { - GST_VIDEO_CAPS_ABGR, GST_VIDEO_FORMAT_ABGR} + GST_VIDEO_CAPS_ABGR, GST_VIDEO_FORMAT_ABGR}, + /* 16 bit */ + { + GST_VIDEO_CAPS_RGB_16, GST_VIDEO_FORMAT_RGB16}, { + GST_VIDEO_CAPS_BGR_16, GST_VIDEO_FORMAT_BGR16}, { + GST_VIDEO_CAPS_RGB_15, GST_VIDEO_FORMAT_RGB15}, { + GST_VIDEO_CAPS_BGR_15, GST_VIDEO_FORMAT_BGR15} }; gint i; From 809546c324b86fbfe37de8ce5543d03d2206a2f2 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Wed, 1 Feb 2012 16:32:24 +0000 Subject: [PATCH 04/12] vorbisparse: fix header detection It was matching non header packets. This fixes various leaks, where buffers would be pushed onto a headers list, but never popped. Might also fix corruption as those buffers were dropped from the output silently... https://bugzilla.gnome.org/show_bug.cgi?id=669167 --- ext/vorbis/gstvorbisparse.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ext/vorbis/gstvorbisparse.c b/ext/vorbis/gstvorbisparse.c index 4e905d8512..19b2b5fc2e 100644 --- a/ext/vorbis/gstvorbisparse.c +++ b/ext/vorbis/gstvorbisparse.c @@ -401,8 +401,13 @@ vorbis_parse_parse_packet (GstVorbisParse * parse, GstBuffer * buf) have_header = FALSE; if (size >= 1) { - if (data[0] >= 0x01 && data[0] <= 0x05) - have_header = TRUE; + switch (data[0]) { + case 1: + case 3: + case 5: + have_header = TRUE; + break; + } } if (have_header) { From b647c627e4ad54cec2c1058e1264ef4239665e6d Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Wed, 1 Feb 2012 16:46:13 +0000 Subject: [PATCH 05/12] vorbisparse: pedantically recognize undefined headers too --- ext/vorbis/gstvorbisparse.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/ext/vorbis/gstvorbisparse.c b/ext/vorbis/gstvorbisparse.c index 19b2b5fc2e..1ffa0f98b7 100644 --- a/ext/vorbis/gstvorbisparse.c +++ b/ext/vorbis/gstvorbisparse.c @@ -401,13 +401,8 @@ vorbis_parse_parse_packet (GstVorbisParse * parse, GstBuffer * buf) have_header = FALSE; if (size >= 1) { - switch (data[0]) { - case 1: - case 3: - case 5: - have_header = TRUE; - break; - } + if (data[0] & 1) + have_header = TRUE; } if (have_header) { From b4d6263f38e631f752830bde792c334ecec9ce41 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Wed, 1 Feb 2012 15:28:45 +0000 Subject: [PATCH 06/12] oggdemux: fix granpos interpolation violating max keyframe distance In case many packets fit on a page, we may not see a granpos for a while, and granpos interpolation can wrap the 'frames since last keyframe' part of the granpos, generating a granpos which is smaller than what it should be. This is fixed by detecting keyframe packets (at least for Theora), and updating the last keyframe granpos from this. This may still be generating potentially wrong granpos for streams which have a Theora like granpos (keyframes, a max keyframe distance and a count of frames since last keyframe), and which allow implicit granules on packets. For these streams, a custom keyframe detection routine should be plugged into their GstOggStream mapper. https://bugzilla.gnome.org/show_bug.cgi?id=669164 --- ext/ogg/gstoggdemux.c | 3 ++ ext/ogg/gstoggstream.c | 76 ++++++++++++++++++++++++++++++++++-------- ext/ogg/gstoggstream.h | 1 + 3 files changed, 67 insertions(+), 13 deletions(-) diff --git a/ext/ogg/gstoggdemux.c b/ext/ogg/gstoggdemux.c index 5baef83518..ac01b05962 100644 --- a/ext/ogg/gstoggdemux.c +++ b/ext/ogg/gstoggdemux.c @@ -581,6 +581,9 @@ gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet, pad->current_granule); } else if (ogg->segment.rate > 0.0 && pad->current_granule != -1) { pad->current_granule += duration; + if (gst_ogg_stream_packet_is_key_frame (&pad->map, packet)) { + pad->keyframe_granule = pad->current_granule; + } GST_DEBUG_OBJECT (ogg, "interpolating granule %" G_GUINT64_FORMAT, pad->current_granule); } diff --git a/ext/ogg/gstoggstream.c b/ext/ogg/gstoggstream.c index 0e9debc9ac..3b267faa13 100644 --- a/ext/ogg/gstoggstream.c +++ b/ext/ogg/gstoggstream.c @@ -47,9 +47,13 @@ typedef gint64 (*GstOggMapToGranuleposFunc) (GstOggStream * pad, gint64 granule, gint64 keyframe_granule); /* returns TRUE if the granulepos denotes a key frame */ -typedef gboolean (*GstOggMapIsKeyFrameFunc) (GstOggStream * pad, +typedef gboolean (*GstOggMapIsGranuleposKeyFrameFunc) (GstOggStream * pad, gint64 granulepos); +/* returns TRUE if the packet is a key frame */ +typedef gboolean (*GstOggMapIsPacketKeyFrameFunc) (GstOggStream * pad, + ogg_packet * packet); + /* returns TRUE if the given packet is a stream header packet */ typedef gboolean (*GstOggMapIsHeaderPacketFunc) (GstOggStream * pad, ogg_packet * packet); @@ -74,7 +78,8 @@ struct _GstOggMap GstOggMapSetupFunc setup_func; GstOggMapToGranuleFunc granulepos_to_granule_func; GstOggMapToGranuleposFunc granule_to_granulepos_func; - GstOggMapIsKeyFrameFunc is_key_frame_func; + GstOggMapIsGranuleposKeyFrameFunc is_granulepos_key_frame_func; + GstOggMapIsPacketKeyFrameFunc is_packet_key_frame_func; GstOggMapIsHeaderPacketFunc is_header_func; GstOggMapPacketDurationFunc packet_duration_func; GstOggMapGranuleposToKeyGranuleFunc granulepos_to_key_granule_func; @@ -189,13 +194,25 @@ gst_ogg_stream_granulepos_is_key_frame (GstOggStream * pad, gint64 granulepos) return FALSE; } - if (mappers[pad->map].is_key_frame_func == NULL) { + if (mappers[pad->map].is_granulepos_key_frame_func == NULL) { GST_WARNING ("Failed to determine keyframeness for %s granulepos", gst_ogg_stream_get_media_type (pad)); return FALSE; } - return mappers[pad->map].is_key_frame_func (pad, granulepos); + return mappers[pad->map].is_granulepos_key_frame_func (pad, granulepos); +} + +gboolean +gst_ogg_stream_packet_is_key_frame (GstOggStream * pad, ogg_packet * packet) +{ + if (mappers[pad->map].is_packet_key_frame_func == NULL) { + GST_WARNING ("Failed to determine keyframeness of %s packet", + gst_ogg_stream_get_media_type (pad)); + return FALSE; + } + + return mappers[pad->map].is_packet_key_frame_func (pad, packet); } gboolean @@ -250,7 +267,13 @@ gst_ogg_stream_get_media_type (GstOggStream * pad) /* some generic functions */ static gboolean -is_keyframe_true (GstOggStream * pad, gint64 granulepos) +is_granulepos_keyframe_true (GstOggStream * pad, gint64 granulepos) +{ + return TRUE; +} + +static gboolean +is_packet_keyframe_true (GstOggStream * pad, ogg_packet * packet) { return TRUE; } @@ -390,6 +413,7 @@ setup_theora_mapper (GstOggStream * pad, ogg_packet * packet) /* 2 bits + 3 bits = 5 bits KFGSHIFT */ pad->granuleshift = ((GST_READ_UINT8 (data + 40) & 0x03) << 3) + (GST_READ_UINT8 (data + 41) >> 5); + GST_LOG ("granshift: %d", pad->granuleshift); pad->is_video = TRUE; pad->n_header_packets = 3; @@ -446,7 +470,7 @@ granulepos_to_granule_theora (GstOggStream * pad, gint64 granulepos) } static gboolean -is_keyframe_theora (GstOggStream * pad, gint64 granulepos) +is_granulepos_keyframe_theora (GstOggStream * pad, gint64 granulepos) { gint64 frame_mask; @@ -458,6 +482,14 @@ is_keyframe_theora (GstOggStream * pad, gint64 granulepos) return ((granulepos & frame_mask) == 0); } +static gboolean +is_packet_keyframe_theora (GstOggStream * pad, ogg_packet * packet) +{ + if (packet->bytes == 0) + return FALSE; + return (packet->packet[0] & 0xc0) == 0x00; +} + static gboolean is_header_theora (GstOggStream * pad, ogg_packet * packet) { @@ -1957,7 +1989,8 @@ const GstOggMap mappers[] = { setup_theora_mapper, granulepos_to_granule_theora, granule_to_granulepos_default, - is_keyframe_theora, + is_granulepos_keyframe_theora, + is_packet_keyframe_theora, is_header_theora, packet_duration_constant, NULL, @@ -1969,7 +2002,8 @@ const GstOggMap mappers[] = { setup_vorbis_mapper, granulepos_to_granule_default, granule_to_granulepos_default, - is_keyframe_true, + is_granulepos_keyframe_true, + is_packet_keyframe_true, is_header_vorbis, packet_duration_vorbis, NULL, @@ -1981,7 +2015,8 @@ const GstOggMap mappers[] = { setup_speex_mapper, granulepos_to_granule_default, granule_to_granulepos_default, - is_keyframe_true, + is_granulepos_keyframe_true, + is_packet_keyframe_true, is_header_count, packet_duration_constant, NULL, @@ -1994,6 +2029,7 @@ const GstOggMap mappers[] = { NULL, NULL, NULL, + NULL, is_header_count, NULL, NULL, @@ -2006,6 +2042,7 @@ const GstOggMap mappers[] = { NULL, NULL, NULL, + NULL, is_header_count, NULL, NULL, @@ -2018,6 +2055,7 @@ const GstOggMap mappers[] = { granulepos_to_granule_default, granule_to_granulepos_default, NULL, + NULL, is_header_count, NULL, NULL, @@ -2030,6 +2068,7 @@ const GstOggMap mappers[] = { NULL, NULL, NULL, + NULL, is_header_true, NULL, NULL, @@ -2041,7 +2080,8 @@ const GstOggMap mappers[] = { setup_fLaC_mapper, granulepos_to_granule_default, granule_to_granulepos_default, - is_keyframe_true, + is_granulepos_keyframe_true, + is_packet_keyframe_true, is_header_fLaC, packet_duration_flac, NULL, @@ -2053,7 +2093,8 @@ const GstOggMap mappers[] = { setup_flac_mapper, granulepos_to_granule_default, granule_to_granulepos_default, - is_keyframe_true, + is_granulepos_keyframe_true, + is_packet_keyframe_true, is_header_flac, packet_duration_flac, NULL, @@ -2068,6 +2109,7 @@ const GstOggMap mappers[] = { NULL, NULL, NULL, + NULL, NULL }, { @@ -2077,6 +2119,7 @@ const GstOggMap mappers[] = { granulepos_to_granule_default, granule_to_granulepos_default, NULL, + NULL, is_header_count, packet_duration_constant, NULL, @@ -2089,6 +2132,7 @@ const GstOggMap mappers[] = { granulepos_to_granule_default, granule_to_granulepos_default, NULL, + NULL, is_header_count, packet_duration_kate, NULL, @@ -2101,6 +2145,7 @@ const GstOggMap mappers[] = { granulepos_to_granule_dirac, granule_to_granulepos_dirac, is_keyframe_dirac, + NULL, is_header_count, packet_duration_constant, granulepos_to_key_granule_dirac, @@ -2113,6 +2158,7 @@ const GstOggMap mappers[] = { granulepos_to_granule_vp8, granule_to_granulepos_vp8, is_keyframe_vp8, + NULL, is_header_vp8, packet_duration_vp8, granulepos_to_key_granule_vp8, @@ -2125,6 +2171,7 @@ const GstOggMap mappers[] = { granulepos_to_granule_default, granule_to_granulepos_default, NULL, + NULL, is_header_opus, packet_duration_opus, NULL, @@ -2136,7 +2183,8 @@ const GstOggMap mappers[] = { setup_ogmaudio_mapper, granulepos_to_granule_default, granule_to_granulepos_default, - is_keyframe_true, + is_granulepos_keyframe_true, + is_packet_keyframe_true, is_header_ogm, packet_duration_ogm, NULL, @@ -2149,6 +2197,7 @@ const GstOggMap mappers[] = { granulepos_to_granule_default, granule_to_granulepos_default, NULL, + NULL, is_header_ogm, packet_duration_constant, NULL, @@ -2160,7 +2209,8 @@ const GstOggMap mappers[] = { setup_ogmtext_mapper, granulepos_to_granule_default, granule_to_granulepos_default, - is_keyframe_true, + is_granulepos_keyframe_true, + is_packet_keyframe_true, is_header_ogm, packet_duration_ogm, NULL, diff --git a/ext/ogg/gstoggstream.h b/ext/ogg/gstoggstream.h index a66a78c34d..c06bc51d6d 100644 --- a/ext/ogg/gstoggstream.h +++ b/ext/ogg/gstoggstream.h @@ -124,6 +124,7 @@ GstClockTime gst_ogg_stream_get_packet_start_time (GstOggStream *pad, gboolean gst_ogg_stream_granulepos_is_key_frame (GstOggStream *pad, gint64 granulepos); gboolean gst_ogg_stream_packet_is_header (GstOggStream *pad, ogg_packet *packet); +gboolean gst_ogg_stream_packet_is_key_frame (GstOggStream *pad, ogg_packet *packet); gint64 gst_ogg_stream_get_packet_duration (GstOggStream * pad, ogg_packet *packet); void gst_ogg_stream_extract_tags (GstOggStream * pad, ogg_packet * packet); const char *gst_ogg_stream_get_media_type (GstOggStream * pad); From e21501043b9e89e776b3443ef3c185451cd50861 Mon Sep 17 00:00:00 2001 From: Ryan Lortie Date: Sat, 7 Jan 2012 23:09:23 -0500 Subject: [PATCH 07/12] autogen.sh: allow calling from out-of-tree https://bugzilla.gnome.org/show_bug.cgi?id=667665 --- autogen.sh | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/autogen.sh b/autogen.sh index 820a017fbb..4a4a8f40c4 100755 --- a/autogen.sh +++ b/autogen.sh @@ -1,6 +1,12 @@ #!/bin/sh # Run this to generate all the initial makefiles, etc. +test -n "$srcdir" || srcdir=`dirname "$0"` +test -n "$srcdir" || srcdir=. + +olddir=`pwd` +cd "$srcdir" + DIE=0 package=gst-plugins-base srcfile=gst/audiotestsrc/gstaudiotestsrc.c @@ -106,13 +112,15 @@ test -n "$NOCONFIGURE" && { exit 0 } +cd "$olddir" + echo "+ running configure ... " -test ! -z "$CONFIGURE_DEF_OPT" && echo " ./configure default flags: $CONFIGURE_DEF_OPT" -test ! -z "$CONFIGURE_EXT_OPT" && echo " ./configure external flags: $CONFIGURE_EXT_OPT" -test ! -z "$CONFIGURE_FILE_OPT" && echo " ./configure enable/disable flags: $CONFIGURE_FILE_OPT" +test ! -z "$CONFIGURE_DEF_OPT" && echo " $srcdir/configure default flags: $CONFIGURE_DEF_OPT" +test ! -z "$CONFIGURE_EXT_OPT" && echo " $srcdir/configure external flags: $CONFIGURE_EXT_OPT" +test ! -z "$CONFIGURE_FILE_OPT" && echo " $srcdir/configure enable/disable flags: $CONFIGURE_FILE_OPT" echo -./configure $CONFIGURE_DEF_OPT $CONFIGURE_EXT_OPT $CONFIGURE_FILE_OPT || { +"$srcdir/configure" $CONFIGURE_DEF_OPT $CONFIGURE_EXT_OPT $CONFIGURE_FILE_OPT || { echo " configure failed" exit 1 } From 19141759c16866d42dfcc2cf2df345bab16e1108 Mon Sep 17 00:00:00 2001 From: David Schleef Date: Tue, 30 Aug 2011 19:02:51 -0700 Subject: [PATCH 08/12] theoraenc: Use GAP flag when possible Set TH_ENCCTL_SET_DUPLICATE_FLAG when we see a gap flag, to indicate to the encoder that the current frame is a duplicate of the previous frame. --- ext/theora/gsttheoraenc.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ext/theora/gsttheoraenc.c b/ext/theora/gsttheoraenc.c index 5d220625f1..de2fcfeab6 100644 --- a/ext/theora/gsttheoraenc.c +++ b/ext/theora/gsttheoraenc.c @@ -1376,6 +1376,11 @@ theora_enc_encode_and_push (GstTheoraEnc * enc, ogg_packet op, goto multipass_read_failed; } } +#ifdef TH_ENCCTL_SET_DUPLICATE_FLAG + if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_GAP)) { + th_encode_ctl (enc->encoder, TH_ENCCTL_SET_DUPLICATE_FLAG, NULL, 0); + } +#endif res = th_encode_ycbcr_in (enc->encoder, ycbcr); /* none of the failure cases can happen here */ From e4f01106d08f3c2a65897bfe919bd65ce55545a8 Mon Sep 17 00:00:00 2001 From: David Schleef Date: Wed, 25 Jan 2012 15:49:00 -0800 Subject: [PATCH 09/12] videoscale: Add AYUV64 path to Lanczos --- gst/videoscale/gstvideoscale.c | 5 ++ gst/videoscale/vs_image.h | 3 + gst/videoscale/vs_lanczos.c | 111 ++++++++++++++++++++++++++++++--- 3 files changed, 109 insertions(+), 10 deletions(-) diff --git a/gst/videoscale/gstvideoscale.c b/gst/videoscale/gstvideoscale.c index 7b44647e37..6df4ba6e70 100644 --- a/gst/videoscale/gstvideoscale.c +++ b/gst/videoscale/gstvideoscale.c @@ -1199,6 +1199,11 @@ gst_video_scale_transform (GstBaseTransform * trans, GstBuffer * in, case GST_VIDEO_SCALE_4TAP: vs_image_scale_4tap_AYUV64 (&dest, &src, videoscale->tmp_buf); break; + case GST_VIDEO_SCALE_LANCZOS: + vs_image_scale_lanczos_AYUV64 (&dest, &src, videoscale->tmp_buf, + videoscale->sharpness, videoscale->dither, videoscale->submethod, + videoscale->envelope, videoscale->sharpen); + break; default: goto unknown_mode; } diff --git a/gst/videoscale/vs_image.h b/gst/videoscale/vs_image.h index 2312acc7d5..7b3499152a 100644 --- a/gst/videoscale/vs_image.h +++ b/gst/videoscale/vs_image.h @@ -52,6 +52,9 @@ void vs_image_scale_linear_RGBA (const VSImage *dest, const VSImage *src, void vs_image_scale_lanczos_AYUV (const VSImage * dest, const VSImage * src, uint8_t * tmpbuf, double sharpness, gboolean dither, int submethod, double a, double sharpen); +void vs_image_scale_lanczos_AYUV64 (const VSImage * dest, const VSImage * src, + uint8_t * tmpbuf, double sharpness, gboolean dither, int submethod, + double a, double sharpen); void vs_image_scale_nearest_RGB (const VSImage *dest, const VSImage *src, uint8_t *tmpbuf); diff --git a/gst/videoscale/vs_lanczos.c b/gst/videoscale/vs_lanczos.c index 1c87ba3dd3..d141a01c78 100644 --- a/gst/videoscale/vs_lanczos.c +++ b/gst/videoscale/vs_lanczos.c @@ -204,6 +204,9 @@ static void vs_image_scale_lanczos_AYUV_float (const VSImage * dest, static void vs_image_scale_lanczos_AYUV_double (const VSImage * dest, const VSImage * src, uint8_t * tmpbuf, double sharpness, gboolean dither, double a, double sharpen); +static void vs_image_scale_lanczos_AYUV64_double (const VSImage * dest, + const VSImage * src, uint8_t * tmpbuf, double sharpness, gboolean dither, + double a, double sharpen); static double sinc (double x) @@ -590,6 +593,15 @@ vs_image_scale_lanczos_AYUV (const VSImage * dest, const VSImage * src, } } +void +vs_image_scale_lanczos_AYUV64 (const VSImage * dest, const VSImage * src, + uint8_t * tmpbuf, double sharpness, gboolean dither, int submethod, + double a, double sharpen) +{ + vs_image_scale_lanczos_AYUV64_double (dest, src, tmpbuf, sharpness, dither, + a, sharpen); +} + #define RESAMPLE_HORIZ_FLOAT(function, dest_type, tap_type, src_type, _n_taps) \ @@ -813,9 +825,9 @@ RESAMPLE_VERT_DITHER (resample_vert_dither_int16_generic, gint16, gint16, n_taps, shift) /* *INDENT-ON* */ -#define RESAMPLE_VERT_FLOAT(function, tap_type, src_type, _n_taps, _shift) \ +#define RESAMPLE_VERT_FLOAT(function, dest_type, clamp, tap_type, src_type, _n_taps, _shift) \ static void \ -function (guint8 *dest, \ +function (dest_type *dest, \ const tap_type *taps, const src_type *src, int stride, int n_taps, \ int shift, int n) \ { \ @@ -828,13 +840,13 @@ function (guint8 *dest, \ const src_type *line = PTR_OFFSET(src, stride * l); \ sum_y += line[i] * taps[l]; \ } \ - dest[i] = CLAMP (floor(0.5 + sum_y), 0, 255); \ + dest[i] = CLAMP (floor(0.5 + sum_y), 0, clamp); \ } \ } -#define RESAMPLE_VERT_FLOAT_DITHER(function, tap_type, src_type, _n_taps, _shift) \ +#define RESAMPLE_VERT_FLOAT_DITHER(function, dest_type, clamp, tap_type, src_type, _n_taps, _shift) \ static void \ -function (guint8 *dest, \ +function (dest_type *dest, \ const tap_type *taps, const src_type *src, int stride, int n_taps, \ int shift, int n) \ { \ @@ -849,19 +861,24 @@ function (guint8 *dest, \ sum_y += line[i] * taps[l]; \ } \ err_y += sum_y; \ - dest[i] = CLAMP (floor (err_y), 0, 255); \ + dest[i] = CLAMP (floor (err_y), 0, clamp); \ err_y -= floor (err_y); \ } \ } /* *INDENT-OFF* */ -RESAMPLE_VERT_FLOAT (resample_vert_double_generic, double, double, n_taps, +RESAMPLE_VERT_FLOAT (resample_vert_double_generic, guint8, 255, double, double, n_taps, shift) -RESAMPLE_VERT_FLOAT_DITHER (resample_vert_dither_double_generic, double, double, +RESAMPLE_VERT_FLOAT_DITHER (resample_vert_dither_double_generic, guint8, 255, double, double, n_taps, shift) -RESAMPLE_VERT_FLOAT (resample_vert_float_generic, float, float, n_taps, shift) -RESAMPLE_VERT_FLOAT_DITHER (resample_vert_dither_float_generic, float, float, +RESAMPLE_VERT_FLOAT (resample_vert_double_generic_u16, guint16, 65535, double, double, n_taps, + shift) +RESAMPLE_VERT_FLOAT_DITHER (resample_vert_dither_double_generic_u16, guint16, 65535, double, double, + n_taps, shift) + +RESAMPLE_VERT_FLOAT (resample_vert_float_generic, guint8, 255, float, float, n_taps, shift) +RESAMPLE_VERT_FLOAT_DITHER (resample_vert_dither_float_generic, guint8, 255, float, float, n_taps, shift) /* *INDENT-ON* */ @@ -1556,3 +1573,77 @@ vs_image_scale_lanczos_AYUV_float (const VSImage * dest, const VSImage * src, scale1d_cleanup (&scale->y_scale1d); g_free (scale->tmpdata); } + +static void +vs_scale_lanczos_AYUV64_double (Scale * scale) +{ + int j; + int yi; + int tmp_yi; + + tmp_yi = 0; + + for (j = 0; j < scale->dest->height; j++) { + guint16 *destline; + double *taps; + + destline = (guint16 *) (scale->dest->pixels + scale->dest->stride * j); + + yi = scale->y_scale1d.offsets[j]; + + while (tmp_yi < yi + scale->y_scale1d.n_taps) { + scale->horiz_resample_func (TMP_LINE_DOUBLE_AYUV (tmp_yi), + scale->x_scale1d.offsets, scale->x_scale1d.taps, SRC_LINE (tmp_yi), + scale->x_scale1d.n_taps, 0, scale->dest->width); + tmp_yi++; + } + + taps = (double *) scale->y_scale1d.taps + j * scale->y_scale1d.n_taps; + if (scale->dither) { + resample_vert_dither_double_generic_u16 (destline, + taps, TMP_LINE_DOUBLE_AYUV (scale->y_scale1d.offsets[j]), + sizeof (double) * 4 * scale->dest->width, + scale->y_scale1d.n_taps, 0, scale->dest->width * 4); + } else { + resample_vert_double_generic_u16 (destline, + taps, TMP_LINE_DOUBLE_AYUV (scale->y_scale1d.offsets[j]), + sizeof (double) * 4 * scale->dest->width, + scale->y_scale1d.n_taps, 0, scale->dest->width * 4); + } + } +} + +void +vs_image_scale_lanczos_AYUV64_double (const VSImage * dest, const VSImage * src, + uint8_t * tmpbuf, double sharpness, gboolean dither, double a, + double sharpen) +{ + Scale s = { 0 }; + Scale *scale = &s; + int n_taps; + + scale->dest = dest; + scale->src = src; + + n_taps = scale1d_get_n_taps (src->width, dest->width, a, sharpness); + scale1d_calculate_taps (&scale->x_scale1d, + src->width, dest->width, n_taps, a, sharpness, sharpen); + + n_taps = scale1d_get_n_taps (src->height, dest->height, a, sharpness); + scale1d_calculate_taps (&scale->y_scale1d, + src->height, dest->height, n_taps, a, sharpness, sharpen); + + scale->dither = dither; + + scale->horiz_resample_func = + (HorizResampleFunc) resample_horiz_double_ayuv_generic; + + scale->tmpdata = + g_malloc (sizeof (double) * scale->dest->width * scale->src->height * 4); + + vs_scale_lanczos_AYUV64_double (scale); + + scale1d_cleanup (&scale->x_scale1d); + scale1d_cleanup (&scale->y_scale1d); + g_free (scale->tmpdata); +} From beacccc3969b82ea9c9a8808c7d353010c082d0b Mon Sep 17 00:00:00 2001 From: David Schleef Date: Mon, 30 Jan 2012 08:21:54 -0800 Subject: [PATCH 10/12] videoscale: Add nearest/linear scaling for NV12 --- gst/videoscale/gstvideoscale.c | 31 +++++++- gst/videoscale/vs_image.c | 132 ++++++++++++++++++++++++++++++++- gst/videoscale/vs_image.h | 5 ++ gst/videoscale/vs_scanline.c | 82 ++++++++++++++++++++ gst/videoscale/vs_scanline.h | 5 ++ 5 files changed, 251 insertions(+), 4 deletions(-) diff --git a/gst/videoscale/gstvideoscale.c b/gst/videoscale/gstvideoscale.c index 6df4ba6e70..9f072a3458 100644 --- a/gst/videoscale/gstvideoscale.c +++ b/gst/videoscale/gstvideoscale.c @@ -1,6 +1,6 @@ /* GStreamer * Copyright (C) <1999> Erik Walthinsen - * Copyright (C) 2005 David Schleef + * Copyright (C) 2005-2012 David Schleef * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -140,7 +140,8 @@ static GstStaticCaps gst_video_scale_format_caps[] = { GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("Y8 ")), GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("GREY")), GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AY64")), - GST_STATIC_CAPS (GST_VIDEO_CAPS_ARGB_64) + GST_STATIC_CAPS (GST_VIDEO_CAPS_ARGB_64), + GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("NV12")) }; #define GST_TYPE_VIDEO_SCALE_METHOD (gst_video_scale_method_get_type()) @@ -1024,7 +1025,8 @@ gst_video_scale_setup_vs_image (VSImage * image, GstVideoFormat format, if (format == GST_VIDEO_FORMAT_I420 || format == GST_VIDEO_FORMAT_YV12 || format == GST_VIDEO_FORMAT_Y444 - || format == GST_VIDEO_FORMAT_Y42B || format == GST_VIDEO_FORMAT_Y41B) { + || format == GST_VIDEO_FORMAT_Y42B || format == GST_VIDEO_FORMAT_Y41B + || format == GST_VIDEO_FORMAT_NV12) { image->real_pixels = data + gst_video_format_get_component_offset (format, component, width, height); } else { @@ -1090,6 +1092,7 @@ _get_black_for_format (GstVideoFormat format) case GST_VIDEO_FORMAT_Y444: case GST_VIDEO_FORMAT_Y42B: case GST_VIDEO_FORMAT_Y41B: + case GST_VIDEO_FORMAT_NV12: return black[4]; /* Y, U, V, 0 */ case GST_VIDEO_FORMAT_RGB16: case GST_VIDEO_FORMAT_RGB15: @@ -1153,6 +1156,14 @@ gst_video_scale_transform (GstBaseTransform * trans, GstBuffer * in, videoscale->to_width, videoscale->to_height, videoscale->borders_w, videoscale->borders_h, GST_BUFFER_DATA (out)); } + if (videoscale->format == GST_VIDEO_FORMAT_NV12) { + gst_video_scale_setup_vs_image (&src_u, videoscale->format, 1, + videoscale->from_width, videoscale->from_height, 0, 0, + GST_BUFFER_DATA (in)); + gst_video_scale_setup_vs_image (&dest_u, videoscale->format, 1, + videoscale->to_width, videoscale->to_height, videoscale->borders_w, + videoscale->borders_h, GST_BUFFER_DATA (out)); + } switch (videoscale->format) { case GST_VIDEO_FORMAT_RGBx: @@ -1340,6 +1351,20 @@ gst_video_scale_transform (GstBaseTransform * trans, GstBuffer * in, goto unknown_mode; } break; + case GST_VIDEO_FORMAT_NV12: + switch (method) { + case GST_VIDEO_SCALE_NEAREST: + vs_image_scale_nearest_Y (&dest, &src, videoscale->tmp_buf); + vs_image_scale_nearest_NV12 (&dest_u, &src_u, videoscale->tmp_buf); + break; + case GST_VIDEO_SCALE_BILINEAR: + vs_image_scale_linear_Y (&dest, &src, videoscale->tmp_buf); + vs_image_scale_linear_NV12 (&dest_u, &src_u, videoscale->tmp_buf); + break; + default: + goto unknown_mode; + } + break; case GST_VIDEO_FORMAT_RGB16: if (add_borders) vs_fill_borders_RGB565 (&dest, black); diff --git a/gst/videoscale/vs_image.c b/gst/videoscale/vs_image.c index c28b0d8ff3..09ccf2580c 100644 --- a/gst/videoscale/vs_image.c +++ b/gst/videoscale/vs_image.c @@ -1,6 +1,6 @@ /* * Image Scaling Functions - * Copyright (c) 2005 David A. Schleef + * Copyright (c) 2005-2012 David A. Schleef * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -522,6 +522,136 @@ vs_image_scale_linear_UYVY (const VSImage * dest, const VSImage * src, } } +/* NV12 */ + +void +vs_image_scale_nearest_NV12 (const VSImage * dest, const VSImage * src, + uint8_t * tmpbuf) +{ + int acc; + int y_increment; + int x_increment; + int i; + int j; + int xacc; + + if (dest->height == 1) + y_increment = 0; + else + y_increment = ((src->height - 1) << 16) / (dest->height - 1); + + if (dest->width == 1) + x_increment = 0; + else + x_increment = ((src->width - 1) << 16) / (dest->width - 1); + + acc = 0; + for (i = 0; i < dest->height; i++) { + j = acc >> 16; + + xacc = 0; + vs_scanline_resample_nearest_NV12 (dest->pixels + i * dest->stride, + src->pixels + j * src->stride, src->width, dest->width, &xacc, + x_increment); + + acc += y_increment; + } +} + +void +vs_image_scale_linear_NV12 (const VSImage * dest, const VSImage * src, + uint8_t * tmpbuf) +{ + int acc; + int y_increment; + int x_increment; + uint8_t *tmp1; + uint8_t *tmp2; + int y1; + int y2; + int i; + int j; + int x; + int dest_size; + int xacc; + + if (dest->height == 1) + y_increment = 0; + else + y_increment = ((src->height - 1) << 16) / (dest->height - 1) - 1; + + if (dest->width == 1) + x_increment = 0; + else + x_increment = ((src->width - 1) << 16) / (dest->width - 1) - 1; + + dest_size = ROUND_UP_4 (dest->width * 2); + + tmp1 = tmpbuf; + tmp2 = tmpbuf + dest_size; + + acc = 0; + xacc = 0; + y2 = -1; + vs_scanline_resample_linear_NV12 (tmp1, src->pixels, src->width, dest->width, + &xacc, x_increment); + y1 = 0; + for (i = 0; i < dest->height; i++) { + j = acc >> 16; + x = acc & 0xffff; + + if (x == 0) { + if (j == y1) { + memcpy (dest->pixels + i * dest->stride, tmp1, dest_size); + } else if (j == y2) { + memcpy (dest->pixels + i * dest->stride, tmp2, dest_size); + } else { + xacc = 0; + vs_scanline_resample_linear_NV12 (tmp1, src->pixels + j * src->stride, + src->width, dest->width, &xacc, x_increment); + y1 = j; + memcpy (dest->pixels + i * dest->stride, tmp1, dest_size); + } + } else { + if (j == y1) { + if (j + 1 != y2) { + xacc = 0; + vs_scanline_resample_linear_NV12 (tmp2, + src->pixels + (j + 1) * src->stride, src->width, dest->width, + &xacc, x_increment); + y2 = j + 1; + } + vs_scanline_merge_linear_NV12 (dest->pixels + i * dest->stride, + tmp1, tmp2, dest->width, x); + } else if (j == y2) { + if (j + 1 != y1) { + xacc = 0; + vs_scanline_resample_linear_NV12 (tmp1, + src->pixels + (j + 1) * src->stride, src->width, dest->width, + &xacc, x_increment); + y1 = j + 1; + } + vs_scanline_merge_linear_NV12 (dest->pixels + i * dest->stride, + tmp2, tmp1, dest->width, x); + } else { + xacc = 0; + vs_scanline_resample_linear_NV12 (tmp1, src->pixels + j * src->stride, + src->width, dest->width, &xacc, x_increment); + y1 = j; + xacc = 0; + vs_scanline_resample_linear_NV12 (tmp2, + src->pixels + (j + 1) * src->stride, src->width, dest->width, + &xacc, x_increment); + y2 = (j + 1); + vs_scanline_merge_linear_NV12 (dest->pixels + i * dest->stride, + tmp1, tmp2, dest->width, x); + } + } + + acc += y_increment; + } +} + /* greyscale */ void diff --git a/gst/videoscale/vs_image.h b/gst/videoscale/vs_image.h index 7b3499152a..d3cc31d7c3 100644 --- a/gst/videoscale/vs_image.h +++ b/gst/videoscale/vs_image.h @@ -71,6 +71,11 @@ void vs_image_scale_nearest_UYVY (const VSImage *dest, const VSImage *src, void vs_image_scale_linear_UYVY (const VSImage *dest, const VSImage *src, uint8_t *tmpbuf); +void vs_image_scale_nearest_NV12 (const VSImage *dest, const VSImage *src, + uint8_t *tmpbuf); +void vs_image_scale_linear_NV12 (const VSImage *dest, const VSImage *src, + uint8_t *tmpbuf); + void vs_image_scale_nearest_Y (const VSImage *dest, const VSImage *src, uint8_t *tmpbuf); void vs_image_scale_linear_Y (const VSImage *dest, const VSImage *src, diff --git a/gst/videoscale/vs_scanline.c b/gst/videoscale/vs_scanline.c index 822a6b6600..c9459c234c 100644 --- a/gst/videoscale/vs_scanline.c +++ b/gst/videoscale/vs_scanline.c @@ -519,6 +519,88 @@ vs_scanline_merge_linear_UYVY (uint8_t * dest, uint8_t * src1, } +/* NV12 */ + +/* n is the number of bi-pixels */ + +void +vs_scanline_downsample_NV12 (uint8_t * dest, uint8_t * src, int n) +{ + int i; + + for (i = 0; i < n; i++) { + dest[i * 2 + 0] = (src[i * 4 + 0] + src[i * 4 + 2]) / 2; + dest[i * 2 + 1] = (src[i * 4 + 1] + src[i * 4 + 3]) / 2; + } +} + +void +vs_scanline_resample_nearest_NV12 (uint8_t * dest, uint8_t * src, int src_width, + int n, int *accumulator, int increment) +{ + int acc = *accumulator; + int i; + int j; + int x; + + for (i = 0; i < n; i++) { + j = acc >> 16; + x = acc & 0xffff; + + dest[i * 2 + 0] = (x < 32768 + || j + 1 >= src_width) ? src[j * 2 + 0] : src[j * 2 + 2]; + dest[i * 2 + 1] = (x < 32768 + || j + 1 >= src_width) ? src[j * 2 + 1] : src[j * 2 + 3]; + + acc += increment; + } + + *accumulator = acc; +} + +void +vs_scanline_resample_linear_NV12 (uint8_t * dest, uint8_t * src, int src_width, + int n, int *accumulator, int increment) +{ + int acc = *accumulator; + int i; + int j; + int x; + + for (i = 0; i < n; i++) { + j = acc >> 16; + x = acc & 0xffff; + + if (j + 1 < src_width) { + dest[i * 2 + 0] = + (src[j * 2 + 0] * (65536 - x) + src[j * 2 + 2] * x) >> 16; + dest[i * 2 + 1] = + (src[j * 2 + 1] * (65536 - x) + src[j * 2 + 3] * x) >> 16; + } else { + dest[i * 4 + 0] = src[j * 2 + 0]; + dest[i * 4 + 1] = src[j * 2 + 1]; + } + + acc += increment; + } + + *accumulator = acc; +} + +void +vs_scanline_merge_linear_NV12 (uint8_t * dest, uint8_t * src1, + uint8_t * src2, int n, int x) +{ + uint32_t value = x >> 8; + + if (value == 0) { + memcpy (dest, src1, n * 2); + } else { + orc_merge_linear_u8 (dest, src1, src2, value, n * 2); + } +} + + /* RGB565 */ /* note that src and dest are uint16_t, and thus endian dependent */ diff --git a/gst/videoscale/vs_scanline.h b/gst/videoscale/vs_scanline.h index 387fc9511b..92d8f6f04d 100644 --- a/gst/videoscale/vs_scanline.h +++ b/gst/videoscale/vs_scanline.h @@ -55,6 +55,11 @@ void vs_scanline_resample_nearest_UYVY (uint8_t *dest, uint8_t *src, int src_wid void vs_scanline_resample_linear_UYVY (uint8_t *dest, uint8_t *src, int src_width, int n, int *accumulator, int increment); void vs_scanline_merge_linear_UYVY (uint8_t *dest, uint8_t *src1, uint8_t *src2, int n, int x); +void vs_scanline_downsample_NV12 (uint8_t *dest, uint8_t *src, int n); +void vs_scanline_resample_nearest_NV12 (uint8_t *dest, uint8_t *src, int src_width, int n, int *accumulator, int increment); +void vs_scanline_resample_linear_NV12 (uint8_t *dest, uint8_t *src, int src_width, int n, int *accumulator, int increment); +void vs_scanline_merge_linear_NV12 (uint8_t *dest, uint8_t *src1, uint8_t *src2, int n, int x); + void vs_scanline_downsample_RGB565 (uint8_t *dest, uint8_t *src, int n); void vs_scanline_resample_nearest_RGB565 (uint8_t *dest, uint8_t *src, int src_width, int n, int *accumulator, int increment); void vs_scanline_resample_linear_RGB565 (uint8_t *dest, uint8_t *src, int src_width, int n, int *accumulator, int increment); From dc08c01935e59ac090dd2fbaa9c7fe1718eead08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Wed, 8 Feb 2012 19:34:57 +0000 Subject: [PATCH 11/12] typefindfunctions: minor cosmetic change Don't write < 1 when we mean == 0. --- gst/typefind/gsttypefindfunctions.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gst/typefind/gsttypefindfunctions.c b/gst/typefind/gsttypefindfunctions.c index 27823b64bf..bd8f97c9e0 100644 --- a/gst/typefind/gsttypefindfunctions.c +++ b/gst/typefind/gsttypefindfunctions.c @@ -2451,9 +2451,8 @@ h264_video_type_find (GstTypeFind * tf, gpointer unused) data_scan_ctx_advance (tf, &c, 1); } - if (good >= 2 && bad < 1) { + if (good >= 2 && bad == 0) { gst_type_find_suggest (tf, GST_TYPE_FIND_POSSIBLE, H264_VIDEO_CAPS); - return; } } From e832929080de83454cf4dac0f5aa194a3e0874ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Wed, 8 Feb 2012 19:39:00 +0000 Subject: [PATCH 12/12] typefindfunctions: make h264 typefinder more picky when returning "likely" probability Only return LIKELY probability if we've seen an SPS, PPS and an IDR slice nal, i.e. try harder to avoid false positives such as with certain VC-1 files. https://bugzilla.gnome.org/show_bug.cgi?id=668565 --- gst/typefind/gsttypefindfunctions.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/gst/typefind/gsttypefindfunctions.c b/gst/typefind/gsttypefindfunctions.c index bd8f97c9e0..1bf32cbded 100644 --- a/gst/typefind/gsttypefindfunctions.c +++ b/gst/typefind/gsttypefindfunctions.c @@ -2402,6 +2402,9 @@ h264_video_type_find (GstTypeFind * tf, gpointer unused) /* Stream consists of: a series of sync codes (00 00 00 01) followed * by NALs */ + gboolean seen_idr = FALSE; + gboolean seen_sps = FALSE; + gboolean seen_pps = FALSE; int nut, ref; int good = 0; int bad = 0; @@ -2426,6 +2429,13 @@ h264_video_type_find (GstTypeFind * tf, gpointer unused) ((nut == 6 || (nut >= 9 && nut <= 12)) && ref != 0)) { bad++; } else { + if (nut == 7) + seen_sps = TRUE; + else if (nut == 8) + seen_pps = TRUE; + else if (nut == 5) + seen_idr = TRUE; + good++; } } else if (nut >= 14 && nut <= 33) { @@ -2439,9 +2449,10 @@ h264_video_type_find (GstTypeFind * tf, gpointer unused) /* don't consider these bad */ } - GST_DEBUG ("good %d bad %d", good, bad); + GST_LOG ("good:%d, bad:%d, pps:%d, sps:%d, idr:%d", good, bad, seen_pps, + seen_sps, seen_idr); - if (good >= 10 && bad < 4) { + if (seen_sps && seen_pps && seen_idr && good >= 10 && bad < 4) { gst_type_find_suggest (tf, GST_TYPE_FIND_LIKELY, H264_VIDEO_CAPS); return; } @@ -2451,6 +2462,9 @@ h264_video_type_find (GstTypeFind * tf, gpointer unused) data_scan_ctx_advance (tf, &c, 1); } + GST_LOG ("good:%d, bad:%d, pps:%d, sps:%d, idr:%d", good, bad, seen_pps, + seen_sps, seen_idr); + if (good >= 2 && bad == 0) { gst_type_find_suggest (tf, GST_TYPE_FIND_POSSIBLE, H264_VIDEO_CAPS); }