pax_global_header00006660000000000000000000000064111130374610014507gustar00rootroot0000000000000052 comment=319ec1dcebb0a6107279a6c20381a0fa7996c492 pyvnc2swf-0.9.5/000077500000000000000000000000001111303746100134635ustar00rootroot00000000000000pyvnc2swf-0.9.5/LICENCE.TXT000066400000000000000000000431201111303746100151260ustar00rootroot00000000000000 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 Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy 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) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. pyvnc2swf-0.9.5/Makefile000066400000000000000000000034271111303746100151310ustar00rootroot00000000000000# Makefile (only for maintainance purpose) # $Id: Makefile,v 1.29 2008/11/16 02:54:22 euske Exp $ # # Copyright (C) 2005 by Yusuke Shinyama (yusuke at cs . nyu . edu) # 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. # CVSROOT=:ext:euske@vnc2swf.cvs.sourceforge.net:/cvsroot/vnc2swf PACKAGE=pyvnc2swf VERSION=0.9.5 PYTHON=python GNUTAR=tar ZIP=zip CVS=cvs -d$(CVSROOT) WORKDIR=/tmp DISTNAME=$(PACKAGE)-$(VERSION) all: clean: -rm *~ '.#*' .DS_Store -cd bin && rm *~ '.#*' .DS_Store -cd docs && rm *~ '.#*' .DS_Store cd pyvnc2swf && make clean up: clean $(CVS) update diff: clean $(CVS) diff -u commit: clean $(CVS) commit check: cd pyvnc2swf && pychecker vnc2swf.py pack: clean cd $(WORKDIR) && $(CVS) export -D now -d $(DISTNAME) pyvnc2swf cd $(WORKDIR) && $(GNUTAR) c -z -f $(WORKDIR)/$(DISTNAME).tar.gz $(DISTNAME) --dereference --numeric-owner cd $(WORKDIR) && $(ZIP) -r $(DISTNAME).zip $(DISTNAME) cd $(WORKDIR) && rm -rf $(DISTNAME) WEBDIR=$(HOME)/Site/unixuser.org/vnc2swf/ publish: pack mv $(WORKDIR)/$(DISTNAME).tar.gz $(WEBDIR) mv $(WORKDIR)/$(DISTNAME).zip $(WEBDIR) cp docs/*.html $(WEBDIR) pyvnc2swf-0.9.5/README.txt000066400000000000000000000030671111303746100151670ustar00rootroot00000000000000pyvnc2swf $Id: README.txt,v 1.33 2008/11/16 02:39:40 euske Exp $ For documentation, see docs/pyvnc2swf.html. Homepage: http://www.unixuser.org/~euske/vnc2swf/ Discussion: http://lists.sourceforge.net/lists/listinfo/vnc2swf-users Acknowledgements: * Jesse Ruderman (Seekbar javascript code) * Radoslaw Grzanka (MemoryError bug fix) * Luis Fernando Kauer (VNC protocol error fix, cursor pseudo-encoding support, FLV support, lots of bugfixes) * Rajesh Menon (OSX assertion error fix) * Vincent Pelletier (MPEG encoding support) * Uchida Yasuo (Windows file bug fix) * Andy Leszczynski (MP3 and PyMedia bug fix) * David Fraser (audio recording with PyMedia) Bugs: * Noises with non-multiple scaling (e.g. 0.7) * Ctrl-C at bad timings might cause the program abort. * Sometimes MPEGVideoStream crashes. (pymedia? - I couldn't replay.) * Timing issue (esp. notable in vnclog) TODOs: * Audio support on FLV. * Neat GUI. * Authoring tool. (combining vnc2swf.py, edit.py and play.py) * Cursor shadow support. * Improve image scaling. (specify the scale ratio by size) * Screen snapshot tool (with no animation). * Distribution of Windows/Mac binaries. (py2exe, py2app) * Audio recording/replay with PyMedia. * Stop recording remotely. * FLV editing. $ x11vnc -quiet -cursor -viewonly -bg -localhost -nopw && ./vnc2swf.py -n -o out.swf :0 $ tcpserver -vRHl0 localhost 10000 sh -c 'x11vnc -quiet -bg -nopw -viewonly -localhost -cursor -wait 10 -defer 10 >/dev/null 2>&1 && echo HTTP/1.0 200 OK && echo Content-Type: video/x-flv && echo && ./vnc2swf.py -n -t flv -o -' pyvnc2swf-0.9.5/bin/000077500000000000000000000000001111303746100142335ustar00rootroot00000000000000pyvnc2swf-0.9.5/bin/recordwin.sh000077500000000000000000000026661111303746100166000ustar00rootroot00000000000000#!/bin/sh ## ## recordwin.sh ## $Id: recordwin.sh,v 1.3 2008/11/16 02:39:40 euske Exp $ ## ## Quick recording script for UNIX. ## ## usage: ## recordwin.sh [-display disp] [-name winname] [-id winid] output.swf ## ## Requires: x11vnc, xwininfo, awk ## PYTHON=python VNC2SWF=pyvnc2swf/vnc2swf.py X11VNC=x11vnc XWININFO=xwininfo AWK=awk usage() { echo "usage: $0 [-all] [-display display] [-name windowname] [-id windowid] [-type filetype] outfile" exit 100 } vncopts= xwopts= desktop= display="$DISPLAY" while [ $# -gt 1 ]; do case "$1" in -all|-a) desktop=1;; -name) shift; xwopts="$xwopts -name $1";; -id) shift; xwopts="$xwopts -id $1";; -display|-d) shift; display="$1"; xwopts="$xwopts -display $1";; -type|-t) shift; vncopts="$vncopts -t $1";; -*) usage;; esac shift done if [ $# -lt 1 ]; then usage; fi outfile="$1" if [ "X$desktop" = "X" ]; then info=`$XWININFO $xwopts 2>/dev/null` if [ "X$info" = "X" ]; then echo "Window $xwopts not found!" exit 2 fi geometry="-C `echo "$info" | $AWK '/Absolute upper-left X:/{x=$4} /Absolute upper-left Y:/{y=$4} /Width:/{w=$2} /Height:/{h=$2} END {printf "%dx%d+%d+%d",w,h,x,y}' `" echo $geometry fi # launch x11vnc and vnc2swf $X11VNC -quiet -bg -nopw -display "$display" -viewonly -localhost -cursor -wait 10 -defer 10 && $PYTHON $VNC2SWF -n $vncopts -o "$outfile" $geometry pyvnc2swf-0.9.5/docs/000077500000000000000000000000001111303746100144135ustar00rootroot00000000000000pyvnc2swf-0.9.5/docs/changes.html000066400000000000000000000157451111303746100167250ustar00rootroot00000000000000 vnc2swf - Changes

vnc2swf
Changes


History

pyvnc2swf-0.9.5 (Nov 15, 2008)

  • FLV direct recording supported.

pyvnc2swf-0.9.3 (Apr 25, 2007)

  • Replaced crippled_des.py with d3des.py. (License issue)

pyvnc2swf-0.9.2 (Mar 31, 2007)

  • FLV support added. (Thanks to Luis Fernando)

pyvnc2swf-0.9.1 (Nov 3, 2006)

  • GUI-related bugfixes.

pyvnc2swf-0.9.0 (Oct 29, 2006)

  • More intuitive UI.
  • Fixed PyMedia related bugs.
  • Fixed MP3 related bugs.
  • Disable the movie scaling by default.
  • A password file must be encrypted.

pyvnc2swf-0.8.2 (Feb 9, 2006)

  • mp3skip can be specified with seconds.
  • Improved error handling.
  • Cursor bug is fixed.

pyvnc2swf-0.8.1 (Nov 28, 2005)

  • MPEG encoding is supported. (Thanks to Vincent Pelletier)
  • Problem with RealVNC is fixed (Thanks to Andy Bueler)
  • Cursor color bug and vnc conversion bug is fixed (Thanks to Luis Fernando)

pyvnc2swf-0.8.0 (Nov 20, 2005)

  • Cursor pseudo encoding is supported. Now it captures the mouse cursor fine on OSX. (Thanks to Luis Fernando)
  • Vnc protocol errors when using TightVNC or UltraVNC is fixed.
  • MPEGVideoStream is added but not tested.

pyvnc2swf-0.7.2 (Nov 11, 2005)

  • "RFB illegal encoding" bug is fixed. (Thanks to David Karnowski for pointing this out)
  • recordwin script is added.
  • More efficient recording when clipping area is specified.

vnc2swf-0.5.0 (Oct 29, 2005)

  • Now C version of vnc2swf no longer depends on Ming library.

pyvnc2swf-0.7.1 (Oct 27, 2005)

  • MemoryError bug on Windows is fixed. (Thanks to Radoslaw Grzanka)

pyvnc2swf-0.7.0 (Oct 23, 2005)

  • Documentation and seekbar function are added. Thanks to Jesse Ruderman, who contributed the seekbar code.

pyvnc2swf-0.6.4 (Aug 23, 2005)

  • pyvnc2swf-0.6.4 was released. Scaling and frame resampling are supported.

pyvnc2swf-0.6.3 (Aug 5, 2005)

  • pyvnc2swf-0.6.3 was released. MP3 seeking and small bugfix.

pyvnc2swf-0.6.2 (Jul 23, 2005)

  • pyvnc2swf-0.6.2 was released. Small bugfix.

pyvnc2swf-0.6.1 (Jul 22, 2005)

  • pyvnc2swf-0.6.1 was released. Subprocess handling improved.

pyvnc2swf-0.6 (Jul 21, 2005)

  • pyvnc2swf-0.6 was released. Several fixes for memory consumption problems.

pyvnc2swf-0.5 (Jul 17, 2005)

  • pyvnc2swf-0.5 was released. Now it supports non-unix platforms, including OSX and Windows.

edit_vnc2swf-0.3 (Feb 4, 2005)

  • Edit_vnc2swf-0.3 was released. Simple viewer is supported.

edit_vnc2swf-0.2 (Jan 29, 2005)

  • Edit_vnc2swf was released. This will be integrated with vnc2swf package in future.
  • edit_vnc2swf (Jan 29, 2005).

vnc2swf-0.4.2 (May 19, 2004)

  • Segmentation Fault bug when attaching mp3 is fixed.

vnc2swf-0.4.1 (May 9, 2004)

  • A MacOS X Bug is fixed (Yellowish screen).

vnc2swf-0.4 (Apr. 3, 2004)

  • Be able to record an existing desktop (with x11vnc).
  • Partial recording support added.
  • Rewinding support added.
  • Status display support added.

vnc2swf-0.3 (Oct. 25, 2003)

  • Based on VNC viewer 3.3.7.
  • ./configure support.
  • Sound file attachment support. (Contributed by Tim Jansen <tim at tjansen dot de>)
  • Pause/Restart recording support.
  • Another recording algorithm.

Older Versions

Pyvnc2swf

Vnc2swf (C version)


Last Modified: Sun Nov 16 11:30:58 JST 2008

Yusuke Shinyama
pyvnc2swf-0.9.5/docs/index-j.html000066400000000000000000000114551111303746100166450ustar00rootroot00000000000000 Vnc2swf - 画面録画ユーティリティ

vnc2swf
画面録画ユーティリティ

Vnc2swf (う゛んくつすわふ) はクロスプラットフォーム対応の画面録画ツールです。

ダウンロード:

Python版: (2008年 11月15日) pyvnc2swf-0.9.5 tar.gz or zip or RPM/Win32
C版: (2005年10月29日) vnc2swf-0.5.0 tar.gz or RPM これらの違いは?

ご意見あるいはご質問:

日本語の場合は、新山 宛に直接お送りください。
英語では vnc2swf-users@lists.sourceforge.net にお送りください (メーリングリストの アーカイブ購読方法)。

更新履歴はこちら。


サンプルムービー


Python 版と C 版はどうちがう?

Vnc2swf には 2種類の異なる実装 (Python版と C版) があります。

  • Python版 (pyvnc2swf):
    • 多くのプラットフォーム (Linux, FreeBSD, Solaris, Mac OS X, Windows) に対応している。
    • 編集などの機能がついている。
    • 活発に開発されている。
  • C版 (vnc2swf):
    • X11 をサポートするプラットフォーム (Unix, Linux, Mac OS X) でのみ動作。
      NOTE: 最新バージョンでは、Ming はもう必要ありません。
    • 簡単で軽快。

どちらの場合も、まず VNC サーバを最低 1種類インストールしておく必要があります:

また、Python 版では Python と Pygame パッケージが必要です。 より詳しい説明は pyvnc2swf のページをごらんください。


ライセンス

Vnc2swf は無保証のソフトウエアです。 このソフトウエアは GNU General Public License (GNU 一般公衆利用承諾契約書) に従って配布されます。


作者

新山 祐介


リンク


Last Modified: Sun Nov 16 10:50:46 JST 2008

Yusuke Shinyama
pyvnc2swf-0.9.5/docs/index.html000066400000000000000000000126441111303746100164170ustar00rootroot00000000000000 vnc2swf - Screen Recorder

vnc2swf
Screen Recorder

Vnc2swf is a cross-platform screen recording tool for ShockWave Flash (swf) or Flash Video (flv) format.

Download:

Python version: (Nov 15, 2008) pyvnc2swf-0.9.5 tar.gz or zip or RPM/Win32
C version: (Oct 29, 2005) vnc2swf-0.5.0 tar.gz or RPM   What is the difference?

Questions or Comments:

Write to vnc2swf-users@lists.sourceforge.net. (Here is the archive and how to subscribe)

Recent changes


Sample Movie


Frequently Asked Questions

What's the difference between Python version and C version?

Vnc2swf comes with two different implementations:

  • Python version (pyvnc2swf):
    • Runs on many platforms, including Linux, FreeBSD, Solaris, Mac OS X, and Windows.
    • Has more functions (e.g. editing, adding a seekbar).
    • Is being actively developed.
  • C version (vnc2swf):
    • Only runs on platforms which support X11 (i.e. Unix, Linux or Mac OS X).
      NOTE: The latest version no longer needs Ming.
    • Has a vnc viewer.
    • Simple and lightweight.

For both versions, you need to install at least one VNC server:

Additionally, for Python version, you need Python and Pygame package. For more information, see pyvnc2swf page.

Output movies are garbled!

Several users reported they had a garbled movie when they used ATI binary drivers for the X server. Please switch to the open source one.

Could you add this new feature?

