gstvideoflip.c 26.8 KB
Newer Older
1
2
3
/* GStreamer
 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
 * Copyright (C) <2003> David Schleef <ds@schleef.org>
4
 * Copyright (C) <2010> Sebastian Dröge <sebastian.droege@collabora.co.uk>
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

22
23
/*
 * This file was (probably) generated from gstvideoflip.c,
24
 * gstvideoflip.c,v 1.7 2003/11/08 02:48:59 dschleef Exp 
25
 */
26
27
28
29
/**
 * SECTION:element-videoflip
 *
 * Flips and rotates video.
30
31
 *
 * <refsect2>
32
 * <title>Example launch line</title>
33
 * |[
34
 * gst-launch videotestsrc ! videoflip method=clockwise ! ffmpegcolorspace ! ximagesink
35
 * ]| This pipeline flips the test image 90 degrees clockwise.
36
37
 * </refsect2>
 *
38
 * Last reviewed on 2010-04-18 (0.10.22)
39
40
 */

41

42
43
44
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
45

46
#include "gstvideoflip.h"
47

48
#include <string.h>
49
#include <gst/gst.h>
50
#include <gst/controller/gstcontroller.h>
Julien Moutte's avatar
Julien Moutte committed
51
#include <gst/video/video.h>
52

53
/* GstVideoFlip properties */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
54
55
enum
{
56
57
  PROP_0,
  PROP_METHOD
58
      /* FILL ME */
59
60
};

61
#define PROP_METHOD_DEFAULT GST_VIDEO_FLIP_METHOD_IDENTITY
62

63
GST_DEBUG_CATEGORY_STATIC (video_flip_debug);
Thomas Vander Stichele's avatar
borgify    
Thomas Vander Stichele committed
64
#define GST_CAT_DEFAULT video_flip_debug
65

Thomas Vander Stichele's avatar
borgify    
Thomas Vander Stichele committed
66
static GstStaticPadTemplate gst_video_flip_src_template =
67
    GST_STATIC_PAD_TEMPLATE ("src",
Julien Moutte's avatar
Julien Moutte committed
68
69
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
70
    GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV") ";"
71
72
        GST_VIDEO_CAPS_ARGB ";" GST_VIDEO_CAPS_BGRA ";"
        GST_VIDEO_CAPS_ABGR ";" GST_VIDEO_CAPS_RGBA ";"
73
        GST_VIDEO_CAPS_YUV ("Y444") ";"
74
75
        GST_VIDEO_CAPS_xRGB ";" GST_VIDEO_CAPS_RGBx ";"
        GST_VIDEO_CAPS_xBGR ";" GST_VIDEO_CAPS_BGRx ";"
76
77
78
79
        GST_VIDEO_CAPS_RGB ";" GST_VIDEO_CAPS_BGR ";"
        GST_VIDEO_CAPS_YUV ("I420") ";"
        GST_VIDEO_CAPS_YUV ("YV12") ";" GST_VIDEO_CAPS_YUV ("IYUV")
    )
Julien Moutte's avatar
Julien Moutte committed
80
    );
81

Thomas Vander Stichele's avatar
borgify    
Thomas Vander Stichele committed
82
static GstStaticPadTemplate gst_video_flip_sink_template =
83
    GST_STATIC_PAD_TEMPLATE ("sink",
Julien Moutte's avatar
Julien Moutte committed
84
85
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
86
    GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV") ";"
87
88
        GST_VIDEO_CAPS_ARGB ";" GST_VIDEO_CAPS_BGRA ";"
        GST_VIDEO_CAPS_ABGR ";" GST_VIDEO_CAPS_RGBA ";"
89
        GST_VIDEO_CAPS_YUV ("Y444") ";"
90
91
        GST_VIDEO_CAPS_xRGB ";" GST_VIDEO_CAPS_RGBx ";"
        GST_VIDEO_CAPS_xBGR ";" GST_VIDEO_CAPS_BGRx ";"
92
93
94
95
        GST_VIDEO_CAPS_RGB ";" GST_VIDEO_CAPS_BGR ";"
        GST_VIDEO_CAPS_YUV ("I420") ";"
        GST_VIDEO_CAPS_YUV ("YV12") ";" GST_VIDEO_CAPS_YUV ("IYUV")
    )
Julien Moutte's avatar
Julien Moutte committed
96
    );
97

Thomas Vander Stichele's avatar
borgify    
Thomas Vander Stichele committed
98
#define GST_TYPE_VIDEO_FLIP_METHOD (gst_video_flip_method_get_type())
99

100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
static const GEnumValue video_flip_methods[] = {
  {GST_VIDEO_FLIP_METHOD_IDENTITY, "Identity (no rotation)", "none"},
  {GST_VIDEO_FLIP_METHOD_90R, "Rotate clockwise 90 degrees", "clockwise"},
  {GST_VIDEO_FLIP_METHOD_180, "Rotate 180 degrees", "rotate-180"},
  {GST_VIDEO_FLIP_METHOD_90L, "Rotate counter-clockwise 90 degrees",
      "counterclockwise"},
  {GST_VIDEO_FLIP_METHOD_HORIZ, "Flip horizontally", "horizontal-flip"},
  {GST_VIDEO_FLIP_METHOD_VERT, "Flip vertically", "vertical-flip"},
  {GST_VIDEO_FLIP_METHOD_TRANS,
      "Flip across upper left/lower right diagonal", "upper-left-diagonal"},
  {GST_VIDEO_FLIP_METHOD_OTHER,
      "Flip across upper right/lower left diagonal", "upper-right-diagonal"},
  {0, NULL, NULL},
};

