gstvideobox.c 21.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/* 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.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gst/gst.h>
24
#include <gst/base/gstbasetransform.h>
25 26
#include <gst/video/video.h>

27
#include <liboil/liboil.h>
28 29
#include <string.h>

30 31 32
GST_DEBUG_CATEGORY (videobox_debug);
#define GST_CAT_DEFAULT videobox_debug

33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
#define GST_TYPE_VIDEO_BOX \
  (gst_video_box_get_type())
#define GST_VIDEO_BOX(obj) \
  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VIDEO_BOX,GstVideoBox))
#define GST_VIDEO_BOX_CLASS(klass) \
  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VIDEO_BOX,GstVideoBoxClass))
#define GST_IS_VIDEO_BOX(obj) \
  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VIDEO_BOX))
#define GST_IS_VIDEO_BOX_CLASS(obj) \
  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VIDEO_BOX))

typedef struct _GstVideoBox GstVideoBox;
typedef struct _GstVideoBoxClass GstVideoBoxClass;

typedef enum
{
  VIDEO_BOX_FILL_BLACK,
  VIDEO_BOX_FILL_GREEN,
  VIDEO_BOX_FILL_BLUE,
}
GstVideoBoxFill;

struct _GstVideoBox
{
57
  GstBaseTransform element;
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76

  /* caps */
  gint in_width, in_height;
  gint out_width, out_height;

  gint box_left, box_right, box_top, box_bottom;

  gint border_left, border_right, border_top, border_bottom;
  gint crop_left, crop_right, crop_top, crop_bottom;

  gboolean use_alpha;
  gdouble alpha;
  gdouble border_alpha;

  GstVideoBoxFill fill_type;
};

struct _GstVideoBoxClass
{
77
  GstBaseTransformClass parent_class;
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
};

/* elementfactory information */
static GstElementDetails gst_video_box_details =
GST_ELEMENT_DETAILS ("video box filter",
    "Filter/Effect/Video",
    "Resizes a video by adding borders or cropping",
    "Wim Taymans <wim@fluendo.com>");


#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
{
98 99 100 101 102 103 104 105
  PROP_0,
  PROP_LEFT,
  PROP_RIGHT,
  PROP_TOP,
  PROP_BOTTOM,
  PROP_FILL_TYPE,
  PROP_ALPHA,
  PROP_BORDER_ALPHA,
106 107 108 109 110 111 112
  /* FILL ME */
};

static GstStaticPadTemplate gst_video_box_src_template =
GST_STATIC_PAD_TEMPLATE ("src",
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
113
    GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("{ AYUV, I420 }"))
114 115 116 117 118 119 120 121 122 123
    );

static GstStaticPadTemplate gst_video_box_sink_template =
GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420"))
    );


124 125
GST_BOILERPLATE (GstVideoBox, gst_video_box, GstBaseTransform,
    GST_TYPE_BASE_TRANSFORM);
126 127 128 129 130 131

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

132
static GstCaps *gst_video_box_transform_caps (GstBaseTransform * trans,
133
    GstPadDirection direction, GstCaps * from);
134 135
static gboolean gst_video_box_set_caps (GstBaseTransform * trans,
    GstCaps * in, GstCaps * out);
136 137
static gboolean gst_video_box_get_unit_size (GstBaseTransform * trans,
    GstCaps * caps, guint * size);
138
static GstFlowReturn gst_video_box_transform (GstBaseTransform * trans,
Wim Taymans's avatar
Wim Taymans committed
139
    GstBuffer * in, GstBuffer * out);
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173


