Fixing race condition on seek, correcting some issues with frame timing

This commit is contained in:
Brad Davis 2015-11-17 13:33:10 -08:00
parent 900f425f35
commit 48b0465e56
3 changed files with 59 additions and 23 deletions

View file

@ -47,19 +47,19 @@ RecordingScriptingInterface::RecordingScriptingInterface() {
connect(audioClient.data(), &AudioClient::inputReceived, this, &RecordingScriptingInterface::processAudioInput); connect(audioClient.data(), &AudioClient::inputReceived, this, &RecordingScriptingInterface::processAudioInput);
} }
bool RecordingScriptingInterface::isPlaying() { bool RecordingScriptingInterface::isPlaying() const {
return _player->isPlaying(); return _player->isPlaying();
} }
bool RecordingScriptingInterface::isPaused() { bool RecordingScriptingInterface::isPaused() const {
return _player->isPaused(); return _player->isPaused();
} }
float RecordingScriptingInterface::playerElapsed() { float RecordingScriptingInterface::playerElapsed() const {
return _player->position(); return _player->position();
} }
float RecordingScriptingInterface::playerLength() { float RecordingScriptingInterface::playerLength() const {
return _player->length(); return _player->length();
} }
@ -103,6 +103,10 @@ void RecordingScriptingInterface::setPlayerAudioOffset(float audioOffset) {
} }
void RecordingScriptingInterface::setPlayerTime(float time) { void RecordingScriptingInterface::setPlayerTime(float time) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setPlayerTime", Qt::BlockingQueuedConnection, Q_ARG(float, time));
return;
}
_player->seek(time); _player->seek(time);
} }
@ -130,23 +134,27 @@ void RecordingScriptingInterface::setPlayerUseSkeletonModel(bool useSkeletonMode
_useSkeletonModel = useSkeletonModel; _useSkeletonModel = useSkeletonModel;
} }
void RecordingScriptingInterface::play() {
_player->play();
}
void RecordingScriptingInterface::pausePlayer() { void RecordingScriptingInterface::pausePlayer() {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "pausePlayer", Qt::BlockingQueuedConnection);
return;
}
_player->pause(); _player->pause();
} }
void RecordingScriptingInterface::stopPlaying() { void RecordingScriptingInterface::stopPlaying() {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "stopPlaying", Qt::BlockingQueuedConnection);
return;
}
_player->stop(); _player->stop();
} }
bool RecordingScriptingInterface::isRecording() { bool RecordingScriptingInterface::isRecording() const {
return _recorder->isRecording(); return _recorder->isRecording();
} }
float RecordingScriptingInterface::recorderElapsed() { float RecordingScriptingInterface::recorderElapsed() const {
return _recorder->position(); return _recorder->position();
} }

View file

@ -25,12 +25,17 @@ public:
RecordingScriptingInterface(); RecordingScriptingInterface();
public slots: public slots:
bool isPlaying();
bool isPaused();
float playerElapsed();
float playerLength();
void loadRecording(const QString& filename); void loadRecording(const QString& filename);
void startPlaying(); void startPlaying();
void pausePlayer();
void stopPlaying();
bool isPlaying() const;
bool isPaused() const;
float playerElapsed() const;
float playerLength() const;
void setPlayerVolume(float volume); void setPlayerVolume(float volume);
void setPlayerAudioOffset(float audioOffset); void setPlayerAudioOffset(float audioOffset);
void setPlayerTime(float time); void setPlayerTime(float time);
@ -40,13 +45,13 @@ public slots:
void setPlayerUseAttachments(bool useAttachments); void setPlayerUseAttachments(bool useAttachments);
void setPlayerUseHeadModel(bool useHeadModel); void setPlayerUseHeadModel(bool useHeadModel);
void setPlayerUseSkeletonModel(bool useSkeletonModel); void setPlayerUseSkeletonModel(bool useSkeletonModel);
void play();
void pausePlayer();
void stopPlaying();
bool isRecording();
float recorderElapsed();
void startRecording(); void startRecording();
void stopRecording(); void stopRecording();
bool isRecording() const;
float recorderElapsed() const;
void saveRecording(const QString& filename); void saveRecording(const QString& filename);
void loadLastRecording(); void loadLastRecording();

