mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-03 09:01:54 -05:00 
			
		
		
		
	alsa: fix rate matching in the sequencer
The alsa sequencer rate matching was not actually working correctly. It would compare the previous queue time with the current time and compare that to the quantum. This would include uncorrected errors from jitter and would result in the timeouts being scaled in the wrong direction forever. Instead, calculate an ideal queue time and compare our current queue time against that. We then use the correction to scale the timeout or the next queue time prediction. Also use the predicted time as the base time for the event timestamps. this results in less jitter. Fixes #3657
This commit is contained in:
		
							parent
							
								
									4e6b629ae2
								
							
						
					
					
						commit
						22db59d8a3
					
				
					 2 changed files with 26 additions and 16 deletions
				
			
		| 
						 | 
					@ -703,9 +703,7 @@ static int update_time(struct seq_state *state, uint64_t nsec, bool follower)
 | 
				
			||||||
	const snd_seq_real_time_t* queue_time;
 | 
						const snd_seq_real_time_t* queue_time;
 | 
				
			||||||
	uint64_t queue_real;
 | 
						uint64_t queue_real;
 | 
				
			||||||
	double err, corr;
 | 
						double err, corr;
 | 
				
			||||||
	uint64_t queue_elapsed;
 | 
						uint64_t q1, q2;
 | 
				
			||||||
 | 
					 | 
				
			||||||
	corr = 1.0 - (state->dll.z2 + state->dll.z3);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* take queue time */
 | 
						/* take queue time */
 | 
				
			||||||
	snd_seq_queue_status_alloca(&status);
 | 
						snd_seq_queue_status_alloca(&status);
 | 
				
			||||||
| 
						 | 
					@ -713,33 +711,43 @@ static int update_time(struct seq_state *state, uint64_t nsec, bool follower)
 | 
				
			||||||
	queue_time = snd_seq_queue_status_get_real_time(status);
 | 
						queue_time = snd_seq_queue_status_get_real_time(status);
 | 
				
			||||||
	queue_real = SPA_TIMESPEC_TO_NSEC(queue_time);
 | 
						queue_real = SPA_TIMESPEC_TO_NSEC(queue_time);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (state->queue_time == 0)
 | 
					 | 
				
			||||||
		queue_elapsed = 0;
 | 
					 | 
				
			||||||
	else
 | 
					 | 
				
			||||||
		queue_elapsed = (queue_real - state->queue_time) / corr;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	state->queue_time = queue_real;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	queue_elapsed = NSEC_TO_CLOCK(&state->rate, queue_elapsed);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	err = ((int64_t)state->threshold - (int64_t) queue_elapsed);
 | 
					 | 
				
			||||||
	err = SPA_CLAMP(err, -64, 64);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (state->dll.bw == 0.0) {
 | 
						if (state->dll.bw == 0.0) {
 | 
				
			||||||
		spa_dll_set_bw(&state->dll, SPA_DLL_BW_MAX, state->threshold,
 | 
							spa_dll_set_bw(&state->dll, SPA_DLL_BW_MAX, state->threshold,
 | 
				
			||||||
				state->rate.denom);
 | 
									state->rate.denom);
 | 
				
			||||||
		state->next_time = nsec;
 | 
							state->next_time = nsec;
 | 
				
			||||||
		state->base_time = nsec;
 | 
							state->base_time = nsec;
 | 
				
			||||||
 | 
							state->queue_next = queue_real;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* track our estimated elapsed time against the real elapsed queue time */
 | 
				
			||||||
 | 
						q1 = NSEC_TO_CLOCK(&state->rate, state->queue_next);
 | 
				
			||||||
 | 
						q2 = NSEC_TO_CLOCK(&state->rate, queue_real);
 | 
				
			||||||
 | 
						err = ((int64_t)q1 - (int64_t) q2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (fabs(err) > state->threshold)
 | 
				
			||||||
 | 
							spa_dll_init(&state->dll);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = SPA_CLAMP(err, -64, 64);
 | 
				
			||||||
	corr = spa_dll_update(&state->dll, err);
 | 
						corr = spa_dll_update(&state->dll, err);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* this is our current estimated queue time and rate */
 | 
				
			||||||
 | 
						state->queue_time = state->queue_next;
 | 
				
			||||||
 | 
						state->queue_corr = corr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* make a new estimated queue time with the current quantum, if we are following,
 | 
				
			||||||
 | 
						 * use the rate correction, else we will use the rate correction only for the new
 | 
				
			||||||
 | 
						 * timeout. */
 | 
				
			||||||
 | 
						if (state->following)
 | 
				
			||||||
 | 
							state->queue_next += state->threshold * corr * 1e9 / state->rate.denom;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							state->queue_next += state->threshold * 1e9 / state->rate.denom;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if ((state->next_time - state->base_time) > BW_PERIOD) {
 | 
						if ((state->next_time - state->base_time) > BW_PERIOD) {
 | 
				
			||||||
		state->base_time = state->next_time;
 | 
							state->base_time = state->next_time;
 | 
				
			||||||
		spa_log_debug(state->log, "%p: follower:%d rate:%f bw:%f err:%f (%f %f %f)",
 | 
							spa_log_debug(state->log, "%p: follower:%d rate:%f bw:%f err:%f (%f %f %f)",
 | 
				
			||||||
				state, follower, corr, state->dll.bw, err,
 | 
									state, follower, corr, state->dll.bw, err,
 | 
				
			||||||
				state->dll.z1, state->dll.z2, state->dll.z3);
 | 
									state->dll.z1, state->dll.z2, state->dll.z3);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	state->next_time += state->threshold / corr * 1e9 / state->rate.denom;
 | 
						state->next_time += state->threshold / corr * 1e9 / state->rate.denom;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!follower && state->clock) {
 | 
						if (!follower && state->clock) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -142,6 +142,8 @@ struct seq_state {
 | 
				
			||||||
	uint64_t next_time;
 | 
						uint64_t next_time;
 | 
				
			||||||
	uint64_t base_time;
 | 
						uint64_t base_time;
 | 
				
			||||||
	uint64_t queue_time;
 | 
						uint64_t queue_time;
 | 
				
			||||||
 | 
						uint64_t queue_next;
 | 
				
			||||||
 | 
						double queue_corr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	unsigned int opened:1;
 | 
						unsigned int opened:1;
 | 
				
			||||||
	unsigned int started:1;
 | 
						unsigned int started:1;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue