gstvideobox.c 39 KB
Newer Older
1 2
/* GStreamer
 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3
 * Copyright (C) <2010> Sebastian Dröge <sebastian.droege@collabora.co.uk>
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
 *
 * 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.
 */
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
/**
 * 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..
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
 *
 * 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
 * ]|
54
 */
55

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

#include "gstvideobox.h"

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

66 67
#include <gst/controller/gstcontroller.h>

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

334
  video_box->box_left = crop_w / 2;
335 336 337 338 339 340 341 342
  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;
  }

343 344 345 346 347 348 349
  /* Round down/up for odd width differences */
  if (crop_w < 0)
    crop_w -= 1;
  else
    crop_w += 1;

  video_box->box_right = crop_w / 2;
350 351 352 353 354 355 356 357
  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;
  }

358
  video_box->box_top = crop_h / 2;
359 360 361 362 363 364 365 366
  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;
  }

367 368 369 370 371 372 373
  /* Round down/up for odd height differences */
  if (crop_h < 0)
    crop_h -= 1;
  else
    crop_h += 1;
  video_box->box_bottom = crop_h / 2;

374 375 376 377 378 379 380
  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;
  }
381
}
382

383 384 385 386
static void
gst_video_box_get_property (GObject * object, guint prop_id, GValue * value,
    GParamSpec * pspec)
{
387
  GstVideoBox *video_box = GST_VIDEO_BOX (object);
388 389

  switch (prop_id) {
390
    case PROP_LEFT:
391 392
      g_value_set_int (value, video_box->box_left);
      break;
393
    case PROP_RIGHT:
394 395
      g_value_set_int (value, video_box->box_right);
      break;
396
    case PROP_TOP:
397 398
      g_value_set_int (value, video_box->box_top);
      break;
399
    case PROP_BOTTOM:
400 401
      g_value_set_int (value, video_box->box_bottom);
      break;
402
    case PROP_FILL_TYPE:
403 404
      g_value_set_enum (value, video_box->fill_type);
      break;
405
    case PROP_ALPHA:
406 407
      g_value_set_double (value, video_box->alpha);
      break;
408
    case PROP_BORDER_ALPHA:
409 410
      g_value_set_double (value, video_box->border_alpha);
      break;
411 412 413
    case PROP_AUTOCROP:
      g_value_set_boolean (value, video_box->autocrop);
      break;
414 415 416 417 418 419
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

420
static GstCaps *
421 422
gst_video_box_transform_caps (GstBaseTransform * trans,
    GstPadDirection direction, GstCaps * from)
423
{
Sebastian Dröge's avatar
Sebastian Dröge committed
424
  GstVideoBox *video_box = GST_VIDEO_BOX (trans);
425 426
  GstCaps *to, *ret;
  const GstCaps *templ;
427
  GstStructure *structure;
428 429
  GstPad *other;
  gint width, height;
430

431
  to = gst_caps_copy (from);
432 433 434 435 436
  structure = gst_caps_get_structure (to, 0);

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

437 438 439 440
  /* otherwise caps nego will fail: */
  if (video_box->autocrop) {
    gst_structure_remove_field (structure, "width");
    gst_structure_remove_field (structure, "height");
441
  } else {
442 443 444 445 446 447 448 449 450 451 452 453 454 455
    /* 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);
456
    }
457

458 459 460 461 462 463 464 465
    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;
      }
466

467 468 469 470 471 472
      if (height <= 0)
        height = 1;

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

475 476 477 478 479 480
  /* 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);
481

482
  GST_DEBUG_OBJECT (video_box, "direction %d, transformed %" GST_PTR_FORMAT
483 484 485 486 487
      " to %" GST_PTR_FORMAT, direction, from, ret);

  return ret;
}

488 489 490 491 492 493
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
494
   * cropping at all, we can just operate in passthrough mode */
495
  if (video_box->in_format == video_box->out_format &&
496 497 498 499
      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
500 501
    gst_base_transform_set_passthrough (GST_BASE_TRANSFORM_CAST (video_box),
        TRUE);
502 503
  } else {
    GST_LOG_OBJECT (video_box, "we are not using passthrough");
Sebastian Dröge's avatar
Sebastian Dröge committed
504 505
    gst_base_transform_set_passthrough (GST_BASE_TRANSFORM_CAST (video_box),
        FALSE);
506 507 508 509
  }
  return res;
}

Wim Taymans's avatar
Wim Taymans committed
510
static gboolean
511
gst_video_box_set_caps (GstBaseTransform * trans, GstCaps * in, GstCaps * out)
512
{
Sebastian Dröge's avatar
Sebastian Dröge committed
513
  GstVideoBox *video_box = GST_VIDEO_BOX (trans);
514 515
  gboolean ret;

516 517
  g_mutex_lock (video_box->mutex);

518 519 520 521 522 523
  ret =
      gst_video_format_parse_caps (in, &video_box->in_format,
      &video_box->in_width, &video_box->in_height);
  ret &=
      gst_video_format_parse_caps (out, &video_box->out_format,
      &video_box->out_width, &video_box->out_height);
524

525 526 527 528
  /* something wrong getting the caps */
  if (!ret)
    goto no_caps;

529 530 531 532 533
  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);

534 535
  if (video_box->autocrop)
    gst_video_box_autocrop (video_box);
536

537 538
  /* recalc the transformation strategy */
  ret = video_box_recalc_transform (video_box);
539

540 541
  g_mutex_unlock (video_box->mutex);

Wim Taymans's avatar
Wim Taymans committed
542
  return ret;
543 544 545 546

  /* ERRORS */
no_caps:
  {
547 548
    GST_DEBUG_OBJECT (video_box,
        "Invalid caps: %" GST_PTR_FORMAT " -> %" GST_PTR_FORMAT, in, out);
549
    g_mutex_unlock (video_box->mutex);
550 551
    return FALSE;
  }
552 553
}

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

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

565 566 567 568
  ret = gst_video_format_parse_caps (caps, &format, &width, &height);
  if (!ret) {
    GST_ERROR_OBJECT (video_box, "Invalid caps: %" GST_PTR_FORMAT, caps);
    return FALSE;
Wim Taymans's avatar
Wim Taymans committed
569
  }
570

571 572
  *size = gst_video_format_get_size (format, width, height);

573 574
  GST_LOG_OBJECT (video_box, "Returning from _unit_size %d", *size);

575
  return TRUE;
Wim Taymans's avatar
Wim Taymans committed
576 577
}

578 579 580
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 };
581

