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