From ac5f5c9f6d4bdffeb76f3f2098b80f27bc532606 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Mardelle Date: Wed, 25 May 2011 13:11:23 +0000 Subject: [PATCH] Complete rewrite of the video4linux capture to use MLT, in progress. Breaks Decklink capture for a few days... svn path=/trunk/kdenlive/; revision=5607 --- src/CMakeLists.txt | 9 +- src/abstractmonitor.cpp | 64 ++++ src/abstractmonitor.h | 88 +++++ src/colorscopes/abstractgfxscopewidget.cpp | 39 +- src/colorscopes/abstractgfxscopewidget.h | 14 +- src/colorscopes/histogram.cpp | 4 +- src/colorscopes/histogram.h | 2 +- src/colorscopes/rgbparade.cpp | 4 +- src/colorscopes/rgbparade.h | 2 +- src/colorscopes/vectorscope.cpp | 4 +- src/colorscopes/vectorscope.h | 2 +- src/colorscopes/waveform.cpp | 4 +- src/colorscopes/waveform.h | 2 +- src/kdenlivedoc.h | 1 - src/kdenlivesettings.kcfg | 57 +-- src/kdenlivesettingsdialog.cpp | 143 ++++++-- src/kdenlivesettingsdialog.h | 4 +- src/mainwindow.cpp | 41 ++- src/mainwindow.h | 2 +- src/mltdevicecapture.cpp | 406 +++++++++++++++++++++ src/mltdevicecapture.h | 127 +++++++ src/monitor.cpp | 30 +- src/monitor.h | 15 +- src/monitormanager.cpp | 86 +++-- src/monitormanager.h | 14 +- src/profilesdialog.cpp | 25 +- src/profilesdialog.h | 7 +- src/recmonitor.cpp | 181 ++++++--- src/recmonitor.h | 32 +- src/renderer.cpp | 4 +- src/renderer.h | 15 +- src/stopmotion/stopmotion.cpp | 199 ++++++++-- src/stopmotion/stopmotion.h | 46 ++- src/v4l/src.c | 339 +++++++---------- src/v4l/src.h | 53 +-- src/v4l/src_v4l2.c | 101 +++-- src/v4l/v4lcapture.cpp | 352 +----------------- src/v4l/v4lcapture.h | 34 +- src/v4l/videodev2.h | 4 +- src/widgets/configcapture_ui.ui | 369 +++++++++---------- src/widgets/stopmotion_ui.ui | 124 +++---- src/widgets/wizardcapture_ui.ui | 62 ++-- src/wizard.cpp | 100 ++++- src/wizard.h | 1 + 44 files changed, 1939 insertions(+), 1273 deletions(-) create mode 100644 src/abstractmonitor.cpp create mode 100644 src/abstractmonitor.h create mode 100644 src/mltdevicecapture.cpp create mode 100644 src/mltdevicecapture.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9cb5093d..4be9af35 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -282,6 +282,8 @@ set(kdenlive_SRCS simplekeyframes/simplekeyframewidget.cpp noteswidget.cpp archivewidget.cpp + mltdevicecapture.cpp + abstractmonitor.cpp ) add_definitions(${KDE4_DEFINITIONS}) @@ -302,13 +304,6 @@ if(NOT APPLE AND NOT CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") ${kdenlive_SRCS} v4l/v4lcapture.cpp v4l/src.c - v4l/src_v4l2.c - v4l/dec_bayer.c - v4l/dec_grey.c - v4l/dec_jpeg.c - v4l/dec_rgb.c - v4l/dec_s561.c - v4l/dec_yuv.c ) endif(NOT APPLE AND NOT CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") diff --git a/src/abstractmonitor.cpp b/src/abstractmonitor.cpp new file mode 100644 index 00000000..b83765e6 --- /dev/null +++ b/src/abstractmonitor.cpp @@ -0,0 +1,64 @@ +/*************************************************************************** + * 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. * + * * + * 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 "abstractmonitor.h" + + +VideoPreviewContainer::VideoPreviewContainer(QWidget *parent) : + QFrame(parent), + m_image(new QImage()) +{ + setFrameShape(QFrame::NoFrame); + setFocusPolicy(Qt::ClickFocus); + setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); +} + + +void VideoPreviewContainer::setImage(QImage img) +{ + if (m_image) delete m_image; + m_image = new QImage(img); + update(); +} + +// virtual +void VideoPreviewContainer::paintEvent(QPaintEvent */*event*/) +{ + if (m_image->isNull()) return; + QPainter painter(this); + double ar = (double) m_image->width() / m_image->height(); + QRect rect = this->frameRect(); + int paintW = rect.height() * ar + 0.5; + if (paintW > rect.width()) { + paintW = rect.width() / ar + 0.5; + int diff = (rect.height() - paintW) / 2; + rect.adjust(0, diff, 0, 0); + rect.setHeight(paintW); + } + else { + int diff = (rect.width() - paintW) / 2; + rect.adjust(diff, 0, 0, 0); + rect.setWidth(paintW); + } + + painter.drawImage(rect, *m_image); +} + + diff --git a/src/abstractmonitor.h b/src/abstractmonitor.h new file mode 100644 index 00000000..2f56ac4d --- /dev/null +++ b/src/abstractmonitor.h @@ -0,0 +1,88 @@ +/*************************************************************************** + * 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. * + * * + * 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 ABSTRACTMONITOR_H +#define ABSTRACTMONITOR_H + +#include +#include +#include +#include +#include +#include + +class VideoPreviewContainer : public QFrame +{ + Q_OBJECT +public: + VideoPreviewContainer(QWidget *parent = 0); + + void setImage(QImage img); + +protected: + virtual void paintEvent(QPaintEvent */*event*/); + +private: + QImage *m_image; +}; + + + +class AbstractRender: public QObject +{ +Q_OBJECT public: + + /** @brief Build an abstract MLT Renderer + * @param rendererName A unique identifier for this 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 renderer (default one will be used if empty). */ + AbstractRender(QWidget *parent = 0):QObject(parent),sendFrameForAnalysis(false) {}; + + /** @brief Destroy the MLT Renderer. */ + virtual ~AbstractRender() {}; + + /** @brief This property is used to decide if the renderer should convert it's frames to QImage for use in other Kdenlive widgets. */ + bool sendFrameForAnalysis; + + /** @brief Someone needs us to send again a frame. */ + virtual void sendFrameUpdate() = 0; + +signals: + /** @brief The renderer refreshed the current frame. */ + void frameUpdated(QImage); + + /** @brief This signal contains the audio of the current frame. */ + void audioSamplesSignal(QVector, int, int, int); +}; + +class AbstractMonitor : public QWidget +{ + Q_OBJECT +public: + AbstractMonitor(QWidget *parent = 0): QWidget(parent) {}; + virtual ~AbstractMonitor() {}; + virtual AbstractRender *abstractRender() = 0; + virtual const QString name() const = 0; + +public slots: + virtual void stop() = 0; + virtual void start() = 0; +}; + +#endif diff --git a/src/colorscopes/abstractgfxscopewidget.cpp b/src/colorscopes/abstractgfxscopewidget.cpp index 275be135..43a7d815 100644 --- a/src/colorscopes/abstractgfxscopewidget.cpp +++ b/src/colorscopes/abstractgfxscopewidget.cpp @@ -12,7 +12,7 @@ #include "abstractgfxscopewidget.h" #include "renderer.h" -#include "monitor.h" +#include "monitormanager.h" #include #include @@ -29,18 +29,18 @@ const int REALTIME_FPS = 30; -AbstractGfxScopeWidget::AbstractGfxScopeWidget(Monitor *projMonitor, Monitor *clipMonitor, bool trackMouse, QWidget *parent) : +AbstractGfxScopeWidget::AbstractGfxScopeWidget(MonitorManager *manager, bool trackMouse, QWidget *parent) : AbstractScopeWidget(trackMouse, parent), - m_projMonitor(projMonitor), - m_clipMonitor(clipMonitor) - + m_manager(manager) { - m_activeRender = (m_clipMonitor->isActive()) ? m_clipMonitor->render : m_projMonitor->render; + m_activeRender = m_manager->activeRenderer(); bool b = true; - b &= connect(m_activeRender, SIGNAL(frameUpdated(QImage)), this, SLOT(slotRenderZoneUpdated(QImage))); + if (m_activeRender != NULL) + b &= connect(m_activeRender, SIGNAL(frameUpdated(QImage)), this, SLOT(slotRenderZoneUpdated(QImage))); Q_ASSERT(b); } + AbstractGfxScopeWidget::~AbstractGfxScopeWidget() { } QImage AbstractGfxScopeWidget::renderScope(uint accelerationFactor) @@ -50,7 +50,7 @@ QImage AbstractGfxScopeWidget::renderScope(uint accelerationFactor) void AbstractGfxScopeWidget::mouseReleaseEvent(QMouseEvent *event) { - if (!m_aAutoRefresh->isChecked()) { + if (!m_aAutoRefresh->isChecked() && m_activeRender) { m_activeRender->sendFrameUpdate(); } AbstractScopeWidget::mouseReleaseEvent(event); @@ -59,20 +59,23 @@ void AbstractGfxScopeWidget::mouseReleaseEvent(QMouseEvent *event) ///// Slots ///// -void AbstractGfxScopeWidget::slotActiveMonitorChanged(bool isClipMonitor) +void AbstractGfxScopeWidget::slotActiveMonitorChanged() { + if (m_activeRender) { + bool b = m_activeRender->disconnect(this); + Q_ASSERT(b); + } + + m_activeRender = m_manager->activeRenderer(); #ifdef DEBUG_AGSW - qDebug() << "Active monitor has changed in " << widgetName() << ". Is the clip monitor active now? " << isClipMonitor; + qDebug() << "Active monitor has changed in " << widgetName() << ". Is the clip monitor active now? " << m_activeRender->name(); #endif - bool b = m_activeRender->disconnect(this); - Q_ASSERT(b); - - m_activeRender = (isClipMonitor) ? m_clipMonitor->render : m_projMonitor->render; - //b &= connect(m_activeRender, SIGNAL(rendererPosition(int)), this, SLOT(slotRenderZoneUpdated())); - b &= connect(m_activeRender, SIGNAL(frameUpdated(QImage)), this, SLOT(slotRenderZoneUpdated(QImage))); - Q_ASSERT(b); + if (m_activeRender) { + bool b = connect(m_activeRender, SIGNAL(frameUpdated(QImage)), this, SLOT(slotRenderZoneUpdated(QImage))); + Q_ASSERT(b); + } // Update the scope for the new monitor. forceUpdate(true); @@ -86,7 +89,7 @@ void AbstractGfxScopeWidget::slotRenderZoneUpdated(QImage frame) void AbstractGfxScopeWidget::slotAutoRefreshToggled(bool autoRefresh) { - if (autoRefresh) { + if (autoRefresh && m_activeRender) { m_activeRender->sendFrameUpdate(); } } diff --git a/src/colorscopes/abstractgfxscopewidget.h b/src/colorscopes/abstractgfxscopewidget.h index 49c48d67..0dc59eb2 100644 --- a/src/colorscopes/abstractgfxscopewidget.h +++ b/src/colorscopes/abstractgfxscopewidget.h @@ -11,31 +11,29 @@ #ifndef ABSTRACTGFXSCOPEWIDGET_H #define ABSTRACTGFXSCOPEWIDGET_H - #include #include #include "abstractscopewidget.h" +#include "renderer.h" class QMenu; -class Monitor; -class Render; +class MonitorManager; class AbstractGfxScopeWidget : public AbstractScopeWidget { Q_OBJECT public: - AbstractGfxScopeWidget(Monitor *projMonitor, Monitor *clipMonitor, bool trackMouse = false, QWidget *parent = 0); + AbstractGfxScopeWidget(MonitorManager *manager, bool trackMouse = false, QWidget *parent = 0); virtual ~AbstractGfxScopeWidget(); // Must be virtual because of inheritance, to avoid memory leaks protected: ///// Variables ///// - Monitor *m_projMonitor; - Monitor *m_clipMonitor; - Render *m_activeRender; + MonitorManager *m_manager; + AbstractRender *m_activeRender; /** @brief Scope renderer. Must emit signalScopeRenderingFinished() when calculation has finished, to allow multi-threading. @@ -53,7 +51,7 @@ public slots: /** @brief Must be called when the active monitor has shown a new frame. This slot must be connected in the implementing class, it is *not* done in this abstract class. */ - void slotActiveMonitorChanged(bool isClipMonitor); + void slotActiveMonitorChanged(); protected slots: virtual void slotAutoRefreshToggled(bool autoRefresh); diff --git a/src/colorscopes/histogram.cpp b/src/colorscopes/histogram.cpp index d0042b6d..60b32a12 100644 --- a/src/colorscopes/histogram.cpp +++ b/src/colorscopes/histogram.cpp @@ -14,8 +14,8 @@ #include "histogram.h" #include "renderer.h" -Histogram::Histogram(Monitor *projMonitor, Monitor *clipMonitor, QWidget *parent) : - AbstractGfxScopeWidget(projMonitor, clipMonitor, false, parent) +Histogram::Histogram(MonitorManager *manager, QWidget *parent) : + AbstractGfxScopeWidget(manager, false, parent) { ui = new Ui::Histogram_UI(); ui->setupUi(this); diff --git a/src/colorscopes/histogram.h b/src/colorscopes/histogram.h index 81849a36..19b86da2 100644 --- a/src/colorscopes/histogram.h +++ b/src/colorscopes/histogram.h @@ -20,7 +20,7 @@ class Histogram : public AbstractGfxScopeWidget { Q_OBJECT public: - Histogram(Monitor *projMonitor, Monitor *clipMonitor, QWidget *parent = 0); + Histogram(MonitorManager *manager, QWidget *parent = 0); ~Histogram(); QString widgetName() const; diff --git a/src/colorscopes/rgbparade.cpp b/src/colorscopes/rgbparade.cpp index 2586cff8..396697fc 100644 --- a/src/colorscopes/rgbparade.cpp +++ b/src/colorscopes/rgbparade.cpp @@ -16,8 +16,8 @@ #include "rgbparade.h" #include "rgbparadegenerator.h" -RGBParade::RGBParade(Monitor *projMonitor, Monitor *clipMonitor, QWidget *parent) : - AbstractGfxScopeWidget(projMonitor, clipMonitor, true, parent) +RGBParade::RGBParade(MonitorManager *manager, QWidget *parent) : + AbstractGfxScopeWidget(manager, true, parent) { ui = new Ui::RGBParade_UI(); ui->setupUi(this); diff --git a/src/colorscopes/rgbparade.h b/src/colorscopes/rgbparade.h index a51b0453..db178b20 100644 --- a/src/colorscopes/rgbparade.h +++ b/src/colorscopes/rgbparade.h @@ -23,7 +23,7 @@ class RGBParadeGenerator; class RGBParade : public AbstractGfxScopeWidget { public: - RGBParade(Monitor *projMonitor, Monitor *clipMonitor, QWidget *parent = 0); + RGBParade(MonitorManager *manager, QWidget *parent = 0); ~RGBParade(); QString widgetName() const; diff --git a/src/colorscopes/vectorscope.cpp b/src/colorscopes/vectorscope.cpp index 9bf50aff..2802a5d4 100644 --- a/src/colorscopes/vectorscope.cpp +++ b/src/colorscopes/vectorscope.cpp @@ -42,8 +42,8 @@ const QPointF YPbPr_Mg(.331, .419); const QPointF YPbPr_Yl(-.5, .081); -Vectorscope::Vectorscope(Monitor *projMonitor, Monitor *clipMonitor, QWidget *parent) : - AbstractGfxScopeWidget(projMonitor, clipMonitor, true, parent), +Vectorscope::Vectorscope(MonitorManager *manager, QWidget *parent) : + AbstractGfxScopeWidget(manager, true, parent), m_gain(1) { ui = new Ui::Vectorscope_UI(); diff --git a/src/colorscopes/vectorscope.h b/src/colorscopes/vectorscope.h index 38fa5a94..de54440d 100644 --- a/src/colorscopes/vectorscope.h +++ b/src/colorscopes/vectorscope.h @@ -28,7 +28,7 @@ class Vectorscope : public AbstractGfxScopeWidget { Q_OBJECT public: - Vectorscope(Monitor *projMonitor, Monitor *clipMonitor, QWidget *parent = 0); + Vectorscope(MonitorManager *manager, QWidget *parent = 0); ~Vectorscope(); QString widgetName() const; diff --git a/src/colorscopes/waveform.cpp b/src/colorscopes/waveform.cpp index 47597ac3..bf09234b 100644 --- a/src/colorscopes/waveform.cpp +++ b/src/colorscopes/waveform.cpp @@ -25,8 +25,8 @@ const QSize Waveform::m_textWidth(35,0); const int Waveform::m_paddingBottom(20); -Waveform::Waveform(Monitor *projMonitor, Monitor *clipMonitor, QWidget *parent) : - AbstractGfxScopeWidget(projMonitor, clipMonitor, true, parent) +Waveform::Waveform(MonitorManager *manager, QWidget *parent) : + AbstractGfxScopeWidget(manager, true, parent) ,ui(NULL) { ui = new Ui::Waveform_UI(); diff --git a/src/colorscopes/waveform.h b/src/colorscopes/waveform.h index f1d648ab..ce031397 100644 --- a/src/colorscopes/waveform.h +++ b/src/colorscopes/waveform.h @@ -22,7 +22,7 @@ class Waveform : public AbstractGfxScopeWidget { Q_OBJECT public: - Waveform(Monitor *projMonitor, Monitor *clipMonitor, QWidget *parent = 0); + Waveform(MonitorManager *manager, QWidget *parent = 0); ~Waveform(); virtual QString widgetName() const; diff --git a/src/kdenlivedoc.h b/src/kdenlivedoc.h index 1e065193..5577a9f4 100644 --- a/src/kdenlivedoc.h +++ b/src/kdenlivedoc.h @@ -224,7 +224,6 @@ signals: void addProjectClip(DocClipBase *, bool getInfo = true); void signalDeleteProjectClip(const QString &); void updateClipDisplay(const QString&); - void deleteTimelineClip(const QString&); void progressInfo(const QString &, int); /** @brief Informs that the document status has been changed. diff --git a/src/kdenlivesettings.kcfg b/src/kdenlivesettings.kcfg index 7c9db250..a3650443 100644 --- a/src/kdenlivesettings.kcfg +++ b/src/kdenlivesettings.kcfg @@ -312,64 +312,39 @@ 0 - - - video4linux2 - - /dev/video0 - - - mjpeg + + + - - - mp2 + + + hw:0,0 - + - avi + f=mpeg acodec=mp2 ab=128k ar=48000 vcodec=mpeg2video minrate=0 b=4000k bf=2 b_strategy=1 trellis=1 - + - avi - - - - - oss - - - - - /dev/dsp + mpeg - - - 320x240 - - - - - 15 - - - - - + + + 0 - - - -qscale 1 -ab 224k + + + false diff --git a/src/kdenlivesettingsdialog.cpp b/src/kdenlivesettingsdialog.cpp index 36a6d645..c87fe9ca 100644 --- a/src/kdenlivesettingsdialog.cpp +++ b/src/kdenlivesettingsdialog.cpp @@ -53,7 +53,7 @@ KdenliveSettingsDialog::KdenliveSettingsDialog(const QMap& map m_shuttleModified(false), m_mappable_actions(mappable_actions) { - + KdenliveSettings::setV4l_format(0); QWidget *p1 = new QWidget; m_configMisc.setupUi(p1); m_page1 = addPage(p1, i18n("Misc"), "configure"); @@ -94,12 +94,13 @@ KdenliveSettingsDialog::KdenliveSettingsDialog(const QMap& map m_configCapture.setupUi(p4); #if !defined(Q_WS_MAC) && !defined(Q_OS_FREEBSD) - V4lCaptureHandler v4l(NULL); + m_configCapture.kcfg_v4l_parameters->setMaximumHeight(QFontMetrics(font()).lineSpacing() * 4); + // Video 4 Linux device detection for (int i = 0; i < 10; i++) { QString path = "/dev/video" + QString::number(i); if (QFile::exists(path)) { - QStringList deviceInfo = v4l.getDeviceName(path); + QStringList deviceInfo = V4lCaptureHandler::getDeviceName(path.toUtf8().constData()); if (!deviceInfo.isEmpty()) { m_configCapture.kcfg_detectedv4ldevices->addItem(deviceInfo.at(0), path); m_configCapture.kcfg_detectedv4ldevices->setItemData(m_configCapture.kcfg_detectedv4ldevices->count() - 1, deviceInfo.at(1), Qt::UserRole + 1); @@ -107,6 +108,10 @@ KdenliveSettingsDialog::KdenliveSettingsDialog(const QMap& map } } connect(m_configCapture.kcfg_detectedv4ldevices, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdatev4lDevice())); + connect(m_configCapture.kcfg_v4l_format, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdatev4lCaptureProfile())); + connect(m_configCapture.kcfg_v4l_captureaudio, SIGNAL(toggled(bool)), m_configCapture.kcfg_v4l_alsadevice, SLOT(setEnabled(bool))); + + slotUpdatev4lDevice(); #endif m_page4 = addPage(p4, i18n("Capture"), "media-record"); @@ -181,15 +186,6 @@ KdenliveSettingsDialog::KdenliveSettingsDialog(const QMap& map connect(m_configTranscode.button_delete, SIGNAL(clicked()), this, SLOT(slotDeleteTranscode())); connect(m_configTranscode.profiles_list, SIGNAL(itemChanged(QTreeWidgetItem *, int)), this, SLOT(slotDialogModified())); - connect(m_configCapture.kcfg_video4vdevice, SIGNAL(editingFinished()), this, SLOT(rebuildVideo4Commands())); - connect(m_configCapture.kcfg_video4adevice, SIGNAL(editingFinished()), this, SLOT(rebuildVideo4Commands())); - connect(m_configCapture.kcfg_video4vcodec, SIGNAL(editingFinished()), this, SLOT(rebuildVideo4Commands())); - connect(m_configCapture.kcfg_video4acodec, SIGNAL(editingFinished()), this, SLOT(rebuildVideo4Commands())); - connect(m_configCapture.kcfg_video4vformat, SIGNAL(editingFinished()), this, SLOT(rebuildVideo4Commands())); - connect(m_configCapture.kcfg_video4aformat, SIGNAL(editingFinished()), this, SLOT(rebuildVideo4Commands())); - connect(m_configCapture.kcfg_video4size, SIGNAL(editingFinished()), this, SLOT(rebuildVideo4Commands())); - connect(m_configCapture.kcfg_video4rate, SIGNAL(editingFinished()), this, SLOT(rebuildVideo4Commands())); - connect(m_configCapture.kcfg_rmd_capture_audio, SIGNAL(clicked(bool)), m_configCapture.audio_group, SLOT(setVisible(bool))); m_configCapture.audio_group->setVisible(KdenliveSettings::rmd_capture_audio()); @@ -356,6 +352,7 @@ void KdenliveSettingsDialog::initDevices() if (line.contains("capture")) { deviceId = line.section(':', 0, 0); m_configCapture.kcfg_rmd_alsa_device->addItem(line.section(':', 1, 1), "plughw:" + QString::number(deviceId.section('-', 0, 0).toInt()) + ',' + QString::number(deviceId.section('-', 1, 1).toInt())); + m_configCapture.kcfg_v4l_alsadevice->addItem(line.section(':', 1, 1), "hw:" + QString::number(deviceId.section('-', 0, 0).toInt()) + ',' + QString::number(deviceId.section('-', 1, 1).toInt())); } } file.close(); @@ -367,12 +364,22 @@ void KdenliveSettingsDialog::initDevices() m_configSdl.kcfg_audio_device->setCurrentIndex(ix); KdenliveSettings::setAudio_device(ix); } + if (!KdenliveSettings::rmd_alsadevicename().isEmpty()) { // Select correct alsa device int ix = m_configCapture.kcfg_rmd_alsa_device->findData(KdenliveSettings::rmd_alsadevicename()); m_configCapture.kcfg_rmd_alsa_device->setCurrentIndex(ix); KdenliveSettings::setRmd_alsa_device(ix); } + + if (!KdenliveSettings::v4l_alsadevicename().isEmpty()) { + // Select correct alsa device + int ix = m_configCapture.kcfg_v4l_alsadevice->findData(KdenliveSettings::v4l_alsadevicename()); + m_configCapture.kcfg_v4l_alsadevice->setCurrentIndex(ix); + KdenliveSettings::setV4l_alsadevice(ix); + } + + loadCurrentV4lProfileInfo(); } @@ -389,6 +396,7 @@ void KdenliveSettingsDialog::slotReadAudioDevices() QString device = data.section(':', 1, 1).section(' ', -1); m_configSdl.kcfg_audio_device->addItem(data.section(':', -1), "plughw:" + card + ',' + device); m_configCapture.kcfg_rmd_alsa_device->addItem(data.section(':', -1), "plughw:" + card + ',' + device); + m_configCapture.kcfg_v4l_alsadevice->addItem(data.section(':', -1), "hw:" + card + ',' + device); } } } @@ -488,14 +496,6 @@ void KdenliveSettingsDialog::slotUpdateShuttleDevice(int ix) #endif /* NO_JOGSHUTTLE */ -void KdenliveSettingsDialog::rebuildVideo4Commands() -{ - QString captureCommand; - if (!m_configCapture.kcfg_video4adevice->text().isEmpty()) captureCommand = "-f " + m_configCapture.kcfg_video4aformat->text() + " -i " + m_configCapture.kcfg_video4adevice->text() + " -acodec " + m_configCapture.kcfg_video4acodec->text(); - - captureCommand += " -f " + m_configCapture.kcfg_video4vformat->text() + " -s " + m_configCapture.kcfg_video4size->text() + " -r " + QString::number(m_configCapture.kcfg_video4rate->value()) + " -i " + m_configCapture.kcfg_video4vdevice->text() + " -vcodec " + m_configCapture.kcfg_video4vcodec->text(); - m_configCapture.kcfg_video4capture->setText(captureCommand); -} void KdenliveSettingsDialog::updateWidgets() { // Revert widgets to last saved state (for example when user pressed "Cancel") @@ -569,6 +569,12 @@ void KdenliveSettingsDialog::updateSettings() updateCapturePath = true; } + if ((uint) m_configCapture.kcfg_v4l_format->currentIndex() != KdenliveSettings::v4l_format()) { + saveCurrentV4lProfile(); + KdenliveSettings::setV4l_format(0); + } + + if (updateCapturePath) emit updateCaptureFolder(); QString value = m_configCapture.kcfg_rmd_alsa_device->itemData(m_configCapture.kcfg_rmd_alsa_device->currentIndex()).toString(); @@ -576,6 +582,11 @@ void KdenliveSettingsDialog::updateSettings() KdenliveSettings::setRmd_alsadevicename(value); } + value = m_configCapture.kcfg_v4l_alsadevice->itemData(m_configCapture.kcfg_v4l_alsadevice->currentIndex()).toString(); + if (value != KdenliveSettings::v4l_alsadevicename()) { + KdenliveSettings::setV4l_alsadevicename(value); + } + value = m_configCapture.kcfg_rmd_audio_freq->itemText(m_configCapture.kcfg_rmd_audio_freq->currentIndex()); kDebug() << "// AUDIO FREQ VALUE: " << value << ", CURRENT: " << KdenliveSettings::rmd_freq() << ", IX: " << m_configCapture.kcfg_rmd_audio_freq->currentIndex(); if (value != KdenliveSettings::rmd_freq()) { @@ -767,11 +778,97 @@ void KdenliveSettingsDialog::slotUpdatev4lDevice() { QString device = m_configCapture.kcfg_detectedv4ldevices->itemData(m_configCapture.kcfg_detectedv4ldevices->currentIndex()).toString(); if (!device.isEmpty()) m_configCapture.kcfg_video4vdevice->setText(device); - QString size = m_configCapture.kcfg_detectedv4ldevices->itemData(m_configCapture.kcfg_detectedv4ldevices->currentIndex(), Qt::UserRole + 1).toString(); - if (!size.isEmpty()) m_configCapture.kcfg_video4size->setText(size); - rebuildVideo4Commands(); + QString info = m_configCapture.kcfg_detectedv4ldevices->itemData(m_configCapture.kcfg_detectedv4ldevices->currentIndex(), Qt::UserRole + 1).toString(); + + m_configCapture.kcfg_v4l_format->blockSignals(true); + m_configCapture.kcfg_v4l_format->clear(); + + QString vl4ProfilePath = KStandardDirs::locateLocal("appdata", "profiles/video4linux"); + if (QFile::exists(vl4ProfilePath)) m_configCapture.kcfg_v4l_format->addItem(i18n("Current settings")); + + QStringList pixelformats = info.split(">", QString::SkipEmptyParts); + QString itemSize; + QString pixelFormat; + QStringList itemRates; + for (int i = 0; i < pixelformats.count(); i++) { + QString format = pixelformats.at(i).section(':', 0, 0); + QStringList sizes = pixelformats.at(i).split(":", QString::SkipEmptyParts); + pixelFormat = sizes.takeFirst(); + for (int j = 0; j < sizes.count(); j++) { + itemSize = sizes.at(j).section("=", 0, 0); + itemRates = sizes.at(j).section("=", 1, 1).split(",", QString::SkipEmptyParts); + for (int k = 0; k < itemRates.count(); k++) { + m_configCapture.kcfg_v4l_format->addItem("[" + format + "] " + itemSize + " (" + itemRates.at(k) + ")", QStringList() << format << itemSize.section('x', 0, 0) << itemSize.section('x', 1, 1) << itemRates.at(k).section('/', 0, 0) << itemRates.at(k).section('/', 1, 1)); + } + } + } + m_configCapture.kcfg_v4l_format->blockSignals(false); + slotUpdatev4lCaptureProfile(); } +void KdenliveSettingsDialog::slotUpdatev4lCaptureProfile() +{ + QStringList info = m_configCapture.kcfg_v4l_format->itemData(m_configCapture.kcfg_v4l_format->currentIndex(), Qt::UserRole).toStringList(); + if (info.isEmpty()) { + // No auto info, display the current ones + loadCurrentV4lProfileInfo(); + return; + } + m_configCapture.p_size->setText(info.at(1) + 'x' + info.at(2)); + m_configCapture.p_fps->setText(info.at(3) + '/' + info.at(4)); + m_configCapture.p_aspect->setText("1/1"); + m_configCapture.p_display->setText(info.at(1) + '/' + info.at(2)); + m_configCapture.p_colorspace->setText(ProfilesDialog::getColorspaceDescription(601)); + m_configCapture.p_progressive->setText(i18n("Progressive")); + + QString vl4ProfilePath = KStandardDirs::locateLocal("appdata", "profiles/video4linux"); + if (!QFile::exists(vl4ProfilePath)) saveCurrentV4lProfile(); +} + +void KdenliveSettingsDialog::loadCurrentV4lProfileInfo() +{ + QString vl4ProfilePath = KStandardDirs::locateLocal("appdata", "profiles/video4linux"); + MltVideoProfile prof; + if (!QFile::exists(vl4ProfilePath)) { + // No default formats found, build one + prof.width = 320; + prof.height = 200; + prof.frame_rate_num = 15; + prof.frame_rate_den = 1; + prof.display_aspect_num = 4; + prof.display_aspect_den = 3; + prof.sample_aspect_num = 1; + prof.sample_aspect_den = 1; + prof.progressive = 1; + prof.colorspace = 601; + ProfilesDialog::saveProfile(prof, vl4ProfilePath); + } + else prof = ProfilesDialog::getVideoProfile(vl4ProfilePath); + m_configCapture.p_size->setText(QString::number(prof.width) + 'x' + QString::number(prof.height)); + m_configCapture.p_fps->setText(QString::number(prof.frame_rate_num) + '/' + QString::number(prof.frame_rate_den)); + m_configCapture.p_aspect->setText(QString::number(prof.sample_aspect_num) + '/' + QString::number(prof.sample_aspect_den)); + m_configCapture.p_display->setText(QString::number(prof.display_aspect_num) + '/' + QString::number(prof.display_aspect_den)); + m_configCapture.p_colorspace->setText(ProfilesDialog::getColorspaceDescription(prof.colorspace)); + if (prof.progressive) m_configCapture.p_progressive->setText(i18n("Progressive")); +} + +void KdenliveSettingsDialog::saveCurrentV4lProfile() +{ + MltVideoProfile profile; + profile.description = "Video4Linux capture"; + profile.colorspace = ProfilesDialog::getColorspaceFromDescription(m_configCapture.p_colorspace->text()); + profile.width = m_configCapture.p_size->text().section('x', 0, 0).toInt(); + profile.height = m_configCapture.p_size->text().section('x', 1, 1).toInt(); + profile.sample_aspect_num = m_configCapture.p_aspect->text().section('/', 0, 0).toInt(); + profile.sample_aspect_den = m_configCapture.p_aspect->text().section('/', 1, 1).toInt(); + profile.display_aspect_num = m_configCapture.p_display->text().section('/', 0, 0).toInt(); + profile.display_aspect_den = m_configCapture.p_display->text().section('/', 1, 1).toInt(); + profile.frame_rate_num = m_configCapture.p_fps->text().section('/', 0, 0).toInt(); + profile.frame_rate_den = m_configCapture.p_fps->text().section('/', 1, 1).toInt(); + profile.progressive = m_configCapture.p_progressive->text() == i18n("Progressive"); + QString vl4ProfilePath = KStandardDirs::locateLocal("appdata", "profiles/video4linux"); + ProfilesDialog::saveProfile(profile, vl4ProfilePath); +} #include "kdenlivesettingsdialog.moc" diff --git a/src/kdenlivesettingsdialog.h b/src/kdenlivesettingsdialog.h index 1f06efc7..e305d870 100644 --- a/src/kdenlivesettingsdialog.h +++ b/src/kdenlivesettingsdialog.h @@ -53,7 +53,6 @@ protected slots: private slots: void slotUpdateDisplay(); - void rebuildVideo4Commands(); #ifndef NO_JOGSHUTTLE void slotCheckShuttle(int state = 0); void slotUpdateShuttleDevice(int ix = 0); @@ -71,6 +70,7 @@ private slots: void slotEnableCaptureFolder(); void slotUpdateHDMIModes(); void slotUpdatev4lDevice(); + void slotUpdatev4lCaptureProfile(); private: KPageWidgetItem *m_page1; @@ -99,6 +99,8 @@ private: void initDevices(); void loadTranscodeProfiles(); void saveTranscodeProfiles(); + void loadCurrentV4lProfileInfo(); + void saveCurrentV4lProfile(); signals: void customChanged(); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 65a39cd5..d94135d1 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -139,6 +139,7 @@ MainWindow::MainWindow(const QString &MltPath, const KUrl & Url, const QString & KXmlGuiWindow(parent), m_activeDocument(NULL), m_activeTimeline(NULL), + m_recMonitor(NULL), m_renderWidget(NULL), #ifndef NO_JOGSHUTTLE m_jogProcess(NULL), @@ -208,11 +209,12 @@ MainWindow::MainWindow(const QString &MltPath, const KUrl & Url, const QString & #ifndef Q_WS_MAC m_recMonitorDock = new QDockWidget(i18n("Record Monitor"), this); m_recMonitorDock->setObjectName("record_monitor"); - m_recMonitor = new RecMonitor("record"); + m_recMonitor = new RecMonitor("record", m_monitorManager); m_recMonitorDock->setWidget(m_recMonitor); connect(m_recMonitor, SIGNAL(addProjectClip(KUrl)), this, SLOT(slotAddProjectClip(KUrl))); connect(m_recMonitor, SIGNAL(showConfigDialog(int, int)), this, SLOT(slotPreferences(int, int))); #endif + m_monitorManager->initMonitors(m_clipMonitor, m_projectMonitor, m_recMonitor); m_notesDock = new QDockWidget(i18n("Project Notes"), this); m_notesDock->setObjectName("notes_widget"); @@ -245,7 +247,7 @@ MainWindow::MainWindow(const QString &MltPath, const KUrl & Url, const QString & m_effectListDock->setWidget(m_effectList); addDockWidget(Qt::TopDockWidgetArea, m_effectListDock); - m_vectorscope = new Vectorscope(m_projectMonitor, m_clipMonitor); + m_vectorscope = new Vectorscope(m_monitorManager); m_vectorscopeDock = new QDockWidget(i18n("Vectorscope"), this); m_vectorscopeDock->setObjectName(m_vectorscope->widgetName()); m_vectorscopeDock->setWidget(m_vectorscope); @@ -255,7 +257,7 @@ MainWindow::MainWindow(const QString &MltPath, const KUrl & Url, const QString & connect(m_vectorscope, SIGNAL(requestAutoRefresh(bool)), this, SLOT(slotUpdateGfxScopeFrameRequest())); m_gfxScopesList.append(m_vectorscopeDock); - m_waveform = new Waveform(m_projectMonitor, m_clipMonitor); + m_waveform = new Waveform(m_monitorManager); m_waveformDock = new QDockWidget(i18n("Waveform"), this); m_waveformDock->setObjectName(m_waveform->widgetName()); m_waveformDock->setWidget(m_waveform); @@ -265,7 +267,7 @@ MainWindow::MainWindow(const QString &MltPath, const KUrl & Url, const QString & connect(m_waveform, SIGNAL(requestAutoRefresh(bool)), this, SLOT(slotUpdateGfxScopeFrameRequest())); m_gfxScopesList.append(m_waveformDock); - m_RGBParade = new RGBParade(m_projectMonitor, m_clipMonitor); + m_RGBParade = new RGBParade(m_monitorManager); m_RGBParadeDock = new QDockWidget(i18n("RGB Parade"), this); m_RGBParadeDock->setObjectName(m_RGBParade->widgetName()); m_RGBParadeDock->setWidget(m_RGBParade); @@ -275,7 +277,7 @@ MainWindow::MainWindow(const QString &MltPath, const KUrl & Url, const QString & connect(m_RGBParade, SIGNAL(requestAutoRefresh(bool)), this, SLOT(slotUpdateGfxScopeFrameRequest())); m_gfxScopesList.append(m_RGBParadeDock); - m_histogram = new Histogram(m_projectMonitor, m_clipMonitor); + m_histogram = new Histogram(m_monitorManager); m_histogramDock = new QDockWidget(i18n("Histogram"), this); m_histogramDock->setObjectName(m_histogram->widgetName()); m_histogramDock->setWidget(m_histogram); @@ -561,12 +563,11 @@ MainWindow::MainWindow(const QString &MltPath, const KUrl & Url, const QString & connect(m_projectMonitorDock, SIGNAL(visibilityChanged(bool)), m_projectMonitor, SLOT(refreshMonitor(bool))); connect(m_clipMonitorDock, SIGNAL(visibilityChanged(bool)), m_clipMonitor, SLOT(refreshMonitor(bool))); //connect(m_monitorManager, SIGNAL(connectMonitors()), this, SLOT(slotConnectMonitors())); - connect(m_monitorManager, SIGNAL(raiseClipMonitor(bool)), this, SLOT(slotRaiseMonitor(bool))); + connect(m_monitorManager, SIGNAL(raiseMonitor(AbstractMonitor *)), this, SLOT(slotRaiseMonitor(AbstractMonitor *))); connect(m_monitorManager, SIGNAL(checkColorScopes()), this, SLOT(slotUpdateColorScopes())); connect(m_effectList, SIGNAL(addEffect(const QDomElement)), this, SLOT(slotAddEffect(const QDomElement))); connect(m_effectList, SIGNAL(reloadEffects()), this, SLOT(slotReloadEffects())); - m_monitorManager->initMonitors(m_clipMonitor, m_projectMonitor); slotConnectMonitors(); // Open or create a file. Command line argument passed in Url has @@ -824,10 +825,10 @@ void MainWindow::slotAddEffect(const QDomElement effect) else m_activeTimeline->projectView()->slotAddEffect(effectToAdd, GenTime(), -1); } -void MainWindow::slotRaiseMonitor(bool clipMonitor) +void MainWindow::slotRaiseMonitor(AbstractMonitor *monitor) { - if (clipMonitor) m_clipMonitorDock->raise(); - else m_projectMonitorDock->raise(); + if (monitor == m_clipMonitor) m_clipMonitorDock->raise(); + else if (monitor == m_projectMonitor) m_projectMonitorDock->raise(); } void MainWindow::slotUpdateClip(const QString &id) @@ -2394,7 +2395,6 @@ void MainWindow::connectDocument(TrackView *trackView, KdenliveDoc *doc) //cha disconnect(m_activeDocument, SIGNAL(signalDeleteProjectClip(const QString &)), this, SLOT(slotDeleteClip(const QString &))); disconnect(m_activeDocument, SIGNAL(updateClipDisplay(const QString &)), m_projectList, SLOT(slotUpdateClip(const QString &))); disconnect(m_activeDocument, SIGNAL(selectLastAddedClip(const QString &)), m_projectList, SLOT(slotSelectClip(const QString &))); - disconnect(m_activeDocument, SIGNAL(deleteTimelineClip(const QString &)), m_activeTimeline, SLOT(slotDeleteClip(const QString &))); disconnect(m_activeTimeline->projectView(), SIGNAL(clipItemSelected(ClipItem*, int, bool)), m_effectStack, SLOT(slotClipItemSelected(ClipItem*, int))); disconnect(m_activeTimeline->projectView(), SIGNAL(clipItemSelected(ClipItem*, int, bool)), this, SLOT(slotActivateEffectStackView(ClipItem*, int, bool))); disconnect(m_activeTimeline->projectView(), SIGNAL(clipItemSelected(ClipItem*, int, bool)), m_projectMonitor, SLOT(slotSetSelectedClip(ClipItem*))); @@ -2450,7 +2450,7 @@ void MainWindow::connectDocument(TrackView *trackView, KdenliveDoc *doc) //cha connect(m_projectList, SIGNAL(findInTimeline(const QString&)), this, SLOT(slotClipInTimeline(const QString&))); - connect(trackView, SIGNAL(cursorMoved()), m_projectMonitor, SLOT(activateMonitor())); + //connect(trackView, SIGNAL(cursorMoved()), m_projectMonitor, SLOT(activateMonitor())); connect(trackView, SIGNAL(insertTrack(int)), this, SLOT(slotInsertTrack(int))); connect(trackView, SIGNAL(deleteTrack(int)), this, SLOT(slotDeleteTrack(int))); connect(trackView, SIGNAL(configTrack(int)), this, SLOT(slotConfigTrack(int))); @@ -2471,7 +2471,6 @@ void MainWindow::connectDocument(TrackView *trackView, KdenliveDoc *doc) //cha connect(doc, SIGNAL(updateClipDisplay(const QString &)), m_projectList, SLOT(slotUpdateClip(const QString &))); connect(doc, SIGNAL(selectLastAddedClip(const QString &)), m_projectList, SLOT(slotSelectClip(const QString &))); - connect(doc, SIGNAL(deleteTimelineClip(const QString &)), trackView, SLOT(slotDeleteClip(const QString &))); connect(doc, SIGNAL(docModified(bool)), this, SLOT(slotUpdateDocumentState(bool))); connect(doc, SIGNAL(guidesUpdated()), this, SLOT(slotGuidesUpdated())); connect(m_notesWidget, SIGNAL(textChanged()), doc, SLOT(setModified())); @@ -4178,9 +4177,11 @@ void MainWindow::slotDoUpdateGfxScopeFrameRequest() m_projectMonitor->render->sendFrameForAnalysis = false; } m_clipMonitor->render->sendFrameForAnalysis = false; + m_recMonitor->analyseFrames(false); } else { m_projectMonitor->render->sendFrameForAnalysis = true; m_clipMonitor->render->sendFrameForAnalysis = true; + m_recMonitor->analyseFrames(true); } } @@ -4214,28 +4215,28 @@ void MainWindow::slotDoUpdateAudioScopeFrameRequest() void MainWindow::slotUpdateColorScopes() { bool request = false; + kDebug()<<"// UPDATE SCOPES"; for (int i = 0; i < m_gfxScopesList.count(); i++) { // Check if we need the renderer to send a new frame for update if (!m_gfxScopesList.at(i)->widget()->visibleRegion().isEmpty() && !(static_cast(m_gfxScopesList.at(i)->widget())->autoRefreshEnabled())) request = true; - static_cast(m_gfxScopesList.at(i)->widget())->slotActiveMonitorChanged(m_clipMonitor->isActive()); + static_cast(m_gfxScopesList.at(i)->widget())->slotActiveMonitorChanged(); } if (request) { - if (m_clipMonitor->isActive()) m_clipMonitor->render->sendFrameUpdate(); - else m_projectMonitor->render->sendFrameUpdate(); + m_monitorManager->activeRenderer()->sendFrameUpdate(); } } void MainWindow::slotOpenStopmotion() { if (m_stopmotion == NULL) { - m_stopmotion = new StopmotionWidget(m_activeDocument->projectFolder(), m_stopmotion_actions->actions(), this); + m_stopmotion = new StopmotionWidget(m_monitorManager, m_activeDocument->projectFolder(), m_stopmotion_actions->actions(), this); connect(m_stopmotion, SIGNAL(addOrUpdateSequence(const QString)), m_projectList, SLOT(slotAddOrUpdateSequence(const QString))); - for (int i = 0; i < m_gfxScopesList.count(); i++) { + //for (int i = 0; i < m_gfxScopesList.count(); i++) { // Check if we need the renderer to send a new frame for update /*if (!m_scopesList.at(i)->widget()->visibleRegion().isEmpty() && !(static_cast(m_scopesList.at(i)->widget())->autoRefreshEnabled())) request = true;*/ - connect(m_stopmotion, SIGNAL(gotFrame(QImage)), static_cast(m_gfxScopesList.at(i)->widget()), SLOT(slotRenderZoneUpdated(QImage))); + //connect(m_stopmotion, SIGNAL(gotFrame(QImage)), static_cast(m_gfxScopesList.at(i)->widget()), SLOT(slotRenderZoneUpdated(QImage))); //static_cast(m_scopesList.at(i)->widget())->slotMonitorCapture(); - } + //} } m_stopmotion->show(); } diff --git a/src/mainwindow.h b/src/mainwindow.h index 703fc625..523bfc9a 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -345,7 +345,7 @@ private slots: /** @brief Reflects setting changes to the GUI. */ void updateConfiguration(); void slotConnectMonitors(); - void slotRaiseMonitor(bool clipMonitor); + void slotRaiseMonitor(AbstractMonitor *monitor); void slotUpdateClip(const QString &id); void slotUpdateMousePosition(int pos); void slotAddEffect(const QDomElement effect); diff --git a/src/mltdevicecapture.cpp b/src/mltdevicecapture.cpp new file mode 100644 index 00000000..f36fda01 --- /dev/null +++ b/src/mltdevicecapture.cpp @@ -0,0 +1,406 @@ +/*************************************************************************** + 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 "kdenlivesettings.h" +#include "definitions.h" +//#include "recmonitor.h" +//#include "renderer.h" +#include "blackmagic/devices.h" + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + + + +static void consumer_gl_frame_show(mlt_consumer, 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); +} + +static void rec_consumer_frame_show(mlt_consumer, MltDeviceCapture * self, mlt_frame frame_ptr) +{ + Mlt::Frame frame(frame_ptr); + if (!frame.is_valid()) return; + self->gotCapturedFrame(frame); +} + +static void rec_consumer_frame_preview(mlt_consumer, MltDeviceCapture * self, mlt_frame frame_ptr) +{ + Mlt::Frame frame(frame_ptr); + if (!frame.is_valid()) return; + if (self->sendFrameForAnalysis && frame_ptr->convert_image) { + self->emitFrameUpdated(frame); + } + if (self->doCapture) { + self->doCapture = false; + self->saveFrame(frame); + } + +/* if (self->analyseAudio) { + self->showAudio(frame); + } + if (frame.get_double("_speed") == 0.0) { + self->emitConsumerStopped(); + } else if (frame.get_double("_speed") < 0.0 && mlt_frame_get_position(frame_ptr) <= 0) { + self->pause(); + self->emitConsumerStopped(); + }*/ +} + + +MltDeviceCapture::MltDeviceCapture(QString profile, VideoPreviewContainer *surface, QWidget *parent) : + AbstractRender(parent), + doCapture(false), + sendFrameForAnalysis(false), + m_mltConsumer(NULL), + m_mltProducer(NULL), + m_mltProfile(NULL), + m_captureDisplayWidget(surface), + m_winid((int) surface->winId()), + m_analyseAudio(KdenliveSettings::monitor_audio()) +{ + if (profile.isEmpty()) profile = KdenliveSettings::current_profile(); + buildConsumer(profile); +} + +MltDeviceCapture::~MltDeviceCapture() +{ + if (m_mltConsumer) delete m_mltConsumer; + if (m_mltProducer) delete m_mltProducer; + if (m_mltProfile) delete m_mltProfile; +} + +void MltDeviceCapture::buildConsumer(const QString &profileName) +{ + if (!profileName.isEmpty()) m_activeProfile = profileName; + + if (m_mltProfile) delete m_mltProfile; + + char *tmp = qstrdup(m_activeProfile.toUtf8().constData()); + setenv("MLT_PROFILE", tmp, 1); + m_mltProfile = new Mlt::Profile(tmp); + m_mltProfile->get_profile()->is_explicit = 1; + delete[] tmp; + + QString videoDriver = KdenliveSettings::videodrivername(); + if (!videoDriver.isEmpty()) { + if (videoDriver == "x11_noaccel") { + setenv("SDL_VIDEO_YUV_HWACCEL", "0", 1); + videoDriver = "x11"; + } else { + unsetenv("SDL_VIDEO_YUV_HWACCEL"); + } + } + setenv("SDL_VIDEO_ALLOW_SCREENSAVER", "1", 1); + + if (m_winid == 0) { + // OpenGL monitor + m_mltConsumer = new Mlt::Consumer(*m_mltProfile, "sdl_audio"); + m_mltConsumer->set("preview_off", 1); + m_mltConsumer->set("preview_format", mlt_image_rgb24a); + m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) consumer_gl_frame_show); + } else { + m_mltConsumer = new Mlt::Consumer(*m_mltProfile, "sdl_preview"); + m_mltConsumer->set("window_id", m_winid); + } + 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"); + + m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) rec_consumer_frame_preview); + + 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", 1); + //m_mltConsumer->set("buffer", 1); + //m_mltConsumer->set("real_time", 0); +} + +void MltDeviceCapture::stop() +{ + if (m_mltConsumer) { + m_mltConsumer->stop(); + //if (!m_mltConsumer->is_stopped()) m_mltConsumer->stop(); + delete m_mltConsumer; + m_mltConsumer = NULL; + } + kDebug()<<"STOPPING cap"; + if (m_mltProducer) { + QList prods; + Mlt::Service service(m_mltProducer->parent().get_service()); + mlt_service_lock(service.get_service()); +kDebug()<<"STOPPING cap 2"; + if (service.type() == tractor_type) { + kDebug()<<"STOPPING cap 3"; + 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 == "transition") { + nextservicetodisconnect = nextservice; + nextservice = mlt_service_producer(nextservice); + mlt_field_disconnect_service(field->get_field(), nextservicetodisconnect); + if (nextservice == NULL) break; + properties = MLT_SERVICE_PROPERTIES(nextservice); + mlt_type = mlt_properties_get(properties, "mlt_type"); + resource = mlt_properties_get(properties, "mlt_service"); + } + for (int trackNb = tractor.count() - 1; trackNb >= 0; --trackNb) { + Mlt::Producer trackProducer(tractor.track(trackNb)); + Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service()); + if (trackPlaylist.type() == playlist_type) { + for (int i = 0; i < trackPlaylist.count();i++) { + // We need to manually decrease the ref count and close the producer, otherwise + // the video4linux device stays open, seems like a bug in MLT that is not cleaning properly + mlt_properties props = MLT_PRODUCER_PROPERTIES(trackPlaylist.get_clip(i)->get_parent()); + while (mlt_properties_ref_count(props) > 0) mlt_properties_dec_ref(props); + mlt_producer_close(trackPlaylist.get_clip(i)->get_parent()); + } + mlt_playlist_close(trackPlaylist.get_playlist()); + //trackPlaylist.clear(); + } + } + delete field; + field = NULL; + } + mlt_service_unlock(service.get_service()); + delete m_mltProducer; + kDebug()<<"/// STOP REC PROD"; + m_mltProducer = NULL; + } +} + + +void MltDeviceCapture::doRefresh() +{ + if (m_mltConsumer) m_mltConsumer->set("refresh", 1); +} + + +void MltDeviceCapture::emitFrameUpdated(Mlt::Frame& frame) +{ + mlt_image_format format = mlt_image_rgb24a; + int width = 0; + int height = 0; + const uchar* image = frame.get_image(format, width, height); + QImage qimage(width, height, QImage::Format_ARGB32); + memcpy(qimage.bits(), image, width * height * 4); + emit frameUpdated(qimage.rgbSwapped()); +} + +void MltDeviceCapture::showFrame(Mlt::Frame& frame) +{ + mlt_image_format format = mlt_image_rgb24a; + int width = 0; + int height = 0; + const uchar* image = frame.get_image(format, width, height); + QImage qimage(width, height, QImage::Format_ARGB32_Premultiplied); + memcpy(qimage.scanLine(0), image, width * height * 4); + emit showImageSignal(qimage); + if (m_analyseAudio) showAudio(frame); + if (sendFrameForAnalysis && frame.get_frame()->convert_image) { + 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; + int16_t* data = (int16_t*)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. + QVector sampleVector(samples*num_channels); + memcpy(sampleVector.data(), data, samples*num_channels*sizeof(int16_t)); + + if (samples > 0) { + emit audioSamplesSignal(sampleVector, freq, num_channels, samples); + } +} + +bool MltDeviceCapture::slotStartPreview(const QString &producer) +{ + //stop(); + if (m_mltConsumer == NULL) buildConsumer(); + /*if (m_mltConsumer) delete m_mltConsumer; + if (m_mltProducer) delete m_mltProducer; + if (m_mltProfile) delete m_mltProfile; + + char *tmp = qstrdup(m_activeProfile.toUtf8().constData()); + setenv("MLT_PROFILE", tmp, 1); + m_mltProfile = new Mlt::Profile(tmp); + delete[] tmp; + m_mltProfile->get_profile()->is_explicit = 1; + + m_mltConsumer = new Mlt::Consumer(*m_mltProfile, "sdl_preview"); + + m_mltConsumer->set("window_id", m_winid); + m_mltConsumer->set("real_time", 0); +// m_mltConsumer->set("buffer", 1); + m_mltConsumer->set("resize", 1); + m_mltConsumer->set("progressive", 1); + m_mltConsumer->set("rescale", "nearest");*/ + + //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()); + + char *tmp = qstrdup(producer.toUtf8().constData()); + + m_mltProducer = new Mlt::Producer(*m_mltProfile, tmp); + delete[] tmp; + if (m_mltProducer == NULL || !m_mltProducer->is_valid()) { + kDebug()<<"//// ERROR CREATRING PROD"; + return false; + } + m_mltConsumer->connect(*m_mltProducer); + if (m_mltConsumer->start() == -1) { + delete m_mltConsumer; + m_mltConsumer = NULL; + return 0; + } + return 1; +} + +void MltDeviceCapture::gotCapturedFrame(Mlt::Frame& frame) +{ + mlt_image_format format = mlt_image_rgb24a; + int width = 0; + int height = 0; + const uchar* image = frame.get_image(format, width, height); + QImage qimage(width, height, QImage::Format_ARGB32); + memcpy(qimage.bits(), image, width * height * 4); + m_captureDisplayWidget->setImage(qimage.rgbSwapped()); +} + +void MltDeviceCapture::saveFrame(Mlt::Frame& frame) +{ + mlt_image_format format = mlt_image_rgb24a; + int width = 0; + int height = 0; + const uchar* image = frame.get_image(format, width, height); + QImage qimage(width, height, QImage::Format_ARGB32); + memcpy(qimage.bits(), image, width * height * 4); + qimage.rgbSwapped().save(m_capturePath); + emit frameSaved(m_capturePath); + m_capturePath.clear(); +} + +void MltDeviceCapture::captureFrame(const QString &path) +{ + m_capturePath = path; + doCapture = true; +} + +bool MltDeviceCapture::slotStartCapture(const QString ¶ms, const QString &path, const QString &playlist) +{ + stop(); + if (m_mltProfile) delete m_mltProfile; + char *tmp = qstrdup(m_activeProfile.toUtf8().constData()); + m_mltProfile = new Mlt::Profile(tmp); + delete[] tmp; + m_mltProfile->get_profile()->is_explicit = 1; + kDebug()<<"-- CREATING CAP: "<set("terminate_on_pause", 1); + delete[] tmp; + + QStringList paramList = params.split(" ", QString::SkipEmptyParts); + char *tmp2; + for (int i = 0; i < paramList.count(); i++) { + tmp = qstrdup(paramList.at(i).section("=", 0, 0).toUtf8().constData()); + tmp2 = qstrdup(paramList.at(i).section("=", 1, 1).toUtf8().constData()); + m_mltConsumer->set(tmp, tmp2); + delete[] tmp; + delete[] tmp2; + } + + if (m_mltConsumer == NULL || !m_mltConsumer->is_valid()) { + if (m_mltConsumer) { + delete m_mltConsumer; + m_mltConsumer = NULL; + } + return false; + } + + // FIXME: the event object returned by the listen gets leaked... + m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) rec_consumer_frame_show); + + //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()); + tmp = qstrdup(playlist.toUtf8().constData()); + m_mltProducer = new Mlt::Producer(*m_mltProfile, "xml-string", tmp); + delete[] tmp; + + if (m_mltProducer == NULL || !m_mltProducer->is_valid()) { + kDebug()<<"//// ERROR CREATRING PROD"; + return false; + } + + m_mltConsumer->connect(*m_mltProducer); + if (m_mltConsumer->start() == -1) { + delete m_mltConsumer; + m_mltConsumer = NULL; + return 0; + } + return 1; +} + + diff --git a/src/mltdevicecapture.h b/src/mltdevicecapture.h new file mode 100644 index 00000000..461c8295 --- /dev/null +++ b/src/mltdevicecapture.h @@ -0,0 +1,127 @@ +/*************************************************************************** + 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. + */ + +#ifndef MLTDEVICECAPTURE_H +#define MLTDEVICECAPTURE_H + +#include "gentime.h" +#include "definitions.h" +#include "abstractmonitor.h" +#include "mlt/framework/mlt_types.h" + +namespace Mlt +{ +class Consumer; +class Playlist; +class Tractor; +class Transition; +class Frame; +class Field; +class Producer; +class Filter; +class Profile; +class Service; +}; + +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). */ + MltDeviceCapture(QString profile, VideoPreviewContainer *surface, QWidget *parent = 0); + + /** @brief Destroy the MLT Renderer. */ + ~MltDeviceCapture(); + + bool doCapture; + + /** @brief This property is used to decide if the renderer should convert it's frames to QImage for use in other Kdenlive widgets. */ + bool sendFrameForAnalysis; + + /** @brief Someone needs us to send again a frame. */ + void sendFrameUpdate() {}; + + void emitFrameUpdated(Mlt::Frame&); + void emitFrameNumber(double position); + void emitConsumerStopped(); + void showFrame(Mlt::Frame&); + void showAudio(Mlt::Frame&); + + void saveFrame(Mlt::Frame& frame); + + /** @brief Starts the MLT Video4Linux process. + * @param surface The widget onto which the frame should be painted + */ + bool slotStartCapture(const QString ¶ms, const QString &path, const QString &playlist); + bool slotStartPreview(const QString &producer); + /** @brief A frame arrived from the MLT Video4Linux process. */ + void gotCapturedFrame(Mlt::Frame& frame); + /** @brief Save current frame to file. */ + void captureFrame(const QString &path); + void doRefresh(); + +private: + Mlt::Consumer * m_mltConsumer; + Mlt::Producer * m_mltProducer; + Mlt::Profile *m_mltProfile; + QString m_activeProfile; + + /** @brief The surface onto which the captured frames should be painted. */ + VideoPreviewContainer *m_captureDisplayWidget; + + /** @brief A human-readable description of this renderer. */ + int m_winid; + + /** @brief This property is used to decide if the renderer should send audio data for monitoring. */ + bool m_analyseAudio; + + QString m_capturePath; + + /** @brief Build the MLT Consumer object with initial settings. + * @param profileName The MLT profile to use for the consumer */ + void buildConsumer(const QString &profileName = QString()); + +private slots: + +signals: + /** @brief A frame's image has to be shown. + * + * Used in Mac OS X. */ + void showImageSignal(QImage); + + /** @brief This signal contains the audio of the current frame. */ + void audioSamplesSignal(const QVector&, int freq, int num_channels, int num_samples); + + void frameSaved(const QString); + +public slots: + + /** @brief Stops the consumer. */ + void stop(); +}; + +#endif diff --git a/src/monitor.cpp b/src/monitor.cpp index 96be7c03..efc1d036 100644 --- a/src/monitor.cpp +++ b/src/monitor.cpp @@ -19,8 +19,6 @@ #include "monitor.h" -#include "renderer.h" -#include "monitormanager.h" #include "smallruler.h" #include "docclipbase.h" #include "abstractclipitem.h" @@ -46,14 +44,13 @@ Monitor::Monitor(QString name, MonitorManager *manager, QString profile, QWidget *parent) : - QWidget(parent), + AbstractMonitor(parent), render(NULL), m_name(name), m_monitorManager(manager), m_currentClip(NULL), m_ruler(new SmallRuler(m_monitorManager)), m_overlay(NULL), - m_isActive(false), m_scale(1), m_length(0), m_dragStarted(false), @@ -185,8 +182,6 @@ Monitor::Monitor(QString name, MonitorManager *manager, QString profile, QWidget connect(render, SIGNAL(durationChanged(int)), this, SLOT(adjustRulerSize(int))); connect(render, SIGNAL(rendererStopped(int)), this, SLOT(rendererStopped(int))); - //render->createVideoXWindow(m_ui.video_frame->winId(), -1); - if (name != "clip") { connect(render, SIGNAL(rendererPosition(int)), this, SIGNAL(renderPosition(int))); connect(render, SIGNAL(durationChanged(int)), this, SIGNAL(durationChanged(int))); @@ -230,7 +225,7 @@ QWidget *Monitor::container() return m_videoBox; } -QString Monitor::name() const +const QString Monitor::name() const { return m_name; } @@ -590,14 +585,12 @@ void Monitor::slotExtractCurrentFrame() bool Monitor::isActive() const { - return m_isActive; + return m_monitorManager->isActive(m_name); } void Monitor::activateMonitor() { - if (!m_isActive) { - m_monitorManager->slotSwitchMonitors(m_name == "clip"); - } + m_monitorManager->activateMonitor(m_name); } void Monitor::setTimePos(const QString &pos) @@ -613,7 +606,7 @@ void Monitor::slotSeek() void Monitor::slotSeek(int pos) { - activateMonitor(); + //activateMonitor(); if (render == NULL) return; render->seekToFrame(pos); } @@ -706,7 +699,7 @@ void Monitor::slotForwardOneFrame(int diff) void Monitor::seekCursor(int pos) { - activateMonitor(); + //activateMonitor(); if (m_ruler->slotNewValue(pos)) { checkOverlay(); m_timePos->setValue(pos); @@ -737,21 +730,19 @@ void Monitor::adjustRulerSize(int length) void Monitor::stop() { - m_isActive = false; disconnect(render, SIGNAL(rendererPosition(int)), this, SLOT(seekCursor(int))); if (render) render->stop(); } void Monitor::start() { - m_isActive = true; if (render) render->start(); connect(render, SIGNAL(rendererPosition(int)), this, SLOT(seekCursor(int))); } void Monitor::refreshMonitor(bool visible) { - if (visible && render && !m_isActive) { + if (visible && render) { activateMonitor(); render->doRefresh(); //askForRefresh(); } @@ -759,7 +750,7 @@ void Monitor::refreshMonitor(bool visible) void Monitor::refreshMonitor() { - if (m_isActive) { + if (isActive()) { render->doRefresh(); } } @@ -1028,6 +1019,11 @@ void Monitor::slotShowVolume() m_volumePopup->show(); } +AbstractRender *Monitor::abstractRender() +{ + return render; +} + MonitorRefresh::MonitorRefresh(QWidget* parent) : QWidget(parent) , m_renderer(NULL) diff --git a/src/monitor.h b/src/monitor.h index ff959c80..6e06f685 100644 --- a/src/monitor.h +++ b/src/monitor.h @@ -22,7 +22,9 @@ #include "gentime.h" +#include "renderer.h" #include "timecodedisplay.h" +#include "abstractmonitor.h" #if defined(Q_WS_MAC) || defined(USE_OPEN_GL) #include "videoglwidget.h" #endif @@ -36,17 +38,14 @@ #include #include - -class MonitorManager; -class Render; class SmallRuler; class DocClipBase; class AbstractClipItem; class Transition; class ClipItem; class MonitorEditWidget; - class Monitor; +class MonitorManager; class VideoContainer : public QFrame { @@ -106,16 +105,17 @@ signals: void editMarker(); }; -class Monitor : public QWidget +class Monitor : public AbstractMonitor { Q_OBJECT public: Monitor(QString name, MonitorManager *manager, QString profile = QString(), QWidget *parent = 0); - virtual ~Monitor(); + ~Monitor(); Render *render; + AbstractRender *abstractRender(); void resetProfile(const QString profile); - QString name() const; + const QString name() const; void resetSize(); bool isActive() const; void pause(); @@ -154,7 +154,6 @@ private: DocClipBase *m_currentClip; SmallRuler *m_ruler; Overlay *m_overlay; - bool m_isActive; double m_scale; int m_length; bool m_dragStarted; diff --git a/src/monitormanager.cpp b/src/monitormanager.cpp index 09eb326f..af5a14cd 100644 --- a/src/monitormanager.cpp +++ b/src/monitormanager.cpp @@ -32,6 +32,7 @@ MonitorManager::MonitorManager(QWidget *parent) : QObject(parent), m_clipMonitor(NULL), m_projectMonitor(NULL), + m_activeMonitor(NULL), m_blocked(false) { } @@ -41,31 +42,51 @@ Timecode MonitorManager::timecode() return m_timecode; } -void MonitorManager::initMonitors(Monitor *clipMonitor, Monitor *projectMonitor) +void MonitorManager::initMonitors(Monitor *clipMonitor, Monitor *projectMonitor, RecMonitor *recMonitor) { m_clipMonitor = clipMonitor; m_projectMonitor = projectMonitor; + + m_monitorsList.append(clipMonitor); + m_monitorsList.append(projectMonitor); + m_monitorsList.append(recMonitor); +} + +void MonitorManager::appendMonitor(AbstractMonitor *monitor) +{ + if (!m_monitorsList.contains(monitor)) m_monitorsList.append(monitor); +} + +void MonitorManager::removeMonitor(AbstractMonitor *monitor) +{ + m_monitorsList.removeAll(monitor); } void MonitorManager::activateMonitor(QString name) { + kDebug()<<"//ACTIVATING MON: "<name() == name) return; - if (name == "clip") { - m_projectMonitor->stop(); - m_clipMonitor->start(); - emit raiseClipMonitor(true); - } else { - m_clipMonitor->stop(); - m_projectMonitor->start(); - emit raiseClipMonitor(false); + m_activeMonitor = NULL; + for (int i = 0; i < m_monitorsList.count(); i++) { + kDebug()<<"PARSING: "<name(); + if (m_monitorsList.at(i)->name() == name) { + m_activeMonitor = m_monitorsList.at(i); + emit raiseMonitor(m_activeMonitor); + } + else m_monitorsList.at(i)->stop(); } - m_activeMonitor = name; + if (m_activeMonitor) m_activeMonitor->start(); emit checkColorScopes(); } +bool MonitorManager::isActive(const QString name) const +{ + return m_activeMonitor ? m_activeMonitor->name() == name: false; +} + void MonitorManager::slotSwitchMonitors(bool activateClip) { if (activateClip) @@ -77,78 +98,78 @@ void MonitorManager::slotSwitchMonitors(bool activateClip) void MonitorManager::stopActiveMonitor() { if (m_blocked) return; - if (m_clipMonitor->isActive()) m_clipMonitor->pause(); + if (m_activeMonitor == m_clipMonitor) m_clipMonitor->pause(); else m_projectMonitor->pause(); } void MonitorManager::slotPlay() { - if (m_clipMonitor->isActive()) m_clipMonitor->slotPlay(); + if (m_activeMonitor == m_clipMonitor) m_clipMonitor->slotPlay(); else m_projectMonitor->slotPlay(); } void MonitorManager::slotPause() { - stopActiveMonitor(); + stopActiveMonitor(); } void MonitorManager::slotPlayZone() { - if (m_clipMonitor->isActive()) m_clipMonitor->slotPlayZone(); + if (m_activeMonitor == m_clipMonitor) m_clipMonitor->slotPlayZone(); else m_projectMonitor->slotPlayZone(); } void MonitorManager::slotLoopZone() { - if (m_clipMonitor->isActive()) m_clipMonitor->slotLoopZone(); + if (m_activeMonitor == m_clipMonitor) m_clipMonitor->slotLoopZone(); else m_projectMonitor->slotLoopZone(); } void MonitorManager::slotRewind(double speed) { - if (m_clipMonitor->isActive()) m_clipMonitor->slotRewind(speed); + if (m_activeMonitor == m_clipMonitor) m_clipMonitor->slotRewind(speed); else m_projectMonitor->slotRewind(speed); } void MonitorManager::slotForward(double speed) { - if (m_clipMonitor->isActive()) m_clipMonitor->slotForward(speed); + if (m_activeMonitor == m_clipMonitor) m_clipMonitor->slotForward(speed); else m_projectMonitor->slotForward(speed); } void MonitorManager::slotRewindOneFrame() { - if (m_clipMonitor->isActive()) m_clipMonitor->slotRewindOneFrame(); + if (m_activeMonitor == m_clipMonitor) m_clipMonitor->slotRewindOneFrame(); else m_projectMonitor->slotRewindOneFrame(); } void MonitorManager::slotForwardOneFrame() { - if (m_clipMonitor->isActive()) m_clipMonitor->slotForwardOneFrame(); + if (m_activeMonitor == m_clipMonitor) m_clipMonitor->slotForwardOneFrame(); else m_projectMonitor->slotForwardOneFrame(); } void MonitorManager::slotRewindOneSecond() { - if (m_clipMonitor->isActive()) m_clipMonitor->slotRewindOneFrame(m_timecode.fps()); + if (m_activeMonitor == m_clipMonitor) m_clipMonitor->slotRewindOneFrame(m_timecode.fps()); else m_projectMonitor->slotRewindOneFrame(m_timecode.fps()); } void MonitorManager::slotForwardOneSecond() { - if (m_clipMonitor->isActive()) m_clipMonitor->slotForwardOneFrame(m_timecode.fps()); + if (m_activeMonitor == m_clipMonitor) m_clipMonitor->slotForwardOneFrame(m_timecode.fps()); else m_projectMonitor->slotForwardOneFrame(m_timecode.fps()); } void MonitorManager::slotStart() { - if (m_clipMonitor->isActive()) m_clipMonitor->slotStart(); + if (m_activeMonitor == m_clipMonitor) m_clipMonitor->slotStart(); else m_projectMonitor->slotStart(); } void MonitorManager::slotEnd() { - if (m_clipMonitor->isActive()) m_clipMonitor->slotEnd(); + if (m_activeMonitor == m_clipMonitor) m_clipMonitor->slotEnd(); else m_projectMonitor->slotEnd(); } @@ -164,7 +185,7 @@ void MonitorManager::slotResetProfiles() { if (m_blocked) return; if (m_projectMonitor == NULL || m_clipMonitor == NULL) return; - QString active = m_activeMonitor; + QString active = m_activeMonitor ? m_activeMonitor->name() : QString(); activateMonitor("clip"); m_clipMonitor->resetProfile(KdenliveSettings::current_profile()); m_clipMonitor->updateTimecodeFormat(); @@ -172,12 +193,12 @@ void MonitorManager::slotResetProfiles() m_projectMonitor->resetProfile(KdenliveSettings::current_profile()); m_projectMonitor->updateTimecodeFormat(); //m_projectMonitor->refreshMonitor(true); - activateMonitor(active); + if (!active.isEmpty()) activateMonitor(active); } void MonitorManager::slotRefreshCurrentMonitor() { - if (m_clipMonitor->isActive()) m_clipMonitor->refreshMonitor(); + if (m_activeMonitor == m_clipMonitor) m_clipMonitor->refreshMonitor(); else m_projectMonitor->refreshMonitor(); } @@ -192,4 +213,15 @@ void MonitorManager::slotUpdateAudioMonitoring() } } +void MonitorManager::updateScopeSource() +{ + emit checkColorScopes(); +} + +AbstractRender *MonitorManager::activeRenderer() +{ + if (m_activeMonitor) return m_activeMonitor->abstractRender(); + return NULL; +} + #include "monitormanager.moc" diff --git a/src/monitormanager.h b/src/monitormanager.h index 625bc6bf..9352f4d7 100644 --- a/src/monitormanager.h +++ b/src/monitormanager.h @@ -22,9 +22,9 @@ #define MONITORMANAGER_H #include "monitor.h" +#include "recmonitor.h" #include "timecode.h" -class Monitor; class MonitorManager : public QObject { @@ -32,16 +32,21 @@ class MonitorManager : public QObject public: MonitorManager(QWidget *parent = 0); - void initMonitors(Monitor *clipMonitor, Monitor *projectMonitor); + void initMonitors(Monitor *clipMonitor, Monitor *projectMonitor, RecMonitor *recMonitor); + void appendMonitor(AbstractMonitor *monitor); + void removeMonitor(AbstractMonitor *monitor); Timecode timecode(); void resetProfiles(Timecode tc); void stopActiveMonitor(); + AbstractRender *activeRenderer(); + void updateScopeSource(); public slots: /** @brief Activates a monitor. * @param name name of the monitor to activate */ void activateMonitor(QString name = QString()); + bool isActive(const QString name) const; void slotPlay(); void slotPause(); void slotPlayZone(); @@ -68,13 +73,14 @@ private slots: private: Monitor *m_clipMonitor; Monitor *m_projectMonitor; - QString m_activeMonitor; Timecode m_timecode; + AbstractMonitor *m_activeMonitor; bool m_blocked; + QList m_monitorsList; signals: /** @brief Emitted when the active monitor changes */ - void raiseClipMonitor(bool); + void raiseMonitor(AbstractMonitor *); /** @brief When the monitor changed, update the visible color scopes */ void checkColorScopes(); diff --git a/src/profilesdialog.cpp b/src/profilesdialog.cpp index 557f6f7c..c9aa53c2 100644 --- a/src/profilesdialog.cpp +++ b/src/profilesdialog.cpp @@ -490,15 +490,17 @@ QString ProfilesDialog::getPathFromDescription(const QString profileDesc) } // static -void ProfilesDialog::saveProfile(MltVideoProfile &profile) +void ProfilesDialog::saveProfile(MltVideoProfile &profile, QString profilePath) { - int i = 0; - QString customName = "profiles/customprofile"; - QString profilePath = KStandardDirs::locateLocal("appdata", customName + QString::number(i)); - kDebug() << " TYING PROFILE FILE: " << profilePath; - while (KIO::NetAccess::exists(KUrl(profilePath), KIO::NetAccess::SourceSide, 0)) { - i++; + if (profilePath.isEmpty()) { + int i = 0; + QString customName = "profiles/customprofile"; profilePath = KStandardDirs::locateLocal("appdata", customName + QString::number(i)); + kDebug() << " TYING PROFILE FILE: " << profilePath; + while (KIO::NetAccess::exists(KUrl(profilePath), KIO::NetAccess::SourceSide, 0)) { + i++; + profilePath = KStandardDirs::locateLocal("appdata", customName + QString::number(i)); + } } QFile file(profilePath); if (!file.open(QIODevice::WriteOnly)) { @@ -569,6 +571,15 @@ QString ProfilesDialog::getColorspaceDescription(int colorspace) } } +//static +int ProfilesDialog::getColorspaceFromDescription(const QString &description) +{ + //TODO: should the descriptions be translated? + if (description == "SMPTE240M") return 240; + if (description == "ITU-R 709") return 709; + return 601; +} + #include "profilesdialog.moc" diff --git a/src/profilesdialog.h b/src/profilesdialog.h index a9927bab..ada0736a 100644 --- a/src/profilesdialog.h +++ b/src/profilesdialog.h @@ -38,7 +38,7 @@ public: static QString getPathFromDescription(const QString profileDesc); static MltVideoProfile getVideoProfile(QString name); static QMap getProfilesInfo(); - static void saveProfile(MltVideoProfile &profile); + static void saveProfile(MltVideoProfile &profile, QString profilePath = QString()); static QString existingProfile(MltVideoProfile profile); static bool existingProfileDescription(const QString &desc); @@ -72,6 +72,11 @@ public: * @return The string description */ static QString getColorspaceDescription(int colorspace); + /** @brief Get the colorspace code (defined by MLT) from a descriptive text + * @param desctiption A string description as defined in getColorspaceDescription(int colorspace) + * @return The int code */ + static int getColorspaceFromDescription(const QString &description); + protected: virtual void closeEvent(QCloseEvent *event); diff --git a/src/recmonitor.cpp b/src/recmonitor.cpp index 51e9b1bf..ba38afcb 100644 --- a/src/recmonitor.cpp +++ b/src/recmonitor.cpp @@ -20,12 +20,15 @@ #include "recmonitor.h" #include "gentime.h" +#include "mltdevicecapture.h" #include "kdenlivesettings.h" #include "managecapturesdialog.h" +#include "monitormanager.h" +#include "monitor.h" +#include "profilesdialog.h" #include #include -#include #include #include #include @@ -44,15 +47,17 @@ #include -RecMonitor::RecMonitor(QString name, QWidget *parent) : - QWidget(parent), +RecMonitor::RecMonitor(QString name, MonitorManager *manager, QWidget *parent) : + AbstractMonitor(parent), m_name(name), - m_isActive(false), m_isCapturing(false), m_didCapture(false), m_isPlaying(false), m_bmCapture(NULL), - m_blackmagicCapturing(false) + m_blackmagicCapturing(false), + m_manager(manager), + m_captureDevice(NULL), + m_analyse(false) { setupUi(this); @@ -60,6 +65,16 @@ RecMonitor::RecMonitor(QString name, QWidget *parent) : device_selector->setCurrentIndex(KdenliveSettings::defaultcapture()); connect(device_selector, SIGNAL(currentIndexChanged(int)), this, SLOT(slotVideoDeviceChanged(int))); + // Video widget holder + QVBoxLayout *l = new QVBoxLayout; + l->setContentsMargins(0, 0, 0, 0); + l->setSpacing(0); + m_videoBox = new VideoPreviewContainer(); + m_videoBox->setContentsMargins(0, 0, 0, 0); + m_videoBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + l->addWidget(m_videoBox); + video_frame->setLayout(l); + QToolBar *toolbar = new QToolBar(this); QHBoxLayout *layout = new QHBoxLayout; layout->setContentsMargins(0, 0, 0, 0); @@ -134,14 +149,6 @@ RecMonitor::RecMonitor(QString name, QWidget *parent) : m_displayProcess->setEnvironment(env); - if (KdenliveSettings::video4capture().isEmpty()) { - QString captureCommand; - if (!KdenliveSettings::video4adevice().isEmpty()) captureCommand = "-f " + KdenliveSettings::video4aformat() + " -i " + KdenliveSettings::video4adevice() + " -acodec " + KdenliveSettings::video4acodec(); - - captureCommand += " -f " + KdenliveSettings::video4vformat() + " -s " + KdenliveSettings::video4size() + " -r " + QString::number(KdenliveSettings::video4rate()) + " -i " + KdenliveSettings::video4vdevice() + " -vcodec " + KdenliveSettings::video4vcodec();; - KdenliveSettings::setVideo4capture(captureCommand); - } - kDebug() << "/////// BUILDING MONITOR, ID: " << video_frame->winId(); } @@ -152,13 +159,23 @@ RecMonitor::~RecMonitor() #endif delete m_captureProcess; delete m_displayProcess; + if (m_captureDevice) delete m_captureDevice; } -QString RecMonitor::name() const +const QString RecMonitor::name() const { return m_name; } +void RecMonitor::stop() +{ + slotStopCapture(); +} + +void RecMonitor::start() +{ +} + void RecMonitor::slotConfigure() { emit showConfigDialog(4, device_selector->currentIndex()); @@ -187,10 +204,16 @@ void RecMonitor::slotVideoDeviceChanged(int ix) QString capturename; video_capture->setHidden(true); video_frame->setHidden(false); - m_fwdAction->setVisible(ix != BLACKMAGIC); - m_discAction->setVisible(ix != BLACKMAGIC); - m_rewAction->setVisible(ix != BLACKMAGIC); + m_fwdAction->setVisible(ix == FIREWIRE); + m_discAction->setVisible(ix == FIREWIRE); + m_rewAction->setVisible(ix == FIREWIRE); m_logger.setVisible(ix == BLACKMAGIC); + if (m_captureDevice) { + // MLT capture still running, abort + m_captureDevice->stop(); + //delete m_captureDevice; + //m_captureDevice = NULL; + } switch (ix) { case SCREENGRAB: m_discAction->setEnabled(false); @@ -365,6 +388,12 @@ void RecMonitor::slotStopCapture() m_isPlaying = false; break; case VIDEO4LINUX: + if (m_captureDevice) { + m_captureDevice->stop(); + } + m_playAction->setEnabled(true); + m_stopAction->setEnabled(false); + break; case SCREENGRAB: m_captureProcess->write("q\n", 3); QTimer::singleShot(1000, m_captureProcess, SLOT(kill())); @@ -401,6 +430,9 @@ void RecMonitor::slotStartCapture(bool play) m_displayArgs.clear(); m_isPlaying = false; QString capturename = KdenliveSettings::dvgrabfilename(); + QString path; + MltVideoProfile profile; + QString producer; QStringList dvargs = KdenliveSettings::dvgrabextra().simplified().split(" ", QString::SkipEmptyParts); video_capture->setVisible(device_selector->currentIndex() == BLACKMAGIC); video_frame->setHidden(device_selector->currentIndex() == BLACKMAGIC); @@ -449,11 +481,32 @@ void RecMonitor::slotStartCapture(bool play) m_discAction->setEnabled(true); break; case VIDEO4LINUX: - m_captureArgs << KdenliveSettings::video4capture().simplified().split(' ') << KdenliveSettings::video4encoding().simplified().split(' ') << "-f" << KdenliveSettings::video4container() << "-"; + path = KStandardDirs::locateLocal("appdata", "profiles/video4linux"); + m_manager->activateMonitor("record"); + if (m_captureDevice == NULL) { + m_captureDevice = new MltDeviceCapture(path, m_videoBox, this); + m_captureDevice->sendFrameForAnalysis = m_analyse; + m_manager->updateScopeSource(); + } + profile = ProfilesDialog::getVideoProfile(path); + 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); + kDebug()<< "PROD: "<slotStartPreview(producer)) { + // v4l capture failed to start + video_frame->setText(i18n("Failed to start Video4Linux,\ncheck your parameters...")); + m_videoBox->setHidden(true); + + } else { + m_videoBox->setHidden(false); + m_playAction->setEnabled(false); + m_stopAction->setEnabled(true); + } + + /*m_captureArgs << KdenliveSettings::video4capture().simplified().split(' ') << KdenliveSettings::video4encoding().simplified().split(' ') << "-f" << KdenliveSettings::video4container() << "-"; m_displayArgs << "-f" << KdenliveSettings::video4container() << "-x" << QString::number(video_frame->width()) << "-y" << QString::number(video_frame->height()) << "-"; m_captureProcess->setStandardOutputProcess(m_displayProcess); kDebug() << "Capture: Running ffmpeg " << m_captureArgs.join(" "); - m_captureProcess->start("ffmpeg", m_captureArgs); + m_captureProcess->start("ffmpeg", m_captureArgs);*/ break; case BLACKMAGIC: m_bmCapture->startPreview(KdenliveSettings::hdmi_capturedevice(), KdenliveSettings::hdmi_capturemode()); @@ -465,7 +518,7 @@ void RecMonitor::slotStartCapture(bool play) break; } - if (device_selector->currentIndex() == FIREWIRE || device_selector->currentIndex() == VIDEO4LINUX) { + if (device_selector->currentIndex() == FIREWIRE) { kDebug() << "Capture: Running ffplay " << m_displayArgs.join(" "); m_displayProcess->start("ffplay", m_displayArgs); video_frame->setText(i18n("Initialising...")); @@ -505,10 +558,11 @@ void RecMonitor::slotRecord() m_recAction->setChecked(false); break; case VIDEO4LINUX: - m_captureProcess->terminate(); slotStopCapture(); - //m_isCapturing = false; - QTimer::singleShot(1000, this, SLOT(slotStartCapture())); + m_isCapturing = false; + m_recAction->setChecked(false); + if (autoaddbox->isChecked() && QFile::exists(m_captureFile.path())) emit addProjectClip(m_captureFile); + //QTimer::singleShot(1000, this, SLOT(slotStartCapture())); break; case SCREENGRAB: //captureProcess->write("q\n", 3); @@ -532,12 +586,12 @@ void RecMonitor::slotRecord() m_recAction->setChecked(true); QString extension = "mp4"; if (device_selector->currentIndex() == SCREENGRAB) extension = "ogv"; //KdenliveSettings::screengrabextension(); - else if (device_selector->currentIndex() == VIDEO4LINUX) extension = KdenliveSettings::video4extension(); - QString path = m_capturePath + "/capture0000." + extension; + else if (device_selector->currentIndex() == VIDEO4LINUX) extension = KdenliveSettings::v4l_extension(); + QString path = KUrl(m_capturePath).path(KUrl::AddTrailingSlash) + "capture0000." + extension; int i = 1; while (QFile::exists(path)) { QString num = QString::number(i).rightJustified(4, '0', false); - path = m_capturePath + "/capture" + num + '.' + extension; + path = KUrl(m_capturePath).path(KUrl::AddTrailingSlash) + "capture" + num + '.' + extension; i++; } m_captureFile = KUrl(path); @@ -545,16 +599,57 @@ void RecMonitor::slotRecord() m_captureArgs.clear(); m_displayArgs.clear(); QString args; + QString playlist; + MltVideoProfile profile; QString capturename = KdenliveSettings::dvgrabfilename(); if (capturename.isEmpty()) capturename = "capture"; switch (device_selector->currentIndex()) { case VIDEO4LINUX: + path = KStandardDirs::locateLocal("appdata", "profiles/video4linux"); + profile = ProfilesDialog::getVideoProfile(path); + if (m_captureDevice == NULL) { + m_captureDevice = new MltDeviceCapture(path, m_videoBox, this); + m_captureDevice->sendFrameForAnalysis = m_analyse; + m_manager->updateScopeSource(); + } + playlist = QString("producer100000pausevideo4linux2:%1?width:%2&height:%3&frame_rate:%4avformat-novalidate").arg(KdenliveSettings::video4vdevice()).arg(profile.width).arg(profile.height).arg((double) profile.frame_rate_num / profile.frame_rate_den); + + // Add alsa audio capture + if (KdenliveSettings::v4l_captureaudio()) { + playlist.append(QString("producer100000pausealsa:%50-1avformat").arg(KdenliveSettings::v4l_alsadevicename())); + } + + + playlist.append(""); + + playlist.append(""); + + // Audio mix + if (KdenliveSettings::v4l_captureaudio()) { + playlist.append(""); + playlist.append("01transitionmix"); + } + + playlist.append(""); + + if (m_captureDevice->slotStartCapture(KdenliveSettings::v4l_parameters(), m_captureFile.path(), playlist)) { + m_videoBox->setHidden(false); + m_isCapturing = true; + } + else { + video_frame->setText(i18n("Failed to start Video4Linux,\ncheck your parameters...")); + m_videoBox->setHidden(true); + m_isCapturing = false; + m_recAction->setChecked(false); + } + + /* m_captureArgs << KdenliveSettings::video4capture().simplified().split(' ') << KdenliveSettings::video4encoding().simplified().split(' ') << "-y" << m_captureFile.path() << "-f" << KdenliveSettings::video4container() << "-acodec" << KdenliveSettings::video4acodec() << "-vcodec" << KdenliveSettings::video4vcodec() << "-"; m_displayArgs << "-f" << KdenliveSettings::video4container() << "-x" << QString::number(video_frame->width()) << "-y" << QString::number(video_frame->height()) << "-"; m_captureProcess->setStandardOutputProcess(m_displayProcess); kDebug() << "Capture: Running ffmpeg " << m_captureArgs.join(" "); - m_captureProcess->start("ffmpeg", m_captureArgs); + m_captureProcess->start("ffmpeg", m_captureArgs);*/ break; case SCREENGRAB: switch (KdenliveSettings::rmd_capture_type()) { @@ -606,7 +701,7 @@ void RecMonitor::slotRecord() } - if (device_selector->currentIndex() != SCREENGRAB) { + if (device_selector->currentIndex() == FIREWIRE) { m_isCapturing = true; kDebug() << "Capture: Running ffplay " << m_displayArgs.join(" "); m_displayProcess->start("ffplay", m_displayArgs); @@ -759,23 +854,6 @@ void RecMonitor::slotUpdateFreeSpace() #endif } -void RecMonitor::activateRecMonitor() -{ - //if (!m_isActive) m_monitorManager->activateRecMonitor(m_name); -} - -void RecMonitor::stop() -{ - m_isActive = false; - -} - -void RecMonitor::start() -{ - m_isActive = true; - -} - void RecMonitor::refreshRecMonitor(bool visible) { if (visible) { @@ -799,5 +877,18 @@ void RecMonitor::slotReadDvgrabInfo() m_dvinfo.updateGeometry(); } +AbstractRender *RecMonitor::abstractRender() +{ + return m_captureDevice; +} + + +void RecMonitor::analyseFrames(bool analyse) +{ + m_analyse = analyse; + if (m_captureDevice) m_captureDevice->sendFrameForAnalysis = analyse; +} + + #include "recmonitor.moc" diff --git a/src/recmonitor.h b/src/recmonitor.h index 79befd52..c61b8041 100644 --- a/src/recmonitor.h +++ b/src/recmonitor.h @@ -26,6 +26,7 @@ #ifndef RECMONITOR_H #define RECMONITOR_H +#include "abstractmonitor.h" #include "blackmagic/capture.h" #include "ui_recmonitor_ui.h" @@ -45,16 +46,21 @@ #include #endif -class RecMonitor : public QWidget, public Ui::RecMonitor_UI +class MonitorManager; +class MltDeviceCapture; +class AbstractRender; + +class RecMonitor : public AbstractMonitor, public Ui::RecMonitor_UI { Q_OBJECT public: - explicit RecMonitor(QString name, QWidget *parent = 0); + explicit RecMonitor(QString name, MonitorManager *manager, QWidget *parent = 0); virtual ~RecMonitor(); - QString name() const; - + const QString name() const; + AbstractRender *abstractRender(); + void analyseFrames(bool analyse); enum CAPTUREDEVICE {FIREWIRE = 0, VIDEO4LINUX = 1, SCREENGRAB = 2, BLACKMAGIC = 3}; protected: @@ -62,7 +68,6 @@ protected: private: QString m_name; - bool m_isActive; KDateTime m_captureTime; /** @brief Provide feedback about dvgrab operations */ QLabel m_dvinfo; @@ -95,17 +100,22 @@ private: QAction *m_rewAction; QAction *m_stopAction; QAction *m_discAction; - void checkDeviceAvailability(); - QPixmap mergeSideBySide(const QPixmap& pix, const QString txt); - void manageCapturedFiles(); + + CaptureHandler *m_bmCapture; /** @brief Indicates whether we are currently capturing from BLACKMAGIC. */ bool m_blackmagicCapturing; + MonitorManager *m_manager; + MltDeviceCapture *m_captureDevice; + VideoPreviewContainer *m_videoBox; + bool m_analyse; + void checkDeviceAvailability(); + QPixmap mergeSideBySide(const QPixmap& pix, const QString txt); + void manageCapturedFiles(); void createBlackmagicDevice(); private slots: void slotStartCapture(bool play = true); - void slotStopCapture(); void slotRecord(); void slotProcessStatus(QProcess::ProcessState status); void slotVideoDeviceChanged(int ix); @@ -121,10 +131,10 @@ private slots: public slots: void refreshRecMonitor(bool visible); + void slotPlay(); void stop(); void start(); - void activateRecMonitor(); - void slotPlay(); + void slotStopCapture(); void slotUpdateCaptureFolder(const QString currentProjectFolder); signals: diff --git a/src/renderer.cpp b/src/renderer.cpp index e9f2dc6e..e6324a1f 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -94,10 +94,9 @@ static void consumer_gl_frame_show(mlt_consumer, Render * self, mlt_frame frame_ } Render::Render(const QString & rendererName, int winid, QString profile, QWidget *parent) : - QObject(parent), + AbstractRender(parent), m_isBlocked(0), analyseAudio(KdenliveSettings::monitor_audio()), - sendFrameForAnalysis(false), m_name(rendererName), m_mltConsumer(NULL), m_mltProducer(NULL), @@ -1267,7 +1266,6 @@ void Render::stop() if (m_mltProducer == NULL) return; if (m_mltConsumer && !m_mltConsumer->is_stopped()) { kDebug() << "///////////// RENDER STOPPED: " << m_name; - m_isBlocked = true; //m_mltConsumer->set("refresh", 0); m_mltConsumer->stop(); // delete m_mltConsumer; diff --git a/src/renderer.h b/src/renderer.h index b20afea5..2d0a2189 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -31,6 +31,7 @@ #include "gentime.h" #include "definitions.h" +#include "abstractmonitor.h" #include "mlt/framework/mlt_types.h" #include @@ -42,8 +43,6 @@ #include -class Render; - class QTimer; class QPixmap; @@ -73,7 +72,8 @@ private: QString m_message; }; -class Render: public QObject + +class Render: public AbstractRender { Q_OBJECT public: @@ -87,7 +87,7 @@ Q_OBJECT public: Render(const QString & rendererName, int winid, QString profile = QString(), QWidget *parent = 0); /** @brief Destroy the MLT Renderer. */ - ~Render(); + virtual ~Render(); /** @brief Seeks the renderer clip to the given time. */ void seek(GenTime time); @@ -260,8 +260,7 @@ Q_OBJECT public: void showAudio(Mlt::Frame&); /** @brief This property is used to decide if the renderer should send audio data for monitoring. */ bool analyseAudio; - /** @brief This property is used to decide if the renderer should convert it's frames to QImage for use in other Kdenlive widgets. */ - bool sendFrameForAnalysis; + QList checkTrackSequence(int); void sendFrameUpdate(); @@ -364,10 +363,6 @@ signals: * Used in Mac OS X. */ void showImageSignal(QImage); void showAudioSignal(const QByteArray); - /** @brief The renderer refreshed the current frame, but no seeking was done. */ - void frameUpdated(QImage); - /** @brief This signal contains the audio of the current frame. */ - void audioSamplesSignal(const QVector&, int freq, int num_channels, int num_samples); public slots: diff --git a/src/stopmotion/stopmotion.cpp b/src/stopmotion/stopmotion.cpp index 6c8f535e..c93a15ac 100644 --- a/src/stopmotion/stopmotion.cpp +++ b/src/stopmotion/stopmotion.cpp @@ -19,6 +19,10 @@ #include "../blackmagic/devices.h" #include "../v4l/v4lcapture.h" #include "../slideshowclip.h" +#include "../profilesdialog.h" +#include "../mltdevicecapture.h" +#include "../recmonitor.h" +#include "../monitormanager.h" #include "ui_smconfig_ui.h" #include "kdenlivesettings.h" @@ -90,16 +94,58 @@ void MyLabel::paintEvent(QPaintEvent* event) } -StopmotionWidget::StopmotionWidget(KUrl projectFolder, QList< QAction* > actions, QWidget* parent) : +StopmotionMonitor::StopmotionMonitor(QWidget *parent) : + AbstractMonitor(parent), + m_captureDevice(NULL) +{ +} + +StopmotionMonitor::~StopmotionMonitor() +{ +} + +void StopmotionMonitor::setRender(MltDeviceCapture *render) +{ + m_captureDevice = render; +} + +AbstractRender *StopmotionMonitor::abstractRender() +{ + return m_captureDevice; +} + +const QString StopmotionMonitor::name() const +{ + return QString("stopmotion"); +} + + +void StopmotionMonitor::stop() +{ + if (m_captureDevice) m_captureDevice->stop(); + emit stopCapture(); +} + +void StopmotionMonitor::start() +{ +} + +StopmotionWidget::StopmotionWidget(MonitorManager *manager, KUrl projectFolder, QList< QAction* > actions, QWidget* parent) : QDialog(parent) , Ui::Stopmotion_UI() , m_projectFolder(projectFolder) - , m_bmCapture(NULL) + , m_captureDevice(NULL) , m_sequenceFrame(0) , m_animatedIndex(-1) , m_animate(false) + , m_manager(manager) + , m_monitor(new StopmotionMonitor(this)) { //setAttribute(Qt::WA_DeleteOnClose); + //HACK: the monitor widget is hidden, it is just used to control the capturedevice from monitormanager + m_monitor->setHidden(true); + connect(m_monitor, SIGNAL(stopCapture()), this, SLOT(slotStopCapture())); + m_manager->appendMonitor(m_monitor); QAction* analyse = new QAction(i18n("Send frames to color scopes"), this); analyse->setCheckable(true); analyse->setChecked(KdenliveSettings::analyse_stopmotion()); @@ -192,20 +238,30 @@ StopmotionWidget::StopmotionWidget(KUrl projectFolder, QList< QAction* > actions connect(sequence_name, SIGNAL(textChanged(const QString&)), this, SLOT(sequenceNameChanged(const QString&))); connect(sequence_name, SIGNAL(currentIndexChanged(int)), live_button, SLOT(setFocus())); - m_layout = new QVBoxLayout; + // Video widget holder + QVBoxLayout *layout = new QVBoxLayout; + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + m_videoBox = new VideoPreviewContainer(); + m_videoBox->setContentsMargins(0, 0, 0, 0); + m_videoBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + //m_videoBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + m_videoBox->setLineWidth(4); + layout->addWidget(m_videoBox); + + if (BMInterface::getBlackMagicDeviceList(capture_device, NULL)) { // Found a BlackMagic device - m_bmCapture = new BmdCaptureHandler(m_layout); - connect(m_bmCapture, SIGNAL(gotMessage(const QString&)), this, SLOT(slotGotHDMIMessage(const QString&))); + //m_bmCapture = new BmdCaptureHandler(m_layout); + //connect(m_bmCapture, SIGNAL(gotMessage(const QString&)), this, SLOT(slotGotHDMIMessage(const QString&))); } if (QFile::exists(KdenliveSettings::video4vdevice())) { #if !defined(Q_WS_MAC) && !defined(Q_OS_FREEBSD) - V4lCaptureHandler v4l(NULL); // Video 4 Linux device detection for (int i = 0; i < 10; i++) { QString path = "/dev/video" + QString::number(i); if (QFile::exists(path)) { - QStringList deviceInfo = v4l.getDeviceName(path); + QStringList deviceInfo = V4lCaptureHandler::getDeviceName(path); if (!deviceInfo.isEmpty()) { capture_device->addItem(deviceInfo.at(0), "v4l"); capture_device->setItemData(capture_device->count() - 1, path, Qt::UserRole + 1); @@ -215,28 +271,36 @@ StopmotionWidget::StopmotionWidget(KUrl projectFolder, QList< QAction* > actions } } - /*V4lCaptureHandler v4lhandler(NULL); - QStringList deviceInfo = v4lhandler.getDeviceName(KdenliveSettings::video4vdevice()); - capture_device->addItem(deviceInfo.at(0), "v4l"); - capture_device->setItemData(capture_device->count() - 1, deviceInfo.at(3), Qt::UserRole + 1);*/ - if (m_bmCapture == NULL) { - m_bmCapture = new V4lCaptureHandler(m_layout); - m_bmCapture->setDevice(capture_device->itemData(capture_device->currentIndex(), Qt::UserRole + 1).toString(), capture_device->itemData(capture_device->currentIndex(), Qt::UserRole + 2).toString()); - } + //if (m_bmCapture == NULL) { + + //m_captureDevice->sendFrameForAnalysis = m_analyse; + /*m_bmCapture = new V4lCaptureHandler(m_layout); + m_bmCapture->setDevice(capture_device->itemData(capture_device->currentIndex(), Qt::UserRole + 1).toString(), capture_device->itemData(capture_device->currentIndex(), Qt::UserRole + 2).toString());*/ + //} #endif } connect(capture_device, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdateHandler())); - if (m_bmCapture) { + /*if (m_bmCapture) { connect(m_bmCapture, SIGNAL(frameSaved(const QString)), this, SLOT(slotNewThumb(const QString))); connect(m_bmCapture, SIGNAL(gotFrame(QImage)), this, SIGNAL(gotFrame(QImage))); - } else live_button->setEnabled(false); + } else live_button->setEnabled(false);*/ + m_frame_preview = new MyLabel(this); connect(m_frame_preview, SIGNAL(seek(bool)), this, SLOT(slotSeekFrame(bool))); connect(m_frame_preview, SIGNAL(switchToLive()), this, SLOT(slotSwitchLive())); - m_layout->addWidget(m_frame_preview); + layout->addWidget(m_frame_preview); m_frame_preview->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - video_preview->setLayout(m_layout); + video_preview->setLayout(layout); + + //kDebug()<winId(); + + QString path = KStandardDirs::locateLocal("appdata", "profiles/video4linux"); + m_captureDevice = new MltDeviceCapture(path, m_videoBox, this); + m_captureDevice->sendFrameForAnalysis = KdenliveSettings::analyse_stopmotion(); + m_monitor->setRender(m_captureDevice); + connect(m_captureDevice, SIGNAL(frameSaved(const QString)), this, SLOT(slotNewThumb(const QString))); + live_button->setChecked(false); button_addsequence->setEnabled(false); connect(live_button, SIGNAL(toggled(bool)), this, SLOT(slotLive(bool))); @@ -258,8 +322,15 @@ StopmotionWidget::StopmotionWidget(KUrl projectFolder, QList< QAction* > actions StopmotionWidget::~StopmotionWidget() { - if (m_bmCapture) - m_bmCapture->stopPreview(); + /*if (m_bmCapture) + m_bmCapture->stopPreview();*/ + if (m_captureDevice) { + m_captureDevice->stop(); + delete m_captureDevice; + m_captureDevice = NULL; + } + + delete m_monitor; } void StopmotionWidget::slotUpdateOverlayEffect(QAction* act) @@ -318,7 +389,7 @@ void StopmotionWidget::slotShowThumbs(bool show) void StopmotionWidget::slotUpdateHandler() { - QString data = capture_device->itemData(capture_device->currentIndex()).toString(); + /*QString data = capture_device->itemData(capture_device->currentIndex()).toString(); slotLive(false); if (m_bmCapture) { delete m_bmCapture; @@ -334,7 +405,7 @@ void StopmotionWidget::slotUpdateHandler() if (m_bmCapture) connect(m_bmCapture, SIGNAL(gotMessage(const QString&)), this, SLOT(slotGotHDMIMessage(const QString&))); } live_button->setEnabled(m_bmCapture != NULL); - m_layout->addWidget(m_frame_preview); + m_layout->addWidget(m_frame_preview);*/ } void StopmotionWidget::slotGotHDMIMessage(const QString& message) @@ -360,20 +431,72 @@ void StopmotionWidget::parseExistingSequences() void StopmotionWidget::slotSwitchLive() { setUpdatesEnabled(false); - if (m_frame_preview->isHidden()) { - if (m_bmCapture) m_bmCapture->hidePreview(true); + slotLive(!live_button->isChecked()); + /*if (m_frame_preview->isHidden()) { + //if (m_bmCapture) m_bmCapture->hidePreview(true); + m_videoBox->setHidden(true); m_frame_preview->setHidden(false); } else { m_frame_preview->setHidden(true); - if (m_bmCapture) m_bmCapture->hidePreview(false); + //if (m_bmCapture) m_bmCapture->hidePreview(false); + m_videoBox->setHidden(false); capture_button->setEnabled(true); - } + }*/ setUpdatesEnabled(true); } +void StopmotionWidget::slotStopCapture() +{ + slotLive(false); +} + void StopmotionWidget::slotLive(bool isOn) { live_button->blockSignals(true); + capture_button->setEnabled(false); + if (isOn) { + m_frame_preview->setHidden(true); + m_videoBox->setHidden(false); + QString path = KStandardDirs::locateLocal("appdata", "profiles/video4linux"); + + kDebug()<<"SURFACE; "<width()<<"x"<height(); + if (m_captureDevice == NULL) { + m_captureDevice = new MltDeviceCapture(path, m_videoBox, this); + m_captureDevice->sendFrameForAnalysis = KdenliveSettings::analyse_stopmotion(); + m_monitor->setRender(m_captureDevice); + connect(m_captureDevice, SIGNAL(frameSaved(const QString)), this, SLOT(slotNewThumb(const QString))); + } + + MltVideoProfile profile = ProfilesDialog::getVideoProfile(path); + m_manager->activateMonitor("stopmotion"); + QString 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)) { + kDebug()<<"// STARt CAPTURE GO"; + capture_button->setEnabled(true); + live_button->setChecked(true); + log_box->insertItem(-1, i18n("Playing %1x%2 (%3 fps)", profile.width, profile.height, QString::number((double)profile.frame_rate_num/profile.frame_rate_den).rightJustified(2, '0'))); + log_box->setCurrentIndex(0); + } + else { + kDebug()<<"// problem starting stopmo"; + log_box->insertItem(-1, i18n("Failed to start device")); + log_box->setCurrentIndex(0); + } + } + else { + m_frame_preview->setHidden(false); + live_button->setChecked(false); + if (m_captureDevice) { + m_captureDevice->stop(); + m_videoBox->setHidden(true); + log_box->insertItem(-1, i18n("Stopped")); + log_box->setCurrentIndex(0); + //delete m_captureDevice; + //m_captureDevice = NULL; + } + } + + /* if (isOn && m_bmCapture) { //m_frame_preview->setImage(QImage()); m_frame_preview->setHidden(true); @@ -385,24 +508,24 @@ void StopmotionWidget::slotLive(bool isOn) m_frame_preview->setHidden(false); capture_button->setEnabled(false); live_button->setChecked(false); - } + }*/ live_button->blockSignals(false); } -void StopmotionWidget::slotShowOverlay(bool isOn) +void StopmotionWidget::slotShowOverlay(bool /*isOn*/) { - if (isOn) { +/* if (isOn) { if (live_button->isChecked() && m_sequenceFrame > 0) { slotUpdateOverlay(); } } else if (m_bmCapture) { m_bmCapture->hideOverlay(); - } + }*/ } void StopmotionWidget::slotUpdateOverlay() { - if (m_bmCapture == NULL) return; + if (m_captureDevice == NULL) return; QString path = getPathForFrame(m_sequenceFrame - 1); if (!QFile::exists(path)) return; QImage img(path); @@ -435,7 +558,7 @@ void StopmotionWidget::slotUpdateOverlay() } #endif - m_bmCapture->showOverlay(img); + //m_bmCapture->showOverlay(img); } void StopmotionWidget::sequenceNameChanged(const QString& name) @@ -467,7 +590,7 @@ void StopmotionWidget::sequenceNameChanged(const QString& name) void StopmotionWidget::slotCaptureFrame() { - if (m_bmCapture == NULL) return; + if (m_captureDevice == NULL) return; if (sequence_name->currentText().isEmpty()) { QString seqName = QInputDialog::getText(this, i18n("Create New Sequence"), i18n("Enter sequence name")); if (seqName.isEmpty()) { @@ -489,7 +612,7 @@ void StopmotionWidget::slotCaptureFrame() return; } QString currentPath = getPathForFrame(m_sequenceFrame); - m_bmCapture->captureFrame(currentPath); + m_captureDevice->captureFrame(currentPath); KNotification::event("FrameCaptured", i18n("Frame Captured"), QPixmap(), this); m_sequenceFrame++; button_addsequence->setEnabled(true); @@ -562,7 +685,8 @@ void StopmotionWidget::slotShowFrame(const QString& path) capture_button->setEnabled(false); slotLive(false); if (!img.isNull()) { - if (m_bmCapture) m_bmCapture->hidePreview(true); + //m_videoBox->setHidden(true); + m_frame_preview->setImage(img); m_frame_preview->setHidden(false); m_frame_preview->update(); @@ -714,7 +838,8 @@ void StopmotionWidget::slotRemoveFrame() void StopmotionWidget::slotSwitchAnalyse(bool isOn) { KdenliveSettings::setAnalyse_stopmotion(isOn); - m_bmCapture->setAnalyse(isOn); + if (m_captureDevice) m_captureDevice->sendFrameForAnalysis = isOn; + //m_bmCapture->setAnalyse(isOn); } diff --git a/src/stopmotion/stopmotion.h b/src/stopmotion/stopmotion.h index 9e82f28f..dcdfa39e 100644 --- a/src/stopmotion/stopmotion.h +++ b/src/stopmotion/stopmotion.h @@ -26,6 +26,11 @@ #include #include #include +#include + +class MltDeviceCapture; +class MonitorManager; +class VideoPreviewContainer; class MyLabel : public QLabel { @@ -51,7 +56,30 @@ signals: void switchToLive(); }; -class StopmotionWidget : public QDialog , public Ui::Stopmotion_UI + +class StopmotionMonitor : public AbstractMonitor +{ + Q_OBJECT +public: + StopmotionMonitor(QWidget *parent); + ~StopmotionMonitor(); + AbstractRender *abstractRender(); + const QString name() const; + void setRender(MltDeviceCapture *render); + +private: + MltDeviceCapture *m_captureDevice; + +public slots: + virtual void stop(); + virtual void start(); + +signals: + void stopCapture(); +}; + + +class StopmotionWidget : public QDialog, public Ui::Stopmotion_UI { Q_OBJECT @@ -61,21 +89,22 @@ public: * @param projectFolder The current project folder, where captured files will be stored. * @param actions The actions for this widget that can have a keyboard shortcut. * @param parent (optional) parent widget */ - StopmotionWidget(KUrl projectFolder, QList< QAction* > actions, QWidget* parent = 0); + StopmotionWidget(MonitorManager *manager, KUrl projectFolder, QList< QAction* > actions, QWidget* parent = 0); virtual ~StopmotionWidget(); protected: virtual void closeEvent(QCloseEvent* e); private: - /** @brief Widget layout holding video and frame preview. */ - QVBoxLayout* m_layout; - /** @brief Current project folder (where the captured frames will be saved). */ KUrl m_projectFolder; /** @brief Capture holder that will handle all video operation. */ - CaptureHandler* m_bmCapture; + MltDeviceCapture *m_captureDevice; + + VideoPreviewContainer *m_videoBox; + + //CaptureHandler* m_bmCapture; /** @brief Holds the name of the current sequence. * Files will be saved in project folder with name: sequence001.png */ @@ -119,6 +148,10 @@ private: /** @brief Timer for interval capture. */ QTimer m_intervalTimer; + MonitorManager *m_manager; + + StopmotionMonitor *m_monitor; + #ifdef QIMAGEBLITZ int m_effectIndex; @@ -128,6 +161,7 @@ public slots: /** @brief Display the live feed from capture device. @param isOn enable or disable the feature */ void slotLive(bool isOn = true); + void slotStopCapture(); private slots: diff --git a/src/v4l/src.c b/src/v4l/src.c index 9cbc27fb..f7278236 100644 --- a/src/v4l/src.c +++ b/src/v4l/src.c @@ -16,14 +16,11 @@ #include #include #include +#include #include "src.h" -#ifdef HAVE_V4L2 -extern src_mod_t src_v4l2; -#endif -#ifdef HAVE_V4L1 -extern src_mod_t src_v4l1; -#endif +#include + /* Supported palette types. */ src_palette_t src_palette[] = { @@ -50,223 +47,141 @@ src_palette_t src_palette[] = { }; -int src_open(src_t *src, char *source) -{ - int i = 0; - struct stat st; - - if(!source) - { - fprintf(stderr, "No source was specified......."); - return(-1); - } - src->source = source; - - i = 0; - int r = src_v4l2.flags; - if(S_ISCHR(st.st_mode) && r & SRC_TYPE_DEVICE) r = -1; - else if(!S_ISCHR(st.st_mode) && r & SRC_TYPE_FILE) r = -1; - else r = 0; - src->type = 0; - r = src_v4l2.open(src); - if(r == -2) return(-1); - - /* - int frame; - for(frame = 0; frame < config->skipframes; frame++) - if(src_grab(src) == -1) break;*/ - - return 0; -} -const char *src_query(src_t *src, char *source, uint *width, uint *height, char **pixelformatdescription) +int v4l2_free_mmap(src_t *src) { - src->source = source; - return src_v4l2.query(src, width, height, pixelformatdescription); -} + src_v4l2_t *s = (src_v4l2_t *) src->state; + int i; -int src_close(src_t *src) -{ - int r; - - if(src->captured_frames) - { - double seconds = - (src->tv_last.tv_sec + src->tv_last.tv_usec / 1000000.0) - - (src->tv_first.tv_sec + src->tv_first.tv_usec / 1000000.0); - - /* Display FPS if enough frames where captured. */ - if(src->captured_frames == 1) - { - /*MSG("Captured frame in %0.2f seconds.", seconds);*/ - } - else if(src->captured_frames < 3) - { - /*MSG("Captured %i frames in %0.2f seconds.", - src->captured_frames, seconds);*/ - } - else - { - /*MSG("Captured %i frames in %0.2f seconds. (%i fps)", - src->captured_frames, seconds, - (int) (src->captured_frames / seconds));*/ - } - } - - r = src_v4l2.close(src); - - if(src->source) free(src->source); - - return(r); -} + for(i = 0; i < s->req.count; i++) + munmap(s->buffer[i].start, s->buffer[i].length); -int src_grab(src_t *src) -{ - int r = src_v4l2.grab(src); - - if(!r) - { - if(!src->captured_frames) gettimeofday(&src->tv_first, NULL); - gettimeofday(&src->tv_last, NULL); - - src->captured_frames++; - } - - return(r); + return(0); } -/* Pointers are great things. Terrible things yes, but great. */ -/* These work but are very ugly and will be re-written soon. */ - -int src_set_option(src_option_t ***options, char *name, char *value) +static int close_v4l2(src_t *src) { - src_option_t **opts, *opt; - int count; - - if(!options) return(-1); - if(!*options) - { - *options = malloc(sizeof(src_option_t *)); - if(!*options) - { - /*ERROR("Out of memory.");*/ - return(-1); - } - - *options[0] = NULL; - } - - count = 0; - opts = *options; - while(*opts) - { - if((*opts)->name) if(!strcasecmp(name, (*opts)->name)) break; - opts++; - count++; - } - - if(!*opts) - { - void *new; - - opt = (src_option_t *) malloc(sizeof(src_option_t)); - if(!opt) - { - /*ERROR("Out of memory.");*/ - return(-1); - } - - new = realloc(*options, sizeof(src_option_t *) * (count + 2)); - if(!new) - { - free(opt); - /*ERROR("Out of memory.");*/ - return(-1); - } - - *options = (src_option_t **) new; - (*options)[count++] = opt; - (*options)[count++] = NULL; - - opt->name = strdup(name); - opt->value = NULL; - } - else opt = *opts; - - if(opt->value) - { - free(opt->value); - opt->value = NULL; - } - if(value) opt->value = strdup(value); - - return(0); + src_v4l2_t *s = (src_v4l2_t *) src->state; + + if(s->buffer) + { + if(!s->map) free(s->buffer[0].start); + else v4l2_free_mmap(src); + free(s->buffer); + } + if(s->fd >= 0) close(s->fd); + free(s); + + return(0); } -int src_get_option_by_number(src_option_t **opt, int number, - char **name, char **value) +//static +const char *query_v4ldevice(src_t *src, char **pixelformatdescription) { - int i; - - if(!opt || !name || !value) return(-1); - - i = 0; - while(*opt) - { - if(i == number) - { - *name = (*opt)->name; - *value = (*opt)->value; - return(0); - } - - i++; - } - - return(-1); + if(!src->source) + { + /*ERROR("No device name specified.");*/ + fprintf(stderr, "No device name specified."); + return NULL; + } + src_v4l2_t *s; + + /* Allocate memory for the state structure. */ + s = calloc(sizeof(src_v4l2_t), 1); + if(!s) + { + fprintf(stderr, "Out of memory."); + return NULL; + } + + src->state = (void *) s; + char value[200]; + //snprintf( value, sizeof(value), '\0' ); + //strcpy(*pixelformatdescription, (char*) value); + /* Open the device. */ + s->fd = open(src->source, O_RDWR | O_NONBLOCK); + if(s->fd < 0) + { + fprintf(stderr, "Cannot open device."); + free(s); + return NULL; + } + + if(ioctl(s->fd, VIDIOC_QUERYCAP, &s->cap) < 0) { + close_v4l2(src); + fprintf(stderr, "Cannot get capabilities."); + return NULL; + } + char *res = strdup((char*) s->cap.card); + /*strcpy(res, (char*) s->cap.card);*/ + if(!s->cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) { + // Device cannot capture + } + else { + struct v4l2_format format; + memset(&format,0,sizeof(format)); + format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (ioctl(s->fd,VIDIOC_G_FMT,&format) < 0) { + fprintf(stderr, "Cannot get format."); + // Cannot query + } + struct v4l2_fmtdesc fmt; + memset(&fmt,0,sizeof(fmt)); + fmt.index = 0; + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + struct v4l2_frmsizeenum sizes; + memset(&sizes,0,sizeof(sizes)); + + struct v4l2_frmivalenum rates; + memset(&rates,0,sizeof(rates)); + + while (ioctl(s->fd, VIDIOC_ENUM_FMT, &fmt) != -1) + { + /*strcpy(*pixelformatdescription, (char *) fmt.description);*/ + //*pixelformatdescription = strdup((char*)fmt.description); + snprintf( value, sizeof(value), ">%c%c%c%c", fmt.pixelformat >> 0, fmt.pixelformat >> 8, fmt.pixelformat >> 16, fmt.pixelformat >> 24 ); + strcat(*pixelformatdescription, (char *) 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(s->fd, VIDIOC_ENUM_FRAMESIZES, &sizes) != -1) { + struct v4l2_frmsize_discrete image_size = sizes.un.discrete; + // Query supported frame rates + rates.index = 0; + rates.pixel_format = fmt.pixelformat; + rates.width = image_size.width; + rates.height = image_size.height; + snprintf( value, sizeof(value), ":%dx%d=", image_size.width, image_size.height ); + strcat(*pixelformatdescription, (char *) value); + fprintf(stderr, "Size: %dx%d: ", image_size.width, image_size.height); + while (ioctl(s->fd, VIDIOC_ENUM_FRAMEINTERVALS, &rates) != -1) { + snprintf( value, sizeof(value), "%d/%d,", rates.un.discrete.denominator, rates.un.discrete.numerator ); + strcat(*pixelformatdescription, (char *) value); + fprintf(stderr, "%d/%d, ", rates.un.discrete.numerator, rates.un.discrete.denominator); + rates.index ++; + } + fprintf(stderr, "\n"); + sizes.index++; + } + + + /*[0x%08X] '%c%c%c%c' (%s)", v4l2_pal, + fmt.pixelformat, + fmt.pixelformat >> 0, fmt.pixelformat >> 8, + fmt.pixelformat >> 16, fmt.pixelformat >> 24*/ + fmt.index++; + } + /*else { + *pixelformatdescription = '\0'; + }*/ + } + close_v4l2(src); + return res; } -int src_get_option_by_name(src_option_t **opt, char *name, char **value) -{ - if(!opt || !name || !value) return(-1); - - while(*opt) - { - if((*opt)->name) - { - if(!strcasecmp(name, (*opt)->name)) - { - *value = (*opt)->value; - return(0); - } - } - - opt++; - } - - return(-1); -} -int src_free_options(src_option_t ***options) -{ - src_option_t **opts; - - if(!options || !*options) return(-1); - - opts = *options; - while(*opts) - { - if((*opts)->name) free((*opts)->name); - if((*opts)->value) free((*opts)->value); - - free(*opts); - - opts++; - } - - free(*options); - *options = NULL; - - return(0); -} diff --git a/src/v4l/src.h b/src/v4l/src.h index 3ce1d3b2..be2f0d99 100644 --- a/src/v4l/src.h +++ b/src/v4l/src.h @@ -11,6 +11,9 @@ extern "C" { #endif +#ifndef INC_SRC_H +#define INC_SRC_H + #include #include #include @@ -20,8 +23,9 @@ extern "C" { #include #include -#ifndef INC_SRC_H -#define INC_SRC_H +#include "videodev2.h" + + typedef unsigned char avgbmp_t; @@ -115,19 +119,6 @@ typedef struct { } src_t; -typedef struct { - - char *name; - - uint8_t flags; - - int (*open)(src_t *); - int (*close)(src_t *); - int (*grab)(src_t *); - const char *(*query)(src_t *, uint*, uint*, char **); - -} src_mod_t; - typedef struct { /* List of options. */ @@ -209,15 +200,31 @@ typedef struct { } fswebcam_config_t; -extern int src_open(src_t *src, char *source); -extern int src_close(src_t *src); -extern int src_grab(src_t *src); -extern const char *src_query(src_t *src, char *source, uint *width, uint *height, char **pixelformatdescription); -extern int src_set_option(src_option_t ***options, char *name, char *value); -extern int src_get_option_by_number(src_option_t **opt, int number, char **name, char **value); -extern int src_get_option_by_name(src_option_t **opt, char *name, char **value); -extern int src_free_options(src_option_t ***options); + +typedef struct { + void *start; + size_t length; +} v4l2_buffer_t; + +typedef struct { + + int fd; + char map; + + struct v4l2_capability cap; + struct v4l2_format fmt; + struct v4l2_requestbuffers req; + struct v4l2_buffer buf; + + v4l2_buffer_t *buffer; + + int pframe; + +} src_v4l2_t; + + +const char *query_v4ldevice(src_t *src, char **pixelformatdescription); #endif diff --git a/src/v4l/src_v4l2.c b/src/v4l/src_v4l2.c index 7d1e03f4..716804d9 100644 --- a/src/v4l/src_v4l2.c +++ b/src/v4l/src_v4l2.c @@ -24,55 +24,6 @@ #ifdef HAVE_V4L2 -typedef struct { - void *start; - size_t length; -} v4l2_buffer_t; - -typedef struct { - - int fd; - char map; - - struct v4l2_capability cap; - struct v4l2_format fmt; - struct v4l2_requestbuffers req; - struct v4l2_buffer buf; - - v4l2_buffer_t *buffer; - - int pframe; - -} src_v4l2_t; - -static int src_v4l2_close(src_t *src); - -typedef struct { - uint16_t src; - uint32_t v4l2; -} v4l2_palette_t; - -v4l2_palette_t v4l2_palette[] = { - { SRC_PAL_JPEG, V4L2_PIX_FMT_JPEG }, - { SRC_PAL_MJPEG, V4L2_PIX_FMT_MJPEG }, - { SRC_PAL_S561, V4L2_PIX_FMT_SPCA561 }, - { SRC_PAL_RGB24, V4L2_PIX_FMT_RGB24 }, - { SRC_PAL_BGR24, V4L2_PIX_FMT_BGR24 }, - { SRC_PAL_RGB32, V4L2_PIX_FMT_RGB32 }, - { SRC_PAL_BGR32, V4L2_PIX_FMT_BGR32 }, - { SRC_PAL_YUYV, V4L2_PIX_FMT_YUYV }, - { SRC_PAL_UYVY, V4L2_PIX_FMT_UYVY }, - { SRC_PAL_YUV420P, V4L2_PIX_FMT_YUV420 }, - { SRC_PAL_BAYER, V4L2_PIX_FMT_SBGGR8 }, - { SRC_PAL_SGBRG8, V4L2_PIX_FMT_SGBRG8 }, - { SRC_PAL_SGRBG8, V4L2_PIX_FMT_SGRBG8 }, - { SRC_PAL_RGB565, V4L2_PIX_FMT_RGB565 }, - { SRC_PAL_RGB555, V4L2_PIX_FMT_RGB555 }, - { SRC_PAL_Y16, V4L2_PIX_FMT_Y16 }, - { SRC_PAL_GREY, V4L2_PIX_FMT_GREY }, - { 0, 0 } -}; - int src_v4l2_get_capability(src_t *src) { src_v4l2_t *s = (src_v4l2_t *) src->state; @@ -857,15 +808,57 @@ static const char *src_v4l2_query(src_t *src, uint *width, uint *height, char ** memset(&fmt,0,sizeof(fmt)); fmt.index = 0; fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (ioctl(s->fd, VIDIOC_ENUM_FMT, &fmt) != -1) + + struct v4l2_frmsizeenum sizes; + memset(&sizes,0,sizeof(sizes)); + + struct v4l2_frmivalenum rates; + memset(&rates,0,sizeof(rates)); + char value[200]; + *pixelformatdescription = strdup((char *) "result:"); + + while (ioctl(s->fd, VIDIOC_ENUM_FMT, &fmt) != -1) { /*strcpy(*pixelformatdescription, (char *) fmt.description);*/ - *pixelformatdescription = strdup((char*)fmt.description); - fprintf(stderr, "format: %s", fmt.description); + //*pixelformatdescription = strdup((char*)fmt.description); + snprintf( value, sizeof(value), "%c%c%c%c:", fmt.pixelformat >> 0, fmt.pixelformat >> 8, fmt.pixelformat >> 16, fmt.pixelformat >> 24 ); + strcat(*pixelformatdescription, strdup((char *) 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(s->fd, VIDIOC_ENUM_FRAMESIZES, &sizes) != -1) { + struct v4l2_frmsize_discrete image_size = sizes.un.discrete; + // Query supported frame rates + rates.index = 0; + rates.pixel_format = fmt.pixelformat; + rates.width = image_size.width; + rates.height = image_size.height; + snprintf( value, sizeof(value), "%dx%d,", image_size.width, image_size.height ); + strcat(*pixelformatdescription, strdup((char *) value)); + fprintf(stderr, "Size: %dx%d: ", image_size.width, image_size.height); + while (ioctl(s->fd, VIDIOC_ENUM_FRAMEINTERVALS, &rates) != -1) { + snprintf( value, sizeof(value), "%d/%d,", rates.un.discrete.numerator, rates.un.discrete.denominator ); + strcat(*pixelformatdescription, strdup((char *) value)); + fprintf(stderr, "%d/%d, ", rates.un.discrete.numerator, rates.un.discrete.denominator); + rates.index ++; + } + fprintf(stderr, "\n"); + sizes.index++; + } + + + /*[0x%08X] '%c%c%c%c' (%s)", v4l2_pal, + fmt.pixelformat, + fmt.pixelformat >> 0, fmt.pixelformat >> 8, + fmt.pixelformat >> 16, fmt.pixelformat >> 24*/ + fmt.index++; } - else { + /*else { *pixelformatdescription = '\0'; - } + }*/ } src_v4l2_close(src); return res; diff --git a/src/v4l/v4lcapture.cpp b/src/v4l/v4lcapture.cpp index 76d9a4a6..530fe814 100644 --- a/src/v4l/v4lcapture.cpp +++ b/src/v4l/v4lcapture.cpp @@ -34,354 +34,42 @@ #include "v4lcapture.h" #include "kdenlivesettings.h" -#include "dec.h" -static src_t v4lsrc; -QImage add_image_png(src_t *src) +V4lCaptureHandler::V4lCaptureHandler() { - QImage im; - im.loadFromData((uchar *)src->img, src->length, "PNG"); - return im; -} - -QImage add_image_jpeg(src_t *src) -{ - uint32_t hlength; - uint8_t *himg = NULL; - QImage im; - - /* MJPEG data may lack the DHT segment required for decoding... */ - verify_jpeg_dht((uint8_t *) src->img, src->length, &himg, &hlength); - - im.loadFromData(himg, hlength, "JPG"); - free(himg); - return im; -} - -class MyDisplay : public QLabel -{ -public: - MyDisplay(QWidget *parent = 0); - void setImage(QImage img); - virtual void paintEvent(QPaintEvent *); - virtual void resizeEvent(QResizeEvent *); - -private: - QImage m_img; - bool m_clear; -}; - -MyDisplay::MyDisplay(QWidget *parent): - QLabel(parent) - , m_clear(false) -{ - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - setAttribute(Qt::WA_PaintOnScreen); - setAttribute(Qt::WA_OpaquePaintEvent); -} - -void MyDisplay::resizeEvent(QResizeEvent *) -{ - m_clear = true; -} - -void MyDisplay::paintEvent(QPaintEvent *) -{ - QPainter p(this); - if (m_clear) { - // widget resized, cleanup - p.fillRect(0, 0, width(), height(), palette().background()); - m_clear = false; - } - if (m_img.isNull()) return; - QImage img = m_img.scaled(width(), height(), Qt::KeepAspectRatio); - p.drawImage((width() - img.width()) / 2, (height() - img.height()) / 2, img); - p.end(); -} - -void MyDisplay::setImage(QImage img) -{ - m_img = img; - update(); } - - -V4lCaptureHandler::V4lCaptureHandler(QVBoxLayout *lay, QWidget *parent): - CaptureHandler(lay, parent) - , m_update(false) - , m_device(KdenliveSettings::video4vdevice()) - , m_width(-1) - , m_height(-1) -{ - if (lay == NULL) return; - m_display = new MyDisplay; - lay->addWidget(m_display); -} +//static QStringList V4lCaptureHandler::getDeviceName(QString input) { - fswebcam_config_t *config; - /* Prepare the configuration structure. */ - config = (fswebcam_config_t *) calloc(sizeof(fswebcam_config_t), 1); - if (!config) { - /*WARN("Out of memory.");*/ - fprintf(stderr, "Out of MEM...."); - return QStringList() << input; - } - - /* Set the defaults. */ - config->loop = 0; - config->offset = 0; - config->background = 0; - config->pidfile = NULL; - config->logfile = NULL; - config->gmt = 0; - config->start = 0; - config->device = strdup(input.toUtf8().constData()); - config->input = NULL; - config->tuner = 0; - config->frequency = 0; - config->delay = 0; - config->use_read = 0; - config->list = 0; - if (m_width > 0) config->width = m_width; - else config->width = 384; - if (m_height > 0) config->height = m_height; - else config->height = 288; - config->fps = 0; - config->frames = 1; - config->skipframes = 0; - config->palette = SRC_PAL_ANY; - config->option = NULL; - config->dumpframe = NULL; - config->jobs = 0; - config->job = NULL; - - /* Set defaults and parse the command line. */ - /*if(fswc_getopts(config, argc, argv)) return(-1);*/ + src_t v4lsrc; - - /* Record the start time. */ - config->start = time(NULL); /* Set source options... */ memset(&v4lsrc, 0, sizeof(v4lsrc)); - v4lsrc.input = config->input; - v4lsrc.tuner = config->tuner; - v4lsrc.frequency = config->frequency; - v4lsrc.delay = config->delay; + v4lsrc.input = NULL; + v4lsrc.tuner = 0; + v4lsrc.frequency = 0; + v4lsrc.delay = 0; v4lsrc.timeout = 10; /* seconds */ - v4lsrc.use_read = config->use_read; - v4lsrc.list = config->list; - v4lsrc.palette = config->palette; - v4lsrc.width = config->width; - v4lsrc.height = config->height; - v4lsrc.fps = config->fps; - v4lsrc.option = config->option; - char *source = config->device; - uint width = 0; - uint height = 0; + v4lsrc.use_read = 0; + v4lsrc.list = 0; + v4lsrc.palette = SRC_PAL_ANY; + v4lsrc.width = 384; + v4lsrc.height = 288; + v4lsrc.fps = 0; + v4lsrc.option = NULL; + v4lsrc.source = strdup(input.toUtf8().constData()); char *pixelformatdescription; - QString deviceName(src_query(&v4lsrc, source, &width, &height, &pixelformatdescription)); - free(config); + pixelformatdescription = (char *) calloc(2048, sizeof(char)); + QString deviceName(query_v4ldevice(&v4lsrc, &pixelformatdescription)); + QString info(pixelformatdescription); + free (pixelformatdescription); QStringList result; - result << (deviceName.isEmpty() ? input : deviceName) << (width == 0 ? QString() : QString("%1x%2").arg(width).arg(height)) << QString(pixelformatdescription); + result << (deviceName.isEmpty() ? input : deviceName) << info; return result; } -void V4lCaptureHandler::setDevice(const QString input, QString size) -{ - m_device = input; - if (!size.isEmpty()) { - m_width = size.section('x', 0, 0).toInt(); - m_height = size.section('x', -1).toInt(); - - } -} - -void V4lCaptureHandler::startPreview(int /*deviceId*/, int /*captureMode*/, bool) -{ - m_display->setHidden(false); - fswebcam_config_t *config; - /* Prepare the configuration structure. */ - config = (fswebcam_config_t *) calloc(sizeof(fswebcam_config_t), 1); - if (!config) { - /*WARN("Out of memory.");*/ - fprintf(stderr, "Out of MEM...."); - return; - } - - /* Set the defaults. */ - config->loop = 0; - config->offset = 0; - config->background = 0; - config->pidfile = NULL; - config->logfile = NULL; - config->gmt = 0; - config->start = 0; - config->device = strdup(m_device.toUtf8().constData()); - config->input = NULL; - config->tuner = 0; - config->frequency = 0; - config->delay = 0; - config->use_read = 0; - config->list = 0; - if (m_width > 0) config->width = m_width; - else config->width = KdenliveSettings::video4size().section("x", 0, 0).toInt();/*384;*/ - if (m_height > 0) config->height = m_height; - else config->height = KdenliveSettings::video4size().section("x", -1).toInt();/*288;*/ - config->fps = 0; - config->frames = 1; - config->skipframes = 0; - config->palette = SRC_PAL_ANY; - config->option = NULL; - config->dumpframe = NULL; - config->jobs = 0; - config->job = NULL; - - /* Set defaults and parse the command line. */ - /*if(fswc_getopts(config, argc, argv)) return(-1);*/ - - - /* Record the start time. */ - config->start = time(NULL); - /* Set source options... */ - memset(&v4lsrc, 0, sizeof(v4lsrc)); - v4lsrc.input = config->input; - v4lsrc.tuner = config->tuner; - v4lsrc.frequency = config->frequency; - v4lsrc.delay = config->delay; - v4lsrc.timeout = 10; /* seconds */ - v4lsrc.use_read = config->use_read; - v4lsrc.list = config->list; - v4lsrc.palette = config->palette; - v4lsrc.width = config->width; - v4lsrc.height = config->height; - v4lsrc.fps = config->fps; - v4lsrc.option = config->option; - char *source = config->device; - - if (src_open(&v4lsrc, source) != 0) return; - m_update = true; - free(config); - QTimer::singleShot(200, this, SLOT(slotUpdate())); -} - -V4lCaptureHandler::~V4lCaptureHandler() -{ - stopCapture(); -} - -void V4lCaptureHandler::slotUpdate() -{ - if (!m_update) return; - src_grab(&v4lsrc); - QImage qimg(v4lsrc.width, v4lsrc.height, QImage::Format_RGB888); - switch (v4lsrc.palette) { - case SRC_PAL_PNG: - qimg = add_image_png(&v4lsrc); - break; - case SRC_PAL_JPEG: - case SRC_PAL_MJPEG: - qimg = add_image_jpeg(&v4lsrc); - break; - case SRC_PAL_S561: - fswc_add_image_s561(qimg.bits(), (uchar *)v4lsrc.img, v4lsrc.length, v4lsrc.width, v4lsrc.height, v4lsrc.palette); - break; - case SRC_PAL_RGB32: - fswc_add_image_rgb32(&v4lsrc, qimg.bits()); - break; - case SRC_PAL_BGR32: - fswc_add_image_bgr32(&v4lsrc, qimg.bits()); - break; - case SRC_PAL_RGB24: - fswc_add_image_rgb24(&v4lsrc, qimg.bits()); - break; - case SRC_PAL_BGR24: - fswc_add_image_bgr24(&v4lsrc, qimg.bits()); - break; - case SRC_PAL_BAYER: - case SRC_PAL_SGBRG8: - case SRC_PAL_SGRBG8: - fswc_add_image_bayer(qimg.bits(), (uchar *)v4lsrc.img, v4lsrc.length, v4lsrc.width, v4lsrc.height, v4lsrc.palette); - break; - case SRC_PAL_YUYV: - case SRC_PAL_UYVY: - fswc_add_image_yuyv(&v4lsrc, (avgbmp_t *)qimg.bits()); - break; - case SRC_PAL_YUV420P: - fswc_add_image_yuv420p(&v4lsrc, qimg.bits()); - break; - case SRC_PAL_NV12MB: - fswc_add_image_nv12mb(&v4lsrc, qimg.bits()); - break; - case SRC_PAL_RGB565: - fswc_add_image_rgb565(&v4lsrc, qimg.bits()); - break; - case SRC_PAL_RGB555: - fswc_add_image_rgb555(&v4lsrc, qimg.bits()); - break; - case SRC_PAL_Y16: - fswc_add_image_y16(&v4lsrc, qimg.bits()); - break; - case SRC_PAL_GREY: - fswc_add_image_grey(&v4lsrc, qimg.bits()); - break; - } - if (m_analyseFrame) { - emit gotFrame(qimg); - } - if (!m_captureFramePath.isEmpty()) { - qimg.save(m_captureFramePath); - emit frameSaved(m_captureFramePath); - m_captureFramePath.clear(); - } - if (!m_overlayImage.isNull()) { - // overlay image - QPainter p(&qimg); - p.setOpacity(0.5); - p.drawImage(0, 0, m_overlayImage); - p.end(); - } - m_display->setImage(qimg); - if (m_update) QTimer::singleShot(50, this, SLOT(slotUpdate())); -} - -void V4lCaptureHandler::startCapture(const QString &/*path*/) -{ -} - -void V4lCaptureHandler::stopCapture() -{ -} - -void V4lCaptureHandler::captureFrame(const QString &fname) -{ - m_captureFramePath = fname; -} - -void V4lCaptureHandler::showOverlay(QImage img, bool /*transparent*/) -{ - m_overlayImage = img; -} - -void V4lCaptureHandler::hideOverlay() -{ - m_overlayImage = QImage(); -} - -void V4lCaptureHandler::hidePreview(bool hide) -{ - m_display->setHidden(hide); -} - -void V4lCaptureHandler::stopPreview() -{ - m_display->setHidden(true); - if (!m_update) return; - m_update = false; - src_close(&v4lsrc); -} diff --git a/src/v4l/v4lcapture.h b/src/v4l/v4lcapture.h index 4a72ed13..03bd9c3b 100644 --- a/src/v4l/v4lcapture.h +++ b/src/v4l/v4lcapture.h @@ -20,7 +20,6 @@ #ifndef __V4LCAPTUREHANDLER_H__ #define __V4LCAPTUREHANDLER_H__ -#include "../stopmotion/capturehandler.h" #include "src.h" #include @@ -28,37 +27,12 @@ #include #include -class MyDisplay; - -class V4lCaptureHandler : public CaptureHandler +class V4lCaptureHandler { - Q_OBJECT -public: - V4lCaptureHandler(QVBoxLayout *lay, QWidget *parent = 0); - ~V4lCaptureHandler(); - void startPreview(int deviceId, int captureMode, bool audio = true); - void stopPreview(); - void startCapture(const QString &path); - void stopCapture(); - void captureFrame(const QString &fname); - void showOverlay(QImage img, bool transparent = true); - void hideOverlay(); - void hidePreview(bool hide); - QStringList getDeviceName(QString input); - /** @brief Sets the path to the capture devide and optionnaly the width / height of the capture. */ - void setDevice(const QString input, QString size = QString()); -private: - bool m_update; - MyDisplay *m_display; - QString m_captureFramePath; - QImage m_overlayImage; - QString m_device; - int m_width; - int m_height; - -private slots: - void slotUpdate(); +public: + V4lCaptureHandler(); + static QStringList getDeviceName(QString input); }; diff --git a/src/v4l/videodev2.h b/src/v4l/videodev2.h index 1e0c066f..1248522c 100644 --- a/src/v4l/videodev2.h +++ b/src/v4l/videodev2.h @@ -395,7 +395,7 @@ struct v4l2_frmsizeenum { union { /* Frame size */ struct v4l2_frmsize_discrete discrete; struct v4l2_frmsize_stepwise stepwise; - }; + } un; __u32 reserved[2]; /* Reserved space for future use */ }; @@ -425,7 +425,7 @@ struct v4l2_frmivalenum { union { /* Frame interval */ struct v4l2_fract discrete; struct v4l2_frmival_stepwise stepwise; - }; + } un; __u32 reserved[2]; /* Reserved space for future use */ }; diff --git a/src/widgets/configcapture_ui.ui b/src/widgets/configcapture_ui.ui index 9f7a5fa3..2697db9f 100644 --- a/src/widgets/configcapture_ui.ui +++ b/src/widgets/configcapture_ui.ui @@ -7,7 +7,7 @@ 0 0 405 - 470 + 563 @@ -63,7 +63,7 @@ - 0 + 1 @@ -180,200 +180,136 @@ Video4Linux - - - - - Video - - - - - - Device - - - - - - - - - - - - - - - 0 - 0 - - - - Format - - - - - - - - - - - - - - - 0 - 0 - - - - Image size - - - - - - - - - - - - - - Frame rate - - - - - - - 15 - - - - - - - Codec - - - - - - - + + + + + Detected devices + - - - - Audio + + + + + + + Video device - - - - - Audio device - - - - - - - - - - - - - - - 0 - 0 - - - - Format - - - - - - - - - - - - - - Audio codec - - - - - - - - - - - Container + + + + - - - - - Format - - - - - - - - - - Extension - - - - - - - - - - - - 0 - 0 - + + + + Size: + + + + - Capture params + 720x576 - - - - - 0 - 0 - + + + + 25/1 + + + + + + + Pixel aspect ratio: + + + + + + + 59/54 + + + Display aspect ratio: + + + + + + + 4/3 + + + + + + + Colorspace + + + + + + + + + + + + + + Interlaced + + + + + + + Qt::Horizontal + + + + 127 + 21 + + + + + + + + Edit + + + + + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + + @@ -382,14 +318,21 @@ - Encoding params + Encoding parameters - - + + + + + 0 + 0 + + + - + Qt::Vertical @@ -402,19 +345,54 @@ - - - - - Select device in list - - + + + + File extension + - - + + + + 5 + + + + + - Detected devices + Capture format + + + + + + + + + + Frame rate: + + + + + + + Capture audio (ALSA) + + + + + + + false + + + + 0 + 0 + @@ -908,11 +886,6 @@ kcfg_firewireformat kcfg_firewireautosplit kcfg_firewiretimestamp - kcfg_video4vdevice - kcfg_video4vformat - kcfg_video4adevice - kcfg_video4aformat - kcfg_video4encoding kcfg_rmd_capture_audio radioButton_2 kcfg_rmd_alsa_device diff --git a/src/widgets/stopmotion_ui.ui b/src/widgets/stopmotion_ui.ui index 0d7d35cc..a31b3ed7 100644 --- a/src/widgets/stopmotion_ui.ui +++ b/src/widgets/stopmotion_ui.ui @@ -67,56 +67,52 @@ - - - - Sequence name + + + + - - true + + ... + + + QToolButton::InstantPopup - - + + - Capture device + Live view - - - - - - - 0 - 0 - + + ... - - 10 + + true - - false + + true - - + + + + Capture frame + - Add to project + ... - - + + - Preview sequence - - - + Interval capture ... @@ -126,23 +122,17 @@ - - - - - + + ... - - QToolButton::InstantPopup - - - + + - Live view + Preview sequence @@ -153,9 +143,6 @@ true - - true - @@ -165,33 +152,46 @@ - - - - ... + + + + Sequence name + + + true - - + + - Interval capture - - - ... + Capture device - - true + + - - - - Capture frame + + + + + 0 + 0 + + + + 10 + + false + + + + + - ... + Add to project diff --git a/src/widgets/wizardcapture_ui.ui b/src/widgets/wizardcapture_ui.ui index 0c151a63..96b1b750 100644 --- a/src/widgets/wizardcapture_ui.ui +++ b/src/widgets/wizardcapture_ui.ui @@ -6,55 +6,49 @@ 0 0 - 273 - 153 + 279 + 110 Form - + Autodetected capture devices - + Check - - - - - 0 - 0 - - - - true + + + + - - true + + + + + + + + + Select capture format - - - Device Name - - - - - Capacities - - - + + + + Qt::Vertical @@ -67,15 +61,15 @@ - - - - - - - + + + KComboBox + QComboBox +
kcombobox.h
+
+
diff --git a/src/wizard.cpp b/src/wizard.cpp index ed2ce32c..985aa4d2 100644 --- a/src/wizard.cpp +++ b/src/wizard.cpp @@ -128,7 +128,8 @@ Wizard::Wizard(bool upgrade, QWidget *parent) : page6->setTitle(i18n("Webcam")); m_capture.setupUi(page6); connect(m_capture.button_reload, SIGNAL(clicked()), this, SLOT(slotDetectWebcam())); - connect(m_capture.device_list, SIGNAL(itemSelectionChanged()), this, SLOT(slotUpdateCaptureParameters())); + connect(m_capture.v4l_devices, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdateCaptureParameters())); + connect(m_capture.v4l_formats, SIGNAL(currentIndexChanged(int)), this, SLOT(slotSaveCaptureFormat())); m_capture.button_reload->setIcon(KIcon("view-refresh")); addPage(page6); @@ -148,47 +149,90 @@ Wizard::Wizard(bool upgrade, QWidget *parent) : void Wizard::slotDetectWebcam() { #if !defined(Q_WS_MAC) && !defined(Q_OS_FREEBSD) - m_capture.device_list->clear(); + m_capture.v4l_devices->blockSignals(true); + m_capture.v4l_devices->clear(); // Video 4 Linux device detection - V4lCaptureHandler v4l(NULL); for (int i = 0; i < 10; i++) { QString path = "/dev/video" + QString::number(i); if (QFile::exists(path)) { - QStringList deviceInfo = v4l.getDeviceName(path.toUtf8().constData()); + QStringList deviceInfo = V4lCaptureHandler::getDeviceName(path.toUtf8().constData()); if (!deviceInfo.isEmpty()) { - QTreeWidgetItem *item = new QTreeWidgetItem(m_capture.device_list, QStringList() << deviceInfo.at(0) << "(" + deviceInfo.at(1) + ") " + deviceInfo.at(2)); - item->setData(0, Qt::UserRole, path); - item->setData(0, Qt::UserRole + 1, deviceInfo.at(1)); + m_capture.v4l_devices->addItem(deviceInfo.at(0), path); + m_capture.v4l_devices->setItemData(m_capture.v4l_devices->count() - 1, deviceInfo.at(1), Qt::UserRole + 1); } } } - if (m_capture.device_list->topLevelItemCount() > 0) { + if (m_capture.v4l_devices->count() > 0) { m_capture.v4l_status->setText(i18n("Select your default video4linux device")); // select default device bool found = false; - for (int i = 0; i < m_capture.device_list->topLevelItemCount(); i++) { - QTreeWidgetItem * item = m_capture.device_list->topLevelItem(i); - if (item && item->data(0, Qt::UserRole).toString() == KdenliveSettings::video4vdevice()) { - m_capture.device_list->setCurrentItem(item); + for (int i = 0; i < m_capture.v4l_devices->count(); i++) { + QString device = m_capture.v4l_devices->itemData(i).toString(); + if (device == KdenliveSettings::video4vdevice()) { + m_capture.v4l_devices->setCurrentIndex(i); found = true; break; } } - if (!found) m_capture.device_list->setCurrentItem(m_capture.device_list->topLevelItem(0)); + slotUpdateCaptureParameters(); + if (!found) m_capture.v4l_devices->setCurrentIndex(0); } else m_capture.v4l_status->setText(i18n("No device found, plug your webcam and refresh.")); + m_capture.v4l_devices->blockSignals(false); #endif } void Wizard::slotUpdateCaptureParameters() { - QTreeWidgetItem * item = m_capture.device_list->currentItem(); - if (!item) return; - QString device = item->data(0, Qt::UserRole).toString(); + QString device = m_capture.v4l_devices->itemData(m_capture.v4l_devices->currentIndex()).toString(); if (!device.isEmpty()) KdenliveSettings::setVideo4vdevice(device); - QString size = item->data(0, Qt::UserRole + 1).toString(); - if (!size.isEmpty()) KdenliveSettings::setVideo4size(size); + QString formats = m_capture.v4l_devices->itemData(m_capture.v4l_devices->currentIndex(), Qt::UserRole + 1).toString(); + + m_capture.v4l_formats->blockSignals(true); + m_capture.v4l_formats->clear(); + + QString vl4ProfilePath = KStandardDirs::locateLocal("appdata", "profiles/video4linux"); + if (QFile::exists(vl4ProfilePath)) { + MltVideoProfile profileInfo = ProfilesDialog::getVideoProfile(vl4ProfilePath); + m_capture.v4l_formats->addItem(i18n("Current settings (%1x%2, %3/%4fps)", profileInfo.width, profileInfo.height, profileInfo.frame_rate_num, profileInfo.frame_rate_den), QStringList() << QString("unknown") <", QString::SkipEmptyParts); + QString itemSize; + QString pixelFormat; + QStringList itemRates; + for (int i = 0; i < pixelformats.count(); i++) { + QString format = pixelformats.at(i).section(':', 0, 0); + QStringList sizes = pixelformats.at(i).split(":", QString::SkipEmptyParts); + pixelFormat = sizes.takeFirst(); + for (int j = 0; j < sizes.count(); j++) { + itemSize = sizes.at(j).section("=", 0, 0); + itemRates = sizes.at(j).section("=", 1, 1).split(",", QString::SkipEmptyParts); + for (int k = 0; k < itemRates.count(); k++) { + m_capture.v4l_formats->addItem("[" + format + "] " + itemSize + " (" + itemRates.at(k) + ")", QStringList() << format << itemSize.section('x', 0, 0) << itemSize.section('x', 1, 1) << itemRates.at(k).section('/', 0, 0) << itemRates.at(k).section('/', 1, 1)); + } + } + } + if (!QFile::exists(vl4ProfilePath)) { + if (m_capture.v4l_formats->count() > 9) slotSaveCaptureFormat(); + else { + // No existing profile and no autodetected profiles + MltVideoProfile profileInfo; + profileInfo.width = 320; + profileInfo.height = 200; + profileInfo.frame_rate_num = 15; + profileInfo.frame_rate_den = 1; + profileInfo.display_aspect_num = 4; + profileInfo.display_aspect_den = 3; + profileInfo.sample_aspect_num = 1; + profileInfo.sample_aspect_den = 1; + profileInfo.progressive = 1; + profileInfo.colorspace = 601; + ProfilesDialog::saveProfile(profileInfo, vl4ProfilePath); + m_capture.v4l_formats->addItem(i18n("Default settings (%1x%2, %3/%4fps)", profileInfo.width, profileInfo.height, profileInfo.frame_rate_num, profileInfo.frame_rate_den), QStringList() << QString("unknown") <blockSignals(false); } void Wizard::checkMltComponents() @@ -646,4 +690,24 @@ void Wizard::slotShowWebInfos() KRun::runUrl(KUrl("http://kdenlive.org/discover/" + QString(kdenlive_version).section(' ', 0, 0)), "text/html", this); } +void Wizard::slotSaveCaptureFormat() +{ + QStringList format = m_capture.v4l_formats->itemData(m_capture.v4l_formats->currentIndex()).toStringList(); + if (format.isEmpty()) return; + MltVideoProfile profile; + profile.description = "Video4Linux capture"; + profile.colorspace = 601; + profile.width = format.at(1).toInt(); + profile.height = format.at(2).toInt(); + profile.sample_aspect_num = 1; + profile.sample_aspect_den = 1; + profile.display_aspect_num = format.at(1).toInt(); + profile.display_aspect_den = format.at(2).toInt(); + profile.frame_rate_num = format.at(3).toInt(); + profile.frame_rate_den = format.at(4).toInt(); + profile.progressive = 1; + QString vl4ProfilePath = KStandardDirs::locateLocal("appdata", "profiles/video4linux"); + ProfilesDialog::saveProfile(profile, vl4ProfilePath); +} + #include "wizard.moc" diff --git a/src/wizard.h b/src/wizard.h index da2a5513..4a0d1344 100644 --- a/src/wizard.h +++ b/src/wizard.h @@ -106,6 +106,7 @@ private slots: void slotShowWebInfos(); void slotDetectWebcam(); void slotUpdateCaptureParameters(); + void slotSaveCaptureFormat(); }; #endif -- 2.39.2