Various fixes for audio thumb management, should slightly improve memory usage/performnace.

Related to #102
parent a0db1142
Pipeline #28984 passed with stage
in 30 minutes and 41 seconds
...@@ -1510,7 +1510,7 @@ void Bin::slotReloadClip() ...@@ -1510,7 +1510,7 @@ void Bin::slotReloadClip()
} }
} }
} }
currentItem->reloadProducer(false); currentItem->reloadProducer(false, false, true);
} }
} }
} }
......
...@@ -220,8 +220,6 @@ void ProjectClip::updateAudioThumbnail() ...@@ -220,8 +220,6 @@ void ProjectClip::updateAudioThumbnail()
pen.setColor(Qt::white); pen.setColor(Qt::white);
painter.setPen(pen); painter.setPen(pen);
int streamCount = 0; int streamCount = 0;
qreal indicesPrPixel = qreal(getFramePlaytime()) / img.width();
double increment = qMax(1., 1. / qAbs(indicesPrPixel));
if (streams.count() > 0) { if (streams.count() > 0) {
double streamHeight = iconHeight / streams.count(); double streamHeight = iconHeight / streams.count();
QMapIterator<int, QString> st(streams); QMapIterator<int, QString> st(streams);
...@@ -230,17 +228,20 @@ void ProjectClip::updateAudioThumbnail() ...@@ -230,17 +228,20 @@ void ProjectClip::updateAudioThumbnail()
int channels = channelsList.value(st.key()); int channels = channelsList.value(st.key());
double channelHeight = (double) streamHeight / channels; double channelHeight = (double) streamHeight / channels;
const QVector <uint8_t> audioLevels = audioFrameCache(st.key()); const QVector <uint8_t> audioLevels = audioFrameCache(st.key());
qreal indicesPrPixel = qreal(audioLevels.length()) / img.width();
int idx;
for (int channel = 0; channel < channels; channel++) { for (int channel = 0; channel < channels; channel++) {
double y = (streamHeight * streamCount) + (channel * channelHeight) + channelHeight / 2; double y = (streamHeight * streamCount) + (channel * channelHeight) + channelHeight / 2;
for (int i = 0; i <= img.width(); i++) { for (int i = 0; i <= img.width(); i++) {
double j = i * increment; idx = ceil(i * indicesPrPixel);
int idx = j * indicesPrPixel;
idx += idx % channels; idx += idx % channels;
idx += channel; idx += channel;
if (idx >= audioLevels.length() || idx < 0) break; if (idx >= audioLevels.length() || idx < 0) {
double level = audioLevels.at(idx) * channelHeight / 510.; // divide height by 510 (2*255) to get height break;
painter.drawLine(i, y - level, i, y + level);
} }
double level = audioLevels.at(idx) * channelHeight / 510.; // divide height by 510 (2*255) to get height
painter.drawLine(i, y - level, i, y + level);
}
} }
streamCount++; streamCount++;
} }
...@@ -353,7 +354,7 @@ size_t ProjectClip::frameDuration() const ...@@ -353,7 +354,7 @@ size_t ProjectClip::frameDuration() const
return (size_t)getFramePlaytime(); return (size_t)getFramePlaytime();
} }
void ProjectClip::reloadProducer(bool refreshOnly, bool isProxy) void ProjectClip::reloadProducer(bool refreshOnly, bool isProxy, bool forceAudioReload)
{ {
// we find if there are some loading job on that clip // we find if there are some loading job on that clip
int loadjobId = -1; int loadjobId = -1;
...@@ -399,7 +400,7 @@ void ProjectClip::reloadProducer(bool refreshOnly, bool isProxy) ...@@ -399,7 +400,7 @@ void ProjectClip::reloadProducer(bool refreshOnly, bool isProxy)
ThumbnailCache::get()->invalidateThumbsForClip(clipId()); ThumbnailCache::get()->invalidateThumbsForClip(clipId());
int loadJob = pCore->jobManager()->startJob<LoadJob>({clipId()}, loadjobId, QString(), xml); int loadJob = pCore->jobManager()->startJob<LoadJob>({clipId()}, loadjobId, QString(), xml);
emit pCore->jobManager()->startJob<ThumbJob>({clipId()}, loadJob, QString(), -1, true, true); emit pCore->jobManager()->startJob<ThumbJob>({clipId()}, loadJob, QString(), -1, true, true);
if (!isProxy && hashChanged) { if (forceAudioReload || (!isProxy && hashChanged)) {
discardAudioThumb(); discardAudioThumb();
} }
if (KdenliveSettings::audiothumbnails()) { if (KdenliveSettings::audiothumbnails()) {
...@@ -1622,7 +1623,7 @@ void ProjectClip::setRating(uint rating) ...@@ -1622,7 +1623,7 @@ void ProjectClip::setRating(uint rating)
pCore->currentDoc()->setModified(true); pCore->currentDoc()->setModified(true);
} }
QVector <uint8_t> ProjectClip::audioFrameCache(int stream) const QVector <uint8_t> ProjectClip::audioFrameCache(int stream)
{ {
QVector <uint8_t> audioLevels; QVector <uint8_t> audioLevels;
if (stream == -1) { if (stream == -1) {
...@@ -1632,12 +1633,19 @@ QVector <uint8_t> ProjectClip::audioFrameCache(int stream) ...@@ -1632,12 +1633,19 @@ QVector <uint8_t> ProjectClip::audioFrameCache(int stream)
return audioLevels; return audioLevels;
} }
} }
QString key = QString("%1:%2").arg(m_binId).arg(stream);
QByteArray audioData;
if (pCore->audioThumbCache.find(key, &audioData)) {
QDataStream in(audioData);
in >> audioLevels;
return audioLevels;
}
// convert cached image // convert cached image
const QString cachePath = getAudioThumbPath(stream); const QString cachePath = getAudioThumbPath(stream);
// checking for cached thumbs // checking for cached thumbs
QImage image(cachePath); QImage image(cachePath);
if (!image.isNull()) { if (!image.isNull()) {
int channels = m_audioInfo->channels(); int channels = m_audioInfo->channelsForStream(stream);
int n = image.width() * image.height(); int n = image.width() * image.height();
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
QRgb p = image.pixel(i / channels, i % channels); QRgb p = image.pixel(i / channels, i % channels);
...@@ -1647,6 +1655,11 @@ QVector <uint8_t> ProjectClip::audioFrameCache(int stream) ...@@ -1647,6 +1655,11 @@ QVector <uint8_t> ProjectClip::audioFrameCache(int stream)
audioLevels << (uint8_t)qAlpha(p); audioLevels << (uint8_t)qAlpha(p);
} }
} }
// populate vector
QDataStream st(&audioData, QIODevice::WriteOnly);
st << audioLevels;
pCore->audioThumbCache.insert(key, audioData);
qDebug()<<"=== AUDIO THUMBS TOTAL : "<<pCore->audioThumbCache.totalSize()<<" // "<<pCore->audioThumbCache.freeSize();
return audioLevels; return audioLevels;
} }
......
...@@ -81,7 +81,7 @@ protected: ...@@ -81,7 +81,7 @@ protected:
public: public:
~ProjectClip() override; ~ProjectClip() override;
void reloadProducer(bool refreshOnly = false, bool isProxy = false); void reloadProducer(bool refreshOnly = false, bool isProxy = false, bool forceAudioReload = false);
/** @brief Returns a unique hash identifier used to store clip thumbnails. */ /** @brief Returns a unique hash identifier used to store clip thumbnails. */
// virtual void hash() = 0; // virtual void hash() = 0;
...@@ -225,7 +225,7 @@ public: ...@@ -225,7 +225,7 @@ public:
void getThumbFromPercent(int percent); void getThumbFromPercent(int percent);
/** @brief Return audio cache for a stream /** @brief Return audio cache for a stream
*/ */
QVector <uint8_t> audioFrameCache(int stream = -1); const QVector <uint8_t> audioFrameCache(int stream = -1);
/** @brief Return FFmpeg's audio stream index for an MLT audio stream index /** @brief Return FFmpeg's audio stream index for an MLT audio stream index
*/ */
int getAudioStreamFfmpegIndex(int mltStream); int getAudioStreamFfmpegIndex(int mltStream);
...@@ -288,6 +288,7 @@ private: ...@@ -288,6 +288,7 @@ private:
QFuture<void> m_thumbThread; QFuture<void> m_thumbThread;
QList<int> m_requestedThumbs; QList<int> m_requestedThumbs;
const QString geometryWithOffset(const QString &data, int offset); const QString geometryWithOffset(const QString &data, int offset);
QMap <QString, QByteArray> m_audioLevels;
// This is a helper function that creates the disabled producer. This is a clone of the original one, with audio and video disabled // This is a helper function that creates the disabled producer. This is a clone of the original one, with audio and video disabled
void createDisabledMasterProducer(); void createDisabledMasterProducer();
......
...@@ -393,9 +393,6 @@ std::shared_ptr<ProjectClip> ProjectItemModel::getClipByBinID(const QString &bin ...@@ -393,9 +393,6 @@ std::shared_ptr<ProjectClip> ProjectItemModel::getClipByBinID(const QString &bin
const QVector<uint8_t> ProjectItemModel::getAudioLevelsByBinID(const QString &binId, int stream) const QVector<uint8_t> ProjectItemModel::getAudioLevelsByBinID(const QString &binId, int stream)
{ {
READ_LOCK(); READ_LOCK();
if (binId.contains(QLatin1Char('_'))) {
return getAudioLevelsByBinID(binId.section(QLatin1Char('_'), 0, 0), stream);
}
for (const auto &clip : m_allItems) { for (const auto &clip : m_allItems) {
auto c = std::static_pointer_cast<AbstractProjectItem>(clip.second.lock()); auto c = std::static_pointer_cast<AbstractProjectItem>(clip.second.lock());
if (c->itemType() == AbstractProjectItem::ClipItem && c->clipId() == binId) { if (c->itemType() == AbstractProjectItem::ClipItem && c->clipId() == binId) {
......
...@@ -44,7 +44,8 @@ the Free Software Foundation, either version 3 of the License, or ...@@ -44,7 +44,8 @@ the Free Software Foundation, either version 3 of the License, or
std::unique_ptr<Core> Core::m_self; std::unique_ptr<Core> Core::m_self;
Core::Core() Core::Core()
: m_thumbProfile(nullptr) : audioThumbCache(QStringLiteral("audioCache"), 2000000)
, m_thumbProfile(nullptr)
, m_capture(new MediaCapture(this)) , m_capture(new MediaCapture(this))
{ {
} }
......
...@@ -20,6 +20,7 @@ the Free Software Foundation, either version 3 of the License, or ...@@ -20,6 +20,7 @@ the Free Software Foundation, either version 3 of the License, or
#include <QUrl> #include <QUrl>
#include <memory> #include <memory>
#include <QPoint> #include <QPoint>
#include <KSharedDataCache>
#include "timecode.h" #include "timecode.h"
class Bin; class Bin;
...@@ -226,6 +227,7 @@ public: ...@@ -226,6 +227,7 @@ public:
int audioChannels(); int audioChannels();
/** @brief Add guides in the project. */ /** @brief Add guides in the project. */
void addGuides(QList <int> guides); void addGuides(QList <int> guides);
KSharedDataCache audioThumbCache;
private: private:
explicit Core(); explicit Core();
......
...@@ -121,6 +121,14 @@ QMap <int, int> AudioStreamInfo::streamChannels() const ...@@ -121,6 +121,14 @@ QMap <int, int> AudioStreamInfo::streamChannels() const
return m_audioChannels; return m_audioChannels;
} }
int AudioStreamInfo::channelsForStream(int stream) const
{
if (m_audioChannels.contains(stream)) {
return m_audioChannels.value(stream);
}
return m_channels;
}
QList <int> AudioStreamInfo::activeStreamChannels() const QList <int> AudioStreamInfo::activeStreamChannels() const
{ {
if (m_activeStreams.size() == 1 && m_activeStreams.contains(INT_MAX)) { if (m_activeStreams.size() == 1 && m_activeStreams.contains(INT_MAX)) {
......
...@@ -32,6 +32,8 @@ public: ...@@ -32,6 +32,8 @@ public:
QMap <int, QString> streams() const; QMap <int, QString> streams() const;
/** @brief returns a list of audio stream index > channels per stream */ /** @brief returns a list of audio stream index > channels per stream */
QMap <int, int> streamChannels() const; QMap <int, int> streamChannels() const;
/** @brief returns the channel count for a stream */
int channelsForStream(int stream) const;
/** @brief returns a list of audio channels per active stream */ /** @brief returns a list of audio channels per active stream */
QList <int> activeStreamChannels() const; QList <int> activeStreamChannels() const;
/** @brief returns a list of enabled audio stream indexes > stream description */ /** @brief returns a list of enabled audio stream indexes > stream description */
......
...@@ -261,13 +261,15 @@ Item { ...@@ -261,13 +261,15 @@ Item {
} }
property double streamHeight: audioThumb.height / streamThumb.count property double streamHeight: audioThumb.height / streamThumb.count
Item { Item {
anchors.fill: parent
TimelineWaveform { TimelineWaveform {
width: audioThumb.width anchors.right: parent.right
anchors.left: parent.left
height: streamThumb.streamHeight height: streamThumb.streamHeight
y: model.index * height y: model.index * height
channels: controller.audioChannels[model.index] channels: controller.audioChannels[model.index]
binId: controller.clipId binId: controller.clipId
audioStream: controller.audioStreams[model.index] //clipRoot.audioStream audioStream: controller.audioStreams[model.index]
isFirstChunk: false isFirstChunk: false
showItem: audioThumb.visible showItem: audioThumb.visible
format: controller.audioThumbFormat format: controller.audioThumbFormat
......
...@@ -264,6 +264,7 @@ bool ProjectManager::closeCurrentDocument(bool saveChanges, bool quit) ...@@ -264,6 +264,7 @@ bool ProjectManager::closeCurrentDocument(bool saveChanges, bool quit)
break; break;
} }
} }
pCore->audioThumbCache.clear();
pCore->jobManager()->slotCancelJobs(); pCore->jobManager()->slotCancelJobs();
disconnect(pCore->window()->getMainTimeline()->controller(), &TimelineController::durationChanged, this, &ProjectManager::adjustProjectDuration); disconnect(pCore->window()->getMainTimeline()->controller(), &TimelineController::durationChanged, this, &ProjectManager::adjustProjectDuration);
pCore->window()->getMainTimeline()->controller()->clipActions.clear(); pCore->window()->getMainTimeline()->controller()->clipActions.clear();
......
...@@ -104,8 +104,13 @@ public: ...@@ -104,8 +104,13 @@ public:
//setMipmap(true); //setMipmap(true);
setTextureSize(QSize(1, 1)); setTextureSize(QSize(1, 1));
connect(this, &TimelineWaveform::levelsChanged, [&]() { connect(this, &TimelineWaveform::levelsChanged, [&]() {
if (!m_binId.isEmpty() && m_audioLevels.isEmpty() && m_stream >= 0) { if (!m_binId.isEmpty()) {
update(); if (m_audioLevels.isEmpty() && m_stream >= 0) {
update();
} else {
// Clip changed, reset levels
m_audioLevels.clear();
}
} }
}); });
connect(this, &TimelineWaveform::propertyChanged, [&]() { connect(this, &TimelineWaveform::propertyChanged, [&]() {
...@@ -140,7 +145,6 @@ public: ...@@ -140,7 +145,6 @@ public:
} }
} }
qreal indicesPrPixel = qreal(m_outPoint - m_inPoint) / width() * m_precisionFactor; qreal indicesPrPixel = qreal(m_outPoint - m_inPoint) / width() * m_precisionFactor;
//qDebug()<<"== GOT DIMENSIONS FOR WAVE: "<<m_inPoint<<"-"<<m_outPoint<<", WID: "<<width();
QPen pen = painter->pen(); QPen pen = painter->pen();
pen.setColor(m_color); pen.setColor(m_color);
painter->setBrush(m_color); painter->setBrush(m_color);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment