neatvnc/src/tight.c

180 lines
4.3 KiB
C
Raw Normal View History

2020-02-09 12:03:14 +00:00
/*
* Copyright (c) 2019 - 2020 Andri Yngvason
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
2019-12-30 09:59:51 +00:00
#include "neatvnc.h"
#include "rfb-proto.h"
#include "vec.h"
#include "fb.h"
#include "tight.h"
#include "common.h"
2020-04-02 21:12:09 +00:00
#include "logging.h"
2019-12-30 09:59:51 +00:00
#include <pixman.h>
#include <turbojpeg.h>
#include <stdlib.h>
#include <sys/param.h>
2019-12-30 09:59:51 +00:00
#include <libdrm/drm_fourcc.h>
#define TIGHT_FILL 0x80
#define TIGHT_JPEG 0x90
#define TIGHT_PNG 0xA0
#define TIGHT_BASIC 0x00
#define TIGHT_MAX_WIDTH 2048
int tight_encoder_init(struct tight_encoder* self)
{
// TODO
return 0;
}
void tight_encoder_destroy(struct tight_encoder* self)
{
// TODO
}
2019-12-30 09:59:51 +00:00
enum TJPF get_jpeg_pixfmt(uint32_t fourcc)
{
switch (fourcc) {
case DRM_FORMAT_RGBA8888:
case DRM_FORMAT_RGBX8888:
return TJPF_XBGR;
2019-12-30 09:59:51 +00:00
case DRM_FORMAT_BGRA8888:
case DRM_FORMAT_BGRX8888:
return TJPF_XRGB;
2019-12-30 09:59:51 +00:00
case DRM_FORMAT_ARGB8888:
case DRM_FORMAT_XRGB8888:
return TJPF_BGRX;
2019-12-30 09:59:51 +00:00
case DRM_FORMAT_ABGR8888:
case DRM_FORMAT_XBGR8888:
return TJPF_RGBX;
2019-12-30 09:59:51 +00:00
}
return TJPF_UNKNOWN;
}
static void tight_encode_size(struct vec* dst, size_t size)
{
vec_fast_append_8(dst, (size & 0x7f) | ((size >= 128) << 7));
if (size >= 128)
vec_fast_append_8(dst, ((size >> 7) & 0x7f) | ((size >= 16384) << 7));
if (size >= 16384)
vec_fast_append_8(dst, (size >> 14) & 0x7f);
}
int tight_encode_box_jpeg(struct tight_encoder* self, struct vec* dst,
const struct nvnc_fb* fb, uint32_t x, uint32_t y,
uint32_t stride, uint32_t width, uint32_t height)
2019-12-30 09:59:51 +00:00
{
unsigned char* buffer = NULL;
size_t size = 0;
int quality = 50; /* 1 - 100 */
enum TJPF tjfmt = get_jpeg_pixfmt(fb->fourcc_format);
2019-12-30 09:59:51 +00:00
if (tjfmt == TJPF_UNKNOWN)
return -1;
vec_reserve(dst, 4096);
struct rfb_server_fb_rect rect = {
.encoding = htonl(RFB_ENCODING_TIGHT),
.x = htons(x),
.y = htons(y),
.width = htons(width),
.height = htons(height),
};
vec_append(dst, &rect, sizeof(rect));
tjhandle handle = tjInitCompress();
2020-04-02 21:12:09 +00:00
if (!handle)
return -1;
2019-12-30 09:59:51 +00:00
void* img = (uint32_t*)fb->addr + x + y * stride;
2019-12-30 09:59:51 +00:00
2020-04-02 21:12:09 +00:00
int rc = -1;
rc = tjCompress2(handle, img, width, stride * 4, height, tjfmt, &buffer,
&size, TJSAMP_422, quality, TJFLAG_FASTDCT);
if (rc < 0) {
log_error("Failed to encode tight JPEG box: %s\n", tjGetErrorStr());
goto compress_failure;
}
2019-12-30 09:59:51 +00:00
vec_fast_append_8(dst, TIGHT_JPEG);
tight_encode_size(dst, size);
vec_append(dst, buffer, size);
2020-04-02 21:12:09 +00:00
rc = 0;
2019-12-30 09:59:51 +00:00
tjFree(buffer);
2020-04-02 21:12:09 +00:00
compress_failure:
2019-12-30 09:59:51 +00:00
tjDestroy(handle);
2020-04-02 21:12:09 +00:00
return rc;
2019-12-30 09:59:51 +00:00
}
int tight_encode_box(struct tight_encoder* self, struct vec* dst,
const struct nvnc_fb* fb, uint32_t x, uint32_t y,
uint32_t stride, uint32_t width, uint32_t height)
{
return tight_encode_box_jpeg(self, dst, fb, x, y, stride, width, height);
}
int tight_encode_frame(struct tight_encoder* self, struct vec* dst,
const struct nvnc_fb* fb, struct pixman_region16* region)
2019-12-30 09:59:51 +00:00
{
int rc = -1;
int n_rects = 0;
struct pixman_box16* box = pixman_region_rectangles(region, &n_rects);
if (n_rects > UINT16_MAX) {
box = pixman_region_extents(region);
n_rects = 1;
}
struct rfb_server_fb_update_msg head = {
.type = RFB_SERVER_TO_CLIENT_FRAMEBUFFER_UPDATE,
.n_rects = htons(n_rects),
};
rc = vec_append(dst, &head, sizeof(head));
if (rc < 0)
return -1;
for (int i = 0; i < n_rects; ++i) {
int x = box[i].x1;
int y = box[i].y1;
int box_width = box[i].x2 - x;
int box_height = box[i].y2 - y;
while (box_width > 0) {
int w = MIN(TIGHT_MAX_WIDTH, box_width);
box_width -= w;
rc = tight_encode_box(self, dst, fb, x, y, fb->width,
w, box_height);
if (rc < 0)
return -1;
x += w;
}
2019-12-30 09:59:51 +00:00
}
return 0;
}