From b998c31e7e0f4f84b2f64c50093069c815772808 Mon Sep 17 00:00:00 2001 From: zwer Date: Tue, 24 Jan 2006 12:54:00 +0000 Subject: FFNetDev-Plugin git-svn-id: svn://svn.berlios.de/ffnetdev/trunk@1 1f4bef6d-8e0a-0410-8695-e467da8aaccf --- CHANGELOG | 25 ++ COPYING | 340 ++++++++++++++++++++++ Makefile | 109 +++++++ MinMax.h | 51 ++++ README | 95 ++++++ config.h | 14 + ffnetdev.c | 245 ++++++++++++++++ ffnetdev.h | 50 ++++ ffnetdevsetup.c | 26 ++ ffnetdevsetup.h | 17 ++ i18n.c | 44 +++ i18n.h | 15 + libvdr-ffnetdev.so | Bin 0 -> 434326 bytes netosd.c | 78 +++++ netosd.h | 38 +++ osdworker.c | 756 ++++++++++++++++++++++++++++++++++++++++++++++++ osdworker.h | 122 ++++++++ pes2ts.c | 210 ++++++++++++++ pes2ts.h | 50 ++++ remote.c | 58 ++++ remote.h | 36 +++ rfb.h | 85 ++++++ rfbproto.h | 786 ++++++++++++++++++++++++++++++++++++++++++++++++++ streamdevice.c | 166 +++++++++++ streamdevice.h | 48 +++ tableinitcmtemplate.c | 108 +++++++ tableinittctemplate.c | 122 ++++++++ tabletranstemplate.c | 99 +++++++ tools/select.c | 49 ++++ tools/select.h | 75 +++++ tools/socket.c | 135 +++++++++ tools/socket.h | 108 +++++++ tools/source.c | 169 +++++++++++ tools/source.h | 109 +++++++ tools/tools.c | 12 + tools/tools.h | 67 +++++ translate.c | 138 +++++++++ translate.h | 81 ++++++ tsworker.c | 248 ++++++++++++++++ tsworker.h | 56 ++++ vncEncodeCoRRE.c | 536 ++++++++++++++++++++++++++++++++++ vncEncodeCoRRE.h | 84 ++++++ vncEncodeHexT.c | 412 ++++++++++++++++++++++++++ vncEncodeHexT.h | 75 +++++ vncEncodeRRE.c | 349 ++++++++++++++++++++++ vncEncodeRRE.h | 69 +++++ vncEncoder.c | 673 ++++++++++++++++++++++++++++++++++++++++++ vncEncoder.h | 141 +++++++++ 48 files changed, 7379 insertions(+) create mode 100644 CHANGELOG create mode 100644 COPYING create mode 100644 Makefile create mode 100644 MinMax.h create mode 100644 README create mode 100644 config.h create mode 100644 ffnetdev.c create mode 100644 ffnetdev.h create mode 100644 ffnetdevsetup.c create mode 100644 ffnetdevsetup.h create mode 100644 i18n.c create mode 100644 i18n.h create mode 100644 libvdr-ffnetdev.so create mode 100644 netosd.c create mode 100644 netosd.h create mode 100644 osdworker.c create mode 100644 osdworker.h create mode 100644 pes2ts.c create mode 100644 pes2ts.h create mode 100644 remote.c create mode 100644 remote.h create mode 100644 rfb.h create mode 100644 rfbproto.h create mode 100644 streamdevice.c create mode 100644 streamdevice.h create mode 100644 tableinitcmtemplate.c create mode 100644 tableinittctemplate.c create mode 100644 tabletranstemplate.c create mode 100644 tools/select.c create mode 100644 tools/select.h create mode 100644 tools/socket.c create mode 100644 tools/socket.h create mode 100644 tools/source.c create mode 100644 tools/source.h create mode 100644 tools/tools.c create mode 100644 tools/tools.h create mode 100644 translate.c create mode 100644 translate.h create mode 100644 tsworker.c create mode 100644 tsworker.h create mode 100644 vncEncodeCoRRE.c create mode 100644 vncEncodeCoRRE.h create mode 100644 vncEncodeHexT.c create mode 100644 vncEncodeHexT.h create mode 100644 vncEncodeRRE.c create mode 100644 vncEncodeRRE.h create mode 100644 vncEncoder.c create mode 100644 vncEncoder.h diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..d366286 --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,25 @@ +Changelog + 2005-04-18: ffnetdev-0.0.1 + - first version of ffnetdev + + 2005-07-07: ffnetdev-0.0.2 + - splitted the main worker thread into two threads. One for VNC and one for TS streaming. + - fixed busy-wait condition causing a high CPU load + - revised the OSD code to support VNC protocol version 3.3 including truecolor mode. + - added command line options for OSD(VNC) port, TS port and an option for explicitly enabling + the plugin's remote control in VDR + + 2005-07-20: ffnetdev-0.0.3 + - fixed Makefile so that "make dist" works + - fixed a bug in osdworker.c which caused a double action on a single keypress (VNC) + - added support for clients with bitsperpixel=8 and depth=8 + - changed README: changed the required VDR version number to >= 1.3.18 due to changes in cDevice + - changed README: added a short description of supported VNC features + - added support for the bigEndian flag in VNC SetPixelFormat Message + - added support for bitsperpixel=32 + - cleanup of debug output + + 2005-08-27: ffnetdev-0.0.4 + - fixed PES2TS remuxer: PES packets shorter than TS payload size(184 bytes) did not have the PUSI + (payload unit start indicator) flag set + - reworked PES2TS remuxer code and added a mutex to synchronize input ringbuffer access diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..5b6e7c6 --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program 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 program 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 program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..fa42c2a --- /dev/null +++ b/Makefile @@ -0,0 +1,109 @@ +# +# Makefile for a Video Disk Recorder plugin +# + +# The official name of this plugin. +# This name will be used in the '-P...' option of VDR to load the plugin. +# By default the main source file also carries this name. +# +PLUGIN = ffnetdev +DEBUG = 1 + +### The version number of this plugin (taken from the main source file): + +VERSION = $(shell grep 'const char .*VERSION *=' ffnetdev.c | awk '{ print $$5 }' | sed -e 's/[";]//g') + +### The C++ compiler and options: + +CXX ?= g++ +CXXFLAGS ?= -W -Woverloaded-virtual + +### The directory environment: + +DVBDIR = ../../../../DVB +VDRDIR = ../../.. +LIBDIR = ../../lib +TMPDIR = /tmp + +### Allow user defined options to overwrite defaults: + +-include $(VDRDIR)/Make.config + +### The version number of VDR (taken from VDR's "config.h"): + +VDRVERSION = $(shell grep 'define VDRVERSION ' $(VDRDIR)/config.h | awk '{ print $$3 }' | sed -e 's/"//g') + +### The name of the distribution archive: + +ARCHIVE = $(PLUGIN)-$(VERSION) +PACKAGE = vdr-$(ARCHIVE) + +### Includes and Defines (add further entries here): + +INCLUDES += -I$(VDRDIR)/include -I$(DVBDIR)/include -I. + +DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"' + +### The object files (add further files here): + +COMMONOBJS = i18n.o \ + \ + tools/source.o tools/select.o tools/socket.o tools/tools.o + + +SERVEROBJS = $(PLUGIN).o \ + \ + ffnetdev.o ffnetdevsetup.o osdworker.o tsworker.o netosd.o streamdevice.o pes2ts.o remote.o \ + vncEncodeRRE.o vncEncodeCoRRE.o vncEncodeHexT.o \ + vncEncoder.o translate.o \ + +ifdef DEBUG + DEFINES += -DDEBUG + CXXFLAGS += -g +else + CXXFLAGS += -O2 +endif + +### Implicit rules: + +%.o: %.c + $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) -o $@ $< + +# Dependencies: + +MAKEDEP = g++ -MM -MG +DEPFILE = .dependencies +ifdef GCC3 +$(DEPFILE): Makefile + @rm -f $@ + @for i in $(SERVEROBJS:%.o=%.c) $(COMMONOBJS:%.o=%.c) ; do \ + $(MAKEDEP) $(DEFINES) $(INCLUDES) -MT "`dirname $$i`/`basename $$i .c`.o" $$i >>$@ ; \ + done +else +$(DEPFILE): Makefile + @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(SERVEROBJS:%.o=%.c) \ + $(COMMONOBJS:%.o=%.c) > $@ +endif + +-include $(DEPFILE) + +### Targets: + +all: libvdr-$(PLUGIN).so + +libvdr-$(PLUGIN).so: $(SERVEROBJS) $(COMMONOBJS) + +%.so: + $(CXX) $(CXXFLAGS) -shared $^ -o $@ + @cp $@ $(LIBDIR)/$@.$(VDRVERSION) + +dist: clean + @-rm -rf $(TMPDIR)/$(ARCHIVE) + @mkdir $(TMPDIR)/$(ARCHIVE) + @cp -a * $(TMPDIR)/$(ARCHIVE) + @tar cjf $(PACKAGE).tar.bz2 --exclude SCCS -C $(TMPDIR) $(ARCHIVE) + @-rm -rf $(TMPDIR)/$(ARCHIVE) + @echo Distribution package created as $(PACKAGE).tar.bz2 + +clean: + @-rm -f $(COMMONOBJS) $(SERVEROBJS) $(DEPFILE) *.so *.tar.bz2 core* *~ *.bak diff --git a/MinMax.h b/MinMax.h new file mode 100644 index 0000000..1c7d479 --- /dev/null +++ b/MinMax.h @@ -0,0 +1,51 @@ +// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. +// +// This file is part of the VNC system. +// +// The VNC system 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 program 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 program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +// USA. +// +// TightVNC distribution homepage on the Web: http://www.tightvnc.com/ +// +// If the source code for the VNC system is not available from the place +// whence you received this file, check http://www.uk.research.att.com/vnc or contact +// the authors on vnc@uk.research.att.com for information on obtaining it. + + +// Routines to calculate the maximum and minimum of two integers + +#if !defined(MINMAX_INCLUDED) +#define MINMAX_INCLUDED +#pragma once + +// Some routines used internally to minimise and maximise integers +inline int Max(int x, int y) +{ + if (x>y) + return x; + else + return y; +} + +inline int Min(int x, int y) +{ + if (x>y) + return y; + else + return x; +} + + +#endif diff --git a/README b/README new file mode 100644 index 0000000..bd5f25e --- /dev/null +++ b/README @@ -0,0 +1,95 @@ +This is a "plugin" for the Video Disk Recorder (VDR). + +Written by: Christian Cier-Zniewski + Jurij Retzlaff +some code taken from: Sascha Volkenandt's streamdev plugin , + TightVNC + + +Project's homepage: https://developer.berlios.de/projects/ffnetdev/ + +See the file COPYING for license information. + +!!! WARNING !!! +The code of this plugin is alpha quality. So expect it to have all kinds of bugs. +If it crashes your machine, do not blame me. You have been warned!!! :-) +!!! WARNING !!! + +------------ +Description: +------------ + +The purpose of this plugin is to provide an "easy" way of connecting possible streaming clients +to VDR by emulating a full featured DVB device over the network including OSD and TS streaming capabilities. + +The FFnetDev Plugin creates two listening TCP ports: +1) a VNC server on default port 20001 that exports the OSD and accepts remote control keys from the keyboard(or whatever the VNC client uses). +2) a TS streaming server on default port 20002 that streams the MPEG2 data instead of decoding and rendering it. + The streaming server registers as a "normal" Full Featured DVB device in VDR. Therefore offering playback capabilites including playback of + recordings and "transfer mode". + +Both built-in servers work independent from each other. +So you can just use the plugin for viewing the OSD on a remote machine for example. + +-------- +Details: +-------- + +OSD (and text2skin) +------------------- +The OSD is transfered using the VNC protocol (aka VNC). See rfbproto.h for details. +Features supported: +-currently supported encodings: RAW, RRE, Hextile, CoRRE (CoRRE have a bug) +-colour-map and truecolor +-depths in truecolor mode: 8-bit, 16-bit and 32-bit + +You can also use the text2skin plugin to get a nice skinned OSD. +BUT BE AWARE that the code supports ONE BIG AREA only(method CanHandleAreas). +This limitation is due to the fact that the built-in VNC server also supports palette based (a.k.a. colour map) framebuffer via VNC. +So if you want to use the text2skin plugin you have to change the desired skin to only use ONE + tag with the greatest bounding rectangle the skin wants to draw in. bpp should be set to 8 bits. +This is NOT going to change in future versions of the ffnetdev plugin. + +TS streaming +------------ +The PES packets are multiplexed into a TS by the plugins own very simple PES2TS remux code. +No PAT/PMT insertion is currently being done. +The two TS PIDs for Audio and Video PIDs have fixed values. So changing a channel does not result in a change +of the TS PIDs. (Video-PID: 99, Audio-PID: 100 [decimal]) + +Existing clients +---------------- +-Dreambox 5620S (simple native client, it is NOT an enigma plugin!, supports OSD via VNC and TS-Streaming) +-Tuxbox VDRViewer(simple native client, it is NOT an enigma plugin!, supports OSD via VNC and TS-Streaming) +-Any VNC client using RFB protocol version 3.3 or 3.5 (TightVNC, RealVNC, etc.) should work + +Command line options +-------------------- + + -t PORT, --tsport PORT port number for sending TS to. + -o PORT, --osdport PORT listen on this port for OSD connect. + -e enable remote control over OSD connection. + +Option "-e" registers a new remote control object causing VDR to start the learning procedure for this new remote control if you start +VDR and the ffnetdev plugin for the first time. If you want to control VDR over SVDRP you can simply leave out the "-e" option. + +-------------- +Prerequisites: +-------------- +This plugin only works for VDR versions >=1.3.18. +It is currently being developed with VDR version 1.3.28. + +Installation: +------------- + +Install ffnetdev like any other plugin. In this example I assume that you have +changed to the folder where the VDR sourcecode is located, and that it is +version 0.0.3 of the plugin you wish to install. + +root@linux # cd PLUGINS/src +root@linux # wget http://nano.gmxhome.de/ffnetdev/vdr-ffnetdev-0.0.4.tar.bz2 +root@linux # tar xfj vdr-ffnetdev-0.0.4.tar.bz2 +root@linux # cd ../.. +root@linux # make plugins +root@linux # ./vdr -P ffnetdev + diff --git a/config.h b/config.h new file mode 100644 index 0000000..d10b02c --- /dev/null +++ b/config.h @@ -0,0 +1,14 @@ +#ifndef __FFNETDEVCONFIG_H +#define __FFNETDEVCONFIG_H + + +struct sFFNetDevConfig { + int iAutoSetPrimaryDVB; +}; + +extern sFFNetDevConfig config; + +// #define DEBUG + +#endif //__FFNETDEVCONFIG_H + diff --git a/ffnetdev.c b/ffnetdev.c new file mode 100644 index 0000000..a10bc14 --- /dev/null +++ b/ffnetdev.c @@ -0,0 +1,245 @@ +/* + * ffnetdev.c: Full Featured Network Device VDR-Plugin for Streaming + * + * See the README file for copyright information and how to reach the author. + * + */ + +#include +#include + +#include + +#include "i18n.h" +#include "tsworker.h" +#include "netosd.h" +#include "ffnetdev.h" +#include "streamdevice.h" +#include "remote.h" +#include "osdworker.h" +#include "config.h" +#include "ffnetdevsetup.h" + + +const char *cPluginFFNetDev::VERSION = "0.0.4"; +const char *cPluginFFNetDev::DESCRIPTION = "Full Featured Network Device for Streaming"; +//const char *cOSDWorker::MAINMENUENTRY = "FFNetDev"; + +// --- cNetOSDProvider ----------------------------------------------- + +cNetOSDProvider::cNetOSDProvider(void) +{ + +} + +cOsd * cNetOSDProvider::CreateOsd(int Left, int Top) +{ + + osd = new cNetOSD(Left, Top); + return osd; +} + + +// --- cPluginFFNetDev ---------------------------------------------------------- + +const char *cPluginFFNetDev::Version(void) { + return tr(VERSION); +} + +const char *cPluginFFNetDev::Description(void) { + return tr(DESCRIPTION); +} + +cPluginFFNetDev::cPluginFFNetDev(void) +{ + // Initialize any member variables here. + // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL + // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT! + TSPort = STREAMPORT; + OSDPort = OSDPORT; + EnableRemote = false; + m_origPrimaryDevice = -1; + m_Remote = NULL; + + config.iAutoSetPrimaryDVB = 0; +} + +cPluginFFNetDev::~cPluginFFNetDev() +{ + cOSDWorker::Exit(); + cTSWorker::Exit(); +} + +const char *cPluginFFNetDev::CommandLineHelp(void) +{ + // Return a string that describes all known command line options. + return + " -t PORT, --tsport PORT port number for sending TS to.\n" + " -o PORT, --osdport PORT listen on this port for OSD connect.\n" + " -e enable remote control over OSD connection.\n" + "\n"; +} + +bool cPluginFFNetDev::ProcessArgs(int argc, char *argv[]) +{ + // Implement command line argument processing here if applicable. + fprintf (stderr, "[ffnetdev] processing args.\n"); + static struct option long_options[] = { + { "tsport", required_argument , NULL, 't' }, + { "osdport", required_argument , NULL, 'o' }, + { "enable-remote", no_argument , NULL, 'e' }, + { NULL, 0 , NULL, 0 } + }; + + int c; + while ((c = getopt_long(argc, argv, "t:o:e", long_options, NULL)) != -1) { + switch (c) { + case 'e': EnableRemote = true; + break; + case 't': if (isnumber(optarg)) { + int n = atoi(optarg); + if (0 < n && n < 65536) { + TSPort = n; + fprintf(stderr, "[ffnetdev] TS Port: %d\n", n); + break; + } + } + fprintf(stderr, "[ffnetdev] invalid port number: %s\n", optarg); + return 2; + break; + case 'o': if (isnumber(optarg)) { + int n = atoi(optarg); + if (0 < n && n < 65536) { + OSDPort = n; + fprintf(stderr, "[ffnetdev] OSD Port: %d\n", n); + break; + } + } + fprintf(stderr, "[ffnetdev] invalid port number: %s\n", optarg); + return 2; + break; + default : return 2; + } + } + fprintf (stderr, "[ffnetdev] finished processing args.\n"); + return true; +} + +bool cPluginFFNetDev::Active(void) { + return (cOSDWorker::Active() || cTSWorker::Active()); +} + +bool cPluginFFNetDev::Start(void) +{ + // Start any background activities the plugin shall perform. + RegisterI18n(Phrases); + + + cOSDWorker::Init(OSDPort, this); + cTSWorker::Init(m_StreamDevice, TSPort, this); + + return true; +} + +bool cPluginFFNetDev::Initialize(void) +{ + // Start any background activities the plugin shall perform. + fprintf(stderr,"[ffnetdev] initializing plugin.\n"); + m_StreamDevice = new cStreamDevice(); + return true; +} + +void cPluginFFNetDev::Housekeeping(void) +{ + // Perform any cleanup or other regular tasks. +} + +cOsdObject *cPluginFFNetDev::MainMenuAction(void) +{ + // Perform the action when selected from the main VDR menu. + fprintf (stderr, "[ffnetdev] MainMenuAction called.\n"); +// return new cMenuSetupSoftdevice; + return NULL; +} + +cMenuSetupPage *cPluginFFNetDev::SetupMenu(void) +{ + // Return our setup menu +// return new cMenuSetupFFNetDev; + return new cFFNetDevSetup; +} + +bool cPluginFFNetDev::SetupParse(const char *Name, const char *Value) +{ + // Parse your own setup parameters and store their values. + if (!strcasecmp(Name, "AutoSetPrimaryDVB")) config.iAutoSetPrimaryDVB = atoi(Value); + else + return false; + + return true; +} + +void cPluginFFNetDev::SetPrimaryDevice() +{ + if ((config.iAutoSetPrimaryDVB == 1) && (m_origPrimaryDevice == -1)) + { + cDevice *PrimaryDevice; + if ((PrimaryDevice = cDevice::PrimaryDevice()) != NULL) + m_origPrimaryDevice = PrimaryDevice->DeviceNumber() + 1; + else + m_origPrimaryDevice = -1; + + if (m_StreamDevice->DeviceNumber() + 1 != m_origPrimaryDevice) + { + cDevice::SetPrimaryDevice(m_StreamDevice->DeviceNumber() + 1); + isyslog("[ffnetdev] set Primary Device to %d", m_StreamDevice->DeviceNumber() + 1); + } + else + { + m_origPrimaryDevice = -1; + } + } + + if(EnableRemote) + { + m_Remote = new cMyRemote("ffnetdev"); + new cLearningThread(); +#ifdef DEBUG + fprintf(stderr, "[ffnetdev] remote control enabled.\n"); +#endif + isyslog("[ffnetdev] remote control enabled.\n"); + } + else + { +#ifdef DEBUG + fprintf(stderr, "[ffnetdev] remote control disabled.\n"); +#endif + isyslog("[ffnetdev] remote control disabled.\n"); + delete m_Remote; + m_Remote = NULL; + } +} + + +void cPluginFFNetDev::RestorePrimaryDevice() +{ +#ifdef DEBUG + fprintf(stderr, "[ffnetdev] remote control disabled.\n"); +#endif + isyslog("[ffnetdev] remote control disabled.\n"); + delete m_Remote; + m_Remote = NULL; + + if (m_origPrimaryDevice != -1) + { + cDevice::SetPrimaryDevice(m_origPrimaryDevice); + isyslog("[ffnetdev] restore Primary Device to %d", m_origPrimaryDevice); + m_origPrimaryDevice = -1; + sleep(5); + } +} + + +sFFNetDevConfig config; + +VDRPLUGINCREATOR(cPluginFFNetDev); // Don't touch this! diff --git a/ffnetdev.h b/ffnetdev.h new file mode 100644 index 0000000..9e5e327 --- /dev/null +++ b/ffnetdev.h @@ -0,0 +1,50 @@ +/* + * ffnetdev.h: Full Featured Network Device VDR-Plugin for Streaming + * + * See the README file for copyright information and how to reach the author. + * + */ +#ifndef _FFNETDEV__H +#define _FFNETDEV__H + +#include +#include "streamdevice.h" +#include "remote.h" + +#define OSDPORT 20001 +#define STREAMPORT 20002 + +class cPluginFFNetDev : public cPlugin { +private: + static const char *DESCRIPTION; + static const char *VERSION; + //static const char *MAINMENUENTRY; + cStreamDevice *m_StreamDevice; + cMyRemote *m_Remote; + int OSDPort; + int TSPort; + bool EnableRemote; + int m_origPrimaryDevice; + +public: + cPluginFFNetDev(void); + virtual ~cPluginFFNetDev(); + virtual const char *Description(void); + virtual const char *Version(void); + virtual const char *CommandLineHelp(void); + virtual bool Initialize(void); + virtual bool ProcessArgs(int argc, char *argv[]); + virtual bool Start(void); + virtual void Housekeeping(void); + //virtual const char *MainMenuEntry(void) { return tr(MAINMENUENTRY); } + virtual cOsdObject *MainMenuAction(void); + virtual cMenuSetupPage *SetupMenu(void); + virtual bool SetupParse(const char *Name, const char *Value); + virtual bool Active(void); + + void SetPrimaryDevice(); + void RestorePrimaryDevice(); + cMyRemote *GetRemote() { return m_Remote; } +}; + +#endif diff --git a/ffnetdevsetup.c b/ffnetdevsetup.c new file mode 100644 index 0000000..6c0c339 --- /dev/null +++ b/ffnetdevsetup.c @@ -0,0 +1,26 @@ +/* + * ffnetdevsetup.c: A plugin for the Video Disk Recorder + * + * See the README file for copyright information and how to reach the author. + * + * $Id$ + */ + + +#include "ffnetdevsetup.h" +#include "config.h" + +cFFNetDevSetup::cFFNetDevSetup(void) +{ + + m_iAutoSetPrimaryDVB = config.iAutoSetPrimaryDVB; + + Add(new cMenuEditBoolItem(tr("auto set as primary device"), &m_iAutoSetPrimaryDVB, tr("no"), tr("yes"))); +} + +void cFFNetDevSetup::Store(void) +{ + SetupStore("AutoSetPrimaryDVB", config.iAutoSetPrimaryDVB = m_iAutoSetPrimaryDVB); +} + + diff --git a/ffnetdevsetup.h b/ffnetdevsetup.h new file mode 100644 index 0000000..befbd6f --- /dev/null +++ b/ffnetdevsetup.h @@ -0,0 +1,17 @@ +#ifndef __FFNETDEVSETUP_H +#define __FFNETDEVSETUP_H + +#include + +class cFFNetDevSetup : public cMenuSetupPage +{ +private: + int m_iAutoSetPrimaryDVB; +protected: + virtual void Store(void); +public: + cFFNetDevSetup(void); +}; + +#endif //__FFNETDEVSETUP_H + diff --git a/i18n.c b/i18n.c new file mode 100644 index 0000000..9a6eff7 --- /dev/null +++ b/i18n.c @@ -0,0 +1,44 @@ +/* + * i18n.c: Internationalization + * + * See the README file for copyright information and how to reach the author. + * + */ + +#include "i18n.h" + +const tI18nPhrase Phrases[] = { + { "Full Featured Network Device for Streaming", + "Full Featured Network Device for Streaming", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "auto set as primary device", + "auto set as primary device", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { NULL } + }; diff --git a/i18n.h b/i18n.h new file mode 100644 index 0000000..2f6951e --- /dev/null +++ b/i18n.h @@ -0,0 +1,15 @@ +/* + * i18n.h: Internationalization + * + * See the README file for copyright information and how to reach the author. + * + */ + +#ifndef _I18N__H +#define _I18N__H + +#include + +extern const tI18nPhrase Phrases[]; + +#endif //_I18N__H diff --git a/libvdr-ffnetdev.so b/libvdr-ffnetdev.so new file mode 100644 index 0000000..4369558 Binary files /dev/null and b/libvdr-ffnetdev.so differ diff --git a/netosd.c b/netosd.c new file mode 100644 index 0000000..5bfdab6 --- /dev/null +++ b/netosd.c @@ -0,0 +1,78 @@ +/* + * netosd.c: OSD over network + * + * See the README file for copyright information and how to reach the author. + * + */ + +#include "netosd.h" +#include "osdworker.h" + +////////////////////////////////////////////////////////////////////////////////////////////////// +cNetOSD::cNetOSD(int Left, int Top) : cOsd(Left, Top) +{ +#ifdef DEBUG + fprintf(stderr,"[ffnetdev] NetOSD: Constructor cNetOSD.\n"); +#endif +} + +cNetOSD::~cNetOSD() +{ +#ifdef DEBUG + fprintf(stderr,"[ffnetdev] NetOSD: Destructor cNetOSD.\n"); +#endif + + cOSDWorker::ClearScreen(); +} + +eOsdError cNetOSD::CanHandleAreas(const tArea *Areas, int NumAreas) +{ + eOsdError Result = cOsd::CanHandleAreas(Areas, NumAreas); + if (Result == oeOk) { + if (NumAreas > 1) // Handle only one big area (because we support VNC colour map mode) + return oeTooManyAreas; // We cannot handle multiple areas having different colour maps. We need a single colourmap. Thus, only one area. + int TotalMemory = 0; + for (int i = 0; i < NumAreas; i++) { + if (Areas[i].bpp != 1 && Areas[i].bpp != 2 && Areas[i].bpp != 4 && Areas[i].bpp != 8) + return oeBppNotSupported; + if ((Areas[i].Width() & (8 / Areas[i].bpp - 1)) != 0) + return oeWrongAlignment; + TotalMemory += Areas[i].Width() * Areas[i].Height() / (8 / Areas[i].bpp); + } +#ifdef DEBUG + fprintf(stderr, "[ffnetdev] NetOSD: CanHandleAreas: OSD area size: %d bytes.\r\n", TotalMemory); +#endif + if (TotalMemory > MAXOSDMEMORY) + return oeOutOfMemory; + } + return Result; +} + +void cNetOSD::Flush(void) +{ + cBitmap *Bitmap; + + for (int i = 0; (Bitmap = GetBitmap(i)) != NULL; i++) + { + int x1=0, x2=0, y1=0, y2=0; + if (Bitmap->Dirty(x1, y1, x2, y2)) + { + // commit colors: + int NumColors; + const tColor *Colors = Bitmap->Colors(NumColors); + if (Colors) { + cOSDWorker::SendCMAP(NumColors , Colors); + } +#ifdef DEBUG + fprintf(stderr, "[ffnetdev] NetOSD: Left: %d, Top: %d, X0: %d, Y0: %d, Width: %d, Height: %d\n", + Left(), Top(), Bitmap->X0(), Bitmap->Y0(), Bitmap->Width(), Bitmap->Height()); + fprintf(stderr, "[ffnetdev] NetOSD: Dirty area x1: %d, y1: %d, x2: %d, y2: %d\n",x1,y1,x2,y2); +#endif + // commit modified data: + cOSDWorker::SendScreen(Bitmap->Width(), Left()+x1 +Bitmap->X0(), Top()+y1+Bitmap->Y0(), x2-x1+1, y2-y1+1, Bitmap->Data(x1, y1)); + } + Bitmap->Clean(); + } +} + + diff --git a/netosd.h b/netosd.h new file mode 100644 index 0000000..1657d5e --- /dev/null +++ b/netosd.h @@ -0,0 +1,38 @@ +/* + * netosd.h: OSD over network + * + * See the README file for copyright information and how to reach the author. + * + */ + +#ifndef _NETOSD__H +#define _NETOSD__H + +#include +#include "tools/socket.h" + +// --- cNetOSD ----------------------------------------------- +class cNetOSD : public cOsd { +private: + bool truecolor; +protected: +public: + cNetOSD(int XOfs, int XOfs); + virtual ~cNetOSD(); + virtual eOsdError CanHandleAreas(const tArea *Areas, int NumAreas); + virtual void Flush(void); +}; + +// --- cNetOSDProvider ---------------------------------------- + +class cNetOSDProvider : public cOsdProvider { +private: + cOsd *osd; + cNetOSD **NetOSD; +public: + cNetOSDProvider(void); + virtual cOsd *CreateOsd(int Left, int Top); +}; + +#endif //_NETOSD__H + diff --git a/osdworker.c b/osdworker.c new file mode 100644 index 0000000..8147ddb --- /dev/null +++ b/osdworker.c @@ -0,0 +1,756 @@ +/* + * osdworker.c: osd worker thread + * + * See the README file for copyright information and how to reach the author. + * + */ + +#include +#include + +#include "tools/socket.h" +#include "tools/select.h" + +#include "osdworker.h" + + +#include "vncEncodeRRE.h" +#include "vncEncodeCoRRE.h" +#include "vncEncodeHexT.h" + +////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////// + +cOSDWorker *cOSDWorker::m_Instance = NULL; + +cOSDWorker::cOSDWorker(void) + : cThread("[ffnetdev] OSD via VNC") +{ + m_Active = false; + m_OSDClient = NULL; + m_pSendBuffer = NULL; + m_SendBufferSize = 0; + state = NO_CLIENT; + close_OSDclient_request = false; + + UseAlpha = false; + + m_pEncoder = NULL; + + ServerFormat.trueColour = false; + ServerFormat.bitsPerPixel = 8; + ServerFormat.depth = 8; + ServerFormat.bigEndian = false; + ServerFormat.redShift = 16; + ServerFormat.redMax = 255; + ServerFormat.greenShift = 8; + ServerFormat.greenMax = 255; + ServerFormat.blueShift = 0; + ServerFormat.blueMax = 255; + + memset(&m_OSDBuffer, 0, sizeof(m_OSDBuffer)); + + numOSDColors = 0; + memset(&OSDColors, 0, sizeof(OSDColors)); + + m_notupdatedLeft = -1; + m_notupdatedTop = -1; + m_notupdatedRight = -1; + m_notupdatedBottom = -1; + memset(&m_lasttime, 0, sizeof(m_lasttime)); + memset(&m_lastupdate, 0, sizeof(m_lastupdate)); +} + +cOSDWorker::~cOSDWorker() { + if (m_Active) Stop(); + + if (m_pEncoder != NULL) + { + m_pEncoder->LogStats(); + delete m_pEncoder; + m_pEncoder = NULL; + } + + if (m_pSendBuffer != NULL) + delete [] m_pSendBuffer; +} + + + + + +void cOSDWorker::Init(int osdport, cPluginFFNetDev *pPlugin) { + if (m_Instance == NULL) + { + m_Instance = new cOSDWorker; + m_Instance->OSDPort = osdport; + m_Instance->Start(); + m_Instance->m_pPlugin = pPlugin; + } +} + + +void cOSDWorker::Exit(void) { + if (m_Instance != NULL) { + m_Instance->Stop(); + DELETENULL(m_Instance); + } +} + +void cOSDWorker::Stop(void) { + m_Active = false; + Cancel(3); +} + +void cOSDWorker::CloseOSDClient(void) { + if (m_Instance == NULL) + return; + + m_Instance->close_OSDclient_request = true; +#ifdef DEBUG + fprintf(stderr, "[ffnetdev] VNC: Closing of OSD client socket requested.\r\n"); +#endif +} + +bool cOSDWorker::SendPlayMode(ePlayMode PlayMode) { + + // not yet implemented + return true; +} + + +void cOSDWorker::CreateSendBuffer(int SendBufferSize) +{ + if (SendBufferSize != m_SendBufferSize) + { + if (m_pSendBuffer != NULL) + delete [] m_pSendBuffer; + + m_pSendBuffer = new BYTE[SendBufferSize]; + memset(m_pSendBuffer, 0, SendBufferSize); + + m_SendBufferSize = SendBufferSize; + } +} + +bool cOSDWorker::ClearScreen(void) +{ + if (m_Instance == NULL) + return false; + + // this should be improved; + // 1) maybe we should send a our very "special" pseudo encoding[CLEAR_SCREEN] to our "special" VNC client to get an empty screen really fast + bool bRetVal = false; + memset(&(m_Instance->m_OSDBuffer), 0, 720*576); + memset(&(m_Instance->m_lasttime), 0, sizeof(m_Instance->m_lasttime)); + bRetVal = SendScreen(720,0,0,720,576,&(m_Instance->m_OSDBuffer)); + + rfbBellMsg fu; + fu.type=rfbBell; + OSDWrite((unsigned char*)&fu, sz_rfbBellMsg); + + return bRetVal; +} + + +bool cOSDWorker::SendScreen(unsigned int stride, unsigned int x1, unsigned int y1, unsigned int w, unsigned int h, const void *data) +{ + if (m_Instance == NULL) + return false; + + rfbFramebufferUpdateMsg fu; + rfbFramebufferUpdateRectHeader furh; + + unsigned int x; + unsigned int y; + for (y=0; ym_OSDBuffer[(720*(y+y1))+x+x1] = *((unsigned char *)data+(y*stride)+x); + + x = x1; + y = y1; + x1 = (m_Instance->m_notupdatedLeft != -1 && m_Instance->m_notupdatedLeft < (int)x1) + ? m_Instance->m_notupdatedLeft : x1; + y1 = (m_Instance->m_notupdatedTop != -1 && m_Instance->m_notupdatedTop < (int)y1) + ? m_Instance->m_notupdatedTop : y1; + + w = (m_Instance->m_notupdatedRight != -1 && m_Instance->m_notupdatedRight > (int)(x1 + w)) + ? m_Instance->m_notupdatedRight - x1 : w + (x - x1); + h = (m_Instance->m_notupdatedBottom != -1 && m_Instance->m_notupdatedBottom > (int)(y1 + h)) + ? m_Instance->m_notupdatedBottom - y1 : h + (y - y1); + + struct timeval curtime; + gettimeofday(&curtime, 0); + curtime.tv_sec = curtime.tv_sec - (((int)curtime.tv_sec / 1000000) * 1000000); + if ((curtime.tv_sec * 1000 + (curtime.tv_usec / 1000) < m_Instance->m_lasttime.tv_sec * 1000 + (m_Instance->m_lasttime.tv_usec / 1000) + 100) || + (m_Instance->m_pEncoder == NULL)) + { + m_Instance->m_notupdatedLeft = (int)x1; + m_Instance->m_notupdatedTop = (int)y1; + m_Instance->m_notupdatedRight = (int)(x1 + w); + m_Instance->m_notupdatedBottom = (int)(y1 + h); + m_Instance->m_lasttime = curtime; + + return false; + } + else + { + m_Instance->m_notupdatedLeft = -1; + m_Instance->m_notupdatedTop = -1; + m_Instance->m_notupdatedRight = -1; + m_Instance->m_notupdatedBottom = -1; + m_Instance->m_lasttime = curtime; + m_Instance->m_lastupdate = curtime; + } + + if ((m_Instance->state==HANDSHAKE_OK) && (m_Instance->m_pEncoder != NULL)) { + RECT rect = {x1, y1, x1 + w, y1 + h}; + fu.type=rfbFramebufferUpdate; + fu.nRects=Swap16IfLE(1); + OSDWrite((unsigned char*)&fu, sz_rfbFramebufferUpdateMsg); + + int BufferSize = m_Instance->m_pEncoder->RequiredBuffSize(w, h); + m_Instance->CreateSendBuffer(BufferSize); + BufferSize = m_Instance->m_pEncoder->EncodeRect( &(m_Instance->m_OSDBuffer[0]), m_Instance->m_pSendBuffer, rect); +#ifdef DEBUG + fprintf(stderr, "[ffnetdev] VNC: Send OSD Data %d Bytes\n", BufferSize); +#endif + dsyslog("[ffnetdev] VNC: Send OSD Data %d Bytes\n", BufferSize); + OSDWrite((unsigned char*)m_Instance->m_pSendBuffer, BufferSize); + + return true; + } + else { + return false; + } +} + + +bool cOSDWorker::SendCMAP(int NumColors, const tColor *Colors) +{ + if (m_Instance == NULL) + return false; + + rfbSetColourMapEntriesMsg scme; + CARD16 red; + CARD16 green; + CARD16 blue; + CARD16 alpha; + + int i; + + if ((m_Instance->state==HANDSHAKE_OK) && !(m_Instance->ClientFormat.trueColour)) { + scme.type=rfbSetColourMapEntries; + scme.firstColour = Swap16IfLE(0); + scme.nColours = Swap16IfLE((CARD16)NumColors); + + OSDWrite((unsigned char*)&scme, sz_rfbSetColourMapEntriesMsg); + + for(i=0; i> 16); + green = ((Colors[i]&0x0000FF00) >> 8); + blue = ((Colors[i]&0x000000FF) ); + + if (m_Instance->UseAlpha) { + alpha = ((Colors[i]&0xFF000000) >> 24); + OSDWrite( (unsigned char*) &alpha, 2); + } + + OSDWrite( (unsigned char*) &red, 2); + OSDWrite( (unsigned char*) &green, 2); + OSDWrite( (unsigned char*) &blue, 2); + + m_Instance->OSDColors[i] = Colors[i]; + } + m_Instance->numOSDColors = NumColors; + if (m_Instance->m_pEncoder != NULL) + m_Instance->m_pEncoder->SetLocalFormat(m_Instance->ServerFormat, 720, 576); + + return true; + } + else { + for (i=0; iOSDColors[i] = Colors[i]; + m_Instance->numOSDColors = NumColors; + if (m_Instance->m_pEncoder != NULL) + m_Instance->m_pEncoder->SetLocalFormat(m_Instance->ServerFormat, 720, 576); + + return false; + } + + +} + +bool cOSDWorker::OSDWrite(unsigned char *data, unsigned int data_length) +{ + if (m_Instance == NULL) + return false; + + if (m_Instance->m_OSDClient!=NULL) { + if (m_Instance->m_OSDClient->IsOpen()) + { + //fprintf(stderr, "[ffnetdev] VNC: Trying to send...\n"); + if (!m_Instance->m_OSDClient->SafeWrite(data, data_length)) + { + CloseOSDClient(); + return false; + } + } //else fprintf(stderr, "[ffnetdev] VNC: Cannot send...\n"); + //for (unsigned int i=0; iRead(buffer, len)==0 ) { +#ifdef DEBUG + fprintf(stderr, "[ffnetdev] VNC: Client closed connection.\n"); +#endif + isyslog("[ffnetdev] VNC: Connection closed: client %s:%d", + m_OSDClient->RemoteIp().c_str(), m_OSDClient->RemotePort()); + state = NO_CLIENT; + m_Instance->m_OSDClient->Close(); + delete m_pEncoder; + m_pEncoder = NULL; + + return false; + } + else + return true; +} + +void cOSDWorker::HandleClientRequests(cTBSelect *select) +{ + if ( select->CanRead(*m_OSDClient) ) { + rfbClientToServerMsg msg; + + if (!RFBRead((char*)&msg, 1)) + return; + + switch (msg.type) { + + case rfbSetPixelFormat: if (!RFBRead( ((char*)&msg.spf)+1, sz_rfbSetPixelFormatMsg-1)) + return; + dsyslog("[ffnetdev] VNC: SetPixelFormat\n"); + dsyslog("[ffnetdev] VNC: ->bitsPerPixel: %d\n",msg.spf.format.bitsPerPixel); + dsyslog("[ffnetdev] VNC: ->depth: %d\n",msg.spf.format.depth); + dsyslog("[ffnetdev] VNC: ->bigEndian: %d\n",msg.spf.format.bigEndian); + dsyslog("[ffnetdev] VNC: ->trueColour: %d\n",msg.spf.format.trueColour); + dsyslog("[ffnetdev] VNC: ->redMax: %d\n",Swap16IfLE(msg.spf.format.redMax)); + dsyslog("[ffnetdev] VNC: ->greenMax: %d\n",Swap16IfLE(msg.spf.format.greenMax)); + dsyslog("[ffnetdev] VNC: ->blueMax: %d\n",Swap16IfLE(msg.spf.format.blueMax)); + dsyslog("[ffnetdev] VNC: ->redShift: %d\n",msg.spf.format.redShift); + dsyslog("[ffnetdev] VNC: ->greenShift: %d\n",msg.spf.format.greenShift); + dsyslog("[ffnetdev] VNC: ->blueShift: %d\n",msg.spf.format.blueShift); + + if ( (msg.spf.format.bitsPerPixel!=8 ) + &&(msg.spf.format.bitsPerPixel!=16) + &&(msg.spf.format.bitsPerPixel!=32)) { + esyslog("[ffnetdev] SORRY!!! VNC client requested unsupported pixel format! Closing connection...\n"); + CloseOSDClient(); + return; + } + else + ClientFormat = msg.spf.format; + dsyslog("[ffnetdev] VNC: RGB %d %d %d %d %d %d\n", + ClientFormat.redShift, ClientFormat.redMax, ClientFormat.greenShift, ClientFormat.greenMax, ClientFormat.blueShift, ClientFormat.blueMax); + break; + case rfbFixColourMapEntries: if (!RFBRead( ((char*)&msg.fcme)+1, sz_rfbFixColourMapEntriesMsg-1)) + return; + isyslog("[ffnetdev] VNC: FixColourMapEntries (ignored).\n"); + break; + case rfbSetEncodings: if (!RFBRead( ((char*)&msg.se)+1, sz_rfbSetEncodingsMsg-1)) + return; + + dsyslog("[ffnetdev] VNC: SetEncodings: Client offers %d encodings:\n", Swap16IfLE(msg.se.nEncodings)); + + CARD32 encodings[16]; + if (!RFBRead( ((char*)&encodings), Swap16IfLE(msg.se.nEncodings)*sizeof(CARD32) )) + return; + + for (int i=0;iRAW encoding.\n"); + m_pEncoder = new vncEncoder(); + } + break; + case rfbEncodingCopyRect: + isyslog("[ffnetdev] VNC: ->CopyRect encoding(not supported).\n"); + break; + case rfbEncodingRRE: + if (m_pEncoder == NULL) + { + dsyslog("[ffnetdev] VNC: ->RRE encoding.\n"); + m_pEncoder = new vncEncodeRRE(); + } + break; + case rfbEncodingCoRRE: + if (m_pEncoder == NULL) + { + isyslog("[ffnetdev] VNC: ->CoRRE encoding(not supported).\n"); + m_pEncoder = new vncEncodeCoRRE(); + } + break; + case rfbEncodingHextile: + if (m_pEncoder == NULL) + { + isyslog("[ffnetdev] VNC: ->Hextile encoding(not supported).\n"); + m_pEncoder = new vncEncodeHexT(); + } + break; + case rfbEncodingZlib: + if (m_pEncoder == NULL) + { + isyslog("[ffnetdev] VNC: ->Zlib encoding(not supported).\n"); + //m_pEncoder = new vncEncodeZlib(); + } + break; + case rfbEncodingTight: + if (m_pEncoder == NULL) + { + isyslog("[ffnetdev] VNC: ->Tight encoding(not supported).\n"); + //m_pEncoder = new vncEncodeTight(); + } + break; + case rfbEncodingZlibHex: + if (m_pEncoder == NULL) + { + isyslog("[ffnetdev] VNC: ->ZlibHex encoding(not supported).\n"); + //m_pEncoder = new vncEncodeZlibHex(); + } + break; + case rfbEncodingZRLE: + if (m_pEncoder == NULL) + { + isyslog("[ffnetdev] VNC: ->ZRLE encoding(not supported).\n"); + } + break; + case rfbEncSpecialUseAlpha: + if (m_pEncoder == NULL) + { + isyslog("[ffnetdev] VNC: ->Special FFnetDev Encoding: client wants alpha channel.\n"); + } + UseAlpha = true; + break; + default: + esyslog("[ffnetdev] VNC: ->Unknown encoding or unknown pseudo encoding.\n"); + } + + if (m_pEncoder != NULL) + { + m_pEncoder->Init(); + m_pEncoder->SetLocalFormat(ServerFormat, 720, 576); + m_pEncoder->SetCompressLevel(6); + m_pEncoder->SetQualityLevel(9); + m_pEncoder->SetRemoteFormat(ClientFormat); + } + } + break; + case rfbFramebufferUpdateRequest: + if (!RFBRead( ((char*)&msg.fur)+1, sz_rfbFramebufferUpdateRequestMsg-1)) + return; +#ifdef DEBUG + fprintf(stderr, "[ffnetdev] VNC: FramebufferUpdateRequest: Client wants %s:\n", + msg.fur.incremental? "incremental update":"complete update"); + fprintf(stderr, "[ffnetdev] VNC: x: %d, y: %d, w: %d, h: %d\n", + Swap16IfLE(msg.fur.x), + Swap16IfLE(msg.fur.y), + Swap16IfLE(msg.fur.w), + Swap16IfLE(msg.fur.h) + ); +#endif + dsyslog("[ffnetdev] VNC: FramebufferUpdateRequest: Client wants %s:\n", + msg.fur.incremental? "incremental update":"complete update"); + dsyslog("[ffnetdev] VNC: x: %d, y: %d, w: %d, h: %d\n", + Swap16IfLE(msg.fur.x), + Swap16IfLE(msg.fur.y), + Swap16IfLE(msg.fur.w), + Swap16IfLE(msg.fur.h) + ); + if (FirstUpdateRequest) { + ClearScreen(); + FirstUpdateRequest = false; + } + else + { + SendCMAP(numOSDColors, OSDColors); + SendScreen(720, //stride + Swap16IfLE(msg.fur.x), Swap16IfLE(msg.fur.y), + Swap16IfLE(msg.fur.w), Swap16IfLE(msg.fur.h), + &m_OSDBuffer); + } + break; + case rfbKeyEvent: if (!RFBRead( ((char*)&msg.ke)+1, sz_rfbKeyEventMsg-1)) + return; +#ifdef DEBUG + fprintf(stderr, "[ffnetdev] VNC: KeyEvent\n"); +#endif + dsyslog("[ffnetdev] VNC: KeyEvent\n"); + cMyRemote *pRemote; + if ((pRemote = m_pPlugin->GetRemote()) != NULL) { + if (msg.ke.down) + pRemote->Put(Swap32IfLE(msg.ke.key), false, false); + else + pRemote->Put(Swap32IfLE(msg.ke.key), false, true); + } +#ifdef DEBUG + fprintf(stderr, "[ffnetdev] VNC: Remote: %04X\n", msg.ke.key); +#endif + dsyslog("[ffnetdev] VNC: Remote: %04X\n", msg.ke.key); + break; + case rfbPointerEvent: if (!RFBRead( ((char*)&msg.pe)+1, sz_rfbPointerEventMsg-1)) + return; + //fprintf(stderr, "[ffnetdev] VNC: PointerEvent\n"); + break; + case rfbClientCutText: if (!RFBRead( ((char*)&msg.cct)+1, sz_rfbClientCutTextMsg-1)) + return; +#ifdef DEBUG + fprintf(stderr, "[ffnetdev] VNC: ClientCutText\n"); +#endif + dsyslog("[ffnetdev] VNC: ClientCutText\n"); + break; + default: +#ifdef DEBUG + fprintf(stderr, "[ffnetdev] VNC: Unknown RFB message.\n"); +#endif + dsyslog("[ffnetdev] VNC: Unknown RFB message.\n"); + } + } +} + +void cOSDWorker::Action(void) { + cTBSelect select; + cTBSocket m_OSDListen; + + const char* m_ListenIp = "0.0.0.0"; + uint m_OSDListenPort = OSDPort; + + m_OSDClient = new cTBSocket; + + m_Active = true; + + if (!m_OSDListen.Listen(m_ListenIp, m_OSDListenPort, 1)) { + esyslog("[ffnetdev] VNC: Couldn't listen %s:%d: %s", m_ListenIp, m_OSDListenPort, strerror(errno)); + m_Active = false; + } + else + isyslog("[ffnetdev] VNC: Listening on port %d", m_OSDListenPort); + + + while (m_Active) { + select.Clear(); + + if (state==NO_CLIENT) + select.Add(m_OSDListen, false); + else + select.Add(*m_OSDClient, false); + + int numfd; + /* React on status change of any of the above file descriptor */ + if ((numfd=select.Select(1000)) < 0) { + if (!m_Active) // Exit was requested while polling + continue; + esyslog("FFNetDev: Fatal error, FFNetDev exiting: %s", strerror(errno)); + m_Active = false; + continue; + } + + //DEBUG + /* + fprintf(stderr, "Num_FD OSD: %d\n", numfd); + + if (select.CanRead(m_OSDListen) || select.CanWrite(m_OSDListen)) + fprintf (stderr, "m_OSDListen can act.\n"); + if (select.CanRead(*m_OSDClient) || select.CanWrite(*m_OSDClient)) + fprintf (stderr, "m_OSDClient can act.\n"); + */ + //fprintf(stderr, "State: %d\n",state); + + int res; + switch (state) { + + case NO_CLIENT: /* Accept connecting OSD clients */ + if (select.CanRead(m_OSDListen) ) { + if (m_OSDClient->Accept(m_OSDListen)) { + isyslog("[ffnetdev] VNC: Accepted client %s:%d", + m_OSDClient->RemoteIp().c_str(), m_OSDClient->RemotePort()); + state = CLIENT_CONNECTED; + /* Sending RFB protocol version */ + rfbProtocolVersionMsg msg; + sprintf(msg, rfbProtocolVersionFormat, rfbProtocolMajorVersion, rfbProtocolMinorVersion); + if (!m_OSDClient->SafeWrite(&msg, sz_rfbProtocolVersionMsg)) + { + state = NO_CLIENT; + } + else + { +// m_pPlugin->SetPrimaryDevice(); + } + break; + + } + else { + esyslog("[ffnetdev] VNC: Couldn't accept : %s", strerror(errno)); + state = NO_CLIENT; + m_Active = false; + continue; + } + } + break; + + case CLIENT_CONNECTED: + rfbProtocolVersionMsg pvmsg; + res = m_OSDClient->Read(&pvmsg, sz_rfbProtocolVersionMsg); +#ifdef DEBUG + fprintf(stderr, "[ffnetdev] VNC: Client wants RFB protocol version %s\n", pvmsg); +#endif + dsyslog("[ffnetdev] VNC: Client wants RFB protocol version %s\n", pvmsg); + if (1) {//FIXME:protocol ok + state = PROTOCOL_OK; + /* Sending security type */ + CARD32 secmsg; + secmsg = Swap32IfLE(rfbNoAuth); + if (!m_OSDClient->SafeWrite(&secmsg, sizeof(secmsg))) + { + state = NO_CLIENT; + break; + } + } + else { + state = NO_CLIENT; +#ifdef DEBUG + fprintf(stderr, "[ffnetdev] VNC: Client wants unsupported protocol version.\n"); +#endif + dsyslog("[ffnetdev] VNC: Client wants unsupported protocol version.\n"); + if ( m_OSDClient->Close() ) { +#ifdef DEBUG + fprintf(stderr, "[ffnetdev] VNC: Client socket closed successfully.\n"); +#endif + isyslog("[ffnetdev] VNC: Connection closed."); + } + else { +#ifdef DEBUG + fprintf(stderr, "[ffnetdev] VNC: Error closing client socket.\n"); +#endif + esyslog("[ffnetdev] VNC: Error closing connection."); + m_Active=false; + continue; + } + } + break; + case PROTOCOL_OK: /* Since we do not need authentication challenge(rfbNoAuth), proceed to next state */ + state = AUTHENTICATED; + break; + case AUTHENTICATED: + rfbClientInitMsg cimsg; + res = m_OSDClient->Read(&cimsg, sz_rfbClientInitMsg); +#ifdef DEBUG + fprintf(stderr, "[ffnetdev] VNC: Client wants %s desktop(ignored).\n", + cimsg.shared ? "shared":"non-shared"); +#endif + dsyslog("[ffnetdev] VNC: Client wants %s desktop(ignored).\n", + cimsg.shared ? "shared":"non-shared"); + + rfbPixelFormat pxfmt; + pxfmt.bitsPerPixel = 8; + pxfmt.depth = 8; + pxfmt.bigEndian = 0; + pxfmt.trueColour = 0; + pxfmt.redMax = 0; + pxfmt.greenMax = 0; + pxfmt.blueMax = 0; + pxfmt.redShift = 0; + pxfmt.greenShift = 0; + pxfmt.blueShift = 0; + pxfmt.pad1 = 0; + pxfmt.pad2 = 0; + + rfbServerInitMsg simsg; + simsg.framebufferWidth = Swap16IfLE(720); + simsg.framebufferHeight = Swap16IfLE(576); + simsg.format = pxfmt; + simsg.nameLength = Swap32IfLE(7); + if (!m_OSDClient->SafeWrite(&simsg, sizeof(simsg))) + { + state = NO_CLIENT; + break; + } + + CARD8 name[7]; + strcpy((char*)&name, "VDR-OSD"); + if (!m_OSDClient->SafeWrite(&name, 7)) + { + state = NO_CLIENT; + break; + } + + state = HANDSHAKE_OK; + FirstUpdateRequest = true; + break; + + case HANDSHAKE_OK: + + /* Check for closed OSD connection */ + if (close_OSDclient_request==true) { + close_OSDclient_request = false; + state = NO_CLIENT; + UseAlpha = false; + + delete m_pEncoder; + m_pEncoder = NULL; + + m_pPlugin->RestorePrimaryDevice(); + + if ( m_OSDClient->Close() ) { +#ifdef DEBUG + fprintf(stderr, "[ffnetdev] VNC: Client socket closed successfully.\n"); +#endif + isyslog("[ffnetdev] VNC: Connection closed."); + } + else { +#ifdef DEBUG + fprintf(stderr, "[ffnetdev] VNC: Error closing client socket.\n"); +#endif + esyslog("[ffnetdev] VNC: Error closing connection."); + m_Active=false; + continue; + } + } + HandleClientRequests(&select); + + break; + default: fprintf(stderr, "[ffnetdev] VNC: Undefined state! This is a bug! Please report.\n"); + esyslog("[ffnetdev] VNC: Fatal error, FFNetDev exiting: undefined state"); + m_Active = false; + continue; + } + + + + struct timeval curtime; + gettimeofday(&curtime, 0); + curtime.tv_sec = curtime.tv_sec - (((int)curtime.tv_sec / 1000000) * 1000000); + if ((curtime.tv_sec * 1000 + (curtime.tv_usec / 1000) > m_lastupdate.tv_sec * 1000 + (m_lastupdate.tv_usec / 1000) + 500) && + (m_notupdatedLeft != -1) && (m_notupdatedTop != -1) && (m_notupdatedRight != -1) && + (m_notupdatedBottom != -1)) + { + memset(&m_lasttime, 0, sizeof(m_lasttime)); + SendScreen(720, m_notupdatedLeft, m_notupdatedTop, + m_notupdatedRight - m_notupdatedLeft, + m_notupdatedBottom - m_notupdatedTop, + &(m_Instance->m_OSDBuffer[m_notupdatedTop * 720 + m_notupdatedLeft])); + } + } // while(m_Active) + +} diff --git a/osdworker.h b/osdworker.h new file mode 100644 index 0000000..296c8b3 --- /dev/null +++ b/osdworker.h @@ -0,0 +1,122 @@ +/* + * osdworker.h: OSD worker thread + * + * See the README file for copyright information and how to reach the author. + * + */ + +#ifndef _OSDWORKER__H +#define _OSDWORKER__H + +#include + +#include "tools/socket.h" +#include "tools/select.h" +#include "streamdevice.h" +#include "remote.h" +#include "netosd.h" +#include "ffnetdev.h" + +#include "vncEncoder.h" + +#define MAXOSDMEMORY 2048000 + +#define rfbEncSpecialUseAlpha 0xFFFFF000 // server shall send alpha values in addition to RGB values + +/* + * Macros for endian swapping. + */ +/* +#define Swap16(s) ((((s) & 0xff) << 8) | (((s) >> 8) & 0xff)) + +#define Swap32(l) (((l) >> 24) | \ + (((l) & 0x00ff0000) >> 8) | \ + (((l) & 0x0000ff00) << 8) | \ + ((l) << 24)) + +static const int rfbEndianTest = 1; // true means swap TODO: check machine endianess - for now -> hardcoded + +#define Swap16IfLE(s) (*(const char *)&rfbEndianTest ? Swap16(s) : (s)) + +#define Swap32IfLE(l) (*(const char *)&rfbEndianTest ? Swap32(l) : (l)) +*/ + +// --- cOSDWorker ------------------------------------------------------------- +enum states { + NO_CLIENT, + CLIENT_CONNECTED, + PROTOCOL_OK, + AUTHENTICATED, + HANDSHAKE_OK +}; + +class cOSDWorker : public cThread { +private: + bool m_Active; + + static cOSDWorker *m_Instance; + + cTBSocket *m_OSDClient; + + bool close_OSDclient_request; + + int OSDPort; + int state; + bool UseAlpha; + bool FirstUpdateRequest; + int numOSDColors; + rfbPixelFormat ClientFormat; + rfbPixelFormat ServerFormat; + + BYTE m_OSDBuffer[720*576]; +// BYTE m_oldOSDData[720*576]; + BYTE *m_pSendBuffer; + int m_SendBufferSize; + tColor OSDColors[256]; + + void HandleClientRequests(cTBSelect *select); + bool RFBRead(char *buffer, int len); + void CreateSendBuffer(int OSDBufferSize); + + vncEncoder *m_pEncoder; + + struct timeval m_lasttime; + struct timeval m_lastupdate; + int m_notupdatedLeft; + int m_notupdatedTop; + int m_notupdatedRight; + int m_notupdatedBottom; + + cPluginFFNetDev *m_pPlugin; + +protected: + virtual void Action(void); + void Stop(void); +public: + cOSDWorker(void); + virtual ~cOSDWorker(); + + static void Init(int, cPluginFFNetDev*); + static void Exit(void); + static bool Active(void); + static bool ClientIsReady(void); + + static void CloseOSDClient(void); + + static bool ClearScreen(void); + static bool SendScreen(unsigned int stride, unsigned int x1, unsigned int y1, unsigned int w, unsigned int h, const void *data); + static bool SendCMAP(int NumColors, const tColor *Colors); + static bool OSDWrite(unsigned char *data, unsigned int data_length); + static bool SendPlayMode(ePlayMode PlayMode); + static void GetOSDColors(tColor **OSDColors, int *numOSDColors) { *OSDColors = &m_Instance->OSDColors[0]; (*numOSDColors) = m_Instance->numOSDColors; }; +}; + +inline bool cOSDWorker::Active(void) { + return m_Instance && (m_Instance->state!=NO_CLIENT); +} + +inline bool cOSDWorker::ClientIsReady(void) { + return m_Instance && (m_Instance->state==HANDSHAKE_OK); +} + +#endif diff --git a/pes2ts.c b/pes2ts.c new file mode 100644 index 0000000..506abd9 --- /dev/null +++ b/pes2ts.c @@ -0,0 +1,210 @@ +/* + * pes2ts.c: PES2TS remux + * + * See the README file for copyright information and how to reach the author. + * + */ +#include +#include +#include +#include + +#include "tsworker.h" + +#include "pes2ts.h" + +cPES2TSRemux::cPES2TSRemux(int VPid, int APid): + cThread("[ffnetdev] PES2TS remux"), + m_OutputBuffer(new cRingBufferLinear(OUTPUTBUFSIZE, TS_SIZE * 2)), + m_InputBuffer(new cRingBufferLinear(INPUTBUFSIZE, IPACKS)), + m_Active(false), + m_PlayModeChanged(false) +{ + vpid = VPid; + apid = APid; + m_InputBuffer->SetTimeouts(0, 1000); // IMPORTANT to avoid busy wait in threads main loop and thus a high CPU load + Start(); + OutputLocked = false; +} + +cPES2TSRemux::~cPES2TSRemux() +{ + m_Active = false; + delete m_InputBuffer; + delete m_OutputBuffer; + +} + +void cPES2TSRemux::Action(void) +{ + unsigned int i; + uchar acc=0; // continutiy counter for audio packets + uchar vcc=0; // continutiy counter for video packets + uchar *cc; // either cc=&vcc; or cc=&acc; + unsigned short pid=0; + unsigned int packetlen; + uchar ts[188]; + uchar pes[IPACKS]; + + unsigned int minNeededPacketlen = 10; // needed for read packet len: 6 Should be enought ... but makes no sense + + + m_Active = true; + while (m_Active) { + int count=0; +// fprintf(stderr, "[ffnetdev] Remuxer: Inputbuffersize: %d, Outputbuffersize: %d\n", +// m_InputBuffer->Available(), m_OutputBuffer->Available()); + + if (m_PlayModeChanged) + { + cCondWait::SleepMs(1500); + m_PlayModeChanged = false; + } + + if (m_InputBuffer->Available() < (int)IPACKS*10) { + cCondWait::SleepMs(5); + continue; + } + + if (!cTSWorker::HaveStreamClient()) { + ClearOutput(); + cCondWait::SleepMs(10); + continue; + } + + + InputMutex.Lock(); + uchar *data = m_InputBuffer->Get(count); + if (data==NULL) { + InputMutex.Unlock(); + cCondWait::SleepMs(3); + continue; + } + +// fprintf(stderr, "[ffnetdev] count: %07d Free: %07d AvailO %07d AvailI: %07d\n", count, m_InputBuffer->Free(), +// m_OutputBuffer->Available(), m_InputBuffer->Available()); + + if ( count < (int)minNeededPacketlen ) { + fprintf(stderr, "[ffnetdev] Remuxer: not enought bytes for PacketLen-Analysis, have only: %d\n", count); + InputMutex.Unlock(); + cCondWait::SleepMs(2); + continue; + } + + //DEBUG + //fprintf(stderr, "Data ready to read: %d\n", count); + //for (i=0; i<20; i++) + // fprintf(stderr, "%02X ", data[i]); + // END DEBUG + + + // check for valid PES signature in PES header + if ( (data[0]==0x00) && (data[1]==0x00) && (data[2]==0x01) ) { + + packetlen = ((data[4]<<8) | data[5]) + 6 ; + + if ( packetlen>IPACKS) { + fprintf(stderr, "[ffnetdev] Remuxer: IPACKS changed? packet length was %d, maximum: %d\n" + "This should not happen! Please report!\n", packetlen, IPACKS); + } + + if ( count < (int)packetlen) { + fprintf(stderr, "[ffnetdev] Remuxer: not enought bytes for whole packet, have only: %d but LenShoud be %d\n", count, packetlen); + InputMutex.Unlock(); + cCondWait::SleepMs(1); + continue; + } + + + // check for valid stream id type: is it video or audio or unknown? + if ( (data[3]>=0xC0) && (data[3]<=0xDF) ) { + pid=apid; + cc=&acc; + } + else { + if ( (data[3]>=0xE0) && (data[3]<=0xEF) ) { + pid=vpid; + cc=&vcc; + } + else { + fprintf(stderr, "[ffnetdev] Remuxer: Unknown stream id: neither video nor audio type.\n"); + // throw away whole PES packet + m_InputBuffer->Del(packetlen); + InputMutex.Unlock(); + continue; + } + } + + memcpy( pes, data, packetlen); + // we are now finished with the PES packet, delete it from ring buffer + m_InputBuffer->Del(packetlen); + + InputMutex.Unlock(); + } + else { + // no valid PES signature was found, so delete this stuff from ring buffer + // normally we should always receive a whole PES packet, since VDR always gives us a whole packet and not less or more + // with each call in streamdevice.c (PlayVideo, PlayAudio) + fprintf(stderr, "[ffnetdev] Remuxer: No valid PES signature found. This should not happen.\n"); + + m_InputBuffer->Del(1); // Probably it is better to delete 1 byte only to get in sync again!? + InputMutex.Unlock(); + continue; + } + + int tspacketlen = ((int)packetlen/184) * 188 + ((packetlen % 184 > 0) ? 188 : 0); + while (m_OutputBuffer->Free() < tspacketlen) { + if (!m_Active) + continue; + cCondWait::SleepMs(10); + //fprintf(stderr, "[ffnetdev] Remuxer: sleep %d %d\n", m_OutputBuffer->Free(), tspacketlen); + } + + LockOutput(); + bool first = true; + //--------------------------------------divide PES packet into small TS packets----------------------- + for (i=0; i< packetlen/184; i++) { + ts[0] = 0x47; //SYNC Byte + if (first) ts[1] = 0x40; // Set PUSI or + else ts[1] = 0x00; // clear PUSI, TODO: PID (high) is missing + ts[2] = pid & 0xFF; // PID (low) + ts[3] = 0x10 | ((*cc)&0x0F); // No adaptation field, payload only, continuity counter + memcpy(ts + 4, pes + i * 184, 184); + ++(*cc); + m_OutputBuffer->Put(ts, 188); + first = false; + } + uchar rest = packetlen % 184; + if (rest>0) { + ts[0] = 0x47; //SYNC Byte + if (first) ts[1] = 0x40; // Set PUSI or + else ts[1] = 0x00; // clear PUSI, TODO: PID (high) is missing + ts[2] = pid & 0xFF; // PID (low) + ts[3] = 0x30 | ((*cc)&0x0F); // adaptation field, payload, continuity counter + ++(*cc); + ts[4] = 183-rest; + if (ts[4]>0) { + ts[5] = 0x00; + memset(ts + 6, 0xFF, ts[4] - 1); + } + memcpy(ts + 5 + ts[4], pes + i * 184, rest); + m_OutputBuffer->Put(ts, 188); + first = false; + } + + UnlockOutput(); + + } + m_Active = false; +} + + +int cPES2TSRemux::Put(const uchar *Data, int Count) +{ + InputMutex.Lock(); + int result = m_InputBuffer->Put(Data, Count); + InputMutex.Unlock(); + return ( result); +} + + diff --git a/pes2ts.h b/pes2ts.h new file mode 100644 index 0000000..43fa3a1 --- /dev/null +++ b/pes2ts.h @@ -0,0 +1,50 @@ +/* + * pes2ts.h: PES2TS remux + * + * See the README file for copyright information and how to reach the author. + * + */ + +#ifndef PES2TSREMUX_H +#define PES2TSREMUX_H + +#include +#include + +#define INPUTBUFSIZE KILOBYTE(2048) +#define OUTPUTBUFSIZE KILOBYTE(2048) +#define TS_SIZE 188 +#define IPACKS 2048 + +class cPES2TSRemux: public cThread { +private: + cRingBufferLinear *m_OutputBuffer; + cRingBufferLinear *m_InputBuffer; + bool m_Active; + unsigned short vpid; + unsigned short apid; + bool OutputLocked; + cMutex InputMutex; + bool m_PlayModeChanged; + +protected: + virtual void Action(void); + +public: + cPES2TSRemux(int VPid, int APid); + virtual ~cPES2TSRemux(); + + int Free(void) { return m_InputBuffer->Free(); } + int Available(void) { return m_OutputBuffer->Available(); } + int Put(const uchar *Data, int Count); + uchar *Get(int &Count) { return m_OutputBuffer->Get(Count); } + void DelOutput(int Count) { m_OutputBuffer->Del(Count); } + void DelInput (int Count) { InputMutex.Lock(); m_InputBuffer ->Del(Count); InputMutex.Unlock(); } + void ClearOutput() { LockOutput(); m_OutputBuffer->Clear(); UnlockOutput(); } + void ClearInput () { InputMutex.Lock(); m_InputBuffer ->Clear(); InputMutex.Unlock(); } + void LockOutput() { while (OutputLocked) cCondWait::SleepMs(1); OutputLocked = true; } + void UnlockOutput() { OutputLocked = false; } + void PlayModeChange() { m_PlayModeChanged = true; } +}; + +#endif // PES2TSREMUX_H diff --git a/remote.c b/remote.c new file mode 100644 index 0000000..e1f392d --- /dev/null +++ b/remote.c @@ -0,0 +1,58 @@ +/* + * remote.c: remote control + * + * See the README file for copyright information and how to reach the author. + * + */ + + +#include + +#include "osdworker.h" +#include "remote.h" + +cMyRemote::cMyRemote(const char *Name) +:cRemote(Name) +{ + +} + +bool cMyRemote::Ready(void) +{ + return true; +} + +bool cMyRemote::Initialize(void) +{ + return true; +} + +bool cMyRemote::Put(uint64 Code, bool Repeat, bool Release) +{ + return cRemote::Put(Code, Repeat ,Release); +} + + + +cLearningThread::cLearningThread(void) +{ + Start(); +} + +cLearningThread::~cLearningThread(void) +{ +} + +void cLearningThread::Action(void) +{ + while (!cOSDWorker::ClientIsReady()) + usleep(100000); + + while (cOsd::IsOpen() > 0) + usleep(100000); + + sleep(5); + dsyslog("[ffnetdev] start learning keys"); + Interface->LearnKeys(); + delete this; +} diff --git a/remote.h b/remote.h new file mode 100644 index 0000000..583693d --- /dev/null +++ b/remote.h @@ -0,0 +1,36 @@ +/* + * remote.h: remote control + * + * See the README file for copyright information and how to reach the author. + * + */ + +#ifndef REMOTE_H +#define REMOTE_H + +#include +#include + +class cMyRemote : public cRemote { +private: +public: + cMyRemote(const char *Name); + virtual bool Initialize(void); + virtual bool Ready(void); + virtual bool Put(uint64 Code, bool Repeat = false, bool Release = false); +}; + + +class cLearningThread : public cThread { +private: + +public: + cLearningThread(void); + virtual ~cLearningThread(void); + +protected: + virtual void Action(void); +}; + +#endif + diff --git a/rfb.h b/rfb.h new file mode 100644 index 0000000..b5194ed --- /dev/null +++ b/rfb.h @@ -0,0 +1,85 @@ +// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. +// +// This file is part of the VNC system. +// +// The VNC system 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 program 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 program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +// USA. +// +// TightVNC distribution homepage on the Web: http://www.tightvnc.com/ +// +// If the source code for the VNC system is not available from the place +// whence you received this file, check http://www.uk.research.att.com/vnc or contact +// the authors on vnc@uk.research.att.com for information on obtaining it. + + +// rfb.h +// This includes the rfb spec header, the port numbers, +// the CARD type definitions and various useful macros. +// + +#ifndef RFB_H__ +#define RFB_H__ + +// Define the CARD* types +typedef unsigned long CARD32; +typedef unsigned short CARD16; +typedef unsigned char CARD8; + +// Define the port number offsets +#define FLASH_PORT_OFFSET 5400 +#define INCOMING_PORT_OFFSET 5500 +#define HTTP_PORT_OFFSET 5800 +#define RFB_PORT_OFFSET 5900 + +#define PORT_TO_DISPLAY(p) ( (p) - RFB_PORT_OFFSET ) +#define HPORT_TO_DISPLAY(p) ( (p) - HTTP_PORT_OFFSET ) +#define DISPLAY_TO_PORT(d) ( (d) + RFB_PORT_OFFSET ) +#define DISPLAY_TO_HPORT(d) ( (d) + HTTP_PORT_OFFSET ) + +// include the protocol spec +#include "rfbproto.h" + +// define some quick endian conversions +// change this if necessary +#define LITTLE_ENDIAN_HOST + +#ifdef LITTLE_ENDIAN_HOST + +#define Swap16IfLE(s) \ + ((CARD16) ((((s) & 0xff) << 8) | (((s) >> 8) & 0xff))) +#define Swap32IfLE(l) \ + ((CARD32) ((((l) & 0xff000000) >> 24) | \ + (((l) & 0x00ff0000) >> 8) | \ + (((l) & 0x0000ff00) << 8) | \ + (((l) & 0x000000ff) << 24))) + +#else + +#define Swap16IfLE(s) (s) +#define Swap32IfLE(l) (l) + +#endif + +// unconditional swaps +#define Swap16(s) \ + ((CARD16) ((((s) & 0xff) << 8) | (((s) >> 8) & 0xff))) +#define Swap32(l) \ + ((CARD32) ((((l) & 0xff000000) >> 24) | \ + (((l) & 0x00ff0000) >> 8) | \ + (((l) & 0x0000ff00) << 8) | \ + (((l) & 0x000000ff) << 24))) + + +#endif diff --git a/rfbproto.h b/rfbproto.h new file mode 100644 index 0000000..85118ad --- /dev/null +++ b/rfbproto.h @@ -0,0 +1,786 @@ +/* + * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved. + * Copyright (C) 2000 Tridia Corporation. All Rights Reserved. + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * 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. + */ + +/* + * rfbproto.h - header file for the RFB protocol version 3.3 + * + * Uses types CARD for an n-bit unsigned integer, INT for an n-bit signed + * integer (for n = 8, 16 and 32). + * + * All multiple byte integers are in big endian (network) order (most + * significant byte first). Unless noted otherwise there is no special + * alignment of protocol structures. + * + * + * Once the initial handshaking is done, all messages start with a type byte, + * (usually) followed by message-specific data. The order of definitions in + * this file is as follows: + * + * (1) Structures used in several types of message. + * (2) Structures used in the initial handshaking. + * (3) Message types. + * (4) Encoding types. + * (5) For each message type, the form of the data following the type byte. + * Sometimes this is defined by a single structure but the more complex + * messages have to be explained by comments. + */ + + +/***************************************************************************** + * + * Structures used in several messages + * + *****************************************************************************/ + +/*----------------------------------------------------------------------------- + * Structure used to specify a rectangle. This structure is a multiple of 4 + * bytes so that it can be interspersed with 32-bit pixel data without + * affecting alignment. + */ + +typedef struct { + CARD16 x; + CARD16 y; + CARD16 w; + CARD16 h; +} rfbRectangle; + +#define sz_rfbRectangle 8 + + +/*----------------------------------------------------------------------------- + * Structure used to specify pixel format. + */ + +typedef struct { + + CARD8 bitsPerPixel; /* 8,16,32 only */ + + CARD8 depth; /* 8 to 32 */ + + CARD8 bigEndian; /* True if multi-byte pixels are interpreted + as big endian, or if single-bit-per-pixel + has most significant bit of the byte + corresponding to first (leftmost) pixel. Of + course this is meaningless for 8 bits/pix */ + + CARD8 trueColour; /* If false then we need a "colour map" to + convert pixels to RGB. If true, xxxMax and + xxxShift specify bits used for red, green + and blue */ + + /* the following fields are only meaningful if trueColour is true */ + + CARD16 redMax; /* maximum red value (= 2^n - 1 where n is the + number of bits used for red). Note this + value is always in big endian order. */ + + CARD16 greenMax; /* similar for green */ + + CARD16 blueMax; /* and blue */ + + CARD8 redShift; /* number of shifts needed to get the red + value in a pixel to the least significant + bit. To find the red value from a given + pixel, do the following: + 1) Swap pixel value according to bigEndian + (e.g. if bigEndian is false and host byte + order is big endian, then swap). + 2) Shift right by redShift. + 3) AND with redMax (in host byte order). + 4) You now have the red value between 0 and + redMax. */ + + CARD8 greenShift; /* similar for green */ + + CARD8 blueShift; /* and blue */ + + CARD8 pad1; + CARD16 pad2; + +} rfbPixelFormat; + +#define sz_rfbPixelFormat 16 + + + +/***************************************************************************** + * + * Initial handshaking messages + * + *****************************************************************************/ + +/*----------------------------------------------------------------------------- + * Protocol Version + * + * The server always sends 12 bytes to start which identifies the latest RFB + * protocol version number which it supports. These bytes are interpreted + * as a string of 12 ASCII characters in the format "RFB xxx.yyy\n" where + * xxx and yyy are the major and minor version numbers (for version 3.3 + * this is "RFB 003.003\n"). + * + * The client then replies with a similar 12-byte message giving the version + * number of the protocol which should actually be used (which may be different + * to that quoted by the server). + * + * It is intended that both clients and servers may provide some level of + * backwards compatibility by this mechanism. Servers in particular should + * attempt to provide backwards compatibility, and even forwards compatibility + * to some extent. For example if a client demands version 3.1 of the + * protocol, a 3.0 server can probably assume that by ignoring requests for + * encoding types it doesn't understand, everything will still work OK. This + * will probably not be the case for changes in the major version number. + * + * The format string below can be used in sprintf or sscanf to generate or + * decode the version string respectively. + */ + +#define rfbProtocolVersionFormat "RFB %03d.%03d\n" +#define rfbProtocolMajorVersion 3 +#define rfbProtocolMinorVersion 3 + +typedef char rfbProtocolVersionMsg[13]; /* allow extra byte for null */ + +#define sz_rfbProtocolVersionMsg 12 + + +/*----------------------------------------------------------------------------- + * Authentication + * + * Once the protocol version has been decided, the server then sends a 32-bit + * word indicating whether any authentication is needed on the connection. + * The value of this word determines the authentication scheme in use. For + * version 3.0 of the protocol this may have one of the following values: + */ + +#define rfbConnFailed 0 +#define rfbNoAuth 1 +#define rfbVncAuth 2 + +/* + * rfbConnFailed: For some reason the connection failed (e.g. the server + * cannot support the desired protocol version). This is + * followed by a string describing the reason (where a + * string is specified as a 32-bit length followed by that + * many ASCII characters). + * + * rfbNoAuth: No authentication is needed. + * + * rfbVncAuth: The VNC authentication scheme is to be used. A 16-byte + * challenge follows, which the client encrypts as + * appropriate using the password and sends the resulting + * 16-byte response. If the response is correct, the + * server sends the 32-bit word rfbVncAuthOK. If a simple + * failure happens, the server sends rfbVncAuthFailed and + * closes the connection. If the server decides that too + * many failures have occurred, it sends rfbVncAuthTooMany + * and closes the connection. In the latter case, the + * server should not allow an immediate reconnection by + * the client. + */ + +#define rfbVncAuthOK 0 +#define rfbVncAuthFailed 1 +#define rfbVncAuthTooMany 2 + + +/*----------------------------------------------------------------------------- + * Client Initialisation Message + * + * Once the client and server are sure that they're happy to talk to one + * another, the client sends an initialisation message. At present this + * message only consists of a boolean indicating whether the server should try + * to share the desktop by leaving other clients connected, or give exclusive + * access to this client by disconnecting all other clients. + */ + +typedef struct { + CARD8 shared; +} rfbClientInitMsg; + +#define sz_rfbClientInitMsg 1 + + +/*----------------------------------------------------------------------------- + * Server Initialisation Message + * + * After the client initialisation message, the server sends one of its own. + * This tells the client the width and height of the server's framebuffer, + * its pixel format and the name associated with the desktop. + */ + +typedef struct { + CARD16 framebufferWidth; + CARD16 framebufferHeight; + rfbPixelFormat format; /* the server's preferred pixel format */ + CARD32 nameLength; + /* followed by char name[nameLength] */ +} rfbServerInitMsg; + +#define sz_rfbServerInitMsg (8 + sz_rfbPixelFormat) + + +/* + * Following the server initialisation message it's up to the client to send + * whichever protocol messages it wants. Typically it will send a + * SetPixelFormat message and a SetEncodings message, followed by a + * FramebufferUpdateRequest. From then on the server will send + * FramebufferUpdate messages in response to the client's + * FramebufferUpdateRequest messages. The client should send + * FramebufferUpdateRequest messages with incremental set to true when it has + * finished processing one FramebufferUpdate and is ready to process another. + * With a fast client, the rate at which FramebufferUpdateRequests are sent + * should be regulated to avoid hogging the network. + */ + + + +/***************************************************************************** + * + * Message types + * + *****************************************************************************/ + +/* server -> client */ + +#define rfbFramebufferUpdate 0 +#define rfbSetColourMapEntries 1 +#define rfbBell 2 +#define rfbServerCutText 3 + + +/* client -> server */ + +#define rfbSetPixelFormat 0 +#define rfbFixColourMapEntries 1 /* not currently supported */ +#define rfbSetEncodings 2 +#define rfbFramebufferUpdateRequest 3 +#define rfbKeyEvent 4 +#define rfbPointerEvent 5 +#define rfbClientCutText 6 + + + + +/***************************************************************************** + * + * Encoding types + * + *****************************************************************************/ + +#define rfbEncodingRaw 0 +#define rfbEncodingCopyRect 1 +#define rfbEncodingRRE 2 +#define rfbEncodingCoRRE 4 +#define rfbEncodingHextile 5 +#define rfbEncodingZlib 6 +#define rfbEncodingTight 7 +#define rfbEncodingZlibHex 8 +#define rfbEncodingZRLE 16 + +/* + * Special encoding numbers: + * 0xFFFFFF00 .. 0xFFFFFF0F -- encoding-specific compression levels; + * 0xFFFFFF10 .. 0xFFFFFF1F -- mouse cursor shape data; + * 0xFFFFFF20 .. 0xFFFFFF2F -- various protocol extensions; + * 0xFFFFFF30 .. 0xFFFFFFDF -- not allocated yet; + * 0xFFFFFFE0 .. 0xFFFFFFEF -- quality level for JPEG compressor; + * 0xFFFFFFF0 .. 0xFFFFFFFF -- cross-encoding compression levels. + */ + +#define rfbEncodingCompressLevel0 0xFFFFFF00 +#define rfbEncodingCompressLevel1 0xFFFFFF01 +#define rfbEncodingCompressLevel2 0xFFFFFF02 +#define rfbEncodingCompressLevel3 0xFFFFFF03 +#define rfbEncodingCompressLevel4 0xFFFFFF04 +#define rfbEncodingCompressLevel5 0xFFFFFF05 +#define rfbEncodingCompressLevel6 0xFFFFFF06 +#define rfbEncodingCompressLevel7 0xFFFFFF07 +#define rfbEncodingCompressLevel8 0xFFFFFF08 +#define rfbEncodingCompressLevel9 0xFFFFFF09 + +#define rfbEncodingXCursor 0xFFFFFF10 +#define rfbEncodingRichCursor 0xFFFFFF11 + +#define rfbEncodingLastRect 0xFFFFFF20 + +#define rfbEncodingQualityLevel0 0xFFFFFFE0 +#define rfbEncodingQualityLevel1 0xFFFFFFE1 +#define rfbEncodingQualityLevel2 0xFFFFFFE2 +#define rfbEncodingQualityLevel3 0xFFFFFFE3 +#define rfbEncodingQualityLevel4 0xFFFFFFE4 +#define rfbEncodingQualityLevel5 0xFFFFFFE5 +#define rfbEncodingQualityLevel6 0xFFFFFFE6 +#define rfbEncodingQualityLevel7 0xFFFFFFE7 +#define rfbEncodingQualityLevel8 0xFFFFFFE8 +#define rfbEncodingQualityLevel9 0xFFFFFFE9 + + +/***************************************************************************** + * + * Server -> client message definitions + * + *****************************************************************************/ + + +/*----------------------------------------------------------------------------- + * FramebufferUpdate - a block of rectangles to be copied to the framebuffer. + * + * This message consists of a header giving the number of rectangles of pixel + * data followed by the rectangles themselves. The header is padded so that + * together with the type byte it is an exact multiple of 4 bytes (to help + * with alignment of 32-bit pixels): + */ + +typedef struct { + CARD8 type; /* always rfbFramebufferUpdate */ + CARD8 pad; + CARD16 nRects; + /* followed by nRects rectangles */ +} rfbFramebufferUpdateMsg; + +#define sz_rfbFramebufferUpdateMsg 4 + +/* + * Each rectangle of pixel data consists of a header describing the position + * and size of the rectangle and a type word describing the encoding of the + * pixel data, followed finally by the pixel data. Note that if the client has + * not sent a SetEncodings message then it will only receive raw pixel data. + * Also note again that this structure is a multiple of 4 bytes. + */ + +typedef struct { + rfbRectangle r; + CARD32 encoding; /* one of the encoding types rfbEncoding... */ +} rfbFramebufferUpdateRectHeader; + +#define sz_rfbFramebufferUpdateRectHeader (sz_rfbRectangle + 4) + + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * Raw Encoding. Pixels are sent in top-to-bottom scanline order, + * left-to-right within a scanline with no padding in between. + */ + + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * CopyRect Encoding. The pixels are specified simply by the x and y position + * of the source rectangle. + */ + +typedef struct { + CARD16 srcX; + CARD16 srcY; +} rfbCopyRect; + +#define sz_rfbCopyRect 4 + + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * RRE - Rise-and-Run-length Encoding. We have an rfbRREHeader structure + * giving the number of subrectangles following. Finally the data follows in + * the form [...] where each is + * []. + */ + +typedef struct { + CARD32 nSubrects; +} rfbRREHeader; + +#define sz_rfbRREHeader 4 + + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * CoRRE - Compact RRE Encoding. We have an rfbRREHeader structure giving + * the number of subrectangles following. Finally the data follows in the form + * [...] where each is + * []. This means that + * the whole rectangle must be at most 255x255 pixels. + */ + +typedef struct { + CARD8 x; + CARD8 y; + CARD8 w; + CARD8 h; +} rfbCoRRERectangle; + +#define sz_rfbCoRRERectangle 4 + + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * Hextile Encoding. The rectangle is divided up into "tiles" of 16x16 pixels, + * starting at the top left going in left-to-right, top-to-bottom order. If + * the width of the rectangle is not an exact multiple of 16 then the width of + * the last tile in each row will be correspondingly smaller. Similarly if the + * height is not an exact multiple of 16 then the height of each tile in the + * final row will also be smaller. Each tile begins with a "subencoding" type + * byte, which is a mask made up of a number of bits. If the Raw bit is set + * then the other bits are irrelevant; w*h pixel values follow (where w and h + * are the width and height of the tile). Otherwise the tile is encoded in a + * similar way to RRE, except that the position and size of each subrectangle + * can be specified in just two bytes. The other bits in the mask are as + * follows: + * + * BackgroundSpecified - if set, a pixel value follows which specifies + * the background colour for this tile. The first non-raw tile in a + * rectangle must have this bit set. If this bit isn't set then the + * background is the same as the last tile. + * + * ForegroundSpecified - if set, a pixel value follows which specifies + * the foreground colour to be used for all subrectangles in this tile. + * If this bit is set then the SubrectsColoured bit must be zero. + * + * AnySubrects - if set, a single byte follows giving the number of + * subrectangles following. If not set, there are no subrectangles (i.e. + * the whole tile is just solid background colour). + * + * SubrectsColoured - if set then each subrectangle is preceded by a pixel + * value giving the colour of that subrectangle. If not set, all + * subrectangles are the same colour, the foreground colour; if the + * ForegroundSpecified bit wasn't set then the foreground is the same as + * the last tile. + * + * The position and size of each subrectangle is specified in two bytes. The + * Pack macros below can be used to generate the two bytes from x, y, w, h, + * and the Extract macros can be used to extract the x, y, w, h values from + * the two bytes. + */ + +#define rfbHextileRaw (1 << 0) +#define rfbHextileBackgroundSpecified (1 << 1) +#define rfbHextileForegroundSpecified (1 << 2) +#define rfbHextileAnySubrects (1 << 3) +#define rfbHextileSubrectsColoured (1 << 4) + +#define rfbHextilePackXY(x,y) (((x) << 4) | (y)) +#define rfbHextilePackWH(w,h) ((((w)-1) << 4) | ((h)-1)) +#define rfbHextileExtractX(byte) ((byte) >> 4) +#define rfbHextileExtractY(byte) ((byte) & 0xf) +#define rfbHextileExtractW(byte) (((byte) >> 4) + 1) +#define rfbHextileExtractH(byte) (((byte) & 0xf) + 1) + + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * zlib - zlib compressed Encoding. We have an rfbZlibHeader structure + * giving the number of bytes following. Finally the data follows is + * zlib compressed version of the raw pixel data as negotiated. + */ + +typedef struct { + CARD32 nBytes; +} rfbZlibHeader; + +#define sz_rfbZlibHeader 4 + + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * Tight Encoding. FIXME: Add more documentation. + */ + +#define rfbTightExplicitFilter 0x04 +#define rfbTightFill 0x08 +#define rfbTightJpeg 0x09 +#define rfbTightMaxSubencoding 0x09 + +/* Filters to improve compression efficiency */ +#define rfbTightFilterCopy 0x00 +#define rfbTightFilterPalette 0x01 +#define rfbTightFilterGradient 0x02 + + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * XCursor encoding. This is a special encoding used to transmit X-style + * cursor shapes from server to clients. Note that for this encoding, + * coordinates in rfbFramebufferUpdateRectHeader structure hold hotspot + * position (r.x, r.y) and cursor size (r.w, r.h). If (w * h != 0), two RGB + * samples are sent after header in the rfbXCursorColors structure. They + * denote foreground and background colors of the cursor. If a client + * supports only black-and-white cursors, it should ignore these colors and + * assume that foreground is black and background is white. Next, two bitmaps + * (1 bits per pixel) follow: first one with actual data (value 0 denotes + * background color, value 1 denotes foreground color), second one with + * transparency data (bits with zero value mean that these pixels are + * transparent). Both bitmaps represent cursor data in a byte stream, from + * left to right, from top to bottom, and each row is byte-aligned. Most + * significant bits correspond to leftmost pixels. The number of bytes in + * each row can be calculated as ((w + 7) / 8). If (w * h == 0), cursor + * should be hidden (or default local cursor should be set by the client). + */ + +typedef struct { + CARD8 foreRed; + CARD8 foreGreen; + CARD8 foreBlue; + CARD8 backRed; + CARD8 backGreen; + CARD8 backBlue; +} rfbXCursorColors; + +#define sz_rfbXCursorColors 6 + + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * RichCursor encoding. This is a special encoding used to transmit cursor + * shapes from server to clients. It is similar to the XCursor encoding but + * uses client pixel format instead of two RGB colors to represent cursor + * image. For this encoding, coordinates in rfbFramebufferUpdateRectHeader + * structure hold hotspot position (r.x, r.y) and cursor size (r.w, r.h). + * After header, two pixmaps follow: first one with cursor image in current + * client pixel format (like in raw encoding), second with transparency data + * (1 bit per pixel, exactly the same format as used for transparency bitmap + * in the XCursor encoding). If (w * h == 0), cursor should be hidden (or + * default local cursor should be set by the client). + */ + + +/*----------------------------------------------------------------------------- + * SetColourMapEntries - these messages are only sent if the pixel + * format uses a "colour map" (i.e. trueColour false) and the client has not + * fixed the entire colour map using FixColourMapEntries. In addition they + * will only start being sent after the client has sent its first + * FramebufferUpdateRequest. So if the client always tells the server to use + * trueColour then it never needs to process this type of message. + */ + +typedef struct { + CARD8 type; /* always rfbSetColourMapEntries */ + CARD8 pad; + CARD16 firstColour; + CARD16 nColours; + + /* Followed by nColours * 3 * CARD16 + r1, g1, b1, r2, g2, b2, r3, g3, b3, ..., rn, bn, gn */ + +} rfbSetColourMapEntriesMsg; + +#define sz_rfbSetColourMapEntriesMsg 6 + + + +/*----------------------------------------------------------------------------- + * Bell - ring a bell on the client if it has one. + */ + +typedef struct { + CARD8 type; /* always rfbBell */ +} rfbBellMsg; + +#define sz_rfbBellMsg 1 + + + +/*----------------------------------------------------------------------------- + * ServerCutText - the server has new text in its cut buffer. + */ + +typedef struct { + CARD8 type; /* always rfbServerCutText */ + CARD8 pad1; + CARD16 pad2; + CARD32 length; + /* followed by char text[length] */ +} rfbServerCutTextMsg; + +#define sz_rfbServerCutTextMsg 8 + + +/*----------------------------------------------------------------------------- + * Union of all server->client messages. + */ + +typedef union { + CARD8 type; + rfbFramebufferUpdateMsg fu; + rfbSetColourMapEntriesMsg scme; + rfbBellMsg b; + rfbServerCutTextMsg sct; +} rfbServerToClientMsg; + + + +/***************************************************************************** + * + * Message definitions (client -> server) + * + *****************************************************************************/ + + +/*----------------------------------------------------------------------------- + * SetPixelFormat - tell the RFB server the format in which the client wants + * pixels sent. + */ + +typedef struct { + CARD8 type; /* always rfbSetPixelFormat */ + CARD8 pad1; + CARD16 pad2; + rfbPixelFormat format; +} rfbSetPixelFormatMsg; + +#define sz_rfbSetPixelFormatMsg (sz_rfbPixelFormat + 4) + + +/*----------------------------------------------------------------------------- + * FixColourMapEntries - when the pixel format uses a "colour map", fix + * read-only colour map entries. + * + * ***************** NOT CURRENTLY SUPPORTED ***************** + */ + +typedef struct { + CARD8 type; /* always rfbFixColourMapEntries */ + CARD8 pad; + CARD16 firstColour; + CARD16 nColours; + + /* Followed by nColours * 3 * CARD16 + r1, g1, b1, r2, g2, b2, r3, g3, b3, ..., rn, bn, gn */ + +} rfbFixColourMapEntriesMsg; + +#define sz_rfbFixColourMapEntriesMsg 6 + + +/*----------------------------------------------------------------------------- + * SetEncodings - tell the RFB server which encoding types we accept. Put them + * in order of preference, if we have any. We may always receive raw + * encoding, even if we don't specify it here. + */ + +typedef struct { + CARD8 type; /* always rfbSetEncodings */ + CARD8 pad; + CARD16 nEncodings; + /* followed by nEncodings * CARD32 encoding types */ +} rfbSetEncodingsMsg; + +#define sz_rfbSetEncodingsMsg 4 + + +/*----------------------------------------------------------------------------- + * FramebufferUpdateRequest - request for a framebuffer update. If incremental + * is true then the client just wants the changes since the last update. If + * false then it wants the whole of the specified rectangle. + */ + +typedef struct { + CARD8 type; /* always rfbFramebufferUpdateRequest */ + CARD8 incremental; + CARD16 x; + CARD16 y; + CARD16 w; + CARD16 h; +} rfbFramebufferUpdateRequestMsg; + +#define sz_rfbFramebufferUpdateRequestMsg 10 + + +/*----------------------------------------------------------------------------- + * KeyEvent - key press or release + * + * Keys are specified using the "keysym" values defined by the X Window System. + * For most ordinary keys, the keysym is the same as the corresponding ASCII + * value. Other common keys are: + * + * BackSpace 0xff08 + * Tab 0xff09 + * Return or Enter 0xff0d + * Escape 0xff1b + * Insert 0xff63 + * Delete 0xffff + * Home 0xff50 + * End 0xff57 + * Page Up 0xff55 + * Page Down 0xff56 + * Left 0xff51 + * Up 0xff52 + * Right 0xff53 + * Down 0xff54 + * F1 0xffbe + * F2 0xffbf + * ... ... + * F12 0xffc9 + * Shift 0xffe1 + * Control 0xffe3 + * Meta 0xffe7 + * Alt 0xffe9 + */ + +typedef struct { + CARD8 type; /* always rfbKeyEvent */ + CARD8 down; /* true if down (press), false if up */ + CARD16 pad; + CARD32 key; /* key is specified as an X keysym */ +} rfbKeyEventMsg; + +#define sz_rfbKeyEventMsg 8 + + +/*----------------------------------------------------------------------------- + * PointerEvent - mouse/pen move and/or button press. + */ + +typedef struct { + CARD8 type; /* always rfbPointerEvent */ + CARD8 buttonMask; /* bits 0-7 are buttons 1-8, 0=up, 1=down */ + CARD16 x; + CARD16 y; +} rfbPointerEventMsg; + +#define rfbButton1Mask 1 +#define rfbButton2Mask 2 +#define rfbButton3Mask 4 + +#define sz_rfbPointerEventMsg 6 + + + +/*----------------------------------------------------------------------------- + * ClientCutText - the client has new text in its cut buffer. + */ + +typedef struct { + CARD8 type; /* always rfbClientCutText */ + CARD8 pad1; + CARD16 pad2; + CARD32 length; + /* followed by char text[length] */ +} rfbClientCutTextMsg; + +#define sz_rfbClientCutTextMsg 8 + + + +/*----------------------------------------------------------------------------- + * Union of all client->server messages. + */ + +typedef union { + CARD8 type; + rfbSetPixelFormatMsg spf; + rfbFixColourMapEntriesMsg fcme; + rfbSetEncodingsMsg se; + rfbFramebufferUpdateRequestMsg fur; + rfbKeyEventMsg ke; + rfbPointerEventMsg pe; + rfbClientCutTextMsg cct; +} rfbClientToServerMsg; diff --git a/streamdevice.c b/streamdevice.c new file mode 100644 index 0000000..9c9aef1 --- /dev/null +++ b/streamdevice.c @@ -0,0 +1,166 @@ +/* + * streamdevice.c: streaming network device + * + * See the README file for copyright information and how to reach the author. + * + */ + +#include "streamdevice.h" +#include "osdworker.h" +#include "tsworker.h" +#include "netosd.h" + +cStreamDevice::cStreamDevice(void) +{ +#ifdef DEBUG + fprintf(stderr,"[ffnetdev] Device: Constructor cStreamDevice \n"); +#endif + m_Remux = new cPES2TSRemux(TS_VPID, TS_APID); + +} + +cStreamDevice::~cStreamDevice(void) +{ +#ifdef DEBUG + fprintf(stderr,"[ffnetdev] Device: Destructor cStreamDevice \n"); +#endif + DELETENULL(m_Remux); +} + + +void cStreamDevice::MakePrimaryDevice(bool On) +{ +#ifdef DEBUG + fprintf(stderr,"[ffnetdev] Device: ffnetdev becomes primary device. Registering our OSD provider...\n"); +#endif + new cNetOSDProvider(); +} + +int cStreamDevice::ProvidesCa(const cChannel *Channel) const +{ + return 0; +} + +bool cStreamDevice::HasDecoder(void) const +{ + return true; // We can decode MPEG2 +} + +bool cStreamDevice::CanReplay(void) const +{ + return true; // We can replay +} + +bool cStreamDevice::SetPlayMode(ePlayMode PlayMode) +{ + fprintf(stderr, "[ffnetdev] Device: Setting playmode(not implemented). Mode: %d\n",PlayMode); + cOSDWorker::SendPlayMode(PlayMode); + m_Remux->ClearInput(); + m_Remux->ClearOutput(); + m_Remux->PlayModeChange(); + return true; +} + +void cStreamDevice::TrickSpeed(int Speed) +{ + fprintf(stderr,"[ffnetdev] Device: Trickspeed(not implemented). Speed: %d\n", Speed); + m_Remux->ClearInput(); + m_Remux->ClearOutput(); + m_Remux->PlayModeChange(); +} + +void cStreamDevice::Clear(void) +{ + fprintf(stderr,"[ffnetdev] Device: Clear(not implemented).\n"); + m_Remux->ClearInput(); + m_Remux->ClearOutput(); + m_Remux->PlayModeChange(); +// cDevice::Clear(); +} +void cStreamDevice::Play(void) +{ + fprintf(stderr,"[ffnetdev] Device: Play(not implemented).\n"); +// cDevice::Play(); +} + +void cStreamDevice::Freeze(void) +{ + fprintf(stderr,"[ffnetdev] Device: Freeze(not implemented).\n"); +// cDevice::Freeze(); +} + +void cStreamDevice::Mute(void) +{ + fprintf(stderr,"[ffnetdev] Device: Mute(not implemented).\n"); +// cDevice::Mute(); +} + +void cStreamDevice::SetVolumeDevice(int Volume) +{ + fprintf (stderr, "[ffnetdev] Device: Setting volume to %d (not implemented).\n", Volume); +} + +void cStreamDevice::StillPicture(const uchar *Data, int Length) +{ + fprintf(stderr,"[ffnetdev] Device: StillPicture(not implemented).\n"); +} + +bool cStreamDevice::Poll(cPoller &Poller, int TimeoutMs) +{ + //fprintf(stderr,"[ffnetdev] Device: Poll TimeoutMs: %d ....\n",TimeoutMs); + return true; +} +/* ---------------------------------------------------------------------------- + */ +int cStreamDevice::PlayAudio(const uchar *Data, int Length) +{ + if (cTSWorker::HaveStreamClient()) { + while ((m_Remux->Free() < Length) && cTSWorker::HaveStreamClient()) + cCondWait::SleepMs(1); + int result=m_Remux->Put(Data, Length); + if (result!=Length) { + fprintf(stderr,"[ffnetdev] Device: Did not put all in input buffer(audio). result:%d Length: %d Skipping Audio PES packet...\n", result, Length ); + // Delete part of data already written to buffer + m_Remux->DelInput(result); + return (0); + } + else + { + return ( Length ); + } + } + else + { + m_Remux->ClearInput(); + usleep(100000); + return ( 0 ); + } + +} + +/* ---------------------------------------------------------------------------- + */ +int cStreamDevice::PlayVideo(const uchar *Data, int Length) +{ + if (cTSWorker::HaveStreamClient()) { + while ((m_Remux->Free() < Length) && cTSWorker::HaveStreamClient()) + cCondWait::SleepMs(1); + int result=m_Remux->Put(Data, Length); + if (result!=Length) { + fprintf(stderr,"[ffnetdev] Device: Did not put all in input buffer(video). result:%d Length: %d Skipping Video PES packet...\n", result, Length ); + // Delete part of data already written to buffer + m_Remux->DelInput(result); + return (0); + } + else + { + return ( Length ); + } + } + else + { + m_Remux->ClearInput(); + usleep(100000); + return ( 0 ); + } +} diff --git a/streamdevice.h b/streamdevice.h new file mode 100644 index 0000000..64a1f0b --- /dev/null +++ b/streamdevice.h @@ -0,0 +1,48 @@ +/* + * streamdevice.h: streaming network device + * + * See the README file for copyright information and how to reach the author. + * + */ + +#ifndef _STREAMDEVICE__H +#define _STREAMDEVICE__H + +#define TS_VPID 99 +#define TS_APID 100 + +#include + +#include "pes2ts.h" + +class cStreamDevice: public cDevice { +private: + cPES2TSRemux *m_Remux; +protected: +public: + cStreamDevice(void); + ~cStreamDevice(void); + virtual bool HasDecoder(void) const; + virtual bool CanReplay(void) const; + virtual bool SetPlayMode(ePlayMode PlayMode); + virtual void TrickSpeed(int Speed); + virtual void Clear(void); + virtual void Play(void); + virtual void Freeze(void); + virtual void Mute(void); + virtual void SetVolumeDevice (int Volume); + virtual void StillPicture(const uchar *Data, int Length); + virtual bool Poll(cPoller &Poller, int TimeoutMs = 0); + virtual int PlayVideo(const uchar *Data, int Length); + virtual int PlayAudio(const uchar *Data, int Length); + virtual int ProvidesCa(const cChannel *Channel) const; + virtual void MakePrimaryDevice(bool On); + uchar *Get(int &Count) { return m_Remux->Get(Count); } + void LockOutput() { m_Remux->LockOutput(); } + void UnlockOutput() { m_Remux->UnlockOutput(); } + void Del(int Count) { m_Remux->DelOutput(Count); } + void ClearOutput() { m_Remux->ClearOutput(); } + int Available(void) { return m_Remux->Available(); } +}; + +#endif diff --git a/tableinitcmtemplate.c b/tableinitcmtemplate.c new file mode 100644 index 0000000..b282326 --- /dev/null +++ b/tableinitcmtemplate.c @@ -0,0 +1,108 @@ +/* + * tableinitcmtemplate.c - template for initialising lookup tables for + * translation from a colour map to true colour. + * + * This file shouldn't be compiled. It is included multiple times by + * translate.c, each time with a different definition of the macro OUTBPP. + * For each value of OUTBPP, this file defines a function which allocates an + * appropriately sized lookup table and initialises it. + * + * I know this code isn't nice to read because of all the macros, but + * efficiency is important here. + */ + +#if !defined(OUTBPP) +#error "This file shouldn't be compiled." +#error "It is included as part of translate.c" +#endif + +#define OUT_T CONCAT2E(CARD,OUTBPP) +#define SwapOUT(x) CONCAT2E(Swap,OUTBPP) (x) +#define rfbInitColourMapSingleTableOUT \ + CONCAT2E(rfbInitColourMapSingleTable,OUTBPP) + +#include +#include + +// THIS CODE HAS BEEN MODIFIED FROM THE ORIGINAL UNIX SOURCE +// TO WORK FOR WINVNC. THE PALETTE SHOULD REALLY BE RETRIEVED +// FROM THE VNCDESKTOP OBJECT, RATHER THAN FROM THE OS DIRECTLY + +static void +rfbInitColourMapSingleTableOUT (char **table, + rfbPixelFormat *in, + rfbPixelFormat *out) +{ + fprintf(stderr, "[ffnetdev] VNC: rfbInitColourMapSingleTable called\n"); + + // ALLOCATE SPACE FOR COLOUR TABLE + + int nEntries = 1 << in->bitsPerPixel; + + // Allocate the table + if (*table) free(*table); + *table = (char *)malloc(nEntries * sizeof(OUT_T)); + if (*table == NULL) + { + fprintf(stderr, "[ffnetdev] VNC: failed to allocate translation table\n"); + return; + } + + // Obtain the system palette + /* + HDC hDC = GetDC(NULL); + PALETTEENTRY palette[256]; + if (GetSystemPaletteEntries(hDC, + 0, 256, palette) == 0) + { + fprintf(stderr, "[ffnetdev] VNC: failed to get system palette, error=%d\n", + GetLastError()); + ReleaseDC(NULL, hDC); + return; + } + ReleaseDC(NULL, hDC); + */ + + tColor *pColors; + int NumColors; + cOSDWorker::GetOSDColors(&pColors, &NumColors); + + // COLOUR TRANSLATION + + // We now have the colour table intact. Map it into a translation table + int i, r, g, b; + OUT_T outRed, outGreen, outBlue; + OUT_T *t = (OUT_T *)*table; + + tColor *pColor = pColors; + for (i = 0; i < NumColors; i++) + { + // Split down the RGB data + r = (((tColor)*pColor) >> in->redShift) & in->redMax; + g = (((tColor)*pColor) >> in->greenShift) & in->greenMax; + b = (((tColor)*pColor) >> in->blueShift) & in->blueMax; + + + outRed = (r / (256 / (1 + Swap16IfLE(out->redMax )))) & Swap16IfLE(out->redMax); + outGreen = (g / (256 / (1 + Swap16IfLE(out->greenMax)))) & Swap16IfLE(out->greenMax); + outBlue = (b / (256 / (1 + Swap16IfLE(out->blueMax )))) & Swap16IfLE(out->blueMax); + + // Now translate it + t[i] = ((outRed << out->redShift) | + (outGreen << out->greenShift) | + (outBlue << out->blueShift)); +#if (OUTBPP != 8) + if (out->bigEndian != in->bigEndian) + { + t[i] = SwapOUT(t[i]); + } +#endif + pColor++; + } + + fprintf(stderr, "[ffnetdev] VNC: rfbInitColourMapSingleTable done\n"); +} + +#undef OUT_T +#undef SwapOUT +#undef rfbInitColourMapSingleTableOUT diff --git a/tableinittctemplate.c b/tableinittctemplate.c new file mode 100644 index 0000000..9849864 --- /dev/null +++ b/tableinittctemplate.c @@ -0,0 +1,122 @@ +/* + * tableinittctemplate.c - template for initialising lookup tables for + * truecolour to truecolour translation. + * + * This file shouldn't be compiled. It is included multiple times by + * translate.c, each time with a different definition of the macro OUTBPP. + * For each value of OUTBPP, this file defines two functions for initialising + * lookup tables. One is for truecolour translation using a single lookup + * table, the other is for truecolour translation using three separate + * lookup tables for the red, green and blue values. + * + * I know this code isn't nice to read because of all the macros, but + * efficiency is important here. + */ + +#if !defined(OUTBPP) +#error "This file shouldn't be compiled." +#error "It is included as part of translate.c" +#endif + +#define OUT_T CONCAT2E(CARD,OUTBPP) +#define SwapOUT(x) CONCAT2E(Swap,OUTBPP) (x) +#define rfbInitTrueColourSingleTableOUT \ + CONCAT2E(rfbInitTrueColourSingleTable,OUTBPP) +#define rfbInitTrueColourRGBTablesOUT CONCAT2E(rfbInitTrueColourRGBTables,OUTBPP) +#define rfbInitOneRGBTableOUT CONCAT2E(rfbInitOneRGBTable,OUTBPP) + +static void +rfbInitOneRGBTableOUT (OUT_T *table, int inMax, int outMax, int outShift, + int swap); + + +/* + * rfbInitTrueColourSingleTable sets up a single lookup table for truecolour + * translation. + */ + +static void +rfbInitTrueColourSingleTableOUT (char **table, rfbPixelFormat *in, + rfbPixelFormat *out) +{ + int i; + int inRed, inGreen, inBlue, outRed, outGreen, outBlue; + OUT_T *t; + int nEntries = 1 << in->bitsPerPixel; + + if (*table) free(*table); + *table = (char *)malloc(nEntries * sizeof(OUT_T)); + if (table == NULL) return; + t = (OUT_T *)*table; + + for (i = 0; i < nEntries; i++) { + inRed = (i >> in->redShift) & in->redMax; + inGreen = (i >> in->greenShift) & in->greenMax; + inBlue = (i >> in->blueShift) & in->blueMax; + + outRed = (inRed * out->redMax + in->redMax / 2) / in->redMax; + outGreen = (inGreen * out->greenMax + in->greenMax / 2) / in->greenMax; + outBlue = (inBlue * out->blueMax + in->blueMax / 2) / in->blueMax; + + t[i] = ((outRed << out->redShift) | + (outGreen << out->greenShift) | + (outBlue << out->blueShift)); +#if (OUTBPP != 8) + if (out->bigEndian != in->bigEndian) { + t[i] = SwapOUT(t[i]); + } +#endif + } +} + + +/* + * rfbInitTrueColourRGBTables sets up three separate lookup tables for the + * red, green and blue values. + */ + +static void +rfbInitTrueColourRGBTablesOUT (char **table, rfbPixelFormat *in, + rfbPixelFormat *out) +{ + OUT_T *redTable; + OUT_T *greenTable; + OUT_T *blueTable; + + if (*table) free(*table); + *table = (char *)malloc((in->redMax + in->greenMax + in->blueMax + 3) + * sizeof(OUT_T)); + redTable = (OUT_T *)*table; + greenTable = redTable + in->redMax + 1; + blueTable = greenTable + in->greenMax + 1; + + rfbInitOneRGBTableOUT (redTable, in->redMax, out->redMax, + out->redShift, (out->bigEndian != in->bigEndian)); + rfbInitOneRGBTableOUT (greenTable, in->greenMax, out->greenMax, + out->greenShift, (out->bigEndian != in->bigEndian)); + rfbInitOneRGBTableOUT (blueTable, in->blueMax, out->blueMax, + out->blueShift, (out->bigEndian != in->bigEndian)); +} + +static void +rfbInitOneRGBTableOUT (OUT_T *table, int inMax, int outMax, int outShift, + int swap) +{ + int i; + int nEntries = inMax + 1; + + for (i = 0; i < nEntries; i++) { + table[i] = ((i * outMax + inMax / 2) / inMax) << outShift; +#if (OUTBPP != 8) + if (swap) { + table[i] = SwapOUT(table[i]); + } +#endif + } +} + +#undef OUT_T +#undef SwapOUT +#undef rfbInitTrueColourSingleTableOUT +#undef rfbInitTrueColourRGBTablesOUT +#undef rfbInitOneRGBTableOUT diff --git a/tabletranstemplate.c b/tabletranstemplate.c new file mode 100644 index 0000000..dc603fb --- /dev/null +++ b/tabletranstemplate.c @@ -0,0 +1,99 @@ +/* + * tabletranstemplate.c - template for translation using lookup tables. + * + * This file shouldn't be compiled. It is included multiple times by + * translate.c, each time with different definitions of the macros INBPP and OUTBPP. + * + * For each pair of values INBPP and OUTBPP, this file defines two functions for + * translating a given rectangle of pixel data. One uses a single lookup + * table, and the other uses three separate lookup tables for the red, green + * and blue values. + * + * I know this code isn't nice to read because of all the macros, but + * efficiency is important here. + */ + +#if !defined(INBPP) || !defined(OUTBPP) +#error "This file shouldn't be compiled." +#error "It is included as part of translate.c" +#endif + +#define IN_T CONCAT2E(CARD,INBPP) +#define OUT_T CONCAT2E(CARD,OUTBPP) +#define rfbTranslateWithSingleTableINtoOUT \ + CONCAT4E(rfbTranslateWithSingleTable,INBPP,to,OUTBPP) +#define rfbTranslateWithRGBTablesINtoOUT \ + CONCAT4E(rfbTranslateWithRGBTables,INBPP,to,OUTBPP) + +/* + * rfbTranslateWithSingleTableINtoOUT translates a rectangle of pixel data + * using a single lookup table. + */ + +static void +rfbTranslateWithSingleTableINtoOUT (char *table, rfbPixelFormat *in, + rfbPixelFormat *out, + char *iptr, char *optr, + int bytesBetweenInputLines, + int width, int height) +{ + IN_T *ip = (IN_T *)iptr; + OUT_T *op = (OUT_T *)optr; + int ipextra = bytesBetweenInputLines / sizeof(IN_T) - width; + OUT_T *opLineEnd; + OUT_T *t = (OUT_T *)table; + + while (height > 0) { + opLineEnd = op + width; + + while (op < opLineEnd) { + *(op++) = t[*(ip++)]; + } + + ip += ipextra; + height--; + } +} + + +/* + * rfbTranslateWithRGBTablesINtoOUT translates a rectangle of pixel data + * using three separate lookup tables for the red, green and blue values. + */ + +static void +rfbTranslateWithRGBTablesINtoOUT (char *table, rfbPixelFormat *in, + rfbPixelFormat *out, + char *iptr, char *optr, + int bytesBetweenInputLines, + int width, int height) +{ + IN_T *ip = (IN_T *)iptr; + OUT_T *op = (OUT_T *)optr; + int ipextra = bytesBetweenInputLines / sizeof(IN_T) - width; + OUT_T *opLineEnd; + OUT_T *redTable = (OUT_T *)table; + OUT_T *greenTable = redTable + in->redMax + 1; + OUT_T *blueTable = greenTable + in->greenMax + 1; + IN_T in_pix; + OUT_T out_pix; + + while (height > 0) { + opLineEnd = op + width; + + while (op < opLineEnd) { + in_pix = *ip++; + out_pix = redTable[(in_pix >> in->redShift) & in->redMax]; + out_pix |= greenTable[(in_pix >> in->greenShift) & in->greenMax]; + out_pix |= blueTable[(in_pix >> in->blueShift) & in->blueMax]; + *op++ = out_pix; + } + ip += ipextra; + height--; + } +} + +#undef IN_T +#undef OUT_T +#undef rfbTranslateWithSingleTableINtoOUT +#undef rfbTranslateWithRGBTablesINtoOUT diff --git a/tools/select.c b/tools/select.c new file mode 100644 index 0000000..0ab5f9b --- /dev/null +++ b/tools/select.c @@ -0,0 +1,49 @@ +#include "tools/select.h" + +#include +#include +#include +#include +#include +#include + +cTBSelect::cTBSelect(void) { + Clear(); +} + +cTBSelect::~cTBSelect() { +} + +int cTBSelect::Select(uint TimeoutMs) { + struct timeval tv; + ssize_t res; + int ms; + + tv.tv_usec = (TimeoutMs % 1000) * 1000; + tv.tv_sec = TimeoutMs / 1000; + + if (TimeoutMs == 0) + return ::select(m_MaxFiled + 1, &m_Rfds, &m_Wfds, NULL, &tv); + + cTimeMs starttime; + ms = TimeoutMs; + while (ms > 0 && (res = ::select(m_MaxFiled + 1, &m_Rfds, &m_Wfds, NULL, + &tv)) == -1 && errno == EINTR) { + ms = TimeoutMs - starttime.Elapsed(); + tv.tv_usec = (ms % 1000) * 1000; + tv.tv_sec = ms / 1000; + } + if (ms <= 0) { + errno = ETIMEDOUT; + return -1; + } + return res; +} + +int cTBSelect::Select(void) { + ssize_t res; + while ((res = ::select(m_MaxFiled + 1, &m_Rfds, &m_Wfds, NULL, NULL)) == -1 + && errno == EINTR) + ; + return res; +} diff --git a/tools/select.h b/tools/select.h new file mode 100644 index 0000000..7e873e2 --- /dev/null +++ b/tools/select.h @@ -0,0 +1,75 @@ +#ifndef TOOLBOX_SELECT_H +#define TOOLBOX_SELECT_H + +#include "tools/tools.h" + +#include + +/* cTBSelect provides an interface for polling UNIX-like file descriptors. */ + +class cTBSelect { +private: + int m_MaxFiled; + + fd_set m_Rfds; + fd_set m_Wfds; + +public: + cTBSelect(void); + virtual ~cTBSelect(); + + /* Clear() resets the object for use in a new Select() call. All file + descriptors and their previous states are invalidated. */ + virtual void Clear(void); + + /* Add() adds a file descriptor to be polled in the next Select() call. + That call polls if the file is readable if Output is set to false, + writeable otherwise. */ + virtual bool Add(int Filed, bool Output = false); + + /* Select() polls all descriptors added by Add() and returns as soon as + one of those changes state (gets readable/writeable), or after + TimeoutMs milliseconds, whichever happens first. It returns the number + of filedescriptors that have changed state. On error, -1 is returned + and errno is set appropriately. */ + virtual int Select(uint TimeoutMs); + + /* Select() polls all descriptors added by Add() and returns as soon as + one of those changes state (gets readable/writeable). It returns the + number of filedescriptors that have changed state. On error, -1 is + returned and errno is set appropriately. */ + virtual int Select(void); + + /* CanRead() returns true if the descriptor has changed to readable during + the last Select() call. Otherwise false is returned. */ + virtual bool CanRead(int FileNo) const; + + /* CanWrite() returns true if the descriptor has changed to writeable + during the last Select() call. Otherwise false is returned. */ + virtual bool CanWrite(int FileNo) const; +}; + +inline void cTBSelect::Clear(void) { + FD_ZERO(&m_Rfds); + FD_ZERO(&m_Wfds); + m_MaxFiled = -1; +} + +inline bool cTBSelect::Add(int Filed, bool Output /* = false */) { + if (Filed < 0) return false; + FD_SET(Filed, Output ? &m_Wfds : &m_Rfds); + if (Filed > m_MaxFiled) m_MaxFiled = Filed; + return true; +} + +inline bool cTBSelect::CanRead(int FileNo) const { + if (FileNo < 0) return false; + return FD_ISSET(FileNo, &m_Rfds); +} + +inline bool cTBSelect::CanWrite(int FileNo) const { + if (FileNo < 0) return false; + return FD_ISSET(FileNo, &m_Wfds); +} + +#endif // TOOLBOX_SELECT_H diff --git a/tools/socket.c b/tools/socket.c new file mode 100644 index 0000000..3e3be65 --- /dev/null +++ b/tools/socket.c @@ -0,0 +1,135 @@ +#include "tools/socket.h" + +#include +#include +#include +#include +#include + +cTBSocket::cTBSocket(int Type) { + memset(&m_LocalAddr, 0, sizeof(m_LocalAddr)); + memset(&m_RemoteAddr, 0, sizeof(m_RemoteAddr)); + m_Type = Type; +} + +cTBSocket::~cTBSocket() { + if (IsOpen()) Close(); +} + +bool cTBSocket::Connect(const std::string &Host, unsigned int Port) { + socklen_t len; + int socket; + + if (IsOpen()) Close(); + + if ((socket = ::socket(PF_INET, m_Type, IPPROTO_IP)) == -1) + return false; + + m_LocalAddr.sin_family = AF_INET; + m_LocalAddr.sin_port = 0; + m_LocalAddr.sin_addr.s_addr = INADDR_ANY; + if (::bind(socket, (struct sockaddr*)&m_LocalAddr, sizeof(m_LocalAddr)) + == -1) + return false; + + m_RemoteAddr.sin_family = AF_INET; + m_RemoteAddr.sin_port = htons(Port); + m_RemoteAddr.sin_addr.s_addr = inet_addr(Host.c_str()); + if (::connect(socket, (struct sockaddr*)&m_RemoteAddr, + sizeof(m_RemoteAddr)) == -1) + return false; + + len = sizeof(struct sockaddr_in); + if (::getpeername(socket, (struct sockaddr*)&m_RemoteAddr, &len) == -1) + return false; + + len = sizeof(struct sockaddr_in); + if (::getsockname(socket, (struct sockaddr*)&m_LocalAddr, &len) == -1) + return false; + + return cTBSource::Open(socket); +} + +bool cTBSocket::Listen(const std::string &Ip, unsigned int Port, int BackLog) { + int val; + socklen_t len; + int socket; + + if (IsOpen()) Close(); + + if ((socket = ::socket(PF_INET, m_Type, IPPROTO_IP)) == -1) + return false; + + val = 1; + if (::setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) == -1) + return false; + + m_LocalAddr.sin_family = AF_INET; + m_LocalAddr.sin_port = htons(Port); + m_LocalAddr.sin_addr.s_addr = inet_addr(Ip.c_str()); + if (::bind(socket, (struct sockaddr*)&m_LocalAddr, sizeof(m_LocalAddr)) + == -1) + return false; + + len = sizeof(struct sockaddr_in); + if (::getsockname(socket, (struct sockaddr*)&m_LocalAddr, &len) == -1) + return false; + + if (m_Type == SOCK_STREAM && ::listen(socket, BackLog) == -1) + return false; + + if (!cTBSource::Open(socket)) + return false; + + return true; +} + +bool cTBSocket::Accept(const cTBSocket &Listener) { + socklen_t addrlen; + int socket; + + if (IsOpen()) Close(); + + addrlen = sizeof(struct sockaddr_in); + if ((socket = ::accept(Listener, (struct sockaddr*)&m_RemoteAddr, + &addrlen)) == -1) + return false; + + addrlen = sizeof(struct sockaddr_in); + if (::getsockname(socket, (struct sockaddr*)&m_LocalAddr, &addrlen) == -1) + return false; + + if (!cTBSource::Open(socket)) + return false; + + return true; +} + +RETURNS(cTBSocket, cTBSocket::Accept(void) const, ret) + ret.Accept(*this); +RETURN(ret) + +bool cTBSocket::Close(void) { + bool ret = true; + + if (!IsOpen()) + ERRNUL(EBADF); + + if (::close(*this) == -1) + ret = false; + + if (!cTBSource::Close()) + ret = false; + + memset(&m_LocalAddr, 0, sizeof(m_LocalAddr)); + memset(&m_RemoteAddr, 0, sizeof(m_RemoteAddr)); + + return ret; +} + +bool cTBSocket::Shutdown(int how) { + if (!IsOpen()) + ERRNUL(EBADF); + + return ::shutdown(*this, how) != -1; +} diff --git a/tools/socket.h b/tools/socket.h new file mode 100644 index 0000000..d1a7d62 --- /dev/null +++ b/tools/socket.h @@ -0,0 +1,108 @@ +#ifndef TOOLBOX_SOCKET_H +#define TOOLBOX_SOCKET_H + +#include "tools/tools.h" +#include "tools/source.h" + +#include +#include +#include +#include + +/* cTBSocket provides a cTBSource-derived interface for input and output on + TCP/IPv4-sockets. */ + +class cTBSocket: public cTBSource { +private: + struct sockaddr_in m_LocalAddr; + struct sockaddr_in m_RemoteAddr; + + int m_Type; + +public: + cTBSocket(int Type = SOCK_STREAM); + virtual ~cTBSocket(); + + /* See cTBSource::SysRead() + Reimplemented for TCP/IPv4 sockets. */ + virtual ssize_t SysRead(void *Buffer, size_t Length) const; + + /* See cTBSource::SysWrite() + Reimplemented for TCP/IPv4 sockets. */ + virtual ssize_t SysWrite(const void *Buffer, size_t Length) const; + + /* Connect() tries to connect an available local socket to the port given + by Port of the target host given by Host in numbers-and-dots notation + (i.e. "212.43.45.21"). Returns true if the connection attempt was + successful and false otherwise, setting errno appropriately. */ + virtual bool Connect(const std::string &Host, uint Port); + + /* Shutdown() shuts down one or both ends of a socket. If called with How + set to SHUT_RD, further reads on this socket will be denied. If called + with SHUT_WR, all writes are denied. Called with SHUT_RDWR, all firther + action on this socket will be denied. Returns true on success and false + otherwise, setting errno appropriately. */ + virtual bool Shutdown(int How); + + /* Close() closes the associated socket and releases all structures. + Returns true on success and false otherwise, setting errno + appropriately. The object is in the closed state afterwards, regardless + of any errors. */ + virtual bool Close(void); + + /* Listen() listens on the local port Port for incoming connections. The + BackLog parameter defines the maximum length the queue of pending + connections may grow to. Returns true if the object is listening on + the specified port and false otherwise, setting errno appropriately. */ + virtual bool Listen(const std::string &Ip, uint Port, int BackLog); + + /* Accept() returns a newly created cTBSocket, which is connected to the + first connection request on the queue of pending connections of a + listening socket. If no connection request was pending, or if any other + error occured, the resulting cTBSocket is closed. */ + virtual cTBSocket Accept(void) const; + + /* Accept() extracts the first connection request on the queue of pending + connections of the listening socket Listener and connects it to this + object. Returns true on success and false otherwise, setting errno to + an appropriate value. */ + virtual bool Accept(const cTBSocket &Listener); + + /* LocalPort() returns the port number this socket is connected to locally. + The result is undefined for a non-open socket. */ + int LocalPort(void) const { return ntohs(m_LocalAddr.sin_port); } + + /* RemotePort() returns the port number this socket is connected to on the + remote side. The result is undefined for a non-open socket. */ + int RemotePort(void) const { return ntohs(m_RemoteAddr.sin_port); } + + /* LocalIp() returns the internet address in numbers-and-dots notation of + the interface this socket is connected to locally. This can be + "0.0.0.0" for a listening socket listening to all interfaces. If the + socket is in its closed state, the result is undefined. */ + std::string LocalIp(void) const { return inet_ntoa(m_LocalAddr.sin_addr); } + + /* RemoteIp() returns the internet address in numbers-and-dots notation of + the interface this socket is connected to on the remote side. If the + socket is in its closed state, the result is undefined. */ + std::string RemoteIp(void) const { return inet_ntoa(m_RemoteAddr.sin_addr); } + + in_addr_t LocalIpAddr(void) const { return m_LocalAddr.sin_addr.s_addr; } + in_addr_t RemoteIpAddr(void) const { return m_RemoteAddr.sin_addr.s_addr; } + + int Type(void) const { return m_Type; } +}; + +inline ssize_t cTBSocket::SysRead(void *Buffer, size_t Length) const { + if (m_Type == SOCK_DGRAM) { + socklen_t len = sizeof(m_RemoteAddr); + return ::recvfrom(*this, Buffer, Length, 0, (sockaddr*)&m_RemoteAddr, &len); + } else + return ::recv(*this, Buffer, Length, 0); +} + +inline ssize_t cTBSocket::SysWrite(const void *Buffer, size_t Length) const { + return ::send(*this, Buffer, Length, 0); +} + +#endif // TOOLBOX_SOCKET_H diff --git a/tools/source.c b/tools/source.c new file mode 100644 index 0000000..f8751b6 --- /dev/null +++ b/tools/source.c @@ -0,0 +1,169 @@ +#include "tools/source.h" +#include "tools/select.h" + +#include +#include +#include +#include +#include + +cTBSource::cTBSource(void) { + m_BytesRead = 0; + m_BytesWritten = 0; + m_Filed = -1; +} + +bool cTBSource::Open(int Filed, bool IsUnixFd) { + if (IsOpen()) + Close(); + + m_Filed = Filed; + if (IsUnixFd && ::fcntl(m_Filed, F_SETFL, O_NONBLOCK) == -1) + return false; + + return true; +} + +cTBSource::~cTBSource() { +} + +bool cTBSource::Close(void) { + if (!IsOpen()) { + errno = EBADF; + return false; + } + + m_Filed = -1; + return true; +} + +ssize_t cTBSource::Read(void *Buffer, size_t Length) { + ssize_t res; + while ((res = SysRead(Buffer, Length)) < 0 && errno == EINTR) + errno = 0; + if (res > 0) m_BytesRead += res; + return res; +} + +ssize_t cTBSource::Write(const void *Buffer, size_t Length) { + ssize_t res; + while ((res = SysWrite(Buffer, Length)) < 0 && errno == EINTR) + errno = 0; + if (res > 0) m_BytesWritten += res; + return res; +} + +bool cTBSource::TimedWrite(const void *Buffer, size_t Length, uint TimeoutMs) { + cTBSelect sel; + int ms, offs; + + cTimeMs starttime; + ms = TimeoutMs; + offs = 0; + while (Length > 0) { + int b; + + sel.Clear(); + sel.Add(m_Filed, true); + if (sel.Select(ms) == -1) + return false; + + if (sel.CanWrite(m_Filed)) { + if ((b = Write((char*)Buffer + offs, Length)) == -1) + return false; + offs += b; + Length -= b; + } + + ms = TimeoutMs - starttime.Elapsed(); + if (ms <= 0) { + errno = ETIMEDOUT; + return false; + } + } + return true; +} + +bool cTBSource::SafeWrite(const void *Buffer, size_t Length) { + cTBSelect sel; + int offs; + + offs = 0; + while (Length > 0) { + int b; + + sel.Clear(); + sel.Add(m_Filed, true); + if (sel.Select() == -1) + return false; + + if (sel.CanWrite(m_Filed)) { + if ((b = Write((char*)Buffer + offs, Length)) == -1) + return false; + offs += b; + Length -= b; + } + + } + return true; +} + +ssize_t cTBSource::ReadUntil(void *Buffer, size_t Length, const char *Seq, + uint TimeoutMs) { + int ms; + size_t len; + cTBSelect sel; + + if ((len = m_LineBuffer.find(Seq)) != (size_t)-1) { + if (len > Length) { + errno = ENOBUFS; + return -1; + } + memcpy(Buffer, m_LineBuffer.data(), len); + m_LineBuffer.erase(0, len + strlen(Seq)); + //Dprintf("ReadUntil: Served from Linebuffer: %d, |%.*s|\n", len, len - 1, + // (char*)Buffer); + return len; + } + + cTimeMs starttime; + ms = TimeoutMs; + while (m_LineBuffer.size() < BUFSIZ) { + sel.Clear(); + sel.Add(m_Filed, false); + + if (sel.Select(ms) == -1) + return -1; + + if (sel.CanRead(m_Filed)) { + int b; + + len = m_LineBuffer.size(); + m_LineBuffer.resize(BUFSIZ); + if ((b = Read((char*)m_LineBuffer.data() + len, BUFSIZ - len)) == -1) + return -1; + m_LineBuffer.resize(len + b); + + if ((len = m_LineBuffer.find(Seq)) != (size_t)-1) { + if (len > Length) { + errno = ENOBUFS; + return -1; + } + memcpy(Buffer, m_LineBuffer.data(), len); + m_LineBuffer.erase(0, len + strlen(Seq)); + //Dprintf("ReadUntil: Served from Linebuffer: %d, |%.*s|\n", len, len - 1, + // (char*)Buffer); + return len; + } + } + + ms = TimeoutMs - starttime.Elapsed(); + if (ms <= 0) { + errno = ETIMEDOUT; + return -1; + } + } + errno = ENOBUFS; + return -1; +} + diff --git a/tools/source.h b/tools/source.h new file mode 100644 index 0000000..09c4bf3 --- /dev/null +++ b/tools/source.h @@ -0,0 +1,109 @@ +#ifndef TOOLBOX_SOURCE_H +#define TOOLBOX_SOURCE_H + +#include "tools/tools.h" + +#include +#include + +/* cTBSource provides an abstract interface for input and output. It can + be used to have common access to different types of UNIX-files. */ + +class cTBSource { +private: + int m_Filed; + + size_t m_BytesRead; + size_t m_BytesWritten; + + std::string m_LineBuffer; + +public: + cTBSource(void); + virtual ~cTBSource(); + + /* SysRead() implements the low-level read on the source. It will store + data into the area pointed to by Buffer, which is at least Length + bytes in size. It will return the exact number of bytes read (which + can be fewer than requested). On error, -1 is returned, and errno + is set to an appropriate value. */ + virtual ssize_t SysRead(void *Buffer, size_t Length) const = 0; + + /* SysWrite() implements the low-level write on the source. It will write + at most Length bytes of the data pointed to by Buffer. It will return + the exact number of bytes written (which can be fewer than requested). + On error, -1 is returned, and errno is set to an appropriate value. */ + virtual ssize_t SysWrite(const void *Buffer, size_t Length) const = 0; + + /* IsOpen() returns true, if this source refers to a valid descriptor. + It is not checked whether this source is really open, so only if + opened by the appropriate Methods this function will return the + correct value */ + virtual bool IsOpen(void) const { return m_Filed != -1; } + + /* Open() associates this source with the descriptor Filed, setting it + to non-blocking mode if IsUnixFd in true. Returns true on success, + and false on error, setting errno to appropriately. + If you want to implement sources that can't be represented by UNIX + filedescriptors, you can use Filed to store any useful information + about the source. + This must be called by any derivations in an appropriate Method (like + open for files, connect for sockets). */ + virtual bool Open(int Filed, bool IsUnixFd = true); + + /* Close() resets the source to the uninitialized state (IsOpen() == false) + and must be called by any derivations after really closing the source. + Returns true on success and false on error, setting errno appropriately. + The object is in closed state afterwards, even if an error occured. */ + virtual bool Close(void); + + /* Read() reads at most Length bytes into the storage pointed to by Buffer, + which must be at least Length bytes in size, using the SysRead()- + Interface. It retries if an EINTR occurs (i.e. the low-level call was + interrupted). It returns the exact number of bytes read (which can be + fewer than requested). On error, -1 is returned, and errno is set + appropriately. */ + ssize_t Read(void *Buffer, size_t Length); + + /* Write() writes at most Length bytes from the storage pointed to by + Buffer, using the SysWrite()-Interface. It retries if EINTR occurs + (i.e. the low-level call was interrupted). It returns the exact number + of bytes written (which can be fewer than requested). On error, -1 is + returned and errno is set appropriately. */ + ssize_t Write(const void *Buffer, size_t Length); + + /* TimedWrite() tries to write Length bytes from the storage pointed to by + Buffer within the time specified by TimeoutMs, using the Write()- + Interface. On success, true is returned. On error, false is returned + and errno is set appropriately. TimedRead only works on UNIX file + descriptor sources. */ + bool TimedWrite(const void *Buffer, size_t Length, uint TimeoutMs); + + bool SafeWrite(const void *Buffer, size_t Length); + + /* ReadUntil() tries to read at most Length bytes into the storage pointed + to by Buffer, which must be at least Length bytes in size, within the + time specified by TimeoutMs, using the Read()-Interface. Reading stops + after the character sequence Seq has been read and on end-of-file. + Returns the number of bytes read (if that is equal to Length, you have + to check if the buffer ends with Seq), or -1 on error, in which case + errno is set appropriately. */ + ssize_t ReadUntil(void *Buffer, size_t Length, const char *Seq, + uint TimeoutMs); + + /* BytesRead() returns the exact number of bytes read through the Read() + method since Close() has been called on this source (or since its + creation). */ + size_t BytesRead(void) const { return m_BytesRead; } + + /* BytesWritten() returns the exact number of bytes written through the + Write() method since Close() has been called on this source (or since + its creation). */ + size_t BytesWritten(void) const { return m_BytesWritten; } + + /* operator int() returns the descriptor (or informative number) associated + with this source. */ + operator int() const { return m_Filed; } +}; + +#endif // TOOLBOX_SOURCE_H diff --git a/tools/tools.c b/tools/tools.c new file mode 100644 index 0000000..fa813fa --- /dev/null +++ b/tools/tools.c @@ -0,0 +1,12 @@ +#include "tools/tools.h" + +#include +#include +#include +#include +#include + +void *operator new(size_t nSize, void *p) throw () { + return p; +} + diff --git a/tools/tools.h b/tools/tools.h new file mode 100644 index 0000000..ab00c60 --- /dev/null +++ b/tools/tools.h @@ -0,0 +1,67 @@ +#ifndef TOOLBOX_TOOLS_H +#define TOOLBOX_TOOLS_H + +//#include +//#include +#include + +//#define KILOBYTE(x) ((x)*1024) +//#define MEGABYTE(x) (KILOBYTE(x)*1024) + +//typedef unsigned int uint; +//typedef unsigned long ulong; +typedef unsigned char uchar; +//typedef unsigned short ushort; + +// Special constructor for CreateElements +void *operator new(size_t, void*) throw (); + +#ifdef TOOLBOX_DEBUG +# define ASSERT(x) if ((x)) cerr << "Warning: ASSERT failed At " << __FILE__ << ":" << __LINE__ << " ["#x"]" << endl +# define CHECK_PTR(x) if (!(x)) cerr << "Warning: Pointer is NULL At " << __FILE__ << ":" << __LINE__ << endl; +# define CHECK_NEXT_ALLOC() _checkNextAlloc() +# define DPRINT(x...) LOGi(x) +#else +# define ASSERT(x) +# define CHECK_PTR(x) +# define CHECK_NEXT_ALLOC() +# define DPRINT(x...) +#endif + +#define ERRNUL(e) {errno=e;return 0;} +#define ERRSYS(e) {errno=e;return -1;} + +/* RETURNS() and RETURN() are macros that can be used if a class object is + being returned. They make use of the GNU C-Compiler's named return value + feature, if available. In this case, the class object isn't returned and + copied, but the result itself is filled. + + RETURNS(ReturnType, FunctionDeclaration, Result) + ... function-body working on Result ... + RETURN(Result) + + A function like this (cXYZ is a class type): + + cXYZ myfunction(int a, char *b) { + cXYZ result; + ... something happens with result ... + return result; + } + + can be written like this: + + RETURNS(cXYZ, myfunction(int a, char *b), result) + ... something happens with result ... + RETURN(result) + + DISABLED SINCE GCC 3.x +*/ +//#ifdef __GNUC__ +//# define RETURNS(t,x,r) t x return r { +//# define RETURN(x) } +//#else +# define RETURNS(t,x,r) t x { t r; +# define RETURN(x) return x; } +//#endif + +#endif // TOOLBOX_TOOLS_H diff --git a/translate.c b/translate.c new file mode 100644 index 0000000..76b980a --- /dev/null +++ b/translate.c @@ -0,0 +1,138 @@ +/* + * translate.c - translate between different pixel formats + */ + +/* + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * 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 "stdhdrs.h" + +#include "translate.h" +#include +#include "rfb.h" +#include + +#define CONCAT2(a,b) a##b +#define CONCAT2E(a,b) CONCAT2(a,b) +#define CONCAT4(a,b,c,d) a##b##c##d +#define CONCAT4E(a,b,c,d) CONCAT4(a,b,c,d) + +#define OUTBPP 8 +#include "tableinittctemplate.c" +#include "tableinitcmtemplate.c" +#define INBPP 8 +#include "tabletranstemplate.c" +#undef INBPP +#define INBPP 16 +#include "tabletranstemplate.c" +#undef INBPP +#define INBPP 32 +#include "tabletranstemplate.c" +#undef INBPP +#undef OUTBPP + +#define OUTBPP 16 +#include "tableinittctemplate.c" +#include "tableinitcmtemplate.c" +#define INBPP 8 +#include "tabletranstemplate.c" +#undef INBPP +#define INBPP 16 +#include "tabletranstemplate.c" +#undef INBPP +#define INBPP 32 +#include "tabletranstemplate.c" +#undef INBPP +#undef OUTBPP + +#define OUTBPP 32 +#include "tableinittctemplate.c" +#include "tableinitcmtemplate.c" +#define INBPP 8 +#include "tabletranstemplate.c" +#undef INBPP +#define INBPP 16 +#include "tabletranstemplate.c" +#undef INBPP +#define INBPP 32 +#include "tabletranstemplate.c" +#undef INBPP +#undef OUTBPP + +rfbInitTableFnType rfbInitTrueColourSingleTableFns[3] = { + rfbInitTrueColourSingleTable8, + rfbInitTrueColourSingleTable16, + rfbInitTrueColourSingleTable32 +}; + +rfbInitTableFnType rfbInitColourMapSingleTableFns[3] = { + rfbInitColourMapSingleTable8, + rfbInitColourMapSingleTable16, + rfbInitColourMapSingleTable32 +}; + +rfbInitTableFnType rfbInitTrueColourRGBTablesFns[3] = { + rfbInitTrueColourRGBTables8, + rfbInitTrueColourRGBTables16, + rfbInitTrueColourRGBTables32 +}; + +rfbTranslateFnType rfbTranslateWithSingleTableFns[3][3] = { + { rfbTranslateWithSingleTable8to8, + rfbTranslateWithSingleTable8to16, + rfbTranslateWithSingleTable8to32 }, + { rfbTranslateWithSingleTable16to8, + rfbTranslateWithSingleTable16to16, + rfbTranslateWithSingleTable16to32 }, + { rfbTranslateWithSingleTable32to8, + rfbTranslateWithSingleTable32to16, + rfbTranslateWithSingleTable32to32 } +}; + +rfbTranslateFnType rfbTranslateWithRGBTablesFns[3][3] = { + { rfbTranslateWithRGBTables8to8, + rfbTranslateWithRGBTables8to16, + rfbTranslateWithRGBTables8to32 }, + { rfbTranslateWithRGBTables16to8, + rfbTranslateWithRGBTables16to16, + rfbTranslateWithRGBTables16to32 }, + { rfbTranslateWithRGBTables32to8, + rfbTranslateWithRGBTables32to16, + rfbTranslateWithRGBTables32to32 } +}; + + + +// rfbTranslateNone is used when no translation is required. + +void +rfbTranslateNone(char *table, rfbPixelFormat *in, rfbPixelFormat *out, + char *iptr, char *optr, int bytesBetweenInputLines, + int width, int height) +{ + int bytesPerOutputLine = width * (out->bitsPerPixel / 8); + + while (height > 0) { + memcpy(optr, iptr, bytesPerOutputLine); + iptr += bytesBetweenInputLines; + optr += bytesPerOutputLine; + height--; + } +} + diff --git a/translate.h b/translate.h new file mode 100644 index 0000000..da1c7b0 --- /dev/null +++ b/translate.h @@ -0,0 +1,81 @@ +// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. +// +// This file is part of the VNC system. +// +// The VNC system 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 program 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 program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +// USA. +// +// TightVNC distribution homepage on the Web: http://www.tightvnc.com/ +// +// If the source code for the VNC system is not available from the place +// whence you received this file, check http://www.uk.research.att.com/vnc or contact +// the authors on vnc@uk.research.att.com for information on obtaining it. + + +/* translate.h - prototypes of functions in translate.cpp */ + +#ifndef TRANSLATE_H__ +#define TRANSLATE_H__ + +//#include "stdhdrs.h" +#include "rfb.h" + +// Translate function prototype! +typedef void (*rfbTranslateFnType)(char *table, rfbPixelFormat *in, + rfbPixelFormat *out, + char *iptr, char *optr, + int bytesBetweenInputLines, + int width, int height); + +// Init function prototype! +typedef void (*rfbInitTableFnType)(char **table, rfbPixelFormat *in, + rfbPixelFormat *out); + + +// External translation stuff +extern void rfbTranslateNone(char *table, rfbPixelFormat *in, + rfbPixelFormat *out, + char *iptr, char *optr, + int bytesBetweenInputLines, + int width, int height); + +// Macro to compare pixel formats. +#define PF_EQ(x,y) \ + ((x.bitsPerPixel == y.bitsPerPixel) && \ + (x.depth == y.depth) && \ + (x.trueColour == y.trueColour) && \ + ((x.bigEndian == y.bigEndian) || (x.bitsPerPixel == 8)) && \ + (!x.trueColour || ((x.redMax == y.redMax) && \ + (x.greenMax == y.greenMax) && \ + (x.blueMax == y.blueMax) && \ + (x.redShift == y.redShift) && \ + (x.greenShift == y.greenShift) && \ + (x.blueShift == y.blueShift)))) + +// Translation functions themselves +extern rfbInitTableFnType rfbInitTrueColourSingleTableFns[]; +extern rfbInitTableFnType rfbInitColourMapSingleTableFns[]; +extern rfbInitTableFnType rfbInitTrueColourRGBTablesFns[]; +extern rfbTranslateFnType rfbTranslateWithSingleTableFns[3][3]; +extern rfbTranslateFnType rfbTranslateWithRGBTablesFns[3][3]; + +/* +extern bool rfbSetTranslateFunction(rfbClientPtr cl); +extern void rfbSetClientColourMaps(int firstColour, int nColours); +extern bool rfbSetClientColourMap(rfbClientPtr cl, int firstColour, + int nColours); +*/ + +#endif diff --git a/tsworker.c b/tsworker.c new file mode 100644 index 0000000..98e893e --- /dev/null +++ b/tsworker.c @@ -0,0 +1,248 @@ +/* + * tsworker.c: ts streaming worker thread + * + * See the README file for copyright information and how to reach the author. + * + */ + +#include + +#include + +#include "tools/socket.h" +#include "tools/select.h" + +#include "tsworker.h" +#include "config.h" + +#define MINSENDBYTES KILOBYTE(500) + +////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////// + +cTSWorker *cTSWorker::m_Instance = NULL; + +cTSWorker::cTSWorker(void) + : cThread("[ffnetdev] TS streamer") +{ + m_Active = false; + + m_StreamClient = NULL; + origPrimaryDevice = -1; +} + +cTSWorker::~cTSWorker() { + if (m_Active) Stop(); +} + +void cTSWorker::Init(cStreamDevice *StreamDevice, int tsport, cPluginFFNetDev *pPlugin ) { + if (m_Instance == NULL) { + m_Instance = new cTSWorker; + m_Instance->m_StreamDevice = StreamDevice; + m_Instance->TSPort = tsport; + m_Instance->Start(); + m_Instance->m_pPlugin = pPlugin; + } +} + +void cTSWorker::Exit(void) { + if (m_Instance != NULL) { + m_Instance->Stop(); + DELETENULL(m_Instance); + } +} + +void cTSWorker::Stop(void) { + m_Active = false; + Cancel(3); +} + +void cTSWorker::CloseStreamClient(void) { + m_Instance->close_Streamclient_request = true; +#ifdef DEBUG + fprintf(stderr, "[ffnetdev] Streamer: Closing of TS-streaming client socket requested.\r\n"); +#endif +} + +void cTSWorker::Action(void) { + cTBSelect select; + //cTBSocket m_StreamListen(SOCK_DGRAM); + cTBSocket m_StreamListen; + struct timeval oldtime; + long bytessend = 0; + long oldbytessend = 0; + + memset(&oldtime, 0, sizeof(oldtime)); + + const char* m_ListenIp = "0.0.0.0"; + uint m_StreamListenPort = TSPort; + + m_StreamClient = new cTBSocket; + + m_Active = true; + have_Streamclient = false; + + if (!m_StreamListen.Listen(m_ListenIp, m_StreamListenPort, 1)) { // ToDo JN place to allow more connections/clients! + esyslog("[ffnetdev] Streamer: Couldn't listen %s:%d: %s", m_ListenIp, m_StreamListenPort, strerror(errno)); + m_Active = false; + } + else + isyslog("[ffnetdev] Streamer: Listening on port %d", m_StreamListenPort); + + while (m_Active) { + select.Clear(); + + if (have_Streamclient==false) + select.Add(m_StreamListen, false); + else { + select.Add(*m_StreamClient, true); //select for writing fd + select.Add(*m_StreamClient, false); //select for reading fd + } + + int numfd; + /* React on status change of any of the above file descriptor */ + if ((numfd=select.Select(1000)) < 0) { + if (!m_Active) // Exit was requested while polling + continue; + esyslog("[ffnetdev] Streamer: Fatal error, ffnetdev exiting: %s", strerror(errno)); + m_Active = false; + continue; + } + + + //DEBUG + /* + fprintf(stderr, "[ffnetdev] Streamer: Num_FD TS: %d", numfd); + + if (select.CanRead(m_StreamListen) || select.CanWrite(m_StreamListen)) + fprintf (stderr, "m_StreamListen can act.\n"); + if (select.CanRead(*m_StreamClient) || select.CanWrite(*m_StreamClient)) + fprintf (stderr, "m_StreamClient can act.\n"); + */ + + /* Accept connecting streaming clients */ + if ( (have_Streamclient==false)&&select.CanRead(m_StreamListen) ) { + if (m_StreamClient->Accept(m_StreamListen)) { + isyslog("[ffnetdev] Streamer: Accepted client %s:%d", + m_StreamClient->RemoteIp().c_str(), m_StreamClient->RemotePort()); + have_Streamclient = true; + + m_pPlugin->SetPrimaryDevice(); + } + else { + esyslog("[ffnetdev] Streamer: Couldn't accept : %s", strerror(errno)); + have_Streamclient = false; + m_Active = false; + continue; + } + } + + + /* Check for closed streaming client connection */ + if (have_Streamclient==true) { + if (close_Streamclient_request==true) { + close_Streamclient_request = false; + have_Streamclient = false; + + m_pPlugin->RestorePrimaryDevice(); + + if ( m_StreamClient->Close() ) { +#ifdef DEBUG + fprintf(stderr, "[ffnetdev] Streamer: Client socket closed successfully.\n"); +#endif + isyslog("[ffnetdev] Streamer: Connection closed: client %s:%d", + m_StreamClient->RemoteIp().c_str(), m_StreamClient->RemotePort()); + } + else + { +#ifdef DEBUG + fprintf(stderr, "[ffnetdev] Streamer: Error closing client socket.\n"); +#endif + esyslog("[ffnetdev] Streamer: Error closing connection."); + m_Active=false; + continue; + } + + } + + if ( select.CanWrite(*m_StreamClient) ) { + int count=0; + + m_StreamDevice->LockOutput(); + uchar *buffer = m_StreamDevice->Get(count); + if (buffer!=NULL) { + int available = count; + int done = 0; + int written = 0; + while ((available > 0) && (have_Streamclient == true) && + (!close_Streamclient_request)) + { + + if (((written=m_StreamClient->Write(&buffer[done], available)) < 0) && + (errno != EAGAIN)) + { + CloseStreamClient(); + } + + if (written > 0) + { + + available -= written; + done += written; + } + else + { + cCondWait::SleepMs(5); + } + } + m_StreamDevice->Del(count); + + struct timeval curtime; + gettimeofday(&curtime, 0); + if (oldtime.tv_sec == 0) + { + oldtime = curtime; + bytessend = 0; + oldbytessend = 0; + } + + bytessend += count; + if (curtime.tv_sec > oldtime.tv_sec + 10) + { + double secs = (curtime.tv_sec * 1000 + (curtime.tv_usec / 1000)) / 1000 + - (oldtime.tv_sec * 1000 + (oldtime.tv_usec / 1000)) / 1000; +#ifdef DEBUG + fprintf(stderr, "[ffnetdev] Streamer: current TransferRate %d Byte/Sec, %d Bytes send\n", + (int)((bytessend - oldbytessend) / secs), bytessend - oldbytessend); +#endif + dsyslog("[ffnetdev] Streamer: current TransferRate %d Byte/Sec, %d Bytes send\n", + (int)((bytessend - oldbytessend) / secs), bytessend - oldbytessend); + + oldbytessend = bytessend; + oldtime = curtime; + } + } + m_StreamDevice->UnlockOutput(); + } + + if ( select.CanRead(*m_StreamClient) ) + if ( m_StreamClient->Read(NULL, 1)==0 ) + CloseStreamClient(); + } + else { + /* simply discard all data in ringbuffer */ + int count=0; + if ( (m_StreamDevice->Get(count)) !=NULL ) { + m_StreamDevice->Del(count); +#ifdef DEBUG + fprintf (stderr, "[ffnetdev] Streamer: Bytes not sent, but deleted from ringbuffer: %d\n",count); +#endif + dsyslog("[ffnetdev] Streamer: Bytes not sent, but deleted from ringbuffer: %d\n",count); + } + } + cCondWait::SleepMs(3); + + } // while(m_Active) + +} + diff --git a/tsworker.h b/tsworker.h new file mode 100644 index 0000000..45e13ab --- /dev/null +++ b/tsworker.h @@ -0,0 +1,56 @@ +/* + * tsworker.h: TS worker thread + * + * See the README file for copyright information and how to reach the author. + * + */ + +#ifndef _TSWORKER__H +#define _TSWORKER__H + +#include + +#include "tools/socket.h" +#include "streamdevice.h" +#include "remote.h" +#include "netosd.h" +#include "ffnetdev.h" + + +// --- cTSWorker ------------------------------------------------------------- + +class cTSWorker : public cThread { +private: + bool m_Active; + bool have_Streamclient; + bool close_Streamclient_request; + static cTSWorker *m_Instance; + cStreamDevice *m_StreamDevice; + cTBSocket *m_StreamClient; + int TSPort; + int origPrimaryDevice; + cPluginFFNetDev *m_pPlugin; + +protected: + virtual void Action(void); + void Stop(void); +public: + cTSWorker(void); + virtual ~cTSWorker(); + + static void Init(cStreamDevice*, int, cPluginFFNetDev*); + static void Exit(void); + static bool Active(void); + + static bool HaveStreamClient(void); + static void CloseStreamClient(void); +}; + +inline bool cTSWorker::Active(void) { + return m_Instance && (m_Instance->have_Streamclient==true); +} + +inline bool cTSWorker::HaveStreamClient(void) { + return m_Instance->have_Streamclient; +} +#endif diff --git a/vncEncodeCoRRE.c b/vncEncodeCoRRE.c new file mode 100644 index 0000000..af398f3 --- /dev/null +++ b/vncEncodeCoRRE.c @@ -0,0 +1,536 @@ +// Copyright (C) 2000 Tridia Corporation. All Rights Reserved. +// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. +// +// This file is part of the VNC system. +// +// The VNC system 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 program 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 program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +// USA. +// +// TightVNC distribution homepage on the Web: http://www.tightvnc.com/ +// +// If the source code for the VNC system is not available from the place +// whence you received this file, check http://www.uk.research.att.com/vnc or contact +// the authors on vnc@uk.research.att.com for information on obtaining it. + + +// vncEncodeCoRRE + +// This file implements the vncEncoder-derived vncEncodeCoRRE class. +// This class overrides some vncEncoder functions to produce a +// Compact RRE encoder. Compact RRE (CoRRE) uses fewer bytes to +// encode each subrect, which makes it faster in general. It also +// splits large rectangles up into ones of at most 256 pixels width +// & height. This results in better granularity to use for deciding +// whether to send RAW or CoRRE/RRE. + +#include "vncEncodeCoRRE.h" +#include "rfb.h" +#include "MinMax.h" +#include +#include + +vncEncodeCoRRE::vncEncodeCoRRE() +{ + m_buffer = NULL; + m_bufflen = 0; + + // Set some sensible defaults + m_maxwidth = 24; + m_maxheight = 24; + m_maxadjust = 1; + + // Set the threshold up/down probability + m_threshold = 50; + + // Seed the random number generator + srand((unsigned)time(NULL)); + + m_statsready = false; + m_encodedbytes = 0; + m_rectbytes = 0; +} + +vncEncodeCoRRE::~vncEncodeCoRRE() +{ + if (m_buffer != NULL) + { + delete [] m_buffer; + m_buffer = NULL; + } +} + +void vncEncodeCoRRE::Init() +{ + vncEncoder::Init(); +} + +UINT vncEncodeCoRRE::RequiredBuffSize(UINT width, UINT height) +{ + RECT fullscreen; + UINT codedrects; + + // Work out how many rectangles the entire screen would + // be re-encoded to... + fullscreen.left = 0; + fullscreen.top = 0; + fullscreen.right = width; + fullscreen.bottom = height; + codedrects = NumCodedRects(fullscreen); + + // The buffer size required is the size of raw data for the whole + // screen plus enough space for the required number of rectangle + // headers. + // This is inherently always greater than the RAW encoded size of + // the whole screen! + return (codedrects * sz_rfbFramebufferUpdateRectHeader) + + (width * height * m_remoteformat.bitsPerPixel)/8; +} + +UINT +vncEncodeCoRRE::NumCodedRects(RECT &rect) +{ + // If we have any statistical data handy then adjust the CoRRE sizes + if (m_statsready) + { + m_statsready = false; + + UINT newscore = m_encodedbytes * m_lastrectbytes; + UINT oldscore = m_lastencodedbytes * m_rectbytes; + + if (newscore <= oldscore) + { + // The change was a good one, so adjust the threshold accordingly! + m_threshold = Max(5, Min(95, m_threshold + m_maxadjust)); + + m_maxwidth = Max(8, Min(255, m_maxwidth + m_maxadjust)); + m_maxheight = Max(8, Min(255, m_maxheight + m_maxadjust)); + } + else + { + // The change was a bad one, so adjust the threshold accordingly! + // m_threshold = Max(5, Min(95, m_threshold - m_maxadjust)); + } + + // Now calculate a new adjustment and apply it + m_maxadjust = ((rand() % 99) m_maxheight) + { + RECT subrect1, subrect2; + + // Find how many rects the two subrects would take + subrect1.left = rect.left; + subrect1.right = rect.right; + subrect1.top = rect.top; + subrect1.bottom = rect.top + m_maxheight; + + subrect2.left = rect.left; + subrect2.right = rect.right; + subrect2.top = rect.top + m_maxheight; + subrect2.bottom = rect.bottom; + + return NumCodedRects(subrect1) + NumCodedRects(subrect2); + } + + if ((rect.right-rect.left) > m_maxwidth) + { + RECT subrect1, subrect2; + + // Find how many rects the two subrects would take + subrect1.left = rect.left; + subrect1.right = rect.left + m_maxwidth; + subrect1.top = rect.top; + subrect1.bottom = rect.bottom; + + subrect2.left = rect.left + m_maxwidth; + subrect2.right = rect.right; + subrect2.top = rect.top; + subrect2.bottom = rect.bottom; + return NumCodedRects(subrect1) + NumCodedRects(subrect2); + } + + // This rectangle is small enough not to require splitting + return 1; +} + +/* + * corre.c + * + * Routines to implement Compact Rise-and-Run-length Encoding (CoRRE). This + * code is based on krw's original javatel rfbserver. + */ + +/* + * This version modified for WinVNC by jnw. + */ + +static int rreAfterBufLen; + +static int subrectEncode8 (CARD8 *source, CARD8 *dest, int w, int h, int max); +static int subrectEncode16 (CARD16 *source, CARD8 *dest, int w, int h, int max); +static int subrectEncode32 (CARD32 *source, CARD8 *dest, int w, int h, int max); +static CARD32 getBgColour (char *data, int size, int bpp); + +/* + * vncEncodeCoRRE::EncodeRect - send an arbitrary size rectangle using CoRRE + * encoding. + */ + +UINT +vncEncodeCoRRE::EncodeRect(BYTE *source, BYTE *dest, const RECT &rect) +{ + // Do the encoding + UINT size = InternalEncodeRect(source, dest, rect); + + const int rectW = rect.right - rect.left; + const int rectH = rect.bottom - rect.top; + + // Will this rectangle have been split for encoding? + if ((rectW>m_maxwidth) || (rectH>m_maxheight)) + { + // Yes : Once we return, the stats will be valid! + m_statsready = true; + + // Update the stats + m_encodedbytes += size; + m_rectbytes += sz_rfbFramebufferUpdateRectHeader + + (rectW*rectH*m_remoteformat.bitsPerPixel/8); + } + + return size; +} + +UINT +vncEncodeCoRRE::InternalEncodeRect(BYTE *source, BYTE *dest, const RECT &rect) +{ + int size = 0; + + if ((rect.bottom-rect.top) > m_maxheight) + { + RECT subrect; + + // Rectangle is too high - split it into two subrects to send + subrect.left = rect.left; + subrect.right = rect.right; + subrect.top = rect.top; + subrect.bottom = rect.top + m_maxheight; + size += InternalEncodeRect(source, dest + size, subrect); + + subrect.left = rect.left; + subrect.right = rect.right; + subrect.top = rect.top + m_maxheight; + subrect.bottom = rect.bottom; + size += InternalEncodeRect(source, dest + size, subrect); + + return size; + } + + if ((rect.right-rect.left) > m_maxwidth) + { + RECT subrect; + + // Rectangle is too high - split it into two subrects to send + subrect.left = rect.left; + subrect.right = rect.left + m_maxwidth; + subrect.top = rect.top; + subrect.bottom = rect.bottom; + size += InternalEncodeRect(source, dest + size, subrect); + + subrect.left = rect.left + m_maxwidth; + subrect.right = rect.right; + subrect.top = rect.top; + subrect.bottom = rect.bottom; + size += InternalEncodeRect(source, dest + size, subrect); + + return size; + } + + return EncodeSmallRect(source, dest, rect); +} + +void +vncEncodeCoRRE::SetCoRREMax(BYTE width, BYTE height) +{ + m_maxwidth = width; + m_maxheight = height; +} + +/* + * EncodeSmallRect - send a small (guaranteed < 256x256) + * rectangle using CoRRE encoding. + */ + +UINT +vncEncodeCoRRE::EncodeSmallRect(BYTE *source, BYTE *dest, const RECT &rect) +{ + int subrects = -1; + + const UINT rectW = rect.right - rect.left; + const UINT rectH = rect.bottom - rect.top; + + // Create the rectangle header + rfbFramebufferUpdateRectHeader *surh=(rfbFramebufferUpdateRectHeader *)dest; + surh->r.x = (CARD16) rect.left; + surh->r.y = (CARD16) rect.top; + surh->r.w = (CARD16) (rectW); + surh->r.h = (CARD16) (rectH); + surh->r.x = Swap16IfLE(surh->r.x); + surh->r.y = Swap16IfLE(surh->r.y); + surh->r.w = Swap16IfLE(surh->r.w); + surh->r.h = Swap16IfLE(surh->r.h); + surh->encoding = Swap32IfLE(rfbEncodingCoRRE); + + // create a space big enough for the CoRRE encoded pixels + + size_t rectSize = rectW * rectH * (m_remoteformat.bitsPerPixel / 8); + if (m_bufflen < rectSize) + { + if (m_buffer != NULL) + { + delete [] m_buffer; + m_buffer = NULL; + } + m_buffer = new BYTE [rectSize + 1]; + if (m_buffer == NULL) + return vncEncoder::EncodeRect(source, dest, rect); + + m_bufflen = rectSize; + } + + // Translate the data into our new buffer + Translate(source, m_buffer, rect); + + // The Buffer object will have ensured that the destination buffer is + // big enough using RequiredBuffSize + + // Choose the appropriate encoding routine (for speed...) + switch(m_remoteformat.bitsPerPixel) + { + case 8: + subrects = subrectEncode8( + m_buffer, + dest+sz_rfbFramebufferUpdateRectHeader+sz_rfbRREHeader, + rectW, + rectH, + m_bufflen-sz_rfbFramebufferUpdateRectHeader-sz_rfbRREHeader + ); + break; + case 16: + subrects = subrectEncode16( + (CARD16 *)m_buffer, + (CARD8 *)(dest+sz_rfbFramebufferUpdateRectHeader+sz_rfbRREHeader), + rectW, + rectH, + m_bufflen-sz_rfbFramebufferUpdateRectHeader-sz_rfbRREHeader + ); + break; + case 32: + subrects = subrectEncode32( + (CARD32 *)m_buffer, + (CARD8 *)(dest+sz_rfbFramebufferUpdateRectHeader+sz_rfbRREHeader), + rectW, + rectH, + m_bufflen-sz_rfbFramebufferUpdateRectHeader-sz_rfbRREHeader + ); + break; + } + + // If we couldn't encode the rectangles then just send the data raw + if (subrects < 0) + return vncEncoder::EncodeRect(source, dest, rect); + + // Send the RREHeader + rfbRREHeader *rreh=(rfbRREHeader *)(dest+sz_rfbFramebufferUpdateRectHeader); + rreh->nSubrects = Swap32IfLE(subrects); + + // Update the statistics for this rectangle. + encodedSize += sz_rfbRREHeader + rreAfterBufLen; + rectangleOverhead += sz_rfbFramebufferUpdateRectHeader; + dataSize += ( rectW * rectH * m_remoteformat.bitsPerPixel) / 8; + transmittedSize += sz_rfbFramebufferUpdateRectHeader + sz_rfbRREHeader + rreAfterBufLen; + + // Calculate the size of the buffer produced + return sz_rfbFramebufferUpdateRectHeader + sz_rfbRREHeader + rreAfterBufLen; +} + +/* + * subrectEncode() encodes the given multicoloured rectangle as a background + * colour overwritten by single-coloured rectangles. It returns the number + * of subrectangles in the encoded buffer, or -1 if subrect encoding won't + * fit in the buffer. It puts the encoded rectangles in rreAfterBuf. The + * single-colour rectangle partition is not optimal, but does find the biggest + * horizontal or vertical rectangle top-left anchored to each consecutive + * coordinate position. + * + * The coding scheme is simply [...] where each + * is []. + */ + +#define DEFINE_SUBRECT_ENCODE(bpp) \ +static int \ +subrectEncode##bpp( \ + CARD##bpp *source, \ + CARD8 *dest, \ + int w, \ + int h, \ + int maxbytes) \ +{ \ + CARD##bpp cl; \ + rfbCoRRERectangle subrect; \ + int x,y; \ + int i,j; \ + int hx=0,hy,vx=0,vy; \ + int hyflag; \ + CARD##bpp *seg; \ + CARD##bpp *line; \ + int hw,hh,vw,vh; \ + int thex,they,thew,theh; \ + int numsubs = 0; \ + int newLen; \ + CARD##bpp bg = (CARD##bpp)getBgColour((char*)source,w*h,bpp); \ + \ + *((CARD##bpp*)dest) = bg; \ + \ + rreAfterBufLen = (bpp/8); \ + \ + for (y=0; y 0) && (i >= hx)) {hy += 1;} else {hyflag = 0;} \ + } \ + vy = j-1; \ + \ + /* We now have two possible subrects: (x,y,hx,hy) and (x,y,vx,vy) \ + * We'll choose the bigger of the two. \ + */ \ + hw = hx-x+1; \ + hh = hy-y+1; \ + vw = vx-x+1; \ + vh = vy-y+1; \ + \ + thex = x; \ + they = y; \ + \ + if ((hw*hh) > (vw*vh)) { \ + thew = hw; \ + theh = hh; \ + } else { \ + thew = vw; \ + theh = vh; \ + } \ + \ + subrect.x = thex; \ + subrect.y = they; \ + subrect.w = thew; \ + subrect.h = theh; \ + \ + newLen = rreAfterBufLen + (bpp/8) + sz_rfbCoRRERectangle; \ + if ((newLen > (w * h * (bpp/8))) || (newLen > maxbytes)) \ + return -1; \ + \ + numsubs += 1; \ + *((CARD##bpp*)(dest + rreAfterBufLen)) = cl; \ + rreAfterBufLen += (bpp/8); \ + memcpy(&dest[rreAfterBufLen],&subrect,sz_rfbCoRRERectangle); \ + rreAfterBufLen += sz_rfbCoRRERectangle; \ + \ + /* \ + * Now mark the subrect as done. \ + */ \ + for (j=they; j < (they+theh); j++) { \ + for (i=thex; i < (thex+thew); i++) { \ + source[j*w+i] = bg; \ + } \ + } \ + } \ + } \ + } \ + \ + return numsubs; \ +} + +DEFINE_SUBRECT_ENCODE(8) +DEFINE_SUBRECT_ENCODE(16) +DEFINE_SUBRECT_ENCODE(32) + +/* + * getBgColour() gets the most prevalent colour in a byte array. + */ +static CARD32 +getBgColour( + char *data, + int size, + int bpp) +{ + +#define NUMCLRS 256 + + static int counts[NUMCLRS]; + int i,j,k; + + int maxcount = 0; + CARD8 maxclr = 0; + + if (bpp != 8) { + if (bpp == 16) { + return ((CARD16 *)data)[0]; + } else if (bpp == 32) { + return ((CARD32 *)data)[0]; + } else { + fprintf(stderr,"getBgColour: bpp %d?\n",bpp); + exit(1); + } + } + + for (i=0; i= NUMCLRS) { + fprintf(stderr, "%s: unusual colour = %d\n", "getBgColour",k); + exit(1); + } + counts[k] += 1; + if (counts[k] > maxcount) { + maxcount = counts[k]; + maxclr = ((CARD8 *)data)[j]; + } + } + + return maxclr; +} diff --git a/vncEncodeCoRRE.h b/vncEncodeCoRRE.h new file mode 100644 index 0000000..6fbd752 --- /dev/null +++ b/vncEncodeCoRRE.h @@ -0,0 +1,84 @@ +// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. +// +// This file is part of the VNC system. +// +// The VNC system 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 program 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 program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +// USA. +// +// TightVNC distribution homepage on the Web: http://www.tightvnc.com/ +// +// If the source code for the VNC system is not available from the place +// whence you received this file, check http://www.uk.research.att.com/vnc or contact +// the authors on vnc@uk.research.att.com for information on obtaining it. + + +// vncEncodeCoRRE object + +// The vncEncodeCoRRE object uses a compression encoding to send rectangles +// to a client + +class vncEncodeCoRRE; + +#if !defined(_WINVNC_ENCODECORRRE) +#define _WINVNC_ENCODECORRE +#pragma once + +#include "vncEncoder.h" +#include "vdr/plugin.h" + +// Class definition + +class vncEncodeCoRRE : public vncEncoder +{ +// Fields +public: + +// Methods +public: + // Create/Destroy methods + vncEncodeCoRRE(); + ~vncEncodeCoRRE(); + + virtual void Init(); + virtual const char* GetEncodingName() { return "CoRRE"; } + + virtual UINT RequiredBuffSize(UINT width, UINT height); + virtual UINT NumCodedRects(RECT &rect); + + virtual UINT EncodeRect(BYTE *source, BYTE *dest, const RECT &rect); + virtual void SetCoRREMax(BYTE width, BYTE height); +protected: + virtual UINT InternalEncodeRect(BYTE *source, BYTE *dest, const RECT &rect); + virtual UINT EncodeSmallRect(BYTE *source, BYTE *dest, const RECT &rect); + +// Implementation +protected: + BYTE *m_buffer; + size_t m_bufflen; + + // Maximum height & width for CoRRE + int m_maxwidth; + int m_maxheight; + + // Last-update stats for CoRRE + UINT m_encodedbytes, m_rectbytes; + UINT m_lastencodedbytes, m_lastrectbytes; + int m_maxadjust; + int m_threshold; + bool m_statsready; +}; + +#endif // _WINVNC_ENCODECORRE + diff --git a/vncEncodeHexT.c b/vncEncodeHexT.c new file mode 100644 index 0000000..ce7c3f0 --- /dev/null +++ b/vncEncodeHexT.c @@ -0,0 +1,412 @@ +// Copyright (C) 2000 Tridia Corporation. All Rights Reserved. +// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. +// +// This file is part of the VNC system. +// +// The VNC system 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 program 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 program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +// USA. +// +// TightVNC distribution homepage on the Web: http://www.tightvnc.com/ +// +// If the source code for the VNC system is not available from the place +// whence you received this file, check http://www.uk.research.att.com/vnc or contact +// the authors on vnc@uk.research.att.com for information on obtaining it. + + +// vncEncodeHexT + +// This file implements the vncEncoder-derived vncEncodeHexT class. +// This class overrides some vncEncoder functions to produce a +// Hextile encoder. Hextile splits all top-level update rectangles +// into smaller, 16x16 rectangles and encodes these using the +// optimised Hextile sub-encodings. + +#include "vncEncodeHexT.h" +#include "rfb.h" +#include "MinMax.h" +#include +#include + +vncEncodeHexT::vncEncodeHexT() +{ +} + +vncEncodeHexT::~vncEncodeHexT() +{ +} + +void +vncEncodeHexT::Init() +{ + vncEncoder::Init(); +} + +UINT +vncEncodeHexT::RequiredBuffSize(UINT width, UINT height) +{ + return vncEncoder::RequiredBuffSize(width, height) + (((width/16)+1) * ((height/16)+1)); +} + +UINT +vncEncodeHexT::NumCodedRects(RECT &rect) +{ + return 1; +} + +/* + * hextile.c + * + * Routines to implement Hextile Encoding + */ + +#include +#include "rfb.h" + +/* + * vncEncodeHexT::EncodeRect - send a rectangle using hextile encoding. + */ + +UINT +vncEncodeHexT::EncodeRect(BYTE *source, BYTE *dest, const RECT &rect) +{ + const UINT rectW = rect.right - rect.left; + const UINT rectH = rect.bottom - rect.top; + int encodedResult; + + // Create the rectangle header + rfbFramebufferUpdateRectHeader *surh=(rfbFramebufferUpdateRectHeader *)dest; + surh->r.x = (CARD16) rect.left; + surh->r.y = (CARD16) rect.top; + surh->r.w = (CARD16) (rectW); + surh->r.h = (CARD16) (rectH); + surh->r.x = Swap16IfLE(surh->r.x); + surh->r.y = Swap16IfLE(surh->r.y); + surh->r.w = Swap16IfLE(surh->r.w); + surh->r.h = Swap16IfLE(surh->r.h); + surh->encoding = Swap32IfLE(rfbEncodingHextile); + + rectangleOverhead += sz_rfbFramebufferUpdateRectHeader; + dataSize += ( rectW * rectH * m_remoteformat.bitsPerPixel) / 8; + + // Do the encoding + switch (m_remoteformat.bitsPerPixel) + { + case 8: + encodedResult = EncodeHextiles8(source, dest + sz_rfbFramebufferUpdateRectHeader, + rect.left, rect.top, rectW, rectH); + encodedSize += encodedResult; + transmittedSize += sz_rfbFramebufferUpdateRectHeader + encodedResult; + return sz_rfbFramebufferUpdateRectHeader + encodedResult; + case 16: + encodedResult = EncodeHextiles16(source, dest + sz_rfbFramebufferUpdateRectHeader, + rect.left, rect.top, rectW, rectH); + encodedSize += encodedResult; + transmittedSize += sz_rfbFramebufferUpdateRectHeader + encodedResult; + return sz_rfbFramebufferUpdateRectHeader + encodedResult; + case 32: + encodedResult = EncodeHextiles32(source, dest + sz_rfbFramebufferUpdateRectHeader, + rect.left, rect.top, rectW, rectH); + encodedSize += encodedResult; + transmittedSize += sz_rfbFramebufferUpdateRectHeader + encodedResult; + return sz_rfbFramebufferUpdateRectHeader + encodedResult; + } + + return vncEncoder::EncodeRect(source, dest, rect); +} + +#define PUT_PIXEL8(pix) (dest[destoffset++] = (pix)) + +#define PUT_PIXEL16(pix) (dest[destoffset++] = ((char*)&(pix))[0], \ + dest[destoffset++] = ((char*)&(pix))[1]) + +#define PUT_PIXEL32(pix) (dest[destoffset++] = ((char*)&(pix))[0], \ + dest[destoffset++] = ((char*)&(pix))[1], \ + dest[destoffset++] = ((char*)&(pix))[2], \ + dest[destoffset++] = ((char*)&(pix))[3]) + +#define DEFINE_SEND_HEXTILES(bpp) \ + \ +static UINT subrectEncode##bpp(CARD##bpp *src, BYTE *dest, \ + int w, int h, CARD##bpp bg, \ + CARD##bpp fg, bool mono); \ +static void testColours##bpp(CARD##bpp *data, int size, bool *mono, \ + bool *solid, CARD##bpp *bg, CARD##bpp *fg); \ + \ + \ +/* \ + * rfbSendHextiles \ + */ \ + \ +UINT \ +vncEncodeHexT::EncodeHextiles##bpp(BYTE *source, BYTE *dest, \ + int rx, int ry, int rw, int rh) \ +{ \ + int x, y, w, h; \ + int rectoffset, destoffset; \ + CARD##bpp bg, fg, newBg, newFg; \ + bool mono, solid; \ + bool validBg = false; \ + CARD##bpp clientPixelData[16*16*(bpp/8)]; \ + bool validFg = false; \ + \ + destoffset = 0; \ + \ + for (y = ry; y < ry+rh; y += 16) \ + { \ + for (x = rx; x < rx+rw; x += 16) \ + { \ + w = h = 16; \ + if (rx+rw - x < 16) \ + w = rx+rw - x; \ + if (ry+rh - y < 16) \ + h = ry+rh - y; \ + \ + RECT hexrect; \ + hexrect.left = x; \ + hexrect.top = y; \ + hexrect.right = x+w; \ + hexrect.bottom = y+h; \ + Translate(source, (BYTE *) &clientPixelData, hexrect); \ + \ + rectoffset = destoffset; \ + dest[rectoffset] = 0; \ + destoffset++; \ + \ + testColours##bpp(clientPixelData, w * h, \ + &mono, &solid, &newBg, &newFg); \ + \ + if (!validBg || (newBg != bg)) \ + { \ + validBg = true; \ + bg = newBg; \ + dest[rectoffset] |= rfbHextileBackgroundSpecified; \ + PUT_PIXEL##bpp(bg); \ + } \ + \ + if (solid) \ + continue; \ + \ + dest[rectoffset] |= rfbHextileAnySubrects; \ + \ + if (mono) \ + { \ + if (!validFg || (newFg != fg)) \ + { \ + validFg = true; \ + fg = newFg; \ + dest[rectoffset] |= rfbHextileForegroundSpecified; \ + PUT_PIXEL##bpp(fg); \ + } \ + } \ + else \ + { \ + validFg = false; \ + dest[rectoffset] |= rfbHextileSubrectsColoured; \ + } \ + \ + int encodedbytes = subrectEncode##bpp(clientPixelData, \ + dest + destoffset, \ + w, h, bg, fg, mono); \ + destoffset += encodedbytes; \ + if (encodedbytes == 0) \ + { \ + /* encoding was too large, use raw */ \ + validBg = false; \ + validFg = false; \ + destoffset = rectoffset; \ + dest[destoffset++] = rfbHextileRaw; \ + \ + Translate(source, (BYTE *) (dest + destoffset), hexrect); \ + \ + destoffset += w * h * (bpp/8); \ + } \ + } \ + } \ + \ + return destoffset; \ +} \ + \ +static UINT \ +subrectEncode##bpp(CARD##bpp *src, BYTE *dest, int w, int h, CARD##bpp bg, \ + CARD##bpp fg, bool mono) \ +{ \ + CARD##bpp cl; \ + int x,y; \ + int i,j; \ + int hx=0,hy,vx=0,vy; \ + int hyflag; \ + CARD##bpp *seg; \ + CARD##bpp *line; \ + int hw,hh,vw,vh; \ + int thex,they,thew,theh; \ + int numsubs = 0; \ + int newLen; \ + int rectoffset; \ + int destoffset; \ + \ + destoffset = 0; \ + rectoffset = destoffset; \ + destoffset++; \ + \ + for (y=0; y 0) && (i >= hx)) \ + { \ + hy += 1; \ + } \ + else \ + { \ + hyflag = 0; \ + } \ + } \ + vy = j-1; \ + \ + /* We now have two possible subrects: (x,y,hx,hy) and \ + * (x,y,vx,vy). We'll choose the bigger of the two. \ + */ \ + hw = hx-x+1; \ + hh = hy-y+1; \ + vw = vx-x+1; \ + vh = vy-y+1; \ + \ + thex = x; \ + they = y; \ + \ + if ((hw*hh) > (vw*vh)) \ + { \ + thew = hw; \ + theh = hh; \ + } \ + else \ + { \ + thew = vw; \ + theh = vh; \ + } \ + \ + if (mono) \ + { \ + newLen = destoffset - rectoffset + 2; \ + } \ + else \ + { \ + newLen = destoffset - rectoffset + bpp/8 + 2; \ + } \ + \ + if (newLen > (w * h * (bpp/8))) \ + return 0; \ + \ + numsubs += 1; \ + \ + if (!mono) PUT_PIXEL##bpp(cl); \ + \ + dest[destoffset++] = rfbHextilePackXY(thex,they); \ + dest[destoffset++] = rfbHextilePackWH(thew,theh); \ + \ + /* \ + * Now mark the subrect as done. \ + */ \ + for (j=they; j < (they+theh); j++) \ + { \ + for (i=thex; i < (thex+thew); i++) \ + { \ + src[j*w+i] = bg; \ + } \ + } \ + } \ + } \ + } \ + \ + dest[rectoffset] = numsubs; \ + \ + return destoffset; \ +} \ + \ + \ +/* \ + * testColours() tests if there are one (solid), two (mono) or more \ + * colours in a tile and gets a reasonable guess at the best background \ + * pixel, and the foreground pixel for mono. \ + */ \ + \ +static void \ +testColours##bpp(CARD##bpp *data, int size, \ + bool *mono, bool *solid, \ + CARD##bpp *bg, CARD##bpp *fg) \ +{ \ + CARD##bpp colour1, colour2; \ + int n1 = 0, n2 = 0; \ + *mono = true; \ + *solid = true; \ + \ + for (; size > 0; size--, data++) \ + { \ + \ + if (n1 == 0) \ + colour1 = *data; \ + \ + if (*data == colour1) \ + { \ + n1++; \ + continue; \ + } \ + \ + if (n2 == 0) \ + { \ + *solid = false; \ + colour2 = *data; \ + } \ + \ + if (*data == colour2) \ + { \ + n2++; \ + continue; \ + } \ + \ + *mono = false; \ + break; \ + } \ + \ + if (n1 > n2) \ + { \ + *bg = colour1; \ + *fg = colour2; \ + } \ + else \ + { \ + *bg = colour2; \ + *fg = colour1; \ + } \ +} + +DEFINE_SEND_HEXTILES(8) +DEFINE_SEND_HEXTILES(16) +DEFINE_SEND_HEXTILES(32) diff --git a/vncEncodeHexT.h b/vncEncodeHexT.h new file mode 100644 index 0000000..828934a --- /dev/null +++ b/vncEncodeHexT.h @@ -0,0 +1,75 @@ +// Copyright (C) 2000 Tridia Corporation. All Rights Reserved. +// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. +// +// This file is part of the VNC system. +// +// The VNC system 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 program 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 program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +// USA. +// +// TightVNC distribution homepage on the Web: http://www.tightvnc.com/ +// +// If the source code for the VNC system is not available from the place +// whence you received this file, check http://www.uk.research.att.com/vnc or contact +// the authors on vnc@uk.research.att.com for information on obtaining it. + + +// vncEncodeHexT object + +// The vncEncodeHexT object uses a compression encoding to send rectangles +// to a client + +class vncEncodeHexT; + +#if !defined(_WINVNC_ENCODEHEXTILE) +#define _WINVNC_ENCODEHEXTILE +#pragma once + +#include "vncEncoder.h" + +// Class definition + +class vncEncodeHexT : public vncEncoder +{ +// Fields +public: + +// Methods +public: + // Create/Destroy methods + vncEncodeHexT(); + ~vncEncodeHexT(); + + virtual void Init(); + virtual const char* GetEncodingName() { return "Hextile"; } + + virtual UINT RequiredBuffSize(UINT width, UINT height); + virtual UINT NumCodedRects(RECT &rect); + + virtual UINT EncodeRect(BYTE *source, BYTE *dest, const RECT &rect); + +protected: + virtual UINT EncodeHextiles8(BYTE *source, BYTE *dest, + int x, int y, int w, int h); + virtual UINT EncodeHextiles16(BYTE *source, BYTE *dest, + int x, int y, int w, int h); + virtual UINT EncodeHextiles32(BYTE *source, BYTE *dest, + int x, int y, int w, int h); + +// Implementation +protected: +}; + +#endif // _WINVNC_ENCODEHEXTILE + diff --git a/vncEncodeRRE.c b/vncEncodeRRE.c new file mode 100644 index 0000000..3a9d44e --- /dev/null +++ b/vncEncodeRRE.c @@ -0,0 +1,349 @@ +// Copyright (C) 2000 Tridia Corporation. All Rights Reserved. +// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. +// +// This file is part of the VNC system. +// +// The VNC system 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 program 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 program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +// USA. +// +// TightVNC distribution homepage on the Web: http://www.tightvnc.com/ +// +// If the source code for the VNC system is not available from the place +// whence you received this file, check http://www.uk.research.att.com/vnc or contact +// the authors on vnc@uk.research.att.com for information on obtaining it. + + +// vncEncodeRRE + +// This file implements the vncEncoder-derived vncEncodeRRE class. +// This class overrides some vncEncoder functions to produce a bitmap +// to RRE encoder. RRE is much more efficient than RAW format on +// most screen data. + +#include +#include +#include "vncEncodeRRE.h" + +vncEncodeRRE::vncEncodeRRE() +{ + m_buffer = NULL; + m_bufflen = 0; +} + +vncEncodeRRE::~vncEncodeRRE() +{ + if (m_buffer != NULL) + { + delete [] m_buffer; + m_buffer = NULL; + } +} + +void +vncEncodeRRE::Init() +{ + vncEncoder::Init(); +} + +UINT +vncEncodeRRE::RequiredBuffSize(UINT width, UINT height) +{ + return vncEncoder::RequiredBuffSize(width, height); +} + +UINT +vncEncodeRRE::NumCodedRects(RECT &rect) +{ + return 1; +} + +/***************************************************************************** + * + * Routines to implement Rise-and-Run-length Encoding (RRE). This code is + * based on krw's original javatel rfbserver. + * This code courtesy of tjr + */ + +/* + * rreBeforeBuf contains pixel data in the client's format. + * rreAfterBuf contains the RRE encoded version. If the RRE encoded version is + * larger than the raw data or if it exceeds rreAfterBufSize then + * normal encoding is used instead. + */ + +static int rreAfterBufLen; + +static int subrectEncode8 (CARD8 *data, CARD8 *buf, int w, int h, int maxBytes); +static int subrectEncode16 (CARD16 *data, CARD8 *buf, int w, int h, int maxBytes); +static int subrectEncode32 (CARD32 *data, CARD8 *buf, int w, int h, int maxBytes); +static CARD32 getBgColour (char *data, int size, int bpp); + +/* + * subrectEncode() encodes the given multicoloured rectangle as a background + * colour overwritten by single-coloured rectangles. It returns the number + * of subrectangles in the encoded buffer, or -1 if subrect encoding won't + * fit in the buffer. It puts the encoded rectangles in rreAfterBuf. The + * single-colour rectangle partition is not optimal, but does find the biggest + * horizontal or vertical rectangle top-left anchored to each consecutive + * coordinate position. + * + * The coding scheme is simply [...] where each + * is []. + * + * This code has been modified from tjr's original by Wez(jnw) + */ + +#define DEFINE_SUBRECT_ENCODE(bpp) \ +static int \ +subrectEncode##bpp( \ + CARD##bpp *data, \ + CARD8 *buf, \ + int w, \ + int h, \ + int maxBytes \ + ) \ +{ \ + CARD##bpp cl; \ + rfbRectangle subrect; \ + int x,y; \ + int i,j; \ + int hx,hy,vx,vy; \ + int hyflag; \ + CARD##bpp *seg; \ + CARD##bpp *line; \ + int hw,hh,vw,vh; \ + int thex,they,thew,theh; \ + int numsubs = 0; \ + int newLen; \ + CARD##bpp bg = (CARD##bpp)getBgColour((char*)data,w*h,bpp); \ + \ + /* Set the background colour value */ \ + *((CARD##bpp *)buf) = bg; \ + \ + rreAfterBufLen = (bpp/8); \ + \ + for (y=0; y 0) && (i >= hx)) {hy += 1;} else {hyflag = 0;} \ + } \ + vy = j-1; \ + \ + /* We now have two possible subrects: (x,y,hx,hy) and (x,y,vx,vy) \ + * We'll choose the bigger of the two. \ + */ \ + hw = hx-x+1; \ + hh = hy-y+1; \ + vw = vx-x+1; \ + vh = vy-y+1; \ + \ + thex = x; \ + they = y; \ + \ + if ((hw*hh) > (vw*vh)) { \ + thew = hw; \ + theh = hh; \ + } else { \ + thew = vw; \ + theh = vh; \ + } \ + \ + subrect.x = Swap16IfLE(thex); \ + subrect.y = Swap16IfLE(they); \ + subrect.w = Swap16IfLE(thew); \ + subrect.h = Swap16IfLE(theh); \ + \ + newLen = rreAfterBufLen + (bpp/8) + sz_rfbRectangle; \ + if ((newLen > (w * h * (bpp/8))) || (newLen > maxBytes)) \ + return -1; \ + \ + numsubs += 1; \ + *((CARD##bpp *)(buf + rreAfterBufLen)) = cl; \ + rreAfterBufLen += (bpp/8); \ + memcpy(&buf[rreAfterBufLen],&subrect, sz_rfbRectangle); \ + rreAfterBufLen += sz_rfbRectangle; \ + \ + /* \ + * Now mark the subrect as done. \ + */ \ + for (j=they; j < (they+theh); j++) { \ + for (i=thex; i < (thex+thew); i++) { \ + data[j*w+i] = bg; \ + } \ + } \ + } \ + } \ + } \ + \ + return numsubs; \ +} + +DEFINE_SUBRECT_ENCODE(8) +DEFINE_SUBRECT_ENCODE(16) +DEFINE_SUBRECT_ENCODE(32) + +/* + * getBgColour() gets the most prevalent colour in a byte array. + */ +static CARD32 +getBgColour(char *data, int size, int bpp) +{ + +#define NUMCLRS 256 + + static int counts[NUMCLRS]; + int i,j,k; + + int maxcount = 0; + CARD8 maxclr = 0; + + if (bpp != 8) { + if (bpp == 16) { + return ((CARD16 *)data)[0]; + } else if (bpp == 32) { + return ((CARD32 *)data)[0]; + } else { + fprintf(stderr,"getBgColour: bpp %d?\n",bpp); + return 0; + } + } + + for (i=0; i= NUMCLRS) { + fprintf(stderr, "%s: unusual colour = %d\n", "getBgColour",k); + return 0; + } + counts[k] += 1; + if (counts[k] > maxcount) { + maxcount = counts[k]; + maxclr = ((CARD8 *)data)[j]; + } + } + + return maxclr; +} + +// Encode the rectangle using RRE +inline UINT +vncEncodeRRE::EncodeRect(BYTE *source, BYTE *dest, const RECT &rect) +{ + int subrects = -1; + + const UINT rectW = rect.right - rect.left; + const UINT rectH = rect.bottom - rect.top; + + // Create the rectangle header + rfbFramebufferUpdateRectHeader *surh=(rfbFramebufferUpdateRectHeader *)dest; + surh->r.x = (CARD16) rect.left; + surh->r.y = (CARD16) rect.top; + surh->r.w = (CARD16) (rectW); + surh->r.h = (CARD16) (rectH); + surh->r.x = Swap16IfLE(surh->r.x); + surh->r.y = Swap16IfLE(surh->r.y); + surh->r.w = Swap16IfLE(surh->r.w); + surh->r.h = Swap16IfLE(surh->r.h); + surh->encoding = Swap32IfLE(rfbEncodingRRE); + + // create a space big enough for the RRE encoded pixels + size_t rectSize = rectW * rectH * (m_remoteformat.bitsPerPixel / 8); + if (m_bufflen < rectSize) + { + if (m_buffer != NULL) + { + delete [] m_buffer; + m_buffer = NULL; + } + m_buffer = new BYTE [rectSize + 1]; + if (m_buffer == NULL) + return vncEncoder::EncodeRect(source, dest, rect); + m_bufflen = rectSize; + } + + // Translate the data into our new buffer + Translate(source, m_buffer, rect); + + // Choose the appropriate encoding routine (for speed...) + switch(m_remoteformat.bitsPerPixel) + { + case 8: + subrects = subrectEncode8( + m_buffer, + dest+sz_rfbFramebufferUpdateRectHeader+sz_rfbRREHeader, + rectW, + rectH, + m_bufflen-sz_rfbFramebufferUpdateRectHeader-sz_rfbRREHeader + ); + break; + case 16: + subrects = subrectEncode16( + (CARD16 *)m_buffer, + (CARD8 *)(dest+sz_rfbFramebufferUpdateRectHeader+sz_rfbRREHeader), + rectW, + rectH, + m_bufflen-sz_rfbFramebufferUpdateRectHeader-sz_rfbRREHeader + ); + break; + case 32: + subrects = subrectEncode32( + (CARD32 *)m_buffer, + (CARD8 *)(dest+sz_rfbFramebufferUpdateRectHeader+sz_rfbRREHeader), + rectW, + rectH, + m_bufflen-sz_rfbFramebufferUpdateRectHeader-sz_rfbRREHeader + ); + break; + } + + // If we couldn't encode the rectangles then just send the data raw + if (subrects < 0) + return vncEncoder::EncodeRect(source, dest, rect); + + // Send the RREHeader + rfbRREHeader *rreh=(rfbRREHeader *)(dest+sz_rfbFramebufferUpdateRectHeader); + rreh->nSubrects = Swap32IfLE(subrects); + + // Update statistics for this rectangle. + rectangleOverhead += sz_rfbFramebufferUpdateRectHeader; + dataSize += ( rectW * rectH * m_remoteformat.bitsPerPixel) / 8; + encodedSize += sz_rfbRREHeader + + (m_remoteformat.bitsPerPixel / 8) + + (subrects * (sz_rfbRectangle + m_remoteformat.bitsPerPixel / 8)); + transmittedSize += sz_rfbFramebufferUpdateRectHeader + + sz_rfbRREHeader + + (m_remoteformat.bitsPerPixel / 8) + + (subrects * (sz_rfbRectangle + m_remoteformat.bitsPerPixel / 8)); + + // Return the amount of data sent + return sz_rfbFramebufferUpdateRectHeader + sz_rfbRREHeader + + (m_remoteformat.bitsPerPixel / 8) + + (subrects * (sz_rfbRectangle + m_remoteformat.bitsPerPixel / 8)); +} diff --git a/vncEncodeRRE.h b/vncEncodeRRE.h new file mode 100644 index 0000000..f11b131 --- /dev/null +++ b/vncEncodeRRE.h @@ -0,0 +1,69 @@ +// Copyright (C) 2000 Tridia Corporation. All Rights Reserved. +// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. +// +// This file is part of the VNC system. +// +// The VNC system 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 program 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 program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +// USA. +// +// TightVNC distribution homepage on the Web: http://www.tightvnc.com/ +// +// If the source code for the VNC system is not available from the place +// whence you received this file, check http://www.uk.research.att.com/vnc or contact +// the authors on vnc@uk.research.att.com for information on obtaining it. + + +// vncEncodeRRE object + +// The vncEncodeRRE object uses a compression encoding to send rectangles +// to a client + +class vncEncodeRRE; + +#if !defined(_WINVNC_ENCODERRRE) +#define _WINVNC_ENCODERRE +#pragma once + +#include "vncEncoder.h" + +// Class definition + +class vncEncodeRRE : public vncEncoder +{ +// Fields +public: + +// Methods +public: + // Create/Destroy methods + vncEncodeRRE(); + ~vncEncodeRRE(); + + virtual void Init(); + virtual const char* GetEncodingName() { return "RRE"; } + + virtual UINT RequiredBuffSize(UINT width, UINT height); + virtual UINT NumCodedRects(RECT &rect); + + virtual UINT EncodeRect(BYTE *source, BYTE *dest, const RECT &rect); + +// Implementation +protected: + BYTE *m_buffer; + size_t m_bufflen; +}; + +#endif // _WINVNC_ENCODERRE + diff --git a/vncEncoder.c b/vncEncoder.c new file mode 100644 index 0000000..04827ae --- /dev/null +++ b/vncEncoder.c @@ -0,0 +1,673 @@ +// Copyright (C) 2001 Constantin Kaplinsky. All Rights Reserved. +// Copyright (C) 2000 Tridia Corporation. All Rights Reserved. +// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. +// +// This file is part of the VNC system. +// +// The VNC system 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 program 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 program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +// USA. +// +// TightVNC distribution homepage on the Web: http://www.tightvnc.com/ +// +// If the source code for the VNC system is not available from the place +// whence you received this file, check http://www.uk.research.att.com/vnc or contact +// the authors on vnc@uk.research.att.com for information on obtaining it. + + +// vncEncoder - Object used to encode data for RFB + +#include "vncEncoder.h" + +#include +//#include "vncBuffer.h" + +// Pixel format used internally when the client is palette-based & server is truecolour + +static const rfbPixelFormat BGR233Format = { + 8, 8, 0, 1, 7, 7, 3, 0, 3, 6 +}; + +// The base (RAW) encoder class + +vncEncoder::vncEncoder() +{ + memset(&m_remoteformat, 0, sizeof(m_remoteformat)); + memset(&m_localformat, 0, sizeof(m_localformat)); + memset(&m_transformat, 0, sizeof(m_transformat)); + m_transtable = NULL; + m_localpalette = NULL; + m_bytesPerRow = 0; + m_compresslevel = 6; + m_qualitylevel = -1; + m_use_xcursor = false; + m_use_richcursor = false; + m_use_lastrect = false; +} + +vncEncoder::~vncEncoder() +{ + if (m_transtable != NULL) + { + free(m_transtable); + m_transtable = NULL; + } + if (m_localpalette != NULL) + { + free(m_localpalette); + m_localpalette = NULL; + } +} + +void +vncEncoder::Init() +{ + dataSize = 0; + rectangleOverhead = 0; + encodedSize = 0; + transmittedSize = 0; +} + +void +vncEncoder::LogStats() +{ + fprintf(stderr, "[ffnetdev] VNC: %s encoder stats: data=%d, overhead=%d, " + "encoded=%d, sent=%d\n", + GetEncodingName(), + dataSize, rectangleOverhead, encodedSize, transmittedSize); + + if (dataSize != 0) { + fprintf(stderr, "[ffnetdev] VNC: %s encoder efficiency: %.3f%%\n", + GetEncodingName(), + (double)((double)((dataSize - transmittedSize) * 100) / dataSize)); + } +} + +UINT +vncEncoder::RequiredBuffSize(UINT width, UINT height) +{ + return sz_rfbFramebufferUpdateRectHeader + + (width * height * m_remoteformat.bitsPerPixel)/8; +} + +UINT +vncEncoder::NumCodedRects(RECT &rect) +{ + return 1; +} + +// Translate a rectangle +inline void +vncEncoder::Translate(BYTE *source, BYTE *dest, const RECT &rect) +{ + // Calculate where in the source rectangle to read from + BYTE *sourcepos = (BYTE *)(source + (m_bytesPerRow * rect.top)+(rect.left * (m_localformat.bitsPerPixel / 8))); + + // Call the translation function + (*m_transfunc) (m_transtable, + &m_localformat, + &m_transformat, + (char *)sourcepos, + (char *)dest, + m_bytesPerRow, + rect.right-rect.left, + rect.bottom-rect.top + ); +} + +// Translate a rectangle (using arbitrary m_bytesPerRow value, +// always translating from the beginning of the source pixel array) +// NOTE: overloaded function! +inline void +vncEncoder::Translate(BYTE *source, BYTE *dest, int w, int h, int bytesPerRow) +{ + // Call the translation function + (*m_transfunc) (m_transtable, &m_localformat, &m_transformat, + (char *)source, (char *)dest, bytesPerRow, w, h); +} + +// Encode a rectangle +inline UINT +vncEncoder::EncodeRect(BYTE *source, BYTE *dest, const RECT &rect) +{ + + const int rectW = rect.right - rect.left; + const int rectH = rect.bottom - rect.top; + + + // Create the header for the update in the destination area + rfbFramebufferUpdateRectHeader *surh = (rfbFramebufferUpdateRectHeader *)dest; + surh->r.x = (CARD16) rect.left; + surh->r.y = (CARD16) rect.top; + surh->r.w = (CARD16) rectW; + surh->r.h = (CARD16) rectH; + surh->r.x = Swap16IfLE(surh->r.x); + surh->r.y = Swap16IfLE(surh->r.y); + surh->r.w = Swap16IfLE(surh->r.w); + surh->r.h = Swap16IfLE(surh->r.h); + surh->encoding = Swap32IfLE(rfbEncodingRaw); + + // Update raw encoding statistics + rectangleOverhead += sz_rfbFramebufferUpdateRectHeader; + dataSize += ( rectW * rectH * m_remoteformat.bitsPerPixel) / 8; + encodedSize += ( rectW * rectH * m_remoteformat.bitsPerPixel) / 8; + transmittedSize += sz_rfbFramebufferUpdateRectHeader + ( rectW * rectH * m_remoteformat.bitsPerPixel) / 8; + + // Translate the data in place in the output buffer + Translate(source, dest + sz_rfbFramebufferUpdateRectHeader, rect); + + // Return the buffer size + return sz_rfbFramebufferUpdateRectHeader + + (rectW*rectH*m_remoteformat.bitsPerPixel) / 8; +} + +// Encode a rectangle directly to the output stream. +// This implementation may not be the best, but it will work with all +// of the existing EncodeRect(BYTE *, BYTE *, const RECT &) implementations. +// Note, that the returned value is that of any data in the dest buffer that +// was not yet transmitted on the outConn. +// The primary justification for adding this method is to allow encodings to +// transmit partial data during the encoding process. This can improve +// performance considerably for slower (more complex) encoding algorithms. +/*inline UINT +vncEncoder::EncodeRect(BYTE *source, VSocket *outConn, BYTE *dest, const RECT &rect) +{ + + return EncodeRect(source, dest, rect); + +} + +bool +vncEncoder::GetRemotePalette(RGBQUAD *quadlist, UINT ncolours) +{ + vnclog.Print(LL_INTINFO, VNCLOG("remote palette data requested\n")); + + // If the local server is palette-based then call SetTranslateFunction + // to update the palette-to-truecolour mapping: + if (!m_localformat.trueColour) + { + if (!SetTranslateFunction()) + return false; + } + + // If the client is truecolour then don't fill in the palette buffer... + if (m_remoteformat.trueColour) + return false; + + // If the server is truecolour then fake BGR233 + if (m_localformat.trueColour) + { + // Fake BGR233... + vnclog.Print(LL_INTINFO, VNCLOG("generating BGR233 palette data\n")); + + int ncolours = 1 << m_transformat.bitsPerPixel; + if (m_localpalette != NULL) + free(m_localpalette); + m_localpalette = (char *)malloc(ncolours * sizeof(RGBQUAD)); + + if (m_localpalette != NULL) + { + RGBQUAD *colour = (RGBQUAD *)m_localpalette; + + for (int i=0; i> m_transformat.blueShift) & m_transformat.blueMax) * 255) / m_transformat.blueMax; + colour[i].rgbRed = (((i >> m_transformat.redShift) & m_transformat.redMax) * 255) / m_transformat.redMax; + colour[i].rgbGreen = (((i >> m_transformat.greenShift) & m_transformat.greenMax) * 255) / m_transformat.greenMax; + } + } + } + else + { + // Set up RGBQUAD rfbPixelFormat info + vnclog.Print(LL_INTINFO, VNCLOG("generating 8-bit palette data\n")); + + rfbPixelFormat remote; + remote.trueColour = true; + remote.bitsPerPixel = 32; + remote.depth = 24; + remote.bigEndian = false; + remote.redMax = remote.greenMax = remote.blueMax = 255; + remote.redShift = 16; + remote.greenShift = 8; + remote.blueShift = 0; + + // We get the ColourMapSingleTableFns procedure to handle retrieval of the + // palette for us, to avoid replicating the code! + (*rfbInitColourMapSingleTableFns[remote.bitsPerPixel / 16]) + (&m_localpalette, &m_localformat, &remote); + } + + // Did we create some palette info? + if (m_localpalette == NULL) + { + vnclog.Print(LL_INTERR, VNCLOG("failed to obtain colour map data!\n")); + return false; + } + + // Copy the data into the RGBQUAD buffer + memcpy(quadlist, m_localpalette, ncolours*sizeof(RGBQUAD)); + + return true; +}*/ + +bool +vncEncoder::SetTranslateFunction() +{ + fprintf(stderr, "[ffnetdev] VNC: SetTranslateFunction called\n"); + + // By default, the actual format translated to matches the client format + m_transformat = m_remoteformat; + + // Check that bits per pixel values are valid + + if ((m_transformat.bitsPerPixel != 8) && + (m_transformat.bitsPerPixel != 16) && + (m_transformat.bitsPerPixel != 32)) + { + fprintf(stderr, "[ffnetdev] only 8, 16 or 32 bits supported remotely - %d requested\n", + m_transformat.bitsPerPixel + ); + + return false; + } + + if ((m_localformat.bitsPerPixel != 8) && + (m_localformat.bitsPerPixel != 16) && + (m_localformat.bitsPerPixel != 32)) + { + fprintf(stderr, "[ffnetdev] only 8, 16 or 32 bits supported locally - %d in use\n", + m_localformat.bitsPerPixel + ); + + return false; + } + + if (!m_transformat.trueColour && (m_transformat.bitsPerPixel != 8)) + { + fprintf(stderr, "[ffnetdev] only 8-bit palette format supported remotely\n"); + return false; + } + if (!m_localformat.trueColour && (m_localformat.bitsPerPixel != 8)) + { + fprintf(stderr, "[ffnetdev] only 8-bit palette format supported locally\n"); + return false; + } + + // Now choose the translation function to use + + // We don't do remote palettes unless they're 8-bit + + if (!m_transformat.trueColour) + { + // Is the local format the same? + if (!m_localformat.trueColour && + (m_localformat.bitsPerPixel == m_transformat.bitsPerPixel)) + { + // Yes, so don't do any encoding + fprintf(stderr, "[ffnetdev] no encoding required - both 8-bit palettized\n"); + + m_transfunc = rfbTranslateNone; + + // The first time the client sends an update, it will call + // GetRemotePalette to get the palette information required + return true; + } + else if (m_localformat.trueColour) + { + // Local side is truecolour, remote is palettized + fprintf(stderr, "[ffnetdev] local truecolour, remote palettized. using BGR233 palette\n"); + + // Fill out the translation table as if writing to BGR233 + m_transformat = BGR233Format; + + // Continue on down to the main translation section + } + else + { + // No, so not supported yet... + fprintf(stderr, "[ffnetdev] unknown local pixel format in use!\n"); + return false; + } + } + + // REMOTE FORMAT IS true-COLOUR + + // Handle 8-bit palette-based local data + if (!m_localformat.trueColour) + { + // 8-bit palette to truecolour... + + // Yes, so pick the right translation function! + fprintf(stderr, "[ffnetdev] using 8-bit colourmap to truecolour translation\n"); + + m_transfunc = rfbTranslateWithSingleTableFns + [m_localformat.bitsPerPixel / 16] + [m_transformat.bitsPerPixel / 16]; + + (*rfbInitColourMapSingleTableFns[m_transformat.bitsPerPixel / 16]) + (&m_transtable, &m_localformat, &m_transformat); + return m_transtable != NULL; + } + + // If we reach here then we're doing truecolour to truecolour + + // Are the formats identical? + if (PF_EQ(m_transformat,m_localformat)) + { + // Yes, so use the null translation function + fprintf(stderr, "[ffnetdev] no translation required\n"); + + m_transfunc = rfbTranslateNone; + + return true; + } + + // Is the local display a 16-bit one + if (m_localformat.bitsPerPixel == 16) + { + // Yes, so use a single lookup-table + fprintf(stderr, "[ffnetdev] single LUT used\n"); + + m_transfunc = rfbTranslateWithSingleTableFns + [m_localformat.bitsPerPixel / 16] + [m_transformat.bitsPerPixel / 16]; + + (*rfbInitTrueColourSingleTableFns[m_transformat.bitsPerPixel / 16]) + (&m_transtable, &m_localformat, &m_transformat); + } + else + { + // No, so use three tables - one for each of R, G, B. + fprintf(stderr, "[ffnetdev] triple LUT used\n"); + + m_transfunc = rfbTranslateWithRGBTablesFns + [m_localformat.bitsPerPixel / 16] + [m_transformat.bitsPerPixel / 16]; + + (*rfbInitTrueColourRGBTablesFns[m_transformat.bitsPerPixel / 16]) + (&m_transtable, &m_localformat, &m_transformat); + } + + return m_transtable != NULL; +} + +bool +vncEncoder::SetLocalFormat(rfbPixelFormat &pixformat, int width, int height) +{ + // Work out the bytes per row at the local end - useful + m_bytesPerRow = width * pixformat.bitsPerPixel/8; + + // Save the pixel format + m_localformat = pixformat; + + // Don't call SetTranslateFunction() if remote format is not set yet. + if (m_remoteformat.depth == 0) + return true; + + return SetTranslateFunction(); +} + +bool +vncEncoder::SetRemoteFormat(rfbPixelFormat &pixformat) +{ + // Save the client pixel format + m_remoteformat = pixformat; + + return SetTranslateFunction(); +} + +void +vncEncoder::SetCompressLevel(int level) +{ + m_compresslevel = (level >= 0 && level <= 9) ? level : 6; +} + +void +vncEncoder::SetQualityLevel(int level) +{ + m_qualitylevel = (level >= 0 && level <= 9) ? level : -1; +} + +// +// New code implementing cursor shape updates. +// +/* +bool +vncEncoder::SendEmptyCursorShape(VSocket *outConn) +{ + rfbFramebufferUpdateRectHeader hdr; + hdr.r.x = Swap16IfLE(0); + hdr.r.y = Swap16IfLE(0); + hdr.r.w = Swap16IfLE(0); + hdr.r.h = Swap16IfLE(0); + if (m_use_xcursor) { + hdr.encoding = Swap32IfLE(rfbEncodingXCursor); + } else { + hdr.encoding = Swap32IfLE(rfbEncodingRichCursor); + } + + return outConn->SendQueued((char *)&hdr, sizeof(hdr)); +} + +bool +vncEncoder::SendCursorShape(VSocket *outConn, vncDesktop *desktop) +{ + // Make sure the function is used correctly + if (!m_use_xcursor && !m_use_richcursor) + return false; + + // Check mouse cursor handle + HCURSOR hcursor = desktop->GetCursor(); + if (hcursor == NULL) { + fprintf(stderr, "[ffnetdev] cursor handle is NULL.\n"); + return false; + } + + // Get cursor info + ICONINFO IconInfo; + if (!GetIconInfo(hcursor, &IconInfo)) { + fprintf(stderr, "[ffnetdev] GetIconInfo() failed.\n"); + return false; + } + bool isColorCursor = false; + if (IconInfo.hbmColor != NULL) { + isColorCursor = true; + DeleteObject(IconInfo.hbmColor); + } + if (IconInfo.hbmMask == NULL) { + fprintf(stderr, "[ffnetdev] cursor bitmap handle is NULL.\n"); + return false; + } + + // Check bitmap info for the cursor + BITMAP bmMask; + if (!GetObject(IconInfo.hbmMask, sizeof(BITMAP), (LPVOID)&bmMask)) { + fprintf(stderr, "[ffnetdev] GetObject() for bitmap failed.\n"); + DeleteObject(IconInfo.hbmMask); + return false; + } + if (bmMask.bmPlanes != 1 || bmMask.bmBitsPixel != 1) { + fprintf(stderr, "[ffnetdev] incorrect data in cursor bitmap.\n"); + DeleteObject(IconInfo.hbmMask); + return false; + } + + // Get monochrome bitmap data for cursor + // NOTE: they say we should use GetDIBits() instead of GetBitmapBits(). + BYTE *mbits = new BYTE[bmMask.bmWidthBytes * bmMask.bmHeight]; + if (mbits == NULL) + return false; + + bool success = GetBitmapBits(IconInfo.hbmMask, + bmMask.bmWidthBytes * bmMask.bmHeight, mbits); + DeleteObject(IconInfo.hbmMask); + + if (!success) { + fprintf(stderr, "[ffnetdev] GetBitmapBits() failed.\n"); + delete[] mbits; + return false; + } + + // Compute cursor dimensions + int width = bmMask.bmWidth; + int height = (isColorCursor) ? bmMask.bmHeight : bmMask.bmHeight/2; + + // Call appropriate routine to send cursor shape update + if (!isColorCursor && m_use_xcursor) { + FixCursorMask(mbits, NULL, width, bmMask.bmHeight, bmMask.bmWidthBytes); + success = SendXCursorShape(outConn, mbits, + IconInfo.xHotspot, IconInfo.yHotspot, + width, height); + } + else if (m_use_richcursor) { + int cbits_size = width * height * 4; + BYTE *cbits = new BYTE[cbits_size]; + if (cbits == NULL) { + delete[] mbits; + return false; + } + if (!desktop->GetRichCursorData(cbits, hcursor, width, height)) { + fprintf(stderr, "[ffnetdev] vncDesktop::GetRichCursorData() failed.\n"); + delete[] mbits; + delete[] cbits; + return false; + } + FixCursorMask(mbits, cbits, width, height, bmMask.bmWidthBytes); + success = SendRichCursorShape(outConn, mbits, cbits, + IconInfo.xHotspot, IconInfo.yHotspot, + width, height); + delete[] cbits; + } + else { + success = false; // FIXME: We could convert RichCursor -> XCursor. + } + + // Cleanup + delete[] mbits; + + return success; +} + +bool +vncEncoder::SendXCursorShape(VSocket *outConn, BYTE *mask, + int xhot, int yhot, int width, int height) +{ + rfbFramebufferUpdateRectHeader hdr; + hdr.r.x = Swap16IfLE(xhot); + hdr.r.y = Swap16IfLE(yhot); + hdr.r.w = Swap16IfLE(width); + hdr.r.h = Swap16IfLE(height); + hdr.encoding = Swap32IfLE(rfbEncodingXCursor); + + BYTE colors[6] = { 0, 0, 0, 0xFF, 0xFF, 0xFF }; + int maskRowSize = (width + 7) / 8; + int maskSize = maskRowSize * height; + + if ( !outConn->SendQueued((char *)&hdr, sizeof(hdr)) || + !outConn->SendQueued((char *)colors, 6) || + !outConn->SendQueued((char *)&mask[maskSize], maskSize) || + !outConn->SendQueued((char *)mask, maskSize) ) { + return false; + } + return true; +} + +bool +vncEncoder::SendRichCursorShape(VSocket *outConn, BYTE *mbits, BYTE *cbits, + int xhot, int yhot, int width, int height) +{ + rfbFramebufferUpdateRectHeader hdr; + hdr.r.x = Swap16IfLE(xhot); + hdr.r.y = Swap16IfLE(yhot); + hdr.r.w = Swap16IfLE(width); + hdr.r.h = Swap16IfLE(height); + hdr.encoding = Swap32IfLE(rfbEncodingRichCursor); + + // Cet cursor image in local pixel format + int srcbuf_rowsize = width * (m_localformat.bitsPerPixel / 8); + while (srcbuf_rowsize % sizeof(DWORD)) + srcbuf_rowsize++; // Actually, this should never happen + + // Translate image to client pixel format + int dstbuf_size = width * height * (m_remoteformat.bitsPerPixel / 8); + BYTE *dstbuf = new BYTE[dstbuf_size]; + Translate(cbits, dstbuf, width, height, srcbuf_rowsize); + + // Send the data + int mask_rowsize = (width + 7) / 8; + int mask_size = mask_rowsize * height; + if ( !outConn->SendQueued((char *)&hdr, sizeof(hdr)) || + !outConn->SendQueued((char *)dstbuf, dstbuf_size) || + !outConn->SendQueued((char *)mbits, mask_size) ) { + delete[] dstbuf; + return false; + } + delete[] dstbuf; + return true; +} +*/ +void +vncEncoder::FixCursorMask(BYTE *mbits, BYTE *cbits, + int width, int height, int width_bytes) +{ + int packed_width_bytes = (width + 7) / 8; + + // Pack and invert bitmap data (mbits) + int x, y; + for (y = 0; y < height; y++) + for (x = 0; x < packed_width_bytes; x++) + mbits[y * packed_width_bytes + x] = ~mbits[y * width_bytes + x]; + + // Replace "inverted background" bits with black color to ensure + // cross-platform interoperability. Not beautiful but necessary code. + if (cbits == NULL) { + BYTE m, c; + height /= 2; + for (y = 0; y < height; y++) { + for (x = 0; x < packed_width_bytes; x++) { + m = mbits[y * packed_width_bytes + x]; + c = mbits[(height + y) * packed_width_bytes + x]; + mbits[y * packed_width_bytes + x] |= ~(m | c); + mbits[(height + y) * packed_width_bytes + x] |= ~(m | c); + } + } + } else { + int bytes_pixel = m_localformat.bitsPerPixel / 8; + int bytes_row = width * bytes_pixel; + while (bytes_row % sizeof(DWORD)) + bytes_row++; // Actually, this should never happen + + BYTE bitmask; + int b1, b2; + for (y = 0; y < height; y++) { + bitmask = 0x80; + for (x = 0; x < width; x++) { + if ((mbits[y * packed_width_bytes + x / 8] & bitmask) == 0) { + for (b1 = 0; b1 < bytes_pixel; b1++) { + if (cbits[y * bytes_row + x * bytes_pixel + b1] != 0) { + mbits[y * packed_width_bytes + x / 8] ^= bitmask; + for (b2 = b1; b2 < bytes_pixel; b2++) + cbits[y * bytes_row + x * bytes_pixel + b2] = 0x00; + break; + } + } + } + if ((bitmask >>= 1) == 0) + bitmask = 0x80; + } + } + } +} + diff --git a/vncEncoder.h b/vncEncoder.h new file mode 100644 index 0000000..561d8ad --- /dev/null +++ b/vncEncoder.h @@ -0,0 +1,141 @@ +// Copyright (C) 2001 Constantin Kaplinsky. All Rights Reserved. +// Copyright (C) 2000 Tridia Corporation. All Rights Reserved. +// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. +// +// This file is part of the VNC system. +// +// The VNC system 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 program 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 program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +// USA. +// +// TightVNC distribution homepage on the Web: http://www.tightvnc.com/ +// +// If the source code for the VNC system is not available from the place +// whence you received this file, check http://www.uk.research.att.com/vnc or contact +// the authors on vnc@uk.research.att.com for information on obtaining it. + + +// vncEncoder object + +// The vncEncoder object encodes regions of a display buffer for sending +// to a client + +class vncEncoder; + +#if !defined(RFBENCODER_DEFINED) +#define RFBENCODER_DEFINED +#pragma once + + +typedef unsigned char BYTE; +typedef unsigned int UINT; +typedef unsigned long DWORD; + +//static + +#include "translate.h" + +typedef struct { + int left; + int top; + int right; + int bottom; +} RECT; + +// Class definition + +class vncEncoder +{ +// Fields +public: + +// Methods +public: + // Create/Destroy methods + vncEncoder(); + virtual ~vncEncoder(); + + // Initialisation + virtual void Init(); + + // A method to return the encoding name, used by the LogStats() method + virtual const char* GetEncodingName() { return "Raw"; } + + // Central method for outputing encoding statistics + virtual void LogStats(); + + // Encoder stats used by the buffer object + virtual UINT RequiredBuffSize(UINT width, UINT height); + virtual UINT NumCodedRects(RECT &rect); + + // Translation & encoding routines + // - Source is the base address of the ENTIRE SCREEN buffer. + // The Translate routine compensates automatically for the desired rectangle. + // - Dest is the base address to encode the rect to. The rect will be encoded + // into a contiguous region of the buffer. + virtual void Translate(BYTE *source, BYTE *dest, const RECT &rect); + virtual UINT EncodeRect(BYTE *source, BYTE *dest, const RECT &rect); +// virtual UINT EncodeRect(BYTE *source, VSocket *outConn, BYTE *dest, const RECT &rect); + + // Additional translation function for cursor shape data (overloaded!) + void Translate(BYTE *source, BYTE *dest, int w, int h, int bytesPerRow); + + // Translation handling + bool SetLocalFormat(rfbPixelFormat &pixformat, int width, int height); + bool SetRemoteFormat(rfbPixelFormat &pixformat); + + // Configuring encoder + void SetCompressLevel(int level); + void SetQualityLevel(int level); + void EnableXCursor(bool enable) { m_use_xcursor = enable; } + void EnableRichCursor(bool enable) { m_use_richcursor = enable; } + void EnableLastRect(bool enable) { m_use_lastrect = enable; } + + // Colour map handling +// BOOL GetRemotePalette(RGBQUAD *quadlist, UINT ncolours); + + // Supporting cursor shape updates +// BOOL SendEmptyCursorShape(VSocket *outConn); +// BOOL SendCursorShape(VSocket *outConn, vncDesktop *desktop); + +protected: + bool SetTranslateFunction(); + + // Supporting cursor shape updates +// BOOL SendXCursorShape(VSocket *outConn, BYTE *mask, int xhot,int yhot,int width,int height); +// BOOL SendRichCursorShape(VSocket *outConn, BYTE *mbits, BYTE *cbits, int xhot,int yhot,int width,int height); + void FixCursorMask(BYTE *mbits, BYTE *cbits, int width, int height, int width_bytes); + +// Implementation +protected: + rfbTranslateFnType m_transfunc; // Translator function + char* m_transtable; // Colour translation LUT + char* m_localpalette; // Palette info if client is palette-based + rfbPixelFormat m_localformat; // Pixel Format info + rfbPixelFormat m_remoteformat; // Client pixel format info + rfbPixelFormat m_transformat; // Internal format used for translation (usually == client format) + int m_bytesPerRow; // Number of bytes per row locally + int dataSize; // Total size of raw data encoded + int rectangleOverhead; // Total size of rectangle header data + int encodedSize; // Total size of encoded data + int transmittedSize; // Total amount of data sent + + int m_compresslevel; // Encoding-specific compression level (if needed). + int m_qualitylevel; // Image quality level for lossy JPEG compression. + bool m_use_xcursor; // XCursor cursor shape updates allowed. + bool m_use_richcursor; // RichCursor cursor shape updates allowed. + bool m_use_lastrect; // LastRect pseudo-encoding allowed. +}; + +#endif // vncENCODER_DEFINED -- cgit v1.2.3