From ad5b05712d9ed8650fcc145373ac878f8e6b8829 Mon Sep 17 00:00:00 2001 From: iovar Date: Wed, 8 Nov 2006 16:26:44 +0000 Subject: Replaced all files with the ones in the rMD-exp module. (this should have been a branch not a module, but it's too late now. rMD-exp module will be purged but if anyone's interested on the not-so descriptive commit logs, they'll be on the attic. This is the start of the 0.3 branch. Files will will be tagged as v0_3_0 in a new branch before final release. Snapshot prior to this release has tagged as v0_2_7 in a new branch. All releases will be tagged and branched from now on. git-svn-id: https://recordmydesktop.svn.sourceforge.net/svnroot/recordmydesktop/trunk@153 f606c939-3180-4ac9-a4b8-4b8779d57d0a --- recordmydesktop/ChangeLog | 40 +-- recordmydesktop/README | 5 +- recordmydesktop/TODO | 5 +- recordmydesktop/configure.ac | 23 +- recordmydesktop/doc/recordmydesktop.1 | 96 +++++--- recordmydesktop/include/recordmydesktop.h | 397 ++++++++++++++++++++++++------ recordmydesktop/src/Makefile.am | 14 +- recordmydesktop/src/cache_audio.c | 67 +++++ recordmydesktop/src/cache_frame.c | 193 +++++++++++++++ recordmydesktop/src/encode_image_buffer.c | 20 +- recordmydesktop/src/encode_sound_buffer.c | 30 ++- recordmydesktop/src/init_encoder.c | 70 ++++-- recordmydesktop/src/load_cache.c | 187 ++++++++++++++ recordmydesktop/src/parseargs.c | 64 ++--- recordmydesktop/src/poll_damage.c | 47 +++- recordmydesktop/src/recordmydesktop.c | 211 +++++++++++----- recordmydesktop/src/register_callbacks.c | 10 + recordmydesktop/src/rmd_cache.c | 102 ++++++++ recordmydesktop/src/wm_check.c | 68 +++++ recordmydesktop/src/zpixmaptobmp.c | 78 ------ 20 files changed, 1385 insertions(+), 342 deletions(-) create mode 100644 recordmydesktop/src/cache_audio.c create mode 100644 recordmydesktop/src/cache_frame.c create mode 100644 recordmydesktop/src/load_cache.c create mode 100644 recordmydesktop/src/rmd_cache.c create mode 100644 recordmydesktop/src/wm_check.c delete mode 100644 recordmydesktop/src/zpixmaptobmp.c (limited to 'recordmydesktop') 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 @@ -267,6 +275,11 @@ Sound Options: 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). @@ -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 #include #include -#include +#include #include #include #include @@ -49,8 +49,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -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;ky[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;ky_width-width_tm;\ + datapi+=yuv->y_width-width_tm;\ + }\ + datapi=(unsigned int*)data+x_tm+y_tm*yuv->y_width;\ + for(k=0;ky_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;ky[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;ky_width-width_tm;\ + datapi+=yuv->y_width-width_tm;\ + }\ + datapi=(unsigned int*)data+x_tm+y_tm*yuv->y_width;\ + for(k=0;ky_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;ky[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;ky_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;ky[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;ky_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/cache_audio.c b/recordmydesktop/src/cache_audio.c new file mode 100644 index 0000000..7b17d7b --- /dev/null +++ b/recordmydesktop/src/cache_audio.c @@ -0,0 +1,67 @@ +/********************************************************************************* +* 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 +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; + + if(Paused){ + pthread_mutex_t tmut; + pthread_mutex_init(&tmut,NULL); + pthread_cond_wait(&((ProgData *)pdata)->pause_cond,&tmut); + } + + if(((ProgData *)pdata)->sound_buffer==NULL) + pthread_cond_wait(&((ProgData *)pdata)->sound_data_read,&smut); + + pthread_mutex_lock(&((ProgData *)pdata)->sound_buffer_mutex); + buff=((ProgData *)pdata)->sound_buffer; + //advance the list + ((ProgData *)pdata)->sound_buffer=((ProgData *)pdata)->sound_buffer->next; + pthread_mutex_unlock(&((ProgData *)pdata)->sound_buffer_mutex); + + 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 + + +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;jcache_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;jargs.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;javd+=((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;iargs.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 -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 + +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;jenc_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;jYnum;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;jUnum;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;jVnum;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+1scale_shot=num; - } - else{ - fprintf(stderr,"Argument Usage: -scale-shot N(0device); + arg_return->device=malloc(strlen(argv[i+1])+1); + strcpy(arg_return->device,argv[i+1]); } else{ - fprintf(stderr,"Argument Usage: -scale-shot N(0device); - 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 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 + +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 + +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; +} + diff --git a/recordmydesktop/src/zpixmaptobmp.c b/recordmydesktop/src/zpixmaptobmp.c deleted file mode 100644 index 0aa9477..0000000 --- a/recordmydesktop/src/zpixmaptobmp.c +++ /dev/null @@ -1,78 +0,0 @@ -/********************************************************************************* -* 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 - -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;ibytes_per_line/4;i+=scale){ - fwrite(&dtap[(i*4)+k*(imgz->bytes_per_line)],1,1,fpbmp); - fwrite(&dtap[(i*4)+k*(imgz->bytes_per_line)+1],1,1,fpbmp); - fwrite(&dtap[(i*4)+k*(imgz->bytes_per_line)+2],1,1,fpbmp); - } - } - - fclose(fpbmp); - return 0; -} - - -- cgit v1.2.3