From e49a358b450d3bbd50b065caf569e91290283c5d Mon Sep 17 00:00:00 2001 From: Alexandre Quessy Date: Wed, 5 Mar 2014 23:04:52 -0500 Subject: [PATCH 01/14] add empty Video class --- Mapping.h | 4 ++++ Paint.cpp | 34 ++++++++++++++++++++++++++++++++++ Paint.h | 24 ++++++++++++++++++++++++ 3 files changed, 62 insertions(+) diff --git a/Mapping.h b/Mapping.h index a84a9d1..68db3e7 100644 --- a/Mapping.h +++ b/Mapping.h @@ -71,6 +71,10 @@ public: static const UidAllocator& getUidAllocator() { return allocator; } + /** + * Sets up this Mapping: its Paint and its Shape. + * Calls the build() method of its Paint and Shape. + */ virtual void build() { _paint->build(); _shape->build(); diff --git a/Paint.cpp b/Paint.cpp index 151b417..a1dc3dc 100644 --- a/Paint.cpp +++ b/Paint.cpp @@ -40,3 +40,37 @@ Paint::~Paint() allocator.free(_id); } +/* Implementation of the Video class */ + +Video::Video(const QString uri_, uid id=NULL_UID): + Texture(id), + uri(uri_) +{ +} + +Video::~Video() +{ +} + +void Video::build() +{ +} + +int Video::getWidth() const +{ + // TODO + return 0; +} + +int Video::getHeight() const +{ + // TODO + return 0; +} + +const uchar* Video::getBits() const +{ + // TODO + return 0; +} + diff --git a/Paint.h b/Paint.h index 174c80e..1663cdb 100644 --- a/Paint.h +++ b/Paint.h @@ -161,4 +161,28 @@ public: virtual const uchar* getBits() const { return image.bits(); } }; +/** + * Paint that is a Texture retrieved via a video file. + */ +class Video : public Texture +{ +protected: + QString uri; +public: + Video(const QString uri_, uid id); + virtual ~Video(); + const QString getUri() const + { + return uri; + } + virtual void build(); + virtual QString getType() const + { + return "video"; + } + virtual int getWidth() const; + virtual int getHeight() const; + virtual const uchar* getBits() const; +}; + #endif /* PAINT_H_ */ From dfed5c10be6cc40213101df03db161e91378e791 Mon Sep 17 00:00:00 2001 From: Alexandre Quessy Date: Wed, 5 Mar 2014 23:13:13 -0500 Subject: [PATCH 02/14] get rid of warnings --- Mapper.cpp | 16 ++++++++++++---- Util.cpp | 10 ++++++++++ mapmap.pro | 4 +++- unused.h | 7 +++++++ 4 files changed, 32 insertions(+), 5 deletions(-) create mode 100644 unused.h diff --git a/Mapper.cpp b/Mapper.cpp index 3eb90d0..f94fb24 100644 --- a/Mapper.cpp +++ b/Mapper.cpp @@ -19,6 +19,7 @@ */ #include "Mapper.h" +#include "unused.h" Mapper::Mapper(Mapping::ptr mapping) : _mapping(mapping) @@ -169,6 +170,7 @@ void ColorMapper::draw(QPainter* painter) void ColorMapper::drawControls(QPainter* painter) { + UNUSED(painter); } MeshColorMapper::MeshColorMapper(Mapping::ptr mapping) @@ -293,6 +295,7 @@ void TextureMapper::updateShape(Shape* shape) void TextureMapper::draw(QPainter* painter) { + UNUSED(painter); painter->beginNativePainting(); // Only works for similar shapes. @@ -324,7 +327,7 @@ void TextureMapper::draw(QPainter* painter) void TextureMapper::drawInput(QPainter* painter) { - + UNUSED(painter); } void TextureMapper::drawControls(QPainter* painter) @@ -344,6 +347,7 @@ TriangleTextureMapper::TriangleTextureMapper(std::tr1::shared_ptrnVertices(); i++) @@ -399,6 +403,8 @@ void MeshTextureMapper::drawControls(QPainter* painter) void MeshTextureMapper::drawInputControls(QPainter* painter) { + UNUSED(painter); + std::tr1::shared_ptr inputMesh = std::tr1::static_pointer_cast(inputShape); QVector inputQuads = inputMesh->getQuads(); for (QVector::const_iterator it = inputQuads.begin(); it != inputQuads.end(); ++it) @@ -409,6 +415,7 @@ void MeshTextureMapper::drawInputControls(QPainter* painter) void MeshTextureMapper::_doDraw(QPainter* painter) { + UNUSED(painter); std::tr1::shared_ptr outputMesh = std::tr1::static_pointer_cast(outputShape); std::tr1::shared_ptr inputMesh = std::tr1::static_pointer_cast(inputShape); QVector > outputQuads = outputMesh->getQuads2d(); @@ -436,16 +443,17 @@ EllipseTextureMapper::EllipseTextureMapper(std::tr1::shared_ptr void EllipseTextureMapper::_doDraw(QPainter* painter) { + UNUSED(painter); // Get input and output ellipses. std::tr1::shared_ptr inputEllipse = std::tr1::static_pointer_cast(inputShape); std::tr1::shared_ptr outputEllipse = std::tr1::static_pointer_cast(outputShape); // Start / end angle. - const float startAngle = 0; - const float endAngle = 2*M_PI; + //const float startAngle = 0; + //const float endAngle = 2*M_PI; // - float angle; + //float angle; QPointF currentInputPoint; QPointF prevInputPoint(0, 0); QPointF currentOutputPoint; diff --git a/Util.cpp b/Util.cpp index 343943a..902f1ca 100644 --- a/Util.cpp +++ b/Util.cpp @@ -19,6 +19,7 @@ */ #include "Util.h" +#include "unused.h" #include namespace Util { @@ -72,6 +73,9 @@ int map_int(int value, int istart, int istop, int ostart, int ostop) Mesh* createMeshForTexture(Texture* texture, int frameWidth, int frameHeight) { + UNUSED(frameHeight); + UNUSED(frameWidth); + return new Mesh( QPointF(texture->getX(), texture->getY()), QPointF(texture->getX() + texture->getWidth(), texture->getY()), @@ -82,6 +86,9 @@ Mesh* createMeshForTexture(Texture* texture, int frameWidth, int frameHeight) Triangle* createTriangleForTexture(Texture* texture, int frameWidth, int frameHeight) { + UNUSED(frameHeight); + UNUSED(frameWidth); + return new Triangle( QPointF(texture->getX(), texture->getY() + texture->getHeight()), QPointF(texture->getX() + texture->getWidth(), texture->getY() + texture->getHeight()), @@ -92,6 +99,9 @@ Triangle* createTriangleForTexture(Texture* texture, int frameWidth, int frameHe Ellipse* createEllipseForTexture(Texture* texture, int frameWidth, int frameHeight) { + UNUSED(frameHeight); + UNUSED(frameWidth); + qreal halfWidth = texture->getWidth() / 2; qreal halfHeight = texture->getHeight() / 2; diff --git a/mapmap.pro b/mapmap.pro index 610837b..86d79c9 100644 --- a/mapmap.pro +++ b/mapmap.pro @@ -17,7 +17,8 @@ HEADERS = \ Shape.h \ SourceGLCanvas.h \ UidAllocator.h \ - Util.h + Util.h \ + unused.h \ SOURCES = \ # Controller.cpp \ @@ -56,4 +57,5 @@ macx:QMAKE_CXXFLAGS += -D__MACOSX_CORE__ # not mac !macx:LIBS += -lglut -lGLU +!macx:QMAKE_CXXFLAGS += -Wno-unused-result -Wfatal-errors diff --git a/unused.h b/unused.h new file mode 100644 index 0000000..ba88459 --- /dev/null +++ b/unused.h @@ -0,0 +1,7 @@ +#ifndef __mapmap_unused_h__ +#define __mapmap_unused_h__ + +#define UNUSED(x) (void)(x) + +#endif // ifndef + From 572ceeadd510486721d16ec5f2559cce308c2401 Mon Sep 17 00:00:00 2001 From: Alexandre Quessy Date: Wed, 5 Mar 2014 23:30:30 -0500 Subject: [PATCH 03/14] Link to GStreamer --- Paint.cpp | 1 + mapmap.pro | 84 ++++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 73 insertions(+), 12 deletions(-) diff --git a/Paint.cpp b/Paint.cpp index a1dc3dc..6c5f4e2 100644 --- a/Paint.cpp +++ b/Paint.cpp @@ -19,6 +19,7 @@ */ #include "Paint.h" +#include UidAllocator Paint::allocator; diff --git a/mapmap.pro b/mapmap.pro index 86d79c9..11143cb 100644 --- a/mapmap.pro +++ b/mapmap.pro @@ -1,5 +1,9 @@ -CONFIG += qt debug TEMPLATE = app +TARGET = mapmap +QT += gui opengl xml +CONFIG += qt debug +DEFINES += UNICODE QT_THREAD_SUPPORT QT_CORE_LIB QT_GUI_LIB + HEADERS = \ DestinationGLCanvas.h \ # Facade.h \ @@ -18,7 +22,7 @@ HEADERS = \ SourceGLCanvas.h \ UidAllocator.h \ Util.h \ - unused.h \ + unused.h SOURCES = \ # Controller.cpp \ @@ -40,22 +44,78 @@ SOURCES = \ Util.cpp \ main.cpp -QT += gui opengl xml RESOURCES = mapmap.qrc - TRANSLATIONS = mapmap_fr.ts - include(contrib/qtpropertybrowser/src/qtpropertybrowser.pri) - docs.depends = $(HEADERS) $(SOURCES) docs.commands = (cat Doxyfile; echo "INPUT = $?") | doxygen - QMAKE_EXTRA_TARGETS += docs -# mac -macx:LIBS += -framework OpenGL -framework GLUT -macx:QMAKE_CXXFLAGS += -D__MACOSX_CORE__ +# Linux-specific: +unix:!mac { + DEFINES += UNIX + # stricter build flags: + QMAKE_CXXFLAGS += -Wno-unused-result -Wfatal-errors + INCLUDEPATH += /usr/include/gstreamer-0.10 \ + /usr/local/include/gstreamer-0.10 \ + /usr/include/glib-2.0 \ + /usr/lib/x86_64-linux-gnu/glib-2.0/include \ + /usr/include/libxml2 + LIBS += -lgstreamer-0.10 \ + -lgstinterfaces-0.10 \ + -lglib-2.0 \ + -lglut \ + -lgmodule-2.0 \ + -lgobject-2.0 \ + -lgthread-2.0 \ + -lGLU \ + -lGL \ + -lX11 \ + -lGLEW +} -# not mac -!macx:LIBS += -lglut -lGLU -!macx:QMAKE_CXXFLAGS += -Wno-unused-result -Wfatal-errors +# Mac OS X-specific: +mac { + DEFINES += MACOSX + INCLUDEPATH += /opt/local/include/ \ + /opt/local/include/gstreamer-0.10/ \ + /opt/local/include/glib-2.0/ \ + /opt/local/lib/glib-2.0/include \ + /opt/local/include/libxml2 + LIBS += -L/opt/local/lib \ + -lGLEW \ + -lgstreamer-0.10 \ + -lgstapp-0.10 \ + -lgstvideo-0.10 \ + -lglib-2.0 \ + -lgobject-2.0 + OBJECTIVE_SOURCES += cocoa_utils.mm + LIBS += -framework OpenGL -framework GLUT + QMAKE_CXXFLAGS += -D__MACOSX_CORE__ +} + +# Windows-specific: +win32 { + DEFINES += WIN32 + INCLUDEPATH += \ + C:/gstreamer/include \ + C:/gstreamer/include/libxml2 \ + C:/gstreamer/include/glib-2.0 \ + C:/gstreamer/lib/glib-2.0/include \ + C:/gstreamer/include/gstreamer-0.10 + LIBS += -L"C:/gstreamer/lib" \ + -L"C:/gstreamer/bin" \ + -lgstreamer-0.10 \ + -lglib-2.0 \ + -lgmodule-2.0 \ + -lgobject-2.0 \ + -lgthread-2.0 \ + -lgstinterfaces-0.10 \ + -lopengl32 \ + -lglu32 \ + -lglew32 + # Add console to the CONFIG to see debug messages printed in + # the console on Windows + CONFIG += console +} From 93b55cb61abf243935084f0ed15a63573453723b Mon Sep 17 00:00:00 2001 From: Alexandre Quessy Date: Wed, 5 Mar 2014 23:43:39 -0500 Subject: [PATCH 04/14] Print GStreamer version --- MainWindow.cpp | 5 +++++ Paint.cpp | 15 +++++++++++++++ Paint.h | 4 ++++ 3 files changed, 24 insertions(+) diff --git a/MainWindow.cpp b/MainWindow.cpp index cbed161..71be29c 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -28,6 +28,11 @@ MainWindow* MainWindow::instance = 0; MainWindow::MainWindow() { + if (Video::hasVideoSupport()) + std::cout << "Video support: yes" << std::endl; + else + std::cout << "Video support: no" << std::endl; + mappingManager = new MappingManager; // _facade = new Facade(mappingManager, this); diff --git a/Paint.cpp b/Paint.cpp index 6c5f4e2..b1602d8 100644 --- a/Paint.cpp +++ b/Paint.cpp @@ -20,6 +20,7 @@ #include "Paint.h" #include +#include UidAllocator Paint::allocator; @@ -75,3 +76,17 @@ const uchar* Video::getBits() const return 0; } +bool Video::hasVideoSupport() +{ + static bool did_print_gst_version = false; + if (! did_print_gst_version) + { + std::cout << "Using GStreamer version " << + GST_VERSION_MAJOR << "." << + GST_VERSION_MINOR << "." << + GST_VERSION_MICRO << std::endl; + did_print_gst_version = true; + } + return true; +} + diff --git a/Paint.h b/Paint.h index 1663cdb..80b0b1f 100644 --- a/Paint.h +++ b/Paint.h @@ -183,6 +183,10 @@ public: virtual int getWidth() const; virtual int getHeight() const; virtual const uchar* getBits() const; + /** + * Checks whether or not video is supported on this platform. + */ + static bool hasVideoSupport(); }; #endif /* PAINT_H_ */ From 16ce2a449fc3dba57e2aa1e1fa4a50a43fdcf9a8 Mon Sep 17 00:00:00 2001 From: Alexandre Quessy Date: Thu, 6 Mar 2014 15:19:06 -0500 Subject: [PATCH 05/14] private declaration of VideoImpl --- Paint.cpp | 29 +++++++----------- Paint.h | 8 +++++ VideoImpl.cpp | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++ VideoImpl.h | 54 ++++++++++++++++++++++++++++++++++ mapmap.pro | 4 ++- 5 files changed, 157 insertions(+), 19 deletions(-) create mode 100644 VideoImpl.cpp create mode 100644 VideoImpl.h diff --git a/Paint.cpp b/Paint.cpp index b1602d8..c2a89ee 100644 --- a/Paint.cpp +++ b/Paint.cpp @@ -19,7 +19,7 @@ */ #include "Paint.h" -#include +#include "VideoImpl.h" #include UidAllocator Paint::allocator; @@ -42,51 +42,44 @@ Paint::~Paint() allocator.free(_id); } + /* Implementation of the Video class */ Video::Video(const QString uri_, uid id=NULL_UID): Texture(id), - uri(uri_) + uri(uri_), + impl_(new VideoImpl) { + this->impl_->setUri(uri); } Video::~Video() { + delete impl_; } void Video::build() { + this->impl_->build(); } int Video::getWidth() const { - // TODO - return 0; + return this->impl_->getWidth(); } int Video::getHeight() const { - // TODO - return 0; + return this->impl_->getHeight(); } const uchar* Video::getBits() const { - // TODO - return 0; + return this->impl_->getBits(); } bool Video::hasVideoSupport() { - static bool did_print_gst_version = false; - if (! did_print_gst_version) - { - std::cout << "Using GStreamer version " << - GST_VERSION_MAJOR << "." << - GST_VERSION_MINOR << "." << - GST_VERSION_MICRO << std::endl; - did_print_gst_version = true; - } - return true; + return VideoImpl::hasVideoSupport(); } diff --git a/Paint.h b/Paint.h index 80b0b1f..998f67c 100644 --- a/Paint.h +++ b/Paint.h @@ -161,6 +161,8 @@ public: virtual const uchar* getBits() const { return image.bits(); } }; +class VideoImpl; // forware declaration + /** * Paint that is a Texture retrieved via a video file. */ @@ -187,6 +189,12 @@ public: * Checks whether or not video is supported on this platform. */ static bool hasVideoSupport(); +private: + /** + * Private implementation, so that GStreamer headers don't need + * to be included from every file in the project. + */ + VideoImpl * impl_; // PIMPL opaque pointer }; #endif /* PAINT_H_ */ diff --git a/VideoImpl.cpp b/VideoImpl.cpp new file mode 100644 index 0000000..f7df37e --- /dev/null +++ b/VideoImpl.cpp @@ -0,0 +1,81 @@ +/* + * VideoImpl.cpp + * + * (c) 2013 Sofian Audry -- info(@)sofianaudry(.)com + * (c) 2013 Alexandre Quessy -- alexandre(@)quessy(.)net + * + * 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 3 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, see . + */ +#include "VideoImpl.h" +#include + +// -------- private implementation of VideoImpl ------- + +bool VideoImpl::hasVideoSupport() +{ + static bool did_print_gst_version = false; + if (! did_print_gst_version) + { + std::cout << "Using GStreamer version " << + GST_VERSION_MAJOR << "." << + GST_VERSION_MINOR << "." << + GST_VERSION_MICRO << std::endl; + did_print_gst_version = true; + } + // TODO: actually check if we have it + return true; +} + +int VideoImpl::getWidth() const +{ + // TODO + std::cout << "TODO" << std::endl; + return 0; +} + +int VideoImpl::getHeight() const +{ + // TODO + std::cout << "TODO" << std::endl; + return 0; +} + +const uchar* VideoImpl::getBits() const +{ + // TODO + std::cout << "TODO" << std::endl; + return 0; +} + +void VideoImpl::build() +{ + std::cout << "TODO" << std::endl; +} + +VideoImpl::VideoImpl() +{ + std::cout << "TODO" << std::endl; +} + +VideoImpl::~VideoImpl() +{ + std::cout << "TODO" << std::endl; +} + +void VideoImpl::setUri(QString uri) +{ + this->uri_ = uri; + std::cout << "TODO" << std::endl; +} + diff --git a/VideoImpl.h b/VideoImpl.h new file mode 100644 index 0000000..350c6bf --- /dev/null +++ b/VideoImpl.h @@ -0,0 +1,54 @@ +/* + * Paint.h + * + * (c) 2013 Sofian Audry -- info(@)sofianaudry(.)com + * (c) 2013 Alexandre Quessy -- alexandre(@)quessy(.)net + * + * 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 3 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, see . + */ + +#ifndef VIDEO_IMPL_H_ +#define VIDEO_IMPL_H_ + +#include +#include +#include +#if __APPLE__ +#include +#else +#include +#endif +#include + +/** + * Private declaration of the video player. + * This is done this way so that GStreamer header don't need to be + * included in the whole project. (just in this file) + */ +class VideoImpl +{ +public: + VideoImpl(); + ~VideoImpl(); + void setUri(const QString uri); + static bool hasVideoSupport(); + void build(); + int getWidth() const; + int getHeight() const; + const uchar* getBits() const; +private: + QString uri_; +}; + +#endif /* ifndef */ diff --git a/mapmap.pro b/mapmap.pro index 11143cb..ea54b9c 100644 --- a/mapmap.pro +++ b/mapmap.pro @@ -22,7 +22,8 @@ HEADERS = \ SourceGLCanvas.h \ UidAllocator.h \ Util.h \ - unused.h + unused.h \ + VideoImpl.h SOURCES = \ # Controller.cpp \ @@ -42,6 +43,7 @@ SOURCES = \ SourceGLCanvas.cpp \ UidAllocator.cpp \ Util.cpp \ + VideoImpl.cpp \ main.cpp RESOURCES = mapmap.qrc From 8224a64374554b4ff57e8631e179044c253a94b6 Mon Sep 17 00:00:00 2001 From: Alexandre Quessy Date: Thu, 13 Mar 2014 16:01:03 -0400 Subject: [PATCH 06/14] re-implement and fix osc receiver --- MainWindow.cpp | 5 ++++- OscInterface.cpp | 19 ++++++++++++++----- OscInterface.h | 8 ++++---- OscReceiver.cpp | 26 ++++++++++++++++++++------ OscReceiver.h | 2 ++ mapmap.pro | 12 +++++++----- 6 files changed, 51 insertions(+), 21 deletions(-) diff --git a/MainWindow.cpp b/MainWindow.cpp index 71be29c..8491dcd 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -73,6 +73,9 @@ MainWindow::~MainWindow() { delete mappingManager; // delete _facade; +#ifdef HAVE_OSC + delete osc_timer; +#endif // ifdef } void MainWindow::handlePaintItemSelectionChanged() @@ -1224,7 +1227,7 @@ void MainWindow::startOscReceiver() void MainWindow::pollOscInterface() { #ifdef HAVE_OSC - osc_interface->consume_commands(*_facade); + osc_interface->consume_commands(*this); #endif } diff --git a/OscInterface.cpp b/OscInterface.cpp index 67ecd43..db66cd1 100644 --- a/OscInterface.cpp +++ b/OscInterface.cpp @@ -21,7 +21,8 @@ */ #include "OscInterface.h" -#include "Facade.h" +#include "MainWindow.h" +#include "unused.h" #include OscInterface::OscInterface( @@ -43,6 +44,11 @@ OscInterface::OscInterface( } } +OscInterface::~OscInterface() +{ + // pass +} + /** * Handles /pong. Does nothing. */ @@ -87,7 +93,7 @@ void OscInterface::push_command(QVariantList command) * * Should be called when it's time to take action, before rendering a frame, for example. */ -void OscInterface::consume_commands(Facade &facade) +void OscInterface::consume_commands(MainWindow &main_window) { bool success = true; while (success) @@ -99,7 +105,7 @@ void OscInterface::consume_commands(Facade &facade) //if (is_verbose()) // std::cout << __FUNCTION__ << ": apply " << // command.first().toString().toStdString() << std::endl; - this->applyOscCommand(facade, command); + this->applyOscCommand(main_window, command); } } } @@ -183,8 +189,9 @@ static void printCommand(QVariantList &command) std::cout.flush(); } -void OscInterface::applyOscCommand(Facade &facade, QVariantList & command) +void OscInterface::applyOscCommand(MainWindow &main_window, QVariantList & command) { + UNUSED(main_window); bool VERBOSE = true; if (VERBOSE) @@ -209,7 +216,8 @@ void OscInterface::applyOscCommand(Facade &facade, QVariantList & command) std::string paint_id = command.at(2).toString().toStdString(); std::string image_uri = command.at(3).toString().toStdString(); //std::cout << "TODO load /image/uri " << image_id << " " << image_uri << std::endl; - facade.updateImagePaintUri(paint_id.c_str(), image_uri.c_str()); + // TODO: + //main_window.updateImagePaintUri(paint_id.c_str(), image_uri.c_str()); } //else if (path == "/add/quad") @@ -221,3 +229,4 @@ void OscInterface::applyOscCommand(Facade &facade, QVariantList & command) //else if (path == "/project/open") // open(); } + diff --git a/OscInterface.h b/OscInterface.h index f6616a0..27e5af3 100644 --- a/OscInterface.h +++ b/OscInterface.h @@ -28,7 +28,7 @@ #include "OscReceiver.h" #include -class Facade; // forward decl +class MainWindow; // forward decl /** * Open Sound Control sending and receiving for MapMap. @@ -40,7 +40,7 @@ class OscInterface OscInterface( //MainWindow* owner, const std::string &listen_port); - ~OscInterface() {} + ~OscInterface(); void start(); /** * Call this method from the main thread. @@ -48,7 +48,7 @@ class OscInterface * Each message is stored as a QVariantList. * [args] */ - void consume_commands(Facade &facade); + void consume_commands(MainWindow &main_window); private: bool is_verbose() { return false; } void push_command(QVariantList command); @@ -72,7 +72,7 @@ class OscInterface /* * In the main thread, handles the messages. */ - void applyOscCommand(Facade &facade, QVariantList & command); + void applyOscCommand(MainWindow &main_window, QVariantList & command); }; diff --git a/OscReceiver.cpp b/OscReceiver.cpp index 7f19119..32a56b5 100644 --- a/OscReceiver.cpp +++ b/OscReceiver.cpp @@ -2,6 +2,8 @@ #include #include +bool OscReceiver::server_is_ok_ = true; + OscReceiver::OscReceiver(const std::string &port) : port_(port), server_(lo_server_thread_new(port_.c_str(), error)) @@ -20,20 +22,31 @@ OscReceiver::~OscReceiver() void OscReceiver::addHandler(const char *path, const char *types, lo_method_handler handler, void *userData) { - lo_server_thread_add_method(server_, path, types, handler, userData); + if (server_is_ok_) + lo_server_thread_add_method(server_, path, types, handler, userData); + else + std::cout << "Could not add OSC handler " << path << std::endl; } void OscReceiver::listen() { - int lo_fd = lo_server_get_socket_fd(server_); - if (lo_fd == 0) + if (server_is_ok_) { - std::cout << "OSC port " << port_ << " is already in use." << std::endl; + int lo_fd = lo_server_get_socket_fd(server_); + if (lo_fd == 0) + { + std::cout << "OSC port " << port_ << " is already in use." << std::endl; + } + else + { + std::cout << "Listening on port " << port_ << std::endl; + lo_server_thread_start(server_); + } } else { - std::cout << "Listening on port " << port_ << std::endl; - lo_server_thread_start(server_); + std::cout << "Could not start OSC receiver. Maybe that "; + std::cout << "OSC port " << port_ << " is already in use." << std::endl; } } @@ -41,6 +54,7 @@ void OscReceiver::error(int num, const char *msg, const char *path) { std::cerr << "liblo server error " << num << " in path " << path << ": " << msg << std::endl; + OscReceiver::server_is_ok_ = false; } // #ifdef CONFIG_DEBUG diff --git a/OscReceiver.h b/OscReceiver.h index fc28720..337bde6 100644 --- a/OscReceiver.h +++ b/OscReceiver.h @@ -16,6 +16,8 @@ class OscReceiver { void addHandler(const char *path, const char *types, lo_method_handler handler, void *user_data); + public: + static bool server_is_ok_; private: std::string toString() const; std::string port_; diff --git a/mapmap.pro b/mapmap.pro index ea54b9c..e0f5297 100644 --- a/mapmap.pro +++ b/mapmap.pro @@ -6,7 +6,6 @@ DEFINES += UNICODE QT_THREAD_SUPPORT QT_CORE_LIB QT_GUI_LIB HEADERS = \ DestinationGLCanvas.h \ -# Facade.h \ MainApplication.h \ MainWindow.h \ Mapper.h \ @@ -14,8 +13,9 @@ HEADERS = \ Mapping.h \ MappingManager.h \ Math.h \ -# NameAllocator.h \ Paint.h \ + OscInterface.h \ + OscReceiver.h \ ProjectReader.h \ ProjectWriter.h \ Shape.h \ @@ -26,16 +26,16 @@ HEADERS = \ VideoImpl.h SOURCES = \ -# Controller.cpp \ DestinationGLCanvas.cpp \ -# Facade.cpp \ MainWindow.cpp \ MainApplication.cpp \ Mapper.cpp \ MapperGLCanvas.cpp \ Mapping.cpp \ MappingManager.cpp \ -# NameAllocator.cpp \ + OscInterface.cpp \ + OscReceiver.cpp \ + ProjectReader.h \ Paint.cpp \ ProjectReader.cpp \ ProjectWriter.cpp \ @@ -58,6 +58,7 @@ unix:!mac { DEFINES += UNIX # stricter build flags: QMAKE_CXXFLAGS += -Wno-unused-result -Wfatal-errors + QMAKE_CXXFLAGS += -DHAVE_OSC INCLUDEPATH += /usr/include/gstreamer-0.10 \ /usr/local/include/gstreamer-0.10 \ /usr/include/glib-2.0 \ @@ -67,6 +68,7 @@ unix:!mac { -lgstinterfaces-0.10 \ -lglib-2.0 \ -lglut \ + -llo -lpthread \ -lgmodule-2.0 \ -lgobject-2.0 \ -lgthread-2.0 \ From ed3f476d6bdc3848060ba8d020021cec002026f4 Mon Sep 17 00:00:00 2001 From: Tats Date: Fri, 25 Apr 2014 23:38:46 +0100 Subject: [PATCH 07/14] Brutal port of Drone code to try to make video work using gstreamer 0.10 using appsink --- MainWindow.cpp | 3 +- Paint.cpp | 8 +- Paint.h | 2 +- VideoImpl.cpp | 683 +++++++++++++++++++++++++++++++++++++++++++++++-- VideoImpl.h | 133 +++++++++- 5 files changed, 798 insertions(+), 31 deletions(-) diff --git a/MainWindow.cpp b/MainWindow.cpp index 8491dcd..021420a 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -400,7 +400,8 @@ uid MainWindow::createImagePaint(uid paintId, QString uri, float x, float y) else { - Image* img = new Image(uri, paintId); + Video* img = new Video(uri, paintId); +// Image* img = new Image(uri, paintId); // Create new image with corresponding ID. img->setPosition(x, y); diff --git a/Paint.cpp b/Paint.cpp index c2a89ee..0de90ae 100644 --- a/Paint.cpp +++ b/Paint.cpp @@ -45,14 +45,16 @@ Paint::~Paint() /* Implementation of the Video class */ -Video::Video(const QString uri_, uid id=NULL_UID): +Video::Video(const QString uri_, uid id): Texture(id), uri(uri_), - impl_(new VideoImpl) + impl_(NULL) { - this->impl_->setUri(uri); + impl_ = new VideoImpl(uri_); } +// vertigo + Video::~Video() { delete impl_; diff --git a/Paint.h b/Paint.h index 998f67c..86382bb 100644 --- a/Paint.h +++ b/Paint.h @@ -171,7 +171,7 @@ class Video : public Texture protected: QString uri; public: - Video(const QString uri_, uid id); + Video(const QString uri_, uid id=NULL_UID); virtual ~Video(); const QString getUri() const { diff --git a/VideoImpl.cpp b/VideoImpl.cpp index f7df37e..be20882 100644 --- a/VideoImpl.cpp +++ b/VideoImpl.cpp @@ -18,7 +18,7 @@ * along with this program. If not, see . */ #include "VideoImpl.h" -#include +#include // -------- private implementation of VideoImpl ------- @@ -27,10 +27,10 @@ bool VideoImpl::hasVideoSupport() static bool did_print_gst_version = false; if (! did_print_gst_version) { - std::cout << "Using GStreamer version " << + qDebug() << "Using GStreamer version " << GST_VERSION_MAJOR << "." << GST_VERSION_MINOR << "." << - GST_VERSION_MICRO << std::endl; + GST_VERSION_MICRO << endl; did_print_gst_version = true; } // TODO: actually check if we have it @@ -39,43 +39,682 @@ bool VideoImpl::hasVideoSupport() int VideoImpl::getWidth() const { - // TODO - std::cout << "TODO" << std::endl; - return 0; + return _width; } int VideoImpl::getHeight() const { - // TODO - std::cout << "TODO" << std::endl; - return 0; + return _height; } const uchar* VideoImpl::getBits() const { - // TODO - std::cout << "TODO" << std::endl; - return 0; + return _data; } void VideoImpl::build() { - std::cout << "TODO" << std::endl; -} - -VideoImpl::VideoImpl() -{ - std::cout << "TODO" << std::endl; + if (!loadMovie(_uri)) + { + qDebug() << "Cannot load movie " << _currentMovie << "." << endl; + } } VideoImpl::~VideoImpl() { - std::cout << "TODO" << std::endl; + freeResources(); + if (_data) + free(_data); } -void VideoImpl::setUri(QString uri) +bool VideoImpl::_videoPull() { - this->uri_ = uri; - std::cout << "TODO" << std::endl; + GstBuffer *buffer; + + // Retrieve the buffer. + g_signal_emit_by_name (_videoSink, "pull-buffer", &buffer); + + if (!buffer) + { + // Either means we are not playing or we have reached EOS. + return false; + } + + else + { + GstCaps* caps = GST_BUFFER_CAPS(buffer); + GstStructure *capsStruct = gst_caps_get_structure (caps, 0); + + int width = 320; + int height = 240; + int bpp = 24; + int depth = 24; + + gst_structure_get_int(capsStruct, "width", &width); + gst_structure_get_int(capsStruct, "height", &height); + gst_structure_get_int(capsStruct, "bpp", &bpp); + gst_structure_get_int(capsStruct, "depth", &depth); + + _width = width; + _height = height; + + if (!_data) + _data = (uchar*)calloc(_width * _height, sizeof(uchar*)); + +// video->resize(width, height); + +// qDebug() << gst_structure_to_string(capsStruct) << endl; +// qDebug() << width << "x" << height << "=" << width*height << "(" << width*height*4 << "," << width*height*3 << ")" << endl; +// qDebug() << "bpp: " << bpp << " depth: " << depth << endl; +// qDebug() << "Buffer size: " << GST_BUFFER_SIZE(buffer) << endl; + + if (bpp == 32) + memcpy(_data, GST_BUFFER_DATA(buffer), _width * _height * 4); + else + convert24to32(_data, GST_BUFFER_DATA(buffer), _width * _height); + + gst_buffer_unref (buffer); + + return true; + } + } +bool VideoImpl::_eos() const +{ + if (_movieReady) + { + Q_ASSERT( _videoSink ); +// Q_ASSERT( _audioSink ); + gboolean videoEos; +// gboolean audioEos; + g_object_get (G_OBJECT (_videoSink), "eos", &videoEos, NULL); +// g_object_get (G_OBJECT (_audioSink), "eos", &audioEos, NULL); + return (bool) (videoEos /*|| audioEos*/); + } + else + return false; +} + +//void VideoImpl::_init() +//{ +// _audioHasNewBuffer = false; +// _videoHasNewBuffer = false; +// +// _terminate = false; +// _seekEnabled = false; +// +// _movieReady=true; +// +// // Stop sleeping the video output. +// _VIDEO_OUT->sleeping(false); +// _AUDIO_OUT->sleeping(false); +//} + + +void VideoImpl::gstNewBufferCallback(GstElement*, int *newBufferCounter) +{ + (*newBufferCounter)++; +} + +VideoImpl::VideoImpl(const QString uri) : +_currentMovie(""), +_bus(NULL), +_pipeline(NULL), +_source(NULL), +//_audioQueue(NULL), +//_audioConvert(NULL), +//_audioResample(NULL), +_videoQueue(NULL), +_videoConvert(NULL), +_videoColorSpace(NULL), +_audioSink(NULL), +_videoSink(NULL), +_width(720), +_height(480), +_data(NULL), +//_audioBufferAdapter(NULL), +_seekEnabled(false), +//_audioNewBufferCounter(0), +_videoNewBufferCounter(0), +_movieReady(false), +_uri(uri) +{ +// addPlug(_VIDEO_OUT = new PlugOut(this, "ImgOut", false)); +// addPlug(_AUDIO_OUT = new PlugOut(this, "AudioOut", false)); +// +// addPlug(_FINISH_OUT = new PlugOut(this, "FinishOut", false)); +// +// QList atLeastOneOfThem; +// atLeastOneOfThem.push_back(_VIDEO_OUT); +// atLeastOneOfThem.push_back(_AUDIO_OUT); +// setPlugAtLeastOneNeeded(atLeastOneOfThem); +// +// addPlug(_RESET_IN = new PlugIn(this, "Reset", false, new ValueType(0, 0, 1))); +// addPlug(_MOVIE_IN = new PlugIn(this, "Movie", false)); +// +// //_settings.add(Property::FILENAME, SETTING_FILENAME)->valueStr(""); +// +// _VIDEO_OUT->sleeping(true); +// _AUDIO_OUT->sleeping(true); +// +// // Crease audio buffer handler. +// _audioBufferAdapter = gst_adapter_new(); +} + +void VideoImpl::unloadMovie() +{ + // Free allocated resources. + freeResources(); + + // Reset flags. +// _audioNewBufferCounter = 0; + _videoNewBufferCounter = 0; + + _terminate = false; + _seekEnabled = false; + + _setReady(false); + + // Unsynch. + // NOTE: I commented this out, it was in Drone, most probably useless but who knows. + // unSynch(); // XXX: I'm not sure why we are doing this... +} + +void VideoImpl::freeResources() +{ + // Free resources. + if (_bus) + { + gst_object_unref (_bus); + _bus = NULL; + } + + if (_pipeline) + { + gst_element_set_state (_pipeline, GST_STATE_NULL); + gst_object_unref (_pipeline); + _pipeline = NULL; + } + + _source = NULL; +// _audioQueue = NULL; +// _audioConvert = NULL; +// _audioResample = NULL; + _videoQueue = NULL; + _videoConvert = NULL; + _videoColorSpace = NULL; + _audioSink = NULL; + _videoSink = NULL; + _padHandlerData = GstPadHandlerData(); + + // Flush buffers in adapter. +// gst_adapter_clear(_audioBufferAdapter); + +} + +void VideoImpl::resetMovie() +{ + // TODO: Check if we can still seek when we reach EOS. It seems like it's then impossible and we + // have to reload but it seems weird so we should check. + if (!_eos() && _seekEnabled) + { + qDebug() << "Seeking at position 0." << endl; + gst_element_seek_simple (_pipeline, GST_FORMAT_TIME, + (GstSeekFlags) (GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT), 0); + _setReady(true); + } + else + { + // Just reload movie. + qDebug() << "Reloading the movie" << endl; + _currentMovie = ""; + } +} + +bool VideoImpl::loadMovie(QString filename) +{ + qDebug() << "Opening movie: " << filename << "."; + + // Free previously allocated structures + unloadMovie(); + + //_firstFrameTime=_formatContext->start_time; + + // Initialize GStreamer. + gst_init (NULL, NULL); + + // Create the elements. + _source = gst_element_factory_make ("uridecodebin", "source"); + +// _audioQueue = gst_element_factory_make ("queue", "aqueue"); +// _audioConvert = gst_element_factory_make ("audioconvert", "aconvert"); +// _audioResample = gst_element_factory_make ("audioresample", "aresample"); +// _audioSink = gst_element_factory_make ("appsink", "asink"); +// + _videoQueue = gst_element_factory_make ("queue", "vqueue"); + _videoColorSpace = gst_element_factory_make ("ffmpegcolorspace", "vcolorspace"); + _videoSink = gst_element_factory_make ("appsink", "vsink"); + + // Prepare handler data. +// _padHandlerData.audioToConnect = _audioQueue; + _padHandlerData.videoToConnect = _videoQueue; + _padHandlerData.videoSink = _videoSink; + //_padHandlerData.audioIsConnected = false; + _padHandlerData.videoIsConnected = false; + +// _newAudioBufferHandlerData.audioSink = _audioSink; +// _newAudioBufferHandlerData.audioBufferAdapter = _audioBufferAdapter; + + // Create the empty pipeline. + _pipeline = gst_pipeline_new ( "video-source-pipeline" ); + + if (!_pipeline || !_source || +// !_audioQueue || !_audioConvert || !_audioResample || !_audioSink || + !_videoQueue || !_videoColorSpace || !_videoSink) { + g_printerr ("Not all elements could be created.\n"); + unloadMovie(); + return -1; + } + + // Build the pipeline. Note that we are NOT linking the source at this + // point. We will do it later. + gst_bin_add_many (GST_BIN (_pipeline), _source, +// _audioQueue, _audioConvert, _audioResample, _audioSink, + _videoQueue, _videoColorSpace, _videoSink, NULL); + +// if (!gst_element_link_many(_audioQueue, _audioConvert, _audioResample, _audioSink, NULL)) { +// g_printerr ("Audio elements could not be linked.\n"); +// unloadMovie(); +// return false; +// } + + if (!gst_element_link_many (_videoQueue, _videoColorSpace, _videoSink, NULL)) { + g_printerr ("Video elements could not be linked.\n"); + unloadMovie(); + return false; + } + + // Process URI. + gchar* uri = (gchar*) filename.toAscii().constData(); + if (!gst_uri_is_valid(uri)) + { + // Try to convert filename to URI. + GError* error = NULL; + uri = gst_filename_to_uri(uri, &error); + if (error) { + qDebug() << "Filename to URI error: " << error->message << endl; + g_error_free(error); + gst_object_unref (uri); + freeResources(); + return false; + } + } + + // Set URI to be played. + g_object_set (_source, "uri", uri, NULL); + + // Connect to the pad-added signal + g_signal_connect (_source, "pad-added", G_CALLBACK (VideoImpl::gstPadAddedCallback), &_padHandlerData); + + // Configure audio appsink. + // TODO: change from mono to stereo +// gchar* audioCapsText = g_strdup_printf ("audio/x-raw-float,channels=1,rate=%d,signed=(boolean)true,width=%d,depth=%d,endianness=BYTE_ORDER", +// Engine::signalInfo().sampleRate(), (int)(sizeof(Signal_T)*8), (int)(sizeof(Signal_T)*8) ); +// GstCaps* audioCaps = gst_caps_from_string (audioCapsText); +// g_object_set (_audioSink, "emit-signals", TRUE, +// "caps", audioCaps, +//// "max-buffers", 1, // only one buffer (the last) is maintained in the queue +//// "drop", TRUE, // ... other buffers are dropped +// NULL); +// g_signal_connect (_audioSink, "new-buffer", G_CALLBACK (VideoImpl::gstNewAudioBufferCallback), &_newAudioBufferHandlerData); +// gst_caps_unref (audioCaps); +// g_free (audioCapsText); + + // Configure video appsink. +// GstCaps *videoCaps = gst_caps_from_string ("video/x-raw-rgb"); + GstCaps *videoCaps = gst_caps_from_string ("video/x-raw-rgb,format=RGBA,bpp=32,depth=32"); + g_object_set (_videoSink, "emit-signals", TRUE, + "caps", videoCaps, // this sets video caps to "video/x-raw-rgb" + "max-buffers", 1, // only one buffer (the last) is maintained in the queue + "drop", TRUE, // ... other buffers are dropped + NULL); + g_signal_connect (_videoSink, "new-buffer", G_CALLBACK (VideoImpl::gstNewBufferCallback), &_videoNewBufferCounter); + gst_caps_unref (videoCaps); + + // Listen to the bus. + _bus = gst_element_get_bus (_pipeline); + + // Start playing. + if (!_setPlayState(true)) + return false; + + qDebug() << "Pipeline started." << endl; + + //_movieReady = true; + return true; +} + + +void VideoImpl::runVideo() { + +// if (!_VIDEO_OUT->connected()) +// return; + + if (!_preRun()) + return; + + if (_videoNewBufferCounter > 0) { + + // Pull video. + if (!_videoPull()) + { + _setFinished(true); +// _FINISH_OUT->type()->setValue(1.0f); +// _VIDEO_OUT->sleeping(true); + } else +// _VIDEO_OUT->sleeping(false); + + + _videoNewBufferCounter--; + } + + _postRun(); +} + + +//void VideoImpl::runAudio() { +// +// if (!_AUDIO_OUT->connected()) +// return; +// +// if (!_preRun()) +// return; +// +// unsigned int blockByteSize = Engine::signalInfo().blockSize()*sizeof(Signal_T); +// if (gst_adapter_available(_audioBufferAdapter) >= blockByteSize ) +// { +// // Copy block of data to audio output. +// gst_adapter_copy(_audioBufferAdapter, (guint8*)_AUDIO_OUT->type()->data(), 0, blockByteSize); +// gst_adapter_flush (_audioBufferAdapter, blockByteSize); +// +// _AUDIO_OUT->sleeping(false); +// } +// else +// { +// _FINISH_OUT->type()->setValue(1.0f); +// _AUDIO_OUT->sleeping(true); +// } +// +// _postRun(); +//} + +bool VideoImpl::_preRun() +{ + // Check for end-of-stream or terminate. + if (_eos() || _terminate) + { + _setFinished(true); +// _FINISH_OUT->type()->setValue(1.0f); +// _VIDEO_OUT->sleeping(true); +// _AUDIO_OUT->sleeping(true); +// +// if (_audioBufferAdapter != NULL) +// gst_adapter_clear(_audioBufferAdapter); + } + else + _setFinished(false); +// _FINISH_OUT->type()->setValue(0.0f); + +// if (_RESET_IN->type()->boolValue()) +// resetMovie(); + + if (!_movieReady || + !_padHandlerData.isConnected()) + return false; + + return true; +} + +void VideoImpl::_postRun() +{ + // Parse message. + if (_bus != NULL) + { + GstMessage *msg = gst_bus_timed_pop_filtered( + _bus, 0, + (GstMessageType) (GST_MESSAGE_STATE_CHANGED | GST_MESSAGE_ERROR | GST_MESSAGE_EOS)); + + if (msg != NULL) { + GError *err; + gchar *debug_info; + + switch (GST_MESSAGE_TYPE (msg)) { + + case GST_MESSAGE_ERROR: + gst_message_parse_error(msg, &err, &debug_info); + g_printerr("Error received from element %s: %s\n", + GST_OBJECT_NAME (msg->src), err->message); + g_printerr("Debugging information: %s\n", + debug_info ? debug_info : "none"); + g_clear_error(&err); + g_free(debug_info); + + _terminate = true; +// _finish(); + break; + + case GST_MESSAGE_EOS: + g_print("End-Of-Stream reached.\n"); +// _terminate = true; +// _finish(); + break; + + case GST_MESSAGE_STATE_CHANGED: + // We are only interested in state-changed messages from the pipeline. + if (GST_MESSAGE_SRC (msg) == GST_OBJECT (_pipeline)) { + GstState oldState, newState, pendingState; + gst_message_parse_state_changed(msg, &oldState, &newState, + &pendingState); + g_print("Pipeline state for movie %s changed from %s to %s:\n", + _currentMovie.toAscii().constData(), + gst_element_state_get_name(oldState), + gst_element_state_get_name(newState)); + +// if (oldState == GST_STATE_PAUSED && newState == GST_STATE_READY) +// gst_adapter_clear(_audioBufferAdapter); + + if (newState == GST_STATE_PLAYING) { + // Check if seeking is allowed. + gint64 start, end; + GstQuery *query = gst_query_new_seeking (GST_FORMAT_TIME); + if (gst_element_query (_pipeline, query)) + { + gst_query_parse_seeking (query, NULL, (gboolean*)&_seekEnabled, &start, &end); + if (_seekEnabled) + { + g_print ("Seeking is ENABLED from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT "\n", + GST_TIME_ARGS (start), GST_TIME_ARGS (end)); + } + else + { + g_print ("Seeking is DISABLED for this stream.\n"); + } + } + else + { + g_printerr ("Seeking query failed."); + } + + gst_query_unref (query); + } + } + break; + + default: + // We should not reach here. + g_printerr("Unexpected message received.\n"); + break; + } + gst_message_unref(msg); + } + } +} + + +bool VideoImpl::_setPlayState(bool play) +{ + if (_pipeline == NULL) + return false; + + GstStateChangeReturn ret = gst_element_set_state (_pipeline, (play ? GST_STATE_PLAYING : GST_STATE_PAUSED)); + if (ret == GST_STATE_CHANGE_FAILURE) + { + qDebug() << "Unable to set the pipeline to the playing state." << endl; + unloadMovie(); + return false; + } + else + { + _setReady(play); + + return true; + } +} + +void VideoImpl::_setReady(bool ready) +{ + _movieReady = ready; +// _VIDEO_OUT->sleeping(!ready); +// _AUDIO_OUT->sleeping(!ready); +} + +void VideoImpl::_setFinished(bool finished) { + qDebug() << "Clip " << (finished ? "finished" : "not finished") << endl; +} + +void VideoImpl::gstPadAddedCallback(GstElement *src, GstPad *newPad, VideoImpl::GstPadHandlerData* data) { + g_print ("Received new pad '%s' from '%s':\n", GST_PAD_NAME (newPad), GST_ELEMENT_NAME (src)); + bool isAudio = false; + GstPad *sinkPad = NULL; + + // Check the new pad's type. + GstCaps *newPadCaps = gst_pad_get_caps (newPad); + GstStructure *newPadStruct = gst_caps_get_structure (newPadCaps, 0); + const gchar *newPadType = gst_structure_get_name (newPadStruct); + g_print("Structure is %s\n", gst_structure_to_string(newPadStruct)); + if (g_str_has_prefix (newPadType, "audio/x-raw")) + { + sinkPad = gst_element_get_static_pad (data->audioToConnect, "sink"); + isAudio = true; + } + else if (g_str_has_prefix (newPadType, "video/x-raw")) + { + sinkPad = gst_element_get_static_pad (data->videoToConnect, "sink"); + isAudio = false; + } + else + { + g_print (" It has type '%s' which is not raw audio/video. Ignoring.\n", newPadType); + goto exit; + } + + // If our converter is already linked, we have nothing to do here. + if (gst_pad_is_linked (sinkPad)) + { + // Best prefixes. + if (g_str_has_prefix (newPadType, "audio/x-raw-float") || + g_str_has_prefix (newPadType, "video/x-raw-int") ) + { + g_print (" Found a better pad.\n"); + GstPad* oldPad = gst_pad_get_peer(sinkPad); + gst_pad_unlink(oldPad, sinkPad); + g_object_unref(oldPad); + } + else + { + g_print (" We are already linked. Ignoring.\n"); + goto exit; + } + } + + // Attempt the link + if (GST_PAD_LINK_FAILED (gst_pad_link (newPad, sinkPad))) { + g_print (" Type is '%s' but link failed.\n", newPadType); + goto exit; + } else { + g_print (" Link succeeded (type '%s').\n", newPadType); + if (isAudio) + { + data->audioIsConnected = true; + } + else + { + data->videoIsConnected = true; + } + } + +exit: + // Unreference the new pad's caps, if we got them. + if (newPadCaps != NULL) + gst_caps_unref (newPadCaps); + + // Unreference the sink pad. + if (sinkPad != NULL) + gst_object_unref (sinkPad); +} + +//void VideoImpl::gstNewAudioBufferCallback(GstElement *sink, GstNewAudioBufferHandlerData *data) { +// GstBuffer *buffer = NULL; +// +// // Retrieve the buffer. +// // TODO: we should pull ALL buffers and add them to the adapter +// g_signal_emit_by_name (data->audioSink, "pull-buffer", &buffer); +// +// if (buffer) +// { +// ASSERT_WARNING_MESSAGE( ! GST_BUFFER_IS_DISCONT(buffer), "Discontinuity detected in audio buffer." ); +// +//// int blockSize = 2; +//// int sampleRate = 1; +//// int channels = 0; +//// int width = 0; +//// GstCaps* caps = GST_BUFFER_CAPS(buffer); +//// GstStructure *capsStruct = gst_caps_get_structure (caps, 0); +//// +//// gst_structure_get_int(capsStruct, "rate", &sampleRate); +//// gst_structure_get_int(capsStruct, "channels", &channels); +//// gst_structure_get_int(capsStruct, "width", &width); +// +//// qDebug() << "rate = " << sampleRate << " channels = " << channels << " width = " << width << endl; +//// unsigned int blockByteSize = Engine::signalInfo().blockSize() * sizeof(Signal_T); +// +//// qDebug() << "bufsize: "<< GST_BUFFER_SIZE(buffer) << +//// " / adaptersize: " << gst_adapter_available(data->audioBufferAdapter) << endl; +// +// // Add buffer to the adapter. +// gst_adapter_push(data->audioBufferAdapter, buffer); +// // qDebug() << " .. after push = : "<< gst_adapter_available(_audioBufferAdapter); +// +// // NOTE: no need to unref the buffer here because the buffer was given away with the +// // call to gst_adapter_push() +// //gst_buffer_unref (buffer); +// } +//} + +void VideoImpl::internalPrePlay() +{ + // Start/resume playback. + _setPlayState(true); +} + +void VideoImpl::internalPostPlay() +{ + // Pause playback. + _setPlayState(false); +} + + + + + diff --git a/VideoImpl.h b/VideoImpl.h index 350c6bf..410f5c8 100644 --- a/VideoImpl.h +++ b/VideoImpl.h @@ -1,8 +1,12 @@ /* - * Paint.h + * VideoImpl.h * * (c) 2013 Sofian Audry -- info(@)sofianaudry(.)com * (c) 2013 Alexandre Quessy -- alexandre(@)quessy(.)net + * (c) 2012 Jean-Sebastien Senecal + * (c) 2004 Mathieu Guindon, Julien Keable + * Based on code from Drone http://github.com/sofian/drone + * Based on code from the GStreamer Tutorials http://docs.gstreamer.com/display/GstSDK/Tutorials * * 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 @@ -39,16 +43,137 @@ class VideoImpl { public: - VideoImpl(); + VideoImpl(const QString uri); ~VideoImpl(); - void setUri(const QString uri); + +// void setUri(const QString uri); static bool hasVideoSupport(); void build(); int getWidth() const; int getHeight() const; const uchar* getBits() const; + + void runVideo(); +// void runAudio(); + +protected: + bool loadMovie(QString filename); + void unloadMovie(); + void freeResources(); + void resetMovie(); + + void internalPrePlay(); + void internalPostPlay(); + private: - QString uri_; + bool _videoPull(); + bool _eos() const; +// void _finish(); +// void _init(); + + bool _preRun(); + void _postRun(); + bool _setPlayState(bool play); + void _setReady(bool ready); + void _setFinished(bool finished); + +public: + // GStreamer callbacks. + + struct GstPadHandlerData { + GstElement* audioToConnect; + GstElement* videoToConnect; + GstElement* videoSink; + bool audioIsConnected; + bool videoIsConnected; + + GstPadHandlerData() : + audioToConnect(NULL), videoToConnect(NULL), videoSink(NULL), + audioIsConnected(false), videoIsConnected(false) + {} + + bool isConnected() const { + return (audioIsConnected && videoIsConnected); + } + }; + +// struct GstNewAudioBufferHandlerData { +// GstElement* audioSink; +// GstAdapter* audioBufferAdapter; +// GstNewAudioBufferHandlerData() : audioSink(NULL), audioBufferAdapter(NULL) {} +// }; + + // GStreamer callback that simply sets the #newBuffer# flag to point to TRUE. + static void gstNewBufferCallback(GstElement *sink, int *newBufferCounter); + +// static void gstNewAudioBufferCallback(GstElement *sink, GstNewAudioBufferHandlerData *data); + + // GStreamer callback that plugs the audio/video pads into the proper elements when they + // are made available by the source. + static void gstPadAddedCallback(GstElement *src, GstPad *newPad, VideoImpl::GstPadHandlerData* data); + +private: +// PlugOut *_VIDEO_OUT; +// PlugOut *_AUDIO_OUT; +// PlugOut *_FINISH_OUT; +// PlugIn *_RESET_IN; +// PlugIn *_MOVIE_IN; +// +// VideoRGBAType *_imageOut; + + //locals + QString _currentMovie; + + // gstreamer + GstBus *_bus; + GstElement *_pipeline; + GstElement *_source; + GstElement *_audioQueue; + GstElement *_audioConvert; + GstElement *_audioResample; + GstElement *_videoQueue; + GstElement *_videoConvert; + GstElement *_videoColorSpace; + GstElement *_audioSink; + GstElement *_videoSink; + +// GstAdapter *_audioBufferAdapter; + + GstPadHandlerData _padHandlerData; +// GstNewAudioBufferHandlerData _newAudioBufferHandlerData; + + int _width; + int _height; + uchar *_data; + + bool _seekEnabled; + + int _audioNewBufferCounter; + int _videoNewBufferCounter; + + bool _terminate; + bool _movieReady; + +private: + QString _uri; }; +//! Fast converts 24-bits color to 32 bits (alpha is set to specified alpha value). +// Based on: http://stackoverflow.com/questions/7069090/convert-rgb-to-rgba-in-c +inline void convert24to32(unsigned char *dst, const unsigned char *src, size_t size, unsigned char alpha=0xff) { + Q_ASSERT(dst != NULL); + Q_ASSERT(src != NULL); + if (size==0) return; + uint32_t alphaMask = ((uint32_t)alpha) << 24; + // Copy by 4 byte blocks. + for (size_t i=size; --i; dst+=4, src+=3) + { + *(uint32_t*)(void*)dst = (*(const uint32_t*)(const void*)src) | alphaMask; + } + // Copy remaining bytes. + *dst++ = *src++; + *dst++ = *src++; + *dst++ = *src++; +} + #endif /* ifndef */ From ada827de41f0f75ed00cac3c295c3b75da0f3a36 Mon Sep 17 00:00:00 2001 From: Tats Date: Sat, 26 Apr 2014 00:07:25 +0100 Subject: [PATCH 08/14] Replaced images by videos + implemented hack on update() / runVideo() to quickly test gstreamer (works!) --- MainWindow.cpp | 5 ++++- Mapper.cpp | 1 + Paint.cpp | 4 ++++ Paint.h | 4 ++++ VideoImpl.cpp | 9 ++++++--- VideoImpl.h | 2 +- 6 files changed, 20 insertions(+), 5 deletions(-) diff --git a/MainWindow.cpp b/MainWindow.cpp index 021420a..48e8e6a 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -1087,7 +1087,7 @@ void MainWindow::addMappingItem(uint mappingId) // Add mapper. // XXX hardcoded for textures std::tr1::shared_ptr textureMapping; - if (paintType == "image") + if (paintType == "image" || paintType == "video") { textureMapping = std::tr1::static_pointer_cast(mapping); Q_CHECK_PTR(textureMapping); @@ -1160,6 +1160,9 @@ void MainWindow::addMappingItem(uint mappingId) item->setSizeHint(QSize(item->sizeHint().width(), MainWindow::SHAPE_LIST_ITEM_HEIGHT)); mappingList->insertItem(0, item); mappingList->setCurrentItem(item); + + // Build mapping! + mapping->build(); } void MainWindow::clearWindow() diff --git a/Mapper.cpp b/Mapper.cpp index f94fb24..c79aeed 100644 --- a/Mapper.cpp +++ b/Mapper.cpp @@ -302,6 +302,7 @@ void TextureMapper::draw(QPainter* painter) Q_ASSERT( outputShape->nVertices() == outputShape->nVertices()); // Project source texture and sent it to destination. + texture->update(); glEnable (GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, texture->getTextureId()); diff --git a/Paint.cpp b/Paint.cpp index 0de90ae..1f4d4b8 100644 --- a/Paint.cpp +++ b/Paint.cpp @@ -75,6 +75,10 @@ int Video::getHeight() const return this->impl_->getHeight(); } +void Video::update() { + impl_->runVideo(); +} + const uchar* Video::getBits() const { return this->impl_->getBits(); diff --git a/Paint.h b/Paint.h index 86382bb..b2f3e6c 100644 --- a/Paint.h +++ b/Paint.h @@ -64,6 +64,9 @@ public: virtual void build() {} + /// This method should be called at each call of draw(). + virtual void update() {} + void setName(const QString& name) { _name = name; } QString getName() const { return _name; } uid getId() const { return _id; } @@ -178,6 +181,7 @@ public: return uri; } virtual void build(); + virtual void update(); virtual QString getType() const { return "video"; diff --git a/VideoImpl.cpp b/VideoImpl.cpp index be20882..d81f354 100644 --- a/VideoImpl.cpp +++ b/VideoImpl.cpp @@ -54,6 +54,7 @@ const uchar* VideoImpl::getBits() const void VideoImpl::build() { + qDebug() << "Building video impl" << endl; if (!loadMovie(_uri)) { qDebug() << "Cannot load movie " << _currentMovie << "." << endl; @@ -69,6 +70,8 @@ VideoImpl::~VideoImpl() bool VideoImpl::_videoPull() { + qDebug() << "video pull" << endl; + GstBuffer *buffer; // Retrieve the buffer. @@ -85,8 +88,8 @@ bool VideoImpl::_videoPull() GstCaps* caps = GST_BUFFER_CAPS(buffer); GstStructure *capsStruct = gst_caps_get_structure (caps, 0); - int width = 320; - int height = 240; + int width = 640; + int height = 480; int bpp = 24; int depth = 24; @@ -170,7 +173,7 @@ _videoConvert(NULL), _videoColorSpace(NULL), _audioSink(NULL), _videoSink(NULL), -_width(720), +_width(640), _height(480), _data(NULL), //_audioBufferAdapter(NULL), diff --git a/VideoImpl.h b/VideoImpl.h index 410f5c8..2ba23ec 100644 --- a/VideoImpl.h +++ b/VideoImpl.h @@ -93,7 +93,7 @@ public: {} bool isConnected() const { - return (audioIsConnected && videoIsConnected); + return (/*audioIsConnected && */videoIsConnected); } }; From 6e58f94a489c5260c01521fddf1893088bec8d05 Mon Sep 17 00:00:00 2001 From: Tats Date: Sat, 26 Apr 2014 01:01:44 +0100 Subject: [PATCH 09/14] Simple "hack" with a QTimer to time the video --- MainWindow.cpp | 5 +++++ MainWindow.h | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/MainWindow.cpp b/MainWindow.cpp index 48e8e6a..344861d 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -42,6 +42,11 @@ MainWindow::MainWindow() _hasCurrentPaint = false; _hasCurrentMapping = false; + videoTimer = new QTimer(this); + videoTimer->setInterval(1000/30); + connect(videoTimer, SIGNAL(timeout()), this, SLOT(updateAll())); + videoTimer->start(); + createLayout(); createActions(); diff --git a/MainWindow.h b/MainWindow.h index b1282cc..5fd8f97 100644 --- a/MainWindow.h +++ b/MainWindow.h @@ -234,6 +234,8 @@ private: bool _hasCurrentMapping; bool _hasCurrentPaint; + QTimer *videoTimer; + static MainWindow* instance; public: @@ -265,7 +267,6 @@ public: public slots: void updateAll(); - public: // Constants. static const int DEFAULT_WIDTH = 1600; From f9ac582804c180eda03b64864853a26a54480d36 Mon Sep 17 00:00:00 2001 From: Tats Date: Sat, 26 Apr 2014 01:02:06 +0100 Subject: [PATCH 10/14] Loop movie --- VideoImpl.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/VideoImpl.cpp b/VideoImpl.cpp index d81f354..5d6978d 100644 --- a/VideoImpl.cpp +++ b/VideoImpl.cpp @@ -270,8 +270,9 @@ void VideoImpl::resetMovie() else { // Just reload movie. - qDebug() << "Reloading the movie" << endl; + qDebug() << "Reloading the movie" << _seekEnabled << endl; _currentMovie = ""; + loadMovie(_uri); } } @@ -458,6 +459,8 @@ bool VideoImpl::_preRun() if (_eos() || _terminate) { _setFinished(true); + resetMovie(); + // _FINISH_OUT->type()->setValue(1.0f); // _VIDEO_OUT->sleeping(true); // _AUDIO_OUT->sleeping(true); From f731d6459ec81569006456be9e9fcbb48bf4e1e3 Mon Sep 17 00:00:00 2001 From: Alexandre Quessy Date: Fri, 25 Apr 2014 20:24:00 -0400 Subject: [PATCH 11/14] link against gst on mac --- mapmap.pro | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mapmap.pro b/mapmap.pro index 1025e23..675b8dd 100644 --- a/mapmap.pro +++ b/mapmap.pro @@ -81,6 +81,11 @@ mac { QMAKE_CXXFLAGS += -D__MACOSX_CORE__ QMAKE_CXXFLAGS += -stdlib=libstdc++ + INCLUDEPATH += /Library/Frameworks/GStreamer.framework/Headers + LIBS+=-L/opt/local/lib -lavformat -lavcodec -lavutil -lswscale -lz + LIBS+=-framework GStreamer + QMAKE_LFLAGS+=-read_only_relocs suppress + # -lGLEW } From d2f43049a6ed538f817b32f3727898e741154937 Mon Sep 17 00:00:00 2001 From: Alexandre Quessy Date: Fri, 25 Apr 2014 21:08:40 -0400 Subject: [PATCH 12/14] fixes to the packaging for gst for os x --- mapmap.pro | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/mapmap.pro b/mapmap.pro index 92ddbe0..bc98a73 100644 --- a/mapmap.pro +++ b/mapmap.pro @@ -104,19 +104,11 @@ mac { LIBS+=-framework GStreamer QMAKE_LFLAGS+=-read_only_relocs suppress - # -lGLEW - INCLUDEPATH += /opt/local/include/ \ - /opt/local/include/gstreamer-0.10/ \ - /opt/local/include/glib-2.0/ \ - /opt/local/lib/glib-2.0/include \ - /opt/local/include/libxml2 - LIBS += -L/opt/local/lib \ - -lGLEW \ - -lgstreamer-0.10 \ - -lgstapp-0.10 \ - -lgstvideo-0.10 \ - -lglib-2.0 \ - -lgobject-2.0 + # LIBS += -lgstreamer-0.10 \ + # -lgstapp-0.10 \ + # -lgstvideo-0.10 \ + # -lglib-2.0 \ + # -lgobject-2.0 LIBS += -framework OpenGL -framework GLUT QMAKE_CXXFLAGS += -D__MACOSX_CORE__ } From 322da96ed22964348e4b6cd94a45da136f01ff49 Mon Sep 17 00:00:00 2001 From: Alexandre Quessy Date: Fri, 25 Apr 2014 22:02:49 -0400 Subject: [PATCH 13/14] more fixes for gst on os x --- build.sh | 2 +- mapmap.pro | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/build.sh b/build.sh index c80254b..949c782 100755 --- a/build.sh +++ b/build.sh @@ -13,7 +13,7 @@ if [[ $unamestr == "Darwin" ]]; then #export QMAKESPEC=macx-xcode PATH=$PATH:/Users/aalex/Qt5.2.1/5.2.1/clang_64/bin qmake5=/Users/aalex/Qt5.2.1/5.2.1/clang_64/bin/qmake - $qmake5 + $qmake5 -spec macx-llvm elif [[ $unamestr == "Linux" ]]; then qmake-qt4 fi diff --git a/mapmap.pro b/mapmap.pro index bc98a73..0fe998c 100644 --- a/mapmap.pro +++ b/mapmap.pro @@ -95,14 +95,13 @@ mac { LIBS += \ -framework OpenGL \ -framework GLUT - # -L/opt/local/lib \ QMAKE_CXXFLAGS += -D__MACOSX_CORE__ QMAKE_CXXFLAGS += -stdlib=libstdc++ INCLUDEPATH += /Library/Frameworks/GStreamer.framework/Headers - LIBS+=-L/opt/local/lib -lavformat -lavcodec -lavutil -lswscale -lz - LIBS+=-framework GStreamer - QMAKE_LFLAGS+=-read_only_relocs suppress + # LIBS+=-lavformat -lavcodec -lavutil -lswscale -lz + LIBS += -F /Library/Frameworks/ -framework GStreamer + # QMAKE_LFLAGS+=-read_only_relocs suppress # LIBS += -lgstreamer-0.10 \ # -lgstapp-0.10 \ From e2b7f9b982998c8ad345c57bb9c5bbc243d787f8 Mon Sep 17 00:00:00 2001 From: Tats Date: Sat, 26 Apr 2014 11:27:59 +0100 Subject: [PATCH 14/14] Compilation fixes --- MainWindow.cpp | 7 +++++++ OscReceiver.h | 1 - 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/MainWindow.cpp b/MainWindow.cpp index fab90fe..83d1214 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -1536,3 +1536,10 @@ void MainWindow::applyOscCommand(const QVariantList& command) open(); } +void MainWindow::updateAll() +{ + sourceCanvas->update(); + destinationCanvas->update(); +} + + diff --git a/OscReceiver.h b/OscReceiver.h index 0d32464..44d1096 100644 --- a/OscReceiver.h +++ b/OscReceiver.h @@ -21,7 +21,6 @@ class OscReceiver { static bool server_is_ok_; private: std::string toString() const; - bool server_is_ok_; std::string port_; lo_server_thread server_; #ifdef CONFIG_DEBUG