playbackcache: made thread-safe
Since this class is regularly accessed by multiple threads, it makes the most sense to make all of its functions thread-safe.
This commit is contained in:
@@ -39,6 +39,8 @@ AudioPlaybackCache::AudioPlaybackCache()
|
||||
|
||||
void AudioPlaybackCache::SetParameters(const AudioRenderingParams ¶ms)
|
||||
{
|
||||
QMutexLocker locker(lock());
|
||||
|
||||
if (params_ == params) {
|
||||
return;
|
||||
}
|
||||
@@ -52,13 +54,19 @@ void AudioPlaybackCache::SetParameters(const AudioRenderingParams ¶ms)
|
||||
}
|
||||
|
||||
// Our current audio cache is unusable, so we truncate it automatically
|
||||
InvalidateAll();
|
||||
TimeRange invalidate_range(0, NoLockGetLength());
|
||||
NoLockInvalidate(invalidate_range);
|
||||
|
||||
locker.unlock();
|
||||
|
||||
emit ParametersChanged();
|
||||
emit Invalidated(invalidate_range);
|
||||
}
|
||||
|
||||
void AudioPlaybackCache::WritePCM(const TimeRange &range, SampleBufferPtr samples)
|
||||
{
|
||||
QMutexLocker locker(lock());
|
||||
|
||||
QFile f(filename_);
|
||||
if (f.open(QFile::ReadWrite)) {
|
||||
qint64 start_offset = params_.time_to_bytes(range.in());
|
||||
@@ -82,7 +90,11 @@ void AudioPlaybackCache::WritePCM(const TimeRange &range, SampleBufferPtr sample
|
||||
|
||||
f.close();
|
||||
|
||||
Validate(range);
|
||||
NoLockValidate(range);
|
||||
|
||||
locker.unlock();
|
||||
|
||||
emit Validated(range);
|
||||
} else {
|
||||
qWarning() << "Failed to write PCM data to" << filename_;
|
||||
}
|
||||
@@ -90,6 +102,8 @@ void AudioPlaybackCache::WritePCM(const TimeRange &range, SampleBufferPtr sample
|
||||
|
||||
void AudioPlaybackCache::WriteSilence(const TimeRange &range)
|
||||
{
|
||||
QMutexLocker locker(lock());
|
||||
|
||||
QFile f(filename_);
|
||||
if (f.open(QFile::ReadWrite)) {
|
||||
qint64 start_offset = params_.time_to_bytes(range.in());
|
||||
|
||||
@@ -33,7 +33,8 @@ class AudioPlaybackCache : public PlaybackCache
|
||||
public:
|
||||
AudioPlaybackCache();
|
||||
|
||||
const AudioRenderingParams& GetParameters() const {
|
||||
AudioRenderingParams GetParameters() {
|
||||
QMutexLocker locker(lock());
|
||||
return params_;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,25 +34,39 @@
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
QByteArray FrameHashCache::GetHash(const rational &time) const
|
||||
QByteArray FrameHashCache::GetHash(const rational &time)
|
||||
{
|
||||
QMutexLocker locker(lock());
|
||||
|
||||
return time_hash_map_.value(time);
|
||||
}
|
||||
|
||||
void FrameHashCache::SetHash(const rational &time, const QByteArray &hash)
|
||||
{
|
||||
QMutexLocker locker(lock());
|
||||
|
||||
time_hash_map_.insert(time, hash);
|
||||
|
||||
Validate(TimeRange(time, time + timebase_));
|
||||
TimeRange validated_range(time, time + timebase_);
|
||||
|
||||
NoLockValidate(validated_range);
|
||||
|
||||
locker.unlock();
|
||||
|
||||
emit Validated(validated_range);
|
||||
}
|
||||
|
||||
void FrameHashCache::SetTimebase(const rational &tb)
|
||||
{
|
||||
QMutexLocker locker(lock());
|
||||
|
||||
timebase_ = tb;
|
||||
}
|
||||
|
||||
QList<rational> FrameHashCache::GetFramesWithHash(const QByteArray &hash) const
|
||||
QList<rational> FrameHashCache::GetFramesWithHash(const QByteArray &hash)
|
||||
{
|
||||
QMutexLocker locker(lock());
|
||||
|
||||
QList<rational> times;
|
||||
|
||||
QMap<rational, QByteArray>::const_iterator iterator;
|
||||
@@ -68,6 +82,8 @@ QList<rational> FrameHashCache::GetFramesWithHash(const QByteArray &hash) const
|
||||
|
||||
QList<rational> FrameHashCache::TakeFramesWithHash(const QByteArray &hash)
|
||||
{
|
||||
QMutexLocker locker(lock());
|
||||
|
||||
QList<rational> times;
|
||||
|
||||
QMap<rational, QByteArray>::iterator iterator = time_hash_map_.begin();
|
||||
@@ -82,11 +98,23 @@ QList<rational> FrameHashCache::TakeFramesWithHash(const QByteArray &hash)
|
||||
}
|
||||
}
|
||||
|
||||
foreach (const rational& r, times) {
|
||||
NoLockInvalidate(TimeRange(r, r + timebase_));
|
||||
}
|
||||
|
||||
locker.unlock();
|
||||
|
||||
foreach (const rational& r, times) {
|
||||
emit Invalidated(TimeRange(r, r + timebase_));
|
||||
}
|
||||
|
||||
return times;
|
||||
}
|
||||
|
||||
const QMap<rational, QByteArray> &FrameHashCache::time_hash_map() const
|
||||
QMap<rational, QByteArray> FrameHashCache::time_hash_map()
|
||||
{
|
||||
QMutexLocker locker(lock());
|
||||
|
||||
return time_hash_map_;
|
||||
}
|
||||
|
||||
@@ -130,14 +158,18 @@ QList<rational> FrameHashCache::GetFrameListFromTimeRange(TimeRangeList range_li
|
||||
return times;
|
||||
}
|
||||
|
||||
QList<rational> FrameHashCache::GetFrameListFromTimeRange(const TimeRangeList &range) const
|
||||
QList<rational> FrameHashCache::GetFrameListFromTimeRange(const TimeRangeList &range)
|
||||
{
|
||||
QMutexLocker locker(lock());
|
||||
|
||||
return GetFrameListFromTimeRange(range, timebase_);
|
||||
}
|
||||
|
||||
QList<rational> FrameHashCache::GetInvalidatedFrames() const
|
||||
QList<rational> FrameHashCache::GetInvalidatedFrames()
|
||||
{
|
||||
return GetFrameListFromTimeRange(GetInvalidatedRanges());
|
||||
QMutexLocker locker(lock());
|
||||
|
||||
return GetFrameListFromTimeRange(NoLockGetInvalidatedRanges());
|
||||
}
|
||||
|
||||
void FrameHashCache::SaveCacheFrame(const QByteArray& hash,
|
||||
|
||||
@@ -37,7 +37,7 @@ class FrameHashCache : public PlaybackCache
|
||||
public:
|
||||
FrameHashCache() = default;
|
||||
|
||||
QByteArray GetHash(const rational& time) const;
|
||||
QByteArray GetHash(const rational& time);
|
||||
|
||||
void SetHash(const rational& time, const QByteArray& hash);
|
||||
|
||||
@@ -46,14 +46,14 @@ public:
|
||||
/**
|
||||
* @brief Returns a list of frames that use a particular hash
|
||||
*/
|
||||
QList<rational> GetFramesWithHash(const QByteArray& hash) const;
|
||||
QList<rational> GetFramesWithHash(const QByteArray& hash);
|
||||
|
||||
/**
|
||||
* @brief Same as FramesWithHash() but also removes these frames from the map
|
||||
*/
|
||||
QList<rational> TakeFramesWithHash(const QByteArray& hash);
|
||||
|
||||
const QMap<rational, QByteArray>& time_hash_map() const;
|
||||
QMap<rational, QByteArray> time_hash_map();
|
||||
|
||||
/**
|
||||
* @brief Return the path of the cached image at this time
|
||||
@@ -67,8 +67,8 @@ public:
|
||||
static QString GetFormatExtension(const PixelFormat::Format& f);
|
||||
|
||||
static QList<rational> GetFrameListFromTimeRange(TimeRangeList range_list, const rational& timebase);
|
||||
QList<rational> GetFrameListFromTimeRange(const TimeRangeList &range) const;
|
||||
QList<rational> GetInvalidatedFrames() const;
|
||||
QList<rational> GetFrameListFromTimeRange(const TimeRangeList &range);
|
||||
QList<rational> GetInvalidatedFrames();
|
||||
|
||||
protected:
|
||||
virtual void LengthChangedEvent(const rational& old, const rational& newlen) override;
|
||||
|
||||
@@ -22,21 +22,93 @@
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
PlaybackCache::PlaybackCache()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void PlaybackCache::Invalidate(const TimeRange &r)
|
||||
{
|
||||
invalidated_.InsertTimeRange(r);
|
||||
QMutexLocker locker(&lock_);
|
||||
|
||||
InvalidateEvent(r);
|
||||
NoLockInvalidate(r);
|
||||
|
||||
locker.unlock();
|
||||
|
||||
emit Invalidated(r);
|
||||
}
|
||||
|
||||
void PlaybackCache::InvalidateAll()
|
||||
{
|
||||
QMutexLocker locker(&lock_);
|
||||
|
||||
TimeRange invalidate_range(0, length_);
|
||||
|
||||
NoLockInvalidate(invalidate_range);
|
||||
|
||||
locker.unlock();
|
||||
|
||||
emit Invalidated(invalidate_range);
|
||||
}
|
||||
|
||||
void PlaybackCache::SetLength(const rational &r)
|
||||
{
|
||||
QMutexLocker locker(&lock_);
|
||||
|
||||
NoLockSetLength(r);
|
||||
}
|
||||
|
||||
void PlaybackCache::Shift(const rational &from, const rational &to)
|
||||
{
|
||||
QMutexLocker locker(&lock_);
|
||||
|
||||
// An region between `from` and `to` will be inserted or spliced out
|
||||
TimeRangeList ranges_to_shift = invalidated_.Intersects(TimeRange(from, RATIONAL_MAX));
|
||||
|
||||
// Remove everything from the minimum point
|
||||
invalidated_.RemoveTimeRange(TimeRange(qMin(from, to), RATIONAL_MAX));
|
||||
|
||||
// Shift everything in our ranges to shift list
|
||||
//
|
||||
// `diff` is POSITIVE when moving forward -> and NEGATIVE when moving backward <-
|
||||
rational diff = to - from;
|
||||
foreach (const TimeRange& r, ranges_to_shift) {
|
||||
invalidated_.InsertTimeRange(r + diff);
|
||||
}
|
||||
|
||||
ShiftEvent(from, to);
|
||||
|
||||
if (diff > rational()) {
|
||||
// If shifting forward, add this section to the invalidated region
|
||||
TimeRange invalidate_range(from, to);
|
||||
|
||||
NoLockInvalidate(invalidate_range);
|
||||
|
||||
locker.unlock();
|
||||
|
||||
emit Invalidated(invalidate_range);
|
||||
}
|
||||
}
|
||||
|
||||
void PlaybackCache::Validate(const TimeRange &r)
|
||||
{
|
||||
QMutexLocker locker(&lock_);
|
||||
|
||||
NoLockValidate(r);
|
||||
|
||||
locker.unlock();
|
||||
|
||||
emit Validated(r);
|
||||
}
|
||||
|
||||
void PlaybackCache::NoLockInvalidate(const TimeRange &r)
|
||||
{
|
||||
invalidated_.InsertTimeRange(r);
|
||||
|
||||
InvalidateEvent(r);
|
||||
}
|
||||
|
||||
void PlaybackCache::NoLockValidate(const TimeRange &r)
|
||||
{
|
||||
invalidated_.RemoveTimeRange(r);
|
||||
}
|
||||
|
||||
void PlaybackCache::NoLockSetLength(const rational &r)
|
||||
{
|
||||
if (length_ == r) {
|
||||
// Same length - do nothing
|
||||
@@ -56,47 +128,6 @@ void PlaybackCache::SetLength(const rational &r)
|
||||
length_ = r;
|
||||
}
|
||||
|
||||
bool PlaybackCache::IsFullyValidated() const
|
||||
{
|
||||
return invalidated_.isEmpty();
|
||||
}
|
||||
|
||||
void PlaybackCache::Shift(const rational &from, const rational &to)
|
||||
{
|
||||
// An region between `from` and `to` will be inserted or spliced out
|
||||
TimeRangeList ranges_to_shift = invalidated_.Intersects(TimeRange(from, RATIONAL_MAX));
|
||||
|
||||
// Remove everything from the minimum point
|
||||
invalidated_.RemoveTimeRange(TimeRange(qMin(from, to), RATIONAL_MAX));
|
||||
|
||||
// Shift everything in our ranges to shift list
|
||||
//
|
||||
// `diff` is POSITIVE when moving forward -> and NEGATIVE when moving backward <-
|
||||
rational diff = to - from;
|
||||
foreach (const TimeRange& r, ranges_to_shift) {
|
||||
invalidated_.InsertTimeRange(r + diff);
|
||||
}
|
||||
|
||||
ShiftEvent(from, to);
|
||||
|
||||
if (diff > rational()) {
|
||||
// If shifting forward, add this section to the invalidated region
|
||||
Invalidate(TimeRange(from, to));
|
||||
}
|
||||
}
|
||||
|
||||
void PlaybackCache::Validate(const TimeRange &r)
|
||||
{
|
||||
invalidated_.RemoveTimeRange(r);
|
||||
|
||||
emit Validated(r);
|
||||
}
|
||||
|
||||
void PlaybackCache::InvalidateAll()
|
||||
{
|
||||
Invalidate(TimeRange(0, length_));
|
||||
}
|
||||
|
||||
void PlaybackCache::LengthChangedEvent(const rational &, const rational &)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#ifndef PLAYBACKCACHE_H
|
||||
#define PLAYBACKCACHE_H
|
||||
|
||||
#include <QMutex>
|
||||
#include <QObject>
|
||||
|
||||
#include "common/timerange.h"
|
||||
@@ -31,30 +32,41 @@ class PlaybackCache : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
PlaybackCache();
|
||||
PlaybackCache() = default;
|
||||
|
||||
void Invalidate(const TimeRange& r);
|
||||
|
||||
void InvalidateAll();
|
||||
|
||||
const rational& GetLength() const
|
||||
const rational& GetLength()
|
||||
{
|
||||
return length_;
|
||||
QMutexLocker locker(lock());
|
||||
|
||||
return NoLockGetLength();
|
||||
}
|
||||
|
||||
void SetLength(const rational& r);
|
||||
|
||||
bool IsFullyValidated() const;
|
||||
bool IsFullyValidated()
|
||||
{
|
||||
QMutexLocker locker(lock());
|
||||
|
||||
return invalidated_.isEmpty();
|
||||
}
|
||||
|
||||
void Shift(const rational& from, const rational& to);
|
||||
|
||||
const TimeRangeList& GetInvalidatedRanges() const
|
||||
const TimeRangeList& GetInvalidatedRanges()
|
||||
{
|
||||
return invalidated_;
|
||||
QMutexLocker locker(lock());
|
||||
|
||||
return NoLockGetInvalidatedRanges();
|
||||
}
|
||||
|
||||
bool HasInvalidatedRanges() const
|
||||
bool HasInvalidatedRanges()
|
||||
{
|
||||
QMutexLocker locker(lock());
|
||||
|
||||
return !invalidated_.isEmpty();
|
||||
}
|
||||
|
||||
@@ -68,13 +80,36 @@ signals:
|
||||
protected:
|
||||
void Validate(const TimeRange& r);
|
||||
|
||||
void NoLockInvalidate(const TimeRange& r);
|
||||
|
||||
void NoLockValidate(const TimeRange& r);
|
||||
|
||||
void NoLockSetLength(const rational& r);
|
||||
|
||||
const rational& NoLockGetLength() const
|
||||
{
|
||||
return length_;
|
||||
}
|
||||
|
||||
const TimeRangeList& NoLockGetInvalidatedRanges()
|
||||
{
|
||||
return invalidated_;
|
||||
}
|
||||
|
||||
virtual void LengthChangedEvent(const rational& old, const rational& newlen);
|
||||
|
||||
virtual void InvalidateEvent(const TimeRange& range);
|
||||
|
||||
virtual void ShiftEvent(const rational& from, const rational& to);
|
||||
|
||||
QMutex* lock()
|
||||
{
|
||||
return &lock_;
|
||||
}
|
||||
|
||||
private:
|
||||
QMutex lock_;
|
||||
|
||||
TimeRangeList invalidated_;
|
||||
|
||||
rational length_;
|
||||
|
||||
Reference in New Issue
Block a user