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()
}
}
}
currentItem->reloadProducer(false);
currentItem->reloadProducer(false, false, true);
}
}
}
......
......@@ -220,8 +220,6 @@ void ProjectClip::updateAudioThumbnail()
pen.setColor(Qt::white);
painter.setPen(pen);
int streamCount = 0;
qreal indicesPrPixel = qreal(getFramePlaytime()) / img.width();
double increment = qMax(1., 1. / qAbs(indicesPrPixel));
if (streams.count() > 0) {
double streamHeight = iconHeight / streams.count();
QMapIterator<int, QString> st(streams);
......@@ -230,14 +228,17 @@ void ProjectClip::updateAudioThumbnail()
int channels = channelsList.value(st.key());
double channelHeight = (double) streamHeight / channels;
const QVector <uint8_t> audioLevels = audioFrameCache(st.key());
qreal indicesPrPixel = qreal(audioLevels.length()) / img.width();
int idx;
for (int channel = 0; channel < channels; channel++) {
double y = (streamHeight * streamCount) + (channel * channelHeight) + channelHeight / 2;
for (int i = 0; i <= img.width(); i++) {
double j = i * increment;
int idx = j * indicesPrPixel;
idx = ceil(i * indicesPrPixel);
idx += idx % channels;
idx += channel;
if (idx >= audioLevels.length() || idx < 0) break;
if (idx >= audioLevels.length() || idx < 0) {
break;
}
double level = audioLevels.at(idx) * channelHeight / 510.; // divide height by 510 (2*255) to get height
painter.drawLine(i, y - level, i, y + level);
}
......@@ -353,7 +354,7 @@ size_t ProjectClip::frameDuration() const
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
int loadjobId = -1;
......@@ -399,7 +400,7 @@ void ProjectClip::reloadProducer(bool refreshOnly, bool isProxy)
ThumbnailCache::get()->invalidateThumbsForClip(clipId());
int loadJob = pCore->jobManager()->startJob<LoadJob>({clipId()}, loadjobId, QString(), xml);
emit pCore->jobManager()->startJob<ThumbJob>({clipId()}, loadJob, QString(), -1, true, true);
if (!isProxy && hashChanged) {
if (forceAudioReload || (!isProxy && hashChanged)) {
discardAudioThumb();
}
if (KdenliveSettings::audiothumbnails()) {
......@@ -1622,7 +1623,7 @@ void ProjectClip::setRating(uint rating)
pCore->currentDoc()->setModified(true);
}
QVector <uint8_t> ProjectClip::audioFrameCache(int stream)
const QVector <uint8_t> ProjectClip::audioFrameCache(int stream)
{
QVector <uint8_t> audioLevels;
if (stream == -1) {
......@@ -1632,12 +1633,19 @@ QVector <uint8_t> ProjectClip::audioFrameCache(int stream)
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
const QString cachePath = getAudioThumbPath(stream);
// checking for cached thumbs
QImage image(cachePath);
if (!image.isNull()) {
int channels = m_audioInfo->channels();
int channels = m_audioInfo->channelsForStream(stream);
int n = image.width() * image.height();
for (int i = 0; i < n; i++) {
QRgb p = image.pixel(i / channels, i % channels);
......@@ -1647,6 +1655,11 @@ QVector <uint8_t> ProjectClip::audioFrameCache(int stream)
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;
}
......
......@@ -81,7 +81,7 @@ protected:
public:
~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. */
// virtual void hash() = 0;
......@@ -225,7 +225,7 @@ public:
void getThumbFromPercent(int percent);
/** @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
*/
int getAudioStreamFfmpegIndex(int mltStream);
......@@ -288,6 +288,7 @@ private:
QFuture<void> m_thumbThread;
QList<int> m_requestedThumbs;
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
void createDisabledMasterProducer();
......
......@@ -393,9 +393,6 @@ std::shared_ptr<ProjectClip> ProjectItemModel::getClipByBinID(const QString &bin
const QVector<uint8_t> ProjectItemModel::getAudioLevelsByBinID(const QString &binId, int stream)
{
READ_LOCK();
if (binId.contains(QLatin1Char('_'))) {
return getAudioLevelsByBinID(binId.section(QLatin1Char('_'), 0, 0), stream);
}
for (const auto &clip : m_allItems) {
auto c = std::static_pointer_cast<AbstractProjectItem>(clip.second.lock());
if (c->itemType() == AbstractProjectItem::ClipItem && c->clipId() == binId) {
......
......@@ -44,7 +44,8 @@ the Free Software Foundation, either version 3 of the License, or
std::unique_ptr<Core> Core::m_self;
Core::Core()
: m_thumbProfile(nullptr)
: audioThumbCache(QStringLiteral("audioCache"), 2000000)
, m_thumbProfile(nullptr)
, m_capture(new MediaCapture(this))
{
}
......
......@@ -20,6 +20,7 @@ the Free Software Foundation, either version 3 of the License, or
#include <QUrl>
#include <memory>
#include <QPoint>
#include <KSharedDataCache>
#include "timecode.h"
class Bin;
......@@ -226,6 +227,7 @@ public:
int audioChannels();
/** @brief Add guides in the project. */
void addGuides(QList <int> guides);
KSharedDataCache audioThumbCache;
private:
explicit Core();
......
......@@ -121,6 +121,14 @@ QMap <int, int> AudioStreamInfo::streamChannels() const
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
{
if (m_activeStreams.size() == 1 && m_activeStreams.contains(INT_MAX)) {
......
......@@ -32,6 +32,8 @@ public:
QMap <int, QString> streams() const;
/** @brief returns a list of audio stream index > channels per stream */
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 */
QList <int> activeStreamChannels() const;
/** @brief returns a list of enabled audio stream indexes > stream description */
......
......@@ -261,13 +261,15 @@ Item {
}
property double streamHeight: audioThumb.height / streamThumb.count
Item {
anchors.fill: parent
TimelineWaveform {
width: audioThumb.width
anchors.right: parent.right
anchors.left: parent.left
height: streamThumb.streamHeight
y: model.index * height
channels: controller.audioChannels[model.index]
binId: controller.clipId
audioStream: controller.audioStreams[model.index] //clipRoot.audioStream
audioStream: controller.audioStreams[model.index]
isFirstChunk: false
showItem: audioThumb.visible
format: controller.audioThumbFormat
......
......@@ -264,6 +264,7 @@ bool ProjectManager::closeCurrentDocument(bool saveChanges, bool quit)
break;
}
}
pCore->audioThumbCache.clear();
pCore->jobManager()->slotCancelJobs();
disconnect(pCore->window()->getMainTimeline()->controller(), &TimelineController::durationChanged, this, &ProjectManager::adjustProjectDuration);
pCore->window()->getMainTimeline()->controller()->clipActions.clear();
......
......@@ -104,8 +104,13 @@ public:
//setMipmap(true);
setTextureSize(QSize(1, 1));
connect(this, &TimelineWaveform::levelsChanged, [&]() {
if (!m_binId.isEmpty() && m_audioLevels.isEmpty() && m_stream >= 0) {
if (!m_binId.isEmpty()) {
if (m_audioLevels.isEmpty() && m_stream >= 0) {
update();
} else {
// Clip changed, reset levels
m_audioLevels.clear();
}
}
});
connect(this, &TimelineWaveform::propertyChanged, [&]() {
......@@ -140,7 +145,6 @@ public:
}
}
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();
pen.setColor(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