Compare commits
231 Commits
v0.9.2-bet
...
video
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
54bc22532a | ||
|
|
f47750131a | ||
|
|
44a194886d | ||
|
|
ac56f5566b | ||
|
|
b09e1b907f | ||
|
|
f8c9983d9b | ||
|
|
c768e70fba | ||
|
|
8b1c0a3bb7 | ||
|
|
f4826ebd20 | ||
|
|
a99b76286c | ||
|
|
c54b705930 | ||
|
|
777b5c57de | ||
|
|
4d7eb8976b | ||
|
|
a930a0cded | ||
|
|
ccae6d174b | ||
|
|
21cc797dd3 | ||
|
|
c961c56c39 | ||
|
|
9144045718 | ||
|
|
7a695c976e | ||
|
|
aefdb1392f | ||
|
|
14e08a2067 | ||
|
|
f64bd4f1a4 | ||
|
|
0b6334369c | ||
|
|
b8c69a7560 | ||
|
|
e84a37fb3d | ||
|
|
23935145a8 | ||
|
|
7f7b177b1a | ||
|
|
95d34f7e58 | ||
|
|
a1cf454f06 | ||
|
|
04461b9d4c | ||
|
|
0d482f9a9b | ||
|
|
a7773bee6b | ||
|
|
75cef8b726 | ||
|
|
380bc0e7f8 | ||
|
|
999cfbb806 | ||
|
|
12a0c0d8af | ||
|
|
7bfcb19ee1 | ||
|
|
df9ca2e0ce | ||
|
|
4fda2526f4 | ||
|
|
102257cb32 | ||
|
|
77509c1e56 | ||
|
|
9e0a5d26af | ||
|
|
c6c2661121 | ||
|
|
8b8201fd73 | ||
|
|
33a113a475 | ||
|
|
6d1cebc24a | ||
|
|
fcd7836a83 | ||
|
|
073737c8ac | ||
|
|
16c72ba0a7 | ||
|
|
d3c9ff8b12 | ||
|
|
4bc4c1d7f6 | ||
|
|
d8ba9a61e2 | ||
|
|
0e2a25476f | ||
|
|
b3b4acaaa3 | ||
|
|
201f2517f7 | ||
|
|
f51167a2ce | ||
|
|
87b3a630cf | ||
|
|
7412dde8ee | ||
|
|
3ef6e1333b | ||
|
|
b84220f319 | ||
|
|
42d437ad25 | ||
|
|
d5b84d9185 | ||
|
|
6db1c2bf0d | ||
|
|
ae1bc396bf | ||
|
|
c7a7ce70be | ||
|
|
55178c76b8 | ||
|
|
5752bf8ab7 | ||
|
|
6fd1035fb1 | ||
|
|
212e0f1a10 | ||
|
|
69b945fc87 | ||
|
|
b837bd6f59 | ||
|
|
670eefbc97 | ||
|
|
b2d8db3f7b | ||
|
|
a3e2b1e46e | ||
|
|
472ede66ea | ||
|
|
da228af778 | ||
|
|
59f5648592 | ||
|
|
19c473f792 | ||
|
|
8a8fa1d906 | ||
|
|
5a81223d96 | ||
|
|
eff38d47f9 | ||
|
|
1de3769fd3 | ||
|
|
749ddce5b9 | ||
|
|
d420aa6413 | ||
|
|
93e42772bc | ||
|
|
9098478faf | ||
|
|
4710ec1822 | ||
|
|
02b9b268ff | ||
|
|
a015e33111 | ||
|
|
950d871b0e | ||
|
|
02e7b006de | ||
|
|
95af51e28d | ||
|
|
64dfb8d1d6 | ||
|
|
b173c8854a | ||
|
|
888f24e7af | ||
|
|
8be81165bd | ||
|
|
509b5795a0 | ||
|
|
581fe511ad | ||
|
|
9ca647667b | ||
|
|
dbe2930758 | ||
|
|
b690ae4c2d | ||
|
|
5e4544d5fc | ||
|
|
0b19961b06 | ||
|
|
286694869b | ||
|
|
fdeefcfab4 | ||
|
|
24cf1f0f9a | ||
|
|
d01f6e6d27 | ||
|
|
c1160d1468 | ||
|
|
1c38b6f120 | ||
|
|
70dd0058ac | ||
|
|
a4c5c38b53 | ||
|
|
789308212b | ||
|
|
ce5fe304cb | ||
|
|
781075c841 | ||
|
|
2f43cead79 | ||
|
|
e7a5aa271e | ||
|
|
ad206180d2 | ||
|
|
89dd199317 | ||
|
|
8be70e5ae0 | ||
|
|
ce94d92e18 | ||
|
|
c3ef9ff557 | ||
|
|
c0276776e9 | ||
|
|
10f4aa9e7b | ||
|
|
b2dc76ee18 | ||
|
|
12cdad066e | ||
|
|
a850be4afa | ||
|
|
354d544843 | ||
|
|
0bf9403a36 | ||
|
|
29db8997ec | ||
|
|
46f15667f7 | ||
|
|
edb5fee88b | ||
|
|
23871b42d1 | ||
|
|
d6d875ef3b | ||
|
|
6c84631bbc | ||
|
|
6bb87ee2b2 | ||
|
|
15c74e7dc9 | ||
|
|
f1be4be653 | ||
|
|
b5f1a00282 | ||
|
|
740a8217ab | ||
|
|
796de9653f | ||
|
|
6916c83b48 | ||
|
|
d10d7167f6 | ||
|
|
a8dc933701 | ||
|
|
d61bf69c33 | ||
|
|
62fca18cb9 | ||
|
|
dc3ddc8efc | ||
|
|
e497b53d09 | ||
|
|
7dc51fa7a5 | ||
|
|
443858cf83 | ||
|
|
6b39933658 | ||
|
|
6067290169 | ||
|
|
a8699ab8cb | ||
|
|
996895268e | ||
|
|
fb14c2dec9 | ||
|
|
83d5e5bffd | ||
|
|
19dfb7901c | ||
|
|
6acf3c9e62 | ||
|
|
0dcb896920 | ||
|
|
d88aefba4e | ||
|
|
b0c54f6b24 | ||
|
|
6342a117ff | ||
|
|
766fc43855 | ||
|
|
19f0803636 | ||
|
|
3c3ac34eb8 | ||
|
|
8c43287afb | ||
|
|
c16cc6e2b9 | ||
|
|
c211f31113 | ||
|
|
4cc536636a | ||
|
|
734bdd3746 | ||
|
|
8218a0cb2a | ||
|
|
2525174260 | ||
|
|
b0896c8859 | ||
|
|
09de4b8349 | ||
|
|
d4747a8c80 | ||
|
|
5b73c1ca23 | ||
|
|
f21fb95bb9 | ||
|
|
7b02f3d8b8 | ||
|
|
03a333fb18 | ||
|
|
54253efaa0 | ||
|
|
8b859f4598 | ||
|
|
8c5b5f17eb | ||
|
|
71fa476514 | ||
|
|
4fd1f19f3b | ||
|
|
257aabecb0 | ||
|
|
74161066af | ||
|
|
e2d86788ba | ||
|
|
4c635f65d3 | ||
|
|
1d4ada6815 | ||
|
|
d3ec2aa4d1 | ||
|
|
10c334303a | ||
|
|
c4987024ef | ||
|
|
04d7bef2ae | ||
|
|
12565dc4ac | ||
|
|
46b7d1db95 | ||
|
|
0045f07965 | ||
|
|
d1fdb877ef | ||
|
|
e62e12fbfe | ||
|
|
50183c7caa | ||
|
|
79ab05192b | ||
|
|
c6c278f9d5 | ||
|
|
37c17ddbf9 | ||
|
|
c755008d15 | ||
|
|
075eed5cbb | ||
|
|
7b7295fd4e | ||
|
|
5964156a6f | ||
|
|
1ff792cf4b | ||
|
|
3043216b63 | ||
|
|
499b251716 | ||
|
|
d20f751441 | ||
|
|
915901848d | ||
|
|
ee2423ed2b | ||
|
|
ce6d66f030 | ||
|
|
41e6fedba6 | ||
|
|
642a67f76f | ||
|
|
d7198cbe5d | ||
|
|
25b8e64adb | ||
|
|
d8caab699d | ||
|
|
da83ecf86a | ||
|
|
189f503b98 | ||
|
|
f54dc7829a | ||
|
|
fde088ce65 | ||
|
|
a55f142c98 | ||
|
|
25995e2490 | ||
|
|
281d65292a | ||
|
|
57a3c3bba8 | ||
|
|
92c7695981 | ||
|
|
7f90205cf2 | ||
|
|
910fd8fa3e | ||
|
|
c97828471c | ||
|
|
c5b7137f2b | ||
|
|
a3c0ce55c9 |
@@ -4,6 +4,7 @@ services:
|
||||
|
||||
variables:
|
||||
GITLAB_SHARED_DIND_DIR: /builds/$CI_PROJECT_PATH/shared
|
||||
GIT_SUBMODULE_STRATEGY: normal
|
||||
GIT_FETCH_EXTRA_FLAGS: --tags
|
||||
|
||||
stages:
|
||||
|
||||
4
.gitmodules
vendored
Normal file
4
.gitmodules
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
[submodule "kasmweb"]
|
||||
path = kasmweb
|
||||
url = https://github.com/kasmtech/noVNC.git
|
||||
branch = video
|
||||
@@ -20,7 +20,7 @@ Incomplete and generally out of date copyright list::
|
||||
All Rights Reserved.
|
||||
|
||||
This software is distributed under the GNU General Public Licence as published
|
||||
by the Free Software Foundation. See the file LICENCE.TXT for the conditions
|
||||
by the Free Software Foundation. See the file LICENSE.TXT for the conditions
|
||||
under which this software is made available. KasmVNC also contains code from
|
||||
other sources. See the Acknowledgements section below, and the individual
|
||||
source files, for details of the conditions under which they are made
|
||||
@@ -105,4 +105,4 @@ This distribution contains software from the X Window System. This is:
|
||||
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
||||
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||||
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
||||
SOFTWARE.
|
||||
SOFTWARE.
|
||||
|
||||
@@ -101,4 +101,4 @@ We also need help with Windows, which is not currently supported. While KasmVNC
|
||||
See the [builder/README.md](https://github.com/kasmtech/KasmVNC/blob/master/builder/README.md). We containerize our build systems to ensure highly repeatable builds.
|
||||
|
||||
### License and Acknowledgements
|
||||
See the [LICENSE.TXT](https://github.com/kasmtech/KasmVNC/blob/master/LICENCE.TXT) and [ACKNOWLEDGEMENTS.MD](https://github.com/kasmtech/KasmVNC/blob/master/LICENSE.TXT)
|
||||
See the [LICENSE.TXT](https://github.com/kasmtech/KasmVNC/blob/master/LICENSE.TXT) and [ACKNOWLEDGEMENTS.MD](https://github.com/kasmtech/KasmVNC/blob/master/LICENSE.TXT)
|
||||
|
||||
@@ -14,6 +14,5 @@ cp -R ./* /build/
|
||||
cd /build
|
||||
rm *.md
|
||||
rm AUTHORS
|
||||
rm *.yml
|
||||
rm vnc.html
|
||||
rm vnc_lite.html
|
||||
|
||||
@@ -13,7 +13,7 @@ RUN apt-get update && \
|
||||
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tzdata
|
||||
RUN apt-get update && apt-get -y build-dep xorg-server libxfont-dev
|
||||
RUN apt-get update && apt-get -y install cmake git libjpeg-dev libgnutls28-dev vim wget tightvncserver
|
||||
RUN apt-get update && apt-get -y install libjpeg-dev libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev
|
||||
RUN apt-get update && apt-get -y install libjpeg-dev libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev libx264-dev
|
||||
|
||||
# Additions for webp
|
||||
RUN cd /tmp && wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz
|
||||
|
||||
@@ -13,7 +13,7 @@ RUN apt-get update && \
|
||||
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tzdata
|
||||
RUN apt-get update && apt-get -y build-dep xorg-server libxfont-dev
|
||||
RUN apt-get update && apt-get -y install cmake git libjpeg-dev libgnutls28-dev vim wget tightvncserver
|
||||
RUN apt-get update && apt-get -y install libjpeg-dev libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev
|
||||
RUN apt-get update && apt-get -y install libjpeg-dev libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev libx264-dev
|
||||
|
||||
# Additions for webp
|
||||
RUN cd /tmp && wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz
|
||||
|
||||
@@ -13,7 +13,7 @@ RUN apt-get update && \
|
||||
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tzdata
|
||||
RUN apt-get update && apt-get -y build-dep xorg-server libxfont-dev
|
||||
RUN apt-get update && apt-get -y install cmake git libjpeg-dev libgnutls28-dev vim wget tightvncserver
|
||||
RUN apt-get update && apt-get -y install libjpeg-dev libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev
|
||||
RUN apt-get update && apt-get -y install libjpeg-dev libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev libx264-dev
|
||||
|
||||
# Additions for webp
|
||||
RUN cd /tmp && wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz
|
||||
|
||||
@@ -11,7 +11,7 @@ RUN apt-get update && \
|
||||
|
||||
RUN apt-get update && apt-get -y build-dep xorg-server libxfont-dev
|
||||
RUN apt-get update && apt-get -y install cmake git libjpeg-dev libgnutls28-dev vim wget tightvncserver
|
||||
RUN apt-get update && apt-get -y install libjpeg-dev libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev
|
||||
RUN apt-get update && apt-get -y install libjpeg-dev libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev libx264-dev
|
||||
|
||||
# Additions for webp
|
||||
RUN cd /tmp && wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz
|
||||
|
||||
@@ -13,7 +13,7 @@ RUN apt-get update && \
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends tzdata
|
||||
RUN apt-get update && apt-get -y build-dep xorg-server libxfont-dev
|
||||
RUN apt-get update && apt-get -y install cmake git libjpeg-dev libgnutls28-dev vim wget tightvncserver
|
||||
RUN apt-get update && apt-get -y install libjpeg-dev libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev
|
||||
RUN apt-get update && apt-get -y install libjpeg-dev libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev libx264-dev
|
||||
|
||||
# Additions for webp
|
||||
RUN cd /tmp && wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz
|
||||
|
||||
@@ -69,7 +69,7 @@ cd $DST_MAN && ln -s vncpasswd.1 kasmvncpasswd.1;
|
||||
/usr/share/man/man1/*
|
||||
/usr/share/kasmvnc/www
|
||||
|
||||
%license /usr/share/doc/kasmvncserver/LICENCE.TXT
|
||||
%license /usr/share/doc/kasmvncserver/LICENSE.TXT
|
||||
%doc /usr/share/doc/kasmvncserver/README.md
|
||||
|
||||
%changelog
|
||||
|
||||
@@ -76,5 +76,5 @@ endif() #UNIX
|
||||
# Common
|
||||
#
|
||||
|
||||
install(FILES ${CMAKE_SOURCE_DIR}/LICENCE.TXT DESTINATION ${DOC_DIR})
|
||||
install(FILES ${CMAKE_SOURCE_DIR}/LICENSE.TXT DESTINATION ${DOC_DIR})
|
||||
install(FILES ${CMAKE_SOURCE_DIR}/README.md DESTINATION ${DOC_DIR})
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#define errorNumber WSAGetLastError()
|
||||
#define SHUT_RD SD_RECEIVE
|
||||
#define SHUT_WR SD_SEND
|
||||
#define SHUT_RDWR SD_BOTH
|
||||
#else
|
||||
#define errorNumber errno
|
||||
#define closesocket close
|
||||
@@ -94,7 +97,7 @@ Socket::~Socket()
|
||||
void Socket::shutdown()
|
||||
{
|
||||
isShutdown_ = true;
|
||||
::shutdown(getFd(), 2);
|
||||
::shutdown(getFd(), SHUT_RDWR);
|
||||
}
|
||||
|
||||
bool Socket::isShutdown() const
|
||||
@@ -149,7 +152,7 @@ void SocketListener::shutdown()
|
||||
closesocket(fd);
|
||||
fd = -1;
|
||||
#else
|
||||
::shutdown(fd, 2);
|
||||
::shutdown(fd, SHUT_RDWR);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -566,7 +566,7 @@ int parse_handshake(ws_ctx_t *ws_ctx, char *handshake) {
|
||||
headers->key3[0] = '\0';
|
||||
|
||||
if ((strlen(handshake) < 92) || (bcmp(handshake, "GET ", 4) != 0) ||
|
||||
(!strstr(handshake, "Upgrade: websocket"))) {
|
||||
(!strcasestr(handshake, "Upgrade: websocket"))) {
|
||||
return 0;
|
||||
}
|
||||
start = handshake+4;
|
||||
@@ -587,7 +587,7 @@ int parse_handshake(ws_ctx_t *ws_ctx, char *handshake) {
|
||||
if (start) {
|
||||
start += 10;
|
||||
} else {
|
||||
start = strstr(handshake, "\r\nSec-WebSocket-Origin: ");
|
||||
start = strcasestr(handshake, "\r\nSec-WebSocket-Origin: ");
|
||||
if (!start) { return 0; }
|
||||
start += 24;
|
||||
}
|
||||
@@ -595,7 +595,7 @@ int parse_handshake(ws_ctx_t *ws_ctx, char *handshake) {
|
||||
strncpy(headers->origin, start, end-start);
|
||||
headers->origin[end-start] = '\0';
|
||||
|
||||
start = strstr(handshake, "\r\nSec-WebSocket-Version: ");
|
||||
start = strcasestr(handshake, "\r\nSec-WebSocket-Version: ");
|
||||
if (start) {
|
||||
// HyBi/RFC 6455
|
||||
start += 25;
|
||||
@@ -605,7 +605,7 @@ int parse_handshake(ws_ctx_t *ws_ctx, char *handshake) {
|
||||
ws_ctx->hixie = 0;
|
||||
ws_ctx->hybi = strtol(headers->version, NULL, 10);
|
||||
|
||||
start = strstr(handshake, "\r\nSec-WebSocket-Key: ");
|
||||
start = strcasestr(handshake, "\r\nSec-WebSocket-Key: ");
|
||||
if (!start) { return 0; }
|
||||
start += 21;
|
||||
end = strstr(start, "\r\n");
|
||||
@@ -619,7 +619,7 @@ int parse_handshake(ws_ctx_t *ws_ctx, char *handshake) {
|
||||
strncpy(headers->connection, start, end-start);
|
||||
headers->connection[end-start] = '\0';
|
||||
|
||||
start = strstr(handshake, "\r\nSec-WebSocket-Protocol: ");
|
||||
start = strcasestr(handshake, "\r\nSec-WebSocket-Protocol: ");
|
||||
if (!start) { return 0; }
|
||||
start += 26;
|
||||
end = strstr(start, "\r\n");
|
||||
@@ -637,14 +637,14 @@ int parse_handshake(ws_ctx_t *ws_ctx, char *handshake) {
|
||||
strncpy(headers->key3, start, 8);
|
||||
headers->key3[8] = '\0';
|
||||
|
||||
start = strstr(handshake, "\r\nSec-WebSocket-Key1: ");
|
||||
start = strcasestr(handshake, "\r\nSec-WebSocket-Key1: ");
|
||||
if (!start) { return 0; }
|
||||
start += 22;
|
||||
end = strstr(start, "\r\n");
|
||||
strncpy(headers->key1, start, end-start);
|
||||
headers->key1[end-start] = '\0';
|
||||
|
||||
start = strstr(handshake, "\r\nSec-WebSocket-Key2: ");
|
||||
start = strcasestr(handshake, "\r\nSec-WebSocket-Key2: ");
|
||||
if (!start) { return 0; }
|
||||
start += 22;
|
||||
end = strstr(start, "\r\n");
|
||||
@@ -779,7 +779,13 @@ static void dirlisting(ws_ctx_t *ws_ctx, const char fullpath[], const char path[
|
||||
if (!strcmp(names[i]->d_name, ".") || !strcmp(names[i]->d_name, ".."))
|
||||
continue;
|
||||
|
||||
sprintf(buf, "<li><a href=\"%s\">%s</a></li>", names[i]->d_name, names[i]->d_name);
|
||||
if (names[i]->d_type == DT_DIR)
|
||||
sprintf(buf, "<li><a href=\"%s/\">%s/</a></li>", names[i]->d_name,
|
||||
names[i]->d_name);
|
||||
else
|
||||
sprintf(buf, "<li><a href=\"%s\">%s</a></li>", names[i]->d_name,
|
||||
names[i]->d_name);
|
||||
|
||||
ws_send(ws_ctx, buf, strlen(buf));
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include "websocket.h"
|
||||
|
||||
/*
|
||||
@@ -227,9 +228,13 @@ void proxy_handler(ws_ctx_t *ws_ctx) {
|
||||
strcpy(addr.sun_path, ".KasmVNCSock");
|
||||
addr.sun_path[0] = '\0';
|
||||
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
|
||||
struct sockaddr_un myaddr;
|
||||
myaddr.sun_family = AF_UNIX;
|
||||
sprintf(myaddr.sun_path, ".%s@%s", ws_ctx->user, ws_ctx->ip);
|
||||
sprintf(myaddr.sun_path, ".%s@%s_%lu.%lu", ws_ctx->user, ws_ctx->ip,
|
||||
tv.tv_sec, tv.tv_usec);
|
||||
myaddr.sun_path[0] = '\0';
|
||||
|
||||
int tsock = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
|
||||
69
common/rdr/BufferedInStream.cxx
Normal file
69
common/rdr/BufferedInStream.cxx
Normal file
@@ -0,0 +1,69 @@
|
||||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
|
||||
* Copyright 2020 Pierre Ossman for Cendio AB
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this software; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <rdr/BufferedInStream.h>
|
||||
#include <rdr/Exception.h>
|
||||
|
||||
using namespace rdr;
|
||||
|
||||
static const size_t DEFAULT_BUF_SIZE = 8192;
|
||||
|
||||
BufferedInStream::BufferedInStream()
|
||||
: bufSize(DEFAULT_BUF_SIZE), offset(0)
|
||||
{
|
||||
ptr = end = start = new U8[bufSize];
|
||||
}
|
||||
|
||||
BufferedInStream::~BufferedInStream()
|
||||
{
|
||||
delete [] start;
|
||||
}
|
||||
|
||||
size_t BufferedInStream::pos()
|
||||
{
|
||||
return offset + ptr - start;
|
||||
}
|
||||
|
||||
bool BufferedInStream::overrun(size_t needed, bool wait)
|
||||
{
|
||||
if (needed > bufSize)
|
||||
throw Exception("BufferedInStream overrun: "
|
||||
"requested size of %lu bytes exceeds maximum of %lu bytes",
|
||||
(long unsigned)needed, (long unsigned)bufSize);
|
||||
|
||||
// Do we need to shuffle things around?
|
||||
if ((bufSize - (ptr - start)) < needed) {
|
||||
memmove(start, ptr, end - ptr);
|
||||
|
||||
offset += ptr - start;
|
||||
end -= ptr - start;
|
||||
ptr = start;
|
||||
}
|
||||
|
||||
while (avail() < needed) {
|
||||
if (!fillBuffer(start + bufSize - end, wait))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
54
common/rdr/BufferedInStream.h
Normal file
54
common/rdr/BufferedInStream.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
|
||||
* Copyright 2020 Pierre Ossman for Cendio AB
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this software; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
//
|
||||
// Base class for input streams with a buffer
|
||||
//
|
||||
|
||||
#ifndef __RDR_BUFFEREDINSTREAM_H__
|
||||
#define __RDR_BUFFEREDINSTREAM_H__
|
||||
|
||||
#include <rdr/InStream.h>
|
||||
|
||||
namespace rdr {
|
||||
|
||||
class BufferedInStream : public InStream {
|
||||
|
||||
public:
|
||||
virtual ~BufferedInStream();
|
||||
|
||||
virtual size_t pos();
|
||||
|
||||
private:
|
||||
virtual bool fillBuffer(size_t maxSize, bool wait) = 0;
|
||||
|
||||
virtual bool overrun(size_t needed, bool wait);
|
||||
|
||||
private:
|
||||
size_t bufSize;
|
||||
size_t offset;
|
||||
U8* start;
|
||||
|
||||
protected:
|
||||
BufferedInStream();
|
||||
};
|
||||
|
||||
} // end of namespace rdr
|
||||
|
||||
#endif
|
||||
108
common/rdr/BufferedOutStream.cxx
Normal file
108
common/rdr/BufferedOutStream.cxx
Normal file
@@ -0,0 +1,108 @@
|
||||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
|
||||
* Copyright 2011-2020 Pierre Ossman for Cendio AB
|
||||
* Copyright 2017 Peter Astrand <astrand@cendio.se> for Cendio AB
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this software; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <rdr/BufferedOutStream.h>
|
||||
#include <rdr/Exception.h>
|
||||
|
||||
|
||||
using namespace rdr;
|
||||
|
||||
static const size_t DEFAULT_BUF_SIZE = 16384;
|
||||
|
||||
BufferedOutStream::BufferedOutStream()
|
||||
: bufSize(DEFAULT_BUF_SIZE), offset(0)
|
||||
{
|
||||
ptr = start = sentUpTo = new U8[bufSize];
|
||||
end = start + bufSize;
|
||||
}
|
||||
|
||||
BufferedOutStream::~BufferedOutStream()
|
||||
{
|
||||
// FIXME: Complain about non-flushed buffer?
|
||||
delete [] start;
|
||||
}
|
||||
|
||||
size_t BufferedOutStream::length()
|
||||
{
|
||||
return offset + ptr - sentUpTo;
|
||||
}
|
||||
|
||||
size_t BufferedOutStream::bufferUsage()
|
||||
{
|
||||
return ptr - sentUpTo;
|
||||
}
|
||||
|
||||
void BufferedOutStream::flush()
|
||||
{
|
||||
while (sentUpTo < ptr) {
|
||||
size_t len;
|
||||
|
||||
len = bufferUsage();
|
||||
|
||||
if (!flushBuffer(false))
|
||||
break;
|
||||
|
||||
offset += len - bufferUsage();
|
||||
}
|
||||
|
||||
// Managed to flush everything?
|
||||
if (sentUpTo == ptr)
|
||||
ptr = sentUpTo = start;
|
||||
}
|
||||
|
||||
void BufferedOutStream::overrun(size_t needed)
|
||||
{
|
||||
if (needed > bufSize)
|
||||
throw Exception("BufferedOutStream overrun: "
|
||||
"requested size of %lu bytes exceeds maximum of %lu bytes",
|
||||
(long unsigned)needed, (long unsigned)bufSize);
|
||||
|
||||
// First try to get rid of the data we have
|
||||
flush();
|
||||
|
||||
// Still not enough space?
|
||||
while (needed > avail()) {
|
||||
// Can we shuffle things around?
|
||||
// (don't do this if it gains us less than 25%)
|
||||
if (((size_t)(sentUpTo - start) > bufSize / 4) &&
|
||||
(needed < bufSize - (ptr - sentUpTo))) {
|
||||
memmove(start, sentUpTo, ptr - sentUpTo);
|
||||
ptr = start + (ptr - sentUpTo);
|
||||
sentUpTo = start;
|
||||
} else {
|
||||
size_t len;
|
||||
|
||||
len = bufferUsage();
|
||||
|
||||
// Have to get rid of more data, so allow the flush to wait...
|
||||
flushBuffer(true);
|
||||
|
||||
offset += len - bufferUsage();
|
||||
|
||||
// Managed to flush everything?
|
||||
if (sentUpTo == ptr)
|
||||
ptr = sentUpTo = start;
|
||||
}
|
||||
}
|
||||
}
|
||||
65
common/rdr/BufferedOutStream.h
Normal file
65
common/rdr/BufferedOutStream.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
|
||||
* Copyright 2011-2020 Pierre Ossman for Cendio AB
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this software; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
//
|
||||
// Base class for output streams with a buffer
|
||||
//
|
||||
|
||||
#ifndef __RDR_BUFFEREDOUTSTREAM_H__
|
||||
#define __RDR_BUFFEREDOUTSTREAM_H__
|
||||
|
||||
#include <rdr/OutStream.h>
|
||||
|
||||
namespace rdr {
|
||||
|
||||
class BufferedOutStream : public OutStream {
|
||||
|
||||
public:
|
||||
virtual ~BufferedOutStream();
|
||||
|
||||
virtual size_t length();
|
||||
virtual void flush();
|
||||
|
||||
size_t bufferUsage();
|
||||
|
||||
private:
|
||||
// flushBuffer() requests that the stream be flushed. Returns true if it is
|
||||
// able to progress the output (which might still not mean any bytes
|
||||
// actually moved) and can be called again. If wait is true then it will
|
||||
// block until all data has been written.
|
||||
|
||||
virtual bool flushBuffer(bool wait) = 0;
|
||||
|
||||
virtual void overrun(size_t needed);
|
||||
|
||||
private:
|
||||
size_t bufSize;
|
||||
size_t offset;
|
||||
U8* start;
|
||||
|
||||
protected:
|
||||
U8* sentUpTo;
|
||||
|
||||
protected:
|
||||
BufferedOutStream();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,6 +1,8 @@
|
||||
include_directories(${CMAKE_SOURCE_DIR}/common ${ZLIB_INCLUDE_DIRS})
|
||||
|
||||
add_library(rdr STATIC
|
||||
BufferedInStream.cxx
|
||||
BufferedOutStream.cxx
|
||||
Exception.cxx
|
||||
FdInStream.cxx
|
||||
FdOutStream.cxx
|
||||
|
||||
@@ -36,13 +36,6 @@
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifndef vncmin
|
||||
#define vncmin(a,b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
#ifndef vncmax
|
||||
#define vncmax(a,b) (((a) > (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
/* Old systems have select() in sys/time.h */
|
||||
#ifdef HAVE_SYS_SELECT_H
|
||||
#include <sys/select.h>
|
||||
@@ -53,31 +46,22 @@
|
||||
|
||||
using namespace rdr;
|
||||
|
||||
enum { DEFAULT_BUF_SIZE = 8192,
|
||||
MIN_BULK_SIZE = 1024 };
|
||||
enum { DEFAULT_BUF_SIZE = 8192 };
|
||||
|
||||
FdInStream::FdInStream(int fd_, int timeoutms_, size_t bufSize_,
|
||||
FdInStream::FdInStream(int fd_, int timeoutms_,
|
||||
bool closeWhenDone_)
|
||||
: fd(fd_), closeWhenDone(closeWhenDone_),
|
||||
timeoutms(timeoutms_), blockCallback(0),
|
||||
timing(false), timeWaitedIn100us(5), timedKbits(0),
|
||||
bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0)
|
||||
timeoutms(timeoutms_), blockCallback(0)
|
||||
{
|
||||
ptr = end = start = new U8[bufSize];
|
||||
}
|
||||
|
||||
FdInStream::FdInStream(int fd_, FdInStreamBlockCallback* blockCallback_,
|
||||
size_t bufSize_)
|
||||
: fd(fd_), timeoutms(0), blockCallback(blockCallback_),
|
||||
timing(false), timeWaitedIn100us(5), timedKbits(0),
|
||||
bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0)
|
||||
FdInStream::FdInStream(int fd_, FdInStreamBlockCallback* blockCallback_)
|
||||
: fd(fd_), timeoutms(0), blockCallback(blockCallback_)
|
||||
{
|
||||
ptr = end = start = new U8[bufSize];
|
||||
}
|
||||
|
||||
FdInStream::~FdInStream()
|
||||
{
|
||||
delete [] start;
|
||||
if (closeWhenDone) close(fd);
|
||||
}
|
||||
|
||||
@@ -92,72 +76,15 @@ void FdInStream::setBlockCallback(FdInStreamBlockCallback* blockCallback_)
|
||||
timeoutms = 0;
|
||||
}
|
||||
|
||||
size_t FdInStream::pos()
|
||||
|
||||
bool FdInStream::fillBuffer(size_t maxSize, bool wait)
|
||||
{
|
||||
return offset + ptr - start;
|
||||
}
|
||||
size_t n = readWithTimeoutOrCallback((U8*)end, maxSize, wait);
|
||||
if (n == 0)
|
||||
return false;
|
||||
end += n;
|
||||
|
||||
void FdInStream::readBytes(void* data, size_t length)
|
||||
{
|
||||
if (length < MIN_BULK_SIZE) {
|
||||
InStream::readBytes(data, length);
|
||||
return;
|
||||
}
|
||||
|
||||
U8* dataPtr = (U8*)data;
|
||||
|
||||
size_t n = end - ptr;
|
||||
if (n > length) n = length;
|
||||
|
||||
memcpy(dataPtr, ptr, n);
|
||||
dataPtr += n;
|
||||
length -= n;
|
||||
ptr += n;
|
||||
|
||||
while (length > 0) {
|
||||
n = readWithTimeoutOrCallback(dataPtr, length);
|
||||
dataPtr += n;
|
||||
length -= n;
|
||||
offset += n;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
size_t FdInStream::overrun(size_t itemSize, size_t nItems, bool wait)
|
||||
{
|
||||
if (itemSize > bufSize)
|
||||
throw Exception("FdInStream overrun: max itemSize exceeded");
|
||||
|
||||
if (end - ptr != 0)
|
||||
memmove(start, ptr, end - ptr);
|
||||
|
||||
offset += ptr - start;
|
||||
end -= ptr - start;
|
||||
ptr = start;
|
||||
|
||||
size_t bytes_to_read;
|
||||
while ((size_t)(end - start) < itemSize) {
|
||||
bytes_to_read = start + bufSize - end;
|
||||
if (!timing) {
|
||||
// When not timing, we must be careful not to read too much
|
||||
// extra data into the buffer. Otherwise, the line speed
|
||||
// estimation might stay at zero for a long time: All reads
|
||||
// during timing=1 can be satisfied without calling
|
||||
// readWithTimeoutOrCallback. However, reading only 1 or 2 bytes
|
||||
// bytes is ineffecient.
|
||||
bytes_to_read = vncmin(bytes_to_read, vncmax(itemSize*nItems, 8));
|
||||
}
|
||||
size_t n = readWithTimeoutOrCallback((U8*)end, bytes_to_read, wait);
|
||||
if (n == 0) return 0;
|
||||
end += n;
|
||||
}
|
||||
|
||||
size_t nAvail;
|
||||
nAvail = (end - ptr) / itemSize;
|
||||
if (nAvail < nItems)
|
||||
return nAvail;
|
||||
|
||||
return nItems;
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
@@ -175,10 +102,6 @@ size_t FdInStream::overrun(size_t itemSize, size_t nItems, bool wait)
|
||||
|
||||
size_t FdInStream::readWithTimeoutOrCallback(void* buf, size_t len, bool wait)
|
||||
{
|
||||
struct timeval before, after;
|
||||
if (timing)
|
||||
gettimeofday(&before, 0);
|
||||
|
||||
int n;
|
||||
while (true) {
|
||||
do {
|
||||
@@ -215,48 +138,5 @@ size_t FdInStream::readWithTimeoutOrCallback(void* buf, size_t len, bool wait)
|
||||
if (n < 0) throw SystemException("read",errno);
|
||||
if (n == 0) throw EndOfStream();
|
||||
|
||||
if (timing) {
|
||||
gettimeofday(&after, 0);
|
||||
int newTimeWaited = ((after.tv_sec - before.tv_sec) * 10000 +
|
||||
(after.tv_usec - before.tv_usec) / 100);
|
||||
int newKbits = n * 8 / 1000;
|
||||
|
||||
// limit rate to between 10kbit/s and 40Mbit/s
|
||||
|
||||
if (newTimeWaited > newKbits*1000) newTimeWaited = newKbits*1000;
|
||||
if (newTimeWaited < newKbits/4) newTimeWaited = newKbits/4;
|
||||
|
||||
timeWaitedIn100us += newTimeWaited;
|
||||
timedKbits += newKbits;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
void FdInStream::startTiming()
|
||||
{
|
||||
timing = true;
|
||||
|
||||
// Carry over up to 1s worth of previous rate for smoothing.
|
||||
|
||||
if (timeWaitedIn100us > 10000) {
|
||||
timedKbits = timedKbits * 10000 / timeWaitedIn100us;
|
||||
timeWaitedIn100us = 10000;
|
||||
}
|
||||
}
|
||||
|
||||
void FdInStream::stopTiming()
|
||||
{
|
||||
timing = false;
|
||||
if (timeWaitedIn100us < timedKbits/2)
|
||||
timeWaitedIn100us = timedKbits/2; // upper limit 20Mbit/s
|
||||
}
|
||||
|
||||
unsigned int FdInStream::kbitsPerSecond()
|
||||
{
|
||||
// The following calculation will overflow 32-bit arithmetic if we have
|
||||
// received more than about 50Mbytes (400Mbits) since we started timing, so
|
||||
// it should be OK for a single RFB update.
|
||||
|
||||
return timedKbits * 10000 / timeWaitedIn100us;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#ifndef __RDR_FDINSTREAM_H__
|
||||
#define __RDR_FDINSTREAM_H__
|
||||
|
||||
#include <rdr/InStream.h>
|
||||
#include <rdr/BufferedInStream.h>
|
||||
|
||||
namespace rdr {
|
||||
|
||||
@@ -33,31 +33,21 @@ namespace rdr {
|
||||
virtual ~FdInStreamBlockCallback() {}
|
||||
};
|
||||
|
||||
class FdInStream : public InStream {
|
||||
class FdInStream : public BufferedInStream {
|
||||
|
||||
public:
|
||||
|
||||
FdInStream(int fd, int timeoutms=-1, size_t bufSize=0,
|
||||
bool closeWhenDone_=false);
|
||||
FdInStream(int fd, FdInStreamBlockCallback* blockCallback,
|
||||
size_t bufSize=0);
|
||||
FdInStream(int fd, int timeoutms=-1, bool closeWhenDone_=false);
|
||||
FdInStream(int fd, FdInStreamBlockCallback* blockCallback);
|
||||
virtual ~FdInStream();
|
||||
|
||||
void setTimeout(int timeoutms);
|
||||
void setBlockCallback(FdInStreamBlockCallback* blockCallback);
|
||||
int getFd() { return fd; }
|
||||
size_t pos();
|
||||
void readBytes(void* data, size_t length);
|
||||
|
||||
void startTiming();
|
||||
void stopTiming();
|
||||
unsigned int kbitsPerSecond();
|
||||
unsigned int timeWaited() { return timeWaitedIn100us; }
|
||||
|
||||
protected:
|
||||
size_t overrun(size_t itemSize, size_t nItems, bool wait);
|
||||
|
||||
private:
|
||||
virtual bool fillBuffer(size_t maxSize, bool wait);
|
||||
|
||||
size_t readWithTimeoutOrCallback(void* buf, size_t len, bool wait=true);
|
||||
|
||||
int fd;
|
||||
@@ -65,11 +55,6 @@ namespace rdr {
|
||||
int timeoutms;
|
||||
FdInStreamBlockCallback* blockCallback;
|
||||
|
||||
bool timing;
|
||||
unsigned int timeWaitedIn100us;
|
||||
unsigned int timedKbits;
|
||||
|
||||
size_t bufSize;
|
||||
size_t offset;
|
||||
U8* start;
|
||||
};
|
||||
|
||||
@@ -35,6 +35,8 @@
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#endif
|
||||
|
||||
/* Old systems have select() in sys/time.h */
|
||||
@@ -49,26 +51,19 @@
|
||||
|
||||
using namespace rdr;
|
||||
|
||||
enum { DEFAULT_BUF_SIZE = 16384 };
|
||||
|
||||
FdOutStream::FdOutStream(int fd_, bool blocking_, int timeoutms_, size_t bufSize_)
|
||||
: fd(fd_), blocking(blocking_), timeoutms(timeoutms_),
|
||||
bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0)
|
||||
FdOutStream::FdOutStream(int fd_, bool blocking_, int timeoutms_)
|
||||
: fd(fd_), blocking(blocking_), timeoutms(timeoutms_)
|
||||
{
|
||||
ptr = start = sentUpTo = new U8[bufSize];
|
||||
end = start + bufSize;
|
||||
|
||||
gettimeofday(&lastWrite, NULL);
|
||||
}
|
||||
|
||||
FdOutStream::~FdOutStream()
|
||||
{
|
||||
try {
|
||||
blocking = true;
|
||||
flush();
|
||||
while (sentUpTo != ptr)
|
||||
flushBuffer(true);
|
||||
} catch (Exception&) {
|
||||
}
|
||||
delete [] start;
|
||||
}
|
||||
|
||||
void FdOutStream::setTimeout(int timeoutms_) {
|
||||
@@ -79,82 +74,29 @@ void FdOutStream::setBlocking(bool blocking_) {
|
||||
blocking = blocking_;
|
||||
}
|
||||
|
||||
size_t FdOutStream::length()
|
||||
{
|
||||
return offset + ptr - sentUpTo;
|
||||
}
|
||||
|
||||
int FdOutStream::bufferUsage()
|
||||
{
|
||||
return ptr - sentUpTo;
|
||||
}
|
||||
|
||||
unsigned FdOutStream::getIdleTime()
|
||||
{
|
||||
return rfb::msSince(&lastWrite);
|
||||
}
|
||||
|
||||
void FdOutStream::flush()
|
||||
bool FdOutStream::flushBuffer(bool wait)
|
||||
{
|
||||
while (sentUpTo < ptr) {
|
||||
size_t n = writeWithTimeout((const void*) sentUpTo,
|
||||
ptr - sentUpTo,
|
||||
blocking? timeoutms : 0);
|
||||
size_t n = writeWithTimeout((const void*) sentUpTo,
|
||||
ptr - sentUpTo,
|
||||
(blocking || wait)? timeoutms : 0);
|
||||
|
||||
// Timeout?
|
||||
if (n == 0) {
|
||||
// If non-blocking then we're done here
|
||||
if (!blocking)
|
||||
break;
|
||||
// Timeout?
|
||||
if (n == 0) {
|
||||
// If non-blocking then we're done here
|
||||
if (!blocking && !wait)
|
||||
return false;
|
||||
|
||||
throw TimedOut();
|
||||
}
|
||||
|
||||
sentUpTo += n;
|
||||
offset += n;
|
||||
throw TimedOut();
|
||||
}
|
||||
|
||||
// Managed to flush everything?
|
||||
if (sentUpTo == ptr)
|
||||
ptr = sentUpTo = start;
|
||||
}
|
||||
sentUpTo += n;
|
||||
|
||||
|
||||
size_t FdOutStream::overrun(size_t itemSize, size_t nItems)
|
||||
{
|
||||
if (itemSize > bufSize)
|
||||
throw Exception("FdOutStream overrun: max itemSize exceeded");
|
||||
|
||||
// First try to get rid of the data we have
|
||||
flush();
|
||||
|
||||
// Still not enough space?
|
||||
if (itemSize > (size_t)(end - ptr)) {
|
||||
// Can we shuffle things around?
|
||||
// (don't do this if it gains us less than 25%)
|
||||
if (((size_t)(sentUpTo - start) > bufSize / 4) &&
|
||||
(itemSize < bufSize - (ptr - sentUpTo))) {
|
||||
memmove(start, sentUpTo, ptr - sentUpTo);
|
||||
ptr = start + (ptr - sentUpTo);
|
||||
sentUpTo = start;
|
||||
} else {
|
||||
// Have to get rid of more data, so turn off non-blocking
|
||||
// for a bit...
|
||||
bool realBlocking;
|
||||
|
||||
realBlocking = blocking;
|
||||
blocking = true;
|
||||
flush();
|
||||
blocking = realBlocking;
|
||||
}
|
||||
}
|
||||
|
||||
size_t nAvail;
|
||||
nAvail = (end - ptr) / itemSize;
|
||||
if (nAvail < nItems)
|
||||
return nAvail;
|
||||
|
||||
return nItems;
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -26,38 +26,29 @@
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <rdr/OutStream.h>
|
||||
#include <rdr/BufferedOutStream.h>
|
||||
|
||||
namespace rdr {
|
||||
|
||||
class FdOutStream : public OutStream {
|
||||
class FdOutStream : public BufferedOutStream {
|
||||
|
||||
public:
|
||||
|
||||
FdOutStream(int fd, bool blocking=true, int timeoutms=-1, size_t bufSize=0);
|
||||
FdOutStream(int fd, bool blocking=true, int timeoutms=-1);
|
||||
virtual ~FdOutStream();
|
||||
|
||||
void setTimeout(int timeoutms);
|
||||
void setBlocking(bool blocking);
|
||||
int getFd() { return fd; }
|
||||
|
||||
void flush();
|
||||
size_t length();
|
||||
|
||||
int bufferUsage();
|
||||
|
||||
unsigned getIdleTime();
|
||||
|
||||
private:
|
||||
size_t overrun(size_t itemSize, size_t nItems);
|
||||
virtual bool flushBuffer(bool wait);
|
||||
size_t writeWithTimeout(const void* data, size_t length, int timeoutms);
|
||||
int fd;
|
||||
bool blocking;
|
||||
int timeoutms;
|
||||
size_t bufSize;
|
||||
size_t offset;
|
||||
U8* start;
|
||||
U8* sentUpTo;
|
||||
struct timeval lastWrite;
|
||||
};
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@ FileInStream::FileInStream(const char *fileName)
|
||||
file = fopen(fileName, "rb");
|
||||
if (!file)
|
||||
throw SystemException("fopen", errno);
|
||||
ptr = end = b;
|
||||
}
|
||||
|
||||
FileInStream::~FileInStream(void) {
|
||||
@@ -40,50 +39,17 @@ FileInStream::~FileInStream(void) {
|
||||
}
|
||||
}
|
||||
|
||||
void FileInStream::reset(void) {
|
||||
if (!file)
|
||||
throw Exception("File is not open");
|
||||
if (fseek(file, 0, SEEK_SET) != 0)
|
||||
throw SystemException("fseek", errno);
|
||||
ptr = end = b;
|
||||
}
|
||||
|
||||
size_t FileInStream::pos()
|
||||
bool FileInStream::fillBuffer(size_t maxSize, bool wait)
|
||||
{
|
||||
if (!file)
|
||||
throw Exception("File is not open");
|
||||
|
||||
return ftell(file) + ptr - b;
|
||||
}
|
||||
|
||||
size_t FileInStream::overrun(size_t itemSize, size_t nItems, bool wait)
|
||||
{
|
||||
if (itemSize > sizeof(b))
|
||||
throw Exception("FileInStream overrun: max itemSize exceeded");
|
||||
|
||||
if (end - ptr != 0)
|
||||
memmove(b, ptr, end - ptr);
|
||||
|
||||
end -= ptr - b;
|
||||
ptr = b;
|
||||
|
||||
|
||||
while ((size_t)(end - b) < itemSize) {
|
||||
size_t n = fread((U8 *)end, b + sizeof(b) - end, 1, file);
|
||||
if (n == 0) {
|
||||
if (ferror(file))
|
||||
throw SystemException("fread", errno);
|
||||
if (feof(file))
|
||||
throw EndOfStream();
|
||||
return 0;
|
||||
}
|
||||
end += b + sizeof(b) - end;
|
||||
size_t n = fread((U8 *)end, 1, maxSize, file);
|
||||
if (n == 0) {
|
||||
if (ferror(file))
|
||||
throw SystemException("fread", errno);
|
||||
if (feof(file))
|
||||
throw EndOfStream();
|
||||
return false;
|
||||
}
|
||||
end += n;
|
||||
|
||||
size_t nAvail;
|
||||
nAvail = (end - ptr) / itemSize;
|
||||
if (nAvail < nItems)
|
||||
return nAvail;
|
||||
|
||||
return nItems;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -22,26 +22,21 @@
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <rdr/InStream.h>
|
||||
#include <rdr/BufferedInStream.h>
|
||||
|
||||
namespace rdr {
|
||||
|
||||
class FileInStream : public InStream {
|
||||
class FileInStream : public BufferedInStream {
|
||||
|
||||
public:
|
||||
|
||||
FileInStream(const char *fileName);
|
||||
~FileInStream(void);
|
||||
|
||||
void reset(void);
|
||||
|
||||
size_t pos();
|
||||
|
||||
protected:
|
||||
size_t overrun(size_t itemSize, size_t nItems, bool wait = true);
|
||||
private:
|
||||
virtual bool fillBuffer(size_t maxSize, bool wait);
|
||||
|
||||
private:
|
||||
U8 b[131072];
|
||||
FILE *file;
|
||||
};
|
||||
|
||||
|
||||
@@ -24,18 +24,14 @@
|
||||
|
||||
using namespace rdr;
|
||||
|
||||
const int DEFAULT_BUF_LEN = 16384;
|
||||
|
||||
static inline int min(int a, int b) {return a<b ? a : b;}
|
||||
|
||||
HexInStream::HexInStream(InStream& is, size_t bufSize_)
|
||||
: bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_LEN), offset(0), in_stream(is)
|
||||
HexInStream::HexInStream(InStream& is)
|
||||
: in_stream(is)
|
||||
{
|
||||
ptr = end = start = new U8[bufSize];
|
||||
}
|
||||
|
||||
HexInStream::~HexInStream() {
|
||||
delete [] start;
|
||||
}
|
||||
|
||||
|
||||
@@ -76,44 +72,24 @@ decodeError:
|
||||
}
|
||||
|
||||
|
||||
size_t HexInStream::pos() {
|
||||
return offset + ptr - start;
|
||||
}
|
||||
bool HexInStream::fillBuffer(size_t maxSize, bool wait) {
|
||||
if (!in_stream.check(2, wait))
|
||||
return false;
|
||||
|
||||
size_t HexInStream::overrun(size_t itemSize, size_t nItems, bool wait) {
|
||||
if (itemSize > bufSize)
|
||||
throw Exception("HexInStream overrun: max itemSize exceeded");
|
||||
const U8* iptr = in_stream.getptr();
|
||||
const U8* eptr = in_stream.getend();
|
||||
size_t length = min((eptr - iptr)/2, maxSize);
|
||||
|
||||
if (end - ptr != 0)
|
||||
memmove(start, ptr, end - ptr);
|
||||
|
||||
end -= ptr - start;
|
||||
offset += ptr - start;
|
||||
ptr = start;
|
||||
|
||||
while ((size_t)(end - ptr) < itemSize) {
|
||||
size_t n = in_stream.check(2, 1, wait);
|
||||
if (n == 0) return 0;
|
||||
const U8* iptr = in_stream.getptr();
|
||||
const U8* eptr = in_stream.getend();
|
||||
size_t length = min((eptr - iptr)/2, start + bufSize - end);
|
||||
|
||||
U8* optr = (U8*) end;
|
||||
for (size_t i=0; i<length; i++) {
|
||||
int v = 0;
|
||||
readHexAndShift(iptr[i*2], &v);
|
||||
readHexAndShift(iptr[i*2+1], &v);
|
||||
optr[i] = v;
|
||||
}
|
||||
|
||||
in_stream.setptr(iptr + length*2);
|
||||
end += length;
|
||||
U8* optr = (U8*) end;
|
||||
for (size_t i=0; i<length; i++) {
|
||||
int v = 0;
|
||||
readHexAndShift(iptr[i*2], &v);
|
||||
readHexAndShift(iptr[i*2+1], &v);
|
||||
optr[i] = v;
|
||||
}
|
||||
|
||||
size_t nAvail;
|
||||
nAvail = (end - ptr) / itemSize;
|
||||
if (nAvail < nItems)
|
||||
return nAvail;
|
||||
in_stream.setptr(iptr + length*2);
|
||||
end += length;
|
||||
|
||||
return nItems;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -19,29 +19,23 @@
|
||||
#ifndef __RDR_HEX_INSTREAM_H__
|
||||
#define __RDR_HEX_INSTREAM_H__
|
||||
|
||||
#include <rdr/InStream.h>
|
||||
#include <rdr/BufferedInStream.h>
|
||||
|
||||
namespace rdr {
|
||||
|
||||
class HexInStream : public InStream {
|
||||
class HexInStream : public BufferedInStream {
|
||||
public:
|
||||
|
||||
HexInStream(InStream& is, size_t bufSize=0);
|
||||
HexInStream(InStream& is);
|
||||
virtual ~HexInStream();
|
||||
|
||||
size_t pos();
|
||||
|
||||
static bool readHexAndShift(char c, int* v);
|
||||
static bool hexStrToBin(const char* s, char** data, size_t* length);
|
||||
|
||||
protected:
|
||||
size_t overrun(size_t itemSize, size_t nItems, bool wait);
|
||||
private:
|
||||
virtual bool fillBuffer(size_t maxSize, bool wait);
|
||||
|
||||
private:
|
||||
size_t bufSize;
|
||||
U8* start;
|
||||
size_t offset;
|
||||
|
||||
InStream& in_stream;
|
||||
};
|
||||
|
||||
|
||||
@@ -25,8 +25,8 @@ const int DEFAULT_BUF_LEN = 16384;
|
||||
|
||||
static inline size_t min(size_t a, size_t b) {return a<b ? a : b;}
|
||||
|
||||
HexOutStream::HexOutStream(OutStream& os, size_t buflen)
|
||||
: out_stream(os), offset(0), bufSize(buflen ? buflen : DEFAULT_BUF_LEN)
|
||||
HexOutStream::HexOutStream(OutStream& os)
|
||||
: out_stream(os), offset(0), bufSize(DEFAULT_BUF_LEN)
|
||||
{
|
||||
if (bufSize % 2)
|
||||
bufSize--;
|
||||
@@ -95,18 +95,10 @@ HexOutStream::flush() {
|
||||
out_stream.flush();
|
||||
}
|
||||
|
||||
size_t
|
||||
HexOutStream::overrun(size_t itemSize, size_t nItems) {
|
||||
if (itemSize > bufSize)
|
||||
throw Exception("HexOutStream overrun: max itemSize exceeded");
|
||||
void HexOutStream::overrun(size_t needed) {
|
||||
if (needed > bufSize)
|
||||
throw Exception("HexOutStream overrun: buffer size exceeded");
|
||||
|
||||
writeBuffer();
|
||||
|
||||
size_t nAvail;
|
||||
nAvail = (end - ptr) / itemSize;
|
||||
if (nAvail < nItems)
|
||||
return nAvail;
|
||||
|
||||
return nItems;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace rdr {
|
||||
class HexOutStream : public OutStream {
|
||||
public:
|
||||
|
||||
HexOutStream(OutStream& os, size_t buflen=0);
|
||||
HexOutStream(OutStream& os);
|
||||
virtual ~HexOutStream();
|
||||
|
||||
void flush();
|
||||
@@ -37,7 +37,7 @@ namespace rdr {
|
||||
|
||||
private:
|
||||
void writeBuffer();
|
||||
size_t overrun(size_t itemSize, size_t nItems);
|
||||
virtual void overrun(size_t needed);
|
||||
|
||||
OutStream& out_stream;
|
||||
|
||||
|
||||
@@ -35,28 +35,25 @@ namespace rdr {
|
||||
|
||||
virtual ~InStream() {}
|
||||
|
||||
// check() ensures there is buffer data for at least one item of size
|
||||
// itemSize bytes. Returns the number of items in the buffer (up to a
|
||||
// maximum of nItems). If wait is false, then instead of blocking to wait
|
||||
// for the bytes, zero is returned if the bytes are not immediately
|
||||
// available. If itemSize or nItems is zero, check() will return zero.
|
||||
// avail() returns the number of bytes that are currenctly directly
|
||||
// available from the stream.
|
||||
|
||||
inline size_t check(size_t itemSize, size_t nItems=1, bool wait=true)
|
||||
inline size_t avail()
|
||||
{
|
||||
size_t nAvail;
|
||||
return end - ptr;
|
||||
}
|
||||
|
||||
if (itemSize == 0 || nItems == 0)
|
||||
return 0;
|
||||
// check() ensures there is buffer data for at least needed bytes. Returns
|
||||
// true once the data is available. If wait is false, then instead of
|
||||
// blocking to wait for the bytes, false is returned if the bytes are not
|
||||
// immediately available.
|
||||
|
||||
if (itemSize > (size_t)(end - ptr))
|
||||
return overrun(itemSize, nItems, wait);
|
||||
inline size_t check(size_t needed, bool wait=true)
|
||||
{
|
||||
if (needed > avail())
|
||||
return overrun(needed, wait);
|
||||
|
||||
// itemSize cannot be zero at this point
|
||||
nAvail = (end - ptr) / itemSize;
|
||||
if (nAvail < nItems)
|
||||
return nAvail;
|
||||
|
||||
return nItems;
|
||||
return true;
|
||||
}
|
||||
|
||||
// checkNoWait() tries to make sure that the given number of bytes can
|
||||
@@ -64,10 +61,7 @@ namespace rdr {
|
||||
// otherwise. The length must be "small" (less than the buffer size).
|
||||
// If length is zero, checkNoWait() will return true.
|
||||
|
||||
inline bool checkNoWait(size_t length)
|
||||
{
|
||||
return length == 0 || check(length, 1, false) > 0;
|
||||
}
|
||||
inline bool checkNoWait(size_t length) { return check(length, false); }
|
||||
|
||||
// readU/SN() methods read unsigned and signed N-bit integers.
|
||||
|
||||
@@ -138,13 +132,12 @@ namespace rdr {
|
||||
private:
|
||||
|
||||
// overrun() is implemented by a derived class to cope with buffer overrun.
|
||||
// It ensures there are at least itemSize bytes of buffer data. Returns
|
||||
// the number of items in the buffer (up to a maximum of nItems). itemSize
|
||||
// is supposed to be "small" (a few bytes). If wait is false, then
|
||||
// instead of blocking to wait for the bytes, zero is returned if the bytes
|
||||
// are not immediately available.
|
||||
// It ensures there are at least needed bytes of buffer data. Returns true
|
||||
// once the data is available. If wait is false, then instead of blocking
|
||||
// to wait for the bytes, false is returned if the bytes are not
|
||||
// immediately available.
|
||||
|
||||
virtual size_t overrun(size_t itemSize, size_t nItems, bool wait=true) = 0;
|
||||
virtual bool overrun(size_t needed, bool wait=true) = 0;
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace rdr {
|
||||
|
||||
private:
|
||||
|
||||
size_t overrun(size_t itemSize, size_t nItems, bool wait) { throw EndOfStream(); }
|
||||
bool overrun(size_t needed, bool wait) { throw EndOfStream(); }
|
||||
const U8* start;
|
||||
bool deleteWhenDone;
|
||||
};
|
||||
|
||||
@@ -41,12 +41,6 @@ namespace rdr {
|
||||
delete [] start;
|
||||
}
|
||||
|
||||
void writeBytes(const void* data, size_t length) {
|
||||
check(length);
|
||||
memcpy(ptr, data, length);
|
||||
ptr += length;
|
||||
}
|
||||
|
||||
size_t length() { return ptr - start; }
|
||||
void clear() { ptr = start; };
|
||||
void clearAndZero() { memset(start, 0, ptr-start); clear(); }
|
||||
@@ -58,11 +52,11 @@ namespace rdr {
|
||||
|
||||
protected:
|
||||
|
||||
// overrun() either doubles the buffer or adds enough space for nItems of
|
||||
// size itemSize bytes.
|
||||
// overrun() either doubles the buffer or adds enough space for
|
||||
// needed bytes.
|
||||
|
||||
size_t overrun(size_t itemSize, size_t nItems) {
|
||||
size_t len = ptr - start + itemSize * nItems;
|
||||
virtual void overrun(size_t needed) {
|
||||
size_t len = ptr - start + needed;
|
||||
if (len < (size_t)(end - start) * 2)
|
||||
len = (end - start) * 2;
|
||||
|
||||
@@ -75,8 +69,6 @@ namespace rdr {
|
||||
delete [] start;
|
||||
start = newStart;
|
||||
end = newStart + len;
|
||||
|
||||
return nItems;
|
||||
}
|
||||
|
||||
U8* start;
|
||||
|
||||
@@ -40,22 +40,20 @@ namespace rdr {
|
||||
|
||||
virtual ~OutStream() {}
|
||||
|
||||
// check() ensures there is buffer space for at least one item of size
|
||||
// itemSize bytes. Returns the number of items which fit (up to a maximum
|
||||
// of nItems).
|
||||
// avail() returns the number of bytes that currently be written to the
|
||||
// stream without any risk of blocking.
|
||||
|
||||
inline size_t check(size_t itemSize, size_t nItems=1)
|
||||
inline size_t avail()
|
||||
{
|
||||
size_t nAvail;
|
||||
return end - ptr;
|
||||
}
|
||||
|
||||
if (itemSize > (size_t)(end - ptr))
|
||||
return overrun(itemSize, nItems);
|
||||
// check() ensures there is buffer space for at least needed bytes.
|
||||
|
||||
nAvail = (end - ptr) / itemSize;
|
||||
if (nAvail < nItems)
|
||||
return nAvail;
|
||||
|
||||
return nItems;
|
||||
inline void check(size_t needed)
|
||||
{
|
||||
if (needed > avail())
|
||||
overrun(needed);
|
||||
}
|
||||
|
||||
// writeU/SN() methods write unsigned and signed N-bit integers.
|
||||
@@ -83,19 +81,14 @@ namespace rdr {
|
||||
while (bytes-- > 0) writeU8(0);
|
||||
}
|
||||
|
||||
inline void skip(size_t bytes) {
|
||||
while (bytes > 0) {
|
||||
size_t n = check(1, bytes);
|
||||
ptr += n;
|
||||
bytes -= n;
|
||||
}
|
||||
}
|
||||
|
||||
// writeBytes() writes an exact number of bytes.
|
||||
|
||||
void writeBytes(const void* data, size_t length) {
|
||||
while (length > 0) {
|
||||
size_t n = check(1, length);
|
||||
check(1);
|
||||
size_t n = length;
|
||||
if (length > avail())
|
||||
n = avail();
|
||||
memcpy(ptr, data, n);
|
||||
ptr += n;
|
||||
data = (U8*)data + n;
|
||||
@@ -107,7 +100,10 @@ namespace rdr {
|
||||
|
||||
void copyBytes(InStream* is, size_t length) {
|
||||
while (length > 0) {
|
||||
size_t n = check(1, length);
|
||||
check(1);
|
||||
size_t n = length;
|
||||
if (length > avail())
|
||||
n = avail();
|
||||
is->readBytes(ptr, n);
|
||||
ptr += n;
|
||||
length -= n;
|
||||
@@ -143,11 +139,9 @@ namespace rdr {
|
||||
private:
|
||||
|
||||
// overrun() is implemented by a derived class to cope with buffer overrun.
|
||||
// It ensures there are at least itemSize bytes of buffer space. Returns
|
||||
// the number of items which fit (up to a maximum of nItems). itemSize is
|
||||
// supposed to be "small" (a few bytes).
|
||||
// It ensures there are at least needed bytes of buffer space.
|
||||
|
||||
virtual size_t overrun(size_t itemSize, size_t nItems) = 0;
|
||||
virtual void overrun(size_t needed) = 0;
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
@@ -32,15 +32,10 @@
|
||||
|
||||
using namespace rdr;
|
||||
|
||||
const size_t DEFAULT_BUF_LEN = 256;
|
||||
|
||||
unsigned int RandomStream::seed;
|
||||
|
||||
RandomStream::RandomStream()
|
||||
: offset(0)
|
||||
{
|
||||
ptr = end = start = new U8[DEFAULT_BUF_LEN];
|
||||
|
||||
#ifdef RFB_HAVE_WINCRYPT
|
||||
provider = 0;
|
||||
if (!CryptAcquireContext(&provider, 0, 0, PROV_RSA_FULL, 0)) {
|
||||
@@ -72,8 +67,6 @@ RandomStream::RandomStream()
|
||||
}
|
||||
|
||||
RandomStream::~RandomStream() {
|
||||
delete [] start;
|
||||
|
||||
#ifdef RFB_HAVE_WINCRYPT
|
||||
if (provider)
|
||||
CryptReleaseContext(provider, 0);
|
||||
@@ -83,50 +76,29 @@ RandomStream::~RandomStream() {
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t RandomStream::pos() {
|
||||
return offset + ptr - start;
|
||||
}
|
||||
|
||||
size_t RandomStream::overrun(size_t itemSize, size_t nItems, bool wait) {
|
||||
if (itemSize > DEFAULT_BUF_LEN)
|
||||
throw Exception("RandomStream overrun: max itemSize exceeded");
|
||||
|
||||
if (end - ptr != 0)
|
||||
memmove(start, ptr, end - ptr);
|
||||
|
||||
end -= ptr - start;
|
||||
offset += ptr - start;
|
||||
ptr = start;
|
||||
|
||||
size_t length = start + DEFAULT_BUF_LEN - end;
|
||||
|
||||
bool RandomStream::fillBuffer(size_t maxSize, bool wait) {
|
||||
#ifdef RFB_HAVE_WINCRYPT
|
||||
if (provider) {
|
||||
if (!CryptGenRandom(provider, length, (U8*)end))
|
||||
if (!CryptGenRandom(provider, maxSize, (U8*)end))
|
||||
throw rdr::SystemException("unable to CryptGenRandom", GetLastError());
|
||||
end += length;
|
||||
end += maxSize;
|
||||
} else {
|
||||
#else
|
||||
#ifndef WIN32
|
||||
if (fp) {
|
||||
size_t n = fread((U8*)end, length, 1, fp);
|
||||
if (n != 1)
|
||||
size_t n = fread((U8*)end, 1, maxSize, fp);
|
||||
if (n <= 0)
|
||||
throw rdr::SystemException("reading /dev/urandom or /dev/random failed",
|
||||
errno);
|
||||
end += length;
|
||||
end += n;
|
||||
} else {
|
||||
#else
|
||||
{
|
||||
#endif
|
||||
#endif
|
||||
for (size_t i=0; i<length; i++)
|
||||
for (size_t i=0; i<maxSize; i++)
|
||||
*(U8*)end++ = (int) (256.0*rand()/(RAND_MAX+1.0));
|
||||
}
|
||||
|
||||
size_t nAvail;
|
||||
nAvail = (end - ptr) / itemSize;
|
||||
if (nAvail < nItems)
|
||||
return nAvail;
|
||||
|
||||
return nItems;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#define __RDR_RANDOMSTREAM_H__
|
||||
|
||||
#include <stdio.h>
|
||||
#include <rdr/InStream.h>
|
||||
#include <rdr/BufferedInStream.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
@@ -32,22 +32,17 @@
|
||||
|
||||
namespace rdr {
|
||||
|
||||
class RandomStream : public InStream {
|
||||
class RandomStream : public BufferedInStream {
|
||||
|
||||
public:
|
||||
|
||||
RandomStream();
|
||||
virtual ~RandomStream();
|
||||
|
||||
size_t pos();
|
||||
|
||||
protected:
|
||||
size_t overrun(size_t itemSize, size_t nItems, bool wait);
|
||||
private:
|
||||
virtual bool fillBuffer(size_t maxSize, bool wait);
|
||||
|
||||
private:
|
||||
U8* start;
|
||||
size_t offset;
|
||||
|
||||
static unsigned int seed;
|
||||
#ifdef RFB_HAVE_WINCRYPT
|
||||
HCRYPTPROV provider;
|
||||
|
||||
@@ -30,21 +30,19 @@
|
||||
#ifdef HAVE_GNUTLS
|
||||
using namespace rdr;
|
||||
|
||||
enum { DEFAULT_BUF_SIZE = 16384 };
|
||||
|
||||
ssize_t TLSInStream::pull(gnutls_transport_ptr_t str, void* data, size_t size)
|
||||
{
|
||||
TLSInStream* self= (TLSInStream*) str;
|
||||
InStream *in = self->in;
|
||||
|
||||
try {
|
||||
if (!in->check(1, 1, false)) {
|
||||
if (!in->check(1, false)) {
|
||||
gnutls_transport_set_errno(self->session, EAGAIN);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((size_t)(in->getend() - in->getptr()) < size)
|
||||
size = in->getend() - in->getptr();
|
||||
if (in->avail() < size)
|
||||
size = in->avail();
|
||||
|
||||
in->readBytes(data, size);
|
||||
|
||||
@@ -57,12 +55,10 @@ ssize_t TLSInStream::pull(gnutls_transport_ptr_t str, void* data, size_t size)
|
||||
}
|
||||
|
||||
TLSInStream::TLSInStream(InStream* _in, gnutls_session_t _session)
|
||||
: session(_session), in(_in), bufSize(DEFAULT_BUF_SIZE), offset(0)
|
||||
: session(_session), in(_in)
|
||||
{
|
||||
gnutls_transport_ptr_t recv, send;
|
||||
|
||||
ptr = end = start = new U8[bufSize];
|
||||
|
||||
gnutls_transport_set_pull_function(session, pull);
|
||||
gnutls_transport_get_ptr2(session, &recv, &send);
|
||||
gnutls_transport_set_ptr2(session, this, send);
|
||||
@@ -71,40 +67,16 @@ TLSInStream::TLSInStream(InStream* _in, gnutls_session_t _session)
|
||||
TLSInStream::~TLSInStream()
|
||||
{
|
||||
gnutls_transport_set_pull_function(session, NULL);
|
||||
|
||||
delete[] start;
|
||||
}
|
||||
|
||||
size_t TLSInStream::pos()
|
||||
bool TLSInStream::fillBuffer(size_t maxSize, bool wait)
|
||||
{
|
||||
return offset + ptr - start;
|
||||
}
|
||||
size_t n = readTLS((U8*) end, maxSize, wait);
|
||||
if (!wait && n == 0)
|
||||
return false;
|
||||
end += n;
|
||||
|
||||
size_t TLSInStream::overrun(size_t itemSize, size_t nItems, bool wait)
|
||||
{
|
||||
if (itemSize > bufSize)
|
||||
throw Exception("TLSInStream overrun: max itemSize exceeded");
|
||||
|
||||
if (end - ptr != 0)
|
||||
memmove(start, ptr, end - ptr);
|
||||
|
||||
offset += ptr - start;
|
||||
end -= ptr - start;
|
||||
ptr = start;
|
||||
|
||||
while ((size_t)(end - start) < itemSize) {
|
||||
size_t n = readTLS((U8*) end, start + bufSize - end, wait);
|
||||
if (!wait && n == 0)
|
||||
return 0;
|
||||
end += n;
|
||||
}
|
||||
|
||||
size_t nAvail;
|
||||
nAvail = (end - ptr) / itemSize;
|
||||
if (nAvail < nItems)
|
||||
return nAvail;
|
||||
|
||||
return nItems;
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t TLSInStream::readTLS(U8* buf, size_t len, bool wait)
|
||||
@@ -112,7 +84,7 @@ size_t TLSInStream::readTLS(U8* buf, size_t len, bool wait)
|
||||
int n;
|
||||
|
||||
if (gnutls_record_check_pending(session) == 0) {
|
||||
n = in->check(1, 1, wait);
|
||||
n = in->check(1, wait);
|
||||
if (n == 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -27,27 +27,22 @@
|
||||
#ifdef HAVE_GNUTLS
|
||||
|
||||
#include <gnutls/gnutls.h>
|
||||
#include <rdr/InStream.h>
|
||||
#include <rdr/BufferedInStream.h>
|
||||
|
||||
namespace rdr {
|
||||
|
||||
class TLSInStream : public InStream {
|
||||
class TLSInStream : public BufferedInStream {
|
||||
public:
|
||||
TLSInStream(InStream* in, gnutls_session_t session);
|
||||
virtual ~TLSInStream();
|
||||
|
||||
size_t pos();
|
||||
|
||||
private:
|
||||
size_t overrun(size_t itemSize, size_t nItems, bool wait);
|
||||
virtual bool fillBuffer(size_t maxSize, bool wait);
|
||||
size_t readTLS(U8* buf, size_t len, bool wait);
|
||||
static ssize_t pull(gnutls_transport_ptr_t str, void* data, size_t size);
|
||||
|
||||
gnutls_session_t session;
|
||||
InStream* in;
|
||||
size_t bufSize;
|
||||
size_t offset;
|
||||
U8* start;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -93,19 +93,12 @@ void TLSOutStream::flush()
|
||||
out->flush();
|
||||
}
|
||||
|
||||
size_t TLSOutStream::overrun(size_t itemSize, size_t nItems)
|
||||
void TLSOutStream::overrun(size_t needed)
|
||||
{
|
||||
if (itemSize > bufSize)
|
||||
throw Exception("TLSOutStream overrun: max itemSize exceeded");
|
||||
if (needed > bufSize)
|
||||
throw Exception("TLSOutStream overrun: buffer size exceeded");
|
||||
|
||||
flush();
|
||||
|
||||
size_t nAvail;
|
||||
nAvail = (end - ptr) / itemSize;
|
||||
if (nAvail < nItems)
|
||||
return nAvail;
|
||||
|
||||
return nItems;
|
||||
}
|
||||
|
||||
size_t TLSOutStream::writeTLS(const U8* data, size_t length)
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace rdr {
|
||||
size_t length();
|
||||
|
||||
protected:
|
||||
size_t overrun(size_t itemSize, size_t nItems);
|
||||
virtual void overrun(size_t needed);
|
||||
|
||||
private:
|
||||
size_t writeTLS(const U8* data, size_t length);
|
||||
|
||||
@@ -24,41 +24,30 @@
|
||||
|
||||
using namespace rdr;
|
||||
|
||||
enum { DEFAULT_BUF_SIZE = 16384 };
|
||||
|
||||
ZlibInStream::ZlibInStream(size_t bufSize_)
|
||||
: underlying(0), bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0),
|
||||
zs(NULL), bytesIn(0)
|
||||
ZlibInStream::ZlibInStream()
|
||||
: underlying(0), zs(NULL), bytesIn(0)
|
||||
{
|
||||
ptr = end = start = new U8[bufSize];
|
||||
init();
|
||||
}
|
||||
|
||||
ZlibInStream::~ZlibInStream()
|
||||
{
|
||||
deinit();
|
||||
delete [] start;
|
||||
}
|
||||
|
||||
void ZlibInStream::setUnderlying(InStream* is, size_t bytesIn_)
|
||||
{
|
||||
underlying = is;
|
||||
bytesIn = bytesIn_;
|
||||
ptr = end = start;
|
||||
}
|
||||
|
||||
size_t ZlibInStream::pos()
|
||||
{
|
||||
return offset + ptr - start;
|
||||
skip(avail());
|
||||
}
|
||||
|
||||
void ZlibInStream::flushUnderlying()
|
||||
{
|
||||
ptr = end = start;
|
||||
|
||||
while (bytesIn > 0) {
|
||||
decompress(true);
|
||||
end = start; // throw away any data
|
||||
if (!check(1))
|
||||
throw Exception("ZlibInStream: failed to flush remaining stream data");
|
||||
skip(avail());
|
||||
}
|
||||
|
||||
setUnderlying(NULL, 0);
|
||||
@@ -96,47 +85,18 @@ void ZlibInStream::deinit()
|
||||
zs = NULL;
|
||||
}
|
||||
|
||||
size_t ZlibInStream::overrun(size_t itemSize, size_t nItems, bool wait)
|
||||
{
|
||||
if (itemSize > bufSize)
|
||||
throw Exception("ZlibInStream overrun: max itemSize exceeded");
|
||||
|
||||
if (end - ptr != 0)
|
||||
memmove(start, ptr, end - ptr);
|
||||
|
||||
offset += ptr - start;
|
||||
end -= ptr - start;
|
||||
ptr = start;
|
||||
|
||||
while ((size_t)(end - ptr) < itemSize) {
|
||||
if (!decompress(wait))
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t nAvail;
|
||||
nAvail = (end - ptr) / itemSize;
|
||||
if (nAvail < nItems)
|
||||
return nAvail;
|
||||
|
||||
return nItems;
|
||||
}
|
||||
|
||||
// decompress() calls the decompressor once. Note that this won't necessarily
|
||||
// generate any output data - it may just consume some input data. Returns
|
||||
// false if wait is false and we would block on the underlying stream.
|
||||
|
||||
bool ZlibInStream::decompress(bool wait)
|
||||
bool ZlibInStream::fillBuffer(size_t maxSize, bool wait)
|
||||
{
|
||||
if (!underlying)
|
||||
throw Exception("ZlibInStream overrun: no underlying stream");
|
||||
|
||||
zs->next_out = (U8*)end;
|
||||
zs->avail_out = start + bufSize - end;
|
||||
zs->avail_out = maxSize;
|
||||
|
||||
size_t n = underlying->check(1, 1, wait);
|
||||
size_t n = underlying->check(1, wait);
|
||||
if (n == 0) return false;
|
||||
zs->next_in = (U8*)underlying->getptr();
|
||||
zs->avail_in = underlying->getend() - underlying->getptr();
|
||||
zs->avail_in = underlying->avail();
|
||||
if (zs->avail_in > bytesIn)
|
||||
zs->avail_in = bytesIn;
|
||||
|
||||
|
||||
@@ -24,38 +24,32 @@
|
||||
#ifndef __RDR_ZLIBINSTREAM_H__
|
||||
#define __RDR_ZLIBINSTREAM_H__
|
||||
|
||||
#include <rdr/InStream.h>
|
||||
#include <rdr/BufferedInStream.h>
|
||||
|
||||
struct z_stream_s;
|
||||
|
||||
namespace rdr {
|
||||
|
||||
class ZlibInStream : public InStream {
|
||||
class ZlibInStream : public BufferedInStream {
|
||||
|
||||
public:
|
||||
|
||||
ZlibInStream(size_t bufSize=0);
|
||||
ZlibInStream();
|
||||
virtual ~ZlibInStream();
|
||||
|
||||
void setUnderlying(InStream* is, size_t bytesIn);
|
||||
void flushUnderlying();
|
||||
size_t pos();
|
||||
void reset();
|
||||
|
||||
private:
|
||||
|
||||
void init();
|
||||
void deinit();
|
||||
|
||||
size_t overrun(size_t itemSize, size_t nItems, bool wait);
|
||||
bool decompress(bool wait);
|
||||
virtual bool fillBuffer(size_t maxSize, bool wait);
|
||||
|
||||
private:
|
||||
InStream* underlying;
|
||||
size_t bufSize;
|
||||
size_t offset;
|
||||
z_stream_s* zs;
|
||||
size_t bytesIn;
|
||||
U8* start;
|
||||
};
|
||||
|
||||
} // end of namespace rdr
|
||||
|
||||
@@ -30,9 +30,9 @@ using namespace rdr;
|
||||
|
||||
enum { DEFAULT_BUF_SIZE = 16384 };
|
||||
|
||||
ZlibOutStream::ZlibOutStream(OutStream* os, size_t bufSize_, int compressLevel)
|
||||
ZlibOutStream::ZlibOutStream(OutStream* os, int compressLevel)
|
||||
: underlying(os), compressionLevel(compressLevel), newLevel(compressLevel),
|
||||
bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0)
|
||||
bufSize(DEFAULT_BUF_SIZE), offset(0)
|
||||
{
|
||||
zs = new z_stream;
|
||||
zs->zalloc = Z_NULL;
|
||||
@@ -95,18 +95,18 @@ void ZlibOutStream::flush()
|
||||
ptr = start;
|
||||
}
|
||||
|
||||
size_t ZlibOutStream::overrun(size_t itemSize, size_t nItems)
|
||||
void ZlibOutStream::overrun(size_t needed)
|
||||
{
|
||||
#ifdef ZLIBOUT_DEBUG
|
||||
fprintf(stderr,"zos overrun\n");
|
||||
#endif
|
||||
|
||||
if (itemSize > bufSize)
|
||||
throw Exception("ZlibOutStream overrun: max itemSize exceeded");
|
||||
if (needed > bufSize)
|
||||
throw Exception("ZlibOutStream overrun: buffer size exceeded");
|
||||
|
||||
checkCompressionLevel();
|
||||
|
||||
while ((size_t)(end - ptr) < itemSize) {
|
||||
while (avail() < needed) {
|
||||
zs->next_in = start;
|
||||
zs->avail_in = ptr - start;
|
||||
|
||||
@@ -126,13 +126,6 @@ size_t ZlibOutStream::overrun(size_t itemSize, size_t nItems)
|
||||
ptr -= zs->next_in - start;
|
||||
}
|
||||
}
|
||||
|
||||
size_t nAvail;
|
||||
nAvail = (end - ptr) / itemSize;
|
||||
if (nAvail < nItems)
|
||||
return nAvail;
|
||||
|
||||
return nItems;
|
||||
}
|
||||
|
||||
void ZlibOutStream::deflate(int flush)
|
||||
@@ -148,7 +141,7 @@ void ZlibOutStream::deflate(int flush)
|
||||
do {
|
||||
underlying->check(1);
|
||||
zs->next_out = underlying->getptr();
|
||||
zs->avail_out = underlying->getend() - underlying->getptr();
|
||||
zs->avail_out = underlying->avail();
|
||||
|
||||
#ifdef ZLIBOUT_DEBUG
|
||||
fprintf(stderr,"zos: calling deflate, avail_in %d, avail_out %d\n",
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace rdr {
|
||||
|
||||
public:
|
||||
|
||||
ZlibOutStream(OutStream* os=0, size_t bufSize=0, int compressionLevel=-1);
|
||||
ZlibOutStream(OutStream* os=0, int compressionLevel=-1);
|
||||
virtual ~ZlibOutStream();
|
||||
|
||||
void setUnderlying(OutStream* os);
|
||||
@@ -45,7 +45,7 @@ namespace rdr {
|
||||
|
||||
private:
|
||||
|
||||
size_t overrun(size_t itemSize, size_t nItems);
|
||||
virtual void overrun(size_t needed);
|
||||
void deflate(int flush);
|
||||
void checkCompressionLevel();
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <rfb/Exception.h>
|
||||
#include <rfb/clipboardTypes.h>
|
||||
#include <rfb/fenceTypes.h>
|
||||
#include <rfb/CMsgReader.h>
|
||||
#include <rfb/CMsgWriter.h>
|
||||
@@ -42,7 +43,8 @@ CConnection::CConnection()
|
||||
: csecurity(0), is(0), os(0), reader_(0), writer_(0),
|
||||
shared(false),
|
||||
state_(RFBSTATE_UNINITIALISED), useProtocol3_3(false),
|
||||
framebuffer(NULL), decoder(this)
|
||||
framebuffer(NULL), decoder(this),
|
||||
serverClipboard(NULL), hasLocalClipboard(false)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -54,6 +56,7 @@ CConnection::~CConnection()
|
||||
reader_ = 0;
|
||||
delete writer_;
|
||||
writer_ = 0;
|
||||
strFree(serverClipboard);
|
||||
}
|
||||
|
||||
void CConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
|
||||
@@ -342,6 +345,79 @@ void CConnection::dataRect(const Rect& r, int encoding)
|
||||
decoder.decodeRect(r, encoding, framebuffer);
|
||||
}
|
||||
|
||||
void CConnection::serverCutText(const char* str)
|
||||
{
|
||||
hasLocalClipboard = false;
|
||||
|
||||
strFree(serverClipboard);
|
||||
serverClipboard = NULL;
|
||||
|
||||
serverClipboard = latin1ToUTF8(str);
|
||||
|
||||
handleClipboardAnnounce(true);
|
||||
}
|
||||
|
||||
void CConnection::handleClipboardCaps(rdr::U32 flags,
|
||||
const rdr::U32* lengths)
|
||||
{
|
||||
rdr::U32 sizes[] = { 0 };
|
||||
|
||||
CMsgHandler::handleClipboardCaps(flags, lengths);
|
||||
|
||||
writer()->writeClipboardCaps(rfb::clipboardUTF8 |
|
||||
rfb::clipboardRequest |
|
||||
rfb::clipboardPeek |
|
||||
rfb::clipboardNotify |
|
||||
rfb::clipboardProvide,
|
||||
sizes);
|
||||
}
|
||||
|
||||
void CConnection::handleClipboardRequest(rdr::U32 flags)
|
||||
{
|
||||
if (!(flags & rfb::clipboardUTF8))
|
||||
return;
|
||||
if (!hasLocalClipboard)
|
||||
return;
|
||||
handleClipboardRequest();
|
||||
}
|
||||
|
||||
void CConnection::handleClipboardPeek(rdr::U32 flags)
|
||||
{
|
||||
if (!hasLocalClipboard)
|
||||
return;
|
||||
if (cp.clipboardFlags() & rfb::clipboardNotify)
|
||||
writer()->writeClipboardNotify(rfb::clipboardUTF8);
|
||||
}
|
||||
|
||||
void CConnection::handleClipboardNotify(rdr::U32 flags)
|
||||
{
|
||||
strFree(serverClipboard);
|
||||
serverClipboard = NULL;
|
||||
|
||||
if (flags & rfb::clipboardUTF8) {
|
||||
hasLocalClipboard = false;
|
||||
handleClipboardAnnounce(true);
|
||||
} else {
|
||||
handleClipboardAnnounce(false);
|
||||
}
|
||||
}
|
||||
|
||||
void CConnection::handleClipboardProvide(rdr::U32 flags,
|
||||
const size_t* lengths,
|
||||
const rdr::U8* const* data)
|
||||
{
|
||||
if (!(flags & rfb::clipboardUTF8))
|
||||
return;
|
||||
|
||||
strFree(serverClipboard);
|
||||
serverClipboard = NULL;
|
||||
|
||||
serverClipboard = convertLF((const char*)data[0], lengths[0]);
|
||||
|
||||
// FIXME: Should probably verify that this data was actually requested
|
||||
handleClipboardData(serverClipboard);
|
||||
}
|
||||
|
||||
void CConnection::authSuccess()
|
||||
{
|
||||
}
|
||||
@@ -364,3 +440,53 @@ void CConnection::fence(rdr::U32 flags, unsigned len, const char data[])
|
||||
|
||||
writer()->writeFence(flags, len, data);
|
||||
}
|
||||
|
||||
void CConnection::handleClipboardRequest()
|
||||
{
|
||||
}
|
||||
|
||||
void CConnection::handleClipboardAnnounce(bool available)
|
||||
{
|
||||
}
|
||||
|
||||
void CConnection::handleClipboardData(const char* data)
|
||||
{
|
||||
}
|
||||
|
||||
void CConnection::requestClipboard()
|
||||
{
|
||||
if (serverClipboard != NULL) {
|
||||
handleClipboardData(serverClipboard);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cp.clipboardFlags() & rfb::clipboardRequest)
|
||||
writer()->writeClipboardRequest(rfb::clipboardUTF8);
|
||||
}
|
||||
|
||||
void CConnection::announceClipboard(bool available)
|
||||
{
|
||||
hasLocalClipboard = available;
|
||||
|
||||
if (cp.clipboardFlags() & rfb::clipboardNotify)
|
||||
writer()->writeClipboardNotify(available ? rfb::clipboardUTF8 : 0);
|
||||
else {
|
||||
if (available)
|
||||
handleClipboardRequest();
|
||||
}
|
||||
}
|
||||
|
||||
void CConnection::sendClipboardData(const char* data)
|
||||
{
|
||||
if (cp.clipboardFlags() & rfb::clipboardProvide) {
|
||||
CharArray filtered(convertCRLF(data));
|
||||
size_t sizes[1] = { strlen(filtered.buf) + 1 };
|
||||
const rdr::U8* data[1] = { (const rdr::U8*)filtered.buf };
|
||||
writer()->writeClipboardProvide(rfb::clipboardUTF8, sizes, data);
|
||||
} else {
|
||||
CharArray latin1(utf8ToLatin1(data));
|
||||
|
||||
writer()->writeClientCutText(latin1.buf, strlen(latin1.buf));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -107,6 +107,17 @@ namespace rfb {
|
||||
virtual void framebufferUpdateEnd();
|
||||
virtual void dataRect(const Rect& r, int encoding);
|
||||
|
||||
virtual void serverCutText(const char* str);
|
||||
|
||||
virtual void handleClipboardCaps(rdr::U32 flags,
|
||||
const rdr::U32* lengths);
|
||||
virtual void handleClipboardRequest(rdr::U32 flags);
|
||||
virtual void handleClipboardPeek(rdr::U32 flags);
|
||||
virtual void handleClipboardNotify(rdr::U32 flags);
|
||||
virtual void handleClipboardProvide(rdr::U32 flags,
|
||||
const size_t* lengths,
|
||||
const rdr::U8* const* data);
|
||||
|
||||
|
||||
// Methods to be overridden in a derived class
|
||||
|
||||
@@ -121,9 +132,43 @@ namespace rfb {
|
||||
// derived class must call on to CConnection::serverInit().
|
||||
virtual void serverInit();
|
||||
|
||||
// handleClipboardRequest() is called whenever the server requests
|
||||
// the client to send over its clipboard data. It will only be
|
||||
// called after the client has first announced a clipboard change
|
||||
// via announceClipboard().
|
||||
virtual void handleClipboardRequest();
|
||||
|
||||
// handleClipboardAnnounce() is called to indicate a change in the
|
||||
// clipboard on the server. Call requestClipboard() to access the
|
||||
// actual data.
|
||||
virtual void handleClipboardAnnounce(bool available);
|
||||
|
||||
// handleClipboardData() is called when the server has sent over
|
||||
// the clipboard data as a result of a previous call to
|
||||
// requestClipboard(). Note that this function might never be
|
||||
// called if the clipboard data was no longer available when the
|
||||
// server received the request.
|
||||
virtual void handleClipboardData(const char* data);
|
||||
|
||||
|
||||
// Other methods
|
||||
|
||||
// requestClipboard() will result in a request to the server to
|
||||
// transfer its clipboard data. A call to handleClipboardData()
|
||||
// will be made once the data is available.
|
||||
virtual void requestClipboard();
|
||||
|
||||
// announceClipboard() informs the server of changes to the
|
||||
// clipboard on the client. The server may later request the
|
||||
// clipboard data via handleClipboardRequest().
|
||||
virtual void announceClipboard(bool available);
|
||||
|
||||
// sendClipboardData() transfers the clipboard data to the server
|
||||
// and should be called whenever the server has requested the
|
||||
// clipboard via handleClipboardRequest().
|
||||
virtual void sendClipboardData(const char* data);
|
||||
|
||||
|
||||
CMsgReader* reader() { return reader_; }
|
||||
CMsgWriter* writer() { return writer_; }
|
||||
|
||||
@@ -190,6 +235,9 @@ namespace rfb {
|
||||
|
||||
ModifiablePixelBuffer* framebuffer;
|
||||
DecodeManager decoder;
|
||||
|
||||
char* serverClipboard;
|
||||
bool hasLocalClipboard;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -58,6 +58,7 @@ set(RFB_SOURCES
|
||||
TightEncoder.cxx
|
||||
TightJPEGEncoder.cxx
|
||||
TightWEBPEncoder.cxx
|
||||
TightX264Encoder.cxx
|
||||
UpdateTracker.cxx
|
||||
VNCSConnectionST.cxx
|
||||
VNCServerST.cxx
|
||||
@@ -65,6 +66,11 @@ set(RFB_SOURCES
|
||||
ZRLEDecoder.cxx
|
||||
encodings.cxx
|
||||
util.cxx
|
||||
mp4.c
|
||||
mp4_bitbuf.c
|
||||
mp4_moov.c
|
||||
mp4_moof.c
|
||||
nvidia.cxx
|
||||
xxhash.c)
|
||||
|
||||
if(UNIX)
|
||||
|
||||
@@ -92,3 +92,26 @@ void CMsgHandler::setLEDState(unsigned int state)
|
||||
{
|
||||
cp.setLEDState(state);
|
||||
}
|
||||
|
||||
void CMsgHandler::handleClipboardCaps(rdr::U32 flags, const rdr::U32* lengths)
|
||||
{
|
||||
cp.setClipboardCaps(flags, lengths);
|
||||
}
|
||||
|
||||
void CMsgHandler::handleClipboardRequest(rdr::U32 flags)
|
||||
{
|
||||
}
|
||||
|
||||
void CMsgHandler::handleClipboardPeek(rdr::U32 flags)
|
||||
{
|
||||
}
|
||||
|
||||
void CMsgHandler::handleClipboardNotify(rdr::U32 flags)
|
||||
{
|
||||
}
|
||||
|
||||
void CMsgHandler::handleClipboardProvide(rdr::U32 flags,
|
||||
const size_t* lengths,
|
||||
const rdr::U8* const* data)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace rfb {
|
||||
int w, int h,
|
||||
const ScreenSet& layout);
|
||||
virtual void setCursor(int width, int height, const Point& hotspot,
|
||||
const rdr::U8* data) = 0;
|
||||
const rdr::U8* data, const bool resizing = false) = 0;
|
||||
virtual void setPixelFormat(const PixelFormat& pf);
|
||||
virtual void setName(const char* name);
|
||||
virtual void fence(rdr::U32 flags, unsigned len, const char data[]);
|
||||
@@ -72,6 +72,15 @@ namespace rfb {
|
||||
|
||||
virtual void setLEDState(unsigned int state);
|
||||
|
||||
virtual void handleClipboardCaps(rdr::U32 flags,
|
||||
const rdr::U32* lengths);
|
||||
virtual void handleClipboardRequest(rdr::U32 flags);
|
||||
virtual void handleClipboardPeek(rdr::U32 flags);
|
||||
virtual void handleClipboardNotify(rdr::U32 flags);
|
||||
virtual void handleClipboardProvide(rdr::U32 flags,
|
||||
const size_t* lengths,
|
||||
const rdr::U8* const* data);
|
||||
|
||||
ConnParams cp;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -18,10 +18,14 @@
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <rdr/OutStream.h>
|
||||
#include <rdr/MemOutStream.h>
|
||||
#include <rdr/ZlibOutStream.h>
|
||||
|
||||
#include <rfb/msgTypes.h>
|
||||
#include <rfb/fenceTypes.h>
|
||||
#include <rfb/encodings.h>
|
||||
#include <rfb/qemuTypes.h>
|
||||
#include <rfb/clipboardTypes.h>
|
||||
#include <rfb/Exception.h>
|
||||
#include <rfb/PixelFormat.h>
|
||||
#include <rfb/Rect.h>
|
||||
@@ -57,7 +61,7 @@ void CMsgWriter::writeSetPixelFormat(const PixelFormat& pf)
|
||||
void CMsgWriter::writeSetEncodings(int nEncodings, rdr::U32* encodings)
|
||||
{
|
||||
startMsg(msgTypeSetEncodings);
|
||||
os->skip(1);
|
||||
os->pad(1);
|
||||
os->writeU16(nEncodings);
|
||||
for (int i = 0; i < nEncodings; i++)
|
||||
os->writeU32(encodings[i]);
|
||||
@@ -265,6 +269,104 @@ void CMsgWriter::writeClientCutText(const char* str, rdr::U32 len)
|
||||
endMsg();
|
||||
}
|
||||
|
||||
void CMsgWriter::writeClipboardCaps(rdr::U32 caps,
|
||||
const rdr::U32* lengths)
|
||||
{
|
||||
size_t i, count;
|
||||
|
||||
if (!(cp->clipboardFlags() & clipboardCaps))
|
||||
throw Exception("Server does not support clipboard \"caps\" action");
|
||||
|
||||
count = 0;
|
||||
for (i = 0;i < 16;i++) {
|
||||
if (caps & (1 << i))
|
||||
count++;
|
||||
}
|
||||
|
||||
startMsg(msgTypeClientCutText);
|
||||
os->pad(3);
|
||||
os->writeS32(-(4 + 4 * count));
|
||||
|
||||
os->writeU32(caps | clipboardCaps);
|
||||
|
||||
count = 0;
|
||||
for (i = 0;i < 16;i++) {
|
||||
if (caps & (1 << i))
|
||||
os->writeU32(lengths[count++]);
|
||||
}
|
||||
|
||||
endMsg();
|
||||
}
|
||||
|
||||
void CMsgWriter::writeClipboardRequest(rdr::U32 flags)
|
||||
{
|
||||
if (!(cp->clipboardFlags() & clipboardRequest))
|
||||
throw Exception("Server does not support clipboard \"request\" action");
|
||||
|
||||
startMsg(msgTypeClientCutText);
|
||||
os->pad(3);
|
||||
os->writeS32(-4);
|
||||
os->writeU32(flags | clipboardRequest);
|
||||
endMsg();
|
||||
}
|
||||
|
||||
void CMsgWriter::writeClipboardPeek(rdr::U32 flags)
|
||||
{
|
||||
if (!(cp->clipboardFlags() & clipboardPeek))
|
||||
throw Exception("Server does not support clipboard \"peek\" action");
|
||||
|
||||
startMsg(msgTypeClientCutText);
|
||||
os->pad(3);
|
||||
os->writeS32(-4);
|
||||
os->writeU32(flags | clipboardPeek);
|
||||
endMsg();
|
||||
}
|
||||
|
||||
void CMsgWriter::writeClipboardNotify(rdr::U32 flags)
|
||||
{
|
||||
if (!(cp->clipboardFlags() & clipboardNotify))
|
||||
throw Exception("Server does not support clipboard \"notify\" action");
|
||||
|
||||
startMsg(msgTypeClientCutText);
|
||||
os->pad(3);
|
||||
os->writeS32(-4);
|
||||
os->writeU32(flags | clipboardNotify);
|
||||
endMsg();
|
||||
}
|
||||
|
||||
void CMsgWriter::writeClipboardProvide(rdr::U32 flags,
|
||||
const size_t* lengths,
|
||||
const rdr::U8* const* data)
|
||||
{
|
||||
rdr::MemOutStream mos;
|
||||
rdr::ZlibOutStream zos;
|
||||
|
||||
int i, count;
|
||||
|
||||
if (!(cp->clipboardFlags() & clipboardProvide))
|
||||
throw Exception("Server does not support clipboard \"provide\" action");
|
||||
|
||||
zos.setUnderlying(&mos);
|
||||
|
||||
count = 0;
|
||||
for (i = 0;i < 16;i++) {
|
||||
if (!(flags & (1 << i)))
|
||||
continue;
|
||||
zos.writeU32(lengths[count]);
|
||||
zos.writeBytes(data[count], lengths[count]);
|
||||
count++;
|
||||
}
|
||||
|
||||
zos.flush();
|
||||
|
||||
startMsg(msgTypeClientCutText);
|
||||
os->pad(3);
|
||||
os->writeS32(-(4 + mos.length()));
|
||||
os->writeU32(flags | clipboardProvide);
|
||||
os->writeBytes(mos.data(), mos.length());
|
||||
endMsg();
|
||||
}
|
||||
|
||||
void CMsgWriter::startMsg(int type)
|
||||
{
|
||||
os->writeU8(type);
|
||||
|
||||
@@ -56,6 +56,13 @@ namespace rfb {
|
||||
void writePointerEvent(const Point& pos, int buttonMask);
|
||||
void writeClientCutText(const char* str, rdr::U32 len);
|
||||
|
||||
void writeClipboardCaps(rdr::U32 caps, const rdr::U32* lengths);
|
||||
void writeClipboardRequest(rdr::U32 flags);
|
||||
void writeClipboardPeek(rdr::U32 flags);
|
||||
void writeClipboardNotify(rdr::U32 flags);
|
||||
void writeClipboardProvide(rdr::U32 flags, const size_t* lengths,
|
||||
const rdr::U8* const* data);
|
||||
|
||||
protected:
|
||||
void startMsg(int type);
|
||||
void endMsg();
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <rfb/Exception.h>
|
||||
#include <rfb/encodings.h>
|
||||
#include <rfb/ledStates.h>
|
||||
#include <rfb/clipboardTypes.h>
|
||||
#include <rfb/ConnParams.h>
|
||||
#include <rfb/ServerCore.h>
|
||||
#include <rfb/SMsgHandler.h>
|
||||
@@ -36,19 +37,25 @@ ConnParams::ConnParams()
|
||||
width(0), height(0), useCopyRect(false),
|
||||
supportsLocalCursor(false), supportsLocalXCursor(false),
|
||||
supportsLocalCursorWithAlpha(false),
|
||||
supportsCursorPosition(false),
|
||||
supportsDesktopResize(false), supportsExtendedDesktopSize(false),
|
||||
supportsDesktopRename(false), supportsLastRect(false),
|
||||
supportsLEDState(false), supportsQEMUKeyEvent(false),
|
||||
supportsWEBP(false),
|
||||
supportsSetDesktopSize(false), supportsFence(false),
|
||||
supportsContinuousUpdates(false),
|
||||
supportsContinuousUpdates(false), supportsExtendedClipboard(false),
|
||||
compressLevel(2), qualityLevel(-1), fineQualityLevel(-1),
|
||||
subsampling(subsampleUndefined), name_(0), verStrPos(0),
|
||||
subsampling(subsampleUndefined), name_(0), cursorPos_(0, 0), verStrPos(0),
|
||||
ledState_(ledUnknown), shandler(NULL)
|
||||
{
|
||||
memset(kasmPassed, 0, KASM_NUM_SETTINGS);
|
||||
setName("");
|
||||
cursor_ = new Cursor(0, 0, Point(), NULL);
|
||||
|
||||
clipFlags = clipboardUTF8 | clipboardRTF | clipboardHTML |
|
||||
clipboardRequest | clipboardNotify | clipboardProvide;
|
||||
memset(clipSizes, 0, sizeof(clipSizes));
|
||||
clipSizes[0] = 20 * 1024 * 1024;
|
||||
}
|
||||
|
||||
ConnParams::~ConnParams()
|
||||
@@ -101,6 +108,11 @@ void ConnParams::setCursor(const Cursor& other)
|
||||
cursor_ = new Cursor(other);
|
||||
}
|
||||
|
||||
void ConnParams::setCursorPos(const Point& pos)
|
||||
{
|
||||
cursorPos_ = pos;
|
||||
}
|
||||
|
||||
bool ConnParams::supportsEncoding(rdr::S32 encoding) const
|
||||
{
|
||||
return encodings_.count(encoding) != 0;
|
||||
@@ -147,6 +159,9 @@ void ConnParams::setEncodings(int nEncodings, const rdr::S32* encodings)
|
||||
case pseudoEncodingExtendedDesktopSize:
|
||||
supportsExtendedDesktopSize = true;
|
||||
break;
|
||||
case pseudoEncodingVMwareCursorPosition:
|
||||
supportsCursorPosition = true;
|
||||
break;
|
||||
case pseudoEncodingDesktopName:
|
||||
supportsDesktopRename = true;
|
||||
break;
|
||||
@@ -168,6 +183,9 @@ void ConnParams::setEncodings(int nEncodings, const rdr::S32* encodings)
|
||||
case pseudoEncodingContinuousUpdates:
|
||||
supportsContinuousUpdates = true;
|
||||
break;
|
||||
case pseudoEncodingExtendedClipboard:
|
||||
supportsExtendedClipboard = true;
|
||||
break;
|
||||
case pseudoEncodingSubsamp1X:
|
||||
subsampling = subsampleNone;
|
||||
break;
|
||||
@@ -259,3 +277,17 @@ void ConnParams::setLEDState(unsigned int state)
|
||||
{
|
||||
ledState_ = state;
|
||||
}
|
||||
|
||||
void ConnParams::setClipboardCaps(rdr::U32 flags, const rdr::U32* lengths)
|
||||
{
|
||||
int i, num;
|
||||
|
||||
clipFlags = flags;
|
||||
|
||||
num = 0;
|
||||
for (i = 0;i < 16;i++) {
|
||||
if (!(flags & (1 << i)))
|
||||
continue;
|
||||
clipSizes[i] = lengths[num++];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,6 +84,9 @@ namespace rfb {
|
||||
const Cursor& cursor() const { return *cursor_; }
|
||||
void setCursor(const Cursor& cursor);
|
||||
|
||||
const Point& cursorPos() const { return cursorPos_; }
|
||||
void setCursorPos(const Point& pos);
|
||||
|
||||
bool supportsEncoding(rdr::S32 encoding) const;
|
||||
|
||||
void setEncodings(int nEncodings, const rdr::S32* encodings);
|
||||
@@ -91,11 +94,15 @@ namespace rfb {
|
||||
unsigned int ledState() { return ledState_; }
|
||||
void setLEDState(unsigned int state);
|
||||
|
||||
rdr::U32 clipboardFlags() const { return clipFlags; }
|
||||
void setClipboardCaps(rdr::U32 flags, const rdr::U32* lengths);
|
||||
|
||||
bool useCopyRect;
|
||||
|
||||
bool supportsLocalCursor;
|
||||
bool supportsLocalXCursor;
|
||||
bool supportsLocalCursorWithAlpha;
|
||||
bool supportsCursorPosition;
|
||||
bool supportsDesktopResize;
|
||||
bool supportsExtendedDesktopSize;
|
||||
bool supportsDesktopRename;
|
||||
@@ -107,6 +114,7 @@ namespace rfb {
|
||||
bool supportsSetDesktopSize;
|
||||
bool supportsFence;
|
||||
bool supportsContinuousUpdates;
|
||||
bool supportsExtendedClipboard;
|
||||
|
||||
int compressLevel;
|
||||
int qualityLevel;
|
||||
@@ -136,11 +144,14 @@ namespace rfb {
|
||||
PixelFormat pf_;
|
||||
char* name_;
|
||||
Cursor* cursor_;
|
||||
Point cursorPos_;
|
||||
std::set<rdr::S32> encodings_;
|
||||
char verStr[13];
|
||||
int verStrPos;
|
||||
unsigned int ledState_;
|
||||
SMsgHandler *shandler;
|
||||
rdr::U32 clipFlags;
|
||||
rdr::U32 clipSizes[16];
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include <rfb/TightEncoder.h>
|
||||
#include <rfb/TightJPEGEncoder.h>
|
||||
#include <rfb/TightWEBPEncoder.h>
|
||||
#include <rfb/TightX264Encoder.h>
|
||||
|
||||
using namespace rfb;
|
||||
|
||||
@@ -68,6 +69,7 @@ enum EncoderClass {
|
||||
encoderTight,
|
||||
encoderTightJPEG,
|
||||
encoderTightWEBP,
|
||||
encoderTightX264,
|
||||
encoderZRLE,
|
||||
encoderClassMax,
|
||||
};
|
||||
@@ -110,6 +112,8 @@ static const char *encoderClassName(EncoderClass klass)
|
||||
return "Tight (JPEG)";
|
||||
case encoderTightWEBP:
|
||||
return "Tight (WEBP)";
|
||||
case encoderTightX264:
|
||||
return "Tight (X264)";
|
||||
case encoderZRLE:
|
||||
return "ZRLE";
|
||||
case encoderClassMax:
|
||||
@@ -170,6 +174,7 @@ EncodeManager::EncodeManager(SConnection* conn_, EncCache *encCache_) : conn(con
|
||||
encoders[encoderTight] = new TightEncoder(conn);
|
||||
encoders[encoderTightJPEG] = new TightJPEGEncoder(conn);
|
||||
encoders[encoderTightWEBP] = new TightWEBPEncoder(conn);
|
||||
encoders[encoderTightX264] = new TightX264Encoder(conn, encCache, encoderTightX264);
|
||||
encoders[encoderZRLE] = new ZRLEEncoder(conn);
|
||||
|
||||
webpBenchResult = ((TightWEBPEncoder *) encoders[encoderTightWEBP])->benchmark();
|
||||
@@ -393,20 +398,33 @@ void EncodeManager::doUpdate(bool allowLossy, const Region& changed_,
|
||||
|
||||
conn->writer()->writeFramebufferUpdateStart(nRects);
|
||||
|
||||
writeCopyRects(copied, copyDelta);
|
||||
writeCopyPassRects(copypassed);
|
||||
|
||||
/*
|
||||
* We start by searching for solid rects, which are then removed
|
||||
* from the changed region.
|
||||
* In extra-low-quality mode, if x264 is enabled, send entire screen frames
|
||||
*/
|
||||
if (conn->cp.supportsLastRect)
|
||||
writeSolidRects(&changed, pb);
|
||||
if (rfb::Server::x264Bitrate && videoDetected &&
|
||||
((TightX264Encoder *) encoders[encoderTightX264])->tryInit(pb)) {
|
||||
std::vector<Rect> rects;
|
||||
changed.get_rects(&rects);
|
||||
updateVideoStats(rects, pb);
|
||||
|
||||
writeRects(changed, pb,
|
||||
&start, true);
|
||||
if (!videoDetected) // In case detection happened between the calls
|
||||
writeRects(cursorRegion, renderedCursor);
|
||||
writeSubRect(pb->getRect(), pb, encoderFullColour, Palette(), std::vector<uint8_t>(),
|
||||
false);
|
||||
} else {
|
||||
writeCopyRects(copied, copyDelta);
|
||||
writeCopyPassRects(copypassed);
|
||||
|
||||
/*
|
||||
* We start by searching for solid rects, which are then removed
|
||||
* from the changed region.
|
||||
*/
|
||||
if (conn->cp.supportsLastRect)
|
||||
writeSolidRects(&changed, pb);
|
||||
|
||||
writeRects(changed, pb,
|
||||
&start, true);
|
||||
if (!videoDetected) // In case detection happened between the calls
|
||||
writeRects(cursorRegion, renderedCursor);
|
||||
}
|
||||
|
||||
updateQualities();
|
||||
|
||||
@@ -617,6 +635,8 @@ Encoder *EncodeManager::startRect(const Rect& rect, int type, const bool trackQu
|
||||
klass = activeEncoders[activeType];
|
||||
if (isWebp)
|
||||
klass = encoderTightWEBP;
|
||||
else if (rfb::Server::x264Bitrate && videoDetected) // if x264 enabled
|
||||
klass = encoderTightX264;
|
||||
|
||||
beforeLength = conn->getOutStream()->length();
|
||||
|
||||
@@ -656,6 +676,8 @@ void EncodeManager::endRect(const uint8_t isWebp)
|
||||
klass = activeEncoders[activeType];
|
||||
if (isWebp)
|
||||
klass = encoderTightWEBP;
|
||||
else if (rfb::Server::x264Bitrate && videoDetected) // if x264 enabled
|
||||
klass = encoderTightX264;
|
||||
stats[klass][activeType].bytes += length;
|
||||
}
|
||||
|
||||
@@ -861,6 +883,8 @@ void EncodeManager::updateVideoStats(const std::vector<Rect> &rects, const Pixel
|
||||
uint32_t i;
|
||||
|
||||
if (!rfb::Server::videoTime) {
|
||||
if (!videoDetected)
|
||||
((TightX264Encoder *) encoders[encoderTightX264])->setKeyframe();
|
||||
videoDetected = true;
|
||||
return;
|
||||
}
|
||||
@@ -888,6 +912,8 @@ void EncodeManager::updateVideoStats(const std::vector<Rect> &rects, const Pixel
|
||||
|
||||
if (area > (unsigned) rfb::Server::videoArea) {
|
||||
// Initiate low-quality video mode
|
||||
if (!videoDetected)
|
||||
((TightX264Encoder *) encoders[encoderTightX264])->setKeyframe();
|
||||
videoDetected = true;
|
||||
videoTimer.start(1000 * rfb::Server::videoOutTime);
|
||||
}
|
||||
@@ -1022,11 +1048,6 @@ void EncodeManager::writeRects(const Region& changed, const PixelBuffer* pb,
|
||||
webpTookTooLong = false;
|
||||
changed.get_rects(&rects);
|
||||
|
||||
// Update stats
|
||||
if (mainScreen) {
|
||||
updateVideoStats(rects, pb);
|
||||
}
|
||||
|
||||
if (videoDetected) {
|
||||
rects.clear();
|
||||
rects.push_back(pb->getRect());
|
||||
@@ -1171,6 +1192,11 @@ void EncodeManager::writeRects(const Region& changed, const PixelBuffer* pb,
|
||||
writeSubRect(subrects[i], pb, encoderTypes[i], palettes[i], compresseds[i], isWebp[i]);
|
||||
}
|
||||
|
||||
// Update stats
|
||||
if (mainScreen) {
|
||||
updateVideoStats(rects, pb);
|
||||
}
|
||||
|
||||
if (scaledpb)
|
||||
delete scaledpb;
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ JpegInitDestination(j_compress_ptr cinfo)
|
||||
|
||||
jc->clear();
|
||||
dest->pub.next_output_byte = jc->getptr();
|
||||
dest->pub.free_in_buffer = jc->getend() - jc->getptr();
|
||||
dest->pub.free_in_buffer = jc->avail();
|
||||
}
|
||||
|
||||
static boolean
|
||||
@@ -95,9 +95,9 @@ JpegEmptyOutputBuffer(j_compress_ptr cinfo)
|
||||
JpegCompressor *jc = dest->instance;
|
||||
|
||||
jc->setptr(jc->getend());
|
||||
jc->overrun(jc->getend() - jc->getstart(), 1);
|
||||
jc->check(jc->length());
|
||||
dest->pub.next_output_byte = jc->getptr();
|
||||
dest->pub.free_in_buffer = jc->getend() - jc->getptr();
|
||||
dest->pub.free_in_buffer = jc->avail();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -47,12 +47,6 @@ namespace rfb {
|
||||
|
||||
void writeBytes(const void*, int);
|
||||
|
||||
inline rdr::U8* getstart() { return start; }
|
||||
|
||||
virtual inline size_t overrun(size_t itemSize, size_t nItems) {
|
||||
return MemOutStream::overrun(itemSize, nItems);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
struct jpeg_compress_struct *cinfo;
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <string.h>
|
||||
#include <rfb/Exception.h>
|
||||
#include <rfb/Security.h>
|
||||
#include <rfb/clipboardTypes.h>
|
||||
#include <rfb/msgTypes.h>
|
||||
#include <rfb/fenceTypes.h>
|
||||
#include <rfb/SMsgReader.h>
|
||||
@@ -52,7 +53,8 @@ SConnection::SConnection()
|
||||
: readyForSetColourMapEntries(false),
|
||||
is(0), os(0), reader_(0), writer_(0),
|
||||
ssecurity(0), state_(RFBSTATE_UNINITIALISED),
|
||||
preferredEncoding(encodingRaw)
|
||||
preferredEncoding(encodingRaw),
|
||||
clientClipboard(NULL), hasLocalClipboard(false)
|
||||
{
|
||||
defaultMajorVersion = 3;
|
||||
defaultMinorVersion = 8;
|
||||
@@ -69,6 +71,7 @@ SConnection::~SConnection()
|
||||
reader_ = 0;
|
||||
delete writer_;
|
||||
writer_ = 0;
|
||||
strFree(clientClipboard);
|
||||
}
|
||||
|
||||
void SConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
|
||||
@@ -281,6 +284,73 @@ void SConnection::setEncodings(int nEncodings, const rdr::S32* encodings)
|
||||
}
|
||||
|
||||
SMsgHandler::setEncodings(nEncodings, encodings);
|
||||
|
||||
if (cp.supportsExtendedClipboard) {
|
||||
rdr::U32 sizes[] = { 0 };
|
||||
writer()->writeClipboardCaps(rfb::clipboardUTF8 |
|
||||
rfb::clipboardRequest |
|
||||
rfb::clipboardPeek |
|
||||
rfb::clipboardNotify |
|
||||
rfb::clipboardProvide,
|
||||
sizes);
|
||||
}
|
||||
}
|
||||
|
||||
void SConnection::clientCutText(const char* str, int len)
|
||||
{
|
||||
hasLocalClipboard = false;
|
||||
|
||||
strFree(clientClipboard);
|
||||
clientClipboard = NULL;
|
||||
|
||||
clientClipboard = latin1ToUTF8(str);
|
||||
|
||||
handleClipboardAnnounce(true);
|
||||
}
|
||||
|
||||
void SConnection::handleClipboardRequest(rdr::U32 flags)
|
||||
{
|
||||
if (!(flags & rfb::clipboardUTF8))
|
||||
return;
|
||||
if (!hasLocalClipboard)
|
||||
return;
|
||||
handleClipboardRequest();
|
||||
}
|
||||
|
||||
void SConnection::handleClipboardPeek(rdr::U32 flags)
|
||||
{
|
||||
if (!hasLocalClipboard)
|
||||
return;
|
||||
if (cp.clipboardFlags() & rfb::clipboardNotify)
|
||||
writer()->writeClipboardNotify(rfb::clipboardUTF8);
|
||||
}
|
||||
|
||||
void SConnection::handleClipboardNotify(rdr::U32 flags)
|
||||
{
|
||||
strFree(clientClipboard);
|
||||
clientClipboard = NULL;
|
||||
|
||||
if (flags & rfb::clipboardUTF8) {
|
||||
handleClipboardAnnounce(true);
|
||||
hasLocalClipboard = false;
|
||||
} else {
|
||||
handleClipboardAnnounce(false);
|
||||
}
|
||||
}
|
||||
|
||||
void SConnection::handleClipboardProvide(rdr::U32 flags,
|
||||
const size_t* lengths,
|
||||
const rdr::U8* const* data)
|
||||
{
|
||||
if (!(flags & rfb::clipboardUTF8))
|
||||
return;
|
||||
|
||||
strFree(clientClipboard);
|
||||
clientClipboard = NULL;
|
||||
|
||||
clientClipboard = convertLF((const char*)data[0], lengths[0]);
|
||||
|
||||
handleClipboardData(clientClipboard, strlen(clientClipboard));
|
||||
}
|
||||
|
||||
void SConnection::supportsQEMUKeyEvent()
|
||||
@@ -375,6 +445,58 @@ void SConnection::enableContinuousUpdates(bool enable,
|
||||
{
|
||||
}
|
||||
|
||||
void SConnection::handleClipboardRequest()
|
||||
{
|
||||
}
|
||||
|
||||
void SConnection::handleClipboardAnnounce(bool available)
|
||||
{
|
||||
}
|
||||
|
||||
void SConnection::handleClipboardData(const char* data, int len)
|
||||
{
|
||||
}
|
||||
|
||||
void SConnection::requestClipboard()
|
||||
{
|
||||
if (clientClipboard != NULL) {
|
||||
handleClipboardData(clientClipboard, strlen(clientClipboard));
|
||||
return;
|
||||
}
|
||||
|
||||
if (cp.supportsExtendedClipboard &&
|
||||
(cp.clipboardFlags() & rfb::clipboardRequest))
|
||||
writer()->writeClipboardRequest(rfb::clipboardUTF8);
|
||||
}
|
||||
|
||||
void SConnection::announceClipboard(bool available)
|
||||
{
|
||||
hasLocalClipboard = available;
|
||||
|
||||
if (cp.supportsExtendedClipboard &&
|
||||
(cp.clipboardFlags() & rfb::clipboardNotify))
|
||||
writer()->writeClipboardNotify(available ? rfb::clipboardUTF8 : 0);
|
||||
else {
|
||||
if (available)
|
||||
handleClipboardRequest();
|
||||
}
|
||||
}
|
||||
|
||||
void SConnection::sendClipboardData(const char* data, int len)
|
||||
{
|
||||
if (cp.supportsExtendedClipboard &&
|
||||
(cp.clipboardFlags() & rfb::clipboardProvide)) {
|
||||
CharArray filtered(convertCRLF(data));
|
||||
size_t sizes[1] = { strlen(filtered.buf) + 1 };
|
||||
const rdr::U8* data[1] = { (const rdr::U8*)filtered.buf };
|
||||
writer()->writeClipboardProvide(rfb::clipboardUTF8, sizes, data);
|
||||
} else {
|
||||
CharArray latin1(utf8ToLatin1(data));
|
||||
|
||||
writer()->writeServerCutText(latin1.buf, strlen(latin1.buf));
|
||||
}
|
||||
}
|
||||
|
||||
void SConnection::writeFakeColourMap(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
@@ -73,6 +73,15 @@ namespace rfb {
|
||||
|
||||
virtual void setEncodings(int nEncodings, const rdr::S32* encodings);
|
||||
|
||||
virtual void clientCutText(const char* str, int len);
|
||||
|
||||
virtual void handleClipboardRequest(rdr::U32 flags);
|
||||
virtual void handleClipboardPeek(rdr::U32 flags);
|
||||
virtual void handleClipboardNotify(rdr::U32 flags);
|
||||
virtual void handleClipboardProvide(rdr::U32 flags,
|
||||
const size_t* lengths,
|
||||
const rdr::U8* const* data);
|
||||
|
||||
virtual void supportsQEMUKeyEvent();
|
||||
|
||||
// Methods to be overridden in a derived class
|
||||
@@ -118,6 +127,25 @@ namespace rfb {
|
||||
virtual void enableContinuousUpdates(bool enable,
|
||||
int x, int y, int w, int h);
|
||||
|
||||
// handleClipboardRequest() is called whenever the client requests
|
||||
// the server to send over its clipboard data. It will only be
|
||||
// called after the server has first announced a clipboard change
|
||||
// via announceClipboard().
|
||||
virtual void handleClipboardRequest();
|
||||
|
||||
// handleClipboardAnnounce() is called to indicate a change in the
|
||||
// clipboard on the client. Call requestClipboard() to access the
|
||||
// actual data.
|
||||
virtual void handleClipboardAnnounce(bool available);
|
||||
|
||||
// handleClipboardData() is called when the client has sent over
|
||||
// the clipboard data as a result of a previous call to
|
||||
// requestClipboard(). Note that this function might never be
|
||||
// called if the clipboard data was no longer available when the
|
||||
// client received the request.
|
||||
virtual void handleClipboardData(const char* data, int len);
|
||||
|
||||
|
||||
virtual void add_changed_all() {}
|
||||
|
||||
// setAccessRights() allows a security package to limit the access rights
|
||||
@@ -138,6 +166,22 @@ namespace rfb {
|
||||
|
||||
// Other methods
|
||||
|
||||
// requestClipboard() will result in a request to the client to
|
||||
// transfer its clipboard data. A call to handleClipboardData()
|
||||
// will be made once the data is available.
|
||||
virtual void requestClipboard();
|
||||
|
||||
// announceClipboard() informs the client of changes to the
|
||||
// clipboard on the server. The client may later request the
|
||||
// clipboard data via handleClipboardRequest().
|
||||
virtual void announceClipboard(bool available);
|
||||
|
||||
// sendClipboardData() transfers the clipboard data to the client
|
||||
// and should be called whenever the client has requested the
|
||||
// clipboard via handleClipboardRequest().
|
||||
virtual void sendClipboardData(const char* data, int len);
|
||||
|
||||
|
||||
// authenticated() returns true if the client has authenticated
|
||||
// successfully.
|
||||
bool authenticated() { return (state_ == RFBSTATE_INITIALISATION ||
|
||||
@@ -203,6 +247,12 @@ namespace rfb {
|
||||
SSecurity* ssecurity;
|
||||
stateEnum state_;
|
||||
rdr::S32 preferredEncoding;
|
||||
|
||||
char* clientClipboard;
|
||||
bool hasLocalClipboard;
|
||||
};
|
||||
}
|
||||
|
||||
#define WRITER_PERMS (AccessKeyEvents | AccessPtrEvents | AccessCutText | AccessSetDesktopSize)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -77,6 +77,25 @@ namespace rfb {
|
||||
// pointerEvent(), keyEvent() and clientCutText() are called in response to
|
||||
// the relevant RFB protocol messages from clients.
|
||||
// See InputHandler for method signatures.
|
||||
|
||||
// handleClipboardRequest() is called whenever a client requests
|
||||
// the server to send over its clipboard data. It will only be
|
||||
// called after the server has first announced a clipboard change
|
||||
// via VNCServer::announceClipboard().
|
||||
virtual void handleClipboardRequest() {}
|
||||
|
||||
// handleClipboardAnnounce() is called to indicate a change in the
|
||||
// clipboard on a client. Call VNCServer::requestClipboard() to
|
||||
// access the actual data.
|
||||
virtual void handleClipboardAnnounce(bool __unused_attr available) {}
|
||||
|
||||
// handleClipboardData() is called when a client has sent over
|
||||
// the clipboard data as a result of a previous call to
|
||||
// VNCServer::requestClipboard(). Note that this function might
|
||||
// never be called if the clipboard data was no longer available
|
||||
// when the client received the request.
|
||||
virtual void handleClipboardData(const char* __unused_attr data, int len __unused_attr) {}
|
||||
|
||||
protected:
|
||||
virtual ~SDesktop() {}
|
||||
};
|
||||
|
||||
@@ -64,6 +64,29 @@ void SMsgHandler::setEncodings(int nEncodings, const rdr::S32* encodings)
|
||||
supportsQEMUKeyEvent();
|
||||
}
|
||||
|
||||
void SMsgHandler::handleClipboardCaps(rdr::U32 flags, const rdr::U32* lengths)
|
||||
{
|
||||
cp.setClipboardCaps(flags, lengths);
|
||||
}
|
||||
|
||||
void SMsgHandler::handleClipboardRequest(rdr::U32 flags)
|
||||
{
|
||||
}
|
||||
|
||||
void SMsgHandler::handleClipboardPeek(rdr::U32 flags)
|
||||
{
|
||||
}
|
||||
|
||||
void SMsgHandler::handleClipboardNotify(rdr::U32 flags)
|
||||
{
|
||||
}
|
||||
|
||||
void SMsgHandler::handleClipboardProvide(rdr::U32 flags,
|
||||
const size_t* lengths,
|
||||
const rdr::U8* const* data)
|
||||
{
|
||||
}
|
||||
|
||||
void SMsgHandler::supportsLocalCursor()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -54,6 +54,15 @@ namespace rfb {
|
||||
virtual void enableContinuousUpdates(bool enable,
|
||||
int x, int y, int w, int h) = 0;
|
||||
|
||||
virtual void handleClipboardCaps(rdr::U32 flags,
|
||||
const rdr::U32* lengths);
|
||||
virtual void handleClipboardRequest(rdr::U32 flags);
|
||||
virtual void handleClipboardPeek(rdr::U32 flags);
|
||||
virtual void handleClipboardNotify(rdr::U32 flags);
|
||||
virtual void handleClipboardProvide(rdr::U32 flags,
|
||||
const size_t* lengths,
|
||||
const rdr::U8* const* data);
|
||||
|
||||
virtual void sendStats() = 0;
|
||||
|
||||
virtual bool canChangeKasmSettings() const = 0;
|
||||
|
||||
@@ -18,8 +18,11 @@
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <rdr/InStream.h>
|
||||
#include <rdr/ZlibInStream.h>
|
||||
|
||||
#include <rfb/msgTypes.h>
|
||||
#include <rfb/qemuTypes.h>
|
||||
#include <rfb/clipboardTypes.h>
|
||||
#include <rfb/Exception.h>
|
||||
#include <rfb/util.h>
|
||||
#include <rfb/SMsgHandler.h>
|
||||
@@ -224,11 +227,15 @@ void SMsgReader::readPointerEvent()
|
||||
void SMsgReader::readClientCutText()
|
||||
{
|
||||
is->skip(3);
|
||||
int len = is->readU32();
|
||||
if (len < 0) {
|
||||
throw Exception("Cut text too long.");
|
||||
rdr::U32 len = is->readU32();
|
||||
|
||||
if (len & 0x80000000) {
|
||||
rdr::S32 slen = len;
|
||||
slen = -slen;
|
||||
readExtendedClipboard(slen);
|
||||
return;
|
||||
}
|
||||
if (len > maxCutText) {
|
||||
if (len > (size_t)maxCutText) {
|
||||
is->skip(len);
|
||||
vlog.error("Cut text too long (%d bytes) - ignoring", len);
|
||||
return;
|
||||
@@ -239,6 +246,100 @@ void SMsgReader::readClientCutText()
|
||||
handler->clientCutText(ca.buf, len);
|
||||
}
|
||||
|
||||
void SMsgReader::readExtendedClipboard(rdr::S32 len)
|
||||
{
|
||||
rdr::U32 flags;
|
||||
rdr::U32 action;
|
||||
|
||||
if (len < 4)
|
||||
throw Exception("Invalid extended clipboard message");
|
||||
if (len > maxCutText) {
|
||||
vlog.error("Extended clipboard message too long (%d bytes) - ignoring", len);
|
||||
is->skip(len);
|
||||
return;
|
||||
}
|
||||
|
||||
flags = is->readU32();
|
||||
action = flags & clipboardActionMask;
|
||||
|
||||
if (action & clipboardCaps) {
|
||||
int i;
|
||||
size_t num;
|
||||
rdr::U32 lengths[16];
|
||||
|
||||
num = 0;
|
||||
for (i = 0;i < 16;i++) {
|
||||
if (flags & (1 << i))
|
||||
num++;
|
||||
}
|
||||
|
||||
if (len < (rdr::S32)(4 + 4*num))
|
||||
throw Exception("Invalid extended clipboard message");
|
||||
|
||||
num = 0;
|
||||
for (i = 0;i < 16;i++) {
|
||||
if (flags & (1 << i))
|
||||
lengths[num++] = is->readU32();
|
||||
}
|
||||
|
||||
handler->handleClipboardCaps(flags, lengths);
|
||||
} else if (action == clipboardProvide) {
|
||||
rdr::ZlibInStream zis;
|
||||
|
||||
int i;
|
||||
size_t num;
|
||||
size_t lengths[16];
|
||||
rdr::U8* buffers[16];
|
||||
|
||||
zis.setUnderlying(is, len - 4);
|
||||
|
||||
num = 0;
|
||||
for (i = 0;i < 16;i++) {
|
||||
if (!(flags & 1 << i))
|
||||
continue;
|
||||
|
||||
lengths[num] = zis.readU32();
|
||||
if (lengths[num] > (size_t)maxCutText) {
|
||||
vlog.error("Extended clipboard data too long (%d bytes) - ignoring",
|
||||
(unsigned)lengths[num]);
|
||||
zis.skip(lengths[num]);
|
||||
flags &= ~(1 << i);
|
||||
continue;
|
||||
}
|
||||
|
||||
buffers[num] = new rdr::U8[lengths[num]];
|
||||
zis.readBytes(buffers[num], lengths[num]);
|
||||
num++;
|
||||
}
|
||||
|
||||
zis.flushUnderlying();
|
||||
zis.setUnderlying(NULL, 0);
|
||||
|
||||
handler->handleClipboardProvide(flags, lengths, buffers);
|
||||
|
||||
num = 0;
|
||||
for (i = 0;i < 16;i++) {
|
||||
if (!(flags & 1 << i))
|
||||
continue;
|
||||
delete [] buffers[num++];
|
||||
}
|
||||
} else {
|
||||
switch (action) {
|
||||
case clipboardRequest:
|
||||
handler->handleClipboardRequest(flags);
|
||||
break;
|
||||
case clipboardPeek:
|
||||
handler->handleClipboardPeek(flags);
|
||||
break;
|
||||
case clipboardNotify:
|
||||
handler->handleClipboardNotify(flags);
|
||||
break;
|
||||
default:
|
||||
throw Exception("Invalid extended clipboard action");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SMsgReader::readRequestStats()
|
||||
{
|
||||
is->skip(3);
|
||||
|
||||
@@ -55,6 +55,7 @@ namespace rfb {
|
||||
void readKeyEvent();
|
||||
void readPointerEvent();
|
||||
void readClientCutText();
|
||||
void readExtendedClipboard(rdr::S32 len);
|
||||
void readRequestStats();
|
||||
|
||||
void readQEMUMessage();
|
||||
|
||||
@@ -19,8 +19,12 @@
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <rdr/OutStream.h>
|
||||
#include <rdr/MemOutStream.h>
|
||||
#include <rdr/ZlibOutStream.h>
|
||||
|
||||
#include <rfb/msgTypes.h>
|
||||
#include <rfb/fenceTypes.h>
|
||||
#include <rfb/clipboardTypes.h>
|
||||
#include <rfb/Exception.h>
|
||||
#include <rfb/ConnParams.h>
|
||||
#include <rfb/UpdateTracker.h>
|
||||
@@ -39,6 +43,7 @@ SMsgWriter::SMsgWriter(ConnParams* cp_, rdr::OutStream* os_)
|
||||
needSetDesktopSize(false), needExtendedDesktopSize(false),
|
||||
needSetDesktopName(false), needSetCursor(false),
|
||||
needSetXCursor(false), needSetCursorWithAlpha(false),
|
||||
needCursorPos(false),
|
||||
needLEDState(false), needQEMUKeyEvent(false)
|
||||
{
|
||||
}
|
||||
@@ -88,6 +93,112 @@ void SMsgWriter::writeServerCutText(const char* str, int len)
|
||||
endMsg();
|
||||
}
|
||||
|
||||
void SMsgWriter::writeClipboardCaps(rdr::U32 caps,
|
||||
const rdr::U32* lengths)
|
||||
{
|
||||
size_t i, count;
|
||||
|
||||
if (!cp->supportsExtendedClipboard)
|
||||
throw Exception("Client does not support extended clipboard");
|
||||
|
||||
count = 0;
|
||||
for (i = 0;i < 16;i++) {
|
||||
if (caps & (1 << i))
|
||||
count++;
|
||||
}
|
||||
|
||||
startMsg(msgTypeServerCutText);
|
||||
os->pad(3);
|
||||
os->writeS32(-(4 + 4 * count));
|
||||
|
||||
os->writeU32(caps | clipboardCaps);
|
||||
|
||||
count = 0;
|
||||
for (i = 0;i < 16;i++) {
|
||||
if (caps & (1 << i))
|
||||
os->writeU32(lengths[count++]);
|
||||
}
|
||||
|
||||
endMsg();
|
||||
}
|
||||
|
||||
void SMsgWriter::writeClipboardRequest(rdr::U32 flags)
|
||||
{
|
||||
if (!cp->supportsExtendedClipboard)
|
||||
throw Exception("Client does not support extended clipboard");
|
||||
if (!(cp->clipboardFlags() & clipboardRequest))
|
||||
throw Exception("Client does not support clipboard \"request\" action");
|
||||
|
||||
startMsg(msgTypeServerCutText);
|
||||
os->pad(3);
|
||||
os->writeS32(-4);
|
||||
os->writeU32(flags | clipboardRequest);
|
||||
endMsg();
|
||||
}
|
||||
|
||||
void SMsgWriter::writeClipboardPeek(rdr::U32 flags)
|
||||
{
|
||||
if (!cp->supportsExtendedClipboard)
|
||||
throw Exception("Client does not support extended clipboard");
|
||||
if (!(cp->clipboardFlags() & clipboardPeek))
|
||||
throw Exception("Client does not support clipboard \"peek\" action");
|
||||
|
||||
startMsg(msgTypeServerCutText);
|
||||
os->pad(3);
|
||||
os->writeS32(-4);
|
||||
os->writeU32(flags | clipboardPeek);
|
||||
endMsg();
|
||||
}
|
||||
|
||||
void SMsgWriter::writeClipboardNotify(rdr::U32 flags)
|
||||
{
|
||||
if (!cp->supportsExtendedClipboard)
|
||||
throw Exception("Client does not support extended clipboard");
|
||||
if (!(cp->clipboardFlags() & clipboardNotify))
|
||||
throw Exception("Client does not support clipboard \"notify\" action");
|
||||
|
||||
startMsg(msgTypeServerCutText);
|
||||
os->pad(3);
|
||||
os->writeS32(-4);
|
||||
os->writeU32(flags | clipboardNotify);
|
||||
endMsg();
|
||||
}
|
||||
|
||||
void SMsgWriter::writeClipboardProvide(rdr::U32 flags,
|
||||
const size_t* lengths,
|
||||
const rdr::U8* const* data)
|
||||
{
|
||||
rdr::MemOutStream mos;
|
||||
rdr::ZlibOutStream zos;
|
||||
|
||||
int i, count;
|
||||
|
||||
if (!cp->supportsExtendedClipboard)
|
||||
throw Exception("Client does not support extended clipboard");
|
||||
if (!(cp->clipboardFlags() & clipboardProvide))
|
||||
throw Exception("Client does not support clipboard \"provide\" action");
|
||||
|
||||
zos.setUnderlying(&mos);
|
||||
|
||||
count = 0;
|
||||
for (i = 0;i < 16;i++) {
|
||||
if (!(flags & (1 << i)))
|
||||
continue;
|
||||
zos.writeU32(lengths[count]);
|
||||
zos.writeBytes(data[count], lengths[count]);
|
||||
count++;
|
||||
}
|
||||
|
||||
zos.flush();
|
||||
|
||||
startMsg(msgTypeServerCutText);
|
||||
os->pad(3);
|
||||
os->writeS32(-(4 + mos.length()));
|
||||
os->writeU32(flags | clipboardProvide);
|
||||
os->writeBytes(mos.data(), mos.length());
|
||||
endMsg();
|
||||
}
|
||||
|
||||
void SMsgWriter::writeStats(const char* str, int len)
|
||||
{
|
||||
startMsg(msgTypeStats);
|
||||
@@ -204,6 +315,14 @@ bool SMsgWriter::writeSetCursorWithAlpha()
|
||||
return true;
|
||||
}
|
||||
|
||||
void SMsgWriter::writeCursorPos()
|
||||
{
|
||||
if (!cp->supportsEncoding(pseudoEncodingVMwareCursorPosition))
|
||||
throw Exception("Client does not support cursor position");
|
||||
|
||||
needCursorPos = true;
|
||||
}
|
||||
|
||||
bool SMsgWriter::writeLEDState()
|
||||
{
|
||||
if (!cp->supportsLEDState)
|
||||
@@ -232,6 +351,8 @@ bool SMsgWriter::needFakeUpdate()
|
||||
return true;
|
||||
if (needSetCursor || needSetXCursor || needSetCursorWithAlpha)
|
||||
return true;
|
||||
if (needCursorPos)
|
||||
return true;
|
||||
if (needLEDState)
|
||||
return true;
|
||||
if (needQEMUKeyEvent)
|
||||
@@ -284,6 +405,8 @@ void SMsgWriter::writeFramebufferUpdateStart(int nRects)
|
||||
nRects++;
|
||||
if (needSetCursorWithAlpha)
|
||||
nRects++;
|
||||
if (needCursorPos)
|
||||
nRects++;
|
||||
if (needLEDState)
|
||||
nRects++;
|
||||
if (needQEMUKeyEvent)
|
||||
@@ -399,6 +522,18 @@ void SMsgWriter::writePseudoRects()
|
||||
needSetCursorWithAlpha = false;
|
||||
}
|
||||
|
||||
if (needCursorPos) {
|
||||
const Point& cursorPos = cp->cursorPos();
|
||||
|
||||
if (cp->supportsEncoding(pseudoEncodingVMwareCursorPosition)) {
|
||||
writeSetVMwareCursorPositionRect(cursorPos.x, cursorPos.y);
|
||||
} else {
|
||||
throw Exception("Client does not support cursor position");
|
||||
}
|
||||
|
||||
needCursorPos = false;
|
||||
}
|
||||
|
||||
if (needSetDesktopName) {
|
||||
writeSetDesktopNameRect(cp->name());
|
||||
needSetDesktopName = false;
|
||||
@@ -577,6 +712,20 @@ void SMsgWriter::writeSetCursorWithAlphaRect(int width, int height,
|
||||
}
|
||||
}
|
||||
|
||||
void SMsgWriter::writeSetVMwareCursorPositionRect(int hotspotX, int hotspotY)
|
||||
{
|
||||
if (!cp->supportsEncoding(pseudoEncodingVMwareCursorPosition))
|
||||
throw Exception("Client does not support cursor position");
|
||||
if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
|
||||
throw Exception("SMsgWriter::writeSetVMwareCursorRect: nRects out of sync");
|
||||
|
||||
os->writeS16(hotspotX);
|
||||
os->writeS16(hotspotY);
|
||||
os->writeU16(0);
|
||||
os->writeU16(0);
|
||||
os->writeU32(pseudoEncodingVMwareCursorPosition);
|
||||
}
|
||||
|
||||
void SMsgWriter::writeLEDStateRect(rdr::U8 state)
|
||||
{
|
||||
if (!cp->supportsLEDState)
|
||||
|
||||
@@ -55,6 +55,14 @@ namespace rfb {
|
||||
// writeBell() and writeServerCutText() do the obvious thing.
|
||||
void writeBell();
|
||||
void writeServerCutText(const char* str, int len);
|
||||
|
||||
void writeClipboardCaps(rdr::U32 caps, const rdr::U32* lengths);
|
||||
void writeClipboardRequest(rdr::U32 flags);
|
||||
void writeClipboardPeek(rdr::U32 flags);
|
||||
void writeClipboardNotify(rdr::U32 flags);
|
||||
void writeClipboardProvide(rdr::U32 flags, const size_t* lengths,
|
||||
const rdr::U8* const* data);
|
||||
|
||||
void writeStats(const char* str, int len);
|
||||
|
||||
// writeFence() sends a new fence request or response to the client.
|
||||
@@ -83,6 +91,9 @@ namespace rfb {
|
||||
bool writeSetXCursor();
|
||||
bool writeSetCursorWithAlpha();
|
||||
|
||||
// Notifies the client that the cursor pointer was moved by the server.
|
||||
void writeCursorPos();
|
||||
|
||||
// Same for LED state message
|
||||
bool writeLEDState();
|
||||
|
||||
@@ -138,6 +149,7 @@ namespace rfb {
|
||||
void writeSetCursorWithAlphaRect(int width, int height,
|
||||
int hotspotX, int hotspotY,
|
||||
const rdr::U8* data);
|
||||
void writeSetVMwareCursorPositionRect(int hotspotX, int hotspotY);
|
||||
void writeLEDStateRect(rdr::U8 state);
|
||||
void writeQEMUKeyEventRect();
|
||||
|
||||
@@ -153,6 +165,7 @@ namespace rfb {
|
||||
bool needSetCursor;
|
||||
bool needSetXCursor;
|
||||
bool needSetCursorWithAlpha;
|
||||
bool needCursorPos;
|
||||
bool needLEDState;
|
||||
bool needQEMUKeyEvent;
|
||||
|
||||
|
||||
@@ -199,6 +199,10 @@ rfb::IntParameter rfb::Server::videoScaling
|
||||
("VideoScaling",
|
||||
"Scaling method to use when in downscaled video mode. 0 = nearest, 1 = bilinear, 2 = prog bilinear",
|
||||
2, 0, 2);
|
||||
rfb::IntParameter rfb::Server::x264Bitrate
|
||||
("x264Bitrate",
|
||||
"Enable x264 encoding for full-screen video, in kbps. Default 0 (off)",
|
||||
0, 0, 50000);
|
||||
rfb::BoolParameter rfb::Server::printVideoArea
|
||||
("PrintVideoArea",
|
||||
"Print the detected video area % value.",
|
||||
|
||||
@@ -59,6 +59,7 @@ namespace rfb {
|
||||
static IntParameter videoOutTime;
|
||||
static IntParameter videoArea;
|
||||
static IntParameter videoScaling;
|
||||
static IntParameter x264Bitrate;
|
||||
static StringParameter kasmPasswordFile;
|
||||
static BoolParameter printVideoArea;
|
||||
static BoolParameter protocol3_3;
|
||||
|
||||
@@ -24,7 +24,8 @@ namespace rfb {
|
||||
const unsigned int tightJpeg = 0x09;
|
||||
const unsigned int tightPng = 0x0a;
|
||||
const unsigned int tightWebp = 0x0b;
|
||||
const unsigned int tightMaxSubencoding = 0x0b;
|
||||
const unsigned int tightX264 = 0x0c;
|
||||
const unsigned int tightMaxSubencoding = 0x0c;
|
||||
|
||||
// Filters to improve compression efficiency
|
||||
const unsigned int tightFilterCopy = 0x00;
|
||||
|
||||
507
common/rfb/TightX264Encoder.cxx
Normal file
507
common/rfb/TightX264Encoder.cxx
Normal file
@@ -0,0 +1,507 @@
|
||||
/* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved.
|
||||
* Copyright (C) 2011 D. R. Commander. All Rights Reserved.
|
||||
* Copyright 2014 Pierre Ossman for Cendio AB
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this software; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
#include <rdr/OutStream.h>
|
||||
#include <rfb/EncCache.h>
|
||||
#include <rfb/encodings.h>
|
||||
#include <rfb/LogWriter.h>
|
||||
#include <rfb/SConnection.h>
|
||||
#include <rfb/ServerCore.h>
|
||||
#include <rfb/PixelBuffer.h>
|
||||
#include <rfb/TightX264Encoder.h>
|
||||
#include <rfb/TightConstants.h>
|
||||
|
||||
#include <webp/encode.h>
|
||||
#include <x264.h>
|
||||
#include "nvidia.h"
|
||||
#include "mp4.h"
|
||||
|
||||
#define MAX_FRAMELEN (1024 * 1024)
|
||||
|
||||
using namespace rfb;
|
||||
|
||||
static LogWriter vlog("x264");
|
||||
|
||||
static const PixelFormat pfRGBX(32, 24, false, true, 255, 255, 255, 0, 8, 16);
|
||||
static const PixelFormat pfBGRX(32, 24, false, true, 255, 255, 255, 16, 8, 0);
|
||||
|
||||
bool TightX264Encoder::skip_nvidia = false;
|
||||
|
||||
TightX264Encoder::TightX264Encoder(SConnection* conn, EncCache *cache_, uint8_t cacheType_) :
|
||||
Encoder(conn, encodingTight, (EncoderFlags)(EncoderUseNativePF | EncoderLossy), -1),
|
||||
keyframe(true), enc(NULL), params(NULL), mux(NULL), muxstate(NULL), framectr(0),
|
||||
nvidia_init_done(false), using_nvidia(true),
|
||||
encCache(cache_), cacheType(cacheType_),
|
||||
framebuf(NULL), framelen(0), bitbuf(NULL), myw(0), myh(0)
|
||||
{
|
||||
params = new x264_param_t;
|
||||
x264_param_default_preset(params, "veryfast", "zerolatency");
|
||||
|
||||
params->i_threads = X264_THREADS_AUTO;
|
||||
params->i_fps_num = params->i_keyint_max = rfb::Server::frameRate;
|
||||
params->i_fps_den = 1;
|
||||
params->rc.i_rc_method = X264_RC_ABR;
|
||||
params->rc.i_bitrate = rfb::Server::x264Bitrate;
|
||||
params->i_csp = X264_CSP_I420;
|
||||
params->i_log_level = X264_LOG_WARNING;
|
||||
params->b_annexb = 0;
|
||||
|
||||
framebuf = new uint8_t[MAX_FRAMELEN];
|
||||
bitbuf = new uint8_t[MAX_FRAMELEN];
|
||||
mux = new Mp4Context;
|
||||
memset(mux, 0, sizeof(Mp4Context));
|
||||
muxstate = new Mp4State;
|
||||
memset(muxstate, 0, sizeof(Mp4State));
|
||||
}
|
||||
|
||||
TightX264Encoder::~TightX264Encoder()
|
||||
{
|
||||
delete params;
|
||||
delete [] framebuf;
|
||||
delete [] bitbuf;
|
||||
delete mux;
|
||||
delete muxstate;
|
||||
}
|
||||
|
||||
bool TightX264Encoder::isSupported()
|
||||
{
|
||||
if (!conn->cp.supportsEncoding(encodingTight))
|
||||
return false;
|
||||
|
||||
// Unconditional support if enabled
|
||||
return rfb::Server::x264Bitrate != 0;
|
||||
}
|
||||
|
||||
void TightX264Encoder::mp4_write_callback(const void *buffer, size_t size)
|
||||
{
|
||||
if (framelen + size > MAX_FRAMELEN)
|
||||
vlog.error("Tried to write too large a frame, %lu bytes", framelen + size);
|
||||
|
||||
memcpy(&framebuf[framelen], buffer, size);
|
||||
framelen += size;
|
||||
}
|
||||
|
||||
void TightX264Encoder::writeRect(const PixelBuffer* pb, const Palette& palette)
|
||||
{
|
||||
const rdr::U8* buffer;
|
||||
int stride;
|
||||
|
||||
rdr::OutStream* os;
|
||||
|
||||
if (pb->width() < 320)
|
||||
return; // Sometimes we get sent an 1x1 frame, or a cursor
|
||||
|
||||
uint32_t w, h;
|
||||
w = pb->width();
|
||||
h = pb->height();
|
||||
|
||||
os = conn->getOutStream();
|
||||
|
||||
if (using_nvidia) {
|
||||
|
||||
if (w != myw || h != myh) {
|
||||
if (nvidia_init_done)
|
||||
nvidia_unload();
|
||||
nvidia_init_done = false;
|
||||
}
|
||||
|
||||
if (!nvidia_init_done) {
|
||||
if (nvidia_init(w, h, rfb::Server::x264Bitrate,
|
||||
rfb::Server::frameRate) != 0) {
|
||||
vlog.error("nvidia init failed, disabling h264");
|
||||
rfb::Server::x264Bitrate.setParam(0);
|
||||
return;
|
||||
}
|
||||
nvidia_init_done = true;
|
||||
myw = w;
|
||||
myh = h;
|
||||
}
|
||||
|
||||
uint32_t cachelen;
|
||||
const void *cachedata;
|
||||
if (encCache->enabled &&
|
||||
(cachedata = encCache->get(cacheType, framectr, 0, w, h, cachelen))) {
|
||||
os->writeU8(tightX264 << 4);
|
||||
writeCompact(cachelen, os);
|
||||
os->writeBytes(cachedata, cachelen);
|
||||
framectr++;
|
||||
return;
|
||||
}
|
||||
|
||||
if (keyframe) {
|
||||
framectr = 0;
|
||||
keyframe = false;
|
||||
|
||||
free(mux->buf_header.buf);
|
||||
free(mux->buf_mdat.buf);
|
||||
free(mux->buf_moof.buf);
|
||||
memset(mux, 0, sizeof(Mp4Context));
|
||||
memset(muxstate, 0, sizeof(Mp4State));
|
||||
}
|
||||
|
||||
mux->framerate = rfb::Server::frameRate;
|
||||
mux->w = w;
|
||||
mux->h = h;
|
||||
|
||||
buffer = pb->getBuffer(pb->getRect(), &stride);
|
||||
|
||||
if (!pfBGRX.equal(pb->getPF())) {
|
||||
vlog.error("unsupported pixel format");
|
||||
return;
|
||||
}
|
||||
|
||||
// Encode
|
||||
uint32_t bitlen;
|
||||
if (nvenc_frame(buffer, framectr++, bitbuf, bitlen) != 0) {
|
||||
vlog.error("encoding failed");
|
||||
return;
|
||||
}
|
||||
|
||||
// Need to parse NALs out of the stream
|
||||
const uint8_t prefix[3] = { 0, 0, 1 };
|
||||
const uint8_t *nalptr = bitbuf;
|
||||
int i_nals = 0;
|
||||
const uint8_t *nalstarts[32] = { NULL };
|
||||
uint32_t nallens[32] = { 0 };
|
||||
uint32_t remlen = bitlen;
|
||||
|
||||
while (1) {
|
||||
const uint8_t *next = (uint8_t *) memmem(nalptr, remlen, prefix, 3);
|
||||
if (!next)
|
||||
break;
|
||||
|
||||
remlen -= (next + 3) - nalptr;
|
||||
nalptr = nalstarts[i_nals] = next + 3;
|
||||
|
||||
i_nals++;
|
||||
};
|
||||
|
||||
// Lens
|
||||
int i;
|
||||
for (i = 0; i < i_nals; i++) {
|
||||
if (i == i_nals - 1) {
|
||||
nallens[i] = bitbuf + bitlen - nalstarts[i];
|
||||
} else {
|
||||
nallens[i] = nalstarts[i + 1] - nalstarts[i] - 3;
|
||||
}
|
||||
}
|
||||
|
||||
// Mux
|
||||
framelen = 0;
|
||||
os->writeU8(tightX264 << 4);
|
||||
|
||||
for (i = 0; i < i_nals; i++) {
|
||||
uint32_t pack_len = nallens[i];
|
||||
const uint8_t *pack_data = nalstarts[i];
|
||||
|
||||
struct NAL nal; nal_parse_header(&nal, pack_data[0]);
|
||||
|
||||
switch (nal.unit_type) {
|
||||
case NalUnitType_SPS: { set_sps(mux, pack_data, pack_len); break; }
|
||||
case NalUnitType_PPS: { set_pps(mux, pack_data, pack_len); break; }
|
||||
case NalUnitType_CodedSliceIdr:
|
||||
case NalUnitType_CodedSliceNonIdr: {
|
||||
// Write all remaining NALs under the assumption they are the same type.
|
||||
const uint32_t origlen = pack_len;
|
||||
pack_len = bitbuf + bitlen - pack_data;
|
||||
set_slice(mux, pack_data, origlen, pack_len, nal.unit_type);
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (nal.unit_type != NalUnitType_CodedSliceIdr &&
|
||||
nal.unit_type != NalUnitType_CodedSliceNonIdr)
|
||||
continue;
|
||||
|
||||
enum BufError err;
|
||||
if (!muxstate->header_sent) {
|
||||
struct BitBuf header_buf;
|
||||
err = get_header(mux, &header_buf); chk_err_continue
|
||||
mp4_write_callback(header_buf.buf, header_buf.offset);
|
||||
|
||||
muxstate->sequence_number = 1;
|
||||
muxstate->base_data_offset = header_buf.offset;
|
||||
muxstate->base_media_decode_time = 0;
|
||||
muxstate->header_sent = true;
|
||||
muxstate->nals_count = 0;
|
||||
muxstate->default_sample_duration = default_sample_size;
|
||||
}
|
||||
|
||||
err = set_mp4_state(mux, muxstate); chk_err_continue
|
||||
{
|
||||
struct BitBuf moof_buf;
|
||||
err = get_moof(mux, &moof_buf); chk_err_continue
|
||||
mp4_write_callback(moof_buf.buf, moof_buf.offset);
|
||||
}
|
||||
{
|
||||
struct BitBuf mdat_buf;
|
||||
err = get_mdat(mux, &mdat_buf); chk_err_continue
|
||||
mp4_write_callback(mdat_buf.buf, mdat_buf.offset);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (encCache->enabled) {
|
||||
void *tmp = malloc(framelen);
|
||||
memcpy(tmp, framebuf, framelen);
|
||||
encCache->add(cacheType, framectr, 0, w, h, framelen, tmp);
|
||||
}
|
||||
|
||||
writeCompact(framelen, os);
|
||||
os->writeBytes(framebuf, framelen);
|
||||
} else {
|
||||
w += w & 1;
|
||||
h += h & 1;
|
||||
|
||||
params->i_width = w;
|
||||
params->i_height = h;
|
||||
|
||||
x264_param_apply_profile(params, "baseline");
|
||||
|
||||
uint32_t cachelen;
|
||||
const void *cachedata;
|
||||
if (encCache->enabled &&
|
||||
(cachedata = encCache->get(cacheType, framectr, 0, w, h, cachelen))) {
|
||||
os->writeU8(tightX264 << 4);
|
||||
writeCompact(cachelen, os);
|
||||
os->writeBytes(cachedata, cachelen);
|
||||
framectr++;
|
||||
return;
|
||||
}
|
||||
|
||||
if (keyframe) {
|
||||
framectr = 0;
|
||||
keyframe = false;
|
||||
|
||||
free(mux->buf_header.buf);
|
||||
free(mux->buf_mdat.buf);
|
||||
free(mux->buf_moof.buf);
|
||||
memset(mux, 0, sizeof(Mp4Context));
|
||||
memset(muxstate, 0, sizeof(Mp4State));
|
||||
}
|
||||
|
||||
mux->framerate = rfb::Server::frameRate;
|
||||
mux->w = params->i_width;
|
||||
mux->h = params->i_height;
|
||||
|
||||
if (!enc) {
|
||||
enc = x264_encoder_open(params);
|
||||
}
|
||||
|
||||
buffer = pb->getBuffer(pb->getRect(), &stride);
|
||||
|
||||
// Convert it to yuv420 using libwebp's helper functions
|
||||
WebPPicture pic;
|
||||
|
||||
WebPPictureInit(&pic);
|
||||
pic.width = pb->getRect().width();
|
||||
pic.height = pb->getRect().height();
|
||||
|
||||
bool freebuffer = false;
|
||||
if (pic.width & 1 || pic.height & 1) {
|
||||
// Expand to divisible-by-2 for x264
|
||||
freebuffer = true;
|
||||
const uint32_t oldw = pic.width;
|
||||
const uint32_t oldh = pic.height;
|
||||
pic.width += pic.width & 1;
|
||||
pic.height += pic.height & 1;
|
||||
stride = pic.width;
|
||||
const rdr::U8 *oldbuffer = buffer;
|
||||
buffer = (const rdr::U8*) calloc(pic.width * pic.height, 4);
|
||||
|
||||
uint32_t y;
|
||||
for (y = 0; y < oldh; y++)
|
||||
memcpy((void *) &buffer[y * stride * 4], &oldbuffer[y * oldw * 4], oldw * 4);
|
||||
}
|
||||
|
||||
if (pfRGBX.equal(pb->getPF())) {
|
||||
WebPPictureImportRGBX(&pic, buffer, stride * 4);
|
||||
} else if (pfBGRX.equal(pb->getPF())) {
|
||||
WebPPictureImportBGRX(&pic, buffer, stride * 4);
|
||||
} else {
|
||||
rdr::U8* tmpbuf = new rdr::U8[pic.width * pic.height * 3];
|
||||
pb->getPF().rgbFromBuffer(tmpbuf, (const rdr::U8 *) buffer, pic.width, stride, pic.height);
|
||||
stride = pic.width * 3;
|
||||
|
||||
WebPPictureImportRGB(&pic, tmpbuf, stride);
|
||||
delete [] tmpbuf;
|
||||
}
|
||||
|
||||
if (freebuffer)
|
||||
free((void *) buffer);
|
||||
|
||||
// Wrap
|
||||
x264_picture_t pic_in, pic_out;
|
||||
x264_picture_init(&pic_in);
|
||||
|
||||
pic_in.img.i_csp = X264_CSP_I420;
|
||||
pic_in.img.i_plane = 3;
|
||||
|
||||
pic_in.img.plane[0] = pic.y;
|
||||
pic_in.img.plane[1] = pic.u;
|
||||
pic_in.img.plane[2] = pic.v;
|
||||
|
||||
pic_in.img.i_stride[0] = pic.y_stride;
|
||||
pic_in.img.i_stride[1] = pic_in.img.i_stride[2] = pic.uv_stride;
|
||||
|
||||
pic_in.i_pts = framectr++;
|
||||
|
||||
// Encode
|
||||
int i_nals;
|
||||
x264_nal_t *nals;
|
||||
const int len = x264_encoder_encode(enc, &nals, &i_nals, &pic_in, &pic_out);
|
||||
|
||||
if (len <= 0 || i_nals <= 0)
|
||||
vlog.info("encoding error");
|
||||
|
||||
// Mux
|
||||
framelen = 0;
|
||||
os->writeU8(tightX264 << 4);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < i_nals; i++) {
|
||||
uint32_t pack_len = nals[i].i_payload - 4;
|
||||
const uint8_t *pack_data = nals[i].p_payload;
|
||||
|
||||
pack_data += 4; // Skip size
|
||||
|
||||
struct NAL nal; nal_parse_header(&nal, pack_data[0]);
|
||||
|
||||
switch (nal.unit_type) {
|
||||
case NalUnitType_SPS: { set_sps(mux, pack_data, pack_len); break; }
|
||||
case NalUnitType_PPS: { set_pps(mux, pack_data, pack_len); break; }
|
||||
case NalUnitType_CodedSliceIdr:
|
||||
case NalUnitType_CodedSliceNonIdr: {
|
||||
// Write all remaining NALs under the assumption they are the same type.
|
||||
const uint32_t origlen = pack_len;
|
||||
int j;
|
||||
for (j = i + 1; j < i_nals; j++)
|
||||
pack_len += nals[j].i_payload;
|
||||
set_slice(mux, pack_data, origlen, pack_len, nal.unit_type);
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (nal.unit_type != NalUnitType_CodedSliceIdr &&
|
||||
nal.unit_type != NalUnitType_CodedSliceNonIdr)
|
||||
continue;
|
||||
|
||||
enum BufError err;
|
||||
if (!muxstate->header_sent) {
|
||||
struct BitBuf header_buf;
|
||||
err = get_header(mux, &header_buf); chk_err_continue
|
||||
mp4_write_callback(header_buf.buf, header_buf.offset);
|
||||
|
||||
muxstate->sequence_number = 1;
|
||||
muxstate->base_data_offset = header_buf.offset;
|
||||
muxstate->base_media_decode_time = 0;
|
||||
muxstate->header_sent = true;
|
||||
muxstate->nals_count = 0;
|
||||
muxstate->default_sample_duration = default_sample_size;
|
||||
}
|
||||
|
||||
err = set_mp4_state(mux, muxstate); chk_err_continue
|
||||
{
|
||||
struct BitBuf moof_buf;
|
||||
err = get_moof(mux, &moof_buf); chk_err_continue
|
||||
mp4_write_callback(moof_buf.buf, moof_buf.offset);
|
||||
}
|
||||
{
|
||||
struct BitBuf mdat_buf;
|
||||
err = get_mdat(mux, &mdat_buf); chk_err_continue
|
||||
mp4_write_callback(mdat_buf.buf, mdat_buf.offset);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (encCache->enabled) {
|
||||
void *tmp = malloc(framelen);
|
||||
memcpy(tmp, framebuf, framelen);
|
||||
encCache->add(cacheType, framectr, 0, w, h, framelen, tmp);
|
||||
}
|
||||
|
||||
writeCompact(framelen, os);
|
||||
os->writeBytes(framebuf, framelen);
|
||||
|
||||
// Cleanup
|
||||
WebPPictureFree(&pic);
|
||||
x264_encoder_close(enc);
|
||||
enc = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void TightX264Encoder::writeSolidRect(int width, int height,
|
||||
const PixelFormat& pf,
|
||||
const rdr::U8* colour)
|
||||
{
|
||||
// FIXME: Add a shortcut in the X264 compressor to handle this case
|
||||
// without having to use the default fallback which is very slow.
|
||||
Encoder::writeSolidRect(width, height, pf, colour);
|
||||
}
|
||||
|
||||
void TightX264Encoder::writeCompact(rdr::U32 value, rdr::OutStream* os) const
|
||||
{
|
||||
// Copied from TightEncoder as it's overkill to inherit just for this
|
||||
rdr::U8 b;
|
||||
|
||||
b = value & 0x7F;
|
||||
if (value <= 0x7F) {
|
||||
os->writeU8(b);
|
||||
} else {
|
||||
os->writeU8(b | 0x80);
|
||||
b = value >> 7 & 0x7F;
|
||||
if (value <= 0x3FFF) {
|
||||
os->writeU8(b);
|
||||
} else {
|
||||
os->writeU8(b | 0x80);
|
||||
os->writeU8(value >> 14 & 0xFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool TightX264Encoder::tryInit(const PixelBuffer* pb) {
|
||||
if (nvidia_init_done)
|
||||
return true;
|
||||
|
||||
uint32_t w, h;
|
||||
w = pb->width();
|
||||
h = pb->height();
|
||||
|
||||
if (skip_nvidia || nvidia_init(w, h, rfb::Server::x264Bitrate,
|
||||
rfb::Server::frameRate) != 0) {
|
||||
if (!skip_nvidia)
|
||||
vlog.error("nvidia init failed, falling back to x264");
|
||||
using_nvidia = false;
|
||||
nvidia_init_done = true;
|
||||
skip_nvidia = true;
|
||||
myw = w;
|
||||
myh = h;
|
||||
return true;
|
||||
}
|
||||
|
||||
nvidia_init_done = true;
|
||||
myw = w;
|
||||
myh = h;
|
||||
|
||||
return true;
|
||||
}
|
||||
82
common/rfb/TightX264Encoder.h
Normal file
82
common/rfb/TightX264Encoder.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved.
|
||||
* Copyright (C) 2011 D. R. Commander
|
||||
* Copyright 2014 Pierre Ossman for Cendio AB
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this software; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
#ifndef __RFB_TIGHTX264ENCODER_H__
|
||||
#define __RFB_TIGHTX264ENCODER_H__
|
||||
|
||||
#include <rfb/Encoder.h>
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
struct x264_t;
|
||||
struct x264_param_t;
|
||||
struct Mp4Context;
|
||||
struct Mp4State;
|
||||
|
||||
namespace rfb {
|
||||
|
||||
class EncCache;
|
||||
|
||||
class TightX264Encoder : public Encoder {
|
||||
public:
|
||||
TightX264Encoder(SConnection* conn, EncCache *encCache, uint8_t cacheType);
|
||||
virtual ~TightX264Encoder();
|
||||
|
||||
virtual bool isSupported();
|
||||
|
||||
virtual void setQualityLevel(int level) {}
|
||||
virtual void setFineQualityLevel(int quality, int subsampling) {}
|
||||
|
||||
virtual void writeRect(const PixelBuffer* pb, const Palette& palette);
|
||||
virtual void writeSolidRect(int width, int height,
|
||||
const PixelFormat& pf,
|
||||
const rdr::U8* colour);
|
||||
|
||||
virtual void setKeyframe() { keyframe = true; }
|
||||
|
||||
bool tryInit(const PixelBuffer* pb);
|
||||
|
||||
protected:
|
||||
void writeCompact(rdr::U32 value, rdr::OutStream* os) const;
|
||||
void mp4_write_callback(const void *buffer, size_t size);
|
||||
|
||||
protected:
|
||||
bool keyframe;
|
||||
x264_t *enc;
|
||||
x264_param_t *params;
|
||||
Mp4Context *mux;
|
||||
Mp4State *muxstate;
|
||||
unsigned framectr;
|
||||
|
||||
bool nvidia_init_done;
|
||||
bool using_nvidia;
|
||||
static bool skip_nvidia;
|
||||
|
||||
EncCache *encCache;
|
||||
uint8_t cacheType;
|
||||
public:
|
||||
uint8_t *framebuf;
|
||||
uint32_t framelen;
|
||||
|
||||
uint8_t *bitbuf;
|
||||
|
||||
uint16_t myw, myh;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
@@ -35,6 +35,8 @@ namespace rfb {
|
||||
dispatch elapsed Timer callbacks and to determine how long to wait in select() for
|
||||
the next timeout to occur.
|
||||
|
||||
For classes that can be derived it's best to use MethodTimer which can call a specific
|
||||
method on the class, thus avoiding conflicts when subclassing.
|
||||
*/
|
||||
|
||||
struct Timer {
|
||||
@@ -101,6 +103,19 @@ namespace rfb {
|
||||
static std::list<Timer*> pending;
|
||||
};
|
||||
|
||||
template<class T> class MethodTimer
|
||||
: public Timer, public Timer::Callback {
|
||||
public:
|
||||
MethodTimer(T* obj_, bool (T::*cb_)(Timer*))
|
||||
: Timer(this), obj(obj_), cb(cb_) {}
|
||||
|
||||
virtual bool handleTimeout(Timer* t) { return (obj->*cb)(t); }
|
||||
|
||||
private:
|
||||
T* obj;
|
||||
bool (T::*cb)(Timer*);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -86,8 +86,9 @@ VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
|
||||
}
|
||||
|
||||
bool write, owner;
|
||||
if (!getPerms(write, owner) || !write)
|
||||
accessRights = (accessRights & ~(AccessPtrEvents | AccessKeyEvents | AccessSetDesktopSize));
|
||||
if (!getPerms(write, owner) || !write) {
|
||||
accessRights &= ~WRITER_PERMS;
|
||||
}
|
||||
|
||||
// Configure the socket
|
||||
setSocketTimeouts();
|
||||
@@ -155,6 +156,17 @@ void VNCSConnectionST::close(const char* reason)
|
||||
server->lastDisconnectTime = time(0);
|
||||
}
|
||||
|
||||
try {
|
||||
if (sock->outStream().bufferUsage() > 0) {
|
||||
sock->cork(false);
|
||||
sock->outStream().flush();
|
||||
if (sock->outStream().bufferUsage() > 0)
|
||||
vlog.error("Failed to flush remaining socket data on close");
|
||||
}
|
||||
} catch (rdr::Exception& e) {
|
||||
vlog.error("Failed to flush remaining socket data on close: %s", e.str());
|
||||
}
|
||||
|
||||
// Just shutdown the socket and mark our state as closing. Eventually the
|
||||
// calling code will call VNCServerST's removeSocket() method causing us to
|
||||
// be deleted.
|
||||
@@ -392,7 +404,31 @@ static void keylog(unsigned keysym, const char *client) {
|
||||
flushKeylog(client);
|
||||
}
|
||||
|
||||
void VNCSConnectionST::serverCutTextOrClose(const char *str, int len)
|
||||
void VNCSConnectionST::requestClipboardOrClose()
|
||||
{
|
||||
try {
|
||||
if (!(accessRights & AccessCutText)) return;
|
||||
if (!rfb::Server::acceptCutText) return;
|
||||
if (state() != RFBSTATE_NORMAL) return;
|
||||
requestClipboard();
|
||||
} catch(rdr::Exception& e) {
|
||||
close(e.str());
|
||||
}
|
||||
}
|
||||
|
||||
void VNCSConnectionST::announceClipboardOrClose(bool available)
|
||||
{
|
||||
try {
|
||||
if (!(accessRights & AccessCutText)) return;
|
||||
if (!rfb::Server::sendCutText) return;
|
||||
if (state() != RFBSTATE_NORMAL) return;
|
||||
announceClipboard(available);
|
||||
} catch(rdr::Exception& e) {
|
||||
close(e.str());
|
||||
}
|
||||
}
|
||||
|
||||
void VNCSConnectionST::sendClipboardDataOrClose(const char* data)
|
||||
{
|
||||
try {
|
||||
if (!(accessRights & AccessCutText)) return;
|
||||
@@ -402,19 +438,19 @@ void VNCSConnectionST::serverCutTextOrClose(const char *str, int len)
|
||||
sock->getPeerAddress());
|
||||
return;
|
||||
}
|
||||
int len = strlen(data);
|
||||
const int origlen = len;
|
||||
if (rfb::Server::DLP_ClipSendMax && len > rfb::Server::DLP_ClipSendMax)
|
||||
len = rfb::Server::DLP_ClipSendMax;
|
||||
cliplog(str, len, origlen, "sent", sock->getPeerAddress());
|
||||
if (state() == RFBSTATE_NORMAL)
|
||||
writer()->writeServerCutText(str, len);
|
||||
cliplog(data, len, origlen, "sent", sock->getPeerAddress());
|
||||
if (state() != RFBSTATE_NORMAL) return;
|
||||
sendClipboardData(data, len);
|
||||
gettimeofday(&lastClipboardOp, NULL);
|
||||
} catch(rdr::Exception& e) {
|
||||
close(e.str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void VNCSConnectionST::setDesktopNameOrClose(const char *name)
|
||||
{
|
||||
try {
|
||||
@@ -506,6 +542,15 @@ void VNCSConnectionST::renderedCursorChange()
|
||||
}
|
||||
}
|
||||
|
||||
// cursorPositionChange() is called whenever the cursor has changed position by
|
||||
// the server. If the client supports being informed about these changes then
|
||||
// it will arrange for the new cursor position to be sent to the client.
|
||||
|
||||
void VNCSConnectionST::cursorPositionChange()
|
||||
{
|
||||
setCursorPos();
|
||||
}
|
||||
|
||||
// needRenderedCursor() returns true if this client needs the server-side
|
||||
// rendered cursor. This may be because it does not support local cursor or
|
||||
// because the current cursor position has not been set by this client.
|
||||
@@ -826,24 +871,6 @@ void VNCSConnectionST::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) {
|
||||
server->desktop->keyEvent(keysym, keycode, down);
|
||||
}
|
||||
|
||||
void VNCSConnectionST::clientCutText(const char* str, int len)
|
||||
{
|
||||
if (!(accessRights & AccessCutText)) return;
|
||||
if (!rfb::Server::acceptCutText) return;
|
||||
if (msSince(&lastClipboardOp) < (unsigned) rfb::Server::DLP_ClipDelay) {
|
||||
vlog.info("DLP: client %s: refused to receive clipboard, too soon",
|
||||
sock->getPeerAddress());
|
||||
return;
|
||||
}
|
||||
const int origlen = len;
|
||||
if (rfb::Server::DLP_ClipAcceptMax && len > rfb::Server::DLP_ClipAcceptMax)
|
||||
len = rfb::Server::DLP_ClipAcceptMax;
|
||||
cliplog(str, len, origlen, "received", sock->getPeerAddress());
|
||||
|
||||
gettimeofday(&lastClipboardOp, NULL);
|
||||
server->desktop->clientCutText(str, len);
|
||||
}
|
||||
|
||||
void VNCSConnectionST::framebufferUpdateRequest(const Rect& r,bool incremental)
|
||||
{
|
||||
Rect safeRect;
|
||||
@@ -863,7 +890,7 @@ void VNCSConnectionST::framebufferUpdateRequest(const Rect& r,bool incremental)
|
||||
|
||||
// Just update the requested region.
|
||||
// Framebuffer update will be sent a bit later, see processMessages().
|
||||
Region reqRgn(r);
|
||||
Region reqRgn(safeRect);
|
||||
if (!incremental || !continuousUpdates)
|
||||
requested.assign_union(reqRgn);
|
||||
|
||||
@@ -977,6 +1004,38 @@ void VNCSConnectionST::enableContinuousUpdates(bool enable,
|
||||
}
|
||||
}
|
||||
|
||||
void VNCSConnectionST::handleClipboardRequest()
|
||||
{
|
||||
if (!(accessRights & AccessCutText)) return;
|
||||
server->handleClipboardRequest(this);
|
||||
}
|
||||
|
||||
void VNCSConnectionST::handleClipboardAnnounce(bool available)
|
||||
{
|
||||
if (!(accessRights & AccessCutText)) return;
|
||||
if (!rfb::Server::acceptCutText) return;
|
||||
server->handleClipboardAnnounce(this, available);
|
||||
}
|
||||
|
||||
void VNCSConnectionST::handleClipboardData(const char* data, int len)
|
||||
{
|
||||
if (!(accessRights & AccessCutText)) return;
|
||||
if (!rfb::Server::acceptCutText) return;
|
||||
if (msSince(&lastClipboardOp) < (unsigned) rfb::Server::DLP_ClipDelay) {
|
||||
vlog.info("DLP: client %s: refused to receive clipboard, too soon",
|
||||
sock->getPeerAddress());
|
||||
return;
|
||||
}
|
||||
const int origlen = len;
|
||||
if (rfb::Server::DLP_ClipAcceptMax && len > rfb::Server::DLP_ClipAcceptMax)
|
||||
len = rfb::Server::DLP_ClipAcceptMax;
|
||||
cliplog(data, len, origlen, "received", sock->getPeerAddress());
|
||||
|
||||
gettimeofday(&lastClipboardOp, NULL);
|
||||
server->handleClipboardData(this, data, len);
|
||||
}
|
||||
|
||||
|
||||
// supportsLocalCursor() is called whenever the status of
|
||||
// cp.supportsLocalCursor has changed. If the client does now support local
|
||||
// cursor, we make sure that the old server-side rendered cursor is cleaned up
|
||||
@@ -1158,9 +1217,9 @@ void VNCSConnectionST::writeFramebufferUpdate()
|
||||
close("User was deleted");
|
||||
return;
|
||||
} else if (!write) {
|
||||
accessRights = (accessRights & ~(AccessPtrEvents | AccessKeyEvents | AccessSetDesktopSize));
|
||||
accessRights &= ~WRITER_PERMS;
|
||||
} else {
|
||||
accessRights |= AccessPtrEvents | AccessKeyEvents | AccessSetDesktopSize;
|
||||
accessRights |= WRITER_PERMS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1471,6 +1530,21 @@ void VNCSConnectionST::setCursor()
|
||||
}
|
||||
}
|
||||
|
||||
// setCursorPos() is called whenever the cursor has changed position by the
|
||||
// server. If the client supports being informed about these changes then it
|
||||
// will arrange for the new cursor position to be sent to the client.
|
||||
|
||||
void VNCSConnectionST::setCursorPos()
|
||||
{
|
||||
if (state() != RFBSTATE_NORMAL)
|
||||
return;
|
||||
|
||||
if (cp.supportsCursorPosition) {
|
||||
cp.setCursorPos(server->cursorPos);
|
||||
writer()->writeCursorPos();
|
||||
}
|
||||
}
|
||||
|
||||
void VNCSConnectionST::setDesktopName(const char *name)
|
||||
{
|
||||
cp.setName(name);
|
||||
|
||||
@@ -75,9 +75,11 @@ namespace rfb {
|
||||
void screenLayoutChangeOrClose(rdr::U16 reason);
|
||||
void setCursorOrClose();
|
||||
void bellOrClose();
|
||||
void serverCutTextOrClose(const char *str, int len);
|
||||
void setDesktopNameOrClose(const char *name);
|
||||
void setLEDStateOrClose(unsigned int state);
|
||||
void requestClipboardOrClose();
|
||||
void announceClipboardOrClose(bool available);
|
||||
void sendClipboardDataOrClose(const char* data);
|
||||
|
||||
// checkIdleTimeout() returns the number of milliseconds left until the
|
||||
// idle timeout expires. If it has expired, the connection is closed and
|
||||
@@ -97,6 +99,11 @@ namespace rfb {
|
||||
// cursor.
|
||||
void renderedCursorChange();
|
||||
|
||||
// cursorPositionChange() is called whenever the cursor has changed position by
|
||||
// the server. If the client supports being informed about these changes then
|
||||
// it will arrange for the new cursor position to be sent to the client.
|
||||
void cursorPositionChange();
|
||||
|
||||
// needRenderedCursor() returns true if this client needs the server-side
|
||||
// rendered cursor. This may be because it does not support local cursor
|
||||
// or because the current cursor position has not been set by this client.
|
||||
@@ -170,13 +177,15 @@ namespace rfb {
|
||||
virtual void setPixelFormat(const PixelFormat& pf);
|
||||
virtual void pointerEvent(const Point& pos, int buttonMask, const bool skipClick, const bool skipRelease);
|
||||
virtual void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down);
|
||||
virtual void clientCutText(const char* str, int len);
|
||||
virtual void framebufferUpdateRequest(const Rect& r, bool incremental);
|
||||
virtual void setDesktopSize(int fb_width, int fb_height,
|
||||
const ScreenSet& layout);
|
||||
virtual void fence(rdr::U32 flags, unsigned len, const char data[]);
|
||||
virtual void enableContinuousUpdates(bool enable,
|
||||
int x, int y, int w, int h);
|
||||
virtual void handleClipboardRequest();
|
||||
virtual void handleClipboardAnnounce(bool available);
|
||||
virtual void handleClipboardData(const char* data, int len);
|
||||
virtual void supportsLocalCursor();
|
||||
virtual void supportsFence();
|
||||
virtual void supportsContinuousUpdates();
|
||||
@@ -197,7 +206,7 @@ namespace rfb {
|
||||
|
||||
bool write, owner;
|
||||
if (!getPerms(write, owner) || !write)
|
||||
accessRights = (accessRights & ~(AccessPtrEvents | AccessKeyEvents));
|
||||
accessRights &= ~WRITER_PERMS;
|
||||
needsPermCheck = false;
|
||||
}
|
||||
|
||||
@@ -223,6 +232,7 @@ namespace rfb {
|
||||
|
||||
void screenLayoutChange(rdr::U16 reason);
|
||||
void setCursor();
|
||||
void setCursorPos();
|
||||
void setDesktopName(const char *name);
|
||||
void setLEDState(unsigned int state);
|
||||
void setSocketTimeouts();
|
||||
|
||||
@@ -52,9 +52,21 @@ namespace rfb {
|
||||
// getPixelBuffer() returns a pointer to the PixelBuffer object.
|
||||
virtual PixelBuffer* getPixelBuffer() const = 0;
|
||||
|
||||
// serverCutText() tells the server that the cut text has changed. This
|
||||
// will normally be sent to all clients.
|
||||
virtual void serverCutText(const char* str, int len) = 0;
|
||||
// requestClipboard() will result in a request to a client to
|
||||
// transfer its clipboard data. A call to
|
||||
// SDesktop::handleClipboardData() will be made once the data is
|
||||
// available.
|
||||
virtual void requestClipboard() = 0;
|
||||
|
||||
// announceClipboard() informs all clients of changes to the
|
||||
// clipboard on the server. A client may later request the
|
||||
// clipboard data via SDesktop::handleClipboardRequest().
|
||||
virtual void announceClipboard(bool available) = 0;
|
||||
|
||||
// sendClipboardData() transfers the clipboard data to a client
|
||||
// and should be called whenever a client has requested the
|
||||
// clipboard via SDesktop::handleClipboardRequest().
|
||||
virtual void sendClipboardData(const char* data) = 0;
|
||||
|
||||
// bell() tells the server that it should make all clients make a bell sound.
|
||||
virtual void bell() = 0;
|
||||
@@ -67,10 +79,12 @@ namespace rfb {
|
||||
// cursorData argument contains width*height rgba quadruplets with
|
||||
// non-premultiplied alpha.
|
||||
virtual void setCursor(int width, int height, const Point& hotspot,
|
||||
const rdr::U8* cursorData) = 0;
|
||||
const rdr::U8* cursorData, const bool resizing = false) = 0;
|
||||
|
||||
// setCursorPos() tells the server the current position of the cursor.
|
||||
virtual void setCursorPos(const Point& p) = 0;
|
||||
// setCursorPos() tells the server the current position of the cursor, and
|
||||
// whether the server initiated that change (e.g. through another X11
|
||||
// client calling XWarpPointer()).
|
||||
virtual void setCursorPos(const Point& p, bool warped) = 0;
|
||||
|
||||
// setName() tells the server what desktop title to supply to clients
|
||||
virtual void setName(const char* name) = 0;
|
||||
|
||||
@@ -123,8 +123,8 @@ static void parseRegionPart(const bool percents, rdr::U16 &pcdest, int &dest,
|
||||
VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
|
||||
: blHosts(&blacklist), desktop(desktop_), desktopStarted(false),
|
||||
blockCounter(0), pb(0), blackedpb(0), ledState(ledUnknown),
|
||||
name(strDup(name_)), pointerClient(0), comparer(0),
|
||||
cursor(new Cursor(0, 0, Point(), NULL)),
|
||||
name(strDup(name_)), pointerClient(0), clipboardClient(0),
|
||||
comparer(0), cursor(new Cursor(0, 0, Point(), NULL)),
|
||||
renderedCursorInvalid(false),
|
||||
queryConnectionHandler(0), keyRemapper(&KeyRemapper::defInstance),
|
||||
lastConnectionTime(0), disableclients(false),
|
||||
@@ -502,6 +502,45 @@ void VNCServerST::setScreenLayout(const ScreenSet& layout)
|
||||
}
|
||||
}
|
||||
|
||||
void VNCServerST::requestClipboard()
|
||||
{
|
||||
if (clipboardClient == NULL)
|
||||
return;
|
||||
|
||||
clipboardClient->requestClipboard();
|
||||
}
|
||||
|
||||
void VNCServerST::announceClipboard(bool available)
|
||||
{
|
||||
std::list<VNCSConnectionST*>::iterator ci, ci_next;
|
||||
|
||||
if (available)
|
||||
clipboardClient = NULL;
|
||||
|
||||
clipboardRequestors.clear();
|
||||
|
||||
for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
|
||||
ci_next = ci; ci_next++;
|
||||
(*ci)->announceClipboard(available);
|
||||
}
|
||||
}
|
||||
|
||||
void VNCServerST::sendClipboardData(const char* data)
|
||||
{
|
||||
std::list<VNCSConnectionST*>::iterator ci, ci_next;
|
||||
|
||||
if (strchr(data, '\r') != NULL)
|
||||
throw Exception("Invalid carriage return in clipboard data");
|
||||
|
||||
for (ci = clipboardRequestors.begin();
|
||||
ci != clipboardRequestors.end(); ci = ci_next) {
|
||||
ci_next = ci; ci_next++;
|
||||
(*ci)->sendClipboardDataOrClose(data);
|
||||
}
|
||||
|
||||
clipboardRequestors.clear();
|
||||
}
|
||||
|
||||
void VNCServerST::bell()
|
||||
{
|
||||
std::list<VNCSConnectionST*>::iterator ci, ci_next;
|
||||
@@ -511,15 +550,6 @@ void VNCServerST::bell()
|
||||
}
|
||||
}
|
||||
|
||||
void VNCServerST::serverCutText(const char* str, int len)
|
||||
{
|
||||
std::list<VNCSConnectionST*>::iterator ci, ci_next;
|
||||
for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
|
||||
ci_next = ci; ci_next++;
|
||||
(*ci)->serverCutTextOrClose(str, len);
|
||||
}
|
||||
}
|
||||
|
||||
void VNCServerST::setName(const char* name_)
|
||||
{
|
||||
name.replaceBuf(strDup(name_));
|
||||
@@ -549,7 +579,7 @@ void VNCServerST::add_copied(const Region& dest, const Point& delta)
|
||||
}
|
||||
|
||||
void VNCServerST::setCursor(int width, int height, const Point& newHotspot,
|
||||
const rdr::U8* data)
|
||||
const rdr::U8* data, const bool resizing)
|
||||
{
|
||||
delete cursor;
|
||||
cursor = new Cursor(width, height, newHotspot, data);
|
||||
@@ -557,6 +587,13 @@ void VNCServerST::setCursor(int width, int height, const Point& newHotspot,
|
||||
|
||||
renderedCursorInvalid = true;
|
||||
|
||||
// If an app has an animated cursor on the resized edge, X internals
|
||||
// will call for it to be rendered. Unlucky for us, the VNC screen
|
||||
// is currently pointing to freed memory, and a cursor change
|
||||
// would want to send a screen update. So, don't do that.
|
||||
if (resizing)
|
||||
return;
|
||||
|
||||
std::list<VNCSConnectionST*>::iterator ci, ci_next;
|
||||
for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
|
||||
ci_next = ci; ci_next++;
|
||||
@@ -565,14 +602,17 @@ void VNCServerST::setCursor(int width, int height, const Point& newHotspot,
|
||||
}
|
||||
}
|
||||
|
||||
void VNCServerST::setCursorPos(const Point& pos)
|
||||
void VNCServerST::setCursorPos(const Point& pos, bool warped)
|
||||
{
|
||||
if (!cursorPos.equals(pos)) {
|
||||
cursorPos = pos;
|
||||
renderedCursorInvalid = true;
|
||||
std::list<VNCSConnectionST*>::iterator ci;
|
||||
for (ci = clients.begin(); ci != clients.end(); ci++)
|
||||
for (ci = clients.begin(); ci != clients.end(); ci++) {
|
||||
(*ci)->renderedCursorChange();
|
||||
if (warped)
|
||||
(*ci)->cursorPositionChange();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1049,3 +1089,32 @@ bool VNCServerST::getComparerState()
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void VNCServerST::handleClipboardRequest(VNCSConnectionST* client)
|
||||
{
|
||||
clipboardRequestors.push_back(client);
|
||||
if (clipboardRequestors.size() == 1)
|
||||
desktop->handleClipboardRequest();
|
||||
}
|
||||
|
||||
void VNCServerST::handleClipboardAnnounce(VNCSConnectionST* client,
|
||||
bool available)
|
||||
{
|
||||
if (available)
|
||||
clipboardClient = client;
|
||||
else {
|
||||
if (client != clipboardClient)
|
||||
return;
|
||||
clipboardClient = NULL;
|
||||
}
|
||||
desktop->handleClipboardAnnounce(available);
|
||||
}
|
||||
|
||||
void VNCServerST::handleClipboardData(VNCSConnectionST* client,
|
||||
const char* data, int len)
|
||||
{
|
||||
if (client != clipboardClient)
|
||||
return;
|
||||
desktop->handleClipboardData(data, len);
|
||||
}
|
||||
|
||||
|
||||
@@ -96,12 +96,14 @@ namespace rfb {
|
||||
virtual void setPixelBuffer(PixelBuffer* pb);
|
||||
virtual void setScreenLayout(const ScreenSet& layout);
|
||||
virtual PixelBuffer* getPixelBuffer() const { if (DLPRegion.enabled && blackedpb) return blackedpb; else return pb; }
|
||||
virtual void serverCutText(const char* str, int len);
|
||||
virtual void requestClipboard();
|
||||
virtual void announceClipboard(bool available);
|
||||
virtual void sendClipboardData(const char* data);
|
||||
virtual void add_changed(const Region ®ion);
|
||||
virtual void add_copied(const Region &dest, const Point &delta);
|
||||
virtual void setCursor(int width, int height, const Point& hotspot,
|
||||
const rdr::U8* data);
|
||||
virtual void setCursorPos(const Point& p);
|
||||
const rdr::U8* data, const bool resizing = false);
|
||||
virtual void setCursorPos(const Point& p, bool warped);
|
||||
virtual void setLEDState(unsigned state);
|
||||
|
||||
virtual void bell();
|
||||
@@ -189,6 +191,10 @@ namespace rfb {
|
||||
|
||||
void setAPIMessager(network::GetAPIMessager *msgr) { apimessager = msgr; }
|
||||
|
||||
void handleClipboardRequest(VNCSConnectionST* client);
|
||||
void handleClipboardAnnounce(VNCSConnectionST* client, bool available);
|
||||
void handleClipboardData(VNCSConnectionST* client, const char* data, int len);
|
||||
|
||||
protected:
|
||||
|
||||
friend class VNCSConnectionST;
|
||||
@@ -217,6 +223,8 @@ namespace rfb {
|
||||
|
||||
std::list<VNCSConnectionST*> clients;
|
||||
VNCSConnectionST* pointerClient;
|
||||
VNCSConnectionST* clipboardClient;
|
||||
std::list<VNCSConnectionST*> clipboardRequestors;
|
||||
std::list<network::Socket*> closingSockets;
|
||||
|
||||
static EncCache encCache;
|
||||
|
||||
@@ -31,7 +31,7 @@ IntParameter zlibLevel("ZlibLevel","Zlib compression level",-1);
|
||||
|
||||
ZRLEEncoder::ZRLEEncoder(SConnection* conn)
|
||||
: Encoder(conn, encodingZRLE, EncoderPlain, 127),
|
||||
zos(0,0,zlibLevel), mos(129*1024)
|
||||
zos(0,zlibLevel), mos(129*1024)
|
||||
{
|
||||
zos.setUnderlying(&mos);
|
||||
}
|
||||
|
||||
41
common/rfb/clipboardTypes.h
Normal file
41
common/rfb/clipboardTypes.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/* Copyright 2019 Pierre Ossman for Cendio AB
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this software; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
#ifndef __RFB_CLIPBOARDTYPES_H__
|
||||
#define __RFB_CLIPBOARDTYPES_H__
|
||||
|
||||
namespace rfb {
|
||||
|
||||
// Formats
|
||||
const unsigned int clipboardUTF8 = 1 << 0;
|
||||
const unsigned int clipboardRTF = 1 << 1;
|
||||
const unsigned int clipboardHTML = 1 << 2;
|
||||
const unsigned int clipboardDIB = 1 << 3;
|
||||
const unsigned int clipboardFiles = 1 << 4;
|
||||
|
||||
const unsigned int clipboardFormatMask = 0x0000ffff;
|
||||
|
||||
// Actions
|
||||
const unsigned int clipboardCaps = 1 << 24;
|
||||
const unsigned int clipboardRequest = 1 << 25;
|
||||
const unsigned int clipboardPeek = 1 << 26;
|
||||
const unsigned int clipboardNotify = 1 << 27;
|
||||
const unsigned int clipboardProvide = 1 << 28;
|
||||
|
||||
const unsigned int clipboardActionMask = 0xff000000;
|
||||
}
|
||||
#endif
|
||||
423
common/rfb/dynlink_cuda.h
Normal file
423
common/rfb/dynlink_cuda.h
Normal file
@@ -0,0 +1,423 @@
|
||||
/*
|
||||
* This copyright notice applies to this header file only:
|
||||
*
|
||||
* Copyright (c) 2016
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#if !defined(FFNV_DYNLINK_CUDA_H) && !defined(CUDA_VERSION)
|
||||
#define FFNV_DYNLINK_CUDA_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#define CUDA_VERSION 7050
|
||||
|
||||
#if defined(_WIN32) || defined(__CYGWIN__)
|
||||
#define CUDAAPI __stdcall
|
||||
#else
|
||||
#define CUDAAPI
|
||||
#endif
|
||||
|
||||
#define CU_CTX_SCHED_BLOCKING_SYNC 4
|
||||
|
||||
typedef int CUdevice;
|
||||
#if defined(__x86_64) || defined(AMD64) || defined(_M_AMD64) || defined(__LP64__) || defined(__aarch64__)
|
||||
typedef unsigned long long CUdeviceptr;
|
||||
#else
|
||||
typedef unsigned int CUdeviceptr;
|
||||
#endif
|
||||
typedef unsigned long long CUtexObject;
|
||||
|
||||
typedef struct CUarray_st *CUarray;
|
||||
typedef struct CUctx_st *CUcontext;
|
||||
typedef struct CUstream_st *CUstream;
|
||||
typedef struct CUevent_st *CUevent;
|
||||
typedef struct CUfunc_st *CUfunction;
|
||||
typedef struct CUmod_st *CUmodule;
|
||||
typedef struct CUmipmappedArray_st *CUmipmappedArray;
|
||||
typedef struct CUgraphicsResource_st *CUgraphicsResource;
|
||||
typedef struct CUextMemory_st *CUexternalMemory;
|
||||
typedef struct CUextSemaphore_st *CUexternalSemaphore;
|
||||
|
||||
typedef struct CUlinkState_st *CUlinkState;
|
||||
|
||||
typedef enum cudaError_enum {
|
||||
CUDA_SUCCESS = 0,
|
||||
CUDA_ERROR_NOT_READY = 600
|
||||
} CUresult;
|
||||
|
||||
/**
|
||||
* Device properties (subset)
|
||||
*/
|
||||
typedef enum CUdevice_attribute_enum {
|
||||
CU_DEVICE_ATTRIBUTE_CLOCK_RATE = 13,
|
||||
CU_DEVICE_ATTRIBUTE_TEXTURE_ALIGNMENT = 14,
|
||||
CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT = 16,
|
||||
CU_DEVICE_ATTRIBUTE_INTEGRATED = 18,
|
||||
CU_DEVICE_ATTRIBUTE_CAN_MAP_HOST_MEMORY = 19,
|
||||
CU_DEVICE_ATTRIBUTE_COMPUTE_MODE = 20,
|
||||
CU_DEVICE_ATTRIBUTE_CONCURRENT_KERNELS = 31,
|
||||
CU_DEVICE_ATTRIBUTE_PCI_BUS_ID = 33,
|
||||
CU_DEVICE_ATTRIBUTE_PCI_DEVICE_ID = 34,
|
||||
CU_DEVICE_ATTRIBUTE_TCC_DRIVER = 35,
|
||||
CU_DEVICE_ATTRIBUTE_MEMORY_CLOCK_RATE = 36,
|
||||
CU_DEVICE_ATTRIBUTE_GLOBAL_MEMORY_BUS_WIDTH = 37,
|
||||
CU_DEVICE_ATTRIBUTE_ASYNC_ENGINE_COUNT = 40,
|
||||
CU_DEVICE_ATTRIBUTE_UNIFIED_ADDRESSING = 41,
|
||||
CU_DEVICE_ATTRIBUTE_PCI_DOMAIN_ID = 50,
|
||||
CU_DEVICE_ATTRIBUTE_TEXTURE_PITCH_ALIGNMENT = 51,
|
||||
CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR = 75,
|
||||
CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR = 76,
|
||||
CU_DEVICE_ATTRIBUTE_MANAGED_MEMORY = 83,
|
||||
CU_DEVICE_ATTRIBUTE_MULTI_GPU_BOARD = 84,
|
||||
CU_DEVICE_ATTRIBUTE_MULTI_GPU_BOARD_GROUP_ID = 85,
|
||||
} CUdevice_attribute;
|
||||
|
||||
typedef enum CUarray_format_enum {
|
||||
CU_AD_FORMAT_UNSIGNED_INT8 = 0x01,
|
||||
CU_AD_FORMAT_UNSIGNED_INT16 = 0x02,
|
||||
CU_AD_FORMAT_UNSIGNED_INT32 = 0x03,
|
||||
CU_AD_FORMAT_SIGNED_INT8 = 0x08,
|
||||
CU_AD_FORMAT_SIGNED_INT16 = 0x09,
|
||||
CU_AD_FORMAT_SIGNED_INT32 = 0x0a,
|
||||
CU_AD_FORMAT_HALF = 0x10,
|
||||
CU_AD_FORMAT_FLOAT = 0x20
|
||||
} CUarray_format;
|
||||
|
||||
typedef enum CUmemorytype_enum {
|
||||
CU_MEMORYTYPE_HOST = 1,
|
||||
CU_MEMORYTYPE_DEVICE = 2,
|
||||
CU_MEMORYTYPE_ARRAY = 3
|
||||
} CUmemorytype;
|
||||
|
||||
typedef enum CUlimit_enum {
|
||||
CU_LIMIT_STACK_SIZE = 0,
|
||||
CU_LIMIT_PRINTF_FIFO_SIZE = 1,
|
||||
CU_LIMIT_MALLOC_HEAP_SIZE = 2,
|
||||
CU_LIMIT_DEV_RUNTIME_SYNC_DEPTH = 3,
|
||||
CU_LIMIT_DEV_RUNTIME_PENDING_LAUNCH_COUNT = 4
|
||||
} CUlimit;
|
||||
|
||||
typedef enum CUresourcetype_enum {
|
||||
CU_RESOURCE_TYPE_ARRAY = 0x00,
|
||||
CU_RESOURCE_TYPE_MIPMAPPED_ARRAY = 0x01,
|
||||
CU_RESOURCE_TYPE_LINEAR = 0x02,
|
||||
CU_RESOURCE_TYPE_PITCH2D = 0x03
|
||||
} CUresourcetype;
|
||||
|
||||
typedef enum CUaddress_mode_enum {
|
||||
CU_TR_ADDRESS_MODE_WRAP = 0,
|
||||
CU_TR_ADDRESS_MODE_CLAMP = 1,
|
||||
CU_TR_ADDRESS_MODE_MIRROR = 2,
|
||||
CU_TR_ADDRESS_MODE_BORDER = 3
|
||||
} CUaddress_mode;
|
||||
|
||||
typedef enum CUfilter_mode_enum {
|
||||
CU_TR_FILTER_MODE_POINT = 0,
|
||||
CU_TR_FILTER_MODE_LINEAR = 1
|
||||
} CUfilter_mode;
|
||||
|
||||
typedef enum CUgraphicsRegisterFlags_enum {
|
||||
CU_GRAPHICS_REGISTER_FLAGS_NONE = 0,
|
||||
CU_GRAPHICS_REGISTER_FLAGS_READ_ONLY = 1,
|
||||
CU_GRAPHICS_REGISTER_FLAGS_WRITE_DISCARD = 2,
|
||||
CU_GRAPHICS_REGISTER_FLAGS_SURFACE_LDST = 4,
|
||||
CU_GRAPHICS_REGISTER_FLAGS_TEXTURE_GATHER = 8
|
||||
} CUgraphicsRegisterFlags;
|
||||
|
||||
typedef enum CUexternalMemoryHandleType_enum {
|
||||
CU_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD = 1,
|
||||
CU_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32 = 2,
|
||||
CU_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT = 3,
|
||||
CU_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_HEAP = 4,
|
||||
CU_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE = 5,
|
||||
} CUexternalMemoryHandleType;
|
||||
|
||||
typedef enum CUexternalSemaphoreHandleType_enum {
|
||||
CU_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD = 1,
|
||||
CU_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32 = 2,
|
||||
CU_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT = 3,
|
||||
CU_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE = 4
|
||||
} CUexternalSemaphoreHandleType;
|
||||
|
||||
typedef enum CUjit_option_enum
|
||||
{
|
||||
CU_JIT_MAX_REGISTERS = 0,
|
||||
CU_JIT_THREADS_PER_BLOCK = 1,
|
||||
CU_JIT_WALL_TIME = 2,
|
||||
CU_JIT_INFO_LOG_BUFFER = 3,
|
||||
CU_JIT_INFO_LOG_BUFFER_SIZE_BYTES = 4,
|
||||
CU_JIT_ERROR_LOG_BUFFER = 5,
|
||||
CU_JIT_ERROR_LOG_BUFFER_SIZE_BYTES = 6,
|
||||
CU_JIT_OPTIMIZATION_LEVEL = 7,
|
||||
CU_JIT_TARGET_FROM_CUCONTEXT = 8,
|
||||
CU_JIT_TARGET = 9,
|
||||
CU_JIT_FALLBACK_STRATEGY = 10,
|
||||
CU_JIT_GENERATE_DEBUG_INFO = 11,
|
||||
CU_JIT_LOG_VERBOSE = 12,
|
||||
CU_JIT_GENERATE_LINE_INFO = 13,
|
||||
CU_JIT_CACHE_MODE = 14,
|
||||
CU_JIT_NEW_SM3X_OPT = 15,
|
||||
CU_JIT_FAST_COMPILE = 16,
|
||||
CU_JIT_GLOBAL_SYMBOL_NAMES = 17,
|
||||
CU_JIT_GLOBAL_SYMBOL_ADDRESSES = 18,
|
||||
CU_JIT_GLOBAL_SYMBOL_COUNT = 19,
|
||||
CU_JIT_NUM_OPTIONS
|
||||
} CUjit_option;
|
||||
|
||||
typedef enum CUjitInputType_enum
|
||||
{
|
||||
CU_JIT_INPUT_CUBIN = 0,
|
||||
CU_JIT_INPUT_PTX = 1,
|
||||
CU_JIT_INPUT_FATBINARY = 2,
|
||||
CU_JIT_INPUT_OBJECT = 3,
|
||||
CU_JIT_INPUT_LIBRARY = 4,
|
||||
CU_JIT_NUM_INPUT_TYPES
|
||||
} CUjitInputType;
|
||||
|
||||
#ifndef CU_UUID_HAS_BEEN_DEFINED
|
||||
#define CU_UUID_HAS_BEEN_DEFINED
|
||||
typedef struct CUuuid_st {
|
||||
char bytes[16];
|
||||
} CUuuid;
|
||||
#endif
|
||||
|
||||
typedef struct CUDA_MEMCPY2D_st {
|
||||
size_t srcXInBytes;
|
||||
size_t srcY;
|
||||
CUmemorytype srcMemoryType;
|
||||
const void *srcHost;
|
||||
CUdeviceptr srcDevice;
|
||||
CUarray srcArray;
|
||||
size_t srcPitch;
|
||||
|
||||
size_t dstXInBytes;
|
||||
size_t dstY;
|
||||
CUmemorytype dstMemoryType;
|
||||
void *dstHost;
|
||||
CUdeviceptr dstDevice;
|
||||
CUarray dstArray;
|
||||
size_t dstPitch;
|
||||
|
||||
size_t WidthInBytes;
|
||||
size_t Height;
|
||||
} CUDA_MEMCPY2D;
|
||||
|
||||
typedef struct CUDA_RESOURCE_DESC_st {
|
||||
CUresourcetype resType;
|
||||
union {
|
||||
struct {
|
||||
CUarray hArray;
|
||||
} array;
|
||||
struct {
|
||||
CUmipmappedArray hMipmappedArray;
|
||||
} mipmap;
|
||||
struct {
|
||||
CUdeviceptr devPtr;
|
||||
CUarray_format format;
|
||||
unsigned int numChannels;
|
||||
size_t sizeInBytes;
|
||||
} linear;
|
||||
struct {
|
||||
CUdeviceptr devPtr;
|
||||
CUarray_format format;
|
||||
unsigned int numChannels;
|
||||
size_t width;
|
||||
size_t height;
|
||||
size_t pitchInBytes;
|
||||
} pitch2D;
|
||||
struct {
|
||||
int reserved[32];
|
||||
} reserved;
|
||||
} res;
|
||||
unsigned int flags;
|
||||
} CUDA_RESOURCE_DESC;
|
||||
|
||||
typedef struct CUDA_TEXTURE_DESC_st {
|
||||
CUaddress_mode addressMode[3];
|
||||
CUfilter_mode filterMode;
|
||||
unsigned int flags;
|
||||
unsigned int maxAnisotropy;
|
||||
CUfilter_mode mipmapFilterMode;
|
||||
float mipmapLevelBias;
|
||||
float minMipmapLevelClamp;
|
||||
float maxMipmapLevelClamp;
|
||||
float borderColor[4];
|
||||
int reserved[12];
|
||||
} CUDA_TEXTURE_DESC;
|
||||
|
||||
/* Unused type */
|
||||
typedef struct CUDA_RESOURCE_VIEW_DESC_st CUDA_RESOURCE_VIEW_DESC;
|
||||
|
||||
typedef unsigned int GLenum;
|
||||
typedef unsigned int GLuint;
|
||||
|
||||
typedef enum CUGLDeviceList_enum {
|
||||
CU_GL_DEVICE_LIST_ALL = 1,
|
||||
CU_GL_DEVICE_LIST_CURRENT_FRAME = 2,
|
||||
CU_GL_DEVICE_LIST_NEXT_FRAME = 3,
|
||||
} CUGLDeviceList;
|
||||
|
||||
typedef struct CUDA_EXTERNAL_MEMORY_HANDLE_DESC_st {
|
||||
CUexternalMemoryHandleType type;
|
||||
union {
|
||||
int fd;
|
||||
struct {
|
||||
void *handle;
|
||||
const void *name;
|
||||
} win32;
|
||||
} handle;
|
||||
unsigned long long size;
|
||||
unsigned int flags;
|
||||
unsigned int reserved[16];
|
||||
} CUDA_EXTERNAL_MEMORY_HANDLE_DESC;
|
||||
|
||||
typedef struct CUDA_EXTERNAL_MEMORY_BUFFER_DESC_st {
|
||||
unsigned long long offset;
|
||||
unsigned long long size;
|
||||
unsigned int flags;
|
||||
unsigned int reserved[16];
|
||||
} CUDA_EXTERNAL_MEMORY_BUFFER_DESC;
|
||||
|
||||
typedef struct CUDA_EXTERNAL_SEMAPHORE_HANDLE_DESC_st {
|
||||
CUexternalSemaphoreHandleType type;
|
||||
union {
|
||||
int fd;
|
||||
struct {
|
||||
void *handle;
|
||||
const void *name;
|
||||
} win32;
|
||||
} handle;
|
||||
unsigned int flags;
|
||||
unsigned int reserved[16];
|
||||
} CUDA_EXTERNAL_SEMAPHORE_HANDLE_DESC;
|
||||
|
||||
typedef struct CUDA_EXTERNAL_SEMAPHORE_SIGNAL_PARAMS_st {
|
||||
struct {
|
||||
struct {
|
||||
unsigned long long value;
|
||||
} fence;
|
||||
unsigned int reserved[16];
|
||||
} params;
|
||||
unsigned int flags;
|
||||
unsigned int reserved[16];
|
||||
} CUDA_EXTERNAL_SEMAPHORE_SIGNAL_PARAMS;
|
||||
|
||||
typedef CUDA_EXTERNAL_SEMAPHORE_SIGNAL_PARAMS CUDA_EXTERNAL_SEMAPHORE_WAIT_PARAMS;
|
||||
|
||||
typedef struct CUDA_ARRAY3D_DESCRIPTOR_st {
|
||||
size_t Width;
|
||||
size_t Height;
|
||||
size_t Depth;
|
||||
|
||||
CUarray_format Format;
|
||||
unsigned int NumChannels;
|
||||
unsigned int Flags;
|
||||
} CUDA_ARRAY3D_DESCRIPTOR;
|
||||
|
||||
typedef struct CUDA_EXTERNAL_MEMORY_MIPMAPPED_ARRAY_DESC_st {
|
||||
unsigned long long offset;
|
||||
CUDA_ARRAY3D_DESCRIPTOR arrayDesc;
|
||||
unsigned int numLevels;
|
||||
unsigned int reserved[16];
|
||||
} CUDA_EXTERNAL_MEMORY_MIPMAPPED_ARRAY_DESC;
|
||||
|
||||
#define CU_STREAM_NON_BLOCKING 1
|
||||
#define CU_EVENT_BLOCKING_SYNC 1
|
||||
#define CU_EVENT_DISABLE_TIMING 2
|
||||
#define CU_TRSF_READ_AS_INTEGER 1
|
||||
|
||||
typedef void CUDAAPI CUstreamCallback(CUstream hStream, CUresult status, void *userdata);
|
||||
|
||||
typedef CUresult CUDAAPI tcuInit(unsigned int Flags);
|
||||
typedef CUresult CUDAAPI tcuDeviceGetCount(int *count);
|
||||
typedef CUresult CUDAAPI tcuDeviceGet(CUdevice *device, int ordinal);
|
||||
typedef CUresult CUDAAPI tcuDeviceGetAttribute(int *pi, CUdevice_attribute attrib, CUdevice dev);
|
||||
typedef CUresult CUDAAPI tcuDeviceGetName(char *name, int len, CUdevice dev);
|
||||
typedef CUresult CUDAAPI tcuDeviceGetUuid(CUuuid *uuid, CUdevice dev);
|
||||
typedef CUresult CUDAAPI tcuDeviceComputeCapability(int *major, int *minor, CUdevice dev);
|
||||
typedef CUresult CUDAAPI tcuCtxCreate_v2(CUcontext *pctx, unsigned int flags, CUdevice dev);
|
||||
typedef CUresult CUDAAPI tcuCtxSetLimit(CUlimit limit, size_t value);
|
||||
typedef CUresult CUDAAPI tcuCtxPushCurrent_v2(CUcontext pctx);
|
||||
typedef CUresult CUDAAPI tcuCtxPopCurrent_v2(CUcontext *pctx);
|
||||
typedef CUresult CUDAAPI tcuCtxDestroy_v2(CUcontext ctx);
|
||||
typedef CUresult CUDAAPI tcuMemAlloc_v2(CUdeviceptr *dptr, size_t bytesize);
|
||||
typedef CUresult CUDAAPI tcuMemAllocPitch_v2(CUdeviceptr *dptr, size_t *pPitch, size_t WidthInBytes, size_t Height, unsigned int ElementSizeBytes);
|
||||
typedef CUresult CUDAAPI tcuMemsetD8Async(CUdeviceptr dstDevice, unsigned char uc, size_t N, CUstream hStream);
|
||||
typedef CUresult CUDAAPI tcuMemFree_v2(CUdeviceptr dptr);
|
||||
typedef CUresult CUDAAPI tcuMemcpy(CUdeviceptr dst, CUdeviceptr src, size_t bytesize);
|
||||
typedef CUresult CUDAAPI tcuMemcpyAsync(CUdeviceptr dst, CUdeviceptr src, size_t bytesize, CUstream hStream);
|
||||
typedef CUresult CUDAAPI tcuMemcpy2D_v2(const CUDA_MEMCPY2D *pcopy);
|
||||
typedef CUresult CUDAAPI tcuMemcpy2DAsync_v2(const CUDA_MEMCPY2D *pcopy, CUstream hStream);
|
||||
typedef CUresult CUDAAPI tcuGetErrorName(CUresult error, const char** pstr);
|
||||
typedef CUresult CUDAAPI tcuGetErrorString(CUresult error, const char** pstr);
|
||||
typedef CUresult CUDAAPI tcuCtxGetDevice(CUdevice *device);
|
||||
|
||||
typedef CUresult CUDAAPI tcuDevicePrimaryCtxRetain(CUcontext *pctx, CUdevice dev);
|
||||
typedef CUresult CUDAAPI tcuDevicePrimaryCtxRelease(CUdevice dev);
|
||||
typedef CUresult CUDAAPI tcuDevicePrimaryCtxSetFlags(CUdevice dev, unsigned int flags);
|
||||
typedef CUresult CUDAAPI tcuDevicePrimaryCtxGetState(CUdevice dev, unsigned int *flags, int *active);
|
||||
typedef CUresult CUDAAPI tcuDevicePrimaryCtxReset(CUdevice dev);
|
||||
|
||||
typedef CUresult CUDAAPI tcuStreamCreate(CUstream *phStream, unsigned int flags);
|
||||
typedef CUresult CUDAAPI tcuStreamQuery(CUstream hStream);
|
||||
typedef CUresult CUDAAPI tcuStreamSynchronize(CUstream hStream);
|
||||
typedef CUresult CUDAAPI tcuStreamDestroy_v2(CUstream hStream);
|
||||
typedef CUresult CUDAAPI tcuStreamAddCallback(CUstream hStream, CUstreamCallback *callback, void *userdata, unsigned int flags);
|
||||
typedef CUresult CUDAAPI tcuEventCreate(CUevent *phEvent, unsigned int flags);
|
||||
typedef CUresult CUDAAPI tcuEventDestroy_v2(CUevent hEvent);
|
||||
typedef CUresult CUDAAPI tcuEventSynchronize(CUevent hEvent);
|
||||
typedef CUresult CUDAAPI tcuEventQuery(CUevent hEvent);
|
||||
typedef CUresult CUDAAPI tcuEventRecord(CUevent hEvent, CUstream hStream);
|
||||
|
||||
typedef CUresult CUDAAPI tcuLaunchKernel(CUfunction f, unsigned int gridDimX, unsigned int gridDimY, unsigned int gridDimZ, unsigned int blockDimX, unsigned int blockDimY, unsigned int blockDimZ, unsigned int sharedMemBytes, CUstream hStream, void** kernelParams, void** extra);
|
||||
typedef CUresult CUDAAPI tcuLinkCreate(unsigned int numOptions, CUjit_option* options, void** optionValues, CUlinkState* stateOut);
|
||||
typedef CUresult CUDAAPI tcuLinkAddData(CUlinkState state, CUjitInputType type, void* data, size_t size, const char* name, unsigned int numOptions, CUjit_option* options, void** optionValues);
|
||||
typedef CUresult CUDAAPI tcuLinkComplete(CUlinkState state, void** cubinOut, size_t* sizeOut);
|
||||
typedef CUresult CUDAAPI tcuLinkDestroy(CUlinkState state);
|
||||
typedef CUresult CUDAAPI tcuModuleLoadData(CUmodule* module, const void* image);
|
||||
typedef CUresult CUDAAPI tcuModuleUnload(CUmodule hmod);
|
||||
typedef CUresult CUDAAPI tcuModuleGetFunction(CUfunction* hfunc, CUmodule hmod, const char* name);
|
||||
typedef CUresult CUDAAPI tcuModuleGetGlobal(CUdeviceptr *dptr, size_t *bytes, CUmodule hmod, const char* name);
|
||||
typedef CUresult CUDAAPI tcuTexObjectCreate(CUtexObject* pTexObject, const CUDA_RESOURCE_DESC* pResDesc, const CUDA_TEXTURE_DESC* pTexDesc, const CUDA_RESOURCE_VIEW_DESC* pResViewDesc);
|
||||
typedef CUresult CUDAAPI tcuTexObjectDestroy(CUtexObject texObject);
|
||||
|
||||
typedef CUresult CUDAAPI tcuGLGetDevices_v2(unsigned int* pCudaDeviceCount, CUdevice* pCudaDevices, unsigned int cudaDeviceCount, CUGLDeviceList deviceList);
|
||||
typedef CUresult CUDAAPI tcuGraphicsGLRegisterImage(CUgraphicsResource* pCudaResource, GLuint image, GLenum target, unsigned int Flags);
|
||||
typedef CUresult CUDAAPI tcuGraphicsUnregisterResource(CUgraphicsResource resource);
|
||||
typedef CUresult CUDAAPI tcuGraphicsMapResources(unsigned int count, CUgraphicsResource* resources, CUstream hStream);
|
||||
typedef CUresult CUDAAPI tcuGraphicsUnmapResources(unsigned int count, CUgraphicsResource* resources, CUstream hStream);
|
||||
typedef CUresult CUDAAPI tcuGraphicsSubResourceGetMappedArray(CUarray* pArray, CUgraphicsResource resource, unsigned int arrayIndex, unsigned int mipLevel);
|
||||
|
||||
typedef CUresult CUDAAPI tcuImportExternalMemory(CUexternalMemory* extMem_out, const CUDA_EXTERNAL_MEMORY_HANDLE_DESC* memHandleDesc);
|
||||
typedef CUresult CUDAAPI tcuDestroyExternalMemory(CUexternalMemory extMem);
|
||||
typedef CUresult CUDAAPI tcuExternalMemoryGetMappedBuffer(CUdeviceptr* devPtr, CUexternalMemory extMem, const CUDA_EXTERNAL_MEMORY_BUFFER_DESC* bufferDesc);
|
||||
typedef CUresult CUDAAPI tcuExternalMemoryGetMappedMipmappedArray(CUmipmappedArray* mipmap, CUexternalMemory extMem, const CUDA_EXTERNAL_MEMORY_MIPMAPPED_ARRAY_DESC* mipmapDesc);
|
||||
typedef CUresult CUDAAPI tcuMipmappedArrayGetLevel(CUarray* pLevelArray, CUmipmappedArray hMipmappedArray, unsigned int level);
|
||||
typedef CUresult CUDAAPI tcuMipmappedArrayDestroy(CUmipmappedArray hMipmappedArray);
|
||||
|
||||
typedef CUresult CUDAAPI tcuImportExternalSemaphore(CUexternalSemaphore* extSem_out, const CUDA_EXTERNAL_SEMAPHORE_HANDLE_DESC* semHandleDesc);
|
||||
typedef CUresult CUDAAPI tcuDestroyExternalSemaphore(CUexternalSemaphore extSem);
|
||||
typedef CUresult CUDAAPI tcuSignalExternalSemaphoresAsync(const CUexternalSemaphore* extSemArray, const CUDA_EXTERNAL_SEMAPHORE_SIGNAL_PARAMS* paramsArray, unsigned int numExtSems, CUstream stream);
|
||||
typedef CUresult CUDAAPI tcuWaitExternalSemaphoresAsync(const CUexternalSemaphore* extSemArray, const CUDA_EXTERNAL_SEMAPHORE_WAIT_PARAMS* paramsArray, unsigned int numExtSems, CUstream stream);
|
||||
#endif
|
||||
340
common/rfb/dynlink_loader.h
Normal file
340
common/rfb/dynlink_loader.h
Normal file
@@ -0,0 +1,340 @@
|
||||
/*
|
||||
* This copyright notice applies to this header file only:
|
||||
*
|
||||
* Copyright (c) 2016
|
||||
*
|
||||
* 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 FFNV_CUDA_DYNLINK_LOADER_H
|
||||
#define FFNV_CUDA_DYNLINK_LOADER_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "dynlink_cuda.h"
|
||||
#include "nvEncodeAPI.h"
|
||||
|
||||
#if defined(_WIN32) && (!defined(FFNV_LOAD_FUNC) || !defined(FFNV_SYM_FUNC) || !defined(FFNV_LIB_HANDLE))
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifndef FFNV_LIB_HANDLE
|
||||
# if defined(_WIN32)
|
||||
# define FFNV_LIB_HANDLE HMODULE
|
||||
# else
|
||||
# define FFNV_LIB_HANDLE void*
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) || defined(__CYGWIN__)
|
||||
# define CUDA_LIBNAME "nvcuda.dll"
|
||||
# define NVCUVID_LIBNAME "nvcuvid.dll"
|
||||
# if defined(_WIN64) || defined(__CYGWIN64__)
|
||||
# define NVENC_LIBNAME "nvEncodeAPI64.dll"
|
||||
# else
|
||||
# define NVENC_LIBNAME "nvEncodeAPI.dll"
|
||||
# endif
|
||||
#else
|
||||
# define CUDA_LIBNAME "libcuda.so.1"
|
||||
# define NVCUVID_LIBNAME "libnvcuvid.so.1"
|
||||
# define NVENC_LIBNAME "libnvidia-encode.so.1"
|
||||
#endif
|
||||
|
||||
#if !defined(FFNV_LOAD_FUNC) || !defined(FFNV_SYM_FUNC)
|
||||
# ifdef _WIN32
|
||||
# define FFNV_LOAD_FUNC(path) LoadLibrary(TEXT(path))
|
||||
# define FFNV_SYM_FUNC(lib, sym) GetProcAddress((lib), (sym))
|
||||
# define FFNV_FREE_FUNC(lib) FreeLibrary(lib)
|
||||
# else
|
||||
# include <dlfcn.h>
|
||||
# define FFNV_LOAD_FUNC(path) dlopen((path), RTLD_LAZY)
|
||||
# define FFNV_SYM_FUNC(lib, sym) dlsym((lib), (sym))
|
||||
# define FFNV_FREE_FUNC(lib) dlclose(lib)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if !defined(FFNV_LOG_FUNC) || !defined(FFNV_DEBUG_LOG_FUNC)
|
||||
# include <stdio.h>
|
||||
# define FFNV_LOG_FUNC(logctx, msg, ...) fprintf(stderr, (msg), __VA_ARGS__)
|
||||
# define FFNV_DEBUG_LOG_FUNC(logctx, msg, ...)
|
||||
#endif
|
||||
|
||||
#define LOAD_LIBRARY(l, path) \
|
||||
do { \
|
||||
if (!((l) = FFNV_LOAD_FUNC(path))) { \
|
||||
FFNV_LOG_FUNC(logctx, "Cannot load %s\n", path); \
|
||||
ret = -1; \
|
||||
goto error; \
|
||||
} \
|
||||
FFNV_DEBUG_LOG_FUNC(logctx, "Loaded lib: %s\n", path); \
|
||||
} while (0)
|
||||
|
||||
#define LOAD_SYMBOL(fun, tp, symbol) \
|
||||
do { \
|
||||
if (!((f->fun) = (tp*)FFNV_SYM_FUNC(f->lib, symbol))) { \
|
||||
FFNV_LOG_FUNC(logctx, "Cannot load %s\n", symbol); \
|
||||
ret = -1; \
|
||||
goto error; \
|
||||
} \
|
||||
FFNV_DEBUG_LOG_FUNC(logctx, "Loaded sym: %s\n", symbol); \
|
||||
} while (0)
|
||||
|
||||
#define LOAD_SYMBOL_OPT(fun, tp, symbol) \
|
||||
do { \
|
||||
if (!((f->fun) = (tp*)FFNV_SYM_FUNC(f->lib, symbol))) { \
|
||||
FFNV_DEBUG_LOG_FUNC(logctx, "Cannot load optional %s\n", symbol); \
|
||||
} else { \
|
||||
FFNV_DEBUG_LOG_FUNC(logctx, "Loaded sym: %s\n", symbol); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define GENERIC_LOAD_FUNC_PREAMBLE(T, n, N) \
|
||||
T *f; \
|
||||
int ret; \
|
||||
\
|
||||
n##_free_functions(functions); \
|
||||
\
|
||||
f = *functions = (T*)calloc(1, sizeof(*f)); \
|
||||
if (!f) \
|
||||
return -1; \
|
||||
\
|
||||
LOAD_LIBRARY(f->lib, N);
|
||||
|
||||
#define GENERIC_LOAD_FUNC_FINALE(n) \
|
||||
return 0; \
|
||||
error: \
|
||||
n##_free_functions(functions); \
|
||||
return ret;
|
||||
|
||||
#define GENERIC_FREE_FUNC() \
|
||||
if (!functions) \
|
||||
return; \
|
||||
if (*functions && (*functions)->lib) \
|
||||
FFNV_FREE_FUNC((*functions)->lib); \
|
||||
free(*functions); \
|
||||
*functions = NULL;
|
||||
|
||||
#ifdef FFNV_DYNLINK_CUDA_H
|
||||
typedef struct CudaFunctions {
|
||||
tcuInit *cuInit;
|
||||
tcuDeviceGetCount *cuDeviceGetCount;
|
||||
tcuDeviceGet *cuDeviceGet;
|
||||
tcuDeviceGetAttribute *cuDeviceGetAttribute;
|
||||
tcuDeviceGetName *cuDeviceGetName;
|
||||
tcuDeviceGetUuid *cuDeviceGetUuid;
|
||||
tcuDeviceComputeCapability *cuDeviceComputeCapability;
|
||||
tcuCtxCreate_v2 *cuCtxCreate;
|
||||
tcuCtxSetLimit *cuCtxSetLimit;
|
||||
tcuCtxPushCurrent_v2 *cuCtxPushCurrent;
|
||||
tcuCtxPopCurrent_v2 *cuCtxPopCurrent;
|
||||
tcuCtxDestroy_v2 *cuCtxDestroy;
|
||||
tcuMemAlloc_v2 *cuMemAlloc;
|
||||
tcuMemAllocPitch_v2 *cuMemAllocPitch;
|
||||
tcuMemsetD8Async *cuMemsetD8Async;
|
||||
tcuMemFree_v2 *cuMemFree;
|
||||
tcuMemcpy *cuMemcpy;
|
||||
tcuMemcpyAsync *cuMemcpyAsync;
|
||||
tcuMemcpy2D_v2 *cuMemcpy2D;
|
||||
tcuMemcpy2DAsync_v2 *cuMemcpy2DAsync;
|
||||
tcuGetErrorName *cuGetErrorName;
|
||||
tcuGetErrorString *cuGetErrorString;
|
||||
tcuCtxGetDevice *cuCtxGetDevice;
|
||||
|
||||
tcuDevicePrimaryCtxRetain *cuDevicePrimaryCtxRetain;
|
||||
tcuDevicePrimaryCtxRelease *cuDevicePrimaryCtxRelease;
|
||||
tcuDevicePrimaryCtxSetFlags *cuDevicePrimaryCtxSetFlags;
|
||||
tcuDevicePrimaryCtxGetState *cuDevicePrimaryCtxGetState;
|
||||
tcuDevicePrimaryCtxReset *cuDevicePrimaryCtxReset;
|
||||
|
||||
tcuStreamCreate *cuStreamCreate;
|
||||
tcuStreamQuery *cuStreamQuery;
|
||||
tcuStreamSynchronize *cuStreamSynchronize;
|
||||
tcuStreamDestroy_v2 *cuStreamDestroy;
|
||||
tcuStreamAddCallback *cuStreamAddCallback;
|
||||
tcuEventCreate *cuEventCreate;
|
||||
tcuEventDestroy_v2 *cuEventDestroy;
|
||||
tcuEventSynchronize *cuEventSynchronize;
|
||||
tcuEventQuery *cuEventQuery;
|
||||
tcuEventRecord *cuEventRecord;
|
||||
|
||||
tcuLaunchKernel *cuLaunchKernel;
|
||||
tcuLinkCreate *cuLinkCreate;
|
||||
tcuLinkAddData *cuLinkAddData;
|
||||
tcuLinkComplete *cuLinkComplete;
|
||||
tcuLinkDestroy *cuLinkDestroy;
|
||||
tcuModuleLoadData *cuModuleLoadData;
|
||||
tcuModuleUnload *cuModuleUnload;
|
||||
tcuModuleGetFunction *cuModuleGetFunction;
|
||||
tcuModuleGetGlobal *cuModuleGetGlobal;
|
||||
tcuTexObjectCreate *cuTexObjectCreate;
|
||||
tcuTexObjectDestroy *cuTexObjectDestroy;
|
||||
|
||||
tcuGLGetDevices_v2 *cuGLGetDevices;
|
||||
tcuGraphicsGLRegisterImage *cuGraphicsGLRegisterImage;
|
||||
tcuGraphicsUnregisterResource *cuGraphicsUnregisterResource;
|
||||
tcuGraphicsMapResources *cuGraphicsMapResources;
|
||||
tcuGraphicsUnmapResources *cuGraphicsUnmapResources;
|
||||
tcuGraphicsSubResourceGetMappedArray *cuGraphicsSubResourceGetMappedArray;
|
||||
|
||||
tcuImportExternalMemory *cuImportExternalMemory;
|
||||
tcuDestroyExternalMemory *cuDestroyExternalMemory;
|
||||
tcuExternalMemoryGetMappedBuffer *cuExternalMemoryGetMappedBuffer;
|
||||
tcuExternalMemoryGetMappedMipmappedArray *cuExternalMemoryGetMappedMipmappedArray;
|
||||
tcuMipmappedArrayDestroy *cuMipmappedArrayDestroy;
|
||||
|
||||
tcuMipmappedArrayGetLevel *cuMipmappedArrayGetLevel;
|
||||
|
||||
tcuImportExternalSemaphore *cuImportExternalSemaphore;
|
||||
tcuDestroyExternalSemaphore *cuDestroyExternalSemaphore;
|
||||
tcuSignalExternalSemaphoresAsync *cuSignalExternalSemaphoresAsync;
|
||||
tcuWaitExternalSemaphoresAsync *cuWaitExternalSemaphoresAsync;
|
||||
|
||||
FFNV_LIB_HANDLE lib;
|
||||
} CudaFunctions;
|
||||
#else
|
||||
typedef struct CudaFunctions CudaFunctions;
|
||||
#endif
|
||||
|
||||
typedef NVENCSTATUS NVENCAPI tNvEncodeAPICreateInstance(NV_ENCODE_API_FUNCTION_LIST *functionList);
|
||||
typedef NVENCSTATUS NVENCAPI tNvEncodeAPIGetMaxSupportedVersion(uint32_t* version);
|
||||
|
||||
typedef struct NvencFunctions {
|
||||
tNvEncodeAPICreateInstance *NvEncodeAPICreateInstance;
|
||||
tNvEncodeAPIGetMaxSupportedVersion *NvEncodeAPIGetMaxSupportedVersion;
|
||||
|
||||
FFNV_LIB_HANDLE lib;
|
||||
} NvencFunctions;
|
||||
|
||||
#ifdef FFNV_DYNLINK_CUDA_H
|
||||
static inline void cuda_free_functions(CudaFunctions **functions)
|
||||
{
|
||||
GENERIC_FREE_FUNC();
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void nvenc_free_functions(NvencFunctions **functions)
|
||||
{
|
||||
GENERIC_FREE_FUNC();
|
||||
}
|
||||
|
||||
#ifdef FFNV_DYNLINK_CUDA_H
|
||||
static inline int cuda_load_functions(CudaFunctions **functions)
|
||||
{
|
||||
GENERIC_LOAD_FUNC_PREAMBLE(CudaFunctions, cuda, CUDA_LIBNAME);
|
||||
|
||||
LOAD_SYMBOL(cuInit, tcuInit, "cuInit");
|
||||
LOAD_SYMBOL(cuDeviceGetCount, tcuDeviceGetCount, "cuDeviceGetCount");
|
||||
LOAD_SYMBOL(cuDeviceGet, tcuDeviceGet, "cuDeviceGet");
|
||||
LOAD_SYMBOL(cuDeviceGetAttribute, tcuDeviceGetAttribute, "cuDeviceGetAttribute");
|
||||
LOAD_SYMBOL(cuDeviceGetName, tcuDeviceGetName, "cuDeviceGetName");
|
||||
LOAD_SYMBOL(cuDeviceComputeCapability, tcuDeviceComputeCapability, "cuDeviceComputeCapability");
|
||||
LOAD_SYMBOL(cuCtxCreate, tcuCtxCreate_v2, "cuCtxCreate_v2");
|
||||
LOAD_SYMBOL(cuCtxSetLimit, tcuCtxSetLimit, "cuCtxSetLimit");
|
||||
LOAD_SYMBOL(cuCtxPushCurrent, tcuCtxPushCurrent_v2, "cuCtxPushCurrent_v2");
|
||||
LOAD_SYMBOL(cuCtxPopCurrent, tcuCtxPopCurrent_v2, "cuCtxPopCurrent_v2");
|
||||
LOAD_SYMBOL(cuCtxDestroy, tcuCtxDestroy_v2, "cuCtxDestroy_v2");
|
||||
LOAD_SYMBOL(cuMemAlloc, tcuMemAlloc_v2, "cuMemAlloc_v2");
|
||||
LOAD_SYMBOL(cuMemAllocPitch, tcuMemAllocPitch_v2, "cuMemAllocPitch_v2");
|
||||
LOAD_SYMBOL(cuMemsetD8Async, tcuMemsetD8Async, "cuMemsetD8Async");
|
||||
LOAD_SYMBOL(cuMemFree, tcuMemFree_v2, "cuMemFree_v2");
|
||||
LOAD_SYMBOL(cuMemcpy, tcuMemcpy, "cuMemcpy");
|
||||
LOAD_SYMBOL(cuMemcpyAsync, tcuMemcpyAsync, "cuMemcpyAsync");
|
||||
LOAD_SYMBOL(cuMemcpy2D, tcuMemcpy2D_v2, "cuMemcpy2D_v2");
|
||||
LOAD_SYMBOL(cuMemcpy2DAsync, tcuMemcpy2DAsync_v2, "cuMemcpy2DAsync_v2");
|
||||
LOAD_SYMBOL(cuGetErrorName, tcuGetErrorName, "cuGetErrorName");
|
||||
LOAD_SYMBOL(cuGetErrorString, tcuGetErrorString, "cuGetErrorString");
|
||||
LOAD_SYMBOL(cuCtxGetDevice, tcuCtxGetDevice, "cuCtxGetDevice");
|
||||
|
||||
LOAD_SYMBOL(cuDevicePrimaryCtxRetain, tcuDevicePrimaryCtxRetain, "cuDevicePrimaryCtxRetain");
|
||||
LOAD_SYMBOL(cuDevicePrimaryCtxRelease, tcuDevicePrimaryCtxRelease, "cuDevicePrimaryCtxRelease");
|
||||
LOAD_SYMBOL(cuDevicePrimaryCtxSetFlags, tcuDevicePrimaryCtxSetFlags, "cuDevicePrimaryCtxSetFlags");
|
||||
LOAD_SYMBOL(cuDevicePrimaryCtxGetState, tcuDevicePrimaryCtxGetState, "cuDevicePrimaryCtxGetState");
|
||||
LOAD_SYMBOL(cuDevicePrimaryCtxReset, tcuDevicePrimaryCtxReset, "cuDevicePrimaryCtxReset");
|
||||
|
||||
LOAD_SYMBOL(cuStreamCreate, tcuStreamCreate, "cuStreamCreate");
|
||||
LOAD_SYMBOL(cuStreamQuery, tcuStreamQuery, "cuStreamQuery");
|
||||
LOAD_SYMBOL(cuStreamSynchronize, tcuStreamSynchronize, "cuStreamSynchronize");
|
||||
LOAD_SYMBOL(cuStreamDestroy, tcuStreamDestroy_v2, "cuStreamDestroy_v2");
|
||||
LOAD_SYMBOL(cuStreamAddCallback, tcuStreamAddCallback, "cuStreamAddCallback");
|
||||
LOAD_SYMBOL(cuEventCreate, tcuEventCreate, "cuEventCreate");
|
||||
LOAD_SYMBOL(cuEventDestroy, tcuEventDestroy_v2, "cuEventDestroy_v2");
|
||||
LOAD_SYMBOL(cuEventSynchronize, tcuEventSynchronize, "cuEventSynchronize");
|
||||
LOAD_SYMBOL(cuEventQuery, tcuEventQuery, "cuEventQuery");
|
||||
LOAD_SYMBOL(cuEventRecord, tcuEventRecord, "cuEventRecord");
|
||||
|
||||
LOAD_SYMBOL(cuLaunchKernel, tcuLaunchKernel, "cuLaunchKernel");
|
||||
LOAD_SYMBOL(cuLinkCreate, tcuLinkCreate, "cuLinkCreate");
|
||||
LOAD_SYMBOL(cuLinkAddData, tcuLinkAddData, "cuLinkAddData");
|
||||
LOAD_SYMBOL(cuLinkComplete, tcuLinkComplete, "cuLinkComplete");
|
||||
LOAD_SYMBOL(cuLinkDestroy, tcuLinkDestroy, "cuLinkDestroy");
|
||||
LOAD_SYMBOL(cuModuleLoadData, tcuModuleLoadData, "cuModuleLoadData");
|
||||
LOAD_SYMBOL(cuModuleUnload, tcuModuleUnload, "cuModuleUnload");
|
||||
LOAD_SYMBOL(cuModuleGetFunction, tcuModuleGetFunction, "cuModuleGetFunction");
|
||||
LOAD_SYMBOL(cuModuleGetGlobal, tcuModuleGetGlobal, "cuModuleGetGlobal");
|
||||
LOAD_SYMBOL(cuTexObjectCreate, tcuTexObjectCreate, "cuTexObjectCreate");
|
||||
LOAD_SYMBOL(cuTexObjectDestroy, tcuTexObjectDestroy, "cuTexObjectDestroy");
|
||||
|
||||
LOAD_SYMBOL(cuGLGetDevices, tcuGLGetDevices_v2, "cuGLGetDevices_v2");
|
||||
LOAD_SYMBOL(cuGraphicsGLRegisterImage, tcuGraphicsGLRegisterImage, "cuGraphicsGLRegisterImage");
|
||||
LOAD_SYMBOL(cuGraphicsUnregisterResource, tcuGraphicsUnregisterResource, "cuGraphicsUnregisterResource");
|
||||
LOAD_SYMBOL(cuGraphicsMapResources, tcuGraphicsMapResources, "cuGraphicsMapResources");
|
||||
LOAD_SYMBOL(cuGraphicsUnmapResources, tcuGraphicsUnmapResources, "cuGraphicsUnmapResources");
|
||||
LOAD_SYMBOL(cuGraphicsSubResourceGetMappedArray, tcuGraphicsSubResourceGetMappedArray, "cuGraphicsSubResourceGetMappedArray");
|
||||
|
||||
LOAD_SYMBOL_OPT(cuDeviceGetUuid, tcuDeviceGetUuid, "cuDeviceGetUuid");
|
||||
LOAD_SYMBOL_OPT(cuImportExternalMemory, tcuImportExternalMemory, "cuImportExternalMemory");
|
||||
LOAD_SYMBOL_OPT(cuDestroyExternalMemory, tcuDestroyExternalMemory, "cuDestroyExternalMemory");
|
||||
LOAD_SYMBOL_OPT(cuExternalMemoryGetMappedBuffer, tcuExternalMemoryGetMappedBuffer, "cuExternalMemoryGetMappedBuffer");
|
||||
LOAD_SYMBOL_OPT(cuExternalMemoryGetMappedMipmappedArray, tcuExternalMemoryGetMappedMipmappedArray, "cuExternalMemoryGetMappedMipmappedArray");
|
||||
LOAD_SYMBOL_OPT(cuMipmappedArrayGetLevel, tcuMipmappedArrayGetLevel, "cuMipmappedArrayGetLevel");
|
||||
LOAD_SYMBOL_OPT(cuMipmappedArrayDestroy, tcuMipmappedArrayDestroy, "cuMipmappedArrayDestroy");
|
||||
|
||||
LOAD_SYMBOL_OPT(cuImportExternalSemaphore, tcuImportExternalSemaphore, "cuImportExternalSemaphore");
|
||||
LOAD_SYMBOL_OPT(cuDestroyExternalSemaphore, tcuDestroyExternalSemaphore, "cuDestroyExternalSemaphore");
|
||||
LOAD_SYMBOL_OPT(cuSignalExternalSemaphoresAsync, tcuSignalExternalSemaphoresAsync, "cuSignalExternalSemaphoresAsync");
|
||||
LOAD_SYMBOL_OPT(cuWaitExternalSemaphoresAsync, tcuWaitExternalSemaphoresAsync, "cuWaitExternalSemaphoresAsync");
|
||||
|
||||
GENERIC_LOAD_FUNC_FINALE(cuda);
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline int nvenc_load_functions(NvencFunctions **functions)
|
||||
{
|
||||
GENERIC_LOAD_FUNC_PREAMBLE(NvencFunctions, nvenc, NVENC_LIBNAME);
|
||||
|
||||
LOAD_SYMBOL(NvEncodeAPICreateInstance, tNvEncodeAPICreateInstance, "NvEncodeAPICreateInstance");
|
||||
LOAD_SYMBOL(NvEncodeAPIGetMaxSupportedVersion, tNvEncodeAPIGetMaxSupportedVersion, "NvEncodeAPIGetMaxSupportedVersion");
|
||||
|
||||
GENERIC_LOAD_FUNC_FINALE(nvenc);
|
||||
}
|
||||
|
||||
#undef GENERIC_LOAD_FUNC_PREAMBLE
|
||||
#undef LOAD_LIBRARY
|
||||
#undef LOAD_SYMBOL
|
||||
#undef GENERIC_LOAD_FUNC_FINALE
|
||||
#undef GENERIC_FREE_FUNC
|
||||
#undef CUDA_LIBNAME
|
||||
#undef NVCUVID_LIBNAME
|
||||
#undef NVENC_LIBNAME
|
||||
|
||||
#endif
|
||||
|
||||
@@ -85,6 +85,12 @@ namespace rfb {
|
||||
const int pseudoEncodingVideoOutTimeLevel1 = -1986;
|
||||
const int pseudoEncodingVideoOutTimeLevel100 = -1887;
|
||||
|
||||
// VMware-specific
|
||||
const int pseudoEncodingVMwareCursorPosition = 0x574d5666;
|
||||
|
||||
// UltraVNC-specific
|
||||
const int pseudoEncodingExtendedClipboard = 0xC0A1E5CE;
|
||||
|
||||
int encodingNum(const char* name);
|
||||
const char* encodingName(int num);
|
||||
}
|
||||
|
||||
98
common/rfb/mp4.c
Normal file
98
common/rfb/mp4.c
Normal file
@@ -0,0 +1,98 @@
|
||||
#include "mp4.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
uint32_t default_sample_size = 40000;
|
||||
|
||||
enum BufError create_header();
|
||||
|
||||
void set_sps(struct Mp4Context *ctx, const uint8_t* nal_data, const uint32_t nal_len) {
|
||||
memcpy(ctx->buf_sps, nal_data, nal_len);
|
||||
ctx->buf_sps_len = nal_len;
|
||||
create_header(ctx);
|
||||
}
|
||||
|
||||
void set_pps(struct Mp4Context *ctx, const uint8_t* nal_data, const uint32_t nal_len) {
|
||||
memcpy(ctx->buf_pps, nal_data, nal_len);
|
||||
ctx->buf_pps_len = nal_len;
|
||||
create_header(ctx);
|
||||
}
|
||||
|
||||
enum BufError create_header(struct Mp4Context *ctx) {
|
||||
if (ctx->buf_header.offset > 0) return BUF_OK;
|
||||
if (ctx->buf_sps_len == 0) return BUF_OK;
|
||||
if (ctx->buf_pps_len == 0) return BUF_OK;
|
||||
|
||||
struct MoovInfo moov_info;
|
||||
memset(&moov_info, 0, sizeof(struct MoovInfo));
|
||||
moov_info.width = ctx->w;
|
||||
moov_info.height = ctx->h;
|
||||
moov_info.horizontal_resolution = 0x00480000; // 72 dpi
|
||||
moov_info.vertical_resolution = 0x00480000; // 72 dpi
|
||||
moov_info.creation_time = 0;
|
||||
moov_info.timescale = default_sample_size * ctx->framerate;
|
||||
moov_info.sps = (uint8_t *) ctx->buf_sps;
|
||||
moov_info.sps_length = ctx->buf_sps_len;
|
||||
moov_info.pps = (uint8_t *) ctx->buf_pps;
|
||||
moov_info.pps_length = ctx->buf_pps_len;
|
||||
|
||||
ctx->buf_header.offset = 0;
|
||||
enum BufError err = write_header(&ctx->buf_header, &moov_info); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError get_header(struct Mp4Context *ctx, struct BitBuf *ptr) {
|
||||
ptr->buf = ctx->buf_header.buf;
|
||||
ptr->size = ctx->buf_header.size;
|
||||
ptr->offset = ctx->buf_header.offset;
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError set_slice(struct Mp4Context *ctx, const uint8_t* nal_data, const uint32_t origlen, const uint32_t nal_len, const enum NalUnitType unit_type) {
|
||||
enum BufError err;
|
||||
|
||||
const uint32_t samples_info_len = 1;
|
||||
struct SampleInfo samples_info[1];
|
||||
memset(&samples_info[0], 0, sizeof(struct SampleInfo));
|
||||
samples_info[0].size = nal_len + 4; // add size of sample
|
||||
samples_info[0].composition_offset = default_sample_size;
|
||||
samples_info[0].decode_time = default_sample_size;
|
||||
samples_info[0].duration = default_sample_size;
|
||||
samples_info[0].flags = unit_type == NalUnitType_CodedSliceIdr ? 0 : 65536;
|
||||
|
||||
ctx->buf_moof.offset = 0;
|
||||
err = write_moof(&ctx->buf_moof, 0, 0, 0, default_sample_size, samples_info, samples_info_len); chk_err
|
||||
|
||||
ctx->buf_mdat.offset = 0;
|
||||
// printf("nal_data %d\n", nal_data);
|
||||
// printf(" slice: "); for (int i = 0; i < 32; ++i) printf(" 0x%02hhX", nal_data[i]); printf("\n");
|
||||
|
||||
err = write_mdat(&ctx->buf_mdat, nal_data, origlen, nal_len); chk_err
|
||||
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError set_mp4_state(struct Mp4Context *ctx, struct Mp4State *state) {
|
||||
enum BufError err = BUF_OK;
|
||||
if (pos_sequence_number > 0) err = put_u32_be_to_offset(&ctx->buf_moof, pos_sequence_number, state->sequence_number); chk_err
|
||||
if (pos_base_data_offset > 0) err = put_u64_be_to_offset(&ctx->buf_moof, pos_base_data_offset, state->base_data_offset); chk_err
|
||||
if (pos_base_media_decode_time > 0) err = put_u64_be_to_offset(&ctx->buf_moof, pos_base_media_decode_time, state->base_media_decode_time); chk_err
|
||||
state->sequence_number++;
|
||||
state->base_data_offset += ctx->buf_moof.offset + ctx->buf_mdat.offset;
|
||||
state->base_media_decode_time += state->default_sample_duration;
|
||||
return BUF_OK;
|
||||
}
|
||||
enum BufError get_moof(struct Mp4Context *ctx, struct BitBuf *ptr) {
|
||||
ptr->buf = ctx->buf_moof.buf;
|
||||
ptr->size = ctx->buf_moof.size;
|
||||
ptr->offset = ctx->buf_moof.offset;
|
||||
return BUF_OK;
|
||||
}
|
||||
enum BufError get_mdat(struct Mp4Context *ctx, struct BitBuf *ptr) {
|
||||
ptr->buf = ctx->buf_mdat.buf;
|
||||
ptr->size = ctx->buf_mdat.size;
|
||||
ptr->offset = ctx->buf_mdat.offset;
|
||||
// int o = 4+4;
|
||||
// printf(" get_mdat: "); for (int i = 0; i < 32; ++i) printf(" 0x%02hhX", ptr->buf[o+i]); printf("\n");
|
||||
return BUF_OK;
|
||||
}
|
||||
197
common/rfb/mp4.h
Normal file
197
common/rfb/mp4.h
Normal file
@@ -0,0 +1,197 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define chk_err if (err != BUF_OK) { printf("Error buf: %s %s(...) %s:%d\n", buf_error_to_str(err), __func__, __FILE__, __LINE__); return err; }
|
||||
#define chk_err_continue if (err != BUF_OK) { printf("Error buf: %s %s(...) %s:%d\n", buf_error_to_str(err), __func__, __FILE__, __LINE__); continue; }
|
||||
|
||||
enum BufError {
|
||||
BUF_OK = 0,
|
||||
BUF_ENDOFBUF_ERROR,
|
||||
BUF_MALLOC_ERROR,
|
||||
BUF_INCORRECT
|
||||
};
|
||||
char* buf_error_to_str(const enum BufError err);
|
||||
|
||||
struct BitBuf {
|
||||
char *buf;
|
||||
uint32_t size;
|
||||
uint32_t offset;
|
||||
};
|
||||
|
||||
enum BufError put_skip(struct BitBuf *ptr, const uint32_t count);
|
||||
enum BufError put_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint8_t* data, const uint32_t size);
|
||||
enum BufError put(struct BitBuf *ptr, const uint8_t* data, const uint32_t size);
|
||||
enum BufError put_u8_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint8_t val);
|
||||
enum BufError put_u8(struct BitBuf *ptr, uint8_t val);
|
||||
enum BufError put_u16_be_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint16_t val);
|
||||
enum BufError put_u16_be(struct BitBuf *ptr, const uint16_t val);
|
||||
enum BufError put_u16_le_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint16_t val);
|
||||
enum BufError put_u16_le(struct BitBuf *ptr, const uint16_t val);
|
||||
enum BufError put_u32_be_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint32_t val);
|
||||
enum BufError put_u32_be(struct BitBuf *ptr, const uint32_t val);
|
||||
enum BufError put_i32_be(struct BitBuf *ptr, const int32_t val);
|
||||
enum BufError put_u64_be_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint64_t val);
|
||||
enum BufError put_u64_be(struct BitBuf *ptr, const uint64_t val);
|
||||
enum BufError put_u32_le_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint32_t val);
|
||||
enum BufError put_u32_le(struct BitBuf *ptr, const uint32_t val);
|
||||
enum BufError put_str4_to_offset(struct BitBuf *ptr, const uint32_t offset, const char str[4]);
|
||||
enum BufError put_str4(struct BitBuf *ptr, const char str[4]);
|
||||
enum BufError put_counted_str_to_offset(struct BitBuf *ptr, const uint32_t offset, const char *str, const uint32_t len);
|
||||
enum BufError put_counted_str(struct BitBuf *ptr, const char *str, const uint32_t len);
|
||||
|
||||
struct MoovInfo {
|
||||
uint8_t profile_idc;
|
||||
uint8_t level_idc;
|
||||
uint8_t *sps;
|
||||
uint16_t sps_length;
|
||||
uint8_t *pps;
|
||||
uint16_t pps_length;
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
uint32_t horizontal_resolution;
|
||||
uint32_t vertical_resolution;
|
||||
uint32_t creation_time;
|
||||
uint32_t timescale;
|
||||
};
|
||||
|
||||
enum BufError write_header(struct BitBuf *ptr, struct MoovInfo *moov_info);
|
||||
|
||||
extern uint32_t pos_sequence_number;
|
||||
extern uint32_t pos_base_data_offset;
|
||||
extern uint32_t pos_base_media_decode_time;
|
||||
|
||||
struct SampleInfo {
|
||||
uint32_t duration;
|
||||
uint32_t decode_time;
|
||||
uint32_t composition_time;
|
||||
uint32_t composition_offset;
|
||||
uint32_t size;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
enum BufError write_mdat(struct BitBuf *ptr, const uint8_t* data, const uint32_t origlen, const uint32_t len);
|
||||
enum BufError write_moof(struct BitBuf *ptr,
|
||||
const uint32_t sequence_number,
|
||||
const uint64_t base_data_offset,
|
||||
const uint64_t base_media_decode_time,
|
||||
const uint32_t default_sample_duration,
|
||||
const struct SampleInfo *samples_info, const uint32_t samples_info_len);
|
||||
|
||||
enum NalUnitType { // Table 7-1 NAL unit type codes
|
||||
NalUnitType_Unspecified = 0, // Unspecified
|
||||
NalUnitType_CodedSliceNonIdr = 1, // Coded slice of a non-IDR picture
|
||||
NalUnitType_CodedSliceDataPartitionA = 2, // Coded slice data partition A
|
||||
NalUnitType_CodedSliceDataPartitionB = 3, // Coded slice data partition B
|
||||
NalUnitType_CodedSliceDataPartitionC = 4, // Coded slice data partition C
|
||||
NalUnitType_CodedSliceIdr = 5, // Coded slice of an IDR picture
|
||||
NalUnitType_SEI = 6, // Supplemental enhancement information (SEI)
|
||||
NalUnitType_SPS = 7, // Sequence parameter set
|
||||
NalUnitType_PPS = 8, // Picture parameter set
|
||||
NalUnitType_AUD = 9, // Access unit delimiter
|
||||
NalUnitType_EndOfSequence = 10, // End of sequence
|
||||
NalUnitType_EndOfStream = 11, // End of stream
|
||||
NalUnitType_Filler = 12, // Filler data
|
||||
NalUnitType_SpsExt = 13, // Sequence parameter set extension
|
||||
// 14..18 // Reserved
|
||||
NalUnitType_CodedSliceAux = 19, // Coded slice of an auxiliary coded picture without partitioning
|
||||
// 20..23 // Reserved
|
||||
// 24..31 // Unspecified
|
||||
};
|
||||
|
||||
struct NAL {
|
||||
char *data;
|
||||
uint64_t data_size;
|
||||
uint32_t picture_order_count;
|
||||
|
||||
// NAL header
|
||||
bool forbidden_zero_bit;
|
||||
uint8_t ref_idc;
|
||||
uint8_t unit_type_value;
|
||||
enum NalUnitType unit_type;
|
||||
};
|
||||
|
||||
static inline const char* nal_type_to_str(const enum NalUnitType nal_type) {
|
||||
switch (nal_type) {
|
||||
case NalUnitType_Unspecified: return "Unspecified";
|
||||
case NalUnitType_CodedSliceNonIdr: return "CodedSliceNonIdr";
|
||||
case NalUnitType_CodedSliceDataPartitionA: return "CodedSliceDataPartitionA";
|
||||
case NalUnitType_CodedSliceDataPartitionB: return "CodedSliceDataPartitionB";
|
||||
case NalUnitType_CodedSliceDataPartitionC: return "CodedSliceDataPartitionC";
|
||||
case NalUnitType_CodedSliceIdr: return "CodedSliceIdr";
|
||||
case NalUnitType_SEI: return "SEI";
|
||||
case NalUnitType_SPS: return "SPS";
|
||||
case NalUnitType_PPS: return "PPS";
|
||||
case NalUnitType_AUD: return "AUD";
|
||||
case NalUnitType_EndOfSequence: return "EndOfSequence";
|
||||
case NalUnitType_EndOfStream: return "EndOfStream";
|
||||
case NalUnitType_Filler: return "Filler";
|
||||
case NalUnitType_SpsExt: return "SpsExt";
|
||||
case NalUnitType_CodedSliceAux: return "CodedSliceAux";
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static inline void nal_parse_header(struct NAL *nal, const char first_byte) {
|
||||
nal->forbidden_zero_bit = ((first_byte & 0x80) >> 7) == 1;
|
||||
nal->ref_idc = (first_byte & 0x60) >> 5;
|
||||
nal->unit_type = (enum NalUnitType) ((first_byte & 0x1f) >> 0);
|
||||
}
|
||||
|
||||
static inline bool nal_chk4(const uint8_t *buf, const uint32_t offset) {
|
||||
if (buf[offset] == 0x00 && buf[offset+1] == 0x00 && buf[offset+2] == 0x01) { return true; }
|
||||
if (buf[offset] == 0x00 && buf[offset+1] == 0x00 && buf[offset+2] == 0x00 && buf[offset+3] == 0x01) { return true; }
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool nal_chk3(const uint8_t *buf, const uint32_t offset) {
|
||||
if (buf[offset] == 0x00 && buf[offset+1] == 0x00 && buf[offset+2] == 0x01) { return true; }
|
||||
return false;
|
||||
}
|
||||
|
||||
extern uint32_t default_sample_size;
|
||||
|
||||
struct Mp4State {
|
||||
bool header_sent;
|
||||
|
||||
uint32_t sequence_number;
|
||||
uint64_t base_data_offset;
|
||||
uint64_t base_media_decode_time;
|
||||
uint32_t default_sample_duration;
|
||||
|
||||
uint32_t nals_count;
|
||||
};
|
||||
|
||||
struct Mp4Context {
|
||||
char buf_sps[128];
|
||||
uint16_t buf_sps_len;
|
||||
char buf_pps[128];
|
||||
uint16_t buf_pps_len;
|
||||
|
||||
uint16_t w, h, framerate;
|
||||
|
||||
struct BitBuf buf_header;
|
||||
struct BitBuf buf_moof;
|
||||
struct BitBuf buf_mdat;
|
||||
};
|
||||
|
||||
|
||||
enum BufError set_slice(struct Mp4Context *ctx, const uint8_t* nal_data, const uint32_t origlen, const uint32_t nal_len, const enum NalUnitType unit_type);
|
||||
void set_sps(struct Mp4Context *ctx, const uint8_t* nal_data, const uint32_t nal_len);
|
||||
void set_pps(struct Mp4Context *ctx, const uint8_t* nal_data, const uint32_t nal_len);
|
||||
|
||||
enum BufError get_header(struct Mp4Context *ctx, struct BitBuf *ptr);
|
||||
|
||||
enum BufError set_mp4_state(struct Mp4Context *ctx, struct Mp4State *state);
|
||||
enum BufError get_moof(struct Mp4Context *ctx, struct BitBuf *ptr);
|
||||
enum BufError get_mdat(struct Mp4Context *ctx, struct BitBuf *ptr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern C
|
||||
#endif
|
||||
193
common/rfb/mp4_bitbuf.c
Normal file
193
common/rfb/mp4_bitbuf.c
Normal file
@@ -0,0 +1,193 @@
|
||||
#include "mp4.h"
|
||||
|
||||
#define chk_ptr if (!ptr) return BUF_INCORRECT;
|
||||
#define chk_realloc { enum BufError err; err = try_to_realloc(ptr, pos); if (err != BUF_OK) return err; }
|
||||
|
||||
char* buf_error_to_str(const enum BufError err) {
|
||||
switch (err) {
|
||||
case BUF_OK: return "BUF_OK";
|
||||
case BUF_ENDOFBUF_ERROR: return "BUF_ENDOFBUF_ERROR";
|
||||
case BUF_MALLOC_ERROR: return "BUF_MALLOC_ERROR";
|
||||
case BUF_INCORRECT: return "BUF_INCORRECT";
|
||||
default: { static char str[32]; sprintf(str, "Unknown(%d)", err); return str; }
|
||||
}
|
||||
}
|
||||
|
||||
enum BufError try_to_realloc(struct BitBuf *ptr, const uint32_t min_size) {
|
||||
chk_ptr
|
||||
uint32_t new_size = ptr->size + min_size + 1024;
|
||||
char* new_buf = realloc(ptr->buf, new_size);
|
||||
if (new_buf == NULL) return BUF_MALLOC_ERROR;
|
||||
ptr->buf = new_buf;
|
||||
ptr->size = new_size;
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError put_skip(struct BitBuf *ptr, const uint32_t count) {
|
||||
chk_ptr
|
||||
uint32_t pos = ptr->offset + count;
|
||||
if (pos >= ptr->size) chk_realloc
|
||||
for (uint32_t i = 0; i < count; i++) ptr->buf[ptr->offset + i] = 0;
|
||||
ptr->offset = pos;
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError put_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint8_t* data, const uint32_t size) {
|
||||
chk_ptr
|
||||
uint32_t pos = offset + size;
|
||||
if (pos >= ptr->size) chk_realloc
|
||||
for (uint32_t i = 0; i < size; i++) ptr->buf[offset + i] = data[i];
|
||||
return BUF_OK;
|
||||
}
|
||||
enum BufError put(struct BitBuf *ptr, const uint8_t* data, const uint32_t size) {
|
||||
chk_ptr
|
||||
enum BufError err = put_to_offset(ptr, ptr->offset, data, size); chk_err
|
||||
ptr->offset += size;
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError put_u8_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint8_t val) {
|
||||
chk_ptr
|
||||
uint32_t pos = offset + sizeof(uint8_t);
|
||||
if (pos >= ptr->size) chk_realloc
|
||||
ptr->buf[offset + 0] = val & 0xff;
|
||||
return BUF_OK;
|
||||
}
|
||||
enum BufError put_u8(struct BitBuf *ptr, uint8_t val) {
|
||||
chk_ptr
|
||||
enum BufError err = put_u8_to_offset(ptr, ptr->offset, val); chk_err
|
||||
ptr->offset += sizeof(uint8_t);
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError put_u16_be_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint16_t val) {
|
||||
chk_ptr
|
||||
uint32_t pos = offset + sizeof(uint16_t);
|
||||
if (pos >= ptr->size) chk_realloc
|
||||
ptr->buf[offset + 0] = (val >> 8) & 0xff;
|
||||
ptr->buf[offset + 1] = (val >> 0) & 0xff;
|
||||
return BUF_OK;
|
||||
}
|
||||
enum BufError put_u16_be(struct BitBuf *ptr, const uint16_t val) {
|
||||
chk_ptr
|
||||
enum BufError err = put_u16_be_to_offset(ptr, ptr->offset, val); chk_err
|
||||
ptr->offset += sizeof(uint16_t);
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError put_u16_le_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint16_t val) {
|
||||
chk_ptr
|
||||
uint32_t pos = offset + sizeof(uint16_t);
|
||||
if (pos >= ptr->size) chk_realloc
|
||||
ptr->buf[offset + 0] = (val >> 0) & 0xff;
|
||||
ptr->buf[offset + 1] = (val >> 8) & 0xff;
|
||||
return BUF_OK;
|
||||
}
|
||||
enum BufError put_u16_le(struct BitBuf *ptr, const uint16_t val) {
|
||||
chk_ptr
|
||||
enum BufError err = put_u16_le_to_offset(ptr, ptr->offset, val); chk_err
|
||||
ptr->offset += sizeof(uint16_t);
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError put_u32_be_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint32_t val) {
|
||||
chk_ptr
|
||||
uint32_t pos = offset + sizeof(uint32_t);
|
||||
if (pos >= ptr->size) chk_realloc
|
||||
ptr->buf[offset + 0] = (val >> 24) & 0xff;
|
||||
ptr->buf[offset + 1] = (val >> 16) & 0xff;
|
||||
ptr->buf[offset + 2] = (val >> 8) & 0xff;
|
||||
ptr->buf[offset + 3] = (val >> 0) & 0xff;
|
||||
return BUF_OK;
|
||||
}
|
||||
enum BufError put_u32_be(struct BitBuf *ptr, const uint32_t val) {
|
||||
chk_ptr
|
||||
enum BufError err = put_u32_be_to_offset(ptr, ptr->offset, val); chk_err
|
||||
ptr->offset += sizeof(uint32_t);
|
||||
return BUF_OK;
|
||||
}
|
||||
enum BufError put_i32_be(struct BitBuf *ptr, const int32_t val) {
|
||||
chk_ptr
|
||||
enum BufError err = put_u32_be_to_offset(ptr, ptr->offset, val); chk_err
|
||||
ptr->offset += sizeof(int32_t);
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError put_u64_be_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint64_t val) {
|
||||
chk_ptr
|
||||
uint32_t pos = offset + sizeof(uint64_t);
|
||||
if (pos > ptr->size) chk_realloc
|
||||
ptr->buf[offset + 0] = (val >> 56) & 0xff;
|
||||
ptr->buf[offset + 1] = (val >> 48) & 0xff;
|
||||
ptr->buf[offset + 2] = (val >> 40) & 0xff;
|
||||
ptr->buf[offset + 3] = (val >> 32) & 0xff;
|
||||
ptr->buf[offset + 4] = (val >> 24) & 0xff;
|
||||
ptr->buf[offset + 5] = (val >> 16) & 0xff;
|
||||
ptr->buf[offset + 6] = (val >> 8) & 0xff;
|
||||
ptr->buf[offset + 7] = (val >> 0) & 0xff;
|
||||
return BUF_OK;
|
||||
}
|
||||
enum BufError put_u64_be(struct BitBuf *ptr, const uint64_t val) {
|
||||
chk_ptr
|
||||
enum BufError err = put_u64_be_to_offset(ptr, ptr->offset, val); chk_err
|
||||
ptr->offset += sizeof(uint64_t);
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError put_u32_le_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint32_t val) {
|
||||
chk_ptr
|
||||
uint32_t pos = offset + 4;
|
||||
if (pos >= ptr->size) chk_realloc
|
||||
ptr->buf[offset + 0] = (val >> 0) & 0xff;
|
||||
ptr->buf[offset + 1] = (val >> 8) & 0xff;
|
||||
ptr->buf[offset + 2] = (val >> 16) & 0xff;
|
||||
ptr->buf[offset + 3] = (val >> 24) & 0xff;
|
||||
return BUF_OK;
|
||||
}
|
||||
enum BufError put_u32_le(struct BitBuf *ptr, const uint32_t val) {
|
||||
chk_ptr
|
||||
enum BufError err = put_u32_le_to_offset(ptr, ptr->offset, val); chk_err
|
||||
ptr->offset += sizeof(uint32_t);
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError put_str4_to_offset(struct BitBuf *ptr, const uint32_t offset, const char str[4]) {
|
||||
chk_ptr
|
||||
uint32_t pos = offset + 4;
|
||||
if (pos >= ptr->size) chk_realloc
|
||||
for (uint8_t i = 0; i < 4; i++) { ptr->buf[offset + i] = str[i]; }
|
||||
return BUF_OK;
|
||||
}
|
||||
enum BufError put_str4(struct BitBuf *ptr, const char str[4]) {
|
||||
chk_ptr
|
||||
enum BufError err = put_str4_to_offset(ptr, ptr->offset, str); chk_err
|
||||
ptr->offset += 4;
|
||||
return BUF_OK;
|
||||
}
|
||||
enum BufError put_counted_str_to_offset(struct BitBuf *ptr, const uint32_t offset, const char *str, const uint32_t len) {
|
||||
chk_ptr
|
||||
uint32_t pos = offset + len + 1;
|
||||
if (pos >= ptr->size) chk_realloc
|
||||
for (uint32_t i = 0; i < len+1; i++) { ptr->buf[offset + i] = str[i]; }
|
||||
ptr->buf[pos] = 0;
|
||||
return BUF_OK;
|
||||
}
|
||||
enum BufError put_counted_str(struct BitBuf *ptr, const char *str, const uint32_t len) {
|
||||
chk_ptr
|
||||
enum BufError err = put_counted_str_to_offset(ptr, ptr->offset, str, len); chk_err
|
||||
ptr->offset += len+1;
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
// enum BufError hexStr(struct BitBuf *ptr, char *str) : string {
|
||||
// let str = '';
|
||||
// for(let i = 0; i < this.offset; i++) {
|
||||
// if (i % 40 === 0) str += '\n';
|
||||
// if (i % 4 === 0 && i+1 < this.data.length) {
|
||||
// if (i > 0 && i % 40 !== 0) str += ' ';
|
||||
// str += '0x';
|
||||
// }
|
||||
// str += decimalToHex(this.data[i]);
|
||||
// }
|
||||
// return str;
|
||||
// }
|
||||
194
common/rfb/mp4_moof.c
Normal file
194
common/rfb/mp4_moof.c
Normal file
@@ -0,0 +1,194 @@
|
||||
#include "mp4.h"
|
||||
#include <string.h>
|
||||
|
||||
uint32_t pos_sequence_number = 0;
|
||||
uint32_t pos_base_data_offset = 0;
|
||||
uint32_t pos_base_media_decode_time = 0;
|
||||
|
||||
struct DataOffsetPos {
|
||||
bool data_offset_present;
|
||||
uint32_t offset;
|
||||
};
|
||||
|
||||
enum BufError write_mfhd(struct BitBuf *ptr, const uint32_t sequence_number);
|
||||
enum BufError write_traf(struct BitBuf *ptr,
|
||||
const uint32_t sequence_number,
|
||||
const uint64_t base_data_offset,
|
||||
const uint64_t base_media_decode_time,
|
||||
const uint32_t default_sample_duration,
|
||||
const struct SampleInfo *samples_info, const uint32_t samples_info_len,
|
||||
struct DataOffsetPos *data_offset);
|
||||
enum BufError write_tfhd(struct BitBuf *ptr,
|
||||
const uint32_t sequence_number,
|
||||
const uint64_t base_data_offset,
|
||||
const uint64_t base_media_decode_time,
|
||||
const uint32_t default_sample_size,
|
||||
const uint32_t default_sample_duration,
|
||||
const struct SampleInfo *samples_info, const uint32_t samples_info_len,
|
||||
struct DataOffsetPos *data_offset);
|
||||
enum BufError write_tfdt(struct BitBuf *ptr, const uint64_t base_media_decode_time);
|
||||
enum BufError write_trun(struct BitBuf *ptr,
|
||||
const struct SampleInfo *samples_info, const uint32_t samples_info_count,
|
||||
struct DataOffsetPos *data_offset);
|
||||
|
||||
enum BufError write_mdat(struct BitBuf *ptr, const uint8_t* data, const uint32_t origlen, const uint32_t len) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "mdat"); chk_err
|
||||
err = put_u32_be(ptr, origlen); chk_err // todo
|
||||
// printf(" write_mdat: "); for (int i = 0; i < 32; ++i) printf(" 0x%02hhX", data[i]); printf("\n");
|
||||
err = put(ptr, data, len); chk_err
|
||||
// printf("mdat len %d ptr->offset %d start_atom: %d \n", len, ptr->offset, start_atom);
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_moof(struct BitBuf *ptr,
|
||||
const uint32_t sequence_number,
|
||||
const uint64_t base_data_offset,
|
||||
const uint64_t base_media_decode_time,
|
||||
const uint32_t default_sample_duration,
|
||||
const struct SampleInfo *samples_info, const uint32_t samples_info_len)
|
||||
{
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "moof"); chk_err
|
||||
err = write_mfhd(ptr, sequence_number); chk_err
|
||||
struct DataOffsetPos data_offset;
|
||||
data_offset.offset = 0;
|
||||
err = write_traf(ptr, sequence_number, base_data_offset, base_media_decode_time, default_sample_duration, samples_info, samples_info_len, &data_offset); chk_err
|
||||
if (data_offset.data_offset_present)
|
||||
err = put_u32_be_to_offset(ptr, data_offset.offset, ptr->offset + 4 /*mdat size*/ + 4 /*mdat id*/); chk_err
|
||||
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_mfhd(struct BitBuf *ptr, const uint32_t sequence_number) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "mfhd"); chk_err
|
||||
err = put_u8(ptr, 0); // 1 version
|
||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // 3 flags
|
||||
pos_sequence_number = ptr->offset;
|
||||
err = put_u32_be(ptr, sequence_number); chk_err // 4 sequence_number
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_traf(struct BitBuf *ptr,
|
||||
const uint32_t sequence_number,
|
||||
const uint64_t base_data_offset,
|
||||
const uint64_t base_media_decode_time,
|
||||
const uint32_t default_sample_duration,
|
||||
const struct SampleInfo *samples_info, const uint32_t samples_info_len,
|
||||
struct DataOffsetPos *data_offset)
|
||||
{
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "traf"); chk_err
|
||||
err = write_tfhd(ptr, sequence_number, base_data_offset, base_media_decode_time, samples_info[0].size, default_sample_duration, samples_info, samples_info_len, data_offset); chk_err
|
||||
err = write_tfdt(ptr, base_media_decode_time); chk_err
|
||||
err = write_trun(ptr, samples_info, samples_info_len, data_offset); chk_err
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_tfhd(struct BitBuf *ptr,
|
||||
const uint32_t sequence_number,
|
||||
const uint64_t base_data_offset,
|
||||
const uint64_t base_media_decode_time,
|
||||
const uint32_t default_sample_size,
|
||||
const uint32_t default_sample_duration,
|
||||
const struct SampleInfo *samples_info, const uint32_t samples_info_len,
|
||||
struct DataOffsetPos *data_offset)
|
||||
{
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "tfhd"); chk_err
|
||||
|
||||
err = put_u8(ptr, 0); chk_err // 1 byte version
|
||||
uint64_t flags = 0x0;
|
||||
const bool base_data_offset_present = false;
|
||||
const bool sample_description_index_present = false;
|
||||
const bool default_sample_duration_present = true;
|
||||
const bool default_sample_size_present = true;
|
||||
const bool default_sample_flags_present = true;
|
||||
const bool duration_is_empty = false;
|
||||
const bool default_base_is_moof = false;
|
||||
|
||||
if (base_data_offset_present) { flags = flags | 0x000001; } // base-data-offset-present
|
||||
if (sample_description_index_present) { flags = flags | 0x000002; } // sample-description-index-present
|
||||
if (default_sample_duration_present) { flags = flags | 0x000008; } // default-sample-duration-present
|
||||
if (default_sample_size_present) { flags = flags | 0x000010; } // default-sample-size-present
|
||||
if (default_sample_flags_present) { flags = flags | 0x000020; } // default-sample-flags-present
|
||||
if (duration_is_empty) { flags = flags | 0x010000; } // duration-is-empty
|
||||
if (default_base_is_moof) { flags = flags | 0x020000; } // default-base-is-moof
|
||||
// buf.put_u8(0); buf.put_u8(0); buf.put_u8(0x39); // 3 flags
|
||||
// println!("tfhd flags: 0x{:06x} 0x{:02x}: 0x{:02x}: 0x{:02x}", flags, (flags >> 16) as u8, (flags >> 8) as u8, (flags >> 0) as u8);
|
||||
err = put_u8(ptr, flags >> 16); chk_err; err = put_u8(ptr, flags >> 8); chk_err; err = put_u8(ptr, flags >> 0); chk_err // 3 flags
|
||||
|
||||
err = put_u32_be(ptr, 1); chk_err // 4 track_ID
|
||||
if (base_data_offset_present) { pos_base_data_offset = ptr->offset; err = put_u64_be(ptr, base_data_offset); chk_err }
|
||||
// if sample_description_index_present { buf.put_u32_be(0); } // 4 default_sample_description_index
|
||||
if (default_sample_duration_present) { err = put_u32_be(ptr, default_sample_duration); chk_err }
|
||||
if (default_sample_size_present) { err = put_u32_be(ptr, default_sample_size); chk_err }
|
||||
if (default_sample_flags_present) { err = put_u32_be(ptr, 16842752); chk_err }
|
||||
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_tfdt(struct BitBuf *ptr, const uint64_t base_media_decode_time) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "tfdt"); chk_err
|
||||
err = put_u8(ptr, 1); // 1 version
|
||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // 3 flags
|
||||
pos_base_media_decode_time = ptr->offset;
|
||||
err = put_u64_be(ptr, base_media_decode_time); chk_err // 4 baseMediaDecodeTime
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_trun(struct BitBuf *ptr,
|
||||
const struct SampleInfo *samples_info, const uint32_t samples_info_count,
|
||||
struct DataOffsetPos *data_offset)
|
||||
{
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "trun"); chk_err
|
||||
|
||||
err = put_u8(ptr, 0); chk_err // 1 version
|
||||
const bool data_offset_present = true;
|
||||
const bool first_sample_flags_present = false;
|
||||
const bool sample_duration_present = true;
|
||||
const bool sample_size_present = true;
|
||||
const bool sample_flags_present = true;
|
||||
const bool sample_composition_time_offsets_present = true;
|
||||
{
|
||||
uint64_t flags = 0x0;
|
||||
if (data_offset_present) { flags = flags | 0x000001; } // 0x000001 data-offset-present.
|
||||
if (first_sample_flags_present) { flags = flags | 0x000004; } // 0x000004 first-sample-flags-present
|
||||
if (sample_duration_present) { flags = flags | 0x000100; } // 0x000100 sample-duration-present
|
||||
if (sample_size_present) { flags = flags | 0x000200; } // 0x000200 sample-size-present
|
||||
if (sample_flags_present) { flags = flags | 0x000400; } // 0x000400 sample-flags-present
|
||||
if (sample_composition_time_offsets_present) { flags = flags | 0x000800; } // 0x000800 sample-composition-time-offsets-present
|
||||
// println!("trup flags: 0x{:06x} 0x{:02x}: 0x{:02x}: 0x{:02x}", flags, (flags >> 16) as u8, (flags >> 8) as u8, (flags >> 0) as u8);
|
||||
err = put_u8(ptr, flags >> 16); chk_err; err = put_u8(ptr, flags >> 8); chk_err; err = put_u8(ptr, flags >> 0); chk_err // 3 flags
|
||||
}
|
||||
err = put_u32_be(ptr, samples_info_count); chk_err // 4 sample_count
|
||||
|
||||
data_offset->data_offset_present = data_offset_present;
|
||||
data_offset->offset = ptr->offset; // save pointer to this place. we will change size after moof atom will created
|
||||
if (data_offset_present) { err = put_i32_be(ptr, 0); chk_err } // 4 fake data_offset
|
||||
|
||||
if (first_sample_flags_present) { err = put_u32_be(ptr, 33554432); chk_err } // 4 first_sample_flags
|
||||
for (uint32_t i = 0; i < samples_info_count; ++i) {
|
||||
const struct SampleInfo sample_info = samples_info[i];
|
||||
if (sample_duration_present) { err = put_u32_be(ptr, sample_info.duration); chk_err } // 4 sample_duration
|
||||
if (sample_size_present) { err = put_u32_be(ptr, sample_info.size); chk_err } // 4 sample_size
|
||||
if (sample_flags_present) { err = put_u32_be(ptr, sample_info.flags); chk_err } // 4 sample_flags
|
||||
if (sample_composition_time_offsets_present) {
|
||||
// if version == 0 { err = put_u32_be(ptr, sample_info.composition_offset as u32); chk_err }
|
||||
// else
|
||||
{ err = put_i32_be(ptr, sample_info.composition_offset); chk_err }
|
||||
}
|
||||
}
|
||||
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
420
common/rfb/mp4_moov.c
Normal file
420
common/rfb/mp4_moov.c
Normal file
@@ -0,0 +1,420 @@
|
||||
#include "mp4.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
enum BufError write_ftyp(struct BitBuf *ptr);
|
||||
enum BufError write_moov(struct BitBuf *ptr, const struct MoovInfo *moov_info);
|
||||
|
||||
enum BufError write_mvhd(struct BitBuf *ptr, const struct MoovInfo *moov_info);
|
||||
enum BufError write_trak(struct BitBuf *ptr, const struct MoovInfo *moov_info);
|
||||
enum BufError write_tkhd(struct BitBuf *ptr, const struct MoovInfo *moov_info);
|
||||
enum BufError write_mdia(struct BitBuf *ptr, const struct MoovInfo *moov_info);
|
||||
enum BufError write_mdhd(struct BitBuf *ptr, const struct MoovInfo *moov_info);
|
||||
enum BufError write_minf(struct BitBuf *ptr, const struct MoovInfo *moov_info);
|
||||
enum BufError write_dinf(struct BitBuf *ptr);
|
||||
enum BufError write_dref(struct BitBuf *ptr);
|
||||
enum BufError write_url(struct BitBuf *ptr);
|
||||
enum BufError write_vmhd(struct BitBuf *ptr);
|
||||
enum BufError write_stbl(struct BitBuf *ptr, const struct MoovInfo *moov_info);
|
||||
enum BufError write_stsd(struct BitBuf *ptr, const struct MoovInfo *moov_info);
|
||||
enum BufError write_avc1(struct BitBuf *ptr, const struct MoovInfo *moov_info);
|
||||
enum BufError write_avcC(struct BitBuf *ptr, const struct MoovInfo *moov_info);
|
||||
enum BufError write_stts(struct BitBuf *ptr);
|
||||
enum BufError write_stsc(struct BitBuf *ptr);
|
||||
enum BufError write_stsz(struct BitBuf *ptr);
|
||||
enum BufError write_stco(struct BitBuf *ptr);
|
||||
enum BufError write_mvex(struct BitBuf *ptr);
|
||||
enum BufError write_trex(struct BitBuf *ptr);
|
||||
enum BufError write_udta(struct BitBuf *ptr);
|
||||
enum BufError write_meta(struct BitBuf *ptr);
|
||||
enum BufError write_hdlr(struct BitBuf *ptr, const char name[4], const char manufacturer[4], const char *value, const uint32_t value_len);
|
||||
enum BufError write_ilst(struct BitBuf *ptr, const uint8_t *array, const uint32_t len);
|
||||
|
||||
enum BufError write_header(struct BitBuf *ptr, struct MoovInfo *moov_info) {
|
||||
enum BufError err;
|
||||
err = write_ftyp(ptr); chk_err
|
||||
err = write_moov(ptr, moov_info); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_ftyp(struct BitBuf *ptr) {
|
||||
enum BufError err;
|
||||
// atom header <fake size><id>
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "ftyp"); chk_err
|
||||
|
||||
err = put_str4(ptr, "isom"); chk_err // major_brand
|
||||
err = put_u32_be(ptr, 0x00000200); chk_err // minor_version
|
||||
err = put_str4(ptr, "isom"); chk_err
|
||||
err = put_str4(ptr, "iso2"); chk_err
|
||||
err = put_str4(ptr, "avc1"); chk_err
|
||||
err = put_str4(ptr, "iso6"); chk_err
|
||||
err = put_str4(ptr, "mp41"); chk_err
|
||||
|
||||
// write atom size
|
||||
uint32_t atom_size = ptr->offset - start_atom;
|
||||
err = put_u32_be_to_offset(ptr, start_atom, atom_size); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
|
||||
enum BufError write_moov(struct BitBuf *ptr, const struct MoovInfo *moov_info) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "moov"); chk_err
|
||||
err = write_mvhd(ptr, moov_info); chk_err
|
||||
err = write_trak(ptr, moov_info); chk_err
|
||||
err = write_mvex(ptr); chk_err
|
||||
err = write_udta(ptr); chk_err
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
|
||||
enum BufError write_mvhd(struct BitBuf *ptr, const struct MoovInfo *moov_info) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "mvhd"); chk_err
|
||||
|
||||
err = put_u8(ptr, 0); chk_err // 1 version
|
||||
err = put_u8(ptr, 0); chk_err; err=put_u8(ptr, 0);chk_err; err=put_u8(ptr, 0);chk_err // 3 flags
|
||||
err = put_u32_be(ptr, moov_info->creation_time); chk_err // 4 creation_time
|
||||
err = put_u32_be(ptr, 0); chk_err // 4 modification_time
|
||||
err = put_u32_be(ptr, moov_info->timescale); chk_err // 4 timescale
|
||||
err = put_u32_be(ptr, 0); chk_err // 4 duration
|
||||
err = put_u32_be(ptr, 65536); chk_err // 4 preferred rate
|
||||
err = put_u16_le(ptr, 1); chk_err // 2 preferred volume
|
||||
err = put_skip(ptr, 10); chk_err // 10 reserved
|
||||
{ // 36 matrix
|
||||
err = put_u32_be(ptr, 65536); chk_err
|
||||
err = put_u32_be(ptr, 0); chk_err
|
||||
err = put_u32_be(ptr, 0); chk_err
|
||||
err = put_u32_be(ptr, 0); chk_err
|
||||
err = put_u32_be(ptr, 65536); chk_err
|
||||
err = put_u32_be(ptr, 0); chk_err
|
||||
err = put_u32_be(ptr, 0); chk_err
|
||||
err = put_u32_be(ptr, 0); chk_err
|
||||
err = put_u32_be(ptr, 1073741824); chk_err
|
||||
}
|
||||
err = put_u32_be(ptr, 0); chk_err // 4 Preview time
|
||||
err = put_u32_be(ptr, 0); chk_err // 4 Preview duration
|
||||
err = put_u32_be(ptr, 0); chk_err // 4 Poster time
|
||||
err = put_u32_be(ptr, 0); chk_err // 4 Selection time
|
||||
err = put_u32_be(ptr, 0); chk_err // 4 Selection duration
|
||||
err = put_u32_be(ptr, 0); chk_err // 4 Current time
|
||||
err = put_u32_be(ptr, 2); chk_err // 4 Next track ID
|
||||
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_trak(struct BitBuf *ptr, const struct MoovInfo *moov_info) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "trak"); chk_err
|
||||
err = write_tkhd(ptr, moov_info); chk_err
|
||||
err = write_mdia(ptr, moov_info); chk_err
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_tkhd(struct BitBuf *ptr, const struct MoovInfo *moov_info) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "tkhd"); chk_err
|
||||
|
||||
err = put_u8(ptr, 0); // 1 version
|
||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 3); chk_err // 3 flags
|
||||
err = put_u32_be(ptr, moov_info->creation_time); // 4 creation_time
|
||||
err = put_u32_be(ptr, 0); // 4 modification_time
|
||||
err = put_u32_be(ptr, 1); // 4 track id
|
||||
err = put_u32_be(ptr, 0); // 4 reserved
|
||||
err = put_u32_be(ptr, 0); // 4 duration
|
||||
err = put_skip(ptr, 8); // 8 reserved
|
||||
err = put_u16_be(ptr, 0); // 2 layer
|
||||
err = put_u16_be(ptr, 0); // 2 Alternate group
|
||||
err = put_u16_be(ptr, 0); // 2 Volume
|
||||
err = put_u16_be(ptr, 0); // 2 Reserved
|
||||
{ // 36 Matrix structure
|
||||
err = put_u32_be(ptr, 65536); chk_err
|
||||
err = put_u32_be(ptr, 0); chk_err
|
||||
err = put_u32_be(ptr, 0); chk_err
|
||||
err = put_u32_be(ptr, 0); chk_err
|
||||
err = put_u32_be(ptr, 65536); chk_err
|
||||
err = put_u32_be(ptr, 0); chk_err
|
||||
err = put_u32_be(ptr, 0); chk_err
|
||||
err = put_u32_be(ptr, 0); chk_err
|
||||
err = put_u32_be(ptr, 1073741824); chk_err
|
||||
}
|
||||
err = put_u32_be(ptr, moov_info->width * 65536); chk_err // 4 Track width
|
||||
err = put_u32_be(ptr, moov_info->height * 65536); chk_err // 4 Track height
|
||||
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
|
||||
enum BufError write_mdia(struct BitBuf *ptr, const struct MoovInfo *moov_info) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "mdia"); chk_err
|
||||
err = write_mdhd(ptr, moov_info); chk_err
|
||||
char *str = "VideoHandler";
|
||||
err = write_hdlr(ptr, "vide", "\0\0\0\0", str, strlen(str)); chk_err
|
||||
err = write_minf(ptr, moov_info); chk_err
|
||||
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_mdhd(struct BitBuf *ptr, const struct MoovInfo *moov_info) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "mdhd"); chk_err
|
||||
err = put_u8(ptr, 0); // 1 version
|
||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // 3 flags
|
||||
err = put_u32_be(ptr, 0); chk_err // 4 creation_time
|
||||
err = put_u32_be(ptr, 0); chk_err // 4 modification_time
|
||||
err = put_u32_be(ptr, moov_info->timescale); chk_err // 4 timescale
|
||||
err = put_u32_be(ptr, 0); chk_err // 4 duration
|
||||
err = put_u16_be(ptr, 21956); chk_err // 2 language
|
||||
err = put_u16_be(ptr, 0); chk_err // 2 quality
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_minf(struct BitBuf *ptr, const struct MoovInfo *moov_info) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "minf"); chk_err
|
||||
err = write_vmhd(ptr); chk_err
|
||||
err = write_dinf(ptr); chk_err
|
||||
err = write_stbl(ptr, moov_info); chk_err
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_dinf(struct BitBuf *ptr) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "dinf"); chk_err
|
||||
err = write_dref(ptr); chk_err
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_dref(struct BitBuf *ptr) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "dref"); chk_err
|
||||
err = put_u8(ptr, 0); chk_err // 1 version
|
||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // 3 flags
|
||||
err = put_u32_be(ptr, 1); chk_err // 4 Component flags mask
|
||||
err = write_url(ptr); chk_err
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_url(struct BitBuf *ptr) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "url "); chk_err
|
||||
err = put_u8(ptr, 0); chk_err // 1 version
|
||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 1); chk_err // 3 flags
|
||||
//err = put_u8(ptr, 0); chk_err // <counted string> end
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_vmhd(struct BitBuf *ptr) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "vmhd"); chk_err
|
||||
|
||||
err = put_u8(ptr, 0); chk_err // 1 version
|
||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 1); chk_err // 3 flags
|
||||
err = put_u16_be(ptr, 0); chk_err // 2 Graphics mode
|
||||
err = put_u16_be(ptr, 0); chk_err // 2 Opcolor
|
||||
err = put_u16_be(ptr, 0); chk_err // 2 Opcolor
|
||||
err = put_u16_be(ptr, 0); chk_err // 2 Opcolor
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_stbl(struct BitBuf *ptr, const struct MoovInfo *moov_info) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "stbl"); chk_err
|
||||
err = write_stsd(ptr, moov_info); chk_err
|
||||
err = write_stts(ptr); chk_err
|
||||
err = write_stsc(ptr); chk_err
|
||||
err = write_stsz(ptr); chk_err
|
||||
err = write_stco(ptr); chk_err
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_stsd(struct BitBuf *ptr, const struct MoovInfo *moov_info) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "stsd"); chk_err
|
||||
err = put_u8(ptr, 0); chk_err // 1 version
|
||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // 3 flags
|
||||
err = put_u32_be(ptr, 1); chk_err // 4 Number of entries
|
||||
err = write_avc1(ptr, moov_info); chk_err
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_avc1(struct BitBuf *ptr, const struct MoovInfo *moov_info) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "avc1"); chk_err
|
||||
|
||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // reserved
|
||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // reserved
|
||||
err = put_u16_be(ptr, 1); chk_err // data_reference_index
|
||||
err = put_u16_be(ptr, 0); chk_err // pre_defined
|
||||
err = put_u16_be(ptr, 0); chk_err // reserved
|
||||
err = put_u32_be(ptr, 0); chk_err
|
||||
err = put_u32_be(ptr, 0); chk_err
|
||||
err = put_u32_be(ptr, 0); chk_err // pre_defined
|
||||
err = put_u16_be(ptr, moov_info->width); chk_err // 2 width
|
||||
err = put_u16_be(ptr, moov_info->height); chk_err // 2 height
|
||||
err = put_u32_be(ptr, moov_info->horizontal_resolution); chk_err // 4 horizontal_resolution
|
||||
err = put_u32_be(ptr, moov_info->vertical_resolution); chk_err // 4 vertical_resolution
|
||||
err = put_u32_be(ptr, 0); chk_err // reserved
|
||||
err = put_u16_be(ptr, 1); chk_err // 2 frame_count
|
||||
err = put_u8(ptr, 0); chk_err
|
||||
// uint8_t *compressorname = { 0, 0, 0, 0, // dailymotion/hls.js
|
||||
// 0, 0, 0, 0,
|
||||
// 0, 0, 0, 0,
|
||||
// 0, 0, 0, 0,
|
||||
// 0, 0, 0, 0,
|
||||
// 0, 0, 0, 0,
|
||||
// 0, 0, 0, 0,
|
||||
// 0, 0, 0 };
|
||||
char compressorname[50] = "OpenIPC project ";
|
||||
|
||||
err = put(ptr, (uint8_t *) compressorname, 31); chk_err // compressorname
|
||||
err = put_u16_be(ptr, 24); chk_err // 2 depth
|
||||
err = put_u16_be(ptr, 0xffff); chk_err // 2 color_table_id
|
||||
err = write_avcC(ptr, moov_info); chk_err
|
||||
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
|
||||
enum BufError write_avcC(struct BitBuf *ptr, const struct MoovInfo *moov_info) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "avcC"); chk_err
|
||||
|
||||
err = put_u8(ptr, 1); chk_err // 1 version
|
||||
err = put_u8(ptr, moov_info->sps[1]); chk_err // 1 profile
|
||||
err = put_u8(ptr, moov_info->sps[2]); chk_err // 1 compatibility
|
||||
err = put_u8(ptr, moov_info->sps[3]); chk_err // 1 level
|
||||
err = put_u8(ptr, 0xFF); chk_err // 6 bits reserved (111111) + 2 bits nal size length - 1 (11)
|
||||
err = put_u8(ptr, 0xE1); chk_err // 3 bits reserved (111) + 5 bits number of sps (00001)
|
||||
err = put_u16_be(ptr, moov_info->sps_length); chk_err
|
||||
err = put(ptr, (const uint8_t *) moov_info->sps, moov_info->sps_length); chk_err // SPS
|
||||
err = put_u8(ptr, 1); chk_err // 1 num pps
|
||||
err = put_u16_be(ptr, moov_info->pps_length); chk_err
|
||||
err = put(ptr, (const uint8_t *)moov_info->pps, moov_info->pps_length); chk_err // pps
|
||||
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_stts(struct BitBuf *ptr) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "stts"); chk_err
|
||||
|
||||
err = put_u8(ptr, 0); chk_err // 1 version
|
||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // 3 flags
|
||||
err = put_u32_be(ptr, 0); chk_err // Number of entries
|
||||
// Time-to-sample table
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_stsc(struct BitBuf *ptr) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "stsc"); chk_err
|
||||
err = put_u8(ptr, 0); chk_err // 1 version
|
||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // 3 flags
|
||||
err = put_u32_be(ptr, 0); chk_err // Number of entries
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_stsz(struct BitBuf *ptr) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "stsz"); chk_err
|
||||
err = put_u8(ptr, 0); chk_err // 1 version
|
||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // 3 flags
|
||||
err = put_u32_be(ptr, 0); chk_err // Sample size
|
||||
err = put_u32_be(ptr, 0); chk_err // Number of entries
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_stco(struct BitBuf *ptr) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "stco"); chk_err
|
||||
err = put_u8(ptr, 0); chk_err // 1 version
|
||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // 3 flags
|
||||
err = put_u32_be(ptr, 0); chk_err // Number of entries
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_mvex(struct BitBuf *ptr) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "mvex"); chk_err
|
||||
err = write_trex(ptr); chk_err
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_trex(struct BitBuf *ptr) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "trex"); chk_err
|
||||
err = put_u8(ptr, 0); chk_err // 1 version
|
||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // 3 flags
|
||||
err = put_u32_be(ptr, 1); chk_err // track_ID
|
||||
err = put_u32_be(ptr, 1); chk_err // default_sample_description_index
|
||||
err = put_u32_be(ptr, 0); chk_err // default_sample_duration
|
||||
err = put_u32_be(ptr, 0); chk_err // default_sample_size
|
||||
err = put_u32_be(ptr, 0); chk_err // default_sample_flags
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_udta(struct BitBuf *ptr) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "udta"); chk_err
|
||||
err = write_meta(ptr); chk_err
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_meta(struct BitBuf *ptr) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "meta"); chk_err
|
||||
err = put_u8(ptr, 0); chk_err // 1 version
|
||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // 3 flags
|
||||
err = write_hdlr(ptr, "mdir", "appl", "", 0); chk_err
|
||||
uint8_t array[37] = {0,0,0,37,169,116,111,111,0,0,0,29,100,97,116,97,0,0,0,1,0,0,0,0,76,97,118,102,53,55,46,56,51,46,49,48,48};
|
||||
err = write_ilst(ptr, array, 37); chk_err
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_hdlr(struct BitBuf *ptr, const char name[4], const char manufacturer[4], const char *value, const uint32_t value_len) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "hdlr"); chk_err
|
||||
err = put_u8(ptr, 0); chk_err // 1 version
|
||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // 3 flags
|
||||
err = put_u32_be(ptr, 0); chk_err // 4 Predefined
|
||||
err = put_str4(ptr, name); chk_err // 4 Component subtype
|
||||
err = put_str4(ptr, manufacturer); chk_err // 4 Component manufacturer
|
||||
err = put_u32_be(ptr, 0); chk_err // 4 Component flags
|
||||
err = put_u32_be(ptr, 0); chk_err // 4 Component flags mask
|
||||
err = put_counted_str(ptr, value, value_len); chk_err // <counted string> Component name
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_ilst(struct BitBuf *ptr, const uint8_t *array, const uint32_t len) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "ilst"); chk_err
|
||||
err = put(ptr, array, len); chk_err // <counted string> Component name
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
3849
common/rfb/nvEncodeAPI.h
Normal file
3849
common/rfb/nvEncodeAPI.h
Normal file
File diff suppressed because it is too large
Load Diff
453
common/rfb/nvidia.cxx
Normal file
453
common/rfb/nvidia.cxx
Normal file
@@ -0,0 +1,453 @@
|
||||
/*
|
||||
* H.264/HEVC hardware encoding using nvidia nvenc
|
||||
* Copyright (c) 2016 Timo Rothenpieler <timo@rothenpieler.org>
|
||||
*
|
||||
* This file is part of FFmpeg.
|
||||
*
|
||||
* FFmpeg is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* FFmpeg is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with FFmpeg; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <rfb/LogWriter.h>
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "nvidia.h"
|
||||
|
||||
using namespace rfb;
|
||||
|
||||
static LogWriter vlog("nvidia");
|
||||
|
||||
#define FFNV_LOG_FUNC(logctx, msg, ...) vlog.info((msg), __VA_ARGS__)
|
||||
#define FFNV_DEBUG_LOG_FUNC(logctx, msg, ...)
|
||||
|
||||
#include "dynlink_loader.h"
|
||||
|
||||
#define NUM_SURF 4
|
||||
|
||||
typedef struct NvencSurface
|
||||
{
|
||||
NV_ENC_INPUT_PTR input_surface;
|
||||
int reg_idx;
|
||||
int width;
|
||||
int height;
|
||||
int pitch;
|
||||
|
||||
NV_ENC_OUTPUT_PTR output_surface;
|
||||
NV_ENC_BUFFER_FORMAT format;
|
||||
} NvencSurface;
|
||||
|
||||
typedef struct NvencDynLoadFunctions
|
||||
{
|
||||
CudaFunctions *cuda_dl;
|
||||
NvencFunctions *nvenc_dl;
|
||||
|
||||
void *nvenc_ctx;
|
||||
NV_ENCODE_API_FUNCTION_LIST nvenc_funcs;
|
||||
|
||||
NV_ENC_INITIALIZE_PARAMS init_enc_parms;
|
||||
NV_ENC_CONFIG enc_cfg;
|
||||
CUdevice cu_dev;
|
||||
CUcontext cu_ctx;
|
||||
|
||||
NvencSurface surf[NUM_SURF];
|
||||
uint8_t cursurf;
|
||||
} NvencDynLoadFunctions;
|
||||
|
||||
static NvencDynLoadFunctions nvenc;
|
||||
|
||||
/*
|
||||
Recommended settings for streaming
|
||||
Low-Latency High Quality preset
|
||||
Rate control mode = Two-pass CBR
|
||||
Very low VBV buffer size (Single frame)
|
||||
No B Frames
|
||||
Infinite GOP length
|
||||
Adaptive Quantization enabled
|
||||
*/
|
||||
|
||||
static int loadfuncs() {
|
||||
int ret;
|
||||
NVENCSTATUS err;
|
||||
uint32_t nvenc_max_ver;
|
||||
|
||||
ret = cuda_load_functions(&nvenc.cuda_dl);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = nvenc_load_functions(&nvenc.nvenc_dl);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
err = nvenc.nvenc_dl->NvEncodeAPIGetMaxSupportedVersion(&nvenc_max_ver);
|
||||
if (err != NV_ENC_SUCCESS)
|
||||
return -1;
|
||||
|
||||
vlog.info("Loaded nvenc version %u.%u", nvenc_max_ver >> 4, nvenc_max_ver & 0xf);
|
||||
|
||||
if ((NVENCAPI_MAJOR_VERSION << 4 | NVENCAPI_MINOR_VERSION) > nvenc_max_ver) {
|
||||
vlog.error("Your Nvidia driver is too old. Nvenc %u.%u required",
|
||||
NVENCAPI_MAJOR_VERSION, NVENCAPI_MINOR_VERSION);
|
||||
return -1;
|
||||
}
|
||||
|
||||
nvenc.nvenc_funcs.version = NV_ENCODE_API_FUNCTION_LIST_VER;
|
||||
|
||||
err = nvenc.nvenc_dl->NvEncodeAPICreateInstance(&nvenc.nvenc_funcs);
|
||||
if (err != NV_ENC_SUCCESS)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nvenc_check_cap(NV_ENC_CAPS cap) {
|
||||
NV_ENC_CAPS_PARAM params;
|
||||
memset(¶ms, 0, sizeof(NV_ENC_CAPS_PARAM));
|
||||
|
||||
params.version = NV_ENC_CAPS_PARAM_VER;
|
||||
params.capsToQuery = cap;
|
||||
|
||||
int ret, val = 0;
|
||||
|
||||
ret = nvenc.nvenc_funcs.nvEncGetEncodeCaps(nvenc.nvenc_ctx,
|
||||
nvenc.init_enc_parms.encodeGUID,
|
||||
¶ms, &val);
|
||||
if (ret == NV_ENC_SUCCESS)
|
||||
return val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setupdevice() {
|
||||
int ret;
|
||||
|
||||
nvenc.init_enc_parms.encodeGUID = NV_ENC_CODEC_H264_GUID;
|
||||
nvenc.init_enc_parms.presetGUID = NV_ENC_PRESET_P7_GUID;
|
||||
|
||||
ret = nvenc.cuda_dl->cuInit(0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = nvenc.cuda_dl->cuDeviceGet(&nvenc.cu_dev, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = nvenc.cuda_dl->cuCtxCreate(&nvenc.cu_ctx, CU_CTX_SCHED_BLOCKING_SYNC,
|
||||
nvenc.cu_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
CUcontext dummy;
|
||||
nvenc.cuda_dl->cuCtxPopCurrent(&dummy);
|
||||
|
||||
// cuda stream is NULL to use the default
|
||||
|
||||
// open session
|
||||
NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS params;
|
||||
memset(¶ms, 0, sizeof(NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS));
|
||||
NVENCSTATUS err;
|
||||
|
||||
params.version = NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER;
|
||||
params.apiVersion = NVENCAPI_VERSION;
|
||||
params.device = nvenc.cu_ctx;
|
||||
params.deviceType = NV_ENC_DEVICE_TYPE_CUDA;
|
||||
|
||||
err = nvenc.nvenc_funcs.nvEncOpenEncodeSessionEx(¶ms, &nvenc.nvenc_ctx);
|
||||
if (err != NV_ENC_SUCCESS)
|
||||
return -1;
|
||||
|
||||
// check caps
|
||||
const int maxw = nvenc_check_cap(NV_ENC_CAPS_WIDTH_MAX);
|
||||
const int maxh = nvenc_check_cap(NV_ENC_CAPS_HEIGHT_MAX);
|
||||
const int minw = nvenc_check_cap(NV_ENC_CAPS_WIDTH_MIN);
|
||||
const int minh = nvenc_check_cap(NV_ENC_CAPS_HEIGHT_MIN);
|
||||
|
||||
vlog.info("Max enc resolution %ux%u, min %ux%u", maxw, maxh, minw, minh);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setupenc(const unsigned w, const unsigned h, const unsigned kbps,
|
||||
const unsigned fps) {
|
||||
NVENCSTATUS err;
|
||||
|
||||
nvenc.enc_cfg.version = NV_ENC_CONFIG_VER;
|
||||
nvenc.init_enc_parms.version = NV_ENC_INITIALIZE_PARAMS_VER;
|
||||
nvenc.init_enc_parms.darWidth =
|
||||
nvenc.init_enc_parms.encodeWidth = w;
|
||||
nvenc.init_enc_parms.darHeight =
|
||||
nvenc.init_enc_parms.encodeHeight = h;
|
||||
|
||||
nvenc.init_enc_parms.frameRateNum = fps;
|
||||
nvenc.init_enc_parms.frameRateDen = 1;
|
||||
|
||||
nvenc.init_enc_parms.encodeConfig = &nvenc.enc_cfg;
|
||||
nvenc.init_enc_parms.tuningInfo = NV_ENC_TUNING_INFO_LOW_LATENCY;
|
||||
|
||||
NV_ENC_PRESET_CONFIG preset_cfg;
|
||||
memset(&preset_cfg, 0, sizeof(NV_ENC_PRESET_CONFIG));
|
||||
|
||||
preset_cfg.version = NV_ENC_PRESET_CONFIG_VER;
|
||||
preset_cfg.presetCfg.version = NV_ENC_CONFIG_VER;
|
||||
|
||||
err = nvenc.nvenc_funcs.nvEncGetEncodePresetConfigEx(nvenc.nvenc_ctx,
|
||||
nvenc.init_enc_parms.encodeGUID,
|
||||
nvenc.init_enc_parms.presetGUID,
|
||||
nvenc.init_enc_parms.tuningInfo,
|
||||
&preset_cfg);
|
||||
if (err != NV_ENC_SUCCESS)
|
||||
return -1;
|
||||
|
||||
memcpy(&nvenc.enc_cfg, &preset_cfg.presetCfg, sizeof(nvenc.enc_cfg));
|
||||
|
||||
nvenc.enc_cfg.version = NV_ENC_CONFIG_VER;
|
||||
|
||||
nvenc.init_enc_parms.enableEncodeAsync = 0;
|
||||
nvenc.init_enc_parms.enablePTD = 1;
|
||||
|
||||
nvenc.enc_cfg.frameIntervalP = 0;
|
||||
nvenc.enc_cfg.gopLength = 1;
|
||||
|
||||
// use 4 surfaces
|
||||
|
||||
// setup rate control
|
||||
nvenc.enc_cfg.rcParams.multiPass = NV_ENC_TWO_PASS_FULL_RESOLUTION;
|
||||
nvenc.enc_cfg.rcParams.averageBitRate = kbps * 1024;
|
||||
nvenc.enc_cfg.rcParams.rateControlMode = NV_ENC_PARAMS_RC_CBR;
|
||||
nvenc.enc_cfg.rcParams.lowDelayKeyFrameScale = 1;
|
||||
|
||||
nvenc.enc_cfg.rcParams.enableAQ = 1;
|
||||
nvenc.enc_cfg.rcParams.aqStrength = 4; // 1 - 15, 0 would be auto
|
||||
|
||||
nvenc.enc_cfg.frameFieldMode = NV_ENC_PARAMS_FRAME_FIELD_MODE_FRAME;
|
||||
|
||||
// setup_codec_config
|
||||
nvenc.enc_cfg.encodeCodecConfig.h264Config.h264VUIParameters.videoFullRangeFlag = 1;
|
||||
nvenc.enc_cfg.encodeCodecConfig.h264Config.outputBufferingPeriodSEI = 1;
|
||||
nvenc.enc_cfg.encodeCodecConfig.h264Config.adaptiveTransformMode = NV_ENC_H264_ADAPTIVE_TRANSFORM_ENABLE;
|
||||
nvenc.enc_cfg.encodeCodecConfig.h264Config.fmoMode = NV_ENC_H264_FMO_DISABLE;
|
||||
nvenc.enc_cfg.profileGUID = NV_ENC_H264_PROFILE_MAIN_GUID;
|
||||
|
||||
nvenc.cuda_dl->cuCtxPushCurrent(nvenc.cu_ctx);
|
||||
|
||||
err = nvenc.nvenc_funcs.nvEncInitializeEncoder(nvenc.nvenc_ctx,
|
||||
&nvenc.init_enc_parms);
|
||||
if (err != NV_ENC_SUCCESS)
|
||||
return -1;
|
||||
|
||||
// custream?
|
||||
|
||||
CUcontext dummy;
|
||||
nvenc.cuda_dl->cuCtxPopCurrent(&dummy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setupsurf(const unsigned w, const unsigned h) {
|
||||
|
||||
nvenc.cuda_dl->cuCtxPushCurrent(nvenc.cu_ctx);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < NUM_SURF; i++) {
|
||||
NVENCSTATUS err;
|
||||
NV_ENC_CREATE_BITSTREAM_BUFFER allocOut;
|
||||
memset(&allocOut, 0, sizeof(NV_ENC_CREATE_BITSTREAM_BUFFER));
|
||||
allocOut.version = NV_ENC_CREATE_BITSTREAM_BUFFER_VER;
|
||||
|
||||
NV_ENC_CREATE_INPUT_BUFFER allocSurf;
|
||||
memset(&allocSurf, 0, sizeof(NV_ENC_CREATE_INPUT_BUFFER));
|
||||
|
||||
nvenc.surf[i].format = NV_ENC_BUFFER_FORMAT_ARGB; // doesn't have RGBA!
|
||||
allocSurf.version = NV_ENC_CREATE_INPUT_BUFFER_VER;
|
||||
allocSurf.width = w;
|
||||
allocSurf.height = h;
|
||||
allocSurf.bufferFmt = nvenc.surf[i].format;
|
||||
|
||||
err = nvenc.nvenc_funcs.nvEncCreateInputBuffer(nvenc.nvenc_ctx, &allocSurf);
|
||||
if (err != NV_ENC_SUCCESS)
|
||||
return -1;
|
||||
|
||||
nvenc.surf[i].input_surface = allocSurf.inputBuffer;
|
||||
nvenc.surf[i].width = allocSurf.width;
|
||||
nvenc.surf[i].height = allocSurf.height;
|
||||
|
||||
// output
|
||||
err = nvenc.nvenc_funcs.nvEncCreateBitstreamBuffer(nvenc.nvenc_ctx, &allocOut);
|
||||
if (err != NV_ENC_SUCCESS)
|
||||
return -1;
|
||||
|
||||
nvenc.surf[i].output_surface = allocOut.bitstreamBuffer;
|
||||
}
|
||||
|
||||
CUcontext dummy;
|
||||
nvenc.cuda_dl->cuCtxPopCurrent(&dummy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nvenc_frame(const uint8_t *data, unsigned pts, uint8_t *out, uint32_t &outlen) {
|
||||
NVENCSTATUS err;
|
||||
|
||||
NV_ENC_PIC_PARAMS params;
|
||||
memset(¶ms, 0, sizeof(NV_ENC_PIC_PARAMS));
|
||||
params.version = NV_ENC_PIC_PARAMS_VER;
|
||||
params.encodePicFlags = NV_ENC_PIC_FLAG_FORCEINTRA | NV_ENC_PIC_FLAG_OUTPUT_SPSPPS;
|
||||
|
||||
nvenc.cuda_dl->cuCtxPushCurrent(nvenc.cu_ctx);
|
||||
|
||||
NV_ENC_LOCK_INPUT_BUFFER lockBufferParams;
|
||||
memset(&lockBufferParams, 0, sizeof(NV_ENC_LOCK_INPUT_BUFFER));
|
||||
lockBufferParams.version = NV_ENC_LOCK_INPUT_BUFFER_VER;
|
||||
lockBufferParams.inputBuffer = nvenc.surf[nvenc.cursurf].input_surface;
|
||||
|
||||
err = nvenc.nvenc_funcs.nvEncLockInputBuffer(nvenc.nvenc_ctx, &lockBufferParams);
|
||||
if (err != NV_ENC_SUCCESS)
|
||||
return -1;
|
||||
|
||||
nvenc.surf[nvenc.cursurf].pitch = lockBufferParams.pitch;
|
||||
//vlog.info("pitch %u", lockBufferParams.pitch);
|
||||
|
||||
// copy frame
|
||||
unsigned y;
|
||||
uint8_t *dst = (uint8_t *) lockBufferParams.bufferDataPtr;
|
||||
const unsigned linelen = nvenc.surf[nvenc.cursurf].width * 4;
|
||||
for (y = 0; y < (unsigned) nvenc.surf[nvenc.cursurf].height; y++) {
|
||||
memcpy(dst, data, linelen);
|
||||
data += linelen;
|
||||
dst += lockBufferParams.pitch;
|
||||
}
|
||||
|
||||
err = nvenc.nvenc_funcs.nvEncUnlockInputBuffer(nvenc.nvenc_ctx,
|
||||
nvenc.surf[nvenc.cursurf].input_surface);
|
||||
if (err != NV_ENC_SUCCESS)
|
||||
return -1;
|
||||
|
||||
CUcontext dummy;
|
||||
nvenc.cuda_dl->cuCtxPopCurrent(&dummy);
|
||||
|
||||
params.inputBuffer = nvenc.surf[nvenc.cursurf].input_surface;
|
||||
params.bufferFmt = nvenc.surf[nvenc.cursurf].format;
|
||||
params.inputWidth = nvenc.surf[nvenc.cursurf].width;
|
||||
params.inputHeight = nvenc.surf[nvenc.cursurf].height;
|
||||
params.inputPitch = nvenc.surf[nvenc.cursurf].pitch;
|
||||
params.outputBitstream = nvenc.surf[nvenc.cursurf].output_surface;
|
||||
params.pictureStruct = NV_ENC_PIC_STRUCT_FRAME;
|
||||
params.inputTimeStamp = pts;
|
||||
|
||||
nvenc.cuda_dl->cuCtxPushCurrent(nvenc.cu_ctx);
|
||||
|
||||
err = nvenc.nvenc_funcs.nvEncEncodePicture(nvenc.nvenc_ctx, ¶ms);
|
||||
|
||||
nvenc.cuda_dl->cuCtxPopCurrent(&dummy);
|
||||
|
||||
if (err != NV_ENC_SUCCESS)
|
||||
return -1;
|
||||
|
||||
|
||||
nvenc.cuda_dl->cuCtxPushCurrent(nvenc.cu_ctx);
|
||||
|
||||
// Get output
|
||||
NV_ENC_LOCK_BITSTREAM lock_params;
|
||||
memset(&lock_params, 0, sizeof(NV_ENC_LOCK_BITSTREAM));
|
||||
|
||||
lock_params.version = NV_ENC_LOCK_BITSTREAM_VER;
|
||||
lock_params.doNotWait = 0;
|
||||
lock_params.outputBitstream = nvenc.surf[nvenc.cursurf].output_surface;
|
||||
// lock_params.sliceOffsets = slice_offsets; TODO?
|
||||
|
||||
err = nvenc.nvenc_funcs.nvEncLockBitstream(nvenc.nvenc_ctx, &lock_params);
|
||||
if (err != NV_ENC_SUCCESS)
|
||||
return -1;
|
||||
|
||||
memcpy(out, lock_params.bitstreamBufferPtr, lock_params.bitstreamSizeInBytes);
|
||||
outlen = lock_params.bitstreamSizeInBytes;
|
||||
|
||||
err = nvenc.nvenc_funcs.nvEncUnlockBitstream(nvenc.nvenc_ctx,
|
||||
nvenc.surf[nvenc.cursurf].output_surface);
|
||||
if (err != NV_ENC_SUCCESS)
|
||||
return -1;
|
||||
|
||||
nvenc.cuda_dl->cuCtxPopCurrent(&dummy);
|
||||
|
||||
//vlog.info("Pic type %x, idr %x i %x", lock_params.pictureType, NV_ENC_PIC_TYPE_IDR,
|
||||
// NV_ENC_PIC_TYPE_I);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nvidia_unload() {
|
||||
NV_ENC_PIC_PARAMS params;
|
||||
memset(¶ms, 0, sizeof(NV_ENC_PIC_PARAMS));
|
||||
params.version = NV_ENC_PIC_PARAMS_VER;
|
||||
params.encodePicFlags = NV_ENC_PIC_FLAG_EOS;
|
||||
|
||||
nvenc.cuda_dl->cuCtxPushCurrent(nvenc.cu_ctx);
|
||||
|
||||
nvenc.nvenc_funcs.nvEncEncodePicture(nvenc.nvenc_ctx, ¶ms);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < NUM_SURF; i++) {
|
||||
nvenc.nvenc_funcs.nvEncDestroyInputBuffer(nvenc.nvenc_ctx,
|
||||
nvenc.surf[i].input_surface);
|
||||
nvenc.nvenc_funcs.nvEncDestroyBitstreamBuffer(nvenc.nvenc_ctx,
|
||||
nvenc.surf[i].output_surface);
|
||||
}
|
||||
|
||||
nvenc.nvenc_funcs.nvEncDestroyEncoder(nvenc.nvenc_ctx);
|
||||
|
||||
CUcontext dummy;
|
||||
nvenc.cuda_dl->cuCtxPopCurrent(&dummy);
|
||||
|
||||
nvenc.cuda_dl->cuCtxDestroy(nvenc.cu_ctx);
|
||||
|
||||
nvenc_free_functions(&nvenc.nvenc_dl);
|
||||
cuda_free_functions(&nvenc.cuda_dl);
|
||||
}
|
||||
/*
|
||||
int main() {
|
||||
|
||||
unsigned w = 256, h = 256, kbps = 400, fps = 15;
|
||||
|
||||
memset(&nvenc, 0, sizeof(NvencDynLoadFunctions));
|
||||
if (loadfuncs() < 0)
|
||||
return 1;
|
||||
if (setupdevice() < 0)
|
||||
return 1;
|
||||
if (setupenc(w, h, kbps, fps) < 0)
|
||||
return 1;
|
||||
if (setupsurf(w, h) < 0)
|
||||
return 1;
|
||||
|
||||
unload();
|
||||
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
|
||||
int nvidia_init(const unsigned w, const unsigned h, const unsigned kbps,
|
||||
const unsigned fps) {
|
||||
|
||||
memset(&nvenc, 0, sizeof(NvencDynLoadFunctions));
|
||||
if (loadfuncs() < 0)
|
||||
return 1;
|
||||
if (setupdevice() < 0)
|
||||
return 1;
|
||||
if (setupenc(w, h, kbps, fps) < 0)
|
||||
return 1;
|
||||
if (setupsurf(w, h) < 0)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
11
common/rfb/nvidia.h
Normal file
11
common/rfb/nvidia.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef KASM_NVIDIA_H
|
||||
#define KASM_NVIDIA_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
int nvidia_init(const unsigned w, const unsigned h, const unsigned kbps,
|
||||
const unsigned fps);
|
||||
int nvenc_frame(const uint8_t *data, unsigned pts, uint8_t *out, uint32_t &outlen);
|
||||
void nvidia_unload();
|
||||
|
||||
#endif
|
||||
@@ -1,4 +1,5 @@
|
||||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
|
||||
* Copyright 2011-2019 Pierre Ossman for Cendio AB
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -63,6 +64,10 @@ namespace rfb {
|
||||
delete [] s;
|
||||
}
|
||||
|
||||
void strFree(wchar_t* s) {
|
||||
delete [] s;
|
||||
}
|
||||
|
||||
|
||||
bool strSplit(const char* src, const char limiter, char** out1, char** out2, bool fromEnd) {
|
||||
CharArray out1old, out2old;
|
||||
@@ -107,6 +112,444 @@ namespace rfb {
|
||||
dest[src ? destlen-1 : 0] = 0;
|
||||
}
|
||||
|
||||
char* convertLF(const char* src, size_t bytes)
|
||||
{
|
||||
char* buffer;
|
||||
size_t sz;
|
||||
|
||||
char* out;
|
||||
const char* in;
|
||||
size_t in_len;
|
||||
|
||||
// Always include space for a NULL
|
||||
sz = 1;
|
||||
|
||||
// Compute output size
|
||||
in = src;
|
||||
in_len = bytes;
|
||||
while ((in_len > 0) && (*in != '\0')) {
|
||||
if (*in != '\r') {
|
||||
sz++;
|
||||
in++;
|
||||
in_len--;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((in_len < 2) || (*(in+1) != '\n'))
|
||||
sz++;
|
||||
|
||||
in++;
|
||||
in_len--;
|
||||
}
|
||||
|
||||
// Alloc
|
||||
buffer = new char[sz];
|
||||
memset(buffer, 0, sz);
|
||||
|
||||
// And convert
|
||||
out = buffer;
|
||||
in = src;
|
||||
in_len = bytes;
|
||||
while ((in_len > 0) && (*in != '\0')) {
|
||||
if (*in != '\r') {
|
||||
*out++ = *in++;
|
||||
in_len--;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((in_len < 2) || (*(in+1) != '\n'))
|
||||
*out++ = '\n';
|
||||
|
||||
in++;
|
||||
in_len--;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
char* convertCRLF(const char* src, size_t bytes)
|
||||
{
|
||||
char* buffer;
|
||||
size_t sz;
|
||||
|
||||
char* out;
|
||||
const char* in;
|
||||
size_t in_len;
|
||||
|
||||
// Always include space for a NULL
|
||||
sz = 1;
|
||||
|
||||
// Compute output size
|
||||
in = src;
|
||||
in_len = bytes;
|
||||
while ((in_len > 0) && (*in != '\0')) {
|
||||
sz++;
|
||||
|
||||
if (*in == '\r') {
|
||||
if ((in_len < 2) || (*(in+1) != '\n'))
|
||||
sz++;
|
||||
} else if (*in == '\n') {
|
||||
if ((in == src) || (*(in-1) != '\r'))
|
||||
sz++;
|
||||
}
|
||||
|
||||
in++;
|
||||
in_len--;
|
||||
}
|
||||
|
||||
// Alloc
|
||||
buffer = new char[sz];
|
||||
memset(buffer, 0, sz);
|
||||
|
||||
// And convert
|
||||
out = buffer;
|
||||
in = src;
|
||||
in_len = bytes;
|
||||
while ((in_len > 0) && (*in != '\0')) {
|
||||
if (*in == '\n') {
|
||||
if ((in == src) || (*(in-1) != '\r'))
|
||||
*out++ = '\r';
|
||||
}
|
||||
|
||||
*out = *in;
|
||||
|
||||
if (*in == '\r') {
|
||||
if ((in_len < 2) || (*(in+1) != '\n')) {
|
||||
out++;
|
||||
*out = '\n';
|
||||
}
|
||||
}
|
||||
|
||||
out++;
|
||||
in++;
|
||||
in_len--;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
size_t ucs4ToUTF8(unsigned src, char* dst) {
|
||||
if (src < 0x80) {
|
||||
*dst++ = src;
|
||||
*dst++ = '\0';
|
||||
return 1;
|
||||
} else if (src < 0x800) {
|
||||
*dst++ = 0xc0 | (src >> 6);
|
||||
*dst++ = 0x80 | (src & 0x3f);
|
||||
*dst++ = '\0';
|
||||
return 2;
|
||||
} else if (src < 0x10000) {
|
||||
*dst++ = 0xe0 | (src >> 12);
|
||||
*dst++ = 0x80 | ((src >> 6) & 0x3f);
|
||||
*dst++ = 0x80 | (src & 0x3f);
|
||||
*dst++ = '\0';
|
||||
return 3;
|
||||
} else if (src < 0x110000) {
|
||||
*dst++ = 0xf0 | (src >> 18);
|
||||
*dst++ = 0x80 | ((src >> 12) & 0x3f);
|
||||
*dst++ = 0x80 | ((src >> 6) & 0x3f);
|
||||
*dst++ = 0x80 | (src & 0x3f);
|
||||
*dst++ = '\0';
|
||||
return 4;
|
||||
} else {
|
||||
return ucs4ToUTF8(0xfffd, dst);
|
||||
}
|
||||
}
|
||||
|
||||
size_t utf8ToUCS4(const char* src, size_t max, unsigned* dst) {
|
||||
size_t count, consumed;
|
||||
|
||||
*dst = 0xfffd;
|
||||
|
||||
if (max == 0)
|
||||
return 0;
|
||||
|
||||
consumed = 1;
|
||||
|
||||
if ((*src & 0x80) == 0) {
|
||||
*dst = *src;
|
||||
count = 0;
|
||||
} else if ((*src & 0xe0) == 0xc0) {
|
||||
*dst = *src & 0x1f;
|
||||
count = 1;
|
||||
} else if ((*src & 0xf0) == 0xe0) {
|
||||
*dst = *src & 0x0f;
|
||||
count = 2;
|
||||
} else if ((*src & 0xf8) == 0xf0) {
|
||||
*dst = *src & 0x07;
|
||||
count = 3;
|
||||
} else {
|
||||
// Invalid sequence, consume all continuation characters
|
||||
src++;
|
||||
max--;
|
||||
while ((max-- > 0) && ((*src++ & 0xc0) == 0x80))
|
||||
consumed++;
|
||||
return consumed;
|
||||
}
|
||||
|
||||
src++;
|
||||
max--;
|
||||
|
||||
while (count--) {
|
||||
consumed++;
|
||||
|
||||
// Invalid or truncated sequence?
|
||||
if ((max == 0) || ((*src & 0xc0) != 0x80)) {
|
||||
*dst = 0xfffd;
|
||||
return consumed;
|
||||
}
|
||||
|
||||
*dst <<= 6;
|
||||
*dst |= *src & 0x3f;
|
||||
|
||||
src++;
|
||||
max--;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
size_t ucs4ToUTF16(unsigned src, wchar_t* dst) {
|
||||
if ((src < 0xd800) || ((src >= 0xe000) && (src < 0x10000))) {
|
||||
*dst++ = src;
|
||||
*dst++ = L'\0';
|
||||
return 1;
|
||||
} else if ((src >= 0x10000) && (src < 0x110000)) {
|
||||
src -= 0x10000;
|
||||
*dst++ = 0xd800 | ((src >> 10) & 0x03ff);
|
||||
*dst++ = 0xdc00 | (src & 0x03ff);
|
||||
*dst++ = L'\0';
|
||||
return 2;
|
||||
} else {
|
||||
return ucs4ToUTF16(0xfffd, dst);
|
||||
}
|
||||
}
|
||||
|
||||
size_t utf16ToUCS4(const wchar_t* src, size_t max, unsigned* dst) {
|
||||
*dst = 0xfffd;
|
||||
|
||||
if (max == 0)
|
||||
return 0;
|
||||
|
||||
if ((*src < 0xd800) || (*src >= 0xe000)) {
|
||||
*dst = *src;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (*src & 0x0400) {
|
||||
size_t consumed;
|
||||
|
||||
// Invalid sequence, consume all continuation characters
|
||||
consumed = 0;
|
||||
while ((max > 0) && (*src & 0x0400)) {
|
||||
src++;
|
||||
max--;
|
||||
consumed++;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
*dst = *src++;
|
||||
max--;
|
||||
|
||||
// Invalid or truncated sequence?
|
||||
if ((max == 0) || ((*src & 0xfc00) != 0xdc00)) {
|
||||
*dst = 0xfffd;
|
||||
return 1;
|
||||
}
|
||||
|
||||
*dst = 0x10000 + ((*dst & 0x03ff) << 10);
|
||||
*dst |= *src & 0x3ff;
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
char* latin1ToUTF8(const char* src, size_t bytes) {
|
||||
char* buffer;
|
||||
size_t sz;
|
||||
|
||||
char* out;
|
||||
const char* in;
|
||||
size_t in_len;
|
||||
|
||||
// Always include space for a NULL
|
||||
sz = 1;
|
||||
|
||||
// Compute output size
|
||||
in = src;
|
||||
in_len = bytes;
|
||||
while ((in_len > 0) && (*in != '\0')) {
|
||||
char buf[5];
|
||||
sz += ucs4ToUTF8(*(const unsigned char*)in, buf);
|
||||
in++;
|
||||
in_len--;
|
||||
}
|
||||
|
||||
// Alloc
|
||||
buffer = new char[sz];
|
||||
memset(buffer, 0, sz);
|
||||
|
||||
// And convert
|
||||
out = buffer;
|
||||
in = src;
|
||||
in_len = bytes;
|
||||
while ((in_len > 0) && (*in != '\0')) {
|
||||
out += ucs4ToUTF8(*(const unsigned char*)in, out);
|
||||
in++;
|
||||
in_len--;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
char* utf8ToLatin1(const char* src, size_t bytes) {
|
||||
char* buffer;
|
||||
size_t sz;
|
||||
|
||||
char* out;
|
||||
const char* in;
|
||||
size_t in_len;
|
||||
|
||||
// Always include space for a NULL
|
||||
sz = 1;
|
||||
|
||||
// Compute output size
|
||||
in = src;
|
||||
in_len = bytes;
|
||||
while ((in_len > 0) && (*in != '\0')) {
|
||||
size_t len;
|
||||
unsigned ucs;
|
||||
|
||||
len = utf8ToUCS4(in, in_len, &ucs);
|
||||
in += len;
|
||||
in_len -= len;
|
||||
sz++;
|
||||
}
|
||||
|
||||
// Alloc
|
||||
buffer = new char[sz];
|
||||
memset(buffer, 0, sz);
|
||||
|
||||
// And convert
|
||||
out = buffer;
|
||||
in = src;
|
||||
in_len = bytes;
|
||||
while ((in_len > 0) && (*in != '\0')) {
|
||||
size_t len;
|
||||
unsigned ucs;
|
||||
|
||||
len = utf8ToUCS4(in, in_len, &ucs);
|
||||
in += len;
|
||||
in_len -= len;
|
||||
|
||||
if (ucs > 0xff)
|
||||
*out++ = '?';
|
||||
else
|
||||
*out++ = (unsigned char)ucs;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
char* utf16ToUTF8(const wchar_t* src, size_t units)
|
||||
{
|
||||
char* buffer;
|
||||
size_t sz;
|
||||
|
||||
char* out;
|
||||
const wchar_t* in;
|
||||
size_t in_len;
|
||||
|
||||
// Always include space for a NULL
|
||||
sz = 1;
|
||||
|
||||
// Compute output size
|
||||
in = src;
|
||||
in_len = units;
|
||||
while ((in_len > 0) && (*in != '\0')) {
|
||||
size_t len;
|
||||
unsigned ucs;
|
||||
char buf[5];
|
||||
|
||||
len = utf16ToUCS4(in, in_len, &ucs);
|
||||
in += len;
|
||||
in_len -= len;
|
||||
|
||||
sz += ucs4ToUTF8(ucs, buf);
|
||||
}
|
||||
|
||||
// Alloc
|
||||
buffer = new char[sz];
|
||||
memset(buffer, 0, sz);
|
||||
|
||||
// And convert
|
||||
out = buffer;
|
||||
in = src;
|
||||
in_len = units;
|
||||
while ((in_len > 0) && (*in != '\0')) {
|
||||
size_t len;
|
||||
unsigned ucs;
|
||||
|
||||
len = utf16ToUCS4(in, in_len, &ucs);
|
||||
in += len;
|
||||
in_len -= len;
|
||||
|
||||
out += ucs4ToUTF8(ucs, out);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
wchar_t* utf8ToUTF16(const char* src, size_t bytes)
|
||||
{
|
||||
wchar_t* buffer;
|
||||
size_t sz;
|
||||
|
||||
wchar_t* out;
|
||||
const char* in;
|
||||
size_t in_len;
|
||||
|
||||
// Always include space for a NULL
|
||||
sz = 1;
|
||||
|
||||
// Compute output size
|
||||
in = src;
|
||||
in_len = bytes;
|
||||
while ((in_len > 0) && (*in != '\0')) {
|
||||
size_t len;
|
||||
unsigned ucs;
|
||||
wchar_t buf[3];
|
||||
|
||||
len = utf8ToUCS4(in, in_len, &ucs);
|
||||
in += len;
|
||||
in_len -= len;
|
||||
|
||||
sz += ucs4ToUTF16(ucs, buf);
|
||||
}
|
||||
|
||||
// Alloc
|
||||
buffer = new wchar_t[sz];
|
||||
memset(buffer, 0, sz * sizeof(wchar_t));
|
||||
|
||||
// And convert
|
||||
out = buffer;
|
||||
in = src;
|
||||
in_len = bytes;
|
||||
while ((in_len > 0) && (*in != '\0')) {
|
||||
size_t len;
|
||||
unsigned ucs;
|
||||
|
||||
len = utf8ToUCS4(in, in_len, &ucs);
|
||||
in += len;
|
||||
in_len -= len;
|
||||
|
||||
out += ucs4ToUTF16(ucs, out);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
unsigned msBetween(const struct timeval *first,
|
||||
const struct timeval *second)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
|
||||
* Copyright 2011-2019 Pierre Ossman for Cendio AB
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -67,6 +68,7 @@ namespace rfb {
|
||||
|
||||
char* strDup(const char* s);
|
||||
void strFree(char* s);
|
||||
void strFree(wchar_t* s);
|
||||
|
||||
// Returns true if split successful. Returns false otherwise.
|
||||
// ALWAYS *copies* first part of string to out1 buffer.
|
||||
@@ -83,6 +85,25 @@ namespace rfb {
|
||||
// Copies src to dest, up to specified length-1, and guarantees termination
|
||||
void strCopy(char* dest, const char* src, int destlen);
|
||||
|
||||
// Makes sure line endings are in a certain format
|
||||
|
||||
char* convertLF(const char* src, size_t bytes = (size_t)-1);
|
||||
char* convertCRLF(const char* src, size_t bytes = (size_t)-1);
|
||||
|
||||
// Convertions between various Unicode formats. The returned strings are
|
||||
// always null terminated and must be freed using strFree().
|
||||
|
||||
size_t ucs4ToUTF8(unsigned src, char* dst);
|
||||
size_t utf8ToUCS4(const char* src, size_t max, unsigned* dst);
|
||||
|
||||
size_t ucs4ToUTF16(unsigned src, wchar_t* dst);
|
||||
size_t utf16ToUCS4(const wchar_t* src, size_t max, unsigned* dst);
|
||||
|
||||
char* latin1ToUTF8(const char* src, size_t bytes = (size_t)-1);
|
||||
char* utf8ToLatin1(const char* src, size_t bytes = (size_t)-1);
|
||||
|
||||
char* utf16ToUTF8(const wchar_t* src, size_t units = (size_t)-1);
|
||||
wchar_t* utf8ToUTF16(const char* src, size_t bytes = (size_t)-1);
|
||||
|
||||
// HELPER functions for timeout handling
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ TigerVNC is
|
||||
All Rights Reserved.
|
||||
|
||||
This software is distributed under the GNU General Public Licence as published
|
||||
by the Free Software Foundation. See the file LICENCE.TXT for the conditions
|
||||
by the Free Software Foundation. See the file LICENSE.TXT for the conditions
|
||||
under which this software is made available. TigerVNC also contains code from
|
||||
other sources. See the Acknowledgements section below, and the individual
|
||||
source files, for details of the conditions under which they are made
|
||||
|
||||
@@ -26,7 +26,7 @@ TigerVNC is
|
||||
All Rights Reserved.
|
||||
|
||||
This software is distributed under the GNU General Public Licence as published
|
||||
by the Free Software Foundation. See the file LICENCE.TXT for the conditions
|
||||
by the Free Software Foundation. See the file LICENSE.TXT for the conditions
|
||||
under which this software is made available. TigerVNC also contains code from
|
||||
other sources. See the Acknowledgements section below, and the individual
|
||||
source files, for details of the conditions under which they are made
|
||||
|
||||
@@ -26,7 +26,7 @@ TigerVNC is
|
||||
All Rights Reserved.
|
||||
|
||||
This software is distributed under the GNU General Public Licence as published
|
||||
by the Free Software Foundation. See the file LICENCE.TXT for the conditions
|
||||
by the Free Software Foundation. See the file LICENSE.TXT for the conditions
|
||||
under which this software is made available. TigerVNC also contains code from
|
||||
other sources. See the Acknowledgements section below, and the individual
|
||||
source files, for details of the conditions under which they are made
|
||||
|
||||
@@ -1031,7 +1031,7 @@ fi
|
||||
|
||||
%files license
|
||||
%defattr(-,root,root,-)
|
||||
%doc LICENCE.TXT
|
||||
%doc LICENSE.TXT
|
||||
|
||||
%files icons
|
||||
%defattr(-,root,root,-)
|
||||
|
||||
@@ -444,7 +444,7 @@ fi
|
||||
|
||||
%files license
|
||||
%defattr(-,root,root,-)
|
||||
%doc LICENCE.TXT
|
||||
%doc LICENSE.TXT
|
||||
|
||||
%files icons
|
||||
%defattr(-,root,root,-)
|
||||
|
||||
@@ -376,7 +376,7 @@ fi
|
||||
%{_datadir}/vnc/classes/*
|
||||
|
||||
%files license
|
||||
%doc %{_docdir}/%{name}-%{version}/LICENCE.TXT
|
||||
%doc %{_docdir}/%{name}-%{version}/LICENSE.TXT
|
||||
|
||||
%files icons
|
||||
%defattr(-,root,root,-)
|
||||
|
||||
5
debian/control
vendored
5
debian/control
vendored
@@ -3,7 +3,8 @@ Section: x11
|
||||
Priority: optional
|
||||
Maintainer: Kasm Technologies LLC <info@kasmweb.com>
|
||||
Build-Depends: debhelper (>= 11), rsync, libjpeg-dev, libjpeg-dev, libpng-dev,
|
||||
libtiff-dev, libgif-dev, libavcodec-dev, libssl-dev, libgl1, libxfont2, libsm6
|
||||
libtiff-dev, libgif-dev, libavcodec-dev, libssl-dev, libgl1, libxfont2, libsm6,
|
||||
libx264-dev
|
||||
Standards-Version: 4.1.3
|
||||
Homepage: https://github.com/kasmtech/KasmVNC
|
||||
#Vcs-Browser: https://salsa.debian.org/debian/kasmvnc
|
||||
@@ -12,7 +13,7 @@ Homepage: https://github.com/kasmtech/KasmVNC
|
||||
Package: kasmvncserver
|
||||
Architecture: amd64
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}, ssl-cert, xauth,
|
||||
x11-xkb-utils, xkb-data, procps
|
||||
x11-xkb-utils, xkb-data, procps, x264
|
||||
Provides: vnc-server
|
||||
Description: VNC server accessible from a web browser
|
||||
VNC stands for Virtual Network Computing. It is, in essence, a remote
|
||||
|
||||
1
kasmweb
Submodule
1
kasmweb
Submodule
Submodule kasmweb added at cd7a460920
@@ -1 +0,0 @@
|
||||
**/xtscancodes.js
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user