|
From: rainbyte <rai...@us...> - 2012-07-17 10:47:39
|
Author: Lasse Karkkainen <tro...@tr...>
Date: Mon Feb 13 04:59:00 2012 +0200
Fix various AudioQueue bugs causing playback not to start or the previous song to keep playing. Prebuffering made faster.
- Race conditions due to improper locking when m_posReq was handled.
- Buffer fill level conditions may have behaved incorrectly in some cases, tests rewritten.
- Buffer size halved (allowed by bugfixes) and prebuffer reduced to 1/16th of capacity.
- Precise song duration set when decoding reaches EOF.
---
game/ffmpeg.cc | 1 +
game/ffmpeg.hh | 37 ++++++++++++++++++++++---------------
2 files changed, 23 insertions(+), 15 deletions(-)
diff --git a/game/ffmpeg.cc b/game/ffmpeg.cc
index 4756997..87efcfa 100644
--- a/game/ffmpeg.cc
+++ b/game/ffmpeg.cc
@@ -110,6 +110,7 @@ void FFmpeg::operator()() {
}
m_running = false;
m_eof = true;
+ audioQueue.setEof();
videoQueue.push(new VideoFrame()); // EOF marker
}
diff --git a/game/ffmpeg.hh b/game/ffmpeg.hh
index 917b89b..6cbdd13 100644
--- a/game/ffmpeg.hh
+++ b/game/ffmpeg.hh
@@ -7,6 +7,7 @@
#include <boost/scoped_ptr.hpp>
#include <boost/thread/condition.hpp>
#include <boost/thread/mutex.hpp>
+#include <boost/thread/recursive_mutex.hpp>
#include <boost/thread/thread.hpp>
#include <vector>
@@ -106,18 +107,19 @@ class VideoFifo {
};
class AudioBuffer {
+ typedef boost::recursive_mutex mutex;
public:
- AudioBuffer(size_t size = 2000000): m_data(size), m_pos(), m_posReq(), m_sps(), m_duration(getNaN()), m_quit() {}
+ AudioBuffer(size_t size = 1000000): m_data(size), m_pos(), m_posReq(), m_sps(), m_duration(getNaN()), m_quit() {}
/// Reset from FFMPEG side (seeking to beginning or terminate stream)
void reset() {
- boost::mutex::scoped_lock l(m_mutex);
+ mutex::scoped_lock l(m_mutex);
m_data.clear();
m_pos = 0;
l.unlock();
m_cond.notify_one();
}
void quit() {
- boost::mutex::scoped_lock l(m_mutex);
+ mutex::scoped_lock l(m_mutex);
m_quit = true;
l.unlock();
m_cond.notify_one();
@@ -127,7 +129,7 @@ class AudioBuffer {
/// get samples per second
unsigned getSamplesPerSecond() const { return m_sps; }
void push(std::vector<int16_t> const& data, double timestamp) {
- boost::mutex::scoped_lock l(m_mutex);
+ mutex::scoped_lock l(m_mutex);
while (!condition()) m_cond.wait(l);
if (m_quit) return;
if (m_pos == 0 && timestamp != 0.0) {
@@ -138,29 +140,28 @@ class AudioBuffer {
m_pos += data.size();
}
bool prepare(int64_t pos) {
- boost::mutex::scoped_try_lock l(m_mutex);
- if (!l.owns_lock()) return false;
+ mutex::scoped_try_lock l(m_mutex);
+ if (!l.owns_lock()) return false; // Didn't get lock, give up for now
if (eof(pos)) return true;
if (pos < 0) pos = 0;
m_posReq = pos;
+ wakeups();
// Has enough been prebuffered already and is the requested position still within buffer
- bool test = m_pos > m_posReq + m_data.capacity() / 16 && m_pos <= m_posReq + m_data.size();
- return test;
+ return m_pos > m_posReq + m_data.capacity() / 16 && m_pos <= m_posReq + m_data.size();
}
bool operator()(float* begin, float* end, int64_t pos, float volume = 1.0f) {
- boost::mutex::scoped_lock l(m_mutex);
+ mutex::scoped_lock l(m_mutex);
size_t idx = pos + m_data.size() - m_pos;
size_t samples = end - begin;
for (size_t s = 0; s < samples; ++s, ++idx) {
if (idx < m_data.size()) begin[s] += volume * da::conv_from_s16(m_data[idx]);
}
- m_posReq = pos + samples;
- l.unlock();
- if (wantSeek()) reset();
- if (condition()) m_cond.notify_one();
+ m_posReq = std::max<int64_t>(0, pos + samples);
+ wakeups();
return !eof(pos);
}
bool eof(int64_t pos) const { return double(pos) / m_sps >= m_duration; }
+ void setEof() { m_duration = double(m_pos) / m_sps; }
double duration() const { return m_duration; }
void setDuration(double seconds) { m_duration = seconds; }
bool wantSeek() {
@@ -168,9 +169,15 @@ class AudioBuffer {
return m_posReq > 0 && m_posReq + m_sps * 2 /* seconds tolerance */ + m_data.size() < m_pos;
}
private:
- bool wantMore() { return int64_t(m_pos) - int64_t(m_data.capacity() / 2) < m_posReq; }
+ /// Handle waking up of input thread etc. whenever m_posReq is changed.
+ void wakeups() {
+ if (wantSeek()) reset();
+ else if (condition()) m_cond.notify_one();
+ }
+ bool wantMore() { return m_pos < m_posReq + m_data.capacity() / 2; }
+ /// Should the input stop waiting?
bool condition() { return m_quit || wantMore() || wantSeek(); }
- mutable boost::mutex m_mutex;
+ mutable mutex m_mutex;
boost::condition m_cond;
boost::circular_buffer<int16_t> m_data;
size_t m_pos;
|