1 /***************************************************************************
2 mltdevicecapture.cpp - description
4 begin : Sun May 21 2011
5 copyright : (C) 2011 by Jean-Baptiste Mardelle (jb@kdenlive.org)
7 ***************************************************************************/
9 /***************************************************************************
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
16 ***************************************************************************/
19 #include "mltdevicecapture.h"
20 #include "kdenlivesettings.h"
21 #include "definitions.h"
22 //#include "recmonitor.h"
23 //#include "renderer.h"
24 #include "blackmagic/devices.h"
26 #include <mlt++/Mlt.h>
29 #include <KStandardDirs>
30 #include <KMessageBox>
32 #include <KTemporaryFile>
37 #include <QApplication>
46 static void consumer_gl_frame_show(mlt_consumer, MltDeviceCapture * self, mlt_frame frame_ptr)
48 // detect if the producer has finished playing. Is there a better way to do it?
49 Mlt::Frame frame(frame_ptr);
50 self->showFrame(frame);
53 static void rec_consumer_frame_show(mlt_consumer, MltDeviceCapture * self, mlt_frame frame_ptr)
55 Mlt::Frame frame(frame_ptr);
56 if (!frame.is_valid()) return;
57 self->gotCapturedFrame(frame);
60 static void rec_consumer_frame_preview(mlt_consumer, MltDeviceCapture * self, mlt_frame frame_ptr)
62 Mlt::Frame frame(frame_ptr);
63 if (!frame.is_valid()) return;
64 if (self->sendFrameForAnalysis && frame_ptr->convert_image) {
65 self->emitFrameUpdated(frame);
67 if (self->doCapture) {
68 self->doCapture = false;
69 self->saveFrame(frame);
72 /* if (self->analyseAudio) {
73 self->showAudio(frame);
75 if (frame.get_double("_speed") == 0.0) {
76 self->emitConsumerStopped();
77 } else if (frame.get_double("_speed") < 0.0 && mlt_frame_get_position(frame_ptr) <= 0) {
79 self->emitConsumerStopped();
84 MltDeviceCapture::MltDeviceCapture(QString profile, VideoPreviewContainer *surface, QWidget *parent) :
85 AbstractRender(parent),
87 sendFrameForAnalysis(false),
91 m_captureDisplayWidget(surface),
92 m_winid((int) surface->winId()),
93 m_analyseAudio(KdenliveSettings::monitor_audio())
95 if (profile.isEmpty()) profile = KdenliveSettings::current_profile();
96 buildConsumer(profile);
99 MltDeviceCapture::~MltDeviceCapture()
101 if (m_mltConsumer) delete m_mltConsumer;
102 if (m_mltProducer) delete m_mltProducer;
103 if (m_mltProfile) delete m_mltProfile;
106 void MltDeviceCapture::buildConsumer(const QString &profileName)
108 if (!profileName.isEmpty()) m_activeProfile = profileName;
110 if (m_mltProfile) delete m_mltProfile;
112 char *tmp = qstrdup(m_activeProfile.toUtf8().constData());
113 setenv("MLT_PROFILE", tmp, 1);
114 m_mltProfile = new Mlt::Profile(tmp);
115 m_mltProfile->get_profile()->is_explicit = 1;
118 QString videoDriver = KdenliveSettings::videodrivername();
119 if (!videoDriver.isEmpty()) {
120 if (videoDriver == "x11_noaccel") {
121 setenv("SDL_VIDEO_YUV_HWACCEL", "0", 1);
124 unsetenv("SDL_VIDEO_YUV_HWACCEL");
127 setenv("SDL_VIDEO_ALLOW_SCREENSAVER", "1", 1);
131 m_mltConsumer = new Mlt::Consumer(*m_mltProfile, "sdl_audio");
132 m_mltConsumer->set("preview_off", 1);
133 m_mltConsumer->set("preview_format", mlt_image_rgb24a);
134 m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) consumer_gl_frame_show);
136 m_mltConsumer = new Mlt::Consumer(*m_mltProfile, "sdl_preview");
137 m_mltConsumer->set("window_id", m_winid);
139 m_mltConsumer->set("resize", 1);
140 //m_mltConsumer->set("terminate_on_pause", 1);
141 m_mltConsumer->set("window_background", KdenliveSettings::window_background().name().toUtf8().constData());
142 m_mltConsumer->set("rescale", "nearest");
144 m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) rec_consumer_frame_preview);
146 QString audioDevice = KdenliveSettings::audiodevicename();
147 if (!audioDevice.isEmpty())
148 m_mltConsumer->set("audio_device", audioDevice.toUtf8().constData());
150 if (!videoDriver.isEmpty())
151 m_mltConsumer->set("video_driver", videoDriver.toUtf8().constData());
153 QString audioDriver = KdenliveSettings::audiodrivername();
155 if (!audioDriver.isEmpty())
156 m_mltConsumer->set("audio_driver", audioDriver.toUtf8().constData());
158 //m_mltConsumer->set("progressive", 1);
159 //m_mltConsumer->set("buffer", 1);
160 //m_mltConsumer->set("real_time", 0);
163 void MltDeviceCapture::stop()
166 m_mltConsumer->stop();
167 //if (!m_mltConsumer->is_stopped()) m_mltConsumer->stop();
168 delete m_mltConsumer;
169 m_mltConsumer = NULL;
171 kDebug()<<"STOPPING cap";
173 QList <Mlt::Producer *> prods;
174 Mlt::Service service(m_mltProducer->parent().get_service());
175 mlt_service_lock(service.get_service());
176 kDebug()<<"STOPPING cap 2";
177 if (service.type() == tractor_type) {
178 kDebug()<<"STOPPING cap 3";
179 Mlt::Tractor tractor(service);
180 mlt_tractor_close(tractor.get_tractor());
181 Mlt::Field *field = tractor.field();
182 mlt_service nextservice = mlt_service_get_producer(service.get_service());
183 mlt_service nextservicetodisconnect;
184 mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
185 QString mlt_type = mlt_properties_get(properties, "mlt_type");
186 QString resource = mlt_properties_get(properties, "mlt_service");
187 // Delete all transitions
188 while (mlt_type == "transition") {
189 nextservicetodisconnect = nextservice;
190 nextservice = mlt_service_producer(nextservice);
191 mlt_field_disconnect_service(field->get_field(), nextservicetodisconnect);
192 if (nextservice == NULL) break;
193 properties = MLT_SERVICE_PROPERTIES(nextservice);
194 mlt_type = mlt_properties_get(properties, "mlt_type");
195 resource = mlt_properties_get(properties, "mlt_service");
197 for (int trackNb = tractor.count() - 1; trackNb >= 0; --trackNb) {
198 Mlt::Producer trackProducer(tractor.track(trackNb));
199 Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
200 if (trackPlaylist.type() == playlist_type) {
201 for (int i = 0; i < trackPlaylist.count();i++) {
202 // We need to manually decrease the ref count and close the producer, otherwise
203 // the video4linux device stays open, seems like a bug in MLT that is not cleaning properly
204 mlt_properties props = MLT_PRODUCER_PROPERTIES(trackPlaylist.get_clip(i)->get_parent());
205 while (mlt_properties_ref_count(props) > 0) mlt_properties_dec_ref(props);
206 mlt_producer_close(trackPlaylist.get_clip(i)->get_parent());
208 mlt_playlist_close(trackPlaylist.get_playlist());
209 //trackPlaylist.clear();
215 mlt_service_unlock(service.get_service());
216 delete m_mltProducer;
217 kDebug()<<"/// STOP REC PROD";
218 m_mltProducer = NULL;
223 void MltDeviceCapture::doRefresh()
225 if (m_mltConsumer) m_mltConsumer->set("refresh", 1);
229 void MltDeviceCapture::emitFrameUpdated(Mlt::Frame& frame)
231 mlt_image_format format = mlt_image_rgb24a;
234 const uchar* image = frame.get_image(format, width, height);
235 QImage qimage(width, height, QImage::Format_ARGB32);
236 memcpy(qimage.bits(), image, width * height * 4);
237 emit frameUpdated(qimage.rgbSwapped());
240 void MltDeviceCapture::showFrame(Mlt::Frame& frame)
242 mlt_image_format format = mlt_image_rgb24a;
245 const uchar* image = frame.get_image(format, width, height);
246 QImage qimage(width, height, QImage::Format_ARGB32_Premultiplied);
247 memcpy(qimage.scanLine(0), image, width * height * 4);
248 emit showImageSignal(qimage);
249 if (m_analyseAudio) showAudio(frame);
250 if (sendFrameForAnalysis && frame.get_frame()->convert_image) {
251 emit frameUpdated(qimage.rgbSwapped());
255 void MltDeviceCapture::showAudio(Mlt::Frame& frame)
257 if (!frame.is_valid() || frame.get_int("test_audio") != 0) {
260 mlt_audio_format audio_format = mlt_audio_s16;
262 int num_channels = 0;
264 int16_t* data = (int16_t*)frame.get_audio(audio_format, freq, num_channels, samples);
270 // Data format: [ c00 c10 c01 c11 c02 c12 c03 c13 ... c0{samples-1} c1{samples-1} for 2 channels.
271 // So the vector is of size samples*channels.
272 QVector<int16_t> sampleVector(samples*num_channels);
273 memcpy(sampleVector.data(), data, samples*num_channels*sizeof(int16_t));
276 emit audioSamplesSignal(sampleVector, freq, num_channels, samples);
280 bool MltDeviceCapture::slotStartPreview(const QString &producer)
283 if (m_mltConsumer == NULL) buildConsumer();
284 /*if (m_mltConsumer) delete m_mltConsumer;
285 if (m_mltProducer) delete m_mltProducer;
286 if (m_mltProfile) delete m_mltProfile;
288 char *tmp = qstrdup(m_activeProfile.toUtf8().constData());
289 setenv("MLT_PROFILE", tmp, 1);
290 m_mltProfile = new Mlt::Profile(tmp);
292 m_mltProfile->get_profile()->is_explicit = 1;
294 m_mltConsumer = new Mlt::Consumer(*m_mltProfile, "sdl_preview");
296 m_mltConsumer->set("window_id", m_winid);
297 m_mltConsumer->set("real_time", 0);
298 // m_mltConsumer->set("buffer", 1);
299 m_mltConsumer->set("resize", 1);
300 m_mltConsumer->set("progressive", 1);
301 m_mltConsumer->set("rescale", "nearest");*/
303 //char *tmp = qstrdup(QString("avformat-novalidate:video4linux2:%1?frame_rate:%2&width:%3&height:%4").arg(KdenliveSettings::video4vdevice()).arg(m_mltProfile->fps()).arg(m_mltProfile->width()).arg(m_mltProfile->height()).toUtf8().constData());
305 char *tmp = qstrdup(producer.toUtf8().constData());
307 m_mltProducer = new Mlt::Producer(*m_mltProfile, tmp);
309 if (m_mltProducer == NULL || !m_mltProducer->is_valid()) {
310 kDebug()<<"//// ERROR CREATRING PROD";
313 m_mltConsumer->connect(*m_mltProducer);
314 if (m_mltConsumer->start() == -1) {
315 delete m_mltConsumer;
316 m_mltConsumer = NULL;
322 void MltDeviceCapture::gotCapturedFrame(Mlt::Frame& frame)
324 mlt_image_format format = mlt_image_rgb24a;
327 const uchar* image = frame.get_image(format, width, height);
328 QImage qimage(width, height, QImage::Format_ARGB32);
329 memcpy(qimage.bits(), image, width * height * 4);
330 m_captureDisplayWidget->setImage(qimage.rgbSwapped());
333 void MltDeviceCapture::saveFrame(Mlt::Frame& frame)
335 mlt_image_format format = mlt_image_rgb24a;
338 const uchar* image = frame.get_image(format, width, height);
339 QImage qimage(width, height, QImage::Format_ARGB32);
340 memcpy(qimage.bits(), image, width * height * 4);
341 qimage.rgbSwapped().save(m_capturePath);
342 emit frameSaved(m_capturePath);
343 m_capturePath.clear();
346 void MltDeviceCapture::captureFrame(const QString &path)
348 m_capturePath = path;
352 bool MltDeviceCapture::slotStartCapture(const QString ¶ms, const QString &path, const QString &playlist)
355 if (m_mltProfile) delete m_mltProfile;
356 char *tmp = qstrdup(m_activeProfile.toUtf8().constData());
357 m_mltProfile = new Mlt::Profile(tmp);
359 m_mltProfile->get_profile()->is_explicit = 1;
360 kDebug()<<"-- CREATING CAP: "<<params<<", PATH: "<<path;
361 tmp = qstrdup(QString("avformat:" + path).toUtf8().constData());
362 m_mltConsumer = new Mlt::Consumer(*m_mltProfile, tmp);
363 m_mltConsumer->set("terminate_on_pause", 1);
366 QStringList paramList = params.split(" ", QString::SkipEmptyParts);
368 for (int i = 0; i < paramList.count(); i++) {
369 tmp = qstrdup(paramList.at(i).section("=", 0, 0).toUtf8().constData());
370 tmp2 = qstrdup(paramList.at(i).section("=", 1, 1).toUtf8().constData());
371 m_mltConsumer->set(tmp, tmp2);
376 if (m_mltConsumer == NULL || !m_mltConsumer->is_valid()) {
378 delete m_mltConsumer;
379 m_mltConsumer = NULL;
384 // FIXME: the event object returned by the listen gets leaked...
385 m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) rec_consumer_frame_show);
387 //tmp = qstrdup(QString("avformat-novalidate:video4linux2:%1?frame_rate:%2&width:%3&height:%4").arg(KdenliveSettings::video4vdevice()).arg(m_mltProfile->fps()).arg(m_mltProfile->width()).arg(m_mltProfile->height()).toUtf8().constData());
388 tmp = qstrdup(playlist.toUtf8().constData());
389 m_mltProducer = new Mlt::Producer(*m_mltProfile, "xml-string", tmp);
392 if (m_mltProducer == NULL || !m_mltProducer->is_valid()) {
393 kDebug()<<"//// ERROR CREATRING PROD";
397 m_mltConsumer->connect(*m_mltProducer);
398 if (m_mltConsumer->start() == -1) {
399 delete m_mltConsumer;
400 m_mltConsumer = NULL;