gstvideobox.c 21.2 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
#include <gst/video/video.h>

#include <string.h>

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

32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
#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
{
56
  GstBaseTransform element;
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75

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

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

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

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


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

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

131
static GstCaps *gst_video_box_transform_caps (GstBaseTransform * trans,
132
    GstPadDirection direction, GstCaps * from);
133 134
static gboolean gst_video_box_set_caps (GstBaseTransform * trans,
    GstCaps * in, GstCaps * out);
135 136
static gboolean gst_video_box_get_unit_size (GstBaseTransform * trans,
    GstCaps * caps, guint * size);
137
static GstFlowReturn gst_video_box_transform (GstBaseTransform * trans,
Wim Taymans's avatar
Wim Taymans committed
138
    GstBuffer * in, GstBuffer * out);
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 170 171 172


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

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

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

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

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

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

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

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

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

  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);
305
}
306

307 308 309 310
static void
gst_video_box_get_property (GObject * object, guint prop_id, GValue * value,
    GParamSpec * pspec)
{
311
  GstVideoBox *video_box = GST_VIDEO_BOX (object);
312 313

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

341
static GstCaps *
342 343
gst_video_box_transform_caps (GstBaseTransform * trans,
    GstPadDirection direction, GstCaps * from)
344 345 346
{
  GstVideoBox *video_box;
  GstCaps *to;
347 348 349 350
  GstStructure *structure;
  GValue list_value = { 0 }, value = {
  0};
  gint dir, i, tmp;
351 352

  video_box = GST_VIDEO_BOX (trans);
353

354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
  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);
373
    }
374 375
    if (gst_structure_get_int (structure, "width", &tmp))
      gst_structure_set (structure, "width", G_TYPE_INT,
376
          tmp + dir * (video_box->box_left + video_box->box_right), NULL);
377 378
    if (gst_structure_get_int (structure, "height", &tmp))
      gst_structure_set (structure, "height", G_TYPE_INT,
379
          tmp + dir * (video_box->box_top + video_box->box_bottom), NULL);
380 381
  }

382 383
  g_value_unset (&list_value);

384 385 386
  GST_DEBUG_OBJECT (video_box, "transformed %" GST_PTR_FORMAT
      " to %" GST_PTR_FORMAT, from, to);

387 388 389
  return to;
}

Wim Taymans's avatar
Wim Taymans committed
390
static gboolean
391
gst_video_box_set_caps (GstBaseTransform * trans, GstCaps * in, GstCaps * out)
392 393 394 395
{
  GstVideoBox *video_box;
  GstStructure *structure;
  gboolean ret;
396
  guint32 fourcc = 0;
397

398
  video_box = GST_VIDEO_BOX (trans);
399

400
  structure = gst_caps_get_structure (in, 0);
401 402 403
  ret = gst_structure_get_int (structure, "width", &video_box->in_width);
  ret &= gst_structure_get_int (structure, "height", &video_box->in_height);

404 405 406
  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);
407 408 409
  ret &= gst_structure_get_fourcc (structure, "format", &fourcc);

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

Wim Taymans's avatar
Wim Taymans committed
411
  return ret;
412 413
}

414
/* see gst-plugins/gst/games/gstvideoimage.c, paint_setup_I420() */
415 416 417
#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)
418

419
#define GST_VIDEO_I420_Y_OFFSET(w,h) (0)
420 421
#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))
422

423
#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))
424

425 426 427
static gboolean
gst_video_box_get_unit_size (GstBaseTransform * trans, GstCaps * caps,
    guint * size)
Wim Taymans's avatar
Wim Taymans committed
428 429
{
  GstVideoBox *video_box;
430 431 432
  GstStructure *structure = NULL;
  guint32 fourcc;
  gint width, height;
Wim Taymans's avatar
Wim Taymans committed
433

434
  g_return_val_if_fail (size, FALSE);
Wim Taymans's avatar
Wim Taymans committed
435 436
  video_box = GST_VIDEO_BOX (trans);

437 438 439 440 441 442 443 444 445 446 447 448 449 450 451
  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
452
  }
453

454
  return TRUE;
Wim Taymans's avatar
Wim Taymans committed
455 456
}

457 458 459 460
static int yuv_colors_Y[] = { 16, 150, 29 };
static int yuv_colors_U[] = { 128, 46, 255 };
static int yuv_colors_V[] = { 128, 21, 107 };

461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490
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;
  }
}

491 492 493 494 495 496 497
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;
498 499
  gint src_width, src_height;
  gint src_stride, dest_stride;
500 501 502 503 504 505 506 507 508 509
  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;

510 511
  src_width = video_box->in_width;
  src_height = video_box->in_height;
512

513 514
  crop_width = src_width - (video_box->crop_left + video_box->crop_right);
  crop_height = src_height - (video_box->crop_top + video_box->crop_bottom);
515

516 517 518
  /* Y plane */
  src_stride = GST_VIDEO_I420_Y_ROWSTRIDE (src_width);
  dest_stride = GST_VIDEO_I420_Y_ROWSTRIDE (out_width);
519

520
  destY = dest + GST_VIDEO_I420_Y_OFFSET (out_width, out_height);
521

522 523
  srcY = src + GST_VIDEO_I420_Y_OFFSET (src_width, src_height);
  srcY += src_stride * video_box->crop_top + video_box->crop_left;
524

525 526 527
  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]);
528

529 530 531
  /* U plane */
  src_stride = GST_VIDEO_I420_U_ROWSTRIDE (src_width);
  dest_stride = GST_VIDEO_I420_U_ROWSTRIDE (out_width);
532 533 534

  destU = dest + GST_VIDEO_I420_U_OFFSET (out_width, out_height);

535
  srcU = src + GST_VIDEO_I420_U_OFFSET (src_width, src_height);
536 537
  srcU += src_stride * (video_box->crop_top / 2) + (video_box->crop_left / 2);

538 539 540
  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]);
541

542 543 544 545 546 547 548 549 550 551 552 553
  /* 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]);
554 555
}

556 557 558
/* Note the source image is always I420, we
 * are converting to AYUV on the fly here */

559 560 561 562 563 564
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;
565
  gint src_stridey, src_strideu, src_stridev;
566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581
  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;

582 583 584
  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);
585 586 587 588 589 590 591 592 593

  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);
594
  srcY += src_stridey * video_box->crop_top + video_box->crop_left;
595 596
  srcU =
      src + GST_VIDEO_I420_U_OFFSET (video_box->in_width, video_box->in_height);
597
  srcU += src_strideu * (video_box->crop_top / 2) + (video_box->crop_left / 2);
598 599
  srcV =
      src + GST_VIDEO_I420_V_OFFSET (video_box->in_width, video_box->in_height);
600
  srcV += src_stridev * (video_box->crop_top / 2) + (video_box->crop_left / 2);
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 626 627 628 629 630 631 632 633 634 635 636

  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 {
637 638
      srcU += src_strideu - crop_width2;
      srcV += src_stridev - crop_width2;
639
    }
640
    srcY += src_stridey - crop_width;
641 642 643 644 645 646 647 648 649 650 651 652 653 654 655

    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
656
static GstFlowReturn
657
gst_video_box_transform (GstBaseTransform * trans, GstBuffer * in,
Wim Taymans's avatar
Wim Taymans committed
658
    GstBuffer * out)
659 660 661
{
  GstVideoBox *video_box;

662
  video_box = GST_VIDEO_BOX (trans);
663 664

  if (video_box->use_alpha) {
Wim Taymans's avatar
Wim Taymans committed
665
    gst_video_box_ayuv (video_box, GST_BUFFER_DATA (in), GST_BUFFER_DATA (out));
666
  } else {
Wim Taymans's avatar
Wim Taymans committed
667
    gst_video_box_i420 (video_box, GST_BUFFER_DATA (in), GST_BUFFER_DATA (out));
668 669
  }

Wim Taymans's avatar
Wim Taymans committed
670
  return GST_FLOW_OK;
671 672 673 674 675 676 677 678 679 680 681 682 683 684
}

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)