582
static void
583
gst_video_box_copy_plane_i420 (GstVideoBox * video_box, const guint8 * src,
584 585 586 587 588 589 590 591
    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++) {
592
    oil_splat_u8_ns (dest, &fill_color, dest_width);
593 594 595 596 597
    dest += dest_stride;
  }

  /* copy and add left and right border */
  for (j = 0; j < src_crop_height; j++) {
598 599 600
    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);
601 602 603 604 605 606
    dest += dest_stride;
    src += src_stride;
  }

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

612
static void
613
gst_video_box_apply_alpha (guint8 * dest, guint8 alpha)
614
{
615 616 617
  if (dest[0] != 0)
    dest[0] = alpha;
}
618

619
static void
620 621
gst_video_box_ayuv_ayuv (GstVideoBox * video_box, const guint8 * src,
    guint8 * dest)
622 623 624
{
  gint dblen = video_box->out_height * video_box->out_width;
  guint32 *destb = (guint32 *) dest;
625
  const guint32 *srcb = (const guint32 *) src;
626 627 628 629 630 631 632
  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
633
  GST_LOG_OBJECT (video_box, "Processing AYUV -> AYUV data");
634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655

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

657 658 659 660 661 662 663 664 665
  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;
  }
666

Sebastian Dröge's avatar
Sebastian Dröge committed
667 668 669
  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);
670

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

676 677 678 679 680 681 682
    /* 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);
    }
683

684 685
    if (bl >= 0)
      src_loc += bl;
686

687 688
    for (i = 0; i < crop_h; i++) {
      gint j;
689

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

696 697
      /* Cropped area */
      oil_copy_u8 ((guint8 *) loc, (guint8 *) src_loc, crop_w * 4);
698

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

702 703
      src_loc += video_box->in_width;
      loc += crop_w;
704

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

