Exemplo n.º 1
0
static int v4lconvert_do_try_format(struct v4lconvert_data *data,
  struct v4l2_format *dest_fmt, struct v4l2_format *src_fmt)
{
  int i;
  unsigned int closest_fmt_size_diff = -1;
  unsigned int desired_pixfmt = dest_fmt->fmt.pix.pixelformat;
  struct v4l2_format try_fmt, closest_fmt = { .type = 0 };

  if (data->flags & V4LCONVERT_IS_UVC)
    return v4lconvert_do_try_format_uvc(data, dest_fmt, src_fmt);

  for (i = 0; i < ARRAY_SIZE(supported_src_pixfmts); i++) {
    /* is this format supported? */
    if (!(data->supported_src_formats & (1 << i)))
      continue;

    try_fmt = *dest_fmt;
    try_fmt.fmt.pix.pixelformat = supported_src_pixfmts[i].fmt;

    if (!syscall(SYS_ioctl, data->fd, VIDIOC_TRY_FMT, &try_fmt))
    {
      if (try_fmt.fmt.pix.pixelformat == supported_src_pixfmts[i].fmt) {
	int size_x_diff = abs((int)try_fmt.fmt.pix.width -
			      (int)dest_fmt->fmt.pix.width);
	int size_y_diff = abs((int)try_fmt.fmt.pix.height -
			      (int)dest_fmt->fmt.pix.height);
	unsigned int size_diff = size_x_diff * size_x_diff +
				 size_y_diff * size_y_diff;
	if (size_diff < closest_fmt_size_diff ||
	    (size_diff == closest_fmt_size_diff &&
	     (supported_src_pixfmts[i].fmt == desired_pixfmt ||
	      ((try_fmt.fmt.pix.width > 180 || try_fmt.fmt.pix.height > 148) &&
	       (supported_src_pixfmts[i].flags & V4LCONVERT_COMPRESSED))))) {
	  closest_fmt_size_diff = size_diff;
	  closest_fmt = try_fmt;
	}
      }
    }
  }

  if (closest_fmt.type == 0)
    return -1;

  *dest_fmt = closest_fmt;
  if (closest_fmt.fmt.pix.pixelformat != desired_pixfmt)
    dest_fmt->fmt.pix.pixelformat = desired_pixfmt;
  *src_fmt = closest_fmt;

  return 0;
}

static void v4lconvert_fixup_fmt(struct v4l2_format *fmt)
{
  switch (fmt->fmt.pix.pixelformat) {
  case V4L2_PIX_FMT_RGB24:
  case V4L2_PIX_FMT_BGR24:
    fmt->fmt.pix.bytesperline = fmt->fmt.pix.width * 3;
    fmt->fmt.pix.sizeimage = fmt->fmt.pix.width * fmt->fmt.pix.height * 3;
    break;
  case V4L2_PIX_FMT_YUV420:
  case V4L2_PIX_FMT_YVU420:
    fmt->fmt.pix.bytesperline = fmt->fmt.pix.width;
    fmt->fmt.pix.sizeimage = fmt->fmt.pix.width * fmt->fmt.pix.height * 3 / 2;
    break;
  }
}

/* See libv4lconvert.h for description of in / out parameters */
int v4lconvert_try_format(struct v4lconvert_data *data,
  struct v4l2_format *dest_fmt, struct v4l2_format *src_fmt)
{
  int i, result;
  unsigned int desired_width = dest_fmt->fmt.pix.width;
  unsigned int desired_height = dest_fmt->fmt.pix.height;
  struct v4l2_format try_src, try_dest = *dest_fmt;

  /* Can we do conversion to the requested format & type? */
  if (!v4lconvert_supported_dst_format(dest_fmt->fmt.pix.pixelformat) ||
      dest_fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
      v4lconvert_do_try_format(data, &try_dest, &try_src)) {
    result = syscall(SYS_ioctl, data->fd, VIDIOC_TRY_FMT, dest_fmt);
    if (src_fmt)
      *src_fmt = *dest_fmt;
    return result;
  }

  /* In case of a non exact resolution match, see if this is a well known
     resolution some apps are hardcoded too and try to give the app what it
     asked for by cropping a slightly larger resolution */
  if (try_dest.fmt.pix.width != desired_width ||
      try_dest.fmt.pix.height != desired_height) {
    for (i = 0; i < ARRAY_SIZE(v4lconvert_crop_res); i++) {
      if (v4lconvert_crop_res[i][0] == desired_width &&
	  v4lconvert_crop_res[i][1] == desired_height) {
	struct v4l2_format try2_src, try2_dest = *dest_fmt;

	/* Note these are chosen so that cropping to vga res just works for
	   vv6410 sensor cams, which have 356x292 and 180x148 */
	try2_dest.fmt.pix.width = desired_width * 113 / 100;
	try2_dest.fmt.pix.height = desired_height * 124 / 100;
	result = v4lconvert_do_try_format(data, &try2_dest, &try2_src);
	if (result == 0 &&
	    ((try2_dest.fmt.pix.width >= desired_width &&
	      try2_dest.fmt.pix.width <= desired_width * 5 / 4 &&
	      try2_dest.fmt.pix.height >= desired_height &&
	      try2_dest.fmt.pix.height <= desired_height * 5 / 4) ||
	     (try2_dest.fmt.pix.width >= desired_width * 2 &&
	      try2_dest.fmt.pix.width <= desired_width * 5 / 2 &&
	      try2_dest.fmt.pix.height >= desired_height &&
	      try2_dest.fmt.pix.height <= desired_height * 5 / 2))) {
	  /* Success! */
	  try2_dest.fmt.pix.width = desired_width;
	  try2_dest.fmt.pix.height = desired_height;
	  try_dest = try2_dest;
	  try_src = try2_src;
	}
	break;
      }
    }
  }

