From 6e4e0674068d82fd7c5e2fc96cad92014cbb1721 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Mardelle Date: Tue, 12 Oct 2010 22:21:12 +0000 Subject: [PATCH] Preliminary support for blackmagic capture (only .raw format for now, cannot be read by kdenlive) svn path=/trunk/kdenlive/; revision=4987 --- src/blackmagic/capture.cpp | 58 +++++++++++++++++--- src/blackmagic/capture.h | 15 ++++-- src/kdenlivesettings.kcfg | 11 ++++ src/recmonitor.cpp | 94 ++++++++++++++++++++++++++++++--- src/recmonitor.h | 15 +++++- src/stopmotion/stopmotion.cpp | 6 +++ src/stopmotion/stopmotion.h | 6 +++ src/widgets/configcapture_ui.ui | 17 ++++++ src/widgets/recmonitor_ui.ui | 86 +++++++++++++++++++++--------- src/widgets/stopmotion_ui.ui | 28 +++++++--- 10 files changed, 286 insertions(+), 50 deletions(-) diff --git a/src/blackmagic/capture.cpp b/src/blackmagic/capture.cpp index e6e618c5..021413b0 100644 --- a/src/blackmagic/capture.cpp +++ b/src/blackmagic/capture.cpp @@ -45,6 +45,7 @@ #endif #include +#include #include "capture.h" #include "kdenlivesettings.h" @@ -58,8 +59,6 @@ static BMDTimecodeFormat g_timecodeFormat = 0; static int g_videoModeIndex = -1; static int g_audioChannels = 2; static int g_audioSampleDepth = 16; -const char * g_videoOutputFile = NULL; -const char * g_audioOutputFile = NULL; static int g_maxFrames = -1; static QString doCaptureFrame; static double g_aspect_ratio = 16.0 / 9.0; @@ -416,6 +415,7 @@ HRESULT DeckLinkCaptureDelegate::VideoInputFrameArrived(IDeckLinkVideoInputFrame if (videoFrame->GetFlags() & bmdFrameHasNoInputSource) { + emit gotMessage(i18n("Frame (%1) - No input signal", frameCount)); fprintf(stderr, "Frame received (#%lu) - No input signal detected\n", frameCount); } else @@ -429,7 +429,8 @@ HRESULT DeckLinkCaptureDelegate::VideoInputFrameArrived(IDeckLinkVideoInputFrame timecode->GetString(&timecodeString); } } - + // There seems to be No timecode with HDMI... Using frame number + emit gotTimeCode(frameCount); /*fprintf(stderr, "Frame received (#%lu) [%s] - %s - Size: %li bytes\n", frameCount, timecodeString != NULL ? timecodeString : "No timecode", @@ -588,6 +589,7 @@ void CaptureHandler::startPreview(int deviceId, int captureMode) if (!deckLinkIterator) { + emit gotMessage(i18n("This application requires the DeckLink drivers installed.")); fprintf(stderr, "This application requires the DeckLink drivers installed.\n"); stopCapture(); return; @@ -599,6 +601,7 @@ void CaptureHandler::startPreview(int deviceId, int captureMode) if (result != S_OK) { fprintf(stderr, "No DeckLink PCI cards found.\n"); + emit gotMessage(i18n("No DeckLink PCI cards found.")); stopCapture(); return; } @@ -610,6 +613,8 @@ void CaptureHandler::startPreview(int deviceId, int captureMode) } delegate = new DeckLinkCaptureDelegate(); + connect(delegate, SIGNAL(gotTimeCode(ulong)), this, SIGNAL(gotTimeCode(ulong))); + connect(delegate, SIGNAL(gotMessage(const QString &)), this, SIGNAL(gotMessage(const QString &))); deckLinkInput->SetCallback(delegate); previewView = new CDeckLinkGLWidget(deckLinkInput, m_parent); @@ -622,6 +627,7 @@ void CaptureHandler::startPreview(int deviceId, int captureMode) result = deckLinkInput->GetDisplayModeIterator(&displayModeIterator); if (result != S_OK) { + emit gotMessage(i18n("Could not obtain the video output display mode iterator - result = ", result)); fprintf(stderr, "Could not obtain the video output display mode iterator - result = %08x\n", result); stopCapture(); return; @@ -701,16 +707,18 @@ void CaptureHandler::startPreview(int deviceId, int captureMode) if (g_videoModeIndex < 0) { + emit gotMessage(i18n("No video mode specified")); fprintf(stderr, "No video mode specified\n"); stopCapture(); return; } - //g_videoOutputFile="/home/one/bm.raw"; - if (g_videoOutputFile != NULL) + + /*if (g_videoOutputFile != NULL) { videoOutputFile = open(g_videoOutputFile, O_WRONLY|O_CREAT|O_TRUNC, 0664); if (videoOutputFile < 0) { + emit gotMessage(i18n("Could not open video output file %1", g_videoOutputFile)); fprintf(stderr, "Could not open video output file \"%s\"\n", g_videoOutputFile); stopCapture(); } @@ -720,10 +728,11 @@ void CaptureHandler::startPreview(int deviceId, int captureMode) audioOutputFile = open(g_audioOutputFile, O_WRONLY|O_CREAT|O_TRUNC, 0664); if (audioOutputFile < 0) { + emit gotMessage(i18n("Could not open audio output file %1", g_audioOutputFile)); fprintf(stderr, "Could not open audio output file \"%s\"\n", g_audioOutputFile); stopCapture(); } - } + }*/ while (displayModeIterator->Next(&displayMode) == S_OK) { @@ -742,6 +751,7 @@ void CaptureHandler::startPreview(int deviceId, int captureMode) if (result == bmdDisplayModeNotSupported) { + emit gotMessage(i18n("The display mode %1 is not supported with the selected pixel format", displayModeName)); fprintf(stderr, "The display mode %s is not supported with the selected pixel format\n", displayModeName); stopCapture(); return; @@ -751,6 +761,7 @@ void CaptureHandler::startPreview(int deviceId, int captureMode) { if (!(displayMode->GetFlags() & bmdDisplayModeSupports3D)) { + emit gotMessage(i18n("The display mode %1 is not supported with 3D", displayModeName)); fprintf(stderr, "The display mode %s is not supported with 3D\n", displayModeName); stopCapture(); return; @@ -765,6 +776,7 @@ void CaptureHandler::startPreview(int deviceId, int captureMode) if (!foundDisplayMode) { + emit gotMessage(i18n("Invalid mode %1 specified", g_videoModeIndex)); fprintf(stderr, "Invalid mode %d specified\n", g_videoModeIndex); stopCapture(); return; @@ -773,6 +785,7 @@ void CaptureHandler::startPreview(int deviceId, int captureMode) result = deckLinkInput->EnableVideoInput(selectedDisplayMode, pixelFormat, inputFlags); if(result != S_OK) { + emit gotMessage(i18n("Failed to enable video input. Is another application using the card?")); fprintf(stderr, "Failed to enable video input. Is another application using the card?\n"); stopCapture(); return; @@ -789,6 +802,7 @@ void CaptureHandler::startPreview(int deviceId, int captureMode) if(result != S_OK) { qDebug()<<"/// CAPTURE FAILED...."; + emit gotMessage(i18n("Capture failed")); } // All Okay. @@ -834,12 +848,42 @@ CaptureHandler::~CaptureHandler() stopCapture(); } -void CaptureHandler::startCapture() +void CaptureHandler::startCapture(const QString &path) { + int i = 0; + QString videopath = path + "_video_" + QString::number(i).rightJustified(4, '0', false) + ".raw"; + QString audiopath = path + "_audio_" + QString::number(i).rightJustified(4, '0', false) + ".raw"; + while (QFile::exists(videopath) || QFile::exists(audiopath)) { + i++; + videopath = path + "_video_" + QString::number(i).rightJustified(4, '0', false) + ".raw"; + audiopath = path + "_audio_" + QString::number(i).rightJustified(4, '0', false) + ".raw"; + } + videoOutputFile = open(videopath.toUtf8().constData(), O_WRONLY|O_CREAT|O_TRUNC, 0664); + if (videoOutputFile < 0) + { + emit gotMessage(i18n("Could not open video output file %1", videopath)); + fprintf(stderr, "Could not open video output file \"%s\"\n", videopath.toUtf8().constData()); + return; + } + if (KdenliveSettings::hdmicaptureaudio()) { + audioOutputFile = open(audiopath.toUtf8().constData(), O_WRONLY|O_CREAT|O_TRUNC, 0664); + if (audioOutputFile < 0) + { + emit gotMessage(i18n("Could not open audio output file %1", audiopath)); + fprintf(stderr, "Could not open video output file \"%s\"\n", audiopath.toUtf8().constData()); + return; + } + } } void CaptureHandler::stopCapture() { + if (videoOutputFile) + close(videoOutputFile); + if (audioOutputFile) + close(audioOutputFile); + videoOutputFile = -1; + audioOutputFile = -1; } void CaptureHandler::captureFrame(const QString &fname) diff --git a/src/blackmagic/capture.h b/src/blackmagic/capture.h index 491c63dc..c876affd 100644 --- a/src/blackmagic/capture.h +++ b/src/blackmagic/capture.h @@ -10,8 +10,9 @@ class CDeckLinkGLWidget; class PlaybackDelegate; -class DeckLinkCaptureDelegate : public IDeckLinkInputCallback +class DeckLinkCaptureDelegate : public QObject, public IDeckLinkInputCallback { +Q_OBJECT public: DeckLinkCaptureDelegate(); virtual ~DeckLinkCaptureDelegate(); @@ -25,17 +26,21 @@ public: private: ULONG m_refCount; pthread_mutex_t m_mutex; +signals: + void gotTimeCode(ulong); + void gotMessage(const QString &); }; -class CaptureHandler +class CaptureHandler : public QObject { + Q_OBJECT public: CaptureHandler(QVBoxLayout *lay, QWidget *parent = 0); ~CaptureHandler(); CDeckLinkGLWidget *previewView; void startPreview(int deviceId, int captureMode); void stopPreview(); - void startCapture(); + void startCapture(const QString &path); void stopCapture(); void captureFrame(const QString &fname); void showOverlay(QImage img, bool transparent = true); @@ -51,6 +56,10 @@ private: IDeckLinkDisplayModeIterator *displayModeIterator; QVBoxLayout *m_layout; QWidget *m_parent; + +signals: + void gotTimeCode(ulong); + void gotMessage(const QString &); }; diff --git a/src/kdenlivesettings.kcfg b/src/kdenlivesettings.kcfg index bcaaf4c4..6c2d17e9 100644 --- a/src/kdenlivesettings.kcfg +++ b/src/kdenlivesettings.kcfg @@ -623,6 +623,17 @@ 0 + + + + capture + + + + + true + + diff --git a/src/recmonitor.cpp b/src/recmonitor.cpp index 24cdd811..b5dac160 100644 --- a/src/recmonitor.cpp +++ b/src/recmonitor.cpp @@ -50,7 +50,9 @@ RecMonitor::RecMonitor(QString name, QWidget *parent) : m_isActive(false), m_isCapturing(false), m_didCapture(false), - m_isPlaying(false) + m_isPlaying(false), + m_bmCapture(NULL), + m_hdmiCapturing(false) { setupUi(this); @@ -58,8 +60,6 @@ RecMonitor::RecMonitor(QString name, QWidget *parent) : device_selector->setCurrentIndex(KdenliveSettings::defaultcapture()); connect(device_selector, SIGNAL(currentIndexChanged(int)), this, SLOT(slotVideoDeviceChanged(int))); - - QToolBar *toolbar = new QToolBar(name, this); QHBoxLayout *layout = new QHBoxLayout; layout->setContentsMargins(0, 0, 0, 0); @@ -92,8 +92,12 @@ RecMonitor::RecMonitor(QString name, QWidget *parent) : configAction->setCheckable(false); layout->addWidget(toolbar); - + layout->addWidget(&m_logger); layout->addWidget(&m_dvinfo); + m_logger.setMaxCount(10); + m_logger.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + m_logger.setFrame(false); + //m_logger.setInsertPolicy(QComboBox::InsertAtTop); #if KDE_IS_VERSION(4,2,0) m_freeSpace = new KCapacityBar(KCapacityBar::DrawTextInline, this); @@ -179,6 +183,12 @@ void RecMonitor::slotUpdateCaptureFolder(const QString currentProjectFolder) void RecMonitor::slotVideoDeviceChanged(int ix) { + QString capturefile; + QString capturename; + m_fwdAction->setVisible(ix != HDMI); + m_discAction->setVisible(ix != HDMI); + m_rewAction->setVisible(ix != HDMI); + m_logger.setVisible(ix == HDMI); switch (ix) { case SCREENGRAB: m_discAction->setEnabled(false); @@ -204,6 +214,21 @@ void RecMonitor::slotVideoDeviceChanged(int ix) m_playAction->setEnabled(true); checkDeviceAvailability(); break; + case HDMI: + createHDMIDevice(); + m_recAction->setEnabled(false); + m_stopAction->setEnabled(false); + m_playAction->setEnabled(true); + video_capture->setHidden(true); + video_frame->setHidden(false); + + capturefile = m_capturePath; + if (!capturefile.endsWith("/")) capturefile.append("/"); + capturename = KdenliveSettings::hdmifilename(); + capturename.append("xxx.raw"); + capturefile.append(capturename); + video_frame->setPixmap(mergeSideBySide(KIcon("camera-photo").pixmap(QSize(50, 50)), i18n("Plug your camcorder and\npress play button\nto start preview.\nFiles will be saved in:\n%1", capturefile))); + break; default: // FIREWIRE m_discAction->setEnabled(true); m_recAction->setEnabled(false); @@ -219,9 +244,9 @@ void RecMonitor::slotVideoDeviceChanged(int ix) else KdenliveSettings::setDvgrab_path(dvgrabpath); } else { // Show capture info - QString capturefile = m_capturePath; + capturefile = m_capturePath; if (!capturefile.endsWith("/")) capturefile.append("/"); - QString capturename = KdenliveSettings::dvgrabfilename(); + capturename = KdenliveSettings::dvgrabfilename(); if (capturename.isEmpty()) capturename = "capture"; QString extension; switch (KdenliveSettings::firewireformat()) { @@ -244,6 +269,28 @@ void RecMonitor::slotVideoDeviceChanged(int ix) } } +void RecMonitor::createHDMIDevice() +{ + //video_capture->setVisible(true); + if (m_bmCapture == NULL) { + QVBoxLayout *lay = new QVBoxLayout; + m_bmCapture = new CaptureHandler(lay); + connect(m_bmCapture, SIGNAL(gotTimeCode(ulong)), this, SLOT(slotGotHDMIFrameNumber(ulong))); + connect(m_bmCapture, SIGNAL(gotMessage(const QString &)), this, SLOT(slotGotHDMIMessage(const QString &))); + video_capture->setLayout(lay); + } +} + +void RecMonitor::slotGotHDMIFrameNumber(ulong ix) +{ + m_dvinfo.setText(QString::number(ix)); +} + +void RecMonitor::slotGotHDMIMessage(const QString &message) +{ + m_logger.insertItem(0, message); +} + QPixmap RecMonitor::mergeSideBySide(const QPixmap& pix, const QString txt) { QPainter p; @@ -320,6 +367,14 @@ void RecMonitor::slotStopCapture() m_captureProcess->write("q\n", 3); QTimer::singleShot(1000, m_captureProcess, SLOT(kill())); break; + case HDMI: + video_capture->setHidden(true); + video_frame->setHidden(false); + m_bmCapture->stopPreview(); + m_playAction->setEnabled(true); + m_stopAction->setEnabled(false); + m_recAction->setEnabled(false); + break; default: break; } @@ -398,11 +453,19 @@ void RecMonitor::slotStartCapture(bool play) kDebug() << "Capture: Running ffmpeg " << m_captureArgs.join(" "); m_captureProcess->start("ffmpeg", m_captureArgs); break; + case HDMI: + video_capture->setVisible(true); + video_frame->setHidden(true); + m_bmCapture->startPreview(KdenliveSettings::hdmicapturedevice(), KdenliveSettings::hdmicapturemode()); + m_playAction->setEnabled(false); + m_stopAction->setEnabled(true); + m_recAction->setEnabled(true); + break; default: break; } - if (device_selector->currentIndex() != SCREENGRAB) { + if (device_selector->currentIndex() == FIREWIRE || device_selector->currentIndex() == VIDEO4LINUX) { kDebug() << "Capture: Running ffplay " << m_displayArgs.join(" "); m_displayProcess->start("ffplay", m_displayArgs); video_frame->setText(i18n("Initialising...")); @@ -413,6 +476,23 @@ void RecMonitor::slotStartCapture(bool play) void RecMonitor::slotRecord() { + if (device_selector->currentIndex() == HDMI) { + if (m_hdmiCapturing) { + // We are capturing, stop it + m_bmCapture->stopCapture(); + m_hdmiCapturing = false; + } + else { + // Start capture, get capture filename first + QString path = m_capturePath; + if (!path.endsWith("/")) path.append("/"); + path.append(KdenliveSettings::hdmifilename()); + m_bmCapture->startCapture(path); + m_hdmiCapturing = true; + } + return; + } + if (m_captureProcess->state() == QProcess::NotRunning && device_selector->currentIndex() == FIREWIRE) { slotStartCapture(); } diff --git a/src/recmonitor.h b/src/recmonitor.h index ad41b0ca..f819c076 100644 --- a/src/recmonitor.h +++ b/src/recmonitor.h @@ -26,6 +26,7 @@ #ifndef RECMONITOR_H #define RECMONITOR_H +#include "blackmagic/capture.h" #include "ui_recmonitor_ui.h" #include @@ -38,6 +39,7 @@ #include #include #include +#include #if KDE_IS_VERSION(4,2,0) #include @@ -53,7 +55,7 @@ public: QString name() const; - enum CAPTUREDEVICE {FIREWIRE = 0, VIDEO4LINUX = 1, SCREENGRAB = 2}; + enum CAPTUREDEVICE {FIREWIRE = 0, VIDEO4LINUX = 1, SCREENGRAB = 2, HDMI = 3}; protected: virtual void mousePressEvent(QMouseEvent * event); @@ -62,7 +64,12 @@ private: QString m_name; bool m_isActive; KDateTime m_captureTime; + /** @brief Provide feedback about dvgrab operations */ QLabel m_dvinfo; + + /** @brief Keeps a brief (max ten items) history of warning or error messages + * (currently only used for HDMI). */ + KComboBox m_logger; QString m_capturePath; #if KDE_IS_VERSION(4,2,0) @@ -91,6 +98,10 @@ private: void checkDeviceAvailability(); QPixmap mergeSideBySide(const QPixmap& pix, const QString txt); void manageCapturedFiles(); + CaptureHandler *m_bmCapture; + /** @brief Indicates whether we are currently capturing from HDMI. */ + bool m_hdmiCapturing; + void createHDMIDevice(); private slots: void slotStartCapture(bool play = true); @@ -105,6 +116,8 @@ private slots: void slotConfigure(); void slotReadDvgrabInfo(); void slotUpdateFreeSpace(); + void slotGotHDMIFrameNumber(ulong ix); + void slotGotHDMIMessage(const QString &message); public slots: void refreshRecMonitor(bool visible); diff --git a/src/stopmotion/stopmotion.cpp b/src/stopmotion/stopmotion.cpp index 67419286..2522cd3b 100644 --- a/src/stopmotion/stopmotion.cpp +++ b/src/stopmotion/stopmotion.cpp @@ -105,6 +105,7 @@ StopmotionWidget::StopmotionWidget(KUrl projectFolder, QWidget *parent) : BMInterface::getBlackMagicDeviceList(capture_device, NULL); QVBoxLayout *lay = new QVBoxLayout; m_bmCapture = new CaptureHandler(lay); + connect(m_bmCapture, SIGNAL(gotMessage(const QString &)), this, SLOT(slotGotHDMIMessage(const QString &))); m_frame_preview = new MyLabel(this); connect(m_frame_preview, SIGNAL(seek(bool)), this, SLOT(slotSeekFrame(bool))); lay->addWidget(m_frame_preview); @@ -129,6 +130,11 @@ StopmotionWidget::~StopmotionWidget() m_bmCapture->stopPreview(); } +void StopmotionWidget::slotGotHDMIMessage(const QString &message) +{ + log_box->insertItem(0, message); +} + void StopmotionWidget::parseExistingSequences() { sequence_name->clear(); diff --git a/src/stopmotion/stopmotion.h b/src/stopmotion/stopmotion.h index 19769352..eab6c9d1 100644 --- a/src/stopmotion/stopmotion.h +++ b/src/stopmotion/stopmotion.h @@ -143,7 +143,13 @@ private slots: * @param forward set to true for next frame, false for previous one. */ void slotSeekFrame(bool forward); + /** @brief Display warning / error message from capture backend. */ + void slotGotHDMIMessage(const QString &message); + + /** @brief Create thumbnails for existing sequence frames. */ void slotCreateThumbs(QImage img, int ix); + + /** @brief Prepare thumbnails creation. */ void slotPrepareThumbs(); signals: diff --git a/src/widgets/configcapture_ui.ui b/src/widgets/configcapture_ui.ui index ebbb457b..9f4f97e3 100644 --- a/src/widgets/configcapture_ui.ui +++ b/src/widgets/configcapture_ui.ui @@ -817,7 +817,17 @@ + + + + Capture file name + + + + + + Qt::Vertical @@ -830,6 +840,13 @@ + + + + Capture audio + + + diff --git a/src/widgets/recmonitor_ui.ui b/src/widgets/recmonitor_ui.ui index 910d2559..7e9ff137 100644 --- a/src/widgets/recmonitor_ui.ui +++ b/src/widgets/recmonitor_ui.ui @@ -1,74 +1,108 @@ - + + RecMonitor_UI - - + + 0 0 - 306 - 242 + 293 + 250 - - - - + + + 0 + + + + + + 0 + 0 + + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + + + + + + 0 + 0 + + + Not connected - + Qt::AlignCenter - - - - + + + + 0 0 - + 0 15 - + QFrame::StyledPanel - + QFrame::Raised - - - + + + Auto add - + true - - + + - + Firewire - + Video4Linux - + Screen grab + + + HDMI + + diff --git a/src/widgets/stopmotion_ui.ui b/src/widgets/stopmotion_ui.ui index 010b093b..4841f1d8 100644 --- a/src/widgets/stopmotion_ui.ui +++ b/src/widgets/stopmotion_ui.ui @@ -6,15 +6,15 @@ 0 0 - 619 - 434 + 643 + 395 Dialog - + Qt::Vertical @@ -164,14 +164,14 @@ - + - Add sequence to project + Add to project - + @@ -197,6 +197,22 @@ + + + + + 0 + 0 + + + + 10 + + + false + + + -- 2.39.2