712 713 714 715 716 717
    /* 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
718
  GST_LOG_OBJECT (video_box, "image created");
719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743
}

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
744 745
gst_video_box_ayuv_i420 (GstVideoBox * video_box, const guint8 * src,
    guint8 * dest)
746 747 748 749 750 751 752 753 754
{
  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
755
  GST_LOG_OBJECT (video_box, "AYUV to I420 conversion");
756 757 758 759 760 761 762 763 764

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

765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786
  Ywidth =
      gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 0,
      video_box->out_width);
  Uwidth =
      gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 1,
      video_box->out_width);
  Vwidth =
      gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 2,
      video_box->out_width);

  Ydest =
      dest + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 0,
      video_box->out_width, video_box->out_height);
  Udest =
      dest + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 1,
      video_box->out_width, video_box->out_height);
  Vdest =
      dest + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 2,
      video_box->out_width, video_box->out_height);

  Ysize =
      Ywidth * gst_video_format_get_component_height (GST_VIDEO_FORMAT_I420, 0,
787
      video_box->out_height);
788 789
  Usize =
      Ywidth * gst_video_format_get_component_height (GST_VIDEO_FORMAT_I420, 1,
790
      video_box->out_height);
791 792
  Vsize =
      Ywidth * gst_video_format_get_component_height (GST_VIDEO_FORMAT_I420, 2,
793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823
      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;
  }

824 825
  Utemp = g_malloc0 (Uwidth);
  Vtemp = g_malloc0 (Vwidth);
826

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

Sebastian Dröge's avatar
Sebastian Dröge committed
830
  GST_LOG_OBJECT (video_box, "Starting conversion");
831 832 833 834 835 836 837

  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;
838
    const guint32 *src_loc1;
839 840
    gint a = 0;

841
    src_loc1 = (const guint32 *) src;
842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866

    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
867 868 869 870 871
    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);
872 873 874
    for (i = 0; i < crop_h; i++) {
      a = 0;
      if (sumbuff) {
875
        /* left border */
876 877 878 879 880 881 882 883 884 885 886 887
        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++) {
888
          /* check ARCH */
889 890 891 892 893 894 895 896
          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;

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

946
        /* right border */
947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970
        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;
      }
    }

971
    /* bottom border */
972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998
    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
999
  GST_LOG_OBJECT (video_box, "image created");
1000 1001
  g_free (Utemp);
  g_free (Vtemp);
1002 1003 1004
}

static void
1005 1006
gst_video_box_i420_ayuv (GstVideoBox * video_box, const guint8 * src,
    guint8 * dest)
1007
{
1008
  const guint8 *srcY, *srcU, *srcV;
1009
  gint crop_width, crop_width2, crop_height;
1010
  gint out_width, out_height;
1011
  gint src_stridey, src_strideu, src_stridev;
1012 1013 1014 1015 1016
  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);
1017 1018
  guint32 *destp;
  guint32 *destb = (guint32 *) dest;
1019 1020 1021 1022 1023 1024 1025 1026
  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;
1027
  out_height = video_box->out_height;
1028

1029 1030 1031 1032 1033 1034 1035 1036 1037
  src_stridey =
      gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 0,
      video_box->in_width);
  src_strideu =
      gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 1,
      video_box->in_width);
  src_stridev =
      gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 2,
      video_box->in_width);
1038

1039 1040
  crop_width = video_box->in_width;
  crop_width -= (video_box->crop_left + video_box->crop_right);
1041
  crop_width2 = crop_width / 2;
1042 1043
  crop_height = video_box->in_height;
  crop_height -= (video_box->crop_top + video_box->crop_bottom);
1044 1045

  srcY =
1046 1047
      src + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 0,
      video_box->in_width, video_box->in_height);
1048
  srcY += src_stridey * video_box->crop_top + video_box->crop_left;
1049
  srcU =
1050 1051
      src + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 1,
      video_box->in_width, video_box->in_height);
1052
  srcU += src_strideu * (video_box->crop_top / 2) + (video_box->crop_left / 2);
1053
  srcV =
1054 1055
      src + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 2,
      video_box->in_width, video_box->in_height);
1056
  srcV += src_stridev * (video_box->crop_top / 2) + (video_box->crop_left / 2);
1057 1058 1059 1060 1061 1062 1063 1064 1065 1066

  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 */