115
static GType
Thomas Vander Stichele's avatar
borgify    
Thomas Vander Stichele committed
116
gst_video_flip_method_get_type (void)
117
{
Thomas Vander Stichele's avatar
borgify    
Thomas Vander Stichele committed
118
  static GType video_flip_method_type = 0;
119

Thomas Vander Stichele's avatar
borgify    
Thomas Vander Stichele committed
120
121
122
  if (!video_flip_method_type) {
    video_flip_method_type = g_enum_register_static ("GstVideoFlipMethod",
        video_flip_methods);
123
  }
Thomas Vander Stichele's avatar
borgify    
Thomas Vander Stichele committed
124
  return video_flip_method_type;
125
126
}

127
128
129
GST_BOILERPLATE (GstVideoFlip, gst_video_flip, GstVideoFilter,
    GST_TYPE_VIDEO_FILTER);

Julien Moutte's avatar
Julien Moutte committed
130
static GstCaps *
Thomas Vander Stichele's avatar
borgify    
Thomas Vander Stichele committed
131
gst_video_flip_transform_caps (GstBaseTransform * trans,
Julien Moutte's avatar
Julien Moutte committed
132
133
    GstPadDirection direction, GstCaps * caps)
{
134
  GstVideoFlip *videoflip = GST_VIDEO_FLIP (trans);
Julien Moutte's avatar
Julien Moutte committed
135
136
137
138
139
140
141
  GstCaps *ret;
  gint width, height, i;

  ret = gst_caps_copy (caps);

  for (i = 0; i < gst_caps_get_size (ret); i++) {
    GstStructure *structure = gst_caps_get_structure (ret, i);
142
    gint par_n, par_d;
Julien Moutte's avatar
Julien Moutte committed
143
144
145
146
147

    if (gst_structure_get_int (structure, "width", &width) &&
        gst_structure_get_int (structure, "height", &height)) {

      switch (videoflip->method) {
Thomas Vander Stichele's avatar
borgify    
Thomas Vander Stichele committed
148
149
150
151
        case GST_VIDEO_FLIP_METHOD_90R:
        case GST_VIDEO_FLIP_METHOD_90L:
        case GST_VIDEO_FLIP_METHOD_TRANS:
        case GST_VIDEO_FLIP_METHOD_OTHER:
Julien Moutte's avatar
Julien Moutte committed
152
153
          gst_structure_set (structure, "width", G_TYPE_INT, height,
              "height", G_TYPE_INT, width, NULL);
154
155
156
157
158
159
160
161
162
163
164
          if (gst_structure_get_fraction (structure, "pixel-aspect-ratio",
                  &par_n, &par_d)) {
            if (par_n != 1 || par_d != 1) {
              GValue val = { 0, };

              g_value_init (&val, GST_TYPE_FRACTION);
              gst_value_set_fraction (&val, par_d, par_n);
              gst_structure_set_value (structure, "pixel-aspect-ratio", &val);
              g_value_unset (&val);
            }
          }
Julien Moutte's avatar
Julien Moutte committed
165
          break;
Thomas Vander Stichele's avatar
borgify    
Thomas Vander Stichele committed
166
167
168
169
        case GST_VIDEO_FLIP_METHOD_IDENTITY:
        case GST_VIDEO_FLIP_METHOD_180:
        case GST_VIDEO_FLIP_METHOD_HORIZ:
        case GST_VIDEO_FLIP_METHOD_VERT:
Julien Moutte's avatar
Julien Moutte committed
170
171
172
173
174
175
176
177
          gst_structure_set (structure, "width", G_TYPE_INT, width,
              "height", G_TYPE_INT, height, NULL);
          break;
        default:
          g_assert_not_reached ();
          break;
      }
    }
178
  }
Julien Moutte's avatar
Julien Moutte committed
179
180
181
182
183

  GST_DEBUG_OBJECT (videoflip, "transformed %" GST_PTR_FORMAT " to %"
      GST_PTR_FORMAT, caps, ret);

  return ret;
184
185
}

Julien Moutte's avatar
Julien Moutte committed
186
static gboolean
Thomas Vander Stichele's avatar
borgify    
Thomas Vander Stichele committed
187
gst_video_flip_get_unit_size (GstBaseTransform * btrans, GstCaps * caps,
Julien Moutte's avatar
Julien Moutte committed
188
    guint * size)
Ronald S. Bultje's avatar
Bla    
Ronald S. Bultje committed
189
{
190
  GstVideoFormat format;
Julien Moutte's avatar
Julien Moutte committed
191
  gint width, height;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
192

193
194
  if (!gst_video_format_parse_caps (caps, &format, &width, &height))
    return FALSE;
195

196
197
  *size = gst_video_format_get_size (format, width, height);

198
  GST_DEBUG_OBJECT (btrans, "our frame size is %d bytes (%dx%d)", *size,
199
200
201
      width, height);

  return TRUE;
Ronald S. Bultje's avatar
Bla    
Ronald S. Bultje committed
202
203
}

