diff --git a/src/monitor/recmanager.cpp b/src/monitor/recmanager.cpp index bbc11a512..868279773 100644 --- a/src/monitor/recmanager.cpp +++ b/src/monitor/recmanager.cpp @@ -1,478 +1,482 @@ /*************************************************************************** * Copyright (C) 2015 by Jean-Baptiste Mardelle (jb@kdenlive.org) * * 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 "recmanager.h" #include "capture/managecapturesdialog.h" #include "capture/mltdevicecapture.h" #include "core.h" #include "dialogs/profilesdialog.h" #include "kdenlivesettings.h" #include "monitor.h" #include "klocalizedstring.h" #include #include #include #include #include #include #include #include #include #include RecManager::RecManager(Monitor *parent) : QObject(parent) , m_monitor(parent) , m_recToolbar(new QToolBar(parent)) , m_checkAudio(false) , m_checkVideo(false) { m_playAction = m_recToolbar->addAction(QIcon::fromTheme(QStringLiteral("media-playback-start")), i18n("Preview")); m_playAction->setCheckable(true); connect(m_playAction, &QAction::toggled, this, &RecManager::slotPreview); m_recAction = m_recToolbar->addAction(QIcon::fromTheme(QStringLiteral("media-record")), i18n("Record")); m_recAction->setCheckable(true); connect(m_recAction, &QAction::toggled, this, &RecManager::slotRecord); m_showLogAction = new QAction(i18n("Show log"), this); connect(m_showLogAction, &QAction::triggered, this, &RecManager::slotShowLog); m_recVideo = new QCheckBox(i18n("Video")); m_recAudio = new QCheckBox(i18n("Audio")); m_recToolbar->addWidget(m_recVideo); m_recToolbar->addWidget(m_recAudio); m_recAudio->setChecked(KdenliveSettings::v4l_captureaudio()); m_recVideo->setChecked(KdenliveSettings::v4l_capturevideo()); // Check number of monitors for FFmpeg screen capture int screens = QApplication::desktop()->screenCount(); if (screens > 1) { m_screenCombo = new QComboBox(parent); for (int ix = 0; ix < screens; ix++) { m_screenCombo->addItem(i18n("Monitor %1", ix)); } m_recToolbar->addWidget(m_screenCombo); // Update screen grab monitor choice in case we changed from fullscreen m_screenCombo->setEnabled(KdenliveSettings::grab_capture_type() == 0); } QWidget *spacer = new QWidget(parent); spacer->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); m_recToolbar->addWidget(spacer); m_audio_device = new QComboBox(parent); QStringList audioDevices = pCore->getAudioCaptureDevices(); for (int ix = 0; ix < audioDevices.count(); ix++) { m_audio_device->addItem(audioDevices.at(ix), ix); } connect(m_audio_device, static_cast(&QComboBox::currentIndexChanged), this, &RecManager::slotAudioDeviceChanged); int selectedCapture = m_audio_device->findData(KdenliveSettings::defaultaudiocapture()); if (selectedCapture > -1) { m_audio_device->setCurrentIndex(selectedCapture); } m_recToolbar->addWidget(m_audio_device); m_audioCaptureSlider = new QSlider(Qt::Vertical); m_audioCaptureSlider->setRange(0, 100); m_audioCaptureSlider->setValue(KdenliveSettings::audiocapturevolume()); connect(m_audioCaptureSlider, &QSlider::valueChanged, this, &RecManager::slotSetVolume); auto *widgetslider = new QWidgetAction(parent); widgetslider->setText(i18n("Audio Capture Volume")); widgetslider->setDefaultWidget(m_audioCaptureSlider); auto *menu = new QMenu(parent); menu->addAction(widgetslider); m_audioCaptureButton = new QToolButton(parent); m_audioCaptureButton->setMenu(menu); m_audioCaptureButton->setToolTip(i18n("Audio Capture Volume")); m_audioCaptureButton->setPopupMode(QToolButton::InstantPopup); QIcon icon; if (KdenliveSettings::audiocapturevolume() == 0) { icon = QIcon::fromTheme(QStringLiteral("audio-volume-muted")); } else { icon = QIcon::fromTheme(QStringLiteral("audio-volume-medium")); } m_audioCaptureButton->setIcon(icon); m_recToolbar->addWidget(m_audioCaptureButton); m_recToolbar->addSeparator(); m_device_selector = new QComboBox(parent); // TODO: re-implement firewire / decklink capture // m_device_selector->addItems(QStringList() << i18n("Firewire") << i18n("Webcam") << i18n("Screen Grab") << i18n("Blackmagic Decklink")); m_device_selector->addItem(i18n("Webcam"), Video4Linux); m_device_selector->addItem(i18n("Screen Grab"), ScreenGrab); selectedCapture = m_device_selector->findData(KdenliveSettings::defaultcapture()); if (selectedCapture > -1) { m_device_selector->setCurrentIndex(selectedCapture); } connect(m_device_selector, static_cast(&QComboBox::currentIndexChanged), this, &RecManager::slotVideoDeviceChanged); m_recToolbar->addWidget(m_device_selector); QAction *configureRec = m_recToolbar->addAction(QIcon::fromTheme(QStringLiteral("configure")), i18n("Configure Recording")); connect(configureRec, &QAction::triggered, this, &RecManager::showRecConfig); m_recToolbar->addSeparator(); m_switchRec = m_recToolbar->addAction(QIcon::fromTheme(QStringLiteral("list-add")), i18n("Show Record Control")); m_switchRec->setCheckable(true); connect(m_switchRec, &QAction::toggled, m_monitor, &Monitor::slotSwitchRec); m_recToolbar->setVisible(false); slotVideoDeviceChanged(); } RecManager::~RecManager() = default; void RecManager::showRecConfig() { m_monitor->showConfigDialog(4, m_device_selector->currentData().toInt()); } QToolBar *RecManager::toolbar() const { return m_recToolbar; } QAction *RecManager::switchAction() const { return m_switchRec; } void RecManager::stopCapture() { if (m_captureProcess) { + connect(m_captureProcess, &QProcess::stateChanged, [&](QProcess::ProcessState status) { + if (status == QProcess::NotRunning) { + emit addClipToProject(m_captureFile); + } + }); slotRecord(false); } else if (pCore->getMediaCaptureState() == 1 && (m_checkAudio || m_checkVideo)) { // QMediaRecorder::RecordingState value is 1 pCore->stopMediaCapture(m_checkAudio, m_checkVideo); m_monitor->slotOpenClip(nullptr); } } void RecManager::stop() { if (m_captureProcess) { // Don't stop screen rec when hiding rec toolbar } else { stopCapture(); m_switchRec->setChecked(false); } toolbar()->setVisible(false); } void RecManager::slotRecord(bool record) { if (m_device_selector->currentData().toInt() == Video4Linux) { if (record) { QDir captureFolder; if (KdenliveSettings::capturetoprojectfolder()) { captureFolder = QDir(m_monitor->projectFolder()); } else { captureFolder = QDir(KdenliveSettings::capturefolder()); } QString extension; if (!m_recVideo->isChecked()) { extension = QStringLiteral("wav"); } else { extension = QStringLiteral("mpeg"); } QString path = captureFolder.absoluteFilePath("capture0000." + extension); int i = 1; while (QFile::exists(path)) { QString num = QString::number(i).rightJustified(4, '0', false); path = captureFolder.absoluteFilePath("capture" + num + QLatin1Char('.') + extension); ++i; } QString audioDevice = m_audio_device->currentText(); m_captureFile = QUrl::fromLocalFile(path); m_checkAudio = m_recAudio->isChecked(); m_checkVideo = m_recVideo->isChecked(); pCore->startMediaCapture(m_checkAudio, m_checkVideo, m_captureFile, audioDevice); } else { stopCapture(); - emit addClipToProject(m_captureFile); } return; } if (!record) { if (!m_captureProcess) { return; } m_captureProcess->terminate(); QTimer::singleShot(1500, m_captureProcess, &QProcess::kill); return; } if (m_captureProcess) { return; } m_recError.clear(); QString extension = KdenliveSettings::grab_extension(); QDir captureFolder; if (KdenliveSettings::capturetoprojectfolder()) { captureFolder = QDir(m_monitor->projectFolder()); } else { captureFolder = QDir(KdenliveSettings::capturefolder()); } QFileInfo checkCaptureFolder(captureFolder.absolutePath()); if (!checkCaptureFolder.isWritable()) { emit warningMessage(i18n("The directory %1, could not be created.\nPlease " "make sure you have the required permissions.", captureFolder.absolutePath())); m_recAction->blockSignals(true); m_recAction->setChecked(false); m_recAction->blockSignals(false); return; } m_captureProcess = new QProcess; connect(m_captureProcess, &QProcess::stateChanged, this, &RecManager::slotProcessStatus); connect(m_captureProcess, &QProcess::readyReadStandardError, this, &RecManager::slotReadProcessInfo); QString path = captureFolder.absoluteFilePath("capture0000." + extension); int i = 1; while (QFile::exists(path)) { QString num = QString::number(i).rightJustified(4, '0', false); path = captureFolder.absoluteFilePath("capture" + num + QLatin1Char('.') + extension); ++i; } m_captureFile = QUrl::fromLocalFile(path); QString captureSize; int screen = -1; if (m_screenCombo) { // Multi monitor setup, capture monitor selected by user screen = m_screenCombo->currentIndex(); } QRect screenSize = QApplication::desktop()->screenGeometry(screen); QStringList captureArgs; #ifdef Q_OS_WIN captureArgs << QStringLiteral("-f") << QStringLiteral("gdigrab"); // fps captureArgs << QStringLiteral("-framerate") << QString::number(KdenliveSettings::grab_fps()); captureSize = QStringLiteral("desktop"); #else captureArgs << QStringLiteral("-f") << QStringLiteral("x11grab"); if (KdenliveSettings::grab_follow_mouse()) { captureArgs << QStringLiteral("-follow_mouse") << QStringLiteral("centered"); } if (!KdenliveSettings::grab_hide_frame()) { captureArgs << QStringLiteral("-show_region") << QStringLiteral("1"); } captureSize = QStringLiteral(":0.0"); if (KdenliveSettings::grab_capture_type() == 0) { // Full screen capture captureArgs << QStringLiteral("-s") << QString::number(screenSize.width()) + QLatin1Char('x') + QString::number(screenSize.height()); captureSize.append(QLatin1Char('+') + QString::number(screenSize.left()) + QLatin1Char('.') + QString::number(screenSize.top())); } else { // Region capture captureArgs << QStringLiteral("-s") << QString::number(KdenliveSettings::grab_width()) + QLatin1Char('x') + QString::number(KdenliveSettings::grab_height()); captureSize.append(QLatin1Char('+') + QString::number(KdenliveSettings::grab_offsetx()) + QLatin1Char(',') + QString::number(KdenliveSettings::grab_offsety())); } if (KdenliveSettings::grab_hide_mouse()) { captureSize.append(QStringLiteral("+nomouse")); } // fps captureArgs << QStringLiteral("-r") << QString::number(KdenliveSettings::grab_fps()); #endif captureArgs << QStringLiteral("-i") << captureSize; if (!KdenliveSettings::grab_parameters().simplified().isEmpty()) { captureArgs << KdenliveSettings::grab_parameters().simplified().split(QLatin1Char(' ')); } captureArgs << path; m_captureProcess->start(KdenliveSettings::ffmpegpath(), captureArgs); if (!m_captureProcess->waitForStarted()) { // Problem launching capture app emit warningMessage(i18n("Failed to start the capture application:\n%1", KdenliveSettings::ffmpegpath())); // delete m_captureProcess; } } void RecManager::slotProcessStatus(QProcess::ProcessState status) { if (status == QProcess::NotRunning) { m_recAction->setEnabled(true); m_recAction->setChecked(false); m_device_selector->setEnabled(true); if (m_captureProcess) { if (m_captureProcess->exitStatus() == QProcess::CrashExit) { emit warningMessage(i18n("Capture crashed, please check your parameters"), -1, QList() << m_showLogAction); } else { if (true) { int code = m_captureProcess->exitCode(); if (code != 0 && code != 255) { emit warningMessage(i18n("Capture crashed, please check your parameters"), -1, QList() << m_showLogAction); } else { // Capture successful, add clip to project emit addClipToProject(m_captureFile); } } } } delete m_captureProcess; m_captureProcess = nullptr; } } void RecManager::slotReadProcessInfo() { QString data = m_captureProcess->readAllStandardError().simplified(); m_recError.append(data + QLatin1Char('\n')); } void RecManager::slotAudioDeviceChanged(int) { int currentItem = m_audio_device->currentData().toInt(); KdenliveSettings::setDefaultaudiocapture(currentItem); } void RecManager::slotSetVolume(int volume) { KdenliveSettings::setAudiocapturevolume(volume); pCore->setAudioCaptureVolume(volume); QIcon icon; if (volume == 0) { icon = QIcon::fromTheme(QStringLiteral("audio-volume-muted")); } else { icon = QIcon::fromTheme(QStringLiteral("audio-volume-medium")); } m_audioCaptureButton->setIcon(icon); } void RecManager::slotVideoDeviceChanged(int) { int currentItem = m_device_selector->currentData().toInt(); KdenliveSettings::setDefaultcapture(currentItem); switch (currentItem) { case Video4Linux: m_playAction->setEnabled(true); break; case BlackMagic: m_playAction->setEnabled(false); break; default: m_playAction->setEnabled(false); break; } /* m_previewSettings->setEnabled(ix == Video4Linux || ix == BlackMagic); control_frame->setVisible(ix == Video4Linux); monitor_box->setVisible(ix == ScreenBag && monitor_box->count() > 0); m_playAction->setVisible(ix != ScreenBag); m_fwdAction->setVisible(ix == Firewire); m_discAction->setVisible(ix == Firewire); m_rewAction->setVisible(ix == Firewire); m_recAction->setEnabled(ix != Firewire); m_logger.setVisible(ix == BlackMagic); if (m_captureDevice) { // MLT capture still running, abort m_monitorManager->clearScopeSource(); m_captureDevice->stop(); delete m_captureDevice; m_captureDevice = nullptr; } // The m_videoBox container has to be shown once before the MLT consumer is build, or preview will fail switch (ix) { case ScreenBag: } */ } Mlt::Producer *RecManager::createV4lProducer() { QString profilePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/profiles/video4linux"); Mlt::Profile *vidProfile = new Mlt::Profile(profilePath.toUtf8().constData()); Mlt::Producer *prod = nullptr; if (m_recVideo->isChecked()) { prod = new Mlt::Producer(*vidProfile, QStringLiteral("video4linux2:%1").arg(KdenliveSettings::video4vdevice()).toUtf8().constData()); if ((prod == nullptr) || !prod->is_valid()) { return nullptr; } prod->set("width", vidProfile->width()); prod->set("height", vidProfile->height()); prod->set("framerate", vidProfile->fps()); /*p->set("standard", ui->v4lStandardCombo->currentText().toLatin1().constData()); p->set("channel", ui->v4lChannelSpinBox->value()); p->set("audio_ix", ui->v4lAudioComboBox->currentIndex());*/ prod->set("force_seekable", 0); } if (m_recAudio->isChecked() && (prod != nullptr) && prod->is_valid()) { // Add audio track Mlt::Producer *audio = new Mlt::Producer( *vidProfile, QStringLiteral("alsa:%1?channels=%2").arg(KdenliveSettings::v4l_alsadevicename()).arg(KdenliveSettings::alsachannels()).toUtf8().constData()); audio->set("mlt_service", "avformat-novalidate"); audio->set("audio_index", 0); audio->set("video_index", -1); auto *tractor = new Mlt::Tractor(*vidProfile); tractor->set_track(*prod, 0); delete prod; tractor->set_track(*audio, 1); delete audio; prod = new Mlt::Producer(tractor->get_producer()); delete tractor; } return prod; } void RecManager::slotPreview(bool preview) { if (m_device_selector->currentData().toInt() == Video4Linux) { if (preview) { std::shared_ptr prod(createV4lProducer()); if (prod && prod->is_valid()) { m_monitor->updateClipProducer(prod); } else { emit warningMessage(i18n("Capture crashed, please check your parameters")); } } else { m_monitor->slotOpenClip(nullptr); } } /* buildMltDevice(path); bool isXml; producer = getV4lXmlPlaylist(profile, &isXml); //producer = QString("avformat-novalidate:video4linux2:%1?width:%2&height:%3&frame_rate:%4").arg(KdenliveSettings::video4vdevice()).arg(profile.width).arg(profile.height).arg((double) profile.frame_rate_num / profile.frame_rate_den); if (!m_captureDevice->slotStartPreview(producer, isXml)) { // v4l capture failed to start video_frame->setText(i18n("Failed to start Video4Linux,\ncheck your parameters...")); } else { m_playAction->setEnabled(false); m_stopAction->setEnabled(true); m_isPlaying = true; } }*/ } void RecManager::slotShowLog() { KMessageBox::information(QApplication::activeWindow(), m_recError); }