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 
38 #include "video-orc.h"
39 
40 /**
41  * SECTION:videoconverter
42  * @title: GstVideoConverter
43  * @short_description: Generic video conversion
44  *
45  * This object is used to convert video frames from one format to another.
46  * The object can perform conversion of:
47  *
48  *  * video format
49  *  * video colorspace
50  *  * chroma-siting
51  *  * video size
52  *
53  */
54 
55 /*
56  * (a)  unpack
57  * (b)  chroma upsample
58  * (c)  (convert Y'CbCr to R'G'B')
59  * (d)  gamma decode
60  * (e)  downscale
61  * (f)  colorspace convert through XYZ
62  * (g)  upscale
63  * (h)  gamma encode
64  * (i)  (convert R'G'B' to Y'CbCr)
65  * (j)  chroma downsample
66  * (k)  pack
67  *
68  * quality options
69  *
70  *  (a) range truncate, range expand
71  *  (b) full upsample, 1-1 non-cosited upsample, no upsample
72  *  (c) 8 bits, 16 bits
73  *  (d)
74  *  (e) 8 bits, 16 bits
75  *  (f) 8 bits, 16 bits
76  *  (g) 8 bits, 16 bits
77  *  (h)
78  *  (i) 8 bits, 16 bits
79  *  (j) 1-1 cosited downsample, no downsample
80  *  (k)
81  *
82  *
83  *         1 : a ->   ->   ->   -> e  -> f  -> g  ->   ->   ->   -> k
84  *         2 : a ->   ->   ->   -> e  -> f* -> g  ->   ->   ->   -> k
85  *         3 : a ->   ->   ->   -> e* -> f* -> g* ->   ->   ->   -> k
86  *         4 : a -> b ->   ->   -> e  -> f  -> g  ->   ->   -> j -> k
87  *         5 : a -> b ->   ->   -> e* -> f* -> g* ->   ->   -> j -> k
88  *         6 : a -> b -> c -> d -> e  -> f  -> g  -> h -> i -> j -> k
89  *         7 : a -> b -> c -> d -> e* -> f* -> g* -> h -> i -> j -> k
90  *
91  *         8 : a -> b -> c -> d -> e* -> f* -> g* -> h -> i -> j -> k
92  *         9 : a -> b -> c -> d -> e* -> f* -> g* -> h -> i -> j -> k
93  *        10 : a -> b -> c -> d -> e* -> f* -> g* -> h -> i -> j -> k
94  */
95 
96 #ifndef GST_DISABLE_GST_DEBUG
97 #define GST_CAT_DEFAULT ensure_debug_category()
98 static GstDebugCategory *
ensure_debug_category(void)99 ensure_debug_category (void)
100 {
101   static gsize cat_gonce = 0;
102 
103   if (g_once_init_enter (&cat_gonce)) {
104     gsize cat_done;
105 
106     cat_done = (gsize) _gst_debug_category_new ("video-converter", 0,
107         "video-converter object");
108 
109     g_once_init_leave (&cat_gonce, cat_done);
110   }
111 
112   return (GstDebugCategory *) cat_gonce;
113 }
114 #else
115 #define ensure_debug_category() /* NOOP */
116 #endif /* GST_DISABLE_GST_DEBUG */
117 
118 typedef void (*GstParallelizedTaskFunc) (gpointer user_data);
119 
120 typedef struct _GstParallelizedTaskRunner GstParallelizedTaskRunner;
121 typedef struct _GstParallelizedTaskThread GstParallelizedTaskThread;
122 
123 struct _GstParallelizedTaskThread
124 {
125   GstParallelizedTaskRunner *runner;
126   guint idx;
127   GThread *thread;
128 };
129 
130 struct _GstParallelizedTaskRunner
131 {
132   guint n_threads;
133 
134   GstParallelizedTaskThread *threads;
135 
136   GstParallelizedTaskFunc func;
137   gpointer *task_data;
138 
139   GMutex lock;
140   GCond cond_todo, cond_done;
141   gint n_todo, n_done;
142   gboolean quit;
143 };
144 
145 static gpointer
gst_parallelized_task_thread_func(gpointer data)146 gst_parallelized_task_thread_func (gpointer data)
147 {
148   GstParallelizedTaskThread *self = data;
149 
150 #if 0
151 #ifdef HAVE_PTHREAD
152   {
153     pthread_t thread = pthread_self ();
154     cpu_set_t cpuset;
155     int r;
156 
157     CPU_ZERO (&cpuset);
158     CPU_SET (self->idx, &cpuset);
159     if ((r = pthread_setaffinity_np (thread, sizeof (cpuset), &cpuset)) != 0)
160       GST_ERROR ("Failed to set thread affinity for thread %d: %s", self->idx,
161           g_strerror (r));
162   }
163 #endif
164 #endif
165 
166   g_mutex_lock (&self->runner->lock);
167   self->runner->n_done++;
168   if (self->runner->n_done == self->runner->n_threads - 1)
169     g_cond_signal (&self->runner->cond_done);
170 
171   do {
172     gint idx;
173 
174     while (self->runner->n_todo == -1 && !self->runner->quit)
175       g_cond_wait (&self->runner->cond_todo, &self->runner->lock);
176 
177     if (self->runner->quit)
178       break;
179 
180     idx = self->runner->n_todo--;
181     g_assert (self->runner->n_todo >= -1);
182     g_mutex_unlock (&self->runner->lock);
183 
184     g_assert (self->runner->func != NULL);
185 
186     self->runner->func (self->runner->task_data[idx]);
187 
188     g_mutex_lock (&self->runner->lock);
189     self->runner->n_done++;
190     if (self->runner->n_done == self->runner->n_threads - 1)
191       g_cond_signal (&self->runner->cond_done);
192   } while (TRUE);
193 
194   g_mutex_unlock (&self->runner->lock);
195 
196   return NULL;
197 }
198 
199 static void
gst_parallelized_task_runner_free(GstParallelizedTaskRunner * self)200 gst_parallelized_task_runner_free (GstParallelizedTaskRunner * self)
201 {
202   guint i;
203 
204   g_mutex_lock (&self->lock);
205   self->quit = TRUE;
206   g_cond_broadcast (&self->cond_todo);
207   g_mutex_unlock (&self->lock);
208 
209   for (i = 1; i < self->n_threads; i++) {
210     if (!self->threads[i].thread)
211       continue;
212 
213     g_thread_join (self->threads[i].thread);
214   }
215 
216   g_mutex_clear (&self->lock);
217   g_cond_clear (&self->cond_todo);
218   g_cond_clear (&self->cond_done);
219   g_free (self->threads);
220   g_free (self);
221 }
222 
223 static GstParallelizedTaskRunner *
gst_parallelized_task_runner_new(guint n_threads)224 gst_parallelized_task_runner_new (guint n_threads)
225 {
226   GstParallelizedTaskRunner *self;
227   guint i;
228   GError *err = NULL;
229 
230   if (n_threads == 0)
231     n_threads = g_get_num_processors ();
232 
233   self = g_new0 (GstParallelizedTaskRunner, 1);
234   self->n_threads = n_threads;
235   self->threads = g_new0 (GstParallelizedTaskThread, n_threads);
236 
237   self->quit = FALSE;
238   self->n_todo = -1;
239   self->n_done = 0;
240   g_mutex_init (&self->lock);
241   g_cond_init (&self->cond_todo);
242   g_cond_init (&self->cond_done);
243 
244   /* Set when scheduling a job */
245   self->func = NULL;
246   self->task_data = NULL;
247 
248   for (i = 0; i < n_threads; i++) {
249     self->threads[i].runner = self;
250     self->threads[i].idx = i;
251 
252     /* First thread is the one calling run() */
253     if (i > 0) {
254       self->threads[i].thread =
255           g_thread_try_new ("videoconvert", gst_parallelized_task_thread_func,
256           &self->threads[i], &err);
257       if (!self->threads[i].thread)
258         goto error;
259     }
260   }
261 
262   g_mutex_lock (&self->lock);
263   while (self->n_done < self->n_threads - 1)
264     g_cond_wait (&self->cond_done, &self->lock);
265   self->n_done = 0;
266   g_mutex_unlock (&self->lock);
267 
268   return self;
269 
270 error:
271   {
272     GST_ERROR ("Failed to start thread %u: %s", i, err->message);
273     g_clear_error (&err);
274 
275     gst_parallelized_task_runner_free (self);
276     return NULL;
277   }
278 }
279 
280 static void
gst_parallelized_task_runner_run(GstParallelizedTaskRunner * self,GstParallelizedTaskFunc func,gpointer * task_data)281 gst_parallelized_task_runner_run (GstParallelizedTaskRunner * self,
282     GstParallelizedTaskFunc func, gpointer * task_data)
283 {
284   guint n_threads = self->n_threads;
285 
286   self->func = func;
287   self->task_data = task_data;
288 
289   if (n_threads > 1) {
290     g_mutex_lock (&self->lock);
291     self->n_todo = self->n_threads - 2;
292     self->n_done = 0;
293     g_cond_broadcast (&self->cond_todo);
294     g_mutex_unlock (&self->lock);
295   }
296 
297   self->func (self->task_data[self->n_threads - 1]);
298 
299   if (n_threads > 1) {
300     g_mutex_lock (&self->lock);
301     while (self->n_done < self->n_threads - 1)
302       g_cond_wait (&self->cond_done, &self->lock);
303     self->n_done = 0;
304     g_mutex_unlock (&self->lock);
305   }
306 
307   self->func = NULL;
308   self->task_data = NULL;
309 }
310 
311 typedef struct _GstLineCache GstLineCache;
312 
313 #define SCALE    (8)
314 #define SCALE_F  ((float) (1 << SCALE))
315 
316 typedef struct _MatrixData MatrixData;
317 
318 struct _MatrixData
319 {
320   gdouble dm[4][4];
321   gint im[4][4];
322   gint width;
323   guint64 orc_p1;
324   guint64 orc_p2;
325   guint64 orc_p3;
326   guint64 orc_p4;
327   gint64 *t_r;
328   gint64 *t_g;
329   gint64 *t_b;
330   gint64 t_c;
331   void (*matrix_func) (MatrixData * data, gpointer pixels);
332 };
333 
334 typedef struct _GammaData GammaData;
335 
336 struct _GammaData
337 {
338   gpointer gamma_table;
339   gint width;
340   void (*gamma_func) (GammaData * data, gpointer dest, gpointer src);
341 };
342 
343 typedef enum
344 {
345   ALPHA_MODE_NONE = 0,
346   ALPHA_MODE_COPY = (1 << 0),
347   ALPHA_MODE_SET = (1 << 1),
348   ALPHA_MODE_MULT = (1 << 2)
349 } AlphaMode;
350 
351 typedef struct
352 {
353   guint8 *data;
354   guint stride;
355   guint n_lines;
356   guint idx;
357   gpointer user_data;
358   GDestroyNotify notify;
359 } ConverterAlloc;
360 
361 typedef void (*FastConvertFunc) (GstVideoConverter * convert,
362     const GstVideoFrame * src, GstVideoFrame * dest, gint plane);
363 
364 struct _GstVideoConverter
365 {
366   gint flags;
367 
368   GstVideoInfo in_info;
369   GstVideoInfo out_info;
370 
371   gint in_x;
372   gint in_y;
373   gint in_width;
374   gint in_height;
375   gint in_maxwidth;
376   gint in_maxheight;
377   gint out_x;
378   gint out_y;
379   gint out_width;
380   gint out_height;
381   gint out_maxwidth;
382   gint out_maxheight;
383 
384   gint current_pstride;
385   gint current_width;
386   gint current_height;
387   GstVideoFormat current_format;
388   gint current_bits;
389 
390   GstStructure *config;
391 
392   GstParallelizedTaskRunner *conversion_runner;
393 
394   guint16 **tmpline;
395 
396   gboolean fill_border;
397   gpointer borderline;
398   guint64 borders[4];
399   guint32 border_argb;
400   guint32 alpha_value;
401   AlphaMode alpha_mode;
402 
403   void (*convert) (GstVideoConverter * convert, const GstVideoFrame * src,
404       GstVideoFrame * dest);
405 
406   /* data for unpack */
407   GstLineCache **unpack_lines;
408   GstVideoFormat unpack_format;
409   guint unpack_bits;
410   gboolean unpack_rgb;
411   gboolean identity_unpack;
412   gint unpack_pstride;
413 
414   /* chroma upsample */
415   GstLineCache **upsample_lines;
416   GstVideoChromaResample **upsample;
417   GstVideoChromaResample **upsample_p;
418   GstVideoChromaResample **upsample_i;
419   guint up_n_lines;
420   gint up_offset;
421 
422   /* to R'G'B */
423   GstLineCache **to_RGB_lines;
424   MatrixData to_RGB_matrix;
425   /* gamma decode */
426   GammaData gamma_dec;
427 
428   /* scaling */
429   GstLineCache **hscale_lines;
430   GstVideoScaler **h_scaler;
431   gint h_scale_format;
432   GstLineCache **vscale_lines;
433   GstVideoScaler **v_scaler;
434   GstVideoScaler **v_scaler_p;
435   GstVideoScaler **v_scaler_i;
436   gint v_scale_width;
437   gint v_scale_format;
438 
439   /* color space conversion */
440   GstLineCache **convert_lines;
441   MatrixData convert_matrix;
442   gint in_bits;
443   gint out_bits;
444 
445   /* alpha correction */
446   GstLineCache **alpha_lines;
447   void (*alpha_func) (GstVideoConverter * convert, gpointer pixels, gint width);
448 
449   /* gamma encode */
450   GammaData gamma_enc;
451   /* to Y'CbCr */
452   GstLineCache **to_YUV_lines;
453   MatrixData to_YUV_matrix;
454 
455   /* chroma downsample */
456   GstLineCache **downsample_lines;
457   GstVideoChromaResample **downsample;
458   GstVideoChromaResample **downsample_p;
459   GstVideoChromaResample **downsample_i;
460   guint down_n_lines;
461   gint down_offset;
462 
463   /* dither */
464   GstLineCache **dither_lines;
465   GstVideoDither **dither;
466 
467   /* pack */
468   GstLineCache **pack_lines;
469   guint pack_nlines;
470   GstVideoFormat pack_format;
471   guint pack_bits;
472   gboolean pack_rgb;
473   gboolean identity_pack;
474   gint pack_pstride;
475   gconstpointer pack_pal;
476   gsize pack_palsize;
477 
478   const GstVideoFrame *src;
479   GstVideoFrame *dest;
480 
481   /* fastpath */
482   GstVideoFormat fformat[4];
483   gint fin_x[4];
484   gint fin_y[4];
485   gint fout_x[4];
486   gint fout_y[4];
487   gint fout_width[4];
488   gint fout_height[4];
489   gint fsplane[4];
490   gint ffill[4];
491 
492   struct
493   {
494     GstVideoScaler **scaler;
495   } fh_scaler[4];
496   struct
497   {
498     GstVideoScaler **scaler;
499   } fv_scaler[4];
500   FastConvertFunc fconvert[4];
501 };
502 
503 typedef gpointer (*GstLineCacheAllocLineFunc) (GstLineCache * cache, gint idx,
504     gpointer user_data);
505 typedef gboolean (*GstLineCacheNeedLineFunc) (GstLineCache * cache, gint idx,
506     gint out_line, gint in_line, gpointer user_data);
507 
508 struct _GstLineCache
509 {
510   gint first;
511   gint backlog;
512   GPtrArray *lines;
513 
514   GstLineCache *prev;
515   gboolean write_input;
516   gboolean pass_alloc;
517   gboolean alloc_writable;
518 
519   GstLineCacheNeedLineFunc need_line;
520   gint need_line_idx;
521   gpointer need_line_data;
522   GDestroyNotify need_line_notify;
523 
524   guint n_lines;
525   guint stride;
526   GstLineCacheAllocLineFunc alloc_line;
527   gpointer alloc_line_data;
528   GDestroyNotify alloc_line_notify;
529 };
530 
531 static GstLineCache *
gst_line_cache_new(GstLineCache * prev)532 gst_line_cache_new (GstLineCache * prev)
533 {
534   GstLineCache *result;
535 
536   result = g_slice_new0 (GstLineCache);
537   result->lines = g_ptr_array_new ();
538   result->prev = prev;
539 
540   return result;
541 }
542 
543 static void
gst_line_cache_clear(GstLineCache * cache)544 gst_line_cache_clear (GstLineCache * cache)
545 {
546   g_return_if_fail (cache != NULL);
547 
548   g_ptr_array_set_size (cache->lines, 0);
549   cache->first = 0;
550 }
551 
552 static void
gst_line_cache_free(GstLineCache * cache)553 gst_line_cache_free (GstLineCache * cache)
554 {
555   if (cache->need_line_notify)
556     cache->need_line_notify (cache->need_line_data);
557   if (cache->alloc_line_notify)
558     cache->alloc_line_notify (cache->alloc_line_data);
559   gst_line_cache_clear (cache);
560   g_ptr_array_unref (cache->lines);
561   g_slice_free (GstLineCache, cache);
562 }
563 
564 static void
gst_line_cache_set_need_line_func(GstLineCache * cache,GstLineCacheNeedLineFunc need_line,gint idx,gpointer user_data,GDestroyNotify notify)565 gst_line_cache_set_need_line_func (GstLineCache * cache,
566     GstLineCacheNeedLineFunc need_line, gint idx, gpointer user_data,
567     GDestroyNotify notify)
568 {
569   cache->need_line = need_line;
570   cache->need_line_idx = idx;
571   cache->need_line_data = user_data;
572   cache->need_line_notify = notify;
573 }
574 
575 static void
gst_line_cache_set_alloc_line_func(GstLineCache * cache,GstLineCacheAllocLineFunc alloc_line,gpointer user_data,GDestroyNotify notify)576 gst_line_cache_set_alloc_line_func (GstLineCache * cache,
577     GstLineCacheAllocLineFunc alloc_line, gpointer user_data,
578     GDestroyNotify notify)
579 {
580   cache->alloc_line = alloc_line;
581   cache->alloc_line_data = user_data;
582   cache->alloc_line_notify = notify;
583 }
584 
585 /* keep this much backlog for interlaced video */
586 #define BACKLOG 2
587 
588 static gpointer *
gst_line_cache_get_lines(GstLineCache * cache,gint idx,gint out_line,gint in_line,gint n_lines)589 gst_line_cache_get_lines (GstLineCache * cache, gint idx, gint out_line,
590     gint in_line, gint n_lines)
591 {
592   if (cache->first + cache->backlog < in_line) {
593     gint to_remove =
594         MIN (in_line - (cache->first + cache->backlog), cache->lines->len);
595     if (to_remove > 0) {
596       g_ptr_array_remove_range (cache->lines, 0, to_remove);
597     }
598     cache->first += to_remove;
599   } else if (in_line < cache->first) {
600     gst_line_cache_clear (cache);
601     cache->first = in_line;
602   }
603 
604   while (TRUE) {
605     gint oline;
606 
607     if (cache->first <= in_line
608         && in_line + n_lines <= cache->first + (gint) cache->lines->len) {
609       return cache->lines->pdata + (in_line - cache->first);
610     }
611 
612     if (cache->need_line == NULL)
613       break;
614 
615     oline = out_line + cache->first + cache->lines->len - in_line;
616 
617     if (!cache->need_line (cache, idx, oline, cache->first + cache->lines->len,
618             cache->need_line_data))
619       break;
620   }
621   GST_DEBUG ("no lines");
622   return NULL;
623 }
624 
625 static void
gst_line_cache_add_line(GstLineCache * cache,gint idx,gpointer line)626 gst_line_cache_add_line (GstLineCache * cache, gint idx, gpointer line)
627 {
628   if (cache->first + cache->lines->len != idx) {
629     gst_line_cache_clear (cache);
630     cache->first = idx;
631   }
632   g_ptr_array_add (cache->lines, line);
633 }
634 
635 static gpointer
gst_line_cache_alloc_line(GstLineCache * cache,gint idx)636 gst_line_cache_alloc_line (GstLineCache * cache, gint idx)
637 {
638   gpointer res;
639 
640   if (cache->alloc_line)
641     res = cache->alloc_line (cache, idx, cache->alloc_line_data);
642   else
643     res = NULL;
644 
645   return res;
646 }
647 
648 static void video_converter_generic (GstVideoConverter * convert,
649     const GstVideoFrame * src, GstVideoFrame * dest);
650 static gboolean video_converter_lookup_fastpath (GstVideoConverter * convert);
651 static void video_converter_compute_matrix (GstVideoConverter * convert);
652 static void video_converter_compute_resample (GstVideoConverter * convert,
653     gint idx);
654 
655 static gpointer get_dest_line (GstLineCache * cache, gint idx,
656     gpointer user_data);
657 
658 static gboolean do_unpack_lines (GstLineCache * cache, gint idx, gint out_line,
659     gint in_line, gpointer user_data);
660 static gboolean do_downsample_lines (GstLineCache * cache, gint idx,
661     gint out_line, gint in_line, gpointer user_data);
662 static gboolean do_convert_to_RGB_lines (GstLineCache * cache, gint idx,
663     gint out_line, gint in_line, gpointer user_data);
664 static gboolean do_convert_lines (GstLineCache * cache, gint idx, gint out_line,
665     gint in_line, gpointer user_data);
666 static gboolean do_alpha_lines (GstLineCache * cache, gint idx, gint out_line,
667     gint in_line, gpointer user_data);
668 static gboolean do_convert_to_YUV_lines (GstLineCache * cache, gint idx,
669     gint out_line, gint in_line, gpointer user_data);
670 static gboolean do_upsample_lines (GstLineCache * cache, gint idx,
671     gint out_line, gint in_line, gpointer user_data);
672 static gboolean do_vscale_lines (GstLineCache * cache, gint idx, gint out_line,
673     gint in_line, gpointer user_data);
674 static gboolean do_hscale_lines (GstLineCache * cache, gint idx, gint out_line,
675     gint in_line, gpointer user_data);
676 static gboolean do_dither_lines (GstLineCache * cache, gint idx, gint out_line,
677     gint in_line, gpointer user_data);
678 
679 static ConverterAlloc *
converter_alloc_new(guint stride,guint n_lines,gpointer user_data,GDestroyNotify notify)680 converter_alloc_new (guint stride, guint n_lines, gpointer user_data,
681     GDestroyNotify notify)
682 {
683   ConverterAlloc *alloc;
684 
685   GST_DEBUG ("stride %d, n_lines %d", stride, n_lines);
686   alloc = g_slice_new0 (ConverterAlloc);
687   alloc->data = g_malloc (stride * n_lines);
688   alloc->stride = stride;
689   alloc->n_lines = n_lines;
690   alloc->idx = 0;
691   alloc->user_data = user_data;
692   alloc->notify = notify;
693 
694   return alloc;
695 }
696 
697 static void
converter_alloc_free(ConverterAlloc * alloc)698 converter_alloc_free (ConverterAlloc * alloc)
699 {
700   if (alloc->notify)
701     alloc->notify (alloc->user_data);
702   g_free (alloc->data);
703   g_slice_free (ConverterAlloc, alloc);
704 }
705 
706 static void
setup_border_alloc(GstVideoConverter * convert,ConverterAlloc * alloc)707 setup_border_alloc (GstVideoConverter * convert, ConverterAlloc * alloc)
708 {
709   gint i;
710 
711   if (convert->borderline) {
712     for (i = 0; i < alloc->n_lines; i++)
713       memcpy (&alloc->data[i * alloc->stride], convert->borderline,
714           alloc->stride);
715   }
716 }
717 
718 static gpointer
get_temp_line(GstLineCache * cache,gint idx,gpointer user_data)719 get_temp_line (GstLineCache * cache, gint idx, gpointer user_data)
720 {
721   ConverterAlloc *alloc = user_data;
722   gpointer tmpline;
723 
724   GST_DEBUG ("get temp line %d (%p %d)", idx, alloc, alloc->idx);
725   tmpline = &alloc->data[alloc->stride * alloc->idx];
726   alloc->idx = (alloc->idx + 1) % alloc->n_lines;
727 
728   return tmpline;
729 }
730 
731 static gpointer
get_border_temp_line(GstLineCache * cache,gint idx,gpointer user_data)732 get_border_temp_line (GstLineCache * cache, gint idx, gpointer user_data)
733 {
734   ConverterAlloc *alloc = user_data;
735   GstVideoConverter *convert = alloc->user_data;
736   gpointer tmpline;
737 
738   GST_DEBUG ("get temp line %d (%p %d)", idx, alloc, alloc->idx);
739   tmpline = &alloc->data[alloc->stride * alloc->idx] +
740       (convert->out_x * convert->pack_pstride);
741   alloc->idx = (alloc->idx + 1) % alloc->n_lines;
742 
743   return tmpline;
744 }
745 
746 static gint
get_opt_int(GstVideoConverter * convert,const gchar * opt,gint def)747 get_opt_int (GstVideoConverter * convert, const gchar * opt, gint def)
748 {
749   gint res;
750   if (!gst_structure_get_int (convert->config, opt, &res))
751     res = def;
752   return res;
753 }
754 
755 static guint
get_opt_uint(GstVideoConverter * convert,const gchar * opt,guint def)756 get_opt_uint (GstVideoConverter * convert, const gchar * opt, guint def)
757 {
758   guint res;
759   if (!gst_structure_get_uint (convert->config, opt, &res))
760     res = def;
761   return res;
762 }
763 
764 static gdouble
get_opt_double(GstVideoConverter * convert,const gchar * opt,gdouble def)765 get_opt_double (GstVideoConverter * convert, const gchar * opt, gdouble def)
766 {
767   gdouble res;
768   if (!gst_structure_get_double (convert->config, opt, &res))
769     res = def;
770   return res;
771 }
772 
773 static gboolean
get_opt_bool(GstVideoConverter * convert,const gchar * opt,gboolean def)774 get_opt_bool (GstVideoConverter * convert, const gchar * opt, gboolean def)
775 {
776   gboolean res;
777   if (!gst_structure_get_boolean (convert->config, opt, &res))
778     res = def;
779   return res;
780 }
781 
782 static gint
get_opt_enum(GstVideoConverter * convert,const gchar * opt,GType type,gint def)783 get_opt_enum (GstVideoConverter * convert, const gchar * opt, GType type,
784     gint def)
785 {
786   gint res;
787   if (!gst_structure_get_enum (convert->config, opt, type, &res))
788     res = def;
789   return res;
790 }
791 
792 #define DEFAULT_OPT_FILL_BORDER TRUE
793 #define DEFAULT_OPT_ALPHA_VALUE 1.0
794 /* options copy, set, mult */
795 #define DEFAULT_OPT_ALPHA_MODE GST_VIDEO_ALPHA_MODE_COPY
796 #define DEFAULT_OPT_BORDER_ARGB 0xff000000
797 /* options full, input-only, output-only, none */
798 #define DEFAULT_OPT_MATRIX_MODE GST_VIDEO_MATRIX_MODE_FULL
799 /* none, remap */
800 #define DEFAULT_OPT_GAMMA_MODE GST_VIDEO_GAMMA_MODE_NONE
801 /* none, merge-only, fast */
802 #define DEFAULT_OPT_PRIMARIES_MODE GST_VIDEO_PRIMARIES_MODE_NONE
803 /* options full, upsample-only, downsample-only, none */
804 #define DEFAULT_OPT_CHROMA_MODE GST_VIDEO_CHROMA_MODE_FULL
805 #define DEFAULT_OPT_RESAMPLER_METHOD GST_VIDEO_RESAMPLER_METHOD_CUBIC
806 #define DEFAULT_OPT_CHROMA_RESAMPLER_METHOD GST_VIDEO_RESAMPLER_METHOD_LINEAR
807 #define DEFAULT_OPT_RESAMPLER_TAPS 0
808 #define DEFAULT_OPT_DITHER_METHOD GST_VIDEO_DITHER_BAYER
809 #define DEFAULT_OPT_DITHER_QUANTIZATION 1
810 
811 #define GET_OPT_FILL_BORDER(c) get_opt_bool(c, \
812     GST_VIDEO_CONVERTER_OPT_FILL_BORDER, DEFAULT_OPT_FILL_BORDER)
813 #define GET_OPT_ALPHA_VALUE(c) get_opt_double(c, \
814     GST_VIDEO_CONVERTER_OPT_ALPHA_VALUE, DEFAULT_OPT_ALPHA_VALUE)
815 #define GET_OPT_ALPHA_MODE(c) get_opt_enum(c, \
816     GST_VIDEO_CONVERTER_OPT_ALPHA_MODE, GST_TYPE_VIDEO_ALPHA_MODE, DEFAULT_OPT_ALPHA_MODE)
817 #define GET_OPT_BORDER_ARGB(c) get_opt_uint(c, \
818     GST_VIDEO_CONVERTER_OPT_BORDER_ARGB, DEFAULT_OPT_BORDER_ARGB)
819 #define GET_OPT_MATRIX_MODE(c) get_opt_enum(c, \
820     GST_VIDEO_CONVERTER_OPT_MATRIX_MODE, GST_TYPE_VIDEO_MATRIX_MODE, DEFAULT_OPT_MATRIX_MODE)
821 #define GET_OPT_GAMMA_MODE(c) get_opt_enum(c, \
822     GST_VIDEO_CONVERTER_OPT_GAMMA_MODE, GST_TYPE_VIDEO_GAMMA_MODE, DEFAULT_OPT_GAMMA_MODE)
823 #define GET_OPT_PRIMARIES_MODE(c) get_opt_enum(c, \
824     GST_VIDEO_CONVERTER_OPT_PRIMARIES_MODE, GST_TYPE_VIDEO_PRIMARIES_MODE, DEFAULT_OPT_PRIMARIES_MODE)
825 #define GET_OPT_CHROMA_MODE(c) get_opt_enum(c, \
826     GST_VIDEO_CONVERTER_OPT_CHROMA_MODE, GST_TYPE_VIDEO_CHROMA_MODE, DEFAULT_OPT_CHROMA_MODE)
827 #define GET_OPT_RESAMPLER_METHOD(c) get_opt_enum(c, \
828     GST_VIDEO_CONVERTER_OPT_RESAMPLER_METHOD, GST_TYPE_VIDEO_RESAMPLER_METHOD, \
829     DEFAULT_OPT_RESAMPLER_METHOD)
830 #define GET_OPT_CHROMA_RESAMPLER_METHOD(c) get_opt_enum(c, \
831     GST_VIDEO_CONVERTER_OPT_CHROMA_RESAMPLER_METHOD, GST_TYPE_VIDEO_RESAMPLER_METHOD, \
832     DEFAULT_OPT_CHROMA_RESAMPLER_METHOD)
833 #define GET_OPT_RESAMPLER_TAPS(c) get_opt_uint(c, \
834     GST_VIDEO_CONVERTER_OPT_RESAMPLER_TAPS, DEFAULT_OPT_RESAMPLER_TAPS)
835 #define GET_OPT_DITHER_METHOD(c) get_opt_enum(c, \
836     GST_VIDEO_CONVERTER_OPT_DITHER_METHOD, GST_TYPE_VIDEO_DITHER_METHOD, \
837     DEFAULT_OPT_DITHER_METHOD)
838 #define GET_OPT_DITHER_QUANTIZATION(c) get_opt_uint(c, \
839     GST_VIDEO_CONVERTER_OPT_DITHER_QUANTIZATION, DEFAULT_OPT_DITHER_QUANTIZATION)
840 
841 #define CHECK_ALPHA_COPY(c) (GET_OPT_ALPHA_MODE(c) == GST_VIDEO_ALPHA_MODE_COPY)
842 #define CHECK_ALPHA_SET(c) (GET_OPT_ALPHA_MODE(c) == GST_VIDEO_ALPHA_MODE_SET)
843 #define CHECK_ALPHA_MULT(c) (GET_OPT_ALPHA_MODE(c) == GST_VIDEO_ALPHA_MODE_MULT)
844 
845 #define CHECK_MATRIX_FULL(c) (GET_OPT_MATRIX_MODE(c) == GST_VIDEO_MATRIX_MODE_FULL)
846 #define CHECK_MATRIX_INPUT(c) (GET_OPT_MATRIX_MODE(c) == GST_VIDEO_MATRIX_MODE_INPUT_ONLY)
847 #define CHECK_MATRIX_OUTPUT(c) (GET_OPT_MATRIX_MODE(c) == GST_VIDEO_MATRIX_MODE_OUTPUT_ONLY)
848 #define CHECK_MATRIX_NONE(c) (GET_OPT_MATRIX_MODE(c) == GST_VIDEO_MATRIX_MODE_NONE)
849 
850 #define CHECK_GAMMA_NONE(c) (GET_OPT_GAMMA_MODE(c) == GST_VIDEO_GAMMA_MODE_NONE)
851 #define CHECK_GAMMA_REMAP(c) (GET_OPT_GAMMA_MODE(c) == GST_VIDEO_GAMMA_MODE_REMAP)
852 
853 #define CHECK_PRIMARIES_NONE(c) (GET_OPT_PRIMARIES_MODE(c) == GST_VIDEO_PRIMARIES_MODE_NONE)
854 #define CHECK_PRIMARIES_MERGE(c) (GET_OPT_PRIMARIES_MODE(c) == GST_VIDEO_PRIMARIES_MODE_MERGE_ONLY)
855 #define CHECK_PRIMARIES_FAST(c) (GET_OPT_PRIMARIES_MODE(c) == GST_VIDEO_PRIMARIES_MODE_FAST)
856 
857 #define CHECK_CHROMA_FULL(c) (GET_OPT_CHROMA_MODE(c) == GST_VIDEO_CHROMA_MODE_FULL)
858 #define CHECK_CHROMA_UPSAMPLE(c) (GET_OPT_CHROMA_MODE(c) == GST_VIDEO_CHROMA_MODE_UPSAMPLE_ONLY)
859 #define CHECK_CHROMA_DOWNSAMPLE(c) (GET_OPT_CHROMA_MODE(c) == GST_VIDEO_CHROMA_MODE_DOWNSAMPLE_ONLY)
860 #define CHECK_CHROMA_NONE(c) (GET_OPT_CHROMA_MODE(c) == GST_VIDEO_CHROMA_MODE_NONE)
861 
862 static GstLineCache *
chain_unpack_line(GstVideoConverter * convert,gint idx)863 chain_unpack_line (GstVideoConverter * convert, gint idx)
864 {
865   GstLineCache *prev;
866   GstVideoInfo *info;
867 
868   info = &convert->in_info;
869 
870   convert->current_format = convert->unpack_format;
871   convert->current_bits = convert->unpack_bits;
872   convert->current_pstride = convert->current_bits >> 1;
873 
874   convert->unpack_pstride = convert->current_pstride;
875   convert->identity_unpack = (convert->current_format == info->finfo->format);
876 
877   GST_DEBUG ("chain unpack line format %s, pstride %d, identity_unpack %d",
878       gst_video_format_to_string (convert->current_format),
879       convert->current_pstride, convert->identity_unpack);
880 
881   prev = convert->unpack_lines[idx] = gst_line_cache_new (NULL);
882   prev->write_input = FALSE;
883   prev->pass_alloc = FALSE;
884   prev->n_lines = 1;
885   prev->stride = convert->current_pstride * convert->current_width;
886   gst_line_cache_set_need_line_func (prev, do_unpack_lines, idx, convert, NULL);
887 
888   return prev;
889 }
890 
891 static GstLineCache *
chain_upsample(GstVideoConverter * convert,GstLineCache * prev,gint idx)892 chain_upsample (GstVideoConverter * convert, GstLineCache * prev, gint idx)
893 {
894   video_converter_compute_resample (convert, idx);
895 
896   if (convert->upsample_p[idx] || convert->upsample_i[idx]) {
897     GST_DEBUG ("chain upsample");
898     prev = convert->upsample_lines[idx] = gst_line_cache_new (prev);
899     prev->write_input = TRUE;
900     prev->pass_alloc = TRUE;
901     prev->n_lines = 4;
902     prev->stride = convert->current_pstride * convert->current_width;
903     gst_line_cache_set_need_line_func (prev,
904         do_upsample_lines, idx, convert, NULL);
905   }
906   return prev;
907 }
908 
909 static void
color_matrix_set_identity(MatrixData * m)910 color_matrix_set_identity (MatrixData * m)
911 {
912   int i, j;
913 
914   for (i = 0; i < 4; i++) {
915     for (j = 0; j < 4; j++) {
916       m->dm[i][j] = (i == j);
917     }
918   }
919 }
920 
921 static void
color_matrix_copy(MatrixData * d,const MatrixData * s)922 color_matrix_copy (MatrixData * d, const MatrixData * s)
923 {
924   gint i, j;
925 
926   for (i = 0; i < 4; i++)
927     for (j = 0; j < 4; j++)
928       d->dm[i][j] = s->dm[i][j];
929 }
930 
931 /* Perform 4x4 matrix multiplication:
932  *  - @dst@ = @a@ * @b@
933  *  - @dst@ may be a pointer to @a@ andor @b@
934  */
935 static void
color_matrix_multiply(MatrixData * dst,MatrixData * a,MatrixData * b)936 color_matrix_multiply (MatrixData * dst, MatrixData * a, MatrixData * b)
937 {
938   MatrixData tmp;
939   int i, j, k;
940 
941   for (i = 0; i < 4; i++) {
942     for (j = 0; j < 4; j++) {
943       double x = 0;
944       for (k = 0; k < 4; k++) {
945         x += a->dm[i][k] * b->dm[k][j];
946       }
947       tmp.dm[i][j] = x;
948     }
949   }
950   color_matrix_copy (dst, &tmp);
951 }
952 
953 static void
color_matrix_invert(MatrixData * d,MatrixData * s)954 color_matrix_invert (MatrixData * d, MatrixData * s)
955 {
956   MatrixData tmp;
957   int i, j;
958   double det;
959 
960   color_matrix_set_identity (&tmp);
961   for (j = 0; j < 3; j++) {
962     for (i = 0; i < 3; i++) {
963       tmp.dm[j][i] =
964           s->dm[(i + 1) % 3][(j + 1) % 3] * s->dm[(i + 2) % 3][(j + 2) % 3] -
965           s->dm[(i + 1) % 3][(j + 2) % 3] * s->dm[(i + 2) % 3][(j + 1) % 3];
966     }
967   }
968   det =
969       tmp.dm[0][0] * s->dm[0][0] + tmp.dm[0][1] * s->dm[1][0] +
970       tmp.dm[0][2] * s->dm[2][0];
971   for (j = 0; j < 3; j++) {
972     for (i = 0; i < 3; i++) {
973       tmp.dm[i][j] /= det;
974     }
975   }
976   color_matrix_copy (d, &tmp);
977 }
978 
979 static void
color_matrix_offset_components(MatrixData * m,double a1,double a2,double a3)980 color_matrix_offset_components (MatrixData * m, double a1, double a2, double a3)
981 {
982   MatrixData a;
983 
984   color_matrix_set_identity (&a);
985   a.dm[0][3] = a1;
986   a.dm[1][3] = a2;
987   a.dm[2][3] = a3;
988   color_matrix_multiply (m, &a, m);
989 }
990 
991 static void
color_matrix_scale_components(MatrixData * m,double a1,double a2,double a3)992 color_matrix_scale_components (MatrixData * m, double a1, double a2, double a3)
993 {
994   MatrixData a;
995 
996   color_matrix_set_identity (&a);
997   a.dm[0][0] = a1;
998   a.dm[1][1] = a2;
999   a.dm[2][2] = a3;
1000   color_matrix_multiply (m, &a, m);
1001 }
1002 
1003 static void
color_matrix_debug(const MatrixData * s)1004 color_matrix_debug (const MatrixData * s)
1005 {
1006   GST_DEBUG ("[%f %f %f %f]", s->dm[0][0], s->dm[0][1], s->dm[0][2],
1007       s->dm[0][3]);
1008   GST_DEBUG ("[%f %f %f %f]", s->dm[1][0], s->dm[1][1], s->dm[1][2],
1009       s->dm[1][3]);
1010   GST_DEBUG ("[%f %f %f %f]", s->dm[2][0], s->dm[2][1], s->dm[2][2],
1011       s->dm[2][3]);
1012   GST_DEBUG ("[%f %f %f %f]", s->dm[3][0], s->dm[3][1], s->dm[3][2],
1013       s->dm[3][3]);
1014 }
1015 
1016 static void
color_matrix_convert(MatrixData * s)1017 color_matrix_convert (MatrixData * s)
1018 {
1019   gint i, j;
1020 
1021   for (i = 0; i < 4; i++)
1022     for (j = 0; j < 4; j++)
1023       s->im[i][j] = rint (s->dm[i][j]);
1024 
1025   GST_DEBUG ("[%6d %6d %6d %6d]", s->im[0][0], s->im[0][1], s->im[0][2],
1026       s->im[0][3]);
1027   GST_DEBUG ("[%6d %6d %6d %6d]", s->im[1][0], s->im[1][1], s->im[1][2],
1028       s->im[1][3]);
1029   GST_DEBUG ("[%6d %6d %6d %6d]", s->im[2][0], s->im[2][1], s->im[2][2],
1030       s->im[2][3]);
1031   GST_DEBUG ("[%6d %6d %6d %6d]", s->im[3][0], s->im[3][1], s->im[3][2],
1032       s->im[3][3]);
1033 }
1034 
1035 static void
color_matrix_YCbCr_to_RGB(MatrixData * m,double Kr,double Kb)1036 color_matrix_YCbCr_to_RGB (MatrixData * m, double Kr, double Kb)
1037 {
1038   double Kg = 1.0 - Kr - Kb;
1039   MatrixData k = {
1040     {
1041           {1., 0., 2 * (1 - Kr), 0.},
1042           {1., -2 * Kb * (1 - Kb) / Kg, -2 * Kr * (1 - Kr) / Kg, 0.},
1043           {1., 2 * (1 - Kb), 0., 0.},
1044           {0., 0., 0., 1.},
1045         }
1046   };
1047 
1048   color_matrix_multiply (m, &k, m);
1049 }
1050 
1051 static void
color_matrix_RGB_to_YCbCr(MatrixData * m,double Kr,double Kb)1052 color_matrix_RGB_to_YCbCr (MatrixData * m, double Kr, double Kb)
1053 {
1054   double Kg = 1.0 - Kr - Kb;
1055   MatrixData k;
1056   double x;
1057 
1058   k.dm[0][0] = Kr;
1059   k.dm[0][1] = Kg;
1060   k.dm[0][2] = Kb;
1061   k.dm[0][3] = 0;
1062 
1063   x = 1 / (2 * (1 - Kb));
1064   k.dm[1][0] = -x * Kr;
1065   k.dm[1][1] = -x * Kg;
1066   k.dm[1][2] = x * (1 - Kb);
1067   k.dm[1][3] = 0;
1068 
1069   x = 1 / (2 * (1 - Kr));
1070   k.dm[2][0] = x * (1 - Kr);
1071   k.dm[2][1] = -x * Kg;
1072   k.dm[2][2] = -x * Kb;
1073   k.dm[2][3] = 0;
1074 
1075   k.dm[3][0] = 0;
1076   k.dm[3][1] = 0;
1077   k.dm[3][2] = 0;
1078   k.dm[3][3] = 1;
1079 
1080   color_matrix_multiply (m, &k, m);
1081 }
1082 
1083 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)1084 color_matrix_RGB_to_XYZ (MatrixData * dst, double Rx, double Ry, double Gx,
1085     double Gy, double Bx, double By, double Wx, double Wy)
1086 {
1087   MatrixData m, im;
1088   double sx, sy, sz;
1089   double wx, wy, wz;
1090 
1091   color_matrix_set_identity (&m);
1092 
1093   m.dm[0][0] = Rx;
1094   m.dm[1][0] = Ry;
1095   m.dm[2][0] = (1.0 - Rx - Ry);
1096   m.dm[0][1] = Gx;
1097   m.dm[1][1] = Gy;
1098   m.dm[2][1] = (1.0 - Gx - Gy);
1099   m.dm[0][2] = Bx;
1100   m.dm[1][2] = By;
1101   m.dm[2][2] = (1.0 - Bx - By);
1102 
1103   color_matrix_invert (&im, &m);
1104 
1105   wx = Wx / Wy;
1106   wy = 1.0;
1107   wz = (1.0 - Wx - Wy) / Wy;
1108 
1109   sx = im.dm[0][0] * wx + im.dm[0][1] * wy + im.dm[0][2] * wz;
1110   sy = im.dm[1][0] * wx + im.dm[1][1] * wy + im.dm[1][2] * wz;
1111   sz = im.dm[2][0] * wx + im.dm[2][1] * wy + im.dm[2][2] * wz;
1112 
1113   m.dm[0][0] *= sx;
1114   m.dm[1][0] *= sx;
1115   m.dm[2][0] *= sx;
1116   m.dm[0][1] *= sy;
1117   m.dm[1][1] *= sy;
1118   m.dm[2][1] *= sy;
1119   m.dm[0][2] *= sz;
1120   m.dm[1][2] *= sz;
1121   m.dm[2][2] *= sz;
1122 
1123   color_matrix_copy (dst, &m);
1124 }
1125 
1126 static void
videoconvert_convert_init_tables(MatrixData * data)1127 videoconvert_convert_init_tables (MatrixData * data)
1128 {
1129   gint i, j;
1130 
1131   data->t_r = g_new (gint64, 256);
1132   data->t_g = g_new (gint64, 256);
1133   data->t_b = g_new (gint64, 256);
1134 
1135   for (i = 0; i < 256; i++) {
1136     gint64 r = 0, g = 0, b = 0;
1137 
1138     for (j = 0; j < 3; j++) {
1139       r = (r << 16) + data->im[j][0] * i;
1140       g = (g << 16) + data->im[j][1] * i;
1141       b = (b << 16) + data->im[j][2] * i;
1142     }
1143     data->t_r[i] = r;
1144     data->t_g[i] = g;
1145     data->t_b[i] = b;
1146   }
1147   data->t_c = ((gint64) data->im[0][3] << 32)
1148       + ((gint64) data->im[1][3] << 16)
1149       + ((gint64) data->im[2][3] << 0);
1150 }
1151 
1152 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)1153 _custom_video_orc_matrix8 (guint8 * ORC_RESTRICT d1,
1154     const guint8 * ORC_RESTRICT s1, orc_int64 p1, orc_int64 p2, orc_int64 p3,
1155     orc_int64 p4, int n)
1156 {
1157   gint i;
1158   gint r, g, b;
1159   gint y, u, v;
1160   gint a00, a01, a02, a03;
1161   gint a10, a11, a12, a13;
1162   gint a20, a21, a22, a23;
1163 
1164   a00 = (gint16) (p1 >> 16);
1165   a01 = (gint16) (p2 >> 16);
1166   a02 = (gint16) (p3 >> 16);
1167   a03 = (gint16) (p4 >> 16);
1168   a10 = (gint16) (p1 >> 32);
1169   a11 = (gint16) (p2 >> 32);
1170   a12 = (gint16) (p3 >> 32);
1171   a13 = (gint16) (p4 >> 32);
1172   a20 = (gint16) (p1 >> 48);
1173   a21 = (gint16) (p2 >> 48);
1174   a22 = (gint16) (p3 >> 48);
1175   a23 = (gint16) (p4 >> 48);
1176 
1177   for (i = 0; i < n; i++) {
1178     r = s1[i * 4 + 1];
1179     g = s1[i * 4 + 2];
1180     b = s1[i * 4 + 3];
1181 
1182     y = ((a00 * r + a01 * g + a02 * b) >> SCALE) + a03;
1183     u = ((a10 * r + a11 * g + a12 * b) >> SCALE) + a13;
1184     v = ((a20 * r + a21 * g + a22 * b) >> SCALE) + a23;
1185 
1186     d1[i * 4 + 1] = CLAMP (y, 0, 255);
1187     d1[i * 4 + 2] = CLAMP (u, 0, 255);
1188     d1[i * 4 + 3] = CLAMP (v, 0, 255);
1189   }
1190 }
1191 
1192 static void
video_converter_matrix8(MatrixData * data,gpointer pixels)1193 video_converter_matrix8 (MatrixData * data, gpointer pixels)
1194 {
1195   gpointer d = pixels;
1196   video_orc_matrix8 (d, pixels, data->orc_p1, data->orc_p2,
1197       data->orc_p3, data->orc_p4, data->width);
1198 }
1199 
1200 static void
video_converter_matrix8_table(MatrixData * data,gpointer pixels)1201 video_converter_matrix8_table (MatrixData * data, gpointer pixels)
1202 {
1203   gint i, width = data->width * 4;
1204   guint8 r, g, b;
1205   gint64 c = data->t_c;
1206   guint8 *p = pixels;
1207   gint64 x;
1208 
1209   for (i = 0; i < width; i += 4) {
1210     r = p[i + 1];
1211     g = p[i + 2];
1212     b = p[i + 3];
1213 
1214     x = data->t_r[r] + data->t_g[g] + data->t_b[b] + c;
1215 
1216     p[i + 1] = x >> (32 + SCALE);
1217     p[i + 2] = x >> (16 + SCALE);
1218     p[i + 3] = x >> (0 + SCALE);
1219   }
1220 }
1221 
1222 static void
video_converter_matrix8_AYUV_ARGB(MatrixData * data,gpointer pixels)1223 video_converter_matrix8_AYUV_ARGB (MatrixData * data, gpointer pixels)
1224 {
1225   gpointer d = pixels;
1226 
1227   video_orc_convert_AYUV_ARGB (d, 0, pixels, 0,
1228       data->im[0][0], data->im[0][2],
1229       data->im[2][1], data->im[1][1], data->im[1][2], data->width, 1);
1230 }
1231 
1232 static gboolean
is_ayuv_to_rgb_matrix(MatrixData * data)1233 is_ayuv_to_rgb_matrix (MatrixData * data)
1234 {
1235   if (data->im[0][0] != data->im[1][0] || data->im[1][0] != data->im[2][0])
1236     return FALSE;
1237 
1238   if (data->im[0][1] != 0 || data->im[2][2] != 0)
1239     return FALSE;
1240 
1241   return TRUE;
1242 }
1243 
1244 static gboolean
is_identity_matrix(MatrixData * data)1245 is_identity_matrix (MatrixData * data)
1246 {
1247   gint i, j;
1248   gint c = data->im[0][0];
1249 
1250   /* not really checking identity because of rounding errors but given
1251    * the conversions we do we just check for anything that looks like:
1252    *
1253    *  c 0 0 0
1254    *  0 c 0 0
1255    *  0 0 c 0
1256    *  0 0 0 1
1257    */
1258   for (i = 0; i < 4; i++) {
1259     for (j = 0; j < 4; j++) {
1260       if (i == j) {
1261         if (i == 3 && data->im[i][j] != 1)
1262           return FALSE;
1263         else if (data->im[i][j] != c)
1264           return FALSE;
1265       } else if (data->im[i][j] != 0)
1266         return FALSE;
1267     }
1268   }
1269   return TRUE;
1270 }
1271 
1272 static gboolean
is_no_clip_matrix(MatrixData * data)1273 is_no_clip_matrix (MatrixData * data)
1274 {
1275   gint i;
1276   static const guint8 test[8][3] = {
1277     {0, 0, 0},
1278     {0, 0, 255},
1279     {0, 255, 0},
1280     {0, 255, 255},
1281     {255, 0, 0},
1282     {255, 0, 255},
1283     {255, 255, 0},
1284     {255, 255, 255}
1285   };
1286 
1287   for (i = 0; i < 8; i++) {
1288     gint r, g, b;
1289     gint y, u, v;
1290 
1291     r = test[i][0];
1292     g = test[i][1];
1293     b = test[i][2];
1294 
1295     y = (data->im[0][0] * r + data->im[0][1] * g +
1296         data->im[0][2] * b + data->im[0][3]) >> SCALE;
1297     u = (data->im[1][0] * r + data->im[1][1] * g +
1298         data->im[1][2] * b + data->im[1][3]) >> SCALE;
1299     v = (data->im[2][0] * r + data->im[2][1] * g +
1300         data->im[2][2] * b + data->im[2][3]) >> SCALE;
1301 
1302     if (y != CLAMP (y, 0, 255) || u != CLAMP (u, 0, 255)
1303         || v != CLAMP (v, 0, 255))
1304       return FALSE;
1305   }
1306   return TRUE;
1307 }
1308 
1309 static void
video_converter_matrix16(MatrixData * data,gpointer pixels)1310 video_converter_matrix16 (MatrixData * data, gpointer pixels)
1311 {
1312   int i;
1313   int r, g, b;
1314   int y, u, v;
1315   guint16 *p = pixels;
1316   gint width = data->width;
1317 
1318   for (i = 0; i < width; i++) {
1319     r = p[i * 4 + 1];
1320     g = p[i * 4 + 2];
1321     b = p[i * 4 + 3];
1322 
1323     y = (data->im[0][0] * r + data->im[0][1] * g +
1324         data->im[0][2] * b + data->im[0][3]) >> SCALE;
1325     u = (data->im[1][0] * r + data->im[1][1] * g +
1326         data->im[1][2] * b + data->im[1][3]) >> SCALE;
1327     v = (data->im[2][0] * r + data->im[2][1] * g +
1328         data->im[2][2] * b + data->im[2][3]) >> SCALE;
1329 
1330     p[i * 4 + 1] = CLAMP (y, 0, 65535);
1331     p[i * 4 + 2] = CLAMP (u, 0, 65535);
1332     p[i * 4 + 3] = CLAMP (v, 0, 65535);
1333   }
1334 }
1335 
1336 
1337 static void
prepare_matrix(GstVideoConverter * convert,MatrixData * data)1338 prepare_matrix (GstVideoConverter * convert, MatrixData * data)
1339 {
1340   if (is_identity_matrix (data))
1341     return;
1342 
1343   color_matrix_scale_components (data, SCALE_F, SCALE_F, SCALE_F);
1344   color_matrix_convert (data);
1345 
1346   data->width = convert->current_width;
1347 
1348   if (convert->current_bits == 8) {
1349     if (!convert->unpack_rgb && convert->pack_rgb
1350         && is_ayuv_to_rgb_matrix (data)) {
1351       GST_DEBUG ("use fast AYUV -> RGB matrix");
1352       data->matrix_func = video_converter_matrix8_AYUV_ARGB;
1353     } else if (is_no_clip_matrix (data)) {
1354       GST_DEBUG ("use 8bit table");
1355       data->matrix_func = video_converter_matrix8_table;
1356       videoconvert_convert_init_tables (data);
1357     } else {
1358       gint a03, a13, a23;
1359 
1360       GST_DEBUG ("use 8bit matrix");
1361       data->matrix_func = video_converter_matrix8;
1362 
1363       data->orc_p1 = (((guint64) (guint16) data->im[2][0]) << 48) |
1364           (((guint64) (guint16) data->im[1][0]) << 32) |
1365           (((guint64) (guint16) data->im[0][0]) << 16);
1366       data->orc_p2 = (((guint64) (guint16) data->im[2][1]) << 48) |
1367           (((guint64) (guint16) data->im[1][1]) << 32) |
1368           (((guint64) (guint16) data->im[0][1]) << 16);
1369       data->orc_p3 = (((guint64) (guint16) data->im[2][2]) << 48) |
1370           (((guint64) (guint16) data->im[1][2]) << 32) |
1371           (((guint64) (guint16) data->im[0][2]) << 16);
1372 
1373       a03 = data->im[0][3] >> SCALE;
1374       a13 = data->im[1][3] >> SCALE;
1375       a23 = data->im[2][3] >> SCALE;
1376 
1377       data->orc_p4 = (((guint64) (guint16) a23) << 48) |
1378           (((guint64) (guint16) a13) << 32) | (((guint64) (guint16) a03) << 16);
1379     }
1380   } else {
1381     GST_DEBUG ("use 16bit matrix");
1382     data->matrix_func = video_converter_matrix16;
1383   }
1384 }
1385 
1386 static void
compute_matrix_to_RGB(GstVideoConverter * convert,MatrixData * data)1387 compute_matrix_to_RGB (GstVideoConverter * convert, MatrixData * data)
1388 {
1389   GstVideoInfo *info;
1390   gdouble Kr = 0, Kb = 0;
1391 
1392   info = &convert->in_info;
1393 
1394   {
1395     const GstVideoFormatInfo *uinfo;
1396     gint offset[4], scale[4];
1397 
1398     uinfo = gst_video_format_get_info (convert->unpack_format);
1399 
1400     /* bring color components to [0..1.0] range */
1401     gst_video_color_range_offsets (info->colorimetry.range, uinfo, offset,
1402         scale);
1403 
1404     color_matrix_offset_components (data, -offset[0], -offset[1], -offset[2]);
1405     color_matrix_scale_components (data, 1 / ((float) scale[0]),
1406         1 / ((float) scale[1]), 1 / ((float) scale[2]));
1407   }
1408 
1409   if (!convert->unpack_rgb && !CHECK_MATRIX_NONE (convert)) {
1410     if (CHECK_MATRIX_OUTPUT (convert))
1411       info = &convert->out_info;
1412 
1413     /* bring components to R'G'B' space */
1414     if (gst_video_color_matrix_get_Kr_Kb (info->colorimetry.matrix, &Kr, &Kb))
1415       color_matrix_YCbCr_to_RGB (data, Kr, Kb);
1416   }
1417   color_matrix_debug (data);
1418 }
1419 
1420 static void
compute_matrix_to_YUV(GstVideoConverter * convert,MatrixData * data,gboolean force)1421 compute_matrix_to_YUV (GstVideoConverter * convert, MatrixData * data,
1422     gboolean force)
1423 {
1424   GstVideoInfo *info;
1425   gdouble Kr = 0, Kb = 0;
1426 
1427   if (force || (!convert->pack_rgb && !CHECK_MATRIX_NONE (convert))) {
1428     if (CHECK_MATRIX_INPUT (convert))
1429       info = &convert->in_info;
1430     else
1431       info = &convert->out_info;
1432 
1433     /* bring components to YCbCr space */
1434     if (gst_video_color_matrix_get_Kr_Kb (info->colorimetry.matrix, &Kr, &Kb))
1435       color_matrix_RGB_to_YCbCr (data, Kr, Kb);
1436   }
1437 
1438   info = &convert->out_info;
1439 
1440   {
1441     const GstVideoFormatInfo *uinfo;
1442     gint offset[4], scale[4];
1443 
1444     uinfo = gst_video_format_get_info (convert->pack_format);
1445 
1446     /* bring color components to nominal range */
1447     gst_video_color_range_offsets (info->colorimetry.range, uinfo, offset,
1448         scale);
1449 
1450     color_matrix_scale_components (data, (float) scale[0], (float) scale[1],
1451         (float) scale[2]);
1452     color_matrix_offset_components (data, offset[0], offset[1], offset[2]);
1453   }
1454 
1455   color_matrix_debug (data);
1456 }
1457 
1458 
1459 static void
gamma_convert_u8_u16(GammaData * data,gpointer dest,gpointer src)1460 gamma_convert_u8_u16 (GammaData * data, gpointer dest, gpointer src)
1461 {
1462   gint i;
1463   guint8 *s = src;
1464   guint16 *d = dest;
1465   guint16 *table = data->gamma_table;
1466   gint width = data->width * 4;
1467 
1468   for (i = 0; i < width; i += 4) {
1469     d[i + 0] = (s[i] << 8) | s[i];
1470     d[i + 1] = table[s[i + 1]];
1471     d[i + 2] = table[s[i + 2]];
1472     d[i + 3] = table[s[i + 3]];
1473   }
1474 }
1475 
1476 static void
gamma_convert_u16_u8(GammaData * data,gpointer dest,gpointer src)1477 gamma_convert_u16_u8 (GammaData * data, gpointer dest, gpointer src)
1478 {
1479   gint i;
1480   guint16 *s = src;
1481   guint8 *d = dest;
1482   guint8 *table = data->gamma_table;
1483   gint width = data->width * 4;
1484 
1485   for (i = 0; i < width; i += 4) {
1486     d[i + 0] = s[i] >> 8;
1487     d[i + 1] = table[s[i + 1]];
1488     d[i + 2] = table[s[i + 2]];
1489     d[i + 3] = table[s[i + 3]];
1490   }
1491 }
1492 
1493 static void
gamma_convert_u16_u16(GammaData * data,gpointer dest,gpointer src)1494 gamma_convert_u16_u16 (GammaData * data, gpointer dest, gpointer src)
1495 {
1496   gint i;
1497   guint16 *s = src;
1498   guint16 *d = dest;
1499   guint16 *table = data->gamma_table;
1500   gint width = data->width * 4;
1501 
1502   for (i = 0; i < width; i += 4) {
1503     d[i + 0] = s[i];
1504     d[i + 1] = table[s[i + 1]];
1505     d[i + 2] = table[s[i + 2]];
1506     d[i + 3] = table[s[i + 3]];
1507   }
1508 }
1509 
1510 static void
setup_gamma_decode(GstVideoConverter * convert)1511 setup_gamma_decode (GstVideoConverter * convert)
1512 {
1513   GstVideoTransferFunction func;
1514   guint16 *t;
1515   gint i;
1516 
1517   func = convert->in_info.colorimetry.transfer;
1518 
1519   convert->gamma_dec.width = convert->current_width;
1520   if (convert->current_bits == 8) {
1521     GST_DEBUG ("gamma decode 8->16: %d", func);
1522     convert->gamma_dec.gamma_func = gamma_convert_u8_u16;
1523     t = convert->gamma_dec.gamma_table = g_malloc (sizeof (guint16) * 256);
1524 
1525     for (i = 0; i < 256; i++)
1526       t[i] = rint (gst_video_color_transfer_decode (func, i / 255.0) * 65535.0);
1527   } else {
1528     GST_DEBUG ("gamma decode 16->16: %d", func);
1529     convert->gamma_dec.gamma_func = gamma_convert_u16_u16;
1530     t = convert->gamma_dec.gamma_table = g_malloc (sizeof (guint16) * 65536);
1531 
1532     for (i = 0; i < 65536; i++)
1533       t[i] =
1534           rint (gst_video_color_transfer_decode (func, i / 65535.0) * 65535.0);
1535   }
1536   convert->current_bits = 16;
1537   convert->current_pstride = 8;
1538   convert->current_format = GST_VIDEO_FORMAT_ARGB64;
1539 }
1540 
1541 static void
setup_gamma_encode(GstVideoConverter * convert,gint target_bits)1542 setup_gamma_encode (GstVideoConverter * convert, gint target_bits)
1543 {
1544   GstVideoTransferFunction func;
1545   gint i;
1546 
1547   func = convert->out_info.colorimetry.transfer;
1548 
1549   convert->gamma_enc.width = convert->current_width;
1550   if (target_bits == 8) {
1551     guint8 *t;
1552 
1553     GST_DEBUG ("gamma encode 16->8: %d", func);
1554     convert->gamma_enc.gamma_func = gamma_convert_u16_u8;
1555     t = convert->gamma_enc.gamma_table = g_malloc (sizeof (guint8) * 65536);
1556 
1557     for (i = 0; i < 65536; i++)
1558       t[i] = rint (gst_video_color_transfer_encode (func, i / 65535.0) * 255.0);
1559   } else {
1560     guint16 *t;
1561 
1562     GST_DEBUG ("gamma encode 16->16: %d", func);
1563     convert->gamma_enc.gamma_func = gamma_convert_u16_u16;
1564     t = convert->gamma_enc.gamma_table = g_malloc (sizeof (guint16) * 65536);
1565 
1566     for (i = 0; i < 65536; i++)
1567       t[i] =
1568           rint (gst_video_color_transfer_encode (func, i / 65535.0) * 65535.0);
1569   }
1570 }
1571 
1572 static GstLineCache *
chain_convert_to_RGB(GstVideoConverter * convert,GstLineCache * prev,gint idx)1573 chain_convert_to_RGB (GstVideoConverter * convert, GstLineCache * prev,
1574     gint idx)
1575 {
1576   gboolean do_gamma;
1577 
1578   do_gamma = CHECK_GAMMA_REMAP (convert);
1579 
1580   if (do_gamma) {
1581     gint scale;
1582 
1583     if (!convert->unpack_rgb) {
1584       color_matrix_set_identity (&convert->to_RGB_matrix);
1585       compute_matrix_to_RGB (convert, &convert->to_RGB_matrix);
1586 
1587       /* matrix is in 0..1 range, scale to current bits */
1588       GST_DEBUG ("chain RGB convert");
1589       scale = 1 << convert->current_bits;
1590       color_matrix_scale_components (&convert->to_RGB_matrix,
1591           (float) scale, (float) scale, (float) scale);
1592 
1593       prepare_matrix (convert, &convert->to_RGB_matrix);
1594 
1595       if (convert->current_bits == 8)
1596         convert->current_format = GST_VIDEO_FORMAT_ARGB;
1597       else
1598         convert->current_format = GST_VIDEO_FORMAT_ARGB64;
1599     }
1600 
1601     prev = convert->to_RGB_lines[idx] = gst_line_cache_new (prev);
1602     prev->write_input = TRUE;
1603     prev->pass_alloc = FALSE;
1604     prev->n_lines = 1;
1605     prev->stride = convert->current_pstride * convert->current_width;
1606     gst_line_cache_set_need_line_func (prev,
1607         do_convert_to_RGB_lines, idx, convert, NULL);
1608 
1609     GST_DEBUG ("chain gamma decode");
1610     setup_gamma_decode (convert);
1611   }
1612   return prev;
1613 }
1614 
1615 static GstLineCache *
chain_hscale(GstVideoConverter * convert,GstLineCache * prev,gint idx)1616 chain_hscale (GstVideoConverter * convert, GstLineCache * prev, gint idx)
1617 {
1618   gint method;
1619   guint taps;
1620 
1621   method = GET_OPT_RESAMPLER_METHOD (convert);
1622   taps = GET_OPT_RESAMPLER_TAPS (convert);
1623 
1624   convert->h_scaler[idx] =
1625       gst_video_scaler_new (method, GST_VIDEO_SCALER_FLAG_NONE, taps,
1626       convert->in_width, convert->out_width, convert->config);
1627 
1628   gst_video_scaler_get_coeff (convert->h_scaler[idx], 0, NULL, &taps);
1629 
1630   GST_DEBUG ("chain hscale %d->%d, taps %d, method %d",
1631       convert->in_width, convert->out_width, taps, method);
1632 
1633   convert->current_width = convert->out_width;
1634   convert->h_scale_format = convert->current_format;
1635 
1636   prev = convert->hscale_lines[idx] = gst_line_cache_new (prev);
1637   prev->write_input = FALSE;
1638   prev->pass_alloc = FALSE;
1639   prev->n_lines = 1;
1640   prev->stride = convert->current_pstride * convert->current_width;
1641   gst_line_cache_set_need_line_func (prev, do_hscale_lines, idx, convert, NULL);
1642 
1643   return prev;
1644 }
1645 
1646 static GstLineCache *
chain_vscale(GstVideoConverter * convert,GstLineCache * prev,gint idx)1647 chain_vscale (GstVideoConverter * convert, GstLineCache * prev, gint idx)
1648 {
1649   gint method;
1650   guint taps, taps_i = 0;
1651   gint backlog = 0;
1652 
1653   method = GET_OPT_RESAMPLER_METHOD (convert);
1654   taps = GET_OPT_RESAMPLER_TAPS (convert);
1655 
1656   if (GST_VIDEO_INFO_IS_INTERLACED (&convert->in_info)) {
1657     convert->v_scaler_i[idx] =
1658         gst_video_scaler_new (method, GST_VIDEO_SCALER_FLAG_INTERLACED,
1659         taps, 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         prepare_matrix (convert, &convert->convert_matrix);
1837       if (convert->in_bits == convert->out_bits)
1838         pass_alloc = TRUE;
1839     } else
1840       do_conversion = FALSE;
1841 
1842     convert->current_bits = convert->pack_bits;
1843     convert->current_format = convert->pack_format;
1844     convert->current_pstride = convert->current_bits >> 1;
1845   } else {
1846     /* we did gamma, just do colorspace conversion if needed */
1847     if (same_primaries) {
1848       do_conversion = FALSE;
1849     } else {
1850       prepare_matrix (convert, &convert->convert_matrix);
1851       convert->in_bits = convert->out_bits = 16;
1852       pass_alloc = TRUE;
1853       do_conversion = TRUE;
1854     }
1855   }
1856 
1857   if (do_conversion) {
1858     GST_DEBUG ("chain conversion");
1859     prev = convert->convert_lines[idx] = gst_line_cache_new (prev);
1860     prev->write_input = TRUE;
1861     prev->pass_alloc = pass_alloc;
1862     prev->n_lines = 1;
1863     prev->stride = convert->current_pstride * convert->current_width;
1864     gst_line_cache_set_need_line_func (prev,
1865         do_convert_lines, idx, convert, NULL);
1866   }
1867   return prev;
1868 }
1869 
1870 static void
convert_set_alpha_u8(GstVideoConverter * convert,gpointer pixels,gint width)1871 convert_set_alpha_u8 (GstVideoConverter * convert, gpointer pixels, gint width)
1872 {
1873   guint8 *p = pixels;
1874   guint8 alpha = MIN (convert->alpha_value, 255);
1875   int i;
1876 
1877   for (i = 0; i < width; i++)
1878     p[i * 4] = alpha;
1879 }
1880 
1881 static void
convert_set_alpha_u16(GstVideoConverter * convert,gpointer pixels,gint width)1882 convert_set_alpha_u16 (GstVideoConverter * convert, gpointer pixels, gint width)
1883 {
1884   guint16 *p = pixels;
1885   guint16 alpha;
1886   int i;
1887 
1888   alpha = MIN (convert->alpha_value, 255);
1889   alpha |= alpha << 8;
1890 
1891   for (i = 0; i < width; i++)
1892     p[i * 4] = alpha;
1893 }
1894 
1895 static void
convert_mult_alpha_u8(GstVideoConverter * convert,gpointer pixels,gint width)1896 convert_mult_alpha_u8 (GstVideoConverter * convert, gpointer pixels, gint width)
1897 {
1898   guint8 *p = pixels;
1899   guint alpha = convert->alpha_value;
1900   int i;
1901 
1902   for (i = 0; i < width; i++) {
1903     gint a = (p[i * 4] * alpha) / 255;
1904     p[i * 4] = CLAMP (a, 0, 255);
1905   }
1906 }
1907 
1908 static void
convert_mult_alpha_u16(GstVideoConverter * convert,gpointer pixels,gint width)1909 convert_mult_alpha_u16 (GstVideoConverter * convert, gpointer pixels,
1910     gint width)
1911 {
1912   guint16 *p = pixels;
1913   guint alpha = convert->alpha_value;
1914   int i;
1915 
1916   for (i = 0; i < width; i++) {
1917     gint a = (p[i * 4] * alpha) / 255;
1918     p[i * 4] = CLAMP (a, 0, 65535);
1919   }
1920 }
1921 
1922 static GstLineCache *
chain_alpha(GstVideoConverter * convert,GstLineCache * prev,gint idx)1923 chain_alpha (GstVideoConverter * convert, GstLineCache * prev, gint idx)
1924 {
1925   switch (convert->alpha_mode) {
1926     case ALPHA_MODE_NONE:
1927     case ALPHA_MODE_COPY:
1928       return prev;
1929 
1930     case ALPHA_MODE_SET:
1931       if (convert->current_bits == 8)
1932         convert->alpha_func = convert_set_alpha_u8;
1933       else
1934         convert->alpha_func = convert_set_alpha_u16;
1935       break;
1936     case ALPHA_MODE_MULT:
1937       if (convert->current_bits == 8)
1938         convert->alpha_func = convert_mult_alpha_u8;
1939       else
1940         convert->alpha_func = convert_mult_alpha_u16;
1941       break;
1942   }
1943 
1944   GST_DEBUG ("chain alpha mode %d", convert->alpha_mode);
1945   prev = convert->alpha_lines[idx] = gst_line_cache_new (prev);
1946   prev->write_input = TRUE;
1947   prev->pass_alloc = TRUE;
1948   prev->n_lines = 1;
1949   prev->stride = convert->current_pstride * convert->current_width;
1950   gst_line_cache_set_need_line_func (prev, do_alpha_lines, idx, convert, NULL);
1951 
1952   return prev;
1953 }
1954 
1955 static GstLineCache *
chain_convert_to_YUV(GstVideoConverter * convert,GstLineCache * prev,gint idx)1956 chain_convert_to_YUV (GstVideoConverter * convert, GstLineCache * prev,
1957     gint idx)
1958 {
1959   gboolean do_gamma;
1960 
1961   do_gamma = CHECK_GAMMA_REMAP (convert);
1962 
1963   if (do_gamma) {
1964     gint scale;
1965 
1966     GST_DEBUG ("chain gamma encode");
1967     setup_gamma_encode (convert, convert->pack_bits);
1968 
1969     convert->current_bits = convert->pack_bits;
1970     convert->current_pstride = convert->current_bits >> 1;
1971 
1972     if (!convert->pack_rgb) {
1973       color_matrix_set_identity (&convert->to_YUV_matrix);
1974       compute_matrix_to_YUV (convert, &convert->to_YUV_matrix, FALSE);
1975 
1976       /* matrix is in 0..255 range, scale to pack bits */
1977       GST_DEBUG ("chain YUV convert");
1978       scale = 1 << convert->pack_bits;
1979       color_matrix_scale_components (&convert->to_YUV_matrix,
1980           1 / (float) scale, 1 / (float) scale, 1 / (float) scale);
1981       prepare_matrix (convert, &convert->to_YUV_matrix);
1982     }
1983     convert->current_format = convert->pack_format;
1984 
1985     prev = convert->to_YUV_lines[idx] = gst_line_cache_new (prev);
1986     prev->write_input = FALSE;
1987     prev->pass_alloc = FALSE;
1988     prev->n_lines = 1;
1989     prev->stride = convert->current_pstride * convert->current_width;
1990     gst_line_cache_set_need_line_func (prev,
1991         do_convert_to_YUV_lines, idx, convert, NULL);
1992   }
1993 
1994   return prev;
1995 }
1996 
1997 static GstLineCache *
chain_downsample(GstVideoConverter * convert,GstLineCache * prev,gint idx)1998 chain_downsample (GstVideoConverter * convert, GstLineCache * prev, gint idx)
1999 {
2000   if (convert->downsample_p[idx] || convert->downsample_i[idx]) {
2001     GST_DEBUG ("chain downsample");
2002     prev = convert->downsample_lines[idx] = gst_line_cache_new (prev);
2003     prev->write_input = TRUE;
2004     prev->pass_alloc = TRUE;
2005     prev->n_lines = 4;
2006     prev->stride = convert->current_pstride * convert->current_width;
2007     gst_line_cache_set_need_line_func (prev,
2008         do_downsample_lines, idx, convert, NULL);
2009   }
2010   return prev;
2011 }
2012 
2013 static GstLineCache *
chain_dither(GstVideoConverter * convert,GstLineCache * prev,gint idx)2014 chain_dither (GstVideoConverter * convert, GstLineCache * prev, gint idx)
2015 {
2016   gint i;
2017   gboolean do_dither = FALSE;
2018   GstVideoDitherFlags flags = 0;
2019   GstVideoDitherMethod method;
2020   guint quant[4], target_quant;
2021 
2022   method = GET_OPT_DITHER_METHOD (convert);
2023   if (method == GST_VIDEO_DITHER_NONE)
2024     return prev;
2025 
2026   target_quant = GET_OPT_DITHER_QUANTIZATION (convert);
2027   GST_DEBUG ("method %d, target-quantization %d", method, target_quant);
2028 
2029   if (convert->pack_pal) {
2030     quant[0] = 47;
2031     quant[1] = 47;
2032     quant[2] = 47;
2033     quant[3] = 1;
2034     do_dither = TRUE;
2035   } else {
2036     for (i = 0; i < GST_VIDEO_MAX_COMPONENTS; i++) {
2037       gint depth;
2038 
2039       depth = convert->out_info.finfo->depth[i];
2040 
2041       if (depth == 0) {
2042         quant[i] = 0;
2043         continue;
2044       }
2045 
2046       if (convert->current_bits >= depth) {
2047         quant[i] = 1 << (convert->current_bits - depth);
2048         if (target_quant > quant[i]) {
2049           flags |= GST_VIDEO_DITHER_FLAG_QUANTIZE;
2050           quant[i] = target_quant;
2051         }
2052       } else {
2053         quant[i] = 0;
2054       }
2055       if (quant[i] > 1)
2056         do_dither = TRUE;
2057     }
2058   }
2059 
2060   if (do_dither) {
2061     GST_DEBUG ("chain dither");
2062 
2063     convert->dither[idx] = gst_video_dither_new (method,
2064         flags, convert->pack_format, quant, convert->current_width);
2065 
2066     prev = convert->dither_lines[idx] = gst_line_cache_new (prev);
2067     prev->write_input = TRUE;
2068     prev->pass_alloc = TRUE;
2069     prev->n_lines = 1;
2070     prev->stride = convert->current_pstride * convert->current_width;
2071     gst_line_cache_set_need_line_func (prev, do_dither_lines, idx, convert,
2072         NULL);
2073   }
2074   return prev;
2075 }
2076 
2077 static GstLineCache *
chain_pack(GstVideoConverter * convert,GstLineCache * prev,gint idx)2078 chain_pack (GstVideoConverter * convert, GstLineCache * prev, gint idx)
2079 {
2080   convert->pack_nlines = convert->out_info.finfo->pack_lines;
2081   convert->pack_pstride = convert->current_pstride;
2082   convert->identity_pack =
2083       (convert->out_info.finfo->format ==
2084       convert->out_info.finfo->unpack_format);
2085   GST_DEBUG ("chain pack line format %s, pstride %d, identity_pack %d (%d %d)",
2086       gst_video_format_to_string (convert->current_format),
2087       convert->current_pstride, convert->identity_pack,
2088       convert->out_info.finfo->format, convert->out_info.finfo->unpack_format);
2089 
2090   return prev;
2091 }
2092 
2093 static void
setup_allocators(GstVideoConverter * convert)2094 setup_allocators (GstVideoConverter * convert)
2095 {
2096   GstLineCache *cache, *prev;
2097   GstLineCacheAllocLineFunc alloc_line;
2098   gboolean alloc_writable;
2099   gpointer user_data;
2100   GDestroyNotify notify;
2101   gint width;
2102   gint i;
2103 
2104   width = MAX (convert->in_maxwidth, convert->out_maxwidth);
2105   width += convert->out_x;
2106 
2107   for (i = 0; i < convert->conversion_runner->n_threads; i++) {
2108     /* start with using dest lines if we can directly write into it */
2109     if (convert->identity_pack) {
2110       alloc_line = get_dest_line;
2111       alloc_writable = TRUE;
2112       user_data = convert;
2113       notify = NULL;
2114     } else {
2115       user_data =
2116           converter_alloc_new (sizeof (guint16) * width * 4, 4 + BACKLOG,
2117           convert, NULL);
2118       setup_border_alloc (convert, user_data);
2119       notify = (GDestroyNotify) converter_alloc_free;
2120       alloc_line = get_border_temp_line;
2121       /* when we add a border, we need to write */
2122       alloc_writable = convert->borderline != NULL;
2123     }
2124 
2125     /* First step, try to calculate how many temp lines we need. Go backwards,
2126      * keep track of the maximum number of lines we need for each intermediate
2127      * step.  */
2128     for (prev = cache = convert->pack_lines[i]; cache; cache = cache->prev) {
2129       GST_DEBUG ("looking at cache %p, %d lines, %d backlog", cache,
2130           cache->n_lines, cache->backlog);
2131       prev->n_lines = MAX (prev->n_lines, cache->n_lines);
2132       if (!cache->pass_alloc) {
2133         GST_DEBUG ("cache %p, needs %d lines", prev, prev->n_lines);
2134         prev = cache;
2135       }
2136     }
2137 
2138     /* now walk backwards, we try to write into the dest lines directly
2139      * and keep track if the source needs to be writable */
2140     for (cache = convert->pack_lines[i]; cache; cache = cache->prev) {
2141       gst_line_cache_set_alloc_line_func (cache, alloc_line, user_data, notify);
2142       cache->alloc_writable = alloc_writable;
2143 
2144       /* make sure only one cache frees the allocator */
2145       notify = NULL;
2146 
2147       if (!cache->pass_alloc) {
2148         /* can't pass allocator, make new temp line allocator */
2149         user_data =
2150             converter_alloc_new (sizeof (guint16) * width * 4,
2151             cache->n_lines + cache->backlog, convert, NULL);
2152         notify = (GDestroyNotify) converter_alloc_free;
2153         alloc_line = get_temp_line;
2154         alloc_writable = FALSE;
2155       }
2156       /* if someone writes to the input, we need a writable line from the
2157        * previous cache */
2158       if (cache->write_input)
2159         alloc_writable = TRUE;
2160     }
2161     /* free leftover allocator */
2162     if (notify)
2163       notify (user_data);
2164   }
2165 }
2166 
2167 static void
setup_borderline(GstVideoConverter * convert)2168 setup_borderline (GstVideoConverter * convert)
2169 {
2170   gint width;
2171 
2172   width = MAX (convert->in_maxwidth, convert->out_maxwidth);
2173   width += convert->out_x;
2174 
2175   if (convert->fill_border && (convert->out_height < convert->out_maxheight ||
2176           convert->out_width < convert->out_maxwidth)) {
2177     guint32 border_val;
2178     gint i, w_sub;
2179     const GstVideoFormatInfo *out_finfo;
2180     gpointer planes[GST_VIDEO_MAX_PLANES];
2181     gint strides[GST_VIDEO_MAX_PLANES];
2182 
2183     convert->borderline = g_malloc0 (sizeof (guint16) * width * 4);
2184 
2185     out_finfo = convert->out_info.finfo;
2186 
2187     if (GST_VIDEO_INFO_IS_YUV (&convert->out_info)) {
2188       MatrixData cm;
2189       gint a, r, g, b;
2190       gint y, u, v;
2191 
2192       /* Get Color matrix. */
2193       color_matrix_set_identity (&cm);
2194       compute_matrix_to_YUV (convert, &cm, TRUE);
2195       color_matrix_convert (&cm);
2196 
2197       border_val = GINT32_FROM_BE (convert->border_argb);
2198 
2199       b = (0xFF000000 & border_val) >> 24;
2200       g = (0x00FF0000 & border_val) >> 16;
2201       r = (0x0000FF00 & border_val) >> 8;
2202       a = (0x000000FF & border_val);
2203 
2204       y = 16 + ((r * cm.im[0][0] + g * cm.im[0][1] + b * cm.im[0][2]) >> 8);
2205       u = 128 + ((r * cm.im[1][0] + g * cm.im[1][1] + b * cm.im[1][2]) >> 8);
2206       v = 128 + ((r * cm.im[2][0] + g * cm.im[2][1] + b * cm.im[2][2]) >> 8);
2207 
2208       a = CLAMP (a, 0, 255);
2209       y = CLAMP (y, 0, 255);
2210       u = CLAMP (u, 0, 255);
2211       v = CLAMP (v, 0, 255);
2212 
2213       border_val = a | (y << 8) | (u << 16) | ((guint32) v << 24);
2214     } else {
2215       border_val = GINT32_FROM_BE (convert->border_argb);
2216     }
2217     if (convert->pack_bits == 8)
2218       video_orc_splat_u32 (convert->borderline, border_val, width);
2219     else
2220       video_orc_splat2_u64 (convert->borderline, border_val, width);
2221 
2222     /* convert pixels */
2223     for (i = 0; i < out_finfo->n_planes; i++) {
2224       planes[i] = &convert->borders[i];
2225       strides[i] = sizeof (guint64);
2226     }
2227     w_sub = 0;
2228     if (out_finfo->n_planes == 1) {
2229       /* for packed formats, convert based on subsampling so that we
2230        * get a complete group of pixels */
2231       for (i = 0; i < out_finfo->n_components; i++) {
2232         w_sub = MAX (w_sub, out_finfo->w_sub[i]);
2233       }
2234     }
2235     out_finfo->pack_func (out_finfo, GST_VIDEO_PACK_FLAG_NONE,
2236         convert->borderline, 0, planes, strides,
2237         GST_VIDEO_CHROMA_SITE_UNKNOWN, 0, 1 << w_sub);
2238   } else {
2239     convert->borderline = NULL;
2240   }
2241 }
2242 
2243 static AlphaMode
convert_get_alpha_mode(GstVideoConverter * convert)2244 convert_get_alpha_mode (GstVideoConverter * convert)
2245 {
2246   gboolean in_alpha, out_alpha;
2247 
2248   in_alpha = GST_VIDEO_INFO_HAS_ALPHA (&convert->in_info);
2249   out_alpha = GST_VIDEO_INFO_HAS_ALPHA (&convert->out_info);
2250 
2251   /* no output alpha, do nothing */
2252   if (!out_alpha)
2253     return ALPHA_MODE_NONE;
2254 
2255   if (in_alpha) {
2256     /* in and out */
2257     if (CHECK_ALPHA_COPY (convert))
2258       return ALPHA_MODE_COPY;
2259 
2260     if (CHECK_ALPHA_MULT (convert)) {
2261       if (GET_OPT_ALPHA_VALUE (convert) == 1.0)
2262         return ALPHA_MODE_COPY;
2263       else
2264         return ALPHA_MODE_MULT;
2265     }
2266   }
2267   /* nothing special, this is what unpack etc does automatically */
2268   if (GET_OPT_ALPHA_VALUE (convert) == 1.0)
2269     return ALPHA_MODE_NONE;
2270 
2271   /* everything else becomes SET */
2272   return ALPHA_MODE_SET;
2273 }
2274 
2275 /**
2276  * gst_video_converter_new: (skip)
2277  * @in_info: a #GstVideoInfo
2278  * @out_info: a #GstVideoInfo
2279  * @config: (transfer full): a #GstStructure with configuration options
2280  *
2281  * Create a new converter object to convert between @in_info and @out_info
2282  * with @config.
2283  *
2284  * Returns: a #GstVideoConverter or %NULL if conversion is not possible.
2285  *
2286  * Since: 1.6
2287  */
2288 GstVideoConverter *
gst_video_converter_new(GstVideoInfo * in_info,GstVideoInfo * out_info,GstStructure * config)2289 gst_video_converter_new (GstVideoInfo * in_info, GstVideoInfo * out_info,
2290     GstStructure * config)
2291 {
2292   GstVideoConverter *convert;
2293   GstLineCache *prev;
2294   const GstVideoFormatInfo *fin, *fout, *finfo;
2295   gdouble alpha_value;
2296   gint n_threads, i;
2297 
2298   g_return_val_if_fail (in_info != NULL, NULL);
2299   g_return_val_if_fail (out_info != NULL, NULL);
2300   /* we won't ever do framerate conversion */
2301   g_return_val_if_fail (in_info->fps_n == out_info->fps_n, NULL);
2302   g_return_val_if_fail (in_info->fps_d == out_info->fps_d, NULL);
2303   /* we won't ever do deinterlace */
2304   g_return_val_if_fail (in_info->interlace_mode == out_info->interlace_mode,
2305       NULL);
2306 
2307   convert = g_slice_new0 (GstVideoConverter);
2308 
2309   fin = in_info->finfo;
2310   fout = out_info->finfo;
2311 
2312   convert->in_info = *in_info;
2313   convert->out_info = *out_info;
2314 
2315   /* default config */
2316   convert->config = gst_structure_new_empty ("GstVideoConverter");
2317   if (config)
2318     gst_video_converter_set_config (convert, config);
2319 
2320   convert->in_maxwidth = GST_VIDEO_INFO_WIDTH (in_info);
2321   convert->in_maxheight = GST_VIDEO_INFO_HEIGHT (in_info);
2322   convert->out_maxwidth = GST_VIDEO_INFO_WIDTH (out_info);
2323   convert->out_maxheight = GST_VIDEO_INFO_HEIGHT (out_info);
2324 
2325   convert->in_x = get_opt_int (convert, GST_VIDEO_CONVERTER_OPT_SRC_X, 0);
2326   convert->in_y = get_opt_int (convert, GST_VIDEO_CONVERTER_OPT_SRC_Y, 0);
2327   convert->in_x &= ~((1 << fin->w_sub[1]) - 1);
2328   convert->in_y &= ~((1 << fin->h_sub[1]) - 1);
2329 
2330   convert->in_width = get_opt_int (convert,
2331       GST_VIDEO_CONVERTER_OPT_SRC_WIDTH, convert->in_maxwidth - convert->in_x);
2332   convert->in_height = get_opt_int (convert,
2333       GST_VIDEO_CONVERTER_OPT_SRC_HEIGHT,
2334       convert->in_maxheight - convert->in_y);
2335 
2336   convert->in_width =
2337       MIN (convert->in_width, convert->in_maxwidth - convert->in_x);
2338   convert->in_height =
2339       MIN (convert->in_height, convert->in_maxheight - convert->in_y);
2340 
2341   convert->out_x = get_opt_int (convert, GST_VIDEO_CONVERTER_OPT_DEST_X, 0);
2342   convert->out_y = get_opt_int (convert, GST_VIDEO_CONVERTER_OPT_DEST_Y, 0);
2343   convert->out_x &= ~((1 << fout->w_sub[1]) - 1);
2344   convert->out_y &= ~((1 << fout->h_sub[1]) - 1);
2345 
2346   convert->out_width = get_opt_int (convert,
2347       GST_VIDEO_CONVERTER_OPT_DEST_WIDTH,
2348       convert->out_maxwidth - convert->out_x);
2349   convert->out_height =
2350       get_opt_int (convert, GST_VIDEO_CONVERTER_OPT_DEST_HEIGHT,
2351       convert->out_maxheight - convert->out_y);
2352 
2353   convert->out_width =
2354       MIN (convert->out_width, convert->out_maxwidth - convert->out_x);
2355   convert->out_height =
2356       MIN (convert->out_height, convert->out_maxheight - convert->out_y);
2357 
2358   convert->fill_border = GET_OPT_FILL_BORDER (convert);
2359   convert->border_argb = get_opt_uint (convert,
2360       GST_VIDEO_CONVERTER_OPT_BORDER_ARGB, DEFAULT_OPT_BORDER_ARGB);
2361 
2362   alpha_value = GET_OPT_ALPHA_VALUE (convert);
2363   convert->alpha_value = 255 * alpha_value;
2364   convert->alpha_mode = convert_get_alpha_mode (convert);
2365 
2366   convert->unpack_format = in_info->finfo->unpack_format;
2367   finfo = gst_video_format_get_info (convert->unpack_format);
2368   convert->unpack_bits = GST_VIDEO_FORMAT_INFO_DEPTH (finfo, 0);
2369   convert->unpack_rgb = GST_VIDEO_FORMAT_INFO_IS_RGB (finfo);
2370   if (convert->unpack_rgb
2371       && in_info->colorimetry.matrix != GST_VIDEO_COLOR_MATRIX_RGB) {
2372     /* force identity matrix for RGB input */
2373     GST_WARNING ("invalid matrix %d for input RGB format, using RGB",
2374         in_info->colorimetry.matrix);
2375     convert->in_info.colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_RGB;
2376   }
2377 
2378   convert->pack_format = out_info->finfo->unpack_format;
2379   finfo = gst_video_format_get_info (convert->pack_format);
2380   convert->pack_bits = GST_VIDEO_FORMAT_INFO_DEPTH (finfo, 0);
2381   convert->pack_rgb = GST_VIDEO_FORMAT_INFO_IS_RGB (finfo);
2382   convert->pack_pal =
2383       gst_video_format_get_palette (GST_VIDEO_INFO_FORMAT (out_info),
2384       &convert->pack_palsize);
2385   if (convert->pack_rgb
2386       && out_info->colorimetry.matrix != GST_VIDEO_COLOR_MATRIX_RGB) {
2387     /* force identity matrix for RGB output */
2388     GST_WARNING ("invalid matrix %d for output RGB format, using RGB",
2389         out_info->colorimetry.matrix);
2390     convert->out_info.colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_RGB;
2391   }
2392 
2393   n_threads = get_opt_uint (convert, GST_VIDEO_CONVERTER_OPT_THREADS, 1);
2394   if (n_threads == 0 || n_threads > g_get_num_processors ())
2395     n_threads = g_get_num_processors ();
2396   /* Magic number of 200 lines */
2397   if (MAX (convert->out_height, convert->in_height) / n_threads < 200)
2398     n_threads = (MAX (convert->out_height, convert->in_height) + 199) / 200;
2399   convert->conversion_runner = gst_parallelized_task_runner_new (n_threads);
2400 
2401   if (video_converter_lookup_fastpath (convert))
2402     goto done;
2403 
2404   if (in_info->finfo->unpack_func == NULL)
2405     goto no_unpack_func;
2406 
2407   if (out_info->finfo->pack_func == NULL)
2408     goto no_pack_func;
2409 
2410   convert->convert = video_converter_generic;
2411 
2412   convert->upsample_p = g_new0 (GstVideoChromaResample *, n_threads);
2413   convert->upsample_i = g_new0 (GstVideoChromaResample *, n_threads);
2414   convert->downsample_p = g_new0 (GstVideoChromaResample *, n_threads);
2415   convert->downsample_i = g_new0 (GstVideoChromaResample *, n_threads);
2416   convert->v_scaler_p = g_new0 (GstVideoScaler *, n_threads);
2417   convert->v_scaler_i = g_new0 (GstVideoScaler *, n_threads);
2418   convert->h_scaler = g_new0 (GstVideoScaler *, n_threads);
2419   convert->unpack_lines = g_new0 (GstLineCache *, n_threads);
2420   convert->pack_lines = g_new0 (GstLineCache *, n_threads);
2421   convert->upsample_lines = g_new0 (GstLineCache *, n_threads);
2422   convert->to_RGB_lines = g_new0 (GstLineCache *, n_threads);
2423   convert->hscale_lines = g_new0 (GstLineCache *, n_threads);
2424   convert->vscale_lines = g_new0 (GstLineCache *, n_threads);
2425   convert->convert_lines = g_new0 (GstLineCache *, n_threads);
2426   convert->alpha_lines = g_new0 (GstLineCache *, n_threads);
2427   convert->to_YUV_lines = g_new0 (GstLineCache *, n_threads);
2428   convert->downsample_lines = g_new0 (GstLineCache *, n_threads);
2429   convert->dither_lines = g_new0 (GstLineCache *, n_threads);
2430   convert->dither = g_new0 (GstVideoDither *, n_threads);
2431 
2432   for (i = 0; i < n_threads; i++) {
2433     convert->current_format = GST_VIDEO_INFO_FORMAT (in_info);
2434     convert->current_width = convert->in_width;
2435     convert->current_height = convert->in_height;
2436 
2437     /* unpack */
2438     prev = chain_unpack_line (convert, i);
2439     /* upsample chroma */
2440     prev = chain_upsample (convert, prev, i);
2441     /* convert to gamma decoded RGB */
2442     prev = chain_convert_to_RGB (convert, prev, i);
2443     /* do all downscaling */
2444     prev = chain_scale (convert, prev, FALSE, i);
2445     /* do conversion between color spaces */
2446     prev = chain_convert (convert, prev, i);
2447     /* do alpha channels */
2448     prev = chain_alpha (convert, prev, i);
2449     /* do all remaining (up)scaling */
2450     prev = chain_scale (convert, prev, TRUE, i);
2451     /* convert to gamma encoded Y'Cb'Cr' */
2452     prev = chain_convert_to_YUV (convert, prev, i);
2453     /* downsample chroma */
2454     prev = chain_downsample (convert, prev, i);
2455     /* dither */
2456     prev = chain_dither (convert, prev, i);
2457     /* pack into final format */
2458     convert->pack_lines[i] = chain_pack (convert, prev, i);
2459   }
2460 
2461   setup_borderline (convert);
2462   /* now figure out allocators */
2463   setup_allocators (convert);
2464 
2465 done:
2466   return convert;
2467 
2468   /* ERRORS */
2469 no_unpack_func:
2470   {
2471     GST_ERROR ("no unpack_func for format %s",
2472         gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (in_info)));
2473     gst_video_converter_free (convert);
2474     return NULL;
2475   }
2476 no_pack_func:
2477   {
2478     GST_ERROR ("no pack_func for format %s",
2479         gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (out_info)));
2480     gst_video_converter_free (convert);
2481     return NULL;
2482   }
2483 }
2484 
2485 static void
clear_matrix_data(MatrixData * data)2486 clear_matrix_data (MatrixData * data)
2487 {
2488   g_free (data->t_r);
2489   g_free (data->t_g);
2490   g_free (data->t_b);
2491 }
2492 
2493 /**
2494  * gst_video_converter_free:
2495  * @convert: a #GstVideoConverter
2496  *
2497  * Free @convert
2498  *
2499  * Since: 1.6
2500  */
2501 void
gst_video_converter_free(GstVideoConverter * convert)2502 gst_video_converter_free (GstVideoConverter * convert)
2503 {
2504   guint i, j;
2505 
2506   g_return_if_fail (convert != NULL);
2507 
2508   for (i = 0; i < convert->conversion_runner->n_threads; i++) {
2509     if (convert->upsample_p && convert->upsample_p[i])
2510       gst_video_chroma_resample_free (convert->upsample_p[i]);
2511     if (convert->upsample_i && convert->upsample_i[i])
2512       gst_video_chroma_resample_free (convert->upsample_i[i]);
2513     if (convert->downsample_p && convert->downsample_p[i])
2514       gst_video_chroma_resample_free (convert->downsample_p[i]);
2515     if (convert->downsample_i && convert->downsample_i[i])
2516       gst_video_chroma_resample_free (convert->downsample_i[i]);
2517     if (convert->v_scaler_p && convert->v_scaler_p[i])
2518       gst_video_scaler_free (convert->v_scaler_p[i]);
2519     if (convert->v_scaler_i && convert->v_scaler_i[i])
2520       gst_video_scaler_free (convert->v_scaler_i[i]);
2521     if (convert->h_scaler && convert->h_scaler[i])
2522       gst_video_scaler_free (convert->h_scaler[i]);
2523     if (convert->unpack_lines && convert->unpack_lines[i])
2524       gst_line_cache_free (convert->unpack_lines[i]);
2525     if (convert->upsample_lines && convert->upsample_lines[i])
2526       gst_line_cache_free (convert->upsample_lines[i]);
2527     if (convert->to_RGB_lines && convert->to_RGB_lines[i])
2528       gst_line_cache_free (convert->to_RGB_lines[i]);
2529     if (convert->hscale_lines && convert->hscale_lines[i])
2530       gst_line_cache_free (convert->hscale_lines[i]);
2531     if (convert->vscale_lines && convert->vscale_lines[i])
2532       gst_line_cache_free (convert->vscale_lines[i]);
2533     if (convert->convert_lines && convert->convert_lines[i])
2534       gst_line_cache_free (convert->convert_lines[i]);
2535     if (convert->alpha_lines && convert->alpha_lines[i])
2536       gst_line_cache_free (convert->alpha_lines[i]);
2537     if (convert->to_YUV_lines && convert->to_YUV_lines[i])
2538       gst_line_cache_free (convert->to_YUV_lines[i]);
2539     if (convert->downsample_lines && convert->downsample_lines[i])
2540       gst_line_cache_free (convert->downsample_lines[i]);
2541     if (convert->dither_lines && convert->dither_lines[i])
2542       gst_line_cache_free (convert->dither_lines[i]);
2543     if (convert->dither && convert->dither[i])
2544       gst_video_dither_free (convert->dither[i]);
2545   }
2546   g_free (convert->upsample_p);
2547   g_free (convert->upsample_i);
2548   g_free (convert->downsample_p);
2549   g_free (convert->downsample_i);
2550   g_free (convert->v_scaler_p);
2551   g_free (convert->v_scaler_i);
2552   g_free (convert->h_scaler);
2553   g_free (convert->unpack_lines);
2554   g_free (convert->pack_lines);
2555   g_free (convert->upsample_lines);
2556   g_free (convert->to_RGB_lines);
2557   g_free (convert->hscale_lines);
2558   g_free (convert->vscale_lines);
2559   g_free (convert->convert_lines);
2560   g_free (convert->alpha_lines);
2561   g_free (convert->to_YUV_lines);
2562   g_free (convert->downsample_lines);
2563   g_free (convert->dither_lines);
2564   g_free (convert->dither);
2565 
2566   g_free (convert->gamma_dec.gamma_table);
2567   g_free (convert->gamma_enc.gamma_table);
2568 
2569   if (convert->tmpline) {
2570     for (i = 0; i < convert->conversion_runner->n_threads; i++)
2571       g_free (convert->tmpline[i]);
2572     g_free (convert->tmpline);
2573   }
2574 
2575   g_free (convert->borderline);
2576 
2577   if (convert->config)
2578     gst_structure_free (convert->config);
2579 
2580   for (i = 0; i < 4; i++) {
2581     for (j = 0; j < convert->conversion_runner->n_threads; j++) {
2582       if (convert->fv_scaler[i].scaler)
2583         gst_video_scaler_free (convert->fv_scaler[i].scaler[j]);
2584       if (convert->fh_scaler[i].scaler)
2585         gst_video_scaler_free (convert->fh_scaler[i].scaler[j]);
2586     }
2587     g_free (convert->fv_scaler[i].scaler);
2588     g_free (convert->fh_scaler[i].scaler);
2589   }
2590 
2591   if (convert->conversion_runner)
2592     gst_parallelized_task_runner_free (convert->conversion_runner);
2593 
2594   clear_matrix_data (&convert->to_RGB_matrix);
2595   clear_matrix_data (&convert->convert_matrix);
2596   clear_matrix_data (&convert->to_YUV_matrix);
2597 
2598   g_slice_free (GstVideoConverter, convert);
2599 }
2600 
2601 static gboolean
copy_config(GQuark field_id,const GValue * value,gpointer user_data)2602 copy_config (GQuark field_id, const GValue * value, gpointer user_data)
2603 {
2604   GstVideoConverter *convert = user_data;
2605 
2606   gst_structure_id_set_value (convert->config, field_id, value);
2607 
2608   return TRUE;
2609 }
2610 
2611 /**
2612  * gst_video_converter_set_config:
2613  * @convert: a #GstVideoConverter
2614  * @config: (transfer full): a #GstStructure
2615  *
2616  * Set @config as extra configuraion for @convert.
2617  *
2618  * If the parameters in @config can not be set exactly, this function returns
2619  * %FALSE and will try to update as much state as possible. The new state can
2620  * then be retrieved and refined with gst_video_converter_get_config().
2621  *
2622  * Look at the #GST_VIDEO_CONVERTER_OPT_* fields to check valid configuration
2623  * option and values.
2624  *
2625  * Returns: %TRUE when @config could be set.
2626  *
2627  * Since: 1.6
2628  */
2629 gboolean
gst_video_converter_set_config(GstVideoConverter * convert,GstStructure * config)2630 gst_video_converter_set_config (GstVideoConverter * convert,
2631     GstStructure * config)
2632 {
2633   g_return_val_if_fail (convert != NULL, FALSE);
2634   g_return_val_if_fail (config != NULL, FALSE);
2635 
2636   gst_structure_foreach (config, copy_config, convert);
2637   gst_structure_free (config);
2638 
2639   return TRUE;
2640 }
2641 
2642 /**
2643  * gst_video_converter_get_config:
2644  * @convert: a #GstVideoConverter
2645  *
2646  * Get the current configuration of @convert.
2647  *
2648  * Returns: a #GstStructure that remains valid for as long as @convert is valid
2649  *   or until gst_video_converter_set_config() is called.
2650  */
2651 const GstStructure *
gst_video_converter_get_config(GstVideoConverter * convert)2652 gst_video_converter_get_config (GstVideoConverter * convert)
2653 {
2654   g_return_val_if_fail (convert != NULL, NULL);
2655 
2656   return convert->config;
2657 }
2658 
2659 /**
2660  * gst_video_converter_frame:
2661  * @convert: a #GstVideoConverter
2662  * @dest: a #GstVideoFrame
2663  * @src: a #GstVideoFrame
2664  *
2665  * Convert the pixels of @src into @dest using @convert.
2666  *
2667  * Since: 1.6
2668  */
2669 void
gst_video_converter_frame(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)2670 gst_video_converter_frame (GstVideoConverter * convert,
2671     const GstVideoFrame * src, GstVideoFrame * dest)
2672 {
2673   g_return_if_fail (convert != NULL);
2674   g_return_if_fail (src != NULL);
2675   g_return_if_fail (dest != NULL);
2676 
2677   convert->convert (convert, src, dest);
2678 }
2679 
2680 static void
video_converter_compute_matrix(GstVideoConverter * convert)2681 video_converter_compute_matrix (GstVideoConverter * convert)
2682 {
2683   MatrixData *dst = &convert->convert_matrix;
2684 
2685   color_matrix_set_identity (dst);
2686   compute_matrix_to_RGB (convert, dst);
2687   compute_matrix_to_YUV (convert, dst, FALSE);
2688 
2689   convert->current_bits = 8;
2690   prepare_matrix (convert, dst);
2691 }
2692 
2693 static void
video_converter_compute_resample(GstVideoConverter * convert,gint idx)2694 video_converter_compute_resample (GstVideoConverter * convert, gint idx)
2695 {
2696   GstVideoInfo *in_info, *out_info;
2697   const GstVideoFormatInfo *sfinfo, *dfinfo;
2698 
2699   if (CHECK_CHROMA_NONE (convert))
2700     return;
2701 
2702   in_info = &convert->in_info;
2703   out_info = &convert->out_info;
2704 
2705   sfinfo = in_info->finfo;
2706   dfinfo = out_info->finfo;
2707 
2708   GST_DEBUG ("site: %d->%d, w_sub: %d->%d, h_sub: %d->%d", in_info->chroma_site,
2709       out_info->chroma_site, sfinfo->w_sub[2], dfinfo->w_sub[2],
2710       sfinfo->h_sub[2], dfinfo->h_sub[2]);
2711 
2712   if (sfinfo->w_sub[2] != dfinfo->w_sub[2] ||
2713       sfinfo->h_sub[2] != dfinfo->h_sub[2] ||
2714       in_info->chroma_site != out_info->chroma_site ||
2715       in_info->width != out_info->width ||
2716       in_info->height != out_info->height) {
2717     if (GST_VIDEO_INFO_IS_INTERLACED (in_info)) {
2718       if (!CHECK_CHROMA_DOWNSAMPLE (convert))
2719         convert->upsample_i[idx] = gst_video_chroma_resample_new (0,
2720             in_info->chroma_site, GST_VIDEO_CHROMA_FLAG_INTERLACED,
2721             sfinfo->unpack_format, sfinfo->w_sub[2], sfinfo->h_sub[2]);
2722       if (!CHECK_CHROMA_UPSAMPLE (convert))
2723         convert->downsample_i[idx] =
2724             gst_video_chroma_resample_new (0, out_info->chroma_site,
2725             GST_VIDEO_CHROMA_FLAG_INTERLACED, dfinfo->unpack_format,
2726             -dfinfo->w_sub[2], -dfinfo->h_sub[2]);
2727     }
2728     if (!CHECK_CHROMA_DOWNSAMPLE (convert))
2729       convert->upsample_p[idx] = gst_video_chroma_resample_new (0,
2730           in_info->chroma_site, 0, sfinfo->unpack_format, sfinfo->w_sub[2],
2731           sfinfo->h_sub[2]);
2732     if (!CHECK_CHROMA_UPSAMPLE (convert))
2733       convert->downsample_p[idx] = gst_video_chroma_resample_new (0,
2734           out_info->chroma_site, 0, dfinfo->unpack_format, -dfinfo->w_sub[2],
2735           -dfinfo->h_sub[2]);
2736   }
2737 }
2738 
2739 #define FRAME_GET_PLANE_STRIDE(frame, plane) \
2740   GST_VIDEO_FRAME_PLANE_STRIDE (frame, plane)
2741 #define FRAME_GET_PLANE_LINE(frame, plane, line) \
2742   (gpointer)(((guint8*)(GST_VIDEO_FRAME_PLANE_DATA (frame, plane))) + \
2743       FRAME_GET_PLANE_STRIDE (frame, plane) * (line))
2744 
2745 #define FRAME_GET_COMP_STRIDE(frame, comp) \
2746   GST_VIDEO_FRAME_COMP_STRIDE (frame, comp)
2747 #define FRAME_GET_COMP_LINE(frame, comp, line) \
2748   (gpointer)(((guint8*)(GST_VIDEO_FRAME_COMP_DATA (frame, comp))) + \
2749       FRAME_GET_COMP_STRIDE (frame, comp) * (line))
2750 
2751 #define FRAME_GET_STRIDE(frame)      FRAME_GET_PLANE_STRIDE (frame, 0)
2752 #define FRAME_GET_LINE(frame,line)   FRAME_GET_PLANE_LINE (frame, 0, line)
2753 
2754 #define FRAME_GET_Y_LINE(frame,line) FRAME_GET_COMP_LINE(frame, GST_VIDEO_COMP_Y, line)
2755 #define FRAME_GET_U_LINE(frame,line) FRAME_GET_COMP_LINE(frame, GST_VIDEO_COMP_U, line)
2756 #define FRAME_GET_V_LINE(frame,line) FRAME_GET_COMP_LINE(frame, GST_VIDEO_COMP_V, line)
2757 #define FRAME_GET_A_LINE(frame,line) FRAME_GET_COMP_LINE(frame, GST_VIDEO_COMP_A, line)
2758 
2759 #define FRAME_GET_Y_STRIDE(frame)    FRAME_GET_COMP_STRIDE(frame, GST_VIDEO_COMP_Y)
2760 #define FRAME_GET_U_STRIDE(frame)    FRAME_GET_COMP_STRIDE(frame, GST_VIDEO_COMP_U)
2761 #define FRAME_GET_V_STRIDE(frame)    FRAME_GET_COMP_STRIDE(frame, GST_VIDEO_COMP_V)
2762 #define FRAME_GET_A_STRIDE(frame)    FRAME_GET_COMP_STRIDE(frame, GST_VIDEO_COMP_A)
2763 
2764 
2765 #define UNPACK_FRAME(frame,dest,line,x,width)        \
2766   frame->info.finfo->unpack_func (frame->info.finfo, \
2767       (GST_VIDEO_FRAME_IS_INTERLACED (frame) ?       \
2768         GST_VIDEO_PACK_FLAG_INTERLACED :             \
2769         GST_VIDEO_PACK_FLAG_NONE),                   \
2770       dest, frame->data, frame->info.stride, x,      \
2771       line, width)
2772 #define PACK_FRAME(frame,src,line,width)             \
2773   frame->info.finfo->pack_func (frame->info.finfo,   \
2774       (GST_VIDEO_FRAME_IS_INTERLACED (frame) ?       \
2775         GST_VIDEO_PACK_FLAG_INTERLACED :             \
2776         GST_VIDEO_PACK_FLAG_NONE),                   \
2777       src, 0, frame->data, frame->info.stride,       \
2778       frame->info.chroma_site, line, width);
2779 
2780 static gpointer
get_dest_line(GstLineCache * cache,gint idx,gpointer user_data)2781 get_dest_line (GstLineCache * cache, gint idx, gpointer user_data)
2782 {
2783   GstVideoConverter *convert = user_data;
2784   guint8 *line;
2785   gint pstride = convert->pack_pstride;
2786   gint out_x = convert->out_x;
2787   guint cline;
2788 
2789   cline = CLAMP (idx, 0, convert->out_maxheight - 1);
2790 
2791   line = FRAME_GET_LINE (convert->dest, cline);
2792   GST_DEBUG ("get dest line %d %p", cline, line);
2793 
2794   if (convert->borderline) {
2795     gint r_border = (out_x + convert->out_width) * pstride;
2796     gint rb_width = convert->out_maxwidth * pstride - r_border;
2797     gint lb_width = out_x * pstride;
2798 
2799     memcpy (line, convert->borderline, lb_width);
2800     memcpy (line + r_border, convert->borderline, rb_width);
2801   }
2802   line += out_x * pstride;
2803 
2804   return line;
2805 }
2806 
2807 static gboolean
do_unpack_lines(GstLineCache * cache,gint idx,gint out_line,gint in_line,gpointer user_data)2808 do_unpack_lines (GstLineCache * cache, gint idx, gint out_line, gint in_line,
2809     gpointer user_data)
2810 {
2811   GstVideoConverter *convert = user_data;
2812   gpointer tmpline;
2813   guint cline;
2814 
2815   cline = CLAMP (in_line + convert->in_y, 0, convert->in_maxheight - 1);
2816 
2817   if (cache->alloc_writable || !convert->identity_unpack) {
2818     tmpline = gst_line_cache_alloc_line (cache, out_line);
2819     GST_DEBUG ("unpack line %d (%u) %p", in_line, cline, tmpline);
2820     UNPACK_FRAME (convert->src, tmpline, cline, convert->in_x,
2821         convert->in_width);
2822   } else {
2823     tmpline = ((guint8 *) FRAME_GET_LINE (convert->src, cline)) +
2824         convert->in_x * convert->unpack_pstride;
2825     GST_DEBUG ("get src line %d (%u) %p", in_line, cline, tmpline);
2826   }
2827   gst_line_cache_add_line (cache, in_line, tmpline);
2828 
2829   return TRUE;
2830 }
2831 
2832 static gboolean
do_upsample_lines(GstLineCache * cache,gint idx,gint out_line,gint in_line,gpointer user_data)2833 do_upsample_lines (GstLineCache * cache, gint idx, gint out_line, gint in_line,
2834     gpointer user_data)
2835 {
2836   GstVideoConverter *convert = user_data;
2837   gpointer *lines;
2838   gint i, start_line, n_lines;
2839 
2840   n_lines = convert->up_n_lines;
2841   start_line = in_line;
2842   if (start_line < n_lines + convert->up_offset) {
2843     start_line += convert->up_offset;
2844     out_line += convert->up_offset;
2845   }
2846 
2847   /* get the lines needed for chroma upsample */
2848   lines =
2849       gst_line_cache_get_lines (cache->prev, idx, out_line, start_line,
2850       n_lines);
2851 
2852   if (convert->upsample) {
2853     GST_DEBUG ("doing upsample %d-%d %p", start_line, start_line + n_lines - 1,
2854         lines[0]);
2855     gst_video_chroma_resample (convert->upsample[idx], lines,
2856         convert->in_width);
2857   }
2858 
2859   for (i = 0; i < n_lines; i++)
2860     gst_line_cache_add_line (cache, start_line + i, lines[i]);
2861 
2862   return TRUE;
2863 }
2864 
2865 static gboolean
do_convert_to_RGB_lines(GstLineCache * cache,gint idx,gint out_line,gint in_line,gpointer user_data)2866 do_convert_to_RGB_lines (GstLineCache * cache, gint idx, gint out_line,
2867     gint in_line, gpointer user_data)
2868 {
2869   GstVideoConverter *convert = user_data;
2870   MatrixData *data = &convert->to_RGB_matrix;
2871   gpointer *lines, destline;
2872 
2873   lines = gst_line_cache_get_lines (cache->prev, idx, out_line, in_line, 1);
2874   destline = lines[0];
2875 
2876   if (data->matrix_func) {
2877     GST_DEBUG ("to RGB line %d %p", in_line, destline);
2878     data->matrix_func (data, destline);
2879   }
2880   if (convert->gamma_dec.gamma_func) {
2881     destline = gst_line_cache_alloc_line (cache, out_line);
2882 
2883     GST_DEBUG ("gamma decode line %d %p->%p", in_line, lines[0], destline);
2884     convert->gamma_dec.gamma_func (&convert->gamma_dec, destline, lines[0]);
2885   }
2886   gst_line_cache_add_line (cache, in_line, destline);
2887 
2888   return TRUE;
2889 }
2890 
2891 static gboolean
do_hscale_lines(GstLineCache * cache,gint idx,gint out_line,gint in_line,gpointer user_data)2892 do_hscale_lines (GstLineCache * cache, gint idx, gint out_line, gint in_line,
2893     gpointer user_data)
2894 {
2895   GstVideoConverter *convert = user_data;
2896   gpointer *lines, destline;
2897 
2898   lines = gst_line_cache_get_lines (cache->prev, idx, out_line, in_line, 1);
2899 
2900   destline = gst_line_cache_alloc_line (cache, out_line);
2901 
2902   GST_DEBUG ("hresample line %d %p->%p", in_line, lines[0], destline);
2903   gst_video_scaler_horizontal (convert->h_scaler[idx], convert->h_scale_format,
2904       lines[0], destline, 0, convert->out_width);
2905 
2906   gst_line_cache_add_line (cache, in_line, destline);
2907 
2908   return TRUE;
2909 }
2910 
2911 static gboolean
do_vscale_lines(GstLineCache * cache,gint idx,gint out_line,gint in_line,gpointer user_data)2912 do_vscale_lines (GstLineCache * cache, gint idx, gint out_line, gint in_line,
2913     gpointer user_data)
2914 {
2915   GstVideoConverter *convert = user_data;
2916   gpointer *lines, destline;
2917   guint sline, n_lines;
2918   guint cline;
2919 
2920   cline = CLAMP (in_line, 0, convert->out_height - 1);
2921 
2922   gst_video_scaler_get_coeff (convert->v_scaler[idx], cline, &sline, &n_lines);
2923   lines = gst_line_cache_get_lines (cache->prev, idx, out_line, sline, n_lines);
2924 
2925   destline = gst_line_cache_alloc_line (cache, out_line);
2926 
2927   GST_DEBUG ("vresample line %d %d-%d %p->%p", in_line, sline,
2928       sline + n_lines - 1, lines[0], destline);
2929   gst_video_scaler_vertical (convert->v_scaler[idx], convert->v_scale_format,
2930       lines, destline, cline, convert->v_scale_width);
2931 
2932   gst_line_cache_add_line (cache, in_line, destline);
2933 
2934   return TRUE;
2935 }
2936 
2937 static gboolean
do_convert_lines(GstLineCache * cache,gint idx,gint out_line,gint in_line,gpointer user_data)2938 do_convert_lines (GstLineCache * cache, gint idx, gint out_line, gint in_line,
2939     gpointer user_data)
2940 {
2941   GstVideoConverter *convert = user_data;
2942   MatrixData *data = &convert->convert_matrix;
2943   gpointer *lines, destline;
2944   guint in_bits, out_bits;
2945   gint width;
2946 
2947   lines = gst_line_cache_get_lines (cache->prev, idx, out_line, in_line, 1);
2948 
2949   destline = lines[0];
2950 
2951   in_bits = convert->in_bits;
2952   out_bits = convert->out_bits;
2953 
2954   width = MIN (convert->in_width, convert->out_width);
2955 
2956   if (out_bits == 16 || in_bits == 16) {
2957     gpointer srcline = lines[0];
2958 
2959     if (out_bits != in_bits)
2960       destline = gst_line_cache_alloc_line (cache, out_line);
2961 
2962     /* FIXME, we can scale in the conversion matrix */
2963     if (in_bits == 8) {
2964       GST_DEBUG ("8->16 line %d %p->%p", in_line, srcline, destline);
2965       video_orc_convert_u8_to_u16 (destline, srcline, width * 4);
2966       srcline = destline;
2967     }
2968 
2969     if (data->matrix_func) {
2970       GST_DEBUG ("matrix line %d %p", in_line, srcline);
2971       data->matrix_func (data, srcline);
2972     }
2973 
2974     /* FIXME, dither here */
2975     if (out_bits == 8) {
2976       GST_DEBUG ("16->8 line %d %p->%p", in_line, srcline, destline);
2977       video_orc_convert_u16_to_u8 (destline, srcline, width * 4);
2978     }
2979   } else {
2980     if (data->matrix_func) {
2981       GST_DEBUG ("matrix line %d %p", in_line, destline);
2982       data->matrix_func (data, destline);
2983     }
2984   }
2985   gst_line_cache_add_line (cache, in_line, destline);
2986 
2987   return TRUE;
2988 }
2989 
2990 static gboolean
do_alpha_lines(GstLineCache * cache,gint idx,gint out_line,gint in_line,gpointer user_data)2991 do_alpha_lines (GstLineCache * cache, gint idx, gint out_line, gint in_line,
2992     gpointer user_data)
2993 {
2994   gpointer *lines, destline;
2995   GstVideoConverter *convert = user_data;
2996   gint width = MIN (convert->in_width, convert->out_width);
2997 
2998   lines = gst_line_cache_get_lines (cache->prev, idx, out_line, in_line, 1);
2999   destline = lines[0];
3000 
3001   GST_DEBUG ("alpha line %d %p", in_line, destline);
3002   convert->alpha_func (convert, destline, width);
3003 
3004   gst_line_cache_add_line (cache, in_line, destline);
3005 
3006   return TRUE;
3007 }
3008 
3009 static gboolean
do_convert_to_YUV_lines(GstLineCache * cache,gint idx,gint out_line,gint in_line,gpointer user_data)3010 do_convert_to_YUV_lines (GstLineCache * cache, gint idx, gint out_line,
3011     gint in_line, gpointer user_data)
3012 {
3013   GstVideoConverter *convert = user_data;
3014   MatrixData *data = &convert->to_YUV_matrix;
3015   gpointer *lines, destline;
3016 
3017   lines = gst_line_cache_get_lines (cache->prev, idx, out_line, in_line, 1);
3018   destline = lines[0];
3019 
3020   if (convert->gamma_enc.gamma_func) {
3021     destline = gst_line_cache_alloc_line (cache, out_line);
3022 
3023     GST_DEBUG ("gamma encode line %d %p->%p", in_line, lines[0], destline);
3024     convert->gamma_enc.gamma_func (&convert->gamma_enc, destline, lines[0]);
3025   }
3026   if (data->matrix_func) {
3027     GST_DEBUG ("to YUV line %d %p", in_line, destline);
3028     data->matrix_func (data, destline);
3029   }
3030   gst_line_cache_add_line (cache, in_line, destline);
3031 
3032   return TRUE;
3033 }
3034 
3035 static gboolean
do_downsample_lines(GstLineCache * cache,gint idx,gint out_line,gint in_line,gpointer user_data)3036 do_downsample_lines (GstLineCache * cache, gint idx, gint out_line,
3037     gint in_line, gpointer user_data)
3038 {
3039   GstVideoConverter *convert = user_data;
3040   gpointer *lines;
3041   gint i, start_line, n_lines;
3042 
3043   n_lines = convert->down_n_lines;
3044   start_line = in_line;
3045   if (start_line < n_lines + convert->down_offset)
3046     start_line += convert->down_offset;
3047 
3048   /* get the lines needed for chroma downsample */
3049   lines =
3050       gst_line_cache_get_lines (cache->prev, idx, out_line, start_line,
3051       n_lines);
3052 
3053   if (convert->downsample) {
3054     GST_DEBUG ("downsample line %d %d-%d %p", in_line, start_line,
3055         start_line + n_lines - 1, lines[0]);
3056     gst_video_chroma_resample (convert->downsample[idx], lines,
3057         convert->out_width);
3058   }
3059 
3060   for (i = 0; i < n_lines; i++)
3061     gst_line_cache_add_line (cache, start_line + i, lines[i]);
3062 
3063   return TRUE;
3064 }
3065 
3066 static gboolean
do_dither_lines(GstLineCache * cache,gint idx,gint out_line,gint in_line,gpointer user_data)3067 do_dither_lines (GstLineCache * cache, gint idx, gint out_line, gint in_line,
3068     gpointer user_data)
3069 {
3070   GstVideoConverter *convert = user_data;
3071   gpointer *lines, destline;
3072 
3073   lines = gst_line_cache_get_lines (cache->prev, idx, out_line, in_line, 1);
3074   destline = lines[0];
3075 
3076   if (convert->dither) {
3077     GST_DEBUG ("Dither line %d %p", in_line, destline);
3078     gst_video_dither_line (convert->dither[idx], destline, 0, out_line,
3079         convert->out_width);
3080   }
3081   gst_line_cache_add_line (cache, in_line, destline);
3082 
3083   return TRUE;
3084 }
3085 
3086 typedef struct
3087 {
3088   GstLineCache *pack_lines;
3089   gint idx;
3090   gint h_0, h_1;
3091   gint pack_lines_count;
3092   gint out_y;
3093   gboolean identity_pack;
3094   gint lb_width, out_maxwidth;
3095   GstVideoFrame *dest;
3096 } ConvertTask;
3097 
3098 static void
convert_generic_task(ConvertTask * task)3099 convert_generic_task (ConvertTask * task)
3100 {
3101   gint i;
3102 
3103   for (i = task->h_0; i < task->h_1; i += task->pack_lines_count) {
3104     gpointer *lines;
3105 
3106     /* load the lines needed to pack */
3107     lines =
3108         gst_line_cache_get_lines (task->pack_lines, task->idx, i + task->out_y,
3109         i, task->pack_lines_count);
3110 
3111     if (!task->identity_pack) {
3112       /* take away the border */
3113       guint8 *l = ((guint8 *) lines[0]) - task->lb_width;
3114       /* and pack into destination */
3115       GST_DEBUG ("pack line %d %p (%p)", i + task->out_y, lines[0], l);
3116       PACK_FRAME (task->dest, l, i + task->out_y, task->out_maxwidth);
3117     }
3118   }
3119 }
3120 
3121 static void
video_converter_generic(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)3122 video_converter_generic (GstVideoConverter * convert, const GstVideoFrame * src,
3123     GstVideoFrame * dest)
3124 {
3125   gint i;
3126   gint out_maxwidth, out_maxheight;
3127   gint out_x, out_y, out_height;
3128   gint pack_lines, pstride;
3129   gint lb_width;
3130   ConvertTask *tasks;
3131   ConvertTask **tasks_p;
3132   gint n_threads;
3133   gint lines_per_thread;
3134 
3135   out_height = convert->out_height;
3136   out_maxwidth = convert->out_maxwidth;
3137   out_maxheight = convert->out_maxheight;
3138 
3139   out_x = convert->out_x;
3140   out_y = convert->out_y;
3141 
3142   convert->src = src;
3143   convert->dest = dest;
3144 
3145   if (GST_VIDEO_FRAME_IS_INTERLACED (src)) {
3146     GST_DEBUG ("setup interlaced frame");
3147     convert->upsample = convert->upsample_i;
3148     convert->downsample = convert->downsample_i;
3149     convert->v_scaler = convert->v_scaler_i;
3150   } else {
3151     GST_DEBUG ("setup progressive frame");
3152     convert->upsample = convert->upsample_p;
3153     convert->downsample = convert->downsample_p;
3154     convert->v_scaler = convert->v_scaler_p;
3155   }
3156   if (convert->upsample[0]) {
3157     gst_video_chroma_resample_get_info (convert->upsample[0],
3158         &convert->up_n_lines, &convert->up_offset);
3159   } else {
3160     convert->up_n_lines = 1;
3161     convert->up_offset = 0;
3162   }
3163   if (convert->downsample[0]) {
3164     gst_video_chroma_resample_get_info (convert->downsample[0],
3165         &convert->down_n_lines, &convert->down_offset);
3166   } else {
3167     convert->down_n_lines = 1;
3168     convert->down_offset = 0;
3169   }
3170 
3171   pack_lines = convert->pack_nlines;    /* only 1 for now */
3172   pstride = convert->pack_pstride;
3173 
3174   lb_width = out_x * pstride;
3175 
3176   if (convert->borderline) {
3177     /* FIXME we should try to avoid PACK_FRAME */
3178     for (i = 0; i < out_y; i++)
3179       PACK_FRAME (dest, convert->borderline, i, out_maxwidth);
3180   }
3181 
3182   n_threads = convert->conversion_runner->n_threads;
3183   tasks = g_newa (ConvertTask, n_threads);
3184   tasks_p = g_newa (ConvertTask *, n_threads);
3185 
3186   lines_per_thread =
3187       GST_ROUND_UP_N ((out_height + n_threads - 1) / n_threads, pack_lines);
3188 
3189   for (i = 0; i < n_threads; i++) {
3190     tasks[i].dest = dest;
3191     tasks[i].pack_lines = convert->pack_lines[i];
3192     tasks[i].idx = i;
3193     tasks[i].pack_lines_count = pack_lines;
3194     tasks[i].out_y = out_y;
3195     tasks[i].identity_pack = convert->identity_pack;
3196     tasks[i].lb_width = lb_width;
3197     tasks[i].out_maxwidth = out_maxwidth;
3198 
3199     tasks[i].h_0 = i * lines_per_thread;
3200     tasks[i].h_1 = MIN ((i + 1) * lines_per_thread, out_height);
3201 
3202     tasks_p[i] = &tasks[i];
3203   }
3204 
3205   gst_parallelized_task_runner_run (convert->conversion_runner,
3206       (GstParallelizedTaskFunc) convert_generic_task, (gpointer) tasks_p);
3207 
3208   if (convert->borderline) {
3209     for (i = out_y + out_height; i < out_maxheight; i++)
3210       PACK_FRAME (dest, convert->borderline, i, out_maxwidth);
3211   }
3212   if (convert->pack_pal) {
3213     memcpy (GST_VIDEO_FRAME_PLANE_DATA (dest, 1), convert->pack_pal,
3214         convert->pack_palsize);
3215   }
3216 }
3217 
3218 static void convert_fill_border (GstVideoConverter * convert,
3219     GstVideoFrame * dest);
3220 
3221 /* Fast paths */
3222 
3223 #define GET_LINE_OFFSETS(interlaced,line,l1,l2) \
3224     if (interlaced) {                           \
3225       l1 = (line & 2 ? line - 1 : line);        \
3226       l2 = l1 + 2;                              \
3227     } else {                                    \
3228       l1 = line;                                \
3229       l2 = l1 + 1;                              \
3230     }
3231 
3232 typedef struct
3233 {
3234   const GstVideoFrame *src;
3235   GstVideoFrame *dest;
3236   gint height_0, height_1;
3237 
3238   /* parameters */
3239   gboolean interlaced;
3240   gint width;
3241   gint alpha;
3242   MatrixData *data;
3243   gint in_x, in_y;
3244   gint out_x, out_y;
3245   gpointer tmpline;
3246 } FConvertTask;
3247 
3248 static void
convert_I420_YUY2_task(FConvertTask * task)3249 convert_I420_YUY2_task (FConvertTask * task)
3250 {
3251   gint i;
3252   gint l1, l2;
3253 
3254   for (i = task->height_0; i < task->height_1; i += 2) {
3255     GET_LINE_OFFSETS (task->interlaced, i, l1, l2);
3256 
3257     video_orc_convert_I420_YUY2 (FRAME_GET_LINE (task->dest, l1),
3258         FRAME_GET_LINE (task->dest, l2),
3259         FRAME_GET_Y_LINE (task->src, l1),
3260         FRAME_GET_Y_LINE (task->src, l2),
3261         FRAME_GET_U_LINE (task->src, i >> 1),
3262         FRAME_GET_V_LINE (task->src, i >> 1), (task->width + 1) / 2);
3263   }
3264 }
3265 
3266 static void
convert_I420_YUY2(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)3267 convert_I420_YUY2 (GstVideoConverter * convert, const GstVideoFrame * src,
3268     GstVideoFrame * dest)
3269 {
3270   int i;
3271   gint width = convert->in_width;
3272   gint height = convert->in_height;
3273   gboolean interlaced = GST_VIDEO_FRAME_IS_INTERLACED (src);
3274   gint h2;
3275   FConvertTask *tasks;
3276   FConvertTask **tasks_p;
3277   gint n_threads;
3278   gint lines_per_thread;
3279 
3280   /* I420 has half as many chroma lines, as such we have to
3281    * always merge two into one. For non-interlaced these are
3282    * the two next to each other, for interlaced one is skipped
3283    * in between. */
3284   if (interlaced)
3285     h2 = GST_ROUND_DOWN_4 (height);
3286   else
3287     h2 = GST_ROUND_DOWN_2 (height);
3288 
3289   n_threads = convert->conversion_runner->n_threads;
3290   tasks = g_newa (FConvertTask, n_threads);
3291   tasks_p = g_newa (FConvertTask *, n_threads);
3292 
3293   lines_per_thread = GST_ROUND_UP_2 ((h2 + n_threads - 1) / n_threads);
3294 
3295   for (i = 0; i < n_threads; i++) {
3296     tasks[i].src = src;
3297     tasks[i].dest = dest;
3298 
3299     tasks[i].interlaced = interlaced;
3300     tasks[i].width = width;
3301 
3302     tasks[i].height_0 = i * lines_per_thread;
3303     tasks[i].height_1 = tasks[i].height_0 + lines_per_thread;
3304     tasks[i].height_1 = MIN (h2, tasks[i].height_1);
3305 
3306     tasks_p[i] = &tasks[i];
3307   }
3308 
3309   gst_parallelized_task_runner_run (convert->conversion_runner,
3310       (GstParallelizedTaskFunc) convert_I420_YUY2_task, (gpointer) tasks_p);
3311 
3312   /* now handle last lines. For interlaced these are up to 3 */
3313   if (h2 != height) {
3314     for (i = h2; i < height; i++) {
3315       UNPACK_FRAME (src, convert->tmpline[0], i, convert->in_x, width);
3316       PACK_FRAME (dest, convert->tmpline[0], i, width);
3317     }
3318   }
3319 }
3320 
3321 static void
convert_I420_UYVY_task(FConvertTask * task)3322 convert_I420_UYVY_task (FConvertTask * task)
3323 {
3324   gint i;
3325   gint l1, l2;
3326 
3327   for (i = task->height_0; i < task->height_1; i += 2) {
3328     GET_LINE_OFFSETS (task->interlaced, i, l1, l2);
3329 
3330     video_orc_convert_I420_UYVY (FRAME_GET_LINE (task->dest, l1),
3331         FRAME_GET_LINE (task->dest, l2),
3332         FRAME_GET_Y_LINE (task->src, l1),
3333         FRAME_GET_Y_LINE (task->src, l2),
3334         FRAME_GET_U_LINE (task->src, i >> 1),
3335         FRAME_GET_V_LINE (task->src, i >> 1), (task->width + 1) / 2);
3336   }
3337 }
3338 
3339 static void
convert_I420_UYVY(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)3340 convert_I420_UYVY (GstVideoConverter * convert, const GstVideoFrame * src,
3341     GstVideoFrame * dest)
3342 {
3343   int i;
3344   gint width = convert->in_width;
3345   gint height = convert->in_height;
3346   gboolean interlaced = GST_VIDEO_FRAME_IS_INTERLACED (src);
3347   gint h2;
3348   FConvertTask *tasks;
3349   FConvertTask **tasks_p;
3350   gint n_threads;
3351   gint lines_per_thread;
3352 
3353   /* I420 has half as many chroma lines, as such we have to
3354    * always merge two into one. For non-interlaced these are
3355    * the two next to each other, for interlaced one is skipped
3356    * in between. */
3357   if (interlaced)
3358     h2 = GST_ROUND_DOWN_4 (height);
3359   else
3360     h2 = GST_ROUND_DOWN_2 (height);
3361 
3362   n_threads = convert->conversion_runner->n_threads;
3363   tasks = g_newa (FConvertTask, n_threads);
3364   tasks_p = g_newa (FConvertTask *, n_threads);
3365 
3366   lines_per_thread = GST_ROUND_UP_2 ((h2 + n_threads - 1) / n_threads);
3367 
3368   for (i = 0; i < n_threads; i++) {
3369     tasks[i].src = src;
3370     tasks[i].dest = dest;
3371 
3372     tasks[i].interlaced = interlaced;
3373     tasks[i].width = width;
3374 
3375     tasks[i].height_0 = i * lines_per_thread;
3376     tasks[i].height_1 = tasks[i].height_0 + lines_per_thread;
3377     tasks[i].height_1 = MIN (h2, tasks[i].height_1);
3378 
3379     tasks_p[i] = &tasks[i];
3380   }
3381 
3382   gst_parallelized_task_runner_run (convert->conversion_runner,
3383       (GstParallelizedTaskFunc) convert_I420_UYVY_task, (gpointer) tasks_p);
3384 
3385   /* now handle last lines. For interlaced these are up to 3 */
3386   if (h2 != height) {
3387     for (i = h2; i < height; i++) {
3388       UNPACK_FRAME (src, convert->tmpline[0], i, convert->in_x, width);
3389       PACK_FRAME (dest, convert->tmpline[0], i, width);
3390     }
3391   }
3392 }
3393 
3394 static void
convert_I420_AYUV_task(FConvertTask * task)3395 convert_I420_AYUV_task (FConvertTask * task)
3396 {
3397   gint i;
3398   gint l1, l2;
3399 
3400   for (i = task->height_0; i < task->height_1; i += 2) {
3401     GET_LINE_OFFSETS (task->interlaced, i, l1, l2);
3402 
3403     video_orc_convert_I420_AYUV (FRAME_GET_LINE (task->dest, l1),
3404         FRAME_GET_LINE (task->dest, l2),
3405         FRAME_GET_Y_LINE (task->src, l1),
3406         FRAME_GET_Y_LINE (task->src, l2),
3407         FRAME_GET_U_LINE (task->src, i >> 1), FRAME_GET_V_LINE (task->src,
3408             i >> 1), task->alpha, task->width);
3409   }
3410 }
3411 
3412 static void
convert_I420_AYUV(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)3413 convert_I420_AYUV (GstVideoConverter * convert, const GstVideoFrame * src,
3414     GstVideoFrame * dest)
3415 {
3416   int i;
3417   gint width = convert->in_width;
3418   gint height = convert->in_height;
3419   gboolean interlaced = GST_VIDEO_FRAME_IS_INTERLACED (src);
3420   guint8 alpha = MIN (convert->alpha_value, 255);
3421   gint h2;
3422   FConvertTask *tasks;
3423   FConvertTask **tasks_p;
3424   gint n_threads;
3425   gint lines_per_thread;
3426 
3427   /* I420 has half as many chroma lines, as such we have to
3428    * always merge two into one. For non-interlaced these are
3429    * the two next to each other, for interlaced one is skipped
3430    * in between. */
3431   if (interlaced)
3432     h2 = GST_ROUND_DOWN_4 (height);
3433   else
3434     h2 = GST_ROUND_DOWN_2 (height);
3435 
3436 
3437   n_threads = convert->conversion_runner->n_threads;
3438   tasks = g_newa (FConvertTask, n_threads);
3439   tasks_p = g_newa (FConvertTask *, n_threads);
3440 
3441   lines_per_thread = GST_ROUND_UP_2 ((h2 + n_threads - 1) / n_threads);
3442 
3443   for (i = 0; i < n_threads; i++) {
3444     tasks[i].src = src;
3445     tasks[i].dest = dest;
3446 
3447     tasks[i].interlaced = interlaced;
3448     tasks[i].width = width;
3449     tasks[i].alpha = alpha;
3450 
3451     tasks[i].height_0 = i * lines_per_thread;
3452     tasks[i].height_1 = tasks[i].height_0 + lines_per_thread;
3453     tasks[i].height_1 = MIN (h2, tasks[i].height_1);
3454 
3455     tasks_p[i] = &tasks[i];
3456   }
3457 
3458   gst_parallelized_task_runner_run (convert->conversion_runner,
3459       (GstParallelizedTaskFunc) convert_I420_AYUV_task, (gpointer) tasks_p);
3460 
3461   /* now handle last lines. For interlaced these are up to 3 */
3462   if (h2 != height) {
3463     for (i = h2; i < height; i++) {
3464       UNPACK_FRAME (src, convert->tmpline[0], i, convert->in_x, width);
3465       if (alpha != 0xff)
3466         convert_set_alpha_u8 (convert, convert->tmpline[0], width);
3467       PACK_FRAME (dest, convert->tmpline[0], i, width);
3468     }
3469   }
3470 }
3471 
3472 static void
convert_YUY2_I420_task(FConvertTask * task)3473 convert_YUY2_I420_task (FConvertTask * task)
3474 {
3475   gint i;
3476   gint l1, l2;
3477 
3478   for (i = task->height_0; i < task->height_1; i += 2) {
3479     GET_LINE_OFFSETS (task->interlaced, i, l1, l2);
3480 
3481     video_orc_convert_YUY2_I420 (FRAME_GET_Y_LINE (task->dest, l1),
3482         FRAME_GET_Y_LINE (task->dest, l2),
3483         FRAME_GET_U_LINE (task->dest, i >> 1),
3484         FRAME_GET_V_LINE (task->dest, i >> 1),
3485         FRAME_GET_LINE (task->src, l1), FRAME_GET_LINE (task->src, l2),
3486         (task->width + 1) / 2);
3487   }
3488 }
3489 
3490 static void
convert_YUY2_I420(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)3491 convert_YUY2_I420 (GstVideoConverter * convert, const GstVideoFrame * src,
3492     GstVideoFrame * dest)
3493 {
3494   int i;
3495   gint width = convert->in_width;
3496   gint height = convert->in_height;
3497   gboolean interlaced = GST_VIDEO_FRAME_IS_INTERLACED (src);
3498   gint h2;
3499   FConvertTask *tasks;
3500   FConvertTask **tasks_p;
3501   gint n_threads;
3502   gint lines_per_thread;
3503 
3504   /* I420 has half as many chroma lines, as such we have to
3505    * always merge two into one. For non-interlaced these are
3506    * the two next to each other, for interlaced one is skipped
3507    * in between. */
3508   if (interlaced)
3509     h2 = GST_ROUND_DOWN_4 (height);
3510   else
3511     h2 = GST_ROUND_DOWN_2 (height);
3512 
3513   n_threads = convert->conversion_runner->n_threads;
3514   tasks = g_newa (FConvertTask, n_threads);
3515   tasks_p = g_newa (FConvertTask *, n_threads);
3516 
3517   lines_per_thread = GST_ROUND_UP_2 ((h2 + n_threads - 1) / n_threads);
3518 
3519   for (i = 0; i < n_threads; i++) {
3520     tasks[i].src = src;
3521     tasks[i].dest = dest;
3522 
3523     tasks[i].interlaced = interlaced;
3524     tasks[i].width = width;
3525 
3526     tasks[i].height_0 = i * lines_per_thread;
3527     tasks[i].height_1 = tasks[i].height_0 + lines_per_thread;
3528     tasks[i].height_1 = MIN (h2, tasks[i].height_1);
3529 
3530     tasks_p[i] = &tasks[i];
3531   }
3532 
3533   gst_parallelized_task_runner_run (convert->conversion_runner,
3534       (GstParallelizedTaskFunc) convert_YUY2_I420_task, (gpointer) tasks_p);
3535 
3536   /* now handle last lines. For interlaced these are up to 3 */
3537   if (h2 != height) {
3538     for (i = h2; i < height; i++) {
3539       UNPACK_FRAME (src, convert->tmpline[0], i, convert->in_x, width);
3540       PACK_FRAME (dest, convert->tmpline[0], i, width);
3541     }
3542   }
3543 }
3544 
3545 typedef struct
3546 {
3547   const guint8 *s, *s2, *su, *sv;
3548   guint8 *d, *d2, *du, *dv;
3549   gint sstride, sustride, svstride;
3550   gint dstride, dustride, dvstride;
3551   gint width, height;
3552   gint alpha;
3553   MatrixData *data;
3554 } FConvertPlaneTask;
3555 
3556 static void
convert_YUY2_AYUV_task(FConvertPlaneTask * task)3557 convert_YUY2_AYUV_task (FConvertPlaneTask * task)
3558 {
3559   video_orc_convert_YUY2_AYUV (task->d, task->dstride, task->s,
3560       task->sstride, task->alpha, (task->width + 1) / 2, task->height);
3561 }
3562 
3563 static void
convert_YUY2_AYUV(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)3564 convert_YUY2_AYUV (GstVideoConverter * convert, const GstVideoFrame * src,
3565     GstVideoFrame * dest)
3566 {
3567   gint width = convert->in_width;
3568   gint height = convert->in_height;
3569   guint8 *s, *d;
3570   guint8 alpha = MIN (convert->alpha_value, 255);
3571   FConvertPlaneTask *tasks;
3572   FConvertPlaneTask **tasks_p;
3573   gint n_threads;
3574   gint lines_per_thread;
3575   gint i;
3576 
3577   s = FRAME_GET_LINE (src, convert->in_y);
3578   s += (GST_ROUND_UP_2 (convert->in_x) * 2);
3579   d = FRAME_GET_LINE (dest, convert->out_y);
3580   d += (convert->out_x * 4);
3581 
3582   n_threads = convert->conversion_runner->n_threads;
3583   tasks = g_newa (FConvertPlaneTask, n_threads);
3584   tasks_p = g_newa (FConvertPlaneTask *, n_threads);
3585 
3586   lines_per_thread = (height + n_threads - 1) / n_threads;
3587 
3588   for (i = 0; i < n_threads; i++) {
3589     tasks[i].dstride = FRAME_GET_STRIDE (dest);
3590     tasks[i].sstride = FRAME_GET_STRIDE (src);
3591     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
3592     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
3593 
3594     tasks[i].width = width;
3595     tasks[i].height = (i + 1) * lines_per_thread;
3596     tasks[i].height = MIN (tasks[i].height, height);
3597     tasks[i].height -= i * lines_per_thread;
3598     tasks[i].alpha = alpha;
3599 
3600     tasks_p[i] = &tasks[i];
3601   }
3602 
3603   gst_parallelized_task_runner_run (convert->conversion_runner,
3604       (GstParallelizedTaskFunc) convert_YUY2_AYUV_task, (gpointer) tasks_p);
3605 
3606   convert_fill_border (convert, dest);
3607 }
3608 
3609 static void
convert_YUY2_Y42B_task(FConvertPlaneTask * task)3610 convert_YUY2_Y42B_task (FConvertPlaneTask * task)
3611 {
3612   video_orc_convert_YUY2_Y42B (task->d, task->dstride, task->du,
3613       task->dustride, task->dv, task->dvstride,
3614       task->s, task->sstride, (task->width + 1) / 2, task->height);
3615 }
3616 
3617 static void
convert_YUY2_Y42B(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)3618 convert_YUY2_Y42B (GstVideoConverter * convert, const GstVideoFrame * src,
3619     GstVideoFrame * dest)
3620 {
3621   gint width = convert->in_width;
3622   gint height = convert->in_height;
3623   guint8 *s, *dy, *du, *dv;
3624   FConvertPlaneTask *tasks;
3625   FConvertPlaneTask **tasks_p;
3626   gint n_threads;
3627   gint lines_per_thread;
3628   gint i;
3629 
3630   s = FRAME_GET_LINE (src, convert->in_y);
3631   s += (GST_ROUND_UP_2 (convert->in_x) * 2);
3632 
3633   dy = FRAME_GET_Y_LINE (dest, convert->out_y);
3634   dy += convert->out_x;
3635   du = FRAME_GET_U_LINE (dest, convert->out_y);
3636   du += convert->out_x >> 1;
3637   dv = FRAME_GET_V_LINE (dest, convert->out_y);
3638   dv += convert->out_x >> 1;
3639 
3640   n_threads = convert->conversion_runner->n_threads;
3641   tasks = g_newa (FConvertPlaneTask, n_threads);
3642   tasks_p = g_newa (FConvertPlaneTask *, n_threads);
3643 
3644   lines_per_thread = (height + n_threads - 1) / n_threads;
3645 
3646   for (i = 0; i < n_threads; i++) {
3647     tasks[i].dstride = FRAME_GET_Y_STRIDE (dest);
3648     tasks[i].dustride = FRAME_GET_U_STRIDE (dest);
3649     tasks[i].dvstride = FRAME_GET_V_STRIDE (dest);
3650     tasks[i].sstride = FRAME_GET_STRIDE (src);
3651     tasks[i].d = dy + i * lines_per_thread * tasks[i].dstride;
3652     tasks[i].du = du + i * lines_per_thread * tasks[i].dustride;
3653     tasks[i].dv = dv + i * lines_per_thread * tasks[i].dvstride;
3654     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
3655 
3656     tasks[i].width = width;
3657     tasks[i].height = (i + 1) * lines_per_thread;
3658     tasks[i].height = MIN (tasks[i].height, height);
3659     tasks[i].height -= i * lines_per_thread;
3660 
3661     tasks_p[i] = &tasks[i];
3662   }
3663 
3664   gst_parallelized_task_runner_run (convert->conversion_runner,
3665       (GstParallelizedTaskFunc) convert_YUY2_Y42B_task, (gpointer) tasks_p);
3666 
3667   convert_fill_border (convert, dest);
3668 }
3669 
3670 static void
convert_YUY2_Y444_task(FConvertPlaneTask * task)3671 convert_YUY2_Y444_task (FConvertPlaneTask * task)
3672 {
3673   video_orc_convert_YUY2_Y444 (task->d,
3674       task->dstride, task->du,
3675       task->dustride, task->dv,
3676       task->dvstride, task->s,
3677       task->sstride, (task->width + 1) / 2, task->height);
3678 }
3679 
3680 static void
convert_YUY2_Y444(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)3681 convert_YUY2_Y444 (GstVideoConverter * convert, const GstVideoFrame * src,
3682     GstVideoFrame * dest)
3683 {
3684   gint width = convert->in_width;
3685   gint height = convert->in_height;
3686   guint8 *s, *dy, *du, *dv;
3687   FConvertPlaneTask *tasks;
3688   FConvertPlaneTask **tasks_p;
3689   gint n_threads;
3690   gint lines_per_thread;
3691   gint i;
3692 
3693   s = FRAME_GET_LINE (src, convert->in_y);
3694   s += (GST_ROUND_UP_2 (convert->in_x) * 2);
3695 
3696   dy = FRAME_GET_Y_LINE (dest, convert->out_y);
3697   dy += convert->out_x;
3698   du = FRAME_GET_U_LINE (dest, convert->out_y);
3699   du += convert->out_x;
3700   dv = FRAME_GET_V_LINE (dest, convert->out_y);
3701   dv += convert->out_x;
3702 
3703   n_threads = convert->conversion_runner->n_threads;
3704   tasks = g_newa (FConvertPlaneTask, n_threads);
3705   tasks_p = g_newa (FConvertPlaneTask *, n_threads);
3706 
3707   lines_per_thread = (height + n_threads - 1) / n_threads;
3708 
3709   for (i = 0; i < n_threads; i++) {
3710     tasks[i].dstride = FRAME_GET_Y_STRIDE (dest);
3711     tasks[i].dustride = FRAME_GET_U_STRIDE (dest);
3712     tasks[i].dvstride = FRAME_GET_V_STRIDE (dest);
3713     tasks[i].sstride = FRAME_GET_STRIDE (src);
3714     tasks[i].d = dy + i * lines_per_thread * tasks[i].dstride;
3715     tasks[i].du = du + i * lines_per_thread * tasks[i].dustride;
3716     tasks[i].dv = dv + i * lines_per_thread * tasks[i].dvstride;
3717     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
3718 
3719     tasks[i].width = width;
3720     tasks[i].height = (i + 1) * lines_per_thread;
3721     tasks[i].height = MIN (tasks[i].height, height);
3722     tasks[i].height -= i * lines_per_thread;
3723 
3724     tasks_p[i] = &tasks[i];
3725   }
3726 
3727   gst_parallelized_task_runner_run (convert->conversion_runner,
3728       (GstParallelizedTaskFunc) convert_YUY2_Y444_task, (gpointer) tasks_p);
3729 
3730   convert_fill_border (convert, dest);
3731 }
3732 
3733 static void
convert_UYVY_I420_task(FConvertTask * task)3734 convert_UYVY_I420_task (FConvertTask * task)
3735 {
3736   gint i;
3737   gint l1, l2;
3738 
3739   for (i = task->height_0; i < task->height_1; i += 2) {
3740     GET_LINE_OFFSETS (task->interlaced, i, l1, l2);
3741 
3742     video_orc_convert_UYVY_I420 (FRAME_GET_COMP_LINE (task->dest, 0, l1),
3743         FRAME_GET_COMP_LINE (task->dest, 0, l2),
3744         FRAME_GET_COMP_LINE (task->dest, 1, i >> 1),
3745         FRAME_GET_COMP_LINE (task->dest, 2, i >> 1),
3746         FRAME_GET_LINE (task->src, l1), FRAME_GET_LINE (task->src, l2),
3747         (task->width + 1) / 2);
3748   }
3749 }
3750 
3751 static void
convert_UYVY_I420(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)3752 convert_UYVY_I420 (GstVideoConverter * convert, const GstVideoFrame * src,
3753     GstVideoFrame * dest)
3754 {
3755   int i;
3756   gint width = convert->in_width;
3757   gint height = convert->in_height;
3758   gboolean interlaced = GST_VIDEO_FRAME_IS_INTERLACED (src);
3759   gint h2;
3760   FConvertTask *tasks;
3761   FConvertTask **tasks_p;
3762   gint n_threads;
3763   gint lines_per_thread;
3764 
3765   /* I420 has half as many chroma lines, as such we have to
3766    * always merge two into one. For non-interlaced these are
3767    * the two next to each other, for interlaced one is skipped
3768    * in between. */
3769   if (interlaced)
3770     h2 = GST_ROUND_DOWN_4 (height);
3771   else
3772     h2 = GST_ROUND_DOWN_2 (height);
3773 
3774   n_threads = convert->conversion_runner->n_threads;
3775   tasks = g_newa (FConvertTask, n_threads);
3776   tasks_p = g_newa (FConvertTask *, n_threads);
3777 
3778   lines_per_thread = GST_ROUND_UP_2 ((h2 + n_threads - 1) / n_threads);
3779 
3780   for (i = 0; i < n_threads; i++) {
3781     tasks[i].src = src;
3782     tasks[i].dest = dest;
3783 
3784     tasks[i].interlaced = interlaced;
3785     tasks[i].width = width;
3786 
3787     tasks[i].height_0 = i * lines_per_thread;
3788     tasks[i].height_1 = tasks[i].height_0 + lines_per_thread;
3789     tasks[i].height_1 = MIN (h2, tasks[i].height_1);
3790 
3791     tasks_p[i] = &tasks[i];
3792   }
3793 
3794   gst_parallelized_task_runner_run (convert->conversion_runner,
3795       (GstParallelizedTaskFunc) convert_UYVY_I420_task, (gpointer) tasks_p);
3796 
3797   /* now handle last lines. For interlaced these are up to 3 */
3798   if (h2 != height) {
3799     for (i = h2; i < height; i++) {
3800       UNPACK_FRAME (src, convert->tmpline[0], i, convert->in_x, width);
3801       PACK_FRAME (dest, convert->tmpline[0], i, width);
3802     }
3803   }
3804 }
3805 
3806 static void
convert_UYVY_AYUV_task(FConvertPlaneTask * task)3807 convert_UYVY_AYUV_task (FConvertPlaneTask * task)
3808 {
3809   video_orc_convert_UYVY_AYUV (task->d, task->dstride, task->s,
3810       task->sstride, task->alpha, (task->width + 1) / 2, task->height);
3811 }
3812 
3813 static void
convert_UYVY_AYUV(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)3814 convert_UYVY_AYUV (GstVideoConverter * convert, const GstVideoFrame * src,
3815     GstVideoFrame * dest)
3816 {
3817   gint width = convert->in_width;
3818   gint height = convert->in_height;
3819   guint8 *s, *d;
3820   guint8 alpha = MIN (convert->alpha_value, 255);
3821   FConvertPlaneTask *tasks;
3822   FConvertPlaneTask **tasks_p;
3823   gint n_threads;
3824   gint lines_per_thread;
3825   gint i;
3826 
3827   s = FRAME_GET_LINE (src, convert->in_y);
3828   s += (GST_ROUND_UP_2 (convert->in_x) * 2);
3829   d = FRAME_GET_LINE (dest, convert->out_y);
3830   d += (convert->out_x * 4);
3831 
3832   n_threads = convert->conversion_runner->n_threads;
3833   tasks = g_newa (FConvertPlaneTask, n_threads);
3834   tasks_p = g_newa (FConvertPlaneTask *, n_threads);
3835 
3836   lines_per_thread = (height + n_threads - 1) / n_threads;
3837 
3838   for (i = 0; i < n_threads; i++) {
3839     tasks[i].dstride = FRAME_GET_STRIDE (dest);
3840     tasks[i].sstride = FRAME_GET_STRIDE (src);
3841     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
3842     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
3843 
3844     tasks[i].width = width;
3845     tasks[i].height = (i + 1) * lines_per_thread;
3846     tasks[i].height = MIN (tasks[i].height, height);
3847     tasks[i].height -= i * lines_per_thread;
3848     tasks[i].alpha = alpha;
3849 
3850     tasks_p[i] = &tasks[i];
3851   }
3852 
3853   gst_parallelized_task_runner_run (convert->conversion_runner,
3854       (GstParallelizedTaskFunc) convert_UYVY_AYUV_task, (gpointer) tasks_p);
3855 
3856   convert_fill_border (convert, dest);
3857 }
3858 
3859 static void
convert_UYVY_YUY2_task(FConvertPlaneTask * task)3860 convert_UYVY_YUY2_task (FConvertPlaneTask * task)
3861 {
3862   video_orc_convert_UYVY_YUY2 (task->d, task->dstride, task->s,
3863       task->sstride, (task->width + 1) / 2, task->height);
3864 }
3865 
3866 static void
convert_UYVY_YUY2(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)3867 convert_UYVY_YUY2 (GstVideoConverter * convert, const GstVideoFrame * src,
3868     GstVideoFrame * dest)
3869 {
3870   gint width = convert->in_width;
3871   gint height = convert->in_height;
3872   guint8 *s, *d;
3873   FConvertPlaneTask *tasks;
3874   FConvertPlaneTask **tasks_p;
3875   gint n_threads;
3876   gint lines_per_thread;
3877   gint i;
3878 
3879   s = FRAME_GET_LINE (src, convert->in_y);
3880   s += (GST_ROUND_UP_2 (convert->in_x) * 2);
3881   d = FRAME_GET_LINE (dest, convert->out_y);
3882   d += (GST_ROUND_UP_2 (convert->out_x) * 2);
3883 
3884   n_threads = convert->conversion_runner->n_threads;
3885   tasks = g_newa (FConvertPlaneTask, n_threads);
3886   tasks_p = g_newa (FConvertPlaneTask *, n_threads);
3887 
3888   lines_per_thread = (height + n_threads - 1) / n_threads;
3889 
3890   for (i = 0; i < n_threads; i++) {
3891     tasks[i].dstride = FRAME_GET_STRIDE (dest);
3892     tasks[i].sstride = FRAME_GET_STRIDE (src);
3893     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
3894     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
3895 
3896     tasks[i].width = width;
3897     tasks[i].height = (i + 1) * lines_per_thread;
3898     tasks[i].height = MIN (tasks[i].height, height);
3899     tasks[i].height -= i * lines_per_thread;
3900 
3901     tasks_p[i] = &tasks[i];
3902   }
3903 
3904   gst_parallelized_task_runner_run (convert->conversion_runner,
3905       (GstParallelizedTaskFunc) convert_UYVY_YUY2_task, (gpointer) tasks_p);
3906 
3907   convert_fill_border (convert, dest);
3908 }
3909 
3910 static void
convert_UYVY_Y42B_task(FConvertPlaneTask * task)3911 convert_UYVY_Y42B_task (FConvertPlaneTask * task)
3912 {
3913   video_orc_convert_UYVY_Y42B (task->d, task->dstride, task->du,
3914       task->dustride, task->dv, task->dvstride,
3915       task->s, task->sstride, (task->width + 1) / 2, task->height);
3916 }
3917 
3918 static void
convert_UYVY_Y42B(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)3919 convert_UYVY_Y42B (GstVideoConverter * convert, const GstVideoFrame * src,
3920     GstVideoFrame * dest)
3921 {
3922   gint width = convert->in_width;
3923   gint height = convert->in_height;
3924   guint8 *s, *dy, *du, *dv;
3925   FConvertPlaneTask *tasks;
3926   FConvertPlaneTask **tasks_p;
3927   gint n_threads;
3928   gint lines_per_thread;
3929   gint i;
3930 
3931   s = FRAME_GET_LINE (src, convert->in_y);
3932   s += (GST_ROUND_UP_2 (convert->in_x) * 2);
3933 
3934   dy = FRAME_GET_Y_LINE (dest, convert->out_y);
3935   dy += convert->out_x;
3936   du = FRAME_GET_U_LINE (dest, convert->out_y);
3937   du += convert->out_x >> 1;
3938   dv = FRAME_GET_V_LINE (dest, convert->out_y);
3939   dv += convert->out_x >> 1;
3940 
3941   n_threads = convert->conversion_runner->n_threads;
3942   tasks = g_newa (FConvertPlaneTask, n_threads);
3943   tasks_p = g_newa (FConvertPlaneTask *, n_threads);
3944 
3945   lines_per_thread = (height + n_threads - 1) / n_threads;
3946 
3947   for (i = 0; i < n_threads; i++) {
3948     tasks[i].dstride = FRAME_GET_Y_STRIDE (dest);
3949     tasks[i].dustride = FRAME_GET_U_STRIDE (dest);
3950     tasks[i].dvstride = FRAME_GET_V_STRIDE (dest);
3951     tasks[i].sstride = FRAME_GET_STRIDE (src);
3952     tasks[i].d = dy + i * lines_per_thread * tasks[i].dstride;
3953     tasks[i].du = du + i * lines_per_thread * tasks[i].dustride;
3954     tasks[i].dv = dv + i * lines_per_thread * tasks[i].dvstride;
3955     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
3956 
3957     tasks[i].width = width;
3958     tasks[i].height = (i + 1) * lines_per_thread;
3959     tasks[i].height = MIN (tasks[i].height, height);
3960     tasks[i].height -= i * lines_per_thread;
3961 
3962     tasks_p[i] = &tasks[i];
3963   }
3964 
3965   gst_parallelized_task_runner_run (convert->conversion_runner,
3966       (GstParallelizedTaskFunc) convert_UYVY_Y42B_task, (gpointer) tasks_p);
3967 
3968   convert_fill_border (convert, dest);
3969 }
3970 
3971 static void
convert_UYVY_Y444_task(FConvertPlaneTask * task)3972 convert_UYVY_Y444_task (FConvertPlaneTask * task)
3973 {
3974   video_orc_convert_UYVY_Y444 (task->d,
3975       task->dstride, task->du,
3976       task->dustride, task->dv,
3977       task->dvstride, task->s,
3978       task->sstride, (task->width + 1) / 2, task->height);
3979 }
3980 
3981 static void
convert_UYVY_Y444(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)3982 convert_UYVY_Y444 (GstVideoConverter * convert, const GstVideoFrame * src,
3983     GstVideoFrame * dest)
3984 {
3985   gint width = convert->in_width;
3986   gint height = convert->in_height;
3987   guint8 *s, *dy, *du, *dv;
3988   FConvertPlaneTask *tasks;
3989   FConvertPlaneTask **tasks_p;
3990   gint n_threads;
3991   gint lines_per_thread;
3992   gint i;
3993 
3994   s = FRAME_GET_LINE (src, convert->in_y);
3995   s += (GST_ROUND_UP_2 (convert->in_x) * 2);
3996 
3997   dy = FRAME_GET_Y_LINE (dest, convert->out_y);
3998   dy += convert->out_x;
3999   du = FRAME_GET_U_LINE (dest, convert->out_y);
4000   du += convert->out_x;
4001   dv = FRAME_GET_V_LINE (dest, convert->out_y);
4002   dv += convert->out_x;
4003 
4004   n_threads = convert->conversion_runner->n_threads;
4005   tasks = g_newa (FConvertPlaneTask, n_threads);
4006   tasks_p = g_newa (FConvertPlaneTask *, n_threads);
4007 
4008   lines_per_thread = (height + n_threads - 1) / n_threads;
4009 
4010   for (i = 0; i < n_threads; i++) {
4011     tasks[i].dstride = FRAME_GET_Y_STRIDE (dest);
4012     tasks[i].dustride = FRAME_GET_U_STRIDE (dest);
4013     tasks[i].dvstride = FRAME_GET_V_STRIDE (dest);
4014     tasks[i].sstride = FRAME_GET_STRIDE (src);
4015     tasks[i].d = dy + i * lines_per_thread * tasks[i].dstride;
4016     tasks[i].du = du + i * lines_per_thread * tasks[i].dustride;
4017     tasks[i].dv = dv + i * lines_per_thread * tasks[i].dvstride;
4018     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
4019 
4020     tasks[i].width = width;
4021     tasks[i].height = (i + 1) * lines_per_thread;
4022     tasks[i].height = MIN (tasks[i].height, height);
4023     tasks[i].height -= i * lines_per_thread;
4024 
4025     tasks_p[i] = &tasks[i];
4026   }
4027 
4028   gst_parallelized_task_runner_run (convert->conversion_runner,
4029       (GstParallelizedTaskFunc) convert_UYVY_Y444_task, (gpointer) tasks_p);
4030 
4031   convert_fill_border (convert, dest);
4032 }
4033 
4034 static void
convert_UYVY_GRAY8_task(FConvertPlaneTask * task)4035 convert_UYVY_GRAY8_task (FConvertPlaneTask * task)
4036 {
4037   video_orc_convert_UYVY_GRAY8 (task->d, task->dstride, (guint16 *) task->s,
4038       task->sstride, task->width, task->height);
4039 }
4040 
4041 static void
convert_UYVY_GRAY8(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)4042 convert_UYVY_GRAY8 (GstVideoConverter * convert, const GstVideoFrame * src,
4043     GstVideoFrame * dest)
4044 {
4045   gint width = convert->in_width;
4046   gint height = convert->in_height;
4047   guint8 *s;
4048   guint8 *d;
4049   FConvertPlaneTask *tasks;
4050   FConvertPlaneTask **tasks_p;
4051   gint n_threads;
4052   gint lines_per_thread;
4053   gint i;
4054 
4055   s = GST_VIDEO_FRAME_PLANE_DATA (src, 0);
4056   d = GST_VIDEO_FRAME_PLANE_DATA (dest, 0);
4057 
4058   n_threads = convert->conversion_runner->n_threads;
4059   tasks = g_newa (FConvertPlaneTask, n_threads);
4060   tasks_p = g_newa (FConvertPlaneTask *, n_threads);
4061 
4062   lines_per_thread = (height + n_threads - 1) / n_threads;
4063 
4064   for (i = 0; i < n_threads; i++) {
4065     tasks[i].dstride = FRAME_GET_STRIDE (dest);
4066     tasks[i].sstride = FRAME_GET_STRIDE (src);
4067     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
4068     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
4069 
4070     tasks[i].width = width;
4071     tasks[i].height = (i + 1) * lines_per_thread;
4072     tasks[i].height = MIN (tasks[i].height, height);
4073     tasks[i].height -= i * lines_per_thread;
4074 
4075     tasks_p[i] = &tasks[i];
4076   }
4077 
4078   gst_parallelized_task_runner_run (convert->conversion_runner,
4079       (GstParallelizedTaskFunc) convert_UYVY_GRAY8_task, (gpointer) tasks_p);
4080 
4081   convert_fill_border (convert, dest);
4082 }
4083 
4084 static void
convert_AYUV_I420_task(FConvertPlaneTask * task)4085 convert_AYUV_I420_task (FConvertPlaneTask * task)
4086 {
4087   video_orc_convert_AYUV_I420 (task->d,
4088       2 * task->dstride, task->d2,
4089       2 * task->dstride, task->du,
4090       task->dustride, task->dv,
4091       task->dvstride, task->s,
4092       2 * task->sstride, task->s2,
4093       2 * task->sstride, task->width / 2, task->height / 2);
4094 }
4095 
4096 static void
convert_AYUV_I420(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)4097 convert_AYUV_I420 (GstVideoConverter * convert, const GstVideoFrame * src,
4098     GstVideoFrame * dest)
4099 {
4100   gint width = convert->in_width;
4101   gint height = convert->in_height;
4102   guint8 *s1, *s2, *dy1, *dy2, *du, *dv;
4103   FConvertPlaneTask *tasks;
4104   FConvertPlaneTask **tasks_p;
4105   gint n_threads;
4106   gint lines_per_thread;
4107   gint i;
4108 
4109   s1 = FRAME_GET_LINE (src, convert->in_y + 0);
4110   s1 += convert->in_x * 4;
4111   s2 = FRAME_GET_LINE (src, convert->in_y + 1);
4112   s2 += convert->in_x * 4;
4113 
4114   dy1 = FRAME_GET_Y_LINE (dest, convert->out_y + 0);
4115   dy1 += convert->out_x;
4116   dy2 = FRAME_GET_Y_LINE (dest, convert->out_y + 1);
4117   dy2 += convert->out_x;
4118   du = FRAME_GET_U_LINE (dest, convert->out_y >> 1);
4119   du += convert->out_x >> 1;
4120   dv = FRAME_GET_V_LINE (dest, convert->out_y >> 1);
4121   dv += convert->out_x >> 1;
4122 
4123   /* only for even width/height */
4124 
4125   n_threads = convert->conversion_runner->n_threads;
4126   tasks = g_newa (FConvertPlaneTask, n_threads);
4127   tasks_p = g_newa (FConvertPlaneTask *, n_threads);
4128 
4129   lines_per_thread = GST_ROUND_UP_2 ((height + n_threads - 1) / n_threads);
4130 
4131   for (i = 0; i < n_threads; i++) {
4132     tasks[i].dstride = FRAME_GET_Y_STRIDE (dest);
4133     tasks[i].dustride = FRAME_GET_U_STRIDE (dest);
4134     tasks[i].dvstride = FRAME_GET_V_STRIDE (dest);
4135     tasks[i].sstride = FRAME_GET_STRIDE (src);
4136     tasks[i].d = dy1 + i * lines_per_thread * tasks[i].dstride;
4137     tasks[i].d2 = dy2 + i * lines_per_thread * tasks[i].dstride;
4138     tasks[i].du = du + i * lines_per_thread * tasks[i].dustride / 2;
4139     tasks[i].dv = dv + i * lines_per_thread * tasks[i].dvstride / 2;
4140     tasks[i].s = s1 + i * lines_per_thread * tasks[i].sstride;
4141     tasks[i].s2 = s2 + i * lines_per_thread * tasks[i].sstride;
4142 
4143     tasks[i].width = width;
4144     tasks[i].height = (i + 1) * lines_per_thread;
4145     tasks[i].height = MIN (tasks[i].height, height);
4146     tasks[i].height -= i * lines_per_thread;
4147 
4148     tasks_p[i] = &tasks[i];
4149   }
4150 
4151   gst_parallelized_task_runner_run (convert->conversion_runner,
4152       (GstParallelizedTaskFunc) convert_AYUV_I420_task, (gpointer) tasks_p);
4153 
4154   convert_fill_border (convert, dest);
4155 }
4156 
4157 static void
convert_AYUV_YUY2_task(FConvertPlaneTask * task)4158 convert_AYUV_YUY2_task (FConvertPlaneTask * task)
4159 {
4160   video_orc_convert_AYUV_YUY2 (task->d, task->dstride, task->s,
4161       task->sstride, task->width / 2, task->height);
4162 }
4163 
4164 static void
convert_AYUV_YUY2(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)4165 convert_AYUV_YUY2 (GstVideoConverter * convert, const GstVideoFrame * src,
4166     GstVideoFrame * dest)
4167 {
4168   gint width = convert->in_width;
4169   gint height = convert->in_height;
4170   guint8 *s, *d;
4171   FConvertPlaneTask *tasks;
4172   FConvertPlaneTask **tasks_p;
4173   gint n_threads;
4174   gint lines_per_thread;
4175   gint i;
4176 
4177   s = FRAME_GET_LINE (src, convert->in_y);
4178   s += convert->in_x * 4;
4179   d = FRAME_GET_LINE (dest, convert->out_y);
4180   d += (GST_ROUND_UP_2 (convert->out_x) * 2);
4181 
4182   /* only for even width */
4183   n_threads = convert->conversion_runner->n_threads;
4184   tasks = g_newa (FConvertPlaneTask, n_threads);
4185   tasks_p = g_newa (FConvertPlaneTask *, n_threads);
4186 
4187   lines_per_thread = (height + n_threads - 1) / n_threads;
4188 
4189   for (i = 0; i < n_threads; i++) {
4190     tasks[i].dstride = FRAME_GET_STRIDE (dest);
4191     tasks[i].sstride = FRAME_GET_STRIDE (src);
4192     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
4193     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
4194 
4195     tasks[i].width = width;
4196     tasks[i].height = (i + 1) * lines_per_thread;
4197     tasks[i].height = MIN (tasks[i].height, height);
4198     tasks[i].height -= i * lines_per_thread;
4199 
4200     tasks_p[i] = &tasks[i];
4201   }
4202 
4203   gst_parallelized_task_runner_run (convert->conversion_runner,
4204       (GstParallelizedTaskFunc) convert_AYUV_YUY2_task, (gpointer) tasks_p);
4205 
4206   convert_fill_border (convert, dest);
4207 }
4208 
4209 static void
convert_AYUV_UYVY_task(FConvertPlaneTask * task)4210 convert_AYUV_UYVY_task (FConvertPlaneTask * task)
4211 {
4212   video_orc_convert_AYUV_UYVY (task->d, task->dstride, task->s,
4213       task->sstride, task->width / 2, task->height);
4214 }
4215 
4216 static void
convert_AYUV_UYVY(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)4217 convert_AYUV_UYVY (GstVideoConverter * convert, const GstVideoFrame * src,
4218     GstVideoFrame * dest)
4219 {
4220   gint width = convert->in_width;
4221   gint height = convert->in_height;
4222   guint8 *s, *d;
4223   FConvertPlaneTask *tasks;
4224   FConvertPlaneTask **tasks_p;
4225   gint n_threads;
4226   gint lines_per_thread;
4227   gint i;
4228 
4229   s = FRAME_GET_LINE (src, convert->in_y);
4230   s += convert->in_x * 4;
4231   d = FRAME_GET_LINE (dest, convert->out_y);
4232   d += (GST_ROUND_UP_2 (convert->out_x) * 2);
4233 
4234   /* only for even width */
4235   n_threads = convert->conversion_runner->n_threads;
4236   tasks = g_newa (FConvertPlaneTask, n_threads);
4237   tasks_p = g_newa (FConvertPlaneTask *, n_threads);
4238 
4239   lines_per_thread = (height + n_threads - 1) / n_threads;
4240 
4241   for (i = 0; i < n_threads; i++) {
4242     tasks[i].dstride = FRAME_GET_STRIDE (dest);
4243     tasks[i].sstride = FRAME_GET_STRIDE (src);
4244     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
4245     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
4246 
4247     tasks[i].width = width;
4248     tasks[i].height = (i + 1) * lines_per_thread;
4249     tasks[i].height = MIN (tasks[i].height, height);
4250     tasks[i].height -= i * lines_per_thread;
4251 
4252     tasks_p[i] = &tasks[i];
4253   }
4254 
4255   gst_parallelized_task_runner_run (convert->conversion_runner,
4256       (GstParallelizedTaskFunc) convert_AYUV_UYVY_task, (gpointer) tasks_p);
4257 
4258   convert_fill_border (convert, dest);
4259 }
4260 
4261 static void
convert_AYUV_Y42B_task(FConvertPlaneTask * task)4262 convert_AYUV_Y42B_task (FConvertPlaneTask * task)
4263 {
4264   video_orc_convert_AYUV_Y42B (task->d, task->dstride, task->du,
4265       task->dustride, task->dv, task->dvstride,
4266       task->s, task->sstride, task->width / 2, task->height);
4267 }
4268 
4269 static void
convert_AYUV_Y42B(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)4270 convert_AYUV_Y42B (GstVideoConverter * convert, const GstVideoFrame * src,
4271     GstVideoFrame * dest)
4272 {
4273   gint width = convert->in_width;
4274   gint height = convert->in_height;
4275   guint8 *s, *dy, *du, *dv;
4276   FConvertPlaneTask *tasks;
4277   FConvertPlaneTask **tasks_p;
4278   gint n_threads;
4279   gint lines_per_thread;
4280   gint i;
4281 
4282   s = FRAME_GET_LINE (src, convert->in_y);
4283   s += convert->in_x * 4;
4284 
4285   dy = FRAME_GET_Y_LINE (dest, convert->out_y);
4286   dy += convert->out_x;
4287   du = FRAME_GET_U_LINE (dest, convert->out_y);
4288   du += convert->out_x >> 1;
4289   dv = FRAME_GET_V_LINE (dest, convert->out_y);
4290   dv += convert->out_x >> 1;
4291 
4292   /* only works for even width */
4293   n_threads = convert->conversion_runner->n_threads;
4294   tasks = g_newa (FConvertPlaneTask, n_threads);
4295   tasks_p = g_newa (FConvertPlaneTask *, n_threads);
4296 
4297   lines_per_thread = (height + n_threads - 1) / n_threads;
4298 
4299   for (i = 0; i < n_threads; i++) {
4300     tasks[i].dstride = FRAME_GET_Y_STRIDE (dest);
4301     tasks[i].dustride = FRAME_GET_U_STRIDE (dest);
4302     tasks[i].dvstride = FRAME_GET_V_STRIDE (dest);
4303     tasks[i].sstride = FRAME_GET_STRIDE (src);
4304     tasks[i].d = dy + i * lines_per_thread * tasks[i].dstride;
4305     tasks[i].du = du + i * lines_per_thread * tasks[i].dustride;
4306     tasks[i].dv = dv + i * lines_per_thread * tasks[i].dvstride;
4307     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
4308 
4309     tasks[i].width = width;
4310     tasks[i].height = (i + 1) * lines_per_thread;
4311     tasks[i].height = MIN (tasks[i].height, height);
4312     tasks[i].height -= i * lines_per_thread;
4313 
4314     tasks_p[i] = &tasks[i];
4315   }
4316 
4317   gst_parallelized_task_runner_run (convert->conversion_runner,
4318       (GstParallelizedTaskFunc) convert_AYUV_Y42B_task, (gpointer) tasks_p);
4319 
4320   convert_fill_border (convert, dest);
4321 }
4322 
4323 static void
convert_AYUV_Y444_task(FConvertPlaneTask * task)4324 convert_AYUV_Y444_task (FConvertPlaneTask * task)
4325 {
4326   video_orc_convert_AYUV_Y444 (task->d, task->dstride, task->du,
4327       task->dustride, task->dv, task->dvstride,
4328       task->s, task->sstride, task->width, task->height);
4329 }
4330 
4331 static void
convert_AYUV_Y444(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)4332 convert_AYUV_Y444 (GstVideoConverter * convert, const GstVideoFrame * src,
4333     GstVideoFrame * dest)
4334 {
4335   gint width = convert->in_width;
4336   gint height = convert->in_height;
4337   guint8 *s, *dy, *du, *dv;
4338   FConvertPlaneTask *tasks;
4339   FConvertPlaneTask **tasks_p;
4340   gint n_threads;
4341   gint lines_per_thread;
4342   gint i;
4343 
4344   s = FRAME_GET_LINE (src, convert->in_y);
4345   s += convert->in_x * 4;
4346 
4347   dy = FRAME_GET_Y_LINE (dest, convert->out_y);
4348   dy += convert->out_x;
4349   du = FRAME_GET_U_LINE (dest, convert->out_y);
4350   du += convert->out_x;
4351   dv = FRAME_GET_V_LINE (dest, convert->out_y);
4352   dv += convert->out_x;
4353 
4354   n_threads = convert->conversion_runner->n_threads;
4355   tasks = g_newa (FConvertPlaneTask, n_threads);
4356   tasks_p = g_newa (FConvertPlaneTask *, n_threads);
4357 
4358   lines_per_thread = (height + n_threads - 1) / n_threads;
4359 
4360   for (i = 0; i < n_threads; i++) {
4361     tasks[i].dstride = FRAME_GET_Y_STRIDE (dest);
4362     tasks[i].dustride = FRAME_GET_U_STRIDE (dest);
4363     tasks[i].dvstride = FRAME_GET_V_STRIDE (dest);
4364     tasks[i].sstride = FRAME_GET_STRIDE (src);
4365     tasks[i].d = dy + i * lines_per_thread * tasks[i].dstride;
4366     tasks[i].du = du + i * lines_per_thread * tasks[i].dustride;
4367     tasks[i].dv = dv + i * lines_per_thread * tasks[i].dvstride;
4368     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
4369 
4370     tasks[i].width = width;
4371     tasks[i].height = (i + 1) * lines_per_thread;
4372     tasks[i].height = MIN (tasks[i].height, height);
4373     tasks[i].height -= i * lines_per_thread;
4374 
4375     tasks_p[i] = &tasks[i];
4376   }
4377 
4378   gst_parallelized_task_runner_run (convert->conversion_runner,
4379       (GstParallelizedTaskFunc) convert_AYUV_Y444_task, (gpointer) tasks_p);
4380   convert_fill_border (convert, dest);
4381 }
4382 
4383 static void
convert_Y42B_YUY2_task(FConvertPlaneTask * task)4384 convert_Y42B_YUY2_task (FConvertPlaneTask * task)
4385 {
4386   video_orc_convert_Y42B_YUY2 (task->d, task->dstride,
4387       task->s, task->sstride,
4388       task->su, task->sustride,
4389       task->sv, task->svstride, (task->width + 1) / 2, task->height);
4390 }
4391 
4392 static void
convert_Y42B_YUY2(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)4393 convert_Y42B_YUY2 (GstVideoConverter * convert, const GstVideoFrame * src,
4394     GstVideoFrame * dest)
4395 {
4396   gint width = convert->in_width;
4397   gint height = convert->in_height;
4398   guint8 *sy, *su, *sv, *d;
4399   FConvertPlaneTask *tasks;
4400   FConvertPlaneTask **tasks_p;
4401   gint n_threads;
4402   gint lines_per_thread;
4403   gint i;
4404 
4405   sy = FRAME_GET_Y_LINE (src, convert->in_y);
4406   sy += convert->in_x;
4407   su = FRAME_GET_U_LINE (src, convert->in_y);
4408   su += convert->in_x >> 1;
4409   sv = FRAME_GET_V_LINE (src, convert->in_y);
4410   sv += convert->in_x >> 1;
4411 
4412   d = FRAME_GET_LINE (dest, convert->out_y);
4413   d += (GST_ROUND_UP_2 (convert->out_x) * 2);
4414 
4415   n_threads = convert->conversion_runner->n_threads;
4416   tasks = g_newa (FConvertPlaneTask, n_threads);
4417   tasks_p = g_newa (FConvertPlaneTask *, n_threads);
4418 
4419   lines_per_thread = (height + n_threads - 1) / n_threads;
4420 
4421   for (i = 0; i < n_threads; i++) {
4422     tasks[i].dstride = FRAME_GET_STRIDE (dest);
4423     tasks[i].sstride = FRAME_GET_Y_STRIDE (src);
4424     tasks[i].sustride = FRAME_GET_U_STRIDE (src);
4425     tasks[i].svstride = FRAME_GET_V_STRIDE (src);
4426     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
4427     tasks[i].s = sy + i * lines_per_thread * tasks[i].sstride;
4428     tasks[i].su = su + i * lines_per_thread * tasks[i].sustride;
4429     tasks[i].sv = sv + i * lines_per_thread * tasks[i].svstride;
4430 
4431     tasks[i].width = width;
4432     tasks[i].height = (i + 1) * lines_per_thread;
4433     tasks[i].height = MIN (tasks[i].height, height);
4434     tasks[i].height -= i * lines_per_thread;
4435 
4436     tasks_p[i] = &tasks[i];
4437   }
4438 
4439   gst_parallelized_task_runner_run (convert->conversion_runner,
4440       (GstParallelizedTaskFunc) convert_Y42B_YUY2_task, (gpointer) tasks_p);
4441 
4442   convert_fill_border (convert, dest);
4443 }
4444 
4445 static void
convert_Y42B_UYVY_task(FConvertPlaneTask * task)4446 convert_Y42B_UYVY_task (FConvertPlaneTask * task)
4447 {
4448   video_orc_convert_Y42B_UYVY (task->d, task->dstride,
4449       task->s, task->sstride,
4450       task->su, task->sustride,
4451       task->sv, task->svstride, (task->width + 1) / 2, task->height);
4452 }
4453 
4454 static void
convert_Y42B_UYVY(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)4455 convert_Y42B_UYVY (GstVideoConverter * convert, const GstVideoFrame * src,
4456     GstVideoFrame * dest)
4457 {
4458   gint width = convert->in_width;
4459   gint height = convert->in_height;
4460   guint8 *sy, *su, *sv, *d;
4461   FConvertPlaneTask *tasks;
4462   FConvertPlaneTask **tasks_p;
4463   gint n_threads;
4464   gint lines_per_thread;
4465   gint i;
4466 
4467   sy = FRAME_GET_Y_LINE (src, convert->in_y);
4468   sy += convert->in_x;
4469   su = FRAME_GET_U_LINE (src, convert->in_y);
4470   su += convert->in_x >> 1;
4471   sv = FRAME_GET_V_LINE (src, convert->in_y);
4472   sv += convert->in_x >> 1;
4473 
4474   d = FRAME_GET_LINE (dest, convert->out_y);
4475   d += (GST_ROUND_UP_2 (convert->out_x) * 2);
4476 
4477   n_threads = convert->conversion_runner->n_threads;
4478   tasks = g_newa (FConvertPlaneTask, n_threads);
4479   tasks_p = g_newa (FConvertPlaneTask *, n_threads);
4480 
4481   lines_per_thread = (height + n_threads - 1) / n_threads;
4482 
4483   for (i = 0; i < n_threads; i++) {
4484     tasks[i].dstride = FRAME_GET_STRIDE (dest);
4485     tasks[i].sstride = FRAME_GET_Y_STRIDE (src);
4486     tasks[i].sustride = FRAME_GET_U_STRIDE (src);
4487     tasks[i].svstride = FRAME_GET_V_STRIDE (src);
4488     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
4489     tasks[i].s = sy + i * lines_per_thread * tasks[i].sstride;
4490     tasks[i].su = su + i * lines_per_thread * tasks[i].sustride;
4491     tasks[i].sv = sv + i * lines_per_thread * tasks[i].svstride;
4492 
4493     tasks[i].width = width;
4494     tasks[i].height = (i + 1) * lines_per_thread;
4495     tasks[i].height = MIN (tasks[i].height, height);
4496     tasks[i].height -= i * lines_per_thread;
4497 
4498     tasks_p[i] = &tasks[i];
4499   }
4500 
4501   gst_parallelized_task_runner_run (convert->conversion_runner,
4502       (GstParallelizedTaskFunc) convert_Y42B_UYVY_task, (gpointer) tasks_p);
4503 
4504   convert_fill_border (convert, dest);
4505 }
4506 
4507 static void
convert_Y42B_AYUV_task(FConvertPlaneTask * task)4508 convert_Y42B_AYUV_task (FConvertPlaneTask * task)
4509 {
4510   video_orc_convert_Y42B_AYUV (task->d, task->dstride, task->s,
4511       task->sstride,
4512       task->su,
4513       task->sustride,
4514       task->sv, task->svstride, task->alpha, task->width / 2, task->height);
4515 }
4516 
4517 static void
convert_Y42B_AYUV(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)4518 convert_Y42B_AYUV (GstVideoConverter * convert, const GstVideoFrame * src,
4519     GstVideoFrame * dest)
4520 {
4521   gint width = convert->in_width;
4522   gint height = convert->in_height;
4523   guint8 *sy, *su, *sv, *d;
4524   guint8 alpha = MIN (convert->alpha_value, 255);
4525   FConvertPlaneTask *tasks;
4526   FConvertPlaneTask **tasks_p;
4527   gint n_threads;
4528   gint lines_per_thread;
4529   gint i;
4530 
4531   sy = FRAME_GET_Y_LINE (src, convert->in_y);
4532   sy += convert->in_x;
4533   su = FRAME_GET_U_LINE (src, convert->in_y);
4534   su += convert->in_x >> 1;
4535   sv = FRAME_GET_V_LINE (src, convert->in_y);
4536   sv += convert->in_x >> 1;
4537 
4538   d = FRAME_GET_LINE (dest, convert->out_y);
4539   d += convert->out_x * 4;
4540 
4541   /* only for even width */
4542   n_threads = convert->conversion_runner->n_threads;
4543   tasks = g_newa (FConvertPlaneTask, n_threads);
4544   tasks_p = g_newa (FConvertPlaneTask *, n_threads);
4545 
4546   lines_per_thread = (height + n_threads - 1) / n_threads;
4547 
4548   for (i = 0; i < n_threads; i++) {
4549     tasks[i].dstride = FRAME_GET_STRIDE (dest);
4550     tasks[i].sstride = FRAME_GET_Y_STRIDE (src);
4551     tasks[i].sustride = FRAME_GET_U_STRIDE (src);
4552     tasks[i].svstride = FRAME_GET_V_STRIDE (src);
4553     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
4554     tasks[i].s = sy + i * lines_per_thread * tasks[i].sstride;
4555     tasks[i].su = su + i * lines_per_thread * tasks[i].sustride;
4556     tasks[i].sv = sv + i * lines_per_thread * tasks[i].svstride;
4557 
4558     tasks[i].width = width;
4559     tasks[i].height = (i + 1) * lines_per_thread;
4560     tasks[i].height = MIN (tasks[i].height, height);
4561     tasks[i].height -= i * lines_per_thread;
4562     tasks[i].alpha = alpha;
4563 
4564     tasks_p[i] = &tasks[i];
4565   }
4566 
4567   gst_parallelized_task_runner_run (convert->conversion_runner,
4568       (GstParallelizedTaskFunc) convert_Y42B_AYUV_task, (gpointer) tasks_p);
4569 
4570   convert_fill_border (convert, dest);
4571 }
4572 
4573 static void
convert_Y444_YUY2_task(FConvertPlaneTask * task)4574 convert_Y444_YUY2_task (FConvertPlaneTask * task)
4575 {
4576   video_orc_convert_Y444_YUY2 (task->d, task->dstride, task->s,
4577       task->sstride,
4578       task->su,
4579       task->sustride, task->sv, task->svstride, task->width / 2, task->height);
4580 }
4581 
4582 static void
convert_Y444_YUY2(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)4583 convert_Y444_YUY2 (GstVideoConverter * convert, const GstVideoFrame * src,
4584     GstVideoFrame * dest)
4585 {
4586   gint width = convert->in_width;
4587   gint height = convert->in_height;
4588   guint8 *sy, *su, *sv, *d;
4589   FConvertPlaneTask *tasks;
4590   FConvertPlaneTask **tasks_p;
4591   gint n_threads;
4592   gint lines_per_thread;
4593   gint i;
4594 
4595   sy = FRAME_GET_Y_LINE (src, convert->in_y);
4596   sy += convert->in_x;
4597   su = FRAME_GET_U_LINE (src, convert->in_y);
4598   su += convert->in_x;
4599   sv = FRAME_GET_V_LINE (src, convert->in_y);
4600   sv += convert->in_x;
4601 
4602   d = FRAME_GET_LINE (dest, convert->out_y);
4603   d += (GST_ROUND_UP_2 (convert->out_x) * 2);
4604 
4605   n_threads = convert->conversion_runner->n_threads;
4606   tasks = g_newa (FConvertPlaneTask, n_threads);
4607   tasks_p = g_newa (FConvertPlaneTask *, n_threads);
4608 
4609   lines_per_thread = (height + n_threads - 1) / n_threads;
4610 
4611   for (i = 0; i < n_threads; i++) {
4612     tasks[i].dstride = FRAME_GET_STRIDE (dest);
4613     tasks[i].sstride = FRAME_GET_Y_STRIDE (src);
4614     tasks[i].sustride = FRAME_GET_U_STRIDE (src);
4615     tasks[i].svstride = FRAME_GET_V_STRIDE (src);
4616     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
4617     tasks[i].s = sy + i * lines_per_thread * tasks[i].sstride;
4618     tasks[i].su = su + i * lines_per_thread * tasks[i].sustride;
4619     tasks[i].sv = sv + i * lines_per_thread * tasks[i].svstride;
4620 
4621     tasks[i].width = width;
4622     tasks[i].height = (i + 1) * lines_per_thread;
4623     tasks[i].height = MIN (tasks[i].height, height);
4624     tasks[i].height -= i * lines_per_thread;
4625 
4626     tasks_p[i] = &tasks[i];
4627   }
4628 
4629   gst_parallelized_task_runner_run (convert->conversion_runner,
4630       (GstParallelizedTaskFunc) convert_Y444_YUY2_task, (gpointer) tasks_p);
4631 
4632   convert_fill_border (convert, dest);
4633 }
4634 
4635 static void
convert_Y444_UYVY_task(FConvertPlaneTask * task)4636 convert_Y444_UYVY_task (FConvertPlaneTask * task)
4637 {
4638   video_orc_convert_Y444_UYVY (task->d, task->dstride, task->s,
4639       task->sstride,
4640       task->su,
4641       task->sustride, task->sv, task->svstride, task->width / 2, task->height);
4642 }
4643 
4644 static void
convert_Y444_UYVY(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)4645 convert_Y444_UYVY (GstVideoConverter * convert, const GstVideoFrame * src,
4646     GstVideoFrame * dest)
4647 {
4648   gint width = convert->in_width;
4649   gint height = convert->in_height;
4650   guint8 *sy, *su, *sv, *d;
4651   FConvertPlaneTask *tasks;
4652   FConvertPlaneTask **tasks_p;
4653   gint n_threads;
4654   gint lines_per_thread;
4655   gint i;
4656 
4657   sy = FRAME_GET_Y_LINE (src, convert->in_y);
4658   sy += convert->in_x;
4659   su = FRAME_GET_U_LINE (src, convert->in_y);
4660   su += convert->in_x;
4661   sv = FRAME_GET_V_LINE (src, convert->in_y);
4662   sv += convert->in_x;
4663 
4664   d = FRAME_GET_LINE (dest, convert->out_y);
4665   d += (GST_ROUND_UP_2 (convert->out_x) * 2);
4666 
4667   n_threads = convert->conversion_runner->n_threads;
4668   tasks = g_newa (FConvertPlaneTask, n_threads);
4669   tasks_p = g_newa (FConvertPlaneTask *, n_threads);
4670 
4671   lines_per_thread = (height + n_threads - 1) / n_threads;
4672 
4673   for (i = 0; i < n_threads; i++) {
4674     tasks[i].dstride = FRAME_GET_STRIDE (dest);
4675     tasks[i].sstride = FRAME_GET_Y_STRIDE (src);
4676     tasks[i].sustride = FRAME_GET_U_STRIDE (src);
4677     tasks[i].svstride = FRAME_GET_V_STRIDE (src);
4678     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
4679     tasks[i].s = sy + i * lines_per_thread * tasks[i].sstride;
4680     tasks[i].su = su + i * lines_per_thread * tasks[i].sustride;
4681     tasks[i].sv = sv + i * lines_per_thread * tasks[i].svstride;
4682 
4683     tasks[i].width = width;
4684     tasks[i].height = (i + 1) * lines_per_thread;
4685     tasks[i].height = MIN (tasks[i].height, height);
4686     tasks[i].height -= i * lines_per_thread;
4687 
4688     tasks_p[i] = &tasks[i];
4689   }
4690 
4691   gst_parallelized_task_runner_run (convert->conversion_runner,
4692       (GstParallelizedTaskFunc) convert_Y444_UYVY_task, (gpointer) tasks_p);
4693 
4694   convert_fill_border (convert, dest);
4695 }
4696 
4697 static void
convert_Y444_AYUV_task(FConvertPlaneTask * task)4698 convert_Y444_AYUV_task (FConvertPlaneTask * task)
4699 {
4700   video_orc_convert_Y444_AYUV (task->d, task->dstride, task->s,
4701       task->sstride,
4702       task->su,
4703       task->sustride,
4704       task->sv, task->svstride, task->alpha, task->width, task->height);
4705 }
4706 
4707 static void
convert_Y444_AYUV(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)4708 convert_Y444_AYUV (GstVideoConverter * convert, const GstVideoFrame * src,
4709     GstVideoFrame * dest)
4710 {
4711   gint width = convert->in_width;
4712   gint height = convert->in_height;
4713   guint8 *sy, *su, *sv, *d;
4714   guint8 alpha = MIN (convert->alpha_value, 255);
4715   FConvertPlaneTask *tasks;
4716   FConvertPlaneTask **tasks_p;
4717   gint n_threads;
4718   gint lines_per_thread;
4719   gint i;
4720 
4721   sy = FRAME_GET_Y_LINE (src, convert->in_y);
4722   sy += convert->in_x;
4723   su = FRAME_GET_U_LINE (src, convert->in_y);
4724   su += convert->in_x;
4725   sv = FRAME_GET_V_LINE (src, convert->in_y);
4726   sv += convert->in_x;
4727 
4728   d = FRAME_GET_LINE (dest, convert->out_y);
4729   d += convert->out_x * 4;
4730 
4731   n_threads = convert->conversion_runner->n_threads;
4732   tasks = g_newa (FConvertPlaneTask, n_threads);
4733   tasks_p = g_newa (FConvertPlaneTask *, n_threads);
4734 
4735   lines_per_thread = (height + n_threads - 1) / n_threads;
4736 
4737   for (i = 0; i < n_threads; i++) {
4738     tasks[i].dstride = FRAME_GET_STRIDE (dest);
4739     tasks[i].sstride = FRAME_GET_Y_STRIDE (src);
4740     tasks[i].sustride = FRAME_GET_U_STRIDE (src);
4741     tasks[i].svstride = FRAME_GET_V_STRIDE (src);
4742     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
4743     tasks[i].s = sy + i * lines_per_thread * tasks[i].sstride;
4744     tasks[i].su = su + i * lines_per_thread * tasks[i].sustride;
4745     tasks[i].sv = sv + i * lines_per_thread * tasks[i].svstride;
4746 
4747     tasks[i].width = width;
4748     tasks[i].height = (i + 1) * lines_per_thread;
4749     tasks[i].height = MIN (tasks[i].height, height);
4750     tasks[i].height -= i * lines_per_thread;
4751     tasks[i].alpha = alpha;
4752 
4753     tasks_p[i] = &tasks[i];
4754   }
4755 
4756   gst_parallelized_task_runner_run (convert->conversion_runner,
4757       (GstParallelizedTaskFunc) convert_Y444_AYUV_task, (gpointer) tasks_p);
4758 
4759   convert_fill_border (convert, dest);
4760 }
4761 
4762 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
4763 static void
convert_AYUV_ARGB_task(FConvertPlaneTask * task)4764 convert_AYUV_ARGB_task (FConvertPlaneTask * task)
4765 {
4766   video_orc_convert_AYUV_ARGB (task->d, task->dstride, task->s,
4767       task->sstride, task->data->im[0][0], task->data->im[0][2],
4768       task->data->im[2][1], task->data->im[1][1], task->data->im[1][2],
4769       task->width, task->height);
4770 }
4771 
4772 static void
convert_AYUV_ARGB(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)4773 convert_AYUV_ARGB (GstVideoConverter * convert, const GstVideoFrame * src,
4774     GstVideoFrame * dest)
4775 {
4776   gint width = convert->in_width;
4777   gint height = convert->in_height;
4778   MatrixData *data = &convert->convert_matrix;
4779   guint8 *s, *d;
4780   FConvertPlaneTask *tasks;
4781   FConvertPlaneTask **tasks_p;
4782   gint n_threads;
4783   gint lines_per_thread;
4784   gint i;
4785 
4786   s = FRAME_GET_LINE (src, convert->in_y);
4787   s += (convert->in_x * 4);
4788   d = FRAME_GET_LINE (dest, convert->out_y);
4789   d += (convert->out_x * 4);
4790 
4791   n_threads = convert->conversion_runner->n_threads;
4792   tasks = g_newa (FConvertPlaneTask, n_threads);
4793   tasks_p = g_newa (FConvertPlaneTask *, n_threads);
4794 
4795   lines_per_thread = (height + n_threads - 1) / n_threads;
4796 
4797   for (i = 0; i < n_threads; i++) {
4798     tasks[i].dstride = FRAME_GET_STRIDE (dest);
4799     tasks[i].sstride = FRAME_GET_STRIDE (src);
4800     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
4801     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
4802 
4803     tasks[i].width = width;
4804     tasks[i].height = (i + 1) * lines_per_thread;
4805     tasks[i].height = MIN (tasks[i].height, height);
4806     tasks[i].height -= i * lines_per_thread;
4807     tasks[i].data = data;
4808 
4809     tasks_p[i] = &tasks[i];
4810   }
4811 
4812   gst_parallelized_task_runner_run (convert->conversion_runner,
4813       (GstParallelizedTaskFunc) convert_AYUV_ARGB_task, (gpointer) tasks_p);
4814 
4815   convert_fill_border (convert, dest);
4816 }
4817 
4818 static void
convert_AYUV_BGRA_task(FConvertPlaneTask * task)4819 convert_AYUV_BGRA_task (FConvertPlaneTask * task)
4820 {
4821   video_orc_convert_AYUV_BGRA (task->d, task->dstride, task->s,
4822       task->sstride, task->data->im[0][0], task->data->im[0][2],
4823       task->data->im[2][1], task->data->im[1][1], task->data->im[1][2],
4824       task->width, task->height);
4825 }
4826 
4827 static void
convert_AYUV_BGRA(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)4828 convert_AYUV_BGRA (GstVideoConverter * convert, const GstVideoFrame * src,
4829     GstVideoFrame * dest)
4830 {
4831   gint width = convert->in_width;
4832   gint height = convert->in_height;
4833   MatrixData *data = &convert->convert_matrix;
4834   guint8 *s, *d;
4835   FConvertPlaneTask *tasks;
4836   FConvertPlaneTask **tasks_p;
4837   gint n_threads;
4838   gint lines_per_thread;
4839   gint i;
4840 
4841   s = FRAME_GET_LINE (src, convert->in_y);
4842   s += (convert->in_x * 4);
4843   d = FRAME_GET_LINE (dest, convert->out_y);
4844   d += (convert->out_x * 4);
4845 
4846   n_threads = convert->conversion_runner->n_threads;
4847   tasks = g_newa (FConvertPlaneTask, n_threads);
4848   tasks_p = g_newa (FConvertPlaneTask *, n_threads);
4849 
4850   lines_per_thread = (height + n_threads - 1) / n_threads;
4851 
4852   for (i = 0; i < n_threads; i++) {
4853     tasks[i].dstride = FRAME_GET_STRIDE (dest);
4854     tasks[i].sstride = FRAME_GET_STRIDE (src);
4855     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
4856     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
4857 
4858     tasks[i].width = width;
4859     tasks[i].height = (i + 1) * lines_per_thread;
4860     tasks[i].height = MIN (tasks[i].height, height);
4861     tasks[i].height -= i * lines_per_thread;
4862     tasks[i].data = data;
4863 
4864     tasks_p[i] = &tasks[i];
4865   }
4866 
4867   gst_parallelized_task_runner_run (convert->conversion_runner,
4868       (GstParallelizedTaskFunc) convert_AYUV_BGRA_task, (gpointer) tasks_p);
4869 
4870   convert_fill_border (convert, dest);
4871 }
4872 
4873 static void
convert_AYUV_ABGR_task(FConvertPlaneTask * task)4874 convert_AYUV_ABGR_task (FConvertPlaneTask * task)
4875 {
4876   video_orc_convert_AYUV_ABGR (task->d, task->dstride, task->s,
4877       task->sstride, task->data->im[0][0], task->data->im[0][2],
4878       task->data->im[2][1], task->data->im[1][1], task->data->im[1][2],
4879       task->width, task->height);
4880 }
4881 
4882 static void
convert_AYUV_ABGR(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)4883 convert_AYUV_ABGR (GstVideoConverter * convert, const GstVideoFrame * src,
4884     GstVideoFrame * dest)
4885 {
4886   gint width = convert->in_width;
4887   gint height = convert->in_height;
4888   MatrixData *data = &convert->convert_matrix;
4889   guint8 *s, *d;
4890   FConvertPlaneTask *tasks;
4891   FConvertPlaneTask **tasks_p;
4892   gint n_threads;
4893   gint lines_per_thread;
4894   gint i;
4895 
4896   s = FRAME_GET_LINE (src, convert->in_y);
4897   s += (convert->in_x * 4);
4898   d = FRAME_GET_LINE (dest, convert->out_y);
4899   d += (convert->out_x * 4);
4900 
4901   n_threads = convert->conversion_runner->n_threads;
4902   tasks = g_newa (FConvertPlaneTask, n_threads);
4903   tasks_p = g_newa (FConvertPlaneTask *, n_threads);
4904 
4905   lines_per_thread = (height + n_threads - 1) / n_threads;
4906 
4907   for (i = 0; i < n_threads; i++) {
4908     tasks[i].dstride = FRAME_GET_STRIDE (dest);
4909     tasks[i].sstride = FRAME_GET_STRIDE (src);
4910     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
4911     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
4912 
4913     tasks[i].width = width;
4914     tasks[i].height = (i + 1) * lines_per_thread;
4915     tasks[i].height = MIN (tasks[i].height, height);
4916     tasks[i].height -= i * lines_per_thread;
4917     tasks[i].data = data;
4918 
4919     tasks_p[i] = &tasks[i];
4920   }
4921 
4922   gst_parallelized_task_runner_run (convert->conversion_runner,
4923       (GstParallelizedTaskFunc) convert_AYUV_ABGR_task, (gpointer) tasks_p);
4924 
4925   convert_fill_border (convert, dest);
4926 }
4927 
4928 static void
convert_AYUV_RGBA_task(FConvertPlaneTask * task)4929 convert_AYUV_RGBA_task (FConvertPlaneTask * task)
4930 {
4931   video_orc_convert_AYUV_RGBA (task->d, task->dstride, task->s,
4932       task->sstride, task->data->im[0][0], task->data->im[0][2],
4933       task->data->im[2][1], task->data->im[1][1], task->data->im[1][2],
4934       task->width, task->height);
4935 }
4936 
4937 static void
convert_AYUV_RGBA(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)4938 convert_AYUV_RGBA (GstVideoConverter * convert, const GstVideoFrame * src,
4939     GstVideoFrame * dest)
4940 {
4941   gint width = convert->in_width;
4942   gint height = convert->in_height;
4943   MatrixData *data = &convert->convert_matrix;
4944   guint8 *s, *d;
4945   FConvertPlaneTask *tasks;
4946   FConvertPlaneTask **tasks_p;
4947   gint n_threads;
4948   gint lines_per_thread;
4949   gint i;
4950 
4951   s = FRAME_GET_LINE (src, convert->in_y);
4952   s += (convert->in_x * 4);
4953   d = FRAME_GET_LINE (dest, convert->out_y);
4954   d += (convert->out_x * 4);
4955 
4956   n_threads = convert->conversion_runner->n_threads;
4957   tasks = g_newa (FConvertPlaneTask, n_threads);
4958   tasks_p = g_newa (FConvertPlaneTask *, n_threads);
4959 
4960   lines_per_thread = (height + n_threads - 1) / n_threads;
4961 
4962   for (i = 0; i < n_threads; i++) {
4963     tasks[i].dstride = FRAME_GET_STRIDE (dest);
4964     tasks[i].sstride = FRAME_GET_STRIDE (src);
4965     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
4966     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
4967 
4968     tasks[i].width = width;
4969     tasks[i].height = (i + 1) * lines_per_thread;
4970     tasks[i].height = MIN (tasks[i].height, height);
4971     tasks[i].height -= i * lines_per_thread;
4972     tasks[i].data = data;
4973 
4974     tasks_p[i] = &tasks[i];
4975   }
4976 
4977   gst_parallelized_task_runner_run (convert->conversion_runner,
4978       (GstParallelizedTaskFunc) convert_AYUV_RGBA_task, (gpointer) tasks_p);
4979 
4980   convert_fill_border (convert, dest);
4981 }
4982 #endif
4983 
4984 static void
convert_I420_BGRA_task(FConvertTask * task)4985 convert_I420_BGRA_task (FConvertTask * task)
4986 {
4987   gint i;
4988 
4989   for (i = task->height_0; i < task->height_1; i++) {
4990     guint8 *sy, *su, *sv, *d;
4991 
4992     d = FRAME_GET_LINE (task->dest, i + task->out_y);
4993     d += (task->out_x * 4);
4994     sy = FRAME_GET_Y_LINE (task->src, i + task->in_y);
4995     sy += task->in_x;
4996     su = FRAME_GET_U_LINE (task->src, (i + task->in_y) >> 1);
4997     su += (task->in_x >> 1);
4998     sv = FRAME_GET_V_LINE (task->src, (i + task->in_y) >> 1);
4999     sv += (task->in_x >> 1);
5000 
5001 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
5002     video_orc_convert_I420_BGRA (d, sy, su, sv,
5003         task->data->im[0][0], task->data->im[0][2],
5004         task->data->im[2][1], task->data->im[1][1], task->data->im[1][2],
5005         task->width);
5006 #else
5007     video_orc_convert_I420_ARGB (d, sy, su, sv,
5008         task->data->im[0][0], task->data->im[0][2],
5009         task->data->im[2][1], task->data->im[1][1], task->data->im[1][2],
5010         task->width);
5011 #endif
5012   }
5013 }
5014 
5015 static void
convert_I420_BGRA(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)5016 convert_I420_BGRA (GstVideoConverter * convert, const GstVideoFrame * src,
5017     GstVideoFrame * dest)
5018 {
5019   int i;
5020   gint width = convert->in_width;
5021   gint height = convert->in_height;
5022   MatrixData *data = &convert->convert_matrix;
5023   FConvertTask *tasks;
5024   FConvertTask **tasks_p;
5025   gint n_threads;
5026   gint lines_per_thread;
5027 
5028   n_threads = convert->conversion_runner->n_threads;
5029   tasks = g_newa (FConvertTask, n_threads);
5030   tasks_p = g_newa (FConvertTask *, n_threads);
5031 
5032   lines_per_thread = (height + n_threads - 1) / n_threads;
5033 
5034   for (i = 0; i < n_threads; i++) {
5035     tasks[i].src = src;
5036     tasks[i].dest = dest;
5037 
5038     tasks[i].width = width;
5039     tasks[i].data = data;
5040     tasks[i].in_x = convert->in_x;
5041     tasks[i].in_y = convert->in_y;
5042     tasks[i].out_x = convert->out_x;
5043     tasks[i].out_y = convert->out_y;
5044 
5045     tasks[i].height_0 = i * lines_per_thread;
5046     tasks[i].height_1 = tasks[i].height_0 + lines_per_thread;
5047     tasks[i].height_1 = MIN (height, tasks[i].height_1);
5048 
5049     tasks_p[i] = &tasks[i];
5050   }
5051 
5052   gst_parallelized_task_runner_run (convert->conversion_runner,
5053       (GstParallelizedTaskFunc) convert_I420_BGRA_task, (gpointer) tasks_p);
5054 
5055   convert_fill_border (convert, dest);
5056 }
5057 
5058 static void
convert_I420_ARGB_task(FConvertTask * task)5059 convert_I420_ARGB_task (FConvertTask * task)
5060 {
5061   gint i;
5062 
5063   for (i = task->height_0; i < task->height_1; i++) {
5064     guint8 *sy, *su, *sv, *d;
5065 
5066     d = FRAME_GET_LINE (task->dest, i + task->out_y);
5067     d += (task->out_x * 4);
5068     sy = FRAME_GET_Y_LINE (task->src, i + task->in_y);
5069     sy += task->in_x;
5070     su = FRAME_GET_U_LINE (task->src, (i + task->in_y) >> 1);
5071     su += (task->in_x >> 1);
5072     sv = FRAME_GET_V_LINE (task->src, (i + task->in_y) >> 1);
5073     sv += (task->in_x >> 1);
5074 
5075 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
5076     video_orc_convert_I420_ARGB (d, sy, su, sv,
5077         task->data->im[0][0], task->data->im[0][2],
5078         task->data->im[2][1], task->data->im[1][1], task->data->im[1][2],
5079         task->width);
5080 #else
5081     video_orc_convert_I420_BGRA (d, sy, su, sv,
5082         task->data->im[0][0], task->data->im[0][2],
5083         task->data->im[2][1], task->data->im[1][1], task->data->im[1][2],
5084         task->width);
5085 #endif
5086   }
5087 }
5088 
5089 static void
convert_I420_ARGB(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)5090 convert_I420_ARGB (GstVideoConverter * convert, const GstVideoFrame * src,
5091     GstVideoFrame * dest)
5092 {
5093   int i;
5094   gint width = convert->in_width;
5095   gint height = convert->in_height;
5096   MatrixData *data = &convert->convert_matrix;
5097   FConvertTask *tasks;
5098   FConvertTask **tasks_p;
5099   gint n_threads;
5100   gint lines_per_thread;
5101 
5102   n_threads = convert->conversion_runner->n_threads;
5103   tasks = g_newa (FConvertTask, n_threads);
5104   tasks_p = g_newa (FConvertTask *, n_threads);
5105 
5106   lines_per_thread = (height + n_threads - 1) / n_threads;
5107 
5108   for (i = 0; i < n_threads; i++) {
5109     tasks[i].src = src;
5110     tasks[i].dest = dest;
5111 
5112     tasks[i].width = width;
5113     tasks[i].data = data;
5114     tasks[i].in_x = convert->in_x;
5115     tasks[i].in_y = convert->in_y;
5116     tasks[i].out_x = convert->out_x;
5117     tasks[i].out_y = convert->out_y;
5118 
5119     tasks[i].height_0 = i * lines_per_thread;
5120     tasks[i].height_1 = tasks[i].height_0 + lines_per_thread;
5121     tasks[i].height_1 = MIN (height, tasks[i].height_1);
5122 
5123     tasks_p[i] = &tasks[i];
5124   }
5125 
5126   gst_parallelized_task_runner_run (convert->conversion_runner,
5127       (GstParallelizedTaskFunc) convert_I420_ARGB_task, (gpointer) tasks_p);
5128 
5129   convert_fill_border (convert, dest);
5130 }
5131 
5132 static void
convert_I420_pack_ARGB_task(FConvertTask * task)5133 convert_I420_pack_ARGB_task (FConvertTask * task)
5134 {
5135   gint i;
5136   gpointer d[GST_VIDEO_MAX_PLANES];
5137 
5138   d[0] = FRAME_GET_LINE (task->dest, 0);
5139   d[0] =
5140       (guint8 *) d[0] +
5141       task->out_x * GST_VIDEO_FORMAT_INFO_PSTRIDE (task->dest->info.finfo, 0);
5142 
5143   for (i = task->height_0; i < task->height_1; i++) {
5144     guint8 *sy, *su, *sv;
5145 
5146     sy = FRAME_GET_Y_LINE (task->src, i + task->in_y);
5147     sy += task->in_x;
5148     su = FRAME_GET_U_LINE (task->src, (i + task->in_y) >> 1);
5149     su += (task->in_x >> 1);
5150     sv = FRAME_GET_V_LINE (task->src, (i + task->in_y) >> 1);
5151     sv += (task->in_x >> 1);
5152 
5153 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
5154     video_orc_convert_I420_ARGB (task->tmpline, sy, su, sv,
5155         task->data->im[0][0], task->data->im[0][2],
5156         task->data->im[2][1], task->data->im[1][1], task->data->im[1][2],
5157         task->width);
5158 #else
5159     video_orc_convert_I420_BGRA (task->tmpline, sy, su, sv,
5160         task->data->im[0][0], task->data->im[0][2],
5161         task->data->im[2][1], task->data->im[1][1], task->data->im[1][2],
5162         task->width);
5163 #endif
5164     task->dest->info.finfo->pack_func (task->dest->info.finfo,
5165         (GST_VIDEO_FRAME_IS_INTERLACED (task->dest) ?
5166             GST_VIDEO_PACK_FLAG_INTERLACED :
5167             GST_VIDEO_PACK_FLAG_NONE),
5168         task->tmpline, 0, d, task->dest->info.stride,
5169         task->dest->info.chroma_site, i + task->out_y, task->width);
5170   }
5171 }
5172 
5173 static void
convert_I420_pack_ARGB(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)5174 convert_I420_pack_ARGB (GstVideoConverter * convert, const GstVideoFrame * src,
5175     GstVideoFrame * dest)
5176 {
5177   int i;
5178   gint width = convert->in_width;
5179   gint height = convert->in_height;
5180   MatrixData *data = &convert->convert_matrix;
5181   FConvertTask *tasks;
5182   FConvertTask **tasks_p;
5183   gint n_threads;
5184   gint lines_per_thread;
5185 
5186   n_threads = convert->conversion_runner->n_threads;
5187   tasks = g_newa (FConvertTask, n_threads);
5188   tasks_p = g_newa (FConvertTask *, n_threads);
5189 
5190   lines_per_thread = (height + n_threads - 1) / n_threads;
5191 
5192   for (i = 0; i < n_threads; i++) {
5193     tasks[i].src = src;
5194     tasks[i].dest = dest;
5195 
5196     tasks[i].width = width;
5197     tasks[i].data = data;
5198     tasks[i].in_x = convert->in_x;
5199     tasks[i].in_y = convert->in_y;
5200     tasks[i].out_x = convert->out_x;
5201     tasks[i].out_y = convert->out_y;
5202     tasks[i].tmpline = convert->tmpline[i];
5203 
5204     tasks[i].height_0 = i * lines_per_thread;
5205     tasks[i].height_1 = tasks[i].height_0 + lines_per_thread;
5206     tasks[i].height_1 = MIN (height, tasks[i].height_1);
5207 
5208     tasks_p[i] = &tasks[i];
5209   }
5210 
5211   gst_parallelized_task_runner_run (convert->conversion_runner,
5212       (GstParallelizedTaskFunc) convert_I420_pack_ARGB_task,
5213       (gpointer) tasks_p);
5214 
5215   convert_fill_border (convert, dest);
5216 }
5217 
5218 static void
memset_u24(guint8 * data,guint8 col[3],unsigned int n)5219 memset_u24 (guint8 * data, guint8 col[3], unsigned int n)
5220 {
5221   unsigned int i;
5222 
5223   for (i = 0; i < n; i++) {
5224     data[0] = col[0];
5225     data[1] = col[1];
5226     data[2] = col[2];
5227     data += 3;
5228   }
5229 }
5230 
5231 static void
memset_u32_16(guint8 * data,guint8 col[4],unsigned int n)5232 memset_u32_16 (guint8 * data, guint8 col[4], unsigned int n)
5233 {
5234   unsigned int i;
5235 
5236   for (i = 0; i < n; i += 2) {
5237     data[0] = col[0];
5238     data[1] = col[1];
5239     if (i + 1 < n) {
5240       data[2] = col[2];
5241       data[3] = col[3];
5242     }
5243     data += 4;
5244   }
5245 }
5246 
5247 #define MAKE_BORDER_FUNC(func)                                                  \
5248         for (i = 0; i < out_y; i++)                                             \
5249           func (FRAME_GET_PLANE_LINE (dest, k, i), col, out_maxwidth);          \
5250         if (rb_width || lb_width) {                                             \
5251           for (i = 0; i < out_height; i++) {                                    \
5252             guint8 *d = FRAME_GET_PLANE_LINE (dest, k, i + out_y);              \
5253             if (lb_width)                                                       \
5254               func (d, col, lb_width);                                          \
5255             if (rb_width)                                                       \
5256               func (d + (pstride * r_border), col, rb_width);                   \
5257           }                                                                     \
5258         }                                                                       \
5259         for (i = out_y + out_height; i < out_maxheight; i++)                    \
5260           func (FRAME_GET_PLANE_LINE (dest, k, i), col, out_maxwidth);          \
5261 
5262 static void
convert_fill_border(GstVideoConverter * convert,GstVideoFrame * dest)5263 convert_fill_border (GstVideoConverter * convert, GstVideoFrame * dest)
5264 {
5265   int k, n_planes;
5266   const GstVideoFormatInfo *out_finfo;
5267 
5268   if (!convert->fill_border || !convert->borderline)
5269     return;
5270 
5271   out_finfo = convert->out_info.finfo;
5272 
5273   n_planes = GST_VIDEO_FRAME_N_PLANES (dest);
5274 
5275   for (k = 0; k < n_planes; k++) {
5276     gint i, out_x, out_y, out_width, out_height, pstride, pgroup;
5277     gint r_border, lb_width, rb_width;
5278     gint out_maxwidth, out_maxheight;
5279     gpointer borders;
5280 
5281     out_x = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (out_finfo, k, convert->out_x);
5282     out_y = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (out_finfo, k, convert->out_y);
5283     out_width =
5284         GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (out_finfo, k, convert->out_width);
5285     out_height =
5286         GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (out_finfo, k, convert->out_height);
5287     out_maxwidth =
5288         GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (out_finfo, k, convert->out_maxwidth);
5289     out_maxheight =
5290         GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (out_finfo, k,
5291         convert->out_maxheight);
5292 
5293     pstride = GST_VIDEO_FORMAT_INFO_PSTRIDE (out_finfo, k);
5294 
5295     switch (GST_VIDEO_FORMAT_INFO_FORMAT (out_finfo)) {
5296       case GST_VIDEO_FORMAT_YUY2:
5297       case GST_VIDEO_FORMAT_YVYU:
5298       case GST_VIDEO_FORMAT_UYVY:
5299         pgroup = 42;
5300         out_maxwidth = GST_ROUND_UP_2 (out_maxwidth);
5301         break;
5302       default:
5303         pgroup = pstride;
5304         break;
5305     }
5306 
5307     r_border = out_x + out_width;
5308     rb_width = out_maxwidth - r_border;
5309     lb_width = out_x;
5310 
5311     borders = &convert->borders[k];
5312 
5313     switch (pgroup) {
5314       case 1:
5315       {
5316         guint8 col = ((guint8 *) borders)[0];
5317         MAKE_BORDER_FUNC (memset);
5318         break;
5319       }
5320       case 2:
5321       {
5322         guint16 col = ((guint16 *) borders)[0];
5323         MAKE_BORDER_FUNC (video_orc_splat_u16);
5324         break;
5325       }
5326       case 3:
5327       {
5328         guint8 col[3];
5329         col[0] = ((guint8 *) borders)[0];
5330         col[1] = ((guint8 *) borders)[1];
5331         col[2] = ((guint8 *) borders)[2];
5332         MAKE_BORDER_FUNC (memset_u24);
5333         break;
5334       }
5335       case 4:
5336       {
5337         guint32 col = ((guint32 *) borders)[0];
5338         MAKE_BORDER_FUNC (video_orc_splat_u32);
5339         break;
5340       }
5341       case 8:
5342       {
5343         guint64 col = ((guint64 *) borders)[0];
5344         MAKE_BORDER_FUNC (video_orc_splat_u64);
5345         break;
5346       }
5347       case 42:
5348       {
5349         guint8 col[4];
5350         col[0] = ((guint8 *) borders)[0];
5351         col[2] = ((guint8 *) borders)[2];
5352         col[1] = ((guint8 *) borders)[r_border & 1 ? 3 : 1];
5353         col[3] = ((guint8 *) borders)[r_border & 1 ? 1 : 3];
5354         MAKE_BORDER_FUNC (memset_u32_16);
5355         break;
5356       }
5357       default:
5358         break;
5359     }
5360   }
5361 }
5362 
5363 typedef struct
5364 {
5365   const guint8 *s, *s2;
5366   guint8 *d, *d2;
5367   gint sstride, dstride;
5368   gint width, height;
5369   gint fill;
5370 } FSimpleScaleTask;
5371 
5372 static void
convert_plane_fill_task(FSimpleScaleTask * task)5373 convert_plane_fill_task (FSimpleScaleTask * task)
5374 {
5375   video_orc_memset_2d (task->d, task->dstride,
5376       task->fill, task->width, task->height);
5377 }
5378 
5379 static void
convert_plane_fill(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest,gint plane)5380 convert_plane_fill (GstVideoConverter * convert,
5381     const GstVideoFrame * src, GstVideoFrame * dest, gint plane)
5382 {
5383   guint8 *d;
5384   FSimpleScaleTask *tasks;
5385   FSimpleScaleTask **tasks_p;
5386   gint n_threads;
5387   gint lines_per_thread;
5388   gint i;
5389 
5390   d = FRAME_GET_PLANE_LINE (dest, plane, convert->fout_y[plane]);
5391   d += convert->fout_x[plane];
5392 
5393   n_threads = convert->conversion_runner->n_threads;
5394   tasks = g_newa (FSimpleScaleTask, n_threads);
5395   tasks_p = g_newa (FSimpleScaleTask *, n_threads);
5396   lines_per_thread = (convert->fout_height[plane] + n_threads - 1) / n_threads;
5397 
5398   for (i = 0; i < n_threads; i++) {
5399     tasks[i].d = d + i * lines_per_thread * convert->fout_width[plane];
5400 
5401     tasks[i].fill = convert->ffill[plane];
5402     tasks[i].width = convert->fout_width[plane];
5403     tasks[i].height = (i + 1) * lines_per_thread;
5404     tasks[i].height = MIN (tasks[i].height, convert->fout_height[plane]);
5405     tasks[i].height -= i * lines_per_thread;
5406     tasks[i].dstride = FRAME_GET_PLANE_STRIDE (dest, plane);
5407 
5408     tasks_p[i] = &tasks[i];
5409   }
5410 
5411   gst_parallelized_task_runner_run (convert->conversion_runner,
5412       (GstParallelizedTaskFunc) convert_plane_fill_task, (gpointer) tasks_p);
5413 }
5414 
5415 static void
convert_plane_h_double_task(FSimpleScaleTask * task)5416 convert_plane_h_double_task (FSimpleScaleTask * task)
5417 {
5418   video_orc_planar_chroma_422_444 (task->d,
5419       task->dstride, task->s, task->sstride, task->width / 2, task->height);
5420 }
5421 
5422 static void
convert_plane_h_double(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest,gint plane)5423 convert_plane_h_double (GstVideoConverter * convert,
5424     const GstVideoFrame * src, GstVideoFrame * dest, gint plane)
5425 {
5426   guint8 *s, *d;
5427   gint splane = convert->fsplane[plane];
5428   FSimpleScaleTask *tasks;
5429   FSimpleScaleTask **tasks_p;
5430   gint n_threads;
5431   gint lines_per_thread;
5432   gint i;
5433 
5434   s = FRAME_GET_PLANE_LINE (src, splane, convert->fin_y[splane]);
5435   s += convert->fin_x[splane];
5436   d = FRAME_GET_PLANE_LINE (dest, plane, convert->fout_y[plane]);
5437   d += convert->fout_x[plane];
5438 
5439   n_threads = convert->conversion_runner->n_threads;
5440   tasks = g_newa (FSimpleScaleTask, n_threads);
5441   tasks_p = g_newa (FSimpleScaleTask *, n_threads);
5442   lines_per_thread = (convert->fout_height[plane] + n_threads - 1) / n_threads;
5443 
5444   for (i = 0; i < n_threads; i++) {
5445     tasks[i].dstride = FRAME_GET_PLANE_STRIDE (dest, plane);
5446     tasks[i].sstride = FRAME_GET_PLANE_STRIDE (src, splane);
5447 
5448     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
5449     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
5450 
5451     tasks[i].width = convert->fout_width[plane];
5452     tasks[i].height = (i + 1) * lines_per_thread;
5453     tasks[i].height = MIN (tasks[i].height, convert->fout_height[plane]);
5454     tasks[i].height -= i * lines_per_thread;
5455 
5456     tasks_p[i] = &tasks[i];
5457   }
5458 
5459   gst_parallelized_task_runner_run (convert->conversion_runner,
5460       (GstParallelizedTaskFunc) convert_plane_h_double_task,
5461       (gpointer) tasks_p);
5462 }
5463 
5464 static void
convert_plane_h_halve_task(FSimpleScaleTask * task)5465 convert_plane_h_halve_task (FSimpleScaleTask * task)
5466 {
5467   video_orc_planar_chroma_444_422 (task->d,
5468       task->dstride, task->s, task->sstride, task->width, task->height);
5469 }
5470 
5471 static void
convert_plane_h_halve(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest,gint plane)5472 convert_plane_h_halve (GstVideoConverter * convert,
5473     const GstVideoFrame * src, GstVideoFrame * dest, gint plane)
5474 {
5475   guint8 *s, *d;
5476   gint splane = convert->fsplane[plane];
5477   FSimpleScaleTask *tasks;
5478   FSimpleScaleTask **tasks_p;
5479   gint n_threads;
5480   gint lines_per_thread;
5481   gint i;
5482 
5483   s = FRAME_GET_PLANE_LINE (src, splane, convert->fin_y[splane]);
5484   s += convert->fin_x[splane];
5485   d = FRAME_GET_PLANE_LINE (dest, plane, convert->fout_y[plane]);
5486   d += convert->fout_x[plane];
5487 
5488   n_threads = convert->conversion_runner->n_threads;
5489   tasks = g_newa (FSimpleScaleTask, n_threads);
5490   tasks_p = g_newa (FSimpleScaleTask *, n_threads);
5491   lines_per_thread = (convert->fout_height[plane] + n_threads - 1) / n_threads;
5492 
5493   for (i = 0; i < n_threads; i++) {
5494     tasks[i].dstride = FRAME_GET_PLANE_STRIDE (dest, plane);
5495     tasks[i].sstride = FRAME_GET_PLANE_STRIDE (src, splane);
5496 
5497     tasks[i].d = d + i * lines_per_thread * tasks[i].dstride;
5498     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride;
5499 
5500     tasks[i].width = convert->fout_width[plane];
5501     tasks[i].height = (i + 1) * lines_per_thread;
5502     tasks[i].height = MIN (tasks[i].height, convert->fout_height[plane]);
5503     tasks[i].height -= i * lines_per_thread;
5504 
5505     tasks_p[i] = &tasks[i];
5506   }
5507 
5508   gst_parallelized_task_runner_run (convert->conversion_runner,
5509       (GstParallelizedTaskFunc) convert_plane_h_halve_task, (gpointer) tasks_p);
5510 }
5511 
5512 static void
convert_plane_v_double_task(FSimpleScaleTask * task)5513 convert_plane_v_double_task (FSimpleScaleTask * task)
5514 {
5515   video_orc_planar_chroma_420_422 (task->d, 2 * task->dstride, task->d2,
5516       2 * task->dstride, task->s, task->sstride, task->width, task->height / 2);
5517 }
5518 
5519 static void
convert_plane_v_double(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest,gint plane)5520 convert_plane_v_double (GstVideoConverter * convert,
5521     const GstVideoFrame * src, GstVideoFrame * dest, gint plane)
5522 {
5523   guint8 *s, *d1, *d2;
5524   gint ds, splane = convert->fsplane[plane];
5525   FSimpleScaleTask *tasks;
5526   FSimpleScaleTask **tasks_p;
5527   gint n_threads;
5528   gint lines_per_thread;
5529   gint i;
5530 
5531   s = FRAME_GET_PLANE_LINE (src, splane, convert->fin_y[splane]);
5532   s += convert->fin_x[splane];
5533   d1 = FRAME_GET_PLANE_LINE (dest, plane, convert->fout_y[plane]);
5534   d1 += convert->fout_x[plane];
5535   d2 = FRAME_GET_PLANE_LINE (dest, plane, convert->fout_y[plane] + 1);
5536   d2 += convert->fout_x[plane];
5537   ds = FRAME_GET_PLANE_STRIDE (dest, plane);
5538 
5539   n_threads = convert->conversion_runner->n_threads;
5540   tasks = g_newa (FSimpleScaleTask, n_threads);
5541   tasks_p = g_newa (FSimpleScaleTask *, n_threads);
5542   lines_per_thread =
5543       GST_ROUND_UP_2 ((convert->fout_height[plane] + n_threads -
5544           1) / n_threads);
5545 
5546   for (i = 0; i < n_threads; i++) {
5547     tasks[i].d = d1 + i * lines_per_thread * ds;
5548     tasks[i].d2 = d2 + i * lines_per_thread * ds;
5549     tasks[i].dstride = ds;
5550     tasks[i].sstride = FRAME_GET_PLANE_STRIDE (src, splane);
5551     tasks[i].s = s + i * lines_per_thread * tasks[i].sstride / 2;
5552 
5553     tasks[i].width = convert->fout_width[plane];
5554     tasks[i].height = (i + 1) * lines_per_thread;
5555     tasks[i].height = MIN (tasks[i].height, convert->fout_height[plane]);
5556     tasks[i].height -= i * lines_per_thread;
5557 
5558     tasks_p[i] = &tasks[i];
5559   }
5560 
5561   gst_parallelized_task_runner_run (convert->conversion_runner,
5562       (GstParallelizedTaskFunc) convert_plane_v_double_task,
5563       (gpointer) tasks_p);
5564 }
5565 
5566 static void
convert_plane_v_halve_task(FSimpleScaleTask * task)5567 convert_plane_v_halve_task (FSimpleScaleTask * task)
5568 {
5569   video_orc_planar_chroma_422_420 (task->d, task->dstride, task->s,
5570       2 * task->sstride, task->s2, 2 * task->sstride, task->width,
5571       task->height);
5572 }
5573 
5574 static void
convert_plane_v_halve(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest,gint plane)5575 convert_plane_v_halve (GstVideoConverter * convert,
5576     const GstVideoFrame * src, GstVideoFrame * dest, gint plane)
5577 {
5578   guint8 *s1, *s2, *d;
5579   gint ss, ds, splane = convert->fsplane[plane];
5580   FSimpleScaleTask *tasks;
5581   FSimpleScaleTask **tasks_p;
5582   gint n_threads;
5583   gint lines_per_thread;
5584   gint i;
5585 
5586   s1 = FRAME_GET_PLANE_LINE (src, splane, convert->fin_y[splane]);
5587   s1 += convert->fin_x[splane];
5588   s2 = FRAME_GET_PLANE_LINE (src, splane, convert->fin_y[splane] + 1);
5589   s2 += convert->fin_x[splane];
5590   d = FRAME_GET_PLANE_LINE (dest, plane, convert->fout_y[plane]);
5591   d += convert->fout_x[plane];
5592 
5593   ss = FRAME_GET_PLANE_STRIDE (src, splane);
5594   ds = FRAME_GET_PLANE_STRIDE (dest, plane);
5595 
5596   n_threads = convert->conversion_runner->n_threads;
5597   tasks = g_newa (FSimpleScaleTask, n_threads);
5598   tasks_p = g_newa (FSimpleScaleTask *, n_threads);
5599   lines_per_thread = (convert->fout_height[plane] + n_threads - 1) / n_threads;
5600 
5601   for (i = 0; i < n_threads; i++) {
5602     tasks[i].d = d + i * lines_per_thread * ds;
5603     tasks[i].dstride = ds;
5604     tasks[i].s = s1 + i * lines_per_thread * ss * 2;
5605     tasks[i].s2 = s2 + i * lines_per_thread * ss * 2;
5606     tasks[i].sstride = ss;
5607 
5608     tasks[i].width = convert->fout_width[plane];
5609     tasks[i].height = (i + 1) * lines_per_thread;
5610     tasks[i].height = MIN (tasks[i].height, convert->fout_height[plane]);
5611     tasks[i].height -= i * lines_per_thread;
5612 
5613     tasks_p[i] = &tasks[i];
5614   }
5615 
5616   gst_parallelized_task_runner_run (convert->conversion_runner,
5617       (GstParallelizedTaskFunc) convert_plane_v_halve_task, (gpointer) tasks_p);
5618 }
5619 
5620 static void
convert_plane_hv_double_task(FSimpleScaleTask * task)5621 convert_plane_hv_double_task (FSimpleScaleTask * task)
5622 {
5623   video_orc_planar_chroma_420_444 (task->d, 2 * task->dstride, task->d2,
5624       2 * task->dstride, task->s, task->sstride, (task->width + 1) / 2,
5625       task->height / 2);
5626 }
5627 
5628 static void
convert_plane_hv_double(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest,gint plane)5629 convert_plane_hv_double (GstVideoConverter * convert,
5630     const GstVideoFrame * src, GstVideoFrame * dest, gint plane)
5631 {
5632   guint8 *s, *d1, *d2;
5633   gint ss, ds, splane = convert->fsplane[plane];
5634   FSimpleScaleTask *tasks;
5635   FSimpleScaleTask **tasks_p;
5636   gint n_threads;
5637   gint lines_per_thread;
5638   gint i;
5639 
5640   s = FRAME_GET_PLANE_LINE (src, splane, convert->fin_y[splane]);
5641   s += convert->fin_x[splane];
5642   d1 = FRAME_GET_PLANE_LINE (dest, plane, convert->fout_y[plane]);
5643   d1 += convert->fout_x[plane];
5644   d2 = FRAME_GET_PLANE_LINE (dest, plane, convert->fout_y[plane] + 1);
5645   d2 += convert->fout_x[plane];
5646   ss = FRAME_GET_PLANE_STRIDE (src, splane);
5647   ds = FRAME_GET_PLANE_STRIDE (dest, plane);
5648 
5649   n_threads = convert->conversion_runner->n_threads;
5650   tasks = g_newa (FSimpleScaleTask, n_threads);
5651   tasks_p = g_newa (FSimpleScaleTask *, n_threads);
5652   lines_per_thread =
5653       GST_ROUND_UP_2 ((convert->fout_height[plane] + n_threads -
5654           1) / n_threads);
5655 
5656   for (i = 0; i < n_threads; i++) {
5657     tasks[i].d = d1 + i * lines_per_thread * ds;
5658     tasks[i].d2 = d2 + i * lines_per_thread * ds;
5659     tasks[i].dstride = ds;
5660     tasks[i].sstride = ss;
5661     tasks[i].s = s + i * lines_per_thread * ss / 2;
5662 
5663     tasks[i].width = convert->fout_width[plane];
5664     tasks[i].height = (i + 1) * lines_per_thread;
5665     tasks[i].height = MIN (tasks[i].height, convert->fout_height[plane]);
5666     tasks[i].height -= i * lines_per_thread;
5667 
5668     tasks_p[i] = &tasks[i];
5669   }
5670 
5671   gst_parallelized_task_runner_run (convert->conversion_runner,
5672       (GstParallelizedTaskFunc) convert_plane_hv_double_task,
5673       (gpointer) tasks_p);
5674 }
5675 
5676 static void
convert_plane_hv_halve_task(FSimpleScaleTask * task)5677 convert_plane_hv_halve_task (FSimpleScaleTask * task)
5678 {
5679   video_orc_planar_chroma_444_420 (task->d, task->dstride, task->s,
5680       2 * task->sstride, task->s2, 2 * task->sstride, task->width,
5681       task->height);
5682 }
5683 
5684 static void
convert_plane_hv_halve(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest,gint plane)5685 convert_plane_hv_halve (GstVideoConverter * convert,
5686     const GstVideoFrame * src, GstVideoFrame * dest, gint plane)
5687 {
5688   guint8 *s1, *s2, *d;
5689   gint ss, ds, splane = convert->fsplane[plane];
5690   FSimpleScaleTask *tasks;
5691   FSimpleScaleTask **tasks_p;
5692   gint n_threads;
5693   gint lines_per_thread;
5694   gint i;
5695 
5696   s1 = FRAME_GET_PLANE_LINE (src, splane, convert->fin_y[splane]);
5697   s1 += convert->fin_x[splane];
5698   s2 = FRAME_GET_PLANE_LINE (src, splane, convert->fin_y[splane] + 1);
5699   s2 += convert->fin_x[splane];
5700   d = FRAME_GET_PLANE_LINE (dest, plane, convert->fout_y[plane]);
5701   d += convert->fout_x[plane];
5702   ss = FRAME_GET_PLANE_STRIDE (src, splane);
5703   ds = FRAME_GET_PLANE_STRIDE (dest, plane);
5704 
5705   n_threads = convert->conversion_runner->n_threads;
5706   tasks = g_newa (FSimpleScaleTask, n_threads);
5707   tasks_p = g_newa (FSimpleScaleTask *, n_threads);
5708   lines_per_thread = (convert->fout_height[plane] + n_threads - 1) / n_threads;
5709 
5710   for (i = 0; i < n_threads; i++) {
5711     tasks[i].d = d + i * lines_per_thread * ds;
5712     tasks[i].dstride = ds;
5713     tasks[i].s = s1 + i * lines_per_thread * ss * 2;
5714     tasks[i].s2 = s2 + i * lines_per_thread * ss * 2;
5715     tasks[i].sstride = ss;
5716 
5717     tasks[i].width = convert->fout_width[plane];
5718     tasks[i].height = (i + 1) * lines_per_thread;
5719     tasks[i].height = MIN (tasks[i].height, convert->fout_height[plane]);
5720     tasks[i].height -= i * lines_per_thread;
5721 
5722     tasks_p[i] = &tasks[i];
5723   }
5724 
5725   gst_parallelized_task_runner_run (convert->conversion_runner,
5726       (GstParallelizedTaskFunc) convert_plane_hv_halve_task,
5727       (gpointer) tasks_p);
5728 }
5729 
5730 typedef struct
5731 {
5732   GstVideoScaler *h_scaler, *v_scaler;
5733   GstVideoFormat format;
5734   const guint8 *s;
5735   guint8 *d;
5736   gint sstride, dstride;
5737   guint x, y, w, h;
5738 } FScaleTask;
5739 
5740 static void
convert_plane_hv_task(FScaleTask * task)5741 convert_plane_hv_task (FScaleTask * task)
5742 {
5743   gst_video_scaler_2d (task->h_scaler, task->v_scaler, task->format,
5744       (guint8 *) task->s, task->sstride,
5745       task->d, task->dstride, task->x, task->y, task->w, task->h);
5746 }
5747 
5748 static void
convert_plane_hv(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest,gint plane)5749 convert_plane_hv (GstVideoConverter * convert,
5750     const GstVideoFrame * src, GstVideoFrame * dest, gint plane)
5751 {
5752   gint in_x, in_y, out_x, out_y, out_width, out_height;
5753   GstVideoFormat format;
5754   gint splane = convert->fsplane[plane];
5755   guint8 *s, *d;
5756   gint sstride, dstride;
5757   FScaleTask *tasks;
5758   FScaleTask **tasks_p;
5759   gint i, n_threads, lines_per_thread;
5760 
5761   in_x = convert->fin_x[splane];
5762   in_y = convert->fin_y[splane];
5763   out_x = convert->fout_x[plane];
5764   out_y = convert->fout_y[plane];
5765   out_width = convert->fout_width[plane];
5766   out_height = convert->fout_height[plane];
5767   format = convert->fformat[plane];
5768 
5769   s = FRAME_GET_PLANE_LINE (src, splane, in_y);
5770   s += in_x;
5771   d = FRAME_GET_PLANE_LINE (dest, plane, out_y);
5772   d += out_x;
5773 
5774   sstride = FRAME_GET_PLANE_STRIDE (src, splane);
5775   dstride = FRAME_GET_PLANE_STRIDE (dest, plane);
5776 
5777   n_threads = convert->conversion_runner->n_threads;
5778   tasks = g_newa (FScaleTask, n_threads);
5779   tasks_p = g_newa (FScaleTask *, n_threads);
5780 
5781   lines_per_thread = (out_height + n_threads - 1) / n_threads;
5782 
5783   for (i = 0; i < n_threads; i++) {
5784     tasks[i].h_scaler =
5785         convert->fh_scaler[plane].scaler ? convert->
5786         fh_scaler[plane].scaler[i] : NULL;
5787     tasks[i].v_scaler =
5788         convert->fv_scaler[plane].scaler ? convert->
5789         fv_scaler[plane].scaler[i] : NULL;
5790     tasks[i].format = format;
5791     tasks[i].s = s;
5792     tasks[i].d = d;
5793     tasks[i].sstride = sstride;
5794     tasks[i].dstride = dstride;
5795 
5796     tasks[i].x = 0;
5797     tasks[i].w = out_width;
5798 
5799     tasks[i].y = i * lines_per_thread;
5800     tasks[i].h = tasks[i].y + lines_per_thread;
5801     tasks[i].h = MIN (out_height, tasks[i].h);
5802 
5803     tasks_p[i] = &tasks[i];
5804   }
5805 
5806   gst_parallelized_task_runner_run (convert->conversion_runner,
5807       (GstParallelizedTaskFunc) convert_plane_hv_task, (gpointer) tasks_p);
5808 }
5809 
5810 static void
convert_scale_planes(GstVideoConverter * convert,const GstVideoFrame * src,GstVideoFrame * dest)5811 convert_scale_planes (GstVideoConverter * convert,
5812     const GstVideoFrame * src, GstVideoFrame * dest)
5813 {
5814   int i, n_planes;
5815 
5816   n_planes = GST_VIDEO_FRAME_N_PLANES (dest);
5817   for (i = 0; i < n_planes; i++) {
5818     if (convert->fconvert[i])
5819       convert->fconvert[i] (convert, src, dest, i);
5820   }
5821   convert_fill_border (convert, dest);
5822 }
5823 
5824 static GstVideoFormat
get_scale_format(GstVideoFormat format,gint plane)5825 get_scale_format (GstVideoFormat format, gint plane)
5826 {
5827   GstVideoFormat res = GST_VIDEO_FORMAT_UNKNOWN;
5828 
5829   switch (format) {
5830     case GST_VIDEO_FORMAT_I420:
5831     case GST_VIDEO_FORMAT_YV12:
5832     case GST_VIDEO_FORMAT_Y41B:
5833     case GST_VIDEO_FORMAT_Y42B:
5834     case GST_VIDEO_FORMAT_Y444:
5835     case GST_VIDEO_FORMAT_GRAY8:
5836     case GST_VIDEO_FORMAT_A420:
5837     case GST_VIDEO_FORMAT_YUV9:
5838     case GST_VIDEO_FORMAT_YVU9:
5839     case GST_VIDEO_FORMAT_GBR:
5840     case GST_VIDEO_FORMAT_GBRA:
5841       res = GST_VIDEO_FORMAT_GRAY8;
5842       break;
5843     case GST_VIDEO_FORMAT_GRAY16_BE:
5844     case GST_VIDEO_FORMAT_GRAY16_LE:
5845       res = GST_VIDEO_FORMAT_GRAY16_BE;
5846       break;
5847     case GST_VIDEO_FORMAT_YUY2:
5848     case GST_VIDEO_FORMAT_UYVY:
5849     case GST_VIDEO_FORMAT_VYUY:
5850     case GST_VIDEO_FORMAT_YVYU:
5851     case GST_VIDEO_FORMAT_AYUV:
5852     case GST_VIDEO_FORMAT_VUYA:
5853     case GST_VIDEO_FORMAT_RGBx:
5854     case GST_VIDEO_FORMAT_BGRx:
5855     case GST_VIDEO_FORMAT_xRGB:
5856     case GST_VIDEO_FORMAT_xBGR:
5857     case GST_VIDEO_FORMAT_RGBA:
5858     case GST_VIDEO_FORMAT_BGRA:
5859     case GST_VIDEO_FORMAT_ARGB:
5860     case GST_VIDEO_FORMAT_ABGR:
5861     case GST_VIDEO_FORMAT_RGB:
5862     case GST_VIDEO_FORMAT_BGR:
5863     case GST_VIDEO_FORMAT_v308:
5864     case GST_VIDEO_FORMAT_IYU2:
5865     case GST_VIDEO_FORMAT_ARGB64:
5866     case GST_VIDEO_FORMAT_AYUV64:
5867       res = format;
5868       break;
5869     case GST_VIDEO_FORMAT_RGB15:
5870     case GST_VIDEO_FORMAT_BGR15:
5871     case GST_VIDEO_FORMAT_RGB16:
5872     case GST_VIDEO_FORMAT_BGR16:
5873       res = GST_VIDEO_FORMAT_NV12;
5874       break;
5875     case GST_VIDEO_FORMAT_NV12:
5876     case GST_VIDEO_FORMAT_NV21:
5877     case GST_VIDEO_FORMAT_NV16:
5878     case GST_VIDEO_FORMAT_NV61:
5879     case GST_VIDEO_FORMAT_NV24:
5880       res = plane == 0 ? GST_VIDEO_FORMAT_GRAY8 : GST_VIDEO_FORMAT_NV12;
5881       break;
5882     case GST_VIDEO_FORMAT_UNKNOWN:
5883     case GST_VIDEO_FORMAT_ENCODED:
5884     case GST_VIDEO_FORMAT_v210:
5885     case GST_VIDEO_FORMAT_v216:
5886     case GST_VIDEO_FORMAT_Y210:
5887     case GST_VIDEO_FORMAT_Y410:
5888     case GST_VIDEO_FORMAT_UYVP:
5889     case GST_VIDEO_FORMAT_RGB8P:
5890     case GST_VIDEO_FORMAT_IYU1:
5891     case GST_VIDEO_FORMAT_r210:
5892     case GST_VIDEO_FORMAT_I420_10BE:
5893     case GST_VIDEO_FORMAT_I420_10LE:
5894     case GST_VIDEO_FORMAT_I422_10BE:
5895     case GST_VIDEO_FORMAT_I422_10LE:
5896     case GST_VIDEO_FORMAT_Y444_10BE:
5897     case GST_VIDEO_FORMAT_Y444_10LE:
5898     case GST_VIDEO_FORMAT_I420_12BE:
5899     case GST_VIDEO_FORMAT_I420_12LE:
5900     case GST_VIDEO_FORMAT_I422_12BE:
5901     case GST_VIDEO_FORMAT_I422_12LE:
5902     case GST_VIDEO_FORMAT_Y444_12BE:
5903     case GST_VIDEO_FORMAT_Y444_12LE:
5904     case GST_VIDEO_FORMAT_GBR_10BE:
5905     case GST_VIDEO_FORMAT_GBR_10LE:
5906     case GST_VIDEO_FORMAT_GBRA_10BE:
5907     case GST_VIDEO_FORMAT_GBRA_10LE:
5908     case GST_VIDEO_FORMAT_GBR_12BE:
5909     case GST_VIDEO_FORMAT_GBR_12LE:
5910     case GST_VIDEO_FORMAT_GBRA_12BE:
5911     case GST_VIDEO_FORMAT_GBRA_12LE:
5912     case GST_VIDEO_FORMAT_NV12_64Z32:
5913     case GST_VIDEO_FORMAT_A420_10BE:
5914     case GST_VIDEO_FORMAT_A420_10LE:
5915     case GST_VIDEO_FORMAT_A422_10BE:
5916     case GST_VIDEO_FORMAT_A422_10LE:
5917     case GST_VIDEO_FORMAT_A444_10BE:
5918     case GST_VIDEO_FORMAT_A444_10LE:
5919     case GST_VIDEO_FORMAT_P010_10BE:
5920     case GST_VIDEO_FORMAT_P010_10LE:
5921     case GST_VIDEO_FORMAT_GRAY10_LE32:
5922     case GST_VIDEO_FORMAT_NV12_10LE32:
5923     case GST_VIDEO_FORMAT_NV16_10LE32:
5924     case GST_VIDEO_FORMAT_NV12_10LE40:
5925     case GST_VIDEO_FORMAT_BGR10A2_LE:
5926       res = format;
5927       g_assert_not_reached ();
5928       break;
5929   }
5930   return res;
5931 }
5932 
5933 static gboolean
is_merge_yuv(GstVideoInfo * info)5934 is_merge_yuv (GstVideoInfo * info)
5935 {
5936   switch (GST_VIDEO_INFO_FORMAT (info)) {
5937     case GST_VIDEO_FORMAT_YUY2:
5938     case GST_VIDEO_FORMAT_YVYU:
5939     case GST_VIDEO_FORMAT_UYVY:
5940     case GST_VIDEO_FORMAT_VYUY:
5941       return TRUE;
5942     default:
5943       return FALSE;
5944   }
5945 }
5946 
5947 static gboolean
setup_scale(GstVideoConverter * convert)5948 setup_scale (GstVideoConverter * convert)
5949 {
5950   int i, n_planes;
5951   gint method, cr_method, in_width, in_height, out_width, out_height;
5952   guint taps;
5953   GstVideoInfo *in_info, *out_info;
5954   const GstVideoFormatInfo *in_finfo, *out_finfo;
5955   GstVideoFormat in_format, out_format;
5956   gboolean interlaced;
5957   guint n_threads = convert->conversion_runner->n_threads;
5958 
5959   in_info = &convert->in_info;
5960   out_info = &convert->out_info;
5961 
5962   in_finfo = in_info->finfo;
5963   out_finfo = out_info->finfo;
5964 
5965   n_planes = GST_VIDEO_INFO_N_PLANES (out_info);
5966 
5967   interlaced = GST_VIDEO_INFO_IS_INTERLACED (&convert->in_info);
5968 
5969   method = GET_OPT_RESAMPLER_METHOD (convert);
5970   if (method == GST_VIDEO_RESAMPLER_METHOD_NEAREST)
5971     cr_method = method;
5972   else
5973     cr_method = GET_OPT_CHROMA_RESAMPLER_METHOD (convert);
5974   taps = GET_OPT_RESAMPLER_TAPS (convert);
5975 
5976   in_format = GST_VIDEO_INFO_FORMAT (in_info);
5977   out_format = GST_VIDEO_INFO_FORMAT (out_info);
5978 
5979   switch (in_format) {
5980     case GST_VIDEO_FORMAT_RGB15:
5981     case GST_VIDEO_FORMAT_RGB16:
5982     case GST_VIDEO_FORMAT_BGR15:
5983     case GST_VIDEO_FORMAT_BGR16:
5984 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
5985     case GST_VIDEO_FORMAT_GRAY16_BE:
5986 #else
5987     case GST_VIDEO_FORMAT_GRAY16_LE:
5988 #endif
5989       if (method != GST_VIDEO_RESAMPLER_METHOD_NEAREST) {
5990         GST_DEBUG ("%s only with nearest resampling",
5991             gst_video_format_to_string (in_format));
5992         return FALSE;
5993       }
5994       break;
5995     default:
5996       break;
5997   }
5998 
5999   in_width = convert->in_width;
6000   in_height = convert->in_height;
6001   out_width = convert->out_width;
6002   out_height = convert->out_height;
6003 
6004   if (n_planes == 1 && !GST_VIDEO_FORMAT_INFO_IS_GRAY (out_finfo)) {
6005     gint pstride;
6006     guint j;
6007 
6008     if (is_merge_yuv (in_info)) {
6009       GstVideoScaler *y_scaler, *uv_scaler;
6010 
6011       if (in_width != out_width) {
6012         convert->fh_scaler[0].scaler = g_new (GstVideoScaler *, n_threads);
6013         for (j = 0; j < n_threads; j++) {
6014           y_scaler =
6015               gst_video_scaler_new (method, GST_VIDEO_SCALER_FLAG_NONE, taps,
6016               GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (in_finfo, GST_VIDEO_COMP_Y,
6017                   in_width), GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (out_finfo,
6018                   GST_VIDEO_COMP_Y, out_width), convert->config);
6019           uv_scaler =
6020               gst_video_scaler_new (method, GST_VIDEO_SCALER_FLAG_NONE,
6021               gst_video_scaler_get_max_taps (y_scaler),
6022               GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (in_finfo, GST_VIDEO_COMP_U,
6023                   in_width), GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (out_finfo,
6024                   GST_VIDEO_COMP_U, out_width), convert->config);
6025 
6026           convert->fh_scaler[0].scaler[j] =
6027               gst_video_scaler_combine_packed_YUV (y_scaler, uv_scaler,
6028               in_format, out_format);
6029 
6030           gst_video_scaler_free (y_scaler);
6031           gst_video_scaler_free (uv_scaler);
6032         }
6033       } else {
6034         convert->fh_scaler[0].scaler = NULL;
6035       }
6036 
6037       pstride = GST_VIDEO_FORMAT_INFO_PSTRIDE (out_finfo, GST_VIDEO_COMP_Y);
6038       convert->fin_x[0] = GST_ROUND_UP_2 (convert->in_x) * pstride;
6039       convert->fout_x[0] = GST_ROUND_UP_2 (convert->out_x) * pstride;
6040 
6041     } else {
6042       if (in_width != out_width && in_width != 0 && out_width != 0) {
6043         convert->fh_scaler[0].scaler = g_new (GstVideoScaler *, n_threads);
6044         for (j = 0; j < n_threads; j++) {
6045           convert->fh_scaler[0].scaler[j] =
6046               gst_video_scaler_new (method, GST_VIDEO_SCALER_FLAG_NONE, taps,
6047               in_width, out_width, convert->config);
6048         }
6049       } else {
6050         convert->fh_scaler[0].scaler = NULL;
6051       }
6052 
6053       pstride = GST_VIDEO_FORMAT_INFO_PSTRIDE (out_finfo, GST_VIDEO_COMP_R);
6054       convert->fin_x[0] = convert->in_x * pstride;
6055       convert->fout_x[0] = convert->out_x * pstride;
6056     }
6057 
6058     if (in_height != out_height && in_height != 0 && out_height != 0) {
6059       convert->fv_scaler[0].scaler = g_new (GstVideoScaler *, n_threads);
6060 
6061       for (j = 0; j < n_threads; j++) {
6062         convert->fv_scaler[0].scaler[j] =
6063             gst_video_scaler_new (method,
6064             interlaced ?
6065             GST_VIDEO_SCALER_FLAG_INTERLACED : GST_VIDEO_SCALER_FLAG_NONE, taps,
6066             in_height, out_height, convert->config);
6067       }
6068     } else {
6069       convert->fv_scaler[0].scaler = NULL;
6070     }
6071 
6072     convert->fin_y[0] = convert->in_y;
6073     convert->fout_y[0] = convert->out_y;
6074     convert->fout_width[0] = out_width;
6075     convert->fout_height[0] = out_height;
6076     convert->fconvert[0] = convert_plane_hv;
6077     convert->fformat[0] = get_scale_format (in_format, 0);
6078     convert->fsplane[0] = 0;
6079   } else {
6080     for (i = 0; i < n_planes; i++) {
6081       gint comp, n_comp, j, iw, ih, ow, oh, pstride;
6082       gboolean need_v_scaler, need_h_scaler;
6083       GstStructure *config;
6084       gint resample_method;
6085 
6086       n_comp = GST_VIDEO_FORMAT_INFO_N_COMPONENTS (in_finfo);
6087 
6088       /* find the component in this plane and map it to the plane of
6089        * the source */
6090       comp = -1;
6091       for (j = 0; j < n_comp; j++) {
6092         if (GST_VIDEO_FORMAT_INFO_PLANE (out_finfo, j) == i) {
6093           comp = j;
6094           break;
6095         }
6096       }
6097 
6098       iw = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (in_finfo, i, in_width);
6099       ih = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (in_finfo, i, in_height);
6100       ow = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (out_finfo, i, out_width);
6101       oh = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (out_finfo, i, out_height);
6102 
6103       GST_DEBUG ("plane %d: %dx%d -> %dx%d", i, iw, ih, ow, oh);
6104 
6105       convert->fout_width[i] = ow;
6106       convert->fout_height[i] = oh;
6107 
6108       pstride = GST_VIDEO_FORMAT_INFO_PSTRIDE (out_finfo, i);
6109       convert->fin_x[i] =
6110           GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (in_finfo, i, convert->in_x);
6111       convert->fin_x[i] *= pstride;
6112       convert->fin_y[i] =
6113           GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (in_finfo, i, convert->in_y);
6114       convert->fout_x[i] =
6115           GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (out_finfo, i, convert->out_x);
6116       convert->fout_x[i] *= pstride;
6117       convert->fout_y[i] =
6118           GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (out_finfo, i, convert->out_y);
6119 
6120       GST_DEBUG ("plane %d: pstride %d", i, pstride);
6121       GST_DEBUG ("plane %d: in_x %d, in_y %d", i, convert->fin_x[i],
6122           convert->fin_y[i]);
6123       GST_DEBUG ("plane %d: out_x %d, out_y %d", i, convert->fout_x[i],
6124           convert->fout_y[i]);
6125 
6126       if (comp == -1) {
6127         convert->fconvert[i] = convert_plane_fill;
6128         if (GST_VIDEO_INFO_IS_YUV (out_info)) {
6129           if (i == 3)
6130             convert->ffill[i] = convert->alpha_value;
6131           if (i == 0)
6132             convert->ffill[i] = 0x00;
6133           else
6134             convert->ffill[i] = 0x80;
6135         } else {
6136           if (i == 3)
6137             convert->ffill[i] = convert->alpha_value;
6138           else
6139             convert->ffill[i] = 0x00;
6140         }
6141         GST_DEBUG ("plane %d fill %02x", i, convert->ffill[i]);
6142         continue;
6143       } else {
6144         convert->fsplane[i] = GST_VIDEO_FORMAT_INFO_PLANE (in_finfo, comp);
6145         GST_DEBUG ("plane %d -> %d (comp %d)", i, convert->fsplane[i], comp);
6146       }
6147 
6148       config = gst_structure_copy (convert->config);
6149 
6150       resample_method = (i == 0 ? method : cr_method);
6151 
6152       need_v_scaler = FALSE;
6153       need_h_scaler = FALSE;
6154       if (iw == ow) {
6155         if (!interlaced && ih == oh) {
6156           convert->fconvert[i] = convert_plane_hv;
6157           GST_DEBUG ("plane %d: copy", i);
6158         } else if (!interlaced && ih == 2 * oh && pstride == 1
6159             && resample_method == GST_VIDEO_RESAMPLER_METHOD_LINEAR) {
6160           convert->fconvert[i] = convert_plane_v_halve;
6161           GST_DEBUG ("plane %d: vertical halve", i);
6162         } else if (!interlaced && 2 * ih == oh && pstride == 1
6163             && resample_method == GST_VIDEO_RESAMPLER_METHOD_NEAREST) {
6164           convert->fconvert[i] = convert_plane_v_double;
6165           GST_DEBUG ("plane %d: vertical double", i);
6166         } else {
6167           convert->fconvert[i] = convert_plane_hv;
6168           GST_DEBUG ("plane %d: vertical scale", i);
6169           need_v_scaler = TRUE;
6170         }
6171       } else if (ih == oh) {
6172         if (!interlaced && iw == 2 * ow && pstride == 1
6173             && resample_method == GST_VIDEO_RESAMPLER_METHOD_LINEAR) {
6174           convert->fconvert[i] = convert_plane_h_halve;
6175           GST_DEBUG ("plane %d: horizontal halve", i);
6176         } else if (!interlaced && 2 * iw == ow && pstride == 1
6177             && resample_method == GST_VIDEO_RESAMPLER_METHOD_NEAREST) {
6178           convert->fconvert[i] = convert_plane_h_double;
6179           GST_DEBUG ("plane %d: horizontal double", i);
6180         } else {
6181           convert->fconvert[i] = convert_plane_hv;
6182           GST_DEBUG ("plane %d: horizontal scale", i);
6183           need_h_scaler = TRUE;
6184         }
6185       } else {
6186         if (!interlaced && iw == 2 * ow && ih == 2 * oh && pstride == 1
6187             && resample_method == GST_VIDEO_RESAMPLER_METHOD_LINEAR) {
6188           convert->fconvert[i] = convert_plane_hv_halve;
6189           GST_DEBUG ("plane %d: horizontal/vertical halve", i);
6190         } else if (!interlaced && 2 * iw == ow && 2 * ih == oh && pstride == 1
6191             && resample_method == GST_VIDEO_RESAMPLER_METHOD_NEAREST) {
6192           convert->fconvert[i] = convert_plane_hv_double;
6193           GST_DEBUG ("plane %d: horizontal/vertical double", i);
6194         } else {
6195           convert->fconvert[i] = convert_plane_hv;
6196           GST_DEBUG ("plane %d: horizontal/vertical scale", i);
6197           need_v_scaler = TRUE;
6198           need_h_scaler = TRUE;
6199         }
6200       }
6201 
6202       if (need_h_scaler && iw != 0 && ow != 0) {
6203         convert->fh_scaler[i].scaler = g_new (GstVideoScaler *, n_threads);
6204 
6205         for (j = 0; j < n_threads; j++) {
6206           convert->fh_scaler[i].scaler[j] =
6207               gst_video_scaler_new (resample_method, GST_VIDEO_SCALER_FLAG_NONE,
6208               taps, iw, ow, config);
6209         }
6210       } else {
6211         convert->fh_scaler[i].scaler = NULL;
6212       }
6213 
6214       if (need_v_scaler && ih != 0 && oh != 0) {
6215         convert->fv_scaler[i].scaler = g_new (GstVideoScaler *, n_threads);
6216 
6217         for (j = 0; j < n_threads; j++) {
6218           convert->fv_scaler[i].scaler[j] =
6219               gst_video_scaler_new (resample_method,
6220               interlaced ?
6221               GST_VIDEO_SCALER_FLAG_INTERLACED : GST_VIDEO_SCALER_FLAG_NONE,
6222               taps, ih, oh, config);
6223         }
6224       } else {
6225         convert->fv_scaler[i].scaler = NULL;
6226       }
6227 
6228       gst_structure_free (config);
6229       convert->fformat[i] = get_scale_format (in_format, i);
6230     }
6231   }
6232 
6233   return TRUE;
6234 }
6235 
6236 /* Fast paths */
6237 
6238 typedef struct
6239 {
6240   GstVideoFormat in_format;
6241   GstVideoFormat out_format;
6242   gboolean keeps_interlaced;
6243   gboolean needs_color_matrix;
6244   gboolean keeps_size;
6245   gboolean do_crop;
6246   gboolean do_border;
6247   gboolean alpha_copy;
6248   gboolean alpha_set;
6249   gboolean alpha_mult;
6250   gint width_align, height_align;
6251   void (*convert) (GstVideoConverter * convert, const GstVideoFrame * src,
6252       GstVideoFrame * dest);
6253 } VideoTransform;
6254 
6255 static const VideoTransform transforms[] = {
6256   /* planar -> packed */
6257   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_YUY2, TRUE, FALSE, TRUE, FALSE,
6258       FALSE, FALSE, FALSE, FALSE, 0, 0, convert_I420_YUY2},
6259   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_UYVY, TRUE, FALSE, TRUE, FALSE,
6260       FALSE, FALSE, FALSE, FALSE, 0, 0, convert_I420_UYVY},
6261   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_AYUV, TRUE, FALSE, TRUE, FALSE,
6262       FALSE, FALSE, TRUE, FALSE, 0, 0, convert_I420_AYUV},
6263 
6264   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_YUY2, TRUE, FALSE, TRUE, FALSE,
6265       FALSE, FALSE, FALSE, FALSE, 0, 0, convert_I420_YUY2},
6266   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_UYVY, TRUE, FALSE, TRUE, FALSE,
6267       FALSE, FALSE, FALSE, FALSE, 0, 0, convert_I420_UYVY},
6268   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_AYUV, TRUE, FALSE, TRUE, FALSE,
6269       FALSE, FALSE, TRUE, FALSE, 0, 0, convert_I420_AYUV},
6270 
6271   {GST_VIDEO_FORMAT_Y42B, GST_VIDEO_FORMAT_YUY2, TRUE, FALSE, TRUE, TRUE,
6272       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_Y42B_YUY2},
6273   {GST_VIDEO_FORMAT_Y42B, GST_VIDEO_FORMAT_UYVY, TRUE, FALSE, TRUE, TRUE,
6274       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_Y42B_UYVY},
6275   {GST_VIDEO_FORMAT_Y42B, GST_VIDEO_FORMAT_AYUV, TRUE, FALSE, TRUE, TRUE,
6276       TRUE, FALSE, TRUE, FALSE, 1, 0, convert_Y42B_AYUV},
6277 
6278   {GST_VIDEO_FORMAT_Y444, GST_VIDEO_FORMAT_YUY2, TRUE, FALSE, TRUE, TRUE,
6279       TRUE, FALSE, FALSE, FALSE, 1, 0, convert_Y444_YUY2},
6280   {GST_VIDEO_FORMAT_Y444, GST_VIDEO_FORMAT_UYVY, TRUE, FALSE, TRUE, TRUE,
6281       TRUE, FALSE, FALSE, FALSE, 1, 0, convert_Y444_UYVY},
6282   {GST_VIDEO_FORMAT_Y444, GST_VIDEO_FORMAT_AYUV, TRUE, FALSE, TRUE, TRUE,
6283       TRUE, FALSE, TRUE, FALSE, 0, 0, convert_Y444_AYUV},
6284 
6285   /* packed -> packed */
6286   {GST_VIDEO_FORMAT_YUY2, GST_VIDEO_FORMAT_YUY2, TRUE, FALSE, FALSE, TRUE,
6287       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6288   {GST_VIDEO_FORMAT_YUY2, GST_VIDEO_FORMAT_UYVY, TRUE, FALSE, TRUE, TRUE,
6289       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_UYVY_YUY2},      /* alias */
6290   {GST_VIDEO_FORMAT_YUY2, GST_VIDEO_FORMAT_AYUV, TRUE, FALSE, TRUE, TRUE,
6291       TRUE, FALSE, TRUE, FALSE, 1, 0, convert_YUY2_AYUV},
6292 
6293   {GST_VIDEO_FORMAT_UYVY, GST_VIDEO_FORMAT_UYVY, TRUE, FALSE, FALSE, TRUE,
6294       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6295   {GST_VIDEO_FORMAT_UYVY, GST_VIDEO_FORMAT_YUY2, TRUE, FALSE, TRUE, TRUE,
6296       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_UYVY_YUY2},
6297   {GST_VIDEO_FORMAT_UYVY, GST_VIDEO_FORMAT_AYUV, TRUE, FALSE, TRUE, TRUE,
6298       TRUE, FALSE, TRUE, FALSE, 0, 0, convert_UYVY_AYUV},
6299 
6300   {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_FORMAT_AYUV, TRUE, FALSE, FALSE, TRUE, TRUE,
6301       TRUE, FALSE, FALSE, 0, 0, convert_scale_planes},
6302   {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_FORMAT_YUY2, TRUE, FALSE, TRUE, TRUE,
6303       TRUE, FALSE, FALSE, FALSE, 1, 0, convert_AYUV_YUY2},
6304   {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_FORMAT_UYVY, TRUE, FALSE, TRUE, TRUE,
6305       TRUE, FALSE, FALSE, FALSE, 1, 0, convert_AYUV_UYVY},
6306 
6307   /* packed -> planar */
6308   {GST_VIDEO_FORMAT_YUY2, GST_VIDEO_FORMAT_I420, TRUE, FALSE, TRUE, FALSE,
6309       FALSE, FALSE, FALSE, FALSE, 0, 0, convert_YUY2_I420},
6310   {GST_VIDEO_FORMAT_YUY2, GST_VIDEO_FORMAT_YV12, TRUE, FALSE, TRUE, FALSE,
6311       FALSE, FALSE, FALSE, FALSE, 0, 0, convert_YUY2_I420},
6312   {GST_VIDEO_FORMAT_YUY2, GST_VIDEO_FORMAT_Y42B, TRUE, FALSE, TRUE, TRUE,
6313       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_YUY2_Y42B},
6314   {GST_VIDEO_FORMAT_YUY2, GST_VIDEO_FORMAT_Y444, TRUE, FALSE, TRUE, TRUE,
6315       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_YUY2_Y444},
6316   {GST_VIDEO_FORMAT_UYVY, GST_VIDEO_FORMAT_GRAY8, TRUE, TRUE, TRUE, TRUE,
6317       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_UYVY_GRAY8},
6318 
6319   {GST_VIDEO_FORMAT_UYVY, GST_VIDEO_FORMAT_I420, TRUE, FALSE, TRUE, FALSE,
6320       FALSE, FALSE, FALSE, FALSE, 0, 0, convert_UYVY_I420},
6321   {GST_VIDEO_FORMAT_UYVY, GST_VIDEO_FORMAT_YV12, TRUE, FALSE, TRUE, FALSE,
6322       FALSE, FALSE, FALSE, FALSE, 0, 0, convert_UYVY_I420},
6323   {GST_VIDEO_FORMAT_UYVY, GST_VIDEO_FORMAT_Y42B, TRUE, FALSE, TRUE, TRUE,
6324       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_UYVY_Y42B},
6325   {GST_VIDEO_FORMAT_UYVY, GST_VIDEO_FORMAT_Y444, TRUE, FALSE, TRUE, TRUE,
6326       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_UYVY_Y444},
6327 
6328   {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_FORMAT_I420, FALSE, FALSE, TRUE, TRUE,
6329       TRUE, FALSE, FALSE, FALSE, 1, 1, convert_AYUV_I420},
6330   {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_FORMAT_YV12, FALSE, FALSE, TRUE, TRUE,
6331       TRUE, FALSE, FALSE, FALSE, 1, 1, convert_AYUV_I420},
6332   {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_FORMAT_Y42B, TRUE, FALSE, TRUE, TRUE,
6333       TRUE, FALSE, FALSE, FALSE, 1, 0, convert_AYUV_Y42B},
6334   {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_FORMAT_Y444, TRUE, FALSE, TRUE, TRUE,
6335       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_AYUV_Y444},
6336 
6337   /* planar -> planar */
6338   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_I420, TRUE, FALSE, FALSE, TRUE,
6339       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6340   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_YV12, TRUE, FALSE, FALSE, TRUE,
6341       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6342   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_Y41B, FALSE, FALSE, FALSE, TRUE,
6343       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6344   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_Y42B, FALSE, FALSE, FALSE, TRUE,
6345       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6346   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_Y444, FALSE, FALSE, FALSE, TRUE,
6347       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6348   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_GRAY8, FALSE, FALSE, FALSE, TRUE,
6349       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6350   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_A420, FALSE, FALSE, FALSE, TRUE,
6351       TRUE, FALSE, TRUE, FALSE, 0, 0, convert_scale_planes},
6352   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_YUV9, FALSE, FALSE, FALSE, TRUE,
6353       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6354   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_YVU9, FALSE, FALSE, FALSE, TRUE,
6355       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6356 
6357   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_I420, TRUE, FALSE, FALSE, TRUE,
6358       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6359   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_YV12, TRUE, FALSE, FALSE, TRUE,
6360       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6361   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_Y41B, FALSE, FALSE, FALSE, TRUE,
6362       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6363   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_Y42B, FALSE, FALSE, FALSE, TRUE,
6364       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6365   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_Y444, FALSE, FALSE, FALSE, TRUE,
6366       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6367   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_GRAY8, FALSE, FALSE, FALSE, TRUE,
6368       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6369   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_A420, FALSE, FALSE, FALSE, TRUE,
6370       TRUE, FALSE, TRUE, FALSE, 0, 0, convert_scale_planes},
6371   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_YUV9, FALSE, FALSE, FALSE, TRUE,
6372       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6373   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_YVU9, FALSE, FALSE, FALSE, TRUE,
6374       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6375 
6376   {GST_VIDEO_FORMAT_Y41B, GST_VIDEO_FORMAT_I420, FALSE, FALSE, FALSE, TRUE,
6377       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6378   {GST_VIDEO_FORMAT_Y41B, GST_VIDEO_FORMAT_YV12, FALSE, FALSE, FALSE, TRUE,
6379       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6380   {GST_VIDEO_FORMAT_Y41B, GST_VIDEO_FORMAT_Y41B, TRUE, FALSE, FALSE, TRUE,
6381       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6382   {GST_VIDEO_FORMAT_Y41B, GST_VIDEO_FORMAT_Y42B, FALSE, FALSE, FALSE, TRUE,
6383       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6384   {GST_VIDEO_FORMAT_Y41B, GST_VIDEO_FORMAT_Y444, FALSE, FALSE, FALSE, TRUE,
6385       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6386   {GST_VIDEO_FORMAT_Y41B, GST_VIDEO_FORMAT_GRAY8, FALSE, FALSE, FALSE, TRUE,
6387       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6388   {GST_VIDEO_FORMAT_Y41B, GST_VIDEO_FORMAT_A420, FALSE, FALSE, FALSE, TRUE,
6389       TRUE, FALSE, TRUE, FALSE, 0, 0, convert_scale_planes},
6390   {GST_VIDEO_FORMAT_Y41B, GST_VIDEO_FORMAT_YUV9, FALSE, FALSE, FALSE, TRUE,
6391       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6392   {GST_VIDEO_FORMAT_Y41B, GST_VIDEO_FORMAT_YVU9, FALSE, FALSE, FALSE, TRUE,
6393       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6394 
6395   {GST_VIDEO_FORMAT_Y42B, GST_VIDEO_FORMAT_I420, FALSE, FALSE, FALSE, TRUE,
6396       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6397   {GST_VIDEO_FORMAT_Y42B, GST_VIDEO_FORMAT_YV12, FALSE, FALSE, FALSE, TRUE,
6398       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6399   {GST_VIDEO_FORMAT_Y42B, GST_VIDEO_FORMAT_Y41B, FALSE, FALSE, FALSE, TRUE,
6400       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6401   {GST_VIDEO_FORMAT_Y42B, GST_VIDEO_FORMAT_Y42B, TRUE, FALSE, FALSE, TRUE,
6402       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6403   {GST_VIDEO_FORMAT_Y42B, GST_VIDEO_FORMAT_Y444, FALSE, FALSE, FALSE, TRUE,
6404       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6405   {GST_VIDEO_FORMAT_Y42B, GST_VIDEO_FORMAT_GRAY8, FALSE, FALSE, FALSE, TRUE,
6406       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6407   {GST_VIDEO_FORMAT_Y42B, GST_VIDEO_FORMAT_A420, FALSE, FALSE, FALSE, TRUE,
6408       TRUE, FALSE, TRUE, FALSE, 0, 0, convert_scale_planes},
6409   {GST_VIDEO_FORMAT_Y42B, GST_VIDEO_FORMAT_YUV9, FALSE, FALSE, FALSE, TRUE,
6410       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6411   {GST_VIDEO_FORMAT_Y42B, GST_VIDEO_FORMAT_YVU9, FALSE, FALSE, FALSE, TRUE,
6412       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6413 
6414   {GST_VIDEO_FORMAT_Y444, GST_VIDEO_FORMAT_I420, FALSE, FALSE, FALSE, TRUE,
6415       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6416   {GST_VIDEO_FORMAT_Y444, GST_VIDEO_FORMAT_YV12, FALSE, FALSE, FALSE, TRUE,
6417       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6418   {GST_VIDEO_FORMAT_Y444, GST_VIDEO_FORMAT_Y41B, FALSE, FALSE, FALSE, TRUE,
6419       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6420   {GST_VIDEO_FORMAT_Y444, GST_VIDEO_FORMAT_Y42B, FALSE, FALSE, FALSE, TRUE,
6421       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6422   {GST_VIDEO_FORMAT_Y444, GST_VIDEO_FORMAT_Y444, TRUE, FALSE, FALSE, TRUE,
6423       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6424   {GST_VIDEO_FORMAT_Y444, GST_VIDEO_FORMAT_GRAY8, FALSE, FALSE, FALSE, TRUE,
6425       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6426   {GST_VIDEO_FORMAT_Y444, GST_VIDEO_FORMAT_A420, FALSE, FALSE, FALSE, TRUE,
6427       TRUE, FALSE, TRUE, FALSE, 0, 0, convert_scale_planes},
6428   {GST_VIDEO_FORMAT_Y444, GST_VIDEO_FORMAT_YUV9, FALSE, FALSE, FALSE, TRUE,
6429       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6430   {GST_VIDEO_FORMAT_Y444, GST_VIDEO_FORMAT_YVU9, FALSE, FALSE, FALSE, TRUE,
6431       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6432 
6433   {GST_VIDEO_FORMAT_GRAY8, GST_VIDEO_FORMAT_I420, FALSE, FALSE, FALSE, TRUE,
6434       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6435   {GST_VIDEO_FORMAT_GRAY8, GST_VIDEO_FORMAT_YV12, FALSE, FALSE, FALSE, TRUE,
6436       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6437   {GST_VIDEO_FORMAT_GRAY8, GST_VIDEO_FORMAT_Y41B, FALSE, FALSE, FALSE, TRUE,
6438       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6439   {GST_VIDEO_FORMAT_GRAY8, GST_VIDEO_FORMAT_Y42B, FALSE, FALSE, FALSE, TRUE,
6440       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6441   {GST_VIDEO_FORMAT_GRAY8, GST_VIDEO_FORMAT_Y444, FALSE, FALSE, FALSE, TRUE,
6442       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6443   {GST_VIDEO_FORMAT_GRAY8, GST_VIDEO_FORMAT_GRAY8, TRUE, FALSE, FALSE, TRUE,
6444       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6445   {GST_VIDEO_FORMAT_GRAY8, GST_VIDEO_FORMAT_A420, FALSE, FALSE, FALSE, TRUE,
6446       TRUE, FALSE, TRUE, FALSE, 0, 0, convert_scale_planes},
6447   {GST_VIDEO_FORMAT_GRAY8, GST_VIDEO_FORMAT_YUV9, FALSE, FALSE, FALSE, TRUE,
6448       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6449   {GST_VIDEO_FORMAT_GRAY8, GST_VIDEO_FORMAT_YVU9, FALSE, FALSE, FALSE, TRUE,
6450       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6451 
6452   {GST_VIDEO_FORMAT_A420, GST_VIDEO_FORMAT_I420, FALSE, FALSE, FALSE, TRUE,
6453       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6454   {GST_VIDEO_FORMAT_A420, GST_VIDEO_FORMAT_YV12, FALSE, FALSE, FALSE, TRUE,
6455       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6456   {GST_VIDEO_FORMAT_A420, GST_VIDEO_FORMAT_Y41B, FALSE, FALSE, FALSE, TRUE,
6457       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6458   {GST_VIDEO_FORMAT_A420, GST_VIDEO_FORMAT_Y42B, FALSE, FALSE, FALSE, TRUE,
6459       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6460   {GST_VIDEO_FORMAT_A420, GST_VIDEO_FORMAT_Y444, FALSE, FALSE, FALSE, TRUE,
6461       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6462   {GST_VIDEO_FORMAT_A420, GST_VIDEO_FORMAT_GRAY8, FALSE, FALSE, FALSE, TRUE,
6463       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6464   {GST_VIDEO_FORMAT_A420, GST_VIDEO_FORMAT_A420, TRUE, FALSE, FALSE, TRUE,
6465       TRUE, TRUE, FALSE, FALSE, 0, 0, convert_scale_planes},
6466   {GST_VIDEO_FORMAT_A420, GST_VIDEO_FORMAT_YUV9, FALSE, FALSE, FALSE, TRUE,
6467       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6468   {GST_VIDEO_FORMAT_A420, GST_VIDEO_FORMAT_YVU9, FALSE, FALSE, FALSE, TRUE,
6469       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6470 
6471   {GST_VIDEO_FORMAT_YUV9, GST_VIDEO_FORMAT_I420, FALSE, FALSE, FALSE, TRUE,
6472       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6473   {GST_VIDEO_FORMAT_YUV9, GST_VIDEO_FORMAT_YV12, FALSE, FALSE, FALSE, TRUE,
6474       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6475   {GST_VIDEO_FORMAT_YUV9, GST_VIDEO_FORMAT_Y41B, FALSE, FALSE, FALSE, TRUE,
6476       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6477   {GST_VIDEO_FORMAT_YUV9, GST_VIDEO_FORMAT_Y42B, FALSE, FALSE, FALSE, TRUE,
6478       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6479   {GST_VIDEO_FORMAT_YUV9, GST_VIDEO_FORMAT_Y444, FALSE, FALSE, FALSE, TRUE,
6480       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6481   {GST_VIDEO_FORMAT_YUV9, GST_VIDEO_FORMAT_GRAY8, FALSE, FALSE, FALSE, TRUE,
6482       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6483   {GST_VIDEO_FORMAT_YUV9, GST_VIDEO_FORMAT_A420, FALSE, FALSE, FALSE, TRUE,
6484       TRUE, FALSE, TRUE, FALSE, 0, 0, convert_scale_planes},
6485   {GST_VIDEO_FORMAT_YUV9, GST_VIDEO_FORMAT_YUV9, TRUE, FALSE, FALSE, TRUE,
6486       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6487   {GST_VIDEO_FORMAT_YUV9, GST_VIDEO_FORMAT_YVU9, TRUE, FALSE, FALSE, TRUE,
6488       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6489 
6490   {GST_VIDEO_FORMAT_YVU9, GST_VIDEO_FORMAT_I420, FALSE, FALSE, FALSE, TRUE,
6491       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6492   {GST_VIDEO_FORMAT_YVU9, GST_VIDEO_FORMAT_YV12, FALSE, FALSE, FALSE, TRUE,
6493       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6494   {GST_VIDEO_FORMAT_YVU9, GST_VIDEO_FORMAT_Y41B, FALSE, FALSE, FALSE, TRUE,
6495       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6496   {GST_VIDEO_FORMAT_YVU9, GST_VIDEO_FORMAT_Y42B, FALSE, FALSE, FALSE, TRUE,
6497       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6498   {GST_VIDEO_FORMAT_YVU9, GST_VIDEO_FORMAT_Y444, FALSE, FALSE, FALSE, TRUE,
6499       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6500   {GST_VIDEO_FORMAT_YVU9, GST_VIDEO_FORMAT_GRAY8, FALSE, FALSE, FALSE, TRUE,
6501       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6502   {GST_VIDEO_FORMAT_YVU9, GST_VIDEO_FORMAT_A420, FALSE, FALSE, FALSE, TRUE,
6503       TRUE, FALSE, TRUE, FALSE, 0, 0, convert_scale_planes},
6504   {GST_VIDEO_FORMAT_YVU9, GST_VIDEO_FORMAT_YUV9, TRUE, FALSE, FALSE, TRUE,
6505       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6506   {GST_VIDEO_FORMAT_YVU9, GST_VIDEO_FORMAT_YVU9, TRUE, FALSE, FALSE, TRUE,
6507       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6508 
6509   /* sempiplanar -> semiplanar */
6510   {GST_VIDEO_FORMAT_NV12, GST_VIDEO_FORMAT_NV12, TRUE, FALSE, FALSE, TRUE,
6511       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6512   {GST_VIDEO_FORMAT_NV12, GST_VIDEO_FORMAT_NV16, TRUE, FALSE, FALSE, TRUE,
6513       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6514   {GST_VIDEO_FORMAT_NV12, GST_VIDEO_FORMAT_NV24, TRUE, FALSE, FALSE, TRUE,
6515       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6516 
6517   {GST_VIDEO_FORMAT_NV21, GST_VIDEO_FORMAT_NV21, TRUE, FALSE, FALSE, TRUE,
6518       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6519 
6520   {GST_VIDEO_FORMAT_NV16, GST_VIDEO_FORMAT_NV12, TRUE, FALSE, FALSE, TRUE,
6521       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6522   {GST_VIDEO_FORMAT_NV16, GST_VIDEO_FORMAT_NV16, TRUE, FALSE, FALSE, TRUE,
6523       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6524   {GST_VIDEO_FORMAT_NV16, GST_VIDEO_FORMAT_NV24, TRUE, FALSE, FALSE, TRUE,
6525       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6526 
6527   {GST_VIDEO_FORMAT_NV61, GST_VIDEO_FORMAT_NV61, TRUE, FALSE, FALSE, TRUE,
6528       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6529 
6530   {GST_VIDEO_FORMAT_NV24, GST_VIDEO_FORMAT_NV12, TRUE, FALSE, FALSE, TRUE,
6531       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6532   {GST_VIDEO_FORMAT_NV24, GST_VIDEO_FORMAT_NV16, TRUE, FALSE, FALSE, TRUE,
6533       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6534   {GST_VIDEO_FORMAT_NV24, GST_VIDEO_FORMAT_NV24, TRUE, FALSE, FALSE, TRUE,
6535       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6536 
6537 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
6538   {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_FORMAT_ARGB, TRUE, TRUE, TRUE, TRUE, TRUE,
6539       TRUE, FALSE, FALSE, 0, 0, convert_AYUV_ARGB},
6540   {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_FORMAT_BGRA, TRUE, TRUE, TRUE, TRUE, TRUE,
6541       TRUE, FALSE, FALSE, 0, 0, convert_AYUV_BGRA},
6542   {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_FORMAT_xRGB, TRUE, TRUE, TRUE, TRUE, TRUE,
6543       FALSE, FALSE, FALSE, 0, 0, convert_AYUV_ARGB},    /* alias */
6544   {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_FORMAT_BGRx, TRUE, TRUE, TRUE, TRUE, TRUE,
6545       FALSE, FALSE, FALSE, 0, 0, convert_AYUV_BGRA},    /* alias */
6546   {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_FORMAT_ABGR, TRUE, TRUE, TRUE, TRUE, TRUE,
6547       TRUE, FALSE, FALSE, 0, 0, convert_AYUV_ABGR},
6548   {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_FORMAT_RGBA, TRUE, TRUE, TRUE, TRUE, TRUE,
6549       TRUE, FALSE, FALSE, 0, 0, convert_AYUV_RGBA},
6550   {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_FORMAT_xBGR, TRUE, TRUE, TRUE, TRUE, TRUE,
6551       FALSE, FALSE, FALSE, 0, 0, convert_AYUV_ABGR},    /* alias */
6552   {GST_VIDEO_FORMAT_AYUV, GST_VIDEO_FORMAT_RGBx, TRUE, TRUE, TRUE, TRUE, TRUE,
6553       FALSE, FALSE, FALSE, 0, 0, convert_AYUV_RGBA},    /* alias */
6554 #endif
6555 
6556   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_BGRA, FALSE, TRUE, TRUE, TRUE,
6557       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_BGRA},
6558   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_BGRx, FALSE, TRUE, TRUE, TRUE,
6559       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_BGRA},
6560   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_BGRA, FALSE, TRUE, TRUE, TRUE,
6561       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_BGRA},
6562   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_BGRx, FALSE, TRUE, TRUE, TRUE,
6563       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_BGRA},
6564 
6565   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_ARGB, FALSE, TRUE, TRUE, TRUE,
6566       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_ARGB},
6567   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_xRGB, FALSE, TRUE, TRUE, TRUE,
6568       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_ARGB},
6569   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_ARGB, FALSE, TRUE, TRUE, TRUE,
6570       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_ARGB},
6571   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_xRGB, FALSE, TRUE, TRUE, TRUE,
6572       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_ARGB},
6573 
6574   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_ABGR, FALSE, TRUE, TRUE, TRUE,
6575       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
6576   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_xBGR, FALSE, TRUE, TRUE, TRUE,
6577       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
6578   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_RGBA, FALSE, TRUE, TRUE, TRUE,
6579       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
6580   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_RGBx, FALSE, TRUE, TRUE, TRUE,
6581       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
6582   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_RGB, FALSE, TRUE, TRUE, TRUE,
6583       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
6584   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_BGR, FALSE, TRUE, TRUE, TRUE,
6585       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
6586   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_RGB15, FALSE, TRUE, TRUE, TRUE,
6587       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
6588   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_BGR15, FALSE, TRUE, TRUE, TRUE,
6589       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
6590   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_RGB16, FALSE, TRUE, TRUE, TRUE,
6591       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
6592   {GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_BGR16, FALSE, TRUE, TRUE, TRUE,
6593       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
6594 
6595   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_ABGR, FALSE, TRUE, TRUE, TRUE,
6596       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
6597   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_xBGR, FALSE, TRUE, TRUE, TRUE,
6598       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
6599   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_RGBA, FALSE, TRUE, TRUE, TRUE,
6600       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
6601   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_RGBx, FALSE, TRUE, TRUE, TRUE,
6602       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
6603   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_RGB, FALSE, TRUE, TRUE, TRUE,
6604       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
6605   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_BGR, FALSE, TRUE, TRUE, TRUE,
6606       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
6607   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_RGB15, FALSE, TRUE, TRUE, TRUE,
6608       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
6609   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_BGR15, FALSE, TRUE, TRUE, TRUE,
6610       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
6611   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_RGB16, FALSE, TRUE, TRUE, TRUE,
6612       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
6613   {GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_BGR16, FALSE, TRUE, TRUE, TRUE,
6614       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_I420_pack_ARGB},
6615 
6616   /* scalers */
6617   {GST_VIDEO_FORMAT_GBR, GST_VIDEO_FORMAT_GBR, TRUE, FALSE, FALSE, TRUE,
6618       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6619 
6620   {GST_VIDEO_FORMAT_YVYU, GST_VIDEO_FORMAT_YVYU, TRUE, FALSE, FALSE, TRUE,
6621       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6622 
6623   {GST_VIDEO_FORMAT_RGB15, GST_VIDEO_FORMAT_RGB15, TRUE, FALSE, FALSE, TRUE,
6624       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6625   {GST_VIDEO_FORMAT_RGB16, GST_VIDEO_FORMAT_RGB16, TRUE, FALSE, FALSE, TRUE,
6626       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6627   {GST_VIDEO_FORMAT_BGR15, GST_VIDEO_FORMAT_BGR15, TRUE, FALSE, FALSE, TRUE,
6628       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6629   {GST_VIDEO_FORMAT_BGR16, GST_VIDEO_FORMAT_BGR16, TRUE, FALSE, FALSE, TRUE,
6630       TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6631 
6632   {GST_VIDEO_FORMAT_RGB, GST_VIDEO_FORMAT_RGB, TRUE, FALSE, FALSE, TRUE, TRUE,
6633       FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6634   {GST_VIDEO_FORMAT_BGR, GST_VIDEO_FORMAT_BGR, TRUE, FALSE, FALSE, TRUE, TRUE,
6635       FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6636   {GST_VIDEO_FORMAT_v308, GST_VIDEO_FORMAT_v308, TRUE, FALSE, FALSE, TRUE, TRUE,
6637       FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6638   {GST_VIDEO_FORMAT_IYU2, GST_VIDEO_FORMAT_IYU2, TRUE, FALSE, FALSE, TRUE, TRUE,
6639       FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6640 
6641   {GST_VIDEO_FORMAT_ARGB, GST_VIDEO_FORMAT_ARGB, TRUE, FALSE, FALSE, TRUE, TRUE,
6642       TRUE, FALSE, FALSE, 0, 0, convert_scale_planes},
6643   {GST_VIDEO_FORMAT_xRGB, GST_VIDEO_FORMAT_xRGB, TRUE, FALSE, FALSE, TRUE, TRUE,
6644       FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6645   {GST_VIDEO_FORMAT_ABGR, GST_VIDEO_FORMAT_ABGR, TRUE, FALSE, FALSE, TRUE, TRUE,
6646       TRUE, FALSE, FALSE, 0, 0, convert_scale_planes},
6647   {GST_VIDEO_FORMAT_xBGR, GST_VIDEO_FORMAT_xBGR, TRUE, FALSE, FALSE, TRUE, TRUE,
6648       FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6649   {GST_VIDEO_FORMAT_RGBA, GST_VIDEO_FORMAT_RGBA, TRUE, FALSE, FALSE, TRUE, TRUE,
6650       TRUE, FALSE, FALSE, 0, 0, convert_scale_planes},
6651   {GST_VIDEO_FORMAT_RGBx, GST_VIDEO_FORMAT_RGBx, TRUE, FALSE, FALSE, TRUE, TRUE,
6652       FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6653   {GST_VIDEO_FORMAT_BGRA, GST_VIDEO_FORMAT_BGRA, TRUE, FALSE, FALSE, TRUE, TRUE,
6654       TRUE, FALSE, FALSE, 0, 0, convert_scale_planes},
6655   {GST_VIDEO_FORMAT_BGRx, GST_VIDEO_FORMAT_BGRx, TRUE, FALSE, FALSE, TRUE, TRUE,
6656       FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6657 
6658   {GST_VIDEO_FORMAT_ARGB64, GST_VIDEO_FORMAT_ARGB64, TRUE, FALSE, FALSE, TRUE,
6659       TRUE, TRUE, FALSE, FALSE, 0, 0, convert_scale_planes},
6660   {GST_VIDEO_FORMAT_AYUV64, GST_VIDEO_FORMAT_AYUV64, TRUE, FALSE, FALSE, TRUE,
6661       TRUE, TRUE, FALSE, FALSE, 0, 0, convert_scale_planes},
6662 
6663   {GST_VIDEO_FORMAT_GRAY16_LE, GST_VIDEO_FORMAT_GRAY16_LE, TRUE, FALSE, FALSE,
6664       TRUE, TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6665   {GST_VIDEO_FORMAT_GRAY16_BE, GST_VIDEO_FORMAT_GRAY16_BE, TRUE, FALSE, FALSE,
6666       TRUE, TRUE, FALSE, FALSE, FALSE, 0, 0, convert_scale_planes},
6667 };
6668 
6669 static gboolean
video_converter_lookup_fastpath(GstVideoConverter * convert)6670 video_converter_lookup_fastpath (GstVideoConverter * convert)
6671 {
6672   int i;
6673   GstVideoFormat in_format, out_format;
6674   GstVideoTransferFunction in_transf, out_transf;
6675   gboolean interlaced, same_matrix, same_primaries, same_size, crop, border;
6676   gboolean need_copy, need_set, need_mult;
6677   gint width, height;
6678 
6679   width = GST_VIDEO_INFO_WIDTH (&convert->in_info);
6680   height = GST_VIDEO_INFO_HEIGHT (&convert->in_info);
6681 
6682   if (GET_OPT_DITHER_QUANTIZATION (convert) != 1)
6683     return FALSE;
6684 
6685   /* we don't do gamma conversion in fastpath */
6686   in_transf = convert->in_info.colorimetry.transfer;
6687   out_transf = convert->out_info.colorimetry.transfer;
6688 
6689   same_size = (width == convert->out_width && height == convert->out_height);
6690 
6691   /* fastpaths don't do gamma */
6692   if (CHECK_GAMMA_REMAP (convert) && (!same_size || in_transf != out_transf))
6693     return FALSE;
6694 
6695   need_copy = (convert->alpha_mode & ALPHA_MODE_COPY) == ALPHA_MODE_COPY;
6696   need_set = (convert->alpha_mode & ALPHA_MODE_SET) == ALPHA_MODE_SET;
6697   need_mult = (convert->alpha_mode & ALPHA_MODE_MULT) == ALPHA_MODE_MULT;
6698   GST_DEBUG ("alpha copy %d, set %d, mult %d", need_copy, need_set, need_mult);
6699 
6700   in_format = GST_VIDEO_INFO_FORMAT (&convert->in_info);
6701   out_format = GST_VIDEO_INFO_FORMAT (&convert->out_info);
6702 
6703   if (CHECK_MATRIX_NONE (convert)) {
6704     same_matrix = TRUE;
6705   } else {
6706     GstVideoColorMatrix in_matrix, out_matrix;
6707 
6708     in_matrix = convert->in_info.colorimetry.matrix;
6709     out_matrix = convert->out_info.colorimetry.matrix;
6710     same_matrix = in_matrix == out_matrix;
6711   }
6712 
6713   if (CHECK_PRIMARIES_NONE (convert)) {
6714     same_primaries = TRUE;
6715   } else {
6716     GstVideoColorPrimaries in_primaries, out_primaries;
6717 
6718     in_primaries = convert->in_info.colorimetry.primaries;
6719     out_primaries = convert->out_info.colorimetry.primaries;
6720     same_primaries = in_primaries == out_primaries;
6721   }
6722 
6723   interlaced = GST_VIDEO_INFO_IS_INTERLACED (&convert->in_info);
6724   interlaced |= GST_VIDEO_INFO_IS_INTERLACED (&convert->out_info);
6725 
6726   crop = convert->in_x || convert->in_y
6727       || convert->in_width < convert->in_maxwidth
6728       || convert->in_height < convert->in_maxheight;
6729   border = convert->out_x || convert->out_y
6730       || convert->out_width < convert->out_maxwidth
6731       || convert->out_height < convert->out_maxheight;
6732 
6733   for (i = 0; i < G_N_ELEMENTS (transforms); i++) {
6734     if (transforms[i].in_format == in_format &&
6735         transforms[i].out_format == out_format &&
6736         (transforms[i].keeps_interlaced || !interlaced) &&
6737         (transforms[i].needs_color_matrix || (same_matrix && same_primaries))
6738         && (!transforms[i].keeps_size || same_size)
6739         && (transforms[i].width_align & width) == 0
6740         && (transforms[i].height_align & height) == 0
6741         && (transforms[i].do_crop || !crop)
6742         && (transforms[i].do_border || !border)
6743         && (transforms[i].alpha_copy || !need_copy)
6744         && (transforms[i].alpha_set || !need_set)
6745         && (transforms[i].alpha_mult || !need_mult)) {
6746       guint j;
6747 
6748       GST_DEBUG ("using fastpath");
6749       if (transforms[i].needs_color_matrix)
6750         video_converter_compute_matrix (convert);
6751       convert->convert = transforms[i].convert;
6752 
6753       convert->tmpline =
6754           g_new (guint16 *, convert->conversion_runner->n_threads);
6755       for (j = 0; j < convert->conversion_runner->n_threads; j++)
6756         convert->tmpline[j] = g_malloc0 (sizeof (guint16) * (width + 8) * 4);
6757 
6758       if (!transforms[i].keeps_size)
6759         if (!setup_scale (convert))
6760           return FALSE;
6761       if (border)
6762         setup_borderline (convert);
6763       return TRUE;
6764     }
6765   }
6766   GST_DEBUG ("no fastpath found");
6767   return FALSE;
6768 }
6769