int v4lconvert_convert(struct v4lconvert_data *data, const struct v4l2_format *src_fmt, /* in */ const struct v4l2_format *dest_fmt, /* in */ unsigned char *src, int src_size, unsigned char *dest, int dest_size) { int res, dest_needed, temp_needed, convert = 0, rotate = 0, crop = 0; unsigned char *convert_dest = dest, *rotate_src = src, *rotate_dest = dest; unsigned char *crop_src = src; struct v4l2_format my_src_fmt = *src_fmt; struct v4l2_format my_dest_fmt = *dest_fmt; /* Special case when no conversion is needed */ if (!v4lconvert_needs_conversion(data, src_fmt, dest_fmt)) { int to_copy = MIN(dest_size, src_size); memcpy(dest, src, to_copy); return to_copy; } /* When field is V4L2_FIELD_ALTERNATE, each buffer only contains half the lines */ if (my_src_fmt.fmt.pix.field == V4L2_FIELD_ALTERNATE) { my_src_fmt.fmt.pix.height /= 2; my_dest_fmt.fmt.pix.height /= 2; } /* sanity check, is the dest buffer large enough? */ switch (my_dest_fmt.fmt.pix.pixelformat) { case V4L2_PIX_FMT_RGB24: case V4L2_PIX_FMT_BGR24: dest_needed = my_dest_fmt.fmt.pix.width * my_dest_fmt.fmt.pix.height * 3; temp_needed = my_src_fmt.fmt.pix.width * my_src_fmt.fmt.pix.height * 3; break; case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: dest_needed = my_dest_fmt.fmt.pix.width * my_dest_fmt.fmt.pix.height * 3 / 2; temp_needed = my_src_fmt.fmt.pix.width * my_src_fmt.fmt.pix.height * 3 / 2; break; default: V4LCONVERT_ERR("Unknown dest format in conversion\n"); errno = EINVAL; return -1; } if (dest_size < dest_needed) { V4LCONVERT_ERR("destination buffer too small\n"); errno = EFAULT; return -1; } if (my_dest_fmt.fmt.pix.pixelformat != my_src_fmt.fmt.pix.pixelformat) convert = 1; if (data->flags & V4LCONVERT_ROTATE_90) rotate += 90; if (data->flags & V4LCONVERT_ROTATE_180) rotate += 180; if (my_dest_fmt.fmt.pix.width != my_src_fmt.fmt.pix.width || my_dest_fmt.fmt.pix.height != my_src_fmt.fmt.pix.height) crop = 1; /* convert_pixfmt -> rotate -> crop, all steps are optional */ if (convert && (rotate || crop)) { convert_dest = v4lconvert_alloc_buffer(data, temp_needed, &data->convert_buf, &data->convert_buf_size); if (!convert_dest) return -1; rotate_src = crop_src = convert_dest; } if (rotate && crop) { rotate_dest = v4lconvert_alloc_buffer(data, temp_needed, &data->rotate_buf, &data->rotate_buf_size); if (!rotate_dest) return -1; crop_src = rotate_dest; } if (convert) { res = v4lconvert_convert_pixfmt(data, src, src_size, convert_dest, &my_src_fmt, my_dest_fmt.fmt.pix.pixelformat); if (res) return res; my_src_fmt.fmt.pix.pixelformat = my_dest_fmt.fmt.pix.pixelformat; v4lconvert_fixup_fmt(&my_src_fmt); } if (rotate) v4lconvert_rotate(rotate_src, rotate_dest, my_src_fmt.fmt.pix.width, my_src_fmt.fmt.pix.height, my_src_fmt.fmt.pix.pixelformat, rotate); if (crop) v4lconvert_crop(crop_src, dest, &my_src_fmt, &my_dest_fmt); return dest_needed; }
int v4lconvert_convert(struct v4lconvert_data *data, const struct v4l2_format *src_fmt, /* in */ const struct v4l2_format *dest_fmt, /* in */ unsigned char *src, int src_size, unsigned char *dest, int dest_size) { int res, dest_needed, temp_needed, processing, convert = 0; int rotate90, vflip, hflip, crop; unsigned char *convert1_dest = dest; int convert1_dest_size = dest_size; unsigned char *convert2_src = src, *convert2_dest = dest; int convert2_dest_size = dest_size; unsigned char *rotate90_src = src, *rotate90_dest = dest; unsigned char *flip_src = src, *flip_dest = dest; unsigned char *crop_src = src; struct v4l2_format my_src_fmt = *src_fmt; struct v4l2_format my_dest_fmt = *dest_fmt; processing = v4lprocessing_pre_processing(data->processing); rotate90 = data->control_flags & V4LCONTROL_ROTATED_90_JPEG; hflip = v4lcontrol_get_ctrl(data->control, V4LCONTROL_HFLIP); vflip = v4lcontrol_get_ctrl(data->control, V4LCONTROL_VFLIP); crop = my_dest_fmt.fmt.pix.width != my_src_fmt.fmt.pix.width || my_dest_fmt.fmt.pix.height != my_src_fmt.fmt.pix.height; if (/* If no conversion/processing is needed */ (src_fmt->fmt.pix.pixelformat == dest_fmt->fmt.pix.pixelformat && !processing && !rotate90 && !hflip && !vflip && !crop) || /* or if we should do processing/rotating/flipping but the app tries to use the native cam format, we just return an unprocessed frame copy */ !v4lconvert_supported_dst_format(dest_fmt->fmt.pix.pixelformat)) { int to_copy = MIN(dest_size, src_size); memcpy(dest, src, to_copy); return to_copy; } /* When field is V4L2_FIELD_ALTERNATE, each buffer only contains half the lines */ if (my_src_fmt.fmt.pix.field == V4L2_FIELD_ALTERNATE) { my_src_fmt.fmt.pix.height /= 2; my_dest_fmt.fmt.pix.height /= 2; } /* sanity check, is the dest buffer large enough? */ switch (my_dest_fmt.fmt.pix.pixelformat) { case V4L2_PIX_FMT_RGB24: case V4L2_PIX_FMT_BGR24: dest_needed = my_dest_fmt.fmt.pix.width * my_dest_fmt.fmt.pix.height * 3; temp_needed = my_src_fmt.fmt.pix.width * my_src_fmt.fmt.pix.height * 3; break; case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: dest_needed = my_dest_fmt.fmt.pix.width * my_dest_fmt.fmt.pix.height * 3 / 2; temp_needed = my_src_fmt.fmt.pix.width * my_src_fmt.fmt.pix.height * 3 / 2; break; default: V4LCONVERT_ERR("Unknown dest format in conversion\n"); errno = EINVAL; return -1; } if (dest_size < dest_needed) { V4LCONVERT_ERR("destination buffer too small (%d < %d)\n", dest_size, dest_needed); errno = EFAULT; return -1; } /* Sometimes we need foo -> rgb -> bar as video processing (whitebalance, etc.) can only be done on rgb data */ if (processing && v4lconvert_processing_needs_double_conversion( my_src_fmt.fmt.pix.pixelformat, my_dest_fmt.fmt.pix.pixelformat)) convert = 2; else if (my_dest_fmt.fmt.pix.pixelformat != my_src_fmt.fmt.pix.pixelformat || /* Special case if we do not need to do conversion, but we are not doing any other step involving copying either, force going through convert_pixfmt to copy the data from source to dest */ (!rotate90 && !hflip && !vflip && !crop)) convert = 1; /* convert_pixfmt (only if convert == 2) -> processing -> convert_pixfmt -> rotate -> flip -> crop, all steps are optional */ if (convert == 2) { convert1_dest = v4lconvert_alloc_buffer( my_src_fmt.fmt.pix.width * my_src_fmt.fmt.pix.height * 3, &data->convert1_buf, &data->convert1_buf_size); if (!convert1_dest) return v4lconvert_oom_error(data); convert1_dest_size = my_src_fmt.fmt.pix.width * my_src_fmt.fmt.pix.height * 3; convert2_src = convert1_dest; } if (convert && (rotate90 || hflip || vflip || crop)) { convert2_dest = v4lconvert_alloc_buffer(temp_needed, &data->convert2_buf, &data->convert2_buf_size); if (!convert2_dest) return v4lconvert_oom_error(data); convert2_dest_size = temp_needed; rotate90_src = flip_src = crop_src = convert2_dest; } if (rotate90 && (hflip || vflip || crop)) { rotate90_dest = v4lconvert_alloc_buffer(temp_needed, &data->rotate90_buf, &data->rotate90_buf_size); if (!rotate90_dest) return v4lconvert_oom_error(data); flip_src = crop_src = rotate90_dest; } if ((vflip || hflip) && crop) { flip_dest = v4lconvert_alloc_buffer(temp_needed, &data->flip_buf, &data->flip_buf_size); if (!flip_dest) return v4lconvert_oom_error(data); crop_src = flip_dest; } /* Done setting sources / dest and allocating intermediate buffers, real conversion / processing / ... starts here. */ if (convert == 2) { res = v4lconvert_convert_pixfmt(data, src, src_size, convert1_dest, convert1_dest_size, &my_src_fmt, V4L2_PIX_FMT_RGB24); if (res) return res; src_size = my_src_fmt.fmt.pix.sizeimage; } if (processing) v4lprocessing_processing(data->processing, convert2_src, &my_src_fmt); if (convert) { res = v4lconvert_convert_pixfmt(data, convert2_src, src_size, convert2_dest, convert2_dest_size, &my_src_fmt, my_dest_fmt.fmt.pix.pixelformat); if (res) return res; src_size = my_src_fmt.fmt.pix.sizeimage; /* We call processing here again in case the source format was not rgb, but the dest is. v4lprocessing checks it self it only actually does the processing once per frame. */ if (processing) v4lprocessing_processing(data->processing, convert2_dest, &my_src_fmt); } if (rotate90) v4lconvert_rotate90(rotate90_src, rotate90_dest, &my_src_fmt); if (hflip || vflip) v4lconvert_flip(flip_src, flip_dest, &my_src_fmt, hflip, vflip); if (crop) v4lconvert_crop(crop_src, dest, &my_src_fmt, &my_dest_fmt); return dest_needed; }