gstv4l2sink.c 19.6 KB
Newer Older
Rob Clark's avatar
Rob Clark committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/* GStreamer
 *
 * Copyright (C) 2009 Texas Instruments, Inc - http://www.ti.com/
 *
 * Description: V4L2 sink element
 *  Created on: Jul 2, 2009
 *      Author: Rob Clark <rob@ti.com>
 *
 * 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
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
21 22
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
Rob Clark's avatar
Rob Clark committed
23 24 25 26 27 28 29 30 31 32 33
 */

/**
 * SECTION:element-v4l2sink
 *
 * v4l2sink can be used to display video to v4l2 devices (screen overlays
 * provided by the graphics hardware, tv-out, etc)
 *
 * <refsect2>
 * <title>Example launch lines</title>
 * |[
34
 * gst-launch-1.0 videotestsrc ! v4l2sink device=/dev/video1
Rob Clark's avatar
Rob Clark committed
35
 * ]| This pipeline displays a test pattern on /dev/video1
Rob Clark's avatar
Rob Clark committed
36
 * |[
37
 * gst-launch-1.0 -v videotestsrc ! navigationtest ! v4l2sink
Rob Clark's avatar
Rob Clark committed
38 39 40 41 42 43 44 45 46 47
 * ]| A pipeline to test navigation events.
 * While moving the mouse pointer over the test signal you will see a black box
 * following the mouse pointer. If you press the mouse button somewhere on the
 * video and release it somewhere else a green box will appear where you pressed
 * the button and a red one where you released it. (The navigationtest element
 * is part of gst-plugins-good.) You can observe here that even if the images
 * are scaled through hardware the pointer coordinates are converted back to the
 * original video frame geometry so that the box can be drawn to the correct
 * position. This also handles borders correctly, limiting coordinates to the
 * image area
Rob Clark's avatar
Rob Clark committed
48 49 50 51 52 53 54 55
 * </refsect2>
 */


#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

Wim Taymans's avatar
Wim Taymans committed
56
#include "gst/video/gstvideometa.h"
Rob Clark's avatar
Rob Clark committed
57 58

#include "gstv4l2colorbalance.h"
Rob Clark's avatar
Rob Clark committed
59
#include "gstv4l2tuner.h"
Rob Clark's avatar
Rob Clark committed
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
#include "gstv4l2vidorient.h"

#include "gstv4l2sink.h"
#include "gst/gst-i18n-plugin.h"

#include <string.h>

GST_DEBUG_CATEGORY (v4l2sink_debug);
#define GST_CAT_DEFAULT v4l2sink_debug

#define DEFAULT_PROP_DEVICE   "/dev/video1"

enum
{
  PROP_0,
  V4L2_STD_OBJECT_PROPS,
  PROP_OVERLAY_TOP,
  PROP_OVERLAY_LEFT,
  PROP_OVERLAY_WIDTH,
  PROP_OVERLAY_HEIGHT,
80 81 82 83
  PROP_CROP_TOP,
  PROP_CROP_LEFT,
  PROP_CROP_WIDTH,
  PROP_CROP_HEIGHT,
Rob Clark's avatar
Rob Clark committed
84 85 86 87
};


GST_IMPLEMENT_V4L2_COLOR_BALANCE_METHODS (GstV4l2Sink, gst_v4l2sink);
Rob Clark's avatar
Rob Clark committed
88
GST_IMPLEMENT_V4L2_TUNER_METHODS (GstV4l2Sink, gst_v4l2sink);
Rob Clark's avatar
Rob Clark committed
89 90
GST_IMPLEMENT_V4L2_VIDORIENT_METHODS (GstV4l2Sink, gst_v4l2sink);

Wim Taymans's avatar
Wim Taymans committed
91 92 93 94 95 96
#define gst_v4l2sink_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstV4l2Sink, gst_v4l2sink, GST_TYPE_VIDEO_SINK,
    G_IMPLEMENT_INTERFACE (GST_TYPE_TUNER, gst_v4l2sink_tuner_interface_init);
    G_IMPLEMENT_INTERFACE (GST_TYPE_COLOR_BALANCE,
        gst_v4l2sink_color_balance_interface_init);
    G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_ORIENTATION,
97
        gst_v4l2sink_video_orientation_interface_init));
