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