gstvideobox.c 38.7 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 331 332 333
}

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;

334
  g_mutex_lock (video_box->mutex);
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 370
  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;
  }

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

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

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

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

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

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

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

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

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

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

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

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

  return ret;
}

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

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

507 508 509 510 511 512
  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);
513

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

518 519 520 521 522
  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);

523 524
  if (video_box->autocrop)
    gst_video_box_autocrop (video_box);
525

526 527
  /* recalc the transformation strategy */
  ret = video_box_recalc_transform (video_box);
528

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

  /* ERRORS */
no_caps:
  {
534 535
    GST_DEBUG_OBJECT (video_box,
        "Invalid caps: %" GST_PTR_FORMAT " -> %" GST_PTR_FORMAT, in, out);
536 537
    return FALSE;
  }
538 539
}

540 541 542
static gboolean
gst_video_box_get_unit_size (GstBaseTransform * trans, GstCaps * caps,
    guint * size)
Wim Taymans's avatar
Wim Taymans committed
543
{
Sebastian Dröge's avatar
Sebastian Dröge committed
544
  GstVideoBox *video_box = GST_VIDEO_BOX (trans);
545
  GstVideoFormat format;
546
  gint width, height;
547
  gboolean ret;
Wim Taymans's avatar
Wim Taymans committed
548

Stefan Kost's avatar
Stefan Kost committed
549 550
  g_assert (size);

551 552 553 554
  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
555
  }
556

557 558
  *size = gst_video_format_get_size (format, width, height);

559 560
  GST_LOG_OBJECT (video_box, "Returning from _unit_size %d", *size);

561
  return TRUE;
Wim Taymans's avatar
Wim Taymans committed
562 563
}

564 565 566
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 };
567

568 569 570 571 572 573 574 575 576 577
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++) {
578
    oil_splat_u8_ns (dest, &fill_color, dest_width);
579 580 581 582 583
    dest += dest_stride;
  }

  /* copy and add left and right border */
  for (j = 0; j < src_crop_height; j++) {
584 585 586
    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);
587 588 589 590 591 592
    dest += dest_stride;
    src += src_stride;
  }

  /* bottom border */
  for (j = 0; j < bb; j++) {
593
    oil_splat_u8_ns (dest, &fill_color, dest_width);
594 595 596 597
    dest += dest_stride;
  }
}

598
static void
599
gst_video_box_apply_alpha (guint8 * dest, guint8 alpha)
600
{
601 602 603
  if (dest[0] != 0)
    dest[0] = alpha;
}
604

605 606 607 608 609 610 611 612 613 614 615 616 617
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
618
  GST_LOG_OBJECT (video_box, "Processing AYUV -> AYUV data");
619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640

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

642 643 644 645 646 647 648 649 650
  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;
  }
651

Sebastian Dröge's avatar
Sebastian Dröge committed
652 653 654
  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);
655

656 657 658 659
  if (crop_h <= 0 || crop_w <= 0) {
    oil_splat_u32_ns (destb, &empty_pixel, dblen);
  } else {
    guint32 *src_loc = srcb;
660

661 662 663 664 665 666 667
    /* 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);
    }
668

669 670
    if (bl >= 0)
      src_loc += bl;
671

672 673
    for (i = 0; i < crop_h; i++) {
      gint j;
674

675 676 677 678 679
      /* Left border */
      if (bl < 0) {
        oil_splat_u32_ns (loc, &empty_pixel, -bl);
        loc += (-bl);
      }
680

681 682
      /* Cropped area */
      oil_copy_u8 ((guint8 *) loc, (guint8 *) src_loc, crop_w * 4);
683

684 685
      for (j = 0; j < crop_w; j++)
        gst_video_box_apply_alpha ((guint8 *) & loc[j], i_alpha);
686

687 688
      src_loc += video_box->in_width;
      loc += crop_w;
689

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

697 698 699 700 701 702
    /* 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
703
  GST_LOG_OBJECT (video_box, "image created");
704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738
}

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
739
  GST_LOG_OBJECT (video_box, "AYUV to I420 conversion");
740 741 742 743 744 745 746 747 748

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

749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770
  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,
771
      video_box->out_height);
772 773
  Usize =
      Ywidth * gst_video_format_get_component_height (GST_VIDEO_FORMAT_I420, 1,
774
      video_box->out_height);
775 776
  Vsize =
      Ywidth * gst_video_format_get_component_height (GST_VIDEO_FORMAT_I420, 2,
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
      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;
  }

808 809
  Utemp = g_malloc0 (Uwidth);
  Vtemp = g_malloc0 (Vwidth);
810

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

Sebastian Dröge's avatar
Sebastian Dröge committed
814
  GST_LOG_OBJECT (video_box, "Starting conversion");
815 816 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

  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
851 852 853 854 855
    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);
856 857 858
    for (i = 0; i < crop_h; i++) {
      a = 0;
      if (sumbuff) {
859
        /* left border */
860 861 862 863 864 865 866 867 868 869 870 871
        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++) {
872
          /* check ARCH */
873 874 875 876 877 878 879 880
          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;

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

930
        /* right border */
931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954
        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;
      }
    }

955
    /* bottom border */
956 957 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
    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
983
  GST_LOG_OBJECT (video_box, "image created");
984 985
  g_free (Utemp);
  g_free (Vtemp);
986 987 988
}

