mirror of
				https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
				synced 2025-10-29 05:40:23 -04:00 
			
		
		
		
	* some commenting work
* add new field "read_only" to memory blocks * add new API function pa_context_get_server() * filter capture data through mcalign on client * make module-tunnel use pa_socket_client_new_string() instead of using pa_resolve_server() directly. * remove pa_resolve_server() * remove debug.h and replace it by a macro definition on the gcc command line * some strbuf cleanups * small fixes in pa_stream for cleanup when server dies * new CLI command "load-sample-dir-lazy" * send FQDN as part of server info * rework mcalign, this time with memory block merging * fix iochannel cleanup when connection dies * check getaddrinfo() results git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@286 fefdeb5f-60dc-0310-8127-8f9354f1896f
This commit is contained in:
		
							parent
							
								
									f5f6605254
								
							
						
					
					
						commit
						0a2bbc528b
					
				
					 30 changed files with 636 additions and 273 deletions
				
			
		
							
								
								
									
										15
									
								
								doc/todo
									
										
									
									
									
								
							
							
						
						
									
										15
									
								
								doc/todo
									
										
									
									
									
								
							|  | @ -2,27 +2,22 @@ | ||||||
| 
 | 
 | ||||||
| *** 0.7 **** | *** 0.7 **** | ||||||
| - per-channel volume | - per-channel volume | ||||||
| - add sample directory |  | ||||||
| - make mcalign merge chunks |  | ||||||
| - option to use default fragment size on alsa drivers | - option to use default fragment size on alsa drivers | ||||||
| - improve module-oss-mmap latency measurement | - improve module-oss-mmap latency measurement | ||||||
| - filter capture data in client through alignment |  | ||||||
| - add radio module | - add radio module | ||||||
| - add sync API |  | ||||||
| - make most buffer sizes dependant on the sample type | - make most buffer sizes dependant on the sample type | ||||||
| 
 |  | ||||||
| - X11: support for the X11 synchronization extension |  | ||||||
| - pass meta info for hearing impaired |  | ||||||
| - limit all resources | - limit all resources | ||||||
| - check getaddrinfo results | - commenting | ||||||
| - non-fp mixing | - non-fp mixing | ||||||
| - non-fp resampling | - non-fp resampling | ||||||
| - make module-tunnel use pa_socket_client_new_string() |  | ||||||
| 
 | 
 | ||||||
| ** later *** | ** later *** | ||||||
|  | - pass meta info for hearing impaired | ||||||
|  | - add sync API | ||||||
|  | - X11: support for the X11 synchronization extension | ||||||
| - xmlrpc/http | - xmlrpc/http | ||||||
| - dbus | - dbus | ||||||
| - slp/rendezvous | - rendezvous | ||||||
| - make alsa modules use mmap | - make alsa modules use mmap | ||||||
| 
 | 
 | ||||||
| *********** | *********** | ||||||
|  |  | ||||||
|  | @ -27,6 +27,9 @@ AM_CFLAGS+=-DDLSEARCHPATH=\"$(modlibdir)\" | ||||||
| AM_CFLAGS+=-DDEFAULT_CONFIG_DIR=\"$(polypconfdir)\" | AM_CFLAGS+=-DDEFAULT_CONFIG_DIR=\"$(polypconfdir)\" | ||||||
| AM_CFLAGS+=-DPOLYPAUDIO_BINARY=\"$(bindir)/polypaudio\" | AM_CFLAGS+=-DPOLYPAUDIO_BINARY=\"$(bindir)/polypaudio\" | ||||||
| 
 | 
 | ||||||
|  | # This cool debug trap works on i386/gcc only | ||||||
|  | AM_CFLAGS+='-DDEBUG_TRAP=__asm__("int $$3")' | ||||||
|  | 
 | ||||||
| AM_LIBADD=$(PTHREAD_LIBS) -lm | AM_LIBADD=$(PTHREAD_LIBS) -lm | ||||||
| AM_LDADD=$(PTHREAD_LIBS) -lm | AM_LDADD=$(PTHREAD_LIBS) -lm | ||||||
| 
 | 
 | ||||||
|  | @ -40,7 +43,8 @@ noinst_PROGRAMS = \ | ||||||
| 		cpulimit-test \ | 		cpulimit-test \ | ||||||
| 		cpulimit-test2 \ | 		cpulimit-test2 \ | ||||||
| 		voltest \ | 		voltest \ | ||||||
| 		strlist-test | 		strlist-test \ | ||||||
|  | 		mcalign-test | ||||||
| 
 | 
 | ||||||
| polypconf_DATA=default.pa daemon.conf client.conf | polypconf_DATA=default.pa daemon.conf client.conf | ||||||
| 
 | 
 | ||||||
|  | @ -196,7 +200,6 @@ polypaudio_SOURCES = idxset.c idxset.h \ | ||||||
| 		autoload.c autoload.h \ | 		autoload.c autoload.h \ | ||||||
| 		xmalloc.c xmalloc.h \ | 		xmalloc.c xmalloc.h \ | ||||||
| 		subscribe.h subscribe.c \ | 		subscribe.h subscribe.c \ | ||||||
| 		debug.h \ |  | ||||||
| 		sound-file-stream.c sound-file-stream.h \ | 		sound-file-stream.c sound-file-stream.h \ | ||||||
| 		cpulimit.c cpulimit.h \ | 		cpulimit.c cpulimit.h \ | ||||||
| 		log.c log.h \ | 		log.c log.h \ | ||||||
|  | @ -206,7 +209,8 @@ polypaudio_SOURCES = idxset.c idxset.h \ | ||||||
| 		dumpmodules.c dumpmodules.h \ | 		dumpmodules.c dumpmodules.h \ | ||||||
| 		conf-parser.h conf-parser.c \ | 		conf-parser.h conf-parser.c \ | ||||||
| 		caps.h caps.c \ | 		caps.h caps.c \ | ||||||
| 		props.h props.c | 		props.h props.c \ | ||||||
|  | 		mcalign.c mcalign.h | ||||||
| 
 | 
 | ||||||
| polypaudio_CFLAGS = $(AM_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $(LIBSNDFILE_CFLAGS) | polypaudio_CFLAGS = $(AM_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $(LIBSNDFILE_CFLAGS) | ||||||
| polypaudio_INCLUDES = $(INCLTDL) | polypaudio_INCLUDES = $(INCLTDL) | ||||||
|  | @ -441,7 +445,8 @@ libpolyp_@PA_MAJORMINOR@_la_SOURCES = polyplib.h \ | ||||||
| 		client-conf.c client-conf.h \ | 		client-conf.c client-conf.h \ | ||||||
| 		conf-parser.c conf-parser.h \ | 		conf-parser.c conf-parser.h \ | ||||||
| 		strlist.c strlist.h \ | 		strlist.c strlist.h \ | ||||||
| 		strbuf.c strbuf.h | 		strbuf.c strbuf.h \ | ||||||
|  | 		mcalign.c mcalign.h | ||||||
| 
 | 
 | ||||||
| libpolyp_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) | libpolyp_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) | ||||||
| libpolyp_@PA_MAJORMINOR@_la_LDFLAGS = -version-info 0:0:0 | libpolyp_@PA_MAJORMINOR@_la_LDFLAGS = -version-info 0:0:0 | ||||||
|  | @ -495,6 +500,10 @@ strlist_test_SOURCES = strlist-test.c strlist.c strlist.h strbuf.c strbuf.h util | ||||||
| strlist_test_CFLAGS = $(AM_CFLAGS) | strlist_test_CFLAGS = $(AM_CFLAGS) | ||||||
| strlist_test_LDADD = $(AM_LDADD) | strlist_test_LDADD = $(AM_LDADD) | ||||||
| 
 | 
 | ||||||
|  | mcalign_test_SOURCES = mcalign-test.c util.c util.h xmalloc.c xmalloc.h log.c log.h mcalign.c mcalign.h memchunk.c memchunk.h memblock.c memblock.h | ||||||
|  | mcalign_test_CFLAGS = $(AM_CFLAGS) | ||||||
|  | mcalign_test_LDADD = $(AM_LDADD) | ||||||
|  | 
 | ||||||
| cpulimit_test_SOURCES = cpulimit-test.c cpulimit.c util.c log.c cpulimit.h util.h log.h | cpulimit_test_SOURCES = cpulimit-test.c cpulimit.c util.c log.c cpulimit.h util.h log.h | ||||||
| cpulimit_test_CFLAGS = $(AM_CFLAGS) | cpulimit_test_CFLAGS = $(AM_CFLAGS) | ||||||
| cpulimit_test_LDADD = $(AM_LDADD) libpolyp-mainloop-@PA_MAJORMINOR@.la | cpulimit_test_LDADD = $(AM_LDADD) libpolyp-mainloop-@PA_MAJORMINOR@.la | ||||||
|  |  | ||||||
|  | @ -35,6 +35,7 @@ | ||||||
| #include "log.h" | #include "log.h" | ||||||
| #include "caps.h" | #include "caps.h" | ||||||
| 
 | 
 | ||||||
