diff options
| -rw-r--r-- | recordmydesktop/ChangeLog | 40 | ||||
| -rw-r--r-- | recordmydesktop/README | 5 | ||||
| -rw-r--r-- | recordmydesktop/TODO | 5 | ||||
| -rw-r--r-- | recordmydesktop/configure.ac | 23 | ||||
| -rw-r--r-- | recordmydesktop/doc/recordmydesktop.1 | 96 | ||||
| -rw-r--r-- | recordmydesktop/include/recordmydesktop.h | 397 | ||||
| -rw-r--r-- | recordmydesktop/src/Makefile.am | 14 | ||||
| -rw-r--r-- | recordmydesktop/src/cache_audio.c (renamed from recordmydesktop/src/zpixmaptobmp.c) | 81 | ||||
| -rw-r--r-- | recordmydesktop/src/cache_frame.c | 193 | ||||
| -rw-r--r-- | recordmydesktop/src/encode_image_buffer.c | 20 | ||||
| -rw-r--r-- | recordmydesktop/src/encode_sound_buffer.c | 30 | ||||
| -rw-r--r-- | recordmydesktop/src/init_encoder.c | 70 | ||||
| -rw-r--r-- | recordmydesktop/src/load_cache.c | 187 | ||||
| -rw-r--r-- | recordmydesktop/src/parseargs.c | 64 | ||||
| -rw-r--r-- | recordmydesktop/src/poll_damage.c | 47 | ||||
| -rw-r--r-- | recordmydesktop/src/recordmydesktop.c | 211 | ||||
| -rw-r--r-- | recordmydesktop/src/register_callbacks.c | 10 | ||||
| -rw-r--r-- | recordmydesktop/src/rmd_cache.c | 102 | ||||
| -rw-r--r-- | recordmydesktop/src/wm_check.c | 68 | 
19 files changed, 1353 insertions, 310 deletions
diff --git a/recordmydesktop/ChangeLog b/recordmydesktop/ChangeLog index 3f5dc1e..bdd5f6f 100644 --- a/recordmydesktop/ChangeLog +++ b/recordmydesktop/ChangeLog @@ -1,14 +1,14 @@  /*VERSION 0.2.1*/  *Unupdated lines bug fixed.  *Fuzzy cursor at border bug fixed. -*Incorrect display of windows that  +*Incorrect display of windows that  extended beyond recording height fixed. -*Size adjustement that resulted in extra  +*Size adjustement that resulted in extra  or missing pixels at borders has been corrected.  New adjustment is 8 pixels max,split evenly if possible.  *The option to drop frames has been added.  *The option to do better subsampling has been added. -*Default behavior now is to use shared memory when  +*Default behavior now is to use shared memory when  size of image gets over 75% of total (or a user set threshold).  *Many other minor bugfixes. @@ -18,18 +18,18 @@ size of image gets over 75% of total (or a user set threshold).  *BGR to YUV convertion corrected.  /*VERSION 0.2.3*/ -*Problems on big-endian architectures, corrected  +*Problems on big-endian architectures, corrected  (thanks to Marcel Unbehaun for bringing up the problem  and providing the neccessary feedback to resolve it). -*Failure to respect change in channels number, while  +*Failure to respect change in channels number, while  opening soundcard, which resulted in crashes, segfaults  and repeated error-messages has been fixed.  *other minor bugfixes  /*VERSION 0.2.4*/ -*Behavior on lack of $DISPLAY evironment  +*Behavior on lack of $DISPLAY evironment  variable has been corrected. -*Size adjustment is now quantized at 16 pixels,  +*Size adjustment is now quantized at 16 pixels,  in order to avoid complete image distortion,  present at certain resolutions, when using shared memory  (conditional or not). @@ -39,12 +39,12 @@ values of options.  /*VERSION 0.2.5*/  *Drawing of the correct cursor is now supported through  the Xfixes extension. -*Change of behavior on lack of any Xserver extension and  +*Change of behavior on lack of any Xserver extension and  failure to open sound device. The program now exits and informs  instead of continuing with a different configuration.  *Exit status also indicates the nature of any error that occured.  *Documentation has been updated with exit status explanations. -*Subsampling of the chroma planes is made by averaging, while  +*Subsampling of the chroma planes is made by averaging, while  pixel discarding is an option(used to be otherwise). @@ -54,21 +54,27 @@ Now, instead of dropping sound buffers, image  capturing speeds up. That is, waiting for timer to  expire is disabled until sync is achieved.  When not recording sound, this does not apply. -*Theora return values are now properly checked,  +*Theora return values are now properly checked,  to avoid inserting corrupt packages into the stream.  *Major memory leaks have been fixed. These were related  to the rectangle insertion algorithm and discarding of  used sound buffers, -*Segmentation fault on seemingly random occasions has been  +*Segmentation fault on seemingly random occasions has been  found and is now fixed. Reason was lack of thread safety over  libogg calls. -/*VERSION 0.2.7*/ -*Opening of the sound device no longer hangs, if it isn't available. -Instead the program exits with the appropriate error code. -The same applies when reopening after a pause. - - +/*VERSION 0.3.0*/ +*Implemented cache functionality +(Encoding by default happens right after the recording) +*Corrected damage event handling. +*Saving under the same file-name,no longer deletes the previous one, +instead it saves under a number postfixed file. +*Opening the sound device doesn't block anymore. +*A few new options were added related to caching. +*Program now checks if it is run uder a known 3d compositing window manager +and set behavior accordingly, to reduce the need for extra options at launch. +*Updated documentation. +*Removed uneeded bitmap screenshot function. diff --git a/recordmydesktop/README b/recordmydesktop/README index 625990c..a64d5a5 100644 --- a/recordmydesktop/README +++ b/recordmydesktop/README @@ -1,3 +1,6 @@ -To see examples of usage and explanation of the options check the manpage in the doc directory. +This is the experimental branch of recordMyDesktop.  Changes that will  +take place here might render the program unusuable and/or unstable. +Any stable changes that influence the program positively and bugfixes +will be merged back into the main tree. diff --git a/recordmydesktop/TODO b/recordmydesktop/TODO index 139597f..62a2465 100644 --- a/recordmydesktop/TODO +++ b/recordmydesktop/TODO @@ -1,2 +1,3 @@ - - +1)write a TODO +2)? +3)Profit! diff --git a/recordmydesktop/configure.ac b/recordmydesktop/configure.ac index 4c6ae69..c8dae65 100644 --- a/recordmydesktop/configure.ac +++ b/recordmydesktop/configure.ac @@ -3,7 +3,7 @@  AC_PREREQ(2.59)  AC_INIT(src/recordmydesktop.c) -AM_INIT_AUTOMAKE(recordmydesktop,0.2.7,) +AM_INIT_AUTOMAKE(recordmydesktop,0.3.0,)  AC_CONFIG_SRCDIR([src/recordmydesktop.c])  AM_CONFIG_HEADER(config.h) @@ -22,22 +22,27 @@ AC_HEADER_STDC  AC_PATH_X  AC_PATH_XTRA -AC_CHECK_HEADER([png.h]) -AC_CHECK_HEADER([alsa/asoundlib.h]) -AC_CHECK_HEADERS([sys/time.h unistd.h vorbis/vorbisfile.h ]) +if test "x$x_libraries" != "x" && test "x$x_libraries" != xNONE ; then +    echo "X libraries are found in $x_libraries" +    LIBS="-L$x_libraries  $LIBS"; +fi -LDFLAGS="$LD_FLAGS -L$x_libraries $X_LIBS $X_EXTRA_LIBS $X_PRE_LIBS" - -#no-O3s-thx.patch ;) -CFLAGS="${CFLAGS} -Wall -D_THREAD_SAFE -pthread" +if test "x$x_includes" != "x" && test "x$x_includes" != xNONE ; then +    echo "X includes are found in $x_includes" +    CFLAGS="-I$x_includes  $CFLAGS"; +fi +AC_CHECK_HEADER([alsa/asoundlib.h]) +AC_CHECK_HEADERS([sys/time.h unistd.h vorbis/vorbisfile.h ])  # Checks for libraries.  AC_CHECK_LIB([m],[isnan],,)  AC_CHECK_LIB([z],[deflate],,) +AC_CHECK_LIB([ICE],[IceOpenConnection],,AC_MSG_ERROR([Can't find libICE]),) +AC_CHECK_LIB([SM],[SmcOpenConnection],,AC_MSG_ERROR([Can't find libSM]),)  AC_CHECK_LIB([X11],[XOpenDisplay],,AC_MSG_ERROR([Can't find libX11]),              -L$x_libraries $X_PRE_LIBS)  AC_CHECK_LIB([Xext],[XShmQueryVersion],,AC_MSG_ERROR([Can't find libXext])) @@ -49,10 +54,10 @@ AC_CHECK_LIB([vorbisenc],[vorbis_encode_init],,AC_MSG_ERROR([Can't find libvorbi  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. diff --git a/recordmydesktop/doc/recordmydesktop.1 b/recordmydesktop/doc/recordmydesktop.1 index e8cbb6a..6fa7da5 100644 --- a/recordmydesktop/doc/recordmydesktop.1 +++ b/recordmydesktop/doc/recordmydesktop.1 @@ -7,7 +7,7 @@ recordMyDesktop \- record desktop sessions to an Ogg\-Theora\-Vorbis file.  .SH SYNOPSIS -.B        recordmydesktop  +.B        recordmydesktop  [  .B  Options @@ -22,14 +22,14 @@ filename  .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  +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)  +or capture windows that do not generate events on change(windows with accelerated 3d context)  .br -but this will notably increase the workload. In this case, enabling the  +but this will notably increase the workload. In this case, enabling the  .B  \-\-with\-shared  option is recommended @@ -71,9 +71,15 @@ while a command like:  .br  .B  ~$ recordmydesktop foo.ogg -.br  +.br  will write output to foo.ogg  .br +.br +Since version 0.3, encoding will happen right after the recording finishes. +.br +While this behavior saves a lot of CPU, you can revert to the old one by entering the \-\-on\-the\-fly\-encoding switch. +.br +.br  To specify a region for recording you can type this:  .br  .B @@ -87,6 +93,8 @@ If the area extends beyond your current resolution, you will be notified appropr  .br  Notice also, that if any option is entered you have to specify the output file with the \-o switch.  .br +If you try to save under a filename that already exists, the name will be post-fixed with a number (incremented if that name exists already) +.br  .br  .B  To normally end a recording you can press ctl-c. @@ -97,15 +105,15 @@ SIGINT  to the program).  .br  .br -For further manipulation of the end result look at the  +For further manipulation of the end result look at the  .B  OPTIONS -and  +and  .B  NOTES  sections.  .br -.br  +.br  .br  .SH EXIT STATUS  0 is success @@ -129,7 +137,7 @@ The following error codes indicate the nature of the error:  .br  7 XInitThreads failed.  .br -8 No $DISPLAY environment variable and none specified as argument.  +8 No $DISPLAY environment variable and none specified as argument.  .br  9 Cannot connect to Xserver.  .br @@ -140,8 +148,8 @@ The following error codes indicate the nature of the error:  12 Cannot attach shared memory to proccess.  .br  13 Cannot open file for writting. -.br  -.br  +.br +.br  .SH OPTIONS  .PP  .B @@ -149,7 +157,7 @@ Generic Options:  .br  .TP  .B -    \-h or \-\-help     +    \-h or \-\-help      Print help summary and exit.  .br  .TP @@ -258,7 +266,7 @@ Sound Options:  .br  .TP  .B -    \-\-nosound +    \-\-no\-sound      Do not record sound.  .br  .PP @@ -268,6 +276,11 @@ Encoding Options:  .br  .TP  .B +    \-\-on\-the\-fly\-encoding +    Encode the audio-video data, while recording. +.br +.TP +.B      \-v_quality n      A number from 0 to 63 for desired encoded video quality(default 63).  .br @@ -293,18 +306,34 @@ 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). +    \-\-no\-wm\-check +    When a 3d compositing window manager is detected the program will function as if the +    \-\-full\-shots and \-\-with\-shared options have been specified. This option disables that +    behavior alltogether.  .br  .TP  .B -    \-\-scshot -    Take a bitmap screenshot(default rmdout.bmp) and exit. +    \-\-zero\-compression +    Image data are always cached uncompressed.  .br  .TP  .B -    \-scale\-shot N -    Factor by which screenshot is scaled down(1<=number<=64,power of 2). +    \-workdir DIR +    Location where a temporary directory will be created to hold project files(default $HOME). +.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 +    \-\-overwrite +    If there is already a file with the same name, delete it. +    Default action is to add a number postfix to the new file. +    For example when not specifying a name, if out.ogg exists, +    the new file will be out.ogg.1 and if that exists too, out.ogg.2 +    and so on (no ad-infinitum though, more like ad-short-integer...)  .br  .TP  .B @@ -320,19 +349,20 @@ If no other option is specified, filename can be given without the \-o switch.  .br  .SH USAGE  .TP -.B        recordmydesktop  +.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)| +\-display DISPLAY| \-x X| \-y Y|\-width N| \-height N| \-fps N(number>0)| \-\-on\-the\-fly\-encoding|  .br  \-v_quality n| \-s_quality n| \-v_bitrate n| \-\-no\-framedrop| \-dummy\-cursor color|  .br  \-\-no\-cursor| \-freq N(number>0)| \-channels N(number>0)| \-device SOUND_DEVICE|  .br -\-\-nosound| \-\-with\-shared| \-\-no\-cond\-shared| \-shared\-threshold n| \-\-full\-shots| +\-\-no\-sound| \-\-with\-shared| \-\-no\-cond\-shared| \-shared\-threshold n| \-\-full\-shots| +.br +\-\-quick\-subsampling| \-workdir DIR| \-\-zero\-compression| \-\-no\-wm\-check| \-\-overwrite| \-o filename]^filename  .br -\-\-quick\-subsampling| \-\-scshot| \-scale\-shot N| \-o filename]^filename  .br  .br  .br @@ -349,7 +379,7 @@ Display environment variable, specifying X server to connect to.  .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  +An easy way to find out the id of a window, is by using the  .B  xwininfo  program. @@ -357,14 +387,14 @@ program.  Running a command like :  .br  .B -xwininfo |grep "Window id:"|sed \-e "s/xwininfo\\:\\ Window id:\\ // ;s/\\ \.*//"  +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/\\ \.*//" )  +~$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 ( @@ -372,7 +402,11 @@ More conviniently you can put all that in the command that launches recordMyDesk  -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. +So +.B +if you are doing the encoding on the fly +,it's better to start with default values and manipulate +the end\-result with another program.  .br  An excellent converter is  .B @@ -382,13 +416,17 @@ ffmpeg2theora  Changing the quality of a recordng with it,can be as simple as :  .br  .B -ffmpeg2theora infile.ogg -v 3 -a 4 -o outfile.ogg +ffmpeg2theora infile.ogg \-v 3 \-a 4 \-o outfile.ogg  .br  It can even perform resizing on the size of the recording, or change the overall duration.  .br  .br  .SH BUGS -Too resource intensive. +Does not record 3d windows, if \-\-full\-shots isn't specified. +.br +Saving 65536 files with the same name, will result in upredictable behavior, +.br +which might manifest as an endless loop, or a segmentation fault.  .br  .SH AUTHORS  John Varouhakis(johnvarouhakis@gmail.com) diff --git a/recordmydesktop/include/recordmydesktop.h b/recordmydesktop/include/recordmydesktop.h index 4533b87..4c3ad18 100644 --- a/recordmydesktop/include/recordmydesktop.h +++ b/recordmydesktop/include/recordmydesktop.h @@ -37,7 +37,7 @@  #include <string.h>  #include <errno.h>  #include <math.h> -#include <unistd.h>   +#include <unistd.h>  #include <fcntl.h>  #include <time.h>  #include <signal.h> @@ -49,8 +49,10 @@  #include <sys/ipc.h>  #include <sys/shm.h>  #include <pthread.h> +#include <zlib.h>  #include <X11/Xlib.h>  #include <X11/Xlibint.h> +#include <X11/Xatom.h>  #include <X11/extensions/Xfixes.h>  #include <X11/extensions/Xdamage.h>  #include <X11/extensions/XShm.h> @@ -67,18 +69,29 @@  #define __RBYTE 2  #define __GBYTE 1  #define __BBYTE 0 + +#define __RVALUE(tmp_val) (((tmp_val)&0x00ff0000)>>16) +#define __GVALUE(tmp_val) (((tmp_val)&0x0000ff00)>>8) +#define __BVALUE(tmp_val) (((tmp_val)&0x000000ff)) +  #elif __BYTE_ORDER == __BIG_ENDIAN +  #define __ABYTE 0  #define __RBYTE 1  #define __GBYTE 2  #define __BBYTE 3 + +#define __RVALUE(tmp_val) (((tmp_val)&0x0000ff00)>>8) +#define __GVALUE(tmp_val) (((tmp_val)&0x00ff0000)>>16) +#define __BVALUE(tmp_val) (((tmp_val)&0xff000000)>>24) +  #else  #error Only little-endian and big-endian systems are supported  #endif  //do not be confused -//this is useless and obsolete.  +//this is useless and obsolete.  //There are no plans for other fotmats  enum {UNSPECIFIED,OGG_THEORA_VORBIS}; @@ -86,7 +99,7 @@ enum {UNSPECIFIED,OGG_THEORA_VORBIS};  /**Structs*/  typedef struct _DisplaySpecs{   //this struct holds some basic information -    int screen;                 //about the display,needed mostly for  +    int screen;                 //about the display,needed mostly for      uint width;                 //validity checks at startup      uint height;      Window root; @@ -102,7 +115,7 @@ typedef struct _WGeometry{  //basic geometry of a window or area      int y;      int width;      int height; -}WGeometry;     +}WGeometry;  typedef struct _RectArea{   //an area that has been damaged gets stored      WGeometry geom;         //in a list comprised of structs of this type @@ -129,7 +142,7 @@ typedef struct _ProgArgs{      int encoding;       //encoding(default OGG_THEORA_VORBIS)      int cursor_color;   //black or white=>1 or 0      int have_dummy_cursor;//disable/enable drawing of the dummy cursor -    int xfixes_cursor;   //disable/enable drawing of a cursor obtained  +    int xfixes_cursor;   //disable/enable drawing of a cursor obtained                          //through the xfixes extension      float fps;            //desired framerate(default 15)      unsigned int frequency;      //desired frequency (default 22050) @@ -137,23 +150,27 @@ typedef struct _ProgArgs{      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 1) -    int nocondshared;   //de not use shared memory on large image aquititions +    int nocondshared;   //do not use shared memory on large image aquititions +    int nowmcheck;      //do not check if there's a 3d comp window manager +                        //(which changes full-shots and with-shared to 1)      int shared_thres;   //threshold to use shared memory      int full_shots;     //do not poll damage, take full screenshots      int no_quick_subsample;//average pixels in chroma planes -    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      int dropframes;     //option for theora encoder +    int encOnTheFly;    //encode while recording, no caching(default 0) +    char *workdir;      //directory to be used for cache files(default $HOME) +    int zerocompression;//image data are always flushed uncompressed +    int overwrite;//overwite a previously existing file(do not add a .number postfix)  }ProgArgs; -//this struct holds anything related to encoding AND  -//writting out to file.  +//this struct holds anything related to encoding AND +//writting out to file.  typedef struct _EncData{      ogg_stream_state m_ogg_ts;//theora      ogg_stream_state m_ogg_vs;//vorbis -    ogg_page         m_ogg_pg;//this could be avoided since  +    ogg_page         m_ogg_pg;//this could be avoided since                                // it is used only while initializing      ogg_packet       m_ogg_pckt1;//theora stream      ogg_packet       m_ogg_pckt2;//vorbis stream @@ -165,7 +182,7 @@ typedef struct _EncData{  //vorbis data      vorbis_info      m_vo_inf;      vorbis_comment   m_vo_cmmnt; -    vorbis_dsp_state m_vo_dsp;  +    vorbis_dsp_state m_vo_dsp;      vorbis_block     m_vo_block;  //these should be 0, since area is quantized  //before input @@ -175,8 +192,27 @@ typedef struct _EncData{      FILE            *fp;  }EncData; +//this struct will hold a few basic +//information, needed for caching the frames. +typedef struct _CacheData{ +    char    *workdir,  //The directory were the project will be stored, while recording. +                        //Since this will take a lot of space, the user must be +                        //able to change the location. +            *projname,  //This is the name of the folder that will hold the project. +                        //It is rMD-session-%d where %d is the pid of the current proccess. +                        //This way, running two instances will not create problems +                        //and also, a frontend can identify leftovers from a possible crash +                        //and delete them +            *imgdata,   //workdir+projname+img.out.gz +            *audiodata; //workdir+projname+audio.pcm + +    gzFile  *ifp;       //image data file pointer +    FILE    *afp;       //audio data file pointer + +}CacheData; +  //sound buffer -//sound keeps coming so we que it in this list  +//sound keeps coming so we que it in this list  //which we then traverse  typedef struct _SndBuffer{      signed char *data; @@ -184,23 +220,24 @@ typedef struct _SndBuffer{  }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.  +//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 +    char *window_manager;//name of the window manager at program launch      XImage *image;//the image that holds the current full screenshot      XImage *shimage;//the image that holds the current full screenshot(shared memory)      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  image  +    char    *datamain,//the data of  image              *datash,//the data of shimage -            *datatemp;//buffer for the temporary image,which will be  +            *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 @@ -209,6 +246,7 @@ typedef struct _ProgData{          running;      SndBuffer *sound_buffer;      EncData *enc_data; +    CacheData *cache_data;      int hard_pause;//if sound device doesn't support pause                      //we have to close and reopen      int avd;//syncronization among audio and video @@ -217,7 +255,7 @@ typedef struct _ProgData{      pthread_mutex_t list_mutex[2],//mutexes for concurrency protection of the lists                      sound_buffer_mutex,                      libogg_mutex,//libogg is not thread safe -                    yuv_mutex;//this might not be needed since we only have  +                    yuv_mutex;//this might not be needed since we only have                                //one read-only and  one write-only thread                                //also on previous versions, y component was looped separately                                //and then u and v so this was needed to avoid wrong coloring to render @@ -231,6 +269,48 @@ typedef struct _ProgData{      snd_pcm_uframes_t periodsize;  }ProgData; + +//This is the header of every frame. +//Reconstruction will be correct only if made on +//the same platform. + +//We need the total number of blocks +//for each plane. + +//The number of the frame compared to the +//number of time expirations at the time of +//caching, will enable us to make up for lost frames. + +//default 4+4+2+2+2=14!bad! +//me add pad, make god of 2 happy! +typedef struct _FrameHeader{ +    char        frame_prefix[4];//always FRAM +    u_int32_t   frameno,//number of frame(cached frames) +                current_total;//number of frames that should have been +                                  //taken at time of caching this one +    u_int16_t   Ynum,//number of changed blocks in the Y plane +                Unum,//number of changed blocks in the U plane +                Vnum;//number of changed blocks in the V plane +    u_int16_t   pad;//always zero + +}FrameHeader; + +//The frame after retrieval. +//Based on the Header information +//we can read the correct amount of bytes. + + +typedef struct _CachedFrame{ +    FrameHeader *header; +    unsigned char *YBlocks;//identifying number on the grid, starting at top left +    unsigned char *UBlocks;//       >>      >> +    unsigned char *VBlocks;//       >>      >> +    unsigned char *YData;//pointer to data for the blocks that have changed, +    unsigned char *UData;//which have to be remapped on the buffer when reading +    unsigned char *VData; +}CachedFrame; + +  /**Globals*/  //I've read somewhere that I'll go to hell for using globals... @@ -241,9 +321,9 @@ unsigned char   Yr[256],Yg[256],Yb[256],                  Vr[256],Vg[256],Vb[256];  //the following values are of no effect  //but they might be usefull later for profiling -unsigned int frames_total,//frames calculated by total time expirations -             frames_lost;//the value of shame -//used to determine frame drop which can  +unsigned int    frames_total,//frames calculated by total time expirations +                frames_lost;//the value of shame +//used to determine frame drop which can  //happen on failure to receive a signal over a condition variable  int capture_busy,      encoder_busy; @@ -318,8 +398,10 @@ int capture_busy,          (args)->display=NULL;\      (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)->nosound=(args)->full_shots=(args)->encOnTheFly\ +    =(args)->zerocompression=(args)->nowmcheck\ +    =(args)->overwrite=0;\ +    (args)->noshared=1;\      (args)->dropframes=(args)->nocondshared=0;\      (args)->no_quick_subsample=1;\      (args)->filename=(char *)malloc(8);\ @@ -337,6 +419,8 @@ int capture_busy,      (args)->v_bitrate=45000;\      (args)->v_quality=63;\      (args)->s_quality=10;\ +    (args)->workdir=(char *)malloc(strlen(getenv("HOME"))+1);\ +    strcpy((args)->workdir,getenv("HOME"));\  }  #define QUERY_DISPLAY_SPECS(display,specstruct){\ @@ -356,83 +440,193 @@ int capture_busy,      +data_array[(k_tm*width_img+i_tm-1)*4+offset]+data_array[((k_tm-1)*width_img+i_tm-1)*4+offset])/4)  #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]=Yr[data[(i+k*yuv->y_width)*4+__RBYTE]] + Yg[data[(i+k*yuv->y_width)*4+__GBYTE]] + Yb[data[(i+k*yuv->y_width)*4+__BBYTE]];\ -            if((k%2)&&(i%2)){\ -                yuv->u[i/2+k/2*yuv->uv_width]=Ur[data[(i+k*yuv->y_width)*4+__RBYTE]] + Ug[data[(i+k*yuv->y_width)*4+__GBYTE]] + Ub[data[(i+k*yuv->y_width)*4+__BBYTE]] ;\ -                yuv->v[i/2+k/2*yuv->uv_width]=Vr[data[(i+k*yuv->y_width)*4+__RBYTE]] + Vg[data[(i+k*yuv->y_width)*4+__GBYTE]] + Vb[data[(i+k*yuv->y_width)*4+__BBYTE]] ;\ -            }\ +    int k,i;\ +    register unsigned int t_val;\ +    register unsigned int *datapi=(unsigned int*)data+x_tm+y_tm*yuv->y_width;\ +    register unsigned char  *yuv_y=yuv->y+x_tm+y_tm*yuv->y_width,\ +                            *yuv_u=yuv->u+x_tm/2+(y_tm*yuv->uv_width)/2,\ +                            *yuv_v=yuv->v+x_tm/2+(y_tm*yuv->uv_width)/2,\ +                            *_yr=Yr,*_yg=Yg,*_yb=Yb,\ +                            *_ur=Ur,*_ug=Ug,*_ub=Ub,\ +                            *_vr=Vr,*_vg=Vg,*_vb=Vb;\ +\ +    for(k=0;k<height_tm;k++){\ +        for(i=0;i<width_tm;i++){\ +            t_val=*datapi;\ +            *yuv_y=_yr[__RVALUE(t_val)] + _yg[__GVALUE(t_val)] + _yb[__BVALUE(t_val)] ;\ +            datapi++;\ +            yuv_y++;\ +        }\ +        yuv_y+=yuv->y_width-width_tm;\ +        datapi+=yuv->y_width-width_tm;\ +    }\ +    datapi=(unsigned int*)data+x_tm+y_tm*yuv->y_width;\ +    for(k=0;k<height_tm;k+=2){\ +        for(i=0;i<width_tm;i+=2){\ +            t_val=*datapi;\ +            *yuv_u=\ +            _ur[__RVALUE(t_val)] + _ug[__GVALUE(t_val)] + _ub[__BVALUE(t_val)];\ +            *yuv_v=\ +            _vr[__RVALUE(t_val)] + _vg[__GVALUE(t_val)] + _vb[__BVALUE(t_val)];\ +            datapi+=2;\ +            yuv_u++;\ +            yuv_v++;\          }\ +        yuv_u+=(yuv->y_width-width_tm)/2;\ +        yuv_v+=(yuv->y_width-width_tm)/2;\ +        datapi+=(2*yuv->y_width-width_tm);\      }\  }  #define UPDATE_YUV_BUFFER_SH_AVG(yuv,data,x_tm,y_tm,width_tm,height_tm){\ -    int i,k;\ -    unsigned char avg0,avg1,avg2;\ -    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]=Yr[data[(i+k*yuv->y_width)*4+__RBYTE]] + Yg[data[(i+k*yuv->y_width)*4+__GBYTE]] + Yb[data[(i+k*yuv->y_width)*4+__BBYTE]];\ -            if((k%2)&&(i%2)){\ -                avg2=AVG_4_PIXELS(data,(yuv->y_width),k,i,__RBYTE);\ -                avg1=AVG_4_PIXELS(data,(yuv->y_width),k,i,__GBYTE);\ -                avg0=AVG_4_PIXELS(data,(yuv->y_width),k,i,__BBYTE);\ -                yuv->u[i/2+k/2*yuv->uv_width]=Ur[avg2] +\ -                Ug[avg1] +\ -                Ub[avg0] ;\ -                yuv->v[i/2+k/2*yuv->uv_width]=Vr[avg2] +\ -                Vg[avg1] +\ -                Vb[avg0] ;\ -            }\ +    int k,i;\ +    register unsigned int t_val,t1,t2,t3,t4;\ +    register unsigned int *datapi=(unsigned int*)data+x_tm+y_tm*yuv->y_width,\ +                          *datapi_next=(unsigned int*)data+x_tm+(y_tm+1)*yuv->y_width;\ +    register unsigned char  *yuv_y=yuv->y+x_tm+y_tm*yuv->y_width,\ +                            *yuv_u=yuv->u+x_tm/2+(y_tm*yuv->uv_width)/2,\ +                            *yuv_v=yuv->v+x_tm/2+(y_tm*yuv->uv_width)/2,\ +                            *_yr=Yr,*_yg=Yg,*_yb=Yb,\ +                            *_ur=Ur,*_ug=Ug,*_ub=Ub,\ +                            *_vr=Vr,*_vg=Vg,*_vb=Vb;\ +\ +    for(k=0;k<height_tm;k++){\ +        for(i=0;i<width_tm;i++){\ +            t_val=*datapi;\ +            *yuv_y=_yr[__RVALUE(t_val)] + _yg[__GVALUE(t_val)] + _yb[__BVALUE(t_val)] ;\ +            datapi++;\ +            yuv_y++;\ +        }\ +        yuv_y+=yuv->y_width-width_tm;\ +        datapi+=yuv->y_width-width_tm;\ +    }\ +    datapi=(unsigned int*)data+x_tm+y_tm*yuv->y_width;\ +    for(k=0;k<height_tm;k+=2){\ +        for(i=0;i<width_tm;i+=2){\ +            t1=*datapi;\ +            t2=*(datapi+1);\ +            t3=*datapi_next;\ +            t4=*(datapi_next+1);\ +            t_val=((((t1&0xff000000) +(t2&0xff000000)+\ +            (t3&0xff000000)+(t4&0xff000000))/4)&0xff000000) \ +            +((((t1&0x00ff0000) +(t2&0x00ff0000)+\ +            (t3&0x00ff0000)+(t4&0x00ff0000))/4)&0x00ff0000)\ +            +((((t1&0x0000ff00) +(t2&0x0000ff00)+\ +            (t3&0x0000ff00)+(t4&0x0000ff00))/4)&0x0000ff00)\ +            +((((t1&0x000000ff) +(t2&0x000000ff)+\ +            (t3&0x000000ff)+(t4&0x000000ff))/4)&0x000000ff);\ +\ +            *yuv_u=\ +            _ur[__RVALUE(t_val)] + _ug[__GVALUE(t_val)] + _ub[__BVALUE(t_val)];\ +            *yuv_v=\ +            _vr[__RVALUE(t_val)] + _vg[__GVALUE(t_val)] + _vb[__BVALUE(t_val)];\ +            datapi+=2;\ +            datapi_next+=2;\ +            yuv_u++;\ +            yuv_v++;\          }\ +        yuv_u+=(yuv->y_width-width_tm)/2;\ +        yuv_v+=(yuv->y_width-width_tm)/2;\ +        datapi+=(2*yuv->y_width-width_tm);\ +        datapi_next+=(2*yuv->y_width-width_tm);\      }\  } + +  #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;\ +    int k,i;\ +    register unsigned int t_val;\ +    register unsigned int *datapi=(unsigned int*)data;\ +    register unsigned char  *yuv_y=yuv->y+x_tm+y_tm*yuv->y_width,\ +                            *yuv_u=yuv->u+x_tm/2+(y_tm*yuv->uv_width)/2,\ +                            *yuv_v=yuv->v+x_tm/2+(y_tm*yuv->uv_width)/2,\ +                            *_yr=Yr,*_yg=Yg,*_yb=Yb,\ +                            *_ur=Ur,*_ug=Ug,*_ub=Ub,\ +                            *_vr=Vr,*_vg=Vg,*_vb=Vb;\ +\      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]=Yr[data[(j*4)+__RBYTE]] + Yg[data[(j*4)+__GBYTE]] + Yb[data[(j*4)+__BBYTE]] ;\ -            if((k%2)&&(i%2)){\ -                yuv->u[x_2+i/2+(k/2+y_2)*yuv->uv_width]=\ -                Ur[data[(k*width_tm+i)*4+__RBYTE]] + Ug[data[(k*width_tm+i)*4+__GBYTE]] + Ub[data[(k*width_tm+i)*4+__BBYTE]];\ -                yuv->v[x_2+i/2+(k/2+y_2)*yuv->uv_width]=\ -                Vr[data[(k*width_tm+i)*4+__RBYTE]] + Vg[data[(k*width_tm+i)*4+__GBYTE]] + Vb[data[(k*width_tm+i)*4+__BBYTE]];\ -            }\ -            \ -            j++;\ +            t_val=*datapi;\ +            *yuv_y=_yr[__RVALUE(t_val)] + _yg[__GVALUE(t_val)] + _yb[__BVALUE(t_val)] ;\ +            datapi++;\ +            yuv_y++;\          }\ +        yuv_y+=yuv->y_width-width_tm;\ +    }\ +    datapi=(unsigned int*)data;\ +    for(k=0;k<height_tm;k+=2){\ +        for(i=0;i<width_tm;i+=2){\ +            t_val=*datapi;\ +            *yuv_u=\ +            _ur[__RVALUE(t_val)] + _ug[__GVALUE(t_val)] + _ub[__BVALUE(t_val)];\ +            *yuv_v=\ +            _vr[__RVALUE(t_val)] + _vg[__GVALUE(t_val)] + _vb[__BVALUE(t_val)];\ +            datapi+=2;\ +            yuv_u++;\ +            yuv_v++;\ +        }\ +        yuv_u+=(yuv->y_width-width_tm)/2;\ +        yuv_v+=(yuv->y_width-width_tm)/2;\ +        datapi+=width_tm;\      }\  } - -  #define UPDATE_YUV_BUFFER_IM_AVG(yuv,data,x_tm,y_tm,width_tm,height_tm){\ -    int i,k,j=0;\ -    unsigned char avg0,avg1,avg2;\ -    int x_2=x_tm/2,y_2=y_tm/2;\ +    int k,i;\ +    register unsigned int t_val,t1,t2,t3,t4;\ +    register unsigned int *datapi=(unsigned int*)data,\ +                          *datapi_next=(unsigned int*)data+width_tm;\ +    register unsigned char  *yuv_y=yuv->y+x_tm+y_tm*yuv->y_width,\ +                            *yuv_u=yuv->u+x_tm/2+(y_tm*yuv->uv_width)/2,\ +                            *yuv_v=yuv->v+x_tm/2+(y_tm*yuv->uv_width)/2,\ +                            *_yr=Yr,*_yg=Yg,*_yb=Yb,\ +                            *_ur=Ur,*_ug=Ug,*_ub=Ub,\ +                            *_vr=Vr,*_vg=Vg,*_vb=Vb;\ +\      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]=Yr[data[(j*4)+__RBYTE]] + Yg[data[(j*4)+__GBYTE]] + Yb[data[(j*4)+__BBYTE]] ;\ -            if((k%2)&&(i%2)){\ -                avg2=AVG_4_PIXELS(data,width_tm,k,i,__RBYTE);\ -                avg1=AVG_4_PIXELS(data,width_tm,k,i,__GBYTE);\ -                avg0=AVG_4_PIXELS(data,width_tm,k,i,__BBYTE);\ -                yuv->u[x_2+i/2+(k/2+y_2)*yuv->uv_width]=\ -                Ur[avg2] + Ug[avg1] +\ -                Ub[avg0];\ -                yuv->v[x_2+i/2+(k/2+y_2)*yuv->uv_width]=\ -                Vr[avg2] + Vg[avg1] +\ -                Vb[avg0];\ -            }\ -            \ -            j++;\ +            t_val=*datapi;\ +            *yuv_y=_yr[__RVALUE(t_val)] + _yg[__GVALUE(t_val)] + _yb[__BVALUE(t_val)] ;\ +            datapi++;\ +            yuv_y++;\          }\ +        yuv_y+=yuv->y_width-width_tm;\ +    }\ +    datapi=(unsigned int*)data;\ +    for(k=0;k<height_tm;k+=2){\ +        for(i=0;i<width_tm;i+=2){\ +            t1=*datapi;\ +            t2=*(datapi+1);\ +            t3=*datapi_next;\ +            t4=*(datapi_next+1);\ +            t_val=((((t1&0xff000000) +(t2&0xff000000)+\ +            (t3&0xff000000)+(t4&0xff000000))/4)&0xff000000) \ +            +((((t1&0x00ff0000) +(t2&0x00ff0000)+\ +            (t3&0x00ff0000)+(t4&0x00ff0000))/4)&0x00ff0000)\ +            +((((t1&0x0000ff00) +(t2&0x0000ff00)+\ +            (t3&0x0000ff00)+(t4&0x0000ff00))/4)&0x0000ff00)\ +            +((((t1&0x000000ff) +(t2&0x000000ff)+\ +            (t3&0x000000ff)+(t4&0x000000ff))/4)&0x000000ff);\ +\ +            *yuv_u=\ +            _ur[__RVALUE(t_val)] + _ug[__GVALUE(t_val)] + _ub[__BVALUE(t_val)];\ +            *yuv_v=\ +            _vr[__RVALUE(t_val)] + _vg[__GVALUE(t_val)] + _vb[__BVALUE(t_val)];\ +            datapi+=2;\ +            datapi_next+=2;\ +            yuv_u++;\ +            yuv_v++;\ +        }\ +        yuv_u+=(yuv->y_width-width_tm)/2;\ +        yuv_v+=(yuv->y_width-width_tm)/2;\ +        datapi+=width_tm;\ +        datapi_next+=width_tm;\      }\  } + +  #define XFIXES_POINTER_TO_YUV(yuv,data,x_tm,y_tm,width_tm,height_tm,column_discard_stride){\      int i,k,j=0;\      unsigned char avg0,avg1,avg2,avg3;\ @@ -479,6 +673,42 @@ int capture_busy,  } +#define I16TOA(number,buffer){\ +    int t_num=(number),k=0,i=0;\ +    char *t_buf=malloc(8);\ +    t_num=t_num&((2<<15)-1);\ +    while(t_num>0){\ +        int digit=t_num%10;\ +        t_buf[k]=digit+48;\ +        t_num-=digit;\ +        t_num/=10;\ +        k++;\ +    }\ +    while(k>0)\ +        (buffer)[i++]=t_buf[--k];\ +    (buffer)[i]='\0';\ +    free(t_buf);\ +};\ + +#define INIT_FRAME(frame_t,fheader_t,yuv_t){\ +    (frame_t)->header=(fheader_t);\ +    (frame_t)->YBlocks=malloc(256);\ +    (frame_t)->UBlocks=malloc(64);\ +    (frame_t)->VBlocks=malloc(64);\ +    (frame_t)->YData=malloc((yuv_t)->y_width*(yuv_t)->y_height);\ +    (frame_t)->UData=malloc((yuv_t)->uv_width*(yuv_t)->uv_height);\ +    (frame_t)->VData=malloc((yuv_t)->uv_width*(yuv_t)->uv_height);\ +}; + +#define CLEAR_FRAME(frame_t){\ +    free((frame_t)->YBlocks);\ +    free((frame_t)->UBlocks);\ +    free((frame_t)->VBlocks);\ +    free((frame_t)->YData);\ +    free((frame_t)->UData);\ +    free((frame_t)->VData);\ +}; +  /**Function prototypes*/  void *PollDamage(void *pdata); @@ -496,13 +726,20 @@ int GetZPixmap(Display *dpy,Window root,char *data,int x,int y,int width,int hei  int ParseArgs(int argc,char **argv,ProgArgs *arg_return);  void 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 *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); +void InitEncoder(ProgData *pdata,EncData *enc_data_t,int buffer_ready);  void MakeMatrices();  void SizePack2_8_16(int *start,int *size,int limit); +void *CacheImageBuffer(void *pdata); +void InitCacheData(ProgData *pdata,EncData *enc_data_t,CacheData *cache_data_t); +void *CacheSoundBuffer(void *pdata); +void *LoadCache(void *pdata); +void SyncEncodeImageBuffer(ProgData *pdata); +void CancelTimer(void); +void SyncEncodeSoundBuffer(ProgData *pdata,signed char *buff); +char *rmdWMCheck(Display *dpy,Window root);  #endif diff --git a/recordmydesktop/src/Makefile.am b/recordmydesktop/src/Makefile.am index e56b856..d5fa847 100644 --- a/recordmydesktop/src/Makefile.am +++ b/recordmydesktop/src/Makefile.am @@ -3,7 +3,6 @@ bin_PROGRAMS = recordmydesktop  recordmydesktop_SOURCES=	recordmydesktop.c\ -							zpixmaptobmp.c\  							getzpixmap.c\  							parseargs.c\  							rectinsert.c\ @@ -20,9 +19,14 @@ recordmydesktop_SOURCES=	recordmydesktop.c\  							opendev.c\  							capture_sound.c\  							encode_sound_buffer.c\ -							init_encoder.c +							init_encoder.c\ +							cache_frame.c\ +							cache_audio.c\ +							rmd_cache.c\ +							load_cache.c\ +							wm_check.c -INCLUDES= $(all_includes) -I../include -I$x_includes - -recordmydesktop_LDFLAGS = -D_THREAD_SAFE -pthread -Wall +INCLUDES= $(all_includes) -I../include +recordmydesktop_LDFLAGS = @X_LIBS@ @X_EXTRA_LIBS@ @X_PRE_LIBS@ +recordmydesktop_CFLAGS = -D_THREAD_SAFE -pthread -Wall diff --git a/recordmydesktop/src/zpixmaptobmp.c b/recordmydesktop/src/cache_audio.c index 0aa9477..7b17d7b 100644 --- a/recordmydesktop/src/zpixmaptobmp.c +++ b/recordmydesktop/src/cache_audio.c @@ -24,55 +24,44 @@  *    For further information contact me at johnvarouhakis@gmail.com              *  **********************************************************************************/ -  #include <recordmydesktop.h> +void *CacheSoundBuffer(void *pdata){ +//We are simply going to throw sound on the disk. +//It's sound is tiny compared to that of image, so +//compressing would reducethe overall size by only an +//insignificant fraction. +    pthread_mutex_t smut; +    pthread_mutex_init(&smut,NULL); +    while((((ProgData *)pdata)->running)){ +        SndBuffer *buff; -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); -            } +        if(Paused){ +            pthread_mutex_t tmut; +            pthread_mutex_init(&tmut,NULL); +            pthread_cond_wait(&((ProgData *)pdata)->pause_cond,&tmut);          } -  -        fclose(fpbmp); -        return 0; + +        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); + +        fwrite(buff->data,((ProgData *)pdata)->periodsize,1,((ProgData *)pdata)->cache_data->afp); + + + +        ((ProgData *)pdata)->avd-=((ProgData *)pdata)->periodtime; + +        free(buff->data); +        free(buff); +    } + +    fclose(((ProgData *)pdata)->cache_data->afp); +    pthread_exit(&errno);  } diff --git a/recordmydesktop/src/cache_frame.c b/recordmydesktop/src/cache_frame.c new file mode 100644 index 0000000..c100505 --- /dev/null +++ b/recordmydesktop/src/cache_frame.c @@ -0,0 +1,193 @@ +/********************************************************************************* +*                             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 johnvarouhakis@gmail.com              * +**********************************************************************************/ + +#include <recordmydesktop.h> + + +int CompareBlocks(unsigned char *incoming,unsigned char *old,int blockno,int width, int height,int divisor){ +    int j,i, +        block_i=blockno/divisor,//place on the grid +        block_k=blockno%divisor; +    register unsigned char *incoming_reg=&(incoming[block_i*(width*height/divisor)+block_k*width/divisor]), +                           *old_reg=&(old[block_i*(width*height/divisor)+block_k*width/divisor]); + +    for(j=0;j<height/divisor;j++){ +        for(i=0;i<width/divisor;i++){ +            if((*(incoming_reg++))!=(*(old_reg++))) +                return 1; +        } +        incoming_reg+=(width-width/divisor); +        old_reg+=(width-width/divisor); +    } + +    return 0; +} + +void FlushBlock(unsigned char *buf,int blockno,int width, int height,int divisor,gzFile *fp){ +    int j, +        block_i=blockno/divisor,//place on the grid +        block_k=blockno%divisor; +    register unsigned char *buf_reg=(&buf[block_i*(width*height/divisor)+block_k*width/divisor]); +    for(j=0;j<height/divisor;j++){//we flush in rows +        gzwrite(fp,(void *)buf_reg,width/divisor); +        buf_reg+=width; +    } +} + +void *CacheImageBuffer(void *pdata){ +    pthread_mutex_t pmut,imut; +    pthread_mutex_init(&pmut,NULL); +    pthread_mutex_init(&imut,NULL); +    yuv_buffer yuv[2]; +    gzFile *fp=((ProgData *)pdata)->cache_data->ifp; + +    if(fp==NULL)exit(13); + +    int i,current=0,divisor=16,firstrun=1,frameno=0; + +    for(i=0;i<2;i++){ +        yuv[i].y_width=((ProgData *)pdata)->enc_data->yuv.y_width; +        yuv[i].y_height=((ProgData *)pdata)->enc_data->yuv.y_height; +        yuv[i].uv_width=((ProgData *)pdata)->enc_data->yuv.uv_width; +        yuv[i].uv_height=((ProgData *)pdata)->enc_data->yuv.uv_height; + +        yuv[i].y=(unsigned char *)malloc(yuv[i].y_width*yuv[i].y_height); +        yuv[i].u=(unsigned char *)malloc(yuv[i].uv_width*yuv[i].uv_height); +        yuv[i].v=(unsigned char *)malloc(yuv[i].uv_width*yuv[i].uv_height); +    } + + +    while(((ProgData *)pdata)->running){ +        int prev; +        int j; +        unsigned short ynum,unum,vnum; +        unsigned char yblocks[256],ublocks[64],vblocks[64]; +        FrameHeader fheader; +        ynum=unum=vnum=0; + +        pthread_cond_wait(&((ProgData *)pdata)->image_buffer_ready,&imut); +        if(Paused) +            pthread_cond_wait(&((ProgData *)pdata)->pause_cond,&pmut); +        pthread_mutex_lock(&((ProgData *)pdata)->yuv_mutex); + +        //rotate buffers +        prev=current; +        current=(current)?0:1; +        //copy incoming +        memcpy(yuv[current].y,((ProgData *)pdata)->enc_data->yuv.y,yuv[current].y_width*yuv[current].y_height); +        memcpy(yuv[current].u,((ProgData *)pdata)->enc_data->yuv.u,yuv[current].uv_width*yuv[current].uv_height); +        memcpy(yuv[current].v,((ProgData *)pdata)->enc_data->yuv.v,yuv[current].uv_width*yuv[current].uv_height); +        //release main buffer +        pthread_mutex_unlock(&((ProgData *)pdata)->yuv_mutex); +        //get checksums for new + +        //find and flush different blocks +        if(firstrun){ +            firstrun=0; +            for(j=0;j<pow(divisor,2);j++){ +                    ynum++; +                    yblocks[ynum-1]=j; +            } +            for(j=0;j<pow(divisor/2,2);j++){ +                    unum++; +                    ublocks[unum-1]=j; +            } +            for(j=0;j<pow(divisor/2,2);j++){ +                    vnum++; +                    vblocks[vnum-1]=j; +            } + +        } +        else{ +            for(j=0;j<pow(divisor,2);j++){ +                if(CompareBlocks(yuv[current].y,yuv[prev].y,j,yuv[current].y_width,yuv[current].y_height,divisor)){ +                    ynum++; +                    yblocks[ynum-1]=j; +                } +            } +            for(j=0;j<pow(divisor/2,2);j++){ +                if(CompareBlocks(yuv[current].u,yuv[prev].u,j,yuv[current].uv_width,yuv[current].uv_height,divisor/2)){ +                    unum++; +                    ublocks[unum-1]=j; +                } +            } +            for(j=0;j<pow(divisor/2,2);j++){ +                if(CompareBlocks(yuv[current].v,yuv[prev].v,j,yuv[current].uv_width,yuv[current].uv_height,divisor/2)){ +                    vnum++; +                    vblocks[vnum-1]=j; +                } +            } + +        } +        /**WRITE FRAME TO DISK*/ +        if(!((ProgData *)pdata)->args.zerocompression){ +            if(ynum+unum+vnum>(pow(divisor,2)+pow(divisor/2,2)*2)/10) +                gzsetparams (fp,1,Z_FILTERED); +            else +                gzsetparams (fp,0,Z_FILTERED); +        } + +        strncpy(fheader.frame_prefix,"FRAM",4); +        fheader.frameno=++frameno; +        fheader.current_total=frames_total; +        fheader.Ynum=ynum; +        fheader.Unum=unum; +        fheader.Vnum=vnum; +        fheader.pad=0; +        gzwrite(fp,(void*)&fheader,sizeof(FrameHeader)); +        //flush indexes +        if(ynum)gzwrite(fp,yblocks,ynum); +        if(unum)gzwrite(fp,ublocks,unum); +        if(vnum)gzwrite(fp,vblocks,vnum); + + +        //flush the blocks for each buffer +        if(ynum) +            for(j=0;j<ynum;j++) +                FlushBlock(yuv[current].y,yblocks[j],yuv[current].y_width,yuv[current].y_height,divisor,fp); +        if(unum) +            for(j=0;j<unum;j++) +                FlushBlock(yuv[current].u,ublocks[j],yuv[current].uv_width,yuv[current].uv_height,divisor/2,fp); +        if(vnum) +            for(j=0;j<vnum;j++) +                FlushBlock(yuv[current].v,vblocks[j],yuv[current].uv_width,yuv[current].uv_height,divisor/2,fp); + + +        /**@________________@**/ +        ((ProgData *)pdata)->avd+=((ProgData *)pdata)->frametime*2*((ProgData *)pdata)->args.channels; + +    } + +    //clean up since we're not finished +    for(i=0;i<2;i++){ +        free(yuv[i].y); +        free(yuv[i].u); +        free(yuv[i].v); +    } +    fprintf(stderr,"Saved %d frames in a total of %d requests\n",frameno,frames_total); +    gzclose(fp); +    pthread_exit(&errno); +} diff --git a/recordmydesktop/src/encode_image_buffer.c b/recordmydesktop/src/encode_image_buffer.c index 38a1d77..8ff9629 100644 --- a/recordmydesktop/src/encode_image_buffer.c +++ b/recordmydesktop/src/encode_image_buffer.c @@ -30,8 +30,8 @@ void *EncodeImageBuffer(void *pdata){      pthread_mutex_init(&pmut,NULL);      pthread_mutex_init(&imut,NULL);      while(((ProgData *)pdata)->running){ -        encoder_busy=1;          pthread_cond_wait(&((ProgData *)pdata)->image_buffer_ready,&imut); +        encoder_busy=1;          if(Paused)              pthread_cond_wait(&((ProgData *)pdata)->pause_cond,&pmut);//this may not be needed          pthread_mutex_lock(&((ProgData *)pdata)->yuv_mutex); @@ -62,4 +62,22 @@ void *EncodeImageBuffer(void *pdata){      pthread_exit(&errno);  } +//this function is meant to be called normally +//not through a thread of it's own +void SyncEncodeImageBuffer(ProgData *pdata){ +    if(theora_encode_YUVin(&pdata->enc_data->m_th_st, +                            &pdata->enc_data->yuv)){ +        fprintf(stderr,"Encoder not ready!\n"); +    } +    else{ +        if(theora_encode_packetout(&pdata->enc_data->m_th_st,0, +                                    &pdata->enc_data->m_ogg_pckt1)==1){ +            pthread_mutex_lock(&pdata->libogg_mutex); +            ogg_stream_packetin(&pdata->enc_data->m_ogg_ts, +                                &pdata->enc_data->m_ogg_pckt1); +            pthread_mutex_unlock(&pdata->libogg_mutex); +            pdata->avd+=pdata->frametime*2*pdata->args.channels; +        } +    } +} diff --git a/recordmydesktop/src/encode_sound_buffer.c b/recordmydesktop/src/encode_sound_buffer.c index 85e277d..73f6d48 100644 --- a/recordmydesktop/src/encode_sound_buffer.c +++ b/recordmydesktop/src/encode_sound_buffer.c @@ -90,4 +90,32 @@ void *EncodeSoundBuffer(void *pdata){      pthread_exit(&errno);  } -  +void SyncEncodeSoundBuffer(ProgData *pdata,signed char *buff){ +    float **vorbis_buffer; +    int count=0,i,j; +    int sampread=pdata->periodsize/(2*pdata->args.channels); +    vorbis_buffer=vorbis_analysis_buffer(&pdata->enc_data->m_vo_dsp,sampread); +    for(i=0;i<sampread;i++){ +        for(j=0;j<pdata->args.channels;j++){ +            vorbis_buffer[j][i]=((buff[count+1]<<8)| +                                    (0x00ff&(int)buff[count]))/32768.f; +            count+=2; +        } +    } + +    vorbis_analysis_wrote(&pdata->enc_data->m_vo_dsp,sampread); + +    while(vorbis_analysis_blockout(&pdata->enc_data->m_vo_dsp,&pdata->enc_data->m_vo_block)==1){ +         +        vorbis_analysis(&pdata->enc_data->m_vo_block,NULL); +        vorbis_bitrate_addblock(&pdata->enc_data->m_vo_block); +         +        while(vorbis_bitrate_flushpacket(&pdata->enc_data->m_vo_dsp,&pdata->enc_data->m_ogg_pckt2)){ +            pthread_mutex_lock(&pdata->libogg_mutex); +            ogg_stream_packetin(&pdata->enc_data->m_ogg_vs,&pdata->enc_data->m_ogg_pckt2); +            pthread_mutex_unlock(&pdata->libogg_mutex); +        } +    } +    pdata->avd-=pdata->periodtime; +} + diff --git a/recordmydesktop/src/init_encoder.c b/recordmydesktop/src/init_encoder.c index ed1ad53..84d36aa 100644 --- a/recordmydesktop/src/init_encoder.c +++ b/recordmydesktop/src/init_encoder.c @@ -27,7 +27,35 @@  #include <recordmydesktop.h> -void InitEncoder(ProgData *pdata,EncData *enc_data_t){ +int IncrementalNaming(char **name){ +    struct stat buff; +    char *base_name; +    int i=0; +    base_name=malloc(strlen(*name)); +    strcpy(base_name,*name); + +    //this will go on an endless loop if you have 65536? files with the same name +    //or it will crash and die.anyone interested in trying ? +    while (stat(*name,&buff)==0){ +        //create new name +        char *tname=malloc(strlen(*name)+10); +        char numbuf[8]; + +        strcpy(tname,base_name); +        strcat(tname,"."); +        I16TOA((++i),numbuf) +        strcat(tname,numbuf); +        //save new name +        free(*name); +        *name=malloc(strlen(tname)+1); +        strcpy(*name,tname); +        free(tname); +    } +    free(base_name); +    return 0; +} + +void InitEncoder(ProgData *pdata,EncData *enc_data_t,int buffer_ready){      int y1,y2;      (pdata)->enc_data=enc_data_t;      srand(time(NULL)); @@ -38,12 +66,13 @@ void InitEncoder(ProgData *pdata,EncData *enc_data_t){      ogg_stream_init(&(enc_data_t)->m_ogg_ts,y1);      if(!pdata->args.nosound)          ogg_stream_init(&(enc_data_t)->m_ogg_vs,y2); -     +    if(!pdata->args.overwrite) +        IncrementalNaming(&(pdata)->args.filename);      (enc_data_t)->fp=fopen((pdata)->args.filename,"w");      if((enc_data_t)->fp==NULL){          fprintf(stderr,"Cannot open file %s for writting!\n",(pdata)->args.filename);          exit(13); -    }     +    }      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; @@ -87,7 +116,7 @@ void InitEncoder(ProgData *pdata,EncData *enc_data_t){          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){ @@ -109,16 +138,16 @@ void InitEncoder(ProgData *pdata,EncData *enc_data_t){          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);  +        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(2);          }          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);      } @@ -150,23 +179,24 @@ void InitEncoder(ProgData *pdata,EncData *enc_data_t){      } +    if(!buffer_ready){ +        (enc_data_t)->yuv.y=(unsigned char *)malloc((enc_data_t)->m_th_inf.height*(enc_data_t)->m_th_inf.width); +        (enc_data_t)->yuv.u=(unsigned char *)malloc((enc_data_t)->m_th_inf.height*(enc_data_t)->m_th_inf.width/4); +        (enc_data_t)->yuv.v=(unsigned char *)malloc((enc_data_t)->m_th_inf.height*(enc_data_t)->m_th_inf.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.y=(unsigned char *)malloc((enc_data_t)->m_th_inf.height*(enc_data_t)->m_th_inf.width); -    (enc_data_t)->yuv.u=(unsigned char *)malloc((enc_data_t)->m_th_inf.height*(enc_data_t)->m_th_inf.width/4); -    (enc_data_t)->yuv.v=(unsigned char *)malloc((enc_data_t)->m_th_inf.height*(enc_data_t)->m_th_inf.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; -    (enc_data_t)->x_offset=(enc_data_t)->m_th_inf.offset_x; -    (enc_data_t)->y_offset=(enc_data_t)->m_th_inf.offset_y; +        (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; +        (enc_data_t)->x_offset=(enc_data_t)->m_th_inf.offset_x; +        (enc_data_t)->y_offset=(enc_data_t)->m_th_inf.offset_y; +    }      theora_info_clear(&(enc_data_t)->m_th_inf);  } -  + diff --git a/recordmydesktop/src/load_cache.c b/recordmydesktop/src/load_cache.c new file mode 100644 index 0000000..cd09956 --- /dev/null +++ b/recordmydesktop/src/load_cache.c @@ -0,0 +1,187 @@ +/********************************************************************************* +*                             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 johnvarouhakis@gmail.com              * +**********************************************************************************/ + + +#include <recordmydesktop.h> + +void LoadBlock(unsigned char *dest,unsigned char *source,int blockno,int width, int height,int divisor){ +    int j, +        block_i=blockno/divisor,//place on the grid +        block_k=blockno%divisor; + +    for(j=0;j<height/divisor;j++)//we copy rows +        memcpy( &dest[block_i*(width*height/divisor)+j*width+block_k*width/divisor], +                &source[j*width/divisor], +                width/divisor); +} + + + +void *LoadCache(void *pdata){ + +    yuv_buffer *yuv=&((ProgData *)pdata)->enc_data->yuv; +    gzFile *ifp=((ProgData *)pdata)->cache_data->ifp; +    FILE *afp=((ProgData *)pdata)->cache_data->afp; +    FrameHeader fheader; +    CachedFrame frame; +    signed char *sound_data=(signed char *)malloc(((ProgData *)pdata)->periodsize); + +    int j=0, +        audio_end=0, +        extra_frames=0,//total number of duplicated frames +        missing_frames=0,//if this is found >0 current run will not load +                        //a frame but it will proccess the previous +        thread_exit=0,//0 success, -1 couldn't find files,1 couldn't remove +        divisor=16, +        blockszy=0,//size of y plane block in bytes +        blockszuv=0;//size of u,v plane blocks in bytes +    //we allocate the frame that we will use +    INIT_FRAME(&frame,&fheader,yuv) +    //and the we open our files +    ifp=gzopen(((ProgData *)pdata)->cache_data->imgdata,"rb"); +    if(ifp==NULL){ +        thread_exit=-1; +        pthread_exit(&thread_exit); +    } + +    if(!((ProgData *)pdata)->args.nosound){ +        afp=fopen(((ProgData *)pdata)->cache_data->audiodata,"rb"); +        if(afp==NULL){ +            thread_exit=-1; +            pthread_exit(&thread_exit); +        } +    } +    //these two are likely to be the same, but not guaranteed, especially on +    //low resolutions +    blockszy=(yuv->y_width*yuv->y_height )/pow(divisor,2); +    blockszuv=(yuv->uv_width*yuv->uv_height)/pow(divisor/2,2); + +    //this will be used now to define if we proccess audio or video +    //on any given loop. +    ((ProgData *)pdata)->avd=0; +    //If sound finishes first,we go on with the video. +    //If video ends we will do one more run to flush audio in the ogg file + +    while(((ProgData *)pdata)->running){ +        //video load and encoding +        if(((ProgData *)pdata)->avd<=0 || ((ProgData *)pdata)->args.nosound || audio_end){ +            if(missing_frames>0){ +                extra_frames++; +                missing_frames--; +                SyncEncodeImageBuffer((ProgData *)pdata); +            } +            else if(gzread(ifp,frame.header,sizeof(FrameHeader))==sizeof(FrameHeader)){ +                //sync +                missing_frames+=frame.header->current_total-(extra_frames+frame.header->frameno); +                fprintf(stdout,"\r[%d%%] ", +                ((frame.header->frameno+extra_frames)*100)/frames_total); + +                fflush(stdout); +                if( (frame.header->Ynum<=pow(divisor,2)) && +                    (frame.header->Unum<=pow(divisor/2,2)) && +                    (frame.header->Vnum<=pow(divisor/2,2)) && +                    (gzread(ifp,frame.YBlocks,frame.header->Ynum)==frame.header->Ynum) && +                    (gzread(ifp,frame.UBlocks,frame.header->Unum)==frame.header->Unum) && +                    (gzread(ifp,frame.VBlocks,frame.header->Vnum)==frame.header->Vnum) && +                    (gzread(ifp,frame.YData,blockszy*frame.header->Ynum)==blockszy*frame.header->Ynum) && +                    (gzread(ifp,frame.UData,(blockszuv*frame.header->Unum))==(blockszuv*frame.header->Unum)) && +                    (gzread(ifp,frame.VData,(blockszuv*frame.header->Vnum))==(blockszuv*frame.header->Vnum))){ +                        //load the blocks for each buffer +                        if(frame.header->Ynum) +                            for(j=0;j<frame.header->Ynum;j++) +                                LoadBlock(  yuv->y, +                                            &frame.YData[j*blockszy], +                                            frame.YBlocks[j], +                                            yuv->y_width, +                                            yuv->y_height, +                                            divisor); +                        if(frame.header->Unum) +                            for(j=0;j<frame.header->Unum;j++) +                                LoadBlock(  yuv->u, +                                            &frame.UData[j*blockszuv], +                                            frame.UBlocks[j], +                                            yuv->uv_width, +                                            yuv->uv_height, +                                            divisor/2); +                        if(frame.header->Vnum) +                            for(j=0;j<frame.header->Vnum;j++) +                                LoadBlock(  yuv->v, +                                            &frame.VData[j*blockszuv], +                                            frame.VBlocks[j], +                                            yuv->uv_width, +                                            yuv->uv_height, +                                            divisor/2); +                        //encode. This is not made in a thread since now blocking is not a problem +                        //and this way sync problems can be avoided more easily. +                        SyncEncodeImageBuffer((ProgData *)pdata); +                } +                else{ +                    raise(SIGINT); +                    continue; +                } +            } +            else{ +                raise(SIGINT); +                continue; +            } +        } +        //audio load and encoding +        else{ +            if(!audio_end){ +                int nbytes=fread(sound_data,((ProgData *)pdata)->periodsize,1,afp); +                if(nbytes<=0) +                    audio_end=1; +                else +                    SyncEncodeSoundBuffer((ProgData *)pdata,sound_data); +            } +        } +    } +    fprintf(stdout,"\n"); +    CLEAR_FRAME(&frame) +    free(sound_data); +    gzclose(ifp); + +    if(remove(((ProgData *)pdata)->cache_data->imgdata)){ +        fprintf(stderr,"Couldn't remove temporary file %s",((ProgData *)pdata)->cache_data->imgdata); +        thread_exit=1; +    } +    if(!((ProgData *)pdata)->args.nosound){ +        fclose(afp); +        if(remove(((ProgData *)pdata)->cache_data->audiodata)){ +            fprintf(stderr,"Couldn't remove temporary file %s",((ProgData *)pdata)->cache_data->audiodata); +            thread_exit=1; +        } +    } +    if(remove(((ProgData *)pdata)->cache_data->projname)){ +        fprintf(stderr,"Couldn't remove temporary directory %s",((ProgData *)pdata)->cache_data->projname); +        thread_exit=1; +    } + +    pthread_exit(&thread_exit); + + +} + diff --git a/recordmydesktop/src/parseargs.c b/recordmydesktop/src/parseargs.c index 5280cf2..c7d20fd 100644 --- a/recordmydesktop/src/parseargs.c +++ b/recordmydesktop/src/parseargs.c @@ -32,11 +32,11 @@ 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-display DISPLAY| -x X| -y Y|-width N| -height N| -fps N(number>0)| --on-the-fly-encoding|\n"      "\t -v_quality n| -s_quality n| -v_bitrate n| --no-framedrop| -dummy-cursor color|\n"      "\t --no-cursor| -freq N(number>0)| -channels N(number>0)| -device SOUND_DEVICE|\n" -    "\t --nosound| --with-shared| --no-cond-shared| -shared-threshold n| --full-shots|\n" -    "\t --quick-subsampling| --scshot| -scale-shot N| -o filename]^filename\n\n\n" +    "\t --no-sound| --with-shared| --no-cond-shared| -shared-threshold n| --full-shots|\n" +    "\t --quick-subsampling| -workdir DIR| --zero-compression| --no-wm-check| --overwite| -o filename]^filename\n\n\n"      "General Options:\n"      "\t-h or --help\t\tPrint this help and exit.\n" @@ -51,7 +51,7 @@ int ParseArgs(int argc,char **argv,ProgArgs *arg_return){      "\t-height N\t\tHeight of recorded window.\n\n"      "\t-dummy-cursor color\tColor of the dummy cursor [black|white]\n" -    "\t--no-cursor\tDisable drawing of the cursor.\n" +    "\t--no-cursor\t\tDisable drawing of the cursor.\n"      "\t--with-shared\t\tEnable usage of MIT-shared memory extension at all times.\n"      "\t--no-cond-shared\tDo not use the MIT-shared memory extension when aquiring large areas.\n"      "\t-shared-threshold n\tThreshold over which shared memory is used(default 75).\n" @@ -63,18 +63,22 @@ int ParseArgs(int argc,char **argv,ProgArgs *arg_return){      "\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" +    "\t--no-sound\t\tDo not record sound.\n\n"      "Encoding Options\n" +    "\t--on-the-fly-encoding\tEncode the audio-video data, while recording.\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--drop-frames\t\tAllow theora encoder to drop frames.\n"      "\t-s_quality n\t\tDesired audio quality(-1 to 10).\n\n"      "Misc Options:\n" +    "\t--no-wm-check\tDo not try to detect the window manager(and set options according to it)\n" +    "\t--zero-compression\tImage data are always cached uncompressed.\n" +    "\t-workdir DIR\t\tLocation where a temporary directory will be created to hold project files(default $HOME).\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--overwrite\t\tIf there is already a file with the same name, delete it\n" +    "\t\t\t\t(default is to add a number postfix to the new one).\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"; @@ -279,7 +283,7 @@ int ParseArgs(int argc,char **argv,ProgArgs *arg_return){                      return 1;                  }                  arg_return->have_dummy_cursor=1; -                arg_return->xfixes_cursor=0;     +                arg_return->xfixes_cursor=0;              }              else{                  fprintf(stderr,"Argument Usage: -dummy-cursor [black|white]\n"); @@ -353,37 +357,31 @@ int ParseArgs(int argc,char **argv,ProgArgs *arg_return){              }              i++;          } -        else if(!strcmp(argv[i],"-scale-shot")){ +        else if(!strcmp(argv[i],"-device")){              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; -                } +                free(arg_return->device); +                arg_return->device=malloc(strlen(argv[i+1])+1); +                strcpy(arg_return->device,argv[i+1]);              }              else{ -                fprintf(stderr,"Argument Usage: -scale-shot N(0<number<64,power of 2)\n"); +                fprintf(stderr,"Argument Usage: -device SOUND_DEVICE\n");                  return 1;              }              i++;          } -        else if(!strcmp(argv[i],"-device")){ +        else if(!strcmp(argv[i],"-workdir")){              if(i+1<argc){ -                free(arg_return->device); -                arg_return->device=malloc(strlen(argv[i+1])+1); -                strcpy(arg_return->device,argv[i+1]); +                free(arg_return->workdir); +                arg_return->workdir=malloc(strlen(argv[i+1])+1); +                strcpy(arg_return->workdir,argv[i+1]);              }              else{ -                fprintf(stderr,"Argument Usage: -device SOUND_DEVICE\n"); +                fprintf(stderr,"Argument Usage: -workdir DIR\n");                  return 1;              }              i++;          } -        else if(!strcmp(argv[i],"--nosound")) +        else if(!strcmp(argv[i],"--no-sound"))              arg_return->nosound=1;          else if(!strcmp(argv[i],"--drop-frames"))              arg_return->dropframes=1; @@ -397,13 +395,19 @@ int ParseArgs(int argc,char **argv,ProgArgs *arg_return){              arg_return->full_shots=1;              arg_return->nocondshared=1;          } -        else if(!strcmp(argv[i],"--scshot")){ -            arg_return->scshot=1; -            arg_return->nocondshared=1; -        }          else if(!strcmp(argv[i],"--quick-subsampling")){              arg_return->no_quick_subsample=0;          } +        else if(!strcmp(argv[i],"--on-the-fly-encoding")){ +            arg_return->encOnTheFly=1; +        } +        else if(!strcmp(argv[i],"--overwrite")) +            arg_return->overwrite=1; +        else if(!strcmp(argv[i],"--no-wm-check")) +            arg_return->nowmcheck=1; +        else if(!strcmp(argv[i],"--zero-compression")){ +            arg_return->zerocompression=1; +        }          else if(!strcmp(argv[i],"--help")||!strcmp(argv[i],"-h")){              fprintf(stderr,"%s",usage);              return 1; @@ -415,7 +419,7 @@ int ParseArgs(int argc,char **argv,ProgArgs *arg_return){          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 index 5433bbc..9bf7ee2 100644 --- a/recordmydesktop/src/poll_damage.c +++ b/recordmydesktop/src/poll_damage.c @@ -28,18 +28,48 @@  #include <recordmydesktop.h>  void *PollDamage(void *pdata){ - -    Damage damage; +    Window root_return, +           parent_return, +           *children; +    unsigned int i, +                 nchildren, +                 inserts=0;      XEvent event; -    int inserts=0; -     -     -    damage= XDamageCreate( ((ProgData *)pdata)->dpy, ((ProgData *)pdata)->brwin.windowid, XDamageReportRawRectangles); + +    XSelectInput (((ProgData *)pdata)->dpy,((ProgData *)pdata)->specs.root, SubstructureNotifyMask); + +    XQueryTree (((ProgData *)pdata)->dpy, +                ((ProgData *)pdata)->specs.root, +                &root_return, +                &parent_return, +                &children, +                &nchildren); + +    for (i = 0; i < nchildren; i++){ +        XWindowAttributes attribs; +        if (XGetWindowAttributes (((ProgData *)pdata)->dpy,children[i],&attribs)){ +            if (!attribs.override_redirect && attribs.depth==((ProgData *)pdata)->specs.depth) +                XDamageCreate (((ProgData *)pdata)->dpy, children[i],XDamageReportRawRectangles); +        } +    } + +    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 +        //30/10/2006 : when and why did I write the above line? what did I mean?          XNextEvent(((ProgData *)pdata)->dpy,&event); -        if(event.type == ((ProgData *)pdata)->damage_event + XDamageNotify ){ +        if (event.type == MapNotify ){ +            XWindowAttributes attribs; +            if (!((XMapEvent *)(&event))->override_redirect && XGetWindowAttributes (((ProgData *)pdata)->dpy, +                                        event.xcreatewindow.window, +                                        &attribs)){ +                if (!attribs.override_redirect && attribs.depth==((ProgData *)pdata)->specs.depth) +                    XDamageCreate (((ProgData *)pdata)->dpy,event.xcreatewindow.window,XDamageReportRawRectangles); +            } +        } +        else if(event.type == ((ProgData *)pdata)->damage_event + XDamageNotify ){              XDamageNotifyEvent *e =(XDamageNotifyEvent *)( &event );              WGeometry wgeom;              CLIP_EVENT_AREA(e,&(((ProgData *)pdata)->brwin),&wgeom); @@ -51,6 +81,7 @@ void *PollDamage(void *pdata){                  pthread_mutex_unlock(&((ProgData *)pdata)->list_mutex[tlist_sel]);              }          } +      }      pthread_exit(&errno);  } diff --git a/recordmydesktop/src/recordmydesktop.c b/recordmydesktop/src/recordmydesktop.c index 6b9e003..77c08ce 100644 --- a/recordmydesktop/src/recordmydesktop.c +++ b/recordmydesktop/src/recordmydesktop.c @@ -51,12 +51,16 @@ int main(int argc,char **argv){      }      else{          EncData enc_data; +        CacheData cache_data;          pthread_t   poll_damage_t,                      image_capture_t,                      image_encode_t, +                    image_cache_t,                      sound_capture_t,                      sound_encode_t, -                    flush_to_ogg_t; +                    sound_cache_t, +                    flush_to_ogg_t, +                    load_cache_t;          XShmSegmentInfo shminfo;          int i; @@ -67,47 +71,68 @@ int main(int argc,char **argv){          }          if(SetBRWindow(pdata.dpy,&pdata.brwin,&pdata.specs,&pdata.args))              exit(11); -         + + +        //check if we are under compiz or beryl,in which case we must enable full-shots +        //and with it use of shared memory.User can override this +        pdata.window_manager=((pdata.args.nowmcheck)?NULL:rmdWMCheck(pdata.dpy,pdata.specs.root)); +        if(pdata.window_manager==NULL){ +            fprintf(stderr,"Not taking window manager into account.\n"); +        } +        //Right now only wm's that I know of performing 3d compositing are beryl and compiz. +        //No, the blue screen in metacity doesn't count :) +        //names can be compiz for compiz and beryl/beryl-co/beryl-core for beryl(so it's strncmp ) +        else if(!strcmp(pdata.window_manager,"compiz") || !strncmp(pdata.window_manager,"beryl",5)){ +            fprintf(stderr,"\nDetected 3d compositing window manager.\n" +                           "Reverting to full screen capture at every frame.\n" +                           "To disable this check run with --no-wm-check\n" +                           "(though that is not advised, since it will probably produce faulty results).\n\n"); +            pdata.args.full_shots=1; +            pdata.args.noshared=0; +            pdata.args.nocondshared=1; +        } +          QueryExtensions(pdata.dpy,&pdata.args,&pdata.damage_event, &pdata.damage_error); -        //init data + +        //init data +          //these are globals, look for them at the header          frames_total=frames_lost=encoder_busy=capture_busy=0; -        if(!pdata.args.scshot){ -            fprintf(stderr,"Initializing...\n"); -            MakeMatrices(); -            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; -            } +        fprintf(stderr,"Initializing...\n"); +        MakeMatrices(); +        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)) + +        if((pdata.args.noshared))              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.libogg_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=pdata.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)){ + +        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.libogg_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=pdata.avd=0; +        pdata.running=1; +        time_cond=&pdata.time_cond; +        pause_cond=&pdata.pause_cond; +        Running=&pdata.running; + +        if((pdata.args.noshared)){              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); @@ -115,7 +140,7 @@ int main(int argc,char **argv){                          pdata.brwin.rgeom.width,pdata.brwin.rgeom.height);          }          if((!pdata.args.noshared)||(!pdata.args.nocondshared)){ -            pdata.shimage=XShmCreateImage (pdata.dpy,pdata.specs.visual,pdata.specs.depth,ZPixmap,pdata.datash,  +            pdata.shimage=XShmCreateImage (pdata.dpy,pdata.specs.visual,pdata.specs.depth,ZPixmap,pdata.datash,                           &shminfo, pdata.brwin.rgeom.width,pdata.brwin.rgeom.height);              shminfo.shmid = shmget (IPC_PRIVATE,                                      pdata.shimage->bytes_per_line * pdata.shimage->height, @@ -128,18 +153,6 @@ int main(int argc,char **argv){              }              XShmGetImage(pdata.dpy,pdata.specs.root,pdata.shimage,pdata.brwin.rgeom.x,pdata.brwin.rgeom.y,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){ @@ -147,12 +160,18 @@ int main(int argc,char **argv){                  exit(3);              }          } -        InitEncoder(&pdata,&enc_data); + +        if(pdata.args.encOnTheFly) +            InitEncoder(&pdata,&enc_data,0); +        else +            InitCacheData(&pdata,&enc_data,&cache_data); +          for(i=0;i<(pdata.enc_data->yuv.y_width*pdata.enc_data->yuv.y_height);i++)              pdata.enc_data->yuv.y[i]=0;          for(i=0;i<(pdata.enc_data->yuv.uv_width*pdata.enc_data->yuv.uv_height);i++){              pdata.enc_data->yuv.v[i]=pdata.enc_data->yuv.u[i]=127;          } +          if((pdata.args.nocondshared)&&(!pdata.args.noshared)){              if(pdata.args.no_quick_subsample){                  UPDATE_YUV_BUFFER_IM_AVG((&pdata.enc_data->yuv),((unsigned char*)pdata.shimage->data), @@ -189,50 +208,120 @@ int main(int argc,char **argv){          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.encOnTheFly) +            pthread_create(&image_encode_t,NULL,EncodeImageBuffer,(void *)&pdata); +        else +            pthread_create(&image_cache_t,NULL,CacheImageBuffer,(void *)&pdata); +          if(!pdata.args.nosound){              pthread_create(&sound_capture_t,NULL,CaptureSound,(void *)&pdata); -            pthread_create(&sound_encode_t,NULL,EncodeSoundBuffer,(void *)&pdata); +            if(pdata.args.encOnTheFly) +                pthread_create(&sound_encode_t,NULL,EncodeSoundBuffer,(void *)&pdata); +            else +                pthread_create(&sound_cache_t,NULL,CacheSoundBuffer,(void *)&pdata);          } -        pthread_create(&flush_to_ogg_t,NULL,FlushToOgg,(void *)&pdata); -         +        if(pdata.args.encOnTheFly) +            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); +        //if no damage events have been received the thread will get stuck +        pthread_cond_broadcast(&pdata.image_buffer_ready); +        if(pdata.args.encOnTheFly) +            pthread_join(image_encode_t,NULL); +        else +            pthread_join(image_cache_t,NULL); + +          fprintf(stderr,".");          if(!pdata.args.nosound){              int *snd_exit; -            pthread_join(sound_capture_t,(&snd_exit)); +            pthread_join(sound_capture_t,(void *)(&snd_exit));              fprintf(stderr,"."); -            if(!(*snd_exit)) -                pthread_join(sound_encode_t,NULL); + +            if(pdata.args.encOnTheFly){ +                if(!(*snd_exit)) +                    pthread_join(sound_encode_t,NULL); +                else{ +                    pthread_cancel(sound_encode_t); +                    exit_status=*snd_exit; +                } +            }              else{ -                pthread_cancel(sound_encode_t); -                exit_status=*snd_exit; +                if(!(*snd_exit)) +                    pthread_join(sound_cache_t,NULL); +                else{ +                    pthread_cancel(sound_cache_t); +                    exit_status=*snd_exit; +                }              } -            fprintf(stderr,".");          }          else              fprintf(stderr,".."); -        pthread_join(flush_to_ogg_t,NULL); + +        if(pdata.args.encOnTheFly) +            pthread_join(flush_to_ogg_t,NULL);          fprintf(stderr,"."); +          if(!pdata.args.full_shots)              pthread_join(poll_damage_t,NULL); + +          fprintf(stderr,".");          if((!pdata.args.noshared)||(!pdata.args.nocondshared)){              XShmDetach (pdata.dpy, &shminfo); -//             XDestroyImage (pdata.image);              shmdt (&shminfo.shmaddr);              shmctl (shminfo.shmid, IPC_RMID, 0);          }          fprintf(stderr,"\n"); + + +        //Now that we are done with recording we cancel the timer +        CancelTimer(); + +/**               Encoding                          */ +        if(!pdata.args.encOnTheFly){ +            if(!Aborted){ +                fprintf(stderr,"Encoding started!\nThis may take several minutes.\n" +                "Pressing Ctrl-C will cancel the procedure (resuming will not be possible, but\n" +                "any portion of the video, which is already encoded won't be deleted).\n" +                "Please wait...\n"); +                pdata.running=1; +                InitEncoder(&pdata,&enc_data,1); +                //load encoding and flushing threads +                if(!pdata.args.nosound){ +                    //before we start loading again +                    //we need to free any left-overs +                    while(pdata.sound_buffer!=NULL){ +                        free(pdata.sound_buffer->data); +                        pdata.sound_buffer=pdata.sound_buffer->next; +                    } +                } +                pthread_create(&flush_to_ogg_t,NULL,FlushToOgg,(void *)&pdata); + + +                //start loading image and audio +                pthread_create(&load_cache_t,NULL,LoadCache,(void *)&pdata); + +                //join and finish +                pthread_join(load_cache_t,NULL); +                fprintf(stderr,"Encoding finished!\nWait a moment please...\n"); +                pthread_join(flush_to_ogg_t,NULL); +                fprintf(stderr,"Done!!!\n"); +            } +        } +/**@_______________________________________________@*/ + +        //This can happen earlier, but in some cases it might get stuck. +        //So we must make sure the recording is not wasted.          XCloseDisplay(pdata.dpy); +          if(Aborted){              if(remove(pdata.args.filename)){                  perror("Error while removing file:\n"); diff --git a/recordmydesktop/src/register_callbacks.c b/recordmydesktop/src/register_callbacks.c index 677fc75..08c528f 100644 --- a/recordmydesktop/src/register_callbacks.c +++ b/recordmydesktop/src/register_callbacks.c @@ -56,6 +56,16 @@ void SetRunning(int signum){          Aborted=1;  } +void CancelTimer(void){ +    struct itimerval value; +    value.it_interval.tv_sec= +    value.it_value.tv_sec= +    value.it_interval.tv_usec= +    value.it_value.tv_usec=0; + +    setitimer(ITIMER_REAL,&value,NULL); +} +  void RegisterCallbacks(ProgArgs *args){      struct itimerval value; diff --git a/recordmydesktop/src/rmd_cache.c b/recordmydesktop/src/rmd_cache.c new file mode 100644 index 0000000..9cb76d5 --- /dev/null +++ b/recordmydesktop/src/rmd_cache.c @@ -0,0 +1,102 @@ +/********************************************************************************* +*                             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 johnvarouhakis@gmail.com              * +**********************************************************************************/ + +#include <recordmydesktop.h> + +void InitCacheData(ProgData *pdata,EncData *enc_data_t,CacheData *cache_data_t){ +    int width,height,offset_x,offset_y,pid; +    char pidbuf[8]; + +    //we set the buffer only since there's +    //no need to initialize the encoder from now. +    width=((pdata->brwin.rgeom.width + 15) >>4)<<4; +    height=((pdata->brwin.rgeom.height + 15) >>4)<<4; +    offset_x=((width-pdata->brwin.rgeom.width)/2)&~1; +    offset_y=((height-pdata->brwin.rgeom.height)/2)&~1; + +    (pdata)->enc_data=enc_data_t; + +    enc_data_t->yuv.y=(unsigned char *)malloc(height*width); +    enc_data_t->yuv.u=(unsigned char *)malloc(height*width/4); +    enc_data_t->yuv.v=(unsigned char *)malloc(height*width/4); +    enc_data_t->yuv.y_width=width; +    enc_data_t->yuv.y_height=height; +    enc_data_t->yuv.y_stride=width; + +    enc_data_t->yuv.uv_width=width/2; +    enc_data_t->yuv.uv_height=height/2; +    enc_data_t->yuv.uv_stride=width/2; +    enc_data_t->x_offset=offset_x; +    enc_data_t->y_offset=offset_y; + + +    //now we set the cache files +    (pdata)->cache_data=cache_data_t; + +    cache_data_t->workdir=(pdata->args).workdir; +    pid=getpid(); + +    I16TOA(pid,pidbuf) +    //names are stored relatively to current dir(i.e. no chdir) +    cache_data_t->projname=malloc(strlen(cache_data_t->workdir)+12+strlen(pidbuf)+3); +    //projname +    strcpy(cache_data_t->projname,cache_data_t->workdir); +    strcat(cache_data_t->projname,"/"); +    strcat(cache_data_t->projname,"rMD-session-"); +    strcat(cache_data_t->projname,pidbuf); +    strcat(cache_data_t->projname,"/"); +    //image data +    cache_data_t->imgdata=malloc(strlen(cache_data_t->projname)+11); +    strcpy(cache_data_t->imgdata,cache_data_t->projname); +    strcat(cache_data_t->imgdata,"img.out.gz"); +    //audio data +    cache_data_t->audiodata=malloc(strlen(cache_data_t->projname)+10); +    strcpy(cache_data_t->audiodata,cache_data_t->projname); +    strcat(cache_data_t->audiodata,"audio.pcm"); + +    //now that've got out buffers and our filenames we start +    //creating the needed files + +    if(mkdir(cache_data_t->projname,0777)){ +        fprintf(stderr,"Could not create temporary directory %s !!!\n",cache_data_t->projname); +        exit(13); +    } +    cache_data_t->ifp=gzopen(cache_data_t->imgdata,"wb0f"); +    if(cache_data_t->ifp==NULL){ +        fprintf(stderr,"Could not create temporary file %s !!!\n",cache_data_t->imgdata); +        exit(13); +    } +    if(!pdata->args.nosound){ +        cache_data_t->afp=fopen(cache_data_t->audiodata,"wb"); +        if(cache_data_t->afp==NULL){ +           fprintf(stderr,"Could not create temporary file %s !!!\n",cache_data_t->audiodata); +           exit(13); +        } +    } + +} + + diff --git a/recordmydesktop/src/wm_check.c b/recordmydesktop/src/wm_check.c new file mode 100644 index 0000000..ee6b0ba --- /dev/null +++ b/recordmydesktop/src/wm_check.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 johnvarouhakis@gmail.com              * +**********************************************************************************/ + + +#include <recordmydesktop.h> + +char *rmdWMCheck(Display *dpy,Window root){ + +    Window  *wm_child; +    Atom    nwm_atom, +            utf8_string, +            wm_name_atom, +            rt; +    unsigned long   nbytes, +                    nitems; + +    char *wm_name_str=NULL; +    int fmt; + +    utf8_string = XInternAtom(dpy, "UTF8_STRING", False); + +    nwm_atom =XInternAtom(dpy,"_NET_SUPPORTING_WM_CHECK",True); +    wm_name_atom =XInternAtom(dpy,"_NET_WM_NAME",True); + +    if(nwm_atom!=None && wm_name_atom!=None){ +        if(!((XGetWindowProperty(  dpy,root,nwm_atom,0,100, +                                False,XA_WINDOW, +                                &rt,&fmt,&nitems, &nbytes, +                                (unsigned char **)((void*)&wm_child)) +                                ==Success ) && +        (XGetWindowProperty( dpy,*wm_child,wm_name_atom,0,100, +                                False,utf8_string,&rt, +                                &fmt,&nitems, &nbytes, +                                (unsigned char **)((void*)&wm_name_str)) +                                ==Success ))){ +            fprintf(stderr,"Warning!!!\nYour window manager appears to be non-compliant!\n"); +        } +    } +    fprintf(stderr,"Your window manager appears to be %s\n\n", +                    ((wm_name_str!=NULL)?wm_name_str:"Uknown")); + + +    return wm_name_str; +} +  | 
