]> git.sesse.net Git - kdenlive/blob - src/kthumb.cpp
2358fc7fee225140ae9ae87902bb13a72ee2bc5b
[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 <qapplication.h>
24
25 #include <kio/netaccess.h>
26 #include <kdebug.h>
27 #include <klocale.h>
28 #include <kdenlivesettings.h>
29 #include <kfileitem.h>
30 #include <kmessagebox.h>
31
32 #include <mlt++/Mlt.h>
33
34 #include <qxml.h>
35 #include <qimage.h>
36
37 #include <QThread>
38 #include <QApplication>
39 #include <QCryptographicHash>
40
41 #include "clipmanager.h"
42 #include "renderer.h"
43 #include "kthumb.h"
44 #include "kdenlivesettings.h"
45 #include "events.h"
46
47 void MyThread::init(QObject *parent, KUrl url, QString target, double frame, double frameLength, int frequency, int channels, int arrayWidth)
48     {
49         stop_me = false;
50         m_parent = parent;
51         m_isWorking = false;
52         f.setFileName(target);
53         m_url = url;
54         m_frame = frame;
55         m_frameLength = frameLength;
56         m_frequency = frequency;
57         m_channels = channels;
58         m_arrayWidth = arrayWidth;
59     }
60
61     bool MyThread::isWorking()
62     {
63         return m_isWorking;
64     }
65
66     void MyThread::run()
67     {
68                 
69                  if (!f.open( QIODevice::WriteOnly )) {
70                         kDebug()<<"++++++++  ERROR WRITING TO FILE: "<<f.fileName()<<endl;
71                         kDebug()<<"++++++++  DISABLING AUDIO THUMBS"<<endl;
72                         //TODO KdenliveSettings::setAudiothumbnails(false);
73                         return;
74                 }
75                 m_isWorking = true;
76                 Mlt::Profile prof((char*) qstrdup(KdenliveSettings::current_profile().toUtf8()));
77                 Mlt::Producer m_producer(prof, m_url.path().toUtf8().data());
78                 
79
80                 /*TODO if (KdenliveSettings::normaliseaudiothumbs()) {
81                     Mlt::Filter m_convert(prof,"volume");
82                     m_convert.set("gain", "normalise");
83                     m_producer.attach(m_convert);
84                 }*/
85
86                 //QApplication::postEvent(m_parent, new ProgressEvent(-1, (QEvent::Type)10005));
87         
88                 int last_val = 0;
89                 int val = 0;
90                 kDebug() << "for " << m_frame << " " << m_frameLength << " " << m_producer.is_valid();
91                 for (int z=(int) m_frame;z<(int) (m_frame+m_frameLength) && m_producer.is_valid();z++){
92                         if (stop_me) break;
93                         val=(int)((z-m_frame)/(m_frame+m_frameLength)*100.0);
94                         if (last_val!=val & val > 1){
95                                 QApplication::postEvent(m_parent, new ProgressEvent(val, (QEvent::Type)10005));
96                                 
97                                 last_val=val;
98                         }
99                                 m_producer.seek( z );
100                                 Mlt::Frame *mlt_frame = m_producer.get_frame();
101                                 if ( mlt_frame && mlt_frame->is_valid() )
102                                 {
103                                         double m_framesPerSecond = mlt_producer_get_fps( m_producer.get_producer() ); //mlt_frame->get_double( "fps" );
104                                         int m_samples = mlt_sample_calculator( m_framesPerSecond, m_frequency, mlt_frame_get_position(mlt_frame->get_frame()) );
105                                         mlt_audio_format m_audioFormat = mlt_audio_pcm;
106                                 
107                                         int16_t* m_pcm = mlt_frame->get_audio(m_audioFormat, m_frequency, m_channels, m_samples ); 
108
109                                         for (int c=0;c< m_channels;c++){
110                                                 QByteArray m_array;
111                                                 m_array.resize(m_arrayWidth);
112                                                 for (uint i = 0; i < m_array.size(); i++){
113                                                         m_array[i] =  ( (*( m_pcm + c + i * m_samples / m_array.size() )) >> 9 ) +127/2 ;
114                                                 }
115                                                 f.write(m_array);
116                                                 
117                                         }
118                                 } else{
119                                         f.write(QByteArray(m_arrayWidth,'\x00'));
120                                 }
121                                 if (mlt_frame)
122                                         delete mlt_frame;
123                 }
124                 kDebug() << "done";
125                 f.close();
126                 m_isWorking = false;
127                 if (stop_me) {
128                     f.remove();
129                     QApplication::postEvent(m_parent, new ProgressEvent(-1, (QEvent::Type)10005));
130                    
131                 }
132                 QApplication::postEvent(m_parent, new ProgressEvent(0, (QEvent::Type)10005));
133                 
134     }
135
136
137 #define _S(a)           (a)>255 ? 255 : (a)<0 ? 0 : (a)
138 #define _R(y,u,v) (0x2568*(y)                          + 0x3343*(u)) /0x2000
139 #define _G(y,u,v) (0x2568*(y) - 0x0c92*(v) - 0x1a1e*(u)) /0x2000
140 #define _B(y,u,v) (0x2568*(y) + 0x40cf*(v))                                          /0x2000
141
142 KThumb::KThumb(ClipManager *clipManager, KUrl url, int width, int height, QObject * parent, const char *name):QObject(parent), m_clipManager(clipManager), m_url(url), m_width(width), m_height(height)
143 {
144
145   m_profile = new Mlt::Profile((char*) KdenliveSettings::current_profile().data());
146   QCryptographicHash context(QCryptographicHash::Sha1);
147   context.addData((KFileItem(m_url,"text/plain", S_IFREG).timeString() + m_url.fileName()).toAscii().data());   
148   m_thumbFile = KdenliveSettings::currenttmpfolder() + context.result().toHex() + ".thumb";
149   kDebug() << "thumbfile=" << m_thumbFile;
150 }
151
152 KThumb::~KThumb()
153 {
154     if (m_profile) delete m_profile;
155     if (thumbProducer.isRunning ()) thumbProducer.exit();
156 }
157
158
159 //static
160 QPixmap KThumb::getImage(KUrl url, int width, int height)
161 {
162     if (url.isEmpty()) return QPixmap();
163     QPixmap pix(width, height);
164   kDebug()<<"+++++++++++  GET THMB IMG FOR: "<<url;
165     char *tmp = Render::decodedString(url.path());
166     Mlt::Profile prof((char*) KdenliveSettings::current_profile().data());
167     Mlt::Producer m_producer(prof, tmp);
168     delete tmp;
169
170     if (m_producer.is_blank()) {
171         pix.fill(Qt::black);
172         return pix;
173     }
174     Mlt::Frame * m_frame;
175     mlt_image_format format = mlt_image_rgb24a;
176     Mlt::Filter m_convert(prof, "avcolour_space");
177     m_convert.set("forced", mlt_image_rgb24a);
178     m_producer.attach(m_convert);
179     //m_producer.seek(frame);
180     m_frame = m_producer.get_frame();
181     if (m_frame && m_frame->is_valid()) {
182       uint8_t *thumb = m_frame->get_image(format, width, height);
183       QImage image(thumb, width, height, QImage::Format_ARGB32);
184       if (!image.isNull()) {
185         pix = pix.fromImage(image);
186       }
187       else pix.fill(Qt::black);
188     }
189     if (m_frame) delete m_frame;
190     return pix;
191 }
192
193 void KThumb::extractImage(int frame, int frame2)
194 {
195     if (m_url.isEmpty()) return;
196     QPixmap pix(m_width, m_height);
197     char *tmp = Render::decodedString(m_url.path());
198     Mlt::Producer m_producer(*m_profile, tmp);
199     delete tmp;
200
201     if (m_producer.is_blank()) {
202         pix.fill(Qt::black);
203         emit thumbReady(frame, pix);
204         return;
205     }
206     Mlt::Frame * m_frame;
207     mlt_image_format format = mlt_image_rgb24a;
208     Mlt::Filter m_convert(*m_profile, "avcolour_space");
209     m_convert.set("forced", mlt_image_rgb24a);
210     m_producer.attach(m_convert);
211     if (frame != -1) {
212       m_producer.seek(frame);
213       m_frame = m_producer.get_frame();
214       if (m_frame && m_frame->is_valid()) {
215         uint8_t *thumb = m_frame->get_image(format, m_width, m_height);
216         QImage image(thumb, m_width, m_height, QImage::Format_ARGB32);
217         if (!image.isNull()) {
218           pix = pix.fromImage(image);
219         }
220         else pix.fill(Qt::black);
221       }
222       if (m_frame) delete m_frame;
223       emit thumbReady(frame, pix);
224     }
225     if (frame2 == -1) return;
226     m_producer.seek(frame2);
227     m_frame = m_producer.get_frame();
228     if (m_frame && m_frame->is_valid()) {
229       uint8_t *thumb = m_frame->get_image(format, m_width, m_height);
230       QImage image(thumb, m_width, m_height, QImage::Format_ARGB32);
231       if (!image.isNull()) {
232         pix = pix.fromImage(image);
233       }
234       else pix.fill(Qt::black);
235     }
236     if (m_frame) delete m_frame;
237     emit thumbReady(frame2, pix);
238
239 }
240 /*
241 void KThumb::getImage(KUrl url, int frame, int width, int height)
242 {
243     if (url.isEmpty()) return;
244     QPixmap image(width, height);
245     char *tmp = KRender::decodedString(url.path());
246     Mlt::Producer m_producer(tmp);
247     delete tmp;
248     image.fill(Qt::black);
249
250     if (m_producer.is_blank()) {
251         emit thumbReady(frame, image);
252         return;
253     }
254     Mlt::Filter m_convert("avcolour_space");
255     m_convert.set("forced", mlt_image_rgb24a);
256     m_producer.attach(m_convert);
257     m_producer.seek(frame);
258     Mlt::Frame * m_frame = m_producer.get_frame();
259     mlt_image_format format = mlt_image_rgb24a;
260     width = width - 2;
261     height = height - 2;
262     if (m_frame && m_frame->is_valid()) {
263         uint8_t *thumb = m_frame->get_image(format, width, height);
264         QImage tmpimage(thumb, width, height, 32, NULL, 0, QImage::IgnoreEndian);
265         if (!tmpimage.isNull()) bitBlt(&image, 1, 1, &tmpimage, 0, 0, width + 2, height + 2);
266     }
267     if (m_frame) delete m_frame;
268     emit thumbReady(frame, image);
269 }
270
271 void KThumb::getThumbs(KUrl url, int startframe, int endframe, int width, int height)
272 {
273     if (url.isEmpty()) return;
274     QPixmap image(width, height);
275     char *tmp = KRender::decodedString(url.path());
276     Mlt::Producer m_producer(tmp);
277     delete tmp;
278     image.fill(Qt::black);
279
280     if (m_producer.is_blank()) {
281         emit thumbReady(startframe, image);
282         emit thumbReady(endframe, image);
283         return;
284     }
285     Mlt::Filter m_convert("avcolour_space");
286     m_convert.set("forced", mlt_image_rgb24a);
287     m_producer.attach(m_convert);
288     m_producer.seek(startframe);
289     Mlt::Frame * m_frame = m_producer.get_frame();
290     mlt_image_format format = mlt_image_rgb24a;
291     width = width - 2;
292     height = height - 2;
293
294     if (m_frame && m_frame->is_valid()) {
295         uint8_t *thumb = m_frame->get_image(format, width, height);
296         QImage tmpimage(thumb, width, height, 32, NULL, 0, QImage::IgnoreEndian);
297         if (!tmpimage.isNull()) bitBlt(&image, 1, 1, &tmpimage, 0, 0, width - 2, height - 2);
298     }
299     if (m_frame) delete m_frame;
300     emit thumbReady(startframe, image);
301
302     image.fill(Qt::black);
303     m_producer.seek(endframe);
304     m_frame = m_producer.get_frame();
305
306     if (m_frame && m_frame->is_valid()) {
307         uint8_t *thumb = m_frame->get_image(format, width, height);
308         QImage tmpimage(thumb, width, height, 32, NULL, 0, QImage::IgnoreEndian);
309         if (!tmpimage.isNull()) bitBlt(&image, 1, 1, &tmpimage, 0, 0, width - 2, height - 2);
310     }
311     if (m_frame) delete m_frame;
312     emit thumbReady(endframe, image);
313 }
314 */
315 void KThumb::stopAudioThumbs()
316 {
317         if (thumbProducer.isRunning ()) thumbProducer.stop_me = true;
318 }
319
320
321 void KThumb::removeAudioThumb()
322 {
323         if (m_thumbFile.isEmpty()) return;
324         stopAudioThumbs();
325         QFile f(m_thumbFile);
326         f.remove();
327 }
328
329 void KThumb::getAudioThumbs(int channel, double frame, double frameLength, int arrayWidth){
330         
331         if ((thumbProducer.isRunning() && thumbProducer.isWorking()) || channel == 0) {
332             return;
333         }
334         
335         QMap <int, QMap <int, QByteArray> > storeIn;
336        //FIXME: Hardcoded!!! 
337         int m_frequency = 48000;
338         int m_channels = channel;
339         
340         QFile f(m_thumbFile);
341         if (f.open( QIODevice::ReadOnly )) {
342                 QByteArray channelarray = f.readAll();
343                 f.close();
344                 if (channelarray.size() != arrayWidth*(frame+frameLength)*m_channels) {
345                         kDebug()<<"--- BROKEN THUMB FOR: "<<m_url.fileName()<<" ---------------------- "<<endl;
346                         f.remove();
347                         return;
348                 }
349                 kDebug() << "reading audio thumbs from file";
350                 for (int z=(int) frame;z<(int) (frame+frameLength);z++) {
351                         for (int c=0;c< m_channels;c++){
352                                 QByteArray m_array(arrayWidth,'\x00');
353                                 for (int i = 0; i < arrayWidth; i++)
354                                         m_array[i] = channelarray[z*arrayWidth*m_channels + c*arrayWidth + i];
355                                 storeIn[z][c] = m_array;
356                         }
357                 }
358                 emit audioThumbReady(storeIn);
359         }
360         else {
361                 if (thumbProducer.isRunning()) return;
362                 thumbProducer.init(this, m_url, m_thumbFile, frame, frameLength, m_frequency, m_channels, arrayWidth);
363                 thumbProducer.start(QThread::LowestPriority );
364                 kDebug() << "STARTING GENERATE THMB FOR: "<<m_url<<" ................................";
365         }
366 }
367
368 void KThumb::customEvent ( QEvent * event ){
369         if (event->type()==10005){
370                 ProgressEvent* p=(ProgressEvent*) event;
371                 m_clipManager->setThumbsProgress(m_url, p->value());
372         }
373 }
374
375
376
377 #include "kthumb.moc"
378