diff options
author | biocrasher <biocrasher@f606c939-3180-4ac9-a4b8-4b8779d57d0a> | 2006-07-13 00:08:45 +0000 |
---|---|---|
committer | biocrasher <biocrasher@f606c939-3180-4ac9-a4b8-4b8779d57d0a> | 2006-07-13 00:08:45 +0000 |
commit | dad971c4a423e39a6cc6886585d25150e0d12c8f (patch) | |
tree | 2cce86963b3fb5de48768395f9a071bb52e3bba6 | |
parent | f635ae4bbdb7397764fbc4b1b1cfd6cde2d2ecce (diff) |
This commit was generated by cvs2svn to compensate for changes in r3,
which included commits to RCS files with non-trunk default branches.
git-svn-id: https://recordmydesktop.svn.sourceforge.net/svnroot/recordmydesktop/trunk@4 f606c939-3180-4ac9-a4b8-4b8779d57d0a
33 files changed, 3522 insertions, 0 deletions
diff --git a/recordmydesktop/AUTHORS b/recordmydesktop/AUTHORS new file mode 100644 index 0000000..bd3dbbc --- /dev/null +++ b/recordmydesktop/AUTHORS @@ -0,0 +1,2 @@ +NAME EMAIL +John Varouhakis biocrasher@gmail.com diff --git a/recordmydesktop/COPYING b/recordmydesktop/COPYING new file mode 100644 index 0000000..d60c31a --- /dev/null +++ b/recordmydesktop/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/recordmydesktop/ChangeLog b/recordmydesktop/ChangeLog new file mode 100644 index 0000000..b28b04f --- /dev/null +++ b/recordmydesktop/ChangeLog @@ -0,0 +1,3 @@ + + + diff --git a/recordmydesktop/INSTALL b/recordmydesktop/INSTALL new file mode 100644 index 0000000..fe3d120 --- /dev/null +++ b/recordmydesktop/INSTALL @@ -0,0 +1,33 @@ +To compile the program you have to go through the regular drill: + +~$ gzip -d recordmydesktop-x.y.z.tar.gz +~$ tar -x recordmydesktop-x.y.z.tar +~$ cd recordmydesktop-x.y.z +~$ ./configure +~$ make +~$ sudo make install + + +You will need the development headers for the following: +alsa (libasound) +X +libXext +libXdamage +libogg +libvorbis +libtheora + +As well the regular headers, plus the ones for pthreads +(both of which you should have if you have compiled anything else before). + +Of the above, the most likely to be missing are probably those of libXdamage and libtheora +but any recent linux distribution should offer an easy way to get them. + + + + +Compiling on *BSD or other *nix flavors is not going to work(because of the alsa dependency) +but it shouldn't be too much of a hassle to change that(at least by disabling sound capture). +Future versions might include support for OSS-based sound capture. + + diff --git a/recordmydesktop/Makefile.am b/recordmydesktop/Makefile.am new file mode 100644 index 0000000..86af0cd --- /dev/null +++ b/recordmydesktop/Makefile.am @@ -0,0 +1,4 @@ +SUBDIRS = src doc + +EXTRA_DIST=include + diff --git a/recordmydesktop/NEWS b/recordmydesktop/NEWS new file mode 100644 index 0000000..b28b04f --- /dev/null +++ b/recordmydesktop/NEWS @@ -0,0 +1,3 @@ + + + diff --git a/recordmydesktop/README b/recordmydesktop/README new file mode 100644 index 0000000..625990c --- /dev/null +++ b/recordmydesktop/README @@ -0,0 +1,3 @@ +To see examples of usage and explanation of the options check the manpage in the doc directory. + + diff --git a/recordmydesktop/TODO b/recordmydesktop/TODO new file mode 100644 index 0000000..139597f --- /dev/null +++ b/recordmydesktop/TODO @@ -0,0 +1,2 @@ + + diff --git a/recordmydesktop/autogen.sh b/recordmydesktop/autogen.sh new file mode 100644 index 0000000..06af23f --- /dev/null +++ b/recordmydesktop/autogen.sh @@ -0,0 +1,7 @@ +#!/bin/sh + + +aclocal +autoheader +automake --copy --add-missing +autoconf
\ No newline at end of file diff --git a/recordmydesktop/configure.ac b/recordmydesktop/configure.ac new file mode 100644 index 0000000..2b246e9 --- /dev/null +++ b/recordmydesktop/configure.ac @@ -0,0 +1,58 @@ + # -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ(2.59) +AC_INIT(src/recordmydesktop.c) +AM_INIT_AUTOMAKE(recordmydesktop,0.2.0,) + +AC_CONFIG_SRCDIR([src/recordmydesktop.c]) +AM_CONFIG_HEADER(config.h) + + + +# Checks for programs. + +AC_PROG_CC + +# Checks for header files. + +AC_HEADER_DIRENT +AC_HEADER_STDC + +AC_PATH_X +AC_CHECK_HEADER([png.h]) +AC_CHECK_HEADER([alsa/asoundlib.h]) +AC_CHECK_HEADERS([sys/time.h unistd.h vorbis/vorbisfile.h ]) + +LDFLAGS="$LD_FLAGS -L$x_libraries " + +CFLAGS="${CFLAGS} -O3 -Wall -D_THREAD_SAFE -pthread" + +# Checks for libraries. + +AC_CHECK_LIB([m],[isnan],,) +AC_CHECK_LIB([z],[deflate],,) +AC_CHECK_LIB([X11],[XOpenDisplay],,AC_MSG_ERROR([Can't find libX11])) +AC_CHECK_LIB([Xext],[XShmQueryVersion],,AC_MSG_ERROR([Can't find libXext])) +AC_CHECK_LIB([Xdamage], [XDamageQueryExtension],,AC_MSG_ERROR([Can't find libXdamage])) +AC_CHECK_LIB([vorbis],[vorbis_info_clear],,AC_MSG_ERROR([Can't find libvorbis])) +AC_CHECK_LIB([vorbisfile],[ov_open],,AC_MSG_ERROR([Can't find libvorbisfile]),-lvorbis) +AC_CHECK_LIB([vorbisenc],[vorbis_encode_init],,AC_MSG_ERROR([Can't find libvorbisenc]),-lvorbis) +AC_CHECK_LIB([ogg],[ogg_stream_init],,AC_MSG_ERROR([Can't find libogg])) +AC_CHECK_LIB([theora],[theora_encode_YUVin],,AC_MSG_ERROR([Can't find libtheora])) +AC_CHECK_LIB([pthread],[pthread_mutex_lock],,AC_MSG_ERROR([Can't find libpthread])) + +AC_CHECK_LIB([asound],[snd_pcm_drain],,AC_MSG_ERROR([Can't find libasound])) + + +# Checks for typedefs, structures, and compiler characteristics. + +# Checks for library functions. +AC_FUNC_CLOSEDIR_VOID +AC_FUNC_MALLOC + +AC_CONFIG_FILES([Makefile + src/Makefile + doc/Makefile ]) +AC_OUTPUT + diff --git a/recordmydesktop/doc/Makefile.am b/recordmydesktop/doc/Makefile.am new file mode 100644 index 0000000..9780b6c --- /dev/null +++ b/recordmydesktop/doc/Makefile.am @@ -0,0 +1,2 @@ +man_MANS = recordmydesktop.1 +EXTRA_DIST = $(man_MANS) diff --git a/recordmydesktop/doc/recordmydesktop.1 b/recordmydesktop/doc/recordmydesktop.1 new file mode 100644 index 0000000..809077e --- /dev/null +++ b/recordmydesktop/doc/recordmydesktop.1 @@ -0,0 +1,338 @@ +.TH "RECORDMYDESKTOP" 1 "13/7/2006" "Linux" + + +.SH NAME +recordMyDesktop \- record desktop sessions to an Ogg\-Theora\-Vorbis file. + + +.SH SYNOPSIS + +.B recordmydesktop +[ +.B +Options +]^ +.B +filename +.br +.br +.SH DESCRIPTION +.PP + recordMyDesktop produces a file(default out.ogg) that contains a video and audio recording +.br +of a linux desktop session. The default behavior of recording is to mark areas that have changed(through libxdamage) +.br +and update the frame. This behavior can be changed (option +.B +\-\-full\-shots +) to produce a more accurate result +.br +or capture windows that do not generate events on change(windows with 3d context or video display) +.br +but this will notably increase the workload. In this case, enabling the +.B +\-\-with\-shared +option is recommended +.br +(by default this option is disabled since copying small areas of display is faster +.br +than uploading the whole image from video memory). +.br +.br +recordMyDesktop doesn't have a commandline interface. +.br +After startup, it can be controled only through the following signals: +.br +.br +.B +SIGUSR1 +causes the program to pause if it's currently recording, and vice-versa. +.br +.B +SIGTERM +causes normal termination of the recording. +.br +.B +SIGINT +also causes normal termination. +.br +.B +SIGABRT +terminates the program and removes the specified output file. +.br +.br +.br +A typical scenario of recording can be a command as simple as: +.br +.B +~$ recordmydesktop +.br +which will produce a fullscreen recording named out.ogg +.br +while a command like: +.br +.B +~$ recordmydesktop foo.ogg +.br +will write output to foo.ogg +.br +To specify a region for recording you can type this: +.br +.B +~$ recordmydesktop -x X_pos -y Y_pos -width WIDTH -height HEIGHT -o foo.ogg +.br +where X_pos and Y_pos specify the offset in pixels from the upper left +.br +corner of your screen and WIDTH and HEIGHT the size of the window to be recorded(again in pixels). +.br +If the area extends beyond your current resolution, you will be notified appropriately and nothing will happen. +.br +Notice also, that if any option is entered you have to specify the output file with the \-o switch. +.br +.br +.B +To normally end a recording you can press ctl-c. +.br +(which will send a +.B +SIGINT +to the program). +.br +.br +For further manipulation of the end result look at the +.B +OPTIONS +and +.B +NOTES +sections. +.br +.br +.br +.SH EXIT STATUS +0 is success +.br +Non-zero means an error occurred, which is printed in stderr. +.br +.SH OPTIONS +.PP +.B +Generic Options: +.br +.TP +.B + \-h or \-\-help + Print help summary and exit. +.br +.TP +.B + \-\-version + Print program version and exit. +.br +.PP +.br +.B +Image Options: +.br +.TP +.B + \-windowid id_of_window + id of window to be recorded. +.br +.TP +.B + \-display DISPLAY + Display to connect to. +.br +.TP +.B + \-x X + Offset in x direction. +.br +.TP +.B + \-y Y + Offset in y direction. +.br +.TP +.B + \-width N + Width of recorded window. +.br +.TP +.B + \-height N + Height of recorded window. +.br +.TP +.B +.br +.br +.TP +.B + \-dummy\-cursor color + Color of the dummy cursor [black|white](default black) +.br +.TP +.B + \-\-no\-dummy\-cursor + Disable drawing of a dummy cursor. +.br +.TP +.B + \-\-with\-shared + Enable usage of MIT\-shared memory extension. +.br +.TP +.B + \-\-full\-shots + Take full screenshot at every frame(Not recomended!). +.br +.TP +.B + \-fps N(number>0.0) + A positive number denoting desired framerate. +.br +.br +.PP +.B +Sound Options: +.br +.TP +.B + \-channels N(number>0) + A positive number denoting desired sound channels in recording. +.br +.TP +.B + \-freq N(number>0) + A positive number denoting desired sound frequency. +.br +.TP +.B + \-device SOUND_DEVICE + Sound device(default hw0:0). +.br +.TP +.B + \-\-nosound + Do not record sound. +.br +.PP +.br +.B +Encoding Options: +.br +.TP +.B + \-v_quality n + A number from 0 to 63 for desired encoded video quality(default 63). +.br +.TP +.B + \-v_bitrate n + A number from 45000 to 2000000 for desired encoded video bitrate(default 45000). +.br +.TP +.B + \-s_quality n + Desired audio quality(\-1 to 10). +.br +.PP +.br +.B +Misc Options: +.br +.TP +.B + \-delay n[H|h|M|m] + Number of secs(default),minutes or hours before capture starts(number can be float). +.br +.TP +.B + \-\-scshot + Take a bitmap screenshot(default rmdout.bmp) and exit. +.br +.TP +.B + \-scale\-shot N + Factor by which screenshot is scaled down(1<=number<=64,power of 2). +.br +.TP +.B + \-o filename + Name of recorded video(default out.ogg). + + + +.PP +.br +If no other option is specified, filename can be given without the \-o switch. +.br +.br +.SH USAGE +.TP +.B recordmydesktop +.br +[\-h| \-\-help| \-\-version| \-delay n[H|h|M|m]| \-windowid id_of_window| +.br +\-display DISPLAY| \-x X| \-y Y|\-width N| \-height N| \-fps N(number>0)| +.br +\-v_quality n| \-s_quality n| \-v_bitrate n| \-dummy\-cursor color| \-\-no\-dummy\-cursor| +.br +\-freq N(number>0)| \-channels N(number>0)| \-device SOUND_DEVICE| \-\-nosound| +.br +\-\-with\-shared| \-\-full\-shots| \-\-scshot| \-scale\-shot N| \-o filename]^filename +.br +.br +.br +.SH ENVIRONMENT +.TP +.B +DISPLAY +.br +Display environment variable, specifying X server to connect to. +.br +.SH NOTES +.br + Recording a window using the \-windowid option, doesn't track the window itself, but the region that it covers. +.br +Also when using that option the \-x,\-y,\-width and \-height options are relative to the specified window area. +.br +An easy way to find out the id of a window, is by using the +.B +xwininfo +program. +.br +Running a command like : +.br +.B +xwininfo |grep "Window id:"|sed \-e "s/xwininfo\\:\\ Window id:\\ // ;s/\\ \.*//" +.br +will give you only the id of the window(which should look like this: 0x4800005) +.br +More conviniently you can put all that in the command that launches recordMyDesktop like this: +.br +.B +~$recordmydesktop -windowid $(xwininfo |grep "Window id:"|sed \-e "s/xwininfo\\:\\ Window id:\\ // ;s/\\ \.*//" ) +.br +.br + Also, the lower quality you select on a video recording ( +.B +-v_quality +option), the highest CPU-power that you will need. +.br +So it's always better to start with default values and manipulate the end\-result with another program. +.br +.br +.SH BUGS +Too resource intensive,outcome quality is not what it should be. +.br +(font colouring is somewhat fuzzy and occasionally borderlines remain unupdated when a window closes) +.br +.SH AUTHORS +John Varouhakis(biocrasher@gmail.com) +.br +.SH SEE ALSO +.BR xwininfo(1) +.br
\ No newline at end of file diff --git a/recordmydesktop/include/recordmydesktop.h b/recordmydesktop/include/recordmydesktop.h new file mode 100644 index 0000000..1740762 --- /dev/null +++ b/recordmydesktop/include/recordmydesktop.h @@ -0,0 +1,367 @@ +/********************************************************************************* +* recordMyDesktop * +********************************************************************************** +* * +* Copyright (C) 2006 John Varouhakis * +* * +* * +* 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 * +* * +* * +* * +* For further information contact me at biocrasher@gmail.com * +**********************************************************************************/ + + +#ifndef RECORDMYDESKTOP_H +#define RECORDMYDESKTOP_H 1 + +#ifdef HAVE_CONFIG_H + #include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <math.h> +#include <unistd.h> +#include <fcntl.h> +#include <time.h> +#include <signal.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ipc.h> +#include <sys/shm.h> +#include <pthread.h> +#include <X11/Xlib.h> +#include <X11/Xlibint.h> +#include <X11/extensions/Xdamage.h> +#include <X11/extensions/XShm.h> +#include <theora/theora.h> +#include <vorbis/codec.h> +#include <vorbis/vorbisenc.h> +#include <ogg/ogg.h> +#include <alsa/asoundlib.h> + + +enum {UNSPECIFIED,OGG_THEORA_VORBIS}; + + +/**Structs*/ + +typedef struct _DisplaySpecs{ //this struct holds some basic information + int screen; //about the display,needed mostly for + uint width; //validity checks at startup + uint height; + Window root; + Visual *visual; + GC gc; + int depth; + unsigned long bpixel; + unsigned long wpixel; +}DisplaySpecs; + +typedef struct _WGeometry{ //basic geometry of a window or area + int x; + int y; + int width; + int height; +}WGeometry; + +typedef struct _RectArea{ //an area that has been damaged gets stored + WGeometry geom; //in a list comprised of structs of this type + struct _RectArea *prev,*next; +}RectArea; + +typedef struct _BRWindow{ //a window to be recorded specs + WGeometry geom; //window attributes + WGeometry rgeom; //part of window that is recorded + int nbytes; //size of zpixmap when screenshoting + Window windowid; //id +}BRWindow; + + +typedef struct _ProgArgs{ + int delay; //start up delay + Window windowid; //window to record(default root) + char *display; //display to connect(default :0) + int x,y; //x,y offset(default 0,0) + int width,height; //defaults to window width and height + int quietmode; //no messages to stderr,stdout + char *filename; //output file(default out.[ogg|*]) + int encoding; //encoding(default OGG_THEORA_VORBIS) + int cursor_color; //black or white=>1 or 0 + int have_dummy_cursor;//disable/enable drawing the dummy cursor + float fps; //desired framerate(default 15) + unsigned int frequency; //desired frequency (default 22050) + unsigned int channels; //no of channels(default 2) + char *device; //default sound device(default according to alsa or oss) + int nosound; //do not record sound(default 0) + int noshared; //do not use shared memory extension(default disabled) + int full_shots; //do not poll damage, take full screenshots + int scshot; //take screenshot and exit(default 0) + int scale_shot; //screenshot subscale factor(default 1) + int v_bitrate,v_quality,s_quality;//video bitrate,video-sound quality +}ProgArgs; + + +//this struct will hold anything related to encoding AND +//writting out to file. +//**TODO add vorbis specifics*/ +typedef struct _EncData{ + ogg_stream_state m_ogg_ts;//theora + ogg_stream_state m_ogg_vs;//vorbis + ogg_page m_ogg_pg; + ogg_packet m_ogg_pckt1; + ogg_packet m_ogg_pckt2; + + theora_state m_th_st; + theora_info m_th_inf; + theora_comment m_th_cmmnt; + yuv_buffer yuv; + + vorbis_info m_vo_inf; + vorbis_comment m_vo_cmmnt; + vorbis_dsp_state m_vo_dsp; + vorbis_block m_vo_block; + + + FILE *fp; +}EncData; + +typedef struct _SndBuffer{ + signed char *data; + struct _SndBuffer *next; +}SndBuffer; + +//this structure holds any data related to the program +//It's usage is mostly to be given as an argument to the +//threads,so they will have access to the program data, avoiding +//at the same time usage of any globals. +typedef struct _ProgData{ + ProgArgs args;//the program arguments + DisplaySpecs specs;//Display specific information + BRWindow brwin;//recording window + Display *dpy;//curtrent display + XImage *image;//the image that holds the current full screenshot + unsigned char *dummy_pointer;//a dummy pointer to be drawn in every frame + //data is casted to unsigned for later use in YUV buffer + int dummy_p_size;//initially 16x16,always square + unsigned char npxl;//this is the no pixel convention when drawing the dummy pointer + char *datamain,//the data of the image above + *datatemp;//buffer for the temporary image,which will be + //preallocated in case shared memory is not used. + RectArea *rect_root[2];//the interchanging list roots for storing the changed regions + int list_selector,//selector for the above + damage_event,//damage event base code + damage_error,//damage error base code + running; + SndBuffer *sound_buffer; + EncData *enc_data; + int hard_pause;//if sound device doesn't support pause + //we have to close and reopen + unsigned int periodtime, + frametime; + pthread_mutex_t list_mutex[2],//mutexes for concurrency protection of the lists + sound_buffer_mutex, + yuv_mutex; + pthread_cond_t time_cond,//this gets a broadcast by the handler whenever it's time to get a screenshot + pause_cond,//this is blocks execution, when program is paused + sound_buffer_ready,//sound encoding finished + sound_data_read,//a buffer is ready for proccessing + image_buffer_ready;//image encoding finished + snd_pcm_t *sound_handle; + snd_pcm_uframes_t periodsize; +}ProgData; + +/**Globals*/ +//I've read somewhere that I'll go to hell for using globals... + +int Paused,*Running,Aborted; +pthread_cond_t *time_cond,*pause_cond; +int avd; + +/**Macros*/ + +#define CLIP_EVENT_AREA(e,brwin,wgeom){\ + if(((e)->area.x<=(brwin)->rgeom.x)&&((e)->area.y<=(brwin)->rgeom.y)&&\ + ((e)->area.width>=(brwin)->rgeom.width)&&((e)->area.height<(brwin)->rgeom.height)){\ + (wgeom)->x=(brwin)->rgeom.x;\ + (wgeom)->y=(brwin)->rgeom.y;\ + (wgeom)->width=(brwin)->rgeom.width;\ + (wgeom)->height=(brwin)->rgeom.height;\ + }\ + else{\ + (wgeom)->x=((((e)->area.x+(e)->area.width>=(brwin)->rgeom.x)&&\ + ((e)->area.x<=(brwin)->rgeom.x+(brwin)->rgeom.width))?\ + (((e)->area.x<=(brwin)->rgeom.x)?(brwin)->rgeom.x:(e)->area.x):-1);\ + \ + (wgeom)->y=((((e)->area.y+(e)->area.height>=(brwin)->rgeom.y)&&\ + ((e)->area.y<=(brwin)->rgeom.y+(brwin)->rgeom.height))?\ + (((e)->area.y<=(brwin)->rgeom.y)?(brwin)->rgeom.y:(e)->area.y):-1);\ + \ + (wgeom)->width=((e)->area.x<=(brwin)->rgeom.x)?\ + (e)->area.width-((brwin)->rgeom.x-(e)->area.x):\ + ((e)->area.x<=(brwin)->rgeom.x+(brwin)->rgeom.width)?\ + (((brwin)->rgeom.width-(e)->area.x+(brwin)->rgeom.x<(e)->area.width)?\ + (brwin)->rgeom.width-(e)->area.x+(brwin)->rgeom.x:e->area.width):-1;\ + \ + (wgeom)->height=((e)->area.y<=(brwin)->rgeom.y)?\ + (e)->area.height-((brwin)->rgeom.y-(e)->area.y):\ + ((e)->area.y<=(brwin)->rgeom.y+(brwin)->rgeom.height)?\ + (((brwin)->rgeom.height-(e)->area.y+(brwin)->rgeom.y<(e)->area.height)?\ + ((brwin)->rgeom.height-(e)->area.y+(brwin)->rgeom.y<(e)->area.height):(e)->area.height):-1;\ + \ + if((wgeom)->width>(brwin)->rgeom.width)(wgeom)->width=(brwin)->rgeom.width;\ + if((wgeom)->height>(brwin)->rgeom.height)(wgeom)->height=(brwin)->rgeom.height;\ + }\ +} + +#define CLIP_DUMMY_POINTER_AREA(dummy_p_area,brwin,wgeom){\ + (wgeom)->x=((((dummy_p_area).x+(dummy_p_area).width>=(brwin)->rgeom.x)&&\ + ((dummy_p_area).x<=(brwin)->rgeom.x+(brwin)->rgeom.width))?\ + (((dummy_p_area).x<=(brwin)->rgeom.x)?(brwin)->rgeom.x:(dummy_p_area).x):-1);\ + (wgeom)->y=((((dummy_p_area).y+(dummy_p_area).height>=(brwin)->rgeom.y)&&\ + ((dummy_p_area).y<=(brwin)->rgeom.y+(brwin)->rgeom.height))?\ + (((dummy_p_area).y<=(brwin)->rgeom.y)?(brwin)->rgeom.y:(dummy_p_area).y):-1);\ + (wgeom)->width=((dummy_p_area).x<=(brwin)->rgeom.x)?\ + (dummy_p_area).width-((brwin)->rgeom.x-(dummy_p_area).x):\ + ((dummy_p_area).x<=(brwin)->rgeom.x+(brwin)->rgeom.width)?\ + ((brwin)->rgeom.width-(dummy_p_area).x+(brwin)->rgeom.x<(dummy_p_area).width)?\ + (brwin)->rgeom.width-(dummy_p_area).x+(brwin)->rgeom.x:(dummy_p_area).width:-1;\ + (wgeom)->height=((dummy_p_area).y<=(brwin)->rgeom.y)?\ + (dummy_p_area).height-((brwin)->rgeom.y-(dummy_p_area).y):\ + ((dummy_p_area).y<=(brwin)->rgeom.y+(brwin)->rgeom.height)?\ + ((brwin)->rgeom.height-(dummy_p_area).y+(brwin)->rgeom.y<(dummy_p_area).height)?\ + ((brwin)->rgeom.height-(dummy_p_area).y+(brwin)->rgeom.y<(dummy_p_area).height):(dummy_p_area).height:-1;\ + if((wgeom)->width>(brwin)->rgeom.width)(wgeom)->width=(brwin)->rgeom.width;\ + if((wgeom)->height>(brwin)->rgeom.height)(wgeom)->height=(brwin)->rgeom.height;\ +} + + + +#define DEFAULT_ARGS(args){\ + (args)->delay=0;\ + (args)->display=(char *)malloc(strlen(getenv("DISPLAY"))+1);\ + strcpy((args)->display,getenv("DISPLAY"));\ + (args)->windowid=(args)->x=(args)->y\ + =(args)->width=(args)->height=(args)->quietmode\ + =(args)->nosound=(args)->scshot=(args)->full_shots=0;\ + (args)->noshared=(args)->scale_shot=1;\ + (args)->filename=(char *)malloc(8);\ + strcpy((args)->filename,"out.ogg");\ + (args)->encoding=OGG_THEORA_VORBIS;\ + (args)->cursor_color=1;\ + (args)->have_dummy_cursor=1;\ + (args)->device=(char *)malloc(8);\ + strcpy((args)->device,"hw:0,0");\ + (args)->fps=15;\ + (args)->channels=1;\ + (args)->frequency=22050;\ + (args)->v_bitrate=45000;\ + (args)->v_quality=63;\ + (args)->s_quality=10;\ +} + +#define QUERY_DISPLAY_SPECS(display,specstruct){\ + (specstruct)->screen=DefaultScreen(display);\ + (specstruct)->width=DisplayWidth(display,(specstruct)->screen);\ + (specstruct)->height=DisplayHeight(display,(specstruct)->screen);\ + (specstruct)->root=RootWindow(display,(specstruct)->screen);\ + (specstruct)->visual=DefaultVisual(display,(specstruct)->screen);\ + (specstruct)->gc=DefaultGC(display,(specstruct)->screen);\ + (specstruct)->depth=DefaultDepth(display,(specstruct)->screen);\ + (specstruct)->bpixel=XBlackPixel(display,(specstruct)->screen);\ + (specstruct)->wpixel=XWhitePixel(display,(specstruct)->screen);\ +} + +#define UPDATE_YUV_BUFFER_SH(yuv,data,x_tm,y_tm,width_tm,height_tm){\ + int i,k;\ + for(k=y_tm;k<y_tm+height_tm;k++){\ + for(i=x_tm;i<x_tm+width_tm;i++){\ + yuv->y[i+k*yuv->y_width]=min(abs(data[(i+k*yuv->y_width)*4+2] * 2104 + data[(i+k*yuv->y_width)*4+1] * 4130 + data[(i+k*yuv->y_width)*4] * 802 + 4096 + 131072) >> 13, 235);\ + if((k%2)&&(i%2)){\ + yuv->u[i/2+k/2*yuv->uv_width]=min(abs(data[(i+k*yuv->y_width)*4+2] * -1214 + data[(i+k*yuv->y_width)*4+1] * -2384 + data[(i+k*yuv->y_width)*4] * 3598 + 4096 + 1048576) >> 13, 240);\ + yuv->v[i/2+k/2*yuv->uv_width]=min(abs(data[(i+k*yuv->y_width)*4+2] * 3598 + data[(i+k*yuv->y_width)*4+1] * -3013 + data[(i+k*yuv->y_width)*4] * -585 + 4096 + 1048576) >> 13, 240);\ + }\ + }\ + }\ +} + +#define UPDATE_YUV_BUFFER_IM(yuv,data,x_tm,y_tm,width_tm,height_tm){\ + int i,k,j=0;\ + int x_2=x_tm/2,y_2=y_tm/2,y_width_2=yuv->y_width/2;\ + for(k=0;k<height_tm;k++){\ + for(i=0;i<width_tm;i++){\ + yuv->y[x_tm+i+(k+y_tm)*yuv->y_width]=min(abs(data[(j*4)+2] * 2104 + data[(j*4)+1] * 4130 + data[(j*4)] * 802 + 4096 + 131072) >> 13, 235);\ + if((k%2)&&(i%2)){\ + yuv->u[x_2+i/2+(k/2+y_2)*y_width_2]=min(abs(data[(k*width_tm+i)*4+2] * -1214 + data[(k*width_tm+i)*4+1] * -2384 + data[(k*width_tm+i)*4] * 3598 + 4096 + 1048576) >> 13, 240);\ + yuv->v[x_2+i/2+(k/2+y_2)*y_width_2]=min(abs(data[(k*width_tm+i)*4+2] * 3598 + data[(k*width_tm+i)*4+1] * -3013 + data[(k*width_tm+i)*4] * -585 + 4096 + 1048576) >> 13, 240);\ + }\ + \ + j++;\ + }\ + }\ +} + +#define DUMMY_POINTER_TO_YUV(yuv,data_tm,x_tm,y_tm,width_tm,height_tm,no_pixel){\ + int i,k,j=0;\ + int x_2=x_tm/2,y_2=y_tm/2,y_width_2=(yuv)->y_width/2;\ + for(k=0;k<height_tm;k++){\ + for(i=0;i<width_tm;i++){\ + if(data_tm[(j*4)]!=(no_pixel)){\ + (yuv)->y[x_tm+i+(k+y_tm)*(yuv)->y_width]=min(abs(data_tm[(j*4)+2] * 2104 + data_tm[(j*4)+1] * 4130 + data_tm[(j*4)] * 802 + 4096 + 131072) >> 13, 235);\ + if((k%2)&&(i%2)){\ + yuv->u[x_2+i/2+(k/2+y_2)*y_width_2]=min(abs(data_tm[(k*width_tm+i)*4+2] * -1214 + data_tm[(k*width_tm+i)*4+1] * -2384 + data_tm[(k*width_tm+i)*4] * 3598 + 4096 + 1048576) >> 13, 240);\ + yuv->v[x_2+i/2+(k/2+y_2)*y_width_2]=min(abs(data_tm[(k*width_tm+i)*4+2] * 3598 + data_tm[(k*width_tm+i)*4+1] * -3013 + data_tm[(k*width_tm+i)*4] * -585 + 4096 + 1048576) >> 13, 240);\ + }\ + }\ + j++;\ + }\ + }\ +} + + +/**Function prototypes*/ + +void *PollDamage(void *pdata); +void *GetFrame(void *pdata); +void *EncodeImageBuffer(void *pdata); +void *FlushToOgg(void *pdata); +void UpdateYUVBuffer(yuv_buffer *yuv,unsigned char *data,int x,int y,int width,int height); +void ClearList(RectArea **root); +int RectInsert(RectArea **root,WGeometry *wgeom); +int CollideRects(WGeometry *wgeom1,WGeometry *wgeom2,WGeometry **wgeom_return,int *ngeoms); +void SetExpired(int signum); +void RegisterCallbacks(ProgArgs *args); +void UpdateImage(Display * dpy,yuv_buffer *yuv,pthread_mutex_t *yuv_mutex,DisplaySpecs *specs,RectArea **root,BRWindow *brwin,char *datatemp,int noshmem); +void XImageToYUV(XImage *imgz,yuv_buffer *yuv); +int GetZPixmap(Display *dpy,Window root,char *data,int x,int y,int width,int height); +int ParseArgs(int argc,char **argv,ProgArgs *arg_return); +int QueryExtensions(Display *dpy,ProgArgs *args,int *damage_event,int *damage_error); +int SetBRWindow(Display *dpy,BRWindow *brwin,DisplaySpecs *specs,ProgArgs *args); +int ZPixmapToBMP(XImage *imgz,BRWindow *brwin,char *fname,int nbytes,int scale); +unsigned char *MakeDummyPointer(DisplaySpecs *specs,int size,int color,int type,unsigned char *npxl); +void UpdateYUVBufferSh(yuv_buffer *yuv,unsigned char *data,int x,int y,int width,int height); +void UpdateYUVBufferIm(yuv_buffer *yuv,unsigned char *data,int x,int y,int width,int height); +void *CaptureSound(void *pdata); +void *EncodeSoundBuffer(void *pdata); +snd_pcm_t *OpenDev(const char *pcm_dev,unsigned int channels,unsigned int *frequency,snd_pcm_uframes_t *periodsize,unsigned int *periodtime,int *hardpause); +void InitEncoder(ProgData *pdata,EncData *enc_data_t); + +#endif + diff --git a/recordmydesktop/src/Makefile.am b/recordmydesktop/src/Makefile.am new file mode 100644 index 0000000..d3d7a20 --- /dev/null +++ b/recordmydesktop/src/Makefile.am @@ -0,0 +1,28 @@ +bin_PROGRAMS = recordmydesktop + + + +recordmydesktop_SOURCES= recordmydesktop.c\ + zpixmaptobmp.c\ + getzpixmap.c\ + parseargs.c\ + rectinsert.c\ + setbrwindow.c\ + queryextensions.c\ + register_callbacks.c\ + get_frame.c\ + update_image.c\ + poll_damage.c\ + encode_image_buffer.c\ + bgr_to_yuv.c\ + flush_to_ogg.c\ + make_dummy_pointer.c\ + opendev.c\ + capture_sound.c\ + encode_sound_buffer.c\ + init_encoder.c + +INCLUDES= $(all_includes) -I../include + +recordmydesktop_LDFLAGS = -D_THREAD_SAFE -pthread -Wall -O3 + diff --git a/recordmydesktop/src/bgr_to_yuv.c b/recordmydesktop/src/bgr_to_yuv.c new file mode 100644 index 0000000..9441e5d --- /dev/null +++ b/recordmydesktop/src/bgr_to_yuv.c @@ -0,0 +1,74 @@ +/********************************************************************************* +* recordMyDesktop * +********************************************************************************** +* * +* Copyright (C) 2006 John Varouhakis * +* * +* * +* 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 * +* * +* * +* * +* For further information contact me at biocrasher@gmail.com * +**********************************************************************************/ + + +#include <recordmydesktop.h> + +void XImageToYUV(XImage *imgz,yuv_buffer *yuv){ + unsigned char *dtap=(unsigned char *)imgz->data; + int i,k,j=0; + + for(k=0;k<(imgz->width*imgz->height);k++){ + yuv->y[k]=min(abs(dtap[(k*4)+2] * 2104 + dtap[(k*4)+1] * 4130 + dtap[(k*4)] * 802 + 4096 + 131072) >> 13, 235); + } + + for(i=0;i<(imgz->height);i+=2){ + for(k=0;k<(imgz->width);k+=2){ + yuv->u[j]=min(abs(dtap[i*imgz->bytes_per_line+k*4+2] * -1214 + dtap[i*imgz->bytes_per_line+k*4+1] * -2384 + dtap[i*imgz->bytes_per_line+k*4] * 3598 + 4096 + 1048576) >> 13, 240); + yuv->v[j]=min(abs(dtap[i*imgz->bytes_per_line+k*4+2] * 3598 + dtap[i*imgz->bytes_per_line+k*4+1] * -3013 + dtap[i*imgz->bytes_per_line+k*4] * -585 + 4096 + 1048576) >> 13, 240); + j++; + } + } +} + +void UpdateYUVBufferSh(yuv_buffer *yuv,unsigned char *data,int x,int y,int width,int height){ + int i,k; + for(k=y;k<y+height;k++){ + for(i=x;i<x+width;i++){ + yuv->y[i+k*yuv->y_width]=min(abs(data[(i+k*yuv->y_width)*4+2] * 2104 + data[(i+k*yuv->y_width)*4+1] * 4130 + data[(i+k*yuv->y_width)*4] * 802 + 4096 + 131072) >> 13, 235); + if((k%2)&&(i%2)){ + yuv->u[i/2+k/2*yuv->uv_width]=min(abs(data[(i+k*yuv->y_width)*4+2] * -1214 + data[(i+k*yuv->y_width)*4+1] * -2384 + data[(i+k*yuv->y_width)*4] * 3598 + 4096 + 1048576) >> 13, 240); + yuv->v[i/2+k/2*yuv->uv_width]=min(abs(data[(i+k*yuv->y_width)*4+2] * 3598 + data[(i+k*yuv->y_width)*4+1] * -3013 + data[(i+k*yuv->y_width)*4] * -585 + 4096 + 1048576) >> 13, 240); + } + } + } +} + +void UpdateYUVBufferIm(yuv_buffer *yuv,unsigned char *data,int x,int y,int width,int height){ + int i,k,j=0; + int x_2=x/2,y_2=y/2,y_width_2=yuv->y_width/2; + for(k=0;k<height;k++){ + for(i=0;i<width;i++){ + yuv->y[x+i+(k+y)*yuv->y_width]=min(abs(data[(j*4)+2] * 2104 + data[(j*4)+1] * 4130 + data[(j*4)] * 802 + 4096 + 131072) >> 13, 235); + if((k%2)&&(i%2)){ + yuv->u[x_2+i/2+(k/2+y_2)*y_width_2]=min(abs(data[(k*width+i)*4+2] * -1214 + data[(k*width+i)*4+1] * -2384 + data[(k*width+i)*4] * 3598 + 4096 + 1048576) >> 13, 240); + yuv->v[x_2+i/2+(k/2+y_2)*y_width_2]=min(abs(data[(k*width+i)*4+2] * 3598 + data[(k*width+i)*4+1] * -3013 + data[(k*width+i)*4] * -585 + 4096 + 1048576) >> 13, 240); + } + j++; + } + } +} + diff --git a/recordmydesktop/src/capture_sound.c b/recordmydesktop/src/capture_sound.c new file mode 100644 index 0000000..5dc3839 --- /dev/null +++ b/recordmydesktop/src/capture_sound.c @@ -0,0 +1,106 @@ +/********************************************************************************* +* recordMyDesktop * +********************************************************************************** +* * +* Copyright (C) 2006 John Varouhakis * +* * +* * +* 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 * +* * +* * +* * +* For further information contact me at biocrasher@gmail.com * +**********************************************************************************/ + + +#include <recordmydesktop.h> + +void *CaptureSound(void *pdata){ + + int frames=((ProgData *)pdata)->periodsize>>((ProgData *)pdata)->args.channels; +// fprintf(stderr,"fr %d ps %d\n",frames,((ProgData *)pdata)->periodsize);fflush(stderr); + pthread_mutex_t pmut; + pthread_mutex_init(&pmut,NULL); + ((ProgData *)pdata)->sound_buffer=NULL; + + while(((ProgData *)pdata)->running){ + int sret=0; + SndBuffer *newbuf,*tmp; + if(Paused){ + if(!((ProgData *)pdata)->hard_pause){ + snd_pcm_pause(((ProgData *)pdata)->sound_handle,1); + pthread_cond_wait(&((ProgData *)pdata)->pause_cond,&pmut); + snd_pcm_pause(((ProgData *)pdata)->sound_handle,0); + } + else{//device doesn't support pause(is this the norm?mine doesn't) + snd_pcm_close(((ProgData *)pdata)->sound_handle); + pthread_cond_wait(&((ProgData *)pdata)->pause_cond,&pmut); + + ((ProgData *)pdata)->sound_handle= + OpenDev(((ProgData *)pdata)->args.device, + ((ProgData *)pdata)->args.channels, + &((ProgData *)pdata)->args.frequency, + NULL, + NULL, + NULL//let's hope that the device capabilities didn't magically change + ); + if(((ProgData *)pdata)->sound_handle==NULL){ + fprintf(stderr,"Couldn't reopen sound device.\nThere will be no sound data from this point on.\n"); + pthread_exit(&errno); + } + } + } + + //create new buffer + newbuf=(SndBuffer *)malloc(sizeof(SndBuffer *)); + newbuf->data=(signed char *)malloc(((ProgData *)pdata)->periodsize); + newbuf->next=NULL; + + //read data into new buffer + while(sret<frames){ + int tsret=snd_pcm_readi(((ProgData *)pdata)->sound_handle, + newbuf->data+2*((ProgData *)pdata)->args.channels*sret, + frames-sret); + if(tsret==-EPIPE) + snd_pcm_prepare(((ProgData *)pdata)->sound_handle); + else if (tsret<0){ + fprintf(stderr,"An error occured while reading sound data:\n %s\n",snd_strerror(sret)); + } + else + sret+=tsret; + } + + //queue the new buffer + pthread_mutex_lock(&((ProgData *)pdata)->sound_buffer_mutex); + tmp=((ProgData *)pdata)->sound_buffer; + if(((ProgData *)pdata)->sound_buffer==NULL) + ((ProgData *)pdata)->sound_buffer=newbuf; + else{ + while(tmp->next!=NULL) + tmp=tmp->next; + tmp->next=newbuf; + } + pthread_mutex_unlock(&((ProgData *)pdata)->sound_buffer_mutex); + + + //signal that there are data to be proccessed + pthread_cond_signal(&((ProgData *)pdata)->sound_data_read); + } + snd_pcm_close(((ProgData *)pdata)->sound_handle); + pthread_exit(&errno); +} + + + diff --git a/recordmydesktop/src/encode_image_buffer.c b/recordmydesktop/src/encode_image_buffer.c new file mode 100644 index 0000000..f4c712a --- /dev/null +++ b/recordmydesktop/src/encode_image_buffer.c @@ -0,0 +1,33 @@ +#include <recordmydesktop.h> + +void *EncodeImageBuffer(void *pdata){ + pthread_mutex_t pmut,imut; + pthread_mutex_init(&pmut,NULL); + pthread_mutex_init(&imut,NULL); + + while(((ProgData *)pdata)->running){ + pthread_cond_wait(&((ProgData *)pdata)->image_buffer_ready,&imut); + if(Paused) + pthread_cond_wait(&((ProgData *)pdata)->pause_cond,&pmut);//this may not be needed + pthread_mutex_lock(&((ProgData *)pdata)->yuv_mutex); + if(theora_encode_YUVin(&((ProgData *)pdata)->enc_data->m_th_st,&((ProgData *)pdata)->enc_data->yuv)){ + fprintf(stderr,"Encoder not ready!\n"); + } + pthread_mutex_unlock(&((ProgData *)pdata)->yuv_mutex); + theora_encode_packetout(&((ProgData *)pdata)->enc_data->m_th_st,0,&((ProgData *)pdata)->enc_data->m_ogg_pckt1); + ogg_stream_packetin(&((ProgData *)pdata)->enc_data->m_ogg_ts,&((ProgData *)pdata)->enc_data->m_ogg_pckt1); + avd+=((ProgData *)pdata)->frametime*2*((ProgData *)pdata)->args.channels; + } + //last packet + if(theora_encode_YUVin(&((ProgData *)pdata)->enc_data->m_th_st,&((ProgData *)pdata)->enc_data->yuv)){ + fprintf(stderr,"Encoder not ready!\n"); + } + + theora_encode_packetout(&((ProgData *)pdata)->enc_data->m_th_st,1,&((ProgData *)pdata)->enc_data->m_ogg_pckt1); + +// ogg_stream_packetin(&((ProgData *)pdata)->enc_data->m_ogg_ts,&((ProgData *)pdata)->enc_data->m_ogg_pckt); + + pthread_exit(&errno); +} + + diff --git a/recordmydesktop/src/encode_sound_buffer.c b/recordmydesktop/src/encode_sound_buffer.c new file mode 100644 index 0000000..1c42ae8 --- /dev/null +++ b/recordmydesktop/src/encode_sound_buffer.c @@ -0,0 +1,64 @@ +#include <recordmydesktop.h> + +void *EncodeSoundBuffer(void *pdata){ + + int sampread=((ProgData *)pdata)->periodsize/(2*((ProgData *)pdata)->args.channels); + pthread_mutex_t smut; + pthread_mutex_init(&smut,NULL); + while((((ProgData *)pdata)->running)){ + float **vorbis_buffer; + int count=0,i,j; + SndBuffer *buff; + + if(Paused){ + pthread_mutex_t tmut; + pthread_mutex_init(&tmut,NULL); + pthread_cond_wait(&((ProgData *)pdata)->pause_cond,&tmut); + } + + if(((ProgData *)pdata)->sound_buffer==NULL) + pthread_cond_wait(&((ProgData *)pdata)->sound_data_read,&smut); + + pthread_mutex_lock(&((ProgData *)pdata)->sound_buffer_mutex); + buff=((ProgData *)pdata)->sound_buffer; + //advance the list + ((ProgData *)pdata)->sound_buffer=((ProgData *)pdata)->sound_buffer->next; + pthread_mutex_unlock(&((ProgData *)pdata)->sound_buffer_mutex); + + if (avd>0){ + vorbis_buffer=vorbis_analysis_buffer(&((ProgData *)pdata)->enc_data->m_vo_dsp,sampread); + + for(i=0;i<sampread;i++){ + for(j=0;j<((ProgData *)pdata)->args.channels;j++){ + vorbis_buffer[j][i]=((buff->data[count+1]<<8)| + (0x00ff&(int)buff->data[count]))/32768.f; + count+=2; + } + } + vorbis_analysis_wrote(&((ProgData *)pdata)->enc_data->m_vo_dsp,sampread); + + while(vorbis_analysis_blockout(&((ProgData *)pdata)->enc_data->m_vo_dsp,&((ProgData *)pdata)->enc_data->m_vo_block)==1){ + + vorbis_analysis(&((ProgData *)pdata)->enc_data->m_vo_block,NULL); + vorbis_bitrate_addblock(&((ProgData *)pdata)->enc_data->m_vo_block); + + while(vorbis_bitrate_flushpacket(&((ProgData *)pdata)->enc_data->m_vo_dsp,&((ProgData *)pdata)->enc_data->m_ogg_pckt2)) + ogg_stream_packetin(&((ProgData *)pdata)->enc_data->m_ogg_vs,&((ProgData *)pdata)->enc_data->m_ogg_pckt2); + } + avd-=((ProgData *)pdata)->periodtime; + } + free(buff); + } + + vorbis_analysis_wrote(&((ProgData *)pdata)->enc_data->m_vo_dsp,0); +// while(vorbis_analysis_blockout(&((ProgData *)pdata)->enc_data->m_vo_dsp,&((ProgData *)pdata)->enc_data->m_vo_block)==1){ +// vorbis_analysis(&((ProgData *)pdata)->enc_data->m_vo_block,NULL); +// vorbis_bitrate_addblock(&((ProgData *)pdata)->enc_data->m_vo_block); +// while(vorbis_bitrate_flushpacket(&((ProgData *)pdata)->enc_data->m_vo_dsp,&((ProgData *)pdata)->enc_data->m_ogg_pckt2)) +// ogg_stream_packetin(&((ProgData *)pdata)->enc_data->m_ogg_vs,&((ProgData *)pdata)->enc_data->m_ogg_pckt2); +// } + + pthread_exit(&errno); +} + + diff --git a/recordmydesktop/src/flush_to_ogg.c b/recordmydesktop/src/flush_to_ogg.c new file mode 100644 index 0000000..c32302c --- /dev/null +++ b/recordmydesktop/src/flush_to_ogg.c @@ -0,0 +1,64 @@ +#include <recordmydesktop.h> + +void *FlushToOgg(void *pdata){ + int videoflag=0,audioflag=0; + double video_bytesout=0,audio_bytesout=0; + ogg_page videopage,audiopage; + int prev=0; + while(((ProgData *)pdata)->running){ +// if(Paused)pthread_cond_wait(&((ProgData *)pdata)->pause_cond,&((ProgData *)pdata)->pause_cond_mutex); +// if(!prev){ + videoflag=ogg_stream_pageout(&((ProgData *)pdata)->enc_data->m_ogg_ts,&videopage); + if(videoflag){ + video_bytesout+=fwrite(videopage.header,1,videopage.header_len,((ProgData *)pdata)->enc_data->fp); + video_bytesout+=fwrite(videopage.body,1,videopage.body_len,((ProgData *)pdata)->enc_data->fp); + videoflag=0; +// prev=(!((ProgData *)pdata)->args.nosound); + + +// } + if(!((ProgData *)pdata)->args.nosound){ + + audioflag=ogg_stream_pageout(&((ProgData *)pdata)->enc_data->m_ogg_vs,&audiopage); + + if(audioflag){ + audio_bytesout+=fwrite(audiopage.header,1,audiopage.header_len,((ProgData *)pdata)->enc_data->fp); + audio_bytesout+=fwrite(audiopage.body,1,audiopage.body_len,((ProgData *)pdata)->enc_data->fp); + audioflag=0; + prev=0; + } + } +// else +// if(!did_one) +// usleep(10000); + } + else /*if(((ProgData *)pdata)->args.nosound)*/ + usleep(10000); + + } + //last packages + videoflag=ogg_stream_flush(&((ProgData *)pdata)->enc_data->m_ogg_ts,&videopage); + if(videoflag){ + video_bytesout+=fwrite(videopage.header,1,videopage.header_len,((ProgData *)pdata)->enc_data->fp); + video_bytesout+=fwrite(videopage.body,1,videopage.body_len,((ProgData *)pdata)->enc_data->fp); + videoflag=0; + } + if(!((ProgData *)pdata)->args.nosound) + audioflag=ogg_stream_flush(&((ProgData *)pdata)->enc_data->m_ogg_vs,&audiopage); + if(audioflag){ + audio_bytesout+=fwrite(audiopage.header,1,audiopage.header_len,((ProgData *)pdata)->enc_data->fp); + audio_bytesout+=fwrite(audiopage.body,1,audiopage.body_len,((ProgData *)pdata)->enc_data->fp); + audioflag=0; + } + + ogg_stream_clear(&((ProgData *)pdata)->enc_data->m_ogg_ts); + if(!((ProgData *)pdata)->args.nosound) + ogg_stream_clear(&((ProgData *)pdata)->enc_data->m_ogg_vs); +//this always gives me a segfault :( +// theora_clear(&((ProgData *)pdata)->enc_data->m_th_st); + + if(((ProgData *)pdata)->enc_data->fp)fclose(((ProgData *)pdata)->enc_data->fp); + + fprintf(stderr,"\r \nDone.\nWritten %.0f bytes\n(%.0f of which were video data and %.0f audio data)\n\n",video_bytesout+audio_bytesout,video_bytesout,audio_bytesout); + pthread_exit(&errno); +} diff --git a/recordmydesktop/src/get_frame.c b/recordmydesktop/src/get_frame.c new file mode 100644 index 0000000..67c6eb2 --- /dev/null +++ b/recordmydesktop/src/get_frame.c @@ -0,0 +1,125 @@ +/********************************************************************************* +* recordMyDesktop * +********************************************************************************** +* * +* Copyright (C) 2006 John Varouhakis * +* * +* * +* 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 * +* * +* * +* * +* For further information contact me at biocrasher@gmail.com * +**********************************************************************************/ + + +#include <recordmydesktop.h> + +void *GetFrame(void *pdata){ + int tlist_sel=0; + pthread_mutex_t pmut,tmut; + uint msk_ret; + WGeometry mouse_pos_abs,mouse_pos_rel,mouse_pos_temp; + Window root_ret,child_ret; + mouse_pos_abs.x=0; + mouse_pos_abs.y=0; + mouse_pos_abs.width=((ProgData *)pdata)->dummy_p_size; + mouse_pos_abs.height=((ProgData *)pdata)->dummy_p_size; + pthread_mutex_init(&pmut,NULL); + pthread_mutex_init(&tmut,NULL); + + while(((ProgData *)pdata)->running){ + pthread_cond_wait(&((ProgData *)pdata)->time_cond,&tmut); + if(Paused){ + pthread_cond_wait(&((ProgData *)pdata)->pause_cond,&pmut); + } + /*pthread_cond_wait(&((ProgData *)pdata)->pause_cond,&((ProgData *)pdata)->pause_cond_mutex);*/ + //mutexes and lists with changes are useless when full_shots is enabled + if(!((ProgData *)pdata)->args.full_shots){ + tlist_sel=((ProgData *)pdata)->list_selector; + ((ProgData *)pdata)->list_selector=((((ProgData *)pdata)->list_selector+1)%2); + pthread_mutex_lock(&((ProgData *)pdata)->list_mutex[tlist_sel]); + } + + if(((ProgData *)pdata)->args.have_dummy_cursor){ + //dummy pointer sequence + //update previous_position + //(if full_shots is enabled this is skipped since it's pointless) + if(!((ProgData *)pdata)->args.full_shots){ + CLIP_DUMMY_POINTER_AREA(mouse_pos_abs,&((ProgData *)pdata)->brwin,&mouse_pos_temp); + if((mouse_pos_temp.x>=0)&&(mouse_pos_temp.y>=0)&&(mouse_pos_temp.width>0)&&(mouse_pos_temp.height>0)) + RectInsert(&((ProgData *)pdata)->rect_root[tlist_sel],&mouse_pos_temp); + } + //find new one + XQueryPointer(((ProgData *)pdata)->dpy, + ((ProgData *)pdata)->specs.root, + &root_ret,&child_ret, + &mouse_pos_abs.x,&mouse_pos_abs.y, + &mouse_pos_rel.x,&mouse_pos_rel.y,&msk_ret); + } + if(!((ProgData *)pdata)->args.noshared) + XShmGetImage(((ProgData *)pdata)->dpy,((ProgData *)pdata)->specs.root,((ProgData *)pdata)->image,(((ProgData *)pdata)->brwin.rgeom.x),(((ProgData *)pdata)->brwin.rgeom.y),AllPlanes); + if(!((ProgData *)pdata)->args.full_shots) + UpdateImage(((ProgData *)pdata)->dpy, + &((ProgData *)pdata)->enc_data->yuv, + &((ProgData *)pdata)->yuv_mutex, + &((ProgData *)pdata)->specs, + &((ProgData *)pdata)->rect_root[tlist_sel], + &((ProgData *)pdata)->brwin, + ((((ProgData *)pdata)->args.noshared)?(((ProgData *)pdata)->datatemp):((ProgData *)pdata)->image->data), + ((ProgData *)pdata)->args.noshared); + else{ + if(((ProgData *)pdata)->args.noshared){ + GetZPixmap( ((ProgData *)pdata)->dpy, + ((ProgData *)pdata)->specs.root, + ((ProgData *)pdata)->image->data, + ((ProgData *)pdata)->brwin.rgeom.x, + ((ProgData *)pdata)->brwin.rgeom.y, + ((ProgData *)pdata)->brwin.rgeom.width, + ((ProgData *)pdata)->brwin.rgeom.height); + pthread_mutex_lock(&((ProgData *)pdata)->yuv_mutex); + XImageToYUV(((ProgData *)pdata)->image,&((ProgData *)pdata)->enc_data->yuv); + pthread_mutex_unlock(&((ProgData *)pdata)->yuv_mutex); + } + else{ + pthread_mutex_lock(&((ProgData *)pdata)->yuv_mutex); + XImageToYUV(((ProgData *)pdata)->image,&((ProgData *)pdata)->enc_data->yuv); + pthread_mutex_unlock(&((ProgData *)pdata)->yuv_mutex); + } + } + if(((ProgData *)pdata)->args.have_dummy_cursor){ + //avoid segfaults + CLIP_DUMMY_POINTER_AREA(mouse_pos_abs,&((ProgData *)pdata)->brwin,&mouse_pos_temp); + //draw the cursor + if((mouse_pos_temp.x>=0)&&(mouse_pos_temp.y>=0)&&(mouse_pos_temp.width>0)&&(mouse_pos_temp.height>0)){ + DUMMY_POINTER_TO_YUV((&((ProgData *)pdata)->enc_data->yuv), + ((ProgData *)pdata)->dummy_pointer, + (mouse_pos_temp.x-((ProgData *)pdata)->brwin.rgeom.x), + (mouse_pos_temp.y-((ProgData *)pdata)->brwin.rgeom.y), + mouse_pos_temp.width, + mouse_pos_temp.height, + ((ProgData *)pdata)->npxl); + } + } + if(!((ProgData *)pdata)->args.full_shots){ + ClearList(&((ProgData *)pdata)->rect_root[tlist_sel]); + pthread_mutex_unlock(&((ProgData *)pdata)->list_mutex[tlist_sel]); + } + pthread_cond_broadcast(&((ProgData *)pdata)->image_buffer_ready); + } + pthread_cond_broadcast(&((ProgData *)pdata)->image_buffer_ready); + pthread_exit(&errno); +} + diff --git a/recordmydesktop/src/getzpixmap.c b/recordmydesktop/src/getzpixmap.c new file mode 100644 index 0000000..d1af10b --- /dev/null +++ b/recordmydesktop/src/getzpixmap.c @@ -0,0 +1,55 @@ +/********************************************************************************* +* recordMyDesktop * +********************************************************************************** +* * +* Copyright (C) 2006 John Varouhakis * +* * +* * +* 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 * +* * +* * +* * +* For further information contact me at biocrasher@gmail.com * +**********************************************************************************/ + + +#include <recordmydesktop.h> + +int GetZPixmap(Display *dpy,Window root,char *data,int x,int y,int width,int height) { + xGetImageReply reply; + xGetImageReq *request; + long nbytes; + + LockDisplay(dpy); + GetReq(GetImage,request); + request->drawable=root; + request->x=x; + request->y=y; + request->width=width; + request->height=height; + request->planeMask=AllPlanes; + request->format=ZPixmap; + if((!_XReply(dpy,(xReply *)&reply,0,xFalse))||(!reply.length)){ + UnlockDisplay(dpy); + SyncHandle(); + return 1; + } + nbytes=(long)reply.length<<2; + _XReadPad(dpy,data,nbytes); + UnlockDisplay(dpy); + SyncHandle(); + return 0; +} + diff --git a/recordmydesktop/src/init_encoder.c b/recordmydesktop/src/init_encoder.c new file mode 100644 index 0000000..35440dc --- /dev/null +++ b/recordmydesktop/src/init_encoder.c @@ -0,0 +1,169 @@ +/********************************************************************************* +* recordMyDesktop * +********************************************************************************** +* * +* Copyright (C) 2006 John Varouhakis * +* * +* * +* 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 * +* * +* * +* * +* For further information contact me at biocrasher@gmail.com * +**********************************************************************************/ + + +#include <recordmydesktop.h> + +void InitEncoder(ProgData *pdata,EncData *enc_data_t){ + int y1,y2; + (pdata)->enc_data=enc_data_t; + srand(time(NULL)); + y1=rand(); + y2=rand(); + y2+=(y1==y2); + + ogg_stream_init(&(enc_data_t)->m_ogg_ts,y1); + if(!pdata->args.nosound) + ogg_stream_init(&(enc_data_t)->m_ogg_vs,y2); + + (enc_data_t)->fp=fopen((pdata)->args.filename,"w"); + + + theora_info_init(&(enc_data_t)->m_th_inf); + (enc_data_t)->m_th_inf.frame_width=(pdata)->brwin.rgeom.width; + (enc_data_t)->m_th_inf.frame_height=(pdata)->brwin.rgeom.height; + (enc_data_t)->m_th_inf.width=(((enc_data_t)->m_th_inf.frame_width + 15) >>4)<<4; + (enc_data_t)->m_th_inf.height=(((enc_data_t)->m_th_inf.frame_height + 15) >>4)<<4; + (enc_data_t)->m_th_inf.offset_x=(((enc_data_t)->m_th_inf.width-(enc_data_t)->m_th_inf.frame_width)/2)&~1; + (enc_data_t)->m_th_inf.offset_y=(((enc_data_t)->m_th_inf.height-(enc_data_t)->m_th_inf.frame_height)/2)&~1; + (enc_data_t)->m_th_inf.fps_numerator=((pdata)->args.fps*100.0); + (enc_data_t)->m_th_inf.fps_denominator=100; + (enc_data_t)->m_th_inf.aspect_numerator=(pdata)->brwin.rgeom.width; + (enc_data_t)->m_th_inf.aspect_denominator=(pdata)->brwin.rgeom.height; + (enc_data_t)->m_th_inf.colorspace=OC_CS_UNSPECIFIED; + (enc_data_t)->m_th_inf.pixelformat=OC_PF_420; + (enc_data_t)->m_th_inf.target_bitrate=(pdata)->args.v_bitrate; + (enc_data_t)->m_th_inf.quality=(pdata)->args.v_quality; + (enc_data_t)->m_th_inf.dropframes_p=0; + (enc_data_t)->m_th_inf.quick_p=1; + (enc_data_t)->m_th_inf.keyframe_auto_p=1; + (enc_data_t)->m_th_inf.keyframe_frequency=64; + (enc_data_t)->m_th_inf.keyframe_frequency_force=64; + (enc_data_t)->m_th_inf.keyframe_data_target_bitrate=(enc_data_t)->m_th_inf.quality*1.5; + (enc_data_t)->m_th_inf.keyframe_auto_threshold=80; + (enc_data_t)->m_th_inf.keyframe_mindistance=8; + (enc_data_t)->m_th_inf.noise_sensitivity=1; + + + theora_encode_init(&(enc_data_t)->m_th_st,&(enc_data_t)->m_th_inf); + + + + if(!pdata->args.nosound){ + int ret; + vorbis_info_init(&(enc_data_t)->m_vo_inf); + ret = vorbis_encode_init_vbr(&(enc_data_t)->m_vo_inf,pdata->args.channels,pdata->args.frequency,(float)pdata->args.s_quality*0.1); + if(ret){ + fprintf(stderr,"Error while setting up vorbis stream quality!\n"); + exit(1); + } + vorbis_comment_init(&(enc_data_t)->m_vo_cmmnt); + vorbis_analysis_init(&(enc_data_t)->m_vo_dsp,&(enc_data_t)->m_vo_inf); + vorbis_block_init(&(enc_data_t)->m_vo_dsp,&(enc_data_t)->m_vo_block); + } + + + theora_encode_header(&(enc_data_t)->m_th_st,&(enc_data_t)->m_ogg_pckt1); + ogg_stream_packetin(&(enc_data_t)->m_ogg_ts,&(enc_data_t)->m_ogg_pckt1); + if(ogg_stream_pageout(&(enc_data_t)->m_ogg_ts,&(enc_data_t)->m_ogg_pg)!=1){ + fprintf(stderr,"Internal Ogg library error.\n"); + exit(1); + } + fwrite((enc_data_t)->m_ogg_pg.header,1,(enc_data_t)->m_ogg_pg.header_len,(enc_data_t)->fp); + fwrite((enc_data_t)->m_ogg_pg.body,1,(enc_data_t)->m_ogg_pg.body_len,(enc_data_t)->fp); + + theora_comment_init(&(enc_data_t)->m_th_cmmnt); + theora_comment_add_tag(&(enc_data_t)->m_th_cmmnt,"recordMyDesktop",VERSION); + theora_encode_comment(&(enc_data_t)->m_th_cmmnt,&(enc_data_t)->m_ogg_pckt1); + ogg_stream_packetin(&(enc_data_t)->m_ogg_ts,&(enc_data_t)->m_ogg_pckt1); + theora_encode_tables(&(enc_data_t)->m_th_st,&(enc_data_t)->m_ogg_pckt1); + ogg_stream_packetin(&(enc_data_t)->m_ogg_ts,&(enc_data_t)->m_ogg_pckt1); + + + if(!pdata->args.nosound){ + ogg_packet header; + ogg_packet header_comm; + ogg_packet header_code; + + vorbis_analysis_headerout(&(enc_data_t)->m_vo_dsp,&(enc_data_t)->m_vo_cmmnt,&header,&header_comm,&header_code); + ogg_stream_packetin(&(enc_data_t)->m_ogg_vs,&header); + if(ogg_stream_pageout(&(enc_data_t)->m_ogg_vs,&(enc_data_t)->m_ogg_pg)!=1){ + fprintf(stderr,"Internal Ogg library error.\n"); + exit(1); + } + fwrite((enc_data_t)->m_ogg_pg.header,1,(enc_data_t)->m_ogg_pg.header_len,(enc_data_t)->fp); + fwrite((enc_data_t)->m_ogg_pg.body,1,(enc_data_t)->m_ogg_pg.body_len,(enc_data_t)->fp); + + ogg_stream_packetin(&(enc_data_t)->m_ogg_vs,&header_comm); + ogg_stream_packetin(&(enc_data_t)->m_ogg_vs,&header_code); + } + + + + while(1){ + int result = ogg_stream_flush(&(enc_data_t)->m_ogg_ts,&(enc_data_t)->m_ogg_pg); + if(result<0){ + fprintf(stderr,"Internal Ogg library error.\n"); + exit(1); + } + if(result==0)break; + fwrite((enc_data_t)->m_ogg_pg.header,1,(enc_data_t)->m_ogg_pg.header_len,(enc_data_t)->fp); + fwrite((enc_data_t)->m_ogg_pg.body,1,(enc_data_t)->m_ogg_pg.body_len,(enc_data_t)->fp); + } + + if(!pdata->args.nosound){ + while(1){ + int result=ogg_stream_flush(&(enc_data_t)->m_ogg_vs,&(enc_data_t)->m_ogg_pg); + if(result<0){ + fprintf(stderr,"Internal Ogg library error.\n"); + exit(1); + } + if(result==0)break; + fwrite((enc_data_t)->m_ogg_pg.header,1,(enc_data_t)->m_ogg_pg.header_len,(enc_data_t)->fp); + fwrite((enc_data_t)->m_ogg_pg.body,1,(enc_data_t)->m_ogg_pg.body_len,(enc_data_t)->fp); + } + } + + + + (enc_data_t)->yuv.y=(unsigned char *)malloc((pdata)->image->height*((ProgData *)pdata)->image->width); + (enc_data_t)->yuv.u=(unsigned char *)malloc((pdata)->image->height*((ProgData *)pdata)->image->width/4); + (enc_data_t)->yuv.v=(unsigned char *)malloc((pdata)->image->height*((ProgData *)pdata)->image->width/4); + (enc_data_t)->yuv.y_width=(enc_data_t)->m_th_inf.width; + (enc_data_t)->yuv.y_height=(enc_data_t)->m_th_inf.height; + (enc_data_t)->yuv.y_stride=(enc_data_t)->m_th_inf.width; + + (enc_data_t)->yuv.uv_width=(enc_data_t)->m_th_inf.width/2; + (enc_data_t)->yuv.uv_height=(enc_data_t)->m_th_inf.height/2; + (enc_data_t)->yuv.uv_stride=(enc_data_t)->m_th_inf.width/2; + + theora_info_clear(&(enc_data_t)->m_th_inf); + +} + + + + diff --git a/recordmydesktop/src/make_dummy_pointer.c b/recordmydesktop/src/make_dummy_pointer.c new file mode 100644 index 0000000..0ab0306 --- /dev/null +++ b/recordmydesktop/src/make_dummy_pointer.c @@ -0,0 +1,68 @@ +/********************************************************************************* +* recordMyDesktop * +********************************************************************************** +* * +* Copyright (C) 2006 John Varouhakis * +* * +* * +* 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 * +* * +* * +* * +* For further information contact me at biocrasher@gmail.com * +**********************************************************************************/ + + +#include <recordmydesktop.h> + +unsigned char *MakeDummyPointer(DisplaySpecs *specs,int size,int color,int type,unsigned char *npxl){ + int i,k,o='.'; + unsigned long w=(color)?1:-1, + b=(color)?-1:1; + char pmask[1][16][16]={{ + {w,w,w,w,w,w,o,o,o,o,o,o,o,o,o,o}, + {w,b,b,w,w,w,w,o,o,o,o,o,o,o,o,o}, + {w,b,b,b,w,w,w,w,o,o,o,o,o,o,o,o}, + {w,b,b,b,b,w,w,w,w,o,o,o,o,o,o,o}, + {w,b,b,b,b,b,w,w,w,w,o,o,o,o,o,o}, + {w,b,b,b,b,b,b,w,w,w,w,o,o,o,o,o}, + {w,b,b,b,b,b,b,b,w,w,w,w,o,o,o,o}, + {w,b,b,b,b,b,b,b,b,w,w,w,w,o,o,o}, + {w,b,b,b,b,b,b,b,b,b,w,w,w,w,o,o}, + {w,b,b,b,b,b,b,b,b,b,b,w,w,w,w,o}, + {w,b,b,b,b,b,b,b,b,w,w,w,w,o,o,o}, + {w,b,b,b,b,b,b,b,b,w,w,w,w,o,o,o}, + {w,w,w,w,w,b,b,b,b,b,w,w,w,o,o,o}, + {w,w,w,w,w,w,b,b,b,b,w,w,w,o,o,o}, + {o,o,o,o,o,w,w,b,b,b,w,w,w,o,o,o}, + {o,o,o,o,o,o,w,w,w,w,w,w,w,o,o,o}} + }; + + + unsigned char *ret=malloc(size*sizeof(char[size*4])); + unsigned char wp[4]={255,255,255,255}; + unsigned char bp[4]={0,0,0,0}; + *npxl=((wp[0]-1)!=bp[0])?wp[0]-100:wp[0]-102; + for(i=0;i<size;i++){ + for(k=0;k<size;k++){ + ret[(i*size+k)*4]=(pmask[type][i][k]==1)?wp[0]:(pmask[type][i][k]==-1)?bp[0]:*npxl; + ret[(i*size+k)*4+1]=(pmask[type][i][k]==1)?wp[1]:(pmask[type][i][k]==-1)?bp[1]:*npxl; + ret[(i*size+k)*4+2]=(pmask[type][i][k]==1)?wp[2]:(pmask[type][i][k]==-1)?bp[2]:*npxl; + ret[(i*size+k)*4+3]=(pmask[type][i][k]==1)?wp[3]:(pmask[type][i][k]==-1)?bp[3]:*npxl; + } + } + + return ret; +} diff --git a/recordmydesktop/src/opendev.c b/recordmydesktop/src/opendev.c new file mode 100644 index 0000000..d8a4179 --- /dev/null +++ b/recordmydesktop/src/opendev.c @@ -0,0 +1,98 @@ +/********************************************************************************* +* recordMyDesktop * +********************************************************************************** +* * +* Copyright (C) 2006 John Varouhakis * +* * +* * +* 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 * +* * +* * +* * +* For further information contact me at biocrasher@gmail.com * +**********************************************************************************/ + + + + +#include <recordmydesktop.h> + + +snd_pcm_t *OpenDev(const char *pcm_dev,unsigned int channels,unsigned int *frequency,snd_pcm_uframes_t *periodsize,unsigned int *periodtime,int *hard_pause){ + + snd_pcm_t *mhandle; + snd_pcm_hw_params_t *hwparams; + unsigned int periods=2; + unsigned int exactrate = *frequency; + + snd_pcm_hw_params_alloca(&hwparams); + snd_pcm_uframes_t buffsize=4096; + + if (snd_pcm_open(&mhandle, pcm_dev, SND_PCM_STREAM_CAPTURE, 0)<0){ + fprintf(stderr, "Couldn't open PCM device %s\n", pcm_dev); + return NULL; + } + if (snd_pcm_hw_params_any(mhandle, hwparams)<0){ + fprintf(stderr, "Couldn't configure PCM device.\n"); + return NULL; + } + if (snd_pcm_hw_params_set_access(mhandle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)<0) { + fprintf(stderr, "Couldn't set access.\n"); + return NULL; + } + if (snd_pcm_hw_params_set_format(mhandle, hwparams, SND_PCM_FORMAT_S16_LE)<0){ + fprintf(stderr, "Couldn't set format.\n"); + return NULL; + } + if (snd_pcm_hw_params_set_rate_near(mhandle, hwparams, &exactrate, 0)<0){ + fprintf(stderr, "Couldn't set frequency.\n"); + return NULL; + } + if (*frequency != exactrate){ + fprintf(stderr, "Playback frequency %dHz is not available...\nUsing %dHz instead.\n",*frequency,exactrate); + *frequency=exactrate; + } + if (snd_pcm_hw_params_set_channels_near(mhandle, hwparams, &channels)<0){ + fprintf(stderr, "Couldn't set channels number.\n"); + return NULL; + } + if (snd_pcm_hw_params_set_periods_near(mhandle, hwparams, &periods,0)<0) { + fprintf(stderr, "Couldn't set periods.\n"); + return NULL; + } + buffsize=(((exactrate*channels)))>>channels; + if (snd_pcm_hw_params_set_buffer_size_near(mhandle, hwparams,&buffsize)<0){ + fprintf(stderr, "Couldn't set buffer size.\n"); + return NULL; + } + if (snd_pcm_hw_params(mhandle, hwparams)<0){ + fprintf(stderr, "Couldn't set hardware parameters.\n"); + return NULL; + } + if(hard_pause!=NULL) + if(!snd_pcm_hw_params_can_pause(hwparams)){ +// fprintf(stderr, "Current sound device doesn't seem to support pausing!\nI will attempt to close/reopen device in case you opt to pause during recording.\n"); + *hard_pause=1; + } + if(periodsize!=NULL) + snd_pcm_hw_params_get_period_size(hwparams,periodsize,0); + if(periodtime!=NULL) + snd_pcm_hw_params_get_period_time(hwparams,periodtime,0); + snd_pcm_prepare(mhandle); + + return mhandle; +} + + diff --git a/recordmydesktop/src/parseargs.c b/recordmydesktop/src/parseargs.c new file mode 100644 index 0000000..ea82920 --- /dev/null +++ b/recordmydesktop/src/parseargs.c @@ -0,0 +1,380 @@ +/********************************************************************************* +* recordMyDesktop * +********************************************************************************** +* * +* Copyright (C) 2006 John Varouhakis * +* * +* * +* 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 * +* * +* * +* * +* For further information contact me at biocrasher@gmail.com * +**********************************************************************************/ + + +#include <recordmydesktop.h> + + +int ParseArgs(int argc,char **argv,ProgArgs *arg_return){ + int i; + char *usage="\nUsage:\n" + "\trecordmydesktop [-h| --help| --version| -delay n[H|h|M|m]| -windowid id_of_window|\n" + "\t-display DISPLAY| -x X| -y Y|-width N| -height N| -fps N(number>0)|\n" + "\t -v_quality n| -s_quality n| -v_bitrate n| -dummy-cursor color| --no-dummy-cursor|\n" + "\t -freq N(number>0)| -channels N(number>0)| -device SOUND_DEVICE| --nosound|\n" + "\t --with-shared| --full-shots| --scshot| -scale-shot N| -o filename]^filename\n\n\n" + + "General Options:\n" + "\t-h or --help\t\tPrint this help and exit.\n" + "\t--version\t\tPrint program version and exit.\n\n" + + "Image Options:\n" + "\t-windowid id_of_window\tid of window to be recorded.\n" + "\t-display DISPLAY\tDisplay to connect to.\n" + "\t-x X\t\t\tOffset in x direction.\n" + "\t-y Y\t\t\tOffset in y direction.\n" + "\t-width N\t\tWidth of recorded window.\n" + "\t-height N\t\tHeight of recorded window.\n\n" + + "\t-dummy-cursor color\tColor of the dummy cursor [black|white](default black)\n" + "\t--no-dummy-cursor\tDisable drawing of a dummy cursor.\n" + "\t--with-shared\t\tEnable usage of MIT-shared memory extension.\n" + "\t--full-shots\t\tTake full screenshot at every frame(Not recomended!).\n" + "\t-fps N(number>0.0)\tA positive number denoting desired framerate.\n\n" + + "Sound Options:\n" + "\t-channels N(number>0)\tA positive number denoting desired sound channels in recording.\n" + "\t-freq N(number>0)\tA positive number denoting desired sound frequency.\n" + "\t-device SOUND_DEVICE\tSound device(default hw0:0).\n" + "\t--nosound\t\tDo not record sound.\n\n" + + "Encoding Options\n" + "\t-v_quality n\t\tA number from 0 to 63 for desired encoded video quality(default 63).\n" + "\t-v_bitrate n\t\tA number from 45000 to 2000000 for desired encoded video bitrate(default 45000).\n" + "\t-s_quality n\t\tDesired audio quality(-1 to 10).\n\n" + + "Misc Options:\n" + "\t-delay n[H|h|M|m]\tNumber of secs(default),minutes or hours before capture starts(number can be float)\n" + "\t--scshot\t\tTake a bitmap screenshot(default rmdout.bmp) and exit.\n" + "\t-scale-shot N\t\tFactor by which screenshot is scaled down(1<=number<=64,power of 2).\n" + "\t-o filename\t\tName of recorded video(default out.ogg).\n" + "\n\tIf no other options are specified, filename can be given without the -o switch.\n\n\n"; + + if(argc==2){ + if(argv[1][0]!='-'){ + realloc(arg_return->filename,strlen(argv[1])+1); + strcpy(arg_return->filename,argv[1]); + return 0; + } + } + for(i=1;i<argc;i++){ + if(!strcmp(argv[i],"-delay")){ + if(i+1<argc){ + float num=atof(argv[i+1]); + if(num>0.0){ + int k; + for(k=0;k<strlen(argv[i+1]);k++){ + if((argv[i+1][k]=='M')||(argv[i+1][k]=='m')){ + num*=60.0; + break; + } + else if((argv[i+1][k]=='H')||(argv[i+1][k]=='h')){ + num*=3600.0; + break; + } + } + arg_return->delay=(int)num; + } + else{ + fprintf(stderr,"Argument Usage: -delay n[H|h|M|m]\nwhere n is a float number\n"); + return 1; + } + } + else{ + fprintf(stderr,"Argument Usage: -delay n[H|h|M|m]\nwhere n is a float number\n"); + return 1; + } + i++; + } + else if(!strcmp(argv[i],"-windowid")){ + if(i+1<argc){ + Window num=strtod(argv[i+1],NULL); + if(num>0) + arg_return->windowid=num; + else{ + fprintf(stderr,"Argument Usage: -windowid id_of_window(number)\n"); + return 1; + } + } + else{ + fprintf(stderr,"Argument Usage: -windowid id_of_window(number)\n"); + return 1; + } + i++; + } + else if(!strcmp(argv[i],"-display")){ + if(i+1<argc){ + realloc(arg_return->display,strlen(argv[i+1])+1); + strcpy(arg_return->display,argv[i+1]); + } + else{ + fprintf(stderr,"Argument Usage: -display DISPLAY\n"); + return 1; + } + i++; + } + else if(!strcmp(argv[i],"-x")){ + if(i+1<argc){ + int num=atoi(argv[i+1]); + if(num>0) + arg_return->x=num; + else{ + fprintf(stderr,"Argument Usage: -x X(number>0)\n"); + return 1; + } + } + else{ + fprintf(stderr,"Argument Usage: -x X(number>0)\n"); + return 1; + } + i++; + } + else if(!strcmp(argv[i],"-y")){ + if(i+1<argc){ + int num=atoi(argv[i+1]); + if(num>0) + arg_return->y=num; + else{ + fprintf(stderr,"Argument Usage: -y Y(number>0)\n"); + return 1; + } + } + else{ + fprintf(stderr,"Argument Usage: -y Y(number>0)\n"); + return 1; + } + i++; + } + else if(!strcmp(argv[i],"-width")){ + if(i+1<argc){ + int num=atoi(argv[i+1]); + if(num>0) + arg_return->width=num; + else{ + fprintf(stderr,"Argument Usage: -width N(number>0)\n"); + return 1; + } + } + else{ + fprintf(stderr,"Argument Usage: -width N(number>0)\n"); + return 1; + } + i++; + } + else if(!strcmp(argv[i],"-height")){ + if(i+1<argc){ + int num=atoi(argv[i+1]); + if(num>0) + arg_return->height=num; + else{ + fprintf(stderr,"Argument Usage: -height N(number>0)\n"); + return 1; + } + } + else{ + fprintf(stderr,"Argument Usage: -height N(number>0)\n"); + return 1; + } + i++; + } + else if(!strcmp(argv[i],"-o")){ + if(i+1<argc){ + realloc(arg_return->filename,strlen(argv[i+1])+1); + strcpy(arg_return->filename,argv[i+1]); + } + else{ + fprintf(stderr,"Argument Usage: -o filename\n"); + return 1; + } + i++; + } + else if(!strcmp(argv[i],"-fps")){ + if(i+1<argc){ + float num=atof(argv[i+1]); + if(num>0.0) + arg_return->fps=num; + else{ + fprintf(stderr,"Argument Usage: -fps N(number>0)\n"); + return 1; + } + } + else{ + fprintf(stderr,"Argument Usage: -fps N(number>0)\n"); + return 1; + } + i++; + } + else if(!strcmp(argv[i],"-v_quality")){ + if(i+1<argc){ + int num=atoi(argv[i+1]); + if((num>=0)&&(num<64)) + arg_return->v_quality=num; + else{ + fprintf(stderr,"Argument Usage: -v_quality n(number 0-63)\n"); + return 1; + } + } + else{ + fprintf(stderr,"Argument Usage: -v_quality n(number 0-63)\n"); + return 1; + } + i++; + } + else if(!strcmp(argv[i],"-v_bitrate")){ + if(i+1<argc){ + int num=atoi(argv[i+1]); + if((num>=45000)&&(num<=2000000)) + arg_return->v_bitrate=num; + else{ + fprintf(stderr,"Argument Usage: -v_bitrate n(number 45000-2000000)\n"); + return 1; + } + } + else{ + fprintf(stderr,"Argument Usage: -v_bitrate n(number 45000-2000000)\n"); + return 1; + } + i++; + } + else if(!strcmp(argv[i],"-dummy-cursor")){ + if(i+1<argc){ + if(!strcmp(argv[i+1],"white")) + arg_return->cursor_color=0; + else if(!strcmp(argv[i+1],"black")) + arg_return->cursor_color=1; + else{ + fprintf(stderr,"Argument Usage: -dummy-cursor [black|white](default black)\n"); + return 1; + } + } + else{ + fprintf(stderr,"Argument Usage: -dummy-cursor [black|white](default black)\n"); + return 1; + } + i++; + } + else if(!strcmp(argv[i],"--no-dummy-cursor")) + arg_return->have_dummy_cursor=0; + else if(!strcmp(argv[i],"-freq")){ + if(i+1<argc){ + int num=atoi(argv[i+1]); + if(num>0) + arg_return->frequency=num; + else{ + fprintf(stderr,"Argument Usage: -freq N(number>0)\n"); + return 1; + } + } + else{ + fprintf(stderr,"Argument Usage: -freq N(number>0)\n"); + return 1; + } + i++; + } + else if(!strcmp(argv[i],"-channels")){ + if(i+1<argc){ + int num=atoi(argv[i+1]); + if(num>0) + arg_return->channels=num; + else{ + fprintf(stderr,"Argument Usage: -channels N(number>0)\n"); + return 1; + } + } + else{ + fprintf(stderr,"Argument Usage: -channels N(number>0)\n"); + return 1; + } + i++; + } + else if(!strcmp(argv[i],"-s_quality")){ + if(i+1<argc){ + int num=atoi(argv[i+1]); + if((num>=-1)&&(num<=10)) + arg_return->s_quality=num; + else{ + fprintf(stderr,"Argument Usage: -s_quality n(number -1 to 10)\n"); + return 1; + } + } + else{ + fprintf(stderr,"Argument Usage: -s_quality n(number -1 to 10)\n"); + return 1; + } + i++; + } + else if(!strcmp(argv[i],"-scale-shot")){ + if(i+1<argc){ + int num=atoi(argv[i+1]); + if((num==1)||(num==2)||(num==4)||(num==8) + ||(num==16)||(num==32)||(num==64)){ + arg_return->scale_shot=num; + } + else{ + fprintf(stderr,"Argument Usage: -scale-shot N(0<number<64,power of 2)\n"); + return 1; + } + } + else{ + fprintf(stderr,"Argument Usage: -scale-shot N(0<number<64,power of 2)\n"); + return 1; + } + i++; + } + else if(!strcmp(argv[i],"-device")){ + if(i+1<argc){ + realloc(arg_return->device,strlen(argv[i+1])+1); + strcpy(arg_return->device,argv[i+1]); + } + else{ + fprintf(stderr,"Argument Usage: -device SOUND_DEVICE\n"); + return 1; + } + i++; + } + else if(!strcmp(argv[i],"--nosound")) + arg_return->nosound=1; + else if(!strcmp(argv[i],"--with-shared")) + arg_return->noshared=0; + else if(!strcmp(argv[i],"--full-shots")) + arg_return->full_shots=1; + else if(!strcmp(argv[i],"--scshot")) + arg_return->scshot=1; + else if(!strcmp(argv[i],"--help")||!strcmp(argv[i],"-h")){ + fprintf(stderr,"%s",usage); + return 1; + } + else if(!strcmp(argv[i],"--version")){ + fprintf(stderr,"recordMyDesktop v%s\n\n",VERSION); + return 1; + } + else{ + fprintf(stderr,"\n\tError parsing arguments.\n\tType --help or -h for usage.\n\n"); + return 1; + } + } + return 0; +} diff --git a/recordmydesktop/src/poll_damage.c b/recordmydesktop/src/poll_damage.c new file mode 100644 index 0000000..9daef6b --- /dev/null +++ b/recordmydesktop/src/poll_damage.c @@ -0,0 +1,57 @@ +/********************************************************************************* +* recordMyDesktop * +********************************************************************************** +* * +* Copyright (C) 2006 John Varouhakis * +* * +* * +* 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 * +* * +* * +* * +* For further information contact me at biocrasher@gmail.com * +**********************************************************************************/ + + +#include <recordmydesktop.h> + +void *PollDamage(void *pdata){ + + Damage damage; + XEvent event; + int inserts=0; + + + damage= XDamageCreate( ((ProgData *)pdata)->dpy, ((ProgData *)pdata)->brwin.windowid, XDamageReportRawRectangles); + while(((ProgData *)pdata)->running){ +// if(Paused)pthread_cond_wait(&((ProgData *)pdata)->pause_cond,&((ProgData *)pdata)->pause_cond_mutex); + //damage polling doesn't stop,eventually full image may be needed + XNextEvent(((ProgData *)pdata)->dpy,&event); + if(event.type == ((ProgData *)pdata)->damage_event + XDamageNotify ){ + XDamageNotifyEvent *e =(XDamageNotifyEvent *)( &event ); + WGeometry wgeom; + CLIP_EVENT_AREA(e,&(((ProgData *)pdata)->brwin),&wgeom); + if((wgeom.x>=0)&&(wgeom.y>=0)&&(wgeom.width>0)&&(wgeom.height>0)) + { + int tlist_sel=((ProgData *)pdata)->list_selector; + pthread_mutex_lock(&((ProgData *)pdata)->list_mutex[tlist_sel]); + inserts+=RectInsert(&((ProgData *)pdata)->rect_root[tlist_sel],&wgeom); + pthread_mutex_unlock(&((ProgData *)pdata)->list_mutex[tlist_sel]); + } + } + } + pthread_exit(&errno); +} + diff --git a/recordmydesktop/src/queryextensions.c b/recordmydesktop/src/queryextensions.c new file mode 100644 index 0000000..1107fab --- /dev/null +++ b/recordmydesktop/src/queryextensions.c @@ -0,0 +1,40 @@ +/********************************************************************************* +* recordMyDesktop * +********************************************************************************** +* * +* Copyright (C) 2006 John Varouhakis * +* * +* * +* 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 * +* * +* * +* * +* For further information contact me at biocrasher@gmail.com * +**********************************************************************************/ + + +#include <recordmydesktop.h> + +int QueryExtensions(Display *dpy,ProgArgs *args,int *damage_event,int *damage_error){ + if(!XDamageQueryExtension( dpy, damage_event, damage_error)){ + fprintf(stderr,"XDamage extension not found!!!\n"); + return 1; + } + if((!args->noshared)&&(XShmQueryExtension(dpy)==False)){ + args->noshared=1; + fprintf(stderr,"Shared Memory extension not present!\nContinuing without it.\n"); + } + return 0; +} diff --git a/recordmydesktop/src/recordmydesktop.c b/recordmydesktop/src/recordmydesktop.c new file mode 100644 index 0000000..f0f1a3d --- /dev/null +++ b/recordmydesktop/src/recordmydesktop.c @@ -0,0 +1,215 @@ +/********************************************************************************* +* recordMyDesktop * +********************************************************************************** +* * +* Copyright (C) 2006 John Varouhakis * +* * +* * +* 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 * +* * +* * +* * +* For further information contact me at biocrasher@gmail.com * +**********************************************************************************/ + + +#include <recordmydesktop.h> + + +int main(int argc,char **argv){ + ProgData pdata; + + if(XInitThreads ()==0){ + fprintf(stderr,"Couldn't initialize thread support!\n"); + exit(1); + } + DEFAULT_ARGS(&pdata.args); + if(ParseArgs(argc,argv,&pdata.args)){ + exit(1); + } + pdata.dpy = XOpenDisplay(pdata.args.display); + + if (pdata.dpy == NULL) { + fprintf(stderr, "Cannot connect to X server %s\n",pdata.args.display); + exit(1); + } + else{ + EncData enc_data; + pthread_t poll_damage_t, + image_capture_t, + image_encode_t, + sound_capture_t, + sound_encode_t, + flush_to_ogg_t; + XShmSegmentInfo shminfo; + + QUERY_DISPLAY_SPECS(pdata.dpy,&pdata.specs); + if(pdata.specs.depth!=24){ + fprintf(stderr,"Only 24bpp color depth mode is currently supported.\n"); + exit(1); + } + if(SetBRWindow(pdata.dpy,&pdata.brwin,&pdata.specs,&pdata.args)) + exit(1); + if(QueryExtensions(pdata.dpy,&pdata.args,&pdata.damage_event, &pdata.damage_error)) + exit(1); + + //init data + + if(!pdata.args.scshot){ + fprintf(stderr,"Initializing...\n"); + + if(pdata.args.have_dummy_cursor){ + pdata.dummy_pointer=MakeDummyPointer(&pdata.specs,16,pdata.args.cursor_color,0,&pdata.npxl); + pdata.dummy_p_size=16; + } + } + if((pdata.args.noshared)||(pdata.args.scshot)) + pdata.datamain=(char *)malloc(pdata.brwin.nbytes); + if(!pdata.args.scshot){ + if(pdata.args.noshared) + pdata.datatemp=(char *)malloc(pdata.brwin.nbytes); + pdata.rect_root[0]=pdata.rect_root[1]=NULL; + pthread_mutex_init(&pdata.list_mutex[0],NULL); + pthread_mutex_init(&pdata.list_mutex[1],NULL); + pthread_mutex_init(&pdata.sound_buffer_mutex,NULL); + pthread_mutex_init(&pdata.yuv_mutex,NULL); + + pthread_cond_init(&pdata.time_cond,NULL); + pthread_cond_init(&pdata.pause_cond,NULL); + pthread_cond_init(&pdata.image_buffer_ready,NULL); + pthread_cond_init(&pdata.sound_buffer_ready,NULL); + pthread_cond_init(&pdata.sound_data_read,NULL); + pdata.list_selector=Paused=Aborted=avd=0; + pdata.running=1; + time_cond=&pdata.time_cond; + pause_cond=&pdata.pause_cond; + Running=&pdata.running; + } + if((pdata.args.noshared)||(pdata.args.scshot)){ + pdata.image=XCreateImage(pdata.dpy, pdata.specs.visual, pdata.specs.depth, ZPixmap, 0,pdata.datamain,pdata.brwin.rgeom.width, + pdata.brwin.rgeom.height, 8, 0); + XInitImage(pdata.image); + GetZPixmap(pdata.dpy,pdata.specs.root,pdata.image->data,pdata.brwin.rgeom.x,pdata.brwin.rgeom.y, + pdata.brwin.rgeom.width,pdata.brwin.rgeom.height); + } + else{ + pdata.image=XShmCreateImage (pdata.dpy,pdata.specs.visual,pdata.specs.depth,ZPixmap,pdata.datamain, + &shminfo, pdata.brwin.rgeom.width,pdata.brwin.rgeom.height); + shminfo.shmid = shmget (IPC_PRIVATE, + pdata.image->bytes_per_line * pdata.image->height, + IPC_CREAT|0777); + shminfo.shmaddr = pdata.image->data = shmat (shminfo.shmid, 0, 0); + shminfo.readOnly = False; + if(!XShmAttach(pdata.dpy,&shminfo)){ + fprintf(stderr,"Failed to attach shared memory to proccess.\n"); + exit(1); + } + XShmGetImage(pdata.dpy,pdata.specs.root,pdata.image,0,0,AllPlanes); + + } + if(pdata.args.scshot){ + if(pdata.args.delay>0){ + fprintf(stderr,"Will sleep for %d seconds now.\n",pdata.args.delay); + sleep(pdata.args.delay); + } + //get a new screenshot + GetZPixmap(pdata.dpy,pdata.specs.root,pdata.image->data,pdata.brwin.rgeom.x,pdata.brwin.rgeom.y, + pdata.brwin.rgeom.width,pdata.brwin.rgeom.height); + ZPixmapToBMP(pdata.image,&pdata.brwin,((!strcmp(pdata.args.filename,"out.ogg"))?"rmdout.bmp":pdata.args.filename),pdata.brwin.nbytes,pdata.args.scale_shot); + fprintf(stderr,"done!\n"); + exit(0); + } + if(!pdata.args.nosound) + pdata.sound_handle=OpenDev(pdata.args.device,pdata.args.channels,&pdata.args.frequency,&pdata.periodsize, &pdata.periodtime,&pdata.hard_pause); + if(pdata.sound_handle==NULL){ + fprintf(stderr,"Error while opening/configuring soundcard %s\nProcceeding with no sound\n",pdata.args.device); + pdata.args.nosound=1; + } + InitEncoder(&pdata,&enc_data); + XImageToYUV(pdata.image,&pdata.enc_data->yuv); + + pdata.frametime=(1000000)/pdata.args.fps; + + if(pdata.args.delay>0){ + fprintf(stderr,"Will sleep for %d seconds now.\n",pdata.args.delay); + sleep(pdata.args.delay); + } + + /*start threads*/ + if(!pdata.args.full_shots) + pthread_create(&poll_damage_t,NULL,PollDamage,(void *)&pdata); + pthread_create(&image_capture_t,NULL,GetFrame,(void *)&pdata); + pthread_create(&image_encode_t,NULL,EncodeImageBuffer,(void *)&pdata); + if(!pdata.args.nosound){ + pthread_create(&sound_capture_t,NULL,CaptureSound,(void *)&pdata); + pthread_create(&sound_encode_t,NULL,EncodeSoundBuffer,(void *)&pdata); + } + pthread_create(&flush_to_ogg_t,NULL,FlushToOgg,(void *)&pdata); + + + RegisterCallbacks(&pdata.args); + fprintf(stderr,"Capturing!\n"); + + //wait all threads to finish + + pthread_join(image_capture_t,NULL); + fprintf(stderr,"Shutting down."); + pthread_join(image_encode_t,NULL); + fprintf(stderr,"."); + if(!pdata.args.nosound){ + pthread_join(sound_capture_t,NULL); + fprintf(stderr,"."); + pthread_join(sound_encode_t,NULL); + fprintf(stderr,"."); + } + else + fprintf(stderr,".."); + pthread_join(flush_to_ogg_t,NULL); + fprintf(stderr,"."); + if(!pdata.args.full_shots) + pthread_join(poll_damage_t,NULL); + fprintf(stderr,"."); + fprintf(stderr,"\n"); + XCloseDisplay(pdata.dpy); + if(Aborted){ + if(remove(pdata.args.filename)){ + perror("Error while removing file:\n"); + return 1; + } + else{ + fprintf(stderr,"SIGABRT received,file %s removed\n",pdata.args.filename); + return 0; + } + } + else + fprintf(stderr,"Goodbye!\n"); + } + return 0; +} + + + + + + + + + + + + + + + diff --git a/recordmydesktop/src/rectinsert.c b/recordmydesktop/src/rectinsert.c new file mode 100644 index 0000000..4caf1b8 --- /dev/null +++ b/recordmydesktop/src/rectinsert.c @@ -0,0 +1,469 @@ +/********************************************************************************* +* recordMyDesktop * +********************************************************************************** +* * +* Copyright (C) 2006 John Varouhakis * +* * +* * +* 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 * +* * +* * +* * +* For further information contact me at biocrasher@gmail.com * +**********************************************************************************/ + + +#include <recordmydesktop.h> +//return 1 and null if geom1 in geom2 ,2 and null if geom2 in geom1,0 if they don't collide +//-1 and two geoms if they collide and geom1 is broken.//-2 and one or two geoms if they collide and geom2 is broken. +//-10 if group and replace is possible +int CollideRects(WGeometry *wgeom1,WGeometry *wgeom2,WGeometry **wgeom_return,int *ngeoms){ + //1 fits in 2 + if((wgeom1->x>=wgeom2->x)&& + (wgeom1->x+wgeom1->width<=wgeom2->x+wgeom2->width)&& + (wgeom1->y>=wgeom2->y)&& + (wgeom1->y+wgeom1->height<=wgeom2->y+wgeom2->height)){ + *ngeoms=0; + return 1; + } + //2 fits in 1 + else if((wgeom2->x>=wgeom1->x)&& + (wgeom2->x+wgeom2->width<=wgeom1->x+wgeom1->width)&& + (wgeom2->y>=wgeom1->y)&& + (wgeom2->y+wgeom2->height<=wgeom1->y+wgeom1->height)){ + *ngeoms=0; + return 2; + } + //no collision + else if((wgeom1->x+wgeom1->width<wgeom2->x)|| + (wgeom2->x+wgeom2->width<wgeom1->x)|| + (wgeom1->y+wgeom1->height<wgeom2->y)|| + (wgeom2->y+wgeom2->height<wgeom1->y)){ + *ngeoms=0; + return 0; + } + else{ +//overlapping points are considered enclosed +//this happens because libxdamage may generate many events for one change +//and some of them may be in the the exact same region +//so identical rects would be considered not colliding +//in order though to avoid endless recursion on the RectInsert +//function should always start at the next element(which is logical since +//if any rect makes it to a points none of it's part collides with previous +//nodes on the list, too) + int x1[2]={wgeom1->x,wgeom1->x+wgeom1->width}; + int y1[2]={wgeom1->y,wgeom1->y+wgeom1->height}; + int x2[2]={wgeom2->x,wgeom2->x+wgeom2->width}; + int y2[2]={wgeom2->y,wgeom2->y+wgeom2->height}; + int enclosed[2][4],tot1,tot2; + enclosed[0][0]=(((x1[0]>=x2[0])&&(x1[0]<=x2[1])&& + (y1[0]>=y2[0])&&(y1[0]<=y2[1]))?1:0); + enclosed[0][1]=(((x1[1]>=x2[0])&&(x1[1]<=x2[1])&& + (y1[0]>=y2[0])&&(y1[0]<=y2[1]))?1:0); + enclosed[0][2]=(((x1[0]>=x2[0])&&(x1[0]<=x2[1])&& + (y1[1]>=y2[0])&&(y1[1]<=y2[1]))?1:0); + enclosed[0][3]=(((x1[1]>=x2[0])&&(x1[1]<=x2[1])&& + (y1[1]>=y2[0])&&(y1[1]<=y2[1]))?1:0); + enclosed[1][0]=(((x2[0]>=x1[0])&&(x2[0]<=x1[1])&& + (y2[0]>=y1[0])&&(y2[0]<=y1[1]))?1:0); + enclosed[1][1]=(((x2[1]>=x1[0])&&(x2[1]<=x1[1])&& + (y2[0]>=y1[0])&&(y2[0]<=y1[1]))?1:0); + enclosed[1][2]=(((x2[0]>=x1[0])&&(x2[0]<=x1[1])&& + (y2[1]>=y1[0])&&(y2[1]<=y1[1]))?1:0); + enclosed[1][3]=(((x2[1]>=x1[0])&&(x2[1]<=x1[1])&& + (y2[1]>=y1[0])&&(y2[1]<=y1[1]))?1:0); + tot1=enclosed[0][0]+enclosed[0][1]+enclosed[0][2]+enclosed[0][3]; + tot2=enclosed[1][0]+enclosed[1][1]+enclosed[1][2]+enclosed[1][3]; + if((tot1==2)&&(tot2==2)){//same width or height, which is the best case + //group + if((enclosed[1][0]&&enclosed[1][1])&&(wgeom1->width==wgeom2->width)){ + wgeom_return[0]=(WGeometry *)malloc(sizeof(WGeometry)); + *ngeoms=1; + wgeom_return[0]->x=wgeom1->x; + wgeom_return[0]->y=wgeom1->y; + wgeom_return[0]->width=wgeom1->width; + wgeom_return[0]->height=wgeom2->height+wgeom2->y-wgeom1->y; + return -10; + } + else if((enclosed[1][0]&&enclosed[1][2])&&(wgeom1->height==wgeom2->height)){ +// wgeom_return[0]=(WGeometry *)malloc(sizeof(WGeometry)); + *ngeoms=1; + wgeom_return[0]->x=wgeom1->x; + wgeom_return[0]->y=wgeom1->y; + wgeom_return[0]->width=wgeom2->width+wgeom2->x-wgeom1->x; + wgeom_return[0]->height=wgeom1->height; + return -10; + } + else if((enclosed[1][3]&&enclosed[1][1])&&(wgeom1->height==wgeom2->height)){ +// wgeom_return[0]=(WGeometry *)malloc(sizeof(WGeometry)); + *ngeoms=1; + wgeom_return[0]->x=wgeom2->x; + wgeom_return[0]->y=wgeom2->y; + wgeom_return[0]->width=wgeom1->width+wgeom1->x-wgeom2->x; + wgeom_return[0]->height=wgeom2->height; + return -10; + } + else if((enclosed[1][3]&&enclosed[1][2])&&(wgeom1->width==wgeom2->width)){ +// wgeom_return[0]=(WGeometry *)malloc(sizeof(WGeometry)); + *ngeoms=1; + wgeom_return[0]->x=wgeom2->x; + wgeom_return[0]->y=wgeom2->y; + wgeom_return[0]->width=wgeom2->width; + wgeom_return[0]->height=wgeom1->height+wgeom1->y-wgeom2->y; + return -10; + } + //if control reaches here therewasn't a group and we go on + } + if(tot2==2){ + //break geom2 +// wgeom_return[0]=(WGeometry *)malloc(sizeof(WGeometry)); + wgeom_return[0]->x=wgeom2->x; + wgeom_return[0]->y=wgeom2->y; + wgeom_return[0]->width=wgeom2->width; + wgeom_return[0]->height=wgeom2->height; + *ngeoms=1; + if(enclosed[1][0]&&enclosed[1][1]){ + wgeom_return[0]->y=y1[1]; + wgeom_return[0]->height-=y1[1]-y2[0]; + } + else if(enclosed[1][0]&&enclosed[1][2]){ + wgeom_return[0]->x=x1[1]; + wgeom_return[0]->width-=x1[1]-x2[0]; + } + else if(enclosed[1][3]&&enclosed[1][1]) + wgeom_return[0]->width-=x2[1]-x1[0]; + else if(enclosed[1][3]&&enclosed[1][2]) + wgeom_return[0]->height-=y2[1]-y1[0]; + return -2; + } + else if(tot1==2){ + //if the first one breaks(which is already inserted) + //then we reenter the part that was left and the one + //that was to be inserted + wgeom_return[1]=wgeom2; +// wgeom_return[0]=(WGeometry *)malloc(sizeof(WGeometry)); + wgeom_return[0]->x=wgeom1->x; + wgeom_return[0]->y=wgeom1->y; + wgeom_return[0]->width=wgeom1->width; + wgeom_return[0]->height=wgeom1->height; + *ngeoms=1; + if(enclosed[0][0]&&enclosed[0][1]){ + wgeom_return[0]->y=y2[1]; + wgeom_return[0]->height-=y2[1]-y1[0]; + } + else if(enclosed[0][0]&&enclosed[0][2]){ + wgeom_return[0]->x=x2[1]; + wgeom_return[0]->width-=x2[1]-x1[0]; + } + else if(enclosed[0][3]&&enclosed[0][1]) + wgeom_return[0]->width-=x1[1]-x2[0]; + else if(enclosed[0][3]&&enclosed[0][2]) + wgeom_return[0]->height-=y1[1]-y2[0]; + return -1; + + } + else if(tot2==1){//in which case there is also tot1==1 but we rather not break that + //break geom2 in two +// wgeom_return[0]=(WGeometry *)malloc(sizeof(WGeometry)); +// wgeom_return[1]=(WGeometry *)malloc(sizeof(WGeometry)); + *ngeoms=2; + if(enclosed[1][0]){ +//first + wgeom_return[0]->x=x1[1]; + wgeom_return[0]->y=y2[0]; + wgeom_return[0]->width=wgeom2->width-x1[1]+x2[0]; + wgeom_return[0]->height=wgeom2->height; +//second + wgeom_return[1]->x=x2[0]; + wgeom_return[1]->y=y1[1]; + wgeom_return[1]->width=x1[1]-x2[0]; + wgeom_return[1]->height=wgeom2->height-y1[1]+y2[0]; + } + else if(enclosed[1][1]){ +//first + wgeom_return[0]->x=x2[0]; + wgeom_return[0]->y=y2[0]; + wgeom_return[0]->width=wgeom2->width-x2[1]+x1[0]; + wgeom_return[0]->height=wgeom2->height; +//second + wgeom_return[1]->x=x1[0]; + wgeom_return[1]->y=y1[1]; + wgeom_return[1]->width=x2[1]-x1[0]; + wgeom_return[1]->height=wgeom2->height-y1[1]+y2[0]; + } + else if(enclosed[1][2]){ +//first(same as [1][0]) + wgeom_return[0]->x=x1[1]; + wgeom_return[0]->y=y2[0]; + wgeom_return[0]->width=wgeom2->width-x1[1]+x2[0]; + wgeom_return[0]->height=wgeom2->height; +//second + wgeom_return[1]->x=x2[0]; + wgeom_return[1]->y=y2[0]; + wgeom_return[1]->width=x1[1]-x2[0]; + wgeom_return[1]->height=wgeom2->height-y2[1]+y1[0]; + } + else if(enclosed[1][3]){ +//first(same as [1][1]) + wgeom_return[0]->x=x2[0]; + wgeom_return[0]->y=y2[0]; + wgeom_return[0]->width=wgeom2->width-x2[1]+x1[0]; + wgeom_return[0]->height=wgeom2->height; +//second + wgeom_return[1]->x=x1[0]; + wgeom_return[1]->y=y2[0]; + wgeom_return[1]->width=x2[1]-x1[0]; + wgeom_return[1]->height=wgeom2->height-y2[1]+y1[0]; + } + return -2; + } + else{//polygons collide but no point of one is in the other + //so we just keep the two parts of geom2 that are outside geom1 + + //break geom2 in two + //two cases: + //geom2 crossing vertically geom1 + //and geom2 crossing horizontally geom1 + //The proper one can be found by simply checking x,y positions +// wgeom_return[0]=(WGeometry *)malloc(sizeof(WGeometry)); +// wgeom_return[1]=(WGeometry *)malloc(sizeof(WGeometry)); + *ngeoms=2; + if(wgeom2->y<wgeom1->y){ + //common + wgeom_return[0]->x=wgeom_return[1]->x=x2[0]; + wgeom_return[0]->width=wgeom_return[1]->width=wgeom2->width; + + wgeom_return[0]->y=y2[0]; + wgeom_return[0]->height=wgeom2->height-y2[1]+y1[0]; + wgeom_return[1]->y=y1[1]; + wgeom_return[1]->height=y2[1]-y1[1]; + } + else{ + //common + wgeom_return[0]->y=wgeom_return[1]->y=y2[0]; + wgeom_return[0]->height=wgeom_return[1]->height=wgeom2->height; + + wgeom_return[0]->x=x2[0]; + wgeom_return[0]->width=wgeom2->width-x2[1]+x1[0]; + wgeom_return[1]->x=x1[1]; + wgeom_return[1]->width=x2[1]-x1[1]; + } + return -2; + } + } +} + +int RectInsert(RectArea **root,WGeometry *wgeom){ + + int total_insertions=0; + RectArea *temp,*newnode=(RectArea *)malloc(sizeof(RectArea)); + newnode->geom.x=wgeom->x; + newnode->geom.y=wgeom->y; + newnode->geom.width=wgeom->width; + newnode->geom.height=wgeom->height; + newnode->prev=newnode->next=NULL; + if(*root==NULL){ + *root=newnode; + total_insertions=1; + } + else{ + WGeometry *wgeom_return[2]; + wgeom_return[0]=(WGeometry *)malloc(sizeof(WGeometry)); + wgeom_return[1]=(WGeometry *)malloc(sizeof(WGeometry)); + + int ngeoms=0,insert_ok=1,i=0; + temp=*root; + while(insert_ok){//if something is broken list does not procceed(except on -1 collres case) + int collres/*=0;//*/=CollideRects(&(temp->geom),wgeom,wgeom_return,&ngeoms); + if((!collres)) + insert_ok=1; + else{ + insert_ok=0; + switch(collres){ + case 1://remove current node,reinsert new one + total_insertions--; + if(temp->prev!=NULL){//no root + if(temp->next!=NULL){// + temp->prev->next=temp->next; + temp->next->prev=temp->prev; + temp=temp->next; + if((wgeom->width>0)&&(wgeom->height>0)) + total_insertions+=RectInsert(&temp,wgeom); + } + else{ + temp->prev->next=newnode; + newnode->prev=temp->prev; + total_insertions++; + free(temp); + } + } + else{//root + if((*root)->next!=NULL){ + (*root)=(*root)->next; + (*root)->prev=NULL; + if((wgeom->width>0)&&(wgeom->height>0)) + total_insertions+=RectInsert(root,wgeom); + } + else if((wgeom->width>0)&&(wgeom->height>0)){ + *root=newnode; + total_insertions++; + } + else{ + *root=NULL; + total_insertions++; + } + free(temp); + } + break; + case 2://done,area is already covered + break; + case -1://current node is broken and reinserted(in same pos) + //newnode is also reinserted + if((wgeom_return[0]->width>0)&&(wgeom_return[0]->height>0)){ + temp->geom.x=wgeom_return[0]->x; + temp->geom.y=wgeom_return[0]->y; + temp->geom.width=wgeom_return[0]->width; + temp->geom.height=wgeom_return[0]->height; + if(temp->next==NULL){ + temp->next=newnode; + newnode->prev=temp; + total_insertions++; + } + else{ + insert_ok=1; + } + } + else{//it might happen that the old and now broken node + //is of zero width or height(so it isn't reinserted) + /*TODO this may cause lines to be left not updated + so maybe it is needed to increase the size by one + pixel(zero width or height cause BadValue*/ + if((temp->prev==NULL)&&(temp->next!=NULL)){ + *root=(*root)->next; + (*root)->prev=NULL; + } + else if((temp->next==NULL)&&(temp->prev!=NULL)){ + temp->prev->next=newnode; + newnode->prev=temp->prev; + } + else if((temp->next==NULL)&&(temp->prev==NULL)) + (*root)=newnode; + else{ + total_insertions--; + temp->next->prev=temp->prev; + temp->prev->next=temp->next; + total_insertions+=RectInsert(&temp->next,wgeom); + } + } + break; + case -2://new is broken and reinserted + if(temp->next==NULL){ + total_insertions+=ngeoms; + newnode->geom.x=wgeom_return[0]->x; + newnode->geom.y=wgeom_return[0]->y; + newnode->geom.width=wgeom_return[0]->width; + newnode->geom.height=wgeom_return[0]->height; + temp->next=newnode; + newnode->prev=temp; + if(ngeoms>1){ + RectArea *newnode1=(RectArea *)malloc(sizeof(RectArea)); + newnode1->geom.x=wgeom_return[1]->x; + newnode1->geom.y=wgeom_return[1]->y; + newnode1->geom.width=wgeom_return[1]->width; + newnode1->geom.height=wgeom_return[1]->height; + newnode->next=newnode1; + newnode1->prev=newnode; + newnode1->next=NULL; + } + } + else{ + for(i=0;i<ngeoms;i++){ + if((wgeom_return[i]->width>0)&&(wgeom_return[i]->height>0)) + total_insertions+=RectInsert(&temp->next,wgeom_return[i]); + } + } + break; + case -10://grouped + if(temp->prev==NULL){ + if(temp->next==NULL){//empty list + newnode->geom.x=wgeom_return[0]->x; + newnode->geom.y=wgeom_return[0]->y; + newnode->geom.width=wgeom_return[0]->width; + newnode->geom.height=wgeom_return[0]->height; + *root=newnode; + } + else{ + total_insertions--; + *root=temp->next; + (*root)->prev=NULL; + free(temp); + if((wgeom_return[0]->width>0)&&(wgeom_return[0]->height>0)) + total_insertions+=RectInsert(root,wgeom_return[0]); + } + } + else if(temp->next==NULL){//last, enter anyway + newnode->geom.x=wgeom_return[0]->x; + newnode->geom.y=wgeom_return[0]->y; + newnode->geom.width=wgeom_return[0]->width; + newnode->geom.height=wgeom_return[0]->height; + temp->prev->next=newnode; + newnode->prev=temp->prev; + free(temp); + } + else{//remove node and reinsert, starting where we were + total_insertions--; + temp->prev->next=temp->next; + temp->next->prev=temp->prev; + temp=temp->next; + if((wgeom_return[0]->width>0)&&(wgeom_return[0]->height>0)) + total_insertions+=RectInsert(&temp,wgeom_return[0]); + } + break; + } + } + if(insert_ok){ + if(temp->next==NULL){ + temp->next=newnode; + newnode->prev=temp; + total_insertions++; + break; + } + else{ + temp=temp->next; + } + } + else{ + break; + } + }; + } + return total_insertions; +} + +void ClearList(RectArea **root){ + + RectArea *temp; + temp=*root; + if(temp!=NULL){ + while(temp->next!=NULL){ + temp=temp->next; + free(temp->prev); + + } + free(temp); + *root=NULL; + } +} + diff --git a/recordmydesktop/src/register_callbacks.c b/recordmydesktop/src/register_callbacks.c new file mode 100644 index 0000000..1bcffe8 --- /dev/null +++ b/recordmydesktop/src/register_callbacks.c @@ -0,0 +1,75 @@ +/********************************************************************************* +* recordMyDesktop * +********************************************************************************** +* * +* Copyright (C) 2006 John Varouhakis * +* * +* * +* 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 * +* * +* * +* * +* For further information contact me at biocrasher@gmail.com * +**********************************************************************************/ + + +#include <recordmydesktop.h> +void SetExpired(int signum){ + pthread_cond_broadcast(time_cond); +} + +void SetPaused(int signum){ + if(!Paused) + Paused=1; + else{ +// pthread_cond_broadcast(pause_cond);//thsi should work, but it doesn't + int i;//this is a bug + Paused=0;//normally with the broadcast all the threads should restart, but sound capture thread + for(i=0;i<15;i++)//remains dead. If a bunch of signals, restarts all threads, why can't a broadcast do the same? + pthread_cond_signal(pause_cond);//if you have any idea please contact me. + //(misses the signal?) + } +} + + +void SetRunning(int signum){ + *Running=0; + if(signum==SIGABRT) + Aborted=1; +} + +void RegisterCallbacks(ProgArgs *args){ + + struct itimerval value; + struct sigaction time_act,pause_act,end_act; + + + value.it_interval.tv_sec=value.it_value.tv_sec=0; + value.it_interval.tv_usec=value.it_value.tv_usec=(1000000)/args->fps; + setitimer(ITIMER_REAL,&value,NULL); + time_act.sa_handler=SetExpired; + pause_act.sa_handler=SetPaused; + end_act.sa_handler=SetRunning; + sigfillset(&(time_act.sa_mask)); + sigfillset(&(pause_act.sa_mask)); + sigfillset(&(end_act.sa_mask)); + time_act.sa_flags=pause_act.sa_flags=end_act.sa_flags=0; + sigaction(SIGALRM,&time_act,NULL); + sigaction(SIGUSR1,&pause_act,NULL); + sigaction(SIGINT,&end_act,NULL); + sigaction(SIGTERM,&end_act,NULL); + sigaction(SIGABRT,&end_act,NULL); +} + diff --git a/recordmydesktop/src/setbrwindow.c b/recordmydesktop/src/setbrwindow.c new file mode 100644 index 0000000..13e7de3 --- /dev/null +++ b/recordmydesktop/src/setbrwindow.c @@ -0,0 +1,90 @@ +/********************************************************************************* +* recordMyDesktop * +********************************************************************************** +* * +* Copyright (C) 2006 John Varouhakis * +* * +* * +* 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 * +* * +* * +* * +* For further information contact me at biocrasher@gmail.com * +**********************************************************************************/ + + +#include <recordmydesktop.h> + +int SetBRWindow(Display *dpy,BRWindow *brwin,DisplaySpecs *specs,ProgArgs *args){ + //before we start recording we have to make sure the ranges are valid + if(args->windowid==0){//root window + //first set it up + brwin->windowid=specs->root; + brwin->geom.x=brwin->geom.y=0; + brwin->geom.width=specs->width; + brwin->geom.height=specs->height; + brwin->rgeom.x=args->x; + brwin->rgeom.y=args->y; + brwin->rgeom.width=((args->width)?args->width:specs->width-brwin->rgeom.x); + brwin->rgeom.height=((args->height)?args->height:specs->height-brwin->rgeom.y); +// brwin->nbytes=(brwin->rgeom.width*brwin->rgeom.height*4); + //and then check validity + if((brwin->rgeom.x+brwin->rgeom.width>specs->width)|| + (brwin->rgeom.y+brwin->rgeom.height>specs->height)){ + fprintf(stderr,"Window size specification out of bounds!(current resolution:%dx%d)\n",specs->width,specs->height); + return 1; + } + } + else{ + Window wchid; + int transl_x,transl_y; + + XWindowAttributes attribs; + XGetWindowAttributes(dpy,args->windowid,&attribs); + if((attribs.map_state==IsUnviewable)||(attribs.map_state==IsUnmapped)){ + fprintf(stderr,"Window must be mapped and visible!\n"); + return 1; + } + XTranslateCoordinates(dpy,specs->root,args->windowid,attribs.x,attribs.y,&transl_x,&transl_y,&wchid); + brwin->windowid=specs->root; + brwin->geom.x=attribs.x-transl_x; + brwin->geom.y=attribs.y-transl_y; + brwin->geom.width=attribs.width; + brwin->geom.height=attribs.height; + if((brwin->geom.x+brwin->geom.width>specs->width)|| + (brwin->geom.y+brwin->geom.height>specs->height)){ + fprintf(stderr,"Window must be on visible screen area!\n"); + return 1; + } + + brwin->rgeom.x=brwin->geom.x+args->x; + brwin->rgeom.y=brwin->geom.y+args->y; + brwin->rgeom.width=((args->width)?args->width:brwin->geom.width-args->x); + brwin->rgeom.height=((args->height)?args->height:brwin->geom.height-args->y); + if((args->x+brwin->rgeom.width>brwin->geom.width)|| + (args->y+brwin->rgeom.height>brwin->geom.height)){ + fprintf(stderr,"Specified Area is larger than window!\n"); + return 1; + } + } + //this is needed for theora + //+-8 pixels + brwin->rgeom.width=((((brwin->rgeom.width+16-(brwin->rgeom.width)%16+brwin->rgeom.x)<=specs->width)&&((brwin->rgeom.width)%16>8))?brwin->rgeom.width+16-(brwin->rgeom.width)%16:brwin->rgeom.width-(brwin->rgeom.width)%16); + brwin->rgeom.height=((((brwin->rgeom.height+16-(brwin->rgeom.height)%16+brwin->rgeom.y)<=specs->height)&&((brwin->rgeom.height)%16>8))?brwin->rgeom.height+16-(brwin->rgeom.height)%16:brwin->rgeom.height-(brwin->rgeom.height)%16); + + brwin->nbytes=(brwin->rgeom.width*brwin->rgeom.height*4); + + return 0; +} diff --git a/recordmydesktop/src/update_image.c b/recordmydesktop/src/update_image.c new file mode 100644 index 0000000..04cb16f --- /dev/null +++ b/recordmydesktop/src/update_image.c @@ -0,0 +1,72 @@ +/********************************************************************************* +* recordMyDesktop * +********************************************************************************** +* * +* Copyright (C) 2006 John Varouhakis * +* * +* * +* 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 * +* * +* * +* * +* For further information contact me at biocrasher@gmail.com * +**********************************************************************************/ + + +#include <recordmydesktop.h> + +void UpdateImage(Display * dpy, + yuv_buffer *yuv, + pthread_mutex_t *yuv_mutex, + DisplaySpecs *specs, + RectArea **root, + BRWindow *brwin, + char *datatemp, + int noshmem){ + RectArea *temp; + unsigned char *dtap=(unsigned char*)datatemp; + temp=*root; + + if(temp!=NULL){ + do{ + if(noshmem){ + GetZPixmap( dpy, + specs->root, + datatemp, + temp->geom.x, + temp->geom.y, + temp->geom.width, + temp->geom.height); + + pthread_mutex_lock(yuv_mutex); + + UPDATE_YUV_BUFFER_IM(yuv,dtap, + (temp->geom.x-brwin->rgeom.x),(temp->geom.y-brwin->rgeom.y), + (temp->geom.width),(temp->geom.height)); + + pthread_mutex_unlock(yuv_mutex); + } + else{ + UPDATE_YUV_BUFFER_SH(yuv,dtap, + (temp->geom.x-brwin->rgeom.x),(temp->geom.y-brwin->rgeom.y), + (temp->geom.width),(temp->geom.height)); + + + } + temp=temp->next; + }while(temp!=NULL); + } +} + diff --git a/recordmydesktop/src/zpixmaptobmp.c b/recordmydesktop/src/zpixmaptobmp.c new file mode 100644 index 0000000..21068c8 --- /dev/null +++ b/recordmydesktop/src/zpixmaptobmp.c @@ -0,0 +1,78 @@ +/********************************************************************************* +* recordMyDesktop * +********************************************************************************** +* * +* Copyright (C) 2006 John Varouhakis * +* * +* * +* 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 * +* * +* * +* * +* For further information contact me at biocrasher@gmail.com * +**********************************************************************************/ + + +#include <recordmydesktop.h> + +int ZPixmapToBMP(XImage *imgz,BRWindow *brwin,char *fname,int nbytes,int scale){ + FILE *fpbmp; + int i,k; + int siz=52+nbytes/(pow(scale,2)); + int offs=54+1024; + short int rsrvd=0; + int hsiz=40; + int width=brwin->rgeom.width/scale,height=brwin->rgeom.height/scale,nbts=nbytes/(pow(scale,2)); + unsigned short int planes=1; + unsigned short int bpp=24; + unsigned int cmpr=0; + unsigned int ncols=0; + char *dtap=imgz->data; + + /*Write header*/ + fpbmp=fopen(fname,"wb"); + fputc('B',fpbmp); + fputc('M',fpbmp); + fwrite(&siz,4,1,fpbmp); + fwrite(&rsrvd,2,1,fpbmp); + fwrite(&rsrvd,2,1,fpbmp); + fwrite(&offs,4,1,fpbmp); + fwrite(&hsiz,4,1,fpbmp); + fwrite(&(width),4,1,fpbmp); + fwrite(&(height),4,1,fpbmp); + fwrite(&planes,2,1,fpbmp); + fwrite(&bpp,2,1,fpbmp); + fwrite(&cmpr,4,1,fpbmp); + fwrite(&nbts,4,1,fpbmp); + fwrite(&(width),4,1,fpbmp); + fwrite(&(height),4,1,fpbmp); + fwrite(&(ncols),4,1,fpbmp); + fwrite(&(ncols),4,1,fpbmp); + for(i=0;i<1024;i++) + fputc(0,fpbmp); + /*Data*/ + for(k=(nbytes/imgz->bytes_per_line)-1;k>=0;k-=scale){ + for(i=0;i<imgz->bytes_per_line/4;i+=scale){ + fwrite(&dtap[(i*4)+k*(imgz->bytes_per_line)],1,1,fpbmp); + fwrite(&dtap[(i*4)+k*(imgz->bytes_per_line)+1],1,1,fpbmp); + fwrite(&dtap[(i*4)+k*(imgz->bytes_per_line)+2],1,1,fpbmp); + } + } + + fclose(fpbmp); + return 0; +} + + |