From de8dba1ff67c02e0de4bc5bb4f612c95a425ccf0 Mon Sep 17 00:00:00 2001 From: paul Date: Fri, 28 Nov 2008 07:31:16 +0000 Subject: [PATCH] netjack changes from torben (packet loss handling leads to "dummy" backend-like behaviour; jitter buffer), along with smaller mods to alsa_{in,out} git-svn-id: svn+ssh://jackaudio.org/trunk/jack@3127 0c269be4-1314-0410-8aa9-9f06e86f4224 --- drivers/netjack/net_driver.c | 178 ++++++++++++++++++------ drivers/netjack/net_driver.h | 12 ++ drivers/netjack/netjack_packet.c | 230 +++++++++++++++++++++++++++++-- drivers/netjack/netjack_packet.h | 24 +++- tools/Makefile.am | 4 +- tools/alsa_in.c | 80 ++++++++--- tools/alsa_out.c | 105 ++++++++++---- tools/netsource.c | 31 ++--- 8 files changed, 538 insertions(+), 126 deletions(-) diff --git a/drivers/netjack/net_driver.c b/drivers/netjack/net_driver.c index be24c9b..2f8fb5e 100644 --- a/drivers/netjack/net_driver.c +++ b/drivers/netjack/net_driver.c @@ -85,29 +85,116 @@ net_driver_wait (net_driver_t *driver, int extra_fd, int *status, float *delayed // on packet loss we should either detect an xrun or just continue running when we // think, that the sync source is not running anymore. - socklen_t address_size = sizeof (struct sockaddr_in); - int bufsize, len; + //socklen_t address_size = sizeof (struct sockaddr_in); + //int len; + int we_have_the_expected_frame = 0; + jack_nframes_t next_frame_avail; jacknet_packet_header *pkthdr = (jacknet_packet_header *) driver->rx_buf; - bufsize = get_sample_size (driver->bitdepth) * driver->capture_channels * driver->net_period_down + sizeof (jacknet_packet_header); + if( !driver->next_deadline_valid ) { + driver->next_deadline = jack_get_microseconds() + 2*driver->period_usecs; + driver->next_deadline_valid = 1; + } else { + driver->next_deadline += driver->period_usecs; + } - if (netjack_poll (driver->sockfd, 500)) - len = netjack_recvfrom (driver->sockfd, (char *)driver->rx_buf, bufsize, MSG_WAITALL, (struct sockaddr*) & driver->syncsource_address, &address_size, driver->mtu); - else - len = 0; - while (len != bufsize) - { - jack_error ("wrong_packet_len: len=%d, expected=%d", len, bufsize); - if (netjack_poll (driver->sockfd, 500)) - len = netjack_recvfrom (driver->sockfd, (char *)driver->rx_buf, bufsize, MSG_WAITALL, (struct sockaddr*) & driver->syncsource_address, &address_size, driver->mtu); - else - len = 0; + while(1) { + if( ! netjack_poll_deadline( driver->sockfd, driver->next_deadline ) ) + break; + + packet_cache_drain_socket( global_packcache, driver->sockfd ); + + if( packet_cache_get_next_available_framecnt( global_packcache, driver->expected_framecnt, &next_frame_avail) ) { + if( next_frame_avail == driver->expected_framecnt ) { + we_have_the_expected_frame = 1; + break; + } + //printf( "next frame = %d (exp: %d) \n", next_frame_avail, driver->expected_framecnt ); + } + } + + driver->running_free = 0; + + if( we_have_the_expected_frame ) { + packet_cache_retreive_packet( global_packcache, driver->expected_framecnt, (char *) driver->rx_buf, driver->rx_bufsize ); + driver->packet_data_valid = 1; + //printf( "ok... %d\n", driver->expected_framecnt ); + } else { + // bah... the packet is not there. + // either + // - it got lost. + // - its late + // - sync source is not sending anymore. + + // lets check if we have the next packets, we will just run a cycle without data. + // in that case. + + if( packet_cache_get_next_available_framecnt( global_packcache, driver->expected_framecnt, &next_frame_avail) ) + { + jack_nframes_t offset = next_frame_avail - driver->expected_framecnt; + + if( offset < driver->resync_threshold ) { + // ok. dont do nothing. we will run without data. + // this seems to be one or 2 lost packets. + driver->packet_data_valid = 0; + //printf( "lost packet... %d\n", driver->expected_framecnt ); + + } else { + // the diff is too high. but we have a packet. + // lets resync. + driver->expected_framecnt = next_frame_avail; + packet_cache_retreive_packet( global_packcache, driver->expected_framecnt, (char *) driver->rx_buf, driver->rx_bufsize ); + driver->next_deadline_valid = 0; + driver->packet_data_valid = 1; + //printf( "resync... expected: %d, offset=%d\n", driver->expected_framecnt, offset ); + } + + } else { + // no packets in buffer. + driver->packet_data_valid = 0; + + if( driver->num_lost_packets < 3 ) { + // increase deadline. + driver->next_deadline += driver->period_usecs/4; + // might be lost packets. + // continue + } else if( (driver->num_lost_packets <= 10) ) { + // lets try adjusting the deadline, for some packets, we might have just ran 2 fast. + } else { + // give up. lets run freely. + driver->running_free = 1; + + // But now we can check for any new frame available. + if( packet_cache_get_next_available_framecnt( global_packcache, 0, &next_frame_avail) ) { + driver->expected_framecnt = next_frame_avail; + packet_cache_retreive_packet( global_packcache, driver->expected_framecnt, (char *) driver->rx_buf, driver->rx_bufsize ); + driver->next_deadline_valid = 0; + driver->packet_data_valid = 1; + driver->running_free = 1; + //printf( "resync after freerun... %d\n", driver->expected_framecnt ); + } + } + + //printf( "free... %d\n", driver->expected_framecnt ); + } } - packet_header_ntoh (pkthdr); + if( !driver->packet_data_valid ) + driver->num_lost_packets += 1; + else { + driver->num_lost_packets = 0; + packet_header_ntoh (pkthdr); + } + + framecnt = driver->expected_framecnt; + driver->expected_framecnt += 1; + + driver->last_wait_ust = jack_get_microseconds (); driver->engine->transport_cycle_start (driver->engine, driver->last_wait_ust); + /* this driver doesn't work so well if we report a delay */ + /* XXX: this might not be the case anymore */ *delayed_usecs = 0; /* lie about it */ *status = 0; return driver->period_size; @@ -123,19 +210,9 @@ net_driver_run_cycle (net_driver_t *driver) jack_nframes_t nframes = net_driver_wait (driver, -1, &wait_status, &delayed_usecs); - // currently there is no xrun detection. - // so nframes will always be period_size. - // XXX: i uncomment this code because the signature of delay() - // changed samewhere in the 0.99.x series. so this is the showstopper for 0.99.0 - -#if 0 - if (nframes == 0) { - /* we detected an xrun and restarted: notify - * clients about the delay. */ - engine->delay (engine, delayed_usecs); - return 0; - } -#endif + // XXX: xrun code removed. + // especially with celt there are no real xruns anymore. + // things are different on the net. if (wait_status == 0) return engine->run_cycle (engine, nframes, delayed_usecs); @@ -149,6 +226,10 @@ net_driver_run_cycle (net_driver_t *driver) static int net_driver_null_cycle (net_driver_t* driver, jack_nframes_t nframes) { + // TODO: talk to paul about this. + // do i wait here ? + // just sending out a packet marked with junk ? + //int rx_size = get_sample_size(driver->bitdepth) * driver->capture_channels * driver->net_period_down + sizeof(jacknet_packet_header); int tx_size = get_sample_size(driver->bitdepth) * driver->playback_channels * driver->net_period_up + sizeof(jacknet_packet_header); unsigned int *packet_buf, *packet_bufX; @@ -157,7 +238,7 @@ net_driver_null_cycle (net_driver_t* driver, jack_nframes_t nframes) jacknet_packet_header *tx_pkthdr = (jacknet_packet_header *)packet_buf; jacknet_packet_header *rx_pkthdr = (jacknet_packet_header *)driver->rx_buf; - framecnt = rx_pkthdr->framecnt; + //framecnt = rx_pkthdr->framecnt; driver->reply_port = rx_pkthdr->reply_port; @@ -195,14 +276,15 @@ net_driver_bufsize (net_driver_t* driver, jack_nframes_t nframes) static int net_driver_read (net_driver_t* driver, jack_nframes_t nframes) { - //jack_default_audio_sample_t* buf; - //jack_port_t *port; jack_position_t local_trans_pos; jack_transport_state_t local_trans_state; - //int bufsize = get_sample_size(driver->bitdepth) * driver->capture_channels * driver->net_period_down + sizeof(jacknet_packet_header); unsigned int *packet_buf, *packet_bufX; + if( ! driver->packet_data_valid ) { + render_payload_to_jack_ports (driver->bitdepth, NULL, driver->net_period_down, driver->capture_ports, driver->capture_srcs, nframes); + return 0; + } packet_buf = driver->rx_buf; jacknet_packet_header *pkthdr = (jacknet_packet_header *)packet_buf; @@ -217,6 +299,8 @@ net_driver_read (net_driver_t* driver, jack_nframes_t nframes) framecnt = pkthdr->framecnt; driver->reply_port = pkthdr->reply_port; + driver->latency = pkthdr->latency; + driver->resync_threshold = pkthdr->latency-1; // check whether, we should handle the transport sync stuff, or leave trnasports untouched. if (driver->handle_transport_sync) { @@ -277,10 +361,13 @@ net_driver_write (net_driver_t* driver, jack_nframes_t nframes) uint32_t *packet_buf, *packet_bufX; int packet_size = get_sample_size(driver->bitdepth) * driver->playback_channels * driver->net_period_up + sizeof(jacknet_packet_header); + jacknet_packet_header *pkthdr; packet_buf = alloca(packet_size); + pkthdr = (jacknet_packet_header *)packet_buf; - jacknet_packet_header *pkthdr = (jacknet_packet_header *)packet_buf; + if( driver->running_free ) + return 0; // offset packet_bufX by the packetheader. packet_bufX = packet_buf + sizeof(jacknet_packet_header) / sizeof(jack_default_audio_sample_t); @@ -294,6 +381,7 @@ net_driver_write (net_driver_t* driver, jack_nframes_t nframes) if (driver->srcaddress_valid) if (driver->reply_port) driver->syncsource_address.sin_port = htons(driver->reply_port); + netjack_sendto(driver->outsockfd, (char *)packet_buf, packet_size, 0, (struct sockaddr*)&driver->syncsource_address, sizeof(struct sockaddr_in), driver->mtu); @@ -454,7 +542,7 @@ net_driver_new (jack_client_t * client, unsigned int bitdepth) { net_driver_t * driver; - int first_pack_len, rx_bufsize; + int first_pack_len; struct sockaddr_in address; jack_info ("creating net driver ... %s|%" PRIu32 "|%" PRIu32 @@ -537,6 +625,8 @@ net_driver_new (jack_client_t * client, jack_info ("Waiting for an incoming packet !!!"); jack_info ("*** IMPORTANT *** Dont connect a client to jackd until the driver is attached to a clock source !!!"); + // XXX: netjack_poll polls forever. + // thats ok here. if (netjack_poll (driver->sockfd, 500)) first_pack_len = recvfrom (driver->sockfd, first_packet, sizeof (jacknet_packet_header), 0, (struct sockaddr*) & driver->syncsource_address, &address_size); else @@ -592,7 +682,7 @@ net_driver_new (jack_client_t * client, // After possible Autoconfig: do all calculations... driver->period_usecs = - (jack_time_t) floor ((((float) driver->period_size) / driver->sample_rate) + (jack_time_t) floor ((((float) driver->period_size) / (float)driver->sample_rate) * 1000000.0f); if( driver->bitdepth == 1000 ) { @@ -605,17 +695,23 @@ net_driver_new (jack_client_t * client, driver->net_period_up = (float) driver->period_size / (float) resample_factor_up; } - /* TODO: this seems... useles */ - rx_bufsize = sizeof (jacknet_packet_header) + driver->net_period_down * driver->capture_channels * get_sample_size (driver->bitdepth); - driver->rx_buf = malloc (rx_bufsize); - driver->pkt_buf = malloc (rx_bufsize); - global_packcache = packet_cache_new (driver->latency + 5, rx_bufsize, driver->mtu); + driver->rx_bufsize = sizeof (jacknet_packet_header) + driver->net_period_down * driver->capture_channels * get_sample_size (driver->bitdepth); + driver->rx_buf = malloc (driver->rx_bufsize); + driver->pkt_buf = malloc (driver->rx_bufsize); + global_packcache = packet_cache_new (driver->latency + 5, driver->rx_bufsize, driver->mtu); + + driver->expected_framecnt_valid = 0; + driver->num_lost_packets = 0; + driver->next_deadline_valid = 0; + + driver->resync_threshold = driver->latency - 1; + driver->running_free = 0; jack_info ("netjack: period : up: %d / dn: %d", driver->net_period_up, driver->net_period_down); jack_info ("netjack: framerate: %d", driver->sample_rate); jack_info ("netjack: audio : cap: %d / pbk: %d)", driver->capture_channels_audio, driver->playback_channels_audio); jack_info ("netjack: midi : cap: %d / pbk: %d)", driver->capture_channels_midi, driver->playback_channels_midi); - jack_info ("netjack: buffsize : rx: %d)", rx_bufsize); + jack_info ("netjack: buffsize : rx: %d)", driver->rx_bufsize); return (jack_driver_t *) driver; } diff --git a/drivers/netjack/net_driver.h b/drivers/netjack/net_driver.h index aa4f7df..ee0181f 100644 --- a/drivers/netjack/net_driver.h +++ b/drivers/netjack/net_driver.h @@ -72,8 +72,20 @@ struct _net_driver unsigned int *rx_buf; unsigned int *pkt_buf; + unsigned int rx_bufsize; + //unsigned int tx_bufsize; unsigned int mtu; unsigned int latency; + + jack_nframes_t expected_framecnt; + int expected_framecnt_valid; + unsigned int num_lost_packets; + jack_time_t next_deadline; + int next_deadline_valid; + int packet_data_valid; + int resync_threshold; + int running_free; + }; #endif /* __JACK_NET_DRIVER_H__ */ diff --git a/drivers/netjack/netjack_packet.c b/drivers/netjack/netjack_packet.c index 2a849bd..d90b538 100644 --- a/drivers/netjack/netjack_packet.c +++ b/drivers/netjack/netjack_packet.c @@ -41,7 +41,11 @@ #include #include #include + +// for ppoll +#define __USE_GNU #include + #include #include @@ -115,7 +119,7 @@ packet_cache int fragment_payload_size = mtu - sizeof (jacknet_packet_header); int fragment_number = (pkt_size - sizeof (jacknet_packet_header) - 1) / fragment_payload_size + 1; int i; - + packet_cache *pcache = malloc (sizeof (packet_cache)); if (pcache == NULL) { @@ -146,6 +150,7 @@ packet_cache return NULL; } } + pcache->mtu = mtu; return pcache; } @@ -197,10 +202,15 @@ cache_packet return retval; } +// TODO: fix wrapping case... need to pass +// current expected frame here. +// +// or just save framecount into packet_cache. + cache_packet *packet_cache_get_oldest_packet (packet_cache *pcache) { - jack_nframes_t minimal_frame = 0; + jack_nframes_t minimal_frame = JACK_MAX_FRAMES; cache_packet *retval = &(pcache->packets[0]); int i; @@ -216,7 +226,6 @@ cache_packet return retval; } - cache_packet *packet_cache_get_free_packet (packet_cache *pcache) { @@ -274,9 +283,10 @@ cache_packet_add_fragment (cache_packet *pack, char *packet_buf, int rcv_len) return; } + if (fragment_nr == 0) { - memcpy (pack->packet_buf, packet_buf, pack->mtu); + memcpy (pack->packet_buf, packet_buf, rcv_len); pack->fragment_array[0] = 1; return; @@ -305,6 +315,70 @@ cache_packet_is_complete (cache_packet *pack) return TRUE; } + +// new poll using nanoseconds resolution and +// not waiting forever. +int +netjack_poll_deadline (int sockfd, jack_time_t deadline) +{ + struct pollfd fds; + int i, poll_err = 0; + sigset_t sigmask; + struct sigaction action; + struct timespec timeout_spec = { 0, 0 }; + + jack_time_t now = jack_get_microseconds(); + if( now >= deadline ) + return 0; + + timeout_spec.tv_nsec = (deadline - now) * 1000; + + sigemptyset(&sigmask); + sigaddset(&sigmask, SIGHUP); + sigaddset(&sigmask, SIGINT); + sigaddset(&sigmask, SIGQUIT); + sigaddset(&sigmask, SIGPIPE); + sigaddset(&sigmask, SIGTERM); + sigaddset(&sigmask, SIGUSR1); + sigaddset(&sigmask, SIGUSR2); + + action.sa_handler = SIG_DFL; + action.sa_mask = sigmask; + action.sa_flags = SA_RESTART; + + for (i = 1; i < NSIG; i++) + if (sigismember (&sigmask, i)) + sigaction (i, &action, 0); + + fds.fd = sockfd; + fds.events = POLLIN; + + poll_err = ppoll (&fds, 1, &timeout_spec, &sigmask); + + if (poll_err == -1) + { + switch (errno) + { + case EBADF: + jack_error ("Error %d: An invalid file descriptor was given in one of the sets", errno); + break; + case EFAULT: + jack_error ("Error %d: The array given as argument was not contained in the calling program's address space", errno); + break; + case EINTR: + jack_error ("Error %d: A signal occurred before any requested event", errno); + break; + case EINVAL: + jack_error ("Error %d: The nfds value exceeds the RLIMIT_NOFILE value", errno); + break; + case ENOMEM: + jack_error ("Error %d: There was no space to allocate file descriptor tables", errno); + break; + } + } + return poll_err; +} + int netjack_poll (int sockfd, int timeout) { @@ -365,6 +439,122 @@ netjack_poll (int sockfd, int timeout) return TRUE; } +// This now reads all a socket has into the cache. +// replacing netjack_recv functions. + +void +packet_cache_drain_socket( packet_cache *pcache, int sockfd ) +{ + char *rx_packet = alloca (pcache->mtu); + jacknet_packet_header *pkthdr = (jacknet_packet_header *) rx_packet; + int rcv_len; + jack_nframes_t framecnt; + cache_packet *cpack; + + while (1) + { + rcv_len = recv (sockfd, rx_packet, pcache->mtu, MSG_DONTWAIT); + if (rcv_len < 0) + return; + framecnt = ntohl (pkthdr->framecnt); + //printf( "Got Packet %d\n", framecnt ); + cpack = packet_cache_get_packet (global_packcache, framecnt); + cache_packet_add_fragment (cpack, rx_packet, rcv_len); + } +} + +int +packet_cache_retreive_packet( packet_cache *pcache, jack_nframes_t framecnt, char *packet_buf, int pkt_size ) +{ + int i; + cache_packet *cpack = NULL; + + for (i = 0; i < pcache->size; i++) { + if (pcache->packets[i].valid && (pcache->packets[i].framecnt == framecnt)) { + cpack = &(pcache->packets[i]); + break; + } + } + + if( cpack == NULL ) + return -1; + + if( !cache_packet_is_complete( cpack ) ) + return -1; + + // ok. cpack is the one we want and its complete. + memcpy (packet_buf, cpack->packet_buf, pkt_size); + cache_packet_reset (cpack); + return pkt_size; +} + +// Returns 0 when no valid packet is inside the cache. +int +packet_cache_get_next_available_framecnt( packet_cache *pcache, jack_nframes_t expected_framecnt, jack_nframes_t *framecnt ) +{ + int i; + jack_nframes_t best_offset = JACK_MAX_FRAMES/2-1; + int retval = 0; + + for (i = 0; i < pcache->size; i++) + { + cache_packet *cpack = &(pcache->packets[i]); + //printf( "p%d: valid=%d, frame %d\n", i, cpack->valid, cpack->framecnt ); + + if (!cpack->valid || !cache_packet_is_complete( cpack )) { + //printf( "invalid\n" ); + continue; + } + + if( (cpack->framecnt - expected_framecnt) > best_offset ) { + continue; + } + + best_offset = cpack->framecnt - expected_framecnt; + retval = 1; + + if( best_offset == 0 ) + break; + } + if( retval && framecnt ) + *framecnt = expected_framecnt + best_offset; + + return retval; +} + +// Returns 0 when no valid packet is inside the cache. +int +packet_cache_find_latency( packet_cache *pcache, jack_nframes_t expected_framecnt, jack_nframes_t *framecnt ) +{ + int i; + jack_nframes_t best_offset = 0; + int retval = 0; + + for (i = 0; i < pcache->size; i++) + { + cache_packet *cpack = &(pcache->packets[i]); + //printf( "p%d: valid=%d, frame %d\n", i, cpack->valid, cpack->framecnt ); + + if (!cpack->valid || !cache_packet_is_complete( cpack )) { + //printf( "invalid\n" ); + continue; + } + + if( (cpack->framecnt - expected_framecnt) < best_offset ) { + continue; + } + + best_offset = cpack->framecnt - expected_framecnt; + retval = 1; + + if( best_offset == 0 ) + break; + } + if( retval && framecnt ) + *framecnt = JACK_MAX_FRAMES - best_offset; + + return retval; +} // fragmented packet IO int netjack_recvfrom (int sockfd, char *packet_buf, int pkt_size, int flags, struct sockaddr *addr, socklen_t *addr_size, int mtu) @@ -427,8 +617,11 @@ netjack_sendto (int sockfd, char *packet_buf, int pkt_size, int flags, struct so int fragment_payload_size = mtu - sizeof (jacknet_packet_header); - if (pkt_size <= mtu) + if (pkt_size <= mtu) { + pkthdr = (jacknet_packet_header *) packet_buf; + pkthdr->fragment_nr = htonl (0); sendto(sockfd, packet_buf, pkt_size, flags, addr, addr_size); + } else { // Copy the packet header to the tx pack first. @@ -533,6 +726,9 @@ render_payload_to_jack_ports_float ( void *packet_payload, jack_nframes_t net_pe uint32_t *packet_bufX = (uint32_t *)packet_payload; + if( !packet_payload ) + return; + while (node != NULL) { int i; @@ -669,6 +865,9 @@ render_payload_to_jack_ports_16bit (void *packet_payload, jack_nframes_t net_per uint16_t *packet_bufX = (uint16_t *)packet_payload; + if( !packet_payload ) + return; + while (node != NULL) { int i; @@ -797,6 +996,9 @@ render_payload_to_jack_ports_8bit (void *packet_payload, jack_nframes_t net_peri int8_t *packet_bufX = (int8_t *)packet_payload; + if( !packet_payload ) + return; + while (node != NULL) { int i; @@ -924,14 +1126,9 @@ render_payload_to_jack_ports_celt (void *packet_payload, jack_nframes_t net_peri while (node != NULL) { - int i; - //uint32_t val; - SRC_DATA src; - jack_port_t *port = (jack_port_t *) node->data; jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); - float *floatbuf = alloca (sizeof(float) * net_period_down); const char *portname = jack_port_type (port); if (strncmp(portname, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size()) == 0) @@ -939,7 +1136,11 @@ render_payload_to_jack_ports_celt (void *packet_payload, jack_nframes_t net_peri // audio port, decode celt data. CELTDecoder *decoder = src_node->data; - celt_decode_float( decoder, packet_bufX, net_period_down, buf ); + if( !packet_payload ) + celt_decode_float( decoder, NULL, net_period_down, buf ); + else + celt_decode_float( decoder, packet_bufX, net_period_down, buf ); + src_node = jack_slist_next (src_node); } else if (strncmp(portname, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size()) == 0) @@ -948,7 +1149,8 @@ render_payload_to_jack_ports_celt (void *packet_payload, jack_nframes_t net_peri // convert the data buffer to a standard format (uint32_t based) unsigned int buffer_size_uint32 = net_period_down / 2; uint32_t * buffer_uint32 = (uint32_t*) packet_bufX; - decode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); + if( packet_payload ) + decode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); } packet_bufX = (packet_bufX + net_period_down); node = jack_slist_next (node); @@ -963,12 +1165,10 @@ render_jack_ports_to_payload_celt (JSList *playback_ports, JSList *playback_srcs JSList *node = playback_ports; JSList *src_node = playback_srcs; - unsigned char *packet_bufX = (uint16_t *)packet_payload; + unsigned char *packet_bufX = (unsigned char *)packet_payload; while (node != NULL) { - SRC_DATA src; - int i; jack_port_t *port = (jack_port_t *) node->data; jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); const char *portname = jack_port_type (port); diff --git a/drivers/netjack/netjack_packet.h b/drivers/netjack/netjack_packet.h index c9ef00b..e854b1a 100644 --- a/drivers/netjack/netjack_packet.h +++ b/drivers/netjack/netjack_packet.h @@ -90,11 +90,13 @@ struct _packet_cache { int size; cache_packet *packets; + int mtu; }; extern packet_cache *global_packcache; // fragment cache function prototypes +// XXX: Some of these are private. packet_cache *packet_cache_new(int num_packets, int pkt_size, int mtu); void packet_cache_free(packet_cache *pkt_cache); @@ -105,13 +107,18 @@ cache_packet *packet_cache_get_free_packet(packet_cache *pkt_cache); void cache_packet_reset(cache_packet *pack); void cache_packet_set_framecnt(cache_packet *pack, jack_nframes_t framecnt); void cache_packet_add_fragment(cache_packet *pack, char *packet_buf, int rcv_len); -int cache_packet_is_complete(cache_packet *pack); +int cache_packet_is_complete(cache_packet *pack); +void packet_cache_drain_socket( packet_cache *pcache, int sockfd ); +int packet_cache_retreive_packet( packet_cache *pcache, jack_nframes_t framecnt, char *packet_buf, int pkt_size ); +int packet_cache_get_next_available_framecnt( packet_cache *pcache, jack_nframes_t expected_framecnt, jack_nframes_t *framecnt ); +int packet_cache_find_latency( packet_cache *pcache, jack_nframes_t expected_framecnt, jack_nframes_t *framecnt ); // Function Prototypes -int netjack_poll(int sockfd, int timeout); + +int netjack_poll_deadline (int sockfd, jack_time_t deadline); + void netjack_sendto(int sockfd, char *packet_buf, int pkt_size, int flags, struct sockaddr *addr, int addr_size, int mtu); -int netjack_recvfrom(int sockfd, char *packet_buf, int pkt_size, int flags, struct sockaddr *addr, socklen_t *addr_size, int mtu); -int netjack_recv(int sockfd, char *packet_buf, int pkt_size, int flags, int mtu); + int get_sample_size(int bitdepth); void packet_header_hton(jacknet_packet_header *pkthdr); @@ -122,5 +129,14 @@ void render_payload_to_jack_ports(int bitdepth, void *packet_payload, jack_nfram void render_jack_ports_to_payload(int bitdepth, JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up); + +// XXX: This is sort of deprecated: +// This one waits forever. an is not using ppoll +int netjack_poll(int sockfd, int timeout); + +// TODO: these are deprecated. +int netjack_recvfrom(int sockfd, char *packet_buf, int pkt_size, int flags, struct sockaddr *addr, socklen_t *addr_size, int mtu); +int netjack_recv(int sockfd, char *packet_buf, int pkt_size, int flags, int mtu); + #endif diff --git a/tools/Makefile.am b/tools/Makefile.am index 4e9117b..1638ee5 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -131,11 +131,11 @@ jack_netsource_LDFLAGS = -lsamplerate @OS_LDFLAGS@ jack_netsource_LDADD = $(top_builddir)/libjack/libjack.la if HAVE_ALSA -alsa_in_SOURCES = alsa_in.c +alsa_in_SOURCES = alsa_in.c time_smoother.c alsa_in_LDFLAGS = -lasound -lsamplerate @OS_LDFLAGS@ alsa_in_LDADD = $(top_builddir)/libjack/libjack.la -alsa_out_SOURCES = alsa_out.c +alsa_out_SOURCES = alsa_out.c time_smoother.c alsa_out_LDFLAGS = -lasound -lsamplerate @OS_LDFLAGS@ alsa_out_LDADD = $(top_builddir)/libjack/libjack.la endif #HAVE_ALSA diff --git a/tools/alsa_in.c b/tools/alsa_in.c index 918aaa5..852c369 100644 --- a/tools/alsa_in.c +++ b/tools/alsa_in.c @@ -21,6 +21,7 @@ #include "alsa/asoundlib.h" #include +#include "time_smoother.h" typedef signed short ALSASAMPLE; @@ -41,6 +42,8 @@ int jack_sample_rate; double current_resample_factor = 1.0; +time_smoother *smoother; + // ------------------------------------------------------ commandline parameters int sample_rate = 0; /* stream rate */ @@ -169,7 +172,7 @@ static int set_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams, int pe return err; } /* allow the transfer when at least period_size samples can be processed */ - err = snd_pcm_sw_params_set_avail_min(handle, swparams, period ); + err = snd_pcm_sw_params_set_avail_min(handle, swparams, 2*period ); if (err < 0) { printf("Unable to set avail min for capture: %s\n", snd_strerror(err)); return err; @@ -220,6 +223,7 @@ static snd_pcm_t *open_audiofd( char *device_name, int capture, int rate, int ch return handle; } +jack_nframes_t soundcard_frames = 0; /** * The process callback for this JACK application. @@ -231,32 +235,51 @@ int process (jack_nframes_t nframes, void *arg) { float *floatbuf, *resampbuf; int rlen; int err; - snd_pcm_sframes_t delay; + snd_pcm_sframes_t delay, absolute_delay; + jack_nframes_t this_frame_time; + jack_nframes_t this_soundcard_time; int put_back_samples=0; + int dont_adjust_resampling_factor = 0; + double a, b; - + { snd_pcm_delay( alsa_handle, &delay ); + this_frame_time = jack_frame_time(client); + this_soundcard_time = soundcard_frames + delay; + } + + time_smoother_put( smoother, this_frame_time, this_soundcard_time ); + + // subtract jack_frames_since_cycle_start, to compensate for + // cpu jitter. + //absolute_delay = delay; + //delay = delay - jack_frames_since_cycle_start( client ); + - delay = delay; // Do it the hard way. // this is for compensating xruns etc... if( delay > (target_delay+max_diff) ) { ALSASAMPLE *tmp = alloca( (delay-target_delay) * sizeof( ALSASAMPLE ) * num_channels ); snd_pcm_readi( alsa_handle, tmp, delay-target_delay ); + soundcard_frames += (delay-target_delay); output_new_delay = (int) delay; - delay = target_delay; + dont_adjust_resampling_factor = 1; + //delay = target_delay; // XXX: at least set it to that value. - current_resample_factor = (double) jack_sample_rate / (double) sample_rate; + //current_resample_factor = (double) jack_sample_rate / (double) sample_rate; } if( delay < (target_delay-max_diff) ) { snd_pcm_rewind( alsa_handle, target_delay - delay ); + soundcard_frames -= (target_delay-delay); output_new_delay = (int) delay; - delay = target_delay; + dont_adjust_resampling_factor = 1; + //delay = target_delay; // XXX: at least set it to that value. - current_resample_factor = (double) jack_sample_rate / (double) sample_rate; + //current_resample_factor = (double) jack_sample_rate / (double) sample_rate; } + if( 0 ) { /* ok... now we should have target_delay +- max_diff on the alsa side. * * calculate the number of frames, we want to get. @@ -284,17 +307,34 @@ int process (jack_nframes_t nframes, void *arg) { double compute_factor = (double) nframes / frlen; double diff_value = pow(current_resample_factor - compute_factor, 3) / (double) catch_factor; + current_resample_factor -= diff_value; + current_resample_factor = current_resample_factor < 0.25 ? 0.25 : current_resample_factor; - rlen = ceil( ((double)nframes) / current_resample_factor )+2; + } + + time_smoother_get_linear_params( smoother, this_frame_time, this_soundcard_time, jack_get_sample_rate(client)/4, + &a, &b ); + + if( !dont_adjust_resampling_factor ) + current_resample_factor = b - a/(double)nframes/(double)catch_factor; + else + current_resample_factor = b; + + double offset = a; + double diff_value = b; - if( (print_counter--) == 0 ) { - print_counter = 10; - //printf( "res: %f, \tdiff = %f, \toffset = %f \n", (float)current_resample_factor, (float)diff_value, (float) offset ); - output_resampling_factor = (float) current_resample_factor; + + output_resampling_factor = (float) current_resample_factor; + //if( fabs( offset ) > fabs( output_offset ) ) { output_diff = (float) diff_value; output_offset = (float) offset; - } + //} + + if( current_resample_factor < 0.25 ) current_resample_factor = 0.25; + if( current_resample_factor > 4 ) current_resample_factor = 4; + rlen = ceil( ((double)nframes) * current_resample_factor )+2; + assert( rlen > 10 ); /* * now this should do it... @@ -309,13 +349,14 @@ int process (jack_nframes_t nframes, void *arg) { again: err = snd_pcm_readi(alsa_handle, outbuf, rlen); if( err < 0 ) { - //printf( "err = %d\n", err ); + printf( "err = %d\n", err ); if (xrun_recovery(alsa_handle, err) < 0) { //printf("Write error: %s\n", snd_strerror(err)); //exit(EXIT_FAILURE); } goto again; } + soundcard_frames += err; if( err != rlen ) { //printf( "read = %d\n", rlen ); } @@ -365,6 +406,7 @@ again: //printf( "putback = %d\n", put_back_samples ); snd_pcm_rewind( alsa_handle, put_back_samples ); + soundcard_frames -= put_back_samples; return 0; } @@ -522,6 +564,11 @@ int main (int argc, char *argv[]) { if( !max_diff ) max_diff = period_size / 2; + smoother = time_smoother_new( 100 ); + if( !smoother ) { + fprintf (stderr, "no memory\n"); + return 10; + } if ((client = jack_client_new (jack_name)) == 0) { @@ -569,12 +616,13 @@ int main (int argc, char *argv[]) { } while(1) { - sleep(1); + usleep(500000); if( output_new_delay ) { printf( "delay = %d\n", output_new_delay ); output_new_delay = 0; } printf( "res: %f, \tdiff = %f, \toffset = %f \n", output_resampling_factor, output_diff, output_offset ); + output_offset = 0.0; } diff --git a/tools/alsa_out.c b/tools/alsa_out.c index b0203e7..31031d1 100644 --- a/tools/alsa_out.c +++ b/tools/alsa_out.c @@ -21,6 +21,7 @@ #include "alsa/asoundlib.h" #include +#include "time_smoother.h" typedef signed short ALSASAMPLE; @@ -40,6 +41,9 @@ snd_pcm_t *alsa_handle; int jack_sample_rate; double current_resample_factor = 1.0; +int periods_until_stability = 10; + +time_smoother *smoother; // ------------------------------------------------------ commandline parameters @@ -61,6 +65,7 @@ volatile int output_new_delay = 0; volatile float output_offset = 0.0; volatile float output_diff = 0.0; + // Alsa stuff... i dont want to touch this bullshit in the next years.... please... static int xrun_recovery(snd_pcm_t *handle, int err) { @@ -225,6 +230,7 @@ static snd_pcm_t *open_audiofd( char *device_name, int capture, int rate, int ch return handle; } +jack_nframes_t soundcard_frames = 0; /** * The process callback for this JACK application. @@ -237,61 +243,97 @@ int process (jack_nframes_t nframes, void *arg) { int rlen; int err; snd_pcm_sframes_t delay; + jack_nframes_t this_frame_time; + jack_nframes_t this_soundcard_time; + int dont_adjust_resampling_factor = 0; + double a, b; + double offset; + double diff_value; snd_pcm_delay( alsa_handle, &delay ); + this_frame_time = jack_frame_time(client); + this_soundcard_time = soundcard_frames + delay; + + time_smoother_put( smoother, this_frame_time, this_soundcard_time ); // Do it the hard way. // this is for compensating xruns etc... - if( delay > (target_delay+max_diff) ) { snd_pcm_rewind( alsa_handle, delay - target_delay ); + soundcard_frames -= (delay-target_delay); output_new_delay = (int) delay; - snd_pcm_delay( alsa_handle, &delay ); - //delay = target_delay; + dont_adjust_resampling_factor = 1; + //snd_pcm_delay( alsa_handle, &delay ); + delay = target_delay; // XXX: at least set it to that value. - current_resample_factor = (double) sample_rate / (double) jack_sample_rate; + //current_resample_factor = (double) sample_rate / (double) jack_sample_rate; + current_resample_factor = (double) jack_sample_rate / (double) sample_rate; + periods_until_stability = 10; } if( delay < (target_delay-max_diff) ) { ALSASAMPLE *tmp = alloca( (target_delay-delay) * sizeof( ALSASAMPLE ) * num_channels ); memset( tmp, 0, sizeof( ALSASAMPLE ) * num_channels * (target_delay-delay) ); snd_pcm_writei( alsa_handle, tmp, target_delay-delay ); + soundcard_frames += (target_delay-delay); output_new_delay = (int) delay; - snd_pcm_delay( alsa_handle, &delay ); - //delay = target_delay; + dont_adjust_resampling_factor = 1; + //snd_pcm_delay( alsa_handle, &delay ); + delay = target_delay; // XXX: at least set it to that value. - current_resample_factor = (double) sample_rate / (double) jack_sample_rate; + //current_resample_factor = (double) sample_rate / (double) jack_sample_rate; + current_resample_factor = (double) jack_sample_rate / (double) sample_rate; + periods_until_stability = 10; } /* ok... now we should have target_delay +- max_diff on the alsa side. * * calculate the number of frames, we want to get. */ - double resamp_rate = (double)jack_sample_rate / (double)sample_rate; // == nframes / alsa_samples. - double request_samples = nframes / resamp_rate; //== alsa_samples; - //double request_samples = nframes * current_resample_factor; //== alsa_samples; - - double offset = delay - target_delay; + //if( periods_until_stability ) { + if( 1 ) { + double resamp_rate = (double)jack_sample_rate / (double)sample_rate; // == nframes / alsa_samples. + double request_samples = nframes / resamp_rate; //== alsa_samples; + //double request_samples = nframes * current_resample_factor; //== alsa_samples; - //double frlen = request_samples - offset / catch_factor; - double frlen = request_samples - offset; + offset = delay - target_delay; - double compute_factor = frlen / (double) nframes; + //double frlen = request_samples - offset / catch_factor; + double frlen = request_samples - offset; - double diff_value = pow(current_resample_factor - compute_factor, 3) / (double) catch_factor; - current_resample_factor -= diff_value; - current_resample_factor = current_resample_factor < 0.25 ? 0.25 : current_resample_factor; - rlen = ceil( ((double)nframes) * current_resample_factor ) + 2; + double compute_factor = frlen / (double) nframes; + //double compute_factor = (double) nframes / frlen; - if( (print_counter--) == 0 ) { - print_counter = 10; - //printf( "res: %f, \tdiff = %f, \toffset = %f \n", (float)current_resample_factor, (float)diff_value, (float) offset ); - output_resampling_factor = (float) current_resample_factor; - output_diff = (float) diff_value; - output_offset = (float) offset; + diff_value = pow(current_resample_factor - compute_factor, 3) / (double) catch_factor; + current_resample_factor -= diff_value; + periods_until_stability -= 1; } + else + { + time_smoother_get_linear_params( smoother, this_frame_time, this_soundcard_time, jack_get_sample_rate(client)/4, + &a, &b ); + + if( dont_adjust_resampling_factor ) { + current_resample_factor = 1.0/( b - a/(double)nframes/(double)catch_factor ); + //double delay_diff = (double)delay - (double)target_delay; + //current_resample_factor = 1.0/( b + a/(double)nframes - delay_diff/(double)nframes/(double)catch_factor ); + } else + current_resample_factor = 1.0/b; + + offset = delay - target_delay; + diff_value = b; + } + + + output_resampling_factor = (float) current_resample_factor; + output_diff = (float) diff_value; + output_offset = (float) offset; + if( current_resample_factor < 0.25 ) current_resample_factor = 0.25; + if( current_resample_factor > 4 ) current_resample_factor = 4; + rlen = ceil( ((double)nframes) * current_resample_factor )+2; + assert( rlen > 10 ); /* * now this should do it... */ @@ -323,10 +365,8 @@ int process (jack_nframes_t nframes, void *arg) { src.output_frames = rlen; src.end_of_input = 0; - //src.src_ratio = (float) frlen / (float) nframes; src.src_ratio = current_resample_factor; - //src_set_ratio( src_state, src.src_ratio ); src_process( src_state, &src ); for (i=0; i < rlen; i++) { @@ -343,13 +383,15 @@ int process (jack_nframes_t nframes, void *arg) { again: err = snd_pcm_writei(alsa_handle, outbuf, src.output_frames_gen); if( err < 0 ) { - //printf( "err = %d\n", err ); + printf( "err = %d\n", err ); if (xrun_recovery(alsa_handle, err) < 0) { //printf("Write error: %s\n", snd_strerror(err)); //exit(EXIT_FAILURE); } goto again; } + soundcard_frames += err; + // if( err != rlen ) { // printf( "write = %d\n", rlen ); // } @@ -513,6 +555,11 @@ int main (int argc, char *argv[]) { if( !max_diff ) max_diff = period_size / 2; + smoother = time_smoother_new( 100 ); + if( !smoother ) { + fprintf (stderr, "no memory\n"); + return 10; + } if ((client = jack_client_new (jack_name)) == 0) { @@ -560,7 +607,7 @@ int main (int argc, char *argv[]) { } while(1) { - sleep(1); + usleep(500000); if( output_new_delay ) { printf( "delay = %d\n", output_new_delay ); output_new_delay = 0; diff --git a/tools/netsource.c b/tools/netsource.c index a53312c..a431e8d 100644 --- a/tools/netsource.c +++ b/tools/netsource.c @@ -238,24 +238,13 @@ process (jack_nframes_t nframes, void *arg) packet_bufX = packet_buf + sizeof (jacknet_packet_header) / sizeof (uint32_t); - /* ---------- Receive ---------- */ + // New Receive Code: if (reply_port) - size = netjack_recv (insockfd, (char *) packet_buf, rx_bufsize, MSG_DONTWAIT, mtu); + packet_cache_drain_socket(global_packcache, insockfd); else - size = netjack_recv (outsockfd, (char *) packet_buf, rx_bufsize, MSG_DONTWAIT, mtu); - packet_header_ntoh (pkthdr); - /* Loop till we get the right packet at the right momment */ - while (size == rx_bufsize && (framecnt - pkthdr->framecnt) > latency) - { - //printf ("Frame %d \tLate packet received with a latency of %d frames (expected frame %d, got frame %d)\n", framecnt, framecnt - pkthdr->framecnt, framecnt - latency, pkthdr->framecnt); - //printf ("Frame %d \tLate packet received with a latency of %d frames\n", framecnt, framecnt - pkthdr->framecnt); + packet_cache_drain_socket(global_packcache, outsockfd); - if (reply_port) - size = netjack_recv (insockfd, (char *) packet_buf, rx_bufsize, MSG_DONTWAIT, mtu); - else - size = netjack_recv (outsockfd, (char *) packet_buf, rx_bufsize, MSG_DONTWAIT, mtu); - packet_header_ntoh (pkthdr); - } + size = packet_cache_retreive_packet( global_packcache, framecnt - latency, (char *)packet_buf, rx_bufsize ); /* First alternative : we received what we expected. Render the data * to the JACK ports so it can be played. */ @@ -273,7 +262,7 @@ process (jack_nframes_t nframes, void *arg) // printf ("Frame %d \tSync has been set\n", framecnt); state_currentframe = framecnt; - state_latency = framecnt - pkthdr->framecnt; + //state_latency = framecnt - pkthdr->framecnt; state_connected = 1; sync_state = pkthdr->sync_state; } @@ -282,9 +271,13 @@ process (jack_nframes_t nframes, void *arg) * to the ouput ports */ else { + jack_nframes_t latency_estimate; + if( packet_cache_find_latency( global_packcache, framecnt, &latency_estimate ) ) + state_latency = latency_estimate; + // Set the counters up. state_currentframe = framecnt; - state_latency = framecnt - pkthdr->framecnt; + //state_latency = framecnt - pkthdr->framecnt; state_netxruns += 1; //printf ("Frame %d \tPacket missed or incomplete (expected: %d bytes, got: %d bytes)\n", framecnt, rx_bufsize, size); @@ -329,11 +322,11 @@ process (jack_nframes_t nframes, void *arg) pkthdr->mtu = mtu; packet_header_hton (pkthdr); - if (cont_miss < 10) + if (cont_miss < 2*latency+5) netjack_sendto (outsockfd, (char *) packet_buf, tx_bufsize, 0, &destaddr, sizeof (destaddr), mtu); // else if (cont_miss >= 10 && cont_miss <= 50) // printf ("Frame %d \tToo many packets missed (%d). We have stopped sending data\n", framecnt, cont_miss); - else if (cont_miss > 50) + else if (cont_miss > 50+5*latency) { state_connected = 0; //printf ("Frame %d \tRealy too many packets missed (%d). Let's reset the counter\n", framecnt, cont_miss);