|  | /* Drop root rights when called SUID root */ | ||||||
| void pa_drop_root(void) { | void pa_drop_root(void) { | ||||||
|     uid_t uid = getuid(); |     uid_t uid = getuid(); | ||||||
|      |      | ||||||
|  | @ -50,6 +51,7 @@ void pa_drop_root(void) { | ||||||
| 
 | 
 | ||||||
| #ifdef HAVE_SYS_CAPABILITY_H | #ifdef HAVE_SYS_CAPABILITY_H | ||||||
| 
 | 
 | ||||||
|  | /* Limit capabilities set to CAPSYS_NICE */ | ||||||
| int pa_limit_caps(void) { | int pa_limit_caps(void) { | ||||||
|     int r = -1; |     int r = -1; | ||||||
|     cap_t caps; |     cap_t caps; | ||||||
|  | @ -76,6 +78,7 @@ fail: | ||||||
|     return r; |     return r; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /* Drop all capabilities, effectively becoming a normal user */ | ||||||
| int pa_drop_caps(void) { | int pa_drop_caps(void) { | ||||||
|     cap_t caps; |     cap_t caps; | ||||||
|     int r = -1; |     int r = -1; | ||||||
|  | @ -100,6 +103,7 @@ fail: | ||||||
| 
 | 
 | ||||||
| #else | #else | ||||||
| 
 | 
 | ||||||
|  | /* NOOPs in case capabilities are not available. */ | ||||||
| int pa_limit_caps(void) { | int pa_limit_caps(void) { | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -79,6 +79,7 @@ static int pa_cli_command_scache_play(struct pa_core *c, struct pa_tokenizer *t, | ||||||
| static int pa_cli_command_scache_remove(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); | static int pa_cli_command_scache_remove(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); | ||||||
| static int pa_cli_command_scache_list(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); | static int pa_cli_command_scache_list(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); | ||||||
| static int pa_cli_command_scache_load(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); | static int pa_cli_command_scache_load(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); | ||||||
|  | static int pa_cli_command_scache_load_dir(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); | ||||||
| static int pa_cli_command_play_file(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); | static int pa_cli_command_play_file(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); | ||||||
| static int pa_cli_command_autoload_list(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); | static int pa_cli_command_autoload_list(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); | ||||||
| static int pa_cli_command_autoload_add(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); | static int pa_cli_command_autoload_add(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); | ||||||
|  | @ -112,7 +113,8 @@ static const struct command commands[] = { | ||||||
|     { "play-sample",             pa_cli_command_scache_play,        "Play a sample from the sample cache (args: name, sink|index)", 3}, |     { "play-sample",             pa_cli_command_scache_play,        "Play a sample from the sample cache (args: name, sink|index)", 3}, | ||||||
|     { "remove-sample",           pa_cli_command_scache_remove,      "Remove a sample from the sample cache (args: name)", 2}, |     { "remove-sample",           pa_cli_command_scache_remove,      "Remove a sample from the sample cache (args: name)", 2}, | ||||||
|     { "load-sample",             pa_cli_command_scache_load,        "Load a sound file into the sample cache (args: name, filename)", 3}, |     { "load-sample",             pa_cli_command_scache_load,        "Load a sound file into the sample cache (args: name, filename)", 3}, | ||||||
|     { "load-sample-lazy",        pa_cli_command_scache_load,        "Lazy load a sound file into the sample cache (args: name, filename)", 3}, |     { "load-sample-lazy",        pa_cli_command_scache_load,        "Lazily load a sound file into the sample cache (args: name, filename)", 3}, | ||||||
|  |     { "load-sample-dir-lazy",    pa_cli_command_scache_load_dir,    "Lazily load all files in a directory into the sample cache (args: pathname)", 2}, | ||||||
|     { "play-file",               pa_cli_command_play_file,          "Play a sound file (args: filename, sink|index)", 3}, |     { "play-file",               pa_cli_command_play_file,          "Play a sound file (args: filename, sink|index)", 3}, | ||||||
|     { "list-autoload",           pa_cli_command_autoload_list,      "List autoload entries", 1}, |     { "list-autoload",           pa_cli_command_autoload_list,      "List autoload entries", 1}, | ||||||
|     { "add-autoload-sink",       pa_cli_command_autoload_add,       "Add autoload entry for a sink (args: sink, module name, arguments)", 4}, |     { "add-autoload-sink",       pa_cli_command_autoload_add,       "Add autoload entry for a sink (args: sink, module name, arguments)", 4}, | ||||||
|  | @ -545,6 +547,23 @@ static int pa_cli_command_scache_load(struct pa_core *c, struct pa_tokenizer *t, | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static int pa_cli_command_scache_load_dir(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { | ||||||
|  |     const char *pname; | ||||||
|  |     assert(c && t && buf && fail && verbose); | ||||||
|  | 
 | ||||||
|  |     if (!(pname = pa_tokenizer_get(t, 1))) { | ||||||
|  |         pa_strbuf_puts(buf, "You need to specify a path name.\n"); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (pa_scache_add_directory_lazy(c, pname) < 0) { | ||||||
|  |         pa_strbuf_puts(buf, "Failed to load directory.\n"); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static int pa_cli_command_play_file(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { | static int pa_cli_command_play_file(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { | ||||||
|     const char *fname, *sink_name; |     const char *fname, *sink_name; | ||||||
|     struct pa_sink *sink; |     struct pa_sink *sink; | ||||||
|  |  | ||||||
|  | @ -32,30 +32,58 @@ | ||||||
| #include "util.h" | #include "util.h" | ||||||
| #include "log.h" | #include "log.h" | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | /* This module implements a watchdog that makes sure that the current
 | ||||||
|  |  * process doesn't consume more than 70% CPU time for 10 seconds. This | ||||||
|  |  * is very useful when using SCHED_FIFO scheduling which effectively | ||||||
|  |  * disables multitasking. */ | ||||||
|  | 
 | ||||||
|  | /* Method of operation: Using SIGXCPU a signal handler is called every
 | ||||||
|  |  * 10s process CPU time. That function checks if less than 14s system | ||||||
|  |  * time have passed. In that case, it tries to contact the main event | ||||||
|  |  * loop through a pipe. After two additional seconds it is checked | ||||||
|  |  * whether the main event loop contact was successful. If not, the | ||||||
|  |  * program is terminated forcibly. */ | ||||||
|  | 
 | ||||||
| /* Utilize this much CPU time at maximum */ | /* Utilize this much CPU time at maximum */ | ||||||
| #define CPUTIME_PERCENT 70 | #define CPUTIME_PERCENT 70 | ||||||
| 
 | 
 | ||||||
|  | /* Check every 10s */ | ||||||
| #define CPUTIME_INTERVAL_SOFT (10) | #define CPUTIME_INTERVAL_SOFT (10) | ||||||
|  | 
 | ||||||
|  | /* Recheck after 2s */ | ||||||
| #define CPUTIME_INTERVAL_HARD (2) | #define CPUTIME_INTERVAL_HARD (2) | ||||||
| 
 | 
 | ||||||
|  | /* Time of the last CPU load check */ | ||||||
| static time_t last_time = 0; | static time_t last_time = 0; | ||||||
|  | 
 | ||||||
|  | /* Pipe for communicating with the main loop */ | ||||||
| static int the_pipe[2] = {-1, -1}; | static int the_pipe[2] = {-1, -1}; | ||||||
|  | 
 | ||||||
|  | /* Main event loop and IO event for the FIFO */ | ||||||
| static struct pa_mainloop_api *api = NULL; | static struct pa_mainloop_api *api = NULL; | ||||||
| static struct pa_io_event *io_event = NULL; | static struct pa_io_event *io_event = NULL; | ||||||
| static struct sigaction sigaction_prev; |  | ||||||
| static int installed = 0; |  | ||||||
| 
 | 
 | ||||||
|  | /* Saved sigaction struct for SIGXCPU */ | ||||||
|  | static struct sigaction sigaction_prev; | ||||||
|  | 
 | ||||||
|  | /* Nonzero after pa_cpu_limit_init() */ | ||||||
|  | static int installed = 0;  | ||||||
|  | 
 | ||||||
|  | /* The current state of operation */ | ||||||
| static enum  { | static enum  { | ||||||
|     PHASE_IDLE, |     PHASE_IDLE,   /* Normal state */ | ||||||
|     PHASE_SOFT |     PHASE_SOFT    /* After CPU overload has been detected */ | ||||||
| } phase = PHASE_IDLE; | } phase = PHASE_IDLE; | ||||||
| 
 | 
 | ||||||
|  | /* Reset the SIGXCPU timer to the next t seconds */ | ||||||
| static void reset_cpu_time(int t) { | static void reset_cpu_time(int t) { | ||||||
|     int r; |     int r; | ||||||
|     long n; |     long n; | ||||||
|     struct rlimit rl; |     struct rlimit rl; | ||||||
|     struct rusage ru; |     struct rusage ru; | ||||||
| 
 | 
 | ||||||
|  |     /* Get the current CPU time of the current process */ | ||||||
|     r = getrusage(RUSAGE_SELF, &ru); |     r = getrusage(RUSAGE_SELF, &ru); | ||||||
|     assert(r >= 0); |     assert(r >= 0); | ||||||
| 
 | 
 | ||||||
|  | @ -69,10 +97,12 @@ static void reset_cpu_time(int t) { | ||||||
|     assert(r >= 0); |     assert(r >= 0); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /* A simple, thread-safe puts() work-alike */ | ||||||
| static void write_err(const char *p) { | static void write_err(const char *p) { | ||||||
|     pa_loop_write(2, p, strlen(p)); |     pa_loop_write(2, p, strlen(p)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /* The signal handler, called on every SIGXCPU */ | ||||||
| static void signal_handler(int sig) { | static void signal_handler(int sig) { | ||||||
|     assert(sig == SIGXCPU); |     assert(sig == SIGXCPU); | ||||||
| 
 | 
 | ||||||
|  | @ -109,23 +139,26 @@ static void signal_handler(int sig) { | ||||||
|          |          | ||||||
|     } else if (phase == PHASE_SOFT) { |     } else if (phase == PHASE_SOFT) { | ||||||
|         write_err("Hard CPU time limit exhausted, terminating forcibly.\n"); |         write_err("Hard CPU time limit exhausted, terminating forcibly.\n"); | ||||||
|         _exit(1); |         _exit(1); /* Forced exit */ | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /* Callback for IO events on the FIFO */ | ||||||
| static void callback(struct pa_mainloop_api*m, struct pa_io_event*e, int fd, enum pa_io_event_flags f, void *userdata) { | static void callback(struct pa_mainloop_api*m, struct pa_io_event*e, int fd, enum pa_io_event_flags f, void *userdata) { | ||||||
|     char c; |     char c; | ||||||
|     assert(m && e && f == PA_IO_EVENT_INPUT && e == io_event && fd == the_pipe[0]); |     assert(m && e && f == PA_IO_EVENT_INPUT && e == io_event && fd == the_pipe[0]); | ||||||
|     read(the_pipe[0], &c, sizeof(c)); |     read(the_pipe[0], &c, sizeof(c)); | ||||||
|     m->quit(m, 1); |     m->quit(m, 1); /* Quit the main loop */ | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /* Initializes CPU load limiter */ | ||||||
| int pa_cpu_limit_init(struct pa_mainloop_api *m) { | int pa_cpu_limit_init(struct pa_mainloop_api *m) { | ||||||
|     struct sigaction sa; |     struct sigaction sa; | ||||||
|     assert(m && !api && !io_event && the_pipe[0] == -1 && the_pipe[1] == -1); |     assert(m && !api && !io_event && the_pipe[0] == -1 && the_pipe[1] == -1 && !installed); | ||||||
|      |      | ||||||
|     time(&last_time); |     time(&last_time); | ||||||
| 
 | 
 | ||||||
|  |     /* Prepare the main loop pipe */ | ||||||
|     if (pipe(the_pipe) < 0) { |     if (pipe(the_pipe) < 0) { | ||||||
|         pa_log(__FILE__": pipe() failed: %s\n", strerror(errno)); |         pa_log(__FILE__": pipe() failed: %s\n", strerror(errno)); | ||||||
|         return -1; |         return -1; | ||||||
|  | @ -141,6 +174,7 @@ int pa_cpu_limit_init(struct pa_mainloop_api *m) { | ||||||
| 
 | 
 | ||||||
|     phase = PHASE_IDLE; |     phase = PHASE_IDLE; | ||||||
| 
 | 
 | ||||||
|  |     /* Install signal handler for SIGXCPU */ | ||||||
|     memset(&sa, 0, sizeof(sa)); |     memset(&sa, 0, sizeof(sa)); | ||||||
|     sa.sa_handler = signal_handler; |     sa.sa_handler = signal_handler; | ||||||
|     sigemptyset(&sa.sa_mask); |     sigemptyset(&sa.sa_mask); | ||||||
|  | @ -158,6 +192,7 @@ int pa_cpu_limit_init(struct pa_mainloop_api *m) { | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /* Shutdown CPU load limiter */ | ||||||
| void pa_cpu_limit_done(void) { | void pa_cpu_limit_done(void) { | ||||||
|     int r; |     int r; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,29 +0,0 @@ | ||||||
| #ifndef foodebughfoo |  | ||||||
| #define foodebughfoo |  | ||||||
| 
 |  | ||||||
| /* $Id$ */ |  | ||||||
| 
 |  | ||||||
| /***
 |  | ||||||
|   This file is part of polypaudio. |  | ||||||
|   |  | ||||||
|   polypaudio is free software; you can redistribute it and/or modify |  | ||||||
|   it under the terms of the GNU Lesser General Public License as published |  | ||||||
|   by the Free Software Foundation; either version 2 of the License, |  | ||||||
|   or (at your option) any later version. |  | ||||||
|   |  | ||||||
|   polypaudio 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 Lesser General Public License |  | ||||||
|   along with polypaudio; if not, write to the Free Software |  | ||||||
|   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |  | ||||||
|   USA. |  | ||||||
| ***/ |  | ||||||
| 
 |  | ||||||
| /* A nice trick for debuggers, working on x86 with GCC only */ |  | ||||||
| 
 |  | ||||||
| #define DEBUG_TRAP __asm__("int $3") |  | ||||||
| 
 |  | ||||||
| #endif |  | ||||||
|  | @ -82,6 +82,9 @@ static void callback(struct pa_mainloop_api* m, struct pa_io_event *e, int fd, e | ||||||
|         if (e == io->input_event) { |         if (e == io->input_event) { | ||||||
|             io->mainloop->io_free(io->input_event); |             io->mainloop->io_free(io->input_event); | ||||||
|             io->input_event = NULL; |             io->input_event = NULL; | ||||||
|  | 
 | ||||||
|  |             if (io->output_event == e) | ||||||
|  |                 io->output_event = NULL; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (e == io->output_event) { |         if (e == io->output_event) { | ||||||
|  |  | ||||||
							
								
								
									
										68
									
								
								polyp/mcalign-test.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								polyp/mcalign-test.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,68 @@ | ||||||
|  | #include <assert.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | #include <sys/types.h> | ||||||
|  | #include <errno.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <time.h> | ||||||
|  | 
 | ||||||
|  | #include "util.h" | ||||||
|  | #include "mcalign.h" | ||||||
|  | 
 | ||||||
|  | int main(int argc, char *argv[]) { | ||||||
|  |     struct pa_mcalign *a = pa_mcalign_new(11, NULL); | ||||||
|  |     struct pa_memchunk c; | ||||||
|  | 
 | ||||||
|  |     pa_memchunk_reset(&c); | ||||||
|  | 
 | ||||||
|  |     srand(time(NULL)); | ||||||
|  | 
 | ||||||
|  |     for (;;) { | ||||||
|  |         ssize_t r; | ||||||
|  |         size_t l; | ||||||
|  | 
 | ||||||
|  |         if (!c.memblock) { | ||||||
|  |             c.memblock = pa_memblock_new(2048, NULL); | ||||||
|  |             c.index = c.length = 0; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         assert(c.index < c.memblock->length); | ||||||
|  | 
 | ||||||
|  |         l = c.memblock->length - c.index; | ||||||
|  | 
 | ||||||
|  |         l = l <= 1 ? l : rand() % (l-1) +1 ; | ||||||
|  |          | ||||||
|  |         if ((r = read(STDIN_FILENO, (uint8_t*) c.memblock->data + c.index, l)) <= 0) { | ||||||
|  |             fprintf(stderr, "read() failed: %s\n", r < 0 ? strerror(errno) : "EOF"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         c.length = r; | ||||||
|  |         pa_mcalign_push(a, &c); | ||||||
|  |         fprintf(stderr, "Read %u bytes\n", r); | ||||||
|  | 
 | ||||||
|  |         c.index += r; | ||||||
|  | 
 | ||||||
|  |         if (c.index >= c.memblock->length) { | ||||||
|  |             pa_memblock_unref(c.memblock); | ||||||
|  |             pa_memchunk_reset(&c); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         for (;;) { | ||||||
|  |             struct pa_memchunk t; | ||||||
|  | 
 | ||||||
|  |             if (pa_mcalign_pop(a, &t) < 0) | ||||||
|  |                 break; | ||||||
|  | 
 | ||||||
|  |             pa_loop_write(STDOUT_FILENO, (uint8_t*) t.memblock->data + t.index, t.length); | ||||||
|  |             fprintf(stderr, "Wrote %u bytes.\n", t.length); | ||||||
|  | 
 | ||||||
|  |             pa_memblock_unref(t.memblock); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pa_mcalign_free(a); | ||||||
|  | 
 | ||||||
|  |     if (c.memblock) | ||||||
|  |         pa_memblock_unref(c.memblock); | ||||||
|  | } | ||||||
							
								
								
									
										188
									
								
								polyp/mcalign.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										188
									
								
								polyp/mcalign.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,188 @@ | ||||||
|  | /* $Id$ */ | ||||||
|  | 
 | ||||||
|  | /***
 | ||||||
|  |   This file is part of polypaudio. | ||||||
|  |   | ||||||
|  |   polypaudio is free software; you can redistribute it and/or modify | ||||||
|  |   it under the terms of the GNU Lesser General Public License as | ||||||
|  |   published by the Free Software Foundation; either version 2.1 of the | ||||||
|  |   License, or (at your option) any later version. | ||||||
|  |   | ||||||
|  |   polypaudio 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 | ||||||
|  |   Lesser General Public License for more details. | ||||||
|  |   | ||||||
|  |   You should have received a copy of the GNU Lesser General Public | ||||||
|  |   License along with polypaudio; if not, write to the Free Software | ||||||
|  |   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 | ||||||
|  |   USA. | ||||||
|  | ***/ | ||||||
|  | 
 | ||||||
|  | #ifdef HAVE_CONFIG_H | ||||||
|  | #include <config.h> | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <assert.h> | ||||||
|  | #include <string.h> | ||||||
|  | 
 | ||||||
|  | #include "mcalign.h" | ||||||
|  | #include "xmalloc.h" | ||||||
|  | 
 | ||||||
|  | struct pa_mcalign { | ||||||
|  |     size_t base; | ||||||
|  |     struct pa_memchunk leftover, current; | ||||||
|  |     struct pa_memblock_stat *memblock_stat; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct pa_mcalign *pa_mcalign_new(size_t base, struct pa_memblock_stat *s) { | ||||||
|  |     struct pa_mcalign *m; | ||||||
|  |     assert(base); | ||||||
|  | 
 | ||||||
|  |     m = pa_xmalloc(sizeof(struct pa_mcalign)); | ||||||
|  |     m->base = base; | ||||||
|  |     pa_memchunk_reset(&m->leftover); | ||||||
|  |     pa_memchunk_reset(&m->current); | ||||||
|  |     m->memblock_stat = s; | ||||||
|  |      | ||||||
|  |     return m; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void pa_mcalign_free(struct pa_mcalign *m) { | ||||||
|  |     assert(m); | ||||||
|  | 
 | ||||||
|  |     if (m->leftover.memblock) | ||||||
|  |         pa_memblock_unref(m->leftover.memblock); | ||||||
|  | 
 | ||||||
|  |     if (m->current.memblock) | ||||||
|  |         pa_memblock_unref(m->current.memblock); | ||||||
|  |      | ||||||
|  |     pa_xfree(m); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void pa_mcalign_push(struct pa_mcalign *m, const struct pa_memchunk *c) { | ||||||
|  |     assert(m && c && c->memblock && c->length); | ||||||
|  |      | ||||||
|  |     /* Append to the leftover memory block */ | ||||||
|  |     if (m->leftover.memblock) { | ||||||
|  |         assert(!m->current.memblock); | ||||||
|  |          | ||||||
|  |         /* Try to merge */ | ||||||
|  |         if (m->leftover.memblock == c->memblock && | ||||||
|  |             m->leftover.index + m->leftover.length == c->index) { | ||||||
|  | 
 | ||||||
|  |             /* Merge */ | ||||||
|  |             m->leftover.length += c->length; | ||||||
|  | 
 | ||||||
|  |             /* If the new chunk is larger than m->base, move it to current */ | ||||||
|  |             if (m->leftover.length >= m->base) { | ||||||
|  |                 m->current = m->leftover; | ||||||
|  |                 pa_memchunk_reset(&m->leftover); | ||||||
|  |             }  | ||||||
|  | 
 | ||||||
|  |         } else { | ||||||
|  |             size_t l; | ||||||
|  | 
 | ||||||
|  |             /* We have to copy */ | ||||||
|  |             assert(m->leftover.length < m->base); | ||||||
|  |             l = m->base - m->leftover.length; | ||||||
|  |              | ||||||
|  |             if (l > c->length) | ||||||
|  |                 l = c->length; | ||||||
|  | 
 | ||||||
|  |             /* Can we use the current block? */ | ||||||
|  |             pa_memchunk_make_writable(&m->leftover, m->memblock_stat, m->base); | ||||||
|  | 
 | ||||||
|  |             memcpy((uint8_t*) m->leftover.memblock->data + m->leftover.index + m->leftover.length, (uint8_t*) c->memblock->data + c->index, l); | ||||||
|  |             m->leftover.length += l; | ||||||
|  | 
 | ||||||
|  |             assert(m->leftover.length <= m->base && m->leftover.length <= m->leftover.memblock->length); | ||||||
|  | 
 | ||||||
|  |             if (c->length > l) { | ||||||
|  |                 /* Save the remainder of the memory block */ | ||||||
|  |                 m->current = *c; | ||||||
|  |                 m->current.index += l; | ||||||
|  |                 m->current.length -= l; | ||||||
|  |                 pa_memblock_ref(m->current.memblock); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         assert(!m->leftover.memblock && !m->current.memblock); | ||||||
|  | 
 | ||||||
|  |         /* Nothing to merge or copy, just store it */ | ||||||
|  |          | ||||||
|  |         if (c->length >= m->base) | ||||||
|  |             m->current = *c; | ||||||
|  |         else | ||||||
|  |             m->leftover = *c; | ||||||
|  | 
 | ||||||
|  |         pa_memblock_ref(c->memblock); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int pa_mcalign_pop(struct pa_mcalign *m, struct pa_memchunk *c) { | ||||||
|  |     assert(m && c); | ||||||
|  | 
 | ||||||
|  |     /* First test if there's a leftover memory block available */ | ||||||
|  |     if (m->leftover.memblock) { | ||||||
|  |         assert(m->leftover.length > 0 && m->leftover.length <= m->base); | ||||||
|  | 
 | ||||||
|  |         /* The leftover memory block is not yet complete */ | ||||||
|  |         if (m->leftover.length < m->base) | ||||||
|  |             return -1; | ||||||
|  | 
 | ||||||
|  |         /* Return the leftover memory block */ | ||||||
|  |         *c = m->leftover; | ||||||
|  |         pa_memchunk_reset(&m->leftover); | ||||||
|  | 
 | ||||||
|  |         /* If the current memblock is too small move it the leftover */ | ||||||
|  |         if (m->current.memblock && m->current.length < m->base) { | ||||||
|  |             m->leftover = m->current; | ||||||
|  |             pa_memchunk_reset(&m->current); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* Now let's see if there is other data available */ | ||||||
|  |     if (m->current.memblock) { | ||||||
|  |         size_t l; | ||||||
|  |         assert(m->current.length >= m->base); | ||||||
|  | 
 | ||||||
|  |         /* The length of the returned memory block */ | ||||||
|  |         l = m->current.length; | ||||||
|  |         l /= m->base; | ||||||
|  |         l *= m->base; | ||||||
|  |         assert(l > 0); | ||||||
|  | 
 | ||||||
|  |         /* Prepare the returned block */ | ||||||
|  |         *c = m->current; | ||||||
|  |         pa_memblock_ref(c->memblock); | ||||||
|  |         c->length = l; | ||||||
|  | 
 | ||||||
|  |         /* Drop that from the current memory block */ | ||||||
|  |         assert(l <= m->current.length); | ||||||
|  |         m->current.index += l; | ||||||
|  |         m->current.length -= l; | ||||||
|  | 
 | ||||||
|  |         /* In case the whole block was dropped ... */ | ||||||
|  |         if (m->current.length == 0) | ||||||
|  |             pa_memblock_unref(m->current.memblock); | ||||||
|  |         else { | ||||||
|  |             /* Move the raimainder to leftover */ | ||||||
|  |             assert(m->current.length < m->base && !m->leftover.memblock); | ||||||
|  | 
 | ||||||
|  |             m->leftover = m->current; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         pa_memchunk_reset(&m->current); | ||||||
|  |              | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* There's simply nothing */ | ||||||
|  |     return -1; | ||||||
|  |      | ||||||
|  | } | ||||||
							
								
								
									
										77
									
								
								polyp/mcalign.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								polyp/mcalign.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,77 @@ | ||||||
|  | #ifndef foomcalignhfoo | ||||||
|  | #define foomcalignhfoo | ||||||
|  | 
 | ||||||
|  | /* $Id$ */ | ||||||
|  | 
 | ||||||
|  | /***
 | ||||||
|  |   This file is part of polypaudio. | ||||||
|  |   | ||||||
|  |   polypaudio is free software; you can redistribute it and/or modify | ||||||
|  |   it under the terms of the GNU Lesser General Public License as | ||||||
|  |   published by the Free Software Foundation; either version 2.1 of the | ||||||
|  |   License, or (at your option) any later version. | ||||||
|  |   | ||||||
|  |   polypaudio 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 | ||||||
|  |   Lesser General Public License for more details. | ||||||
|  |   | ||||||
|  |   You should have received a copy of the GNU Lesser General Public | ||||||
|  |   License along with polypaudio; if not, write to the Free Software | ||||||
|  |   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 | ||||||
|  |   USA. | ||||||
|  | ***/ | ||||||
|  | 
 | ||||||
|  | #include "memblock.h" | ||||||
|  | #include "memchunk.h" | ||||||
|  | 
 | ||||||
|  | /* An alignment object, used for aligning memchunks to multiples of
 | ||||||
|  |  * the frame size. */ | ||||||
|  | 
 | ||||||
|  | /* Method of operation: the user creates a new mcalign object by
 | ||||||
|  |  * calling pa_mcalign_new() with the appropriate aligning | ||||||
|  |  * granularity. After that he may call pa_mcalign_push() for an input | ||||||
|  |  * memchunk. After exactly one memchunk the user has to call | ||||||
|  |  * pa_mcalign_pop() until it returns -1. If pa_mcalign_pop() returns | ||||||
|  |  * 0, the memchunk *c is valid and aligned to the granularity. Some | ||||||
|  |  * pseudocode illustrating this: | ||||||
|  |  * | ||||||
|  |  * struct pa_mcalign *a = pa_mcalign_new(4, NULL); | ||||||
|  |  * | ||||||
|  |  * for (;;) { | ||||||
|  |  *   struct pa_memchunk input; | ||||||
|  |  * | ||||||
|  |  *   ... fill input ...  | ||||||
|  |  * | ||||||
|  |  *   pa_mcalign_push(m, &input); | ||||||
|  |  *   pa_memblock_unref(input.memblock); | ||||||
|  |  *  | ||||||
|  |  *   for (;;) { | ||||||
|  |  *     struct pa_memchunk output; | ||||||
|  |  * | ||||||
|  |  *     if (pa_mcalign_pop(m, &output) < 0) | ||||||
|  |  *       break; | ||||||
|  |  * | ||||||
|  |  *     ... consume output ... | ||||||
|  |  * | ||||||
|  |  *     pa_memblock_unref(output.memblock); | ||||||
|  |  *   } | ||||||
|  |  * } | ||||||
|  |  * | ||||||
|  |  * pa_memchunk_free(a); | ||||||
|  |  * */ | ||||||
|  | 
 | ||||||
|  | struct pa_mcalign; | ||||||
|  | 
 | ||||||
|  | struct pa_mcalign *pa_mcalign_new(size_t base, struct pa_memblock_stat *s); | ||||||
|  | void pa_mcalign_free(struct pa_mcalign *m); | ||||||
|  | 
 | ||||||
|  | /* Push a new memchunk into the aligner. The caller of this routine
 | ||||||
|  |  * has to free the memchunk by himself. */ | ||||||
|  | void pa_mcalign_push(struct pa_mcalign *m, const struct pa_memchunk *c); | ||||||
|  | 
 | ||||||
|  | /* Pop a new memchunk from the aligner. Returns 0 when sucessful,
 | ||||||
|  |  * nonzero otherwise. */ | ||||||
|  | int pa_mcalign_pop(struct pa_mcalign *m, struct pa_memchunk *c); | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
|  | @ -34,6 +34,11 @@ | ||||||
| static void stat_add(struct pa_memblock*m, struct pa_memblock_stat *s) { | static void stat_add(struct pa_memblock*m, struct pa_memblock_stat *s) { | ||||||
|     assert(m); |     assert(m); | ||||||
| 
 | 
 | ||||||
|  |     if (!s) { | ||||||
|  |         m->stat = NULL; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     m->stat = pa_memblock_stat_ref(s); |     m->stat = pa_memblock_stat_ref(s); | ||||||
|     s->total++; |     s->total++; | ||||||
|     s->allocated++; |     s->allocated++; | ||||||
|  | @ -61,17 +66,7 @@ struct pa_memblock *pa_memblock_new(size_t length, struct pa_memblock_stat*s) { | ||||||
|     b->length = length; |     b->length = length; | ||||||
|     b->data = b+1; |     b->data = b+1; | ||||||
|     b->free_cb = NULL; |     b->free_cb = NULL; | ||||||
|     stat_add(b, s); |     b->read_only = 0; | ||||||
|     return b; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| struct pa_memblock *pa_memblock_new_fixed(void *d, size_t length, struct pa_memblock_stat*s) { |  | ||||||
|     struct pa_memblock *b = pa_xmalloc(sizeof(struct pa_memblock)); |  | ||||||
|     b->type = PA_MEMBLOCK_FIXED; |  | ||||||
|     b->ref = 1; |  | ||||||
|     b->length = length; |  | ||||||
|     b->data = d; |  | ||||||
|     b->free_cb = NULL; |  | ||||||
|     stat_add(b, s); |     stat_add(b, s); | ||||||
|     return b; |     return b; | ||||||
| } | } | ||||||
|  | @ -83,11 +78,24 @@ struct pa_memblock *pa_memblock_new_dynamic(void *d, size_t length, struct pa_me | ||||||
|     b->length = length; |     b->length = length; | ||||||
|     b->data = d; |     b->data = d; | ||||||
|     b->free_cb = NULL; |     b->free_cb = NULL; | ||||||
|  |     b->read_only = 0; | ||||||
|     stat_add(b, s); |     stat_add(b, s); | ||||||
|     return b; |     return b; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| struct pa_memblock *pa_memblock_new_user(void *d, size_t length, void (*free_cb)(void *p), struct pa_memblock_stat*s) { | struct pa_memblock *pa_memblock_new_fixed(void *d, size_t length, int read_only, struct pa_memblock_stat*s) { | ||||||
|  |     struct pa_memblock *b = pa_xmalloc(sizeof(struct pa_memblock)); | ||||||
|  |     b->type = PA_MEMBLOCK_FIXED; | ||||||
|  |     b->ref = 1; | ||||||
|  |     b->length = length; | ||||||
|  |     b->data = d; | ||||||
|  |     b->free_cb = NULL; | ||||||
|  |     b->read_only = read_only; | ||||||
|  |     stat_add(b, s); | ||||||
|  |     return b; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct pa_memblock *pa_memblock_new_user(void *d, size_t length, void (*free_cb)(void *p), int read_only, struct pa_memblock_stat*s) { | ||||||
|     struct pa_memblock *b; |     struct pa_memblock *b; | ||||||
|     assert(d && length && free_cb); |     assert(d && length && free_cb); | ||||||
|     b = pa_xmalloc(sizeof(struct pa_memblock)); |     b = pa_xmalloc(sizeof(struct pa_memblock)); | ||||||
|  | @ -96,6 +104,7 @@ struct pa_memblock *pa_memblock_new_user(void *d, size_t length, void (*free_cb) | ||||||
|     b->length = length; |     b->length = length; | ||||||
|     b->data = d; |     b->data = d; | ||||||
|     b->free_cb = free_cb; |     b->free_cb = free_cb; | ||||||
|  |     b->read_only = read_only; | ||||||
|     stat_add(b, s); |     stat_add(b, s); | ||||||
|     return b; |     return b; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -32,6 +32,7 @@ struct pa_memblock_stat; | ||||||
| struct pa_memblock { | struct pa_memblock { | ||||||
|     enum pa_memblock_type type; |     enum pa_memblock_type type; | ||||||
|     unsigned ref; |     unsigned ref; | ||||||
|  |     int read_only; | ||||||
|     size_t length; |     size_t length; | ||||||
|     void *data; |     void *data; | ||||||
|     void (*free_cb)(void *p); |     void (*free_cb)(void *p); | ||||||
|  | @ -39,9 +40,9 @@ struct pa_memblock { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct pa_memblock *pa_memblock_new(size_t length, struct pa_memblock_stat*s); | struct pa_memblock *pa_memblock_new(size_t length, struct pa_memblock_stat*s); | ||||||
| struct pa_memblock *pa_memblock_new_fixed(void *data, size_t length, struct pa_memblock_stat*s); |  | ||||||
| struct pa_memblock *pa_memblock_new_dynamic(void *data, size_t length, struct pa_memblock_stat*s); | struct pa_memblock *pa_memblock_new_dynamic(void *data, size_t length, struct pa_memblock_stat*s); | ||||||
| struct pa_memblock *pa_memblock_new_user(void *data, size_t length, void (*free_cb)(void *p), struct pa_memblock_stat*s); | struct pa_memblock *pa_memblock_new_fixed(void *data, size_t length, int read_only, struct pa_memblock_stat*s); | ||||||
|  | struct pa_memblock *pa_memblock_new_user(void *data, size_t length, void (*free_cb)(void *p), int read_only, struct pa_memblock_stat*s); | ||||||
| 
 | 
 | ||||||
| void pa_memblock_unref(struct pa_memblock*b); | void pa_memblock_unref(struct pa_memblock*b); | ||||||
| struct pa_memblock* pa_memblock_ref(struct pa_memblock*b); | struct pa_memblock* pa_memblock_ref(struct pa_memblock*b); | ||||||
|  |  | ||||||
|  | @ -33,6 +33,7 @@ | ||||||
| #include "memblockq.h" | #include "memblockq.h" | ||||||
| #include "xmalloc.h" | #include "xmalloc.h" | ||||||
| #include "log.h" | #include "log.h" | ||||||
|  | #include "mcalign.h" | ||||||
| 
 | 
 | ||||||
| struct memblock_list { | struct memblock_list { | ||||||
|     struct memblock_list *next, *prev; |     struct memblock_list *next, *prev; | ||||||
|  |  | ||||||
							
								
								
									
										118
									
								
								polyp/memchunk.c
									
										
									
									
									
								
							
							
						
						
									
										118
									
								
								polyp/memchunk.c
									
										
									
									
									
								
							|  | @ -31,120 +31,28 @@ | ||||||
| #include "memchunk.h" | #include "memchunk.h" | ||||||
| #include "xmalloc.h" | #include "xmalloc.h" | ||||||
| 
 | 
 | ||||||
| void pa_memchunk_make_writable(struct pa_memchunk *c, struct pa_memblock_stat *s) { | void pa_memchunk_make_writable(struct pa_memchunk *c, struct pa_memblock_stat *s, size_t min) { | ||||||
|     struct pa_memblock *n; |     struct pa_memblock *n; | ||||||
|  |     size_t l; | ||||||
|     assert(c && c->memblock && c->memblock->ref >= 1); |     assert(c && c->memblock && c->memblock->ref >= 1); | ||||||
| 
 | 
 | ||||||
|     if (c->memblock->ref == 1) |     if (c->memblock->ref == 1 && !c->memblock->read_only && c->memblock->length >= c->index+min) | ||||||
|         return; |         return; | ||||||
|  | 
 | ||||||
|  |     l = c->length; | ||||||
|  |     if (l < min) | ||||||
|  |         l = min; | ||||||
|      |      | ||||||
|     n = pa_memblock_new(c->length, s); |     n = pa_memblock_new(l, s); | ||||||
|     assert(n); |     memcpy(n->data, (uint8_t*) c->memblock->data + c->index, c->length); | ||||||
|     memcpy(n->data, (uint8_t*) c->memblock->data+c->index, c->length); |  | ||||||
|     pa_memblock_unref(c->memblock); |     pa_memblock_unref(c->memblock); | ||||||
|     c->memblock = n; |     c->memblock = n; | ||||||
|     c->index = 0; |     c->index = 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void pa_memchunk_reset(struct pa_memchunk *c) { | ||||||
|  |     assert(c); | ||||||
| 
 | 
 | ||||||
| struct pa_mcalign { |     c->memblock = NULL; | ||||||
|     size_t base; |     c->length = c->index = 0; | ||||||
|     struct pa_memchunk chunk; |  | ||||||
|     uint8_t *buffer; |  | ||||||
|     size_t buffer_fill; |  | ||||||
|     struct pa_memblock_stat *memblock_stat; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| struct pa_mcalign *pa_mcalign_new(size_t base, struct pa_memblock_stat *s) { |  | ||||||
|     struct pa_mcalign *m; |  | ||||||
|     assert(base); |  | ||||||
| 
 |  | ||||||
|     m = pa_xmalloc(sizeof(struct pa_mcalign)); |  | ||||||
|     m->base = base; |  | ||||||
|     m->chunk.memblock = NULL; |  | ||||||
|     m->chunk.length = m->chunk.index = 0; |  | ||||||
|     m->buffer = NULL; |  | ||||||
|     m->buffer_fill = 0; |  | ||||||
|     m->memblock_stat = s; |  | ||||||
|     return m; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void pa_mcalign_free(struct pa_mcalign *m) { |  | ||||||
|     assert(m); |  | ||||||
| 
 |  | ||||||
|     pa_xfree(m->buffer); |  | ||||||
|      |  | ||||||
|     if (m->chunk.memblock) |  | ||||||
|         pa_memblock_unref(m->chunk.memblock); |  | ||||||
|      |  | ||||||
|     pa_xfree(m); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void pa_mcalign_push(struct pa_mcalign *m, const struct pa_memchunk *c) { |  | ||||||
|     assert(m && c && !m->chunk.memblock && c->memblock && c->length); |  | ||||||
| 
 |  | ||||||
|     m->chunk = *c; |  | ||||||
|     pa_memblock_ref(m->chunk.memblock); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| int pa_mcalign_pop(struct pa_mcalign *m, struct pa_memchunk *c) { |  | ||||||
|     int ret; |  | ||||||
|     assert(m && c && m->base > m->buffer_fill); |  | ||||||
| 
 |  | ||||||
|     if (!m->chunk.memblock) |  | ||||||
|         return -1; |  | ||||||
| 
 |  | ||||||
|     if (m->buffer_fill) { |  | ||||||
|         size_t l = m->base - m->buffer_fill; |  | ||||||
|         if (l > m->chunk.length) |  | ||||||
|             l = m->chunk.length; |  | ||||||
|         assert(m->buffer && l); |  | ||||||
| 
 |  | ||||||
|         memcpy((uint8_t*) m->buffer + m->buffer_fill, (uint8_t*) m->chunk.memblock->data + m->chunk.index, l); |  | ||||||
|         m->buffer_fill += l; |  | ||||||
|         m->chunk.index += l; |  | ||||||
|         m->chunk.length -= l; |  | ||||||
| 
 |  | ||||||
|         if (m->chunk.length == 0) { |  | ||||||
|             m->chunk.length = m->chunk.index = 0; |  | ||||||
|             pa_memblock_unref(m->chunk.memblock); |  | ||||||
|             m->chunk.memblock = NULL; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         assert(m->buffer_fill <= m->base); |  | ||||||
|         if (m->buffer_fill == m->base) { |  | ||||||
|             c->memblock = pa_memblock_new_dynamic(m->buffer, m->base, m->memblock_stat); |  | ||||||
|             assert(c->memblock); |  | ||||||
|             c->index = 0; |  | ||||||
|             c->length = m->base; |  | ||||||
|             m->buffer = NULL; |  | ||||||
|             m->buffer_fill = 0; |  | ||||||
| 
 |  | ||||||
|             return 0; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return -1; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     m->buffer_fill = m->chunk.length % m->base; |  | ||||||
| 
 |  | ||||||
|     if (m->buffer_fill) { |  | ||||||
|         assert(!m->buffer); |  | ||||||
|         m->buffer = pa_xmalloc(m->base); |  | ||||||
|         m->chunk.length -= m->buffer_fill; |  | ||||||
|         memcpy(m->buffer, (uint8_t*) m->chunk.memblock->data + m->chunk.index + m->chunk.length, m->buffer_fill); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if (m->chunk.length) { |  | ||||||
|         *c = m->chunk; |  | ||||||
|         pa_memblock_ref(c->memblock); |  | ||||||
|         ret = 0; |  | ||||||
|     } else |  | ||||||
|         ret = -1; |  | ||||||
|      |  | ||||||
|     m->chunk.length = m->chunk.index = 0; |  | ||||||
|     pa_memblock_unref(m->chunk.memblock); |  | ||||||
|     m->chunk.memblock = NULL; |  | ||||||
| 
 |  | ||||||
|     return ret; |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -24,18 +24,22 @@ | ||||||
| 
 | 
 | ||||||
| #include "memblock.h" | #include "memblock.h" | ||||||
| 
 | 
 | ||||||
|  | /* A memchunk is a part of a memblock. In contrast to the memblock, a
 | ||||||
|  |  * memchunk is not allocated dynamically or reference counted, instead | ||||||
|  |  * it is usually stored on the stack and copied around */ | ||||||
|  | 
 | ||||||
| struct pa_memchunk { | struct pa_memchunk { | ||||||
|     struct pa_memblock *memblock; |     struct pa_memblock *memblock; | ||||||
|     size_t index, length; |     size_t index, length; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| void pa_memchunk_make_writable(struct pa_memchunk *c, struct pa_memblock_stat *s); | /* Make a memchunk writable, i.e. make sure that the caller may have
 | ||||||
|  |  * exclusive access to the memblock and it is not read_only. If needed | ||||||
|  |  * the memblock in the structure is replaced by a copy. */ | ||||||
|  | void pa_memchunk_make_writable(struct pa_memchunk *c, struct pa_memblock_stat *s, size_t min); | ||||||
| 
 | 
 | ||||||
| struct pa_mcalign; | /* Invalidate a memchunk. This does not free the cotaining memblock,
 | ||||||
| 
 |  * but sets all members to zero. */ | ||||||
| struct pa_mcalign *pa_mcalign_new(size_t base, struct pa_memblock_stat *s); | void pa_memchunk_reset(struct pa_memchunk *c); | ||||||
| void pa_mcalign_free(struct pa_mcalign *m); |  | ||||||
| void pa_mcalign_push(struct pa_mcalign *m, const struct pa_memchunk *c); |  | ||||||
| int pa_mcalign_pop(struct pa_mcalign *m, struct pa_memchunk *c); |  | ||||||
| 
 | 
 | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | @ -107,7 +107,7 @@ static void out_fill_memblocks(struct userdata *u, unsigned n) { | ||||||
|         if (u->out_memblocks[u->out_current]) |         if (u->out_memblocks[u->out_current]) | ||||||
|             pa_memblock_unref_fixed(u->out_memblocks[u->out_current]); |             pa_memblock_unref_fixed(u->out_memblocks[u->out_current]); | ||||||
|              |              | ||||||
|         chunk.memblock = u->out_memblocks[u->out_current] = pa_memblock_new_fixed((uint8_t*)u->out_mmap+u->out_fragment_size*u->out_current, u->out_fragment_size, u->core->memblock_stat); |         chunk.memblock = u->out_memblocks[u->out_current] = pa_memblock_new_fixed((uint8_t*)u->out_mmap+u->out_fragment_size*u->out_current, u->out_fragment_size, 1, u->core->memblock_stat); | ||||||
|         assert(chunk.memblock); |         assert(chunk.memblock); | ||||||
|         chunk.length = chunk.memblock->length; |         chunk.length = chunk.memblock->length; | ||||||
|         chunk.index = 0; |         chunk.index = 0; | ||||||
|  | @ -148,7 +148,7 @@ static void in_post_memblocks(struct userdata *u, unsigned n) { | ||||||
|         struct pa_memchunk chunk; |         struct pa_memchunk chunk; | ||||||
|          |          | ||||||
|         if (!u->in_memblocks[u->in_current]) { |         if (!u->in_memblocks[u->in_current]) { | ||||||
|             chunk.memblock = u->in_memblocks[u->in_current] = pa_memblock_new_fixed((uint8_t*) u->in_mmap+u->in_fragment_size*u->in_current, u->in_fragment_size, u->core->memblock_stat); |             chunk.memblock = u->in_memblocks[u->in_current] = pa_memblock_new_fixed((uint8_t*) u->in_mmap+u->in_fragment_size*u->in_current, u->in_fragment_size, 1, u->core->memblock_stat); | ||||||
|             chunk.length = chunk.memblock->length; |             chunk.length = chunk.memblock->length; | ||||||
|             chunk.index = 0; |             chunk.index = 0; | ||||||
|              |              | ||||||
|  |  | ||||||
|  | @ -610,19 +610,9 @@ int pa__init(struct pa_core *c, struct pa_module*m) { | ||||||
|         goto fail; |         goto fail; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (u->server_name[0] == '/') |     if (!(u->client = pa_socket_client_new_string(c->mainloop, u->server_name, PA_NATIVE_DEFAULT_PORT))) { | ||||||
|         u->client = pa_socket_client_new_unix(c->mainloop, u->server_name); |         pa_log(__FILE__": failed to connect to server '%s'\n", u->server_name); | ||||||
|     else { |         goto fail; | ||||||
|         size_t len;  |  | ||||||
|         struct sockaddr *sa; |  | ||||||
| 
 |  | ||||||
|         if (!(sa = pa_resolve_server(u->server_name, &len, PA_NATIVE_DEFAULT_PORT))) { |  | ||||||
|             pa_log(__FILE__": failed to resolve server '%s'\n", u->server_name); |  | ||||||
|             goto fail; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         u->client = pa_socket_client_new_sockaddr(c->mainloop, sa, len); |  | ||||||
|         pa_xfree(sa); |  | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     if (!u->client) |     if (!u->client) | ||||||
|  |  | ||||||
|  | @ -56,8 +56,6 @@ | ||||||
| 
 | 
 | ||||||
| #define AUTOSPAWN_LOCK "autospawn.lock" | #define AUTOSPAWN_LOCK "autospawn.lock" | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static const struct pa_pdispatch_command command_table[PA_COMMAND_MAX] = { | static const struct pa_pdispatch_command command_table[PA_COMMAND_MAX] = { | ||||||
|     [PA_COMMAND_REQUEST] = { pa_command_request }, |     [PA_COMMAND_REQUEST] = { pa_command_request }, | ||||||
|     [PA_COMMAND_PLAYBACK_STREAM_KILLED] = { pa_command_stream_killed }, |     [PA_COMMAND_PLAYBACK_STREAM_KILLED] = { pa_command_stream_killed }, | ||||||
|  | @ -72,7 +70,6 @@ static void unlock_autospawn_lock_file(struct pa_context *c) { | ||||||
|         pa_unlock_lockfile(c->autospawn_lock_fd); |         pa_unlock_lockfile(c->autospawn_lock_fd); | ||||||
|         c->autospawn_lock_fd = -1; |         c->autospawn_lock_fd = -1; | ||||||
|     } |     } | ||||||
|      |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| struct pa_context *pa_context_new(struct pa_mainloop_api *mainloop, const char *name) { | struct pa_context *pa_context_new(struct pa_mainloop_api *mainloop, const char *name) { | ||||||
|  | @ -106,6 +103,7 @@ struct pa_context *pa_context_new(struct pa_mainloop_api *mainloop, const char * | ||||||
|     c->memblock_stat = pa_memblock_stat_new(); |     c->memblock_stat = pa_memblock_stat_new(); | ||||||
|     c->local = -1; |     c->local = -1; | ||||||
|     c->server_list = NULL; |     c->server_list = NULL; | ||||||
|  |     c->server = NULL; | ||||||
|     c->autospawn_lock_fd = -1; |     c->autospawn_lock_fd = -1; | ||||||
|     memset(&c->spawn_api, 0, sizeof(c->spawn_api)); |     memset(&c->spawn_api, 0, sizeof(c->spawn_api)); | ||||||
|     c->do_autospawn = 0; |     c->do_autospawn = 0; | ||||||
|  | @ -155,6 +153,7 @@ static void context_free(struct pa_context *c) { | ||||||
|     pa_strlist_free(c->server_list); |     pa_strlist_free(c->server_list); | ||||||
|      |      | ||||||
|     pa_xfree(c->name); |     pa_xfree(c->name); | ||||||
|  |     pa_xfree(c->server); | ||||||
|     pa_xfree(c); |     pa_xfree(c); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -246,9 +245,20 @@ static void pstream_memblock_callback(struct pa_pstream *p, uint32_t channel, ui | ||||||
|     pa_context_ref(c); |     pa_context_ref(c); | ||||||
|      |      | ||||||
|     if ((s = pa_dynarray_get(c->record_streams, channel))) { |     if ((s = pa_dynarray_get(c->record_streams, channel))) { | ||||||
|         if (s->read_callback) { |         pa_mcalign_push(s->mcalign, chunk); | ||||||
|             s->read_callback(s, (uint8_t*) chunk->memblock->data + chunk->index, chunk->length, s->read_userdata); | 
 | ||||||
|             s->counter += chunk->length; |         for (;;) { | ||||||
|  |             struct pa_memchunk t; | ||||||
|  | 
 | ||||||
|  |             if (pa_mcalign_pop(s->mcalign, &t) < 0) | ||||||
|  |                 break; | ||||||
|  |          | ||||||
|  |             if (s->read_callback) { | ||||||
|  |                 s->read_callback(s, (uint8_t*) t.memblock->data + t.index, t.length, s->read_userdata); | ||||||
|  |                 s->counter += chunk->length; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             pa_memblock_unref(t.memblock); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -496,6 +506,9 @@ static int try_next_connection(struct pa_context *c) { | ||||||
|         } |         } | ||||||
|          |          | ||||||
| /*          pa_log(__FILE__": Trying to connect to %s...\n", u);  */ | /*          pa_log(__FILE__": Trying to connect to %s...\n", u);  */ | ||||||
|  | 
 | ||||||
|  |         pa_xfree(c->server); | ||||||
|  |         c->server = pa_xstrdup(u); | ||||||
|          |          | ||||||
|         if (!(c->client = pa_socket_client_new_string(c->mainloop, u, PA_NATIVE_DEFAULT_PORT))) |         if (!(c->client = pa_socket_client_new_string(c->mainloop, u, PA_NATIVE_DEFAULT_PORT))) | ||||||
|             continue; |             continue; | ||||||
|  | @ -811,3 +824,15 @@ const char* pa_get_library_version(void) { | ||||||
|     return PACKAGE_VERSION; |     return PACKAGE_VERSION; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | const char* pa_context_get_server(struct pa_context *c) { | ||||||
|  | 
 | ||||||
|  |     if (!c->server) | ||||||
|  |         return NULL; | ||||||
|  |      | ||||||
|  |     if (*c->server == '{') { | ||||||
|  |         char *e = strchr(c->server+1, '}'); | ||||||
|  |         return e ? e+1 : c->server; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     return c->server; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -108,6 +108,9 @@ int pa_context_is_local(struct pa_context *c); | ||||||
| /** Set a different application name for context on the server. \since 0.5 */ | /** Set a different application name for context on the server. \since 0.5 */ | ||||||
| struct pa_operation* pa_context_set_name(struct pa_context *c, const char *name, void(*cb)(struct pa_context*c, int success,  void *userdata), void *userdata); | struct pa_operation* pa_context_set_name(struct pa_context *c, const char *name, void(*cb)(struct pa_context*c, int success,  void *userdata), void *userdata); | ||||||
| 
 | 
 | ||||||
|  | /** Return the server name this context is connected to. \since 0.7 */ | ||||||
|  | const char* pa_context_get_server(struct pa_context *c); | ||||||
|  | 
 | ||||||
| PA_C_DECL_END | PA_C_DECL_END | ||||||
| 
 | 
 | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | @ -35,6 +35,7 @@ | ||||||
| #include "native-common.h" | #include "native-common.h" | ||||||
| #include "client-conf.h" | #include "client-conf.h" | ||||||
| #include "strlist.h" | #include "strlist.h" | ||||||
|  | #include "mcalign.h" | ||||||
| 
 | 
 | ||||||
| #define DEFAULT_TLENGTH (44100*2*2/2)  //(10240*8)
 | #define DEFAULT_TLENGTH (44100*2*2/2)  //(10240*8)
 | ||||||
| #define DEFAULT_MAXLENGTH ((DEFAULT_TLENGTH*3)/2) | #define DEFAULT_MAXLENGTH ((DEFAULT_TLENGTH*3)/2) | ||||||
|  | @ -77,6 +78,8 @@ struct pa_context { | ||||||
|      |      | ||||||
|     struct pa_strlist *server_list; |     struct pa_strlist *server_list; | ||||||
| 
 | 
 | ||||||
|  |     char *server; | ||||||
|  | 
 | ||||||
|     struct pa_client_conf *conf; |     struct pa_client_conf *conf; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -97,6 +100,7 @@ struct pa_stream { | ||||||
|     uint64_t counter; |     uint64_t counter; | ||||||
|     pa_usec_t previous_time; |     pa_usec_t previous_time; | ||||||
|     enum pa_stream_state state; |     enum pa_stream_state state; | ||||||
|  |     struct pa_mcalign *mcalign; | ||||||
| 
 | 
 | ||||||
|     int interpolate; |     int interpolate; | ||||||
|     int corked; |     int corked; | ||||||
|  |  | ||||||
|  | @ -61,6 +61,8 @@ struct pa_stream *pa_stream_new(struct pa_context *c, const char *name, const st | ||||||
|     s->state = PA_STREAM_DISCONNECTED; |     s->state = PA_STREAM_DISCONNECTED; | ||||||
|     memset(&s->buffer_attr, 0, sizeof(s->buffer_attr)); |     memset(&s->buffer_attr, 0, sizeof(s->buffer_attr)); | ||||||
| 
 | 
 | ||||||
|  |     s->mcalign = pa_mcalign_new(pa_frame_size(ss), c->memblock_stat); | ||||||
|  | 
 | ||||||
|     s->counter = 0; |     s->counter = 0; | ||||||
|     s->previous_time = 0; |     s->previous_time = 0; | ||||||
| 
 | 
 | ||||||
|  | @ -83,6 +85,8 @@ static void stream_free(struct pa_stream *s) { | ||||||
|         assert(s->mainloop); |         assert(s->mainloop); | ||||||
|         s->mainloop->time_free(s->ipol_event); |         s->mainloop->time_free(s->ipol_event); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     pa_mcalign_free(s->mcalign); | ||||||
|      |      | ||||||
|     pa_xfree(s->name); |     pa_xfree(s->name); | ||||||
|     pa_xfree(s); |     pa_xfree(s); | ||||||
|  | @ -203,11 +207,13 @@ static void ipol_callback(struct pa_mainloop_api *m, struct pa_time_event *e, co | ||||||
|     struct pa_stream *s = userdata; |     struct pa_stream *s = userdata; | ||||||
| 
 | 
 | ||||||
|     pa_stream_ref(s); |     pa_stream_ref(s); | ||||||
|     pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL)); |  | ||||||
| 
 | 
 | ||||||
|  |     if (s->state == PA_STREAM_READY) | ||||||
|  |         pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL)); | ||||||
|  |      | ||||||
|     gettimeofday(&tv2, NULL); |     gettimeofday(&tv2, NULL); | ||||||
|     tv2.tv_usec += LATENCY_IPOL_INTERVAL_USEC; |     tv2.tv_usec += LATENCY_IPOL_INTERVAL_USEC; | ||||||
| 
 |      | ||||||
|     m->time_restart(e, &tv2); |     m->time_restart(e, &tv2); | ||||||
|      |      | ||||||
|     pa_stream_unref(s); |     pa_stream_unref(s); | ||||||
|  | @ -329,7 +335,7 @@ void pa_stream_write(struct pa_stream *s, const void *data, size_t length, void | ||||||
|     assert(s && s->context && data && length && s->state == PA_STREAM_READY && s->ref >= 1); |     assert(s && s->context && data && length && s->state == PA_STREAM_READY && s->ref >= 1); | ||||||
| 
 | 
 | ||||||
|     if (free_cb) { |     if (free_cb) { | ||||||
|         chunk.memblock = pa_memblock_new_user((void*) data, length, free_cb, s->context->memblock_stat); |         chunk.memblock = pa_memblock_new_user((void*) data, length, free_cb, 1, s->context->memblock_stat); | ||||||
|         assert(chunk.memblock && chunk.memblock->data); |         assert(chunk.memblock && chunk.memblock->data); | ||||||
|     } else { |     } else { | ||||||
|         chunk.memblock = pa_memblock_new(length, s->context->memblock_stat); |         chunk.memblock = pa_memblock_new(length, s->context->memblock_stat); | ||||||
|  |  | ||||||
|  | @ -42,7 +42,6 @@ | ||||||
| #include "scache.h" | #include "scache.h" | ||||||
| #include "sample-util.h" | #include "sample-util.h" | ||||||
| #include "authkey.h" | #include "authkey.h" | ||||||
| #include "debug.h" |  | ||||||
| #include "namereg.h" | #include "namereg.h" | ||||||
| #include "xmalloc.h" | #include "xmalloc.h" | ||||||
| #include "log.h" | #include "log.h" | ||||||
|  |  | ||||||
|  | @ -1343,7 +1343,7 @@ static void command_get_server_info(struct pa_pdispatch *pd, uint32_t command, u | ||||||
|     pa_tagstruct_puts(reply, PACKAGE_NAME); |     pa_tagstruct_puts(reply, PACKAGE_NAME); | ||||||
|     pa_tagstruct_puts(reply, PACKAGE_VERSION); |     pa_tagstruct_puts(reply, PACKAGE_VERSION); | ||||||
|     pa_tagstruct_puts(reply, pa_get_user_name(txt, sizeof(txt))); |     pa_tagstruct_puts(reply, pa_get_user_name(txt, sizeof(txt))); | ||||||
|     pa_tagstruct_puts(reply, pa_get_host_name(txt, sizeof(txt))); |     pa_tagstruct_puts(reply, pa_get_fqdn(txt, sizeof(txt))); | ||||||
|     pa_tagstruct_put_sample_spec(reply, &c->protocol->core->default_sample_spec); |     pa_tagstruct_put_sample_spec(reply, &c->protocol->core->default_sample_spec); | ||||||
| 
 | 
 | ||||||
|     n = pa_namereg_get_default_sink_name(c->protocol->core); |     n = pa_namereg_get_default_sink_name(c->protocol->core); | ||||||
|  |  | ||||||
|  | @ -27,6 +27,12 @@ | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
|  | #include <sys/types.h> | ||||||
|  | #include <dirent.h> | ||||||
|  | #include <sys/stat.h> | ||||||
|  | #include <errno.h> | ||||||
|  | #include <limits.h> | ||||||
|  | #include <glob.h> | ||||||
| 
 | 
 | ||||||
| #include "scache.h" | #include "scache.h" | ||||||
| #include "sink-input.h" | #include "sink-input.h" | ||||||
|  | @ -38,6 +44,7 @@ | ||||||
| #include "namereg.h" | #include "namereg.h" | ||||||
| #include "sound-file.h" | #include "sound-file.h" | ||||||
| #include "util.h" | #include "util.h" | ||||||
|  | #include "log.h" | ||||||
| 
 | 
 | ||||||
| #define UNLOAD_POLL_TIME 2 | #define UNLOAD_POLL_TIME 2 | ||||||
| 
 | 
 | ||||||
|  | @ -291,3 +298,58 @@ void pa_scache_unload_unused(struct pa_core *c) { | ||||||
|         pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index); |         pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | static void add_file(struct pa_core *c, const char *pathname) { | ||||||
|  |     struct stat st; | ||||||
|  |     const char *e; | ||||||
|  | 
 | ||||||
|  |     if (!(e = strrchr(pathname, '/'))) | ||||||
|  |         e = pathname; | ||||||
|  |     else | ||||||
|  |         e++; | ||||||
|  |      | ||||||
|  |     if (stat(pathname, &st) < 0) { | ||||||
|  |         pa_log(__FILE__": stat('%s') failed: %s\n", pathname, strerror(errno)); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) | ||||||
|  |         pa_scache_add_file_lazy(c, e, pathname, NULL); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int pa_scache_add_directory_lazy(struct pa_core *c, const char *pathname) { | ||||||
|  |     DIR *dir; | ||||||
|  |     assert(c && pathname); | ||||||
|  | 
 | ||||||
|  |     /* First try to open this as directory */ | ||||||
|  |     if (!(dir = opendir(pathname))) { | ||||||
|  |         glob_t p; | ||||||
|  |         unsigned int i; | ||||||
|  |         /* If that fails, try to open it as shell glob */ | ||||||
|  | 
 | ||||||
|  |         if (glob(pathname, GLOB_ERR|GLOB_NOSORT, NULL, &p) < 0) { | ||||||
|  |             pa_log(__FILE__": Failed to open directory: %s\n", strerror(errno)); | ||||||
|  |             return -1; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         for (i = 0; i < p.gl_pathc; i++) | ||||||
|  |             add_file(c, p.gl_pathv[i]); | ||||||
|  |          | ||||||
|  |         globfree(&p); | ||||||
|  |     } else { | ||||||
|  |         struct dirent *e; | ||||||
|  | 
 | ||||||
|  |         while ((e = readdir(dir))) { | ||||||
|  |             char p[PATH_MAX]; | ||||||
|  | 
 | ||||||
|  |             if (e->d_name[0] == '.') | ||||||
|  |                 continue; | ||||||
|  | 
 | ||||||
|  |             snprintf(p, sizeof(p), "%s/%s", pathname, e->d_name); | ||||||
|  |             add_file(c, p); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     closedir(dir); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -45,6 +45,8 @@ int pa_scache_add_item(struct pa_core *c, const char *name, struct pa_sample_spe | ||||||
| int pa_scache_add_file(struct pa_core *c, const char *name, const char *filename, uint32_t *index); | int pa_scache_add_file(struct pa_core *c, const char *name, const char *filename, uint32_t *index); | ||||||
| int pa_scache_add_file_lazy(struct pa_core *c, const char *name, const char *filename, uint32_t *index); | int pa_scache_add_file_lazy(struct pa_core *c, const char *name, const char *filename, uint32_t *index); | ||||||
| 
 | 
 | ||||||
|  | int pa_scache_add_directory_lazy(struct pa_core *c, const char *pathname); | ||||||
|  | 
 | ||||||
| int pa_scache_remove_item(struct pa_core *c, const char *name); | int pa_scache_remove_item(struct pa_core *c, const char *name); | ||||||
| int pa_scache_play_item(struct pa_core *c, const char *name, struct pa_sink *sink, uint32_t volume); | int pa_scache_play_item(struct pa_core *c, const char *name, struct pa_sink *sink, uint32_t volume); | ||||||
| void pa_scache_free(struct pa_core *c); | void pa_scache_free(struct pa_core *c); | ||||||
|  |  | ||||||
|  | @ -225,7 +225,7 @@ int pa_sink_render(struct pa_sink*s, size_t length, struct pa_memchunk *result) | ||||||
|             volume = pa_volume_multiply(s->volume, info[0].volume); |             volume = pa_volume_multiply(s->volume, info[0].volume); | ||||||
|          |          | ||||||
|         if (volume != PA_VOLUME_NORM) { |         if (volume != PA_VOLUME_NORM) { | ||||||
|             pa_memchunk_make_writable(result, s->core->memblock_stat); |             pa_memchunk_make_writable(result, s->core->memblock_stat, 0); | ||||||
|             pa_volume_memchunk(result, &s->sample_spec, volume); |             pa_volume_memchunk(result, &s->sample_spec, volume); | ||||||
|         } |         } | ||||||
|     } else { |     } else { | ||||||
|  |  | ||||||
|  | @ -338,16 +338,24 @@ struct pa_socket_client* pa_socket_client_new_string(struct pa_mainloop_api *m, | ||||||
|             memset(&hints, 0, sizeof(hints)); |             memset(&hints, 0, sizeof(hints)); | ||||||
|             hints.ai_family = kind == KIND_TCP4 ? AF_INET : (kind == KIND_TCP6 ? AF_INET6 : AF_UNSPEC); |             hints.ai_family = kind == KIND_TCP4 ? AF_INET : (kind == KIND_TCP6 ? AF_INET6 : AF_UNSPEC); | ||||||
|              |              | ||||||
|             if (getaddrinfo(h, NULL, &hints, &res) < 0 || !res) |             if (getaddrinfo(h, NULL, &hints, &res) < 0 || !res || !res->ai_addr) | ||||||
|                 return NULL; |                 return NULL; | ||||||
| 
 | 
 | ||||||
|             if (res->ai_addr->sa_family == AF_INET) |             if (res->ai_family == AF_INET) { | ||||||
|  |                 if (res->ai_addrlen != sizeof(struct sockaddr_in)) | ||||||
|  |                     return NULL; | ||||||
|  |                 assert(res->ai_addr->sa_family == res->ai_family); | ||||||
|  |                  | ||||||
|                 ((struct sockaddr_in*) res->ai_addr)->sin_port = htons(port); |                 ((struct sockaddr_in*) res->ai_addr)->sin_port = htons(port); | ||||||
|             else if (res->ai_addr->sa_family == AF_INET6) |             } else if (res->ai_family == AF_INET6) { | ||||||
|  |                 if (res->ai_addrlen != sizeof(struct sockaddr_in6)) | ||||||
|  |                     return NULL; | ||||||
|  |                 assert(res->ai_addr->sa_family == res->ai_family); | ||||||
|  |                  | ||||||
|                 ((struct sockaddr_in6*) res->ai_addr)->sin6_port = htons(port); |                 ((struct sockaddr_in6*) res->ai_addr)->sin6_port = htons(port); | ||||||
|             else |             } else | ||||||
|                 return NULL; |                 return NULL; | ||||||
|              | 
 | ||||||
|             c = pa_socket_client_new_sockaddr(m, res->ai_addr, res->ai_addrlen); |             c = pa_socket_client_new_sockaddr(m, res->ai_addr, res->ai_addrlen); | ||||||
|             freeaddrinfo(res); |             freeaddrinfo(res); | ||||||
|             return c; |             return c; | ||||||
|  | @ -360,6 +368,9 @@ struct pa_socket_client* pa_socket_client_new_string(struct pa_mainloop_api *m, | ||||||
|      |      | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /* Return non-zero when the target sockaddr is considered
 | ||||||
|  |    local. "local" means UNIX socket or TCP socket on localhost. Other | ||||||
|  |    local IP addresses are not considered local. */ | ||||||
| int pa_socket_client_is_local(struct pa_socket_client *c) { | int pa_socket_client_is_local(struct pa_socket_client *c) { | ||||||
|     assert(c); |     assert(c); | ||||||
|     return c->local; |     return c->local; | ||||||
|  |  | ||||||
|  | @ -202,36 +202,3 @@ int pa_unix_socket_remove_stale(const char *fn) { | ||||||
| 
 | 
 | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| 
 |  | ||||||
| struct sockaddr *pa_resolve_server(const char *server, size_t *len, uint16_t nport) { |  | ||||||
|     struct sockaddr *sa; |  | ||||||
|     struct addrinfo hints, *result = NULL; |  | ||||||
|     char *port, host[256], tmp[16]; |  | ||||||
|     assert(server && len); |  | ||||||
| 
 |  | ||||||
|     snprintf(host, sizeof(host), "%s", server); |  | ||||||
|     host[strcspn(host, ":")] = 0; |  | ||||||
|      |  | ||||||
|     if ((port = strrchr(server, ':'))) |  | ||||||
|         port++; |  | ||||||
|      |  | ||||||
|     if (!port)  |  | ||||||
|         snprintf(port = tmp, sizeof(tmp), "%u", nport); |  | ||||||
| 
 |  | ||||||
|     memset(&hints, 0, sizeof(hints)); |  | ||||||
|     hints.ai_family = PF_UNSPEC; |  | ||||||
|     hints.ai_socktype = SOCK_STREAM; |  | ||||||
|     hints.ai_protocol = 0; |  | ||||||
| 
 |  | ||||||
|     if (getaddrinfo(host, port, &hints, &result) != 0) |  | ||||||
|         return NULL; |  | ||||||
|     assert(result); |  | ||||||
|      |  | ||||||
|     sa = pa_xmalloc(*len = result->ai_addrlen); |  | ||||||
|     memcpy(sa, result->ai_addr, *len); |  | ||||||
| 
 |  | ||||||
|     freeaddrinfo(result); |  | ||||||
|      |  | ||||||
|     return sa; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
|  |  | ||||||
|  | @ -35,6 +35,4 @@ int pa_socket_set_rcvbuf(int fd, size_t l); | ||||||
| int pa_unix_socket_is_stale(const char *fn); | int pa_unix_socket_is_stale(const char *fn); | ||||||
| int pa_unix_socket_remove_stale(const char *fn); | int pa_unix_socket_remove_stale(const char *fn); | ||||||
| 
 | 
 | ||||||
| struct sockaddr *pa_resolve_server(const char *server, size_t *len, uint16_t port); |  | ||||||
| 
 |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | @ -33,6 +33,7 @@ | ||||||
| 
 | 
 | ||||||
| #include "strbuf.h" | #include "strbuf.h" | ||||||
| 
 | 
 | ||||||
|  | /* Some magic for zero-length arrays */ | ||||||
| #ifdef __STDC_VERSION__ | #ifdef __STDC_VERSION__ | ||||||
| #if __STDC_VERSION__ >= 199901L | #if __STDC_VERSION__ >= 199901L | ||||||
| #ifndef STDC99 | #ifndef STDC99 | ||||||
|  | @ -41,6 +42,7 @@ | ||||||
| #endif | #endif | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | /* A chunk of the linked list that makes up the string */ | ||||||
| struct chunk { | struct chunk { | ||||||
|     struct chunk *next; |     struct chunk *next; | ||||||
|     size_t length; |     size_t length; | ||||||
|  | @ -74,6 +76,8 @@ void pa_strbuf_free(struct pa_strbuf *sb) { | ||||||
|     pa_xfree(sb); |     pa_xfree(sb); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /* Make a C string from the string buffer. The caller has to free
 | ||||||
|  |  * string with pa_xfree(). */ | ||||||
| char *pa_strbuf_tostring(struct pa_strbuf *sb) { | char *pa_strbuf_tostring(struct pa_strbuf *sb) { | ||||||
|     char *t, *e; |     char *t, *e; | ||||||
|     struct chunk *c; |     struct chunk *c; | ||||||
|  | @ -83,15 +87,18 @@ char *pa_strbuf_tostring(struct pa_strbuf *sb) { | ||||||
| 
 | 
 | ||||||
|     e = t; |     e = t; | ||||||
|     for (c = sb->head; c; c = c->next) { |     for (c = sb->head; c; c = c->next) { | ||||||
|  |         assert((size_t) (e-t) <= sb->length); | ||||||
|         memcpy(e, c->text, c->length); |         memcpy(e, c->text, c->length); | ||||||
|         e += c->length; |         e += c->length; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /* Trailing NUL */ | ||||||
|     *e = 0; |     *e = 0; | ||||||
|      |      | ||||||
|     return t; |     return t; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /* Combination of pa_strbuf_free() and pa_strbuf_tostring() */ | ||||||
| char *pa_strbuf_tostring_free(struct pa_strbuf *sb) { | char *pa_strbuf_tostring_free(struct pa_strbuf *sb) { | ||||||
|     char *t; |     char *t; | ||||||
|     assert(sb); |     assert(sb); | ||||||
|  | @ -100,23 +107,15 @@ char *pa_strbuf_tostring_free(struct pa_strbuf *sb) { | ||||||
|     return t; |     return t; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /* Append a string to the string buffer */ | ||||||
| void pa_strbuf_puts(struct pa_strbuf *sb, const char *t) { | void pa_strbuf_puts(struct pa_strbuf *sb, const char *t) { | ||||||
|     assert(sb && t); |     assert(sb && t); | ||||||
|     pa_strbuf_putsn(sb, t, strlen(t)); |     pa_strbuf_putsn(sb, t, strlen(t)); | ||||||
| }  | } | ||||||
| 
 | 
 | ||||||
| void pa_strbuf_putsn(struct pa_strbuf *sb, const char *t, size_t l) { | /* Append a new chunk to the linked list */ | ||||||
|     struct chunk *c; | static void append(struct pa_strbuf *sb, struct chunk *c) { | ||||||
|     assert(sb && t); |     assert(sb && c); | ||||||
|      |  | ||||||
|     if (!l) |  | ||||||
|        return; |  | ||||||
|     |  | ||||||
|     c = pa_xmalloc(sizeof(struct chunk)+l); |  | ||||||
| 
 |  | ||||||
|     c->next = NULL; |  | ||||||
|     c->length = l; |  | ||||||
|     memcpy(c->text, t, l); |  | ||||||
| 
 | 
 | ||||||
|     if (sb->tail) { |     if (sb->tail) { | ||||||
|         assert(sb->head); |         assert(sb->head); | ||||||
|  | @ -127,19 +126,36 @@ void pa_strbuf_putsn(struct pa_strbuf *sb, const char *t, size_t l) { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     sb->tail = c; |     sb->tail = c; | ||||||
|     sb->length += l; |     sb->length += c->length; | ||||||
|  |     c->next = NULL; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /* The following is based on an example from the GNU libc documentation */ | /* Append up to l bytes of a string to the string buffer */ | ||||||
|  | void pa_strbuf_putsn(struct pa_strbuf *sb, const char *t, size_t l) { | ||||||
|  |     struct chunk *c; | ||||||
|  |     assert(sb && t); | ||||||
|  |      | ||||||
|  |     if (!l) | ||||||
|  |        return; | ||||||
|  |     | ||||||
|  |     c = pa_xmalloc(sizeof(struct chunk)+l); | ||||||
|  |     c->length = l; | ||||||
|  |     memcpy(c->text, t, l); | ||||||
| 
 | 
 | ||||||
|  |     append(sb, c); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Append a printf() style formatted string to the string buffer. */ | ||||||
|  | /* The following is based on an example from the GNU libc documentation */ | ||||||
| int pa_strbuf_printf(struct pa_strbuf *sb, const char *format, ...) { | int pa_strbuf_printf(struct pa_strbuf *sb, const char *format, ...) { | ||||||
|     int r, size = 100; |     int size = 100; | ||||||
|     struct chunk *c = NULL; |     struct chunk *c = NULL; | ||||||
| 
 | 
 | ||||||
|     assert(sb); |     assert(sb); | ||||||
|      |      | ||||||
|     for(;;) { |     for(;;) { | ||||||
|         va_list ap; |         va_list ap; | ||||||
|  |         int r; | ||||||
| 
 | 
 | ||||||
|         c = pa_xrealloc(c, sizeof(struct chunk)+size); |         c = pa_xrealloc(c, sizeof(struct chunk)+size); | ||||||
| 
 | 
 | ||||||
|  | @ -149,19 +165,7 @@ int pa_strbuf_printf(struct pa_strbuf *sb, const char *format, ...) { | ||||||
|          |          | ||||||
|         if (r > -1 && r < size) { |         if (r > -1 && r < size) { | ||||||
|             c->length = r; |             c->length = r; | ||||||
|             c->next = NULL; |             append(sb, c); | ||||||
|              |  | ||||||
|             if (sb->tail) { |  | ||||||
|                 assert(sb->head); |  | ||||||
|                 sb->tail->next = c; |  | ||||||
|             } else { |  | ||||||
|                 assert(!sb->head); |  | ||||||
|                 sb->head = c; |  | ||||||
|             } |  | ||||||
|              |  | ||||||
|             sb->tail = c; |  | ||||||
|             sb->length += r; |  | ||||||
|              |  | ||||||
|             return r; |             return r; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Lennart Poettering
						Lennart Poettering