#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;
  static GEnumValue video_box_fill[] = {
    {VIDEO_BOX_FILL_BLACK, "0", "Black"},
    {VIDEO_BOX_FILL_GREEN, "1", "Colorkey green"},
    {VIDEO_BOX_FILL_BLUE, "2", "Colorkey blue"},
    {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);

  gst_element_class_set_details (element_class, &gst_video_box_details);

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

175 176 177 178
static void
gst_video_box_class_init (GstVideoBoxClass * klass)
{
  GObjectClass *gobject_class;
179
  GstBaseTransformClass *trans_class;
180 181

  gobject_class = (GObjectClass *) klass;
182
  trans_class = (GstBaseTransformClass *) klass;
183

Wim Taymans's avatar
Wim Taymans committed
184 185 186
  gobject_class->set_property = gst_video_box_set_property;
  gobject_class->get_property = gst_video_box_get_property;

187
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FILL_TYPE,
188 189 190
      g_param_spec_enum ("fill", "Fill", "How to fill the borders",
          GST_TYPE_VIDEO_BOX_FILL, DEFAULT_FILL_TYPE,
          (GParamFlags) G_PARAM_READWRITE));
191
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LEFT,
192 193 194
      g_param_spec_int ("left", "Left",
          "Pixels to box at left (<0  = add a border)", G_MININT, G_MAXINT,
          DEFAULT_LEFT, G_PARAM_READWRITE));
195
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_RIGHT,
196 197 198
      g_param_spec_int ("right", "Right",
          "Pixels to box at right (<0 = add a border)", G_MININT, G_MAXINT,
          DEFAULT_RIGHT, G_PARAM_READWRITE));
199
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TOP,
200 201 202
      g_param_spec_int ("top", "Top",
          "Pixels to box at top (<0 = add a border)", G_MININT, G_MAXINT,
          DEFAULT_TOP, G_PARAM_READWRITE));
203
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BOTTOM,
204 205 206
      g_param_spec_int ("bottom", "Bottom",
          "Pixels to box at bottom (<0 = add a border)", G_MININT, G_MAXINT,
          DEFAULT_BOTTOM, G_PARAM_READWRITE));
207
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_ALPHA,
208 209
      g_param_spec_double ("alpha", "Alpha", "Alpha value picture", 0.0, 1.0,
          DEFAULT_ALPHA, G_PARAM_READWRITE));
210
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BORDER_ALPHA,
211 212 213 214
      g_param_spec_double ("border_alpha", "Border Alpha",
          "Alpha value of the border", 0.0, 1.0, DEFAULT_BORDER_ALPHA,
          G_PARAM_READWRITE));

215 216
  trans_class->transform_caps = gst_video_box_transform_caps;
  trans_class->set_caps = gst_video_box_set_caps;
217
  trans_class->get_unit_size = gst_video_box_get_unit_size;
218
  trans_class->transform = gst_video_box_transform;
219 220 221

  GST_DEBUG_CATEGORY_INIT (videobox_debug, "videobox", 0,
      "Resizes a video by adding borders or cropping");
222 223 224
}

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

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

  switch (prop_id) {
247
    case PROP_LEFT:
248 249 250 251 252 253 254 255 256
      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;
257
    case PROP_RIGHT:
258 259 260 261 262 263 264 265 266
      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;
267
    case PROP_TOP:
268 269 270 271 272 273 274 275 276
      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;
277
    case PROP_BOTTOM:
278 279 280 281 282 283 284 285 286
      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;
287
    case PROP_FILL_TYPE:
288 289
      video_box->fill_type = g_value_get_enum (value);
      break;
290
    case PROP_ALPHA:
291 292
      video_box->alpha = g_value_get_double (value);
      break;
293
    case PROP_BORDER_ALPHA:
294 295 296 297 298 299 300
      video_box->border_alpha = g_value_get_double (value);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}
301