204
205
206
static void
gst_video_flip_planar_yuv (GstVideoFlip * videoflip, guint8 * dest,
    const guint8 * src)
207
{
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
  gint x, y;
  guint8 const *s;
  guint8 *d;
  GstVideoFormat format = videoflip->format;
  gint sw = videoflip->from_width;
  gint sh = videoflip->from_height;
  gint dw = videoflip->to_width;
  gint dh = videoflip->to_height;
  gint src_y_stride, src_u_stride, src_v_stride;
  gint src_y_offset, src_u_offset, src_v_offset;
  gint src_y_height, src_u_height, src_v_height;
  gint src_y_width, src_u_width, src_v_width;
  gint dest_y_stride, dest_u_stride, dest_v_stride;
  gint dest_y_offset, dest_u_offset, dest_v_offset;
  gint dest_y_height, dest_u_height, dest_v_height;
  gint dest_y_width, dest_u_width, dest_v_width;

  src_y_stride = gst_video_format_get_row_stride (format, 0, sw);
  src_u_stride = gst_video_format_get_row_stride (format, 1, sw);
  src_v_stride = gst_video_format_get_row_stride (format, 2, sw);

  dest_y_stride = gst_video_format_get_row_stride (format, 0, dw);
  dest_u_stride = gst_video_format_get_row_stride (format, 1, dw);
  dest_v_stride = gst_video_format_get_row_stride (format, 2, dw);

  src_y_offset = gst_video_format_get_component_offset (format, 0, sw, sh);
  src_u_offset = gst_video_format_get_component_offset (format, 1, sw, sh);
  src_v_offset = gst_video_format_get_component_offset (format, 2, sw, sh);

  dest_y_offset = gst_video_format_get_component_offset (format, 0, dw, dh);
  dest_u_offset = gst_video_format_get_component_offset (format, 1, dw, dh);
  dest_v_offset = gst_video_format_get_component_offset (format, 2, dw, dh);

  src_y_width = gst_video_format_get_component_width (format, 0, sw);
  src_u_width = gst_video_format_get_component_width (format, 1, sw);
  src_v_width = gst_video_format_get_component_width (format, 2, sw);

  dest_y_width = gst_video_format_get_component_width (format, 0, dw);
  dest_u_width = gst_video_format_get_component_width (format, 1, dw);
  dest_v_width = gst_video_format_get_component_width (format, 2, dw);

  src_y_height = gst_video_format_get_component_height (format, 0, sh);
  src_u_height = gst_video_format_get_component_height (format, 1, sh);
  src_v_height = gst_video_format_get_component_height (format, 2, sh);

  dest_y_height = gst_video_format_get_component_height (format, 0, dh);
  dest_u_height = gst_video_format_get_component_height (format, 1, dh);
  dest_v_height = gst_video_format_get_component_height (format, 2, dh);
Julien Moutte's avatar
Julien Moutte committed
256
257

  switch (videoflip->method) {
Thomas Vander Stichele's avatar
borgify    
Thomas Vander Stichele committed
258
    case GST_VIDEO_FLIP_METHOD_90R:
259
      /* Flip Y */
260
261
262
263
264
265
      s = src + src_y_offset;
      d = dest + dest_y_offset;
      for (y = 0; y < dest_y_height; y++) {
        for (x = 0; x < dest_y_width; x++) {
          d[y * dest_y_stride + x] =
              s[(src_y_height - 1 - x) * src_y_stride + y];
266
267
268
        }
      }
      /* Flip U */
269
270
271
272
273
274
      s = src + src_u_offset;
      d = dest + dest_u_offset;
      for (y = 0; y < dest_u_height; y++) {
        for (x = 0; x < dest_u_width; x++) {
          d[y * dest_u_stride + x] =
              s[(src_u_height - 1 - x) * src_u_stride + y];
275
276
277
        }
      }
      /* Flip V */
278
279
280
281
282
283
      s = src + src_v_offset;
      d = dest + dest_v_offset;
      for (y = 0; y < dest_v_height; y++) {
        for (x = 0; x < dest_v_width; x++) {
          d[y * dest_v_stride + x] =
              s[(src_v_height - 1 - x) * src_v_stride + y];
Julien Moutte's avatar
Julien Moutte committed
284
285
286
        }
      }
      break;
Thomas Vander Stichele's avatar
borgify    
Thomas Vander Stichele committed
287
    case GST_VIDEO_FLIP_METHOD_90L:
288
      /* Flip Y */
289
290
291
292
293
294
      s = src + src_y_offset;
      d = dest + dest_y_offset;
      for (y = 0; y < dest_y_height; y++) {
        for (x = 0; x < dest_y_width; x++) {
          d[y * dest_y_stride + x] =
              s[x * src_y_stride + (src_y_width - 1 - y)];
295
296
297
        }
      }
      /* Flip U */
298
299
300
301
302
303
      s = src + src_u_offset;
      d = dest + dest_u_offset;
      for (y = 0; y < dest_u_height; y++) {
        for (x = 0; x < dest_u_width; x++) {
          d[y * dest_u_stride + x] =
              s[x * src_u_stride + (src_u_width - 1 - y)];
304
305
306
        }
      }
      /* Flip V */
307
308
309
310
311
312
      s = src + src_v_offset;
      d = dest + dest_v_offset;
      for (y = 0; y < dest_v_height; y++) {
        for (x = 0; x < dest_v_width; x++) {
          d[y * dest_v_stride + x] =
              s[x * src_v_stride + (src_v_width - 1 - y)];
Julien Moutte's avatar
Julien Moutte committed
313
314
315
        }
      }
      break;
Thomas Vander Stichele's avatar
borgify    
Thomas Vander Stichele committed
316
    case GST_VIDEO_FLIP_METHOD_180:
317
      /* Flip Y */
318
319
320
321
322
323
      s = src + src_y_offset;
      d = dest + dest_y_offset;
      for (y = 0; y < dest_y_height; y++) {
        for (x = 0; x < dest_y_width; x++) {
          d[y * dest_y_stride + x] =
              s[(src_y_height - 1 - y) * src_y_stride + (src_y_width - 1 - x)];
324
325
326
        }
      }
      /* Flip U */
327
328
329
330
331
332
      s = src + src_u_offset;
      d = dest + dest_u_offset;
      for (y = 0; y < dest_u_height; y++) {
        for (x = 0; x < dest_u_width; x++) {
          d[y * dest_u_stride + x] =
              s[(src_u_height - 1 - y) * src_u_stride + (src_u_width - 1 - x)];
333
334
335
        }
      }
      /* Flip V */
336
337
338
339
340
341
      s = src + src_v_offset;
      d = dest + dest_v_offset;
      for (y = 0; y < dest_v_height; y++) {
        for (x = 0; x < dest_v_width; x++) {
          d[y * dest_v_stride + x] =
              s[(src_v_height - 1 - y) * src_v_stride + (src_v_width - 1 - x)];
Julien Moutte's avatar
Julien Moutte committed
342
343
344
        }
      }
      break;
Thomas Vander Stichele's avatar
borgify    
Thomas Vander Stichele committed
345
    case GST_VIDEO_FLIP_METHOD_HORIZ:
346
      /* Flip Y */
347
348
349
350
351
352
      s = src + src_y_offset;
      d = dest + dest_y_offset;
      for (y = 0; y < dest_y_height; y++) {
        for (x = 0; x < dest_y_width; x++) {
          d[y * dest_y_stride + x] =
              s[y * src_y_stride + (src_y_width - 1 - x)];
353
354
355
        }
      }
      /* Flip U */
356
357
358
359
360
361
      s = src + src_u_offset;
      d = dest + dest_u_offset;
      for (y = 0; y < dest_u_height; y++) {
        for (x = 0; x < dest_u_width; x++) {
          d[y * dest_u_stride + x] =
              s[y * src_u_stride + (src_u_width - 1 - x)];
362
363
364
        }
      }
      /* Flip V */
365
366
367
368
369
370
      s = src + src_v_offset;
      d = dest + dest_v_offset;
      for (y = 0; y < dest_v_height; y++) {
        for (x = 0; x < dest_v_width; x++) {
          d[y * dest_v_stride + x] =
              s[y * src_v_stride + (src_v_width - 1 - x)];
Julien Moutte's avatar
Julien Moutte committed
371
372
373
        }
      }
      break;
Thomas Vander Stichele's avatar
borgify    
Thomas Vander Stichele committed
374
    case GST_VIDEO_FLIP_METHOD_VERT:
375
      /* Flip Y */
376
377
378
379
380
381
      s = src + src_y_offset;
      d = dest + dest_y_offset;
      for (y = 0; y < dest_y_height; y++) {
        for (x = 0; x < dest_y_width; x++) {
          d[y * dest_y_stride + x] =
              s[(src_y_height - 1 - y) * src_y_stride + x];
382
383
384
        }
      }
      /* Flip U */
385
386
387
388
389
390
      s = src + src_u_offset;
      d = dest + dest_u_offset;
      for (y = 0; y < dest_u_height; y++) {
        for (x = 0; x < dest_u_width; x++) {
          d[y * dest_u_stride + x] =
              s[(src_u_height - 1 - y) * src_u_stride + x];
391
392
393
        }
      }
      /* Flip V */
394
395
396
397
398
399
      s = src + src_v_offset;
      d = dest + dest_v_offset;
      for (y = 0; y < dest_v_height; y++) {
        for (x = 0; x < dest_v_width; x++) {
          d[y * dest_v_stride + x] =
              s[(src_v_height - 1 - y) * src_v_stride + x];
Julien Moutte's avatar
Julien Moutte committed
400
401
402
        }
      }
      break;
Thomas Vander Stichele's avatar
borgify    
Thomas Vander Stichele committed
403
    case GST_VIDEO_FLIP_METHOD_TRANS:
404
      /* Flip Y */
405
406
407
408
409
      s = src + src_y_offset;
      d = dest + dest_y_offset;
      for (y = 0; y < dest_y_height; y++) {
        for (x = 0; x < dest_y_width; x++) {
          d[y * dest_y_stride + x] = s[x * src_y_stride + y];
410
411
412
        }
      }
      /* Flip U */
413
414
415
416
417
      s = src + src_u_offset;
      d = dest + dest_u_offset;
      for (y = 0; y < dest_u_height; y++) {
        for (x = 0; x < dest_u_width; x++) {
          d[y * dest_u_stride + x] = s[x * src_u_stride + y];
418
419
420
        }
      }
      /* Flip V */
421
422
423
424
425
      s = src + src_v_offset;
      d = dest + dest_v_offset;
      for (y = 0; y < dest_u_height; y++) {
        for (x = 0; x < dest_u_width; x++) {
          d[y * dest_v_stride + x] = s[x * src_v_stride + y];
Julien Moutte's avatar
Julien Moutte committed
426
427
428
        }
      }
      break;
Thomas Vander Stichele's avatar
borgify    
Thomas Vander Stichele committed
429
    case GST_VIDEO_FLIP_METHOD_OTHER:
430
      /* Flip Y */
431
432
433
434
435
436
      s = src + src_y_offset;
      d = dest + dest_y_offset;
      for (y = 0; y < dest_y_height; y++) {
        for (x = 0; x < dest_y_width; x++) {
          d[y * dest_y_stride + x] =
              s[(src_y_height - 1 - x) * src_y_stride + (src_y_width - 1 - y)];
437
438
439
        }
      }
      /* Flip U */
440
441
442
443
444
445
      s = src + src_u_offset;
      d = dest + dest_u_offset;
      for (y = 0; y < dest_u_height; y++) {
        for (x = 0; x < dest_u_width; x++) {
          d[y * dest_u_stride + x] =
              s[(src_u_height - 1 - x) * src_u_stride + (src_u_width - 1 - y)];
Julien Moutte's avatar
Julien Moutte committed
446
447
        }
      }
448
      /* Flip V */
449
450
451
452
453
454
      s = src + src_v_offset;
      d = dest + dest_v_offset;
      for (y = 0; y < dest_v_height; y++) {
        for (x = 0; x < dest_v_width; x++) {
          d[y * dest_v_stride + x] =
              s[(src_v_height - 1 - x) * src_v_stride + (src_v_width - 1 - y)];
455
456
457
        }
      }
      break;
Thomas Vander Stichele's avatar
borgify    
Thomas Vander Stichele committed
458
    case GST_VIDEO_FLIP_METHOD_IDENTITY:
459
      g_assert_not_reached ();
Julien Moutte's avatar
Julien Moutte committed
460
461
      break;
    default:
462
      g_assert_not_reached ();
Julien Moutte's avatar
Julien Moutte committed
463
464
      break;
  }
465
}
466

