QObject(),
m_audioThumbsQueue(),
m_doc(doc),
- m_generatingAudioId(),
m_abortThumb(false),
- m_closing(false)
+ m_closing(false),
+ m_abortAudioThumb(false)
{
m_clipIdCounter = 1;
m_folderIdCounter = 1;
{
m_closing = true;
m_abortThumb = true;
+ m_abortAudioThumb = true;
m_thumbsThread.waitForFinished();
+ m_audioThumbsThread.waitForFinished();
m_thumbsMutex.lock();
m_requestedThumbs.clear();
- m_thumbsMutex.unlock();
m_audioThumbsQueue.clear();
- m_generatingAudioId.clear();
- m_thumbsMutex.lock();
- m_requestedThumbs.clear();
m_thumbsMutex.unlock();
+
qDeleteAll(m_clipList);
m_clipList.clear();
#if KDE_IS_VERSION(4,5,0)
void ClipManager::clear()
{
m_abortThumb = true;
+ m_abortAudioThumb = true;
m_thumbsThread.waitForFinished();
+ m_audioThumbsThread.waitForFinished();
m_thumbsMutex.lock();
m_requestedThumbs.clear();
+ m_audioThumbsQueue.clear();
m_thumbsMutex.unlock();
m_abortThumb = false;
+ m_abortAudioThumb = false;
m_folderList.clear();
- m_audioThumbsQueue.clear();
m_modifiedClips.clear();
qDeleteAll(m_clipList);
m_clipList.clear();
void ClipManager::checkAudioThumbs()
{
if (!KdenliveSettings::audiothumbnails()) {
- if (!m_generatingAudioId.isEmpty()) {
- DocClipBase *clip = getClipById(m_generatingAudioId);
- if (clip) clip->slotClearAudioCache();
+ if (m_audioThumbsThread.isRunning()) {
+ m_abortAudioThumb = true;
+ m_thumbsMutex.lock();
+ m_audioThumbsQueue.clear();
+ m_thumbsMutex.unlock();
+ m_audioThumbsThread.waitForFinished();
+ m_abortAudioThumb = false;
}
- m_audioThumbsQueue.clear();
- m_generatingAudioId.clear();
return;
}
+ m_thumbsMutex.lock();
for (int i = 0; i < m_clipList.count(); i++) {
- m_audioThumbsQueue.append(m_clipList.at(i)->getId());
+ DocClipBase *clip = m_clipList.at(i);
+ if (clip->clipType() & (AUDIO | AV | PLAYLIST) && !clip->audioThumbCreated())
+ m_audioThumbsQueue.append(m_clipList.at(i)->getId());
}
- if (m_generatingAudioId.isEmpty()) startAudioThumbsGeneration();
+ m_thumbsMutex.unlock();
+ if (!m_audioThumbsThread.isRunning()) m_audioThumbsThread = QtConcurrent::run(this, &ClipManager::slotGetAudioThumbs);
}
void ClipManager::askForAudioThumb(const QString &id)
{
DocClipBase *clip = getClipById(id);
- if (clip && KdenliveSettings::audiothumbnails()) {
- m_audioThumbsQueue.append(id);
- if (m_generatingAudioId.isEmpty()) startAudioThumbsGeneration();
+ if (clip && KdenliveSettings::audiothumbnails() && (clip->clipType() & (AUDIO | AV | PLAYLIST))) {
+ m_thumbsMutex.lock();
+ if (!m_audioThumbsQueue.contains(id)) m_audioThumbsQueue.append(id);
+ m_thumbsMutex.unlock();
+ if (!m_audioThumbsThread.isRunning()) m_audioThumbsThread = QtConcurrent::run(this, &ClipManager::slotGetAudioThumbs);
}
}
-void ClipManager::startAudioThumbsGeneration()
+void ClipManager::slotGetAudioThumbs()
{
- if (!KdenliveSettings::audiothumbnails()) {
- m_audioThumbsQueue.clear();
- m_generatingAudioId.clear();
- return;
- }
- if (!m_audioThumbsQueue.isEmpty()) {
- m_generatingAudioId = m_audioThumbsQueue.takeFirst();
- DocClipBase *clip = getClipById(m_generatingAudioId);
- if (!clip || !clip->slotGetAudioThumbs())
- endAudioThumbsGeneration(m_generatingAudioId);
- } else {
- m_generatingAudioId.clear();
- }
-}
+ Mlt::Profile prof((char*) KdenliveSettings::current_profile().toUtf8().constData());
+ mlt_audio_format audioFormat = mlt_audio_pcm;
+ while (!m_abortAudioThumb && !m_audioThumbsQueue.isEmpty()) {
+ m_thumbsMutex.lock();
+ QString clipId = m_audioThumbsQueue.takeFirst();
+ m_thumbsMutex.unlock();
+ DocClipBase *clip = getClipById(clipId);
+ if (!clip || clip->audioThumbCreated()) continue;
+ KUrl url = clip->fileURL();
+ QString hash = clip->getClipHash();
+ if (hash.isEmpty()) continue;
+ QString audioPath = projectFolder() + "/thumbs/" + hash + ".thumb";
+ double lengthInFrames = clip->duration().frames(m_doc->fps());
+ //FIXME: should this be hardcoded??
+ int channels = 2;
+ int frequency = 48000;
+ int arrayWidth = 20;
+ double frame = 0.0;
+ audioByteArray storeIn;
+ QFile f(audioPath);
+ if (QFileInfo(audioPath).size() > 0 && f.open(QIODevice::ReadOnly)) {
+ const QByteArray channelarray = f.readAll();
+ f.close();
+ if (channelarray.size() != arrayWidth*(frame + lengthInFrames) * channels) {
+ kDebug() << "--- BROKEN THUMB FOR: " << url.fileName() << " ---------------------- ";
+ f.remove();
+ continue;
+ }
+ kDebug() << "reading audio thumbs from file";
+
+ int h1 = arrayWidth * channels;
+ int h2 = (int) frame * h1;
+ int h3;
+ for (int z = (int) frame; z < (int)(frame + lengthInFrames) && !m_abortAudioThumb; z++) {
+ h3 = 0;
+ for (int c = 0; c < channels; c++) {
+ QByteArray audioArray(arrayWidth, '\x00');
+ for (int i = 0; i < arrayWidth; i++) {
+ audioArray[i] = channelarray.at(h2 + h3 + i);
+ }
+ h3 += arrayWidth;
+ storeIn[z][c] = audioArray;
+ }
+ h2 += h1;
+ }
+ if (!m_abortAudioThumb) clip->updateAudioThumbnail(storeIn);
+ continue;
+ }
+
+ if (!f.open(QIODevice::WriteOnly)) {
+ kDebug() << "++++++++ ERROR WRITING TO FILE: " << audioPath;
+ kDebug() << "++++++++ DISABLING AUDIO THUMBS";
+ m_thumbsMutex.lock();
+ m_audioThumbsQueue.clear();
+ m_thumbsMutex.unlock();
+ KdenliveSettings::setAudiothumbnails(false);
+ break;
+ }
-void ClipManager::endAudioThumbsGeneration(const QString &requestedId)
-{
- if (!KdenliveSettings::audiothumbnails()) {
- m_audioThumbsQueue.clear();
- m_generatingAudioId.clear();
- return;
- }
- if (!m_audioThumbsQueue.isEmpty()) {
- if (m_generatingAudioId == requestedId) {
- startAudioThumbsGeneration();
+ Mlt::Producer producer(prof, url.path().toUtf8().constData());
+ if (!producer.is_valid()) {
+ kDebug() << "++++++++ INVALID CLIP: " << url.path();
+ continue;
+ }
+
+ if (KdenliveSettings::normaliseaudiothumbs()) {
+ Mlt::Filter m_convert(prof, "volume");
+ m_convert.set("gain", "normalise");
+ producer.attach(m_convert);
+ }
+
+ int last_val = 0;
+ int val = 0;
+ double framesPerSecond = mlt_producer_get_fps(producer.get_producer());
+ Mlt::Frame *mlt_frame;
+
+ for (int z = (int) frame; z < (int)(frame + lengthInFrames) && producer.is_valid() && !m_abortAudioThumb; z++) {
+ val = (int)((z - frame) / (frame + lengthInFrames) * 100.0);
+ if (last_val != val && val > 1) {
+ setThumbsProgress(i18n("Creating thumbnail for %1", url.fileName()), val);
+ last_val = val;
+ }
+ producer.seek(z);
+ mlt_frame = producer.get_frame();
+ if (mlt_frame && mlt_frame->is_valid()) {
+ int samples = mlt_sample_calculator(framesPerSecond, frequency, mlt_frame_get_position(mlt_frame->get_frame()));
+ qint16* pcm = static_cast<qint16*>(mlt_frame->get_audio(audioFormat, frequency, channels, samples));
+ for (int c = 0; c < channels; c++) {
+ QByteArray audioArray;
+ audioArray.resize(arrayWidth);
+ for (int i = 0; i < audioArray.size(); i++) {
+ audioArray[i] = ((*(pcm + c + i * samples / audioArray.size())) >> 9) + 127 / 2 ;
+ }
+ f.write(audioArray);
+ storeIn[z][c] = audioArray;
+ }
+ } else {
+ f.write(QByteArray(arrayWidth, '\x00'));
+ }
+ delete mlt_frame;
+ }
+ f.close();
+ //TODO: post 8.2.1, change text to AUDIO thumbnails
+ setThumbsProgress(i18n("Creating thumbnail for %1", url.fileName()), -1);
+ if (m_abortAudioThumb) {
+ f.remove();
+ } else {
+ clip->updateAudioThumbnail(storeIn);
}
- } else {
- m_generatingAudioId.clear();
}
}
int getFreeClipId();
int getFreeFolderId();
int lastClipId() const;
- void startAudioThumbsGeneration();
- void endAudioThumbsGeneration(const QString &requestedId);
void askForAudioThumb(const QString &id);
QString projectFolder() const;
void clearUnusedProducers();
/** Check the list of externally modified clips, and process them if they were not modified in the last 1500 milliseconds */
void slotProcessModifiedClips();
void slotGetThumbs();
+ void slotGetAudioThumbs();
private: // Private attributes
/** the list of clips in the document */
KdenliveDoc *m_doc;
int m_clipIdCounter;
int m_folderIdCounter;
- QString m_generatingAudioId;
KDirWatch m_fileWatcher;
/** Timer used to reload clips when they have been externally modified */
QTimer m_modifiedTimer;
bool m_abortThumb;
/** @brief We are about to delete the clip producer, stop processing thumbs. */
bool m_closing;
+ QFuture<void> m_audioThumbsThread;
+ /** @brief If true, abort processing of audio thumbs. */
+ bool m_abortAudioThumb;
signals:
void reloadClip(const QString &);
m_videoOnlyProducer(NULL),
m_snapMarkers(QList < CommentedTime >()),
m_duration(),
- m_audioTimer(NULL),
m_thumbProd(NULL),
m_audioThumbCreated(false),
m_id(id),
if (!m_properties.contains("name")) m_properties.insert("name", url.fileName());
m_thumbProd = new KThumb(clipManager, url, m_id, m_properties.value("file_hash"));
- if (m_clipType == AV || m_clipType == AUDIO || m_clipType == PLAYLIST) slotCreateAudioTimer();
+ if (m_clipType & (AV | AUDIO | PLAYLIST)) getAudioThumbs();
}
DocClipBase::~DocClipBase()
{
delete m_thumbProd;
- if (m_audioTimer) {
- m_audioTimer->stop();
- delete m_audioTimer;
- }
qDeleteAll(m_toDeleteProducers);
m_toDeleteProducers.clear();
qDeleteAll(m_baseTrackProducers);
return zone;
}
-void DocClipBase::slotCreateAudioTimer()
-{
- connect(m_thumbProd, SIGNAL(audioThumbReady(const audioByteArray&)), this , SLOT(updateAudioThumbnail(const audioByteArray&)));
- m_audioTimer = new QTimer(this);
- connect(m_audioTimer, SIGNAL(timeout()), this, SLOT(slotGetAudioThumbs()));
-}
-
-void DocClipBase::askForAudioThumbs()
-{
- if (m_thumbProd && m_audioTimer) m_thumbProd->askForAudioThumbs(getId());
-}
void DocClipBase::slotClearAudioCache()
{
- if (m_thumbProd) m_thumbProd->stopAudioThumbs();
- if (m_audioTimer != NULL) m_audioTimer->stop();
m_audioFrameCache.clear();
m_audioThumbCreated = false;
}
void DocClipBase::setClipType(CLIPTYPE type)
{
m_clipType = type;
-
m_properties.insert("type", QString::number((int) type));
- if (m_thumbProd && m_audioTimer == NULL && (m_clipType == AV || m_clipType == AUDIO || m_clipType == PLAYLIST))
- slotCreateAudioTimer();
}
KUrl DocClipBase::fileURL() const
return m_properties;
}
-bool DocClipBase::slotGetAudioThumbs()
+bool DocClipBase::getAudioThumbs()
{
- if (m_thumbProd == NULL || isPlaceHolder()) return false;
- if (!KdenliveSettings::audiothumbnails() || m_audioTimer == NULL) {
- if (m_audioTimer != NULL) m_audioTimer->stop();
- return false;
- }
+ if (m_thumbProd == NULL || isPlaceHolder() || !KdenliveSettings::audiothumbnails()) return false;
if (m_audioThumbCreated) {
- m_audioTimer->stop();
return false;
}
- m_audioTimer->start(1500);
- double lengthInFrames = duration().frames(KdenliveSettings::project_fps());
- m_thumbProd->getAudioThumbs(2, 0, lengthInFrames /*must be number of frames*/, 20);
+ QTimer::singleShot(800, m_thumbProd, SLOT(slotCreateAudioThumbs()));
return true;
}
/** Free cache data */
void slotClearAudioCache();
- void askForAudioThumbs();
QString getClipHash() const;
void refreshThumbUrl();
const char *producerProperty(const char *name) const;
void reloadThumbProducer();
void cleanupProducers();
bool isClean() const;
+ bool getAudioThumbs();
private: // Private attributes
QPixmap m_thumbnail;
GenTime m_duration;
- QTimer *m_audioTimer;
KThumb *m_thumbProd;
bool m_audioThumbCreated;
QMutex m_replaceMutex;
/** Create connections for audio thumbnails */
- void slotCreateAudioTimer();
void slotRefreshProducer();
void setProducerProperty(const char *name, int data);
void setProducerProperty(const char *name, double data);
public slots:
void updateAudioThumbnail(const audioByteArray& data);
- bool slotGetAudioThumbs();
QList < CommentedTime > commentedSnapMarkers() const;
GenTime findNextSnapMarker(const GenTime & currTime);
GenTime findPreviousSnapMarker(const GenTime & currTime);
KThumb::KThumb(ClipManager *clipManager, KUrl url, const QString &id, const QString &hash, QObject * parent, const char */*name*/) :
QObject(parent),
- m_audioThumbProducer(),
m_url(url),
m_thumbFile(),
m_dar(1),
m_ratio(1),
m_producer(NULL),
m_clipManager(clipManager),
- m_id(id),
- m_stopAudioThumbs(false)
+ m_id(id)
{
m_thumbFile = clipManager->projectFolder() + "/thumbs/" + hash + ".thumb";
}
{
if (m_producer) m_clipManager->stopThumbs(m_id);
m_intraFramesQueue.clear();
- if (m_audioThumbProducer.isRunning()) {
- m_stopAudioThumbs = true;
- m_audioThumbProducer.waitForFinished();
- slotAudioThumbOver();
- }
m_intra.waitForFinished();
}
emit thumbReady(endframe, image);
}
*/
-void KThumb::stopAudioThumbs()
-{
- if (m_audioThumbProducer.isRunning()) {
- m_stopAudioThumbs = true;
- m_audioThumbProducer.waitForFinished();
- slotAudioThumbOver();
- }
-}
-
-void KThumb::removeAudioThumb()
-{
- if (m_thumbFile.isEmpty()) return;
- stopAudioThumbs();
- QFile f(m_thumbFile);
- f.remove();
-}
-
-void KThumb::getAudioThumbs(int channel, double frame, double frameLength, int arrayWidth)
-{
- if (channel == 0) {
- slotAudioThumbOver();
- return;
- }
- if (m_audioThumbProducer.isRunning()) {
- return;
- }
-
- audioByteArray storeIn;
- //FIXME: Hardcoded!!!
- m_frequency = 48000;
- m_channels = channel;
-
- QFile f(m_thumbFile);
- if (f.open(QIODevice::ReadOnly)) {
- const QByteArray channelarray = f.readAll();
- f.close();
- if (channelarray.size() != arrayWidth*(frame + frameLength)*m_channels) {
- kDebug() << "--- BROKEN THUMB FOR: " << m_url.fileName() << " ---------------------- " << endl;
- f.remove();
- slotAudioThumbOver();
- return;
- }
-
- kDebug() << "reading audio thumbs from file";
-
- int h1 = arrayWidth * m_channels;
- int h2 = (int) frame * h1;
- int h3;
- for (int z = (int) frame; z < (int)(frame + frameLength); z++) {
- h3 = 0;
- for (int c = 0; c < m_channels; c++) {
- QByteArray m_array(arrayWidth, '\x00');
- for (int i = 0; i < arrayWidth; i++) {
- m_array[i] = channelarray.at(h2 + h3 + i);
- }
- h3 += arrayWidth;
- storeIn[z][c] = m_array;
- }
- h2 += h1;
- }
- emit audioThumbReady(storeIn);
- slotAudioThumbOver();
- } else {
- if (m_audioThumbProducer.isRunning()) return;
- m_audioThumbFile.setFileName(m_thumbFile);
- m_frame = frame;
- m_frameLength = frameLength;
- m_arrayWidth = arrayWidth;
- m_audioThumbProducer = QtConcurrent::run(this, &KThumb::slotCreateAudioThumbs);
- /*m_audioThumbProducer.init(m_url, m_thumbFile, frame, frameLength, m_frequency, m_channels, arrayWidth);
- m_audioThumbProducer.start(QThread::LowestPriority);*/
- // kDebug() << "STARTING GENERATE THMB FOR: " <<m_id<<", URL: "<< m_url << " ................................";
- }
-}
void KThumb::slotCreateAudioThumbs()
{
- Mlt::Profile prof((char*) KdenliveSettings::current_profile().toUtf8().constData());
- Mlt::Producer producer(prof, m_url.path().toUtf8().constData());
- if (!producer.is_valid()) {
- kDebug() << "++++++++ INVALID CLIP: " << m_url.path();
- return;
- }
- if (!m_audioThumbFile.open(QIODevice::WriteOnly)) {
- kDebug() << "++++++++ ERROR WRITING TO FILE: " << m_audioThumbFile.fileName();
- kDebug() << "++++++++ DISABLING AUDIO THUMBS";
- KdenliveSettings::setAudiothumbnails(false);
- return;
- }
-
- if (KdenliveSettings::normaliseaudiothumbs()) {
- Mlt::Filter m_convert(prof, "volume");
- m_convert.set("gain", "normalise");
- producer.attach(m_convert);
- }
-
- int last_val = 0;
- int val = 0;
- mlt_audio_format m_audioFormat = mlt_audio_pcm;
- double framesPerSecond = mlt_producer_get_fps(producer.get_producer());
- Mlt::Frame *mlt_frame;
-
- for (int z = (int) m_frame; z < (int)(m_frame + m_frameLength) && producer.is_valid(); z++) {
- if (m_stopAudioThumbs) break;
- val = (int)((z - m_frame) / (m_frame + m_frameLength) * 100.0);
- if (last_val != val && val > 1) {
- m_clipManager->setThumbsProgress(i18n("Creating thumbnail for %1", m_url.fileName()), val);
- last_val = val;
- }
- producer.seek(z);
- mlt_frame = producer.get_frame();
- if (mlt_frame && mlt_frame->is_valid()) {
- int m_samples = mlt_sample_calculator(framesPerSecond, m_frequency, mlt_frame_get_position(mlt_frame->get_frame()));
- qint16* m_pcm = static_cast<qint16*>(mlt_frame->get_audio(m_audioFormat, m_frequency, m_channels, m_samples));
- for (int c = 0; c < m_channels; c++) {
- QByteArray m_array;
- m_array.resize(m_arrayWidth);
- for (int i = 0; i < m_array.size(); i++) {
- m_array[i] = ((*(m_pcm + c + i * m_samples / m_array.size())) >> 9) + 127 / 2 ;
- }
- m_audioThumbFile.write(m_array);
-
- }
- } else {
- m_audioThumbFile.write(QByteArray(m_arrayWidth, '\x00'));
- }
- delete mlt_frame;
- }
- m_audioThumbFile.close();
- if (m_stopAudioThumbs) {
- m_audioThumbFile.remove();
- } else {
- slotAudioThumbOver();
- }
-}
-
-void KThumb::slotAudioThumbOver()
-{
- m_clipManager->setThumbsProgress(i18n("Creating thumbnail for %1", m_url.fileName()), -1);
- m_clipManager->endAudioThumbsGeneration(m_id);
-}
-
-void KThumb::askForAudioThumbs(const QString &id)
-{
- m_clipManager->askForAudioThumb(id);
+ m_clipManager->askForAudioThumb(m_id);
}
#if KDE_IS_VERSION(4,5,0)
KThumb(ClipManager *clipManager, KUrl url, const QString &id, const QString &hash, QObject * parent = 0, const char *name = 0);
~KThumb();
void setProducer(Mlt::Producer *producer);
- void askForAudioThumbs(const QString &id);
bool hasProducer() const;
void clearProducer();
void updateThumbUrl(const QString &hash);
// static QPixmap getImage(QDomElement xml, int frame, int width, int height);
/* void getImage(KUrl url, int frame, int width, int height);
void getThumbs(KUrl url, int startframe, int endframe, int width, int height);*/
- void stopAudioThumbs();
- void removeAudioThumb();
- void getAudioThumbs(int channel, double frame, double frameLength, int arrayWidth);
+ void slotCreateAudioThumbs();
static QPixmap getImage(KUrl url, int frame, int width, int height);
static QImage getFrame(Mlt::Producer *producer, int framepos, int frameWidth, int displayWidth, int height);
static QImage getFrame(Mlt::Frame *frame, int frameWidth, int displayWidth, int height);
static uint imageVariance(QImage image);
private slots:
- void slotAudioThumbOver();
- void slotCreateAudioThumbs();
#if KDE_IS_VERSION(4,5,0)
/** @brief Fetch all requested frames. */
void slotGetIntraThumbs();
#endif
private:
- QFuture<void> m_audioThumbProducer;
KUrl m_url;
QString m_thumbFile;
double m_dar;
QString m_id;
/** @brief Controls the intra frames thumbnails process (cached thumbnails). */
QFuture<void> m_intra;
- QFile m_audioThumbFile;
- bool m_stopAudioThumbs;
- double m_frame;
- double m_frameLength;
- int m_frequency;
- int m_channels;
- int m_arrayWidth;
/** @brief List of frame numbers from which we want to extract thumbnails. */
QList <int> m_intraFramesQueue;
QMutex m_mutex;
else if (item->hasProxy() && !item->isProxyRunning()) {
slotCreateProxy(clip->getId());
}
- clip->askForAudioThumbs();
KUrl url = clip->fileURL();
#ifdef NEPOMUK
}
item->setProperties(properties, metadata);
clip->setProducer(producer, replace);
- clip->askForAudioThumbs();
+ clip->getAudioThumbs();
// Proxy stuff
QString size = properties.value("frame_size");