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