Plug open h264

h264-encoding
Andri Yngvason 2021-12-24 16:29:37 +00:00
parent 0a70f7fa6a
commit 90f61f03c6
4 changed files with 97 additions and 35 deletions

View File

@ -50,6 +50,10 @@ gnutls = dependency('gnutls', required: get_option('tls'))
zlib = dependency('zlib')
gbm = dependency('gbm', required: get_option('gbm'))
libavcodec = dependency('libavcodec', required: get_option('h264'))
libavfilter = dependency('libavfilter', required: get_option('h264'))
libavutil = dependency('libavutil', required: get_option('h264'))
aml_project = subproject('aml', required: false)
if aml_project.found()
aml = aml_project.get_variable('aml_dep')
@ -108,6 +112,12 @@ if gbm.found()
config.set('HAVE_GBM', true)
endif
if gbm.found() and libavcodec.found() and libavfilter.found() and libavutil.found()
sources += [ 'src/h264-encoder.c', 'src/open-h264.c' ]
dependencies += [libavcodec, libavfilter, libavutil]
config.set('ENABLE_OPEN_H264', true)
endif
configure_file(
output: 'config.h',
configuration: config,

View File

@ -4,3 +4,4 @@ option('jpeg', type: 'feature', value: 'auto', description: 'Enable JPEG compres
option('tls', type: 'feature', value: 'auto', description: 'Enable encryption & authentication')
option('systemtap', type: 'boolean', value: false, description: 'Enable tracing using sdt')
option('gbm', type: 'feature', value: 'auto', description: 'Enable GBM integration')
option('h264', type: 'feature', value: 'disabled', description: 'Enable open h264 encoding (experimental)')

View File

@ -20,6 +20,7 @@
#include "fb.h"
#include "resampler.h"
#include "transform-util.h"
#include "encoder.h"
#include <assert.h>
#include <stdlib.h>
@ -40,8 +41,14 @@ static void nvnc_display__on_resampler_done(struct nvnc_fb* fb,
nvnc_fb_ref(fb);
nvnc_fb_hold(fb);
// TODO: Shift according to display position
assert(self->server);
struct nvnc_client* client;
LIST_FOREACH(client, &self->server->clients, link)
if (client->encoder)
encoder_push(client->encoder, fb, damage);
// TODO: Shift according to display position
nvnc__damage_region(self->server, damage);
}

View File

@ -68,11 +68,13 @@ enum addrtype {
static int send_desktop_resize(struct nvnc_client* client, struct nvnc_fb* fb);
static int send_qemu_key_ext_frame(struct nvnc_client* client);
static enum rfb_encodings choose_frame_encoding(struct nvnc_client* client);
static enum rfb_encodings choose_frame_encoding(struct nvnc_client* client,
struct nvnc_fb*);
static enum tight_quality client_get_tight_quality(struct nvnc_client* client);
static void on_encode_frame_done(struct encoder*, struct rcbuf*);
static bool client_has_encoding(const struct nvnc_client* client,
enum rfb_encodings encoding);
static void process_fb_update_requests(struct nvnc_client* client);
#if defined(GIT_VERSION)
EXPORT const char nvnc_version[] = GIT_VERSION;
@ -488,6 +490,7 @@ static int on_client_set_encodings(struct nvnc_client* client)
case RFB_ENCODING_TIGHT:
case RFB_ENCODING_TRLE:
case RFB_ENCODING_ZRLE:
case RFB_ENCODING_OPEN_H264:
case RFB_ENCODING_CURSOR:
case RFB_ENCODING_DESKTOPSIZE:
case RFB_ENCODING_JPEG_HIGHQ:
@ -502,6 +505,14 @@ static int on_client_set_encodings(struct nvnc_client* client)
return sizeof(*msg) + 4 * n_encodings;
}
static void on_encoder_push_done(struct encoder* encoder, struct rcbuf* payload)
{
(void)payload;
struct nvnc_client* client = encoder->userdata;
process_fb_update_requests(client);
}
static void process_fb_update_requests(struct nvnc_client* client)
{
struct nvnc* server = client->server;
@ -545,6 +556,41 @@ static void process_fb_update_requests(struct nvnc_client* client)
DTRACE_PROBE1(neatvnc, update_fb_start, client);
enum rfb_encodings encoding = choose_frame_encoding(client, fb);
if (!client->encoder || encoding != encoder_get_type(client->encoder)) {
int width = server->display->buffer->width;
int height = server->display->buffer->height;
encoder_destroy(client->encoder);
client->encoder = encoder_new(encoding, width, height);
if (!client->encoder) {
log_error("Failed to allocate new encoder");
return;
}
if (encoder_get_kind(client->encoder) == ENCODER_KIND_PUSH_PULL)
{
client->encoder->on_done = on_encoder_push_done;
client->encoder->userdata = client;
}
}
enum encoder_kind kind = encoder_get_kind(client->encoder);
if (kind == ENCODER_KIND_PUSH_PULL) {
struct rcbuf* buf = encoder_pull(client->encoder);
if (!buf)
return;
struct rfb_server_fb_update_msg update_msg = {
.type = RFB_SERVER_TO_CLIENT_FRAMEBUFFER_UPDATE,
.n_rects = htons(client->encoder->n_rects),
};
stream_write(client->net_stream, &update_msg,
sizeof(update_msg), NULL, NULL);
stream_send(client->net_stream, buf, NULL, NULL);
pixman_region_clear(&client->damage);
--client->n_pending_requests;
} else if (kind == ENCODER_KIND_REGULAR) {
/* The client's damage is exchanged for an empty one */
struct pixman_region16 damage = client->damage;
pixman_region_init(&client->damage);
@ -554,18 +600,6 @@ static void process_fb_update_requests(struct nvnc_client* client)
nvnc_fb_hold(fb);
nvnc_fb_ref(fb);
enum rfb_encodings encoding = choose_frame_encoding(client);
if (!client->encoder || encoding != encoder_get_type(client->encoder)) {
int width = server->display->buffer->width;
int height = server->display->buffer->height;
encoder_destroy(client->encoder);
client->encoder = encoder_new(encoding, width, height);
if (!client->encoder) {
log_error("Failed to allocate new encoder");
goto failure;
}
}
client_ref(client);
int q = client_get_tight_quality(client);
@ -578,13 +612,6 @@ static void process_fb_update_requests(struct nvnc_client* client)
if (encoder_encode(client->encoder, fb, &damage) < 0) {
log_error("Failed to encode current frame");
client_unref(client);
goto failure;
}
pixman_region_fini(&damage);
return;
failure:
client->is_updating = false;
assert(client->current_fb);
nvnc_fb_release(client->current_fb);
@ -592,6 +619,12 @@ failure:
client->current_fb = NULL;
}
pixman_region_fini(&damage);
} else {
abort();
}
}
static int on_client_fb_update_request(struct nvnc_client* client)
{
struct nvnc* server = client->server;
@ -614,10 +647,14 @@ static int on_client_fb_update_request(struct nvnc_client* client)
/* Note: The region sent from the client is ignored for incremental
* updates. This avoids superfluous complexity.
*/
if (!incremental)
if (!incremental) {
pixman_region_union_rect(&client->damage, &client->damage, x, y,
width, height);
if (client->encoder)
encoder_request_key_frame(client->encoder);
}
DTRACE_PROBE1(neatvnc, update_fb_request, client);
nvnc_fb_req_fn fn = server->fb_req_fn;
@ -1183,13 +1220,20 @@ static void on_write_frame_done(void* userdata, enum stream_req_status status)
client_unref(client);
}
static enum rfb_encodings choose_frame_encoding(struct nvnc_client* client)
static enum rfb_encodings choose_frame_encoding(struct nvnc_client* client,
struct nvnc_fb* fb)
{
for (size_t i = 0; i < client->n_encodings; ++i)
switch (client->encodings[i]) {
case RFB_ENCODING_RAW:
case RFB_ENCODING_TIGHT:
case RFB_ENCODING_ZRLE:
#ifdef ENABLE_OPEN_H264
case RFB_ENCODING_OPEN_H264:
// h264 is useless for sw frames
if (fb->type != NVNC_FB_GBM_BO)
break;
#endif
return client->encodings[i];
default:
break;