467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
static void
gst_video_flip_packed_simple (GstVideoFlip * videoflip, guint8 * dest,
    const guint8 * src)
{
  gint x, y, z;
  guint8 const *s = src;
  guint8 *d = dest;
  GstVideoFormat format = videoflip->format;
  gint sw = videoflip->from_width;
  gint sh = videoflip->from_height;
  gint dw = videoflip->to_width;
  gint dh = videoflip->to_height;
  gint src_stride, dest_stride;
  gint bpp;

  src_stride = gst_video_format_get_row_stride (format, 0, sw);
  dest_stride = gst_video_format_get_row_stride (format, 0, dw);
  /* This is only true for non-subsampled formats! */
  bpp = gst_video_format_get_pixel_stride (format, 0);

  switch (videoflip->method) {
    case GST_VIDEO_FLIP_METHOD_90R:
      for (y = 0; y < dh; y++) {
        for (x = 0; x < dw; x++) {
          for (z = 0; z < bpp; z++) {
            d[y * dest_stride + x * bpp + z] =
                s[(sh - 1 - x) * src_stride + y * bpp + z];
          }
        }
      }
      break;
    case GST_VIDEO_FLIP_METHOD_90L:
      for (y = 0; y < dh; y++) {
        for (x = 0; x < dw; x++) {
          for (z = 0; z < bpp; z++) {
            d[y * dest_stride + x * bpp + z] =
                s[x * src_stride + (sw - 1 - y) * bpp + z];
          }
        }
      }
      break;
    case GST_VIDEO_FLIP_METHOD_180:
      for (y = 0; y < dh; y++) {
        for (x = 0; x < dw; x++) {
          for (z = 0; z < bpp; z++) {
            d[y * dest_stride + x * bpp + z] =
                s[(sh - 1 - y) * src_stride + (sw - 1 - x) * bpp + z];
          }
        }
      }
      break;
    case GST_VIDEO_FLIP_METHOD_HORIZ:
      for (y = 0; y < dh; y++) {
        for (x = 0; x < dw; x++) {
          for (z = 0; z < bpp; z++) {
            d[y * dest_stride + x * bpp + z] =
                s[y * src_stride + (sw - 1 - x) * bpp + z];
          }
        }
      }
      break;
    case GST_VIDEO_FLIP_METHOD_VERT:
      for (y = 0; y < dh; y++) {
        for (x = 0; x < dw; x++) {
          for (z = 0; z < bpp; z++) {
            d[y * dest_stride + x * bpp + z] =
                s[(sh - 1 - y) * src_stride + x * bpp + z];
          }
        }
      }
      break;
    case GST_VIDEO_FLIP_METHOD_TRANS:
      for (y = 0; y < dh; y++) {
        for (x = 0; x < dw; x++) {
          for (z = 0; z < bpp; z++) {
            d[y * dest_stride + x * bpp + z] = s[x * src_stride + y * bpp + z];
          }
        }
      }
      break;
    case GST_VIDEO_FLIP_METHOD_OTHER:
      for (y = 0; y < dh; y++) {
        for (x = 0; x < dw; x++) {
          for (z = 0; z < bpp; z++) {
            d[y * dest_stride + x * bpp + z] =
                s[(sh - 1 - x) * src_stride + (sw - 1 - y) * bpp + z];
          }
        }
      }
      break;
    case GST_VIDEO_FLIP_METHOD_IDENTITY:
      g_assert_not_reached ();
      break;
    default:
      g_assert_not_reached ();
      break;
  }
}

