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