gstvideobox.c 38 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/* GStreamer
 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
 *
 * 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.
 */
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
/**
 * SECTION:element-videobox
 * @see_also: #GstVideoCrop
 *
 * This plugin crops or enlarges the image. It takes 4 values as input, a
 * top, bottom, left and right offset. Positive values will crop that much
 * pixels from the respective border of the image, negative values will add
 * that much pixels. When pixels are added, you can specify their color. 
 * Some predefined colors are usable with an enum property.
 * 
 * The plugin is alpha channel aware and will try to negotiate with a format
 * that supports alpha channels first. When alpha channel is active two
 * other properties, alpha and border_alpha can be used to set the alpha
 * values of the inner picture and the border respectively. an alpha value of
 * 0.0 means total transparency, 1.0 is opaque.
 * 
 * The videobox plugin has many uses such as doing a mosaic of pictures, 
 * letterboxing video, cutting out pieces of video, picture in picture, etc..
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
 *
 * Setting autocrop to true changes the behavior of the plugin so that
 * caps determine crop properties rather than the other way around: given
 * input and output dimensions, the crop values are selected so that the
 * smaller frame is effectively centered in the larger frame.  This
 * involves either cropping or padding.
 * 
 * If you use autocrop there is little point in setting the other
 * properties manually because they will be overriden if the caps change,
 * but nothing stops you from doing so.
 * 
 * Sample pipeline:
 * |[
 * gst-launch videotestsrc ! videobox autocrop=true ! \
 *   "video/x-raw-yuv, width=600, height=400" ! ffmpegcolorspace ! ximagesink
 * ]|
53
 */
54

55 56 57
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
58 59 60

#include "gstvideobox.h"

61
#include <math.h>
62
#include <liboil/liboil.h>
63 64
#include <string.h>

65
GST_DEBUG_CATEGORY_STATIC (videobox_debug);
66 67
#define GST_CAT_DEFAULT videobox_debug

68 69 70 71 72 73 74 75 76 77
#define DEFAULT_LEFT      0
#define DEFAULT_RIGHT     0
#define DEFAULT_TOP       0
#define DEFAULT_BOTTOM    0
#define DEFAULT_FILL_TYPE VIDEO_BOX_FILL_BLACK
#define DEFAULT_ALPHA     1.0
#define DEFAULT_BORDER_ALPHA 1.0

enum
{
78 79 80 81 82 83 84
  PROP_0,
  PROP_LEFT,
  PROP_RIGHT,
  PROP_TOP,
  PROP_BOTTOM,
  PROP_FILL_TYPE,
  PROP_ALPHA,
85 86
  PROP_BORDER_ALPHA,
  PROP_AUTOCROP
87
      /* FILL ME */
88 89 90
};

static GstStaticPadTemplate gst_video_box_src_template =
91
    GST_STATIC_PAD_TEMPLATE ("src",
92 93
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
94 95
    GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV") ";"
        GST_VIDEO_CAPS_YUV ("I420"))
96 97 98
    );

static GstStaticPadTemplate gst_video_box_sink_template =
99
    GST_STATIC_PAD_TEMPLATE ("sink",
100 101
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
102 103
    GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV") ";"
        GST_VIDEO_CAPS_YUV ("I420"))
104 105
    );

106 107
GST_BOILERPLATE (GstVideoBox, gst_video_box, GstBaseTransform,
    GST_TYPE_BASE_TRANSFORM);
108 109 110 111 112 113

static void gst_video_box_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec);
static void gst_video_box_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec);

114
static gboolean video_box_recalc_transform (GstVideoBox * video_box);
115
static GstCaps *gst_video_box_transform_caps (GstBaseTransform * trans,
116
    GstPadDirection direction, GstCaps * from);
117 118
static gboolean gst_video_box_set_caps (GstBaseTransform * trans,
    GstCaps * in, GstCaps * out);
119 120
static gboolean gst_video_box_get_unit_size (GstBaseTransform * trans,
    GstCaps * caps, guint * size);
121
static GstFlowReturn gst_video_box_transform (GstBaseTransform * trans,
Wim Taymans's avatar
Wim Taymans committed
122
    GstBuffer * in, GstBuffer * out);
123 124 125 126 127 128

#define GST_TYPE_VIDEO_BOX_FILL (gst_video_box_fill_get_type())
static GType
gst_video_box_fill_get_type (void)
{
  static GType video_box_fill_type = 0;
129
  static const GEnumValue video_box_fill[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
130 131 132
    {VIDEO_BOX_FILL_BLACK, "Black", "black"},
    {VIDEO_BOX_FILL_GREEN, "Colorkey green", "green"},
    {VIDEO_BOX_FILL_BLUE, "Colorkey blue", "blue"},
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
    {0, NULL, NULL},
  };

  if (!video_box_fill_type) {
    video_box_fill_type =
        g_enum_register_static ("GstVideoBoxFill", video_box_fill);
  }
  return video_box_fill_type;
}


static void
gst_video_box_base_init (gpointer g_class)
{
  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);

Sebastian Dröge's avatar
Sebastian Dröge committed
149 150 151 152
  gst_element_class_set_details_simple (element_class, "Video box filter",
      "Filter/Effect/Video",
      "Resizes a video by adding borders or cropping",
      "Wim Taymans <wim@fluendo.com>");
153 154 155 156 157 158

  gst_element_class_add_pad_template (element_class,
      gst_static_pad_template_get (&gst_video_box_sink_template));
  gst_element_class_add_pad_template (element_class,
      gst_static_pad_template_get (&gst_video_box_src_template));
}
159

160 161 162 163 164 165 166 167 168 169 170 171 172
static void
gst_video_box_finalize (GObject * object)
{
  GstVideoBox *video_box = GST_VIDEO_BOX (object);

  if (video_box->mutex) {
    g_mutex_free (video_box->mutex);
    video_box->mutex = NULL;
  }

  G_OBJECT_CLASS (parent_class)->finalize (object);
}

