/* GStreamer * Copyright (C) 2010 David Schleef * Copyright (C) 2010 Sebastian Dröge * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "video-converter.h" #include #include #include #include "video-orc.h" /** * SECTION:videoconverter * @short_description: Generic video conversion * * * * This object is used to convert video frames from one format to another. * The object can perform conversion of: * * * video format * * * video colorspace * * * chroma-siting * * * video size (planned) * * * */ typedef struct _GstLineCache GstLineCache; struct _GstVideoConverter { GstVideoInfo in_info; GstVideoInfo out_info; gint in_x; gint in_y; gint in_width; gint in_height; gint in_maxwidth; gint in_maxheight; gint out_x; gint out_y; gint out_width; gint out_height; gint out_maxwidth; gint out_maxheight; gint current_pstride; gint current_width; GstVideoFormat current_format; gint in_bits; gint out_bits; gint cmatrix[4][4]; guint64 orc_p1; guint64 orc_p2; guint64 orc_p3; GstStructure *config; GstVideoDitherMethod dither; guint lines; guint n_tmplines; gpointer *tmplines; guint16 *errline; guint tmplines_idx; guint n_btmplines; gpointer *btmplines; guint btmplines_idx; gboolean fill_border; gpointer borderline; guint32 border_argb; GstVideoChromaResample *upsample; guint up_n_lines; gint up_offset; GstVideoChromaResample *downsample; guint down_n_lines; gint down_offset; void (*convert) (GstVideoConverter * convert, const GstVideoFrame * src, GstVideoFrame * dest); void (*matrix) (GstVideoConverter * convert, gpointer pixels); void (*dither16) (GstVideoConverter * convert, guint16 * pixels, int j); gboolean identity_unpack; GstLineCache *unpack_lines; GstLineCache *upsample_lines; GstLineCache *hscale_lines; GstLineCache *vscale_lines; GstLineCache *convert_lines; GstLineCache *downsample_lines; GstLineCache *pack_lines; gboolean identity_pack; GstVideoScaler *h_scaler; gint h_scale_format; GstVideoScaler *v_scaler; gint v_scale_width; gint v_scale_format; gint pack_pstride; const GstVideoFrame *src; GstVideoFrame *dest; }; typedef gpointer (*GstLineCacheAllocLineFunc) (GstLineCache * cache, gint idx, gpointer user_data); typedef gboolean (*GstLineCacheNeedLineFunc) (GstLineCache * cache, gint out_line, gint in_line, gpointer user_data); struct _GstLineCache { gint first; GPtrArray *lines; GstLineCache *prev; gboolean write_input; gboolean pass_alloc; gboolean alloc_writable; GstLineCacheNeedLineFunc need_line; gpointer need_line_data; GDestroyNotify need_line_notify; GstLineCacheAllocLineFunc alloc_line; gpointer alloc_line_data; GDestroyNotify alloc_line_notify; }; static GstLineCache * gst_line_cache_new (GstLineCache * prev) { GstLineCache *result; result = g_slice_new0 (GstLineCache); result->lines = g_ptr_array_new (); result->prev = prev; return result; } static void gst_line_cache_clear (GstLineCache * cache) { g_return_if_fail (cache != NULL); g_ptr_array_set_size (cache->lines, 0); cache->first = 0; } static void gst_line_cache_free (GstLineCache * cache) { gst_line_cache_clear (cache); g_ptr_array_unref (cache->lines); g_slice_free (GstLineCache, cache); } static void gst_line_cache_set_need_line_func (GstLineCache * cache, GstLineCacheNeedLineFunc need_line, gpointer user_data, GDestroyNotify notify) { cache->need_line = need_line; cache->need_line_data = user_data; cache->need_line_notify = notify; } static void gst_line_cache_set_alloc_line_func (GstLineCache * cache, GstLineCacheAllocLineFunc alloc_line, gpointer user_data, GDestroyNotify notify) { cache->alloc_line = alloc_line; cache->alloc_line_data = user_data; cache->alloc_line_notify = notify; } static gpointer * gst_line_cache_get_lines (GstLineCache * cache, gint out_line, gint in_line, gint n_lines) { if (cache->first < in_line) { gint to_remove = MIN (in_line - cache->first, cache->lines->len); if (to_remove > 0) g_ptr_array_remove_range (cache->lines, 0, to_remove); cache->first = in_line; } else if (in_line < cache->first) { gst_line_cache_clear (cache); cache->first = in_line; } while (TRUE) { gint oline; if (cache->first <= in_line && in_line + n_lines <= cache->first + (gint) cache->lines->len) { return cache->lines->pdata + (in_line - cache->first); } if (cache->need_line == NULL) break; oline = out_line + cache->first + cache->lines->len - in_line; if (!cache->need_line (cache, oline, cache->first + cache->lines->len, cache->need_line_data)) break; } GST_DEBUG ("no lines"); return NULL; } static void gst_line_cache_add_line (GstLineCache * cache, gint idx, gpointer line) { if (cache->first + cache->lines->len != idx) { gst_line_cache_clear (cache); cache->first = idx; } g_ptr_array_add (cache->lines, line); } static gpointer gst_line_cache_alloc_line (GstLineCache * cache, gint idx) { gpointer res; if (cache->alloc_line) res = cache->alloc_line (cache, idx, cache->alloc_line_data); else res = NULL; return res; } static void video_converter_generic (GstVideoConverter * convert, const GstVideoFrame * src, GstVideoFrame * dest); static void video_converter_matrix8 (GstVideoConverter * convert, gpointer pixels); static void video_converter_matrix16 (GstVideoConverter * convert, gpointer pixels); static gboolean video_converter_lookup_fastpath (GstVideoConverter * convert); static void video_converter_compute_matrix (GstVideoConverter * convert); static void video_converter_compute_resample (GstVideoConverter * convert); static gpointer get_dest_line (GstLineCache * cache, gint idx, gpointer user_data); static gboolean do_unpack_lines (GstLineCache * cache, gint out_line, gint in_line, gpointer user_data); static gboolean do_downsample_lines (GstLineCache * cache, gint out_line, gint in_line, gpointer user_data); static gboolean do_convert_lines (GstLineCache * cache, gint out_line, gint in_line, gpointer user_data); static gboolean do_upsample_lines (GstLineCache * cache, gint out_line, gint in_line, gpointer user_data); static gboolean do_vscale_lines (GstLineCache * cache, gint out_line, gint in_line, gpointer user_data); static gboolean do_hscale_lines (GstLineCache * cache, gint out_line, gint in_line, gpointer user_data); static void alloc_tmplines (GstVideoConverter * convert, guint lines, guint blines, gint width) { gint i; convert->n_tmplines = lines; convert->tmplines = g_malloc (lines * sizeof (gpointer)); for (i = 0; i < lines; i++) convert->tmplines[i] = g_malloc (sizeof (guint16) * (width + 8) * 4); convert->tmplines_idx = 0; convert->n_btmplines = blines; convert->btmplines = g_malloc (blines * sizeof (gpointer)); for (i = 0; i < blines; i++) { convert->btmplines[i] = g_malloc (sizeof (guint16) * (width + 8) * 4); if (convert->borderline) memcpy (convert->btmplines[i], convert->borderline, width * 8); } convert->btmplines_idx = 0; } static gpointer get_temp_line (GstLineCache * cache, gint idx, gpointer user_data) { GstVideoConverter *convert = user_data; gpointer tmpline; GST_DEBUG ("get temp line %d", idx); tmpline = (guint8 *) convert->tmplines[convert->tmplines_idx] + (convert->out_x * convert->pack_pstride); convert->tmplines_idx = (convert->tmplines_idx + 1) % convert->n_tmplines; return tmpline; } static gpointer get_border_temp_line (GstLineCache * cache, gint idx, gpointer user_data) { GstVideoConverter *convert = user_data; gpointer tmpline; GST_DEBUG ("get border temp line %d", idx); tmpline = (guint8 *) convert->btmplines[convert->btmplines_idx] + (convert->out_x * convert->pack_pstride); convert->btmplines_idx = (convert->btmplines_idx + 1) % convert->n_btmplines; return tmpline; } static GstLineCache * chain_unpack_line (GstVideoConverter * convert) { GstLineCache *prev; convert->current_format = convert->in_info.finfo->unpack_format; convert->current_pstride = convert->in_bits >> 1; convert->identity_unpack = (convert->current_format == convert->in_info.finfo->format); GST_DEBUG ("chain unpack line format %s, pstride %d, identity_unpack %d", gst_video_format_to_string (convert->current_format), convert->current_pstride, convert->identity_unpack); prev = convert->unpack_lines = gst_line_cache_new (NULL); prev->write_input = FALSE; prev->pass_alloc = FALSE; gst_line_cache_set_need_line_func (convert->unpack_lines, do_unpack_lines, convert, NULL); return convert->unpack_lines; } static GstLineCache * chain_upsample (GstVideoConverter * convert, GstLineCache * prev) { video_converter_compute_resample (convert); if (convert->upsample) { GST_DEBUG ("chain upsample"); prev = convert->upsample_lines = gst_line_cache_new (prev); prev->write_input = TRUE; prev->pass_alloc = TRUE; gst_line_cache_set_need_line_func (convert->upsample_lines, do_upsample_lines, convert, NULL); } return prev; } static GstLineCache * chain_convert (GstVideoConverter * convert, GstLineCache * prev) { if (convert->in_bits != convert->out_bits || convert->matrix) { GST_DEBUG ("chain convert"); prev = convert->convert_lines = gst_line_cache_new (prev); prev->write_input = TRUE; prev->pass_alloc = TRUE; gst_line_cache_set_need_line_func (convert->convert_lines, do_convert_lines, convert, NULL); } convert->current_format = convert->out_info.finfo->unpack_format; convert->current_pstride = convert->out_bits >> 1; GST_DEBUG ("chain after convert %s, pstride %d", gst_video_format_to_string (convert->current_format), convert->current_pstride); return prev; } static GstLineCache * chain_hscale (GstVideoConverter * convert, GstLineCache * prev) { gint method; guint taps; prev = convert->hscale_lines = gst_line_cache_new (prev); prev->write_input = FALSE; prev->pass_alloc = FALSE; gst_line_cache_set_need_line_func (convert->hscale_lines, do_hscale_lines, convert, NULL); if (!gst_structure_get_enum (convert->config, GST_VIDEO_CONVERTER_OPT_RESAMPLER_METHOD, GST_TYPE_VIDEO_RESAMPLER_METHOD, &method)) method = GST_VIDEO_RESAMPLER_METHOD_CUBIC; if (!gst_structure_get_uint (convert->config, GST_VIDEO_CONVERTER_OPT_RESAMPLER_TAPS, &taps)) taps = 0; convert->h_scaler = gst_video_scaler_new (method, GST_VIDEO_SCALER_FLAG_NONE, taps, convert->in_width, convert->out_width, convert->config); GST_DEBUG ("chain hscale %d->%d, taps %d, method %d", convert->in_width, convert->out_width, taps, method); convert->current_width = convert->out_width; convert->h_scale_format = convert->current_format; return prev; } static GstLineCache * chain_vscale (GstVideoConverter * convert, GstLineCache * prev) { GstVideoScalerFlags flags; gint method; guint taps; flags = GST_VIDEO_INFO_IS_INTERLACED (&convert->in_info) ? GST_VIDEO_SCALER_FLAG_INTERLACED : 0; if (!gst_structure_get_enum (convert->config, GST_VIDEO_CONVERTER_OPT_RESAMPLER_METHOD, GST_TYPE_VIDEO_RESAMPLER_METHOD, &method)) method = GST_VIDEO_RESAMPLER_METHOD_CUBIC; if (!gst_structure_get_uint (convert->config, GST_VIDEO_CONVERTER_OPT_RESAMPLER_TAPS, &taps)) taps = 0; convert->v_scaler = gst_video_scaler_new (method, flags, taps, convert->in_height, convert->out_height, convert->config); convert->v_scale_width = convert->current_width; convert->v_scale_format = convert->current_format; gst_video_scaler_get_coeff (convert->v_scaler, 0, NULL, &taps); GST_DEBUG ("chain vscale %d->%d, taps %d, method %d", convert->in_width, convert->out_height, taps, method); prev = convert->vscale_lines = gst_line_cache_new (prev); prev->pass_alloc = (taps == 1); prev->write_input = FALSE; gst_line_cache_set_need_line_func (convert->vscale_lines, do_vscale_lines, convert, NULL); return prev; } static GstLineCache * chain_downsample (GstVideoConverter * convert, GstLineCache * prev) { if (convert->downsample) { GST_DEBUG ("chain downsample"); prev = convert->downsample_lines = gst_line_cache_new (prev); prev->write_input = TRUE; prev->pass_alloc = TRUE; gst_line_cache_set_need_line_func (convert->downsample_lines, do_downsample_lines, convert, NULL); } return prev; } static GstLineCache * chain_pack (GstVideoConverter * convert, GstLineCache * prev) { convert->pack_pstride = convert->current_pstride; convert->identity_pack = (convert->out_info.finfo->format == convert->out_info.finfo->unpack_format); GST_DEBUG ("chain pack line format %s, pstride %d, identity_pack %d (%d %d)", gst_video_format_to_string (convert->current_format), convert->current_pstride, convert->identity_pack, convert->out_info.finfo->format, convert->out_info.finfo->unpack_format); return prev; } static gint get_opt_int (GstVideoConverter * convert, const gchar * opt, gint def) { gint res; if (!gst_structure_get_int (convert->config, opt, &res)) res = def; return res; } static guint get_opt_uint (GstVideoConverter * convert, const gchar * opt, guint def) { guint res; if (!gst_structure_get_uint (convert->config, opt, &res)) res = def; return res; } static gboolean get_opt_bool (GstVideoConverter * convert, const gchar * opt, gboolean def) { gboolean res; if (!gst_structure_get_boolean (convert->config, opt, &res)) res = def; return res; } static void setup_allocators (GstVideoConverter * convert) { GstLineCache *cache; GstLineCacheAllocLineFunc alloc_line; gboolean alloc_writable; /* start with using dest lines if we can directly write into it */ if (convert->identity_pack) { alloc_line = get_dest_line; alloc_writable = TRUE; } else { alloc_line = get_border_temp_line; alloc_writable = FALSE; } /* now walk backwards, we try to write into the dest lines directly * and keep track if the source needs to be writable */ for (cache = convert->pack_lines; cache; cache = cache->prev) { gst_line_cache_set_alloc_line_func (cache, alloc_line, convert, NULL); cache->alloc_writable = alloc_writable; if (cache->pass_alloc == FALSE) { /* can't pass allocator, use temp lines */ alloc_line = get_temp_line; alloc_writable = FALSE; } /* if someone writes to the input, we need a writable line from the * previous cache */ if (cache->write_input) alloc_writable = TRUE; } } /** * gst_video_converter_new: * @in_info: a #GstVideoInfo * @out_info: a #GstVideoInfo * @config: a #GstStructure with configuration options * * Create a new converter object to convert between @in_info and @out_info * with @config. * * Returns: a #GstVideoConverter or %NULL if conversion is not possible. * * Since: 1.6 */ GstVideoConverter * gst_video_converter_new (GstVideoInfo * in_info, GstVideoInfo * out_info, GstStructure * config) { GstVideoConverter *convert; gint width; GstLineCache *prev; gint s2, s3; const GstVideoFormatInfo *fin, *fout; g_return_val_if_fail (in_info != NULL, NULL); g_return_val_if_fail (out_info != NULL, NULL); /* we won't ever do framerate conversion */ g_return_val_if_fail (in_info->fps_n == out_info->fps_n, NULL); g_return_val_if_fail (in_info->fps_d == out_info->fps_d, NULL); /* we won't ever do deinterlace */ g_return_val_if_fail (in_info->interlace_mode == out_info->interlace_mode, NULL); convert = g_slice_new0 (GstVideoConverter); fin = in_info->finfo; fout = out_info->finfo; convert->in_info = *in_info; convert->out_info = *out_info; /* default config */ convert->config = gst_structure_new ("GstVideoConverter", "dither", GST_TYPE_VIDEO_DITHER_METHOD, GST_VIDEO_DITHER_NONE, NULL); if (config) gst_video_converter_set_config (convert, config); convert->in_maxwidth = GST_VIDEO_INFO_WIDTH (in_info); convert->in_maxheight = GST_VIDEO_INFO_HEIGHT (in_info); convert->out_maxwidth = GST_VIDEO_INFO_WIDTH (out_info); convert->out_maxheight = GST_VIDEO_INFO_HEIGHT (out_info); convert->in_x = get_opt_int (convert, GST_VIDEO_CONVERTER_OPT_SRC_X, 0); convert->in_y = get_opt_int (convert, GST_VIDEO_CONVERTER_OPT_SRC_Y, 0); convert->in_width = get_opt_int (convert, GST_VIDEO_CONVERTER_OPT_SRC_WIDTH, convert->in_maxwidth); convert->in_height = get_opt_int (convert, GST_VIDEO_CONVERTER_OPT_SRC_HEIGHT, convert->in_maxheight); convert->in_x &= ~((1 << fin->w_sub[1]) - 1); convert->in_y &= ~((1 << fin->h_sub[1]) - 1); convert->out_x = get_opt_int (convert, GST_VIDEO_CONVERTER_OPT_DEST_X, 0); convert->out_y = get_opt_int (convert, GST_VIDEO_CONVERTER_OPT_DEST_Y, 0); convert->out_width = get_opt_int (convert, GST_VIDEO_CONVERTER_OPT_DEST_WIDTH, convert->out_maxwidth); convert->out_height = get_opt_int (convert, GST_VIDEO_CONVERTER_OPT_DEST_HEIGHT, convert->out_maxheight); convert->out_x &= ~((1 << fout->w_sub[1]) - 1); convert->out_y &= ~((1 << fout->h_sub[1]) - 1); convert->fill_border = get_opt_bool (convert, GST_VIDEO_CONVERTER_OPT_FILL_BORDER, TRUE); convert->border_argb = get_opt_uint (convert, GST_VIDEO_CONVERTER_OPT_BORDER_ARGB, 0x00000000); if (video_converter_lookup_fastpath (convert)) goto done; if (in_info->finfo->unpack_func == NULL) goto no_unpack_func; if (out_info->finfo->pack_func == NULL) goto no_pack_func; convert->convert = video_converter_generic; s2 = convert->in_width * convert->out_height; s3 = convert->out_width * convert->in_height; video_converter_compute_matrix (convert); convert->current_format = GST_VIDEO_INFO_FORMAT (in_info); convert->current_width = convert->in_width; /* unpack */ prev = chain_unpack_line (convert); /* upsample chroma */ prev = chain_upsample (convert, prev); if (s3 <= s2) { /* horizontal scaling first produces less pixels */ if (convert->in_width <= convert->out_width) { /* upscaling, first convert then */ prev = chain_convert (convert, prev); } if (convert->in_width != convert->out_width) { prev = chain_hscale (convert, prev); } if (convert->in_width > convert->out_width) { /* downscaling, convert after scaling then */ prev = chain_convert (convert, prev); } } /* v-scale */ if (convert->in_height != convert->out_height) { prev = chain_vscale (convert, prev); } if (s3 > s2) { /* vertical scaling first produced less pixels */ if (convert->in_width <= convert->out_width) { /* upscaling, first convert then */ prev = chain_convert (convert, prev); } if (convert->in_width != convert->out_width) { prev = chain_hscale (convert, prev); } if (convert->in_width > convert->out_width) { /* downscaling, convert after scaling then */ prev = chain_convert (convert, prev); } } /* downsample chroma */ prev = chain_downsample (convert, prev); /* pack into final format */ convert->pack_lines = chain_pack (convert, prev); /* now figure out allocators */ setup_allocators (convert); convert->lines = out_info->finfo->pack_lines; width = MAX (convert->in_maxwidth, convert->out_maxwidth); width += convert->out_x; convert->errline = g_malloc0 (sizeof (guint16) * width * 4); if (convert->fill_border && (convert->out_height < convert->out_maxheight || convert->out_width < convert->out_maxwidth)) { gint i; guint8 *b; guint64 border_val, v; b = convert->borderline = g_malloc0 (sizeof (guint16) * width * 4); if (GST_VIDEO_INFO_IS_YUV (&convert->out_info)) { /* FIXME, convert to AYUV, just black for now */ border_val = 0x00007f7f; } else { border_val = convert->border_argb; } if (convert->out_bits == 8) v = (border_val << 32) | border_val; else { guint64 c; c = (border_val >> 24) & 0xff; v = (c << 56) | (c << 48); c = (border_val >> 16) & 0xff; v |= (c << 40) | (c << 32); c = (border_val >> 8) & 0xff; v |= (c << 24) | (c << 16); c = (border_val) & 0xff; v |= (c << 8) | c; } v = GINT64_TO_BE (v); for (i = 0; i < width; i++) { memcpy (b, &v, 8); b += 8; } } else { convert->borderline = NULL; } /* FIXME */ alloc_tmplines (convert, 64, 4, width); done: return convert; /* ERRORS */ no_unpack_func: { GST_ERROR ("no unpack_func for format %s", gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (in_info))); gst_video_converter_free (convert); return NULL; } no_pack_func: { GST_ERROR ("no pack_func for format %s", gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (out_info))); gst_video_converter_free (convert); return NULL; } } /** * gst_video_converter_free: * @convert: a #GstVideoConverter * * Free @convert * * Since: 1.6 */ void gst_video_converter_free (GstVideoConverter * convert) { gint i; g_return_if_fail (convert != NULL); if (convert->upsample) gst_video_chroma_resample_free (convert->upsample); if (convert->downsample) gst_video_chroma_resample_free (convert->downsample); if (convert->v_scaler) gst_video_scaler_free (convert->v_scaler); if (convert->h_scaler) gst_video_scaler_free (convert->h_scaler); if (convert->unpack_lines) gst_line_cache_free (convert->unpack_lines); if (convert->upsample_lines) gst_line_cache_free (convert->upsample_lines); if (convert->hscale_lines) gst_line_cache_free (convert->hscale_lines); if (convert->vscale_lines) gst_line_cache_free (convert->vscale_lines); if (convert->convert_lines) gst_line_cache_free (convert->convert_lines); if (convert->downsample_lines) gst_line_cache_free (convert->downsample_lines); for (i = 0; i < convert->n_tmplines; i++) g_free (convert->tmplines[i]); g_free (convert->tmplines); for (i = 0; i < convert->n_btmplines; i++) g_free (convert->btmplines[i]); g_free (convert->btmplines); g_free (convert->errline); g_free (convert->borderline); if (convert->config) gst_structure_free (convert->config); g_slice_free (GstVideoConverter, convert); } static void video_dither_verterr (GstVideoConverter * convert, guint16 * pixels, int j) { int i; guint16 *errline = convert->errline; unsigned int mask = 0xff; for (i = 0; i < 4 * convert->in_width; i++) { int x = pixels[i] + errline[i]; if (x > 65535) x = 65535; pixels[i] = x; errline[i] = x & mask; } } static void video_dither_halftone (GstVideoConverter * convert, guint16 * pixels, int j) { int i; static guint16 halftone[8][8] = { {0, 128, 32, 160, 8, 136, 40, 168}, {192, 64, 224, 96, 200, 72, 232, 104}, {48, 176, 16, 144, 56, 184, 24, 152}, {240, 112, 208, 80, 248, 120, 216, 88}, {12, 240, 44, 172, 4, 132, 36, 164}, {204, 76, 236, 108, 196, 68, 228, 100}, {60, 188, 28, 156, 52, 180, 20, 148}, {252, 142, 220, 92, 244, 116, 212, 84} }; for (i = 0; i < convert->in_width * 4; i++) { int x; x = pixels[i] + halftone[(i >> 2) & 7][j & 7]; if (x > 65535) x = 65535; pixels[i] = x; } } static gboolean copy_config (GQuark field_id, const GValue * value, gpointer user_data) { GstVideoConverter *convert = user_data; gst_structure_id_set_value (convert->config, field_id, value); return TRUE; } /** * gst_video_converter_set_config: * @convert: a #GstVideoConverter * @config: (transfer full): a #GstStructure * * Set @config as extra configuraion for @convert. * * If the parameters in @config can not be set exactly, this function returns * %FALSE and will try to update as much state as possible. The new state can * then be retrieved and refined with gst_video_converter_get_config(). * * The config is a GstStructure that can contain the the following fields: * * "dither" GST_TYPE_VIDEO_DITHER_METHOD The dithering used when reducing * colors * * Returns: %TRUE when @config could be set. * * Since: 1.6 */ gboolean gst_video_converter_set_config (GstVideoConverter * convert, GstStructure * config) { gint dither; gboolean res = TRUE; g_return_val_if_fail (convert != NULL, FALSE); g_return_val_if_fail (config != NULL, FALSE); if (gst_structure_get_enum (config, GST_VIDEO_CONVERTER_OPT_DITHER_METHOD, GST_TYPE_VIDEO_DITHER_METHOD, &dither)) { gboolean update = TRUE; switch (dither) { case GST_VIDEO_DITHER_NONE: convert->dither16 = NULL; break; case GST_VIDEO_DITHER_VERTERR: convert->dither16 = video_dither_verterr; break; case GST_VIDEO_DITHER_HALFTONE: convert->dither16 = video_dither_halftone; break; default: update = FALSE; break; } if (update) gst_structure_set (convert->config, GST_VIDEO_CONVERTER_OPT_DITHER_METHOD, GST_TYPE_VIDEO_DITHER_METHOD, dither, NULL); else res = FALSE; } if (res) gst_structure_foreach (config, copy_config, convert); gst_structure_free (config); return res; } /** * gst_video_converter_get_config: * @@convert: a #GstVideoConverter * * Get the current configuration of @convert. * * Returns: a #GstStructure that remains valid for as long as @convert is valid * or until gst_video_converter_set_config() is called. */ const GstStructure * gst_video_converter_get_config (GstVideoConverter * convert) { g_return_val_if_fail (convert != NULL, NULL); return convert->config; } /** * gst_video_converter_frame: * @convert: a #GstVideoConverter * @dest: a #GstVideoFrame * @src: a #GstVideoFrame * * Convert the pixels of @src into @dest using @convert. * * Since: 1.6 */ void gst_video_converter_frame (GstVideoConverter * convert, const GstVideoFrame * src, GstVideoFrame * dest) { g_return_if_fail (convert != NULL); g_return_if_fail (src != NULL); g_return_if_fail (dest != NULL); convert->convert (convert, src, dest); } #define SCALE (8) #define SCALE_F ((float) (1 << SCALE)) static void video_converter_matrix8 (GstVideoConverter * convert, gpointer pixels) { gint width = MIN (convert->in_width, convert->out_width); #if 1 video_orc_matrix8 (pixels, pixels, convert->orc_p1, convert->orc_p2, convert->orc_p3, width); #elif 0 /* FIXME we would like to set this as a backup function, it's faster than the * orc generated one */ int i; int r, g, b; int y, u, v; guint8 *p = pixels; for (i = 0; i < convert->in_width; i++) { r = p[i * 4 + 1]; g = p[i * 4 + 2]; b = p[i * 4 + 3]; y = (convert->cmatrix[0][0] * r + convert->cmatrix[0][1] * g + convert->cmatrix[0][2] * b + convert->cmatrix[0][3]) >> SCALE; u = (convert->cmatrix[1][0] * r + convert->cmatrix[1][1] * g + convert->cmatrix[1][2] * b + convert->cmatrix[1][3]) >> SCALE; v = (convert->cmatrix[2][0] * r + convert->cmatrix[2][1] * g + convert->cmatrix[2][2] * b + convert->cmatrix[2][3]) >> SCALE; p[i * 4 + 1] = CLAMP (y, 0, 255); p[i * 4 + 2] = CLAMP (u, 0, 255); p[i * 4 + 3] = CLAMP (v, 0, 255); } #endif } static void video_converter_matrix8_AYUV_ARGB (GstVideoConverter * convert, gpointer pixels) { video_orc_convert_AYUV_ARGB (pixels, 0, pixels, 0, convert->cmatrix[0][0], convert->cmatrix[0][2], convert->cmatrix[2][1], convert->cmatrix[1][1], convert->cmatrix[1][2], convert->in_width, 1); } static void video_converter_matrix16 (GstVideoConverter * convert, gpointer pixels) { int i; int r, g, b; int y, u, v; guint16 *p = pixels; for (i = 0; i < convert->in_width; i++) { r = p[i * 4 + 1]; g = p[i * 4 + 2]; b = p[i * 4 + 3]; y = (convert->cmatrix[0][0] * r + convert->cmatrix[0][1] * g + convert->cmatrix[0][2] * b + convert->cmatrix[0][3]) >> SCALE; u = (convert->cmatrix[1][0] * r + convert->cmatrix[1][1] * g + convert->cmatrix[1][2] * b + convert->cmatrix[1][3]) >> SCALE; v = (convert->cmatrix[2][0] * r + convert->cmatrix[2][1] * g + convert->cmatrix[2][2] * b + convert->cmatrix[2][3]) >> SCALE; p[i * 4 + 1] = CLAMP (y, 0, 65535); p[i * 4 + 2] = CLAMP (u, 0, 65535); p[i * 4 + 3] = CLAMP (v, 0, 65535); } } typedef struct { double m[4][4]; } ColorMatrix; static void color_matrix_set_identity (ColorMatrix * m) { int i, j; for (i = 0; i < 4; i++) { for (j = 0; j < 4; j++) { m->m[i][j] = (i == j); } } } /* Perform 4x4 matrix multiplication: * - @dst@ = @a@ * @b@ * - @dst@ may be a pointer to @a@ andor @b@ */ static void color_matrix_multiply (ColorMatrix * dst, ColorMatrix * a, ColorMatrix * b) { ColorMatrix tmp; int i, j, k; for (i = 0; i < 4; i++) { for (j = 0; j < 4; j++) { double x = 0; for (k = 0; k < 4; k++) { x += a->m[i][k] * b->m[k][j]; } tmp.m[i][j] = x; } } memcpy (dst, &tmp, sizeof (ColorMatrix)); } static void color_matrix_offset_components (ColorMatrix * m, double a1, double a2, double a3) { ColorMatrix a; color_matrix_set_identity (&a); a.m[0][3] = a1; a.m[1][3] = a2; a.m[2][3] = a3; color_matrix_multiply (m, &a, m); } static void color_matrix_scale_components (ColorMatrix * m, double a1, double a2, double a3) { ColorMatrix a; color_matrix_set_identity (&a); a.m[0][0] = a1; a.m[1][1] = a2; a.m[2][2] = a3; color_matrix_multiply (m, &a, m); } static void color_matrix_YCbCr_to_RGB (ColorMatrix * m, double Kr, double Kb) { double Kg = 1.0 - Kr - Kb; ColorMatrix k = { { {1., 0., 2 * (1 - Kr), 0.}, {1., -2 * Kb * (1 - Kb) / Kg, -2 * Kr * (1 - Kr) / Kg, 0.}, {1., 2 * (1 - Kb), 0., 0.}, {0., 0., 0., 1.}, } }; color_matrix_multiply (m, &k, m); } static void color_matrix_RGB_to_YCbCr (ColorMatrix * m, double Kr, double Kb) { double Kg = 1.0 - Kr - Kb; ColorMatrix k; double x; k.m[0][0] = Kr; k.m[0][1] = Kg; k.m[0][2] = Kb; k.m[0][3] = 0; x = 1 / (2 * (1 - Kb)); k.m[1][0] = -x * Kr; k.m[1][1] = -x * Kg; k.m[1][2] = x * (1 - Kb); k.m[1][3] = 0; x = 1 / (2 * (1 - Kr)); k.m[2][0] = x * (1 - Kr); k.m[2][1] = -x * Kg; k.m[2][2] = -x * Kb; k.m[2][3] = 0; k.m[3][0] = 0; k.m[3][1] = 0; k.m[3][2] = 0; k.m[3][3] = 1; color_matrix_multiply (m, &k, m); } static void video_converter_compute_matrix (GstVideoConverter * convert) { GstVideoInfo *in_info, *out_info; ColorMatrix dst; gint i, j; const GstVideoFormatInfo *sfinfo, *dfinfo; const GstVideoFormatInfo *suinfo, *duinfo; gint offset[4], scale[4]; gdouble Kr = 0, Kb = 0; in_info = &convert->in_info; out_info = &convert->out_info; sfinfo = in_info->finfo; dfinfo = out_info->finfo; suinfo = gst_video_format_get_info (sfinfo->unpack_format); duinfo = gst_video_format_get_info (dfinfo->unpack_format); convert->in_bits = GST_VIDEO_FORMAT_INFO_DEPTH (suinfo, 0); convert->out_bits = GST_VIDEO_FORMAT_INFO_DEPTH (duinfo, 0); GST_DEBUG ("in bits %d, out bits %d", convert->in_bits, convert->out_bits); if (in_info->colorimetry.range == out_info->colorimetry.range && in_info->colorimetry.matrix == out_info->colorimetry.matrix) { GST_DEBUG ("using identity color transform"); convert->matrix = NULL; return; } /* calculate intermediate format for the matrix. When unpacking, we expand * input to 16 when one of the inputs is 16 bits */ if (convert->in_bits == 16 || convert->out_bits == 16) { convert->matrix = video_converter_matrix16; if (GST_VIDEO_FORMAT_INFO_IS_RGB (suinfo)) suinfo = gst_video_format_get_info (GST_VIDEO_FORMAT_ARGB64); else suinfo = gst_video_format_get_info (GST_VIDEO_FORMAT_AYUV64); if (GST_VIDEO_FORMAT_INFO_IS_RGB (duinfo)) duinfo = gst_video_format_get_info (GST_VIDEO_FORMAT_ARGB64); else duinfo = gst_video_format_get_info (GST_VIDEO_FORMAT_AYUV64); } else { if (GST_VIDEO_FORMAT_INFO_IS_YUV (suinfo) && GST_VIDEO_FORMAT_INFO_IS_RGB (duinfo)) convert->matrix = video_converter_matrix8_AYUV_ARGB; else convert->matrix = video_converter_matrix8; } color_matrix_set_identity (&dst); /* 1, bring color components to [0..1.0] range */ gst_video_color_range_offsets (in_info->colorimetry.range, suinfo, offset, scale); color_matrix_offset_components (&dst, -offset[0], -offset[1], -offset[2]); color_matrix_scale_components (&dst, 1 / ((float) scale[0]), 1 / ((float) scale[1]), 1 / ((float) scale[2])); /* 2. bring components to R'G'B' space */ if (gst_video_color_matrix_get_Kr_Kb (in_info->colorimetry.matrix, &Kr, &Kb)) color_matrix_YCbCr_to_RGB (&dst, Kr, Kb); /* 3. inverse transfer function. R'G'B' to linear RGB */ /* 4. from RGB to XYZ using the primaries */ /* 5. from XYZ to RGB using the primaries */ /* 6. transfer function. linear RGB to R'G'B' */ /* 7. bring components to YCbCr space */ if (gst_video_color_matrix_get_Kr_Kb (out_info->colorimetry.matrix, &Kr, &Kb)) color_matrix_RGB_to_YCbCr (&dst, Kr, Kb); /* 8, bring color components to nominal range */ gst_video_color_range_offsets (out_info->colorimetry.range, duinfo, offset, scale); color_matrix_scale_components (&dst, (float) scale[0], (float) scale[1], (float) scale[2]); color_matrix_offset_components (&dst, offset[0], offset[1], offset[2]); /* because we're doing fixed point matrix coefficients */ color_matrix_scale_components (&dst, SCALE_F, SCALE_F, SCALE_F); for (i = 0; i < 4; i++) for (j = 0; j < 4; j++) convert->cmatrix[i][j] = rint (dst.m[i][j]); GST_DEBUG ("[%6d %6d %6d %6d]", convert->cmatrix[0][0], convert->cmatrix[0][1], convert->cmatrix[0][2], convert->cmatrix[0][3]); GST_DEBUG ("[%6d %6d %6d %6d]", convert->cmatrix[1][0], convert->cmatrix[1][1], convert->cmatrix[1][2], convert->cmatrix[1][3]); GST_DEBUG ("[%6d %6d %6d %6d]", convert->cmatrix[2][0], convert->cmatrix[2][1], convert->cmatrix[2][2], convert->cmatrix[2][3]); GST_DEBUG ("[%6d %6d %6d %6d]", convert->cmatrix[3][0], convert->cmatrix[3][1], convert->cmatrix[3][2], convert->cmatrix[3][3]); convert->orc_p1 = (((guint64) (guint16) convert->cmatrix[2][0]) << 48) | (((guint64) (guint16) convert->cmatrix[1][0]) << 32) | (((guint64) (guint16) convert->cmatrix[0][0]) << 16); convert->orc_p2 = (((guint64) (guint16) convert->cmatrix[2][1]) << 48) | (((guint64) (guint16) convert->cmatrix[1][1]) << 32) | (((guint64) (guint16) convert->cmatrix[0][1]) << 16); convert->orc_p3 = (((guint64) (guint16) convert->cmatrix[2][2]) << 48) | (((guint64) (guint16) convert->cmatrix[1][2]) << 32) | (((guint64) (guint16) convert->cmatrix[0][2]) << 16); } static void video_converter_compute_resample (GstVideoConverter * convert) { GstVideoInfo *in_info, *out_info; const GstVideoFormatInfo *sfinfo, *dfinfo; in_info = &convert->in_info; out_info = &convert->out_info; sfinfo = in_info->finfo; dfinfo = out_info->finfo; if (sfinfo->w_sub[2] != dfinfo->w_sub[2] || sfinfo->h_sub[2] != dfinfo->h_sub[2] || in_info->chroma_site != out_info->chroma_site || in_info->width != out_info->width || in_info->height != out_info->height) { GstVideoChromaFlags flags = (GST_VIDEO_INFO_IS_INTERLACED (in_info) ? GST_VIDEO_CHROMA_FLAG_INTERLACED : 0); convert->upsample = gst_video_chroma_resample_new (0, in_info->chroma_site, flags, sfinfo->unpack_format, sfinfo->w_sub[2], sfinfo->h_sub[2]); convert->downsample = gst_video_chroma_resample_new (0, out_info->chroma_site, flags, dfinfo->unpack_format, -dfinfo->w_sub[2], -dfinfo->h_sub[2]); } else { convert->upsample = NULL; convert->downsample = NULL; } if (convert->upsample) { gst_video_chroma_resample_get_info (convert->upsample, &convert->up_n_lines, &convert->up_offset); } else { convert->up_n_lines = 1; convert->up_offset = 0; } if (convert->downsample) { gst_video_chroma_resample_get_info (convert->downsample, &convert->down_n_lines, &convert->down_offset); } else { convert->down_n_lines = 1; convert->down_offset = 0; } GST_DEBUG ("upsample: %p, site: %d, offset %d, n_lines %d", convert->upsample, in_info->chroma_site, convert->up_offset, convert->up_n_lines); GST_DEBUG ("downsample: %p, site: %d, offset %d, n_lines %d", convert->downsample, out_info->chroma_site, convert->down_offset, convert->down_n_lines); } #define TO_16(x) (((x)<<8) | (x)) static void convert_to16 (gpointer line, gint width) { guint8 *line8 = line; guint16 *line16 = line; gint i; for (i = (width - 1) * 4; i >= 0; i--) line16[i] = TO_16 (line8[i]); } static void convert_to8 (gpointer line, gint width) { guint8 *line8 = line; guint16 *line16 = line; gint i; for (i = 0; i < width * 4; i++) line8[i] = line16[i] >> 8; } #define FRAME_GET_PLANE_STRIDE(frame, plane) \ GST_VIDEO_FRAME_PLANE_STRIDE (frame, plane) #define FRAME_GET_PLANE_LINE(frame, plane, line) \ (gpointer)(((guint8*)(GST_VIDEO_FRAME_PLANE_DATA (frame, plane))) + \ FRAME_GET_PLANE_STRIDE (frame, plane) * (line)) #define FRAME_GET_COMP_STRIDE(frame, comp) \ GST_VIDEO_FRAME_COMP_STRIDE (frame, comp) #define FRAME_GET_COMP_LINE(frame, comp, line) \ (gpointer)(((guint8*)(GST_VIDEO_FRAME_COMP_DATA (frame, comp))) + \ FRAME_GET_COMP_STRIDE (frame, comp) * (line)) #define FRAME_GET_STRIDE(frame) FRAME_GET_PLANE_STRIDE (frame, 0) #define FRAME_GET_LINE(frame,line) FRAME_GET_PLANE_LINE (frame, 0, line) #define FRAME_GET_Y_LINE(frame,line) FRAME_GET_COMP_LINE(frame, GST_VIDEO_COMP_Y, line) #define FRAME_GET_U_LINE(frame,line) FRAME_GET_COMP_LINE(frame, GST_VIDEO_COMP_U, line) #define FRAME_GET_V_LINE(frame,line) FRAME_GET_COMP_LINE(frame, GST_VIDEO_COMP_V, line) #define FRAME_GET_A_LINE(frame,line) FRAME_GET_COMP_LINE(frame, GST_VIDEO_COMP_A, line) #define FRAME_GET_Y_STRIDE(frame) FRAME_GET_COMP_STRIDE(frame, GST_VIDEO_COMP_Y) #define FRAME_GET_U_STRIDE(frame) FRAME_GET_COMP_STRIDE(frame, GST_VIDEO_COMP_U) #define FRAME_GET_V_STRIDE(frame) FRAME_GET_COMP_STRIDE(frame, GST_VIDEO_COMP_V) #define FRAME_GET_A_STRIDE(frame) FRAME_GET_COMP_STRIDE(frame, GST_VIDEO_COMP_A) #define UNPACK_FRAME(frame,dest,line,x,width) \ frame->info.finfo->unpack_func (frame->info.finfo, \ (GST_VIDEO_FRAME_IS_INTERLACED (frame) ? \ GST_VIDEO_PACK_FLAG_INTERLACED : \ GST_VIDEO_PACK_FLAG_NONE), \ dest, frame->data, frame->info.stride, x, \ line, width) #define PACK_FRAME(frame,src,line,width) \ frame->info.finfo->pack_func (frame->info.finfo, \ (GST_VIDEO_FRAME_IS_INTERLACED (frame) ? \ GST_VIDEO_PACK_FLAG_INTERLACED : \ GST_VIDEO_PACK_FLAG_NONE), \ src, 0, frame->data, frame->info.stride, \ frame->info.chroma_site, line, width); static gpointer get_dest_line (GstLineCache * cache, gint idx, gpointer user_data) { GstVideoConverter *convert = user_data; guint8 *line; gint pstride = convert->pack_pstride; gint out_x = convert->out_x; GST_DEBUG ("get dest line %d", idx); line = FRAME_GET_LINE (convert->dest, idx); if (convert->borderline) { gint r_border = (out_x + convert->out_width) * pstride; gint rb_width = convert->out_maxwidth * pstride - r_border; gint lb_width = out_x * pstride; memcpy (line, convert->borderline, lb_width); memcpy (line + r_border, convert->borderline, rb_width); } line += out_x * pstride; return line; } static gboolean do_unpack_lines (GstLineCache * cache, gint out_line, gint in_line, gpointer user_data) { GstVideoConverter *convert = user_data; gpointer tmpline; guint cline; cline = CLAMP (in_line + convert->in_y, 0, convert->in_maxheight - 1); if (cache->alloc_writable || !convert->identity_unpack) { tmpline = gst_line_cache_alloc_line (cache, out_line); GST_DEBUG ("unpack line %d (%u) %p", in_line, cline, tmpline); UNPACK_FRAME (convert->src, tmpline, cline, convert->in_x, convert->in_width); } else { GST_DEBUG ("get src line %d (%u)", in_line, cline); tmpline = FRAME_GET_LINE (convert->src, cline); } gst_line_cache_add_line (cache, in_line, tmpline); return TRUE; } static gboolean do_upsample_lines (GstLineCache * cache, gint out_line, gint in_line, gpointer user_data) { GstVideoConverter *convert = user_data; gpointer *lines; gint i, start_line, n_lines; n_lines = convert->up_n_lines; start_line = in_line; if (start_line < n_lines + convert->up_offset) start_line += convert->up_offset; /* get the lines needed for chroma upsample */ lines = gst_line_cache_get_lines (cache->prev, out_line, start_line, n_lines); GST_DEBUG ("doing upsample %d-%d", start_line, start_line + n_lines - 1); gst_video_chroma_resample (convert->upsample, lines, convert->in_width); for (i = 0; i < n_lines; i++) gst_line_cache_add_line (cache, start_line + i, lines[i]); return TRUE; } static gboolean do_hscale_lines (GstLineCache * cache, gint out_line, gint in_line, gpointer user_data) { GstVideoConverter *convert = user_data; gpointer *lines, destline; lines = gst_line_cache_get_lines (cache->prev, out_line, in_line, 1); destline = gst_line_cache_alloc_line (cache, out_line); GST_DEBUG ("hresample line %d", in_line); gst_video_scaler_horizontal (convert->h_scaler, convert->h_scale_format, lines[0], destline, 0, convert->out_width); gst_line_cache_add_line (cache, in_line, destline); return TRUE; } static gboolean do_vscale_lines (GstLineCache * cache, gint out_line, gint in_line, gpointer user_data) { GstVideoConverter *convert = user_data; gpointer *lines, destline; guint sline, n_lines; guint cline; cline = CLAMP (in_line, 0, convert->out_height - 1); gst_video_scaler_get_coeff (convert->v_scaler, cline, &sline, &n_lines); lines = gst_line_cache_get_lines (cache->prev, out_line, sline, n_lines); destline = gst_line_cache_alloc_line (cache, out_line); GST_DEBUG ("vresample line %d %d-%d", in_line, sline, sline + n_lines - 1); gst_video_scaler_vertical (convert->v_scaler, convert->v_scale_format, lines, destline, cline, convert->v_scale_width); gst_line_cache_add_line (cache, in_line, destline); return TRUE; } static gboolean do_convert_lines (GstLineCache * cache, gint out_line, gint in_line, gpointer user_data) { GstVideoConverter *convert = user_data; gpointer *lines; guint in_bits, out_bits; gint width; lines = gst_line_cache_get_lines (cache->prev, out_line, in_line, 1); in_bits = convert->in_bits; out_bits = convert->out_bits; width = MIN (convert->in_width, convert->out_width); GST_DEBUG ("convert line %d", in_line); if (out_bits == 16 || in_bits == 16) { /* FIXME, we can scale in the conversion matrix */ if (in_bits == 8) convert_to16 (lines[0], width); if (convert->matrix) convert->matrix (convert, lines[0]); if (convert->dither16) convert->dither16 (convert, lines[0], in_line); if (out_bits == 8) convert_to8 (lines[0], width); } else { if (convert->matrix) convert->matrix (convert, lines[0]); } gst_line_cache_add_line (cache, in_line, lines[0]); return TRUE; } static gboolean do_downsample_lines (GstLineCache * cache, gint out_line, gint in_line, gpointer user_data) { GstVideoConverter *convert = user_data; gpointer *lines; gint i, start_line, n_lines; n_lines = convert->down_n_lines; start_line = in_line; if (start_line < n_lines + convert->down_offset) start_line += convert->down_offset; /* get the lines needed for chroma downsample */ lines = gst_line_cache_get_lines (cache->prev, out_line, start_line, n_lines); GST_DEBUG ("downsample line %d %d-%d", in_line, start_line, start_line + n_lines - 1); gst_video_chroma_resample (convert->downsample, lines, convert->out_width); for (i = 0; i < n_lines; i++) gst_line_cache_add_line (cache, start_line + i, lines[i]); return TRUE; } static void video_converter_generic (GstVideoConverter * convert, const GstVideoFrame * src, GstVideoFrame * dest) { gint i; gint out_maxwidth, out_maxheight; gint out_x, out_y, out_height; gint pack_lines, pstride; gint lb_width; out_height = convert->out_height; out_maxwidth = convert->out_maxwidth; out_maxheight = convert->out_maxheight; out_x = convert->out_x; out_y = convert->out_y; convert->src = src; convert->dest = dest; pack_lines = convert->lines; /* only 1 for now */ pstride = convert->pack_pstride; lb_width = out_x * pstride; if (convert->borderline) { /* FIXME we should try to avoid PACK_FRAME */ for (i = 0; i < out_y; i++) PACK_FRAME (dest, convert->borderline, i, out_maxwidth); } for (i = 0; i < out_height; i += pack_lines) { gpointer *lines; /* load the lines needed to pack */ lines = gst_line_cache_get_lines (convert->pack_lines, i + out_y, i, pack_lines); if (!convert->identity_pack) { /* take away the border */ guint8 *l = ((guint8 *) lines[0]) - lb_width; /* and pack into destination */ GST_DEBUG ("pack line %d", i + out_y); PACK_FRAME (dest, l, i + out_y, out_maxwidth); } } if (convert->borderline) { for (i = out_y + out_height; i < out_maxheight; i++) PACK_FRAME (dest, convert->borderline, i, out_maxwidth); } } /* Fast paths */ #define GET_LINE_OFFSETS(interlaced,line,l1,l2) \ if (interlaced) { \ l1 = (line & 2 ? line - 1 : line); \ l2 = l1 + 2; \ } else { \ l1 = line; \ l2 = l1 + 1; \ } static void convert_I420_YUY2 (GstVideoConverter * convert, const GstVideoFrame * src, GstVideoFrame * dest) { int i; gint width = convert->in_width; gint height = convert->in_height; gboolean interlaced = GST_VIDEO_FRAME_IS_INTERLACED (src); gint l1, l2; for (i = 0; i < GST_ROUND_DOWN_2 (height); i += 2) { GET_LINE_OFFSETS (interlaced, i, l1, l2); video_orc_convert_I420_YUY2 (FRAME_GET_LINE (dest, l1), FRAME_GET_LINE (dest, l2), FRAME_GET_Y_LINE (src, l1), FRAME_GET_Y_LINE (src, l2), FRAME_GET_U_LINE (src, i >> 1), FRAME_GET_V_LINE (src, i >> 1), (width + 1) / 2); } /* now handle last line */ if (height & 1) { UNPACK_FRAME (src, convert->tmplines[0], height - 1, convert->in_x, width); PACK_FRAME (dest, convert->tmplines[0], height - 1, width); } } static void convert_I420_UYVY (GstVideoConverter * convert, const GstVideoFrame * src, GstVideoFrame * dest) { int i; gint width = convert->in_width; gint height = convert->in_height; gboolean interlaced = GST_VIDEO_FRAME_IS_INTERLACED (src); gint l1, l2; for (i = 0; i < GST_ROUND_DOWN_2 (height); i += 2) { GET_LINE_OFFSETS (interlaced, i, l1, l2); video_orc_convert_I420_UYVY (FRAME_GET_LINE (dest, l1), FRAME_GET_LINE (dest, l2), FRAME_GET_Y_LINE (src, l1), FRAME_GET_Y_LINE (src, l2), FRAME_GET_U_LINE (src, i >> 1), FRAME_GET_V_LINE (src, i >> 1), (width + 1) / 2); } /* now handle last line */ if (height & 1) { UNPACK_FRAME (src, convert->tmplines[0], height - 1, convert->in_x, width); PACK_FRAME (dest, convert->tmplines[0], height - 1, width); } } static void convert_I420_AYUV (GstVideoConverter * convert, const GstVideoFrame * src, GstVideoFrame * dest) { int i; gint width = convert->in_width; gint height = convert->in_height; gboolean interlaced = GST_VIDEO_FRAME_IS_INTERLACED (src); gint l1, l2; for (i = 0; i < GST_ROUND_DOWN_2 (height); i += 2) { GET_LINE_OFFSETS (interlaced, i, l1, l2); video_orc_convert_I420_AYUV (FRAME_GET_LINE (dest, l1), FRAME_GET_LINE (dest, l2), FRAME_GET_Y_LINE (src, l1), FRAME_GET_Y_LINE (src, l2), FRAME_GET_U_LINE (src, i >> 1), FRAME_GET_V_LINE (src, i >> 1), width); } /* now handle last line */ if (height & 1) { UNPACK_FRAME (src, convert->tmplines[0], height - 1, convert->in_x, width); PACK_FRAME (dest, convert->tmplines[0], height - 1, width); } } static void convert_I420_Y42B (GstVideoConverter * convert, const GstVideoFrame * src, GstVideoFrame * dest) { gint width = convert->in_width; gint height = convert->in_height; video_orc_memcpy_2d (FRAME_GET_Y_LINE (dest, 0), FRAME_GET_Y_STRIDE (dest), FRAME_GET_Y_LINE (src, 0), FRAME_GET_Y_STRIDE (src), width, height); video_orc_planar_chroma_420_422 (FRAME_GET_U_LINE (dest, 0), 2 * FRAME_GET_U_STRIDE (dest), FRAME_GET_U_LINE (dest, 1), 2 * FRAME_GET_U_STRIDE (dest), FRAME_GET_U_LINE (src, 0), FRAME_GET_U_STRIDE (src), (width + 1) / 2, height / 2); video_orc_planar_chroma_420_422 (FRAME_GET_V_LINE (dest, 0), 2 * FRAME_GET_V_STRIDE (dest), FRAME_GET_V_LINE (dest, 1), 2 * FRAME_GET_V_STRIDE (dest), FRAME_GET_V_LINE (src, 0), FRAME_GET_V_STRIDE (src), (width + 1) / 2, height / 2); } static void convert_I420_Y444 (GstVideoConverter * convert, const GstVideoFrame * src, GstVideoFrame * dest) { gint width = convert->in_width; gint height = convert->in_height; video_orc_memcpy_2d (FRAME_GET_Y_LINE (dest, 0), FRAME_GET_Y_STRIDE (dest), FRAME_GET_Y_LINE (src, 0), FRAME_GET_Y_STRIDE (src), width, height); video_orc_planar_chroma_420_444 (FRAME_GET_U_LINE (dest, 0), 2 * FRAME_GET_U_STRIDE (dest), FRAME_GET_U_LINE (dest, 1), 2 * FRAME_GET_U_STRIDE (dest), FRAME_GET_U_LINE (src, 0), FRAME_GET_U_STRIDE (src), (width + 1) / 2, height / 2); video_orc_planar_chroma_420_444 (FRAME_GET_V_LINE (dest, 0), 2 * FRAME_GET_V_STRIDE (dest), FRAME_GET_V_LINE (dest, 1), 2 * FRAME_GET_V_STRIDE (dest), FRAME_GET_V_LINE (src, 0), FRAME_GET_V_STRIDE (src), (width + 1) / 2, height / 2); /* now handle last line */ if (height & 1) { UNPACK_FRAME (src, convert->tmplines[0], height - 1, convert->in_x, width); PACK_FRAME (dest, convert->tmplines[0], height - 1, width); } } static void convert_YUY2_I420 (GstVideoConverter * convert, const GstVideoFrame * src, GstVideoFrame * dest) { int i; gint width = convert->in_width; gint height = convert->in_height; gboolean interlaced = GST_VIDEO_FRAME_IS_INTERLACED (src); gint l1, l2; for (i = 0; i < GST_ROUND_DOWN_2 (height); i += 2) { GET_LINE_OFFSETS (interlaced, i, l1, l2); video_orc_convert_YUY2_I420 (FRAME_GET_Y_LINE (dest, l1), FRAME_GET_Y_LINE (dest, l2), FRAME_GET_U_LINE (dest, i >> 1), FRAME_GET_V_LINE (dest, i >> 1), FRAME_GET_LINE (src, l1), FRAME_GET_LINE (src, l2), (width + 1) / 2); } /* now handle last line */ if (height & 1) { UNPACK_FRAME (src, convert->tmplines[0], height - 1, convert->in_x, width); PACK_FRAME (dest, convert->tmplines[0], height - 1, width); } } static void convert_YUY2_AYUV (GstVideoConverter * convert, const GstVideoFrame * src, GstVideoFrame * dest) { gint width = convert->in_width; gint height = convert->in_height; video_orc_convert_YUY2_AYUV (FRAME_GET_LINE (dest, 0), FRAME_GET_STRIDE (dest), FRAME_GET_LINE (src, 0), FRAME_GET_STRIDE (src), (width + 1) / 2, height); } static void convert_YUY2_Y42B (GstVideoConverter * convert, const GstVideoFrame * src, GstVideoFrame * dest) { gint width = convert->in_width; gint height = convert->in_height; video_orc_convert_YUY2_Y42B (FRAME_GET_Y_LINE (dest, 0), FRAME_GET_Y_STRIDE (dest), FRAME_GET_U_LINE (dest, 0), FRAME_GET_U_STRIDE (dest), FRAME_GET_V_LINE (dest, 0), FRAME_GET_V_STRIDE (dest), FRAME_GET_LINE (src, 0), FRAME_GET_STRIDE (src), (width + 1) / 2, height); } static void convert_YUY2_Y444 (GstVideoConverter * convert, const GstVideoFrame * src, GstVideoFrame * dest) { gint width = convert->in_width; gint height = convert->in_height; video_orc_convert_YUY2_Y444 (FRAME_GET_COMP_LINE (dest, 0, 0), FRAME_GET_COMP_STRIDE (dest, 0), FRAME_GET_COMP_LINE (dest, 1, 0), FRAME_GET_COMP_STRIDE (dest, 1), FRAME_GET_COMP_LINE (dest, 2, 0), FRAME_GET_COMP_STRIDE (dest, 2), FRAME_GET_LINE (src, 0), FRAME_GET_STRIDE (src), (width + 1) / 2, height); } static void convert_UYVY_I420 (GstVideoConverter * convert, const GstVideoFrame * src, GstVideoFrame * dest) { int i; gint width = convert->in_width; gint height = convert->in_height; gboolean interlaced = GST_VIDEO_FRAME_IS_INTERLACED (src); gint l1, l2; for (i = 0; i < GST_ROUND_DOWN_2 (height); i += 2) { GET_LINE_OFFSETS (interlaced, i, l1, l2); video_orc_convert_UYVY_I420 (FRAME_GET_COMP_LINE (dest, 0, l1), FRAME_GET_COMP_LINE (dest, 0, l2), FRAME_GET_COMP_LINE (dest, 1, i >> 1), FRAME_GET_COMP_LINE (dest, 2, i >> 1), FRAME_GET_LINE (src, l1), FRAME_GET_LINE (src, l2), (width + 1) / 2); } /* now handle last line */ if (height & 1) { UNPACK_FRAME (src, convert->tmplines[0], height - 1, convert->in_x, width); PACK_FRAME (dest, convert->tmplines[0], height - 1, width); } } static void convert_UYVY_AYUV (GstVideoConverter * convert, const GstVideoFrame * src, GstVideoFrame * dest) { gint width = convert->in_width; gint height = convert->in_height; video_orc_convert_UYVY_AYUV (FRAME_GET_LINE (dest, 0), FRAME_GET_STRIDE (dest), FRAME_GET_LINE (src, 0), FRAME_GET_STRIDE (src), (width + 1) / 2, height); } static void convert_UYVY_YUY2 (GstVideoConverter * convert, const GstVideoFrame * src, GstVideoFrame * dest) { gint width = convert->in_width; gint height = convert->in_height; video_orc_convert_UYVY_YUY2 (FRAME_GET_LINE (dest, 0), FRAME_GET_STRIDE (dest), FRAME_GET_LINE (src, 0), FRAME_GET_STRIDE (src), (width + 1) / 2, height); } static void convert_UYVY_Y42B (GstVideoConverter * convert, const GstVideoFrame * src, GstVideoFrame * dest) { gint width = convert->in_width; gint height = convert->in_height; video_orc_convert_UYVY_Y42B (FRAME_GET_Y_LINE (dest, 0), FRAME_GET_Y_STRIDE (dest), FRAME_GET_U_LINE (dest, 0), FRAME_GET_U_STRIDE (dest), FRAME_GET_V_LINE (dest, 0), FRAME_GET_V_STRIDE (dest), FRAME_GET_LINE (src, 0), FRAME_GET_STRIDE (src), (width + 1) / 2, height); } static void convert_UYVY_Y444 (GstVideoConverter * convert, const GstVideoFrame * src, GstVideoFrame * dest) { gint width = convert->in_width; gint height = convert->in_height; video_orc_convert_UYVY_Y444 (FRAME_GET_Y_LINE (dest, 0), FRAME_GET_Y_STRIDE (dest), FRAME_GET_U_LINE (dest, 0), FRAME_GET_U_STRIDE (dest), FRAME_GET_V_LINE (dest, 0), FRAME_GET_V_STRIDE (dest), FRAME_GET_LINE (src, 0), FRAME_GET_STRIDE (src), (width + 1) / 2, height); } static void convert_AYUV_I420 (GstVideoConverter * convert, const GstVideoFrame * src, GstVideoFrame * dest) { gint width = convert->in_width; gint height = convert->in_height; /* only for even width/height */ video_orc_convert_AYUV_I420 (FRAME_GET_Y_LINE (dest, 0), 2 * FRAME_GET_Y_STRIDE (dest), FRAME_GET_Y_LINE (dest, 1), 2 * FRAME_GET_Y_STRIDE (dest), FRAME_GET_U_LINE (dest, 0), FRAME_GET_U_STRIDE (dest), FRAME_GET_V_LINE (dest, 0), FRAME_GET_V_STRIDE (dest), FRAME_GET_LINE (src, 0), 2 * FRAME_GET_STRIDE (src), FRAME_GET_LINE (src, 1), 2 * FRAME_GET_STRIDE (src), width / 2, height / 2); } static void convert_AYUV_YUY2 (GstVideoConverter * convert, const GstVideoFrame * src, GstVideoFrame * dest) { gint width = convert->in_width; gint height = convert->in_height; /* only for even width */ video_orc_convert_AYUV_YUY2 (FRAME_GET_LINE (dest, 0), FRAME_GET_STRIDE (dest), FRAME_GET_LINE (src, 0), FRAME_GET_STRIDE (src), width / 2, height); } static void convert_AYUV_UYVY (GstVideoConverter * convert, const GstVideoFrame * src, GstVideoFrame * dest) { gint width = convert->in_width; gint height = convert->in_height; /* only for even width */ video_orc_convert_AYUV_UYVY (FRAME_GET_LINE (dest, 0), FRAME_GET_STRIDE (dest), FRAME_GET_LINE (src, 0), FRAME_GET_STRIDE (src), width / 2, height); } static void convert_AYUV_Y42B (GstVideoConverter * convert, const GstVideoFrame * src, GstVideoFrame * dest) { gint width = convert->in_width; gint height = convert->in_height; /* only works for even width */ video_orc_convert_AYUV_Y42B (FRAME_GET_Y_LINE (dest, 0), FRAME_GET_Y_STRIDE (dest), FRAME_GET_U_LINE (dest, 0), FRAME_GET_U_STRIDE (dest), FRAME_GET_V_LINE (dest, 0), FRAME_GET_V_STRIDE (dest), FRAME_GET_LINE (src, 0), FRAME_GET_STRIDE (src), width / 2, height); } static void convert_AYUV_Y444 (GstVideoConverter * convert, const GstVideoFrame * src, GstVideoFrame * dest) { gint width = convert->in_width; gint height = convert->in_height; video_orc_convert_AYUV_Y444 (FRAME_GET_Y_LINE (dest, 0), FRAME_GET_Y_STRIDE (dest), FRAME_GET_U_LINE (dest, 0), FRAME_GET_U_STRIDE (dest), FRAME_GET_V_LINE (dest, 0), FRAME_GET_V_STRIDE (dest), FRAME_GET_LINE (src, 0), FRAME_GET_STRIDE (src), width, height); } static void convert_Y42B_I420 (GstVideoConverter * convert, const GstVideoFrame * src, GstVideoFrame * dest) { gint width = convert->in_width; gint height = convert->in_height; video_orc_memcpy_2d (FRAME_GET_Y_LINE (dest, 0), FRAME_GET_Y_STRIDE (dest), FRAME_GET_Y_LINE (src, 0), FRAME_GET_Y_STRIDE (src), width, height); video_orc_planar_chroma_422_420 (FRAME_GET_U_LINE (dest, 0), FRAME_GET_U_STRIDE (dest), FRAME_GET_U_LINE (src, 0), 2 * FRAME_GET_U_STRIDE (src), FRAME_GET_U_LINE (src, 1), 2 * FRAME_GET_U_STRIDE (src), (width + 1) / 2, height / 2); video_orc_planar_chroma_422_420 (FRAME_GET_V_LINE (dest, 0), FRAME_GET_V_STRIDE (dest), FRAME_GET_V_LINE (src, 0), 2 * FRAME_GET_V_STRIDE (src), FRAME_GET_V_LINE (src, 1), 2 * FRAME_GET_V_STRIDE (src), (width + 1) / 2, height / 2); /* now handle last line */ if (height & 1) { UNPACK_FRAME (src, convert->tmplines[0], height - 1, convert->in_x, width); PACK_FRAME (dest, convert->tmplines[0], height - 1, width); } } static void convert_Y42B_Y444 (GstVideoConverter * convert, const GstVideoFrame * src, GstVideoFrame * dest) { gint width = convert->in_width; gint height = convert->in_height; video_orc_memcpy_2d (FRAME_GET_Y_LINE (dest, 0), FRAME_GET_Y_STRIDE (dest), FRAME_GET_Y_LINE (src, 0), FRAME_GET_Y_STRIDE (src), width, height); video_orc_planar_chroma_422_444 (FRAME_GET_U_LINE (dest, 0), FRAME_GET_U_STRIDE (dest), FRAME_GET_U_LINE (src, 0), FRAME_GET_U_STRIDE (src), (width + 1) / 2, height); video_orc_planar_chroma_422_444 (FRAME_GET_V_LINE (dest, 0), FRAME_GET_V_STRIDE (dest), FRAME_GET_V_LINE (src, 0), FRAME_GET_V_STRIDE (src), (width + 1) / 2, height); } static void convert_Y42B_YUY2 (GstVideoConverter * convert, const GstVideoFrame * src, GstVideoFrame * dest) { gint width = convert->in_width; gint height = convert->in_height; video_orc_convert_Y42B_YUY2 (FRAME_GET_LINE (dest, 0), FRAME_GET_STRIDE (dest), FRAME_GET_Y_LINE (src, 0), FRAME_GET_Y_STRIDE (src), FRAME_GET_U_LINE (src, 0), FRAME_GET_U_STRIDE (src), FRAME_GET_V_LINE (src, 0), FRAME_GET_V_STRIDE (src), (width + 1) / 2, height); } static void convert_Y42B_UYVY (GstVideoConverter * convert, const GstVideoFrame * src, GstVideoFrame * dest) { gint width = convert->in_width; gint height = convert->in_height; video_orc_convert_Y42B_UYVY (FRAME_GET_LINE (dest, 0), FRAME_GET_STRIDE (dest), FRAME_GET_Y_LINE (src, 0), FRAME_GET_Y_STRIDE (src), FRAME_GET_U_LINE (src, 0), FRAME_GET_U_STRIDE (src), FRAME_GET_V_LINE (src, 0), FRAME_GET_V_STRIDE (src), (width + 1) / 2, height); } static void convert_Y42B_AYUV (GstVideoConverter * convert, const GstVideoFrame * src, GstVideoFrame * dest) { gint width = convert->in_width; gint height = convert->in_height; /* only for even width */ video_orc_convert_Y42B_AYUV (FRAME_GET_LINE (dest, 0), FRAME_GET_STRIDE (dest), FRAME_GET_Y_LINE (src, 0), FRAME_GET_Y_STRIDE (src), FRAME_GET_U_LINE (src, 0), FRAME_GET_U_STRIDE (src), FRAME_GET_V_LINE (src, 0), FRAME_GET_V_STRIDE (src), width / 2, height); } static void convert_Y444_I420 (GstVideoConverter * convert, const GstVideoFrame * src, GstVideoFrame * dest) { gint width = convert->in_width; gint height = convert->in_height; video_orc_memcpy_2d (FRAME_GET_Y_LINE (dest, 0), FRAME_GET_Y_STRIDE (dest), FRAME_GET_Y_LINE (src, 0), FRAME_GET_Y_STRIDE (src), width, height); video_orc_planar_chroma_444_420 (FRAME_GET_U_LINE (dest, 0), FRAME_GET_U_STRIDE (dest), FRAME_GET_U_LINE (src, 0), 2 * FRAME_GET_U_STRIDE (src), FRAME_GET_U_LINE (src, 1), 2 * FRAME_GET_U_STRIDE (src), width / 2, height / 2); video_orc_planar_chroma_444_420 (FRAME_GET_V_LINE (dest, 0), FRAME_GET_V_STRIDE (dest), FRAME_GET_V_LINE (src, 0), 2 * FRAME_GET_V_STRIDE (src), FRAME_GET_V_LINE (src, 1), 2 * FRAME_GET_V_STRIDE (src), width / 2, height / 2); /* now handle last line */ if (height & 1) { UNPACK_FRAME (src, convert->tmplines[0], height - 1, convert->in_x, width); PACK_FRAME (dest, convert->tmplines[0], height - 1, width); } } static void convert_Y444_Y42B (GstVideoConverter * convert, const GstVideoFrame * src, GstVideoFrame * dest) { gint width = convert->in_width; gint height = convert->in_height; video_orc_memcpy_2d (FRAME_GET_Y_LINE (dest, 0), FRAME_GET_Y_STRIDE (dest), FRAME_GET_Y_LINE (src, 0), FRAME_GET_Y_STRIDE (src), width, height); video_orc_planar_chroma_444_422 (FRAME_GET_U_LINE (dest, 0), FRAME_GET_U_STRIDE (dest), FRAME_GET_U_LINE (src, 0), FRAME_GET_U_STRIDE (src), width / 2, height); video_orc_planar_chroma_444_422 (FRAME_GET_V_LINE (dest, 0), FRAME_GET_V_STRIDE (dest), FRAME_GET_V_LINE (src, 0), FRAME_GET_V_STRIDE (src), width / 2, height); } static void convert_Y444_YUY2 (GstVideoConverter * convert, const GstVideoFrame * src, GstVideoFrame * dest) { gint width = convert->in_width; gint height = convert->in_height; video_orc_convert_Y444_YUY2 (FRAME_GET_LINE (dest, 0), FRAME_GET_STRIDE (dest), FRAME_GET_Y_LINE (src, 0), FRAME_GET_Y_STRIDE (src), FRAME_GET_U_LINE (src, 0), FRAME_GET_U_STRIDE (src), FRAME_GET_V_LINE (src, 0), FRAME_GET_V_STRIDE (src), width / 2, height); } static void convert_Y444_UYVY (GstVideoConverter * convert, const GstVideoFrame * src, GstVideoFrame * dest) { gint width = convert->in_width; gint height = convert->in_height; video_orc_convert_Y444_UYVY (FRAME_GET_LINE (dest, 0), FRAME_GET_STRIDE (dest), FRAME_GET_Y_LINE (src, 0), FRAME_GET_Y_STRIDE (src), FRAME_GET_U_LINE (src, 0), FRAME_GET_U_STRIDE (src), FRAME_GET_V_LINE (src, 0), FRAME_GET_V_STRIDE (src), width / 2, height); } static void convert_Y444_AYUV (GstVideoConverter * convert, const GstVideoFrame * src, GstVideoFrame * dest) { gint width = convert->in_width; gint height = convert->in_height; video_orc_convert_Y444_AYUV (FRAME_GET_LINE (dest, 0), FRAME_GET_STRIDE (dest), FRAME_GET_Y_LINE (src, 0), FRAME_GET_Y_STRIDE (src), FRAME_GET_U_LINE (src, 0), FRAME_GET_U_STRIDE (src), FRAME_GET_V_LINE (src, 0), FRAME_GET_V_STRIDE (src), width, height); } #if G_BYTE_ORDER == G_LITTLE_ENDIAN static void convert_AYUV_ARGB (GstVideoConverter * convert, const GstVideoFrame * src, GstVideoFrame * dest) { gint width = convert->in_width; gint height = convert->in_height; video_orc_convert_AYUV_ARGB (FRAME_GET_LINE (dest, 0), FRAME_GET_STRIDE (dest), FRAME_GET_LINE (src, 0), FRAME_GET_STRIDE (src), convert->cmatrix[0][0], convert->cmatrix[0][2], convert->cmatrix[2][1], convert->cmatrix[1][1], convert->cmatrix[1][2], width, height); } static void convert_AYUV_BGRA (GstVideoConverter * convert, const GstVideoFrame * src, GstVideoFrame * dest) { gint width = convert->in_width; gint height = convert->in_height; video_orc_convert_AYUV_BGRA (FRAME_GET_LINE (dest, 0), FRAME_GET_STRIDE (dest), FRAME_GET_LINE (src, 0), FRAME_GET_STRIDE (src), convert->cmatrix[0][0], convert->cmatrix[0][2], convert->cmatrix[2][1], convert->cmatrix[1][1], convert->cmatrix[1][2], width, height); } static void convert_AYUV_ABGR (GstVideoConverter * convert, const GstVideoFrame * src, GstVideoFrame * dest) { gint width = convert->in_width; gint height = convert->in_height; video_orc_convert_AYUV_ABGR (FRAME_GET_LINE (dest, 0), FRAME_GET_STRIDE (dest), FRAME_GET_LINE (src, 0), FRAME_GET_STRIDE (src), convert->cmatrix[0][0], convert->cmatrix[0][2], convert->cmatrix[2][1], convert->cmatrix[1][1], convert->cmatrix[1][2], width, height); } static void convert_AYUV_RGBA (GstVideoConverter * convert, const GstVideoFrame * src, GstVideoFrame * dest) { gint width = convert->in_width; gint height = convert->in_height; video_orc_convert_AYUV_RGBA (FRAME_GET_LINE (dest, 0), FRAME_GET_STRIDE (dest), FRAME_GET_LINE (src, 0), FRAME_GET_STRIDE (src), convert->cmatrix[0][0], convert->cmatrix[0][2], convert->cmatrix[2][1], convert->cmatrix[1][1], convert->cmatrix[1][2], width, height); } static void convert_I420_BGRA (GstVideoConverter * convert, const GstVideoFrame * src, GstVideoFrame * dest) { int i; gint width = convert->in_width; gint height = convert->in_height; for (i = 0; i < height; i++) { video_orc_convert_I420_BGRA (FRAME_GET_LINE (dest, i), FRAME_GET_Y_LINE (src, i), FRAME_GET_U_LINE (src, i >> 1), FRAME_GET_V_LINE (src, i >> 1), convert->cmatrix[0][0], convert->cmatrix[0][2], convert->cmatrix[2][1], convert->cmatrix[1][1], convert->cmatrix[1][2], width); } } #endif /* Fast paths */ typedef struct { GstVideoFormat in_format; GstVideoColorMatrix in_matrix; GstVideoFormat out_format; GstVideoColorMatrix out_matrix; gboolean keeps_color_matrix; gboolean keeps_interlaced; gboolean needs_color_matrix; gint width_align, height_align; void (*convert) (GstVideoConverter * convert, const GstVideoFrame * src, GstVideoFrame * dest); } VideoTransform; static const VideoTransform transforms[] = { {GST_VIDEO_FORMAT_I420, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_YUY2, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, TRUE, FALSE, 0, 0, convert_I420_YUY2}, {GST_VIDEO_FORMAT_I420, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_UYVY, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, TRUE, FALSE, 0, 0, convert_I420_UYVY}, {GST_VIDEO_FORMAT_I420, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_AYUV, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, TRUE, FALSE, 0, 0, convert_I420_AYUV}, {GST_VIDEO_FORMAT_I420, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_Y42B, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, FALSE, FALSE, 0, 0, convert_I420_Y42B}, {GST_VIDEO_FORMAT_I420, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_Y444, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, FALSE, FALSE, 0, 0, convert_I420_Y444}, {GST_VIDEO_FORMAT_YV12, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_YUY2, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, TRUE, FALSE, 0, 0, convert_I420_YUY2}, {GST_VIDEO_FORMAT_YV12, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_UYVY, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, TRUE, FALSE, 0, 0, convert_I420_UYVY}, {GST_VIDEO_FORMAT_YV12, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_AYUV, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, TRUE, FALSE, 0, 0, convert_I420_AYUV}, {GST_VIDEO_FORMAT_YV12, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_Y42B, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, FALSE, FALSE, 0, 0, convert_I420_Y42B}, {GST_VIDEO_FORMAT_YV12, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_Y444, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, FALSE, FALSE, 0, 0, convert_I420_Y444}, {GST_VIDEO_FORMAT_YUY2, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_I420, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, TRUE, FALSE, 0, 0, convert_YUY2_I420}, {GST_VIDEO_FORMAT_YUY2, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_YV12, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, TRUE, FALSE, 0, 0, convert_YUY2_I420}, {GST_VIDEO_FORMAT_YUY2, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_UYVY, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, TRUE, FALSE, 0, 0, convert_UYVY_YUY2}, /* alias */ {GST_VIDEO_FORMAT_YUY2, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_AYUV, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, TRUE, FALSE, 0, 0, convert_YUY2_AYUV}, {GST_VIDEO_FORMAT_YUY2, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_Y42B, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, TRUE, FALSE, 0, 0, convert_YUY2_Y42B}, {GST_VIDEO_FORMAT_YUY2, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_Y444, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, TRUE, FALSE, 0, 0, convert_YUY2_Y444}, {GST_VIDEO_FORMAT_UYVY, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_I420, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, TRUE, FALSE, 0, 0, convert_UYVY_I420}, {GST_VIDEO_FORMAT_UYVY, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_YV12, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, TRUE, FALSE, 0, 0, convert_UYVY_I420}, {GST_VIDEO_FORMAT_UYVY, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_YUY2, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, TRUE, FALSE, 0, 0, convert_UYVY_YUY2}, {GST_VIDEO_FORMAT_UYVY, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_AYUV, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, TRUE, FALSE, 0, 0, convert_UYVY_AYUV}, {GST_VIDEO_FORMAT_UYVY, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_Y42B, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, TRUE, FALSE, 0, 0, convert_UYVY_Y42B}, {GST_VIDEO_FORMAT_UYVY, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_Y444, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, TRUE, FALSE, 0, 0, convert_UYVY_Y444}, {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_I420, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, FALSE, FALSE, 1, 1, convert_AYUV_I420}, {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_YV12, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, FALSE, FALSE, 1, 1, convert_AYUV_I420}, {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_YUY2, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, TRUE, FALSE, 1, 0, convert_AYUV_YUY2}, {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_UYVY, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, TRUE, FALSE, 1, 0, convert_AYUV_UYVY}, {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_Y42B, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, TRUE, FALSE, 1, 0, convert_AYUV_Y42B}, {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_Y444, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, TRUE, FALSE, 0, 0, convert_AYUV_Y444}, {GST_VIDEO_FORMAT_Y42B, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_I420, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, FALSE, FALSE, 0, 0, convert_Y42B_I420}, {GST_VIDEO_FORMAT_Y42B, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_YV12, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, FALSE, FALSE, 0, 0, convert_Y42B_I420}, {GST_VIDEO_FORMAT_Y42B, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_YUY2, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, TRUE, FALSE, 0, 0, convert_Y42B_YUY2}, {GST_VIDEO_FORMAT_Y42B, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_UYVY, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, TRUE, FALSE, 0, 0, convert_Y42B_UYVY}, {GST_VIDEO_FORMAT_Y42B, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_AYUV, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, TRUE, FALSE, 1, 0, convert_Y42B_AYUV}, {GST_VIDEO_FORMAT_Y42B, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_Y444, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, TRUE, FALSE, 0, 0, convert_Y42B_Y444}, {GST_VIDEO_FORMAT_Y444, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_I420, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, FALSE, FALSE, 1, 0, convert_Y444_I420}, {GST_VIDEO_FORMAT_Y444, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_YV12, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, FALSE, FALSE, 1, 0, convert_Y444_I420}, {GST_VIDEO_FORMAT_Y444, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_YUY2, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, TRUE, FALSE, 1, 0, convert_Y444_YUY2}, {GST_VIDEO_FORMAT_Y444, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_UYVY, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, TRUE, FALSE, 1, 0, convert_Y444_UYVY}, {GST_VIDEO_FORMAT_Y444, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_AYUV, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, TRUE, FALSE, 0, 0, convert_Y444_AYUV}, {GST_VIDEO_FORMAT_Y444, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_Y42B, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, TRUE, FALSE, 1, 0, convert_Y444_Y42B}, #if G_BYTE_ORDER == G_LITTLE_ENDIAN {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_ARGB, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, TRUE, TRUE, 0, 0, convert_AYUV_ARGB}, {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_BGRA, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, TRUE, TRUE, 0, 0, convert_AYUV_BGRA}, {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_xRGB, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, TRUE, TRUE, 0, 0, convert_AYUV_ARGB}, /* alias */ {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_BGRx, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, TRUE, TRUE, 0, 0, convert_AYUV_BGRA}, /* alias */ {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_ABGR, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, TRUE, TRUE, 0, 0, convert_AYUV_ABGR}, {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_RGBA, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, TRUE, TRUE, 0, 0, convert_AYUV_RGBA}, {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_xBGR, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, TRUE, TRUE, 0, 0, convert_AYUV_ABGR}, /* alias */ {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_RGBx, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, TRUE, TRUE, 0, 0, convert_AYUV_RGBA}, /* alias */ {GST_VIDEO_FORMAT_I420, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_BGRA, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, FALSE, TRUE, 0, 0, convert_I420_BGRA}, {GST_VIDEO_FORMAT_I420, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_BGRx, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, FALSE, TRUE, 0, 0, convert_I420_BGRA}, {GST_VIDEO_FORMAT_YV12, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_BGRA, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, FALSE, TRUE, 0, 0, convert_I420_BGRA}, {GST_VIDEO_FORMAT_YV12, GST_VIDEO_COLOR_MATRIX_UNKNOWN, GST_VIDEO_FORMAT_BGRx, GST_VIDEO_COLOR_MATRIX_UNKNOWN, TRUE, FALSE, TRUE, 0, 0, convert_I420_BGRA}, #endif }; static gboolean video_converter_lookup_fastpath (GstVideoConverter * convert) { int i; GstVideoFormat in_format, out_format; GstVideoColorMatrix in_matrix, out_matrix; gboolean interlaced; gint width, height; width = GST_VIDEO_INFO_WIDTH (&convert->in_info); height = GST_VIDEO_INFO_HEIGHT (&convert->in_info); if (width != convert->out_width || height != convert->out_height) return FALSE; in_format = GST_VIDEO_INFO_FORMAT (&convert->in_info); out_format = GST_VIDEO_INFO_FORMAT (&convert->out_info); in_matrix = convert->in_info.colorimetry.matrix; out_matrix = convert->out_info.colorimetry.matrix; interlaced = GST_VIDEO_INFO_IS_INTERLACED (&convert->in_info); interlaced |= GST_VIDEO_INFO_IS_INTERLACED (&convert->out_info); for (i = 0; i < sizeof (transforms) / sizeof (transforms[0]); i++) { if (transforms[i].in_format == in_format && transforms[i].out_format == out_format && (transforms[i].keeps_color_matrix || (transforms[i].in_matrix == in_matrix && transforms[i].out_matrix == out_matrix)) && (transforms[i].keeps_interlaced || !interlaced) && (transforms[i].width_align & width) == 0 && (transforms[i].height_align & height) == 0) { GST_DEBUG ("using fastpath"); if (transforms[i].needs_color_matrix) video_converter_compute_matrix (convert); convert->convert = transforms[i].convert; alloc_tmplines (convert, 1, 0, GST_VIDEO_INFO_WIDTH (&convert->in_info)); return TRUE; } } GST_DEBUG ("no fastpath found"); return FALSE; }