• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) 2010 David Schleef <ds@schleef.org>
3  * Copyright (C) 2010 Sebastian Dröge <sebastian.droege@collabora.co.uk>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #if 0
26 #ifdef HAVE_PTHREAD
27 #define _GNU_SOURCE
28 #include <pthread.h>
29 #endif
30 #endif
31 
32 #include "video-converter.h"
33 
34 #include <glib.h>
35 #include <string.h>
36 #include <math.h>
37 #include <gst/base/base.h>
38 
39 #include "video-orc.h"
40 
41 /**
42  * SECTION:videoconverter
43  * @title: GstVideoConverter
44  * @short_description: Generic video conversion
45  *
46  * This object is used to convert video frames from one format to another.
47  * The object can perform conversion of:
48  *
49  *  * video format
50  *  * video colorspace
51  *  * chroma-siting
52  *  * video size
53  *
54  */
55 
56 /*
57  * (a)  unpack
58  * (b)  chroma upsample
59  * (c)  (convert Y'CbCr to R'G'B')
60  * (d)  gamma decode
61  * (e)  downscale
62  * (f)  colorspace convert through XYZ
63  * (g)  upscale
64  * (h)  gamma encode
65  * (i)  (convert R'G'B' to Y'CbCr)
66  * (j)  chroma downsample
67  * (k)  pack
68  *
69  * quality options
70  *
71  *  (a) range truncate, range expand
72  *  (b) full upsample, 1-1 non-cosited upsample, no upsample
73  *  (c) 8 bits, 16 bits
74  *  (d)
75  *  (e) 8 bits, 16 bits
76  *  (f) 8 bits, 16 bits
77  *  (g) 8 bits, 16 bits
78  *  (h)
79  *  (i) 8 bits, 16 bits
80  *  (j) 1-1 cosited downsample, no downsample
81  *  (k)
82  *
83  *
84  *         1 : a ->   ->   ->   -> e  -> f  -> g  ->   ->   ->   -> k
85  *         2 : a ->   ->   ->   -> e  -> f* -> g  ->   ->   ->   -> k
86  *         3 : a ->   ->   ->   -> e* -> f* -> g* ->   ->   ->   -> k
87  *         4 : a -> b ->   ->   -> e  -> f  -> g  ->   ->   -> j -> k
88  *         5 : a -> b ->   ->   -> e* -> f* -> g* ->   ->   -> j -> k
89  *         6 : a -> b -> c -> d -> e  -> f  -> g  -> h -> i -> j -> k
90  *         7 : a -> b -> c -> d -> e* -> f* -> g* -> h -> i -> j -> k
91  *
92  *         8 : a -> b -> c -> d -> e* -> f* -> g* -> h -> i -> j -> k
93  *         9 : a -> b -> c -> d -> e* -> f* -> g* -> h -> i -> j -> k
94  *        10 : a -> b -> c -> d -> e* -> f* -> g* -> h -> i -> j -> k
95  */
96 
97 #ifndef GST_DISABLE_GST_DEBUG
98 #define GST_CAT_DEFAULT ensure_debug_category()
99 static GstDebugCategory *
ensure_debug_category(void)100 ensure_debug_category (void)
101 {
102   static gsize cat_gonce = 0;
103 
104   if (g_once_init_enter (&cat_gonce)) {
105     gsize cat_done;
106 
107     cat_done = (gsize) _gst_debug_category_new ("video-converter", 0,
108         "video-converter object");
109 
110     g_once_init_leave (&cat_gonce, cat_done);
111   }
112 
113   return (GstDebugCategory *) cat_gonce;
114 }
115 #else
116 #define ensure_debug_category() /* NOOP */
117 #endif /* GST_DISABLE_GST_DEBUG */
118 
119 typedef void (*GstParallelizedTaskFunc) (gpointer user_data);
120 
121 typedef struct _GstParallelizedTaskRunner GstParallelizedTaskRunner;
122 typedef struct _GstParallelizedWorkItem GstParallelizedWorkItem;
123 
124 struct _GstParallelizedWorkItem
125 {
126   GstParallelizedTaskRunner *self;
127   GstParallelizedTaskFunc func;
128   gpointer user_data;
129 };
130 
131 struct _GstParallelizedTaskRunner
132 {
133   GstTaskPool *pool;
134   gboolean own_pool;
135   guint n_threads;
136 
137   GstQueueArray *tasks;
138   GstQueueArray *work_items;
139 
140   GMutex lock;
141 
142   gboolean async_tasks;
143 };
144 
145 static void
gst_parallelized_task_thread_func(gpointer data)146 gst_parallelized_task_thread_func (gpointer data)
147 {
148   GstParallelizedTaskRunner *runner = data;
149   GstParallelizedWorkItem *work_item;
150 
151   g_mutex_lock (&runner->lock);
152   work_item = gst_queue_array_pop_head (runner->work_items);
153   g_mutex_unlock (&runner->lock);
154 
155   g_assert (work_item != NULL);
156   g_assert (work_item->func != NULL);
157 
158 
159   work_item->func (work_item->user_data);
160   if (runner->async_tasks)
161     g_free (work_item);
162 }
163 
164 static void
gst_parallelized_task_runner_join(GstParallelizedTaskRunner * self)165 gst_parallelized_task_runner_join (GstParallelizedTaskRunner * self)
166 {
167   gboolean joined = FALSE;
168 
169   while (!joined) {
170     g_mutex_lock (&self->lock);
171     if (!(joined = gst_queue_array_is_empty (self->tasks))) {
172       gpointer task = gst_queue_array_pop_head (self->tasks);
173       g_mutex_unlock (&self->lock);
174       gst_task_pool_join (self->pool, task);
175     } else {
176       g_mutex_unlock (&self->lock);
177     }
178   }
179 }
180 
181 static void
gst_parallelized_task_runner_free(GstParallelizedTaskRunner * self)182 gst_parallelized_task_runner_free (GstParallelizedTaskRunner * self)
183 {
184   gst_parallelized_task_runner_join (self);
185 
186   gst_queue_array_free (self->work_items);
187   gst_queue_array_free (self->tasks);
188   if (self->own_pool)
189     gst_task_pool_cleanup (self->pool);
190   gst_object_unref (self->pool);
191   g_mutex_clear (&self->lock);
192   g_free (self);
193 }
194 
195 static GstParallelizedTaskRunner *
gst_parallelized_task_runner_new(guint n_threads,GstTaskPool * pool,gboolean async_tasks)196 gst_parallelized_task_runner_new (guint n_threads, GstTaskPool * pool,
197     gboolean async_tasks)
198 {
199   GstParallelizedTaskRunner *self;
200 
201   if (n_threads == 0)
202     n_threads = g_get_num_processors ();
203 
204   self = g_new0 (GstParallelizedTaskRunner, 1);
205 
206   if (pool) {
207     self->pool = g_object_ref (pool);
208     self->own_pool = FALSE;
209 
210     /* No reason to split up the work between more threads than the
211      * pool can spawn */
212     if (GST_IS_SHARED_TASK_POOL (pool))
213       n_threads =
214           MIN (n_threads,
215           gst_shared_task_pool_get_max_threads (GST_SHARED_TASK_POOL (pool)));
216   } else {
217     self->pool = gst_shared_task_pool_new ();
218     self->own_pool = TRUE;
219     gst_shared_task_pool_set_max_threads (GST_SHARED_TASK_POOL (self->pool),
220         n_threads);
221     gst_task_pool_prepare (self->pool, NULL);
222   }
223 
224   self->tasks = gst_queue_array_new (n_threads);
225   self->work_items = gst_queue_array_new (n_threads);
226 
227   self->n_threads = n_threads;
228 
229   g_mutex_init (&self->lock);
230 
231   /* Set when scheduling a job */
232   self->async_tasks = async_tasks;
233 
234   return self;
235 }
236 
237 static void
gst_parallelized_task_runner_finish(GstParallelizedTaskRunner * self)238 gst_parallelized_task_runner_finish (GstParallelizedTaskRunner * self)
239 {
240   gst_parallelized_task_runner_join (self);
241 }
242 
243 static void
gst_parallelized_task_runner_run(GstParallelizedTaskRunner * self,GstParallelizedTaskFunc func,gpointer * task_data)244 gst_parallelized_task_runner_run (GstParallelizedTaskRunner * self,
245     GstParallelizedTaskFunc func, gpointer * task_data)
246 {
247   guint n_threads = self->n_threads;
248 
249   if (n_threads > 1 || self->async_tasks) {
250     guint i = 0;
251     g_mutex_lock (&self->lock);
252     if (!self->async_tasks) {
253       /* if not async, perform one of the functions in the current thread */
254       i = 1;
255     }
256     for (; i < n_threads; i++) {
257       gpointer task;
258       GstParallelizedWorkItem *work_item;
259 
260       if (!self->async_tasks)
261         work_item = g_newa (GstParallelizedWorkItem, 1);
262       else
263         work_item = g_new0 (GstParallelizedWorkItem, 1);
264 
265       work_item->self = self;
266       work_item->func = func;
267       work_item->user_data = task_data[i];
268       gst_queue_array_push_tail (self->work_items, work_item);
269 
270       task =
271           gst_task_pool_push (self->pool, gst_parallelized_task_thread_func,
272           self, NULL);
273 
274       /* The return value of push() is unfortunately nullable, and we can't deal with that */
275       g_assert (task != NULL);
276       gst_queue_array_push_tail (self->tasks, task);
277     }
278     g_mutex_unlock (&self->lock);
279   }
280 
281   if (!self->async_tasks) {
282     func (task_data[0]);
283 
284     gst_parallelized_task_runner_finish (self);
285   }
286 }
287 
288 typedef struct _GstLineCache GstLineCache;
289 
290 #define SCALE    (8)
291 #define SCALE_F  ((float) (1 << SCALE))
292 
293 typedef struct _MatrixData MatrixData;
294 
295 struct _MatrixData
296 {
297   gdouble dm[4][4];
298   gint im[4][4];
299   gint width;
300   guint64 orc_p1;
301   guint64 orc_p2;
302   guint64 orc_p3;
303   guint64 orc_p4;
304   gint64 *t_r;
305   gint64 *t_g;
306   gint64 *t_b;
307   gint64 t_c;
308   void (*matrix_func) (MatrixData * data, gpointer pixels);
309 };
310 
311 typedef struct _GammaData GammaData;
312 
313 struct _GammaData
314 {
315   gpointer gamma_table;
316   gint width;
317   void (*gamma_func) (GammaData * data, gpointer dest, gpointer src);
318 };
319 
320 typedef enum
321 {
322   ALPHA_MODE_NONE = 0,
323   ALPHA_MODE_COPY = (1 << 0),
324   ALPHA_MODE_SET = (1 << 1),
325   ALPHA_MODE_MULT = (1 << 2)
326 } AlphaMode;
327 
328 typedef struct
329 {
330   guint8 *data;
331   guint stride;
332   guint n_lines;
333   guint idx;
334   gpointer user_data;
335   GDestroyNotify notify;
336 } ConverterAlloc;
337 
338 typedef void (*FastConvertFunc) (GstVideoConverter * convert,
339     const GstVideoFrame * src, GstVideoFrame * dest, gint plane);
340 
341 struct _GstVideoConverter
342 {
343   gint flags;
344 
345   GstVideoInfo in_info;
346   GstVideoInfo out_info;
347 
348   gint in_x;
349   gint in_y;
350   gint in_width;
351   gint in_height;
352   gint in_maxwidth;
353   gint in_maxheight;
354   gint out_x;
355   gint out_y;
356   gint out_width;
357   gint out_height;
358   gint out_maxwidth;
359   gint out_maxheight;
360 
361   gint current_pstride;
362   gint current_width;
363   gint current_height;
364   GstVideoFormat current_format;
365   gint current_bits;
366 
367   GstStructure *config;
368 
369   GstParallelizedTaskRunner *conversion_runner;
370 
371   guint16 **tmpline;
372 
373   gboolean fill_border;
374   gpointer borderline;
375   guint64 borders[4];
376   guint32 border_argb;
377   guint32 alpha_value;
378   AlphaMode alpha_mode;
379 
380   void (*convert) (GstVideoConverter * convert, const GstVideoFrame * src,
381       GstVideoFrame * dest);
382 
383   /* data for unpack */
384   GstLineCache **unpack_lines;
385   GstVideoFormat unpack_format;
386   guint unpack_bits;
387   gboolean unpack_rgb;
388   gboolean identity_unpack;
389   gint unpack_pstride;
390 
391   /* chroma upsample */
392   GstLineCache **upsample_lines;
393   GstVideoChromaResample **upsample;
394   GstVideoChromaResample **upsample_p;
395   GstVideoChromaResample **upsample_i;
396   guint up_n_lines;
397   gint up_offset;
398 
399   /* to R'G'B */
400   GstLineCache **to_RGB_lines;
401   MatrixData to_RGB_matrix;
402   /* gamma decode */
403   GammaData gamma_dec;
404 
405   /* scaling */
406   GstLineCache **hscale_lines;
407   GstVideoScaler **h_scaler;
408   gint h_scale_format;
409   GstLineCache **vscale_lines;
410   GstVideoScaler **v_scaler;
411   GstVideoScaler **v_scaler_p;
412   GstVideoScaler **v_scaler_i;
413   gint v_scale_width;
414   gint v_scale_format;
415 
416   /* color space conversion */
417   GstLineCache **convert_lines;
418   MatrixData convert_matrix;
419   gint in_bits;
420   gint out_bits;
421 
422   /* alpha correction */
423   GstLineCache **alpha_lines;
424   void (*alpha_func) (GstVideoConverter * convert, gpointer pixels, gint width);
425 
426   /* gamma encode */
427   GammaData gamma_enc;
428   /* to Y'CbCr */
429   GstLineCache **to_YUV_lines;
430   MatrixData to_YUV_matrix;
431 
432   /* chroma downsample */
433   GstLineCache **downsample_lines;
434   GstVideoChromaResample **downsample;
435   GstVideoChromaResample **downsample_p;
436   GstVideoChromaResample **downsample_i;
437   guint down_n_lines;
438   gint down_offset;
439 
440   /* dither */
441   GstLineCache **dither_lines;
442   GstVideoDither **dither;
443 
444   /* pack */
445   GstLineCache **pack_lines;
446   guint pack_nlines;
447   GstVideoFormat pack_format;
448   guint pack_bits;
449   gboolean pack_rgb;
450   gboolean identity_pack;
451   gint pack_pstride;
452   gconstpointer pack_pal;
453   gsize pack_palsize;
454 
455   const GstVideoFrame *src;
456   GstVideoFrame *dest;
457 
458   /* fastpath */
459   GstVideoFormat fformat[4];
460   gint fin_x[4];
461   gint fin_y[4];
462   gint fout_x[4];
463   gint fout_y[4];
464   gint fout_width[4];
465   gint fout_height[4];
466   gint fsplane[4];
467   gint ffill[4];
468 
469   struct
470   {
471     GstVideoScaler **scaler;
472   } fh_scaler[4];
473   struct
474   {
475     GstVideoScaler **scaler;
476   } fv_scaler[4];
477   FastConvertFunc fconvert[4];
478 
479   /* for parallel async running */
480   gpointer tasks[4];
481   gpointer tasks_p[4];
482 };
483 
484 typedef gpointer (*GstLineCacheAllocLineFunc) (GstLineCache * cache, gint idx,
485     gpointer user_data);
486 typedef gboolean (*GstLineCacheNeedLineFunc) (GstLineCache * cache, gint idx,
487     gint out_line, gint in_line, gpointer user_data);
488 
489 struct _GstLineCache
490 {
491   gint first;
492   gint backlog;
493   GPtrArray *lines;
494 
495   GstLineCache *prev;
496   gboolean write_input;
497   gboolean pass_alloc;
498   gboolean alloc_writable;
499 
500   GstLineCacheNeedLineFunc need_line;
501   gint need_line_idx;
502   gpointer need_line_data;
503   GDestroyNotify need_line_notify;
504 
505   guint n_lines;
506   guint stride;
507   GstLineCacheAllocLineFunc alloc_line;
508   gpointer alloc_line_data;
509   GDestroyNotify alloc_line_notify;
510 };
511 
512 static GstLineCache *
gst_line_cache_new(GstLineCache * prev)513 gst_line_cache_new (GstLineCache * prev)
514 {
515   GstLineCache *result;
516 
517   result = g_slice_new0 (GstLineCache);
518   result->lines = g_ptr_array_new ();
519   result->prev = prev;
520 
521   return result;
522 }
523 
524 static void
gst_line_cache_clear(GstLineCache * cache)525 gst_line_cache_clear (GstLineCache * cache)
526 {
527   g_return_if_fail (cache != NULL);
528 
529   g_ptr_array_set_size (cache->lines, 0);
530   cache->first = 0;
531 }
532 
533 static void
gst_line_cache_free(GstLineCache * cache)534 gst_line_cache_free (GstLineCache * cache)
535 {
536   if (cache->need_line_notify)
537     cache->need_line_notify (cache->need_line_data);
538   if (cache->alloc_line_notify)
539     cache->alloc_line_notify (cache->alloc_line_data);
540   gst_line_cache_clear (cache);
541   g_ptr_array_unref (cache->lines);
542   g_slice_free (GstLineCache, cache);
543 }
544 
545 static void
gst_line_cache_set_need_line_func(GstLineCache * cache,GstLineCacheNeedLineFunc need_line,gint idx,gpointer user_data,GDestroyNotify notify)546 gst_line_cache_set_need_line_func (GstLineCache * cache,
547     GstLineCacheNeedLineFunc need_line, gint idx, gpointer user_data,
548     GDestroyNotify notify)
549 {
550   cache->need_line = need_line;
551   cache->need_line_idx = idx;
552   cache->need_line_data = user_data;
553   cache->need_line_notify = notify;
554 }
555 
556 static void
gst_line_cache_set_alloc_line_func(GstLineCache * cache,GstLineCacheAllocLineFunc alloc_line,gpointer user_data,GDestroyNotify notify)557 gst_line_cache_set_alloc_line_func (GstLineCache * cache,
558     GstLineCacheAllocLineFunc alloc_line, gpointer user_data,
559     GDestroyNotify notify)
560 {
561   cache->alloc_line = alloc_line;
562   cache->alloc_line_data = user_data;
563   cache->alloc_line_notify = notify;
564 }
565 
566 /* keep this much backlog for interlaced video */
567 #define BACKLOG 2
568 
569 static gpointer *
gst_line_cache_get_lines(GstLineCache * cache,gint idx,gint out_line,gint in_line,gint n_lines)570 gst_line_cache_get_lines (GstLineCache * cache, gint idx, gint out_line,
571     gint in_line, gint n_lines)
572 {
573   if (cache->first + cache->backlog < in_line) {
574     gint to_remove =
575         MIN (in_line - (cache->first + cache->backlog), cache->lines->len);
576     if (to_remove > 0) {
577       g_ptr_array_remove_range (cache->lines, 0, to_remove);
578     }
579     cache->first += to_remove;
580   } else if (in_line < cache->first) {
581     gst_line_cache_clear (cache);
582     cache->first = in_line;
583   }
584 
585   while (TRUE) {
586     gint oline;
587 
588     if (cache->first <= in_line
589         && in_line + n_lines <= cache->first + (gint) cache->lines->len) {
590       return cache->lines->pdata + (in_line - cache->first);
591     }
592 
593     if (cache->need_line == NULL)
594       break;
595 
596     /* We may be able to skip ahead to the earliest line needed */
597     if (cache->lines->len == 0 && cache->first + cache->backlog < in_line)
598       cache->first = in_line - cache->backlog;
599 
600     oline = out_line + cache->first + cache->lines->len - in_line;
601 
602     if (!cache->need_line (cache, idx, oline, cache->first + cache->lines->len,
603             cache->need_line_data))
604       break;
605   }
606   GST_DEBUG ("no lines");
607   return NULL;
608 }
609 
610 static void
gst_line_cache_add_line(GstLineCache * cache,gint idx,gpointer line)611 gst_line_cache_add_line (GstLineCache * cache, gint idx, gpointer line)
612 {
613   if (cache->first + cache->lines->len != idx) {
614     gst_line_cache_clear (cache);
615     cache->first = idx;
616   }
617   g_ptr_array_add (cache->lines, line);
618 }
619 
620 static gpointer
gst_line_cache_alloc_line(GstLineCache * cache,gint idx)621 gst_line_cache_alloc_line (GstLineCache * cache, gint idx)
622 {
623   gpointer res;
624 
625   if (cache->alloc_line)
626     res = cache->alloc_line (cache, idx, cache->alloc_line_data);
627   else
628     res = NULL;
629 
630   return res;
631 }
632 
633 static void video_converter_generic (GstVideoConverter * convert,
634     const GstVideoFrame * src, GstVideoFrame * dest);
635 static gboolean video_converter_lookup_fastpath (GstVideoConverter * convert);
636 static void video_converter_compute_matrix (GstVideoConverter * convert);
637 static void video_converter_compute_resample (GstVideoConverter * convert,
638     gint idx);
639 
640 static gpointer get_dest_line (GstLineCache * cache, gint idx,
641     gpointer user_data);
642 
643 static gboolean do_unpack_lines (GstLineCache * cache, gint idx, gint out_line,
644     gint in_line, gpointer user_data);
645 static gboolean do_downsample_lines (GstLineCache * cache, gint idx,
646     gint out_line, gint in_line, gpointer user_data);
647 static gboolean do_convert_to_RGB_lines (GstLineCache * cache, gint idx,
648     gint out_line, gint in_line, gpointer user_data);
649 static gboolean do_convert_lines (GstLineCache * cache, gint idx, gint out_line,
650     gint in_line, gpointer user_data);
651 static gboolean do_alpha_lines (GstLineCache * cache, gint idx, gint out_line,
652     gint in_line, gpointer user_data);
653 static gboolean do_convert_to_YUV_lines (GstLineCache * cache, gint idx,
654     gint out_line, gint in_line, gpointer user_data);
655 static gboolean do_upsample_lines (GstLineCache * cache, gint idx,
656     gint out_line, gint in_line, gpointer user_data);
657 static gboolean do_vscale_lines (GstLineCache * cache, gint idx, gint out_line,
658     gint in_line, gpointer user_data);
659 static gboolean do_hscale_lines (GstLineCache * cache, gint idx, gint out_line,
660     gint in_line, gpointer user_data);
661 static gboolean do_dither_lines (GstLineCache * cache, gint idx, gint out_line,
662     gint in_line, gpointer user_data);
663 
664 static ConverterAlloc *
converter_alloc_new(guint stride,guint n_lines,gpointer user_data,GDestroyNotify notify)665 converter_alloc_new (guint stride, guint n_lines, gpointer user_data,
666     GDestroyNotify notify)
667 {
668   ConverterAlloc *alloc;
669 
670   GST_DEBUG ("stride %d, n_lines %d", stride, n_lines);
671   alloc = g_slice_new0 (ConverterAlloc);
672   alloc->data = g_malloc (stride * n_lines);
673   alloc->stride = stride;
674   alloc->n_lines = n_lines;
675   alloc->idx = 0;
676   alloc->user_data = user_data;
677   alloc->notify = notify;
678 
679   return alloc;
680 }
681 
682 static void
converter_alloc_free(ConverterAlloc * alloc)683 converter_alloc_free (ConverterAlloc * alloc)
684 {
685   if (alloc->notify)
686     alloc->notify (alloc->user_data);
687   g_free (alloc->data);
688   g_slice_free (ConverterAlloc, alloc);
689 }
690 
691 static void
setup_border_alloc(GstVideoConverter * convert,ConverterAlloc * alloc)692 setup_border_alloc (GstVideoConverter * convert, ConverterAlloc * alloc)
693 {
694   gint i;
695 
696   if (convert->borderline) {
697     for (i = 0; i < alloc->n_lines; i++)
698       memcpy (&alloc->data[i * alloc->stride], convert->borderline,
699           alloc->stride);
700   }
701 }
702 
703 static gpointer
get_temp_line(GstLineCache * cache,gint idx,gpointer user_data)704 get_temp_line (GstLineCache * cache, gint idx, gpointer user_data)
705 {
706   ConverterAlloc *alloc = user_data;
707   gpointer tmpline;
708 
709   GST_DEBUG ("get temp line %d (%p %d)", idx, alloc, alloc->idx);
710   tmpline = &alloc->data[alloc->stride * alloc->idx];
711   alloc->idx = (alloc->idx + 1) % alloc->n_lines;
712 
713   return tmpline;
714 }
715 
716 static gpointer
get_border_temp_line(GstLineCache * cache,gint idx,gpointer user_data)717 get_border_temp_line (GstLineCache * cache, gint idx, gpointer user_data)
718 {
719   ConverterAlloc *alloc = user_data;
720   GstVideoConverter *convert = alloc->user_data;
721   gpointer tmpline;
722 
723   GST_DEBUG ("get temp line %d (%p %d)", idx, alloc, alloc->idx);
724   tmpline = &alloc->data[alloc->stride * alloc->idx] +
725       (convert->out_x * convert->pack_pstride);
726   alloc->idx = (alloc->idx + 1) % alloc->n_lines;
727 
728   return tmpline;
729 }
730 
731 static gint
get_opt_int(GstVideoConverter * convert,const gchar * opt,gint def)732 get_opt_int (GstVideoConverter * convert, const gchar * opt, gint def)
733 {
734   gint res;
735   if (!gst_structure_get_int (convert->config, opt, &res))
736     res = def;
737   return res;
738 }
739 
740 static guint
get_opt_uint(GstVideoConverter * convert,const gchar * opt,guint def)741 get_opt_uint (GstVideoConverter * convert, const gchar * opt, guint def)
742 {
743   guint res;
744   if (!gst_structure_get_uint (convert->config, opt, &res))
745     res = def;
746   return res;
747 }
748 
749 static gdouble
get_opt_double(GstVideoConverter * convert,const gchar * opt,gdouble def)750 get_opt_double (GstVideoConverter * convert, const gchar * opt, gdouble def)
751 {
752   gdouble res;
753   if (!gst_structure_get_double (convert->config, opt, &res))
754     res = def;
755   return res;
756 }
757 
758 static gboolean
get_opt_bool(GstVideoConverter * convert,const gchar * opt,gboolean def)759 get_opt_bool (GstVideoConverter * convert, const gchar * opt, gboolean def)
760 {
761   gboolean res;
762   if (!gst_structure_get_boolean (convert->config, opt, &res))
763     res = def;
764   return res;
765 }
766 
767 static gint
get_opt_enum(GstVideoConverter * convert,const gchar * opt,GType type,gint def)768 get_opt_enum (GstVideoConverter * convert, const gchar * opt, GType type,
769     gint def)
770 {
771   gint res;
772   if (!gst_structure_get_enum (convert->config, opt, type, &res))
773     res = def;
774   return res;
775 }
776 
777 #define DEFAULT_OPT_FILL_BORDER TRUE
778 #define DEFAULT_OPT_ALPHA_VALUE 1.0
779 /* options copy, set, mult */
780 #define DEFAULT_OPT_ALPHA_MODE GST_VIDEO_ALPHA_MODE_COPY
781 #define DEFAULT_OPT_BORDER_ARGB 0xff000000
782 /* options full, input-only, output-only, none */
783 #define DEFAULT_OPT_MATRIX_MODE GST_VIDEO_MATRIX_MODE_FULL
784 /* none, remap */
785 #define DEFAULT_OPT_GAMMA_MODE GST_VIDEO_GAMMA_MODE_NONE
786 /* none, merge-only, fast */
787 #define DEFAULT_OPT_PRIMARIES_MODE GST_VIDEO_PRIMARIES_MODE_NONE
788 /* options full, upsample-only, downsample-only, none */
789 #define DEFAULT_OPT_CHROMA_MODE GST_VIDEO_CHROMA_MODE_FULL
790 #define DEFAULT_OPT_RESAMPLER_METHOD GST_VIDEO_RESAMPLER_METHOD_CUBIC
791 #define DEFAULT_OPT_CHROMA_RESAMPLER_METHOD GST_VIDEO_RESAMPLER_METHOD_LINEAR
792 #define DEFAULT_OPT_RESAMPLER_TAPS 0
793 #define DEFAULT_OPT_DITHER_METHOD GST_VIDEO_DITHER_BAYER
794 #define DEFAULT_OPT_DITHER_QUANTIZATION 1
795 #define DEFAULT_OPT_ASYNC_TASKS FALSE
796 
797 #define GET_OPT_FILL_BORDER(c) get_opt_bool(c, \
798     GST_VIDEO_CONVERTER_OPT_FILL_BORDER, DEFAULT_OPT_FILL_BORDER)
799 #define GET_OPT_ALPHA_VALUE(c) get_opt_double(c, \
800     GST_VIDEO_CONVERTER_OPT_ALPHA_VALUE, DEFAULT_OPT_ALPHA_VALUE)
801 #define GET_OPT_ALPHA_MODE(c) get_opt_enum(c, \
802     GST_VIDEO_CONVERTER_OPT_ALPHA_MODE, GST_TYPE_VIDEO_ALPHA_MODE, DEFAULT_OPT_ALPHA_MODE)
803 #define GET_OPT_BORDER_ARGB(c) get_opt_uint(c, \
804     GST_VIDEO_CONVERTER_OPT_BORDER_ARGB, DEFAULT_OPT_BORDER_ARGB)
805 #define GET_OPT_MATRIX_MODE(c) get_opt_enum(c, \
806     GST_VIDEO_CONVERTER_OPT_MATRIX_MODE, GST_TYPE_VIDEO_MATRIX_MODE, DEFAULT_OPT_MATRIX_MODE)
807 #define GET_OPT_GAMMA_MODE(c) get_opt_enum(c, \
808     GST_VIDEO_CONVERTER_OPT_GAMMA_MODE, GST_TYPE_VIDEO_GAMMA_MODE, DEFAULT_OPT_GAMMA_MODE)
809 #define GET_OPT_PRIMARIES_MODE(c) get_opt_enum(c, \
810     GST_VIDEO_CONVERTER_OPT_PRIMARIES_MODE, GST_TYPE_VIDEO_PRIMARIES_MODE, DEFAULT_OPT_PRIMARIES_MODE)
811 #define GET_OPT_CHROMA_MODE(c) get_opt_enum(c, \
812     GST_VIDEO_CONVERTER_OPT_CHROMA_MODE, GST_TYPE_VIDEO_CHROMA_MODE, DEFAULT_OPT_CHROMA_MODE)
813 #define GET_OPT_RESAMPLER_METHOD(c) get_opt_enum(c, \
814     GST_VIDEO_CONVERTER_OPT_RESAMPLER_METHOD, GST_TYPE_VIDEO_RESAMPLER_METHOD, \
815     DEFAULT_OPT_RESAMPLER_METHOD)
816 #define GET_OPT_CHROMA_RESAMPLER_METHOD(c) get_opt_enum(c, \
817     GST_VIDEO_CONVERTER_OPT_CHROMA_RESAMPLER_METHOD, GST_TYPE_VIDEO_RESAMPLER_METHOD, \
818     DEFAULT_OPT_CHROMA_RESAMPLER_METHOD)
819 #define GET_OPT_RESAMPLER_TAPS(c) get_opt_uint(c, \
820     GST_VIDEO_CONVERTER_OPT_RESAMPLER_TAPS, DEFAULT_OPT_RESAMPLER_TAPS)
821 #define GET_OPT_DITHER_METHOD(c) get_opt_enum(c, \
822     GST_VIDEO_CONVERTER_OPT_DITHER_METHOD, GST_TYPE_VIDEO_DITHER_METHOD, \
823     DEFAULT_OPT_DITHER_METHOD)
824 #define GET_OPT_DITHER_QUANTIZATION(c) get_opt_uint(c, \
825     GST_VIDEO_CONVERTER_OPT_DITHER_QUANTIZATION, DEFAULT_OPT_DITHER_QUANTIZATION)
826 #define GET_OPT_ASYNC_TASKS(c) get_opt_bool(c, \
827     GST_VIDEO_CONVERTER_OPT_ASYNC_TASKS, DEFAULT_OPT_ASYNC_TASKS)
828 
829 #define CHECK_ALPHA_COPY(c) (GET_OPT_ALPHA_MODE(c) == GST_VIDEO_ALPHA_MODE_COPY)
830 #define CHECK_ALPHA_SET(c) (GET_OPT_ALPHA_MODE(c) == GST_VIDEO_ALPHA_MODE_SET)
831 #define CHECK_ALPHA_MULT(c) (GET_OPT_ALPHA_MODE(c) == GST_VIDEO_ALPHA_MODE_MULT)
832 
833 #define CHECK_MATRIX_FULL(c) (GET_OPT_MATRIX_MODE(c) == GST_VIDEO_MATRIX_MODE_FULL)
834 #define CHECK_MATRIX_INPUT(c) (GET_OPT_MATRIX_MODE(c) == GST_VIDEO_MATRIX_MODE_INPUT_ONLY)
835 #define CHECK_MATRIX_OUTPUT(c) (GET_OPT_MATRIX_MODE(c) == GST_VIDEO_MATRIX_MODE_OUTPUT_ONLY)
836 #define CHECK_MATRIX_NONE(c) (GET_OPT_MATRIX_MODE(c) == GST_VIDEO_MATRIX_MODE_NONE)
837 
838 #define CHECK_GAMMA_NONE(c) (GET_OPT_GAMMA_MODE(c) == GST_VIDEO_GAMMA_MODE_NONE)
839 #define CHECK_GAMMA_REMAP(c) (GET_OPT_GAMMA_MODE(c) == GST_VIDEO_GAMMA_MODE_REMAP)
840 
841 #define CHECK_PRIMARIES_NONE(c) (GET_OPT_PRIMARIES_MODE(c) == GST_VIDEO_PRIMARIES_MODE_NONE)
842 #define CHECK_PRIMARIES_MERGE(c) (GET_OPT_PRIMARIES_MODE(c) == GST_VIDEO_PRIMARIES_MODE_MERGE_ONLY)
843 #define CHECK_PRIMARIES_FAST(c) (GET_OPT_PRIMARIES_MODE(c) == GST_VIDEO_PRIMARIES_MODE_FAST)
844 
845 #define CHECK_CHROMA_FULL(c) (GET_OPT_CHROMA_MODE(c) == GST_VIDEO_CHROMA_MODE_FULL)
846 #define CHECK_CHROMA_UPSAMPLE(c) (GET_OPT_CHROMA_MODE(c) == GST_VIDEO_CHROMA_MODE_UPSAMPLE_ONLY)
847 #define CHECK_CHROMA_DOWNSAMPLE(c) (GET_OPT_CHROMA_MODE(c) == GST_VIDEO_CHROMA_MODE_DOWNSAMPLE_ONLY)
848 #define CHECK_CHROMA_NONE(c) (GET_OPT_CHROMA_MODE(c) == GST_VIDEO_CHROMA_MODE_NONE)
849 
850 static GstLineCache *
chain_unpack_line(GstVideoConverter * convert,gint idx)851 chain_unpack_line (GstVideoConverter * convert, gint idx)
852 {
853   GstLineCache *prev;
854   GstVideoInfo *info;
855 
856   info = &convert->in_info;
857 
858   convert->current_format = convert->unpack_format;
859   convert->current_bits = convert->unpack_bits;
860   convert->current_pstride = convert->current_bits >> 1;
861 
862   convert->unpack_pstride = convert->current_pstride;
863   convert->identity_unpack = (convert->current_format == info->finfo->format);
864 
865   GST_DEBUG ("chain unpack line format %s, pstride %d, identity_unpack %d",
866       gst_video_format_to_string (convert->current_format),
867       convert->current_pstride, convert->identity_unpack);
868 
869   prev = convert->unpack_lines[idx] = gst_line_cache_new (NULL);
870   prev->write_input = FALSE;
871   prev->pass_alloc = FALSE;
872   prev->n_lines = 1;
873   prev->stride = convert->current_pstride * convert->current_width;
874   gst_line_cache_set_need_line_func (prev, do_unpack_lines, idx, convert, NULL);
875 
876   return prev;
877 }
878 
879 static GstLineCache *
chain_upsample(GstVideoConverter * convert,GstLineCache * prev,gint idx)880 chain_upsample (GstVideoConverter * convert, GstLineCache * prev, gint idx)
881 {
882   video_converter_compute_resample (convert, idx);
883 
884   if (convert->upsample_p[idx] || convert->upsample_i[idx]) {
885     GST_DEBUG ("chain upsample");
886     prev = convert->upsample_lines[idx] = gst_line_cache_new (prev);
887     prev->write_input = TRUE;
888     prev->pass_alloc = TRUE;
889     /* XXX: why this hardcoded value? */
890     prev->n_lines = 5;
891     prev->stride = convert->current_pstride * convert->current_width;
892     gst_line_cache_set_need_line_func (prev,
893         do_upsample_lines, idx, convert, NULL);
894   }
895   return prev;
896 }
897 
898 static void
color_matrix_set_identity(MatrixData * m)899 color_matrix_set_identity (MatrixData * m)
900 {
901   int i, j;
902 
903   for (i = 0; i < 4; i++) {
904     for (j = 0; j < 4; j++) {
905       m->dm[i][j] = (i == j);
906     }
907   }
908 }
909 
910 static void
color_matrix_copy(MatrixData * d,const MatrixData * s)911 color_matrix_copy (MatrixData * d, const MatrixData * s)
912 {
913   gint i, j;
914 
915   for (i = 0; i < 4; i++)
916     for (j = 0; j < 4; j++)
917       d->dm[i][j] = s->dm[i][j];
918 }
919 
920 /* Perform 4x4 matrix multiplication:
921  *  - @dst@ = @a@ * @b@
922  *  - @dst@ may be a pointer to @a@ andor @b@
923  */
924 static void
color_matrix_multiply(MatrixData * dst,MatrixData * a,MatrixData * b)925 color_matrix_multiply (MatrixData * dst, MatrixData * a, MatrixData * b)
926 {
927   MatrixData tmp;
928   int i, j, k;
929 
930   for (i = 0; i < 4; i++) {
931     for (j = 0; j < 4; j++) {
932       double x = 0;
933       for (k = 0; k < 4; k++) {
934         x += a->dm[i][k] * b->dm[k][j];
935       }
936       tmp.dm[i][j] = x;
937     }
938   }
939   color_matrix_copy (dst, &tmp);
940 }
941 
942 static void
color_matrix_invert(MatrixData * d,MatrixData * s)943 color_matrix_invert (MatrixData * d, MatrixData * s)
944 {
945   MatrixData tmp;
946   int i, j;
947   double det;
948 
949   color_matrix_set_identity (&tmp);
950   for (j = 0; j < 3; j++) {
951     for (i = 0; i < 3; i++) {
952       tmp.dm[j][i] =
953           s->dm[(i + 1) % 3][(j + 1) % 3] * s->dm[(i + 2) % 3][(j + 2) % 3] -
954           s->dm[(i + 1) % 3][(j + 2) % 3] * s->dm[(i + 2) % 3][(j + 1) % 3];
955     }
956   }
957   det =
958       tmp.dm[0][0] * s->dm[0][0] + tmp.dm[0][1] * s->dm[1][0] +
959       tmp.dm[0][2] * s->dm[2][0];
960   for (j = 0; j < 3; j++) {
961     for (i = 0; i < 3; i++) {
962       tmp.dm[i][j] /= det;
963     }
964   }
965   color_matrix_copy (d, &tmp);
966 }
967 
968 static void
color_matrix_offset_components(MatrixData * m,double a1,double a2,double a3)969 color_matrix_offset_components (MatrixData * m, double a1, double a2, double a3)
970 {
971   MatrixData a;
972 
973   color_matrix_set_identity (&a);
974   a.dm[0][3] = a1;
975   a.dm[1][3] = a2;
976   a.dm[2][3] = a3;
977   color_matrix_multiply (m, &a, m);
978 }
979 
980 static void
color_matrix_scale_components(MatrixData * m,double a1,double a2,double a3)981 color_matrix_scale_components (MatrixData * m, double a1, double a2, double a3)
982 {
983   MatrixData a;
984 
985   color_matrix_set_identity (&a);
986   a.dm[0][0] = a1;
987   a.dm[1][1] = a2;
988   a.dm[2][2] = a3;
989   color_matrix_multiply (m, &a, m);
990 }
991 
992 static void
color_matrix_debug(const MatrixData * s)993 color_matrix_debug (const MatrixData * s)
994 {
995   GST_DEBUG ("[%f %f %f %f]", s->dm[0][0], s->dm[0][1], s->dm[0][2],
996       s->dm[0][3]);
997   GST_DEBUG ("[%f %f %f %f]", s->dm[1][0], s->dm[1][1], s->dm[1][2],
998       s->dm[1][3]);
999   GST_DEBUG ("[%f %f %f %f]", s->dm[2][0], s->dm[2][1], s->dm[2][2],
1000       s->dm[2][3]);
1001   GST_DEBUG ("[%f %f %f %f]", s->dm[3][0], s->dm[3][1], s->dm[3][2],
1002       s->dm[3][3]);
1003 }
1004 
1005 static void
color_matrix_convert(MatrixData * s)1006 color_matrix_convert (MatrixData * s)
1007 {
1008   gint i, j;
1009 
1010   for (i = 0; i < 4; i++)
1011     for (j = 0; j < 4; j++)
1012       s->im[i][j] = rint (s->dm[i][j]);
1013 
1014   GST_DEBUG ("[%6d %6d %6d %6d]", s->im[0][0], s->im[0][1], s->im[0][2],
1015       s->im[0][3]);
1016   GST_DEBUG ("[%6d %6d %6d %6d]", s->im[1][0], s->im[1][1], s->im[1][2],
1017       s->im[1][3]);
1018   GST_DEBUG ("[%6d %6d %6d %6d]", s->im[2][0], s->im[2][1], s->im[2][2],
1019       s->im[2][3]);
1020   GST_DEBUG ("[%6d %6d %6d %6d]", s->im[3][0], s->im[3][1], s->im[3][2],
1021       s->im[3][3]);
1022 }
1023 
1024 static void
color_matrix_YCbCr_to_RGB(MatrixData * m,double Kr,double Kb)1025 color_matrix_YCbCr_to_RGB (MatrixData * m, double Kr, double Kb)
1026 {
1027   double Kg = 1.0 - Kr - Kb;
1028   MatrixData k = {
1029     {
1030           {1., 0., 2 * (1 - Kr), 0.},
1031           {1., -2 * Kb * (1 - Kb) / Kg, -2 * Kr * (1 - Kr) / Kg, 0.},
1032           {1., 2 * (1 - Kb), 0., 0.},
1033           {0., 0., 0., 1.},
1034         }
1035   };
1036 
1037   color_matrix_multiply (m, &k, m);
1038 }
1039 
1040 static void
color_matrix_RGB_to_YCbCr(MatrixData * m,double Kr,double Kb)1041 color_matrix_RGB_to_YCbCr (MatrixData * m, double Kr, double Kb)
1042 {
1043   double Kg = 1.0 - Kr - Kb;
1044   MatrixData k;
1045   double x;
1046 
1047   k.dm[0][0] = Kr;
1048   k.dm[0][1] = Kg;
1049   k.dm[0][2] = Kb;
1050   k.dm[0][3] = 0;
1051 
1052   x = 1 / (2 * (1 - Kb));
1053   k.dm[1][0] = -x * Kr;
1054   k.dm[1][1] = -x * Kg;
1055   k.dm[1][2] = x * (1 - Kb);
1056   k.dm[1][3] = 0;
1057 
1058   x = 1 / (2 * (1 - Kr));
1059   k.dm[2][0] = x * (1 - Kr);
1060   k.dm[2][1] = -x * Kg;
1061   k.dm[2][2] = -x * Kb;
1062   k.dm[2][3] = 0;
1063 
1064   k.dm[3][0] = 0;
1065   k.dm[3][1] = 0;
1066   k.dm[3][2] = 0;
1067   k.dm[3][3] = 1;
1068 
1069   color_matrix_multiply (m, &k, m);
1070 }
1071 
1072 static void
color_matrix_RGB_to_XYZ(MatrixData * dst,double Rx,double Ry,double Gx,double Gy,double Bx,double By,double Wx,double Wy)1073 color_matrix_RGB_to_XYZ (MatrixData * dst, double Rx, double Ry, double Gx,
1074     double Gy, double Bx, double By, double Wx, double Wy)
1075 {
1076   MatrixData m, im;
1077   double sx, sy, sz;
1078   double wx, wy, wz;
1079 
1080   color_matrix_set_identity (&m);
1081 
1082   m.dm[0][0] = Rx;
1083   m.dm[1][0] = Ry;
1084   m.dm[2][0] = (1.0 - Rx - Ry);
1085   m.dm[0][1] = Gx;
1086   m.dm[1][1] = Gy;
1087   m.dm[2][1] = (1.0 - Gx - Gy);
1088   m.dm[0][2] = Bx;
1089   m.dm[1][2] = By;
1090   m.dm[2][2] = (1.0 - Bx - By);
1091 
1092   color_matrix_invert (&im, &m);
1093 
1094   wx = Wx / Wy;
1095   wy = 1.0;
1096   wz = (1.0 - Wx - Wy) / Wy;
1097 
1098   sx = im.dm[0][0] * wx + im.dm[0][1] * wy + im.dm[0][2] * wz;
1099   sy = im.dm[1][0] * wx + im.dm[1][1] * wy + im.dm[1][2] * wz;
1100   sz = im.dm[2][0] * wx + im.dm[2][1] * wy + im.dm[2][2] * wz;
1101 
1102   m.dm[0][0] *= sx;
1103   m.dm[1][0] *= sx;
1104   m.dm[2][0] *= sx;
1105   m.dm[0][1] *= sy;
1106   m.dm[1][1] *= sy;
1107   m.dm[2][1] *= sy;
1108   m.dm[0][2] *= sz;
1109   m.dm[1][2] *= sz;
1110   m.dm[2][2] *= sz;
1111 
1112   color_matrix_copy (dst, &m);
1113 }
1114 
1115 static void
videoconvert_convert_init_tables(MatrixData * data)1116 videoconvert_convert_init_tables (MatrixData * data)
1117 {
1118   gint i, j;
1119 
1120   data->t_r = g_new (gint64, 256);
1121   data->t_g = g_new (gint64, 256);
1122   data->t_b = g_new (gint64, 256);
1123 
1124   for (i = 0; i < 256; i++) {
1125     gint64 r = 0, g = 0, b = 0;
1126 
1127     for (j = 0; j < 3; j++) {
1128       r = (r << 16) + data->im[j][0] * i;
1129       g = (g << 16) + data->im[j][1] * i;
1130       b = (b << 16) + data->im[j][2] * i;
1131     }
1132     data->t_r[i] = r;
1133     data->t_g[i] = g;
1134     data->t_b[i] = b;
1135   }
1136   data->t_c = ((gint64) data->im[0][3] << 32)
1137       + ((gint64) data->im[1][3] << 16)
1138       + ((gint64) data->im[2][3] << 0);
1139 }
1140 
1141 void
_custom_video_orc_matrix8(guint8 * ORC_RESTRICT d1,const guint8 * ORC_RESTRICT s1,orc_int64 p1,orc_int64 p2,orc_int64 p3,orc_int64 p4,int n)1142 _custom_video_orc_matrix8 (guint8 * ORC_RESTRICT d1,
1143     const guint8 * ORC_RESTRICT s1, orc_int64 p1, orc_int64 p2, orc_int64 p3,
1144     orc_int64 p4, int n)
1145 {
1146   gint i;
1147   gint r, g, b;
1148   gint y, u, v;
1149   gint a00, a01, a02, a03;
1150   gint a10, a11, a12, a13;
1151   gint a20, a21, a22, a23;
1152 
1153   a00 = (gint16) (p1 >> 16);
1154   a01 = (gint16) (p2 >> 16);
1155   a02 = (gint16) (p3 >> 16);
1156   a03 = (gint16) (p4 >> 16);
1157   a10 = (gint16) (p1 >> 32);
1158   a11 = (gint16) (p2 >> 32);
1159   a12 = (gint16) (p3 >> 32);
1160   a13 = (gint16) (p4 >> 32);
1161   a20 = (gint16) (p1 >> 48);
1162   a21 = (gint16) (p2 >> 48);
1163   a22 = (gint16) (p3 >> 48);
1164   a23 = (gint16) (p4 >> 48);
1165 
1166   for (i = 0; i < n; i++) {
1167     r = s1[i * 4 + 1];
1168     g = s1[i * 4 + 2];
1169     b = s1[i * 4 + 3];
1170 
1171     y = ((a00 * r + a01 * g + a02 * b) >> SCALE) + a03;
1172     u = ((a10 * r + a11 * g + a12 * b) >> SCALE) + a13;
1173     v = ((a20 * r + a21 * g + a22 * b) >> SCALE) + a23;
1174 
1175     d1[i * 4 + 1] = CLAMP (y, 0, 255);
1176     d1[i * 4 + 2] = CLAMP (u, 0, 255);
1177     d1[i * 4 + 3] = CLAMP (v, 0, 255);
1178   }
1179 }
1180 
1181 static void
video_converter_matrix8(MatrixData * data,gpointer pixels)1182 video_converter_matrix8 (MatrixData * data, gpointer pixels)
1183 {
1184   gpointer d = pixels;
1185   video_orc_matrix8 (d, pixels, data->orc_p1, data->orc_p2,
1186       data->orc_p3, data->orc_p4, data->width);
1187 }
1188 
1189 static void
video_converter_matrix8_table(MatrixData * data,gpointer pixels)1190 video_converter_matrix8_table (MatrixData * data, gpointer pixels)
1191 {
1192   gint i, width = data->width * 4;
1193   guint8 r, g, b;
1194   gint64 c = data->t_c;
1195   guint8 *p = pixels;
1196   gint64 x;
1197 
1198   for (i = 0; i < width; i += 4) {
1199     r = p[i + 1];
1200     g = p[i + 2];
1201     b = p[i + 3];
1202 
1203     x = data->t_r[r] + data->t_g[g] + data->t_b[b] + c;
1204 
1205     p[i + 1] = x >> (32 + SCALE);
1206     p[i + 2] = x >> (16 + SCALE);
1207     p[i + 3] = x >> (0 + SCALE);
1208   }
1209 }
1210 
1211 static void
video_converter_matrix8_AYUV_ARGB(MatrixData * data,gpointer pixels)1212 video_converter_matrix8_AYUV_ARGB (MatrixData * data, gpointer pixels)
1213 {
1214   gpointer d = pixels;
1215 
1216   video_orc_convert_AYUV_ARGB (d, 0, pixels, 0,
1217       data->im[0][0], data->im[0][2],
1218       data->im[2][1], data->im[1][1], data->im[1][2], data->width, 1);
1219 }
1220 
1221 static gboolean
is_ayuv_to_rgb_matrix(MatrixData * data)1222 is_ayuv_to_rgb_matrix (MatrixData * data)
1223 {
1224   if (data->im[0][0] != data->im[1][0] || data->im[1][0] != data->im[2][0])
1225     return FALSE;
1226 
1227   if (data->im[0][1] != 0 || data->im[2][2] != 0)
1228     return FALSE;
1229 
1230   return TRUE;
1231 }
1232 
1233 static gboolean
is_identity_matrix(MatrixData * data)1234 is_identity_matrix (MatrixData * data)
1235 {
1236   gint i, j;
1237   gint c = data->im[0][0];
1238 
1239   /* not really checking identity because of rounding errors but given
1240    * the conversions we do we just check for anything that looks like:
1241    *
1242    *  c 0 0 0
1243    *  0 c 0 0
1244    *  0 0 c 0
1245    *  0 0 0 1
1246    */
1247   for (i = 0; i < 4; i++) {
1248     for (j = 0; j < 4; j++) {
1249       if (i == j) {
1250         if (i == 3 && data->im[i][j] != 1)
1251           return FALSE;
1252         else if (data->im[i][j] != c)
1253           return FALSE;
1254       } else if (data->im[i][j] != 0)
1255         return FALSE;
1256     }
1257   }
1258   return TRUE;
1259 }
1260 
1261 static gboolean
is_no_clip_matrix(MatrixData * data)1262 is_no_clip_matrix (MatrixData * data)
1263 {
1264   gint i;
1265   static const guint8 test[8][3] = {
1266     {0, 0, 0},
1267     {0, 0, 255},
1268     {0, 255, 0},
1269     {0, 255, 255},
1270     {255, 0, 0},
1271     {255, 0, 255},
1272     {255, 255, 0},
1273     {255, 255, 255}
1274   };
1275 
1276   for (i = 0; i < 8; i++) {
1277     gint r, g, b;
1278     gint y, u, v;
1279 
1280     r = test[i][0];
1281     g = test[i][1];
1282     b = test[i][2];
1283 
1284     y = (data->im[0][0] * r + data->im[0][1] * g +
1285         data->im[0][2] * b + data->im[0][3]) >> SCALE;
1286     u = (data->im[1][0] * r + data->im[1][1] * g +
1287         data->im[1][2] * b + data->im[1][3]) >> SCALE;
1288     v = (data->im[2][0] * r + data->im[2][1] * g +
1289         data->im[2][2] * b + data->im[2][3]) >> SCALE;
1290 
1291     if (y != CLAMP (y, 0, 255) || u != CLAMP (u, 0, 255)
1292         || v != CLAMP (v, 0, 255))
1293       return FALSE;
1294   }
1295   return TRUE;
1296 }
1297 
1298 static void
video_converter_matrix16(MatrixData * data,gpointer pixels)1299 video_converter_matrix16 (MatrixData * data, gpointer pixels)
1300 {
1301   int i;
1302   int r, g, b;
1303   int y, u, v;
1304   guint16 *p = pixels;
1305   gint width = data->width;
1306 
1307   for (i = 0; i < width; i++) {
1308     r = p[i * 4 + 1];
1309     g = p[i * 4 + 2];
1310     b = p[i * 4 + 3];
1311 
1312     y = (data->im[0][0] * r + data->im[0][1] * g +
1313         data->im[0][2] * b + data->im[0][3]) >> SCALE;
1314     u = (data->im[1][0] * r + data->im[1][1] * g +
1315         data->im[1][2] * b + data->im[1][3]) >> SCALE;
1316     v = (data->im[2][0] * r + data->im[2][1] * g +
1317         data->im[2][2] * b + data->im[2][3]) >> SCALE;
1318 
1319     p[i * 4 + 1] = CLAMP (y, 0, 65535);
1320     p[i * 4 + 2] = CLAMP (u, 0, 65535);
1321     p[i * 4 + 3] = CLAMP (v, 0, 65535);
1322   }
1323 }
1324 
1325 
1326 static void
prepare_matrix(GstVideoConverter * convert,MatrixData * data)1327 prepare_matrix (GstVideoConverter * convert, MatrixData * data)
1328 {
1329   if (is_identity_matrix (data))
1330     return;
1331 
1332   color_matrix_scale_components (data, SCALE_F, SCALE_F, SCALE_F);
1333   color_matrix_convert (data);
1334 
1335   data->width = convert->current_width;
1336 
1337   if (convert->current_bits == 8) {
1338     if (!convert->unpack_rgb && convert->pack_rgb
1339         && is_ayuv_to_rgb_matrix (data)) {
1340       GST_DEBUG ("use fast AYUV -> RGB matrix");
1341       data->matrix_func = video_converter_matrix8_AYUV_ARGB;
1342     } else if (is_no_clip_matrix (data)) {
1343       GST_DEBUG ("use 8bit table");
1344       data->matrix_func = video_converter_matrix8_table;
1345       videoconvert_convert_init_tables (data);
1346     } else {
1347       gint a03, a13, a23;
1348 
1349       GST_DEBUG ("use 8bit matrix");
1350       data->matrix_func = video_converter_matrix8;
1351 
1352       data->orc_p1 = (((guint64) (guint16) data->im[2][0]) << 48) |
1353           (((guint64) (guint16) data->im[1][0]) << 32) |
1354           (((guint64) (guint16) data->im[0][0]) << 16);
1355       data->orc_p2 = (((guint64) (guint16) data->im[2][1]) << 48) |
1356           (((guint64) (guint16) data->im[1][1]) << 32) |
1357           (((guint64) (guint16) data->im[0][1]) << 16);
1358       data->orc_p3 = (((guint64) (guint16) data->im[2][2]) << 48) |
1359           (((guint64) (guint16) data->im[1][2]) << 32) |
1360           (((guint64) (guint16) data->im[0][2]) << 16);
1361 
1362       a03 = data->im[0][3] >> SCALE;
1363       a13 = data->im[1][3] >> SCALE;
1364       a23 = data->im[2][3] >> SCALE;
1365 
1366       data->orc_p4 = (((guint64) (guint16) a23) << 48) |
1367           (((guint64) (guint16) a13) << 32) | (((guint64) (guint16) a03) << 16);
1368     }
1369   } else {
1370     GST_DEBUG ("use 16bit matrix");
1371     data->matrix_func = video_converter_matrix16;
1372   }
1373 }
1374 
1375 static void
compute_matrix_to_RGB(GstVideoConverter * convert,MatrixData * data)1376 compute_matrix_to_RGB (GstVideoConverter * convert, MatrixData * data)
1377 {
1378   GstVideoInfo *info;
1379   gdouble Kr = 0, Kb = 0;
1380 
1381   info = &convert->in_info;
1382 
1383   {
1384     const GstVideoFormatInfo *uinfo;
1385     gint offset[4], scale[4];
1386 
1387     uinfo = gst_video_format_get_info (convert->unpack_format);
1388 
1389     /* bring color components to [0..1.0] range */
1390     gst_video_color_range_offsets (info->colorimetry.range, uinfo, offset,
1391         scale);
1392 
1393     color_matrix_offset_components (data, -offset[0], -offset[1], -offset[2]);
1394     color_matrix_scale_components (data, 1 / ((float) scale[0]),
1395         1 / ((float) scale[1]), 1 / ((float) scale[2]));
1396   }
1397 
1398   if (!convert->unpack_rgb && !CHECK_MATRIX_NONE (convert)) {
1399     if (CHECK_MATRIX_OUTPUT (convert))
1400       info = &convert->out_info;
1401 
1402     /* bring components to R'G'B' space */
1403     if (gst_video_color_matrix_get_Kr_Kb (info->colorimetry.matrix, &Kr, &Kb))
1404       color_matrix_YCbCr_to_RGB (data, Kr, Kb);
1405   }
1406   color_matrix_debug (data);
1407 }
1408 
1409 static void
compute_matrix_to_YUV(GstVideoConverter * convert,MatrixData * data,gboolean force)1410 compute_matrix_to_YUV (GstVideoConverter * convert, MatrixData * data,
1411     gboolean force)
1412 {
1413   GstVideoInfo *info;
1414   gdouble Kr = 0, Kb = 0;
1415 
1416   if (force || (!convert->pack_rgb && !CHECK_MATRIX_NONE (convert))) {
1417     if (CHECK_MATRIX_INPUT (convert))
1418       info = &convert->in_info;
1419     else
1420       info = &convert->out_info;
1421 
1422     /* bring components to YCbCr space */
1423     if (gst_video_color_matrix_get_Kr_Kb (info->colorimetry.matrix, &Kr, &Kb))
1424       color_matrix_RGB_to_YCbCr (data, Kr, Kb);
1425   }
1426 
1427   info = &convert->out_info;
1428 
1429   {
1430     const GstVideoFormatInfo *uinfo;
1431     gint offset[4], scale[4];
1432 
1433     uinfo = gst_video_format_get_info (convert->pack_format);
1434 
1435     /* bring color components to nominal range */
1436     gst_video_color_range_offsets (info->colorimetry.range, uinfo, offset,
1437         scale);
1438 
1439     color_matrix_scale_components (data, (float) scale[0], (float) scale[1],
1440         (float) scale[2]);
1441     color_matrix_offset_components (data, offset[0], offset[1], offset[2]);
1442   }
1443 
1444   color_matrix_debug (data);
1445 }
1446 
1447 
1448 static void
gamma_convert_u8_u16(GammaData * data,gpointer dest,gpointer src)1449 gamma_convert_u8_u16 (GammaData * data, gpointer dest, gpointer src)
1450 {
1451   gint i;
1452   guint8 *s = src;
1453   guint16 *d = dest;
1454   guint16 *table = data->gamma_table;
1455   gint width = data->width * 4;
1456 
1457   for (i = 0; i < width; i += 4) {
1458     d[i + 0] = (s[i] << 8) | s[i];
1459     d[i + 1] = table[s[i + 1]];
1460     d[i + 2] = table[s[i + 2]];
1461     d[i + 3] = table[s[i + 3]];
1462   }
1463 }
1464 
1465 static void
gamma_convert_u16_u8(GammaData * data,gpointer dest,gpointer src)1466 gamma_convert_u16_u8 (GammaData * data, gpointer dest, gpointer src)
1467 {
1468   gint i;
1469   guint16 *s = src;
1470   guint8 *d = dest;
1471   guint8 *table = data->gamma_table;
1472   gint width = data->width * 4;
1473 
1474   for (i = 0; i < width; i += 4) {
1475     d[i + 0] = s[i] >> 8;
1476     d[i + 1] = table[s[i + 1]];
1477     d[i + 2] = table[s[i + 2]];
1478     d[i + 3] = table[s[i + 3]];
1479   }
1480 }
1481 
1482 static void
gamma_convert_u16_u16(GammaData * data,gpointer dest,gpointer src)1483 gamma_convert_u16_u16 (GammaData * data, gpointer dest, gpointer src)
1484 {
1485   gint i;
1486   guint16 *s = src;
1487   guint16 *d = dest;
1488   guint16 *table = data->gamma_table;
1489   gint width = data->width * 4;
1490 
1491   for (i = 0; i < width; i += 4) {
1492     d[i + 0] = s[i];
1493     d[i + 1] = table[s[i + 1]];
1494     d[i + 2] = table[s[i + 2]];
1495     d[i + 3] = table[s[i + 3]];
1496   }
1497 }
1498 
1499 static void
setup_gamma_decode(GstVideoConverter * convert)1500 setup_gamma_decode (GstVideoConverter * convert)
1501 {
1502   GstVideoTransferFunction func;
1503   guint16 *t;
1504   gint i;
1505 
1506   func = convert->in_info.colorimetry.transfer;
1507 
1508   convert->gamma_dec.width = convert->current_width;
1509   if (convert->gamma_dec.gamma_table) {
1510     GST_DEBUG ("gamma decode already set up");
1511   } else if (convert->current_bits == 8) {
1512     GST_DEBUG ("gamma decode 8->16: %d", func);
1513     convert->gamma_dec.gamma_func = gamma_convert_u8_u16;
1514     t = convert->gamma_dec.gamma_table = g_malloc (sizeof (guint16) * 256);
1515 
1516     for (i = 0; i < 256; i++)
1517       t[i] =
1518           rint (gst_video_transfer_function_decode (func, i / 255.0) * 65535.0);
1519   } else {
1520     GST_DEBUG ("gamma decode 16->16: %d", func);
1521     convert->gamma_dec.gamma_func = gamma_convert_u16_u16;
1522     t = convert->gamma_dec.gamma_table = g_malloc (sizeof (guint16) * 65536);
1523 
1524     for (i = 0; i < 65536; i++)
1525       t[i] =
1526           rint (gst_video_transfer_function_decode (func,
1527               i / 65535.0) * 65535.0);
1528   }
1529   convert->current_bits = 16;
1530   convert->current_pstride = 8;
1531   convert->current_format = GST_VIDEO_FORMAT_ARGB64;
1532 }
1533 
1534 static void
setup_gamma_encode(GstVideoConverter * convert,gint target_bits)1535 setup_gamma_encode (GstVideoConverter * convert, gint target_bits)
1536 {
1537   GstVideoTransferFunction func;
1538   gint i;
1539 
1540   func = convert->out_info.colorimetry.transfer;
1541 
1542   convert->gamma_enc.width = convert->current_width;
1543   if (convert->gamma_enc.gamma_table) {
1544     GST_DEBUG ("gamma encode already set up");
1545   } else if (target_bits == 8) {
1546     guint8 *t;
1547 
1548     GST_DEBUG ("gamma encode 16->8: %d", func);
1549     convert->gamma_enc.gamma_func = gamma_convert_u16_u8;
1550     t = convert->gamma_enc.gamma_table = g_malloc (sizeof (guint8) * 65536);
1551 
1552     for (i = 0; i < 65536; i++)
1553       t[i] =
1554           rint (gst_video_transfer_function_encode (func, i / 65535.0) * 255.0);
1555   } else {
1556     guint16 *t;
1557 
1558     GST_DEBUG ("gamma encode 16->16: %d", func);
1559     convert->gamma_enc.gamma_func = gamma_convert_u16_u16;
1560     t = convert->gamma_enc.gamma_table = g_malloc (sizeof (guint16) * 65536);
1561 
1562     for (i = 0; i < 65536; i++)
1563       t[i] =
1564           rint (gst_video_transfer_function_encode (func,
1565               i / 65535.0) * 65535.0);
1566   }
1567 }
1568 
1569 static GstLineCache *
chain_convert_to_RGB(GstVideoConverter * convert,GstLineCache * prev,gint idx)1570 chain_convert_to_RGB (GstVideoConverter * convert, GstLineCache * prev,
1571     gint idx)
1572 {
1573   gboolean do_gamma;
1574 
1575   do_gamma = CHECK_GAMMA_REMAP (convert);
1576 
1577   if (do_gamma) {
1578     gint scale;
1579 
1580     /* Set up conversion matrices if needed, but only for the first thread */
1581     if (idx == 0 && !convert->unpack_rgb) {
1582       color_matrix_set_identity (&convert->to_RGB_matrix);
1583       compute_matrix_to_RGB (convert, &convert->to_RGB_matrix);
1584 
1585       /* matrix is in 0..1 range, scale to current bits */
1586       GST_DEBUG ("chain RGB convert");
1587       scale = 1 << convert->current_bits;
1588       color_matrix_scale_components (&convert->to_RGB_matrix,
1589           (float) scale, (float) scale, (float) scale);
1590 
1591       prepare_matrix (convert, &convert->to_RGB_matrix);
1592 
1593       if (convert->current_bits == 8)
1594         convert->current_format = GST_VIDEO_FORMAT_ARGB;
1595       else
1596         convert->current_format = GST_VIDEO_FORMAT_ARGB64;
1597     }
1598 
1599     prev = convert->to_RGB_lines[idx] = gst_line_cache_new (prev);
1600     prev->write_input = TRUE;
1601     prev->pass_alloc = FALSE;
1602     prev->n_lines = 1;
1603     prev->stride = convert->current_pstride * convert->current_width;
1604     gst_line_cache_set_need_line_func (prev,
1605         do_convert_to_RGB_lines, idx, convert, NULL);
1606 
1607     GST_DEBUG ("chain gamma decode");
1608     setup_gamma_decode (convert);
1609   }
1610   return prev;
1611 }
1612 
1613 static GstLineCache *
chain_hscale(GstVideoConverter * convert,GstLineCache * prev,gint idx)1614 chain_hscale (GstVideoConverter * convert, GstLineCache * prev, gint idx)
1615 {
1616   gint method;
1617   guint taps;
1618 
1619   method = GET_OPT_RESAMPLER_METHOD (convert);
1620   taps = GET_OPT_RESAMPLER_TAPS (convert);
1621 
1622   convert->h_scaler[idx] =
1623       gst_video_scaler_new (method, GST_VIDEO_SCALER_FLAG_NONE, taps,
1624       convert->in_width, convert->out_width, convert->config);
1625 
1626   gst_video_scaler_get_coeff (convert->h_scaler[idx], 0, NULL, &taps);
1627 
1628   GST_DEBUG ("chain hscale %d->%d, taps %d, method %d",
1629       convert->in_width, convert->out_width, taps, method);
1630 
1631   convert->current_width = convert->out_width;
1632   convert->h_scale_format = convert->current_format;
1633 
1634   prev = convert->hscale_lines[idx] = gst_line_cache_new (prev);
1635   prev->write_input = FALSE;
1636   prev->pass_alloc = FALSE;
1637   prev->n_lines = 1;
1638   prev->stride = convert->current_pstride * convert->current_width;
1639   gst_line_cache_set_need_line_func (prev, do_hscale_lines, idx, convert, NULL);
1640 
1641   return prev;
1642 }
1643 
1644 static GstLineCache *
chain_vscale(GstVideoConverter * convert,GstLineCache * prev,gint idx)1645 chain_vscale (GstVideoConverter * convert, GstLineCache * prev, gint idx)
1646 {
1647   gint method;
1648   guint taps, taps_i = 0;
1649   gint backlog = 0;
1650 
1651   method = GET_OPT_RESAMPLER_METHOD (convert);
1652   taps = GET_OPT_RESAMPLER_TAPS (convert);
1653 
1654   if (GST_VIDEO_INFO_IS_INTERLACED (&convert->in_info)
1655       && (GST_VIDEO_INFO_INTERLACE_MODE (&convert->in_info) !=
1656           GST_VIDEO_INTERLACE_MODE_ALTERNATE)) {
1657     convert->v_scaler_i[idx] =
1658         gst_video_scaler_new (method, GST_VIDEO_SCALER_FLAG_INTERLACED, taps,
1659         convert->in_height, convert->out_height, convert->config);
1660 
1661     gst_video_scaler_get_coeff (convert->v_scaler_i[idx], 0, NULL, &taps_i);
1662     backlog = taps_i;
1663   }
1664   convert->v_scaler_p[idx] =
1665       gst_video_scaler_new (method, 0, taps, convert->in_height,
1666       convert->out_height, convert->config);
1667   convert->v_scale_width = convert->current_width;
1668   convert->v_scale_format = convert->current_format;
1669   convert->current_height = convert->out_height;
1670 
1671   gst_video_scaler_get_coeff (convert->v_scaler_p[idx], 0, NULL, &taps);
1672 
1673   GST_DEBUG ("chain vscale %d->%d, taps %d, method %d, backlog %d",
1674       convert->in_height, convert->out_height, taps, method, backlog);
1675 
1676   prev->backlog = backlog;
1677   prev = convert->vscale_lines[idx] = gst_line_cache_new (prev);
1678   prev->pass_alloc = (taps == 1);
1679   prev->write_input = FALSE;
1680   prev->n_lines = MAX (taps_i, taps);
1681   prev->stride = convert->current_pstride * convert->current_width;
1682   gst_line_cache_set_need_line_func (prev, do_vscale_lines, idx, convert, NULL);
1683 
1684   return prev;
1685 }
1686 
1687 static GstLineCache *
chain_scale(GstVideoConverter * convert,GstLineCache * prev,gboolean force,gint idx)1688 chain_scale (GstVideoConverter * convert, GstLineCache * prev, gboolean force,
1689     gint idx)
1690 {
1691   gint s0, s1, s2, s3;
1692 
1693   s0 = convert->current_width * convert->current_height;
1694   s3 = convert->out_width * convert->out_height;
1695 
1696   GST_DEBUG ("in pixels %d <> out pixels %d", s0, s3);
1697 
1698   if (s3 <= s0 || force) {
1699     /* we are making the image smaller or are forced to resample */
1700     s1 = convert->out_width * convert->current_height;
1701     s2 = convert->current_width * convert->out_height;
1702 
1703     GST_DEBUG ("%d <> %d", s1, s2);
1704 
1705     if (s1 <= s2) {
1706       /* h scaling first produces less pixels */
1707       if (convert->current_width != convert->out_width)
1708         prev = chain_hscale (convert, prev, idx);
1709       if (convert->current_height != convert->out_height)
1710         prev = chain_vscale (convert, prev, idx);
1711     } else {
1712       /* v scaling first produces less pixels */
1713       if (convert->current_height != convert->out_height)
1714         prev = chain_vscale (convert, prev, idx);
1715       if (convert->current_width != convert->out_width)
1716         prev = chain_hscale (convert, prev, idx);
1717     }
1718   }
1719   return prev;
1720 }
1721 
1722 static GstLineCache *
chain_convert(GstVideoConverter * convert,GstLineCache * prev,gint idx)1723 chain_convert (GstVideoConverter * convert, GstLineCache * prev, gint idx)
1724 {
1725   gboolean do_gamma, do_conversion, pass_alloc = FALSE;
1726   gboolean same_matrix, same_primaries, same_bits;
1727   MatrixData p1, p2;
1728 
1729   same_bits = convert->unpack_bits == convert->pack_bits;
1730   if (CHECK_MATRIX_NONE (convert)) {
1731     same_matrix = TRUE;
1732   } else {
1733     same_matrix =
1734         convert->in_info.colorimetry.matrix ==
1735         convert->out_info.colorimetry.matrix;
1736   }
1737 
1738   if (CHECK_PRIMARIES_NONE (convert)) {
1739     same_primaries = TRUE;
1740   } else {
1741     same_primaries =
1742         convert->in_info.colorimetry.primaries ==
1743         convert->out_info.colorimetry.primaries;
1744   }
1745 
1746   GST_DEBUG ("matrix %d -> %d (%d)", convert->in_info.colorimetry.matrix,
1747       convert->out_info.colorimetry.matrix, same_matrix);
1748   GST_DEBUG ("bits %d -> %d (%d)", convert->unpack_bits, convert->pack_bits,
1749       same_bits);
1750   GST_DEBUG ("primaries %d -> %d (%d)", convert->in_info.colorimetry.primaries,
1751       convert->out_info.colorimetry.primaries, same_primaries);
1752 
1753   color_matrix_set_identity (&convert->convert_matrix);
1754 
1755   if (!same_primaries) {
1756     const GstVideoColorPrimariesInfo *pi;
1757 
1758     /* Convert from RGB_input to RGB_output via XYZ
1759      *    res = XYZ_to_RGB_output ( RGB_to_XYZ_input ( input ) )
1760      * or in matricial form:
1761      *    RGB_output = XYZ_to_RGB_output_matrix * RGB_TO_XYZ_input_matrix * RGB_input
1762      *
1763      * The RGB_input is the pre-existing convert_matrix
1764      * The convert_matrix will become the RGB_output
1765      */
1766 
1767     /* Convert input RGB to XYZ */
1768     pi = gst_video_color_primaries_get_info (convert->in_info.colorimetry.
1769         primaries);
1770     /* Get the RGB_TO_XYZ_input_matrix */
1771     color_matrix_RGB_to_XYZ (&p1, pi->Rx, pi->Ry, pi->Gx, pi->Gy, pi->Bx,
1772         pi->By, pi->Wx, pi->Wy);
1773     GST_DEBUG ("to XYZ matrix");
1774     color_matrix_debug (&p1);
1775     GST_DEBUG ("current matrix");
1776     /* convert_matrix = RGB_TO_XYZ_input_matrix * input_RGB */
1777     color_matrix_multiply (&convert->convert_matrix, &convert->convert_matrix,
1778         &p1);
1779     color_matrix_debug (&convert->convert_matrix);
1780 
1781     /* Convert XYZ to output RGB */
1782     pi = gst_video_color_primaries_get_info (convert->out_info.colorimetry.
1783         primaries);
1784     /* Calculate the XYZ_to_RGB_output_matrix
1785      *  * Get the RGB_TO_XYZ_output_matrix
1786      *  * invert it
1787      *  * store in p2
1788      */
1789     color_matrix_RGB_to_XYZ (&p2, pi->Rx, pi->Ry, pi->Gx, pi->Gy, pi->Bx,
1790         pi->By, pi->Wx, pi->Wy);
1791     color_matrix_invert (&p2, &p2);
1792     GST_DEBUG ("to RGB matrix");
1793     color_matrix_debug (&p2);
1794     /* Finally:
1795      * convert_matrix = XYZ_to_RGB_output_matrix * RGB_TO_XYZ_input_matrix * RGB_input
1796      *                = XYZ_to_RGB_output_matrix * convert_matrix
1797      *                = p2 * convert_matrix
1798      */
1799     color_matrix_multiply (&convert->convert_matrix, &p2,
1800         &convert->convert_matrix);
1801     GST_DEBUG ("current matrix");
1802     color_matrix_debug (&convert->convert_matrix);
1803   }
1804 
1805   do_gamma = CHECK_GAMMA_REMAP (convert);
1806   if (!do_gamma) {
1807 
1808     convert->in_bits = convert->unpack_bits;
1809     convert->out_bits = convert->pack_bits;
1810 
1811     if (!same_bits || !same_matrix || !same_primaries) {
1812       /* no gamma, combine all conversions into 1 */
1813       if (convert->in_bits < convert->out_bits) {
1814         gint scale = 1 << (convert->out_bits - convert->in_bits);
1815         color_matrix_scale_components (&convert->convert_matrix,
1816             1 / (float) scale, 1 / (float) scale, 1 / (float) scale);
1817       }
1818       GST_DEBUG ("to RGB matrix");
1819       compute_matrix_to_RGB (convert, &convert->convert_matrix);
1820       GST_DEBUG ("current matrix");
1821       color_matrix_debug (&convert->convert_matrix);
1822 
1823       GST_DEBUG ("to YUV matrix");
1824       compute_matrix_to_YUV (convert, &convert->convert_matrix, FALSE);
1825       GST_DEBUG ("current matrix");
1826       color_matrix_debug (&convert->convert_matrix);
1827       if (convert->in_bits > convert->out_bits) {
1828         gint scale = 1 << (convert->in_bits - convert->out_bits);
1829         color_matrix_scale_components (&convert->convert_matrix,
1830             (float) scale, (float) scale, (float) scale);
1831       }
1832       convert->current_bits = MAX (convert->in_bits, convert->out_bits);
1833 
1834       do_conversion = TRUE;
1835       if (!same_matrix || !same_primaries) {
1836         if (idx == 0)
1837           prepare_matrix (convert, &convert->convert_matrix);
1838       }
1839       if (convert->in_bits == convert->out_bits)
1840         pass_alloc = TRUE;
1841     } else
1842       do_conversion = FALSE;
1843 
1844     convert->current_bits = convert->pack_bits;
1845     convert->current_format = convert->pack_format;
1846     convert->current_pstride = convert->current_bits >> 1;
1847   } else {
1848     /* we did gamma, just do colorspace conversion if needed */
1849     if (same_primaries) {
1850       do_conversion = FALSE;
1851     } else {
1852       if (idx == 0)
1853         prepare_matrix (convert, &convert->convert_matrix);
1854       convert->in_bits = convert->out_bits = 16;
1855       pass_alloc = TRUE;
1856       do_conversion = TRUE;
1857     }
1858   }
1859 
1860   if (do_conversion) {
1861     GST_DEBUG ("chain conversion");
1862     prev = convert->convert_lines[idx] = gst_line_cache_new (prev);
1863     prev->write_input = TRUE;
1864     prev->pass_alloc = pass_alloc;
1865     prev->n_lines = 1;
1866     prev->stride = convert->current_pstride * convert->current_width;
1867     gst_line_cache_set_need_line_func (prev,
1868         do_convert_lines, idx, convert, NULL);
1869   }
1870   return prev;
1871 }
1872 
1873 static void
convert_set_alpha_u8(GstVideoConverter * convert,gpointer pixels,gint width)1874 convert_set_alpha_u8 (GstVideoConverter * convert, gpointer pixels, gint width)
1875 {
1876   guint8 *p = pixels;
1877   guint8 alpha = MIN (convert->alpha_value, 255);
1878   int i;
1879 
1880   for (i = 0; i < width; i++)
1881     p[i * 4] = alpha;
1882 }
1883 
1884 static void
convert_set_alpha_u16(GstVideoConverter * convert,gpointer pixels,gint width)1885 convert_set_alpha_u16 (GstVideoConverter * convert, gpointer pixels, gint width)
1886 {
1887   guint16 *p = pixels;
1888   guint16 alpha;
1889   int i;
1890 
1891   alpha = MIN (convert->alpha_value, 255);
1892   alpha |= alpha << 8;
1893 
1894   for (i = 0; i < width; i++)
1895     p[i * 4] = alpha;
1896 }
1897 
1898 static void
convert_mult_alpha_u8(GstVideoConverter * convert,gpointer pixels,gint width)1899 convert_mult_alpha_u8 (GstVideoConverter * convert, gpointer pixels, gint width)
1900 {
1901   guint8 *p = pixels;
1902   guint alpha = convert->alpha_value;
1903   int i;
1904 
1905   for (i = 0; i < width; i++) {
1906     gint a = (p[i * 4] * alpha) / 255;
1907     p[i * 4] = CLAMP (a, 0, 255);
1908   }
1909 }
1910 
1911 static void
convert_mult_alpha_u16(GstVideoConverter * convert,gpointer pixels,gint width)1912 convert_mult_alpha_u16 (GstVideoConverter * convert, gpointer pixels,
1913     gint width)
1914 {
1915   guint16 *p = pixels;
1916   guint alpha = convert->alpha_value;
1917   int i;
1918 
1919   for (i = 0; i < width; i++) {
1920     gint a = (p[i * 4] * alpha) / 255;
1921     p[i * 4] = CLAMP (a, 0, 65535);
1922   }
1923 }
1924 
1925 static GstLineCache *
chain_alpha(GstVideoConverter * convert,GstLineCache * prev,gint idx)1926 chain_alpha (GstVideoConverter * convert, GstLineCache * prev, gint idx)
1927 {
1928   switch (convert->alpha_mode) {
1929     case ALPHA_MODE_NONE:
1930     case ALPHA_MODE_COPY:
1931       return prev;
1932 
1933     case ALPHA_MODE_SET:
1934       if (convert->current_bits == 8)
1935         convert->alpha_func = convert_set_alpha_u8;
1936       else
1937         convert->alpha_func = convert_set_alpha_u16;
1938       break;
1939     case ALPHA_MODE_MULT:
1940       if (convert->current_bits == 8)
1941         convert->alpha_func = convert_mult_alpha_u8;
1942       else
1943         convert->alpha_func = convert_mult_alpha_u16;
1944       break;
1945   }
1946 
1947   GST_DEBUG ("chain alpha mode %d", convert->alpha_mode);
1948   prev = convert->alpha_lines[idx] = gst_line_cache_new (prev);
1949   prev->write_input = TRUE;
1950   prev->pass_alloc = TRUE;
1951   prev->n_lines = 1;
1952   prev->stride = convert->current_pstride * convert->current_width;
1953   gst_line_cache_set_need_line_func (prev, do_alpha_lines, idx, convert, NULL);
1954 
1955   return prev;
1956 }
1957 
1958 static GstLineCache *
chain_convert_to_YUV(GstVideoConverter * convert,GstLineCache * prev,gint idx)1959 chain_convert_to_YUV (GstVideoConverter * convert, GstLineCache * prev,
1960     gint idx)
1961 {
1962   gboolean do_gamma;
1963 
1964   do_gamma = CHECK_GAMMA_REMAP (convert);
1965 
1966   if (do_gamma) {
1967     gint scale;
1968 
1969     GST_DEBUG ("chain gamma encode");
1970     setup_gamma_encode (convert, convert->pack_bits);
1971 
1972     convert->current_bits = convert->pack_bits;
1973     convert->current_pstride = convert->current_bits >> 1;
1974 
1975     if (idx == 0 && !convert->pack_rgb) {
1976       color_matrix_set_identity (&convert->to_YUV_matrix);
1977 
1978       /* When gamma remap is enabled, we do
1979        * 1) converts to ARGB64 linear RGB
1980        *   - if input is 8bits, convert to ARGB and scaled to 16bits with gamma
1981        *     decoding at once
1982        *   - otherwise converted ARGB64 and gamma decoded
1983        * 2) scale/convert etc,
1984        * 3) and gamma encode
1985        *
1986        * So source data to the do_convert_to_YUV_lines() method is always
1987        * ARGB64
1988        *
1989        * Then, if output unpack format is 8bits, setup_gamma_encode() will scale
1990        * ARGB64 down to ARGB as a part of gamma encoding, otherwise it's still
1991        * ARGB64
1992        *
1993        * Finally this to_YUV_matrix is applied. Since compute_matrix_to_YUV()
1994        * expects [0, 1.0] range RGB as an input, scale down identity matrix
1995        * to expected scale here, otherwise offset of the matrix would be
1996        * very wrong
1997        */
1998       GST_DEBUG ("chain YUV convert");
1999       scale = 1 << convert->pack_bits;
2000       color_matrix_scale_components (&convert->to_YUV_matrix,
2001           1 / (float) scale, 1 / (float) scale, 1 / (float) scale);
2002 
2003       compute_matrix_to_YUV (convert, &convert->to_YUV_matrix, FALSE);
2004       prepare_matrix (convert, &convert->to_YUV_matrix);
2005     }
2006     convert->current_format = convert->pack_format;
2007 
2008     prev = convert->to_YUV_lines[idx] = gst_line_cache_new (prev);
2009     prev->write_input = FALSE;
2010     prev->pass_alloc = FALSE;
2011     prev->n_lines = 1;
2012     prev->stride = convert->current_pstride * convert->current_width;
2013     gst_line_cache_set_need_line_func (prev,
2014         do_convert_to_YUV_lines, idx, convert, NULL);
2015   }
2016 
2017   return prev;
2018 }
2019 
2020 static GstLineCache *
chain_downsample(GstVideoConverter * convert,GstLineCache * prev,gint idx)2021 chain_downsample (GstVideoConverter * convert, GstLineCache * prev, gint idx)
2022 {
2023   if (convert->downsample_p[idx] || convert->downsample_i[idx]) {
2024     GST_DEBUG ("chain downsample");
2025     prev = convert->downsample_lines[idx] = gst_line_cache_new (prev);
2026     prev->write_input = TRUE;
2027     prev->pass_alloc = TRUE;
2028     /* XXX: why this hardcoded value? */
2029     prev->n_lines = 5;
2030     prev->stride = convert->current_pstride * convert->current_width;
2031     gst_line_cache_set_need_line_func (prev,
2032         do_downsample_lines, idx, convert, NULL);
2033   }
2034   return prev;
2035 }
2036 
2037 static GstLineCache *
chain_dither(GstVideoConverter * convert,GstLineCache * prev,gint idx)2038 chain_dither (GstVideoConverter * convert, GstLineCache * prev, gint idx)
2039 {
2040   gint i;
2041   gboolean do_dither = FALSE;
2042   GstVideoDitherFlags flags = 0;
2043   GstVideoDitherMethod method;
2044   guint quant[4], target_quant;
2045 
2046   method = GET_OPT_DITHER_METHOD (convert);
2047   if (method == GST_VIDEO_DITHER_NONE)
2048     return prev;
2049 
2050   target_quant = GET_OPT_DITHER_QUANTIZATION (convert);
2051   GST_DEBUG ("method %d, target-quantization %d", method, target_quant);
2052 
2053   if (convert->pack_pal) {
2054     quant[0] = 47;
2055     quant[1] = 47;
2056     quant[2] = 47;
2057     quant[3] = 1;
2058     do_dither = TRUE;
2059   } else {
2060     for (i = 0; i < GST_VIDEO_MAX_COMPONENTS; i++) {
2061       gint depth;
2062 
2063       depth = convert->out_info.finfo->depth[i];
2064 
2065       if (depth == 0) {
2066         quant[i] = 0;
2067         continue;
2068       }
2069 
2070       if (convert->current_bits >= depth) {
2071         quant[i] = 1 << (convert->current_bits - depth);
2072         if (target_quant > quant[i]) {
2073           flags |= GST_VIDEO_DITHER_FLAG_QUANTIZE;
2074           quant[i] = target_quant;
2075         }
2076       } else {
2077         quant[i] = 0;
2078       }
2079       if (quant[i] > 1)
2080         do_dither = TRUE;
2081     }
2082   }
2083 
2084   if (do_dither) {
2085     GST_DEBUG ("chain dither");
2086 
2087     convert->dither[idx] = gst_video_dither_new (method,
2088         flags, convert->pack_format, quant, convert->current_width);
2089 
2090     prev = convert->dither_lines[idx] = gst_line_cache_new (prev);
2091     prev->write_input = TRUE;
2092     prev->pass_alloc = TRUE;
2093     prev->n_lines = 1;
2094     prev->stride = convert->current_pstride * convert->current_width;
2095     gst_line_cache_set_need_line_func (prev, do_dither_lines, idx, convert,
2096         NULL);
2097   }
2098   return prev;
2099 }
2100 
2101 static GstLineCache *
chain_pack(GstVideoConverter * convert,GstLineCache * prev,gint idx)2102 chain_pack (GstVideoConverter * convert, GstLineCache * prev, gint idx)
2103 {
2104   convert->pack_nlines = convert->out_info.finfo->pack_lines;
2105   convert->pack_pstride = convert->current_pstride;
2106   convert->identity_pack =
2107       (convert->out_info.finfo->format ==
2108       convert->out_info.finfo->unpack_format);
2109   GST_DEBUG ("chain pack line format %s, pstride %d, identity_pack %d (%d %d)",
2110       gst_video_format_to_string (convert->current_format),
2111       convert->current_pstride, convert->identity_pack,
2112       convert->out_info.finfo->format, convert->out_info.finfo->unpack_format);
2113 
2114   return prev;
2115 }
2116 
2117 static void
setup_allocators(GstVideoConverter * convert)2118 setup_allocators (GstVideoConverter * convert)
2119 {
2120   GstLineCache *cache, *prev;
2121   GstLineCacheAllocLineFunc alloc_line;
2122   gboolean alloc_writable;
2123   gpointer user_data;
2124   GDestroyNotify notify;
2125   gint width;
2126   gint i;
2127 
2128   width = MAX (convert->in_maxwidth, convert->out_maxwidth);
2129   width += convert->out_x;
2130 
2131   for (i = 0; i < convert->conversion_runner->n_threads; i++) {
2132     /* start with using dest lines if we can directly write into it */
2133     if (convert->identity_pack) {
2134       alloc_line = get_dest_line;
2135       alloc_writable = TRUE;
2136       user_data = convert;
2137       notify = NULL;
2138     } else {
2139       user_data =
2140           converter_alloc_new (sizeof (guint16) * width * 4, 4 + BACKLOG,
2141           convert, NULL);
2142       setup_border_alloc (convert, user_data);
2143       notify = (GDestroyNotify) converter_alloc_free;
2144       alloc_line = get_border_temp_line;
2145       /* when we add a border, we need to write */
2146       alloc_writable = convert->borderline != NULL;
2147     }
2148 
2149     /* First step, try to calculate how many temp lines we need. Go backwards,
2150      * keep track of the maximum number of lines we need for each intermediate
2151      * step.  */
2152     for (prev = cache = convert->pack_lines[i]; cache; cache = cache->prev) {
2153       GST_DEBUG ("looking at cache %p, %d lines, %d backlog", cache,
2154           cache->n_lines, cache->backlog);
2155       prev->n_lines = MAX (prev->n_lines, cache->n_lines);
2156       if (!cache->pass_alloc) {
2157         GST_DEBUG ("cache %p, needs %d lines", prev, prev->n_lines);
2158         prev = cache;
2159       }
2160     }
2161 
2162     /* now walk backwards, we try to write into the dest lines directly
2163      * and keep track if the source needs to be writable */
2164     for (cache = convert->pack_lines[i]; cache; cache = cache->prev) {
2165       gst_line_cache_set_alloc_line_func (cache, alloc_line, user_data, notify);
2166       cache->alloc_writable = alloc_writable;
2167 
2168       /* make sure only one cache frees the allocator */
2169       notify = NULL;
2170 
2171       if (!cache->pass_alloc) {
2172         /* can't pass allocator, make new temp line allocator */
2173         user_data =
2174             converter_alloc_new (sizeof (guint16) * width * 4,
2175             cache->n_lines + cache->backlog, convert, NULL);
2176         notify = (GDestroyNotify) converter_alloc_free;
2177         alloc_line = get_temp_line;
2178         alloc_writable = FALSE;
2179       }
2180       /* if someone writes to the input, we need a writable line from the
2181        * previous cache */
2182       if (cache->write_input)
2183         alloc_writable = TRUE;
2184     }
2185     /* free leftover allocator */
2186     if (notify)
2187       notify (user_data);
2188   }
2189 }
2190 
2191 static void
setup_borderline(GstVideoConverter * convert)2192 setup_borderline (GstVideoConverter * convert)
2193 {
2194   gint width;
2195 
2196   width = MAX (convert->in_maxwidth, convert->out_maxwidth);
2197   width += convert->out_x;
2198 
2199   if (convert->fill_border && (convert->out_height < convert->out_maxheight ||
2200           convert->out_width < convert->out_maxwidth)) {
2201     guint32 border_val;
2202     gint i, w_sub;
2203     const GstVideoFormatInfo *out_finfo;
2204     gpointer planes[GST_VIDEO_MAX_PLANES];
2205     gint strides[GST_VIDEO_MAX_PLANES];
2206 
2207     convert->borderline = g_malloc0 (sizeof (guint16) * width * 4);
2208 
2209     out_finfo = convert->out_info.finfo;
2210 
2211     if (GST_VIDEO_INFO_IS_YUV (&convert->out_info)) {
2212       MatrixData cm;
2213       gint a, r, g, b;
2214       gint y, u, v;
2215 
2216       /* Get Color matrix. */
2217       color_matrix_set_identity (&cm);
2218       compute_matrix_to_YUV (convert, &cm, TRUE);
2219       color_matrix_convert (&cm);
2220 
2221       border_val = GINT32_FROM_BE (convert->border_argb);
2222 
2223       b = (0xFF000000 & border_val) >> 24;
2224       g = (0x00FF0000 & border_val) >> 16;
2225       r = (0x0000FF00 & border_val) >> 8;
2226       a = (0x000000FF & border_val);
2227 
2228       y = 16 + ((r * cm.im[0][0] + g * cm.im[0][1] + b * cm.im[0][2]) >> 8);
2229       u = 128 + ((r * cm.im[1][0] + g * cm.im[1][1] + b * cm.im[1][2]) >> 8);
2230       v = 128 + ((r * cm.im[2][0] + g * cm.im[2][1] + b * cm.im[2][2]) >> 8);
2231 
2232       a = CLAMP (a, 0, 255);
2233       y = CLAMP (y, 0, 255);
2234       u = CLAMP (u, 0, 255);
2235       v = CLAMP (v, 0, 255);
2236 
2237       border_val = a | (y << 8) | (u << 16) | ((guint32) v << 24);
2238     } else {
2239       border_val = GINT32_FROM_BE (convert->border_argb);
2240     }
2241     if (convert->pack_bits == 8)
2242       video_orc_splat_u32 (convert->borderline, border_val, width);
2243     else
2244       video_orc_splat2_u64 (convert->borderline, border_val, width);
2245 
2246     /* convert pixels */
2247     for (i = 0; i < out_finfo->n_planes; i++) {
2248       planes[i] = &convert->borders[i];
2249       strides[i] = sizeof (guint64);
2250     }
2251     w_sub = 0;
2252     if (out_finfo->n_planes == 1) {
2253       /* for packed formats, convert based on subsampling so that we
2254        * get a complete group of pixels */
2255       for (i = 0; i < out_finfo->n_components; i++) {
2256         w_sub = MAX (w_sub, out_finfo->w_sub[i]);
2257       }
2258     }
2259     out_finfo->pack_func (out_finfo, GST_VIDEO_PACK_FLAG_NONE,
2260         convert->borderline, 0, planes, strides,
2261         GST_VIDEO_CHROMA_SITE_UNKNOWN, 0, 1 << w_sub);
2262   } else {
2263     convert->borderline = NULL;
2264   }
2265 }
2266 
2267 static AlphaMode
convert_get_alpha_mode(GstVideoConverter * convert)2268 convert_get_alpha_mode (GstVideoConverter * convert)
2269 {
2270   gboolean in_alpha, out_alpha;
2271 
2272   in_alpha = GST_VIDEO_INFO_HAS_ALPHA (&convert->in_info);
2273   out_alpha = GST_VIDEO_INFO_HAS_ALPHA (&convert->out_info);
2274 
2275   /* no output alpha, do nothing */
2276   if (!out_alpha)
2277     return ALPHA_MODE_NONE;
2278 
2279   if (in_alpha) {
2280     /* in and out */
2281     if (CHECK_ALPHA_COPY (convert))
2282       return ALPHA_MODE_COPY;
2283 
2284     if (CHECK_ALPHA_MULT (convert)) {
2285       if (GET_OPT_ALPHA_VALUE (convert) == 1.0)
2286         return ALPHA_MODE_COPY;
2287       else
2288         return ALPHA_MODE_MULT;
2289     }
2290   }
2291   /* nothing special, this is what unpack etc does automatically */
2292   if (GET_OPT_ALPHA_VALUE (convert) == 1.0)
2293     return ALPHA_MODE_NONE;
2294 
2295   /* everything else becomes SET */
2296   return ALPHA_MODE_SET;
2297 }
2298 
2299 /**
2300  * gst_video_converter_new_with_pool: (skip)
2301  * @in_info: a #GstVideoInfo
2302  * @out_info: a #GstVideoInfo
2303  * @config: (transfer full): a #GstStructure with configuration options
2304  * @pool: (nullable): a #GstTaskPool to spawn threads from
2305  *
2306  * Create a new converter object to convert between @in_info and @out_info
2307  * with @config.
2308  *
2309  * The optional @pool can be used to spawn threads, this is useful when
2310  * creating new converters rapidly, for example when updating cropping.
2311  *
2312  * Returns: a #GstVideoConverter or %NULL if conversion is not possible.
2313  *
2314  * Since: 1.20
2315  */
2316 GstVideoConverter *
gst_video_converter_new_with_pool(const GstVideoInfo * in_info,const GstVideoInfo * out_info,GstStructure * config,GstTaskPool * pool)2317 gst_video_converter_new_with_pool (const GstVideoInfo * in_info,
2318     const GstVideoInfo * out_info, GstStructure * config, GstTaskPool * pool)
2319 {
2320   GstVideoConverter *convert;
2321   GstLineCache *prev;
2322   const GstVideoFormatInfo *fin, *fout, *finfo;
2323   gdouble alpha_value;
2324   gint n_threads, i;
2325   gboolean async_tasks;
2326 
2327   g_return_val_if_fail (in_info != NULL, NULL);
2328   g_return_val_if_fail (out_info != NULL, NULL);
2329   /* we won't ever do framerate conversion */
2330   g_return_val_if_fail (in_info->fps_n == out_info->fps_n, NULL);
2331   g_return_val_if_fail (in_info->fps_d == out_info->fps_d, NULL);
2332   /* we won't ever do deinterlace */
2333   g_return_val_if_fail (in_info->interlace_mode == out_info->interlace_mode,
2334       NULL);
2335 
2336   convert = g_slice_new0 (GstVideoConverter);
2337 
2338   fin = in_info->finfo;
2339   fout = out_info->finfo;
2340 
2341   convert->in_info = *in_info;
2342   convert->out_info = *out_info;
2343 
2344   /* default config */
2345   convert->config = gst_structure_new_empty ("GstVideoConverter");
2346   if (config)
2347     gst_video_converter_set_config (convert, config);
2348 
2349   convert->in_maxwidth = GST_VIDEO_INFO_WIDTH (in_info);
2350   convert->in_maxheight = GST_VIDEO_INFO_FIELD_HEIGHT (in_info);
2351   convert->out_maxwidth = GST_VIDEO_INFO_WIDTH (out_info);
2352   convert->out_maxheight = GST_VIDEO_INFO_FIELD_HEIGHT (out_info);
2353 
2354   convert->in_x = get_opt_int (convert, GST_VIDEO_CONVERTER_OPT_SRC_X, 0);
2355   convert->in_y = get_opt_int (convert, GST_VIDEO_CONVERTER_OPT_SRC_Y, 0);
2356   convert->in_x &= ~((1 << fin->w_sub[1]) - 1);
2357   convert->in_y &= ~((1 << fin->h_sub[1]) - 1);
2358 
2359   convert->in_width = get_opt_int (convert,
2360       GST_VIDEO_CONVERTER_OPT_SRC_WIDTH, convert->in_maxwidth - convert->in_x);
2361   convert->in_height = get_opt_int (convert,
2362       GST_VIDEO_CONVERTER_OPT_SRC_HEIGHT,
2363       convert->in_maxheight - convert->in_y);
2364 
2365   convert->in_width =
2366       MIN (convert->in_width, convert->in_maxwidth - convert->in_x);
2367   if (convert->in_width + convert->in_x < 0 ||
2368       convert->in_width + convert->in_x > convert->in_maxwidth) {
2369     convert->in_width = 0;
2370   }
2371 
2372   convert->in_height =
2373       MIN (convert->in_height, convert->in_maxheight - convert->in_y);
2374   if (convert->in_height + convert->in_y < 0 ||
2375       convert->in_height + convert->in_y > convert->in_maxheight) {
2376     convert->in_height = 0;
2377   }
2378 
2379   convert->out_x = get_opt_int (convert, GST_VIDEO_CONVERTER_OPT_DEST_X, 0);
2380   convert->out_y = get_opt_int (convert, GST_VIDEO_CONVERTER_OPT_DEST_Y, 0);
2381   convert->out_x &= ~((1 << fout->w_sub[1]) - 1);
2382   convert->out_y &= ~((1 << fout->h_sub[1]) - 1);
2383 
2384   convert->out_width = get_opt_int (convert,
2385       GST_VIDEO_CONVERTER_OPT_DEST_WIDTH,
2386       convert->out_maxwidth - convert->out_x);
2387   convert->out_height =
2388       get_opt_int (convert, GST_VIDEO_CONVERTER_OPT_DEST_HEIGHT,
2389       convert->out_maxheight - convert->out_y);
2390 
2391   if (convert->out_width > convert->out_maxwidth - convert->out_x)
2392     convert->out_width = convert->out_maxwidth - convert->out_x;
2393   convert->out_width = CLAMP (convert->out_width, 0, convert->out_maxwidth);
2394 
2395   /* Check if completely outside the framebuffer */
2396   if (convert->out_width + convert->out_x < 0 ||
2397       convert->out_width + convert->out_x > convert->out_maxwidth) {
2398     convert->out_width = 0;
2399   }
2400 
2401   /* Same for height */
2402   if (convert->out_height > convert->out_maxheight - convert->out_y)
2403     convert->out_height = convert->out_maxheight - convert->out_y;
2404   convert->out_height = CLAMP (convert->out_height, 0, convert->out_maxheight);
2405 
2406   if (convert->out_height + convert->out_y < 0 ||
2407       convert->out_height + convert->out_y > convert->out_maxheight) {
2408     convert->out_height = 0;
2409   }
2410 
2411   convert->fill_border = GET_OPT_FILL_BORDER (convert);
2412   convert->border_argb = get_opt_uint (convert,
2413       GST_VIDEO_CONVERTER_OPT_BORDER_ARGB, DEFAULT_OPT_BORDER_ARGB);
2414 
2415   alpha_value = GET_OPT_ALPHA_VALUE (convert);
2416   convert->alpha_value = 255 * alpha_value;
2417   convert->alpha_mode = convert_get_alpha_mode (convert);
2418 
2419   convert->unpack_format = in_info->finfo->unpack_format;
2420   finfo = gst_video_format_get_info (convert->unpack_format);
2421   convert->unpack_bits = GST_VIDEO_FORMAT_INFO_DEPTH (finfo, 0);
2422   convert->unpack_rgb = GST_VIDEO_FORMAT_INFO_IS_RGB (finfo);
2423   if (convert->unpack_rgb
2424       && in_info->colorimetry.matrix != GST_VIDEO_COLOR_MATRIX_RGB) {
2425     /* force identity matrix for RGB input */
2426     GST_WARNING ("invalid matrix %d for input RGB format, using RGB",
2427         in_info->colorimetry.matrix);
2428     convert->in_info.colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_RGB;
2429   }
2430 
2431   convert->pack_format = out_info->finfo->unpack_format;
2432   finfo = gst_video_format_get_info (convert->pack_format);
2433   convert->pack_bits = GST_VIDEO_FORMAT_INFO_DEPTH (finfo, 0);
2434   convert->pack_rgb = GST_VIDEO_FORMAT_INFO_IS_RGB (finfo);
2435   convert->pack_pal =
2436       gst_video_format_get_palette (GST_VIDEO_INFO_FORMAT (out_info),
2437       &convert->pack_palsize);
2438   if (convert->pack_rgb
2439       && out_info->colorimetry.matrix != GST_VIDEO_COLOR_MATRIX_RGB) {
2440     /* force identity matrix for RGB output */
2441     GST_WARNING ("invalid matrix %d for output RGB format, using RGB",
2442         out_info->colorimetry.matrix);
2443     convert->out_info.colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_RGB;
2444   }
2445 
2446   n_threads = get_opt_uint (convert, GST_VIDEO_CONVERTER_OPT_THREADS, 1);
2447   if (n_threads == 0 || n_threads > g_get_num_processors ())
2448     n_threads = g_get_num_processors ();
2449   /* Magic number of 200 lines */
2450   if (MAX (convert->out_height, convert->in_height) / n_threads < 200)
2451     n_threads = (MAX (convert->out_height, convert->in_height) + 199) / 200;
2452   if (n_threads < 1)
2453     n_threads = 1;
2454 
2455   async_tasks = GET_OPT_ASYNC_TASKS (convert);
2456   convert->conversion_runner =
2457       gst_parallelized_task_runner_new (n_threads, pool, async_tasks);
2458 
2459   if (video_converter_lookup_fastpath (convert))
2460     goto done;
2461 
2462   if (in_info->finfo->unpack_func == NULL)
2463     goto no_unpack_func;
2464 
2465   if (out_info->finfo->pack_func == NULL)
2466     goto no_pack_func;
2467 
2468   convert->convert = video_converter_generic;
2469 
2470   convert->upsample_p = g_new0 (GstVideoChromaResample *, n_threads);
2471   convert->upsample_i = g_new0 (GstVideoChromaResample *, n_threads);
2472   convert->downsample_p = g_new0 (GstVideoChromaResample *, n_threads);
2473   convert->downsample_i = g_new0 (GstVideoChromaResample *, n_threads);
2474   convert->v_scaler_p = g_new0 (GstVideoScaler *, n_threads);
2475   convert->v_scaler_i = g_new0 (GstVideoScaler *, n_threads);
2476   convert->h_scaler = g_new0 (GstVideoScaler *, n_threads);
2477   convert->unpack_lines = g_new0 (GstLineCache *, n_threads);
2478   convert->pack_lines = g_new0 (GstLineCache *, n_threads);
2479   convert->upsample_lines = g_new0 (GstLineCache *, n_threads);
2480   convert->to_RGB_lines = g_new0 (GstLineCache *, n_threads);
2481   convert->hscale_lines = g_new0 (GstLineCache *, n_threads);
2482   convert->vscale_lines = g_new0 (GstLineCache *, n_threads);
2483   convert->convert_lines = g_new0 (GstLineCache *, n_threads);
2484   convert->alpha_lines = g_new0 (GstLineCache *, n_threads);
2485   convert->to_YUV_lines = g_new0 (GstLineCache *, n_threads);
2486   convert->downsample_lines = g_new0 (GstLineCache *, n_threads);
2487   convert->dither_lines = g_new0 (GstLineCache *, n_threads);
2488   convert->dither = g_new0 (GstVideoDither *, n_threads);
2489 
2490   if (convert->in_width > 0 && convert->out_width > 0 && convert->in_height > 0
2491       && convert->out_height > 0) {
2492     for (i = 0; i < n_threads; i++) {
2493       convert->current_format = GST_VIDEO_INFO_FORMAT (in_info);
2494       convert->current_width = convert->in_width;
2495       convert->current_height = convert->in_height;
2496 
2497       /* unpack */
2498       prev = chain_unpack_line (convert, i);
2499       /* upsample chroma */
2500       prev = chain_upsample (convert, prev, i);
2501       /* convert to gamma decoded RGB */
2502       prev = chain_convert_to_RGB (convert, prev, i);
2503       /* do all downscaling */
2504       prev = chain_scale (convert, prev, FALSE, i);
2505       /* do conversion between color spaces */
2506       prev = chain_convert (convert, prev, i);
2507       /* do alpha channels */
2508       prev = chain_alpha (convert, prev, i);
2509       /* do all remaining (up)scaling */
2510       prev = chain_scale (convert, prev, TRUE, i);
2511       /* convert to gamma encoded Y'Cb'Cr' */
2512       prev = chain_convert_to_YUV (convert, prev, i);
2513       /* downsample chroma */
2514       prev = chain_downsample (convert, prev, i);
2515       /* dither */
2516       prev = chain_dither (convert, prev, i);
2517       /* pack into final format */
2518       convert->pack_lines[i] = chain_pack (convert, prev, i);
2519     }
2520   }
2521 
2522   setup_borderline (convert);
2523   /* now figure out allocators */
2524   setup_allocators (convert);
2525 
2526 done:
2527   return convert;
2528 
2529   /* ERRORS */
2530 no_unpack_func:
2531   {
2532     GST_ERROR ("no unpack_func for format %s",
2533         gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (in_info)));
2534     gst_video_converter_free (convert);
2535     return NULL;
2536   }
2537 no_pack_func:
2538   {
2539     GST_ERROR ("no pack_func for format %s",
2540         gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (out_info)));
2541     gst_video_converter_free (convert);
2542     return NULL;
2543   }
2544 }
2545 
2546 /**
2547  * gst_video_converter_new: (skip)
2548  * @in_info: a #GstVideoInfo
2549  * @out_info: a #GstVideoInfo
2550  * @config: (transfer full): a #GstStructure with configuration options
2551  *
2552  * Create a new converter object to convert between @in_info and @out_info
2553  * with @config.
2554  *
2555  * Returns: a #GstVideoConverter or %NULL if conversion is not possible.
2556  *
2557  * Since: 1.6
2558  */
2559 GstVideoConverter *
gst_video_converter_new(const GstVideoInfo * in_info,const GstVideoInfo * out_info,GstStructure * config)2560 gst_video_converter_new (const GstVideoInfo * in_info,
2561     const GstVideoInfo * out_info, GstStructure * config)
2562 {
2563   return gst_video_converter_new_with_pool (in_info, out_info, config, NULL);
2564 }
2565 
2566 static void
clear_matrix_data(MatrixData * data)2567 clear_matrix_data (MatrixData * data)
2568 {
2569   g_free (data->t_r);
2570   g_free (data->t_g);
2571   g_free (data->t_b);
2572 }
2573 
2574 /**
2575  * gst_video_converter_free:
2576  * @convert: a #GstVideoConverter
2577  *
2578  * Free @convert
2579  *
2580  * Since: 1.6
2581  */
2582 void
gst_video_converter_free(GstVideoConverter * convert)2583 gst_video_converter_free (GstVideoConverter * convert)
2584 {
2585   guint i, j;
2586 
2587   g_return_if_fail (convert != NULL);
2588 
2589   for (i = 0; i < convert->conversion_runner->n_threads; i++) {
2590     if (convert->upsample_p && convert->upsample_p[i])
2591       gst_video_chroma_resample_free (convert->upsample_p[i]);
2592     if (convert->upsample_i && convert->upsample_i[i])
2593       gst_video_chroma_resample_free (convert->upsample_i[i]);
2594     if (convert->downsample_p && convert->downsample_p[i])
2595       gst_video_chroma_resample_free (convert->downsample_p[i]);
2596     if (convert->downsample_i && convert->downsample_i[i])
2597       gst_video_chroma_resample_free (convert->downsample_i[i]);
2598     if (convert->v_scaler_p && convert->v_scaler_p[i])
2599       gst_video_scaler_free (convert->v_scaler_p[i]);
2600     if (convert->v_scaler_i && convert->v_scaler_i[i])
2601       gst_video_scaler_free (convert->v_scaler_i[i]);
2602     if (convert->h_scaler && convert->h_scaler[i])
2603       gst_video_scaler_free (convert->h_scaler[i]);
2604     if (convert->unpack_lines && convert->unpack_lines[i])
2605       gst_line_cache_free (convert->unpack_lines[i]);
2606     if (convert->upsample_lines && convert->upsample_lines[i])
2607       gst_line_cache_free (convert->upsample_lines[i]);
2608     if (convert->to_RGB_lines && convert->to_RGB_lines[i])
2609       gst_line_cache_free (convert->to_RGB_lines[i]);
2610     if (convert->hscale_lines && convert->hscale_lines[i])
2611       gst_line_cache_free (convert->hscale_lines[i]);
2612     if (convert->vscale_lines && convert->vscale_lines[i])
2613       gst_line_cache_free (convert->vscale_lines[i]);
2614     if (convert->convert_lines && convert->convert_lines[i])
2615       gst_line_cache_free (convert->convert_lines[i]);
2616     if (convert->alpha_lines && convert->alpha_lines[i])
2617       gst_line_cache_free (convert->alpha_lines[i]);
2618     if (convert->to_YUV_lines && convert->to_YUV_lines[i])
2619       gst_line_cache_free (convert->to_YUV_lines[i]);
2620     if (convert->downsample_lines && convert->downsample_lines[i])
2621       gst_line_cache_free (convert->downsample_lines[i]);
2622     if (convert->dither_lines && convert->dither_lines[i])
2623       gst_line_cache_free (convert->dither_lines[i]);
2624     if (convert->dither && convert->dither[i])
2625       gst_video_dither_free (convert->dither[i]);
2626   }
2627   g_free (convert->upsample_p);
2628   g_free (convert->upsample_i);
2629   g_free (convert->downsample_p);
2630   g_free (convert->downsample_i);
2631   g_free (convert->v_scaler_p);
2632   g_free (convert->v_scaler_i);
2633   g_free (convert->h_scaler);
2634   g_free (convert->unpack_lines);
2635   g_free (convert->pack_lines);
2636   g_free (convert->upsample_lines);
2637   g_free (convert->to_RGB_lines);
2638   g_free (convert->hscale_lines);
2639   g_free (convert->vscale_lines);
2640   g_free (convert->convert_lines);
2641   g_free (convert->alpha_lines);
2642   g_free (convert->to_YUV_lines);
2643   g_free (convert->downsample_lines);
2644   g_free (convert->dither_lines);
2645   g_free (convert->dither);
2646 
2647   g_free (convert->gamma_dec.gamma_table);
2648   g_free (convert->gamma_enc.gamma_table);
2649 
2650   if (convert->tmpline) {
2651     for (i = 0; i < convert->conversion_runner->n_threads; i++)
2652       g_free (convert->tmpline[i]);
2653     g_free (convert->tmpline);
2654   }
2655 
2656   g_free (convert->borderline);
2657 
2658   if (convert->config)
2659     gst_structure_free (convert->config);
2660 
2661   for (i = 0; i < 4; i++) {
2662     for (j = 0; j < convert->conversion_runner->n_threads; j++) {
2663       if (convert->fv_scaler[i].scaler)
2664         gst_video_scaler_free (convert->fv_scaler[i].scaler[j]);
2665       if (convert->fh_scaler[i].scaler)
2666         gst_video_scaler_free (convert->fh_scaler[i].scaler[j]);
2667     }
2668     g_free (convert->fv_scaler[i].scaler);
2669     g_free (convert->fh_scaler[i].scaler);
2670   }
2671 
2672   if (convert->conversion_runner)
2673     gst_parallelized_task_runner_free (convert->conversion_runner);
2674 
2675   clear_matrix_data (&convert->to_RGB_matrix);
2676   clear_matrix_data (&convert->convert_matrix);
2677   clear_matrix_data (&convert->to_YUV_matrix);
2678 
2679   for (i = 0; i < 4; i++) {
2680     g_free (convert->tasks[i]);
2681     g_free (convert->tasks_p[i]);
2682   }
2683 
2684   g_slice_free (GstVideoConverter, convert);
2685 }
2686 
2687 static gboolean
copy_config(GQuark field_id,const GValue * value,gpointer user_data)2688 copy_config (GQuark field_id, const GValue * value, gpointer user_data)
2689 {
2690   GstVideoConverter *convert = user_data;
2691 
2692   gst_structure_id_set_value (convert->config, field_id, value);
2693 
2694   return TRUE;
2695 }
2696 
2697 /**
2698  * gst_video_converter_set_config:
2699  * @convert: a #GstVideoConverter
2700  * @config: (transfer full): a #GstStructure
2701  *
2702  * Set @config as extra configuration for @convert.
2703  *
2704  * If the parameters in @config can not be set exactly, this function returns
2705  * %FALSE and will try to update as much state as possible. The new state can
2706  * then be retrieved and refined with gst_video_converter_get_config().
2707  *
2708  * Look at the `GST_VIDEO_CONVERTER_OPT_*` fields to check valid configuration
2709  * option and values.
2710  *
2711  * Returns: %TRUE when @config could be set.
2712  *
2713  * Since: 1.6
2714  */
2715 gboolean
gst_video_converter_set_config(GstVideoConverter * convert,GstStructure * config)2716 gst_video_converter_set_config (GstVideoConverter * convert,
2717     GstStructure * config)
2718 {
2719   g_return_val_if_fail (convert != NULL, FALSE);
2720   g_return_val_if_fail (config != NULL, FALSE);
2721 
2722   gst_structure_foreach (config, copy_config, convert);
2723   gst_structure_free (config);
2724 
2725   return TRUE;
2726 }
2727 
2728 /**
2729  * gst_video_converter_get_config:
2730  * @convert: a #GstVideoConverter
2731  *
2732  * Get the current configuration of @convert.
2733  *
2734  * Returns: a #GstStructure that remains valid for as long as @convert is valid
2735  *   or until gst_video_converter_set_config() is called.
2736  */
2737 const GstStructure *
gst_video_converter_get_config(GstVideoConverter * convert)2738 gst_video_converter_get_config (GstVideoConverter * convert)
2739 {
2740   g_return_val_if_fail (convert != NULL, NULL);
2741 
2742   return convert->config;
2743 }
2744 
2745 /**
2746  * gst_video_converter_frame:
2747  * @convert: a #GstVideoConverter
2748  * @dest: a #GstVideoFrame
2749  * @src: a #GstVideoFrame
2750  *
2751  * Convert the pixels of @src into @dest using @convert.
2752  *
2753  * If #GST_VIDEO_CONVERTER_OPT_ASYNC_TASKS is %TRUE then this function will
2754  * return immediately and needs to be followed by a call to
2755  * gst_video_converter_frame_finish().
2756  *
2757  * Since: 1.6
2758  */
2759 void
gst_video_converter_frame(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)2760 gst_video_converter_frame (GstVideoConverter * convert,
2761     const GstVideoFrame * src, GstVideoFrame * dest)
2762 {
2763   g_return_if_fail (convert != NULL);
2764   g_return_if_fail (src != NULL);
2765   g_return_if_fail (dest != NULL);
2766 
2767   /* Check the frames we've been passed match the layout
2768    * we were configured for or we might go out of bounds */
2769   if (G_UNLIKELY (GST_VIDEO_INFO_FORMAT (&convert->in_info) !=
2770           GST_VIDEO_FRAME_FORMAT (src)
2771           || GST_VIDEO_INFO_WIDTH (&convert->in_info) >
2772           GST_VIDEO_FRAME_WIDTH (src)
2773           || GST_VIDEO_INFO_FIELD_HEIGHT (&convert->in_info) >
2774           GST_VIDEO_FRAME_HEIGHT (src))) {
2775     g_critical ("Input video frame does not match configuration");
2776     return;
2777   }
2778   if (G_UNLIKELY (GST_VIDEO_INFO_FORMAT (&convert->out_info) !=
2779           GST_VIDEO_FRAME_FORMAT (dest)
2780           || GST_VIDEO_INFO_WIDTH (&convert->out_info) >
2781           GST_VIDEO_FRAME_WIDTH (dest)
2782           || GST_VIDEO_INFO_FIELD_HEIGHT (&convert->out_info) >
2783           GST_VIDEO_FRAME_HEIGHT (dest))) {
2784     g_critical ("Output video frame does not match configuration");
2785     return;
2786   }
2787 
2788   if (G_UNLIKELY (convert->in_width == 0 || convert->in_height == 0 ||
2789           convert->out_width == 0 || convert->out_height == 0))
2790     return;
2791 
2792   convert->convert (convert, src, dest);
2793 }
2794 
2795 /**
2796  * gst_video_converter_frame_finish:
2797  * @convert: a #GstVideoConverter
2798  *
2799  * Wait for a previous async conversion performed using
2800  * gst_video_converter_frame() to complete.
2801  *
2802  * Since: 1.20
2803  */
2804 void
gst_video_converter_frame_finish(GstVideoConverter * convert)2805 gst_video_converter_frame_finish (GstVideoConverter * convert)
2806 {
2807   g_return_if_fail (convert);
2808   g_return_if_fail (convert->conversion_runner);
2809   g_return_if_fail (convert->conversion_runner->async_tasks);
2810 
2811   gst_parallelized_task_runner_finish (convert->conversion_runner);
2812 }
2813 
2814 static void
video_converter_compute_matrix(GstVideoConverter * convert)2815 video_converter_compute_matrix (GstVideoConverter * convert)
2816 {
2817   MatrixData *dst = &convert->convert_matrix;
2818 
2819   color_matrix_set_identity (dst);
2820   compute_matrix_to_RGB (convert, dst);
2821   compute_matrix_to_YUV (convert, dst, FALSE);
2822 
2823   convert->current_bits = 8;
2824   prepare_matrix (convert, dst);
2825 }
2826 
2827 static void
video_converter_compute_resample(GstVideoConverter * convert,gint idx)2828 video_converter_compute_resample (GstVideoConverter * convert, gint idx)
2829 {
2830   GstVideoInfo *in_info, *out_info;
2831   const GstVideoFormatInfo *sfinfo, *dfinfo;
2832 
2833   if (CHECK_CHROMA_NONE (convert))
2834     return;
2835 
2836   in_info = &convert->in_info;
2837   out_info = &convert->out_info;
2838 
2839   sfinfo = in_info->finfo;
2840   dfinfo = out_info->finfo;
2841 
2842   GST_DEBUG ("site: %d->%d, w_sub: %d->%d, h_sub: %d->%d", in_info->chroma_site,
2843       out_info->chroma_site, sfinfo->w_sub[2], dfinfo->w_sub[2],
2844       sfinfo->h_sub[2], dfinfo->h_sub[2]);
2845 
2846   if (sfinfo->w_sub[2] != dfinfo->w_sub[2] ||
2847       sfinfo->h_sub[2] != dfinfo->h_sub[2] ||
2848       in_info->chroma_site != out_info->chroma_site ||
2849       in_info->width != out_info->width ||
2850       in_info->height != out_info->height) {
2851     if (GST_VIDEO_INFO_IS_INTERLACED (in_info)
2852         && GST_VIDEO_INFO_INTERLACE_MODE (in_info) !=
2853         GST_VIDEO_INTERLACE_MODE_ALTERNATE) {
2854       if (!CHECK_CHROMA_DOWNSAMPLE (convert))
2855         convert->upsample_i[idx] = gst_video_chroma_resample_new (0,
2856             in_info->chroma_site, GST_VIDEO_CHROMA_FLAG_INTERLACED,
2857             sfinfo->unpack_format, sfinfo->w_sub[2], sfinfo->h_sub[2]);
2858       if (!CHECK_CHROMA_UPSAMPLE (convert))
2859         convert->downsample_i[idx] =
2860             gst_video_chroma_resample_new (0, out_info->chroma_site,
2861             GST_VIDEO_CHROMA_FLAG_INTERLACED, dfinfo->unpack_format,
2862             -dfinfo->w_sub[2], -dfinfo->h_sub[2]);
2863     }
2864     if (!CHECK_CHROMA_DOWNSAMPLE (convert))
2865       convert->upsample_p[idx] = gst_video_chroma_resample_new (0,
2866           in_info->chroma_site, 0, sfinfo->unpack_format, sfinfo->w_sub[2],
2867           sfinfo->h_sub[2]);
2868     if (!CHECK_CHROMA_UPSAMPLE (convert))
2869       convert->downsample_p[idx] = gst_video_chroma_resample_new (0,
2870           out_info->chroma_site, 0, dfinfo->unpack_format, -dfinfo->w_sub[2],
2871           -dfinfo->h_sub[2]);
2872   }
2873 }
2874 
2875 #define FRAME_GET_PLANE_STRIDE(frame, plane) \
2876   GST_VIDEO_FRAME_PLANE_STRIDE (frame, plane)
2877 #define FRAME_GET_PLANE_LINE(frame, plane, line) \
2878   (gpointer)(((guint8*)(GST_VIDEO_FRAME_PLANE_DATA (frame, plane))) + \
2879       FRAME_GET_PLANE_STRIDE (frame, plane) * (line))
2880 
2881 #define FRAME_GET_COMP_STRIDE(frame, comp) \
2882   GST_VIDEO_FRAME_COMP_STRIDE (frame, comp)
2883 #define FRAME_GET_COMP_LINE(frame, comp, line) \
2884   (gpointer)(((guint8*)(GST_VIDEO_FRAME_COMP_DATA (frame, comp))) + \
2885       FRAME_GET_COMP_STRIDE (frame, comp) * (line))
2886 
2887 #define FRAME_GET_STRIDE(frame)      FRAME_GET_PLANE_STRIDE (frame, 0)
2888 #define FRAME_GET_LINE(frame,line)   FRAME_GET_PLANE_LINE (frame, 0, line)
2889 
2890 #define FRAME_GET_Y_LINE(frame,line) FRAME_GET_COMP_LINE(frame, GST_VIDEO_COMP_Y, line)
2891 #define FRAME_GET_U_LINE(frame,line) FRAME_GET_COMP_LINE(frame, GST_VIDEO_COMP_U, line)
2892 #define FRAME_GET_V_LINE(frame,line) FRAME_GET_COMP_LINE(frame, GST_VIDEO_COMP_V, line)
2893 #define FRAME_GET_A_LINE(frame,line) FRAME_GET_COMP_LINE(frame, GST_VIDEO_COMP_A, line)
2894 
2895 #define FRAME_GET_Y_STRIDE(frame)    FRAME_GET_COMP_STRIDE(frame, GST_VIDEO_COMP_Y)
2896 #define FRAME_GET_U_STRIDE(frame)    FRAME_GET_COMP_STRIDE(frame, GST_VIDEO_COMP_U)
2897 #define FRAME_GET_V_STRIDE(frame)    FRAME_GET_COMP_STRIDE(frame, GST_VIDEO_COMP_V)
2898 #define FRAME_GET_A_STRIDE(frame)    FRAME_GET_COMP_STRIDE(frame, GST_VIDEO_COMP_A)
2899 
2900 
2901 #define UNPACK_FRAME(frame,dest,line,x,width)        \
2902   frame->info.finfo->unpack_func (frame->info.finfo, \
2903       (GST_VIDEO_FRAME_IS_INTERLACED (frame) ?       \
2904         GST_VIDEO_PACK_FLAG_INTERLACED :             \
2905         GST_VIDEO_PACK_FLAG_NONE),                   \
2906       dest, frame->data, frame->info.stride, x,      \
2907       line, width)
2908 #define PACK_FRAME(frame,src,line,width)             \
2909   frame->info.finfo->pack_func (frame->info.finfo,   \
2910       (GST_VIDEO_FRAME_IS_INTERLACED (frame) ?       \
2911         GST_VIDEO_PACK_FLAG_INTERLACED :             \
2912         GST_VIDEO_PACK_FLAG_NONE),                   \
2913       src, 0, frame->data, frame->info.stride,       \
2914       frame->info.chroma_site, line, width);
2915 
2916 static gpointer
get_dest_line(GstLineCache * cache,gint idx,gpointer user_data)2917 get_dest_line (GstLineCache * cache, gint idx, gpointer user_data)
2918 {
2919   GstVideoConverter *convert = user_data;
2920   guint8 *line;
2921   gint pstride = convert->pack_pstride;
2922   gint out_x = convert->out_x;
2923   guint cline;
2924 
2925   cline = CLAMP (idx, 0, convert->out_maxheight - 1);
2926 
2927   line = FRAME_GET_LINE (convert->dest, cline);
2928   GST_DEBUG ("get dest line %d %p", cline, line);
2929 
2930   if (convert->borderline) {
2931     gint r_border = (out_x + convert->out_width) * pstride;
2932     gint rb_width = convert->out_maxwidth * pstride - r_border;
2933     gint lb_width = out_x * pstride;
2934 
2935     memcpy (line, convert->borderline, lb_width);
2936     memcpy (line + r_border, convert->borderline, rb_width);
2937   }
2938   line += out_x * pstride;
2939 
2940   return line;
2941 }
2942 
2943 static gboolean
do_unpack_lines(GstLineCache * cache,gint idx,gint out_line,gint in_line,gpointer user_data)2944 do_unpack_lines (GstLineCache * cache, gint idx, gint out_line, gint in_line,
2945     gpointer user_data)
2946 {
2947   GstVideoConverter *convert = user_data;
2948   gpointer tmpline;
2949   guint cline;
2950 
2951   cline = CLAMP (in_line + convert->in_y, 0, convert->in_maxheight - 1);
2952 
2953   if (cache->alloc_writable || !convert->identity_unpack) {
2954     tmpline = gst_line_cache_alloc_line (cache, out_line);
2955     GST_DEBUG ("unpack line %d (%u) %p", in_line, cline, tmpline);
2956     UNPACK_FRAME (convert->src, tmpline, cline, convert->in_x,
2957         convert->in_width);
2958   } else {
2959     tmpline = ((guint8 *) FRAME_GET_LINE (convert->src, cline)) +
2960         convert->in_x * convert->unpack_pstride;
2961     GST_DEBUG ("get src line %d (%u) %p", in_line, cline, tmpline);
2962   }
2963   gst_line_cache_add_line (cache, in_line, tmpline);
2964 
2965   return TRUE;
2966 }
2967 
2968 static gboolean
do_upsample_lines(GstLineCache * cache,gint idx,gint out_line,gint in_line,gpointer user_data)2969 do_upsample_lines (GstLineCache * cache, gint idx, gint out_line, gint in_line,
2970     gpointer user_data)
2971 {
2972   GstVideoConverter *convert = user_data;
2973   gpointer *lines;
2974   gint i, start_line, n_lines;
2975 
2976   n_lines = convert->up_n_lines;
2977   start_line = in_line;
2978   if (start_line < n_lines + convert->up_offset) {
2979     start_line += convert->up_offset;
2980     out_line += convert->up_offset;
2981   }
2982 
2983   /* get the lines needed for chroma upsample */
2984   lines =
2985       gst_line_cache_get_lines (cache->prev, idx, out_line, start_line,
2986       n_lines);
2987 
2988   if (convert->upsample[idx]) {
2989     GST_DEBUG ("doing upsample %d-%d %p", start_line, start_line + n_lines - 1,
2990         lines[0]);
2991     gst_video_chroma_resample (convert->upsample[idx], lines,
2992         convert->in_width);
2993   }
2994 
2995   for (i = 0; i < n_lines; i++)
2996     gst_line_cache_add_line (cache, start_line + i, lines[i]);
2997 
2998   return TRUE;
2999 }
3000 
3001 static gboolean
do_convert_to_RGB_lines(GstLineCache * cache,gint idx,gint out_line,gint in_line,gpointer user_data)3002 do_convert_to_RGB_lines (GstLineCache * cache, gint idx, gint out_line,
3003     gint in_line, gpointer user_data)
3004 {
3005   GstVideoConverter *convert = user_data;
3006   MatrixData *data = &convert->to_RGB_matrix;
3007   gpointer *lines, destline;
3008 
3009   lines = gst_line_cache_get_lines (cache->prev, idx, out_line, in_line, 1);
3010   destline = lines[0];
3011 
3012   if (data->matrix_func) {
3013     GST_DEBUG ("to RGB line %d %p", in_line, destline);
3014     data->matrix_func (data, destline);
3015   }
3016   if (convert->gamma_dec.gamma_func) {
3017     destline = gst_line_cache_alloc_line (cache, out_line);
3018 
3019     GST_DEBUG ("gamma decode line %d %p->%p", in_line, lines[0], destline);
3020     convert->gamma_dec.gamma_func (&convert->gamma_dec, destline, lines[0]);
3021   }
3022   gst_line_cache_add_line (cache, in_line, destline);
3023 
3024   return TRUE;
3025 }
3026 
3027 static gboolean
do_hscale_lines(GstLineCache * cache,gint idx,gint out_line,gint in_line,gpointer user_data)3028 do_hscale_lines (GstLineCache * cache, gint idx, gint out_line, gint in_line,
3029     gpointer user_data)
3030 {
3031   GstVideoConverter *convert = user_data;
3032   gpointer *lines, destline;
3033 
3034   lines = gst_line_cache_get_lines (cache->prev, idx, out_line, in_line, 1);
3035 
3036   destline = gst_line_cache_alloc_line (cache, out_line);
3037 
3038   GST_DEBUG ("hresample line %d %p->%p", in_line, lines[0], destline);
3039   gst_video_scaler_horizontal (convert->h_scaler[idx], convert->h_scale_format,
3040       lines[0], destline, 0, convert->out_width);
3041 
3042   gst_line_cache_add_line (cache, in_line, destline);
3043 
3044   return TRUE;
3045 }
3046 
3047 static gboolean
do_vscale_lines(GstLineCache * cache,gint idx,gint out_line,gint in_line,gpointer user_data)3048 do_vscale_lines (GstLineCache * cache, gint idx, gint out_line, gint in_line,
3049     gpointer user_data)
3050 {
3051   GstVideoConverter *convert = user_data;
3052   gpointer *lines, destline;
3053   guint sline, n_lines;
3054   guint cline;
3055 
3056   cline = CLAMP (in_line, 0, convert->out_height - 1);
3057 
3058   gst_video_scaler_get_coeff (convert->v_scaler[idx], cline, &sline, &n_lines);
3059   lines = gst_line_cache_get_lines (cache->prev, idx, out_line, sline, n_lines);
3060 
3061   destline = gst_line_cache_alloc_line (cache, out_line);
3062 
3063   GST_DEBUG ("vresample line %d %d-%d %p->%p", in_line, sline,
3064       sline + n_lines - 1, lines[0], destline);
3065   gst_video_scaler_vertical (convert->v_scaler[idx], convert->v_scale_format,
3066       lines, destline, cline, convert->v_scale_width);
3067 
3068   gst_line_cache_add_line (cache, in_line, destline);
3069 
3070   return TRUE;
3071 }
3072 
3073 static gboolean
do_convert_lines(GstLineCache * cache,gint idx,gint out_line,gint in_line,gpointer user_data)3074 do_convert_lines (GstLineCache * cache, gint idx, gint out_line, gint in_line,
3075     gpointer user_data)
3076 {
3077   GstVideoConverter *convert = user_data;
3078   MatrixData *data = &convert->convert_matrix;
3079   gpointer *lines, destline;
3080   guint in_bits, out_bits;
3081   gint width;
3082 
3083   lines = gst_line_cache_get_lines (cache->prev, idx, out_line, in_line, 1);
3084 
3085   destline = lines[0];
3086 
3087   in_bits = convert->in_bits;
3088   out_bits = convert->out_bits;
3089 
3090   width = MIN (convert->in_width, convert->out_width);
3091 
3092   if (out_bits == 16 || in_bits == 16) {
3093     gpointer srcline = lines[0];
3094 
3095     if (out_bits != in_bits)
3096       destline = gst_line_cache_alloc_line (cache, out_line);
3097 
3098     /* FIXME, we can scale in the conversion matrix */
3099     if (in_bits == 8) {
3100       GST_DEBUG ("8->16 line %d %p->%p", in_line, srcline, destline);
3101       video_orc_convert_u8_to_u16 (destline, srcline, width * 4);
3102       srcline = destline;
3103     }
3104 
3105     if (data->matrix_func) {
3106       GST_DEBUG ("matrix line %d %p", in_line, srcline);
3107       data->matrix_func (data, srcline);
3108     }
3109 
3110     /* FIXME, dither here */
3111     if (out_bits == 8) {
3112       GST_DEBUG ("16->8 line %d %p->%p", in_line, srcline, destline);
3113       video_orc_convert_u16_to_u8 (destline, srcline, width * 4);
3114     }
3115   } else {
3116     if (data->matrix_func) {
3117       GST_DEBUG ("matrix line %d %p", in_line, destline);
3118       data->matrix_func (data, destline);
3119     }
3120   }
3121   gst_line_cache_add_line (cache, in_line, destline);
3122 
3123   return TRUE;
3124 }
3125 
3126 static gboolean
do_alpha_lines(GstLineCache * cache,gint idx,gint out_line,gint in_line,gpointer user_data)3127 do_alpha_lines (GstLineCache * cache, gint idx, gint out_line, gint in_line,
3128     gpointer user_data)
3129 {
3130   gpointer *lines, destline;
3131   GstVideoConverter *convert = user_data;
3132   gint width = MIN (convert->in_width, convert->out_width);
3133 
3134   lines = gst_line_cache_get_lines (cache->prev, idx, out_line, in_line, 1);
3135   destline = lines[0];
3136 
3137   GST_DEBUG ("alpha line %d %p", in_line, destline);
3138   convert->alpha_func (convert, destline, width);
3139 
3140   gst_line_cache_add_line (cache, in_line, destline);
3141 
3142   return TRUE;
3143 }
3144 
3145 static gboolean
do_convert_to_YUV_lines(GstLineCache * cache,gint idx,gint out_line,gint in_line,gpointer user_data)3146 do_convert_to_YUV_lines (GstLineCache * cache, gint idx, gint out_line,
3147     gint in_line, gpointer user_data)
3148 {
3149   GstVideoConverter *convert = user_data;
3150   MatrixData *data = &convert->to_YUV_matrix;
3151   gpointer *lines, destline;
3152 
3153   lines = gst_line_cache_get_lines (cache->prev, idx, out_line, in_line, 1);
3154   destline = lines[0];
3155 
3156   if (convert->gamma_enc.gamma_func) {
3157     destline = gst_line_cache_alloc_line (cache, out_line);
3158 
3159     GST_DEBUG ("gamma encode line %d %p->%p", in_line, lines[0], destline);
3160     convert->gamma_enc.gamma_func (&convert->gamma_enc, destline, lines[0]);
3161   }
3162   if (data->matrix_func) {
3163     GST_DEBUG ("to YUV line %d %p", in_line, destline);
3164     data->matrix_func (data, destline);
3165   }
3166   gst_line_cache_add_line (cache, in_line, destline);
3167 
3168   return TRUE;
3169 }
3170 
3171 static gboolean
do_downsample_lines(GstLineCache * cache,gint idx,gint out_line,gint in_line,gpointer user_data)3172 do_downsample_lines (GstLineCache * cache, gint idx, gint out_line,
3173     gint in_line, gpointer user_data)
3174 {
3175   GstVideoConverter *convert = user_data;
3176   gpointer *lines;
3177   gint i, start_line, n_lines;
3178 
3179   n_lines = convert->down_n_lines;
3180   start_line = in_line;
3181   if (start_line < n_lines + convert->down_offset)
3182     start_line += convert->down_offset;
3183 
3184   /* get the lines needed for chroma downsample */
3185   lines =
3186       gst_line_cache_get_lines (cache->prev, idx, out_line, start_line,
3187       n_lines);
3188 
3189   if (convert->downsample[idx]) {
3190     GST_DEBUG ("downsample line %d %d-%d %p", in_line, start_line,
3191         start_line + n_lines - 1, lines[0]);
3192     gst_video_chroma_resample (convert->downsample[idx], lines,
3193         convert->out_width);
3194   }
3195 
3196   for (i = 0; i < n_lines; i++)
3197     gst_line_cache_add_line (cache, start_line + i, lines[i]);
3198 
3199   return TRUE;
3200 }
3201 
3202 static gboolean
do_dither_lines(GstLineCache * cache,gint idx,gint out_line,gint in_line,gpointer user_data)3203 do_dither_lines (GstLineCache * cache, gint idx, gint out_line, gint in_line,
3204     gpointer user_data)
3205 {
3206   GstVideoConverter *convert = user_data;
3207   gpointer *lines, destline;
3208 
3209   lines = gst_line_cache_get_lines (cache->prev, idx, out_line, in_line, 1);
3210   destline = lines[0];
3211 
3212   if (convert->dither[idx]) {
3213     GST_DEBUG ("Dither line %d %p", in_line, destline);
3214     gst_video_dither_line (convert->dither[idx], destline, 0, out_line,
3215         convert->out_width);
3216   }
3217   gst_line_cache_add_line (cache, in_line, destline);
3218 
3219   return TRUE;
3220 }
3221 
3222 typedef struct
3223 {
3224   GstLineCache *pack_lines;
3225   gint idx;
3226   gint h_0, h_1;
3227   gint pack_lines_count;
3228   gint out_y;
3229   gboolean identity_pack;
3230   gint lb_width, out_maxwidth;
3231   GstVideoFrame *dest;
3232 } ConvertTask;
3233 
3234 static void
convert_generic_task(ConvertTask * task)3235 convert_generic_task (ConvertTask * task)
3236 {
3237   gint i;
3238 
3239   for (i = task->h_0; i < task->h_1; i += task->pack_lines_count) {
3240     gpointer *lines;
3241 
3242     /* load the lines needed to pack */
3243     lines =
3244         gst_line_cache_get_lines (task->pack_lines, task->idx, i + task->out_y,
3245         i, task->pack_lines_count);
3246 
3247     if (!task->identity_pack) {
3248       /* take away the border */
3249       guint8 *l = ((guint8 *) lines[0]) - task->lb_width;
3250       /* and pack into destination */
3251       GST_DEBUG ("pack line %d %p (%p)", i + task->out_y, lines[0], l);
3252       PACK_FRAME (task->dest, l, i + task->out_y, task->out_maxwidth);
3253     }
3254   }
3255 }
3256 
3257 static void
video_converter_generic(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)3258 video_converter_generic (GstVideoConverter * convert, const GstVideoFrame * src,
3259     GstVideoFrame * dest)
3260 {
3261   gint i;
3262   gint out_maxwidth, out_maxheight;
3263   gint out_x, out_y, out_height;
3264   gint pack_lines, pstride;
3265   gint lb_width;
3266   ConvertTask *tasks;
3267   ConvertTask **tasks_p;
3268   gint n_threads;
3269   gint lines_per_thread;
3270 
3271   out_height = convert->out_height;
3272   out_maxwidth = convert->out_maxwidth;
3273   out_maxheight = convert->out_maxheight;
3274 
3275   out_x = convert->out_x;
3276   out_y = convert->out_y;
3277 
3278   convert->src = src;
3279   convert->dest = dest;
3280 
3281   if (GST_VIDEO_FRAME_IS_INTERLACED (src)) {
3282     GST_DEBUG ("setup interlaced frame");
3283     convert->upsample = convert->upsample_i;
3284     convert->downsample = convert->downsample_i;
3285     convert->v_scaler = convert->v_scaler_i;
3286   } else {
3287     GST_DEBUG ("setup progressive frame");
3288     convert->upsample = convert->upsample_p;
3289     convert->downsample = convert->downsample_p;
3290     convert->v_scaler = convert->v_scaler_p;
3291   }
3292   if (convert->upsample[0]) {
3293     gst_video_chroma_resample_get_info (convert->upsample[0],
3294         &convert->up_n_lines, &convert->up_offset);
3295   } else {
3296     convert->up_n_lines = 1;
3297     convert->up_offset = 0;
3298   }
3299   if (convert->downsample[0]) {
3300     gst_video_chroma_resample_get_info (convert->downsample[0],
3301         &convert->down_n_lines, &convert->down_offset);
3302   } else {
3303     convert->down_n_lines = 1;
3304     convert->down_offset = 0;
3305   }
3306 
3307   pack_lines = convert->pack_nlines;    /* only 1 for now */
3308   pstride = convert->pack_pstride;
3309 
3310   lb_width = out_x * pstride;
3311 
3312   if (convert->borderline) {
3313     /* FIXME we should try to avoid PACK_FRAME */
3314     for (i = 0; i < out_y; i++)
3315       PACK_FRAME (dest, convert->borderline, i, out_maxwidth);
3316   }
3317 
3318   n_threads = convert->conversion_runner->n_threads;
3319   tasks = convert->tasks[0] =
3320       g_renew (ConvertTask, convert->tasks[0], n_threads);
3321   tasks_p = convert->tasks_p[0] =
3322       g_renew (ConvertTask *, convert->tasks_p[0], n_threads);
3323 
3324   lines_per_thread =
3325       GST_ROUND_UP_N ((out_height + n_threads - 1) / n_threads, pack_lines);
3326 
3327   for (i = 0; i < n_threads; i++) {
3328     tasks[i].dest = dest;
3329     tasks[i].pack_lines = convert->pack_lines[i];
3330     tasks[i].idx = i;
3331     tasks[i].pack_lines_count = pack_lines;
3332     tasks[i].out_y = out_y;
3333     tasks[i].identity_pack = convert->identity_pack;
3334     tasks[i].lb_width = lb_width;
3335     tasks[i].out_maxwidth = out_maxwidth;
3336 
3337     tasks[i].h_0 = i * lines_per_thread;
3338     tasks[i].h_1 = MIN ((i + 1) * lines_per_thread, out_height);
3339 
3340     tasks_p[i] = &tasks[i];
3341   }
3342 
3343   gst_parallelized_task_runner_run (convert->conversion_runner,
3344       (GstParallelizedTaskFunc) convert_generic_task, (gpointer) tasks_p);
3345 
3346   if (convert->borderline) {
3347     for (i = out_y + out_height; i < out_maxheight; i++)
3348       PACK_FRAME (dest, convert->borderline, i, out_maxwidth);
3349   }
3350   if (convert->pack_pal) {
3351     memcpy (GST_VIDEO_FRAME_PLANE_DATA (dest, 1), convert->pack_pal,
3352         convert->pack_palsize);
3353   }
3354 }
3355 
3356 static void convert_fill_border (GstVideoConverter * convert,
3357     GstVideoFrame * dest);
3358 
3359 /* Fast paths */
3360 
3361 #define GET_LINE_OFFSETS(interlaced,line,l1,l2) \
3362     if (interlaced) {                           \
3363       l1 = (line & 2 ? line - 1 : line);        \
3364       l2 = l1 + 2;                              \
3365     } else {                                    \
3366       l1 = line;                                \
3367       l2 = l1 + 1;                              \
3368     }
3369 
3370 typedef struct
3371 {
3372   const GstVideoFrame *src;
3373   GstVideoFrame *dest;
3374   gint height_0, height_1;
3375 
3376   /* parameters */
3377   gboolean interlaced;
3378   gint width;
3379   gint alpha;
3380   MatrixData *data;
3381   gint in_x, in_y;
3382   gint out_x, out_y;
3383   gpointer tmpline;
3384 } FConvertTask;
3385 
3386 static void
convert_I420_YUY2_task(FConvertTask * task)3387 convert_I420_YUY2_task (FConvertTask * task)
3388 {
3389   gint i;
3390   gint l1, l2;
3391 
3392   for (i = task->height_0; i < task->height_1; i += 2) {
3393     GET_LINE_OFFSETS (task->interlaced, i, l1, l2);
3394 
3395     video_orc_convert_I420_YUY2 (FRAME_GET_LINE (task->dest, l1),
3396         FRAME_GET_LINE (task->dest, l2),
3397         FRAME_GET_Y_LINE (task->src, l1),
3398         FRAME_GET_Y_LINE (task->src, l2),
3399         FRAME_GET_U_LINE (task->src, i >> 1),
3400         FRAME_GET_V_LINE (task->src, i >> 1), (task->width + 1) / 2);
3401   }
3402 }
3403 
3404 static void
convert_I420_YUY2(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)3405 convert_I420_YUY2 (GstVideoConverter * convert, const GstVideoFrame * src,
3406     GstVideoFrame * dest)
3407 {
3408   int i;
3409   gint width = convert->in_width;
3410   gint height = convert->in_height;
3411   gboolean interlaced = GST_VIDEO_FRAME_IS_INTERLACED (src)
3412       && (GST_VIDEO_INFO_INTERLACE_MODE (&src->info) !=
3413       GST_VIDEO_INTERLACE_MODE_ALTERNATE);
3414   gint h2;
3415   FConvertTask *tasks;
3416   FConvertTask **tasks_p;
3417   gint n_threads;
3418   gint lines_per_thread;
3419 
3420   /* I420 has half as many chroma lines, as such we have to
3421    * always merge two into one. For non-interlaced these are
3422    * the two next to each other, for interlaced one is skipped
3423    * in between. */
3424   if (interlaced)
3425     h2 = GST_ROUND_DOWN_4 (height);
3426   else
3427     h2 = GST_ROUND_DOWN_2 (height);
3428 
3429   n_threads = convert->conversion_runner->n_threads;
3430   tasks = convert->tasks[0] =
3431       g_renew (FConvertTask, convert->tasks[0], n_threads);
3432   tasks_p = convert->tasks_p[0] =
3433       g_renew (FConvertTask *, convert->tasks_p[0], n_threads);
3434 
3435   lines_per_thread = GST_ROUND_UP_2 ((h2 + n_threads - 1) / n_threads);
3436 
3437   for (i = 0; i < n_threads; i++) {
3438     tasks[i].src = src;
3439     tasks[i].dest = dest;
3440 
3441     tasks[i].interlaced = interlaced;
3442     tasks[i].width = width;
3443 
3444     tasks[i].height_0 = i * lines_per_thread;
3445     tasks[i].height_1 = tasks[i].height_0 + lines_per_thread;
3446     tasks[i].height_1 = MIN (h2, tasks[i].height_1);
3447 
3448     tasks_p[i] = &tasks[i];
3449   }
3450 
3451   gst_parallelized_task_runner_run (convert->conversion_runner,
3452       (GstParallelizedTaskFunc) convert_I420_YUY2_task, (gpointer) tasks_p);
3453 
3454   /* now handle last lines. For interlaced these are up to 3 */
3455   if (h2 != height) {
3456     for (i = h2; i < height; i++) {
3457       UNPACK_FRAME (src, convert->tmpline[0], i, convert->in_x, width);
3458       PACK_FRAME (dest, convert->tmpline[0], i, width);
3459     }
3460   }
3461 }
3462 
3463 static void
convert_I420_UYVY_task(FConvertTask * task)3464 convert_I420_UYVY_task (FConvertTask * task)
3465 {
3466   gint i;
3467   gint l1, l2;
3468 
3469   for (i = task->height_0; i < task->height_1; i += 2) {
3470     GET_LINE_OFFSETS (task->interlaced, i, l1, l2);
3471 
3472     video_orc_convert_I420_UYVY (FRAME_GET_LINE (task->dest, l1),
3473         FRAME_GET_LINE (task->dest, l2),
3474         FRAME_GET_Y_LINE (task->src, l1),
3475         FRAME_GET_Y_LINE (task->src, l2),
3476         FRAME_GET_U_LINE (task->src, i >> 1),
3477         FRAME_GET_V_LINE (task->src, i >> 1), (task->width + 1) / 2);
3478   }
3479 }
3480 
3481 static void
convert_I420_UYVY(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)3482 convert_I420_UYVY (GstVideoConverter * convert, const GstVideoFrame * src,
3483     GstVideoFrame * dest)
3484 {
3485   int i;
3486   gint width = convert->in_width;
3487   gint height = convert->in_height;
3488   gboolean interlaced = GST_VIDEO_FRAME_IS_INTERLACED (src)
3489       && (GST_VIDEO_INFO_INTERLACE_MODE (&src->info) !=
3490       GST_VIDEO_INTERLACE_MODE_ALTERNATE);
3491   gint h2;
3492   FConvertTask *tasks;
3493   FConvertTask **tasks_p;
3494   gint n_threads;
3495   gint lines_per_thread;
3496 
3497   /* I420 has half as many chroma lines, as such we have to
3498    * always merge two into one. For non-interlaced these are
3499    * the two next to each other, for interlaced one is skipped
3500    * in between. */
3501   if (interlaced)
3502     h2 = GST_ROUND_DOWN_4 (height);
3503   else
3504     h2 = GST_ROUND_DOWN_2 (height);
3505 
3506   n_threads = convert->conversion_runner->n_threads;
3507   tasks = convert->tasks[0] =
3508       g_renew (FConvertTask, convert->tasks[0], n_threads);
3509   tasks_p = convert->tasks_p[0] =
3510       g_renew (FConvertTask *, convert->tasks_p[0], n_threads);
3511 
3512   lines_per_thread = GST_ROUND_UP_2 ((h2 + n_threads - 1) / n_threads);
3513 
3514   for (i = 0; i < n_threads; i++) {
3515     tasks[i].src = src;
3516     tasks[i].dest = dest;
3517 
3518     tasks[i].interlaced = interlaced;
3519     tasks[i].width = width;
3520 
3521     tasks[i].height_0 = i * lines_per_thread;
3522     tasks[i].height_1 = tasks[i].height_0 + lines_per_thread;
3523     tasks[i].height_1 = MIN (h2, tasks[i].height_1);
3524 
3525     tasks_p[i] = &tasks[i];
3526   }
3527 
3528   gst_parallelized_task_runner_run (convert->conversion_runner,
3529       (GstParallelizedTaskFunc) convert_I420_UYVY_task, (gpointer) tasks_p);
3530 
3531   /* now handle last lines. For interlaced these are up to 3 */
3532   if (h2 != height) {
3533     for (i = h2; i < height; i++) {
3534       UNPACK_FRAME (src, convert->tmpline[0], i, convert->in_x, width);
3535       PACK_FRAME (dest, convert->tmpline[0], i, width);
3536     }
3537   }
3538 }
3539 
3540 static void
convert_I420_AYUV_task(FConvertTask * task)3541 convert_I420_AYUV_task (FConvertTask * task)
3542 {
3543   gint i;
3544   gint l1, l2;
3545 
3546   for (i = task->height_0; i < task->height_1; i += 2) {
3547     GET_LINE_OFFSETS (task->interlaced, i, l1, l2);
3548 
3549     video_orc_convert_I420_AYUV (FRAME_GET_LINE (task->dest, l1),
3550         FRAME_GET_LINE (task->dest, l2),
3551         FRAME_GET_Y_LINE (task->src, l1),
3552         FRAME_GET_Y_LINE (task->src, l2),
3553         FRAME_GET_U_LINE (task->src, i >> 1), FRAME_GET_V_LINE (task->src,
3554             i >> 1), task->alpha, task->width);
3555   }
3556 }
3557 
3558 static void
convert_I420_AYUV(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)3559 convert_I420_AYUV (GstVideoConverter * convert, const GstVideoFrame * src,
3560     GstVideoFrame * dest)
3561 {
3562   int i;
3563   gint width = convert->in_width;
3564   gint height = convert->in_height;
3565   gboolean interlaced = GST_VIDEO_FRAME_IS_INTERLACED (src)
3566       && (GST_VIDEO_INFO_INTERLACE_MODE (&src->info) !=
3567       GST_VIDEO_INTERLACE_MODE_ALTERNATE);
3568   guint8 alpha = MIN (convert->alpha_value, 255);
3569   gint h2;
3570   FConvertTask *tasks;
3571   FConvertTask **tasks_p;
3572   gint n_threads;
3573   gint lines_per_thread;
3574 
3575   /* I420 has half as many chroma lines, as such we have to
3576    * always merge two into one. For non-interlaced these are
3577    * the two next to each other, for interlaced one is skipped
3578    * in between. */
3579   if (interlaced)
3580     h2 = GST_ROUND_DOWN_4 (height);
3581   else
3582     h2 = GST_ROUND_DOWN_2 (height);
3583 
3584 
3585   n_threads = convert->conversion_runner->n_threads;
3586   tasks = convert->tasks[0] =
3587       g_renew (FConvertTask, convert->tasks[0], n_threads);
3588   tasks_p = convert->tasks_p[0] =
3589       g_renew (FConvertTask *, convert->tasks_p[0], n_threads);
3590 
3591   lines_per_thread = GST_ROUND_UP_2 ((h2 + n_threads - 1) / n_threads);
3592 
3593   for (i = 0; i < n_threads; i++) {
3594     tasks[i].src = src;
3595     tasks[i].dest = dest;
3596 
3597     tasks[i].interlaced = interlaced;
3598     tasks[i].width = width;
3599     tasks[i].alpha = alpha;
3600 
3601     tasks[i].height_0 = i * lines_per_thread;
3602     tasks[i].height_1 = tasks[i].height_0 + lines_per_thread;
3603     tasks[i].height_1 = MIN (h2, tasks[i].height_1);
3604 
3605     tasks_p[i] = &tasks[i];
3606   }
3607 
3608   gst_parallelized_task_runner_run (convert->conversion_runner,
3609       (GstParallelizedTaskFunc) convert_I420_AYUV_task, (gpointer) tasks_p);
3610 
3611   /* now handle last lines. For interlaced these are up to 3 */
3612   if (h2 != height) {
3613     for (i = h2; i < height; i++) {
3614       UNPACK_FRAME (src, convert->tmpline[0], i, convert->in_x, width);
3615       if (alpha != 0xff)
3616         convert_set_alpha_u8 (convert, convert->tmpline[0], width);
3617       PACK_FRAME (dest, convert->tmpline[0], i, width);
3618     }
3619   }
3620 }
3621 
3622 static void
convert_I420_v210_task(FConvertTask * task)3623 convert_I420_v210_task (FConvertTask * task)
3624 {
3625   gint i, j;
3626   gint l1, l2;
3627   const guint8 *s_y1, *s_y2, *s_u, *s_v;
3628   guint8 *d1, *d2;
3629   guint32 a0, a1, a2, a3;
3630   guint8 y0_1, y1_1, y2_1, y3_1, y4_1, y5_1;
3631   guint8 u0_1, u2_1, u4_1;
3632   guint8 v0_1, v2_1, v4_1;
3633   guint8 y0_2, y1_2, y2_2, y3_2, y4_2, y5_2;
3634   guint8 u0_2, u2_2, u4_2;
3635   guint8 v0_2, v2_2, v4_2;
3636 
3637   for (i = task->height_0; i < task->height_1; i += 2) {
3638     GET_LINE_OFFSETS (task->interlaced, i, l1, l2);
3639 
3640     s_y1 = FRAME_GET_Y_LINE (task->src, l1);
3641     s_y2 = FRAME_GET_Y_LINE (task->src, l2);
3642     s_u = FRAME_GET_U_LINE (task->src, i >> 1);
3643     s_v = FRAME_GET_V_LINE (task->src, i >> 1);
3644 
3645     d1 = FRAME_GET_LINE (task->dest, l1);
3646     d2 = FRAME_GET_LINE (task->dest, l2);
3647 
3648     for (j = 0; j < task->width; j += 6) {
3649       y1_1 = y2_1 = y3_1 = y4_1 = y5_1 = 0;
3650       u2_1 = u4_1 = v2_1 = v4_1 = 0;
3651       y1_2 = y2_2 = y3_2 = y4_2 = y5_2 = 0;
3652       u2_2 = u4_2 = v2_2 = v4_2 = 0;
3653 
3654       y0_1 = s_y1[j];
3655       y0_2 = s_y2[j];
3656 
3657       u0_1 = u0_2 = s_u[j / 2];
3658       v0_1 = v0_2 = s_v[j / 2];
3659 
3660       if (j < task->width - 1) {
3661         y1_1 = s_y1[j + 1];
3662         y1_2 = s_y2[j + 1];
3663       }
3664 
3665       if (j < task->width - 2) {
3666         y2_1 = s_y1[j + 2];
3667         y2_2 = s_y2[j + 2];
3668 
3669         u2_1 = u2_2 = s_u[j / 2 + 1];
3670         v2_1 = v2_2 = s_v[j / 2 + 1];
3671       }
3672 
3673       if (j < task->width - 3) {
3674         y3_1 = s_y1[j + 3];
3675         y3_2 = s_y2[j + 3];
3676       }
3677 
3678       if (j < task->width - 4) {
3679         y4_1 = s_y1[j + 4];
3680         y4_2 = s_y2[j + 4];
3681 
3682         u4_1 = u4_2 = s_u[j / 2 + 2];
3683         v4_1 = v4_2 = s_v[j / 2 + 2];
3684       }
3685 
3686       if (j < task->width - 5) {
3687         y5_1 = s_y1[j + 5];
3688         y5_2 = s_y2[j + 5];
3689       }
3690 
3691       a0 = u0_1 << 2 | (y0_1 << 12) | (v0_1 << 22);
3692       a1 = y1_1 << 2 | (u2_1 << 12) | (y2_1 << 22);
3693       a2 = v2_1 << 2 | (y3_1 << 12) | (u4_1 << 22);
3694       a3 = y4_1 << 2 | (v4_1 << 12) | (y5_1 << 22);
3695 
3696       GST_WRITE_UINT32_LE (d1 + (j / 6) * 16 + 0, a0);
3697       GST_WRITE_UINT32_LE (d1 + (j / 6) * 16 + 4, a1);
3698       GST_WRITE_UINT32_LE (d1 + (j / 6) * 16 + 8, a2);
3699       GST_WRITE_UINT32_LE (d1 + (j / 6) * 16 + 12, a3);
3700 
3701       a0 = u0_2 << 2 | (y0_2 << 12) | (v0_2 << 22);
3702       a1 = y1_2 << 2 | (u2_2 << 12) | (y2_2 << 22);
3703       a2 = v2_2 << 2 | (y3_2 << 12) | (u4_2 << 22);
3704       a3 = y4_2 << 2 | (v4_2 << 12) | (y5_2 << 22);
3705 
3706       GST_WRITE_UINT32_LE (d2 + (j / 6) * 16 + 0, a0);
3707       GST_WRITE_UINT32_LE (d2 + (j / 6) * 16 + 4, a1);
3708       GST_WRITE_UINT32_LE (d2 + (j / 6) * 16 + 8, a2);
3709       GST_WRITE_UINT32_LE (d2 + (j / 6) * 16 + 12, a3);
3710     }
3711   }
3712 }
3713 
3714 static void
convert_I420_v210(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)3715 convert_I420_v210 (GstVideoConverter * convert, const GstVideoFrame * src,
3716     GstVideoFrame * dest)
3717 {
3718   int i;
3719   gint width = convert->in_width;
3720   gint height = convert->in_height;
3721   gboolean interlaced = GST_VIDEO_FRAME_IS_INTERLACED (src)
3722       && (GST_VIDEO_INFO_INTERLACE_MODE (&src->info) !=
3723       GST_VIDEO_INTERLACE_MODE_ALTERNATE);
3724   gint h2;
3725   FConvertTask *tasks;
3726   FConvertTask **tasks_p;
3727   gint n_threads;
3728   gint lines_per_thread;
3729   guint8 *tmpline_8;
3730 
3731   /* I420 has half as many chroma lines, as such we have to
3732    * always merge two into one. For non-interlaced these are
3733    * the two next to each other, for interlaced one is skipped
3734    * in between. */
3735   if (interlaced)
3736     h2 = GST_ROUND_DOWN_4 (height);
3737   else
3738     h2 = GST_ROUND_DOWN_2 (height);
3739 
3740   n_threads = convert->conversion_runner->n_threads;
3741   tasks = convert->tasks[0] =
3742       g_renew (FConvertTask, convert->tasks[0], n_threads);
3743   tasks_p = convert->tasks_p[0] =
3744       g_renew (FConvertTask *, convert->tasks_p[0], n_threads);
3745 
3746   lines_per_thread = GST_ROUND_UP_2 ((h2 + n_threads - 1) / n_threads);
3747 
3748   for (i = 0; i < n_threads; i++) {
3749     tasks[i].src = src;
3750     tasks[i].dest = dest;
3751 
3752     tasks[i].interlaced = interlaced;
3753     tasks[i].width = width;
3754 
3755     tasks[i].height_0 = i * lines_per_thread;
3756     tasks[i].height_1 = tasks[i].height_0 + lines_per_thread;
3757     tasks[i].height_1 = MIN (h2, tasks[i].height_1);
3758 
3759     tasks_p[i] = &tasks[i];
3760   }
3761 
3762   gst_parallelized_task_runner_run (convert->conversion_runner,
3763       (GstParallelizedTaskFunc) convert_I420_v210_task, (gpointer) tasks_p);
3764 
3765   /* now handle last lines. For interlaced these are up to 3 */
3766   if (h2 != height) {
3767     for (i = h2; i < height; i++) {
3768       UNPACK_FRAME (src, convert->tmpline[0], i, convert->in_x, width);
3769 
3770       tmpline_8 = (guint8 *) convert->tmpline[0];
3771       for (int j = width * 4 - 1; j >= 0; j--) {
3772         convert->tmpline[0][j] = tmpline_8[j] << 8;
3773       }
3774 
3775       PACK_FRAME (dest, convert->tmpline[0], i, width);
3776     }
3777   }
3778 }
3779 
3780 static void
convert_YUY2_I420_task(FConvertTask * task)3781 convert_YUY2_I420_task (FConvertTask * task)
3782 {
3783   gint i;
3784   gint l1, l2;
3785 
3786   for (i = task->height_0; i < task->height_1; i += 2) {
3787     GET_LINE_OFFSETS (task->interlaced, i, l1, l2);
3788 
3789     video_orc_convert_YUY2_I420 (FRAME_GET_Y_LINE (task->dest, l1),
3790         FRAME_GET_Y_LINE (task->dest, l2),
3791         FRAME_GET_U_LINE (task->dest, i >> 1),
3792         FRAME_GET_V_LINE (task->dest, i >> 1),
3793         FRAME_GET_LINE (task->src, l1), FRAME_GET_LINE (task->src, l2),
3794         (task->width + 1) / 2);
3795   }
3796 }
3797 
3798 static void
convert_YUY2_I420(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)3799 convert_YUY2_I420 (GstVideoConverter * convert, const GstVideoFrame * src,
3800     GstVideoFrame * dest)
3801 {
3802   int i;
3803   gint width = convert->in_width;
3804   gint height = convert->in_height;
3805   gboolean interlaced = GST_VIDEO_FRAME_IS_INTERLACED (src)
3806       && (GST_VIDEO_INFO_INTERLACE_MODE (&src->info) !=
3807       GST_VIDEO_INTERLACE_MODE_ALTERNATE);
3808   gint h2;
3809   FConvertTask *tasks;
3810   FConvertTask **tasks_p;
3811   gint n_threads;
3812   gint lines_per_thread;
3813 
3814   /* I420 has half as many chroma lines, as such we have to
3815    * always merge two into one. For non-interlaced these are
3816    * the two next to each other, for interlaced one is skipped
3817    * in between. */
3818   if (interlaced)
3819     h2 = GST_ROUND_DOWN_4 (height);
3820   else
3821     h2 = GST_ROUND_DOWN_2 (height);
3822 
3823   n_threads = convert->conversion_runner->n_threads;
3824   tasks = convert->tasks[0] =
3825       g_renew (FConvertTask, convert->tasks[0], n_threads);
3826   tasks_p = convert->tasks_p[0] =
3827       g_renew (FConvertTask *, convert->tasks_p[0], n_threads);
3828 
3829   lines_per_thread = GST_ROUND_UP_2 ((h2 + n_threads - 1) / n_threads);
3830 
3831   for (i = 0; i < n_threads; i++) {
3832     tasks[i].src = src;
3833     tasks[i].dest = dest;
3834 
3835     tasks[i].interlaced = interlaced;
3836     tasks[i].width = width;
3837 
3838     tasks[i].height_0 = i * lines_per_thread;
3839     tasks[i].height_1 = tasks[i].height_0 + lines_per_thread;
3840     tasks[i].height_1 = MIN (h2, tasks[i].height_1);
3841 
3842     tasks_p[i] = &tasks[i];
3843   }
3844 
3845   gst_parallelized_task_runner_run (convert->conversion_runner,
3846       (GstParallelizedTaskFunc) convert_YUY2_I420_task, (gpointer) tasks_p);
3847 
3848   /* now handle last lines. For interlaced these are up to 3 */
3849   if (h2 != height) {
3850     for (i = h2; i < height; i++) {
3851       UNPACK_FRAME (src, convert->tmpline[0], i, convert->in_x, width);
3852       PACK_FRAME (dest, convert->tmpline[0], i, width);
3853     }
3854   }
3855 }
3856 
3857 static void
convert_v210_I420_task(FConvertTask * task)3858 convert_v210_I420_task (FConvertTask * task)
3859 {
3860   gint i, j;
3861   gint l1, l2;
3862   guint8 *d_y1, *d_y2, *d_u, *d_v;
3863   const guint8 *s1, *s2;
3864   guint32 a0, a1, a2, a3;
3865   guint16 y0_1, y1_1, y2_1, y3_1, y4_1, y5_1;
3866   guint16 u0_1, u2_1, u4_1;
3867   guint16 v0_1, v2_1, v4_1;
3868   guint16 y0_2, y1_2, y2_2, y3_2, y4_2, y5_2;
3869   guint16 u0_2, u2_2, u4_2;
3870   guint16 v0_2, v2_2, v4_2;
3871 
3872   for (i = task->height_0; i < task->height_1; i += 2) {
3873     GET_LINE_OFFSETS (task->interlaced, i, l1, l2);
3874 
3875     d_y1 = FRAME_GET_Y_LINE (task->dest, l1);
3876     d_y2 = FRAME_GET_Y_LINE (task->dest, l2);
3877     d_u = FRAME_GET_U_LINE (task->dest, i >> 1);
3878     d_v = FRAME_GET_V_LINE (task->dest, i >> 1);
3879 
3880     s1 = FRAME_GET_LINE (task->src, l1);
3881     s2 = FRAME_GET_LINE (task->src, l2);
3882 
3883     for (j = 0; j < task->width; j += 6) {
3884       a0 = GST_READ_UINT32_LE (s1 + (j / 6) * 16 + 0);
3885       a1 = GST_READ_UINT32_LE (s1 + (j / 6) * 16 + 4);
3886       a2 = GST_READ_UINT32_LE (s1 + (j / 6) * 16 + 8);
3887       a3 = GST_READ_UINT32_LE (s1 + (j / 6) * 16 + 12);
3888 
3889       u0_1 = ((a0 >> 0) & 0x3ff) >> 2;
3890       y0_1 = ((a0 >> 10) & 0x3ff) >> 2;
3891       v0_1 = ((a0 >> 20) & 0x3ff) >> 2;
3892       y1_1 = ((a1 >> 0) & 0x3ff) >> 2;
3893 
3894       u2_1 = ((a1 >> 10) & 0x3ff) >> 2;
3895       y2_1 = ((a1 >> 20) & 0x3ff) >> 2;
3896       v2_1 = ((a2 >> 0) & 0x3ff) >> 2;
3897       y3_1 = ((a2 >> 10) & 0x3ff) >> 2;
3898 
3899       u4_1 = ((a2 >> 20) & 0x3ff) >> 2;
3900       y4_1 = ((a3 >> 0) & 0x3ff) >> 2;
3901       v4_1 = ((a3 >> 10) & 0x3ff) >> 2;
3902       y5_1 = ((a3 >> 20) & 0x3ff) >> 2;
3903 
3904       a0 = GST_READ_UINT32_LE (s2 + (j / 6) * 16 + 0);
3905       a1 = GST_READ_UINT32_LE (s2 + (j / 6) * 16 + 4);
3906       a2 = GST_READ_UINT32_LE (s2 + (j / 6) * 16 + 8);
3907       a3 = GST_READ_UINT32_LE (s2 + (j / 6) * 16 + 12);
3908 
3909       u0_2 = ((a0 >> 0) & 0x3ff) >> 2;
3910       y0_2 = ((a0 >> 10) & 0x3ff) >> 2;
3911       v0_2 = ((a0 >> 20) & 0x3ff) >> 2;
3912       y1_2 = ((a1 >> 0) & 0x3ff) >> 2;
3913 
3914       u2_2 = ((a1 >> 10) & 0x3ff) >> 2;
3915       y2_2 = ((a1 >> 20) & 0x3ff) >> 2;
3916       v2_2 = ((a2 >> 0) & 0x3ff) >> 2;
3917       y3_2 = ((a2 >> 10) & 0x3ff) >> 2;
3918 
3919       u4_2 = ((a2 >> 20) & 0x3ff) >> 2;
3920       y4_2 = ((a3 >> 0) & 0x3ff) >> 2;
3921       v4_2 = ((a3 >> 10) & 0x3ff) >> 2;
3922       y5_2 = ((a3 >> 20) & 0x3ff) >> 2;
3923 
3924       d_y1[j] = y0_1;
3925       d_y2[j] = y0_2;
3926       d_u[j / 2] = (u0_1 + u0_2) / 2;
3927       d_v[j / 2] = (v0_1 + v0_2) / 2;
3928 
3929       if (j < task->width - 1) {
3930         d_y1[j + 1] = y1_1;
3931         d_y2[j + 1] = y1_2;
3932       }
3933 
3934       if (j < task->width - 2) {
3935         d_y1[j + 2] = y2_1;
3936         d_y2[j + 2] = y2_2;
3937         d_u[j / 2 + 1] = (u2_1 + u2_2) / 2;
3938         d_v[j / 2 + 1] = (v2_1 + v2_2) / 2;
3939       }
3940 
3941       if (j < task->width - 3) {
3942         d_y1[j + 3] = y3_1;
3943         d_y2[j + 3] = y3_2;
3944       }
3945 
3946       if (j < task->width - 4) {
3947         d_y1[j + 4] = y4_1;
3948         d_y2[j + 4] = y4_2;
3949         d_u[j / 2 + 2] = (u4_1 + u4_2) / 2;
3950         d_v[j / 2 + 2] = (v4_1 + v4_2) / 2;
3951       }
3952 
3953       if (j < task->width - 5) {
3954         d_y1[j + 5] = y5_1;
3955         d_y2[j + 5] = y5_2;
3956       }
3957     }
3958   }
3959 }
3960 
3961 static void
convert_v210_I420(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)3962 convert_v210_I420 (GstVideoConverter * convert, const GstVideoFrame * src,
3963     GstVideoFrame * dest)
3964 {
3965   int i;
3966   gint width = convert->in_width;
3967   gint height = convert->in_height;
3968   gboolean interlaced = GST_VIDEO_FRAME_IS_INTERLACED (src)
3969       && (GST_VIDEO_INFO_INTERLACE_MODE (&src->info) !=
3970       GST_VIDEO_INTERLACE_MODE_ALTERNATE);
3971   gint h2;
3972   FConvertTask *tasks;
3973   FConvertTask **tasks_p;
3974   gint n_threads;
3975   gint lines_per_thread;
3976   guint8 *tmpline_8;
3977 
3978   /* I420 has half as many chroma lines, as such we have to
3979    * always merge two into one. For non-interlaced these are
3980    * the two next to each other, for interlaced one is skipped
3981    * in between. */
3982   if (interlaced)
3983     h2 = GST_ROUND_DOWN_4 (height);
3984   else
3985     h2 = GST_ROUND_DOWN_2 (height);
3986 
3987   n_threads = convert->conversion_runner->n_threads;
3988   tasks = convert->tasks[0] =
3989       g_renew (FConvertTask, convert->tasks[0], n_threads);
3990   tasks_p = convert->tasks_p[0] =
3991       g_renew (FConvertTask *, convert->tasks_p[0], n_threads);
3992 
3993   lines_per_thread = GST_ROUND_UP_2 ((h2 + n_threads - 1) / n_threads);
3994 
3995   for (i = 0; i < n_threads; i++) {
3996     tasks[i].src = src;
3997     tasks[i].dest = dest;
3998 
3999     tasks[i].interlaced = interlaced;
4000     tasks[i].width = width;
4001 
4002     tasks[i].height_0 = i * lines_per_thread;
4003     tasks[i].height_1 = tasks[i].height_0 + lines_per_thread;
4004     tasks[i].height_1 = MIN (h2, tasks[i].height_1);
4005 
4006     tasks_p[i] = &tasks[i];
4007   }
4008 
4009   gst_parallelized_task_runner_run (convert->conversion_runner,
4010       (GstParallelizedTaskFunc) convert_v210_I420_task, (gpointer) tasks_p);
4011 
4012   /* now handle last lines. For interlaced these are up to 3 */
4013   if (h2 != height) {
4014     for (i = h2; i < height; i++) {
4015       UNPACK_FRAME (src, convert->tmpline[0], i, convert->in_x, width);
4016 
4017       tmpline_8 = (guint8 *) convert->tmpline[0];
4018       for (int j = 0; j < width * 4; j++) {
4019         tmpline_8[j] = convert->tmpline[0][j] >> 8;
4020       }
4021 
4022       PACK_FRAME (dest, convert->tmpline[0], i, width);
4023     }
4024   }
4025 }
4026 
4027 typedef struct
4028 {
4029   const guint8 *s, *s2, *su, *sv;
4030   guint8 *d, *d2, *du, *dv;
4031   gint sstride, sustride, svstride;
4032   gint dstride, dustride, dvstride;
4033   gint width, height;
4034   gint alpha;
4035   MatrixData *data;
4036 } FConvertPlaneTask;
4037 
4038 static void
convert_YUY2_AYUV_task(FConvertPlaneTask * task)4039 convert_YUY2_AYUV_task (FConvertPlaneTask * task)
4040 {
4041   video_orc_convert_YUY2_AYUV (task->d, task->dstride, task->s,
4042       task->sstride, task->alpha, (task->width + 1) / 2, task->height);
4043 }
4044 
4045 static void
convert_YUY2_AYUV(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)4046 convert_YUY2_AYUV (GstVideoConverter * convert, const GstVideoFrame * src,
4047     GstVideoFrame * dest)
4048 {
4049   gint width = convert->in_width;
4050   gint height = convert->in_height;
4051   guint8 *s, *d;
4052   guint8 alpha = MIN (convert->alpha_value, 255);
4053   FConvertPlaneTask *tasks;
4054   FConvertPlaneTask **tasks_p;
4055   gint n_threads;
4056   gint lines_per_thread;
4057   gint i;
4058 
4059   s = FRAME_GET_LINE (src, convert->in_y);
4060   s += (GST_ROUND_UP_2 (convert->in_x) * 2);
4061   d = FRAME_GET_LINE (dest, convert->out_y);
4062   d += (convert->out_x * 4);
4063 
4064   n_threads = convert->conversion_runner->n_threads;
4065   tasks = convert->tasks[0] =
4066       g_renew (FConvertPlaneTask, convert->tasks[0], n_threads);
4067   tasks_p = convert->tasks_p[0] =
4068       g_renew (FConvertPlaneTask *, convert->tasks_p[0], n_threads);
4069 
4070   lines_per_thread = (height + n_threads - 1) / n_threads;
4071 
4072   for (i = 0; i < n_threads; i++) {
4073     tasks[i].dstride = FRAME_GET_STRIDE (dest);
4074     tasks[i].sstride = FRAME_GET_STRIDE (src);
4075     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
4076     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
4077 
4078     tasks[i].width = width;
4079     tasks[i].height = (i + 1) * lines_per_thread;
4080     tasks[i].height = MIN (tasks[i].height, height);
4081     tasks[i].height -= i * lines_per_thread;
4082     tasks[i].alpha = alpha;
4083 
4084     tasks_p[i] = &tasks[i];
4085   }
4086 
4087   gst_parallelized_task_runner_run (convert->conversion_runner,
4088       (GstParallelizedTaskFunc) convert_YUY2_AYUV_task, (gpointer) tasks_p);
4089 
4090   convert_fill_border (convert, dest);
4091 }
4092 
4093 static void
convert_YUY2_v210_task(FConvertPlaneTask * task)4094 convert_YUY2_v210_task (FConvertPlaneTask * task)
4095 {
4096   gint i, j;
4097   guint8 *d;
4098   const guint8 *s;
4099   guint32 a0, a1, a2, a3;
4100   guint8 y0, y1, y2, y3, y4, y5;
4101   guint8 u0, u2, u4;
4102   guint8 v0, v2, v4;
4103 
4104   for (i = 0; i < task->height; i++) {
4105     d = task->d + i * task->dstride;
4106     s = task->s + i * task->sstride;
4107 
4108     for (j = 0; j < task->width; j += 6) {
4109       y1 = y2 = y3 = y4 = y5 = 0;
4110       u2 = u4 = v2 = v4 = 0;
4111 
4112       y0 = s[2 * j];
4113       u0 = s[2 * j + 1];
4114       v0 = s[2 * j + 3];
4115 
4116       if (j < task->width - 1) {
4117         y1 = s[2 * j + 2];
4118       }
4119 
4120       if (j < task->width - 2) {
4121         y2 = s[2 * j + 4];
4122         u2 = s[2 * j + 5];
4123         v2 = s[2 * j + 7];
4124       }
4125 
4126       if (j < task->width - 3) {
4127         y3 = s[2 * j + 6];
4128       }
4129 
4130       if (j < task->width - 4) {
4131         y4 = s[2 * j + 8];
4132         u4 = s[2 * j + 9];
4133         v4 = s[2 * j + 11];
4134       }
4135 
4136       if (j < task->width - 5) {
4137         y5 = s[2 * j + 10];
4138       }
4139 
4140       a0 = u0 << 2 | (y0 << 12) | (v0 << 22);
4141       a1 = y1 << 2 | (u2 << 12) | (y2 << 22);
4142       a2 = v2 << 2 | (y3 << 12) | (u4 << 22);
4143       a3 = y4 << 2 | (v4 << 12) | (y5 << 22);
4144 
4145       GST_WRITE_UINT32_LE (d + (j / 6) * 16 + 0, a0);
4146       GST_WRITE_UINT32_LE (d + (j / 6) * 16 + 4, a1);
4147       GST_WRITE_UINT32_LE (d + (j / 6) * 16 + 8, a2);
4148       GST_WRITE_UINT32_LE (d + (j / 6) * 16 + 12, a3);
4149     }
4150   }
4151 }
4152 
4153 static void
convert_YUY2_v210(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)4154 convert_YUY2_v210 (GstVideoConverter * convert, const GstVideoFrame * src,
4155     GstVideoFrame * dest)
4156 {
4157   gint width = convert->in_width;
4158   gint height = convert->in_height;
4159   guint8 *s, *d;
4160   FConvertPlaneTask *tasks;
4161   FConvertPlaneTask **tasks_p;
4162   gint n_threads;
4163   gint lines_per_thread;
4164   gint i;
4165 
4166   s = FRAME_GET_LINE (src, convert->in_y);
4167   s += (GST_ROUND_UP_2 (convert->in_x) * 2);
4168   d = FRAME_GET_LINE (dest, convert->out_y);
4169   d += (GST_ROUND_UP_2 (convert->out_x) * 2);
4170 
4171   n_threads = convert->conversion_runner->n_threads;
4172   tasks = convert->tasks[0] =
4173       g_renew (FConvertPlaneTask, convert->tasks[0], n_threads);
4174   tasks_p = convert->tasks_p[0] =
4175       g_renew (FConvertPlaneTask *, convert->tasks_p[0], n_threads);
4176 
4177   lines_per_thread = (height + n_threads - 1) / n_threads;
4178 
4179   for (i = 0; i < n_threads; i++) {
4180     tasks[i].dstride = FRAME_GET_STRIDE (dest);
4181     tasks[i].sstride = FRAME_GET_STRIDE (src);
4182     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
4183     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
4184 
4185     tasks[i].width = width;
4186     tasks[i].height = (i + 1) * lines_per_thread;
4187     tasks[i].height = MIN (tasks[i].height, height);
4188     tasks[i].height -= i * lines_per_thread;
4189 
4190     tasks_p[i] = &tasks[i];
4191   }
4192 
4193   gst_parallelized_task_runner_run (convert->conversion_runner,
4194       (GstParallelizedTaskFunc) convert_YUY2_v210_task, (gpointer) tasks_p);
4195 
4196   convert_fill_border (convert, dest);
4197 }
4198 
4199 static void
convert_YUY2_Y42B_task(FConvertPlaneTask * task)4200 convert_YUY2_Y42B_task (FConvertPlaneTask * task)
4201 {
4202   video_orc_convert_YUY2_Y42B (task->d, task->dstride, task->du,
4203       task->dustride, task->dv, task->dvstride,
4204       task->s, task->sstride, (task->width + 1) / 2, task->height);
4205 }
4206 
4207 static void
convert_YUY2_Y42B(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)4208 convert_YUY2_Y42B (GstVideoConverter * convert, const GstVideoFrame * src,
4209     GstVideoFrame * dest)
4210 {
4211   gint width = convert->in_width;
4212   gint height = convert->in_height;
4213   guint8 *s, *dy, *du, *dv;
4214   FConvertPlaneTask *tasks;
4215   FConvertPlaneTask **tasks_p;
4216   gint n_threads;
4217   gint lines_per_thread;
4218   gint i;
4219 
4220   s = FRAME_GET_LINE (src, convert->in_y);
4221   s += (GST_ROUND_UP_2 (convert->in_x) * 2);
4222 
4223   dy = FRAME_GET_Y_LINE (dest, convert->out_y);
4224   dy += convert->out_x;
4225   du = FRAME_GET_U_LINE (dest, convert->out_y);
4226   du += convert->out_x >> 1;
4227   dv = FRAME_GET_V_LINE (dest, convert->out_y);
4228   dv += convert->out_x >> 1;
4229 
4230   n_threads = convert->conversion_runner->n_threads;
4231   tasks = convert->tasks[0] =
4232       g_renew (FConvertPlaneTask, convert->tasks[0], n_threads);
4233   tasks_p = convert->tasks_p[0] =
4234       g_renew (FConvertPlaneTask *, convert->tasks_p[0], n_threads);
4235 
4236   lines_per_thread = (height + n_threads - 1) / n_threads;
4237 
4238   for (i = 0; i < n_threads; i++) {
4239     tasks[i].dstride = FRAME_GET_Y_STRIDE (dest);
4240     tasks[i].dustride = FRAME_GET_U_STRIDE (dest);
4241     tasks[i].dvstride = FRAME_GET_V_STRIDE (dest);
4242     tasks[i].sstride = FRAME_GET_STRIDE (src);
4243     tasks[i].d = dy + i * lines_per_thread * tasks[i].dstride;
4244     tasks[i].du = du + i * lines_per_thread * tasks[i].dustride;
4245     tasks[i].dv = dv + i * lines_per_thread * tasks[i].dvstride;
4246     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
4247 
4248     tasks[i].width = width;
4249     tasks[i].height = (i + 1) * lines_per_thread;
4250     tasks[i].height = MIN (tasks[i].height, height);
4251     tasks[i].height -= i * lines_per_thread;
4252 
4253     tasks_p[i] = &tasks[i];
4254   }
4255 
4256   gst_parallelized_task_runner_run (convert->conversion_runner,
4257       (GstParallelizedTaskFunc) convert_YUY2_Y42B_task, (gpointer) tasks_p);
4258 
4259   convert_fill_border (convert, dest);
4260 }
4261 
4262 static void
convert_YUY2_Y444_task(FConvertPlaneTask * task)4263 convert_YUY2_Y444_task (FConvertPlaneTask * task)
4264 {
4265   video_orc_convert_YUY2_Y444 (task->d,
4266       task->dstride, task->du,
4267       task->dustride, task->dv,
4268       task->dvstride, task->s,
4269       task->sstride, (task->width + 1) / 2, task->height);
4270 }
4271 
4272 static void
convert_YUY2_Y444(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)4273 convert_YUY2_Y444 (GstVideoConverter * convert, const GstVideoFrame * src,
4274     GstVideoFrame * dest)
4275 {
4276   gint width = convert->in_width;
4277   gint height = convert->in_height;
4278   guint8 *s, *dy, *du, *dv;
4279   FConvertPlaneTask *tasks;
4280   FConvertPlaneTask **tasks_p;
4281   gint n_threads;
4282   gint lines_per_thread;
4283   gint i;
4284 
4285   s = FRAME_GET_LINE (src, convert->in_y);
4286   s += (GST_ROUND_UP_2 (convert->in_x) * 2);
4287 
4288   dy = FRAME_GET_Y_LINE (dest, convert->out_y);
4289   dy += convert->out_x;
4290   du = FRAME_GET_U_LINE (dest, convert->out_y);
4291   du += convert->out_x;
4292   dv = FRAME_GET_V_LINE (dest, convert->out_y);
4293   dv += convert->out_x;
4294 
4295   n_threads = convert->conversion_runner->n_threads;
4296   tasks = convert->tasks[0] =
4297       g_renew (FConvertPlaneTask, convert->tasks[0], n_threads);
4298   tasks_p = convert->tasks_p[0] =
4299       g_renew (FConvertPlaneTask *, convert->tasks_p[0], n_threads);
4300 
4301   lines_per_thread = (height + n_threads - 1) / n_threads;
4302 
4303   for (i = 0; i < n_threads; i++) {
4304     tasks[i].dstride = FRAME_GET_Y_STRIDE (dest);
4305     tasks[i].dustride = FRAME_GET_U_STRIDE (dest);
4306     tasks[i].dvstride = FRAME_GET_V_STRIDE (dest);
4307     tasks[i].sstride = FRAME_GET_STRIDE (src);
4308     tasks[i].d = dy + i * lines_per_thread * tasks[i].dstride;
4309     tasks[i].du = du + i * lines_per_thread * tasks[i].dustride;
4310     tasks[i].dv = dv + i * lines_per_thread * tasks[i].dvstride;
4311     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
4312 
4313     tasks[i].width = width;
4314     tasks[i].height = (i + 1) * lines_per_thread;
4315     tasks[i].height = MIN (tasks[i].height, height);
4316     tasks[i].height -= i * lines_per_thread;
4317 
4318     tasks_p[i] = &tasks[i];
4319   }
4320 
4321   gst_parallelized_task_runner_run (convert->conversion_runner,
4322       (GstParallelizedTaskFunc) convert_YUY2_Y444_task, (gpointer) tasks_p);
4323 
4324   convert_fill_border (convert, dest);
4325 }
4326 
4327 static void
convert_v210_Y42B_task(FConvertPlaneTask * task)4328 convert_v210_Y42B_task (FConvertPlaneTask * task)
4329 {
4330   gint i, j;
4331   guint8 *d_y, *d_u, *d_v;
4332   const guint8 *s;
4333   guint32 a0, a1, a2, a3;
4334   guint16 y0, y1, y2, y3, y4, y5;
4335   guint16 u0, u2, u4;
4336   guint16 v0, v2, v4;
4337 
4338   for (i = 0; i < task->height; i++) {
4339     d_y = task->d + i * task->dstride;
4340     d_u = task->du + i * task->dustride;
4341     d_v = task->dv + i * task->dvstride;
4342     s = task->s + i * task->sstride;
4343 
4344     for (j = 0; j < task->width; j += 6) {
4345       a0 = GST_READ_UINT32_LE (s + (j / 6) * 16 + 0);
4346       a1 = GST_READ_UINT32_LE (s + (j / 6) * 16 + 4);
4347       a2 = GST_READ_UINT32_LE (s + (j / 6) * 16 + 8);
4348       a3 = GST_READ_UINT32_LE (s + (j / 6) * 16 + 12);
4349 
4350       u0 = ((a0 >> 0) & 0x3ff) >> 2;
4351       y0 = ((a0 >> 10) & 0x3ff) >> 2;
4352       v0 = ((a0 >> 20) & 0x3ff) >> 2;
4353       y1 = ((a1 >> 0) & 0x3ff) >> 2;
4354 
4355       u2 = ((a1 >> 10) & 0x3ff) >> 2;
4356       y2 = ((a1 >> 20) & 0x3ff) >> 2;
4357       v2 = ((a2 >> 0) & 0x3ff) >> 2;
4358       y3 = ((a2 >> 10) & 0x3ff) >> 2;
4359 
4360       u4 = ((a2 >> 20) & 0x3ff) >> 2;
4361       y4 = ((a3 >> 0) & 0x3ff) >> 2;
4362       v4 = ((a3 >> 10) & 0x3ff) >> 2;
4363       y5 = ((a3 >> 20) & 0x3ff) >> 2;
4364 
4365       d_y[j] = y0;
4366       d_u[j / 2] = u0;
4367       d_v[j / 2] = v0;
4368 
4369       if (j < task->width - 1) {
4370         d_y[j + 1] = y1;
4371       }
4372 
4373       if (j < task->width - 2) {
4374         d_y[j + 2] = y2;
4375         d_u[j / 2 + 1] = u2;
4376         d_v[j / 2 + 1] = v2;
4377       }
4378 
4379       if (j < task->width - 3) {
4380         d_y[j + 3] = y3;
4381       }
4382 
4383       if (j < task->width - 4) {
4384         d_y[j + 4] = y4;
4385         d_u[j / 2 + 2] = u4;
4386         d_v[j / 2 + 2] = v4;
4387       }
4388 
4389       if (j < task->width - 5) {
4390         d_y[j + 5] = y5;
4391       }
4392     }
4393   }
4394 }
4395 
4396 static void
convert_v210_Y42B(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)4397 convert_v210_Y42B (GstVideoConverter * convert, const GstVideoFrame * src,
4398     GstVideoFrame * dest)
4399 {
4400   gint width = convert->in_width;
4401   gint height = convert->in_height;
4402   guint8 *s, *dy, *du, *dv;
4403   FConvertPlaneTask *tasks;
4404   FConvertPlaneTask **tasks_p;
4405   gint n_threads;
4406   gint lines_per_thread;
4407   gint i;
4408 
4409   s = FRAME_GET_LINE (src, convert->in_y);
4410   s += (GST_ROUND_UP_2 (convert->in_x) * 2);
4411 
4412   dy = FRAME_GET_Y_LINE (dest, convert->out_y);
4413   dy += convert->out_x;
4414   du = FRAME_GET_U_LINE (dest, convert->out_y);
4415   du += convert->out_x >> 1;
4416   dv = FRAME_GET_V_LINE (dest, convert->out_y);
4417   dv += convert->out_x >> 1;
4418 
4419   n_threads = convert->conversion_runner->n_threads;
4420   tasks = convert->tasks[0] =
4421       g_renew (FConvertPlaneTask, convert->tasks[0], n_threads);
4422   tasks_p = convert->tasks_p[0] =
4423       g_renew (FConvertPlaneTask *, convert->tasks_p[0], n_threads);
4424 
4425   lines_per_thread = (height + n_threads - 1) / n_threads;
4426 
4427   for (i = 0; i < n_threads; i++) {
4428     tasks[i].dstride = FRAME_GET_Y_STRIDE (dest);
4429     tasks[i].dustride = FRAME_GET_U_STRIDE (dest);
4430     tasks[i].dvstride = FRAME_GET_V_STRIDE (dest);
4431     tasks[i].sstride = FRAME_GET_STRIDE (src);
4432     tasks[i].d = dy + i * lines_per_thread * tasks[i].dstride;
4433     tasks[i].du = du + i * lines_per_thread * tasks[i].dustride;
4434     tasks[i].dv = dv + i * lines_per_thread * tasks[i].dvstride;
4435     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
4436 
4437     tasks[i].width = width;
4438     tasks[i].height = (i + 1) * lines_per_thread;
4439     tasks[i].height = MIN (tasks[i].height, height);
4440     tasks[i].height -= i * lines_per_thread;
4441 
4442     tasks_p[i] = &tasks[i];
4443   }
4444 
4445   gst_parallelized_task_runner_run (convert->conversion_runner,
4446       (GstParallelizedTaskFunc) convert_v210_Y42B_task, (gpointer) tasks_p);
4447 
4448   convert_fill_border (convert, dest);
4449 }
4450 
4451 static void
convert_UYVY_I420_task(FConvertTask * task)4452 convert_UYVY_I420_task (FConvertTask * task)
4453 {
4454   gint i;
4455   gint l1, l2;
4456 
4457   for (i = task->height_0; i < task->height_1; i += 2) {
4458     GET_LINE_OFFSETS (task->interlaced, i, l1, l2);
4459 
4460     video_orc_convert_UYVY_I420 (FRAME_GET_COMP_LINE (task->dest, 0, l1),
4461         FRAME_GET_COMP_LINE (task->dest, 0, l2),
4462         FRAME_GET_COMP_LINE (task->dest, 1, i >> 1),
4463         FRAME_GET_COMP_LINE (task->dest, 2, i >> 1),
4464         FRAME_GET_LINE (task->src, l1), FRAME_GET_LINE (task->src, l2),
4465         (task->width + 1) / 2);
4466   }
4467 }
4468 
4469 static void
convert_UYVY_I420(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)4470 convert_UYVY_I420 (GstVideoConverter * convert, const GstVideoFrame * src,
4471     GstVideoFrame * dest)
4472 {
4473   int i;
4474   gint width = convert->in_width;
4475   gint height = convert->in_height;
4476   gboolean interlaced = GST_VIDEO_FRAME_IS_INTERLACED (src)
4477       && (GST_VIDEO_INFO_INTERLACE_MODE (&src->info) !=
4478       GST_VIDEO_INTERLACE_MODE_ALTERNATE);
4479   gint h2;
4480   FConvertTask *tasks;
4481   FConvertTask **tasks_p;
4482   gint n_threads;
4483   gint lines_per_thread;
4484 
4485   /* I420 has half as many chroma lines, as such we have to
4486    * always merge two into one. For non-interlaced these are
4487    * the two next to each other, for interlaced one is skipped
4488    * in between. */
4489   if (interlaced)
4490     h2 = GST_ROUND_DOWN_4 (height);
4491   else
4492     h2 = GST_ROUND_DOWN_2 (height);
4493 
4494   n_threads = convert->conversion_runner->n_threads;
4495   tasks = convert->tasks[0] =
4496       g_renew (FConvertTask, convert->tasks[0], n_threads);
4497   tasks_p = convert->tasks_p[0] =
4498       g_renew (FConvertTask *, convert->tasks_p[0], n_threads);
4499 
4500   lines_per_thread = GST_ROUND_UP_2 ((h2 + n_threads - 1) / n_threads);
4501 
4502   for (i = 0; i < n_threads; i++) {
4503     tasks[i].src = src;
4504     tasks[i].dest = dest;
4505 
4506     tasks[i].interlaced = interlaced;
4507     tasks[i].width = width;
4508 
4509     tasks[i].height_0 = i * lines_per_thread;
4510     tasks[i].height_1 = tasks[i].height_0 + lines_per_thread;
4511     tasks[i].height_1 = MIN (h2, tasks[i].height_1);
4512 
4513     tasks_p[i] = &tasks[i];
4514   }
4515 
4516   gst_parallelized_task_runner_run (convert->conversion_runner,
4517       (GstParallelizedTaskFunc) convert_UYVY_I420_task, (gpointer) tasks_p);
4518 
4519   /* now handle last lines. For interlaced these are up to 3 */
4520   if (h2 != height) {
4521     for (i = h2; i < height; i++) {
4522       UNPACK_FRAME (src, convert->tmpline[0], i, convert->in_x, width);
4523       PACK_FRAME (dest, convert->tmpline[0], i, width);
4524     }
4525   }
4526 }
4527 
4528 static void
convert_UYVY_AYUV_task(FConvertPlaneTask * task)4529 convert_UYVY_AYUV_task (FConvertPlaneTask * task)
4530 {
4531   video_orc_convert_UYVY_AYUV (task->d, task->dstride, task->s,
4532       task->sstride, task->alpha, (task->width + 1) / 2, task->height);
4533 }
4534 
4535 static void
convert_UYVY_AYUV(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)4536 convert_UYVY_AYUV (GstVideoConverter * convert, const GstVideoFrame * src,
4537     GstVideoFrame * dest)
4538 {
4539   gint width = convert->in_width;
4540   gint height = convert->in_height;
4541   guint8 *s, *d;
4542   guint8 alpha = MIN (convert->alpha_value, 255);
4543   FConvertPlaneTask *tasks;
4544   FConvertPlaneTask **tasks_p;
4545   gint n_threads;
4546   gint lines_per_thread;
4547   gint i;
4548 
4549   s = FRAME_GET_LINE (src, convert->in_y);
4550   s += (GST_ROUND_UP_2 (convert->in_x) * 2);
4551   d = FRAME_GET_LINE (dest, convert->out_y);
4552   d += (convert->out_x * 4);
4553 
4554   n_threads = convert->conversion_runner->n_threads;
4555   tasks = convert->tasks[0] =
4556       g_renew (FConvertPlaneTask, convert->tasks[0], n_threads);
4557   tasks_p = convert->tasks_p[0] =
4558       g_renew (FConvertPlaneTask *, convert->tasks_p[0], n_threads);
4559 
4560   lines_per_thread = (height + n_threads - 1) / n_threads;
4561 
4562   for (i = 0; i < n_threads; i++) {
4563     tasks[i].dstride = FRAME_GET_STRIDE (dest);
4564     tasks[i].sstride = FRAME_GET_STRIDE (src);
4565     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
4566     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
4567 
4568     tasks[i].width = width;
4569     tasks[i].height = (i + 1) * lines_per_thread;
4570     tasks[i].height = MIN (tasks[i].height, height);
4571     tasks[i].height -= i * lines_per_thread;
4572     tasks[i].alpha = alpha;
4573 
4574     tasks_p[i] = &tasks[i];
4575   }
4576 
4577   gst_parallelized_task_runner_run (convert->conversion_runner,
4578       (GstParallelizedTaskFunc) convert_UYVY_AYUV_task, (gpointer) tasks_p);
4579 
4580   convert_fill_border (convert, dest);
4581 }
4582 
4583 static void
convert_UYVY_v210_task(FConvertPlaneTask * task)4584 convert_UYVY_v210_task (FConvertPlaneTask * task)
4585 {
4586   gint i, j;
4587   guint8 *d;
4588   const guint8 *s;
4589   guint32 a0, a1, a2, a3;
4590   guint8 y0, y1, y2, y3, y4, y5;
4591   guint8 u0, u2, u4;
4592   guint8 v0, v2, v4;
4593 
4594   for (i = 0; i < task->height; i++) {
4595     d = task->d + i * task->dstride;
4596     s = task->s + i * task->sstride;
4597 
4598     for (j = 0; j < task->width; j += 6) {
4599       y1 = y2 = y3 = y4 = y5 = 0;
4600       u2 = u4 = v2 = v4 = 0;
4601 
4602       y0 = s[2 * j + 1];
4603       u0 = s[2 * j];
4604       v0 = s[2 * j + 2];
4605 
4606       if (j < task->width - 1) {
4607         y1 = s[2 * j + 3];
4608       }
4609 
4610       if (j < task->width - 2) {
4611         y2 = s[2 * j + 5];
4612         u2 = s[2 * j + 4];
4613         v2 = s[2 * j + 6];
4614       }
4615 
4616       if (j < task->width - 3) {
4617         y3 = s[2 * j + 7];
4618       }
4619 
4620       if (j < task->width - 4) {
4621         y4 = s[2 * j + 9];
4622         u4 = s[2 * j + 8];
4623         v4 = s[2 * j + 10];
4624       }
4625 
4626       if (j < task->width - 5) {
4627         y5 = s[2 * j + 11];
4628       }
4629 
4630       a0 = u0 << 2 | (y0 << 12) | (v0 << 22);
4631       a1 = y1 << 2 | (u2 << 12) | (y2 << 22);
4632       a2 = v2 << 2 | (y3 << 12) | (u4 << 22);
4633       a3 = y4 << 2 | (v4 << 12) | (y5 << 22);
4634 
4635       GST_WRITE_UINT32_LE (d + (j / 6) * 16 + 0, a0);
4636       GST_WRITE_UINT32_LE (d + (j / 6) * 16 + 4, a1);
4637       GST_WRITE_UINT32_LE (d + (j / 6) * 16 + 8, a2);
4638       GST_WRITE_UINT32_LE (d + (j / 6) * 16 + 12, a3);
4639     }
4640   }
4641 }
4642 
4643 static void
convert_UYVY_v210(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)4644 convert_UYVY_v210 (GstVideoConverter * convert, const GstVideoFrame * src,
4645     GstVideoFrame * dest)
4646 {
4647   gint width = convert->in_width;
4648   gint height = convert->in_height;
4649   guint8 *s, *d;
4650   FConvertPlaneTask *tasks;
4651   FConvertPlaneTask **tasks_p;
4652   gint n_threads;
4653   gint lines_per_thread;
4654   gint i;
4655 
4656   s = FRAME_GET_LINE (src, convert->in_y);
4657   s += (GST_ROUND_UP_2 (convert->in_x) * 2);
4658   d = FRAME_GET_LINE (dest, convert->out_y);
4659   d += (GST_ROUND_UP_2 (convert->out_x) * 2);
4660 
4661   n_threads = convert->conversion_runner->n_threads;
4662   tasks = convert->tasks[0] =
4663       g_renew (FConvertPlaneTask, convert->tasks[0], n_threads);
4664   tasks_p = convert->tasks_p[0] =
4665       g_renew (FConvertPlaneTask *, convert->tasks_p[0], n_threads);
4666 
4667   lines_per_thread = (height + n_threads - 1) / n_threads;
4668 
4669   for (i = 0; i < n_threads; i++) {
4670     tasks[i].dstride = FRAME_GET_STRIDE (dest);
4671     tasks[i].sstride = FRAME_GET_STRIDE (src);
4672     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
4673     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
4674 
4675     tasks[i].width = width;
4676     tasks[i].height = (i + 1) * lines_per_thread;
4677     tasks[i].height = MIN (tasks[i].height, height);
4678     tasks[i].height -= i * lines_per_thread;
4679 
4680     tasks_p[i] = &tasks[i];
4681   }
4682 
4683   gst_parallelized_task_runner_run (convert->conversion_runner,
4684       (GstParallelizedTaskFunc) convert_UYVY_v210_task, (gpointer) tasks_p);
4685 
4686   convert_fill_border (convert, dest);
4687 }
4688 
4689 static void
convert_UYVY_YUY2_task(FConvertPlaneTask * task)4690 convert_UYVY_YUY2_task (FConvertPlaneTask * task)
4691 {
4692   video_orc_convert_UYVY_YUY2 (task->d, task->dstride, task->s,
4693       task->sstride, (task->width + 1) / 2, task->height);
4694 }
4695 
4696 static void
convert_UYVY_YUY2(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)4697 convert_UYVY_YUY2 (GstVideoConverter * convert, const GstVideoFrame * src,
4698     GstVideoFrame * dest)
4699 {
4700   gint width = convert->in_width;
4701   gint height = convert->in_height;
4702   guint8 *s, *d;
4703   FConvertPlaneTask *tasks;
4704   FConvertPlaneTask **tasks_p;
4705   gint n_threads;
4706   gint lines_per_thread;
4707   gint i;
4708 
4709   s = FRAME_GET_LINE (src, convert->in_y);
4710   s += (GST_ROUND_UP_2 (convert->in_x) * 2);
4711   d = FRAME_GET_LINE (dest, convert->out_y);
4712   d += (GST_ROUND_UP_2 (convert->out_x) * 2);
4713 
4714   n_threads = convert->conversion_runner->n_threads;
4715   tasks = convert->tasks[0] =
4716       g_renew (FConvertPlaneTask, convert->tasks[0], n_threads);
4717   tasks_p = convert->tasks_p[0] =
4718       g_renew (FConvertPlaneTask *, convert->tasks_p[0], n_threads);
4719 
4720   lines_per_thread = (height + n_threads - 1) / n_threads;
4721 
4722   for (i = 0; i < n_threads; i++) {
4723     tasks[i].dstride = FRAME_GET_STRIDE (dest);
4724     tasks[i].sstride = FRAME_GET_STRIDE (src);
4725     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
4726     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
4727 
4728     tasks[i].width = width;
4729     tasks[i].height = (i + 1) * lines_per_thread;
4730     tasks[i].height = MIN (tasks[i].height, height);
4731     tasks[i].height -= i * lines_per_thread;
4732 
4733     tasks_p[i] = &tasks[i];
4734   }
4735 
4736   gst_parallelized_task_runner_run (convert->conversion_runner,
4737       (GstParallelizedTaskFunc) convert_UYVY_YUY2_task, (gpointer) tasks_p);
4738 
4739   convert_fill_border (convert, dest);
4740 }
4741 
4742 static void
convert_v210_UYVY_task(FConvertPlaneTask * task)4743 convert_v210_UYVY_task (FConvertPlaneTask * task)
4744 {
4745   gint i, j;
4746   guint8 *d;
4747   const guint8 *s;
4748   guint32 a0, a1, a2, a3;
4749   guint16 y0, y1, y2, y3, y4, y5;
4750   guint16 u0, u2, u4;
4751   guint16 v0, v2, v4;
4752 
4753   for (i = 0; i < task->height; i++) {
4754     d = task->d + i * task->dstride;
4755     s = task->s + i * task->sstride;
4756 
4757     for (j = 0; j < task->width; j += 6) {
4758       a0 = GST_READ_UINT32_LE (s + (j / 6) * 16 + 0);
4759       a1 = GST_READ_UINT32_LE (s + (j / 6) * 16 + 4);
4760       a2 = GST_READ_UINT32_LE (s + (j / 6) * 16 + 8);
4761       a3 = GST_READ_UINT32_LE (s + (j / 6) * 16 + 12);
4762 
4763       u0 = ((a0 >> 0) & 0x3ff) >> 2;
4764       y0 = ((a0 >> 10) & 0x3ff) >> 2;
4765       v0 = ((a0 >> 20) & 0x3ff) >> 2;
4766       y1 = ((a1 >> 0) & 0x3ff) >> 2;
4767 
4768       u2 = ((a1 >> 10) & 0x3ff) >> 2;
4769       y2 = ((a1 >> 20) & 0x3ff) >> 2;
4770       v2 = ((a2 >> 0) & 0x3ff) >> 2;
4771       y3 = ((a2 >> 10) & 0x3ff) >> 2;
4772 
4773       u4 = ((a2 >> 20) & 0x3ff) >> 2;
4774       y4 = ((a3 >> 0) & 0x3ff) >> 2;
4775       v4 = ((a3 >> 10) & 0x3ff) >> 2;
4776       y5 = ((a3 >> 20) & 0x3ff) >> 2;
4777 
4778       d[2 * j + 1] = y0;
4779       d[2 * j] = u0;
4780       d[2 * j + 2] = v0;
4781 
4782       if (j < task->width - 1) {
4783         d[2 * j + 3] = y1;
4784       }
4785 
4786       if (j < task->width - 2) {
4787         d[2 * j + 5] = y2;
4788         d[2 * j + 4] = u2;
4789         d[2 * j + 6] = v2;
4790       }
4791 
4792       if (j < task->width - 3) {
4793         d[2 * j + 7] = y3;
4794       }
4795 
4796       if (j < task->width - 4) {
4797         d[2 * j + 9] = y4;
4798         d[2 * j + 8] = u4;
4799         d[2 * j + 10] = v4;
4800       }
4801 
4802       if (j < task->width - 5) {
4803         d[2 * j + 11] = y5;
4804       }
4805     }
4806   }
4807 }
4808 
4809 static void
convert_v210_UYVY(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)4810 convert_v210_UYVY (GstVideoConverter * convert, const GstVideoFrame * src,
4811     GstVideoFrame * dest)
4812 {
4813   gint width = convert->in_width;
4814   gint height = convert->in_height;
4815   guint8 *s, *d;
4816   FConvertPlaneTask *tasks;
4817   FConvertPlaneTask **tasks_p;
4818   gint n_threads;
4819   gint lines_per_thread;
4820   gint i;
4821 
4822   s = FRAME_GET_LINE (src, convert->in_y);
4823   s += (GST_ROUND_UP_2 (convert->in_x) * 2);
4824   d = FRAME_GET_LINE (dest, convert->out_y);
4825   d += (GST_ROUND_UP_2 (convert->out_x) * 2);
4826 
4827   n_threads = convert->conversion_runner->n_threads;
4828   tasks = convert->tasks[0] =
4829       g_renew (FConvertPlaneTask, convert->tasks[0], n_threads);
4830   tasks_p = convert->tasks_p[0] =
4831       g_renew (FConvertPlaneTask *, convert->tasks_p[0], n_threads);
4832 
4833   lines_per_thread = (height + n_threads - 1) / n_threads;
4834 
4835   for (i = 0; i < n_threads; i++) {
4836     tasks[i].dstride = FRAME_GET_STRIDE (dest);
4837     tasks[i].sstride = FRAME_GET_STRIDE (src);
4838     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
4839     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
4840 
4841     tasks[i].width = width;
4842     tasks[i].height = (i + 1) * lines_per_thread;
4843     tasks[i].height = MIN (tasks[i].height, height);
4844     tasks[i].height -= i * lines_per_thread;
4845 
4846     tasks_p[i] = &tasks[i];
4847   }
4848 
4849   gst_parallelized_task_runner_run (convert->conversion_runner,
4850       (GstParallelizedTaskFunc) convert_v210_UYVY_task, (gpointer) tasks_p);
4851 
4852   convert_fill_border (convert, dest);
4853 }
4854 
4855 static void
convert_v210_YUY2_task(FConvertPlaneTask * task)4856 convert_v210_YUY2_task (FConvertPlaneTask * task)
4857 {
4858   gint i, j;
4859   guint8 *d;
4860   const guint8 *s;
4861   guint32 a0, a1, a2, a3;
4862   guint16 y0, y1, y2, y3, y4, y5;
4863   guint16 u0, u2, u4;
4864   guint16 v0, v2, v4;
4865 
4866   for (i = 0; i < task->height; i++) {
4867     d = task->d + i * task->dstride;
4868     s = task->s + i * task->sstride;
4869 
4870     for (j = 0; j < task->width; j += 6) {
4871       a0 = GST_READ_UINT32_LE (s + (j / 6) * 16 + 0);
4872       a1 = GST_READ_UINT32_LE (s + (j / 6) * 16 + 4);
4873       a2 = GST_READ_UINT32_LE (s + (j / 6) * 16 + 8);
4874       a3 = GST_READ_UINT32_LE (s + (j / 6) * 16 + 12);
4875 
4876       u0 = ((a0 >> 0) & 0x3ff) >> 2;
4877       y0 = ((a0 >> 10) & 0x3ff) >> 2;
4878       v0 = ((a0 >> 20) & 0x3ff) >> 2;
4879       y1 = ((a1 >> 0) & 0x3ff) >> 2;
4880 
4881       u2 = ((a1 >> 10) & 0x3ff) >> 2;
4882       y2 = ((a1 >> 20) & 0x3ff) >> 2;
4883       v2 = ((a2 >> 0) & 0x3ff) >> 2;
4884       y3 = ((a2 >> 10) & 0x3ff) >> 2;
4885 
4886       u4 = ((a2 >> 20) & 0x3ff) >> 2;
4887       y4 = ((a3 >> 0) & 0x3ff) >> 2;
4888       v4 = ((a3 >> 10) & 0x3ff) >> 2;
4889       y5 = ((a3 >> 20) & 0x3ff) >> 2;
4890 
4891       d[2 * j] = y0;
4892       d[2 * j + 1] = u0;
4893       d[2 * j + 3] = v0;
4894 
4895       if (j < task->width - 1) {
4896         d[2 * j + 2] = y1;
4897       }
4898 
4899       if (j < task->width - 2) {
4900         d[2 * j + 4] = y2;
4901         d[2 * j + 5] = u2;
4902         d[2 * j + 7] = v2;
4903       }
4904 
4905       if (j < task->width - 3) {
4906         d[2 * j + 6] = y3;
4907       }
4908 
4909       if (j < task->width - 4) {
4910         d[2 * j + 8] = y4;
4911         d[2 * j + 9] = u4;
4912         d[2 * j + 11] = v4;
4913       }
4914 
4915       if (j < task->width - 5) {
4916         d[2 * j + 10] = y5;
4917       }
4918     }
4919   }
4920 }
4921 
4922 static void
convert_v210_YUY2(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)4923 convert_v210_YUY2 (GstVideoConverter * convert, const GstVideoFrame * src,
4924     GstVideoFrame * dest)
4925 {
4926   gint width = convert->in_width;
4927   gint height = convert->in_height;
4928   guint8 *s, *d;
4929   FConvertPlaneTask *tasks;
4930   FConvertPlaneTask **tasks_p;
4931   gint n_threads;
4932   gint lines_per_thread;
4933   gint i;
4934 
4935   s = FRAME_GET_LINE (src, convert->in_y);
4936   s += (GST_ROUND_UP_2 (convert->in_x) * 2);
4937   d = FRAME_GET_LINE (dest, convert->out_y);
4938   d += (GST_ROUND_UP_2 (convert->out_x) * 2);
4939 
4940   n_threads = convert->conversion_runner->n_threads;
4941   tasks = convert->tasks[0] =
4942       g_renew (FConvertPlaneTask, convert->tasks[0], n_threads);
4943   tasks_p = convert->tasks_p[0] =
4944       g_renew (FConvertPlaneTask *, convert->tasks_p[0], n_threads);
4945 
4946   lines_per_thread = (height + n_threads - 1) / n_threads;
4947 
4948   for (i = 0; i < n_threads; i++) {
4949     tasks[i].dstride = FRAME_GET_STRIDE (dest);
4950     tasks[i].sstride = FRAME_GET_STRIDE (src);
4951     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
4952     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
4953 
4954     tasks[i].width = width;
4955     tasks[i].height = (i + 1) * lines_per_thread;
4956     tasks[i].height = MIN (tasks[i].height, height);
4957     tasks[i].height -= i * lines_per_thread;
4958 
4959     tasks_p[i] = &tasks[i];
4960   }
4961 
4962   gst_parallelized_task_runner_run (convert->conversion_runner,
4963       (GstParallelizedTaskFunc) convert_v210_YUY2_task, (gpointer) tasks_p);
4964 
4965   convert_fill_border (convert, dest);
4966 }
4967 
4968 static void
convert_UYVY_Y42B_task(FConvertPlaneTask * task)4969 convert_UYVY_Y42B_task (FConvertPlaneTask * task)
4970 {
4971   video_orc_convert_UYVY_Y42B (task->d, task->dstride, task->du,
4972       task->dustride, task->dv, task->dvstride,
4973       task->s, task->sstride, (task->width + 1) / 2, task->height);
4974 }
4975 
4976 static void
convert_UYVY_Y42B(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)4977 convert_UYVY_Y42B (GstVideoConverter * convert, const GstVideoFrame * src,
4978     GstVideoFrame * dest)
4979 {
4980   gint width = convert->in_width;
4981   gint height = convert->in_height;
4982   guint8 *s, *dy, *du, *dv;
4983   FConvertPlaneTask *tasks;
4984   FConvertPlaneTask **tasks_p;
4985   gint n_threads;
4986   gint lines_per_thread;
4987   gint i;
4988 
4989   s = FRAME_GET_LINE (src, convert->in_y);
4990   s += (GST_ROUND_UP_2 (convert->in_x) * 2);
4991 
4992   dy = FRAME_GET_Y_LINE (dest, convert->out_y);
4993   dy += convert->out_x;
4994   du = FRAME_GET_U_LINE (dest, convert->out_y);
4995   du += convert->out_x >> 1;
4996   dv = FRAME_GET_V_LINE (dest, convert->out_y);
4997   dv += convert->out_x >> 1;
4998 
4999   n_threads = convert->conversion_runner->n_threads;
5000   tasks = convert->tasks[0] =
5001       g_renew (FConvertPlaneTask, convert->tasks[0], n_threads);
5002   tasks_p = convert->tasks_p[0] =
5003       g_renew (FConvertPlaneTask *, convert->tasks_p[0], n_threads);
5004 
5005   lines_per_thread = (height + n_threads - 1) / n_threads;
5006 
5007   for (i = 0; i < n_threads; i++) {
5008     tasks[i].dstride = FRAME_GET_Y_STRIDE (dest);
5009     tasks[i].dustride = FRAME_GET_U_STRIDE (dest);
5010     tasks[i].dvstride = FRAME_GET_V_STRIDE (dest);
5011     tasks[i].sstride = FRAME_GET_STRIDE (src);
5012     tasks[i].d = dy + i * lines_per_thread * tasks[i].dstride;
5013     tasks[i].du = du + i * lines_per_thread * tasks[i].dustride;
5014     tasks[i].dv = dv + i * lines_per_thread * tasks[i].dvstride;
5015     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
5016 
5017     tasks[i].width = width;
5018     tasks[i].height = (i + 1) * lines_per_thread;
5019     tasks[i].height = MIN (tasks[i].height, height);
5020     tasks[i].height -= i * lines_per_thread;
5021 
5022     tasks_p[i] = &tasks[i];
5023   }
5024 
5025   gst_parallelized_task_runner_run (convert->conversion_runner,
5026       (GstParallelizedTaskFunc) convert_UYVY_Y42B_task, (gpointer) tasks_p);
5027 
5028   convert_fill_border (convert, dest);
5029 }
5030 
5031 static void
convert_UYVY_Y444_task(FConvertPlaneTask * task)5032 convert_UYVY_Y444_task (FConvertPlaneTask * task)
5033 {
5034   video_orc_convert_UYVY_Y444 (task->d,
5035       task->dstride, task->du,
5036       task->dustride, task->dv,
5037       task->dvstride, task->s,
5038       task->sstride, (task->width + 1) / 2, task->height);
5039 }
5040 
5041 static void
convert_UYVY_Y444(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)5042 convert_UYVY_Y444 (GstVideoConverter * convert, const GstVideoFrame * src,
5043     GstVideoFrame * dest)
5044 {
5045   gint width = convert->in_width;
5046   gint height = convert->in_height;
5047   guint8 *s, *dy, *du, *dv;
5048   FConvertPlaneTask *tasks;
5049   FConvertPlaneTask **tasks_p;
5050   gint n_threads;
5051   gint lines_per_thread;
5052   gint i;
5053 
5054   s = FRAME_GET_LINE (src, convert->in_y);
5055   s += (GST_ROUND_UP_2 (convert->in_x) * 2);
5056 
5057   dy = FRAME_GET_Y_LINE (dest, convert->out_y);
5058   dy += convert->out_x;
5059   du = FRAME_GET_U_LINE (dest, convert->out_y);
5060   du += convert->out_x;
5061   dv = FRAME_GET_V_LINE (dest, convert->out_y);
5062   dv += convert->out_x;
5063 
5064   n_threads = convert->conversion_runner->n_threads;
5065   tasks = convert->tasks[0] =
5066       g_renew (FConvertPlaneTask, convert->tasks[0], n_threads);
5067   tasks_p = convert->tasks_p[0] =
5068       g_renew (FConvertPlaneTask *, convert->tasks_p[0], n_threads);
5069 
5070   lines_per_thread = (height + n_threads - 1) / n_threads;
5071 
5072   for (i = 0; i < n_threads; i++) {
5073     tasks[i].dstride = FRAME_GET_Y_STRIDE (dest);
5074     tasks[i].dustride = FRAME_GET_U_STRIDE (dest);
5075     tasks[i].dvstride = FRAME_GET_V_STRIDE (dest);
5076     tasks[i].sstride = FRAME_GET_STRIDE (src);
5077     tasks[i].d = dy + i * lines_per_thread * tasks[i].dstride;
5078     tasks[i].du = du + i * lines_per_thread * tasks[i].dustride;
5079     tasks[i].dv = dv + i * lines_per_thread * tasks[i].dvstride;
5080     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
5081 
5082     tasks[i].width = width;
5083     tasks[i].height = (i + 1) * lines_per_thread;
5084     tasks[i].height = MIN (tasks[i].height, height);
5085     tasks[i].height -= i * lines_per_thread;
5086 
5087     tasks_p[i] = &tasks[i];
5088   }
5089 
5090   gst_parallelized_task_runner_run (convert->conversion_runner,
5091       (GstParallelizedTaskFunc) convert_UYVY_Y444_task, (gpointer) tasks_p);
5092 
5093   convert_fill_border (convert, dest);
5094 }
5095 
5096 static void
convert_UYVY_GRAY8_task(FConvertPlaneTask * task)5097 convert_UYVY_GRAY8_task (FConvertPlaneTask * task)
5098 {
5099   video_orc_convert_UYVY_GRAY8 (task->d, task->dstride, (guint16 *) task->s,
5100       task->sstride, task->width, task->height);
5101 }
5102 
5103 static void
convert_UYVY_GRAY8(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)5104 convert_UYVY_GRAY8 (GstVideoConverter * convert, const GstVideoFrame * src,
5105     GstVideoFrame * dest)
5106 {
5107   gint width = convert->in_width;
5108   gint height = convert->in_height;
5109   guint8 *s;
5110   guint8 *d;
5111   FConvertPlaneTask *tasks;
5112   FConvertPlaneTask **tasks_p;
5113   gint n_threads;
5114   gint lines_per_thread;
5115   gint i;
5116 
5117   s = GST_VIDEO_FRAME_PLANE_DATA (src, 0);
5118   d = GST_VIDEO_FRAME_PLANE_DATA (dest, 0);
5119 
5120   n_threads = convert->conversion_runner->n_threads;
5121   tasks = convert->tasks[0] =
5122       g_renew (FConvertPlaneTask, convert->tasks[0], n_threads);
5123   tasks_p = convert->tasks_p[0] =
5124       g_renew (FConvertPlaneTask *, convert->tasks_p[0], n_threads);
5125 
5126   lines_per_thread = (height + n_threads - 1) / n_threads;
5127 
5128   for (i = 0; i < n_threads; i++) {
5129     tasks[i].dstride = FRAME_GET_STRIDE (dest);
5130     tasks[i].sstride = FRAME_GET_STRIDE (src);
5131     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
5132     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
5133 
5134     tasks[i].width = width;
5135     tasks[i].height = (i + 1) * lines_per_thread;
5136     tasks[i].height = MIN (tasks[i].height, height);
5137     tasks[i].height -= i * lines_per_thread;
5138 
5139     tasks_p[i] = &tasks[i];
5140   }
5141 
5142   gst_parallelized_task_runner_run (convert->conversion_runner,
5143       (GstParallelizedTaskFunc) convert_UYVY_GRAY8_task, (gpointer) tasks_p);
5144 
5145   convert_fill_border (convert, dest);
5146 }
5147 
5148 static void
convert_AYUV_I420_task(FConvertPlaneTask * task)5149 convert_AYUV_I420_task (FConvertPlaneTask * task)
5150 {
5151   video_orc_convert_AYUV_I420 (task->d,
5152       2 * task->dstride, task->d2,
5153       2 * task->dstride, task->du,
5154       task->dustride, task->dv,
5155       task->dvstride, task->s,
5156       2 * task->sstride, task->s2,
5157       2 * task->sstride, task->width / 2, task->height / 2);
5158 }
5159 
5160 static void
convert_AYUV_I420(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)5161 convert_AYUV_I420 (GstVideoConverter * convert, const GstVideoFrame * src,
5162     GstVideoFrame * dest)
5163 {
5164   gint width = convert->in_width;
5165   gint height = convert->in_height;
5166   guint8 *s1, *s2, *dy1, *dy2, *du, *dv;
5167   FConvertPlaneTask *tasks;
5168   FConvertPlaneTask **tasks_p;
5169   gint n_threads;
5170   gint lines_per_thread;
5171   gint i;
5172 
5173   s1 = FRAME_GET_LINE (src, convert->in_y + 0);
5174   s1 += convert->in_x * 4;
5175   s2 = FRAME_GET_LINE (src, convert->in_y + 1);
5176   s2 += convert->in_x * 4;
5177 
5178   dy1 = FRAME_GET_Y_LINE (dest, convert->out_y + 0);
5179   dy1 += convert->out_x;
5180   dy2 = FRAME_GET_Y_LINE (dest, convert->out_y + 1);
5181   dy2 += convert->out_x;
5182   du = FRAME_GET_U_LINE (dest, convert->out_y >> 1);
5183   du += convert->out_x >> 1;
5184   dv = FRAME_GET_V_LINE (dest, convert->out_y >> 1);
5185   dv += convert->out_x >> 1;
5186 
5187   /* only for even width/height */
5188 
5189   n_threads = convert->conversion_runner->n_threads;
5190   tasks = convert->tasks[0] =
5191       g_renew (FConvertPlaneTask, convert->tasks[0], n_threads);
5192   tasks_p = convert->tasks_p[0] =
5193       g_renew (FConvertPlaneTask *, convert->tasks_p[0], n_threads);
5194 
5195   lines_per_thread = GST_ROUND_UP_2 ((height + n_threads - 1) / n_threads);
5196 
5197   for (i = 0; i < n_threads; i++) {
5198     tasks[i].dstride = FRAME_GET_Y_STRIDE (dest);
5199     tasks[i].dustride = FRAME_GET_U_STRIDE (dest);
5200     tasks[i].dvstride = FRAME_GET_V_STRIDE (dest);
5201     tasks[i].sstride = FRAME_GET_STRIDE (src);
5202     tasks[i].d = dy1 + i * lines_per_thread * tasks[i].dstride;
5203     tasks[i].d2 = dy2 + i * lines_per_thread * tasks[i].dstride;
5204     tasks[i].du = du + i * lines_per_thread * tasks[i].dustride / 2;
5205     tasks[i].dv = dv + i * lines_per_thread * tasks[i].dvstride / 2;
5206     tasks[i].s = s1 + i * lines_per_thread * tasks[i].sstride;
5207     tasks[i].s2 = s2 + i * lines_per_thread * tasks[i].sstride;
5208 
5209     tasks[i].width = width;
5210     tasks[i].height = (i + 1) * lines_per_thread;
5211     tasks[i].height = MIN (tasks[i].height, height);
5212     tasks[i].height -= i * lines_per_thread;
5213 
5214     tasks_p[i] = &tasks[i];
5215   }
5216 
5217   gst_parallelized_task_runner_run (convert->conversion_runner,
5218       (GstParallelizedTaskFunc) convert_AYUV_I420_task, (gpointer) tasks_p);
5219 
5220   convert_fill_border (convert, dest);
5221 }
5222 
5223 static void
convert_AYUV_YUY2_task(FConvertPlaneTask * task)5224 convert_AYUV_YUY2_task (FConvertPlaneTask * task)
5225 {
5226   video_orc_convert_AYUV_YUY2 (task->d, task->dstride, task->s,
5227       task->sstride, task->width / 2, task->height);
5228 }
5229 
5230 static void
convert_AYUV_YUY2(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)5231 convert_AYUV_YUY2 (GstVideoConverter * convert, const GstVideoFrame * src,
5232     GstVideoFrame * dest)
5233 {
5234   gint width = convert->in_width;
5235   gint height = convert->in_height;
5236   guint8 *s, *d;
5237   FConvertPlaneTask *tasks;
5238   FConvertPlaneTask **tasks_p;
5239   gint n_threads;
5240   gint lines_per_thread;
5241   gint i;
5242 
5243   s = FRAME_GET_LINE (src, convert->in_y);
5244   s += convert->in_x * 4;
5245   d = FRAME_GET_LINE (dest, convert->out_y);
5246   d += (GST_ROUND_UP_2 (convert->out_x) * 2);
5247 
5248   /* only for even width */
5249   n_threads = convert->conversion_runner->n_threads;
5250   tasks = convert->tasks[0] =
5251       g_renew (FConvertPlaneTask, convert->tasks[0], n_threads);
5252   tasks_p = convert->tasks_p[0] =
5253       g_renew (FConvertPlaneTask *, convert->tasks_p[0], n_threads);
5254 
5255   lines_per_thread = (height + n_threads - 1) / n_threads;
5256 
5257   for (i = 0; i < n_threads; i++) {
5258     tasks[i].dstride = FRAME_GET_STRIDE (dest);
5259     tasks[i].sstride = FRAME_GET_STRIDE (src);
5260     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
5261     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
5262 
5263     tasks[i].width = width;
5264     tasks[i].height = (i + 1) * lines_per_thread;
5265     tasks[i].height = MIN (tasks[i].height, height);
5266     tasks[i].height -= i * lines_per_thread;
5267 
5268     tasks_p[i] = &tasks[i];
5269   }
5270 
5271   gst_parallelized_task_runner_run (convert->conversion_runner,
5272       (GstParallelizedTaskFunc) convert_AYUV_YUY2_task, (gpointer) tasks_p);
5273 
5274   convert_fill_border (convert, dest);
5275 }
5276 
5277 static void
convert_AYUV_UYVY_task(FConvertPlaneTask * task)5278 convert_AYUV_UYVY_task (FConvertPlaneTask * task)
5279 {
5280   video_orc_convert_AYUV_UYVY (task->d, task->dstride, task->s,
5281       task->sstride, task->width / 2, task->height);
5282 }
5283 
5284 static void
convert_AYUV_UYVY(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)5285 convert_AYUV_UYVY (GstVideoConverter * convert, const GstVideoFrame * src,
5286     GstVideoFrame * dest)
5287 {
5288   gint width = convert->in_width;
5289   gint height = convert->in_height;
5290   guint8 *s, *d;
5291   FConvertPlaneTask *tasks;
5292   FConvertPlaneTask **tasks_p;
5293   gint n_threads;
5294   gint lines_per_thread;
5295   gint i;
5296 
5297   s = FRAME_GET_LINE (src, convert->in_y);
5298   s += convert->in_x * 4;
5299   d = FRAME_GET_LINE (dest, convert->out_y);
5300   d += (GST_ROUND_UP_2 (convert->out_x) * 2);
5301 
5302   /* only for even width */
5303   n_threads = convert->conversion_runner->n_threads;
5304   tasks = convert->tasks[0] =
5305       g_renew (FConvertPlaneTask, convert->tasks[0], n_threads);
5306   tasks_p = convert->tasks_p[0] =
5307       g_renew (FConvertPlaneTask *, convert->tasks_p[0], n_threads);
5308 
5309   lines_per_thread = (height + n_threads - 1) / n_threads;
5310 
5311   for (i = 0; i < n_threads; i++) {
5312     tasks[i].dstride = FRAME_GET_STRIDE (dest);
5313     tasks[i].sstride = FRAME_GET_STRIDE (src);
5314     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
5315     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
5316 
5317     tasks[i].width = width;
5318     tasks[i].height = (i + 1) * lines_per_thread;
5319     tasks[i].height = MIN (tasks[i].height, height);
5320     tasks[i].height -= i * lines_per_thread;
5321 
5322     tasks_p[i] = &tasks[i];
5323   }
5324 
5325   gst_parallelized_task_runner_run (convert->conversion_runner,
5326       (GstParallelizedTaskFunc) convert_AYUV_UYVY_task, (gpointer) tasks_p);
5327 
5328   convert_fill_border (convert, dest);
5329 }
5330 
5331 static void
convert_AYUV_Y42B_task(FConvertPlaneTask * task)5332 convert_AYUV_Y42B_task (FConvertPlaneTask * task)
5333 {
5334   video_orc_convert_AYUV_Y42B (task->d, task->dstride, task->du,
5335       task->dustride, task->dv, task->dvstride,
5336       task->s, task->sstride, task->width / 2, task->height);
5337 }
5338 
5339 static void
convert_AYUV_Y42B(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)5340 convert_AYUV_Y42B (GstVideoConverter * convert, const GstVideoFrame * src,
5341     GstVideoFrame * dest)
5342 {
5343   gint width = convert->in_width;
5344   gint height = convert->in_height;
5345   guint8 *s, *dy, *du, *dv;
5346   FConvertPlaneTask *tasks;
5347   FConvertPlaneTask **tasks_p;
5348   gint n_threads;
5349   gint lines_per_thread;
5350   gint i;
5351 
5352   s = FRAME_GET_LINE (src, convert->in_y);
5353   s += convert->in_x * 4;
5354 
5355   dy = FRAME_GET_Y_LINE (dest, convert->out_y);
5356   dy += convert->out_x;
5357   du = FRAME_GET_U_LINE (dest, convert->out_y);
5358   du += convert->out_x >> 1;
5359   dv = FRAME_GET_V_LINE (dest, convert->out_y);
5360   dv += convert->out_x >> 1;
5361 
5362   /* only works for even width */
5363   n_threads = convert->conversion_runner->n_threads;
5364   tasks = convert->tasks[0] =
5365       g_renew (FConvertPlaneTask, convert->tasks[0], n_threads);
5366   tasks_p = convert->tasks_p[0] =
5367       g_renew (FConvertPlaneTask *, convert->tasks_p[0], n_threads);
5368 
5369   lines_per_thread = (height + n_threads - 1) / n_threads;
5370 
5371   for (i = 0; i < n_threads; i++) {
5372     tasks[i].dstride = FRAME_GET_Y_STRIDE (dest);
5373     tasks[i].dustride = FRAME_GET_U_STRIDE (dest);
5374     tasks[i].dvstride = FRAME_GET_V_STRIDE (dest);
5375     tasks[i].sstride = FRAME_GET_STRIDE (src);
5376     tasks[i].d = dy + i * lines_per_thread * tasks[i].dstride;
5377     tasks[i].du = du + i * lines_per_thread * tasks[i].dustride;
5378     tasks[i].dv = dv + i * lines_per_thread * tasks[i].dvstride;
5379     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
5380 
5381     tasks[i].width = width;
5382     tasks[i].height = (i + 1) * lines_per_thread;
5383     tasks[i].height = MIN (tasks[i].height, height);
5384     tasks[i].height -= i * lines_per_thread;
5385 
5386     tasks_p[i] = &tasks[i];
5387   }
5388 
5389   gst_parallelized_task_runner_run (convert->conversion_runner,
5390       (GstParallelizedTaskFunc) convert_AYUV_Y42B_task, (gpointer) tasks_p);
5391 
5392   convert_fill_border (convert, dest);
5393 }
5394 
5395 static void
convert_AYUV_Y444_task(FConvertPlaneTask * task)5396 convert_AYUV_Y444_task (FConvertPlaneTask * task)
5397 {
5398   video_orc_convert_AYUV_Y444 (task->d, task->dstride, task->du,
5399       task->dustride, task->dv, task->dvstride,
5400       task->s, task->sstride, task->width, task->height);
5401 }
5402 
5403 static void
convert_AYUV_Y444(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)5404 convert_AYUV_Y444 (GstVideoConverter * convert, const GstVideoFrame * src,
5405     GstVideoFrame * dest)
5406 {
5407   gint width = convert->in_width;
5408   gint height = convert->in_height;
5409   guint8 *s, *dy, *du, *dv;
5410   FConvertPlaneTask *tasks;
5411   FConvertPlaneTask **tasks_p;
5412   gint n_threads;
5413   gint lines_per_thread;
5414   gint i;
5415 
5416   s = FRAME_GET_LINE (src, convert->in_y);
5417   s += convert->in_x * 4;
5418 
5419   dy = FRAME_GET_Y_LINE (dest, convert->out_y);
5420   dy += convert->out_x;
5421   du = FRAME_GET_U_LINE (dest, convert->out_y);
5422   du += convert->out_x;
5423   dv = FRAME_GET_V_LINE (dest, convert->out_y);
5424   dv += convert->out_x;
5425 
5426   n_threads = convert->conversion_runner->n_threads;
5427   tasks = convert->tasks[0] =
5428       g_renew (FConvertPlaneTask, convert->tasks[0], n_threads);
5429   tasks_p = convert->tasks_p[0] =
5430       g_renew (FConvertPlaneTask *, convert->tasks_p[0], n_threads);
5431 
5432   lines_per_thread = (height + n_threads - 1) / n_threads;
5433 
5434   for (i = 0; i < n_threads; i++) {
5435     tasks[i].dstride = FRAME_GET_Y_STRIDE (dest);
5436     tasks[i].dustride = FRAME_GET_U_STRIDE (dest);
5437     tasks[i].dvstride = FRAME_GET_V_STRIDE (dest);
5438     tasks[i].sstride = FRAME_GET_STRIDE (src);
5439     tasks[i].d = dy + i * lines_per_thread * tasks[i].dstride;
5440     tasks[i].du = du + i * lines_per_thread * tasks[i].dustride;
5441     tasks[i].dv = dv + i * lines_per_thread * tasks[i].dvstride;
5442     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
5443 
5444     tasks[i].width = width;
5445     tasks[i].height = (i + 1) * lines_per_thread;
5446     tasks[i].height = MIN (tasks[i].height, height);
5447     tasks[i].height -= i * lines_per_thread;
5448 
5449     tasks_p[i] = &tasks[i];
5450   }
5451 
5452   gst_parallelized_task_runner_run (convert->conversion_runner,
5453       (GstParallelizedTaskFunc) convert_AYUV_Y444_task, (gpointer) tasks_p);
5454   convert_fill_border (convert, dest);
5455 }
5456 
5457 static void
convert_Y42B_YUY2_task(FConvertPlaneTask * task)5458 convert_Y42B_YUY2_task (FConvertPlaneTask * task)
5459 {
5460   video_orc_convert_Y42B_YUY2 (task->d, task->dstride,
5461       task->s, task->sstride,
5462       task->su, task->sustride,
5463       task->sv, task->svstride, (task->width + 1) / 2, task->height);
5464 }
5465 
5466 static void
convert_Y42B_YUY2(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)5467 convert_Y42B_YUY2 (GstVideoConverter * convert, const GstVideoFrame * src,
5468     GstVideoFrame * dest)
5469 {
5470   gint width = convert->in_width;
5471   gint height = convert->in_height;
5472   guint8 *sy, *su, *sv, *d;
5473   FConvertPlaneTask *tasks;
5474   FConvertPlaneTask **tasks_p;
5475   gint n_threads;
5476   gint lines_per_thread;
5477   gint i;
5478 
5479   sy = FRAME_GET_Y_LINE (src, convert->in_y);
5480   sy += convert->in_x;
5481   su = FRAME_GET_U_LINE (src, convert->in_y);
5482   su += convert->in_x >> 1;
5483   sv = FRAME_GET_V_LINE (src, convert->in_y);
5484   sv += convert->in_x >> 1;
5485 
5486   d = FRAME_GET_LINE (dest, convert->out_y);
5487   d += (GST_ROUND_UP_2 (convert->out_x) * 2);
5488 
5489   n_threads = convert->conversion_runner->n_threads;
5490   tasks = convert->tasks[0] =
5491       g_renew (FConvertPlaneTask, convert->tasks[0], n_threads);
5492   tasks_p = convert->tasks_p[0] =
5493       g_renew (FConvertPlaneTask *, convert->tasks_p[0], n_threads);
5494 
5495   lines_per_thread = (height + n_threads - 1) / n_threads;
5496 
5497   for (i = 0; i < n_threads; i++) {
5498     tasks[i].dstride = FRAME_GET_STRIDE (dest);
5499     tasks[i].sstride = FRAME_GET_Y_STRIDE (src);
5500     tasks[i].sustride = FRAME_GET_U_STRIDE (src);
5501     tasks[i].svstride = FRAME_GET_V_STRIDE (src);
5502     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
5503     tasks[i].s = sy + i * lines_per_thread * tasks[i].sstride;
5504     tasks[i].su = su + i * lines_per_thread * tasks[i].sustride;
5505     tasks[i].sv = sv + i * lines_per_thread * tasks[i].svstride;
5506 
5507     tasks[i].width = width;
5508     tasks[i].height = (i + 1) * lines_per_thread;
5509     tasks[i].height = MIN (tasks[i].height, height);
5510     tasks[i].height -= i * lines_per_thread;
5511 
5512     tasks_p[i] = &tasks[i];
5513   }
5514 
5515   gst_parallelized_task_runner_run (convert->conversion_runner,
5516       (GstParallelizedTaskFunc) convert_Y42B_YUY2_task, (gpointer) tasks_p);
5517 
5518   convert_fill_border (convert, dest);
5519 }
5520 
5521 static void
convert_Y42B_UYVY_task(FConvertPlaneTask * task)5522 convert_Y42B_UYVY_task (FConvertPlaneTask * task)
5523 {
5524   video_orc_convert_Y42B_UYVY (task->d, task->dstride,
5525       task->s, task->sstride,
5526       task->su, task->sustride,
5527       task->sv, task->svstride, (task->width + 1) / 2, task->height);
5528 }
5529 
5530 static void
convert_Y42B_UYVY(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)5531 convert_Y42B_UYVY (GstVideoConverter * convert, const GstVideoFrame * src,
5532     GstVideoFrame * dest)
5533 {
5534   gint width = convert->in_width;
5535   gint height = convert->in_height;
5536   guint8 *sy, *su, *sv, *d;
5537   FConvertPlaneTask *tasks;
5538   FConvertPlaneTask **tasks_p;
5539   gint n_threads;
5540   gint lines_per_thread;
5541   gint i;
5542 
5543   sy = FRAME_GET_Y_LINE (src, convert->in_y);
5544   sy += convert->in_x;
5545   su = FRAME_GET_U_LINE (src, convert->in_y);
5546   su += convert->in_x >> 1;
5547   sv = FRAME_GET_V_LINE (src, convert->in_y);
5548   sv += convert->in_x >> 1;
5549 
5550   d = FRAME_GET_LINE (dest, convert->out_y);
5551   d += (GST_ROUND_UP_2 (convert->out_x) * 2);
5552 
5553   n_threads = convert->conversion_runner->n_threads;
5554   tasks = convert->tasks[0] =
5555       g_renew (FConvertPlaneTask, convert->tasks[0], n_threads);
5556   tasks_p = convert->tasks_p[0] =
5557       g_renew (FConvertPlaneTask *, convert->tasks_p[0], n_threads);
5558 
5559   lines_per_thread = (height + n_threads - 1) / n_threads;
5560 
5561   for (i = 0; i < n_threads; i++) {
5562     tasks[i].dstride = FRAME_GET_STRIDE (dest);
5563     tasks[i].sstride = FRAME_GET_Y_STRIDE (src);
5564     tasks[i].sustride = FRAME_GET_U_STRIDE (src);
5565     tasks[i].svstride = FRAME_GET_V_STRIDE (src);
5566     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
5567     tasks[i].s = sy + i * lines_per_thread * tasks[i].sstride;
5568     tasks[i].su = su + i * lines_per_thread * tasks[i].sustride;
5569     tasks[i].sv = sv + i * lines_per_thread * tasks[i].svstride;
5570 
5571     tasks[i].width = width;
5572     tasks[i].height = (i + 1) * lines_per_thread;
5573     tasks[i].height = MIN (tasks[i].height, height);
5574     tasks[i].height -= i * lines_per_thread;
5575 
5576     tasks_p[i] = &tasks[i];
5577   }
5578 
5579   gst_parallelized_task_runner_run (convert->conversion_runner,
5580       (GstParallelizedTaskFunc) convert_Y42B_UYVY_task, (gpointer) tasks_p);
5581 
5582   convert_fill_border (convert, dest);
5583 }
5584 
5585 static void
convert_Y42B_AYUV_task(FConvertPlaneTask * task)5586 convert_Y42B_AYUV_task (FConvertPlaneTask * task)
5587 {
5588   video_orc_convert_Y42B_AYUV (task->d, task->dstride, task->s,
5589       task->sstride,
5590       task->su,
5591       task->sustride,
5592       task->sv, task->svstride, task->alpha, task->width / 2, task->height);
5593 }
5594 
5595 static void
convert_Y42B_AYUV(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)5596 convert_Y42B_AYUV (GstVideoConverter * convert, const GstVideoFrame * src,
5597     GstVideoFrame * dest)
5598 {
5599   gint width = convert->in_width;
5600   gint height = convert->in_height;
5601   guint8 *sy, *su, *sv, *d;
5602   guint8 alpha = MIN (convert->alpha_value, 255);
5603   FConvertPlaneTask *tasks;
5604   FConvertPlaneTask **tasks_p;
5605   gint n_threads;
5606   gint lines_per_thread;
5607   gint i;
5608 
5609   sy = FRAME_GET_Y_LINE (src, convert->in_y);
5610   sy += convert->in_x;
5611   su = FRAME_GET_U_LINE (src, convert->in_y);
5612   su += convert->in_x >> 1;
5613   sv = FRAME_GET_V_LINE (src, convert->in_y);
5614   sv += convert->in_x >> 1;
5615 
5616   d = FRAME_GET_LINE (dest, convert->out_y);
5617   d += convert->out_x * 4;
5618 
5619   /* only for even width */
5620   n_threads = convert->conversion_runner->n_threads;
5621   tasks = convert->tasks[0] =
5622       g_renew (FConvertPlaneTask, convert->tasks[0], n_threads);
5623   tasks_p = convert->tasks_p[0] =
5624       g_renew (FConvertPlaneTask *, convert->tasks_p[0], n_threads);
5625 
5626   lines_per_thread = (height + n_threads - 1) / n_threads;
5627 
5628   for (i = 0; i < n_threads; i++) {
5629     tasks[i].dstride = FRAME_GET_STRIDE (dest);
5630     tasks[i].sstride = FRAME_GET_Y_STRIDE (src);
5631     tasks[i].sustride = FRAME_GET_U_STRIDE (src);
5632     tasks[i].svstride = FRAME_GET_V_STRIDE (src);
5633     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
5634     tasks[i].s = sy + i * lines_per_thread * tasks[i].sstride;
5635     tasks[i].su = su + i * lines_per_thread * tasks[i].sustride;
5636     tasks[i].sv = sv + i * lines_per_thread * tasks[i].svstride;
5637 
5638     tasks[i].width = width;
5639     tasks[i].height = (i + 1) * lines_per_thread;
5640     tasks[i].height = MIN (tasks[i].height, height);
5641     tasks[i].height -= i * lines_per_thread;
5642     tasks[i].alpha = alpha;
5643 
5644     tasks_p[i] = &tasks[i];
5645   }
5646 
5647   gst_parallelized_task_runner_run (convert->conversion_runner,
5648       (GstParallelizedTaskFunc) convert_Y42B_AYUV_task, (gpointer) tasks_p);
5649 
5650   convert_fill_border (convert, dest);
5651 }
5652 
5653 static void
convert_Y42B_v210_task(FConvertPlaneTask * task)5654 convert_Y42B_v210_task (FConvertPlaneTask * task)
5655 {
5656   gint i, j;
5657   guint8 *d;
5658   const guint8 *s_y, *s_u, *s_v;
5659   guint32 a0, a1, a2, a3;
5660   guint8 y0, y1, y2, y3, y4, y5;
5661   guint8 u0, u2, u4;
5662   guint8 v0, v2, v4;
5663 
5664   for (i = 0; i < task->height; i++) {
5665     d = task->d + i * task->dstride;
5666     s_y = task->s + i * task->sstride;
5667     s_u = task->su + i * task->sustride;
5668     s_v = task->sv + i * task->svstride;
5669 
5670     for (j = 0; j < task->width; j += 6) {
5671       y1 = y2 = y3 = y4 = y5 = 0;
5672       u2 = u4 = v2 = v4 = 0;
5673 
5674       y0 = s_y[j];
5675       u0 = s_u[j / 2];
5676       v0 = s_v[j / 2];
5677 
5678       if (j < task->width - 1) {
5679         y1 = s_y[j + 1];
5680       }
5681 
5682       if (j < task->width - 2) {
5683         y2 = s_y[j + 2];
5684         u2 = s_u[j / 2 + 1];
5685         v2 = s_v[j / 2 + 1];
5686       }
5687 
5688       if (j < task->width - 3) {
5689         y3 = s_y[j + 3];
5690       }
5691 
5692       if (j < task->width - 4) {
5693         y4 = s_y[j + 4];
5694         u4 = s_u[j / 2 + 2];
5695         v4 = s_v[j / 2 + 2];
5696       }
5697 
5698       if (j < task->width - 5) {
5699         y5 = s_y[j + 5];
5700       }
5701 
5702       a0 = u0 << 2 | (y0 << 12) | (v0 << 22);
5703       a1 = y1 << 2 | (u2 << 12) | (y2 << 22);
5704       a2 = v2 << 2 | (y3 << 12) | (u4 << 22);
5705       a3 = y4 << 2 | (v4 << 12) | (y5 << 22);
5706 
5707       GST_WRITE_UINT32_LE (d + (j / 6) * 16 + 0, a0);
5708       GST_WRITE_UINT32_LE (d + (j / 6) * 16 + 4, a1);
5709       GST_WRITE_UINT32_LE (d + (j / 6) * 16 + 8, a2);
5710       GST_WRITE_UINT32_LE (d + (j / 6) * 16 + 12, a3);
5711     }
5712   }
5713 }
5714 
5715 static void
convert_Y42B_v210(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)5716 convert_Y42B_v210 (GstVideoConverter * convert, const GstVideoFrame * src,
5717     GstVideoFrame * dest)
5718 {
5719   gint width = convert->in_width;
5720   gint height = convert->in_height;
5721   guint8 *d, *sy, *su, *sv;
5722   FConvertPlaneTask *tasks;
5723   FConvertPlaneTask **tasks_p;
5724   gint n_threads;
5725   gint lines_per_thread;
5726   gint i;
5727 
5728   d = FRAME_GET_LINE (dest, convert->out_y);
5729   d += (GST_ROUND_UP_2 (convert->out_x) * 2);
5730 
5731   sy = FRAME_GET_Y_LINE (src, convert->in_y);
5732   sy += convert->in_x;
5733   su = FRAME_GET_U_LINE (src, convert->in_y);
5734   su += convert->in_x >> 1;
5735   sv = FRAME_GET_V_LINE (src, convert->in_y);
5736   sv += convert->in_x >> 1;
5737 
5738   n_threads = convert->conversion_runner->n_threads;
5739   tasks = convert->tasks[0] =
5740       g_renew (FConvertPlaneTask, convert->tasks[0], n_threads);
5741   tasks_p = convert->tasks_p[0] =
5742       g_renew (FConvertPlaneTask *, convert->tasks_p[0], n_threads);
5743 
5744   lines_per_thread = (height + n_threads - 1) / n_threads;
5745 
5746   for (i = 0; i < n_threads; i++) {
5747     tasks[i].dstride = FRAME_GET_STRIDE (dest);
5748     tasks[i].sstride = FRAME_GET_Y_STRIDE (src);
5749     tasks[i].sustride = FRAME_GET_U_STRIDE (src);
5750     tasks[i].svstride = FRAME_GET_V_STRIDE (src);
5751     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
5752     tasks[i].s = sy + i * lines_per_thread * tasks[i].sstride;
5753     tasks[i].su = su + i * lines_per_thread * tasks[i].sustride;
5754     tasks[i].sv = sv + i * lines_per_thread * tasks[i].svstride;
5755 
5756     tasks[i].width = width;
5757     tasks[i].height = (i + 1) * lines_per_thread;
5758     tasks[i].height = MIN (tasks[i].height, height);
5759     tasks[i].height -= i * lines_per_thread;
5760 
5761     tasks_p[i] = &tasks[i];
5762   }
5763 
5764   gst_parallelized_task_runner_run (convert->conversion_runner,
5765       (GstParallelizedTaskFunc) convert_Y42B_v210_task, (gpointer) tasks_p);
5766 
5767   convert_fill_border (convert, dest);
5768 }
5769 
5770 static void
convert_Y444_YUY2_task(FConvertPlaneTask * task)5771 convert_Y444_YUY2_task (FConvertPlaneTask * task)
5772 {
5773   video_orc_convert_Y444_YUY2 (task->d, task->dstride, task->s,
5774       task->sstride,
5775       task->su,
5776       task->sustride, task->sv, task->svstride, task->width / 2, task->height);
5777 }
5778 
5779 static void
convert_Y444_YUY2(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)5780 convert_Y444_YUY2 (GstVideoConverter * convert, const GstVideoFrame * src,
5781     GstVideoFrame * dest)
5782 {
5783   gint width = convert->in_width;
5784   gint height = convert->in_height;
5785   guint8 *sy, *su, *sv, *d;
5786   FConvertPlaneTask *tasks;
5787   FConvertPlaneTask **tasks_p;
5788   gint n_threads;
5789   gint lines_per_thread;
5790   gint i;
5791 
5792   sy = FRAME_GET_Y_LINE (src, convert->in_y);
5793   sy += convert->in_x;
5794   su = FRAME_GET_U_LINE (src, convert->in_y);
5795   su += convert->in_x;
5796   sv = FRAME_GET_V_LINE (src, convert->in_y);
5797   sv += convert->in_x;
5798 
5799   d = FRAME_GET_LINE (dest, convert->out_y);
5800   d += (GST_ROUND_UP_2 (convert->out_x) * 2);
5801 
5802   n_threads = convert->conversion_runner->n_threads;
5803   tasks = convert->tasks[0] =
5804       g_renew (FConvertPlaneTask, convert->tasks[0], n_threads);
5805   tasks_p = convert->tasks_p[0] =
5806       g_renew (FConvertPlaneTask *, convert->tasks_p[0], n_threads);
5807 
5808   lines_per_thread = (height + n_threads - 1) / n_threads;
5809 
5810   for (i = 0; i < n_threads; i++) {
5811     tasks[i].dstride = FRAME_GET_STRIDE (dest);
5812     tasks[i].sstride = FRAME_GET_Y_STRIDE (src);
5813     tasks[i].sustride = FRAME_GET_U_STRIDE (src);
5814     tasks[i].svstride = FRAME_GET_V_STRIDE (src);
5815     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
5816     tasks[i].s = sy + i * lines_per_thread * tasks[i].sstride;
5817     tasks[i].su = su + i * lines_per_thread * tasks[i].sustride;
5818     tasks[i].sv = sv + i * lines_per_thread * tasks[i].svstride;
5819 
5820     tasks[i].width = width;
5821     tasks[i].height = (i + 1) * lines_per_thread;
5822     tasks[i].height = MIN (tasks[i].height, height);
5823     tasks[i].height -= i * lines_per_thread;
5824 
5825     tasks_p[i] = &tasks[i];
5826   }
5827 
5828   gst_parallelized_task_runner_run (convert->conversion_runner,
5829       (GstParallelizedTaskFunc) convert_Y444_YUY2_task, (gpointer) tasks_p);
5830 
5831   convert_fill_border (convert, dest);
5832 }
5833 
5834 static void
convert_Y444_UYVY_task(FConvertPlaneTask * task)5835 convert_Y444_UYVY_task (FConvertPlaneTask * task)
5836 {
5837   video_orc_convert_Y444_UYVY (task->d, task->dstride, task->s,
5838       task->sstride,
5839       task->su,
5840       task->sustride, task->sv, task->svstride, task->width / 2, task->height);
5841 }
5842 
5843 static void
convert_Y444_UYVY(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)5844 convert_Y444_UYVY (GstVideoConverter * convert, const GstVideoFrame * src,
5845     GstVideoFrame * dest)
5846 {
5847   gint width = convert->in_width;
5848   gint height = convert->in_height;
5849   guint8 *sy, *su, *sv, *d;
5850   FConvertPlaneTask *tasks;
5851   FConvertPlaneTask **tasks_p;
5852   gint n_threads;
5853   gint lines_per_thread;
5854   gint i;
5855 
5856   sy = FRAME_GET_Y_LINE (src, convert->in_y);
5857   sy += convert->in_x;
5858   su = FRAME_GET_U_LINE (src, convert->in_y);
5859   su += convert->in_x;
5860   sv = FRAME_GET_V_LINE (src, convert->in_y);
5861   sv += convert->in_x;
5862 
5863   d = FRAME_GET_LINE (dest, convert->out_y);
5864   d += (GST_ROUND_UP_2 (convert->out_x) * 2);
5865 
5866   n_threads = convert->conversion_runner->n_threads;
5867   tasks = convert->tasks[0] =
5868       g_renew (FConvertPlaneTask, convert->tasks[0], n_threads);
5869   tasks_p = convert->tasks_p[0] =
5870       g_renew (FConvertPlaneTask *, convert->tasks_p[0], n_threads);
5871 
5872   lines_per_thread = (height + n_threads - 1) / n_threads;
5873 
5874   for (i = 0; i < n_threads; i++) {
5875     tasks[i].dstride = FRAME_GET_STRIDE (dest);
5876     tasks[i].sstride = FRAME_GET_Y_STRIDE (src);
5877     tasks[i].sustride = FRAME_GET_U_STRIDE (src);
5878     tasks[i].svstride = FRAME_GET_V_STRIDE (src);
5879     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
5880     tasks[i].s = sy + i * lines_per_thread * tasks[i].sstride;
5881     tasks[i].su = su + i * lines_per_thread * tasks[i].sustride;
5882     tasks[i].sv = sv + i * lines_per_thread * tasks[i].svstride;
5883 
5884     tasks[i].width = width;
5885     tasks[i].height = (i + 1) * lines_per_thread;
5886     tasks[i].height = MIN (tasks[i].height, height);
5887     tasks[i].height -= i * lines_per_thread;
5888 
5889     tasks_p[i] = &tasks[i];
5890   }
5891 
5892   gst_parallelized_task_runner_run (convert->conversion_runner,
5893       (GstParallelizedTaskFunc) convert_Y444_UYVY_task, (gpointer) tasks_p);
5894 
5895   convert_fill_border (convert, dest);
5896 }
5897 
5898 static void
convert_Y444_AYUV_task(FConvertPlaneTask * task)5899 convert_Y444_AYUV_task (FConvertPlaneTask * task)
5900 {
5901   video_orc_convert_Y444_AYUV (task->d, task->dstride, task->s,
5902       task->sstride,
5903       task->su,
5904       task->sustride,
5905       task->sv, task->svstride, task->alpha, task->width, task->height);
5906 }
5907 
5908 static void
convert_Y444_AYUV(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)5909 convert_Y444_AYUV (GstVideoConverter * convert, const GstVideoFrame * src,
5910     GstVideoFrame * dest)
5911 {
5912   gint width = convert->in_width;
5913   gint height = convert->in_height;
5914   guint8 *sy, *su, *sv, *d;
5915   guint8 alpha = MIN (convert->alpha_value, 255);
5916   FConvertPlaneTask *tasks;
5917   FConvertPlaneTask **tasks_p;
5918   gint n_threads;
5919   gint lines_per_thread;
5920   gint i;
5921 
5922   sy = FRAME_GET_Y_LINE (src, convert->in_y);
5923   sy += convert->in_x;
5924   su = FRAME_GET_U_LINE (src, convert->in_y);
5925   su += convert->in_x;
5926   sv = FRAME_GET_V_LINE (src, convert->in_y);
5927   sv += convert->in_x;
5928 
5929   d = FRAME_GET_LINE (dest, convert->out_y);
5930   d += convert->out_x * 4;
5931 
5932   n_threads = convert->conversion_runner->n_threads;
5933   tasks = convert->tasks[0] =
5934       g_renew (FConvertPlaneTask, convert->tasks[0], n_threads);
5935   tasks_p = convert->tasks_p[0] =
5936       g_renew (FConvertPlaneTask *, convert->tasks_p[0], n_threads);
5937 
5938   lines_per_thread = (height + n_threads - 1) / n_threads;
5939 
5940   for (i = 0; i < n_threads; i++) {
5941     tasks[i].dstride = FRAME_GET_STRIDE (dest);
5942     tasks[i].sstride = FRAME_GET_Y_STRIDE (src);
5943     tasks[i].sustride = FRAME_GET_U_STRIDE (src);
5944     tasks[i].svstride = FRAME_GET_V_STRIDE (src);
5945     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
5946     tasks[i].s = sy + i * lines_per_thread * tasks[i].sstride;
5947     tasks[i].su = su + i * lines_per_thread * tasks[i].sustride;
5948     tasks[i].sv = sv + i * lines_per_thread * tasks[i].svstride;
5949 
5950     tasks[i].width = width;
5951     tasks[i].height = (i + 1) * lines_per_thread;
5952     tasks[i].height = MIN (tasks[i].height, height);
5953     tasks[i].height -= i * lines_per_thread;
5954     tasks[i].alpha = alpha;
5955 
5956     tasks_p[i] = &tasks[i];
5957   }
5958 
5959   gst_parallelized_task_runner_run (convert->conversion_runner,
5960       (GstParallelizedTaskFunc) convert_Y444_AYUV_task, (gpointer) tasks_p);
5961 
5962   convert_fill_border (convert, dest);
5963 }
5964 
5965 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
5966 static void
convert_AYUV_ARGB_task(FConvertPlaneTask * task)5967 convert_AYUV_ARGB_task (FConvertPlaneTask * task)
5968 {
5969   video_orc_convert_AYUV_ARGB (task->d, task->dstride, task->s,
5970       task->sstride, task->data->im[0][0], task->data->im[0][2],
5971       task->data->im[2][1], task->data->im[1][1], task->data->im[1][2],
5972       task->width, task->height);
5973 }
5974 
5975 static void
convert_AYUV_ARGB(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)5976 convert_AYUV_ARGB (GstVideoConverter * convert, const GstVideoFrame * src,
5977     GstVideoFrame * dest)
5978 {
5979   gint width = convert->in_width;
5980   gint height = convert->in_height;
5981   MatrixData *data = &convert->convert_matrix;
5982   guint8 *s, *d;
5983   FConvertPlaneTask *tasks;
5984   FConvertPlaneTask **tasks_p;
5985   gint n_threads;
5986   gint lines_per_thread;
5987   gint i;
5988 
5989   s = FRAME_GET_LINE (src, convert->in_y);
5990   s += (convert->in_x * 4);
5991   d = FRAME_GET_LINE (dest, convert->out_y);
5992   d += (convert->out_x * 4);
5993 
5994   n_threads = convert->conversion_runner->n_threads;
5995   tasks = convert->tasks[0] =
5996       g_renew (FConvertPlaneTask, convert->tasks[0], n_threads);
5997   tasks_p = convert->tasks_p[0] =
5998       g_renew (FConvertPlaneTask *, convert->tasks_p[0], n_threads);
5999 
6000   lines_per_thread = (height + n_threads - 1) / n_threads;
6001 
6002   for (i = 0; i < n_threads; i++) {
6003     tasks[i].dstride = FRAME_GET_STRIDE (dest);
6004     tasks[i].sstride = FRAME_GET_STRIDE (src);
6005     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
6006     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
6007 
6008     tasks[i].width = width;
6009     tasks[i].height = (i + 1) * lines_per_thread;
6010     tasks[i].height = MIN (tasks[i].height, height);
6011     tasks[i].height -= i * lines_per_thread;
6012     tasks[i].data = data;
6013 
6014     tasks_p[i] = &tasks[i];
6015   }
6016 
6017   gst_parallelized_task_runner_run (convert->conversion_runner,
6018       (GstParallelizedTaskFunc) convert_AYUV_ARGB_task, (gpointer) tasks_p);
6019 
6020   convert_fill_border (convert, dest);
6021 }
6022 
6023 static void
convert_AYUV_BGRA_task(FConvertPlaneTask * task)6024 convert_AYUV_BGRA_task (FConvertPlaneTask * task)
6025 {
6026   video_orc_convert_AYUV_BGRA (task->d, task->dstride, task->s,
6027       task->sstride, task->data->im[0][0], task->data->im[0][2],
6028       task->data->im[2][1], task->data->im[1][1], task->data->im[1][2],
6029       task->width, task->height);
6030 }
6031 
6032 static void
convert_AYUV_BGRA(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)6033 convert_AYUV_BGRA (GstVideoConverter * convert, const GstVideoFrame * src,
6034     GstVideoFrame * dest)
6035 {
6036   gint width = convert->in_width;
6037   gint height = convert->in_height;
6038   MatrixData *data = &convert->convert_matrix;
6039   guint8 *s, *d;
6040   FConvertPlaneTask *tasks;
6041   FConvertPlaneTask **tasks_p;
6042   gint n_threads;
6043   gint lines_per_thread;
6044   gint i;
6045 
6046   s = FRAME_GET_LINE (src, convert->in_y);
6047   s += (convert->in_x * 4);
6048   d = FRAME_GET_LINE (dest, convert->out_y);
6049   d += (convert->out_x * 4);
6050 
6051   n_threads = convert->conversion_runner->n_threads;
6052   tasks = convert->tasks[0] =
6053       g_renew (FConvertPlaneTask, convert->tasks[0], n_threads);
6054   tasks_p = convert->tasks_p[0] =
6055       g_renew (FConvertPlaneTask *, convert->tasks_p[0], n_threads);
6056 
6057   lines_per_thread = (height + n_threads - 1) / n_threads;
6058 
6059   for (i = 0; i < n_threads; i++) {
6060     tasks[i].dstride = FRAME_GET_STRIDE (dest);
6061     tasks[i].sstride = FRAME_GET_STRIDE (src);
6062     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
6063     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
6064 
6065     tasks[i].width = width;
6066     tasks[i].height = (i + 1) * lines_per_thread;
6067     tasks[i].height = MIN (tasks[i].height, height);
6068     tasks[i].height -= i * lines_per_thread;
6069     tasks[i].data = data;
6070 
6071     tasks_p[i] = &tasks[i];
6072   }
6073 
6074   gst_parallelized_task_runner_run (convert->conversion_runner,
6075       (GstParallelizedTaskFunc) convert_AYUV_BGRA_task, (gpointer) tasks_p);
6076 
6077   convert_fill_border (convert, dest);
6078 }
6079 
6080 static void
convert_AYUV_ABGR_task(FConvertPlaneTask * task)6081 convert_AYUV_ABGR_task (FConvertPlaneTask * task)
6082 {
6083   video_orc_convert_AYUV_ABGR (task->d, task->dstride, task->s,
6084       task->sstride, task->data->im[0][0], task->data->im[0][2],
6085       task->data->im[2][1], task->data->im[1][1], task->data->im[1][2],
6086       task->width, task->height);
6087 }
6088 
6089 static void
convert_AYUV_ABGR(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)6090 convert_AYUV_ABGR (GstVideoConverter * convert, const GstVideoFrame * src,
6091     GstVideoFrame * dest)
6092 {
6093   gint width = convert->in_width;
6094   gint height = convert->in_height;
6095   MatrixData *data = &convert->convert_matrix;
6096   guint8 *s, *d;
6097   FConvertPlaneTask *tasks;
6098   FConvertPlaneTask **tasks_p;
6099   gint n_threads;
6100   gint lines_per_thread;
6101   gint i;
6102 
6103   s = FRAME_GET_LINE (src, convert->in_y);
6104   s += (convert->in_x * 4);
6105   d = FRAME_GET_LINE (dest, convert->out_y);
6106   d += (convert->out_x * 4);
6107 
6108   n_threads = convert->conversion_runner->n_threads;
6109   tasks = convert->tasks[0] =
6110       g_renew (FConvertPlaneTask, convert->tasks[0], n_threads);
6111   tasks_p = convert->tasks_p[0] =
6112       g_renew (FConvertPlaneTask *, convert->tasks_p[0], n_threads);
6113 
6114   lines_per_thread = (height + n_threads - 1) / n_threads;
6115 
6116   for (i = 0; i < n_threads; i++) {
6117     tasks[i].dstride = FRAME_GET_STRIDE (dest);
6118     tasks[i].sstride = FRAME_GET_STRIDE (src);
6119     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
6120     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
6121 
6122     tasks[i].width = width;
6123     tasks[i].height = (i + 1) * lines_per_thread;
6124     tasks[i].height = MIN (tasks[i].height, height);
6125     tasks[i].height -= i * lines_per_thread;
6126     tasks[i].data = data;
6127 
6128     tasks_p[i] = &tasks[i];
6129   }
6130 
6131   gst_parallelized_task_runner_run (convert->conversion_runner,
6132       (GstParallelizedTaskFunc) convert_AYUV_ABGR_task, (gpointer) tasks_p);
6133 
6134   convert_fill_border (convert, dest);
6135 }
6136 
6137 static void
convert_AYUV_RGBA_task(FConvertPlaneTask * task)6138 convert_AYUV_RGBA_task (FConvertPlaneTask * task)
6139 {
6140   video_orc_convert_AYUV_RGBA (task->d, task->dstride, task->s,
6141       task->sstride, task->data->im[0][0], task->data->im[0][2],
6142       task->data->im[2][1], task->data->im[1][1], task->data->im[1][2],
6143       task->width, task->height);
6144 }
6145 
6146 static void
convert_AYUV_RGBA(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)6147 convert_AYUV_RGBA (GstVideoConverter * convert, const GstVideoFrame * src,
6148     GstVideoFrame * dest)
6149 {
6150   gint width = convert->in_width;
6151   gint height = convert->in_height;
6152   MatrixData *data = &convert->convert_matrix;
6153   guint8 *s, *d;
6154   FConvertPlaneTask *tasks;
6155   FConvertPlaneTask **tasks_p;
6156   gint n_threads;
6157   gint lines_per_thread;
6158   gint i;
6159 
6160   s = FRAME_GET_LINE (src, convert->in_y);
6161   s += (convert->in_x * 4);
6162   d = FRAME_GET_LINE (dest, convert->out_y);
6163   d += (convert->out_x * 4);
6164 
6165   n_threads = convert->conversion_runner->n_threads;
6166   tasks = convert->tasks[0] =
6167       g_renew (FConvertPlaneTask, convert->tasks[0], n_threads);
6168   tasks_p = convert->tasks_p[0] =
6169       g_renew (FConvertPlaneTask *, convert->tasks_p[0], n_threads);
6170 
6171   lines_per_thread = (height + n_threads - 1) / n_threads;
6172 
6173   for (i = 0; i < n_threads; i++) {
6174     tasks[i].dstride = FRAME_GET_STRIDE (dest);
6175     tasks[i].sstride = FRAME_GET_STRIDE (src);
6176     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
6177     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
6178 
6179     tasks[i].width = width;
6180     tasks[i].height = (i + 1) * lines_per_thread;
6181     tasks[i].height = MIN (tasks[i].height, height);
6182     tasks[i].height -= i * lines_per_thread;
6183     tasks[i].data = data;
6184 
6185     tasks_p[i] = &tasks[i];
6186   }
6187 
6188   gst_parallelized_task_runner_run (convert->conversion_runner,
6189       (GstParallelizedTaskFunc) convert_AYUV_RGBA_task, (gpointer) tasks_p);
6190 
6191   convert_fill_border (convert, dest);
6192 }
6193 #endif
6194 
6195 static void
convert_I420_BGRA_task(FConvertTask * task)6196 convert_I420_BGRA_task (FConvertTask * task)
6197 {
6198   gint i;
6199 
6200   for (i = task->height_0; i < task->height_1; i++) {
6201     guint8 *sy, *su, *sv, *d;
6202 
6203     d = FRAME_GET_LINE (task->dest, i + task->out_y);
6204     d += (task->out_x * 4);
6205     sy = FRAME_GET_Y_LINE (task->src, i + task->in_y);
6206     sy += task->in_x;
6207     su = FRAME_GET_U_LINE (task->src, (i + task->in_y) >> 1);
6208     su += (task->in_x >> 1);
6209     sv = FRAME_GET_V_LINE (task->src, (i + task->in_y) >> 1);
6210     sv += (task->in_x >> 1);
6211 
6212 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
6213     video_orc_convert_I420_BGRA (d, sy, su, sv,
6214         task->data->im[0][0], task->data->im[0][2],
6215         task->data->im[2][1], task->data->im[1][1], task->data->im[1][2],
6216         task->width);
6217 #else
6218     video_orc_convert_I420_ARGB (d, sy, su, sv,
6219         task->data->im[0][0], task->data->im[0][2],
6220         task->data->im[2][1], task->data->im[1][1], task->data->im[1][2],
6221         task->width);
6222 #endif
6223   }
6224 }
6225 
6226 static void
convert_I420_BGRA(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)6227 convert_I420_BGRA (GstVideoConverter * convert, const GstVideoFrame * src,
6228     GstVideoFrame * dest)
6229 {
6230   int i;
6231   gint width = convert->in_width;
6232   gint height = convert->in_height;
6233   MatrixData *data = &convert->convert_matrix;
6234   FConvertTask *tasks;
6235   FConvertTask **tasks_p;
6236   gint n_threads;
6237   gint lines_per_thread;
6238 
6239   n_threads = convert->conversion_runner->n_threads;
6240   tasks = convert->tasks[0] =
6241       g_renew (FConvertTask, convert->tasks[0], n_threads);
6242   tasks_p = convert->tasks_p[0] =
6243       g_renew (FConvertTask *, convert->tasks_p[0], n_threads);
6244 
6245   lines_per_thread = (height + n_threads - 1) / n_threads;
6246 
6247   for (i = 0; i < n_threads; i++) {
6248     tasks[i].src = src;
6249     tasks[i].dest = dest;
6250 
6251     tasks[i].width = width;
6252     tasks[i].data = data;
6253     tasks[i].in_x = convert->in_x;
6254     tasks[i].in_y = convert->in_y;
6255     tasks[i].out_x = convert->out_x;
6256     tasks[i].out_y = convert->out_y;
6257 
6258     tasks[i].height_0 = i * lines_per_thread;
6259     tasks[i].height_1 = tasks[i].height_0 + lines_per_thread;
6260     tasks[i].height_1 = MIN (height, tasks[i].height_1);
6261 
6262     tasks_p[i] = &tasks[i];
6263   }
6264 
6265   gst_parallelized_task_runner_run (convert->conversion_runner,
6266       (GstParallelizedTaskFunc) convert_I420_BGRA_task, (gpointer) tasks_p);
6267 
6268   convert_fill_border (convert, dest);
6269 }
6270 
6271 static void
convert_I420_ARGB_task(FConvertTask * task)6272 convert_I420_ARGB_task (FConvertTask * task)
6273 {
6274   gint i;
6275 
6276   for (i = task->height_0; i < task->height_1; i++) {
6277     guint8 *sy, *su, *sv, *d;
6278 
6279     d = FRAME_GET_LINE (task->dest, i + task->out_y);
6280     d += (task->out_x * 4);
6281     sy = FRAME_GET_Y_LINE (task->src, i + task->in_y);
6282     sy += task->in_x;
6283     su = FRAME_GET_U_LINE (task->src, (i + task->in_y) >> 1);
6284     su += (task->in_x >> 1);
6285     sv = FRAME_GET_V_LINE (task->src, (i + task->in_y) >> 1);
6286     sv += (task->in_x >> 1);
6287 
6288 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
6289     video_orc_convert_I420_ARGB (d, sy, su, sv,
6290         task->data->im[0][0], task->data->im[0][2],
6291         task->data->im[2][1], task->data->im[1][1], task->data->im[1][2],
6292         task->width);
6293 #else
6294     video_orc_convert_I420_BGRA (d, sy, su, sv,
6295         task->data->im[0][0], task->data->im[0][2],
6296         task->data->im[2][1], task->data->im[1][1], task->data->im[1][2],
6297         task->width);
6298 #endif
6299   }
6300 }
6301 
6302 static void
convert_I420_ARGB(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)6303 convert_I420_ARGB (GstVideoConverter * convert, const GstVideoFrame * src,
6304     GstVideoFrame * dest)
6305 {
6306   int i;
6307   gint width = convert->in_width;
6308   gint height = convert->in_height;
6309   MatrixData *data = &convert->convert_matrix;
6310   FConvertTask *tasks;
6311   FConvertTask **tasks_p;
6312   gint n_threads;
6313   gint lines_per_thread;
6314 
6315   n_threads = convert->conversion_runner->n_threads;
6316   tasks = convert->tasks[0] =
6317       g_renew (FConvertTask, convert->tasks[0], n_threads);
6318   tasks_p = convert->tasks_p[0] =
6319       g_renew (FConvertTask *, convert->tasks_p[0], n_threads);
6320 
6321   lines_per_thread = (height + n_threads - 1) / n_threads;
6322 
6323   for (i = 0; i < n_threads; i++) {
6324     tasks[i].src = src;
6325     tasks[i].dest = dest;
6326 
6327     tasks[i].width = width;
6328     tasks[i].data = data;
6329     tasks[i].in_x = convert->in_x;
6330     tasks[i].in_y = convert->in_y;
6331     tasks[i].out_x = convert->out_x;
6332     tasks[i].out_y = convert->out_y;
6333 
6334     tasks[i].height_0 = i * lines_per_thread;
6335     tasks[i].height_1 = tasks[i].height_0 + lines_per_thread;
6336     tasks[i].height_1 = MIN (height, tasks[i].height_1);
6337 
6338     tasks_p[i] = &tasks[i];
6339   }
6340 
6341   gst_parallelized_task_runner_run (convert->conversion_runner,
6342       (GstParallelizedTaskFunc) convert_I420_ARGB_task, (gpointer) tasks_p);
6343 
6344   convert_fill_border (convert, dest);
6345 }
6346 
6347 static void
convert_I420_pack_ARGB_task(FConvertTask * task)6348 convert_I420_pack_ARGB_task (FConvertTask * task)
6349 {
6350   gint i;
6351   gpointer d[GST_VIDEO_MAX_PLANES];
6352 
6353   d[0] = FRAME_GET_LINE (task->dest, 0);
6354   d[0] =
6355       (guint8 *) d[0] +
6356       task->out_x * GST_VIDEO_FORMAT_INFO_PSTRIDE (task->dest->info.finfo, 0);
6357 
6358   for (i = task->height_0; i < task->height_1; i++) {
6359     guint8 *sy, *su, *sv;
6360 
6361     sy = FRAME_GET_Y_LINE (task->src, i + task->in_y);
6362     sy += task->in_x;
6363     su = FRAME_GET_U_LINE (task->src, (i + task->in_y) >> 1);
6364     su += (task->in_x >> 1);
6365     sv = FRAME_GET_V_LINE (task->src, (i + task->in_y) >> 1);
6366     sv += (task->in_x >> 1);
6367 
6368 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
6369     video_orc_convert_I420_ARGB (task->tmpline, sy, su, sv,
6370         task->data->im[0][0], task->data->im[0][2],
6371         task->data->im[2][1], task->data->im[1][1], task->data->im[1][2],
6372         task->width);
6373 #else
6374     video_orc_convert_I420_BGRA (task->tmpline, sy, su, sv,
6375         task->data->im[0][0], task->data->im[0][2],
6376         task->data->im[2][1], task->data->im[1][1], task->data->im[1][2],
6377         task->width);
6378 #endif
6379     task->dest->info.finfo->pack_func (task->dest->info.finfo,
6380         (GST_VIDEO_FRAME_IS_INTERLACED (task->dest) ?
6381             GST_VIDEO_PACK_FLAG_INTERLACED :
6382             GST_VIDEO_PACK_FLAG_NONE),
6383         task->tmpline, 0, d, task->dest->info.stride,
6384         task->dest->info.chroma_site, i + task->out_y, task->width);
6385   }
6386 }
6387 
6388 static void
convert_I420_pack_ARGB(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)6389 convert_I420_pack_ARGB (GstVideoConverter * convert, const GstVideoFrame * src,
6390     GstVideoFrame * dest)
6391 {
6392   int i;
6393   gint width = convert->in_width;
6394   gint height = convert->in_height;
6395   MatrixData *data = &convert->convert_matrix;
6396   FConvertTask *tasks;
6397   FConvertTask **tasks_p;
6398   gint n_threads;
6399   gint lines_per_thread;
6400 
6401   n_threads = convert->conversion_runner->n_threads;
6402   tasks = convert->tasks[0] =
6403       g_renew (FConvertTask, convert->tasks[0], n_threads);
6404   tasks_p = convert->tasks_p[0] =
6405       g_renew (FConvertTask *, convert->tasks_p[0], n_threads);
6406 
6407   lines_per_thread = (height + n_threads - 1) / n_threads;
6408 
6409   for (i = 0; i < n_threads; i++) {
6410     tasks[i].src = src;
6411     tasks[i].dest = dest;
6412 
6413     tasks[i].width = width;
6414     tasks[i].data = data;
6415     tasks[i].in_x = convert->in_x;
6416     tasks[i].in_y = convert->in_y;
6417     tasks[i].out_x = convert->out_x;
6418     tasks[i].out_y = convert->out_y;
6419     tasks[i].tmpline = convert->tmpline[i];
6420 
6421     tasks[i].height_0 = i * lines_per_thread;
6422     tasks[i].height_1 = tasks[i].height_0 + lines_per_thread;
6423     tasks[i].height_1 = MIN (height, tasks[i].height_1);
6424 
6425     tasks_p[i] = &tasks[i];
6426   }
6427 
6428   gst_parallelized_task_runner_run (convert->conversion_runner,
6429       (GstParallelizedTaskFunc) convert_I420_pack_ARGB_task,
6430       (gpointer) tasks_p);
6431 
6432   convert_fill_border (convert, dest);
6433 }
6434 
6435 static void
convert_A420_pack_ARGB_task(FConvertTask * task)6436 convert_A420_pack_ARGB_task (FConvertTask * task)
6437 {
6438   gint i;
6439   gpointer d[GST_VIDEO_MAX_PLANES];
6440 
6441   d[0] = FRAME_GET_LINE (task->dest, 0);
6442   d[0] =
6443       (guint8 *) d[0] +
6444       task->out_x * GST_VIDEO_FORMAT_INFO_PSTRIDE (task->dest->info.finfo, 0);
6445 
6446   for (i = task->height_0; i < task->height_1; i++) {
6447     guint8 *sy, *su, *sv, *sa;
6448 
6449     sy = FRAME_GET_Y_LINE (task->src, i + task->in_y);
6450     sy += task->in_x;
6451     su = FRAME_GET_U_LINE (task->src, (i + task->in_y) >> 1);
6452     su += (task->in_x >> 1);
6453     sv = FRAME_GET_V_LINE (task->src, (i + task->in_y) >> 1);
6454     sv += (task->in_x >> 1);
6455     sa = FRAME_GET_A_LINE (task->src, i + task->in_y);
6456     sa += task->in_x;
6457 
6458 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
6459     video_orc_convert_A420_ARGB (task->tmpline, sy, su, sv, sa,
6460         task->data->im[0][0], task->data->im[0][2],
6461         task->data->im[2][1], task->data->im[1][1], task->data->im[1][2],
6462         task->width);
6463 #else
6464     video_orc_convert_A420_BGRA (task->tmpline, sy, su, sv, sa,
6465         task->data->im[0][0], task->data->im[0][2],
6466         task->data->im[2][1], task->data->im[1][1], task->data->im[1][2],
6467         task->width);
6468 #endif
6469 
6470     task->dest->info.finfo->pack_func (task->dest->info.finfo,
6471         (GST_VIDEO_FRAME_IS_INTERLACED (task->dest) ?
6472             GST_VIDEO_PACK_FLAG_INTERLACED :
6473             GST_VIDEO_PACK_FLAG_NONE),
6474         task->tmpline, 0, d, task->dest->info.stride,
6475         task->dest->info.chroma_site, i + task->out_y, task->width);
6476   }
6477 }
6478 
6479 static void
convert_A420_pack_ARGB(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)6480 convert_A420_pack_ARGB (GstVideoConverter * convert, const GstVideoFrame * src,
6481     GstVideoFrame * dest)
6482 {
6483   int i;
6484   gint width = convert->in_width;
6485   gint height = convert->in_height;
6486   MatrixData *data = &convert->convert_matrix;
6487   FConvertTask *tasks;
6488   FConvertTask **tasks_p;
6489   gint n_threads;
6490   gint lines_per_thread;
6491 
6492   n_threads = convert->conversion_runner->n_threads;
6493   tasks = convert->tasks[0] =
6494       g_renew (FConvertTask, convert->tasks[0], n_threads);
6495   tasks_p = convert->tasks_p[0] =
6496       g_renew (FConvertTask *, convert->tasks_p[0], n_threads);
6497 
6498   lines_per_thread = (height + n_threads - 1) / n_threads;
6499 
6500   for (i = 0; i < n_threads; i++) {
6501     tasks[i].src = src;
6502     tasks[i].dest = dest;
6503 
6504     tasks[i].width = width;
6505     tasks[i].data = data;
6506     tasks[i].in_x = convert->in_x;
6507     tasks[i].in_y = convert->in_y;
6508     tasks[i].out_x = convert->out_x;
6509     tasks[i].out_y = convert->out_y;
6510     tasks[i].tmpline = convert->tmpline[i];
6511 
6512     tasks[i].height_0 = i * lines_per_thread;
6513     tasks[i].height_1 = tasks[i].height_0 + lines_per_thread;
6514     tasks[i].height_1 = MIN (height, tasks[i].height_1);
6515 
6516     tasks_p[i] = &tasks[i];
6517   }
6518 
6519   gst_parallelized_task_runner_run (convert->conversion_runner,
6520       (GstParallelizedTaskFunc) convert_A420_pack_ARGB_task,
6521       (gpointer) tasks_p);
6522 
6523   convert_fill_border (convert, dest);
6524 }
6525 
6526 static void
convert_A420_BGRA_task(FConvertTask * task)6527 convert_A420_BGRA_task (FConvertTask * task)
6528 {
6529   gint i;
6530 
6531   for (i = task->height_0; i < task->height_1; i++) {
6532     guint8 *sy, *su, *sv, *sa, *d;
6533 
6534     d = FRAME_GET_LINE (task->dest, i + task->out_y);
6535     d += (task->out_x * 4);
6536     sy = FRAME_GET_Y_LINE (task->src, i + task->in_y);
6537     sy += task->in_x;
6538     su = FRAME_GET_U_LINE (task->src, (i + task->in_y) >> 1);
6539     su += (task->in_x >> 1);
6540     sv = FRAME_GET_V_LINE (task->src, (i + task->in_y) >> 1);
6541     sv += (task->in_x >> 1);
6542     sa = FRAME_GET_A_LINE (task->src, i + task->in_y);
6543     sa += task->in_x;
6544 
6545 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
6546     video_orc_convert_A420_BGRA (d, sy, su, sv, sa,
6547         task->data->im[0][0], task->data->im[0][2],
6548         task->data->im[2][1], task->data->im[1][1], task->data->im[1][2],
6549         task->width);
6550 #else
6551     video_orc_convert_A420_ARGB (d, sy, su, sv, sa,
6552         task->data->im[0][0], task->data->im[0][2],
6553         task->data->im[2][1], task->data->im[1][1], task->data->im[1][2],
6554         task->width);
6555 #endif
6556   }
6557 }
6558 
6559 static void
convert_A420_BGRA(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)6560 convert_A420_BGRA (GstVideoConverter * convert, const GstVideoFrame * src,
6561     GstVideoFrame * dest)
6562 {
6563   int i;
6564   gint width = convert->in_width;
6565   gint height = convert->in_height;
6566   MatrixData *data = &convert->convert_matrix;
6567   FConvertTask *tasks;
6568   FConvertTask **tasks_p;
6569   gint n_threads;
6570   gint lines_per_thread;
6571 
6572   n_threads = convert->conversion_runner->n_threads;
6573   tasks = convert->tasks[0] =
6574       g_renew (FConvertTask, convert->tasks[0], n_threads);
6575   tasks_p = convert->tasks_p[0] =
6576       g_renew (FConvertTask *, convert->tasks_p[0], n_threads);
6577 
6578   lines_per_thread = (height + n_threads - 1) / n_threads;
6579 
6580   for (i = 0; i < n_threads; i++) {
6581     tasks[i].src = src;
6582     tasks[i].dest = dest;
6583 
6584     tasks[i].width = width;
6585     tasks[i].data = data;
6586     tasks[i].in_x = convert->in_x;
6587     tasks[i].in_y = convert->in_y;
6588     tasks[i].out_x = convert->out_x;
6589     tasks[i].out_y = convert->out_y;
6590 
6591     tasks[i].height_0 = i * lines_per_thread;
6592     tasks[i].height_1 = tasks[i].height_0 + lines_per_thread;
6593     tasks[i].height_1 = MIN (height, tasks[i].height_1);
6594 
6595     tasks_p[i] = &tasks[i];
6596   }
6597 
6598   gst_parallelized_task_runner_run (convert->conversion_runner,
6599       (GstParallelizedTaskFunc) convert_A420_BGRA_task, (gpointer) tasks_p);
6600 
6601   convert_fill_border (convert, dest);
6602 }
6603 
6604 static void
memset_u24(guint8 * data,guint8 col[3],unsigned int n)6605 memset_u24 (guint8 * data, guint8 col[3], unsigned int n)
6606 {
6607   unsigned int i;
6608 
6609   for (i = 0; i < n; i++) {
6610     data[0] = col[0];
6611     data[1] = col[1];
6612     data[2] = col[2];
6613     data += 3;
6614   }
6615 }
6616 
6617 static void
memset_u32_16(guint8 * data,guint8 col[4],unsigned int n)6618 memset_u32_16 (guint8 * data, guint8 col[4], unsigned int n)
6619 {
6620   unsigned int i;
6621 
6622   for (i = 0; i < n; i += 2) {
6623     data[0] = col[0];
6624     data[1] = col[1];
6625     if (i + 1 < n) {
6626       data[2] = col[2];
6627       data[3] = col[3];
6628     }
6629     data += 4;
6630   }
6631 }
6632 
6633 #define MAKE_BORDER_FUNC(func)                                                  \
6634         for (i = 0; i < out_y; i++)                                             \
6635           func (FRAME_GET_PLANE_LINE (dest, k, i), col, out_maxwidth);          \
6636         if (rb_width || lb_width) {                                             \
6637           for (i = 0; i < out_height; i++) {                                    \
6638             guint8 *d = FRAME_GET_PLANE_LINE (dest, k, i + out_y);              \
6639             if (lb_width)                                                       \
6640               func (d, col, lb_width);                                          \
6641             if (rb_width)                                                       \
6642               func (d + (pstride * r_border), col, rb_width);                   \
6643           }                                                                     \
6644         }                                                                       \
6645         for (i = out_y + out_height; i < out_maxheight; i++)                    \
6646           func (FRAME_GET_PLANE_LINE (dest, k, i), col, out_maxwidth);          \
6647 
6648 static void
convert_fill_border(GstVideoConverter * convert,GstVideoFrame * dest)6649 convert_fill_border (GstVideoConverter * convert, GstVideoFrame * dest)
6650 {
6651   int k, n_planes;
6652   const GstVideoFormatInfo *out_finfo;
6653 
6654   if (!convert->fill_border || !convert->borderline)
6655     return;
6656 
6657   out_finfo = convert->out_info.finfo;
6658 
6659   n_planes = GST_VIDEO_FRAME_N_PLANES (dest);
6660 
6661   for (k = 0; k < n_planes; k++) {
6662     gint comp[GST_VIDEO_MAX_COMPONENTS];
6663     gint i, out_x, out_y, out_width, out_height, pstride, pgroup;
6664     gint r_border, lb_width, rb_width;
6665     gint out_maxwidth, out_maxheight;
6666     gpointer borders;
6667 
6668     gst_video_format_info_component (out_finfo, k, comp);
6669     out_x = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (out_finfo, comp[0],
6670         convert->out_x);
6671     out_y = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (out_finfo, comp[0],
6672         convert->out_y);
6673     out_width = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (out_finfo, comp[0],
6674         convert->out_width);
6675     out_height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (out_finfo, comp[0],
6676         convert->out_height);
6677     out_maxwidth = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (out_finfo, comp[0],
6678         convert->out_maxwidth);
6679     out_maxheight = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (out_finfo, comp[0],
6680         convert->out_maxheight);
6681 
6682     pstride = GST_VIDEO_FORMAT_INFO_PSTRIDE (out_finfo, comp[0]);
6683 
6684     switch (GST_VIDEO_FORMAT_INFO_FORMAT (out_finfo)) {
6685       case GST_VIDEO_FORMAT_YUY2:
6686       case GST_VIDEO_FORMAT_YVYU:
6687       case GST_VIDEO_FORMAT_UYVY:
6688         pgroup = 42;
6689         out_maxwidth = GST_ROUND_UP_2 (out_maxwidth);
6690         break;
6691       default:
6692         pgroup = pstride;
6693         break;
6694     }
6695 
6696     r_border = out_x + out_width;
6697     rb_width = out_maxwidth - r_border;
6698     lb_width = out_x;
6699 
6700     borders = &convert->borders[k];
6701 
6702     switch (pgroup) {
6703       case 1:
6704       {
6705         guint8 col = ((guint8 *) borders)[0];
6706         MAKE_BORDER_FUNC (memset);
6707         break;
6708       }
6709       case 2:
6710       {
6711         guint16 col = ((guint16 *) borders)[0];
6712         MAKE_BORDER_FUNC (video_orc_splat_u16);
6713         break;
6714       }
6715       case 3:
6716       {
6717         guint8 col[3];
6718         col[0] = ((guint8 *) borders)[0];
6719         col[1] = ((guint8 *) borders)[1];
6720         col[2] = ((guint8 *) borders)[2];
6721         MAKE_BORDER_FUNC (memset_u24);
6722         break;
6723       }
6724       case 4:
6725       {
6726         guint32 col = ((guint32 *) borders)[0];
6727         MAKE_BORDER_FUNC (video_orc_splat_u32);
6728         break;
6729       }
6730       case 8:
6731       {
6732         guint64 col = ((guint64 *) borders)[0];
6733         MAKE_BORDER_FUNC (video_orc_splat_u64);
6734         break;
6735       }
6736       case 42:
6737       {
6738         guint8 col[4];
6739         col[0] = ((guint8 *) borders)[0];
6740         col[2] = ((guint8 *) borders)[2];
6741         col[1] = ((guint8 *) borders)[r_border & 1 ? 3 : 1];
6742         col[3] = ((guint8 *) borders)[r_border & 1 ? 1 : 3];
6743         MAKE_BORDER_FUNC (memset_u32_16);
6744         break;
6745       }
6746       default:
6747         break;
6748     }
6749   }
6750 }
6751 
6752 typedef struct
6753 {
6754   const guint8 *s, *s2;
6755   guint8 *d, *d2;
6756   gint sstride, dstride;
6757   gint width, height;
6758   gint fill;
6759 } FSimpleScaleTask;
6760 
6761 static void
convert_plane_fill_task(FSimpleScaleTask * task)6762 convert_plane_fill_task (FSimpleScaleTask * task)
6763 {
6764   video_orc_memset_2d (task->d, task->dstride,
6765       task->fill, task->width, task->height);
6766 }
6767 
6768 static void
convert_plane_fill(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest,gint plane)6769 convert_plane_fill (GstVideoConverter * convert,
6770     const GstVideoFrame * src, GstVideoFrame * dest, gint plane)
6771 {
6772   guint8 *d;
6773   FSimpleScaleTask *tasks;
6774   FSimpleScaleTask **tasks_p;
6775   gint n_threads;
6776   gint lines_per_thread;
6777   gint i;
6778 
6779   d = FRAME_GET_PLANE_LINE (dest, plane, convert->fout_y[plane]);
6780   d += convert->fout_x[plane];
6781 
6782   n_threads = convert->conversion_runner->n_threads;
6783   tasks = convert->tasks[plane] =
6784       g_renew (FSimpleScaleTask, convert->tasks[plane], n_threads);
6785   tasks_p = convert->tasks_p[plane] =
6786       g_renew (FSimpleScaleTask *, convert->tasks_p[plane], n_threads);
6787   lines_per_thread = (convert->fout_height[plane] + n_threads - 1) / n_threads;
6788 
6789   for (i = 0; i < n_threads; i++) {
6790     tasks[i].d = d + i * lines_per_thread * convert->fout_width[plane];
6791 
6792     tasks[i].fill = convert->ffill[plane];
6793     tasks[i].width = convert->fout_width[plane];
6794     tasks[i].height = (i + 1) * lines_per_thread;
6795     tasks[i].height = MIN (tasks[i].height, convert->fout_height[plane]);
6796     tasks[i].height -= i * lines_per_thread;
6797     tasks[i].dstride = FRAME_GET_PLANE_STRIDE (dest, plane);
6798 
6799     tasks_p[i] = &tasks[i];
6800   }
6801 
6802   gst_parallelized_task_runner_run (convert->conversion_runner,
6803       (GstParallelizedTaskFunc) convert_plane_fill_task, (gpointer) tasks_p);
6804 }
6805 
6806 static void
convert_plane_h_double_task(FSimpleScaleTask * task)6807 convert_plane_h_double_task (FSimpleScaleTask * task)
6808 {
6809   video_orc_planar_chroma_422_444 (task->d,
6810       task->dstride, task->s, task->sstride, task->width / 2, task->height);
6811 }
6812 
6813 static void
convert_plane_h_double(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest,gint plane)6814 convert_plane_h_double (GstVideoConverter * convert,
6815     const GstVideoFrame * src, GstVideoFrame * dest, gint plane)
6816 {
6817   guint8 *s, *d;
6818   gint splane = convert->fsplane[plane];
6819   FSimpleScaleTask *tasks;
6820   FSimpleScaleTask **tasks_p;
6821   gint n_threads;
6822   gint lines_per_thread;
6823   gint i;
6824 
6825   s = FRAME_GET_PLANE_LINE (src, splane, convert->fin_y[splane]);
6826   s += convert->fin_x[splane];
6827   d = FRAME_GET_PLANE_LINE (dest, plane, convert->fout_y[plane]);
6828   d += convert->fout_x[plane];
6829 
6830   n_threads = convert->conversion_runner->n_threads;
6831   tasks = convert->tasks[plane] =
6832       g_renew (FSimpleScaleTask, convert->tasks[plane], n_threads);
6833   tasks_p = convert->tasks_p[plane] =
6834       g_renew (FSimpleScaleTask *, convert->tasks_p[plane], n_threads);
6835   lines_per_thread = (convert->fout_height[plane] + n_threads - 1) / n_threads;
6836 
6837   for (i = 0; i < n_threads; i++) {
6838     tasks[i].dstride = FRAME_GET_PLANE_STRIDE (dest, plane);
6839     tasks[i].sstride = FRAME_GET_PLANE_STRIDE (src, splane);
6840 
6841     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
6842     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
6843 
6844     tasks[i].width = convert->fout_width[plane];
6845     tasks[i].height = (i + 1) * lines_per_thread;
6846     tasks[i].height = MIN (tasks[i].height, convert->fout_height[plane]);
6847     tasks[i].height -= i * lines_per_thread;
6848 
6849     tasks_p[i] = &tasks[i];
6850   }
6851 
6852   gst_parallelized_task_runner_run (convert->conversion_runner,
6853       (GstParallelizedTaskFunc) convert_plane_h_double_task,
6854       (gpointer) tasks_p);
6855 }
6856 
6857 static void
convert_plane_h_halve_task(FSimpleScaleTask * task)6858 convert_plane_h_halve_task (FSimpleScaleTask * task)
6859 {
6860   video_orc_planar_chroma_444_422 (task->d,
6861       task->dstride, task->s, task->sstride, task->width, task->height);
6862 }
6863 
6864 static void
convert_plane_h_halve(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest,gint plane)6865 convert_plane_h_halve (GstVideoConverter * convert,
6866     const GstVideoFrame * src, GstVideoFrame * dest, gint plane)
6867 {
6868   guint8 *s, *d;
6869   gint splane = convert->fsplane[plane];
6870   FSimpleScaleTask *tasks;
6871   FSimpleScaleTask **tasks_p;
6872   gint n_threads;
6873   gint lines_per_thread;
6874   gint i;
6875 
6876   s = FRAME_GET_PLANE_LINE (src, splane, convert->fin_y[splane]);
6877   s += convert->fin_x[splane];
6878   d = FRAME_GET_PLANE_LINE (dest, plane, convert->fout_y[plane]);
6879   d += convert->fout_x[plane];
6880 
6881   n_threads = convert->conversion_runner->n_threads;
6882   tasks = convert->tasks[plane] =
6883       g_renew (FSimpleScaleTask, convert->tasks[plane], n_threads);
6884   tasks_p = convert->tasks_p[plane] =
6885       g_renew (FSimpleScaleTask *, convert->tasks_p[plane], n_threads);
6886   lines_per_thread = (convert->fout_height[plane] + n_threads - 1) / n_threads;
6887 
6888   for (i = 0; i < n_threads; i++) {
6889     tasks[i].dstride = FRAME_GET_PLANE_STRIDE (dest, plane);
6890     tasks[i].sstride = FRAME_GET_PLANE_STRIDE (src, splane);
6891 
6892     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
6893     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
6894 
6895     tasks[i].width = convert->fout_width[plane];
6896     tasks[i].height = (i + 1) * lines_per_thread;
6897     tasks[i].height = MIN (tasks[i].height, convert->fout_height[plane]);
6898     tasks[i].height -= i * lines_per_thread;
6899 
6900     tasks_p[i] = &tasks[i];
6901   }
6902 
6903   gst_parallelized_task_runner_run (convert->conversion_runner,
6904       (GstParallelizedTaskFunc) convert_plane_h_halve_task, (gpointer) tasks_p);
6905 }
6906 
6907 static void
convert_plane_v_double_task(FSimpleScaleTask * task)6908 convert_plane_v_double_task (FSimpleScaleTask * task)
6909 {
6910   video_orc_planar_chroma_420_422 (task->d, 2 * task->dstride, task->d2,
6911       2 * task->dstride, task->s, task->sstride, task->width, task->height / 2);
6912 }
6913 
6914 static void
convert_plane_v_double(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest,gint plane)6915 convert_plane_v_double (GstVideoConverter * convert,
6916     const GstVideoFrame * src, GstVideoFrame * dest, gint plane)
6917 {
6918   guint8 *s, *d1, *d2;
6919   gint ds, splane = convert->fsplane[plane];
6920   FSimpleScaleTask *tasks;
6921   FSimpleScaleTask **tasks_p;
6922   gint n_threads;
6923   gint lines_per_thread;
6924   gint i;
6925 
6926   s = FRAME_GET_PLANE_LINE (src, splane, convert->fin_y[splane]);
6927   s += convert->fin_x[splane];
6928   d1 = FRAME_GET_PLANE_LINE (dest, plane, convert->fout_y[plane]);
6929   d1 += convert->fout_x[plane];
6930   d2 = FRAME_GET_PLANE_LINE (dest, plane, convert->fout_y[plane] + 1);
6931   d2 += convert->fout_x[plane];
6932   ds = FRAME_GET_PLANE_STRIDE (dest, plane);
6933 
6934   n_threads = convert->conversion_runner->n_threads;
6935   tasks = convert->tasks[plane] =
6936       g_renew (FSimpleScaleTask, convert->tasks[plane], n_threads);
6937   tasks_p = convert->tasks_p[plane] =
6938       g_renew (FSimpleScaleTask *, convert->tasks_p[plane], n_threads);
6939   lines_per_thread =
6940       GST_ROUND_UP_2 ((convert->fout_height[plane] + n_threads -
6941           1) / n_threads);
6942 
6943   for (i = 0; i < n_threads; i++) {
6944     tasks[i].d = d1 + i * lines_per_thread * ds;
6945     tasks[i].d2 = d2 + i * lines_per_thread * ds;
6946     tasks[i].dstride = ds;
6947     tasks[i].sstride = FRAME_GET_PLANE_STRIDE (src, splane);
6948     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride / 2;
6949 
6950     tasks[i].width = convert->fout_width[plane];
6951     tasks[i].height = (i + 1) * lines_per_thread;
6952     tasks[i].height = MIN (tasks[i].height, convert->fout_height[plane]);
6953     tasks[i].height -= i * lines_per_thread;
6954 
6955     tasks_p[i] = &tasks[i];
6956   }
6957 
6958   gst_parallelized_task_runner_run (convert->conversion_runner,
6959       (GstParallelizedTaskFunc) convert_plane_v_double_task,
6960       (gpointer) tasks_p);
6961 }
6962 
6963 static void
convert_plane_v_halve_task(FSimpleScaleTask * task)6964 convert_plane_v_halve_task (FSimpleScaleTask * task)
6965 {
6966   video_orc_planar_chroma_422_420 (task->d, task->dstride, task->s,
6967       2 * task->sstride, task->s2, 2 * task->sstride, task->width,
6968       task->height);
6969 }
6970 
6971 static void
convert_plane_v_halve(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest,gint plane)6972 convert_plane_v_halve (GstVideoConverter * convert,
6973     const GstVideoFrame * src, GstVideoFrame * dest, gint plane)
6974 {
6975   guint8 *s1, *s2, *d;
6976   gint ss, ds, splane = convert->fsplane[plane];
6977   FSimpleScaleTask *tasks;
6978   FSimpleScaleTask **tasks_p;
6979   gint n_threads;
6980   gint lines_per_thread;
6981   gint i;
6982 
6983   s1 = FRAME_GET_PLANE_LINE (src, splane, convert->fin_y[splane]);
6984   s1 += convert->fin_x[splane];
6985   s2 = FRAME_GET_PLANE_LINE (src, splane, convert->fin_y[splane] + 1);
6986   s2 += convert->fin_x[splane];
6987   d = FRAME_GET_PLANE_LINE (dest, plane, convert->fout_y[plane]);
6988   d += convert->fout_x[plane];
6989 
6990   ss = FRAME_GET_PLANE_STRIDE (src, splane);
6991   ds = FRAME_GET_PLANE_STRIDE (dest, plane);
6992 
6993   n_threads = convert->conversion_runner->n_threads;
6994   tasks = convert->tasks[plane] =
6995       g_renew (FSimpleScaleTask, convert->tasks[plane], n_threads);
6996   tasks_p = convert->tasks_p[plane] =
6997       g_renew (FSimpleScaleTask *, convert->tasks_p[plane], n_threads);
6998   lines_per_thread = (convert->fout_height[plane] + n_threads - 1) / n_threads;
6999 
7000   for (i = 0; i < n_threads; i++) {
7001     tasks[i].d = d + i * lines_per_thread * ds;
7002     tasks[i].dstride = ds;
7003     tasks[i].s = s1 + i * lines_per_thread * ss * 2;
7004     tasks[i].s2 = s2 + i * lines_per_thread * ss * 2;
7005     tasks[i].sstride = ss;
7006 
7007     tasks[i].width = convert->fout_width[plane];
7008     tasks[i].height = (i + 1) * lines_per_thread;
7009     tasks[i].height = MIN (tasks[i].height, convert->fout_height[plane]);
7010     tasks[i].height -= i * lines_per_thread;
7011 
7012     tasks_p[i] = &tasks[i];
7013   }
7014 
7015   gst_parallelized_task_runner_run (convert->conversion_runner,
7016       (GstParallelizedTaskFunc) convert_plane_v_halve_task, (gpointer) tasks_p);
7017 }
7018 
7019 static void
convert_plane_hv_double_task(FSimpleScaleTask * task)7020 convert_plane_hv_double_task (FSimpleScaleTask * task)
7021 {
7022   video_orc_planar_chroma_420_444 (task->d, 2 * task->dstride, task->d2,
7023       2 * task->dstride, task->s, task->sstride, (task->width + 1) / 2,
7024       task->height / 2);
7025 }
7026 
7027 static void
convert_plane_hv_double(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest,gint plane)7028 convert_plane_hv_double (GstVideoConverter * convert,
7029     const GstVideoFrame * src, GstVideoFrame * dest, gint plane)
7030 {
7031   guint8 *s, *d1, *d2;
7032   gint ss, ds, splane = convert->fsplane[plane];
7033   FSimpleScaleTask *tasks;
7034   FSimpleScaleTask **tasks_p;
7035   gint n_threads;
7036   gint lines_per_thread;
7037   gint i;
7038 
7039   s = FRAME_GET_PLANE_LINE (src, splane, convert->fin_y[splane]);
7040   s += convert->fin_x[splane];
7041   d1 = FRAME_GET_PLANE_LINE (dest, plane, convert->fout_y[plane]);
7042   d1 += convert->fout_x[plane];
7043   d2 = FRAME_GET_PLANE_LINE (dest, plane, convert->fout_y[plane] + 1);
7044   d2 += convert->fout_x[plane];
7045   ss = FRAME_GET_PLANE_STRIDE (src, splane);
7046   ds = FRAME_GET_PLANE_STRIDE (dest, plane);
7047 
7048   n_threads = convert->conversion_runner->n_threads;
7049   tasks = convert->tasks[plane] =
7050       g_renew (FSimpleScaleTask, convert->tasks[plane], n_threads);
7051   tasks_p = convert->tasks_p[plane] =
7052       g_renew (FSimpleScaleTask *, convert->tasks_p[plane], n_threads);
7053   lines_per_thread =
7054       GST_ROUND_UP_2 ((convert->fout_height[plane] + n_threads -
7055           1) / n_threads);
7056 
7057   for (i = 0; i < n_threads; i++) {
7058     tasks[i].d = d1 + i * lines_per_thread * ds;
7059     tasks[i].d2 = d2 + i * lines_per_thread * ds;
7060     tasks[i].dstride = ds;
7061     tasks[i].sstride = ss;
7062     tasks[i].s = s + i * lines_per_thread * ss / 2;
7063 
7064     tasks[i].width = convert->fout_width[plane];
7065     tasks[i].height = (i + 1) * lines_per_thread;
7066     tasks[i].height = MIN (tasks[i].height, convert->fout_height[plane]);
7067     tasks[i].height -= i * lines_per_thread;
7068 
7069     tasks_p[i] = &tasks[i];
7070   }
7071 
7072   gst_parallelized_task_runner_run (convert->conversion_runner,
7073       (GstParallelizedTaskFunc) convert_plane_hv_double_task,
7074       (gpointer) tasks_p);
7075 }
7076 
7077 static void
convert_plane_hv_halve_task(FSimpleScaleTask * task)7078 convert_plane_hv_halve_task (FSimpleScaleTask * task)
7079 {
7080   video_orc_planar_chroma_444_420 (task->d, task->dstride, task->s,
7081       2 * task->sstride, task->s2, 2 * task->sstride, task->width,
7082       task->height);
7083 }
7084 
7085 static void
convert_plane_hv_halve(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest,gint plane)7086 convert_plane_hv_halve (GstVideoConverter * convert,
7087     const GstVideoFrame * src, GstVideoFrame * dest, gint plane)
7088 {
7089   guint8 *s1, *s2, *d;
7090   gint ss, ds, splane = convert->fsplane[plane];
7091   FSimpleScaleTask *tasks;
7092   FSimpleScaleTask **tasks_p;
7093   gint n_threads;
7094   gint lines_per_thread;
7095   gint i;
7096 
7097   s1 = FRAME_GET_PLANE_LINE (src, splane, convert->fin_y[splane]);
7098   s1 += convert->fin_x[splane];
7099   s2 = FRAME_GET_PLANE_LINE (src, splane, convert->fin_y[splane] + 1);
7100   s2 += convert->fin_x[splane];
7101   d = FRAME_GET_PLANE_LINE (dest, plane, convert->fout_y[plane]);
7102   d += convert->fout_x[plane];
7103   ss = FRAME_GET_PLANE_STRIDE (src, splane);
7104   ds = FRAME_GET_PLANE_STRIDE (dest, plane);
7105 
7106   n_threads = convert->conversion_runner->n_threads;
7107   tasks = convert->tasks[plane] =
7108       g_renew (FSimpleScaleTask, convert->tasks[plane], n_threads);
7109   tasks_p = convert->tasks_p[plane] =
7110       g_renew (FSimpleScaleTask *, convert->tasks_p[plane], n_threads);
7111   lines_per_thread = (convert->fout_height[plane] + n_threads - 1) / n_threads;
7112 
7113   for (i = 0; i < n_threads; i++) {
7114     tasks[i].d = d + i * lines_per_thread * ds;
7115     tasks[i].dstride = ds;
7116     tasks[i].s = s1 + i * lines_per_thread * ss * 2;
7117     tasks[i].s2 = s2 + i * lines_per_thread * ss * 2;
7118     tasks[i].sstride = ss;
7119 
7120     tasks[i].width = convert->fout_width[plane];
7121     tasks[i].height = (i + 1) * lines_per_thread;
7122     tasks[i].height = MIN (tasks[i].height, convert->fout_height[plane]);
7123     tasks[i].height -= i * lines_per_thread;
7124 
7125     tasks_p[i] = &tasks[i];
7126   }
7127 
7128   gst_parallelized_task_runner_run (convert->conversion_runner,
7129       (GstParallelizedTaskFunc) convert_plane_hv_halve_task,
7130       (gpointer) tasks_p);
7131 }
7132 
7133 typedef struct
7134 {
7135   GstVideoScaler *h_scaler, *v_scaler;
7136   GstVideoFormat format;
7137   const guint8 *s;
7138   guint8 *d;
7139   gint sstride, dstride;
7140   guint x, y, w, h;
7141 } FScaleTask;
7142 
7143 static void
convert_plane_hv_task(FScaleTask * task)7144 convert_plane_hv_task (FScaleTask * task)
7145 {
7146   gst_video_scaler_2d (task->h_scaler, task->v_scaler, task->format,
7147       (guint8 *) task->s, task->sstride,
7148       task->d, task->dstride, task->x, task->y, task->w, task->h);
7149 }
7150 
7151 static void
convert_plane_hv(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest,gint plane)7152 convert_plane_hv (GstVideoConverter * convert,
7153     const GstVideoFrame * src, GstVideoFrame * dest, gint plane)
7154 {
7155   gint in_x, in_y, out_x, out_y, out_width, out_height;
7156   GstVideoFormat format;
7157   gint splane = convert->fsplane[plane];
7158   guint8 *s, *d;
7159   gint sstride, dstride;
7160   FScaleTask *tasks;
7161   FScaleTask **tasks_p;
7162   gint i, n_threads, lines_per_thread;
7163 
7164   in_x = convert->fin_x[splane];
7165   in_y = convert->fin_y[splane];
7166   out_x = convert->fout_x[plane];
7167   out_y = convert->fout_y[plane];
7168   out_width = convert->fout_width[plane];
7169   out_height = convert->fout_height[plane];
7170   format = convert->fformat[plane];
7171 
7172   s = FRAME_GET_PLANE_LINE (src, splane, in_y);
7173   s += in_x;
7174   d = FRAME_GET_PLANE_LINE (dest, plane, out_y);
7175   d += out_x;
7176 
7177   sstride = FRAME_GET_PLANE_STRIDE (src, splane);
7178   dstride = FRAME_GET_PLANE_STRIDE (dest, plane);
7179 
7180   n_threads = convert->conversion_runner->n_threads;
7181   tasks = convert->tasks[plane] =
7182       g_renew (FScaleTask, convert->tasks[plane], n_threads);
7183   tasks_p = convert->tasks_p[plane] =
7184       g_renew (FScaleTask *, convert->tasks_p[plane], n_threads);
7185 
7186   lines_per_thread = (out_height + n_threads - 1) / n_threads;
7187 
7188   for (i = 0; i < n_threads; i++) {
7189     tasks[i].h_scaler =
7190         convert->fh_scaler[plane].scaler ? convert->
7191         fh_scaler[plane].scaler[i] : NULL;
7192     tasks[i].v_scaler =
7193         convert->fv_scaler[plane].scaler ? convert->
7194         fv_scaler[plane].scaler[i] : NULL;
7195     tasks[i].format = format;
7196     tasks[i].s = s;
7197     tasks[i].d = d;
7198     tasks[i].sstride = sstride;
7199     tasks[i].dstride = dstride;
7200 
7201     tasks[i].x = 0;
7202     tasks[i].w = out_width;
7203 
7204     tasks[i].y = i * lines_per_thread;
7205     tasks[i].h = tasks[i].y + lines_per_thread;
7206     tasks[i].h = MIN (out_height, tasks[i].h);
7207 
7208     tasks_p[i] = &tasks[i];
7209   }
7210 
7211   gst_parallelized_task_runner_run (convert->conversion_runner,
7212       (GstParallelizedTaskFunc) convert_plane_hv_task, (gpointer) tasks_p);
7213 }
7214 
7215 static void
convert_scale_planes(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)7216 convert_scale_planes (GstVideoConverter * convert,
7217     const GstVideoFrame * src, GstVideoFrame * dest)
7218 {
7219   int i, n_planes;
7220 
7221   n_planes = GST_VIDEO_FRAME_N_PLANES (dest);
7222   for (i = 0; i < n_planes; i++) {
7223     if (convert->fconvert[i])
7224       convert->fconvert[i] (convert, src, dest, i);
7225   }
7226   convert_fill_border (convert, dest);
7227 }
7228 
7229 static GstVideoFormat
get_scale_format(GstVideoFormat format,gint plane)7230 get_scale_format (GstVideoFormat format, gint plane)
7231 {
7232   GstVideoFormat res = GST_VIDEO_FORMAT_UNKNOWN;
7233 
7234   switch (format) {
7235     case GST_VIDEO_FORMAT_I420:
7236     case GST_VIDEO_FORMAT_YV12:
7237     case GST_VIDEO_FORMAT_Y41B:
7238     case GST_VIDEO_FORMAT_Y42B:
7239     case GST_VIDEO_FORMAT_Y444:
7240     case GST_VIDEO_FORMAT_GRAY8:
7241     case GST_VIDEO_FORMAT_A420:
7242     case GST_VIDEO_FORMAT_YUV9:
7243     case GST_VIDEO_FORMAT_YVU9:
7244     case GST_VIDEO_FORMAT_GBR:
7245     case GST_VIDEO_FORMAT_GBRA:
7246     case GST_VIDEO_FORMAT_RGBP:
7247     case GST_VIDEO_FORMAT_BGRP:
7248       res = GST_VIDEO_FORMAT_GRAY8;
7249       break;
7250     case GST_VIDEO_FORMAT_GRAY16_BE:
7251     case GST_VIDEO_FORMAT_GRAY16_LE:
7252       res = GST_VIDEO_FORMAT_GRAY16_BE;
7253       break;
7254     case GST_VIDEO_FORMAT_YUY2:
7255     case GST_VIDEO_FORMAT_UYVY:
7256     case GST_VIDEO_FORMAT_VYUY:
7257     case GST_VIDEO_FORMAT_YVYU:
7258     case GST_VIDEO_FORMAT_AYUV:
7259     case GST_VIDEO_FORMAT_VUYA:
7260     case GST_VIDEO_FORMAT_RGBx:
7261     case GST_VIDEO_FORMAT_BGRx:
7262     case GST_VIDEO_FORMAT_xRGB:
7263     case GST_VIDEO_FORMAT_xBGR:
7264     case GST_VIDEO_FORMAT_RGBA:
7265     case GST_VIDEO_FORMAT_BGRA:
7266     case GST_VIDEO_FORMAT_ARGB:
7267     case GST_VIDEO_FORMAT_ABGR:
7268     case GST_VIDEO_FORMAT_RGB:
7269     case GST_VIDEO_FORMAT_BGR:
7270     case GST_VIDEO_FORMAT_v308:
7271     case GST_VIDEO_FORMAT_IYU2:
7272     case GST_VIDEO_FORMAT_ARGB64:
7273     case GST_VIDEO_FORMAT_ARGB64_LE:
7274     case GST_VIDEO_FORMAT_ARGB64_BE:
7275     case GST_VIDEO_FORMAT_RGBA64_BE:
7276     case GST_VIDEO_FORMAT_RGBA64_LE:
7277     case GST_VIDEO_FORMAT_BGRA64_BE:
7278     case GST_VIDEO_FORMAT_BGRA64_LE:
7279     case GST_VIDEO_FORMAT_ABGR64_BE:
7280     case GST_VIDEO_FORMAT_ABGR64_LE:
7281     case GST_VIDEO_FORMAT_AYUV64:
7282       res = format;
7283       break;
7284     case GST_VIDEO_FORMAT_RGB15:
7285     case GST_VIDEO_FORMAT_BGR15:
7286     case GST_VIDEO_FORMAT_RGB16:
7287     case GST_VIDEO_FORMAT_BGR16:
7288       res = GST_VIDEO_FORMAT_NV12;
7289       break;
7290     case GST_VIDEO_FORMAT_NV12:
7291     case GST_VIDEO_FORMAT_NV21:
7292     case GST_VIDEO_FORMAT_NV16:
7293     case GST_VIDEO_FORMAT_NV61:
7294     case GST_VIDEO_FORMAT_NV24:
7295       res = plane == 0 ? GST_VIDEO_FORMAT_GRAY8 : GST_VIDEO_FORMAT_NV12;
7296       break;
7297     case GST_VIDEO_FORMAT_AV12:
7298       res = (plane == 0
7299           || plane == 2) ? GST_VIDEO_FORMAT_GRAY8 : GST_VIDEO_FORMAT_NV12;
7300       break;
7301     case GST_VIDEO_FORMAT_UNKNOWN:
7302     case GST_VIDEO_FORMAT_ENCODED:
7303     case GST_VIDEO_FORMAT_v210:
7304     case GST_VIDEO_FORMAT_v216:
7305     case GST_VIDEO_FORMAT_Y210:
7306     case GST_VIDEO_FORMAT_Y410:
7307     case GST_VIDEO_FORMAT_UYVP:
7308     case GST_VIDEO_FORMAT_RGB8P:
7309     case GST_VIDEO_FORMAT_IYU1:
7310     case GST_VIDEO_FORMAT_r210:
7311     case GST_VIDEO_FORMAT_I420_10BE:
7312     case GST_VIDEO_FORMAT_I420_10LE:
7313     case GST_VIDEO_FORMAT_I422_10BE:
7314     case GST_VIDEO_FORMAT_I422_10LE:
7315     case GST_VIDEO_FORMAT_Y444_10BE:
7316     case GST_VIDEO_FORMAT_Y444_10LE:
7317     case GST_VIDEO_FORMAT_I420_12BE:
7318     case GST_VIDEO_FORMAT_I420_12LE:
7319     case GST_VIDEO_FORMAT_I422_12BE:
7320     case GST_VIDEO_FORMAT_I422_12LE:
7321     case GST_VIDEO_FORMAT_Y444_12BE:
7322     case GST_VIDEO_FORMAT_Y444_12LE:
7323     case GST_VIDEO_FORMAT_GBR_10BE:
7324     case GST_VIDEO_FORMAT_GBR_10LE:
7325     case GST_VIDEO_FORMAT_GBRA_10BE:
7326     case GST_VIDEO_FORMAT_GBRA_10LE:
7327     case GST_VIDEO_FORMAT_GBR_12BE:
7328     case GST_VIDEO_FORMAT_GBR_12LE:
7329     case GST_VIDEO_FORMAT_GBRA_12BE:
7330     case GST_VIDEO_FORMAT_GBRA_12LE:
7331     case GST_VIDEO_FORMAT_NV12_64Z32:
7332     case GST_VIDEO_FORMAT_NV12_4L4:
7333     case GST_VIDEO_FORMAT_NV12_32L32:
7334     case GST_VIDEO_FORMAT_A420_10BE:
7335     case GST_VIDEO_FORMAT_A420_10LE:
7336     case GST_VIDEO_FORMAT_A422_10BE:
7337     case GST_VIDEO_FORMAT_A422_10LE:
7338     case GST_VIDEO_FORMAT_A444_10BE:
7339     case GST_VIDEO_FORMAT_A444_10LE:
7340     case GST_VIDEO_FORMAT_P010_10BE:
7341     case GST_VIDEO_FORMAT_P010_10LE:
7342     case GST_VIDEO_FORMAT_GRAY10_LE32:
7343     case GST_VIDEO_FORMAT_NV12_10LE32:
7344     case GST_VIDEO_FORMAT_NV16_10LE32:
7345     case GST_VIDEO_FORMAT_NV12_10LE40:
7346     case GST_VIDEO_FORMAT_BGR10A2_LE:
7347     case GST_VIDEO_FORMAT_RGB10A2_LE:
7348     case GST_VIDEO_FORMAT_Y444_16BE:
7349     case GST_VIDEO_FORMAT_Y444_16LE:
7350     case GST_VIDEO_FORMAT_P016_BE:
7351     case GST_VIDEO_FORMAT_P016_LE:
7352     case GST_VIDEO_FORMAT_P012_BE:
7353     case GST_VIDEO_FORMAT_P012_LE:
7354     case GST_VIDEO_FORMAT_Y212_BE:
7355     case GST_VIDEO_FORMAT_Y212_LE:
7356     case GST_VIDEO_FORMAT_Y412_BE:
7357     case GST_VIDEO_FORMAT_Y412_LE:
7358       res = format;
7359       g_assert_not_reached ();
7360       break;
7361   }
7362   return res;
7363 }
7364 
7365 static gboolean
is_merge_yuv(GstVideoInfo * info)7366 is_merge_yuv (GstVideoInfo * info)
7367 {
7368   switch (GST_VIDEO_INFO_FORMAT (info)) {
7369     case GST_VIDEO_FORMAT_YUY2:
7370     case GST_VIDEO_FORMAT_YVYU:
7371     case GST_VIDEO_FORMAT_UYVY:
7372     case GST_VIDEO_FORMAT_VYUY:
7373       return TRUE;
7374     default:
7375       return FALSE;
7376   }
7377 }
7378 
7379 static gboolean
setup_scale(GstVideoConverter * convert)7380 setup_scale (GstVideoConverter * convert)
7381 {
7382   int i, n_planes;
7383   gint method, cr_method, in_width, in_height, out_width, out_height;
7384   guint taps;
7385   GstVideoInfo *in_info, *out_info;
7386   const GstVideoFormatInfo *in_finfo, *out_finfo;
7387   GstVideoFormat in_format, out_format;
7388   gboolean interlaced;
7389   guint n_threads = convert->conversion_runner->n_threads;
7390 
7391   in_info = &convert->in_info;
7392   out_info = &convert->out_info;
7393 
7394   in_finfo = in_info->finfo;
7395   out_finfo = out_info->finfo;
7396 
7397   n_planes = GST_VIDEO_INFO_N_PLANES (out_info);
7398 
7399   interlaced = GST_VIDEO_INFO_IS_INTERLACED (&convert->in_info)
7400       && GST_VIDEO_INFO_INTERLACE_MODE (&convert->in_info) !=
7401       GST_VIDEO_INTERLACE_MODE_ALTERNATE;
7402 
7403   method = GET_OPT_RESAMPLER_METHOD (convert);
7404   if (method == GST_VIDEO_RESAMPLER_METHOD_NEAREST)
7405     cr_method = method;
7406   else
7407     cr_method = GET_OPT_CHROMA_RESAMPLER_METHOD (convert);
7408   taps = GET_OPT_RESAMPLER_TAPS (convert);
7409 
7410   in_format = GST_VIDEO_INFO_FORMAT (in_info);
7411   out_format = GST_VIDEO_INFO_FORMAT (out_info);
7412 
7413   switch (in_format) {
7414     case GST_VIDEO_FORMAT_RGB15:
7415     case GST_VIDEO_FORMAT_RGB16:
7416     case GST_VIDEO_FORMAT_BGR15:
7417     case GST_VIDEO_FORMAT_BGR16:
7418 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
7419     case GST_VIDEO_FORMAT_GRAY16_BE:
7420 #else
7421     case GST_VIDEO_FORMAT_GRAY16_LE:
7422 #endif
7423       if (method != GST_VIDEO_RESAMPLER_METHOD_NEAREST) {
7424         GST_DEBUG ("%s only with nearest resampling",
7425             gst_video_format_to_string (in_format));
7426         return FALSE;
7427       }
7428       break;
7429     default:
7430       break;
7431   }
7432 
7433   in_width = convert->in_width;
7434   in_height = convert->in_height;
7435   out_width = convert->out_width;
7436   out_height = convert->out_height;
7437 
7438   if (n_planes == 1 && !GST_VIDEO_FORMAT_INFO_IS_GRAY (out_finfo)) {
7439     gint pstride;
7440     guint j;
7441 
7442     if (is_merge_yuv (in_info)) {
7443       GstVideoScaler *y_scaler, *uv_scaler;
7444 
7445       if (in_width != out_width) {
7446         convert->fh_scaler[0].scaler = g_new (GstVideoScaler *, n_threads);
7447         for (j = 0; j < n_threads; j++) {
7448           y_scaler =
7449               gst_video_scaler_new (method, GST_VIDEO_SCALER_FLAG_NONE, taps,
7450               GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (in_finfo, GST_VIDEO_COMP_Y,
7451                   in_width), GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (out_finfo,
7452                   GST_VIDEO_COMP_Y, out_width), convert->config);
7453           uv_scaler =
7454               gst_video_scaler_new (method, GST_VIDEO_SCALER_FLAG_NONE,
7455               gst_video_scaler_get_max_taps (y_scaler),
7456               GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (in_finfo, GST_VIDEO_COMP_U,
7457                   in_width), GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (out_finfo,
7458                   GST_VIDEO_COMP_U, out_width), convert->config);
7459 
7460           convert->fh_scaler[0].scaler[j] =
7461               gst_video_scaler_combine_packed_YUV (y_scaler, uv_scaler,
7462               in_format, out_format);
7463 
7464           gst_video_scaler_free (y_scaler);
7465           gst_video_scaler_free (uv_scaler);
7466         }
7467       } else {
7468         convert->fh_scaler[0].scaler = NULL;
7469       }
7470 
7471       pstride = GST_VIDEO_FORMAT_INFO_PSTRIDE (out_finfo, GST_VIDEO_COMP_Y);
7472       convert->fin_x[0] = GST_ROUND_UP_2 (convert->in_x) * pstride;
7473       convert->fout_x[0] = GST_ROUND_UP_2 (convert->out_x) * pstride;
7474 
7475     } else {
7476       if (in_width != out_width && in_width != 0 && out_width != 0) {
7477         convert->fh_scaler[0].scaler = g_new (GstVideoScaler *, n_threads);
7478         for (j = 0; j < n_threads; j++) {
7479           convert->fh_scaler[0].scaler[j] =
7480               gst_video_scaler_new (method, GST_VIDEO_SCALER_FLAG_NONE, taps,
7481               in_width, out_width, convert->config);
7482         }
7483       } else {
7484         convert->fh_scaler[0].scaler = NULL;
7485       }
7486 
7487       pstride = GST_VIDEO_FORMAT_INFO_PSTRIDE (out_finfo, GST_VIDEO_COMP_R);
7488       convert->fin_x[0] = convert->in_x * pstride;
7489       convert->fout_x[0] = convert->out_x * pstride;
7490     }
7491 
7492     if (in_height != out_height && in_height != 0 && out_height != 0) {
7493       convert->fv_scaler[0].scaler = g_new (GstVideoScaler *, n_threads);
7494 
7495       for (j = 0; j < n_threads; j++) {
7496         convert->fv_scaler[0].scaler[j] =
7497             gst_video_scaler_new (method,
7498             interlaced ?
7499             GST_VIDEO_SCALER_FLAG_INTERLACED : GST_VIDEO_SCALER_FLAG_NONE, taps,
7500             in_height, out_height, convert->config);
7501       }
7502     } else {
7503       convert->fv_scaler[0].scaler = NULL;
7504     }
7505 
7506     convert->fin_y[0] = convert->in_y;
7507     convert->fout_y[0] = convert->out_y;
7508     convert->fout_width[0] = out_width;
7509     convert->fout_height[0] = out_height;
7510     convert->fconvert[0] = convert_plane_hv;
7511     convert->fformat[0] = get_scale_format (in_format, 0);
7512     convert->fsplane[0] = 0;
7513   } else {
7514     for (i = 0; i < n_planes; i++) {
7515       gint out_comp[GST_VIDEO_MAX_COMPONENTS];
7516       gint comp, j, iw, ih, ow, oh, pstride;
7517       gboolean need_v_scaler, need_h_scaler;
7518       GstStructure *config;
7519       gint resample_method;
7520 
7521       gst_video_format_info_component (out_finfo, i, out_comp);
7522       ow = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (out_finfo, out_comp[0],
7523           out_width);
7524       oh = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (out_finfo, out_comp[0],
7525           out_height);
7526       pstride = GST_VIDEO_FORMAT_INFO_PSTRIDE (out_finfo, out_comp[0]);
7527 
7528       /* find the component in this plane and map it to the plane of
7529        * the source */
7530       if (out_comp[0] < GST_VIDEO_FORMAT_INFO_N_COMPONENTS (in_finfo)) {
7531         comp = out_comp[0];
7532         iw = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (in_finfo, comp, in_width);
7533         ih = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (in_finfo, comp, in_height);
7534         convert->fin_x[i] = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (in_finfo, comp,
7535             convert->in_x);
7536         convert->fin_x[i] *= pstride;
7537         convert->fin_y[i] = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (in_finfo, comp,
7538             convert->in_y);
7539       } else {
7540         /* we will use a fill instead, setting the parameters to an invalid
7541          * size to reduce confusion */
7542         comp = -1;
7543         iw = ih = -1;
7544         convert->fin_x[i] = -1;
7545         convert->fin_y[i] = -1;
7546       }
7547 
7548       convert->fout_width[i] = ow;
7549       convert->fout_height[i] = oh;
7550 
7551       convert->fout_x[i] = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (out_finfo,
7552           out_comp[0], convert->out_x);
7553       convert->fout_x[i] *= pstride;
7554       convert->fout_y[i] = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (out_finfo,
7555           out_comp[0], convert->out_y);
7556 
7557       GST_DEBUG ("plane %d: %dx%d -> %dx%d", i, iw, ih, ow, oh);
7558       GST_DEBUG ("plane %d: pstride %d", i, pstride);
7559       GST_DEBUG ("plane %d: in_x %d, in_y %d", i, convert->fin_x[i],
7560           convert->fin_y[i]);
7561       GST_DEBUG ("plane %d: out_x %d, out_y %d", i, convert->fout_x[i],
7562           convert->fout_y[i]);
7563 
7564       if (comp == -1) {
7565         convert->fconvert[i] = convert_plane_fill;
7566         if (GST_VIDEO_INFO_IS_YUV (out_info)) {
7567           if (i == 3)
7568             convert->ffill[i] = convert->alpha_value;
7569           if (i == 0)
7570             convert->ffill[i] = 0x00;
7571           else
7572             convert->ffill[i] = 0x80;
7573         } else {
7574           if (i == 3)
7575             convert->ffill[i] = convert->alpha_value;
7576           else
7577             convert->ffill[i] = 0x00;
7578         }
7579         GST_DEBUG ("plane %d fill %02x", i, convert->ffill[i]);
7580         continue;
7581       } else {
7582         convert->fsplane[i] = GST_VIDEO_FORMAT_INFO_PLANE (in_finfo, comp);
7583         GST_DEBUG ("plane %d -> %d (comp %d)", i, convert->fsplane[i], comp);
7584       }
7585 
7586       config = gst_structure_copy (convert->config);
7587 
7588       resample_method = (i == 0 ? method : cr_method);
7589 
7590       need_v_scaler = FALSE;
7591       need_h_scaler = FALSE;
7592       if (iw == ow) {
7593         if (!interlaced && ih == oh) {
7594           convert->fconvert[i] = convert_plane_hv;
7595           GST_DEBUG ("plane %d: copy", i);
7596         } else if (!interlaced && ih == 2 * oh && pstride == 1
7597             && resample_method == GST_VIDEO_RESAMPLER_METHOD_LINEAR) {
7598           convert->fconvert[i] = convert_plane_v_halve;
7599           GST_DEBUG ("plane %d: vertical halve", i);
7600         } else if (!interlaced && 2 * ih == oh && pstride == 1
7601             && resample_method == GST_VIDEO_RESAMPLER_METHOD_NEAREST) {
7602           convert->fconvert[i] = convert_plane_v_double;
7603           GST_DEBUG ("plane %d: vertical double", i);
7604         } else {
7605           convert->fconvert[i] = convert_plane_hv;
7606           GST_DEBUG ("plane %d: vertical scale", i);
7607           need_v_scaler = TRUE;
7608         }
7609       } else if (ih == oh) {
7610         if (!interlaced && iw == 2 * ow && pstride == 1
7611             && resample_method == GST_VIDEO_RESAMPLER_METHOD_LINEAR) {
7612           convert->fconvert[i] = convert_plane_h_halve;
7613           GST_DEBUG ("plane %d: horizontal halve", i);
7614         } else if (!interlaced && 2 * iw == ow && pstride == 1
7615             && resample_method == GST_VIDEO_RESAMPLER_METHOD_NEAREST) {
7616           convert->fconvert[i] = convert_plane_h_double;
7617           GST_DEBUG ("plane %d: horizontal double", i);
7618         } else {
7619           convert->fconvert[i] = convert_plane_hv;
7620           GST_DEBUG ("plane %d: horizontal scale", i);
7621           need_h_scaler = TRUE;
7622         }
7623       } else {
7624         if (!interlaced && iw == 2 * ow && ih == 2 * oh && pstride == 1
7625             && resample_method == GST_VIDEO_RESAMPLER_METHOD_LINEAR) {
7626           convert->fconvert[i] = convert_plane_hv_halve;
7627           GST_DEBUG ("plane %d: horizontal/vertical halve", i);
7628         } else if (!interlaced && 2 * iw == ow && 2 * ih == oh && pstride == 1
7629             && resample_method == GST_VIDEO_RESAMPLER_METHOD_NEAREST) {
7630           convert->fconvert[i] = convert_plane_hv_double;
7631           GST_DEBUG ("plane %d: horizontal/vertical double", i);
7632         } else {
7633           convert->fconvert[i] = convert_plane_hv;
7634           GST_DEBUG ("plane %d: horizontal/vertical scale", i);
7635           need_v_scaler = TRUE;
7636           need_h_scaler = TRUE;
7637         }
7638       }
7639 
7640       if (need_h_scaler && iw != 0 && ow != 0) {
7641         convert->fh_scaler[i].scaler = g_new (GstVideoScaler *, n_threads);
7642 
7643         for (j = 0; j < n_threads; j++) {
7644           convert->fh_scaler[i].scaler[j] =
7645               gst_video_scaler_new (resample_method, GST_VIDEO_SCALER_FLAG_NONE,
7646               taps, iw, ow, config);
7647         }
7648       } else {
7649         convert->fh_scaler[i].scaler = NULL;
7650       }
7651 
7652       if (need_v_scaler && ih != 0 && oh != 0) {
7653         convert->fv_scaler[i].scaler = g_new (GstVideoScaler *, n_threads);
7654 
7655         for (j = 0; j < n_threads; j++) {
7656           convert->fv_scaler[i].scaler[j] =
7657               gst_video_scaler_new (resample_method,
7658               interlaced ?
7659               GST_VIDEO_SCALER_FLAG_INTERLACED : GST_VIDEO_SCALER_FLAG_NONE,
7660               taps, ih, oh, config);
7661         }
7662       } else {
7663         convert->fv_scaler[i].scaler = NULL;
7664       }
7665 
7666       gst_structure_free (config);
7667       convert->fformat[i] = get_scale_format (in_format, i);
7668     }
7669   }
7670 
7671   return TRUE;
7672 }
7673 
7674 /* Fast paths */
7675 
7676 typedef struct
7677 {
7678   GstVideoFormat in_format;
7679   GstVideoFormat out_format;
7680   gboolean keeps_interlaced;
7681   gboolean needs_color_matrix;
7682   gboolean keeps_size;
7683   gboolean do_crop;
7684   gboolean do_border;
7685   gboolean alpha_copy;
7686   gboolean alpha_set;
7687   gboolean alpha_mult;
7688   gint width_align, height_align;
7689   void (*convert) (GstVideoConverter * convert, const GstVideoFrame * src,
7690       GstVideoFrame * dest);
7691 } VideoTransform;
7692 
7693 static const VideoTransform transforms[] = {
7694   /* planar -> packed */
7695   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_YUY2, TRUE, FALSE, TRUE, FALSE,
7696       FALSE, FALSE, FALSE, FALSE, 0, 0, convert_I420_YUY2},
7697   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_UYVY, TRUE, FALSE, TRUE, FALSE,
7698       FALSE, FALSE, FALSE, FALSE, 0, 0, convert_I420_UYVY},
7699   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_AYUV, TRUE, FALSE, TRUE, FALSE,
7700       FALSE, FALSE, TRUE, FALSE, 0, 0, convert_I420_AYUV},
7701   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_v210, TRUE, FALSE, TRUE, FALSE,
7702       FALSE, FALSE, FALSE, FALSE, 0, 0, convert_I420_v210},
7703 
7704   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_YUY2, TRUE, FALSE, TRUE, FALSE,
7705       FALSE, FALSE, FALSE, FALSE, 0, 0, convert_I420_YUY2},
7706   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_UYVY, TRUE, FALSE, TRUE, FALSE,
7707       FALSE, FALSE, FALSE, FALSE, 0, 0, convert_I420_UYVY},
7708   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_AYUV, TRUE, FALSE, TRUE, FALSE,
7709       FALSE, FALSE, TRUE, FALSE, 0, 0, convert_I420_AYUV},
7710   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_v210, TRUE, FALSE, TRUE, FALSE,
7711       FALSE, FALSE, FALSE, FALSE, 0, 0, convert_I420_v210},
7712 
7713   {GST_VIDEO_FORMAT_Y42B, GST_VIDEO_FORMAT_YUY2, TRUE, FALSE, TRUE, TRUE,
7714       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_Y42B_YUY2},
7715   {GST_VIDEO_FORMAT_Y42B, GST_VIDEO_FORMAT_UYVY, TRUE, FALSE, TRUE, TRUE,
7716       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_Y42B_UYVY},
7717   {GST_VIDEO_FORMAT_Y42B, GST_VIDEO_FORMAT_AYUV, TRUE, FALSE, TRUE, TRUE,
7718       TRUE, FALSE, TRUE, FALSE, 1, 0, convert_Y42B_AYUV},
7719   {GST_VIDEO_FORMAT_Y42B, GST_VIDEO_FORMAT_v210, TRUE, FALSE, TRUE, TRUE,
7720       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_Y42B_v210},
7721 
7722   {GST_VIDEO_FORMAT_Y444, GST_VIDEO_FORMAT_YUY2, TRUE, FALSE, TRUE, TRUE,
7723       TRUE, FALSE, FALSE, FALSE, 1, 0, convert_Y444_YUY2},
7724   {GST_VIDEO_FORMAT_Y444, GST_VIDEO_FORMAT_UYVY, TRUE, FALSE, TRUE, TRUE,
7725       TRUE, FALSE, FALSE, FALSE, 1, 0, convert_Y444_UYVY},
7726   {GST_VIDEO_FORMAT_Y444, GST_VIDEO_FORMAT_AYUV, TRUE, FALSE, TRUE, TRUE,
7727       TRUE, FALSE, TRUE, FALSE, 0, 0, convert_Y444_AYUV},
7728 
7729   /* packed -> packed */
7730   {GST_VIDEO_FORMAT_YUY2, GST_VIDEO_FORMAT_YUY2, TRUE, FALSE, FALSE, TRUE,
7731       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7732   {GST_VIDEO_FORMAT_YUY2, GST_VIDEO_FORMAT_UYVY, TRUE, FALSE, TRUE, TRUE,
7733       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_UYVY_YUY2},      /* alias */
7734   {GST_VIDEO_FORMAT_YUY2, GST_VIDEO_FORMAT_AYUV, TRUE, FALSE, TRUE, TRUE,
7735       TRUE, FALSE, TRUE, FALSE, 1, 0, convert_YUY2_AYUV},
7736   {GST_VIDEO_FORMAT_YUY2, GST_VIDEO_FORMAT_v210, TRUE, FALSE, TRUE, FALSE,
7737       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_YUY2_v210},
7738 
7739   {GST_VIDEO_FORMAT_UYVY, GST_VIDEO_FORMAT_UYVY, TRUE, FALSE, FALSE, TRUE,
7740       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7741   {GST_VIDEO_FORMAT_UYVY, GST_VIDEO_FORMAT_YUY2, TRUE, FALSE, TRUE, TRUE,
7742       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_UYVY_YUY2},
7743   {GST_VIDEO_FORMAT_UYVY, GST_VIDEO_FORMAT_AYUV, TRUE, FALSE, TRUE, TRUE,
7744       TRUE, FALSE, TRUE, FALSE, 0, 0, convert_UYVY_AYUV},
7745   {GST_VIDEO_FORMAT_UYVY, GST_VIDEO_FORMAT_v210, TRUE, FALSE, TRUE, FALSE,
7746       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_UYVY_v210},
7747 
7748   {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_FORMAT_AYUV, TRUE, FALSE, FALSE, TRUE, TRUE,
7749       TRUE, FALSE, FALSE, 0, 0, convert_scale_planes},
7750   {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_FORMAT_YUY2, TRUE, FALSE, TRUE, TRUE,
7751       TRUE, FALSE, FALSE, FALSE, 1, 0, convert_AYUV_YUY2},
7752   {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_FORMAT_UYVY, TRUE, FALSE, TRUE, TRUE,
7753       TRUE, FALSE, FALSE, FALSE, 1, 0, convert_AYUV_UYVY},
7754 
7755   {GST_VIDEO_FORMAT_v210, GST_VIDEO_FORMAT_UYVY, TRUE, FALSE, TRUE, FALSE,
7756       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_v210_UYVY},
7757   {GST_VIDEO_FORMAT_v210, GST_VIDEO_FORMAT_YUY2, TRUE, FALSE, TRUE, FALSE,
7758       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_v210_YUY2},
7759 
7760   /* packed -> planar */
7761   {GST_VIDEO_FORMAT_YUY2, GST_VIDEO_FORMAT_I420, TRUE, FALSE, TRUE, FALSE,
7762       FALSE, FALSE, FALSE, FALSE, 0, 0, convert_YUY2_I420},
7763   {GST_VIDEO_FORMAT_YUY2, GST_VIDEO_FORMAT_YV12, TRUE, FALSE, TRUE, FALSE,
7764       FALSE, FALSE, FALSE, FALSE, 0, 0, convert_YUY2_I420},
7765   {GST_VIDEO_FORMAT_YUY2, GST_VIDEO_FORMAT_Y42B, TRUE, FALSE, TRUE, TRUE,
7766       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_YUY2_Y42B},
7767   {GST_VIDEO_FORMAT_YUY2, GST_VIDEO_FORMAT_Y444, TRUE, FALSE, TRUE, TRUE,
7768       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_YUY2_Y444},
7769   {GST_VIDEO_FORMAT_UYVY, GST_VIDEO_FORMAT_GRAY8, TRUE, TRUE, TRUE, TRUE,
7770       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_UYVY_GRAY8},
7771 
7772   {GST_VIDEO_FORMAT_UYVY, GST_VIDEO_FORMAT_I420, TRUE, FALSE, TRUE, FALSE,
7773       FALSE, FALSE, FALSE, FALSE, 0, 0, convert_UYVY_I420},
7774   {GST_VIDEO_FORMAT_UYVY, GST_VIDEO_FORMAT_YV12, TRUE, FALSE, TRUE, FALSE,
7775       FALSE, FALSE, FALSE, FALSE, 0, 0, convert_UYVY_I420},
7776   {GST_VIDEO_FORMAT_UYVY, GST_VIDEO_FORMAT_Y42B, TRUE, FALSE, TRUE, TRUE,
7777       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_UYVY_Y42B},
7778   {GST_VIDEO_FORMAT_UYVY, GST_VIDEO_FORMAT_Y444, TRUE, FALSE, TRUE, TRUE,
7779       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_UYVY_Y444},
7780 
7781   {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_FORMAT_I420, FALSE, FALSE, TRUE, TRUE,
7782       TRUE, FALSE, FALSE, FALSE, 1, 1, convert_AYUV_I420},
7783   {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_FORMAT_YV12, FALSE, FALSE, TRUE, TRUE,
7784       TRUE, FALSE, FALSE, FALSE, 1, 1, convert_AYUV_I420},
7785   {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_FORMAT_Y42B, TRUE, FALSE, TRUE, TRUE,
7786       TRUE, FALSE, FALSE, FALSE, 1, 0, convert_AYUV_Y42B},
7787   {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_FORMAT_Y444, TRUE, FALSE, TRUE, TRUE,
7788       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_AYUV_Y444},
7789 
7790   {GST_VIDEO_FORMAT_v210, GST_VIDEO_FORMAT_I420, TRUE, FALSE, TRUE, FALSE,
7791       FALSE, FALSE, FALSE, FALSE, 0, 0, convert_v210_I420},
7792   {GST_VIDEO_FORMAT_v210, GST_VIDEO_FORMAT_YV12, TRUE, FALSE, TRUE, FALSE,
7793       FALSE, FALSE, FALSE, FALSE, 0, 0, convert_v210_I420},
7794   {GST_VIDEO_FORMAT_v210, GST_VIDEO_FORMAT_Y42B, TRUE, FALSE, TRUE, FALSE,
7795       FALSE, FALSE, FALSE, FALSE, 0, 0, convert_v210_Y42B},
7796 
7797   /* planar -> planar */
7798   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_I420, TRUE, FALSE, FALSE, TRUE,
7799       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7800   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_YV12, TRUE, FALSE, FALSE, TRUE,
7801       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7802   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_Y41B, FALSE, FALSE, FALSE, TRUE,
7803       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7804   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_Y42B, FALSE, FALSE, FALSE, TRUE,
7805       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7806   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_Y444, FALSE, FALSE, FALSE, TRUE,
7807       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7808   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_GRAY8, FALSE, FALSE, FALSE, TRUE,
7809       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7810   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_A420, FALSE, FALSE, FALSE, TRUE,
7811       TRUE, FALSE, TRUE, FALSE, 0, 0, convert_scale_planes},
7812   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_YUV9, FALSE, FALSE, FALSE, TRUE,
7813       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7814   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_YVU9, FALSE, FALSE, FALSE, TRUE,
7815       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7816 
7817   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_I420, TRUE, FALSE, FALSE, TRUE,
7818       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7819   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_YV12, TRUE, FALSE, FALSE, TRUE,
7820       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7821   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_Y41B, FALSE, FALSE, FALSE, TRUE,
7822       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7823   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_Y42B, FALSE, FALSE, FALSE, TRUE,
7824       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7825   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_Y444, FALSE, FALSE, FALSE, TRUE,
7826       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7827   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_GRAY8, FALSE, FALSE, FALSE, TRUE,
7828       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7829   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_A420, FALSE, FALSE, FALSE, TRUE,
7830       TRUE, FALSE, TRUE, FALSE, 0, 0, convert_scale_planes},
7831   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_YUV9, FALSE, FALSE, FALSE, TRUE,
7832       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7833   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_YVU9, FALSE, FALSE, FALSE, TRUE,
7834       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7835 
7836   {GST_VIDEO_FORMAT_Y41B, GST_VIDEO_FORMAT_I420, FALSE, FALSE, FALSE, TRUE,
7837       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7838   {GST_VIDEO_FORMAT_Y41B, GST_VIDEO_FORMAT_YV12, FALSE, FALSE, FALSE, TRUE,
7839       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7840   {GST_VIDEO_FORMAT_Y41B, GST_VIDEO_FORMAT_Y41B, TRUE, FALSE, FALSE, TRUE,
7841       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7842   {GST_VIDEO_FORMAT_Y41B, GST_VIDEO_FORMAT_Y42B, FALSE, FALSE, FALSE, TRUE,
7843       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7844   {GST_VIDEO_FORMAT_Y41B, GST_VIDEO_FORMAT_Y444, FALSE, FALSE, FALSE, TRUE,
7845       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7846   {GST_VIDEO_FORMAT_Y41B, GST_VIDEO_FORMAT_GRAY8, FALSE, FALSE, FALSE, TRUE,
7847       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7848   {GST_VIDEO_FORMAT_Y41B, GST_VIDEO_FORMAT_A420, FALSE, FALSE, FALSE, TRUE,
7849       TRUE, FALSE, TRUE, FALSE, 0, 0, convert_scale_planes},
7850   {GST_VIDEO_FORMAT_Y41B, GST_VIDEO_FORMAT_YUV9, FALSE, FALSE, FALSE, TRUE,
7851       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7852   {GST_VIDEO_FORMAT_Y41B, GST_VIDEO_FORMAT_YVU9, FALSE, FALSE, FALSE, TRUE,
7853       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7854 
7855   {GST_VIDEO_FORMAT_Y42B, GST_VIDEO_FORMAT_I420, FALSE, FALSE, FALSE, TRUE,
7856       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7857   {GST_VIDEO_FORMAT_Y42B, GST_VIDEO_FORMAT_YV12, FALSE, FALSE, FALSE, TRUE,
7858       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7859   {GST_VIDEO_FORMAT_Y42B, GST_VIDEO_FORMAT_Y41B, FALSE, FALSE, FALSE, TRUE,
7860       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7861   {GST_VIDEO_FORMAT_Y42B, GST_VIDEO_FORMAT_Y42B, TRUE, FALSE, FALSE, TRUE,
7862       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7863   {GST_VIDEO_FORMAT_Y42B, GST_VIDEO_FORMAT_Y444, FALSE, FALSE, FALSE, TRUE,
7864       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7865   {GST_VIDEO_FORMAT_Y42B, GST_VIDEO_FORMAT_GRAY8, FALSE, FALSE, FALSE, TRUE,
7866       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7867   {GST_VIDEO_FORMAT_Y42B, GST_VIDEO_FORMAT_A420, FALSE, FALSE, FALSE, TRUE,
7868       TRUE, FALSE, TRUE, FALSE, 0, 0, convert_scale_planes},
7869   {GST_VIDEO_FORMAT_Y42B, GST_VIDEO_FORMAT_YUV9, FALSE, FALSE, FALSE, TRUE,
7870       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7871   {GST_VIDEO_FORMAT_Y42B, GST_VIDEO_FORMAT_YVU9, FALSE, FALSE, FALSE, TRUE,
7872       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7873 
7874   {GST_VIDEO_FORMAT_Y444, GST_VIDEO_FORMAT_I420, FALSE, FALSE, FALSE, TRUE,
7875       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7876   {GST_VIDEO_FORMAT_Y444, GST_VIDEO_FORMAT_YV12, FALSE, FALSE, FALSE, TRUE,
7877       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7878   {GST_VIDEO_FORMAT_Y444, GST_VIDEO_FORMAT_Y41B, FALSE, FALSE, FALSE, TRUE,
7879       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7880   {GST_VIDEO_FORMAT_Y444, GST_VIDEO_FORMAT_Y42B, FALSE, FALSE, FALSE, TRUE,
7881       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7882   {GST_VIDEO_FORMAT_Y444, GST_VIDEO_FORMAT_Y444, TRUE, FALSE, FALSE, TRUE,
7883       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7884   {GST_VIDEO_FORMAT_Y444, GST_VIDEO_FORMAT_GRAY8, FALSE, FALSE, FALSE, TRUE,
7885       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7886   {GST_VIDEO_FORMAT_Y444, GST_VIDEO_FORMAT_A420, FALSE, FALSE, FALSE, TRUE,
7887       TRUE, FALSE, TRUE, FALSE, 0, 0, convert_scale_planes},
7888   {GST_VIDEO_FORMAT_Y444, GST_VIDEO_FORMAT_YUV9, FALSE, FALSE, FALSE, TRUE,
7889       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7890   {GST_VIDEO_FORMAT_Y444, GST_VIDEO_FORMAT_YVU9, FALSE, FALSE, FALSE, TRUE,
7891       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7892 
7893   {GST_VIDEO_FORMAT_GRAY8, GST_VIDEO_FORMAT_I420, FALSE, FALSE, FALSE, TRUE,
7894       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7895   {GST_VIDEO_FORMAT_GRAY8, GST_VIDEO_FORMAT_YV12, FALSE, FALSE, FALSE, TRUE,
7896       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7897   {GST_VIDEO_FORMAT_GRAY8, GST_VIDEO_FORMAT_Y41B, FALSE, FALSE, FALSE, TRUE,
7898       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7899   {GST_VIDEO_FORMAT_GRAY8, GST_VIDEO_FORMAT_Y42B, FALSE, FALSE, FALSE, TRUE,
7900       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7901   {GST_VIDEO_FORMAT_GRAY8, GST_VIDEO_FORMAT_Y444, FALSE, FALSE, FALSE, TRUE,
7902       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7903   {GST_VIDEO_FORMAT_GRAY8, GST_VIDEO_FORMAT_GRAY8, TRUE, FALSE, FALSE, TRUE,
7904       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7905   {GST_VIDEO_FORMAT_GRAY8, GST_VIDEO_FORMAT_A420, FALSE, FALSE, FALSE, TRUE,
7906       TRUE, FALSE, TRUE, FALSE, 0, 0, convert_scale_planes},
7907   {GST_VIDEO_FORMAT_GRAY8, GST_VIDEO_FORMAT_YUV9, FALSE, FALSE, FALSE, TRUE,
7908       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7909   {GST_VIDEO_FORMAT_GRAY8, GST_VIDEO_FORMAT_YVU9, FALSE, FALSE, FALSE, TRUE,
7910       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7911 
7912   {GST_VIDEO_FORMAT_A420, GST_VIDEO_FORMAT_I420, FALSE, FALSE, FALSE, TRUE,
7913       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7914   {GST_VIDEO_FORMAT_A420, GST_VIDEO_FORMAT_YV12, FALSE, FALSE, FALSE, TRUE,
7915       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7916   {GST_VIDEO_FORMAT_A420, GST_VIDEO_FORMAT_Y41B, FALSE, FALSE, FALSE, TRUE,
7917       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7918   {GST_VIDEO_FORMAT_A420, GST_VIDEO_FORMAT_Y42B, FALSE, FALSE, FALSE, TRUE,
7919       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7920   {GST_VIDEO_FORMAT_A420, GST_VIDEO_FORMAT_Y444, FALSE, FALSE, FALSE, TRUE,
7921       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7922   {GST_VIDEO_FORMAT_A420, GST_VIDEO_FORMAT_GRAY8, FALSE, FALSE, FALSE, TRUE,
7923       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7924   {GST_VIDEO_FORMAT_A420, GST_VIDEO_FORMAT_A420, TRUE, FALSE, FALSE, TRUE,
7925       TRUE, TRUE, FALSE, FALSE, 0, 0, convert_scale_planes},
7926   {GST_VIDEO_FORMAT_A420, GST_VIDEO_FORMAT_YUV9, FALSE, FALSE, FALSE, TRUE,
7927       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7928   {GST_VIDEO_FORMAT_A420, GST_VIDEO_FORMAT_YVU9, FALSE, FALSE, FALSE, TRUE,
7929       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7930 
7931   {GST_VIDEO_FORMAT_YUV9, GST_VIDEO_FORMAT_I420, FALSE, FALSE, FALSE, TRUE,
7932       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7933   {GST_VIDEO_FORMAT_YUV9, GST_VIDEO_FORMAT_YV12, FALSE, FALSE, FALSE, TRUE,
7934       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7935   {GST_VIDEO_FORMAT_YUV9, GST_VIDEO_FORMAT_Y41B, FALSE, FALSE, FALSE, TRUE,
7936       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7937   {GST_VIDEO_FORMAT_YUV9, GST_VIDEO_FORMAT_Y42B, FALSE, FALSE, FALSE, TRUE,
7938       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7939   {GST_VIDEO_FORMAT_YUV9, GST_VIDEO_FORMAT_Y444, FALSE, FALSE, FALSE, TRUE,
7940       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7941   {GST_VIDEO_FORMAT_YUV9, GST_VIDEO_FORMAT_GRAY8, FALSE, FALSE, FALSE, TRUE,
7942       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7943   {GST_VIDEO_FORMAT_YUV9, GST_VIDEO_FORMAT_A420, FALSE, FALSE, FALSE, TRUE,
7944       TRUE, FALSE, TRUE, FALSE, 0, 0, convert_scale_planes},
7945   {GST_VIDEO_FORMAT_YUV9, GST_VIDEO_FORMAT_YUV9, TRUE, FALSE, FALSE, TRUE,
7946       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7947   {GST_VIDEO_FORMAT_YUV9, GST_VIDEO_FORMAT_YVU9, TRUE, FALSE, FALSE, TRUE,
7948       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7949 
7950   {GST_VIDEO_FORMAT_YVU9, GST_VIDEO_FORMAT_I420, FALSE, FALSE, FALSE, TRUE,
7951       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7952   {GST_VIDEO_FORMAT_YVU9, GST_VIDEO_FORMAT_YV12, FALSE, FALSE, FALSE, TRUE,
7953       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7954   {GST_VIDEO_FORMAT_YVU9, GST_VIDEO_FORMAT_Y41B, FALSE, FALSE, FALSE, TRUE,
7955       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7956   {GST_VIDEO_FORMAT_YVU9, GST_VIDEO_FORMAT_Y42B, FALSE, FALSE, FALSE, TRUE,
7957       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7958   {GST_VIDEO_FORMAT_YVU9, GST_VIDEO_FORMAT_Y444, FALSE, FALSE, FALSE, TRUE,
7959       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7960   {GST_VIDEO_FORMAT_YVU9, GST_VIDEO_FORMAT_GRAY8, FALSE, FALSE, FALSE, TRUE,
7961       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7962   {GST_VIDEO_FORMAT_YVU9, GST_VIDEO_FORMAT_A420, FALSE, FALSE, FALSE, TRUE,
7963       TRUE, FALSE, TRUE, FALSE, 0, 0, convert_scale_planes},
7964   {GST_VIDEO_FORMAT_YVU9, GST_VIDEO_FORMAT_YUV9, TRUE, FALSE, FALSE, TRUE,
7965       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7966   {GST_VIDEO_FORMAT_YVU9, GST_VIDEO_FORMAT_YVU9, TRUE, FALSE, FALSE, TRUE,
7967       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7968 
7969   /* sempiplanar -> semiplanar */
7970   {GST_VIDEO_FORMAT_NV12, GST_VIDEO_FORMAT_NV12, TRUE, FALSE, FALSE, TRUE,
7971       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7972   {GST_VIDEO_FORMAT_NV12, GST_VIDEO_FORMAT_NV16, TRUE, FALSE, FALSE, TRUE,
7973       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7974   {GST_VIDEO_FORMAT_NV12, GST_VIDEO_FORMAT_NV24, TRUE, FALSE, FALSE, TRUE,
7975       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7976 
7977   {GST_VIDEO_FORMAT_NV21, GST_VIDEO_FORMAT_NV21, TRUE, FALSE, FALSE, TRUE,
7978       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7979 
7980   {GST_VIDEO_FORMAT_NV16, GST_VIDEO_FORMAT_NV12, TRUE, FALSE, FALSE, TRUE,
7981       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7982   {GST_VIDEO_FORMAT_NV16, GST_VIDEO_FORMAT_NV16, TRUE, FALSE, FALSE, TRUE,
7983       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7984   {GST_VIDEO_FORMAT_NV16, GST_VIDEO_FORMAT_NV24, TRUE, FALSE, FALSE, TRUE,
7985       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7986 
7987   {GST_VIDEO_FORMAT_NV61, GST_VIDEO_FORMAT_NV61, TRUE, FALSE, FALSE, TRUE,
7988       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7989 
7990   {GST_VIDEO_FORMAT_NV24, GST_VIDEO_FORMAT_NV12, TRUE, FALSE, FALSE, TRUE,
7991       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7992   {GST_VIDEO_FORMAT_NV24, GST_VIDEO_FORMAT_NV16, TRUE, FALSE, FALSE, TRUE,
7993       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7994   {GST_VIDEO_FORMAT_NV24, GST_VIDEO_FORMAT_NV24, TRUE, FALSE, FALSE, TRUE,
7995       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
7996 
7997 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
7998   {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_FORMAT_ARGB, TRUE, TRUE, TRUE, TRUE, TRUE,
7999       TRUE, FALSE, FALSE, 0, 0, convert_AYUV_ARGB},
8000   {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_FORMAT_BGRA, TRUE, TRUE, TRUE, TRUE, TRUE,
8001       TRUE, FALSE, FALSE, 0, 0, convert_AYUV_BGRA},
8002   {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_FORMAT_xRGB, TRUE, TRUE, TRUE, TRUE, TRUE,
8003       FALSE, FALSE, FALSE, 0, 0, convert_AYUV_ARGB},    /* alias */
8004   {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_FORMAT_BGRx, TRUE, TRUE, TRUE, TRUE, TRUE,
8005       FALSE, FALSE, FALSE, 0, 0, convert_AYUV_BGRA},    /* alias */
8006   {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_FORMAT_ABGR, TRUE, TRUE, TRUE, TRUE, TRUE,
8007       TRUE, FALSE, FALSE, 0, 0, convert_AYUV_ABGR},
8008   {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_FORMAT_RGBA, TRUE, TRUE, TRUE, TRUE, TRUE,
8009       TRUE, FALSE, FALSE, 0, 0, convert_AYUV_RGBA},
8010   {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_FORMAT_xBGR, TRUE, TRUE, TRUE, TRUE, TRUE,
8011       FALSE, FALSE, FALSE, 0, 0, convert_AYUV_ABGR},    /* alias */
8012   {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_FORMAT_RGBx, TRUE, TRUE, TRUE, TRUE, TRUE,
8013       FALSE, FALSE, FALSE, 0, 0, convert_AYUV_RGBA},    /* alias */
8014 #endif
8015 
8016   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_BGRA, FALSE, TRUE, TRUE, TRUE,
8017       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_BGRA},
8018   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_BGRx, FALSE, TRUE, TRUE, TRUE,
8019       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_BGRA},
8020   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_BGRA, FALSE, TRUE, TRUE, TRUE,
8021       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_BGRA},
8022   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_BGRx, FALSE, TRUE, TRUE, TRUE,
8023       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_BGRA},
8024 
8025   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_ARGB, FALSE, TRUE, TRUE, TRUE,
8026       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_ARGB},
8027   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_xRGB, FALSE, TRUE, TRUE, TRUE,
8028       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_ARGB},
8029   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_ARGB, FALSE, TRUE, TRUE, TRUE,
8030       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_ARGB},
8031   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_xRGB, FALSE, TRUE, TRUE, TRUE,
8032       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_ARGB},
8033 
8034   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_ABGR, FALSE, TRUE, TRUE, TRUE,
8035       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
8036   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_xBGR, FALSE, TRUE, TRUE, TRUE,
8037       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
8038   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_RGBA, FALSE, TRUE, TRUE, TRUE,
8039       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
8040   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_RGBx, FALSE, TRUE, TRUE, TRUE,
8041       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
8042   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_RGB, FALSE, TRUE, TRUE, TRUE,
8043       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
8044   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_BGR, FALSE, TRUE, TRUE, TRUE,
8045       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
8046   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_RGB15, FALSE, TRUE, TRUE, TRUE,
8047       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
8048   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_BGR15, FALSE, TRUE, TRUE, TRUE,
8049       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
8050   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_RGB16, FALSE, TRUE, TRUE, TRUE,
8051       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
8052   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_BGR16, FALSE, TRUE, TRUE, TRUE,
8053       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
8054 
8055   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_ABGR, FALSE, TRUE, TRUE, TRUE,
8056       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
8057   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_xBGR, FALSE, TRUE, TRUE, TRUE,
8058       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
8059   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_RGBA, FALSE, TRUE, TRUE, TRUE,
8060       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
8061   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_RGBx, FALSE, TRUE, TRUE, TRUE,
8062       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
8063   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_RGB, FALSE, TRUE, TRUE, TRUE,
8064       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
8065   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_BGR, FALSE, TRUE, TRUE, TRUE,
8066       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
8067   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_RGB15, FALSE, TRUE, TRUE, TRUE,
8068       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
8069   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_BGR15, FALSE, TRUE, TRUE, TRUE,
8070       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
8071   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_RGB16, FALSE, TRUE, TRUE, TRUE,
8072       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
8073   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_BGR16, FALSE, TRUE, TRUE, TRUE,
8074       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
8075 
8076   {GST_VIDEO_FORMAT_A420, GST_VIDEO_FORMAT_ABGR, FALSE, TRUE, TRUE, TRUE,
8077       TRUE, TRUE, FALSE, FALSE, 0, 0, convert_A420_pack_ARGB},
8078   {GST_VIDEO_FORMAT_A420, GST_VIDEO_FORMAT_RGBA, FALSE, TRUE, TRUE, TRUE,
8079       TRUE, TRUE, FALSE, FALSE, 0, 0, convert_A420_pack_ARGB},
8080   {GST_VIDEO_FORMAT_A420, GST_VIDEO_FORMAT_BGRA, FALSE, TRUE, TRUE, TRUE,
8081       TRUE, TRUE, FALSE, FALSE, 0, 0, convert_A420_BGRA},
8082   /* A420 to non-alpha RGB formats, reuse I420_* method */
8083   {GST_VIDEO_FORMAT_A420, GST_VIDEO_FORMAT_BGRx, FALSE, TRUE, TRUE, TRUE,
8084       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_BGRA},
8085   {GST_VIDEO_FORMAT_A420, GST_VIDEO_FORMAT_xBGR, FALSE, TRUE, TRUE, TRUE,
8086       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
8087   {GST_VIDEO_FORMAT_A420, GST_VIDEO_FORMAT_RGBx, FALSE, TRUE, TRUE, TRUE,
8088       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
8089   {GST_VIDEO_FORMAT_A420, GST_VIDEO_FORMAT_RGB, FALSE, TRUE, TRUE, TRUE,
8090       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
8091   {GST_VIDEO_FORMAT_A420, GST_VIDEO_FORMAT_BGR, FALSE, TRUE, TRUE, TRUE,
8092       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
8093   {GST_VIDEO_FORMAT_A420, GST_VIDEO_FORMAT_RGB15, FALSE, TRUE, TRUE, TRUE,
8094       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
8095   {GST_VIDEO_FORMAT_A420, GST_VIDEO_FORMAT_BGR16, FALSE, TRUE, TRUE, TRUE,
8096       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
8097 
8098   /* scalers */
8099   {GST_VIDEO_FORMAT_GBR, GST_VIDEO_FORMAT_GBR, TRUE, FALSE, FALSE, TRUE,
8100       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
8101   {GST_VIDEO_FORMAT_GBRA, GST_VIDEO_FORMAT_GBRA, TRUE, FALSE, FALSE, TRUE,
8102       TRUE, TRUE, FALSE, FALSE, 0, 0, convert_scale_planes},
8103   {GST_VIDEO_FORMAT_RGBP, GST_VIDEO_FORMAT_RGBP, TRUE, FALSE, FALSE, TRUE,
8104       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
8105   {GST_VIDEO_FORMAT_BGRP, GST_VIDEO_FORMAT_BGRP, TRUE, FALSE, FALSE, TRUE,
8106       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
8107 
8108   {GST_VIDEO_FORMAT_YVYU, GST_VIDEO_FORMAT_YVYU, TRUE, FALSE, FALSE, TRUE,
8109       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
8110 
8111   {GST_VIDEO_FORMAT_RGB15, GST_VIDEO_FORMAT_RGB15, TRUE, FALSE, FALSE, TRUE,
8112       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
8113   {GST_VIDEO_FORMAT_RGB16, GST_VIDEO_FORMAT_RGB16, TRUE, FALSE, FALSE, TRUE,
8114       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
8115   {GST_VIDEO_FORMAT_BGR15, GST_VIDEO_FORMAT_BGR15, TRUE, FALSE, FALSE, TRUE,
8116       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
8117   {GST_VIDEO_FORMAT_BGR16, GST_VIDEO_FORMAT_BGR16, TRUE, FALSE, FALSE, TRUE,
8118       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
8119 
8120   {GST_VIDEO_FORMAT_RGB, GST_VIDEO_FORMAT_RGB, TRUE, FALSE, FALSE, TRUE, TRUE,
8121       FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
8122   {GST_VIDEO_FORMAT_BGR, GST_VIDEO_FORMAT_BGR, TRUE, FALSE, FALSE, TRUE, TRUE,
8123       FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
8124   {GST_VIDEO_FORMAT_v308, GST_VIDEO_FORMAT_v308, TRUE, FALSE, FALSE, TRUE, TRUE,
8125       FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
8126   {GST_VIDEO_FORMAT_IYU2, GST_VIDEO_FORMAT_IYU2, TRUE, FALSE, FALSE, TRUE, TRUE,
8127       FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
8128 
8129   {GST_VIDEO_FORMAT_ARGB, GST_VIDEO_FORMAT_ARGB, TRUE, FALSE, FALSE, TRUE, TRUE,
8130       TRUE, FALSE, FALSE, 0, 0, convert_scale_planes},
8131   {GST_VIDEO_FORMAT_xRGB, GST_VIDEO_FORMAT_xRGB, TRUE, FALSE, FALSE, TRUE, TRUE,
8132       FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
8133   {GST_VIDEO_FORMAT_ABGR, GST_VIDEO_FORMAT_ABGR, TRUE, FALSE, FALSE, TRUE, TRUE,
8134       TRUE, FALSE, FALSE, 0, 0, convert_scale_planes},
8135   {GST_VIDEO_FORMAT_xBGR, GST_VIDEO_FORMAT_xBGR, TRUE, FALSE, FALSE, TRUE, TRUE,
8136       FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
8137   {GST_VIDEO_FORMAT_RGBA, GST_VIDEO_FORMAT_RGBA, TRUE, FALSE, FALSE, TRUE, TRUE,
8138       TRUE, FALSE, FALSE, 0, 0, convert_scale_planes},
8139   {GST_VIDEO_FORMAT_RGBx, GST_VIDEO_FORMAT_RGBx, TRUE, FALSE, FALSE, TRUE, TRUE,
8140       FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
8141   {GST_VIDEO_FORMAT_BGRA, GST_VIDEO_FORMAT_BGRA, TRUE, FALSE, FALSE, TRUE, TRUE,
8142       TRUE, FALSE, FALSE, 0, 0, convert_scale_planes},
8143   {GST_VIDEO_FORMAT_BGRx, GST_VIDEO_FORMAT_BGRx, TRUE, FALSE, FALSE, TRUE, TRUE,
8144       FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
8145 
8146   {GST_VIDEO_FORMAT_ARGB64, GST_VIDEO_FORMAT_ARGB64, TRUE, FALSE, FALSE, TRUE,
8147       TRUE, TRUE, FALSE, FALSE, 0, 0, convert_scale_planes},
8148   {GST_VIDEO_FORMAT_AYUV64, GST_VIDEO_FORMAT_AYUV64, TRUE, FALSE, FALSE, TRUE,
8149       TRUE, TRUE, FALSE, FALSE, 0, 0, convert_scale_planes},
8150 
8151   {GST_VIDEO_FORMAT_GRAY16_LE, GST_VIDEO_FORMAT_GRAY16_LE, TRUE, FALSE, FALSE,
8152       TRUE, TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
8153   {GST_VIDEO_FORMAT_GRAY16_BE, GST_VIDEO_FORMAT_GRAY16_BE, TRUE, FALSE, FALSE,
8154       TRUE, TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
8155 };
8156 
8157 static gboolean
video_converter_lookup_fastpath(GstVideoConverter * convert)8158 video_converter_lookup_fastpath (GstVideoConverter * convert)
8159 {
8160   int i;
8161   GstVideoFormat in_format, out_format;
8162   GstVideoTransferFunction in_transf, out_transf;
8163   gboolean interlaced, same_matrix, same_primaries, same_size, crop, border;
8164   gboolean need_copy, need_set, need_mult;
8165   gint width, height;
8166   guint in_bpp, out_bpp;
8167 
8168   width = GST_VIDEO_INFO_WIDTH (&convert->in_info);
8169   height = GST_VIDEO_INFO_FIELD_HEIGHT (&convert->in_info);
8170 
8171   if (GET_OPT_DITHER_QUANTIZATION (convert) != 1)
8172     return FALSE;
8173 
8174   in_bpp = convert->in_info.finfo->bits;
8175   out_bpp = convert->out_info.finfo->bits;
8176 
8177   /* we don't do gamma conversion in fastpath */
8178   in_transf = convert->in_info.colorimetry.transfer;
8179   out_transf = convert->out_info.colorimetry.transfer;
8180 
8181   same_size = (width == convert->out_width && height == convert->out_height);
8182 
8183   /* fastpaths don't do gamma */
8184   if (CHECK_GAMMA_REMAP (convert) && (!same_size
8185           || !gst_video_transfer_function_is_equivalent (in_transf, in_bpp,
8186               out_transf, out_bpp)))
8187     return FALSE;
8188 
8189   need_copy = (convert->alpha_mode & ALPHA_MODE_COPY) == ALPHA_MODE_COPY;
8190   need_set = (convert->alpha_mode & ALPHA_MODE_SET) == ALPHA_MODE_SET;
8191   need_mult = (convert->alpha_mode & ALPHA_MODE_MULT) == ALPHA_MODE_MULT;
8192   GST_DEBUG ("alpha copy %d, set %d, mult %d", need_copy, need_set, need_mult);
8193 
8194   in_format = GST_VIDEO_INFO_FORMAT (&convert->in_info);
8195   out_format = GST_VIDEO_INFO_FORMAT (&convert->out_info);
8196 
8197   if (CHECK_MATRIX_NONE (convert)) {
8198     same_matrix = TRUE;
8199   } else {
8200     GstVideoColorMatrix in_matrix, out_matrix;
8201 
8202     in_matrix = convert->in_info.colorimetry.matrix;
8203     out_matrix = convert->out_info.colorimetry.matrix;
8204     same_matrix = in_matrix == out_matrix;
8205   }
8206 
8207   if (CHECK_PRIMARIES_NONE (convert)) {
8208     same_primaries = TRUE;
8209   } else {
8210     GstVideoColorPrimaries in_primaries, out_primaries;
8211 
8212     in_primaries = convert->in_info.colorimetry.primaries;
8213     out_primaries = convert->out_info.colorimetry.primaries;
8214     same_primaries = in_primaries == out_primaries;
8215   }
8216 
8217   interlaced = GST_VIDEO_INFO_IS_INTERLACED (&convert->in_info);
8218   interlaced |= GST_VIDEO_INFO_IS_INTERLACED (&convert->out_info);
8219 
8220   crop = convert->in_x || convert->in_y
8221       || convert->in_width < convert->in_maxwidth
8222       || convert->in_height < convert->in_maxheight;
8223   border = convert->out_x || convert->out_y
8224       || convert->out_width < convert->out_maxwidth
8225       || convert->out_height < convert->out_maxheight;
8226 
8227   for (i = 0; i < G_N_ELEMENTS (transforms); i++) {
8228     if (transforms[i].in_format == in_format &&
8229         transforms[i].out_format == out_format &&
8230         (transforms[i].keeps_interlaced || !interlaced) &&
8231         (transforms[i].needs_color_matrix || (same_matrix && same_primaries))
8232         && (!transforms[i].keeps_size || same_size)
8233         && (transforms[i].width_align & width) == 0
8234         && (transforms[i].height_align & height) == 0
8235         && (transforms[i].do_crop || !crop)
8236         && (transforms[i].do_border || !border)
8237         && (transforms[i].alpha_copy || !need_copy)
8238         && (transforms[i].alpha_set || !need_set)
8239         && (transforms[i].alpha_mult || !need_mult)) {
8240       guint j;
8241 
8242       GST_DEBUG ("using fastpath");
8243       if (transforms[i].needs_color_matrix)
8244         video_converter_compute_matrix (convert);
8245       convert->convert = transforms[i].convert;
8246 
8247       convert->tmpline =
8248           g_new (guint16 *, convert->conversion_runner->n_threads);
8249       for (j = 0; j < convert->conversion_runner->n_threads; j++)
8250         convert->tmpline[j] = g_malloc0 (sizeof (guint16) * (width + 8) * 4);
8251 
8252       if (!transforms[i].keeps_size)
8253         if (!setup_scale (convert))
8254           return FALSE;
8255       if (border)
8256         setup_borderline (convert);
8257       return TRUE;
8258     }
8259   }
8260   GST_DEBUG ("no fastpath found");
8261   return FALSE;
8262 }
8263