Set of patches by Pete Goodeve to imrpove the handling of audio latency.

Closes #7285.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@43164 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Adrien Destugues
2011-11-03 14:26:03 +00:00
parent 530590d3b9
commit ba5e652d79
7 changed files with 52 additions and 38 deletions
+2 -1
View File
@@ -38,8 +38,9 @@ struct media_timed_event {
int32 data; int32 data;
int64 bigdata; int64 bigdata;
char user_data[64]; char user_data[64];
bigtime_t queued_time; // Real time when put in queue
uint32 _reserved_media_timed_event_[8]; uint32 _reserved_media_timed_event_[6];
}; };
+2
View File
@@ -37,6 +37,8 @@ enum {
// Raw port based communication // Raw port based communication
enum { enum {
GENERAL_PURPOSE_WAKEUP = 0, // when no action but wait termination needed
ADD_ON_SERVER_RESCAN_ADD_ON_FLAVORS = 0x50, ADD_ON_SERVER_RESCAN_ADD_ON_FLAVORS = 0x50,
ADD_ON_SERVER_RESCAN_FINISHED_NOTIFY, ADD_ON_SERVER_RESCAN_FINISHED_NOTIFY,
ADD_ON_SERVER_INSTANTIATE_DORMANT_NODE, ADD_ON_SERVER_INSTANTIATE_DORMANT_NODE,
@@ -1,7 +1,7 @@
/* /*
* Copyright 2002 David Shipman, * Copyright 2002 David Shipman,
* Copyright 2003-2007 Marcus Overhagen * Copyright 2003-2007 Marcus Overhagen
* Copyright 2007-2010 Haiku Inc. All rights reserved. * Copyright 2007-2011 Haiku Inc. All rights reserved.
* *
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
*/ */
@@ -113,7 +113,8 @@ AudioMixer::AudioMixer(BMediaAddOn *addOn, bool isSystemMixer)
fDownstreamLatency(1), fDownstreamLatency(1),
fInternalLatency(1), fInternalLatency(1),
fDisableStop(false), fDisableStop(false),
fLastLateNotification(0) fLastLateNotification(0),
fLastLateness(0)
{ {
BMediaNode::AddNodeKind(B_SYSTEM_MIXER); BMediaNode::AddNodeKind(B_SYSTEM_MIXER);
@@ -319,11 +320,11 @@ AudioMixer::BufferReceived(BBuffer *buffer)
void void
AudioMixer::HandleInputBuffer(BBuffer* buffer, bigtime_t lateness) AudioMixer::HandleInputBuffer(BBuffer* buffer, bigtime_t lateness)
{ {
if (lateness > kMaxJitter) { if (lateness > kMaxJitter && lateness > fLastLateness) {
debug_printf("Received buffer %Ld usec late\n", lateness); debug_printf("AudioMixer: Dequeued input buffer %Ld usec late\n", lateness);
if (RunMode() == B_DROP_DATA || RunMode() == B_DECREASE_PRECISION if (RunMode() == B_DROP_DATA || RunMode() == B_DECREASE_PRECISION
|| RunMode() == B_INCREASE_LATENCY) { || RunMode() == B_INCREASE_LATENCY) {
debug_printf("sending notify\n"); debug_printf("AudioMixer: sending notify\n");
// Build a media_source out of the header data // Build a media_source out of the header data
media_source source = media_source::null; media_source source = media_source::null;
@@ -333,13 +334,12 @@ AudioMixer::HandleInputBuffer(BBuffer* buffer, bigtime_t lateness)
NotifyLateProducer(source, lateness, TimeSource()->Now()); NotifyLateProducer(source, lateness, TimeSource()->Now());
if (RunMode() == B_DROP_DATA) { if (RunMode() == B_DROP_DATA) {
debug_printf("dropping buffer\n"); debug_printf("AudioMixer: dropping buffer\n");
return; return;
} }
} }
} }
fLastLateness = lateness;
// printf("Received buffer with lateness %Ld\n", lateness);
fCore->Lock(); fCore->Lock();
fCore->BufferReceived(buffer, lateness); fCore->BufferReceived(buffer, lateness);
@@ -1006,18 +1006,19 @@ AudioMixer::LateNoticeReceived(const media_source& what, bigtime_t howMuch,
if (what == fCore->Output()->MediaOutput().source if (what == fCore->Output()->MediaOutput().source
&& RunMode() == B_INCREASE_LATENCY) { && RunMode() == B_INCREASE_LATENCY) {
// We need to ignore subsequent notices whose performance time // We need to ignore subsequent notices whose arrival time here
// lies before the performance time of the last notification // lies within the last lateness, because queued-up buffers will all be 'late'
if (performanceTime < fLastLateNotification) if (performanceTime < fLastLateNotification)
return; return;
fInternalLatency += howMuch; fInternalLatency += howMuch;
// At some point a too large latency can get annoying // At some point a too large latency can get annoying
// (actually more than annoying, as there won't be enough buffers long before this!)
if (fInternalLatency > kMaxLatency) if (fInternalLatency > kMaxLatency)
fInternalLatency = kMaxLatency; fInternalLatency = kMaxLatency;
fLastLateNotification = TimeSource()->Now(); fLastLateNotification = TimeSource()->Now() + howMuch;
debug_printf("AudioMixer: increasing internal latency to %Ld usec\n", fInternalLatency); debug_printf("AudioMixer: increasing internal latency to %Ld usec\n", fInternalLatency);
SetEventLatency(fDownstreamLatency + fInternalLatency); SetEventLatency(fDownstreamLatency + fInternalLatency);
@@ -136,6 +136,7 @@ private:
bool fDisableStop; bool fDisableStop;
media_format fDefaultFormat; media_format fDefaultFormat;
bigtime_t fLastLateNotification; bigtime_t fLastLateNotification;
bigtime_t fLastLateness;
}; };
+25 -23
View File
@@ -31,6 +31,7 @@
#include <TimeSource.h> #include <TimeSource.h>
#include <scheduler.h> #include <scheduler.h>
#include <Buffer.h> #include <Buffer.h>
#include <ServerInterface.h>
#include "debug.h" #include "debug.h"
/************************************************************* /*************************************************************
@@ -210,6 +211,7 @@ BMediaEventLooper::ControlLoop()
status_t err; status_t err;
bigtime_t latency; bigtime_t latency;
bigtime_t waituntil; bigtime_t waituntil;
bigtime_t lateness;
for (;;) { for (;;) {
// while there are no events or it is not time for the earliest event, // while there are no events or it is not time for the earliest event,
// process messages using WaitForMessages. Whenever this funtion times out, // process messages using WaitForMessages. Whenever this funtion times out,
@@ -222,31 +224,37 @@ BMediaEventLooper::ControlLoop()
// only the scheduling latency). // only the scheduling latency).
latency = fEventLatency + fSchedulingLatency; latency = fEventLatency + fSchedulingLatency;
if (fEventQueue.HasEvents() && (TimeSource()->Now() - latency) >= fEventQueue.FirstEventTime()) {
// printf("node %02d waiting for %12Ld that has already happened, now %12Ld\n", ID(), fEventQueue.FirstEventTime(), system_time());
is_realtime = false;
break;
}
if (fRealTimeQueue.HasEvents() && (TimeSource()->RealTime() - fSchedulingLatency) >= fRealTimeQueue.FirstEventTime()) {
latency = fSchedulingLatency;
is_realtime = true;
break;
}
waituntil = B_INFINITE_TIMEOUT; waituntil = B_INFINITE_TIMEOUT;
if (fEventQueue.HasEvents()) { if (fEventQueue.HasEvents()) {
waituntil = TimeSource()->RealTimeFor(fEventQueue.FirstEventTime(), latency); const media_timed_event *firstEvent = fEventQueue.FirstEvent();
// printf("node %02d waiting for %12Ld that will happen at %12Ld\n", ID(), fEventQueue.FirstEventTime(), waituntil); waituntil = TimeSource()->RealTimeFor(firstEvent->event_time, latency);
is_realtime = false; is_realtime = false;
lateness = firstEvent->queued_time - waituntil;
if (lateness > 0) {
// if (lateness > 1000)
// printf("node %02ld handling %12Ld at %12Ld -- %Ld late, queued at %Ld now %12Ld \n",
// ID(), fEventQueue.FirstEventTime(), TimeSource()->Now(), lateness,
// firstEvent->queued_time, TimeSource()->RealTime());
is_realtime = false;
break;
}
// printf("node %02ld waiting for %12Ld that will happen at %12Ld\n", ID(), fEventQueue.FirstEventTime(), waituntil);
} }
if (fRealTimeQueue.HasEvents()) { if (fRealTimeQueue.HasEvents()) {
const media_timed_event *firstEvent = fRealTimeQueue.FirstEvent();
bigtime_t temp; bigtime_t temp;
temp = fRealTimeQueue.FirstEventTime() - fSchedulingLatency; temp = firstEvent->event_time - fSchedulingLatency;
lateness = firstEvent->queued_time - temp;
if (lateness > 0) {
is_realtime = true;
break;
}
if (temp < waituntil) { if (temp < waituntil) {
waituntil = temp; waituntil = temp;
is_realtime = true; is_realtime = true;
latency = fSchedulingLatency;
} }
} }
lateness = 0; // remove any extraneous value if we get this far
err = WaitForMessage(waituntil); err = WaitForMessage(waituntil);
if (err == B_TIMED_OUT) if (err == B_TIMED_OUT)
break; break;
@@ -258,16 +266,9 @@ BMediaEventLooper::ControlLoop()
else else
err = fEventQueue.RemoveFirstEvent(&event); err = fEventQueue.RemoveFirstEvent(&event);
// printf("node %02d handling %12Ld at %12Ld\n", ID(), event.event_time, system_time()); // printf("node %02ld handling %12Ld at %12Ld\n", ID(), event.event_time, TimeSource()->Now());
if (err == B_OK) { if (err == B_OK) DispatchEvent(&event, lateness, is_realtime);
bigtime_t lateness;
if (is_realtime)
lateness = TimeSource()->RealTime() - event.event_time;
else
lateness = TimeSource()->RealTime() - TimeSource()->RealTimeFor(event.event_time, 0) + fEventLatency;
DispatchEvent(&event, lateness, is_realtime);
}
} }
} }
@@ -388,6 +389,7 @@ BMediaEventLooper::SetEventLatency(bigtime_t latency)
latency = 0; latency = 0;
fEventLatency = latency; fEventLatency = latency;
write_port_etc(ControlPort(), GENERAL_PURPOSE_WAKEUP, 0, 0, B_TIMEOUT, 0);
} }
+2
View File
@@ -377,6 +377,8 @@ BMediaNode::WaitForMessage(bigtime_t waitUntil,
TRACE("BMediaNode::WaitForMessage request is: %#lx, node %ld, this %p\n", TRACE("BMediaNode::WaitForMessage request is: %#lx, node %ld, this %p\n",
message, fNodeID, this); message, fNodeID, this);
if (message == GENERAL_PURPOSE_WAKEUP) return B_OK; // no action needed
if (message > NODE_MESSAGE_START && message < NODE_MESSAGE_END) { if (message > NODE_MESSAGE_START && message < NODE_MESSAGE_END) {
TRACE("BMediaNode::WaitForMessage calling BMediaNode\n"); TRACE("BMediaNode::WaitForMessage calling BMediaNode\n");
if (B_OK == BMediaNode::HandleMessage(message, data, size)) if (B_OK == BMediaNode::HandleMessage(message, data, size))
+8 -3
View File
@@ -29,16 +29,19 @@
/* Implements _event_queue_imp used by BTimedEventQueue, not thread save! /* Implements _event_queue_imp used by BTimedEventQueue, not thread save!
*/ */
#include <TimedEventQueue.h> #include <string.h>
#include <Autolock.h> #include <Autolock.h>
#include <Buffer.h> #include <Buffer.h>
#include <InterfaceDefs.h> //defines B_DELETE #include <InterfaceDefs.h> //defines B_DELETE
#include <TimedEventQueue.h>
#include <TimeSource.h>
#include "TimedEventQueuePrivate.h" #include "TimedEventQueuePrivate.h"
#include "Debug.h" #include "Debug.h"
#include "debug.h" #include "debug.h"
#include <string.h>
_event_queue_imp::_event_queue_imp() : _event_queue_imp::_event_queue_imp() :
fLock(new BLocker("BTimedEventQueue locker")), fLock(new BLocker("BTimedEventQueue locker")),
fEventCount(0), fEventCount(0),
@@ -74,6 +77,8 @@ _event_queue_imp::AddEvent(const media_timed_event &event)
if (event.type <= 0) { if (event.type <= 0) {
return B_BAD_VALUE; return B_BAD_VALUE;
} }
*(bigtime_t *)&event.queued_time = BTimeSource::RealTime();
//create a new queue //create a new queue
if (fFirstEntry == NULL) { if (fFirstEntry == NULL) {