302 303 304 305
static void
gst_video_box_get_property (GObject * object, guint prop_id, GValue * value,
    GParamSpec * pspec)
{
306
  GstVideoBox *video_box = GST_VIDEO_BOX (object);
307 308

  switch (prop_id) {
309
    case PROP_LEFT:
310 311
      g_value_set_int (value, video_box->box_left);
      break;
312
    case PROP_RIGHT:
313 314
      g_value_set_int (value, video_box->box_right);
      break;
315
    case PROP_TOP:
316 317
      g_value_set_int (value, video_box->box_top);
      break;
318
    case PROP_BOTTOM:
319 320
      g_value_set_int (value, video_box->box_bottom);
      break;
321
    case PROP_FILL_TYPE:
322 323
      g_value_set_enum (value, video_box->fill_type);
      break;
324
    case PROP_ALPHA:
325 326
      g_value_set_double (value, video_box->alpha);
      break;
327
    case PROP_BORDER_ALPHA:
328 329 330 331 332 333 334 335
      g_value_set_double (value, video_box->border_alpha);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

336
static GstCaps *
337 338
gst_video_box_transform_caps (GstBaseTransform * trans,
    GstPadDirection direction, GstCaps * from)
339 340 341
{
  GstVideoBox *video_box;
  GstCaps *to;
342 343 344 345
  GstStructure *structure;
  GValue list_value = { 0 }, value = {
  0};
  gint dir, i, tmp;
346 347

  video_box = GST_VIDEO_BOX (trans);
348

349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
  g_value_init (&list_value, GST_TYPE_LIST);
  g_value_init (&value, GST_TYPE_FOURCC);
  gst_value_set_fourcc (&value, GST_MAKE_FOURCC ('I', '4', '2', '0'));
  gst_value_list_append_value (&list_value, &value);
  g_value_unset (&value);

  to = gst_caps_copy (from);
  dir = (direction == GST_PAD_SINK) ? -1 : 1;

  for (i = 0; i < gst_caps_get_size (to); i++) {
    structure = gst_caps_get_structure (to, i);
    if (direction == GST_PAD_SINK) {    /* I420 to { I420, AYUV } */
      g_value_init (&value, GST_TYPE_FOURCC);
      gst_value_set_fourcc (&value, GST_MAKE_FOURCC ('A', 'Y', 'U', 'V'));
      gst_value_list_append_value (&list_value, &value);
      g_value_unset (&value);
      gst_structure_set_value (structure, "format", &list_value);
    } else if (direction == GST_PAD_SRC) {
      gst_structure_set_value (structure, "format", &list_value);
368
    }
369 370
    if (gst_structure_get_int (structure, "width", &tmp))
      gst_structure_set (structure, "width", G_TYPE_INT,
371
          tmp + dir * (video_box->box_left + video_box->box_right), NULL);
372 373
    if (gst_structure_get_int (structure, "height", &tmp))
      gst_structure_set (structure, "height", G_TYPE_INT,
374
          tmp + dir * (video_box->box_top + video_box->box_bottom), NULL);
375 376
  }

377 378
  g_value_unset (&list_value);

379 380
  GST_DEBUG_OBJECT (video_box, "direction %d, transformed %" GST_PTR_FORMAT
      " to %" GST_PTR_FORMAT, direction, from, to);
381

382 383 384
  return to;
}

Wim Taymans's avatar
Wim Taymans committed
385
static gboolean
386
gst_video_box_set_caps (GstBaseTransform * trans, GstCaps * in, GstCaps * out)
387 388 389 390
{
  GstVideoBox *video_box;
  GstStructure *structure;
  gboolean ret;
391
  guint32 fourcc = 0;
392

393
  video_box = GST_VIDEO_BOX (trans);
394

395
  structure = gst_caps_get_structure (in, 0);
396 397 398
  ret = gst_structure_get_int (structure, "width", &video_box->in_width);
  ret &= gst_structure_get_int (structure, "height", &video_box->in_height);

399 400 401
  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);
402 403
  ret &= gst_structure_get_fourcc (structure, "format", &fourcc);

404 405 406 407 408 409 410 411 412 413 414 415 416
  if (fourcc == GST_STR_FOURCC ("AYUV")) {
    video_box->use_alpha = TRUE;
  } else {
    if (video_box->box_left == 0 && video_box->box_right == 0 &&
        video_box->box_top == 0 && video_box->box_bottom == 0) {
      gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (video_box), TRUE);
      GST_LOG ("we are using passthrough");
    } else {
      gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (video_box),
          FALSE);
      GST_LOG ("we are not using passthrough");
    }
  }
417

Wim Taymans's avatar
Wim Taymans committed
418
  return ret;
419 420
}

421
/* see gst-plugins/gst/games/gstvideoimage.c, paint_setup_I420() */
422 423 424
#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)
425

426
#define GST_VIDEO_I420_Y_OFFSET(w,h) (0)
427 428
#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))
429

430
#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))
431

432 433 434
static gboolean
gst_video_box_get_unit_size (GstBaseTransform * trans, GstCaps * caps,
    guint * size)
Wim Taymans's avatar
Wim Taymans committed
435 436
{
  GstVideoBox *video_box;
437 438 439
  GstStructure *structure = NULL;
  guint32 fourcc;
  gint width, height;
Wim Taymans's avatar
Wim Taymans committed
440

441
  g_return_val_if_fail (size, FALSE);
Wim Taymans's avatar
Wim Taymans committed
442 443
  video_box = GST_VIDEO_BOX (trans);

444 445 446 447 448 449 450 451 452 453 454 455 456 457 458
  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
459
  }
460

461
  return TRUE;
Wim Taymans's avatar
Wim Taymans committed
462 463
}

464 465 466 467
static int yuv_colors_Y[] = { 16, 150, 29 };
static int yuv_colors_U[] = { 128, 46, 255 };
static int yuv_colors_V[] = { 128, 21, 107 };

468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497
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++) {
    memset (dest, fill_color, dest_width);
    dest += dest_stride;
  }

  /* copy and add left and right border */
  for (j = 0; j < src_crop_height; j++) {
    memset (dest, fill_color, bl);
    memcpy (dest + bl, src, src_crop_width);
    memset (dest + bl + src_crop_width, fill_color, br);
    dest += dest_stride;
    src += src_stride;
  }

  /* bottom border */
  for (j = 0; j < bb; j++) {
    memset (dest, fill_color, dest_width);
    dest += dest_stride;
  }
}

498 499 500 501 502 503 504
static void
gst_video_box_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;
505 506
  gint src_width, src_height;
  gint src_stride, dest_stride;
507 508 509 510 511 512 513 514 515 516
  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;

517 518
  src_width = video_box->in_width;
  src_height = video_box->in_height;
519

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

523 524 525
  /* Y plane */
  src_stride = GST_VIDEO_I420_Y_ROWSTRIDE (src_width);
  dest_stride = GST_VIDEO_I420_Y_ROWSTRIDE (out_width);
526

527
  destY = dest + GST_VIDEO_I420_Y_OFFSET (out_width, out_height);
528

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

532 533 534
  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]);
535

536 537 538
  /* U plane */
  src_stride = GST_VIDEO_I420_U_ROWSTRIDE (src_width);
  dest_stride = GST_VIDEO_I420_U_ROWSTRIDE (out_width);
539 540 541

  destU = dest + GST_VIDEO_I420_U_OFFSET (out_width, out_height);

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

545 546 547
  gst_video_box_copy_plane_i420 (video_box, srcU, destU, br / 2, bl / 2, bt / 2,
      bb / 2, crop_width / 2, crop_height / 2, src_stride, out_width / 2,
      dest_stride, yuv_colors_U[video_box->fill_type]);
548

549 550 551 552 553 554 555 556 557 558 559 560
  /* V plane */
  src_stride = GST_VIDEO_I420_V_ROWSTRIDE (src_width);
  dest_stride = GST_VIDEO_I420_V_ROWSTRIDE (out_width);

  destV = dest + GST_VIDEO_I420_V_OFFSET (out_width, out_height);

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

  gst_video_box_copy_plane_i420 (video_box, srcV, destV, br / 2, bl / 2, bt / 2,
      bb / 2, crop_width / 2, crop_height / 2, src_stride, out_width / 2,
      dest_stride, yuv_colors_V[video_box->fill_type]);
561 562
}

563 564
/* Note the source image is always I420, we
 * are converting to AYUV on the fly here */
565 566 567 568 569 570
static void
gst_video_box_ayuv (GstVideoBox * video_box, guint8 * src, guint8 * dest)
{
  guint8 *srcY, *srcU, *srcV;
  gint crop_width, crop_width2, crop_height;
  gint out_width, out_height;
571
  gint src_stridey, src_strideu, src_stridev;
572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587
  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);
  guint32 *destp = (guint32 *) dest;
  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;
  out_height = video_box->out_height;

