gstvideobox.c 38.7 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 66
#include <gst/controller/gstcontroller.h>

67
GST_DEBUG_CATEGORY_STATIC (videobox_debug);
68 69
#define GST_CAT_DEFAULT videobox_debug

70 71 72 73 74 75 76 77 78 79
#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
{
80 81 82 83 84 85 86
  PROP_0,
  PROP_LEFT,
  PROP_RIGHT,
  PROP_TOP,
  PROP_BOTTOM,
  PROP_FILL_TYPE,
  PROP_ALPHA,
87 88
  PROP_BORDER_ALPHA,
  PROP_AUTOCROP
89
      /* FILL ME */
90 91 92
};

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

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

108 109
GST_BOILERPLATE (GstVideoBox, gst_video_box, GstBaseTransform,
    GST_TYPE_BASE_TRANSFORM);
110 111 112 113 114 115

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);

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

#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;
131
  static const GEnumValue video_box_fill[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
132 133 134
    {VIDEO_BOX_FILL_BLACK, "Black", "black"},
    {VIDEO_BOX_FILL_GREEN, "Colorkey green", "green"},
    {VIDEO_BOX_FILL_BLUE, "Colorkey blue", "blue"},
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
    {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
151 152 153 154
  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>");
155 156 157 158 159 160

  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));
}
161

162 163 164 165 166 167 168 169 170 171 172 173 174
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);
}

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

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

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

229
  trans_class->transform = GST_DEBUG_FUNCPTR (gst_video_box_transform);
230 231 232 233
  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);
234 235 236
}

static void
237
gst_video_box_init (GstVideoBox * video_box, GstVideoBoxClass * g_class)
238 239 240 241 242
{
  video_box->box_right = DEFAULT_RIGHT;
  video_box->box_left = DEFAULT_LEFT;
  video_box->box_top = DEFAULT_TOP;
  video_box->box_bottom = DEFAULT_BOTTOM;
243 244 245 246
  video_box->crop_right = 0;
  video_box->crop_left = 0;
  video_box->crop_top = 0;
  video_box->crop_bottom = 0;
247 248 249
  video_box->fill_type = DEFAULT_FILL_TYPE;
  video_box->alpha = DEFAULT_ALPHA;
  video_box->border_alpha = DEFAULT_BORDER_ALPHA;
250
  video_box->autocrop = FALSE;
251 252

  video_box->mutex = g_mutex_new ();
253 254 255 256 257 258
}

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

261
  g_mutex_lock (video_box->mutex);
262
  switch (prop_id) {
263
    case PROP_LEFT:
264 265 266 267 268 269 270 271 272
      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;
273
    case PROP_RIGHT:
274 275 276 277 278 279 280 281 282
      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;
283
    case PROP_TOP:
284 285 286 287 288 289 290 291 292
      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;
293
    case PROP_BOTTOM:
294 295 296 297 298 299 300 301 302
      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;
303
    case PROP_FILL_TYPE:
304 305
      video_box->fill_type = g_value_get_enum (value);
      break;
306
    case PROP_ALPHA:
307 308
      video_box->alpha = g_value_get_double (value);
      break;
309
    case PROP_BORDER_ALPHA:
310 311
      video_box->border_alpha = g_value_get_double (value);
      break;
312 313 314
    case PROP_AUTOCROP:
      video_box->autocrop = g_value_get_boolean (value);
      break;
315 316 317 318
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
319
  video_box_recalc_transform (video_box);
320 321

  GST_DEBUG_OBJECT (video_box, "Calling reconfigure");
Sebastian Dröge's avatar
Sebastian Dröge committed
322
  gst_base_transform_reconfigure (GST_BASE_TRANSFORM_CAST (video_box));
323 324

  g_mutex_unlock (video_box->mutex);
325 326 327 328 329 330 331 332
}

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;

333
  g_mutex_lock (video_box->mutex);
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 363 364 365 366 367 368 369
  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;
  }

370
  g_mutex_unlock (video_box->mutex);
371
}
372

373 374 375 376
static void
gst_video_box_get_property (GObject * object, guint prop_id, GValue * value,
    GParamSpec * pspec)
{
377
  GstVideoBox *video_box = GST_VIDEO_BOX (object);
378 379

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

410
static GstCaps *
411 412
gst_video_box_transform_caps (GstBaseTransform * trans,
    GstPadDirection direction, GstCaps * from)
413
{
Sebastian Dröge's avatar
Sebastian Dröge committed
414
  GstVideoBox *video_box = GST_VIDEO_BOX (trans);
415 416
  GstCaps *to, *ret;
  const GstCaps *templ;
417
  GstStructure *structure;
418 419
  GstPad *other;
  gint width, height;
420

421
  to = gst_caps_copy (from);
422 423 424 425 426
  structure = gst_caps_get_structure (to, 0);

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

427 428 429 430
  /* otherwise caps nego will fail: */
  if (video_box->autocrop) {
    gst_structure_remove_field (structure, "width");
    gst_structure_remove_field (structure, "height");
431
  } else {
432 433 434 435 436 437 438 439 440 441 442 443 444 445
    /* 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);
446
    }
447

448 449 450 451 452 453 454 455
    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;
      }
456

457 458 459 460 461 462
      if (height <= 0)
        height = 1;

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

465 466 467 468 469 470
  /* 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);
471

472
  GST_DEBUG_OBJECT (video_box, "direction %d, transformed %" GST_PTR_FORMAT
473 474 475 476 477
      " to %" GST_PTR_FORMAT, direction, from, ret);

  return ret;
}

478 479 480 481 482 483
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
484
   * cropping at all, we can just operate in passthrough mode */
485 486 487 488 489
  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
490 491
    gst_base_transform_set_passthrough (GST_BASE_TRANSFORM_CAST (video_box),
        TRUE);
492 493
  } else {
    GST_LOG_OBJECT (video_box, "we are not using passthrough");
Sebastian Dröge's avatar
Sebastian Dröge committed
494 495
    gst_base_transform_set_passthrough (GST_BASE_TRANSFORM_CAST (video_box),
        FALSE);
496 497 498 499
  }
  return res;
}

Wim Taymans's avatar
Wim Taymans committed
500
static gboolean
501
gst_video_box_set_caps (GstBaseTransform * trans, GstCaps * in, GstCaps * out)
502
{
Sebastian Dröge's avatar
Sebastian Dröge committed
503
  GstVideoBox *video_box = GST_VIDEO_BOX (trans);
504 505 506
  GstStructure *structure;
  gboolean ret;

507
  structure = gst_caps_get_structure (in, 0);
508 509
  ret = gst_structure_get_int (structure, "width", &video_box->in_width);
  ret &= gst_structure_get_int (structure, "height", &video_box->in_height);
510
  ret &= gst_structure_get_fourcc (structure, "format", &video_box->in_fourcc);
511

512 513 514
  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);
515
  ret &= gst_structure_get_fourcc (structure, "format", &video_box->out_fourcc);
516

517 518 519 520
  /* something wrong getting the caps */
  if (!ret)
    goto no_caps;

521 522 523 524 525
  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);

526 527
  if (video_box->autocrop)
    gst_video_box_autocrop (video_box);
528

529 530
  /* recalc the transformation strategy */
  ret = video_box_recalc_transform (video_box);
531

Wim Taymans's avatar
Wim Taymans committed
532
  return ret;
533 534 535 536 537 538 539

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

542
/* see gst-plugins/gst/games/gstvideoimage.c, paint_setup_I420() */
543 544 545
#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)
546

547
#define GST_VIDEO_I420_Y_OFFSET(w,h) (0)
548 549
#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))
550

551
#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))
552

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

Stefan Kost's avatar
Stefan Kost committed
562 563
  g_assert (size);

564 565 566 567 568 569 570 571 572 573 574 575 576 577 578
  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
579
  }
580

581 582
  GST_LOG_OBJECT (video_box, "Returning from _unit_size %d", *size);

583
  return TRUE;
Wim Taymans's avatar
Wim Taymans committed
584 585
}

586 587 588
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 };
589

590 591 592 593 594 595 596 597 598 599
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++) {
600
    oil_splat_u8_ns (dest, &fill_color, dest_width);
601 602 603 604 605
    dest += dest_stride;
  }

  /* copy and add left and right border */
  for (j = 0; j < src_crop_height; j++) {
606 607 608
    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);
609 610 611 612 613 614
    dest += dest_stride;
    src += src_stride;
  }

  /* bottom border */
  for (j = 0; j < bb; j++) {
615
    oil_splat_u8_ns (dest, &fill_color, dest_width);
616 617 618 619
    dest += dest_stride;
  }
}

620
static void
621
gst_video_box_apply_alpha (guint8 * dest, guint8 alpha)
622
{
623 624 625
  if (dest[0] != 0)
    dest[0] = alpha;
}
626

627 628 629 630 631 632 633 634 635 636 637 638 639
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
640
  GST_LOG_OBJECT (video_box, "Processing AYUV -> AYUV data");
641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662

  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;
  }
663

664 665 666 667 668 669 670 671 672
  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;
  }
673

Sebastian Dröge's avatar
Sebastian Dröge committed
674 675 676
  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);
677

