Contributors: 2
Author Tokens Token Proportion Commits Commit Proportion
Hans Verkuil 2130 55.10% 4 30.77%
Dafna Hirschfeld 1736 44.90% 9 69.23%
Total 3866 13


// SPDX-License-Identifier: LGPL-2.1
/*
 * A V4L2 frontend for the FWHT codec
 *
 * Copyright 2018 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
 */

#include <linux/errno.h>
#include <linux/string.h>
#include <linux/videodev2.h>
#include "codec-v4l2-fwht.h"

static const struct v4l2_fwht_pixfmt_info v4l2_fwht_pixfmts[] = {
	{ V4L2_PIX_FMT_YUV420,  1, 3, 2, 1, 1, 2, 2, 3, 3, FWHT_FL_PIXENC_YUV},
	{ V4L2_PIX_FMT_YVU420,  1, 3, 2, 1, 1, 2, 2, 3, 3, FWHT_FL_PIXENC_YUV},
	{ V4L2_PIX_FMT_YUV422P, 1, 2, 1, 1, 1, 2, 1, 3, 3, FWHT_FL_PIXENC_YUV},
	{ V4L2_PIX_FMT_NV12,    1, 3, 2, 1, 2, 2, 2, 3, 2, FWHT_FL_PIXENC_YUV},
	{ V4L2_PIX_FMT_NV21,    1, 3, 2, 1, 2, 2, 2, 3, 2, FWHT_FL_PIXENC_YUV},
	{ V4L2_PIX_FMT_NV16,    1, 2, 1, 1, 2, 2, 1, 3, 2, FWHT_FL_PIXENC_YUV},
	{ V4L2_PIX_FMT_NV61,    1, 2, 1, 1, 2, 2, 1, 3, 2, FWHT_FL_PIXENC_YUV},
	{ V4L2_PIX_FMT_NV24,    1, 3, 1, 1, 2, 1, 1, 3, 2, FWHT_FL_PIXENC_YUV},
	{ V4L2_PIX_FMT_NV42,    1, 3, 1, 1, 2, 1, 1, 3, 2, FWHT_FL_PIXENC_YUV},
	{ V4L2_PIX_FMT_YUYV,    2, 2, 1, 2, 4, 2, 1, 3, 1, FWHT_FL_PIXENC_YUV},
	{ V4L2_PIX_FMT_YVYU,    2, 2, 1, 2, 4, 2, 1, 3, 1, FWHT_FL_PIXENC_YUV},
	{ V4L2_PIX_FMT_UYVY,    2, 2, 1, 2, 4, 2, 1, 3, 1, FWHT_FL_PIXENC_YUV},
	{ V4L2_PIX_FMT_VYUY,    2, 2, 1, 2, 4, 2, 1, 3, 1, FWHT_FL_PIXENC_YUV},
	{ V4L2_PIX_FMT_BGR24,   3, 3, 1, 3, 3, 1, 1, 3, 1, FWHT_FL_PIXENC_RGB},
	{ V4L2_PIX_FMT_RGB24,   3, 3, 1, 3, 3, 1, 1, 3, 1, FWHT_FL_PIXENC_RGB},
	{ V4L2_PIX_FMT_HSV24,   3, 3, 1, 3, 3, 1, 1, 3, 1, FWHT_FL_PIXENC_HSV},
	{ V4L2_PIX_FMT_BGR32,   4, 4, 1, 4, 4, 1, 1, 3, 1, FWHT_FL_PIXENC_RGB},
	{ V4L2_PIX_FMT_XBGR32,  4, 4, 1, 4, 4, 1, 1, 3, 1, FWHT_FL_PIXENC_RGB},
	{ V4L2_PIX_FMT_RGB32,   4, 4, 1, 4, 4, 1, 1, 3, 1, FWHT_FL_PIXENC_RGB},
	{ V4L2_PIX_FMT_XRGB32,  4, 4, 1, 4, 4, 1, 1, 3, 1, FWHT_FL_PIXENC_RGB},
	{ V4L2_PIX_FMT_HSV32,   4, 4, 1, 4, 4, 1, 1, 3, 1, FWHT_FL_PIXENC_HSV},
	{ V4L2_PIX_FMT_ARGB32,  4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB},
	{ V4L2_PIX_FMT_ABGR32,  4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB},
	{ V4L2_PIX_FMT_GREY,    1, 1, 1, 1, 0, 1, 1, 1, 1, FWHT_FL_PIXENC_RGB},
};