588 589 590
  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);
591 592 593 594 595 596 597 598 599

  crop_width =
      video_box->in_width - (video_box->crop_left + video_box->crop_right);
  crop_width2 = crop_width / 2;
  crop_height =
      video_box->in_height - (video_box->crop_top + video_box->crop_bottom);

  srcY =
      src + GST_VIDEO_I420_Y_OFFSET (video_box->in_width, video_box->in_height);
600
  srcY += src_stridey * video_box->crop_top + video_box->crop_left;
601 602
  srcU =
      src + GST_VIDEO_I420_U_OFFSET (video_box->in_width, video_box->in_height);
603
  srcU += src_strideu * (video_box->crop_top / 2) + (video_box->crop_left / 2);
604 605
  srcV =
      src + GST_VIDEO_I420_V_OFFSET (video_box->in_width, video_box->in_height);
606
  srcV += src_stridev * (video_box->crop_top / 2) + (video_box->crop_left / 2);
607 608 609 610 611 612 613 614 615 616

  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 */
617 618 619 620 621
  if (bt) {
    size_t nb_pixels = bt * out_width;

    oil_splat_u32_ns (destp, &ayuv, nb_pixels);
    destp += nb_pixels;
622 623 624
  }
  for (i = 0; i < crop_height; i++) {
    /* left border */
625 626 627
    if (bl) {
      oil_splat_u32_ns (destp, &ayuv, bl);
      destp += bl;
628 629 630
    }
    dest = (guint8 *) destp;
    /* center */
631 632
    /* We can splat the alpha channel for the whole line */
    oil_splat_u8 (dest, 4, &i_alpha, crop_width);
633
    for (j = 0; j < crop_width2; j++) {
634
      dest++;
635 636 637
      *dest++ = *srcY++;
      *dest++ = *srcU;
      *dest++ = *srcV;
638
      dest++;
639 640 641 642 643 644 645 646
      *dest++ = *srcY++;
      *dest++ = *srcU++;
      *dest++ = *srcV++;
    }
    if (i % 2 == 0) {
      srcU -= crop_width2;
      srcV -= crop_width2;
    } else {
647 648
      srcU += src_strideu - crop_width2;
      srcV += src_stridev - crop_width2;
649
    }
650
    srcY += src_stridey - crop_width;
651 652 653

    destp = (guint32 *) dest;
    /* right border */
654 655 656
    if (br) {
      oil_splat_u32_ns (destp, &ayuv, br);
      destp += br;
657 658 659
    }
  }
  /* bottom border */
660 661 662 663 664
  if (bb) {
    size_t nb_pixels = bb * out_width;

    oil_splat_u32_ns (destp, &ayuv, nb_pixels);
    destp += nb_pixels;
665 666 667
  }
}

Wim Taymans's avatar
Wim Taymans committed
668
static GstFlowReturn
669
gst_video_box_transform (GstBaseTransform * trans, GstBuffer * in,
Wim Taymans's avatar
Wim Taymans committed
670
    GstBuffer * out)
671 672 673
{
  GstVideoBox *video_box;

674
  video_box = GST_VIDEO_BOX (trans);
675 676

  if (video_box->use_alpha) {
Wim Taymans's avatar
Wim Taymans committed
677
    gst_video_box_ayuv (video_box, GST_BUFFER_DATA (in), GST_BUFFER_DATA (out));
678
  } else {
Wim Taymans's avatar
Wim Taymans committed
679
    gst_video_box_i420 (video_box, GST_BUFFER_DATA (in), GST_BUFFER_DATA (out));
680 681
  }

Wim Taymans's avatar
Wim Taymans committed
682
  return GST_FLOW_OK;
683 684 685 686 687 688 689 690 691 692 693 694 695 696
}

static gboolean
plugin_init (GstPlugin * plugin)
{
  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",
    plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN)