diff --git a/src/capture/managecapturesdialog.h b/src/capture/managecapturesdialog.h index f7ca599bb..35ef423ab 100644 --- a/src/capture/managecapturesdialog.h +++ b/src/capture/managecapturesdialog.h @@ -1,49 +1,49 @@ /*************************************************************************** * Copyright (C) 2008 by Jean-Baptiste Mardelle (jb@kdenlive.org) * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ #ifndef MANAGECAPTURESDIALOG_H #define MANAGECAPTURESDIALOG_H #include #include #include "ui_managecaptures_ui.h" class ManageCapturesDialog : public QDialog { Q_OBJECT public: explicit ManageCapturesDialog(const QList &files, QWidget *parent = nullptr); ~ManageCapturesDialog() override; QList importFiles() const; private slots: void slotRefreshButtons(); void slotDeleteCurrent(); void slotToggle(); void slotCheckItemIcon(); private: - Ui::ManageCaptures_UI m_view; + Ui::ManageCaptures_UI m_view{}; QPushButton *m_importButton; }; #endif diff --git a/src/capture/mediacapture.cpp b/src/capture/mediacapture.cpp index cb5198d5c..407b046a7 100644 --- a/src/capture/mediacapture.cpp +++ b/src/capture/mediacapture.cpp @@ -1,225 +1,268 @@ /* Copyright (C) 2019 Akhil K Gangadharan This file is part of Kdenlive. See www.kdenlive.org. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "mediacapture.h" #include +#include +#include MediaCapture::MediaCapture(QObject *parent) : QObject(parent) , m_volume(1.) { - m_probe.reset(new QAudioProbe(this)); + m_probe = std::make_unique(this); connect(m_probe.get(), &QAudioProbe::audioBufferProbed, this, &MediaCapture::processBuffer); } -MediaCapture::~MediaCapture() {} +MediaCapture::~MediaCapture() = default; void MediaCapture::displayErrorMessage() { qDebug() << " !!!!!!!!!!!!!!!! ERROR : QMediarecorder - Capture failed"; } void MediaCapture::recordAudio(bool record) { if (!m_audioRecorder) { - m_audioRecorder.reset(new QAudioRecorder(this)); + m_audioRecorder = std::make_unique(this); } m_probe->setSource(m_audioRecorder.get()); if (record && m_audioRecorder->state() == QMediaRecorder::StoppedState) { m_audioRecorder->setAudioInput(m_audioDevice); m_audioRecorder->setVolume(m_volume); m_audioRecorder->setOutputLocation(m_path); connect(m_audioRecorder.get(), SIGNAL(error(QMediaRecorder::Error)), this, SLOT(displayErrorMessage())); QAudioEncoderSettings audioSettings; audioSettings.setBitRate(48000); QString container = "audio/x-wav"; m_audioRecorder->setEncodingSettings(audioSettings, QVideoEncoderSettings(), container); m_audioRecorder->record(); } else if (m_audioRecorder->state() != QMediaRecorder::PausedState) { m_audioRecorder->stop(); m_audioRecorder.reset(); } else { m_audioRecorder->record(); } } void MediaCapture::recordVideo(bool record) { // TO DO - fix video capture if (!m_videoRecorder) { QList availableCameras = QCameraInfo::availableCameras(); foreach (const QCameraInfo &cameraInfo, availableCameras) { if (cameraInfo == QCameraInfo::defaultCamera()) { - m_camera.reset(new QCamera(cameraInfo, this)); + m_camera = std::make_unique(cameraInfo, this); break; } } - m_videoRecorder.reset(new QMediaRecorder(m_camera.get(), this)); + m_videoRecorder = std::make_unique(m_camera.get(), this); } if (record && m_videoRecorder->state() == QMediaRecorder::StoppedState) { connect(m_videoRecorder.get(), SIGNAL(error(QMediaRecorder::Error)), this, SLOT(displayErrorMessage())); m_videoRecorder->setOutputLocation(m_path); m_camera->setCaptureMode(QCamera::CaptureVideo); m_camera->start(); // QString container = "video/mpeg"; // By default, Qt chooses appropriate parameters m_videoRecorder->record(); } else { m_videoRecorder->stop(); m_camera->stop(); m_videoRecorder.reset(); m_camera.reset(); } } void MediaCapture::setCaptureOutputLocation(QUrl path) { - m_path = path; + m_path = std::move(path); } QStringList MediaCapture::getAudioCaptureDevices() { - m_audioRecorder.reset(new QAudioRecorder(this)); + m_audioRecorder = std::make_unique(this); QStringList audioDevices = m_audioRecorder->audioInputs(); m_audioRecorder.reset(); return audioDevices; } void MediaCapture::setAudioCaptureDevice(QString audioDevice) { - m_audioDevice = audioDevice; + m_audioDevice = std::move(audioDevice); } void MediaCapture::setAudioVolume(qreal volume) { m_volume = volume; } int MediaCapture::getState() { - if (m_audioRecorder.get() != nullptr) { + if (m_audioRecorder != nullptr) { return m_audioRecorder->state(); - } else if (m_videoRecorder.get() != nullptr) { + } + if (m_videoRecorder != nullptr) { return m_videoRecorder->state(); } return -1; } template QVector getBufferLevels(const T *buffer, int frames, int channels) { QVector max_values; max_values.fill(0, channels); for (int i = 0; i < frames; ++i) { for (int j = 0; j < channels; ++j) { qreal value = qAbs(qreal(buffer[i * channels + j])); - if (value > max_values.at(j)) max_values.replace(j, value); + if (value > max_values.at(j)) { + max_values.replace(j, value); + } } } return max_values; } // This function returns the maximum possible sample value for a given audio format qreal getPeakValue(const QAudioFormat &format) { // Note: Only the most common sample formats are supported - if (!format.isValid()) return qreal(0); + if (!format.isValid()) { + return qreal(0); + } - if (format.codec() != "audio/pcm") return qreal(0); + if (format.codec() != "audio/pcm") { + return qreal(0); + } switch (format.sampleType()) { case QAudioFormat::Unknown: break; case QAudioFormat::Float: - if (format.sampleSize() != 32) // other sample formats are not supported + if (format.sampleSize() != 32) { // other sample formats are not supported return qreal(0); + } return qreal(1.00003); case QAudioFormat::SignedInt: - if (format.sampleSize() == 32) return qreal(INT_MAX); - if (format.sampleSize() == 16) return qreal(SHRT_MAX); - if (format.sampleSize() == 8) return qreal(CHAR_MAX); + if (format.sampleSize() == 32) { + return qreal(INT_MAX); + } + if (format.sampleSize() == 16) { + return qreal(SHRT_MAX); + } + if (format.sampleSize() == 8) { + return qreal(CHAR_MAX); + } break; case QAudioFormat::UnSignedInt: - if (format.sampleSize() == 32) return qreal(UINT_MAX); - if (format.sampleSize() == 16) return qreal(USHRT_MAX); - if (format.sampleSize() == 8) return qreal(UCHAR_MAX); + if (format.sampleSize() == 32) { + return qreal(UINT_MAX); + } + if (format.sampleSize() == 16) { + return qreal(USHRT_MAX); + } + if (format.sampleSize() == 8) { + return qreal(UCHAR_MAX); + } break; } return qreal(0); } QVector getBufferLevels(const QAudioBuffer &buffer) { QVector values; - if (!buffer.format().isValid() || buffer.format().byteOrder() != QAudioFormat::LittleEndian) return values; + if (!buffer.format().isValid() || buffer.format().byteOrder() != QAudioFormat::LittleEndian) { + return values; + } - if (buffer.format().codec() != "audio/pcm") return values; + if (buffer.format().codec() != "audio/pcm") { + return values; + } int channelCount = buffer.format().channelCount(); values.fill(0, channelCount); qreal peak_value = getPeakValue(buffer.format()); - if (qFuzzyCompare(peak_value, qreal(0))) return values; + if (qFuzzyCompare(peak_value, qreal(0))) { + return values; + } switch (buffer.format().sampleType()) { case QAudioFormat::Unknown: case QAudioFormat::UnSignedInt: - if (buffer.format().sampleSize() == 32) values = getBufferLevels(buffer.constData(), buffer.frameCount(), channelCount); - if (buffer.format().sampleSize() == 16) values = getBufferLevels(buffer.constData(), buffer.frameCount(), channelCount); - if (buffer.format().sampleSize() == 8) values = getBufferLevels(buffer.constData(), buffer.frameCount(), channelCount); - for (int i = 0; i < values.size(); ++i) - values[i] = qAbs(values.at(i) - peak_value / 2) / (peak_value / 2); + if (buffer.format().sampleSize() == 32) { + values = getBufferLevels(buffer.constData(), buffer.frameCount(), channelCount); + } + if (buffer.format().sampleSize() == 16) { + values = getBufferLevels(buffer.constData(), buffer.frameCount(), channelCount); + } + if (buffer.format().sampleSize() == 8) { + values = getBufferLevels(buffer.constData(), buffer.frameCount(), channelCount); + } + for (double &value : values) { + value = qAbs(value - peak_value / 2) / (peak_value / 2); + } break; case QAudioFormat::Float: if (buffer.format().sampleSize() == 32) { values = getBufferLevels(buffer.constData(), buffer.frameCount(), channelCount); - for (int i = 0; i < values.size(); ++i) - values[i] /= peak_value; + for (double &value : values) { + value /= peak_value; + } } break; case QAudioFormat::SignedInt: - if (buffer.format().sampleSize() == 32) values = getBufferLevels(buffer.constData(), buffer.frameCount(), channelCount); - if (buffer.format().sampleSize() == 16) values = getBufferLevels(buffer.constData(), buffer.frameCount(), channelCount); - if (buffer.format().sampleSize() == 8) values = getBufferLevels(buffer.constData(), buffer.frameCount(), channelCount); - for (int i = 0; i < values.size(); ++i) - values[i] /= peak_value; + if (buffer.format().sampleSize() == 32) { + values = getBufferLevels(buffer.constData(), buffer.frameCount(), channelCount); + } + if (buffer.format().sampleSize() == 16) { + values = getBufferLevels(buffer.constData(), buffer.frameCount(), channelCount); + } + if (buffer.format().sampleSize() == 8) { + values = getBufferLevels(buffer.constData(), buffer.frameCount(), channelCount); + } + for (double &value : values) { + value /= peak_value; + } break; } return values; } void MediaCapture::processBuffer(const QAudioBuffer &buffer) { m_levels = getBufferLevels(buffer); emit levelsChanged(); } QVector MediaCapture::levels() const { return m_levels; } diff --git a/src/capture/mediacapture.h b/src/capture/mediacapture.h index 39d297d35..f7e9b6aa7 100644 --- a/src/capture/mediacapture.h +++ b/src/capture/mediacapture.h @@ -1,81 +1,81 @@ /* Copyright (C) 2019 Akhil K Gangadharan This file is part of Kdenlive. See www.kdenlive.org. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef MEDIACAPTURE_H #define MEDIACAPTURE_H #include #include #include #include #include #include #include #include #include #include class QAudioRecorder; class QAudioProbe; class MediaCapture : public QObject { Q_OBJECT Q_PROPERTY(QVector levels READ levels NOTIFY levelsChanged) public: MediaCapture(QObject *parent); - ~MediaCapture(); - void recordAudio(bool); - void recordVideo(bool); + ~MediaCapture() override; + void recordAudio(bool /*record*/); + void recordVideo(bool /*record*/); /** @brief Sets m_path to selected output location **/ - void setCaptureOutputLocation(QUrl); + void setCaptureOutputLocation(QUrl /*path*/); /** @brief Sets m_device to selected audio capture device **/ - void setAudioCaptureDevice(QString); + void setAudioCaptureDevice(QString /*audioDevice*/); /** @brief Sets m_volume to selected audio capture volume **/ - void setAudioVolume(qreal); + void setAudioVolume(qreal /*volume*/); /** @brief Returns list of audio devices available for capture **/ QStringList getAudioCaptureDevices(); /** @brief Returns QMediaRecorder::State value **/ int getState(); Q_INVOKABLE QVector levels() const; public slots: void displayErrorMessage(); private: std::unique_ptr m_audioRecorder; std::unique_ptr m_videoRecorder; std::unique_ptr m_camera; std::unique_ptr m_probe; QString m_audioDevice; qreal m_volume; QUrl m_path; QVector m_levels; private slots: void processBuffer(const QAudioBuffer &buffer); signals: void levelsChanged(); }; #endif diff --git a/src/capture/mltdevicecapture.cpp b/src/capture/mltdevicecapture.cpp index b83892705..df6fd6b1b 100644 --- a/src/capture/mltdevicecapture.cpp +++ b/src/capture/mltdevicecapture.cpp @@ -1,689 +1,689 @@ /*************************************************************************** mltdevicecapture.cpp - description ------------------- begin : Sun May 21 2011 copyright : (C) 2011 by Jean-Baptiste Mardelle (jb@kdenlive.org) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "mltdevicecapture.h" #include "definitions.h" #include "kdenlivesettings.h" #include #include "kdenlive_debug.h" #include #include #include #include #include -static void consumer_gl_frame_show(mlt_consumer, MltDeviceCapture *self, mlt_frame frame_ptr) +static void consumer_gl_frame_show(mlt_consumer /*unused*/, MltDeviceCapture *self, mlt_frame frame_ptr) { // detect if the producer has finished playing. Is there a better way to do it? Mlt::Frame frame(frame_ptr); self->showFrame(frame); } MltDeviceCapture::MltDeviceCapture(const QString &profile, /*VideoSurface *surface, */ QWidget *parent) : AbstractRender(Kdenlive::RecordMonitor, parent) , doCapture(0) , processingImage(false) , m_mltConsumer(nullptr) , m_mltProducer(nullptr) , m_mltProfile(nullptr) , m_showFrameEvent(nullptr) , m_droppedFrames(0) , m_livePreview(KdenliveSettings::enable_recording_preview()) { analyseAudio = KdenliveSettings::monitor_audio(); if (profile.isEmpty()) { // profile = KdenliveSettings::current_profile(); } buildConsumer(profile); connect(this, &MltDeviceCapture::unblockPreview, this, &MltDeviceCapture::slotPreparePreview); m_droppedFramesTimer.setSingleShot(false); m_droppedFramesTimer.setInterval(1000); connect(&m_droppedFramesTimer, &QTimer::timeout, this, &MltDeviceCapture::slotCheckDroppedFrames); } MltDeviceCapture::~MltDeviceCapture() { delete m_mltConsumer; delete m_mltProducer; delete m_mltProfile; } bool MltDeviceCapture::buildConsumer(const QString &profileName) { if (!profileName.isEmpty()) { m_activeProfile = profileName; } delete m_mltProfile; char *tmp = qstrdup(m_activeProfile.toUtf8().constData()); qputenv("MLT_PROFILE", tmp); m_mltProfile = new Mlt::Profile(tmp); m_mltProfile->set_explicit(1); delete[] tmp; QString videoDriver = KdenliveSettings::videodrivername(); if (!videoDriver.isEmpty()) { if (videoDriver == QLatin1String("x11_noaccel")) { qputenv("SDL_VIDEO_YUV_HWACCEL", "0"); videoDriver = QStringLiteral("x11"); } else { qunsetenv("SDL_VIDEO_YUV_HWACCEL"); } } qputenv("SDL_VIDEO_ALLOW_SCREENSAVER", "1"); // OpenGL monitor m_mltConsumer = new Mlt::Consumer(*m_mltProfile, KdenliveSettings::audiobackend().toUtf8().constData()); m_mltConsumer->set("preview_off", 1); m_mltConsumer->set("preview_format", mlt_image_rgb24); m_showFrameEvent = m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener)consumer_gl_frame_show); // m_mltConsumer->set("resize", 1); // m_mltConsumer->set("terminate_on_pause", 1); m_mltConsumer->set("window_background", KdenliveSettings::window_background().name().toUtf8().constData()); // m_mltConsumer->set("rescale", "nearest"); QString audioDevice = KdenliveSettings::audiodevicename(); if (!audioDevice.isEmpty()) { m_mltConsumer->set("audio_device", audioDevice.toUtf8().constData()); } if (!videoDriver.isEmpty()) { m_mltConsumer->set("video_driver", videoDriver.toUtf8().constData()); } QString audioDriver = KdenliveSettings::audiodrivername(); if (!audioDriver.isEmpty()) { m_mltConsumer->set("audio_driver", audioDriver.toUtf8().constData()); } // m_mltConsumer->set("progressive", 0); // m_mltConsumer->set("buffer", 1); // m_mltConsumer->set("real_time", 0); if (!m_mltConsumer->is_valid()) { delete m_mltConsumer; m_mltConsumer = nullptr; return false; } return true; } void MltDeviceCapture::pause() { if (m_mltConsumer) { m_mltConsumer->set("refresh", 0); // m_mltProducer->set_speed(0.0); m_mltConsumer->purge(); } } void MltDeviceCapture::stop() { m_droppedFramesTimer.stop(); bool isPlaylist = false; // disconnect(this, SIGNAL(imageReady(QImage)), this, SIGNAL(frameUpdated(QImage))); // m_captureDisplayWidget->stop(); delete m_showFrameEvent; m_showFrameEvent = nullptr; if (m_mltConsumer) { m_mltConsumer->set("refresh", 0); m_mltConsumer->purge(); m_mltConsumer->stop(); // if (!m_mltConsumer->is_stopped()) m_mltConsumer->stop(); } if (m_mltProducer) { QList prods; Mlt::Service service(m_mltProducer->parent().get_service()); mlt_service_lock(service.get_service()); if (service.type() == tractor_type) { isPlaylist = true; Mlt::Tractor tractor(service); mlt_tractor_close(tractor.get_tractor()); Mlt::Field *field = tractor.field(); mlt_service nextservice = mlt_service_get_producer(service.get_service()); mlt_service nextservicetodisconnect; mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice); QString mlt_type = mlt_properties_get(properties, "mlt_type"); QString resource = mlt_properties_get(properties, "mlt_service"); // Delete all transitions while (mlt_type == QLatin1String("transition")) { nextservicetodisconnect = nextservice; nextservice = mlt_service_producer(nextservice); mlt_field_disconnect_service(field->get_field(), nextservicetodisconnect); if (nextservice == nullptr) { break; } properties = MLT_SERVICE_PROPERTIES(nextservice); mlt_type = mlt_properties_get(properties, "mlt_type"); resource = mlt_properties_get(properties, "mlt_service"); } delete field; field = nullptr; } mlt_service_unlock(service.get_service()); delete m_mltProducer; m_mltProducer = nullptr; } // For some reason, the consumer seems to be deleted by previous stuff when in playlist mode if (!isPlaylist && (m_mltConsumer != nullptr)) { delete m_mltConsumer; } m_mltConsumer = nullptr; } void MltDeviceCapture::emitFrameUpdated(Mlt::Frame &frame) { /* //TEST: is it better to convert the frame in a thread outside of MLT?? if (processingImage) return; mlt_image_format format = (mlt_image_format) frame.get_int("format"); //mlt_image_rgb24; int width = frame.get_int("width"); int height = frame.get_int("height"); unsigned char *buffer = (unsigned char *) frame.get_data("image"); if (format == mlt_image_yuv422) { QtConcurrent::run(this, &MltDeviceCapture::uyvy2rgb, (unsigned char *) buffer, width, height); } */ mlt_image_format format = mlt_image_rgb24; int width = 0; int height = 0; const uchar *image = frame.get_image(format, width, height); QImage qimage(width, height, QImage::Format_RGB888); // QImage qimage(width, height, QImage::Format_ARGB32_Premultiplied); memcpy(qimage.bits(), image, (size_t)(width * height * 3)); emit frameUpdated(qimage); } void MltDeviceCapture::showFrame(Mlt::Frame &frame) { mlt_image_format format = mlt_image_rgb24; int width = 0; int height = 0; const uchar *image = frame.get_image(format, width, height); QImage qimage(width, height, QImage::Format_RGB888); memcpy(qimage.scanLine(0), image, static_cast(width * height * 3)); emit showImageSignal(qimage); if (sendFrameForAnalysis && (frame.get_frame()->convert_image != nullptr)) { emit frameUpdated(qimage.rgbSwapped()); } } void MltDeviceCapture::showAudio(Mlt::Frame &frame) { if (!frame.is_valid() || frame.get_int("test_audio") != 0) { return; } mlt_audio_format audio_format = mlt_audio_s16; int freq = 0; int num_channels = 0; int samples = 0; auto *data = (qint16 *)frame.get_audio(audio_format, freq, num_channels, samples); if (!data) { return; } // Data format: [ c00 c10 c01 c11 c02 c12 c03 c13 ... c0{samples-1} c1{samples-1} for 2 channels. // So the vector is of size samples*channels. audioShortVector sampleVector(samples * num_channels); memcpy(sampleVector.data(), data, (size_t)(samples * num_channels) * sizeof(qint16)); if (samples > 0) { emit audioSamplesSignal(sampleVector, freq, num_channels, samples); } } bool MltDeviceCapture::slotStartPreview(const QString &producer, bool xmlFormat) { if (m_mltConsumer == nullptr) { if (!buildConsumer()) { return false; } } char *tmp = qstrdup(producer.toUtf8().constData()); if (xmlFormat) { m_mltProducer = new Mlt::Producer(*m_mltProfile, "xml-string", tmp); } else { m_mltProducer = new Mlt::Producer(*m_mltProfile, tmp); } delete[] tmp; if (m_mltProducer == nullptr || !m_mltProducer->is_valid()) { if (m_mltProducer) { delete m_mltProducer; m_mltProducer = nullptr; } // qCDebug(KDENLIVE_LOG)<<"//// ERROR CREATRING PROD"; return false; } m_mltConsumer->connect(*m_mltProducer); if (m_mltConsumer->start() == -1) { delete m_mltConsumer; m_mltConsumer = nullptr; return false; } m_droppedFramesTimer.start(); // connect(this, SIGNAL(imageReady(QImage)), this, SIGNAL(frameUpdated(QImage))); return true; } void MltDeviceCapture::slotCheckDroppedFrames() { if (m_mltProducer) { int dropped = m_mltProducer->get_int("dropped"); if (dropped > m_droppedFrames) { m_droppedFrames = dropped; emit droppedFrames(m_droppedFrames); } } } void MltDeviceCapture::saveFrame(Mlt::Frame &frame) { mlt_image_format format = mlt_image_rgb24; int width = 0; int height = 0; const uchar *image = frame.get_image(format, width, height); QImage qimage(width, height, QImage::Format_RGB888); memcpy(qimage.bits(), image, static_cast(width * height * 3)); // Re-enable overlay Mlt::Service service(m_mltProducer->parent().get_service()); Mlt::Tractor tractor(service); Mlt::Producer trackProducer(tractor.track(0)); trackProducer.set("hide", 0); qimage.save(m_capturePath); emit frameSaved(m_capturePath); m_capturePath.clear(); } void MltDeviceCapture::captureFrame(const QString &path) { if (m_mltProducer == nullptr || !m_mltProducer->is_valid()) { return; } // Hide overlay track before doing the capture Mlt::Service service(m_mltProducer->parent().get_service()); Mlt::Tractor tractor(service); Mlt::Producer trackProducer(tractor.track(0)); mlt_service_lock(service.get_service()); trackProducer.set("hide", 1); m_mltConsumer->purge(); mlt_service_unlock(service.get_service()); m_capturePath = path; // Wait for 5 frames before capture to make sure overlay is gone doCapture = 5; } bool MltDeviceCapture::slotStartCapture(const QString ¶ms, const QString &path, const QString &playlist, bool livePreview, bool xmlPlaylist) { stop(); m_livePreview = livePreview; m_frameCount = 0; m_droppedFrames = 0; delete m_mltProfile; char *tmp = qstrdup(m_activeProfile.toUtf8().constData()); m_mltProfile = new Mlt::Profile(tmp); delete[] tmp; m_mltConsumer = new Mlt::Consumer(*m_mltProfile, "multi"); if (m_mltConsumer == nullptr || !m_mltConsumer->is_valid()) { delete m_mltConsumer; m_mltConsumer = nullptr; return false; } // Create multi consumer setup auto *renderProps = new Mlt::Properties; renderProps->set("mlt_service", "avformat"); renderProps->set("target", path.toUtf8().constData()); renderProps->set("real_time", -KdenliveSettings::mltthreads()); renderProps->set("terminate_on_pause", 0); // was commented out. restoring it fixes mantis#3415 - FFmpeg recording freezes // without this line a call to mlt_properties_get_int(terminate on pause) for in mlt/src/modules/core/consumer_multi.c is returning 1 // and going into and endless loop. renderProps->set("mlt_profile", m_activeProfile.toUtf8().constData()); const QStringList paramList = params.split(' ', QString::SkipEmptyParts); for (int i = 0; i < paramList.count(); ++i) { tmp = qstrdup(paramList.at(i).section(QLatin1Char('='), 0, 0).toUtf8().constData()); QString value = paramList.at(i).section(QLatin1Char('='), 1, 1); if (value == QLatin1String("%threads")) { value = QString::number(QThread::idealThreadCount()); } char *tmp2 = qstrdup(value.toUtf8().constData()); renderProps->set(tmp, tmp2); delete[] tmp; delete[] tmp2; } mlt_properties consumerProperties = m_mltConsumer->get_properties(); mlt_properties_set_data(consumerProperties, "0", renderProps->get_properties(), 0, (mlt_destructor)mlt_properties_close, nullptr); if (m_livePreview) { // user wants live preview auto *previewProps = new Mlt::Properties; QString videoDriver = KdenliveSettings::videodrivername(); if (!videoDriver.isEmpty()) { if (videoDriver == QLatin1String("x11_noaccel")) { qputenv("SDL_VIDEO_YUV_HWACCEL", "0"); videoDriver = QStringLiteral("x11"); } else { qunsetenv("SDL_VIDEO_YUV_HWACCEL"); } } qputenv("SDL_VIDEO_ALLOW_SCREENSAVER", "1"); // OpenGL monitor previewProps->set("mlt_service", KdenliveSettings::audiobackend().toUtf8().constData()); previewProps->set("preview_off", 1); previewProps->set("preview_format", mlt_image_rgb24); previewProps->set("terminate_on_pause", 0); m_showFrameEvent = m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener)consumer_gl_frame_show); // m_mltConsumer->set("resize", 1); previewProps->set("window_background", KdenliveSettings::window_background().name().toUtf8().constData()); QString audioDevice = KdenliveSettings::audiodevicename(); if (!audioDevice.isEmpty()) { previewProps->set("audio_device", audioDevice.toUtf8().constData()); } if (!videoDriver.isEmpty()) { previewProps->set("video_driver", videoDriver.toUtf8().constData()); } QString audioDriver = KdenliveSettings::audiodrivername(); if (!audioDriver.isEmpty()) { previewProps->set("audio_driver", audioDriver.toUtf8().constData()); } previewProps->set("real_time", "0"); previewProps->set("mlt_profile", m_activeProfile.toUtf8().constData()); mlt_properties_set_data(consumerProperties, "1", previewProps->get_properties(), 0, (mlt_destructor)mlt_properties_close, nullptr); // m_showFrameEvent = m_mltConsumer->listen("consumer-frame-render", this, (mlt_listener) rec_consumer_frame_show); } else { } if (xmlPlaylist) { // create an xml producer m_mltProducer = new Mlt::Producer(*m_mltProfile, "xml-string", playlist.toUtf8().constData()); } else { // create a producer based on mltproducer parameter m_mltProducer = new Mlt::Producer(*m_mltProfile, playlist.toUtf8().constData()); } if (m_mltProducer == nullptr || !m_mltProducer->is_valid()) { // qCDebug(KDENLIVE_LOG)<<"//// ERROR CREATRING PROD"; delete m_mltConsumer; m_mltConsumer = nullptr; delete m_mltProducer; m_mltProducer = nullptr; return false; } m_mltConsumer->connect(*m_mltProducer); if (m_mltConsumer->start() == -1) { delete m_showFrameEvent; m_showFrameEvent = nullptr; delete m_mltConsumer; m_mltConsumer = nullptr; return false; } m_droppedFramesTimer.start(); return true; } void MltDeviceCapture::setOverlay(const QString &path) { if (m_mltProducer == nullptr || !m_mltProducer->is_valid()) { return; } Mlt::Producer parentProd(m_mltProducer->parent()); if (parentProd.get_producer() == nullptr) { // qCDebug(KDENLIVE_LOG) << "PLAYLIST BROKEN, CANNOT INSERT CLIP //////"; return; } Mlt::Service service(parentProd.get_service()); if (service.type() != tractor_type) { qCWarning(KDENLIVE_LOG) << "// TRACTOR PROBLEM"; return; } Mlt::Tractor tractor(service); if (tractor.count() < 2) { qCWarning(KDENLIVE_LOG) << "// TRACTOR PROBLEM"; return; } mlt_service_lock(service.get_service()); Mlt::Producer trackProducer(tractor.track(0)); Mlt::Playlist trackPlaylist((mlt_playlist)trackProducer.get_service()); trackPlaylist.remove(0); if (path.isEmpty()) { mlt_service_unlock(service.get_service()); return; } // Add overlay clip char *tmp = qstrdup(path.toUtf8().constData()); auto *clip = new Mlt::Producer(*m_mltProfile, "loader", tmp); delete[] tmp; clip->set_in_and_out(0, 99999); trackPlaylist.insert_at(0, clip, 1); // Add transition mlt_service serv = m_mltProducer->parent().get_service(); mlt_service nextservice = mlt_service_get_producer(serv); mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice); QString mlt_type = mlt_properties_get(properties, "mlt_type"); if (mlt_type != QLatin1String("transition")) { // transition does not exist, add it Mlt::Field *field = tractor.field(); auto *transition = new Mlt::Transition(*m_mltProfile, "composite"); transition->set_in_and_out(0, 0); transition->set("geometry", "0/0:100%x100%:70"); transition->set("fill", 1); transition->set("operator", "and"); transition->set("a_track", 0); transition->set("b_track", 1); field->plant_transition(*transition, 0, 1); } mlt_service_unlock(service.get_service()); // delete clip; } void MltDeviceCapture::setOverlayEffect(const QString &tag, const QStringList ¶meters) { if (m_mltProducer == nullptr || !m_mltProducer->is_valid()) { return; } Mlt::Service service(m_mltProducer->parent().get_service()); Mlt::Tractor tractor(service); Mlt::Producer trackProducer(tractor.track(0)); Mlt::Playlist trackPlaylist((mlt_playlist)trackProducer.get_service()); Mlt::Service trackService(trackProducer.get_service()); mlt_service_lock(service.get_service()); // delete previous effects Mlt::Filter *filter; filter = trackService.filter(0); if ((filter != nullptr) && !tag.isEmpty()) { QString currentService = filter->get("mlt_service"); if (currentService == tag) { // Effect is already there mlt_service_unlock(service.get_service()); return; } } while (filter != nullptr) { trackService.detach(*filter); delete filter; filter = trackService.filter(0); } if (tag.isEmpty()) { mlt_service_unlock(service.get_service()); return; } char *tmp = qstrdup(tag.toUtf8().constData()); filter = new Mlt::Filter(*m_mltProfile, tmp); delete[] tmp; if ((filter != nullptr) && filter->is_valid()) { for (int j = 0; j < parameters.count(); ++j) { filter->set(parameters.at(j).section(QLatin1Char('='), 0, 0).toUtf8().constData(), parameters.at(j).section(QLatin1Char('='), 1, 1).toUtf8().constData()); } trackService.attach(*filter); } mlt_service_unlock(service.get_service()); } void MltDeviceCapture::mirror(bool activate) { if (m_mltProducer == nullptr || !m_mltProducer->is_valid()) { return; } Mlt::Service service(m_mltProducer->parent().get_service()); Mlt::Tractor tractor(service); Mlt::Producer trackProducer(tractor.track(1)); Mlt::Playlist trackPlaylist((mlt_playlist)trackProducer.get_service()); Mlt::Service trackService(trackProducer.get_service()); mlt_service_lock(service.get_service()); // delete previous effects Mlt::Filter *filter; filter = trackService.filter(0); while (filter != nullptr) { trackService.detach(*filter); delete filter; filter = trackService.filter(0); } if (!activate) { mlt_service_unlock(service.get_service()); return; } filter = new Mlt::Filter(*m_mltProfile, "mirror"); if ((filter != nullptr) && filter->is_valid()) { filter->set("mirror", "flip"); trackService.attach(*filter); } mlt_service_unlock(service.get_service()); } -void MltDeviceCapture::uyvy2rgb(unsigned char *yuv_buffer, int width, int height) +void MltDeviceCapture::uyvy2rgb(const unsigned char *yuv_buffer, int width, int height) { processingImage = true; QImage image(width, height, QImage::Format_RGB888); unsigned char *rgb_buffer = image.bits(); int rgb_ptr = 0, y_ptr = 0; int len = width * height / 2; for (int t = 0; t < len; ++t) { int Y = yuv_buffer[y_ptr]; int U = yuv_buffer[y_ptr + 1]; int Y2 = yuv_buffer[y_ptr + 2]; int V = yuv_buffer[y_ptr + 3]; y_ptr += 4; int r = ((298 * (Y - 16) + 409 * (V - 128) + 128) >> 8); int g = ((298 * (Y - 16) - 100 * (U - 128) - 208 * (V - 128) + 128) >> 8); int b = ((298 * (Y - 16) + 516 * (U - 128) + 128) >> 8); if (r > 255) { r = 255; } if (g > 255) { g = 255; } if (b > 255) { b = 255; } if (r < 0) { r = 0; } if (g < 0) { g = 0; } if (b < 0) { b = 0; } rgb_buffer[rgb_ptr] = static_cast(r); rgb_buffer[rgb_ptr + 1] = static_cast(g); rgb_buffer[rgb_ptr + 2] = static_cast(b); rgb_ptr += 3; r = ((298 * (Y2 - 16) + 409 * (V - 128) + 128) >> 8); g = ((298 * (Y2 - 16) - 100 * (U - 128) - 208 * (V - 128) + 128) >> 8); b = ((298 * (Y2 - 16) + 516 * (U - 128) + 128) >> 8); if (r > 255) { r = 255; } if (g > 255) { g = 255; } if (b > 255) { b = 255; } if (r < 0) { r = 0; } if (g < 0) { g = 0; } if (b < 0) { b = 0; } rgb_buffer[rgb_ptr] = static_cast(r); rgb_buffer[rgb_ptr + 1] = static_cast(g); rgb_buffer[rgb_ptr + 2] = static_cast(b); rgb_ptr += 3; } // emit imageReady(image); // m_captureDisplayWidget->setImage(image); emit unblockPreview(); // processingImage = false; } void MltDeviceCapture::slotPreparePreview() { QTimer::singleShot(1000, this, &MltDeviceCapture::slotAllowPreview); } void MltDeviceCapture::slotAllowPreview() { processingImage = false; } diff --git a/src/capture/mltdevicecapture.h b/src/capture/mltdevicecapture.h index 0140dccee..93bf909db 100644 --- a/src/capture/mltdevicecapture.h +++ b/src/capture/mltdevicecapture.h @@ -1,148 +1,148 @@ /*************************************************************************** mltdevicecapture.h - description ------------------- begin : Sun May 21 2011 copyright : (C) 2011 by Jean-Baptiste Mardelle (jb@kdenlive.org) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ /*! * @class MltDeviceCapture * @brief Interface for MLT capture. * Capturing started by MltDeviceCapture::slotStartCapture () * * Capturing is stopped by RecMonitor::slotStopCapture() */ #ifndef MLTDEVICECAPTURE_H #define MLTDEVICECAPTURE_H #include "definitions.h" #include "gentime.h" #include "monitor/abstractmonitor.h" #include #include // include after QTimer to have C++ phtreads defined #include namespace Mlt { class Consumer; class Frame; class Event; class Producer; class Profile; } // namespace Mlt class MltDeviceCapture : public AbstractRender { Q_OBJECT public : enum FailStates { OK = 0, APP_NOEXIST }; /** @brief Build a MLT Renderer * @param winid The parent widget identifier (required for SDL display). Set to 0 for OpenGL rendering * @param profile The MLT profile used for the capture (default one will be used if empty). */ explicit MltDeviceCapture(const QString &profile, /*VideoSurface *surface,*/ QWidget *parent = nullptr); /** @brief Destroy the MLT Renderer. */ ~MltDeviceCapture() override; int doCapture; /** @brief Someone needs us to send again a frame. */ void sendFrameUpdate() override {} - void emitFrameUpdated(Mlt::Frame &); + void emitFrameUpdated(Mlt::Frame & /*frame*/); void emitFrameNumber(double position); void emitConsumerStopped(); - void showFrame(Mlt::Frame &); - void showAudio(Mlt::Frame &); + void showFrame(Mlt::Frame & /*frame*/); + void showAudio(Mlt::Frame & /*frame*/); void saveFrame(Mlt::Frame &frame); /** @brief Starts the MLT Video4Linux process. * @param surface The widget onto which the frame should be painted * Called by RecMonitor::slotRecord () */ bool slotStartCapture(const QString ¶ms, const QString &path, const QString &playlist, bool livePreview, bool xmlPlaylist = true); bool slotStartPreview(const QString &producer, bool xmlFormat = false); /** @brief Save current frame to file. */ void captureFrame(const QString &path); /** @brief This will add the video clip from path and add it in the overlay track. */ void setOverlay(const QString &path); /** @brief This will add an MLT video effect to the overlay track. */ void setOverlayEffect(const QString &tag, const QStringList ¶meters); /** @brief This will add a horizontal flip effect, easier to work when filming yourself. */ void mirror(bool activate); /** @brief True if we are processing an image (yuv > rgb) when recording. */ bool processingImage; void pause(); private: Mlt::Consumer *m_mltConsumer; Mlt::Producer *m_mltProducer; Mlt::Profile *m_mltProfile; Mlt::Event *m_showFrameEvent; QString m_activeProfile; int m_droppedFrames; /** @brief When true, images will be displayed on monitor while capturing. */ bool m_livePreview; /** @brief Count captured frames, used to display only one in ten images while capturing. */ - int m_frameCount; + int m_frameCount{}; - void uyvy2rgb(unsigned char *yuv_buffer, int width, int height); + void uyvy2rgb(const unsigned char *yuv_buffer, int width, int height); QString m_capturePath; QTimer m_droppedFramesTimer; QMutex m_mutex; /** @brief Build the MLT Consumer object with initial settings. * @param profileName The MLT profile to use for the consumer * @returns true if consumer is valid */ bool buildConsumer(const QString &profileName = QString()); private slots: void slotPreparePreview(); void slotAllowPreview(); /** @brief When capturing, check every second for dropped frames. */ void slotCheckDroppedFrames(); signals: /** @brief A frame's image has to be shown. * * Used in Mac OS X. */ void showImageSignal(const QImage &); void frameSaved(const QString &); void droppedFrames(int); void unblockPreview(); void imageReady(const QImage &); public slots: /** @brief Stops the consumer. */ void stop(); }; #endif diff --git a/src/capture/v4lcapture.cpp b/src/capture/v4lcapture.cpp index 5be14d999..4e613d73b 100644 --- a/src/capture/v4lcapture.cpp +++ b/src/capture/v4lcapture.cpp @@ -1,133 +1,143 @@ /*************************************************************************** * Copyright (C) 2010 by Jean-Baptiste Mardelle (jb@kdenlive.org) * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ #include "v4lcapture.h" #include "kdenlivesettings.h" #include #include #include #include #include #include #include #include V4lCaptureHandler::V4lCaptureHandler() = default; // static QStringList V4lCaptureHandler::getDeviceName(const QString &input) { char *src = strdup(input.toUtf8().constData()); QString pixelformatdescription; int fd = open(src, O_RDWR | O_NONBLOCK); if (fd < 0) { free(src); return QStringList(); } - struct v4l2_capability cap; + struct v4l2_capability cap + { + }; char *devName = nullptr; int captureEnabled = 1; if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) { fprintf(stderr, "Cannot get capabilities."); // return nullptr; } else { devName = strdup((char *)cap.card); if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0u) { // Device cannot capture captureEnabled = 0; } } if (captureEnabled != 0) { - struct v4l2_format format; + struct v4l2_format format + { + }; memset(&format, 0, sizeof(format)); format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - struct v4l2_fmtdesc fmt; + struct v4l2_fmtdesc fmt + { + }; memset(&fmt, 0, sizeof(fmt)); fmt.index = 0; fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - struct v4l2_frmsizeenum sizes; + struct v4l2_frmsizeenum sizes + { + }; memset(&sizes, 0, sizeof(sizes)); - struct v4l2_frmivalenum rates; + struct v4l2_frmivalenum rates + { + }; memset(&rates, 0, sizeof(rates)); char value[200]; while (ioctl(fd, VIDIOC_ENUM_FMT, &fmt) != -1) { if (pixelformatdescription.length() > 2000) { break; } if (snprintf(value, sizeof(value), ">%c%c%c%c", fmt.pixelformat >> 0, fmt.pixelformat >> 8, fmt.pixelformat >> 16, fmt.pixelformat >> 24) > 0) { pixelformatdescription.append(value); } fprintf(stderr, "detected format: %s: %c%c%c%c\n", fmt.description, fmt.pixelformat >> 0, fmt.pixelformat >> 8, fmt.pixelformat >> 16, fmt.pixelformat >> 24); sizes.pixel_format = fmt.pixelformat; sizes.index = 0; // Query supported frame size while (ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &sizes) != -1) { struct v4l2_frmsize_discrete image_size = sizes.discrete; // Query supported frame rates rates.index = 0; rates.pixel_format = fmt.pixelformat; rates.width = image_size.width; rates.height = image_size.height; if (pixelformatdescription.length() > 2000) { break; } if (snprintf(value, sizeof(value), ":%dx%d=", image_size.width, image_size.height) > 0) { pixelformatdescription.append(value); } fprintf(stderr, "Size: %dx%d: ", image_size.width, image_size.height); while (ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &rates) != -1) { if (pixelformatdescription.length() > 2000) { break; } if (snprintf(value, sizeof(value), "%d/%d,", rates.discrete.denominator, rates.discrete.numerator) > 0) { pixelformatdescription.append(value); } fprintf(stderr, "%d/%d, ", rates.discrete.numerator, rates.discrete.denominator); rates.index++; } fprintf(stderr, "\n"); sizes.index++; } fmt.index++; } } close(fd); free(src); QStringList result; if (devName == nullptr) { return result; } QString deviceName(devName); free(devName); result << (deviceName.isEmpty() ? input : deviceName) << pixelformatdescription; return result; } diff --git a/src/definitions.h b/src/definitions.h index e95b812d0..a55ec8df7 100644 --- a/src/definitions.h +++ b/src/definitions.h @@ -1,324 +1,324 @@ /*************************************************************************** * Copyright (C) 2007 by Jean-Baptiste Mardelle (jb@kdenlive.org) * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ #ifndef DEFINITIONS_H #define DEFINITIONS_H #include "gentime.h" #include "kdenlive_debug.h" #include #include #include #include #include #include const int MAXCLIPDURATION = 15000; namespace Kdenlive { enum MonitorId { NoMonitor = 0x01, ClipMonitor = 0x02, ProjectMonitor = 0x04, RecordMonitor = 0x08, StopMotionMonitor = 0x10, DvdMonitor = 0x20 }; const int DefaultThumbHeight = 100; } // namespace Kdenlive enum class GroupType { Normal, Selection, // in that case, the group is used to emulate a selection AVSplit, // in that case, the group links the audio and video of the same clip Leaf // This is a leaf (clip or composition) }; const QString groupTypeToStr(GroupType t); GroupType groupTypeFromStr(const QString &s); enum class ObjectType { TimelineClip, TimelineComposition, TimelineTrack, BinClip, NoItem }; using ObjectId = std::pair; enum OperationType { None = 0, WaitingForConfirm, MoveOperation, ResizeStart, ResizeEnd, RollingStart, RollingEnd, RippleStart, RippleEnd, FadeIn, FadeOut, TransitionStart, TransitionEnd, MoveGuide, KeyFrame, Seek, Spacer, RubberSelection, ScrollTimeline, ZoomTimeline }; namespace PlaylistState { Q_NAMESPACE enum ClipState { VideoOnly = 1, AudioOnly = 2, Disabled = 3 }; Q_ENUM_NS(ClipState) } // namespace PlaylistState // returns a pair corresponding to (video, audio) std::pair stateToBool(PlaylistState::ClipState state); PlaylistState::ClipState stateFromBool(std::pair av); namespace TimelineMode { enum EditMode { NormalEdit = 0, OverwriteEdit = 1, InsertEdit = 2 }; } namespace ClipType { Q_NAMESPACE enum ProducerType { Unknown = 0, Audio = 1, Video = 2, AV = 3, Color = 4, Image = 5, Text = 6, SlideShow = 7, Virtual = 8, Playlist = 9, WebVfx = 10, TextTemplate = 11, QText = 12, Composition = 13, Track = 14 }; Q_ENUM_NS(ProducerType) } // namespace ClipType enum ProjectItemType { ProjectClipType = 0, ProjectFolderType, ProjectSubclipType }; enum GraphicsRectItem { AVWidget = 70000, LabelWidget, TransitionWidget, GroupWidget }; enum ProjectTool { SelectTool = 0, RazorTool = 1, SpacerTool = 2 }; enum MonitorSceneType { MonitorSceneNone = 0, MonitorSceneDefault, MonitorSceneGeometry, MonitorSceneCorners, MonitorSceneRoto, MonitorSceneSplit, MonitorSceneRipple }; enum MessageType { DefaultMessage, ProcessingJobMessage, OperationCompletedMessage, InformationMessage, ErrorMessage, MltError }; enum TrackType { AudioTrack = 0, VideoTrack = 1, AnyTrack = 2 }; enum CacheType { SystemCacheRoot = -1, CacheRoot = 0, CacheBase = 1, CachePreview = 2, CacheProxy = 3, CacheAudio = 4, CacheThumbs = 5 }; enum TrimMode { NormalTrim, RippleTrim, RollingTrim, SlipTrim, SlideTrim }; class TrackInfo { public: TrackType type; QString trackName; bool isMute; bool isBlind; bool isLocked; int duration; /*EffectsList effectsList; TrackInfo() : type(VideoTrack) , isMute(0) , isBlind(0) , isLocked(0) , duration(0) , effectsList(true) { }*/ }; struct requestClipInfo { QDomElement xml; QString clipId; int imageHeight; bool replaceProducer; bool operator==(const requestClipInfo &a) const { return clipId == a.clipId; } }; typedef QMap stringMap; typedef QMap> audioByteArray; using audioShortVector = QVector; class ItemInfo { public: /** startPos is the position where the clip starts on the track */ GenTime startPos; /** endPos is the duration where the clip ends on the track */ GenTime endPos; /** cropStart is the position where the sub-clip starts, relative to the clip's 0 position */ GenTime cropStart; /** cropDuration is the duration of the clip */ GenTime cropDuration; /** Track number */ int track{0}; - ItemInfo() {} + ItemInfo() = default; bool isValid() const { return startPos != endPos; } bool contains(GenTime pos) const { if (startPos == endPos) { return true; } return (pos <= endPos && pos >= startPos); } bool operator==(const ItemInfo &a) const { return startPos == a.startPos && endPos == a.endPos && track == a.track && cropStart == a.cropStart; } }; class TransitionInfo { public: /** startPos is the position where the clip starts on the track */ GenTime startPos; /** endPos is the duration where the clip ends on the track */ GenTime endPos; /** the track on which the transition is (b_track)*/ int b_track{0}; /** the track on which the transition is applied (a_track)*/ int a_track{0}; /** Does the user request for a special a_track */ bool forceTrack{0}; - TransitionInfo() {} + TransitionInfo() = default; }; class CommentedTime { public: CommentedTime(); CommentedTime(const GenTime &time, QString comment, int markerType = 0); CommentedTime(const QString &hash, const GenTime &time); QString comment() const; GenTime time() const; /** @brief Returns a string containing infos needed to store marker info. string equals marker type + QLatin1Char(':') + marker comment */ QString hash() const; void setComment(const QString &comm); void setMarkerType(int t); int markerType() const; static QColor markerColor(int type); /* Implementation of > operator; Works identically as with basic types. */ bool operator>(const CommentedTime &op) const; /* Implementation of < operator; Works identically as with basic types. */ bool operator<(const CommentedTime &op) const; /* Implementation of >= operator; Works identically as with basic types. */ bool operator>=(const CommentedTime &op) const; /* Implementation of <= operator; Works identically as with basic types. */ bool operator<=(const CommentedTime &op) const; /* Implementation of == operator; Works identically as with basic types. */ bool operator==(const CommentedTime &op) const; /* Implementation of != operator; Works identically as with basic types. */ bool operator!=(const CommentedTime &op) const; private: GenTime m_time; QString m_comment; int m_type{0}; }; QDebug operator<<(QDebug qd, const ItemInfo &info); // we provide hash function for qstring and QPersistentModelIndex namespace std { template <> struct hash { std::size_t operator()(const QString &k) const { return qHash(k); } }; template <> struct hash { std::size_t operator()(const QPersistentModelIndex &k) const { return qHash(k); } }; } // namespace std // The following is a hack that allows to use shared_from_this in the case of a multiple inheritance. // Credit: https://stackoverflow.com/questions/14939190/boost-shared-from-this-and-multiple-inheritance template struct enable_shared_from_this_virtual; class enable_shared_from_this_virtual_base : public std::enable_shared_from_this { using base_type = std::enable_shared_from_this; template friend struct enable_shared_from_this_virtual; std::shared_ptr shared_from_this() { return base_type::shared_from_this(); } std::shared_ptr shared_from_this() const { return base_type::shared_from_this(); } }; template struct enable_shared_from_this_virtual : virtual enable_shared_from_this_virtual_base { using base_type = enable_shared_from_this_virtual_base; public: std::shared_ptr shared_from_this() { std::shared_ptr result(base_type::shared_from_this(), static_cast(this)); return result; } std::shared_ptr shared_from_this() const { std::shared_ptr result(base_type::shared_from_this(), static_cast(this)); return result; } }; // This is a small trick to have a QAbstractItemModel with shared_from_this enabled without multiple inheritance // Be careful, if you use this class, you have to make sure to init weak_this_ when you construct a shared_ptr to your object template class QAbstractItemModel_shared_from_this : public QAbstractItemModel { protected: QAbstractItemModel_shared_from_this() : QAbstractItemModel() { } public: std::shared_ptr shared_from_this() { std::shared_ptr p(weak_this_); assert(p.get() == this); return p; } std::shared_ptr shared_from_this() const { std::shared_ptr p(weak_this_); assert(p.get() == this); return p; } public: // actually private, but avoids compiler template friendship issues mutable std::weak_ptr weak_this_; }; #endif