From ba5e652d79a41a15230b0940003e7dc352b41974 Mon Sep 17 00:00:00 2001 From: Adrien Destugues Date: Thu, 3 Nov 2011 14:26:03 +0000 Subject: [PATCH] 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 --- headers/os/media/TimedEventQueue.h | 3 +- headers/private/media/ServerInterface.h | 2 + .../media/media-add-ons/mixer/AudioMixer.cpp | 23 ++++----- .../media/media-add-ons/mixer/AudioMixer.h | 1 + src/kits/media/MediaEventLooper.cpp | 48 ++++++++++--------- src/kits/media/MediaNode.cpp | 2 + src/kits/media/TimedEventQueuePrivate.cpp | 11 +++-- 7 files changed, 52 insertions(+), 38 deletions(-) diff --git a/headers/os/media/TimedEventQueue.h b/headers/os/media/TimedEventQueue.h index 732fd1727b..190a792b1b 100644 --- a/headers/os/media/TimedEventQueue.h +++ b/headers/os/media/TimedEventQueue.h @@ -38,8 +38,9 @@ struct media_timed_event { int32 data; int64 bigdata; 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]; }; diff --git a/headers/private/media/ServerInterface.h b/headers/private/media/ServerInterface.h index 07913e1e7e..2c28980826 100644 --- a/headers/private/media/ServerInterface.h +++ b/headers/private/media/ServerInterface.h @@ -37,6 +37,8 @@ enum { // Raw port based communication enum { + GENERAL_PURPOSE_WAKEUP = 0, // when no action but wait termination needed + ADD_ON_SERVER_RESCAN_ADD_ON_FLAVORS = 0x50, ADD_ON_SERVER_RESCAN_FINISHED_NOTIFY, ADD_ON_SERVER_INSTANTIATE_DORMANT_NODE, diff --git a/src/add-ons/media/media-add-ons/mixer/AudioMixer.cpp b/src/add-ons/media/media-add-ons/mixer/AudioMixer.cpp index b41b95dc88..a4f1882e80 100644 --- a/src/add-ons/media/media-add-ons/mixer/AudioMixer.cpp +++ b/src/add-ons/media/media-add-ons/mixer/AudioMixer.cpp @@ -1,7 +1,7 @@ /* * Copyright 2002 David Shipman, * 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. */ @@ -113,7 +113,8 @@ AudioMixer::AudioMixer(BMediaAddOn *addOn, bool isSystemMixer) fDownstreamLatency(1), fInternalLatency(1), fDisableStop(false), - fLastLateNotification(0) + fLastLateNotification(0), + fLastLateness(0) { BMediaNode::AddNodeKind(B_SYSTEM_MIXER); @@ -319,11 +320,11 @@ AudioMixer::BufferReceived(BBuffer *buffer) void AudioMixer::HandleInputBuffer(BBuffer* buffer, bigtime_t lateness) { - if (lateness > kMaxJitter) { - debug_printf("Received buffer %Ld usec late\n", lateness); + if (lateness > kMaxJitter && lateness > fLastLateness) { + debug_printf("AudioMixer: Dequeued input buffer %Ld usec late\n", lateness); if (RunMode() == B_DROP_DATA || RunMode() == B_DECREASE_PRECISION || RunMode() == B_INCREASE_LATENCY) { - debug_printf("sending notify\n"); + debug_printf("AudioMixer: sending notify\n"); // Build a media_source out of the header data media_source source = media_source::null; @@ -333,13 +334,12 @@ AudioMixer::HandleInputBuffer(BBuffer* buffer, bigtime_t lateness) NotifyLateProducer(source, lateness, TimeSource()->Now()); if (RunMode() == B_DROP_DATA) { - debug_printf("dropping buffer\n"); + debug_printf("AudioMixer: dropping buffer\n"); return; } } } - - // printf("Received buffer with lateness %Ld\n", lateness); + fLastLateness = lateness; fCore->Lock(); fCore->BufferReceived(buffer, lateness); @@ -1006,18 +1006,19 @@ AudioMixer::LateNoticeReceived(const media_source& what, bigtime_t howMuch, if (what == fCore->Output()->MediaOutput().source && RunMode() == B_INCREASE_LATENCY) { - // We need to ignore subsequent notices whose performance time - // lies before the performance time of the last notification + // We need to ignore subsequent notices whose arrival time here + // lies within the last lateness, because queued-up buffers will all be 'late' if (performanceTime < fLastLateNotification) return; fInternalLatency += howMuch; // 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) fInternalLatency = kMaxLatency; - fLastLateNotification = TimeSource()->Now(); + fLastLateNotification = TimeSource()->Now() + howMuch; debug_printf("AudioMixer: increasing internal latency to %Ld usec\n", fInternalLatency); SetEventLatency(fDownstreamLatency + fInternalLatency); diff --git a/src/add-ons/media/media-add-ons/mixer/AudioMixer.h b/src/add-ons/media/media-add-ons/mixer/AudioMixer.h index 9939cbb342..123593cb39 100644 --- a/src/add-ons/media/media-add-ons/mixer/AudioMixer.h +++ b/src/add-ons/media/media-add-ons/mixer/AudioMixer.h @@ -136,6 +136,7 @@ private: bool fDisableStop; media_format fDefaultFormat; bigtime_t fLastLateNotification; + bigtime_t fLastLateness; }; diff --git a/src/kits/media/MediaEventLooper.cpp b/src/kits/media/MediaEventLooper.cpp index 51591d96af..1c2c71fd33 100644 --- a/src/kits/media/MediaEventLooper.cpp +++ b/src/kits/media/MediaEventLooper.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include "debug.h" /************************************************************* @@ -210,6 +211,7 @@ BMediaEventLooper::ControlLoop() status_t err; bigtime_t latency; bigtime_t waituntil; + bigtime_t lateness; for (;;) { // while there are no events or it is not time for the earliest event, // process messages using WaitForMessages. Whenever this funtion times out, @@ -222,31 +224,37 @@ BMediaEventLooper::ControlLoop() // only the scheduling latency). 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; if (fEventQueue.HasEvents()) { - waituntil = TimeSource()->RealTimeFor(fEventQueue.FirstEventTime(), latency); -// printf("node %02d waiting for %12Ld that will happen at %12Ld\n", ID(), fEventQueue.FirstEventTime(), waituntil); + const media_timed_event *firstEvent = fEventQueue.FirstEvent(); + waituntil = TimeSource()->RealTimeFor(firstEvent->event_time, latency); 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()) { + const media_timed_event *firstEvent = fRealTimeQueue.FirstEvent(); 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) { waituntil = temp; is_realtime = true; - latency = fSchedulingLatency; } } + lateness = 0; // remove any extraneous value if we get this far err = WaitForMessage(waituntil); if (err == B_TIMED_OUT) break; @@ -258,16 +266,9 @@ BMediaEventLooper::ControlLoop() else 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) { - 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); - } + if (err == B_OK) DispatchEvent(&event, lateness, is_realtime); } } @@ -388,6 +389,7 @@ BMediaEventLooper::SetEventLatency(bigtime_t latency) latency = 0; fEventLatency = latency; + write_port_etc(ControlPort(), GENERAL_PURPOSE_WAKEUP, 0, 0, B_TIMEOUT, 0); } diff --git a/src/kits/media/MediaNode.cpp b/src/kits/media/MediaNode.cpp index 2b3e9143a4..beb41e24fb 100644 --- a/src/kits/media/MediaNode.cpp +++ b/src/kits/media/MediaNode.cpp @@ -377,6 +377,8 @@ BMediaNode::WaitForMessage(bigtime_t waitUntil, TRACE("BMediaNode::WaitForMessage request is: %#lx, node %ld, this %p\n", message, fNodeID, this); + if (message == GENERAL_PURPOSE_WAKEUP) return B_OK; // no action needed + if (message > NODE_MESSAGE_START && message < NODE_MESSAGE_END) { TRACE("BMediaNode::WaitForMessage calling BMediaNode\n"); if (B_OK == BMediaNode::HandleMessage(message, data, size)) diff --git a/src/kits/media/TimedEventQueuePrivate.cpp b/src/kits/media/TimedEventQueuePrivate.cpp index af01d6aeaf..ebf61a6baa 100644 --- a/src/kits/media/TimedEventQueuePrivate.cpp +++ b/src/kits/media/TimedEventQueuePrivate.cpp @@ -29,16 +29,19 @@ /* Implements _event_queue_imp used by BTimedEventQueue, not thread save! */ -#include +#include + #include #include #include //defines B_DELETE +#include +#include + #include "TimedEventQueuePrivate.h" + #include "Debug.h" #include "debug.h" -#include - _event_queue_imp::_event_queue_imp() : fLock(new BLocker("BTimedEventQueue locker")), fEventCount(0), @@ -74,6 +77,8 @@ _event_queue_imp::AddEvent(const media_timed_event &event) if (event.type <= 0) { return B_BAD_VALUE; } + + *(bigtime_t *)&event.queued_time = BTimeSource::RealTime(); //create a new queue if (fFirstEntry == NULL) {