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