566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
static gboolean
gst_video_flip_set_caps (GstBaseTransform * btrans, GstCaps * incaps,
    GstCaps * outcaps)
{
  GstVideoFlip *vf = GST_VIDEO_FLIP (btrans);
  GstVideoFormat in_format, out_format;
  gboolean ret = FALSE;

  vf->process = NULL;

  if (!gst_video_format_parse_caps (incaps, &in_format, &vf->from_width,
          &vf->from_height)
      || !gst_video_format_parse_caps (outcaps, &out_format, &vf->to_width,
          &vf->to_height))
    goto invalid_caps;

  if (in_format != out_format)
    goto invalid_caps;
  vf->format = in_format;

  /* Check that they are correct */
  switch (vf->method) {
    case GST_VIDEO_FLIP_METHOD_90R:
    case GST_VIDEO_FLIP_METHOD_90L:
    case GST_VIDEO_FLIP_METHOD_TRANS:
    case GST_VIDEO_FLIP_METHOD_OTHER:
      if ((vf->from_width != vf->to_height) ||
          (vf->from_height != vf->to_width)) {
        GST_ERROR_OBJECT (vf, "we are inverting width and height but caps "
            "are not correct : %dx%d to %dx%d", vf->from_width,
            vf->from_height, vf->to_width, vf->to_height);
        goto beach;
      }
      break;
    case GST_VIDEO_FLIP_METHOD_IDENTITY:

      break;
    case GST_VIDEO_FLIP_METHOD_180:
    case GST_VIDEO_FLIP_METHOD_HORIZ:
    case GST_VIDEO_FLIP_METHOD_VERT:
      if ((vf->from_width != vf->to_width) ||
          (vf->from_height != vf->to_height)) {
        GST_ERROR_OBJECT (vf, "we are keeping width and height but caps "
            "are not correct : %dx%d to %dx%d", vf->from_width,
            vf->from_height, vf->to_width, vf->to_height);
        goto beach;
      }
      break;
    default:
      g_assert_not_reached ();
      break;
  }

  ret = TRUE;

  switch (vf->format) {
    case GST_VIDEO_FORMAT_I420:
    case GST_VIDEO_FORMAT_YV12:
624
    case GST_VIDEO_FORMAT_Y444:
625
626
      vf->process = gst_video_flip_planar_yuv;
      break;
627
628
629
630
631
632
633
634
635
636
637
638
639
    case GST_VIDEO_FORMAT_AYUV:
    case GST_VIDEO_FORMAT_ARGB:
    case GST_VIDEO_FORMAT_ABGR:
    case GST_VIDEO_FORMAT_RGBA:
    case GST_VIDEO_FORMAT_BGRA:
    case GST_VIDEO_FORMAT_xRGB:
    case GST_VIDEO_FORMAT_xBGR:
    case GST_VIDEO_FORMAT_RGBx:
    case GST_VIDEO_FORMAT_BGRx:
    case GST_VIDEO_FORMAT_RGB:
    case GST_VIDEO_FORMAT_BGR:
      vf->process = gst_video_flip_packed_simple;
      break;
640
641
642
643
644
645
646
647
648
649
650
    default:
      break;
  }

beach:
  return ret && (vf->process != NULL);

invalid_caps:
  GST_ERROR_OBJECT (vf, "Invalid caps: %" GST_PTR_FORMAT " -> %" GST_PTR_FORMAT,
      incaps, outcaps);
  return FALSE;
Julien Moutte's avatar
Julien Moutte committed
651
}
652

