Merge pull request #125 from glyptodon/screen-recording
GUAC-246: Implement screen recording
This commit is contained in:
commit
f5f77fea35
10
Makefile.am
10
Makefile.am
@ -29,6 +29,7 @@ DIST_SUBDIRS = \
|
||||
src/common-ssh \
|
||||
src/terminal \
|
||||
src/guacd \
|
||||
src/guacenc \
|
||||
src/protocols/rdp \
|
||||
src/protocols/ssh \
|
||||
src/protocols/telnet \
|
||||
@ -38,7 +39,6 @@ DIST_SUBDIRS = \
|
||||
SUBDIRS = \
|
||||
src/libguac \
|
||||
src/common \
|
||||
src/guacd \
|
||||
tests
|
||||
|
||||
if ENABLE_COMMON_SSH
|
||||
@ -65,6 +65,14 @@ if ENABLE_VNC
|
||||
SUBDIRS += src/protocols/vnc
|
||||
endif
|
||||
|
||||
if ENABLE_GUACD
|
||||
SUBDIRS += src/guacd
|
||||
endif
|
||||
|
||||
if ENABLE_GUACENC
|
||||
SUBDIRS += src/guacenc
|
||||
endif
|
||||
|
||||
EXTRA_DIST = \
|
||||
LICENSE \
|
||||
bin/guacctl \
|
||||
|
115
configure.ac
115
configure.ac
@ -146,6 +146,63 @@ AC_ARG_WITH(guacd_conf,
|
||||
[guacd_conf=/etc/guacamole/guacd.conf])
|
||||
AC_DEFINE_UNQUOTED([GUACD_CONF_FILE], ["$guacd_conf"], [The full path to the guacd config file])
|
||||
|
||||
#
|
||||
# libavcodec
|
||||
#
|
||||
|
||||
have_libavcodec=disabled
|
||||
AC_ARG_WITH([libavcodec],
|
||||
[AS_HELP_STRING([--with-libavcodec],
|
||||
[use libavcodec when encoding video @<:@default=check@:>@])],
|
||||
[],
|
||||
[with_libavcodec=check])
|
||||
|
||||
if test "x$with_libavcodec" != "xno"
|
||||
then
|
||||
have_libavcodec=yes
|
||||
PKG_CHECK_MODULES([AVCODEC], [libavcodec],, [have_libavcodec=no]);
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL([ENABLE_AVCODEC], [test "x${have_libavcodec}" = "xyes"])
|
||||
|
||||
#
|
||||
# libavutil
|
||||
#
|
||||
|
||||
have_libavutil=disabled
|
||||
AC_ARG_WITH([libavutil],
|
||||
[AS_HELP_STRING([--with-libavutil],
|
||||
[use libavutil when encoding video @<:@default=check@:>@])],
|
||||
[],
|
||||
[with_libavutil=check])
|
||||
|
||||
if test "x$with_libavutil" != "xno"
|
||||
then
|
||||
have_libavutil=yes
|
||||
PKG_CHECK_MODULES([AVUTIL], [libavutil],, [have_libavutil=no]);
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL([ENABLE_AVUTIL], [test "x${have_libavutil}" = "xyes"])
|
||||
|
||||
#
|
||||
# libswscale
|
||||
#
|
||||
|
||||
have_libswscale=disabled
|
||||
AC_ARG_WITH([libswscale],
|
||||
[AS_HELP_STRING([--with-libswscale],
|
||||
[use libswscale when encoding video @<:@default=check@:>@])],
|
||||
[],
|
||||
[with_libswscale=check])
|
||||
|
||||
if test "x$with_libswscale" != "xno"
|
||||
then
|
||||
have_libswscale=yes
|
||||
PKG_CHECK_MODULES([SWSCALE], [libswscale],, [have_libswscale=no]);
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL([ENABLE_SWSCALE], [test "x${have_libswscale}" = "xyes"])
|
||||
|
||||
#
|
||||
# libssl
|
||||
#
|
||||
@ -966,6 +1023,36 @@ fi
|
||||
AM_CONDITIONAL([ENABLE_WEBP], [test "x${have_webp}" = "xyes"])
|
||||
AC_SUBST(WEBP_LIBS)
|
||||
|
||||
#
|
||||
# guacd
|
||||
#
|
||||
|
||||
AC_ARG_ENABLE([guacd],
|
||||
[AS_HELP_STRING([--disable-guacd],
|
||||
[do not build the Guacamole proxy daemon])],
|
||||
[],
|
||||
[enable_guacd=yes])
|
||||
|
||||
AM_CONDITIONAL([ENABLE_GUACD], [test "x${enable_guacd}" = "xyes"])
|
||||
|
||||
#
|
||||
# guacenc
|
||||
#
|
||||
|
||||
AC_ARG_ENABLE([guacenc],
|
||||
[AS_HELP_STRING([--disable-guacenc],
|
||||
[do not build the Guacamole video encoding tool])],
|
||||
[],
|
||||
[enable_guacenc=yes])
|
||||
|
||||
AM_CONDITIONAL([ENABLE_GUACENC], [test "x${enable_guacenc}" = "xyes" \
|
||||
-a "x${have_libavcodec}" = "xyes" \
|
||||
-a "x${have_libavutil}" = "xyes" \
|
||||
-a "x${have_libswscale}" = "xyes"])
|
||||
|
||||
#
|
||||
# Output Makefiles
|
||||
#
|
||||
|
||||
AC_CONFIG_FILES([Makefile
|
||||
tests/Makefile
|
||||
@ -974,19 +1061,39 @@ AC_CONFIG_FILES([Makefile
|
||||
src/terminal/Makefile
|
||||
src/libguac/Makefile
|
||||
src/guacd/Makefile
|
||||
src/guacenc/Makefile
|
||||
src/protocols/rdp/Makefile
|
||||
src/protocols/ssh/Makefile
|
||||
src/protocols/telnet/Makefile
|
||||
src/protocols/vnc/Makefile])
|
||||
AC_OUTPUT
|
||||
|
||||
#
|
||||
# Protocol build status
|
||||
#
|
||||
|
||||
AM_COND_IF([ENABLE_RDP], [build_rdp=yes], [build_rdp=no])
|
||||
AM_COND_IF([ENABLE_SSH], [build_ssh=yes], [build_ssh=no])
|
||||
AM_COND_IF([ENABLE_TELNET], [build_telnet=yes], [build_telnet=no])
|
||||
AM_COND_IF([ENABLE_VNC], [build_vnc=yes], [build_vnc=no])
|
||||
|
||||
#
|
||||
# Service / tool build status
|
||||
#
|
||||
|
||||
AM_COND_IF([ENABLE_GUACD], [build_guacd=yes], [build_guacd=no])
|
||||
AM_COND_IF([ENABLE_GUACENC], [build_guacenc=yes], [build_guacenc=no])
|
||||
|
||||
#
|
||||
# Init scripts
|
||||
#
|
||||
|
||||
AM_COND_IF([ENABLE_INIT], [build_init="${init_dir}"], [build_init=no])
|
||||
|
||||
#
|
||||
# Display summary
|
||||
#
|
||||
|
||||
echo "
|
||||
------------------------------------------------
|
||||
$PACKAGE_NAME version $PACKAGE_VERSION
|
||||
@ -996,8 +1103,11 @@ $PACKAGE_NAME version $PACKAGE_VERSION
|
||||
|
||||
freerdp ............. ${have_freerdp}
|
||||
pango ............... ${have_pango}
|
||||
libavcodec .......... ${have_libavcodec}
|
||||
libavutil ........... ${have_libavutil}
|
||||
libssh2 ............. ${have_libssh2}
|
||||
libssl .............. ${have_ssl}
|
||||
libswscale .......... ${have_libswscale}
|
||||
libtelnet ........... ${have_libtelnet}
|
||||
libVNCServer ........ ${have_libvncserver}
|
||||
libvorbis ........... ${have_vorbis}
|
||||
@ -1011,6 +1121,11 @@ $PACKAGE_NAME version $PACKAGE_VERSION
|
||||
Telnet .... ${build_telnet}
|
||||
VNC ....... ${build_vnc}
|
||||
|
||||
Services / tools:
|
||||
|
||||
guacd ...... ${build_guacd}
|
||||
guacenc .... ${build_guacenc}
|
||||
|
||||
Init scripts: ${build_init}
|
||||
|
||||
Type \"make\" to compile $PACKAGE_NAME.
|
||||
|
@ -37,6 +37,7 @@ noinst_HEADERS = \
|
||||
guac_json.h \
|
||||
guac_list.h \
|
||||
guac_pointer_cursor.h \
|
||||
guac_recording.h \
|
||||
guac_rect.h \
|
||||
guac_string.h \
|
||||
guac_surface.h
|
||||
@ -53,6 +54,7 @@ libguac_common_la_SOURCES = \
|
||||
guac_json.c \
|
||||
guac_list.c \
|
||||
guac_pointer_cursor.c \
|
||||
guac_recording.c \
|
||||
guac_rect.c \
|
||||
guac_string.c \
|
||||
guac_surface.c
|
||||
|
159
src/common/guac_recording.c
Normal file
159
src/common/guac_recording.c
Normal file
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Glyptodon LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "guac_recording.h"
|
||||
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/socket.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/**
|
||||
* Attempts to open a new recording within the given path and having the given
|
||||
* name. If such a file already exists, sequential numeric suffixes (.1, .2,
|
||||
* .3, etc.) are appended until a filename is found which does not exist (or
|
||||
* until the maximum number of numeric suffixes has been tried). If the file
|
||||
* absolutely cannot be opened due to an error, -1 is returned and errno is set
|
||||
* appropriately.
|
||||
*
|
||||
* @param path
|
||||
* The full path to the directory in which the data file should be created.
|
||||
*
|
||||
* @param name
|
||||
* The name of the data file which should be crated within the given path.
|
||||
*
|
||||
* @param basename
|
||||
* A buffer in which the path, a path separator, the filename, any
|
||||
* necessary suffix, and a NULL terminator will be stored. If insufficient
|
||||
* space is available, -1 will be returned, and errno will be set to
|
||||
* ENAMETOOLONG.
|
||||
*
|
||||
* @param basename_size
|
||||
* The number of bytes available within the provided basename buffer.
|
||||
*
|
||||
* @return
|
||||
* The file descriptor of the open data file if open succeeded, or -1 on
|
||||
* failure.
|
||||
*/
|
||||
static int guac_common_recording_open(const char* path,
|
||||
const char* name, char* basename, int basename_size) {
|
||||
|
||||
int i;
|
||||
|
||||
/* Concatenate path and name (separated by a single slash) */
|
||||
int basename_length = snprintf(basename,
|
||||
basename_size - GUAC_COMMON_RECORDING_MAX_SUFFIX_LENGTH,
|
||||
"%s/%s", path, name);
|
||||
|
||||
/* Abort if maximum length reached */
|
||||
if (basename_length ==
|
||||
basename_size - GUAC_COMMON_RECORDING_MAX_SUFFIX_LENGTH) {
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Attempt to open recording */
|
||||
int fd = open(basename,
|
||||
O_CREAT | O_EXCL | O_WRONLY,
|
||||
S_IRUSR | S_IWUSR);
|
||||
|
||||
/* Continuously retry with alternate names on failure */
|
||||
if (fd == -1) {
|
||||
|
||||
/* Prepare basename for additional suffix */
|
||||
basename[basename_length] = '.';
|
||||
char* suffix = &(basename[basename_length + 1]);
|
||||
|
||||
/* Continue retrying alternative suffixes if file already exists */
|
||||
for (i = 1; fd == -1 && errno == EEXIST
|
||||
&& i <= GUAC_COMMON_RECORDING_MAX_SUFFIX; i++) {
|
||||
|
||||
/* Append new suffix */
|
||||
sprintf(suffix, "%i", i);
|
||||
|
||||
/* Retry with newly-suffixed filename */
|
||||
fd = open(basename,
|
||||
O_CREAT | O_EXCL | O_WRONLY,
|
||||
S_IRUSR | S_IWUSR);
|
||||
|
||||
}
|
||||
|
||||
} /* end if open succeeded */
|
||||
|
||||
/* Lock entire output file for writing by the current process */
|
||||
struct flock file_lock = {
|
||||
.l_type = F_WRLCK,
|
||||
.l_whence = SEEK_SET,
|
||||
.l_start = 0,
|
||||
.l_len = 0,
|
||||
.l_pid = getpid()
|
||||
};
|
||||
|
||||
/* Abort if file cannot be locked for reading */
|
||||
if (fcntl(fd, F_SETLK, &file_lock) == -1) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
|
||||
}
|
||||
|
||||
int guac_common_recording_create(guac_client* client, const char* path,
|
||||
const char* name, int create_path) {
|
||||
|
||||
char filename[GUAC_COMMON_RECORDING_MAX_NAME_LENGTH];
|
||||
|
||||
/* Create path if it does not exist, fail if impossible */
|
||||
if (create_path && mkdir(path, S_IRWXU) && errno != EEXIST) {
|
||||
guac_client_log(client, GUAC_LOG_ERROR,
|
||||
"Creation of recording failed: %s", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Attempt to open recording file */
|
||||
int fd = guac_common_recording_open(path, name, filename, sizeof(filename));
|
||||
if (fd == -1) {
|
||||
guac_client_log(client, GUAC_LOG_ERROR,
|
||||
"Creation of recording failed: %s", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Replace client socket with wrapped socket */
|
||||
client->socket = guac_socket_tee(client->socket, guac_socket_open(fd));
|
||||
|
||||
/* Recording creation succeeded */
|
||||
guac_client_log(client, GUAC_LOG_INFO,
|
||||
"Recording of session will be saved to \"%s\".",
|
||||
filename);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
81
src/common/guac_recording.h
Normal file
81
src/common/guac_recording.h
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef GUAC_COMMON_RECORDING_H
|
||||
#define GUAC_COMMON_RECORDING_H
|
||||
|
||||
#include <guacamole/client.h>
|
||||
|
||||
/**
|
||||
* The maximum numeric value allowed for the .1, .2, .3, etc. suffix appended
|
||||
* to the end of the session recording filename if a recording having the
|
||||
* requested name already exists.
|
||||
*/
|
||||
#define GUAC_COMMON_RECORDING_MAX_SUFFIX 255
|
||||
|
||||
/**
|
||||
* The maximum length of the string containing a sequential numeric suffix
|
||||
* between 1 and GUAC_COMMON_RECORDING_MAX_SUFFIX inclusive, in bytes,
|
||||
* including NULL terminator.
|
||||
*/
|
||||
#define GUAC_COMMON_RECORDING_MAX_SUFFIX_LENGTH 4
|
||||
|
||||
/**
|
||||
* The maximum overall length of the full path to the session recording file,
|
||||
* including any additional suffix and NULL terminator, in bytes.
|
||||
*/
|
||||
#define GUAC_COMMON_RECORDING_MAX_NAME_LENGTH 2048
|
||||
|
||||
/**
|
||||
* Replaces the socket of the given client such that all further Guacamole
|
||||
* protocol output will be copied into a file within the given path and having
|
||||
* the given name. If the create_path flag is non-zero, the given path will be
|
||||
* created if it does not yet exist. If creation of the recording file or path
|
||||
* fails, error messages will automatically be logged, and no recording will be
|
||||
* written. The recording will automatically be closed once the client is
|
||||
* freed.
|
||||
*
|
||||
* @param client
|
||||
* The client whose output should be copied to a recording file.
|
||||
*
|
||||
* @param path
|
||||
* The full absolute path to a directory in which the recording file should
|
||||
* be created.
|
||||
*
|
||||
* @param name
|
||||
* The base name to use for the recording file created within the specified
|
||||
* path.
|
||||
*
|
||||
* @param create_path
|
||||
* Zero if the specified path MUST exist for the recording file to be
|
||||
* written, or non-zero if the path should be created if it does not yet
|
||||
* exist.
|
||||
*
|
||||
* @return
|
||||
* Zero if the recording file has been successfully created and a recording
|
||||
* will be written, non-zero otherwise.
|
||||
*/
|
||||
int guac_common_recording_create(guac_client* client, const char* path,
|
||||
const char* name, int create_path);
|
||||
|
||||
#endif
|
||||
|
5
src/guacenc/.gitignore
vendored
Normal file
5
src/guacenc/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
|
||||
# Compiled guacenc
|
||||
guacenc
|
||||
guacenc.exe
|
||||
|
103
src/guacenc/Makefile.am
Normal file
103
src/guacenc/Makefile.am
Normal file
@ -0,0 +1,103 @@
|
||||
#
|
||||
# Copyright (C) 2016 Glyptodon, Inc.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
|
||||
AUTOMAKE_OPTIONS = foreign
|
||||
|
||||
bin_PROGRAMS = guacenc
|
||||
|
||||
man_MANS = \
|
||||
man/guacenc.1
|
||||
|
||||
noinst_HEADERS = \
|
||||
buffer.h \
|
||||
display.h \
|
||||
encode.h \
|
||||
ffmpeg-compat.h \
|
||||
guacenc.h \
|
||||
image-stream.h \
|
||||
instructions.h \
|
||||
jpeg.h \
|
||||
layer.h \
|
||||
log.h \
|
||||
parse.h \
|
||||
png.h \
|
||||
video.h
|
||||
|
||||
guacenc_SOURCES = \
|
||||
buffer.c \
|
||||
display.c \
|
||||
display-buffers.c \
|
||||
display-image-streams.c \
|
||||
display-flatten.c \
|
||||
display-layers.c \
|
||||
display-sync.c \
|
||||
encode.c \
|
||||
guacenc.c \
|
||||
image-stream.c \
|
||||
instructions.c \
|
||||
instruction-blob.c \
|
||||
instruction-cfill.c \
|
||||
instruction-copy.c \
|
||||
instruction-cursor.c \
|
||||
instruction-dispose.c \
|
||||
instruction-end.c \
|
||||
instruction-img.c \
|
||||
instruction-move.c \
|
||||
instruction-rect.c \
|
||||
instruction-shade.c \
|
||||
instruction-size.c \
|
||||
instruction-sync.c \
|
||||
instruction-transfer.c \
|
||||
jpeg.c \
|
||||
layer.c \
|
||||
log.c \
|
||||
parse.c \
|
||||
png.c \
|
||||
video.c
|
||||
|
||||
# Compile WebP support if available
|
||||
if ENABLE_WEBP
|
||||
guacenc_SOURCES += webp.c
|
||||
noinst_HEADERS += webp.h
|
||||
endif
|
||||
|
||||
guacenc_CFLAGS = \
|
||||
-Werror -Wall -pedantic \
|
||||
@AVCODEC_CFLAGS@ \
|
||||
@AVUTIL_CFLAGS@ \
|
||||
@LIBGUAC_INCLUDE@ \
|
||||
@SWSCALE_CFLAGS@
|
||||
|
||||
guacenc_LDADD = \
|
||||
@LIBGUAC_LTLIB@
|
||||
|
||||
guacenc_LDFLAGS = \
|
||||
@AVCODEC_LIBS@ \
|
||||
@AVUTIL_LIBS@ \
|
||||
@CAIRO_LIBS@ \
|
||||
@JPEG_LIBS@ \
|
||||
@SWSCALE_LIBS@ \
|
||||
@WEBP_LIBS@
|
||||
|
||||
EXTRA_DIST = \
|
||||
man/guacenc.1
|
||||
|
174
src/guacenc/buffer.c
Normal file
174
src/guacenc/buffer.c
Normal file
@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "buffer.h"
|
||||
|
||||
#include <cairo/cairo.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
guacenc_buffer* guacenc_buffer_alloc() {
|
||||
return calloc(1, sizeof(guacenc_buffer));
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees the underlying image data, surface, and graphics context of the given
|
||||
* buffer, marking each as unallocated.
|
||||
*
|
||||
* @param buffer
|
||||
* The guacenc_buffer whose image data, surface, and graphics context
|
||||
* should be freed.
|
||||
*/
|
||||
static void guacenc_buffer_free_image(guacenc_buffer* buffer) {
|
||||
|
||||
/* Free graphics context */
|
||||
if (buffer->cairo != NULL) {
|
||||
cairo_destroy(buffer->cairo);
|
||||
buffer->cairo = NULL;
|
||||
}
|
||||
|
||||
/* Free Cairo surface */
|
||||
if (buffer->surface != NULL) {
|
||||
cairo_surface_destroy(buffer->surface);
|
||||
buffer->surface = NULL;
|
||||
}
|
||||
|
||||
/* Free image data (previously wrapped by Cairo surface */
|
||||
free(buffer->image);
|
||||
buffer->image = NULL;
|
||||
|
||||
}
|
||||
|
||||
void guacenc_buffer_free(guacenc_buffer* buffer) {
|
||||
|
||||
/* Ignore NULL buffer */
|
||||
if (buffer == NULL)
|
||||
return;
|
||||
|
||||
/* Free buffer and underlying image */
|
||||
guacenc_buffer_free_image(buffer);
|
||||
free(buffer);
|
||||
|
||||
}
|
||||
|
||||
int guacenc_buffer_resize(guacenc_buffer* buffer, int width, int height) {
|
||||
|
||||
/* Ignore requests which do not change the size */
|
||||
if (buffer->width == width && buffer->height == height)
|
||||
return 0;
|
||||
|
||||
/* Simply deallocate if new image has absolutely no pixels */
|
||||
if (width == 0 || height == 0) {
|
||||
guacenc_buffer_free_image(buffer);
|
||||
buffer->width = width;
|
||||
buffer->height = height;
|
||||
buffer->stride = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Allocate data for new image */
|
||||
int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
|
||||
unsigned char* image = calloc(1, stride*height);
|
||||
|
||||
/* Wrap data in surface */
|
||||
cairo_surface_t* surface = cairo_image_surface_create_for_data(image,
|
||||
CAIRO_FORMAT_ARGB32, width, height, stride);
|
||||
|
||||
/* Obtain graphics context of new surface */
|
||||
cairo_t* cairo = cairo_create(surface);
|
||||
|
||||
/* Copy old surface, if defined */
|
||||
if (buffer->surface != NULL) {
|
||||
cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_set_source_surface(cairo, buffer->surface, 0, 0);
|
||||
cairo_set_operator(cairo, CAIRO_OPERATOR_OVER);
|
||||
cairo_paint(cairo);
|
||||
}
|
||||
|
||||
/* Update properties */
|
||||
buffer->width = width;
|
||||
buffer->height = height;
|
||||
buffer->stride = stride;
|
||||
|
||||
/* Replace old image */
|
||||
guacenc_buffer_free_image(buffer);
|
||||
buffer->image = image;
|
||||
buffer->surface = surface;
|
||||
buffer->cairo = cairo;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int guacenc_buffer_fit(guacenc_buffer* buffer, int x, int y) {
|
||||
|
||||
/* Increase width to fit X (if necessary) */
|
||||
int new_width = buffer->width;
|
||||
if (new_width < x+1)
|
||||
new_width = x+1;
|
||||
|
||||
/* Increase height to fit Y (if necessary) */
|
||||
int new_height = buffer->height;
|
||||
if (new_height < y+1)
|
||||
new_height = y+1;
|
||||
|
||||
/* Resize buffer if size needs to change to fit X/Y coordinate */
|
||||
if (new_width != buffer->width || new_height != buffer->height)
|
||||
return guacenc_buffer_resize(buffer, new_width, new_height);
|
||||
|
||||
/* No change necessary */
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int guacenc_buffer_copy(guacenc_buffer* dst, guacenc_buffer* src) {
|
||||
|
||||
/* Resize destination to exactly fit source */
|
||||
if (guacenc_buffer_resize(dst, src->width, src->height))
|
||||
return 1;
|
||||
|
||||
/* Copy surface contents identically */
|
||||
if (src->surface != NULL) {
|
||||
|
||||
/* Destination must be non-NULL as its size is that of the source */
|
||||
assert(dst->cairo != NULL);
|
||||
|
||||
/* Reset state of destination */
|
||||
cairo_t* cairo = dst->cairo;
|
||||
cairo_reset_clip(cairo);
|
||||
|
||||
/* Overwrite destination with contents of source */
|
||||
cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_set_source_surface(cairo, src->surface, 0, 0);
|
||||
cairo_paint(cairo);
|
||||
|
||||
/* Reset operator of destination to default */
|
||||
cairo_set_operator(cairo, CAIRO_OPERATOR_OVER);
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
157
src/guacenc/buffer.h
Normal file
157
src/guacenc/buffer.h
Normal file
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef GUACENC_BUFFER_H
|
||||
#define GUACENC_BUFFER_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <cairo/cairo.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* The image and size storage for either a buffer (a Guacamole layer with a
|
||||
* negative index) or a layer (a Guacamole layer with a non-negative index).
|
||||
*/
|
||||
typedef struct guacenc_buffer {
|
||||
|
||||
/**
|
||||
* Whether this buffer should be automatically resized to fit any draw
|
||||
* operation.
|
||||
*/
|
||||
bool autosize;
|
||||
|
||||
/**
|
||||
* The width of this buffer or layer, in pixels.
|
||||
*/
|
||||
int width;
|
||||
|
||||
/**
|
||||
* The height of this buffer or layer, in pixels.
|
||||
*/
|
||||
int height;
|
||||
|
||||
/**
|
||||
* The number of bytes in each row of image data.
|
||||
*/
|
||||
int stride;
|
||||
|
||||
/**
|
||||
* The underlying image data of this surface. If the width or height of
|
||||
* this surface are 0, this will be NULL.
|
||||
*/
|
||||
unsigned char* image;
|
||||
|
||||
/**
|
||||
* The Cairo surface wrapping the underlying image data of this surface. If
|
||||
* the width or height of this surface are 0, this will be NULL.
|
||||
*/
|
||||
cairo_surface_t* surface;
|
||||
|
||||
/**
|
||||
* The current graphics context of the Cairo surface. If the width or
|
||||
* height of this surface are 0, this will be NULL.
|
||||
*/
|
||||
cairo_t* cairo;
|
||||
|
||||
} guacenc_buffer;
|
||||
|
||||
/**
|
||||
* Allocates and initializes a new buffer object. This allocation is
|
||||
* independent of the Guacamole video encoder display; the allocated
|
||||
* guacenc_buffer will not automatically be associated with the active display.
|
||||
*
|
||||
* @return
|
||||
* A newly-allocated and initialized guacenc_buffer, or NULL if allocation
|
||||
* fails.
|
||||
*/
|
||||
guacenc_buffer* guacenc_buffer_alloc();
|
||||
|
||||
/**
|
||||
* Frees all memory associated with the given buffer object. If the buffer
|
||||
* provided is NULL, this function has no effect.
|
||||
*
|
||||
* @param buffer
|
||||
* The buffer to free, which may be NULL.
|
||||
*/
|
||||
void guacenc_buffer_free(guacenc_buffer* buffer);
|
||||
|
||||
/**
|
||||
* Resizes the given buffer to the given dimensions, allocating or freeing
|
||||
* memory as necessary, and updating the buffer's width, height, and stride
|
||||
* properties.
|
||||
*
|
||||
* @param buffer
|
||||
* The buffer to resize.
|
||||
*
|
||||
* @param width
|
||||
* The new width of the buffer, in pixels.
|
||||
*
|
||||
* @param height
|
||||
* The new height of the buffer, in pixels.
|
||||
*
|
||||
* @return
|
||||
* Zero if the resize operation is successful, non-zero on error.
|
||||
*/
|
||||
int guacenc_buffer_resize(guacenc_buffer* buffer, int width, int height);
|
||||
|
||||
/**
|
||||
* Resizes the given buffer as necessary to contain at the given X/Y
|
||||
* coordinate, allocating or freeing memory as necessary, and updating the
|
||||
* buffer's width, height, and stride properties. If the buffer already
|
||||
* contains the given coordinate, this function has no effect.
|
||||
*
|
||||
* @param buffer
|
||||
* The buffer to resize.
|
||||
*
|
||||
* @param x
|
||||
* The X coordinate to ensure is within the buffer.
|
||||
*
|
||||
* @param y
|
||||
* The Y coordinate to ensure is within the buffer.
|
||||
*
|
||||
* @return
|
||||
* Zero if the resize operation is successful or no resize was performed,
|
||||
* non-zero if the resize operation failed.
|
||||
*/
|
||||
int guacenc_buffer_fit(guacenc_buffer* buffer, int x, int y);
|
||||
|
||||
/**
|
||||
* Copies the entire contents of the given source buffer to the destination
|
||||
* buffer, ignoring the current contents of the destination. The destination
|
||||
* buffer's contents are entirely replaced.
|
||||
*
|
||||
* @param dst
|
||||
* The destination buffer whose contents should be replaced.
|
||||
*
|
||||
* @param src
|
||||
* The source buffer whose contents should replace those of the destination
|
||||
* buffer.
|
||||
*
|
||||
* @return
|
||||
* Zero if the copy operation was successful, non-zero on failure.
|
||||
*/
|
||||
int guacenc_buffer_copy(guacenc_buffer* dst, guacenc_buffer* src);
|
||||
|
||||
#endif
|
||||
|
110
src/guacenc/display-buffers.c
Normal file
110
src/guacenc/display-buffers.c
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "display.h"
|
||||
#include "buffer.h"
|
||||
#include "layer.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <guacamole/client.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
guacenc_buffer* guacenc_display_get_buffer(guacenc_display* display,
|
||||
int index) {
|
||||
|
||||
/* Transform index to buffer space */
|
||||
int internal_index = -index - 1;
|
||||
|
||||
/* Do not lookup / allocate if index is invalid */
|
||||
if (internal_index < 0 || internal_index > GUACENC_DISPLAY_MAX_BUFFERS) {
|
||||
guacenc_log(GUAC_LOG_WARNING, "Buffer index out of bounds: %i", index);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Lookup buffer, allocating a new buffer if necessary */
|
||||
guacenc_buffer* buffer = display->buffers[internal_index];
|
||||
if (buffer == NULL) {
|
||||
|
||||
/* Attempt to allocate buffer */
|
||||
buffer = guacenc_buffer_alloc();
|
||||
if (buffer == NULL) {
|
||||
guacenc_log(GUAC_LOG_WARNING, "Buffer allocation failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* All non-layer buffers must autosize */
|
||||
buffer->autosize = true;
|
||||
|
||||
/* Store buffer within display for future retrieval / management */
|
||||
display->buffers[internal_index] = buffer;
|
||||
|
||||
}
|
||||
|
||||
return buffer;
|
||||
|
||||
}
|
||||
|
||||
int guacenc_display_free_buffer(guacenc_display* display,
|
||||
int index) {
|
||||
|
||||
/* Transform index to buffer space */
|
||||
int internal_index = -index - 1;
|
||||
|
||||
/* Do not lookup / free if index is invalid */
|
||||
if (internal_index < 0 || internal_index > GUACENC_DISPLAY_MAX_BUFFERS) {
|
||||
guacenc_log(GUAC_LOG_WARNING, "Buffer index out of bounds: %i", index);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Free buffer (if allocated) */
|
||||
guacenc_buffer_free(display->buffers[internal_index]);
|
||||
|
||||
/* Mark buffer as freed */
|
||||
display->buffers[internal_index] = NULL;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
guacenc_buffer* guacenc_display_get_related_buffer(guacenc_display* display,
|
||||
int index) {
|
||||
|
||||
/* Retrieve underlying buffer of layer if a layer is requested */
|
||||
if (index >= 0) {
|
||||
|
||||
/* Retrieve / allocate layer (if possible */
|
||||
guacenc_layer* layer = guacenc_display_get_layer(display, index);
|
||||
if (layer == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Return underlying buffer */
|
||||
return layer->buffer;
|
||||
|
||||
}
|
||||
|
||||
/* Otherwise retrieve buffer directly */
|
||||
return guacenc_display_get_buffer(display, index);
|
||||
|
||||
}
|
||||
|
160
src/guacenc/display-flatten.c
Normal file
160
src/guacenc/display-flatten.c
Normal file
@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "display.h"
|
||||
#include "layer.h"
|
||||
|
||||
#include <cairo/cairo.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* The Guacamole video encoder display related to the current qsort()
|
||||
* operation. As qsort() does not provide a means of passing arbitrary data to
|
||||
* the comparitor, this value must be set prior to invoking qsort() with
|
||||
* guacenc_display_layer_comparator.
|
||||
*/
|
||||
guacenc_display* __qsort_display;
|
||||
|
||||
/**
|
||||
* Comparator which orders layer pointers such that (1) NULL pointers are last,
|
||||
* (2) layers with the same parent_index are adjacent, and (3) layers with the
|
||||
* same parent_index are ordered by Z.
|
||||
*
|
||||
* @see qsort()
|
||||
*/
|
||||
static int guacenc_display_layer_comparator(const void* a, const void* b) {
|
||||
|
||||
guacenc_layer* layer_a = *((guacenc_layer**) a);
|
||||
guacenc_layer* layer_b = *((guacenc_layer**) b);
|
||||
|
||||
/* If a is NULL, sort it to bottom */
|
||||
if (layer_a == NULL) {
|
||||
|
||||
/* ... unless b is also NULL, in which case they are equal */
|
||||
if (layer_b == NULL)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* If b is NULL (and a is not NULL), sort it to bottom */
|
||||
if (layer_b == NULL)
|
||||
return -1;
|
||||
|
||||
/* Order such that the deepest layers are first */
|
||||
int a_depth = guacenc_display_get_depth(__qsort_display, layer_a);
|
||||
int b_depth = guacenc_display_get_depth(__qsort_display, layer_b);
|
||||
if (b_depth != a_depth)
|
||||
return b_depth - a_depth;
|
||||
|
||||
/* Order such that sibling layers are adjacent */
|
||||
if (layer_b->parent_index != layer_a->parent_index)
|
||||
return layer_b->parent_index - layer_a->parent_index;
|
||||
|
||||
/* Order sibling layers according to descending Z */
|
||||
return layer_b->z - layer_a->z;
|
||||
|
||||
}
|
||||
|
||||
int guacenc_display_flatten(guacenc_display* display) {
|
||||
|
||||
int i;
|
||||
guacenc_layer* render_order[GUACENC_DISPLAY_MAX_LAYERS];
|
||||
|
||||
/* Copy list of layers within display */
|
||||
memcpy(render_order, display->layers, sizeof(render_order));
|
||||
|
||||
/* Sort layers by depth, parent, and Z */
|
||||
__qsort_display = display;
|
||||
qsort(render_order, GUACENC_DISPLAY_MAX_LAYERS, sizeof(guacenc_layer*),
|
||||
guacenc_display_layer_comparator);
|
||||
|
||||
/* Reset layer frame buffers */
|
||||
for (i = 0; i < GUACENC_DISPLAY_MAX_LAYERS; i++) {
|
||||
|
||||
/* Pull current layer, ignoring unallocated layers */
|
||||
guacenc_layer* layer = render_order[i];
|
||||
if (layer == NULL)
|
||||
continue;
|
||||
|
||||
/* Get source buffer and destination frame buffer */
|
||||
guacenc_buffer* buffer = layer->buffer;
|
||||
guacenc_buffer* frame = layer->frame;
|
||||
|
||||
/* Reset frame contents */
|
||||
guacenc_buffer_copy(frame, buffer);
|
||||
|
||||
}
|
||||
|
||||
/* Render each layer, in order */
|
||||
for (i = 0; i < GUACENC_DISPLAY_MAX_LAYERS; i++) {
|
||||
|
||||
/* Pull current layer, ignoring unallocated layers */
|
||||
guacenc_layer* layer = render_order[i];
|
||||
if (layer == NULL)
|
||||
continue;
|
||||
|
||||
/* Skip fully-transparent layers */
|
||||
if (layer->opacity == 0)
|
||||
continue;
|
||||
|
||||
/* Ignore layers without a parent */
|
||||
int parent_index = layer->parent_index;
|
||||
if (parent_index == GUACENC_LAYER_NO_PARENT)
|
||||
continue;
|
||||
|
||||
/* Retrieve parent layer, ignoring layers with invalid parents */
|
||||
guacenc_layer* parent = guacenc_display_get_layer(display, parent_index);
|
||||
if (parent == NULL)
|
||||
continue;
|
||||
|
||||
/* Get source and destination frame buffer */
|
||||
guacenc_buffer* src = layer->frame;
|
||||
guacenc_buffer* dst = parent->frame;
|
||||
|
||||
/* Ignore layers with empty buffers */
|
||||
cairo_surface_t* surface = src->surface;
|
||||
if (surface == NULL)
|
||||
continue;
|
||||
|
||||
/* Ignore if parent has no pixels */
|
||||
cairo_t* cairo = dst->cairo;
|
||||
if (cairo == NULL)
|
||||
continue;
|
||||
|
||||
/* Render buffer to layer */
|
||||
cairo_reset_clip(cairo);
|
||||
cairo_rectangle(cairo, layer->x, layer->y, src->width, src->height);
|
||||
cairo_clip(cairo);
|
||||
|
||||
cairo_set_source_surface(cairo, surface, layer->x, layer->y);
|
||||
cairo_paint_with_alpha(cairo, layer->opacity / 255.0);
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
84
src/guacenc/display-image-streams.c
Normal file
84
src/guacenc/display-image-streams.c
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "display.h"
|
||||
#include "image-stream.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <guacamole/client.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
int guacenc_display_create_image_stream(guacenc_display* display, int index,
|
||||
int mask, int layer_index, const char* mimetype, int x, int y) {
|
||||
|
||||
/* Do not lookup / allocate if index is invalid */
|
||||
if (index < 0 || index > GUACENC_DISPLAY_MAX_STREAMS) {
|
||||
guacenc_log(GUAC_LOG_WARNING, "Stream index out of bounds: %i", index);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Free existing stream (if any) */
|
||||
guacenc_image_stream_free(display->image_streams[index]);
|
||||
|
||||
/* Associate new stream */
|
||||
guacenc_image_stream* stream = display->image_streams[index] =
|
||||
guacenc_image_stream_alloc(mask, layer_index, mimetype, x, y);
|
||||
|
||||
/* Return zero only if stream is not NULL */
|
||||
return stream == NULL;
|
||||
|
||||
}
|
||||
|
||||
guacenc_image_stream* guacenc_display_get_image_stream(
|
||||
guacenc_display* display, int index) {
|
||||
|
||||
/* Do not lookup / allocate if index is invalid */
|
||||
if (index < 0 || index > GUACENC_DISPLAY_MAX_STREAMS) {
|
||||
guacenc_log(GUAC_LOG_WARNING, "Stream index out of bounds: %i", index);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Return existing stream (if any) */
|
||||
return display->image_streams[index];
|
||||
|
||||
}
|
||||
|
||||
int guacenc_display_free_image_stream(guacenc_display* display, int index) {
|
||||
|
||||
/* Do not lookup / allocate if index is invalid */
|
||||
if (index < 0 || index > GUACENC_DISPLAY_MAX_STREAMS) {
|
||||
guacenc_log(GUAC_LOG_WARNING, "Stream index out of bounds: %i", index);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Free stream (if allocated) */
|
||||
guacenc_image_stream_free(display->image_streams[index]);
|
||||
|
||||
/* Mark stream as freed */
|
||||
display->image_streams[index] = NULL;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
102
src/guacenc/display-layers.c
Normal file
102
src/guacenc/display-layers.c
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "display.h"
|
||||
#include "layer.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <guacamole/client.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
guacenc_layer* guacenc_display_get_layer(guacenc_display* display,
|
||||
int index) {
|
||||
|
||||
/* Do not lookup / allocate if index is invalid */
|
||||
if (index < 0 || index > GUACENC_DISPLAY_MAX_LAYERS) {
|
||||
guacenc_log(GUAC_LOG_WARNING, "Layer index out of bounds: %i", index);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Lookup layer, allocating a new layer if necessary */
|
||||
guacenc_layer* layer = display->layers[index];
|
||||
if (layer == NULL) {
|
||||
|
||||
/* Attempt to allocate layer */
|
||||
layer = guacenc_layer_alloc();
|
||||
if (layer == NULL) {
|
||||
guacenc_log(GUAC_LOG_WARNING, "Layer allocation failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* The default layer has no parent */
|
||||
if (index == 0)
|
||||
layer->parent_index = GUACENC_LAYER_NO_PARENT;
|
||||
|
||||
/* Store layer within display for future retrieval / management */
|
||||
display->layers[index] = layer;
|
||||
|
||||
}
|
||||
|
||||
return layer;
|
||||
|
||||
}
|
||||
|
||||
int guacenc_display_get_depth(guacenc_display* display, guacenc_layer* layer) {
|
||||
|
||||
/* Non-existent layers have a depth of 0 */
|
||||
if (layer == NULL)
|
||||
return 0;
|
||||
|
||||
/* Layers with no parent have a depth of 0 */
|
||||
if (layer->parent_index == GUACENC_LAYER_NO_PARENT)
|
||||
return 0;
|
||||
|
||||
/* Retrieve parent layer */
|
||||
guacenc_layer* parent =
|
||||
guacenc_display_get_layer(display, layer->parent_index);
|
||||
|
||||
/* Current layer depth is the depth of the parent + 1 */
|
||||
return guacenc_display_get_depth(display, parent) + 1;
|
||||
|
||||
}
|
||||
|
||||
int guacenc_display_free_layer(guacenc_display* display,
|
||||
int index) {
|
||||
|
||||
/* Do not lookup / free if index is invalid */
|
||||
if (index < 0 || index > GUACENC_DISPLAY_MAX_LAYERS) {
|
||||
guacenc_log(GUAC_LOG_WARNING, "Layer index out of bounds: %i", index);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Free layer (if allocated) */
|
||||
guacenc_layer_free(display->layers[index]);
|
||||
|
||||
/* Mark layer as freed */
|
||||
display->layers[index] = NULL;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
63
src/guacenc/display-sync.c
Normal file
63
src/guacenc/display-sync.c
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "display.h"
|
||||
#include "layer.h"
|
||||
#include "log.h"
|
||||
#include "video.h"
|
||||
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/timestamp.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int guacenc_display_sync(guacenc_display* display, guac_timestamp timestamp) {
|
||||
|
||||
/* Verify timestamp is not decreasing */
|
||||
if (timestamp < display->last_sync) {
|
||||
guacenc_log(GUAC_LOG_WARNING, "Decreasing sync timestamp");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Update timestamp of display */
|
||||
display->last_sync = timestamp;
|
||||
|
||||
/* Flatten display to default layer */
|
||||
if (guacenc_display_flatten(display))
|
||||
return 1;
|
||||
|
||||
/* Retrieve default layer (guaranteed to not be NULL) */
|
||||
guacenc_layer* def_layer = guacenc_display_get_layer(display, 0);
|
||||
assert(def_layer != NULL);
|
||||
|
||||
/* Update video timeline */
|
||||
if (guacenc_video_advance_timeline(display->output, timestamp))
|
||||
return 1;
|
||||
|
||||
/* Prepare frame for write upon next flush */
|
||||
guacenc_video_prepare_frame(display->output, def_layer->frame);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
134
src/guacenc/display.c
Normal file
134
src/guacenc/display.c
Normal file
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "display.h"
|
||||
#include "video.h"
|
||||
|
||||
#include <cairo/cairo.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
cairo_operator_t guacenc_display_cairo_operator(guac_composite_mode mask) {
|
||||
|
||||
/* Translate Guacamole channel mask into Cairo operator */
|
||||
switch (mask) {
|
||||
|
||||
/* Source */
|
||||
case GUAC_COMP_SRC:
|
||||
return CAIRO_OPERATOR_SOURCE;
|
||||
|
||||
/* Over */
|
||||
case GUAC_COMP_OVER:
|
||||
return CAIRO_OPERATOR_OVER;
|
||||
|
||||
/* In */
|
||||
case GUAC_COMP_IN:
|
||||
return CAIRO_OPERATOR_IN;
|
||||
|
||||
/* Out */
|
||||
case GUAC_COMP_OUT:
|
||||
return CAIRO_OPERATOR_OUT;
|
||||
|
||||
/* Atop */
|
||||
case GUAC_COMP_ATOP:
|
||||
return CAIRO_OPERATOR_ATOP;
|
||||
|
||||
/* Over (source/destination reversed) */
|
||||
case GUAC_COMP_ROVER:
|
||||
return CAIRO_OPERATOR_DEST_OVER;
|
||||
|
||||
/* In (source/destination reversed) */
|
||||
case GUAC_COMP_RIN:
|
||||
return CAIRO_OPERATOR_DEST_IN;
|
||||
|
||||
/* Out (source/destination reversed) */
|
||||
case GUAC_COMP_ROUT:
|
||||
return CAIRO_OPERATOR_DEST_OUT;
|
||||
|
||||
/* Atop (source/destination reversed) */
|
||||
case GUAC_COMP_RATOP:
|
||||
return CAIRO_OPERATOR_DEST_ATOP;
|
||||
|
||||
/* XOR */
|
||||
case GUAC_COMP_XOR:
|
||||
return CAIRO_OPERATOR_XOR;
|
||||
|
||||
/* Additive */
|
||||
case GUAC_COMP_PLUS:
|
||||
return CAIRO_OPERATOR_ADD;
|
||||
|
||||
/* If unrecognized, just default to CAIRO_OPERATOR_OVER */
|
||||
default:
|
||||
return CAIRO_OPERATOR_OVER;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
guacenc_display* guacenc_display_alloc(const char* path, const char* codec,
|
||||
int width, int height, int bitrate) {
|
||||
|
||||
/* Prepare video encoding */
|
||||
guacenc_video* video = guacenc_video_alloc(path, codec, width, height, bitrate);
|
||||
if (video == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Allocate display */
|
||||
guacenc_display* display =
|
||||
(guacenc_display*) calloc(1, sizeof(guacenc_display));
|
||||
|
||||
/* Associate display with video output */
|
||||
display->output = video;
|
||||
|
||||
return display;
|
||||
|
||||
}
|
||||
|
||||
int guacenc_display_free(guacenc_display* display) {
|
||||
|
||||
int i;
|
||||
|
||||
/* Ignore NULL display */
|
||||
if (display == NULL)
|
||||
return 0;
|
||||
|
||||
/* Finalize video */
|
||||
int retval = guacenc_video_free(display->output);
|
||||
|
||||
/* Free all buffers */
|
||||
for (i = 0; i < GUACENC_DISPLAY_MAX_BUFFERS; i++)
|
||||
guacenc_buffer_free(display->buffers[i]);
|
||||
|
||||
/* Free all layers */
|
||||
for (i = 0; i < GUACENC_DISPLAY_MAX_LAYERS; i++)
|
||||
guacenc_layer_free(display->layers[i]);
|
||||
|
||||
/* Free all streams */
|
||||
for (i = 0; i < GUACENC_DISPLAY_MAX_STREAMS; i++)
|
||||
guacenc_image_stream_free(display->image_streams[i]);
|
||||
|
||||
free(display);
|
||||
return retval;
|
||||
|
||||
}
|
||||
|
367
src/guacenc/display.h
Normal file
367
src/guacenc/display.h
Normal file
@ -0,0 +1,367 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef GUACENC_DISPLAY_H
|
||||
#define GUACENC_DISPLAY_H
|
||||
|
||||
#include "config.h"
|
||||
#include "buffer.h"
|
||||
#include "image-stream.h"
|
||||
#include "layer.h"
|
||||
#include "video.h"
|
||||
|
||||
#include <guacamole/protocol.h>
|
||||
#include <guacamole/timestamp.h>
|
||||
|
||||
/**
|
||||
* The maximum number of buffers that the Guacamole video encoder will handle
|
||||
* within a single Guacamole protocol dump.
|
||||
*/
|
||||
#define GUACENC_DISPLAY_MAX_BUFFERS 4096
|
||||
|
||||
/**
|
||||
* The maximum number of layers that the Guacamole video encoder will handle
|
||||
* within a single Guacamole protocol dump.
|
||||
*/
|
||||
#define GUACENC_DISPLAY_MAX_LAYERS 64
|
||||
|
||||
/**
|
||||
* The maximum number of streams that the Guacamole video encoder will handle
|
||||
* within a single Guacamole protocol dump.
|
||||
*/
|
||||
#define GUACENC_DISPLAY_MAX_STREAMS 64
|
||||
|
||||
/**
|
||||
* The current state of the Guacamole video encoder's internal display.
|
||||
*/
|
||||
typedef struct guacenc_display {
|
||||
|
||||
/**
|
||||
* All currently-allocated buffers. The index of the buffer corresponds to
|
||||
* its position within this array, where -1 is the 0th entry. If a buffer
|
||||
* has not yet been allocated, or a buffer has been freed (due to a
|
||||
* "dispose" instruction), its entry here will be NULL.
|
||||
*/
|
||||
guacenc_buffer* buffers[GUACENC_DISPLAY_MAX_BUFFERS];
|
||||
|
||||
/**
|
||||
* All currently-allocated layers. The index of the layer corresponds to
|
||||
* its position within this array. If a layer has not yet been allocated,
|
||||
* or a layer has been freed (due to a "dispose" instruction), its entry
|
||||
* here will be NULL.
|
||||
*/
|
||||
guacenc_layer* layers[GUACENC_DISPLAY_MAX_LAYERS];
|
||||
|
||||
/**
|
||||
* All currently-allocated image streams. The index of the stream
|
||||
* corresponds to its position within this array. If a stream has not yet
|
||||
* been allocated, or a stream has been freed (due to an "end"
|
||||
* instruction), its entry here will be NULL.
|
||||
*/
|
||||
guacenc_image_stream* image_streams[GUACENC_DISPLAY_MAX_STREAMS];
|
||||
|
||||
/**
|
||||
* The timestamp of the last sync instruction handled, or 0 if no sync has
|
||||
* yet been read.
|
||||
*/
|
||||
guac_timestamp last_sync;
|
||||
|
||||
/**
|
||||
* The video that this display is recording to.
|
||||
*/
|
||||
guacenc_video* output;
|
||||
|
||||
} guacenc_display;
|
||||
|
||||
/**
|
||||
* Handles a received "sync" instruction having the given timestamp, flushing
|
||||
* the current display to the in-progress video encoding.
|
||||
*
|
||||
* @param display
|
||||
* The display to flush to the video encoding as a new frame.
|
||||
*
|
||||
* @param timestamp
|
||||
* The timestamp of the new frame, as dictated by the "sync" instruction
|
||||
* sent at the end of the frame.
|
||||
*
|
||||
* @return
|
||||
* Zero if the frame was successfully written, non-zero if an error occurs.
|
||||
*/
|
||||
int guacenc_display_sync(guacenc_display* display, guac_timestamp timestamp);
|
||||
|
||||
/**
|
||||
* Flattens the given display, rendering all child layers to the frame buffers
|
||||
* of their parent layers. The frame buffer of the default layer of the display
|
||||
* will thus contain the flattened, composited rendering of the entire display
|
||||
* state after this function succeeds. The contents of the frame buffers of
|
||||
* each layer are replaced by this function.
|
||||
*
|
||||
* @param display
|
||||
* The display to flatten.
|
||||
*
|
||||
* @return
|
||||
* Zero if the flatten operation succeeds, non-zero if an error occurs
|
||||
* preventing proper rendering.
|
||||
*/
|
||||
int guacenc_display_flatten(guacenc_display* display);
|
||||
|
||||
/**
|
||||
* Allocates a new Guacamole video encoder display. This display serves as the
|
||||
* representation of encoding state, as well as the state of the Guacamole
|
||||
* display as instructions are read and handled.
|
||||
*
|
||||
* @param path
|
||||
* The full path to the file in which encoded video should be written.
|
||||
*
|
||||
* @param codec
|
||||
* The name of the codec to use for the video encoding, as defined by
|
||||
* ffmpeg / libavcodec.
|
||||
*
|
||||
* @param width
|
||||
* The width of the desired video, in pixels.
|
||||
*
|
||||
* @param height
|
||||
* The height of the desired video, in pixels.
|
||||
*
|
||||
* @param bitrate
|
||||
* The desired overall bitrate of the resulting encoded video, in bits per
|
||||
* second.
|
||||
*
|
||||
* @return
|
||||
* The newly-allocated Guacamole video encoder display, or NULL if the
|
||||
* display could not be allocated.
|
||||
*/
|
||||
guacenc_display* guacenc_display_alloc(const char* path, const char* codec,
|
||||
int width, int height, int bitrate);
|
||||
|
||||
/**
|
||||
* Frees all memory associated with the given Guacamole video encoder display,
|
||||
* and finishes any underlying encoding process. If the given display is NULL,
|
||||
* this function has no effect.
|
||||
*
|
||||
* @param display
|
||||
* The Guacamole video encoder display to free, which may be NULL.
|
||||
*
|
||||
* @return
|
||||
* Zero if the encoding process completed successfully, non-zero otherwise.
|
||||
*/
|
||||
int guacenc_display_free(guacenc_display* display);
|
||||
|
||||
/**
|
||||
* Returns the layer having the given index. A new layer will be allocated if
|
||||
* necessary. If the layer having the given index already exists, it will be
|
||||
* returned.
|
||||
*
|
||||
* @param display
|
||||
* The Guacamole video encoder display to retrieve the layer from.
|
||||
*
|
||||
* @param index
|
||||
* The index of the layer to retrieve. All valid layer indices are
|
||||
* non-negative.
|
||||
*
|
||||
* @return
|
||||
* The layer having the given index, or NULL if the index is invalid or
|
||||
* a new layer cannot be allocated.
|
||||
*/
|
||||
guacenc_layer* guacenc_display_get_layer(guacenc_display* display,
|
||||
int index);
|
||||
|
||||
/**
|
||||
* Returns the depth of a given layer in terms of parent layers. The layer
|
||||
* depth is the number of layers above the given layer in hierarchy, where a
|
||||
* layer without any parent (such as the default layer) has a depth of 0.
|
||||
*
|
||||
* @param layer
|
||||
* The layer to check.
|
||||
*
|
||||
* @return
|
||||
* The depth of the layer.
|
||||
*/
|
||||
int guacenc_display_get_depth(guacenc_display* display, guacenc_layer* layer);
|
||||
|
||||
/**
|
||||
* Frees all resources associated with the layer having the given index. If
|
||||
* the layer has not been allocated, this function has no effect.
|
||||
*
|
||||
* @param display
|
||||
* The Guacamole video encoder display associated with the layer being
|
||||
* freed.
|
||||
*
|
||||
* @param index
|
||||
* The index of the layer to free. All valid layer indices are
|
||||
* non-negative.
|
||||
*
|
||||
* @return
|
||||
* Zero if the layer was successfully freed or was not allocated, non-zero
|
||||
* if the layer could not be freed as the index was invalid.
|
||||
*/
|
||||
int guacenc_display_free_layer(guacenc_display* display, int index);
|
||||
|
||||
/**
|
||||
* Returns the buffer having the given index. A new buffer will be allocated if
|
||||
* necessary. If the buffer having the given index already exists, it will be
|
||||
* returned.
|
||||
*
|
||||
* @param display
|
||||
* The Guacamole video encoder display to retrieve the buffer from.
|
||||
*
|
||||
* @param index
|
||||
* The index of the buffer to retrieve. All valid buffer indices are
|
||||
* negative.
|
||||
*
|
||||
* @return
|
||||
* The buffer having the given index, or NULL if the index is invalid or
|
||||
* a new buffer cannot be allocated.
|
||||
*/
|
||||
guacenc_buffer* guacenc_display_get_buffer(guacenc_display* display,
|
||||
int index);
|
||||
|
||||
/**
|
||||
* Frees all resources associated with the buffer having the given index. If
|
||||
* the buffer has not been allocated, this function has no effect.
|
||||
*
|
||||
* @param display
|
||||
* The Guacamole video encoder display associated with the buffer being
|
||||
* freed.
|
||||
*
|
||||
* @param index
|
||||
* The index of the buffer to free. All valid buffer indices are negative.
|
||||
*
|
||||
* @return
|
||||
* Zero if the buffer was successfully freed or was not allocated, non-zero
|
||||
* if the buffer could not be freed as the index was invalid.
|
||||
*/
|
||||
int guacenc_display_free_buffer(guacenc_display* display, int index);
|
||||
|
||||
/**
|
||||
* Returns the buffer associated with the layer or buffer having the given
|
||||
* index. A new buffer or layer will be allocated if necessary. If the given
|
||||
* index refers to a layer (is non-negative), the buffer underlying that layer
|
||||
* will be returned. If the given index refers to a buffer (is negative), that
|
||||
* buffer will be returned directly.
|
||||
*
|
||||
* @param display
|
||||
* The Guacamole video encoder display to retrieve the buffer from.
|
||||
*
|
||||
* @param index
|
||||
* The index of the buffer or layer whose associated buffer should be
|
||||
* retrieved.
|
||||
*
|
||||
* @return
|
||||
* The buffer associated with the buffer or layer having the given index,
|
||||
* or NULL if the index is invalid.
|
||||
*/
|
||||
guacenc_buffer* guacenc_display_get_related_buffer(guacenc_display* display,
|
||||
int index);
|
||||
|
||||
/**
|
||||
* Creates a new image stream having the given index. If the stream having the
|
||||
* given index already exists, it will be freed and replaced. If the mimetype
|
||||
* specified is not supported, the image stream will still be allocated but
|
||||
* will have no associated decoder (blobs send to that stream will have no
|
||||
* effect).
|
||||
*
|
||||
* @param display
|
||||
* The Guacamole video encoder display to associate with the
|
||||
* newly-created image stream.
|
||||
*
|
||||
* @param index
|
||||
* The index of the stream to create. All valid stream indices are
|
||||
* non-negative.
|
||||
*
|
||||
* @param mask
|
||||
* The Guacamole protocol compositing operation (channel mask) to apply
|
||||
* when drawing the image.
|
||||
*
|
||||
* @param layer_index
|
||||
* The index of the layer or buffer that the image should be drawn to.
|
||||
*
|
||||
* @param mimetype
|
||||
* The mimetype of the image data that will be received along this stream.
|
||||
*
|
||||
* @param x
|
||||
* The X coordinate of the upper-left corner of the rectangle within the
|
||||
* destination layer or buffer that the image should be drawn to.
|
||||
*
|
||||
* @param y
|
||||
* The Y coordinate of the upper-left corner of the rectangle within the
|
||||
* destination layer or buffer that the image should be drawn to.
|
||||
*
|
||||
* @return
|
||||
* Zero if the image stream was successfully created, non-zero otherwise.
|
||||
*/
|
||||
int guacenc_display_create_image_stream(guacenc_display* display, int index,
|
||||
int mask, int layer_index, const char* mimetype, int x, int y);
|
||||
|
||||
/**
|
||||
* Returns the stream having the given index. If no such stream exists, NULL
|
||||
* will be returned.
|
||||
*
|
||||
* @param display
|
||||
* The Guacamole video encoder display to retrieve the image stream from.
|
||||
*
|
||||
* @param index
|
||||
* The index of the stream to retrieve. All valid stream indices are
|
||||
* non-negative.
|
||||
*
|
||||
* @return
|
||||
* The stream having the given index, or NULL if the index is invalid or
|
||||
* a no such stream exists.
|
||||
*/
|
||||
guacenc_image_stream* guacenc_display_get_image_stream(
|
||||
guacenc_display* display, int index);
|
||||
|
||||
/**
|
||||
* Frees all resources associated with the stream having the given index. If
|
||||
* the stream has not been allocated, this function has no effect.
|
||||
*
|
||||
* @param display
|
||||
* The Guacamole video encoder display associated with the image stream
|
||||
* being freed.
|
||||
*
|
||||
* @param index
|
||||
* The index of the stream to free. All valid stream indices are
|
||||
* non-negative.
|
||||
*
|
||||
* @return
|
||||
* Zero if the stream was successfully freed or was not allocated, non-zero
|
||||
* if the stream could not be freed as the index was invalid.
|
||||
*/
|
||||
int guacenc_display_free_image_stream(guacenc_display* display, int index);
|
||||
|
||||
/**
|
||||
* Translates the given Guacamole protocol compositing mode (channel mask) to
|
||||
* the corresponding Cairo composition operator. If no such operator exists,
|
||||
* CAIRO_OPERATOR_OVER will be returned by default.
|
||||
*
|
||||
* @param mask
|
||||
* The Guacamole protocol compositing mode (channel mask) to translate.
|
||||
*
|
||||
* @return
|
||||
* The cairo_operator_t that corresponds to the given compositing mode
|
||||
* (channel mask). CAIRO_OPERATOR_OVER will be returned by default if no
|
||||
* such operator exists.
|
||||
*/
|
||||
cairo_operator_t guacenc_display_cairo_operator(guac_composite_mode mask);
|
||||
|
||||
#endif
|
||||
|
156
src/guacenc/encode.c
Normal file
156
src/guacenc/encode.c
Normal file
@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "display.h"
|
||||
#include "instructions.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/error.h>
|
||||
#include <guacamole/parser.h>
|
||||
#include <guacamole/socket.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/**
|
||||
* Reads and handles all Guacamole instructions from the given guac_socket
|
||||
* until end-of-stream is reached.
|
||||
*
|
||||
* @param display
|
||||
* The current internal display of the Guacamole video encoder.
|
||||
*
|
||||
* @param path
|
||||
* The name of the file being parsed (for logging purposes). This file
|
||||
* must already be open and available through the given socket.
|
||||
*
|
||||
* @param socket
|
||||
* The guac_socket through which instructions should be read.
|
||||
*
|
||||
* @return
|
||||
* Zero on success, non-zero if parsing of Guacamole protocol data through
|
||||
* the given socket fails.
|
||||
*/
|
||||
static int guacenc_read_instructions(guacenc_display* display,
|
||||
const char* path, guac_socket* socket) {
|
||||
|
||||
/* Obtain Guacamole protocol parser */
|
||||
guac_parser* parser = guac_parser_alloc();
|
||||
if (parser == NULL)
|
||||
return 1;
|
||||
|
||||
/* Continuously read and handle all instructions */
|
||||
while (!guac_parser_read(parser, socket, -1)) {
|
||||
guacenc_handle_instruction(display, parser->opcode,
|
||||
parser->argc, parser->argv);
|
||||
}
|
||||
|
||||
/* Fail on read/parse error */
|
||||
if (guac_error != GUAC_STATUS_CLOSED) {
|
||||
guacenc_log(GUAC_LOG_ERROR, "%s: %s",
|
||||
path, guac_status_string(guac_error));
|
||||
guac_parser_free(parser);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Parse complete */
|
||||
guac_parser_free(parser);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int guacenc_encode(const char* path, const char* out_path, const char* codec,
|
||||
int width, int height, int bitrate, bool force) {
|
||||
|
||||
/* Open input file */
|
||||
int fd = open(path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
guacenc_log(GUAC_LOG_ERROR, "%s: %s", path, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Lock entire input file for reading by the current process */
|
||||
struct flock file_lock = {
|
||||
.l_type = F_RDLCK,
|
||||
.l_whence = SEEK_SET,
|
||||
.l_start = 0,
|
||||
.l_len = 0,
|
||||
.l_pid = getpid()
|
||||
};
|
||||
|
||||
/* Abort if file cannot be locked for reading */
|
||||
if (!force && fcntl(fd, F_SETLK, &file_lock) == -1) {
|
||||
|
||||
/* Warn if lock cannot be acquired */
|
||||
if (errno == EACCES || errno == EAGAIN)
|
||||
guacenc_log(GUAC_LOG_WARNING, "Refusing to encode in-progress "
|
||||
"recording \"%s\" (specify the -f option to override "
|
||||
"this behavior).", path);
|
||||
|
||||
/* Log an error if locking fails in an unexpected way */
|
||||
else
|
||||
guacenc_log(GUAC_LOG_ERROR, "Cannot lock \"%s\" for reading: %s",
|
||||
path, strerror(errno));
|
||||
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Allocate display for encoding process */
|
||||
guacenc_display* display = guacenc_display_alloc(out_path, codec,
|
||||
width, height, bitrate);
|
||||
if (display == NULL) {
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Obtain guac_socket wrapping file descriptor */
|
||||
guac_socket* socket = guac_socket_open(fd);
|
||||
if (socket == NULL) {
|
||||
guacenc_log(GUAC_LOG_ERROR, "%s: %s", path,
|
||||
guac_status_string(guac_error));
|
||||
close(fd);
|
||||
guacenc_display_free(display);
|
||||
return 1;
|
||||
}
|
||||
|
||||
guacenc_log(GUAC_LOG_INFO, "Encoding \"%s\" to \"%s\" ...", path, out_path);
|
||||
|
||||
/* Attempt to read all instructions in the file */
|
||||
if (guacenc_read_instructions(display, path, socket)) {
|
||||
guac_socket_free(socket);
|
||||
guacenc_display_free(display);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Close input and finish encoding process */
|
||||
guac_socket_free(socket);
|
||||
return guacenc_display_free(display);
|
||||
|
||||
}
|
||||
|
68
src/guacenc/encode.h
Normal file
68
src/guacenc/encode.h
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef GUACENC_ENCODE_H
|
||||
#define GUACENC_ENCODE_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* Encodes the given Guacamole protocol dump as video. A read lock will be
|
||||
* acquired on the input file to ensure that in-progress recordings are not
|
||||
* encoded. This behavior can be overridden by specifying true for the force
|
||||
* parameter.
|
||||
*
|
||||
* @param path
|
||||
* The path to the file containing the raw Guacamole protocol dump.
|
||||
*
|
||||
* @param out_path
|
||||
* The full path to the file in which encoded video should be written.
|
||||
*
|
||||
* @param codec
|
||||
* The name of the codec to use for the video encoding, as defined by
|
||||
* ffmpeg / libavcodec.
|
||||
*
|
||||
* @param width
|
||||
* The width of the desired video, in pixels.
|
||||
*
|
||||
* @param height
|
||||
* The height of the desired video, in pixels.
|
||||
*
|
||||
* @param bitrate
|
||||
* The desired overall bitrate of the resulting encoded video, in bits per
|
||||
* second.
|
||||
*
|
||||
* @param force
|
||||
* Perform the encoding, even if the input file appears to be an
|
||||
* in-progress recording (has an associated lock).
|
||||
*
|
||||
* @return
|
||||
* Zero on success, non-zero if an error prevented successful encoding of
|
||||
* the video.
|
||||
*/
|
||||
int guacenc_encode(const char* path, const char* out_path, const char* codec,
|
||||
int width, int height, int bitrate, bool force);
|
||||
|
||||
#endif
|
||||
|
53
src/guacenc/ffmpeg-compat.h
Normal file
53
src/guacenc/ffmpeg-compat.h
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef GUACENC_FFMPEG_COMPAT_H
|
||||
#define GUACENC_FFMPEG_COMPAT_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <libavcodec/avcodec.h>
|
||||
|
||||
/*
|
||||
* For a full list of FFmpeg API changes over the years, see:
|
||||
*
|
||||
* https://github.com/FFmpeg/FFmpeg/blob/master/doc/APIchanges
|
||||
*/
|
||||
|
||||
/* For libavcodec < 55.28.1: av_frame_*() was avcodec_*_frame(). */
|
||||
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,28,1)
|
||||
#define av_frame_alloc avcodec_alloc_frame
|
||||
#define av_frame_free avcodec_free_frame
|
||||
#endif
|
||||
|
||||
/* For libavcodec < 55.52.0: avcodec_free_context did not exist */
|
||||
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,52,0)
|
||||
#define avcodec_free_context av_freep
|
||||
#endif
|
||||
|
||||
/* For libavcodec < 57.7.0: av_packet_unref() was av_free_packet() */
|
||||
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57,7,0)
|
||||
#define av_packet_unref av_free_packet
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
152
src/guacenc/guacenc.c
Normal file
152
src/guacenc/guacenc.c
Normal file
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "encode.h"
|
||||
#include "guacenc.h"
|
||||
#include "log.h"
|
||||
#include "parse.h"
|
||||
|
||||
#include <libavcodec/avcodec.h>
|
||||
|
||||
#include <getopt.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
|
||||
int i;
|
||||
|
||||
/* Load defaults */
|
||||
bool force = false;
|
||||
int width = GUACENC_DEFAULT_WIDTH;
|
||||
int height = GUACENC_DEFAULT_HEIGHT;
|
||||
int bitrate = GUACENC_DEFAULT_BITRATE;
|
||||
|
||||
/* Parse arguments */
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "s:r:f")) != -1) {
|
||||
|
||||
/* -s: Dimensions (WIDTHxHEIGHT) */
|
||||
if (opt == 's') {
|
||||
if (guacenc_parse_dimensions(optarg, &width, &height)) {
|
||||
guacenc_log(GUAC_LOG_ERROR, "Invalid dimensions.");
|
||||
goto invalid_options;
|
||||
}
|
||||
}
|
||||
|
||||
/* -r: Bitrate (bits per second) */
|
||||
else if (opt == 'r') {
|
||||
if (guacenc_parse_int(optarg, &bitrate)) {
|
||||
guacenc_log(GUAC_LOG_ERROR, "Invalid bitrate.");
|
||||
goto invalid_options;
|
||||
}
|
||||
}
|
||||
|
||||
/* -f: Force */
|
||||
else if (opt == 'f')
|
||||
force = true;
|
||||
|
||||
/* Invalid option */
|
||||
else {
|
||||
goto invalid_options;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Log start */
|
||||
guacenc_log(GUAC_LOG_INFO, "Guacamole video encoder (guacenc) "
|
||||
"version " VERSION);
|
||||
|
||||
/* Prepare libavcodec */
|
||||
avcodec_register_all();
|
||||
|
||||
/* Track number of overall failures */
|
||||
int total_files = argc - optind;
|
||||
int failures = 0;
|
||||
|
||||
/* Abort if no files given */
|
||||
if (total_files <= 0) {
|
||||
guacenc_log(GUAC_LOG_INFO, "No input files specified. Nothing to do.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
guacenc_log(GUAC_LOG_INFO, "%i input file(s) provided.", total_files);
|
||||
|
||||
guacenc_log(GUAC_LOG_INFO, "Video will be encoded at %ix%i "
|
||||
"and %i bps.", width, height, bitrate);
|
||||
|
||||
/* Encode all input files */
|
||||
for (i = optind; i < argc; i++) {
|
||||
|
||||
/* Get current filename */
|
||||
const char* path = argv[i];
|
||||
|
||||
/* Generate output filename */
|
||||
char out_path[4096];
|
||||
int len = snprintf(out_path, sizeof(out_path), "%s.m4v", path);
|
||||
|
||||
/* Do not write if filename exceeds maximum length */
|
||||
if (len >= sizeof(out_path)) {
|
||||
guacenc_log(GUAC_LOG_ERROR, "Cannot write output file for \"%s\": "
|
||||
"Name too long", path);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Attempt encoding, log granular success/failure at debug level */
|
||||
if (guacenc_encode(path, out_path, "mpeg4",
|
||||
width, height, bitrate, force)) {
|
||||
failures++;
|
||||
guacenc_log(GUAC_LOG_DEBUG,
|
||||
"%s was NOT successfully encoded.", path);
|
||||
}
|
||||
else
|
||||
guacenc_log(GUAC_LOG_DEBUG, "%s was successfully encoded.", path);
|
||||
|
||||
}
|
||||
|
||||
/* Warn if at least one file failed */
|
||||
if (failures != 0)
|
||||
guacenc_log(GUAC_LOG_WARNING, "Encoding failed for %i of %i file(s).",
|
||||
failures, total_files);
|
||||
|
||||
/* Notify of success */
|
||||
else
|
||||
guacenc_log(GUAC_LOG_INFO, "All files encoded successfully.");
|
||||
|
||||
/* Encoding complete */
|
||||
return 0;
|
||||
|
||||
/* Display usage and exit with error if options are invalid */
|
||||
invalid_options:
|
||||
|
||||
fprintf(stderr, "USAGE: %s"
|
||||
" [-s WIDTHxHEIGHT]"
|
||||
" [-r BITRATE]"
|
||||
" [-f]"
|
||||
" [FILE]...\n", argv[0]);
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
56
src/guacenc/guacenc.h
Normal file
56
src/guacenc/guacenc.h
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef GUACENC_H
|
||||
#define GUACENC_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <guacamole/client.h>
|
||||
|
||||
/**
|
||||
* The width of the output video, in pixels, if no other width is given on the
|
||||
* command line. Note that different codecs will have different restrictions
|
||||
* regarding legal widths.
|
||||
*/
|
||||
#define GUACENC_DEFAULT_WIDTH 640
|
||||
|
||||
/**
|
||||
* The height of the output video, in pixels, if no other height is given on the
|
||||
* command line. Note that different codecs will have different restrictions
|
||||
* regarding legal heights.
|
||||
*/
|
||||
#define GUACENC_DEFAULT_HEIGHT 480
|
||||
|
||||
/**
|
||||
* The desired bitrate of the output video, in bits per second, if no other
|
||||
* bitrate is given on the command line.
|
||||
*/
|
||||
#define GUACENC_DEFAULT_BITRATE 2000000
|
||||
|
||||
/**
|
||||
* The default log level below which no messages should be logged.
|
||||
*/
|
||||
#define GUACENC_DEFAULT_LOG_LEVEL GUAC_LOG_INFO
|
||||
|
||||
#endif
|
||||
|
169
src/guacenc/image-stream.c
Normal file
169
src/guacenc/image-stream.c
Normal file
@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "image-stream.h"
|
||||
#include "jpeg.h"
|
||||
#include "log.h"
|
||||
#include "png.h"
|
||||
|
||||
#ifdef ENABLE_WEBP
|
||||
#include "webp.h"
|
||||
#endif
|
||||
|
||||
#include <cairo/cairo.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
guacenc_decoder_mapping guacenc_decoder_map[] = {
|
||||
{"image/png", guacenc_png_decoder},
|
||||
{"image/jpeg", guacenc_jpeg_decoder},
|
||||
#ifdef ENABLE_WEBP
|
||||
{"image/webp", guacenc_webp_decoder},
|
||||
#endif
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
guacenc_decoder* guacenc_get_decoder(const char* mimetype) {
|
||||
|
||||
/* Search through mapping for the decoder having given mimetype */
|
||||
guacenc_decoder_mapping* current = guacenc_decoder_map;
|
||||
while (current->mimetype != NULL) {
|
||||
|
||||
/* Return decoder if mimetype matches */
|
||||
if (strcmp(current->mimetype, mimetype) == 0)
|
||||
return current->decoder;
|
||||
|
||||
/* Next candidate decoder */
|
||||
current++;
|
||||
|
||||
}
|
||||
|
||||
/* No such decoder */
|
||||
guacenc_log(GUAC_LOG_WARNING, "Support for \"%s\" not present", mimetype);
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
guacenc_image_stream* guacenc_image_stream_alloc(int mask, int index,
|
||||
const char* mimetype, int x, int y) {
|
||||
|
||||
/* Allocate stream */
|
||||
guacenc_image_stream* stream = malloc(sizeof(guacenc_image_stream));
|
||||
if (stream == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Init properties */
|
||||
stream->index = index;
|
||||
stream->mask = mask;
|
||||
stream->x = x;
|
||||
stream->y = y;
|
||||
|
||||
/* Associate with corresponding decoder */
|
||||
stream->decoder = guacenc_get_decoder(mimetype);
|
||||
|
||||
/* Allocate initial buffer */
|
||||
stream->length = 0;
|
||||
stream->max_length = GUACENC_IMAGE_STREAM_INITIAL_LENGTH;
|
||||
stream->buffer = (unsigned char*) malloc(stream->max_length);
|
||||
|
||||
return stream;
|
||||
|
||||
}
|
||||
|
||||
int guacenc_image_stream_receive(guacenc_image_stream* stream,
|
||||
unsigned char* data, int length) {
|
||||
|
||||
/* Allocate more space if necessary */
|
||||
if (stream->max_length - stream->length < length) {
|
||||
|
||||
/* Calculate a reasonable new max length guaranteed to fit buffer */
|
||||
int new_max_length = stream->max_length * 2 + length;
|
||||
|
||||
/* Attempt to resize buffer */
|
||||
unsigned char* new_buffer =
|
||||
(unsigned char*) realloc(stream->buffer, new_max_length);
|
||||
if (new_buffer == NULL)
|
||||
return 1;
|
||||
|
||||
/* Store updated buffer and size */
|
||||
stream->buffer = new_buffer;
|
||||
stream->max_length = new_max_length;
|
||||
|
||||
}
|
||||
|
||||
/* Append data */
|
||||
memcpy(stream->buffer + stream->length, data, length);
|
||||
stream->length += length;
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int guacenc_image_stream_end(guacenc_image_stream* stream,
|
||||
guacenc_buffer* buffer) {
|
||||
|
||||
/* If there is no decoder, simply return success */
|
||||
guacenc_decoder* decoder = stream->decoder;
|
||||
if (decoder == NULL)
|
||||
return 0;
|
||||
|
||||
/* Decode received data to a Cairo surface */
|
||||
cairo_surface_t* surface = stream->decoder(stream->buffer, stream->length);
|
||||
if (surface == NULL)
|
||||
return 1;
|
||||
|
||||
/* Get surface dimensions */
|
||||
int width = cairo_image_surface_get_width(surface);
|
||||
int height = cairo_image_surface_get_height(surface);
|
||||
|
||||
/* Expand the buffer as necessary to fit the draw operation */
|
||||
if (buffer->autosize)
|
||||
guacenc_buffer_fit(buffer, stream->x + width, stream->y + height);
|
||||
|
||||
/* Draw surface to buffer */
|
||||
if (buffer->cairo != NULL) {
|
||||
cairo_set_source_surface(buffer->cairo, surface, stream->x, stream->y);
|
||||
cairo_rectangle(buffer->cairo, stream->x, stream->y, width, height);
|
||||
cairo_fill(buffer->cairo);
|
||||
}
|
||||
|
||||
cairo_surface_destroy(surface);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int guacenc_image_stream_free(guacenc_image_stream* stream) {
|
||||
|
||||
/* Ignore NULL streams */
|
||||
if (stream == NULL)
|
||||
return 0;
|
||||
|
||||
/* Free image buffer */
|
||||
free(stream->buffer);
|
||||
|
||||
/* Free actual stream */
|
||||
free(stream);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
235
src/guacenc/image-stream.h
Normal file
235
src/guacenc/image-stream.h
Normal file
@ -0,0 +1,235 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef GUACENC_IMAGE_STREAM_H
|
||||
#define GUACENC_IMAGE_STREAM_H
|
||||
|
||||
#include "config.h"
|
||||
#include "buffer.h"
|
||||
|
||||
#include <cairo/cairo.h>
|
||||
|
||||
/**
|
||||
* The initial number of bytes to allocate for the image data buffer. If this
|
||||
* buffer is not sufficiently large, it will be dynamically reallocated as it
|
||||
* grows.
|
||||
*/
|
||||
#define GUACENC_IMAGE_STREAM_INITIAL_LENGTH 4096
|
||||
|
||||
/**
|
||||
* Callback function which is provided raw, encoded image data of the given
|
||||
* length. The function is expected to return a new Cairo surface which will
|
||||
* later (by guacenc) be freed via cairo_surface_destroy().
|
||||
*
|
||||
* @param data
|
||||
* The raw encoded image data that this function must decode.
|
||||
*
|
||||
* @param length
|
||||
* The length of the image data, in bytes.
|
||||
*
|
||||
* @return
|
||||
* A newly-allocated Cairo surface containing the decoded image, or NULL
|
||||
* or decoding fails.
|
||||
*/
|
||||
typedef cairo_surface_t* guacenc_decoder(unsigned char* data, int length);
|
||||
|
||||
/**
|
||||
* The current state of an allocated Guacamole image stream.
|
||||
*/
|
||||
typedef struct guacenc_image_stream {
|
||||
|
||||
/**
|
||||
* The index of the destination layer or buffer.
|
||||
*/
|
||||
int index;
|
||||
|
||||
/**
|
||||
* The Guacamole protocol compositing operation (channel mask) to apply
|
||||
* when drawing the image.
|
||||
*/
|
||||
int mask;
|
||||
|
||||
/**
|
||||
* The X coordinate of the upper-left corner of the rectangle within the
|
||||
* destination layer or buffer that the decoded image should be drawn to.
|
||||
*/
|
||||
int x;
|
||||
|
||||
/**
|
||||
* The Y coordinate of the upper-left corner of the rectangle within the
|
||||
* destination layer or buffer that the decoded image should be drawn to.
|
||||
*/
|
||||
int y;
|
||||
|
||||
/**
|
||||
* Buffer of image data which will be built up over time as chunks are
|
||||
* received via "blob" instructions. This will ultimately be passed in its
|
||||
* entirety to the decoder function.
|
||||
*/
|
||||
unsigned char* buffer;
|
||||
|
||||
/**
|
||||
* The number of bytes currently stored in the buffer.
|
||||
*/
|
||||
int length;
|
||||
|
||||
/**
|
||||
* The maximum number of bytes that can be stored in the current buffer
|
||||
* before it must be reallocated.
|
||||
*/
|
||||
int max_length;
|
||||
|
||||
/**
|
||||
* The decoder to use when decoding the raw data received along this
|
||||
* stream, or NULL if no such decoder exists.
|
||||
*/
|
||||
guacenc_decoder* decoder;
|
||||
|
||||
} guacenc_image_stream;
|
||||
|
||||
/**
|
||||
* Mapping of image mimetype to corresponding decoder function.
|
||||
*/
|
||||
typedef struct guacenc_decoder_mapping {
|
||||
|
||||
/**
|
||||
* The mimetype of the image that the associated decoder can read.
|
||||
*/
|
||||
const char* mimetype;
|
||||
|
||||
/**
|
||||
* The decoder function to use when an image stream of the associated
|
||||
* mimetype is received.
|
||||
*/
|
||||
guacenc_decoder* decoder;
|
||||
|
||||
} guacenc_decoder_mapping;
|
||||
|
||||
/**
|
||||
* Array of all mimetype/decoder mappings for all supported image types,
|
||||
* terminated by an entry with a NULL mimetype.
|
||||
*/
|
||||
extern guacenc_decoder_mapping guacenc_decoder_map[];
|
||||
|
||||
/**
|
||||
* Returns the decoder associated with the given mimetype. If no such decoder
|
||||
* exists, NULL is returned.
|
||||
*
|
||||
* @param mimetype
|
||||
* The image mimetype to return the associated decoder of.
|
||||
*
|
||||
* @return
|
||||
* The decoder associated with the given mimetype, or NULL if no such
|
||||
* decoder exists.
|
||||
*/
|
||||
guacenc_decoder* guacenc_get_decoder(const char* mimetype);
|
||||
|
||||
/**
|
||||
* Allocates and initializes a new image stream. This allocation is independent
|
||||
* of the Guacamole video encoder display; the allocated guacenc_image_stream
|
||||
* will not automatically be associated with the active display, nor will the
|
||||
* provided layer/buffer index be validated.
|
||||
*
|
||||
* @param mask
|
||||
* The Guacamole protocol compositing operation (channel mask) to apply
|
||||
* when drawing the image.
|
||||
*
|
||||
* @param index
|
||||
* The index of the layer or buffer that the image should be drawn to.
|
||||
*
|
||||
* @param mimetype
|
||||
* The mimetype of the image data that will be received along this stream.
|
||||
*
|
||||
* @param x
|
||||
* The X coordinate of the upper-left corner of the rectangle within the
|
||||
* destination layer or buffer that the image should be drawn to.
|
||||
*
|
||||
* @param y
|
||||
* The Y coordinate of the upper-left corner of the rectangle within the
|
||||
* destination layer or buffer that the image should be drawn to.
|
||||
*
|
||||
* @return
|
||||
* A newly-allocated and initialized guacenc_image_stream, or NULL if
|
||||
* allocation fails.
|
||||
*/
|
||||
guacenc_image_stream* guacenc_image_stream_alloc(int mask, int index,
|
||||
const char* mimetype, int x, int y);
|
||||
|
||||
/**
|
||||
* Appends newly-received data to the internal buffer of the given image
|
||||
* stream, such that the entire received image can be fed to the decoder as one
|
||||
* buffer once the stream ends.
|
||||
*
|
||||
* @param stream
|
||||
* The image stream that received the data.
|
||||
*
|
||||
* @param data
|
||||
* The chunk of data received along the image stream.
|
||||
*
|
||||
* @param length
|
||||
* The length of the chunk of data received, in bytes.
|
||||
*
|
||||
* @return
|
||||
* Zero if the given data was successfully appended to the in-progress
|
||||
* image, non-zero if an error occurs.
|
||||
*/
|
||||
int guacenc_image_stream_receive(guacenc_image_stream* stream,
|
||||
unsigned char* data, int length);
|
||||
|
||||
/**
|
||||
* Marks the end of the given image stream (no more data will be received) and
|
||||
* invokes the associated decoder. The decoded image will be written to the
|
||||
* given buffer as-is. If no decoder is associated with the given image stream,
|
||||
* this function has no effect. Meta-information describing the image draw
|
||||
* operation itself is pulled from the guacenc_image_stream, having been stored
|
||||
* there when the image stream was created.
|
||||
*
|
||||
* @param stream
|
||||
* The image stream that has ended.
|
||||
*
|
||||
* @param buffer
|
||||
* The buffer that the decoded image should be written to.
|
||||
*
|
||||
* @return
|
||||
* Zero if the image is written successfully, or non-zero if an error
|
||||
* occurs.
|
||||
*/
|
||||
int guacenc_image_stream_end(guacenc_image_stream* stream,
|
||||
guacenc_buffer* buffer);
|
||||
|
||||
/**
|
||||
* Frees the given image stream and all associated data. If the image stream
|
||||
* has not yet ended (reached end-of-stream), no image will be drawn to the
|
||||
* associated buffer or layer.
|
||||
*
|
||||
* @param stream
|
||||
* The stream to free.
|
||||
*
|
||||
* @return
|
||||
* Zero if freeing the stream succeeded, or non-zero if freeing the stream
|
||||
* failed (for example, due to an error in the free handler of the
|
||||
* decoder).
|
||||
*/
|
||||
int guacenc_image_stream_free(guacenc_image_stream* stream);
|
||||
|
||||
#endif
|
||||
|
55
src/guacenc/instruction-blob.c
Normal file
55
src/guacenc/instruction-blob.c
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "display.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/protocol.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
int guacenc_handle_blob(guacenc_display* display, int argc, char** argv) {
|
||||
|
||||
/* Verify argument count */
|
||||
if (argc < 2) {
|
||||
guacenc_log(GUAC_LOG_WARNING, "\"blob\" instruction incomplete");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Parse arguments */
|
||||
int index = atoi(argv[0]);
|
||||
char* data = argv[1];
|
||||
int length = guac_protocol_decode_base64(data);
|
||||
|
||||
/* Retrieve image stream */
|
||||
guacenc_image_stream* stream =
|
||||
guacenc_display_get_image_stream(display, index);
|
||||
if (stream == NULL)
|
||||
return 1;
|
||||
|
||||
/* Send data to decoder within associated stream */
|
||||
return guacenc_image_stream_receive(stream, (unsigned char*) data, length);
|
||||
|
||||
}
|
||||
|
62
src/guacenc/instruction-cfill.c
Normal file
62
src/guacenc/instruction-cfill.c
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "display.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <guacamole/client.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
int guacenc_handle_cfill(guacenc_display* display, int argc, char** argv) {
|
||||
|
||||
/* Verify argument count */
|
||||
if (argc < 6) {
|
||||
guacenc_log(GUAC_LOG_WARNING, "\"cfill\" instruction incomplete");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Parse arguments */
|
||||
int mask = atoi(argv[0]);
|
||||
int index = atoi(argv[1]);
|
||||
double r = atoi(argv[2]) / 255.0;
|
||||
double g = atoi(argv[3]) / 255.0;
|
||||
double b = atoi(argv[4]) / 255.0;
|
||||
double a = atoi(argv[5]) / 255.0;
|
||||
|
||||
/* Pull buffer of requested layer/buffer */
|
||||
guacenc_buffer* buffer = guacenc_display_get_related_buffer(display, index);
|
||||
if (buffer == NULL)
|
||||
return 1;
|
||||
|
||||
/* Fill with RGBA color */
|
||||
if (buffer->cairo != NULL) {
|
||||
cairo_set_operator(buffer->cairo, guacenc_display_cairo_operator(mask));
|
||||
cairo_set_source_rgba(buffer->cairo, r, g, b, a);
|
||||
cairo_fill(buffer->cairo);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
106
src/guacenc/instruction-copy.c
Normal file
106
src/guacenc/instruction-copy.c
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "display.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <guacamole/client.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
int guacenc_handle_copy(guacenc_display* display, int argc, char** argv) {
|
||||
|
||||
/* Verify argument count */
|
||||
if (argc < 9) {
|
||||
guacenc_log(GUAC_LOG_WARNING, "\"copy\" instruction incomplete");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Parse arguments */
|
||||
int sindex = atoi(argv[0]);
|
||||
int sx = atoi(argv[1]);
|
||||
int sy = atoi(argv[2]);
|
||||
int width = atoi(argv[3]);
|
||||
int height = atoi(argv[4]);
|
||||
int mask = atoi(argv[5]);
|
||||
int dindex = atoi(argv[6]);
|
||||
int dx = atoi(argv[7]);
|
||||
int dy = atoi(argv[8]);
|
||||
|
||||
/* Pull buffer of source layer/buffer */
|
||||
guacenc_buffer* src = guacenc_display_get_related_buffer(display, sindex);
|
||||
if (src == NULL)
|
||||
return 1;
|
||||
|
||||
/* Pull buffer of destination layer/buffer */
|
||||
guacenc_buffer* dst = guacenc_display_get_related_buffer(display, dindex);
|
||||
if (dst == NULL)
|
||||
return 1;
|
||||
|
||||
/* Expand the destination buffer as necessary to fit the draw operation */
|
||||
if (dst->autosize)
|
||||
guacenc_buffer_fit(dst, dx + width, dy + height);
|
||||
|
||||
/* Copy rectangle from source to destination */
|
||||
if (src->surface != NULL && dst->cairo != NULL) {
|
||||
|
||||
/* If surfaces are different, no need to copy */
|
||||
cairo_surface_t* surface;
|
||||
if (src != dst)
|
||||
surface = src->surface;
|
||||
|
||||
/* Otherwise, copy to a temporary surface */
|
||||
else {
|
||||
|
||||
/* Create new surface to hold the source rect */
|
||||
surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
|
||||
width, height);
|
||||
|
||||
/* Copy relevant rectangle from source surface */
|
||||
cairo_t* cairo = cairo_create(surface);
|
||||
cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_set_source_surface(cairo, src->surface, -sx, -sy);
|
||||
cairo_paint(cairo);
|
||||
cairo_destroy(cairo);
|
||||
|
||||
/* Source coordinates are now (0, 0) */
|
||||
sx = sy = 0;
|
||||
|
||||
}
|
||||
|
||||
/* Perform copy */
|
||||
cairo_set_operator(dst->cairo, guacenc_display_cairo_operator(mask));
|
||||
cairo_set_source_surface(dst->cairo, surface, dx - sx, dy - sy);
|
||||
cairo_rectangle(dst->cairo, dx, dy, width, height);
|
||||
cairo_fill(dst->cairo);
|
||||
|
||||
/* Destroy temporary surface if it was created */
|
||||
if (surface != src->surface)
|
||||
cairo_surface_destroy(surface);
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
56
src/guacenc/instruction-cursor.c
Normal file
56
src/guacenc/instruction-cursor.c
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "display.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <guacamole/client.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
int guacenc_handle_cursor(guacenc_display* display, int argc, char** argv) {
|
||||
|
||||
/* Verify argument count */
|
||||
if (argc < 7) {
|
||||
guacenc_log(GUAC_LOG_WARNING, "\"cursor\" instruction incomplete");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Parse arguments */
|
||||
int hotspot_x = atoi(argv[0]);
|
||||
int hotspot_y = atoi(argv[1]);
|
||||
int src_index = atoi(argv[2]);
|
||||
int src_x = atoi(argv[3]);
|
||||
int src_y = atoi(argv[4]);
|
||||
int src_w = atoi(argv[5]);
|
||||
int src_h = atoi(argv[6]);
|
||||
|
||||
/* Nothing to do with cursor (yet) */
|
||||
guacenc_log(GUAC_LOG_DEBUG, "Ignoring cursor: hotspot (%i, %i) "
|
||||
"src_layer=%i (%i, %i) %ix%i", hotspot_x, hotspot_y,
|
||||
src_index, src_x, src_y, src_w, src_h);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
50
src/guacenc/instruction-dispose.c
Normal file
50
src/guacenc/instruction-dispose.c
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "display.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <guacamole/client.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
int guacenc_handle_dispose(guacenc_display* display, int argc, char** argv) {
|
||||
|
||||
/* Verify argument count */
|
||||
if (argc < 1) {
|
||||
guacenc_log(GUAC_LOG_WARNING, "\"dispose\" instruction incomplete");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Parse arguments */
|
||||
int index = atoi(argv[0]);
|
||||
|
||||
/* If non-negative, dispose of layer */
|
||||
if (index >= 0)
|
||||
return guacenc_display_free_layer(display, index);
|
||||
|
||||
/* Otherwise, we're referring to a buffer */
|
||||
return guacenc_display_free_buffer(display, index);
|
||||
|
||||
}
|
||||
|
59
src/guacenc/instruction-end.c
Normal file
59
src/guacenc/instruction-end.c
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "display.h"
|
||||
#include "image-stream.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <guacamole/client.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
int guacenc_handle_end(guacenc_display* display, int argc, char** argv) {
|
||||
|
||||
/* Verify argument count */
|
||||
if (argc < 1) {
|
||||
guacenc_log(GUAC_LOG_WARNING, "\"end\" instruction incomplete");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Parse arguments */
|
||||
int index = atoi(argv[0]);
|
||||
|
||||
/* Retrieve image stream */
|
||||
guacenc_image_stream* stream =
|
||||
guacenc_display_get_image_stream(display, index);
|
||||
if (stream == NULL)
|
||||
return 1;
|
||||
|
||||
/* Retrieve destination buffer */
|
||||
guacenc_buffer* buffer =
|
||||
guacenc_display_get_related_buffer(display, stream->index);
|
||||
if (buffer == NULL)
|
||||
return 1;
|
||||
|
||||
/* End image stream, drawing final image to the buffer */
|
||||
return guacenc_image_stream_end(stream, buffer);
|
||||
|
||||
}
|
||||
|
52
src/guacenc/instruction-img.c
Normal file
52
src/guacenc/instruction-img.c
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "display.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <guacamole/client.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
int guacenc_handle_img(guacenc_display* display, int argc, char** argv) {
|
||||
|
||||
/* Verify argument count */
|
||||
if (argc < 6) {
|
||||
guacenc_log(GUAC_LOG_WARNING, "\"img\" instruction incomplete");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Parse arguments */
|
||||
int stream_index = atoi(argv[0]);
|
||||
int mask = atoi(argv[1]);
|
||||
int layer_index = atoi(argv[2]);
|
||||
char* mimetype = argv[3];
|
||||
int x = atoi(argv[4]);
|
||||
int y = atoi(argv[5]);
|
||||
|
||||
/* Create requested stream */
|
||||
return guacenc_display_create_image_stream(display, stream_index,
|
||||
mask, layer_index, mimetype, x, y);
|
||||
|
||||
}
|
||||
|
64
src/guacenc/instruction-move.c
Normal file
64
src/guacenc/instruction-move.c
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "display.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <guacamole/client.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
int guacenc_handle_move(guacenc_display* display, int argc, char** argv) {
|
||||
|
||||
/* Verify argument count */
|
||||
if (argc < 5) {
|
||||
guacenc_log(GUAC_LOG_WARNING, "\"move\" instruction incomplete");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Parse arguments */
|
||||
int layer_index = atoi(argv[0]);
|
||||
int parent_index = atoi(argv[1]);
|
||||
int x = atoi(argv[2]);
|
||||
int y = atoi(argv[3]);
|
||||
int z = atoi(argv[4]);
|
||||
|
||||
/* Retrieve requested layer */
|
||||
guacenc_layer* layer = guacenc_display_get_layer(display, layer_index);
|
||||
if (layer == NULL)
|
||||
return 1;
|
||||
|
||||
/* Validate parent layer */
|
||||
if (guacenc_display_get_layer(display, parent_index) == NULL)
|
||||
return 1;
|
||||
|
||||
/* Update layer properties */
|
||||
layer->parent_index = parent_index;
|
||||
layer->x = x;
|
||||
layer->y = y;
|
||||
layer->z = z;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
64
src/guacenc/instruction-rect.c
Normal file
64
src/guacenc/instruction-rect.c
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "buffer.h"
|
||||
#include "display.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <cairo/cairo.h>
|
||||
#include <guacamole/client.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
int guacenc_handle_rect(guacenc_display* display, int argc, char** argv) {
|
||||
|
||||
/* Verify argument count */
|
||||
if (argc < 5) {
|
||||
guacenc_log(GUAC_LOG_WARNING, "\"rect\" instruction incomplete");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Parse arguments */
|
||||
int index = atoi(argv[0]);
|
||||
int x = atoi(argv[1]);
|
||||
int y = atoi(argv[2]);
|
||||
int width = atoi(argv[3]);
|
||||
int height = atoi(argv[4]);
|
||||
|
||||
/* Pull buffer of requested layer/buffer */
|
||||
guacenc_buffer* buffer = guacenc_display_get_related_buffer(display, index);
|
||||
if (buffer == NULL)
|
||||
return 1;
|
||||
|
||||
/* Expand the buffer as necessary to fit the draw operation */
|
||||
if (buffer->autosize)
|
||||
guacenc_buffer_fit(buffer, x + width, y + height);
|
||||
|
||||
/* Set path to rectangle */
|
||||
if (buffer->cairo != NULL)
|
||||
cairo_rectangle(buffer->cairo, x, y, width, height);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
54
src/guacenc/instruction-shade.c
Normal file
54
src/guacenc/instruction-shade.c
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "display.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <guacamole/client.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
int guacenc_handle_shade(guacenc_display* display, int argc, char** argv) {
|
||||
|
||||
/* Verify argument count */
|
||||
if (argc < 2) {
|
||||
guacenc_log(GUAC_LOG_WARNING, "\"shade\" instruction incomplete");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Parse arguments */
|
||||
int index = atoi(argv[0]);
|
||||
int opacity = atoi(argv[1]);
|
||||
|
||||
/* Retrieve requested layer */
|
||||
guacenc_layer* layer = guacenc_display_get_layer(display, index);
|
||||
if (layer == NULL)
|
||||
return 1;
|
||||
|
||||
/* Update layer properties */
|
||||
layer->opacity = opacity;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
53
src/guacenc/instruction-size.c
Normal file
53
src/guacenc/instruction-size.c
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "display.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <guacamole/client.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
int guacenc_handle_size(guacenc_display* display, int argc, char** argv) {
|
||||
|
||||
/* Verify argument count */
|
||||
if (argc < 3) {
|
||||
guacenc_log(GUAC_LOG_WARNING, "\"size\" instruction incomplete");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Parse arguments */
|
||||
int index = atoi(argv[0]);
|
||||
int width = atoi(argv[1]);
|
||||
int height = atoi(argv[2]);
|
||||
|
||||
/* Retrieve requested layer */
|
||||
guacenc_layer* layer = guacenc_display_get_layer(display, index);
|
||||
if (layer == NULL)
|
||||
return 1;
|
||||
|
||||
/* Resize layer */
|
||||
return guacenc_buffer_resize(layer->buffer, width, height);
|
||||
|
||||
}
|
||||
|
82
src/guacenc/instruction-sync.c
Normal file
82
src/guacenc/instruction-sync.c
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "display.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/timestamp.h>
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/**
|
||||
* Parses a guac_timestamp from the given string. The string is assumed to
|
||||
* consist solely of decimal digits with an optional leading minus sign. If the
|
||||
* given string contains other characters, the behavior of this function is
|
||||
* undefined.
|
||||
*
|
||||
* @param str
|
||||
* The string to parse, which must contain only decimal digits and an
|
||||
* optional leading minus sign.
|
||||
*
|
||||
* @return
|
||||
* A guac_timestamp having the same value as the provided string.
|
||||
*/
|
||||
static guac_timestamp guacenc_parse_timestamp(const char* str) {
|
||||
|
||||
int sign = 1;
|
||||
int64_t num = 0;
|
||||
|
||||
for (; *str != '\0'; str++) {
|
||||
|
||||
/* Flip sign for each '-' encountered */
|
||||
if (*str == '-')
|
||||
sign = -sign;
|
||||
|
||||
/* If not '-', assume the character is a digit */
|
||||
else
|
||||
num = num * 10 + (*str - '0');
|
||||
|
||||
}
|
||||
|
||||
return (guac_timestamp) (num * sign);
|
||||
|
||||
}
|
||||
|
||||
int guacenc_handle_sync(guacenc_display* display, int argc, char** argv) {
|
||||
|
||||
/* Verify argument count */
|
||||
if (argc < 1) {
|
||||
guacenc_log(GUAC_LOG_WARNING, "\"sync\" instruction incomplete");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Parse arguments */
|
||||
guac_timestamp timestamp = guacenc_parse_timestamp(argv[0]);
|
||||
|
||||
/* Update timestamp / flush frame */
|
||||
return guacenc_display_sync(display, timestamp);
|
||||
|
||||
}
|
||||
|
58
src/guacenc/instruction-transfer.c
Normal file
58
src/guacenc/instruction-transfer.c
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "display.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <guacamole/client.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
int guacenc_handle_transfer(guacenc_display* display, int argc, char** argv) {
|
||||
|
||||
/* Verify argument count */
|
||||
if (argc < 9) {
|
||||
guacenc_log(GUAC_LOG_WARNING, "\"transform\" instruction incomplete");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Parse arguments */
|
||||
int src_index = atoi(argv[0]);
|
||||
int src_x = atoi(argv[1]);
|
||||
int src_y = atoi(argv[2]);
|
||||
int src_w = atoi(argv[3]);
|
||||
int src_h = atoi(argv[4]);
|
||||
int function = atoi(argv[5]);
|
||||
int dst_index = atoi(argv[6]);
|
||||
int dst_x = atoi(argv[7]);
|
||||
int dst_y = atoi(argv[8]);
|
||||
|
||||
/* TODO: Unimplemented for now (rarely used) */
|
||||
guacenc_log(GUAC_LOG_DEBUG, "transform: src_layer=%i (%i, %i) %ix%i "
|
||||
"function=0x%X dst_layer=%i (%i, %i)", src_index, src_x, src_y,
|
||||
src_w, src_h, function, dst_index, dst_x, dst_y);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
79
src/guacenc/instructions.c
Normal file
79
src/guacenc/instructions.c
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "display.h"
|
||||
#include "instructions.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <guacamole/client.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
guacenc_instruction_handler_mapping guacenc_instruction_handler_map[] = {
|
||||
{"blob", guacenc_handle_blob},
|
||||
{"img", guacenc_handle_img},
|
||||
{"end", guacenc_handle_end},
|
||||
{"sync", guacenc_handle_sync},
|
||||
{"cursor", guacenc_handle_cursor},
|
||||
{"copy", guacenc_handle_copy},
|
||||
{"transfer", guacenc_handle_transfer},
|
||||
{"size", guacenc_handle_size},
|
||||
{"rect", guacenc_handle_rect},
|
||||
{"cfill", guacenc_handle_cfill},
|
||||
{"move", guacenc_handle_move},
|
||||
{"shade", guacenc_handle_shade},
|
||||
{"dispose", guacenc_handle_dispose},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
int guacenc_handle_instruction(guacenc_display* display, const char* opcode,
|
||||
int argc, char** argv) {
|
||||
|
||||
/* Search through mapping for instruction handler having given opcode */
|
||||
guacenc_instruction_handler_mapping* current = guacenc_instruction_handler_map;
|
||||
while (current->opcode != NULL) {
|
||||
|
||||
/* Invoke handler if opcode matches (if defined) */
|
||||
if (strcmp(current->opcode, opcode) == 0) {
|
||||
|
||||
/* Invoke defined handler */
|
||||
guacenc_instruction_handler* handler = current->handler;
|
||||
if (handler != NULL)
|
||||
return handler(display, argc, argv);
|
||||
|
||||
/* Log defined but unimplemented instructions */
|
||||
guacenc_log(GUAC_LOG_DEBUG, "\"%s\" not implemented", opcode);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/* Next candidate handler */
|
||||
current++;
|
||||
|
||||
} /* end opcode search */
|
||||
|
||||
/* Ignore any unknown instructions */
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
171
src/guacenc/instructions.h
Normal file
171
src/guacenc/instructions.h
Normal file
@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef GUACENC_INSTRUCTIONS_H
|
||||
#define GUACENC_INSTRUCTIONS_H
|
||||
|
||||
#include "config.h"
|
||||
#include "display.h"
|
||||
|
||||
/**
|
||||
* A callback function which, when invoked, handles a particular Guacamole
|
||||
* instruction. The opcode of the instruction is implied (as it is expected
|
||||
* that there will be a 1:1 mapping of opcode to callback function), while the
|
||||
* arguments for that instruction are included in the parameters given to the
|
||||
* callback.
|
||||
*
|
||||
* @param display
|
||||
* The current internal display of the Guacamole video encoder.
|
||||
*
|
||||
* @param argc
|
||||
* The number of arguments (excluding opcode) passed to the instruction
|
||||
* being handled by the callback.
|
||||
*
|
||||
* @param argv
|
||||
* All arguments (excluding opcode) associated with the instruction being
|
||||
* handled by the callback.
|
||||
*
|
||||
* @return
|
||||
* Zero if the instruction was handled successfully, non-zero if an error
|
||||
* occurs.
|
||||
*/
|
||||
typedef int guacenc_instruction_handler(guacenc_display* display,
|
||||
int argc, char** argv);
|
||||
|
||||
/**
|
||||
* Mapping of instruction opcode to corresponding handler function.
|
||||
*/
|
||||
typedef struct guacenc_instruction_handler_mapping {
|
||||
|
||||
/**
|
||||
* The opcode of the instruction that the associated handler function
|
||||
* should be invoked for.
|
||||
*/
|
||||
const char* opcode;
|
||||
|
||||
/**
|
||||
* The handler function to invoke whenever an instruction having the
|
||||
* associated opcode is parsed.
|
||||
*/
|
||||
guacenc_instruction_handler* handler;
|
||||
|
||||
} guacenc_instruction_handler_mapping;
|
||||
|
||||
/**
|
||||
* Array of all opcode/handler mappings for all supported opcodes, terminated
|
||||
* by an entry with a NULL opcode. All opcodes not listed here can be safely
|
||||
* ignored.
|
||||
*/
|
||||
extern guacenc_instruction_handler_mapping guacenc_instruction_handler_map[];
|
||||
|
||||
/**
|
||||
* Handles the instruction having the given opcode and arguments, encoding the
|
||||
* result to the in-progress video.
|
||||
*
|
||||
* @param display
|
||||
* The current internal display of the Guacamole video encoder.
|
||||
*
|
||||
* @param opcode
|
||||
* The opcode of the instruction being handled.
|
||||
*
|
||||
* @param argc
|
||||
* The number of arguments (excluding opcode) passed to the instruction
|
||||
* being handled by the callback.
|
||||
*
|
||||
* @param argv
|
||||
* All arguments (excluding opcode) associated with the instruction being
|
||||
* handled by the callback.
|
||||
*
|
||||
* @return
|
||||
* Zero if the instruction was handled successfully, non-zero if an error
|
||||
* occurs.
|
||||
*/
|
||||
int guacenc_handle_instruction(guacenc_display* display,
|
||||
const char* opcode, int argc, char** argv);
|
||||
|
||||
/**
|
||||
* Handler for the Guacamole "blob" instruction.
|
||||
*/
|
||||
guacenc_instruction_handler guacenc_handle_blob;
|
||||
|
||||
/**
|
||||
* Handler for the Guacamole "img" instruction.
|
||||
*/
|
||||
guacenc_instruction_handler guacenc_handle_img;
|
||||
|
||||
/**
|
||||
* Handler for the Guacamole "end" instruction.
|
||||
*/
|
||||
guacenc_instruction_handler guacenc_handle_end;
|
||||
|
||||
/**
|
||||
* Handler for the Guacamole "sync" instruction.
|
||||
*/
|
||||
guacenc_instruction_handler guacenc_handle_sync;
|
||||
|
||||
/**
|
||||
* Handler for the Guacamole "cursor" instruction.
|
||||
*/
|
||||
guacenc_instruction_handler guacenc_handle_cursor;
|
||||
|
||||
/**
|
||||
* Handler for the Guacamole "copy" instruction.
|
||||
*/
|
||||
guacenc_instruction_handler guacenc_handle_copy;
|
||||
|
||||
/**
|
||||
* Handler for the Guacamole "transfer" instruction.
|
||||
*/
|
||||
guacenc_instruction_handler guacenc_handle_transfer;
|
||||
|
||||
/**
|
||||
* Handler for the Guacamole "size" instruction.
|
||||
*/
|
||||
guacenc_instruction_handler guacenc_handle_size;
|
||||
|
||||
/**
|
||||
* Handler for the Guacamole "rect" instruction.
|
||||
*/
|
||||
guacenc_instruction_handler guacenc_handle_rect;
|
||||
|
||||
/**
|
||||
* Handler for the Guacamole "cfill" instruction.
|
||||
*/
|
||||
guacenc_instruction_handler guacenc_handle_cfill;
|
||||
|
||||
/**
|
||||
* Handler for the Guacamole "move" instruction.
|
||||
*/
|
||||
guacenc_instruction_handler guacenc_handle_move;
|
||||
|
||||
/**
|
||||
* Handler for the Guacamole "shade" instruction.
|
||||
*/
|
||||
guacenc_instruction_handler guacenc_handle_shade;
|
||||
|
||||
/**
|
||||
* Handler for the Guacamole "dispose" instruction.
|
||||
*/
|
||||
guacenc_instruction_handler guacenc_handle_dispose;
|
||||
|
||||
#endif
|
||||
|
87
src/guacenc/jpeg.c
Normal file
87
src/guacenc/jpeg.c
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "jpeg.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cairo/cairo.h>
|
||||
#include <jpeglib.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
cairo_surface_t* guacenc_jpeg_decoder(unsigned char* data, int length) {
|
||||
|
||||
struct jpeg_decompress_struct cinfo;
|
||||
struct jpeg_error_mgr jerr;
|
||||
|
||||
/* Create decompressor with standard error handling */
|
||||
jpeg_create_decompress(&cinfo);
|
||||
cinfo.err = jpeg_std_error(&jerr);
|
||||
|
||||
/* Read JPEG directly from memory buffer */
|
||||
jpeg_mem_src(&cinfo, data, length);
|
||||
|
||||
/* Read and validate JPEG header */
|
||||
if (!jpeg_read_header(&cinfo, TRUE)) {
|
||||
guacenc_log(GUAC_LOG_WARNING, "Invalid JPEG data");
|
||||
jpeg_destroy_decompress(&cinfo);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Begin decompression */
|
||||
cinfo.out_color_space = JCS_EXT_BGRX;
|
||||
jpeg_start_decompress(&cinfo);
|
||||
|
||||
/* Pull JPEG dimensions from decompressor */
|
||||
int width = cinfo.output_width;
|
||||
int height = cinfo.output_height;
|
||||
|
||||
/* Create blank Cairo surface (no transparency in JPEG) */
|
||||
cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24,
|
||||
width, height);
|
||||
|
||||
/* Pull underlying buffer and its stride */
|
||||
int stride = cairo_image_surface_get_stride(surface);
|
||||
unsigned char* row = cairo_image_surface_get_data(surface);
|
||||
|
||||
/* Read JPEG into surface */
|
||||
while (cinfo.output_scanline < height) {
|
||||
unsigned char* buffers[1] = { row };
|
||||
jpeg_read_scanlines(&cinfo, buffers, 1);
|
||||
row += stride;
|
||||
}
|
||||
|
||||
/* End decompression */
|
||||
jpeg_finish_decompress(&cinfo);
|
||||
|
||||
/* Free decompressor */
|
||||
jpeg_destroy_decompress(&cinfo);
|
||||
|
||||
/* JPEG was read successfully */
|
||||
return surface;
|
||||
|
||||
}
|
||||
|
35
src/guacenc/jpeg.h
Normal file
35
src/guacenc/jpeg.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef GUACENC_JPEG_H
|
||||
#define GUACENC_JPEG_H
|
||||
|
||||
#include "config.h"
|
||||
#include "image-stream.h"
|
||||
|
||||
/**
|
||||
* Decoder implementation which handles "image/jpeg" images.
|
||||
*/
|
||||
guacenc_decoder guacenc_jpeg_decoder;
|
||||
|
||||
#endif
|
||||
|
76
src/guacenc/layer.c
Normal file
76
src/guacenc/layer.c
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "buffer.h"
|
||||
#include "layer.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
guacenc_layer* guacenc_layer_alloc() {
|
||||
|
||||
/* Allocate new layer */
|
||||
guacenc_layer* layer = (guacenc_layer*) calloc(1, sizeof(guacenc_layer));
|
||||
if (layer == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Allocate associated buffer (width, height, and image storage) */
|
||||
layer->buffer = guacenc_buffer_alloc();
|
||||
if (layer->buffer == NULL) {
|
||||
free(layer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Allocate buffer for frame rendering */
|
||||
layer->frame = guacenc_buffer_alloc();
|
||||
if (layer->frame== NULL) {
|
||||
guacenc_buffer_free(layer->buffer);
|
||||
free(layer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Layers default to fully opaque */
|
||||
layer->opacity = 0xFF;
|
||||
|
||||
/* Default parented to default layer */
|
||||
layer->parent_index = 0;
|
||||
|
||||
return layer;
|
||||
|
||||
}
|
||||
|
||||
void guacenc_layer_free(guacenc_layer* layer) {
|
||||
|
||||
/* Ignore NULL layers */
|
||||
if (layer == NULL)
|
||||
return;
|
||||
|
||||
/* Free internal frame buffer */
|
||||
guacenc_buffer_free(layer->frame);
|
||||
|
||||
/* Free underlying buffer */
|
||||
guacenc_buffer_free(layer->buffer);
|
||||
|
||||
free(layer);
|
||||
|
||||
}
|
||||
|
106
src/guacenc/layer.h
Normal file
106
src/guacenc/layer.h
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef GUACENC_LAYER_H
|
||||
#define GUACENC_LAYER_H
|
||||
|
||||
#include "config.h"
|
||||
#include "buffer.h"
|
||||
|
||||
/**
|
||||
* The value assigned to the parent_index property of a guacenc_layer if it has
|
||||
* no parent.
|
||||
*/
|
||||
#define GUACENC_LAYER_NO_PARENT -1
|
||||
|
||||
/**
|
||||
* A visible Guacamole layer.
|
||||
*/
|
||||
typedef struct guacenc_layer {
|
||||
|
||||
/**
|
||||
* The actual image contents of this layer, as well as this layer's size
|
||||
* (width and height).
|
||||
*/
|
||||
guacenc_buffer* buffer;
|
||||
|
||||
/**
|
||||
* The index of the layer that contains this layer. If this layer is the
|
||||
* default layer (and thus has no parent), this will be
|
||||
* GUACENC_LAYER_NO_PARENT.
|
||||
*/
|
||||
int parent_index;
|
||||
|
||||
/**
|
||||
* The X coordinate of the upper-left corner of this layer within the
|
||||
* Guacamole display.
|
||||
*/
|
||||
int x;
|
||||
|
||||
/**
|
||||
* The Y coordinate of the upper-left corner of this layer within the
|
||||
* Guacamole display.
|
||||
*/
|
||||
int y;
|
||||
|
||||
/**
|
||||
* The relative stacking order of this layer with respect to other sibling
|
||||
* layers.
|
||||
*/
|
||||
int z;
|
||||
|
||||
/**
|
||||
* The opacity of this layer, where 0 is completely transparent and 255 is
|
||||
* completely opaque.
|
||||
*/
|
||||
int opacity;
|
||||
|
||||
/**
|
||||
* The internal buffer used by to record the state of this layer in the
|
||||
* previous frame and to render additional frames.
|
||||
*/
|
||||
guacenc_buffer* frame;
|
||||
|
||||
} guacenc_layer;
|
||||
|
||||
/**
|
||||
* Allocates and initializes a new layer object. This allocation is independent
|
||||
* of the Guacamole video encoder display; the allocated guacenc_layer will not
|
||||
* automatically be associated with the active display.
|
||||
*
|
||||
* @return
|
||||
* A newly-allocated and initialized guacenc_layer, or NULL if allocation
|
||||
* fails.
|
||||
*/
|
||||
guacenc_layer* guacenc_layer_alloc();
|
||||
|
||||
/**
|
||||
* Frees all memory associated with the given layer object. If the layer
|
||||
* provided is NULL, this function has no effect.
|
||||
*
|
||||
* @param layer
|
||||
* The layer to free, which may be NULL.
|
||||
*/
|
||||
void guacenc_layer_free(guacenc_layer* layer);
|
||||
|
||||
#endif
|
||||
|
88
src/guacenc/log.c
Normal file
88
src/guacenc/log.c
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "guacenc.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/error.h>
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int guacenc_log_level = GUACENC_DEFAULT_LOG_LEVEL;
|
||||
|
||||
void vguacenc_log(guac_client_log_level level, const char* format,
|
||||
va_list args) {
|
||||
|
||||
const char* priority_name;
|
||||
char message[2048];
|
||||
|
||||
/* Don't bother if the log level is too high */
|
||||
if (level > guacenc_log_level)
|
||||
return;
|
||||
|
||||
/* Copy log message into buffer */
|
||||
vsnprintf(message, sizeof(message), format, args);
|
||||
|
||||
/* Convert log level to human-readable name */
|
||||
switch (level) {
|
||||
|
||||
/* Error log level */
|
||||
case GUAC_LOG_ERROR:
|
||||
priority_name = "ERROR";
|
||||
break;
|
||||
|
||||
/* Warning log level */
|
||||
case GUAC_LOG_WARNING:
|
||||
priority_name = "WARNING";
|
||||
break;
|
||||
|
||||
/* Informational log level */
|
||||
case GUAC_LOG_INFO:
|
||||
priority_name = "INFO";
|
||||
break;
|
||||
|
||||
/* Debug log level */
|
||||
case GUAC_LOG_DEBUG:
|
||||
priority_name = "DEBUG";
|
||||
break;
|
||||
|
||||
/* Any unknown/undefined log level */
|
||||
default:
|
||||
priority_name = "UNKNOWN";
|
||||
break;
|
||||
}
|
||||
|
||||
/* Log to STDERR */
|
||||
fprintf(stderr, GUACENC_LOG_NAME ": %s: %s\n", priority_name, message);
|
||||
|
||||
}
|
||||
|
||||
void guacenc_log(guac_client_log_level level, const char* format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vguacenc_log(level, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
76
src/guacenc/log.h
Normal file
76
src/guacenc/log.h
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef GUACENC_LOG_H
|
||||
#define GUACENC_LOG_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <guacamole/client.h>
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
/**
|
||||
* The maximum level at which to log messages. All other messages will be
|
||||
* dropped.
|
||||
*/
|
||||
extern int guacenc_log_level;
|
||||
|
||||
/**
|
||||
* The string to prepend to all log messages.
|
||||
*/
|
||||
#define GUACENC_LOG_NAME "guacenc"
|
||||
|
||||
/**
|
||||
* Writes a message to guacenc's logs. This function takes a format and
|
||||
* va_list, similar to vprintf.
|
||||
*
|
||||
* @param level
|
||||
* The level at which to log this message.
|
||||
*
|
||||
* @param format
|
||||
* A printf-style format string to log.
|
||||
*
|
||||
* @param args
|
||||
* The va_list containing the arguments to be used when filling the format
|
||||
* string for printing.
|
||||
*/
|
||||
void vguacenc_log(guac_client_log_level level, const char* format,
|
||||
va_list args);
|
||||
|
||||
/**
|
||||
* Writes a message to guacenc's logs. This function accepts parameters
|
||||
* identically to printf.
|
||||
*
|
||||
* @param level
|
||||
* The level at which to log this message.
|
||||
*
|
||||
* @param format
|
||||
* A printf-style format string to log.
|
||||
*
|
||||
* @param ...
|
||||
* Arguments to use when filling the format string for printing.
|
||||
*/
|
||||
void guacenc_log(guac_client_log_level level, const char* format, ...);
|
||||
|
||||
#endif
|
||||
|
62
src/guacenc/man/guacenc.1
Normal file
62
src/guacenc/man/guacenc.1
Normal file
@ -0,0 +1,62 @@
|
||||
.TH guacenc 1 "12 Mar 2016" "version 0.9.9" "Guacamole"
|
||||
.
|
||||
.SH NAME
|
||||
guacenc \- Guacamole video encoder
|
||||
.
|
||||
.SH SYNOPSIS
|
||||
.B guacenc
|
||||
[\fB-s\fR \fIWIDTH\fRx\fIHEIGHT\fR]
|
||||
[\fB-r\fR \fIBITRATE\fR]
|
||||
[\fB-f\fR]
|
||||
[\fIFILE\fR]...
|
||||
.
|
||||
.SH DESCRIPTION
|
||||
.B guacenc
|
||||
is a video encoder which accepts Guacamole protocol dumps, such as those saved
|
||||
when screen recording is enabled on a Guacamole connection, writing standard
|
||||
video files as output.
|
||||
.B guacenc
|
||||
is essentially an implementation of a Guacamole client which accepts
|
||||
its input from files instead of a network connection, and renders directly to
|
||||
video instead of to the user's screen.
|
||||
.P
|
||||
Each \fIFILE\fR specified will be encoded as a raw MPEG-4 video stream to a new
|
||||
file named \fIFILE\fR.m4v, encoded according to the other options specified. By
|
||||
default, the output video will be \fI640\fRx\fI480\fR pixels, and will be saved
|
||||
with a bitrate of \fI2000000\fR bits per second (2 Mbps). These defaults can be
|
||||
overridden with the \fB-s\fR and \fB-r\fR options respectively. Existing files
|
||||
will not be overwritten; the encoding process for any input file will be
|
||||
aborted if it would result in overwriting an existing file.
|
||||
.P
|
||||
Guacamole acquires a write lock on recordings as they are being written. By
|
||||
default,
|
||||
.B guacenc
|
||||
will check whether the each input file is locked and will refuse to read and
|
||||
encode an input file if it appears to be an in-progress recording. This
|
||||
behavior can be overridden by specifying the \fB-f\fR option. Encoding an
|
||||
in-progress recording will still result in a valid video; the video will simply
|
||||
cover the user's session only up to the current point in time.
|
||||
.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB-s\fR \fIWIDTH\fRx\fIHEIGHT\fR
|
||||
Changes the resolution of the video that
|
||||
.B guacenc
|
||||
renders. By default, this will be \fI640\fRx\fI480\fR.
|
||||
.TP
|
||||
\fB-r\fR \fIBITRATE\fR
|
||||
Changes the bitrate that
|
||||
.B guacenc
|
||||
will use for the saved video. This is specified in bits per second. By default,
|
||||
this will be \fI2000000\fR (2 Mbps). Higher values will result in larger but
|
||||
higher-quality video files. Lower values will result in smaller but
|
||||
lower-quality video files.
|
||||
.TP
|
||||
\fB-f\fR
|
||||
Overrides the default behavior of
|
||||
.B guacenc
|
||||
such that input files will be encoded even if they appear to be recordings of
|
||||
in-progress Guacamole sessions.
|
||||
.
|
||||
.SH AUTHOR
|
||||
Written by Michael Jumper <mike.jumper@guac-dev.org>
|
72
src/guacenc/parse.c
Normal file
72
src/guacenc/parse.c
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int guacenc_parse_int(char* arg, int* i) {
|
||||
|
||||
char* end;
|
||||
|
||||
/* Parse string as an integer */
|
||||
errno = 0;
|
||||
long int value = strtol(arg, &end, 10);
|
||||
|
||||
/* Ignore number if invalid / non-positive */
|
||||
if (errno != 0 || value <= 0 || value > INT_MAX || *end != '\0')
|
||||
return 1;
|
||||
|
||||
/* Store value */
|
||||
*i = value;
|
||||
|
||||
/* Parsing successful */
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int guacenc_parse_dimensions(char* arg, int* width, int* height) {
|
||||
|
||||
/* Locate the 'x' within the dimensions string */
|
||||
char* x = strchr(arg, 'x');
|
||||
if (x == NULL)
|
||||
return 1;
|
||||
|
||||
/* Replace 'x' with a null terminator */
|
||||
*x = '\0';
|
||||
|
||||
/* Parse width and height */
|
||||
int w, h;
|
||||
if (guacenc_parse_int(arg, &w) || guacenc_parse_int(x+1, &h))
|
||||
return 1;
|
||||
|
||||
/* Width and height are both valid */
|
||||
*width = w;
|
||||
*height = h;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
71
src/guacenc/parse.h
Normal file
71
src/guacenc/parse.h
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef GUACENC_PARSE_H
|
||||
#define GUACENC_PARSE_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
/**
|
||||
* Parses a string into a single integer. Only positive integers are accepted.
|
||||
* The input string may be modified during parsing. A value will be stored in
|
||||
* the provided int pointer only if valid.
|
||||
*
|
||||
* @param arg
|
||||
* The string to parse.
|
||||
*
|
||||
* @param i
|
||||
* A pointer to the integer in which the parsed value of the given string
|
||||
* should be stored.
|
||||
*
|
||||
* @return
|
||||
* Zero if parsing was successful, non-zero if the provided string was
|
||||
* invalid.
|
||||
*/
|
||||
int guacenc_parse_int(char* arg, int* i);
|
||||
|
||||
/**
|
||||
* Parses a string of the form WIDTHxHEIGHT into individual width and height
|
||||
* integers. The input string may be modified during parsing. Values will be
|
||||
* stored in the provided width and height pointers only if the given
|
||||
* dimensions are valid.
|
||||
*
|
||||
* @param arg
|
||||
* The string to parse.
|
||||
*
|
||||
* @param width
|
||||
* A pointer to the integer in which the parsed width component of the
|
||||
* given string should be stored.
|
||||
*
|
||||
* @param height
|
||||
* A pointer to the integer in which the parsed height component of the
|
||||
* given string should be stored.
|
||||
*
|
||||
* @return
|
||||
* Zero if parsing was successful, non-zero if the provided string was
|
||||
* invalid.
|
||||
*/
|
||||
int guacenc_parse_dimensions(char* arg, int* width, int* height);
|
||||
|
||||
#endif
|
||||
|
||||
|
112
src/guacenc/png.c
Normal file
112
src/guacenc/png.c
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "log.h"
|
||||
#include "png.h"
|
||||
|
||||
#include <cairo/cairo.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* The current state of the PNG decoder.
|
||||
*/
|
||||
typedef struct guacenc_png_read_state {
|
||||
|
||||
/**
|
||||
* The buffer of unread image data. This pointer will be updated to point
|
||||
* to the next unread byte when data is read.
|
||||
*/
|
||||
unsigned char* data;
|
||||
|
||||
/**
|
||||
* The number of bytes remaining to be read within the buffer.
|
||||
*/
|
||||
unsigned int length;
|
||||
|
||||
} guacenc_png_read_state;
|
||||
|
||||
/**
|
||||
* Attempts to fill the given buffer with read image data. The behavior of
|
||||
* this function is dictated by cairo_read_t.
|
||||
*
|
||||
* @param closure
|
||||
* The current state of the PNG decoding process (an instance of
|
||||
* guacenc_png_read_state).
|
||||
*
|
||||
* @param data
|
||||
* The data buffer to fill.
|
||||
*
|
||||
* @param length
|
||||
* The number of bytes to fill within the data buffer.
|
||||
*
|
||||
* @return
|
||||
* CAIRO_STATUS_SUCCESS if all data was read successfully (the entire
|
||||
* buffer was filled), CAIRO_STATUS_READ_ERROR otherwise.
|
||||
*/
|
||||
static cairo_status_t guacenc_png_read(void* closure, unsigned char* data,
|
||||
unsigned int length) {
|
||||
|
||||
guacenc_png_read_state* state = (guacenc_png_read_state*) closure;
|
||||
|
||||
/* If more data is requested than is available in buffer, fail */
|
||||
if (length > state->length)
|
||||
return CAIRO_STATUS_READ_ERROR;
|
||||
|
||||
/* Read chunk into buffer */
|
||||
memcpy(data, state->data, length);
|
||||
|
||||
/* Advance to next chunk */
|
||||
state->length -= length;
|
||||
state->data += length;
|
||||
|
||||
/* Read was successful */
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
|
||||
}
|
||||
|
||||
cairo_surface_t* guacenc_png_decoder(unsigned char* data, int length) {
|
||||
|
||||
guacenc_png_read_state state = {
|
||||
.data = data,
|
||||
.length = length
|
||||
};
|
||||
|
||||
/* Read PNG from data */
|
||||
cairo_surface_t* surface =
|
||||
cairo_image_surface_create_from_png_stream(guacenc_png_read, &state);
|
||||
|
||||
/* If surface returned with an error, just return NULL */
|
||||
if (surface != NULL &&
|
||||
cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
|
||||
guacenc_log(GUAC_LOG_WARNING, "Invalid PNG data");
|
||||
cairo_surface_destroy(surface);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* PNG was read successfully */
|
||||
return surface;
|
||||
|
||||
}
|
||||
|
35
src/guacenc/png.h
Normal file
35
src/guacenc/png.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef GUACENC_PNG_H
|
||||
#define GUACENC_PNG_H
|
||||
|
||||
#include "config.h"
|
||||
#include "image-stream.h"
|
||||
|
||||
/**
|
||||
* Decoder implementation which handles "image/png" images.
|
||||
*/
|
||||
guacenc_decoder guacenc_png_decoder;
|
||||
|
||||
#endif
|
||||
|
489
src/guacenc/video.c
Normal file
489
src/guacenc/video.c
Normal file
@ -0,0 +1,489 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "buffer.h"
|
||||
#include "ffmpeg-compat.h"
|
||||
#include "log.h"
|
||||
#include "video.h"
|
||||
|
||||
#include <cairo/cairo.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavutil/common.h>
|
||||
#include <libavutil/imgutils.h>
|
||||
#include <libswscale/swscale.h>
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/timestamp.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
guacenc_video* guacenc_video_alloc(const char* path, const char* codec_name,
|
||||
int width, int height, int bitrate) {
|
||||
|
||||
/* Pull codec based on name */
|
||||
AVCodec* codec = avcodec_find_encoder_by_name(codec_name);
|
||||
if (codec == NULL) {
|
||||
guacenc_log(GUAC_LOG_ERROR, "Failed to locate codec \"%s\".",
|
||||
codec_name);
|
||||
goto fail_codec;
|
||||
}
|
||||
|
||||
/* Retrieve encoding context */
|
||||
AVCodecContext* context = avcodec_alloc_context3(codec);
|
||||
if (context == NULL) {
|
||||
guacenc_log(GUAC_LOG_ERROR, "Failed to allocate context for "
|
||||
"codec \"%s\".", codec_name);
|
||||
goto fail_context;
|
||||
}
|
||||
|
||||
/* Init context with encoding parameters */
|
||||
context->bit_rate = bitrate;
|
||||
context->width = width;
|
||||
context->height = height;
|
||||
context->time_base = (AVRational) { 1, GUACENC_VIDEO_FRAMERATE };
|
||||
context->gop_size = 10;
|
||||
context->max_b_frames = 1;
|
||||
context->pix_fmt = AV_PIX_FMT_YUV420P;
|
||||
|
||||
/* Open codec for use */
|
||||
if (avcodec_open2(context, codec, NULL) < 0) {
|
||||
guacenc_log(GUAC_LOG_ERROR, "Failed to open codec \"%s\".", codec_name);
|
||||
goto fail_codec_open;
|
||||
}
|
||||
|
||||
/* Allocate corresponding frame */
|
||||
AVFrame* frame = av_frame_alloc();
|
||||
if (frame == NULL) {
|
||||
goto fail_frame;
|
||||
}
|
||||
|
||||
/* Copy necessary data for frame from context */
|
||||
frame->format = context->pix_fmt;
|
||||
frame->width = context->width;
|
||||
frame->height = context->height;
|
||||
|
||||
/* Allocate actual backing data for frame */
|
||||
if (av_image_alloc(frame->data, frame->linesize, frame->width,
|
||||
frame->height, frame->format, 32) < 0) {
|
||||
goto fail_frame_data;
|
||||
}
|
||||
|
||||
/* Open output file */
|
||||
int fd = open(path, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR | S_IWUSR);
|
||||
if (fd == -1) {
|
||||
guacenc_log(GUAC_LOG_ERROR, "Failed to open output file \"%s\": %s",
|
||||
path, strerror(errno));
|
||||
goto fail_output_fd;
|
||||
}
|
||||
|
||||
/* Create stream for output file */
|
||||
FILE* output = fdopen(fd, "wb");
|
||||
if (output == NULL) {
|
||||
guacenc_log(GUAC_LOG_ERROR, "Failed to allocate stream for output "
|
||||
"file \"%s\": %s", path, strerror(errno));
|
||||
goto fail_output_file;
|
||||
}
|
||||
|
||||
/* Allocate video structure */
|
||||
guacenc_video* video = malloc(sizeof(guacenc_video));
|
||||
if (video == NULL) {
|
||||
goto fail_video;
|
||||
}
|
||||
|
||||
/* Init properties of video */
|
||||
video->output = output;
|
||||
video->context = context;
|
||||
video->next_frame = frame;
|
||||
video->width = width;
|
||||
video->height = height;
|
||||
video->bitrate = bitrate;
|
||||
|
||||
/* No frames have been written or prepared yet */
|
||||
video->last_timestamp = 0;
|
||||
video->next_pts = 0;
|
||||
|
||||
return video;
|
||||
|
||||
/* Free all allocated data in case of failure */
|
||||
fail_video:
|
||||
fclose(output);
|
||||
|
||||
fail_output_file:
|
||||
close(fd);
|
||||
|
||||
fail_output_fd:
|
||||
av_freep(&frame->data[0]);
|
||||
|
||||
fail_frame_data:
|
||||
av_frame_free(&frame);
|
||||
|
||||
fail_frame:
|
||||
fail_codec_open:
|
||||
avcodec_free_context(&context);
|
||||
|
||||
fail_context:
|
||||
fail_codec:
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes the specied frame as a new frame of video, updating the internal
|
||||
* video timestamp by one frame's worth of time. The pts member of the given
|
||||
* frame structure will be updated with the current presentation timestamp of
|
||||
* the video. If pending frames of the video are being flushed, the given frame
|
||||
* may be NULL (as required by avcodec_encode_video2()).
|
||||
*
|
||||
* @param video
|
||||
* The video to write the given frame to.
|
||||
*
|
||||
* @param frame
|
||||
* The frame to write to the video, or NULL if previously-written frames
|
||||
* are being flushed.
|
||||
*
|
||||
* @return
|
||||
* A positive value if the frame was successfully written, zero if the
|
||||
* frame has been saved for later writing / reordering, negative if an
|
||||
* error occurs.
|
||||
*/
|
||||
static int guacenc_video_write_frame(guacenc_video* video, AVFrame* frame) {
|
||||
|
||||
/* Init video packet */
|
||||
AVPacket packet;
|
||||
av_init_packet(&packet);
|
||||
|
||||
/* Request that encoder allocate data for packet */
|
||||
packet.data = NULL;
|
||||
packet.size = 0;
|
||||
|
||||
/* Set timestamp of frame, if frame given */
|
||||
if (frame != NULL)
|
||||
frame->pts = video->next_pts;
|
||||
|
||||
/* Write frame to video */
|
||||
int got_data;
|
||||
if (avcodec_encode_video2(video->context, &packet, frame, &got_data) < 0) {
|
||||
guacenc_log(GUAC_LOG_WARNING, "Error encoding frame #%" PRId64,
|
||||
video->next_pts);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Write corresponding data to file */
|
||||
if (got_data) {
|
||||
|
||||
/* Write data, logging any errors */
|
||||
if (fwrite(packet.data, 1, packet.size, video->output) == 0) {
|
||||
guacenc_log(GUAC_LOG_ERROR, "Unable to write frame "
|
||||
"#%" PRId64 ": %s", video->next_pts, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Data was written successfully */
|
||||
guacenc_log(GUAC_LOG_DEBUG, "Frame #%08" PRId64 ": wrote %i bytes",
|
||||
video->next_pts, packet.size);
|
||||
av_packet_unref(&packet);
|
||||
|
||||
}
|
||||
|
||||
/* Frame may have been queued for later writing / reordering */
|
||||
else
|
||||
guacenc_log(GUAC_LOG_DEBUG, "Frame #%08" PRId64 ": queued for later",
|
||||
video->next_pts);
|
||||
|
||||
/* Update presentation timestamp for next frame */
|
||||
video->next_pts++;
|
||||
|
||||
/* Write was successful */
|
||||
return got_data;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes the frame previously specified by guacenc_video_prepare_frame() as a
|
||||
* new frame of video, updating the internal video timestamp by one frame's
|
||||
* worth of time.
|
||||
*
|
||||
* @param video
|
||||
* The video to flush.
|
||||
*
|
||||
* @return
|
||||
* Zero if flushing was successful, non-zero if an error occurs.
|
||||
*/
|
||||
static int guacenc_video_flush_frame(guacenc_video* video) {
|
||||
|
||||
/* Write frame to video */
|
||||
return guacenc_video_write_frame(video, video->next_frame) < 0;
|
||||
|
||||
}
|
||||
|
||||
int guacenc_video_advance_timeline(guacenc_video* video,
|
||||
guac_timestamp timestamp) {
|
||||
|
||||
/* Flush frames as necessary if previously updated */
|
||||
if (video->last_timestamp != 0) {
|
||||
|
||||
/* Calculate the number of frames that should have been written */
|
||||
int elapsed = (timestamp - video->last_timestamp)
|
||||
* GUACENC_VIDEO_FRAMERATE / 1000;
|
||||
|
||||
/* Keep previous timestamp if insufficient time has elapsed */
|
||||
if (elapsed == 0)
|
||||
return 0;
|
||||
|
||||
/* Flush frames to bring timeline in sync, duplicating if necessary */
|
||||
do {
|
||||
guacenc_video_flush_frame(video);
|
||||
} while (--elapsed != 0);
|
||||
|
||||
}
|
||||
|
||||
/* Update timestamp */
|
||||
video->last_timestamp = timestamp;
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given Guacamole video encoder buffer to a frame in the format
|
||||
* required by libavcodec / libswscale. Black margins of the specified sizes
|
||||
* will be added. No scaling is performed; the image data is copied verbatim.
|
||||
*
|
||||
* @param buffer
|
||||
* The guacenc_buffer to copy as a new AVFrame.
|
||||
*
|
||||
* @param lsize
|
||||
* The size of the letterboxes to add, in pixels. Letterboxes are the
|
||||
* horizontal black boxes added to images which are scaled down to fit the
|
||||
* destination because they are too wide (the width is scaled to exactly
|
||||
* fit the destination, resulting in extra space at the top and bottom).
|
||||
*
|
||||
* @param psize
|
||||
* The size of the pillarboxes to add, in pixels. Pillarboxes are the
|
||||
* vertical black boxes added to images which are scaled down to fit the
|
||||
* destination because they are too tall (the height is scaled to exactly
|
||||
* fit the destination, resulting in extra space on the sides).
|
||||
*
|
||||
* @return
|
||||
* A pointer to a newly-allocated AVFrame containing exactly the same image
|
||||
* data as the given buffer. The image data within the frame and the frame
|
||||
* itself must be manually freed later.
|
||||
*/
|
||||
static AVFrame* guacenc_video_frame_convert(guacenc_buffer* buffer, int lsize,
|
||||
int psize) {
|
||||
|
||||
/* Init size of left/right pillarboxes */
|
||||
int left = psize;
|
||||
int right = psize;
|
||||
|
||||
/* Init size of top/bottom letterboxes */
|
||||
int top = lsize;
|
||||
int bottom = lsize;
|
||||
|
||||
/* Prepare source frame for buffer */
|
||||
AVFrame* frame = av_frame_alloc();
|
||||
if (frame == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Copy buffer properties to frame */
|
||||
frame->format = AV_PIX_FMT_RGB32;
|
||||
frame->width = buffer->width + left + right;
|
||||
frame->height = buffer->height + top + bottom;
|
||||
|
||||
/* Allocate actual backing data for frame */
|
||||
if (av_image_alloc(frame->data, frame->linesize, frame->width,
|
||||
frame->height, frame->format, 32) < 0) {
|
||||
av_frame_free(&frame);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Flush any pending operations */
|
||||
cairo_surface_flush(buffer->surface);
|
||||
|
||||
/* Get pointer to source image data */
|
||||
unsigned char* src_data = buffer->image;
|
||||
int src_stride = buffer->stride;
|
||||
|
||||
/* Get pointer to destination image data */
|
||||
unsigned char* dst_data = frame->data[0];
|
||||
int dst_stride = frame->linesize[0];
|
||||
|
||||
/* Get source/destination dimensions */
|
||||
int width = buffer->width;
|
||||
int height = buffer->height;
|
||||
|
||||
/* Source buffer is guaranteed to fit within destination buffer */
|
||||
assert(width <= frame->width);
|
||||
assert(height <= frame->height);
|
||||
|
||||
/* Add top margin */
|
||||
while (top > 0) {
|
||||
memset(dst_data, 0, frame->width * 4);
|
||||
dst_data += dst_stride;
|
||||
top--;
|
||||
}
|
||||
|
||||
/* Copy all data from source buffer to destination frame */
|
||||
while (height > 0) {
|
||||
|
||||
/* Calculate size of margin and data regions */
|
||||
int left_size = left * 4;
|
||||
int data_size = width * 4;
|
||||
int right_size = right * 4;
|
||||
|
||||
/* Add left margin */
|
||||
memset(dst_data, 0, left_size);
|
||||
|
||||
/* Copy data */
|
||||
memcpy(dst_data + left_size, src_data, data_size);
|
||||
|
||||
/* Add right margin */
|
||||
memset(dst_data + left_size + data_size, 0, right_size);
|
||||
|
||||
dst_data += dst_stride;
|
||||
src_data += src_stride;
|
||||
|
||||
height--;
|
||||
|
||||
}
|
||||
|
||||
/* Add bottom margin */
|
||||
while (bottom > 0) {
|
||||
memset(dst_data, 0, frame->width * 4);
|
||||
dst_data += dst_stride;
|
||||
bottom--;
|
||||
}
|
||||
|
||||
/* Frame converted */
|
||||
return frame;
|
||||
|
||||
}
|
||||
|
||||
void guacenc_video_prepare_frame(guacenc_video* video, guacenc_buffer* buffer) {
|
||||
|
||||
int lsize;
|
||||
int psize;
|
||||
|
||||
/* Ignore NULL buffers */
|
||||
if (buffer == NULL || buffer->surface == NULL)
|
||||
return;
|
||||
|
||||
/* Obtain destination frame */
|
||||
AVFrame* dst = video->next_frame;
|
||||
|
||||
/* Determine width of image if height is scaled to match destination */
|
||||
int scaled_width = buffer->width * dst->height / buffer->height;
|
||||
|
||||
/* Determine height of image if width is scaled to match destination */
|
||||
int scaled_height = buffer->height * dst->width / buffer->width;
|
||||
|
||||
/* If height-based scaling results in a fit width, add pillarboxes */
|
||||
if (scaled_width <= dst->width) {
|
||||
lsize = 0;
|
||||
psize = (dst->width - scaled_width)
|
||||
* buffer->height / dst->height / 2;
|
||||
}
|
||||
|
||||
/* If width-based scaling results in a fit width, add letterboxes */
|
||||
else {
|
||||
assert(scaled_height <= dst->height);
|
||||
psize = 0;
|
||||
lsize = (dst->height - scaled_height)
|
||||
* buffer->width / dst->width / 2;
|
||||
}
|
||||
|
||||
/* Prepare source frame for buffer */
|
||||
AVFrame* src = guacenc_video_frame_convert(buffer, lsize, psize);
|
||||
if (src == NULL) {
|
||||
guacenc_log(GUAC_LOG_WARNING, "Failed to allocate source frame. "
|
||||
"Frame dropped.");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Prepare scaling context */
|
||||
struct SwsContext* sws = sws_getContext(src->width, src->height,
|
||||
PIX_FMT_RGB32, dst->width, dst->height, PIX_FMT_YUV420P,
|
||||
SWS_BICUBIC, NULL, NULL, NULL);
|
||||
|
||||
/* Abort if scaling context could not be created */
|
||||
if (sws == NULL) {
|
||||
guacenc_log(GUAC_LOG_WARNING, "Failed to allocate software scaling "
|
||||
"context. Frame dropped.");
|
||||
av_freep(&src->data[0]);
|
||||
av_frame_free(&src);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Apply scaling, copying the source frame to the destination */
|
||||
sws_scale(sws, (const uint8_t* const*) src->data, src->linesize,
|
||||
0, src->height, dst->data, dst->linesize);
|
||||
|
||||
/* Free scaling context */
|
||||
sws_freeContext(sws);
|
||||
|
||||
/* Free source frame */
|
||||
av_freep(&src->data[0]);
|
||||
av_frame_free(&src);
|
||||
|
||||
}
|
||||
|
||||
int guacenc_video_free(guacenc_video* video) {
|
||||
|
||||
/* Ignore NULL video */
|
||||
if (video == NULL)
|
||||
return 0;
|
||||
|
||||
/* Write final frame */
|
||||
guacenc_video_flush_frame(video);
|
||||
|
||||
/* Init video packet for final flush of encoded data */
|
||||
AVPacket packet;
|
||||
av_init_packet(&packet);
|
||||
|
||||
/* Flush any unwritten frames */
|
||||
int retval;
|
||||
do {
|
||||
retval = guacenc_video_write_frame(video, NULL);
|
||||
} while (retval > 0);
|
||||
|
||||
/* File is now completely written */
|
||||
fclose(video->output);
|
||||
|
||||
/* Free frame encoding data */
|
||||
av_freep(&video->next_frame->data[0]);
|
||||
av_frame_free(&video->next_frame);
|
||||
|
||||
/* Clean up encoding context */
|
||||
avcodec_close(video->context);
|
||||
avcodec_free_context(&(video->context));
|
||||
|
||||
free(video);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
181
src/guacenc/video.h
Normal file
181
src/guacenc/video.h
Normal file
@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef GUACENC_VIDEO_H
|
||||
#define GUACENC_VIDEO_H
|
||||
|
||||
#include "config.h"
|
||||
#include "buffer.h"
|
||||
|
||||
#include <guacamole/timestamp.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/**
|
||||
* The framerate at which video should be encoded, in frames per second.
|
||||
*/
|
||||
#define GUACENC_VIDEO_FRAMERATE 25
|
||||
|
||||
/**
|
||||
* A video which is actively being encoded. Frames can be added to the video
|
||||
* as they are generated, along with their associated timestamps, and the
|
||||
* corresponding video will be continuously written as it is encoded.
|
||||
*/
|
||||
typedef struct guacenc_video {
|
||||
|
||||
/**
|
||||
* Output file stream.
|
||||
*/
|
||||
FILE* output;
|
||||
|
||||
/**
|
||||
* The open encoding context from libavcodec, created for the codec
|
||||
* specified when this guacenc_video was created.
|
||||
*/
|
||||
AVCodecContext* context;
|
||||
|
||||
/**
|
||||
* The width of the video, in pixels.
|
||||
*/
|
||||
int width;
|
||||
|
||||
/**
|
||||
* The height of the video, in pixels.
|
||||
*/
|
||||
int height;
|
||||
|
||||
/**
|
||||
* The desired output bitrate of the video, in bits per second.
|
||||
*/
|
||||
int bitrate;
|
||||
|
||||
/**
|
||||
* An image data area containing the next frame to be written, encoded as
|
||||
* YCbCr image data in the format required by avcodec_encode_video2(), for
|
||||
* use and re-use as frames are rendered.
|
||||
*/
|
||||
AVFrame* next_frame;
|
||||
|
||||
/**
|
||||
* The presentation timestamp that should be used for the next frame. This
|
||||
* is equivalent to the frame number.
|
||||
*/
|
||||
int64_t next_pts;
|
||||
|
||||
/**
|
||||
* The timestamp associated with the last frame, or 0 if no frames have yet
|
||||
* been added.
|
||||
*/
|
||||
guac_timestamp last_timestamp;
|
||||
|
||||
} guacenc_video;
|
||||
|
||||
/**
|
||||
* Allocates a new guacenc_video which encodes video according to the given
|
||||
* specifications, saving the output in the given file. If the output file
|
||||
* already exists, encoding will be aborted, and the original file contents
|
||||
* will be preserved. Frames will be scaled up or down as necessary to fit the
|
||||
* given width and height.
|
||||
*
|
||||
* @param path
|
||||
* The full path to the file in which encoded video should be written.
|
||||
*
|
||||
* @param codec_name
|
||||
* The name of the codec to use for the video encoding, as defined by
|
||||
* ffmpeg / libavcodec.
|
||||
*
|
||||
* @param width
|
||||
* The width of the desired video, in pixels.
|
||||
*
|
||||
* @param height
|
||||
* The height of the desired video, in pixels.
|
||||
*
|
||||
* @param bitrate
|
||||
* The desired overall bitrate of the resulting encoded video, in bits per
|
||||
* second.
|
||||
*/
|
||||
guacenc_video* guacenc_video_alloc(const char* path, const char* codec_name,
|
||||
int width, int height, int bitrate);
|
||||
|
||||
/**
|
||||
* Advances the timeline of the encoding process to the given timestamp, such
|
||||
* that frames added via guacenc_video_prepare_frame() will be encoded at the
|
||||
* proper frame boundaries within the video. Duplicate frames will be encoded
|
||||
* as necessary to ensure that the output is correctly timed with respect to
|
||||
* the given timestamp. This is particularly important as Guacamole does not
|
||||
* have a framerate per se, and the time between each Guacamole "frame" will
|
||||
* vary significantly.
|
||||
*
|
||||
* This function MUST be called prior to invoking guacenc_video_prepare_frame()
|
||||
* to ensure the prepared frame will be encoded at the correct point in time.
|
||||
*
|
||||
* @param video
|
||||
* The video whose timeline should be adjusted.
|
||||
*
|
||||
* @param timestamp
|
||||
* The Guacamole timestamp denoting the point in time that the video
|
||||
* timeline should be advanced to, as dictated by a parsed "sync"
|
||||
* instruction.
|
||||
*
|
||||
* @return
|
||||
* Zero if the timeline was adjusted successfully, non-zero if an error
|
||||
* occurs (such as during the encoding of duplicate frames).
|
||||
*/
|
||||
int guacenc_video_advance_timeline(guacenc_video* video,
|
||||
guac_timestamp timestamp);
|
||||
|
||||
/**
|
||||
* Stores the given buffer within the given video structure such that it will
|
||||
* be written if it falls within proper frame boundaries. If the timeline of
|
||||
* the video (as dictated by guacenc_video_advance_timeline()) is not at a
|
||||
* frame boundary with respect to the video framerate (it occurs between frame
|
||||
* boundaries), the prepared frame will only be written if another frame is not
|
||||
* prepared within the same pair of frame boundaries). The prepared frame will
|
||||
* not be written until it is implicitly flushed through updates to the video
|
||||
* timeline or through reaching the end of the encoding process
|
||||
* (guacenc_video_free()).
|
||||
*
|
||||
* @param video
|
||||
* The video in which the given buffer should be queued for possible
|
||||
* writing (depending on timing vs. video framerate).
|
||||
*
|
||||
* @param buffer
|
||||
* The guacenc_buffer representing the image data of the frame that should
|
||||
* be queued.
|
||||
*/
|
||||
void guacenc_video_prepare_frame(guacenc_video* video, guacenc_buffer* buffer);
|
||||
|
||||
/**
|
||||
* Frees all resources associated with the given video, finalizing the encoding
|
||||
* process. Any buffered frames which have not yet been written will be written
|
||||
* at this point.
|
||||
*
|
||||
* @return
|
||||
* Zero if the video was successfully written and freed, non-zero if the
|
||||
* video could not be written due to an error.
|
||||
*/
|
||||
int guacenc_video_free(guacenc_video* video);
|
||||
|
||||
#endif
|
||||
|
77
src/guacenc/webp.c
Normal file
77
src/guacenc/webp.c
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "log.h"
|
||||
#include "webp.h"
|
||||
|
||||
#include <cairo/cairo.h>
|
||||
#include <guacamole/client.h>
|
||||
#include <webp/decode.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
cairo_surface_t* guacenc_webp_decoder(unsigned char* data, int length) {
|
||||
|
||||
int width, height;
|
||||
|
||||
/* Validate WebP and pull dimensions */
|
||||
if (!WebPGetInfo((uint8_t*) data, length, &width, &height)) {
|
||||
guacenc_log(GUAC_LOG_WARNING, "Invalid WebP data");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Create blank Cairo surface */
|
||||
cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
|
||||
width, height);
|
||||
|
||||
/* Fill surface with opaque black */
|
||||
cairo_t* cairo = cairo_create(surface);
|
||||
cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_set_source_rgba(cairo, 0.0, 0.0, 0.0, 1.0);
|
||||
cairo_paint(cairo);
|
||||
cairo_destroy(cairo);
|
||||
|
||||
/* Finish any pending draws */
|
||||
cairo_surface_flush(surface);
|
||||
|
||||
/* Pull underlying buffer and its stride */
|
||||
int stride = cairo_image_surface_get_stride(surface);
|
||||
unsigned char* image = cairo_image_surface_get_data(surface);
|
||||
|
||||
/* Read WebP into surface */
|
||||
uint8_t* result = WebPDecodeBGRAInto((uint8_t*) data, length,
|
||||
(uint8_t*) image, stride * height, stride);
|
||||
|
||||
/* Verify WebP was successfully decoded */
|
||||
if (result == NULL) {
|
||||
guacenc_log(GUAC_LOG_WARNING, "Invalid WebP data");
|
||||
cairo_surface_destroy(surface);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* WebP was read successfully */
|
||||
return surface;
|
||||
|
||||
}
|
||||
|
35
src/guacenc/webp.h
Normal file
35
src/guacenc/webp.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef GUACENC_WEBP_H
|
||||
#define GUACENC_WEBP_H
|
||||
|
||||
#include "config.h"
|
||||
#include "image-stream.h"
|
||||
|
||||
/**
|
||||
* Decoder implementation which handles "image/webp" images.
|
||||
*/
|
||||
guacenc_decoder guacenc_webp_decoder;
|
||||
|
||||
#endif
|
||||
|
@ -89,6 +89,7 @@ libguac_la_SOURCES = \
|
||||
socket.c \
|
||||
socket-fd.c \
|
||||
socket-nest.c \
|
||||
socket-tee.c \
|
||||
timestamp.c \
|
||||
unicode.c \
|
||||
user.c \
|
||||
|
@ -462,6 +462,9 @@ void guac_client_free(guac_client* client) {
|
||||
|
||||
}
|
||||
|
||||
/* Free socket */
|
||||
guac_socket_free(client->socket);
|
||||
|
||||
/* Free layer pools */
|
||||
guac_pool_free(client->__buffer_pool);
|
||||
guac_pool_free(client->__layer_pool);
|
||||
|
@ -191,6 +191,38 @@ guac_socket* guac_socket_open(int fd);
|
||||
*/
|
||||
guac_socket* guac_socket_nest(guac_socket* parent, int index);
|
||||
|
||||
/**
|
||||
* Allocates and initializes a new guac_socket which delegates all socket
|
||||
* operations to the given primary socket, while simultaneously duplicating all
|
||||
* written data to the secondary socket. Freeing the returned guac_socket will
|
||||
* free both primary and secondary sockets.
|
||||
*
|
||||
* Return values (error codes) will come only from the primary socket. Locks
|
||||
* (like those used by guac_socket_instruction_begin() and
|
||||
* guac_socket_instruction_end()) will affect only the primary socket.
|
||||
*
|
||||
* If an error occurs while allocating the guac_socket object, NULL is returned,
|
||||
* and guac_error is set appropriately.
|
||||
*
|
||||
* @param primary
|
||||
* The primary guac_socket to which all socket operations should be
|
||||
* delegated. The error codes returned by socket operations, if any, will
|
||||
* always come from this socket. This socket will also be the only socket
|
||||
* locked when instructions begin (or unlocked when instructions end).
|
||||
*
|
||||
* @param secondary
|
||||
* The secondary guac_socket to which all data written to the primary
|
||||
* guac_socket should be copied. If an error prevents the write from
|
||||
* succeeding, that error will be ignored. Only errors from the primary
|
||||
* guac_socket will be acknowledged.
|
||||
*
|
||||
* @return
|
||||
* A newly allocated guac_socket object associated with the given primary
|
||||
* and secondary sockets, or NULL if an error occurs while allocating the
|
||||
* guac_socket object.
|
||||
*/
|
||||
guac_socket* guac_socket_tee(guac_socket* primary, guac_socket* secondary);
|
||||
|
||||
/**
|
||||
* Writes the given unsigned int to the given guac_socket object. The data
|
||||
* written may be buffered until the buffer is flushed automatically or
|
||||
|
234
src/libguac/socket-tee.c
Normal file
234
src/libguac/socket-tee.c
Normal file
@ -0,0 +1,234 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Glyptodon LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "socket.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
/**
|
||||
* Data specific to the tee implementation of guac_socket.
|
||||
*/
|
||||
typedef struct guac_socket_tee_data {
|
||||
|
||||
/**
|
||||
* The guac_socket to which all socket operations should be delegated.
|
||||
*/
|
||||
guac_socket* primary;
|
||||
|
||||
/**
|
||||
* The guac_socket to which all write and flush operations should be
|
||||
* duplicated.
|
||||
*/
|
||||
guac_socket* secondary;
|
||||
|
||||
} guac_socket_tee_data;
|
||||
|
||||
/**
|
||||
* Callback function which reads only from the primary socket.
|
||||
*
|
||||
* @param socket
|
||||
* The tee socket to read from.
|
||||
*
|
||||
* @param buf
|
||||
* The buffer to read data into.
|
||||
*
|
||||
* @param count
|
||||
* The maximum number of bytes to read into the given buffer.
|
||||
*
|
||||
* @return
|
||||
* The value returned by guac_socket_read() when invoked on the primary
|
||||
* socket with the given parameters.
|
||||
*/
|
||||
static ssize_t __guac_socket_tee_read_handler(guac_socket* socket,
|
||||
void* buf, size_t count) {
|
||||
|
||||
guac_socket_tee_data* data = (guac_socket_tee_data*) socket->data;
|
||||
|
||||
/* Delegate read to wrapped socket */
|
||||
return guac_socket_read(data->primary, buf, count);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback function which writes the given data to both underlying sockets,
|
||||
* returning only the result from the primary socket.
|
||||
*
|
||||
* @param socket
|
||||
* The tee socket to write through.
|
||||
*
|
||||
* @param buf
|
||||
* The buffer of data to write.
|
||||
*
|
||||
* @param count
|
||||
* The number of bytes in the buffer to be written.
|
||||
*
|
||||
* @return
|
||||
* The number of bytes written if the write was successful, or -1 if an
|
||||
* error occurs.
|
||||
*/
|
||||
static ssize_t __guac_socket_tee_write_handler(guac_socket* socket,
|
||||
const void* buf, size_t count) {
|
||||
|
||||
guac_socket_tee_data* data = (guac_socket_tee_data*) socket->data;
|
||||
|
||||
/* Write to secondary socket (ignoring result) */
|
||||
guac_socket_write(data->secondary, buf, count);
|
||||
|
||||
/* Delegate write to wrapped socket */
|
||||
if (guac_socket_write(data->primary, buf, count))
|
||||
return -1;
|
||||
|
||||
/* All data written successfully */
|
||||
return count;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback function which flushes both underlying sockets, returning only the
|
||||
* result from the primary socket.
|
||||
*
|
||||
* @param socket
|
||||
* The tee socket to flush.
|
||||
*
|
||||
* @return
|
||||
* The value returned by guac_socket_flush() when invoked on the primary
|
||||
* socket.
|
||||
*/
|
||||
static ssize_t __guac_socket_tee_flush_handler(guac_socket* socket) {
|
||||
|
||||
guac_socket_tee_data* data = (guac_socket_tee_data*) socket->data;
|
||||
|
||||
/* Flush secondary socket (ignoring result) */
|
||||
guac_socket_flush(data->secondary);
|
||||
|
||||
/* Delegate flush to wrapped socket */
|
||||
return guac_socket_flush(data->primary);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback function which delegates the lock operation to the primary
|
||||
* socket alone.
|
||||
*
|
||||
* @param socket
|
||||
* The tee socket on which guac_socket_instruction_begin() was invoked.
|
||||
*/
|
||||
static void __guac_socket_tee_lock_handler(guac_socket* socket) {
|
||||
|
||||
guac_socket_tee_data* data = (guac_socket_tee_data*) socket->data;
|
||||
|
||||
/* Delegate lock to wrapped socket */
|
||||
guac_socket_instruction_begin(data->primary);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback function which delegates the unlock operation to the primary
|
||||
* socket alone.
|
||||
*
|
||||
* @param socket
|
||||
* The tee socket on which guac_socket_instruction_end() was invoked.
|
||||
*/
|
||||
static void __guac_socket_tee_unlock_handler(guac_socket* socket) {
|
||||
|
||||
guac_socket_tee_data* data = (guac_socket_tee_data*) socket->data;
|
||||
|
||||
/* Delegate unlock to wrapped socket */
|
||||
guac_socket_instruction_end(data->primary);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback function which delegates the select operation to the primary
|
||||
* socket alone.
|
||||
*
|
||||
* @param socket
|
||||
* The tee socket on which guac_socket_select() was invoked.
|
||||
*
|
||||
* @param usec_timeout
|
||||
* The timeout to specify when invoking guac_socket_select() on the
|
||||
* primary socket.
|
||||
*
|
||||
* @return
|
||||
* The value returned by guac_socket_select() when invoked with the
|
||||
* given parameters on the primary socket.
|
||||
*/
|
||||
static int __guac_socket_tee_select_handler(guac_socket* socket,
|
||||
int usec_timeout) {
|
||||
|
||||
guac_socket_tee_data* data = (guac_socket_tee_data*) socket->data;
|
||||
|
||||
/* Delegate select to wrapped socket */
|
||||
return guac_socket_select(data->primary, usec_timeout);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback function which frees all underlying data associated with the
|
||||
* given tee socket, including both primary and secondary sockets.
|
||||
*
|
||||
* @param socket
|
||||
* The tee socket being freed.
|
||||
*
|
||||
* @return
|
||||
* Always zero.
|
||||
*/
|
||||
static int __guac_socket_tee_free_handler(guac_socket* socket) {
|
||||
|
||||
guac_socket_tee_data* data = (guac_socket_tee_data*) socket->data;
|
||||
|
||||
/* Free underlying sockets */
|
||||
guac_socket_free(data->primary);
|
||||
guac_socket_free(data->secondary);
|
||||
|
||||
/* Freeing the tee socket always succeeds */
|
||||
free(data);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
guac_socket* guac_socket_tee(guac_socket* primary, guac_socket* secondary) {
|
||||
|
||||
/* Set up socket to split outout into a file */
|
||||
guac_socket_tee_data* data = malloc(sizeof(guac_socket_tee_data));
|
||||
data->primary = primary;
|
||||
data->secondary = secondary;
|
||||
|
||||
/* Associate tee-specific data with new socket */
|
||||
guac_socket* socket = guac_socket_alloc();
|
||||
socket->data = data;
|
||||
|
||||
/* Assign handlers */
|
||||
socket->read_handler = __guac_socket_tee_read_handler;
|
||||
socket->write_handler = __guac_socket_tee_write_handler;
|
||||
socket->select_handler = __guac_socket_tee_select_handler;
|
||||
socket->flush_handler = __guac_socket_tee_flush_handler;
|
||||
socket->lock_handler = __guac_socket_tee_lock_handler;
|
||||
socket->unlock_handler = __guac_socket_tee_unlock_handler;
|
||||
socket->free_handler = __guac_socket_tee_free_handler;
|
||||
|
||||
return socket;
|
||||
|
||||
}
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "client.h"
|
||||
#include "guac_cursor.h"
|
||||
#include "guac_display.h"
|
||||
#include "guac_recording.h"
|
||||
#include "rdp.h"
|
||||
#include "rdp_bitmap.h"
|
||||
#include "rdp_cliprdr.h"
|
||||
@ -699,6 +700,14 @@ void* guac_rdp_client_thread(void* data) {
|
||||
/* Init random number generator */
|
||||
srandom(time(NULL));
|
||||
|
||||
/* Set up screen recording, if requested */
|
||||
if (settings->recording_path != NULL) {
|
||||
guac_common_recording_create(client,
|
||||
settings->recording_path,
|
||||
settings->recording_name,
|
||||
settings->create_recording_path);
|
||||
}
|
||||
|
||||
/* Create display */
|
||||
rdp_client->display = guac_common_display_alloc(client,
|
||||
rdp_client->settings->width,
|
||||
|
@ -89,6 +89,10 @@ const char* GUAC_RDP_CLIENT_ARGS[] = {
|
||||
"sftp-directory",
|
||||
#endif
|
||||
|
||||
"recording-path",
|
||||
"recording-name",
|
||||
"create-recording-path",
|
||||
|
||||
NULL
|
||||
};
|
||||
|
||||
@ -352,6 +356,10 @@ enum RDP_ARGS_IDX {
|
||||
|
||||
#endif
|
||||
|
||||
IDX_RECORDING_PATH,
|
||||
IDX_RECORDING_NAME,
|
||||
IDX_CREATE_RECORDING_PATH,
|
||||
|
||||
RDP_ARGS_COUNT
|
||||
};
|
||||
|
||||
@ -673,6 +681,21 @@ guac_rdp_settings* guac_rdp_parse_args(guac_user* user,
|
||||
IDX_SFTP_DIRECTORY, NULL);
|
||||
#endif
|
||||
|
||||
/* Read recording path */
|
||||
settings->recording_path =
|
||||
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_RECORDING_PATH, NULL);
|
||||
|
||||
/* Read recording name */
|
||||
settings->recording_name =
|
||||
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_RECORDING_NAME, GUAC_RDP_DEFAULT_RECORDING_NAME);
|
||||
|
||||
/* Parse path creation flag */
|
||||
settings->create_recording_path =
|
||||
guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_CREATE_RECORDING_PATH, 0);
|
||||
|
||||
/* Success */
|
||||
return settings;
|
||||
|
||||
@ -688,6 +711,8 @@ void guac_rdp_settings_free(guac_rdp_settings* settings) {
|
||||
free(settings->initial_program);
|
||||
free(settings->password);
|
||||
free(settings->preconnection_blob);
|
||||
free(settings->recording_name);
|
||||
free(settings->recording_path);
|
||||
free(settings->remote_app);
|
||||
free(settings->remote_app_args);
|
||||
free(settings->remote_app_dir);
|
||||
|
@ -56,6 +56,11 @@
|
||||
*/
|
||||
#define RDP_DEFAULT_DEPTH 16
|
||||
|
||||
/**
|
||||
* The filename to use for the screen recording, if not specified.
|
||||
*/
|
||||
#define GUAC_RDP_DEFAULT_RECORDING_NAME "recording"
|
||||
|
||||
/**
|
||||
* All supported combinations of security types.
|
||||
*/
|
||||
@ -328,6 +333,23 @@ typedef struct guac_rdp_settings {
|
||||
char* sftp_directory;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* The path in which the screen recording should be saved, if enabled. If
|
||||
* no screen recording should be saved, this will be NULL.
|
||||
*/
|
||||
char* recording_path;
|
||||
|
||||
/**
|
||||
* The filename to use for the screen recording, if enabled.
|
||||
*/
|
||||
char* recording_name;
|
||||
|
||||
/**
|
||||
* Whether the screen recording path should be automatically created if it
|
||||
* does not already exist.
|
||||
*/
|
||||
int create_recording_path;
|
||||
|
||||
} guac_rdp_settings;
|
||||
|
||||
/**
|
||||
|
@ -50,6 +50,9 @@ const char* GUAC_SSH_CLIENT_ARGS[] = {
|
||||
"typescript-path",
|
||||
"typescript-name",
|
||||
"create-typescript-path",
|
||||
"recording-path",
|
||||
"recording-name",
|
||||
"create-recording-path",
|
||||
NULL
|
||||
};
|
||||
|
||||
@ -140,6 +143,24 @@ enum SSH_ARGS_IDX {
|
||||
*/
|
||||
IDX_CREATE_TYPESCRIPT_PATH,
|
||||
|
||||
/**
|
||||
* The full absolute path to the directory in which screen recordings
|
||||
* should be written.
|
||||
*/
|
||||
IDX_RECORDING_PATH,
|
||||
|
||||
/**
|
||||
* The name that should be given to screen recording which are written in
|
||||
* the given path.
|
||||
*/
|
||||
IDX_RECORDING_NAME,
|
||||
|
||||
/**
|
||||
* Whether the specified screen recording path should automatically be
|
||||
* created if it does not yet exist.
|
||||
*/
|
||||
IDX_CREATE_RECORDING_PATH,
|
||||
|
||||
SSH_ARGS_COUNT
|
||||
};
|
||||
|
||||
@ -234,6 +255,21 @@ guac_ssh_settings* guac_ssh_parse_args(guac_user* user,
|
||||
guac_user_parse_args_boolean(user, GUAC_SSH_CLIENT_ARGS, argv,
|
||||
IDX_CREATE_TYPESCRIPT_PATH, false);
|
||||
|
||||
/* Read recording path */
|
||||
settings->recording_path =
|
||||
guac_user_parse_args_string(user, GUAC_SSH_CLIENT_ARGS, argv,
|
||||
IDX_RECORDING_PATH, NULL);
|
||||
|
||||
/* Read recording name */
|
||||
settings->recording_name =
|
||||
guac_user_parse_args_string(user, GUAC_SSH_CLIENT_ARGS, argv,
|
||||
IDX_RECORDING_NAME, GUAC_SSH_DEFAULT_RECORDING_NAME);
|
||||
|
||||
/* Parse path creation flag */
|
||||
settings->create_recording_path =
|
||||
guac_user_parse_args_boolean(user, GUAC_SSH_CLIENT_ARGS, argv,
|
||||
IDX_CREATE_RECORDING_PATH, false);
|
||||
|
||||
/* Parsing was successful */
|
||||
return settings;
|
||||
|
||||
@ -262,6 +298,10 @@ void guac_ssh_settings_free(guac_ssh_settings* settings) {
|
||||
free(settings->typescript_name);
|
||||
free(settings->typescript_path);
|
||||
|
||||
/* Free screen recording settings */
|
||||
free(settings->recording_name);
|
||||
free(settings->recording_path);
|
||||
|
||||
/* Free overall structure */
|
||||
free(settings);
|
||||
|
||||
|
@ -51,6 +51,11 @@
|
||||
*/
|
||||
#define GUAC_SSH_DEFAULT_TYPESCRIPT_NAME "typescript"
|
||||
|
||||
/**
|
||||
* The filename to use for the screen recording, if not specified.
|
||||
*/
|
||||
#define GUAC_SSH_DEFAULT_RECORDING_NAME "recording"
|
||||
|
||||
/**
|
||||
* Settings for the SSH connection. The values for this structure are parsed
|
||||
* from the arguments given during the Guacamole protocol handshake using the
|
||||
@ -157,6 +162,23 @@ typedef struct guac_ssh_settings {
|
||||
*/
|
||||
bool create_typescript_path;
|
||||
|
||||
/**
|
||||
* The path in which the screen recording should be saved, if enabled. If
|
||||
* no screen recording should be saved, this will be NULL.
|
||||
*/
|
||||
char* recording_path;
|
||||
|
||||
/**
|
||||
* The filename to use for the screen recording, if enabled.
|
||||
*/
|
||||
char* recording_name;
|
||||
|
||||
/**
|
||||
* Whether the screen recording path should be automatically created if it
|
||||
* does not already exist.
|
||||
*/
|
||||
bool create_recording_path;
|
||||
|
||||
} guac_ssh_settings;
|
||||
|
||||
/**
|
||||
|
@ -22,6 +22,7 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "guac_recording.h"
|
||||
#include "guac_sftp.h"
|
||||
#include "guac_ssh.h"
|
||||
#include "settings.h"
|
||||
@ -183,6 +184,14 @@ void* ssh_client_thread(void* data) {
|
||||
if (guac_common_ssh_init(client))
|
||||
return NULL;
|
||||
|
||||
/* Set up screen recording, if requested */
|
||||
if (settings->recording_path != NULL) {
|
||||
guac_common_recording_create(client,
|
||||
settings->recording_path,
|
||||
settings->recording_name,
|
||||
settings->create_recording_path);
|
||||
}
|
||||
|
||||
/* Create terminal */
|
||||
ssh_client->term = guac_terminal_create(client,
|
||||
settings->font_name, settings->font_size,
|
||||
|
@ -46,6 +46,9 @@ const char* GUAC_TELNET_CLIENT_ARGS[] = {
|
||||
"typescript-path",
|
||||
"typescript-name",
|
||||
"create-typescript-path",
|
||||
"recording-path",
|
||||
"recording-name",
|
||||
"create-recording-path",
|
||||
NULL
|
||||
};
|
||||
|
||||
@ -120,6 +123,24 @@ enum TELNET_ARGS_IDX {
|
||||
*/
|
||||
IDX_CREATE_TYPESCRIPT_PATH,
|
||||
|
||||
/**
|
||||
* The full absolute path to the directory in which screen recordings
|
||||
* should be written.
|
||||
*/
|
||||
IDX_RECORDING_PATH,
|
||||
|
||||
/**
|
||||
* The name that should be given to screen recording which are written in
|
||||
* the given path.
|
||||
*/
|
||||
IDX_RECORDING_NAME,
|
||||
|
||||
/**
|
||||
* Whether the specified screen recording path should automatically be
|
||||
* created if it does not yet exist.
|
||||
*/
|
||||
IDX_CREATE_RECORDING_PATH,
|
||||
|
||||
TELNET_ARGS_COUNT
|
||||
};
|
||||
|
||||
@ -239,6 +260,21 @@ guac_telnet_settings* guac_telnet_parse_args(guac_user* user,
|
||||
guac_user_parse_args_boolean(user, GUAC_TELNET_CLIENT_ARGS, argv,
|
||||
IDX_CREATE_TYPESCRIPT_PATH, false);
|
||||
|
||||
/* Read recording path */
|
||||
settings->recording_path =
|
||||
guac_user_parse_args_string(user, GUAC_TELNET_CLIENT_ARGS, argv,
|
||||
IDX_RECORDING_PATH, NULL);
|
||||
|
||||
/* Read recording name */
|
||||
settings->recording_name =
|
||||
guac_user_parse_args_string(user, GUAC_TELNET_CLIENT_ARGS, argv,
|
||||
IDX_RECORDING_NAME, GUAC_TELNET_DEFAULT_RECORDING_NAME);
|
||||
|
||||
/* Parse path creation flag */
|
||||
settings->create_recording_path =
|
||||
guac_user_parse_args_boolean(user, GUAC_TELNET_CLIENT_ARGS, argv,
|
||||
IDX_CREATE_RECORDING_PATH, false);
|
||||
|
||||
/* Parsing was successful */
|
||||
return settings;
|
||||
|
||||
@ -274,6 +310,10 @@ void guac_telnet_settings_free(guac_telnet_settings* settings) {
|
||||
free(settings->typescript_name);
|
||||
free(settings->typescript_path);
|
||||
|
||||
/* Free screen recording settings */
|
||||
free(settings->recording_name);
|
||||
free(settings->recording_path);
|
||||
|
||||
/* Free overall structure */
|
||||
free(settings);
|
||||
|
||||
|
@ -53,6 +53,11 @@
|
||||
*/
|
||||
#define GUAC_TELNET_DEFAULT_TYPESCRIPT_NAME "typescript"
|
||||
|
||||
/**
|
||||
* The filename to use for the screen recording, if not specified.
|
||||
*/
|
||||
#define GUAC_TELNET_DEFAULT_RECORDING_NAME "recording"
|
||||
|
||||
/**
|
||||
* The regular expression to use when searching for the username/login prompt
|
||||
* if no other regular expression is specified.
|
||||
@ -157,6 +162,23 @@ typedef struct guac_telnet_settings {
|
||||
*/
|
||||
bool create_typescript_path;
|
||||
|
||||
/**
|
||||
* The path in which the screen recording should be saved, if enabled. If
|
||||
* no screen recording should be saved, this will be NULL.
|
||||
*/
|
||||
char* recording_path;
|
||||
|
||||
/**
|
||||
* The filename to use for the screen recording, if enabled.
|
||||
*/
|
||||
char* recording_name;
|
||||
|
||||
/**
|
||||
* Whether the screen recording path should be automatically created if it
|
||||
* does not already exist.
|
||||
*/
|
||||
bool create_recording_path;
|
||||
|
||||
} guac_telnet_settings;
|
||||
|
||||
/**
|
||||
|
@ -21,6 +21,7 @@
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "guac_recording.h"
|
||||
#include "telnet.h"
|
||||
#include "terminal.h"
|
||||
|
||||
@ -468,6 +469,14 @@ void* guac_telnet_client_thread(void* data) {
|
||||
char buffer[8192];
|
||||
int wait_result;
|
||||
|
||||
/* Set up screen recording, if requested */
|
||||
if (settings->recording_path != NULL) {
|
||||
guac_common_recording_create(client,
|
||||
settings->recording_path,
|
||||
settings->recording_name,
|
||||
settings->create_recording_path);
|
||||
}
|
||||
|
||||
/* Create terminal */
|
||||
telnet_client->term = guac_terminal_create(client,
|
||||
settings->font_name, settings->font_size,
|
||||
|
@ -71,6 +71,10 @@ const char* GUAC_VNC_CLIENT_ARGS[] = {
|
||||
"sftp-directory",
|
||||
#endif
|
||||
|
||||
"recording-path",
|
||||
"recording-name",
|
||||
"create-recording-path",
|
||||
|
||||
NULL
|
||||
};
|
||||
|
||||
@ -228,6 +232,10 @@ enum VNC_ARGS_IDX {
|
||||
IDX_SFTP_DIRECTORY,
|
||||
#endif
|
||||
|
||||
IDX_RECORDING_PATH,
|
||||
IDX_RECORDING_NAME,
|
||||
IDX_CREATE_RECORDING_PATH,
|
||||
|
||||
VNC_ARGS_COUNT
|
||||
};
|
||||
|
||||
@ -378,6 +386,20 @@ guac_vnc_settings* guac_vnc_parse_args(guac_user* user,
|
||||
IDX_SFTP_DIRECTORY, NULL);
|
||||
#endif
|
||||
|
||||
/* Read recording path */
|
||||
settings->recording_path =
|
||||
guac_user_parse_args_string(user, GUAC_VNC_CLIENT_ARGS, argv,
|
||||
IDX_RECORDING_PATH, NULL);
|
||||
|
||||
/* Read recording name */
|
||||
settings->recording_name =
|
||||
guac_user_parse_args_string(user, GUAC_VNC_CLIENT_ARGS, argv,
|
||||
IDX_RECORDING_NAME, GUAC_VNC_DEFAULT_RECORDING_NAME);
|
||||
|
||||
/* Parse path creation flag */
|
||||
settings->create_recording_path =
|
||||
guac_user_parse_args_boolean(user, GUAC_VNC_CLIENT_ARGS, argv,
|
||||
IDX_CREATE_RECORDING_PATH, false);
|
||||
|
||||
return settings;
|
||||
|
||||
@ -389,6 +411,8 @@ void guac_vnc_settings_free(guac_vnc_settings* settings) {
|
||||
free(settings->clipboard_encoding);
|
||||
free(settings->encodings);
|
||||
free(settings->hostname);
|
||||
free(settings->recording_name);
|
||||
free(settings->recording_path);
|
||||
|
||||
#ifdef ENABLE_VNC_REPEATER
|
||||
/* Free VNC repeater settings */
|
||||
|
@ -28,6 +28,11 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* The filename to use for the screen recording, if not specified.
|
||||
*/
|
||||
#define GUAC_VNC_DEFAULT_RECORDING_NAME "recording"
|
||||
|
||||
/**
|
||||
* VNC-specific client data.
|
||||
*/
|
||||
@ -173,6 +178,23 @@ typedef struct guac_vnc_settings {
|
||||
char* sftp_directory;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* The path in which the screen recording should be saved, if enabled. If
|
||||
* no screen recording should be saved, this will be NULL.
|
||||
*/
|
||||
char* recording_path;
|
||||
|
||||
/**
|
||||
* The filename to use for the screen recording, if enabled.
|
||||
*/
|
||||
char* recording_name;
|
||||
|
||||
/**
|
||||
* Whether the screen recording path should be automatically created if it
|
||||
* does not already exist.
|
||||
*/
|
||||
bool create_recording_path;
|
||||
|
||||
} guac_vnc_settings;
|
||||
|
||||
/**
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "guac_clipboard.h"
|
||||
#include "guac_cursor.h"
|
||||
#include "guac_display.h"
|
||||
#include "guac_recording.h"
|
||||
#include "log.h"
|
||||
#include "settings.h"
|
||||
#include "vnc.h"
|
||||
@ -317,6 +318,14 @@ void* guac_vnc_client_thread(void* data) {
|
||||
/* Set remaining client data */
|
||||
vnc_client->rfb_client = rfb_client;
|
||||
|
||||
/* Set up screen recording, if requested */
|
||||
if (settings->recording_path != NULL) {
|
||||
guac_common_recording_create(client,
|
||||
settings->recording_path,
|
||||
settings->recording_name,
|
||||
settings->create_recording_path);
|
||||
}
|
||||
|
||||
/* Send name */
|
||||
guac_protocol_send_name(client->socket, rfb_client->desktopName);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user