diff --git a/src/monitor/recmanager.cpp b/src/monitor/recmanager.cpp index 4a0088372..35cfcc9df 100644 --- a/src/monitor/recmanager.cpp +++ b/src/monitor/recmanager.cpp @@ -1,442 +1,467 @@ /*************************************************************************** * 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_screenIndex(-1) { 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 = new QAction(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()); 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(); m_audio_device->addItems(audioDevices); connect(m_audio_device, static_cast(&QComboBox::currentIndexChanged), this, &RecManager::slotAudioDeviceChanged); QString selectedDevice = KdenliveSettings::defaultaudiocapture(); int selectedIndex = m_audio_device->findText(selectedDevice); if (!selectedDevice.isNull() && selectedIndex > -1) { m_audio_device->setCurrentIndex(selectedIndex); } 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); selectedIndex = m_device_selector->findData(KdenliveSettings::defaultcapture()); if (selectedIndex > -1) { m_device_selector->setCurrentIndex(selectedIndex); } 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(); connect(m_monitor, &Monitor::screenChanged, this, &RecManager::slotSetScreen); } RecManager::~RecManager() = default; void RecManager::showRecConfig() { pCore->showConfigDialog(4, m_device_selector->currentData().toInt()); } QToolBar *RecManager::toolbar() const { return m_recToolbar; } QAction *RecManager::recAction() const { return m_recAction; } void RecManager::stopCapture() { if (m_captureProcess) { 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) { m_checkAudio = m_recAudio->isChecked(); m_checkVideo = m_recVideo->isChecked(); pCore->startMediaCapture(m_checkAudio, m_checkVideo); } else { stopCapture(); } return; }*/ if (!record) { if (!m_captureProcess) { return; } m_captureProcess->write("q"); if (!m_captureProcess->waitForFinished()) { 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, QOverload::of(&QProcess::finished), 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; QRect screenSize = QApplication::desktop()->screenGeometry(m_screenIndex); QStringList captureArgs; #ifdef Q_OS_WIN captureArgs << QStringLiteral("-f") << QStringLiteral("gdigrab"); // fps captureArgs << QStringLiteral("-framerate") << QString::number(KdenliveSettings::grab_fps()); captureSize = QStringLiteral("desktop"); + captureArgs << QStringLiteral("-i") << captureSize; + if (KdenliveSettings::grab_parameters().contains(QLatin1String("alsa"))) { + // Add audio device + QString params = KdenliveSettings::grab_parameters().simplified(); + // Use Windows dshow for audio capture + params.replace(QLatin1String("alsa"), QStringLiteral("dshow")); + // Remove vorbis codec + params.replace(QLatin1String("-acodec libvorbis"), QString()); + + // Find first audio device + QProcess tst; + tst.start(KdenliveSettings::ffmpegpath(), {"-hide_banner","-list_devices","true","-f","dshow","-i","dummy"}); + tst.waitForFinished(4000); + QString dshowOutput = QString::fromUtf8(tst.readAll()); + if (dshowOutput.contains(QLatin1String("audio devices"))) { + dshowOutput = dshowOutput.section(QLatin1String("audio devices"), 1); + dshowOutput = dshowOutput.section(QLatin1Char('"'), 1, 1); + params.replace(QLatin1String("default"), QString("audio=\"%1\"").arg(dshowOutput)); + } + captureArgs << params.split(QLatin1Char(' ')); + } else if (!KdenliveSettings::grab_parameters().simplified().isEmpty()) { + captureArgs << KdenliveSettings::grab_parameters().simplified().split(QLatin1Char(' ')); + } + qDebug()<<"== STARTING WIN CAPTURE: "<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(int exitCode, QProcess::ExitStatus exitStatus) { m_recAction->setEnabled(true); m_recAction->setChecked(false); m_device_selector->setEnabled(true); if (exitStatus == QProcess::CrashExit) { emit warningMessage(i18n("Capture crashed, please check your parameters"), -1, QList() << m_showLogAction); } else { if (exitCode != 0 && exitCode != 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); } } if (m_captureProcess) { 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) { QString currentDevice = m_audio_device->currentText(); KdenliveSettings::setDefaultaudiocapture(currentDevice); } void RecManager::slotSetVolume(int volume) { KdenliveSettings::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::slotSetScreen(int screenIndex) { m_screenIndex = screenIndex; } 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); }