653
654
static void
gst_video_flip_before_transform (GstBaseTransform * trans, GstBuffer * in)
Julien Moutte's avatar
Julien Moutte committed
655
{
656
  GstVideoFlip *videoflip = GST_VIDEO_FLIP (trans);
657
658
  GstClockTime timestamp, stream_time;

659
  timestamp = GST_BUFFER_TIMESTAMP (in);
660
661
662
663
664
665
666
667
  stream_time =
      gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME, timestamp);

  GST_DEBUG_OBJECT (videoflip, "sync to %" GST_TIME_FORMAT,
      GST_TIME_ARGS (timestamp));

  if (GST_CLOCK_TIME_IS_VALID (stream_time))
    gst_object_sync_values (G_OBJECT (videoflip), stream_time);
668
669
670
671
672
673
674
675
676
677
678
679
}

static GstFlowReturn
gst_video_flip_transform (GstBaseTransform * trans, GstBuffer * in,
    GstBuffer * out)
{
  GstVideoFlip *videoflip = GST_VIDEO_FLIP (trans);
  guint8 *dest;
  const guint8 *src;

  if (G_UNLIKELY (videoflip->process == NULL))
    goto not_negotiated;
680

Julien Moutte's avatar
Julien Moutte committed
681
682
683
  src = GST_BUFFER_DATA (in);
  dest = GST_BUFFER_DATA (out);

684
  GST_LOG_OBJECT (videoflip, "videoflip: flipping %dx%d to %dx%d (%s)",
685
686
      videoflip->from_width, videoflip->from_height, videoflip->to_width,
      videoflip->to_height, video_flip_methods[videoflip->method].value_nick);
Julien Moutte's avatar
Julien Moutte committed
687

688
  GST_OBJECT_LOCK (videoflip);
689
  videoflip->process (videoflip, dest, src);
690
  GST_OBJECT_UNLOCK (videoflip);
691

692
693
694
695
696
  return GST_FLOW_OK;

not_negotiated:
  GST_ERROR_OBJECT (videoflip, "Not negotiated yet");
  return GST_FLOW_NOT_NEGOTIATED;
697
698
}