Rob Clark's avatar
Rob Clark committed
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112


static void gst_v4l2sink_finalize (GstV4l2Sink * v4l2sink);

/* GObject methods: */
static void gst_v4l2sink_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec);
static void gst_v4l2sink_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec);

/* GstElement methods: */
static GstStateChangeReturn gst_v4l2sink_change_state (GstElement * element,
    GstStateChange transition);

/* GstBaseSink methods: */
113
static gboolean gst_v4l2sink_propose_allocation (GstBaseSink * bsink,
114
    GstQuery * query);
Wim Taymans's avatar
Wim Taymans committed
115
static GstCaps *gst_v4l2sink_get_caps (GstBaseSink * bsink, GstCaps * filter);
Rob Clark's avatar
Rob Clark committed
116
static gboolean gst_v4l2sink_set_caps (GstBaseSink * bsink, GstCaps * caps);
Wim Taymans's avatar
Wim Taymans committed
117
#if 0
Rob Clark's avatar
Rob Clark committed
118 119
static GstFlowReturn gst_v4l2sink_buffer_alloc (GstBaseSink * bsink,
    guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf);
Wim Taymans's avatar
Wim Taymans committed
120
#endif
Rob Clark's avatar
Rob Clark committed
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
static GstFlowReturn gst_v4l2sink_show_frame (GstBaseSink * bsink,
    GstBuffer * buf);

