gstvideobox.c 19.3 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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
#include <gst/video/video.h>

#include <string.h>

#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
{
53
  GstBaseTransform element;
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72

  /* 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
{
73
  GstBaseTransformClass parent_class;
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
};

/* 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
{
94 95 96 97 98 99 100 101
  PROP_0,
  PROP_LEFT,
  PROP_RIGHT,
  PROP_TOP,
  PROP_BOTTOM,
  PROP_FILL_TYPE,
  PROP_ALPHA,
  PROP_BORDER_ALPHA,
102 103 104 105 106 107 108
  /* FILL ME */
};

static GstStaticPadTemplate gst_video_box_src_template =
GST_STATIC_PAD_TEMPLATE ("src",
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
109
    GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("{ AYUV, I420 }"))
110 111 112 113 114 115 116 117 118 119
    );

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


120 121
GST_BOILERPLATE (GstVideoBox, gst_video_box, GstBaseTransform,
    GST_TYPE_BASE_TRANSFORM);
122 123 124 125 126 127

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

128 129 130 131
static GstCaps *gst_video_box_transform_caps (GstBaseTransform * trans,
    GstPad * pad, GstCaps * from);
static gboolean gst_video_box_set_caps (GstBaseTransform * trans,
    GstCaps * in, GstCaps * out);
Wim Taymans's avatar
Wim Taymans committed
132
static guint gst_video_box_get_size (GstBaseTransform * trans);
133
static GstFlowReturn gst_video_box_transform (GstBaseTransform * trans,
Wim Taymans's avatar
Wim Taymans committed
134
    GstBuffer * in, GstBuffer * out);
135 136 137 138 139 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


#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));
}
169

170 171 172 173
static void
gst_video_box_class_init (GstVideoBoxClass * klass)
{
  GObjectClass *gobject_class;
174
  GstBaseTransformClass *trans_class;
175 176

  gobject_class = (GObjectClass *) klass;
177
  trans_class = (GstBaseTransformClass *) klass;
178

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

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

210 211
  trans_class->transform_caps = gst_video_box_transform_caps;
  trans_class->set_caps = gst_video_box_set_caps;
Wim Taymans's avatar
Wim Taymans committed
212
  trans_class->get_size = gst_video_box_get_size;
213
  trans_class->transform = gst_video_box_transform;
214 215 216 217 218 219 220 221 222
}

static void
gst_video_box_init (GstVideoBox * video_box)
{
  video_box->box_right = DEFAULT_RIGHT;
  video_box->box_left = DEFAULT_LEFT;
  video_box->box_top = DEFAULT_TOP;
  video_box->box_bottom = DEFAULT_BOTTOM;
223 224 225 226
  video_box->crop_right = 0;
  video_box->crop_left = 0;
  video_box->crop_top = 0;
  video_box->crop_bottom = 0;
227 228 229 230 231 232 233 234 235
  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)
{
236
  GstVideoBox *video_box = GST_VIDEO_BOX (object);
237 238

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

  switch (prop_id) {
300
    case PROP_LEFT:
301 302
      g_value_set_int (value, video_box->box_left);
      break;
303
    case PROP_RIGHT:
304 305
      g_value_set_int (value, video_box->box_right);
      break;
306
    case PROP_TOP:
307 308
      g_value_set_int (value, video_box->box_top);
      break;
309
    case PROP_BOTTOM:
310 311
      g_value_set_int (value, video_box->box_bottom);
      break;
312
    case PROP_FILL_TYPE:
313 314
      g_value_set_enum (value, video_box->fill_type);
      break;
315
    case PROP_ALPHA:
316 317
      g_value_set_double (value, video_box->alpha);
      break;
318
    case PROP_BORDER_ALPHA:
319 320 321 322 323 324 325 326
      g_value_set_double (value, video_box->border_alpha);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

327 328 329 330 331 332 333 334 335 336 337
static GstCaps *
gst_video_box_transform_caps (GstBaseTransform * trans, GstPad * pad,
    GstCaps * from)
{
  GstVideoBox *video_box;
  GstCaps *to;
  GstStructure *structure;
  gint direction, i, tmp;

  video_box = GST_VIDEO_BOX (trans);
  to = gst_caps_copy (from);
338
  direction = (pad == trans->sinkpad) ? -1 : 1;
339

340
  /* FIXME, include AYUV */
341 342 343 344 345 346 347 348 349 350 351 352 353
  for (i = 0; i < gst_caps_get_size (to); i++) {
    structure = gst_caps_get_structure (to, i);
    if (gst_structure_get_int (structure, "width", &tmp))
      gst_structure_set (structure, "width", G_TYPE_INT,
          tmp + direction * (video_box->box_left + video_box->box_right), NULL);
    if (gst_structure_get_int (structure, "height", &tmp))
      gst_structure_set (structure, "height", G_TYPE_INT,
          tmp + direction * (video_box->box_top + video_box->box_bottom), NULL);
  }

  return to;
}

Wim Taymans's avatar
Wim Taymans committed
354
static gboolean
355
gst_video_box_set_caps (GstBaseTransform * trans, GstCaps * in, GstCaps * out)
356 357 358 359
{
  GstVideoBox *video_box;
  GstStructure *structure;
  gboolean ret;
360
  guint32 fourcc = 0;
361

362
  video_box = GST_VIDEO_BOX (trans);
363

364
  structure = gst_caps_get_structure (in, 0);
365 366 367
  ret = gst_structure_get_int (structure, "width", &video_box->in_width);
  ret &= gst_structure_get_int (structure, "height", &video_box->in_height);

368 369 370
  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);
371 372 373
  ret &= gst_structure_get_fourcc (structure, "format", &fourcc);

  video_box->use_alpha = fourcc == GST_STR_FOURCC ("AYUV");
374

Wim Taymans's avatar
Wim Taymans committed
375
  return ret;
376 377
}

378 379 380 381 382 383 384 385
#define ROUND_UP_2(x)  (((x)+1)&~1)
#define ROUND_UP_4(x)  (((x)+3)&~3)
#define ROUND_UP_8(x)  (((x)+7)&~7)

/* see gst-plugins/gst/games/gstvideoimage.c, paint_setup_I420() */
#define GST_VIDEO_I420_Y_ROWSTRIDE(width) (ROUND_UP_4(width))
#define GST_VIDEO_I420_U_ROWSTRIDE(width) (ROUND_UP_8(width)/2)
#define GST_VIDEO_I420_V_ROWSTRIDE(width) ((ROUND_UP_8(GST_VIDEO_I420_Y_ROWSTRIDE(width)))/2)
386

387 388 389 390 391
#define GST_VIDEO_I420_Y_OFFSET(w,h) (0)
#define GST_VIDEO_I420_U_OFFSET(w,h) (GST_VIDEO_I420_Y_OFFSET(w,h)+(GST_VIDEO_I420_Y_ROWSTRIDE(w)*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)*ROUND_UP_2(h)/2))

#define GST_VIDEO_I420_SIZE(w,h)     (GST_VIDEO_I420_V_OFFSET(w,h)+(GST_VIDEO_I420_V_ROWSTRIDE(w)*ROUND_UP_2(h)/2))
392

Wim Taymans's avatar
Wim Taymans committed
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408
static guint
gst_video_box_get_size (GstBaseTransform * trans)
{
  guint size;
  GstVideoBox *video_box;

  video_box = GST_VIDEO_BOX (trans);

  if (video_box->use_alpha) {
    size = video_box->out_height * video_box->out_height * 4;
  } else {
    size = GST_VIDEO_I420_SIZE (video_box->out_width, video_box->out_height);
  }
  return size;
}

409 410 411 412
static int yuv_colors_Y[] = { 16, 150, 29 };
static int yuv_colors_U[] = { 128, 46, 255 };
static int yuv_colors_V[] = { 128, 21, 107 };

413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442
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;
  }
}

443 444 445 446 447 448 449
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;
450 451
  gint src_width, src_height;
  gint src_stride, dest_stride;
452 453 454 455 456 457 458 459 460 461
  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;

462 463
  src_width = video_box->in_width;
  src_height = video_box->in_height;
464

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

468 469 470
  /* Y plane */
  src_stride = GST_VIDEO_I420_Y_ROWSTRIDE (src_width);
  dest_stride = GST_VIDEO_I420_Y_ROWSTRIDE (out_width);
471

472
  destY = dest + GST_VIDEO_I420_Y_OFFSET (out_width, out_height);
473

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

477 478 479
  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]);
480

481 482 483
  /* U plane */
  src_stride = GST_VIDEO_I420_U_ROWSTRIDE (src_width);
  dest_stride = GST_VIDEO_I420_U_ROWSTRIDE (out_width);
484 485 486

  destU = dest + GST_VIDEO_I420_U_OFFSET (out_width, out_height);

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

490 491 492
  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]);
493

494 495 496 497 498 499 500 501 502 503 504 505
  /* 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]);
506 507
}

508 509 510
/* Note the source image is always I420, we
 * are converting to AYUV on the fly here */

511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606
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;
  gint src_stride, src_stride2;
  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;

  src_stride = GST_VIDEO_I420_Y_ROWSTRIDE (video_box->in_width);
  src_stride2 = src_stride / 2;

  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);
  srcY += src_stride * video_box->crop_top + video_box->crop_left;
  srcU =
      src + GST_VIDEO_I420_U_OFFSET (video_box->in_width, video_box->in_height);
  srcU += src_stride2 * (video_box->crop_top / 2) + (video_box->crop_left / 2);
  srcV =
      src + GST_VIDEO_I420_V_OFFSET (video_box->in_width, video_box->in_height);
  srcV += src_stride2 * (video_box->crop_top / 2) + (video_box->crop_left / 2);

  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 */
  for (i = 0; i < bt; i++) {
    for (j = 0; j < out_width; j++) {
      *destp++ = ayuv;
    }
  }
  for (i = 0; i < crop_height; i++) {
    /* left border */
    for (j = 0; j < bl; j++) {
      *destp++ = ayuv;
    }
    dest = (guint8 *) destp;
    /* center */
    for (j = 0; j < crop_width2; j++) {
      *dest++ = i_alpha;
      *dest++ = *srcY++;
      *dest++ = *srcU;
      *dest++ = *srcV;
      *dest++ = i_alpha;
      *dest++ = *srcY++;
      *dest++ = *srcU++;
      *dest++ = *srcV++;
    }
    if (i % 2 == 0) {
      srcU -= crop_width2;
      srcV -= crop_width2;
    } else {
      srcU += src_stride2 - crop_width2;
      srcV += src_stride2 - crop_width2;
    }
    srcY += src_stride - crop_width;

    destp = (guint32 *) dest;
    /* right border */
    for (j = 0; j < br; j++) {
      *destp++ = ayuv;
    }
  }
  /* bottom border */
  for (i = 0; i < bb; i++) {
    for (j = 0; j < out_width; j++) {
      *destp++ = ayuv;
    }
  }
}

Wim Taymans's avatar
Wim Taymans committed
607
static GstFlowReturn
608
gst_video_box_transform (GstBaseTransform * trans, GstBuffer * in,
Wim Taymans's avatar
Wim Taymans committed
609
    GstBuffer * out)
610 611 612
{
  GstVideoBox *video_box;

613
  video_box = GST_VIDEO_BOX (trans);
614 615

  if (video_box->use_alpha) {
Wim Taymans's avatar
Wim Taymans committed
616
    gst_video_box_ayuv (video_box, GST_BUFFER_DATA (in), GST_BUFFER_DATA (out));
617
  } else {
Wim Taymans's avatar
Wim Taymans committed
618
    gst_video_box_i420 (video_box, GST_BUFFER_DATA (in), GST_BUFFER_DATA (out));
619 620
  }

Wim Taymans's avatar
Wim Taymans committed
621
  return GST_FLOW_OK;
622 623 624 625 626 627 628 629 630 631 632 633 634 635
}

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)