Julien Moutte's avatar
Julien Moutte committed
699
static gboolean
700
gst_video_flip_src_event (GstBaseTransform * trans, GstEvent * event)
701
{
702
  GstVideoFlip *vf = GST_VIDEO_FLIP (trans);
703
  gdouble new_x, new_y, x, y;
Julien Moutte's avatar
Julien Moutte committed
704
705
706
707
708
709
710
711
712
713
714
715
  GstStructure *structure;

  GST_DEBUG_OBJECT (vf, "handling %s event", GST_EVENT_TYPE_NAME (event));

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_NAVIGATION:
      event =
          GST_EVENT (gst_mini_object_make_writable (GST_MINI_OBJECT (event)));

      structure = (GstStructure *) gst_event_get_structure (event);
      if (gst_structure_get_double (structure, "pointer_x", &x) &&
          gst_structure_get_double (structure, "pointer_y", &y)) {
716
        GST_DEBUG_OBJECT (vf, "converting %fx%f", x, y);
Julien Moutte's avatar
Julien Moutte committed
717
        switch (vf->method) {
Thomas Vander Stichele's avatar
borgify    
Thomas Vander Stichele committed
718
          case GST_VIDEO_FLIP_METHOD_90R:
719
720
            new_x = y;
            new_y = vf->to_width - x;
Julien Moutte's avatar
Julien Moutte committed
721
            break;
Thomas Vander Stichele's avatar
borgify    
Thomas Vander Stichele committed
722
          case GST_VIDEO_FLIP_METHOD_90L:
723
724
725
            new_x = vf->to_height - y;
            new_y = x;
            break;
Thomas Vander Stichele's avatar
borgify    
Thomas Vander Stichele committed
726
          case GST_VIDEO_FLIP_METHOD_OTHER:
727
728
729
            new_x = vf->to_height - y;
            new_y = vf->to_width - x;
            break;
Thomas Vander Stichele's avatar
borgify    
Thomas Vander Stichele committed
730
          case GST_VIDEO_FLIP_METHOD_TRANS:
731
732
            new_x = y;
            new_y = x;
Julien Moutte's avatar
Julien Moutte committed
733
            break;
Thomas Vander Stichele's avatar
borgify    
Thomas Vander Stichele committed
734
          case GST_VIDEO_FLIP_METHOD_180:
735
736
            new_x = vf->to_width - x;
            new_y = vf->to_height - y;
Julien Moutte's avatar
Julien Moutte committed
737
            break;
Thomas Vander Stichele's avatar
borgify    
Thomas Vander Stichele committed
738
          case GST_VIDEO_FLIP_METHOD_HORIZ:
739
740
            new_x = vf->to_width - x;
            new_y = y;
Julien Moutte's avatar
Julien Moutte committed
741
            break;
Thomas Vander Stichele's avatar
borgify    
Thomas Vander Stichele committed
742
          case GST_VIDEO_FLIP_METHOD_VERT:
743
744
            new_x = x;
            new_y = vf->to_height - y;
Julien Moutte's avatar
Julien Moutte committed
745
746
            break;
          default:
747
748
            new_x = x;
            new_y = y;
Julien Moutte's avatar
Julien Moutte committed
749
750
            break;
        }
751
        GST_DEBUG_OBJECT (vf, "to %fx%f", new_x, new_y);
752
753
        gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, new_x,
            "pointer_y", G_TYPE_DOUBLE, new_y, NULL);
Julien Moutte's avatar
Julien Moutte committed
754
755
756
757
758
      }
      break;
    default:
      break;
  }
759

760
  return TRUE;
761
762
763
}

static void
Thomas Vander Stichele's avatar
borgify    
Thomas Vander Stichele committed
764
gst_video_flip_set_property (GObject * object, guint prop_id,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
765
    const GValue * value, GParamSpec * pspec)
766
{
767
  GstVideoFlip *videoflip = GST_VIDEO_FLIP (object);
768
769

  switch (prop_id) {
770
    case PROP_METHOD:
Julien Moutte's avatar
Julien Moutte committed
771
    {
Thomas Vander Stichele's avatar
borgify    
Thomas Vander Stichele committed
772
      GstVideoFlipMethod method;
Julien Moutte's avatar
Julien Moutte committed
773
774

      method = g_value_get_enum (value);
775
      GST_OBJECT_LOCK (videoflip);
Julien Moutte's avatar
Julien Moutte committed
776
777
778
      if (method != videoflip->method) {
        GstBaseTransform *btrans = GST_BASE_TRANSFORM (videoflip);

779
780
781
782
        GST_DEBUG_OBJECT (videoflip, "Changing method from %s to %s",
            video_flip_methods[videoflip->method].value_nick,
            video_flip_methods[method].value_nick);

Julien Moutte's avatar
Julien Moutte committed
783
        videoflip->method = method;
784
785
786
787
        GST_OBJECT_UNLOCK (videoflip);

        gst_base_transform_set_passthrough (btrans,
            method == GST_VIDEO_FLIP_METHOD_IDENTITY);
788
        gst_base_transform_reconfigure (btrans);
789
790
      } else {
        GST_OBJECT_UNLOCK (videoflip);
791
      }
Julien Moutte's avatar
Julien Moutte committed
792
    }
793
794
      break;
    default:
Julien Moutte's avatar
Julien Moutte committed
795
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
796
797
798
799
800
      break;
  }
}

static void
Thomas Vander Stichele's avatar
borgify    
Thomas Vander Stichele committed
801
gst_video_flip_get_property (GObject * object, guint prop_id, GValue * value,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
802
    GParamSpec * pspec)
803
{
804
  GstVideoFlip *videoflip = GST_VIDEO_FLIP (object);
805
806

  switch (prop_id) {
807
    case PROP_METHOD:
808
      g_value_set_enum (value, videoflip->method);
809
810
811
812
813
814
815
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

Julien Moutte's avatar
Julien Moutte committed
816
static void
Thomas Vander Stichele's avatar
borgify    
Thomas Vander Stichele committed
817
gst_video_flip_base_init (gpointer g_class)
818
{
Julien Moutte's avatar
Julien Moutte committed
819
  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
820

821
822
823
  gst_element_class_set_details_simple (element_class, "Video flipper",
      "Filter/Effect/Video",
      "Flips and rotates video", "David Schleef <ds@schleef.org>");
824

Julien Moutte's avatar
Julien Moutte committed
825
  gst_element_class_add_pad_template (element_class,
Thomas Vander Stichele's avatar
borgify    
Thomas Vander Stichele committed
826
      gst_static_pad_template_get (&gst_video_flip_sink_template));
Julien Moutte's avatar
Julien Moutte committed
827
  gst_element_class_add_pad_template (element_class,
Thomas Vander Stichele's avatar
borgify    
Thomas Vander Stichele committed
828
      gst_static_pad_template_get (&gst_video_flip_src_template));
Julien Moutte's avatar
Julien Moutte committed
829
}
830

831
static void
832
gst_video_flip_class_init (GstVideoFlipClass * klass)
833
{
834
835
  GObjectClass *gobject_class = (GObjectClass *) klass;
  GstBaseTransformClass *trans_class = (GstBaseTransformClass *) klass;
836

837
838
  GST_DEBUG_CATEGORY_INIT (video_flip_debug, "videoflip", 0, "videoflip");

Thomas Vander Stichele's avatar
borgify    
Thomas Vander Stichele committed
839
840
  gobject_class->set_property = gst_video_flip_set_property;
  gobject_class->get_property = gst_video_flip_get_property;
841

842
  g_object_class_install_property (gobject_class, PROP_METHOD,
Julien Moutte's avatar
Julien Moutte committed
843
      g_param_spec_enum ("method", "method", "method",
844
          GST_TYPE_VIDEO_FLIP_METHOD, PROP_METHOD_DEFAULT,
845
          GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
846

Julien Moutte's avatar
Julien Moutte committed
847
  trans_class->transform_caps =
Thomas Vander Stichele's avatar
borgify    
Thomas Vander Stichele committed
848
849
850
851
      GST_DEBUG_FUNCPTR (gst_video_flip_transform_caps);
  trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_video_flip_set_caps);
  trans_class->get_unit_size = GST_DEBUG_FUNCPTR (gst_video_flip_get_unit_size);
  trans_class->transform = GST_DEBUG_FUNCPTR (gst_video_flip_transform);
852
853
  trans_class->before_transform =
      GST_DEBUG_FUNCPTR (gst_video_flip_before_transform);
854
  trans_class->src_event = GST_DEBUG_FUNCPTR (gst_video_flip_src_event);
855
856
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
857
static void
858
gst_video_flip_init (GstVideoFlip * videoflip, GstVideoFlipClass * klass)
859
{
860
  videoflip->method = PROP_METHOD_DEFAULT;
861
  gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (videoflip), TRUE);
862
}