static void
989
gst_video_box_i420_ayuv (GstVideoBox * video_box, guint8 * src, guint8 * dest)
990 991 992
{
  guint8 *srcY, *srcU, *srcV;
  gint crop_width, crop_width2, crop_height;
993
  gint out_width, out_height;
994
  gint src_stridey, src_strideu, src_stridev;
995 996 997 998 999
  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);
1000 1001
  guint32 *destp;
  guint32 *destb = (guint32 *) dest;
1002 1003 1004 1005 1006 1007 1008 1009
  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;
1010
  out_height = video_box->out_height;
1011

1012 1013 1014 1015 1016 1017 1018 1019 1020
  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);
1021

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

  srcY =
1029 1030
      src + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 0,
      video_box->in_width, video_box->in_height);
1031
  srcY += src_stridey * video_box->crop_top + video_box->crop_left;
1032
  srcU =
1033 1034
      src + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 1,
      video_box->in_width, video_box->in_height);
1035
  srcU += src_strideu * (video_box->crop_top / 2) + (video_box->crop_left / 2);
1036
  srcV =
1037 1038
      src + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 2,
      video_box->in_width, video_box->in_height);
1039
  srcV += src_stridev * (video_box->crop_top / 2) + (video_box->crop_left / 2);
1040 1041 1042 1043 1044 1045 1046 1047 1048 1049

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

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

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

1097
    oil_splat_u32_ns (destb, &ayuv, nb_pixels);
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

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 */
1128 1129 1130 1131
  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);
1132

1133 1134 1135
  destY =
      dest + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 0,
      out_width, out_height);
1136

1137 1138 1139
  srcY =
      src + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 0,
      src_width, src_height);
1140 1141 1142 1143 1144 1145
  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]);

1146 1147 1148 1149 1150 1151 1152 1153 1154
  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;

1155
  /* U plane */
1156 1157 1158 1159
  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);
1160

1161 1162 1163
  destU =
      dest + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 1,
      out_width, out_height);
1164

1165 1166 1167
  srcU =
      src + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 1,
      src_width, src_height);
1168 1169
  srcU += src_stride * (video_box->crop_top / 2) + (video_box->crop_left / 2);

1170 1171 1172
  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]);
1173 1174

  /* V plane */
1175 1176 1177 1178
  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);
1179

1180 1181 1182
  destV =
      dest + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 2,
      out_width, out_height);
1183

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

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

Wim Taymans's avatar
Wim Taymans committed
1194
static GstFlowReturn
1195
gst_video_box_transform (GstBaseTransform * trans, GstBuffer * in,
Wim Taymans's avatar
Wim Taymans committed
1196
    GstBuffer * out)
1197
{
Sebastian Dröge's avatar
Sebastian Dröge committed
1198
  GstVideoBox *video_box = GST_VIDEO_BOX (trans);
1199
  guint8 *indata, *outdata;
1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210
  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);
1211

1212 1213
  indata = GST_BUFFER_DATA (in);
  outdata = GST_BUFFER_DATA (out);
1214

1215
  g_mutex_lock (video_box->mutex);
1216 1217 1218 1219
  switch (video_box->in_format) {
    case GST_VIDEO_FORMAT_AYUV:
      switch (video_box->out_format) {
        case GST_VIDEO_FORMAT_AYUV:
1220 1221
          gst_video_box_ayuv_ayuv (video_box, indata, outdata);
          break;
1222
        case GST_VIDEO_FORMAT_I420:
1223 1224 1225 1226 1227 1228
          gst_video_box_ayuv_i420 (video_box, indata, outdata);
          break;
        default:
          goto invalid_format;
      }
      break;
1229 1230 1231
    case GST_VIDEO_FORMAT_I420:
      switch (video_box->out_format) {
        case GST_VIDEO_FORMAT_AYUV:
1232 1233
          gst_video_box_i420_ayuv (video_box, indata, outdata);
          break;
1234
        case GST_VIDEO_FORMAT_I420:
1235 1236 1237 1238 1239 1240 1241 1242 1243
          gst_video_box_i420_i420 (video_box, indata, outdata);
          break;
        default:
          goto invalid_format;
      }
      break;
    default:
      goto invalid_format;
  }
1244
  g_mutex_unlock (video_box->mutex);
Wim Taymans's avatar
Wim Taymans committed
1245
  return GST_FLOW_OK;
1246 1247 1248 1249

  /* ERRORS */
invalid_format:
  {
1250
    g_mutex_unlock (video_box->mutex);
1251 1252
    return GST_FLOW_ERROR;
  }
1253 1254
}

1255
/* FIXME: 0.11 merge with videocrop plugin */
1256 1257 1258
static gboolean
plugin_init (GstPlugin * plugin)
{
1259 1260
  oil_init ();

1261 1262
  gst_controller_init (NULL, NULL);

Sebastian Dröge's avatar
Sebastian Dröge committed
1263 1264 1265
  GST_DEBUG_CATEGORY_INIT (videobox_debug, "videobox", 0,
      "Resizes a video by adding borders or cropping");

1266 1267 1268 1269 1270 1271 1272 1273
  return gst_element_register (plugin, "videobox", GST_RANK_NONE,
      GST_TYPE_VIDEO_BOX);
}

GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
    GST_VERSION_MINOR,
    "videobox",
    "resizes a video by adding borders or cropping",
1274
    plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)