  /* Are we converting? */
  if(try_src.fmt.pix.width != try_dest.fmt.pix.width ||
     try_src.fmt.pix.height != try_dest.fmt.pix.height ||
     try_src.fmt.pix.pixelformat != try_dest.fmt.pix.pixelformat)
    v4lconvert_fixup_fmt(&try_dest);

  *dest_fmt = try_dest;
  if (src_fmt)
    *src_fmt = try_src;

  return 0;
}
Exemplo n.º 2
0
static int v4lconvert_do_try_format(struct v4lconvert_data *data,
		struct v4l2_format *dest_fmt, struct v4l2_format *src_fmt)
{
	int i, size_x_diff, size_y_diff, rank, best_rank = 0;
	unsigned int size_diff, closest_fmt_size_diff = -1;
	unsigned int desired_pixfmt = dest_fmt->fmt.pix.pixelformat;
	struct v4l2_format try_fmt, closest_fmt = { .type = 0 };

	if (data->flags & V4LCONVERT_IS_UVC)
		return v4lconvert_do_try_format_uvc(data, dest_fmt, src_fmt);

	for (i = 0; i < ARRAY_SIZE(supported_src_pixfmts); i++) {
		/* is this format supported? */
		if (!(data->supported_src_formats & (1 << i)))
			continue;

		try_fmt = *dest_fmt;
		try_fmt.fmt.pix.pixelformat = supported_src_pixfmts[i].fmt;
		if (SYS_IOCTL(data->fd, VIDIOC_TRY_FMT, &try_fmt))
			continue;

		if (try_fmt.fmt.pix.pixelformat !=
		    supported_src_pixfmts[i].fmt)
			continue;

		/* Did we get a better match then before? */
		size_x_diff = (int)try_fmt.fmt.pix.width -
			      (int)dest_fmt->fmt.pix.width;
		size_y_diff = (int)try_fmt.fmt.pix.height -
			      (int)dest_fmt->fmt.pix.height;
		size_diff = size_x_diff * size_x_diff +
			    size_y_diff * size_y_diff;

		rank = v4lconvert_get_rank(data, i,
					   try_fmt.fmt.pix.width,
					   try_fmt.fmt.pix.height,
					   desired_pixfmt);
		if (size_diff < closest_fmt_size_diff ||
		    (size_diff == closest_fmt_size_diff && rank < best_rank)) {
			closest_fmt = try_fmt;
			closest_fmt_size_diff = size_diff;
			best_rank = rank;
		}
	}

	if (closest_fmt.type == 0)
		return -1;

	*dest_fmt = closest_fmt;
	if (closest_fmt.fmt.pix.pixelformat != desired_pixfmt)
		dest_fmt->fmt.pix.pixelformat = desired_pixfmt;
	*src_fmt = closest_fmt;

	return 0;
}

void v4lconvert_fixup_fmt(struct v4l2_format *fmt)
{
	switch (fmt->fmt.pix.pixelformat) {
	case V4L2_PIX_FMT_RGB24:
	case V4L2_PIX_FMT_BGR24:
		fmt->fmt.pix.bytesperline = fmt->fmt.pix.width * 3;
		fmt->fmt.pix.sizeimage = fmt->fmt.pix.width * fmt->fmt.pix.height * 3;
		break;
	case V4L2_PIX_FMT_YUV420:
	case V4L2_PIX_FMT_YVU420:
		fmt->fmt.pix.bytesperline = fmt->fmt.pix.width;
		fmt->fmt.pix.sizeimage = fmt->fmt.pix.width * fmt->fmt.pix.height * 3 / 2;
		break;
	}
}

/* See libv4lconvert.h for description of in / out parameters */
int v4lconvert_try_format(struct v4lconvert_data *data,
		struct v4l2_format *dest_fmt, struct v4l2_format *src_fmt)
{
	int i, result;
	unsigned int desired_width = dest_fmt->fmt.pix.width;
	unsigned int desired_height = dest_fmt->fmt.pix.height;
	struct v4l2_format try_src, try_dest, try2_src, try2_dest;

	if (dest_fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
			v4lconvert_supported_dst_fmt_only(data) &&
			!v4lconvert_supported_dst_format(dest_fmt->fmt.pix.pixelformat))
		dest_fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24;

	try_dest = *dest_fmt;

	/* Can we do conversion to the requested format & type? */
	if (!v4lconvert_supported_dst_format(dest_fmt->fmt.pix.pixelformat) ||
			dest_fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
			v4lconvert_do_try_format(data, &try_dest, &try_src)) {
		result = SYS_IOCTL(data->fd, VIDIOC_TRY_FMT, dest_fmt);
		if (src_fmt)
			*src_fmt = *dest_fmt;
		return result;
	}

	/* In case of a non exact resolution match, try again with a slightly larger
	   resolution as some weird devices are not able to crop of the number of
	   extra (border) pixels most sensors have compared to standard resolutions,
	   which we will then just crop of in software */
	if (try_dest.fmt.pix.width != desired_width ||
			try_dest.fmt.pix.height != desired_height) {
		try2_dest = *dest_fmt;
		try2_dest.fmt.pix.width  = desired_width + 7;
		try2_dest.fmt.pix.height = desired_height + 1;
		result = v4lconvert_do_try_format(data, &try2_dest, &try2_src);
		if (result == 0 &&
				try2_dest.fmt.pix.width >= desired_width &&
				try2_dest.fmt.pix.width <= desired_width + 7 &&
				try2_dest.fmt.pix.height >= desired_height &&
				try2_dest.fmt.pix.height <= desired_height + 1) {
			/* Success! */
			try2_dest.fmt.pix.width = desired_width;
			try2_dest.fmt.pix.height = desired_height;
			try_dest = try2_dest;
			try_src = try2_src;
		}
	}

	/* In case of a non exact resolution match, see if this is a well known
	   resolution some apps are hardcoded too and try to give the app what it
	   asked for by cropping a slightly larger resolution or adding a small
	   black border to a slightly smaller resolution */
	if (try_dest.fmt.pix.width != desired_width ||
			try_dest.fmt.pix.height != desired_height) {
		for (i = 0; i < ARRAY_SIZE(v4lconvert_crop_res); i++) {
			if (v4lconvert_crop_res[i][0] == desired_width &&
					v4lconvert_crop_res[i][1] == desired_height) {
				try2_dest = *dest_fmt;

				/* Note these are chosen so that cropping to vga res just works for
				   vv6410 sensor cams, which have 356x292 and 180x148 */
				try2_dest.fmt.pix.width = desired_width * 113 / 100;
				try2_dest.fmt.pix.height = desired_height * 124 / 100;
				result = v4lconvert_do_try_format(data, &try2_dest, &try2_src);
				if (result == 0 &&
						(/* Add a small black border of max 16 pixels */
						 (try2_dest.fmt.pix.width >= desired_width - 16 &&
						  try2_dest.fmt.pix.width <= desired_width &&
						  try2_dest.fmt.pix.height >= desired_height - 16 &&
						  try2_dest.fmt.pix.height <= desired_height) ||
						 /* Standard cropping to max 80% of actual width / height */
						 (try2_dest.fmt.pix.width >= desired_width &&
						  try2_dest.fmt.pix.width <= desired_width * 5 / 4 &&
						  try2_dest.fmt.pix.height >= desired_height &&
						  try2_dest.fmt.pix.height <= desired_height * 5 / 4) ||
						 /* Downscale 2x + cropping to max 80% of actual width / height */
						 (try2_dest.fmt.pix.width >= desired_width * 2 &&
						  try2_dest.fmt.pix.width <= desired_width * 5 / 2 &&
						  try2_dest.fmt.pix.height >= desired_height * 2 &&
						  try2_dest.fmt.pix.height <= desired_height * 5 / 2))) {
					/* Success! */
					try2_dest.fmt.pix.width = desired_width;
					try2_dest.fmt.pix.height = desired_height;
					try_dest = try2_dest;
					try_src = try2_src;
				}
				break;
			}
		}
	}

	/* Some applications / libs (*cough* gstreamer *cough*) will not work
	   correctly with planar YUV formats when the width is not a multiple of 8
	   or the height is not a multiple of 2. With RGB formats these apps require
	   the width to be a multiple of 4. We apply the same rounding to all
	   formats to not end up with 2 close but different resolutions. */
	try_dest.fmt.pix.width &= ~7;
	try_dest.fmt.pix.height &= ~1;

	/* Are we converting / cropping ? */
	if (try_src.fmt.pix.width != try_dest.fmt.pix.width ||
			try_src.fmt.pix.height != try_dest.fmt.pix.height ||
			try_src.fmt.pix.pixelformat != try_dest.fmt.pix.pixelformat)
		v4lconvert_fixup_fmt(&try_dest);

	*dest_fmt = try_dest;
	if (src_fmt)
		*src_fmt = try_src;

	return 0;
}