1067 1068 1069
  if (bt) {
    size_t nb_pixels = bt * out_width;

1070 1071
    oil_splat_u32_ns (destb, &ayuv, nb_pixels);
    destb += nb_pixels;
1072 1073
  }
  for (i = 0; i < crop_height; i++) {
1074
    destp = destb;
1075
    /* left border */
1076 1077 1078
    if (bl) {
      oil_splat_u32_ns (destp, &ayuv, bl);
      destp += bl;
1079 1080 1081
    }
    dest = (guint8 *) destp;
    /* center */
1082 1083
    /* We can splat the alpha channel for the whole line */
    oil_splat_u8 (dest, 4, &i_alpha, crop_width);
1084
    for (j = 0; j < crop_width2; j++) {
1085
      dest++;
1086 1087 1088
      *dest++ = *srcY++;
      *dest++ = *srcU;
      *dest++ = *srcV;
1089
      dest++;
1090 1091 1092 1093 1094 1095 1096 1097
      *dest++ = *srcY++;
      *dest++ = *srcU++;
      *dest++ = *srcV++;
    }
    if (i % 2 == 0) {
      srcU -= crop_width2;
      srcV -= crop_width2;
    } else {
1098 1099
      srcU += src_strideu - crop_width2;
      srcV += src_stridev - crop_width2;
1100
    }
1101
    srcY += src_stridey - (crop_width2 * 2);
1102 1103 1104

    destp = (guint32 *) dest;
    /* right border */
1105 1106
    if (br) {
      oil_splat_u32_ns (destp, &ayuv, br);
1107
    }
1108
    destb += out_width;
1109 1110
  }
  /* bottom border */
1111 1112 1113
  if (bb) {
    size_t nb_pixels = bb * out_width;

1114
    oil_splat_u32_ns (destb, &ayuv, nb_pixels);
1115 1116 1117
  }
}

1118 1119

static void
1120 1121
gst_video_box_i420_i420 (GstVideoBox * video_box, const guint8 * src,
    guint8 * dest)
1122
{
1123
  const guint8 *srcY, *srcU, *srcV;
1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145
  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 */
1146 1147 1148 1149
  src_stride =
      gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 0, src_width);
  dest_stride =
      gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 0, out_width);
1150

1151 1152 1153
  destY =
      dest + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 0,
      out_width, out_height);
1154

1155 1156 1157
  srcY =
      src + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 0,
      src_width, src_height);
1158 1159 1160 1161 1162 1163
  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]);

1164 1165 1166 1167 1168 1169 1170 1171 1172
  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;

1173
  /* U plane */
1174 1175 1176 1177
  src_stride =
      gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 1, src_width);
  dest_stride =
      gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 1, out_width);
1178

1179 1180 1181
  destU =
      dest + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 1,
      out_width, out_height);
1182

1183 1184 1185
  srcU =
      src + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 1,
      src_width, src_height);
1186 1187
  srcU += src_stride * (video_box->crop_top / 2) + (video_box->crop_left / 2);

1188 1189 1190
  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]);
1191 1192

  /* V plane */
1193 1194 1195 1196
  src_stride =
      gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 2, src_width);
  dest_stride =
      gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 2, out_width);
1197

1198 1199 1200
  destV =
      dest + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 2,
      out_width, out_height);
1201

1202 1203 1204
  srcV =
      src + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 2,
      src_width, src_height);
1205 1206
  srcV += src_stride * (video_box->crop_top / 2) + (video_box->crop_left / 2);

1207 1208 1209
  gst_video_box_copy_plane_i420 (video_box, srcV, destV, br, bl, bt, bb,
      crop_width, crop_height, src_stride, out_width / 2, dest_stride,
      yuv_colors_V[video_box->fill_type]);
1210 1211
}

Wim Taymans's avatar
Wim Taymans committed
1212
static GstFlowReturn
1213
gst_video_box_transform (GstBaseTransform * trans, GstBuffer * in,
Wim Taymans's avatar
Wim Taymans committed
1214
    GstBuffer * out)
1215
{
Sebastian Dröge's avatar
Sebastian Dröge committed
1216
  GstVideoBox *video_box = GST_VIDEO_BOX (trans);
1217 1218
  const guint8 *indata;
  guint8 *outdata;
1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229
  GstClockTime timestamp, stream_time;

  timestamp = GST_BUFFER_TIMESTAMP (in);
  stream_time =
      gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME, timestamp);

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

  if (GST_CLOCK_TIME_IS_VALID (stream_time))
    gst_object_sync_values (G_OBJECT (video_box), stream_time);
1230

1231 1232
  indata = GST_BUFFER_DATA (in);
  outdata = GST_BUFFER_DATA (out);
1233

1234
  g_mutex_lock (video_box->mutex);
1235 1236 1237 1238
  switch (video_box->in_format) {
    case GST_VIDEO_FORMAT_AYUV:
      switch (video_box->out_format) {
        case GST_VIDEO_FORMAT_AYUV: