diff options
author | zwer <zwer@1f4bef6d-8e0a-0410-8695-e467da8aaccf> | 2006-01-24 12:54:00 +0000 |
---|---|---|
committer | zwer <zwer@1f4bef6d-8e0a-0410-8695-e467da8aaccf> | 2006-01-24 12:54:00 +0000 |
commit | b998c31e7e0f4f84b2f64c50093069c815772808 (patch) | |
tree | 7b65667843ea5db07766d23688f045d20140361c | |
download | vdr-plugin-ffnetdev-b998c31e7e0f4f84b2f64c50093069c815772808.tar.gz vdr-plugin-ffnetdev-b998c31e7e0f4f84b2f64c50093069c815772808.tar.bz2 |
FFNetDev-Plugin
git-svn-id: svn://svn.berlios.de/ffnetdev/trunk@1 1f4bef6d-8e0a-0410-8695-e467da8aaccf
-rw-r--r-- | CHANGELOG | 25 | ||||
-rw-r--r-- | COPYING | 340 | ||||
-rw-r--r-- | Makefile | 109 | ||||
-rw-r--r-- | MinMax.h | 51 | ||||
-rw-r--r-- | README | 95 | ||||
-rw-r--r-- | config.h | 14 | ||||
-rw-r--r-- | ffnetdev.c | 245 | ||||
-rw-r--r-- | ffnetdev.h | 50 | ||||
-rw-r--r-- | ffnetdevsetup.c | 26 | ||||
-rw-r--r-- | ffnetdevsetup.h | 17 | ||||
-rw-r--r-- | i18n.c | 44 | ||||
-rw-r--r-- | i18n.h | 15 | ||||
-rw-r--r-- | libvdr-ffnetdev.so | bin | 0 -> 434326 bytes | |||
-rw-r--r-- | netosd.c | 78 | ||||
-rw-r--r-- | netosd.h | 38 | ||||
-rw-r--r-- | osdworker.c | 756 | ||||
-rw-r--r-- | osdworker.h | 122 | ||||
-rw-r--r-- | pes2ts.c | 210 | ||||
-rw-r--r-- | pes2ts.h | 50 | ||||
-rw-r--r-- | remote.c | 58 | ||||
-rw-r--r-- | remote.h | 36 | ||||
-rw-r--r-- | rfb.h | 85 | ||||
-rw-r--r-- | rfbproto.h | 786 | ||||
-rw-r--r-- | streamdevice.c | 166 | ||||
-rw-r--r-- | streamdevice.h | 48 | ||||
-rw-r--r-- | tableinitcmtemplate.c | 108 | ||||
-rw-r--r-- | tableinittctemplate.c | 122 | ||||
-rw-r--r-- | tabletranstemplate.c | 99 | ||||
-rw-r--r-- | tools/select.c | 49 | ||||
-rw-r--r-- | tools/select.h | 75 | ||||
-rw-r--r-- | tools/socket.c | 135 | ||||
-rw-r--r-- | tools/socket.h | 108 | ||||
-rw-r--r-- | tools/source.c | 169 | ||||
-rw-r--r-- | tools/source.h | 109 | ||||
-rw-r--r-- | tools/tools.c | 12 | ||||
-rw-r--r-- | tools/tools.h | 67 | ||||
-rw-r--r-- | translate.c | 138 | ||||
-rw-r--r-- | translate.h | 81 | ||||
-rw-r--r-- | tsworker.c | 248 | ||||
-rw-r--r-- | tsworker.h | 56 | ||||
-rw-r--r-- | vncEncodeCoRRE.c | 536 | ||||
-rw-r--r-- | vncEncodeCoRRE.h | 84 | ||||
-rw-r--r-- | vncEncodeHexT.c | 412 | ||||
-rw-r--r-- | vncEncodeHexT.h | 75 | ||||
-rw-r--r-- | vncEncodeRRE.c | 349 | ||||
-rw-r--r-- | vncEncodeRRE.h | 69 | ||||
-rw-r--r-- | vncEncoder.c | 673 | ||||
-rw-r--r-- | vncEncoder.h | 141 |
48 files changed, 7379 insertions, 0 deletions
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 @@ -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. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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. + + <signature of Ty Coon>, 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 @@ -0,0 +1,95 @@ +This is a "plugin" for the Video Disk Recorder (VDR). + +Written by: Christian Cier-Zniewski <c.cier@gmx.de> + Jurij Retzlaff <jurij@topofweb.de> +some code taken from: Sascha Volkenandt's streamdev plugin <sascha@akv-soft.de>, + TightVNC <http://www.tightvnc.com> + + +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 +<window> 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 <getopt.h> +#include <stdlib.h> + +#include <vdr/tools.h> + +#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 <vdr/plugin.h> +#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 <vdr/plugin.h> + +class cFFNetDevSetup : public cMenuSetupPage +{ +private: + int m_iAutoSetPrimaryDVB; +protected: + virtual void Store(void); +public: + cFFNetDevSetup(void); +}; + +#endif //__FFNETDEVSETUP_H + @@ -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 } + }; @@ -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 <vdr/i18n.h> + +extern const tI18nPhrase Phrases[]; + +#endif //_I18N__H diff --git a/libvdr-ffnetdev.so b/libvdr-ffnetdev.so Binary files differnew file mode 100644 index 0000000..4369558 --- /dev/null +++ b/libvdr-ffnetdev.so 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 <vdr/osd.h> +#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 <vdr/tools.h> +#include <sys/time.h> + +#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; y<h; y++) + for (x=0; x<w; x++) + m_Instance->m_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<NumColors; i++) + { + red = ((Colors[i]&0x00FF0000) >> 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; i<NumColors; i++) + 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 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; i<data_length; i++) fprintf(stderr,"%02X ",*(data+i)); + //fprintf(stderr,"\n"); + + return true; + } + return false; +} + +bool cOSDWorker::RFBRead(char *buffer, int len) +{ + if (m_Instance == NULL) + return false; + + if ( m_OSDClient->Read(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;i<Swap16IfLE(msg.se.nEncodings);i++) { + switch (Swap32IfLE(encodings[i])) { + case rfbEncodingRaw: + if (m_pEncoder == NULL) + { + dsyslog("[ffnetdev] VNC: ->RAW 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 <vdr/thread.h> + +#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 <vdr/tools.h> +#include <sys/types.h> +#include <signal.h> +#include <unistd.h> + +#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 <vdr/ringbuffer.h> +#include <vdr/tools.h> + +#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 <vdr/interface.h> + +#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 <vdr/remote.h> +#include <vdr/thread.h> + +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 + @@ -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<n> for an n-bit unsigned integer, INT<n> 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 [<bgpixel><subrect><subrect>...] where each <subrect> is + * [<pixel><rfbRectangle>]. + */ + +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 + * [<bgpixel><subrect><subrect>...] where each <subrect> is + * [<pixel><rfbCoRRERectangle>]. 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 <vdr/device.h> + +#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 <vdr/plugin.h> +#include <osdworker.h> + +// 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 <vdr/tools.h> +#include <sys/time.h> +#include <sys/types.h> +#include <time.h> +#include <unistd.h> +#include <errno.h> + +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 <sys/types.h> + +/* 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 <string.h> +#include <arpa/inet.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> + +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 <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string> + +/* 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 <vdr/tools.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> + +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 <sys/types.h> +#include <string> + +/* 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 <sys/time.h> +#include <time.h> +#include <sys/types.h> +#include <unistd.h> +#include <stdarg.h> + +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 <stdio.h> +//#include <iostream> +#include <sys/types.h> + +//#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 <stdio.h> +#include "rfb.h" +#include <vdr/plugin.h> + +#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 <sys/time.h> + +#include <vdr/tools.h> + +#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 <vdr/thread.h> + +#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 <stdlib.h> +#include <time.h> + +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_threshold) ? 1 : -1; + + // Prepare the stats data for next time... + m_lastencodedbytes = m_encodedbytes; + m_lastrectbytes = m_rectbytes; + + m_encodedbytes = 0; + m_rectbytes = 0; + } + + // Now return the number of rects that this one would encode to + if ((rect.bottom-rect.top) > 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 [<bgcolour><subrect><subrect>...] where each + * <subrect> is [<colour><x><y><w><h>]. + */ + +#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<h; y++) { \ + line = source+(y*w); \ + for (x=0; x<w; x++) { \ + if (line[x] != bg) { \ + cl = line[x]; \ + hy = y-1; \ + hyflag = 1; \ + for (j=y; j<h; j++) { \ + seg = source+(j*w); \ + if (seg[x] != cl) {break;} \ + i = x; \ + while ((seg[i] == cl) && (i < w)) i += 1; \ + i -= 1; \ + if (j == y) vx = hx = i; \ + if (i < vx) vx = i; \ + if ((hyflag > 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; i++) { + counts[i] = 0; + } + + for (j=0; j<size; j++) { + k = (int)(((CARD8 *)data)[j]); + if (k >= 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 <stdlib.h> +#include <time.h> + +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 <stdio.h> +#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<h; y++) \ + { \ + line = src+(y*w); \ + for (x=0; x<w; x++) \ + { \ + if (line[x] != bg) \ + { \ + cl = line[x]; \ + hy = y-1; \ + hyflag = 1; \ + for (j=y; j<h; j++) \ + { \ + seg = src+(j*w); \ + if (seg[x] != cl) {break;} \ + i = x; \ + while ((seg[i] == cl) && (i < w)) i += 1; \ + i -= 1; \ + if (j == y) vx = hx = i; \ + if (i < vx) vx = i; \ + if ((hyflag > 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 <vdr/tools.h> +#include <sys/types.h> +#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 [<bgcolour><subrect><subrect>...] where each + * <subrect> is [<colour><x><y><w><h>]. + * + * 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<h; y++) { \ + line = data+(y*w); \ + for (x=0; x<w; x++) { \ + if (line[x] != bg) { \ + cl = line[x]; \ + hy = y-1; \ + hyflag = 1; \ + for (j=y; j<h; j++) { \ + seg = data+(j*w); \ + if (seg[x] != cl) {break;} \ + i = x; \ + while ((i < w) && (seg[i] == cl)) i += 1; \ + i -= 1; \ + if (j == y) vx = hx = i; \ + if (i < vx) vx = i; \ + if ((hyflag > 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; i++) { + counts[i] = 0; + } + + for (j=0; j<size; j++) { + k = (int)(((CARD8 *)data)[j]); + if (k >= 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 <vdr/plugin.h> +//#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<ncolours; i++) + { + colour[i].rgbBlue = (((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 |