From 9c05582a007788295d872172c5109ae9bccfcf68 Mon Sep 17 00:00:00 2001 From: iovar Date: Wed, 7 Feb 2007 18:44:02 +0000 Subject: Added support for recording audio through jack. libjack is dlopened so there's no runtime dependency on it. Ports must be connected at startup. New files: src/rmd_jack.c git-svn-id: https://recordmydesktop.svn.sourceforge.net/svnroot/recordmydesktop/trunk@273 f606c939-3180-4ac9-a4b8-4b8779d57d0a --- recordmydesktop/src/Makefile.am | 3 +- recordmydesktop/src/cache_audio.c | 72 ++++++++--- recordmydesktop/src/encode_sound_buffer.c | 114 +++++++++++----- recordmydesktop/src/get_frame.c | 1 - recordmydesktop/src/initialize_data.c | 85 ++++++++---- recordmydesktop/src/load_cache.c | 5 + recordmydesktop/src/opendev.c | 1 - recordmydesktop/src/parseargs.c | 82 ++++++++++-- recordmydesktop/src/recordmydesktop.c | 6 +- recordmydesktop/src/register_callbacks.c | 24 +++- recordmydesktop/src/rmd_jack.c | 208 ++++++++++++++++++++++++++++++ recordmydesktop/src/rmdthreads.c | 24 +++- 12 files changed, 523 insertions(+), 102 deletions(-) create mode 100644 recordmydesktop/src/rmd_jack.c (limited to 'recordmydesktop/src') diff --git a/recordmydesktop/src/Makefile.am b/recordmydesktop/src/Makefile.am index 98ddda2..879c7b7 100644 --- a/recordmydesktop/src/Makefile.am +++ b/recordmydesktop/src/Makefile.am @@ -27,7 +27,8 @@ recordmydesktop_SOURCES= recordmydesktop.c\ wm_check.c\ encode_cache.c\ rmdthreads.c\ - initialize_data.c + initialize_data.c\ + rmd_jack.c INCLUDES= $(all_includes) -I../include diff --git a/recordmydesktop/src/cache_audio.c b/recordmydesktop/src/cache_audio.c index bff4df2..7896df7 100644 --- a/recordmydesktop/src/cache_audio.c +++ b/recordmydesktop/src/cache_audio.c @@ -35,37 +35,67 @@ void *CacheSoundBuffer(ProgData *pdata){ pdata->args.channels; #else int framesize=pdata->args.channels<<1;//Always signed 16 bit data +#endif +#ifdef HAVE_JACK_H + void *jackbuf=NULL; + if(pdata->args.use_jack){ + framesize=sizeof(jack_default_audio_sample_t)* + pdata->jdata->nports; + jackbuf=malloc(framesize*pdata->jdata->buffersize); + } #endif while((pdata->running)){ - SndBuffer *buff; + SndBuffer *buff=NULL; if(Paused){ pthread_mutex_lock(&pause_mutex); pthread_cond_wait(&pdata->pause_cond,&pause_mutex); pthread_mutex_unlock(&pause_mutex); } - if(pdata->sound_buffer==NULL){ - pdata->v_enc_thread_waiting=1; - pthread_mutex_lock(&pdata->snd_buff_ready_mutex); - pthread_cond_wait(&pdata->sound_data_read, - &pdata->snd_buff_ready_mutex); - pthread_mutex_unlock(&pdata->snd_buff_ready_mutex); - pdata->v_enc_thread_waiting=0; + if(!pdata->args.use_jack){ + if(pdata->sound_buffer==NULL){ + pdata->v_enc_thread_waiting=1; + pthread_mutex_lock(&pdata->snd_buff_ready_mutex); + pthread_cond_wait(&pdata->sound_data_read, + &pdata->snd_buff_ready_mutex); + pthread_mutex_unlock(&pdata->snd_buff_ready_mutex); + pdata->v_enc_thread_waiting=0; + } + if(pdata->sound_buffer==NULL || !pdata->running){ + break; + } + pthread_mutex_lock(&pdata->sound_buffer_mutex); + buff=pdata->sound_buffer; + //advance the list + pdata->sound_buffer=pdata->sound_buffer->next; + pthread_mutex_unlock(&pdata->sound_buffer_mutex); + fwrite(buff->data,1,pdata->periodsize*framesize, + pdata->cache_data->afp); + free(buff->data); + free(buff); + } + else{ +#ifdef HAVE_JACK_H + if((*jack_ringbuffer_read_space_p)(pdata->jdata->sound_buffer)>= + (framesize*pdata->jdata->buffersize)){ + (*jack_ringbuffer_read_p)(pdata->jdata->sound_buffer, + jackbuf, + (framesize*pdata->jdata->buffersize)); + fwrite(jackbuf,1,(framesize*pdata->jdata->buffersize), + pdata->cache_data->afp); + } + else{ + pdata->v_enc_thread_waiting=1; + pthread_mutex_lock(&pdata->snd_buff_ready_mutex); + pthread_cond_wait(&pdata->sound_data_read, + &pdata->snd_buff_ready_mutex); + pthread_mutex_unlock(&pdata->snd_buff_ready_mutex); + pdata->v_enc_thread_waiting=0; + continue; + } +#endif } - if(pdata->sound_buffer==NULL || !pdata->running) - break; - - pthread_mutex_lock(&pdata->sound_buffer_mutex); - buff=pdata->sound_buffer; - //advance the list - pdata->sound_buffer=pdata->sound_buffer->next; - pthread_mutex_unlock(&pdata->sound_buffer_mutex); - fwrite(buff->data,1,pdata->periodsize*framesize, - pdata->cache_data->afp); pdata->avd-=pdata->periodtime; - - free(buff->data); - free(buff); } fclose(pdata->cache_data->afp); diff --git a/recordmydesktop/src/encode_sound_buffer.c b/recordmydesktop/src/encode_sound_buffer.c index c8a8781..7945567 100644 --- a/recordmydesktop/src/encode_sound_buffer.c +++ b/recordmydesktop/src/encode_sound_buffer.c @@ -29,43 +29,84 @@ void *EncodeSoundBuffer(ProgData *pdata){ int sampread=pdata->periodsize; - +#ifdef HAVE_JACK_H + void *jackbuf=NULL; + int framesize=sizeof(jack_default_audio_sample_t)* + pdata->jdata->nports; + if(pdata->args.use_jack){ + framesize=sizeof(jack_default_audio_sample_t)* + pdata->jdata->nports; + jackbuf=malloc(framesize*pdata->jdata->buffersize); + } +#endif pdata->v_encoding_clean=0; while((pdata->running)){ float **vorbis_buffer; int count=0,i,j; - SndBuffer *buff; + SndBuffer *buff=NULL; if(Paused){ pthread_mutex_lock(&pause_mutex); pthread_cond_wait(&pdata->pause_cond,&pause_mutex); pthread_mutex_unlock(&pause_mutex); } - - if(pdata->sound_buffer==NULL){ - pdata->v_enc_thread_waiting=1; - pthread_mutex_lock(&pdata->snd_buff_ready_mutex); - pthread_cond_wait(&pdata->sound_data_read, - &pdata->snd_buff_ready_mutex); - pthread_mutex_unlock(&pdata->snd_buff_ready_mutex); - pdata->v_enc_thread_waiting=0; + if(!pdata->args.use_jack){ + if(pdata->sound_buffer==NULL){ + pdata->v_enc_thread_waiting=1; + pthread_mutex_lock(&pdata->snd_buff_ready_mutex); + pthread_cond_wait(&pdata->sound_data_read, + &pdata->snd_buff_ready_mutex); + pthread_mutex_unlock(&pdata->snd_buff_ready_mutex); + pdata->v_enc_thread_waiting=0; + } + if(pdata->sound_buffer==NULL || !pdata->running) + break; + pthread_mutex_lock(&pdata->sound_buffer_mutex); + buff=pdata->sound_buffer; + //advance the list + pdata->sound_buffer=pdata->sound_buffer->next; + pthread_mutex_unlock(&pdata->sound_buffer_mutex); + + vorbis_buffer=vorbis_analysis_buffer(&pdata->enc_data->m_vo_dsp, + sampread); + + for(i=0;iargs.channels;j++){ + vorbis_buffer[j][i]=((buff->data[count+1]<<8)| + (0x00ff&(int)buff->data[count]))/ + 32768.f; + count+=2; + } + } + free(buff->data); + free(buff); } - if(pdata->sound_buffer==NULL || !pdata->running) - break; - pthread_mutex_lock(&pdata->sound_buffer_mutex); - buff=pdata->sound_buffer; - //advance the list - pdata->sound_buffer=pdata->sound_buffer->next; - pthread_mutex_unlock(&pdata->sound_buffer_mutex); - - vorbis_buffer=vorbis_analysis_buffer(&pdata->enc_data->m_vo_dsp, - sampread); - for(i=0;iargs.channels;j++){ - vorbis_buffer[j][i]=((buff->data[count+1]<<8)| - (0x00ff&(int)buff->data[count]))/32768.f; - count+=2; + else{ +#ifdef HAVE_JACK_H + if((*jack_ringbuffer_read_space_p)(pdata->jdata->sound_buffer)>= + (framesize*pdata->jdata->buffersize)){ + (*jack_ringbuffer_read_p)(pdata->jdata->sound_buffer, + jackbuf, + (framesize*pdata->jdata->buffersize)); + vorbis_buffer=vorbis_analysis_buffer(&pdata->enc_data->m_vo_dsp, + sampread); + for(j=0;jargs.channels;j++){ + for(i=0;iv_enc_thread_waiting=1; + pthread_mutex_lock(&pdata->snd_buff_ready_mutex); + pthread_cond_wait(&pdata->sound_data_read, + &pdata->snd_buff_ready_mutex); + pthread_mutex_unlock(&pdata->snd_buff_ready_mutex); + pdata->v_enc_thread_waiting=0; + continue; } +#endif } vorbis_analysis_wrote(&pdata->enc_data->m_vo_dsp,sampread); @@ -86,8 +127,7 @@ void *EncodeSoundBuffer(ProgData *pdata){ pdata->avd-=pdata->periodtime; - free(buff->data); - free(buff); + } pdata->v_encoding_clean=1; @@ -103,11 +143,23 @@ void SyncEncodeSoundBuffer(ProgData *pdata,signed char *buff){ int sampread=(buff!=NULL)?pdata->periodsize:0; vorbis_buffer=vorbis_analysis_buffer(&pdata->enc_data->m_vo_dsp,sampread); - for(i=0;iargs.use_jack){ + for(i=0;iargs.channels;j++){ + vorbis_buffer[j][i]=((buff[count+1]<<8)| + (0x00ff&(int)buff[count]))/ + 32768.f; + count+=2; + } + } + } + else{ for(j=0;jargs.channels;j++){ - vorbis_buffer[j][i]=((buff[count+1]<<8)| - (0x00ff&(int)buff[count]))/32768.f; - count+=2; + for(i=0;iargs.full_shots){ tlist_sel=pdata->list_selector; diff --git a/recordmydesktop/src/initialize_data.c b/recordmydesktop/src/initialize_data.c index d8f52c0..74d32cf 100644 --- a/recordmydesktop/src/initialize_data.c +++ b/recordmydesktop/src/initialize_data.c @@ -27,6 +27,22 @@ #include +#ifdef HAVE_LIBASOUND +void FixBufferSize(snd_pcm_uframes_t *buffsize){ + snd_pcm_uframes_t buffsize_t=*buffsize, +#else +void FixBufferSize(u_int32_t *buffsize){ + u_int32_t buffsize_t=*buffsize, +#endif + buffsize_ret=1; + while(buffsize_t>1){ + buffsize_t>>=1; + buffsize_ret<<=1; + } + fprintf(stderr,"Buffer size adjusted to %d from %d frames.\n", + (int)buffsize_ret,(int)*buffsize); +} + int InitializeData(ProgData *pdata, EncData *enc_data, CacheData *cache_data){ @@ -124,30 +140,56 @@ int InitializeData(ProgData *pdata, AllPlanes); } if(!pdata->args.nosound){ + if(!pdata->args.use_jack){ + FixBufferSize(&pdata->args.buffsize); #ifdef HAVE_LIBASOUND - pdata->sound_handle=OpenDev( pdata->args.device, - &pdata->args.channels, - &pdata->args.frequency, - &pdata->args.buffsize, - &pdata->periodsize, - &pdata->periodtime, - &pdata->hard_pause); - if(pdata->sound_handle==NULL){ + pdata->sound_handle=OpenDev( pdata->args.device, + &pdata->args.channels, + &pdata->args.frequency, + &pdata->args.buffsize, + &pdata->periodsize, + &pdata->periodtime, + &pdata->hard_pause); + if(pdata->sound_handle==NULL){ #else - pdata->sound_handle=OpenDev(pdata->args.device, - pdata->args.channels, - pdata->args.frequency); - pdata->periodtime=(1000000*pdata->args.buffsize)/ - ((pdata->args.channels<<1)*pdata->args.frequency); - //when using OSS periodsize serves as an alias of buffsize - pdata->periodsize=pdata->args.buffsize; - if(pdata->sound_handle<0){ + pdata->sound_handle=OpenDev(pdata->args.device, + pdata->args.channels, + pdata->args.frequency); + pdata->periodtime=(1000000*pdata->args.buffsize)/ + ((pdata->args.channels<<1)*pdata->args.frequency); + //when using OSS periodsize serves as an alias of buffsize + pdata->periodsize=pdata->args.buffsize; + if(pdata->sound_handle<0){ +#endif + fprintf(stderr,"Error while opening/configuring soundcard %s\n" + "Try running with the --no-sound or specify a " + "correct device.\n", + pdata->args.device); + return 3; + } + } + else{ +#ifdef HAVE_JACK_H + int jack_error=0; + pdata->jdata->port_names=pdata->args.jack_port_names; + pdata->jdata->nports=pdata->args.jack_nports; + pdata->jdata->snd_buff_ready_mutex=&pdata->snd_buff_ready_mutex; + pdata->jdata->sound_data_read=&pdata->sound_data_read; + pdata->jdata->capture_started=0; + + if((jack_error=StartJackClient(pdata->jdata))!=0) + return jack_error; + + pdata->args.buffsize=pdata->jdata->buffersize; + pdata->periodsize=pdata->args.buffsize; + pdata->args.frequency=pdata->jdata->frequency; + pdata->args.channels=pdata->jdata->nports; + pdata->periodtime=(1000000*pdata->args.buffsize)/ + pdata->args.frequency; +#else + fprintf(stderr,"Should not be here!\n"); + exit(-1); #endif - fprintf(stderr,"Error while opening/configuring soundcard %s\n" - "Try running with the --no-sound or specify a " - "correct device.\n", - pdata->args.device); - return 3; } } @@ -176,7 +218,6 @@ int InitializeData(ProgData *pdata, pdata->specs.depth); pdata->frametime=(1000000)/pdata->args.fps; - return 0; } diff --git a/recordmydesktop/src/load_cache.c b/recordmydesktop/src/load_cache.c index f75c375..78c81c5 100644 --- a/recordmydesktop/src/load_cache.c +++ b/recordmydesktop/src/load_cache.c @@ -137,6 +137,11 @@ void *LoadCache(ProgData *pdata){ pdata->args.channels;//audio frame size #else int framesize=pdata->args.channels<<1;//Always signed 16 bit data +#endif +#ifdef HAVE_JACK_H + if(pdata->args.use_jack) + framesize=sizeof(jack_default_audio_sample_t)* + pdata->jdata->nports; #endif signed char *sound_data=(signed char *)malloc(pdata->periodsize*framesize); diff --git a/recordmydesktop/src/opendev.c b/recordmydesktop/src/opendev.c index 7810ec6..ea6f436 100644 --- a/recordmydesktop/src/opendev.c +++ b/recordmydesktop/src/opendev.c @@ -109,7 +109,6 @@ snd_pcm_t *OpenDev( const char *pcm_dev, snd_pcm_hw_params_get_period_time(hwparams,periodtime,0); fprintf(stderr,"Recording on device %s is set to:\n%d channels at %dHz\n", pcm_dev,*channels,*frequency); - fprintf(stderr,"Buffer size set to %d frames.\n",(int)(*buffsize)); snd_pcm_prepare(mhandle); return mhandle; diff --git a/recordmydesktop/src/parseargs.c b/recordmydesktop/src/parseargs.c index 9cda42b..4643bf1 100644 --- a/recordmydesktop/src/parseargs.c +++ b/recordmydesktop/src/parseargs.c @@ -27,11 +27,26 @@ #include +void PrintConfig(void){ + fprintf(stderr,"\nrecordMyDesktop was compiled with" + " the following options:\n\n"); +#ifdef HAVE_JACK_H + fprintf(stdout,"Jack\t\t\t:Enabled\n"); +#else + fprintf(stdout,"Jack\t\t\t:Disabled\n"); +#endif +#ifdef HAVE_LIBASOUND + fprintf(stdout,"Default Audio Backend\t:ALSA\n"); +#else + fprintf(stdout,"Default Audio Backend\t:OSS\n"); +#endif + fprintf(stderr,"\n\n"); +} int ParseArgs(int argc,char **argv,ProgArgs *arg_return){ int i; char *usage="\nUsage:\n" - "\trecordmydesktop [-h| --help| --version|" + "\trecordmydesktop [-h| --help| --version| --print-config|" " -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)| --on-the-fly-encoding|\n" @@ -39,14 +54,17 @@ int ParseArgs(int argc,char **argv,ProgArgs *arg_return){ " -dummy-cursor color|\n" "\t --no-cursor| -freq N(number>0)| -channels N(number>0)|" " -buffer-size N(number>0)| -device SOUND_DEVICE|\n" - "\t --no-sound| --with-shared| --no-cond-shared| -shared-threshold n|" + "\t -use-jack port1 port2... portn| --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" - "\t--version\t\tPrint program version and exit.\n\n" + "\t--version\t\tPrint program version and exit.\n" + "\t--print-config\t\tPrint info about options " + "selected during compilation and exit.\n\n" "Image Options:\n" "\t-windowid id_of_window\tid of window to be recorded.\n" @@ -74,17 +92,19 @@ int ParseArgs(int argc,char **argv,ProgArgs *arg_return){ "\t-fps N(number>0.0)\tA positive number denoting desired framerate.\n\n" "Sound Options:\n" - "\t-channels N\t\tA positive number denoting" + "\t-channels N\t\t\tA positive number denoting" " desired sound channels in recording.\n" - "\t-freq N\t\t\tA positive number denoting desired sound frequency.\n" - "\t-buffer-size N\t\tA positive number denoting the desired" + "\t-freq N\t\t\t\tA positive number denoting desired sound frequency.\n" + "\t-buffer-size N\t\t\tA positive number denoting the desired" " sound buffer size(in frames)\n" - "\t-device SOUND_DEVICE\tSound device(default " + "\t-device SOUND_DEVICE\t\tSound device(default " DEFAULT_AUDIO_DEVICE ").\n" - "\t--no-sound\t\tDo not record sound.\n\n" + "\t-use-jack port1 port2... portn\tRecord audio from the specified\n" + "\t\t\t\t\tlist of space-separated jack ports.\n" + "\t--no-sound\t\t\tDo not record sound.\n\n" "Encoding Options\n" "\t--on-the-fly-encoding\tEncode the audio-video data, while recording.\n" @@ -446,6 +466,44 @@ int ParseArgs(int argc,char **argv,ProgArgs *arg_return){ } i++; } + else if(!strcmp(argv[i],"-use-jack")){ + if(i+1jack_nports=0; + while((kjack_nports++; + k++; + } + if(arg_return->jack_nports>0){ + arg_return->jack_port_names=malloc(sizeof(char*)* + arg_return->jack_nports); + for(k=i+1;kjack_nports;k++){ + arg_return->jack_port_names[k-i-1]= + malloc(strlen(argv[k])+1); + strcpy(arg_return->jack_port_names[k-i-1], + argv[k]); + } + i+=arg_return->jack_nports; + arg_return->use_jack=1; + } + else{ + fprintf(stderr,"Argument Usage: -use-jack port1" + " port2... portn\n"); + return 1; + } +#else + fprintf(stderr,"recordMyDesktop is not compiled" + " with Jack support!\n"); + return 1; +#endif + } + else{ + fprintf(stderr,"Argument Usage: -use-jack port1" + " port2... portn\n"); + return 1; + } + } else if(!strcmp(argv[i],"--no-sound")) arg_return->nosound=1; else if(!strcmp(argv[i],"--drop-frames")) @@ -475,11 +533,15 @@ int ParseArgs(int argc,char **argv,ProgArgs *arg_return){ } else if(!strcmp(argv[i],"--help")||!strcmp(argv[i],"-h")){ fprintf(stderr,"%s",usage); - return 1; + exit(0); } else if(!strcmp(argv[i],"--version")){ fprintf(stderr,"recordMyDesktop v%s\n\n",VERSION); - return 1; + exit(0); + } + else if(!strcmp(argv[i],"--print-config")){ + PrintConfig(); + exit(0); } else{ fprintf(stderr,"\n\tError parsing arguments.\n\t" diff --git a/recordmydesktop/src/recordmydesktop.c b/recordmydesktop/src/recordmydesktop.c index c832a3e..f30485c 100644 --- a/recordmydesktop/src/recordmydesktop.c +++ b/recordmydesktop/src/recordmydesktop.c @@ -30,6 +30,7 @@ int main(int argc,char **argv){ ProgData pdata; + int exit_status=0; if(XInitThreads ()==0){ fprintf(stderr,"Couldn't initialize thread support!\n"); @@ -52,7 +53,10 @@ int main(int argc,char **argv){ else{ EncData enc_data; CacheData cache_data; - +#ifdef HAVE_JACK_H + JackData jdata; + pdata.jdata=&jdata; +#endif QUERY_DISPLAY_SPECS(pdata.dpy,&pdata.specs); if((pdata.specs.depth!=32)&& (pdata.specs.depth!=24)&& diff --git a/recordmydesktop/src/register_callbacks.c b/recordmydesktop/src/register_callbacks.c index 7f8d04f..2c5a8a3 100644 --- a/recordmydesktop/src/register_callbacks.c +++ b/recordmydesktop/src/register_callbacks.c @@ -32,14 +32,16 @@ void SetExpired(int signum){ if(capture_busy){ frames_lost++; } +/*FIXME */ +//This is not safe. +//cond_var signaling must move away from signal handlers +//alltogether (JackCapture, SetExpired, SetPaused). +//Better would be a set of pipes for each of these. +//The callback should write on the pipe and the main thread +//should perform a select over the fd's, signaling afterwards the +//appropriate cond_var. pthread_mutex_lock(&time_mutex); - pthread_cond_broadcast(time_cond); //sig handlers should - //not call this func - //could be a set_expired - // and main thread - //doing a while(running) - //if set_expired broadcast - //else usleep(n) + pthread_cond_broadcast(time_cond); pthread_mutex_unlock(&time_mutex); } } @@ -49,6 +51,14 @@ void SetPaused(int signum){ Paused=1; else{ Paused=0; +/*FIXME */ +//This is not safe. +//cond_var signaling must move away from signal handlers +//alltogether (JackCapture, SetExpired, SetPaused). +//Better would be a set of pipes for each of these. +//The callback should write on the pipe and the main thread +//should perform a select over the fd's, signaling afterwards the +//appropriate cond_var. pthread_mutex_lock(&pause_mutex); pthread_cond_broadcast(pause_cond); pthread_mutex_unlock(&pause_mutex); diff --git a/recordmydesktop/src/rmd_jack.c b/recordmydesktop/src/rmd_jack.c new file mode 100644 index 0000000..6d846a1 --- /dev/null +++ b/recordmydesktop/src/rmd_jack.c @@ -0,0 +1,208 @@ +/****************************************************************************** +* recordMyDesktop * +******************************************************************************* +* * +* Copyright (C) 2006,2007 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 + +#ifdef HAVE_JACK_H +int JackCapture(jack_nframes_t nframes,void *jdata_t){ + int i=0; + JackData *jdata=(JackData *)jdata_t; + + if((!*Running)||(Paused) || (!jdata->capture_started)) + return 0; + for(i= 0;inports;i++) + jdata->portbuf[i]=jack_port_get_buffer_p(jdata->ports[i],nframes); +//vorbis analysis buffer wants uninterleaved data +//so we are simply placing the buffers for every channel +//sequentially on the ringbuffer + for(i=0;inports;i++) + (*jack_ringbuffer_write_p)(jdata->sound_buffer, + (void *)(jdata->portbuf[i]), + nframes* + sizeof(jack_default_audio_sample_t)); +/*FIXME */ +//This is not safe. +//cond_var signaling must move away from signal handlers +//alltogether (JackCapture, SetExpired, SetPaused). +//Better would be a set of pipes for each of these. +//The callback should write on the pipe and the main thread +//should perform a select over the fd's, signaling afterwards the +//appropriate cond_var. + pthread_mutex_lock(jdata->snd_buff_ready_mutex); + pthread_cond_signal(jdata->sound_data_read); + pthread_mutex_unlock(jdata->snd_buff_ready_mutex); + + return 0; +} + +int SetupPorts(JackData *jdata){ + int i=0; + jdata->ports=malloc(sizeof(jack_port_t *)* + jdata->nports); + jdata->portbuf=malloc(sizeof(jack_default_audio_sample_t*)* + jdata->nports); + memset(jdata->portbuf,0,sizeof(jack_default_audio_sample_t*)* + jdata->nports); + + for(i=0;inports;i++){ + char name[64];//recordMyDesktop:input_n<64 is enough for full name + char num[8]; + strcpy(name,"input_"); + I16TOA((i+1),num); + strcat(name,num); + if((jdata->ports[i]=jack_port_register_p(jdata->client, + name, + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsInput, + 0))==0){ + fprintf(stderr,"Cannot register input port \"%s\"!\n",name); + return 1; + } + if(jack_connect_p(jdata->client, + jdata->port_names[i], + jack_port_name_p(jdata->ports[i]))){ + fprintf(stderr,"Cannot connect input port %s to %s\n", + jack_port_name_p(jdata->ports[i]), + jdata->port_names[i]); + return 1; + } + } + return 0; +} + +int LoadJackLib(void *jack_lib_handle){ + char *error; + jack_lib_handle=dlopen("libjack.so",RTLD_LAZY); + if(!jack_lib_handle){ + fprintf(stderr,"%s\n",dlerror()); + return 1; + } + if((error=dlerror())!=NULL){ + fprintf(stderr,"%s\n",dlerror()); + } +//this macro will call return with status 1 on failure + DLSYM_AND_CHECK(jack_lib_handle,jack_client_new,error) + DLSYM_AND_CHECK(jack_lib_handle,jack_get_sample_rate,error) + DLSYM_AND_CHECK(jack_lib_handle,jack_set_buffer_size,error) + DLSYM_AND_CHECK(jack_lib_handle,jack_get_buffer_size,error) + DLSYM_AND_CHECK(jack_lib_handle,jack_set_process_callback,error) + DLSYM_AND_CHECK(jack_lib_handle,jack_on_shutdown,error) + DLSYM_AND_CHECK(jack_lib_handle,jack_activate,error) + DLSYM_AND_CHECK(jack_lib_handle,jack_client_close,error) + DLSYM_AND_CHECK(jack_lib_handle,jack_port_get_buffer,error) + DLSYM_AND_CHECK(jack_lib_handle,jack_port_register,error) + DLSYM_AND_CHECK(jack_lib_handle,jack_connect,error) + DLSYM_AND_CHECK(jack_lib_handle,jack_port_name,error) + DLSYM_AND_CHECK(jack_lib_handle,jack_port_name_size,error) + DLSYM_AND_CHECK(jack_lib_handle,jack_ringbuffer_create,error) + DLSYM_AND_CHECK(jack_lib_handle,jack_ringbuffer_free,error) + DLSYM_AND_CHECK(jack_lib_handle,jack_ringbuffer_read,error) + DLSYM_AND_CHECK(jack_lib_handle,jack_ringbuffer_read_space,error) + DLSYM_AND_CHECK(jack_lib_handle,jack_ringbuffer_write,error) + + return 0; +} +//in case the jack server shuts down +//the program should stop recording, +//encode the result(if not on the fly) +//an exit cleanly. +void JackShutdown(void *jdata_t){ + fprintf (stderr, "JACK shutdown\n"); + *Running=0; +} + +int StartJackClient(JackData *jdata){ + + if(LoadJackLib(jdata->jack_lib_handle)){ + fprintf (stderr,"Couldn't load the Jack library (libjack.so)!\n"); + return 14; + } + if ((jdata->client=(*jack_client_new_p)("recordMyDesktop"))==0){ + fprintf(stderr,"Could not create new client!\n" + "Make sure that Jack server is running!\n"); + return 15; + } +//in contrast to ALSA and OSS, Jack dictates frequency +//and buffersize to the values it was launched with. +//Supposedly jack_set_buffer_size can set the buffersize +//but that causes some kind of halt and keeps giving me +//zero buffers. +//recordMyDesktop cannot handle buffer size changes. +//FIXME +//There is a callback for buffer size changes that I should use. +//It will provide clean exits, instead of ?segfaults? . +//Continuing though is not possible, with the current layout +//(it might be in some cases, but it will certainly be the cause +//of unpredicted problems). A clean exit is preferable +//and any recording up to that point will be encoded and saved. + jdata->frequency=jack_get_sample_rate_p(jdata->client); + jdata->buffersize=jack_get_buffer_size_p(jdata->client); + jdata->sound_buffer= + (*jack_ringbuffer_create_p)(jdata->nports* + sizeof(jack_default_audio_sample_t)* + jdata->buffersize* + BUFFERS_IN_RING); + jack_set_process_callback_p(jdata->client,JackCapture,jdata); + jack_on_shutdown_p(jdata->client,JackShutdown,jdata); + + if (jack_activate_p(jdata->client)) { + fprintf(stderr,"cannot activate client!\n"); + return 16; + } + if(SetupPorts(jdata)){ + jack_client_close_p(jdata->client); + return 17; + } + + return 0; +} + +int StopJackClient(JackData *jdata){ + int ret=0; + + (*jack_ringbuffer_free_p)(jdata->sound_buffer); + if(jack_client_close_p(jdata->client)){ + fprintf(stderr,"Cannot close Jack client!\n"); + ret=1; + } + +/*TODO*/ +//I need to make some kind of program/thread +//flow diagram to see where it's safe to dlclose +//because here it causes a segfault. + +// if(dlclose(jdata->jack_lib_handle)){ +// fprintf(stderr,"Cannot unload Jack library!\n"); +// ret=1; +// } +// else fprintf(stderr,"Unloaded Jack library.\n"); + + return ret; +} + +#endif + diff --git a/recordmydesktop/src/rmdthreads.c b/recordmydesktop/src/rmdthreads.c index 4199472..014a1d6 100644 --- a/recordmydesktop/src/rmdthreads.c +++ b/recordmydesktop/src/rmdthreads.c @@ -65,10 +65,11 @@ void rmdThreads(ProgData *pdata){ (void *)pdata); if(!pdata->args.nosound){ - pthread_create(&sound_capture_t, - NULL, - (void *)CaptureSound, - (void *)pdata); + if(!pdata->args.use_jack) + pthread_create(&sound_capture_t, + NULL, + (void *)CaptureSound, + (void *)pdata); if(pdata->args.encOnTheFly) pthread_create(&sound_encode_t, NULL, @@ -88,7 +89,11 @@ void rmdThreads(ProgData *pdata){ RegisterCallbacks(&pdata->args); fprintf(stderr,"Capturing!\n"); - +#ifdef HAVE_JACK_H + if(pdata->args.use_jack){ + pdata->jdata->capture_started=1; + } +#endif //wait all threads to finish pthread_join(image_capture_t,NULL); @@ -108,9 +113,14 @@ void rmdThreads(ProgData *pdata){ pthread_join(image_cache_t,NULL); fprintf(stderr,"."); if(!pdata->args.nosound){ - pthread_join(sound_capture_t,NULL); +#ifdef HAVE_JACK_H + if(pdata->args.use_jack) + StopJackClient(pdata->jdata); +#endif + if(!pdata->args.use_jack) + pthread_join(sound_capture_t,NULL); fprintf(stderr,"."); - while(!pdata->v_enc_thread_waiting && !pdata->v_encoding_clean){ + while(pdata->v_enc_thread_waiting || !pdata->v_encoding_clean){ usleep(10000); pthread_mutex_lock(&pdata->snd_buff_ready_mutex); pthread_cond_signal(&pdata->sound_data_read); -- cgit v1.2.3