gstvideobox.c 20 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
static GstCaps *gst_video_box_transform_caps (GstBaseTransform * trans,
129
    GstPadDirection direction, GstCaps * from);
130 131
static gboolean gst_video_box_set_caps (GstBaseTransform * trans,
    GstCaps * in, GstCaps * out);
132 133
static gboolean gst_video_box_get_unit_size (GstBaseTransform * trans,
    GstCaps * caps, guint * size);
134
static GstFlowReturn gst_video_box_transform (GstBaseTransform * trans,
Wim Taymans's avatar
Wim Taymans committed
135
    GstBuffer * in, GstBuffer * out);
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 169


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

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

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

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

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

211 212
  trans_class->transform_caps = gst_video_box_transform_caps;
  trans_class->set_caps = gst_video_box_set_caps;
213
  trans_class->get_unit_size = gst_video_box_get_unit_size;
214
  trans_class->transform = gst_video_box_transform;
215 216 217
}

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

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

  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);
  else
    gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (video_box), FALSE);
299 300 301 302 303
}
static void
gst_video_box_get_property (GObject * object, guint prop_id, GValue * value,
    GParamSpec * pspec)
{
304
  GstVideoBox *video_box = GST_VIDEO_BOX (object);
305 306

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

334
static GstCaps *
335 336
gst_video_box_transform_caps (GstBaseTransform * trans,
    GstPadDirection direction, GstCaps * from)
337 338 339 340
{
  GstVideoBox *video_box;
  GstCaps *to;
  GstStructure *structure;
341
  gint dir, i, tmp;
342 343 344

  video_box = GST_VIDEO_BOX (trans);
  to = gst_caps_copy (from);
345
  dir = (direction == GST_PAD_SINK) ? -1 : 1;
346

347
  /* FIXME, include AYUV */
348 349 350 351
  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,
352
          tmp + dir * (video_box->box_left + video_box->box_right), NULL);
353 354
    if (gst_structure_get_int (structure, "height", &tmp))
      gst_structure_set (structure, "height", G_TYPE_INT,
355
          tmp + dir * (video_box->box_top + video_box->box_bottom), NULL);
356 357 358 359 360
  }

  return to;
}

Wim Taymans's avatar
Wim Taymans committed
361
static gboolean
362
gst_video_box_set_caps (GstBaseTransform * trans, GstCaps * in, GstCaps * out)
363 364 365 366
{
  GstVideoBox *video_box;
  GstStructure *structure;
  gboolean ret;
367
  guint32 fourcc = 0;
368

369
  video_box = GST_VIDEO_BOX (trans);
370

371
  structure = gst_caps_get_structure (in, 0);
372 373 374
  ret = gst_structure_get_int (structure, "width", &video_box->in_width);
  ret &= gst_structure_get_int (structure, "height", &video_box->in_height);

375 376 377
  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);
378 379 380
  ret &= gst_structure_get_fourcc (structure, "format", &fourcc);

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

Wim Taymans's avatar
Wim Taymans committed
382
  return ret;
383 384
}

385
/* see gst-plugins/gst/games/gstvideoimage.c, paint_setup_I420() */
386 387 388
#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)
389

390
#define GST_VIDEO_I420_Y_OFFSET(w,h) (0)
391 392
#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))
393

394
#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))
395

396 397 398
static gboolean
gst_video_box_get_unit_size (GstBaseTransform * trans, GstCaps * caps,
    guint * size)
Wim Taymans's avatar
Wim Taymans committed
399 400
{
  GstVideoBox *video_box;
401 402 403
  GstStructure *structure = NULL;
  guint32 fourcc;
  gint width, height;
Wim Taymans's avatar
Wim Taymans committed
404

405
  g_return_val_if_fail (size, FALSE);
Wim Taymans's avatar
Wim Taymans committed
406 407
  video_box = GST_VIDEO_BOX (trans);

408 409 410 411 412 413 414 415 416 417 418 419 420 421 422
  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
423
  }
424

425
  return TRUE;
Wim Taymans's avatar
Wim Taymans committed
426 427
}

428 429 430 431
static int yuv_colors_Y[] = { 16, 150, 29 };
static int yuv_colors_U[] = { 128, 46, 255 };
static int yuv_colors_V[] = { 128, 21, 107 };

432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461
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;
  }
}

462 463 464 465 466 467 468
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;
469 470
  gint src_width, src_height;
  gint src_stride, dest_stride;
471 472 473 474 475 476 477 478 479 480
  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;

481 482
  src_width = video_box->in_width;
  src_height = video_box->in_height;
483

484 485
  crop_width = src_width - (video_box->crop_left + video_box->crop_right);
  crop_height = src_height - (video_box->crop_top + video_box->crop_bottom);
486

487 488 489
  /* Y plane */
  src_stride = GST_VIDEO_I420_Y_ROWSTRIDE (src_width);
  dest_stride = GST_VIDEO_I420_Y_ROWSTRIDE (out_width);
490

491
  destY = dest + GST_VIDEO_I420_Y_OFFSET (out_width, out_height);
492

493 494
  srcY = src + GST_VIDEO_I420_Y_OFFSET (src_width, src_height);
  srcY += src_stride * video_box->crop_top + video_box->crop_left;
495

496 497 498
  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]);
499

500 501 502
  /* U plane */
  src_stride = GST_VIDEO_I420_U_ROWSTRIDE (src_width);
  dest_stride = GST_VIDEO_I420_U_ROWSTRIDE (out_width);
503 504 505

  destU = dest + GST_VIDEO_I420_U_OFFSET (out_width, out_height);

506
  srcU = src + GST_VIDEO_I420_U_OFFSET (src_width, src_height);
507 508
  srcU += src_stride * (video_box->crop_top / 2) + (video_box->crop_left / 2);

509 510 511
  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]);
512

513 514 515 516 517 518 519 520 521 522 523 524
  /* 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]);
525 526
}

527 528 529
/* Note the source image is always I420, we
 * are converting to AYUV on the fly here */

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 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
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
626
static GstFlowReturn
627
gst_video_box_transform (GstBaseTransform * trans, GstBuffer * in,
Wim Taymans's avatar
Wim Taymans committed
628
    GstBuffer * out)
629 630 631
{
  GstVideoBox *video_box;

632
  video_box = GST_VIDEO_BOX (trans);
633 634

  if (video_box->use_alpha) {
Wim Taymans's avatar
Wim Taymans committed
635
    gst_video_box_ayuv (video_box, GST_BUFFER_DATA (in), GST_BUFFER_DATA (out));
636
  } else {
Wim Taymans's avatar
Wim Taymans committed
637
    gst_video_box_i420 (video_box, GST_BUFFER_DATA (in), GST_BUFFER_DATA (out));
638 639
  }

Wim Taymans's avatar
Wim Taymans committed
640
  return GST_FLOW_OK;
641 642 643 644 645 646 647 648 649 650 651 652 653 654
}

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)