static void
gst_v4l2sink_class_init (GstV4l2SinkClass * klass)
{
  GObjectClass *gobject_class;
  GstElementClass *element_class;
  GstBaseSinkClass *basesink_class;

  gobject_class = G_OBJECT_CLASS (klass);
  element_class = GST_ELEMENT_CLASS (klass);
  basesink_class = GST_BASE_SINK_CLASS (klass);

  gobject_class->finalize = (GObjectFinalizeFunc) gst_v4l2sink_finalize;
  gobject_class->set_property = gst_v4l2sink_set_property;
  gobject_class->get_property = gst_v4l2sink_get_property;

  element_class->change_state = gst_v4l2sink_change_state;

  gst_v4l2_object_install_properties_helper (gobject_class,
      DEFAULT_PROP_DEVICE);
Wim Taymans's avatar
Wim Taymans committed
143

Rob Clark's avatar
Rob Clark committed
144 145 146
  g_object_class_install_property (gobject_class, PROP_OVERLAY_TOP,
      g_param_spec_int ("overlay-top", "Overlay top",
          "The topmost (y) coordinate of the video overlay; top left corner of screen is 0,0",
147
          G_MININT, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Rob Clark's avatar
Rob Clark committed
148 149 150
  g_object_class_install_property (gobject_class, PROP_OVERLAY_LEFT,
      g_param_spec_int ("overlay-left", "Overlay left",
          "The leftmost (x) coordinate of the video overlay; top left corner of screen is 0,0",
151
          G_MININT, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Rob Clark's avatar
Rob Clark committed
152 153 154
  g_object_class_install_property (gobject_class, PROP_OVERLAY_WIDTH,
      g_param_spec_uint ("overlay-width", "Overlay width",
          "The width of the video overlay; default is equal to negotiated image width",
155
          0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Rob Clark's avatar
Rob Clark committed
156 157 158
  g_object_class_install_property (gobject_class, PROP_OVERLAY_HEIGHT,
      g_param_spec_uint ("overlay-height", "Overlay height",
          "The height of the video overlay; default is equal to negotiated image height",
159
          0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Rob Clark's avatar
Rob Clark committed
160

161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
  g_object_class_install_property (gobject_class, PROP_CROP_TOP,
      g_param_spec_int ("crop-top", "Crop top",
          "The topmost (y) coordinate of the video crop; top left corner of image is 0,0",
          0x80000000, 0x7fffffff, 0, G_PARAM_READWRITE));
  g_object_class_install_property (gobject_class, PROP_CROP_LEFT,
      g_param_spec_int ("crop-left", "Crop left",
          "The leftmost (x) coordinate of the video crop; top left corner of image is 0,0",
          0x80000000, 0x7fffffff, 0, G_PARAM_READWRITE));
  g_object_class_install_property (gobject_class, PROP_CROP_WIDTH,
      g_param_spec_uint ("crop-width", "Crop width",
          "The width of the video crop; default is equal to negotiated image width",
          0, 0xffffffff, 0, G_PARAM_READWRITE));
  g_object_class_install_property (gobject_class, PROP_CROP_HEIGHT,
      g_param_spec_uint ("crop-height", "Crop height",
          "The height of the video crop; default is equal to negotiated image height",
          0, 0xffffffff, 0, G_PARAM_READWRITE));

178
  gst_element_class_set_static_metadata (element_class,
Wim Taymans's avatar
Wim Taymans committed
179 180 181 182 183 184 185
      "Video (video4linux2) Sink", "Sink/Video",
      "Displays frames on a video4linux2 device", "Rob Clark <rob@ti.com>,");

  gst_element_class_add_pad_template (element_class,
      gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
          gst_v4l2_object_get_all_caps ()));

Rob Clark's avatar
Rob Clark committed
186 187
  basesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_v4l2sink_get_caps);
  basesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_v4l2sink_set_caps);
188 189
  basesink_class->propose_allocation =
      GST_DEBUG_FUNCPTR (gst_v4l2sink_propose_allocation);
Rob Clark's avatar
Rob Clark committed
190
  basesink_class->render = GST_DEBUG_FUNCPTR (gst_v4l2sink_show_frame);
Wim Taymans's avatar
Wim Taymans committed
191 192 193 194 195

  klass->v4l2_class_devices = NULL;

  GST_DEBUG_CATEGORY_INIT (v4l2sink_debug, "v4l2sink", 0, "V4L2 sink element");

Rob Clark's avatar
Rob Clark committed
196 197 198
}

static void
Wim Taymans's avatar
Wim Taymans committed
199
gst_v4l2sink_init (GstV4l2Sink * v4l2sink)
Rob Clark's avatar
Rob Clark committed
200 201 202
{
  v4l2sink->v4l2object = gst_v4l2_object_new (GST_ELEMENT (v4l2sink),
      V4L2_BUF_TYPE_VIDEO_OUTPUT, DEFAULT_PROP_DEVICE,
Rob Clark's avatar
Rob Clark committed
203
      gst_v4l2_get_output, gst_v4l2_set_output, NULL);
Rob Clark's avatar
Rob Clark committed
204 205 206 207 208 209 210 211 212

  /* same default value for video output device as is used for
   * v4l2src/capture is no good..  so lets set a saner default
   * (which can be overridden by the one creating the v4l2sink
   * after the constructor returns)
   */
  g_object_set (v4l2sink, "device", "/dev/video1", NULL);

  v4l2sink->overlay_fields_set = 0;
213
  v4l2sink->crop_fields_set = 0;
Rob Clark's avatar
Rob Clark committed
214 215 216 217 218 219 220 221 222 223 224 225
}


static void
gst_v4l2sink_finalize (GstV4l2Sink * v4l2sink)
{
  gst_v4l2_object_destroy (v4l2sink->v4l2object);

  G_OBJECT_CLASS (parent_class)->finalize ((GObject *) (v4l2sink));
}


Stefan Kost's avatar
Stefan Kost committed
226
/*
227 228
 * flags to indicate which overlay/crop properties the user has set (and
 * therefore which ones should override the defaults from the driver)
Rob Clark's avatar
Rob Clark committed
229 230 231
 */
enum
{
232 233 234 235
  RECT_TOP_SET = 0x01,
  RECT_LEFT_SET = 0x02,
  RECT_WIDTH_SET = 0x04,
  RECT_HEIGHT_SET = 0x08
Rob Clark's avatar
Rob Clark committed
236 237 238 239 240
};

static void
gst_v4l2sink_sync_overlay_fields (GstV4l2Sink * v4l2sink)
{
241 242 243
  if (!v4l2sink->overlay_fields_set)
    return;

Rob Clark's avatar
Rob Clark committed
244 245 246 247 248 249 250 251
  if (GST_V4L2_IS_OPEN (v4l2sink->v4l2object)) {

    gint fd = v4l2sink->v4l2object->video_fd;
    struct v4l2_format format;

    memset (&format, 0x00, sizeof (struct v4l2_format));
    format.type = V4L2_BUF_TYPE_VIDEO_OVERLAY;

252 253 254 255
    if (v4l2_ioctl (fd, VIDIOC_G_FMT, &format) < 0) {
      GST_WARNING_OBJECT (v4l2sink, "VIDIOC_G_FMT failed");
      return;
    }
Rob Clark's avatar
Rob Clark committed
256

257 258 259 260 261 262 263
    GST_DEBUG_OBJECT (v4l2sink,
        "setting overlay: overlay_fields_set=0x%02x, top=%d, left=%d, width=%d, height=%d",
        v4l2sink->overlay_fields_set,
        v4l2sink->overlay.top, v4l2sink->overlay.left,
        v4l2sink->overlay.width, v4l2sink->overlay.height);

    if (v4l2sink->overlay_fields_set & RECT_TOP_SET)
264
      format.fmt.win.w.top = v4l2sink->overlay.top;
265
    if (v4l2sink->overlay_fields_set & RECT_LEFT_SET)
266
      format.fmt.win.w.left = v4l2sink->overlay.left;
267
    if (v4l2sink->overlay_fields_set & RECT_WIDTH_SET)
268
      format.fmt.win.w.width = v4l2sink->overlay.width;
269
    if (v4l2sink->overlay_fields_set & RECT_HEIGHT_SET)
270 271
      format.fmt.win.w.height = v4l2sink->overlay.height;

272 273 274 275
    if (v4l2_ioctl (fd, VIDIOC_S_FMT, &format) < 0) {
      GST_WARNING_OBJECT (v4l2sink, "VIDIOC_S_FMT failed");
      return;
    }
Rob Clark's avatar
Rob Clark committed
276

277
    v4l2sink->overlay_fields_set = 0;
Rob Clark's avatar
Rob Clark committed
278 279 280 281
    v4l2sink->overlay = format.fmt.win.w;
  }
}

282 283 284 285 286 287 288 289 290 291 292 293 294 295
static void
gst_v4l2sink_sync_crop_fields (GstV4l2Sink * v4l2sink)
{
  if (!v4l2sink->crop_fields_set)
    return;

  if (GST_V4L2_IS_OPEN (v4l2sink->v4l2object)) {

    gint fd = v4l2sink->v4l2object->video_fd;
    struct v4l2_crop crop;

    memset (&crop, 0x00, sizeof (struct v4l2_crop));
    crop.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;

296 297 298 299
    if (v4l2_ioctl (fd, VIDIOC_G_CROP, &crop) < 0) {
      GST_WARNING_OBJECT (v4l2sink, "VIDIOC_G_CROP failed");
      return;
    }
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315

    GST_DEBUG_OBJECT (v4l2sink,
        "setting crop: crop_fields_set=0x%02x, top=%d, left=%d, width=%d, height=%d",
        v4l2sink->crop_fields_set,
        v4l2sink->crop.top, v4l2sink->crop.left,
        v4l2sink->crop.width, v4l2sink->crop.height);

    if (v4l2sink->crop_fields_set & RECT_TOP_SET)
      crop.c.top = v4l2sink->crop.top;
    if (v4l2sink->crop_fields_set & RECT_LEFT_SET)
      crop.c.left = v4l2sink->crop.left;
    if (v4l2sink->crop_fields_set & RECT_WIDTH_SET)
      crop.c.width = v4l2sink->crop.width;
    if (v4l2sink->crop_fields_set & RECT_HEIGHT_SET)
      crop.c.height = v4l2sink->crop.height;

316 317 318 319
    if (v4l2_ioctl (fd, VIDIOC_S_CROP, &crop) < 0) {
      GST_WARNING_OBJECT (v4l2sink, "VIDIOC_S_CROP failed");
      return;
    }
320 321 322 323 324 325

    v4l2sink->crop_fields_set = 0;
    v4l2sink->crop = crop.c;
  }
}

Rob Clark's avatar
Rob Clark committed
326 327 328 329 330 331 332 333 334 335 336 337

static void
gst_v4l2sink_set_property (GObject * object,
    guint prop_id, const GValue * value, GParamSpec * pspec)
{
  GstV4l2Sink *v4l2sink = GST_V4L2SINK (object);

  if (!gst_v4l2_object_set_property_helper (v4l2sink->v4l2object,
          prop_id, value, pspec)) {
    switch (prop_id) {
      case PROP_OVERLAY_TOP:
        v4l2sink->overlay.top = g_value_get_int (value);
338
        v4l2sink->overlay_fields_set |= RECT_TOP_SET;
Rob Clark's avatar
Rob Clark committed
339 340 341 342
        gst_v4l2sink_sync_overlay_fields (v4l2sink);
        break;
      case PROP_OVERLAY_LEFT:
        v4l2sink->overlay.left = g_value_get_int (value);
343
        v4l2sink->overlay_fields_set |= RECT_LEFT_SET;
Rob Clark's avatar
Rob Clark committed
344 345 346 347
        gst_v4l2sink_sync_overlay_fields (v4l2sink);
        break;
      case PROP_OVERLAY_WIDTH:
        v4l2sink->overlay.width = g_value_get_uint (value);
348
        v4l2sink->overlay_fields_set |= RECT_WIDTH_SET;
Rob Clark's avatar
Rob Clark committed
349 350 351 352
        gst_v4l2sink_sync_overlay_fields (v4l2sink);
        break;
      case PROP_OVERLAY_HEIGHT:
        v4l2sink->overlay.height = g_value_get_uint (value);
353
        v4l2sink->overlay_fields_set |= RECT_HEIGHT_SET;
Rob Clark's avatar
Rob Clark committed
354 355
        gst_v4l2sink_sync_overlay_fields (v4l2sink);
        break;
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375
      case PROP_CROP_TOP:
        v4l2sink->crop.top = g_value_get_int (value);
        v4l2sink->crop_fields_set |= RECT_TOP_SET;
        gst_v4l2sink_sync_crop_fields (v4l2sink);
        break;
      case PROP_CROP_LEFT:
        v4l2sink->crop.left = g_value_get_int (value);
        v4l2sink->crop_fields_set |= RECT_LEFT_SET;
        gst_v4l2sink_sync_crop_fields (v4l2sink);
        break;
      case PROP_CROP_WIDTH:
        v4l2sink->crop.width = g_value_get_uint (value);
        v4l2sink->crop_fields_set |= RECT_WIDTH_SET;
        gst_v4l2sink_sync_crop_fields (v4l2sink);
        break;
      case PROP_CROP_HEIGHT:
        v4l2sink->crop.height = g_value_get_uint (value);
        v4l2sink->crop_fields_set |= RECT_HEIGHT_SET;
        gst_v4l2sink_sync_crop_fields (v4l2sink);
        break;
Rob Clark's avatar
Rob Clark committed
376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404
      default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
        break;
    }
  }
}


static void
gst_v4l2sink_get_property (GObject * object,
    guint prop_id, GValue * value, GParamSpec * pspec)
{
  GstV4l2Sink *v4l2sink = GST_V4L2SINK (object);

  if (!gst_v4l2_object_get_property_helper (v4l2sink->v4l2object,
          prop_id, value, pspec)) {
    switch (prop_id) {
      case PROP_OVERLAY_TOP:
        g_value_set_int (value, v4l2sink->overlay.top);
        break;
      case PROP_OVERLAY_LEFT:
        g_value_set_int (value, v4l2sink->overlay.left);
        break;
      case PROP_OVERLAY_WIDTH:
        g_value_set_uint (value, v4l2sink->overlay.width);
        break;
      case PROP_OVERLAY_HEIGHT:
        g_value_set_uint (value, v4l2sink->overlay.height);
        break;
405 406 407 408 409 410 411 412 413 414 415 416
      case PROP_CROP_TOP:
        g_value_set_int (value, v4l2sink->crop.top);
        break;
      case PROP_CROP_LEFT:
        g_value_set_int (value, v4l2sink->crop.left);
        break;
      case PROP_CROP_WIDTH:
        g_value_set_uint (value, v4l2sink->crop.width);
        break;
      case PROP_CROP_HEIGHT:
        g_value_set_uint (value, v4l2sink->crop.height);
        break;
Rob Clark's avatar
Rob Clark committed
417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
      default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
        break;
    }
  }
}

static GstStateChangeReturn
gst_v4l2sink_change_state (GstElement * element, GstStateChange transition)
{
  GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
  GstV4l2Sink *v4l2sink = GST_V4L2SINK (element);

  GST_DEBUG_OBJECT (v4l2sink, "%d -> %d",
      GST_STATE_TRANSITION_CURRENT (transition),
      GST_STATE_TRANSITION_NEXT (transition));

  switch (transition) {
    case GST_STATE_CHANGE_NULL_TO_READY:
      /* open the device */
437
      if (!gst_v4l2_object_open (v4l2sink->v4l2object))
Rob Clark's avatar
Rob Clark committed
438 439 440 441 442 443 444 445 446 447
        return GST_STATE_CHANGE_FAILURE;
      break;
    default:
      break;
  }

  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);

  switch (transition) {
    case GST_STATE_CHANGE_PAUSED_TO_READY:
448 449
      if (!gst_v4l2_object_stop (v4l2sink->v4l2object))
        return GST_STATE_CHANGE_FAILURE;
Rob Clark's avatar
Rob Clark committed
450 451
      break;
    case GST_STATE_CHANGE_READY_TO_NULL:
452 453 454
      /* we need to call stop here too */
      if (!gst_v4l2_object_stop (v4l2sink->v4l2object))
        return GST_STATE_CHANGE_FAILURE;
Rob Clark's avatar
Rob Clark committed
455
      /* close the device */
456
      if (!gst_v4l2_object_close (v4l2sink->v4l2object))
Rob Clark's avatar
Rob Clark committed
457 458 459 460 461 462 463 464 465 466 467
        return GST_STATE_CHANGE_FAILURE;
      break;
    default:
      break;
  }

  return ret;
}


static GstCaps *
Wim Taymans's avatar
Wim Taymans committed
468
gst_v4l2sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
Rob Clark's avatar
Rob Clark committed
469 470 471 472 473 474
{
  GstV4l2Sink *v4l2sink = GST_V4L2SINK (bsink);

  if (!GST_V4L2_IS_OPEN (v4l2sink->v4l2object)) {
    /* FIXME: copy? */
    GST_DEBUG_OBJECT (v4l2sink, "device is not open");
Wim Taymans's avatar
Wim Taymans committed
475
    return gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD (v4l2sink));
Rob Clark's avatar
Rob Clark committed
476 477 478
  }


479
  return gst_v4l2_object_get_caps (v4l2sink->v4l2object, filter);
Rob Clark's avatar
Rob Clark committed
480 481 482 483 484 485
}

static gboolean
gst_v4l2sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
{
  GstV4l2Sink *v4l2sink = GST_V4L2SINK (bsink);
486
  GstV4l2Object *obj = v4l2sink->v4l2object;
Rob Clark's avatar
Rob Clark committed
487 488 489 490 491 492 493 494

  LOG_CAPS (v4l2sink, caps);

  if (!GST_V4L2_IS_OPEN (v4l2sink->v4l2object)) {
    GST_DEBUG_OBJECT (v4l2sink, "device is not open");
    return FALSE;
  }

495 496 497 498
  /* make sure the caps changed before doing anything */
  if (gst_v4l2_object_caps_equal (obj, caps))
    return TRUE;

499 500
  if (!gst_v4l2_object_stop (obj))
    goto stop_failed;
Rob Clark's avatar
Rob Clark committed
501

502
  if (!gst_v4l2_object_set_format (v4l2sink->v4l2object, caps))
Wim Taymans's avatar
Wim Taymans committed
503 504 505 506 507 508 509
    goto invalid_format;

  gst_v4l2sink_sync_overlay_fields (v4l2sink);
  gst_v4l2sink_sync_crop_fields (v4l2sink);

  GST_INFO_OBJECT (v4l2sink, "outputting buffers via mmap()");

510 511
  v4l2sink->video_width = GST_V4L2_WIDTH (v4l2sink->v4l2object);
  v4l2sink->video_height = GST_V4L2_HEIGHT (v4l2sink->v4l2object);
Rob Clark's avatar
Rob Clark committed
512 513 514 515

  /* TODO: videosink width/height should be scaled according to
   * pixel-aspect-ratio
   */
516 517
  GST_VIDEO_SINK_WIDTH (v4l2sink) = v4l2sink->video_width;
  GST_VIDEO_SINK_HEIGHT (v4l2sink) = v4l2sink->video_height;
Rob Clark's avatar
Rob Clark committed
518

Rob Clark's avatar
Rob Clark committed
519 520
  return TRUE;

Wim Taymans's avatar
Wim Taymans committed
521 522 523 524 525 526 527 528 529 530 531 532
  /* ERRORS */
stop_failed:
  {
    GST_DEBUG_OBJECT (v4l2sink, "failed to stop streaming");
    return FALSE;
  }
invalid_format:
  {
    /* error already posted */
    GST_DEBUG_OBJECT (v4l2sink, "can't set format");
    return FALSE;
  }
Rob Clark's avatar
Rob Clark committed
533 534
}

535
static gboolean
536
gst_v4l2sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
537
{
538
  GstV4l2Sink *v4l2sink = GST_V4L2SINK (bsink);
539
  GstV4l2Object *obj = v4l2sink->v4l2object;
540
  GstBufferPool *pool;
541
  guint size = 0;
542 543
  GstCaps *caps;
  gboolean need_pool;
544

545
  gst_query_parse_allocation (query, &caps, &need_pool);
546

547 548 549
  if (caps == NULL)
    goto no_caps;

550
  if ((pool = obj->pool))
551 552 553
    gst_object_ref (pool);

  if (pool != NULL) {
Wim Taymans's avatar
Wim Taymans committed
554
    GstCaps *pcaps;
555
    GstStructure *config;
556 557 558

    /* we had a pool, check caps */
    config = gst_buffer_pool_get_config (pool);
Wim Taymans's avatar
Wim Taymans committed
559
    gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL);
560 561 562 563

    GST_DEBUG_OBJECT (v4l2sink,
        "we had a pool with caps %" GST_PTR_FORMAT, pcaps);
    if (!gst_caps_is_equal (caps, pcaps)) {
Wim Taymans's avatar
Wim Taymans committed
564
      gst_structure_free (config);
565 566
      gst_object_unref (pool);
      goto different_caps;
567
    }
Wim Taymans's avatar
Wim Taymans committed
568
    gst_structure_free (config);
569
  }
Wim Taymans's avatar
Wim Taymans committed
570
  /* we need at least 2 buffers to operate */
571
  gst_query_add_allocation_pool (query, pool, size, 2, 0);
572 573

  /* we also support various metadata */
Wim Taymans's avatar
Wim Taymans committed
574 575
  gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
  gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
576 577 578 579 580

  if (pool)
    gst_object_unref (pool);

  return TRUE;
581 582 583 584

  /* ERRORS */
no_caps:
  {
585
    GST_DEBUG_OBJECT (v4l2sink, "no caps specified");
586 587 588 589 590 591 592 593 594 595
    return FALSE;
  }
different_caps:
  {
    /* different caps, we can't use this pool */
    GST_DEBUG_OBJECT (v4l2sink, "pool has different caps");
    return FALSE;
  }
}

Stefan Kost's avatar
Stefan Kost committed
596
/* called after A/V sync to render frame */
Rob Clark's avatar
Rob Clark committed
597 598 599
static GstFlowReturn
gst_v4l2sink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
{
600
  GstFlowReturn ret;
Rob Clark's avatar
Rob Clark committed
601
  GstV4l2Sink *v4l2sink = GST_V4L2SINK (bsink);
602
  GstV4l2Object *obj = v4l2sink->v4l2object;
Rob Clark's avatar
Rob Clark committed
603

604
  GST_DEBUG_OBJECT (v4l2sink, "render buffer: %p", buf);
Rob Clark's avatar
Rob Clark committed
605

Wim Taymans's avatar
Wim Taymans committed
606 607 608
  if (G_UNLIKELY (obj->pool == NULL))
    goto not_negotiated;

609 610
  ret =
      gst_v4l2_buffer_pool_process (GST_V4L2_BUFFER_POOL_CAST (obj->pool), buf);
611

612
  return ret;
Wim Taymans's avatar
Wim Taymans committed
613 614 615 616 617 618 619

  /* ERRORS */
not_negotiated:
  {
    GST_ERROR_OBJECT (bsink, "not negotiated");
    return GST_FLOW_NOT_NEGOTIATED;
  }
Rob Clark's avatar
Rob Clark committed
620
}