Recently I've received lots of feature requests. Unfortunately, some of them are hard to implement because of the nature of this software, including:

  • Recording mouse click. (VNC doesn't send mouse click information)
  • Recording key stroke. (VNC doesn't send this, too)
  • Supporting OGG format instead of MP3. (SWF file doesn't support this.)

License

Vnc2swf comes with ABSOLUTELY NO WARRANTY. This software is distributed under the GNU General Public License.


Author

Yusuke Shinyama (For questions or comments, please write to the mailing list so that we can share them!)


Links


Last Modified: Sun Nov 16 11:55:13 JST 2008

Yusuke Shinyama
pyvnc2swf-0.9.5/docs/pyvnc2swf-j.html000066400000000000000000000457461111303746100175110ustar00rootroot00000000000000 pyvnc2swf

pyvnc2swf

back [English]

ホームページ: http://www.unixuser.org/~euske/vnc2swf/
メーリングリスト: http://lists.sourceforge.net/lists/listinfo/vnc2swf-users

$Id: pyvnc2swf-j.html,v 1.2 2008/11/16 02:39:40 euske Exp $


Pyvnc2swf はクロスプラットフォームの画面録画ツールです。 これは VNC プロトコルを経由して画面の動きを記録し、Shockwave Flash (SWF) ムービーを生成します。 Pyvnc2swf は 3つの Python プログラムから構成されています:

  • vnc2swf.py - 録画ツール
  • edit.py - 編集ツール (これは vnc2swf によって生成された SWF のみをサポートしており、一般的な SWFエディタではありません)
  • play.py - 再生ツール

ご質問がある方は、作者にメールを送る前にまず FAQ および メーリングリストのアーカイブ をご覧ください。

ライセンス: Pyvnc2swf は無保証のソフトウェアです。 これは GNU 一般公衆利用許諾契約書 (GPL) に従って配布されます。


インストール

すべてのプラットフォームで、以下のパッケージがあらかじめ必要です:

  • Python (2.4 以上)
  • Pygame (1.6 以上)
  • オプション: PyMedia (1.3.5 以上 - MPEG エンコーディングに必要)

ほとんどの Linux ディストリビューションでは、 これらのパッケージはすぐにインストールできます。 Mac OS X では、Python はすでにインストールされていますが、 OS X 版の Pygame を インストールする必要があります。

また、VNC サーバが最低 1種類必要になります:

  • RealVNC or TightVNC (Unix, Linux or Windows)
  • x11vnc (Unix/Linux、現在のデスクトップを録画する場合)
  • OSXVnc (Mac OS X)
  • GNOME Vino (これは独立したパッケージではありませんが、「リモートデスクトップ」機能が塔載されている GNOME環境をお持ちの場合、このプログラムはインストールされています)

録画する

vnc2swf.py プログラムは VNC のセッションを記録し、これを SWF または VNCLog 形式で記録します。 これは VNC クライアントであり、VNC サーバと直接通信します。 ユーザはこのプログラムに先立って VNCサーバを開始させておく必要があります。

vnc2swf.py には 2つのモードがあります: GUI (グラフィカル・インターフェイス) モードと CLI (コマンドライン・インターフェイス) モードです。 GUI モードの場合、 "Start" ボタンを押すと録画が始まります。 その後 "File" メニューから "Save as..." を選び録画したムービーを保存します。 CLI モードの場合、ユーザは SWFムービーを保存するファイル名を コマンドラインから指定し、録画はすぐに始まります。 どちらの場合も、ユーザはサーバが要求した場合 (パスワードファイルを指定していない限り) VNCパスワードの入力が求められます。CLIモードでは、 録画を中止するにはControl-C を押してください。この後あらかじめ指定されたファイル名で .swf.html の 2つのファイルが生成されます。 この .html には適切な HTMLタグと、シークバー機能を提供する javascript コードが 含まれています。

vnc2swf.py では画像をエンコードするために 3つの方式が選べます: "swf5", "swf7" および "vnc" です。 最初の swf5エンコーディング (デフォルト) では、そこそこの大きさのムービーを生成します。 2番目の swf7エンコーディングは、より小さな SWF ムービーが生成できますが、 vnc2swf.py の中ではこの方式はおすすめできません。なぜなら、 1) このエンコーディングは Flash Player version 7以降でしかサポートされていない。 また、2) swf7 エンコーディングのムービー生成は遅いので、 フレーム落ちすることがある、といった理由からです。実際には、ムービーを録画したあとに edit.pyコマンドを使って swf5方式のムービーを swf7方式の ムービーに変換できるので、どのみち録画時にはこの方法は必要ありません。 3番目のエンコーディング方法は vnc です。 これは vncrec と互換である .vnc ファイル (VNCLogファイル) を生成します。このファイル自体は SWFムービーではありませんが、生成する速度はいちばん速く、 edit.py コマンドによって SWFムービーに変換することができます。

NOTE: Cバージョンの vnc2swf とは異なり、vnc2swf.py では 実際のクライアント画面を表示しません。VNC サーバのデスクトップを 制御したい場合は、vncviewer のようなものを個別に起動する必要があります。

構文

  • (GUI) $ vnc2swf.py [-o ファイル名] [オプション] [ホスト[:display] [ポート番号]]
  • (CLI) $ vnc2swf.py -n -o ファイル名 [オプション] [ホスト[:display] [ポート番号]]

使用例

(仮想スクリーンを録画する)
$ vncserver -geometry 640x480
...
$ vnc2swf.py -o out.swf localhost:1

(現在の X11画面を録画する)
$ x11vnc -localhost -viewonly -wait 10 -defer 10 &
...
$ vnc2swf.py -o out.swf -S "arecord -r 22050 voice.wav" localhost:0

x11vnc を使っている場合は recordwin の説明もごらんください。 これはある特定のウィンドウだけを録画する簡素なスクリプトです。

録画のコツ

  • なるべく小さい画面サイズを使うか、クリッピング領域を指定してください。 画面サイズが大きいと、VNC の画面スキャンも遅くなり、vnc2swf もまた 大きな画像を変換するために遅くなります。また、小さい画面を使うと ムービーのサイズも小さくなります。
  • フレームレートを落とすと、ムービーのサイズが削減できることがあります。 生成されたムービーがそれでも大きい場合、 edit.py でフレームを間引くか、 画像を縮小してみてください。
  • 長い動画 (20分以上) を録画する場合、使用可能な形式は flv (あるいは PyMedia がある場合は mpeg) だけです。 swf5 または swf7 ファイルには制限があって、 最大16000フレーム (約22分) までの動画にしか使えません。
  • コマ落ちがある場合は、VNCサーバ側の検出オプションを (もしあれば) 変更してみてください。 x11vnc を使っている場合は -wait 10 -defer 10 を追加します。
  • flv動画では、出力ファイル名として '-' を指定することにより 動画ストリームを標準出力に送ることができます。(なんに使うんだ?)

オプション

-n
コンソールモード (GUIなし) にします。

-o 生成ファイル名
生成するファイル名を指定します。CLIモードでは、このオプションはかならず必要です。 GUIモードでは、これが指定されない場合はユーザにファイル名の入力を促します。 ムービーのエンコーディング方式はふつうファイル名から決定されます。 このためには、ファイル名の拡張子は ".swf" または ".vnc" のどちらかである必要があります。 それ以外の拡張子の場合、ユーザはムービーのエンコーディング方式を -t オプション (下記参照) で指定する必要があります。

-C クリッピング
矩形のクリッピング領域を指定します。 領域は "widthxheight+left+top" のような形式で指定します (e.g. "400x300+120+0")。 他の X11アプリケーションと違い、すべての値を指定する必要があります。負の値はサポートされていません。

-r フレームレート
ムービーのフレームレートを fps (フレーム/秒) で指定します。 (default=12.0)

-t エンコーディング
生成するムービーのエンコーディング方式を指定します ("swf5" または "swf7" または "vnc")。 省略された場合、エンコーディング方式は生成するファイル名から自動的に決定されます (*.swf = swf5, *.vnc = vnc)。

-N
Cursor pseudo-encoding の使用を禁止します。Pyvnc2swf は通常 マウスカーソルの位置を取得するために Cursor pseudo-encoding を使おうとします。 これによりカーソルを画面イメージとは別個に動かすことが可能になり、 生成するムービーサイズが小さくなりますが、vnc サーバによっては この方法が使えないこともあります。このオプションはこの動作を禁止するのに使います。

-P パスワードファイル
パスワードファイルを指定します。これが指定された場合、VNCサーバがパスワードを要求したときに このファイルの内容が自動的にロードされ入力されます。このファイルは暗号化された パスワード文字列を含んでおり、vncpasswd を使って作ることができます。 ユーザはローカルな vnc サーバのパスワードを含んでいる ~/.vnc/passwd を直接指定することもできます。

-e vncエンコーディング
VNCの画像伝送に使うエンコーディング方式を指定します (これはムービーのエンコーディングとは異なります)。 ふつうこのオプションは指定する必要がありません。 エンコーディングはカンマで区切られた整数で (default="5,4,0")、 これを変更すると録画速度が改善される場合があります。

-S サブプロセス (Un*x でのみサポート、要Python 2.4 以上)
録画中に実行するコマンドを指定します。 このオプションは別のプログラムで音声を録音するときに便利です。 コマンドラインはスペースで区切られた引数のリストで、シェルのコマンドラインと同様、 この引数は子プロセスに渡されます。しかし、ここでは実際にはシェルは起動されないため、 ここでの引数の値は展開されずそのまま子プロセスに渡されます。 子プロセスは録画が始まった直後に開始され、録画が終了すると子プロセスには SIGINT が送られます。

-d
デバッグレベルを上げます。


編集する

edit.pyプログラムは、vnc2swf.pyで 生成したムービーファイル (ひとつまたは複数) を編集したり再構築するためのツールです。 このプログラムはまた .vnc ファイルを .swfムービーに変換したり、 エンコーディング方式を変えたり (swf5->swf7)、MP3音声ファイルを画像にのせたり、 ムービーからイメージを抽出したり、ムービーの画像を間引く/縮小/クリップする、などの操作ができます。

現在のところ、edit.pyがサポートするのはコマンドラインのみです。 ユーザはひとつ以上の入力ファイル名と、ひとつの出力ファイル名を指定する必要があります。 入力されるムービーは指定された順に連結され、その後要求された操作が実行されます。

構文

$ edit.py -o 出力ファイル.swf [オプション] 入力ファイル ...

使用例

(圧縮つき swf7 形式で、.vnc を .swf に変換する)
$ edit.py -o out.swf -c -t swf7 input.vnc

(mp3ファイルを .swf に添加する)
$ edit.py -o out.swf -a voice.mp3 in.swf

(2つのムービーを連結し、一部のフレームをとりだして別のムービーをつくる)
$ edit.py -o out.swf -f 100-200,350- movie1.swf movie2.swf

(ムービーの左上部分をとりだし、それを半分のサイズに縮小したムービーをつくる)
$ edit.py -o small.swf -C 320x240+0+0 -s 0.5 in.swf

(.swf 形式を MPEG に変換する)
$ edit.py -o out.mpg input.swf

(.swf 形式を FLV に変換する)
$ edit.py -o out.flv input.swf

オプション

-o 出力ファイル
出力ファイル名を指定します。このオプションはつねに指定する必要があります。

-c
zlib でムービーを圧縮します。圧縮は swf5 または swf7形式のエンコーディングで使えますが、 これは swf7エンコーディングに適用されたときにもっとも効率的に圧縮されます。

-t エンコーディング
ムービーを指定された出力方式でエンコーディングします。 このオプションが指定されていない場合、ムービーの形式は 出力するファイル名の拡張子によって決定されます。

エンコーディング拡張子説明
swf5.swfSWF ムービー (デフォルト)
swf7SWFムービー (video エンコーディングをサポートしているプレイヤー向け)
flv.flvFLV ムービー
mpeg.mpgMPEG ムービー (要PyMedia)
bmp.bmpBMP 画像の列
png.pngPNG 画像の列

注意: swf7方式は Flash Player version 7 以上でのみサポートされています。

-f フレーム or
-F フレーム
出力するフレーム列を指定します。このオプションが指定されない場合は、 もとのムービーにあったフレームがすべてそのままの順序で出力されます (デフォルト)。 フレーム列はカンマで区切られた整数のリストです。 ある範囲を指定したいときは - (ハイフン) を使います。 たとえば、10,200,300-400 はフレーム番号 10 と 200 に加えて、 フレーム番号 300 から 400 までのすべてのフレームを指定します。 開始フレーム番号あるいは終了フレーム番号は省略することができます (e.g. -100 or 300-)。この場合、最初のフレーム (あるいは最後のフレーム) が もう一方の終端として使用されます。

-Fオプションと -fオプションの違いは、-F が 音声を切り取らないのに対して、 -fは音声も分断することです。 音声ファイルをムービーにのせたい場合、もし -fオプションと -aオプションを同時に使うと、これは与えられた mp3 ファイルを 選ばれたフレームに応じて自動的に切り分けます。しかし連続した音 (音楽など) をムービーに 載せたいときは、これは意図した結果とは違うことがあります。このような場合は -fのかわりに -F を使ってください。

-a mp3ファイル
mp3 ファイルをムービーに添加します。(現在FLV形式はサポートされていません。) 複数の mp3 ファイルが指定された場合、これらは指定された順序で連結されます。
注意: 複数の mp3 ファイルを指定する場合、 かならずすべてのファイルのビットレートが最初に指定したファイルと同じになるようにしてください。 また、 "ビットレート可変" (VBR) の mp3ファイルは SWF でサポートされていませんので 使わないようにしてください。

-s 比率
ムービー画像を縮小する比率を小数で指定します。
注意: 割り切れない比率 (0.7 など) を指定した場合、ノイズが出ることがあります。

-C クリッピング
ムービーを指定された矩形領域にクリッピングします。 矩形は "widthxheight+left+top" のようにして指定します (例: "400x300+120+0")。

-K キーフレーム間隔
指定された Nフレームごとに、キーフレームを挿入します。 キーフレームは Flash Player が長いムービーでフレームをシークする際の手がかりとして使用されます。 生成するムービーの合計フレーム数が 10,000 を超える場合、500フレームごとに 1枚のキーフレームを挿入するとよいでしょう (-K 500)。

-r フレームレート
ムービーのフレームレートを指定します。このオプション自体はフレームを間引くことはせず、 たんにムービーの速度を変更します。このオプションが省略された場合、edit.py は 元のムービーのフレームレートを使用します。

-R フレーム再サンプル頻度
ムービーのフレームを間引きます。指定した Nフレームごとに 1フレームが選ばれます。

-S mp3フレームスキップ
mp3フレーム (ムービーのフレームとは異なる概念です) で、最初の N個をスキップします。 数値のあとに s をつけた場合 (1.0s など)、これは フレーム数ではなく秒数を表します。 このオプションは録画された画像と音声の間にタイムラグがあるときに有用です。

-B ブロックサイズ
swf7およびflvエンコーディング方式における ブロックサイズを指定します (default=32)。この値は 16の倍数である必要があります。

-b
生成される HTML でシークバーをつけないようにします。

-l
生成される HTML でムービーがループしないようにします。

-d
デバッグレベルを上げます。


プレビュー

play.py は、.swfファイルまたは .vncファイルのための 単純なプレイヤーです。これは生成されたムービーのフレーム番号を見るのに有用です。 しかし、まだこのバージョンでは動きがぎこちないうえに、音声の出力ができません。 またこれは vnc2swfが生成したムービーだけを扱っており、一般的な SWF プレイヤーとしては使えません。

以下のようなキー操作を受け付けます:

  • Space または Enter または マウスクリック: 再生/停止の切り換え。
  • カーソルキー またはシークバーをドラッグ: フレームをシークする。
  • "q" キーまたは Escキー: 終了する。
  • "s": スナップショットを取る。 このときの画像は "inputfile-frameno.bmp" のような名前で保存されます。

構文

$ play.py [オプション] ムービーファイル ...

オプション

-r フレームレート
再生するフレームレートを指定します。

-s 比率
画像を縮小する比率を小数で指定します。

-C クリッピング
クリッピングの矩形領域を指定します。 矩形は "widthxheight+left+top" のようにして指定します (例: "400x300+120+0").

-d
デバッグレベルを上げます。


FAQ (よくある質問と回答)

録画したムービーにおかしなノイズが含まれている。どうやれば直る?
たぶん SWF ファイルをじかに再生したためです。 SWFムービーは、録画されたのと正確に同じサイズで再生する必要があります。 vnc2swf.py または edit.py ファイルが生成した HTMLファイルを使ってください。

音声つきのムービーをつくるにはどうやるの?
音声を別のプログラムで録音したあと Lame などで MP3 にエンコーディングし、edit.py-a オプションをつかってムービーに添加します。

シークバーを使うとブラウザが固まるんだけど。
数百フレームに 1枚、キーフレームを入れてください。edit.py-K オプションを使います。 ふつう、500フレームに 1枚 (-K 500) ほどキーフレームを入れるとそこそこましな結果になるようです。


プログラムの構造

このプログラムの動きを学習あるいは拡張したい方のために、 pyvnc2swf のオブジェクト間のデータの流れを説明する簡単な図 を描きました。


Yusuke Shinyama <yusuke at cs dot nyu dot edu>
pyvnc2swf-0.9.5/docs/pyvnc2swf.html000066400000000000000000000444331111303746100172520ustar00rootroot00000000000000 pyvnc2swf

pyvnc2swf

back [Japanese]

Homepage: http://www.unixuser.org/~euske/vnc2swf/
Discussion: http://lists.sourceforge.net/lists/listinfo/vnc2swf-users

$Id: pyvnc2swf.html,v 1.2 2008/11/16 02:39:40 euske Exp $


Pyvnc2swf is a cross-platform screen recording tool. It captures screen motion through VNC protocol and generates a Shockwave Flash (SWF) movie. Pyvnc2swf suite comes with three Python programs:

  • vnc2swf.py - Recorder
  • edit.py - Movie editor (This is NOT a general SWF file editor. It only supports movies generated by vnc2swf.)
  • play.py - Simple movie viewer

For questions, please read the FAQ and list archives before sending me emails.

Terms and Conditions: Pyvnc2swf comes with ABSOLUTELY NO WARRANTY. This software is distributed under the GNU General Public License.


Installation

In all platforms, the following packages are required:

  • Python (2.4 or above)
  • Pygame (1.6 or above)
  • Optional: PyMedia (1.3.5 or above - required for mpeg encoding)

In most Linux distros, these packages are readily available. In Mac OS X, you would need an additional OS X build of Pygame package.

Also, you need at least one VNC server:

  • RealVNC or TightVNC (Unix, Linux or Windows)
  • x11vnc (Unix/Linux for recording an existing desktop)
  • OSXVnc (Mac OS X)
  • GNOME Vino (This is not an independent package, but if you have GNOME desktop environment which supports remote desktop functionality, you have it.)

Recording

vnc2swf.py program captures a VNC sessions and records it in either SWF or VNCLog format. This is a VNC client and communicates directly with a VNC server. A user need to start a VNC server in advance.

vnc2swf.py runs in two different modes: GUI (Graphical User Interface) mode and CLI (Command Line Interface) mode. In the GUI mode, start recording by clicking the "Start" button. Then choose the "Save as..." command from the "File" menu to save the recorded movie to a file. In the CLI mode, a user needs to specify the output filename from command line. Recording is started immediately. In both modes, a user is prompted for a VNC password if the server requires authentication (and unless the user doesn't specify the password file). In the CLI mode, hit Control-C to stop recording. After finishing recording, it generates two files with the specified name: a .swf and .html file. The .html file contains an HTML tag and a javascript code to provide seek bar function.

A user can choose three different methods to encode movie image: "flv", "swf5", "swf7", "mpeg" (PyMedia required), or "vnc". The first swf5 encoding (default) provides a reasonable movie size. The second encoding method, swf7 provides a smaller SWF movie. This is, however, not recommended to use within vnc2swf.py for two reasons: This type of encoding is only supported by Flash Player version 7 or newer. Also, generating a movie with on-the-fly swf7 encoding is slower so you might experience frame dropping. Actually, you can convert a swf5-encoded movie into swf7-encoded one after recording by using edit.py, so anyway you don't need to use this method when recording. The third encoding method is vnc. This method generates a .vnc (VNCLog) file, which is compatible with vncrec output file. You can convert it to a SWF movie with edit.py. A .vnc file is not a SWF movie by itself, but its encoding is the fastest.

NOTE: Unlike the C version, vnc2swf.py doesn't handle any user interaction. If you want to control the server's desktop, you need to launch vncviewer or its equivalent separately.

Syntax

  • (GUI) $ vnc2swf.py [-o filename] [options] [host[:display] [port]]
  • (CLI) $ vnc2swf.py -n -o filename [options] [host[:display] [port]]

Example

(Record a virtual screen)
$ vncserver -geometry 640x480
$ vnc2swf.py -o out.swf localhost:1

(Record an existing screen)
$ x11vnc -localhost -viewonly -wait 10 -defer 10 -bg
$ vnc2swf.py -o out.swf localhost:0

(Record a remote screen)
$ vnc2swf.py -n -o out.flv vnc.example.com:1

If you're using x11vnc, see also recordwin. This is a convenient script to record a particular window.

Recording Tips

  • Use as small screen size as possible, or try to specify the smallest clipping rectangle. With a large screen, VNC's screen polling gets slower and vnc2swf also gets slower for converting bigger images. Using a small screen also helps reducing the movie size.
  • Reducing the framerate sometimes helps reducing the movie size. If the generated movie is still very big, try resampling frames or scaling the image with edit.py.
  • To record a long movie (more then 20min.), the only available option is to use flv (or mpeg if you have PyMedia). swf5 or swf7 files have a limitation and lcan have up to 16000 frames, which is roughly 22 minutes.
  • If you experience frame dropping, try changing the server options for screen polling (if any). If you're using x11vnc, try adding -wait 10 -defer 10.
  • When recording flv movie, you can dump a live stream output to stdout by speficing '-' as a filename. But currently I have no idea how to use. (broadcasting your desktop on the web?)

Options

-n
Console mode (no GUI).

-o outputfile
Specifies the output filename. This option is required in CLI mode. In GUI mode, when not specified it prompts the user for the filename. The movie encoding type is usually inferred from the filename, so the filename should end with either ".swf" or ".vnc". Otherwise, the user need to specify the output movie encoding with -t option (see below.)

-C clipping
Specifies the clipping rectangle. The geometry must be as form of "widthxheight+left+top" (e.g. "400x300+120+0"). Unlike other X11 applications, all rectangular components are required. Negative values are not supported.

-r framerate
Specifies the framerate in fps. (default=12.0)

-t encodingtype
Specifies the output movie encoding method ("flv", "mpeg", "swf5", "swf7" or "vnc"). When omitted, the encoding type is automatically inferred from the filename (*.swf = swf5, *.vnc = vnc).

-N
Disables cursor pseudo-encoding. Pyvnc2swf normally tries to use cursor pseudo-encoding to capture a mouse cursor position so that a cursor can be moved separately from a screen image and it reduces the movie size. However this might not work with some vnc servers. This option can be used for disabling the function.

-P passwdfile
Specifies a password file. If specified, its content is automatically loaded and supplied as password when a VNC server requires it. A password file contains an encrypted password string and can be created with vncpasswd. A user can directly specify ~/.vnc/passwd, which normally contains the password for the local vnc server.

-e vncencodings
Specifies the preferred encodings for VNC image transfer (this is different from movie encoding). Normally you don't need to change this option. Encodings are comma-separated integers (default="5,4,0"). Changing encodings might improve recording performance.

-S subprocess (Supported on Un*x only, Python 2.4 or above is required)
Set a command to run during recording. This option is useful for recording voice with a separated program. A command line is a space-separated sequence of arguments which are passed to a child process like a usual shell command line. However the command line is not passed to a shell and the arguments are directly passed to the child process without any substitution. A child process is started immediately after recording starts and sent SIGINT after recording finishes.

-d
Increases debug level.


Editing

edit.py program is for editing or reorganizing one or multiple movies generated by vnc2swf.py. This program also supports converting a .vnc file into .swf movie, changing the encoding method (swf5->swf7), attaching MP3 audio file to a movie, extracting images from a movie and resampling/scaling/clipping a movie image.

edit.py currently supports only command line interface. The user must give one output filename and one or more input filename(s). Input movies are concatenated sequentially (in the specified order) and the desired effects are applied.

Syntax

$ edit.py -o outfile.swf [options] infile ...

Example

(Convert .vnc file into .swf with compressed video encoding)
$ edit.py -o out.swf -c -t swf7 input.vnc

(Attach an mp3 file to .swf)
$ edit.py -o out.swf -a voice.mp3 in.swf

(Concatenate two movies and extract the frames into another movie)
$ edit.py -o out.swf -f 100-200,350- movie1.swf movie2.swf

(Clip the top left area of the movie and shrink it to half the size)
$ edit.py -o small.swf -C 320x240+0+0 -s 0.5 in.swf

(Convert .swf into MPEG)
$ edit.py -o out.mpg input.swf

(Convert .swf into .flv)
$ edit.py -o out.flv input.swf

Options

-o outputfile
Specifies the output filename. This option is always required.

-c
Compress a movie with zlib. Compressions is supported for both (swf5 and swf7) types of movies, but usually it's most effective when applied to swf7-encoded movies.

-t encodingtype
Specifies the encoding method for an output movie. When this option is not specified, the encoding type is inferred from the output filename extention.

TypeExtensionDescription
swf5.swfSWF movie (default)
swf7SWF movie with stream video encoding support
flv.flvFLV movie
mpeg.mpgMPEG movie (requires PyMedia)
bmp.bmpBMP image sequence
png.pngPNG image sequence

Note that swf7 encoding is supported only with Flash Player version 7 or newer.

-f frames or
-F frames
Specifies a sequence of frames presented in the output movie. When omitted, all frames are presented in the original order (default). Frames can be specified with a comma-separated list of integers. A range of integers can be also specified by using - (hyphen) sign. For example: 10,200,300-400 specifies the frames whose number is 10 and 200, plus every frame between frame 300 and 400. The beginning (or ending) frame number can be omitted (e.g. -100 or 300-). In this case, the first (or last) frame number is used as the other end of the range.

-F option and -f option is same except that -F doesn't chop the audio while -f does. When you're putting audio on the movie, if you use -f and -a option at the same time, it chops an mp3 file according to the selected frames. However, when you want to cast a continuous sound (such as music) onto a whole movie, this might not be the desired effect. In such a case, use -F instead of -f.

-a mp3file
Attaches mp3 file(s) to the movie. (Adding audio to FLV format is currently not supported.) Multiple mp3 files are concatenated in the specified order.
NOTE: When specifying multiple mp3 files, make sure every file has the same bitrate as the first one. And do NOT use "variable bitrate" (VBR) mp3 files, as the SWF format doesn't support them!

-s scaling
Rescale the movie image with a specified ratio which is given as a fraction.
NOTE: There will be noises with non-multiple scaling (e.g. 0.7).

-C clipping
Clips the movie into a specified rectangle. The geometry must be as form of "widthxheight+left+top" (e.g. "400x300+120+0").

-K keyinterval
Insert a keyframe in every N frames. Keyframes work as hints for Flash Player and are useful for seeking a frame within a long movie. When the number of total frames exceeds 10,000, inserting keyframes in every 500 frames (-K 500) is recommended.

-r framerate
Changes the frame rate of the movie. This option itself doesn't do any frame resampling and simply changes the movie speed. When this option is omitted, edit.py tries to keep the original frame rate in the output movie.

-R resampleframes
Resample (or "thin down") a movie by picking one from every N frames.

-S skipmp3frames
Skip the first N mp3-frames of the mp3 file. When 's' is appended to the number (such as '1.0s'), it indicates the number of seconds instead of frames. This option is useful when there is a time lag between a recorded image and audio.

-B blocksize
Sets the blocksize for swf7 or flv encoding method (default=32). This must be a multiple of 16.

-b
Suppress a seekbar in a generated html file.

-l
Disables movie loop in a generated html file.

-d
Increases debug level.


Previewing

play.py is a simple player for a .swf or .vnc file. This program might be useful for spotting the right frame number in a recorded movie. However its speed is awkward and audio output is not supported. It only supports vnc2swf-generated files and cannot play general SWF movies.

The player accepts the following keys:

  • Space / Enter / mouse click: Toggle Play/Stop.
  • Left / Right / dragging a seek bar: Skip frames.
  • "q" / Escape: Quit.
  • "s": Take a snapshot. The image is saved as "inputfile-frameno.bmp".

Syntax

$ play.py [options] moviefile ...

Options

-r framerate
Specifies the framerate to play.

-s scaling
Specifies the scaling ratio with a fraction.

-C clipping
Specifies the clipping rectangle. The geometry must be as form of "widthxheight+left+top" (e.g. "400x300+120+0").

-d
Increases debug level.


Frequently Asked Questions

I found weird artifacts in a recorded movie. How to fix it?
Probably you played the SWF file directly. A SWF movie should be played in the exactly same size as it is recorded. Use an HTML file generated by vnc2swf.py or edit.py.

How to make a movie with audio?
Record your audio separately and encode it with an MP3 encoder like Lame. Then combine them with edit.py.

A browser freezes when I try to seek within a movie.
Put keyframes in every hundreds frame. Try -K option in edit.py. Normally putting keyframes in every 500 frames (-K 500) gives a reasonable result.


Structure of the Program

For those who are interested in learning or extending the program, I drew a simple figure to explain how data goes between objects within pyvnc2swf.


Yusuke Shinyama <yusuke at cs dot nyu dot edu>
pyvnc2swf-0.9.5/docs/recordwin.html000066400000000000000000000042441111303746100173010ustar00rootroot00000000000000 recordwin

recordwin

A wrapper program for pyvnc2swf and x11vnc.

$Id: recordwin.html,v 1.2 2008/11/16 02:39:40 euske Exp $


Syntax

recordwin.sh [-all] [-display displayname] [-name windowtitle] [-id windowid] [-type filetype] outputfile

Example

(Manually specify the window to record)
$ recordwin.sh out.swf

(Record the window titled "login@giko")
$ recordwin.sh -name "login@giko" out.flv

Description

recordwin allows you to record a specific window in the current screen instead of the entire display. This is a wrapper program for vnc2swf.py and x11vnc. First it invokes xwininfo program to get the position of a target window. If no option is given, a user is prompted to specify a window with a cross (+) cursor. Then it runs x11vnc in background and starts vnc2swf.py with a proper option to record only the window you specified. With the option -all specified, the entire screen is recorded.

Security consideration: The user must be aware that recordwin will open up x11vnc to accept any local client that comes first. Although this client is only allowed to peek the current screen, this can be potentially security threat. So don't use this if your desktop machine allows other users to login.

Bugs

  • Error handling is poor.
  • To prevent a possibly unwanted access, recordwin should have set a temporaly password for x11vnc.

See Also


Yusuke Shinyama
<yusuke at cs dot nyu dot edu> pyvnc2swf-0.9.5/pyvnc2swf/000077500000000000000000000000001111303746100154245ustar00rootroot00000000000000pyvnc2swf-0.9.5/pyvnc2swf/Makefile000066400000000000000000000020231111303746100170610ustar00rootroot00000000000000# Makefile (only for maintainance purpose) # $Id: Makefile,v 1.1 2008/11/15 10:07:09 euske Exp $ # # Copyright (C) 2005 by Yusuke Shinyama (yusuke at cs . nyu . edu) # 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. # all: clean: -rm *.pyc *~ '.#*' .DS_Store test: x11vnc -quiet -localhost -viewonly -nopw -bg ./vnc2swf.py -n -o out.swf localhost:0 pyvnc2swf-0.9.5/pyvnc2swf/__init__.py000066400000000000000000000000001111303746100175230ustar00rootroot00000000000000pyvnc2swf-0.9.5/pyvnc2swf/d3des.py000077500000000000000000000331571111303746100170140ustar00rootroot00000000000000#!/usr/bin/env python ## ## pyvnc2swf - d3des.py ## ## $Id: d3des.py,v 1.1 2007/04/25 16:55:25 euske Exp $ ## ## Copyright (C) 2007 by Yusuke Shinyama (yusuke at cs . nyu . edu) ## 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. ## # The following code is derived from vncviewer/rfb/d3des.c (GPL). # This is D3DES (V5.09) by Richard Outerbridge with the double and # triple-length support removed for use in VNC. Also the bytebit[] array # has been reversed so that the most significant bit in each byte of the # key is ignored, not the least significant. # # These changes are: # Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. # # 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. # # # D3DES (V5.09) - # # A portable, public domain, version of the Data Encryption Standard. # # Written with Symantec's THINK (Lightspeed) C by Richard Outerbridge. # Thanks to: Dan Hoey for his excellent Initial and Inverse permutation # code; Jim Gillogly & Phil Karn for the DES key schedule code; Dennis # Ferguson, Eric Young and Dana How for comparing notes; and Ray Lau, # for humouring me on. # # Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge. # (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992. # from struct import pack, unpack bytebit = [ 01, 02, 04, 010, 020, 040, 0100, 0200 ] bigbyte = [ 0x800000L, 0x400000L, 0x200000L, 0x100000L, 0x80000L, 0x40000L, 0x20000L, 0x10000L, 0x8000L, 0x4000L, 0x2000L, 0x1000L, 0x800L, 0x400L, 0x200L, 0x100L, 0x80L, 0x40L, 0x20L, 0x10L, 0x8L, 0x4L, 0x2L, 0x1L ] # Use the key schedule specified in the Standard (ANSI X3.92-1981). pc1 = [ 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3 ] totrot = [ 1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28 ] pc2 = [ 13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9, 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1, 40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47, 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31 ] def deskey(key, decrypt): # Thanks to James Gillogly & Phil Karn! key = unpack('8B', key) pc1m = [0]*56 pcr = [0]*56 kn = [0L]*32 for j in range(56): l = pc1[j] m = l & 07 if key[l >> 3] & bytebit[m]: pc1m[j] = 1 else: pc1m[j] = 0 for i in range(16): if decrypt: m = (15 - i) << 1 else: m = i << 1 n = m + 1 kn[m] = kn[n] = 0L for j in range(28): l = j + totrot[i] if l < 28: pcr[j] = pc1m[l] else: pcr[j] = pc1m[l - 28] for j in range(28, 56): l = j + totrot[i] if l < 56: pcr[j] = pc1m[l] else: pcr[j] = pc1m[l - 28] for j in range(24): if pcr[pc2[j]]: kn[m] |= bigbyte[j] if pcr[pc2[j+24]]: kn[n] |= bigbyte[j] return cookey(kn) def cookey(raw): key = [] for i in range(0, 32, 2): (raw0, raw1) = (raw[i], raw[i+1]) k = (raw0 & 0x00fc0000L) << 6 k |= (raw0 & 0x00000fc0L) << 10 k |= (raw1 & 0x00fc0000L) >> 10 k |= (raw1 & 0x00000fc0L) >> 6 key.append(k) k = (raw0 & 0x0003f000L) << 12 k |= (raw0 & 0x0000003fL) << 16 k |= (raw1 & 0x0003f000L) >> 4 k |= (raw1 & 0x0000003fL) key.append(k) return key SP1 = [ 0x01010400L, 0x00000000L, 0x00010000L, 0x01010404L, 0x01010004L, 0x00010404L, 0x00000004L, 0x00010000L, 0x00000400L, 0x01010400L, 0x01010404L, 0x00000400L, 0x01000404L, 0x01010004L, 0x01000000L, 0x00000004L, 0x00000404L, 0x01000400L, 0x01000400L, 0x00010400L, 0x00010400L, 0x01010000L, 0x01010000L, 0x01000404L, 0x00010004L, 0x01000004L, 0x01000004L, 0x00010004L, 0x00000000L, 0x00000404L, 0x00010404L, 0x01000000L, 0x00010000L, 0x01010404L, 0x00000004L, 0x01010000L, 0x01010400L, 0x01000000L, 0x01000000L, 0x00000400L, 0x01010004L, 0x00010000L, 0x00010400L, 0x01000004L, 0x00000400L, 0x00000004L, 0x01000404L, 0x00010404L, 0x01010404L, 0x00010004L, 0x01010000L, 0x01000404L, 0x01000004L, 0x00000404L, 0x00010404L, 0x01010400L, 0x00000404L, 0x01000400L, 0x01000400L, 0x00000000L, 0x00010004L, 0x00010400L, 0x00000000L, 0x01010004L ] SP2 = [ 0x80108020L, 0x80008000L, 0x00008000L, 0x00108020L, 0x00100000L, 0x00000020L, 0x80100020L, 0x80008020L, 0x80000020L, 0x80108020L, 0x80108000L, 0x80000000L, 0x80008000L, 0x00100000L, 0x00000020L, 0x80100020L, 0x00108000L, 0x00100020L, 0x80008020L, 0x00000000L, 0x80000000L, 0x00008000L, 0x00108020L, 0x80100000L, 0x00100020L, 0x80000020L, 0x00000000L, 0x00108000L, 0x00008020L, 0x80108000L, 0x80100000L, 0x00008020L, 0x00000000L, 0x00108020L, 0x80100020L, 0x00100000L, 0x80008020L, 0x80100000L, 0x80108000L, 0x00008000L, 0x80100000L, 0x80008000L, 0x00000020L, 0x80108020L, 0x00108020L, 0x00000020L, 0x00008000L, 0x80000000L, 0x00008020L, 0x80108000L, 0x00100000L, 0x80000020L, 0x00100020L, 0x80008020L, 0x80000020L, 0x00100020L, 0x00108000L, 0x00000000L, 0x80008000L, 0x00008020L, 0x80000000L, 0x80100020L, 0x80108020L, 0x00108000L ] SP3 = [ 0x00000208L, 0x08020200L, 0x00000000L, 0x08020008L, 0x08000200L, 0x00000000L, 0x00020208L, 0x08000200L, 0x00020008L, 0x08000008L, 0x08000008L, 0x00020000L, 0x08020208L, 0x00020008L, 0x08020000L, 0x00000208L, 0x08000000L, 0x00000008L, 0x08020200L, 0x00000200L, 0x00020200L, 0x08020000L, 0x08020008L, 0x00020208L, 0x08000208L, 0x00020200L, 0x00020000L, 0x08000208L, 0x00000008L, 0x08020208L, 0x00000200L, 0x08000000L, 0x08020200L, 0x08000000L, 0x00020008L, 0x00000208L, 0x00020000L, 0x08020200L, 0x08000200L, 0x00000000L, 0x00000200L, 0x00020008L, 0x08020208L, 0x08000200L, 0x08000008L, 0x00000200L, 0x00000000L, 0x08020008L, 0x08000208L, 0x00020000L, 0x08000000L, 0x08020208L, 0x00000008L, 0x00020208L, 0x00020200L, 0x08000008L, 0x08020000L, 0x08000208L, 0x00000208L, 0x08020000L, 0x00020208L, 0x00000008L, 0x08020008L, 0x00020200L ] SP4 = [ 0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L, 0x00802080L, 0x00800081L, 0x00800001L, 0x00002001L, 0x00000000L, 0x00802000L, 0x00802000L, 0x00802081L, 0x00000081L, 0x00000000L, 0x00800080L, 0x00800001L, 0x00000001L, 0x00002000L, 0x00800000L, 0x00802001L, 0x00000080L, 0x00800000L, 0x00002001L, 0x00002080L, 0x00800081L, 0x00000001L, 0x00002080L, 0x00800080L, 0x00002000L, 0x00802080L, 0x00802081L, 0x00000081L, 0x00800080L, 0x00800001L, 0x00802000L, 0x00802081L, 0x00000081L, 0x00000000L, 0x00000000L, 0x00802000L, 0x00002080L, 0x00800080L, 0x00800081L, 0x00000001L, 0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L, 0x00802081L, 0x00000081L, 0x00000001L, 0x00002000L, 0x00800001L, 0x00002001L, 0x00802080L, 0x00800081L, 0x00002001L, 0x00002080L, 0x00800000L, 0x00802001L, 0x00000080L, 0x00800000L, 0x00002000L, 0x00802080L ] SP5 = [ 0x00000100L, 0x02080100L, 0x02080000L, 0x42000100L, 0x00080000L, 0x00000100L, 0x40000000L, 0x02080000L, 0x40080100L, 0x00080000L, 0x02000100L, 0x40080100L, 0x42000100L, 0x42080000L, 0x00080100L, 0x40000000L, 0x02000000L, 0x40080000L, 0x40080000L, 0x00000000L, 0x40000100L, 0x42080100L, 0x42080100L, 0x02000100L, 0x42080000L, 0x40000100L, 0x00000000L, 0x42000000L, 0x02080100L, 0x02000000L, 0x42000000L, 0x00080100L, 0x00080000L, 0x42000100L, 0x00000100L, 0x02000000L, 0x40000000L, 0x02080000L, 0x42000100L, 0x40080100L, 0x02000100L, 0x40000000L, 0x42080000L, 0x02080100L, 0x40080100L, 0x00000100L, 0x02000000L, 0x42080000L, 0x42080100L, 0x00080100L, 0x42000000L, 0x42080100L, 0x02080000L, 0x00000000L, 0x40080000L, 0x42000000L, 0x00080100L, 0x02000100L, 0x40000100L, 0x00080000L, 0x00000000L, 0x40080000L, 0x02080100L, 0x40000100L ] SP6 = [ 0x20000010L, 0x20400000L, 0x00004000L, 0x20404010L, 0x20400000L, 0x00000010L, 0x20404010L, 0x00400000L, 0x20004000L, 0x00404010L, 0x00400000L, 0x20000010L, 0x00400010L, 0x20004000L, 0x20000000L, 0x00004010L, 0x00000000L, 0x00400010L, 0x20004010L, 0x00004000L, 0x00404000L, 0x20004010L, 0x00000010L, 0x20400010L, 0x20400010L, 0x00000000L, 0x00404010L, 0x20404000L, 0x00004010L, 0x00404000L, 0x20404000L, 0x20000000L, 0x20004000L, 0x00000010L, 0x20400010L, 0x00404000L, 0x20404010L, 0x00400000L, 0x00004010L, 0x20000010L, 0x00400000L, 0x20004000L, 0x20000000L, 0x00004010L, 0x20000010L, 0x20404010L, 0x00404000L, 0x20400000L, 0x00404010L, 0x20404000L, 0x00000000L, 0x20400010L, 0x00000010L, 0x00004000L, 0x20400000L, 0x00404010L, 0x00004000L, 0x00400010L, 0x20004010L, 0x00000000L, 0x20404000L, 0x20000000L, 0x00400010L, 0x20004010L ] SP7 = [ 0x00200000L, 0x04200002L, 0x04000802L, 0x00000000L, 0x00000800L, 0x04000802L, 0x00200802L, 0x04200800L, 0x04200802L, 0x00200000L, 0x00000000L, 0x04000002L, 0x00000002L, 0x04000000L, 0x04200002L, 0x00000802L, 0x04000800L, 0x00200802L, 0x00200002L, 0x04000800L, 0x04000002L, 0x04200000L, 0x04200800L, 0x00200002L, 0x04200000L, 0x00000800L, 0x00000802L, 0x04200802L, 0x00200800L, 0x00000002L, 0x04000000L, 0x00200800L, 0x04000000L, 0x00200800L, 0x00200000L, 0x04000802L, 0x04000802L, 0x04200002L, 0x04200002L, 0x00000002L, 0x00200002L, 0x04000000L, 0x04000800L, 0x00200000L, 0x04200800L, 0x00000802L, 0x00200802L, 0x04200800L, 0x00000802L, 0x04000002L, 0x04200802L, 0x04200000L, 0x00200800L, 0x00000000L, 0x00000002L, 0x04200802L, 0x00000000L, 0x00200802L, 0x04200000L, 0x00000800L, 0x04000002L, 0x04000800L, 0x00000800L, 0x00200002L ] SP8 = [ 0x10001040L, 0x00001000L, 0x00040000L, 0x10041040L, 0x10000000L, 0x10001040L, 0x00000040L, 0x10000000L, 0x00040040L, 0x10040000L, 0x10041040L, 0x00041000L, 0x10041000L, 0x00041040L, 0x00001000L, 0x00000040L, 0x10040000L, 0x10000040L, 0x10001000L, 0x00001040L, 0x00041000L, 0x00040040L, 0x10040040L, 0x10041000L, 0x00001040L, 0x00000000L, 0x00000000L, 0x10040040L, 0x10000040L, 0x10001000L, 0x00041040L, 0x00040000L, 0x00041040L, 0x00040000L, 0x10041000L, 0x00001000L, 0x00000040L, 0x10040040L, 0x00001000L, 0x00041040L, 0x10001000L, 0x00000040L, 0x10000040L, 0x10040000L, 0x10040040L, 0x10000000L, 0x00040000L, 0x10001040L, 0x00000000L, 0x10041040L, 0x00040040L, 0x10000040L, 0x10040000L, 0x10001000L, 0x10001040L, 0x00000000L, 0x10041040L, 0x00041000L, 0x00041000L, 0x00001040L, 0x00001040L, 0x00040040L, 0x10000000L, 0x10041000L ] def desfunc(block, keys): (leftt, right) = unpack('>II', block) work = ((leftt >> 4) ^ right) & 0x0f0f0f0fL right ^= work leftt ^= (work << 4) work = ((leftt >> 16) ^ right) & 0x0000ffffL right ^= work leftt ^= (work << 16) work = ((right >> 2) ^ leftt) & 0x33333333L leftt ^= work right ^= (work << 2) work = ((right >> 8) ^ leftt) & 0x00ff00ffL leftt ^= work right ^= (work << 8) right = ((right << 1) | ((right >> 31) & 1L)) & 0xffffffffL work = (leftt ^ right) & 0xaaaaaaaaL leftt ^= work right ^= work leftt = ((leftt << 1) | ((leftt >> 31) & 1L)) & 0xffffffffL for i in range(0, 32, 4): work = (right << 28) | (right >> 4) work ^= keys[i] fval = SP7[ work & 0x3fL] fval |= SP5[(work >> 8) & 0x3fL] fval |= SP3[(work >> 16) & 0x3fL] fval |= SP1[(work >> 24) & 0x3fL] work = right ^ keys[i+1] fval |= SP8[ work & 0x3fL] fval |= SP6[(work >> 8) & 0x3fL] fval |= SP4[(work >> 16) & 0x3fL] fval |= SP2[(work >> 24) & 0x3fL] leftt ^= fval work = (leftt << 28) | (leftt >> 4) work ^= keys[i+2] fval = SP7[ work & 0x3fL] fval |= SP5[(work >> 8) & 0x3fL] fval |= SP3[(work >> 16) & 0x3fL] fval |= SP1[(work >> 24) & 0x3fL] work = leftt ^ keys[i+3] fval |= SP8[ work & 0x3fL] fval |= SP6[(work >> 8) & 0x3fL] fval |= SP4[(work >> 16) & 0x3fL] fval |= SP2[(work >> 24) & 0x3fL] right ^= fval right = (right << 31) | (right >> 1) work = (leftt ^ right) & 0xaaaaaaaaL leftt ^= work right ^= work leftt = (leftt << 31) | (leftt >> 1) work = ((leftt >> 8) ^ right) & 0x00ff00ffL right ^= work leftt ^= (work << 8) work = ((leftt >> 2) ^ right) & 0x33333333L right ^= work leftt ^= (work << 2) work = ((right >> 16) ^ leftt) & 0x0000ffffL leftt ^= work right ^= (work << 16) work = ((right >> 4) ^ leftt) & 0x0f0f0f0fL leftt ^= work right ^= (work << 4) leftt &= 0xffffffffL right &= 0xffffffffL return pack('>II', right, leftt) # from vncviewer/rfb/vncauth.c: fixedkey = [ 23,82,107,6,35,78,88,7 ] def decrypt_passwd(data): dk = deskey(pack('8B', *fixedkey), True) return desfunc(data, dk) def generate_response(passwd, challange): ek = deskey((passwd+'\x00'*8)[:8], False) return desfunc(challange[:8], ek) + desfunc(challange[8:], ek) # test if __name__ == '__main__': key = 'test1234' plain = 'hello321' cipher = '\xb4f\x01UnZ1\t' ek = deskey(key, False) dk = deskey(key, True) assert desfunc(plain, ek) == cipher assert desfunc(desfunc(plain, ek), dk) == plain assert desfunc(desfunc(plain, dk), ek) == plain print 'test succeeded.' pyvnc2swf-0.9.5/pyvnc2swf/edit.py000077500000000000000000000170431111303746100167330ustar00rootroot00000000000000#!/usr/bin/env python ## ## pyvnc2swf - edit.py ## ## $Id: edit.py,v 1.6 2008/11/16 02:39:40 euske Exp $ ## ## Copyright (C) 2005 by Yusuke Shinyama (yusuke at cs . nyu . edu) ## 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. ## import sys, re from movie import SWFInfo, MovieContainer from output import FLVVideoStream, MPEGVideoStream, SWFVideoStream, \ SWFShapeStream, ImageSequenceStream, MovieBuilder stderr = sys.stderr # range2list: converts strings like "1,5-8" to [1,5,6,7,8]. class RangeError(ValueError): pass def range2list(s, n0, n1, step=1): PAT_RANGE = re.compile(r'^([0-9]*)-([0-9]*)$') r = [] for i in s.split(','): i = i.strip() if not i: continue if i.isdigit(): n = int(i) if n0 <= n and n <= n1: r.append(n) else: raise RangeError('%d: must be in %d...%d' % (n,n0,n1)) else: m = PAT_RANGE.match(i.strip()) if not m: raise RangeError('%r: illegal number' % i) b = n0 if m.group(1): b = int(m.group(1)) e = n1 if m.group(2): e = int(m.group(2)) if e < b: (b,e) = (e,b) if b < n0: raise RangeError('%d: must be in %d...%d' % (b,n0,n1)) if n1 < e: raise RangeError('%d: must be in %d...%d' % (e,n0,n1)) r.extend(xrange(b,e+1,step)) return r # reorganize def reorganize(info, stream, moviefiles, range_str='-', loop=True, seekbar=True, step=1, kfinterval=0, mp3seek=True, mp3skip=0, debug=0): movie = MovieContainer(info) for fname in moviefiles: if fname.endswith('.swf'): # vnc2swf file movie.parse_vnc2swf(fname, True, debug=debug) elif fname.endswith('.flv'): # flv file movie.parse_flv(fname, True, debug=debug) elif fname.endswith('.vnc'): # vncrec file movie.parse_vncrec(fname, debug=debug) else: raise ValueError('unsupported format: %r' % fname) r = range2list(range_str, 0, movie.nframes-1, step) if movie.info.mp3: if isinstance(mp3skip, float): mp3skip = int(mp3skip * movie.info.mp3.sample_rate) movie.info.mp3.set_initial_skip(mp3skip) builder = MovieBuilder(movie, stream, mp3seek=mp3seek, kfinterval=kfinterval, debug=debug) builder.build(r) stream.close() movie.info.write_html(seekbar=seekbar, loop=loop) return 0 # main def main(argv): import getopt def usage(): print >>stderr, '''usage: %s [-d] [-c] [-t type] [-f|-F frames] [-a mp3file] [-r framerate] [-S mp3sampleskip] [-C WxH+X+Y] [-B blocksize] [-K keyframe] [-R framestep] [-s scaling] -o outfile.swf file1 file2 ... Specify one output filename from the following: *.swf: generate a SWF movie. *.flv: generate a FLV movie. *.mpg: generate a MPEG movie. *.png|*.bmp: save snapshots of given frames as "X-nnn.png" -d: debug mode. -c: compression. -t {swf5,swf7,flv,mpeg,png,bmp}: specify the output movie type. -f(-F) frames: frames to extract. e.g. 1-2,100-300,310,500- -F disables seeking audio. -R framestep: frame resampling step (default: 1) -s scaling: scale factor (default: 1.0) -a filename: attach MP3 file(s). (multiple files can be specified) -r framerate: override framerate. -B blocksize: (SWF7 and FLV mode only) blocksize of video packet (must be a multiple of 16) -K keyframe: keyframe interval -S N[s]: skip the first N samples (or N seconds) of the sound when the movie starts. -C WxH+X+Y: crop a specific area of the movie. -b: disable seekbar. -l: disable loop. -z: make the movie scalable. ''' % argv[0] return 100 try: (opts, args) = getopt.getopt(argv[1:], 'dr:o:t:cHa:S:C:B:K:f:F:R:s:blz') except getopt.GetoptError: return usage() # debug = 0 info = SWFInfo() range_str = '-' step = 1 streamtype = None kfinterval = 0 mp3skip = 0 mp3seek = True loop = True seekbar = True for (k, v) in opts: if k == '-d': debug += 1 elif k == '-r': info.set_framerate(float(v)) elif k == '-o': info.filename = v elif k == '-t': v = v.lower() if v not in ('swf5','swf7','mpeg','mpg','flv','png','bmp','gif'): print >>stderr, 'Invalid output type:', v return usage() streamtype = v elif k == '-a': fp = file(v, 'rb') print >>stderr, 'Reading mp3 file: %s...' % v info.reg_mp3blocks(fp) fp.close() elif k == '-S': if v.endswith('s'): mp3skip = float(v[:-1]) else: mp3skip = int(v) elif k == '-C': try: info.set_clipping(v) except ValueError: print >>stderr, 'Invalid clipping specification:', v return usage() elif k == '-B': blocksize = int(v) assert 0 < blocksize and blocksize <= 256 and blocksize % 16 == 0, 'Invalid block size.' info.blocksize = blocksize elif k == '-K': kfinterval = int(v) elif k == '-c': info.compression = True elif k == '-f': range_str = v elif k == '-F': range_str = v mp3seek = False elif k == '-R': step = int(v) mp3seek = False elif k == '-s': info.scaling = float(v) assert 0 < info.scaling and info.scaling <= 1.0, 'Invalid scaling.' elif k == '-b': seekbar = False elif k == '-l': loop = False elif k == '-z': info.set_scalable(True) if not args: print >>stderr, 'Specify at least one input movie.' return usage() if not info.filename: print >>stderr, 'Specify exactly one output file.' return usage() if not streamtype: v = info.filename if v.endswith('.swf'): streamtype = 'swf5' elif v.endswith('.png'): streamtype = 'png' elif v.endswith('.bmp'): streamtype = 'bmp' elif v.endswith('.gif'): streamtype = 'gif' elif v.endswith('.mpg') or v.endswith('.mpeg'): streamtype = 'mpeg' elif v.endswith('.flv'): streamtype = 'flv' else: print >>stderr, 'Unknown stream type.' return 100 if streamtype == 'mpeg' and not MPEGVideoStream: print >>stderr, 'MPEGVideoStream is not supported.' return 100 stream = None if streamtype == 'swf5': stream = SWFShapeStream(info, debug=debug) elif streamtype == 'swf7': stream = SWFVideoStream(info, debug=debug) elif streamtype in ('mpg', 'mpeg'): stream = MPEGVideoStream(info, debug=debug) elif streamtype == 'flv': stream = FLVVideoStream(info, debug=debug) else: stream = ImageSequenceStream(info, debug=debug) try: return reorganize(info, stream, args, range_str, loop=loop, seekbar=seekbar, step=step, kfinterval=kfinterval, mp3seek=mp3seek, mp3skip=mp3skip, debug=debug) except RangeError, e: print >>stderr, 'RangeError:', e return 100 if __name__ == "__main__": sys.exit(main(sys.argv)) pyvnc2swf-0.9.5/pyvnc2swf/html_templates.py000066400000000000000000000213171111303746100210240ustar00rootroot00000000000000#!/usr/bin/env python ## ## pyvnc2swf - seekbar.py ## ## $Id: html_templates.py,v 1.2 2008/11/15 10:07:09 euske Exp $ ## ## Copyright (C) 2005 by Yusuke Shinyama (yusuke at cs . nyu . edu) ## 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. ## import sys, os, os.path from swf import SWFParser PYVNC2SWF_VERSION = '0.9.2' ## Acknowledgement: ## ## The following javascript code is contributed by Jesse Ruderman ## (http://www.squarefree.com/). He kindly gave us permission to ## release his code under GPL. Thank you, Jesse! ## # seekbar header SEEKBAR_HEADER = """ """ # for emacs coloring: " # normal header NORMAL_HEADER = "\n" # generate_html def generate_html(out, fname, seekbar=True, loop=True): parser = SWFParser() parser.open(fname, header_only=True) (x,width, y,height) = parser.rect basename = os.path.basename(fname) (title, ext) = os.path.splitext(basename) out.write('\n' '\n\n%s' % title) if seekbar: out.write(SEEKBAR_HEADER) else: out.write(NORMAL_HEADER) dic = { 'title':title, 'width':int(width/20), 'height':int(height/20), 'basename':basename, 'swf_version':parser.swf_version, 'loop':loop, 'pyvnc2swf_version': PYVNC2SWF_VERSION } out.write('

%(title)s

\n' '
\n' '\n' ' \n' ' \n' ' \n' ' \n' '\n' '
\n' '
\n' '
\n' 'Generated by pyvnc2swf-%(pyvnc2swf_version)s\n' '
\n' % dic) return # test if __name__ == '__main__': import getopt def usage(): print 'usage: %s [-S)eekbarless] [-L)oopless] file' % sys.argv[0] sys.exit(2) try: (opts, args) = getopt.getopt(sys.argv[1:], 'SL') except getopt.GetoptError: usage() (seekbar, loop) = (True, True) for (k, v) in opts: if k == '-S': seekbar = False elif k == '-L': loop = False if not args: usage() generate_html(sys.stdout, args[0], seekbar=seekbar, loop=loop) pyvnc2swf-0.9.5/pyvnc2swf/image.py000066400000000000000000000116251111303746100170650ustar00rootroot00000000000000#!/usr/bin/env python ## ## pyvnc2swf - image.py ## ## $Id: image.py,v 1.2 2008/11/15 12:05:08 euske Exp $ ## ## Copyright (C) 2005 by Yusuke Shinyama (yusuke at cs . nyu . edu) ## 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. ## import sys lowerbound = max upperbound = min # format: 1: solid, # 2: raw (uncompressed) # 3: DefineBitLossless # 4: SCREENVIDEOPACKET IMG_SOLID = 1 IMG_RAW = 2 IMG_LOSSLESS = 3 IMG_VIDEOPACKET = 4 def bgr2rgb(data): return ''.join([ data[i+2]+data[i+1]+data[i] for i in xrange(0, len(data), 3) ]) try: # try to pygame 1.6 or newer. import pygame print >>sys.stderr, 'Using pygame', pygame.ver pygame.init() try: pygame.mixer.quit() except NotImplementedError: pass def imgsize(img): return img.get_size() def create_image(w, h): return pygame.Surface((w, h), 0, 32) def create_image_from_string_rgb(w, h, data): return pygame.image.fromstring(data, (w, h), 'RGB') def create_image_from_string_rgbx(w, h, data): return pygame.image.fromstring(data, (w, h), 'RGBX') def create_image_from_string_xrgb(w, h, data): return pygame.image.fromstring(data[1:]+'x', (w, h), 'RGBX') def create_image_from_string_argb(w, h, data): data = ''.join([ data[i+1]+data[i+2]+data[i+3]+data[i] for i in xrange(0, len(data), 4) ]) return pygame.image.fromstring(data, (w, h), 'RGBA') def create_image_from_string_rgb_flipped(w, h, data): return pygame.image.fromstring(data, (w, h), 'RGB', 1) def crop_image(img, (x,y,w,h)): (wm,hm) = img.get_size() return img.subsurface((x,y,upperbound(wm-x,w),upperbound(hm-y,h))) def paste_image(dest, src, (x0, y0)): return dest.blit(src, (x0, y0)) def save_image(img, fname): if not fname.endswith('.bmp'): print >>sys.stderr, 'Warning: this format not supported by pygame, raw rgb is used instead.' return pygame.image.save(img, fname) def convert_image_to_string_rgb_flipped(img): return pygame.image.tostring(img, 'RGB', 1) def convert_image_to_string_rgb(img): return pygame.image.tostring(img, 'RGB') def convert_image_to_string_xrgb(img): return pygame.image.tostring(img, 'ARGB') def solid_fill(dest, rect, color): return dest.fill(color, rect) def scale_image(img, scaling): # this might cause segmentation faults sometimes :( # In that case, use the following instead: # (w,h) = img.get_size() # return pygame.transform.scale(img, (int(w*scaling), int(h*scaling))) return pygame.transform.rotozoom(img, 0, scaling) except ImportError: # use PIL instead pygame = None try: import Image except ImportError: print >>sys.stderr, 'Either Pygame or Python Imaging Library is required.' sys.exit(1) print >>sys.stderr, 'Using PIL', Image.VERSION def imgsize(img): return img.size def create_image(w, h): return Image.new('RGB', (w, h)) def create_image_from_string_rgb(w, h, data): return Image.fromstring('RGB', (w, h), data, 'raw', 'RGB') def create_image_from_string_rgbx(w, h, data): return Image.fromstring('RGB', (w, h), data, 'raw', 'RGBX') def create_image_from_string_xrgb(w, h, data): return Image.fromstring('RGB', (w, h), data[1:]+'x', 'raw', 'RGBX') def create_image_from_string_argb(w, h, data): return Image.fromstring('RGBA', (w, h), data, 'raw', 'ARGB') def create_image_from_string_rgb_flipped(w, h, data): return Image.fromstring('RGB', (w, h), data, 'raw', 'RGB').transpose(Image.FLIP_TOP_BOTTOM) def crop_image(img, (x0,y0,w,h)): (wm,hm) = img.size return img.crop((x0, y0, upperbound(x0+w,wm), upperbound(y0+h,hm))) def paste_image(dest, src, (x0, y0)): return dest.paste(src, (x0, y0)) def save_image(img, fname): return img.save(fname) def convert_image_to_string_rgb_flipped(img): return img.transpose(Image.FLIP_TOP_BOTTOM).tostring('raw', 'RGB') def convert_image_to_string_rgb(img): return img.tostring('raw', 'RGB') def convert_image_to_string_xrgb(img): return img.tostring('raw', 'XRGB') def solid_fill(dest, (x0,y0,w,h), color): return dest.paste(color, (x0, y0, x0+w, y0+h)) def scale_image(img, scaling): img = img.copy() (w,h) = img.size img.thumbnail((int(w*scaling), int(h*scaling)), resample=1) return img pyvnc2swf-0.9.5/pyvnc2swf/movie.py000066400000000000000000000361671111303746100171320ustar00rootroot00000000000000#!/usr/bin/env python ## ## pyvnc2swf - movie.py ## ## $Id: movie.py,v 1.4 2008/11/15 13:42:57 euske Exp $ ## ## Copyright (C) 2005 by Yusuke Shinyama (yusuke at cs . nyu . edu) ## 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. ## import sys, zlib, re from swf import SWFParser, FLVParser, CURSOR_DEPTH from mp3 import MP3Reader, MP3Storage from rfb import RFBMovieConverter from image import IMG_LOSSLESS, IMG_VIDEOPACKET import html_templates stderr = sys.stderr lowerbound = max upperbound = min ## SWFInfo ## class SWFInfo: """ SWFInfo holds information about headers and mp3 data in a SWF file. The values of this object are changed as parsing goes on. """ def __init__(self, filename=None): self.filename = filename self.compression = None self.clipping = None self.framerate = None self.scaling = None self.blocksize = None self.swf_version = None self.width = None self.height = None self.mp3 = None self.scalable = False return def __repr__(self): return '' % \ (self.filename, self.compression, self.clipping, self.framerate, self.scaling, self.blocksize, self.swf_version, self.mp3) def set_defaults(self, w0, h0): # size in pixels # THIS MUST BE CALLED BEFORE MovieOutputStream.open() if not self.clipping: self.clipping = (0,0,w0,h0) else: (w0,h0) = (self.clipping[2], self.clipping[3]) if self.scaling: (w0,h0) = (int(w0*self.scaling), int(h0*self.scaling)) if self.width != None and (self.width != w0 or self.height != h0): print >>stderr, 'Warning: movie size already set: %dx%d' % (self.width, self.height) elif self.width == None: (self.width, self.height) = (w0,h0) print >>stderr, 'Output movie size: %dx%d' % (self.width, self.height) if not self.framerate: self.framerate = 12.0 if not self.blocksize: self.blocksize = 32 return def set_scalable(self, scalable): self.scalable = scalable return def set_framerate(self, framerate): if self.framerate != None and self.framerate != framerate: print >>stderr, 'Warning: movie framerate is overridden.' return self.framerate = float(framerate) return def set_clipping(self, s): m = re.match(r'^(\d+)x(\d+)\+(\d+)\+(\d+)$', s) if not m: raise ValueError('Invalid clipping spec: %r' % s) f = map(int, m.groups()) self.clipping = (f[2],f[3], f[0],f[1]) return def get_clipping(self): if not self.clipping: raise ValueError('Clipping not set.') (x,y,w,h) = self.clipping return '%dx%d+%d+%d' % (w,h,x,y) def set_swf_version(self, swf_version): self.swf_version = swf_version return def set_mp3header(self, isstereo, mp3samplerate, mp3sampleskip): if not self.mp3: self.mp3 = MP3Storage() self.mp3.set_stereo(isstereo) self.mp3.set_sample_rate(mp3samplerate) self.mp3.set_initial_skip(mp3sampleskip) print >>stderr, 'MP3: stereo=%s, samplerate=%d, initialskip=%d' % (isstereo, mp3samplerate, mp3sampleskip) return def reg_mp3blocks(self, fp, length=None, nsamples=None, seeksamples=None): if not self.mp3: self.mp3 = MP3Storage() MP3Reader(self.mp3).read_mp3file(fp, length, nsamples, seeksamples) return def write_html(self, seekbar=True, loop=True, filename=None): if not (self.swf_version and self.width and self.height): return if not filename: filename = self.filename if filename.endswith('.swf'): outfname = filename.replace('.swf','.html') else: outfname = filename+'.html' print >>stderr, 'Writing: %s...' % outfname out = file(outfname, 'w') html_templates.generate_html(out, filename, seekbar=seekbar, loop=loop) out.close() return ## MovieContainer ## class MovieContainer: """ MovieContainer holds all frame images of a movie. """ def __init__(self, info): self.info = info self.nframes = 0 self.parsers = [] return # get frame def get_frame(self, i): (images, othertags, cursor_info) = ([], [], (None,None)) for (n,parser) in self.parsers: if i < n: (images, othertags, cursor_info) = parser.parse_frame(i) break i -= n return (images, othertags, cursor_info) def parse_vnc2swf(self, fname, read_mp3=False, debug=0): parser = VNC2SWF_Parser(self, read_mp3, debug=debug) parser.open(fname) nframes = len(parser.framepos) self.parsers.append( (nframes, parser) ) self.nframes += nframes return self def parse_flv(self, fname, read_mp3=False, debug=0): parser = FLVMovieParser(self, read_mp3, debug=debug) parser.open(fname) #raise NotImplementedError nframes = len(parser.frames) self.parsers.append( (nframes, parser) ) self.nframes += nframes return self def parse_vncrec(self, fname, debug=0): parser = RFBMovieConverter(self, debug=debug) parser.open(fname) nframes = len(parser.frameinfo) self.parsers.append( (nframes, parser) ) self.nframes += nframes return self ## VNC2SWF_Parser ## class VNC2SWF_Parser(SWFParser): """ VNC2SWF_Parser parses a SWF file which is specifically created by vnc2swf. This does not support a generic Flash file. """ def __init__(self, movie, read_mp3=False, debug=0): SWFParser.__init__(self, debug) self.movie = movie self.read_mp3 = read_mp3 self.video1_cid = None return def parse_header(self): SWFParser.parse_header(self) (x,width, y,height) = self.rect print >>stderr, 'Input movie: version=%d, size=%dx%d, framerate=%dfps, frames=%d, duration=%.1fs.' % \ (self.swf_version, width/20, height/20, self.framerate, self.framecount, self.framecount/float(self.framerate)) self.movie.info.set_framerate(self.framerate) self.movie.info.set_defaults(width/20, height/20) return def parse_frame(self, i): self.image1 = {} self.shape1 = None self.images = [] self.othertags = [] self.cursor_image = None self.cursor_pos = None SWFParser.parse_frame(self, i) return (self.images, self.othertags, (self.cursor_image, self.cursor_pos)) def do_tag0(self, tag, length): return def do_unknown_tag(self, tag, length): data = self.read(length) self.othertags.append((tag, data)) return def do_tag1(self, tag, length): # ShowFrame if self.debug: print >>stderr, 'ShowFrame' return def do_tag9(self, tag, length): # SetBackgroundColor bgcolor = self.readrgb() if self.debug: print >>stderr, 'BGColor:', bgcolor return def do_tag20(self, tag, length): # DefineBitsLossless cid = self.readui16() fmt = self.readui8() width = self.readui16() height = self.readui16() length -= 7 tablesize = 0 if fmt == 3: tablesize = self.readui8()+1 length -= 1 if fmt == 5: # RGB or RGBA data = self.read(length) if self.debug: print >>stderr, 'DefineBitsLossless:', cid, fmt, width, height, len(data) self.image1[cid] = (width, height, (IMG_LOSSLESS, data)) return # DefineBitsLossless2 do_tag36 = do_tag20 def do_tag32(self, tag, length): # DefineShape3 sid = self.readui16() rect = self.readrect() (fillstyles, linestyles) = self.read_style(3) shape = self.read_shape(3, fillstyles, linestyles) if fillstyles: cid = fillstyles[0][3] if self.debug: print >>stderr, 'Shape', sid, cid, rect, shape, fillstyles, linestyles self.shape1 = (sid, cid) return def do_tag26(self, tag, length): # PlaceObject2 flags = self.readui8() depth = self.readui16() (sid, ratio, name) = (None, None, None) (scalex,scaley, rot0,rot1, transx,transy) = (None, None, None, None, None, None) if flags & 2: sid = self.readui16() if flags & 4: (scalex,scaley, rot0,rot1, transx,transy) = self.readmatrix() #assert not (flags & 8) if flags & 16: ratio = self.readui16() if flags & 32: name = self.readstring() #assert not (flags & 64) #assert not (flags & 128) if self.debug: print >>stderr, 'Place', flags, depth, sid, (scalex,scaley, rot0,rot1, transx,transy) if depth == CURSOR_DEPTH: # this is a cursor sprite! if sid: (sid0,cid) = self.shape1 if sid0 == sid and cid in self.image1: (width, height, (t, data)) = self.image1[cid] self.cursor_image = (width, height, 0, 0, zlib.decompress(data)) if transx != None: self.cursor_pos = (transx/20, transy/20) elif not sid or sid == self.video1_cid: # ignore video frame pass elif self.shape1 and transx != None: (sid0,cid) = self.shape1 if sid0 == sid and cid in self.image1: data = self.image1[cid] del self.image1[cid] self.images.append(((transx/20, transy/20), data)) self.shape1 = None return def do_tag28(self, tag, length): # RemoveObject2 depth = self.readui16() if self.debug: print >>stderr, 'RemoveObject', depth return def scan_tag60(self, tag, length): # DefineVideoStream if self.video1_cid: print >>stderr, 'DefineVideoStream already appeared.' return cid = self.readui16() frames = self.readui16() width = self.readui16() height = self.readui16() flags = self.readui8() # ignore this. codec = self.readui8() # must be ScreenVideo if codec == 3: self.video1_cid = cid if self.debug: print >>stderr, 'DefineVideoStream', cid, frames, width, height, flags, codec return def do_tag60(self, tag, length): return def do_tag61(self, tag, length): # VideoFrame stream_id = self.readui16() if self.video1_cid != stream_id: return # Video ID does not match framenum = self.readui16() self.setbuff() (frametype, codecid) = self.readbits(4), self.readbits(4) if codecid != 3: return # must be ScreenVideo (blockwidth, imagewidth) = self.readbits(4), self.readbits(12) (blockheight, imageheight) = self.readbits(4), self.readbits(12) blockwidth = (blockwidth+1)*16 blockheight = (blockheight+1)*16 if self.debug: print >>stderr, 'VideoFrame', framenum, frametype, ':', blockwidth, imagewidth, blockheight, imageheight hblocks = (imagewidth+blockwidth-1)/blockwidth vblocks = (imageheight+blockheight-1)/blockheight for y in xrange(0, vblocks): for x in xrange(0, hblocks): length = self.readub16() if length: data = self.read(length) x0 = x*blockwidth y0 = imageheight-(y+1)*blockheight w = upperbound(blockwidth, imagewidth-x0) h = blockheight if y0 < 0: h += y0 y0 = 0 self.images.append( ((x0,y0), (w,h,(IMG_VIDEOPACKET,data))) ) return def scan_tag18(self, tag, length): # SoundStreamHead if not self.read_mp3: return flags1 = self.readui8() flags2 = self.readui8() playrate = (flags1 & 0x0c) >> 2 if not (flags1 & 2): return # playbacksoundsize is given playstereo = flags1 & 1 compression = (flags2 & 0xf0) >> 4 if compression != 2: return # must be mp3 samplerate = (flags2 & 0x0c) >> 2 if samplerate == 0: return samplerate = [0,11025,22050,44100][samplerate] if not (flags2 & 2): return # streamsoundsize is given streamstereo = flags2 & 1 avgsamplecount = self.readui16() latseek = self.readui16() self.movie.info.set_mp3header(streamstereo, samplerate, latseek) if self.debug: print >>stderr, 'SoundStreamHeader', flags1, flags2, avgsamplecount, latseek return def do_tag18(self, tag, length): return def scan_tag19(self, tag, length): # SoundStreamBlock if not self.read_mp3: return nsamples = self.readui16() seeksamples = self.readsi16() self.movie.info.reg_mp3blocks(self.fp, length-4, nsamples, seeksamples) if self.debug: print >>stderr, 'SoundStreamBlock', nsamples, seeksamples return def do_tag19(self, tag, length): return ## FLVMovieParser ## class FLVMovieParser(FLVParser): def __init__(self, movie, read_mp3, debug=0): FLVParser.__init__(self, debug=debug) self.movie = movie self.read_mp3 = read_mp3 self.framerate = 12 return def open(self, fname): FLVParser.open(self, fname) self.movie.info.set_framerate(self.framerate) for (tag, _, _, offset) in self.tags: if tag == 9: self.fp.seek(offset+1) self.setbuff() (_, imagewidth) = self.readbits(4), self.readbits(12) (_, imageheight) = self.readbits(4), self.readbits(12) self.movie.info.set_defaults(imagewidth, imageheight) break self.frames = [] tagids = [] for (tagid,(_,_,t,_)) in enumerate(self.tags): if len(self.frames)*1000 / self.framerate < t: self.frames.append(tagids) tagids = [] tagids.append(tagid) self.frames.append(tagids) return def parse_frame(self, i): self.images = [] self.othertags = [] for tagid in self.frames[i]: self.process_tag(tagid) return (self.images, self.othertags, (None, None)) def process_tag(self, tagid): (tag, _, _, offset) = self.tags[tagid] if tag != 9: return # Video self.fp.seek(offset) self.setbuff() (frametype, codecid) = self.readbits(4), self.readbits(4) if codecid != 3: return # must be ScreenVideo (blockwidth, imagewidth) = self.readbits(4), self.readbits(12) (blockheight, imageheight) = self.readbits(4), self.readbits(12) blockwidth = (blockwidth+1)*16 blockheight = (blockheight+1)*16 if self.debug: print >>stderr, 'VideoFrame', framenum, frametype, ':', blockwidth, imagewidth, blockheight, imageheight hblocks = (imagewidth+blockwidth-1)/blockwidth vblocks = (imageheight+blockheight-1)/blockheight for y in xrange(0, vblocks): for x in xrange(0, hblocks): length = self.readub16() if length: data = self.read(length) x0 = x*blockwidth y0 = imageheight-(y+1)*blockheight w = upperbound(blockwidth, imagewidth-x0) h = blockheight if y0 < 0: h += y0 y0 = 0 self.images.append( ((x0,y0), (w,h,(IMG_VIDEOPACKET,data))) ) return # main if __name__ == '__main__': info = SWFInfo() movie = MovieContainer(info).parse_vnc2swf(sys.argv[1], read_mp3=True, debug=1) print movie.nframes, info pyvnc2swf-0.9.5/pyvnc2swf/mp3.py000066400000000000000000000165011111303746100165000ustar00rootroot00000000000000#!/usr/bin/env python ## ## pyvnc2swf - mp3.py ## ## $Id: mp3.py,v 1.2 2008/07/12 06:06:34 euske Exp $ ## ## Copyright (C) 2005 by Yusuke Shinyama (yusuke at cs . nyu . edu) ## 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. ## import sys from struct import pack, unpack stderr = sys.stderr ## MP3Storage ## class MP3Storage: def __init__(self, debug=0): self.debug = debug self.isstereo = None self.bit_rate = None self.sample_rate = None self.initial_skip = 0 self.frames = [] # self.played_samples = 0 self.playing_frame = 0 self.seeksamples = 0 return def __repr__(self): return '' % \ (self.isstereo, self.bit_rate, self.sample_rate, self.initial_skip, len(self.frames)) def set_stereo(self, isstereo): if self.isstereo == None: self.isstereo = isstereo elif self.isstereo != isstereo: print >>stderr, 'mp3: isstereo does not match!' return def set_bit_rate(self, bit_rate): if self.bit_rate == None: self.bit_rate = bit_rate elif self.bit_rate != bit_rate: print >>stderr, 'mp3: bit_rate does not match! (variable bitrate mp3 cannot be used for SWF)' return def set_sample_rate(self, sample_rate): if self.sample_rate == None: self.sample_rate = sample_rate elif self.sample_rate != sample_rate: print >>stderr, 'mp3: sample_rate does not match! (variable bitrate mp3 cannot be used for SWF)' return def set_initial_skip(self, initial_skip): if initial_skip: self.initial_skip = initial_skip return def add_frame(self, nsamples, frame): self.frames.append((nsamples, frame)) return def needsamples(self, t): return int(self.sample_rate * t) + self.initial_skip def get_frames_until(self, t): # write mp3 frames # # Before: # # MP3 |----|played_samples # SWF |-------|-----|needsamples(t) # prev cur. # # After: # ->| |<- next seeksamples # MP3 |----------|played_samples # SWF |-------|-----|needsamples(t) # prev cur. needsamples = self.needsamples(t) if needsamples < 0: return (0, 0, []) nsamples = 0 frames = [] while self.playing_frame < len(self.frames): (samples,data) = self.frames[self.playing_frame] if needsamples <= self.played_samples+nsamples+samples: break nsamples += samples frames.append(data) self.playing_frame += 1 seeksamples = self.seeksamples self.played_samples += nsamples self.seeksamples = needsamples-self.played_samples # next seeksample return (nsamples, seeksamples, frames) def seek_frame(self, t): needsamples = self.needsamples(t) self.played_samples = 0 for (i,(samples,data)) in enumerate(self.frames): if needsamples <= self.played_samples+samples: break self.played_samples += samples self.playing_frame = i self.seeksamples = needsamples-self.played_samples return ## MP3Reader ## class MP3Reader: """ read MPEG frames. """ def __init__(self, storage): self.storage = storage return def read(self, n): if self.length != None: if self.length <= 0: return '' self.length -= n return self.fp.read(n) BIT_RATE = { (1,1): (0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0), (1,2): (0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0), (1,3): (0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0), (2,1): (0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 144, 176, 192, 224, 256, 0), (2,2): (0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0), (2,3): (0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0), } SAMPLE_RATE = { 3: (44100, 48000, 32000), # V1 2: (22050, 24000, 16000), # V2 0: (11025, 12000, 8000), # V2.5 } def read_mp3file(self, fp, length=None, totalsamples0=None, seeksamples=None, verbose=False): """parameter seeksamples is ignored.""" self.fp = fp self.length = length totalsamples = 0 while 1: x = self.read(4) if len(x) < 4: break if x.startswith('TAG'): # TAG - ignored data = x[3]+self.read(128-4) if verbose: print >>stderr, 'TAG', repr(data) continue elif x.startswith('ID3'): # ID3 - ignored id3version = x[3]+fp.read(1) flags = ord(fp.read(1)) s = [ ord(c) & 0x7f for c in fp.read(4) ] size = (s[0]<<21) | (s[1]<<14) | (s[2]<<7) | s[3] data = fp.read(size) if verbose: print >>stderr, 'ID3', repr(data) continue h = unpack('>L', x)[0] #if (h & 0xfffb0003L) != 0xfffb0000L: continue # All sync bits (b31-21) are set? if (h & 0xffe00000L) != 0xffe00000L: continue # MPEG Audio Version ID (0, 2 or 3) version = (h & 0x00180000L) >> 19 if version == 1: continue # Layer (3: mp3) layer = 4 - ((h & 0x00060000L) >> 17) if layer == 4: continue # Protection protected = not (h & 0x00010000L) # Bitrate b = (h & 0xf000) >> 12 if b == 0 or b == 15: continue # Frequency s = (h & 0x0c00) >> 10 if s == 3: continue if version == 3: # V1 bit_rate = self.BIT_RATE[(1,layer)][b] else: # V2 or V2.5 bit_rate = self.BIT_RATE[(2,layer)][b] self.storage.set_bit_rate(bit_rate) sample_rate = self.SAMPLE_RATE[version][s] self.storage.set_sample_rate(sample_rate) #print (version, layer, bit_rate, sample_rate) nsamples = 1152 if sample_rate <= 24000: nsamples = 576 pad = (h & 0x0200) >> 9 channel = (h & 0xc0) >> 6 self.storage.set_stereo(1-(channel/2)) joint = (h & 0x30) >> 4 copyright = bool(h & 8) original = bool(h & 4) emphasis = h & 3 if version == 3: framesize = 144000 * bit_rate / sample_rate + pad else: framesize = 72000 * bit_rate / sample_rate + pad if protected: # skip 16bit CRC self.read(2) if verbose: print >>stderr, 'Frame: bit_rate=%dk, sample_rate=%d, framesize=%d' % \ (bit_rate, sample_rate, framesize) data = x+self.read(framesize-4) self.storage.add_frame(nsamples, data) totalsamples += nsamples if totalsamples0: assert totalsamples == totalsamples0 return if __name__ == "__main__": s = MP3Storage(True) MP3Reader(s).read_mp3file(file(sys.argv[1]), verbose=1) pyvnc2swf-0.9.5/pyvnc2swf/output.py000066400000000000000000001002141111303746100173340ustar00rootroot00000000000000#!/usr/bin/env python ## ## pyvnc2swf - output.py ## ## $Id: output.py,v 1.8 2008/11/16 02:39:40 euske Exp $ ## ## Copyright (C) 2005 by Yusuke Shinyama (yusuke at cs . nyu . edu) ## 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. ## import sys, zlib from swf import SWFWriter, FLVWriter, CURSOR_DEPTH from image import * stderr = sys.stderr lowerbound = max upperbound = min ## SWFScreen ## class SWFScreen: """ SWFScreen is a framebuffer which is temporarily used for movie construction. """ def __init__(self, x0, y0, w, h, scaling=None): (self.x0, self.y0, self.width, self.height) = (x0, y0, w, h) self.scaling = scaling if scaling: (self.out_width, self.out_height) = (int(w*scaling), int(h*scaling)) else: (self.out_width, self.out_height) = (w, h) self.buf = create_image(w, h) self.out_buf = None return def __repr__(self): return '' % \ (self.width, self.height, self.x0, self.y0, self.out_width, self.out_height) def adjust_cursor_pos(self, (x,y), (dx,dy)): x = x - self.x0 - dx y = y - self.y0 - dy if self.scaling: x *= self.scaling y *= self.scaling return (int(x), int(y)) def prepare_image(self, cursor_image=None, cursor_offset=None, cursor_pos=None): # do proper scaling if self.scaling: self.out_buf = scale_image(self.buf, self.scaling) elif cursor_image and cursor_pos and cursor_offset: self.out_buf = create_image(self.out_width, self.out_height) paste_image(self.out_buf, self.buf, (0, 0)) else: self.out_buf = self.buf if cursor_image and cursor_pos and cursor_offset: cursor_pos = self.adjust_cursor_pos(cursor_pos, cursor_offset) paste_image(self.out_buf, cursor_image, cursor_pos) return self.out_buf def get_image(self, x, y, w, h): #assert 0 <= x and 0 <= y, (x,y) return crop_image(self.out_buf, (x, y, w, h)) def dump_image(self, fname): save_image(self.out_buf, fname) return # returns True if the image is actually painted. def paint_image(self, x0, y0, w, h, (format, data)): x0 -= self.x0 y0 -= self.y0 if not (w and h and 0 < x0+w and x0 < self.width and 0 < y0+h and y0 < self.height): return False if format == IMG_SOLID: # fill color solid_fill(self.buf, (x0, y0, w, h), data) return True if format == IMG_RAW: # raw buffer (RGB or RGBX) if len(data) == (w*h*3): img = create_image_from_string_rgb(w, h, data) elif len(data) == (w*h*4): img = create_image_from_string_rgbx(w, h, data) else: assert 0 elif format == IMG_LOSSLESS: # image defined by DefineBitsLossless (XRGB) data = zlib.decompress(data) assert len(data) == (w*h*4) img = create_image_from_string_xrgb(w, h, data) elif format == IMG_VIDEOPACKET: # image defined by SCREENVIDEOPACKET (BGR) data = zlib.decompress(data) assert len(data) == (w*h*3) img = create_image_from_string_rgb_flipped(w, h, bgr2rgb(data)) else: assert 0, 'illegal image format: %d' % format # sometime the pasted image doesn't fit in the screen, but just let it out. paste_image(self.buf, img, (x0, y0)) return True ## SWFBlockScreen ## class SWFBlockScreen(SWFScreen): """SWFBlockScreen is a blockized SWFScreen.""" def __init__(self, x0, y0, w, h, block_w, block_h, scaling=None): SWFScreen.__init__(self, x0, y0, w, h, scaling=scaling) self.block_w = block_w self.block_h = block_h if scaling: (w,h) = (int(w*scaling), int(h*scaling)) self.hblocks = (w+self.block_w-1)/self.block_w self.vblocks = (h+self.block_h-1)/self.block_h return ## SWFShapeScreen ## class SWFShapeScreen(SWFBlockScreen): """ SWFShapeScreen is a SWFBlockScreen which consists of overlapping objects of images. This is used by SWFShapeStream. """ MAPBLOCKSIZE = 4 class SWFShapeRef: def __init__(self, depth, count): self.depth = depth self.count = count return def __repr__(self): return '(%d,%d)' % (self.depth, self.count) def __init__(self, x0, y0, w, h, scaling=None): SWFBlockScreen.__init__(self, x0, y0, w, h, self.MAPBLOCKSIZE, self.MAPBLOCKSIZE, scaling=scaling) self.map = None self.current_depth = 1 self.last_depth = 1 return def initmap(self): self.map = [ [None]*self.hblocks for i in xrange(self.vblocks) ] return def next_frame(self): self.last_depth = self.current_depth return # x0,y0,w,h: output rectangle (scaled) def place_object(self, added, x0, y0, w, h, replaced): x0 -= self.x0 y0 -= self.y0 if x0+w <= 0 or self.out_width <= x0 or y0+h <= 0 or self.out_height <= y0: return x1 = upperbound((x0+w)/self.block_w+1, self.hblocks) y1 = upperbound((y0+h)/self.block_h+1, self.vblocks) x0 = lowerbound(x0/self.block_w, 0) y0 = lowerbound(y0/self.block_h, 0) depth0 = self.last_depth for y in xrange(y0, y1): line = self.map[y] for x in xrange(x0, x1): obj0 = line[x] if not obj0 or obj0.depth < depth0: depth0 = -1 break if depth0 == -1: break else: return obj1 = SWFShapeScreen.SWFShapeRef(self.current_depth, (x1-x0)*(y1-y0)) self.current_depth += 1 # find completely covered objects (whose ref==0). for line in self.map[y0:y1]: for x in xrange(x0, x1): obj0 = line[x] if obj0: obj0.count -= 1 if obj0.count == 0: replaced[obj0.depth] = 1 line[x] = obj1 added.append((obj1.depth, x0*self.block_w, y0*self.block_h, (x1-x0)*self.block_w, (y1-y0)*self.block_h)) return ## SWFVideoScreen ## class SWFVideoScreen(SWFBlockScreen): """ SWFVideoScreen is a SWFBlockScreen which consists of a grid of blocks. This is used by SWFVideoStream. """ def __init__(self, x0, y0, w, h, block_w, block_h, scaling=None): SWFBlockScreen.__init__(self, x0, y0, w, h, block_w, block_h, scaling=scaling) return def init_blocks(self): self.block_changed = [ [True]*self.hblocks for i in xrange(self.vblocks) ] self.block_image = [ [None]*self.hblocks for i in xrange(self.vblocks) ] return # must return a string! def get_block_change(self, x, y): # okay, this function is going to be called millions of times. # so it must perform ultra-fast. '''get change of block (x,y)''' if not self.block_changed[y][x]: return '' x0 = x*self.block_w y0 = self.out_height-(y+1)*self.block_h # if the block is partial, the player also expects a partial image. w = upperbound(self.block_w, self.out_width-x0) h = self.block_h if y0 < 0: h += y0 y0 = 0 # for some reason y-axis is filpped in VideoPacket. # so we flip it in advance so that it can go back correctly... data = convert_image_to_string_rgb_flipped(self.get_image(x0, y0, w, h)) hval = hash(data) if self.block_image[y][x] == hval: return '' self.block_changed[y][x] = False self.block_image[y][x] = hval return bgr2rgb(data) def paint_image(self, x0, y0, w, h, data): if not SWFScreen.paint_image(self, x0, y0, w, h, data): return False x0 -= self.x0 y0 -= self.y0 #assert w and h and 0 < x0+w and x0 < self.out_width and 0 < y0+h and y0 < self.out_height if self.scaling: (x0,y0,w,h) = (int(x0*self.scaling), int(y0*self.scaling), int(w*self.scaling), int(h*self.scaling)) x1 = upperbound((x0+w-1)/self.block_w+1, self.hblocks) y1 = upperbound((self.out_height-y0)/self.block_h+1, self.vblocks) x0 = lowerbound(x0/self.block_w, 0) y0 = lowerbound((self.out_height-(y0+h-1))/self.block_h, 0) for line in self.block_changed[y0:y1]: for x in xrange(x0, x1): line[x] = True return True ################################################################## ## MovieOutputStream ## class MovieOutputStream: """ MovieOutputStream is an abstract class which produces some external representation of a movie (either to a file or to a display). This is used for generating SWF files or playing movies on the screen. """ def __init__(self, info, debug=0): self.debug = debug self.info = info self.output_frames = 0 self.cursor_image = None self.cursor_offset = None self.cursor_pos = None return def open(self): return def set_keyframe(self): return def paint_frame(self, (images, othertags, cursor_info)): if cursor_info: (cursor_image, cursor_pos) = cursor_info self.cursor_image = cursor_image or self.cursor_image self.cursor_pos = cursor_pos or self.cursor_pos return def next_frame(self): self.output_frames += 1 return def close(self): if self.debug: print >>stderr, 'stream: close' return def write_mp3frames(self, frameid=None): return def preserve_frame(self): return None def recover_frame(self, img): raise NotImplementedError ## SWFOutputStream ## class SWFOutputStream(MovieOutputStream): """ SWFOutputStream is a MovieOutputStream which produces a SWF file. """ swf_version = None def __init__(self, info, debug=0): assert info.filename, 'Filename not specified!' MovieOutputStream.__init__(self, info, debug) self.info.set_swf_version(self.swf_version) self.writer = None self.cursor_depth = None self.cursor_pos0 = None return def open(self): MovieOutputStream.open(self) print >>stderr, 'Creating movie: %r: version=%d, size=%dx%d, framerate=%s, compression=%s' % \ (self.info.filename, self.info.swf_version, self.info.width, self.info.height, self.info.framerate, self.info.compression) self.writer = SWFWriter(self.info.filename, self.swf_version, (0,self.info.width*20, 0,self.info.height*20), self.info.framerate, self.info.compression) # Write BGColor self.writer.start_tag() self.writer.writergb((255,255,255)) self.writer.end_tag(9) # add mp3 header (if any) if self.info.mp3: # write SoundStreamHeader assert self.info.mp3.isstereo != None, 'mp3 isstereo is not set.' assert self.info.mp3.sample_rate != None, 'mp3 sample_rate is not set.' self.writer.start_tag() MP3_RATE = {11025:1, 22050:2, 44100:3} rate = MP3_RATE[self.info.mp3.sample_rate] self.writer.writeui8(rate << 2 | 2 | int(self.info.mp3.isstereo)) self.writer.writeui8(rate << 2 | (2<<4) | 2 | int(self.info.mp3.isstereo)) self.writer.writeui16(int(self.info.mp3.sample_rate / self.info.framerate)) # the first seeksamples, mp3.seek_frame should be preformed in advance. self.writer.writeui16(self.info.mp3.seeksamples) self.writer.end_tag(18) # Make the movie unscalable. if not self.info.scalable: # add actionscript: Stage.scaleMode("noScale") self.writer.start_action() self.writer.do_action(0x96,7) # PushData, size= 1 (type) + len(data) + 1 (0x00) self.writer.writeui8(0x00) # String self.writer.writestring('Stage') self.writer.do_action(0x1c) # GetVariable self.writer.do_action(0x96, 11) # Push self.writer.writeui8(0x00) # String self.writer.writestring('scaleMode') self.writer.do_action(0x96, 9) # Push self.writer.writeui8(0x00) # String self.writer.writestring('noScale') self.writer.do_action(0x4f) # setMember self.writer.end_action() self.othertags = [] return def write_mp3frames(self, frameid=None): # add mp3 frames (if any) if frameid == None: frameid = self.output_frames if self.info.mp3: t = (frameid+1) / self.info.framerate (nsamples, seeksamples, mp3frames) = self.info.mp3.get_frames_until(t) # SoundStreamBlock self.writer.start_tag() self.writer.writeui16(nsamples) self.writer.writeui16(seeksamples) self.writer.write(''.join(mp3frames)) self.writer.end_tag(19) return def define_shape(self, w, h, data, alpha=False): if self.debug: print >>stderr, 'define_shape:', (w,h), len(data) self.writer.start_tag() image_id = self.writer.newid() self.writer.writeui16(image_id) self.writer.writeui8(5) # fmt=5: 24bits color self.writer.writeui16(w) self.writer.writeui16(h) self.writer.write(zlib.compress(data)) if alpha: # DefineBitsLossless2 self.writer.end_tag(36, forcelong=True) # because of flashplayer's bug else: # DefineBitsLossless self.writer.end_tag(20, forcelong=True) # because of flashplayer's bug # DefineShape3 self.writer.start_tag() shape_id = self.writer.newid() self.writer.writeui16(shape_id) self.writer.writerect((20, w*20+20, 20, h*20+20)) self.writer.write_style(3, [(0x43,None,None,None,image_id,(20,20,None,None,0,0))], []) self.writer.write_shape(3, [(0,(20,20)),(1,(w*20,0)),(1,(0,h*20)),(1,(-w*20,0)),(1,(0,-h*20))], fillstyle=1) self.writer.end_tag(32) return shape_id def place_object2(self, shape_id, x, y, depth): if self.debug: print >>stderr, 'place_object2:', shape_id, (x,y), depth # PlaceObject2 self.writer.start_tag() if shape_id: self.writer.writeui8(2|4) self.writer.writeui16(depth) self.writer.writeui16(shape_id) else: self.writer.writeui8(1|4) self.writer.writeui16(depth) self.writer.writematrix((None, None, None, None, x*20, y*20)) self.writer.end_tag(26) return # remove shape object # if you leave objects on the screen, it gets verry slow. def remove_object(self, depth): if self.debug: print >>stderr, 'remove_object:', depth # RemoveObject2 self.writer.start_tag() self.writer.writeui16(depth) self.writer.end_tag(28) return # paint cursor def paint_frame(self, (images, othertags, cursor_info)): MovieOutputStream.paint_frame(self, (images, othertags, cursor_info)) self.othertags.extend(othertags) return def next_frame(self): MovieOutputStream.next_frame(self) # add other unknown tags for (tag, data) in self.othertags: self.writer.start_tag() self.writer.write(data) self.writer.end_tag(tag) # show cursor if self.cursor_pos: shape_id = None if self.cursor_image: (w, h, dx, dy, data) = self.cursor_image self.cursor_image = None self.cursor_offset = (dx, dy) shape_id = self.define_shape(w, h, data, alpha=True) # shape_id is set when the cursor is changed. if shape_id or (self.cursor_offset and self.cursor_pos and self.cursor_pos0 != self.cursor_pos): if shape_id: if self.cursor_depth: self.remove_object(self.cursor_depth) else: self.cursor_depth = CURSOR_DEPTH (x,y) = self.screen.adjust_cursor_pos(self.cursor_pos, self.cursor_offset) self.place_object2(shape_id, x, y, self.cursor_depth) self.cursor_pos0 = self.cursor_pos # ShowFrame self.writer.start_tag() self.writer.end_tag(1) self.othertags = [] return def close(self): MovieOutputStream.close(self) self.writer.start_tag() self.writer.end_tag(0) self.writer.write_file(self.output_frames) return ## SWFShapeStream ## class SWFShapeStream(SWFOutputStream): """ SWFShapeStream produces a SWF file with a set of overlapped shapes with lossless images. """ swf_version = 5 # SWF5 def open(self): SWFOutputStream.open(self) (x,y,w,h) = self.info.clipping self.screen = SWFShapeScreen(x, y, w, h, scaling=self.info.scaling) self.set_keyframe() self.tmp_objs = [] self.replaced = {} return # add shape object def add_object(self, img, depth, x, y): (w,h) = imgsize(img) data = convert_image_to_string_xrgb(img) self.place_object2(self.define_shape(w, h, data), x, y, depth) return def paint_frame(self, (images, othertags, cursor_info)): SWFOutputStream.paint_frame(self, (images, othertags, cursor_info)) for ((x0,y0), (w,h,data)) in images: if self.debug: print >>stderr, 'paint:', (x0,y0), (w,h) if self.screen.paint_image(x0, y0, w, h, data): # do not attempt to create another shape object if # its entire area is already covered by other objects which are # going to be created. if self.info.scaling: (x0,y0,w,h) = (int(x0*self.info.scaling), int(y0*self.info.scaling), int(w*self.info.scaling), int(h*self.info.scaling)) self.screen.place_object(self.tmp_objs, x0, y0, w, h, self.replaced) return def next_frame(self): self.screen.prepare_image() addobjs = [] for (depth,x0,y0,w,h) in self.tmp_objs: if depth in self.replaced: # if the object is completely covered by another object which is # placed within the same frame, do nothing. del self.replaced[depth] else: addobjs.append((depth,x0,y0,w,h)) # Remove completely overriden objects. for depth in self.replaced.iterkeys(): self.remove_object(depth) for (depth,x0,y0,w,h) in addobjs: # Image & Shape & Place tags. self.add_object(self.screen.get_image(x0, y0, w, h), depth, x0, y0) self.screen.next_frame() self.tmp_objs = [] self.replaced = {} SWFOutputStream.next_frame(self) return def set_keyframe(self): self.screen.initmap() return ## SWFVideoStream ## class SWFVideoStream(SWFOutputStream): """ SWFVideoStream produces a SWF file with a video object. """ swf_version = 7 # SWF7 def open(self): SWFOutputStream.open(self) (x,y,w,h) = self.info.clipping self.screen = SWFVideoScreen(x, y, w, h, self.info.blocksize, self.info.blocksize, scaling=self.info.scaling) self.video_object = self.writer.newid() # write DefineVideoStream assert not self.writer.fpstack pos0 = self.writer.fp.tell() self.writer.start_tag() self.writer.writeui16(self.video_object) # video char self.mangle_pos = pos0 + 4 # XXX: # Here we need to put the total number of the frames in this video object. # However, we don't know this for now. So we put a tentative number # and change it later on. self.writer.writeui16(0) # must be changed later. self.writer.writeui16(self.screen.out_width) self.writer.writeui16(self.screen.out_height) self.writer.writeui8(0) # smoothing off self.writer.writeui8(3) # SCREENVIDEO self.writer.end_tag(60) self.place_object2(self.video_object, 0, 0, 1) self.set_keyframe() self.painted = False return def paint_frame(self, (images, othertags, cursor_info)): SWFOutputStream.paint_frame(self, (images, othertags, cursor_info)) for ((x0,y0), (w,h,data)) in images: if self.debug: print >>stderr, 'paint:', (x0,y0), (w,h) if self.screen.paint_image(x0, y0, w, h, data): self.painted = True return def next_frame(self): if self.is_keyframe or self.painted: r = [] changed = self.is_keyframe self.screen.prepare_image() for y in xrange(self.screen.vblocks): for x in xrange(self.screen.hblocks): data = self.screen.get_block_change(x, y) r.append(data) if data: changed = True if changed: # write VideoFrame tag self.writer.start_tag() self.writer.writeui16(self.video_object) # video char self.writer.writeui16(self.output_frames) # SCREENVIDEOPACKET if self.is_keyframe: self.writer.writebits(4, 1) self.is_keyframe = False else: self.writer.writebits(4, 2) self.writer.writebits(4, 3) # screenvideo codec self.writer.writebits(4, self.screen.block_w/16-1) self.writer.writebits(12, self.screen.out_width) self.writer.writebits(4, self.screen.block_h/16-1) self.writer.writebits(12, self.screen.out_height) self.writer.finishbits() for data in r: if data: data = zlib.compress(data) self.writer.writeub16(len(data)) self.writer.write(data) else: self.writer.writeub16(0) self.writer.end_tag(61) # PlaceObject2 # For some reason we need to set the RATIO to the current frame number every time. # This is not documented! self.writer.start_tag() self.writer.writeui8(17) self.writer.writeui16(self.video_object) self.writer.writeui16(self.output_frames) self.writer.end_tag(26) SWFOutputStream.next_frame(self) return def set_keyframe(self): self.screen.init_blocks() self.is_keyframe = True return def close(self): assert not self.writer.fpstack self.writer.fp.seek(self.mangle_pos) # mangle this self.writer.writeui16(self.output_frames) # set the number of frames into DefineVideoStream tag. self.writer.fp.seek(0, 2) # go back SWFOutputStream.close(self) return ## ImageSequenceStream ## class ImageSequenceStream(MovieOutputStream): def __init__(self, info, debug=0): import os.path MovieOutputStream.__init__(self, info, debug=debug) (root, ext) = os.path.splitext(info.filename) self.filename_template = '%s-%%05d%s' % (root, ext) return def open(self): (x,y,w,h) = self.info.clipping self.screen = SWFScreen(x, y, w, h, scaling=self.info.scaling) return def paint_frame(self, (images, othertags, cursor_info)): for ((x0,y0), (w,h,data)) in images: if self.debug: print >>stderr, 'paint:', (x0,y0), (w,h) self.screen.paint_image(x0, y0, w, h, data) if cursor_info: (cursor_image, cursor_pos) = cursor_info if cursor_image: (w, h, dx, dy, data) = cursor_image self.cursor_offset = (dx, dy) self.cursor_image = create_image_from_string_argb(w, h, data) if cursor_pos: self.cursor_pos = cursor_pos return def next_frame(self): fname = self.filename_template % self.output_frames if self.debug: print >>stderr, 'writing:', fname self.screen.prepare_image(self.cursor_image, self.cursor_offset, self.cursor_pos) self.screen.dump_image(fname) MovieOutputStream.next_frame(self) return ## MPEGVideoStream ## Contributed by Vincent Pelletier ## try: import pymedia from pymedia.video import vcodec print >>stderr, 'Using pymedia', pymedia.__version__ except ImportError: vcodec = None class MPEGVideoStream(MovieOutputStream): """ MPEGVideoStream produces a MPEG file. """ # Supported codecs: mpeg2video mpeg1video # Depending on installed codecs: h264 h263 mpeg4 msmpeg4v3 msmpeg4v2 msmpeg4v1 ... def __init__(self, info, codec='mpeg2video', debug=False): assert info.filename, 'Filename not specified!' MovieOutputStream.__init__(self, info, debug) self.mpeg_codec = codec return def open (self): MovieOutputStream.open(self) print >>stderr, 'Creating MPEG: %r: codec=%s, size=%dx%d, framerate=%s' % \ (self.info.filename, self.mpeg_codec, self.info.width, self.info.height, self.info.framerate) (x,y,w,h) = self.info.clipping # Crop to match codec constraints w = w - w % 2 # width constraint : multiple of 2 h = h - h % 2 # height constraint : multiple of 2 self.screen = SWFScreen(x, y, w, h, scaling=self.info.scaling) params = { 'type': 0, 'gop_size': 12, 'frame_rate_base': 125, 'max_b_frames': 0, 'height': self.screen.out_height, 'width': self.screen.out_width, 'frame_rate': 2997, 'deinterlace': 0, 'id': vcodec.getCodecID(self.mpeg_codec), 'format': vcodec.formats.PIX_FMT_YUV420P } params['frame_rate'] = int(params['frame_rate_base'] * self.info.framerate) if self.mpeg_codec == 'mpeg1video': params['bitrate'] = 2700000 else: params['bitrate'] = 9800000 if self.debug: print >>stderr, 'Setting codec to ', params self.encoder = vcodec.Encoder(params) self.out_file = open(self.info.filename, 'wb') return def paint_frame (self, (images, othertags, cursor_info)): MovieOutputStream.paint_frame(self, (images, othertags, cursor_info)) for ((x0, y0), (w, h, data)) in images: if self.debug: print >>stderr, 'paint:', (x0,y0), (w,h) self.screen.paint_image(x0, y0, w, h, data) if cursor_info: (cursor_image, cursor_pos) = cursor_info if cursor_image: (w, h, dx, dy, data) = cursor_image self.cursor_offset = (dx, dy) self.cursor_image = create_image_from_string_argb(w, h, data) if cursor_pos: self.cursor_pos = cursor_pos return def next_frame (self): if self.debug: print >>stderr, 'prepare_image:', (self.screen.out_width, self.screen.out_height) img = self.screen.prepare_image(self.cursor_image, self.cursor_offset, self.cursor_pos) strFrame = convert_image_to_string_rgb(img) bmpFrame = vcodec.VFrame(vcodec.formats.PIX_FMT_RGB24, (self.screen.out_width, self.screen.out_height), (strFrame, None, None)) yuvFrame = bmpFrame.convert(vcodec.formats.PIX_FMT_YUV420P) encFrame = self.encoder.encode(yuvFrame) self.out_file.write(encFrame.data) MovieOutputStream.next_frame(self) return def close (self): MovieOutputStream.close(self) self.out_file.close() return ## FLVVideoStream ## Contributed by Luis Fernando ## class FLVVideoStream(MovieOutputStream): """ FLVVideoStream produces a FLV file with a video object. """ flv_version = 1 # FLV 1 def __init__(self, info, debug=0): assert info.filename, 'Filename not specified!' MovieOutputStream.__init__(self, info, debug) self.info.set_swf_version(None) return def open(self): MovieOutputStream.open(self) self.writer = FLVWriter(self.info.filename, self.flv_version, (0,self.info.width*20, 0,self.info.height*20), self.info.framerate) self.othertags = [] (x,y,w,h) = self.info.clipping self.screen = SWFVideoScreen(x, y, w, h, self.info.blocksize, self.info.blocksize, scaling=self.info.scaling) self.set_keyframe() return def paint_frame(self, (images, othertags, cursor_info)): MovieOutputStream.paint_frame(self, (images, othertags, cursor_info)) self.othertags.extend(othertags) for ((x0,y0), (w,h,data)) in images: if self.debug: print >>stderr, 'paint:', (x0,y0), (w,h) self.screen.paint_image(x0, y0, w, h, data) if cursor_info: (cursor_image, cursor_pos) = cursor_info if cursor_image: (w, h, dx, dy, data) = cursor_image self.cursor_offset = (dx, dy) self.cursor_image = create_image_from_string_argb(w, h, data) if cursor_pos: self.cursor_pos = cursor_pos return def next_frame(self): r = [] self.screen.prepare_image(self.cursor_image, self.cursor_offset, self.cursor_pos) for y in xrange(self.screen.vblocks): for x in xrange(self.screen.hblocks): r.append(self.screen.get_block_change(x, y)) # write FLV tag self.writer.start_tag() # SCREENVIDEOPACKET if self.is_keyframe: self.writer.writebits(4, 1) self.is_keyframe = False else: self.writer.writebits(4, 2) self.writer.writebits(4, 3) # screenvideo codec self.writer.writebits(4, self.screen.block_w/16-1) self.writer.writebits(12, self.screen.out_width) self.writer.writebits(4, self.screen.block_h/16-1) self.writer.writebits(12, self.screen.out_height) self.writer.finishbits() for data in r: if data: data = zlib.compress(data) self.writer.writeub16(len(data)) self.writer.write(data) else: self.writer.writeub16(0) # the first tag: always t == 0 t = (self.output_frames*1000) / self.info.framerate self.writer.end_tag(9, t) MovieOutputStream.next_frame(self) return def set_keyframe(self): self.screen.init_blocks() self.is_keyframe = True return def close(self): assert not self.writer.fpstack MovieOutputStream.close(self) self.writer.write_file(self.output_frames) return ## StreamFactory ## def StreamFactory(type): return { 'flv': FLVVideoStream, 'swf5': SWFShapeStream, 'swf7': SWFVideoStream, 'mpeg': MPEGVideoStream, 'image': ImageSequenceStream, }[type] ## MovieBuilder ## class MovieBuilder: """ MovieBuilder arranges a set of partial images to construct a consistent image of each frame. It provides a proper sequence of images to a MovieOutputStream object. """ # src: MovieContainer, stream: MovieOutputStream def __init__(self, movie, stream, kfinterval=0, mp3seek=False, verbose=True, pinterval=50, debug=0): self.movie = movie self.stream = stream self.debug = debug self.verbose = verbose self.mp3seek = mp3seek self.kfinterval = kfinterval self.pinterval = pinterval return def start(self): self.frameid = -1 self.preserved = {} if self.movie.info.mp3: self.movie.info.mp3.seek_frame(0) self.stream.open() return def step(self): if self.debug: print >>stderr, 'step: %d -> %d' % (self.frameid, self.frameid+1) self.frameid += 1 self.stream.paint_frame(self.movie.get_frame(self.frameid)) if ((self.frameid % self.pinterval) == 0 and self.frameid not in self.preserved): img = self.stream.preserve_frame() if img: self.preserved[self.frameid] = img if self.debug: print >>stderr, 'preserve: %d' % self.frameid return def seek(self, frameid): if self.debug: print >>stderr, 'seek: %d -> %d' % (self.frameid, frameid) if frameid == 0: self.frameid = -1 self.step() if self.movie.info.mp3 and self.mp3seek: self.movie.info.mp3.seek_frame(0) elif frameid == self.frameid+1: self.step() else: if frameid < self.frameid: prev = 0 image = None for (fid,img) in self.preserved.iteritems(): if fid <= frameid and prev <= fid: (prev, image) = (fid, img) if image: self.stream.recover_frame(image) self.stream.set_keyframe() else: prev = self.frameid # replay the sequences. if self.debug: print >>stderr, 'range:', prev, frameid self.frameid = prev for fid in xrange(prev, frameid): self.step() if self.movie.info.mp3 and self.mp3seek: self.movie.info.mp3.seek_frame(frameid / self.movie.info.framerate) if self.kfinterval and (frameid % self.kfinterval) == 0: self.stream.set_keyframe() return def finish(self): return def build(self, frames=None): if not frames: frames = range(self.movie.nframes) self.start() for frameid in frames: self.seek(frameid) if self.debug: print >>stderr, 'next_frame' if self.verbose: stderr.write('.'); stderr.flush() if self.movie.info.mp3: if self.mp3seek: self.stream.write_mp3frames(frameid) else: self.stream.write_mp3frames() self.stream.next_frame() self.finish() if self.verbose: print >>stderr, '%d frames written (duration=%.1fs)' % \ (len(frames), len(frames)/self.movie.info.framerate) return pyvnc2swf-0.9.5/pyvnc2swf/play.py000077500000000000000000000217571111303746100167620ustar00rootroot00000000000000#!/usr/bin/env python ## ## pyvnc2swf - play.py ## ## $Id: play.py,v 1.6 2008/11/16 02:39:40 euske Exp $ ## ## Copyright (C) 2005 by Yusuke Shinyama (yusuke at cs . nyu . edu) ## 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. ## import sys, os.path, subprocess import pygame from image import create_image_from_string_argb from movie import SWFInfo, MovieContainer from output import SWFScreen, MovieOutputStream, MovieBuilder lowerbound = max upperbound = min stderr = sys.stderr ## PygameMoviePlayer ## class PygameMoviePlayer(MovieOutputStream): """ A simple movie player using Pygame. """ font_size = 24 def __init__(self, movie, debug=0): MovieOutputStream.__init__(self, movie.info, debug) self.builder = MovieBuilder(movie, self, debug) self.movie = movie return # MovieOuputStream methods def open(self): MovieOutputStream.open(self) # open window (x,y,w,h) = self.info.clipping self.imagesize = ( int(w*(self.info.scaling or 1)), int(h*(self.info.scaling or 1)) ) self.screen = SWFScreen(x, y, w, h) (self.winwidth, self.winheight) = self.imagesize self.font = pygame.font.SysFont(pygame.font.get_default_font(), self.font_size) (fw1,fh1) = self.font.size('00000 ') (fw2,fh2) = self.font.size('[>] ') self.panel_x0 = 0 self.panel_x1 = fw1 self.panel_x2 = fw1+fw2 self.panel_y0 = self.winheight self.panel_y1 = self.winheight + fh1/2 self.panel_h = fh1 self.panel_w = lowerbound(64, self.winwidth-fw1-fw2-4) self.slide_h = fh1/2 self.slide_w = 8 self.actualwidth = self.panel_w+fw1+fw2+4 pygame.display.set_caption(self.info.filename, self.info.filename) self.window = pygame.display.set_mode((self.actualwidth, self.winheight+self.panel_h)) self.cursor_image = None self.cursor_pos = None self.playing = True self.mp3_out = self.mp3_dec = None #import pymedia #self.mp3_out = subprocess.Popen(['mpg123','-q','-'], stdin=subprocess.PIPE) #self.mp3_dec = pymedia.audio.acodec.Decoder('mp3') #self.mp3_out = pymedia.audio.sound.Output(44100,2,pymedia.audio.sound.AFMT_S16_LE) return def paint_frame(self, (images, othertags, cursor_info)): for ((x0,y0), (w,h,data)) in images: self.screen.paint_image(x0, y0, w, h, data) if cursor_info: (cursor_image, cursor_pos) = cursor_info if cursor_image: (w, h, dx, dy, data) = cursor_image self.cursor_offset = (dx, dy) self.cursor_image = create_image_from_string_argb(w, h, data) if cursor_pos: self.cursor_pos = cursor_pos return def preserve_frame(self): img = pygame.Surface(self.screen.buf.get_size()) img.blit(self.screen.buf, (0,0)) return img def recover_frame(self, img): self.screen.buf.blit(img, (0,0)) return # additional methods def show_status(self): f = self.current_frame n = self.movie.nframes s = '%05d' % f self.window.fill((0,0,0), (0, self.panel_y0, self.actualwidth, self.panel_h)) self.window.blit(self.font.render(s, 0, (255,255,255)), (0, self.panel_y0)) if self.playing: self.window.blit(self.font.render('[>]', 0, (0,255,0)), (self.panel_x1, self.panel_y0)) else: self.window.blit(self.font.render('[||]', 0, (255,0,0)), (self.panel_x1, self.panel_y0)) self.window.fill((255,255,255), (self.panel_x2, self.panel_y1, self.panel_w, 1)) x = self.panel_x2 + self.panel_w*f/n - self.slide_w/2 y = self.panel_y1 - self.slide_h/2 self.window.fill((255,255,255), (x, y, self.slide_w, self.slide_h)) return def update(self): surface = self.screen.buf if self.info.scaling: # rotozoom is still very unstable... it sometime causes segfault :( # in case it doesn't work, use scale instead. # surface = pygame.transform.scale(surface, self.imagesize) # surface.set_alpha() surface = pygame.transform.rotozoom(surface, 0, self.info.scaling) self.window.blit(surface, (0,0)) if self.cursor_image and self.cursor_pos: (x, y) = self.cursor_pos (dx, dy) = self.cursor_offset self.window.blit(self.cursor_image, (x-dx, y-dy)) self.show_status() pygame.display.update() if self.mp3_out and self.info.mp3: t = (self.current_frame+1) / self.info.framerate (nsamples, seeksamples, mp3frames) = self.info.mp3.get_frames_until(t) r = self.mp3_dec.decode(''.join(mp3frames)) self.mp3_out.play(r.data) return def toggle_playing(self): self.playing = not self.playing if self.playing and self.movie.nframes-1 <= self.current_frame: self.current_frame = 0 return def seek(self, goal): self.current_frame = upperbound(lowerbound(goal, 0), self.movie.nframes-1) self.builder.seek(self.current_frame) self.playing = False self.update() return def play(self): drag = False loop = True ticks0 = 0 self.current_frame = 0 self.builder.start() while loop: if self.playing: events = pygame.event.get() else: events = [pygame.event.wait()] for e in events: if e.type in (pygame.MOUSEBUTTONDOWN, pygame.MOUSEMOTION): (x,y) = e.pos if (e.type == pygame.MOUSEBUTTONDOWN and y < self.panel_y0): # the screen clicked self.toggle_playing() elif (self.panel_y0 < y and (e.type == pygame.MOUSEBUTTONDOWN or drag)): # slide bar dragging drag = True (x,y) = e.pos self.seek((x-self.panel_x2)*self.movie.nframes/self.panel_w) elif e.type == pygame.MOUSEBUTTONUP: drag = False elif e.type == pygame.KEYDOWN: if e.key in (13, 32): # space or enter self.toggle_playing() elif e.key in (113, 27): # 'q'uit, esc loop = False elif e.key in (115, 83): # 's'napshot (root, ext) = os.path.splitext(self.info.filename) fname = '%s-%05d.bmp' % (root, self.current_frame) pygame.image.save(self.screen.buf, fname) print >>stderr, 'Save:', fname elif e.key == 275: # right self.current_frame += 1 self.seek(self.current_frame) elif e.key == 276: # left self.current_frame -= 1 self.seek(self.current_frame) else: print >>stderr, 'Unknown key:', e elif e.type == pygame.QUIT: # window close attmpt loop = False if self.playing: self.builder.seek(self.current_frame) if self.movie.nframes-1 <= self.current_frame: # reach the end. self.playing = False else: self.current_frame += 1 ticks1 = pygame.time.get_ticks() d = lowerbound(int(1000.0/self.info.framerate), ticks0-ticks1) ticks0 = ticks1 pygame.time.wait(d) self.update() # loop end self.builder.finish() self.close() return # play def play(moviefiles, info, debug=0): movie = MovieContainer(info) for fname in moviefiles: if fname.endswith('.swf'): # vnc2swf file movie.parse_vnc2swf(fname, True, debug=debug) elif fname.endswith('.flv'): # flv file movie.parse_flv(fname, True, debug=debug) elif fname.endswith('.vnc'): # vncrec file movie.parse_vncrec(fname, debug=debug) else: raise ValueError('unsupported format: %r' % fname) info.filename = os.path.basename(fname) PygameMoviePlayer(movie, debug=debug).play() return # main def main(argv): import getopt, re def usage(): print 'usage: %s [-d] [-r framerate] [-C WxH+X+Y] [-s scaling] file1 file2 ...' % argv[0] return 100 try: (opts, args) = getopt.getopt(argv[1:], 'dr:C:s:') except getopt.GetoptError: return usage() # debug = 0 info = SWFInfo() for (k, v) in opts: if k == '-d': debug += 1 elif k == '-r': info.set_framerate(float(v)) elif k == '-C': m = re.match(r'^(\d+)x(\d+)\+(\d+)\+(\d+)$', v) if not m: print >>stderr, 'Invalid clipping specification:', v return usage() x = map(int, m.groups()) info.clipping = (x[2],x[3], x[0],x[1]) elif k == '-s': info.scaling = float(v) if not args: print >>stderr, 'Specify at least one input movie.' return usage() return play(args, info, debug=debug) if __name__ == "__main__": sys.exit(main(sys.argv)) pyvnc2swf-0.9.5/pyvnc2swf/record_sound.py000077500000000000000000000051001111303746100204630ustar00rootroot00000000000000#!/usr/bin/env python # record_sound.py - Sound recording routine with PyMedia. # Contributed by David Fraser import sys, time import pymedia.audio.sound as sound import pymedia.audio.acodec as acodec import pymedia.muxer as muxer import threading class voiceRecorder: def __init__(self, name ): self.name = name self.finished = False def record(self): self.snd.start() while not self.finished: s= self.snd.getData() if s and len( s ): for fr in self.ac.encode( s ): # We definitely should use mux first, but for # simplicity reasons this way it'll work also block = self.mux.write( self.stream_index, fr ) if block is not None: self.f.write( block ) else: time.sleep( .003 ) # time.sleep( 0 ) self.snd.stop() def run(self): print "recording to", self.name self.f= open( self.name, 'wb' ) # Minimum set of parameters we need to create Encoder cparams= { 'id': acodec.getCodecID( 'mp3' ), 'bitrate': 128000, 'sample_rate': 44100, 'channels': 1 } self.ac= acodec.Encoder( cparams ) self.snd= sound.Input( 44100, 1, sound.AFMT_S16_LE ) self.mux = muxer.Muxer("mp3") self.stream_index = self.mux.addStream( muxer.CODEC_TYPE_AUDIO, self.ac.getParams() ) block = self.mux.start() if block: self.f.write(block) # Loop until Ctrl-C pressed or finished set from outside self.finished = False thread = threading.Thread(target=self.record) thread.start() try: while not self.finished: time.sleep( .003 ) except KeyboardInterrupt: self.finished = True print "finishing recording to", self.name # Stop listening the incoming sound from the microphone or line in thread.join() footer = self.mux.end() if footer is not None: self.f.write(footer) self.f.close() print "finished recording to", self.name print "snipping leading zeroes..." f = open( self.name, "rb" ) buffer = f.read() f.close() buffer = buffer.lstrip(chr(0)) f = open( self.name, "wb" ) f.write( buffer ) f.close() print "snipped leading zeroes" # ---------------------------------------------------------------------------------- # Record stereo sound from the line in or microphone and save it as mp3 file # Specify length and output file name # http://pymedia.org/ if __name__ == "__main__": import time if len( sys.argv )!= 2: print 'Usage: %s ' % sys.argv[ 0 ] else: voiceRecorder( sys.argv[ 1 ] ).run() pyvnc2swf-0.9.5/pyvnc2swf/rfb.py000066400000000000000000000651341111303746100165600ustar00rootroot00000000000000#!/usr/bin/env python ## ## pyvnc2swf - rfb.py ## ## $Id: rfb.py,v 1.5 2008/11/16 02:39:40 euske Exp $ ## ## Copyright (C) 2005 by Yusuke Shinyama (yusuke at cs . nyu . edu) ## 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. ## # For the details of RFB protocol, # see http://www.realvnc.com/docs/rfbproto.pdf import sys, time, socket from struct import pack, unpack from d3des import decrypt_passwd, generate_response from image import IMG_SOLID, IMG_RAW stderr = sys.stderr lowerbound = max def byte2bit(s): return ''.join([ chr((ord(s[i>>3]) >> (7 - i&7)) & 1) for i in xrange(len(s)*8) ]) # Exceptions class RFBError(Exception): pass class RFBAuthError(RFBError): pass class RFBProtocolError(RFBError): pass ## RFBFrameBuffer ## class RFBFrameBuffer: def init_screen(self, width, height, name): #print >>stderr, 'init_screen: %dx%d, name=%r' % (width, height, name) raise NotImplementedError def set_converter(self, convert_pixels, convert_color1): self.convert_pixels = convert_pixels self.convert_color1 = convert_color1 return def process_pixels(self, x, y, width, height, data): #print >>stderr, 'process_pixels: %dx%d at (%d,%d)' % (width,height,x,y) raise NotImplementedError def process_solid(self, x, y, width, height, data): #print >>stderr, 'process_solid: %dx%d at (%d,%d), color=%r' % (width,height,x,y, color) raise NotImplementedError def update_screen(self, t): #print >>stderr, 'update_screen' raise NotImplementedError # data is given as ARGB def change_cursor(self, width, height, data): #print >>stderr, 'change_cursor' raise NotImplementedError def move_cursor(self, x, y): #print >>stderr, 'move_cursor' raise NotImplementedError def close(self): return ## RFBProxy ## class RFBProxy: "Abstract class of RFB clients." def __init__(self, fb=None, pwdfile=None, preferred_encoding=(5,0), debug=0): self.fb = fb self.debug = debug self.pwdfile = pwdfile self.pwdcache = None self.preferred_encoding = preferred_encoding return FASTEST_FORMAT = (32, 8, 1, 1, 255, 255, 255, 24, 16, 8) def preferred_format(self, bitsperpixel, depth, bigendian, truecolour, red_max, green_max, blue_max, red_shift, green_shift, blue_shift): # should return 10-tuple (bitsperpixel, depth, bigendian, truecolour, # red_max, green_max, blue_max, red_shift, green_shift, blue_shift) if self.fb: self.fb.set_converter(lambda data: data, lambda data: unpack('BBBx', data)) return self.FASTEST_FORMAT def send(self, s): "Send data s to the server." raise NotImplementedError def recv(self, n): "Receive n-bytes data from the server." raise NotImplementedError def recv_relay(self, n): "Same as recv() except the received data is also passed to self.relay.recv_framedata." return self.recv(n) def recv_byte_with_timeout(self): return self.recv_relay(1) def write(self, n): return def request_update(self): "Send a request to the server." raise NotImplementedError def finish_update(self): if self.fb: self.fb.update_screen(time.time()) return def init(self): # recv: server protocol version server_version = self.recv(12) # send: client protocol version self.protocol_version = 3 if server_version.startswith('RFB 003.007'): self.protocol_version = 7 elif server_version.startswith('RFB 003.008'): self.protocol_version = 8 self.send('RFB 003.%03d\x0a' % self.protocol_version) if self.debug: print >>stderr, 'protocol_version: 3.%d' % self.protocol_version return self def getpass(self): raise NotImplementedError def auth(self): # vnc challange & response auth def crauth(): if self.pwdcache: p = self.pwdcache elif self.pwdfile: fp = file(self.pwdfile) s = fp.read() fp.close() p = decrypt_passwd(s) elif not self.pwdcache: p = self.getpass() if not p: raise RFBError('Auth cancelled') # from pyvncviewer challange = self.recv(16) if self.debug: print >>stderr, 'challange: %r' % challange response = generate_response(p, challange) if self.debug: print >>stderr, 'response: %r' % response self.send(response) # recv: security result (result,) = unpack('>L', self.recv(4)) return (p, result) (p, server_result) = (None, 0) if self.protocol_version == 3: # protocol 3.3 (or 3.6) # recv: server security (server_security,) = unpack('>L', self.recv(4)) if self.debug: print >>stderr, 'server_security: %r' % server_security # server_security might be 0, 1 or 2. if server_security == 0: (reason_length,) = unpack('>L', self.recv(4)) reason = self.recv(reason_length) raise RFBAuthError('Auth Error: %s' % reason) elif server_security == 1: pass else: (p, server_result) = crauth() else: # protocol 3.7 or 3.8 # recv: multiple server securities (nsecurities,) = unpack('>B', self.recv(1)) server_securities = self.recv(nsecurities) if self.debug: print >>stderr, 'server_securities: %r' % server_securities # must include None or VNCAuth if '\x01' in server_securities: # None self.send('\x01') if self.protocol_version == 8: # Protocol 3.8: must recv security result (server_result,) = unpack('>L', self.recv(4)) else: server_result = 0 elif '\x02' in server_securities: # VNCAuth self.send('\x02') (p, server_result) = crauth() # result returned. if self.debug: print >>stderr, 'server_result: %r' % server_result if server_result != 0: # auth failed. if self.protocol_version != 3: (reason_length,) = unpack('>L', self.recv(4)) reason = self.recv(reason_length) else: reason = server_result raise RFBAuthError('Auth Error: %s' % reason) # negotiation ok. # save the password. self.pwdcache = p # send: always shared. self.send('\x01') return self def start(self): # server info. server_init = self.recv(24) (width, height, pixelformat, namelen) = unpack('>HH16sL', server_init) self.name = self.recv(namelen) (bitsperpixel, depth, bigendian, truecolour, red_max, green_max, blue_max, red_shift, green_shift, blue_shift) = unpack('>BBBBHHHBBBxxx', pixelformat) if self.debug: print >>stderr, 'Server Encoding:' print >>stderr, ' width=%d, height=%d, name=%r' % (width, height, self.name) print >>stderr, ' pixelformat=', (bitsperpixel, depth, bigendian, truecolour) print >>stderr, ' rgbmax=', (red_max, green_max, blue_max) print >>stderr, ' rgbshift=', (red_shift, green_shift, blue_shift) # setformat self.send('\x00\x00\x00\x00') # 32bit, 8bit-depth, big-endian(RGBX), truecolour, 255max (bitsperpixel, depth, bigendian, truecolour, red_max, green_max, blue_max, red_shift, green_shift, blue_shift) = self.preferred_format(bitsperpixel, depth, bigendian, truecolour, red_max, green_max, blue_max, red_shift, green_shift, blue_shift) self.bytesperpixel = bitsperpixel/8 pixelformat = pack('>BBBBHHHBBBxxx', bitsperpixel, depth, bigendian, truecolour, red_max, green_max, blue_max, red_shift, green_shift, blue_shift) self.send(pixelformat) self.write(pack('>HH16sL', width, height, pixelformat, namelen)) self.write(self.name) if self.fb: self.clipping = self.fb.init_screen(width, height, self.name) else: self.clipping = (0,0, width, height) self.send('\x02\x00' + pack('>H', len(self.preferred_encoding))) for e in self.preferred_encoding: self.send(pack('>l', e)) return self def loop1(self): self.request_update() c = self.recv_byte_with_timeout() if c == '': return False elif c == None: # timeout pass elif c == '\x00': (nrects,) = unpack('>xH', self.recv_relay(3)) if self.debug: print >>stderr, 'FrameBufferUpdate: nrects=%d' % nrects for rectindex in xrange(nrects): (x0, y0, width, height, t) = unpack('>HHHHl', self.recv_relay(12)) if self.debug: print >>stderr, ' %d: %d x %d at (%d,%d), type=%d' % (rectindex, width, height, x0, y0, t) # RawEncoding if t == 0: l = width*height*self.bytesperpixel data = self.recv_relay(l) if self.debug: print >>stderr, ' RawEncoding: len=%d, received=%d' % (l, len(data)) if self.fb: self.fb.process_pixels(x0, y0, width, height, data) # CopyRectEncoding elif t == 1: raise RFBProtocolError('unsupported: CopyRectEncoding') # RREEncoding elif t == 2: (nsubrects,) = unpack('>L', self.recv_relay(4)) bgcolor = self.recv_relay(self.bytesperpixel) if self.debug: print >>stderr, ' RREEncoding: subrects=%d, bgcolor=%r' % (nsubrects, bgcolor) if self.fb: self.fb.process_solid(x0, y0, width, height, bgcolor) for i in xrange(nsubrects): fgcolor = self.recv_relay(self.bytesperpixel) (x,y,w,h) = unpack('>HHHH', self.recv_relay(8)) if self.fb: self.fb.process_solid(x0+x, y0+y, w, h, fgcolor) if 2 <= self.debug: print >>stderr, ' RREEncoding: ', (x,y,w,h,fgcolor) # CoRREEncoding elif t == 4: (nsubrects,) = unpack('>L', self.recv_relay(4)) bgcolor = self.recv_relay(self.bytesperpixel) if self.debug: print >>stderr, ' CoRREEncoding: subrects=%d, bgcolor=%r' % (nsubrects, bgcolor) if self.fb: self.fb.process_solid(x0, y0, width, height, bgcolor) for i in xrange(nsubrects): fgcolor = self.recv_relay(self.bytesperpixel) (x,y,w,h) = unpack('>BBBB', self.recv_relay(4)) if self.fb: self.fb.process_solid(x0+x, y0+y, w, h, fgcolor) if 2 <= self.debug: print >>stderr, ' CoRREEncoding: ', (x,y,w,h,fgcolor) # HextileEncoding elif t == 5: if self.debug: print >>stderr, ' HextileEncoding' (fgcolor, bgcolor) = (None, None) for y in xrange(0, height, 16): for x in xrange(0, width, 16): w = min(width-x, 16) h = min(height-y, 16) c = ord(self.recv_relay(1)) assert c < 32 # Raw if c & 1: l = w*h*self.bytesperpixel data = self.recv_relay(l) if self.fb: self.fb.process_pixels(x0+x, y0+y, w, h, data) if 2 <= self.debug: print >>stderr, ' Raw:', l continue if c & 2: bgcolor = self.recv_relay(self.bytesperpixel) if c & 4: fgcolor = self.recv_relay(self.bytesperpixel) if self.fb: self.fb.process_solid(x0+x, y0+y, w, h, bgcolor) # Solid if not c & 8: if 2 <= self.debug: print >>stderr, ' Solid:', repr(bgcolor) continue nsubrects = ord(self.recv_relay(1)) # SubrectsColoured if c & 16: if 2 <= self.debug: print >>stderr, ' SubrectsColoured:', nsubrects, repr(bgcolor) for i in xrange(nsubrects): color = self.recv_relay(self.bytesperpixel) (xy,wh) = unpack('>BB', self.recv_relay(2)) if self.fb: self.fb.process_solid(x0+x+(xy>>4), y0+y+(xy&15), (wh>>4)+1, (wh&15)+1, color) if 3 <= self.debug: print >>stderr, ' ', repr(color), (xy,wh) # NoSubrectsColoured else: if 2 <= self.debug: print >>stderr, ' NoSubrectsColoured:', nsubrects, repr(bgcolor) for i in xrange(nsubrects): (xy,wh) = unpack('>BB', self.recv_relay(2)) if self.fb: self.fb.process_solid(x0+x+(xy>>4), y0+y+(xy&15), (wh>>4)+1, (wh&15)+1, fgcolor) if 3 <= self.debug: print >>stderr, ' ', (xy,wh) # ZRLEEncoding elif t == 16: raise RFBProtocolError('unsupported: ZRLEEncoding') # RichCursor elif t == -239: if width and height: rowbytes = (width + 7) / 8; # Cursor image RGB data = self.recv_relay(width * height * self.bytesperpixel) # Cursor mask -> 1 bit/pixel (1 -> image; 0 -> transparent) mask = self.recv_relay(rowbytes * height) # Set the alpha channel with maskData where bit=1 -> alpha = 255, bit=0 -> alpha=255 if self.debug: print >>stderr, 'RichCursor: %dx%d at %d,%d' % (width,height,x0,y0) if self.fb: data = self.fb.convert_pixels(data) mask = ''.join([ byte2bit(mask[p:p+rowbytes])[:width] for p in xrange(0, height*rowbytes, rowbytes) ]) def conv1(i): if mask[i/4] == '\x01': return '\xff'+data[i]+data[i+1]+data[i+2] else: return '\x00\x00\x00\x00' data = ''.join([ conv1(i) for i in xrange(0, len(data), 4) ]) self.fb.change_cursor(width, height, x0, y0, data) # XCursor elif t == -240: if width and height: rowbytes = (width + 7) / 8; # Foreground RGB fgcolor = self.recv_relay(3) # Background RGB bgcolor = self.recv_relay(3) # Cursor Data -> 1 bit/pixel data = self.recv_relay(rowbytes * height) # Cursor Mask -> 1 bit/pixel mask = self.recv_relay(rowbytes * height) # Create the image from cursordata and maskdata. if self.debug: print >>stderr, 'XCursor: %dx%d at %d,%d' % (width,height,x0,y0) if self.fb: data = ''.join([ byte2bit(data[p:p+rowbytes])[:width] for p in xrange(0, height*rowbytes, rowbytes) ]) mask = ''.join([ byte2bit(mask[p:p+rowbytes])[:width] for p in xrange(0, height*rowbytes, rowbytes) ]) def conv1(i): if mask[i] == '\x01': if data[i] == '\x01': return '\xff'+fgcolor else: return '\xff'+bgcolor else: return '\x00\x00\x00\x00' data = ''.join([ conv1(i) for i in xrange(len(data)) ]) self.fb.change_cursor(width, height, x0, y0, data) # CursorPos -> only change the cursor position elif t == -232: if self.debug: print >>stderr, 'CursorPos: %d,%d' % (x0,y0) if self.fb: self.fb.move_cursor(x0, y0) else: raise RFBProtocolError('Illegal encoding: 0x%02x' % t) self.finish_update() elif c == '\x01': (first, ncolours) = unpack('>xHH', self.recv_relay(11)) if self.debug: print >>stderr, 'SetColourMapEntries: first=%d, ncolours=%d' % (first, ncolours) for i in ncolours: self.recv_relay(6) elif c == '\x02': if self.debug: print >>stderr, 'Bell' elif c == '\x03': (length, ) = unpack('>3xL', self.recv_relay(7)) data = self.recv_relay(length) if self.debug: print >>stderr, 'ServerCutText: %r' % data else: raise RFBProtocolError('Unsupported msg: %d' % ord(c)) return True def loop(self): while self.loop1(): pass self.finish_update() return self def close(self): if self.fb: self.fb.close() return ## RFBNetworkClient ## class RFBNetworkClient(RFBProxy): def __init__(self, host, port, fb=None, pwdfile=None, preferred_encoding=(0,5), debug=0): RFBProxy.__init__(self, fb=fb, pwdfile=pwdfile, preferred_encoding=preferred_encoding, debug=debug) self.host = host self.port = port return def init(self): self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.connect((self.host, self.port)) x = RFBProxy.init(self) print >>stderr, 'Connected: %s:%d, protocol_version=3.%d, preferred_encoding=%s' % \ (self.host, self.port, self.protocol_version, self.preferred_encoding) return x def recv(self, n): # MS-Windows doesn't have MSG_WAITALL, so we emulate it. buf = '' n0 = n while n: x = self.sock.recv(n) if not x: raise RFBProtocolError('Connection closed unexpectedly.') buf += x n -= len(x) return buf def recv_byte_with_timeout(self): self.sock.settimeout(0.05) try: c = self.recv_relay(1) except socket.timeout: c = None self.sock.settimeout(None) return c def send(self, s): return self.sock.send(s) def getpass(self): import getpass return getpass.getpass('Password for %s:%d: ' % (self.host, self.port)) def request_update(self): if self.debug: print >>stderr, 'FrameBufferUpdateRequest' self.send('\x03\x01' + pack('>HHHH', *self.clipping)) return def close(self): RFBProxy.close(self) self.sock.close() return ## RFBNetworkClientForRecording (vncrec equivalent) ## class RFBNetworkClientForRecording(RFBNetworkClient): def __init__(self, host, port, fp, pwdfile=None, preferred_encoding=(5,0), debug=0): RFBNetworkClient.__init__(self, host, port, fb=None, pwdfile=pwdfile, preferred_encoding=preferred_encoding, debug=debug) print >>stderr, 'Creating vncrec: %r: vncLog0.0' % fp self.fp = fp self.write('vncLog0.0') # disguise data (security=none) self.write('RFB 003.003\x0a') self.write('\x00\x00\x00\x01') self.updated = True return def write(self, x): self.fp.write(x) return def request_update(self): if self.updated: self.updated = False t = time.time() self.write(pack('>LL', int(t), int((t-int(t))*1000000))) RFBNetworkClient.request_update(self) return def finish_update(self): self.updated = True return def recv_relay(self, n): data = self.recv(n) self.write(data) return data ## RFBFileParser ## class RFBFileParser(RFBProxy): def __init__(self, fp, fb=None, debug=0): RFBProxy.__init__(self, fb=fb, debug=debug) if self.fb: self.fb.change_format = False self.fp = fp return def preferred_format(self, bitsperpixel, depth, bigendian, truecolour, red_max, green_max, blue_max, red_shift, green_shift, blue_shift): if (bitsperpixel, depth, bigendian, truecolour, red_max, green_max, blue_max, red_shift, green_shift, blue_shift) == self.FASTEST_FORMAT: return RFBProxy.preferred_format(self, bitsperpixel, depth, bigendian, truecolour, red_max, green_max, blue_max, red_shift, green_shift, blue_shift) elif self.fb: if bigendian: endian = '>' else: endian = '<' try: length = {8:'B', 16:'H', 32:'L'}[bitsperpixel] except KeyError: raise 'invalid bitsperpixel: %d' % bitsperpixel unpackstr = endian + length nbytes = bitsperpixel / 8 bits = {1:1, 3:2, 7:3, 15:4, 31:5, 63:6, 127:7, 255:8} try: e = 'lambda p: (((p>>%d)&%d)<<%d, ((p>>%d)&%d)<<%d, ((p>>%d)&%d)<<%d)' % \ (red_shift, red_max, 8-bits[red_max], green_shift, green_max, 8-bits[green_max], blue_shift, blue_max, 8-bits[blue_max]) except KeyError: raise 'invalid {red,green,blue}_max: %d, %d or %d' % (red_max, green_max, blue_max) getrgb = eval(e) unpack_pixels = eval('lambda data: unpack("%s%%d%s" %% (len(data)/%d), data)' % (endian, length, nbytes)) unpack_color1 = eval('lambda data: unpack("%s", data)' % unpackstr) self.fb.set_converter(lambda data: ''.join([ pack('>BBB', *getrgb(p)) for p in unpack_pixels(data) ]), lambda data: getrgb(unpack_color1(data)[0])) return (bitsperpixel, depth, bigendian, truecolour, red_max, green_max, blue_max, red_shift, green_shift, blue_shift) def seek(self, pos): self.fp.seek(pos) return def tell(self): return self.fp.tell() def init(self): self.curtime = 0 version = self.fp.read(9) print >>stderr, 'Reading vncrec file: %s, version=%r...' % (self.fp, version) if version != 'vncLog0.0': raise RFBProtocolError('Unsupported vncrec version: %r' % version) return RFBProxy.init(self) def recv(self, n): x = self.fp.read(n) if len(x) != n: raise EOFError return x def send(self, s): return def auth(self): # XXX for some reason, we force it act as ver 3.3 client. if self.protocol_version == 3 or True: # protocol 3.3 # recv: server security (server_security,) = unpack('>L', self.recv(4)) if self.debug: print >>stderr, 'server_security: %r' % server_security if server_security == 2: # skip challenge+result (dummy) self.recv(20) else: # protocol 3.7 or 3.8 RFBProxy.auth(self) return self def request_update(self): (sec, usec) = unpack('>LL', self.recv(8)) self.curtime = sec+usec/1000000.0 return def finish_update(self): if self.fb: self.fb.update_screen(self.curtime) # use the file time instead return def loop(self, endpos=0): try: while self.loop1(): if endpos and endpos <= self.tell(): break except EOFError: self.finish_update() return self def close(self): RFBProxy.close(self) self.fp.close() return ## RFBConverter ## class RFBConverter(RFBFrameBuffer): def __init__(self, info, debug=0): self.debug = debug self.info = info return def init_screen(self, width, height, name): print >>stderr, 'VNC Screen: size=%dx%d, name=%r' % (width, height, name) self.info.set_defaults(width, height) self.images = [] self.cursor_image = None self.cursor_pos = None self.t0 = 0 return self.info.clipping def process_pixels(self, x, y, width, height, data): self.images.append( ((x, y), (width, height, (IMG_RAW, self.convert_pixels(data)))) ) return def process_solid(self, x, y, width, height, data): self.images.append( ((x, y), (width, height, (IMG_SOLID, self.convert_color1(data)))) ) return def move_cursor(self, x, y): self.cursor_pos = (x, y) return def change_cursor(self, width, height, dx, dy, data): if width and height: self.cursor_image = (width, height, dx, dy, data) return def calc_frames(self, t): if not self.t0: self.t0 = t return int((t - self.t0) * self.info.framerate)+1 ## RFBMovieConverter ## class RFBMovieConverter(RFBConverter): def __init__(self, movie, debug=0): RFBConverter.__init__(self, movie.info, debug) self.movie = movie self.frameinfo = [] return def process_pixels(self, x, y, width, height, data): if self.processing: RFBConverter.process_pixels(self, x, y, width, height, data) return def process_solid(self, x, y, width, height, data): if self.processing: RFBConverter.process_solid(self, x, y, width, height, data) return def update_screen(self, t): if not self.processing: frames = RFBConverter.calc_frames(self, t) done = False while len(self.frameinfo) < frames: if done: self.frameinfo.append((self.beginpos, -1)) else: endpos = self.rfbparser.tell() self.frameinfo.append((self.beginpos, endpos)) if self.debug: print >>stderr, 'scan:', self.beginpos, endpos self.beginpos = endpos done = True return def open(self, fname): self.processing = False fp = file(fname, 'rb') self.rfbparser = RFBFileParser(fp, self, self.debug) self.rfbparser.init().auth().start() self.beginpos = self.rfbparser.tell() self.rfbparser.loop() return def parse_frame(self, i): (pos, endpos) = self.frameinfo[i] if self.debug: print >>stderr, 'seek:', i, pos, endpos self.rfbparser.seek(pos) self.images = [] self.processing = True self.cursor_image = None self.cursor_pos = None self.rfbparser.loop(endpos) return (self.images, [], (self.cursor_image, self.cursor_pos)) ## RFBStreamConverter ## class RFBStreamConverter(RFBConverter): def __init__(self, info, stream, debug=0): RFBConverter.__init__(self, info, debug) self.stream = stream self.stream_opened = False return def init_screen(self, width, height, name): clipping = RFBConverter.init_screen(self, width, height, name) if not self.stream_opened: self.stream.open() self.stream_opened = True self.nframes = 0 return clipping def update_screen(self, t): frames = RFBConverter.calc_frames(self, t) if self.nframes < frames: # First we should create the frames up to now while self.nframes < frames-1: self.stream.next_frame() self.nframes += 1 # And only after that we should paint the frame with the updates self.stream.paint_frame((self.images, [], (self.cursor_image, self.cursor_pos))) self.images = [] self.cursor_image = None self.cursor_pos = None self.stream.next_frame() self.nframes += 1 return pyvnc2swf-0.9.5/pyvnc2swf/swf.py000066400000000000000000000552131111303746100166030ustar00rootroot00000000000000#!/usr/bin/env python ## ## pyvnc2swf - swf.py ## ## $Id: swf.py,v 1.6 2008/11/16 02:39:40 euske Exp $ ## ## Copyright (C) 2005 by Yusuke Shinyama (yusuke at cs . nyu . edu) ## 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. ## import sys, zlib from struct import pack, unpack try: from cStringIO import StringIO except ImportError: from StringIO import StringIO stderr = sys.stderr lowerbound = max upperbound = min CURSOR_DEPTH = 65535 ## DataParser ## class DataParser: """ Low-level byte sequence parser. Inheritable. """ def __init__(self, debug=0): self.fp = None self.buff = 0 self.bpos = 8 self.debug = debug return def open(self, fname): self.fp = file(fname, 'rb') return # fixed bytes read def read(self, n): x = self.fp.read(n) if len(x) != n: raise EOFError return x def readui8(self): return ord(self.read(1)) def readsi8(self): return unpack('H', self.read(2))[0] def readsi16(self): return unpack('L', '\x00'+self.read(3))[0] def readui32(self): return unpack('L', self.read(4))[0] def readrgb(self): return ( self.readui8(), self.readui8(), self.readui8() ) def readrgba(self): return ( self.readui8(), self.readui8(), self.readui8(), self.readui8() ) # fixed bits read def setbuff(self, bpos=8, buff=0): (self.bpos, self.buff) = (bpos, buff) return def readbits(self, bits, signed=False): if bits == 0: return 0 bits0 = bits v = 0 while 1: r = 8-self.bpos # the number of remaining bits we can get from the current buffer. if bits <= r: # |-----8-bits-----| # |-bpos-|-bits-| | # | |----r----| v = (v<>(r-bits)) & ((1<>(bits0-1)): v -= (1<>stderr, 'Scanning source swf file: %s...' % fname pos = self.fp.tell() try: while 1: x = self.readui16() tag = x>>6 if x & 63 == 63: length = self.readui32() else: length = x & 63 pos0 = self.fp.tell() name = 'scan_tag%d' % tag # branch to scan_tag if hasattr(self, name): getattr(self, name)(tag, length) if self.debug: data = self.fp.read(length) print >>stderr, 'tag=%d, data=%r' % (tag, data) else: self.fp.seek(pos0+length) if tag == 1: # next frame self.framepos.append(pos) pos = self.fp.tell() except EOFError: pass return def parse_header(self): (F,W,S,V) = self.read(4) assert W+S == 'WS' self.swf_version = ord(V) if 6 <= self.swf_version: self.encoding = 'utf-8' self.totallen = self.readui32() if self.debug: print >>stderr, 'Header:', (F,W,S,self.swf_version,self.totallen) if F == 'C': # compressed x = zlib.decompress(self.fp.read()) self.fp = StringIO(x) self.rect = self.readrect() self.framerate = self.readui16()/256.0 self.framecount = self.readui16() if self.debug: print >>stderr, 'Header:', self.rect, self.framerate, self.framecount return def parse_frame(self, n): self.fp.seek(self.framepos[n]) if self.debug: print >>stderr, 'seek:', n, self.framepos[n] try: while 1: x = self.readui16() tag = x>>6 if x & 63 == 63: length = self.readui32() else: length = x & 63 pos0 = self.fp.tell() name = 'do_tag%d' % tag # branch to do_tag if hasattr(self, name): getattr(self, name)(tag, length) else: self.do_unknown_tag(tag, length) self.fp.seek(pos0+length) if tag == 1: break except EOFError: pass return def parse_tag1(self): x = self.readui16() tag = x>>6 if x & 63 == 63: length = self.readui32() else: length = x & 63 pos0 = self.fp.tell() name = 'do_tag%d' % tag # branch to do_tag if hasattr(self, name): getattr(self, name)(tag, length) else: self.do_unknown_tag(tag, length) self.fp.seek(pos0+length) return def do_unknown_tag(self, tag, length): if self.debug: print >>stderr, 'unknown tag: %d, length=%d' % (tag, length) return def do_tag0(self, tag, length): # end return def readrect(self): '''(xmin, xmax, ymin, ymax) NOT width and height!''' x = ord(self.read(1)) bits = x>>3 self.setbuff(5, x) return ( self.readbits(bits,1), self.readbits(bits,1), self.readbits(bits,1), self.readbits(bits,1) ) def readmatrix(self): '''returns (scalex, scaley, rot0, rot1, transx, transy)''' self.setbuff() (scalex, scaley) = (None, None) if self.readbits(1): # hasscale n = self.readbits(5) scalex = self.readbits(n,1)/65536.0 scaley = self.readbits(n,1)/65536.0 (rot0, rot1) = (None, None) if self.readbits(1): # hasrotate n = self.readbits(5) rot0 = self.readbits(n,1)/65536.0 rot1 = self.readbits(n,1)/65536.0 (transx, transy) = (None, None) n = self.readbits(5) transx = self.readbits(n,1) transy = self.readbits(n,1) return (scalex, scaley, rot0, rot1, transx, transy) def readgradient(self, version): n = self.readui8() r = [] for i in xrange(n): ratio = self.readui8() if version < 3: color = self.readrgb() else: color = self.readrgba() r.append((ratio, color)) return r def read_style(self, version): ''' fillstyles: list of (color, matrix, gradient, bitmapid, bitmapmatrix) linestyles: list of (width, color) ''' # fillstylearray fillstyles = [] nfills = self.readui8() if 2 <= version and nfills == 0xff: nfills = self.readui16() for i in xrange(nfills): t = self.readui8() (color, matrix, gradient, bitmapid, bitmapmatrix) = (None, None, None, None, None) if t == 0x00: if version == 3: color = self.readrgba() else: color = self.readrgb() elif t in (0x10, 0x12): matrix = self.readmatrix() gradient = self.readgradient(version) elif t in (0x40, 0x41, 0x42, 0x43): bitmapid = self.readui16() bitmapmatrix = self.readmatrix() fillstyles.append((color, matrix, gradient, bitmapid, bitmapmatrix)) # linestylearray linestyles = [] nlines = self.readui8() if 2 <= version and nlines == 0xff: nlines = self.readui16() for i in xrange(nlines): width = self.readui16() if version == 3: color = self.readrgba() else: color = self.readrgb() linestyles.append((width, color)) return (fillstyles, linestyles) def read_shape(self, version, fillstyles=[], linestyles=[]): self.setbuff() nfillbits = self.readbits(4) nlinebits = self.readbits(4) r = [] while 1: typeflag = self.readbits(1) if typeflag: # edge spec. straightflag = self.readbits(1) if straightflag: # StraightEdgeRecord n = self.readbits(4)+2 if self.readbits(1): dx = self.readbits(n,1) dy = self.readbits(n,1) r.append( (1,(dx,dy)) ) elif self.readbits(1): dy = self.readbits(n,1) r.append( (1,(0,dy)) ) else: dx = self.readbits(n,1) r.append( (1,(dx,0)) ) else: # CurveEdgeRecord n = self.readbits(4)+2 cx = self.readbits(n,1) cy = self.readbits(n,1) ax = self.readbits(n,1) ay = self.readbits(n,1) r.append( (2,(cx,cy),(ax,ay)) ) else: # style spec. flags = self.readbits(5) if flags == 0: break if flags & 1: n = self.readbits(5) x0 = self.readbits(n,1) y0 = self.readbits(n,1) r.append( (0,(x0,y0)) ) if flags & 2: fillstyle0 = self.readbits(nfillbits) if flags & 4: fillstyle1 = self.readbits(nfillbits) if flags & 8: linestyle1 = self.readbits(nlinebits) if flags & 16: (fillstyles, linestyles) = self.read_style(version) nfillbits = self.readbits(4) nlinebits = self.readbits(4) return r ## FLVParser ## class FLVParser(DataParser): def __init__(self, debug=0): DataParser.__init__(self, debug) self.tags = [] return def open(self, fname, header_only=False): DataParser.open(self, fname) self.parse_header() if header_only: return print >>stderr, 'Scanning source flv file: %s...' % fname try: offset = self.readub32() # always 0 while 1: tag = self.readui8() length = self.readub24() timestamp = self.readub24() # timestamp in msec. reserved = self.readub32() offset = self.fp.tell() self.tags.append((tag, length, timestamp, offset)) self.fp.seek(offset + length + 4) # skip PreviousTagSize except EOFError: pass return def parse_header(self): (F,L,V,ver) = self.read(4) assert F+L+V == 'FLV' self.flv_version = ord(ver) flags = self.readui8() offset = self.readub32() if self.debug: print >>stderr, 'Header:', (F,L,V,self.flv_version,flags) return def get_tag(self, i): (tag, length, timestamp, offset) = self.tags[i] self.fp.seek(offset) data = self.read(length) return (tag, timestamp, data) def seek_tag(self, t): i0 = 0 i1 = len(self.tags) while i0 < i1: i = (i0+i1)/2 (tag, length, timestamp, offset) = self.tags[i] if timestamp < t: i0 = i else: i1 = i return (tag, length, timestamp, offset) ## SWFWriter ## # return the number of required bits for x. def needbits1(x, signed=False): if x == 0: return 0 if signed: n = 1 if x < 0: x = -x-1 else: n = 0 assert 0 < x while 1: n += 1 x >>= 1 if x == 0: break return n def needbits(args, signed=False): return max([ needbits1(x, signed) for x in args ]) # assert needbits1(0,0) == 0 # assert needbits1(0,1) == 0 # assert needbits1(1,0) == 1 # assert needbits1(1,1) == 2 # assert needbits1(2,0) == 2 # assert needbits1(-2,1) == 2 # assert needbits1(-3,1) == 3 # assert needbits1(127,0) == 7 # assert needbits1(127,1) == 8 # assert needbits1(128,0) == 8 # assert needbits1(-128,1) == 8 # assert needbits1(-129,1) == 9 # assert needbits1(-6380,1) == 14 ## DataWriter ## A common superclass for SWFWriter and FLVWriter ## class DataWriter: def push(self): self.fpstack.append(self.fp) self.fp = StringIO() return def pop(self): assert self.fpstack, 'empty fpstack' self.fp.seek(0) data = self.fp.read() self.fp = self.fpstack.pop() return data # fixed bytes write def write(self, *args): for x in args: self.fp.write(x) return def writeui8(self, *args): for x in args: self.fp.write(chr(x)) return def writesi8(self, *args): for x in args: self.fp.write(pack('H', x)) return def writesi16(self, *args): for x in args: self.fp.write(pack('L', x)[1:4]) return def writeui32(self, *args): for x in args: self.fp.write(pack('L', x)) return def writergb(self, (r,g,b)): self.writeui8(r,g,b) return def writergba(self, (r,g,b,a)): self.writeui8(r,g,b,a) return # fixed bits write def writebits(self, bits, x, signed=False): if signed and x < 0: x += (1<> (bits-r)))) # r < bits self.buff = 0 self.bpos = 0 bits -= r # cut the upper r bits x &= (1< ## class FLVWriter(DataWriter): def __init__(self, outfile, flv_version, rect, framerate): if outfile == '-': self.outfp = sys.stdout else: self.outfp = file(outfile, 'wb') self.rect = rect self.flv_version = flv_version self.framerate = framerate self.fpstack = [] self.bpos = 0 self.buff = 0 self.fp = self.outfp self.fp.write('FLV%c' % self.flv_version) self.writebits(5,0) self.writebits(1,0) # has audio self.writebits(1,0) self.writebits(1,1) # has video self.finishbits() self.writeub32(9) # dataoffset (header size) = 9 self.writeub32(0) # previous tag size = 0 return def end_tag(self, tag, timestamp): data = self.pop() self.writeui8(tag) self.writeub24(len(data)) self.writeub24(int(timestamp)) self.writeui32(0) # reserved self.write(data) self.writeub32(len(data)+11) #size of this tag return def write_file(self, framecount): self.outfp.close() return # test if __name__ == "__main__": parser = FLVParser(True) parser.open(sys.argv[1]) pyvnc2swf-0.9.5/pyvnc2swf/vnc2swf.py000077500000000000000000000473371111303746100174070ustar00rootroot00000000000000#!/usr/bin/env python ## ## pyvnc2swf - vnc2swf.py ## ## $Id: vnc2swf.py,v 1.7 2008/11/16 02:39:40 euske Exp $ ## ## Copyright (C) 2005 by Yusuke Shinyama (yusuke at cs . nyu . edu) ## 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. ## import sys, os, os.path, time, socket, re import Tkinter, tkFileDialog, tkMessageBox, tempfile, shutil from tkSimpleDialog import Dialog from struct import pack, unpack import threading from movie import SWFInfo from output import StreamFactory from rfb import RFBError, RFBNetworkClient, RFBFileParser, RFBNetworkClientForRecording, RFBStreamConverter stderr = sys.stderr ## tkPasswordDialog ## class tkPasswordDialog(Dialog): def __init__(self, title, prompt, master=None): if not master: master = Tkinter._default_root self.prompt = prompt Dialog.__init__(self, master, title) return def destroy(self): self.entry = None Dialog.destroy(self) return def body(self, master): w = Tkinter.Label(master, text=self.prompt, justify=Tkinter.LEFT) w.grid(row=0, padx=5, sticky=Tkinter.W) self.entry = Tkinter.Entry(master, name="entry", show="*") self.entry.grid(row=1, padx=5, sticky=Tkinter.W+Tkinter.E) return self.entry def validate(self): self.result = self.entry.get() return 1 ## tkEntryDialog ## class tkEntryDialog(Dialog): def __init__(self, title, prompt, pattern=None, default=None, master=None): if not master: master = Tkinter._default_root self.prompt = prompt self.default = default self.pattern = re.compile(pattern) Dialog.__init__(self, master, title) return def destroy(self): self.entry = None Dialog.destroy(self) return def body(self, master): w = Tkinter.Label(master, text=self.prompt, justify=Tkinter.LEFT) w.grid(row=0, padx=5, sticky=Tkinter.W) self.entry = Tkinter.Entry(master, name="entry") self.entry.grid(row=1, padx=5, sticky=Tkinter.W+Tkinter.E) if self.default: self.entry.insert(0, self.default) return self.entry def validate(self): self.result = self.entry.get() if self.pattern and not self.pattern.match(self.result): return 0 return 1 ## RFBNetworkClientWithTkMixin ## class RFBNetworkClientWithTkMixin: def tk_init(self, root): self.root = root self.doloop = True return def interrupt(self): self.doloop = False return def loop(self): self.doloop = True while self.doloop: self.root.update() if not self.loop1(): break self.finish_update() return self def getpass(self): return tkPasswordDialog('Login', 'Password for %s:%d' % (self.host, self.port), self.root).result class RFBNetworkClientWithTk(RFBNetworkClientWithTkMixin, RFBNetworkClient): pass class RFBNetworkClientForRecordingWithTk(RFBNetworkClientWithTkMixin, RFBNetworkClientForRecording): pass ## VNC2SWFWithTk ## class VNC2SWFWithTk: FILE_TYPES = [ ('Flash(v5)', 'swf5', 'Macromedia Flash Files', '.swf'), # 0 ('Flash(v7)', 'swf7', 'Macromedia Flash Files', '.swf'), # 1 ('FLV', 'flv', 'Macromedia Flash Video Files', '.flv'), # 3 ('MPEG', 'mpeg', 'MPEG Files', '.mpeg'), # 2 ('VNCRec', 'vnc', 'VNCRec Files', '.vnc'), # 4 ] def __init__(self, tempdir, info, outtype='swf5', host='localhost', port=5900, preferred_encoding=(0,), subprocess=None, pwdfile=None, debug=0): self.tempdir = tempdir self.moviefile = info.filename self.info = info self.debug = debug self.preferred_encoding = preferred_encoding self.subprocess = subprocess self.pwdfile = pwdfile self.outtype = outtype self.host = host self.port = port self.recording = False self.exit_immediately = False self.root = Tkinter.Tk() self.root.title('vnc2swf.py') self.root.wm_protocol('WM_DELETE_WINDOW', self.file_exit) self.toggle_button = Tkinter.Button(master=self.root) self.toggle_button.pack(side=Tkinter.LEFT) self.status_label = Tkinter.Label(master=self.root, justify=Tkinter.LEFT) self.status_label.pack(side=Tkinter.LEFT) self.setup_menubar() self.file_new(True) return def frames(self): return self.stream and self.stream.output_frames # Set up the GUI components. def setup_menubar(self): def option_server(): x = tkEntryDialog('Server', 'Server? (host:port)', pattern='^([^:/]+(:\d+)?|)$', default='%s:%d' % (self.host, self.port), master=self.root).result if not x: return m = re.match(r'^([^:/]*)(:(\d+))?', x) if not m: tkMessageBox.showerror('Invalid address: %s' % x) return (host, port) = (m.group(1) or 'localhost', int(m.group(3) or '5900')) if host != self.host or port != self.port and self.file_new_ask(): (self.host, self.port) = (host, port) self.file_new(True) return def option_framerate(): x = tkEntryDialog('Framerate', 'Framerate? (fps)', pattern='^([1-9][.0-9]+|)$', default=self.info.framerate, master=self.root).result if not x: return framerate = float(x) if framerate != self.info.framerate and self.file_new_ask(): self.info.framerate = framerate self.file_new(True) return def option_clipping(): try: s = self.info.get_clipping() except ValueError: s = '' x = tkEntryDialog('Clipping', 'Clipping? (ex. 640x480+0+0)', pattern='^(\d+x\d+\+\d+\+\d+|)$', default=s, master=self.root).result if not x: return if x != s and self.file_new_ask(): self.info.set_clipping(x) self.file_new(True) return record_type = Tkinter.StringVar(self.root) record_type.set(self.outtype) def option_type(): if record_type.get() != self.outtype and self.file_new_ask(): self.outtype = record_type.get() self.file_new() else: record_type.set(self.outtype) return menubar = Tkinter.Menu(self.root) self.file_menu = Tkinter.Menu(menubar, tearoff=0) self.file_menu.add_command(label="New...", underline=0, command=self.file_new, accelerator='Alt-N') self.file_menu.add_command(label="Save as...", underline=0, command=self.file_saveas, accelerator='Alt-S') self.file_menu.add_separator() self.file_menu.add_command(label="Exit", underline=1, command=self.file_exit) self.option_menu = Tkinter.Menu(menubar, tearoff=0) self.option_menu.add_command(label="Server...", underline=0, command=option_server) self.option_menu.add_command(label="Clipping...", underline=0, command=option_clipping) self.option_menu.add_command(label="Framerate...", underline=0, command=option_framerate) type_submenu = Tkinter.Menu(self.option_menu, tearoff=0) for (k,v,_,_) in self.FILE_TYPES: type_submenu.add_radiobutton(label=k, value=v, variable=record_type, command=option_type) self.option_menu.add_cascade(label="Type", underline=0, menu=type_submenu) menubar.add_cascade(label="File", underline=0, menu=self.file_menu) menubar.add_cascade(label="Option", underline=0, menu=self.option_menu) self.root.config(menu=menubar) self.root.bind('', lambda e: self.file_new()) self.root.bind('', lambda e: self.file_saveas()) return # Change the current status of UI. def set_status(self): def enable_menus(state): self.file_menu.entryconfig(0, state=state) # "File->New..." self.option_menu.entryconfig(0, state=state) # "Option->Server..." self.option_menu.entryconfig(1, state=state) # "Option->Clipping..." self.option_menu.entryconfig(2, state=state) # "Option->Framerate..." self.option_menu.entryconfig(3, state=state) # "Option->Type" return # "File->Save As..." if not self.recording and self.frames(): self.file_menu.entryconfig(1, state='normal') else: self.file_menu.entryconfig(1, state='disabled') s = [] if not self.recording: s.append('Ready (%d frames recorded).' % (self.frames() or 0)) self.toggle_button.config(text='Start', underline=0) self.toggle_button.config(background='#80ff80', activebackground='#00ff00') self.toggle_button.config(command=self.record) self.root.bind('', lambda e: self.record()) self.root.bind('', lambda e: self.record()) enable_menus('normal') else: s.append('Recording.') self.toggle_button.config(text='Stop', underline=0) self.toggle_button.config(background='#ff8080', activebackground='#ff0000') self.toggle_button.config(command=self.client.interrupt) self.root.bind('', lambda e: self.client.interrupt()) self.root.bind('', lambda e: self.client.interrupt()) enable_menus('disabled') if self.host != 'localhost' or self.port != 5900: s.append('Server: %s:%d' % (self.host, self.port)) if self.info.clipping: s.append('Clipping: %s' % self.info.get_clipping()) if self.info.framerate: s.append('Framerate: %s' % self.info.framerate) self.status_label.config(text='\n'.join(s)) return # File->New def file_new_ask(self): if self.frames(): if not tkMessageBox.askokcancel('New file', 'Discard the current session?'): return False return True def file_new(self, force=False): if self.recording or (not force and not self.file_new_ask()): return ext = dict([ (t,ext) for (_,t,desc,ext) in self.FILE_TYPES ])[self.outtype] if self.moviefile: moviefile = self.moviefile else: moviefile = os.path.join(self.tempdir, 'pyvnc2swf-%d%s' % (os.getpid(), ext)) self.info.filename = moviefile self.fp = None if self.outtype == 'vnc': self.fp = file(self.info.filename, 'wb') self.client = RFBNetworkClientForRecordingWithTk( self.host, self.port, self.fp, pwdfile=self.pwdfile, preferred_encoding=self.preferred_encoding) self.stream = None else: self.stream = StreamFactory(self.outtype)(self.info) self.client = RFBNetworkClientWithTk( self.host, self.port, RFBStreamConverter(self.info, self.stream), pwdfile=self.pwdfile, preferred_encoding=self.preferred_encoding) self.set_status() return True # File->SaveAs def file_saveas(self): if self.recording or not self.frames(): return (ext,desc) = dict([ (t,(ext,desc)) for (_,t,desc,ext) in self.FILE_TYPES ])[self.outtype] filename = tkFileDialog.asksaveasfilename( master=self.root, title='Vnc2swf Save As', defaultextension=ext, filetypes=[(desc,'*'+ext), ("All Files", "*")] ) if not filename: return if self.stream: # Finish the movie. self.stream.close() self.stream = None if self.fp: self.fp.close() self.fp = None shutil.move(self.info.filename, filename) self.info.write_html(filename=filename) self.set_status() return # File->Exit def file_exit(self): if self.recording: self.client.interrupt() self.exit_immediately = True else: if self.frames(): if not tkMessageBox.askokcancel('Exit', 'Discard the current session?'): return self.root.destroy() return # Do recording. def record(self): self.client.tk_init(self.root) try: self.client.init().auth().start() except socket.error, e: return self.error('Socket error', e) except RFBError, e: return self.error('RFB protocol error', e) if self.debug: print >>stderr, 'start recording' self.recording = True self.set_status() if self.subprocess: self.subprocess.start() try: self.client.loop() except socket.error, e: return self.error('Socket error', e) except RFBError, e: return self.error('RFB protocol error', e) if self.debug: print >>stderr, 'stop recording' if self.subprocess: self.subprocess.stop() self.client.close() self.recording = False self.set_status() if self.exit_immediately: self.file_exit() return # Displays an error message. def error(self, msg, arg): print >>stderr, arg tkMessageBox.showerror('vnc2swf: %s' % msg, str(arg)) return # Runs Tk mainloop. def run(self): self.root.mainloop() return ## vnc2swf - CLI routine ## def vnc2swf(info, outtype='swf5', host='localhost', port=5900, preferred_encoding=(0,), subprocess=None, pwdfile=None, vncfile=None, debug=0, merge=False): fp = None if outtype == 'vnc': if info.filename == '-': fp = sys.stdout else: fp = file(info.filename, 'wb') client = RFBNetworkClientForRecording(host, port, fp, pwdfile=pwdfile, preferred_encoding=preferred_encoding, debug=debug) else: stream = StreamFactory(outtype)(info, debug=debug) converter = RFBStreamConverter(info, stream, debug=debug) if vncfile: client = RFBFileParser(vncfile, converter, debug=debug) else: client = RFBNetworkClient(host, port, converter, pwdfile=pwdfile, preferred_encoding=preferred_encoding, debug=debug) try: client.init().auth().start() except socket.error, e: print >>stderr, 'Socket error:', e except RFBError, e: print >>stderr, 'RFB error:', e if debug: print >>stderr, 'start recording' if subprocess: subprocess.start() try: client.loop() except KeyboardInterrupt: pass except socket.error, e: print >>stderr, 'Socket error:', e except RFBError, e: print >>stderr, 'RFB error:', e if debug: print >>stderr, 'stop recording' if subprocess: subprocess.stop() client.close() stream.close() info.write_html() if fp: fp.close() # Contributed by David Fraser if merge: tmpfile = os.tempnam(os.path.dirname(info.filename), "vncmerge-") + os.path.basename(info.filename) print >>stderr, "renaming %s to %s for merge" % (info.filename, tmpfile) if os.path.exists(tmpfile): os.remove(tmpfile) os.rename( info.filename, tmpfile ) try: # TODO: sort out getting outputfilename properly audiofilename = subprocess.outputfile import edit args = ["-d", "-o", info.filename, "-a", audiofilename, tmpfile] if not edit.main(args): print >>stderr, "Error doing merge..." finally: origexists, tmpexists = os.path.exists(info.filename), os.path.exists(tmpfile) print >>stderr, "origexists %r, tmpexists %r" % (origexists, tmpexists) if origexists and tmpexists: try: os.remove(tmpfile) except OSError, e: print >>stderr, "Could not remove temporary file: %s" % e elif tmpexists: print >>stderr, "only tmpfile remains after merge, renaming to original (but will not contain sound)" os.rename( tmpfile, info.filename ) return # Thread management class RecordingThread: def __init__(self, outputfile): try: import record_sound except ImportError, e: print >>stderr, "unable to use pymedia?:", e raise self.outputfile = outputfile self.recorder = record_sound.voiceRecorder(self.outputfile) self.thread = threading.Thread(target=self.recorder.run) def start(self): self.thread.start() def stop(self): self.recorder.finished = True self.thread.join() # Subprocess management class Subprocess: def __init__(self, s): try: import subprocess except ImportError: print >>stderr, '-S option requires Python 2.4 or newer.' sys.exit(111) if not hasattr(os, 'kill'): print >>stderr, '-S option works only on Unix or Mac OS X.' sys.exit(111) self.args = s.split(' ') self.popen = None return def start(self): import subprocess self.popen = subprocess.Popen(self.args) return def stop(self): import signal os.kill(self.popen.pid, signal.SIGINT) self.popen.wait() return # main # ./vnc2swf.py -S 'arecord -t wav -c 1 -r 22050 out.wav' -n -o out.swf def main(argv): import getopt def usage(): print ('usage: %s [-d] [-n] [-o filename] [-t {flv|mpeg|swf5|swf7|vnc}]' ' [-e encoding] [-N] [-C clipping] [-r framerate] [-s scaling] [-z] [-m] [-a] [-V]' ' [-S subprocess] [-P pwdfile] [host[:display] [port]]' % argv[0]) return 100 try: (opts, args) = getopt.getopt(argv[1:], 'dno:t:e:NC:r:S:P:s:zmaV') except getopt.GetoptError: return usage() (debug, console, outtype, subprocess, merge, pwdfile, isfile) = (0, False, None, None, False, None, False) (cursor, host, port, preferred_encoding) = (True, 'localhost', 5900, (0,)) info = SWFInfo() for (k, v) in opts: if k == '-d': debug += 1 elif k == '-n': console = True elif k == '-t': outtype = v elif k == '-e': preferred_encoding = tuple([ int(i) for i in v.split(',') ]) elif k == '-N': cursor = False elif k == '-S': subprocess = Subprocess(v) elif k == '-a': subprocess = RecordingThread(v) elif k == '-m': merge = True elif k == '-P': pwdfile = v elif k == '-V': isfile = True elif k == '-o': info.filename = v elif k == '-C': try: info.set_clipping(v) except ValueError: print 'Invalid clipping specification:', v return usage() elif k == "-r": info.framerate = int(v) elif k == "-z": info.set_scalable(True) elif k == '-s': info.scaling = float(v) assert 0 < info.scaling and info.scaling <= 1.0, 'Invalid scaling.' if not outtype: if info.filename: if info.filename.endswith('.vnc'): outtype = 'vnc' elif info.filename.endswith('.swf'): outtype = 'swf5' elif info.filename.endswith('.mpg') or info.filename.endswith('.mpeg'): outtype = 'mpeg' elif info.filename.endswith('.flv'): outtype = 'flv' else: outtype = 'swf5' if outtype not in ('swf5','swf7','vnc','mpeg','flv'): print 'Please specify the output type or file extension.' return usage() if cursor: preferred_encoding += (-232,-239,) if 1 <= len(args): if ':' in args[0]: i = args[0].index(':') host = args[0][:i] or 'localhost' port = int(args[0][i+1:])+5900 else: host = args[0] if 2 <= len(args): port = int(args[1]) if console: if not info.filename: print 'Please specify the output filename.' return usage() vncfile = None if isfile: vncfile = sys.stdin if args: vncfile = file(args[0], 'rb') vnc2swf(info, outtype, host, port, preferred_encoding=preferred_encoding, subprocess=subprocess, pwdfile=pwdfile, vncfile=vncfile, merge=merge, debug=debug) else: tempdir = os.path.join(tempfile.gettempdir(), 'pyvnc2swf') try: os.mkdir(tempdir) except OSError: pass VNC2SWFWithTk(tempdir, info, outtype, host, port, preferred_encoding=preferred_encoding, subprocess=subprocess, pwdfile=pwdfile, debug=debug).run() return if __name__ == "__main__": sys.exit(main(sys.argv))