173 174 175
static void
gst_video_box_class_init (GstVideoBoxClass * klass)
{
Sebastian Dröge's avatar
Sebastian Dröge committed
176 177
  GObjectClass *gobject_class = (GObjectClass *) klass;
  GstBaseTransformClass *trans_class = (GstBaseTransformClass *) klass;
178

Wim Taymans's avatar
Wim Taymans committed
179 180
  gobject_class->set_property = gst_video_box_set_property;
  gobject_class->get_property = gst_video_box_get_property;
181
  gobject_class->finalize = gst_video_box_finalize;
Wim Taymans's avatar
Wim Taymans committed
182

183
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FILL_TYPE,
184 185
      g_param_spec_enum ("fill", "Fill", "How to fill the borders",
          GST_TYPE_VIDEO_BOX_FILL, DEFAULT_FILL_TYPE,
Sebastian Dröge's avatar
Sebastian Dröge committed
186
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
187
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LEFT,
188 189
      g_param_spec_int ("left", "Left",
          "Pixels to box at left (<0  = add a border)", G_MININT, G_MAXINT,
Sebastian Dröge's avatar
Sebastian Dröge committed
190
          DEFAULT_LEFT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
191
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_RIGHT,
192 193
      g_param_spec_int ("right", "Right",
          "Pixels to box at right (<0 = add a border)", G_MININT, G_MAXINT,
Sebastian Dröge's avatar
Sebastian Dröge committed
194
          DEFAULT_RIGHT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
195
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TOP,
196 197
      g_param_spec_int ("top", "Top",
          "Pixels to box at top (<0 = add a border)", G_MININT, G_MAXINT,
Sebastian Dröge's avatar
Sebastian Dröge committed
198
          DEFAULT_TOP, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
199
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BOTTOM,
200 201
      g_param_spec_int ("bottom", "Bottom",
          "Pixels to box at bottom (<0 = add a border)", G_MININT, G_MAXINT,
Sebastian Dröge's avatar
Sebastian Dröge committed
202
          DEFAULT_BOTTOM, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
203
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_ALPHA,
204
      g_param_spec_double ("alpha", "Alpha", "Alpha value picture", 0.0, 1.0,
Sebastian Dröge's avatar
Sebastian Dröge committed
205
          DEFAULT_ALPHA, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
206
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BORDER_ALPHA,
Sebastian Dröge's avatar
Sebastian Dröge committed
207
      g_param_spec_double ("border-alpha", "Border Alpha",
208
          "Alpha value of the border", 0.0, 1.0, DEFAULT_BORDER_ALPHA,
Sebastian Dröge's avatar
Sebastian Dröge committed
209
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
210 211 212 213 214 215 216 217
  /**
   * GstVideoBox:autocrop
   *
   * If set to %TRUE videobox will automatically crop/pad the input
   * video to be centered in the output.
   *
   * Since: 0.10.16
   **/
218 219
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_AUTOCROP,
      g_param_spec_boolean ("autocrop", "Auto crop",
Sebastian Dröge's avatar
Sebastian Dröge committed
220
          "Auto crop", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
221

222
  trans_class->transform = GST_DEBUG_FUNCPTR (gst_video_box_transform);
223 224 225 226
  trans_class->transform_caps =
      GST_DEBUG_FUNCPTR (gst_video_box_transform_caps);
  trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_video_box_set_caps);
  trans_class->get_unit_size = GST_DEBUG_FUNCPTR (gst_video_box_get_unit_size);
227 228 229
}

static void
230
gst_video_box_init (GstVideoBox * video_box, GstVideoBoxClass * g_class)
231 232 233 234 235
{
  video_box->box_right = DEFAULT_RIGHT;
  video_box->box_left = DEFAULT_LEFT;
  video_box->box_top = DEFAULT_TOP;
  video_box->box_bottom = DEFAULT_BOTTOM;
236 237 238 239
  video_box->crop_right = 0;
  video_box->crop_left = 0;
  video_box->crop_top = 0;
  video_box->crop_bottom = 0;
240 241 242
  video_box->fill_type = DEFAULT_FILL_TYPE;
  video_box->alpha = DEFAULT_ALPHA;
  video_box->border_alpha = DEFAULT_BORDER_ALPHA;
243
  video_box->autocrop = FALSE;
244 245

  video_box->mutex = g_mutex_new ();
246 247 248 249 250 251
}

static void
gst_video_box_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec)
{
252
  GstVideoBox *video_box = GST_VIDEO_BOX (object);
253

254
  g_mutex_lock (video_box->mutex);
255
  switch (prop_id) {
256
    case PROP_LEFT:
257 258 259 260 261 262 263 264 265
      video_box->box_left = g_value_get_int (value);
      if (video_box->box_left < 0) {
        video_box->border_left = -video_box->box_left;
        video_box->crop_left = 0;
      } else {
        video_box->border_left = 0;
        video_box->crop_left = video_box->box_left;
      }
      break;
266
    case PROP_RIGHT:
267 268 269 270 271 272 273 274 275
      video_box->box_right = g_value_get_int (value);
      if (video_box->box_right < 0) {
        video_box->border_right = -video_box->box_right;
        video_box->crop_right = 0;
      } else {
        video_box->border_right = 0;
        video_box->crop_right = video_box->box_right;
      }
      break;
276
    case PROP_TOP:
277 278 279 280 281 282 283 284 285
      video_box->box_top = g_value_get_int (value);
      if (video_box->box_top < 0) {
        video_box->border_top = -video_box->box_top;
        video_box->crop_top = 0;
      } else {
        video_box->border_top = 0;
        video_box->crop_top = video_box->box_top;
      }
      break;
286
    case PROP_BOTTOM:
287 288 289 290 291 292 293 294 295
      video_box->box_bottom = g_value_get_int (value);
      if (video_box->box_bottom < 0) {
        video_box->border_bottom = -video_box->box_bottom;
        video_box->crop_bottom = 0;
      } else {
        video_box->border_bottom = 0;
        video_box->crop_bottom = video_box->box_bottom;
      }
      break;
296
    case PROP_FILL_TYPE:
297 298
      video_box->fill_type = g_value_get_enum (value);
      break;
299
    case PROP_ALPHA:
300 301
      video_box->alpha = g_value_get_double (value);
      break;
302
    case PROP_BORDER_ALPHA:
303 304
      video_box->border_alpha = g_value_get_double (value);
      break;
305 306 307
    case PROP_AUTOCROP:
      video_box->autocrop = g_value_get_boolean (value);
      break;
308 309 310 311
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
312
  video_box_recalc_transform (video_box);
313 314

  GST_DEBUG_OBJECT (video_box, "Calling reconfigure");
Sebastian Dröge's avatar
Sebastian Dröge committed
315
  gst_base_transform_reconfigure (GST_BASE_TRANSFORM_CAST (video_box));
316 317

  g_mutex_unlock (video_box->mutex);
318 319 320 321 322 323 324 325
}

static void
gst_video_box_autocrop (GstVideoBox * video_box)
{
  gint crop_w = (video_box->in_width - video_box->out_width) / 2;
  gint crop_h = (video_box->in_height - video_box->out_height) / 2;

326
  g_mutex_lock (video_box->mutex);
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362
  video_box->box_left = crop_w;
  if (video_box->box_left < 0) {
    video_box->border_left = -video_box->box_left;
    video_box->crop_left = 0;
  } else {
    video_box->border_left = 0;
    video_box->crop_left = video_box->box_left;
  }

  video_box->box_right = crop_w;
  if (video_box->box_right < 0) {
    video_box->border_right = -video_box->box_right;
    video_box->crop_right = 0;
  } else {
    video_box->border_right = 0;
    video_box->crop_right = video_box->box_right;
  }

  video_box->box_top = crop_h;
  if (video_box->box_top < 0) {
    video_box->border_top = -video_box->box_top;
    video_box->crop_top = 0;
  } else {
    video_box->border_top = 0;
    video_box->crop_top = video_box->box_top;
  }

  video_box->box_bottom = crop_h;
  if (video_box->box_bottom < 0) {
    video_box->border_bottom = -video_box->box_bottom;
    video_box->crop_bottom = 0;
  } else {
    video_box->border_bottom = 0;
    video_box->crop_bottom = video_box->box_bottom;
  }

363
  g_mutex_unlock (video_box->mutex);
364
}
365

366 367 368 369
static void
gst_video_box_get_property (GObject * object, guint prop_id, GValue * value,
    GParamSpec * pspec)
{
370
  GstVideoBox *video_box = GST_VIDEO_BOX (object);
371 372

  switch (prop_id) {
373
    case PROP_LEFT:
374 375
      g_value_set_int (value, video_box->box_left);
      break;
376
    case PROP_RIGHT:
377 378
      g_value_set_int (value, video_box->box_right);
      break;
379
    case PROP_TOP:
380 381
      g_value_set_int (value, video_box->box_top);
      break;
382
    case PROP_BOTTOM:
383 384
      g_value_set_int (value, video_box->box_bottom);
      break;
385
    case PROP_FILL_TYPE:
386 387
      g_value_set_enum (value, video_box->fill_type);
      break;
388
    case PROP_ALPHA:
389 390
      g_value_set_double (value, video_box->alpha);
      break;
391
    case PROP_BORDER_ALPHA:
392 393
      g_value_set_double (value, video_box->border_alpha);
      break;
394 395 396
    case PROP_AUTOCROP:
      g_value_set_boolean (value, video_box->autocrop);
      break;
397 398 399 400 401 402
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

403
static GstCaps *
404 405
gst_video_box_transform_caps (GstBaseTransform * trans,
    GstPadDirection direction, GstCaps * from)
406
{
Sebastian Dröge's avatar
Sebastian Dröge committed
407
  GstVideoBox *video_box = GST_VIDEO_BOX (trans);
408 409
  GstCaps *to, *ret;
  const GstCaps *templ;
410
  GstStructure *structure;
411 412
  GstPad *other;
  gint width, height;
413

414
  to = gst_caps_copy (from);
415 416 417 418 419
  structure = gst_caps_get_structure (to, 0);

  /* get rid of format */
  gst_structure_remove_field (structure, "format");

420 421 422 423
  /* otherwise caps nego will fail: */
  if (video_box->autocrop) {
    gst_structure_remove_field (structure, "width");
    gst_structure_remove_field (structure, "height");
424 425
  }

426 427 428 429 430 431 432 433 434 435 436 437 438 439 440
  if (!video_box->autocrop) {
    /* calculate width and height */
    if (gst_structure_get_int (structure, "width", &width)) {
      if (direction == GST_PAD_SINK) {
        width -= video_box->box_left;
        width -= video_box->box_right;
      } else {
        width += video_box->box_left;
        width += video_box->box_right;
      }
      if (width <= 0)
        width = 1;

      GST_DEBUG_OBJECT (trans, "New caps width: %d", width);
      gst_structure_set (structure, "width", G_TYPE_INT, width, NULL);
441
    }
442

443 444 445 446 447 448 449 450
    if (gst_structure_get_int (structure, "height", &height)) {
      if (direction == GST_PAD_SINK) {
        height -= video_box->box_top;
        height -= video_box->box_bottom;
      } else {
        height += video_box->box_top;
        height += video_box->box_bottom;
      }
451

452 453 454 455 456 457
      if (height <= 0)
        height = 1;

      GST_DEBUG_OBJECT (trans, "New caps height: %d", height);
      gst_structure_set (structure, "height", G_TYPE_INT, height, NULL);
    }
458 459
  }

460 461 462 463 464 465
  /* filter against set allowed caps on the pad */
  other = (direction == GST_PAD_SINK) ? trans->srcpad : trans->sinkpad;

  templ = gst_pad_get_pad_template_caps (other);
  ret = gst_caps_intersect (to, templ);
  gst_caps_unref (to);
466

467
  GST_DEBUG_OBJECT (video_box, "direction %d, transformed %" GST_PTR_FORMAT
468 469 470 471 472
      " to %" GST_PTR_FORMAT, direction, from, ret);

  return ret;
}

473 474 475 476 477 478
static gboolean
video_box_recalc_transform (GstVideoBox * video_box)
{
  gboolean res = TRUE;

  /* if we have the same format in and out and we don't need to perform any
Sebastian Dröge's avatar
Sebastian Dröge committed
479
   * cropping at all, we can just operate in passthrough mode */
480 481 482 483 484
  if (video_box->in_fourcc == video_box->out_fourcc &&
      video_box->box_left == 0 && video_box->box_right == 0 &&
      video_box->box_top == 0 && video_box->box_bottom == 0) {

    GST_LOG_OBJECT (video_box, "we are using passthrough");
Sebastian Dröge's avatar
Sebastian Dröge committed
485 486
    gst_base_transform_set_passthrough (GST_BASE_TRANSFORM_CAST (video_box),
        TRUE);
487 488
  } else {
    GST_LOG_OBJECT (video_box, "we are not using passthrough");
Sebastian Dröge's avatar
Sebastian Dröge committed
489 490
    gst_base_transform_set_passthrough (GST_BASE_TRANSFORM_CAST (video_box),
        FALSE);
491 492 493 494
  }
  return res;
}

Wim Taymans's avatar
Wim Taymans committed
495
static gboolean
496
gst_video_box_set_caps (GstBaseTransform * trans, GstCaps * in, GstCaps * out)
497
{
Sebastian Dröge's avatar
Sebastian Dröge committed
498
  GstVideoBox *video_box = GST_VIDEO_BOX (trans);
499 500 501
  GstStructure *structure;
  gboolean ret;

502
  structure = gst_caps_get_structure (in, 0);
503 504
  ret = gst_structure_get_int (structure, "width", &video_box->in_width);
  ret &= gst_structure_get_int (structure, "height", &video_box->in_height);
505
  ret &= gst_structure_get_fourcc (structure, "format", &video_box->in_fourcc);
506

507 508 509
  structure = gst_caps_get_structure (out, 0);
  ret &= gst_structure_get_int (structure, "width", &video_box->out_width);
  ret &= gst_structure_get_int (structure, "height", &video_box->out_height);
510
  ret &= gst_structure_get_fourcc (structure, "format", &video_box->out_fourcc);
511

512 513 514 515
  /* something wrong getting the caps */
  if (!ret)
    goto no_caps;

516 517 518 519 520
  GST_DEBUG_OBJECT (trans, "Input w: %d h: %d", video_box->in_width,
      video_box->in_height);
  GST_DEBUG_OBJECT (trans, "Output w: %d h: %d", video_box->out_width,
      video_box->out_height);

521 522
  if (video_box->autocrop)
    gst_video_box_autocrop (video_box);
523

524 525
  /* recalc the transformation strategy */
  ret = video_box_recalc_transform (video_box);
526

Wim Taymans's avatar
Wim Taymans committed
527
  return ret;
528 529 530 531 532 533 534

  /* ERRORS */
no_caps:
  {
    GST_DEBUG_OBJECT (video_box, "Could not get all caps fields");
    return FALSE;
  }
535 536
}

537
/* see gst-plugins/gst/games/gstvideoimage.c, paint_setup_I420() */
538 539 540
#define GST_VIDEO_I420_Y_ROWSTRIDE(width) (GST_ROUND_UP_4(width))
#define GST_VIDEO_I420_U_ROWSTRIDE(width) (GST_ROUND_UP_8(width)/2)
#define GST_VIDEO_I420_V_ROWSTRIDE(width) ((GST_ROUND_UP_8(GST_VIDEO_I420_Y_ROWSTRIDE(width)))/2)
541

542
#define GST_VIDEO_I420_Y_OFFSET(w,h) (0)
543 544
#define GST_VIDEO_I420_U_OFFSET(w,h) (GST_VIDEO_I420_Y_OFFSET(w,h)+(GST_VIDEO_I420_Y_ROWSTRIDE(w)*GST_ROUND_UP_2(h)))
#define GST_VIDEO_I420_V_OFFSET(w,h) (GST_VIDEO_I420_U_OFFSET(w,h)+(GST_VIDEO_I420_U_ROWSTRIDE(w)*GST_ROUND_UP_2(h)/2))
545

546
#define GST_VIDEO_I420_SIZE(w,h)     (GST_VIDEO_I420_V_OFFSET(w,h)+(GST_VIDEO_I420_V_ROWSTRIDE(w)*GST_ROUND_UP_2(h)/2))
547

548 549 550
static gboolean
gst_video_box_get_unit_size (GstBaseTransform * trans, GstCaps * caps,
    guint * size)
Wim Taymans's avatar
Wim Taymans committed
551
{
Sebastian Dröge's avatar
Sebastian Dröge committed
552
  GstVideoBox *video_box = GST_VIDEO_BOX (trans);
553 554 555
  GstStructure *structure = NULL;
  guint32 fourcc;
  gint width, height;
Wim Taymans's avatar
Wim Taymans committed
556

Stefan Kost's avatar
Stefan Kost committed
557 558
  g_assert (size);

559 560 561 562 563 564 565 566 567 568 569 570 571 572 573
  structure = gst_caps_get_structure (caps, 0);
  gst_structure_get_fourcc (structure, "format", &fourcc);
  gst_structure_get_int (structure, "width", &width);
  gst_structure_get_int (structure, "height", &height);

  switch (fourcc) {
    case GST_MAKE_FOURCC ('A', 'Y', 'U', 'V'):
      *size = width * height * 4;
      break;
    case GST_MAKE_FOURCC ('I', '4', '2', '0'):
      *size = GST_VIDEO_I420_SIZE (width, height);
      break;
    default:
      return FALSE;
      break;
Wim Taymans's avatar
Wim Taymans committed
574
  }
575

576 577
  GST_LOG_OBJECT (video_box, "Returning from _unit_size %d", *size);

578
  return TRUE;
Wim Taymans's avatar
Wim Taymans committed
579 580
}

581 582 583
static const guint8 yuv_colors_Y[VIDEO_BOX_FILL_LAST] = { 16, 150, 29 };
static const guint8 yuv_colors_U[VIDEO_BOX_FILL_LAST] = { 128, 46, 255 };
static const guint8 yuv_colors_V[VIDEO_BOX_FILL_LAST] = { 128, 21, 107 };
584

585 586 587 588 589 590 591 592 593 594
static void
gst_video_box_copy_plane_i420 (GstVideoBox * video_box, guint8 * src,
    guint8 * dest, gint br, gint bl, gint bt, gint bb, gint src_crop_width,
    gint src_crop_height, gint src_stride, gint dest_width, gint dest_stride,
    guint8 fill_color)
{
  gint j;

  /* top border */
  for (j = 0; j < bt; j++) {
595
    oil_splat_u8_ns (dest, &fill_color, dest_width);
596 597 598 599 600
    dest += dest_stride;
  }

  /* copy and add left and right border */
  for (j = 0; j < src_crop_height; j++) {
601 602 603
    oil_splat_u8_ns (dest, &fill_color, bl);
    oil_memcpy (dest + bl, src, src_crop_width);
    oil_splat_u8_ns (dest + bl + src_crop_width, &fill_color, br);
604 605 606 607 608 609
    dest += dest_stride;
    src += src_stride;
  }

  /* bottom border */
  for (j = 0; j < bb; j++) {
610
    oil_splat_u8_ns (dest, &fill_color, dest_width);
611 612 613 614
    dest += dest_stride;
  }
}

615
static void
616
gst_video_box_apply_alpha (guint8 * dest, guint8 alpha)
617
{
618 619 620
  if (dest[0] != 0)
    dest[0] = alpha;
}
621

622 623 624 625 626 627 628 629 630 631 632 633 634
static void
gst_video_box_ayuv_ayuv (GstVideoBox * video_box, guint8 * src, guint8 * dest)
{
  gint dblen = video_box->out_height * video_box->out_width;
  guint32 *destb = (guint32 *) dest;
  guint32 *srcb = (guint32 *) src;
  guint8 b_alpha = (guint8) (video_box->border_alpha * 255);
  guint8 i_alpha = (guint8) (video_box->alpha * 255);
  gint br, bl, bt, bb, crop_w, crop_h;
  gint i;
  guint32 *loc = destb;
  guint32 empty_pixel;

Sebastian Dröge's avatar
Sebastian Dröge committed
635
  GST_LOG_OBJECT (video_box, "Processing AYUV -> AYUV data");
636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657

  crop_h = 0;
  crop_w = 0;
  empty_pixel = GUINT32_FROM_BE ((b_alpha << 24) |
      (yuv_colors_Y[video_box->fill_type] << 16) |
      (yuv_colors_U[video_box->fill_type] << 8) |
      yuv_colors_V[video_box->fill_type]);

  br = video_box->box_right;
  bl = video_box->box_left;
  bt = video_box->box_top;
  bb = video_box->box_bottom;

  if (br >= 0 && bl >= 0) {
    crop_w = video_box->in_width - (br + bl);
  } else if (br >= 0 && bl < 0) {
    crop_w = video_box->in_width - (br);
  } else if (br < 0 && bl >= 0) {
    crop_w = video_box->in_width - (bl);
  } else if (br < 0 && bl < 0) {
    crop_w = video_box->in_width;
  }
658

659 660 661 662 663 664 665 666 667
  if (bb >= 0 && bt >= 0) {
    crop_h = video_box->in_height - (bb + bt);
  } else if (bb >= 0 && bt < 0) {
    crop_h = video_box->in_height - (bb);
  } else if (bb < 0 && bt >= 0) {
    crop_h = video_box->in_height - (bt);
  } else if (bb < 0 && bt < 0) {
    crop_h = video_box->in_height;
  }
668

Sebastian Dröge's avatar
Sebastian Dröge committed
669 670 671
  GST_DEBUG_OBJECT (video_box, "Borders are: L:%d, R:%d, T:%d, B:%d", bl, br,
      bt, bb);
  GST_DEBUG_OBJECT (video_box, "Alpha value is: %d", i_alpha);
672

673 674 675 676
  if (crop_h <= 0 || crop_w <= 0) {
    oil_splat_u32_ns (destb, &empty_pixel, dblen);
  } else {
    guint32 *src_loc = srcb;
677

678 679 680 681 682 683 684
    /* Top border */
    if (bt < 0) {
      oil_splat_u32_ns (loc, &empty_pixel, (-bt) * video_box->out_width);
      loc = loc + ((-bt) * video_box->out_width);
    } else {
      src_loc = src_loc + (bt * video_box->in_width);
    }
685

686 687
    if (bl >= 0)
      src_loc += bl;
688

689 690
    for (i = 0; i < crop_h; i++) {
      gint j;
691

692 693 694 695 696
      /* Left border */
      if (bl < 0) {
        oil_splat_u32_ns (loc, &empty_pixel, -bl);
        loc += (-bl);
      }
697

698 699
      /* Cropped area */
      oil_copy_u8 ((guint8 *) loc, (guint8 *) src_loc, crop_w * 4);
700

701 702
      for (j = 0; j < crop_w; j++)
        gst_video_box_apply_alpha ((guint8 *) & loc[j], i_alpha);
703

704 705
      src_loc += video_box->in_width;
      loc += crop_w;
706

707 708 709 710 711 712
      /* Right border */
      if (br < 0) {
        oil_splat_u32_ns (loc, &empty_pixel, -br);
        loc += (-br);
      }
    }
713

714 715 716 717 718 719
    /* Bottom border */
    if (bb < 0) {
      oil_splat_u32_ns (loc, &empty_pixel, (-bb) * video_box->out_width);
    }
  }

Sebastian Dröge's avatar
Sebastian Dröge committed
720
  GST_LOG_OBJECT (video_box, "image created");
721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755
}

static gpointer
gst_video_box_clear (gpointer dest, gint size)
{
  guint8 nil = 255;

  oil_splat_u8_ns (dest, &nil, size);

  return dest;
}

static gint
UVfloor (gint j)
{
  return floor (((float) j) / 2);
}

static gint
UVceil (gint j)
{
  return ceil (((float) j) / 2);
}

static void
gst_video_box_ayuv_i420 (GstVideoBox * video_box, guint8 * src, guint8 * dest)
{
  gint br, bl, bt, bb, crop_w, crop_h, rest;
  gint Ysize, Usize, Vsize;
  guint8 *Ydest, *Udest, *Vdest;
  guint8 *Utemp, *Vtemp;
  guint32 empty_px_values[3];
  gint i, j;
  guint Ywidth, Uwidth, Vwidth;

Sebastian Dröge's avatar
Sebastian Dröge committed
756
  GST_LOG_OBJECT (video_box, "AYUV to I420 conversion");
757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809

  crop_h = 0;
  crop_w = 0;
  rest = 0;

  empty_px_values[0] = yuv_colors_Y[video_box->fill_type];
  empty_px_values[1] = yuv_colors_U[video_box->fill_type];
  empty_px_values[2] = yuv_colors_V[video_box->fill_type];

  Ywidth = GST_VIDEO_I420_Y_ROWSTRIDE (video_box->out_width);
  Uwidth = GST_VIDEO_I420_U_ROWSTRIDE (video_box->out_width);
  Vwidth = GST_VIDEO_I420_V_ROWSTRIDE (video_box->out_width);

  Ydest = dest + GST_VIDEO_I420_Y_OFFSET (video_box->out_width,
      video_box->out_height);
  Udest = Ydest + GST_VIDEO_I420_U_OFFSET (video_box->out_width,
      video_box->out_height);
  Vdest = Ydest + GST_VIDEO_I420_V_OFFSET (video_box->out_width,
      video_box->out_height);

  Ysize = Ywidth * video_box->out_height;
  Usize = Uwidth * UVceil (video_box->out_height);
  Vsize = Vwidth * UVceil (video_box->out_height);

  br = video_box->box_right;
  bl = video_box->box_left;
  bt = video_box->box_top;
  bb = video_box->box_bottom;

  if (br >= 0 && bl >= 0) {
    rest = Ywidth - video_box->out_width;
    crop_w = video_box->in_width - (bl + br);
  } else if (br >= 0 && bl < 0) {
    rest = Ywidth - video_box->out_width;
    crop_w = video_box->in_width - (br);
  } else if (br < 0 && bl >= 0) {
    rest = Ywidth - video_box->out_width;
    crop_w = video_box->in_width - (bl);
  } else if (br < 0 && bl < 0) {
    rest = Ywidth - video_box->out_width;
    crop_w = video_box->in_width;
  }

  if (bb >= 0 && bt >= 0) {
    crop_h = video_box->in_height - (bb + bt);
  } else if (bb >= 0 && bt < 0) {
    crop_h = video_box->in_height - (bb);
  } else if (bb < 0 && bt >= 0) {
    crop_h = video_box->in_height - (bt);
  } else if (bb < 0 && bt < 0) {
    crop_h = video_box->in_height;
  }

810 811
  Utemp = g_malloc0 (Uwidth);
  Vtemp = g_malloc0 (Vwidth);
812

Sebastian Dröge's avatar
Sebastian Dröge committed
813 814
  GST_LOG_OBJECT (video_box, "Borders are: L:%d, R:%d, T:%d, B:%d", bl, br, bt,
      bb);
815

Sebastian Dröge's avatar
Sebastian Dröge committed
816
  GST_LOG_OBJECT (video_box, "Starting conversion");
817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852

  if (crop_h <= 0 || crop_w <= 0) {
    oil_splat_u8_ns (Ydest, (guint8 *) & empty_px_values[0], Ysize);
    oil_splat_u8_ns (Udest, (guint8 *) & empty_px_values[1], Usize);
    oil_splat_u8_ns (Vdest, (guint8 *) & empty_px_values[2], Vsize);
  } else {
    gboolean sumbuff = FALSE;
    guint32 *src_loc1;
    gint a = 0;

    src_loc1 = (guint32 *) src;

    if (bt < 0) {
      oil_splat_u8_ns (Ydest, (guint8 *) & empty_px_values[0], (-bt) * Ywidth);

      oil_splat_u8_ns (Udest, (guint8 *) & empty_px_values[1],
          (UVfloor (-bt) * Uwidth) + 7);
      oil_splat_u8_ns (Vdest, (guint8 *) & empty_px_values[2],
          UVfloor (-bt) * Vwidth);

      if ((-bt) % 2 > 0) {
        oil_splat_u8_ns (Utemp, (guint8 *) & empty_px_values[1], Uwidth);
        oil_splat_u8_ns (Vtemp, (guint8 *) & empty_px_values[2], Vwidth);
        sumbuff = TRUE;
      }

      Ydest += ((-bt) * Ywidth);
      Udest += (UVfloor (-bt) * Uwidth);
      Vdest += (UVfloor (-bt) * Vwidth);
    } else {
      src_loc1 = src_loc1 + (bt * video_box->in_width);
    }

    if (bl >= 0)
      src_loc1 += bl;

Sebastian Dröge's avatar
Sebastian Dröge committed
853 854 855 856 857
    GST_LOG_OBJECT (video_box, "Cropped area");
    GST_LOG_OBJECT (video_box, "Ydest value: %p Ywidth: %u", Ydest, Ywidth);
    GST_LOG_OBJECT (video_box, "Udest value: %p Uwidth: %u", Udest, Uwidth);
    GST_LOG_OBJECT (video_box, "Vdest value: %p Vwidth: %u", Vdest, Vwidth);
    GST_LOG_OBJECT (video_box, "Rest: %d", rest);
858 859 860
    for (i = 0; i < crop_h; i++) {
      a = 0;
      if (sumbuff) {
861
        /* left border */
862 863 864 865 866 867 868 869 870 871 872 873
        if (bl < 0) {
          oil_splat_u8_ns (Ydest, (guint8 *) & empty_px_values[0], -bl);

          for (j = 0; j < -bl; j++) {
            Utemp[UVfloor (j)] = (Utemp[UVfloor (j)] + empty_px_values[1]) / 2;
            Vtemp[UVfloor (j)] = (Vtemp[UVfloor (j)] + empty_px_values[2]) / 2;
          }
          Ydest += -bl;
          a = -bl;
        }

        for (j = 0; j < crop_w; j++) {
874
          /* check ARCH */
875 876 877 878 879 880 881 882
          Ydest[j] = ((guint8 *) & src_loc1[j])[1];
          Utemp[UVfloor (a + j)] =
              (Utemp[UVfloor (a + j)] + ((guint8 *) & src_loc1[j])[2]) / 2;
          Vtemp[UVfloor (a + j)] =
              (Vtemp[UVfloor (a + j)] + ((guint8 *) & src_loc1[j])[3]) / 2;
        }
        Ydest += crop_w;

883
        /* right border */
884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903
        if (br < 0) {
          oil_splat_u8_ns (Ydest, (guint8 *) & empty_px_values[0], -br);
          for (j = 0; j < -br; j++) {
            Utemp[UVfloor (a + crop_w + j)] =
                (Utemp[UVfloor (a + crop_w + j)] + empty_px_values[1]) / 2;
            Vtemp[UVfloor (a + crop_w + j)] =
                (Vtemp[UVfloor (a + crop_w + j)] + empty_px_values[2]) / 2;
          }
          Ydest += -br;
        }
        oil_copy_u8 (Udest, Utemp, Uwidth);
        oil_copy_u8 (Vdest, Vtemp, Vwidth);
        Udest += Uwidth;
        Vdest += Vwidth;
        Ydest += rest;
        gst_video_box_clear (Utemp, Uwidth);
        gst_video_box_clear (Vtemp, Vwidth);
        src_loc1 += video_box->in_width;
        sumbuff = FALSE;
      } else {
904
        /* left border */
905 906 907 908 909 910 911 912 913 914 915 916
        a = 0;
        if (bl < 0) {
          oil_splat_u8_ns (Ydest, (guint8 *) & empty_px_values[0], -bl);
          oil_splat_u8_ns (Vtemp, (guint8 *) & empty_px_values[1],
              UVceil (-bl));
          oil_splat_u8_ns (Utemp, (guint8 *) & empty_px_values[2],
              UVceil (-bl));
          Ydest += -bl;
          a = -bl;
        }

        for (j = 0; j < crop_w; j++) {
917
          /* check ARCH */
918 919 920 921 922 923 924 925
          Ydest[j] = ((guint8 *) & src_loc1[j])[1];

          if ((a + j) % 2 > 0) {
            Utemp[UVfloor (a + j)] =
                (Utemp[UVfloor (a + j)] + ((guint8 *) & src_loc1[j])[2]) / 2;
            Vtemp[UVfloor (a + j)] =
                (Vtemp[UVfloor (a + j)] + ((guint8 *) & src_loc1[j])[3]) / 2;
          } else {
926 927
            Utemp[UVfloor (a + j)] = ((guint8 *) & src_loc1[j])[2];
            Vtemp[UVfloor (a + j)] = ((guint8 *) & src_loc1[j])[3];
928 929 930 931
          }
        }
        Ydest += crop_w;

932
        /* right border */
933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956
        if (br < 0) {
          j = 0;
          if ((a + crop_w) % 2 > 0) {
            Utemp[UVfloor (a + crop_w)] =
                (Utemp[UVfloor (a + crop_w)] + empty_px_values[1]) / 2;
            Vtemp[UVfloor (a + crop_w)] =
                (Vtemp[UVfloor (a + crop_w)] + empty_px_values[2]) / 2;
            a++;
            j = -1;
          }

          oil_splat_u8_ns (Ydest, (guint8 *) & empty_px_values[0], -br);
          oil_splat_u8_ns (&Utemp[UVfloor (a + crop_w)],
              (guint8 *) & empty_px_values[1], UVceil ((-br) + j));
          oil_splat_u8_ns (&Vtemp[UVfloor (a + crop_w)],
              (guint8 *) & empty_px_values[2], UVceil ((-br) + j));
          Ydest += -br;
        }
        Ydest += rest;
        src_loc1 += video_box->in_width;
        sumbuff = TRUE;
      }
    }

957
    /* bottom border */
958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984
    if (bb < 0) {
      oil_splat_u8_ns (Ydest, (guint8 *) & empty_px_values[0], (-bb) * Ywidth);
      if (sumbuff) {
        for (i = 0; i < Uwidth; i++) {
          Utemp[i] = (Utemp[i] + empty_px_values[1]) / 2;
        }
        for (i = 0; i < Vwidth; i++) {
          Vtemp[i] = (Vtemp[i] + empty_px_values[2]) / 2;
        }

        oil_copy_u8 (Udest, Utemp, Uwidth);
        oil_copy_u8 (Vdest, Vtemp, Vwidth);
        Udest += Uwidth;
        Vdest += Vwidth;
        sumbuff = FALSE;
      }
      oil_splat_u8_ns (Udest, (guint8 *) & empty_px_values[1],
          (UVfloor ((-bb))) * Uwidth);
      oil_splat_u8_ns (Vdest, (guint8 *) & empty_px_values[2],
          (UVfloor ((-bb))) * Vwidth);
    }
    if (sumbuff) {
      oil_copy_u8 (Udest, Utemp, Uwidth);
      oil_copy_u8 (Vdest, Vtemp, Vwidth);
    }
  }

Sebastian Dröge's avatar
Sebastian Dröge committed
985
  GST_LOG_OBJECT (video_box, "image created");
986 987
  g_free (Utemp);
  g_free (Vtemp);
988 989 990
}

static void
991
gst_video_box_i420_ayuv (GstVideoBox * video_box, guint8 * src, guint8 * dest)
992 993 994
{
  guint8 *srcY, *srcU, *srcV;
  gint crop_width, crop_width2, crop_height;
995
  gint out_width, out_height;
996
  gint src_stridey, src_strideu, src_stridev;
997 998 999 1000 1001
  gint br, bl, bt, bb;
  gint colorY, colorU, colorV;
  gint i, j;
  guint8 b_alpha = (guint8) (video_box->border_alpha * 255);
  guint8 i_alpha = (guint8) (video_box->alpha * 255);
1002 1003
  guint32 *destp;
  guint32 *destb = (guint32 *) dest;
1004 1005 1006 1007 1008 1009 1010 1011
  guint32 ayuv;

  br = video_box->border_right;
  bl = video_box->border_left;
  bt = video_box->border_top;
  bb = video_box->border_bottom;

  out_width = video_box->out_width;
1012
  out_height = video_box->out_height;
1013

1014 1015 1016
  src_stridey = GST_VIDEO_I420_Y_ROWSTRIDE (video_box->in_width);
  src_strideu = GST_VIDEO_I420_U_ROWSTRIDE (video_box->in_width);
  src_stridev = GST_VIDEO_I420_V_ROWSTRIDE (video_box->in_width);
1017

1018 1019
  crop_width = video_box->in_width;
  crop_width -= (video_box->crop_left + video_box->crop_right);
1020
  crop_width2 = crop_width / 2;
1021 1022
  crop_height = video_box->in_height;
  crop_height -= (video_box->crop_top + video_box->crop_bottom);
1023 1024 1025

  srcY =
      src + GST_VIDEO_I420_Y_OFFSET (video_box->in_width, video_box->in_height);
1026
  srcY += src_stridey * video_box->crop_top + video_box->crop_left;
1027 1028
  srcU =
      src + GST_VIDEO_I420_U_OFFSET (video_box->in_width, video_box->in_height);
1029
  srcU += src_strideu * (video_box->crop_top / 2) + (video_box->crop_left / 2);
1030 1031
  srcV =
      src + GST_VIDEO_I420_V_OFFSET (video_box->in_width, video_box->in_height);
1032
  srcV += src_stridev * (video_box->crop_top / 2) + (video_box->crop_left / 2);
1033 1034 1035 1036 1037 1038 1039 1040 1041 1042

  colorY = yuv_colors_Y[video_box->fill_type];
  colorU = yuv_colors_U[video_box->fill_type];
  colorV = yuv_colors_V[video_box->fill_type];

  ayuv =
      GUINT32_FROM_BE ((b_alpha << 24) | (colorY << 16) | (colorU << 8) |
      colorV);

  /* top border */
1043 1044 1045
  if (bt) {
    size_t nb_pixels = bt * out_width;

1046 1047
    oil_splat_u32_ns (destb, &ayuv, nb_pixels);
    destb += nb_pixels;
1048 1049
  }
  for (i = 0; i < crop_height; i++) {
1050
    destp = destb;
1051
    /* left border */
1052 1053 1054
    if (bl) {
      oil_splat_u32_ns (destp, &ayuv, bl);
      destp += bl;
1055 1056 1057
    }
    dest = (guint8 *) destp;
    /* center */
1058 1059
    /* We can splat the alpha channel for the whole line */
    oil_splat_u8 (dest, 4, &i_alpha, crop_width);
1060
    for (j = 0; j < crop_width2; j++) {
1061
      dest++;
1062 1063 1064
      *dest++ = *srcY++;
      *dest++ = *srcU;
      *dest++ = *srcV;
1065
      dest++;
1066 1067 1068 1069 1070 1071 1072 1073
      *dest++ = *srcY++;
      *dest++ = *srcU++;
      *dest++ = *srcV++;
    }
    if (i % 2 == 0) {
      srcU -= crop_width2;
      srcV -= crop_width2;
    } else {
1074 1075
      srcU += src_strideu - crop_width2;
      srcV += src_stridev - crop_width2;
1076
    }
1077
    srcY += src_stridey - (crop_width2 * 2);
1078 1079 1080

    destp = (guint32 *) dest;
    /* right border */
1081 1082
    if (br) {
      oil_splat_u32_ns (destp, &ayuv, br);
1083
    }
1084
    destb += out_width;
1085 1086
  }
  /* bottom border */
1087 1088 1089
  if (bb) {
    size_t nb_pixels = bb * out_width;

1090
    oil_splat_u32_ns (destb, &ayuv, nb_pixels);
1091 1092 1093
  }
}

1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132

static void
gst_video_box_i420_i420 (GstVideoBox * video_box, guint8 * src, guint8 * dest)
{
  guint8 *srcY, *srcU, *srcV;
  guint8 *destY, *destU, *destV;
  gint crop_width, crop_height;
  gint out_width, out_height;
  gint src_width, src_height;
  gint src_stride, dest_stride;
  gint br, bl, bt, bb;

  br = video_box->border_right;
  bl = video_box->border_left;
  bt = video_box->border_top;
  bb = video_box->border_bottom;

  out_width = video_box->out_width;
  out_height = video_box->out_height;

  src_width = video_box->in_width;
  src_height = video_box->in_height;

  crop_width = src_width - (video_box->crop_left + video_box->crop_right);
  crop_height = src_height - (video_box->crop_top + video_box->crop_bottom);

  /* Y plane */
  src_stride = GST_VIDEO_I420_Y_ROWSTRIDE (src_width);
  dest_stride = GST_VIDEO_I420_Y_ROWSTRIDE (out_width);

  destY = dest + GST_VIDEO_I420_Y_OFFSET (out_width, out_height);

  srcY = src + GST_VIDEO_I420_Y_OFFSET (src_width, src_height);
  srcY += src_stride * video_box->crop_top + video_box->crop_left;

  gst_video_box_copy_plane_i420 (video_box, srcY, destY, br, bl, bt, bb,
      crop_width, crop_height, src_stride, out_width, dest_stride,
      yuv_colors_Y[video_box->fill_type]);

1133 1134 1135 1136 1137 1138 1139 1140 1141
  br /= 2;
  bb /= 2;
  bl /= 2;
  bt /= 2;

  /* we need to round up to make sure we draw all the U and V lines */
  crop_width = (crop_width + 1) / 2;
  crop_height = (crop_height + 1) / 2;

1142 1143 1144 1145 1146 1147 1148 1149 1150
  /* U plane */
  src_stride = GST_VIDEO_I420_U_ROWSTRIDE (src_width);
  dest_stride = GST_VIDEO_I420_U_ROWSTRIDE (out_width);

  destU = dest + GST_VIDEO_I420_U_OFFSET (out_width, out_height);

  srcU = src + GST_VIDEO_I420_U_OFFSET (src_width, src_height);
  srcU += src_stride * (video_box->crop_top / 2) + (video_box->crop_left / 2);

1151 1152 1153
  gst_video_box_copy_plane_i420 (video_box, srcU, destU, br, bl, bt, bb,
      crop_width, crop_height, src_stride, out_width / 2, dest_stride,
      yuv_colors_U[video_box->fill_type]);
1154 1155 1156 1157 1158 1159 1160 1161 1162 1163

  /* V plane */
  src_stride = GST_VIDEO_I420_V_ROWSTRIDE (src_width);
  dest_stride = GST_VIDEO_I420_V_ROWSTRIDE (out_width);

  destV = dest + GST_VIDEO_I420_V_OFFSET (out_width, out_height);

  srcV = src + GST_VIDEO_I420_V_OFFSET (src_width, src_height);
  srcV += src_stride * (video_box->crop_top / 2) + (video_box->crop_left / 2);