module-rtp-recv: Use new algorithm for adjusting sample rate

This commit is contained in:
Maarten Bosmans 2011-01-12 07:24:58 +01:00
parent 8b4cb54595
commit d053a25b67

View file

@ -109,6 +109,7 @@ struct session {
pa_usec_t sink_latency; pa_usec_t sink_latency;
pa_usec_t last_rate_update; pa_usec_t last_rate_update;
pa_usec_t last_latency;
}; };
struct userdata { struct userdata {
@ -286,11 +287,11 @@ static int rtpoll_work_cb(pa_rtpoll_item *i) {
pa_atomic_store(&s->timestamp, (int) now.tv_sec); pa_atomic_store(&s->timestamp, (int) now.tv_sec);
if (s->last_rate_update + RATE_UPDATE_INTERVAL < pa_timeval_load(&now)) { if (s->last_rate_update + RATE_UPDATE_INTERVAL < pa_timeval_load(&now)) {
pa_usec_t wi, ri, render_delay, sink_delay = 0, latency, fix; pa_usec_t wi, ri, render_delay, sink_delay = 0, latency;
unsigned fix_samples;
uint32_t base_rate = s->sink_input->sink->sample_spec.rate; uint32_t base_rate = s->sink_input->sink->sample_spec.rate;
uint32_t current_rate = s->sink_input->sample_spec.rate; uint32_t current_rate = s->sink_input->sample_spec.rate;
uint32_t new_rate; uint32_t new_rate;
double estimated_rate;
pa_log_debug("Updating sample rate"); pa_log_debug("Updating sample rate");
@ -314,19 +315,31 @@ static int rtpoll_work_cb(pa_rtpoll_item *i) {
pa_log_debug("Write index deviates by %0.2f ms, expected %0.2f ms", (double) latency/PA_USEC_PER_MSEC, (double) s->intended_latency/PA_USEC_PER_MSEC); pa_log_debug("Write index deviates by %0.2f ms, expected %0.2f ms", (double) latency/PA_USEC_PER_MSEC, (double) s->intended_latency/PA_USEC_PER_MSEC);
/* Calculate deviation */ /* The buffer is filling with some unknown rate R̂ samples/second. If the rate of reading in
if (latency < s->intended_latency) * the last T seconds was Rⁿ, then the increase in buffer latency ΔLⁿ = Lⁿ - Lⁿ in that
fix = s->intended_latency - latency; * same period is ΔLⁿ = (TR̂ - TRⁿ) / , giving the estimated target rate
else * T
fix = latency - s->intended_latency; * = Rⁿ . (1)
* T - (Lⁿ - Lⁿ)
/* How many samples is this per second? */ *
fix_samples = (unsigned) (fix * (pa_usec_t) s->sink_input->thread_info.sample_spec.rate / (pa_usec_t) RATE_UPDATE_INTERVAL); * Setting the sample rate to results in the latency being constant (if the estimate of
* is correct). But there is also the requirement to keep the buffer at a predefined target
if (latency < s->intended_latency) * latency . So instead of setting Rⁿ to immediately, the strategy will be to reduce R
new_rate = current_rate - fix_samples; * from Rⁿ to in a steps of T seconds, where Rⁿ is chosen such that in the total time
else * aT the latency is reduced from Lⁿ to . This strategy translates to the requirements
new_rate = current_rate + fix_samples; * - Rⁿʲ a-j+1 j-1
* Σ T = - Lⁿ with Rⁿʲ = Rⁿ + .
* ʲ a a
* Solving for Rⁿ gives
* T - ²( - Lⁿ)
* Rⁿ = . (2)
* T
* Together Equations (1) and (2) specify the algorithm used below, where a = 7 is used.
*/
estimated_rate = (double) current_rate * (double) RATE_UPDATE_INTERVAL / (double) (RATE_UPDATE_INTERVAL + s->last_latency - latency);
pa_log_debug("Estimated target rate: %.0f Hz", estimated_rate);
new_rate = (uint32_t) ((double) (RATE_UPDATE_INTERVAL + latency/4 - s->intended_latency/4) / (double) RATE_UPDATE_INTERVAL * estimated_rate);
s->last_latency = latency;
if (new_rate < (uint32_t) (base_rate*0.8) || new_rate > (uint32_t) (base_rate*1.25)) { if (new_rate < (uint32_t) (base_rate*0.8) || new_rate > (uint32_t) (base_rate*1.25)) {
pa_log_warn("Sample rates too different, not adjusting (%u vs. %u).", base_rate, new_rate); pa_log_warn("Sample rates too different, not adjusting (%u vs. %u).", base_rate, new_rate);
@ -488,6 +501,7 @@ static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_in
pa_timeval_load(&now), pa_timeval_load(&now),
TRUE); TRUE);
s->last_rate_update = pa_timeval_load(&now); s->last_rate_update = pa_timeval_load(&now);
s->last_latency = LATENCY_USEC;
pa_atomic_store(&s->timestamp, (int) now.tv_sec); pa_atomic_store(&s->timestamp, (int) now.tv_sec);
if ((fd = mcast_socket((const struct sockaddr*) &sdp_info->sa, sdp_info->salen)) < 0) if ((fd = mcast_socket((const struct sockaddr*) &sdp_info->sa, sdp_info->salen)) < 0)