mirror of
https://github.com/badaix/snapcast.git
synced 2025-08-03 16:48:52 +02:00
Improve oboe latency estimation
This commit is contained in:
parent
5b9fc3d68d
commit
08de66fe70
2 changed files with 54 additions and 14 deletions
|
@ -26,10 +26,12 @@
|
|||
|
||||
using namespace std;
|
||||
|
||||
static constexpr auto LOG_TAG = "OboePlayer";
|
||||
static constexpr double kDefaultLatency = 50;
|
||||
|
||||
OboePlayer::OboePlayer(const PcmDevice& pcmDevice, std::shared_ptr<Stream> stream) : Player(pcmDevice, stream), ms_(50), buff_size(0), pubStream_(stream)
|
||||
OboePlayer::OboePlayer(const PcmDevice& pcmDevice, std::shared_ptr<Stream> stream) : Player(pcmDevice, stream)
|
||||
{
|
||||
|
||||
LOG(DEBUG, LOG_TAG) << "Contructor\n";
|
||||
oboe::AudioStreamBuilder builder;
|
||||
// The builder set methods can be chained for convenience.
|
||||
builder.setSharingMode(oboe::SharingMode::Exclusive)
|
||||
|
@ -38,22 +40,62 @@ OboePlayer::OboePlayer(const PcmDevice& pcmDevice, std::shared_ptr<Stream> strea
|
|||
->setSampleRate(stream->getFormat().rate)
|
||||
->setFormat(oboe::AudioFormat::I16)
|
||||
->setCallback(this)
|
||||
//->setFramesPerCallback((20 * stream->getFormat().rate) / 1000)
|
||||
->openManagedStream(out_stream_);
|
||||
}
|
||||
|
||||
|
||||
OboePlayer::~OboePlayer()
|
||||
{
|
||||
LOG(DEBUG, LOG_TAG) << "Destructor\n";
|
||||
stop();
|
||||
out_stream_->stop(std::chrono::nanoseconds(100ms).count());
|
||||
}
|
||||
|
||||
|
||||
oboe::DataCallbackResult OboePlayer::onAudioReady(oboe::AudioStream* oboeStream, void* audioData, int32_t numFrames)
|
||||
double OboePlayer::getCurrentOutputLatencyMillis() const
|
||||
{
|
||||
|
||||
int32_t buffer_fill_size = oboeStream->getBufferCapacityInFrames() - numFrames;
|
||||
double delay_ms = static_cast<double>(buffer_fill_size) / stream_->getFormat().msRate();
|
||||
chronos::usec delay(static_cast<int>(delay_ms * 1000.));
|
||||
//LOG(INFO) << "onAudioReady frames: " << numFrames << ", capacity: " << oboeStream->getBufferCapacityInFrames() << ", size: " << oboeStream->getBufferSizeInFrames() << ", delay ms: " << delay_ms << "\n";
|
||||
// if (!mIsLatencyDetectionSupported)
|
||||
// return -1;
|
||||
// Get the time that a known audio frame was presented for playing
|
||||
auto result = out_stream_->getTimestamp(CLOCK_MONOTONIC);
|
||||
double outputLatencyMillis = kDefaultLatency;
|
||||
const int64_t kNanosPerMillisecond = 1000000;
|
||||
if (result == oboe::Result::OK)
|
||||
{
|
||||
oboe::FrameTimestamp playedFrame = result.value();
|
||||
// Get the write index for the next audio frame
|
||||
int64_t writeIndex = out_stream_->getFramesWritten();
|
||||
// Calculate the number of frames between our known frame and the write index
|
||||
int64_t frameIndexDelta = writeIndex - playedFrame.position;
|
||||
// Calculate the time which the next frame will be presented
|
||||
int64_t frameTimeDelta = (frameIndexDelta * oboe::kNanosPerSecond) / (out_stream_->getSampleRate());
|
||||
int64_t nextFramePresentationTime = playedFrame.timestamp + frameTimeDelta;
|
||||
// Assume that the next frame will be written at the current time
|
||||
using namespace std::chrono;
|
||||
int64_t nextFrameWriteTime = duration_cast<nanoseconds>(steady_clock::now().time_since_epoch()).count();
|
||||
// Calculate the latency
|
||||
outputLatencyMillis = static_cast<double>(nextFramePresentationTime - nextFrameWriteTime) / kNanosPerMillisecond;
|
||||
}
|
||||
else
|
||||
{
|
||||
// LOG(ERROR, LOG_TAG) << "Error calculating latency: " << oboe::convertToText(result.error()) << "\n";
|
||||
}
|
||||
return outputLatencyMillis;
|
||||
}
|
||||
|
||||
|
||||
oboe::DataCallbackResult OboePlayer::onAudioReady(oboe::AudioStream* /*oboeStream*/, void* audioData, int32_t numFrames)
|
||||
{
|
||||
|
||||
// int32_t buffer_fill_size = oboeStream->getBufferCapacityInFrames() - numFrames;
|
||||
// double delay_ms = static_cast<double>(oboeStream->getBufferCapacityInFrames()) / stream_->getFormat().msRate();
|
||||
|
||||
// LOG(INFO, LOG_TAG) << "onAudioReady frames: " << numFrames << ", capacity: " << oboeStream->getBufferCapacityInFrames() << ", size: " <<
|
||||
// oboeStream->getBufferSizeInFrames() << ", available: " << oboeStream->getAvailableFrames() << ", delay ms: " << delay_ms << "\n";
|
||||
double output_latency = getCurrentOutputLatencyMillis();
|
||||
// LOG(INFO, LOG_TAG) << "getCurrentOutputLatencyMillis: " << output_latency << "\n";
|
||||
chronos::usec delay(static_cast<int>(output_latency * 1000.));
|
||||
|
||||
if (!stream_->getPlayerChunk(audioData, delay, numFrames))
|
||||
{
|
||||
|
@ -72,12 +114,14 @@ oboe::DataCallbackResult OboePlayer::onAudioReady(oboe::AudioStream* oboeStream,
|
|||
void OboePlayer::start()
|
||||
{
|
||||
// Typically, start the stream after querying some stream information, as well as some input from the user
|
||||
LOG(INFO, LOG_TAG) << "Start\n";
|
||||
out_stream_->requestStart();
|
||||
}
|
||||
|
||||
|
||||
void OboePlayer::stop()
|
||||
{
|
||||
LOG(INFO, LOG_TAG) << "Stop\n";
|
||||
out_stream_->requestStop();
|
||||
}
|
||||
|
||||
|
|
|
@ -40,16 +40,12 @@ public:
|
|||
void stop() override;
|
||||
|
||||
protected:
|
||||
oboe::DataCallbackResult onAudioReady(oboe::AudioStream *oboeStream, void *audioData, int32_t numFrames) override;
|
||||
oboe::DataCallbackResult onAudioReady(oboe::AudioStream* oboeStream, void* audioData, int32_t numFrames) override;
|
||||
double getCurrentOutputLatencyMillis() const;
|
||||
|
||||
void worker() override;
|
||||
|
||||
oboe::ManagedStream out_stream_;
|
||||
|
||||
size_t ms_;
|
||||
size_t frames_;
|
||||
size_t buff_size;
|
||||
std::shared_ptr<Stream> pubStream_;
|
||||
};
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue