Complete rewrite of the video4linux capture to use MLT, in progress.

Breaks Decklink capture for a few days...

svn path=/trunk/kdenlive/; revision=5607
parent 13caa29b
......@@ -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")
......
/***************************************************************************
* 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);
}
/***************************************************************************
* 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 <QObject>
#include <QVector>
#include <QWidget>
#include <QImage>
#include <QPainter>
#include <QFrame>
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<int16_t>, 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
......@@ -12,7 +12,7 @@
#include "abstractgfxscopewidget.h"
#include "renderer.h"
#include "monitor.h"
#include "monitormanager.h"
#include <QFuture>
#include <QColor>
......@@ -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();
}
}
......
......@@ -11,31 +11,29 @@
#ifndef ABSTRACTGFXSCOPEWIDGET_H
#define ABSTRACTGFXSCOPEWIDGET_H
#include <QtCore>
#include <QWidget>
#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);
......
......@@ -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);
......
......@@ -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;
......
......@@ -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);
......
......@@ -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;
......
......@@ -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();
......
......@@ -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;
......
......@@ -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();
......
......@@ -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;
......
......@@ -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.
......
......@@ -312,64 +312,39 @@
<default>0</default>
</entry>
<entry name="video4vformat" type="String">
<label>Default video4linux capture format.</label>
<default>video4linux2</default>
</entry>
<entry name="video4vdevice" type="String">
<label>Default video4linux capture format.</label>
<default>/dev/video0</default>
</entry>
<entry name="video4vcodec" type="String">
<label>Default video4linux video codec.</label>
<default>mjpeg</default>
<entry name="v4l_alsadevice" type="UInt">
<label>Audio device for v4l capture.</label>
<default></default>
</entry>
<entry name="video4acodec" type="String">
<label>Default video4linux audio codec.</label>
<default>mp2</default>
<entry name="v4l_alsadevicename" type="String">
<label>Audio device for v4l capture.</label>
<default>hw:0,0</default>
</entry>
<entry name="video4container" type="String">
<entry name="v4l_parameters" type="String">
<label>Default video4linux format.</label>
<default>avi</default>
<default>f=mpeg acodec=mp2 ab=128k ar=48000 vcodec=mpeg2video minrate=0 b=4000k bf=2 b_strategy=1 trellis=1</default>
</entry>
<entry name="video4extension" type="String">
<entry name="v4l_extension" type="String">
<label>Default video4linux file extension.</label>
<default>avi</default>
</entry>
<entry name="video4aformat" type="String">
<label>Default video4linux capture format.</label>
<default>oss</default>
</entry>
<entry name="video4adevice" type="String">
<label>Default video4linux capture format.</label>
<default>/dev/dsp</default>
<default>mpeg</default>
</entry>
<entry name="video4size" type="String">
<label>Default video4linux capture format.</label>
<default>320x240</default>
</entry>
<entry name="video4rate" type="Int">
<label>Default video4linux capture format.</label>
<default>15</default>
</entry>
<entry name="video4capture" type="String">
<label>ffmpeg arguments for video capture.</label>
<default></default>
<entry name="v4l_format" type="UInt">
<label>Selected capture format.</label>
<default>0</default>
</entry>
<entry name="video4encoding" type="String">
<label>ffmpeg encoding arguments.</label>
<default>-qscale 1 -ab 224k</default>
<entry name="v4l_captureaudio" type="Bool">
<label>Should we also capture audio.</label>
<default>false</default>
</entry>
<entry name="rmd_capture_audio" type="Bool">
......
......@@ -53,7 +53,7 @@ KdenliveSettingsDialog::KdenliveSettingsDialog(const QMap<QString, QString>& 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<QString, QString>& 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<QString, QString>& 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<QString, QString>& 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);
}