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