const struct v4l2_fwht_pixfmt_info *v4l2_fwht_default_fmt(u32 width_div,
							  u32 height_div,
							  u32 components_num,
							  u32 pixenc,
							  unsigned int start_idx)
{
	unsigned int i;

	for (i = 0; i < ARRAY_SIZE(v4l2_fwht_pixfmts); i++) {
		if (v4l2_fwht_pixfmts[i].width_div == width_div &&
		    v4l2_fwht_pixfmts[i].height_div == height_div &&
		    (!pixenc || v4l2_fwht_pixfmts[i].pixenc == pixenc) &&
		    v4l2_fwht_pixfmts[i].components_num == components_num) {
			if (start_idx == 0)
				return v4l2_fwht_pixfmts + i;
			start_idx--;
		}
	}
	return NULL;
}

const struct v4l2_fwht_pixfmt_info *v4l2_fwht_find_pixfmt(u32 pixelformat)
{
	unsigned int i;

	for (i = 0; i < ARRAY_SIZE(v4l2_fwht_pixfmts); i++)
		if (v4l2_fwht_pixfmts[i].id == pixelformat)
			return v4l2_fwht_pixfmts + i;
	return NULL;
}

const struct v4l2_fwht_pixfmt_info *v4l2_fwht_get_pixfmt(u32 idx)
{
	if (idx >= ARRAY_SIZE(v4l2_fwht_pixfmts))
		return NULL;
	return v4l2_fwht_pixfmts + idx;
}

int v4l2_fwht_encode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out)
{
	unsigned int size = state->stride * state->coded_height;
	unsigned int chroma_stride = state->stride;
	const struct v4l2_fwht_pixfmt_info *info = state->info;
	struct fwht_cframe_hdr *p_hdr;
	struct fwht_cframe cf;
	struct fwht_raw_frame rf;
	u32 encoding;
	u32 flags = 0;

	if (!info)
		return -EINVAL;

	rf.luma = p_in;
	rf.width_div = info->width_div;
	rf.height_div = info->height_div;
	rf.luma_alpha_step = info->luma_alpha_step;
	rf.chroma_step = info->chroma_step;
	rf.alpha = NULL;
	rf.components_num = info->components_num;

	switch (info->id) {
	case V4L2_PIX_FMT_GREY:
		rf.cb = NULL;
		rf.cr = NULL;
		break;
	case V4L2_PIX_FMT_YUV420:
		rf.cb = rf.luma + size;
		rf.cr = rf.cb + size / 4;
		chroma_stride /= 2;
		break;
	case V4L2_PIX_FMT_YVU420:
		rf.cr = rf.luma + size;
		rf.cb = rf.cr + size / 4;
		chroma_stride /= 2;
		break;
	case V4L2_PIX_FMT_YUV422P:
		rf.cb = rf.luma + size;
		rf.cr = rf.cb + size / 2;
		chroma_stride /= 2;
		break;
	case V4L2_PIX_FMT_NV12:
	case V4L2_PIX_FMT_NV16:
	case V4L2_PIX_FMT_NV24:
		rf.cb = rf.luma + size;
		rf.cr = rf.cb + 1;
		break;
	case V4L2_PIX_FMT_NV21:
	case V4L2_PIX_FMT_NV61:
	case V4L2_PIX_FMT_NV42:
		rf.cr = rf.luma + size;
		rf.cb = rf.cr + 1;
		break;
	case V4L2_PIX_FMT_YUYV:
		rf.cb = rf.luma + 1;
		rf.cr = rf.cb + 2;
		break;
	case V4L2_PIX_FMT_YVYU:
		rf.cr = rf.luma + 1;
		rf.cb = rf.cr + 2;
		break;
	case V4L2_PIX_FMT_UYVY:
		rf.cb = rf.luma;
		rf.cr = rf.cb + 2;
		rf.luma++;
		break;
	case V4L2_PIX_FMT_VYUY:
		rf.cr = rf.luma;
		rf.cb = rf.cr + 2;
		rf.luma++;
		break;
	case V4L2_PIX_FMT_RGB24:
	case V4L2_PIX_FMT_HSV24:
		rf.cr = rf.luma;
		rf.cb = rf.cr + 2;
		rf.luma++;
		break;
	case V4L2_PIX_FMT_BGR24:
		rf.cb = rf.luma;
		rf.cr = rf.cb + 2;
		rf.luma++;
		break;
	case V4L2_PIX_FMT_RGB32:
	case V4L2_PIX_FMT_XRGB32:
	case V4L2_PIX_FMT_HSV32:
		rf.cr = rf.luma + 1;
		rf.cb = rf.cr + 2;
		rf.luma += 2;
		break;
	case V4L2_PIX_FMT_BGR32:
	case V4L2_PIX_FMT_XBGR32:
		rf.cb = rf.luma;
		rf.cr = rf.cb + 2;
		rf.luma++;
		break;
	case V4L2_PIX_FMT_ARGB32:
		rf.alpha = rf.luma;
		rf.cr = rf.luma + 1;
		rf.cb = rf.cr + 2;
		rf.luma += 2;
		break;
	case V4L2_PIX_FMT_ABGR32:
		rf.cb = rf.luma;
		rf.cr = rf.cb + 2;
		rf.luma++;
		rf.alpha = rf.cr + 1;
		break;
	default:
		return -EINVAL;
	}

	cf.i_frame_qp = state->i_frame_qp;
	cf.p_frame_qp = state->p_frame_qp;
	cf.rlc_data = (__be16 *)(p_out + sizeof(*p_hdr));

	encoding = fwht_encode_frame(&rf, &state->ref_frame, &cf,
				     !state->gop_cnt,
				     state->gop_cnt == state->gop_size - 1,
				     state->visible_width,
				     state->visible_height,
				     state->stride, chroma_stride);
	if (!(encoding & FWHT_FRAME_PCODED))
		state->gop_cnt = 0;
	if (++state->gop_cnt >= state->gop_size)
		state->gop_cnt = 0;

	p_hdr = (struct fwht_cframe_hdr *)p_out;
	p_hdr->magic1 = FWHT_MAGIC1;
	p_hdr->magic2 = FWHT_MAGIC2;
	p_hdr->version = htonl(FWHT_VERSION);
	p_hdr->width = htonl(state->visible_width);
	p_hdr->height = htonl(state->visible_height);
	flags |= (info->components_num - 1) << FWHT_FL_COMPONENTS_NUM_OFFSET;
	flags |= info->pixenc;
	if (encoding & FWHT_LUMA_UNENCODED)
		flags |= FWHT_FL_LUMA_IS_UNCOMPRESSED;
	if (encoding & FWHT_CB_UNENCODED)
		flags |= FWHT_FL_CB_IS_UNCOMPRESSED;
	if (encoding & FWHT_CR_UNENCODED)
		flags |= FWHT_FL_CR_IS_UNCOMPRESSED;
	if (encoding & FWHT_ALPHA_UNENCODED)
		flags |= FWHT_FL_ALPHA_IS_UNCOMPRESSED;
	if (!(encoding & FWHT_FRAME_PCODED))
		flags |= FWHT_FL_I_FRAME;
	if (rf.height_div == 1)
		flags |= FWHT_FL_CHROMA_FULL_HEIGHT;
	if (rf.width_div == 1)
		flags |= FWHT_FL_CHROMA_FULL_WIDTH;
	p_hdr->flags = htonl(flags);
	p_hdr->colorspace = htonl(state->colorspace);
	p_hdr->xfer_func = htonl(state->xfer_func);
	p_hdr->ycbcr_enc = htonl(state->ycbcr_enc);
	p_hdr->quantization = htonl(state->quantization);
	p_hdr->size = htonl(cf.size);
	return cf.size + sizeof(*p_hdr);
}

int v4l2_fwht_decode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out)
{
	unsigned int i, j, k;
	u32 flags;
	struct fwht_cframe cf;
	u8 *p, *ref_p;
	unsigned int components_num = 3;
	unsigned int version;
	const struct v4l2_fwht_pixfmt_info *info;
	unsigned int hdr_width_div, hdr_height_div;

	if (!state->info)
		return -EINVAL;

	info = state->info;

	version = ntohl(state->header.version);
	if (!version || version > FWHT_VERSION) {
		pr_err("version %d is not supported, current version is %d\n",
		       version, FWHT_VERSION);
		return -EINVAL;
	}

	if (state->header.magic1 != FWHT_MAGIC1 ||
	    state->header.magic2 != FWHT_MAGIC2)
		return -EINVAL;

	/* TODO: support resolution changes */
	if (ntohl(state->header.width)  != state->visible_width ||
	    ntohl(state->header.height) != state->visible_height)
		return -EINVAL;

	flags = ntohl(state->header.flags);

	if (version >= 2) {
		if ((flags & FWHT_FL_PIXENC_MSK) != info->pixenc)
			return -EINVAL;
		components_num = 1 + ((flags & FWHT_FL_COMPONENTS_NUM_MSK) >>
				FWHT_FL_COMPONENTS_NUM_OFFSET);
	}

	if (components_num != info->components_num)
		return -EINVAL;

	state->colorspace = ntohl(state->header.colorspace);
	state->xfer_func = ntohl(state->header.xfer_func);
	state->ycbcr_enc = ntohl(state->header.ycbcr_enc);
	state->quantization = ntohl(state->header.quantization);
	cf.rlc_data = (__be16 *)p_in;
	cf.size = ntohl(state->header.size);

	hdr_width_div = (flags & FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2;
	hdr_height_div = (flags & FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2;
	if (hdr_width_div != info->width_div ||
	    hdr_height_div != info->height_div)
		return -EINVAL;

	if (!fwht_decode_frame(&cf, &state->ref_frame, flags, components_num,
			       state->visible_width, state->visible_height,
			       state->coded_width))
		return -EINVAL;

	/*
	 * TODO - handle the case where the compressed stream encodes a
	 * different format than the requested decoded format.
	 */
	switch (state->info->id) {
	case V4L2_PIX_FMT_GREY:
		ref_p = state->ref_frame.luma;
		for (i = 0; i < state->coded_height; i++)  {
			memcpy(p_out, ref_p, state->visible_width);
			p_out += state->stride;
			ref_p += state->coded_width;
		}
		break;
	case V4L2_PIX_FMT_YUV420:
	case V4L2_PIX_FMT_YUV422P:
		ref_p = state->ref_frame.luma;
		for (i = 0; i < state->coded_height; i++)  {
			memcpy(p_out, ref_p, state->visible_width);
			p_out += state->stride;
			ref_p += state->coded_width;
		}

		ref_p = state->ref_frame.cb;
		for (i = 0; i < state->coded_height / 2; i++)  {
			memcpy(p_out, ref_p, state->visible_width / 2);
			p_out += state->stride / 2;
			ref_p += state->coded_width / 2;
		}
		ref_p = state->ref_frame.cr;
		for (i = 0; i < state->coded_height / 2; i++)  {
			memcpy(p_out, ref_p, state->visible_width / 2);
			p_out += state->stride / 2;
			ref_p += state->coded_width / 2;
		}
		break;
	case V4L2_PIX_FMT_YVU420:
		ref_p = state->ref_frame.luma;
		for (i = 0; i < state->coded_height; i++)  {
			memcpy(p_out, ref_p, state->visible_width);
			p_out += state->stride;
			ref_p += state->coded_width;
		}

		ref_p = state->ref_frame.cr;
		for (i = 0; i < state->coded_height / 2; i++)  {
			memcpy(p_out, ref_p, state->visible_width / 2);
			p_out += state->stride / 2;
			ref_p += state->coded_width / 2;
		}
		ref_p = state->ref_frame.cb;
		for (i = 0; i < state->coded_height / 2; i++)  {
			memcpy(p_out, ref_p, state->visible_width / 2);
			p_out += state->stride / 2;
			ref_p += state->coded_width / 2;
		}
		break;
	case V4L2_PIX_FMT_NV12:
	case V4L2_PIX_FMT_NV16:
	case V4L2_PIX_FMT_NV24:
		ref_p = state->ref_frame.luma;
		for (i = 0; i < state->coded_height; i++)  {
			memcpy(p_out, ref_p, state->visible_width);
			p_out += state->stride;
			ref_p += state->coded_width;
		}

		k = 0;
		for (i = 0; i < state->coded_height / 2; i++) {
			for (j = 0, p = p_out; j < state->coded_width / 2; j++) {
				*p++ = state->ref_frame.cb[k];
				*p++ = state->ref_frame.cr[k];
				k++;
			}
			p_out += state->stride;
		}
		break;
	case V4L2_PIX_FMT_NV21:
	case V4L2_PIX_FMT_NV61:
	case V4L2_PIX_FMT_NV42:
		ref_p = state->ref_frame.luma;
		for (i = 0; i < state->coded_height; i++)  {
			memcpy(p_out, ref_p, state->visible_width);
			p_out += state->stride;
			ref_p += state->coded_width;
		}

		k = 0;
		for (i = 0; i < state->coded_height / 2; i++) {
			for (j = 0, p = p_out; j < state->coded_width / 2; j++) {
				*p++ = state->ref_frame.cr[k];
				*p++ = state->ref_frame.cb[k];
				k++;
			}
			p_out += state->stride;
		}
		break;
	case V4L2_PIX_FMT_YUYV:
		k = 0;
		for (i = 0; i < state->coded_height; i++) {
			for (j = 0, p = p_out; j < state->coded_width / 2; j++) {
				*p++ = state->ref_frame.luma[k];
				*p++ = state->ref_frame.cb[k / 2];
				*p++ = state->ref_frame.luma[k + 1];
				*p++ = state->ref_frame.cr[k / 2];
				k += 2;
			}
			p_out += state->stride;
		}
		break;
	case V4L2_PIX_FMT_YVYU:
		k = 0;
		for (i = 0; i < state->coded_height; i++) {
			for (j = 0, p = p_out; j < state->coded_width / 2; j++) {
				*p++ = state->ref_frame.luma[k];
				*p++ = state->ref_frame.cr[k / 2];
				*p++ = state->ref_frame.luma[k + 1];
				*p++ = state->ref_frame.cb[k / 2];
				k += 2;
			}
			p_out += state->stride;
		}
		break;
	case V4L2_PIX_FMT_UYVY:
		k = 0;
		for (i = 0; i < state->coded_height; i++) {
			for (j = 0, p = p_out; j < state->coded_width / 2; j++) {
				*p++ = state->ref_frame.cb[k / 2];
				*p++ = state->ref_frame.luma[k];
				*p++ = state->ref_frame.cr[k / 2];
				*p++ = state->ref_frame.luma[k + 1];
				k += 2;
			}
			p_out += state->stride;
		}
		break;
	case V4L2_PIX_FMT_VYUY:
		k = 0;
		for (i = 0; i < state->coded_height; i++) {
			for (j = 0, p = p_out; j < state->coded_width / 2; j++) {
				*p++ = state->ref_frame.cr[k / 2];
				*p++ = state->ref_frame.luma[k];
				*p++ = state->ref_frame.cb[k / 2];
				*p++ = state->ref_frame.luma[k + 1];
				k += 2;
			}
			p_out += state->stride;
		}
		break;
	case V4L2_PIX_FMT_RGB24:
	case V4L2_PIX_FMT_HSV24:
		k = 0;
		for (i = 0; i < state->coded_height; i++) {
			for (j = 0, p = p_out; j < state->coded_width; j++) {
				*p++ = state->ref_frame.cr[k];
				*p++ = state->ref_frame.luma[k];
				*p++ = state->ref_frame.cb[k];
				k++;
			}
			p_out += state->stride;
		}
		break;
	case V4L2_PIX_FMT_BGR24:
		k = 0;
		for (i = 0; i < state->coded_height; i++) {
			for (j = 0, p = p_out; j < state->coded_width; j++) {
				*p++ = state->ref_frame.cb[k];
				*p++ = state->ref_frame.luma[k];
				*p++ = state->ref_frame.cr[k];
				k++;
			}
			p_out += state->stride;
		}
		break;
	case V4L2_PIX_FMT_RGB32:
	case V4L2_PIX_FMT_XRGB32:
	case V4L2_PIX_FMT_HSV32:
		k = 0;
		for (i = 0; i < state->coded_height; i++) {
			for (j = 0, p = p_out; j < state->coded_width; j++) {
				*p++ = 0;
				*p++ = state->ref_frame.cr[k];
				*p++ = state->ref_frame.luma[k];
				*p++ = state->ref_frame.cb[k];
				k++;
			}
			p_out += state->stride;
		}
		break;
	case V4L2_PIX_FMT_BGR32:
	case V4L2_PIX_FMT_XBGR32:
		k = 0;
		for (i = 0; i < state->coded_height; i++) {
			for (j = 0, p = p_out; j < state->coded_width; j++) {
				*p++ = state->ref_frame.cb[k];
				*p++ = state->ref_frame.luma[k];
				*p++ = state->ref_frame.cr[k];
				*p++ = 0;
				k++;
			}
			p_out += state->stride;
		}
		break;
	case V4L2_PIX_FMT_ARGB32:
		k = 0;
		for (i = 0; i < state->coded_height; i++) {
			for (j = 0, p = p_out; j < state->coded_width; j++) {
				*p++ = state->ref_frame.alpha[k];
				*p++ = state->ref_frame.cr[k];
				*p++ = state->ref_frame.luma[k];
				*p++ = state->ref_frame.cb[k];
				k++;
			}
			p_out += state->stride;
		}
		break;
	case V4L2_PIX_FMT_ABGR32:
		k = 0;
		for (i = 0; i < state->coded_height; i++) {
			for (j = 0, p = p_out; j < state->coded_width; j++) {
				*p++ = state->ref_frame.cb[k];
				*p++ = state->ref_frame.luma[k];
				*p++ = state->ref_frame.cr[k];
				*p++ = state->ref_frame.alpha[k];
				k++;
			}
			p_out += state->stride;
		}
		break;
	default:
		return -EINVAL;
	}
	return 0;
}