summaryrefslogtreecommitdiffstats
path: root/xrdp/xrdp_encoder.c
diff options
context:
space:
mode:
Diffstat (limited to 'xrdp/xrdp_encoder.c')
-rw-r--r--xrdp/xrdp_encoder.c479
1 files changed, 479 insertions, 0 deletions
diff --git a/xrdp/xrdp_encoder.c b/xrdp/xrdp_encoder.c
new file mode 100644
index 00000000..78d1b52e
--- /dev/null
+++ b/xrdp/xrdp_encoder.c
@@ -0,0 +1,479 @@
+/**
+ * xrdp: A Remote Desktop Protocol server.
+ *
+ * Copyright (C) Laxmikant Rashinkar 2004-2014
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Encoder
+ */
+
+#include "xrdp_encoder.h"
+#include "xrdp.h"
+#include "thread_calls.h"
+#include "fifo.h"
+
+#ifdef XRDP_RFXCODEC
+#include "rfxcodec_encode.h"
+#endif
+
+#define LLOG_LEVEL 1
+#define LLOGLN(_level, _args) \
+ do \
+ { \
+ if (_level < LLOG_LEVEL) \
+ { \
+ g_write("xrdp:xrdp_encoder [%10.10u]: ", g_time3()); \
+ g_writeln _args ; \
+ } \
+ } \
+ while (0)
+
+#define JPG_CODEC 0
+#define RFX_CODEC 1
+
+/*****************************************************************************/
+static int
+process_enc_jpg(struct xrdp_mm *self, XRDP_ENC_DATA *enc);
+static int
+process_enc_rfx(struct xrdp_mm *self, XRDP_ENC_DATA *enc);
+
+/**
+ * Init encoder
+ *
+ * @return 0 on success, -1 on failure
+ *****************************************************************************/
+/* called from main thread */
+int APP_CC
+init_xrdp_encoder(struct xrdp_mm *self)
+{
+ char buf[1024];
+ int pid;
+
+ if (self == 0)
+ {
+ return -1;
+ }
+
+ LLOGLN(0, ("init_xrdp_encoder: initing encoder codec_id %d", self->codec_id));
+
+ /* setup required FIFOs */
+ self->fifo_to_proc = fifo_create();
+ self->fifo_processed = fifo_create();
+ self->mutex = tc_mutex_create();
+
+ pid = g_getpid();
+ /* setup wait objects for signalling */
+ g_snprintf(buf, 1024, "xrdp_%8.8x_encoder_event_to_proc", pid);
+ self->xrdp_encoder_event_to_proc = g_create_wait_obj(buf);
+ g_snprintf(buf, 1024, "xrdp_%8.8x_encoder_event_processed", pid);
+ self->xrdp_encoder_event_processed = g_create_wait_obj(buf);
+ g_snprintf(buf, 1024, "xrdp_%8.8x_encoder_term", pid);
+ self->xrdp_encoder_term = g_create_wait_obj(buf);
+
+ switch (self->codec_id)
+ {
+ case 2:
+ self->process_enc = process_enc_jpg;
+ break;
+ case 3:
+ self->process_enc = process_enc_rfx;
+#ifdef XRDP_RFXCODEC
+ self->codec_handle =
+ rfxcodec_encode_create(self->wm->screen->width,
+ self->wm->screen->height,
+ RFX_FORMAT_YUV, 0);
+ //RFX_FORMAT_BGRA, 0);
+#endif
+ break;
+ default:
+ LLOGLN(0, ("init_xrdp_encoder: unknown codec_id %d",
+ self->codec_id));
+ break;
+
+ }
+
+ /* create thread to process messages */
+ tc_thread_create(proc_enc_msg, self);
+
+ return 0;
+}
+
+/**
+ * Deinit xrdp encoder
+ *****************************************************************************/
+/* called from main thread */
+void APP_CC
+deinit_xrdp_encoder(struct xrdp_mm *self)
+{
+ XRDP_ENC_DATA *enc;
+ XRDP_ENC_DATA_DONE *enc_done;
+ FIFO *fifo;
+
+ LLOGLN(0, ("deinit_xrdp_encoder: deiniting encoder"));
+
+ if (self == 0)
+ {
+ return;
+ }
+
+ if (self->in_codec_mode == 0)
+ {
+ return;
+ }
+ /* tell worker thread to shut down */
+ g_set_wait_obj(self->xrdp_encoder_term);
+ g_sleep(1000);
+
+ if (self->codec_id == 3)
+ {
+#ifdef XRDP_RFXCODEC
+ rfxcodec_encode_destroy(self->codec_handle);
+#endif
+ }
+
+ /* destroy wait objects used for signalling */
+ g_delete_wait_obj(self->xrdp_encoder_event_to_proc);
+ g_delete_wait_obj(self->xrdp_encoder_event_processed);
+ g_delete_wait_obj(self->xrdp_encoder_term);
+
+ /* cleanup fifo_to_proc */
+ fifo = self->fifo_to_proc;
+ if (fifo)
+ {
+ while (!fifo_is_empty(fifo))
+ {
+ enc = fifo_remove_item(fifo);
+ if (enc == 0)
+ {
+ continue;
+ }
+ g_free(enc->drects);
+ g_free(enc->crects);
+ g_free(enc);
+ }
+
+ fifo_delete(fifo);
+ }
+
+ /* cleanup fifo_processed */
+ fifo = self->fifo_processed;
+ if (fifo)
+ {
+ while (!fifo_is_empty(fifo))
+ {
+ enc_done = fifo_remove_item(fifo);
+ if (enc == 0)
+ {
+ continue;
+ }
+ g_free(enc_done->comp_pad_data);
+ g_free(enc_done);
+ }
+ fifo_delete(fifo);
+ }
+}
+
+/*****************************************************************************/
+/* called from encoder thread */
+static int
+process_enc_jpg(struct xrdp_mm *self, XRDP_ENC_DATA *enc)
+{
+ int index;
+ int x;
+ int y;
+ int cx;
+ int cy;
+ int quality;
+ int error;
+ int out_data_bytes;
+ int count;
+ char *out_data;
+ XRDP_ENC_DATA_DONE *enc_done;
+ FIFO *fifo_processed;
+ tbus mutex;
+ tbus event_processed;
+
+ LLOGLN(10, ("process_enc_jpg:"));
+ quality = self->codec_quality;
+ fifo_processed = self->fifo_processed;
+ mutex = self->mutex;
+ event_processed = self->xrdp_encoder_event_processed;
+ count = enc->num_crects;
+ for (index = 0; index < count; index++)
+ {
+ x = enc->crects[index * 4 + 0];
+ y = enc->crects[index * 4 + 1];
+ cx = enc->crects[index * 4 + 2];
+ cy = enc->crects[index * 4 + 3];
+ if (cx < 1 || cy < 1)
+ {
+ LLOGLN(0, ("process_enc_jpg: error 1"));
+ continue;
+ }
+
+ LLOGLN(10, ("process_enc_jpg: x %d y %d cx %d cy %d", x, y, cx, cy));
+
+ out_data_bytes = MAX((cx + 4) * cy * 4, 8192);
+ if ((out_data_bytes < 1) || (out_data_bytes > 16 * 1024 * 1024))
+ {
+ LLOGLN(0, ("process_enc_jpg: error 2"));
+ return 1;
+ }
+ out_data = (char *) g_malloc(out_data_bytes + 256 + 2, 0);
+ if (out_data == 0)
+ {
+ LLOGLN(0, ("process_enc_jpg: error 3"));
+ return 1;
+ }
+ out_data[256] = 0; /* header bytes */
+ out_data[257] = 0;
+ error = libxrdp_codec_jpeg_compress(self->wm->session, 0, enc->data,
+ enc->width, enc->height,
+ enc->width * 4, x, y, cx, cy,
+ quality,
+ out_data + 256 + 2, &out_data_bytes);
+ if (error < 0)
+ {
+ LLOGLN(0, ("process_enc_jpg: jpeg error %d bytes %d",
+ error, out_data_bytes));
+ g_free(out_data);
+ return 1;
+ }
+ LLOGLN(10, ("jpeg error %d bytes %d", error, out_data_bytes));
+ enc_done = (XRDP_ENC_DATA_DONE *)
+ g_malloc(sizeof(XRDP_ENC_DATA_DONE), 1);
+ enc_done->comp_bytes = out_data_bytes + 2;
+ enc_done->pad_bytes = 256;
+ enc_done->comp_pad_data = out_data;
+ enc_done->enc = enc;
+ enc_done->last = index == (enc->num_crects - 1);
+ enc_done->x = x;
+ enc_done->y = y;
+ enc_done->cx = cx;
+ enc_done->cy = cy;
+ /* done with msg */
+ /* inform main thread done */
+ tc_mutex_lock(mutex);
+ fifo_add_item(fifo_processed, enc_done);
+ tc_mutex_unlock(mutex);
+ /* signal completion for main thread */
+ g_set_wait_obj(event_processed);
+ }
+ return 0;
+}
+
+#ifdef XRDP_RFXCODEC
+
+/*****************************************************************************/
+/* called from encoder thread */
+static int
+process_enc_rfx(struct xrdp_mm *self, XRDP_ENC_DATA *enc)
+{
+ int index;
+ int x;
+ int y;
+ int cx;
+ int cy;
+ int out_data_bytes;
+ int count;
+ int error;
+ char *out_data;
+ XRDP_ENC_DATA_DONE *enc_done;
+ FIFO *fifo_processed;
+ tbus mutex;
+ tbus event_processed;
+ struct rfx_tile *tiles;
+ struct rfx_rect *rfxrects;
+
+ LLOGLN(10, ("process_enc_rfx:"));
+ LLOGLN(10, ("process_enc_rfx: num_crects %d num_drects %d",
+ enc->num_crects, enc->num_drects));
+ fifo_processed = self->fifo_processed;
+ mutex = self->mutex;
+ event_processed = self->xrdp_encoder_event_processed;
+
+ if ((enc->num_crects > 512) || (enc->num_drects > 512))
+ {
+ return 0;
+ }
+
+ out_data_bytes = 16 * 1024 * 1024;
+ index = 256 + sizeof(struct rfx_tile) * 512 +
+ sizeof(struct rfx_rect) * 512;
+ out_data = (char *) g_malloc(out_data_bytes + index, 0);
+ if (out_data == 0)
+ {
+ return 0;
+ }
+ tiles = (struct rfx_tile *) (out_data + out_data_bytes + 256);
+ rfxrects = (struct rfx_rect *) (tiles + 512);
+
+ count = enc->num_crects;
+ for (index = 0; index < count; index++)
+ {
+ x = enc->crects[index * 4 + 0];
+ y = enc->crects[index * 4 + 1];
+ cx = enc->crects[index * 4 + 2];
+ cy = enc->crects[index * 4 + 3];
+ LLOGLN(10, ("process_enc_rfx:"));
+ tiles[index].x = x;
+ tiles[index].y = y;
+ tiles[index].cx = cx;
+ tiles[index].cy = cy;
+ LLOGLN(10, ("x %d y %d cx %d cy %d", x, y, cx, cy));
+ tiles[index].quant_y = 0;
+ tiles[index].quant_cb = 0;
+ tiles[index].quant_cr = 0;
+ }
+
+ count = enc->num_drects;
+ for (index = 0; index < count; index++)
+ {
+ x = enc->drects[index * 4 + 0];
+ y = enc->drects[index * 4 + 1];
+ cx = enc->drects[index * 4 + 2];
+ cy = enc->drects[index * 4 + 3];
+ LLOGLN(10, ("process_enc_rfx:"));
+ rfxrects[index].x = x;
+ rfxrects[index].y = y;
+ rfxrects[index].cx = cx;
+ rfxrects[index].cy = cy;
+ }
+
+ error = rfxcodec_encode(self->codec_handle, out_data + 256, &out_data_bytes,
+ enc->data, enc->width, enc->height, enc->width * 4,
+ rfxrects, enc->num_drects,
+ tiles, enc->num_crects, 0, 0);
+ LLOGLN(10, ("process_enc_rfx: rfxcodec_encode rv %d", error));
+
+ enc_done = (XRDP_ENC_DATA_DONE *)
+ g_malloc(sizeof(XRDP_ENC_DATA_DONE), 1);
+ enc_done->comp_bytes = out_data_bytes;
+ enc_done->pad_bytes = 256;
+ enc_done->comp_pad_data = out_data;
+ enc_done->enc = enc;
+ enc_done->last = 1;
+ enc_done->cx = self->wm->screen->width;
+ enc_done->cy = self->wm->screen->height;
+
+ /* done with msg */
+ /* inform main thread done */
+ tc_mutex_lock(mutex);
+ fifo_add_item(fifo_processed, enc_done);
+ tc_mutex_unlock(mutex);
+ /* signal completion for main thread */
+ g_set_wait_obj(event_processed);
+
+ return 0;
+}
+
+#else
+
+/*****************************************************************************/
+/* called from encoder thread */
+static int
+process_enc_rfx(struct xrdp_mm *self, XRDP_ENC_DATA *enc)
+{
+ return 0;
+}
+
+#endif
+
+/**
+ * Encoder thread main loop
+ *****************************************************************************/
+THREAD_RV THREAD_CC
+proc_enc_msg(void *arg)
+{
+ XRDP_ENC_DATA *enc;
+ FIFO *fifo_to_proc;
+ tbus mutex;
+ tbus event_to_proc;
+ tbus term_obj;
+ tbus lterm_obj;
+ int robjs_count;
+ int wobjs_count;
+ int cont;
+ int timeout;
+ tbus robjs[32];
+ tbus wobjs[32];
+ struct xrdp_mm *self;
+
+ LLOGLN(0, ("proc_enc_msg: thread is running"));
+
+ self = (struct xrdp_mm *) arg;
+ if (self == 0)
+ {
+ LLOGLN(0, ("proc_enc_msg: self nil"));
+ return 0;
+ }
+
+ fifo_to_proc = self->fifo_to_proc;
+ mutex = self->mutex;
+ event_to_proc = self->xrdp_encoder_event_to_proc;
+
+ term_obj = g_get_term_event();
+ lterm_obj = self->xrdp_encoder_term;
+
+ cont = 1;
+ while (cont)
+ {
+ timeout = -1;
+ robjs_count = 0;
+ wobjs_count = 0;
+ robjs[robjs_count++] = term_obj;
+ robjs[robjs_count++] = lterm_obj;
+ robjs[robjs_count++] = event_to_proc;
+
+ if (g_obj_wait(robjs, robjs_count, wobjs, wobjs_count, timeout) != 0)
+ {
+ /* error, should not get here */
+ g_sleep(100);
+ }
+
+ if (g_is_wait_obj_set(term_obj)) /* global term */
+ {
+ LLOGLN(0, ("proc_enc_msg: global term"));
+ break;
+ }
+
+ if (g_is_wait_obj_set(lterm_obj)) /* xrdp_mm term */
+ {
+ LLOGLN(0, ("proc_enc_msg: xrdp_mm term"));
+ break;
+ }
+
+ if (g_is_wait_obj_set(event_to_proc))
+ {
+ /* clear it right away */
+ g_reset_wait_obj(event_to_proc);
+ /* get first msg */
+ tc_mutex_lock(mutex);
+ enc = (XRDP_ENC_DATA *) fifo_remove_item(fifo_to_proc);
+ tc_mutex_unlock(mutex);
+ while (enc != 0)
+ {
+ /* do work */
+ self->process_enc(self, enc);
+ /* get next msg */
+ tc_mutex_lock(mutex);
+ enc = (XRDP_ENC_DATA *) fifo_remove_item(fifo_to_proc);
+ tc_mutex_unlock(mutex);
+ }
+ }
+
+ } /* end while (cont) */
+ LLOGLN(0, ("proc_enc_msg: thread exit"));
+ return 0;
+}