View file

@ -8,6 +8,8 @@
#include "Deck.h" #include "Deck.h"
#include <QtCore/QThread>
#include <NumericalConstants.h> #include <NumericalConstants.h>
#include <SharedUtil.h> #include <SharedUtil.h>
@ -101,9 +103,13 @@ float Deck::position() const {
} }
static const Frame::Time MIN_FRAME_WAIT_INTERVAL = Frame::secondsToFrameTime(0.001f); static const Frame::Time MIN_FRAME_WAIT_INTERVAL = Frame::secondsToFrameTime(0.001f);
static const Frame::Time MAX_FRAME_PROCESSING_TIME = Frame::secondsToFrameTime(0.002f); static const Frame::Time MAX_FRAME_PROCESSING_TIME = Frame::secondsToFrameTime(0.004f);
void Deck::processFrames() { void Deck::processFrames() {
if (qApp->thread() != QThread::currentThread()) {
qWarning() << "Processing frames must only happen on the main thread.";
return;
}
Locker lock(_mutex); Locker lock(_mutex);
if (_pause) { if (_pause) {
return; return;
@ -115,10 +121,17 @@ void Deck::processFrames() {
// FIXME add code to start dropping frames if we fall behind. // FIXME add code to start dropping frames if we fall behind.
// Alternatively, add code to cache frames here and then process only the last frame of a given type // Alternatively, add code to cache frames here and then process only the last frame of a given type
// ... the latter will work for Avatar, but not well for audio I suspect. // ... the latter will work for Avatar, but not well for audio I suspect.
bool overLimit = false;
for (nextClip = getNextClip(); nextClip; nextClip = getNextClip()) { for (nextClip = getNextClip(); nextClip; nextClip = getNextClip()) {
auto currentPosition = Frame::frameTimeFromEpoch(_startEpoch); auto currentPosition = Frame::frameTimeFromEpoch(_startEpoch);
if ((currentPosition - startingPosition) >= MAX_FRAME_PROCESSING_TIME) { if ((currentPosition - startingPosition) >= MAX_FRAME_PROCESSING_TIME) {
qCWarning(recordingLog) << "Exceeded maximum frame processing time, breaking early"; qCWarning(recordingLog) << "Exceeded maximum frame processing time, breaking early";
#ifdef WANT_RECORDING_DEBUG
qCDebug(recordingLog) << "Starting: " << currentPosition;
qCDebug(recordingLog) << "Current: " << startingPosition;
qCDebug(recordingLog) << "Trigger: " << triggerPosition;
#endif
overLimit = true;
break; break;
} }
@ -150,9 +163,19 @@ void Deck::processFrames() {
// If we have more clip frames available, set the timer for the next one // If we have more clip frames available, set the timer for the next one
_position = Frame::frameTimeFromEpoch(_startEpoch); _position = Frame::frameTimeFromEpoch(_startEpoch);
auto nextFrameTime = nextClip->positionFrameTime(); int nextInterval = 1;
auto interval = Frame::frameTimeToMilliseconds(nextFrameTime - _position); if (!overLimit) {
_timer.singleShot(interval, [this] { auto nextFrameTime = nextClip->positionFrameTime();
nextInterval = (int)Frame::frameTimeToMilliseconds(nextFrameTime - _position);
#ifdef WANT_RECORDING_DEBUG
qCDebug(recordingLog) << "Now " << _position;
qCDebug(recordingLog) << "Next frame time " << nextInterval;
#endif
}
#ifdef WANT_RECORDING_DEBUG
qCDebug(recordingLog) << "Setting timer for next processing " << nextInterval;
#endif
_timer.singleShot(nextInterval, [this] {
processFrames(); processFrames();
}); });
} }