blob: 44ee137ea1459270b3fedd10a893bef12de2bdab [file] [log] [blame]
// SPDX-License-Identifier: LGPL-2.1-only
/*
* V4L2 run-length image encoder source
*
* Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
*/
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include "v4l-stream.h"
#include "codec-fwht.h"
#define MIN_WIDTH 64
#define MAX_WIDTH 4096
#define MIN_HEIGHT 64
#define MAX_HEIGHT 2160
/*
* Since Bayer uses alternating lines of BG and GR color components
* you cannot compare one line with the next to see if they are identical,
* instead you need to look at two consecutive lines at a time.
* So here we double the bytesperline value for Bayer formats.
*/
unsigned rle_calc_bpl(unsigned bpl, __u32 pixelformat)
{
switch (pixelformat) {
case V4L2_PIX_FMT_SBGGR8:
case V4L2_PIX_FMT_SGBRG8:
case V4L2_PIX_FMT_SGRBG8:
case V4L2_PIX_FMT_SRGGB8:
case V4L2_PIX_FMT_SBGGR10:
case V4L2_PIX_FMT_SGBRG10:
case V4L2_PIX_FMT_SGRBG10:
case V4L2_PIX_FMT_SRGGB10:
case V4L2_PIX_FMT_SBGGR10P:
case V4L2_PIX_FMT_SGBRG10P:
case V4L2_PIX_FMT_SGRBG10P:
case V4L2_PIX_FMT_SRGGB10P:
case V4L2_PIX_FMT_SBGGR10ALAW8:
case V4L2_PIX_FMT_SGBRG10ALAW8:
case V4L2_PIX_FMT_SGRBG10ALAW8:
case V4L2_PIX_FMT_SRGGB10ALAW8:
case V4L2_PIX_FMT_SBGGR10DPCM8:
case V4L2_PIX_FMT_SGBRG10DPCM8:
case V4L2_PIX_FMT_SGRBG10DPCM8:
case V4L2_PIX_FMT_SRGGB10DPCM8:
case V4L2_PIX_FMT_SBGGR12:
case V4L2_PIX_FMT_SGBRG12:
case V4L2_PIX_FMT_SGRBG12:
case V4L2_PIX_FMT_SRGGB12:
case V4L2_PIX_FMT_SBGGR16:
case V4L2_PIX_FMT_SGBRG16:
case V4L2_PIX_FMT_SGRBG16:
case V4L2_PIX_FMT_SRGGB16:
return 2 * bpl;
default:
return bpl;
}
}
void rle_decompress(__u8 *b, unsigned size, unsigned rle_size, unsigned bpl)
{
__u32 magic_x = ntohl(V4L_STREAM_PACKET_FRAME_VIDEO_X_RLE);
__u32 magic_y = ntohl(V4L_STREAM_PACKET_FRAME_VIDEO_Y_RLE);
unsigned offset = size - rle_size;
__u32 *dst = (__u32 *)b;
__u32 *p = (__u32 *)(b + offset);
__u32 *next_line = NULL;
unsigned l = 0;
unsigned i;
if (size == rle_size)
return;
if (bpl & 3)
bpl = 0;
if (bpl == 0)
magic_y = magic_x;
for (i = 0; i < rle_size; i += 4, p++) {
__u32 v = *p;
__u32 n = 1;
if (bpl && v == magic_y) {
l = ntohl(*++p);
i += 4;
next_line = dst + bpl / 4;
continue;
}
if (v == magic_x) {
v = *++p;
n = ntohl(*++p);
i += 8;
}
while (n--)
*dst++ = v;
if (dst == next_line) {
while (l--) {
memcpy(dst, dst - bpl / 4, bpl);
dst += bpl / 4;
}
next_line = NULL;
}
}
}
unsigned rle_compress(__u8 *b, unsigned size, unsigned bpl)
{
__u32 magic_x = ntohl(V4L_STREAM_PACKET_FRAME_VIDEO_X_RLE);
__u32 magic_y = ntohl(V4L_STREAM_PACKET_FRAME_VIDEO_Y_RLE);
__u32 magic_r = ntohl(V4L_STREAM_PACKET_FRAME_VIDEO_RPLC);
__u32 *p = (__u32 *)b;
__u32 *dst = p;
unsigned i;
/*
* Only attempt runlength encoding if b is aligned
* to a multiple of 4 bytes and if size is a multiple of 4.
*/
if (((unsigned long)b & 3) || (size & 3))
return size;
if (bpl & 3)
bpl = 0;
if (bpl == 0)
magic_y = magic_x;
for (i = 0; i < size; i += 4, p++) {
unsigned n, max;
if (bpl && i % bpl == 0) {
unsigned l = 0;
while (i + (l + 2) * bpl <= size &&
!memcmp(p, p + (l + 1) * (bpl / 4), bpl))
l++;
if (l) {
*dst++ = magic_y;
*dst++ = htonl(l);
i += l * bpl - 4;
p += (l * bpl / 4) - 1;
continue;
}
}
if (*p == magic_x || *p == magic_y) {
*dst++ = magic_r;
continue;
}
max = bpl ? bpl * (i / bpl + 1) : size;
if (i >= max - 16) {
*dst++ = *p;
continue;
}
if (*p != p[1] || *p != p[2] || *p != p[3]) {
*dst++ = *p;
continue;
}
n = 4;
while (i + n * 4 < max && *p == p[n])
n++;
*dst++ = magic_x;
*dst++ = p[1];
*dst++ = htonl(n);
p += n - 1;
i += n * 4 - 4;
}
return (__u8 *)dst - b;
}
struct codec_ctx *fwht_alloc(unsigned pixfmt, unsigned visible_width, unsigned visible_height,
unsigned coded_width, unsigned coded_height,
unsigned field, unsigned colorspace, unsigned xfer_func,
unsigned ycbcr_enc, unsigned quantization)
{
struct codec_ctx *ctx;
const struct v4l2_fwht_pixfmt_info *info = v4l2_fwht_find_pixfmt(pixfmt);
unsigned int chroma_div;
unsigned int size = coded_width * coded_height;
// fwht expects macroblock alignment, check can be dropped once that
// restriction is lifted.
if (!info || coded_width % 8 || coded_height % 8)
return NULL;
ctx = malloc(sizeof(*ctx));
if (!ctx)
return NULL;
ctx->state.coded_width = coded_width;
ctx->state.coded_height = coded_height;
ctx->state.visible_width = visible_width;
ctx->state.visible_height = visible_height;
ctx->state.stride = coded_width * info->bytesperline_mult;
ctx->state.ref_stride = coded_width * info->luma_alpha_step;
ctx->state.info = info;
ctx->field = field;
ctx->state.colorspace = colorspace;
ctx->state.xfer_func = xfer_func;
ctx->state.ycbcr_enc = ycbcr_enc;
ctx->state.quantization = quantization;
ctx->flags = 0;
chroma_div = info->width_div * info->height_div;
ctx->size = size;
if (info->components_num == 4)
ctx->size = 2 * size + 2 * (size / chroma_div);
else if (info->components_num == 3)
ctx->size = size + 2 * (size / chroma_div);
ctx->state.ref_frame.buf = malloc(ctx->size);
ctx->state.ref_frame.luma = ctx->state.ref_frame.buf;
ctx->comp_max_size = ctx->size + sizeof(struct fwht_cframe_hdr);
ctx->state.compressed_frame = malloc(ctx->comp_max_size);
if (!ctx->state.ref_frame.luma || !ctx->state.compressed_frame) {
free(ctx->state.ref_frame.luma);
free(ctx->state.compressed_frame);
free(ctx);
return NULL;
}
if (info->components_num >= 3) {
ctx->state.ref_frame.cb = ctx->state.ref_frame.luma + size;
ctx->state.ref_frame.cr = ctx->state.ref_frame.cb + size / chroma_div;
} else {
ctx->state.ref_frame.cb = NULL;
ctx->state.ref_frame.cr = NULL;
}
if (info->components_num == 4)
ctx->state.ref_frame.alpha =
ctx->state.ref_frame.cr + size / chroma_div;
else
ctx->state.ref_frame.alpha = NULL;
ctx->state.gop_size = 10;
ctx->state.gop_cnt = 0;
return ctx;
}
void fwht_free(struct codec_ctx *ctx)
{
free(ctx->state.ref_frame.luma);
free(ctx->state.compressed_frame);
free(ctx);
}
__u8 *fwht_compress(struct codec_ctx *ctx, __u8 *buf, unsigned uncomp_size, unsigned *comp_size)
{
ctx->state.i_frame_qp = ctx->state.p_frame_qp = 20;
*comp_size = v4l2_fwht_encode(&ctx->state, buf, ctx->state.compressed_frame);
return ctx->state.compressed_frame;
}
static void copy_cap_to_ref(const u8 *cap, const struct v4l2_fwht_pixfmt_info *info,
struct v4l2_fwht_state *state)
{
int plane_idx;
u8 *p_ref = state->ref_frame.buf;
unsigned int cap_stride = state->stride;
unsigned int ref_stride = state->ref_stride;
for (plane_idx = 0; plane_idx < info->planes_num; plane_idx++) {
int i;
unsigned int h_div = (plane_idx == 1 || plane_idx == 2) ?
info->height_div : 1;
const u8 *row_cap = cap;
u8 *row_ref = p_ref;
if (info->planes_num == 3 && plane_idx == 1) {
cap_stride /= 2;
ref_stride /= 2;
}
if (plane_idx == 1 &&
(info->id == V4L2_PIX_FMT_NV24 ||
info->id == V4L2_PIX_FMT_NV42)) {
cap_stride *= 2;
ref_stride *= 2;
}
for (i = 0; i < state->visible_height / h_div; i++) {
memcpy(row_ref, row_cap, ref_stride);
row_ref += ref_stride;
row_cap += cap_stride;
}
cap += cap_stride * (state->coded_height / h_div);
p_ref += ref_stride * (state->coded_height / h_div);
}
}
bool fwht_decompress(struct codec_ctx *ctx, __u8 *p_in, unsigned comp_size,
__u8 *p_out, unsigned uncomp_size)
{
memcpy(&ctx->state.header, p_in, sizeof(ctx->state.header));
p_in += sizeof(ctx->state.header);
if (v4l2_fwht_decode(&ctx->state, p_in, p_out))
return false;
copy_cap_to_ref(p_out, ctx->state.info, &ctx->state);
return true;
}