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