678 679 680 681
  if (crop_h <= 0 || crop_w <= 0) {
    oil_splat_u32_ns (destb, &empty_pixel, dblen);
  } else {
    guint32 *src_loc = srcb;
682

683 684 685 686 687 688 689
    /* 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);
    }
690

691 692
    if (bl >= 0)
      src_loc += bl;
693

694 695
    for (i = 0; i < crop_h; i++) {
      gint j;
696

697 698 699 700 701
      /* Left border */
      if (bl < 0) {
        oil_splat_u32_ns (loc, &empty_pixel, -bl);
        loc += (-bl);
      }
702

703 704
      /* Cropped area */
      oil_copy_u8 ((guint8 *) loc, (guint8 *) src_loc, crop_w * 4);
705

706 707
      for (j = 0; j < crop_w; j++)
        gst_video_box_apply_alpha ((guint8 *) & loc[j], i_alpha);
708

709 710
      src_loc += video_box->in_width;
      loc += crop_w;
711

712 713 714 715 716 717
      /* Right border */
      if (br < 0) {
        oil_splat_u32_ns (loc, &empty_pixel, -br);
        loc += (-br);
      }
    }
718

719 720 721 722 723 724
    /* 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
725
  GST_LOG_OBJECT (video_box, "image created");
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 756 757 758 759 760
}

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
761
  GST_LOG_OBJECT (video_box, "AYUV to I420 conversion");
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 810 811 812 813 814

  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;
  }

815 816
  Utemp = g_malloc0 (Uwidth);
  Vtemp = g_malloc0 (Vwidth);
817

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

Sebastian Dröge's avatar
Sebastian Dröge committed
821
  GST_LOG_OBJECT (video_box, "Starting conversion");
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 853 854 855 856 857

  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
858 859 860 861 862
    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);
863 864 865
    for (i = 0; i < crop_h; i++) {
      a = 0;
      if (sumbuff) {
866
        /* left border */
867 868 869 870 871 872 873 874 875 876 877 878
        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++) {
879
          /* check ARCH */
880 881 882 883 884 885 886 887
          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;

888
        /* right border */
889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908
        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 {
909
        /* left border */
910 911 912 913 914 915 916 917 918 919 920 921
        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++) {
922
          /* check ARCH */
923 924 925 926 927 928 929 930
          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 {
931 932
            Utemp[UVfloor (a + j)] = ((guint8 *) & src_loc1[j])[2];
            Vtemp[UVfloor (a + j)] = ((guint8 *) & src_loc1[j])[3];
933 934 935 936
          }
        }
        Ydest += crop_w;

937
        /* right border */
938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961
        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;
      }
    }

962
    /* bottom border */
963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989
    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
990
  GST_LOG_OBJECT (video_box, "image created");
991 992
  g_free (Utemp);
  g_free (Vtemp);
993 994 995
}

static void
996
gst_video_box_i420_ayuv (GstVideoBox * video_box, guint8 * src, guint8 * dest)
997 998 999
{
  guint8 *srcY, *srcU, *srcV;
  gint crop_width, crop_width2, crop_height;
1000
  gint out_width, out_height;
1001
  gint src_stridey, src_strideu, src_stridev;
1002 1003 1004 1005 1006
  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);
1007 1008
  guint32 *destp;
  guint32 *destb = (guint32 *) dest;
1009 1010 1011 1012 1013 1014 1015 1016
  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;
1017
  out_height = video_box->out_height;
1018

1019 1020 1021
  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);
1022

1023 1024
  crop_width = video_box->in_width;
  crop_width -= (video_box->crop_left + video_box->crop_right);
1025
  crop_width2 = crop_width / 2;
1026 1027
  crop_height = video_box->in_height;
  crop_height -= (video_box->crop_top + video_box->crop_bottom);
1028 1029 1030

  srcY =
      src + GST_VIDEO_I420_Y_OFFSET (video_box->in_width, video_box->in_height);
1031
  srcY += src_stridey * video_box->crop_top + video_box->crop_left;
1032 1033
  srcU =
      src + GST_VIDEO_I420_U_OFFSET (video_box->in_width, video_box->in_height);
1034
  srcU += src_strideu * (video_box->crop_top / 2) + (video_box->crop_left / 2);
1035 1036
  srcV =
      src + GST_VIDEO_I420_V_OFFSET (video_box->in_width, video_box->in_height);
1037
  srcV += src_stridev * (video_box->crop_top / 2) + (video_box->crop_left / 2);
1038 1039 1040 1041 1042 1043 1044 1045 1046 1047

  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 */
1048 1049 1050
  if (bt) {
    size_t nb_pixels = bt * out_width;

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

    destp = (guint32 *) dest;
    /* right border */
1086 1087
    if (br) {
      oil_splat_u32_ns (destp, &ayuv, br);
1088
    }
1089
    destb += out_width;
1090 1091
  }
  /* bottom border */
1092 1093 1094
  if (bb) {
    size_t nb_pixels = bb * out_width;

1095
    oil_splat_u32_ns (destb, &ayuv, nb_pixels);
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 1133 1134 1135 1136 1137

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]);

1138 1139 1140 1141 1142 1143 1144 1145 1146
  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;

1147 1148 1149 1150 1151 1152 1153 1154 1155
  /* 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);

1156 1157 1158
  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]);
1159 1160 1161