]> git.sesse.net Git - kdenlive/blob - src/kthumb.cpp
Various changes for getting an OpenGL context (almost) everywhere it is needed.
[kdenlive] / src / kthumb.cpp
1 /***************************************************************************
2                         krender.cpp  -  description
3                            -------------------
4   begin                : Fri Nov 22 2002
5   copyright            : (C) 2002 by Jason Wood
6   email                : jasonwood@blueyonder.co.uk
7   copyright            : (C) 2005 Lcio Fl�io Corr�
8   email                : lucio.correa@gmail.com
9   copyright            : (C) Marco Gittler
10   email                : g.marco@freenet.de
11
12 ***************************************************************************/
13
14 /***************************************************************************
15  *                                                                         *
16  *   This program is free software; you can redistribute it and/or modify  *
17  *   it under the terms of the GNU General Public License as published by  *
18  *   the Free Software Foundation; either version 2 of the License, or     *
19  *   (at your option) any later version.                                   *
20  *                                                                         *
21  ***************************************************************************/
22
23 #include "kthumb.h"
24 #include "clipmanager.h"
25 #include "renderer.h"
26 #include "kdenlivesettings.h"
27
28 #include <mlt++/Mlt.h>
29
30 #include <kio/netaccess.h>
31 #include <kdebug.h>
32 #include <klocale.h>
33 #include <kfileitem.h>
34 #include <kmessagebox.h>
35 #include <KStandardDirs>
36
37 #include <qxml.h>
38 #include <QImage>
39 #include <QApplication>
40 #include <QtConcurrentRun>
41 #include <QVarLengthArray>
42 #include <QPainter>
43 #include <QGLWidget>
44
45 KThumb::KThumb(ClipManager *clipManager, const KUrl &url, const QString &id, const QString &hash, QObject * parent) :
46     QObject(parent),
47     m_url(url),
48     m_thumbFile(),
49     m_dar(1),
50     m_ratio(1),
51     m_producer(NULL),
52     m_clipManager(clipManager),
53     m_id(id)
54 {
55     m_thumbFile = clipManager->projectFolder() + "/thumbs/" + hash + ".thumb";
56 }
57
58 KThumb::~KThumb()
59 {
60     if (m_producer) m_clipManager->stopThumbs(m_id);
61     m_producer = NULL;
62     m_intraFramesQueue.clear();
63     m_intra.waitForFinished();
64 }
65
66 void KThumb::setProducer(Mlt::Producer *producer)
67 {
68     if (m_producer) m_clipManager->stopThumbs(m_id);
69     m_intraFramesQueue.clear();
70     m_intra.waitForFinished();
71     QMutexLocker lock(&m_mutex);
72     m_producer = producer;
73     // FIXME: the profile() call leaks an object, but trying to free
74     // it leads to a double-free in Profile::~Profile()
75     if (producer) {
76         Mlt::Profile *profile = producer->profile();
77         m_dar = profile->dar();
78         m_ratio = (double) profile->width() / profile->height();
79     }
80 }
81
82 void KThumb::clearProducer()
83 {
84     if (m_producer) setProducer(NULL);
85 }
86
87 bool KThumb::hasProducer() const
88 {
89     return m_producer != NULL;
90 }
91
92 void KThumb::updateThumbUrl(const QString &hash)
93 {
94     m_thumbFile = m_clipManager->projectFolder() + "/thumbs/" + hash + ".thumb";
95 }
96
97 void KThumb::updateClipUrl(const KUrl &url, const QString &hash)
98 {
99     m_url = url;
100     m_thumbFile = m_clipManager->projectFolder() + "/thumbs/" + hash + ".thumb";
101 }
102
103 //static
104 QPixmap KThumb::getImage(const KUrl& url, int width, int height)
105 {
106     if (url.isEmpty()) return QPixmap();
107     return getImage(url, 0, width, height);
108 }
109
110 void KThumb::extractImage(const QList<int> &frames)
111 {
112     if (!KdenliveSettings::videothumbnails() || m_producer == NULL) return;
113     m_clipManager->slotRequestThumbs(m_id, frames);
114 }
115
116
117 void KThumb::getThumb(int frame)
118 {
119     const int theight = Kdenlive::DefaultThumbHeight;
120     const int swidth = (int)(theight * m_ratio + 0.5);
121     const int dwidth = (int)(theight * m_dar + 0.5);
122     QImage img = getProducerFrame(frame, swidth, dwidth, theight);
123     emit thumbReady(frame, img);
124 }
125
126 void KThumb::getGenericThumb(int frame, int height, int type)
127 {
128     const int swidth = (int)(height * m_ratio + 0.5);
129     const int dwidth = (int)(height * m_dar + 0.5);
130     QImage img = getProducerFrame(frame, swidth, dwidth, height);
131     m_clipManager->projectTreeThumbReady(m_id, frame, img, type);
132 }
133
134 QImage KThumb::extractImage(int frame, int width, int height)
135 {
136     if (m_producer == NULL) {
137         QImage img(width, height, QImage::Format_ARGB32_Premultiplied);
138         img.fill(QColor(Qt::black).rgb());
139         return img;
140     }
141     return getProducerFrame(frame, (int) (height * m_ratio + 0.5), width, height);
142 }
143
144 //static
145 QPixmap KThumb::getImage(const KUrl& url, int frame, int width, int height)
146 {
147     Mlt::Profile profile(KdenliveSettings::current_profile().toUtf8().constData());
148     QPixmap pix(width, height);
149     if (url.isEmpty()) return pix;
150     Mlt::Producer *producer = new Mlt::Producer(profile, url.path().toUtf8().constData());
151     double swidth = (double) profile.width() / profile.height();
152     pix = QPixmap::fromImage(getFrame(producer, frame, (int) (height * swidth + 0.5), width, height));
153     delete producer;
154     return pix;
155 }
156
157
158 QImage KThumb::getProducerFrame(int framepos, int frameWidth, int displayWidth, int height)
159 {
160     if (m_producer == NULL || !m_producer->is_valid()) {
161         QImage p(displayWidth, height, QImage::Format_ARGB32_Premultiplied);
162         p.fill(QColor(Qt::red).rgb());
163         return p;
164     }
165     if (m_producer->is_blank()) {
166         QImage p(displayWidth, height, QImage::Format_ARGB32_Premultiplied);
167         p.fill(QColor(Qt::black).rgb());
168         return p;
169     }
170     QMutexLocker lock(&m_mutex);
171     m_producer->seek(framepos);
172     Mlt::Frame *frame = m_producer->get_frame();
173     /*frame->set("rescale.interp", "nearest");
174     frame->set("deinterlace_method", "onefield");
175     frame->set("top_field_first", -1 );*/
176     QImage p = getFrame(frame, frameWidth, displayWidth, height);
177     delete frame;
178     return p;
179 }
180
181 //static
182 QImage KThumb::getFrame(Mlt::Producer *producer, int framepos, int frameWidth, int displayWidth, int height)
183 {
184     if (producer == NULL || !producer->is_valid()) {
185         QImage p(displayWidth, height, QImage::Format_ARGB32_Premultiplied);
186         p.fill(QColor(Qt::red).rgb());
187         return p;
188     }
189     if (producer->is_blank()) {
190         QImage p(displayWidth, height, QImage::Format_ARGB32_Premultiplied);
191         p.fill(QColor(Qt::black).rgb());
192         return p;
193     }
194
195     producer->seek(framepos);
196     Mlt::Frame *frame = producer->get_frame();
197     frame->set("rescale.interp", "nearest");
198     frame->set("deinterlace_method", "onefield");
199     frame->set("top_field_first", -1 );
200     const QImage p = getFrame(frame, frameWidth, displayWidth, height);
201     delete frame;
202     return p;
203 }
204
205
206 //static
207 QImage KThumb::getFrame(Mlt::Frame *frame, int frameWidth, int displayWidth, int height)
208 {
209     QImage p(displayWidth, height, QImage::Format_ARGB32_Premultiplied);
210     if (frame == NULL || !frame->is_valid()) {
211         p.fill(QColor(Qt::red).rgb());
212         return p;
213     }
214     
215     int ow = frameWidth;
216     int oh = height;
217     mlt_image_format format = mlt_image_rgb24a;
218     //frame->set("progressive", "1");
219     if (ow % 2 == 1) ow++;
220     QImage image(ow, oh, QImage::Format_ARGB32_Premultiplied);
221     const uchar* imagedata = frame->get_image(format, ow, oh);
222     if (imagedata == NULL) {
223         p.fill(QColor(Qt::red).rgb());
224         return p;
225     }
226     memcpy(image.bits(), imagedata, ow * oh * 4);//.byteCount());
227     
228     //const uchar* imagedata = frame->get_image(format, ow, oh);
229     //QImage image(imagedata, ow, oh, QImage::Format_ARGB32_Premultiplied);
230     
231     if (!image.isNull()) {
232         if (ow > (2 * displayWidth)) {
233             // there was a scaling problem, do it manually
234             image = image.scaled(displayWidth, height).rgbSwapped();
235         } else {
236             image = image.scaled(displayWidth, height, Qt::IgnoreAspectRatio).rgbSwapped();
237         }
238 #if QT_VERSION >= 0x040800
239         p.fill(QColor(100, 100, 100, 70));
240         QPainter painter(&p);
241 #else
242         p.fill(Qt::transparent);
243         QPainter painter(&p);
244         painter.fillRect(p.rect(), QColor(100, 100, 100, 70));
245 #endif
246         painter.drawImage(p.rect(), image);
247         painter.end();
248     } else
249         p.fill(QColor(Qt::red).rgb());
250     return p;
251 }
252
253 //static
254 uint KThumb::imageVariance(const QImage &image )
255 {
256     uint delta = 0;
257     uint avg = 0;
258     uint bytes = image.numBytes();
259     uint STEPS = bytes/2;
260     QVarLengthArray<uchar> pivot(STEPS);
261     const uchar *bits=image.bits();
262     // First pass: get pivots and taking average
263     for( uint i=0; i<STEPS ; ++i ){
264         pivot[i] = bits[2 * i];
265 #if QT_VERSION >= 0x040700
266         avg+=pivot.at(i);
267 #else
268         avg+=pivot[i];
269 #endif
270     }
271     if (STEPS)
272         avg=avg/STEPS;
273     // Second Step: calculate delta (average?)
274     for (uint i=0; i<STEPS; ++i)
275     {
276 #if QT_VERSION >= 0x040700
277         int curdelta=abs(int(avg - pivot.at(i)));
278 #else
279         int curdelta=abs(int(avg - pivot[i]));
280 #endif
281         delta+=curdelta;
282     }
283     if (STEPS)
284         return delta/STEPS;
285     else
286         return 0;
287 }
288
289 /*
290 void KThumb::getImage(KUrl url, int frame, int width, int height)
291 {
292     if (url.isEmpty()) return;
293     QPixmap image(width, height);
294     Mlt::Producer m_producer(url.path().toUtf8().constData());
295     image.fill(Qt::black);
296
297     if (m_producer.is_blank()) {
298  emit thumbReady(frame, image);
299  return;
300     }
301     Mlt::Filter m_convert("avcolour_space");
302     m_convert.set("forced", mlt_image_rgb24a);
303     m_producer.attach(m_convert);
304     m_producer.seek(frame);
305     Mlt::Frame * m_frame = m_producer.get_frame();
306     mlt_image_format format = mlt_image_rgb24a;
307     width = width - 2;
308     height = height - 2;
309     if (m_frame && m_frame->is_valid()) {
310      uint8_t *thumb = m_frame->get_image(format, width, height);
311      QImage tmpimage(thumb, width, height, 32, NULL, 0, QImage::IgnoreEndian);
312      if (!tmpimage.isNull()) bitBlt(&image, 1, 1, &tmpimage, 0, 0, width + 2, height + 2);
313     }
314     if (m_frame) delete m_frame;
315     emit thumbReady(frame, image);
316 }
317
318 void KThumb::getThumbs(KUrl url, int startframe, int endframe, int width, int height)
319 {
320     if (url.isEmpty()) return;
321     QPixmap image(width, height);
322     Mlt::Producer m_producer(url.path().toUtf8().constData());
323     image.fill(QColor(Qt::black).rgb());
324
325     if (m_producer.is_blank()) {
326  emit thumbReady(startframe, image);
327  emit thumbReady(endframe, image);
328  return;
329     }
330     Mlt::Filter m_convert("avcolour_space");
331     m_convert.set("forced", mlt_image_rgb24a);
332     m_producer.attach(m_convert);
333     m_producer.seek(startframe);
334     Mlt::Frame * m_frame = m_producer.get_frame();
335     mlt_image_format format = mlt_image_rgb24a;
336     width = width - 2;
337     height = height - 2;
338
339     if (m_frame && m_frame->is_valid()) {
340      uint8_t *thumb = m_frame->get_image(format, width, height);
341      QImage tmpimage(thumb, width, height, 32, NULL, 0, QImage::IgnoreEndian);
342      if (!tmpimage.isNull()) bitBlt(&image, 1, 1, &tmpimage, 0, 0, width - 2, height - 2);
343     }
344     if (m_frame) delete m_frame;
345     emit thumbReady(startframe, image);
346
347     image.fill(Qt::black);
348     m_producer.seek(endframe);
349     m_frame = m_producer.get_frame();
350
351     if (m_frame && m_frame->is_valid()) {
352      uint8_t *thumb = m_frame->get_image(format, width, height);
353      QImage tmpimage(thumb, width, height, 32, NULL, 0, QImage::IgnoreEndian);
354      if (!tmpimage.isNull()) bitBlt(&image, 1, 1, &tmpimage, 0, 0, width - 2, height - 2);
355     }
356     if (m_frame) delete m_frame;
357     emit thumbReady(endframe, image);
358 }
359 */
360
361 void KThumb::slotCreateAudioThumbs()
362 {
363     m_clipManager->askForAudioThumb(m_id);
364 }
365
366 #if KDE_IS_VERSION(4,5,0)
367 void KThumb::queryIntraThumbs(const QList <int> &missingFrames)
368 {
369     foreach (int i, missingFrames) {
370         if (!m_intraFramesQueue.contains(i)) m_intraFramesQueue.append(i);
371     }
372     qSort(m_intraFramesQueue);
373     if (!m_intra.isRunning()) {
374         m_intra = QtConcurrent::run(this, &KThumb::slotGetIntraThumbs);
375     }
376 }
377
378 void KThumb::slotGetIntraThumbs()
379 {
380     // We are in a new thread, so we need a new OpenGL context for the remainder of the function.
381     QGLWidget ctx(0, m_clipManager->getMainContext());
382     ctx.makeCurrent();
383
384     const int theight = KdenliveSettings::trackheight();
385     const int frameWidth = (int)(theight * m_ratio + 0.5);
386     const int displayWidth = (int)(theight * m_dar + 0.5);
387     QString path = m_url.path() + '_';
388     bool addedThumbs = false;
389
390     while (!m_intraFramesQueue.isEmpty()) {
391         int pos = m_intraFramesQueue.takeFirst();
392         if (!m_clipManager->pixmapCache->contains(path + QString::number(pos))) {
393             if (m_clipManager->pixmapCache->insertImage(path + QString::number(pos), getProducerFrame(pos, frameWidth, displayWidth, theight))) {
394                 addedThumbs = true;
395             }
396             else kDebug()<<"// INSERT FAILD FOR: "<<pos;
397         }
398         m_intraFramesQueue.removeAll(pos);
399     }
400     if (addedThumbs) emit thumbsCached();
401 }
402
403 QImage KThumb::findCachedThumb(const QString &path)
404 {
405     QImage img;
406     m_clipManager->pixmapCache->findImage(path, &img);
407     return img;
408 }
409 #endif
410
411 #include "kthumb.moc"
412