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